diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 7ba39768871b..535da488a013 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -27,7 +27,7 @@ import dotty.tools.dotc.report import dotty.tools.sjs.ir import dotty.tools.sjs.ir.{ClassKind, Position, Names => jsNames, Trees => js, Types => jstpe} -import dotty.tools.sjs.ir.Names.{ClassName, MethodName, SimpleMethodName} +import dotty.tools.sjs.ir.Names.{ClassName, LocalName, MethodName, SimpleMethodName} import dotty.tools.sjs.ir.OriginalName import dotty.tools.sjs.ir.OriginalName.NoOriginalName import dotty.tools.sjs.ir.Trees.OptimizerHints @@ -77,7 +77,7 @@ class JSCodeGen()(using genCtx: Context) { val currentClassSym = new ScopedVar[Symbol] private val currentMethodSym = new ScopedVar[Symbol] private val localNames = new ScopedVar[LocalNameGenerator] - private val thisLocalVarIdent = new ScopedVar[Option[js.LocalIdent]] + private val thisLocalVarName = new ScopedVar[Option[LocalName]] private val isModuleInitialized = new ScopedVar[ScopedVar.VarBox[Boolean]] private val undefinedDefaultParams = new ScopedVar[mutable.Set[Symbol]] @@ -90,7 +90,7 @@ class JSCodeGen()(using genCtx: Context) { currentClassSym := null, currentMethodSym := null, localNames := null, - thisLocalVarIdent := null, + thisLocalVarName := null, isModuleInitialized := null, undefinedDefaultParams := null ) { @@ -101,7 +101,7 @@ class JSCodeGen()(using genCtx: Context) { private def withPerMethodBodyState[A](methodSym: Symbol)(body: => A): A = { withScopedVars( currentMethodSym := methodSym, - thisLocalVarIdent := None, + thisLocalVarName := None, isModuleInitialized := new ScopedVar.VarBox(false), undefinedDefaultParams := mutable.Set.empty, ) { @@ -127,12 +127,19 @@ class JSCodeGen()(using genCtx: Context) { /** Implicitly materializes the current local name generator. */ implicit def implicitLocalNames: LocalNameGenerator = localNames.get + def currentThisTypeNullable: jstpe.Type = + encodeClassType(currentClassSym) + def currentThisType: jstpe.Type = { - encodeClassType(currentClassSym) match { - case tpe @ jstpe.ClassType(cls) => - jstpe.BoxedClassToPrimType.getOrElse(cls, tpe) - case tpe => + currentThisTypeNullable match { + case tpe @ jstpe.ClassType(cls, _) => + jstpe.BoxedClassToPrimType.getOrElse(cls, tpe.toNonNullable) + case tpe @ jstpe.AnyType => + // We are in a JS class, in which even `this` is nullable tpe + case tpe => + throw new AssertionError( + s"Unexpected IR this type $tpe for class ${currentClassSym.get}") } } @@ -954,7 +961,7 @@ class JSCodeGen()(using genCtx: Context) { * In dotc this is usually not an issue, because it unboxes `null` to * the zero of the underlying type, unlike scalac which throws an NPE. */ - jstpe.ClassType(encodeClassName(tpe.tycon.typeSymbol)) + jstpe.ClassType(encodeClassName(tpe.tycon.typeSymbol), nullable = true) case _ => // Other types are not boxed, so we can initialized them to their true zero. @@ -971,7 +978,7 @@ class JSCodeGen()(using genCtx: Context) { js.MethodIdent(name), NoOriginalName, Nil, - jstpe.NoType, + jstpe.VoidType, Some(stats))( OptimizerHints.empty, Unversioned) } @@ -1150,7 +1157,7 @@ class JSCodeGen()(using genCtx: Context) { var preSuperStats = List.newBuilder[js.Tree] var jsSuperCall: Option[js.JSSuperConstructorCall] = None - val postSuperStats = List.newBuilder[js.Tree] + val postSuperStats = mutable.ListBuffer.empty[js.Tree] /* Move param accessor initializers after the super constructor call since * JS cannot access `this` before the super constructor call. @@ -1210,6 +1217,13 @@ class JSCodeGen()(using genCtx: Context) { assert(jsSuperCall.isDefined, s"Did not find Super call in primary JS construtor at ${dd.sourcePos}") + /* Insert a StoreModule if required. + * Do this now so we have the pos of the super ctor call. + * +=: prepends to the ListBuffer in O(1) -- yes, it's a cryptic name. + */ + if (isStaticModule(currentClassSym)) + js.StoreModule()(jsSuperCall.get.pos) +=: postSuperStats + new PrimaryJSCtor(sym, genParamsAndInfo(sym, dd.paramss), js.JSConstructorBody(preSuperStats.result(), jsSuperCall.get, postSuperStats.result())(dd.span)) } @@ -1339,7 +1353,7 @@ class JSCodeGen()(using genCtx: Context) { } } - js.If(cond, body, js.Skip())(jstpe.NoType) + js.If(cond, body, js.Skip())(jstpe.VoidType) } /* preStats / postStats use pre/post order traversal respectively to @@ -1591,7 +1605,7 @@ class JSCodeGen()(using genCtx: Context) { if (sym.isClassConstructor) { val namespace = js.MemberNamespace.Constructor js.MethodDef(js.MemberFlags.empty.withNamespace(namespace), - methodName, originalName, jsParams, jstpe.NoType, Some(genStat(rhs)))( + methodName, originalName, jsParams, jstpe.VoidType, Some(genStat(rhs)))( optimizerHints, Unversioned) } else { val namespace = if (isMethodStaticInIR(sym)) { @@ -1630,7 +1644,7 @@ class JSCodeGen()(using genCtx: Context) { val jsParams = paramsSyms.map(genParamDef(_)) def genBody() = localNames.makeLabeledIfRequiresEnclosingReturn(resultIRType) { - if (resultIRType == jstpe.NoType) genStat(tree) + if (resultIRType == jstpe.VoidType) genStat(tree) else genExpr(tree) } @@ -1641,7 +1655,7 @@ class JSCodeGen()(using genCtx: Context) { } else { val thisLocalIdent = freshLocalIdent("this") withScopedVars( - thisLocalVarIdent := Some(thisLocalIdent) + thisLocalVarName := Some(thisLocalIdent.name) ) { val staticNamespace = if (namespace.isPrivate) js.MemberNamespace.PrivateStatic @@ -1660,18 +1674,9 @@ class JSCodeGen()(using genCtx: Context) { // ParamDefs --------------------------------------------------------------- - def genParamDef(sym: Symbol): js.ParamDef = - genParamDef(sym, toIRType(sym.info)) - - private def genParamDef(sym: Symbol, ptpe: jstpe.Type): js.ParamDef = - genParamDef(sym, ptpe, sym.span) - - private def genParamDef(sym: Symbol, pos: Position): js.ParamDef = - genParamDef(sym, toIRType(sym.info), pos) - - private def genParamDef(sym: Symbol, ptpe: jstpe.Type, pos: Position): js.ParamDef = { - js.ParamDef(encodeLocalSym(sym)(implicitly, pos, implicitly), - originalNameOfLocal(sym), ptpe, mutable = false)(pos) + private def genParamDef(sym: Symbol): js.ParamDef = { + implicit val pos = sym.span + js.ParamDef(encodeLocalSym(sym), originalNameOfLocal(sym), toIRType(sym.info), mutable = false) } // Generate statements and expressions ------------------------------------- @@ -1691,7 +1696,7 @@ class JSCodeGen()(using genCtx: Context) { tree match { case js.Block(stats :+ expr) => js.Block(stats :+ exprToStat(expr)) - case _:js.Literal | _:js.This | _:js.VarRef => + case _:js.Literal | _:js.VarRef => js.Skip() case _ => tree @@ -1702,8 +1707,8 @@ class JSCodeGen()(using genCtx: Context) { */ 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.span}") + assert(result.tpe != jstpe.VoidType, + s"genExpr($tree) returned a tree with type VoidType at pos ${tree.span}") result } @@ -1798,7 +1803,7 @@ class JSCodeGen()(using genCtx: Context) { case If(cond, thenp, elsep) => val tpe = - if (isStat) jstpe.NoType + if (isStat) jstpe.VoidType else toIRType(tree.tpe) js.If(genExpr(cond), genStatOrExpr(thenp, isStat), @@ -1813,8 +1818,8 @@ class JSCodeGen()(using genCtx: Context) { if (fromSym.is(Label)) encodeLabelSym(fromSym) else localNames.get.getEnclosingReturnLabel() js.Return(toIRType(expr.tpe) match { - case jstpe.NoType => js.Block(genStat(expr), js.Undefined()) - case _ => genExpr(expr) + case jstpe.VoidType => genStat(expr) + case _ => genExpr(expr) }, label) case WhileDo(cond, body) => @@ -2002,7 +2007,7 @@ class JSCodeGen()(using genCtx: Context) { /** 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 + * But must be replaced by the `thisLocalVarName` local variable if there * is one. */ private def genThis()(implicit pos: Position): js.Tree = { @@ -2011,10 +2016,10 @@ class JSCodeGen()(using genCtx: Context) { "Trying to generate `this` inside the body") }*/ - thisLocalVarIdent.fold[js.Tree] { + thisLocalVarName.fold[js.Tree] { js.This()(currentThisType) - } { thisLocalIdent => - js.VarRef(thisLocalIdent)(currentThisType) + } { thisLocalName => + js.VarRef(thisLocalName)(currentThisTypeNullable) } } @@ -2049,7 +2054,7 @@ class JSCodeGen()(using genCtx: Context) { val blockAST = genStatOrExpr(block, isStat) val resultType = - if (isStat) jstpe.NoType + if (isStat) jstpe.VoidType else toIRType(tree.tpe) val handled = @@ -2066,7 +2071,7 @@ class JSCodeGen()(using genCtx: Context) { resultType: jstpe.Type, isStat: Boolean)(implicit pos: SourcePosition): js.Tree = { val exceptIdent = freshLocalIdent("e") - val origExceptVar = js.VarRef(exceptIdent)(jstpe.AnyType) + val origExceptVar = js.VarRef(exceptIdent.name)(jstpe.AnyType) val mightCatchJavaScriptException = catches.exists { caseDef => caseDef.pat match { @@ -2081,13 +2086,14 @@ class JSCodeGen()(using genCtx: Context) { val (exceptValDef, exceptVar) = if (mightCatchJavaScriptException) { val valDef = js.VarDef(freshLocalIdent("e"), NoOriginalName, - encodeClassType(defn.ThrowableClass), mutable = false, js.WrapAsThrowable(origExceptVar)) + encodeClassType(defn.ThrowableClass), mutable = false, + js.UnaryOp(js.UnaryOp.WrapAsThrowable, origExceptVar)) (valDef, valDef.ref) } else { (js.Skip(), origExceptVar) } - val elseHandler: js.Tree = js.Throw(origExceptVar) + val elseHandler: js.Tree = js.UnaryOp(js.UnaryOp.Throw, origExceptVar) val handler = catches.foldRight(elseHandler) { (caseDef, elsep) => implicit val pos: SourcePosition = caseDef.sourcePos @@ -2227,10 +2233,7 @@ class JSCodeGen()(using genCtx: Context) { val Apply(fun @ Select(sup @ Super(qual, _), _), args) = tree: @unchecked val sym = fun.symbol - if (sym == defn.Any_getClass) { - // The only primitive that is also callable as super call - js.GetClass(genThis()) - } else if (currentClassSym.isNonNativeJSClass) { + if (currentClassSym.isNonNativeJSClass) { genJSSuperCall(tree, isStat) } else { /* #3013 `qual` can be `this.$outer()` in some cases since Scala 2.12, @@ -2303,7 +2306,7 @@ class JSCodeGen()(using genCtx: Context) { val newMethodIdent = js.MethodIdent(newName) js.ApplyStatic(js.ApplyFlags.empty, className, newMethodIdent, args)( - jstpe.ClassType(className)) + jstpe.ClassType(className, nullable = true)) } /** Gen JS code for a new of a JS class (subclass of `js.Any`). */ @@ -2419,9 +2422,9 @@ class JSCodeGen()(using genCtx: Context) { * class capture. It seems Scala 2 has the same vulnerability. How do we * avoid this? */ - val selfName = freshLocalIdent("this")(pos) + val selfIdent = freshLocalIdent("this")(pos) def selfRef(implicit pos: ir.Position) = - js.VarRef(selfName)(jstpe.AnyType) + js.VarRef(selfIdent.name)(jstpe.AnyType) def memberLambda(params: List[js.ParamDef], restParam: Option[js.ParamDef], body: js.Tree)(implicit pos: ir.Position): js.Closure = js.Closure(arrow = false, captureParams = Nil, params, restParam, body, captureValues = Nil) @@ -2518,25 +2521,20 @@ class JSCodeGen()(using genCtx: Context) { js.JSNew(jsSuperClassRef, args) } - val selfVarDef = js.VarDef(selfName, thisOriginalName, jstpe.AnyType, mutable = false, newTree) + val selfVarDef = js.VarDef(selfIdent.copy(), // copy for the correct `pos` + thisOriginalName, jstpe.AnyType, mutable = false, newTree) selfVarDef :: memberDefinitions } // After the super call, substitute `selfRef` for `This()` - val afterSuper = new ir.Transformers.Transformer { - override def transform(tree: js.Tree, isStat: Boolean): js.Tree = tree match { + val afterSuper = new ir.Transformers.LocalScopeTransformer { + override def transform(tree: js.Tree): js.Tree = tree match { case js.This() => selfRef(tree.pos) - - // Don't traverse closure boundaries - case closure: js.Closure => - val newCaptureValues = closure.captureValues.map(transformExpr) - closure.copy(captureValues = newCaptureValues)(closure.pos) - case tree => - super.transform(tree, isStat) + super.transform(tree) } - }.transformStats(ctorBody.afterSuper) + }.transformTrees(ctorBody.afterSuper) beforeSuper ::: superCall ::: afterSuper } @@ -2988,7 +2986,8 @@ class JSCodeGen()(using genCtx: Context) { js.Assign(genSelect(), genArgs(1)) } else { // length of the array - js.ArrayLength(genArray) + js.UnaryOp(js.UnaryOp.Array_length, + js.UnaryOp(js.UnaryOp.CheckNotNull, genArray)) } } @@ -3004,15 +3003,13 @@ class JSCodeGen()(using genCtx: Context) { val genArg = genStatOrExpr(arg, isStat) genReceiver match { - case js.This() => - // common case for which there is no side-effect nor NPE + case genReceiver: js.VarRef if !genReceiver.tpe.isNullable => + // common case (notably for `this`) for which there is no side-effect nor NPE genArg case _ => implicit val pos = tree.span js.Block( - js.If(js.BinaryOp(js.BinaryOp.===, genReceiver, js.Null()), - js.Throw(js.New(NullPointerExceptionClass, js.MethodIdent(jsNames.NoArgConstructorName), Nil)), - js.Skip())(jstpe.NoType), + js.UnaryOp(js.UnaryOp.CheckNotNull, genReceiver), genArg) } } @@ -3034,9 +3031,10 @@ class JSCodeGen()(using genCtx: Context) { genException match { case js.New(cls, _, _) if cls != JavaScriptExceptionClassName => // Common case where ex is neither null nor a js.JavaScriptException - js.Throw(genException) + js.UnaryOp(js.UnaryOp.Throw, genException) case _ => - js.Throw(js.UnwrapFromThrowable(genException)) + js.UnaryOp(js.UnaryOp.Throw, js.UnaryOp(js.UnaryOp.UnwrapFromThrowable, + js.UnaryOp(js.UnaryOp.CheckNotNull, genException))) } } @@ -3332,15 +3330,15 @@ class JSCodeGen()(using genCtx: Context) { // Sanity check: we can handle Ints and Strings (including `null`s), but nothing else genSelector.tpe match { - case jstpe.IntType | jstpe.ClassType(jsNames.BoxedStringClass) | jstpe.NullType | jstpe.NothingType => + case jstpe.IntType | jstpe.ClassType(jsNames.BoxedStringClass, _) | jstpe.NullType | jstpe.NothingType => // ok case _ => abortMatch(s"Invalid selector type ${genSelector.tpe}") } val resultType = toIRType(tree.tpe) match { - case jstpe.NothingType => jstpe.NothingType // must take priority over NoType below - case _ if isStat => jstpe.NoType + case jstpe.NothingType => jstpe.NothingType // must take priority over VoidType below + case _ if isStat => jstpe.VoidType case resType => resType } @@ -3681,7 +3679,7 @@ class JSCodeGen()(using genCtx: Context) { } private def genThrowClassCastException()(implicit pos: Position): js.Tree = { - js.Throw(js.New(jsNames.ClassCastExceptionClass, + js.UnaryOp(js.UnaryOp.Throw, js.New(jsNames.ClassCastExceptionClass, js.MethodIdent(jsNames.NoArgConstructorName), Nil)) } @@ -3707,7 +3705,7 @@ class JSCodeGen()(using genCtx: Context) { // The Scala type system prevents x.isInstanceOf[Null] and ...[Nothing] assert(sym != defn.NullClass && sym != defn.NothingClass, s"Found a .isInstanceOf[$sym] at $pos") - js.IsInstanceOf(value, toIRType(to)) + js.IsInstanceOf(value, toIRType(to).toNonNullable) } } @@ -3764,7 +3762,7 @@ class JSCodeGen()(using genCtx: Context) { private def makePrimitiveBox(expr: js.Tree, tpe: Type)( implicit pos: Position): js.Tree = { toIRType(tpe) match { - case jstpe.NoType => // for JS interop cases + case jstpe.VoidType => // for JS interop cases js.Block(expr, js.Undefined()) case jstpe.BooleanType | jstpe.CharType | jstpe.ByteType | jstpe.ShortType | jstpe.IntType | jstpe.LongType | jstpe.FloatType | @@ -3780,8 +3778,8 @@ class JSCodeGen()(using genCtx: Context) { private def makePrimitiveUnbox(expr: js.Tree, tpe: Type)( implicit pos: Position): js.Tree = { toIRType(tpe) match { - case jstpe.NoType => expr // for JS interop cases - case irTpe => js.AsInstanceOf(expr, irTpe) + case jstpe.VoidType => expr // for JS interop cases + case irTpe => js.AsInstanceOf(expr, irTpe) } } @@ -3885,10 +3883,6 @@ class JSCodeGen()(using genCtx: Context) { genStatOrExpr(args(1), isStat) } - case LINKING_INFO => - // runtime.linkingInfo - js.JSLinkingInfo() - case DEBUGGER => // js.special.debugger() js.Debugger() @@ -3973,7 +3967,7 @@ class JSCodeGen()(using genCtx: Context) { case arg: js.JSGlobalRef => js.JSTypeOfGlobalRef(arg) case _ => js.JSUnaryOp(js.JSUnaryOp.typeof, arg) } - js.AsInstanceOf(typeofExpr, jstpe.ClassType(jsNames.BoxedStringClass)) + js.AsInstanceOf(typeofExpr, jstpe.ClassType(jsNames.BoxedStringClass, nullable = true)) case STRICT_EQ => // js.special.strictEquals(arg1, arg2) @@ -4017,7 +4011,7 @@ class JSCodeGen()(using genCtx: Context) { val fVarDef = js.VarDef(freshLocalIdent("f"), NoOriginalName, jstpe.AnyType, mutable = false, arg2) val keyVarIdent = freshLocalIdent("key") - val keyVarRef = js.VarRef(keyVarIdent)(jstpe.AnyType) + val keyVarRef = js.VarRef(keyVarIdent.name)(jstpe.AnyType) js.Block( objVarDef, fVarDef, @@ -4027,7 +4021,7 @@ class JSCodeGen()(using genCtx: Context) { case JS_THROW => // js.special.throw(arg) - js.Throw(genArgs1) + js.UnaryOp(js.UnaryOp.Throw, genArgs1) case JS_TRY_CATCH => /* js.special.tryCatch(arg1, arg2) @@ -4052,7 +4046,7 @@ class JSCodeGen()(using genCtx: Context) { val handlerVarDef = js.VarDef(freshLocalIdent("handler"), NoOriginalName, jstpe.AnyType, mutable = false, arg2) val exceptionVarIdent = freshLocalIdent("e") - val exceptionVarRef = js.VarRef(exceptionVarIdent)(jstpe.AnyType) + val exceptionVarRef = js.VarRef(exceptionVarIdent.name)(jstpe.AnyType) js.Block( bodyVarDef, handlerVarDef, @@ -4066,11 +4060,12 @@ class JSCodeGen()(using genCtx: Context) { case WRAP_AS_THROWABLE => // js.special.wrapAsThrowable(arg) - js.WrapAsThrowable(genArgs1) + js.UnaryOp(js.UnaryOp.WrapAsThrowable, genArgs1) case UNWRAP_FROM_THROWABLE => // js.special.unwrapFromThrowable(arg) - js.UnwrapFromThrowable(genArgs1) + js.UnaryOp(js.UnaryOp.UnwrapFromThrowable, + js.UnaryOp(js.UnaryOp.CheckNotNull, genArgs1)) case UNION_FROM | UNION_FROM_TYPE_CONSTRUCTOR => /* js.|.from and js.|.fromTypeConstructor @@ -4447,7 +4442,7 @@ class JSCodeGen()(using genCtx: Context) { } private def genVarRef(sym: Symbol)(implicit pos: Position): js.VarRef = - js.VarRef(encodeLocalSym(sym))(toIRType(sym.info)) + js.VarRef(encodeLocalSymName(sym))(toIRType(sym.info)) private def genAssignableField(sym: Symbol, qualifier: Tree)(implicit pos: SourcePosition): (js.AssignLhs, Boolean) = { def qual = genExpr(qualifier) @@ -4882,10 +4877,8 @@ object JSCodeGen { def traverse(traverser: ir.Traversers.Traverser): Unit = () - def transform(transformer: ir.Transformers.Transformer, isStat: Boolean)( - implicit pos: ir.Position): js.Tree = { + def transform(transformer: ir.Transformers.Transformer)(implicit pos: ir.Position): js.Tree = js.Transient(this) - } def printIR(out: ir.Printers.IRTreePrinter): Unit = out.print("") diff --git a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala index ab7f9a89f9c5..2f6591763fdd 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala @@ -179,8 +179,6 @@ final class JSDefinitions()(using Context) { def Runtime_createLocalJSClass(using Context) = Runtime_createLocalJSClassR.symbol @threadUnsafe lazy val Runtime_withContextualJSClassValueR = RuntimePackageClass.requiredMethodRef("withContextualJSClassValue") def Runtime_withContextualJSClassValue(using Context) = Runtime_withContextualJSClassValueR.symbol - @threadUnsafe lazy val Runtime_linkingInfoR = RuntimePackageClass.requiredMethodRef("linkingInfo") - def Runtime_linkingInfo(using Context) = Runtime_linkingInfoR.symbol @threadUnsafe lazy val Runtime_dynamicImportR = RuntimePackageClass.requiredMethodRef("dynamicImport") def Runtime_dynamicImport(using Context) = Runtime_dynamicImportR.symbol diff --git a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala index 9a7753680bc3..b3d28b73d81c 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSEncoding.scala @@ -135,16 +135,13 @@ object JSEncoding { 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)(using Context): LabelName = labelSymbolNames.getOrElseUpdate(sym, freshLabelName(sym.javaSimpleName)) - def getEnclosingReturnLabel()(implicit pos: ir.Position): js.LabelIdent = { + def getEnclosingReturnLabel(): LabelName = { if (returnLabelName.isEmpty) returnLabelName = Some(freshLabelName("_return")) - js.LabelIdent(returnLabelName.get) + returnLabelName.get } /* If this `LocalNameGenerator` has a `returnLabelName` (often added in the @@ -155,7 +152,7 @@ object JSEncoding { case None => body case Some(labelName) => - js.Labeled(js.LabelIdent(labelName), tpe, body) + js.Labeled(labelName, tpe, body) } } } @@ -166,10 +163,9 @@ object JSEncoding { // Encoding methods ---------------------------------------------------------- - def encodeLabelSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.LabelIdent = { + def encodeLabelSym(sym: Symbol)(implicit ctx: Context, localNames: LocalNameGenerator): LabelName = { require(sym.is(Flags.Label), "encodeLabelSym called with non-label symbol: " + sym) - js.LabelIdent(localNames.labelSymbolName(sym)) + localNames.labelSymbolName(sym) } def encodeFieldSym(sym: Symbol)(implicit ctx: Context, pos: ir.Position): js.FieldIdent = @@ -248,11 +244,13 @@ object JSEncoding { private def paramOrResultTypeRef(tpe: Type)(using Context): jstpe.TypeRef = toParamOrResultTypeRef(toTypeRef(tpe)) - def encodeLocalSym(sym: Symbol)( - implicit ctx: Context, pos: ir.Position, localNames: LocalNameGenerator): js.LocalIdent = { + def encodeLocalSym(sym: Symbol)(using Context, ir.Position, LocalNameGenerator): js.LocalIdent = + js.LocalIdent(encodeLocalSymName(sym)) + + def encodeLocalSymName(sym: Symbol)(using ctx: Context, localNames: LocalNameGenerator): LocalName = { require(!sym.owner.isClass && sym.isTerm && !sym.is(Flags.Method) && !sym.is(Flags.Module), "encodeLocalSym called with non-local symbol: " + sym) - js.LocalIdent(localNames.localSymbolName(sym)) + localNames.localSymbolName(sym) } def encodeClassType(sym: Symbol)(using Context): jstpe.Type = { @@ -261,7 +259,7 @@ object JSEncoding { else { assert(sym != defn.ArrayClass, "encodeClassType() cannot be called with ArrayClass") - jstpe.ClassType(encodeClassName(sym)) + jstpe.ClassType(encodeClassName(sym), nullable = true) } } @@ -324,10 +322,10 @@ object JSEncoding { else if (sym == defn.NullClass) jstpe.NullType else - jstpe.ClassType(typeRef.className) + jstpe.ClassType(typeRef.className, nullable = true) case typeRef: jstpe.ArrayTypeRef => - jstpe.ArrayType(typeRef) + jstpe.ArrayType(typeRef, nullable = true) } } diff --git a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala index e6c73357aa4c..42205f9b70c2 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala @@ -652,7 +652,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { val superClass = { val superClassSym = currentClassSym.asClass.superClass if (superClassSym.isNestedJSClass) - js.VarRef(js.LocalIdent(JSSuperClassParamName))(jstpe.AnyType) + js.VarRef(JSSuperClassParamName)(jstpe.AnyType) else js.LoadJSConstructor(encodeClassName(superClassSym)) } @@ -793,7 +793,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { } // #15419 If the getter returns void, we must "box" it by returning undefined - if (callGetter.tpe == jstpe.NoType) + if (callGetter.tpe == jstpe.VoidType) js.Block(callGetter, js.Undefined()) else callGetter @@ -837,7 +837,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { } private def genThrowTypeError(msg: String = "No matching overload")(implicit pos: Position): js.Tree = - js.Throw(js.JSNew(js.JSGlobalRef("TypeError"), js.StringLiteral(msg) :: Nil)) + js.UnaryOp(js.UnaryOp.Throw, js.JSNew(js.JSGlobalRef("TypeError"), js.StringLiteral(msg) :: Nil)) abstract class Exported( val sym: Symbol, @@ -935,9 +935,9 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { import dotty.tools.sjs.ir.Names (toIRType(tpe): @unchecked) match { - case jstpe.AnyType => NoTypeTest + case jstpe.AnyType | jstpe.AnyNotNullType => NoTypeTest - case jstpe.NoType => PrimitiveTypeTest(jstpe.UndefType, 0) + case jstpe.VoidType => PrimitiveTypeTest(jstpe.UndefType, 0) case jstpe.BooleanType => PrimitiveTypeTest(jstpe.BooleanType, 1) case jstpe.CharType => PrimitiveTypeTest(jstpe.CharType, 2) case jstpe.ByteType => PrimitiveTypeTest(jstpe.ByteType, 3) @@ -947,11 +947,11 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { case jstpe.FloatType => PrimitiveTypeTest(jstpe.FloatType, 7) case jstpe.DoubleType => PrimitiveTypeTest(jstpe.DoubleType, 8) - case jstpe.ClassType(Names.BoxedUnitClass) => PrimitiveTypeTest(jstpe.UndefType, 0) - case jstpe.ClassType(Names.BoxedStringClass) => PrimitiveTypeTest(jstpe.StringType, 9) - case jstpe.ClassType(_) => InstanceOfTypeTest(tpe) + case jstpe.ClassType(Names.BoxedUnitClass, _) => PrimitiveTypeTest(jstpe.UndefType, 0) + case jstpe.ClassType(Names.BoxedStringClass, _) => PrimitiveTypeTest(jstpe.StringType, 9) + case jstpe.ClassType(_, _) => InstanceOfTypeTest(tpe) - case jstpe.ArrayType(_) => InstanceOfTypeTest(tpe) + case jstpe.ArrayType(_, _) => InstanceOfTypeTest(tpe) } } } @@ -998,7 +998,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { def genArgRef(index: Int)(implicit pos: Position): js.Tree = { if (index < minArgc) - js.VarRef(js.LocalIdent(fixedParamNames(index)))(jstpe.AnyType) + js.VarRef(fixedParamNames(index))(jstpe.AnyType) else js.JSSelect(genRestArgRef(), js.IntLiteral(index - minArgc)) } @@ -1014,16 +1014,16 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { def genRestArgRef()(implicit pos: Position): js.Tree = { assert(needsRestParam, s"trying to generate a reference to non-existent rest param at $pos") - js.VarRef(js.LocalIdent(restParamName))(jstpe.AnyType) + js.VarRef(restParamName)(jstpe.AnyType) } def genAllArgsRefsForForwarder()(implicit pos: Position): List[js.TreeOrJSSpread] = { val fixedArgRefs = fixedParamNames.toList.map { paramName => - js.VarRef(js.LocalIdent(paramName))(jstpe.AnyType) + js.VarRef(paramName)(jstpe.AnyType) } if (needsRestParam) { - val restArgRef = js.VarRef(js.LocalIdent(restParamName))(jstpe.AnyType) + val restArgRef = js.VarRef(restParamName)(jstpe.AnyType) fixedArgRefs :+ js.JSSpread(restArgRef) } else { fixedArgRefs diff --git a/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala b/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala index a3a37795826a..60c671b21c5b 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala @@ -34,8 +34,7 @@ object JSPrimitives { inline val CREATE_INNER_JS_CLASS = CONSTRUCTOROF + 1 // runtime.createInnerJSClass inline val CREATE_LOCAL_JS_CLASS = CREATE_INNER_JS_CLASS + 1 // runtime.createLocalJSClass inline val WITH_CONTEXTUAL_JS_CLASS_VALUE = CREATE_LOCAL_JS_CLASS + 1 // runtime.withContextualJSClassValue - inline val LINKING_INFO = WITH_CONTEXTUAL_JS_CLASS_VALUE + 1 // runtime.linkingInfo - inline val DYNAMIC_IMPORT = LINKING_INFO + 1 // runtime.dynamicImport + inline val DYNAMIC_IMPORT = WITH_CONTEXTUAL_JS_CLASS_VALUE + 1 // runtime.dynamicImport inline val STRICT_EQ = DYNAMIC_IMPORT + 1 // js.special.strictEquals inline val IN = STRICT_EQ + 1 // js.special.in @@ -122,7 +121,6 @@ class JSPrimitives(ictx: Context) extends DottyPrimitives(ictx) { addPrimitive(jsdefn.Runtime_createInnerJSClass, CREATE_INNER_JS_CLASS) addPrimitive(jsdefn.Runtime_createLocalJSClass, CREATE_LOCAL_JS_CLASS) addPrimitive(jsdefn.Runtime_withContextualJSClassValue, WITH_CONTEXTUAL_JS_CLASS_VALUE) - addPrimitive(jsdefn.Runtime_linkingInfo, LINKING_INFO) addPrimitive(jsdefn.Runtime_dynamicImport, DYNAMIC_IMPORT) addPrimitive(jsdefn.Special_strictEquals, STRICT_EQ) diff --git a/project/Build.scala b/project/Build.scala index e9b5e1d12218..5df09a660f74 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1673,6 +1673,7 @@ object Build { "productionMode" -> sems.productionMode, "esVersion" -> linkerConfig.esFeatures.esVersion.edition, "useECMAScript2015Semantics" -> linkerConfig.esFeatures.useECMAScript2015Semantics, + "isWebAssembly" -> linkerConfig.experimentalUseWebAssembly, ) }.taskValue, diff --git a/project/plugins.sbt b/project/plugins.sbt index 21d8826b6b24..b3e4af1f72e7 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,7 +6,7 @@ libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.18.1") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.21") diff --git a/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSLink.scala b/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSLink.scala index 2560021aec99..60a21602860e 100644 --- a/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSLink.scala +++ b/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSLink.scala @@ -17,6 +17,10 @@ object ScalaJSLink: Semantics.Defaults .withAsInstanceOfs(CheckedBehavior.Compliant) .withArrayIndexOutOfBounds(CheckedBehavior.Compliant) + .withArrayStores(CheckedBehavior.Compliant) + .withNegativeArraySizes(CheckedBehavior.Compliant) + .withNullPointers(CheckedBehavior.Compliant) + .withStringIndexOutOfBounds(CheckedBehavior.Compliant) .withModuleInit(CheckedBehavior.Compliant) end compliantSemantics diff --git a/tests/run/exceptions-2.scala b/tests/run/exceptions-2.scala index ddb4c9c51ee1..73ca70eb5add 100644 --- a/tests/run/exceptions-2.scala +++ b/tests/run/exceptions-2.scala @@ -1,4 +1,4 @@ -// scalajs: --skip +// scalajs: --compliant-semantics /* * Try exception handling and finally blocks. diff --git a/tests/run/exceptions-nest.scala b/tests/run/exceptions-nest.scala index 2e58459fb9f7..cfaf09929a42 100644 --- a/tests/run/exceptions-nest.scala +++ b/tests/run/exceptions-nest.scala @@ -1,4 +1,4 @@ -// scalajs: --skip +// scalajs: --compliant-semantics object Test extends App { @@ -12,9 +12,12 @@ object Test extends App { try { println(test8) } catch { case _: Throwable => println("OK") } println(test9) println(test10) - println(test11) + portablePrintln(test11) println(test12) + def portablePrintln(x: Any): Unit = + println(if (x == ()) "()" else x) + def test1 = { var x = 1 try { diff --git a/tests/run/t8601d.scala b/tests/run/t8601d.scala index a6962847cb83..6e022de90ab8 100644 --- a/tests/run/t8601d.scala +++ b/tests/run/t8601d.scala @@ -1,3 +1,5 @@ +// scalajs: --compliant-semantics + object Test { def monitor(x: AnyRef): Unit = {x.synchronized(()); ()} def check(x: => Any) = try { x; sys.error("failed to throw NPE") } catch { case _: NullPointerException => }