@@ -324,13 +324,16 @@ class JSCodeGen()(using genCtx: Context) {
324
324
325
325
// Optimizer hints
326
326
327
+ val isDynamicImportThunk = sym.isSubClass(jsdefn.DynamicImportThunkClass )
328
+
327
329
def isStdLibClassWithAdHocInlineAnnot (sym : Symbol ): Boolean = {
328
330
val fullName = sym.fullName.toString
329
331
(fullName.startsWith(" scala.Tuple" ) && ! fullName.endsWith(" $" )) ||
330
332
(fullName.startsWith(" scala.collection.mutable.ArrayOps$of" ))
331
333
}
332
334
333
335
val shouldMarkInline = (
336
+ isDynamicImportThunk ||
334
337
sym.hasAnnotation(jsdefn.InlineAnnot ) ||
335
338
(sym.isAnonymousFunction && ! sym.isSubClass(defn.PartialFunctionClass )) ||
336
339
isStdLibClassWithAdHocInlineAnnot(sym))
@@ -350,14 +353,17 @@ class JSCodeGen()(using genCtx: Context) {
350
353
tree match {
351
354
case EmptyTree => ()
352
355
353
- case _ : ValDef =>
354
- () // fields are added via genClassFields()
356
+ case vd : ValDef =>
357
+ // fields are added via genClassFields(), but we need to generate the JS native members
358
+ val sym = vd.symbol
359
+ if (! sym.is(Module ) && sym.hasAnnotation(jsdefn.JSNativeAnnot ))
360
+ generatedNonFieldMembers += genJSNativeMemberDef(vd)
355
361
356
362
case dd : DefDef =>
357
363
val sym = dd.symbol
358
-
359
- if ( sym.hasAnnotation(jsdefn. JSNativeAnnot ))
360
- generatedNonFieldMembers += genJSNativeMemberDef(dd)
364
+ if sym.hasAnnotation(jsdefn. JSNativeAnnot ) then
365
+ if ! sym.is( Accessor ) then
366
+ generatedNonFieldMembers += genJSNativeMemberDef(dd)
361
367
else
362
368
generatedNonFieldMembers ++= genMethod(dd)
363
369
@@ -404,8 +410,12 @@ class JSCodeGen()(using genCtx: Context) {
404
410
Nil
405
411
}
406
412
413
+ val optDynamicImportForwarder =
414
+ if (isDynamicImportThunk) List (genDynamicImportForwarder(sym))
415
+ else Nil
416
+
407
417
val allMemberDefsExceptStaticForwarders =
408
- generatedMembers ::: memberExports ::: optStaticInitializer
418
+ generatedMembers ::: memberExports ::: optStaticInitializer ::: optDynamicImportForwarder
409
419
410
420
// Add static forwarders
411
421
val allMemberDefs = if (! isCandidateForForwarders(sym)) {
@@ -1372,12 +1382,12 @@ class JSCodeGen()(using genCtx: Context) {
1372
1382
// Generate a method -------------------------------------------------------
1373
1383
1374
1384
/** Generates the JSNativeMemberDef. */
1375
- def genJSNativeMemberDef (tree : DefDef ): js.JSNativeMemberDef = {
1385
+ def genJSNativeMemberDef (tree : ValOrDefDef ): js.JSNativeMemberDef = {
1376
1386
implicit val pos = tree.span
1377
1387
1378
1388
val sym = tree.symbol
1379
1389
val flags = js.MemberFlags .empty.withNamespace(js.MemberNamespace .PublicStatic )
1380
- val methodName = encodeMethodSym (sym)
1390
+ val methodName = encodeJSNativeMemberSym (sym)
1381
1391
val jsNativeLoadSpec = computeJSNativeLoadSpecOfValDef(sym)
1382
1392
js.JSNativeMemberDef (flags, methodName, jsNativeLoadSpec)
1383
1393
}
@@ -1775,6 +1785,8 @@ class JSCodeGen()(using genCtx: Context) {
1775
1785
genLoadModule(sym)
1776
1786
} else if (sym.is(JavaStatic )) {
1777
1787
genLoadStaticField(sym)
1788
+ } else if (sym.hasAnnotation(jsdefn.JSNativeAnnot )) {
1789
+ genJSNativeMemberSelect(tree)
1778
1790
} else {
1779
1791
val (field, boxed) = genAssignableField(sym, qualifier)
1780
1792
if (boxed) unbox(field, atPhase(elimErasedValueTypePhase)(sym.info))
@@ -3023,7 +3035,7 @@ class JSCodeGen()(using genCtx: Context) {
3023
3035
else
3024
3036
genApplyJSClassMethod(genExpr(receiver), sym, genActualArgs(sym, args))
3025
3037
} else if (sym.hasAnnotation(jsdefn.JSNativeAnnot )) {
3026
- genJSNativeMemberCall(tree, isStat )
3038
+ genJSNativeMemberCall(tree)
3027
3039
} else {
3028
3040
genApplyMethodMaybeStatically(genExpr(receiver), sym, genActualArgs(sym, args))
3029
3041
}
@@ -3154,14 +3166,21 @@ class JSCodeGen()(using genCtx: Context) {
3154
3166
}
3155
3167
3156
3168
/** Gen JS code for a call to a native JS def or val. */
3157
- private def genJSNativeMemberCall (tree : Apply , isStat : Boolean ): js.Tree = {
3169
+ private def genJSNativeMemberSelect (tree : Tree ): js.Tree =
3170
+ genJSNativeMemberSelectOrCall(tree, Nil )
3171
+
3172
+ /** Gen JS code for a call to a native JS def or val. */
3173
+ private def genJSNativeMemberCall (tree : Apply ): js.Tree =
3174
+ genJSNativeMemberSelectOrCall(tree, tree.args)
3175
+
3176
+ /** Gen JS code for a call to a native JS def or val. */
3177
+ private def genJSNativeMemberSelectOrCall (tree : Tree , args : List [Tree ]): js.Tree = {
3158
3178
val sym = tree.symbol
3159
- val Apply (_, args) = tree
3160
3179
3161
3180
implicit val pos = tree.span
3162
3181
3163
3182
val jsNativeMemberValue =
3164
- js.SelectJSNativeMember (encodeClassName(sym.owner), encodeMethodSym (sym))
3183
+ js.SelectJSNativeMember (encodeClassName(sym.owner), encodeJSNativeMemberSym (sym))
3165
3184
3166
3185
val boxedResult =
3167
3186
if (sym.isJSGetter) jsNativeMemberValue
@@ -3497,6 +3516,36 @@ class JSCodeGen()(using genCtx: Context) {
3497
3516
}
3498
3517
}
3499
3518
3519
+ /** Generates a static method instantiating and calling this
3520
+ * DynamicImportThunk's `apply`:
3521
+ *
3522
+ * {{{
3523
+ * static def dynamicImport$;<params>;Ljava.lang.Object(<params>): any = {
3524
+ * new <clsSym>.<init>;<params>:V(<params>).apply;Ljava.lang.Object()
3525
+ * }
3526
+ * }}}
3527
+ */
3528
+ private def genDynamicImportForwarder (clsSym : Symbol )(using Position ): js.MethodDef = {
3529
+ withNewLocalNameScope {
3530
+ val ctor = clsSym.primaryConstructor
3531
+ val paramSyms = ctor.paramSymss.flatten
3532
+ val paramDefs = paramSyms.map(genParamDef(_))
3533
+
3534
+ val body = {
3535
+ val inst = js.New (encodeClassName(clsSym), encodeMethodSym(ctor), paramDefs.map(_.ref))
3536
+ genApplyMethod(inst, jsdefn.DynamicImportThunkClass_apply , Nil )
3537
+ }
3538
+
3539
+ js.MethodDef (
3540
+ js.MemberFlags .empty.withNamespace(js.MemberNamespace .PublicStatic ),
3541
+ encodeDynamicImportForwarderIdent(paramSyms),
3542
+ NoOriginalName ,
3543
+ paramDefs,
3544
+ jstpe.AnyType ,
3545
+ Some (body))(OptimizerHints .empty, None )
3546
+ }
3547
+ }
3548
+
3500
3549
/** Boxes a value of the given type before `elimErasedValueType`.
3501
3550
*
3502
3551
* This should be used when sending values to a JavaScript context, which
@@ -3800,6 +3849,46 @@ class JSCodeGen()(using genCtx: Context) {
3800
3849
// js.import.meta
3801
3850
js.JSImportMeta ()
3802
3851
3852
+ case DYNAMIC_IMPORT =>
3853
+ // runtime.dynamicImport
3854
+ assert(args.size == 1 ,
3855
+ s " Expected exactly 1 argument for JS primitive $code but got " +
3856
+ s " ${args.size} at $pos" )
3857
+
3858
+ args.head match {
3859
+ case Block (stats, expr @ Typed (Apply (fun @ Select (New (tpt), _), args), _)) =>
3860
+ /* stats is always empty if no other compiler plugin is present.
3861
+ * However, code instrumentation (notably scoverage) might add
3862
+ * statements here. If this is the case, the thunk anonymous class
3863
+ * has already been created when the other plugin runs (i.e. the
3864
+ * plugin ran after jsinterop).
3865
+ *
3866
+ * Therefore, it is OK to leave the statements on our side of the
3867
+ * dynamic loading boundary.
3868
+ */
3869
+
3870
+ val clsSym = tpt.symbol
3871
+ val ctor = fun.symbol
3872
+
3873
+ assert(clsSym.isSubClass(jsdefn.DynamicImportThunkClass ),
3874
+ s " expected subclass of DynamicImportThunk, got: $clsSym at: ${expr.sourcePos}" )
3875
+ assert(ctor.isPrimaryConstructor,
3876
+ s " expected primary constructor, got: $ctor at: ${expr.sourcePos}" )
3877
+
3878
+ js.Block (
3879
+ stats.map(genStat(_)),
3880
+ js.ApplyDynamicImport (
3881
+ js.ApplyFlags .empty,
3882
+ encodeClassName(clsSym),
3883
+ encodeDynamicImportForwarderIdent(ctor.paramSymss.flatten),
3884
+ genActualArgs(ctor, args))
3885
+ )
3886
+
3887
+ case tree =>
3888
+ throw new FatalError (
3889
+ s " Unexpected argument tree in dynamicImport: $tree/ ${tree.getClass} at: $pos" )
3890
+ }
3891
+
3803
3892
case JS_NATIVE =>
3804
3893
// js.native
3805
3894
report.error(
0 commit comments