Skip to content

Commit b8d607e

Browse files
committed
Emit LinkTimeProperty via LinkingInfo.linkTimeProperty APIs
TLDR: - Add `scala.scalajs.LinkingInfo.linkTimeProperty` APIs that emits `LinkTimeProperty` IRs. - Deprecate `scala.scalajs.runtime.linkingInfo` in favor of `scala.scalajs.LinkingInfo` and `scala.scalajs.js.special.fileLevelThis`. - Keeping `scala.scalajs.runtime.linkingInfo` because Scala3 compiler looks like depends on the symbol of `runtime.linkingInfo` https://github.com/scala/scala3/blob/67cd3ebe3f70bb9045cc966af5b96a623a7da973/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala#L182-L183 This commit introduces new APIs under `scala.scalajs.LinkingInfo`: `linkTimeProperty(Int|String|Boolean)`. These APIs allow us to define link-time properties in a more optimized and JS-independent way, replacing the previous `scala.scalajs.runtime.linkingInfo`. For instance, `linkTimePropertyInt("core/esVersion")` emits the IR `LinkTimeProperty("core/esVersion")(IntType)`, which will later be transformed into an `IntLiteral` by the optimizer or linker backend. `scala.scalajs.runtime.linkingInfo` is now deprecated in favor of `scala.scalajs.LinkingInfo`. For cases where `scala.scalajs.runtime.linkingInfo.fileLevelThis` was used, users should migrate to `scala.scalajs.js.special.fileLevelThis`.
1 parent d5c2f7f commit b8d607e

File tree

39 files changed

+231
-102
lines changed

39 files changed

+231
-102
lines changed

compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,15 @@ import scala.reflect.internal.Flags
2727

2828
import org.scalajs.ir
2929
import org.scalajs.ir.{Trees => js, Types => jstpe, ClassKind, Hashers, OriginalName}
30-
import org.scalajs.ir.Names.{LocalName, SimpleFieldName, FieldName, SimpleMethodName, MethodName, ClassName}
30+
import org.scalajs.ir.Names.{
31+
LocalName,
32+
SimpleFieldName,
33+
FieldName,
34+
SimpleMethodName,
35+
MethodName,
36+
ClassName,
37+
BoxedStringClass
38+
}
3139
import org.scalajs.ir.OriginalName.NoOriginalName
3240
import org.scalajs.ir.Trees.OptimizerHints
3341
import org.scalajs.ir.Version.Unversioned
@@ -5202,10 +5210,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
52025210
genStatOrExpr(args(1), isStat)
52035211
}
52045212

5205-
case LINKING_INFO =>
5206-
// runtime.linkingInfo
5207-
js.JSLinkingInfo()
5208-
52095213
case IDENTITY_HASH_CODE =>
52105214
// runtime.identityHashCode(arg)
52115215
val arg = genArgs1
@@ -5390,6 +5394,22 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
53905394
case UNWRAP_FROM_THROWABLE =>
53915395
// js.special.unwrapFromThrowable(arg)
53925396
js.UnwrapFromThrowable(genArgs1)
5397+
5398+
case LINKTIME_PROPERTY =>
5399+
// LinkingInfo.linkTimePropertyXXX("...")
5400+
val arg = genArgs1
5401+
val tpe: jstpe.Type = toIRType(tree.tpe) match {
5402+
case jstpe.ClassType(BoxedStringClass, _) => jstpe.StringType
5403+
case irType => irType
5404+
}
5405+
arg match {
5406+
case js.StringLiteral(name) =>
5407+
js.LinkTimeProperty(name)(tpe)
5408+
case _ =>
5409+
reporter.error(args.head.pos,
5410+
"The argument of linkTimePropertyXXX must be a String literal: \"...\"")
5411+
js.LinkTimeProperty("erroneous")(tpe)
5412+
}
53935413
}
53945414
}
53955415

@@ -5501,8 +5521,16 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
55015521
def genSelectGet(propName: js.Tree): js.Tree =
55025522
genSuperReference(propName)
55035523

5504-
def genSelectSet(propName: js.Tree, value: js.Tree): js.Tree =
5505-
js.Assign(genSuperReference(propName), value)
5524+
def genSelectSet(propName: js.Tree, value: js.Tree): js.Tree = {
5525+
val lhs = genSuperReference(propName)
5526+
lhs match {
5527+
case js.JSGlobalRef(js.JSGlobalRef.FileLevelThis) =>
5528+
reporter.error(pos,
5529+
"Illegal assignment to global this.")
5530+
case _ =>
5531+
}
5532+
js.Assign(lhs, value)
5533+
}
55065534

55075535
def genCall(methodName: js.Tree,
55085536
args: List[js.TreeOrJSSpread]): js.Tree = {

compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ trait JSDefinitions {
127127
lazy val Runtime_identityHashCode = getMemberMethod(RuntimePackageModule, newTermName("identityHashCode"))
128128
lazy val Runtime_dynamicImport = getMemberMethod(RuntimePackageModule, newTermName("dynamicImport"))
129129

130+
lazy val LinkingInfoModule = getRequiredModule("scala.scalajs.LinkingInfo")
131+
lazy val LinkingInfo_linkTimePropertyBoolean = getMemberMethod(LinkingInfoModule, newTermName("linkTimePropertyBoolean"))
132+
lazy val LinkingInfo_linkTimePropertyInt = getMemberMethod(LinkingInfoModule, newTermName("linkTimePropertyInt"))
133+
lazy val LinkingInfo_linkTimePropertyString = getMemberMethod(LinkingInfoModule, newTermName("linkTimePropertyString"))
134+
130135
lazy val DynamicImportThunkClass = getRequiredClass("scala.scalajs.runtime.DynamicImportThunk")
131136
lazy val DynamicImportThunkClass_apply = getMemberMethod(DynamicImportThunkClass, nme.apply)
132137

compiler/src/main/scala/org/scalajs/nscplugin/JSPrimitives.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ abstract class JSPrimitives {
5555
final val CREATE_INNER_JS_CLASS = CONSTRUCTOROF + 1 // runtime.createInnerJSClass
5656
final val CREATE_LOCAL_JS_CLASS = CREATE_INNER_JS_CLASS + 1 // runtime.createLocalJSClass
5757
final val WITH_CONTEXTUAL_JS_CLASS_VALUE = CREATE_LOCAL_JS_CLASS + 1 // runtime.withContextualJSClassValue
58-
final val LINKING_INFO = WITH_CONTEXTUAL_JS_CLASS_VALUE + 1 // runtime.linkingInfo
59-
final val IDENTITY_HASH_CODE = LINKING_INFO + 1 // runtime.identityHashCode
58+
final val IDENTITY_HASH_CODE = WITH_CONTEXTUAL_JS_CLASS_VALUE + 1 // runtime.identityHashCode
6059
final val DYNAMIC_IMPORT = IDENTITY_HASH_CODE + 1 // runtime.dynamicImport
6160

6261
final val STRICT_EQ = DYNAMIC_IMPORT + 1 // js.special.strictEquals
@@ -69,8 +68,9 @@ abstract class JSPrimitives {
6968
final val WRAP_AS_THROWABLE = JS_TRY_CATCH + 1 // js.special.wrapAsThrowable
7069
final val UNWRAP_FROM_THROWABLE = WRAP_AS_THROWABLE + 1 // js.special.unwrapFromThrowable
7170
final val DEBUGGER = UNWRAP_FROM_THROWABLE + 1 // js.special.debugger
71+
final val LINKTIME_PROPERTY = DEBUGGER + 1 // LinkingInfo.linkTimePropertyXXX
7272

73-
final val LastJSPrimitiveCode = DEBUGGER
73+
final val LastJSPrimitiveCode = LINKTIME_PROPERTY
7474

7575
/** Initialize the map of primitive methods (for GenJSCode) */
7676
def init(): Unit = initWithPrimitives(addPrimitive)
@@ -109,7 +109,6 @@ abstract class JSPrimitives {
109109
addPrimitive(Runtime_createLocalJSClass, CREATE_LOCAL_JS_CLASS)
110110
addPrimitive(Runtime_withContextualJSClassValue,
111111
WITH_CONTEXTUAL_JS_CLASS_VALUE)
112-
addPrimitive(Runtime_linkingInfo, LINKING_INFO)
113112
addPrimitive(Runtime_identityHashCode, IDENTITY_HASH_CODE)
114113
addPrimitive(Runtime_dynamicImport, DYNAMIC_IMPORT)
115114

@@ -123,6 +122,10 @@ abstract class JSPrimitives {
123122
addPrimitive(Special_wrapAsThrowable, WRAP_AS_THROWABLE)
124123
addPrimitive(Special_unwrapFromThrowable, UNWRAP_FROM_THROWABLE)
125124
addPrimitive(Special_debugger, DEBUGGER)
125+
126+
addPrimitive(LinkingInfo_linkTimePropertyBoolean, LINKTIME_PROPERTY)
127+
addPrimitive(LinkingInfo_linkTimePropertyInt, LINKTIME_PROPERTY)
128+
addPrimitive(LinkingInfo_linkTimePropertyString, LINKTIME_PROPERTY)
126129
}
127130

128131
def isJavaScriptPrimitive(code: Int): Boolean =

compiler/src/test/scala/org/scalajs/nscplugin/test/JSGlobalScopeTest.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,4 +497,34 @@ class JSGlobalScopeTest extends DirectTest with TestHelpers {
497497
}
498498
}
499499

500+
@Test
501+
def rejectAssignmentToGlobalThis(): Unit = {
502+
"""
503+
import scala.scalajs.js
504+
import scala.scalajs.js.annotation._
505+
506+
object Main {
507+
def main(): Unit = {
508+
js.Dynamic.global.`this` = 0
509+
GlobalScope.globalThis = 0
510+
}
511+
}
512+
513+
@js.native
514+
@JSGlobalScope
515+
object GlobalScope extends js.Any {
516+
@JSName("this")
517+
var globalThis: Any = js.native
518+
}
519+
""" hasErrors
520+
s"""
521+
|newSource1.scala:44: error: Illegal assignment to global this.
522+
| js.Dynamic.global.`this` = 0
523+
| ^
524+
|newSource1.scala:45: error: Illegal assignment to global this.
525+
| GlobalScope.globalThis = 0
526+
| ^
527+
"""
528+
}
529+
500530
}

compiler/src/test/scala/org/scalajs/nscplugin/test/OptimizationTest.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,14 @@ class OptimizationTest extends JSASTTest {
170170
// Verify the optimized emitted code for 'new js.Object' and 'new js.Array'
171171
"""
172172
import scala.scalajs.js
173-
174173
class A {
175174
val o = new js.Object
176175
val a = new js.Array
177176
}
178177
""".
179-
hasNot("any reference to the global scope") {
180-
case js.JSLinkingInfo() =>
178+
hasNot("any reference to the global scope nor loading JS constructor") {
179+
case js.JSGlobalRef(_) =>
180+
case js.LoadJSConstructor(_) =>
181181
}
182182
}
183183

ir/shared/src/main/scala/org/scalajs/ir/Hashers.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -478,9 +478,6 @@ object Hashers {
478478
mixTag(TagJSTypeOfGlobalRef)
479479
mixTree(globalRef)
480480

481-
case JSLinkingInfo() =>
482-
mixTag(TagJSLinkingInfo)
483-
484481
case Undefined() =>
485482
mixTag(TagUndefined)
486483

ir/shared/src/main/scala/org/scalajs/ir/Printers.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -778,9 +778,6 @@ object Printers {
778778
print(globalRef)
779779
print(")")
780780

781-
case JSLinkingInfo() =>
782-
print("<linkinginfo>")
783-
784781
// Literals
785782

786783
case Undefined() =>

ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -501,9 +501,6 @@ object Serializers {
501501
writeTagAndPos(TagJSTypeOfGlobalRef)
502502
writeTree(globalRef)
503503

504-
case JSLinkingInfo() =>
505-
writeTagAndPos(TagJSLinkingInfo)
506-
507504
case Undefined() =>
508505
writeTagAndPos(TagUndefined)
509506

@@ -1300,7 +1297,7 @@ object Serializers {
13001297
case TagJSPrivateSelect => JSPrivateSelect(readTree(), readFieldIdent())
13011298

13021299
case TagJSSelect =>
1303-
if (/* hacks.use17 */ true && buf.get(buf.position()) == TagJSLinkingInfo) { // scalastyle:ignore
1300+
if (hacks.use17 && buf.get(buf.position()) == TagJSLinkingInfo) {
13041301
val jsLinkingInfo = readTree()
13051302
readTree() match {
13061303
case StringLiteral("productionMode") =>
@@ -1343,7 +1340,7 @@ object Serializers {
13431340
case TagJSTypeOfGlobalRef => JSTypeOfGlobalRef(readTree().asInstanceOf[JSGlobalRef])
13441341

13451342
case TagJSLinkingInfo =>
1346-
if (/* hacks.use17 */ true) { // scalastyle:ignore
1343+
if (hacks.use17) {
13471344
JSObjectConstr(List(
13481345
(StringLiteral("productionMode"), LinkTimeProperty(ProductionMode)(BooleanType)),
13491346
(StringLiteral("esVersion"), LinkTimeProperty(ESVersion)(IntType)),
@@ -2475,6 +2472,8 @@ object Serializers {
24752472
assert(sourceVersion != "1.15", "source version 1.15 does not exist")
24762473

24772474
val use16: Boolean = use13 || sourceVersion == "1.16"
2475+
2476+
val use17: Boolean = use16 || sourceVersion == "1.17"
24782477
}
24792478

24802479
/** Names needed for hacks. */

ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ object Transformers {
222222

223223
case _:Skip | _:Debugger | _:LoadModule | _:StoreModule |
224224
_:SelectStatic | _:SelectJSNativeMember | _:LoadJSConstructor |
225-
_:LoadJSModule | _:JSNewTarget | _:JSImportMeta | _:JSLinkingInfo |
225+
_:LoadJSModule | _:JSNewTarget | _:JSImportMeta |
226226
_:Literal | _:VarRef | _:This | _:JSGlobalRef | _:LinkTimeProperty =>
227227
tree
228228
}

ir/shared/src/main/scala/org/scalajs/ir/Traversers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ object Traversers {
221221

222222
case _:Skip | _:Debugger | _:LoadModule | _:StoreModule |
223223
_:SelectStatic | _:SelectJSNativeMember | _:LoadJSConstructor |
224-
_:LoadJSModule | _:JSNewTarget | _:JSImportMeta | _:JSLinkingInfo |
224+
_:LoadJSModule | _:JSNewTarget | _:JSImportMeta |
225225
_:Literal | _:VarRef | _:This | _:JSGlobalRef | _:LinkTimeProperty =>
226226
}
227227

ir/shared/src/main/scala/org/scalajs/ir/Trees.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -982,10 +982,6 @@ object Trees {
982982
val tpe = AnyType
983983
}
984984

985-
sealed case class JSLinkingInfo()(implicit val pos: Position) extends Tree {
986-
val tpe = AnyType
987-
}
988-
989985
// Literals
990986

991987
/** Marker for literals. Literals are always pure.

ir/shared/src/test/scala/org/scalajs/ir/PrintersTest.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -807,10 +807,6 @@ class PrintersTest {
807807
assertPrintEquals("(typeof global:Foo)", JSTypeOfGlobalRef(JSGlobalRef("Foo")))
808808
}
809809

810-
@Test def printJSLinkingInfo(): Unit = {
811-
assertPrintEquals("<linkinginfo>", JSLinkingInfo())
812-
}
813-
814810
@Test def printUndefined(): Unit = {
815811
assertPrintEquals("(void 0)", Undefined())
816812
}

javalib/src/main/scala/java/lang/Character.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ package java.lang
1515
import scala.annotation.{tailrec, switch}
1616

1717
import scala.scalajs.js
18-
import scala.scalajs.runtime.linkingInfo
18+
import scala.scalajs.LinkingInfo
1919
import scala.scalajs.LinkingInfo.ESVersion
2020

2121
import java.lang.constant.Constable
@@ -128,7 +128,7 @@ object Character {
128128
if (!isValidCodePoint(codePoint))
129129
throw new IllegalArgumentException()
130130

131-
if (linkingInfo.esVersion >= ESVersion.ES2015) {
131+
if (LinkingInfo.esVersion >= ESVersion.ES2015) {
132132
js.Dynamic.global.String.fromCodePoint(codePoint).asInstanceOf[String]
133133
} else {
134134
if (codePoint < MIN_SUPPLEMENTARY_CODE_POINT) {

javalib/src/main/scala/java/lang/ClassValue.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import java.util.HashMap
1616

1717
import scala.scalajs.js
1818
import scala.scalajs.js.annotation._
19-
import scala.scalajs.runtime.linkingInfo
19+
import scala.scalajs.LinkingInfo
2020
import scala.scalajs.LinkingInfo.ESVersion
2121

2222
import Utils._
2323

2424
abstract class ClassValue[T] protected () {
2525
private val jsMap: js.Map[Class[_], T] = {
26-
if (linkingInfo.esVersion >= ESVersion.ES2015 || js.typeOf(js.Dynamic.global.Map) != "undefined")
26+
if (LinkingInfo.esVersion >= ESVersion.ES2015 || js.typeOf(js.Dynamic.global.Map) != "undefined")
2727
new js.Map()
2828
else
2929
null
@@ -35,7 +35,7 @@ abstract class ClassValue[T] protected () {
3535
* emitting ES 2015 code, which allows to dead-code-eliminate the branches
3636
* using `HashMap`s, and therefore `HashMap` itself.
3737
*/
38-
linkingInfo.esVersion >= ESVersion.ES2015 || jsMap != null
38+
LinkingInfo.esVersion >= ESVersion.ES2015 || jsMap != null
3939
}
4040

4141
/* We use a HashMap instead of an IdentityHashMap because the latter is

javalib/src/main/scala/java/lang/Double.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ package java.lang
1515
import java.lang.constant.{Constable, ConstantDesc}
1616

1717
import scala.scalajs.js
18-
import scala.scalajs.runtime.linkingInfo
18+
import scala.scalajs.LinkingInfo
1919

2020
import Utils._
2121

@@ -365,7 +365,7 @@ object Double {
365365
!isNaN(d) && !isInfinite(d)
366366

367367
@inline def hashCode(value: scala.Double): Int = {
368-
if (linkingInfo.isWebAssembly)
368+
if (LinkingInfo.isWebAssembly)
369369
hashCodeForWasm(value)
370370
else
371371
FloatingPointBits.numberHashCode(value)

javalib/src/main/scala/java/lang/FloatingPointBits.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ import scala.scalajs.LinkingInfo.ESVersion
2020
/** Manipulating the bits of floating point numbers. */
2121
private[lang] object FloatingPointBits {
2222

23-
import scala.scalajs.runtime.linkingInfo
23+
import scala.scalajs.LinkingInfo
2424

2525
private[this] val _areTypedArraysSupported = {
2626
// Here we use the `esVersion` test to dce the 4 subsequent tests
27-
linkingInfo.esVersion >= ESVersion.ES2015 || {
27+
LinkingInfo.esVersion >= ESVersion.ES2015 || {
2828
js.typeOf(global.ArrayBuffer) != "undefined" &&
2929
js.typeOf(global.Int32Array) != "undefined" &&
3030
js.typeOf(global.Float32Array) != "undefined" &&
@@ -42,7 +42,7 @@ private[lang] object FloatingPointBits {
4242
* * If we emit ES5, replace `areTypedArraysSupported` by
4343
* `_areTypedArraysSupported` so we do not calculate it multiple times.
4444
*/
45-
linkingInfo.esVersion >= ESVersion.ES2015 || _areTypedArraysSupported
45+
LinkingInfo.esVersion >= ESVersion.ES2015 || _areTypedArraysSupported
4646
}
4747

4848
private val arrayBuffer =

javalib/src/main/scala/java/lang/Integer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ package java.lang
1515
import java.lang.constant.{Constable, ConstantDesc}
1616

1717
import scala.scalajs.js
18-
import scala.scalajs.runtime.linkingInfo
18+
import scala.scalajs.LinkingInfo
1919
import scala.scalajs.LinkingInfo.ESVersion
2020

2121
/* This is a hijacked class. Its instances are primitive numbers.
@@ -279,7 +279,7 @@ object Integer {
279279

280280
// Intrinsic, fallback on actual code for non-literal in JS
281281
@inline def numberOfLeadingZeros(i: scala.Int): scala.Int = {
282-
if (linkingInfo.esVersion >= ESVersion.ES2015) js.Math.clz32(i)
282+
if (LinkingInfo.esVersion >= ESVersion.ES2015) js.Math.clz32(i)
283283
else clz32Dynamic(i)
284284
}
285285

javalib/src/main/scala/java/lang/Math.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ package lang
1616
import scala.scalajs.js
1717
import js.Dynamic.{ global => g }
1818

19-
import scala.scalajs.runtime.linkingInfo
19+
import scala.scalajs.LinkingInfo
2020
import scala.scalajs.LinkingInfo.ESVersion
2121

2222
object Math {
2323
final val E = 2.718281828459045
2424
final val PI = 3.141592653589793
2525

2626
@inline private def assumingES6: scala.Boolean =
27-
linkingInfo.esVersion >= ESVersion.ES2015
27+
LinkingInfo.esVersion >= ESVersion.ES2015
2828

2929
@inline def abs(a: scala.Int): scala.Int = if (a < 0) -a else a
3030
@inline def abs(a: scala.Long): scala.Long = if (a < 0) -a else a

0 commit comments

Comments
 (0)