8000 Fix #2285: Adapt to 2.12 trait encoding (removal of impl classes). · sjrd/scala-js@e6eefd8 · GitHub
Skip to content

Commit e6eefd8

Browse files
committed
Fix scala-js#2285: Adapt to 2.12 trait encoding (removal of impl classes).
The main changes are actually related to keeping source compatibility with older versions, through more compat hacks. Also: * Use isTraitOrInterface instead of isInterface to identify something that should be an interface at the IR level. * A method is abstract iff its body is EmptyTree.
1 parent d80b057 commit e6eefd8

File tree

5 files changed

+65
-26
lines changed

5 files changed

+65
-26
lines changed

compiler/src/main/scala/org/scalajs/core/compiler/Compat210Component.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ trait Compat210Component {
2727
def originalName: Name = sys.error("infinite loop in Compat")
2828

2929
def isLocalToBlock: Boolean = self.isLocal
30+
31+
def implClass: Symbol = NoSymbol
32+
33+
def isTraitOrInterface: Boolean = self.isTrait || self.isInterface
3034
}
3135

3236
// enteringPhase/exitingPhase replace beforePhase/afterPhase
@@ -58,6 +62,24 @@ trait Compat210Component {
5862
}
5963
}
6064

65+
// Impl classes disappeared in 2.12.0-M4
66+
67+
lazy val scalaUsesImplClasses: Boolean =
68+
definitions.SeqClass.implClass != NoSymbol // a trait we know has an impl class
69+
70+
implicit final class StdTermNamesCompat(self: global.nme.type) {
71+
def IMPL_CLASS_SUFFIX: String = sys.error("No impl classes in this version")
72+
73+
def isImplClassName(name: Name): Boolean = false
74+
}
75+
76+
implicit final class StdTypeNamesCompat(self: global.tpnme.type) {
77+
def IMPL_CLASS_SUFFIX: String = sys.error("No impl classes in this version")
78+
79+
def interfaceName(implname: Name): TypeName =
80+
sys.error("No impl classes in this version")
81+
}
82+
6183
/* global.genBCode.bTypes.initializeCoreBTypes()
6284
*
6385
* This one has a very particular history:

compiler/src/main/scala/org/scalajs/core/compiler/GenJSCode.scala

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -247,11 +247,11 @@ abstract class GenJSCode extends plugins.PluginComponent
247247
val tree = if (isRawJSType(sym.tpe)) {
248248
assert(!isRawJSFunctionDef(sym),
249249
s"Raw JS function def should have been recorded: $cd")
250-
if (!sym.isInterface && isScalaJSDefinedJSClass(sym))
250+
if (!sym.isTraitOrInterface && isScalaJSDefinedJSClass(sym))
251251
genScalaJSDefinedJSClass(cd)
252252
else
253253
genRawJSClassData(cd)
254-
} else if (sym.isInterface) {
254+
} else if (sym.isTraitOrInterface) {
255255
genInterface(cd)
256256
} else if (sym.isImplClass) {
257257
genImplClass(cd)
@@ -288,7 +288,7 @@ abstract class GenJSCode extends plugins.PluginComponent
288288
val sym = cd.symbol
289289
implicit val pos = sym.pos
290290

291-
assert(!sym.isInterface && !sym.isImplClass,
291+
assert(!sym.isTraitOrInterface && !sym.isImplClass,
292292
"genClass() must be called only for normal classes: "+sym)
293293
assert(sym.superClass != NoSymbol, sym)
294294

@@ -496,10 +496,10 @@ abstract class GenJSCode extends plugins.PluginComponent
496496

497497
val classIdent = encodeClassFullNameIdent(sym)
498498
val superClass =
499-
if (sym.isInterface) None
499+
if (sym.isTraitOrInterface) None
500500
else Some(encodeClassFullNameIdent(sym.superClass))
501501
val jsName =
502-
if (sym.isInterface || sym.isModuleClass) None
502+
if (sym.isTraitOrInterface || sym.isModuleClass) None
503503
else Some(fullJSNameOf(sym))
504504

505505
js.ClassDef(classIdent, ClassKind.RawJSType,
@@ -584,7 +584,7 @@ abstract class GenJSCode extends plugins.PluginComponent
584584
parent <- sym.info.parents
585585
typeSym = parent.typeSymbol
586586
_ = assert(typeSym != NoSymbol, "parent needs symbol")
587-
if (typeSym.isInterface)
587+
if typeSym.isTraitOrInterface
588588
} yield {
589589
encodeClassFullNameIdent(typeSym)
590590
}
@@ -1045,11 +1045,22 @@ abstract class GenJSCode extends plugins.PluginComponent
10451045
mutable = false, rest = false)
10461046
}
10471047

1048+
/* When scalac uses impl classes, we cannot trust `rhs` to be
1049+
* `EmptyTree` for deferred methods (probably due to an internal bug
1050+
* of scalac), as can be seen in run/t6443.scala.
1051+
* However, when it does not use impl class anymore, we have to use
1052+
* `rhs == EmptyTree` as predicate, just like the JVM back-end does.
1053+
*/
1054+
def isAbstractMethod =
1055+
if (scalaUsesImplClasses) sym.isDeferred || sym.owner.isInterface
1056+
else rhs == EmptyTree
1057+
10481058
if (scalaPrimitives.isPrimitive(sym) &&
10491059
!jsPrimitives.shouldEmitPrimitiveBody(sym)) {
10501060
None
1051-
} else if (sym.isDeferred || sym.owner.isInterface) {
1052-
val body = if (sym.hasAnnotation(JavaDefaultMethodAnnotation)) {
1061+
} else if (isAbstractMethod) {
1062+
val body = if (scalaUsesImplClasses &&
1063+
sym.hasAnnotation(JavaDefaultMethodAnnotation)) {
10531064
/* For an interface method with @JavaDefaultMethod, make it a
10541065
* default method calling the impl class method.
10551066
*/
@@ -1075,7 +1086,7 @@ abstract class GenJSCode extends plugins.PluginComponent
10751086
None
10761087
} else if (sym.isClassConstructor && isHijackedBoxedClass(sym.owner)) {
10771088
None
1078-
} else if (!sym.owner.isImplClass &&
1089+
} else if (scalaUsesImplClasses && !sym.owner.isImplClass &&
10791090
sym.hasAnnotation(JavaDefaultMethodAnnotation)) {
10801091
// Do not emit trait impl forwarders with @JavaDefaultMethod
10811092
None
@@ -1259,14 +1270,23 @@ abstract class GenJSCode extends plugins.PluginComponent
12591270
}
12601271

12611272
initialThis match {
1262-
case This(_) =>
1273+
case Ident(_) =>
1274+
// TODO Is this special-case really needed?
1275+
withScopedVars(
1276+
fakeTailJumpParamRepl := (thisDef.symbol, initialThis.symbol)
1277+
) {
1278+
genInnerBody()
1279+
}
1280+
1281+
case _ =>
12631282
val thisSym = thisDef.symbol
12641283
if (thisSym.isMutable)
12651284
mutableLocalVars += thisSym
12661285

12671286
val thisLocalIdent = encodeLocalSym(thisSym)
1287+
val genRhs = genExpr(initialThis)
12681288
val thisLocalVarDef = js.VarDef(thisLocalIdent,
1269-
currentClassType, thisSym.isMutable, genThis())
1289+
currentClassType, thisSym.isMutable, genRhs)
12701290

12711291
val innerBody = {
12721292
withScopedVars(
@@ -1277,13 +1297,6 @@ abstract class 10000 GenJSCode extends plugins.PluginComponent
12771297
}
12781298

12791299
js.Block(thisLocalVarDef, innerBody)
1280-
1281-
case Ident(_) =>
1282-
withScopedVars(
1283-
fakeTailJumpParamRepl := (thisDef.symbol, initialThis.symbol)
1284-
) {
1285-
genInnerBody()
1286-
}
12871300
}
12881301

12891302
case _ =>
@@ -3803,7 +3816,7 @@ abstract class GenJSCode extends plugins.PluginComponent
38033816
/** Gen JS code representing a JS class (subclass of js.Any) */
38043817
private def genPrimitiveJSClass(sym: Symbol)(
38053818
implicit pos: Position): js.Tree = {
3806-
assert(!isStaticModule(sym) && !sym.isInterface,
3819+
assert(!isStaticModule(sym) && !sym.isTraitOrInterface,
38073820
s"genPrimitiveJSClass called with non-class $sym")
38083821
js.LoadJSConstructor(jstpe.ClassType(encodeClassFullName(sym)))
38093822
}

compiler/src/main/scala/org/scalajs/core/compiler/GenJSExports.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ trait GenJSExports extends SubComponent { self: GenJSCode =>
727727

728728
private def isOverridingExport(sym: Symbol): Boolean = {
729729
lazy val osym = sym.nextOverriddenSymbol
730-
sym.isOverridingSymbol && !osym.owner.isInterface
730+
sym.isOverridingSymbol && !osym.owner.isTraitOrInterface
731731
}
732732

733733
private sealed abstract class RTTypeTest

compiler/src/main/scala/org/scalajs/core/compiler/JSEncoding.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ trait JSEncoding extends SubComponent { self: GenJSCode =>
102102
*/
103103
val idSuffix =
104104
if (sym.isPrivate || allRefClasses.contains(sym.owner))
105-
sym.owner.ancestors.count(!_.isInterface).toString
105+
sym.owner.ancestors.count(!_.isTraitOrInterface).toString
106106
else
107107
"f"
108108

@@ -148,12 +148,15 @@ trait JSEncoding extends SubComponent { self: GenJSCode =>
148148

149149
def name = encodeMemberNameInternal(sym)
150150

151+
def privateSuffix: String =
152+
if (sym.owner.isTraitOrInterface) encodeClassFullName(sym.owner)
153+
else sym.owner.ancestors.count(!_.isTraitOrInterface).toString
154+
151155
val encodedName = {
152156
if (sym.isClassConstructor)
153157
"init" + InnerSep
154158
else if (sym.isPrivate)
155-
mangleJSName(name) + OuterSep + "p" +
156-
sym.owner.ancestors.count(!_.isInterface).toString
159+
mangleJSName(name) + OuterSep + "p" + privateSuffix
157160
else
158161
mangleJSName(name)
159162
}

test-suite/js/src/test/scala/org/scalajs/testsuite/library/StackTraceTest.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class StackTraceTest {
3030
assertTrue(trace exists { elem =>
3131
/* We use startsWith for class name because some VMs will add
3232
* additional information at the end of the class name, for some
33-
* reason.
33+
* reason + there can be a '$class' suffix for methods in impl
34+
* classes (when default methods are not used by scalac).
3435
*/
3536
val prefix = "org.scalajs.testsuite.library.StackTraceTest$"
3637
(elem.getClassName.startsWith(prefix + className) &&
@@ -57,11 +58,11 @@ class StackTraceTest {
5758
new Bar().g(7)
5859
}
5960

60-
verifyClassMethodNames("Foo" -> "f", "FooTrait$class" -> "h") {
61+
verifyClassMethodNames("Foo" -> "f", "FooTrait" -> "h") {
6162
< 5FB9 span class="pl-k">new Foo().h(78)
6263
}
6364

64-
verifyClassMethodNames("Foo" -> "f", "FooTrait$class" -> "h",
65+
verifyClassMethodNames("Foo" -> "f", "FooTrait" -> "h",
6566
"Baz" -> "<init>") {
6667
new Baz()
6768
}

0 commit comments

Comments
 (0)
0