diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 96608a565adc..635e0d353d5e 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -3791,6 +3791,19 @@ class JSCodeGen()(using genCtx: Context) { // BoxedUnit.UNIT, which is the boxed version of () js.Undefined() + case JS_NEW_TARGET => + // js.new.target + val valid = currentMethodSym.get.isClassConstructor && currentClassSym.isNonNativeJSClass + if (!valid) { + report.error( + "Illegal use of js.`new`.target.\n" + + "It can only be used in the constructor of a JS class, " + + "as a statement or in the rhs of a val or var.\n" + + "It cannot be used inside a lambda or by-name parameter, nor in any other location.", + tree.sourcePos) + } + js.JSNewTarget() + case JS_IMPORT => // js.import(arg) val arg = genArgs1 diff --git a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala index 99dad4deb203..20cce3ae68b3 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala @@ -145,6 +145,11 @@ final class JSDefinitions()(using Context) { @threadUnsafe lazy val JSConstructorTag_materializeR = JSConstructorTagModule.requiredMethodRef("materialize") def JSConstructorTag_materialize(using Context) = JSConstructorTag_materializeR.symbol + @threadUnsafe lazy val JSNewModuleRef = requiredModuleRef("scala.scalajs.js.new") + def JSNewModule(using Context) = JSNewModuleRef.symbol + @threadUnsafe lazy val JSNew_targetR = JSNewModule.requiredMethodRef("target") + def JSNew_target(using Context) = JSNew_targetR.symbol + @threadUnsafe lazy val JSImportModuleRef = requiredModuleRef("scala.scalajs.js.import") def JSImportModule(using Context) = JSImportModuleRef.symbol @threadUnsafe lazy val JSImport_applyR = JSImportModule.requiredMethodRef(nme.apply) diff --git a/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala b/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala index 000ec334a127..e43fbbbc8018 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala @@ -24,7 +24,9 @@ object JSPrimitives { inline val UNITVAL = JS_NATIVE + 1 // () value, which is undefined - inline val JS_IMPORT = UNITVAL + 1 // js.import.apply(specifier) + inline val JS_NEW_TARGET = UNITVAL + 1 // js.new.target + + inline val JS_IMPORT = JS_NEW_TARGET + 1 // js.import.apply(specifier) inline val JS_IMPORT_META = JS_IMPORT + 1 // js.import.meta inline val CONSTRUCTOROF = JS_IMPORT_META + 1 // runtime.constructorOf(clazz) @@ -105,6 +107,8 @@ class JSPrimitives(ictx: Context) extends DottyPrimitives(ictx) { addPrimitive(defn.BoxedUnit_UNIT, UNITVAL) + addPrimitive(jsdefn.JSNew_target, JS_NEW_TARGET) + addPrimitive(jsdefn.JSImport_apply, JS_IMPORT) addPrimitive(jsdefn.JSImport_meta, JS_IMPORT_META) diff --git a/project/Build.scala b/project/Build.scala index 453b34901807..76e59db67e6c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1218,6 +1218,7 @@ object Build { )).get ++ (dir / "js/src/test/require-2.12" ** "*.scala").get + ++ (dir / "js/src/test/require-new-target" ** "*.scala").get ++ (dir / "js/src/test/require-sam" ** "*.scala").get ++ (dir / "js/src/test/scala-new-collections" ** "*.scala").get ++ (dir / "js/src/test/require-no-modules" ** "*.scala").get diff --git a/tests/neg-scalajs/js-new-target.scala b/tests/neg-scalajs/js-new-target.scala new file mode 100644 index 000000000000..1d66d9c2c154 --- /dev/null +++ b/tests/neg-scalajs/js-new-target.scala @@ -0,0 +1,41 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation.* + +object IllegalInScalaClass { + class A { + js.`new`.target // error: Illegal use of js.`new`.target. + + def this(x: Int) = { + this() + js.`new`.target // error: Illegal use of js.`new`.target. + } + } + + class B { + def foo(x: Int): Unit = + js.`new`.target // error: Illegal use of js.`new`.target. + } + + class C extends js.Object { + class D { + js.`new`.target // error: Illegal use of js.`new`.target. + } + } +} + +object IllegalInDefOrLazyVal { + class A extends js.Object { + lazy val x = js.`new`.target // error: Illegal use of js.`new`.target. + def y: js.Dynamic = js.`new`.target // error: Illegal use of js.`new`.target. + def z(x: Int): Any = js.`new`.target // error: Illegal use of js.`new`.target. + } +} + +object IllegalInLambdaOrByName { + class A extends js.Object { + val x = () => js.`new`.target // error: Illegal use of js.`new`.target. + val y = Option(null).getOrElse(js.`new`.target) // error: Illegal use of js.`new`.target. + val z: js.Function1[Int, Any] = (x: Int) => js.`new`.target // error: Illegal use of js.`new`.target. + val w: js.ThisFunction0[Any, Any] = (x: Any) => js.`new`.target // error: Illegal use of js.`new`.target. + } +}