@@ -2815,9 +2815,179 @@ class JSCodeGen()(using genCtx: Context) {
2815
2815
js.ForIn (objVarDef.ref, keyVarIdent, NoOriginalName , {
2816
2816
js.JSFunctionApply (fVarDef.ref, List (keyVarRef))
2817
2817
}))
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 )
2818
2831
}
2819
2832
}
2820
2833
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
+
2821
2991
/** Gen actual actual arguments to Scala method call.
2822
2992
* Returns a list of the transformed arguments.
2823
2993
*
@@ -2992,8 +3162,9 @@ class JSCodeGen()(using genCtx: Context) {
2992
3162
lazy val isWrapArray : Set [Symbol ] = {
2993
3163
val names0 = defn.ScalaValueClasses ().map(sym => nme.wrapXArray(sym.name))
2994
3164
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
2997
3168
}
2998
3169
2999
3170
def unapply (tree : Apply ): Option [Tree ] = tree match {
0 commit comments