Skip to content

Commit a98d50a

Browse files
committed
Pass desugaring info as a dedicated field of LinkedClass.
1 parent 808638e commit a98d50a

File tree

7 files changed

+80
-182
lines changed

7 files changed

+80
-182
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ object DerivedClasses {
146146
staticDependencies = Set.empty,
147147
externalDependencies = Set.empty,
148148
dynamicDependencies = Set.empty,
149+
desugaringInfo = LinkedClass.DesugaringInfo.Empty,
149150
clazz.version
150151
)
151152
}

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

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ package org.scalajs.linker.checker
1515
import org.scalajs.ir.Trees.Transient
1616

1717
import org.scalajs.linker.checker.CheckingPhase._
18-
import org.scalajs.linker.frontend.Desugarer.{Transients => DesugarerTransientNodes}
1918

2019
/** A set of conditional IR features that the checkers can accept.
2120
*
@@ -48,14 +47,11 @@ private[checker] object FeatureSet {
4847
/** Reflective proxy definitions. */
4948
val ReflectiveProxies = new FeatureSet(1 << 2)
5049

51-
/** The `Desugarer.Transients.Desugar` transient. */
52-
val DesugarTransient = new FeatureSet(1 << 3)
53-
5450
/** Transients that are the result of optimizations. */
55-
val OptimizedTransients = new FeatureSet(1 << 4)
51+
val OptimizedTransients = new FeatureSet(1 << 3)
5652

5753
/** Records and record types. */
58-
val Records = new FeatureSet(1 << 5)
54+
val Records = new FeatureSet(1 << 4)
5955

6056
/** Relaxed constructor discipline.
6157
*
@@ -64,7 +60,7 @@ private[checker] object FeatureSet {
6460
* - `this.x = ...` assignments before the delegate call can assign super class fields.
6561
* - `StoreModule` can be anywhere, or not be there at all.
6662
*/
67-
val RelaxedCtorBodies = new FeatureSet(1 << 6)
63+
val RelaxedCtorBodies = new FeatureSet(1 << 5)
6864

6965
// Common sets
7066

@@ -87,7 +83,7 @@ private[checker] object FeatureSet {
8783
/** The set of features supported (as input) by the given phase. */
8884
def supportedBy(phase: CheckingPhase): FeatureSet = phase match {
8985
case BaseLinker => NeedsDesugaring
90-
case Desugarer => Linked | NeedsDesugaring | DesugarTransient
86+
case Desugarer => Linked | NeedsDesugaring
9187
case Emitter(false) => Linked | Desugared
9288
case Emitter(true) | Optimizer => Linked | Desugared | Optimized
9389
}
@@ -97,10 +93,6 @@ private[checker] object FeatureSet {
9793
* We are not very specific here. By default, we consider that the transient
9894
* is the result of optimizations.
9995
*/
100-
def ofTransient(transientValue: Transient.Value): FeatureSet = transientValue match {
101-
case _: DesugarerTransientNodes.Desugar =>
102-
DesugarTransient
103-
case _ =>
104-
OptimizedTransients
105-
}
96+
def ofTransient(transientValue: Transient.Value): FeatureSet =
97+
OptimizedTransients
10698
}

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

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -720,16 +720,10 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter,
720720
}
721721

722722
case Transient(transient) if featureSet.supports(FeatureSet.ofTransient(transient)) =>
723-
transient match {
724-
case Desugarer.Transients.Desugar(body) =>
725-
// Stricter type check for that particular transient
726-
typecheckExpect(body, env, tree.tpe)
727-
case _ =>
728-
// No precise rules, but at least check that its children type-check on their own
729-
transient.traverse(new Traversers.Traverser {
730-
override def traverse(tree: Tree): Unit = typecheck(tree, env)
731-
})
732-
}
723+
// No precise rules, but at least check that its children type-check on their own
724+
transient.traverse(new Traversers.Traverser {
725+
override def traverse(tree: Tree): Unit = typecheck(tree, env)
726+
})
733727

734728
case RecordSelect(record, SimpleFieldIdent(fieldName))
735729
if featureSet.supports(FeatureSet.Records) =>

linker/shared/src/main/scala/org/scalajs/linker/frontend/BaseLinker.scala

Lines changed: 19 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -135,28 +135,26 @@ private[frontend] object BaseLinker {
135135
classInfo.isAnySubclassInstantiated
136136
}
137137

138+
// var of immutable data type because it will stay empty for most classes
139+
var methodsDesugaring = LinkedClass.DesugaringInfo.Empty.methods
140+
138141
val methods: List[MethodDef] = classDef.methods.iterator
139142
.map(m => m -> classInfo.methodInfos(m.flags.namespace)(m.methodName))
140143
.filter(_._2.isReachable)
141144
.map { case (m, info) =>
142145
assert(m.body.isDefined,
143146
s"The abstract method ${classDef.name.name}.${m.methodName} is reachable.")
144-
if (!info.needsDesugaring)
145-
m
146-
else
147-
markNeedsDesugaring(m)
147+
if (info.needsDesugaring) {
148+
val namespaceOrdinal = m.flags.namespace.ordinal
149+
methodsDesugaring = methodsDesugaring.updated(namespaceOrdinal,
150+
methodsDesugaring(namespaceOrdinal) + m.methodName)
151+
}
152+
m
148153
}
149154
.toList
150155

151156
val (jsConstructor, jsMethodProps) = if (classInfo.isAnySubclassInstantiated) {
152-
val anyJSMemberNeedsDesugaring = classInfo.anyJSMemberNeedsDesugaring
153-
154-
if (!anyJSMemberNeedsDesugaring) {
155-
(classDef.jsConstructor, classDef.jsMethodProps)
156-
} else {
157-
(classDef.jsConstructor.map(markNeedsDesugaring(_)),
158-
classDef.jsMethodProps.map(markNeedsDesugaring(_)))
159-
}
157+
(classDef.jsConstructor, classDef.jsMethodProps)
160158
} else {
161159
(None, Nil)
162160
}
@@ -168,6 +166,11 @@ private[frontend] object BaseLinker {
168166

169167
val ancestors = classInfo.ancestors.map(_.className)
170168

169+
val desugaringInfo = new LinkedClass.DesugaringInfo(
170+
methods = methodsDesugaring,
171+
exportedMembers = classInfo.anyJSMemberNeedsDesugaring
172+
)
173+
171174
val linkedClass = new LinkedClass(
172175
classDef.name,
173176
classDef.kind,
@@ -193,65 +196,19 @@ private[frontend] object BaseLinker {
193196
staticDependencies = classInfo.staticDependencies.toSet,
194197
externalDependencies = classInfo.externalDependencies.toSet,
195198
dynamicDependencies = classInfo.dynamicDependencies.toSet,
199+
desugaringInfo,
196200
version)
197201

198202
val linkedTopLevelExports = for {
199203
topLevelExport <- classDef.topLevelExportDefs
200204
} yield {
201205
val infos = analysis.topLevelExportInfos(
202206
(ModuleID(topLevelExport.moduleID), topLevelExport.topLevelExportName))
203-
val maybeMarked =
204-
if (!infos.needsDesugaring) topLevelExport
205-
else markNeedsDesugaring(topLevelExport)
206-
new LinkedTopLevelExport(classDef.className, maybeMarked,
207-
infos.staticDependencies.toSet, infos.externalDependencies.toSet)
207+
new LinkedTopLevelExport(classDef.className, topLevelExport,
208+
infos.staticDependencies.toSet, infos.externalDependencies.toSet,
209+
needsDesugaring = infos.needsDesugaring)
208210
}
209211

210212
(linkedClass, linkedTopLevelExports)
211213
}
212-
213-
private def markNeedsDesugaring(methodDef: MethodDef): MethodDef = {
214-
import methodDef._
215-
MethodDef(flags, name, originalName, args, resultType,
216-
Some(makeDesugarNode(body.get)))(
217-
optimizerHints, version)(
218-
pos)
219-
}
220-
221-
private def markNeedsDesugaring(jsConstructorDef: JSConstructorDef): JSConstructorDef = {
222-
import jsConstructorDef._
223-
val newBody = JSConstructorBody(makeDesugarNode(Skip()(pos)) :: body.beforeSuper,
224-
body.superCall, body.afterSuper)
225-
JSConstructorDef(flags, args, restParam, newBody)(optimizerHints, version)(pos)
226-
}
227-
228-
private def markNeedsDesugaring(jsMethodPropDef: JSMethodPropDef): JSMethodPropDef = {
229-
jsMethodPropDef match {
230-
case jsMethodDef: JSMethodDef =>
231-
import jsMethodDef._
232-
JSMethodDef(flags, name, args, restParam, makeDesugarNode(body))(
233-
optimizerHints, version)(pos)
234-
235-
case jsPropDef: JSPropertyDef =>
236-
import jsPropDef._
237-
JSPropertyDef(flags, name,
238-
getterBody.map(makeDesugarNode(_)),
239-
setterArgAndBody.map(t => (t._1 -> makeDesugarNode(t._2))))(
240-
version)
241-
}
242-
}
243-
244-
private def markNeedsDesugaring(exportDef: TopLevelExportDef): TopLevelExportDef = {
245-
exportDef match {
246-
case TopLevelMethodExportDef(exportName, jsMethodDef) =>
247-
TopLevelMethodExportDef(exportName,
248-
markNeedsDesugaring(jsMethodDef).asInstanceOf[JSMethodDef])(
249-
exportDef.pos)
250-
case _ =>
251-
exportDef
252-
}
253-
}
254-
255-
private def makeDesugarNode(body: Tree): Tree =
256-
Transient(Desugarer.Transients.Desugar(body))(body.pos)
257214
}

linker/shared/src/main/scala/org/scalajs/linker/frontend/Desugarer.scala

Lines changed: 28 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import org.scalajs.ir.{Position, Version}
3232
/** Desugars a linking unit. */
3333
final class Desugarer(config: CommonPhaseConfig, checkIRFor: Option[CheckingPhase]) {
3434
import Desugarer._
35-
import Transients._
3635

3736
private val desugarTransformer = new DesugarTransformer(config.coreSpec)
3837

@@ -62,16 +61,19 @@ final class Desugarer(config: CommonPhaseConfig, checkIRFor: Option[CheckingPhas
6261
import linkedClass._
6362

6463
val newMethods = methods.mapConserve { method =>
65-
desugarTransformer.transformMethodDef(method)
64+
if (!desugaringInfo.methods(method.flags.namespace.ordinal).contains(method.methodName))
65+
method
66+
else
67+
desugarTransformer.transformMethodDef(method)
6668
}
6769

68-
val newJSConstructorDef = jsConstructorDef.mapConserve { jsCtor =>
69-
desugarTransformer.transformJSConstructorDef(jsCtor)
70-
}
70+
val newJSConstructorDef =
71+
if (!desugaringInfo.exportedMembers) jsConstructorDef
72+
else jsConstructorDef.map(desugarTransformer.transformJSConstructorDef(_))
7173

72-
val newExportedMembers = exportedMembers.mapConserve { jsMethodProp =>
73-
desugarTransformer.transformJSMethodPropDef(jsMethodProp)
74-
}
74+
val newExportedMembers =
75+
if (!desugaringInfo.exportedMembers) exportedMembers
76+
else exportedMembers.map(desugarTransformer.transformJSMethodPropDef(_))
7577

7678
if ((newMethods eq methods) && (newJSConstructorDef eq jsConstructorDef) &&
7779
(newExportedMembers eq exportedMembers)) {
@@ -102,50 +104,31 @@ final class Desugarer(config: CommonPhaseConfig, checkIRFor: Option[CheckingPhas
102104
staticDependencies,
103105
externalDependencies,
104106
dynamicDependencies,
107+
LinkedClass.DesugaringInfo.Empty,
105108
version
106109
)
107110
}
108111
}
109112

110113
private def desugarTopLevelExport(tle: LinkedTopLevelExport): LinkedTopLevelExport = {
111114
import tle._
112-
val newTree = desugarTransformer.transformTopLevelExportDef(tree)
113-
if (newTree eq tree)
115+
if (!tle.needsDesugaring) {
114116
tle
115-
else
116-
new LinkedTopLevelExport(owningClass, newTree, staticDependencies, externalDependencies)
117+
} else {
118+
val newTree = desugarTransformer.transformTopLevelExportDef(tree)
119+
new LinkedTopLevelExport(owningClass, newTree, staticDependencies,
120+
externalDependencies, needsDesugaring = false)
121+
}
117122
}
118123
}
119124

120125
private[linker] object Desugarer {
121126

122-
object Transients {
123-
final case class Desugar(body: Tree) extends Transient.Value {
124-
val tpe = body.tpe
125-
126-
def traverse(traverser: Traverser): Unit =
127-
traverser.traverse(body)
128-
129-
def transform(transformer: Transformer)(implicit pos: Position): Tree =
130-
Transient(Desugar(transformer.transform(body)))
131-
132-
def printIR(out: IRTreePrinter): Unit = {
133-
out.print("desugar ")
134-
out.print(body)
135-
}
136-
}
137-
}
138-
139-
import Transients._
140-
141127
private final class DesugarTransformer(coreSpec: CoreSpec)
142128
extends ClassTransformer {
143129

144130
override def transform(tree: Tree): Tree = {
145131
tree match {
146-
case Transient(Desugar(body)) =>
147-
transform(body)
148-
149132
case prop: LinkTimeProperty =>
150133
coreSpec.linkTimeProperties.transformLinkTimeProperty(prop)
151134

@@ -160,84 +143,35 @@ private[linker] object Desugarer {
160143
*/
161144

162145
override def transformMethodDef(methodDef: MethodDef): MethodDef = {
163-
methodDef.body match {
164-
case Some(Transient(Desugar(_))) =>
165-
val newMethodDef = super.transformMethodDef(methodDef)
166-
newMethodDef.copy()(newMethodDef.optimizerHints, methodDef.version)(newMethodDef.pos)
167-
case _ =>
168-
methodDef
169-
}
146+
val newMethodDef = super.transformMethodDef(methodDef)
147+
newMethodDef.copy()(newMethodDef.optimizerHints, methodDef.version)(newMethodDef.pos)
170148
}
171149

172150
override def transformJSConstructorDef(jsConstructor: JSConstructorDef): JSConstructorDef = {
173-
/* We cheat here. A JSConstructorBody has a mandatory super call,
174-
* statically separated from the other statements. Therefore we cannot
175-
* wrap the entire body with a `Desugar` node. Instead, we put a
176-
* `Desugar` node at the beginning of the body, but it still signals that
177-
* we should transform the whole body.
178-
*/
179-
jsConstructor.body.beforeSuper match {
180-
case Transient(Desugar(_)) :: _ =>
181-
val newJSConstructor = super.transformJSConstructorDef(jsConstructor)
182-
newJSConstructor.copy()(newJSConstructor.optimizerHints, jsConstructor.version)(
183-
newJSConstructor.pos)
184-
case _ =>
185-
jsConstructor
186-
}
151+
val newJSConstructor = super.transformJSConstructorDef(jsConstructor)
152+
newJSConstructor.copy()(newJSConstructor.optimizerHints, jsConstructor.version)(
153+
newJSConstructor.pos)
187154
}
188155

189156
override def transformJSMethodDef(jsMethodDef: JSMethodDef): JSMethodDef = {
190-
jsMethodDef.body match {
191-
case Transient(Desugar(_)) =>
192-
val newJSMethodDef = super.transformJSMethodDef(jsMethodDef)
193-
newJSMethodDef.copy()(newJSMethodDef.optimizerHints, jsMethodDef.version)(
194-
newJSMethodDef.pos)
195-
case _ =>
196-
jsMethodDef
197-
}
157+
val newJSMethodDef = super.transformJSMethodDef(jsMethodDef)
158+
newJSMethodDef.copy()(newJSMethodDef.optimizerHints, jsMethodDef.version)(
159+
newJSMethodDef.pos)
198160
}
199161

200162
override def transformJSPropertyDef(jsPropertyDef: JSPropertyDef): JSPropertyDef = {
201-
val needsDesugaring = jsPropertyDef match {
202-
case JSPropertyDef(_, _, Some(Transient(Desugar(_))), _) => true
203-
case JSPropertyDef(_, _, _, Some((_, Transient(Desugar(_))))) => true
204-
case _ => false
205-
}
206-
if (needsDesugaring) {
207-
val newJSPropertyDef = super.transformJSPropertyDef(jsPropertyDef)
208-
newJSPropertyDef.copy()(jsPropertyDef.version)(newJSPropertyDef.pos)
209-
} else {
210-
jsPropertyDef
211-
}
163+
val newJSPropertyDef = super.transformJSPropertyDef(jsPropertyDef)
164+
newJSPropertyDef.copy()(jsPropertyDef.version)(newJSPropertyDef.pos)
212165
}
213166

214167
override def transformTopLevelExportDef(exportDef: TopLevelExportDef): TopLevelExportDef = {
215168
exportDef match {
216169
case TopLevelMethodExportDef(exportName, jsMethodDef) =>
217170
val newJSMethodDef = transformJSMethodDef(jsMethodDef)
218-
if (newJSMethodDef eq jsMethodDef)
219-
exportDef
220-
else
221-
TopLevelMethodExportDef(exportName, newJSMethodDef)(exportDef.pos)
222-
171+
TopLevelMethodExportDef(exportName, newJSMethodDef)(exportDef.pos)
223172
case _ =>
224173
exportDef
225174
}
226175
}
227176
}
228-
229-
private implicit class OptionMapConserve[+A <: AnyRef](private val self: Option[A])
230-
extends AnyVal {
231-
232-
def mapConserve[B >: A <: AnyRef](f: A => B): Option[B] = self match {
233-
case None =>
234-
None
235-
case Some(value) =>
236-
val newValue = f(value)
237-
if (newValue eq value)
238-
self
239-
else
240-
Some(newValue)
241-
}
242-
}
243177
}

0 commit comments

Comments
 (0)