summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2015-02-02 15:37:24 +0100
committerLukas Rytz <lukas.rytz@gmail.com>2015-02-07 07:45:51 +0100
commitcd8f2f327106c7e2944afa7ac8b7675262626c1e (patch)
treec4d820e34fd0a975655d7b30f0975e30fe990fdd /src/compiler
parent1437aa88241d5e99900a86b25c6ce385c2708570 (diff)
downloadscala-cd8f2f327106c7e2944afa7ac8b7675262626c1e.tar.gz
scala-cd8f2f327106c7e2944afa7ac8b7675262626c1e.tar.bz2
scala-cd8f2f327106c7e2944afa7ac8b7675262626c1e.zip
Fix InnerClass/EnclosingMethod for trait impl and specialized classes
Trait implementation classes and specialized classes are always considered top-level in terms of the InnerClass / EnclosingMethod attributes. These attributes describe source-level properties, and such classes are a compilation artifact. Note that the same is true for delambdafy:method closure classes (they are always top-level).
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala22
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala52
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala27
5 files changed, 90 insertions, 39 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
index 09d78fdf41..fb9804be7f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
@@ -22,6 +22,14 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
}
/**
+ * True for classes generated by the Scala compiler that are considered top-level in terms of
+ * the InnerClass / EnclosingMethod classfile attributes. See comment in BTypes.
+ */
+ def considerAsTopLevelImplementationArtifact(classSym: Symbol) = {
+ classSym.isImplClass || classSym.isSpecialized
+ }
+
+ /**
* Cache the value of delambdafy == "inline" for each run. We need to query this value many
* times, so caching makes sense.
*/
@@ -148,7 +156,10 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
if (sym.isClass) sym
else enclosingClass(nextEnclosing(sym))
}
- enclosingClass(nextEnclosing(classSym))
+ val r = enclosingClass(nextEnclosing(classSym))
+ // this should be an assertion, but we are more cautious for now as it was introduced before the 2.11.6 minor release
+ if (considerAsTopLevelImplementationArtifact(r)) devWarning(s"enclosing class of $classSym should not be an implementation artifact class: $r")
+ r
}
final case class EnclosingMethodEntry(owner: String, name: String, methodDescriptor: String)
@@ -162,7 +173,8 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
* on the implementation of GenASM / GenBCode, so they need to be passed in.
*/
def enclosingMethodAttribute(classSym: Symbol, classDesc: Symbol => String, methodDesc: Symbol => String): Option[EnclosingMethodEntry] = {
- if (isAnonymousOrLocalClass(classSym)) {
+ // trait impl classes are always top-level, see comment in BTypes
+ if (isAnonymousOrLocalClass(classSym) && !considerAsTopLevelImplementationArtifact(classSym)) {
val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym)
debuglog(s"enclosing method for $classSym is $methodOpt (in ${methodOpt.map(_.enclClass)})")
Some(EnclosingMethodEntry(
@@ -191,11 +203,13 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
* The member classes of a class symbol. Note that the result of this method depends on the
* current phase, for example, after lambdalift, all local classes become member of the enclosing
* class.
+ *
+ * Impl classes are always considered top-level, see comment in BTypes.
*/
- def memberClassesOf(classSymbol: Symbol): List[Symbol] = classSymbol.info.decls.collect({
- case sym if sym.isClass =>
+ def memberClassesForInnerClassTable(classSymbol: Symbol): List[Symbol] = classSymbol.info.decls.collect({
+ case sym if sym.isClass && !considerAsTopLevelImplementationArtifact(sym) =>
sym
- case sym if sym.isModule =>
+ case sym if sym.isModule && !considerAsTopLevelImplementationArtifact(sym) => // impl classes get the lateMODULE flag in mixin
val r = exitingPickler(sym.moduleClass)
assert(r != NoSymbol, sym.fullLocationString)
r
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index 8d1c37532e..ccee230191 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -362,8 +362,8 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
else if (sym == definitions.NullClass) RT_NULL
else {
val r = classBTypeFromSymbol(sym)
- if (r.isNestedClass) innerClassBufferASM += r
- r
+ if (r.isNestedClass) innerClassBufferASM += r
+ r
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index 7814ed858b..a194fe8fe4 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -634,6 +634,28 @@ abstract class BTypes {
* }
* }
*
+ *
+ * Traits Members
+ * --------------
+ *
+ * Some trait methods don't exist in the generated interface, but only in the implementation class
+ * (private methods in traits for example). Since EnclosingMethod expresses a source-level property,
+ * but the source-level enclosing method doesn't exist in the classfile, we the enclosing method
+ * is null (the enclosing class is still emitted).
+ * See BCodeAsmCommon.considerAsTopLevelImplementationArtifact
+ *
+ *
+ * Implementation Classes, Specialized Classes, Delambdafy:method closure classes
+ * ------------------------------------------------------------------------------
+ *
+ * Trait implementation classes and specialized classes are always considered top-level. Again,
+ * the InnerClass / EnclosingMethod attributes describe a source-level properties. The impl
+ * classes are compilation artifacts.
+ *
+ * The same is true for delambdafy:method closure classes. These classes are generated at
+ * top-level in the delambdafy phase, no special support is required in the backend.
+ *
+ *
* Mirror Classes
* --------------
*
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index bc880e002e..52d5cafc9c 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -129,35 +129,42 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
// member classes right after lambdalift, we obtain all nested classes, including local and
// anonymous ones.
val nestedClasses = {
- val nested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesOf(classSym))
+ val nested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(classSym))
if (isTopLevelModuleClass(classSym)) {
// For Java compatibility, member classes of top-level objects are treated as members of
// the top-level companion class, see comment below.
- val members = exitingPickler(memberClassesOf(classSym))
+ val members = exitingPickler(memberClassesForInnerClassTable(classSym))
nested diff members
} else {
nested
}
}
- // If this is a top-level class, the member classes of the companion object are added as
- // members of the class. For example:
- // class C { }
- // object C {
- // class D
- // def f = { class E }
- // }
- // The class D is added as a member of class C. The reason is: for Java compatibility, the
- // InnerClass attribute for D has "C" (NOT the module class "C$") as the outer class of D
- // (done by buildNestedInfo). See comment in BTypes.
- // For consistency, the InnerClass entry for D needs to be present in C - to Java it looks
- // like D is a member of C, not C$.
- val linkedClass = exitingPickler(classSym.linkedClassOfClass) // linkedCoC does not work properly in late phases
- val companionModuleMembers = {
- // phase travel to exitingPickler: this makes sure that memberClassesOf only sees member classes,
- // not local classes of the companion module (E in the exmaple) that were lifted by lambdalift.
- if (isTopLevelModuleClass(linkedClass)) exitingPickler(memberClassesOf(linkedClass))
- else Nil
+ val companionModuleMembers = if (considerAsTopLevelImplementationArtifact(classSym)) Nil else {
+ // If this is a top-level non-impl (*) class, the member classes of the companion object are
+ // added as members of the class. For example:
+ // class C { }
+ // object C {
+ // class D
+ // def f = { class E }
+ // }
+ // The class D is added as a member of class C. The reason is: for Java compatibility, the
+ // InnerClass attribute for D has "C" (NOT the module class "C$") as the outer class of D
+ // (done by buildNestedInfo). See comment in BTypes.
+ // For consistency, the InnerClass entry for D needs to be present in C - to Java it looks
+ // like D is a member of C, not C$.
+ //
+ // (*) We exclude impl classes: if the classfile for the impl class exists on the classpath,
+ // a linkedClass symbol is found for which isTopLevelModule is true, so we end up searching
+ // members of that weird impl-class-module-class-symbol. that search probably cannot return
+ // any classes, but it's better to exclude it.
+ val linkedClass = exitingPickler(classSym.linkedClassOfClass) // linkedCoC does not work properly in late phases
+ if (linkedClass != NoSymbol && isTopLevelModuleClass(linkedClass))
+ // phase travel to exitingPickler: this makes sure that memberClassesForInnerClassTable only sees member
+ // classes, not local classes of the companion module (E in the exmaple) that were lifted by lambdalift.
+ exitingPickler(memberClassesForInnerClassTable(linkedClass))
+ else
+ Nil
}
nestedClasses ++ companionModuleMembers
@@ -191,7 +198,8 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
assert(innerClassSym.isClass, s"Cannot build NestedInfo for non-class symbol $innerClassSym")
val isTopLevel = innerClassSym.rawowner.isPackageClass
- if (isTopLevel) None
+ // impl classes are considered top-level, see comment in BTypes
+ if (isTopLevel || considerAsTopLevelImplementationArtifact(innerClassSym)) None
else {
// See comment in BTypes, when is a class marked static in the InnerClass table.
val isStaticNestedClass = isOriginallyStaticOwner(innerClassSym.originalOwner)
@@ -248,7 +256,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
classBTypeFromInternalName.getOrElse(internalName, {
val c = ClassBType(internalName)
// class info consistent with BCodeHelpers.genMirrorClass
- val nested = exitingPickler(memberClassesOf(moduleClassSym)) map classBTypeFromSymbol
+ val nested = exitingPickler(memberClassesForInnerClassTable(moduleClassSym)) map classBTypeFromSymbol
c.info = ClassInfo(
superClass = Some(ObjectReference),
interfaces = Nil,
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index c36afd018b..0543929aef 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -595,7 +595,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
val x = innerClassSymbolFor(s)
if(x ne NoSymbol) {
assert(x.isClass, "not an inner-class symbol")
- val isInner = !x.rawowner.isPackageClass
+ // impl classes are considered top-level, see comment in BTypes
+ val isInner = !considerAsTopLevelImplementationArtifact(s) && !x.rawowner.isPackageClass
if (isInner) {
innerClassBuffer += x
collectInnerClass(x.rawowner)
@@ -700,23 +701,29 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
}
innerClassBuffer ++= {
- val members = exitingPickler(memberClassesOf(csym))
+ val members = exitingPickler(memberClassesForInnerClassTable(csym))
// lambdalift makes all classes (also local, anonymous) members of their enclosing class
- val allNested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesOf(csym))
+ val allNested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(csym))
// for the mirror class, we take the members of the companion module class (Java compat,
// see doc in BTypes.scala). for module classes, we filter out those members.
- if (isMirror) members
+ if (isMirror) members
else if (isTopLevelModule(csym)) allNested diff members
else allNested
}
- // If this is a top-level class, add members of the companion object.
- val linkedClass = exitingPickler(csym.linkedClassOfClass) // linkedCoC does not work properly in late phases
- if (isTopLevelModule(linkedClass)) {
- // phase travel to exitingPickler: this makes sure that memberClassesOf only sees member classes,
- // not local classes that were lifted by lambdalift.
- innerClassBuffer ++= exitingPickler(memberClassesOf(linkedClass))
+ if (!considerAsTopLevelImplementationArtifact(csym)) {
+ // If this is a top-level non-impl class, add members of the companion object.
+ // We exclude impl classes: if the classfile for the impl class exists on the classpath, a
+ // linkedClass symbol is found for which isTopLevelModule is true, so we end up searching
+ // members of that weird impl-class-module-class-symbol. that search probably cannot return
+ // any classes, but it's better to exclude it.
+ val linkedClass = exitingPickler(csym.linkedClassOfClass) // linkedCoC does not work properly in late phases
+ if (linkedClass != NoSymbol && isTopLevelModule(linkedClass)) {
+ // phase travel to exitingPickler: this makes sure that memberClassesForInnerClassTable only
+ // sees member classes, not local classes that were lifted by lambdalift.
+ innerClassBuffer ++= exitingPickler(memberClassesForInnerClassTable(linkedClass))
+ }
}
val allInners: List[Symbol] = innerClassBuffer.toList filterNot deadCode.elidedClosures