diff options
author | Iulian Dragos <jaguarul@gmail.com> | 2008-08-17 16:03:53 +0000 |
---|---|---|
committer | Iulian Dragos <jaguarul@gmail.com> | 2008-08-17 16:03:53 +0000 |
commit | 2ea691629734a13c4fc0478c51047865228d15ea (patch) | |
tree | a447963300350a05db52b268400367671d4c4027 | |
parent | 2de0e86f9b5595be34fd3f71298d09b5a4a3f212 (diff) | |
download | scala-2ea691629734a13c4fc0478c51047865228d15ea.tar.gz scala-2ea691629734a13c4fc0478c51047865228d15ea.tar.bz2 scala-2ea691629734a13c4fc0478c51047865228d15ea.zip |
Java inner classes are parsed correctly.
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala | 167 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 249 |
2 files changed, 274 insertions, 142 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 1d9c65b38c..3754457319 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -92,10 +92,9 @@ abstract class GenJVM extends SubComponent { var clasz: IClass = _ var method: IMethod = _ - var code: Code = _ var jclass: JClass = _ var jmethod: JMethod = _ - var jcode: JExtendedCode = _ +// var jcode: JExtendedCode = _ var innerClasses: Set[Symbol] = ListSet.empty // referenced inner classes @@ -246,7 +245,7 @@ abstract class GenJVM extends SubComponent { !m.symbol.isSetter) yield javaName(m.symbol) val constructor = beanInfoClass.addNewMethod(JAccessFlags.ACC_PUBLIC, "<init>", JType.VOID, javaTypes(Nil), javaNames(Nil)) - jcode = constructor.getCode().asInstanceOf[JExtendedCode] + val jcode = constructor.getCode().asInstanceOf[JExtendedCode] val strKind = new JObjectType(javaName(definitions.StringClass)) val stringArrayKind = new JArrayType(strKind) val conType = new JMethodType(JType.VOID, Array(javaType(definitions.ClassClass), stringArrayKind, stringArrayKind)) @@ -548,7 +547,7 @@ abstract class GenJVM extends SubComponent { addRemoteException(jmethod, m.symbol) if (!jmethod.isAbstract() && !method.native) { - jcode = jmethod.getCode().asInstanceOf[JExtendedCode] + val jcode = jmethod.getCode().asInstanceOf[JExtendedCode] // add a fake local for debugging purpuses if (emitVars && isClosureApply(method.symbol)) { @@ -576,7 +575,7 @@ abstract class GenJVM extends SubComponent { genCode(m) if (emitVars) - genLocalVariableTable(m); + genLocalVariableTable(m, jcode); } addGenericSignature(jmethod, m.symbol) @@ -740,19 +739,22 @@ abstract class GenJVM extends SubComponent { * @param m ... */ def genCode(m: IMethod) { - labels.clear + val jcode = jmethod.getCode.asInstanceOf[JExtendedCode] + + def makeLabels(bs: List[BasicBlock]) = { + if (settings.debug.value) + log("Making labels for: " + method); + HashMap.empty ++ bs.zip(bs map (b => jcode.newLabel)) + } + isModuleInitialized = false - code = m.code linearization = linearizer.linearize(m) - makeLabels(linearization) - genBlocks(linearization) - - if (this.method.exh != Nil) - genExceptionHandlers; - } + val labels = makeLabels(linearization) + /** local variables whose scope appears in this block. */ + var varsInBlock: collection.mutable.Set[Local] = new HashSet - var nextBlock: BasicBlock = _ + var nextBlock: BasicBlock = linearization.head def genBlocks(l: List[BasicBlock]): Unit = l match { case Nil => () @@ -823,9 +825,6 @@ abstract class GenJVM extends SubComponent { } } - /** local variables whose scope appears in this block. */ - var varsInBlock: collection.mutable.Set[Local] = new HashSet - def genBlock(b: BasicBlock) { labels(b).anchorToNext() @@ -1031,14 +1030,19 @@ abstract class GenJVM extends SubComponent { i = i + 1 caze = caze.tail } - val branchArray = new Array[JCode$Label](tagArray.length) + val branchArray = jcode.newLabels(tagArray.length) + i = 0 + while (i < branchArray.length) { + branchArray(i) = labels(branches(i)) + i += 1 + } if (settings.debug.value) log("Emitting SWITHCH:\ntags: " + tags + "\nbranches: " + branches); jcode.emitSWITCH(tagArray, - { (branches map labels dropRight 1).copyToArray(branchArray, 0); - branchArray }, + branchArray, labels(branches.last), MIN_SWITCH_DENSITY); + () case JUMP(whereto) => if (nextBlock != whereto) @@ -1203,6 +1207,7 @@ abstract class GenJVM extends SubComponent { } } + /** * @param primitive ... * @param pos ... @@ -1376,9 +1381,70 @@ abstract class GenJVM extends SubComponent { } } + // genCode starts here + genBlocks(linearization) + + if (this.method.exh != Nil) + genExceptionHandlers; + } + + + /** Emit a Local variable table for debugging purposes. + * Synthetic locals are skipped. All variables are method-scoped. + */ + private def genLocalVariableTable(m: IMethod, jcode: JCode) { + var vars = m.locals.filter(l => !l.sym.hasFlag(Flags.SYNTHETIC)) + + if (vars.length == 0) return + + val pool = jclass.getConstantPool() + val pc = jcode.getPC() + var anonCounter = 0 + var entries = 0 + vars.foreach { lv => + lv.ranges = mergeEntries(lv.ranges.reverse); + entries += lv.ranges.length + } + if (!jmethod.isStatic()) entries += 1 + + val lvTab = ByteBuffer.allocate(2 + 10 * entries) + def emitEntry(name: String, signature: String, idx: Short, start: Short, end: Short) { + lvTab.putShort(start) + lvTab.putShort(end) + lvTab.putShort(pool.addUtf8(name).asInstanceOf[Short]) + lvTab.putShort(pool.addUtf8(signature).asInstanceOf[Short]) + lvTab.putShort(idx) + } + + lvTab.putShort(entries.asInstanceOf[Short]) + + if (!jmethod.isStatic()) { + emitEntry("this", jclass.getType().getSignature(), 0, 0.asInstanceOf[Short], pc.asInstanceOf[Short]) + } + + for (lv <- vars) { + val name = if (javaName(lv.sym) eq null) { + anonCounter += 1 + "<anon" + anonCounter + ">" + } else javaName(lv.sym) + + val index = indexOf(lv).asInstanceOf[Short] + val tpe = javaType(lv.kind).getSignature() + for ((start, end) <- lv.ranges) { + emitEntry(name, tpe, index, start.asInstanceOf[Short], (end - start).asInstanceOf[Short]) + } + } + val attr = + fjbgContext.JOtherAttribute(jclass, + jmethod, + "LocalVariableTable", + lvTab.array()) + jcode.addAttribute(attr) + } + + /** For each basic block, the first PC address following it. */ val endPC: HashMap[BasicBlock, Int] = new HashMap() - val labels: HashMap[BasicBlock, JCode$Label] = new HashMap() val conds: HashMap[TestOp, Int] = new HashMap() conds += (EQ -> JExtendedCode.COND_EQ) @@ -1411,13 +1477,6 @@ abstract class GenJVM extends SubComponent { classLiteral += (FLOAT -> new JObjectType("java.lang.Float")) classLiteral += (DOUBLE -> new JObjectType("java.lang.Double")) - def makeLabels(bs: List[BasicBlock]) { - //labels.clear - if (settings.debug.value) - log("Making labels for: " + method); - bs foreach (bb => labels += (bb -> jcode.newLabel()) ) - } - ////////////////////// local vars /////////////////////// @@ -1591,58 +1650,6 @@ abstract class GenJVM extends SubComponent { dir.fileNamed(pathParts.last + suffix) } - /** Emit a Local variable table for debugging purposes. - * Synthetic locals are skipped. All variables are method-scoped. - */ - private def genLocalVariableTable(m: IMethod) { - var vars = m.locals.filter(l => !l.sym.hasFlag(Flags.SYNTHETIC)) - - if (vars.length == 0) return - - val pool = jclass.getConstantPool() - val pc = jcode.getPC() - var anonCounter = 0 - var entries = 0 - vars.foreach { lv => - lv.ranges = mergeEntries(lv.ranges.reverse); - entries += lv.ranges.length - } - if (!jmethod.isStatic()) entries += 1 - - val lvTab = ByteBuffer.allocate(2 + 10 * entries) - def emitEntry(name: String, signature: String, idx: Short, start: Short, end: Short) { - lvTab.putShort(start) - lvTab.putShort(end) - lvTab.putShort(pool.addUtf8(name).asInstanceOf[Short]) - lvTab.putShort(pool.addUtf8(signature).asInstanceOf[Short]) - lvTab.putShort(idx) - } - - lvTab.putShort(entries.asInstanceOf[Short]) - - if (!jmethod.isStatic()) { - emitEntry("this", jclass.getType().getSignature(), 0, 0.asInstanceOf[Short], pc.asInstanceOf[Short]) - } - - for (lv <- vars) { - val name = if (javaName(lv.sym) eq null) { - anonCounter += 1 - "<anon" + anonCounter + ">" - } else javaName(lv.sym) - - val index = indexOf(lv).asInstanceOf[Short] - val tpe = javaType(lv.kind).getSignature() - for ((start, end) <- lv.ranges) { - emitEntry(name, tpe, index, start.asInstanceOf[Short], (end - start).asInstanceOf[Short]) - } - } - val attr = - fjbgContext.JOtherAttribute(jclass, - jmethod, - "LocalVariableTable", - lvTab.array()) - jcode.addAttribute(attr) - } /** Merge adjacent ranges. */ diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 5142ebad4e..8a94f20df6 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -38,6 +38,7 @@ abstract class ClassfileParser { protected var isScala: Boolean = _ // does class file describe a scala class? protected var hasMeta: Boolean = _ // does class file contain jaco meta attribute?s protected var busy: Boolean = false // lock to detect recursive reads + private var externalName: Name = _ // JVM name of the current class protected var classTParams = Map[Name,Symbol]() private object metaParser extends MetaParser { @@ -137,6 +138,7 @@ abstract class ClassfileParser { } } + /** Return the name found at given index. The returned name is a term name. */ def getName(index: Int): Name = { if (index <= 0 || len <= index) errorBadIndex(index) var name = values(index).asInstanceOf[Name] @@ -149,6 +151,7 @@ abstract class ClassfileParser { name } + /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */ def getExternalName(index: Int): Name = { if (index <= 0 || len <= index) errorBadIndex(index) if (internalized(index) eq null) { @@ -173,6 +176,15 @@ abstract class ClassfileParser { c } + /** Return the external name of the class info structure found at 'index'. + * Use 'getClassSymbol' if the class is sure to be a top-level class. + */ + def getClassName(index: Int): Name = { + val start = starts(index) + if (in.buf(start) != CONSTANT_CLASS) errorBadTag(start) + getExternalName(in.getChar(start + 1)) + } + /** Return the symbol of the class member at <code>index</code>. * The following special cases exist: * - If the member refers to special MODULE$ static field, return @@ -314,11 +326,24 @@ abstract class ClassfileParser { /** Return the class symbol of the given name. */ - def classNameToSymbol(name: Name): Symbol = - if (name.pos('.') == name.length) - definitions.getMember(definitions.EmptyPackageClass, name.toTypeName) - else - definitions.getClass(name) + def classNameToSymbol(name: Name): Symbol = { + def lookupClass(name: Name) = + if (name.pos('.') == name.length) + definitions.getMember(definitions.EmptyPackageClass, name.toTypeName) + else + definitions.getClass(name) + + innerClasses.get(name) match { + case Some(entry) => + //println("found inner class " + name) + val res = innerClasses.classSymbol(entry.externalName) + //println("\trouted to: " + res) + res + case None => + //if (name.toString.contains("$")) println("No inner class: " + name + innerClasses + " while parsing " + in.file.name) + lookupClass(name) + } + } var sawPrivateConstructor = false @@ -327,14 +352,18 @@ abstract class ClassfileParser { val isAnnotation = (jflags & JAVA_ACC_ANNOTATION) != 0 var sflags = transFlags(jflags) if ((sflags & DEFERRED) != 0) sflags = sflags & ~DEFERRED | ABSTRACT - val c = pool.getClassSymbol(in.nextChar) - if (c != clazz) { + var nameIdx = in.nextChar + externalName = pool.getClassName(nameIdx) + val c = pool.getClassSymbol(nameIdx) + if (c != clazz && !c.nameString.contains("$")) { if ((clazz eq NoSymbol) && (c ne NoSymbol)) { // XXX: needed for build compiler, so can't protect with inIDE clazz = c } else if (inIDE) { Console.println("WRONG CLASS: expected: " + clazz + " found " + c) } else throw new IOException("class file '" + in.file + "' contains wrong " + c) } + + addEnclosingTParams(clazz) val superType = if (isAnnotation) { in.nextChar; definitions.AnnotationClass.tpe } else pool.getSuperClass(in.nextChar).tpe val ifaceCount = in.nextChar @@ -393,6 +422,19 @@ abstract class ClassfileParser { } } + /** Add type parameters of enclosing classes */ + def addEnclosingTParams(clazz: Symbol) { + var sym = clazz.owner + while (sym.isClass && !sym.isModuleClass) { +// println("adding tparams of " + sym) + for (t <- sym.tpe.typeArgs) { +// println("\tadding " + (t.typeSymbol.name + "->" + t.typeSymbol)) + classTParams = classTParams + (t.typeSymbol.name -> t.typeSymbol) + } + sym = sym.owner + } + } + def parseField() { val jflags = in.nextChar var sflags = transFlags(jflags) @@ -426,7 +468,7 @@ abstract class ClassfileParser { in.skip(4); skipAttributes() } else { val name = pool.getName(in.nextChar) - var info = pool.getType(in.nextChar) + var info = pool.getType(in.nextChar) if (name == nme.CONSTRUCTOR) info match { case MethodType(formals, restpe) => @@ -484,57 +526,66 @@ abstract class ClassfileParser { case VOID_TAG => definitions.UnitClass.tpe case BOOL_TAG => definitions.BooleanClass.tpe case 'L' => - def processClassType(tp: Type): Type = { - val classSym = tp.typeSymbol - val existentials = new ListBuffer[Symbol]() - if (sig(index) == '<') { - accept('<') - val xs = new ListBuffer[Type]() - var i = 0 - while (sig(index) != '>') { - sig(index) match { - case variance @ ('+' | '-' | '*') => - index += 1 - val bounds = variance match { - case '+' => mkTypeBounds(definitions.NothingClass.tpe, - sig2type(tparams)) - case '-' => mkTypeBounds(sig2type(tparams), - definitions.AnyClass.tpe) - case '*' => mkTypeBounds(definitions.NothingClass.tpe, - definitions.AnyClass.tpe) - } - val newtparam = makeExistential("?"+i, sym, bounds) - existentials += newtparam - xs += newtparam.tpe - i += 1 - case _ => - xs += sig2type(tparams) + def processInner(tp: Type): Type = tp match { + case TypeRef(pre, sym, args) if (!sym.isStatic) => + TypeRef(processInner(pre.widen), sym, args) + case _ => + tp + } + def processClassType(tp: Type): Type = tp match { + case TypeRef(pre, classSym, args) => + val existentials = new ListBuffer[Symbol]() + if (sig(index) == '<') { + accept('<') + val xs = new ListBuffer[Type]() + var i = 0 + while (sig(index) != '>') { + sig(index) match { + case variance @ ('+' | '-' | '*') => + index += 1 + val bounds = variance match { + case '+' => mkTypeBounds(definitions.NothingClass.tpe, + sig2type(tparams)) + case '-' => mkTypeBounds(sig2type(tparams), + definitions.AnyClass.tpe) + case '*' => mkTypeBounds(definitions.NothingClass.tpe, + definitions.AnyClass.tpe) + } + val newtparam = makeExistential("?"+i, sym, bounds) + existentials += newtparam + xs += newtparam.tpe + i += 1 + case _ => + xs += sig2type(tparams) + } } + accept('>') + assert(xs.length > 0) + existentialAbstraction(existentials.toList, TypeRef(pre, classSym, xs.toList)) + } else if (classSym.isMonomorphicType) { + tp + } else { + // raw type - existentially quantify all type parameters + val eparams = typeParamsToExistentials(classSym, classSym.unsafeTypeParams) + val t = TypeRef(pre, classSym, eparams.map(_.tpe)) + val res = existentialAbstraction(eparams, t) + if (settings.debug.value && settings.verbose.value) println("raw type " + classSym + " -> " + res) + res } - accept('>') - assert(xs.length > 0) - existentialAbstraction(existentials.toList, - appliedType(tp, xs.toList)) - } else if (classSym.isMonomorphicType) { + case tp => + assert(sig(index) != '<') tp - } else { - // raw type - existentially quantify all type parameters - val eparams = typeParamsToExistentials(classSym, classSym.unsafeTypeParams) - val t = appliedType(classSym.typeConstructor, eparams.map(_.tpe)) - val res = existentialAbstraction(eparams, t) - if (settings.debug.value && settings.verbose.value) println("raw type " + classSym + " -> " + res) - res - } } + val classSym = classNameToSymbol(subName(c => c == ';' || c == '<')) assert(!classSym.hasFlag(OVERLOADED), classSym.alternatives) - var tpe = processClassType(classSym.tpe) + var tpe = processClassType(processInner(classSym.tpe)) while (sig(index) == '.') { accept('.') val name = subName(c => c == ';' || c == '<' || c == '.').toTypeName val clazz = tpe.member(name) - assert(clazz.isAliasType, tpe) - tpe = processClassType(clazz.tpe) + //assert(clazz.isAliasType, tpe) + tpe = processClassType(processInner(clazz.tpe)) } accept(';') tpe @@ -761,32 +812,96 @@ abstract class ClassfileParser { } def parseInnerClasses() { + def enterClassAndModule(name: Name, completer: global.loaders.SymbolLoader, jflags: Int): Symbol = { + val innerClass = getOwner(jflags).newClass(NoPosition, name.toTypeName).setInfo(completer) + val innerModule = getOwner(jflags).newModule(NoPosition, name).setInfo(completer) + innerClass.moduleClass.setInfo(global.loaders.moduleClassLoader) + + getScope(jflags).enter(innerClass) + getScope(jflags).enter(innerModule) + } + for (i <- 0 until in.nextChar) { val innerIndex = in.nextChar val outerIndex = in.nextChar val nameIndex = in.nextChar val jflags = in.nextChar - if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0 && - pool.getClassSymbol(outerIndex) == sym) { - makeInnerAlias( - getOwner(jflags), - pool.getName(nameIndex).toTypeName, - pool.getClassSymbol(innerIndex), - getScope(jflags)) - - if ((jflags & JAVA_ACC_STATIC) != 0) { - val innerVal = staticModule.newValue(NoPosition, pool.getName(nameIndex).toTermName) - .setInfo(pool.getClassSymbol(innerIndex).linkedModuleOfClass.moduleClass.thisType) - .setFlag(JAVA | MODULE) - staticDefs.enter(innerVal) + if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) { + val entry = InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) + innerClasses += (pool.getClassName(innerIndex) -> entry) + + // create a new class member for immediate inner classes + if (entry.outerName == externalName) { + val file = global.classPath.lookupPath(entry.externalName.replace('.', '/').toString, false) + enterClassAndModule(entry.originalName, new global.loaders.ClassfileLoader(file, null, null), jflags) } } } } + + // begin parseAttributes val attrCount = in.nextChar for (i <- 0 until attrCount) parseAttribute() } + /** An entry in the InnerClasses attribute of this class file. */ + case class InnerClassEntry(external: Int, outer: Int, name: Int, jflags: Int) { + def externalName: Name = pool.getClassName(external) + def outerName: Name = pool.getClassName(outer) + def originalName: Name = pool.getName(name) + + override def toString = + originalName + " in " + outerName + "(" + externalName +")" + } + + object innerClasses extends collection.mutable.HashMap[Name, InnerClassEntry] { + /** Return the Symbol of the top level class enclosing 'name', or 'name's symbol + * if no entry found for 'name'. + */ + def topLevelClass(name: Name): Symbol = { + val tlName = if (isDefinedAt(name)) { + var entry = this(name) + while (isDefinedAt(entry.outerName)) + entry = this(entry.outerName) + entry.outerName + } else + name + classNameToSymbol(tlName) + } + + /** Return the class symbol for 'externalName'. It looks it up in its outer class. + * Forces all outer class symbols to be completed. + * + * If the given name is not an inner class, it returns the symbol found in 'definitions'. + */ + def classSymbol(externalName: Name): Symbol = { + /** Return the symbol of `innerName', having the given `externalName'. */ + def innerSymbol(externalName: Name, innerName: Name, static: Boolean): Symbol = + innerClasses.get(externalName) match { + case Some(entry) => + val sym = classSymbol(entry.outerName) + if (static) { + val s = sym.linkedModuleOfClass.info.member(innerName.toTypeName) + assert(s ne NoSymbol, sym + " members: " + sym.info.members) + s + } else + sym.info.member(innerName.toTypeName) + case None => + val cls = classNameToSymbol(externalName) + cls + //if (static) cls.linkedClassOfModule else cls + } + + get(externalName) match { + case Some(entry) => + val clazz = innerSymbol(entry.externalName, entry.originalName, (entry.jflags & JAVA_ACC_STATIC) != 0) + clazz + case None => + definitions.getClass(externalName) + } + } + } + class LazyAliasType(alias: Symbol) extends LazyType { override def complete(sym: Symbol) { alias.initialize @@ -795,6 +910,16 @@ abstract class ClassfileParser { } } + /** A lazy type that represents a Java inner class. */ + class LazyInnerClassType(innerEntry: InnerClassEntry) extends LazyType { + override def complete(sym: Symbol) { +// println("completing " + sym) + val clazz = innerClasses.classSymbol(innerEntry.externalName) +// println("found symbol: " + clazz) + sym.setInfo(clazz.info) + } + } + def skipAttributes() { val attrCount = in.nextChar for (i <- 0 until attrCount) { |