diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index edc1c07e3785..d28c775023e3 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -30,7 +30,10 @@ import dotty.tools.dotc.util.Spans.Span import org.scalajs.ir import org.scalajs.ir.{ClassKind, Position, Trees => js, Types => jstpe} -import js.OptimizerHints +import org.scalajs.ir.Names.{ClassName, MethodName, SimpleMethodName} +import org.scalajs.ir.OriginalName +import org.scalajs.ir.OriginalName.NoOriginalName +import org.scalajs.ir.Trees.OptimizerHints import JSEncoding._ import JSInterop._ @@ -67,7 +70,7 @@ class JSCodeGen()(implicit ctx: Context) { 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 thisLocalVarIdent = new ScopedVar[Option[js.LocalIdent]] private val undefinedDefaultParams = new ScopedVar[mutable.Set[Symbol]] private def withNewLocalNameScope[A](body: => A): A = { @@ -87,15 +90,15 @@ class JSCodeGen()(implicit ctx: Context) { private def currentClassType = encodeClassType(currentClassSym) /** Returns a new fresh local identifier. */ - private def freshLocalIdent()(implicit pos: Position): js.Ident = + private def freshLocalIdent()(implicit pos: Position): js.LocalIdent = localNames.get.freshLocalIdent() /** Returns a new fresh local identifier. */ - private def freshLocalIdent(base: String)(implicit pos: Position): js.Ident = + private def freshLocalIdent(base: String)(implicit pos: Position): js.LocalIdent = localNames.get.freshLocalIdent(base) /** Returns a new fresh local identifier. */ - private def freshLocalIdent(base: TermName)(implicit pos: Position): js.Ident = + private def freshLocalIdent(base: TermName)(implicit pos: Position): js.LocalIdent = localNames.get.freshLocalIdent(base) // Compilation unit -------------------------------------------------------- @@ -222,7 +225,7 @@ class JSCodeGen()(implicit ctx: Context) { "if their companion module is JS native.") }*/ - val classIdent = encodeClassFullNameIdent(sym) + val classIdent = encodeClassNameIdent(sym) val isHijacked = false //isHijackedBoxedClass(sym) // Optimizer hints @@ -333,9 +336,10 @@ class JSCodeGen()(implicit ctx: Context) { val classDefinition = js.ClassDef( classIdent, + originalNameOfClass(sym), kind, None, - Some(encodeClassFullNameIdent(sym.superClass)), + Some(encodeClassNameIdent(sym.superClass)), genClassInterfaces(sym), None, None, @@ -357,7 +361,7 @@ class JSCodeGen()(implicit ctx: Context) { val sym = td.symbol.asClass implicit val pos: Position = sym.span - val classIdent = encodeClassFullNameIdent(sym) + val classIdent = encodeClassNameIdent(sym) val kind = { if (sym.is(Trait)) ClassKind.AbstractJSType else if (sym.is(ModuleClass)) ClassKind.NativeJSModuleClass @@ -365,7 +369,7 @@ class JSCodeGen()(implicit ctx: Context) { } val superClass = if (sym.is(Trait)) None - else Some(encodeClassFullNameIdent(sym.superClass)) + else Some(encodeClassNameIdent(sym.superClass)) val jsNativeLoadSpec = { if (sym.is(Trait)) None else if (sym.hasAnnotation(jsdefn.JSGlobalScopeAnnot)) None @@ -377,6 +381,7 @@ class JSCodeGen()(implicit ctx: Context) { js.ClassDef( classIdent, + originalNameOfClass(sym), kind, None, superClass, @@ -394,7 +399,7 @@ class JSCodeGen()(implicit ctx: Context) { val sym = td.symbol.asClass implicit val pos: Position = sym.span - val classIdent = encodeClassFullNameIdent(sym) + val classIdent = encodeClassNameIdent(sym) val generatedMethods = new mutable.ListBuffer[js.MethodDef] @@ -419,6 +424,7 @@ class JSCodeGen()(implicit ctx: Context) { js.ClassDef( classIdent, + originalNameOfClass(sym), ClassKind.Interface, None, None, @@ -431,12 +437,12 @@ class JSCodeGen()(implicit ctx: Context) { } private def genClassInterfaces(sym: ClassSymbol)( - implicit pos: Position): List[js.Ident] = { + implicit pos: Position): List[js.ClassIdent] = { import dotty.tools.dotc.transform.SymUtils._ for { intf <- sym.directlyInheritedTraits } yield { - encodeClassFullNameIdent(intf) + encodeClassNameIdent(intf) } } @@ -495,7 +501,7 @@ class JSCodeGen()(implicit ctx: Context) { }*/ val flags = js.MemberFlags.empty.withMutable(f.is(Mutable)) - js.FieldDef(flags, name, irTpe) + js.FieldDef(flags, name, originalNameOfField(f), irTpe) }).toList } @@ -505,7 +511,8 @@ class JSCodeGen()(implicit ctx: Context) { implicit pos: Position): js.MethodDef = { js.MethodDef( js.MemberFlags.empty.withNamespace(js.MemberNamespace.StaticConstructor), - js.Ident(ir.Definitions.StaticInitializerName), + js.MethodIdent(ir.Names.StaticInitializerName), + NoOriginalName, Nil, jstpe.NoType, Some(stats))( @@ -556,8 +563,8 @@ class JSCodeGen()(implicit ctx: Context) { (paramName, paramInfo) <- ctor.info.paramNamess.flatten.zip(ctor.info.paramInfoss.flatten) } yield { val paramType = js.ClassOf(toTypeRef(paramInfo)) - val paramDef = js.ParamDef(freshLocalIdent(paramName), jstpe.AnyType, - mutable = false, rest = false) + val paramDef = js.ParamDef(freshLocalIdent(paramName), + NoOriginalName, jstpe.AnyType, mutable = false, rest = false) val actualParam = unbox(paramDef.ref, paramInfo) (paramType, paramDef, actualParam) }).unzip3 @@ -565,7 +572,7 @@ class JSCodeGen()(implicit ctx: Context) { val paramTypesArray = js.JSArrayConstr(parameterTypes) val newInstanceFun = js.Closure(arrow = true, Nil, formalParams, { - js.New(encodeClassRef(sym), encodeMethodSym(ctor), actualParams) + js.New(encodeClassName(sym), encodeMethodSym(ctor), actualParams) }, Nil) js.JSArrayConstr(List(paramTypesArray, newInstanceFun)) @@ -628,18 +635,19 @@ class JSCodeGen()(implicit ctx: Context) { val isJSClassConstructor = sym.isClassConstructor && isScalaJSDefinedJSClass(currentClassSym) - val methodName: js.PropertyName = encodeMethodSym(sym) + val methodName = encodeMethodSym(sym) + val originalName = originalNameOfMethod(sym) def jsParams = for (param <- params) yield { implicit val pos = param.span - js.ParamDef(encodeLocalSym(param), toIRType(param.info), - mutable = false, rest = false) + js.ParamDef(encodeLocalSym(param), originalNameOfLocal(param), + toIRType(param.info), mutable = false, rest = false) } /*if (primitives.isPrimitive(sym)) { None } else*/ if (sym.is(Deferred)) { - Some(js.MethodDef(js.MemberFlags.empty, methodName, + Some(js.MethodDef(js.MemberFlags.empty, methodName, originalName, jsParams, toIRType(patchedResultType(sym)), None)( OptimizerHints.empty, None)) } else /*if (isJSNativeCtorDefaultParam(sym)) { @@ -679,7 +687,7 @@ class JSCodeGen()(implicit ctx: Context) { } else*/ if (sym.isClassConstructor) { val namespace = js.MemberNamespace.Constructor js.MethodDef(js.MemberFlags.empty.withNamespace(namespace), - methodName, jsParams, jstpe.NoType, + methodName, originalName, jsParams, jstpe.NoType, Some(genStat(rhs)))(optimizerHints, None) } else { val namespace = if (isMethodStaticInIR(sym)) { @@ -690,7 +698,7 @@ class JSCodeGen()(implicit ctx: Context) { else js.MemberNamespace.Public } val resultIRType = toIRType(patchedResultType(sym)) - genMethodDef(namespace, methodName, + genMethodDef(namespace, methodName, originalName, params, resultIRType, rhs, optimizerHints) } } @@ -709,18 +717,15 @@ class JSCodeGen()(implicit ctx: Context) { * Methods Scala.js-defined JS classes are compiled as static methods taking * an explicit parameter for their `this` value. */ - private def genMethodDef(namespace: js.MemberNamespace, methodName: js.PropertyName, - paramsSyms: List[Symbol], resultIRType: jstpe.Type, + private def genMethodDef(namespace: js.MemberNamespace, methodName: js.MethodIdent, + originalName: OriginalName,paramsSyms: List[Symbol], resultIRType: jstpe.Type, tree: Tree, optimizerHints: OptimizerHints): js.MethodDef = { implicit val pos = tree.span - ctx.debuglog("genMethod " + methodName.encodedName) - ctx.debuglog("") - val jsParams = for (param <- paramsSyms) yield { implicit val pos = param.span - js.ParamDef(encodeLocalSym(param), toIRType(param.info), - mutable = false, rest = false) + js.ParamDef(encodeLocalSym(param), originalNameOfLocal(param), + toIRType(param.info), mutable = false, rest = false) } def genBody() = localNames.makeLabeledIfRequiresEnclosingReturn(resultIRType) { @@ -730,7 +735,7 @@ class JSCodeGen()(implicit ctx: Context) { //if (!isScalaJSDefinedJSClass(currentClassSym)) { val flags = js.MemberFlags.empty.withNamespace(namespace) - js.MethodDef(flags, methodName, jsParams, resultIRType, Some(genBody()))( + js.MethodDef(flags, methodName, originalName, jsParams, resultIRType, Some(genBody()))( optimizerHints, None) /*} else { assert(!namespace.isStatic, tree.span) @@ -844,7 +849,7 @@ class JSCodeGen()(implicit ctx: Context) { undefinedDefaultParams += sym js.Skip() case _ => - js.VarDef(encodeLocalSym(sym), + js.VarDef(encodeLocalSym(sym), originalNameOfLocal(sym), toIRType(sym.info), sym.is(Mutable), rhsTree) } @@ -911,7 +916,7 @@ class JSCodeGen()(implicit ctx: Context) { fromAny(boxed, enteringPhase(currentRun.posterasurePhase)(sym.tpe)) } else*/ { - js.Select(genExpr(qualifier), + js.Select(genExpr(qualifier), encodeClassName(sym.owner), encodeFieldSym(sym))(toIRType(sym.info)) } @@ -1008,7 +1013,8 @@ class JSCodeGen()(implicit ctx: Context) { js.Assign(genLhs, boxedRhs) } else {*/ js.Assign( - js.Select(genQual, encodeFieldSym(sym))(toIRType(sym.info)), + js.Select(genQual, encodeClassName(sym.owner), + encodeFieldSym(sym))(toIRType(sym.info)), genRhs) //} case _ => @@ -1128,7 +1134,7 @@ class JSCodeGen()(implicit ctx: Context) { } val (exceptValDef, exceptVar) = if (mightCatchJavaScriptException) { - val valDef = js.VarDef(freshLocalIdent("e"), + val valDef = js.VarDef(freshLocalIdent("e"), NoOriginalName, encodeClassType(defn.ThrowableClass), mutable = false, { genModuleApplyMethod(jsdefn.Runtime_wrapJavaScriptException, origExceptVar :: Nil) }) @@ -1150,17 +1156,20 @@ class JSCodeGen()(implicit ctx: Context) { case Ident(nme.WILDCARD) => (defn.ThrowableType, None) case Bind(_, _) => - (pat.symbol.info, Some(encodeLocalSym(pat.symbol))) + val ident = encodeLocalSym(pat.symbol) + val origName = originalNameOfLocal(pat.symbol) + (pat.symbol.info, Some(ident, origName)) }) // Generate the body that must be executed if the exception matches val bodyWithBoundVar = (boundVar match { case None => genStatOrExpr(body, isStat) - case Some(bv) => + case Some((boundVarIdent, boundVarOriginalName)) => val castException = genAsInstanceOf(exceptVar, tpe) js.Block( - js.VarDef(bv, toIRType(tpe), mutable = false, castException), + js.VarDef(boundVarIdent, boundVarOriginalName, toIRType(tpe), + mutable = false, castException), genStatOrExpr(body, isStat)) }) @@ -1173,7 +1182,7 @@ class JSCodeGen()(implicit ctx: Context) { } } - js.TryCatch(body, exceptIdent, + js.TryCatch(body, exceptIdent, NoOriginalName, js.Block(exceptValDef, handler))(resultType) } @@ -1248,9 +1257,9 @@ class JSCodeGen()(implicit ctx: Context) { if (isStaticModule(currentClassSym) && !isModuleInitialized && currentMethodSym.get.isClassConstructor) { isModuleInitialized = true - val thisType = jstpe.ClassType(encodeClassFullName(currentClassSym)) - val initModule = js.StoreModule(encodeClassRef(currentClassSym), - js.This()(thisType)) + val className = encodeClassName(currentClassSym) + val thisType = jstpe.ClassType(className) + val initModule = js.StoreModule(className, js.This()(thisType)) js.Block(superCall, initModule) } else { superCall @@ -1290,8 +1299,8 @@ class JSCodeGen()(implicit ctx: Context) { else js.JSNew(genLoadJSConstructor(clsSym), genActualJSArgs(ctor, args)) } else { toTypeRef(tpe) match { - case cls: jstpe.ClassRef => - js.New(cls, encodeMethodSym(ctor), genActualArgs(ctor, args)) + case jstpe.ClassRef(className) => + js.New(className, encodeMethodSym(ctor), genActualArgs(ctor, args)) case other => throw new FatalError(s"Non ClassRef cannot be instantiated: $other") @@ -1306,20 +1315,16 @@ class JSCodeGen()(implicit ctx: Context) { private def genNewHijackedClass(clazz: Symbol, ctor: Symbol, args: List[js.Tree])(implicit pos: SourcePosition): js.Tree = { - val encodedName = encodeClassFullName(clazz) + val className = encodeClassName(clazz) val moduleClass = clazz.companionModule.moduleClass - val js.Ident(initName, origName) = encodeMethodSym(ctor) - val newMethodName = initName match { - case "init___" => - "$new__" + encodedName - case _ => - "$new" + initName.stripPrefix("init_") + "__" + encodedName - } - val newMethodIdent = js.Ident(newMethodName, origName) + val initName = encodeMethodSym(ctor).name + val newName = MethodName(newSimpleMethodName, initName.paramTypeRefs, + jstpe.ClassRef(className)) + val newMethodIdent = js.MethodIdent(newName) js.Apply(js.ApplyFlags.empty, genLoadModule(moduleClass), newMethodIdent, args)( - jstpe.ClassType(encodedName)) + jstpe.ClassType(className)) } /** Gen JS code for a primitive method call. */ @@ -1694,8 +1699,8 @@ class JSCodeGen()(implicit ctx: Context) { /* 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) + val ltemp = js.VarDef(freshLocalIdent(), NoOriginalName, lsrc.tpe, mutable = false, lsrc) + val rtemp = js.VarDef(freshLocalIdent(), NoOriginalName, rsrc.tpe, mutable = false, rsrc) js.Block( ltemp, rtemp, @@ -1917,7 +1922,7 @@ class JSCodeGen()(implicit ctx: Context) { case nme.apply if !hasExplicitJSEncoding => requireNotSuper() if (jsdefn.isJSThisFunctionClass(sym.owner)) - js.JSBracketMethodApply(ruleOutGlobalScope(receiver), js.StringLiteral("call"), args) + js.JSMethodApply(ruleOutGlobalScope(receiver), js.StringLiteral("call"), args) else js.JSFunctionApply(ruleOutGlobalScope(receiver), args) @@ -1926,9 +1931,9 @@ class JSCodeGen()(implicit ctx: Context) { def genSuperReference(propName: js.Tree): js.Tree = { jsSuperClassValue.fold[js.Tree] { - genJSBracketSelectOrGlobalRef(receiver, propName) + genJSSelectOrGlobalRef(receiver, propName) } { superClassValue => - js.JSSuperBracketSelect(superClassValue, ruleOutGlobalScope(receiver), propName) + js.JSSuperSelect(superClassValue, ruleOutGlobalScope(receiver), propName) } } @@ -1940,9 +1945,9 @@ class JSCodeGen()(implicit ctx: Context) { def genCall(methodName: js.Tree, args: List[js.TreeOrJSSpread]): js.Tree = { jsSuperClassValue.fold[js.Tree] { - genJSBracketMethodApplyOrGlobalRefApply(receiver, methodName, args) + genJSMethodApplyOrGlobalRefApply(receiver, methodName, args) } { superClassValue => - js.JSSuperBracketCall(superClassValue, ruleOutGlobalScope(receiver), methodName, args) + js.JSSuperMethodCall(superClassValue, ruleOutGlobalScope(receiver), methodName, args) } } @@ -2140,13 +2145,13 @@ class JSCodeGen()(implicit ctx: Context) { val formalAndActualCaptures = allCaptureValues.map { value => implicit val pos = value.span - val formalIdent = value match { - case Ident(name) => freshLocalIdent(name.toString) - case This(_) => freshLocalIdent("this") - case _ => freshLocalIdent() + val (formalIdent, originalName) = value match { + case Ident(name) => (freshLocalIdent(name.toString), OriginalName(name.toString)) + case This(_) => (freshLocalIdent("this"), thisOriginalName) + case _ => (freshLocalIdent(), NoOriginalName) } - val formalCapture = - js.ParamDef(formalIdent, toIRType(value.tpe), mutable = false, rest = false) + val formalCapture = js.ParamDef(formalIdent, originalName, + toIRType(value.tpe), mutable = false, rest = false) val actualCapture = genExpr(value) (formalCapture, actualCapture) } @@ -2158,7 +2163,8 @@ class JSCodeGen()(implicit ctx: Context) { val formalAndActualParams = formalParamNamesAndTypes.map { case (name, tpe) => val formalParam = js.ParamDef(freshLocalIdent(name.toString), - jstpe.AnyType, mutable = false, rest = false) + OriginalName(name.toString), jstpe.AnyType, mutable = false, + rest = false) val actualParam = unbox(formalParam.ref, tpe) (formalParam, actualParam) } @@ -2184,9 +2190,11 @@ class JSCodeGen()(implicit ctx: Context) { } 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.ClassRef(cls), ctor, List(closure)) + val formalCount = formalParams.size + val cls = ClassName("scala.scalajs.runtime.AnonFunction" + formalCount) + val ctorName = MethodName.constructor( + jstpe.ClassRef(ClassName("scala.scalajs.js.Function" + formalCount)) :: Nil) + js.New(cls, js.MethodIdent(ctorName), List(closure)) } } @@ -2261,7 +2269,7 @@ class JSCodeGen()(implicit ctx: Context) { */ value } else { - js.AsInstanceOf(value, toTypeRef(to)) + js.AsInstanceOf(value, toIRType(to)) } } @@ -2279,11 +2287,12 @@ class JSCodeGen()(implicit ctx: Context) { pos) js.BooleanLiteral(true) } else { - js.Unbox(js.JSBinaryOp( - js.JSBinaryOp.instanceof, value, genLoadJSConstructor(sym)), 'Z') + js.AsInstanceOf(js.JSBinaryOp( + js.JSBinaryOp.instanceof, value, genLoadJSConstructor(sym)), + jstpe.BooleanType) } } else { - js.IsInstanceOf(value, toTypeRef(to)) + js.IsInstanceOf(value, toIRType(to)) } } @@ -2312,7 +2321,7 @@ class JSCodeGen()(implicit ctx: Context) { val flags = js.ApplyFlags.empty .withPrivate(method.isPrivate && !method.isClassConstructor) .withConstructor(method.isClassConstructor) - js.ApplyStatically(flags, receiver, encodeClassRef(method.owner), + js.ApplyStatically(flags, receiver, encodeClassName(method.owner), encodeMethodSym(method), arguments)( toIRType(patchedResultType(method))) } @@ -2321,7 +2330,7 @@ class JSCodeGen()(implicit ctx: Context) { private def genApplyStatic(method: Symbol, arguments: List[js.Tree])( implicit pos: Position): js.Tree = { js.ApplyStatic(js.ApplyFlags.empty.withPrivate(method.isPrivate), - encodeClassRef(method.owner), encodeMethodSym(method), arguments)( + encodeClassName(method.owner), encodeMethodSym(method), arguments)( toIRType(patchedResultType(method))) } @@ -2340,11 +2349,13 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen a boxing operation (tpe is the primitive type) */ private def makePrimitiveBox(expr: js.Tree, tpe: Type)( implicit pos: Position): js.Tree = { - toTypeRef(tpe) match { - case jstpe.ClassRef(ir.Definitions.VoidClass) => + toIRType(tpe) match { + case jstpe.NoType => // for JS interop cases js.Block(expr, js.Undefined()) - case jstpe.ClassRef(cls) if ir.Definitions.PrimitiveClasses.contains(cls) => - expr // box is identity for all non-Unit types + case jstpe.BooleanType | jstpe.CharType | jstpe.ByteType | + jstpe.ShortType | jstpe.IntType | jstpe.LongType | jstpe.FloatType | + jstpe.DoubleType => + expr // box is identity for all those primitive types case typeRef => throw new FatalError( s"makePrimitiveBox requires a primitive type, found $typeRef for $tpe at $pos") @@ -2354,19 +2365,9 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen an unboxing operation (tpe is the primitive type) */ private def makePrimitiveUnbox(expr: js.Tree, tpe: Type)( implicit pos: Position): js.Tree = { - toTypeRef(tpe) match { - case jstpe.ClassRef(cls) if ir.Definitions.PrimitiveClasses.contains(cls) => - assert(cls.length == 1) - cls.charAt(0) match { - case 'V' => - expr - case primitiveCharCode => - js.Unbox(expr, primitiveCharCode) - } - - case _ => - throw new FatalError( - s"makePrimitiveUnbox requires a primitive type, found $tpe at $pos") + toIRType(tpe) match { + case jstpe.NoType => expr // for JS interop cases + case irTpe => js.AsInstanceOf(expr, irTpe) } } @@ -2493,17 +2494,19 @@ class JSCodeGen()(implicit ctx: Context) { case IN => // js.special.in(arg1, arg2) val (arg1, arg2) = genArgs2 - js.Unbox(js.JSBinaryOp(js.JSBinaryOp.in, arg1, arg2), 'Z') + js.AsInstanceOf(js.JSBinaryOp(js.JSBinaryOp.in, arg1, arg2), + jstpe.BooleanType) case INSTANCEOF => // js.special.instanceof(arg1, arg2) val (arg1, arg2) = genArgs2 - js.Unbox(js.JSBinaryOp(js.JSBinaryOp.instanceof, arg1, arg2), 'Z') + js.AsInstanceOf(js.JSBinaryOp(js.JSBinaryOp.instanceof, arg1, arg2), + jstpe.BooleanType) case DELETE => // js.special.delete(arg1, arg2) val (arg1, arg2) = genArgs2 - js.JSDelete(js.JSBracketSelect(arg1, arg2)) + js.JSDelete(arg1, arg2) case FORIN => /* js.special.forin(arg1, arg2) @@ -2520,16 +2523,16 @@ class JSCodeGen()(implicit ctx: Context) { * once, and after `arg1`. */ val (arg1, arg2) = genArgs2 - val objVarDef = js.VarDef(freshLocalIdent("obj"), jstpe.AnyType, - mutable = false, arg1) - val fVarDef = js.VarDef(freshLocalIdent("f"), jstpe.AnyType, - mutable = false, arg2) + val objVarDef = js.VarDef(freshLocalIdent("obj"), NoOriginalName, + jstpe.AnyType, mutable = false, arg1) + val fVarDef = js.VarDef(freshLocalIdent("f"), NoOriginalName, + jstpe.AnyType, mutable = false, arg2) val keyVarIdent = freshLocalIdent("key") val keyVarRef = js.VarRef(keyVarIdent)(jstpe.AnyType) js.Block( objVarDef, fVarDef, - js.ForIn(objVarDef.ref, keyVarIdent, { + js.ForIn(objVarDef.ref, keyVarIdent, NoOriginalName, { js.JSFunctionApply(fVarDef.ref, List(keyVarRef)) })) } @@ -2733,7 +2736,7 @@ class JSCodeGen()(implicit ctx: Context) { if (sym == defn.BoxedUnit_UNIT) { js.Undefined() } else if (sym == defn.BoxedUnit_TYPE) { - js.ClassOf(jstpe.ClassRef("V")) + js.ClassOf(jstpe.VoidRef) } else { val inst = genLoadModule(sym.owner) val method = encodeStaticMemberSym(sym) @@ -2771,7 +2774,7 @@ class JSCodeGen()(implicit ctx: Context) { if (sym.hasAnnotation(jsdefn.JSGlobalScopeAnnot)) { MaybeGlobalScope.GlobalScope(pos) } else { - val cls = encodeClassRef(sym) + val cls = encodeClassName(sym) val tree = if (isJSType(sym)) js.LoadJSModule(cls) else js.LoadModule(cls) @@ -2784,7 +2787,7 @@ class JSCodeGen()(implicit ctx: Context) { implicit pos: Position): js.Tree = { assert(!isStaticModule(sym) && !sym.is(Trait), s"genPrimitiveJSClass called with non-class $sym") - js.LoadJSConstructor(encodeClassRef(sym)) + js.LoadJSConstructor(encodeClassName(sym)) } private final val GenericGlobalObjectInformationMsg = { @@ -2822,38 +2825,38 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen a JS bracket select or a `JSGlobalRef`. * * If the receiver is a normal value, i.e., not the global scope, then - * emit a `JSBracketSelect`. + * emit a `JSSelect`. * * Otherwise, if the `item` is a constant string that is a valid * JavaScript identifier, emit a `JSGlobalRef`. * * Otherwise, report a compile error. */ - private def genJSBracketSelectOrGlobalRef(qual: MaybeGlobalScope, item: js.Tree)( + private def genJSSelectOrGlobalRef(qual: MaybeGlobalScope, item: js.Tree)( implicit pos: SourcePosition): js.Tree = { qual match { case MaybeGlobalScope.NotGlobalScope(qualTree) => - js.JSBracketSelect(qualTree, item) + js.JSSelect(qualTree, item) case MaybeGlobalScope.GlobalScope(_) => item match { case js.StringLiteral(value) => - if (value == "arguments") { + if (js.JSGlobalRef.isValidJSGlobalRefName(value)) { + js.JSGlobalRef(value) + } else if (js.JSGlobalRef.ReservedJSIdentifierNames.contains(value)) { ctx.error( - "Selecting a field of the global scope whose name is " + - "`arguments` is not allowed." + + "Invalid selection in the global scope of the reserved " + + s"identifier name `$value`." + GenericGlobalObjectInformationMsg, pos) - js.JSGlobalRef(js.Ident("erroneous")) - } else if (js.isValidIdentifier(value)) { - js.JSGlobalRef(js.Ident(value)) + js.JSGlobalRef("erroneous") } else { ctx.error( "Selecting a field of the global scope whose name is " + "not a valid JavaScript identifier is not allowed." + GenericGlobalObjectInformationMsg, pos) - js.JSGlobalRef(js.Ident("erroneous")) + js.JSGlobalRef("erroneous") } case _ => @@ -2862,7 +2865,7 @@ class JSCodeGen()(implicit ctx: Context) { "name is not allowed." + GenericGlobalObjectInformationMsg, pos) - js.JSGlobalRef(js.Ident("erroneous")) + js.JSGlobalRef("erroneous") } } } @@ -2870,32 +2873,32 @@ class JSCodeGen()(implicit ctx: Context) { /** Gen a JS bracket method apply or an apply of a `GlobalRef`. * * If the receiver is a normal value, i.e., not the global scope, then - * emit a `JSBracketMethodApply`. + * emit a `JSMethodApply`. * * Otherwise, if the `method` is a constant string that is a valid * JavaScript identifier, emit a `JSFunctionApply(JSGlobalRef(...), ...)`. * * Otherwise, report a compile error. */ - private def genJSBracketMethodApplyOrGlobalRefApply( + private def genJSMethodApplyOrGlobalRefApply( receiver: MaybeGlobalScope, method: js.Tree, args: List[js.TreeOrJSSpread])( implicit pos: SourcePosition): js.Tree = { receiver match { case MaybeGlobalScope.NotGlobalScope(receiverTree) => - js.JSBracketMethodApply(receiverTree, method, args) + js.JSMethodApply(receiverTree, method, args) case MaybeGlobalScope.GlobalScope(_) => method match { case js.StringLiteral(value) => - if (value == "arguments") { + if (js.JSGlobalRef.isValidJSGlobalRefName(value)) { + js.JSFunctionApply(js.JSGlobalRef(value), args) + } else if (js.JSGlobalRef.ReservedJSIdentifierNames.contains(value)) { ctx.error( - "Calling a method of the global scope whose name is " + - "`arguments` is not allowed." + + "Invalid call in the global scope of the reserved " + + s"identifier name `$value`." + GenericGlobalObjectInformationMsg, pos) js.Undefined() - } else if (js.isValidIdentifier(value)) { - js.JSFunctionApply(js.JSGlobalRef(js.Ident(value)), args) } else { ctx.error( "Calling a method of the global scope whose name is not " + @@ -2968,6 +2971,15 @@ class JSCodeGen()(implicit ctx: Context) { object JSCodeGen { + private val JSObjectClassName = ClassName("scala.scalajs.js.Object") + + private val newSimpleMethodName = SimpleMethodName("new") + + private val ObjectArgConstructorName = + MethodName.constructor(List(jstpe.ClassRef(ir.Names.ObjectClass))) + + private val thisOriginalName = OriginalName("this") + sealed abstract class MaybeGlobalScope object MaybeGlobalScope { diff --git a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala index 4ddd94725ce9..8e65af9adb68 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala @@ -20,7 +20,11 @@ import Names._ import StdNames._ import org.scalajs.ir -import ir.{Trees => js, Types => jstpe} +import org.scalajs.ir.{Trees => js, Types => jstpe} +import org.scalajs.ir.Names.{LocalName, LabelName, FieldName, SimpleMethodName, MethodName, ClassName} +import org.scalajs.ir.OriginalName +import org.scalajs.ir.OriginalName.NoOriginalName +import org.scalajs.ir.UTF8String import ScopedVar.withScopedVars import JSDefinitions._ @@ -37,60 +41,90 @@ import JSInterop._ */ 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): Name = - self.name.unexpandedName - } - - implicit class MyNameOps(val self: Name) extends AnyVal { - def decoded: String = self.decode.toString - } + private val ScalaNothingClassName = ClassName("scala.Nothing") + private val ScalaNullClassName = ClassName("scala.Null") // Fresh local name generator ---------------------------------------------- class LocalNameGenerator { import LocalNameGenerator._ - private val usedLocalNames = mutable.Set.empty[String] - private val localSymbolNames = mutable.Map.empty[Symbol, String] - private var returnLabelName: Option[String] = None + private val usedLocalNames = mutable.Set.empty[LocalName] + private val localSymbolNames = mutable.Map.empty[Symbol, LocalName] + private val usedLabelNames = mutable.Set.empty[LabelName] + private val labelSymbolNames = mutable.Map.empty[Symbol, LabelName] + private var returnLabelName: Option[LabelName] = None - def localSymbolName(sym: Symbol)(implicit ctx: Context): String = - localSymbolNames.getOrElseUpdate(sym, freshName(sym.name.toString)) + private def freshNameGeneric[N <: ir.Names.Name](base: N, usedNamesSet: mutable.Set[N])( + withSuffix: (N, String) => N): N = { - 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)) - - def freshLocalIdent(base: TermName)(implicit pos: ir.Position): js.Ident = - js.Ident(freshName(base.toString), Some(base.unexpandedName.toString)) - - private def freshName(base: String = "x"): String = { var suffix = 1 - var longName = base - while (usedLocalNames(longName) || isReserved(longName)) { + var result = base + while (usedNamesSet(result)) { suffix += 1 - longName = base+"$"+suffix + result = withSuffix(base, "$" + suffix) } - usedLocalNames += longName - mangleJSName(longName) + usedNamesSet += result + result } - def getEnclosingReturnLabel()(implicit pos: ir.Position): js.Ident = { - /*val box = returnLabelName.get - if (box == null) - throw new IllegalStateException(s"No enclosing returnable scope at $pos")*/ + def freshName(base: LocalName): LocalName = + freshNameGeneric(base, usedLocalNames)(_.withSuffix(_)) + + def freshName(base: String): LocalName = + freshName(LocalName(base)) + + def freshLocalIdent()(implicit pos: ir.Position): js.LocalIdent = + js.LocalIdent(freshName(xLocalName)) + + def freshLocalIdent(base: LocalName)(implicit pos: ir.Position): js.LocalIdent = + js.LocalIdent(freshName(base)) + + def freshLocalIdent(base: String)(implicit pos: ir.Position): js.LocalIdent = + freshLocalIdent(LocalName(base)) + + def freshLocalIdent(base: TermName)(implicit pos: ir.Position): js.LocalIdent = + freshLocalIdent(base.mangledString) + + def localSymbolName(sym: Symbol)(implicit ctx: Context): LocalName = { + localSymbolNames.getOrElseUpdate(sym, { + /* The emitter does not like local variables that start with a '$', + * because it needs to encode them not to clash with emitter-generated + * names. There are two common cases, caused by scalac-generated names: + * - the `$this` parameter of tailrec methods and "extension" methods of + * AnyVals, which scalac knows as `nme.SELF`, and + * - the `$outer` parameter of inner class constructors, which scalac + * knows as `nme.OUTER`. + * We choose different base names for those two cases instead, so that + * the avoidance mechanism of the emitter doesn't happen as a common + * case. It can still happen for user-defined variables, but in that case + * the emitter will deal with it. + */ + val base = sym.name match { + case nme.SELF => "this$" // instead of $this + case nme.OUTER => "outer" // instead of $outer + case name => name.mangledString + } + freshName(base) + }) + } + + def freshLabelName(base: LabelName): LabelName = + freshNameGeneric(base, usedLabelNames)(_.withSuffix(_)) + + def freshLabelName(base: String): LabelName = + freshLabelName(LabelName(base)) + + def freshLabelIdent(base: String)(implicit pos: ir.Position): js.LabelIdent = + js.LabelIdent(freshLabelName(base)) + + def labelSymbolName(sym: Symbol)(implicit ctx: Context): LabelName = + labelSymbolNames.getOrElseUpdate(sym, freshLabelName(sym.name.mangledString)) + + def getEnclosingReturnLabel()(implicit pos: ir.Position): js.LabelIdent = { if (returnLabelName.isEmpty) - returnLabelName = Some(freshName("_return")) - js.Ident(returnLabelName.get) + returnLabelName = Some(freshLabelName("_return")) + js.LabelIdent(returnLabelName.get) } /* If this `LocalNameGenerator` has a `returnLabelName` (often added in the @@ -101,98 +135,88 @@ object JSEncoding { case None => body case Some(labelName) => - js.Labeled(js.Ident(labelName), tpe, body) + js.Labeled(js.LabelIdent(labelName), tpe, body) } } } private object LocalNameGenerator { - private val isReserved = - Set("arguments", "eval", ScalaJSEnvironmentName) + private val xLocalName = LocalName("x") } // Encoding methods ---------------------------------------------------------- def encodeLabelSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.Ident = { + implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.LabelIdent = { require(sym.is(Flags.Label), "encodeLabelSym called with non-label symbol: " + sym) - js.Ident(localNames.localSymbolName(sym), Some(sym.unexpandedName.decoded)) + js.LabelIdent(localNames.labelSymbolName(sym)) } def encodeFieldSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { + implicit ctx: Context, pos: ir.Position): js.FieldIdent = { 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 name0 = sym.name.mangledString val name = if (name0.charAt(name0.length()-1) != ' ') name0 else name0.substring(0, name0.length()-1) - - @tailrec - def superClassCount(sym: Symbol, acc: Int): Int = - if (sym == defn.ObjectClass) acc - else superClassCount(sym.asClass.superClass, acc + 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) || jsdefn.allRefClasses.contains(sym.owner)) - superClassCount(sym.owner, 0).toString - else - "f" - - val encodedName = name + "$" + idSuffix - js.Ident(mangleJSName(encodedName), Some(sym.unexpandedName.decoded)) + js.FieldIdent(FieldName(name)) } 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)) - } + implicit ctx: Context, pos: ir.Position): js.MethodIdent = { + require(sym.is(Flags.Method), "encodeMethodSym called with non-method symbol: " + sym) - def encodeMethodName(sym: Symbol, reflProxy: Boolean = false)( - implicit ctx: Context): String = { - val (encodedName, paramsString) = encodeMethodNameInternal(sym, reflProxy) - encodedName + paramsString - } + val tpe = sym.info - private def encodeMethodNameInternal(sym: Symbol, - reflProxy: Boolean = false)( - implicit ctx: Context): (String, String) = { - require(sym.is(Flags.Method), "encodeMethodSym called with non-method symbol: " + sym) + val paramTypeRefs0 = tpe.firstParamTypes.map(paramOrResultTypeRef(_)) - def name = encodeMemberNameInternal(sym) + val hasExplicitThisParameter = isScalaJSDefinedJSClass(sym.owner) + val paramTypeRefs = + if (!hasExplicitThisParameter) paramTypeRefs0 + else encodeClassRef(sym.owner) :: paramTypeRefs0 - val encodedName = - if (sym.isClassConstructor) "init_" - else mangleJSName(name) + val name = sym.name + val simpleName = SimpleMethodName(name.mangledString) - val paramsString = makeParamsString(sym, reflProxy) + val methodName = { + if (sym.isClassConstructor) + MethodName.constructor(paramTypeRefs) + else if (reflProxy) + MethodName.reflectiveProxy(simpleName, paramTypeRefs) + else + MethodName(simpleName, paramTypeRefs, paramOrResultTypeRef(patchedResultType(sym))) + } - (encodedName, paramsString) + js.MethodIdent(methodName) } def encodeStaticMemberSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { + implicit ctx: Context, pos: ir.Position): js.MethodIdent = { 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)) + + val name = sym.name + val resultTypeRef = paramOrResultTypeRef(sym.info) + val methodName = MethodName(name.mangledString, Nil, resultTypeRef) + js.MethodIdent(methodName) + } + + /** Computes the type ref for a type, to be used in a method signature. */ + private def paramOrResultTypeRef(tpe: Type)(implicit ctx: Context): jstpe.TypeRef = { + toTypeRef(tpe) match { + case jstpe.ClassRef(ScalaNullClassName) => jstpe.NullRef + case jstpe.ClassRef(ScalaNothingClassName) => jstpe.NothingRef + case otherTypeRef => otherTypeRef + } } def encodeLocalSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.Ident = { + implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.LocalIdent = { 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)) + js.LocalIdent(localNames.localSymbolName(sym)) } def encodeClassType(sym: Symbol)(implicit ctx: Context): jstpe.Type = { @@ -201,100 +225,44 @@ object JSEncoding { else { assert(sym != defn.ArrayClass, "encodeClassType() cannot be called with ArrayClass") - jstpe.ClassType(encodeClassFullName(sym)) + jstpe.ClassType(encodeClassName(sym)) } } def encodeClassRef(sym: Symbol)(implicit ctx: Context): jstpe.ClassRef = - jstpe.ClassRef(encodeClassFullName(sym)) + jstpe.ClassRef(encodeClassName(sym)) - def encodeClassFullNameIdent(sym: Symbol)( - implicit ctx: Context, pos: ir.Position): js.Ident = { - js.Ident(encodeClassFullName(sym), Some(sym.fullName.toString)) - } + def encodeClassNameIdent(sym: Symbol)( + implicit ctx: Context, pos: ir.Position): js.ClassIdent = + js.ClassIdent(encodeClassName(sym)) - def encodeClassFullName(sym: Symbol)(implicit ctx: Context): String = { + def encodeClassName(sym: Symbol)(implicit ctx: Context): ClassName = { if (sym == defn.BoxedUnitClass) { /* Rewire scala.runtime.BoxedUnit to java.lang.Void, as the IR expects. * BoxedUnit$ is a JVM artifact. */ - ir.Definitions.BoxedUnitClass + ir.Names.BoxedUnitClass } else { - ir.Definitions.encodeClassName(fullyMangledString(sym.fullName)) + ClassName(sym.fullName.mangledString) } } - private def encodeMemberNameInternal(sym: Symbol)( - implicit ctx: Context): String = { - fullyMangledString(sym.name) - } - - /** Convert Dotty mangled names into valid IR identifier names. */ - private def fullyMangledString(name: Name): String = { - val base = name.mangledString - val len = base.length - - // slow path - def encodeFurther(): String = { - val result = new java.lang.StringBuilder() - var i = 0 - while (i != len) { - val c = base.charAt(i) - if (c == '_') - result.append("$und") - else - result.append(c) - i += 1 - } - result.toString() - } - - var i = 0 - while (i != len) { - val c = base.charAt(i) - if (c == '_') - return encodeFurther() - i += 1 - } - base - } - def toIRType(tp: Type)(implicit ctx: Context): jstpe.Type = { val typeRefInternal = toTypeRefInternal(tp) typeRefInternal._1 match { + case jstpe.PrimRef(irTpe) => + irTpe + case typeRef: jstpe.ClassRef => val sym = typeRefInternal._2 - if (sym.asClass.isPrimitiveValueClass) { - if (sym == defn.BooleanClass) - jstpe.BooleanType - else if (sym == defn.CharClass) - jstpe.CharType - else if (sym == defn.ByteClass) - jstpe.ByteType - else if (sym == defn.ShortClass) - jstpe.ShortType - else if (sym == defn.IntClass) - jstpe.IntType - else if (sym == defn.LongClass) - jstpe.LongType - else if (sym == defn.FloatClass) - jstpe.FloatType - else if (sym == defn.DoubleClass) - jstpe.DoubleType - else if (sym == defn.UnitClass) - jstpe.NoType - else - throw new AssertionError(s"unknown primitive value class $sym") - } else { - if (sym == defn.ObjectClass || isJSType(sym)) - jstpe.AnyType - else if (sym == defn.NothingClass) - jstpe.NothingType - else if (sym == defn.NullClass) - jstpe.NullType - else - jstpe.ClassType(typeRef.className) - } + if (sym == defn.ObjectClass || isJSType(sym)) + jstpe.AnyType + else if (sym == defn.NothingClass) + jstpe.NothingType + else if (sym == defn.NullClass) + jstpe.NullType + else + jstpe.ClassType(typeRef.className) case typeRef: jstpe.ArrayTypeRef => jstpe.ArrayType(typeRef) @@ -305,28 +273,24 @@ object JSEncoding { toTypeRefInternal(tp)._1 private def toTypeRefInternal(tp: Type)(implicit ctx: Context): (jstpe.TypeRef, Symbol) = { - /** - * Primitive types are represented as TypeRefs to the class symbol of, for example, scala.Int. - * The `primitiveTypeMap` maps those class symbols to the corresponding PrimitiveBType. - */ def primitiveOrClassToTypeRef(sym: Symbol): (jstpe.TypeRef, Symbol) = { assert(sym.isClass, sym) //assert(sym != defn.ArrayClass || isCompilingArray, sym) - val className = if (sym.isPrimitiveValueClass) { - if (sym == defn.UnitClass) ir.Definitions.VoidClass - else if (sym == defn.BooleanClass) ir.Definitions.BooleanClass - else if (sym == defn.CharClass) ir.Definitions.CharClass - else if (sym == defn.ByteClass) ir.Definitions.ByteClass - else if (sym == defn.ShortClass) ir.Definitions.ShortClass - else if (sym == defn.IntClass) ir.Definitions.IntClass - else if (sym == defn.LongClass) ir.Definitions.LongClass - else if (sym == defn.FloatClass) ir.Definitions.FloatClass - else if (sym == defn.DoubleClass) ir.Definitions.DoubleClass + val typeRef = if (sym.isPrimitiveValueClass) { + if (sym == defn.UnitClass) jstpe.VoidRef + else if (sym == defn.BooleanClass) jstpe.BooleanRef + else if (sym == defn.CharClass) jstpe.CharRef + else if (sym == defn.ByteClass) jstpe.ByteRef + else if (sym == defn.ShortClass) jstpe.ShortRef + else if (sym == defn.IntClass) jstpe.IntRef + else if (sym == defn.LongClass) jstpe.LongRef + else if (sym == defn.FloatClass) jstpe.FloatRef + else if (sym == defn.DoubleClass) jstpe.DoubleRef else throw new Exception(s"unknown primitive value class $sym") } else { - encodeClassFullName(sym) + encodeClassRef(sym) } - (jstpe.ClassRef(className), sym) + (typeRef, sym) } /** @@ -335,7 +299,7 @@ object JSEncoding { */ def nonClassTypeRefToTypeRef(sym: Symbol): (jstpe.TypeRef, Symbol) = { //assert(sym.isType && isCompilingArray, sym) - (jstpe.ClassRef(ir.Definitions.ObjectClass), defn.ObjectClass) + (jstpe.ClassRef(ir.Names.ObjectClass), defn.ObjectClass) } tp.widenDealias match { @@ -377,59 +341,26 @@ object JSEncoding { if (sym.isConstructor) defn.UnitType else sym.info.resultType - // Encoding of method signatures - - private def makeParamsString(sym: Symbol, reflProxy: Boolean)( - implicit ctx: Context): String = { - val tpe = sym.info - - val paramTypeNames0 = tpe.firstParamTypes.map(internalName(_)) - - val hasExplicitThisParameter = 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) + def originalNameOfLocal(sym: Symbol)( + implicit ctx: Context, localNames: LocalNameGenerator): OriginalName = { + val irName = localNames.localSymbolName(sym) + val originalName = UTF8String(sym.name.unexpandedName.toString) + if (UTF8String.equals(originalName, irName.encoded)) NoOriginalName + else OriginalName(originalName) } - 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 = { - val typeRef = toTypeRef(tpe) + def originalNameOfField(sym: Symbol)(implicit ctx: Context): OriginalName = + originalNameOf(sym.name) - val safeTypeRef: jstpe.TypeRef = typeRef match { - case jstpe.ClassRef("s_Null") => jstpe.ClassRef(ir.Definitions.NullClass) - case jstpe.ClassRef("s_Nothing") => jstpe.ClassRef(ir.Definitions.NothingClass) - case otherTypeRef => otherTypeRef - } + def originalNameOfMethod(sym: Symbol)(implicit ctx: Context): OriginalName = + originalNameOf(sym.name) - encodeTypeRef(safeTypeRef) - } + def originalNameOfClass(sym: Symbol)(implicit ctx: Context): OriginalName = + originalNameOf(sym.fullName) - /** Encodes a [[Types.TypeRef]], such as in an encoded method signature. - */ - private def encodeTypeRef(typeRef: jstpe.TypeRef): String = { - typeRef match { - case jstpe.ClassRef(className) => className - case jstpe.ArrayTypeRef(base, depth) => "A" * depth + base - } + private def originalNameOf(name: Name): OriginalName = { + val originalName = name.unexpandedName.toString + if (originalName == name.mangledString) NoOriginalName + else OriginalName(originalName) } - - /** 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/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala index 2647285bc5a6..947af8c47978 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala @@ -41,9 +41,15 @@ class CheckReentrant extends MiniPhase { private val unsharedAnnot = new CtxLazy( summon[Context].requiredClass("scala.annotation.internal.unshared")) + private val scalaJSIRPackageClass = new CtxLazy( + summon[Context].getPackageClassIfDefined("org.scalajs.ir")) + def isIgnored(sym: Symbol)(implicit ctx: Context): Boolean = sym.hasAnnotation(sharableAnnot()) || sym.hasAnnotation(unsharedAnnot()) || + sym.topLevelClass.owner == scalaJSIRPackageClass() || + // We would add @sharable annotations on ScalaJSVersions and + // VersionChecks but we do not have control over that code sym.owner == defn.EnumValuesClass // enum values are initialized eagerly before use // in the long run, we should make them vals diff --git a/project/Build.scala b/project/Build.scala index b626fcb32b58..1d1de7ab8df0 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -913,7 +913,7 @@ object Build { ("org.scala-js" %% "scalajs-junit-test-runtime" % scalaJSVersion).withDottyCompat(scalaVersion.value), sourceGenerators in Compile += Def.task { - import org.scalajs.linker.CheckedBehavior + import org.scalajs.linker.interface.CheckedBehavior val stage = scalaJSStage.value @@ -958,7 +958,7 @@ object Build { ( (dir / "shared/src/test/scala/org/scalajs/testsuite/compiler" ** (("*.scala":FileFilter) -- "RegressionTest.scala" -- "ReflectiveCallTest.scala")).get ++ (dir / "shared/src/test/scala/org/scalajs/testsuite/javalib/lang" ** (("*.scala": FileFilter) -- "StringTest.scala")).get - ++ (dir / "shared/src/test/scala/org/scalajs/testsuite/javalib/io" ** (("*.scala": FileFilter) -- "ByteArrayInputStreamTest.scala" -- "ByteArrayOutputStreamTest.scala" -- "DataInputStreamTest.scala" -- "DataOutputStreamTest.scala" -- "InputStreamTest.scala" -- "OutputStreamWriterTest.scala" -- "PrintStreamTest.scala" -- "CommonStreamsTests.scala")).get + ++ (dir / "shared/src/test/scala/org/scalajs/testsuite/javalib/io" ** (("*.scala": FileFilter) -- "ByteArrayInputStreamTest.scala" -- "ByteArrayOutputStreamTest.scala" -- "DataInputStreamTest.scala" -- "DataOutputStreamTest.scala" -- "InputStreamTest.scala" -- "OutputStreamWriterTest.scala" -- "PrintStreamTest.scala" -- "ReadersTest.scala" -- "CommonStreamsTests.scala")).get ++ (dir / "shared/src/test/scala/org/scalajs/testsuite/javalib/math" ** "*.scala").get ++ (dir / "shared/src/test/scala/org/scalajs/testsuite/javalib/net" ** "*.scala").get ++ (dir / "shared/src/test/scala/org/scalajs/testsuite/javalib/security" ** "*.scala").get @@ -970,9 +970,9 @@ object Build { -- "CollectionTest.scala" -- "CollectionsOnCheckedCollectionTest.scala" -- "CollectionsOnCheckedListTest.scala" -- "CollectionsOnCheckedMapTest.scala" -- "CollectionsOnCheckedSetTest.scala" -- "CollectionsOnCollectionsTest.scala" -- "CollectionsOnListsTest.scala" -- "CollectionsOnMapsTest.scala" -- "CollectionsOnSetFromMapTest.scala" -- "CollectionsOnSetsTest.scala" -- "CollectionsOnSynchronizedCollectionTest.scala" -- "CollectionsOnSynchronizedListTest.scala" -- "CollectionsOnSynchronizedMapTest.scala" -- "CollectionsOnSynchronizedSetTest.scala" -- "CollectionsTest.scala" - -- "DequeTest.scala" -- "EventObjectTest.scala" -- "FormatterTest.scala" -- "HashMapTest.scala" -- "HashSetTest.scala" + -- "DequeTest.scala" -- "EventObjectTest.scala" -- "FormatterTest.scala" -- "HashMapTest.scala" -- "HashSetTest.scala" -- "IdentityHashMapTest.scala" -- "LinkedHashMapTest.scala" -- "LinkedHashSetTest.scala" -- "LinkedListTest.scala" -- "ListTest.scala" -- "MapTest.scala" - -- "NavigableSetTest.scala" -- "SetTest.scala" -- "SortedMapTest.scala" -- "SortedSetTest.scala" -- "TreeSetTest.scala")).get + -- "NavigableSetTest.scala" -- "PriorityQueueTest.scala" -- "SetTest.scala" -- "SortedMapTest.scala" -- "SortedSetTest.scala" -- "TreeSetTest.scala")).get ++ (dir / "shared/src/test/scala/org/scalajs/testsuite/utils" ** "*.scala").get ++ (dir / "shared/src/test/scala/org/scalajs/testsuite/junit" ** (("*.scala": FileFilter) -- "JUnitAnnotationsParamTest.scala")).get diff --git a/project/plugins.sbt b/project/plugins.sbt index b95473016f71..65a15fe6390d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,7 +2,7 @@ // // e.g. addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.1.0") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.0.0-M8") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.0.0-RC1") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.6")