Skip to content

Commit b9bc925

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

File tree

2 files changed

+171
-3
lines changed

2 files changed

+171
-3
lines changed

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

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

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

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

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

+3
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)