diff options
author | Martin Odersky <odersky@gmail.com> | 2005-02-04 13:04:57 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2005-02-04 13:04:57 +0000 |
commit | 4ab12055ef6a548afdf1b9d583c291b74d3630bc (patch) | |
tree | f678ed874115f2654a7ec0398ddf027c0803a707 /sources/scala/tools/nsc/symtab/classfile/ClassfileParser.scala | |
parent | 204dbd6dacd0bd3c16c3266b98673e4a630932ce (diff) | |
download | scala-4ab12055ef6a548afdf1b9d583c291b74d3630bc.tar.gz scala-4ab12055ef6a548afdf1b9d583c291b74d3630bc.tar.bz2 scala-4ab12055ef6a548afdf1b9d583c291b74d3630bc.zip |
*** empty log message ***
Diffstat (limited to 'sources/scala/tools/nsc/symtab/classfile/ClassfileParser.scala')
-rwxr-xr-x | sources/scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/sources/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/sources/scala/tools/nsc/symtab/classfile/ClassfileParser.scala new file mode 100755 index 0000000000..ee16209bdb --- /dev/null +++ b/sources/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -0,0 +1,388 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scala.tools.nsc.symtab.classfile; + +import scala.tools.util._; +import java.io.IOException; + +abstract class ClassfileParser { + + val global: Global; + import global._; + + import ClassfileConstants._; + import Flags._; + + private var in: AbstractFileReader = _; // the class file + private var clazz: Symbol = _; // the class symbol containing dynamic members + private var statics: Symbol = _; // the module class symbol containing static members + private var classMembers: Scope = _; // the scope of all dynamic members + private var staticMembers: Scope = _; // the scope of all static members + private var pool: ConstantPool = _; // the classfile's constant pool + private var isScala: boolean = _; // does class file describe a scala class? + private var hasMeta: boolean = _; // does class file contain jaco meta attribute?s + private var busy: boolean = false; // lock to detect recursive reads + + private object metaParser extends MetaParser { + val global: ClassfileParser.this.global.type = ClassfileParser.this.global + } + + private object unpickle extends UnPickle { + val global: ClassfileParser.this.global.type = ClassfileParser.this.global + } + + def parse(file: AbstractFile, root: Symbol): unit = { + assert(!busy); + busy = true; + this.in = new AbstractFileReader(file); + if (root.isModule) { + this.clazz = root.linkedClass; + this.statics = root.moduleClass + } else { + this.clazz = root; + this.statics = root.linkedModule.moduleClass; + } + this.isScala = false; + this.hasMeta = false; + try { + parseHeader; + this.pool = new ConstantPool; + parseClass() + } catch { + case e: RuntimeException => + if (settings.debug.value) e.printStackTrace(); + throw new IOException("class file '" + in.path + "' is broken") + } + busy = false + } + + private def parseHeader: unit = { + val magic = in.nextInt(); + if (magic != JAVA_MAGIC) + throw new IOException("class file '" + in.path + "' " + + "has wrong magic number 0x" + Integer.toHexString(magic) + + ", should be 0x" + Integer.toHexString(JAVA_MAGIC)); + val minorVersion = in.nextChar(); + val majorVersion = in.nextChar(); + if ((majorVersion < JAVA_MAJOR_VERSION) || + ((majorVersion == JAVA_MAJOR_VERSION) && + (minorVersion < JAVA_MINOR_VERSION))) + throw new IOException("class file '" + in.path + "' " + + "has unknown version " + + majorVersion + "." + minorVersion + + ", should be at least " + + JAVA_MAJOR_VERSION + "." + JAVA_MINOR_VERSION); + + } + + class ConstantPool { + private val len = in.nextChar(); + private val starts = new Array[int](len); + private val values = new Array[Object](len); + private val internalized = new Array[Name](len); + { var i = 1; + while (i < starts.length) { + starts(i) = in.bp; + i = i + 1; + in.nextByte() match { + case CONSTANT_UTF8 | CONSTANT_UNICODE => + in.skip(in.nextChar()); + case CONSTANT_CLASS | CONSTANT_STRING => + in.skip(2); + case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT => + in.skip(4); + case CONSTANT_LONG | CONSTANT_DOUBLE => + in.skip(8); + i = i + 1 + case _ => + errorBadTag(in.bp - 1); + } + } + } + + def getName(index: int): Name = { + if (index <= 0 || len <= index) errorBadIndex(index); + var name = values(index).asInstanceOf[Name]; + if (name == null) { + val start = starts(index); + if (in.buf(start) != CONSTANT_UTF8) errorBadTag(start); + val len = in.getChar(start + 1); + val cs = new Array[char](len); + val defined = UTF8Codec.decode(in.buf, start + 3, cs, 0, len); + name = newTermName(cs, 0, defined); + values(index) = name; + } + name + } + + def getExternalName(index: int): Name = { + if (index <= 0 || len <= index) errorBadIndex(index); + if (internalized(index) == null) { + internalized(index) = getName(index).replace('/', '.') + } + internalized(index) + } + + def getClassSymbol(index: int): Symbol = { + if (index <= 0 || len <= index) errorBadIndex(index); + var c = values(index).asInstanceOf[Symbol]; + if (c == null) { + val start = starts(index); + if (in.buf(start) != CONSTANT_CLASS) errorBadTag(start); + val name = getExternalName(in.getChar(start + 1)); + c = definitions.getClass(name); + values(index) = c; + } + c + } + + def getType(index: int): Type = + sigToType(getExternalName(index)); + + def getSuperClass(index: int): Symbol = + if (index == 0) definitions.AnyClass else getClassSymbol(index); + + def getLiteral(index: int): Any = { + if (index <= 0 || len <= index) errorBadIndex(index); + var value = values(index); + if (value == null) { + val start = starts(index); + value = in.buf(start) match { + case CONSTANT_STRING => + getName(in.getChar(start + 1)).toString() + case CONSTANT_INTEGER => + in.getInt(start + 1) + case CONSTANT_FLOAT => + in.getFloat(start + 1) + case CONSTANT_LONG => + in.getLong(start + 1) + case CONSTANT_DOUBLE => + in.getDouble(start + 1) + case _ => + errorBadTag(start); + } + values(index) = value; + } + value + } + + /** Throws an exception signaling a bad constant index. */ + private def errorBadIndex(index: int) = + throw new RuntimeException("bad constant pool index: " + index); + + /** Throws an exception signaling a bad tag at given address. */ + private def errorBadTag(start: int) = + throw new RuntimeException("bad constant pool tag " + in.buf(start) + " at byte " + start); + } + + private def sigToType(name: Name): Type = { + var index = 0; + val end = name.length; + def paramsigs2types: List[Type] = + if (name(index) == ')') { index = index + 1; List() } + else sig2type :: paramsigs2types; + def sig2type: Type = { + val tag = name(index); index = index + 1; + tag match { + case 'B' => definitions.ByteClass.tpe + case 'C' => definitions.CharClass.tpe + case 'D' => definitions.DoubleClass.tpe + case 'F' => definitions.FloatClass.tpe + case 'I' => definitions.IntClass.tpe + case 'J' => definitions.LongClass.tpe + case 'S' => definitions.ShortClass.tpe + case 'V' => definitions.UnitClass.tpe + case 'Z' => definitions.BooleanClass.tpe + case 'L' => + val start = index; + while (name(index) != ';') { index = index + 1 } + val end = index; + index = index + 1; + definitions.getClass(name.subName(start, end)).tpe + case '[' => + while ('0' <= name(index) && name(index) <= '9') index = index + 1; + appliedType(definitions.ArrayClass.tpe, List(sig2type)) + case '(' => + MethodType(paramsigs2types, sig2type) + } + } + sig2type + } + + def parseClass(): unit = { + val jflags = in.nextChar(); + var sflags = transFlags(jflags); + if ((sflags & DEFERRED) != 0) sflags = sflags & ~DEFERRED | ABSTRACT; + val c = pool.getClassSymbol(in.nextChar()); + if (c != clazz) + throw new IOException("class file '" + in.path + "' contains wrong " + clazz); + val superType = pool.getSuperClass(in.nextChar()).tpe; + val ifaceCount = in.nextChar(); + val parents = superType :: + (for (val i <- List.range(0, ifaceCount)) + yield pool.getSuperClass(in.nextChar()).tpe); + classMembers = new Scope(); + staticMembers = new Scope(); + val classInfo = ClassInfoType(parents, classMembers, clazz); + val staticInfo = ClassInfoType(List(), staticMembers, statics); + + val curbp = in.bp; + skipMembers(); // fields + skipMembers(); // methods + parseAttributes(clazz, classInfo); + if (!isScala) { + clazz.setFlag(sflags); + if (!hasMeta) { + clazz.setInfo(classInfo); + statics.setInfo(staticInfo); + } + statics.sourceModule.setInfo(statics.tpe); + in.bp = curbp; + val fieldCount = in.nextChar(); + for (val i <- Iterator.range(0, fieldCount)) parseField(); + val methodCount = in.nextChar(); + for (val i <- Iterator.range(0, methodCount)) parseMethod(); + } + } + + def parseField(): unit = { + val jflags = in.nextChar(); + var sflags = transFlags(jflags); + if ((sflags & FINAL) != 0) sflags = sflags | MUTABLE; + if ((sflags & PRIVATE) != 0) { + in.skip(4); skipAttributes(); + } else { + val name = pool.getName(in.nextChar()); + val info = pool.getType(in.nextChar()); + val sym = getOwner(jflags) + .newValue(Position.NOPOS, name).setFlag(sflags).setInfo(info); + parseAttributes(sym, info); + getScope(jflags).enter(sym); + } + } + + def parseMethod(): unit = { + val jflags = in.nextChar(); + var sflags = transFlags(jflags); + if ((sflags & JAVA_ACC_BRIDGE) != 0) sflags = sflags | PRIVATE; + if ((sflags & PRIVATE) != 0) { + in.skip(4); skipAttributes(); + } else { + val name = pool.getName(in.nextChar()); + val info = pool.getType(in.nextChar()); + val sym = getOwner(jflags) + .newMethod(Position.NOPOS, name).setFlag(sflags).setInfo(info); + parseAttributes(sym, info); + getScope(jflags).enter(sym); + } + } + + def parseAttributes(sym: Symbol, symtype: Type): unit = { + def parseAttribute(): unit = { + val attrName = pool.getName(in.nextChar()); + val attrLen = in.nextInt(); + attrName match { + case nme.SyntheticATTR => + sym.setFlag(SYNTHETIC); + in.skip(attrLen) + case nme.BridgeATTR => + sym.setFlag(BRIDGE); + in.skip(attrLen) + case nme.DeprecatedATTR => + sym.setFlag(DEPRECATED); + in.skip(attrLen) + case nme.ConstantValueATTR => + def cast(value: Any, tp: Type): Any = { + val s = symtype.symbol; + if (s == definitions.ByteClass) value.asInstanceOf[byte] + else if (s == definitions.ShortClass) value.asInstanceOf[short] + else if (s == definitions.CharClass) value.asInstanceOf[char] + else if (s == definitions.IntClass) value.asInstanceOf[int] + else if (s == definitions.LongClass) value.asInstanceOf[long] + else if (s == definitions.FloatClass) value.asInstanceOf[float] + else if (s == definitions.DoubleClass) value.asInstanceOf[double] + else if (s == definitions.UnitClass) () + else if (s == definitions.BooleanClass) value.asInstanceOf[int] != 0 + else if (s == definitions.StringClass) value.asInstanceOf[String] + else value + } + val value = pool.getLiteral(in.nextChar()); + sym.setInfo(ConstantType(symtype, cast(value, symtype))) + case nme.InnerClassesATTR => + parseInnerClasses() + case nme.ScalaSignatureATTR => + //unpickle.parse(in.nextBytes(attrLen), clazz, statics.sourceModule); + //this.isScala = true; + case nme.JacoMetaATTR => + val meta = pool.getName(in.nextChar()).toString().trim(); + metaParser.parse(meta, sym, symtype); + this.hasMeta = true; + case _ => + in.skip(attrLen) + } + } + def parseInnerClasses(): unit = { + for (val i <- Iterator.range(0, 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 && + (jflags & (JAVA_ACC_PUBLIC | JAVA_ACC_PROTECTED)) != 0 && + pool.getClassSymbol(outerIndex) == sym) { + val innerAlias = getOwner(jflags) + .newAliasType(Position.NOPOS, pool.getName(nameIndex).toTypeName) + .setInfo(pool.getClassSymbol(innerIndex).tpe); + getScope(jflags).enter(innerAlias); + } + } + } + val attrCount = in.nextChar(); + for (val i <- Iterator.range(0, attrCount)) parseAttribute() + } + + def skipAttributes(): unit = { + val attrCount = in.nextChar(); + for (val i <- Iterator.range(0, attrCount)) { + in.skip(2); in.skip(in.nextInt()) + } + } + + def skipMembers(): unit = { + val memberCount = in.nextChar(); + for (val i <- Iterator.range(0, memberCount)) { + in.skip(6); skipAttributes() + } + } + + private def getOwner(flags: int): Symbol = + if ((flags & JAVA_ACC_STATIC) != 0) statics else clazz; + + private def getScope(flags: int): Scope = + if ((flags & JAVA_ACC_STATIC) != 0) staticMembers else classMembers; + + private def transFlags(flags: int): int = { + var res = 0; + if ((flags & JAVA_ACC_PRIVATE) != 0) + res = res | PRIVATE + else if ((flags & JAVA_ACC_PROTECTED) != 0) + res = res | PROTECTED + else if ((flags & JAVA_ACC_PUBLIC) == 0) + res = res | PRIVATE; + if ((flags & JAVA_ACC_ABSTRACT) != 0) + res = res | DEFERRED; + if ((flags & JAVA_ACC_FINAL) != 0) + res = res | FINAL; + if ((flags & JAVA_ACC_INTERFACE) != 0) + res = res | TRAIT | ABSTRACT; + if ((flags & JAVA_ACC_SYNTHETIC) != 0) + res = res | SYNTHETIC; + res | JAVA; + } +} |