@@ -21,8 +21,9 @@ abstract class BCodeTypes extends BCodeIdiomatic {
21
21
import global ._
22
22
import bTypes ._
23
23
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)
26
27
27
28
// special names
28
29
var StringReference : ClassBType = null
@@ -175,12 +176,21 @@ abstract class BCodeTypes extends BCodeIdiomatic {
175
176
// ------------------------------------------------
176
177
177
178
/**
178
- * TODO @lry should probably be a map form ClassBType to Tracked
179
+ * Type information for classBTypes.
180
+ *
181
+ * TODO rename Tracked
179
182
*/
180
- val exemplars = new java.util.concurrent.ConcurrentHashMap [BType , Tracked ]
183
+ val exemplars = new java.util.concurrent.ConcurrentHashMap [ClassBType , Tracked ]
181
184
182
185
/**
183
186
* 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.
184
194
*/
185
195
val symExemplars = new java.util.concurrent.ConcurrentHashMap [Symbol , Tracked ]
186
196
@@ -313,7 +323,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
313
323
final def isDeprecated (sym : Symbol ): Boolean = { sym.annotations exists (_ matches definitions.DeprecatedAttr ) }
314
324
315
325
/* 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
317
327
318
328
/* must-single-thread */
319
329
def getSuperInterfaces (csym : Symbol ): List [Symbol ] = {
@@ -617,20 +627,52 @@ abstract class BCodeTypes extends BCodeIdiomatic {
617
627
false
618
628
}
619
629
620
- /*
630
+ /**
621
631
* must-single-thread
632
+ *
633
+ * True for module classes of package level objects. The backend will generate a mirror class for
634
+ * such objects.
622
635
*/
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
625
643
}
626
644
627
- /*
645
+ /**
628
646
* 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.
629
650
*/
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
+ }
632
673
}
633
674
675
+
634
676
// ---------------------------------------------------------------------
635
677
// ---------------- InnerClasses attribute (JVMS 4.7.6) ----------------
636
678
// ---------------------------------------------------------------------
@@ -702,6 +744,10 @@ abstract class BCodeTypes extends BCodeIdiomatic {
702
744
var x = ics
703
745
while (x ne NoSymbol ) {
704
746
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.
705
751
val isInner = ! x.rawowner.isPackageClass
706
752
if (isInner) {
707
753
chain ::= x
@@ -729,7 +775,7 @@ abstract class BCodeTypes extends BCodeIdiomatic {
729
775
null
730
776
else {
731
777
val outerName = innerSym.rawowner.javaBinaryName
732
- if (isTopLevelModule (innerSym.rawowner)) nme.stripModuleSuffix(outerName)
778
+ if (isTopLevelModuleClass (innerSym.rawowner)) nme.stripModuleSuffix(outerName)
733
779
else outerName
734
780
}
735
781
}
@@ -741,12 +787,23 @@ abstract class BCodeTypes extends BCodeIdiomatic {
741
787
innerSym.rawname + innerSym.moduleSuffix
742
788
}
743
789
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.
745
803
if (innerSym.rawowner.hasModuleFlag) asm.Opcodes .ACC_STATIC else 0 ,
746
804
javaFlags(innerSym),
747
805
if (isDeprecated(innerSym)) asm.Opcodes .ACC_DEPRECATED else 0 // ASM pseudo-access flag
748
806
) & (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.
750
807
751
808
val jname = innerSym.javaBinaryName.toString // never null
752
809
val oname = { // null when method-enclosed
@@ -794,14 +851,23 @@ abstract class BCodeTypes extends BCodeIdiomatic {
794
851
* must-single-thread
795
852
*/
796
853
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.
799
859
val privateFlag =
800
- sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModule (sym.owner))
860
+ sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModuleClass (sym.owner))
801
861
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).
804
869
//
870
+ // For fields, only eager val fields can receive ACC_FINAL. vars or lazy vals can't:
805
871
// Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
806
872
// "Another problem is that the specification allows aggressive
807
873
// optimization of final fields. Within a thread, it is permissible to
@@ -818,10 +884,9 @@ abstract class BCodeTypes extends BCodeIdiomatic {
818
884
// we can exclude lateFINAL. Such symbols are eligible for inlining, but to
819
885
// avoid breaking proxy software which depends on subclassing, we do not
820
886
// emit ACC_FINAL.
821
- // Nested objects won't receive ACC_FINAL in order to allow for their overriding.
822
887
823
888
val finalFlag = (
824
- (((sym.rawflags & symtab.Flags .FINAL ) != 0 ) || isTopLevelModule (sym))
889
+ (((sym.rawflags & symtab.Flags .FINAL ) != 0 ) || isTopLevelModuleClass (sym))
825
890
&& ! sym.enclClass.isInterface
826
891
&& ! sym.isClassConstructor
827
892
&& ! sym.isMutable // lazy vals and vars both
@@ -845,6 +910,10 @@ abstract class BCodeTypes extends BCodeIdiomatic {
845
910
if (sym.isVarargsMethod) ACC_VARARGS else 0 ,
846
911
if (sym.hasFlag(symtab.Flags .SYNCHRONIZED )) ACC_SYNCHRONIZED else 0
847
912
)
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.
848
917
}
849
918
850
919
/*
0 commit comments