diff --git a/.drone.yml b/.drone.yml index 1aae8732cc42..c67afae1b7c0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -34,7 +34,7 @@ pipeline: image: lampepfl/dotty:2018-10-01 commands: - cp -R . /tmp/2/ && cd /tmp/2/ - - ./project/scripts/sbt ";dotty-bootstrapped/compile ;dotty-bootstrapped/test" + - ./project/scripts/sbt ";dotty-bootstrapped/compile ;dotty-bootstrapped/test ;sjsSandbox/run" - ./project/scripts/bootstrapCmdTests test_sbt: diff --git a/build.sbt b/build.sbt index 11804a2ccec1..dfe2f1e0d986 100644 --- a/build.sbt +++ b/build.sbt @@ -20,6 +20,8 @@ val scalap = Build.scalap val dist = Build.dist val `dist-bootstrapped` = Build.`dist-bootstrapped` +val sjsSandbox = Build.sjsSandbox + val `sbt-dotty` = Build.`sbt-dotty` val `vscode-dotty` = Build.`vscode-dotty` diff --git a/compiler/sjs/backend/sjs/JSPrimitives.scala b/compiler/sjs/backend/sjs/JSPrimitives.scala deleted file mode 100644 index f8a5a31bef99..000000000000 --- a/compiler/sjs/backend/sjs/JSPrimitives.scala +++ /dev/null @@ -1,118 +0,0 @@ -package dotty.tools.backend.sjs - -import dotty.tools.dotc.core._ -import Names.TermName -import StdNames._ -import Types._ -import Contexts._ -import Symbols._ - -import dotty.tools.dotc.ast.tpd._ -import dotty.tools.dotc.backend.jvm.DottyPrimitives - -import scala.collection.mutable - -object JSPrimitives { - - final val GETCLASS = 301 // jl.Object.getClass() - - final val F2JS = 302 // js.Any.fromFunctionN - final val F2JSTHIS = 303 // js.ThisFunction.fromFunctionN - - final val DYNNEW = 304 // js.Dynamic.newInstance - final val DYNLIT = 305 // js.Dynamic.literal.applyDynamic{,Named} - final val DICT_DEL = 306 // js.Dictionary.delete - final val ARR_CREATE = 307 // js.Array.apply (array literal syntax) - - final val TYPEOF = 308 // js.typeOf(x) - final val DEBUGGER = 309 // js.debugger() - final val HASPROP = 310 // js.Object.hasProperty(o, p), equiv to `p in o` in JS - final val OBJPROPS = 311 // js.Object.properties(o), equiv to `for (p in o)` in JS - final val JS_NATIVE = 312 // js.native. Marker method. Fails if tried to be emitted. - - final val UNITVAL = 313 // () value, which is undefined - final val UNITTYPE = 314 // BoxedUnit.TYPE (== classOf[Unit]) - - final val CONSTRUCTOROF = 315 // runtime.constructorOf(clazz) - final val ENV_INFO = 316 // runtime.environmentInfo - final val LINKING_INFO = 317 // runtime.linkingInfo - - final val THROW = 318 // .throw - -} - -class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) { - import JSPrimitives._ - import scala.tools.nsc.backend.ScalaPrimitives._ - - private lazy val jsPrimitives: Map[Symbol, Int] = initJSPrimitives(ctx) - - override def getPrimitive(sym: Symbol): Int = - jsPrimitives.getOrElse(sym, super.getPrimitive(sym)) - - override def getPrimitive(app: Apply, tpe: Type)(implicit ctx: Context): Int = - jsPrimitives.getOrElse(app.fun.symbol, super.getPrimitive(app, tpe)) - - override def isPrimitive(fun: Tree): Boolean = - jsPrimitives.contains(fun.symbol(ctx)) || super.isPrimitive(fun) - - /** Initialize the primitive map */ - private def initJSPrimitives(implicit ctx: Context): Map[Symbol, Int] = { - - val primitives = newMutableSymbolMap[Int] - - // !!! Code duplicate with DottyPrimitives - /** Add a primitive operation to the map */ - def addPrimitive(s: Symbol, code: Int): Unit = { - assert(!(primitives contains s), "Duplicate primitive " + s) - primitives(s) = code - } - - def addPrimitives(cls: Symbol, method: TermName, code: Int)(implicit ctx: Context): Unit = { - val alts = cls.info.member(method).alternatives.map(_.symbol) - if (alts.isEmpty) { - ctx.error(s"Unknown primitive method $cls.$method") - } else { - for (s <- alts) - addPrimitive(s, code) - } - } - - val jsdefn = JSDefinitions.jsdefn - - addPrimitive(defn.Any_getClass, GETCLASS) - - for (i <- 0 to 22) - addPrimitive(jsdefn.JSAny_fromFunction(i), F2JS) - for (i <- 1 to 22) - addPrimitive(jsdefn.JSThisFunction_fromFunction(i), F2JSTHIS) - - addPrimitive(jsdefn.JSDynamic_newInstance, DYNNEW) - - addPrimitive(jsdefn.JSDynamicLiteral_applyDynamicNamed, DYNLIT) - addPrimitive(jsdefn.JSDynamicLiteral_applyDynamic, DYNLIT) - - addPrimitive(jsdefn.JSDictionary_delete, DICT_DEL) - - //addPrimitive(jsdefn.JSArray_create, ARR_CREATE) - - addPrimitive(jsdefn.JSPackage_typeOf, TYPEOF) - addPrimitive(jsdefn.JSPackage_debugger, DEBUGGER) - addPrimitive(jsdefn.JSPackage_native, JS_NATIVE) - - addPrimitive(jsdefn.JSObject_hasProperty, HASPROP) - addPrimitive(jsdefn.JSObject_properties, OBJPROPS) - - addPrimitive(defn.BoxedUnit_UNIT, UNITVAL) - //addPrimitive(defn.BoxedUnit_TYPE, UNITTYPE) - - //addPrimitive(jsdefn.Runtime_constructorOf, CONSTRUCTOROF) - //addPrimitive(jsdefn.Runtime_environmentInfo, ENV_INFO) - //addPrimitive(jsdefn.Runtime_linkingInfo, LINKING_INFO) - - addPrimitive(defn.throwMethod, THROW) - - primitives.toMap - } - -} diff --git a/compiler/sjs/backend/sjs/GenSJSIR.scala b/compiler/src/dotty/tools/backend/sjs/GenSJSIR.scala similarity index 81% rename from compiler/sjs/backend/sjs/GenSJSIR.scala rename to compiler/src/dotty/tools/backend/sjs/GenSJSIR.scala index 819a8f0e37f6..6088c86c1ffd 100644 --- a/compiler/sjs/backend/sjs/GenSJSIR.scala +++ b/compiler/src/dotty/tools/backend/sjs/GenSJSIR.scala @@ -9,6 +9,7 @@ class GenSJSIR extends Phase { def phaseName: String = "genSJSIR" def run(implicit ctx: Context): Unit = { - new JSCodeGen().run() + if (ctx.settings.scalajs.value) + new JSCodeGen().run() } } diff --git a/compiler/sjs/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala similarity index 82% rename from compiler/sjs/backend/sjs/JSCodeGen.scala rename to compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 1d6c538e6fdc..48c363ee65e0 100644 --- a/compiler/sjs/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -17,6 +17,7 @@ import Contexts._ import Decorators._ import Flags._ import dotty.tools.dotc.ast.Trees._ +import Names._ import Types._ import Symbols._ import Denotations._ @@ -25,8 +26,8 @@ import StdNames._ import dotty.tools.dotc.transform.Erasure -import org.scalajs.core.ir -import org.scalajs.core.ir.{ClassKind, Position, Trees => js, Types => jstpe} +import org.scalajs.ir +import org.scalajs.ir.{ClassKind, Position, Trees => js, Types => jstpe} import js.OptimizerHints import JSEncoding._ @@ -50,6 +51,7 @@ import ScopedVar.withScopedVars * - `genStatOrExpr()` and everything else generate the bodies of methods. */ class JSCodeGen()(implicit ctx: Context) { + import JSCodeGen._ import tpd._ private val jsdefn = JSDefinitions.jsdefn @@ -127,7 +129,7 @@ class JSCodeGen()(implicit ctx: Context) { /* Finally, we emit true code for the remaining class defs. */ for (td <- allTypeDefs) { val sym = td.symbol - implicit val pos = sym.pos + implicit val pos: Position = sym.pos /* Do not actually emit code for primitive types nor scala.Array. */ val isPrimitive = @@ -157,15 +159,8 @@ class JSCodeGen()(implicit ctx: Context) { val clDefs = generatedClasses.map(_._2).toList - for ((sym, tree) <- generatedClasses) { - val writer = new java.io.PrintWriter(System.err) - try { - new ir.Printers.IRTreePrinter(writer).print(tree) - } finally { - writer.flush() - } + for ((sym, tree) <- generatedClasses) genIRFile(cunit, sym, tree) - } } private def genIRFile(cunit: CompilationUnit, sym: Symbol, @@ -173,7 +168,6 @@ class JSCodeGen()(implicit ctx: Context) { val outfile = getFileFor(cunit, sym, ".sjsir") val output = outfile.bufferedOutput try { - ir.InfoSerializers.serialize(output, ir.Infos.generateClassInfo(tree)) ir.Serializers.serialize(output, tree) } finally { output.close() @@ -181,11 +175,11 @@ class JSCodeGen()(implicit ctx: Context) { } private def getFileFor(cunit: CompilationUnit, sym: Symbol, - suffix: String) = { + suffix: String): dotty.tools.io.AbstractFile = { import dotty.tools.io._ - val outputDirectory: AbstractFile = // TODO Support virtual files - new PlainDirectory(new Directory(new java.io.File(ctx.settings.d.value))) + val outputDirectory: AbstractFile = + ctx.settings.outputDir.value val pathParts = sym.fullName.toString.split("[./]") val dir = (outputDirectory /: pathParts.init)(_.subdirectoryNamed(_)) @@ -203,7 +197,7 @@ class JSCodeGen()(implicit ctx: Context) { */ private def genScalaClass(td: TypeDef): js.ClassDef = { val sym = td.symbol.asClass - implicit val pos = sym.pos + implicit val pos: Position = sym.pos assert(!sym.is(Trait), "genScalaClass() must be called only for normal classes: "+sym) @@ -279,35 +273,23 @@ class JSCodeGen()(implicit ctx: Context) { // Generate the exported members, constructors and accessors val exports = { - // Hack to export hello.world - if (sym.fullName.toString == "hello.world$") { - List( - js.ModuleExportDef("hello.world"), - js.MethodDef(static = false, js.StringLiteral("main"), - Nil, jstpe.AnyType, - js.Block(List( - js.Apply(js.This()(jstpe.ClassType(classIdent.name)), js.Ident("main__V"), Nil)(jstpe.NoType), - js.Undefined())))( - OptimizerHints.empty, None)) - } else { - /* - // Generate the exported members - val memberExports = genMemberExports(sym, exportedSymbols.toList) - - // Generate exported constructors or accessors - val exportedConstructorsOrAccessors = - if (isStaticModule(sym)) genModuleAccessorExports(sym) - else genConstructorExports(sym) - - memberExports ++ exportedConstructorsOrAccessors - */ - Nil - } + /* + // Generate the exported members + val memberExports = genMemberExports(sym, exportedSymbols.toList) + + // Generate exported constructors or accessors + val exportedConstructorsOrAccessors = + if (isStaticModule(sym)) genModuleAccessorExports(sym) + else genConstructorExports(sym) + + memberExports ++ exportedConstructorsOrAccessors + */ + Nil } // Hashed definitions of the class val hashedDefs = - ir.Hashers.hashDefs(generatedMembers ++ exports) + ir.Hashers.hashMemberDefs(generatedMembers ++ exports) // The complete class definition val kind = @@ -318,10 +300,13 @@ class JSCodeGen()(implicit ctx: Context) { val classDefinition = js.ClassDef( classIdent, kind, + None, Some(encodeClassFullNameIdent(sym.superClass)), genClassInterfaces(sym), None, - hashedDefs)( + None, + hashedDefs, + Nil)( optimizerHints) classDefinition @@ -336,20 +321,34 @@ class JSCodeGen()(implicit ctx: Context) { */ private def genRawJSClassData(td: TypeDef): js.ClassDef = { val sym = td.symbol.asClass - implicit val pos = sym.pos + implicit val pos: Position = sym.pos val classIdent = encodeClassFullNameIdent(sym) + val kind = { + if (sym.is(Trait)) ClassKind.AbstractJSType + else if (sym.is(ModuleClass)) ClassKind.NativeJSModuleClass + else ClassKind.NativeJSClass + } val superClass = if (sym.is(Trait)) None else Some(encodeClassFullNameIdent(sym.superClass)) - val jsName = - if (sym.is(Trait) || sym.is(ModuleClass)) None - else Some(fullJSNameOf(sym)) + val jsNativeLoadSpec = { + if (sym.is(Trait)) None + else { + val path = fullJSNameOf(sym).split('.').toList + Some(js.JSNativeLoadSpec.Global(path.head, path.tail)) + } + } - js.ClassDef(classIdent, ClassKind.RawJSType, + js.ClassDef( + classIdent, + kind, + None, superClass, genClassInterfaces(sym), - jsName, + None, + jsNativeLoadSpec, + Nil, Nil)( OptimizerHints.empty) } @@ -358,7 +357,7 @@ class JSCodeGen()(implicit ctx: Context) { */ private def genInterface(td: TypeDef): js.ClassDef = { val sym = td.symbol.asClass - implicit val pos = sym.pos + implicit val pos: Position = sym.pos val classIdent = encodeClassFullNameIdent(sym) @@ -378,10 +377,19 @@ class JSCodeGen()(implicit ctx: Context) { // Hashed definitions of the interface val hashedDefs = - ir.Hashers.hashDefs(generatedMethods.toList) + ir.Hashers.hashMemberDefs(generatedMethods.toList) - js.ClassDef(classIdent, ClassKind.Interface, None, superInterfaces, None, - hashedDefs)(OptimizerHints.empty) + js.ClassDef( + classIdent, + ClassKind.Interface, + None, + None, + superInterfaces, + None, + None, + hashedDefs, + Nil)( + OptimizerHints.empty) } private def genClassInterfaces(sym: ClassSymbol)( @@ -404,10 +412,7 @@ class JSCodeGen()(implicit ctx: Context) { "genClassFields called with a ClassDef other than the current one") // Non-method term members are fields - (for { - f <- classSym.info.decls - if !f.is(Method) && f.isTerm - } yield { + classSym.info.decls.filter(f => !f.is(Method) && f.isTerm).map({ f => implicit val pos = f.pos val name = @@ -451,7 +456,7 @@ class JSCodeGen()(implicit ctx: Context) { } }*/ - js.FieldDef(name, irTpe, f.is(Mutable)) + js.FieldDef(static = false, name, irTpe, f.is(Mutable)) }).toList } @@ -510,7 +515,7 @@ class JSCodeGen()(implicit ctx: Context) { None } else*/ if (sym.is(Deferred)) { Some(js.MethodDef(static = false, methodName, - jsParams, toIRType(patchedResultType(sym)), js.EmptyTree)( + jsParams, toIRType(patchedResultType(sym)), None)( OptimizerHints.empty, None)) } else /*if (isJSNativeCtorDefaultParam(sym)) { None @@ -549,7 +554,7 @@ class JSCodeGen()(implicit ctx: Context) { } else*/ if (sym.isConstructor) { js.MethodDef(static = false, methodName, jsParams, jstpe.NoType, - genStat(rhs))(optimizerHints, None) + Some(genStat(rhs)))(optimizerHints, None) } else { val resultIRType = toIRType(patchedResultType(sym)) genMethodDef(static = false, methodName, @@ -576,7 +581,7 @@ class JSCodeGen()(implicit ctx: Context) { tree: Tree, optimizerHints: OptimizerHints): js.MethodDef = { implicit val pos = tree.pos - ctx.debuglog("genMethod " + methodName.name) + ctx.debuglog("genMethod " + methodName.encodedName) ctx.debuglog("") val jsParams = for (param <- paramsSyms) yield { @@ -590,7 +595,7 @@ class JSCodeGen()(implicit ctx: Context) { else genExpr(tree) //if (!isScalaJSDefinedJSClass(currentClassSym)) { - js.MethodDef(static, methodName, jsParams, resultIRType, genBody())( + js.MethodDef(static, methodName, jsParams, resultIRType, Some(genBody()))( optimizerHints, None) /*} else { assert(!static, tree.pos) @@ -650,10 +655,6 @@ class JSCodeGen()(implicit ctx: Context) { ctx.debuglog("") tree match { - /** LabelDefs (for while and do..while loops) */ - /*case lblDf: LabelDef => - genLabelDef(lblDf)*/ - /** Local val or var declaration */ case tree @ ValDef(name, _, _) => /* Must have been eliminated by the tail call transform performed @@ -666,7 +667,7 @@ class JSCodeGen()(implicit ctx: Context) { val rhsTree = genExpr(rhs) rhsTree match { - case js.UndefinedParam() => + case js.Transient(UndefinedParam) => /* This is an intermediate assignment for default params on a * js.Any. Add the symbol to the corresponding set to inform * the Ident resolver how to replace it and don't emit the symbol. @@ -682,12 +683,21 @@ class JSCodeGen()(implicit ctx: Context) { js.If(genExpr(cond), genStatOrExpr(thenp, isStat), genStatOrExpr(elsep, isStat))(toIRType(tree.tpe)) + case Labeled(bind, expr) => + js.Labeled(encodeLabelSym(bind.symbol), toIRType(tree.tpe), genStatOrExpr(expr, isStat)) + case Return(expr, from) => - // TODO Need to consider `from`? + val fromSym = from.symbol + val label = + if (fromSym.is(Label)) encodeLabelSym(fromSym) + else localNames.get.getEnclosingReturnLabel() js.Return(toIRType(expr.tpe) match { case jstpe.NoType => js.Block(genStat(expr), js.Undefined()) case _ => genExpr(expr) - }) + }, label) + + case WhileDo(cond, body) => + js.While(genExpr(cond), genStat(body)) /*case t: Try => genTry(t, isStat)*/ @@ -745,7 +755,7 @@ class JSCodeGen()(implicit ctx: Context) { /* This is a default parameter whose assignment was moved to * a local variable. Put an undefined param instead. */ - js.UndefinedParam()(toIRType(sym.info)) + js.Transient(UndefinedParam)(toIRType(sym.info)) } else { js.VarRef(encodeLocalSym(sym))(toIRType(sym.info)) } @@ -793,7 +803,7 @@ class JSCodeGen()(implicit ctx: Context) { throw new FatalError(s"Assignment to static member ${sym.fullName} not supported") val genRhs = genExpr(rhs) val lhs = lhs0 match { - case lhs: Ident => desugarIdent(lhs) + case lhs: Ident => desugarIdent(lhs).getOrElse(lhs) case lhs => lhs } lhs match { @@ -898,7 +908,7 @@ class JSCodeGen()(implicit ctx: Context) { fun match { case _ if isJSDefaultParam(sym) => - js.UndefinedParam()(toIRType(sym.info.finalResultType)) + js.Transient(UndefinedParam)(toIRType(sym.info.finalResultType)) case Select(Super(_, _), _) => genSuperCall(tree, isStat) @@ -907,9 +917,7 @@ class JSCodeGen()(implicit ctx: Context) { genApplyNew(tree) case _ => - /*if (sym.isLabel) { - genLabelApply(tree) - } else*/ if (primitives.isPrimitive(tree)) { + if (primitives.isPrimitive(tree)) { genPrimitiveOp(tree, isStat) } else if (Erasure.Boxing.isBox(sym)) { // Box a primitive value (cannot be Unit) @@ -981,15 +989,14 @@ class JSCodeGen()(implicit ctx: Context) { assert(ctor.isClassConstructor, "'new' call to non-constructor: " + ctor.name) - if (tpe.isRef(defn.StringClass)) { - genNewString(ctor, genActualArgs(ctor, args)) - } else /*if (isHijackedBoxedClass(tpe.typeSymbol)) { - genNewHijackedBoxedClass(tpe.typeSymbol, ctor, args map genExpr) - } else if (translatedAnonFunctions contains tpe.typeSymbol) { + val clsSym = tpe.widenDealias.typeSymbol + + if (isHijackedClass(clsSym)) { + genNewHijackedClass(clsSym, ctor, args.map(genExpr)) + } else /*if (translatedAnonFunctions contains tpe.typeSymbol) { val functionMaker = translatedAnonFunctions(tpe.typeSymbol) functionMaker(args map genExpr) - } else*/ if (isJSType(tpe.widenDealias.typeSymbol)) { - val clsSym = tpe.widenDealias.typeSymbol + } else*/ if (isJSType(clsSym)) { if (clsSym == jsdefn.JSObjectClass && args.isEmpty) js.JSObjectConstr(Nil) else if (clsSym == jsdefn.JSArrayClass && args.isEmpty) js.JSArrayConstr(Nil) else js.JSNew(genLoadJSConstructor(clsSym), genActualJSArgs(ctor, args)) @@ -1004,9 +1011,32 @@ class JSCodeGen()(implicit ctx: Context) { } } + /** Gen JS code for a call to a constructor of a hijacked class. + * Reroute them to the `new` method with the same signature in the + * companion object. + */ + private def genNewHijackedClass(clazz: Symbol, ctor: Symbol, + args: List[js.Tree])(implicit pos: Position): js.Tree = { + + val encodedName = encodeClassFullName(clazz) + val moduleClass = clazz.companionModule.moduleClass + + val js.Ident(initName, origName) = encodeMethodSym(ctor) + val newMethodName = initName match { + case "init___" => + "$new__" + encodedName + case _ => + "$new" + initName.stripPrefix("init_") + "__" + encodedName + } + val newMethodIdent = js.Ident(newMethodName, origName) + + js.Apply(genLoadModule(moduleClass), newMethodIdent, args)( + jstpe.ClassType(encodedName)) + } + /** Gen JS code for a primitive method call. */ private def genPrimitiveOp(tree: Apply, isStat: Boolean): js.Tree = { - import scala.tools.nsc.backend.ScalaPrimitives._ + import scala.tools.nsc.backend.ScalaPrimitivesOps._ implicit val pos = tree.pos @@ -1046,12 +1076,12 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen JS code for a simple unary operation. */ private def genSimpleUnaryOp(tree: Apply, arg: Tree, code: Int): js.Tree = { - import scala.tools.nsc.backend.ScalaPrimitives._ + import scala.tools.nsc.backend.ScalaPrimitivesOps._ implicit val pos = tree.pos - val genArg = genExpr(arg) val resultIRType = toIRType(tree.tpe) + val genArg = adaptPrimitive(genExpr(arg), resultIRType) (code: @switch) match { case POS => @@ -1087,83 +1117,52 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen JS code for a simple binary operation. */ private def genSimpleBinaryOp(tree: Apply, lhs: Tree, rhs: Tree, code: Int): js.Tree = { - import scala.tools.nsc.backend.ScalaPrimitives._ + import scala.tools.nsc.backend.ScalaPrimitivesOps._ import js.UnaryOp._ - /* Codes for operation types, in an object so that they can be 'final val' - * and be used in switch-matches. - */ - object OpTypes { - final val DoubleOp = 1 - final val FloatOp = 2 - final val LongOp = 3 - final val IntOp = 4 - final val BooleanOp = 5 - final val AnyOp = 6 - } - import OpTypes._ - implicit val pos = tree.pos val lhsIRType = toIRType(lhs.tpe) val rhsIRType = toIRType(rhs.tpe) - val opType = (lhsIRType, rhsIRType) match { - case (jstpe.DoubleType, _) | (_, jstpe.DoubleType) => DoubleOp - case (jstpe.FloatType, _) | (_, jstpe.FloatType) => FloatOp - case (jstpe.LongType, _) | (_, jstpe.LongType) => LongOp - case (jstpe.IntType, _) | (_, jstpe.IntType) => IntOp - case (jstpe.BooleanType, jstpe.BooleanType) => BooleanOp - case _ => AnyOp + val isShift = isShiftOp(code) + + val opType = { + if (isShift) { + if (lhsIRType == jstpe.LongType) jstpe.LongType + else jstpe.IntType + } else { + (lhsIRType, rhsIRType) match { + case (jstpe.DoubleType, _) | (_, jstpe.DoubleType) => jstpe.DoubleType + case (jstpe.FloatType, _) | (_, jstpe.FloatType) => jstpe.FloatType + case (jstpe.LongType, _) | (_, jstpe.LongType) => jstpe.LongType + case (jstpe.IntType | jstpe.ByteType | jstpe.ShortType | jstpe.CharType, _) => jstpe.IntType + case (_, jstpe.IntType | jstpe.ByteType | jstpe.ShortType | jstpe.CharType) => jstpe.IntType + case (jstpe.BooleanType, _) | (_, jstpe.BooleanType) => jstpe.BooleanType + case _ => jstpe.AnyType + } + } } - if (opType == AnyOp && isUniversalEqualityOp(code)) { - genUniversalEqualityOp(lhs, rhs, code) + val lsrc = + if (opType == jstpe.AnyType) genExpr(lhs) + else adaptPrimitive(genExpr(lhs), opType) + val rsrc = + if (opType == jstpe.AnyType) genExpr(rhs) + else adaptPrimitive(genExpr(rhs), if (isShift) jstpe.IntType else opType) + + if (opType == jstpe.AnyType && isUniversalEqualityOp(code)) { + genUniversalEqualityOp(lhs.tpe, rhs.tpe, lsrc, rsrc, code) } else if (code == ZOR) { - js.If(genExpr(lhs), js.BooleanLiteral(true), genExpr(rhs))(jstpe.BooleanType) + js.If(lsrc, js.BooleanLiteral(true), rsrc)(jstpe.BooleanType) } else if (code == ZAND) { - js.If(genExpr(lhs), genExpr(rhs), js.BooleanLiteral(false))(jstpe.BooleanType) + js.If(lsrc, rsrc, js.BooleanLiteral(false))(jstpe.BooleanType) } else { import js.BinaryOp._ - def coerce(tree: js.Tree, opType: Int): js.Tree = (opType: @switch) match { - case DoubleOp => - if (tree.tpe == jstpe.LongType) js.UnaryOp(LongToDouble, tree) - else tree - - case FloatOp => - if (tree.tpe == jstpe.FloatType || tree.tpe == jstpe.IntType) tree - else js.UnaryOp(DoubleToFloat, coerce(tree, DoubleOp)) - - case LongOp => - if (tree.tpe == jstpe.LongType) tree - else { - assert(tree.tpe == jstpe.IntType) - js.UnaryOp(IntToLong, tree) - } - - case IntOp => - if (tree.tpe == jstpe.IntType) tree - else { - assert(tree.tpe == jstpe.LongType) - js.UnaryOp(LongToInt, tree) - } - - case BooleanOp | AnyOp => - tree - } - - val rhsOpType = code match { - case LSL | LSR | ASR => IntOp - case _ => opType - } - - val genLhs = coerce(genExpr(lhs), opType) - val genRhs = coerce(genExpr(rhs), rhsOpType) - - val op = (opType: @switch) match { - case IntOp => - (code: @switch) match { + (opType: @unchecked) match { + case jstpe.IntType => + val op = (code: @switch) match { case ADD => Int_+ case SUB => Int_- case MUL => Int_* @@ -1176,48 +1175,59 @@ class JSCodeGen()(implicit ctx: Context) { case LSR => Int_>>> case ASR => Int_>> - case EQ => Num_== - case NE => Num_!= - case LT => Num_< - case LE => Num_<= - case GT => Num_> - case GE => Num_>= + case EQ => Int_== + case NE => Int_!= + case LT => Int_< + case LE => Int_<= + case GT => Int_> + case GE => Int_>= } + js.BinaryOp(op, lsrc, rsrc) + + case jstpe.FloatType => + def withFloats(op: Int): js.Tree = + js.BinaryOp(op, lsrc, rsrc) + + def toDouble(value: js.Tree): js.Tree = + js.UnaryOp(js.UnaryOp.FloatToDouble, value) + + def withDoubles(op: Int): js.Tree = + js.BinaryOp(op, toDouble(lsrc), toDouble(rsrc)) - case FloatOp => (code: @switch) match { - case ADD => Float_+ - case SUB => Float_- - case MUL => Float_* - case DIV => Float_/ - case MOD => Float_% - - case EQ => Num_== - case NE => Num_!= - case LT => Num_< - case LE => Num_<= - case GT => Num_> - case GE => Num_>= + case ADD => withFloats(Float_+) + case SUB => withFloats(Float_-) + case MUL => withFloats(Float_*) + case DIV => withFloats(Float_/) + case MOD => withFloats(Float_%) + + case EQ => withDoubles(Double_==) + case NE => withDoubles(Double_!=) + case LT => withDoubles(Double_<) + case LE => withDoubles(Double_<=) + case GT => withDoubles(Double_>) + case GE => withDoubles(Double_>=) } - case DoubleOp => - (code: @switch) match { + case jstpe.DoubleType => + val op = (code: @switch) match { case ADD => Double_+ case SUB => Double_- case MUL => Double_* case DIV => Double_/ case MOD => Double_% - case EQ => Num_== - case NE => Num_!= - case LT => Num_< - case LE => Num_<= - case GT => Num_> - case GE => Num_>= + case EQ => Double_== + case NE => Double_!= + case LT => Double_< + case LE => Double_<= + case GT => Double_> + case GE => Double_>= } + js.BinaryOp(op, lsrc, rsrc) - case LongOp => - (code: @switch) match { + case jstpe.LongType => + val op = (code: @switch) match { case ADD => Long_+ case SUB => Long_- case MUL => Long_* @@ -1237,51 +1247,105 @@ class JSCodeGen()(implicit ctx: Context) { case GT => Long_> case GE => Long_>= } + js.BinaryOp(op, lsrc, rsrc) - case BooleanOp => - (code: @switch) match { + case jstpe.BooleanType => + val op = (code: @switch) match { case EQ => Boolean_== case NE => Boolean_!= case OR => Boolean_| case AND => Boolean_& case XOR => Boolean_!= } + js.BinaryOp(op, lsrc, rsrc) - case AnyOp => - /* No @switch because some 2.11 version erroneously report a warning - * for switches with less than 3 non-default cases. - */ - code match { + case jstpe.AnyType => + val op = code match { case ID => === case NI => !== } + js.BinaryOp(op, lsrc, rsrc) + } + } + } + + private def adaptPrimitive(value: js.Tree, to: jstpe.Type)( + implicit pos: Position): js.Tree = { + genConversion(value.tpe, to, value) + } + + /* This method corresponds to the method of the same name in + * BCodeBodyBuilder of the JVM back-end. It ends up calling the method + * BCodeIdiomatic.emitT2T, whose logic we replicate here. + */ + private def genConversion(from: jstpe.Type, to: jstpe.Type, value: js.Tree)( + implicit pos: Position): js.Tree = { + import js.UnaryOp._ + + if (from == to || from == jstpe.NothingType) { + value + } else if (from == jstpe.BooleanType || to == jstpe.BooleanType) { + throw new AssertionError(s"Invalid genConversion from $from to $to") + } else { + def intValue = (from: @unchecked) match { + case jstpe.IntType => value + case jstpe.CharType => js.UnaryOp(CharToInt, value) + case jstpe.ByteType => js.UnaryOp(ByteToInt, value) + case jstpe.ShortType => js.UnaryOp(ShortToInt, value) + case jstpe.LongType => js.UnaryOp(LongToInt, value) + case jstpe.FloatType => js.UnaryOp(DoubleToInt, js.UnaryOp(FloatToDouble, value)) + case jstpe.DoubleType => js.UnaryOp(DoubleToInt, value) + } + + def doubleValue = from match { + case jstpe.DoubleType => value + case jstpe.FloatType => js.UnaryOp(FloatToDouble, value) + case jstpe.LongType => js.UnaryOp(LongToDouble, value) + case _ => js.UnaryOp(IntToDouble, intValue) } - js.BinaryOp(op, genLhs, genRhs) + (to: @unchecked) match { + case jstpe.CharType => + js.UnaryOp(IntToChar, intValue) + case jstpe.ByteType => + js.UnaryOp(IntToByte, intValue) + case jstpe.ShortType => + js.UnaryOp(IntToShort, intValue) + case jstpe.IntType => + intValue + case jstpe.LongType => + from match { + case jstpe.FloatType | jstpe.DoubleType => + js.UnaryOp(DoubleToLong, doubleValue) + case _ => + js.UnaryOp(IntToLong, intValue) + } + case jstpe.FloatType => + js.UnaryOp(js.UnaryOp.DoubleToFloat, doubleValue) + case jstpe.DoubleType => + doubleValue + } } } /** Gen JS code for a universal equality test. */ - private def genUniversalEqualityOp(lhs: Tree, rhs: Tree, code: Int)( + private def genUniversalEqualityOp(ltpe: Type, rtpe: Type, lhs: js.Tree, rhs: js.Tree, code: Int)( implicit pos: Position): js.Tree = { - import scala.tools.nsc.backend.ScalaPrimitives._ - - val genLhs = genExpr(lhs) - val genRhs = genExpr(rhs) + import scala.tools.nsc.backend.ScalaPrimitivesOps._ val bypassEqEq = { // Do not call equals if we have a literal null at either side. - genLhs.isInstanceOf[js.Null] || - genRhs.isInstanceOf[js.Null] + lhs.isInstanceOf[js.Null] || + rhs.isInstanceOf[js.Null] } if (bypassEqEq) { js.BinaryOp( if (code == EQ) js.BinaryOp.=== else js.BinaryOp.!==, - genLhs, genRhs) + lhs, rhs) } else { - val body = genEqEqPrimitive(lhs.tpe, rhs.tpe, genLhs, genRhs) + val body = genEqEqPrimitive(ltpe, rtpe, lhs, rhs) if (code == EQ) body else js.UnaryOp(js.UnaryOp.Boolean_!, body) } @@ -1394,7 +1458,7 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen JS code for an array operation (get, set or length) */ private def genArrayOp(tree: Tree, code: Int): js.Tree = { - import scala.tools.nsc.backend.ScalaPrimitives._ + import scala.tools.nsc.backend.ScalaPrimitivesOps._ implicit val pos = tree.pos @@ -1408,8 +1472,9 @@ class JSCodeGen()(implicit ctx: Context) { case defn.ArrayOf(el) => el case JavaArrayType(el) => el case tpe => - ctx.error(s"expected Array $tpe") - ErrorType + val msg = ex"expected Array $tpe" + ctx.error(msg) + ErrorType(msg) } def genSelect(): js.Tree = @@ -1457,74 +1522,11 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen JS code for a coercion */ private def genCoercion(tree: Apply, receiver: Tree, code: Int): js.Tree = { - import scala.tools.nsc.backend.ScalaPrimitives._ - implicit val pos = tree.pos val source = genExpr(receiver) - - def source2int = (code: @switch) match { - case F2C | D2C | F2B | D2B | F2S | D2S | F2I | D2I => - js.UnaryOp(js.UnaryOp.DoubleToInt, source) - case L2C | L2B | L2S | L2I => - js.UnaryOp(js.UnaryOp.LongToInt, source) - case _ => - source - } - - (code: @switch) match { - // To Char, need to crop at unsigned 16-bit - case B2C | S2C | I2C | L2C | F2C | D2C => - js.BinaryOp(js.BinaryOp.Int_&, source2int, js.IntLiteral(0xffff)) - - // To Byte, need to crop at signed 8-bit - case C2B | S2B | I2B | L2B | F2B | D2B => - // note: & 0xff would not work because of negative values - js.BinaryOp(js.BinaryOp.Int_>>, - js.BinaryOp(js.BinaryOp.Int_<<, source2int, js.IntLiteral(24)), - js.IntLiteral(24)) - - // To Short, need to crop at signed 16-bit - case C2S | I2S | L2S | F2S | D2S => - // note: & 0xffff would not work because of negative values - js.BinaryOp(js.BinaryOp.Int_>>, - js.BinaryOp(js.BinaryOp.Int_<<, source2int, js.IntLiteral(16)), - js.IntLiteral(16)) - - // To Int, need to crop at signed 32-bit - case L2I | F2I | D2I => - source2int - - // Any int to Long - case C2L | B2L | S2L | I2L => - js.UnaryOp(js.UnaryOp.IntToLong, source) - - // Any double to Long - case F2L | D2L => - js.UnaryOp(js.UnaryOp.DoubleToLong, source) - - // Long to Double - case L2D => - js.UnaryOp(js.UnaryOp.LongToDouble, source) - - // Any int, or Double, to Float - case C2F | B2F | S2F | I2F | D2F => - js.UnaryOp(js.UnaryOp.DoubleToFloat, source) - - // Long to Float === Long to Double to Float - case L2F => - js.UnaryOp(js.UnaryOp.DoubleToFloat, - js.UnaryOp(js.UnaryOp.LongToDouble, source)) - - // Identities and IR upcasts - case C2C | B2B | S2S | I2I | L2L | F2F | D2D | - C2I | C2D | - B2S | B2I | B2D | - S2I | S2D | - I2D | - F2D => - source - } + val resultType = toIRType(tree.tpe) + adaptPrimitive(source, resultType) } /** Gen a call to the special `throw` method. */ @@ -1535,7 +1537,7 @@ class JSCodeGen()(implicit ctx: Context) { js.Throw { if (exception.tpe.widenDealias.typeSymbol.derivesFrom(jsdefn.JavaScriptExceptionClass)) { genModuleApplyMethod( - jsdefn.RuntimePackage_unwrapJavaScriptException, + jsdefn.Runtime_unwrapJavaScriptException, List(genException)) } else { genException @@ -1556,7 +1558,7 @@ class JSCodeGen()(implicit ctx: Context) { implicit val pos = tree.pos val fun = tree.fun match { - case fun: Ident => desugarIdent(fun).asInstanceOf[Select] + case fun: Ident => desugarIdent(fun).get case fun: Select => fun } val receiver = fun.qualifier @@ -1568,9 +1570,7 @@ class JSCodeGen()(implicit ctx: Context) { case _ => false } - if (sym.owner == defn.StringClass && !isStringMethodFromObject) { - genApplyMethodOfString(genExpr(receiver), sym, genActualArgs(sym, args)) - } else if (isJSType(sym.owner)) { + if (isJSType(sym.owner)) { //if (!isScalaJSDefinedJSClass(sym.owner) || isExposed(sym)) genApplyJSMethodGeneric(tree, sym, genExpr(receiver), genActualJSArgs(sym, args), isStat) /*else @@ -1597,8 +1597,8 @@ class JSCodeGen()(implicit ctx: Context) { * - Setters are translated to `Assign` to `JSBracketSelect` */ private def genApplyJSMethodGeneric(tree: Tree, sym: Symbol, - receiver: js.Tree, args: List[js.Tree], isStat: Boolean, - superIn: Option[Symbol] = None)( + receiver: js.Tree, args: List[js.TreeOrJSSpread], isStat: Boolean, + jsSuperClassValue: Option[js.Tree] = None)( implicit pos: Position): js.Tree = { implicit val pos = tree.pos @@ -1607,10 +1607,13 @@ class JSCodeGen()(implicit ctx: Context) { val argc = args.size // meaningful only for methods that don't have varargs def requireNotSuper(): Unit = { - if (superIn.isDefined) + if (jsSuperClassValue.isDefined) ctx.error("Illegal super call in Scala.js-defined JS class", tree.pos) } + def requireNotSpread(arg: js.TreeOrJSSpread): js.Tree = + arg.asInstanceOf[js.Tree] + def hasExplicitJSEncoding = { sym.hasAnnotation(jsdefn.JSNameAnnot) || sym.hasAnnotation(jsdefn.JSBracketAccessAnnot) || @@ -1624,7 +1627,7 @@ class JSCodeGen()(implicit ctx: Context) { case JSBinaryOpMethodName(code) if argc == 1 => requireNotSuper() - js.JSBinaryOp(code, receiver, args.head) + js.JSBinaryOp(code, receiver, requireNotSpread(args.head)) case nme.apply if !hasExplicitJSEncoding => requireNotSuper() @@ -1637,12 +1640,10 @@ class JSCodeGen()(implicit ctx: Context) { def jsFunName = js.StringLiteral(jsNameOf(sym)) def genSuperReference(propName: js.Tree): js.Tree = { - superIn.fold[js.Tree] { + jsSuperClassValue.fold[js.Tree] { js.JSBracketSelect(receiver, propName) - } { superInSym => - js.JSSuperBracketSelect( - jstpe.ClassType(encodeClassFullName(superInSym)), - receiver, propName) + } { superClassValue => + js.JSSuperBracketSelect(superClassValue, receiver, propName) } } @@ -1652,14 +1653,12 @@ class JSCodeGen()(implicit ctx: Context) { def genSelectSet(propName: js.Tree, value: js.Tree): js.Tree = js.Assign(genSuperReference(propName), value) - def genCall(methodName: js.Tree, args: List[js.Tree]): js.Tree = { - superIn.fold[js.Tree] { + def genCall(methodName: js.Tree, args: List[js.TreeOrJSSpread]): js.Tree = { + jsSuperClassValue.fold[js.Tree] { js.JSBracketMethodApply( receiver, methodName, args) - } { superInSym => - js.JSSuperBracketCall( - jstpe.ClassType(encodeClassFullName(superInSym)), - receiver, methodName, args) + } { superClassValue => + js.JSSuperBracketCall(superClassValue, receiver, methodName, args) } } @@ -1668,15 +1667,15 @@ class JSCodeGen()(implicit ctx: Context) { genSelectGet(jsFunName) } else if (isJSSetter(sym)) { assert(noSpread && argc == 1) - genSelectSet(jsFunName, args.head) + genSelectSet(jsFunName, requireNotSpread(args.head)) } else if (isJSBracketAccess(sym)) { assert(noSpread && (argc == 1 || argc == 2), s"@JSBracketAccess methods should have 1 or 2 non-varargs arguments") - args match { + (args: @unchecked) match { case List(keyArg) => - genSelectGet(keyArg) + genSelectGet(requireNotSpread(keyArg)) case List(keyArg, valueArg) => - genSelectSet(keyArg, valueArg) + genSelectSet(requireNotSpread(keyArg), requireNotSpread(valueArg)) } } else if (isJSBracketCall(sym)) { val (methodName, actualArgs) = extractFirstArg(args) @@ -1741,14 +1740,14 @@ class JSCodeGen()(implicit ctx: Context) { * This is nothing else than decomposing into head and tail, except that * we assert that the first element is not a JSSpread. */ - private def extractFirstArg(args: List[js.Tree]): (js.Tree, List[js.Tree]) = { + private def extractFirstArg(args: List[js.TreeOrJSSpread]): (js.Tree, List[js.TreeOrJSSpread]) = { assert(args.nonEmpty, "Trying to extract the first argument of an empty argument list") val firstArg = args.head assert(!firstArg.isInstanceOf[js.JSSpread], "Trying to extract the first argument of an argument list starting " + "with a Spread argument: " + firstArg) - (firstArg, args.tail) + (firstArg.asInstanceOf[js.Tree], args.tail) } /** Gen JS code for a call to a polymorphic method. @@ -1791,8 +1790,8 @@ class JSCodeGen()(implicit ctx: Context) { implicit val pos = tree.pos val genElems = tree.elems.map(genExpr) - val arrayType = toReferenceType(tree.tpe).asInstanceOf[jstpe.ArrayType] - js.ArrayValue(arrayType, genElems) + val arrayTypeRef = toTypeRef(tree.tpe).asInstanceOf[jstpe.ArrayTypeRef] + js.ArrayValue(jstpe.ArrayType(arrayTypeRef), genElems) } /** Gen JS code for a closure. @@ -1852,7 +1851,7 @@ class JSCodeGen()(implicit ctx: Context) { val qualifier = qualifierOf(fun) val allCaptureValues = qualifier :: env - val (formalCaptures, actualCaptures) = allCaptureValues.map { value => + val formalAndActualCaptures = allCaptureValues.map { value => implicit val pos = value.pos val formalIdent = value match { case Ident(name) => freshLocalIdent(name.toString) @@ -1863,17 +1862,20 @@ class JSCodeGen()(implicit ctx: Context) { js.ParamDef(formalIdent, toIRType(value.tpe), mutable = false, rest = false) val actualCapture = genExpr(value) (formalCapture, actualCapture) - }.unzip + } + val (formalCaptures, actualCaptures) = formalAndActualCaptures.unzip val formalParamNames = sym.info.paramNamess.flatten.drop(envSize) - val formalParamTypes = sym.info.paramTypess.flatten.drop(envSize) - val (formalParams, actualParams) = formalParamNames.zip(formalParamTypes).map { + val formalParamTypes = sym.info.paramInfoss.flatten.drop(envSize) + val formalParamNamesAndTypes = formalParamNames.zip(formalParamTypes) + val formalAndActualParams = formalParamNamesAndTypes.map { case (name, tpe) => val formalParam = js.ParamDef(freshLocalIdent(name.toString), jstpe.AnyType, mutable = false, rest = false) val actualParam = unbox(formalParam.ref, tpe) (formalParam, actualParam) - }.unzip + } + val (formalParams, actualParams) = formalAndActualParams.unzip val genBody = { val thisCaptureRef :: argCaptureRefs = formalCaptures.map(_.ref) @@ -1881,7 +1883,7 @@ class JSCodeGen()(implicit ctx: Context) { box(call, sym.info.finalResultType) } - val closure = js.Closure(formalCaptures, formalParams, genBody, actualCaptures) + val closure = js.Closure(arrow = true, formalCaptures, formalParams, genBody, actualCaptures) ctx.debuglog(closure.toString) val funInterfaceSym = functionalInterface.tpe.widenDealias.typeSymbol @@ -1967,7 +1969,7 @@ class JSCodeGen()(implicit ctx: Context) { */ value } else { - js.AsInstanceOf(value, toReferenceType(to)) + js.AsInstanceOf(value, toTypeRef(to)) } } @@ -1989,7 +1991,7 @@ class JSCodeGen()(implicit ctx: Context) { js.JSBinaryOp.instanceof, value, genLoadJSConstructor(sym)), 'Z') } } else { - js.IsInstanceOf(value, toReferenceType(to)) + js.IsInstanceOf(value, toTypeRef(to)) } } @@ -2038,67 +2040,29 @@ class JSCodeGen()(implicit ctx: Context) { genApplyMethod(genLoadModule(methodSym.owner), methodSym, arguments) } - /** Gen JS code for `new java.lang.String(...)`. - * - * Rewires the instantiation to calling the appropriate overload of - * `newString` in the object `scala.scalajs.runtime.RuntimeString`. - */ - private def genNewString(ctor: Symbol, arguments: List[js.Tree])( - implicit pos: Position): js.Tree = { - js.Apply( - genLoadModule(jsdefn.RuntimeStringModuleClass), - encodeRTStringCtorSym(ctor), arguments)( - jstpe.ClassType(ir.Definitions.StringClass)) - } - - /** Gen a dynamically linked call to a method of java.lang.String. - * - * Forwards the call to the module scala.scalajs.runtime.RuntimeString. - */ - private def genApplyMethodOfString(receiver: js.Tree, - methodSym: Symbol, arguments: List[js.Tree])( - implicit pos: Position): js.Tree = { - js.Apply( - genLoadModule(jsdefn.RuntimeStringModuleClass), - encodeRTStringMethodSym(methodSym), - receiver :: arguments)( - toIRType(patchedResultType(methodSym))) - } - /** Gen a boxing operation (tpe is the primitive type) */ private def makePrimitiveBox(expr: js.Tree, tpe: Type)( implicit pos: Position): js.Tree = { - toReferenceType(tpe) match { - case jstpe.ClassType(cls) if ir.Definitions.isPrimitiveClass(cls) => - assert(cls.length == 1) - (cls.charAt(0): @switch) match { - case 'V' => - // must be handled at least for JS interop - js.Block(expr, js.Undefined()) - case 'C' => - genModuleApplyMethod(jsdefn.BoxesRunTime_boxToCharacter, List(expr)) - case _ => - expr // box is identity for all non-Char types - } - - case _ => + toTypeRef(tpe) match { + case jstpe.ClassRef(ir.Definitions.VoidClass) => + js.Block(expr, js.Undefined()) + case jstpe.ClassRef(cls) if ir.Definitions.PrimitiveClasses.contains(cls) => + expr // box is identity for all non-Unit types + case typeRef => throw new FatalError( - s"makePrimitiveBox requires a primitive type, found $tpe at $pos") + s"makePrimitiveBox requires a primitive type, found $typeRef for $tpe at $pos") } } /** Gen an unboxing operation (tpe is the primitive type) */ private def makePrimitiveUnbox(expr: js.Tree, tpe: Type)( implicit pos: Position): js.Tree = { - toReferenceType(tpe) match { - case jstpe.ClassType(cls) if ir.Definitions.isPrimitiveClass(cls) => + toTypeRef(tpe) match { + case jstpe.ClassRef(cls) if ir.Definitions.PrimitiveClasses.contains(cls) => assert(cls.length == 1) - (cls.charAt(0): @switch) match { + cls.charAt(0) match { case 'V' => - // must be handled at least for JS interop expr - case 'C' => - genModuleApplyMethod(jsdefn.BoxesRunTime_unboxToChar, List(expr)) case primitiveCharCode => js.Unbox(expr, primitiveCharCode) } @@ -2157,23 +2121,25 @@ class JSCodeGen()(implicit ctx: Context) { * wrapped in a [[js.JSSpread]] node to be expanded at runtime. */ private def genActualJSArgs(sym: Symbol, args: List[Tree])( - implicit pos: Position): List[js.Tree] = { + implicit pos: Position): List[js.TreeOrJSSpread] = { def paramNamesAndTypes(implicit ctx: Context): List[(Names.TermName, Type)] = - sym.info.paramNamess.flatten.zip(sym.info.paramTypess.flatten) + sym.info.paramNamess.flatten.zip(sym.info.paramInfoss.flatten) val wereRepeated = ctx.atPhase(ctx.elimRepeatedPhase) { implicit ctx => - for ((name, tpe) <- paramNamesAndTypes) + val list = for ((name, tpe) <- paramNamesAndTypes) yield (name -> tpe.isRepeatedParam) - }.toMap + list.toMap + } val paramTypes = ctx.atPhase(ctx.elimErasedValueTypePhase) { implicit ctx => - paramNamesAndTypes - }.toMap + paramNamesAndTypes.toMap + } - var reversedArgs: List[js.Tree] = Nil + var reversedArgs: List[js.TreeOrJSSpread] = Nil - for ((arg, (paramName, paramType)) <- args.zip(paramNamesAndTypes)) { + val argsParamNamesAndTypes = args.zip(paramNamesAndTypes) + for ((arg, (paramName, paramType)) <- argsParamNamesAndTypes) { val wasRepeated = wereRepeated.getOrElse(paramName, false) if (wasRepeated) { reversedArgs = @@ -2181,7 +2147,7 @@ class JSCodeGen()(implicit ctx: Context) { } else { val unboxedArg = genExpr(arg) val boxedArg = unboxedArg match { - case js.UndefinedParam() => + case js.Transient(UndefinedParam) => unboxedArg case _ => val tpe = paramTypes.getOrElse(paramName, paramType) @@ -2191,18 +2157,18 @@ class JSCodeGen()(implicit ctx: Context) { } } - /* Remove all consecutive js.UndefinedParam's at the end of the argument + /* Remove all consecutive UndefinedParam's at the end of the argument * list. No check is performed whether they may be there, since they will * only be placed where default arguments can be anyway. */ - reversedArgs = reversedArgs.dropWhile(_.isInstanceOf[js.UndefinedParam]) + reversedArgs = reversedArgs.dropWhile(_.isInstanceOf[js.Transient]) - /* Find remaining js.UndefinedParam and replace by js.Undefined. This can + /* Find remaining UndefinedParam and replace by js.Undefined. This can * happen with named arguments or with multiple argument lists. */ reversedArgs = reversedArgs map { - case js.UndefinedParam() => js.Undefined() - case arg => arg + case js.Transient(UndefinedParam) => js.Undefined() + case arg => arg } reversedArgs.reverse @@ -2221,7 +2187,7 @@ class JSCodeGen()(implicit ctx: Context) { * Otherwise, it returns a `JSSpread` with the `Seq` converted to a * `js.Array`. */ - private def genJSRepeatedParam(arg: Tree): List[js.Tree] = { + private def genJSRepeatedParam(arg: Tree): List[js.TreeOrJSSpread] = { tryGenRepeatedParamAsJSArray(arg, handleNil = true).getOrElse { /* Fall back to calling runtime.genTraversableOnce2jsArray * to perform the conversion to js.Array, then wrap in a Spread @@ -2229,7 +2195,7 @@ class JSCodeGen()(implicit ctx: Context) { */ implicit val pos = arg.pos val jsArrayArg = genModuleApplyMethod( - jsdefn.RuntimePackage_genTraversableOnce2jsArray, + jsdefn.Runtime_toJSVarArgs, List(genExpr(arg))) List(js.JSSpread(jsArrayArg)) } @@ -2278,11 +2244,10 @@ class JSCodeGen()(implicit ctx: Context) { private object WrapArray { lazy val isWrapArray: Set[Symbol] = { - val names = { - defn.ScalaValueClasses().map(sym => nme.wrapXArray(sym.name)) ++ - Set(nme.wrapRefArray, nme.genericWrapArray) - } - names.map(defn.ScalaPredefModule.requiredMethod(_)).toSet + val names0 = defn.ScalaValueClasses().map(sym => nme.wrapXArray(sym.name)) + val names1 = names0 ++ Set(nme.wrapRefArray, nme.genericWrapArray) + val names2 = names1.map(defn.ScalaPredefModule.requiredMethod(_)) + names2.toSet } def unapply(tree: Apply): Option[Tree] = tree match { @@ -2318,16 +2283,13 @@ class JSCodeGen()(implicit ctx: Context) { private def genLoadModule(sym0: Symbol)(implicit pos: Position): js.Tree = { require(sym0.is(Module), "genLoadModule called with non-module symbol: " + sym0) - val sym1 = if (sym0.isTerm) sym0.moduleClass else sym0 - val sym = // redirect all static methods of String to RuntimeString - if (sym1 == defn.StringModule) jsdefn.RuntimeStringModule.moduleClass - else sym1 + val sym = if (sym0.isTerm) sym0.moduleClass else sym0 if (isJSType(sym)) { if (isScalaJSDefinedJSClass(sym)) js.LoadJSModule(jstpe.ClassType(encodeClassFullName(sym))) - else if (sym.derivesFrom(jsdefn.JSGlobalScopeClass)) - genLoadJSGlobal() + /*else if (sym.derivesFrom(jsdefn.JSGlobalScopeClass)) + genLoadJSGlobal()*/ else genLoadNativeJSModule(sym) } else { @@ -2362,7 +2324,7 @@ class JSCodeGen()(implicit ctx: Context) { /** Generate a Class[_] value (e.g. coming from classOf[T]) */ private def genClassConstant(tpe: Type)(implicit pos: Position): js.Tree = - js.ClassOf(toReferenceType(tpe)) + js.ClassOf(toTypeRef(tpe)) private def isStaticModule(sym: Symbol): Boolean = sym.is(Module) && sym.isStatic @@ -2374,4 +2336,44 @@ class JSCodeGen()(implicit ctx: Context) { } } + protected lazy val isHijackedClass: Set[Symbol] = { + /* This list is a duplicate of ir.Definitions.HijackedClasses, but + * with global.Symbol's instead of IR encoded names as Strings. + */ + Set[Symbol]( + defn.BoxedUnitClass, defn.BoxedBooleanClass, defn.BoxedCharClass, defn.BoxedByteClass, + defn.BoxedShortClass, defn.BoxedIntClass, defn.BoxedLongClass, defn.BoxedFloatClass, + defn.BoxedDoubleClass, defn.StringClass + ) + } + + // Copied from DottyBackendInterface + + private val desugared = new java.util.IdentityHashMap[Type, tpd.Select] + + def desugarIdent(i: Ident): Option[tpd.Select] = { + var found = desugared.get(i.tpe) + if (found == null) { + tpd.desugarIdent(i) match { + case sel: tpd.Select => + desugared.put(i.tpe, sel) + found = sel + case _ => + } + } + if (found == null) None else Some(found) + } +} + +object JSCodeGen { + + /** Marker object for undefined parameters in JavaScript semantic calls. + * + * To be used inside a `js.Transient` node. + */ + case object UndefinedParam extends js.Transient.Value { + def printIR(out: ir.Printers.IRTreePrinter): Unit = + out.print("") + } + } diff --git a/compiler/sjs/backend/sjs/JSDefinitions.scala b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala similarity index 68% rename from compiler/sjs/backend/sjs/JSDefinitions.scala rename to compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala index bd0b740318e0..e19834cdc4f5 100644 --- a/compiler/sjs/backend/sjs/JSDefinitions.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala @@ -30,8 +30,6 @@ final class JSDefinitions()(implicit ctx: Context) { def JSPackage_typeOf(implicit ctx: Context) = JSPackage_typeOfR.symbol lazy val JSPackage_constructorOfR = ScalaJSJSPackageClass.requiredMethodRef("constructorOf") def JSPackage_constructorOf(implicit ctx: Context) = JSPackage_constructorOfR.symbol - lazy val JSPackage_debuggerR = ScalaJSJSPackageClass.requiredMethodRef("debugger") - def JSPackage_debugger(implicit ctx: Context) = JSPackage_debuggerR.symbol lazy val JSPackage_nativeR = ScalaJSJSPackageClass.requiredMethodRef("native") def JSPackage_native(implicit ctx: Context) = JSPackage_nativeR.symbol @@ -45,14 +43,6 @@ final class JSDefinitions()(implicit ctx: Context) { lazy val JSBaseThisFunctionType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.ThisFunction") def JSBaseThisFunctionClass(implicit ctx: Context) = JSBaseThisFunctionType.symbol.asClass - lazy val JSDictionaryType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.Dictionary") - def JSDictionaryClass(implicit ctx: Context) = JSDictionaryType.symbol.asClass - lazy val JSDictionary_deleteR = JSDictionaryClass.requiredMethodRef("delete") - def JSDictionary_delete(implicit ctx: Context) = JSDictionary_deleteR.symbol - - lazy val JSGlobalScopeType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.GlobalScope") - def JSGlobalScopeClass(implicit ctx: Context) = JSGlobalScopeType.symbol.asClass - lazy val JSArrayType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.Array") def JSArrayClass(implicit ctx: Context) = JSArrayType.symbol.asClass @@ -108,10 +98,6 @@ final class JSDefinitions()(implicit ctx: Context) { lazy val JSObjectModuleRef = ctx.requiredModuleRef("scala.scalajs.js.Object") def JSObjectModule(implicit ctx: Context) = JSObjectModuleRef.symbol - lazy val JSObject_hasPropertyR = JSObjectModule.requiredMethodRef("hasProperty") - def JSObject_hasProperty(implicit ctx: Context) = JSObject_hasPropertyR.symbol - lazy val JSObject_propertiesR = JSObjectModule.requiredMethodRef("properties") - def JSObject_properties(implicit ctx: Context) = JSObject_propertiesR.symbol lazy val JSArrayModuleRef = ctx.requiredModuleRef("scala.scalajs.js.Array") def JSArrayModule(implicit ctx: Context) = JSArrayModuleRef.symbol @@ -128,39 +114,35 @@ final class JSDefinitions()(implicit ctx: Context) { lazy val JSConstructorTag_materializeR = JSConstructorTagModule.requiredMethodRef("materialize") def JSConstructorTag_materialize(implicit ctx: Context) = JSConstructorTag_materializeR.symbol - lazy val RuntimeStringModuleRef = ctx.requiredModuleRef("scala.scalajs.runtime.RuntimeString") - def RuntimeStringModule(implicit ctx: Context) = RuntimeStringModuleRef.symbol - def RuntimeStringModuleClass(implicit ctx: Context) = RuntimeStringModule.moduleClass.asClass - - lazy val BooleanReflectiveCallType: TypeRef = ctx.requiredClassRef("scala.scalajs.runtime.BooleanReflectiveCall") - def BooleanReflectiveCallClass(implicit ctx: Context) = BooleanReflectiveCallType.symbol.asClass - lazy val NumberReflectiveCallType: TypeRef = ctx.requiredClassRef("scala.scalajs.runtime.NumberReflectiveCall") - def NumberReflectiveCallClass(implicit ctx: Context) = NumberReflectiveCallType.symbol.asClass - lazy val IntegerReflectiveCallType: TypeRef = ctx.requiredClassRef("scala.scalajs.runtime.IntegerReflectiveCall") - def IntegerReflectiveCallClass(implicit ctx: Context) = IntegerReflectiveCallType.symbol.asClass - lazy val LongReflectiveCallType: TypeRef = ctx.requiredClassRef("scala.scalajs.runtime.LongReflectiveCall") - def LongReflectiveCallClass(implicit ctx: Context) = LongReflectiveCallType.symbol.asClass - lazy val RuntimePackageVal = ctx.requiredPackage("scala.scalajs.runtime") lazy val RuntimePackageClass = RuntimePackageVal.moduleClass.asClass lazy val RuntimePackage_wrapJavaScriptExceptionR = RuntimePackageClass.requiredMethodRef("wrapJavaScriptException") - def RuntimePackage_typeOf(implicit ctx: Context) = RuntimePackage_wrapJavaScriptExceptionR.symbol - lazy val RuntimePackage_unwrapJavaScriptExceptionR = RuntimePackageClass.requiredMethodRef("unwrapJavaScriptException") - def RuntimePackage_unwrapJavaScriptException(implicit ctx: Context) = RuntimePackage_unwrapJavaScriptExceptionR.symbol - lazy val RuntimePackage_genTraversableOnce2jsArrayR = RuntimePackageClass.requiredMethodRef("genTraversableOnce2jsArray") - def RuntimePackage_genTraversableOnce2jsArray(implicit ctx: Context) = RuntimePackage_genTraversableOnce2jsArrayR.symbol - lazy val RuntimePackage_jsTupleArray2jsObjectR = RuntimePackageClass.requiredMethodRef("jsTupleArray2jsObject") - def RuntimePackage_jsTupleArray2jsObject(implicit ctx: Context) = RuntimePackage_jsTupleArray2jsObjectR.symbol - lazy val RuntimePackage_constructorOfR = RuntimePackageClass.requiredMethodRef("constructorOf") - def RuntimePackage_constructorOf(implicit ctx: Context) = RuntimePackage_constructorOfR.symbol - lazy val RuntimePackage_newConstructorTagR = RuntimePackageClass.requiredMethodRef("newConstructorTag") - def RuntimePackage_newConstructorTag(implicit ctx: Context) = RuntimePackage_newConstructorTagR.symbol - lazy val RuntimePackage_propertiesOfR = RuntimePackageClass.requiredMethodRef("propertiesOf") - def RuntimePackage_propertiesOf(implicit ctx: Context) = RuntimePackage_propertiesOfR.symbol - lazy val RuntimePackage_environmentInfoR = RuntimePackageClass.requiredMethodRef("environmentInfo") - def RuntimePackage_environmentInfo(implicit ctx: Context) = RuntimePackage_environmentInfoR.symbol - lazy val RuntimePackage_linkingInfoR = RuntimePackageClass.requiredMethodRef("linkingInfo") - def RuntimePackage_linkingInfo(implicit ctx: Context) = RuntimePackage_linkingInfoR.symbol + def Runtime_wrapJavaScriptException(implicit ctx: Context) = RuntimePackage_wrapJavaScriptExceptionR.symbol + lazy val Runtime_unwrapJavaScriptExceptionR = RuntimePackageClass.requiredMethodRef("unwrapJavaScriptException") + def Runtime_unwrapJavaScriptException(implicit ctx: Context) = Runtime_unwrapJavaScriptExceptionR.symbol + lazy val Runtime_toScalaVarArgsR = RuntimePackageClass.requiredMethodRef("toScalaVarArgs") + def Runtime_toScalaVarArgs(implicit ctx: Context) = Runtime_toScalaVarArgsR.symbol + lazy val Runtime_toJSVarArgsR = RuntimePackageClass.requiredMethodRef("toJSVarArgs") + def Runtime_toJSVarArgs(implicit ctx: Context) = Runtime_toJSVarArgsR.symbol + lazy val Runtime_constructorOfR = RuntimePackageClass.requiredMethodRef("constructorOf") + def Runtime_constructorOf(implicit ctx: Context) = Runtime_constructorOfR.symbol + lazy val Runtime_newConstructorTagR = RuntimePackageClass.requiredMethodRef("newConstructorTag") + def Runtime_newConstructorTag(implicit ctx: Context) = Runtime_newConstructorTagR.symbol + lazy val Runtime_linkingInfoR = RuntimePackageClass.requiredMethodRef("linkingInfo") + def Runtime_linkingInfo(implicit ctx: Context) = Runtime_linkingInfoR.symbol + + lazy val SpecialPackageVal = ctx.requiredPackage("scala.scalajs.js.special") + lazy val SpecialPackageClass = SpecialPackageVal.moduleClass.asClass + lazy val Special_debuggerR = SpecialPackageClass.requiredMethodRef("debugger") + def Special_debugger(implicit ctx: Context) = Special_debuggerR.symbol + lazy val Special_deleteR = SpecialPackageClass.requiredMethodRef("delete") + def Special_delete(implicit ctx: Context) = Special_deleteR.symbol + lazy val Special_forinR = SpecialPackageClass.requiredMethodRef("forin") + def Special_forin(implicit ctx: Context) = Special_forinR.symbol + lazy val Special_inR = SpecialPackageClass.requiredMethodRef("in") + def Special_in(implicit ctx: Context) = Special_inR.symbol + lazy val Special_instanceofR = SpecialPackageClass.requiredMethodRef("instanceof") + def Special_instanceof(implicit ctx: Context) = Special_instanceofR.symbol lazy val WrappedArrayType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.WrappedArray") def WrappedArrayClass(implicit ctx: Context) = WrappedArrayType.symbol.asClass @@ -183,17 +165,15 @@ final class JSDefinitions()(implicit ctx: Context) { * * This is similar to `isVarArityClass` in `Definitions.scala`. */ - private def isScalaJSVarArityClass(cls: Symbol, prefix: Name): Boolean = { + private def isScalaJSVarArityClass(cls: Symbol, prefix: String): Boolean = { val name = scalajsClassName(cls) - name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit) + name.startsWith(prefix) && name.toString.drop(prefix.length).forall(_.isDigit) } def isJSFunctionClass(cls: Symbol): Boolean = - isScalaJSVarArityClass(cls, nme.Function) - - private val ThisFunctionName = termName("ThisFunction") + isScalaJSVarArityClass(cls, str.Function) def isJSThisFunctionClass(cls: Symbol): Boolean = - isScalaJSVarArityClass(cls, ThisFunctionName) + isScalaJSVarArityClass(cls, "ThisFunction") } diff --git a/compiler/sjs/backend/sjs/JSEncoding.scala b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala similarity index 77% rename from compiler/sjs/backend/sjs/JSEncoding.scala rename to compiler/src/dotty/tools/backend/sjs/JSEncoding.scala index 8644cdaf5760..95ee81515edf 100644 --- a/compiler/sjs/backend/sjs/JSEncoding.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala @@ -12,9 +12,10 @@ import Types._ import Symbols._ import Denotations._ import NameOps._ +import Names._ import StdNames._ -import org.scalajs.core.ir +import org.scalajs.ir import ir.{Trees => js, Types => jstpe} import ScopedVar.withScopedVars @@ -54,6 +55,7 @@ object JSEncoding { private val usedLocalNames = mutable.Set.empty[String] private val localSymbolNames = mutable.Map.empty[Symbol, String] + private var returnLabelName: Option[String] = None def localSymbolName(sym: Symbol)(implicit ctx: Context): String = localSymbolNames.getOrElseUpdate(sym, freshName(sym.name.toString)) @@ -74,6 +76,15 @@ object JSEncoding { usedLocalNames += longName mangleJSName(longName) } + + def getEnclosingReturnLabel()(implicit pos: ir.Position): js.Ident = { + /*val box = returnLabelName.get + if (box == null) + throw new IllegalStateException(s"No enclosing returnable scope at $pos")*/ + if (returnLabelName.isEmpty) + returnLabelName = Some(freshName("_return")) + js.Ident(returnLabelName.get) + } } private object LocalNameGenerator { @@ -134,40 +145,6 @@ object JSEncoding { encodedName + paramsString } - /** Encodes a method symbol of java.lang.String for use in RuntimeString. - * - * This basically means adding an initial parameter of type - * java.lang.String, which is the `this` parameter. - */ - def encodeRTStringMethodSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - require(sym.owner == defn.StringClass) - require(!sym.isClassConstructor && !sym.is(Flags.Private)) - - val (encodedName, paramsString) = - encodeMethodNameInternal(sym, inRTClass = true) - js.Ident(encodedName + paramsString, - Some(sym.unexpandedName.decoded + paramsString)) - } - - /** Encodes a constructor symbol of java.lang.String for use in RuntimeString. - * - * - The name is rerouted to `newString` - * - The result type is set to `java.lang.String` - */ - def encodeRTStringCtorSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - require(sym.owner == defn.StringClass) - require(sym.isClassConstructor && !sym.is(Flags.Private)) - - val paramTypeNames = sym.info.firstParamTypes.map(internalName(_)) - val paramAndResultTypeNames = paramTypeNames :+ ir.Definitions.StringClass - val paramsString = makeParamsString(paramAndResultTypeNames) - - js.Ident("newString" + paramsString, - Some(sym.unexpandedName.decoded + paramsString)) - } - private def encodeMethodNameInternal(sym: Symbol, reflProxy: Boolean = false, inRTClass: Boolean = false)( implicit ctx: Context): (String, String) = { @@ -209,7 +186,7 @@ object JSEncoding { } def foreignIsImplClass(sym: Symbol)(implicit ctx: Context): Boolean = - sym.name.isImplClassName + sym.name.endsWith(nme.IMPL_CLASS_SUFFIX.toString) def encodeClassType(sym: Symbol)(implicit ctx: Context): jstpe.Type = { if (sym == defn.ObjectClass) jstpe.AnyType @@ -226,11 +203,8 @@ object JSEncoding { js.Ident(encodeClassFullName(sym), Some(sym.fullName.toString)) } - def encodeClassFullName(sym: Symbol)(implicit ctx: Context): String = { - if (sym == defn.NothingClass) ir.Definitions.RuntimeNothingClass - else if (sym == defn.NullClass) ir.Definitions.RuntimeNullClass - else ir.Definitions.encodeClassName(sym.fullName.toString) - } + def encodeClassFullName(sym: Symbol)(implicit ctx: Context): String = + ir.Definitions.encodeClassName(sym.fullName.toString) private def encodeMemberNameInternal(sym: Symbol)( implicit ctx: Context): String = { @@ -238,10 +212,10 @@ object JSEncoding { } def toIRType(tp: Type)(implicit ctx: Context): jstpe.Type = { - val refType = toReferenceTypeInternal(tp) - refType._1 match { - case tpe: jstpe.ClassType => - val sym = refType._2 + val typeRefInternal = toTypeRefInternal(tp) + typeRefInternal._1 match { + case typeRef: jstpe.ClassRef => + val sym = typeRefInternal._2 if (sym.asClass.isPrimitiveValueClass) { if (sym == defn.BooleanClass) jstpe.BooleanType @@ -263,57 +237,66 @@ object JSEncoding { else if (sym == defn.NullClass) jstpe.NullType else - tpe + jstpe.ClassType(typeRef.className) } - case tpe: jstpe.ArrayType => - tpe + case typeRef: jstpe.ArrayTypeRef => + jstpe.ArrayType(typeRef) } } - def toReferenceType(tp: Type)(implicit ctx: Context): jstpe.ReferenceType = - toReferenceTypeInternal(tp)._1 - - private def toReferenceTypeInternal(tp: Type)( - implicit ctx: Context): (jstpe.ReferenceType, Symbol) = { + def toTypeRef(tp: Type)(implicit ctx: Context): jstpe.TypeRef = + toTypeRefInternal(tp)._1 + private def toTypeRefInternal(tp: Type)(implicit ctx: Context): (jstpe.TypeRef, Symbol) = { /** * Primitive types are represented as TypeRefs to the class symbol of, for example, scala.Int. * The `primitiveTypeMap` maps those class symbols to the corresponding PrimitiveBType. */ - def primitiveOrClassToRefType(sym: Symbol): (jstpe.ReferenceType, Symbol) = { + def primitiveOrClassToTypeRef(sym: Symbol): (jstpe.TypeRef, Symbol) = { assert(sym.isClass, sym) //assert(sym != defn.ArrayClass || isCompilingArray, sym) - (jstpe.ClassType(encodeClassFullName(sym)), sym) + val className = if (sym.isPrimitiveValueClass) { + if (sym == defn.UnitClass) ir.Definitions.VoidClass + else if (sym == defn.BooleanClass) ir.Definitions.BooleanClass + else if (sym == defn.CharClass) ir.Definitions.CharClass + else if (sym == defn.ByteClass) ir.Definitions.ByteClass + else if (sym == defn.ShortClass) ir.Definitions.ShortClass + else if (sym == defn.IntClass) ir.Definitions.IntClass + else if (sym == defn.LongClass) ir.Definitions.LongClass + else if (sym == defn.FloatClass) ir.Definitions.FloatClass + else if (sym == defn.DoubleClass) ir.Definitions.DoubleClass + else throw new Exception(s"unknown primitive value class $sym") + } else { + encodeClassFullName(sym) + } + (jstpe.ClassRef(className), sym) } /** * When compiling Array.scala, the type parameter T is not erased and shows up in method * signatures, e.g. `def apply(i: Int): T`. A TyperRef to T is replaced by ObjectReference. */ - def nonClassTypeRefToRefType(sym: Symbol): (jstpe.ReferenceType, Symbol) = { + def nonClassTypeRefToTypeRef(sym: Symbol): (jstpe.TypeRef, Symbol) = { //assert(sym.isType && isCompilingArray, sym) - (jstpe.ClassType(ir.Definitions.ObjectClass), defn.ObjectClass) + (jstpe.ClassRef(ir.Definitions.ObjectClass), defn.ObjectClass) } tp.widenDealias match { // Array type such as Array[Int] (kept by erasure) case JavaArrayType(el) => - val elRefType = toReferenceTypeInternal(el) - (jstpe.ArrayType(elRefType._1), elRefType._2) + val elTypeRef = toTypeRefInternal(el) + (jstpe.ArrayTypeRef.of(elTypeRef._1), elTypeRef._2) case t: TypeRef => - if (!t.symbol.isClass) nonClassTypeRefToRefType(t.symbol) // See comment on nonClassTypeRefToBType - else primitiveOrClassToRefType(t.symbol) // Common reference to a type such as scala.Int or java.lang.String + if (!t.symbol.isClass) nonClassTypeRefToTypeRef(t.symbol) // See comment on nonClassTypeRefToBType + else primitiveOrClassToTypeRef(t.symbol) // Common reference to a type such as scala.Int or java.lang.String case Types.ClassInfo(_, sym, _, _, _) => /* We get here, for example, for genLoadModule, which invokes * toTypeKind(moduleClassSymbol.info) */ - primitiveOrClassToRefType(sym) - - case t: MethodType => // triggers for LabelDefs - toReferenceTypeInternal(t.resultType) + primitiveOrClassToTypeRef(sym) /* AnnotatedType should (probably) be eliminated by erasure. However we know it happens for * meta-annotated annotations (@(ann @getter) val x = 0), so we don't emit a warning. @@ -321,7 +304,7 @@ object JSEncoding { */ case a @ AnnotatedType(t, _) => //debuglog(s"typeKind of annotated type $a") - toReferenceTypeInternal(t) + toTypeRefInternal(t) } } @@ -369,14 +352,14 @@ object JSEncoding { /** Computes the internal name for a type. */ private def internalName(tpe: Type)(implicit ctx: Context): String = - encodeReferenceType(toReferenceType(tpe)) + encodeTypeRef(toTypeRef(tpe)) - /** Encodes a [[Types.ReferenceType]], such as in an encoded method signature. + /** Encodes a [[Types.TypeRef]], such as in an encoded method signature. */ - private def encodeReferenceType(refType: jstpe.ReferenceType): String = { - refType match { - case jstpe.ClassType(encodedName) => encodedName - case jstpe.ArrayType(base, depth) => "A" * depth + base + private def encodeTypeRef(typeRef: jstpe.TypeRef): String = { + typeRef match { + case jstpe.ClassRef(className) => className + case jstpe.ArrayTypeRef(base, depth) => "A" * depth + base } } diff --git a/compiler/sjs/backend/sjs/JSInterop.scala b/compiler/src/dotty/tools/backend/sjs/JSInterop.scala similarity index 90% rename from compiler/sjs/backend/sjs/JSInterop.scala rename to compiler/src/dotty/tools/backend/sjs/JSInterop.scala index 6d66c32064ee..e984489cf51d 100644 --- a/compiler/sjs/backend/sjs/JSInterop.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSInterop.scala @@ -6,6 +6,7 @@ import Flags._ import Symbols._ import NameOps._ import StdNames._ +import NameKinds.DefaultGetterName import JSDefinitions._ @@ -66,11 +67,17 @@ object JSInterop { * is a JS type. */ def isJSDefaultParam(sym: Symbol)(implicit ctx: Context): Boolean = { - sym.name.isDefaultGetterName && { + sym.name.is(DefaultGetterName) && { val owner = sym.owner - if (owner.is(ModuleClass) && - sym.name.asTermName.defaultGetterToMethod == nme.CONSTRUCTOR) { - isJSType(owner.linkedClass) + if (owner.is(ModuleClass)) { + val isConstructor = sym.name match { + case DefaultGetterName(methName, _) => methName == nme.CONSTRUCTOR + case _ => false + } + if (isConstructor) + isJSType(owner.linkedClass) + else + isJSType(owner) } else { isJSType(owner) } diff --git a/compiler/sjs/backend/sjs/JSPositions.scala b/compiler/src/dotty/tools/backend/sjs/JSPositions.scala similarity index 98% rename from compiler/sjs/backend/sjs/JSPositions.scala rename to compiler/src/dotty/tools/backend/sjs/JSPositions.scala index 019b87f32818..0e2bae540d97 100644 --- a/compiler/sjs/backend/sjs/JSPositions.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSPositions.scala @@ -5,7 +5,7 @@ import Contexts._ import dotty.tools.dotc.util.Positions import Positions.Position -import org.scalajs.core.ir +import org.scalajs.ir /** Conversion utilities from dotty Positions to IR Positions. */ class JSPositions()(implicit ctx: Context) { diff --git a/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala b/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala new file mode 100644 index 000000000000..26f52a242a50 --- /dev/null +++ b/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala @@ -0,0 +1,111 @@ +package dotty.tools.backend.sjs + +import dotty.tools.dotc.core._ +import Names.TermName +import StdNames._ +import Types._ +import Contexts._ +import Symbols._ + +import dotty.tools.dotc.ast.tpd._ +import dotty.tools.backend.jvm.DottyPrimitives + +import scala.collection.mutable + +object JSPrimitives { + + final val FirstJSPrimitiveCode = 300 + + final val DYNNEW = FirstJSPrimitiveCode + 1 // Instantiate a new JavaScript object + + final val ARR_CREATE = DYNNEW + 1 // js.Array.apply (array literal syntax) + + final val TYPEOF = ARR_CREATE + 1 // typeof x + final val JS_NATIVE = TYPEOF + 1 // js.native. Marker method. Fails if tried to be emitted. + + final val UNITVAL = JS_NATIVE + 1 // () value, which is undefined + + final val CONSTRUCTOROF = UNITVAL + 1 // runtime.constructorOf(clazz) + final val CREATE_INNER_JS_CLASS = CONSTRUCTOROF + 1 // runtime.createInnerJSClass + final val CREATE_LOCAL_JS_CLASS = CREATE_INNER_JS_CLASS + 1 // runtime.createLocalJSClass + final val WITH_CONTEXTUAL_JS_CLASS_VALUE = CREATE_LOCAL_JS_CLASS + 1 // runtime.withContextualJSClassValue + final val LINKING_INFO = WITH_CONTEXTUAL_JS_CLASS_VALUE + 1 // runtime.linkingInfo + + final val IN = LINKING_INFO + 1 // js.special.in + final val INSTANCEOF = IN + 1 // js.special.instanceof + final val DELETE = INSTANCEOF + 1 // js.special.delete + final val FORIN = DELETE + 1 // js.special.forin + final val DEBUGGER = FORIN + 1 // js.special.debugger + + final val THROW = DEBUGGER + 1 + + final val LastJSPrimitiveCode = THROW + +} + +class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) { + import JSPrimitives._ + import scala.tools.nsc.backend.ScalaPrimitivesOps._ + + private lazy val jsPrimitives: Map[Symbol, Int] = initJSPrimitives(ctx) + + override def getPrimitive(sym: Symbol): Int = + jsPrimitives.getOrElse(sym, super.getPrimitive(sym)) + + override def getPrimitive(app: Apply, tpe: Type)(implicit ctx: Context): Int = + jsPrimitives.getOrElse(app.fun.symbol, super.getPrimitive(app, tpe)) + + override def isPrimitive(fun: Tree): Boolean = + jsPrimitives.contains(fun.symbol(ctx)) || super.isPrimitive(fun) + + /** Initialize the primitive map */ + private def initJSPrimitives(implicit ctx: Context): Map[Symbol, Int] = { + + val primitives = newMutableSymbolMap[Int] + + // !!! Code duplicate with DottyPrimitives + /** Add a primitive operation to the map */ + def addPrimitive(s: Symbol, code: Int): Unit = { + assert(!(primitives contains s), "Duplicate primitive " + s) + primitives(s) = code + } + + def addPrimitives(cls: Symbol, method: TermName, code: Int)(implicit ctx: Context): Unit = { + val alts = cls.info.member(method).alternatives.map(_.symbol) + if (alts.isEmpty) { + ctx.error(s"Unknown primitive method $cls.$method") + } else { + for (s <- alts) + addPrimitive(s, code) + } + } + + val jsdefn = JSDefinitions.jsdefn + + addPrimitive(jsdefn.JSDynamic_newInstance, DYNNEW) + + addPrimitive(jsdefn.JSArray_apply, ARR_CREATE) + + addPrimitive(jsdefn.JSPackage_typeOf, TYPEOF) + addPrimitive(jsdefn.JSPackage_native, JS_NATIVE) + + addPrimitive(defn.BoxedUnit_UNIT, UNITVAL) + + addPrimitive(jsdefn.Runtime_constructorOf, CONSTRUCTOROF) + /*addPrimitive(jsdefn.Runtime_createInnerJSClass, CREATE_INNER_JS_CLASS) + addPrimitive(jsdefn.Runtime_createLocalJSClass, CREATE_LOCAL_JS_CLASS) + addPrimitive(jsdefn.Runtime_withContextualJSClassValue, WITH_CONTEXTUAL_JS_CLASS_VALUE)*/ + addPrimitive(jsdefn.Runtime_linkingInfo, LINKING_INFO) + + addPrimitive(jsdefn.Special_in, IN) + addPrimitive(jsdefn.Special_instanceof, INSTANCEOF) + addPrimitive(jsdefn.Special_delete, DELETE) + addPrimitive(jsdefn.Special_forin, FORIN) + addPrimitive(jsdefn.Special_debugger, DEBUGGER) + + addPrimitive(defn.throwMethod, THROW) + + primitives.toMap + } + +} diff --git a/compiler/sjs/backend/sjs/ScopedVar.scala b/compiler/src/dotty/tools/backend/sjs/ScopedVar.scala similarity index 96% rename from compiler/sjs/backend/sjs/ScopedVar.scala rename to compiler/src/dotty/tools/backend/sjs/ScopedVar.scala index ac2dacd4a7df..f8185697e15d 100644 --- a/compiler/sjs/backend/sjs/ScopedVar.scala +++ b/compiler/src/dotty/tools/backend/sjs/ScopedVar.scala @@ -5,7 +5,7 @@ import language.implicitConversions class ScopedVar[A](init: A) { import ScopedVar.Assignment - private[this] var value = init + private[ScopedVar] var value = init def this()(implicit ev: Null <:< A) = this(ev(null)) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index ec2a870e8c87..0ae262dfff8d 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -7,6 +7,7 @@ import typer.{FrontEnd, RefChecks} import Phases.Phase import transform._ import dotty.tools.backend.jvm.{CollectSuperCalls, GenBCode} +import dotty.tools.backend.sjs import dotty.tools.dotc.transform.localopt.StringInterpolatorOpt /** The central class of the dotc compiler. The job of a compiler is to create @@ -113,6 +114,7 @@ class Compiler { /** Generate the output of the compilation */ protected def backendPhases: List[List[Phase]] = + List(new sjs.GenSJSIR) :: // Generate .sjsir files for Scala.js (not enabled by default) List(new GenBCode) :: // Generate JVM bytecode Nil diff --git a/compiler/sjs/tools/dotc/config/SJSPlatform.scala b/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala similarity index 100% rename from compiler/sjs/tools/dotc/config/SJSPlatform.scala rename to compiler/src/dotty/tools/dotc/config/SJSPlatform.scala diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index bcd80977efd5..398325c15dd0 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -34,6 +34,7 @@ class ScalaSettings extends Settings.SettingGroup { val color: Setting[String] = ChoiceSetting("-color", "mode", "Colored output", List("always", "never"/*, "auto"*/), "always"/* "auto"*/) val target: Setting[String] = ChoiceSetting("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.", List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "jvm-1.8", "msil"), "jvm-1.8") + val scalajs: Setting[Boolean] = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).") val unchecked: Setting[Boolean] = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.") val uniqid: Setting[Boolean] = BooleanSetting("-uniqid", "Uniquely tag all identifiers in debugging output.") val usejavacp: Setting[Boolean] = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.") diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 7ea68b0ae816..93c92522a407 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -22,7 +22,7 @@ import reporting._ import reporting.diagnostic.Message import collection.mutable import printing._ -import config.{JavaPlatform, Platform, ScalaSettings} +import config.{JavaPlatform, SJSPlatform, Platform, ScalaSettings} import scala.annotation.internal.sharable @@ -590,7 +590,8 @@ object Contexts { } protected def newPlatform(implicit ctx: Context): Platform = - new JavaPlatform + if (settings.scalajs.value) new SJSPlatform + else new JavaPlatform /** The loader that loads the members of _root_ */ def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root) diff --git a/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala b/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala index 069eea1644db..94c6b3d233ff 100644 --- a/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala +++ b/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala @@ -33,7 +33,8 @@ class FunctionalInterfaces extends MiniPhase { val List(implParamTypes) = implType.paramInfoss val implResultType = implType.resultType - if (defn.isSpecializableFunction(cls, implParamTypes, implResultType)) { + if (defn.isSpecializableFunction(cls, implParamTypes, implResultType) && + !ctx.settings.scalajs.value) { // never do anything for Scala.js, but do this test as late as possible not to slow down Scala/JVM val names = ctx.atPhase(ctx.erasurePhase) { implicit ctx => cls.typeParams.map(_.name) } diff --git a/compiler/src/dotty/tools/dotc/transform/GetClass.scala b/compiler/src/dotty/tools/dotc/transform/GetClass.scala index 60e47d077aa0..77ba6d1b618d 100644 --- a/compiler/src/dotty/tools/dotc/transform/GetClass.scala +++ b/compiler/src/dotty/tools/dotc/transform/GetClass.scala @@ -20,7 +20,7 @@ class GetClass extends MiniPhase { override def phaseName: String = "getClass" // getClass transformation should be applied to specialized methods - override def runsAfter: Set[String] = Set(Erasure.name, FunctionalInterfaces.name) + override def runsAfter: Set[String] = Set(Erasure.name) override def transformApply(tree: Apply)(implicit ctx: Context): Tree = { import ast.Trees._ diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 4cbbc24d7d7c..d1f15b683f5a 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -232,6 +232,7 @@ class CompilationTests extends ParallelTesting { val backendDir = Paths.get("scala-backend/src/compiler/scala/tools/nsc/backend") val backendJvmDir = Paths.get("scala-backend/src/compiler/scala/tools/nsc/backend/jvm") + val scalaJSIRDir = Paths.get("compiler/target/scala-2.12/src_managed/main/scalajs-ir-src/org/scalajs/ir") // NOTE: Keep these exclusions synchronized with the ones in the sbt build (Build.scala) val backendExcluded = @@ -243,9 +244,11 @@ class CompilationTests extends ParallelTesting { sources(Files.list(backendDir), excludedFiles = backendExcluded) val backendJvmSources = sources(Files.list(backendJvmDir), excludedFiles = backendJvmExcluded) + val scalaJSIRSources = + sources(Files.list(scalaJSIRDir)) - val dotty1 = compileList("dotty", compilerSources ++ backendSources ++ backendJvmSources, opt)(dotty1Group) - val dotty2 = compileList("dotty", compilerSources ++ backendSources ++ backendJvmSources, opt)(dotty2Group) + val dotty1 = compileList("dotty", compilerSources ++ backendSources ++ backendJvmSources ++ scalaJSIRSources, opt)(dotty1Group) + val dotty2 = compileList("dotty", compilerSources ++ backendSources ++ backendJvmSources ++ scalaJSIRSources, opt)(dotty2Group) val tests = { lib.keepOutput :: dotty1.keepOutput :: { @@ -262,7 +265,8 @@ class CompilationTests extends ParallelTesting { compileShallowFilesInDir("compiler/src/dotty/tools/dotc/typer", opt) + compileShallowFilesInDir("compiler/src/dotty/tools/dotc/util", opt) + compileList("shallow-backend", backendSources, opt) + - compileList("shallow-backend-jvm", backendJvmSources, opt) + compileList("shallow-backend-jvm", backendJvmSources, opt) + + compileList("shallow-scalajs-ir", scalaJSIRSources, opt) }.keepOutput :: Nil }.map(_.checkCompile()) @@ -300,27 +304,6 @@ class CompilationTests extends ParallelTesting { compileFilesInDir("tests/plugins/neg").checkExpectedErrors() } - - private val (compilerSources, backendSources, backendJvmSources) = { - val compilerDir = Paths.get("compiler/src") - val compilerSources0 = sources(Files.walk(compilerDir)) - - val backendDir = Paths.get("scala-backend/src/compiler/scala/tools/nsc/backend") - val backendJvmDir = Paths.get("scala-backend/src/compiler/scala/tools/nsc/backend/jvm") - - // NOTE: Keep these exclusions synchronized with the ones in the sbt build (Build.scala) - val backendExcluded = - List("JavaPlatform.scala", "Platform.scala", "ScalaPrimitives.scala") - val backendJvmExcluded = - List("BCodeICodeCommon.scala", "GenASM.scala", "GenBCode.scala", "ScalacBackendInterface.scala", "BackendStats.scala") - - val backendSources0 = - sources(Files.list(backendDir), excludedFiles = backendExcluded) - val backendJvmSources0 = - sources(Files.list(backendJvmDir), excludedFiles = backendJvmExcluded) - - (compilerSources0, backendSources0, backendJvmSources0) - } } object CompilationTests { diff --git a/project/Build.scala b/project/Build.scala index 670e1983a41b..c0b70463aa52 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -19,6 +19,9 @@ import dotty.tools.sbtplugin.DottyPlugin.autoImport._ import dotty.tools.sbtplugin.DottyIDEPlugin.{ installCodeExtension, prepareCommand, runProcess } import dotty.tools.sbtplugin.DottyIDEPlugin.autoImport._ +import org.scalajs.sbtplugin.ScalaJSPlugin +import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ + import sbtbuildinfo.BuildInfoPlugin import sbtbuildinfo.BuildInfoPlugin.autoImport._ @@ -114,6 +117,8 @@ object Build { lazy val ideTestsCompilerArguments = taskKey[Seq[String]]("Compiler arguments to use in IDE tests") lazy val ideTestsDependencyClasspath = taskKey[Seq[File]]("Dependency classpath to use in IDE tests") + lazy val SourceDeps = config("sourcedeps") + // Settings shared by the build (scoped in ThisBuild). Used in build.sbt lazy val thisBuildSettings = Def.settings( organization := dottyOrganization, @@ -651,40 +656,40 @@ object Build { run := dotc.evaluated, dotc := runCompilerMain().evaluated, - repl := runCompilerMain(repl = true).evaluated + repl := runCompilerMain(repl = true).evaluated, /* Add the sources of scalajs-ir. * To guarantee that dotty can bootstrap without depending on a version * of scalajs-ir built with a different Scala compiler, we add its * sources instead of depending on the binaries. */ - //TODO: disabling until moved to separate project - //ivyConfigurations += config("sourcedeps").hide, - //libraryDependencies += - // "org.scala-js" %% "scalajs-ir" % scalaJSVersion % "sourcedeps", - //sourceGenerators in Compile += Def.task { - // val s = streams.value - // val cacheDir = s.cacheDirectory - // val trgDir = (sourceManaged in Compile).value / "scalajs-ir-src" - - // val report = updateClassifiers.value - // val scalaJSIRSourcesJar = report.select( - // configuration = Set("sourcedeps"), - // module = (_: ModuleID).name.startsWith("scalajs-ir_"), - // artifact = artifactFilter(`type` = "src")).headOption.getOrElse { - // sys.error(s"Could not fetch scalajs-ir sources") - // } - - // FileFunction.cached(cacheDir / s"fetchScalaJSIRSource", - // FilesInfo.lastModified, FilesInfo.exists) { dependencies => - // s.log.info(s"Unpacking scalajs-ir sources to $trgDir...") - // if (trgDir.exists) - // IO.delete(trgDir) - // IO.createDirectory(trgDir) - // IO.unzip(scalaJSIRSourcesJar, trgDir) - // (trgDir ** "*.scala").get.toSet - // } (Set(scalaJSIRSourcesJar)).toSeq - //}.taskValue + ivyConfigurations += SourceDeps.hide, + transitiveClassifiers := Seq("sources"), + libraryDependencies += + ("org.scala-js" %% "scalajs-ir" % scalaJSVersion % "sourcedeps").withDottyCompat(scalaVersion.value), + sourceGenerators in Compile += Def.task { + val s = streams.value + val cacheDir = s.cacheDirectory + val trgDir = (sourceManaged in Compile).value / "scalajs-ir-src" + + val report = updateClassifiers.value + val scalaJSIRSourcesJar = report.select( + configuration = configurationFilter("sourcedeps"), + module = (_: ModuleID).name.startsWith("scalajs-ir_"), + artifact = artifactFilter(`type` = "src")).headOption.getOrElse { + sys.error(s"Could not fetch scalajs-ir sources") + } + + FileFunction.cached(cacheDir / s"fetchScalaJSIRSource", + FilesInfo.lastModified, FilesInfo.exists) { dependencies => + s.log.info(s"Unpacking scalajs-ir sources to $trgDir...") + if (trgDir.exists) + IO.delete(trgDir) + IO.createDirectory(trgDir) + IO.unzip(scalaJSIRSourcesJar, trgDir) + (trgDir ** "*.scala").get.toSet + } (Set(scalaJSIRSourcesJar)).toSeq + }.taskValue, ) def runCompilerMain(repl: Boolean = false) = Def.inputTaskDyn { @@ -915,6 +920,42 @@ object Build { BuildInfoPlugin.buildInfoDefaultSettings ) + /** A sandbox to play with the Scala.js back-end of dotty. + * + * This sandbox is compiled with dotty with support for Scala.js. It can be + * used like any regular Scala.js project. In particular, `fastOptJS` will + * produce a .js file, and `run` will run the JavaScript code with a JS VM. + * + * Simply running `dotty/run -scalajs` without this sandbox is not very + * useful, as that would not provide the linker and JS runners. + */ + lazy val sjsSandbox = project.in(file("sandbox/scalajs")). + enablePlugins(ScalaJSPlugin). + settings(commonBootstrappedSettings). + settings( + /* Remove the Scala.js compiler plugin for scalac, and enable the + * Scala.js back-end of dotty instead. + */ + libraryDependencies := { + val deps = libraryDependencies.value + deps.filterNot(_.name.startsWith("scalajs-compiler")).map(_.withDottyCompat(scalaVersion.value)) + }, + scalacOptions += "-scalajs", + + // The main class cannot be found automatically due to the empty inc.Analysis + mainClass in Compile := Some("hello.HelloWorld"), + + scalaJSUseMainModuleInitializer := true, + + /* Debug-friendly Scala.js optimizer options. + * In particular, typecheck the Scala.js IR found on the classpath. + */ + scalaJSLinkerConfig ~= { + _.withCheckIR(true).withParallel(false) + } + ). + settings(compileWithDottySettings) + lazy val `dotty-bench` = project.in(file("bench")).asDottyBench(NonBootstrapped) lazy val `dotty-bench-bootstrapped` = project.in(file("bench")).asDottyBench(Bootstrapped) @@ -1236,48 +1277,6 @@ object Build { packResourceDir += ((baseDirectory in dist).value / "bin" -> "bin"), ) - // /** A sandbox to play with the Scala.js back-end of dotty. - // * - // * This sandbox is compiled with dotty with support for Scala.js. It can be - // * used like any regular Scala.js project. In particular, `fastOptJS` will - // * produce a .js file, and `run` will run the JavaScript code with a JS VM. - // * - // * Simply running `dotty/run -scalajs` without this sandbox is not very - // * useful, as that would not provide the linker and JS runners. - // */ - // lazy val sjsSandbox = project.in(file("sandbox/scalajs")). - // enablePlugins(ScalaJSPlugin). - // settings(commonNonBootstrappedSettings). - // settings( - // /* Remove the Scala.js compiler plugin for scalac, and enable the - // * Scala.js back-end of dotty instead. - // */ - // libraryDependencies ~= { deps => - // deps.filterNot(_.name.startsWith("scalajs-compiler")) - // }, - // scalacOptions += "-scalajs", - - // // The main class cannot be found automatically due to the empty inc.Analysis - // mainClass in Compile := Some("hello.world"), - - // // While developing the Scala.js back-end, it is very useful to see the trees dotc gives us - // scalacOptions += "-Xprint:collectSuperCalls", - - // /* Debug-friendly Scala.js optimizer options. - // * In particular, typecheck the Scala.js IR found on the classpath. - // */ - // scalaJSOptimizerOptions ~= { - // _.withCheckScalaJSIR(true).withParallel(false) - // } - // ). - // settings(compileWithDottySettings). - // settings(inConfig(Compile)(Seq( - // /* Make sure jsDependencyManifest runs after compile, otherwise compile - // * might remove the entire directory afterwards. - // */ - // jsDependencyManifest := jsDependencyManifest.dependsOn(compile).value - // ))) - implicit class ProjectDefinitions(val project: Project) extends AnyVal { // FIXME: we do not aggregate `bin` because its tests delete jars, thus breaking other tests diff --git a/project/plugins.sbt b/project/plugins.sbt index 5cd6b814e6fb..0d59bb27f966 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -5,7 +5,7 @@ // Scala IDE project file generator addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4") -// addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.21") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.0.0-M6") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.1") diff --git a/sandbox/scalajs/src/hello.scala b/sandbox/scalajs/src/hello.scala index bd4aa8cc5f55..5a2b6a2952c7 100644 --- a/sandbox/scalajs/src/hello.scala +++ b/sandbox/scalajs/src/hello.scala @@ -7,8 +7,8 @@ trait MyTrait { def foo(y: Int) = x } -object world extends js.JSApp with MyTrait { - def main(): Unit = { +object HelloWorld extends MyTrait { + def main(args: Array[String]): Unit = { println("hello dotty.js!") println(foo(4)) }