diff options
Diffstat (limited to 'src/dotty/tools')
-rw-r--r-- | src/dotty/tools/backend/sjs/GenSJSIR.scala | 14 | ||||
-rw-r--r-- | src/dotty/tools/backend/sjs/JSCodeGen.scala | 2392 | ||||
-rw-r--r-- | src/dotty/tools/backend/sjs/JSDefinitions.scala | 199 | ||||
-rw-r--r-- | src/dotty/tools/backend/sjs/JSEncoding.scala | 389 | ||||
-rw-r--r-- | src/dotty/tools/backend/sjs/JSInterop.scala | 110 | ||||
-rw-r--r-- | src/dotty/tools/backend/sjs/JSPositions.scala | 65 | ||||
-rw-r--r-- | src/dotty/tools/backend/sjs/JSPrimitives.scala | 118 | ||||
-rw-r--r-- | src/dotty/tools/backend/sjs/ScopedVar.scala | 38 | ||||
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 17 | ||||
-rw-r--r-- | src/dotty/tools/dotc/config/PathResolver.scala | 24 | ||||
-rw-r--r-- | src/dotty/tools/dotc/config/SJSPlatform.scala | 18 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Contexts.scala | 5 |
12 files changed, 13 insertions, 3376 deletions
diff --git a/src/dotty/tools/backend/sjs/GenSJSIR.scala b/src/dotty/tools/backend/sjs/GenSJSIR.scala deleted file mode 100644 index 819a8f0e3..000000000 --- a/src/dotty/tools/backend/sjs/GenSJSIR.scala +++ /dev/null @@ -1,14 +0,0 @@ -package dotty.tools.backend.sjs - -import dotty.tools.dotc.core._ -import Contexts._ -import Phases._ - -/** Generates Scala.js IR files for the compilation unit. */ -class GenSJSIR extends Phase { - def phaseName: String = "genSJSIR" - - def run(implicit ctx: Context): Unit = { - new JSCodeGen().run() - } -} diff --git a/src/dotty/tools/backend/sjs/JSCodeGen.scala b/src/dotty/tools/backend/sjs/JSCodeGen.scala deleted file mode 100644 index 401e01784..000000000 --- a/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ /dev/null @@ -1,2392 +0,0 @@ -package dotty.tools.backend.sjs - -import scala.annotation.switch - -import scala.collection.mutable - -import dotty.tools.FatalError - -import dotty.tools.dotc.CompilationUnit -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Phases.Phase - -import dotty.tools.dotc.core._ -import Periods._ -import SymDenotations._ -import Contexts._ -import Decorators._ -import Flags._ -import dotty.tools.dotc.ast.Trees._ -import Types._ -import Symbols._ -import Denotations._ -import Phases._ -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 js.OptimizerHints - -import JSEncoding._ -import JSInterop._ -import ScopedVar.withScopedVars - -/** Main codegen for Scala.js IR. - * - * [[GenSJSIR]] creates one instance of `JSCodeGen` per compilation unit. - * The `run()` method processes the whole compilation unit and generates - * `.sjsir` files for it. - * - * There are 4 main levels of translation: - * - * - `genCompilationUnit()` iterates through all the type definitions in the - * compilation unit. Each generated `js.ClassDef` is serialized to an - * `.sjsir` file. - * - `genScalaClass()` and other similar methods generate the skeleton of - * classes. - * - `genMethod()` and similar methods generate the declarations of methods. - * - `genStatOrExpr()` and everything else generate the bodies of methods. - */ -class JSCodeGen()(implicit ctx: Context) { - import tpd._ - - private val jsdefn = JSDefinitions.jsdefn - private val primitives = new JSPrimitives(ctx) - - private val positionConversions = new JSPositions()(ctx) - import positionConversions.{pos2irPos, implicitPos2irPos} - - // Some state -------------------------------------------------------------- - - private val currentClassSym = new ScopedVar[Symbol] - private val currentMethodSym = new ScopedVar[Symbol] - private val localNames = new ScopedVar[LocalNameGenerator] - private val thisLocalVarIdent = new ScopedVar[Option[js.Ident]] - private val undefinedDefaultParams = new ScopedVar[mutable.Set[Symbol]] - - /** Implicitly materializes the current local name generator. */ - private implicit def implicitLocalNames: LocalNameGenerator = localNames.get - - /* See genSuperCall() - * TODO Can we avoid this unscoped var? - */ - private var isModuleInitialized: Boolean = false - - private def currentClassType = encodeClassType(currentClassSym) - - /** Returns a new fresh local identifier. */ - private def freshLocalIdent()(implicit pos: Position): js.Ident = - localNames.get.freshLocalIdent() - - /** Returns a new fresh local identifier. */ - private def freshLocalIdent(base: String)(implicit pos: Position): js.Ident = - localNames.get.freshLocalIdent(base) - - // Compilation unit -------------------------------------------------------- - - def run(): Unit = { - genCompilationUnit(ctx.compilationUnit) - } - - /** Generates the Scala.js IR for a compilation unit - * This method iterates over all the class and interface definitions - * found in the compilation unit and emits their IR (.sjsir). - * - * Some classes are never actually emitted: - * - Classes representing primitive types - * - The scala.Array class - * - * TODO Some classes representing anonymous functions are not actually emitted. - * Instead, a temporary representation of their `apply` method is built - * and recorded, so that it can be inlined as a JavaScript anonymous - * function in the method that instantiates it. - * - * Other ClassDefs are emitted according to their nature: - * * Scala.js-defined JS class -> `genScalaJSDefinedJSClass()` - * * Other raw JS type (<: js.Any) -> `genRawJSClassData()` - * * Interface -> `genInterface()` - * * Normal class -> `genClass()` - */ - private def genCompilationUnit(cunit: CompilationUnit): Unit = { - def collectTypeDefs(tree: Tree): List[TypeDef] = { - tree match { - case EmptyTree => Nil - case PackageDef(_, stats) => stats.flatMap(collectTypeDefs) - case cd: TypeDef => cd :: Nil - case _: ValDef => Nil // module instance - } - } - val allTypeDefs = collectTypeDefs(cunit.tpdTree) - - val generatedClasses = mutable.ListBuffer.empty[(Symbol, js.ClassDef)] - - // TODO Record anonymous JS function classes - - /* Finally, we emit true code for the remaining class defs. */ - for (td <- allTypeDefs) { - val sym = td.symbol - implicit val pos: Position = sym.pos - - /* Do not actually emit code for primitive types nor scala.Array. */ - val isPrimitive = - sym.isPrimitiveValueClass || sym == defn.ArrayClass - - if (!isPrimitive) { - withScopedVars( - currentClassSym := sym - ) { - val tree = if (isJSType(sym)) { - /*assert(!isRawJSFunctionDef(sym), - s"Raw JS function def should have been recorded: $cd")*/ - if (!sym.is(Trait) && isScalaJSDefinedJSClass(sym)) - genScalaJSDefinedJSClass(td) - else - genRawJSClassData(td) - } else if (sym.is(Trait)) { - genInterface(td) - } else { - genScalaClass(td) - } - - generatedClasses += ((sym, tree)) - } - } - } - - 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() - } - genIRFile(cunit, sym, tree) - } - } - - private def genIRFile(cunit: CompilationUnit, sym: Symbol, - tree: ir.Trees.ClassDef): Unit = { - 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() - } - } - - private def getFileFor(cunit: CompilationUnit, sym: Symbol, - suffix: String) = { - import scala.reflect.io._ - - val outputDirectory: AbstractFile = // TODO Support virtual files - new PlainDirectory(new Directory(new java.io.File(ctx.settings.d.value))) - - val pathParts = sym.fullName.toString.split("[./]") - val dir = (outputDirectory /: pathParts.init)(_.subdirectoryNamed(_)) - - var filename = pathParts.last - if (sym.is(ModuleClass)) - filename = filename + nme.MODULE_SUFFIX.toString - - dir fileNamed (filename + suffix) - } - - // Generate a class -------------------------------------------------------- - - /** Gen the IR ClassDef for a Scala class definition (maybe a module class). - */ - private def genScalaClass(td: TypeDef): js.ClassDef = { - val sym = td.symbol.asClass - implicit val pos: Position = sym.pos - - assert(!sym.is(Trait), - "genScalaClass() must be called only for normal classes: "+sym) - assert(sym.superClass != NoSymbol, sym) - - /*if (hasDefaultCtorArgsAndRawJSModule(sym)) { - reporter.error(pos, - "Implementation restriction: constructors of " + - "Scala classes cannot have default parameters " + - "if their companion module is JS native.") - }*/ - - val classIdent = encodeClassFullNameIdent(sym) - val isHijacked = false //isHijackedBoxedClass(sym) - - // Optimizer hints - - def isStdLibClassWithAdHocInlineAnnot(sym: Symbol): Boolean = { - val fullName = sym.fullName.toString - (fullName.startsWith("scala.Tuple") && !fullName.endsWith("$")) || - (fullName.startsWith("scala.collection.mutable.ArrayOps$of")) - } - - val shouldMarkInline = ( - sym.hasAnnotation(jsdefn.InlineAnnot) || - (sym.isAnonymousFunction && !sym.isSubClass(defn.PartialFunctionClass)) || - isStdLibClassWithAdHocInlineAnnot(sym)) - - val optimizerHints = { - OptimizerHints.empty - .withInline(shouldMarkInline) - .withNoinline(sym.hasAnnotation(jsdefn.NoinlineAnnot)) - } - - // Generate members (constructor + methods) - - val generatedMethods = new mutable.ListBuffer[js.MethodDef] - val exportedSymbols = new mutable.ListBuffer[Symbol] - - val tpl = td.rhs.asInstanceOf[Template] - for (tree <- tpl.constr :: tpl.body) { - tree match { - case EmptyTree => () - - case _: ValDef => - () // fields are added via genClassFields() - - case dd: DefDef => - val sym = dd.symbol - - val isExport = false //jsInterop.isExport(sym) - val isNamedExport = false /*isExport && sym.annotations.exists( - _.symbol == JSExportNamedAnnotation)*/ - - /*if (isNamedExport) - generatedMethods += genNamedExporterDef(dd) - else*/ - generatedMethods ++= genMethod(dd) - - if (isExport) { - // We add symbols that we have to export here. This way we also - // get inherited stuff that is implemented in this class. - exportedSymbols += sym - } - - case _ => - throw new FatalError("Illegal tree in body of genScalaClass(): " + tree) - } - } - - // Generate fields and add to methods + ctors - val generatedMembers = genClassFields(td) ++ generatedMethods.toList - - // 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 - } - } - - // Hashed definitions of the class - val hashedDefs = - ir.Hashers.hashDefs(generatedMembers ++ exports) - - // The complete class definition - val kind = - if (isStaticModule(sym)) ClassKind.ModuleClass - else if (isHijacked) ClassKind.HijackedClass - else ClassKind.Class - - val classDefinition = js.ClassDef( - classIdent, - kind, - Some(encodeClassFullNameIdent(sym.superClass)), - genClassInterfaces(sym), - None, - hashedDefs)( - optimizerHints) - - classDefinition - } - - /** Gen the IR ClassDef for a Scala.js-defined JS class. */ - private def genScalaJSDefinedJSClass(td: TypeDef): js.ClassDef = { - ??? - } - - /** Gen the IR ClassDef for a raw JS class or trait. - */ - private def genRawJSClassData(td: TypeDef): js.ClassDef = { - val sym = td.symbol.asClass - implicit val pos: Position = sym.pos - - val classIdent = encodeClassFullNameIdent(sym) - 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)) - - js.ClassDef(classIdent, ClassKind.RawJSType, - superClass, - genClassInterfaces(sym), - jsName, - Nil)( - OptimizerHints.empty) - } - - /** Gen the IR ClassDef for an interface definition. - */ - private def genInterface(td: TypeDef): js.ClassDef = { - val sym = td.symbol.asClass - implicit val pos: Position = sym.pos - - val classIdent = encodeClassFullNameIdent(sym) - - val generatedMethods = new mutable.ListBuffer[js.MethodDef] - - val tpl = td.rhs.asInstanceOf[Template] - for (tree <- tpl.constr :: tpl.body) { - tree match { - case EmptyTree => () - case dd: DefDef => generatedMethods ++= genMethod(dd) - case _ => - throw new FatalError("Illegal tree in gen of genInterface(): " + tree) - } - } - - val superInterfaces = genClassInterfaces(sym) - - // Hashed definitions of the interface - val hashedDefs = - ir.Hashers.hashDefs(generatedMethods.toList) - - js.ClassDef(classIdent, ClassKind.Interface, None, superInterfaces, None, - hashedDefs)(OptimizerHints.empty) - } - - private def genClassInterfaces(sym: ClassSymbol)( - implicit pos: Position): List[js.Ident] = { - import dotty.tools.dotc.transform.SymUtils._ - for { - intf <- sym.directlyInheritedTraits - } yield { - encodeClassFullNameIdent(intf) - } - } - - // Generate the fields of a class ------------------------------------------ - - /** Gen definitions for the fields of a class. - */ - private def genClassFields(td: TypeDef): List[js.FieldDef] = { - val classSym = td.symbol.asClass - assert(currentClassSym.get == classSym, - "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 { - implicit val pos: Position = f.pos - - val name = - /*if (isExposed(f)) js.StringLiteral(jsNameOf(f)) - else*/ encodeFieldSym(f) - - val irTpe = //if (!isScalaJSDefinedJSClass(classSym)) { - toIRType(f.info) - /*} else { - val tpeEnteringPosterasure = - enteringPhase(currentRun.posterasurePhase)(f.tpe) - tpeEnteringPosterasure match { - case tpe: ErasedValueType => - /* Here, we must store the field as the boxed representation of - * the value class. The default value of that field, as - * initialized at the time the instance is created, will - * therefore be null. This will not match the behavior we would - * get in a Scala class. To match the behavior, we would need to - * initialized to an instance of the boxed representation, with - * an underlying value set to the zero of its type. However we - * cannot implement that, so we live with the discrepancy. - * Anyway, scalac also has problems with uninitialized value - * class values, if they come from a generic context. - * - * TODO Evaluate how much of this needs to be adapted for dotc, - * which unboxes `null` to the zero of their underlying. - */ - jstpe.ClassType(encodeClassFullName(tpe.valueClazz)) - - case _ if f.tpe.typeSymbol == CharClass => - /* Will be initialized to null, which will unbox to '\0' when - * read. - */ - jstpe.ClassType(ir.Definitions.BoxedCharacterClass) - - case _ => - /* Other types are not boxed, so we can initialized them to - * their true zero. - */ - toIRType(f.tpe) - } - }*/ - - js.FieldDef(name, irTpe, f.is(Mutable)) - }).toList - } - - // Generate a method ------------------------------------------------------- - - private def genMethod(dd: DefDef): Option[js.MethodDef] = { - withScopedVars( - localNames := new LocalNameGenerator - ) { - genMethodWithCurrentLocalNameScope(dd) - } - } - - /** Gen JS code for a method definition in a class or in an impl class. - * On the JS side, method names are mangled to encode the full signature - * of the Scala method, as described in `JSEncoding`, to support - * overloading. - * - * Some methods are not emitted at all: - * - Primitives, since they are never actually called - * - Constructors of hijacked classes - * - * Constructors are emitted by generating their body as a statement. - * - * Other (normal) methods are emitted with `genMethodBody()`. - */ - private def genMethodWithCurrentLocalNameScope(dd: DefDef): Option[js.MethodDef] = { - implicit val pos: Position = dd.pos - val sym = dd.symbol - val vparamss = dd.vparamss - val rhs = dd.rhs - - isModuleInitialized = false - - withScopedVars( - currentMethodSym := sym, - undefinedDefaultParams := mutable.Set.empty, - thisLocalVarIdent := None - ) { - assert(vparamss.isEmpty || vparamss.tail.isEmpty, - "Malformed parameter list: " + vparamss) - val params = if (vparamss.isEmpty) Nil else vparamss.head.map(_.symbol) - - val isJSClassConstructor = - sym.isClassConstructor && isScalaJSDefinedJSClass(currentClassSym) - - val methodName: js.PropertyName = encodeMethodSym(sym) - - def jsParams = for (param <- params) yield { - implicit val pos: Position = param.pos - js.ParamDef(encodeLocalSym(param), toIRType(param.info), - mutable = false, rest = false) - } - - /*if (primitives.isPrimitive(sym)) { - None - } else*/ if (sym.is(Deferred)) { - Some(js.MethodDef(static = false, methodName, - jsParams, toIRType(patchedResultType(sym)), js.EmptyTree)( - OptimizerHints.empty, None)) - } else /*if (isJSNativeCtorDefaultParam(sym)) { - None - } else if (sym.isClassConstructor && isHijackedBoxedClass(sym.owner)) { - None - } else*/ { - /*def isTraitImplForwarder = dd.rhs match { - case app: Apply => foreignIsImplClass(app.symbol.owner) - case _ => false - }*/ - - val shouldMarkInline = { - sym.hasAnnotation(jsdefn.InlineAnnot) || - sym.isAnonymousFunction - } - - val shouldMarkNoinline = { - sym.hasAnnotation(jsdefn.NoinlineAnnot) /*&& - !isTraitImplForwarder*/ - } - - val optimizerHints = { - OptimizerHints.empty - .withInline(shouldMarkInline) - .withNoinline(shouldMarkNoinline) - } - - val methodDef = { - /*if (isJSClassConstructor) { - val body0 = genStat(rhs) - val body1 = - if (!sym.isPrimaryConstructor) body0 - else moveAllStatementsAfterSuperConstructorCall(body0) - js.MethodDef(static = false, methodName, - jsParams, jstpe.NoType, body1)(optimizerHints, None) - } else*/ if (sym.isConstructor) { - js.MethodDef(static = false, methodName, - jsParams, jstpe.NoType, - genStat(rhs))(optimizerHints, None) - } else { - val resultIRType = toIRType(patchedResultType(sym)) - genMethodDef(static = false, methodName, - params, resultIRType, rhs, optimizerHints) - } - } - - Some(methodDef) - } - } - } - - /** Generates the MethodDef of a (non-constructor) method - * - * Most normal methods are emitted straightforwardly. If the result - * type is Unit, then the body is emitted as a statement. Otherwise, it is - * emitted as an expression. - * - * Methods Scala.js-defined JS classes are compiled as static methods taking - * an explicit parameter for their `this` value. - */ - private def genMethodDef(static: Boolean, methodName: js.PropertyName, - paramsSyms: List[Symbol], resultIRType: jstpe.Type, - tree: Tree, optimizerHints: OptimizerHints): js.MethodDef = { - implicit val pos: Position = tree.pos - - ctx.debuglog("genMethod " + methodName.name) - ctx.debuglog("") - - val jsParams = for (param <- paramsSyms) yield { - implicit val pos: Position = param.pos - js.ParamDef(encodeLocalSym(param), toIRType(param.info), - mutable = false, rest = false) - } - - def genBody() = - if (resultIRType == jstpe.NoType) genStat(tree) - else genExpr(tree) - - //if (!isScalaJSDefinedJSClass(currentClassSym)) { - js.MethodDef(static, methodName, jsParams, resultIRType, genBody())( - optimizerHints, None) - /*} else { - assert(!static, tree.pos) - - withScopedVars( - thisLocalVarIdent := Some(freshLocalIdent("this")) - ) { - val thisParamDef = js.ParamDef(thisLocalVarIdent.get.get, - jstpe.AnyType, mutable = false, rest = false) - - js.MethodDef(static = true, methodName, thisParamDef :: jsParams, - resultIRType, genBody())( - optimizerHints, None) - } - }*/ - } - - // Generate statements and expressions ------------------------------------- - - /** Gen JS code for a tree in statement position (in the IR). - */ - private def genStat(tree: Tree): js.Tree = { - exprToStat(genStatOrExpr(tree, isStat = true)) - } - - /** Turn a JavaScript expression of type Unit into a statement */ - private def exprToStat(tree: js.Tree): js.Tree = { - /* Any JavaScript expression is also a statement, but at least we get rid - * of some pure expressions that come from our own codegen. - */ - implicit val pos: Position = tree.pos - tree match { - case js.Block(stats :+ expr) => js.Block(stats :+ exprToStat(expr)) - case _:js.Literal | js.This() => js.Skip() - case _ => tree - } - } - - /** Gen JS code for a tree in expression position (in the IR). - */ - private def genExpr(tree: Tree): js.Tree = { - val result = genStatOrExpr(tree, isStat = false) - assert(result.tpe != jstpe.NoType, - s"genExpr($tree) returned a tree with type NoType at pos ${tree.pos}") - result - } - - /** Gen JS code for a tree in statement or expression position (in the IR). - * - * This is the main transformation method. Each node of the Scala AST - * is transformed into an equivalent portion of the JS AST. - */ - private def genStatOrExpr(tree: Tree, isStat: Boolean): js.Tree = { - implicit val pos: Position = tree.pos - - ctx.debuglog(" " + tree) - 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 - * by genMethodBody(). */ - assert(name != nme.THIS, - s"ValDef(_, nme.THIS, _, _) found at ${tree.pos}") - - val sym = tree.symbol - val rhs = tree.rhs - val rhsTree = genExpr(rhs) - - rhsTree match { - case js.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. - */ - undefinedDefaultParams += sym - js.Skip() - case _ => - js.VarDef(encodeLocalSym(sym), - toIRType(sym.info), sym.is(Mutable), rhsTree) - } - - case If(cond, thenp, elsep) => - js.If(genExpr(cond), genStatOrExpr(thenp, isStat), - genStatOrExpr(elsep, isStat))(toIRType(tree.tpe)) - - case Return(expr, from) => - // TODO Need to consider `from`? - js.Return(toIRType(expr.tpe) match { - case jstpe.NoType => js.Block(genStat(expr), js.Undefined()) - case _ => genExpr(expr) - }) - - /*case t: Try => - genTry(t, isStat)*/ - - case app: Apply => - genApply(app, isStat) - - case app: TypeApply => - genTypeApply(app) - - /*case app: ApplyDynamic => - genApplyDynamic(app)*/ - - case tree: This => - if (tree.symbol == currentClassSym.get) { - genThis() - } else { - assert(tree.symbol.is(Module), - "Trying to access the this of another class: " + - "tree.symbol = " + tree.symbol + - ", class symbol = " + currentClassSym.get + - " pos:" + pos) - genLoadModule(tree.symbol) - } - - case Select(qualifier, _) => - val sym = tree.symbol - if (sym.is(Module)) { - assert(!sym.is(Package), "Cannot use package as value: " + tree) - genLoadModule(sym) - } else if (sym.is(JavaStatic)) { - genLoadStaticField(sym) - } else /*if (paramAccessorLocals contains sym) { - paramAccessorLocals(sym).ref - } else if (isScalaJSDefinedJSClass(sym.owner)) { - val genQual = genExpr(qualifier) - val boxed = if (isExposed(sym)) - js.JSBracketSelect(genQual, js.StringLiteral(jsNameOf(sym))) - else - js.JSDotSelect(genQual, encodeFieldSym(sym)) - fromAny(boxed, - enteringPhase(currentRun.posterasurePhase)(sym.tpe)) - } else*/ { - js.Select(genExpr(qualifier), - encodeFieldSym(sym))(toIRType(sym.info)) - } - - case tree: Ident => - desugarIdent(tree).fold[js.Tree] { - val sym = tree.symbol - assert(!sym.is(Package), "Cannot use package as value: " + tree) - if (sym.is(Module)) { - genLoadModule(sym) - } else if (undefinedDefaultParams.contains(sym)) { - /* This is a default parameter whose assignment was moved to - * a local variable. Put an undefined param instead. - */ - js.UndefinedParam()(toIRType(sym.info)) - } else { - js.VarRef(encodeLocalSym(sym))(toIRType(sym.info)) - } - } { select => - genStatOrExpr(select, isStat) - } - - case Literal(value) => - import Constants._ - value.tag match { - case UnitTag => - js.Skip() - case BooleanTag => - js.BooleanLiteral(value.booleanValue) - case ByteTag | ShortTag | CharTag | IntTag => - js.IntLiteral(value.intValue) - case LongTag => - js.LongLiteral(value.longValue) - case FloatTag => - js.FloatLiteral(value.floatValue) - case DoubleTag => - js.DoubleLiteral(value.doubleValue) - case StringTag => - js.StringLiteral(value.stringValue) - case NullTag => - js.Null() - case ClazzTag => - genClassConstant(value.typeValue) - /*case EnumTag => - genStaticMember(value.symbolValue)*/ - } - - case Block(stats, expr) => - js.Block(stats.map(genStat) :+ genStatOrExpr(expr, isStat)) - - case Typed(expr, _) => - expr match { - case _: Super => genThis() - case _ => genExpr(expr) - } - - case Assign(lhs0, rhs) => - val sym = lhs0.symbol - if (sym.is(JavaStaticTerm)) - 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).getOrElse(lhs) - case lhs => lhs - } - lhs match { - case lhs: Select => - val qualifier = lhs.qualifier - - def ctorAssignment = ( - currentMethodSym.get.name == nme.CONSTRUCTOR && - currentMethodSym.get.owner == qualifier.symbol && - qualifier.isInstanceOf[This] - ) - if (!sym.is(Mutable) && !ctorAssignment) - throw new FatalError(s"Assigning to immutable field ${sym.fullName} at $pos") - - val genQual = genExpr(qualifier) - - /*if (isScalaJSDefinedJSClass(sym.owner)) { - val genLhs = if (isExposed(sym)) - js.JSBracketSelect(genQual, js.StringLiteral(jsNameOf(sym))) - else - js.JSDotSelect(genQual, encodeFieldSym(sym)) - val boxedRhs = - ensureBoxed(genRhs, - enteringPhase(currentRun.posterasurePhase)(rhs.tpe)) - js.Assign(genLhs, boxedRhs) - } else {*/ - js.Assign( - js.Select(genQual, encodeFieldSym(sym))(toIRType(sym.info)), - genRhs) - //} - case _ => - js.Assign( - js.VarRef(encodeLocalSym(sym))(toIRType(sym.info)), - genRhs) - } - - /** Array constructor */ - case javaSeqLiteral: JavaSeqLiteral => - genJavaSeqLiteral(javaSeqLiteral) - - /** A Match reaching the backend is supposed to be optimized as a switch */ - /*case mtch: Match => - genMatch(mtch, isStat)*/ - - case tree: Closure => - genClosure(tree) - - /*case EmptyTree => - js.Skip()*/ - - case _ => - throw new FatalError("Unexpected tree in genExpr: " + - tree + "/" + tree.getClass + " at: " + (tree.pos: Position)) - } - } // end of genStatOrExpr() - - // !!! DUPLICATE code with DottyBackendInterface - private def desugarIdent(i: Ident): Option[Select] = { - i.tpe match { - case TermRef(prefix: TermRef, name) => - Some(tpd.ref(prefix).select(i.symbol)) - case TermRef(prefix: ThisType, name) => - Some(tpd.This(prefix.cls).select(i.symbol)) - /*case TermRef(NoPrefix, name) => - if (i.symbol is Method) Some(This(i.symbol.topLevelClass).select(i.symbol)) // workaround #342 todo: remove after fixed - else None*/ - case _ => - None - } - } - - private def qualifierOf(fun: Tree): Tree = fun match { - case fun: Ident => - fun.tpe match { - case TermRef(prefix: TermRef, _) => tpd.ref(prefix) - case TermRef(prefix: ThisType, _) => tpd.This(prefix.cls) - } - case Select(qualifier, _) => - qualifier - case TypeApply(fun, _) => - qualifierOf(fun) - } - - /** Gen JS this of the current class. - * Normally encoded straightforwardly as a JS this. - * But must be replaced by the `thisLocalVarIdent` local variable if there - * is one. - */ - private def genThis()(implicit pos: Position): js.Tree = { - /*if (tryingToGenMethodAsJSFunction) { - throw new CancelGenMethodAsJSFunction( - "Trying to generate `this` inside the body") - }*/ - - thisLocalVarIdent.fold[js.Tree] { - js.This()(currentClassType) - } { thisLocalIdent => - js.VarRef(thisLocalIdent)(currentClassType) - } - } - - /** Gen JS code for an Apply node (method call) - * - * There's a whole bunch of varieties of Apply nodes: regular method - * calls, super calls, constructor calls, isInstanceOf/asInstanceOf, - * primitives, JS calls, etc. They are further dispatched in here. - */ - private def genApply(tree: Apply, isStat: Boolean): js.Tree = { - implicit val pos: Position = tree.pos - val args = tree.args - val sym = tree.fun.symbol - - val fun = tree.fun match { - case fun: Ident => desugarIdent(fun).getOrElse(fun) - case fun => fun - } - - fun match { - case _ if isJSDefaultParam(sym) => - js.UndefinedParam()(toIRType(sym.info.finalResultType)) - - case Select(Super(_, _), _) => - genSuperCall(tree, isStat) - - case Select(New(_), nme.CONSTRUCTOR) => - genApplyNew(tree) - - case _ => - /*if (sym.isLabel) { - genLabelApply(tree) - } else*/ if (primitives.isPrimitive(tree)) { - genPrimitiveOp(tree, isStat) - } else if (Erasure.Boxing.isBox(sym)) { - // Box a primitive value (cannot be Unit) - val arg = args.head - makePrimitiveBox(genExpr(arg), arg.tpe) - } else if (Erasure.Boxing.isUnbox(sym)) { - // Unbox a primitive value (cannot be Unit) - val arg = args.head - makePrimitiveUnbox(genExpr(arg), tree.tpe) - } else { - genNormalApply(tree, isStat) - } - } - } - - /** Gen JS code for a super call, of the form Class.super[mix].fun(args). - * - * This does not include calls defined in mixin traits, as these are - * already desugared by the 'mixin' phase. Only calls to super classes - * remain. - * - * Since a class has exactly one direct superclass, and calling a method - * two classes above the current one is invalid in Scala, the `mix` item is - * irrelevant. - */ - private def genSuperCall(tree: Apply, isStat: Boolean): js.Tree = { - implicit val pos: Position = tree.pos - val Apply(fun @ Select(sup @ Super(_, mix), _), args) = tree - val sym = fun.symbol - - if (sym == defn.Any_getClass) { - // The only primitive that is also callable as super call - js.GetClass(genThis()) - } else /*if (isScalaJSDefinedJSClass(currentClassSym)) { - genJSSuperCall(tree, isStat) - } else*/ { - val superCall = genApplyMethodStatically( - genThis()(sup.pos), sym, genActualArgs(sym, args)) - - // Initialize the module instance just after the super constructor call. - if (isStaticModule(currentClassSym) && !isModuleInitialized && - currentMethodSym.get.isClassConstructor) { - isModuleInitialized = true - val thisType = jstpe.ClassType(encodeClassFullName(currentClassSym)) - val initModule = js.StoreModule(thisType, js.This()(thisType)) - js.Block(superCall, initModule) - } else { - superCall - } - } - } - - /** Gen JS code for a constructor call (new). - * Further refined into: - * * new String(...) - * * new of a hijacked boxed class - * * new of an anonymous function class that was recorded as JS function - * * new of a raw JS class - * * new Array - * * regular new - */ - private def genApplyNew(tree: Apply): js.Tree = { - implicit val pos: Position = tree.pos - - val Apply(fun @ Select(New(tpt), nme.CONSTRUCTOR), args) = tree - val ctor = fun.symbol - val tpe = tpt.tpe - - 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 functionMaker = translatedAnonFunctions(tpe.typeSymbol) - functionMaker(args map genExpr) - } else*/ if (isJSType(tpe.widenDealias.typeSymbol)) { - val clsSym = tpe.widenDealias.typeSymbol - 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)) - } else { - toIRType(tpe) match { - case cls: jstpe.ClassType => - js.New(cls, encodeMethodSym(ctor), genActualArgs(ctor, args)) - - case other => - throw new FatalError(s"Non ClassType cannot be instantiated: $other") - } - } - } - - /** Gen JS code for a primitive method call. */ - private def genPrimitiveOp(tree: Apply, isStat: Boolean): js.Tree = { - import scala.tools.nsc.backend.ScalaPrimitives._ - - implicit val pos: Position = tree.pos - - val Apply(fun, args) = tree - val receiver = qualifierOf(fun) - - val code = primitives.getPrimitive(tree, receiver.tpe) - - if (isArithmeticOp(code) || isLogicalOp(code) || isComparisonOp(code)) - genSimpleOp(tree, receiver :: args, code) - else if (code == CONCAT) - genStringConcat(tree, receiver, args) - else if (code == HASH) - genScalaHash(tree, receiver) - else if (isArrayOp(code)) - genArrayOp(tree, code) - else if (code == SYNCHRONIZED) - genSynchronized(tree, isStat) - else if (isCoercion(code)) - genCoercion(tree, receiver, code) - else if (code == JSPrimitives.THROW) - genThrow(tree, args) - else /*if (primitives.isJSPrimitive(code)) - genJSPrimitive(tree, receiver, args, code) - else*/ - throw new FatalError(s"Unknown primitive: ${tree.symbol.fullName} at: $pos") - } - - /** Gen JS code for a simple operation (arithmetic, logical, or comparison) */ - private def genSimpleOp(tree: Apply, args: List[Tree], code: Int): js.Tree = { - args match { - case List(arg) => genSimpleUnaryOp(tree, arg, code) - case List(lhs, rhs) => genSimpleBinaryOp(tree, lhs, rhs, code) - case _ => throw new FatalError("Incorrect arity for primitive") - } - } - - /** 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._ - - implicit val pos: Position = tree.pos - - val genArg = genExpr(arg) - val resultIRType = toIRType(tree.tpe) - - (code: @switch) match { - case POS => - genArg - - case NEG => - (resultIRType: @unchecked) match { - case jstpe.IntType => - js.BinaryOp(js.BinaryOp.Int_-, js.IntLiteral(0), genArg) - case jstpe.LongType => - js.BinaryOp(js.BinaryOp.Long_-, js.LongLiteral(0), genArg) - case jstpe.FloatType => - js.BinaryOp(js.BinaryOp.Float_-, js.FloatLiteral(0.0f), genArg) - case jstpe.DoubleType => - js.BinaryOp(js.BinaryOp.Double_-, js.DoubleLiteral(0), genArg) - } - - case NOT => - (resultIRType: @unchecked) match { - case jstpe.IntType => - js.BinaryOp(js.BinaryOp.Int_^, js.IntLiteral(-1), genArg) - case jstpe.LongType => - js.BinaryOp(js.BinaryOp.Long_^, js.LongLiteral(-1), genArg) - } - - case ZNOT => - js.UnaryOp(js.UnaryOp.Boolean_!, genArg) - - case _ => - throw new FatalError("Unknown unary operation code: " + code) - } - } - - /** 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 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: Position = 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 - } - - if (opType == AnyOp && isUniversalEqualityOp(code)) { - genUniversalEqualityOp(lhs, rhs, code) - } else if (code == ZOR) { - js.If(genExpr(lhs), js.BooleanLiteral(true), genExpr(rhs))(jstpe.BooleanType) - } else if (code == ZAND) { - js.If(genExpr(lhs), genExpr(rhs), 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 { - case ADD => Int_+ - case SUB => Int_- - case MUL => Int_* - case DIV => Int_/ - case MOD => Int_% - case OR => Int_| - case AND => Int_& - case XOR => Int_^ - case LSL => Int_<< - 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 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 DoubleOp => - (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 LongOp => - (code: @switch) match { - case ADD => Long_+ - case SUB => Long_- - case MUL => Long_* - case DIV => Long_/ - case MOD => Long_% - case OR => Long_| - case XOR => Long_^ - case AND => Long_& - case LSL => Long_<< - case LSR => Long_>>> - case ASR => Long_>> - - case EQ => Long_== - case NE => Long_!= - case LT => Long_< - case LE => Long_<= - case GT => Long_> - case GE => Long_>= - } - - case BooleanOp => - (code: @switch) match { - case EQ => Boolean_== - case NE => Boolean_!= - case OR => Boolean_| - case AND => Boolean_& - case XOR => Boolean_!= - } - - 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 ID => === - case NI => !== - } - } - - js.BinaryOp(op, genLhs, genRhs) - } - } - - /** Gen JS code for a universal equality test. */ - private def genUniversalEqualityOp(lhs: Tree, rhs: Tree, code: Int)( - implicit pos: Position): js.Tree = { - - import scala.tools.nsc.backend.ScalaPrimitives._ - - val genLhs = genExpr(lhs) - val genRhs = genExpr(rhs) - - val bypassEqEq = { - // Do not call equals if we have a literal null at either side. - genLhs.isInstanceOf[js.Null] || - genRhs.isInstanceOf[js.Null] - } - - if (bypassEqEq) { - js.BinaryOp( - if (code == EQ) js.BinaryOp.=== else js.BinaryOp.!==, - genLhs, genRhs) - } else { - val body = genEqEqPrimitive(lhs.tpe, rhs.tpe, genLhs, genRhs) - if (code == EQ) body - else js.UnaryOp(js.UnaryOp.Boolean_!, body) - } - } - - private lazy val externalEqualsNumNum: Symbol = - defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumNum) - private lazy val externalEqualsNumChar: Symbol = - NoSymbol // ctx.requiredMethod(BoxesRunTimeTypeRef, nme.equalsNumChar) // this method is private - private lazy val externalEqualsNumObject: Symbol = - defn.BoxesRunTimeModule.requiredMethod(nme.equalsNumObject) - private lazy val externalEquals: Symbol = - defn.BoxesRunTimeClass.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol - - /** Gen JS code for a call to Any.== */ - private def genEqEqPrimitive(ltpe: Type, rtpe: Type, lsrc: js.Tree, rsrc: js.Tree)( - implicit pos: Position): js.Tree = { - ctx.debuglog(s"$ltpe == $rtpe") - val lsym = ltpe.widenDealias.typeSymbol.asClass - val rsym = rtpe.widenDealias.typeSymbol.asClass - - /* True if the equality comparison is between values that require the - * use of the rich equality comparator - * (scala.runtime.BoxesRunTime.equals). - * This is the case when either side of the comparison might have a - * run-time type subtype of java.lang.Number or java.lang.Character, - * **which includes when either is a JS type**. - * When it is statically known that both sides are equal and subtypes of - * Number or Character, not using the rich equality is possible (their - * own equals method will do ok.) - */ - val mustUseAnyComparator: Boolean = { - isJSType(lsym) || isJSType(rsym) || { - val p = ctx.platform - val areSameFinals = lsym.is(Final) && rsym.is(Final) && (ltpe =:= rtpe) - !areSameFinals && p.isMaybeBoxed(lsym) && p.isMaybeBoxed(rsym) - } - } - - if (mustUseAnyComparator) { - val equalsMethod: Symbol = { - // scalastyle:off line.size.limit - val ptfm = ctx.platform - if (lsym.derivesFrom(defn.BoxedNumberClass)) { - if (rsym.derivesFrom(defn.BoxedNumberClass)) externalEqualsNumNum - else if (rsym.derivesFrom(defn.BoxedCharClass)) externalEqualsNumObject // will be externalEqualsNumChar in 2.12, SI-9030 - else externalEqualsNumObject - } else externalEquals - // scalastyle:on line.size.limit - } - genModuleApplyMethod(equalsMethod, List(lsrc, rsrc)) - } else { - // if (lsrc eq null) rsrc eq null else lsrc.equals(rsrc) - if (lsym == defn.StringClass) { - // String.equals(that) === (this eq that) - js.BinaryOp(js.BinaryOp.===, lsrc, rsrc) - } else { - /* This requires to evaluate both operands in local values first. - * The optimizer will eliminate them if possible. - */ - val ltemp = js.VarDef(freshLocalIdent(), lsrc.tpe, mutable = false, lsrc) - val rtemp = js.VarDef(freshLocalIdent(), rsrc.tpe, mutable = false, rsrc) - js.Block( - ltemp, - rtemp, - js.If(js.BinaryOp(js.BinaryOp.===, ltemp.ref, js.Null()), - js.BinaryOp(js.BinaryOp.===, rtemp.ref, js.Null()), - genApplyMethod(ltemp.ref, defn.Any_equals, List(rtemp.ref)))( - jstpe.BooleanType)) - } - } - } - - /** Gen JS code for string concatenation. - */ - private def genStringConcat(tree: Apply, receiver: Tree, - args: List[Tree]): js.Tree = { - implicit val pos: Position = tree.pos - - val arg = args.head - - /* Primitive number types such as scala.Int have a - * def +(s: String): String - * method, which is why we have to box the lhs sometimes. - * Otherwise, both lhs and rhs are already reference types (Any or String) - * so boxing is not necessary (in particular, rhs is never a primitive). - */ - assert(!isPrimitiveValueType(receiver.tpe) || arg.tpe.isRef(defn.StringClass)) - assert(!isPrimitiveValueType(arg.tpe)) - - val genLhs = { - val genLhs0 = genExpr(receiver) - // Box the receiver if it is a primitive value - if (!isPrimitiveValueType(receiver.tpe)) genLhs0 - else makePrimitiveBox(genLhs0, receiver.tpe) - } - - val genRhs = genExpr(arg) - - js.BinaryOp(js.BinaryOp.String_+, genLhs, genRhs) - } - - /** Gen JS code for a call to Any.## */ - private def genScalaHash(tree: Apply, receiver: Tree): js.Tree = { - implicit val pos: Position = tree.pos - - genModuleApplyMethod(defn.ScalaRuntimeModule.requiredMethod(nme.hash_), - List(genExpr(receiver))) - } - - /** 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._ - - implicit val pos: Position = tree.pos - - val Apply(fun, args) = tree - val arrayObj = qualifierOf(fun) - - val genArray = genExpr(arrayObj) - val genArgs = args.map(genExpr) - - def elementType: Type = arrayObj.tpe.widenDealias match { - case defn.ArrayOf(el) => el - case JavaArrayType(el) => el - case tpe => - ctx.error(s"expected Array $tpe") - ErrorType - } - - def genSelect(): js.Tree = - js.ArraySelect(genArray, genArgs(0))(toIRType(elementType)) - - if (isArrayGet(code)) { - // get an item of the array - assert(args.length == 1, - s"Array get requires 1 argument, found ${args.length} in $tree") - genSelect() - } else if (isArraySet(code)) { - // set an item of the array - assert(args.length == 2, - s"Array set requires 2 arguments, found ${args.length} in $tree") - js.Assign(genSelect(), genArgs(1)) - } else { - // length of the array - js.ArrayLength(genArray) - } - } - - /** Gen JS code for a call to AnyRef.synchronized */ - private def genSynchronized(tree: Apply, isStat: Boolean): js.Tree = { - /* JavaScript is single-threaded, so we can drop the - * synchronization altogether. - */ - val Apply(fun, List(arg)) = tree - val receiver = qualifierOf(fun) - - val genReceiver = genExpr(receiver) - val genArg = genStatOrExpr(arg, isStat) - - genReceiver match { - case js.This() => - // common case for which there is no side-effect nor NPE - genArg - case _ => - implicit val pos: Position = tree.pos - /* TODO Check for a null receiver? - * In theory, it's UB, but that decision should be left for link time. - */ - js.Block(genReceiver, genArg) - } - } - - /** 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: Position = 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 - } - } - - /** Gen a call to the special `throw` method. */ - private def genThrow(tree: Apply, args: List[Tree]): js.Tree = { - implicit val pos: Position = tree.pos - val exception = args.head - val genException = genExpr(exception) - js.Throw { - if (exception.tpe.widenDealias.typeSymbol.derivesFrom(jsdefn.JavaScriptExceptionClass)) { - genModuleApplyMethod( - jsdefn.RuntimePackage_unwrapJavaScriptException, - List(genException)) - } else { - genException - } - } - } - - /** Gen a "normal" apply (to a true method). - * - * But even these are further refined into: - * * Methods of java.lang.String, which are redirected to the - * RuntimeString trait implementation. - * * Calls to methods of raw JS types (Scala.js -> JS interop) - * * Calls to methods in impl classes of Scala2 traits. - * * Regular method call - */ - private def genNormalApply(tree: Apply, isStat: Boolean): js.Tree = { - implicit val pos: Position = tree.pos - - val fun = tree.fun match { - case fun: Ident => desugarIdent(fun).get - case fun: Select => fun - } - val receiver = fun.qualifier - val args = tree.args - val sym = fun.symbol - - def isStringMethodFromObject: Boolean = sym.name match { - case nme.toString_ | nme.equals_ | nme.hashCode_ => true - case _ => false - } - - if (sym.owner == defn.StringClass && !isStringMethodFromObject) { - genApplyMethodOfString(genExpr(receiver), sym, genActualArgs(sym, args)) - } else if (isJSType(sym.owner)) { - //if (!isScalaJSDefinedJSClass(sym.owner) || isExposed(sym)) - genApplyJSMethodGeneric(tree, sym, genExpr(receiver), genActualJSArgs(sym, args), isStat) - /*else - genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))*/ - } else if (foreignIsImplClass(sym.owner)) { - genTraitImplApply(sym, args.map(genExpr)) - } else if (sym.isClassConstructor) { - // Calls to constructors are always statically linked - genApplyMethodStatically(genExpr(receiver), sym, genActualArgs(sym, args)) - } else { - genApplyMethod(genExpr(receiver), sym, genActualArgs(sym, args)) - } - } - - /** Gen JS code for a call to a JS method (of a subclass of `js.Any`). - * - * Basically it boils down to calling the method as a `JSBracketSelect`, - * without name mangling. But other aspects come into play: - * - * - Operator methods are translated to JS operators (not method calls) - * - `apply` is translated as a function call, i.e., `o()` instead of `o.apply()` - * - Scala varargs are turned into JS varargs (see `genPrimitiveJSArgs()`) - * - Getters and parameterless methods are translated as `JSBracketSelect` - * - 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)( - implicit pos: Position): js.Tree = { - - implicit val pos: Position = tree.pos - - def noSpread = !args.exists(_.isInstanceOf[js.JSSpread]) - val argc = args.size // meaningful only for methods that don't have varargs - - def requireNotSuper(): Unit = { - if (superIn.isDefined) - ctx.error("Illegal super call in Scala.js-defined JS class", tree.pos) - } - - def hasExplicitJSEncoding = { - sym.hasAnnotation(jsdefn.JSNameAnnot) || - sym.hasAnnotation(jsdefn.JSBracketAccessAnnot) || - sym.hasAnnotation(jsdefn.JSBracketCallAnnot) - } - - val boxedResult = sym.name match { - case JSUnaryOpMethodName(code) if argc == 0 => - requireNotSuper() - js.JSUnaryOp(code, receiver) - - case JSBinaryOpMethodName(code) if argc == 1 => - requireNotSuper() - js.JSBinaryOp(code, receiver, args.head) - - case nme.apply if !hasExplicitJSEncoding => - requireNotSuper() - if (jsdefn.isJSThisFunctionClass(sym.owner)) - js.JSBracketMethodApply(receiver, js.StringLiteral("call"), args) - else - js.JSFunctionApply(receiver, args) - - case _ => - def jsFunName = js.StringLiteral(jsNameOf(sym)) - - def genSuperReference(propName: js.Tree): js.Tree = { - superIn.fold[js.Tree] { - js.JSBracketSelect(receiver, propName) - } { superInSym => - js.JSSuperBracketSelect( - jstpe.ClassType(encodeClassFullName(superInSym)), - receiver, propName) - } - } - - def genSelectGet(propName: js.Tree): js.Tree = - genSuperReference(propName) - - 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] { - js.JSBracketMethodApply( - receiver, methodName, args) - } { superInSym => - js.JSSuperBracketCall( - jstpe.ClassType(encodeClassFullName(superInSym)), - receiver, methodName, args) - } - } - - if (isJSGetter(sym)) { - assert(noSpread && argc == 0) - genSelectGet(jsFunName) - } else if (isJSSetter(sym)) { - assert(noSpread && argc == 1) - genSelectSet(jsFunName, 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 { - case List(keyArg) => - genSelectGet(keyArg) - case List(keyArg, valueArg) => - genSelectSet(keyArg, valueArg) - } - } else if (isJSBracketCall(sym)) { - val (methodName, actualArgs) = extractFirstArg(args) - genCall(methodName, actualArgs) - } else { - genCall(jsFunName, args) - } - } - - if (isStat) { - boxedResult - } else { - val tpe = ctx.atPhase(ctx.elimErasedValueTypePhase) { implicit ctx => - sym.info.finalResultType - } - unbox(boxedResult, tpe) - } - } - - private object JSUnaryOpMethodName { - private val map = Map( - nme.UNARY_+ -> js.JSUnaryOp.+, - nme.UNARY_- -> js.JSUnaryOp.-, - nme.UNARY_~ -> js.JSUnaryOp.~, - nme.UNARY_! -> js.JSUnaryOp.! - ) - - def unapply(name: Names.TermName): Option[js.JSUnaryOp.Code] = - map.get(name) - } - - private object JSBinaryOpMethodName { - private val map = Map( - nme.ADD -> js.JSBinaryOp.+, - nme.SUB -> js.JSBinaryOp.-, - nme.MUL -> js.JSBinaryOp.*, - nme.DIV -> js.JSBinaryOp./, - nme.MOD -> js.JSBinaryOp.%, - - nme.LSL -> js.JSBinaryOp.<<, - nme.ASR -> js.JSBinaryOp.>>, - nme.LSR -> js.JSBinaryOp.>>>, - nme.OR -> js.JSBinaryOp.|, - nme.AND -> js.JSBinaryOp.&, - nme.XOR -> js.JSBinaryOp.^, - - nme.LT -> js.JSBinaryOp.<, - nme.LE -> js.JSBinaryOp.<=, - nme.GT -> js.JSBinaryOp.>, - nme.GE -> js.JSBinaryOp.>=, - - nme.ZAND -> js.JSBinaryOp.&&, - nme.ZOR -> js.JSBinaryOp.|| - ) - - def unapply(name: Names.TermName): Option[js.JSBinaryOp.Code] = - map.get(name) - } - - /** Extract the first argument in a list of actual arguments. - * - * 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]) = { - 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) - } - - /** Gen JS code for a call to a polymorphic method. - * - * The only methods that reach the back-end as polymorphic are - * `isInstanceOf` and `asInstanceOf`. - * - * (Well, in fact `DottyRunTime.newRefArray` too, but it is handled as a - * primitive instead.) - */ - private def genTypeApply(tree: TypeApply): js.Tree = { - implicit val pos: Position = tree.pos - - val TypeApply(fun, targs) = tree - - val sym = fun.symbol - val receiver = qualifierOf(fun) - - val to = targs.head.tpe - - assert(!isPrimitiveValueType(receiver.tpe), - s"Found receiver of type test with primitive type ${receiver.tpe} at $pos") - assert(!isPrimitiveValueType(to), - s"Found target type of type test with primitive type ${receiver.tpe} at $pos") - - val genReceiver = genExpr(receiver) - - if (sym == defn.Any_asInstanceOf) { - genAsInstanceOf(genReceiver, to) - } else if (sym == defn.Any_isInstanceOf) { - genIsInstanceOf(tree, genReceiver, to) - } else { - throw new FatalError( - s"Unexpected type application $fun with symbol ${sym.fullName}") - } - } - - /** Gen JS code for a Java Seq literal. */ - private def genJavaSeqLiteral(tree: JavaSeqLiteral): js.Tree = { - implicit val pos: Position = tree.pos - - val genElems = tree.elems.map(genExpr) - val arrayType = toReferenceType(tree.tpe).asInstanceOf[jstpe.ArrayType] - js.ArrayValue(arrayType, genElems) - } - - /** Gen JS code for a closure. - * - * Input: a `Closure` tree of the form - * {{{ - * Closure(env, call, functionalInterface) - * }}} - * representing the pseudo-syntax - * {{{ - * { (p1, ..., pm) => call(env1, ..., envn, p1, ..., pm) }: functionInterface - * }}} - * where `envi` are identifiers in the local scope. The qualifier of `call` - * is also implicitly captured. - * - * Output: a `js.Closure` tree of the form - * {{{ - * js.Closure(formalCaptures, formalParams, body, actualCaptures) - * }}} - * representing the pseudo-syntax - * {{{ - * lambda<formalCapture1 = actualCapture1, ..., formalCaptureN = actualCaptureN>( - * formalParam1, ..., formalParamM) = body - * }}} - * where the `actualCaptures` and `body` are, in general, arbitrary - * expressions. But in this case, `actualCaptures` will be identifiers from - * `env`, and the `body` will be of the form - * {{{ - * call(formalCapture1.ref, ..., formalCaptureN.ref, - * formalParam1.ref, ...formalParamM.ref) - * }}} - * - * When the `js.Closure` node is evaluated, i.e., when the closure value is - * created, the expressions of the `actualCaptures` are evaluated, and the - * results of those evaluations is "stored" in the environment of the - * closure as the corresponding `formalCapture`. - * - * When we later *call* the closure, the `formalCaptures` already have their - * values from the environment, and they are available in the `body`. The - * `formalParams` of the created closure receive their values from the - * actual arguments at the call-site of the closure, and they are also - * available in the `body`. - */ - private def genClosure(tree: Closure): js.Tree = { - implicit val pos: Position = tree.pos - val Closure(env, call, functionalInterface) = tree - - val envSize = env.size - - val (fun, args) = call match { - // case Apply(fun, args) => (fun, args) // Conjectured not to happen - case t @ Select(_, _) => (t, Nil) - case t @ Ident(_) => (t, Nil) - } - val sym = fun.symbol - - val qualifier = qualifierOf(fun) - val allCaptureValues = qualifier :: env - - val (formalCaptures, actualCaptures) = allCaptureValues.map { value => - implicit val pos: Position = value.pos - val formalIdent = value match { - case Ident(name) => freshLocalIdent(name.toString) - case This(_) => freshLocalIdent("this") - case _ => freshLocalIdent() - } - val formalCapture = - js.ParamDef(formalIdent, toIRType(value.tpe), mutable = false, rest = false) - val actualCapture = genExpr(value) - (formalCapture, actualCapture) - }.unzip - - val formalParamNames = sym.info.paramNamess.flatten.drop(envSize) - val formalParamTypes = sym.info.paramTypess.flatten.drop(envSize) - val (formalParams, actualParams) = formalParamNames.zip(formalParamTypes).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 genBody = { - val thisCaptureRef :: argCaptureRefs = formalCaptures.map(_.ref) - val call = genApplyMethod(thisCaptureRef, sym, argCaptureRefs ::: actualParams) - box(call, sym.info.finalResultType) - } - - val closure = js.Closure(formalCaptures, formalParams, genBody, actualCaptures) - ctx.debuglog(closure.toString) - - val funInterfaceSym = functionalInterface.tpe.widenDealias.typeSymbol - if (jsdefn.isJSFunctionClass(funInterfaceSym)) { - closure - } else { - assert(!funInterfaceSym.exists || defn.isFunctionClass(funInterfaceSym), - s"Invalid functional interface $funInterfaceSym reached the back-end") - val cls = "sjsr_AnonFunction" + formalParams.size - val ctor = js.Ident("init___sjs_js_Function" + formalParams.size) - js.New(jstpe.ClassType(cls), ctor, List(closure)) - } - } - - /** Boxes a value of the given type before `elimErasedValueType`. - * - * This should be used when sending values to a JavaScript context, which - * is erased/boxed at the IR level, although it is not erased at the - * dotty/JVM level. - * - * @param expr Tree to be boxed if needed. - * @param tpeEnteringElimErasedValueType The type of `expr` as it was - * entering the `elimErasedValueType` phase. - */ - private def box(expr: js.Tree, tpeEnteringElimErasedValueType: Type)( - implicit pos: Position): js.Tree = { - - tpeEnteringElimErasedValueType match { - case tpe if isPrimitiveValueType(tpe) => - makePrimitiveBox(expr, tpe) - - /*case tpe: ErasedValueType => - val boxedClass = tpe.valueClazz - val ctor = boxedClass.primaryConstructor - genNew(boxedClass, ctor, List(expr))*/ - - case _ => - expr - } - } - - /** Unboxes a value typed as Any to the given type before `elimErasedValueType`. - * - * This should be used when receiving values from a JavaScript context, - * which is erased/boxed at the IR level, although it is not erased at the - * dotty/JVM level. - * - * @param expr Tree to be extracted. - * @param tpeEnteringElimErasedValueType The type of `expr` as it was - * entering the `elimErasedValueType` phase. - */ - private def unbox(expr: js.Tree, tpeEnteringElimErasedValueType: Type)( - implicit pos: Position): js.Tree = { - - tpeEnteringElimErasedValueType match { - case tpe if isPrimitiveValueType(tpe) => - makePrimitiveUnbox(expr, tpe) - - /*case tpe: ErasedValueType => - val boxedClass = tpe.valueClazz - val unboxMethod = boxedClass.derivedValueClassUnbox - val content = genApplyMethod( - genAsInstanceOf(expr, tpe), unboxMethod, Nil) - if (unboxMethod.tpe.resultType <:< tpe.erasedUnderlying) - content - else - fromAny(content, tpe.erasedUnderlying)*/ - - case tpe => - genAsInstanceOf(expr, tpe) - } - } - - /** Gen JS code for an asInstanceOf cast (for reference types only) */ - private def genAsInstanceOf(value: js.Tree, to: Type)( - implicit pos: Position): js.Tree = { - - val sym = to.widenDealias.typeSymbol - - if (sym == defn.ObjectClass || isJSType(sym)) { - /* asInstanceOf[Object] always succeeds, and - * asInstanceOf to a raw JS type is completely erased. - */ - value - } else { - js.AsInstanceOf(value, toReferenceType(to)) - } - } - - /** Gen JS code for an isInstanceOf test (for reference types only) */ - private def genIsInstanceOf(tree: Tree, value: js.Tree, to: Type): js.Tree = { - implicit val pos: Position = tree.pos - val sym = to.widenDealias.typeSymbol - - if (sym == defn.ObjectClass) { - js.BinaryOp(js.BinaryOp.!==, value, js.Null()) - } else if (isJSType(sym)) { - if (sym.is(Trait)) { - ctx.error( - s"isInstanceOf[${sym.fullName}] not supported because it is a JS trait", - tree.pos) - js.BooleanLiteral(true) - } else { - js.Unbox(js.JSBinaryOp( - js.JSBinaryOp.instanceof, value, genLoadJSConstructor(sym)), 'Z') - } - } else { - js.IsInstanceOf(value, toReferenceType(to)) - } - } - - /** Gen a dynamically linked call to a Scala method. */ - private def genApplyMethod(receiver: js.Tree, - methodSym: Symbol, arguments: List[js.Tree])( - implicit pos: Position): js.Tree = { - js.Apply(receiver, encodeMethodSym(methodSym), arguments)( - toIRType(patchedResultType(methodSym))) - } - - /** Gen a statically linked call to an instance method. */ - private def genApplyMethodStatically(receiver: js.Tree, method: Symbol, - arguments: List[js.Tree])(implicit pos: Position): js.Tree = { - val className = encodeClassFullName(method.owner) - val methodIdent = encodeMethodSym(method) - val resultType = toIRType(patchedResultType(method)) - js.ApplyStatically(receiver, jstpe.ClassType(className), - methodIdent, arguments)(resultType) - } - - /** Gen a call to a static method. */ - private def genApplyStatic(method: Symbol, arguments: List[js.Tree])( - implicit pos: Position): js.Tree = { - val cls = jstpe.ClassType(encodeClassFullName(method.owner)) - val methodIdent = encodeMethodSym(method) - js.ApplyStatic(cls, methodIdent, arguments)( - toIRType(patchedResultType(method))) - } - - /** Gen a call to a Scala2 impl class method. */ - private def genTraitImplApply(method: Symbol, arguments: List[js.Tree])( - implicit pos: Position): js.Tree = { - genApplyStatic(method, arguments) - } - - /** Gen a call to a non-exposed method of a non-native JS class. */ - private def genApplyJSClassMethod(receiver: js.Tree, method: Symbol, - arguments: List[js.Tree])(implicit pos: Position): js.Tree = { - genApplyStatic(method, receiver :: arguments) - } - - /** Gen a call to a method of a Scala top-level module. */ - private def genModuleApplyMethod(methodSym: Symbol, arguments: List[js.Tree])( - implicit pos: Position): js.Tree = { - 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 _ => - throw new FatalError( - s"makePrimitiveBox requires a primitive type, found $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) => - assert(cls.length == 1) - (cls.charAt(0): @switch) 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) - } - - case _ => - throw new FatalError( - s"makePrimitiveUnbox requires a primitive type, found $tpe at $pos") - } - } - - /** Gen actual actual arguments to Scala method call. - * Returns a list of the transformed arguments. - * - * This tries to optimize repeated arguments (varargs) by turning them - * into js.WrappedArray instead of Scala wrapped arrays. - */ - private def genActualArgs(sym: Symbol, args: List[Tree])( - implicit pos: Position): List[js.Tree] = { - args.map(genExpr) - /*val wereRepeated = exitingPhase(currentRun.typerPhase) { - sym.tpe.params.map(p => isScalaRepeatedParamType(p.tpe)) - } - - if (wereRepeated.size > args.size) { - // Should not happen, but let's not crash - args.map(genExpr) - } else { - /* Arguments that are in excess compared to the type signature after - * erasure are lambda-lifted arguments. They cannot be repeated, hence - * the extension to `false`. - */ - for ((arg, wasRepeated) <- args.zipAll(wereRepeated, EmptyTree, false)) yield { - if (wasRepeated) { - tryGenRepeatedParamAsJSArray(arg, handleNil = false).fold { - genExpr(arg) - } { genArgs => - genNew(WrappedArrayClass, WrappedArray_ctor, - List(js.JSArrayConstr(genArgs))) - } - } else { - genExpr(arg) - } - } - }*/ - } - - /** Gen actual actual arguments to a JS method call. - * Returns a list of the transformed arguments. - * - * - TODO Repeated arguments (varargs) are expanded - * - Default arguments are omitted or replaced by undefined - * - All arguments are boxed - * - * Repeated arguments that cannot be expanded at compile time (i.e., if a - * Seq is passed to a varargs parameter with the syntax `seq: _*`) will be - * 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] = { - - def paramNamesAndTypes(implicit ctx: Context): List[(Names.TermName, Type)] = - sym.info.paramNamess.flatten.zip(sym.info.paramTypess.flatten) - - val wereRepeated = ctx.atPhase(ctx.elimRepeatedPhase) { implicit ctx => - for ((name, tpe) <- paramNamesAndTypes) - yield (name -> tpe.isRepeatedParam) - }.toMap - - val paramTypes = ctx.atPhase(ctx.elimErasedValueTypePhase) { implicit ctx => - paramNamesAndTypes - }.toMap - - var reversedArgs: List[js.Tree] = Nil - - for ((arg, (paramName, paramType)) <- args.zip(paramNamesAndTypes)) { - val wasRepeated = wereRepeated.getOrElse(paramName, false) - if (wasRepeated) { - reversedArgs = - genJSRepeatedParam(arg) reverse_::: reversedArgs - } else { - val unboxedArg = genExpr(arg) - val boxedArg = unboxedArg match { - case js.UndefinedParam() => - unboxedArg - case _ => - val tpe = paramTypes.getOrElse(paramName, paramType) - box(unboxedArg, tpe) - } - reversedArgs ::= boxedArg - } - } - - /* Remove all consecutive js.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]) - - /* Find remaining js.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 - } - - reversedArgs.reverse - } - - /** Gen JS code for a repeated param of a JS method. - * - * In this case `arg` has type `Seq[T]` for some `T`, but the result should - * be an expanded list of the elements in the sequence. So this method - * takes care of the conversion. - * - * It is specialized for the shapes of tree generated by the desugaring - * of repeated params in Scala, so that these are actually expanded at - * compile-time. - * - * Otherwise, it returns a `JSSpread` with the `Seq` converted to a - * `js.Array`. - */ - private def genJSRepeatedParam(arg: Tree): List[js.Tree] = { - tryGenRepeatedParamAsJSArray(arg, handleNil = true).getOrElse { - /* Fall back to calling runtime.genTraversableOnce2jsArray - * to perform the conversion to js.Array, then wrap in a Spread - * operator. - */ - implicit val pos: Position = arg.pos - val jsArrayArg = genModuleApplyMethod( - jsdefn.RuntimePackage_genTraversableOnce2jsArray, - List(genExpr(arg))) - List(js.JSSpread(jsArrayArg)) - } - } - - /** Try and expand an actual argument to a repeated param `(xs: T*)`. - * - * This method recognizes the shapes of tree generated by the desugaring - * of repeated params in Scala, and expands them. - * If `arg` does not have the shape of a generated repeated param, this - * method returns `None`. - */ - private def tryGenRepeatedParamAsJSArray(arg: Tree, - handleNil: Boolean): Option[List[js.Tree]] = { - implicit val pos: Position = arg.pos - - // Given a method `def foo(args: T*)` - arg match { - // foo(arg1, arg2, ..., argN) where N > 0 - case MaybeAsInstanceOf(WrapArray(MaybeAsInstanceOf(array: JavaSeqLiteral))) => - /* Value classes in arrays are already boxed, so no need to use - * the type before erasure. - * TODO Is this true in dotty? - */ - Some(array.elems.map(e => box(genExpr(e), e.tpe))) - - // foo() - case Ident(_) if handleNil && arg.symbol == defn.NilModule => - Some(Nil) - - // foo(argSeq: _*) - cannot be optimized - case _ => - None - } - } - - private object MaybeAsInstanceOf { - def unapply(tree: Tree): Some[Tree] = tree match { - case TypeApply(asInstanceOf_? @ Select(base, _), _) - if asInstanceOf_?.symbol == defn.Any_asInstanceOf => - Some(base) - case _ => - Some(tree) - } - } - - 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 - } - - def unapply(tree: Apply): Option[Tree] = tree match { - case Apply(wrapArray_?, List(wrapped)) if isWrapArray(wrapArray_?.symbol) => - Some(wrapped) - case _ => - None - } - } - - /** Gen JS code for loading a Java static field. - */ - private def genLoadStaticField(sym: Symbol)(implicit pos: Position): js.Tree = { - /* Actually, there is no static member in Scala.js. If we come here, that - * is because we found the symbol in a Java-emitted .class in the - * classpath. But the corresponding implementation in Scala.js will - * actually be a val in the companion module. - */ - - if (sym == defn.BoxedUnit_UNIT) { - js.Undefined() - } else { - val instance = genLoadModule(sym.owner) - val method = encodeStaticMemberSym(sym) - js.Apply(instance, method, Nil)(toIRType(sym.info)) - } - } - - /** Gen JS code for loading a module. - * - * Can be given either the module symbol, or its module class symbol. - */ - 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 - - if (isJSType(sym)) { - if (isScalaJSDefinedJSClass(sym)) - js.LoadJSModule(jstpe.ClassType(encodeClassFullName(sym))) - else if (sym.derivesFrom(jsdefn.JSGlobalScopeClass)) - genLoadJSGlobal() - else - genLoadNativeJSModule(sym) - } else { - js.LoadModule(jstpe.ClassType(encodeClassFullName(sym))) - } - } - - /** Gen JS code representing the constructor of a JS class. */ - private def genLoadJSConstructor(sym: Symbol)( - implicit pos: Position): js.Tree = { - assert(!isStaticModule(sym) && !sym.is(Trait), - s"genPrimitiveJSClass called with non-class $sym") - js.LoadJSConstructor(jstpe.ClassType(encodeClassFullName(sym))) - } - - /** Gen JS code representing a native JS module. */ - private def genLoadNativeJSModule(sym: Symbol)( - implicit pos: Position): js.Tree = { - require(sym.is(ModuleClass), - s"genLoadNativeJSModule called with non-module $sym") - fullJSNameOf(sym).split('.').foldLeft(genLoadJSGlobal()) { (memo, chunk) => - js.JSBracketSelect(memo, js.StringLiteral(chunk)) - } - } - - /** Gen JS code to load the JavaScript global scope. */ - private def genLoadJSGlobal()(implicit pos: Position): js.Tree = { - js.JSBracketSelect( - js.JSBracketSelect(js.JSLinkingInfo(), js.StringLiteral("envInfo")), - js.StringLiteral("global")) - } - - /** Generate a Class[_] value (e.g. coming from classOf[T]) */ - private def genClassConstant(tpe: Type)(implicit pos: Position): js.Tree = - js.ClassOf(toReferenceType(tpe)) - - private def isStaticModule(sym: Symbol): Boolean = - sym.is(Module) && sym.isStatic - - private def isPrimitiveValueType(tpe: Type): Boolean = { - tpe.widenDealias match { - case JavaArrayType(_) => false - case t => t.typeSymbol.asClass.isPrimitiveValueClass - } - } - -} diff --git a/src/dotty/tools/backend/sjs/JSDefinitions.scala b/src/dotty/tools/backend/sjs/JSDefinitions.scala deleted file mode 100644 index bd0b74031..000000000 --- a/src/dotty/tools/backend/sjs/JSDefinitions.scala +++ /dev/null @@ -1,199 +0,0 @@ -package dotty.tools.backend.sjs - -import dotty.tools.dotc.core._ - -import Types._ -import Contexts._ -import Symbols._ -import Names._ -import StdNames._ -import Decorators._ - -import dotty.tools.dotc.config.SJSPlatform - -object JSDefinitions { - /** The Scala.js-specific definitions for the current context. */ - def jsdefn(implicit ctx: Context): JSDefinitions = - ctx.platform.asInstanceOf[SJSPlatform].jsDefinitions -} - -final class JSDefinitions()(implicit ctx: Context) { - - lazy val InlineAnnotType: TypeRef = ctx.requiredClassRef("scala.inline") - def InlineAnnot(implicit ctx: Context) = InlineAnnotType.symbol.asClass - lazy val NoinlineAnnotType: TypeRef = ctx.requiredClassRef("scala.noinline") - def NoinlineAnnot(implicit ctx: Context) = NoinlineAnnotType.symbol.asClass - - lazy val ScalaJSJSPackageVal = ctx.requiredPackage("scala.scalajs.js") - lazy val ScalaJSJSPackageClass = ScalaJSJSPackageVal.moduleClass.asClass - lazy val JSPackage_typeOfR = ScalaJSJSPackageClass.requiredMethodRef("typeOf") - 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 - - lazy val JSNativeAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.native") - def JSNativeAnnot(implicit ctx: Context) = JSNativeAnnotType.symbol.asClass - - lazy val JSAnyType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.Any") - def JSAnyClass(implicit ctx: Context) = JSAnyType.symbol.asClass - lazy val JSObjectType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.Object") - def JSObjectClass(implicit ctx: Context) = JSObjectType.symbol.asClass - 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 - - lazy val JSFunctionType = (0 to 22).map(n => ctx.requiredClassRef("scala.scalajs.js.Function" + n)).toArray - def JSFunctionClass(n: Int)(implicit ctx: Context) = JSFunctionType(n).symbol.asClass - lazy val JSThisFunctionType = (0 to 21).map(n => ctx.requiredClassRef("scala.scalajs.js.ThisFunction" + n)).toArray - def JSThisFunctionClass(n: Int)(implicit ctx: Context) = JSThisFunctionType(n).symbol.asClass - - lazy val RuntimeExceptionType: TypeRef = ctx.requiredClassRef("java.lang.RuntimeException") - def RuntimeExceptionClass(implicit ctx: Context) = RuntimeExceptionType.symbol.asClass - lazy val JavaScriptExceptionType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.JavaScriptException") - def JavaScriptExceptionClass(implicit ctx: Context) = JavaScriptExceptionType.symbol.asClass - - lazy val JSNameAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSName") - def JSNameAnnot(implicit ctx: Context) = JSNameAnnotType.symbol.asClass - lazy val JSFullNameAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSFullName") - def JSFullNameAnnot(implicit ctx: Context) = JSFullNameAnnotType.symbol.asClass - lazy val JSBracketAccessAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSBracketAccess") - def JSBracketAccessAnnot(implicit ctx: Context) = JSBracketAccessAnnotType.symbol.asClass - lazy val JSBracketCallAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSBracketCall") - def JSBracketCallAnnot(implicit ctx: Context) = JSBracketCallAnnotType.symbol.asClass - lazy val JSExportAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSExport") - def JSExportAnnot(implicit ctx: Context) = JSExportAnnotType.symbol.asClass - lazy val JSExportDescendentObjectsAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSExportDescendentObjects") - def JSExportDescendentObjectsAnnot(implicit ctx: Context) = JSExportDescendentObjectsAnnotType.symbol.asClass - lazy val JSExportDescendentClassesAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSExportDescendentClasses") - def JSExportDescendentClassesAnnot(implicit ctx: Context) = JSExportDescendentClassesAnnotType.symbol.asClass - lazy val JSExportAllAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSExportAll") - def JSExportAllAnnot(implicit ctx: Context) = JSExportAllAnnotType.symbol.asClass - lazy val JSExportNamedAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.JSExportNamed") - def JSExportNamedAnnot(implicit ctx: Context) = JSExportNamedAnnotType.symbol.asClass - lazy val RawJSTypeAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.RawJSType") - def RawJSTypeAnnot(implicit ctx: Context) = RawJSTypeAnnotType.symbol.asClass - lazy val ExposedJSMemberAnnotType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.annotation.ExposedJSMember") - def ExposedJSMemberAnnot(implicit ctx: Context) = ExposedJSMemberAnnotType.symbol.asClass - - lazy val JSAnyModuleRef = ctx.requiredModuleRef("scala.scalajs.js.Any") - def JSAnyModule(implicit ctx: Context) = JSAnyModuleRef.symbol - lazy val JSAny_fromFunctionR = (0 to 22).map(n => JSAnyModule.requiredMethodRef("fromFunction" + n)).toArray - def JSAny_fromFunction(n: Int)(implicit ctx: Context) = JSAny_fromFunctionR(n).symbol - - lazy val JSDynamicModuleRef = ctx.requiredModuleRef("scala.scalajs.js.Dynamic") - def JSDynamicModule(implicit ctx: Context) = JSDynamicModuleRef.symbol - lazy val JSDynamic_newInstanceR = JSDynamicModule.requiredMethodRef("newInstance") - def JSDynamic_newInstance(implicit ctx: Context) = JSDynamic_newInstanceR.symbol - - lazy val JSDynamicLiteralModuleRef = JSDynamicModule.moduleClass.requiredValueRef("literal") - def JSDynamicLiteralModule(implicit ctx: Context) = JSDynamicLiteralModuleRef.symbol - lazy val JSDynamicLiteral_applyDynamicNamedR = JSDynamicLiteralModule.requiredMethodRef("applyDynamicNamed") - def JSDynamicLiteral_applyDynamicNamed(implicit ctx: Context) = JSDynamicLiteral_applyDynamicNamedR.symbol - lazy val JSDynamicLiteral_applyDynamicR = JSDynamicLiteralModule.requiredMethodRef("applyDynamic") - def JSDynamicLiteral_applyDynamic(implicit ctx: Context) = JSDynamicLiteral_applyDynamicR.symbol - - 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 - lazy val JSArray_applyR = JSArrayModule.requiredMethodRef(nme.apply) - def JSArray_apply(implicit ctx: Context) = JSArray_applyR.symbol - - lazy val JSThisFunctionModuleRef = ctx.requiredModuleRef("scala.scalajs.js.ThisFunction") - def JSThisFunctionModule(implicit ctx: Context) = JSThisFunctionModuleRef.symbol - lazy val JSThisFunction_fromFunctionR = (1 to 22).map(n => JSThisFunctionModule.requiredMethodRef("fromFunction" + n)).toArray - def JSThisFunction_fromFunction(n: Int)(implicit ctx: Context) = JSThisFunction_fromFunctionR(n - 1).symbol - - lazy val JSConstructorTagModuleRef = ctx.requiredModuleRef("scala.scalajs.js.ConstructorTag") - def JSConstructorTagModule(implicit ctx: Context) = JSConstructorTagModuleRef.symbol - 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 - - lazy val WrappedArrayType: TypeRef = ctx.requiredClassRef("scala.scalajs.js.WrappedArray") - def WrappedArrayClass(implicit ctx: Context) = WrappedArrayType.symbol.asClass - - lazy val ScalaRunTime_isArrayR = defn.ScalaRuntimeModule.requiredMethodRef("isArray", List(???, ???)) - def ScalaRunTime_isArray(implicit ctx: Context): Symbol = ScalaRunTime_isArrayR.symbol - - lazy val BoxesRunTime_boxToCharacterR = defn.BoxesRunTimeModule.requiredMethodRef("boxToCharacter") - def BoxesRunTime_boxToCharacter(implicit ctx: Context): Symbol = BoxesRunTime_boxToCharacterR.symbol - lazy val BoxesRunTime_unboxToCharR = defn.BoxesRunTimeModule.requiredMethodRef("unboxToChar") - def BoxesRunTime_unboxToChar(implicit ctx: Context): Symbol = BoxesRunTime_unboxToCharR.symbol - - /** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */ - private def scalajsClassName(cls: Symbol)(implicit ctx: Context): TypeName = - if (cls.isClass && cls.owner == ScalaJSJSPackageClass) cls.asClass.name - else EmptyTypeName - - /** Is the given `cls` a class of the form `scala.scalajs.js.prefixN` where - * `N` is a number. - * - * This is similar to `isVarArityClass` in `Definitions.scala`. - */ - private def isScalaJSVarArityClass(cls: Symbol, prefix: Name): Boolean = { - val name = scalajsClassName(cls) - name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit) - } - - def isJSFunctionClass(cls: Symbol): Boolean = - isScalaJSVarArityClass(cls, nme.Function) - - private val ThisFunctionName = termName("ThisFunction") - - def isJSThisFunctionClass(cls: Symbol): Boolean = - isScalaJSVarArityClass(cls, ThisFunctionName) - -} diff --git a/src/dotty/tools/backend/sjs/JSEncoding.scala b/src/dotty/tools/backend/sjs/JSEncoding.scala deleted file mode 100644 index e8ea3258b..000000000 --- a/src/dotty/tools/backend/sjs/JSEncoding.scala +++ /dev/null @@ -1,389 +0,0 @@ -package dotty.tools.backend.sjs - -import scala.collection.mutable - -import dotty.tools.FatalError - -import dotty.tools.dotc.core._ -import Periods._ -import SymDenotations._ -import Contexts._ -import Types._ -import Symbols._ -import Denotations._ -import NameOps._ -import StdNames._ - -import org.scalajs.core.ir -import ir.{Trees => js, Types => jstpe} - -import ScopedVar.withScopedVars -import JSDefinitions._ -import JSInterop._ - -/** Encoding of symbol names for JavaScript - * - * Some issues that this encoding solves: - * * Overloading: encode the full signature in the JS name - * * Same scope for fields and methods of a class - * * Global access to classes and modules (by their full name) - * - * @author Sébastien Doeraene - */ -object JSEncoding { - - /** Signature separator string (between parameter types) */ - private final val SignatureSep = "__" - - /** Name given to the local Scala.js environment variable */ - private final val ScalaJSEnvironmentName = "ScalaJS" - - implicit class SymOps(val self: Symbol) extends AnyVal { - def unexpandedName(implicit ctx: Context): Names.Name = - self.name.unexpandedName - } - - implicit class MyNameOps(val self: Names.Name) extends AnyVal { - def decoded: String = self.decode.toString - } - - // Fresh local name generator ---------------------------------------------- - - class LocalNameGenerator { - import LocalNameGenerator._ - - private val usedLocalNames = mutable.Set.empty[String] - private val localSymbolNames = mutable.Map.empty[Symbol, String] - - def localSymbolName(sym: Symbol)(implicit ctx: Context): String = - localSymbolNames.getOrElseUpdate(sym, freshName(sym.name.toString)) - - def freshLocalIdent()(implicit pos: ir.Position): js.Ident = - js.Ident(freshName(), None) - - def freshLocalIdent(base: String)(implicit pos: ir.Position): js.Ident = - js.Ident(freshName(base), Some(base)) - - private def freshName(base: String = "x"): String = { - var suffix = 1 - var longName = base - while (usedLocalNames(longName) || isReserved(longName)) { - suffix += 1 - longName = base+"$"+suffix - } - usedLocalNames += longName - mangleJSName(longName) - } - } - - private object LocalNameGenerator { - private val isReserved = - Set("arguments", "eval", ScalaJSEnvironmentName) - } - - // Encoding methods ---------------------------------------------------------- - - def encodeLabelSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.Ident = { - require(sym.is(Flags.Label), "encodeLabelSym called with non-label symbol: " + sym) - js.Ident(localNames.localSymbolName(sym), Some(sym.unexpandedName.decoded)) - } - - private def allRefClasses(implicit ctx: Context): Set[Symbol] = { - //TODO - /*(Set(ObjectRefClass, VolatileObjectRefClass) ++ - refClass.values ++ volatileRefClass.values)*/ - Set() - } - - def encodeFieldSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - require(sym.owner.isClass && sym.isTerm && !sym.is(Flags.Method) && !sym.is(Flags.Module), - "encodeFieldSym called with non-field symbol: " + sym) - - val name0 = encodeMemberNameInternal(sym) - val name = - if (name0.charAt(name0.length()-1) != ' ') name0 - else name0.substring(0, name0.length()-1) - - /* We have to special-case fields of Ref types (IntRef, ObjectRef, etc.) - * because they are emitted as private by our .scala source files, but - * they are considered public at use site since their symbols come from - * Java-emitted .class files. - */ - val idSuffix = - if (sym.is(Flags.Private) || allRefClasses.contains(sym.owner)) - sym.owner.asClass.baseClasses.size.toString - else - "f" - - val encodedName = name + "$" + idSuffix - js.Ident(mangleJSName(encodedName), Some(sym.unexpandedName.decoded)) - } - - def encodeMethodSym(sym: Symbol, reflProxy: Boolean = false)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy) - js.Ident(encodedName + paramsString, - Some(sym.unexpandedName.decoded + paramsString)) - } - - def encodeMethodName(sym: Symbol, reflProxy: Boolean = false)( - implicit ctx: Context): String = { - val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy) - 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) = { - require(sym.is(Flags.Method), "encodeMethodSym called with non-method symbol: " + sym) - - def name = encodeMemberNameInternal(sym) - - val encodedName = { - if (sym.isClassConstructor) { - "init_" - } else if (sym.is(Flags.Private)) { - (mangleJSName(name) + SignatureSep + "p" + - sym.owner.asClass.baseClasses.size.toString) - } else { - mangleJSName(name) - } - } - - val paramsString = makeParamsString(sym, reflProxy, inRTClass) - - (encodedName, paramsString) - } - - def encodeStaticMemberSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - require(sym.is(Flags.JavaStaticTerm), - "encodeStaticMemberSym called with non-static symbol: " + sym) - js.Ident( - mangleJSName(encodeMemberNameInternal(sym)) + - makeParamsString(List(internalName(sym.info))), - Some(sym.unexpandedName.decoded)) - } - - def encodeLocalSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.Ident = { - require(!sym.owner.isClass && sym.isTerm && !sym.is(Flags.Method) && !sym.is(Flags.Module), - "encodeLocalSym called with non-local symbol: " + sym) - js.Ident(localNames.localSymbolName(sym), Some(sym.unexpandedName.decoded)) - } - - def foreignIsImplClass(sym: Symbol)(implicit ctx: Context): Boolean = - sym.name.isImplClassName - - def encodeClassType(sym: Symbol)(implicit ctx: Context): jstpe.Type = { - if (sym == defn.ObjectClass) jstpe.AnyType - else if (isJSType(sym)) jstpe.AnyType - else { - assert(sym != defn.ArrayClass, - "encodeClassType() cannot be called with ArrayClass") - jstpe.ClassType(encodeClassFullName(sym)) - } - } - - def encodeClassFullNameIdent(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - 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) - } - - private def encodeMemberNameInternal(sym: Symbol)( - implicit ctx: Context): String = { - sym.name.toString.replace("_", "$und").replace("~", "$tilde") - } - - def toIRType(tp: Type)(implicit ctx: Context): jstpe.Type = { - val refType = toReferenceTypeInternal(tp) - refType._1 match { - case tpe: jstpe.ClassType => - val sym = refType._2 - if (sym.asClass.isPrimitiveValueClass) { - if (sym == defn.BooleanClass) - jstpe.BooleanType - else if (sym == defn.FloatClass) - jstpe.FloatType - else if (sym == defn.DoubleClass) - jstpe.DoubleType - else if (sym == defn.LongClass) - jstpe.LongType - else if (sym == defn.UnitClass) - jstpe.NoType - else - jstpe.IntType - } else { - if (sym == defn.ObjectClass || isJSType(sym)) - jstpe.AnyType - else if (sym == defn.NothingClass) - jstpe.NothingType - else if (sym == defn.NullClass) - jstpe.NullType - else - tpe - } - - case tpe: jstpe.ArrayType => - tpe - } - } - - def toReferenceType(tp: Type)(implicit ctx: Context): jstpe.ReferenceType = - toReferenceTypeInternal(tp)._1 - - private def toReferenceTypeInternal(tp: Type)( - implicit ctx: Context): (jstpe.ReferenceType, 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) = { - assert(sym.isClass, sym) - //assert(sym != defn.ArrayClass || isCompilingArray, sym) - (jstpe.ClassType(encodeClassFullName(sym)), 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) = { - //assert(sym.isType && isCompilingArray, sym) - (jstpe.ClassType(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) - - 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 - - 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) - - /* 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. - * The type in the AnnotationInfo is an AnnotatedTpe. Tested in jvm/annotations.scala. - */ - case a @ AnnotatedType(t, _) => - //debuglog(s"typeKind of annotated type $a") - toReferenceTypeInternal(t) - } - } - - /** Patches the result type of a method symbol to sanitize it. - * - * For some reason, dotc thinks that the `info.resultType`of an - * `isConstructor` method (for classes or traits) is the enclosing class - * or trait, but the bodies and usages act as if the result type was `Unit`. - * - * This method returns `UnitType` for constructor methods, and otherwise - * `sym.info.resultType`. - */ - def patchedResultType(sym: Symbol)(implicit ctx: Context): Type = - if (sym.isConstructor) defn.UnitType - else sym.info.resultType - - // Encoding of method signatures - - private def makeParamsString(sym: Symbol, reflProxy: Boolean, - inRTClass: Boolean)( - implicit ctx: Context): String = { - val tpe = sym.info - - val paramTypeNames0 = tpe.firstParamTypes.map(internalName(_)) - - val hasExplicitThisParameter = - inRTClass || isScalaJSDefinedJSClass(sym.owner) - val paramTypeNames = - if (!hasExplicitThisParameter) paramTypeNames0 - else encodeClassFullName(sym.owner) :: paramTypeNames0 - - val paramAndResultTypeNames = { - if (sym.isClassConstructor) - paramTypeNames - else if (reflProxy) - paramTypeNames :+ "" - else - paramTypeNames :+ internalName(patchedResultType(sym)) - } - makeParamsString(paramAndResultTypeNames) - } - - private def makeParamsString(paramAndResultTypeNames: List[String]) = - paramAndResultTypeNames.mkString(SignatureSep, SignatureSep, "") - - /** Computes the internal name for a type. */ - private def internalName(tpe: Type)(implicit ctx: Context): String = - encodeReferenceType(toReferenceType(tpe)) - - /** Encodes a [[Types.ReferenceType]], 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 - } - } - - /** Mangles names that are illegal in JavaScript by prepending a `$`. - * Also mangles names that would collide with these mangled names. - */ - private def mangleJSName(name: String) = - if (js.isKeyword(name) || name(0).isDigit || name(0) == '$') "$" + name - else name -} diff --git a/src/dotty/tools/backend/sjs/JSInterop.scala b/src/dotty/tools/backend/sjs/JSInterop.scala deleted file mode 100644 index 6d66c3206..000000000 --- a/src/dotty/tools/backend/sjs/JSInterop.scala +++ /dev/null @@ -1,110 +0,0 @@ -package dotty.tools.backend.sjs - -import dotty.tools.dotc.core._ -import Contexts._ -import Flags._ -import Symbols._ -import NameOps._ -import StdNames._ - -import JSDefinitions._ - -/** Management of the interoperability with JavaScript. */ -object JSInterop { - - /** Is this symbol a JavaScript type? */ - def isJSType(sym: Symbol)(implicit ctx: Context): Boolean = { - //sym.hasAnnotation(jsdefn.RawJSTypeAnnot) - ctx.atPhase(ctx.erasurePhase) { implicit ctx => - sym.derivesFrom(jsdefn.JSAnyClass) - } - } - - /** Is this symbol a Scala.js-defined JS class, i.e., a non-native JS class? */ - def isScalaJSDefinedJSClass(sym: Symbol)(implicit ctx: Context): Boolean = - isJSType(sym) && !sym.hasAnnotation(jsdefn.JSNativeAnnot) - - /** Should this symbol be translated into a JS getter? - * - * This is true for any parameterless method, i.e., defined without `()`. - * Unlike `SymDenotations.isGetter`, it applies to user-defined methods as - * much as *accessor* methods created for `val`s and `var`s. - */ - def isJSGetter(sym: Symbol)(implicit ctx: Context): Boolean = { - sym.info.firstParamTypes.isEmpty && ctx.atPhase(ctx.erasurePhase) { implicit ctx => - sym.info.isParameterless - } - } - - /** Should this symbol be translated into a JS setter? - * - * This is true for any method whose name ends in `_=`. - * Unlike `SymDenotations.isGetter`, it applies to user-defined methods as - * much as *accessor* methods created for `var`s. - */ - def isJSSetter(sym: Symbol)(implicit ctx: Context): Boolean = - sym.name.isSetterName && sym.is(Method) - - /** Should this symbol be translated into a JS bracket access? - * - * This is true for methods annotated with `@JSBracketAccess`. - */ - def isJSBracketAccess(sym: Symbol)(implicit ctx: Context): Boolean = - sym.hasAnnotation(jsdefn.JSBracketAccessAnnot) - - /** Should this symbol be translated into a JS bracket call? - * - * This is true for methods annotated with `@JSBracketCall`. - */ - def isJSBracketCall(sym: Symbol)(implicit ctx: Context): Boolean = - sym.hasAnnotation(jsdefn.JSBracketCallAnnot) - - /** Is this symbol a default param accessor for a JS method? - * - * For default param accessors of *constructors*, we need to test whether - * the companion *class* of the owner is a JS type; not whether the owner - * is a JS type. - */ - def isJSDefaultParam(sym: Symbol)(implicit ctx: Context): Boolean = { - sym.name.isDefaultGetterName && { - val owner = sym.owner - if (owner.is(ModuleClass) && - sym.name.asTermName.defaultGetterToMethod == nme.CONSTRUCTOR) { - isJSType(owner.linkedClass) - } else { - isJSType(owner) - } - } - } - - /** Gets the unqualified JS name of a symbol. - * - * If it is not explicitly specified with an `@JSName` annotation, the - * JS name is inferred from the Scala name. - */ - def jsNameOf(sym: Symbol)(implicit ctx: Context): String = { - sym.getAnnotation(jsdefn.JSNameAnnot).flatMap(_.argumentConstant(0)).fold { - val base = sym.name.unexpandedName.decode.toString.stripSuffix("_=") - if (sym.is(ModuleClass)) base.stripSuffix("$") - else if (!sym.is(Method)) base.stripSuffix(" ") - else base - } { constant => - constant.stringValue - } - } - - /** Gets the fully qualified JS name of a static class of module Symbol. - * - * This is the JS name of the symbol qualified by the fully qualified JS - * name of its original owner if the latter is a native JS object. - */ - def fullJSNameOf(sym: Symbol)(implicit ctx: Context): String = { - assert(sym.isClass, s"fullJSNameOf called for non-class symbol $sym") - sym.getAnnotation(jsdefn.JSFullNameAnnot).flatMap(_.argumentConstant(0)).fold { - jsNameOf(sym) - } { constant => - constant.stringValue - } - } - -} diff --git a/src/dotty/tools/backend/sjs/JSPositions.scala b/src/dotty/tools/backend/sjs/JSPositions.scala deleted file mode 100644 index 10570da00..000000000 --- a/src/dotty/tools/backend/sjs/JSPositions.scala +++ /dev/null @@ -1,65 +0,0 @@ -package dotty.tools.backend.sjs - -import dotty.tools.dotc.core._ -import Contexts._ -import dotty.tools.dotc.util.Positions -import Positions.Position - -import org.scalajs.core.ir - -/** Conversion utilities from dotty Positions to IR Positions. */ -class JSPositions()(implicit ctx: Context) { - - /** Implicit conversion from dotty Position to ir.Position. */ - implicit def pos2irPos(pos: Positions.Position): ir.Position = { - if (!pos.exists) ir.Position.NoPosition - else { - val source = pos2irPosCache.toIRSource(ctx.compilationUnit.source) - val sourcePos = ctx.compilationUnit.source.atPos(pos) - // dotty positions are 1-based but IR positions are 0-based - ir.Position(source, sourcePos.line-1, sourcePos.column-1) - } - } - - /** Implicitly materializes an ir.Position from an implicit dotty Position. */ - implicit def implicitPos2irPos( - implicit pos: Positions.Position): ir.Position = { - pos2irPos(pos) - } - - private[this] object pos2irPosCache { // scalastyle:ignore - import dotty.tools.dotc.util._ - - private[this] var lastDotcSource: SourceFile = null - private[this] var lastIRSource: ir.Position.SourceFile = null - - def toIRSource(dotcSource: SourceFile): ir.Position.SourceFile = { - if (dotcSource != lastDotcSource) { - lastIRSource = convert(dotcSource) - lastDotcSource = dotcSource - } - lastIRSource - } - - private[this] def convert(dotcSource: SourceFile): ir.Position.SourceFile = { - dotcSource.file.file match { - case null => - new java.net.URI( - "virtualfile", // Pseudo-Scheme - dotcSource.file.path, // Scheme specific part - null // Fragment - ) - case file => - val srcURI = file.toURI - def matches(pat: java.net.URI) = pat.relativize(srcURI) != srcURI - - // TODO - /*scalaJSOpts.sourceURIMaps.collectFirst { - case ScalaJSOptions.URIMap(from, to) if matches(from) => - val relURI = from.relativize(srcURI) - to.fold(relURI)(_.resolve(relURI)) - } getOrElse*/ srcURI - } - } - } -} diff --git a/src/dotty/tools/backend/sjs/JSPrimitives.scala b/src/dotty/tools/backend/sjs/JSPrimitives.scala deleted file mode 100644 index 6c3c5715c..000000000 --- a/src/dotty/tools/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 // <special-ops>.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 = new mutable.HashMap[Symbol, 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/src/dotty/tools/backend/sjs/ScopedVar.scala b/src/dotty/tools/backend/sjs/ScopedVar.scala deleted file mode 100644 index 0e47f7b79..000000000 --- a/src/dotty/tools/backend/sjs/ScopedVar.scala +++ /dev/null @@ -1,38 +0,0 @@ -package dotty.tools.backend.sjs - -import language.implicitConversions - -class ScopedVar[A](init: A) { - import ScopedVar.Assignment - - private var value = init - - def this()(implicit ev: Null <:< A) = this(ev(null)) - - def get: A = value - def :=(newValue: A): Assignment[A] = new Assignment(this, newValue) -} - -object ScopedVar { - class Assignment[T](scVar: ScopedVar[T], value: T) { - private[ScopedVar] def push(): AssignmentStackElement[T] = { - val stack = new AssignmentStackElement(scVar, scVar.value) - scVar.value = value - stack - } - } - - private class AssignmentStackElement[T](scVar: ScopedVar[T], oldValue: T) { - private[ScopedVar] def pop(): Unit = { - scVar.value = oldValue - } - } - - implicit def toValue[T](scVar: ScopedVar[T]): T = scVar.get - - def withScopedVars[T](ass: Assignment[_]*)(body: => T): T = { - val stack = ass.map(_.push()) - try body - finally stack.reverse.foreach(_.pop()) - } -} diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 4bc5263e9..ad3249be2 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -17,7 +17,6 @@ import core.DenotTransformers.DenotTransformer import core.Denotations.SingleDenotation import dotty.tools.backend.jvm.{LabelDefs, GenBCode, CollectSuperCalls} -import dotty.tools.backend.sjs.GenSJSIR /** The central class of the dotc compiler. The job of a compiler is to create * runs, which process given `phases` in a given `rootContext`. @@ -98,7 +97,6 @@ class Compiler { new DropInlined, // Drop Inlined nodes, since backend has no use for them new MoveStatics, // Move static methods to companion classes new LabelDefs), // Converts calls to labels to jumps - List(new GenSJSIR), // Generate .js code List(new GenBCode) // Generate JVM bytecode ) @@ -117,20 +115,7 @@ class Compiler { */ def rootContext(implicit ctx: Context): Context = { ctx.initialize()(ctx) - val actualPhases = if (ctx.settings.scalajs.value) { - // Remove phases that Scala.js does not want - phases.mapConserve(_.filter { - case _: FunctionalInterfaces => false - case _ => true - }).filter(_.nonEmpty) - } else { - // Remove Scala.js-related phases - phases.mapConserve(_.filter { - case _: GenSJSIR => false - case _ => true - }).filter(_.nonEmpty) - } - ctx.setPhasePlan(actualPhases) + ctx.setPhasePlan(phases) val rootScope = new MutableScope val bootstrap = ctx.fresh .setPeriod(Period(nextRunId, FirstPhaseId)) diff --git a/src/dotty/tools/dotc/config/PathResolver.scala b/src/dotty/tools/dotc/config/PathResolver.scala index 1f9ee7eec..14a44531a 100644 --- a/src/dotty/tools/dotc/config/PathResolver.scala +++ b/src/dotty/tools/dotc/config/PathResolver.scala @@ -46,18 +46,7 @@ object PathResolver { def classPathEnv = envOrElse("CLASSPATH", "") def sourcePathEnv = envOrElse("SOURCEPATH", "") - def javaBootClassPath = - propOrElse("sun.boot.class.path", searchForBootClasspath) - .split(":") - .filterNot { jar => - // let's blacklist locally compiled classes: - jar.contains("/dotty/library/target/classes") || - jar.contains("/dotty/library/target/scala-2.11/classes") || - jar.contains("/dotty/interfaces/target/classes") || - jar.contains("/dotty/target/scala-2.11/classes") || - jar.contains("/dotty/target/classes") - } - .mkString(":") + def javaBootClassPath = propOrElse("sun.boot.class.path", searchForBootClasspath) def javaExtDirs = propOrEmpty("java.ext.dirs") def scalaHome = propOrEmpty("scala.home") @@ -266,8 +255,15 @@ class PathResolver(implicit ctx: Context) { def containers = Calculated.containers lazy val result: JavaClassPath = { - val (dottyJars, others) = containers.partition(_.name.contains("dotty")) - val cp = new JavaClassPath((dottyJars ++ others).toIndexedSeq, context) + // Prioritize `dotty.jar` and `dotty-lib.jar` to shadow others + val (dottyJars, others) = + containers.partition(x => x.name.contains("dotty-lib.jar") || x.name.contains("dotty.jar")) + // Then any jars with `dotty` in the name - putting them before scala-library + val (dottyCp, remaining) = + others.partition(_.name.contains("dotty-")) + + val cp = new JavaClassPath((dottyJars ++ dottyCp ++ remaining).toIndexedSeq, context) + if (settings.Ylogcp.value) { Console.println("Classpath built from " + settings.toConciseString(ctx.sstate)) Console.println("Defaults: " + PathResolver.Defaults) diff --git a/src/dotty/tools/dotc/config/SJSPlatform.scala b/src/dotty/tools/dotc/config/SJSPlatform.scala deleted file mode 100644 index 3ec8049ae..000000000 --- a/src/dotty/tools/dotc/config/SJSPlatform.scala +++ /dev/null @@ -1,18 +0,0 @@ -package dotty.tools.dotc.config - -import dotty.tools.dotc.core._ -import Contexts._ -import Symbols._ - -import dotty.tools.backend.sjs.JSDefinitions - -class SJSPlatform()(implicit ctx: Context) extends JavaPlatform { - - /** Scala.js-specific definitions. */ - val jsDefinitions: JSDefinitions = new JSDefinitions() - - /** Is the SAMType `cls` also a SAM under the rules of the Scala.js back-end? */ - override def isSam(cls: ClassSymbol)(implicit ctx: Context): Boolean = - defn.isFunctionClass(cls) || jsDefinitions.isJSFunctionClass(cls) - -} diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index edc68588d..639c4d111 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -27,7 +27,7 @@ import reporting._ import collection.mutable import collection.immutable.BitSet import printing._ -import config.{Settings, ScalaSettings, Platform, JavaPlatform, SJSPlatform} +import config.{Settings, ScalaSettings, Platform, JavaPlatform} import language.implicitConversions import DenotTransformers.DenotTransformer import util.Property.Key @@ -550,8 +550,7 @@ object Contexts { } protected def newPlatform(implicit ctx: Context): Platform = - if (settings.scalajs.value) new SJSPlatform - else new JavaPlatform + new JavaPlatform /** The loader that loads the members of _root_ */ def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root) |