summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@gmail.com>2014-12-16 10:50:10 +0100
committerLukas Rytz <lukas.rytz@gmail.com>2015-01-16 23:18:12 +0100
commitafebceee78155c66564921eab7dd2170f820fdbf (patch)
tree65fba50f00339b69e4b0713f4120722115cf9a9c /src
parent7c1983d153a81ba43858a1bb5f0b59c5708bbfcf (diff)
downloadscala-afebceee78155c66564921eab7dd2170f820fdbf.tar.gz
scala-afebceee78155c66564921eab7dd2170f820fdbf.tar.bz2
scala-afebceee78155c66564921eab7dd2170f820fdbf.zip
Construct ClassBTypes from parsed classfiles
This infrastructure is required for the inliner: when inlining code from a classfile, the corresponding ClassBType is needed for various things (eg access checks, InnerClass attribute). The test creates two ClassBTypes for the same class: once using the (unpickled) Symbol, once using the parsed ASM ClassNode, and verifies that the two are the same. There's a cleanup to the InnerClass attribute: object T { class Member; def foo = { class Local } } class T For Java compatibility the InnerClass entry for Member says the class is nested in T (not in the module class T$). We now make sure to add that entry only to T, not to T$ (unless Member is actually referenced in the classfile T$, in that case it will be added, as required).
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala22
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala116
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala110
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala20
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CodeRepository.scala93
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/OptimizerReporting.scala21
8 files changed, 306 insertions, 80 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index 806d4b277c..8d1c37532e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -791,32 +791,28 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
assert(moduleClass.companionClass == NoSymbol, moduleClass)
innerClassBufferASM.clear()
this.cunit = cunit
- val moduleName = internalName(moduleClass) // + "$"
- val mirrorName = moduleName.substring(0, moduleName.length() - 1)
- val flags = (asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL)
+ val bType = mirrorClassClassBType(moduleClass)
val mirrorClass = new asm.tree.ClassNode
mirrorClass.visit(
classfileVersion,
- flags,
- mirrorName,
+ bType.info.flags,
+ bType.internalName,
null /* no java-generic-signature */,
ObjectReference.internalName,
EMPTY_STRING_ARRAY
)
- if (emitSource) {
- mirrorClass.visitSource("" + cunit.source,
- null /* SourceDebugExtension */)
- }
+ if (emitSource)
+ mirrorClass.visitSource("" + cunit.source, null /* SourceDebugExtension */)
- val ssa = getAnnotPickle(mirrorName, moduleClass.companionSymbol)
+ val ssa = getAnnotPickle(bType.internalName, moduleClass.companionSymbol)
mirrorClass.visitAttribute(if (ssa.isDefined) pickleMarkerLocal else pickleMarkerForeign)
emitAnnotations(mirrorClass, moduleClass.annotations ++ ssa)
- addForwarders(isRemote(moduleClass), mirrorClass, mirrorName, moduleClass)
+ addForwarders(isRemote(moduleClass), mirrorClass, bType.internalName, moduleClass)
- innerClassBufferASM ++= classBTypeFromSymbol(moduleClass).info.memberClasses
+ innerClassBufferASM ++= bType.info.nestedClasses
addInnerClassesASM(mirrorClass, innerClassBufferASM.toList)
mirrorClass.visitEnd()
@@ -932,7 +928,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments
constructor.visitEnd()
- innerClassBufferASM ++= classBTypeFromSymbol(cls).info.memberClasses
+ innerClassBufferASM ++= classBTypeFromSymbol(cls).info.nestedClasses
addInnerClassesASM(beanInfoClass, innerClassBufferASM.toList)
beanInfoClass.visitEnd()
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index 03bc32061b..142c901c21 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -118,7 +118,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
addClassFields()
- innerClassBufferASM ++= classBTypeFromSymbol(claszSymbol).info.memberClasses
+ innerClassBufferASM ++= classBTypeFromSymbol(claszSymbol).info.nestedClasses
gen(cd.impl)
addInnerClassesASM(cnode, innerClassBufferASM.toList)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index e1ca13cba9..11ee0d127b 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -8,6 +8,9 @@ package backend.jvm
import scala.tools.asm
import asm.Opcodes
+import scala.tools.asm.tree.{InnerClassNode, ClassNode}
+import opt.CodeRepository
+import scala.collection.convert.decorateAsScala._
/**
* The BTypes component defines The BType class hierarchy. BTypes encapsulate all type information
@@ -20,6 +23,17 @@ import asm.Opcodes
abstract class BTypes {
import BTypes.InternalName
+ // Some core BTypes are required here, in class BType, where no Global instance is available.
+ // The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual
+ // implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol.
+ val coreBTypes: CoreBTypesProxyGlobalIndependent[this.type]
+ import coreBTypes._
+
+ /**
+ * Tools for parsing classfiles, used by the inliner.
+ */
+ val codeRepository: CodeRepository
+
/**
* A map from internal names to ClassBTypes. Every ClassBType is added to this map on its
* construction.
@@ -31,18 +45,77 @@ abstract class BTypes {
* Concurrent because stack map frames are computed when in the class writer, which might run
* on multiple classes concurrently.
*/
- protected val classBTypeFromInternalNameMap: collection.concurrent.Map[InternalName, ClassBType]
+ val classBTypeFromInternalName: collection.concurrent.Map[InternalName, ClassBType]
/**
- * Obtain a previously constructed ClassBType for a given internal name.
+ * Parse the classfile for `internalName` and construct the [[ClassBType]].
*/
- def classBTypeFromInternalName(internalName: InternalName) = classBTypeFromInternalNameMap(internalName)
+ def classBTypeFromParsedClassfile(internalName: InternalName): ClassBType = {
+ classBTypeFromClassNode(codeRepository.classNode(internalName))
+ }
- // Some core BTypes are required here, in class BType, where no Global instance is available.
- // The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual
- // implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol.
- val coreBTypes: CoreBTypesProxyGlobalIndependent[this.type]
- import coreBTypes._
+ /**
+ * Construct the [[ClassBType]] for a parsed classfile.
+ */
+ def classBTypeFromClassNode(classNode: ClassNode): ClassBType = {
+ classBTypeFromInternalName.getOrElse(classNode.name, {
+ setClassInfo(classNode, ClassBType(classNode.name))
+ })
+ }
+
+ private def setClassInfo(classNode: ClassNode, classBType: ClassBType): ClassBType = {
+ val superClass = classNode.superName match {
+ case null =>
+ assert(classNode.name == ObjectReference.internalName, s"class with missing super type: ${classNode.name}")
+ None
+ case superName =>
+ Some(classBTypeFromParsedClassfile(superName))
+ }
+
+ val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
+
+ val flags = classNode.access
+
+ /**
+ * Find all nested classes of classNode. The innerClasses attribute contains all nested classes
+ * that are declared inside classNode or used in the bytecode of classNode. So some of them are
+ * nested in some other class than classNode, and we need to filter them.
+ *
+ * For member classes, innerClassNode.outerName is defined, so we compare that to classNode.name.
+ *
+ * For local and anonymous classes, innerClassNode.outerName is null. Such classes are required
+ * to have an EnclosingMethod attribute declaring the outer class. So we keep those local and
+ * anonymous classes whose outerClass is classNode.name.
+ *
+ */
+ def nestedInCurrentClass(innerClassNode: InnerClassNode): Boolean = {
+ (innerClassNode.outerName != null && innerClassNode.outerName == classNode.name) ||
+ (innerClassNode.outerName == null && codeRepository.classNode(innerClassNode.name).outerClass == classNode.name)
+ }
+
+ val nestedClasses: List[ClassBType] = classNode.innerClasses.asScala.collect({
+ case i if nestedInCurrentClass(i) => classBTypeFromParsedClassfile(i.name)
+ })(collection.breakOut)
+
+ // if classNode is a nested class, it has an innerClass attribute for itself. in this
+ // case we build the NestedInfo.
+ val nestedInfo = classNode.innerClasses.asScala.find(_.name == classNode.name) map {
+ case innerEntry =>
+ val enclosingClass =
+ if (innerEntry.outerName != null) {
+ // if classNode is a member class, the outerName is non-null
+ classBTypeFromParsedClassfile(innerEntry.outerName)
+ } else {
+ // for anonymous or local classes, the outerName is null, but the enclosing class is
+ // stored in the EnclosingMethod attribute (which ASM encodes in classNode.outerClass).
+ classBTypeFromParsedClassfile(classNode.outerClass)
+ }
+ val staticFlag = (innerEntry.access & Opcodes.ACC_STATIC) != 0
+ NestedInfo(enclosingClass, Option(innerEntry.outerName), Option(innerEntry.innerName), staticFlag)
+ }
+ classBType.info = ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo)
+ classBType
+ }
/**
* A BType is either a primitive type, a ClassBType, an ArrayBType of one of these, or a MethodType
@@ -574,7 +647,7 @@ abstract class BTypes {
* nested classes. Example: for the definition `class A { class B }` we have
*
* B.info.nestedInfo.outerClass == A
- * A.info.memberClasses contains B
+ * A.info.nestedClasses contains B
*/
private var _info: ClassInfo = null
@@ -589,7 +662,7 @@ abstract class BTypes {
checkInfoConsistency()
}
- classBTypeFromInternalNameMap(internalName) = this
+ classBTypeFromInternalName(internalName) = this
private def checkInfoConsistency(): Unit = {
// we assert some properties. however, some of the linked ClassBType (members, superClass,
@@ -597,7 +670,7 @@ abstract class BTypes {
// best-effort verification.
def ifInit(c: ClassBType)(p: ClassBType => Boolean): Boolean = c._info == null || p(c)
- def isJLO(t: ClassBType) = t.internalName == "java/lang/Object"
+ def isJLO(t: ClassBType) = t.internalName == ObjectReference.internalName
assert(!ClassBType.isInternalPhantomType(internalName), s"Cannot create ClassBType for phantom type $this")
@@ -612,7 +685,7 @@ abstract class BTypes {
s"Invalid interfaces in $this: ${info.interfaces}"
)
- assert(info.memberClasses.forall(c => ifInit(c)(_.isNestedClass)), info.memberClasses)
+ assert(info.nestedClasses.forall(c => ifInit(c)(_.isNestedClass)), info.nestedClasses)
}
/**
@@ -640,8 +713,9 @@ abstract class BTypes {
outerName.orNull,
innerName.orNull,
GenBCode.mkFlags(
- info.flags,
- if (isStaticNestedClass) asm.Opcodes.ACC_STATIC else 0
+ // the static flag in the InnerClass table has a special meaning, see InnerClass comment
+ info.flags & ~Opcodes.ACC_STATIC,
+ if (isStaticNestedClass) Opcodes.ACC_STATIC else 0
) & ClassBType.INNER_CLASSES_FLAGS
)
}
@@ -757,12 +831,12 @@ abstract class BTypes {
* through the superclass.
* @param flags The java flags, obtained through `javaFlags`. Used also to derive
* the flags for InnerClass entries.
- * @param memberClasses Classes nested in this class. Those need to be added to the
+ * @param nestedClasses Classes nested in this class. Those need to be added to the
* InnerClass table, see the InnerClass spec summary above.
* @param nestedInfo If this describes a nested class, information for the InnerClass table.
*/
- case class ClassInfo(superClass: Option[ClassBType], interfaces: List[ClassBType], flags: Int,
- memberClasses: List[ClassBType], nestedInfo: Option[NestedInfo])
+ final case class ClassInfo(superClass: Option[ClassBType], interfaces: List[ClassBType], flags: Int,
+ nestedClasses: List[ClassBType], nestedInfo: Option[NestedInfo])
/**
* Information required to add a class to an InnerClass table.
@@ -779,10 +853,10 @@ abstract class BTypes {
* a source-level property: if the class is in a static context (does not have an outer pointer).
* This is checked when building the NestedInfo.
*/
- case class NestedInfo(enclosingClass: ClassBType,
- outerName: Option[String],
- innerName: Option[String],
- isStaticNestedClass: Boolean)
+ final case class NestedInfo(enclosingClass: ClassBType,
+ outerName: Option[String],
+ innerName: Option[String],
+ isStaticNestedClass: Boolean)
/**
* This class holds the data for an entry in the InnerClass table. See the InnerClass summary
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index 0582088710..9b8ac82340 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -7,6 +7,7 @@ package scala.tools.nsc
package backend.jvm
import scala.tools.asm
+import opt.CodeRepository
import BTypes.InternalName
/**
@@ -33,11 +34,13 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
val coreBTypes = new CoreBTypesProxy[this.type](this)
import coreBTypes._
- final def intializeCoreBTypes(): Unit = {
+ val codeRepository = new CodeRepository(global.classPath)
+
+ final def initializeCoreBTypes(): Unit = {
coreBTypes.setBTypes(new CoreBTypes[this.type](this))
}
- protected val classBTypeFromInternalNameMap = {
+ val classBTypeFromInternalName = {
global.perRunCaches.recordCache(collection.concurrent.TrieMap.empty[InternalName, ClassBType])
}
@@ -84,7 +87,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
s"Cannot create ClassBType for special class symbol ${classSym.fullName}")
val internalName = classSym.javaBinaryName.toString
- classBTypeFromInternalNameMap.getOrElse(internalName, {
+ classBTypeFromInternalName.getOrElse(internalName, {
// The new ClassBType is added to the map in its constructor, before we set its info. This
// allows initializing cyclic dependencies, see the comment on variable ClassBType._info.
setClassInfo(classSym, ClassBType(internalName))
@@ -118,25 +121,35 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
* code generation, but those duplicates will be eliminated when emitting the InnerClass
* attribute.
*
- * Why doe we need to collect classes into innerClassBufferASM at all? To collect references to
+ * Why do we need to collect classes into innerClassBufferASM at all? To collect references to
* nested classes, but NOT nested in C, that are used within C.
*/
val nestedClassSymbols = {
// The lambdalift phase lifts all nested classes to the enclosing class, so if we collect
// member classes right after lambdalift, we obtain all nested classes, including local and
// anonymous ones.
- val nestedClasses = exitingPhase(currentRun.lambdaliftPhase)(memberClassesOf(classSym))
+ val nestedClasses = {
+ val nested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesOf(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))
+ nested diff members
+ } else {
+ nested
+ }
+ }
- // If this is a top-level class, and it has a companion object, the member classes of the
- // companion are added as members of the class. For example:
+ // 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 that the InnerClass attribute
- // for D will containt class "C" and NOT the module class "C$" as the outer class of D.
- // This is done by buildNestedInfo, the reason is Java compatibility, see comment in BTypes.
+ // 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
@@ -166,53 +179,51 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
} else true
})
- val memberClasses = nestedClassSymbolsNoJavaModuleClasses.map(classBTypeFromSymbol)
+ val nestedClasses = nestedClassSymbolsNoJavaModuleClasses.map(classBTypeFromSymbol)
val nestedInfo = buildNestedInfo(classSym)
- classBType.info = ClassInfo(superClass, interfaces, flags, memberClasses, nestedInfo)
+ classBType.info = ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo)
classBType
}
private def buildNestedInfo(innerClassSym: Symbol): Option[NestedInfo] = {
assert(innerClassSym.isClass, s"Cannot build NestedInfo for non-class symbol $innerClassSym")
- val isNested = !innerClassSym.rawowner.isPackageClass
- if (!isNested) None
+ val isTopLevel = innerClassSym.rawowner.isPackageClass
+ if (isTopLevel) None
else {
// See comment in BTypes, when is a class marked static in the InnerClass table.
val isStaticNestedClass = isOriginallyStaticOwner(innerClassSym.originalOwner)
// After lambdalift (which is where we are), the rawowoner field contains the enclosing class.
- val enclosingClassSym = {
- if (innerClassSym.isJavaDefined && innerClassSym.rawowner.isModuleClass) {
- // Example java source: class C { static class D { } }
- // The Scala compiler creates a class and a module symbol for C. Because D is a static
- // nested class, the symbol for D is nested in the module class C (not in the class C).
- // For the InnerClass attribute, we use the class symbol C, which represents the situation
- // in the source code.
-
- // Cannot use innerClassSym.isStatic: this method looks at the owner, which is a package
- // at this pahse (after lambdalift, flatten).
- assert(isOriginallyStaticOwner(innerClassSym.originalOwner), innerClassSym.originalOwner)
-
+ val enclosingClass = {
+ // (1) Example java source: class C { static class D { } }
+ // The Scala compiler creates a class and a module symbol for C. Because D is a static
+ // nested class, the symbol for D is nested in the module class C (not in the class C).
+ // For the InnerClass attribute, we use the class symbol C, which represents the situation
+ // in the source code.
+
+ // (2) Java compatibility. See the big comment in BTypes that summarizes the InnerClass spec.
+ if ((innerClassSym.isJavaDefined && innerClassSym.rawowner.isModuleClass) || // (1)
+ (!isAnonymousOrLocalClass(innerClassSym) && isTopLevelModuleClass(innerClassSym.rawowner))) { // (2)
// phase travel for linkedCoC - does not always work in late phases
- exitingPickler(innerClassSym.rawowner.linkedClassOfClass)
+ exitingPickler(innerClassSym.rawowner.linkedClassOfClass) match {
+ case NoSymbol =>
+ // For top-level modules without a companion class, see doc of mirrorClassClassBType.
+ mirrorClassClassBType(exitingPickler(innerClassSym.rawowner))
+
+ case companionClass =>
+ classBTypeFromSymbol(companionClass)
+ }
+ } else {
+ classBTypeFromSymbol(innerClassSym.rawowner)
}
- else innerClassSym.rawowner
}
- val enclosingClass: ClassBType = classBTypeFromSymbol(enclosingClassSym)
val outerName: Option[String] = {
- if (isAnonymousOrLocalClass(innerClassSym)) {
- None
- } else {
- val outerName = innerClassSym.rawowner.javaBinaryName
- // Java compatibility. See the big comment in BTypes that summarizes the InnerClass spec.
- val outerNameModule = if (isTopLevelModuleClass(innerClassSym.rawowner)) outerName.dropModule
- else outerName
- Some(outerNameModule.toString)
- }
+ if (isAnonymousOrLocalClass(innerClassSym)) None
+ else Some(enclosingClass.internalName)
}
val innerName: Option[String] = {
@@ -225,6 +236,29 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
}
/**
+ * For top-level objects without a companion class, the compilere generates a mirror class with
+ * static forwarders (Java compat). There's no symbol for the mirror class, but we still need a
+ * ClassBType (its info.nestedClasses will hold the InnerClass entries, see comment in BTypes).
+ */
+ def mirrorClassClassBType(moduleClassSym: Symbol): ClassBType = {
+ assert(isTopLevelModuleClass(moduleClassSym), s"not a top-level module class: $moduleClassSym")
+ val internalName = moduleClassSym.javaBinaryName.dropModule.toString
+ classBTypeFromInternalName.getOrElse(internalName, {
+ val c = ClassBType(internalName)
+ // class info consistent with BCodeHelpers.genMirrorClass
+ val nested = exitingPickler(memberClassesOf(moduleClassSym)) map classBTypeFromSymbol
+ c.info = ClassInfo(
+ superClass = Some(ObjectReference),
+ interfaces = Nil,
+ flags = asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL,
+ nestedClasses = nested,
+ nestedInfo = None
+ )
+ c
+ })
+ }
+
+ /**
* True for module classes of package level objects. The backend will generate a mirror class for
* such objects.
*/
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index de468c0a6d..abe3bc512c 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -677,7 +677,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
def isDeprecated(sym: Symbol): Boolean = { sym.annotations exists (_ matches definitions.DeprecatedAttr) }
- def addInnerClasses(csym: Symbol, jclass: asm.ClassVisitor) {
+ def addInnerClasses(csym: Symbol, jclass: asm.ClassVisitor, isMirror: Boolean = false) {
/* The outer name for this inner class. Note that it returns null
* when the inner class should not get an index in the constant pool.
* That means non-member classes (anonymous). See Section 4.7.5 in the JVMS.
@@ -698,11 +698,19 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
else
innerSym.rawname + innerSym.moduleSuffix
- // This collects all inner classes of csym, including local and anonymous: lambdalift makes
- // them members of their enclosing class.
- innerClassBuffer ++= exitingPhase(currentRun.lambdaliftPhase)(memberClassesOf(csym))
+ innerClassBuffer ++= {
+ val members = exitingPickler(memberClassesOf(csym))
+ // lambdalift makes all classes (also local, anonymous) members of their enclosing class
+ val allNested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesOf(csym))
- // Add members of the companion object (if top-level). why, see comment in BTypes.scala.
+ // 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
+ 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,
@@ -2796,7 +2804,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
addForwarders(isRemote(modsym), mirrorClass, mirrorName, modsym)
- addInnerClasses(modsym, mirrorClass)
+ addInnerClasses(modsym, mirrorClass, isMirror = true)
mirrorClass.visitEnd()
writeIfNotTooBig("" + modsym.name, mirrorName, mirrorClass, modsym)
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
index a45f586666..d5e95c47cf 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
@@ -286,7 +286,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
val initStart = Statistics.startTimer(BackendStats.bcodeInitTimer)
arrivalPos = 0 // just in case
scalaPrimitives.init()
- bTypes.intializeCoreBTypes()
+ bTypes.initializeCoreBTypes()
Statistics.stopTimer(BackendStats.bcodeInitTimer, initStart)
// initBytecodeWriter invokes fullName, thus we have to run it before the typer-dependent thread is activated.
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CodeRepository.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CodeRepository.scala
new file mode 100644
index 0000000000..285855d083
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CodeRepository.scala
@@ -0,0 +1,93 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc
+package backend.jvm
+package opt
+
+import scala.tools.asm
+import asm.tree._
+import scala.collection.convert.decorateAsScala._
+import scala.tools.nsc.io.AbstractFile
+import scala.tools.nsc.util.ClassFileLookup
+import OptimizerReporting._
+
+class CodeRepository(val classPath: ClassFileLookup[AbstractFile]) {
+ import BTypes.InternalName
+
+ /**
+ * Cache for parsed ClassNodes.
+ */
+ val classes: collection.concurrent.Map[InternalName, ClassNode] = collection.concurrent.TrieMap.empty[InternalName, ClassNode]
+
+ /**
+ * The class node for an internal name. If the class node is not yet available, it is parsed from
+ * the classfile on the compile classpath.
+ */
+ def classNode(internalName: InternalName): ClassNode = {
+ classes.getOrElseUpdate(internalName, parseClass(internalName))
+ }
+
+ /**
+ * The field node for a field matching `name` and `descriptor`, accessed in class `classInternalName`.
+ * The declaration of the field may be in one of the superclasses.
+ *
+ * @return The [[FieldNode]] of the requested field and the [[InternalName]] of its declaring class.
+ */
+ def fieldNode(classInternalName: InternalName, name: String, descriptor: String): Option[(FieldNode, InternalName)] = {
+ val c = classNode(classInternalName)
+ c.fields.asScala.find(f => f.name == name && f.desc == descriptor).map((_, classInternalName)) orElse {
+ Option(c.superName).flatMap(n => fieldNode(n, name, descriptor))
+ }
+ }
+
+ /**
+ * The method node for a method matching `name` and `descriptor`, accessed in class `classInternalName`.
+ * The declaration of the method may be in one of the parents.
+ *
+ * @return The [[MethodNode]] of the requested method and the [[InternalName]] of its declaring class.
+ */
+ def methodNode(classInternalName: InternalName, name: String, descriptor: String): Option[(MethodNode, InternalName)] = {
+ val c = classNode(classInternalName)
+ c.methods.asScala.find(m => m.name == name && m.desc == descriptor).map((_, classInternalName)) orElse {
+ val parents = Option(c.superName) ++ c.interfaces.asScala
+ // `view` to stop at the first result
+ parents.view.flatMap(methodNode(_, name, descriptor)).headOption
+ }
+ }
+
+ private def parseClass(internalName: InternalName): ClassNode = {
+ val fullName = internalName.replace('/', '.')
+ classPath.findClassFile(fullName) map { classFile =>
+ val classNode = new asm.tree.ClassNode()
+ val classReader = new asm.ClassReader(classFile.toByteArray)
+ // We don't need frames when inlining, but we want to keep the local variable table, so we
+ // don't use SKIP_DEBUG.
+ classReader.accept(classNode, asm.ClassReader.SKIP_FRAMES)
+ // SKIP_FRAMES leaves line number nodes. Remove them because they are not correct after
+ // inlining.
+ // TODO: we need to remove them also for classes that are not parsed from classfiles, why not simplify and do it once when inlining?
+ // OR: instead of skipping line numbers for inlined code, use write a SourceDebugExtension
+ // attribute that contains JSR-45 data that encodes debugging info.
+ // http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.11
+ // https://jcp.org/aboutJava/communityprocess/final/jsr045/index.html
+ removeLineNumberNodes(classNode)
+ classes(internalName) = classNode
+ classNode
+ } getOrElse {
+ inlineFailure(s"Class file for class $fullName not found.")
+ }
+ }
+
+ private def removeLineNumberNodes(classNode: ClassNode): Unit = {
+ for (method <- classNode.methods.asScala) {
+ val iter = method.instructions.iterator()
+ while (iter.hasNext) iter.next() match {
+ case _: LineNumberNode => iter.remove()
+ case _ =>
+ }
+ }
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/OptimizerReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/OptimizerReporting.scala
new file mode 100644
index 0000000000..c07fda2bc5
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/OptimizerReporting.scala
@@ -0,0 +1,21 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc
+package backend.jvm
+
+import scala.tools.asm
+import asm.tree._
+
+object OptimizerReporting {
+ def methodSignature(className: String, methodName: String, methodDescriptor: String): String = {
+ className + "::" + methodName + methodDescriptor
+ }
+
+ def methodSignature(className: String, method: MethodNode): String = methodSignature(className, method.name, method.desc)
+
+ def inlineFailure(reason: String): Nothing = MissingRequirementError.signal(reason)
+ def assertionError(message: String): Nothing = throw new AssertionError(message)
+}