summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.number6
-rwxr-xr-xscripts/jobs/scala-release-2.11.x-build5
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala70
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala22
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala188
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala132
-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/ByteCodeRepository.scala112
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/OptimizerReporting.scala24
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala1
-rw-r--r--test/files/jvm/innerClassAttribute/Test.scala29
-rw-r--r--test/files/jvm/t8582.check6
-rw-r--r--test/files/jvm/t8582.scala4
-rw-r--r--test/files/res/t9089.check4
-rw-r--r--test/files/res/t9089.res2
-rw-r--r--test/files/res/t9089/A.scala1
-rw-r--r--test/files/run/icode-reader-dead-code.check8
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala2
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala95
-rw-r--r--versions.properties4
23 files changed, 553 insertions, 190 deletions
diff --git a/build.number b/build.number
index dc85ecb777..5f8ed6d6b6 100644
--- a/build.number
+++ b/build.number
@@ -1,9 +1,9 @@
#Tue Sep 11 19:21:09 CEST 2007
version.major=2
version.minor=11
-version.patch=5
+version.patch=6
# This is the -N part of a version. if it's 0, it's dropped from maven versions.
version.bnum=0
-# Note: To build a release run ant with -Dbuild.release=true
-# To build an RC, run ant with -Dmaven.version.suffix=-RCN
+# To build a release, see scripts/jobs/scala-release-2.11.x-build
+# (normally run by the eponymous job on scala-ci.typesafe.com). \ No newline at end of file
diff --git a/scripts/jobs/scala-release-2.11.x-build b/scripts/jobs/scala-release-2.11.x-build
index 21fbb8fa76..09a97dc34b 100755
--- a/scripts/jobs/scala-release-2.11.x-build
+++ b/scripts/jobs/scala-release-2.11.x-build
@@ -1,4 +1,9 @@
#!/bin/bash -e
+# TODO: different scripts for the different phases -- usually we don't need to bootstrap the modules,
+# since we can use the previous version of scala for STARR as well as for compiling the modules (assuming it's binary compatible)
+# We should move away from the complicated bootstrap and set up our release schedule so we always have a previous build that satisfies these criteria.
+# (Potentially trivially, by splitting up this script, and publishing locker as if it were a real release.)
+
# requirements:
# sbtCmd must point to sbt from sbt-extras (this is the standard on the Scala jenkins, so we only support that one)
# - ~/.sonatype-curl that consists of user = USER:PASS
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
index 7269910af6..75aa0fc984 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
@@ -5,10 +5,11 @@
package scala.tools.nsc.backend.jvm
-import scala.tools.asm.tree.{AbstractInsnNode, ClassNode, MethodNode}
-import java.io.PrintWriter
+import scala.tools.asm.tree.{InsnList, AbstractInsnNode, ClassNode, MethodNode}
+import java.io.{StringWriter, PrintWriter}
import scala.tools.asm.util.{TraceClassVisitor, TraceMethodVisitor, Textifier}
import scala.tools.asm.ClassReader
+import scala.collection.convert.decorateAsScala._
object AsmUtils {
@@ -36,19 +37,12 @@ object AsmUtils {
def traceMethod(mnode: MethodNode): Unit = {
println(s"Bytecode for method ${mnode.name}")
- val p = new Textifier
- val tracer = new TraceMethodVisitor(p)
- mnode.accept(tracer)
- val w = new PrintWriter(System.out)
- p.print(w)
- w.flush()
+ println(textify(mnode))
}
def traceClass(cnode: ClassNode): Unit = {
println(s"Bytecode for class ${cnode.name}")
- val w = new PrintWriter(System.out)
- cnode.accept(new TraceClassVisitor(w))
- w.flush()
+ println(textify(cnode))
}
def traceClass(bytes: Array[Byte]): Unit = traceClass(readClass(bytes))
@@ -59,8 +53,56 @@ object AsmUtils {
node
}
- def instructionString(instruction: AbstractInsnNode): String = instruction.getOpcode match {
- case -1 => instruction.toString
- case op => scala.tools.asm.util.Printer.OPCODES(op)
+ /**
+ * Returns a human-readable representation of the cnode ClassNode.
+ */
+ def textify(cnode: ClassNode): String = {
+ val trace = new TraceClassVisitor(new PrintWriter(new StringWriter))
+ cnode.accept(trace)
+ val sw = new StringWriter
+ val pw = new PrintWriter(sw)
+ trace.p.print(pw)
+ sw.toString
}
+
+ /**
+ * Returns a human-readable representation of the code in the mnode MethodNode.
+ */
+ def textify(mnode: MethodNode): String = {
+ val trace = new TraceClassVisitor(new PrintWriter(new StringWriter))
+ mnode.accept(trace)
+ val sw = new StringWriter
+ val pw = new PrintWriter(sw)
+ trace.p.print(pw)
+ sw.toString
+ }
+
+ /**
+ * Returns a human-readable representation of the given instruction.
+ */
+ def textify(insn: AbstractInsnNode): String = {
+ val trace = new TraceMethodVisitor(new Textifier)
+ insn.accept(trace)
+ val sw = new StringWriter
+ val pw = new PrintWriter(sw)
+ trace.p.print(pw)
+ sw.toString.trim
+ }
+
+ /**
+ * Returns a human-readable representation of the given instruction sequence.
+ */
+ def textify(insns: Iterator[AbstractInsnNode]): String = {
+ val trace = new TraceMethodVisitor(new Textifier)
+ insns.foreach(_.accept(trace))
+ val sw: StringWriter = new StringWriter
+ val pw: PrintWriter = new PrintWriter(sw)
+ trace.p.print(pw)
+ sw.toString.trim
+ }
+
+ /**
+ * Returns a human-readable representation of the given instruction sequence.
+ */
+ def textify(insns: InsnList): String = textify(insns.iterator().asScala)
}
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/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
index d58368b19d..c3db28151b 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
@@ -271,7 +271,7 @@ abstract class BCodeIdiomatic extends SubComponent {
assert(from != BOOL && to != BOOL, s"inconvertible types : $from -> $to")
// We're done with BOOL already
- from match {
+ (from: @unchecked) match {
// using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match"
@@ -361,7 +361,7 @@ abstract class BCodeIdiomatic extends SubComponent {
assert(elem.isNonVoidPrimitiveType)
val rand = {
// using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match"
- elem match {
+ (elem: @unchecked) match {
case BOOL => Opcodes.T_BOOLEAN
case BYTE => Opcodes.T_BYTE
case SHORT => Opcodes.T_SHORT
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 7defd7c873..a9bce82acd 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -8,9 +8,12 @@ package backend.jvm
import scala.tools.asm
import asm.Opcodes
+import scala.tools.asm.tree.{InnerClassNode, ClassNode}
+import opt.ByteCodeRepository
+import scala.collection.convert.decorateAsScala._
/**
- * The BTypes component defines The BType class hierarchy. BTypes encapsulates all type information
+ * The BTypes component defines The BType class hierarchy. BTypes encapsulate all type information
* that is required after building the ASM nodes. This includes optimizations, generation of
* InnerClass attributes and generation of stack map frames.
*
@@ -18,6 +21,22 @@ import asm.Opcodes
* be queried by concurrent threads.
*/
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 byteCodeRepository: ByteCodeRepository
+
+ // Allows to define per-run caches here and in the CallGraph component, which don't have a global
+ def recordPerRunCache[T <: collection.generic.Clearable](cache: T): T
+
/**
* A map from internal names to ClassBTypes. Every ClassBType is added to this map on its
* construction.
@@ -29,30 +48,83 @@ 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[String, ClassBType]
+ val classBTypeFromInternalName: collection.concurrent.Map[InternalName, ClassBType] = recordPerRunCache(collection.concurrent.TrieMap.empty[InternalName, ClassBType])
/**
- * The string represented by the `offset` / `length` values of a ClassBType, see comment of that
- * class.
+ * Parse the classfile for `internalName` and construct the [[ClassBType]].
*/
- protected def internalNameString(offset: Int, lenght: Int): String
+ def classBTypeFromParsedClassfile(internalName: InternalName): ClassBType = {
+ classBTypeFromClassNode(byteCodeRepository.classNode(internalName))
+ }
/**
- * Obtain a previously constructed ClassBType for a given internal name.
+ * Construct the [[ClassBType]] for a parsed classfile.
*/
- def classBTypeFromInternalName(internalName: String) = classBTypeFromInternalNameMap(internalName)
+ def classBTypeFromClassNode(classNode: ClassNode): ClassBType = {
+ classBTypeFromInternalName.getOrElse(classNode.name, {
+ setClassInfo(classNode, ClassBType(classNode.name))
+ })
+ }
- // 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._
+ 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 && byteCodeRepository.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
* referring to BTypes.
*/
- /*sealed*/ trait BType { // Not sealed for now due to SI-8546
+ sealed trait BType {
final override def toString: String = this match {
case UNIT => "V"
case BOOL => "Z"
@@ -171,6 +243,9 @@ abstract class BTypes {
assert(other.isRef, s"Cannot compute maxType: $this, $other")
// Approximate `lub`. The common type of two references is always ObjectReference.
ObjectReference
+
+ case _: MethodBType =>
+ throw new AssertionError(s"unexpected method type when computing maxType: $this")
}
/**
@@ -568,28 +643,14 @@ abstract class BTypes {
/**
* A ClassBType represents a class or interface type. The necessary information to build a
* ClassBType is extracted from compiler symbols and types, see BTypesFromSymbols.
- *
- * The `offset` and `length` fields are used to represent the internal name of the class. They
- * are indices into some character array. The internal name can be obtained through the method
- * `internalNameString`, which is abstract in this component. Name creation is assumed to be
- * hash-consed, so if two ClassBTypes have the same internal name, they NEED to have the same
- * `offset` and `length`.
- *
- * The actual implementation in subclass BTypesFromSymbols uses the global `chrs` array from the
- * name table. This representation is efficient because the JVM class name is obtained through
- * `classSymbol.javaBinaryName`. This already adds the necessary string to the `chrs` array,
- * so it makes sense to reuse the same name table in the backend.
- *
- * ClassBType is not a case class because we want a custom equals method, and because the
- * extractor extracts the internalName, which is what you typically need.
*/
- final class ClassBType(val offset: Int, val length: Int) extends RefBType {
+ final case class ClassBType(internalName: InternalName) extends RefBType {
/**
* Write-once variable allows initializing a cyclic graph of infos. This is required for
* 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
@@ -604,7 +665,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,
@@ -612,7 +673,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")
@@ -627,16 +688,10 @@ 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)
}
/**
- * The internal name of a class is the string returned by java.lang.Class.getName, with all '.'
- * replaced by '/'. For example "java/lang/String".
- */
- def internalName: String = internalNameString(offset, length)
-
- /**
* @return The class name without the package prefix
*/
def simpleName: String = internalName.split("/").last
@@ -661,8 +716,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
)
}
@@ -736,33 +792,10 @@ abstract class BTypes {
} while (fcs == null)
fcs
}
-
- /**
- * Custom equals / hashCode: we only compare the name (offset / length)
- */
- override def equals(o: Any): Boolean = (this eq o.asInstanceOf[Object]) || (o match {
- case c: ClassBType => c.offset == this.offset && c.length == this.length
- case _ => false
- })
-
- override def hashCode: Int = {
- import scala.runtime.Statics
- var acc: Int = -889275714
- acc = Statics.mix(acc, offset)
- acc = Statics.mix(acc, length)
- Statics.finalizeHash(acc, 2)
- }
}
object ClassBType {
/**
- * Pattern matching on a ClassBType extracts the `internalName` of the class.
- */
- def unapply(c: ClassBType): Option[String] =
- if (c == null) None
- else Some(c.internalName)
-
- /**
* Valid flags for InnerClass attribute entry.
* See http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6
*/
@@ -801,12 +834,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.
@@ -823,10 +856,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
@@ -839,9 +872,9 @@ abstract class BTypes {
* @param innerName The simple name of the inner class, may be null.
* @param flags The flags for this class in the InnerClass entry.
*/
- case class InnerClassEntry(name: String, outerName: String, innerName: String, flags: Int)
+ final case class InnerClassEntry(name: String, outerName: String, innerName: String, flags: Int)
- case class ArrayBType(componentType: BType) extends RefBType {
+ final case class ArrayBType(componentType: BType) extends RefBType {
def dimension: Int = componentType match {
case a: ArrayBType => 1 + a.dimension
case _ => 1
@@ -853,7 +886,7 @@ abstract class BTypes {
}
}
- case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType
+ final case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType
/* Some definitions that are required for the implementation of BTypes. They are abstract because
* initializing them requires information from types / symbols, which is not accessible here in
@@ -873,3 +906,12 @@ abstract class BTypes {
*/
def isCompilingPrimitive: Boolean
}
+
+object BTypes {
+ /**
+ * A marker for strings that represent class internal names.
+ * Ideally the type would be incompatible with String, for example by making it a value class.
+ * But that would create overhead in a Collection[InternalName].
+ */
+ type InternalName = String
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index a0b8d28f18..94f9b585d9 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -7,6 +7,10 @@ package scala.tools.nsc
package backend.jvm
import scala.tools.asm
+import opt.ByteCodeRepository
+import scala.tools.asm.tree.ClassNode
+import scala.tools.nsc.backend.jvm.opt.ByteCodeRepository.Source
+import BTypes.InternalName
/**
* This class mainly contains the method classBTypeFromSymbol, which extracts the necessary
@@ -32,20 +36,13 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
val coreBTypes = new CoreBTypesProxy[this.type](this)
import coreBTypes._
- final def intializeCoreBTypes(): Unit = {
- coreBTypes.setBTypes(new CoreBTypes[this.type](this))
- }
-
- def internalNameString(offset: Int, length: Int) = new String(global.chrs, offset, length)
+ val byteCodeRepository = new ByteCodeRepository(global.classPath, recordPerRunCache(collection.concurrent.TrieMap.empty[InternalName, (ClassNode, Source)]))
- protected val classBTypeFromInternalNameMap = {
- global.perRunCaches.recordCache(collection.concurrent.TrieMap.empty[String, ClassBType])
+ final def initializeCoreBTypes(): Unit = {
+ coreBTypes.setBTypes(new CoreBTypes[this.type](this))
}
- /**
- * Cache for the method classBTypeFromSymbol.
- */
- private val convertedClasses = perRunCaches.newMap[Symbol, ClassBType]()
+ def recordPerRunCache[T <: collection.generic.Clearable](cache: T): T = perRunCaches.recordCache(cache)
// helpers that need access to global.
// TODO @lry create a separate component, they don't belong to BTypesFromSymbols
@@ -89,13 +86,11 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
(classSym != NothingClass && classSym != NullClass),
s"Cannot create ClassBType for special class symbol ${classSym.fullName}")
- convertedClasses.getOrElse(classSym, {
- val internalName = classSym.javaBinaryName.toTypeName
- // We first create and add the ClassBType to the hash map before computing its info. This
- // allows initializing cylic dependencies, see the comment on variable ClassBType._info.
- val classBType = new ClassBType(internalName.start, internalName.length)
- convertedClasses(classSym) = classBType
- setClassInfo(classSym, classBType)
+ val internalName = classSym.javaBinaryName.toString
+ 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))
})
}
@@ -126,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
@@ -174,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] = {
@@ -233,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/ByteCodeRepository.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala
new file mode 100644
index 0000000000..7b424d2107
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala
@@ -0,0 +1,112 @@
+/* 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._
+import ByteCodeRepository._
+import BTypes.InternalName
+
+/**
+ * The ByteCodeRepository provides utilities to read the bytecode of classfiles from the compilation
+ * classpath. Parsed classes are cached in the `classes` map.
+ *
+ * @param classPath The compiler classpath where classfiles are searched and read from.
+ * @param classes Cache for parsed ClassNodes. Also stores the source of the bytecode:
+ * [[Classfile]] if read from `classPath`, [[CompilationUnit]] if the bytecode
+ * corresponds to a class being compiled.
+ */
+class ByteCodeRepository(val classPath: ClassFileLookup[AbstractFile], val classes: collection.concurrent.Map[InternalName, (ClassNode, Source)]) {
+ /**
+ * The class node and source for an internal name. If the class node is not yet available, it is
+ * parsed from the classfile on the compile classpath.
+ */
+ def classNodeAndSource(internalName: InternalName): (ClassNode, Source) = {
+ classes.getOrElseUpdate(internalName, (parseClass(internalName), Classfile))
+ }
+
+ /**
+ * 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) = classNodeAndSource(internalName)._1
+
+ /**
+ * 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)
+ 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 _ =>
+ }
+ }
+ }
+}
+
+object ByteCodeRepository {
+ /**
+ * The source of a ClassNode in the ByteCodeRepository. Can be either [[CompilationUnit]] if the
+ * class is being compiled or [[Classfile]] if the class was parsed from the compilation classpath.
+ */
+ sealed trait Source
+ object CompilationUnit extends Source
+ object Classfile extends Source
+}
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..7002e43d98
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/OptimizerReporting.scala
@@ -0,0 +1,24 @@
+/* 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._
+
+/**
+ * Reporting utilities used in the optimizer.
+ */
+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)
+}
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index c86a1108b2..1691b01e3e 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -894,7 +894,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
val specMember = subst(outerEnv)(specializedOverload(owner, sym, spec))
- owner.info.decls.enter(specMember)
typeEnv(specMember) = typeEnv(sym) ++ outerEnv ++ spec
wasSpecializedForTypeVars(specMember) ++= spec collect { case (s, tp) if s.tpe == tp => s }
diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala
index 1c1a0522e4..3820048cb4 100644
--- a/test/files/jvm/innerClassAttribute/Test.scala
+++ b/test/files/jvm/innerClassAttribute/Test.scala
@@ -84,17 +84,15 @@ object Test extends BytecodeTest {
}
def testA3() = {
- def t(c: String) = {
- val List(b1, b2) = innerClassNodes(c)
- // the outer class for classes nested inside top-level modules is not the module class, but the mirror class.
- // this is a hack for java interop, handled in the backend. see BTypes.scala, comment on "Java Compatibility".
- assertMember(b1, "A3", "B1", flags = publicStatic)
- assertMember(b2, "A3", "B2$", flags = publicStatic)
- }
- t("A3$")
- // the mirror class has the same inner class attributes as the module
- // class (added when the mirror is created in the backend)
- t("A3")
+ // the inner class entries for top-level object members are in the companion class, so nothing in the module class
+ val List() = innerClassNodes("A3$")
+
+ // inner class entries in the companion class (a backend-generated mirror class in this case)
+ val List(b1, b2) = innerClassNodes("A3")
+ // the outer class for classes nested inside top-level modules is not the module class, but the mirror class.
+ // this is a hack for java interop, handled in the backend. see BTypes.scala, comment on "Java Compatibility".
+ assertMember(b1, "A3", "B1", flags = publicStatic)
+ assertMember(b2, "A3", "B2$", flags = publicStatic)
}
def testA4() = {
@@ -164,7 +162,10 @@ object Test extends BytecodeTest {
}
def testA15() = {
- val List(b) = innerClassNodes("A15")
+ // no member classes, only anonymous / local. these are nested in the module class, not the companion.
+ val List() = innerClassNodes("A15")
+
+ val List(b) = innerClassNodes("A15$")
assertLocal(b, "A15$B$3", "B$3")
val List(_, c) = innerClassNodes("A15$B$3")
@@ -283,9 +284,7 @@ object Test extends BytecodeTest {
assertMember(i3c, "A21", "I3$", flags = publicStatic)
assertLocal(j1, "A21$J1$1", "J1$1")
- val List(i2m, i3m, j3, j4, j5) = innerClassNodes("A21$")
- assertMember(i2m, "A21", "I2", flags = publicStatic)
- assertMember(i3m, "A21", "I3$", flags = publicStatic)
+ val List(j3, j4, j5) = innerClassNodes("A21$")
assertLocal(j3, "A21$J3$1", "J3$1")
assertLocal(j4, "A21$J4$1", "J4$1")
assertLocal(j5, "A21$J5$1", "J5$1") // non-static!
diff --git a/test/files/jvm/t8582.check b/test/files/jvm/t8582.check
index 564f482ff8..e388366270 100644
--- a/test/files/jvm/t8582.check
+++ b/test/files/jvm/t8582.check
@@ -14,10 +14,10 @@ Reflection can find direct nested classes (A2-B2-C2)
A2$B2: List(class A2$B2$C2)
A2$B2$C2: List()
-Mirror classes have the same InnerClass attributes as the corresponding module class:
- className[p1/p2/Singleton$Singleton$] outerClassName[p1/p2/Singleton] innerName[Singleton$] access[9]
-Module class
+The InnerClass attribute of a mirror class contains the members of the module class:
className[p1/p2/Singleton$Singleton$] outerClassName[p1/p2/Singleton] innerName[Singleton$] access[9]
+The module members are not in the InnerClass table of the module class (unless referenced):
+
An outer class has a InnerClass attribute for direct nested classes
className[A1$B1] outerClassName[A1] innerName[B1] access[1]
diff --git a/test/files/jvm/t8582.scala b/test/files/jvm/t8582.scala
index 8a57ef7952..e9a01f9016 100644
--- a/test/files/jvm/t8582.scala
+++ b/test/files/jvm/t8582.scala
@@ -55,9 +55,9 @@ object Test extends BytecodeTest {
println(nested(classOf[A2#B2]))
println(nested(classOf[A2#B2#C2]))
- nprintln("Mirror classes have the same InnerClass attributes as the corresponding module class:")
+ nprintln("The InnerClass attribute of a mirror class contains the members of the module class:")
printInner("p1.p2.Singleton") // mirror class
- println("Module class")
+ println("The module members are not in the InnerClass table of the module class (unless referenced):")
printInner("p1.p2.Singleton$")
nprintln("An outer class has a InnerClass attribute for direct nested classes")
diff --git a/test/files/res/t9089.check b/test/files/res/t9089.check
new file mode 100644
index 0000000000..6cf64f734b
--- /dev/null
+++ b/test/files/res/t9089.check
@@ -0,0 +1,4 @@
+
+nsc>
+nsc>
+nsc>
diff --git a/test/files/res/t9089.res b/test/files/res/t9089.res
new file mode 100644
index 0000000000..ab5cc8534d
--- /dev/null
+++ b/test/files/res/t9089.res
@@ -0,0 +1,2 @@
+t9089/A.scala
+t9089/A.scala
diff --git a/test/files/res/t9089/A.scala b/test/files/res/t9089/A.scala
new file mode 100644
index 0000000000..bccf269639
--- /dev/null
+++ b/test/files/res/t9089/A.scala
@@ -0,0 +1 @@
+object O { def f(x: => Int): Int = x }
diff --git a/test/files/run/icode-reader-dead-code.check b/test/files/run/icode-reader-dead-code.check
index d1739fed3b..c9de93283e 100644
--- a/test/files/run/icode-reader-dead-code.check
+++ b/test/files/run/icode-reader-dead-code.check
@@ -1,4 +1,7 @@
Bytecode for method f
+
+ // access flags 0x11
+ public final f()I
L0
LINENUMBER 4 L0
ICONST_1
@@ -7,7 +10,11 @@ Bytecode for method f
LOCALVARIABLE this Lp/A; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
+
Bytecode for method f
+
+ // access flags 0x11
+ public final f()I
L0
LINENUMBER 4 L0
ICONST_1
@@ -17,3 +24,4 @@ Bytecode for method f
LOCALVARIABLE this Lp/A; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
+
diff --git a/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala b/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
index 221aad6536..2347e8288e 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
@@ -19,7 +19,7 @@ class BTypesTest {
val btypes = new BTypesFromSymbols[g.type](g)
import btypes._
- duringBackend(btypes.intializeCoreBTypes())
+ duringBackend(btypes.initializeCoreBTypes())
def classBTypeFromSymbol(sym: Symbol) = duringBackend(btypes.classBTypeFromSymbol(sym))
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
new file mode 100644
index 0000000000..2975bd060d
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala
@@ -0,0 +1,95 @@
+package scala.tools.nsc
+package backend.jvm
+package opt
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import scala.tools.asm.Opcodes._
+import org.junit.Assert._
+
+import scala.tools.nsc.backend.jvm.BTypes.InternalName
+import scala.tools.testing.AssertUtil._
+
+import CodeGenTools._
+import scala.tools.partest.ASMConverters
+import ASMConverters._
+
+import scala.collection.convert.decorateAsScala._
+
+@RunWith(classOf[JUnit4])
+class BTypesFromClassfileTest {
+ val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode")
+
+ import compiler._
+ import definitions._
+ import genBCode.bTypes
+ import bTypes._
+
+ def duringBackend[T](f: => T) = compiler.exitingDelambdafy(f)
+
+ val run = new compiler.Run() // initializes some of the compiler
+ duringBackend(bTypes.initializeCoreBTypes())
+
+ def clearCache() = bTypes.classBTypeFromInternalName.clear()
+
+ def sameBType(fromSym: ClassBType, fromClassfile: ClassBType, checked: Set[InternalName] = Set.empty): Set[InternalName] = {
+ if (checked(fromSym.internalName)) checked
+ else {
+ assert(fromSym == fromClassfile, s"$fromSym != $fromClassfile")
+ sameInfo(fromSym.info, fromClassfile.info, checked + fromSym.internalName)
+ }
+ }
+
+ def sameBTypes(fromSyms: Iterable[ClassBType], fromClassfiles: Iterable[ClassBType], checked: Set[InternalName]): Set[InternalName] = {
+ assert(fromSyms.size == fromClassfiles.size, s"\n$fromSyms\n$fromClassfiles")
+ (fromSyms, fromClassfiles).zipped.foldLeft(checked) {
+ case (chk, (fromSym, fromClassfile)) => sameBType(fromSym, fromClassfile, chk)
+ }
+ }
+
+ def sameInfo(fromSym: ClassInfo, fromClassfile: ClassInfo, checked: Set[InternalName]): Set[InternalName] = {
+ assert({
+ // Nested class symbols can undergo makeNotPrivate (ExplicitOuter). But this is only applied
+ // for symbols of class symbols that are being compiled, not those read from a pickle.
+ // So a class may be public in bytecode, but the symbol still says private.
+ if (fromSym.nestedInfo.isEmpty) fromSym.flags == fromClassfile.flags
+ else (fromSym.flags | ACC_PRIVATE | ACC_PUBLIC) == (fromClassfile.flags | ACC_PRIVATE | ACC_PUBLIC)
+ }, s"class flags differ\n$fromSym\n$fromClassfile")
+
+ val chk1 = sameBTypes(fromSym.superClass, fromClassfile.superClass, checked)
+
+ val chk2 = sameBTypes(fromSym.interfaces, fromClassfile.interfaces, chk1)
+
+ // The fromSym info has only member classes, no local or anonymous. The symbol is read from the
+ // Scala pickle data and only member classes are created / entered.
+ // (This is different for symbols that are being compiled, there flatten will enter all local
+ // and anonymous classes as members of the outer class. But not for unpickled symbols).
+ // The fromClassfile info has all nested classes, including anonymous and local. So we filter
+ // them out: member classes are identified by having the `outerName` defined.
+ val memberClassesFromClassfile = fromClassfile.nestedClasses.filter(_.info.nestedInfo.get.outerName.isDefined)
+ // Sorting is required: the backend sorts all InnerClass entries by internalName before writing
+ // them to the classfile (to make it deterministic: the entries are collected in a Set during
+ // code generation).
+ val chk3 = sameBTypes(fromSym.nestedClasses.sortBy(_.internalName), memberClassesFromClassfile.sortBy(_.internalName), chk2)
+ sameBTypes(fromSym.nestedInfo.map(_.enclosingClass), fromClassfile.nestedInfo.map(_.enclosingClass), chk3)
+ }
+
+ def check(classSym: Symbol): Unit = duringBackend {
+ clearCache()
+ val fromSymbol = classBTypeFromSymbol(classSym)
+ clearCache()
+ val fromClassfile = bTypes.classBTypeFromParsedClassfile(fromSymbol.internalName)
+ sameBType(fromSymbol, fromClassfile)
+ }
+
+ @Test
+ def compareClassBTypes(): Unit = {
+ // Note that not only these classes are tested, but also all their parents and all nested
+ // classes in their InnerClass attributes.
+ check(ObjectClass)
+ check(JavaNumberClass)
+ check(ConsClass)
+ check(ListModule.moduleClass)
+ }
+}
diff --git a/versions.properties b/versions.properties
index a474b19c5b..9e2e6cea79 100644
--- a/versions.properties
+++ b/versions.properties
@@ -4,7 +4,7 @@
# when adding new properties that influence a release,
# also add them to the update.versions mechanism in build.xml,
# which is used by scala-release-2.11.x in scala/jenkins-scripts
-starr.version=2.11.2
+starr.version=2.11.5
starr.use.released=1
# These are the versions of the modules that go with this release.
@@ -14,7 +14,7 @@ starr.use.released=1
scala.binary.version=2.11
# e.g. 2.11.0-RC1, 2.11.0, 2.11.1-RC1, 2.11.1
# this defines the dependency on scala-continuations-plugin in scala-dist's pom
-scala.full.version=2.11.2
+scala.full.version=2.11.5
# external modules shipped with distribution, as specified by scala-library-all's pom
scala-xml.version.number=1.0.3