8000 Merge pull request #29 from scala-wasm/revert-revert-pull · sjrd/scala-js@242e5fc · GitHub
[go: up one dir, main page]

Skip to content

Commit 242e5fc

Browse files
authored
Merge pull request #29 from scala-wasm/revert-revert-pull
Sync upstream
2 parents 59395e7 + fb23c44 commit 242e5fc

File tree

13 files changed

+455
-146
lines changed

13 files changed

+455
-146
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import java.util.concurrent.ConcurrentHashMap
1717
import scala.util.matching.Regex
1818

1919
object ScalaJSVersions extends VersionChecks(
20-
current = "1.19.0-SNAPSHOT",
21-
binaryEmitted = "1.19-SNAPSHOT"
20+
current = "1.19.1-SNAPSHOT",
21+
binaryEmitted = "1.19"
2222
)
2323

2424
/** Helper class to allow for testing of logic. */

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

Lines changed: 263 additions & 7 deletions
+
* }))
Original file line numberDiff line numberDiff line change
@@ -1327,8 +1327,15 @@ object Serializers {
13271327

13281328
case TagDebugger => Debugger()
13291329

1330-
case TagNew => New(readClassName(), readMethodIdent(), readTrees())
1331-
case TagLoadModule => LoadModule(readClassName())
1330+
case TagNew =>
1331+
val tree = New(readClassName(), readMethodIdent(), readTrees())
1332+
if (hacks.useBelow(19))
1333+
anonFunctionNewNodeHackBelow19(tree)
1334+
else
1335+
tree
1336+
1337+
case TagLoadModule =>
1338+
LoadModule(readClassName())
13321339

13331340
case TagStoreModule =>
13341341
if (hacks.useBelow(16)) {
@@ -1649,6 +1656,129 @@ object Serializers {
16491656
UnaryOp(UnaryOp.CheckNotNull, expr)
16501657
}
16511658

1659+
/** Rewrites `New` nodes of `AnonFunctionN`s coming from before 1.19 into `NewLambda` nodes.
1660+
*
1661+
* Before 1.19, the codegen for `scala.FunctionN` lambda was of the following shape:
1662+
* {{{
1663+
* new scala.scalajs.runtime.AnonFunctionN(arrow-lambda<...captures>(...args: any): any = {
1664+
* body
1665+
* })
1666+
* }}}
1667+
*
1668+
* This function rewrites such calls to `NewLambda` nodes, using the new
1669+
* definition of these classes:
1670+
* {{{
1671+
* <newLambda>(scala.scalajs.runtime.AnonFunctionN,
1672+
* apply;Ljava.lang.Object;...;Ljava.lang.Object,
1673+
* any, any, (typed-lambda<...captures>(...args: any): any = {
1674+
* body
1675
1676+
* }}}
1677+
*
1678+
* The rewrite ensures that previously published lambdas get the same
1679+
* optimizations on Wasm as those recompiled with 1.19+.
1680+
*
1681+
* The rewrite also applies to Scala 3's `AnonFunctionXXL` classes, with
1682+
* an additional adaptation of the parameter's type. It rewrites
1683+
* {{{
1684+
* new scala.scalajs.runtime.AnonFunctionXXL(arrow-lambda<...captures>(argArray: any): any = {
1685+
* body
1686+
* })
1687+
* }}}
1688+
* to
1689+
* {{{
1690+
* <newLambda>(scala.scalajs.runtime.AnonFunctionXXL,
1691+
* apply;Ljava.lang.Object[];Ljava.lang.Object,
1692+
* any, any, (typed-lambda<...captures>(argArray: jl.Object[]): any = {
1693+
* newBody
1694+
* }))
1695+
* }}}
1696+
* where `newBody` is `body` transformed to adapt the type of `argArray`
1697+
* everywhere.
1698+
*
1699+
* Tests are in `sbt-plugin/src/sbt-test/linker/anonfunction-compat/`.
1700+
*
1701+
* ---
1702+
*
1703+
* In case the argument is not an arrow-lambda of the expected shape, we
1704+
* use a fallback. This never happens for our published codegens, but
1705+
* could happen for other valid IR. We rewrite
1706+
* {{{
1707+
* new scala.scalajs.runtime.AnonFunctionN(jsFunctionArg)
1708+
* }}}
1709+
* to
1710+
* {{{
1711+
* <newLambda>(scala.scalajs.runtime.AnonFunctionN,
1712+
* apply;Ljava.lang.Object;...;Ljava.lang.Object,
1713+
* any, any, (typed-lambda<f: any = jsFunctionArg>(...args: any): any = {
1714+
* f(...args)
1715+
* }))
1716+
* }}}
1717+
*
1718+
* This code path is not tested in the CI, but can be locally tested by
1719+
* commenting out the `case Closure(...) =>`.
1720+
*/
1721+
private def anonFunctionNewNodeHackBelow19(tree: New): Tree = {
1722+
tree match {
1723+
case New(cls, _, funArg :: Nil) =>
1724+
def makeFallbackTypedClosure(paramTypes: List[Type]): Closure = {
1725+
implicit val pos = funArg.pos
1726+
val fParamDef = ParamDef(LocalIdent(LocalName("f")), NoOriginalName, AnyType, mutable = false)
1727+
val xParamDefs = paramTypes.zipWithIndex.map { case (ptpe, i) =>
1728+
ParamDef(LocalIdent(LocalName(s"x$i")), NoOriginalName, ptpe, mutable = false)
1729+
}
1730+
Closure(ClosureFlags.typed, List(fParamDef), xParamDefs, None, AnyType,
1731+
JSFunctionApply(fParamDef.ref, xParamDefs.map(_.ref)),
1732+
List(funArg))
1733+
}
1734+
1735+
cls match {
1736+
case HackNames.AnonFunctionClass(arity) =>
1737+
val typedClosure = funArg match {
1738+
// The shape produced by our earlier compilers, which we can optimally rewrite
1739+
case Closure(ClosureFlags.arrow, captureParams, params, None, AnyType, body, captureValues)
1740+
if params.lengthCompare(arity) == 0 =>
1741+
Closure(ClosureFlags.typed, captureParams, params, None, AnyType,
1742+
body, captureValues)(funArg.pos)
1743+
1744+
// Fallback for other shapes (theoretically required; dead code in practice)
1745+
case _ =>
1746+
makeFallbackTypedClosure(List.fill(arity)(AnyType))
1747+
}
1748+
1749+
NewLambda(HackNames.anonFunctionDescriptors(arity), typedClosure)(tree.tpe)(tree.pos)
1750+
1751+
case HackNames.AnonFunctionXXLClass =>
1752+
val typedClosure = funArg match {
1753+
// The shape produced by our earlier compilers, which we can optimally rewrite
1754+
case Closure(ClosureFlags.arrow, captureParams, oldParam :: Nil, None, AnyType, body, captureValues) =>
1755+
// Here we need to adapt the type of the parameter from `any` to `jl.Object[]`.
1756+
val newParam = oldParam.copy(ptpe = HackNames.ObjectArrayType)(oldParam.pos)
1757+
val newBody = new Transformers.LocalScopeTransformer {
1758+
override def transform(tree: Tree): Tree = tree match {
1759+
case tree @ VarRef(newParam.name.name) => tree.copy()(newParam.ptpe)(tree.pos)
1760+
case _ => super.transform(tree)
1761+
}
1762+
}.transform(body)
1763+
Closure(ClosureFlags.typed, captureParams, List(newParam), None, AnyType,
1764+
newBody, captureValues)(funArg.pos)
1765+
1766+
// Fallback for other shapes (theoretically required; dead code in practice)
1767+
case _ =>
1768+
makeFallbackTypedClosure(List(HackNames.ObjectArrayType))
1769+
}
1770+
1771+
NewLambda(HackNames.anonFunctionXXLDescriptor, typedClosure)(tree.tpe)(tree.pos)
1772+
1773+
case _ =>
1774+
tree
1775+
}
1776+
1777+
case _ =>
1778+
tree
1779+
}
1780+
}
1781+
16521782
def readTrees(): List[Tree] =
16531783
List.fill(readInt())(readTree())
16541784

@@ -1751,10 +1881,15 @@ object Serializers {
17511881
val jsNativeMembers = jsNativeMembersBuilder.result()
17521882
val componentNativeMembers = componentNativeMembersBuilder.result()
17531883

1754-
ClassDef(name, originalName, kind, jsClassCaptures, superClass, parents,
1884+
val classDef = ClassDef(name, originalName, kind, jsClassCaptures, superClass, parents,
17551885
jsSuperClass, jsNativeLoadSpec, fields, methods, jsConstructor,
17561886
jsMethodProps, jsNativeMembers, componentNativeMembers, topLevelExportDefs)(
17571887
optimizerHints)
1888+
1889+
if (hacks.useBelow(19))
1890+
anonFunctionClassDefHackBelow19(classDef)
1891+
else
1892+
classDef
17581893
}
17591894

17601895
private def jlClassMethodsHackBelow17(methods: List[MethodDef]): List[MethodDef] = {
@@ -2041,6 +2176,89 @@ object Serializers {
20412176
(jsConstructorBuilder.result(), jsMethodPropsBuilder.result())
20422177
}
20432178

2179+
/** Rewrites `scala.scalajs.runtime.AnonFunctionN`s from before 1.19.
2180+
*
2181+
* Before 1.19, these classes were defined as
2182+
* {{{
2183+
* // final in source code
2184+
* class AnonFunctionN extends AbstractFunctionN {
2185+
* val f: any
2186+
* def this(f: any) = {
2187+
* this.f = f;
2188+
* super()
2189+
* }
2190+
* def apply(...args: any): any = f(...args)
2191+
* }
2192+
* }}}
2193+
*
2194+
* Starting with 1.19, they were rewritten to be used as SAM classes for
2195+
* `NewLambda` nodes. The new IR shape is
2196+
* {{{
2197+
* // sealed abstract in source code
2198+
* class AnonFunctionN extends AbstractFunctionN {
2199+
* def this() = super()
2200+
* }
2201+
* }}}
2202+
*
2203+
* This function rewrites those classes to the new shape.
2204+
*
2205+
* The rewrite also applies to Scala 3's `AnonFunctionXXL`.
2206+
*
2207+
* Tests are in `sbt-plugin/src/sbt-test/linker/anonfunction-compat/`.
2208+
*/
2209+
private def anonFunctionClassDefHackBelow19(classDef: ClassDef): ClassDef = {
2210+
import classDef._
2211+
2212+
if (!HackNames.allAnonFunctionClasses.contains(className)) {
2213+
classDef
2214+
} else {
2215+
val newCtor: MethodDef = {
2216+
// Find the old constructor to get its position and version
2217+
val oldCtor = methods.find(_.methodName.isConstructor).getOrElse {
2218+
throw new InvalidIRException(classDef,
2219+
s"Did not find a constructor in ${className.nameString}")
2220+
}
2221+
implicit val pos = oldCtor.pos
2222+
2223+
// constructor def <init>() = this.superClass::<init>()
2224+
MethodDef(
2225+
MemberFlags.empty.withNamespace(MemberNamespace.Constructor),
2226+
MethodIdent(NoArgConstructorName),
2227+
NoOriginalName,
2228+
Nil,
2229+
VoidType,
2230+
Some {
2231+
ApplyStatically(
2232+
ApplyFlags.empty.withConstructor(true),
2233+
This()(ClassType(className, nullable = false)),
2234+
superClass.get.name,
2235+
MethodIdent(NoArgConstructorName),
2236+
Nil
2237+
)(VoidType)
2238+
}
2239+
)(OptimizerHints.empty, oldCtor.version)
2240+
}
2241+
2242+
ClassDef(
2243+
name,
2244+
originalName,
2245+
kind,
2246+
jsClassCaptures,
2247+
superClass,
2248+
interfaces,
2249+
jsSuperClass,
2250+
jsNativeLoadSpec,
2251+
fields = Nil, // throws away the `f` field
2252+
methods = List(newCtor), // throws away the old constructor and `apply` method
2253+
jsConstructor,
2254+
jsMethodProps,
2255+
jsNativeMembers,
2256+
componentNativeMembers,
2257+
topLevelExportDefs
2258+
)(OptimizerHints.empty)(pos) // throws away the `@inline`
2259+
}
2260+
}
2261+
20442262
private def readFieldDef()(implicit pos: Position): FieldDef = {
20452263
val flags = MemberFlags.fromBits(readInt())
20462264
val name = readFieldIdentForEnclosingClass()
@@ -2800,6 +3018,8 @@ object Serializers {
28003018

28013019
/** Names needed for hacks. */
28023020
private object HackNames {
3021+
val AnonFunctionXXLClass =
3022+
ClassName("scala.scalajs.runtime.AnonFunctionXXL") // from the Scala 3 library
28033023
val CloneNotSupportedExceptionClass =
28043024
ClassName("java.lang.CloneNotSupportedException")
28053025
val SystemModule: ClassName =
@@ -2809,14 +3029,50 @@ object Serializers {
28093029
val ReflectArrayModClass =
28103030
ClassName("java.lang.reflect.Array$")
28113031

3032+
val ObjectArrayType = ArrayType(ArrayTypeRef(ObjectRef, 1), nullable = true)
3033+
3034+
private val applySimpleName = SimpleMethodName("apply")
3035+
28123036
val cloneName: MethodName =
2813-
MethodName("clone", Nil, ClassRef(ObjectClass))
3037+
MethodName("clone", Nil, ObjectRef)
28143038
val identityHashCodeName: MethodName =
2815-
MethodName("identityHashCode", List(ClassRef(ObjectClass)), IntRef)
3039+
MethodName("identityHashCode", List(ObjectRef), IntRef)
28163040
val newInstanceSingleName: MethodName =
2817-
MethodName("newInstance", List(ClassRef(ClassClass), IntRef), ClassRef(ObjectClass))
3041+
MethodName("newInstance", List(ClassRef(ClassClass), IntRef), ObjectRef)
28183042
val newInstanceMultiName: MethodName =
2819-
MethodName("newInstance", List(ClassRef(ClassClass), ArrayTypeRef(IntRef, 1)), ClassRef(ObjectClass))
3043+
MethodName("newInstance", List(ClassRef(ClassClass), ArrayTypeRef(IntRef, 1)), ObjectRef)
3044+
3045+
private val anonFunctionArities: Map[ClassName, Int] =
3046+
(0 to 22).map(arity => ClassName(s"scala.scalajs.runtime.AnonFunction$arity") -> arity).toMap
3047+
val allAnonFunctionClasses: Set[ClassName] =
3048+
anonFunctionArities.keySet + AnonFunctionXXLClass
3049+
3050+
object AnonFunctionClass {
3051+
def unapply(cls: ClassName): Option[Int] =
3052+
anonFunctionArities.get(cls)
3053+
}
3054+
3055+
lazy val anonFunctionDescriptors: IndexedSeq[NewLambda.Descriptor] = {
3056+
anonFunctionArities.toIndexedSeq.sortBy(_._2).map { case (className, arity) =>
3057+
NewLambda.Descriptor(
3058+
superClass = className,
3059+
interfaces = Nil,
3060+
methodName = MethodName(applySimpleName, List.fill(arity)(ObjectRef), ObjectRef),
3061+
paramTypes = List.fill(arity)(AnyType),
3062+
resultType = AnyType
3063+
)
3064+
}
3065+
}
3066+
3067+
lazy val anonFunctionXXLDescriptor: NewLambda.Descriptor = {
3068+
NewLambda.Descriptor(
3069+
superClass = AnonFunctionXXLClass,
3070+
interfaces = Nil,
3071+
methodName = MethodName(applySimpleName, List(ObjectArrayType.arrayTypeRef), ObjectRef),
3072+
paramTypes = List(ObjectArrayType),
3073+
resultType = AnyType
3074+
)
3075+
}
28203076
}
28213077

28223078
private class OptionBuilder[T] {

0 commit comments

Comments
 (0)
0