Skip to content

Commit e89fe92

Browse files
committed
Merge pull request #3826 from lrytz/opt/refactorTracked
Assortiment of cleanups and comments around the backend
2 parents 33b847b + a8c88b1 commit e89fe92

15 files changed

+481
-89
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
977977
if (!isModuleInitialized &&
978978
jMethodName == INSTANCE_CONSTRUCTOR_NAME &&
979979
jname == INSTANCE_CONSTRUCTOR_NAME &&
980-
isStaticModule(siteSymbol)) {
980+
isStaticModuleClass(siteSymbol)) {
981981
isModuleInitialized = true
982982
mnode.visitVarInsn(asm.Opcodes.ALOAD, 0)
983983
mnode.visitFieldInsn(

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

+3
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
460460
// Can't call .toInterface (at this phase) or we trip an assertion.
461461
// See PackratParser#grow for a method which fails with an apparent mismatch
462462
// between "object PackratParsers$class" and "trait PackratParsers"
463+
// TODO @lry do we have a test for that?
463464
if (sym.isImplClass) {
464465
// pos/spec-List.scala is the sole failure if we don't check for NoSymbol
465466
val traitSym = sym.owner.info.decl(tpnme.interfaceName(sym.name))
@@ -469,6 +470,8 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
469470
}
470471
}
471472

473+
// TODO @lry: code duplication between here and method asmClassType.
474+
472475
assert(hasInternalName(sym), s"Invoked for a symbol lacking JVM internal name: ${sym.fullName}")
473476
assert(!phantomTypeMap.contains(sym), "phantom types not supposed to reach here.")
474477

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

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ abstract class BCodeIdiomatic extends SubComponent {
3434
case "jvm-1.5" => asm.Opcodes.V1_5
3535
case "jvm-1.6" => asm.Opcodes.V1_6
3636
case "jvm-1.7" => asm.Opcodes.V1_7
37+
case "jvm-1.8" => asm.Opcodes.V1_8
3738
}
3839

3940
val majorVersion: Int = (classfileVersion & 0xFF)

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
9595

9696
claszSymbol = cd.symbol
9797
isCZParcelable = isAndroidParcelableClass(claszSymbol)
98-
isCZStaticModule = isStaticModule(claszSymbol)
98+
isCZStaticModule = isStaticModuleClass(claszSymbol)
9999
isCZRemote = isRemote(claszSymbol)
100100
thisName = internalName(claszSymbol)
101101

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

+90-21
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ abstract class BCodeTypes extends BCodeIdiomatic {
2121
import global._
2222
import bTypes._
2323

24-
// when compiling the Scala library, some assertions don't hold (e.g., scala.Boolean has null superClass although it's not an interface)
25-
val isCompilingStdLib = !(settings.sourcepath.isDefault)
24+
// Used only for assertions. When compiling the Scala library, some assertions don't hold
25+
// (e.g., scala.Boolean has null superClass although it's not an interface)
26+
private val isCompilingStdLib = !(settings.sourcepath.isDefault)
2627

2728
// special names
2829
var StringReference : ClassBType = null
@@ -175,12 +176,21 @@ abstract class BCodeTypes extends BCodeIdiomatic {
175176
// ------------------------------------------------
176177

177178
/**
178-
* TODO @lry should probably be a map form ClassBType to Tracked
179+
* Type information for classBTypes.
180+
*
181+
* TODO rename Tracked
179182
*/
180-
val exemplars = new java.util.concurrent.ConcurrentHashMap[BType, Tracked]
183+
val exemplars = new java.util.concurrent.ConcurrentHashMap[ClassBType, Tracked]
181184

182185
/**
183186
* Maps class symbols to their corresponding `Tracked` instance.
187+
*
188+
* This map is only used during the first backend phase (Worker1) where ClassDef trees are
189+
* transformed into ClassNode asm trees. In this phase, ClassBTypes and their Tracked are created
190+
* and added to the `exemplars` map. The `symExemplars` map is only used to know if a symbol has
191+
* already been visited.
192+
*
193+
* TODO move this map to the builder class. it's only used during building. can be gc'd with the builder.
184194
*/
185195
val symExemplars = new java.util.concurrent.ConcurrentHashMap[Symbol, Tracked]
186196

@@ -313,7 +323,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
313323
final def isDeprecated(sym: Symbol): Boolean = { sym.annotations exists (_ matches definitions.DeprecatedAttr) }
314324

315325
/* must-single-thread */
316-
final def hasInternalName(sym: Symbol) = { sym.isClass || (sym.isModule && !sym.isMethod) }
326+
final def hasInternalName(sym: Symbol) = sym.isClass || sym.isModuleNotMethod
317327

318328
/* must-single-thread */
319329
def getSuperInterfaces(csym: Symbol): List[Symbol] = {
@@ -617,20 +627,52 @@ abstract class BCodeTypes extends BCodeIdiomatic {
617627
false
618628
}
619629

620-
/*
630+
/**
621631
* must-single-thread
632+
*
633+
* True for module classes of package level objects. The backend will generate a mirror class for
634+
* such objects.
622635
*/
623-
def isTopLevelModule(sym: Symbol): Boolean = {
624-
exitingPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass }
636+
def isTopLevelModuleClass(sym: Symbol): Boolean = exitingPickler {
637+
// phase travel to pickler required for isNestedClass (looks at owner)
638+
val r = sym.isModuleClass && !sym.isNestedClass
639+
// The mixin phase adds the `lateMODULE` flag to trait implementation classes. Since the flag
640+
// is late, it should not be visible here inside the time travel. We check this.
641+
if (r) assert(!sym.isImplClass, s"isModuleClass should be false for impl class $sym")
642+
r
625643
}
626644

627-
/*
645+
/**
628646
* must-single-thread
647+
*
648+
* True for module classes of modules that are top-level or owned only by objects. Module classes
649+
* for such objects will get a MODULE$ flag and a corresponding static initializer.
629650
*/
630-
def isStaticModule(sym: Symbol): Boolean = {
631-
sym.isModuleClass && !sym.isImplClass && !sym.isLifted
651+
def isStaticModuleClass(sym: Symbol): Boolean = {
652+
/* The implementation of this method is tricky because it is a source-level property. Various
653+
* phases changed the symbol's properties in the meantime.
654+
*
655+
* (1) Phase travel to to pickler is required to exclude implementation classes; they have the
656+
* lateMODULEs after mixin, so isModuleClass would be true.
657+
*
658+
* (2) We cannot use `sym.isStatic` because lambdalift modified (destructively) the owner. For
659+
* example, in
660+
* object T { def f { object U } }
661+
* the owner of U is T, so UModuleClass.isStatic is true. Phase travel does not help here.
662+
* So we basically re-implement `sym.isStaticOwner`, but using the original owner chain.
663+
*/
664+
665+
def isOriginallyStaticOwner(sym: Symbol): Boolean = {
666+
sym.isPackageClass || sym.isModuleClass && isOriginallyStaticOwner(sym.originalOwner)
667+
}
668+
669+
exitingPickler { // (1)
670+
sym.isModuleClass &&
671+
isOriginallyStaticOwner(sym.originalOwner) // (2)
672+
}
632673
}
633674

675+
634676
// ---------------------------------------------------------------------
635677
// ---------------- InnerClasses attribute (JVMS 4.7.6) ----------------
636678
// ---------------------------------------------------------------------
@@ -702,6 +744,10 @@ abstract class BCodeTypes extends BCodeIdiomatic {
702744
var x = ics
703745
while (x ne NoSymbol) {
704746
assert(x.isClass, s"not a class symbol: ${x.fullName}")
747+
// Uses `rawowner` because `owner` reflects changes in the owner chain due to flattening.
748+
// The owner chain of a class only contains classes. This is because the lambdalift phase
749+
// changes the `rawowner` destructively to point to the enclosing class. Before, the owner
750+
// might be for example a method.
705751
val isInner = !x.rawowner.isPackageClass
706752
if (isInner) {
707753
chain ::= x
@@ -729,7 +775,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
729775
null
730776
else {
731777
val outerName = innerSym.rawowner.javaBinaryName
732-
if (isTopLevelModule(innerSym.rawowner)) nme.stripModuleSuffix(outerName)
778+
if (isTopLevelModuleClass(innerSym.rawowner)) nme.stripModuleSuffix(outerName)
733779
else outerName
734780
}
735781
}
@@ -741,12 +787,23 @@ abstract class BCodeTypes extends BCodeIdiomatic {
741787
innerSym.rawname + innerSym.moduleSuffix
742788
}
743789

744-
val flagsWithFinal: Int = mkFlags(
790+
// TODO @lry compare with table in spec: for example, deprecated should not be there it seems.
791+
// http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6-300-D.1-D.1
792+
// including "deprecated" was added in the initial commit of GenASM, but it was never in GenJVM.
793+
val flags: Int = mkFlags(
794+
// TODO @lry adding "static" whenever the class is owned by a module seems wrong.
795+
// class C { object O { class I } }
796+
// here, I is marked static in the InnerClass attribute. But the I constructor takes an outer instance.
797+
// was added in 0469d41
798+
// what should it be? check what would make sense for java reflection.
799+
// member of top-level object should be static? how about anonymous / local class that has
800+
// been lifted to a top-level object?
801+
// member that is only nested in objects should be static?
802+
// verify: will ICodeReader still work after that? the code was introduced because of icode reader.
745803
if (innerSym.rawowner.hasModuleFlag) asm.Opcodes.ACC_STATIC else 0,
746804
javaFlags(innerSym),
747805
if (isDeprecated(innerSym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo-access flag
748806
) & (INNER_CLASSES_FLAGS | asm.Opcodes.ACC_DEPRECATED)
749-
val flags = if (innerSym.isModuleClass) flagsWithFinal & ~asm.Opcodes.ACC_FINAL else flagsWithFinal // For SI-5676, object overriding.
750807

751808
val jname = innerSym.javaBinaryName.toString // never null
752809
val oname = { // null when method-enclosed
@@ -794,14 +851,23 @@ abstract class BCodeTypes extends BCodeIdiomatic {
794851
* must-single-thread
795852
*/
796853
def javaFlags(sym: Symbol): Int = {
797-
// constructors of module classes should be private
798-
// PP: why are they only being marked private at this stage and not earlier?
854+
// constructors of module classes should be private. introduced in b06edbc, probably to prevent
855+
// creating module instances from java. for nested modules, the constructor needs to be public
856+
// since they are created by the outer class and stored in a field. a java client can create
857+
// new instances via outerClassInstance.new InnerModuleClass$().
858+
// TODO: do this early, mark the symbol private.
799859
val privateFlag =
800-
sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModule(sym.owner))
860+
sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModuleClass(sym.owner))
801861

802-
// Final: the only fields which can receive ACC_FINAL are eager vals.
803-
// Neither vars nor lazy vals can, because:
862+
// Symbols marked in source as `final` have the FINAL flag. (In the past, the flag was also
863+
// added to modules and module classes, not anymore since 296b706).
864+
// Note that the presence of the `FINAL` flag on a symbol does not correspond 1:1 to emitting
865+
// ACC_FINAL in bytecode.
866+
//
867+
// Top-level modules are marked ACC_FINAL in bytecode (even without the FINAL flag). Nested
868+
// objects don't get the flag to allow overriding (under -Yoverride-objects, SI-5676).
804869
//
870+
// For fields, only eager val fields can receive ACC_FINAL. vars or lazy vals can't:
805871
// Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
806872
// "Another problem is that the specification allows aggressive
807873
// optimization of final fields. Within a thread, it is permissible to
@@ -818,10 +884,9 @@ abstract class BCodeTypes extends BCodeIdiomatic {
818884
// we can exclude lateFINAL. Such symbols are eligible for inlining, but to
819885
// avoid breaking proxy software which depends on subclassing, we do not
820886
// emit ACC_FINAL.
821-
// Nested objects won't receive ACC_FINAL in order to allow for their overriding.
822887

823888
val finalFlag = (
824-
(((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModule(sym))
889+
(((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym))
825890
&& !sym.enclClass.isInterface
826891
&& !sym.isClassConstructor
827892
&& !sym.isMutable // lazy vals and vars both
@@ -845,6 +910,10 @@ abstract class BCodeTypes extends BCodeIdiomatic {
845910
if (sym.isVarargsMethod) ACC_VARARGS else 0,
846911
if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0
847912
)
913+
// TODO @lry should probably also check / add "deprectated"
914+
// all call sites of "javaFlags" seem to check for deprecation rigth after.
915+
// Exception: the call below in javaFieldFlags. However, the caller of javaFieldFlags then
916+
// does the check.
848917
}
849918

850919
/*

0 commit comments

Comments
 (0)