Skip to content

Scala.js: Implement support for js.new.target. #15734

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions tests/neg-scalajs/js-new-target.scala
Original file line number Diff line number Diff line change
@@ -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.
}
}