From 9481522bcd4251bda3ccee0cc57863826b7738f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 30 Jan 2025 15:43:20 +0100 Subject: [PATCH] Rewrite old IR with `AnonFunctionN` references to use `NewLambda`. We change the definition of `AnonFunctionN`s from `final class`es with concrete `apply` methods to `sealed abstract` classes. We rewrite `New` nodes to them to use `NewLambda` instead. See comments in the IR deserializer for more details on the performed rewrites. The rewritten `NewLambda` must target classes with the same identity (and not directly `AbstractFunctionN`) because the class names can be used in *types* somewhere else in the IR. The result of the `NewLambda` nodes must stay compatible with those types. This change ensures that even old libraries benefit from all the new optimizations for lambdas on Wasm, and that they can participate in a call chain between `js.async` and an orphan `js.await`. --- .../scala/org/scalajs/ir/Serializers.scala | 269 +++++++++++++++++- .../scala/scalajs/runtime/AnonFunctions.scala | 135 +++------ project/BinaryIncompatibilities.scala | 4 + .../linker/anonfunction-compat/build.sbt | 61 ++++ .../project/build.properties | 1 + .../anonfunction-compat/project/build.sbt | 5 + .../scala2-old-compiler-new-lib/Main.scala | 9 + .../scala2-old-compiler-old-lib/Main.scala | 9 + .../scala3-old-compiler-new-lib/Main.scala | 18 ++ .../scala3-old-compiler-old-lib/Main.scala | 18 ++ .../sbt-test/linker/anonfunction-compat/test | 25 ++ 11 files changed, 455 insertions(+), 99 deletions(-) create mode 100644 sbt-plugin/src/sbt-test/linker/anonfunction-compat/build.sbt create mode 100644 sbt-plugin/src/sbt-test/linker/anonfunction-compat/project/build.properties create mode 100644 sbt-plugin/src/sbt-test/linker/anonfunction-compat/project/build.sbt create mode 100644 sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala2-old-compiler-new-lib/Main.scala create mode 100644 sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala2-old-compiler-old-lib/Main.scala create mode 100644 sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala3-old-compiler-new-lib/Main.scala create mode 100644 sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala3-old-compiler-old-lib/Main.scala create mode 100644 sbt-plugin/src/sbt-test/linker/anonfunction-compat/test diff --git a/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala b/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala index 43abb124ae..7cc64e28e1 100644 --- a/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala +++ b/ir/shared/src/main/scala/org/scalajs/ir/Serializers.scala @@ -1226,8 +1226,15 @@ object Serializers { case TagDebugger => Debugger() - case TagNew => New(readClassName(), readMethodIdent(), readTrees()) - case TagLoadModule => LoadModule(readClassName()) + case TagNew => + val tree = New(readClassName(), readMethodIdent(), readTrees()) + if (hacks.useBelow(19)) + anonFunctionNewNodeHackBelow19(tree) + else + tree + + case TagLoadModule => + LoadModule(readClassName()) case TagStoreModule => if (hacks.useBelow(16)) { @@ -1542,6 +1549,129 @@ object Serializers { UnaryOp(UnaryOp.CheckNotNull, expr) } + /** Rewrites `New` nodes of `AnonFunctionN`s coming from before 1.19 into `NewLambda` nodes. + * + * Before 1.19, the codegen for `scala.FunctionN` lambda was of the following shape: + * {{{ + * new scala.scalajs.runtime.AnonFunctionN(arrow-lambda<...captures>(...args: any): any = { + * body + * }) + * }}} + * + * This function rewrites such calls to `NewLambda` nodes, using the new + * definition of these classes: + * {{{ + * (scala.scalajs.runtime.AnonFunctionN, + * apply;Ljava.lang.Object;...;Ljava.lang.Object, + * any, any, (typed-lambda<...captures>(...args: any): any = { + * body + * })) + * }}} + * + * The rewrite ensures that previously published lambdas get the same + * optimizations on Wasm as those recompiled with 1.19+. + * + * The rewrite also applies to Scala 3's `AnonFunctionXXL` classes, with + * an additional adaptation of the parameter's type. It rewrites + * {{{ + * new scala.scalajs.runtime.AnonFunctionXXL(arrow-lambda<...captures>(argArray: any): any = { + * body + * }) + * }}} + * to + * {{{ + * (scala.scalajs.runtime.AnonFunctionXXL, + * apply;Ljava.lang.Object[];Ljava.lang.Object, + * any, any, (typed-lambda<...captures>(argArray: jl.Object[]): any = { + * newBody + * })) + * }}} + * where `newBody` is `body` transformed to adapt the type of `argArray` + * everywhere. + * + * Tests are in `sbt-plugin/src/sbt-test/linker/anonfunction-compat/`. + * + * --- + * + * In case the argument is not an arrow-lambda of the expected shape, we + * use a fallback. This never happens for our published codegens, but + * could happen for other valid IR. We rewrite + * {{{ + * new scala.scalajs.runtime.AnonFunctionN(jsFunctionArg) + * }}} + * to + * {{{ + * (scala.scalajs.runtime.AnonFunctionN, + * apply;Ljava.lang.Object;...;Ljava.lang.Object, + * any, any, (typed-lambda(...args: any): any = { + * f(...args) + * })) + * }}} + * + * This code path is not tested in the CI, but can be locally tested by + * commenting out the `case Closure(...) =>`. + */ + private def anonFunctionNewNodeHackBelow19(tree: New): Tree = { + tree match { + case New(cls, _, funArg :: Nil) => + def makeFallbackTypedClosure(paramTypes: List[Type]): Closure = { + implicit val pos = funArg.pos + val fParamDef = ParamDef(LocalIdent(LocalName("f")), NoOriginalName, AnyType, mutable = false) + val xParamDefs = paramTypes.zipWithIndex.map { case (ptpe, i) => + ParamDef(LocalIdent(LocalName(s"x$i")), NoOriginalName, ptpe, mutable = false) + } + Closure(ClosureFlags.typed, List(fParamDef), xParamDefs, None, AnyType, + JSFunctionApply(fParamDef.ref, xParamDefs.map(_.ref)), + List(funArg)) + } + + cls match { + case HackNames.AnonFunctionClass(arity) => + val typedClosure = funArg match { + // The shape produced by our earlier compilers, which we can optimally rewrite + case Closure(ClosureFlags.arrow, captureParams, params, None, AnyType, body, captureValues) + if params.lengthCompare(arity) == 0 => + Closure(ClosureFlags.typed, captureParams, params, None, AnyType, + body, captureValues)(funArg.pos) + + // Fallback for other shapes (theoretically required; dead code in practice) + case _ => + makeFallbackTypedClosure(List.fill(arity)(AnyType)) + } + + NewLambda(HackNames.anonFunctionDescriptors(arity), typedClosure)(tree.tpe)(tree.pos) + + case HackNames.AnonFunctionXXLClass => + val typedClosure = funArg match { + // The shape produced by our earlier compilers, which we can optimally rewrite + case Closure(ClosureFlags.arrow, captureParams, oldParam :: Nil, None, AnyType, body, captureValues) => + // Here we need to adapt the type of the parameter from `any` to `jl.Object[]`. + val newParam = oldParam.copy(ptpe = HackNames.ObjectArrayType)(oldParam.pos) + val newBody = new Transformers.LocalScopeTransformer { + override def transform(tree: Tree): Tree = tree match { + case tree @ VarRef(newParam.name.name) => tree.copy()(newParam.ptpe)(tree.pos) + case _ => super.transform(tree) + } + }.transform(body) + Closure(ClosureFlags.typed, captureParams, List(newParam), None, AnyType, + newBody, captureValues)(funArg.pos) + + // Fallback for other shapes (theoretically required; dead code in practice) + case _ => + makeFallbackTypedClosure(List(HackNames.ObjectArrayType)) + } + + NewLambda(HackNames.anonFunctionXXLDescriptor, typedClosure)(tree.tpe)(tree.pos) + + case _ => + tree + } + + case _ => + tree + } + } + def readTrees(): List[Tree] = List.fill(readInt())(readTree()) @@ -1641,10 +1771,15 @@ object Serializers { val jsNativeMembers = jsNativeMembersBuilder.result() - ClassDef(name, originalName, kind, jsClassCaptures, superClass, parents, + val classDef = ClassDef(name, originalName, kind, jsClassCaptures, superClass, parents, jsSuperClass, jsNativeLoadSpec, fields, methods, jsConstructor, jsMethodProps, jsNativeMembers, topLevelExportDefs)( optimizerHints) + + if (hacks.useBelow(19)) + anonFunctionClassDefHackBelow19(classDef) + else + classDef } private def jlClassMethodsHackBelow17(methods: List[MethodDef]): List[MethodDef] = { @@ -1931,6 +2066,88 @@ object Serializers { (jsConstructorBuilder.result(), jsMethodPropsBuilder.result()) } + /** Rewrites `scala.scalajs.runtime.AnonFunctionN`s from before 1.19. + * + * Before 1.19, these classes were defined as + * {{{ + * // final in source code + * class AnonFunctionN extends AbstractFunctionN { + * val f: any + * def this(f: any) = { + * this.f = f; + * super() + * } + * def apply(...args: any): any = f(...args) + * } + * }}} + * + * Starting with 1.19, they were rewritten to be used as SAM classes for + * `NewLambda` nodes. The new IR shape is + * {{{ + * // sealed abstract in source code + * class AnonFunctionN extends AbstractFunctionN { + * def this() = super() + * } + * }}} + * + * This function rewrites those classes to the new shape. + * + * The rewrite also applies to Scala 3's `AnonFunctionXXL`. + * + * Tests are in `sbt-plugin/src/sbt-test/linker/anonfunction-compat/`. + */ + private def anonFunctionClassDefHackBelow19(classDef: ClassDef): ClassDef = { + import classDef._ + + if (!HackNames.allAnonFunctionClasses.contains(className)) { + classDef + } else { + val newCtor: MethodDef = { + // Find the old constructor to get its position and version + val oldCtor = methods.find(_.methodName.isConstructor).getOrElse { + throw new InvalidIRException(classDef, + s"Did not find a constructor in ${className.nameString}") + } + implicit val pos = oldCtor.pos + + // constructor def () = this.superClass::() + MethodDef( + MemberFlags.empty.withNamespace(MemberNamespace.Constructor), + MethodIdent(NoArgConstructorName), + NoOriginalName, + Nil, + VoidType, + Some { + ApplyStatically( + ApplyFlags.empty.withConstructor(true), + This()(ClassType(className, nullable = false)), + superClass.get.name, + MethodIdent(NoArgConstructorName), + Nil + )(VoidType) + } + )(OptimizerHints.empty, oldCtor.version) + } + + ClassDef( + name, + originalName, + kind, + jsClassCaptures, + superClass, + interfaces, + jsSuperClass, + jsNativeLoadSpec, + fields = Nil, // throws away the `f` field + methods = List(newCtor), // throws away the old constructor and `apply` method + jsConstructor, + jsMethodProps, + jsNativeMembers, + topLevelExportDefs + )(OptimizerHints.empty)(pos) // throws away the `@inline` + } + } + private def readFieldDef()(implicit pos: Position): FieldDef = { val flags = MemberFlags.fromBits(readInt()) val name = readFieldIdentForEnclosingClass() @@ -2602,6 +2819,8 @@ object Serializers { /** Names needed for hacks. */ private object HackNames { + val AnonFunctionXXLClass = + ClassName("scala.scalajs.runtime.AnonFunctionXXL") // from the Scala 3 library val CloneNotSupportedExceptionClass = ClassName("java.lang.CloneNotSupportedException") val SystemModule: ClassName = @@ -2611,14 +2830,50 @@ object Serializers { val ReflectArrayModClass = ClassName("java.lang.reflect.Array$") + val ObjectArrayType = ArrayType(ArrayTypeRef(ObjectRef, 1), nullable = true) + + private val applySimpleName = SimpleMethodName("apply") + val cloneName: MethodName = - MethodName("clone", Nil, ClassRef(ObjectClass)) + MethodName("clone", Nil, ObjectRef) val identityHashCodeName: MethodName = - MethodName("identityHashCode", List(ClassRef(ObjectClass)), IntRef) + MethodName("identityHashCode", List(ObjectRef), IntRef) val newInstanceSingleName: MethodName = - MethodName("newInstance", List(ClassRef(ClassClass), IntRef), ClassRef(ObjectClass)) + MethodName("newInstance", List(ClassRef(ClassClass), IntRef), ObjectRef) val newInstanceMultiName: MethodName = - MethodName("newInstance", List(ClassRef(ClassClass), ArrayTypeRef(IntRef, 1)), ClassRef(ObjectClass)) + MethodName("newInstance", List(ClassRef(ClassClass), ArrayTypeRef(IntRef, 1)), ObjectRef) + + private val anonFunctionArities: Map[ClassName, Int] = + (0 to 22).map(arity => ClassName(s"scala.scalajs.runtime.AnonFunction$arity") -> arity).toMap + val allAnonFunctionClasses: Set[ClassName] = + anonFunctionArities.keySet + AnonFunctionXXLClass + + object AnonFunctionClass { + def unapply(cls: ClassName): Option[Int] = + anonFunctionArities.get(cls) + } + + lazy val anonFunctionDescriptors: IndexedSeq[NewLambda.Descriptor] = { + anonFunctionArities.toIndexedSeq.sortBy(_._2).map { case (className, arity) => + NewLambda.Descriptor( + superClass = className, + interfaces = Nil, + methodName = MethodName(applySimpleName, List.fill(arity)(ObjectRef), ObjectRef), + paramTypes = List.fill(arity)(AnyType), + resultType = AnyType + ) + } + } + + lazy val anonFunctionXXLDescriptor: NewLambda.Descriptor = { + NewLambda.Descriptor( + superClass = AnonFunctionXXLClass, + interfaces = Nil, + methodName = MethodName(applySimpleName, List(ObjectArrayType.arrayTypeRef), ObjectRef), + paramTypes = List(ObjectArrayType), + resultType = AnyType + ) + } } private class OptionBuilder[T] { diff --git a/library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala b/library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala index 6441a5ee23..dd83d20a1a 100644 --- a/library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala +++ b/library/src/main/scala/scala/scalajs/runtime/AnonFunctions.scala @@ -17,142 +17,93 @@ import scala.runtime._ // scalastyle:off line.size.limit +/* Before 1.19, these classes were concrete. They had a 1-argument constructor + * taking a js.FunctionN, and their `apply()` method called that function. + * + * In 1.19, we introduced `NewLambda` nodes, which superseded these specialized + * classes with a compilation mode that is more efficient on Wasm. + * However, libraries compiled with earlier versions still contain references + * to `AnonFunctionN`. + * + * The IR deserializer patches allocations of the form + * New(AnonFunctionN, ctor, closure :: Nil) + * into + * NewLambda(AnonFunctionN, ..., (...xs) => closure(...xs)) + * + * When the `closure` is directly a JS `Closure` with the right number of + * arguments (which is supposed to be always, as far as our codegens were + * concerned), it rewrites that as + * NewLambda(AnonFunctionN, ..., (...closureParams) => closureBody) + * which provides the best performance for old code. + */ + @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction0[+R](f: js.Function0[R]) extends AbstractFunction0[R] { - override def apply(): R = f() -} +sealed abstract class AnonFunction0[+R] extends AbstractFunction0[R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction1[-T1, +R](f: js.Function1[T1, R]) extends AbstractFunction1[T1, R] { - override def apply(arg1: T1): R = f(arg1) -} +sealed abstract class AnonFunction1[-T1, +R] extends AbstractFunction1[T1, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction2[-T1, -T2, +R](f: js.Function2[T1, T2, R]) extends AbstractFunction2[T1, T2, R] { - override def apply(arg1: T1, arg2: T2): R = f(arg1, arg2) -} +sealed abstract class AnonFunction2[-T1, -T2, +R] extends AbstractFunction2[T1, T2, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction3[-T1, -T2, -T3, +R](f: js.Function3[T1, T2, T3, R]) extends AbstractFunction3[T1, T2, T3, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3): R = f(arg1, arg2, arg3) -} +sealed abstract class AnonFunction3[-T1, -T2, -T3, +R] extends AbstractFunction3[T1, T2, T3, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction4[-T1, -T2, -T3, -T4, +R](f: js.Function4[T1, T2, T3, T4, R]) extends AbstractFunction4[T1, T2, T3, T4, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4): R = f(arg1, arg2, arg3, arg4) -} +sealed abstract class AnonFunction4[-T1, -T2, -T3, -T4, +R] extends AbstractFunction4[T1, T2, T3, T4, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction5[-T1, -T2, -T3, -T4, -T5, +R](f: js.Function5[T1, T2, T3, T4, T5, R]) extends AbstractFunction5[T1, T2, T3, T4, T5, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5): R = f(arg1, arg2, arg3, arg4, arg5) -} +sealed abstract class AnonFunction5[-T1, -T2, -T3, -T4, -T5, +R] extends AbstractFunction5[T1, T2, T3, T4, T5, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction6[-T1, -T2, -T3, -T4, -T5, -T6, +R](f: js.Function6[T1, T2, T3, T4, T5, T6, R]) extends AbstractFunction6[T1, T2, T3, T4, T5, T6, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6): R = f(arg1, arg2, arg3, arg4, arg5, arg6) -} +sealed abstract class AnonFunction6[-T1, -T2, -T3, -T4, -T5, -T6, +R] extends AbstractFunction6[T1, T2, T3, T4, T5, T6, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction7[-T1, -T2, -T3, -T4, -T5, -T6, -T7, +R](f: js.Function7[T1, T2, T3, T4, T5, T6, T7, R]) extends AbstractFunction7[T1, T2, T3, T4, T5, T6, T7, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) -} +sealed abstract class AnonFunction7[-T1, -T2, -T3, -T4, -T5, -T6, -T7, +R] extends AbstractFunction7[T1, T2, T3, T4, T5, T6, T7, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction8[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R](f: js.Function8[T1, T2, T3, T4, T5, T6, T7, T8, R]) extends AbstractFunction8[T1, T2, T3, T4, T5, T6, T7, T8, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) -} +sealed abstract class AnonFunction8[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, +R] extends AbstractFunction8[T1, T2, T3, T4, T5, T6, T7, T8, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction9[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, +R](f: js.Function9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R]) extends AbstractFunction9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) -} +sealed abstract class AnonFunction9[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, +R] extends AbstractFunction9[T1, T2, T3, T4, T5, T6, T7, T8, T9, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction10[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, +R](f: js.Function10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R]) extends AbstractFunction10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) -} +sealed abstract class AnonFunction10[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, +R] extends AbstractFunction10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction11[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, +R](f: js.Function11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R]) extends AbstractFunction11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11) -} +sealed abstract class AnonFunction11[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, +R] extends AbstractFunction11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction12[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, +R](f: js.Function12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R]) extends AbstractFunction12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12) -} +sealed abstract class AnonFunction12[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, +R] extends AbstractFunction12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction13[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, +R](f: js.Function13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R]) extends AbstractFunction13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13) -} +sealed abstract class AnonFunction13[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, +R] extends AbstractFunction13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction14[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, +R](f: js.Function14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R]) extends AbstractFunction14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14) -} +sealed abstract class AnonFunction14[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, +R] extends AbstractFunction14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction15[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, +R](f: js.Function15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R]) extends AbstractFunction15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15) -} +sealed abstract class AnonFunction15[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, +R] extends AbstractFunction15[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction16[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, +R](f: js.Function16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R]) extends AbstractFunction16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16) -} +sealed abstract class AnonFunction16[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, +R] extends AbstractFunction16[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction17[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, +R](f: js.Function17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R]) extends AbstractFunction17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17) -} +sealed abstract class AnonFunction17[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, +R] extends AbstractFunction17[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction18[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, +R](f: js.Function18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R]) extends AbstractFunction18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18) -} +sealed abstract class AnonFunction18[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, +R] extends AbstractFunction18[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction19[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, +R](f: js.Function19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R]) extends AbstractFunction19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19) -} +sealed abstract class AnonFunction19[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, +R] extends AbstractFunction19[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction20[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, +R](f: js.Function20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R]) extends AbstractFunction20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20) -} +sealed abstract class AnonFunction20[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, +R] extends AbstractFunction20[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction21[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, +R](f: js.Function21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R]) extends AbstractFunction21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20, arg21: T21): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21) -} +sealed abstract class AnonFunction21[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, +R] extends AbstractFunction21[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, R] @deprecated("used by the codegen before 1.19", since = "1.19.0") -@inline -final class AnonFunction22[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, -T22, +R](f: js.Function22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R]) extends AbstractFunction22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R] { - override def apply(arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6, arg7: T7, arg8: T8, arg9: T9, arg10: T10, arg11: T11, arg12: T12, arg13: T13, arg14: T14, arg15: T15, arg16: T16, arg17: T17, arg18: T18, arg19: T19, arg20: T20, arg21: T21, arg22: T22): R = f(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22) -} +sealed abstract class AnonFunction22[-T1, -T2, -T3, -T4, -T5, -T6, -T7, -T8, -T9, -T10, -T11, -T12, -T13, -T14, -T15, -T16, -T17, -T18, -T19, -T20, -T21, -T22, +R] extends AbstractFunction22[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, R] // scalastyle:on line.size.limit diff --git a/project/BinaryIncompatibilities.scala b/project/BinaryIncompatibilities.scala index b361c1701b..5435860a02 100644 --- a/project/BinaryIncompatibilities.scala +++ b/project/BinaryIncompatibilities.scala @@ -64,6 +64,10 @@ object BinaryIncompatibilities { ) val Library = Seq( + // Changes covered by a deserialization hack (and the code cannot be used on the JVM, such as in macros) + ProblemFilters.exclude[AbstractClassProblem]("scala.scalajs.runtime.AnonFunction*"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.scalajs.runtime.AnonFunction*.this"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.scalajs.runtime.AnonFunction*.apply"), ) val TestInterface = Seq( diff --git a/sbt-plugin/src/sbt-test/linker/anonfunction-compat/build.sbt b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/build.sbt new file mode 100644 index 0000000000..4ada232e79 --- /dev/null +++ b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/build.sbt @@ -0,0 +1,61 @@ +val scala2Version = "2.12.20" + +ThisBuild / version := scalaJSVersion +ThisBuild / scalaVersion := scala2Version + +ThisBuild / scalaJSLinkerConfig ~= { _.withCheckIR(true) } + +val ScalaJSVersionBeforeTypedClosures = "1.18.2" + +/** In libraryDependencies, replace the dependency whose `name` starts with + * `artifactNamePrefix` by the given `newModuleID`. + */ +def replaceDependency(artifactNamePrefix: String, newModuleID: ModuleID): Setting[_] = { + libraryDependencies := { + libraryDependencies.value.map { dep => + if (dep.name.startsWith(artifactNamePrefix)) + newModuleID + else + dep + } + } +} + +def scalaJSCompilerPlugin(v: String): ModuleID = + compilerPlugin("org.scala-js" % "scalajs-compiler" % v cross CrossVersion.full) + +lazy val scala2OldCompilerOldLib = project.in(file("scala2-old-compiler-old-lib")) + .enablePlugins(ScalaJSPlugin) + .settings( + replaceDependency("scalajs-compiler", + scalaJSCompilerPlugin(ScalaJSVersionBeforeTypedClosures)), + replaceDependency("scalajs-library", + "org.scala-js" %% "scalajs-library" % ScalaJSVersionBeforeTypedClosures), + replaceDependency("scalajs-scalalib", + "org.scala-js" %% "scalajs-scalalib" % s"$scala2Version+$ScalaJSVersionBeforeTypedClosures"), + scalaJSUseMainModuleInitializer := true + ) + +lazy val scala2OldCompilerNewLib = project.in(file("scala2-old-compiler-new-lib")) + .enablePlugins(ScalaJSPlugin) + .settings( + replaceDependency("scalajs-compiler", + scalaJSCompilerPlugin(ScalaJSVersionBeforeTypedClosures)), + scalaJSUseMainModuleInitializer := true + ) + +lazy val scala3OldCompilerOldLib = project.in(file("scala3-old-compiler-old-lib")) + .enablePlugins(ScalaJSPlugin) + .settings( + scalaVersion := "3.6.3", + replaceDependency("scalajs-library", + "org.scala-js" % "scalajs-library_2.13" % ScalaJSVersionBeforeTypedClosures), + scalaJSUseMainModuleInitializer := true + ) + +lazy val scala3OldCompilerNewLib = project.in(file("scala3-old-compiler-new-lib")) + .enablePlugins(ScalaJSPlugin) + .settings( + scalaVersion := "3.6.3", + scalaJSUseMainModuleInitializer := true + ) diff --git a/sbt-plugin/src/sbt-test/linker/anonfunction-compat/project/build.properties b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/project/build.properties new file mode 100644 index 0000000000..40b3b8e7b6 --- /dev/null +++ b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.0 diff --git a/sbt-plugin/src/sbt-test/linker/anonfunction-compat/project/build.sbt b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/project/build.sbt new file mode 100644 index 0000000000..4465f18281 --- /dev/null +++ b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/project/build.sbt @@ -0,0 +1,5 @@ +val scalaJSVersion = sys.props("plugin.version") + +addSbtPlugin("org.scala-js" % "sbt-scalajs" % scalaJSVersion) + +libraryDependencies += "org.scala-js" %% "scalajs-linker" % scalaJSVersion diff --git a/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala2-old-compiler-new-lib/Main.scala b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala2-old-compiler-new-lib/Main.scala new file mode 100644 index 0000000000..358d46f21b --- /dev/null +++ b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala2-old-compiler-new-lib/Main.scala @@ -0,0 +1,9 @@ +package org.scalajs.sbtplugin.test + +object Main { + def main(args: Array[String]): Unit = { + val xs = List(2, 3, 5, 7, 11) + xs.foreach(println(_)) + xs.map(i => i * 2).foreach(println(_)) + } +} diff --git a/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala2-old-compiler-old-lib/Main.scala b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala2-old-compiler-old-lib/Main.scala new file mode 100644 index 0000000000..358d46f21b --- /dev/null +++ b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala2-old-compiler-old-lib/Main.scala @@ -0,0 +1,9 @@ +package org.scalajs.sbtplugin.test + +object Main { + def main(args: Array[String]): Unit = { + val xs = List(2, 3, 5, 7, 11) + xs.foreach(println(_)) + xs.map(i => i * 2).foreach(println(_)) + } +} diff --git a/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala3-old-compiler-new-lib/Main.scala b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala3-old-compiler-new-lib/Main.scala new file mode 100644 index 0000000000..ca1337ac22 --- /dev/null +++ b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala3-old-compiler-new-lib/Main.scala @@ -0,0 +1,18 @@ +package org.scalajs.sbtplugin.test + +object Main { + def foo(f: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, + Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int): Int = { + f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25) + } + + def main(args: Array[String]): Unit = { + val xs = List(2, 3, 5, 7, 11) + xs.foreach(println(_)) + xs.map(i => i * 2).foreach(println(_)) + + println(foo { (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25) => + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20 + x21 + x22 + x23 + x24 + x25 + }) + } +} diff --git a/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala3-old-compiler-old-lib/Main.scala b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala3-old-compiler-old-lib/Main.scala new file mode 100644 index 0000000000..ca1337ac22 --- /dev/null +++ b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/scala3-old-compiler-old-lib/Main.scala @@ -0,0 +1,18 @@ +package org.scalajs.sbtplugin.test + +object Main { + def foo(f: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, + Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int): Int = { + f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25) + } + + def main(args: Array[String]): Unit = { + val xs = List(2, 3, 5, 7, 11) + xs.foreach(println(_)) + xs.map(i => i * 2).foreach(println(_)) + + println(foo { (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25) => + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20 + x21 + x22 + x23 + x24 + x25 + }) + } +} diff --git a/sbt-plugin/src/sbt-test/linker/anonfunction-compat/test b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/test new file mode 100644 index 0000000000..2621440f32 --- /dev/null +++ b/sbt-plugin/src/sbt-test/linker/anonfunction-compat/test @@ -0,0 +1,25 @@ +> show scala2OldCompilerOldLib/libraryDependencies +> show scala2OldCompilerOldLib/Compile/fullClasspath +> scala2OldCompilerOldLib/scalajsp scala.scalajs.runtime.AnonFunction2 +> scala2OldCompilerOldLib/scalajsp org.scalajs.sbtplugin.test.Main$ +> scala2OldCompilerOldLib/run + +> show scala2OldCompilerNewLib/libraryDependencies +> show scala2OldCompilerNewLib/Compile/fullClasspath +> scala2OldCompilerNewLib/scalajsp scala.scalajs.runtime.AnonFunction2 +> scala2OldCompilerNewLib/scalajsp org.scalajs.sbtplugin.test.Main$ +> scala2OldCompilerNewLib/run + +> show scala3OldCompilerOldLib/libraryDependencies +> show scala3OldCompilerOldLib/Compile/fullClasspath +> scala3OldCompilerOldLib/scalajsp scala.scalajs.runtime.AnonFunction2 +> scala3OldCompilerOldLib/scalajsp scala.scalajs.runtime.AnonFunctionXXL +> scala3OldCompilerOldLib/scalajsp org.scalajs.sbtplugin.test.Main$ +> scala3OldCompilerOldLib/run + +> show scala3OldCompilerNewLib/libraryDependencies +> show scala3OldCompilerNewLib/Compile/fullClasspath +> scala3OldCompilerNewLib/scalajsp scala.scalajs.runtime.AnonFunction2 +> scala3OldCompilerNewLib/scalajsp scala.scalajs.runtime.AnonFunctionXXL +> scala3OldCompilerNewLib/scalajsp org.scalajs.sbtplugin.test.Main$ +> scala3OldCompilerNewLib/run