Skip to content

Commit db9f5a3

Browse files
committed
Add a desugaring step in the base linker.
Previously, the emitters and the optimizer all had to perform the same desugaring for `LinkTimeProperty` nodes. Instead, we now perform the desugaring in the `BaseLinker`, after the reachability analysis. The reachability analysis records whether each method needs desugaring or not. For those that do, we cache the resulting desugaring. Methods that do not require desugaring are not processed, and so incur no additional cost. The machinery is heavy. It definitely outweighs the benefits in terms of duplication for `LinkTimeProperty` alone. However, the same machinery will be used to desugar `NewLambda` nodes. This commit serves as a stepping stone in that direction.
1 parent 98e0875 commit db9f5a3

File tree

12 files changed

+227
-59
lines changed

12 files changed

+227
-59
lines changed

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -234,14 +234,8 @@ object Transformers {
234234
case jsMethodDef: JSMethodDef =>
235235
transformJSMethodDef(jsMethodDef)
236236

237-
case JSPropertyDef(flags, name, getterBody, setterArgAndBody) =>
238-
JSPropertyDef(
239-
flags,
240-
transform(name),
241-
transformTreeOpt(getterBody),
242-
setterArgAndBody.map { case (arg, body) =>
243-
(arg, transform(body))
244-
})(Unversioned)(jsMethodPropDef.pos)
237+
case jsPropertyDef: JSPropertyDef =>
238+
transformJSPropertyDef(jsPropertyDef)
245239
}
246240
}
247241

@@ -251,6 +245,18 @@ object Transformers {
251245
jsMethodDef.optimizerHints, Unversioned)(jsMethodDef.pos)
252246
}
253247

248+
def transformJSPropertyDef(jsPropertyDef: JSPropertyDef): JSPropertyDef = {
249+
val JSPropertyDef(flags, name, getterBody, setterArgAndBody) = jsPropertyDef
250+
JSPropertyDef(
251+
flags,
252+
transform(name),
253+
transformTreeOpt(getterBody),
254+
setterArgAndBody.map { case (arg, body) =>
255+
(arg, transform(body))
256+
}
257+
)(Unversioned)(jsPropertyDef.pos)
258+
}
259+
254260
def transformJSConstructorBody(body: JSConstructorBody): JSConstructorBody = {
255261
implicit val pos = body.pos
256262

linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analysis.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ object Analysis {
8484
def methodInfos(
8585
namespace: MemberNamespace): scala.collection.Map[MethodName, MethodInfo]
8686

87+
def anyJSMemberNeedsDesugaring: Boolean
88+
8789
def displayName: String = className.nameString
8890
}
8991

@@ -103,6 +105,7 @@ object Analysis {
103105
def instantiatedSubclasses: scala.collection.Seq[ClassInfo]
104106
def nonExistent: Boolean
105107
def syntheticKind: MethodSyntheticKind
108+
def needsDesugaring: Boolean
106109

107110
def displayName: String = methodName.displayName
108111

@@ -161,6 +164,7 @@ object Analysis {
161164
def owningClass: ClassName
162165
def staticDependencies: scala.collection.Set[ClassName]
163166
def externalDependencies: scala.collection.Set[String]
167+
def needsDesugaring: Boolean
164168
}
165169

166170
sealed trait Error {

linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,9 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean,
690690
val publicMethodInfos: mutable.Map[MethodName, MethodInfo] =
691691
methodInfos(MemberNamespace.Public)
692692

693+
def anyJSMemberNeedsDesugaring: Boolean =
694+
data.jsMethodProps.exists(info => (info.globalFlags & ReachabilityInfo.FlagNeedsDesugaring) != 0)
695+
693696
def lookupAbstractMethod(methodName: MethodName): MethodInfo = {
694697
val candidatesIterator = for {
695698
ancestor <- ancestors.iterator
@@ -1289,6 +1292,9 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean,
12891292
def isDefaultBridge: Boolean =
12901293
syntheticKind.isInstanceOf[MethodSyntheticKind.DefaultBridge]
12911294

1295+
def needsDesugaring: Boolean =
1296+
(data.globalFlags & ReachabilityInfo.FlagNeedsDesugaring) != 0
1297+
12921298
/** Throws MatchError if `!isDefaultBridge`. */
12931299
def defaultBridgeTarget: ClassName = (syntheticKind: @unchecked) match {
12941300
case MethodSyntheticKind.DefaultBridge(target) => target
@@ -1371,6 +1377,9 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean,
13711377
def staticDependencies: scala.collection.Set[ClassName] = _staticDependencies.keySet
13721378
def externalDependencies: scala.collection.Set[String] = _externalDependencies.keySet
13731379

1380+
def needsDesugaring: Boolean =
1381+
(data.reachability.globalFlags & ReachabilityInfo.FlagNeedsDesugaring) != 0
1382+
13741383
def reach(): Unit = followReachabilityInfo(data.reachability, this)(FromExports)
13751384
}
13761385

@@ -1445,7 +1454,7 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean,
14451454
}
14461455
}
14471456

1448-
val globalFlags = data.globalFlags
1457+
val globalFlags = data.globalFlags & ~ReachabilityInfo.FlagNeedsDesugaring
14491458

14501459
if (globalFlags != 0) {
14511460
if ((globalFlags & ReachabilityInfo.FlagAccessedClassClass) != 0) {

linker/shared/src/main/scala/org/scalajs/linker/analyzer/Infos.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ object Infos {
115115
final val FlagAccessedImportMeta = 1 << 2
116116
final val FlagUsedExponentOperator = 1 << 3
117117
final val FlagUsedClassSuperClass = 1 << 4
118+
final val FlagNeedsDesugaring = 1 << 5
118119
}
119120

120121
/** Things from a given class that are reached by one method. */
@@ -395,6 +396,7 @@ object Infos {
395396
setFlag(ReachabilityInfo.FlagUsedClassSuperClass)
396397

397398
def addReferencedLinkTimeProperty(linkTimeProperty: LinkTimeProperty): this.type = {
399+
setFlag(ReachabilityInfo.FlagNeedsDesugaring)
398400
linkTimeProperties.append((linkTimeProperty.name, linkTimeProperty.tpe))
399401
this
400402
}

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,9 +1260,8 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
12601260

12611261
def test(tree: Tree): Boolean = tree match {
12621262
// Atomic expressions
1263-
case _: Literal => true
1264-
case _: JSNewTarget => true
1265-
case _: LinkTimeProperty => true
1263+
case _: Literal => true
1264+
case _: JSNewTarget => true
12661265

12671266
// Vars (side-effect free, pure if immutable)
12681267
case VarRef(name) =>
@@ -2811,11 +2810,6 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {
28112810
case AsInstanceOf(expr, tpe) =>
28122811
extractWithGlobals(genAsInstanceOf(transformExprNoChar(expr), tpe))
28132812

2814-
case prop: LinkTimeProperty =>
2815-
transformExpr(
2816-
config.coreSpec.linkTimeProperties.transformLinkTimeProperty(prop),
2817-
preserveChar)
2818-
28192813
// Transients
28202814

28212815
case Transient(Cast(expr, tpe)) =>

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

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,6 @@ private class FunctionEmitter private (
550550
case t: Match => genMatch(t, expectedType)
551551
case t: Debugger => VoidType // ignore
552552
case t: Skip => VoidType
553-
case t: LinkTimeProperty => genLinkTimeProperty(t)
554553

555554
// JavaScript expressions
556555
case t: JSNew => genJSNew(t)
@@ -590,7 +589,7 @@ private class FunctionEmitter private (
590589
// Transients (only generated by the optimizer)
591590
case t: Transient => genTransient(t)
592591

593-
case _: JSSuperConstructorCall =>
592+
case _:JSSuperConstructorCall | _:LinkTimeProperty =>
594593
throw new AssertionError(s"Invalid tree: $tree")
595594
}
596595

@@ -2649,12 +2648,6 @@ private class FunctionEmitter private (
26492648
ClassType(boxClassName, nullable = false)
26502649
}
26512650

2652-
private def genLinkTimeProperty(tree: LinkTimeProperty): Type = {
2653-
val lit = ctx.coreSpec.linkTimeProperties.transformLinkTimeProperty(tree)
2654-
genLiteral(lit, lit.tpe)
2655-
lit.tpe
2656-
}
2657-
26582651
private def genJSNew(tree: JSNew): Type = {
26592652
val JSNew(ctor, args) = tree
26602653

linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,8 @@ private final class ClassDefChecker(classDef: ClassDef,
851851
}
852852

853853
case LinkTimeProperty(name) =>
854+
if (postBaseLinker)
855+
reportError(i"Illegal link-time property '$name' post base linker")
854856

855857
// JavaScript expressions
856858

linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -575,8 +575,6 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter,
575575
typecheckAny(expr, env)
576576
checkIsAsInstanceTargetType(tpe)
577577

578-
case LinkTimeProperty(name) =>
579-
580578
// JavaScript expressions
581579

582580
case JSNew(ctor, args) =>
@@ -755,7 +753,8 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter,
755753
typecheck(elem, env)
756754
}
757755

758-
case _:RecordSelect | _:RecordValue | _:Transient | _:JSSuperConstructorCall =>
756+
case _:RecordSelect | _:RecordValue | _:Transient |
757+
_:JSSuperConstructorCall | _:LinkTimeProperty =>
759758
reportError("invalid tree")
760759
}
761760
}

0 commit comments

Comments
 (0)