Skip to content

Commit c7b1616

Browse files
committed
Scala.js: Implement JS-specific primitives.
Except those related to non-native JS classes.
1 parent 8fb0be8 commit c7b1616

File tree

2 files changed

+171
-3
lines changed

2 files changed

+171
-3
lines changed

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 168 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,9 +1185,9 @@ class JSCodeGen()(implicit ctx: Context) {
11851185
genCoercion(tree, receiver, code)
11861186
else if (code == JSPrimitives.THROW)
11871187
genThrow(tree, args)
1188-
else /*if (primitives.isJSPrimitive(code))
1189-
genJSPrimitive(tree, receiver, args, code)
1190-
else*/
1188+
else if (JSPrimitives.isJSPrimitive(code))
1189+
genJSPrimitive(tree, args, code, isStat)
1190+
else
11911191
throw new FatalError(s"Unknown primitive: ${tree.symbol.fullName} at: $pos")
11921192
}
11931193

@@ -2200,6 +2200,171 @@ class JSCodeGen()(implicit ctx: Context) {
22002200
}
22012201
}
22022202

2203+
/** Gen JS code for a Scala.js-specific primitive method */
2204+
private def genJSPrimitive(tree: Apply, args: List[Tree], code: Int,
2205+
isStat: Boolean): js.Tree = {
2206+
2207+
import JSPrimitives._
2208+
2209+
implicit val pos = tree.span
2210+
2211+
def genArgs1: js.Tree = {
2212+
assert(args.size == 1,
2213+
s"Expected exactly 1 argument for JS primitive $code but got " +
2214+
s"${args.size} at $pos")
2215+
genExpr(args.head)
2216+
}
2217+
2218+
def genArgs2: (js.Tree, js.Tree) = {
2219+
assert(args.size == 2,
2220+
s"Expected exactly 2 arguments for JS primitive $code but got " +
2221+
s"${args.size} at $pos")
2222+
(genExpr(args.head), genExpr(args.tail.head))
2223+
}
2224+
2225+
def genArgsVarLength: List[js.TreeOrJSSpread] =
2226+
genActualJSArgs(tree.symbol, args)
2227+
2228+
def resolveReifiedJSClassSym(arg: Tree): Symbol = {
2229+
def fail(): Symbol = {
2230+
ctx.error(
2231+
tree.symbol.name.toString + " must be called with a constant " +
2232+
"classOf[T] representing a class extending js.Any " +
2233+
"(not a trait nor an object)",
2234+
tree.sourcePos)
2235+
NoSymbol
2236+
}
2237+
arg match {
2238+
case Literal(value) if value.tag == Constants.ClazzTag =>
2239+
val classSym = value.typeValue.typeSymbol
2240+
if (isJSType(classSym) && !classSym.is(Trait) && !classSym.is(ModuleClass))
2241+
classSym
2242+
else
2243+
fail()
2244+
case _ =>
2245+
fail()
2246+
}
2247+
}
2248+
2249+
(code: @switch) match {
2250+
case DYNNEW =>
2251+
// js.Dynamic.newInstance(clazz)(actualArgs: _*)
2252+
val (jsClass, actualArgs) = extractFirstArg(genArgsVarLength)
2253+
js.JSNew(jsClass, actualArgs)
2254+
2255+
case ARR_CREATE =>
2256+
// js.Array(elements: _*)
2257+
js.JSArrayConstr(genArgsVarLength)
2258+
2259+
case CONSTRUCTOROF =>
2260+
// runtime.constructorOf(clazz)
2261+
val classSym = resolveReifiedJSClassSym(args.head)
2262+
if (classSym == NoSymbol)
2263+
js.Undefined() // compile error emitted by resolveReifiedJSClassSym
2264+
else
2265+
genLoadJSConstructor(classSym)
2266+
2267+
/*
2268+
case CREATE_INNER_JS_CLASS | CREATE_LOCAL_JS_CLASS =>
2269+
// runtime.createInnerJSClass(clazz, superClass)
2270+
// runtime.createLocalJSClass(clazz, superClass, fakeNewInstances)
2271+
val classSym = resolveReifiedJSClassSym(args(0))
2272+
val superClassValue = genExpr(args(1))
2273+
if (classSym == NoSymbol) {
2274+
js.Undefined() // compile error emitted by resolveReifiedJSClassSym
2275+
} else {
2276+
val captureValues = {
2277+
if (code == CREATE_INNER_JS_CLASS) {
2278+
val outer = genThis()
2279+
List.fill(classSym.info.decls.count(_.isClassConstructor))(outer)
2280+
} else {
2281+
val ArrayValue(_, fakeNewInstances) = args(2)
2282+
fakeNewInstances.flatMap(genCaptureValuesFromFakeNewInstance(_))
2283+
}
2284+
}
2285+
js.CreateJSClass(encodeClassRef(classSym),
2286+
superClassValue :: captureValues)
2287+
}
2288+
2289+
case WITH_CONTEXTUAL_JS_CLASS_VALUE =>
2290+
// withContextualJSClassValue(jsclass, inner)
2291+
val jsClassValue = genExpr(args(0))
2292+
withScopedVars(
2293+
contextualJSClassValue := Some(jsClassValue)
2294+
) {
2295+
genStatOrExpr(args(1), isStat)
2296+
}
2297+
*/
2298+
2299+
case LINKING_INFO =>
2300+
// runtime.linkingInfo
2301+
js.JSLinkingInfo()
2302+
2303+
case DEBUGGER =>
2304+
// js.special.debugger()
2305+
js.Debugger()
2306+
2307+
case UNITVAL =>
2308+
// BoxedUnit.UNIT, which is the boxed version of ()
2309+
js.Undefined()
2310+
2311+
case JS_NATIVE =>
2312+
// js.native
2313+
ctx.error(
2314+
"js.native may only be used as stub implementation in facade types",
2315+
tree.sourcePos)
2316+
js.Undefined()
2317+
2318+
case TYPEOF =>
2319+
// js.typeOf(arg)
2320+
val arg = genArgs1
2321+
genAsInstanceOf(js.JSUnaryOp(js.JSUnaryOp.typeof, arg), defn.StringType)
2322+
2323+
case IN =>
2324+
// js.special.in(arg1, arg2)
2325+
val (arg1, arg2) = genArgs2
2326+
js.Unbox(js.JSBinaryOp(js.JSBinaryOp.in, arg1, arg2), 'Z')
2327+
2328+
case INSTANCEOF =>
2329+
// js.special.instanceof(arg1, arg2)
2330+
val (arg1, arg2) = genArgs2
2331+
js.Unbox(js.JSBinaryOp(js.JSBinaryOp.instanceof, arg1, arg2), 'Z')
2332+
2333+
case DELETE =>
2334+
// js.special.delete(arg1, arg2)
2335+
val (arg1, arg2) = genArgs2
2336+
js.JSDelete(js.JSBracketSelect(arg1, arg2))
2337+
2338+
case FORIN =>
2339+
/* js.special.forin(arg1, arg2)
2340+
*
2341+
* We must generate:
2342+
*
2343+
* val obj = arg1
2344+
* val f = arg2
2345+
* for (val key in obj) {
2346+
* f(key)
2347+
* }
2348+
*
2349+
* with temporary vals, because `arg2` must be evaluated only
2350+
* once, and after `arg1`.
2351+
*/
2352+
val (arg1, arg2) = genArgs2
2353+
val objVarDef = js.VarDef(freshLocalIdent("obj"), jstpe.AnyType,
2354+
mutable = false, arg1)
2355+
val fVarDef = js.VarDef(freshLocalIdent("f"), jstpe.AnyType,
2356+
mutable = false, arg2)
2357+
val keyVarIdent = freshLocalIdent("key")
2358+
val keyVarRef = js.VarRef(keyVarIdent)(jstpe.AnyType)
2359+
js.Block(
2360+
objVarDef,
2361+
fVarDef,
2362+
js.ForIn(objVarDef.ref, keyVarIdent, {
2363+
js.JSFunctionApply(fVarDef.ref, List(keyVarRef))
2364+
}))
2365+
}
2366+
}
2367+
22032368
/** Gen actual actual arguments to Scala method call.
22042369
* Returns a list of the transformed arguments.
22052370
*

compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ object JSPrimitives {
4141

4242
final val LastJSPrimitiveCode = THROW
4343

44+
def isJSPrimitive(code: Int): Boolean =
45+
code >= FirstJSPrimitiveCode && code <= LastJSPrimitiveCode
46+
4447
}
4548

4649
class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) {

0 commit comments

Comments
 (0)