From d80b05791bb80be0220327fc5465d79a732662fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 10 Mar 2016 17:37:38 +0100 Subject: [PATCH 1/2] Fix #2287: Recognize default methods in StackTrace. --- library/src/main/scala/scala/scalajs/runtime/StackTrace.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/scala/scala/scalajs/runtime/StackTrace.scala b/library/src/main/scala/scala/scalajs/runtime/StackTrace.scala index 1583246abb..02e0be8c08 100644 --- a/library/src/main/scala/scala/scalajs/runtime/StackTrace.scala +++ b/library/src/main/scala/scala/scalajs/runtime/StackTrace.scala @@ -175,7 +175,7 @@ object StackTrace { */ private def extractClassMethod(functionName: String): (String, String) = { val PatC = """^(?:Object\.|\[object Object\]\.)?(?:ScalaJS\.c\.|\$c_)([^\.]+)(?:\.prototype)?\.([^\.]+)$""".re - val PatS = """^(?:Object\.|\[object Object\]\.)?(?:ScalaJS\.s\.|\$s_)((?:_[^_]|[^_])+)__([^\.]+)$""".re + val PatS = """^(?:Object\.|\[object Object\]\.)?(?:ScalaJS\.(?:s|f)\.|\$(?:s|f)_)((?:_[^_]|[^_])+)__([^\.]+)$""".re val PatM = """^(?:Object\.|\[object Object\]\.)?(?:ScalaJS\.m\.|\$m_)([^\.]+)$""".re var isModule = false From eefb2cbd57e89d482b3a0f3c87aff0626425362a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 10 Mar 2016 18:15:15 +0100 Subject: [PATCH 2/2] Fix #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. --- .../core/compiler/Compat210Component.scala | 22 ++++++++ .../org/scalajs/core/compiler/GenJSCode.scala | 51 ++++++++++++------- .../scalajs/core/compiler/GenJSExports.scala | 2 +- .../scalajs/core/compiler/JSEncoding.scala | 9 ++-- .../testsuite/library/StackTraceTest.scala | 7 +-- 5 files changed, 65 insertions(+), 26 deletions(-) diff --git a/compiler/src/main/scala/org/scalajs/core/compiler/Compat210Component.scala b/compiler/src/main/scala/org/scalajs/core/compiler/Compat210Component.scala index f416829bc4..ed74123170 100644 --- a/compiler/src/main/scala/org/scalajs/core/compiler/Compat210Component.scala +++ b/compiler/src/main/scala/org/scalajs/core/compiler/Compat210Component.scala @@ -27,6 +27,10 @@ trait Compat210Component { def originalName: Name = sys.error("infinite loop in Compat") def isLocalToBlock: Boolean = self.isLocal + + def implClass: Symbol = NoSymbol + + def isTraitOrInterface: Boolean = self.isTrait || self.isInterface } // enteringPhase/exitingPhase replace beforePhase/afterPhase @@ -58,6 +62,24 @@ trait Compat210Component { } } + // Impl classes disappeared in 2.12.0-M4 + + lazy val scalaUsesImplClasses: Boolean = + definitions.SeqClass.implClass != NoSymbol // a trait we know has an impl class + + implicit final class StdTermNamesCompat(self: global.nme.type) { + def IMPL_CLASS_SUFFIX: String = sys.error("No impl classes in this version") + + def isImplClassName(name: Name): Boolean = false + } + + implicit final class StdTypeNamesCompat(self: global.tpnme.type) { + def IMPL_CLASS_SUFFIX: String = sys.error("No impl classes in this version") + + def interfaceName(implname: Name): TypeName = + sys.error("No impl classes in this version") + } + /* global.genBCode.bTypes.initializeCoreBTypes() * * This one has a very particular history: diff --git a/compiler/src/main/scala/org/scalajs/core/compiler/GenJSCode.scala b/compiler/src/main/scala/org/scalajs/core/compiler/GenJSCode.scala index 8ea6bf913b..a172dcee85 100644 --- a/compiler/src/main/scala/org/scalajs/core/compiler/GenJSCode.scala +++ b/compiler/src/main/scala/org/scalajs/core/compiler/GenJSCode.scala @@ -247,11 +247,11 @@ abstract class GenJSCode extends plugins.PluginComponent val tree = if (isRawJSType(sym.tpe)) { assert(!isRawJSFunctionDef(sym), s"Raw JS function def should have been recorded: $cd") - if (!sym.isInterface && isScalaJSDefinedJSClass(sym)) + if (!sym.isTraitOrInterface && isScalaJSDefinedJSClass(sym)) genScalaJSDefinedJSClass(cd) else genRawJSClassData(cd) - } else if (sym.isInterface) { + } else if (sym.isTraitOrInterface) { genInterface(cd) } else if (sym.isImplClass) { genImplClass(cd) @@ -288,7 +288,7 @@ abstract class GenJSCode extends plugins.PluginComponent val sym = cd.symbol implicit val pos = sym.pos - assert(!sym.isInterface && !sym.isImplClass, + assert(!sym.isTraitOrInterface && !sym.isImplClass, "genClass() must be called only for normal classes: "+sym) assert(sym.superClass != NoSymbol, sym) @@ -496,10 +496,10 @@ abstract class GenJSCode extends plugins.PluginComponent val classIdent = encodeClassFullNameIdent(sym) val superClass = - if (sym.isInterface) None + if (sym.isTraitOrInterface) None else Some(encodeClassFullNameIdent(sym.superClass)) val jsName = - if (sym.isInterface || sym.isModuleClass) None + if (sym.isTraitOrInterface || sym.isModuleClass) None else Some(fullJSNameOf(sym)) js.ClassDef(classIdent, ClassKind.RawJSType, @@ -584,7 +584,7 @@ abstract class GenJSCode extends plugins.PluginComponent parent <- sym.info.parents typeSym = parent.typeSymbol _ = assert(typeSym != NoSymbol, "parent needs symbol") - if (typeSym.isInterface) + if typeSym.isTraitOrInterface } yield { encodeClassFullNameIdent(typeSym) } @@ -1045,11 +1045,22 @@ abstract class GenJSCode extends plugins.PluginComponent mutable = false, rest = false) } + /* When scalac uses impl classes, we cannot trust `rhs` to be + * `EmptyTree` for deferred methods (probably due to an internal bug + * of scalac), as can be seen in run/t6443.scala. + * However, when it does not use impl class anymore, we have to use + * `rhs == EmptyTree` as predicate, just like the JVM back-end does. + */ + def isAbstractMethod = + if (scalaUsesImplClasses) sym.isDeferred || sym.owner.isInterface + else rhs == EmptyTree + if (scalaPrimitives.isPrimitive(sym) && !jsPrimitives.shouldEmitPrimitiveBody(sym)) { None - } else if (sym.isDeferred || sym.owner.isInterface) { - val body = if (sym.hasAnnotation(JavaDefaultMethodAnnotation)) { + } else if (isAbstractMethod) { + val body = if (scalaUsesImplClasses && + sym.hasAnnotation(JavaDefaultMethodAnnotation)) { /* For an interface method with @JavaDefaultMethod, make it a * default method calling the impl class method. */ @@ -1075,7 +1086,7 @@ abstract class GenJSCode extends plugins.PluginComponent None } else if (sym.isClassConstructor && isHijackedBoxedClass(sym.owner)) { None - } else if (!sym.owner.isImplClass && + } else if (scalaUsesImplClasses && !sym.owner.isImplClass && sym.hasAnnotation(JavaDefaultMethodAnnotation)) { // Do not emit trait impl forwarders with @JavaDefaultMethod None @@ -1259,14 +1270,23 @@ abstract class GenJSCode extends plugins.PluginComponent } initialThis match { - case This(_) => + case Ident(_) => + // TODO Is this special-case really needed? + withScopedVars( + fakeTailJumpParamRepl := (thisDef.symbol, initialThis.symbol) + ) { + genInnerBody() + } + + case _ => val thisSym = thisDef.symbol if (thisSym.isMutable) mutableLocalVars += thisSym val thisLocalIdent = encodeLocalSym(thisSym) + val genRhs = genExpr(initialThis) val thisLocalVarDef = js.VarDef(thisLocalIdent, - currentClassType, thisSym.isMutable, genThis()) + currentClassType, thisSym.isMutable, genRhs) val innerBody = { withScopedVars( @@ -1277,13 +1297,6 @@ abstract class GenJSCode extends plugins.PluginComponent } js.Block(thisLocalVarDef, innerBody) - - case Ident(_) => - withScopedVars( - fakeTailJumpParamRepl := (thisDef.symbol, initialThis.symbol) - ) { - genInnerBody() - } } case _ => @@ -3803,7 +3816,7 @@ abstract class GenJSCode extends plugins.PluginComponent /** Gen JS code representing a JS class (subclass of js.Any) */ private def genPrimitiveJSClass(sym: Symbol)( implicit pos: Position): js.Tree = { - assert(!isStaticModule(sym) && !sym.isInterface, + assert(!isStaticModule(sym) && !sym.isTraitOrInterface, s"genPrimitiveJSClass called with non-class $sym") js.LoadJSConstructor(jstpe.ClassType(encodeClassFullName(sym))) } diff --git a/compiler/src/main/scala/org/scalajs/core/compiler/GenJSExports.scala b/compiler/src/main/scala/org/scalajs/core/compiler/GenJSExports.scala index d0fc7aac7a..6caa82c99b 100644 --- a/compiler/src/main/scala/org/scalajs/core/compiler/GenJSExports.scala +++ b/compiler/src/main/scala/org/scalajs/core/compiler/GenJSExports.scala @@ -727,7 +727,7 @@ trait GenJSExports extends SubComponent { self: GenJSCode => private def isOverridingExport(sym: Symbol): Boolean = { lazy val osym = sym.nextOverriddenSymbol - sym.isOverridingSymbol && !osym.owner.isInterface + sym.isOverridingSymbol && !osym.owner.isTraitOrInterface } private sealed abstract class RTTypeTest diff --git a/compiler/src/main/scala/org/scalajs/core/compiler/JSEncoding.scala b/compiler/src/main/scala/org/scalajs/core/compiler/JSEncoding.scala index 347e6e4ac5..a35217f9ec 100644 --- a/compiler/src/main/scala/org/scalajs/core/compiler/JSEncoding.scala +++ b/compiler/src/main/scala/org/scalajs/core/compiler/JSEncoding.scala @@ -102,7 +102,7 @@ trait JSEncoding extends SubComponent { self: GenJSCode => */ val idSuffix = if (sym.isPrivate || allRefClasses.contains(sym.owner)) - sym.owner.ancestors.count(!_.isInterface).toString + sym.owner.ancestors.count(!_.isTraitOrInterface).toString else "f" @@ -148,12 +148,15 @@ trait JSEncoding extends SubComponent { self: GenJSCode => def name = encodeMemberNameInternal(sym) + def privateSuffix(owner: Symbol): String = + if (owner.isTraitOrInterface && !owner.isImplClass) encodeClassFullName(owner) + else owner.ancestors.count(!_.isTraitOrInterface).toString + val encodedName = { if (sym.isClassConstructor) "init" + InnerSep else if (sym.isPrivate) - mangleJSName(name) + OuterSep + "p" + - sym.owner.ancestors.count(!_.isInterface).toString + mangleJSName(name) + OuterSep + "p" + privateSuffix(sym.owner) else mangleJSName(name) } diff --git a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/StackTraceTest.scala b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/StackTraceTest.scala index e34092fd37..17d322749c 100644 --- a/test-suite/js/src/test/scala/org/scalajs/testsuite/library/StackTraceTest.scala +++ b/test-suite/js/src/test/scala/org/scalajs/testsuite/library/StackTraceTest.scala @@ -30,7 +30,8 @@ class StackTraceTest { assertTrue(trace exists { elem => /* We use startsWith for class name because some VMs will add * additional information at the end of the class name, for some - * reason. + * reason + there can be a '$class' suffix for methods in impl + * classes (when default methods are not used by scalac). */ val prefix = "org.scalajs.testsuite.library.StackTraceTest$" (elem.getClassName.startsWith(prefix + className) && @@ -57,11 +58,11 @@ class StackTraceTest { new Bar().g(7) } - verifyClassMethodNames("Foo" -> "f", "FooTrait$class" -> "h") { + verifyClassMethodNames("Foo" -> "f", "FooTrait" -> "h") { new Foo().h(78) } - verifyClassMethodNames("Foo" -> "f", "FooTrait$class" -> "h", + verifyClassMethodNames("Foo" -> "f", "FooTrait" -> "h", "Baz" -> "") { new Baz() }