Skip to content

Commit 66d5a0a

Browse files
committed
Scala.js: Implement support for js.new.target.
Forward port of the upstream commit scala-js/scala-js@d011393
1 parent 634c580 commit 66d5a0a

File tree

5 files changed

+65
-1
lines changed

5 files changed

+65
-1
lines changed

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3791,6 +3791,19 @@ class JSCodeGen()(using genCtx: Context) {
37913791
// BoxedUnit.UNIT, which is the boxed version of ()
37923792
js.Undefined()
37933793

3794+
case JS_NEW_TARGET =>
3795+
// js.new.target
3796+
val valid = currentMethodSym.get.isClassConstructor && currentClassSym.isNonNativeJSClass
3797+
if (!valid) {
3798+
report.error(
3799+
"Illegal use of js.`new`.target.\n" +
3800+
"It can only be used in the constructor of a JS class, " +
3801+
"as a statement or in the rhs of a val or var.\n" +
3802+
"It cannot be used inside a lambda or by-name parameter, nor in any other location.",
3803+
tree.sourcePos)
3804+
}
3805+
js.JSNewTarget()
3806+
37943807
case JS_IMPORT =>
37953808
// js.import(arg)
37963809
val arg = genArgs1

compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ final class JSDefinitions()(using Context) {
145145
@threadUnsafe lazy val JSConstructorTag_materializeR = JSConstructorTagModule.requiredMethodRef("materialize")
146146
def JSConstructorTag_materialize(using Context) = JSConstructorTag_materializeR.symbol
147147

148+
@threadUnsafe lazy val JSNewModuleRef = requiredModuleRef("scala.scalajs.js.new")
149+
def JSNewModule(using Context) = JSNewModuleRef.symbol
150+
@threadUnsafe lazy val JSNew_targetR = JSNewModule.requiredMethodRef("target")
151+
def JSNew_target(using Context) = JSNew_targetR.symbol
152+
148153
@threadUnsafe lazy val JSImportModuleRef = requiredModuleRef("scala.scalajs.js.import")
149154
def JSImportModule(using Context) = JSImportModuleRef.symbol
150155
@threadUnsafe lazy val JSImport_applyR = JSImportModule.requiredMethodRef(nme.apply)

compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ object JSPrimitives {
2424

2525
inline val UNITVAL = JS_NATIVE + 1 // () value, which is undefined
2626

27-
inline val JS_IMPORT = UNITVAL + 1 // js.import.apply(specifier)
27+
inline val JS_NEW_TARGET = UNITVAL + 1 // js.new.target
28+
29+
inline val JS_IMPORT = JS_NEW_TARGET + 1 // js.import.apply(specifier)
2830
inline val JS_IMPORT_META = JS_IMPORT + 1 // js.import.meta
2931

3032
inline val CONSTRUCTOROF = JS_IMPORT_META + 1 // runtime.constructorOf(clazz)
@@ -105,6 +107,8 @@ class JSPrimitives(ictx: Context) extends DottyPrimitives(ictx) {
105107

106108
addPrimitive(defn.BoxedUnit_UNIT, UNITVAL)
107109

110+
addPrimitive(jsdefn.JSNew_target, JS_NEW_TARGET)
111+
108112
addPrimitive(jsdefn.JSImport_apply, JS_IMPORT)
109113
addPrimitive(jsdefn.JSImport_meta, JS_IMPORT_META)
110114

project/Build.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,7 @@ object Build {
12181218
)).get
12191219

12201220
++ (dir / "js/src/test/require-2.12" ** "*.scala").get
1221+
++ (dir / "js/src/test/require-new-target" ** "*.scala").get
12211222
++ (dir / "js/src/test/require-sam" ** "*.scala").get
12221223
++ (dir / "js/src/test/scala-new-collections" ** "*.scala").get
12231224
++ (dir / "js/src/test/require-no-modules" ** "*.scala").get

tests/neg-scalajs/js-new-target.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import scala.scalajs.js
2+
import scala.scalajs.js.annotation.*
3+
4+
object IllegalInScalaClass {
5+
class A {
6+
js.`new`.target // error: Illegal use of js.`new`.target.
7+
8+
def this(x: Int) = {
9+
this()
10+
js.`new`.target // error: Illegal use of js.`new`.target.
11+
}
12+
}
13+
14+
class B {
15+
def foo(x: Int): Unit =
16+
js.`new`.target // error: Illegal use of js.`new`.target.
17+
}
18+
19+
class C extends js.Object {
20+
class D {
21+
js.`new`.target // error: Illegal use of js.`new`.target.
22+
}
23+
}
24+
}
25+
26+
object IllegalInDefOrLazyVal {
27+
class A extends js.Object {
28+
lazy val x = js.`new`.target // error: Illegal use of js.`new`.target.
29+
def y: js.Dynamic = js.`new`.target // error: Illegal use of js.`new`.target.
30+
def z(x: Int): Any = js.`new`.target // error: Illegal use of js.`new`.target.
31+
}
32+
}
33+
34+
object IllegalInLambdaOrByName {
35+
class A extends js.Object {
36+
val x = () => js.`new`.target // error: Illegal use of js.`new`.target.
37+
val y = Option(null).getOrElse(js.`new`.target) // error: Illegal use of js.`new`.target.
38+
val z: js.Function1[Int, Any] = (x: Int) => js.`new`.target // error: Illegal use of js.`new`.target.
39+
val w: js.ThisFunction0[Any, Any] = (x: Any) => js.`new`.target // error: Illegal use of js.`new`.target.
40+
}
41+
}

0 commit comments

Comments
 (0)