Skip to content

Commit

Permalink
Merge pull request scala#15734 from dotty-staging/sjs-new-target
Browse files Browse the repository at this point in the history
Scala.js: Implement support for js.`new`.target.
  • Loading branch information
sjrd authored Jul 27, 2022
2 parents 8e17110 + 66d5a0a commit 7441a13
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 1 deletion.
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 @@ -3840,6 +3840,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 @@ -147,6 +147,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 @@ -106,6 +108,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 @@ -1254,6 +1254,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

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.
}
}

0 comments on commit 7441a13

Please sign in to comment.