8000 Add a desugaring pass between the base linker and the optimizer. by sjrd · Pull Request #5101 · scala-js/scala-js · GitHub
[go: up one dir, main page]

Skip to content

Add a desugaring 8000 pass between the base linker and the optimizer. #5101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import java.util.concurrent.ConcurrentHashMap
import scala.util.matching.Regex

object ScalaJSVersions extends VersionChecks(
current = "1.18.3-SNAPSHOT",
current = "1.19.0-SNAPSHOT",
binaryEmitted = "1.18"
)

Expand Down
22 changes: 14 additions & 8 deletions ir/shared/src/main/scala/org/scalajs/ir/Transformers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -234,14 +234,8 @@ object Transformers {
case jsMethodDef: JSMethodDef =>
transformJSMethodDef(jsMethodDef)

case JSPropertyDef(flags, name, getterBody, setterArgAndBody) =>
JSPropertyDef(
flags,
transform(name),
transformTreeOpt(getterBody),
setterArgAndBody.map { case (arg, body) =>
(arg, transform(body))
})(Unversioned)(jsMethodPropDef.pos)
case jsPropertyDef: JSPropertyDef =>
transformJSPropertyDef(jsPropertyDef)
}
}

Expand All @@ -251,6 +245,18 @@ object Transformers {
jsMethodDef.optimizerHints, Unversioned)(jsMethodDef.pos)
}

def transformJSPropertyDef(jsPropertyDef: JSPropertyDef): JSPropertyDef = {
val JSPropertyDef(flags, name, getterBody, setterArgAndBody) = jsPropertyDef
JSPropertyDef(
flags,
transform(name),
transformTreeOpt(getterBody),
setterArgAndBody.map { case (arg, body) =>
(arg, transform(body))
}
)(Unversioned)(jsPropertyDef.pos)
}

def transformJSConstructorBody(body: JSConstructorBody): JSConstructorBody = {
implicit val pos = body.pos

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ object Analysis {
def methodInfos(
namespace: MemberNamespace): scala.collection.Map[MethodName, MethodInfo]

def anyJSMemberNeedsDesugaring: Boolean

def displayName: String = className.nameString
}

Expand All @@ -103,6 +105,7 @@ object Analysis {
def instantiatedSubclasses: scala.collection.Seq[ClassInfo]
def nonExistent: Boolean
def syntheticKind: MethodSyntheticKind
def needsDesugaring: Boolean

def displayName: String = methodName.displayName

Expand Down Expand Up @@ -161,6 +164,7 @@ object Analysis {
def owningClass: ClassName
def staticDependencies: scala.collection.Set[ClassName]
def externalDependencies: scala.collection.Set[String]
def needsDesugaring: Boolean
}

sealed trait Error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,9 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean,
val publicMethodInfos: mutable.Map[MethodName, MethodInfo] =
methodInfos(MemberNamespace.Public)

def anyJSMemberNeedsDesugaring: Boolean =
data.jsMethodProps.exists(info => (info.globalFlags & ReachabilityInfo.FlagNeedsDesugaring) != 0)

def lookupAbstractMethod(methodName: MethodName): MethodInfo = {
val candidatesIterator = for {
ancestor <- ancestors.iterator
Expand Down Expand Up @@ -1285,6 +1288,9 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean,
def isDefaultBridge: Boolean =
syntheticKind.isInstanceOf[MethodSyntheticKind.DefaultBridge]

def needsDesugaring: Boolean =
(data.globalFlags & ReachabilityInfo.FlagNeedsDesugaring) != 0

/** Throws MatchError if `!isDefaultBridge`. */
def defaultBridgeTarget: ClassName = (syntheticKind: @unchecked) match {
case MethodSyntheticKind.DefaultBridge(target) => target
Expand Down Expand Up @@ -1367,6 +1373,9 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean,
def staticDependencies: scala.collection.Set[ClassName] = _staticDependencies.keySet
def externalDependencies: scala.collection.Set[String] = _externalDependencies.keySet

def needsDesugaring: Boolean =
(data.reachability.globalFlags & ReachabilityInfo.FlagNeedsDesugaring) != 0

def reach(): Unit = followReachabilityInfo(data.reachability, this)(FromExports)
}

Expand Down Expand Up @@ -1441,7 +1450,7 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean,
}
}

val globalFlags = data.globalFlags
val globalFlags = data.globalFlags & ~ReachabilityInfo.FlagNeedsDesugaring

if (globalFlags != 0) {
if ((globalFlags & ReachabilityInfo.FlagAccessedClassClass) != 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ object Infos {
final val FlagAccessedImportMeta = 1 << 2
final val FlagUsedExponentOperator = 1 << 3
final val FlagUsedClassSuperClass = 1 << 4
final val FlagNeedsDesugaring = 1 << 5
}

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

def addReferencedLinkTimeProperty(linkTimeProperty: LinkTimeProperty): this.type = {
setFlag(ReachabilityInfo.FlagNeedsDesugaring)
linkTimeProperties.append((linkTimeProperty.name, linkTimeProperty.tpe))
this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1260,9 +1260,8 @@ private[emitter] class FunctionEmitter(sjsGen: SJSGen) {

def test(tree: Tree): Boolean = tree match {
// Atomic expressions
case _: Literal => true
case _: JSNewTarget => true
case _: LinkTimeProperty => true
case _: Literal => true
case _: JSNewTarget => true

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

case prop: LinkTimeProperty =>
transformExpr(
config.coreSpec.linkTimeProperties.transformLinkTimeProperty(prop),
preserveChar)

// Transients

case Transient(Cast(expr, tpe)) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ object DerivedClasses {
staticDependencies = Set.empty,
externalDependencies = Set.empty,
dynamicDependencies = Set.empty,
desugaringRequirements = LinkedClass.DesugaringRequirements.Empty,
clazz.version
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,6 @@ private class FunctionEmitter private (
case t: Match => genMatch(t, expectedType)
case t: Debugger => VoidType // ignore
case t: Skip => VoidType
case t: LinkTimeProperty => genLinkTimeProperty(t)

// JavaScript expressions
case t: JSNew => genJSNew(t)
Expand Down Expand Up @@ -590,7 +589,7 @@ private class FunctionEmitter private (
// Transients (only generated by the optimizer)
case t: Transient => genTransient(t)

case _: JSSuperConstructorCall =>
case _:JSSuperConstructorCall | _:LinkTimeProperty =>
throw new AssertionError(s"Invalid tree: $tree")
}

Expand Down Expand Up @@ -2649,12 +2648,6 @@ private class FunctionEmitter private (
ClassType(boxClassName, nullable = false)
}

private def genLinkTimeProperty(tree: LinkTimeProperty): Type = {
val lit = ctx.coreSpec.linkTimeProperties.transformLinkTimeProperty(tree)
genLiteral(lit, lit.tpe)
lit.tpe
}

private def genJSNew(tree: JSNew): Type = {
val JSNew(ctor, args) = tree

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ sealed abstract class CheckingPhase
object CheckingPhase {
case object Compiler extends CheckingPhase
case object BaseLinker extends CheckingPhase
case object Desugarer extends CheckingPhase
case object Optimizer extends CheckingPhase
}
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,8 @@ private final class ClassDefChecker(classDef: ClassDef,
}

case LinkTimeProperty(name) =>
if (!featureSet.supports(FeatureSet.LinkTimeProperty))
reportError(i"Illegal link-time property '$name' after desugaring")

// JavaScript expressions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,20 @@ private[checker] object FeatureSet {

// Individual features

/** The `LinkTimeProperty` IR node. */
val LinkTimeProperty = new FeatureSet(1 << 0)

/** Optional constructors in module classes and JS classes. */
val OptionalConstructors = new FeatureSet(1 << 0)
val OptionalConstructors = new FeatureSet(1 << 1)

/** Explicit reflective proxy definitions. */
val ReflectiveProxies = new FeatureSet(1 << 1)
val ReflectiveProxies = new FeatureSet(1 << 2)

/** Transients that are the result of optimizations. */
val OptimizedTransients = new FeatureSet(1 << 2)
val OptimizedTransients = new FeatureSet(1 << 3)

/** Records and record types. */
val Records = new FeatureSet(1 << 3)
val Records = new FeatureSet(1 << 4)

/** Relaxed constructor discipline.
*
Expand All @@ -55,22 +58,31 @@ private[checker] object FeatureSet {
* - `this.x = ...` assignments before the delegate call can assign super class fields.
* - `StoreModule` can be anywhere, or not be there at all.
*/
val RelaxedCtorBodies = new FeatureSet(1 << 4)
val RelaxedCtorBodies = new FeatureSet(1 << 5)

// Common sets

/** Features introduced by the base linker. */
private val Linked =
OptionalConstructors | ReflectiveProxies

/** Features that must be desugared away. */
private val NeedsDesugaring =
LinkTimeProperty

/** IR that is only the result of desugaring (currently empty). */
private val Desugared =
Empty

/** IR that is only the result of optimizations. */
private val Optimized =
OptimizedTransients | Records | RelaxedCtorBodies

/** The set of features allowed as output of the given phase. */
def allowedAfter(phase: CheckingPhase): FeatureSet = phase match {
case Compiler => Empty
case BaseLinker => Linked
case Optimizer => Linked | Optimized
case Compiler => NeedsDesugaring
case BaseLinker => Linked | NeedsDesugaring
case Desugarer => Linked | Desugared
case Optimizer => Linked | Desugared | Optimized
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter,
typecheckAny(expr, env)
checkIsAsInstanceTargetType(tpe)

case LinkTimeProperty(name) =>
case LinkTimeProperty(name) if featureSet.supports(FeatureSet.LinkTimeProperty) =>

// JavaScript expressions

Expand Down Expand Up @@ -760,7 +760,8 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter,
typecheck(elem, env)
}

case _:RecordSelect | _:RecordValue | _:Transient | _:JSSuperConstructorCall =>
case _:RecordSelect | _:RecordValue | _:Transient |
_:JSSuperConstructorCall | _:LinkTimeProperty =>
reportError("invalid tree")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,17 +129,20 @@ private[frontend] object BaseLinker {
classInfo.isAnySubclassInstantiated
}

val methods = classDef.methods.filter { m =>
val methodInfo =
classInfo.methodInfos(m.flags.namespace)(m.methodName)

val reachable = methodInfo.isReachable
assert(m.body.isDefined || !reachable,
s"The abstract method ${classDef.name.name}.${m.methodName} " +
"is reachable.")

reachable
}
// Will stay empty for most classes
var desugaringRequirements = LinkedClass.DesugaringRequirements.Empty

val methods: List[MethodDef] = classDef.methods.iterator
.map(m => m -> classInfo.methodInfos(m.flags.namespace)(m.methodName))
.filter(_._2.isReachable)
.map { case (m, info) =>
assert(m.body.isDefined,
s"The abstract method ${classDef.name.name}.${m.methodName} is reachable.")
if (info.needsDesugaring)
desugaringRequirements = desugaringRequirements.addMethod(m.flags.namespace, m.methodName)
m
}
.toList

val jsConstructor =
if (classInfo.isAnySubclassInstantiated) classDef.jsConstructor
Expand All @@ -149,6 +152,9 @@ private[frontend] object BaseLinker {
if (classInfo.isAnySubclassInstantiated) classDef.jsMethodProps
else Nil

if (classInfo.anyJSMemberNeedsDesugaring)
desugaringRequirements = desugaringRequirements.addAnyExportedMember()

val jsNativeMembers = classDef.jsNativeMembers
.filter(m => classInfo.jsNativeMembersUsed.contains(m.name.name))

Expand Down Expand Up @@ -181,6 +187,7 @@ private[frontend] object BaseLinker {
staticDependencies = classInfo.staticDependencies.toSet,
externalDependencies = classInfo.externalDependencies.toSet,
dynamicDependencies = classInfo.dynamicDependencies.toSet,
desugaringRequirements,
version)

val linkedTopLevelExports = for {
Expand All @@ -189,7 +196,8 @@ private[frontend] object BaseLinker {
val infos = analysis.topLevelExportInfos(
(ModuleID(topLevelExport.moduleID), topLevelExport.topLevelExportName))
new LinkedTopLevelExport(classDef.className, topLevelExport,
infos.staticDependencies.toSet, infos.externalDependencies.toSet)
infos.staticDependencies.toSet, infos.externalDependencies.toSet,
needsDesugaring = infos.needsDesugaring)
}

(linkedClass, linkedTopLevelExports)
Expand Down
Loading
0