Skip to content

Commit afebcee

Browse files
committed
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).
1 parent 7c1983d commit afebcee

File tree

13 files changed

+421
-101
lines changed

13 files changed

+421
-101
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -791,32 +791,28 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
791791
assert(moduleClass.companionClass == NoSymbol, moduleClass)
792792
innerClassBufferASM.clear()
793793
this.cunit = cunit
794-
val moduleName = internalName(moduleClass) // + "$"
795-
val mirrorName = moduleName.substring(0, moduleName.length() - 1)
796794

797-
val flags = (asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL)
795+
val bType = mirrorClassClassBType(moduleClass)
798796
val mirrorClass = new asm.tree.ClassNode
799797
mirrorClass.visit(
800798
classfileVersion,
801-
flags,
802-
mirrorName,
799+
bType.info.flags,
800+
bType.internalName,
803801
null /* no java-generic-signature */,
804802
ObjectReference.internalName,
805803
EMPTY_STRING_ARRAY
806804
)
807805

808-
if (emitSource) {
809-
mirrorClass.visitSource("" + cunit.source,
810-
null /* SourceDebugExtension */)
811-
}
806+
if (emitSource)
807+
mirrorClass.visitSource("" + cunit.source, null /* SourceDebugExtension */)
812808

813-
val ssa = getAnnotPickle(mirrorName, moduleClass.companionSymbol)
809+
val ssa = getAnnotPickle(bType.internalName, moduleClass.companionSymbol)
814810
mirrorClass.visitAttribute(if (ssa.isDefined) pickleMarkerLocal else pickleMarkerForeign)
815811
emitAnnotations(mirrorClass, moduleClass.annotations ++ ssa)
816812

817-
addForwarders(isRemote(moduleClass), mirrorClass, mirrorName, moduleClass)
813+
addForwarders(isRemote(moduleClass), mirrorClass, bType.internalName, moduleClass)
818814

819-
innerClassBufferASM ++= classBTypeFromSymbol(moduleClass).info.memberClasses
815+
innerClassBufferASM ++= bType.info.nestedClasses
820816
addInnerClassesASM(mirrorClass, innerClassBufferASM.toList)
821817

822818
mirrorClass.visitEnd()
@@ -932,7 +928,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
932928
constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments
933929
constructor.visitEnd()
934930

935-
innerClassBufferASM ++= classBTypeFromSymbol(cls).info.memberClasses
931+
innerClassBufferASM ++= classBTypeFromSymbol(cls).info.nestedClasses
936932
addInnerClassesASM(beanInfoClass, innerClassBufferASM.toList)
937933

938934
beanInfoClass.visitEnd()

src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
118118

119119
addClassFields()
120120

121-
innerClassBufferASM ++= classBTypeFromSymbol(claszSymbol).info.memberClasses
121+
innerClassBufferASM ++= classBTypeFromSymbol(claszSymbol).info.nestedClasses
122122
gen(cd.impl)
123123
addInnerClassesASM(cnode, innerClassBufferASM.toList)
124124

src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala

Lines changed: 95 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ package backend.jvm
88

99
import scala.tools.asm
1010
import asm.Opcodes
11+
import scala.tools.asm.tree.{InnerClassNode, ClassNode}
12+
import opt.CodeRepository
13+
import scala.collection.convert.decorateAsScala._
1114

1215
/**
1316
* The BTypes component defines The BType class hierarchy. BTypes encapsulate all type information
@@ -20,6 +23,17 @@ import asm.Opcodes
2023
abstract class BTypes {
2124
import BTypes.InternalName
2225

26+
// Some core BTypes are required here, in class BType, where no Global instance is available.
27+
// The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual
28+
// implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol.
29+
val coreBTypes: CoreBTypesProxyGlobalIndependent[this.type]
30+
import coreBTypes._
31+
32+
/**
33+
* Tools for parsing classfiles, used by the inliner.
34+
*/
35+
val codeRepository: CodeRepository
36+
2337
/**
2438
* A map from internal names to ClassBTypes. Every ClassBType is added to this map on its
2539
* construction.
@@ -31,18 +45,77 @@ abstract class BTypes {
3145
* Concurrent because stack map frames are computed when in the class writer, which might run
3246
* on multiple classes concurrently.
3347
*/
34-
protected val classBTypeFromInternalNameMap: collection.concurrent.Map[InternalName, ClassBType]
48+
val classBTypeFromInternalName: collection.concurrent.Map[InternalName, ClassBType]
3549

3650
/**
37-
* Obtain a previously constructed ClassBType for a given internal name.
51+
* Parse the classfile for `internalName` and construct the [[ClassBType]].
3852
*/
39-
def classBTypeFromInternalName(internalName: InternalName) = classBTypeFromInternalNameMap(internalName)
53+
def classBTypeFromParsedClassfile(internalName: InternalName): ClassBType = {
54+
classBTypeFromClassNode(codeRepository.classNode(internalName))
55+
}
4056

41-
// Some core BTypes are required here, in class BType, where no Global instance is available.
42-
// The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual
43-
// implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol.
44-
val coreBTypes: CoreBTypesProxyGlobalIndependent[this.type]
45-
import coreBTypes._
57+
/**
58+
* Construct the [[ClassBType]] for a parsed classfile.
59+
*/
60+
def classBTypeFromClassNode(classNode: ClassNode): ClassBType = {
61+
classBTypeFromInternalName.getOrElse(classNode.name, {
62+
setClassInfo(classNode, ClassBType(classNode.name))
63+
})
64+
}
65+
66+
private def setClassInfo(classNode: ClassNode, classBType: ClassBType): ClassBType = {
67+
val superClass = classNode.superName match {
68+
case null =>
69+
assert(classNode.name == ObjectReference.internalName, s"class with missing super type: ${classNode.name}")
70+
None
71+
case superName =>
72+
Some(classBTypeFromParsedClassfile(superName))
73+
}
74+
75+
val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
76+
77+
val flags = classNode.access
78+
79+
/**
80+
* Find all nested classes of classNode. The innerClasses attribute contains all nested classes
81+
* that are declared inside classNode or used in the bytecode of classNode. So some of them are
82+
* nested in some other class than classNode, and we need to filter them.
83+
*
84+
* For member classes, innerClassNode.outerName is defined, so we compare that to classNode.name.
85+
*
86+
* For local and anonymous classes, innerClassNode.outerName is null. Such classes are required
87+
* to have an EnclosingMethod attribute declaring the outer class. So we keep those local and
88+
* anonymous classes whose outerClass is classNode.name.
89+
*
90+
*/
91+
def nestedInCurrentClass(innerClassNode: InnerClassNode): Boolean = {
92+
(innerClassNode.outerName != null && innerClassNode.outerName == classNode.name) ||
93+
(innerClassNode.outerName == null && codeRepository.classNode(innerClassNode.name).outerClass == classNode.name)
94+
}
95+
96+
val nestedClasses: List[ClassBType] = classNode.innerClasses.asScala.collect({
97+
case i if nestedInCurrentClass(i) => classBTypeFromParsedClassfile(i.name)
98+
})(collection.breakOut)
99+
100+
// if classNode is a nested class, it has an innerClass attribute for itself. in this
101+
// case we build the NestedInfo.
102+
val nestedInfo = classNode.innerClasses.asScala.find(_.name == classNode.name) map {
103+
case innerEntry =>
104+
val enclosingClass =
105+
if (innerEntry.outerName != null) {
106+
// if classNode is a member class, the outerName is non-null
107+
classBTypeFromParsedClassfile(innerEntry.outerName)
108+
} else {
109+
// for anonymous or local classes, the outerName is null, but the enclosing class is
110+
// stored in the EnclosingMethod attribute (which ASM encodes in classNode.outerClass).
111+
classBTypeFromParsedClassfile(classNode.outerClass)
112+
}
113+
val staticFlag = (innerEntry.access & Opcodes.ACC_STATIC) != 0
114+
NestedInfo(enclosingClass, Option(innerEntry.outerName), Option(innerEntry.innerName), staticFlag)
115+
}
116+
classBType.info = ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo)
117+
classBType
118+
}
46119

47120
/**
48121
* 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 {
574647
* nested classes. Example: for the definition `class A { class B }` we have
575648
*
576649
* B.info.nestedInfo.outerClass == A
577-
* A.info.memberClasses contains B
650+
* A.info.nestedClasses contains B
578651
*/
579652
private var _info: ClassInfo = null
580653

@@ -589,15 +662,15 @@ abstract class BTypes {
589662
checkInfoConsistency()
590663
}
591664

592-
classBTypeFromInternalNameMap(internalName) = this
665+
classBTypeFromInternalName(internalName) = this
593666

594667
private def checkInfoConsistency(): Unit = {
595668
// we assert some properties. however, some of the linked ClassBType (members, superClass,
596669
// interfaces) may not yet have an `_info` (initialization of cyclic structures). so we do a
597670
// best-effort verification.
598671
def ifInit(c: ClassBType)(p: ClassBType => Boolean): Boolean = c._info == null || p(c)
599672

600-
def isJLO(t: ClassBType) = t.internalName == "java/lang/Object"
673+
def isJLO(t: ClassBType) = t.internalName == ObjectReference.internalName
601674

602675
assert(!ClassBType.isInternalPhantomType(internalName), s"Cannot create ClassBType for phantom type $this")
603676

@@ -612,7 +685,7 @@ abstract class BTypes {
612685
s"Invalid interfaces in $this: ${info.interfaces}"
613686
)
614687

615-
assert(info.memberClasses.forall(c => ifInit(c)(_.isNestedClass)), info.memberClasses)
688+
assert(info.nestedClasses.forall(c => ifInit(c)(_.isNestedClass)), info.nestedClasses)
616689
}
617690

618691
/**
@@ -640,8 +713,9 @@ abstract class BTypes {
640713
outerName.orNull,
641714
innerName.orNull,
642715
GenBCode.mkFlags(
643-
info.flags,
644-
if (isStaticNestedClass) asm.Opcodes.ACC_STATIC else 0
716+
// the static flag in the InnerClass table has a special meaning, see InnerClass comment
717+
info.flags & ~Opcodes.ACC_STATIC,
718+
if (isStaticNestedClass) Opcodes.ACC_STATIC else 0
645719
) & ClassBType.INNER_CLASSES_FLAGS
646720
)
647721
}
@@ -757,12 +831,12 @@ abstract class BTypes {
757831
* through the superclass.
758832
* @param flags The java flags, obtained through `javaFlags`. Used also to derive
759833
* the flags for InnerClass entries.
760-
* @param memberClasses Classes nested in this class. Those need to be added to the
834+
* @param nestedClasses Classes nested in this class. Those need to be added to the
761835
* InnerClass table, see the InnerClass spec summary above.
762836
* @param nestedInfo If this describes a nested class, information for the InnerClass table.
763837
*/
764-
case class ClassInfo(superClass: Option[ClassBType], interfaces: List[ClassBType], flags: Int,
765-
memberClasses: List[ClassBType], nestedInfo: Option[NestedInfo])
838+
final case class ClassInfo(superClass: Option[ClassBType], interfaces: List[ClassBType], flags: Int,
839+
nestedClasses: List[ClassBType], nestedInfo: Option[NestedInfo])
766840

767841
/**
768842
* Information required to add a class to an InnerClass table.
@@ -779,10 +853,10 @@ abstract class BTypes {
779853
* a source-level property: if the class is in a static context (does not have an outer pointer).
780854
* This is checked when building the NestedInfo.
781855
*/
782-
case class NestedInfo(enclosingClass: ClassBType,
783-
outerName: Option[String],
784-
innerName: Option[String],
785-
isStaticNestedClass: Boolean)
856+
final case class NestedInfo(enclosingClass: ClassBType,
857+
outerName: Option[String],
858+
innerName: Option[String],
859+
isStaticNestedClass: Boolean)
786860

787861
/**
788862
* This class holds the data for an entry in the InnerClass table. See the InnerClass summary

0 commit comments

Comments
 (0)