@@ -183,11 +183,11 @@ abstract class GenJSCode extends plugins.PluginComponent
183
183
184
184
// Global class generation state -------------------------------------------
185
185
186
- private val lazilyGeneratedAnonClasses = mutable.Map .empty[Symbol , ClassDef ]
186
+ private val lazilyGeneratedClasses = mutable.Map .empty[Symbol , ClassDef ]
187
187
private val generatedClasses =
188
188
ListBuffer .empty[(Symbol , Option [String ], js.ClassDef )]
189
189
190
- private def consumeLazilyGeneratedAnonClass (sym : Symbol ): ClassDef = {
190
+ private def consumeLazilyGeneratedClass (sym : Symbol ): ClassDef = {
191
191
/* If we are trying to generate an method as JSFunction, we cannot
192
192
* actually consume the symbol, since we might fail trying and retry.
193
193
* We will then see the same tree again and not find the symbol anymore.
@@ -197,9 +197,9 @@ abstract class GenJSCode extends plugins.PluginComponent
197
197
*/
198
198
val optDef = {
199
199
if (tryingToGenMethodAsJSFunction)
200
- lazilyGeneratedAnonClasses .get(sym)
200
+ lazilyGeneratedClasses .get(sym)
201
201
else
202
- lazilyGeneratedAnonClasses .remove(sym)
202
+ lazilyGeneratedClasses .remove(sym)
203
203
}
204
204
205
205
optDef.getOrElse {
@@ -233,7 +233,7 @@ abstract class GenJSCode extends plugins.PluginComponent
233
233
*
234
234
* Other ClassDefs are emitted according to their nature:
235
235
* * Scala.js-defined JS class -> `genScalaJSDefinedJSClass()`
236
- * * Other raw JS type (<: js.Any) -> `genRawJSClassData ()`
236
+ * * Other raw JS type (<: js.Any) -> `genRawJSClass ()`
237
237
* * Interface -> `genInterface()`
238
238
* * Implementation class -> `genImplClass()`
239
239
* * Normal class -> `genClass()`
@@ -249,8 +249,8 @@ abstract class GenJSCode extends plugins.PluginComponent
249
249
}
250
250
val allClassDefs = collectClassDefs(cunit.body)
251
251
252
- /* There are three types of anonymous classes we want to generate
253
- * only once we need them so we can inline them at construction site:
252
+ /* There are four types of classes we want to generate only once we need
253
+ * them so we can inline them at usage site:
254
254
*
255
255
* - lambdas for js.FunctionN and js.ThisFunctionN (SAMs). (We may not
256
256
* generate actual Scala classes for these).
@@ -260,33 +260,36 @@ abstract class GenJSCode extends plugins.PluginComponent
260
260
* - lambdas for scala.FunctionN. This is only an optimization and may
261
261
* fail. In the case of failure, we fall back to generating a
262
262
* fully-fledged Scala class.
263
+ * - Implementation classes for native JS classes. The only useful
264
+ * thing they contain are symbol forwarders for @JSSymbol access.
265
+ * We want to emit the symbol forwarders as static methods on the
266
+ * native JS class so we do not need to generate another class file.
263
267
*
264
268
* Since for all these, we don't know how they inter-depend, we just
265
269
* store them in a map at this point.
266
270
*/
267
- val (lazyAnons, fullClassDefs) = allClassDefs.partition { cd =>
271
+ def isRawJSImplClass (sym : Symbol ) = {
272
+ sym.isImplClass && isRawJSType(sym.owner.info.decl(
273
+ sym.name.dropRight(nme.IMPL_CLASS_SUFFIX .length)).tpe)
274
+ }
275
+
276
+ val (lazyClasses, fullClassDefs) = allClassDefs.partition { cd =>
268
277
val sym = cd.symbol
269
278
isRawJSFunctionDef(sym) || sym.isAnonymousFunction ||
270
- isScalaJSDefinedAnonJSClass(sym)
279
+ isScalaJSDefinedAnonJSClass(sym) || isRawJSImplClass(sym)
271
280
}
272
281
273
- lazilyGeneratedAnonClasses ++= lazyAnons .map(cd => cd.symbol -> cd)
282
+ lazilyGeneratedClasses ++= lazyClasses .map(cd => cd.symbol -> cd)
274
283
275
284
/* Finally, we emit true code for the remaining class defs. */
276
285
for (cd <- fullClassDefs) {
277
286
val sym = cd.symbol
278
287
implicit val pos = sym.pos
279
288
280
289
/* Do not actually emit code for primitive types nor scala.Array. */
281
- val isPrimitive =
282
- isPrimitiveValueClass(sym) || (sym == ArrayClass )
290
+ val isPrimitive = isPrimitiveValueClass(sym) || (sym == ArrayClass )
283
291
284
- /* Similarly, do not emit code for impl classes of raw JS traits. */
285
- val isRawJSImplClass =
286
- sym.isImplClass && isRawJSType(
287
- sym.owner.info.decl(sym.name.dropRight(nme.IMPL_CLASS_SUFFIX .length)).tpe)
288
-
289
- if (! isPrimitive && ! isRawJSImplClass) {
292
+ if (! isPrimitive) {
290
293
withScopedVars(
291
294
currentClassSym := sym,
292
295
unexpectedMutatedFields := mutable.Set .empty,
@@ -298,7 +301,7 @@ abstract class GenJSCode extends plugins.PluginComponent
298
301
if (! sym.isTraitOrInterface && isScalaJSDefinedJSClass(sym))
299
302
genScalaJSDefinedJSClass(cd)
300
303
else
301
- genRawJSClassData (cd)
304
+ genRawJSClass (cd)
302
305
} else if (sym.isTraitOrInterface) {
303
306
genInterface(cd)
304
307
} else if (sym.isImplClass) {
@@ -319,7 +322,7 @@ abstract class GenJSCode extends plugins.PluginComponent
319
322
genIRFile(cunit, sym, suffix, tree)
320
323
}
321
324
} finally {
322
- lazilyGeneratedAnonClasses .clear()
325
+ lazilyGeneratedClasses .clear()
323
326
generatedClasses.clear()
324
327
pos2irPosCache.clear()
325
328
}
@@ -531,7 +534,7 @@ abstract class GenJSCode extends plugins.PluginComponent
531
534
" Generating AnonSJSDefinedNew of non anonymous SJSDefined JS class" )
532
535
533
536
// Find the ClassDef for this anonymous class
534
- val classDef = consumeLazilyGeneratedAnonClass (sym)
537
+ val classDef = consumeLazilyGeneratedClass (sym)
535
538
536
539
// Generate a normal SJSDefinedJSClass
537
540
val origJsClass =
@@ -685,9 +688,8 @@ abstract class GenJSCode extends plugins.PluginComponent
685
688
686
689
// Generate the class data of a raw JS class -------------------------------
687
690
688
- /** Gen the IR ClassDef for a raw JS class or trait.
689
- */
690
- def genRawJSClassData (cd : ClassDef ): js.ClassDef = {
691
+ /** Gen the IR ClassDef for a raw JS class or trait. */
692
+ def genRawJSClass (cd : ClassDef ): js.ClassDef = {
691
693
val sym = cd.symbol
692
694
implicit val pos = sym.pos
693
695
@@ -704,8 +706,56 @@ abstract class GenJSCode extends plugins.PluginComponent
704
706
if (sym.isTraitOrInterface) None
705
707
else Some (jsNativeLoadSpecOf(sym))
706
708
709
+ lazy val implMethodsByName : Map [String , DefDef ] = {
710
+ val implClassDef = consumeLazilyGeneratedClass(sym.implClass)
711
+
712
+ def gen (tree : Tree ): List [(String , DefDef )] = tree match {
713
+ case Template (_, _, body) => body.flatMap(gen)
714
+
715
+ case dd : DefDef =>
716
+ List (dd.symbol.unexpandedName.encoded -> dd)
717
+
718
+ case _ => Nil
719
+ }
720
+
721
+ gen(implClassDef.impl).toMap
722
+ }
723
+
724
+ // Generates symbol forwarders
725
+ def gen (tree : Tree ): List [js.MethodDef ] = tree match {
726
+ case Template (_, _, body) => body.flatMap(gen)
727
+
728
+ case dd : DefDef if jsInterop.isSymbolForwarder(dd.symbol) =>
729
+ val sym = dd.symbol
730
+ val patchedDef = {
731
+ if (scalaUsesImplClasses && sym.owner.isTraitOrInterface) {
732
+ assert(sym.isDeferred, " Found non-abstract method in trait at " +
733
+ s " ${dd.pos}: ${sym.fullName}" )
734
+
735
+ /* We grab the body from the implementation class. This does not
736
+ * work so directly in general, since the parameter symbols and
737
+ * the `this` reference would be wrong in the body. Here it works,
738
+ * because we have a static, parameterless method.
739
+ */
740
+ val nrhs = implMethodsByName(sym.unexpandedName.encoded).rhs
741
+ treeCopy.DefDef (dd, dd.mods, dd.name, dd.tparams,
742
+ dd.vparamss, dd.tpt, nrhs)
743
+ } else {
744
+ assert(! sym.isDeferred, " Found an abstract symbol forwarder at " +
745
+ s " ${dd.pos}: ${sym.fullName}" )
746
+ dd // No patching necessary.
747
+ }
748
+ }
749
+
750
+ genMethod(patchedDef).toList
751
+
752
+ case _ => Nil
753
+ }
754
+
755
+ val generatedMethods = Hashers .hashDefs(gen(cd.impl))
756
+
707
757
js.ClassDef (classIdent, kind, superClass, genClassInterfaces(sym),
708
- jsNativeLoadSpec, Nil )(
758
+ jsNativeLoadSpec, generatedMethods )(
709
759
OptimizerHints .empty)
710
760
}
711
761
@@ -1255,7 +1305,8 @@ abstract class GenJSCode extends plugins.PluginComponent
1255
1305
if (scalaPrimitives.isPrimitive(sym) &&
1256
1306
! jsPrimitives.shouldEmitPrimitiveBody(sym)) {
1257
1307
None
1258
- } else if (isAbstractMethod(dd)) {
1308
+ } else if (isAbstractMethod(dd) && ! (scalaUsesImplClasses &&
1309
+ jsInterop.isSymbolForwarder(sym))) {
1259
1310
val body = if (scalaUsesImplClasses &&
1260
1311
sym.hasAnnotation(JavaDefaultMethodAnnotation )) {
1261
1312
/* For an interface method with @JavaDefaultMethod, make it a
@@ -1297,10 +1348,13 @@ abstract class GenJSCode extends plugins.PluginComponent
1297
1348
case _ => false
1298
1349
}
1299
1350
1351
+ val isSymbolForwarder = jsInterop.isSymbolForwarder(sym)
1352
+
1300
1353
val shouldMarkInline = {
1301
1354
sym.hasAnnotation(InlineAnnotationClass ) ||
1302
1355
sym.name.startsWith(nme.ANON_FUN_NAME ) ||
1303
- adHocInlineMethods.contains(sym.fullName)
1356
+ adHocInlineMethods.contains(sym.fullName) ||
1357
+ isSymbolForwarder
1304
1358
}
1305
1359
1306
1360
val shouldMarkNoinline = {
@@ -1328,8 +1382,9 @@ abstract class GenJSCode extends plugins.PluginComponent
1328
1382
Some (genStat(rhs)))(optimizerHints, None )
1329
1383
} else {
1330
1384
val resultIRType = toIRType(sym.tpe.resultType)
1331
- genMethodDef(static = sym.owner.isImplClass, methodName,
1332
- params, resultIRType, rhs, optimizerHints)
1385
+ val static = sym.owner.isImplClass || isSymbolForwarder
1386
+ genMethodDef(static, methodName, params, resultIRType, rhs,
1387
+ optimizerHints)
1333
1388
}
1334
1389
}
1335
1390
@@ -2173,10 +2228,10 @@ abstract class GenJSCode extends plugins.PluginComponent
2173
2228
} else if (isHijackedBoxedClass(clsSym)) {
2174
2229
genNewHijackedBoxedClass(clsSym, ctor, args map genExpr)
2175
2230
} else if (isRawJSFunctionDef(clsSym)) {
2176
- val classDef = consumeLazilyGeneratedAnonClass (clsSym)
2231
+ val classDef = consumeLazilyGeneratedClass (clsSym)
2177
2232
genRawJSFunction(classDef, args.map(genExpr))
2178
2233
} else if (clsSym.isAnonymousFunction) {
2179
- val classDef = consumeLazilyGeneratedAnonClass (clsSym)
2234
+ val classDef = consumeLazilyGeneratedClass (clsSym)
2180
2235
tryGenAnonFunctionClass(classDef, args.map(genExpr)).getOrElse {
2181
2236
// Cannot optimize anonymous function class. Generate full class.
2182
2237
generatedClasses +=
@@ -3949,7 +4004,8 @@ abstract class GenJSCode extends plugins.PluginComponent
3949
4004
def hasExplicitJSEncoding =
3950
4005
sym.hasAnnotation(JSNameAnnotation ) ||
3951
4006
sym.hasAnnotation(JSBracketAccessAnnotation ) ||
3952
- sym.hasAnnotation(JSBracketCallAnnotation )
4007
+ sym.hasAnnotation(JSBracketCallAnnotation ) ||
4008
+ sym.hasAnnotation(JSSymbolAnnotation )
3953
4009
3954
4010
val boxedResult = sym.name match {
3955
4011
case JSUnaryOpMethodName (code) if argc == 0 =>
@@ -3969,7 +4025,13 @@ abstract class GenJSCode extends plugins.PluginComponent
3969
4025
js.JSFunctionApply (receiver, args)
3970
4026
3971
4027
case _ =>
3972
- def jsFunName = js.StringLiteral (jsNameOf(sym))
4028
+ def jsFunName : js.Tree = {
4029
+ sym.getAnnotation(JSSymbolAnnotation ).fold[js.Tree ] {
4030
+ js.StringLiteral (jsNameOf(sym))
4031
+ } { annot =>
4032
+ genApplyStatic(annot.args(0 ).symbol, Nil )
4033
+ }
4034
+ }
3973
4035
3974
4036
def genSuperReference (propName : js.Tree ): js.Tree = {
3975
4037
superIn.fold[js.Tree ] {
0 commit comments