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