@@ -2815,9 +2815,181 @@ class JSCodeGen()(using genCtx: Context) {
28152815 js.ForIn (objVarDef.ref, keyVarIdent, NoOriginalName , {
28162816 js.JSFunctionApply (fVarDef.ref, List (keyVarRef))
28172817 }))
2818+
2819+ case REFLECT_SELECTABLE_SELECTDYN =>
2820+ // scala.reflect.Selectable.selectDynamic
2821+ genReflectiveCall(tree, isSelectDynamic = true )
2822+ case REFLECT_SELECTABLE_APPLYDYN =>
2823+ // scala.reflect.Selectable.applyDynamic
2824+ genReflectiveCall(tree, isSelectDynamic = false )
28182825 }
28192826 }
28202827
2828+ /** Gen the SJSIR for a reflective call.
2829+ *
2830+ * Reflective calls are calls to a structural type field or method that
2831+ * involve a reflective Selectable. They look like the following in source
2832+ * code:
2833+ * {{{
2834+ * import scala.reflect.Selectable.reflectiveSelectable
2835+ *
2836+ * type Structural = {
2837+ * val foo: Int
2838+ * def bar(x: Int, y: String): String
2839+ * }
2840+ *
2841+ * val structural: Structural = new {
2842+ * val foo: Int = 5
2843+ * def bar(x: Int, y: String): String = x.toString + y
2844+ * }
2845+ *
2846+ * structural.foo
2847+ * structural.bar(6, "hello")
2848+ * }}}
2849+ *
2850+ * After expansion by the Scala 3 rules for structural member selections and
2851+ * calls, they look like
2852+ *
2853+ * {{{
2854+ * reflectiveSelectable(structural).selectDynamic("foo")
2855+ * reflectiveSelectable(structural).applyDynamic("bar",
2856+ * ClassTag(classOf[Int]), ClassTag(classOf[String])
2857+ * )(
2858+ * 6, "hello"
2859+ * )
2860+ * }}}
2861+ *
2862+ * and eventually reach the back-end as
2863+ *
2864+ * {{{
2865+ * reflectiveSelectable(structural).selectDynamic("foo") // same as above
2866+ * reflectiveSelectable(structural).applyDynamic("bar",
2867+ * wrapRefArray([ ClassTag(classOf[Int]), ClassTag(classOf[String]) : ClassTag ]
2868+ * )(
2869+ * genericWrapArray([ Int.box(6), "hello" : Object ])
2870+ * )
2871+ * }}}
2872+ *
2873+ * If we use the deprecated `import scala.language.reflectiveCalls`, the
2874+ * wrapper for the receiver `structural` are the following instead:
2875+ *
2876+ * {{{
2877+ * reflectiveSelectableFromLangReflectiveCalls(structural)(
2878+ * using scala.languageFeature.reflectiveCalls)
2879+ * }}}
2880+ *
2881+ * (in which case we don't care about the contextual argument).
2882+ *
2883+ * In SJSIR, they must be encoded as follows:
2884+ *
2885+ * {{{
2886+ * structural.foo;R()
2887+ * structural.bar;I;Ljava.lang.String;R(
2888+ * Int.box(6).asInstanceOf[int],
2889+ * "hello".asInstanceOf[java.lang.String]
2890+ * )
2891+ * }}}
2892+ *
2893+ * This means that we must deconstruct the elaborated calls to recover:
2894+ *
2895+ * - the original receiver `structural`
2896+ * - the method name as a compile-time string `foo` or `bar`
2897+ * - the `tp: Type`s that have been wrapped in `ClassTag(classOf[tp])`, as a
2898+ * compile-time List[Type], from which we'll derive `jstpe.Type`s for the
2899+ * `asInstanceOf`s and `jstpe.TypeRef`s for the `MethodName.reflectiveProxy`
2900+ * - the actual arguments as a compile-time `List[Tree]`
2901+ *
2902+ * Virtually all of the code in `genReflectiveCall` deals with recovering
2903+ * those elements. Constructing the IR Tree is the easy part after that.
2904+ */
2905+ private def genReflectiveCall (tree : Apply , isSelectDynamic : Boolean ): js.Tree = {
2906+ implicit val pos = tree.span
2907+ val Apply (fun @ Select (receiver0, _), args) = tree
2908+
2909+ /* Extract the real receiver, which is the first argument to one of the
2910+ * implicit conversions scala.reflect.Selectable.reflectiveSelectable or
2911+ * scala.Selectable.reflectiveSelectableFromLangReflectiveCalls.
2912+ */
2913+ val receiver = receiver0 match {
2914+ case Apply (fun1, receiver :: _)
2915+ if fun1.symbol == jsdefn.ReflectSelectable_reflectiveSelectable ||
2916+ fun1.symbol == jsdefn.Selectable_reflectiveSelectableFromLangReflectiveCalls =>
2917+ genExpr(receiver)
2918+
2919+ case _ =>
2920+ report.error(
2921+ " The receiver of Selectable.selectDynamic or Selectable.applyDynamic " +
2922+ " must be a call to the (implicit) method scala.reflect.Selectable.reflectiveSelectable. " +
2923+ " Other uses are not supported in Scala.js." ,
2924+ tree.sourcePos)
2925+ js.Undefined ()
2926+ }
2927+
2928+ // Extract the method name as a String
2929+ val methodNameStr = args.head match {
2930+ case Literal (Constants .Constant (name : String )) =>
2931+ name
2932+ case _ =>
2933+ report.error(
2934+ " The method name given to Selectable.selectDynamic or Selectable.applyDynamic " +
2935+ " must be a literal string. " +
2936+ " Other uses are not supported in Scala.js." ,
2937+ args.head.sourcePos)
2938+ " erroneous"
2939+ }
2940+
2941+ val (formalParamTypeRefs, actualArgs) = if (isSelectDynamic) {
2942+ (Nil , Nil )
2943+ } else {
2944+ // Extract the param type refs and actual args from the 2nd and 3rd argument to applyDynamic
2945+ args.tail match {
2946+ case WrapArray (classTagsArray : JavaSeqLiteral ) :: WrapArray (actualArgsAnyArray : JavaSeqLiteral ) :: Nil =>
2947+ // Extract jstpe.Type's and jstpe.TypeRef's from the ClassTag.apply(_) trees
2948+ val formalParamTypesAndTypeRefs = classTagsArray.elems.map {
2949+ // ClassTag.apply(classOf[tp]) -> tp
2950+ case Apply (fun, Literal (const) :: Nil )
2951+ if fun.symbol == defn.ClassTagModule_apply && const.tag == Constants .ClazzTag =>
2952+ toIRTypeAndTypeRef(const.typeValue)
2953+ // ClassTag.SpecialType -> erasure(SepecialType.typeRef) (e.g., ClassTag.Any -> Object)
2954+ case Apply (Select (classTagModule, name), Nil )
2955+ if classTagModule.symbol == defn.ClassTagModule &&
2956+ defn.SpecialClassTagClasses .exists(_.name == name.toTypeName) =>
2957+ toIRTypeAndTypeRef(TypeErasure .erasure(
2958+ defn.SpecialClassTagClasses .find(_.name == name.toTypeName).get.typeRef))
2959+ // Anything else is invalid
2960+ case classTag =>
2961+ report.error(
2962+ " The ClassTags passed to Selectable.applyDynamic must be " +
2963+ " literal ClassTag(classOf[T]) expressions " +
2964+ " (typically compiler-generated). " +
2965+ " Other uses are not supported in Scala.js." ,
2966+ classTag.sourcePos)
2967+ (jstpe.AnyType , jstpe.ClassRef (jsNames.ObjectClass ))
2968+ }
2969+
2970+ // Gen the actual args, downcasting them to the formal param types
2971+ val actualArgs = actualArgsAnyArray.elems.zip(formalParamTypesAndTypeRefs).map {
2972+ (actualArgAny, formalParamTypeAndTypeRef) =>
2973+ val genActualArgAny = genExpr(actualArgAny)
2974+ js.AsInstanceOf (genActualArgAny, formalParamTypeAndTypeRef._1)(genActualArgAny.pos)
2975+ }
2976+
2977+ (formalParamTypesAndTypeRefs.map(_._2), actualArgs)
2978+
2979+ case _ =>
2980+ report.error(
2981+ " Passing the varargs of Selectable.applyDynamic with `: _*` " +
2982+ " is not supported in Scala.js." ,
2983+ tree.sourcePos)
2984+ (Nil , Nil )
2985+ }
2986+ }
2987+
2988+ val methodName = MethodName .reflectiveProxy(methodNameStr, formalParamTypeRefs)
2989+
2990+ js.Apply (js.ApplyFlags .empty, receiver, js.MethodIdent (methodName), actualArgs)(jstpe.AnyType )
2991+ }
2992+
28212993 /** Gen actual actual arguments to Scala method call.
28222994 * Returns a list of the transformed arguments.
28232995 *
@@ -2992,8 +3164,9 @@ class JSCodeGen()(using genCtx: Context) {
29923164 lazy val isWrapArray : Set [Symbol ] = {
29933165 val names0 = defn.ScalaValueClasses ().map(sym => nme.wrapXArray(sym.name))
29943166 val names1 = names0 ++ Set (nme.wrapRefArray, nme.genericWrapArray)
2995- val names2 = names1.map(defn.ScalaPredefModule .requiredMethod(_))
2996- names2.toSet
3167+ val symsInPredef = names1.map(defn.ScalaPredefModule .requiredMethod(_))
3168+ val symsInScalaRunTime = names1.map(defn.ScalaRuntimeModule .requiredMethod(_))
3169+ (symsInPredef ++ symsInScalaRunTime).toSet
29973170 }
29983171
29993172 def unapply (tree : Apply ): Option [Tree ] = tree match {
0 commit comments