@@ -183,11 +183,11 @@ abstract class GenJSCode extends plugins.PluginComponent
183183
184184 // Global class generation state -------------------------------------------
185185
186- private val lazilyGeneratedAnonClasses = mutable.Map .empty[Symbol , ClassDef ]
186+ private val lazilyGeneratedClasses = mutable.Map .empty[Symbol , ClassDef ]
187187 private val generatedClasses =
188188 ListBuffer .empty[(Symbol , Option [String ], js.ClassDef )]
189189
190- private def consumeLazilyGeneratedAnonClass (sym : Symbol ): ClassDef = {
190+ private def consumeLazilyGeneratedClass (sym : Symbol ): ClassDef = {
191191 /* If we are trying to generate an method as JSFunction, we cannot
192192 * actually consume the symbol, since we might fail trying and retry.
193193 * We will then see the same tree again and not find the symbol anymore.
@@ -197,9 +197,9 @@ abstract class GenJSCode extends plugins.PluginComponent
197197 */
198198 val optDef = {
199199 if (tryingToGenMethodAsJSFunction)
200- lazilyGeneratedAnonClasses .get(sym)
200+ lazilyGeneratedClasses .get(sym)
201201 else
202- lazilyGeneratedAnonClasses .remove(sym)
202+ lazilyGeneratedClasses .remove(sym)
203203 }
204204
205205 optDef.getOrElse {
@@ -233,7 +233,7 @@ abstract class GenJSCode extends plugins.PluginComponent
233233 *
234234 * Other ClassDefs are emitted according to their nature:
235235 * * Scala.js-defined JS class -> `genScalaJSDefinedJSClass()`
236- * * Other raw JS type (<: js.Any) -> `genRawJSClassData ()`
236+ * * Other raw JS type (<: js.Any) -> `genRawJSClass ()`
237237 * * Interface -> `genInterface()`
238238 * * Implementation class -> `genImplClass()`
239239 * * Normal class -> `genClass()`
@@ -249,8 +249,8 @@ abstract class GenJSCode extends plugins.PluginComponent
249249 }
250250 val allClassDefs = collectClassDefs(cunit.body)
251251
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:
254254 *
255255 * - lambdas for js.FunctionN and js.ThisFunctionN (SAMs). (We may not
256256 * generate actual Scala classes for these).
@@ -260,33 +260,36 @@ abstract class GenJSCode extends plugins.PluginComponent
260260 * - lambdas for scala.FunctionN. This is only an optimization and may
261261 * fail. In the case of failure, we fall back to generating a
262262 * 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.
263267 *
264268 * Since for all these, we don't know how they inter-depend, we just
265269 * store them in a map at this point.
266270 */
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 =>
268277 val sym = cd.symbol
269278 isRawJSFunctionDef(sym) || sym.isAnonymousFunction ||
270- isScalaJSDefinedAnonJSClass(sym)
279+ isScalaJSDefinedAnonJSClass(sym) || isRawJSImplClass(sym)
271280 }
272281
273- lazilyGeneratedAnonClasses ++= lazyAnons .map(cd => cd.symbol -> cd)
282+ lazilyGeneratedClasses ++= lazyClasses .map(cd => cd.symbol -> cd)
274283
275284 /* Finally, we emit true code for the remaining class defs. */
276285 for (cd <- fullClassDefs) {
277286 val sym = cd.symbol
278287 implicit val pos = sym.pos
279288
280289 /* 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 )
283291
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) {
290293 withScopedVars(
291294 currentClassSym := sym,
292295 unexpectedMutatedFields := mutable.Set .empty,
@@ -298,7 +301,7 @@ abstract class GenJSCode extends plugins.PluginComponent
298301 if (! sym.isTraitOrInterface && isScalaJSDefinedJSClass(sym))
299302 genScalaJSDefinedJSClass(cd)
300303 else
301- genRawJSClassData (cd)
304+ genRawJSClass (cd)
302305 } else if (sym.isTraitOrInterface) {
303306 genInterface(cd)
304307 } else if (sym.isImplClass) {
@@ -319,7 +322,7 @@ abstract class GenJSCode extends plugins.PluginComponent
319322 genIRFile(cunit, sym, suffix, tree)
320323 }
321324 } finally {
322- lazilyGeneratedAnonClasses .clear()
325+ lazilyGeneratedClasses .clear()
323326 generatedClasses.clear()
324327 pos2irPosCache.clear()
325328 }
@@ -531,7 +534,7 @@ abstract class GenJSCode extends plugins.PluginComponent
531534 " Generating AnonSJSDefinedNew of non anonymous SJSDefined JS class" )
532535
533536 // Find the ClassDef for this anonymous class
534- val classDef = consumeLazilyGeneratedAnonClass (sym)
537+ val classDef = consumeLazilyGeneratedClass (sym)
535538
536539 // Generate a normal SJSDefinedJSClass
537540 val origJsClass =
@@ -685,9 +688,8 @@ abstract class GenJSCode extends plugins.PluginComponent
685688
686689 // Generate the class data of a raw JS class -------------------------------
687690
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 = {
691693 val sym = cd.symbol
692694 implicit val pos = sym.pos
693695
@@ -704,8 +706,56 @@ abstract class GenJSCode extends plugins.PluginComponent
704706 if (sym.isTraitOrInterface) None
705707 else Some (jsNativeLoadSpecOf(sym))
706708
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+
707757 js.ClassDef (classIdent, kind, superClass, genClassInterfaces(sym),
708- jsNativeLoadSpec, Nil )(
758+ jsNativeLoadSpec, generatedMethods )(
709759 OptimizerHints .empty)
710760 }
711761
@@ -1255,7 +1305,8 @@ abstract class GenJSCode extends plugins.PluginComponent
12551305 if (scalaPrimitives.isPrimitive(sym) &&
12561306 ! jsPrimitives.shouldEmitPrimitiveBody(sym)) {
12571307 None
1258- } else if (isAbstractMethod(dd)) {
1308+ } else if (isAbstractMethod(dd) && ! (scalaUsesImplClasses &&
1309+ jsInterop.isSymbolForwarder(sym))) {
12591310 val body = if (scalaUsesImplClasses &&
12601311 sym.hasAnnotation(JavaDefaultMethodAnnotation )) {
12611312 /* For an interface method with @JavaDefaultMethod, make it a
@@ -1297,10 +1348,13 @@ abstract class GenJSCode extends plugins.PluginComponent
12971348 case _ => false
12981349 }
12991350
1351+ val isSymbolForwarder = jsInterop.isSymbolForwarder(sym)
1352+
13001353 val shouldMarkInline = {
13011354 sym.hasAnnotation(InlineAnnotationClass ) ||
13021355 sym.name.startsWith(nme.ANON_FUN_NAME ) ||
1303- adHocInlineMethods.contains(sym.fullName)
1356+ adHocInlineMethods.contains(sym.fullName) ||
1357+ isSymbolForwarder
13041358 }
13051359
13061360 val shouldMarkNoinline = {
@@ -1328,8 +1382,9 @@ abstract class GenJSCode extends plugins.PluginComponent
13281382 Some (genStat(rhs)))(optimizerHints, None )
13291383 } else {
13301384 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)
13331388 }
13341389 }
13351390
@@ -2173,10 +2228,10 @@ abstract class GenJSCode extends plugins.PluginComponent
21732228 } else if (isHijackedBoxedClass(clsSym)) {
21742229 genNewHijackedBoxedClass(clsSym, ctor, args map genExpr)
21752230 } else if (isRawJSFunctionDef(clsSym)) {
2176- val classDef = consumeLazilyGeneratedAnonClass (clsSym)
2231+ val classDef = consumeLazilyGeneratedClass (clsSym)
21772232 genRawJSFunction(classDef, args.map(genExpr))
21782233 } else if (clsSym.isAnonymousFunction) {
2179- val classDef = consumeLazilyGeneratedAnonClass (clsSym)
2234+ val classDef = consumeLazilyGeneratedClass (clsSym)
21802235 tryGenAnonFunctionClass(classDef, args.map(genExpr)).getOrElse {
21812236 // Cannot optimize anonymous function class. Generate full class.
21822237 generatedClasses +=
@@ -3949,7 +4004,8 @@ abstract class GenJSCode extends plugins.PluginComponent
39494004 def hasExplicitJSEncoding =
39504005 sym.hasAnnotation(JSNameAnnotation ) ||
39514006 sym.hasAnnotation(JSBracketAccessAnnotation ) ||
3952- sym.hasAnnotation(JSBracketCallAnnotation )
4007+ sym.hasAnnotation(JSBracketCallAnnotation ) ||
4008+ sym.hasAnnotation(JSSymbolAnnotation )
39534009
39544010 val boxedResult = sym.name match {
39554011 case JSUnaryOpMethodName (code) if argc == 0 =>
@@ -3969,7 +4025,13 @@ abstract class GenJSCode extends plugins.PluginComponent
39694025 js.JSFunctionApply (receiver, args)
39704026
39714027 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+ }
39734035
39744036 def genSuperReference (propName : js.Tree ): js.Tree = {
39754037 superIn.fold[js.Tree ] {
0 commit comments