Skip to content

Commit 5e5880a

Browse files
authored
Merge pull request scala-js#4630 from sjrd/call-direct-builtins
Directly call ES2015 builtins instead of storing them in a var.
2 parents f115c47 + e4b60eb commit 5e5880a

File tree

5 files changed

+116
-55
lines changed

5 files changed

+116
-55
lines changed

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -442,20 +442,17 @@ private[emitter] final class ClassEmitter(sjsGen: SJSGen) {
442442
} yield {
443443
implicit val pos = field.pos
444444

445-
val symbolValue = {
445+
val symbolValueWithGlobals = {
446446
def description = origName.getOrElse(name).toString()
447447
val args =
448448
if (semantics.productionMode) Nil
449449
else js.StringLiteral(description) :: Nil
450-
451-
if (esFeatures.esVersion >= ESVersion.ES2015)
452-
js.Apply(js.VarRef(js.Ident("Symbol")), args)
453-
else
454-
genCallHelper("privateJSFieldSymbol", args: _*)
450+
genCallPolyfillableBuiltin(PolyfillableBuiltin.PrivateSymbolBuiltin, args: _*)
455451
}
456452

457-
globalVarDef("r", (tree.className, name), symbolValue,
458-
origName.orElse(name))
453+
symbolValueWithGlobals.flatMap { symbolValue =>
454+
globalVarDef("r", (tree.className, name), symbolValue, origName.orElse(name))
455+
}
459456
}
460457

461458
WithGlobals.list(defs)

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/CoreJSLib.scala

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import org.scalajs.linker.interface.unstable.RuntimeClassNameMapperImpl
2828
import org.scalajs.linker.backend.javascript.Trees._
2929

3030
import EmitterNames._
31+
import PolyfillableBuiltin._
3132

3233
private[emitter] object CoreJSLib {
3334

@@ -167,8 +168,8 @@ private[emitter] object CoreJSLib {
167168
}
168169

169170
private def defineJSBuiltinsSnapshotsAndPolyfills(): Tree = {
170-
def genPolyfillFor(builtinName: String): Tree = builtinName match {
171-
case "is" =>
171+
def genPolyfillFor(builtin: PolyfillableBuiltin): Tree = builtin match {
172+
case ObjectIsBuiltin =>
172173
val x = varRef("x")
173174
val y = varRef("y")
174175
genArrowFunction(paramList(x, y), Return {
@@ -181,7 +182,7 @@ private[emitter] object CoreJSLib {
181182
})
182183
})
183184

184-
case "imul" =>
185+
case ImulBuiltin =>
185186
val a = varRef("a")
186187
val b = varRef("b")
187188
val ah = varRef("ah")
@@ -196,7 +197,7 @@ private[emitter] object CoreJSLib {
196197
Return((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0)
197198
))
198199

199-
case "fround" =>
200+
case FroundBuiltin =>
200201
val v = varRef("v")
201202
if (!strictFloats) {
202203
genArrowFunction(paramList(v), Return(+v))
@@ -300,7 +301,7 @@ private[emitter] object CoreJSLib {
300301
typedArrayPolyfill, noTypedArrayPolyfill)
301302
}
302303

303-
case "clz32" =>
304+
case Clz32Builtin =>
304305
val i = varRef("i")
305306
val r = varRef("r")
306307
genArrowFunction(paramList(i), Block(
@@ -314,7 +315,7 @@ private[emitter] object CoreJSLib {
314315
Return(r + (i >> 31))
315316
))
316317

317-
case "privateJSFieldSymbol" =>
318+
case PrivateSymbolBuiltin =>
318319
/* function privateJSFieldSymbol(description) {
319320
* function rand32() {
320321
* const s = ((Math.random() * 4294967296.0) | 0).toString(16);
@@ -359,7 +360,7 @@ private[emitter] object CoreJSLib {
359360
}
360361
))
361362

362-
case "getOwnPropertyDescriptors" =>
363+
case GetOwnPropertyDescriptorsBuiltin =>
363364
/* getOwnPropertyDescriptors = (() => {
364365
* // Fetch or polyfill Reflect.ownKeys
365366
* var ownKeysFun;
@@ -457,31 +458,24 @@ private[emitter] object CoreJSLib {
457458
Apply(funGenerator, Nil)
458459
}
459460

460-
val mathBuiltins = Block(
461-
List("imul", "fround", "clz32").map { builtinName =>
462-
val rhs0 = genIdentBracketSelect(MathRef, builtinName)
463-
val rhs =
464-
if (esVersion >= ESVersion.ES2015) rhs0
465-
else rhs0 || genPolyfillFor(builtinName)
466-
extractWithGlobals(globalVarDef(builtinName, CoreVar, rhs))
461+
val polyfillDefs = for {
462+
builtin <- PolyfillableBuiltin.All
463+
if esVersion < builtin.availableInESVersion
464+
} yield {
465+
val polyfill = genPolyfillFor(builtin)
466+
val rhs = builtin match {
467+
case builtin: GlobalVarBuiltin =>
468+
// (typeof GlobalVar !== "undefined") ? GlobalVar : polyfill
469+
val globalVarRef = globalRef(builtin.globalVar)
470+
If(UnaryOp(JSUnaryOp.typeof, globalVarRef) !== str("undefined"),
471+
globalVarRef, polyfill)
472+
case builtin: NamespacedBuiltin =>
473+
// NamespaceGlobalVar.builtinName || polyfill
474+
genIdentBracketSelect(globalRef(builtin.namespaceGlobalVar), builtin.builtinName) || polyfill
467475
}
468-
)
469-
470-
val es5Compat = condTree(esVersion < ESVersion.ES2015)(Block(
471-
extractWithGlobals(globalVarDef("is", CoreVar,
472-
genIdentBracketSelect(ObjectRef, "is") || genPolyfillFor("is"))),
473-
extractWithGlobals(globalVarDef("privateJSFieldSymbol", CoreVar,
474-
If(UnaryOp(JSUnaryOp.typeof, SymbolRef) !== str("undefined"),
475-
SymbolRef, genPolyfillFor("privateJSFieldSymbol"))))
476-
))
477-
478-
val es2017Compat = condTree(esVersion < ESVersion.ES2017)(Block(
479-
extractWithGlobals(globalVarDef("getOwnPropertyDescriptors", CoreVar,
480-
genIdentBracketSelect(ObjectRef, "getOwnPropertyDescriptors") ||
481-
genPolyfillFor("getOwnPropertyDescriptors")))
482-
))
483-
484-
Block(mathBuiltins, es5Compat, es2017Compat)
476+
extractWithGlobals(globalVarDef(builtin.builtinName, CoreVar, rhs))
477+
}
478+
Block(polyfillDefs)
485479
}
486480

487481
private def declareCachedL0(): Tree = {
@@ -611,12 +605,8 @@ private[emitter] object CoreJSLib {
611605

612606
defineFunction1("objectClone") { instance =>
613607
// return Object.create(Object.getPrototypeOf(instance), $getOwnPropertyDescriptors(instance));
614-
val callGetOwnPropertyDescriptors = {
615-
if (esVersion >= ESVersion.ES2017)
616-
Apply(genIdentBracketSelect(ObjectRef, "getOwnPropertyDescriptors"), instance :: Nil)
617-
else
618-
genCallHelper("getOwnPropertyDescriptors", instance)
619-
}
608+
val callGetOwnPropertyDescriptors = genCallPolyfillableBuiltin(
609+
GetOwnPropertyDescriptorsBuiltin, instance)
620610
Return(Apply(genIdentBracketSelect(ObjectRef, "create"), List(
621611
Apply(genIdentBracketSelect(ObjectRef, "getPrototypeOf"), instance :: Nil),
622612
callGetOwnPropertyDescriptors)))
@@ -894,7 +884,7 @@ private[emitter] object CoreJSLib {
894884
(abs & bigInt(~0xffffL)) | bigInt(0x8000L)
895885
})),
896886
const(absR, Apply(NumberRef, y :: Nil)),
897-
Return(genCallHelper("fround", If(x < bigInt(0L), -absR, absR)))
887+
Return(genCallPolyfillableBuiltin(FroundBuiltin, If(x < bigInt(0L), -absR, absR)))
898888
)
899889
}
900890
))
@@ -1188,7 +1178,7 @@ private[emitter] object CoreJSLib {
11881178
condTree(strictFloats)(
11891179
defineFunction1("isFloat") { v =>
11901180
Return((typeof(v) === str("number")) &&
1191-
((v !== v) || (genCallHelper("fround", v) === v)))
1181+
((v !== v) || (genCallPolyfillableBuiltin(FroundBuiltin, v) === v)))
11921182
}
11931183
)
11941184
)
@@ -1953,6 +1943,11 @@ private[emitter] object CoreJSLib {
19531943
private def genArrowFunction(args: List[ParamDef], body: Tree): Function =
19541944
jsGen.genArrowFunction(args, None, body)
19551945

1946+
private def genCallPolyfillableBuiltin(builtin: PolyfillableBuiltin,
1947+
args: Tree*): Tree = {
1948+
extractWithGlobals(sjsGen.genCallPolyfillableBuiltin(builtin, args: _*))
1949+
}
1950+
19561951
private def maybeWrapInUBE(behavior: CheckedBehavior, exception: Tree): Tree = {
19571952
if (behavior == CheckedBehavior.Fatal) {
19581953
genScalaClassNew(UndefinedBehaviorErrorClass,

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/FunctionEmitter.scala

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import org.scalajs.linker.backend.javascript.{Trees => js}
3333
import java.io.StringWriter
3434

3535
import EmitterNames._
36+
import PolyfillableBuiltin._
3637
import Transients._
3738

3839
/** Desugaring of the IR to JavaScript functions.
@@ -2361,10 +2362,8 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
23612362
js.BinaryOp(if (op == ===) JSBinaryOp.=== else JSBinaryOp.!==,
23622363
newLhs, newRhs)
23632364
} else {
2364-
val objectIs =
2365-
if (!es2015) globalVar("is", CoreVar)
2366-
else genIdentBracketSelect(genGlobalVarRef("Object"), "is")
2367-
val objectIsCall = js.Apply(objectIs, newLhs :: newRhs :: Nil)
2365+
val objectIsCall =
2366+
genCallPolyfillableBuiltin(ObjectIsBuiltin, newLhs, newRhs)
23682367
if (op == ===) objectIsCall
23692368
else js.UnaryOp(JSUnaryOp.!, objectIsCall)
23702369
}
@@ -2388,7 +2387,8 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
23882387
case IntLiteral(0) => or0(js.UnaryOp(JSUnaryOp.-, newRhs))
23892388
case _ => or0(js.BinaryOp(JSBinaryOp.-, newLhs, newRhs))
23902389
}
2391-
case Int_* => genCallHelper("imul", newLhs, newRhs)
2390+
case Int_* =>
2391+
genCallPolyfillableBuiltin(ImulBuiltin, newLhs, newRhs)
23922392
case Int_/ =>
23932393
rhs match {
23942394
case IntLiteral(r) if r != 0 =>
@@ -2662,7 +2662,7 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
26622662
}
26632663

26642664
case Transient(NumberOfLeadingZeroes(num)) =>
2665-
genCallHelper("clz32", transformExprNoChar(num))
2665+
genCallPolyfillableBuiltin(Clz32Builtin, transformExprNoChar(num))
26662666

26672667
case Transient(ObjectClassName(obj)) =>
26682668
genCallHelper("objectClassName", transformExprNoChar(obj))
@@ -2991,10 +2991,15 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
29912991
js.Apply(globalVar(field, (className, method.name)), args)
29922992
}
29932993

2994-
private def genFround(arg: js.Tree)(implicit pos: Position): js.Tree = {
2995-
genCallHelper("fround", arg)
2994+
private def genCallPolyfillableBuiltin(
2995+
builtin: PolyfillableBuiltin, args: js.Tree*)(
2996+
implicit pos: Position): js.Tree = {
2997+
extractWithGlobals(sjsGen.genCallPolyfillableBuiltin(builtin, args: _*))
29962998
}
29972999

3000+
private def genFround(arg: js.Tree)(implicit pos: Position): js.Tree =
3001+
genCallPolyfillableBuiltin(FroundBuiltin, arg)
3002+
29983003
private def wrapBigInt32(tree: js.Tree)(implicit pos: Position): js.Tree =
29993004
wrapBigIntN(32, tree)
30003005

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Scala.js (https://www.scala-js.org/)
3+
*
4+
* Copyright EPFL.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (https://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package org.scalajs.linker.backend.emitter
14+
15+
import org.scalajs.linker.interface.ESVersion
16+
17+
private[emitter] sealed abstract class PolyfillableBuiltin(
18+
val builtinName: String, val availableInESVersion: ESVersion)
19+
20+
private[emitter] object PolyfillableBuiltin {
21+
lazy val All: List[PolyfillableBuiltin] = List(
22+
ObjectIsBuiltin,
23+
ImulBuiltin,
24+
FroundBuiltin,
25+
Clz32Builtin,
26+
PrivateSymbolBuiltin,
27+
GetOwnPropertyDescriptorsBuiltin
28+
)
29+
30+
sealed abstract class GlobalVarBuiltin(val globalVar: String,
31+
builtinName: String, availableInESVersion: ESVersion)
32+
extends PolyfillableBuiltin(builtinName, availableInESVersion)
33+
34+
sealed abstract class NamespacedBuiltin(val namespaceGlobalVar: String,
35+
builtinName: String, availableInESVersion: ESVersion)
36+
extends PolyfillableBuiltin(builtinName, availableInESVersion)
37+
38+
case object ObjectIsBuiltin extends NamespacedBuiltin("Object", "is", ESVersion.ES2015)
39+
case object ImulBuiltin extends NamespacedBuiltin("Math", "imul", ESVersion.ES2015)
40+
case object FroundBuiltin extends NamespacedBuiltin("Math", "fround", ESVersion.ES2015)
41+
case object Clz32Builtin extends NamespacedBuiltin("Math", "clz32", ESVersion.ES2015)
42+
case object PrivateSymbolBuiltin
43+
extends GlobalVarBuiltin("Symbol", "privateJSFieldSymbol", ESVersion.ES2015)
44+
case object GetOwnPropertyDescriptorsBuiltin
45+
extends NamespacedBuiltin("Object", "getOwnPropertyDescriptors", ESVersion.ES2017)
46+
}

linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/SJSGen.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.scalajs.linker.backend.javascript.Trees._
2121
import org.scalajs.linker.interface._
2222

2323
import EmitterNames._
24+
import PolyfillableBuiltin._
2425

2526
/** Scala.js specific tree generators that are used across the board.
2627
*
@@ -372,6 +373,23 @@ private[emitter] final class SJSGen(
372373
Apply(globalVar(helperName, CoreVar), args.toList)
373374
}
374375

376+
def genCallPolyfillableBuiltin(builtin: PolyfillableBuiltin, args: Tree*)(
377+
implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
378+
pos: Position): WithGlobals[Tree] = {
379+
if (esFeatures.esVersion >= builtin.availableInESVersion) {
380+
builtin match {
381+
case builtin: GlobalVarBuiltin =>
382+
for (global <- globalRef(builtin.globalVar)) yield
383+
Apply(global, args.toList)
384+
case builtin: NamespacedBuiltin =>
385+
for (namespace <- globalRef(builtin.namespaceGlobalVar)) yield
386+
Apply(genIdentBracketSelect(namespace, builtin.builtinName), args.toList)
387+
}
388+
} else {
389+
WithGlobals(genCallHelper(builtin.builtinName, args: _*))
390+
}
391+
}
392+
375393
def genLoadModule(moduleClass: ClassName)(
376394
implicit moduleContext: ModuleContext, globalKnowledge: GlobalKnowledge,
377395
pos: Position): Tree = {

0 commit comments

Comments
 (0)