8000 Merge the IR node `This` into `VarRef`, with a magic `LocalName`. · scala-js/scala-js@eac8bf7 · GitHub
[go: up one dir, main page]

Skip to content

Commit eac8bf7

Browse files
committed
Merge the IR node This into VarRef, with a magic LocalName.
Everywhere we handle `VarRef`s, we applied the same treatment to `This` nodes. In some places, like in the optimizer's `Binding`s, we also had an additional abstraction to represent either an actual `VarRef` or a `this` value. We now merge `This` as a particular case of `VarRef`. We use a magic `LocalName` that is otherwise invalid, namely `.this`. A variable of that name cannot be declared, either in a `ParamDef` or in a `VarDef`. It is only introduced for receivers, and is always immutable. Several areas of the code get simpler with the merge. The optimizer's `Binding`s is the most obvious example. The `FunctionEmitter`s also benefit from the changes. Other areas get nothing but a reduction of alternatives in pattern matches. `this` values still hold particular meaning in many situations. Therefore, we keep a source compatible constructor/extractor object for `This()`.
1 parent 7da555a commit eac8bf7

File tree

20 files changed

+298
-220
lines changed

20 files changed

+298
-220
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
11071107
selfVarDef :: memberDefinitions
11081108
}
11091109

1110-
// After the super call, substitute `selfRef` for `This()`
1110+
// After the super call, substitute `selfRef` for `this`
11111111
val afterSuper = new ir.Transformers.Transformer {
11121112
override def transform(tree: js.Tree): js.Tree = tree match {
11131113
case js.This() =>
@@ -2430,7 +2430,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
24302430
tree match {
24312431
case js.Block(stats :+ expr) =>
24322432
js.Block(stats :+ exprToStat(expr))
2433-
case _:js.Literal | _:js.This | _:js.VarRef =>
2433+
case _:js.Literal | _:js.VarRef =>
24342434
js.Skip()
24352435
case _ =>
24362436
tree
@@ -4907,8 +4907,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
49074907
val newReceiver = genExpr(receiver)
49084908
val newArg = genStatOrExpr(arg, isStat)
49094909
newReceiver match {
4910-
case js.This() =>
4911-
// common case for which there is no side-effect nor NPE
4910+
case newReceiver: js.VarRef if !newReceiver.tpe.isNullable =>
4911+
// common case (notably for `this`) for which there is no side-effect nor NPE
49124912
newArg
49134913
case _ =>
49144914
js.Block(

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -497,12 +497,13 @@ object Hashers {
497497
mixTypeRef(typeRef)
498498

499499
case VarRef(name) =>
500-
mixTag(TagVarRef)
501-
mixName(name)
502-
mixType(tree.tpe)
503-
504-
case This() =>
505-
mixTag(TagThis)
500+
if (name.isThis) {
501+
// "Optimized" representation, like in Serializers
502+
mixTag(TagThis)
503+
} else {
504+
mixTag(TagVarRef)
505+
mixName(name)
506+
}
506507
mixType(tree.tpe)
507508

508509
case Closure(arrow, captureParams, params, restParam, body, captureValues) =>

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

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ object Names {
6464
*
6565
* Local names must be non-empty, and can contain any Unicode code point
6666
* except `/ . ; [`.
67+
*
68+
* As an exception, the local name `".this"` represents the `this` binding.
69+
* It cannot be used to declare variables (in `VarDef`s or `ParamDef`s) but
70+
* can be referred to with a `VarRef`.
6771
*/
6872
final class LocalName private (encoded: UTF8String)
6973
extends Name(encoded) with Comparable[LocalName] {
@@ -79,22 +83,40 @@ object Names {
7983

8084
protected def stringPrefix: String = "LocalName"
8185

82-
final def withPrefix(prefix: LocalName): LocalName =
86+
final def isThis: Boolean =
87+
this eq LocalName.This
88+
89+
final def withPrefix(prefix: LocalName): LocalName = {
90+
require(!isThis && !prefix.isThis, "cannot concatenate LocalName.This")
8391
new LocalName(prefix.encoded ++ this.encoded)
92+
}
8493

8594
final def withPrefix(prefix: String): LocalName =
8695
LocalName(UTF8String(prefix) ++ this.encoded)
8796

88-
final def withSuffix(suffix: LocalName): LocalName =
97+
final def withSuffix(suffix: LocalName): LocalName = {
98+
require(!isThis && !suffix.isThis, "cannot concatenate LocalName.This")
8999
new LocalName(this.encoded ++ suffix.encoded)
100+
}
90101

91102
final def withSuffix(suffix: String): LocalName =
92103
LocalName(this.encoded ++ UTF8String(suffix))
93104
}
94105

95106
object LocalName {
96-
def apply(name: UTF8String): LocalName =
97-
new LocalName(validateSimpleEncodedName(name))
107+
private final val ThisEncodedName: UTF8String =
108+
UTF8String(".this")
109+
110+
/** The unique `LocalName` with encoded name `.this`. */
111+
val This: LocalName =
112+
new LocalName(ThisEncodedName)
113+
114+
def apply(name: UTF8String): LocalName = {
115+
if (UTF8String.equals(name, ThisEncodedName))
116+
This
117+
else
118+
new LocalName(validateSimpleEncodedName(name))
119+
}
98120

99121
def apply(name: String): LocalName =
100122
LocalName(UTF8String(name))

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,6 @@ object Printers {
570570
case JSPrivateSelect(qual, _) => containsOnlySelectsFromAtom(qual)
571571
case JSSelect(qual, _) => containsOnlySelectsFromAtom(qual)
572572
case VarRef(_) => true
573-
case This() => true
574573
case _ => false // in particular, Apply
575574
}
576575
if (containsOnlySelectsFromAtom(ctor)) {
@@ -844,10 +843,10 @@ object Printers {
844843
// Atomic expressions
845844

846845
case VarRef(name) =>
847-
print(name)
848-
849-
case This() =>
850-
print("this")
846+
if (name.isThis)
847+
print("this")
848+
else
849+
print(name)
851850

852851
case Closure(arrow, captureParams, params, restParam, body, captureValues) =>
853852
if (arrow)

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -524,12 +524,13 @@ object Serializers {
524524
writeTypeRef(typeRef)
525525

526526
case VarRef(name) =>
527-
writeTagAndPos(TagVarRef)
528-
writeName(name)
529-
writeType(tree.tpe)
530-
531-
case This() =>
532-
writeTagAndPos(TagThis)
527+
if (name.isThis) {
528+
// "Optimized" representation that is compatible with IR < 1.18
529+
writeTagAndPos(TagThis)
530+
} else {
531+
writeTagAndPos(TagVarRef)
532+
writeName(name)
533+
}
533534
writeType(tree.tpe)
534535

535536
case Closure(arrow, captureParams, params, restParam, body, captureValues) =>
@@ -1155,10 +1156,13 @@ object Serializers {
11551156
if (hacks.use13) {
11561157
val cls = readClassName()
11571158
val rhs = readTree()
1158-
if (cls != enclosingClassName || !rhs.isInstanceOf[This]) {
1159-
throw new IOException(
1160-
s"Illegal legacy StoreModule(${cls.nameString}, $rhs) " +
1161-
s"found in class ${enclosingClassName.nameString}")
1159+
rhs match {
1160+
case This() if cls == enclosingClassName =>
116 179B 1+
// ok
1162+
case _ =>
1163+
throw new IOException(
1164+
s"Illegal legacy StoreModule(${cls.nameString}, $rhs) " +
1165+
s"found in class ${enclosingClassName.nameString}")
11621166
}
11631167
}
11641168
StoreModule()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ object Transformers {
195195
case _:Skip | _:Debugger | _:LoadModule | _:StoreModule |
196196
_:SelectStatic | _:SelectJSNativeMember | _:LoadJSConstructor |
197197
_:LoadJSModule | _:JSNewTarget | _:JSImportMeta |
198-
_:Literal | _:VarRef | _:This | _:JSGlobalRef | _:LinkTimeProperty =>
198+
_:Literal | _:VarRef | _:JSGlobalRef | _:LinkTimeProperty =>
199199
tree
200200
}
201201
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ object Traversers {
201201
case _:Skip | _:Debugger | _:LoadModule | _:StoreModule |
202202
_:SelectStatic | _:SelectJSNativeMember | _:LoadJSConstructor |
203203
_:LoadJSModule | _:JSNewTarget | _:JSImportMeta |
204-
_:Literal | _:VarRef | _:This | _:JSGlobalRef | _:LinkTimeProperty =>
204+
_:Literal | _:VarRef | _:JSGlobalRef | _:LinkTimeProperty =>
205205
}
206206

207207
def traverseClassDef(tree: ClassDef): Unit = {

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,8 +1081,14 @@ object Trees {
10811081
sealed case class VarRef(name: LocalName)(val tpe: Type)(
10821082
implicit val pos: Position) extends AssignLhs
10831083

1084-
sealed case class This()(val tpe: Type)(implicit val pos: Position)
1085-
extends Tree
1084+
/** Convenience constructor and extractor for `VarRef`s representing `this` bindings. */
1085+
object This {
1086+
def apply()(tpe: Type)(implicit pos: Position): VarRef =
1087+
VarRef(LocalName.This)(tpe)
1088+
1089+
def unapply(tree: VarRef): Boolean =
1090+
tree.name.isThis
1091+
}
10861092

10871093
/** Closure with explicit captures.
10881094
*

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -863,9 +863,6 @@ class PrintersTest {
863863

864864
@Test def printVarRef(): Unit = {
865865
assertPrintEquals("x", VarRef("x")(IntType))
866-
}
867-
868-
@Test def printThis(): Unit = {
869866
assertPrintEquals("this", This()(AnyType))
870867
}
871868

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

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,7 +1261,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
12611261
def test(tree: Tree): Boolean = tree match {
12621262
// Atomic expressions
12631263
case _: Literal => true
1264-
case _: This => true
12651264
case _: JSNewTarget => true
12661265
case _: LinkTimeProperty => true
12671266

@@ -2468,18 +2467,18 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
24682467
genCallHelper(VarField.systemIdentityHashCode, newLhs)
24692468

24702469
case WrapAsThrowable =>
2471-
val newLhsVar = newLhs.asInstanceOf[js.VarRef]
2470+
assert(newLhs.isInstanceOf[js.VarRef] || newLhs.isInstanceOf[js.This], newLhs)
24722471
js.If(
2473-
genIsInstanceOfClass(newLhsVar, ThrowableClass),
2474-
newLhsVar,
2475-
genScalaClassNew(JavaScriptExceptionClass, AnyArgConstructorName, newLhsVar))
2472+
genIsInstanceOfClass(newLhs, ThrowableClass),
2473+
newLhs,
2474+
genScalaClassNew(JavaScriptExceptionClass, AnyArgConstructorName, newLhs))
24762475

24772476
case UnwrapFromThrowable =>
2478-
val newLhsVar = newLhs.asInstanceOf[js.VarRef]
2477+
assert(newLhs.isInstanceOf[js.VarRef] || newLhs.isInstanceOf[js.This], newLhs)
24792478
js.If(
2480-
genIsInstanceOfClass(newLhsVar, JavaScriptExceptionClass),
2481-
genSelect(newLhsVar, FieldIdent(exceptionFieldName)),
2482-
newLhsVar)
2479+
genIsInstanceOfClass(newLhs, JavaScriptExceptionClass),
2480+
genSelect(newLhs, FieldIdent(exceptionFieldName)),
2481+
newLhs)
24832482
}
24842483

24852484
case BinaryOp(op, lhs, rhs) =>
@@ -2519,7 +2518,10 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
25192518
* that the body of `Object.equals__O__Z` can be compiled as
25202519
* `this === that` instead of `Object.is(this, that)`.
25212520
*/
2522-
!tree.isInstanceOf[This]
2521+
tree match {
2522+
case This() => false
2523+
case _ => true
2524+
}
25232525
case ClassType(BoxedByteClass | BoxedShortClass |
25242526
BoxedIntegerClass | BoxedFloatClass | BoxedDoubleClass, _) =>
25252527
true
@@ -3021,12 +3023,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
30213023
case Transient(JSVarRef(name, _)) =>
30223024
js.VarRef(name)
30233025

3024-
case This() =>
3025-
if (env.hasExplicitThis)
3026-
fileLevelVar(VarField.thiz)
3027-
else
3028-
js.This()
3029-
30303026
case tree: Closure =>
30313027
transformClosure(tree)
30323028

@@ -3180,12 +3176,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
31803176
VarKind.Immutable
31813177
}
31823178

3183-
case This() if env.hasExplicitThis =>
3184-
VarKind.ExplicitThisAlias
3185-
3186-
case This() if permitImplicitJSThisCapture =>
3187-
VarKind.ThisAlias
3188-
31893179
case _ =>
31903180
explicitCapture()
31913181
VarKind.Immutable
@@ -3415,7 +3405,6 @@ private object FunctionEmitter {
34153405
// Environment
34163406

34173407
final class Env private (
3418-
val hasExplicitThis: Boolean,
34193408
val expectedReturnType: Type,
34203409
val enclosingClassName: Option[ClassName],
34213410
vars: Map[LocalName, VarKind],
@@ -3448,7 +3437,7 @@ private object FunctionEmitter {
34483437
copy(enclosingClassName = enclosingClassName)
34493438

34503439
def withExplicitThis(): Env =
3451-
copy(hasExplicitThis = true)
3440+
copy(vars = vars + (LocalName.This -> VarKind.ExplicitThisAlias))
34523441

34533442
def withVars(newVars: Map[LocalName, VarKind]): Env =
34543443
copy(vars = vars ++ newVars)
@@ -3483,7 +3472,6 @@ private object FunctionEmitter {
34833472
copy(inLoopForVarCapture = inLoopForVarCapture)
34843473

34853474
private def copy(
3486-
hasExplicitThis: Boolean = this.hasExplicitThis,
34873475
expectedReturnType: Type = this.expectedReturnType,
34883476
enclosingClassName: Option[ClassName] = this.enclosingClassName,
34893477
vars: Map[LocalName, VarKind] = this.vars,
34923480
defaultBreakTargets: Set[LabelName] = this.defaultBreakTargets,
34933481
defaultContinueTargets: Set[LabelName] = this.defaultContinueTargets,
34943482
inLoopForVarCapture: Boolean = this.inLoopForVarCapture): Env = {
3495-
new Env(hasExplicitThis, expectedReturnType, enclosingClassName, vars,
3483+
new Env(expectedReturnType, enclosingClassName, vars,
34963484
labeledExprLHSes, labelsTurnedIntoContinue, defaultBreakTargets,
34973485
defaultContinueTargets, inLoopForVarCapture)
34983486
}
34993487
}
35003488

35013489
object Env {
3490+
private val InitVars: Map[LocalName, VarKind] =
3491+
Map(LocalName.This -> VarKind.ThisAlias)
3492+
35023493
def empty(expectedReturnType: Type): Env = {
3503-
new Env(false, expectedReturnType, None, Map.empty, Map.empty, Set.empty,
3494+
new Env(expectedReturnType, None, InitVars, Map.empty, Set.empty,
35043495
Set.empty, Set.empty, false)
35053496
}
35063497
}

0 commit comments

Comments
 (0)
0