From d205bf404fb361de6c8829d8f13d8adcc809e87d Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Sat, 13 Nov 2010 13:31:49 +0000 Subject: Fix InnerClasses attribute: anonymous classes d... Fix InnerClasses attribute: anonymous classes don't have an outer name. EnclosingMethod is correctly generated. Fixed isAnonymousClass definition. Updated test that depends on anonymous inner class names. Closes (again) #3249, references #2749. review by odersky,extempore. --- .../scala/tools/nsc/backend/jvm/GenJVM.scala | 42 ++++++++++++++++------ .../scala/tools/nsc/symtab/SymbolLoaders.scala | 2 +- src/compiler/scala/tools/nsc/symtab/Symbols.scala | 22 +++++++++--- .../ch/epfl/lamp/fjbg/JInnerClassesAttribute.java | 9 ++++- 4 files changed, 57 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 0c7455c6c8..384dfacbc8 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -200,7 +200,7 @@ abstract class GenJVM extends SubComponent { def genClass(c: IClass) { val needsEnclosingMethod: Boolean = - c.symbol.isClass && c.symbol.asInstanceOf[ClassSymbol].originalOwner.isMethod + c.symbol.isClass && (c.symbol.originalEnclosingMethod != NoSymbol) clasz = c innerClasses = immutable.ListSet.empty @@ -293,11 +293,13 @@ abstract class GenJVM extends SubComponent { } def addEnclosingMethodAttribute(jclass: JClass, clazz: Symbol) { - val sym = clazz.asInstanceOf[ClassSymbol].originalOwner + val sym = clazz.originalEnclosingMethod if (sym.isMethod) { - jclass.addAttribute(fjbgContext.JEnclosingMethodAttribute(jclass, javaName(sym.enclClass), javaName(sym), javaType(sym))) - } else - log("Local class %s has non-method owner: %s".format(clazz, sym)) + log("enclosing method for %s is %s".format(clazz, sym)) + var outerName = javaName(sym.enclClass) + if (outerName.endsWith("$")) outerName = outerName.dropRight(1) + jclass.addAttribute(fjbgContext.JEnclosingMethodAttribute(jclass, outerName, javaName(sym), javaType(sym))) + } } /** @@ -580,6 +582,28 @@ abstract class GenJVM extends SubComponent { def addOwnInnerClasses(cls: Symbol): Unit = innerClasses ++= (cls.info.decls filter (_.isClass)) + /** 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. + */ + def outerName(innerSym: Symbol): String = { + if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction || innerSym.originalEnclosingMethod != NoSymbol) + null + else { + var outerName = javaName(innerSym.rawowner) + // remove the trailing '$' + if (outerName.endsWith("$") && isTopLevelModule(innerSym.rawowner)) + outerName = outerName dropRight 1 + outerName + } + } + + def innerName(innerSym: Symbol): String = + if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction) + null + else + innerSym.rawname.toString + // add inner classes which might not have been referenced yet atPhase(currentRun.erasurePhase.next) { addOwnInnerClasses(clasz.symbol) @@ -591,17 +615,13 @@ abstract class GenJVM extends SubComponent { // sort them so inner classes succeed their enclosing class // to satisfy the Eclipse Java compiler for (innerSym <- innerClasses.toList sortBy (_.name.length)) { - var outerName = javaName(innerSym.rawowner) - // remove the trailing '$' - if (outerName.endsWith("$") && isTopLevelModule(innerSym.rawowner)) - outerName = outerName dropRight 1 var flags = javaFlags(innerSym) if (innerSym.rawowner.hasModuleFlag) flags |= ACC_STATIC innerClassesAttr.addEntry(javaName(innerSym), - outerName, - innerSym.rawname.toString, + outerName(innerSym), + innerName(innerSym), (flags & INNER_CLASSES_FLAGS)); } } diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index fb1e705a56..695e95b72d 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -118,7 +118,7 @@ abstract class SymbolLoaders { module.moduleClass setInfo moduleClassLoader owner.info.decls enter clazz owner.info.decls enter module - assert(clazz.companionModule == module, module) + assert(clazz.companionModule == module || clazz.isAnonymousClass, module) assert(module.companionClass == clazz, clazz) } diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index d94b617904..8aa9c716c9 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -41,6 +41,11 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => abstract class Symbol(initOwner: Symbol, initPos: Position, initName: Name) extends AbsSymbol { var rawowner = initOwner + + /** The original owner of this class. Used by the backend to generate + * EnclosingMethod attributes. */ + var originalOwner: Symbol = initOwner + var rawname = initName var rawflags: Long = 0 private var rawpos = initPos @@ -369,7 +374,7 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => // class C extends D( { class E { ... } ... } ). Here, E is a class local to a constructor final def isClassLocalToConstructor = isClass && hasFlag(INCONSTRUCTOR) - final def isAnonymousClass = isClass && (originalName startsWith nme.ANON_CLASS_NAME) // todo: find out why we can't use containsName here. + final def isAnonymousClass = isClass && (name containsName nme.ANON_CLASS_NAME) // todo: find out why we can't use containsName here. final def isAnonymousFunction = hasFlag(SYNTHETIC) && (name containsName nme.ANON_FUN_NAME) final def isClassOfModule = isModuleClass || isClass && nme.isLocalName(name) @@ -1163,6 +1168,16 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => else packSym } + /** Return the original enclosing method of this symbol. It + * should return the same thing as enclMethod when called before + * lambda lift, but it preserves the original nesting when called afterwards. + */ + def originalEnclosingMethod: Symbol = { + if (isMethod) this + else + originalOwner.originalEnclosingMethod + } + /** The top-level class containing this symbol */ def toplevelClass: Symbol = if (owner.isPackageClass) { @@ -1897,10 +1912,6 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => /** The classfile from which this class was loaded. Maybe null. */ var classFile: AbstractFile = null; - /** The original owner of this class. Used by the backend to generate - * EnclosingMethod attributes. */ - var originalOwner: Symbol = initOwner - private var source: AbstractFile = null override def sourceFile = if (owner.isPackageClass) source else super.sourceFile @@ -2031,6 +2042,7 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => protected def doCookJavaRawInfo() {} override def accessBoundary(base: Symbol): Symbol = RootClass def cloneSymbolImpl(owner: Symbol): Symbol = abort() + override def originalEnclosingMethod = this } def cloneSymbols(syms: List[Symbol]): List[Symbol] = { diff --git a/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java b/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java index 81a7db7488..ac69d24947 100644 --- a/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java +++ b/src/fjbg/ch/epfl/lamp/fjbg/JInnerClassesAttribute.java @@ -24,7 +24,14 @@ public class JInnerClassesAttribute extends JAttribute { } public void addEntry(String inner, String outer, String name, int flags) { - Entry e = new Entry(inner, outer, name, flags); + int inIdx = pool.addClass(inner); + int ouIdx = 0; + if (outer != null) ouIdx = pool.addClass(outer); + int nIdx = 0; + if (name != null) nIdx = pool.addUtf8(name); + + Entry e = new Entry(inIdx, ouIdx, nIdx, flags); + if (entries.containsKey(inner)) { Entry other = (Entry) entries.get(inner); assert other.outerInfo == e.outerInfo && other.originalName == e.originalName && other.innerFlags == e.innerFlags -- cgit v1.2.3