From 4c2217e8a0e67ccd675aa4f1be253f87697c9025 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 10 Jun 2014 10:55:06 +0200 Subject: Minor cleanups and comments in GenBCode --- .../scala/tools/nsc/backend/jvm/BCodeHelpers.scala | 3 ++ .../scala/tools/nsc/backend/jvm/BCodeTypes.scala | 46 +++++++++++++++++----- .../scala/tools/nsc/backend/jvm/BTypes.scala | 22 +++++++++-- .../scala/tools/nsc/backend/jvm/GenASM.scala | 3 ++ .../scala/tools/nsc/backend/jvm/GenBCode.scala | 2 +- src/reflect/scala/reflect/internal/Symbols.scala | 28 ++++++++++--- 6 files changed, 83 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index 51bfdf0027..07890dd5f8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -460,6 +460,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { // Can't call .toInterface (at this phase) or we trip an assertion. // See PackratParser#grow for a method which fails with an apparent mismatch // between "object PackratParsers$class" and "trait PackratParsers" + // TODO @lry do we have a test for that? if (sym.isImplClass) { // pos/spec-List.scala is the sole failure if we don't check for NoSymbol val traitSym = sym.owner.info.decl(tpnme.interfaceName(sym.name)) @@ -469,6 +470,8 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { } } + // TODO @lry: code duplication between here and method asmClassType. + assert(hasInternalName(sym), s"Invoked for a symbol lacking JVM internal name: ${sym.fullName}") assert(!phantomTypeMap.contains(sym), "phantom types not supposed to reach here.") diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala index 62dfb4917d..22cb189c70 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala @@ -21,8 +21,9 @@ abstract class BCodeTypes extends BCodeIdiomatic { import global._ import bTypes._ - // when compiling the Scala library, some assertions don't hold (e.g., scala.Boolean has null superClass although it's not an interface) - val isCompilingStdLib = !(settings.sourcepath.isDefault) + // Used only for assertions. When compiling the Scala library, some assertions don't hold + // (e.g., scala.Boolean has null superClass although it's not an interface) + private val isCompilingStdLib = !(settings.sourcepath.isDefault) // special names var StringReference : ClassBType = null @@ -175,12 +176,21 @@ abstract class BCodeTypes extends BCodeIdiomatic { // ------------------------------------------------ /** - * TODO @lry should probably be a map form ClassBType to Tracked + * Type information for classBTypes. + * + * TODO rename Tracked */ - val exemplars = new java.util.concurrent.ConcurrentHashMap[BType, Tracked] + val exemplars = new java.util.concurrent.ConcurrentHashMap[ClassBType, Tracked] /** * Maps class symbols to their corresponding `Tracked` instance. + * + * This map is only used during the first backend phase (Worker1) where ClassDef trees are + * transformed into ClassNode asm trees. In this phase, ClassBTypes and their Tracked are created + * and added to the `exemplars` map. The `symExemplars` map is only used to know if a symbol has + * already been visited. + * + * TODO move this map to the builder class. it's only used during building. can be gc'd with the builder. */ val symExemplars = new java.util.concurrent.ConcurrentHashMap[Symbol, Tracked] @@ -313,7 +323,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { final def isDeprecated(sym: Symbol): Boolean = { sym.annotations exists (_ matches definitions.DeprecatedAttr) } /* must-single-thread */ - final def hasInternalName(sym: Symbol) = { sym.isClass || (sym.isModule && !sym.isMethod) } + final def hasInternalName(sym: Symbol) = sym.isClass || sym.isModuleNotMethod /* must-single-thread */ def getSuperInterfaces(csym: Symbol): List[Symbol] = { @@ -702,6 +712,10 @@ abstract class BCodeTypes extends BCodeIdiomatic { var x = ics while (x ne NoSymbol) { assert(x.isClass, s"not a class symbol: ${x.fullName}") + // Uses `rawowner` because `owner` reflects changes in the owner chain due to flattening. + // The owner chain of a class only contains classes. This is because the lambdalift phase + // changes the `rawowner` destructively to point to the enclosing class. Before, the owner + // might be for example a method. val isInner = !x.rawowner.isPackageClass if (isInner) { chain ::= x @@ -794,14 +808,23 @@ abstract class BCodeTypes extends BCodeIdiomatic { * must-single-thread */ def javaFlags(sym: Symbol): Int = { - // constructors of module classes should be private - // PP: why are they only being marked private at this stage and not earlier? + // constructors of module classes should be private. introduced in b06edbc, probably to prevent + // creating module instances from java. for nested modules, the constructor needs to be public + // since they are created by the outer class and stored in a field. a java client can create + // new instances via outerClassInstance.new InnerModuleClass$(). + // TODO: do this early, mark the symbol private. val privateFlag = sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModule(sym.owner)) - // Final: the only fields which can receive ACC_FINAL are eager vals. - // Neither vars nor lazy vals can, because: + // Symbols marked in source as `final` have the FINAL flag. (In the past, the flag was also + // added to modules and module classes, not anymore since 296b706). + // Note that the presence of the `FINAL` flag on a symbol does not correspond 1:1 to emitting + // ACC_FINAL in bytecode. + // + // Top-level modules are marked ACC_FINAL in bytecode (even without the FINAL flag). Nested + // objects don't get the flag to allow overriding (under -Yoverride-objects, SI-5676). // + // For fields, only eager val fields can receive ACC_FINAL. vars or lazy vals can't: // Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3 // "Another problem is that the specification allows aggressive // optimization of final fields. Within a thread, it is permissible to @@ -818,7 +841,6 @@ abstract class BCodeTypes extends BCodeIdiomatic { // we can exclude lateFINAL. Such symbols are eligible for inlining, but to // avoid breaking proxy software which depends on subclassing, we do not // emit ACC_FINAL. - // Nested objects won't receive ACC_FINAL in order to allow for their overriding. val finalFlag = ( (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModule(sym)) @@ -845,6 +867,10 @@ abstract class BCodeTypes extends BCodeIdiomatic { if (sym.isVarargsMethod) ACC_VARARGS else 0, if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0 ) + // TODO @lry should probably also check / add "deprectated" + // all call sites of "javaFlags" seem to check for deprecation rigth after. + // Exception: the call below in javaFieldFlags. However, the caller of javaFieldFlags then + // does the check. } /* diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index 5b0fa6f7a8..aac4f9cbfc 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -235,10 +235,24 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) { /** * Class or Interface type. * - * Classes are represented using their name as a slice of the `chrs` array. This representation is - * efficient because the JVM class name is initially created using `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. + * The information for creating a ClassBType (superClass, interfaces, etc) is obtained + * - either from a ClassSymbol, for classes being compiled or referenced from source (see + * BCodeTypes) + * - or, during inlining, from ASM ClassNodes that are parsed from class files. + * + * The class name is represented as a slice of the `chrs` array. 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. + * + * Not a case class because that would expose the constructor that takes (offset, length) + * parameters (I didn't find a way to make it private, also the factory in the companion). + * + * @param offset See below + * @param length The class name is represented as offset and length in the `chrs` array. + * The (public) constructors of ClassBType take a BTypeName, which are + * hash-consed. This ensures that two ClassBType instances for the same name + * have the same offset and length. * * Not a case class because that would expose the (Int, Int) constructor (didn't find a way to * make it private, also the factory in the companion). diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 988c04f514..ce7e7c49ff 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -636,6 +636,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { innerSym.rawname + innerSym.moduleSuffix // add inner classes which might not have been referenced yet + // TODO @lry according to the spec, all nested classes should be added, also local and + // anonymous. This seems to add only member classes - or not? it's exitingErasure, so maybe + // local / anonymous classes have been lifted by lambdalift. are they in the "decls" though? exitingErasure { for (sym <- List(csym, csym.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass) innerClassBuffer += m diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala index 9b292fee7f..6b192556d9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala @@ -271,7 +271,7 @@ abstract class GenBCode extends BCodeSyncAndTry { override def run() { arrivalPos = 0 // just in case - scalaPrimitives.init + scalaPrimitives.init() initBCodeTypes() // initBytecodeWriter invokes fullName, thus we have to run it before the typer-dependent thread is activated. diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 2ce54d2259..cc750fb88a 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1099,13 +1099,28 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ------ owner attribute -------------------------------------------------------------- - /** In general when seeking the owner of a symbol, one should call `owner`. - * The other possibilities include: - * - call `safeOwner` if it is expected that the target may be NoSymbol - * - call `assertOwner` if it is an unrecoverable error if the target is NoSymbol + /** + * The owner of a symbol. Changes over time to adapt to the structure of the trees: + * - Up to lambdalift, the owner is the lexically enclosing definition. For definitions + * in a local block, the owner is also the next enclosing definition. + * - After lambdalift, all local method and class definitions (those not owned by a class + * or package class) change their owner to the enclosing class. This is done through + * a destructive "sym.owner = sym.owner.enclClass". The old owner is saved by + * saveOriginalOwner for the backend (needed to generate the EnclosingMethod attribute). + * - After flatten, all classes are owned by a PackageClass. This is done through a + * phase check (if after flatten) in the (overridden) method "def owner" in + * ModuleSymbol / ClassSymbol. The `rawowner` field is not modified. + * - Owners are also changed in other situations, for example when moving trees into a new + * lexical context, e.g. in the named/default arguments tranformation, or when translating + * extension method definitions. + * + * In general when seeking the owner of a symbol, one should call `owner`. + * The other possibilities include: + * - call `safeOwner` if it is expected that the target may be NoSymbol + * - call `assertOwner` if it is an unrecoverable error if the target is NoSymbol * - * `owner` behaves like `safeOwner`, but logs NoSymbol.owner calls under -Xdev. - * `assertOwner` aborts compilation immediately if called on NoSymbol. + * `owner` behaves like `safeOwner`, but logs NoSymbol.owner calls under -Xdev. + * `assertOwner` aborts compilation immediately if called on NoSymbol. */ def owner: Symbol = { if (Statistics.hotEnabled) Statistics.incCounter(ownerCount) @@ -2811,6 +2826,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def owner = { if (Statistics.hotEnabled) Statistics.incCounter(ownerCount) + // a module symbol may have the lateMETHOD flag after refchecks, see isModuleNotMethod if (!isMethod && needsFlatClasses) rawowner.owner else rawowner } -- cgit v1.2.3 From 91c7be16288e060aadb8aa7b0315b12e98621f02 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 10 Jun 2014 10:59:51 +0200 Subject: Comment summarizing the JVM spec for InnerClass / EnclosingMethod --- .../scala/tools/nsc/backend/jvm/BTypes.scala | 199 +++++++++++++++++++++ 1 file changed, 199 insertions(+) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index aac4f9cbfc..508c585393 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -232,6 +232,205 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) { } } + /** + * InnerClass and EnclosingMethod attributes (EnclosingMethod is displayed as OUTERCLASS in asm). + * + * In this summary, "class" means "class or interface". + * + * JLS: http://docs.oracle.com/javase/specs/jls/se8/html/index.html + * JVMS: http://docs.oracle.com/javase/specs/jvms/se8/html/index.html + * + * Terminology + * ----------- + * + * - Nested class (JLS 8): class whose declaration occurs within the body of another class + * + * - Top-level class (JLS 8): non-nested class + * + * - Inner class (JLS 8.1.3): nested class that is not (explicitly or implicitly) static + * + * - Member class (JLS 8.5): class directly enclosed in the body of a class (and not, for + * example, defined in a method). Member classes cannot be anonymous. May be static. + * + * - Local class (JLS 14.3): nested, non-anonymous class that is not a member of a class + * - cannot be static (therefore they are "inner" classes) + * - can be defined in a method, a constructor or in an initializer block + * + * - Initializer block (JLS 8.6 / 8.7): block of statements in a java class + * - static initializer: executed before constructor body + * - instance initializer: exectued when class is initialized (instance creation, static + * field access, ...) + * + * + * InnerClass + * ---------- + * + * The JVMS 4.7.6 requires an entry for every class mentioned in a CONSTANT_Class_info in the + * constant pool (CP) that is not a member of a package (JLS 7.1). + * + * The JLS 13.1, points 9. / 10. requires: a class must reference (in the CP) + * - its immediately enclosing class + * - all of its member classes + * - all local and anonymous classes that appear elsewhere (method, constructor, initializer + * block, field initializer) + * + * In a comment, the 4.7.6 spec says: this implies an entry in the InnerClass attribute for + * - All enclosing classes (except the outermost, which is top-level) + * - My comment: not sure how this is implied, below (*) a Java counter-example. + * In any case, the Java compiler seems to add all enclosing classes, even if they are not + * otherwise mentioned in the CP. So we should do the same. + * - All nested classes (including anonymous and local, but not transitively) + * + * Fields in the InnerClass entries: + * - inner class: the (nested) class C we are talking about + * - outer class: the class of which C is a member. Has to be null for non-members, i.e. for + * local and anonymous classes. + * - inner name: A string with the simple name of the inner class. Null for anonymous classes. + * - flags: access property flags, details in JVMS, table in 4.7.6. + * + * + * Note 1: when a nested class is present in the InnerClass attribute, all of its enclosing + * classes have to be present as well (by the rules above). Example: + * + * class Outer { class I1 { class I2 { } } } + * class User { Outer.I1.I2 foo() { } } + * + * The return type "Outer.I1.I2" puts "Outer$I1$I2" in the CP, therefore the class is added to the + * InnerClass attribute. For this entry, the "outer class" field will be "Outer$I1". This in turn + * adds "Outer$I1" to the CP, which requires adding that class to the InnerClass attribute. + * (For local / anonymous classes this would not be the case, since the "outer class" attribute + * would be empty. However, no class (other than the enclosing class) can refer to them, as they + * have no name.) + * + * In the current implementation of the Scala compiler, when adding a class to the InnerClass + * attribute, all of its enclosing classes will be added as well. Javac seems to do the same, + * see (*). + * + * + * Note 2: If a class name is mentioned only in a CONSTANT_Utf8_info, but not in a + * CONSTANT_Class_info, the JVMS does not require an entry in the InnerClass attribute. However, + * the Java compiler seems to add such classes anyway. For example, when using an annotation, the + * annotation class is stored as a CONSTANT_Utf8_info in the CP: + * + * @O.Ann void foo() { } + * + * adds "const #13 = Asciz LO$Ann;;" in the constant pool. The "RuntimeInvisibleAnnotations" + * attribute refers to that constant pool entry. Even though there is no other reference to + * `O.Ann`, the java compiler adds an entry for that class to the InnerClass attribute (which + * entails adding a CONSTANT_Class_info for the class). + * + * + * + * EnclosingMethod + * --------------- + * + * JVMS 4.7.7: the attribute must be present "if and only if it represents a local class + * or an anonymous class" (i.e. not for member classes). + * + * Fields: + * - class: the enclosing class + * - method: the enclosing method (or constructor). Null if the class is not enclosed by a + * method, i.e. for + * - local or anonymous classes defined in (static or non-static) initializer blocks + * - anonymous classes defined in initializer blocks or field initializers + * + * Note: the field is required for anonymous classes defined within local variable + * initializers (within a method), Java example below (**). + * + * Currently, the Scala compiler sets "method" to the class constructor for classes + * defined in initializer blocks or field initializers. This is probably OK, since the + * Scala compiler desugars these statements into to the primary constructor. + * + * + * (*) + * public class Test { + * void foo() { + * class Foo1 { + * // constructor statement block + * { + * class Foo2 { + * class Foo3 { } + * } + * } + * } + * } + * } + * + * The class file Test$1Foo1$1Foo2$Foo3 has no reference to the class Test$1Foo1, however it + * still contains an InnerClass attribute for Test$1Foo1. + * Maybe this is just because the Java compiler follows the JVMS comment ("InnerClasses + * information for each enclosing class"). + * + * + * (**) + * void foo() { + * // anonymous class defined in local variable initializer expression. + * Runnable x = true ? (new Runnable() { + * public void run() { return; } + * }) : null; + * } + * + * The EnclosingMethod attribute of the anonymous class mentions "foo" in the "method" field. + * + * + * Java Compatibility + * ------------------ + * + * In the InnerClass entry for classes in top-level modules, the "outer class" is emitted as the + * mirror class (or the existing companion class), i.e. C1 is nested in T (not T$). + * For classes nested in a nested object, the "outer class" is the module class: C2 is nested in T$N$ + * object T { + * class C1 + * object N { class C2 } + * } + * + * Reason: java compat. It's a "best effort" "solution". If you want to use "C1" from Java, you + * can write "T.C1", and the Java compiler will translate that to the classfile T$C1. + * + * If we would emit the "outer class" of C1 as "T$", then in Java you'd need to write "T$.C1" + * because the java compiler looks at the InnerClass attribute to find if an inner class exists. + * However, the Java compiler would then translate the '.' to '$' and you'd get the class name + * "T$$C1". This class file obviously does not exist. + * + * Directly using the encoded class name "T$C1" in Java does not work: since the classfile + * describes a nested class, the Java compiler hides it from the classpath and will report + * "cannot find symbol T$C1". This means that the class T.N.C2 cannot be referenced from a + * Java source file in any way. + * + * + * STATIC flag + * ----------- + * + * Java: static nested classes have the "static" flag in the InnerClass attribute. This is not the + * case for local classes defined within a static method, even though such classes, as they are + * defined in a static context, don't take an "outer" instance. + * Non-static nested classes (inner classes, including local classes defined in a non-static + * method) take an "outer" instance on construction. + * + * Scala: Explicitouter adds an "outer" parameter to nested classes, except for classes defined + * in a static context, i.e. when all outer classes are module classes. + * package p + * object O1 { + * class C1 // static + * object O2 { + * def f = { + * class C2 { // static + * class C3 // non-static, needs outer + * } + * } + * } + * } + * + * Int the InnerClass attribute, the `static` flag is added for all classes defined in a static + * context, i.e. also for C2. This is different than in Java. + * + * + * Mirror Classes + * -------------- + * + * TODO: innerclass attributes on mirror class, bean info class + */ + /** * Class or Interface type. * -- cgit v1.2.3 From 57de87e655820df9d016613badc13e3e86059417 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 10 Jun 2014 11:00:25 +0200 Subject: Remove unnessecary check when computing InnerClass attribute flags The final flag is computed correctly by javaFlags. --- src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala index 22cb189c70..9b131cb123 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala @@ -755,12 +755,23 @@ abstract class BCodeTypes extends BCodeIdiomatic { innerSym.rawname + innerSym.moduleSuffix } - val flagsWithFinal: Int = mkFlags( + // TODO @lry compare with table in spec: for example, deprecated should not be there it seems. + // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6-300-D.1-D.1 + // including "deprecated" was added in the initial commit of GenASM, but it was never in GenJVM. + val flags: Int = mkFlags( + // TODO @lry adding "static" whenever the class is owned by a module seems wrong. + // class C { object O { class I } } + // here, I is marked static in the InnerClass attribute. But the I constructor takes an outer instance. + // was added in 0469d41 + // what should it be? check what would make sense for java reflection. + // member of top-level object should be static? how about anonymous / local class that has + // been lifted to a top-level object? + // member that is only nested in objects should be static? + // verify: will ICodeReader still work after that? the code was introduced because of icode reader. if (innerSym.rawowner.hasModuleFlag) asm.Opcodes.ACC_STATIC else 0, javaFlags(innerSym), if (isDeprecated(innerSym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo-access flag ) & (INNER_CLASSES_FLAGS | asm.Opcodes.ACC_DEPRECATED) - val flags = if (innerSym.isModuleClass) flagsWithFinal & ~asm.Opcodes.ACC_FINAL else flagsWithFinal // For SI-5676, object overriding. val jname = innerSym.javaBinaryName.toString // never null val oname = { // null when method-enclosed -- cgit v1.2.3 From 1bed39a1cb2be13cb26a038dfd1649964063c498 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 10 Jun 2014 11:17:47 +0200 Subject: Pattern matching on ClassBType extracts the inernalName --- .../scala/tools/nsc/backend/jvm/BTypes.scala | 49 ++++++++++++---------- 1 file changed, 27 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala index 508c585393..15bc068533 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -43,9 +43,9 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) { case FLOAT => "F" case LONG => "J" case DOUBLE => "D" - case c @ ClassBType(_, _) => "L" + c.internalName + ";" - case ArrayBType(component) => "[" + component - case MethodBType(args, res) => "(" + args.mkString + ")" + res + case ClassBType(internalName) => "L" + internalName + ";" + case ArrayBType(component) => "[" + component + case MethodBType(args, res) => "(" + args.mkString + ")" + res } /** @@ -160,9 +160,9 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) { case FLOAT => asm.Type.FLOAT_TYPE case LONG => asm.Type.LONG_TYPE case DOUBLE => asm.Type.DOUBLE_TYPE - case c @ ClassBType(_, _) => asm.Type.getObjectType(c.internalName) // (*) - case a @ ArrayBType(_) => asm.Type.getObjectType(a.descriptor) - case m @ MethodBType(_, _) => asm.Type.getMethodType(m.descriptor) + case ClassBType(internalName) => asm.Type.getObjectType(internalName) // see (*) above + case a: ArrayBType => asm.Type.getObjectType(a.descriptor) + case m: MethodBType => asm.Type.getMethodType(m.descriptor) } def asRefBType : RefBType = this.asInstanceOf[RefBType] @@ -227,8 +227,8 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) { * This can be verified for example using javap or ASMifier. */ def classOrArrayType: String = this match { - case c: ClassBType => c.internalName - case a: ArrayBType => a.descriptor + case ClassBType(internalName) => internalName + case a: ArrayBType => a.descriptor } } @@ -458,21 +458,23 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) { */ class ClassBType private(val offset: Int, val length: Int) extends RefBType { /** - * Construct a ClassBType for a given (intenred) class name. + * Construct a ClassBType from the (intenred) internal name of a class. * - * @param n The class name as a slice of the `chrs` array, without the surrounding 'L' and ';'. - * Note that `classSymbol.javaBinaryName` returns exactly such a name. + * @param internalName The internal name as a slice of the `chrs` array. The internal name does + * not have the surrounding 'L' and ';'. Note that + * `classSymbol.javaBinaryName` returns exactly such a name. */ - def this(n: BTypeName) = this(n.start, n.length) + def this(internalName: BTypeName) = this(internalName.start, internalName.length) /** - * Construct a ClassBType for a given java class name. + * Construct a ClassBType from the internal name of a class. * - * @param s A class name of the form "java/lang/String", without the surrounding 'L' and ';'. + * @param internalName The internal name of a class has the form "java/lang/String", without the + * surrounding 'L' and ';'. */ - def this(s: String) = this({ - assert(!(s.head == 'L' && s.last == ';'), s"Descriptor instead of internal name: $s") - createNewName(s) + def this(internalName: String) = this({ + assert(!(internalName.head == 'L' && internalName.last == ';'), s"Descriptor instead of internal name: $internalName") + createNewName(internalName) }) /** @@ -490,7 +492,7 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) { * Custom equals / hashCode are needed because this is not a case class. */ override def equals(o: Any): Boolean = (this eq o.asInstanceOf[Object]) || (o match { - case ClassBType(`offset`, `length`) => true + case c: ClassBType => c.offset == this.offset && c.length == this.length case _ => false }) @@ -504,12 +506,15 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) { } object ClassBType { - def apply(n: BTypeName): ClassBType = new ClassBType(n) - def apply(s: String): ClassBType = new ClassBType(s) + def apply(internalName: BTypeName): ClassBType = new ClassBType(internalName) + def apply(internalName: String): ClassBType = new ClassBType(internalName) - def unapply(c: ClassBType): Option[(Int, Int)] = + /** + * Pattern matching on a ClassBType extracts the `internalName` of the class. + */ + def unapply(c: ClassBType): Option[String] = if (c == null) None - else Some((c.offset, c.length)) + else Some(c.internalName) } case class ArrayBType(componentType: BType) extends RefBType { -- cgit v1.2.3 From 0ccdb151ffe9caa9eae7d01a4f2eacc87fa8f5ff Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 10 Jun 2014 11:21:13 +0200 Subject: Clean up and document some usages of flags in the backend --- .../tools/nsc/backend/jvm/BCodeBodyBuilder.scala | 2 +- .../tools/nsc/backend/jvm/BCodeSkelBuilder.scala | 2 +- .../scala/tools/nsc/backend/jvm/BCodeTypes.scala | 50 ++++++++++++--- .../scala/tools/nsc/backend/jvm/GenBCode.scala | 2 +- .../scala/tools/nsc/symtab/SymbolLoaders.scala | 41 ++++++++---- .../scala/tools/nsc/transform/Flatten.scala | 11 +++- .../scala/tools/nsc/transform/LazyVals.scala | 4 +- src/reflect/scala/reflect/internal/Symbols.scala | 75 +++++++++++++++++----- .../scala/reflect/runtime/SymbolLoaders.scala | 11 +++- 9 files changed, 154 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index bffa4bc51d..b79cf01f41 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -977,7 +977,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { if (!isModuleInitialized && jMethodName == INSTANCE_CONSTRUCTOR_NAME && jname == INSTANCE_CONSTRUCTOR_NAME && - isStaticModule(siteSymbol)) { + isStaticModuleClass(siteSymbol)) { isModuleInitialized = true mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) mnode.visitFieldInsn( diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index effc68c5e3..f8ecb8e643 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -95,7 +95,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { claszSymbol = cd.symbol isCZParcelable = isAndroidParcelableClass(claszSymbol) - isCZStaticModule = isStaticModule(claszSymbol) + isCZStaticModule = isStaticModuleClass(claszSymbol) isCZRemote = isRemote(claszSymbol) thisName = internalName(claszSymbol) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala index 9b131cb123..b373f8d74d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala @@ -627,20 +627,52 @@ abstract class BCodeTypes extends BCodeIdiomatic { false } - /* + /** * must-single-thread + * + * True for module classes of package level objects. The backend will generate a mirror class for + * such objects. */ - def isTopLevelModule(sym: Symbol): Boolean = { - exitingPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass } + def isTopLevelModuleClass(sym: Symbol): Boolean = exitingPickler { + // phase travel to pickler required for isNestedClass (looks at owner) + val r = sym.isModuleClass && !sym.isNestedClass + // The mixin phase adds the `lateMODULE` flag to trait implementation classes. Since the flag + // is late, it should not be visible here inside the time travel. We check this. + if (r) assert(!sym.isImplClass, s"isModuleClass should be false for impl class $sym") + r } - /* + /** * must-single-thread + * + * True for module classes of modules that are top-level or owned only by objects. Module classes + * for such objects will get a MODULE$ flag and a corresponding static initializer. */ - def isStaticModule(sym: Symbol): Boolean = { - sym.isModuleClass && !sym.isImplClass && !sym.isLifted + def isStaticModuleClass(sym: Symbol): Boolean = { + /* The implementation of this method is tricky because it is a source-level property. Various + * phases changed the symbol's properties in the meantime. + * + * (1) Phase travel to to pickler is required to exclude implementation classes; they have the + * lateMODULEs after mixin, so isModuleClass would be true. + * + * (2) We cannot use `sym.isStatic` because lambdalift modified (destructively) the owner. For + * example, in + * object T { def f { object U } } + * the owner of U is T, so UModuleClass.isStatic is true. Phase travel does not help here. + * So we basically re-implement `sym.isStaticOwner`, but using the original owner chain. + */ + + def isOriginallyStaticOwner(sym: Symbol): Boolean = { + sym.isPackageClass || sym.isModuleClass && isOriginallyStaticOwner(sym.originalOwner) + } + + exitingPickler { // (1) + sym.isModuleClass && + isOriginallyStaticOwner(sym.originalOwner) // (2) + } } + // --------------------------------------------------------------------- // ---------------- InnerClasses attribute (JVMS 4.7.6) ---------------- // --------------------------------------------------------------------- @@ -743,7 +775,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { null else { val outerName = innerSym.rawowner.javaBinaryName - if (isTopLevelModule(innerSym.rawowner)) nme.stripModuleSuffix(outerName) + if (isTopLevelModuleClass(innerSym.rawowner)) nme.stripModuleSuffix(outerName) else outerName } } @@ -825,7 +857,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { // new instances via outerClassInstance.new InnerModuleClass$(). // TODO: do this early, mark the symbol private. val privateFlag = - sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModule(sym.owner)) + sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModuleClass(sym.owner)) // Symbols marked in source as `final` have the FINAL flag. (In the past, the flag was also // added to modules and module classes, not anymore since 296b706). @@ -854,7 +886,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { // emit ACC_FINAL. val finalFlag = ( - (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModule(sym)) + (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym)) && !sym.enclClass.isInterface && !sym.isClassConstructor && !sym.isMutable // lazy vals and vars both diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala index 6b192556d9..af5d01458a 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala @@ -165,7 +165,7 @@ abstract class GenBCode extends BCodeSyncAndTry { // -------------- mirror class, if needed -------------- val mirrorC = - if (isStaticModule(claszSymbol) && isTopLevelModule(claszSymbol)) { + if (isTopLevelModuleClass(claszSymbol)) { if (claszSymbol.companionClass == NoSymbol) { mirrorCodeGen.genMirrorClass(claszSymbol, cunit) } else { diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 447fa66ae4..82c2a4d6ed 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -240,6 +240,12 @@ abstract class SymbolLoaders { } } + private def phaseBeforeRefchecks: Phase = { + var resPhase = phase + while (resPhase.refChecked) resPhase = resPhase.prev + resPhase + } + /** * Load contents of a package */ @@ -248,19 +254,24 @@ abstract class SymbolLoaders { protected def doComplete(root: Symbol) { assert(root.isPackageClass, root) - root.setInfo(new PackageClassInfoType(newScope, root)) - - if (!root.isRoot) { - for (classRep <- classpath.classes) { - initializeFromClassPath(root, classRep) - } - } - if (!root.isEmptyPackageClass) { - for (pkg <- classpath.packages) { - enterPackage(root, pkg.name, new PackageLoader(pkg)) + // Time travel to a phase before refchecks avoids an initialization issue. `openPackageModule` + // creates a module symbol and invokes invokes `companionModule` while the `infos` field is + // still null. This calls `isModuleNotMethod`, which forces the `info` if run after refchecks. + enteringPhase(phaseBeforeRefchecks) { + root.setInfo(new PackageClassInfoType(newScope, root)) + + if (!root.isRoot) { + for (classRep <- classpath.classes) { + initializeFromClassPath(root, classRep) + } } + if (!root.isEmptyPackageClass) { + for (pkg <- classpath.packages) { + enterPackage(root, pkg.name, new PackageLoader(pkg)) + } - openPackageModule(root) + openPackageModule(root) + } } } } @@ -290,7 +301,13 @@ abstract class SymbolLoaders { protected def doComplete(root: Symbol) { val start = if (Statistics.canEnable) Statistics.startTimer(classReadNanos) else null - classfileParser.parse(classfile, root) + + // Running the classfile parser after refchecks can lead to "illegal class file dependency" + // errors. More concretely, the classfile parser calls "sym.companionModule", which calls + // "isModuleNotMethod" on the companion. After refchecks, this method forces the info, which + // may run the classfile parser. This produces the error. + enteringPhase(phaseBeforeRefchecks)(classfileParser.parse(classfile, root)) + if (root.associatedFile eq NoAbstractFile) { root match { // In fact, the ModuleSymbol forwards its setter to the module class diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala index c3fbfae322..fa53ef48b5 100644 --- a/src/compiler/scala/tools/nsc/transform/Flatten.scala +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -76,8 +76,17 @@ abstract class Flatten extends InfoTransform { for (sym <- decls) { if (sym.isTerm && !sym.isStaticModule) { decls1 enter sym - if (sym.isModule) + if (sym.isModule) { + // Nested, non-static moduls are transformed to methods. + assert(sym.isMethod, s"Non-static $sym should have the lateMETHOD flag from RefChecks") + // Note that module classes are not entered into the 'decls' of the ClassInfoType + // of the outer class, only the module symbols are. So the current loop does + // not visit module classes. Therefore we set the LIFTED flag here for module + // classes. + // TODO: should we also set the LIFTED flag for static, nested module classes? + // currently they don't get the flag, even though they are lifted to the package sym.moduleClass setFlag LIFTED + } } else if (sym.isClass) liftSymbol(sym) } diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index b71d14a04f..38671ebaae 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -192,13 +192,15 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): Tree = { + // Q: is there a reason to first set owner to `clazz` (by using clazz.newMethod), and then + // changing it to lzyVal.owner very soon after? Could we just do lzyVal.owner.newMethod? val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE) defSym setInfo MethodType(List(), lzyVal.tpe.resultType) defSym.owner = lzyVal.owner debuglog(s"crete slow compute path $defSym with owner ${defSym.owner} for lazy val $lzyVal") if (bitmaps.contains(lzyVal)) bitmaps(lzyVal).map(_.owner = defSym) - val rhs: Tree = (gen.mkSynchronizedCheck(clazz, cond, syncBody, stats)).changeOwner(currentOwner -> defSym) + val rhs: Tree = gen.mkSynchronizedCheck(clazz, cond, syncBody, stats).changeOwner(currentOwner -> defSym) DefDef(defSym, addBitmapDefs(lzyVal, BLOCK(rhs, retVal))) } diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index cc750fb88a..b6cce4524b 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -55,23 +55,29 @@ trait Symbols extends api.Symbols { self: SymbolTable => def newFreeTypeSymbol(name: TypeName, flags: Long = 0L, origin: String): FreeTypeSymbol = new FreeTypeSymbol(name, origin) initFlags flags - /** The original owner of a class. Used by the backend to generate - * EnclosingMethod attributes. + /** + * This map stores the original owner the the first time the owner of a symbol is re-assigned. + * The original owner of a symbol is needed in some places in the backend. Ideally, owners should + * be versioned like the type history. */ - val originalOwner = perRunCaches.newMap[Symbol, Symbol]() + private val originalOwnerMap = perRunCaches.newMap[Symbol, Symbol]() // TODO - don't allow the owner to be changed without checking invariants, at least // when under some flag. Define per-phase invariants for owner/owned relationships, // e.g. after flatten all classes are owned by package classes, there are lots and // lots of these to be declared (or more realistically, discovered.) - protected def saveOriginalOwner(sym: Symbol) { - if (originalOwner contains sym) () - else originalOwner(sym) = sym.rawowner + protected def saveOriginalOwner(sym: Symbol): Unit = { + // some synthetic symbols have NoSymbol as owner initially + if (sym.owner != NoSymbol) { + if (originalOwnerMap contains sym) () + else originalOwnerMap(sym) = sym.rawowner + } } + protected def originalEnclosingMethod(sym: Symbol): Symbol = { if (sym.isMethod || sym == NoSymbol) sym else { - val owner = originalOwner.getOrElse(sym, sym.rawowner) + val owner = sym.originalOwner if (sym.isLocalDummy) owner.enclClass.primaryConstructor else originalEnclosingMethod(owner) } @@ -757,8 +763,22 @@ trait Symbols extends api.Symbols { self: SymbolTable => * So "isModuleNotMethod" exists not for its achievement in * brevity, but to encapsulate the relevant condition. */ - def isModuleNotMethod = isModule && !isMethod - def isStaticModule = isModuleNotMethod && isStatic + def isModuleNotMethod = { + if (isModule) { + if (phase.refChecked) this.info // force completion to make sure lateMETHOD is there. + !isMethod + } else false + } + + // After RefChecks, the `isStatic` check is mostly redundant: all non-static modules should + // be methods (and vice versa). There's a corner case on the vice-versa with mixed-in module + // symbols: + // trait T { object A } + // object O extends T + // The module symbol A is cloned into T$impl (addInterfaces), and then cloned into O (mixin). + // Since the original A is not static, it's turned into a method. The clone in O however is + // static (owned by a module), but it's also a method. + def isStaticModule = isModuleNotMethod && isStatic final def isInitializedToDefault = !isType && hasAllFlags(DEFAULTINIT | ACCESSOR) final def isThisSym = isTerm && owner.thisSym == this @@ -909,10 +929,31 @@ trait Symbols extends api.Symbols { self: SymbolTable => ) final def isModuleVar = hasFlag(MODULEVAR) - /** Is this symbol static (i.e. with no outer instance)? - * Q: When exactly is a sym marked as STATIC? - * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or any number of levels deep. - * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6 + /** + * Is this symbol static (i.e. with no outer instance)? + * Q: When exactly is a sym marked as STATIC? + * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or + * any number of levels deep. + * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6 + * + * TODO: should this only be invoked on class / module symbols? because there's also `isStaticMember`. + * + * Note: the result of `isStatic` changes over time. + * - Lambdalift local definitions to the class level, the `owner` field is modified. + * object T { def foo { object O } } + * After lambdalift, the OModule.isStatic is true. + * + * - After flatten, nested classes are moved to the package level. Invoking `owner` on a + * class returns a package class, for which `isStaticOwner` is true. For example, + * class C { object O } + * OModuleClass.isStatic is true after flatten. Using phase travel to get before flatten, + * method `owner` returns the class C. + * + * Why not make a stable version of `isStatic`? Maybe some parts of the compiler depend on the + * current implementation. For example + * trait T { def foo = 1 } + * The method `foo` in the implementation class T$impl will be `isStatic`, because trait + * impl classes get the `lateMODULE` flag (T$impl.isStaticOwner is true). */ def isStatic = (this hasFlag STATIC) || owner.isStaticOwner @@ -1106,7 +1147,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * - After lambdalift, all local method and class definitions (those not owned by a class * or package class) change their owner to the enclosing class. This is done through * a destructive "sym.owner = sym.owner.enclClass". The old owner is saved by - * saveOriginalOwner for the backend (needed to generate the EnclosingMethod attribute). + * saveOriginalOwner. * - After flatten, all classes are owned by a PackageClass. This is done through a * phase check (if after flatten) in the (overridden) method "def owner" in * ModuleSymbol / ClassSymbol. The `rawowner` field is not modified. @@ -1129,6 +1170,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def safeOwner: Symbol = if (this eq NoSymbol) NoSymbol else owner final def assertOwner: Symbol = if (this eq NoSymbol) abort("no-symbol does not have an owner") else owner + /** + * The initial owner of this symbol. + */ + def originalOwner: Symbol = originalOwnerMap.getOrElse(this, rawowner) + // TODO - don't allow the owner to be changed without checking invariants, at least // when under some flag. Define per-phase invariants for owner/owned relationships, // e.g. after flatten all classes are owned by package classes, there are lots and @@ -1142,7 +1188,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => } def ownerChain: List[Symbol] = this :: owner.ownerChain - def originalOwnerChain: List[Symbol] = this :: originalOwner.getOrElse(this, rawowner).originalOwnerChain // Non-classes skip self and return rest of owner chain; overridden in ClassSymbol. def enclClassChain: List[Symbol] = owner.enclClassChain diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 7ba68b8733..50ea8d9868 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -65,10 +65,15 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => class LazyPackageType extends LazyType with FlagAgnosticCompleter { override def complete(sym: Symbol) { assert(sym.isPackageClass) - sym setInfo new ClassInfoType(List(), new PackageScope(sym), sym) + // Time travel to a phase before refchecks avoids an initialization issue. `openPackageModule` + // creates a module symbol and invokes invokes `companionModule` while the `infos` field is + // still null. This calls `isModuleNotMethod`, which forces the `info` if run after refchecks. + slowButSafeEnteringPhaseNotLaterThan(picklerPhase) { + sym setInfo new ClassInfoType(List(), new PackageScope(sym), sym) // override def safeToString = pkgClass.toString - openPackageModule(sym) - markAllCompleted(sym) + openPackageModule(sym) + markAllCompleted(sym) + } } } -- cgit v1.2.3 From ee706b873a288deaba55df0b55768e86af0b0167 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 10 Jun 2014 11:37:26 +0200 Subject: Support writing classfile of version 52 --- src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala | 1 + src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala | 1 + src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index 9b7c975960..2343d378db 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -34,6 +34,7 @@ abstract class BCodeIdiomatic extends SubComponent { case "jvm-1.5" => asm.Opcodes.V1_5 case "jvm-1.6" => asm.Opcodes.V1_6 case "jvm-1.7" => asm.Opcodes.V1_7 + case "jvm-1.8" => asm.Opcodes.V1_8 } val majorVersion: Int = (classfileVersion & 0xFF) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index ce7e7c49ff..d04b111d58 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -381,6 +381,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { case "jvm-1.5" => asm.Opcodes.V1_5 case "jvm-1.6" => asm.Opcodes.V1_6 case "jvm-1.7" => asm.Opcodes.V1_7 + case "jvm-1.8" => asm.Opcodes.V1_8 } private val majorVersion: Int = (classfileVersion & 0xFF) diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index 37dfafb01c..d42c0dd730 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -39,7 +39,7 @@ trait StandardScalaSettings { val optimise: BooleanSetting // depends on post hook which mutates other settings val print = BooleanSetting ("-print", "Print program with Scala-specific features removed.") val target = ChoiceSetting ("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.", - List("jvm-1.5", "jvm-1.6", "jvm-1.7"), "jvm-1.6") + List("jvm-1.5", "jvm-1.6", "jvm-1.7", "jvm-1.8"), "jvm-1.6") val unchecked = BooleanSetting ("-unchecked", "Enable additional warnings where generated code depends on assumptions.") val uniqid = BooleanSetting ("-uniqid", "Uniquely tag all identifiers in debugging output.") val usejavacp = BooleanSetting ("-usejavacp", "Utilize the java.class.path in classpath resolution.") -- cgit v1.2.3 From a8c88b194eefd5d4d55361b934faa0ebd954ef08 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Tue, 10 Jun 2014 11:38:24 +0200 Subject: Documentation for isModuleClass --- src/reflect/scala/reflect/api/Symbols.scala | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index dddd3c0e61..42cf600c85 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -260,6 +260,9 @@ trait Symbols { self: Universe => * with an object definition (module class in scala compiler parlance). * If yes, `isType` is also guaranteed to be true. * + * Note to compiler developers: During the "mixin" phase, trait implementation class symbols + * receive the `lateMODULE` flag, hence `isImplClass && isModuleClass` becomes true. + * * @group Tests */ def isModuleClass: Boolean = false -- cgit v1.2.3