From 88510deba601ed8eb7473e3ea546f4563d315925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 28 Aug 2025 12:06:20 +0200 Subject: [PATCH 1/2] Fix the compat API of `Reflect` on Scala 2.13.{3-8}. Previously, we had the following compile error: Reflect.scala:109:87: Passing an explicit array value to a Scala varargs method is deprecated (since 2.13.0) and will result in a defensive copy; Use the more efficient non-copying ArraySeq.unsafeWrapArray or an explicit toIndexedSeq call (c._1.toArray, (args: Array[Any]) => c._2.asInstanceOf[JSFunctionVarArgs].apply(args: _*)) ^ --- .../main/scala/scala/scalajs/reflect/Reflect.scala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/library/src/main/scala/scala/scalajs/reflect/Reflect.scala b/library/src/main/scala/scala/scalajs/reflect/Reflect.scala index d96fc17454..42f1fdbd6e 100644 --- a/library/src/main/scala/scala/scalajs/reflect/Reflect.scala +++ b/library/src/main/scala/scala/scalajs/reflect/Reflect.scala @@ -106,7 +106,17 @@ object Reflect { constructors: js.Array[js.Tuple2[js.Array[Class[_]], js.Function]]): Unit = { registerInstantiatableClassV2(fqcn, runtimeClass, constructors.map { c => - (c._1.toArray, (args: Array[Any]) => c._2.asInstanceOf[JSFunctionVarArgs].apply(args: _*)) + val paramClassesArray = c._1.toArray + val newInstanceJSFun = c._2.asInstanceOf[JSFunctionVarArgs] + + val newInstanceFun: Array[Any] => Any = { (args: Array[Any]) => + // The shenanigans in this function are required to be compatible across all Scala patch versions + import scala.scalajs.runtime.toRefVarArgs // this is fine because we are inside scalajs-library + val argsAsRefArray = args.asInstanceOf[Array[AnyRef]] // no-op because Array[Any] also erases to jl.Object[] + newInstanceJSFun.apply(toRefVarArgs(argsAsRefArray): _*) + } + + (paramClassesArray, newInstanceFun) }.toArray) } From 3af18e2680ef3a92469294c5b8a1a10e7f8e8df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 28 Aug 2025 13:16:57 +0200 Subject: [PATCH 2/2] Revert "Call the new Scala API of `Reflect` from the codegen." This reverts commit e754a55072727e0300ecace3f5d622725db6de1e. --- .../org/scalajs/nscplugin/GenJSCode.scala | 71 +++++-------------- .../org/scalajs/nscplugin/JSDefinitions.scala | 4 +- 2 files changed, 18 insertions(+), 57 deletions(-) diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala index 0afbae4eba..3ee837a286 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/GenJSCode.scala @@ -1417,13 +1417,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) implicit pos: Position): Option[js.Tree] = { val fqcnArg = js.StringLiteral(sym.fullName + "$") val runtimeClassArg = js.ClassOf(toTypeRef(sym.info)) - - val loadModuleFunArg = js.NewLambda( - js.NewLambda.Descriptor(encodeClassName(AbstractFunctionClass(0)), Nil, - MethodName("apply", Nil, jswkn.ObjectRef), - Nil, jstpe.AnyType), - js.Closure(js.ClosureFlags.typed, Nil, Nil, None, jstpe.AnyType, genLoadModule(sym), Nil) - )(encodeClassType(FunctionClass(0))) + val loadModuleFunArg = + js.Closure(js.ClosureFlags.arrow, Nil, Nil, None, jstpe.AnyType, genLoadModule(sym), Nil) val stat = genApplyMethod( genLoadModule(ReflectModule), @@ -1442,40 +1437,12 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) if (ctors.isEmpty) { None } else { - val objectArrayRef = jstpe.ArrayTypeRef(jswkn.ObjectRef, 1) - val objectArrayType = jstpe.ArrayType(objectArrayRef, nullable = true) - val tuple2Class = encodeClassName(TupleClass(2)) - val tuple2ArrayRef = jstpe.ArrayTypeRef(jstpe.ClassRef(tuple2Class), 1) - val classClassRef = jstpe.ClassRef(jswkn.ClassClass) - val classArrayRef = jstpe.ArrayTypeRef(classClassRef, 1) - - val tuple2Ctor = MethodName.constructor(List(jswkn.ObjectRef, jswkn.ObjectRef)) - - val newInstanceFunDescriptor = { - js.NewLambda.Descriptor(encodeClassName(AbstractFunctionClass(1)), Nil, - MethodName("apply", List(jswkn.ObjectRef), jswkn.ObjectRef), - List(jstpe.AnyType), jstpe.AnyType) - } - val constructorsInfos = for { ctor <- ctors } yield { - val paramTypesArray = js.ArrayValue(classArrayRef, - ctor.tpe.params.map(p => js.ClassOf(toTypeRef(p.tpe)))) - - val newInstanceClosure = { - // param args: Object - val argsParamDef = js.ParamDef(js.LocalIdent(LocalName("args")), - NoOriginalName, jstpe.AnyType, mutable = false) - - // val argsArray: Object[] = args.asInstanceOf[Object[]] - val argsArrayVarDef = js.VarDef(js.LocalIdent(LocalName("argsArray")), - NoOriginalName, objectArrayType, mutable = false, - js.AsInstanceOf(argsParamDef.ref, objectArrayType)) - - // argsArray[i].asInstanceOf[Ti] for every parameter of the constructor - val actualParams = for { - (param, index) <- ctor.tpe.params.zipWithIndex + withNewLocalNameScope { + val (parameterTypes, formalParams, actualParams) = (for { + param <- ctor.tpe.params } yield { /* Note that we do *not* use `param.tpe` entering posterasure * (neither to compute `paramType` nor to give to `fromAny`). @@ -1493,30 +1460,24 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G) * parameter types is `List(classOf[Int])`, and when invoked * reflectively, it must be given an `Int` (or `Integer`). */ - fromAny( - js.ArraySelect(argsArrayVarDef.ref, js.IntLiteral(index))(jstpe.AnyType), - param.tpe) - } + val paramType = js.ClassOf(toTypeRef(param.tpe)) + val paramDef = genParamDef(param, jstpe.AnyType) + val actualParam = fromAny(paramDef.ref, param.tpe) + (paramType, paramDef, actualParam) + }).unzip3 - /* typed-lambda<>(args: Object): any = { - * val argsArray: Object[] = args.asInstanceOf[Object[]] - * new MyClass(...argsArray[i].asInstanceOf[Ti]) - * } - */ - js.Closure(js.ClosureFlags.typed, Nil, argsParamDef :: Nil, None, jstpe.AnyType, { - js.Block(argsArrayVarDef, genNew(sym, ctor, actualParams)) - }, Nil) - } + val paramTypesArray = js.JSArrayConstr(parameterTypes) - val newInstanceFun = js.NewLambda(newInstanceFunDescriptor, newInstanceClosure)( - encodeClassType(FunctionClass(1))) + val newInstanceFun = js.Closure(js.ClosureFlags.arrow, Nil, + formalParams, None, jstpe.AnyType, genNew(sym, ctor, actualParams), Nil) - js.New(tuple2Class, js.MethodIdent(tuple2Ctor), List(paramTypesArray, newInstanceFun)) + js.JSArrayConstr(List(paramTypesArray, newInstanceFun)) + } } val fqcnArg = js.StringLiteral(sym.fullName) val runtimeClassArg = js.ClassOf(toTypeRef(sym.info)) - val ctorsInfosArg = js.ArrayValue(tuple2ArrayRef, constructorsInfos) + val ctorsInfosArg = js.JSArrayConstr(constructorsInfos) val stat = genApplyMethod( genLoadModule(ReflectModule), diff --git a/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala b/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala index 5e5700feb3..e91b74d4ff 100644 --- a/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala +++ b/compiler/src/main/scala/org/scalajs/nscplugin/JSDefinitions.scala @@ -151,8 +151,8 @@ trait JSDefinitions { def ScalaRunTime_isArray: Symbol = getMemberMethod(ScalaRunTimeModule, newTermName("isArray")).suchThat(_.tpe.params.size == 2) lazy val ReflectModule = getRequiredModule("scala.scalajs.reflect.Reflect") - lazy val Reflect_registerLoadableModuleClass = getMemberMethod(ReflectModule, newTermName("registerLoadableModuleClassV2")) - lazy val Reflect_registerInstantiatableClass = getMemberMethod(ReflectModule, newTermName("registerInstantiatableClassV2")) + lazy val Reflect_registerLoadableModuleClass = getMemberMethod(ReflectModule, newTermName("registerLoadableModuleClass")) + lazy val Reflect_registerInstantiatableClass = getMemberMethod(ReflectModule, newTermName("registerInstantiatableClass")) lazy val EnableReflectiveInstantiationAnnotation = getRequiredClass("scala.scalajs.reflect.annotation.EnableReflectiveInstantiation")