From de5aaf45603f83e3a15872df395533b6b054564b Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 26 Apr 2012 13:20:08 -0700 Subject: Improving organization of classfile parser. --- .../scala/reflect/internal/ClassfileModel.scala | 169 ++++++++++ .../scala/reflect/internal/ConstantPool.scala | 78 +++++ .../scala/reflect/internal/JvmClassInfo.scala | 344 +++------------------ 3 files changed, 298 insertions(+), 293 deletions(-) create mode 100644 src/compiler/scala/reflect/internal/ClassfileModel.scala create mode 100644 src/compiler/scala/reflect/internal/ConstantPool.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/reflect/internal/ClassfileModel.scala b/src/compiler/scala/reflect/internal/ClassfileModel.scala new file mode 100644 index 0000000000..c96459037f --- /dev/null +++ b/src/compiler/scala/reflect/internal/ClassfileModel.scala @@ -0,0 +1,169 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.reflect.internal + +import java.io.DataInput +import ConstantPool._ +import ClassfileConstants._ + +trait ClassfileModel { + type Result + type Entry + type InterfaceInfo + type MemberInfo + type AttributeInfo + type InnerClassInfo + + protected implicit def EntryArrayTag: ArrayTag[Entry] + protected implicit def InterfaceInfoArrayTag: ArrayTag[InterfaceInfo] + protected implicit def MemberInfoArrayTag: ArrayTag[MemberInfo] + protected implicit def AttributeInfoArrayTag: ArrayTag[AttributeInfo] + protected implicit def InnerClassInfoArrayTag: ArrayTag[InnerClassInfo] + + // These could be implemented to jump forward in the stream if the + // result is not wanted. + def readConstantPoolEntry(): Entry + def readInterface(): InterfaceInfo + def readMember(): MemberInfo + def readAttribute(): AttributeInfo + def readInnerClass(): InnerClassInfo + + def createInfo( + version: JvmVersion, + entries: Array[Entry], + flags: Int, + name: String, + superName: String, + interfaces: Array[InterfaceInfo], + fields: Array[MemberInfo], + methods: Array[MemberInfo], + attributes: Array[AttributeInfo] + ): Result +} + +abstract class StreamingClassfileModel extends ClassfileModel { + protected[this] val in: DataInput + private[this] var name: String = _ + private[this] var entries: Array[PoolEntry] = _ + + type Entry = PoolEntry + + // These translate null into "", it's less troublesome. + protected def nameAt(idx: Int) = entries(idx) match { + case x: Name_Info => stringAt(x.name_index).replace('/', '.') + case _ => "" + } + protected def stringAt(idx: Int) = entries(idx) match { + case x: Utf8_info => x.stringValue + case _ => "" + } + + protected def u4 = in.readInt + protected def u2 = in.readUnsignedShort.toChar + protected def u1 = in.readUnsignedByte + + // The constant_pool table is indexed from 1 to constant_pool_count−1. + protected def readConstantPool(): Array[Entry] = { + val count = u2 + val entries = new Array[Entry](count) + var i = 1 + while (i < count) { + val entry = readConstantPoolEntry() + entries(i) = entry + i += entry.width + } + entries + } + protected def readInterfaces() = { + val count = u2 + val interfaces = new Array[InterfaceInfo](count) + var i = 0 + while (i < count) { + interfaces(i) = readInterface() + i += 1 + } + interfaces + } + protected def readMembers() = { + val count = u2 + val arr = new Array[MemberInfo](count) + var i = 0 + while (i < count) { + arr(i) = readMember() + i += 1 + } + arr + } + protected def readAttributes(): Array[AttributeInfo] = { + val count = u2 + val arr = new Array[AttributeInfo](count) + var i = 0 + while (i < count) { + arr(i) = readAttribute() + i += 1 + } + arr + } + protected def readInnerClasses() = { + val count = u2 + val arr = new Array[InnerClassInfo](count) + var i = 0 + while (i < count) { + arr(i) = readInnerClass() + i += 1 + } + arr + } + protected def thisClass = name + + def parse() = { + assert(u4 == JAVA_MAGIC, "Bad magic number") + val version = JvmVersion(u2, u2) + this.entries = readConstantPool() + val flags = u2.toShort + this.name = nameAt(u2) + val superName = nameAt(u2) + val interfaces = readInterfaces() + val fields = readMembers() + val methods = readMembers() + val attributes = readAttributes() + + try createInfo(version, entries, flags, name, superName, interfaces, fields, methods, attributes) + finally entries = null + } +} + +abstract class ScalacClassfileModel extends StreamingClassfileModel { + type Result = JvmClassInfo + type InterfaceInfo = String + type MemberInfo = JvmMemberInfo + type AttributeInfo = JvmAttributeInfo + type InnerClassInfo = JvmInnerClassInfo + + protected implicit def EntryArrayTag = arrayTag[PoolEntry] + protected implicit def InterfaceInfoArrayTag = arrayTag[InterfaceInfo] + protected implicit def MemberInfoArrayTag = arrayTag[MemberInfo] + protected implicit def AttributeInfoArrayTag = arrayTag[AttributeInfo] + protected implicit def InnerClassInfoArrayTag = arrayTag[InnerClassInfo] + + def readConstantPoolEntry(): PoolEntry + def readInterface(): String + def readMember(): JvmMemberInfo + def readAttribute(): JvmAttributeInfo + def readInnerClass(): JvmInnerClassInfo + + def createInfo( + version: JvmVersion, + entries: Array[PoolEntry], + flags: Int, + name: String, + superName: String, + interfaces: Array[String], + fields: Array[JvmMemberInfo], + methods: Array[JvmMemberInfo], + attributes: Array[JvmAttributeInfo] + ): JvmClassInfo = new JvmClassInfo(name, superName, interfaces, fields, methods, attributes) +} diff --git a/src/compiler/scala/reflect/internal/ConstantPool.scala b/src/compiler/scala/reflect/internal/ConstantPool.scala new file mode 100644 index 0000000000..f8dd7b9845 --- /dev/null +++ b/src/compiler/scala/reflect/internal/ConstantPool.scala @@ -0,0 +1,78 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.reflect.internal + +import ClassfileConstants._ + +object ConstantPool { + final case class JvmVersion(minorVersion: Int, majorVersion: Int) + + type UShort = Char + + final val CONSTANT_Utf8 = 1 + final val CONSTANT_Integer = 3 + final val CONSTANT_Float = 4 + final val CONSTANT_Long = 5 + final val CONSTANT_Double = 6 + final val CONSTANT_Class = 7 + final val CONSTANT_String = 8 + final val CONSTANT_Fieldref = 9 + final val CONSTANT_Methodref = 10 + final val CONSTANT_InterfaceMethodref = 11 + final val CONSTANT_NameAndType = 12 + + /* + 4.2.2 Unqualified Names + Names of methods, fields and local variables are stored as unqualified + names. Unqualified names must not contain the characters '.', ';', '[' + or '/'. Method names are further constrained so that, with the exception + of the special method names and (§3.9), they must not + contain the characters '<' or '>'. + + 4.3 Descriptors and Signatures + A descriptor is a string representing the type of a field or method. + Descriptors are represented in the class file format using modified + UTF-8 strings (§4.4.7) and thus may be drawn, where not further + constrained, from the entire Unicode character set. A signature is a + string representing the generic type of a field or method, or generic + type information for a class declaration. + */ + abstract class Name_Info(tag: Byte) extends PoolEntry(tag) { + def name_index: UShort + } + abstract class Ref_Info(tag: Byte) extends PoolEntry(tag) { + def class_index: UShort + def name_and_type_index: UShort + } + class Class_info(val name_index: UShort) extends Name_Info(CONSTANT_Class) { } + class Double_info(val value: Double) extends PoolEntry(CONSTANT_Double) { + override def width = 2 + } + class Fieldref_info(val class_index: UShort, val name_and_type_index: UShort) extends Ref_Info(CONSTANT_Fieldref) + class Float_info(val value: Float) extends PoolEntry(CONSTANT_Float) + class Integer_info(val value: Int) extends PoolEntry(CONSTANT_Integer) + class InterfaceMethodref_info(val class_index: UShort, val name_and_type_index: UShort) extends Ref_Info(CONSTANT_InterfaceMethodref) + class Long_info(val value: Long) extends PoolEntry(CONSTANT_Long) { + override def width = 2 + } + class Methodref_info(val class_index: UShort, val name_and_type_index: UShort) extends Ref_Info(CONSTANT_Methodref) + class NameAndType_info(val name_index: UShort, val descriptor_index: UShort) extends Name_Info(CONSTANT_NameAndType) { + override def toString = "NameAndType #%s:#%s;".format(name_index, descriptor_index) + } + class String_info(val string_index: UShort) extends PoolEntry(CONSTANT_String) { } + class Utf8_info(override val stringValue: String) extends PoolEntry(CONSTANT_Utf8) { + override def toString = ("Asciz " + stringValue).trim + } + + abstract class PoolEntry(tag: Byte) { + def width = 1 + def stringValue: String = sys.error("Not a String-valued constant pool entry: " + this) + override def toString = ( + getClass.getName.split("[.$]").last + "/" + tag + ) + } + object NoEntry extends PoolEntry(-1) { } +} diff --git a/src/compiler/scala/reflect/internal/JvmClassInfo.scala b/src/compiler/scala/reflect/internal/JvmClassInfo.scala index d47f51e512..f034fd992d 100644 --- a/src/compiler/scala/reflect/internal/JvmClassInfo.scala +++ b/src/compiler/scala/reflect/internal/JvmClassInfo.scala @@ -11,279 +11,6 @@ import scala.reflect.NameTransformer.decode import scala.tools.util.StringOps.trimTrailingSpace import ConstantPool._ -final case class JvmVersion(minorVersion: Int, majorVersion: Int) - -trait ClassfileModel { - type Result - type Entry - type InterfaceInfo - type MemberInfo - type AttributeInfo - type InnerClassInfo - - protected implicit def EntryArrayTag: ArrayTag[Entry] - protected implicit def InterfaceInfoArrayTag: ArrayTag[InterfaceInfo] - protected implicit def MemberInfoArrayTag: ArrayTag[MemberInfo] - protected implicit def AttributeInfoArrayTag: ArrayTag[AttributeInfo] - protected implicit def InnerClassInfoArrayTag: ArrayTag[InnerClassInfo] - - // These could be implemented to jump forward in the stream if the - // result is not wanted. - def readConstantPoolEntry(): Entry - def readInterface(): InterfaceInfo - def readMember(): MemberInfo - def readAttribute(): AttributeInfo - def readInnerClass(): InnerClassInfo - - def createInfo( - version: JvmVersion, - entries: Array[Entry], - flags: Int, - name: String, - superName: String, - interfaces: Array[InterfaceInfo], - fields: Array[MemberInfo], - methods: Array[MemberInfo], - attributes: Array[AttributeInfo] - ): Result -} - -abstract class StreamingClassfileModel extends ClassfileModel { - protected[this] val in: DataInput - private[this] var name: String = _ - private[this] var entries: Array[PoolEntry] = _ - - type Entry = PoolEntry - - // These translate null into "", it's less troublesome. - protected def nameAt(idx: Int) = entries(idx) match { - case x: Name_Info => stringAt(x.name_index).replace('/', '.') - case _ => "" - } - protected def stringAt(idx: Int) = entries(idx) match { - case x: Utf8_info => x.stringValue - case _ => "" - } - - protected def u4 = in.readInt - protected def u2 = in.readUnsignedShort.toChar - protected def u1 = in.readUnsignedByte - - // The constant_pool table is indexed from 1 to constant_pool_count−1. - protected def readConstantPool(): Array[Entry] = { - val count = u2 - val entries = new Array[Entry](count) - var i = 1 - while (i < count) { - val entry = readConstantPoolEntry() - entries(i) = entry - i += entry.width - } - entries - } - protected def readInterfaces() = { - val count = u2 - val interfaces = new Array[InterfaceInfo](count) - var i = 0 - while (i < count) { - interfaces(i) = readInterface() - i += 1 - } - interfaces - } - protected def readMembers() = { - val count = u2 - val arr = new Array[MemberInfo](count) - var i = 0 - while (i < count) { - arr(i) = readMember() - i += 1 - } - arr - } - protected def readAttributes(): Array[AttributeInfo] = { - val count = u2 - val arr = new Array[AttributeInfo](count) - var i = 0 - while (i < count) { - arr(i) = readAttribute() - i += 1 - } - arr - } - protected def readInnerClasses() = { - val count = u2 - val arr = new Array[InnerClassInfo](count) - var i = 0 - while (i < count) { - arr(i) = readInnerClass() - i += 1 - } - arr - } - protected def thisClass = name - - def parse() = { - assert(u4 == 0xCAFEBABE, "Bad magic number") - val version = JvmVersion(u2, u2) - this.entries = readConstantPool() - val flags = u2.toShort - this.name = nameAt(u2) - val superName = nameAt(u2) - val interfaces = readInterfaces() - val fields = readMembers() - val methods = readMembers() - val attributes = readAttributes() - - try createInfo(version, entries, flags, name, superName, interfaces, fields, methods, attributes) - finally entries = null - } -} - -abstract class ScalacClassfileModel extends StreamingClassfileModel { - type Result = JvmClassInfo - type InterfaceInfo = String - type MemberInfo = JvmMemberInfo - type AttributeInfo = JvmAttributeInfo - type InnerClassInfo = JvmInnerClassInfo - - protected implicit def EntryArrayTag = arrayTag[PoolEntry] - protected implicit def InterfaceInfoArrayTag = arrayTag[InterfaceInfo] - protected implicit def MemberInfoArrayTag = arrayTag[MemberInfo] - protected implicit def AttributeInfoArrayTag = arrayTag[AttributeInfo] - protected implicit def InnerClassInfoArrayTag = arrayTag[InnerClassInfo] - - def readConstantPoolEntry(): PoolEntry - def readInterface(): String - def readMember(): JvmMemberInfo - def readAttribute(): JvmAttributeInfo - def readInnerClass(): JvmInnerClassInfo - - def createInfo( - version: JvmVersion, - entries: Array[PoolEntry], - flags: Int, - name: String, - superName: String, - interfaces: Array[String], - fields: Array[JvmMemberInfo], - methods: Array[JvmMemberInfo], - attributes: Array[JvmAttributeInfo] - ): JvmClassInfo = new JvmClassInfo(name, superName, interfaces, fields, methods, attributes) -} - -class JvmClassInfoBuilder(protected[this] val in: DataInput) extends ScalacClassfileModel { - def readInterface(): InterfaceInfo = nameAt(u2) - def readMember(): JvmMemberInfo = new JvmMemberInfo(u2.toShort, stringAt(u2), stringAt(u2), readAttributes()) - def readInnerClass(): JvmInnerClassInfo = new JvmInnerClassInfo(thisClass, nameAt(u2), nameAt(u2), stringAt(u2), u2.toShort) - - def readConstantPoolEntry(): Entry = (u1: @annotation.switch) match { - case CONSTANT_Utf8 => new Utf8_info(in.readUTF) - case CONSTANT_Integer => new Integer_info(in.readInt) - case CONSTANT_Float => new Float_info(in.readFloat) - case CONSTANT_Long => new Long_info(in.readLong) - case CONSTANT_Double => new Double_info(in.readDouble) - case CONSTANT_Class => new Class_info(u2) - case CONSTANT_String => new String_info(u2) - case CONSTANT_Fieldref => new Fieldref_info(u2, u2) - case CONSTANT_Methodref => new Methodref_info(u2, u2) - case CONSTANT_InterfaceMethodref => new InterfaceMethodref_info(u2, u2) - case CONSTANT_NameAndType => new NameAndType_info(u2, u2) - } - - // field_info attributes: - // ConstantValue (§4.7.2), Synthetic (§4.7.8), Signature (§4.7.9), Deprecated (§4.7.15), - // RuntimeVisibleAnnotations (§4.7.16) and RuntimeInvisibleAnnotations (§4.7.17). - // - // method_info attributes: - // Code (§4.7.3), Exceptions (§4.7.5), Synthetic (§4.7.8), Signature (§4.7.9), Deprecated (§4.7.15), - // RuntimeVisibleAnnotations (§4.7.16), RuntimeInvisibleAnnotations (§4.7.17), RuntimeVisibleParameterAnnotations (§4.7.18), - // RuntimeInvisibleParameterAnnotations (§4.7.19) and AnnotationDefault (§4.7.20). - - def readAttribute(): AttributeInfo = stringAt(u2) match { - case "Signature" => u4 ; new SignatureAttr(stringAt(u2)) - case "InnerClasses" => u4 ; new InnerClassesAttr(readInnerClasses()) - case name => val bytes = new Array[Byte](u4) ; in.readFully(bytes) ; new GenericAttr(name, bytes) - } -} - -object Classify { - - /* - - - 4.2.2 Unqualified Names - -Names of methods, fields and local variables are stored as unqualified -names. Unqualified names must not contain the characters '.', ';', '[' -or '/'. Method names are further constrained so that, with the exception -of the special method names and (§3.9), they must not -contain the characters '<' or '>'. - - 4.3 Descriptors and Signatures - -A descriptor is a string representing the type of a field or method. -Descriptors are represented in the class file format using modified -UTF-8 strings (§4.4.7) and thus may be drawn, where not further -constrained, from the entire Unicode character set. A signature is a -string representing the generic type of a field or method, or generic -type information for a class declaration. -*/ - -} - -object ConstantPool { - type UShort = Char - - final val CONSTANT_Utf8 = 1 - final val CONSTANT_Integer = 3 - final val CONSTANT_Float = 4 - final val CONSTANT_Long = 5 - final val CONSTANT_Double = 6 - final val CONSTANT_Class = 7 - final val CONSTANT_String = 8 - final val CONSTANT_Fieldref = 9 - final val CONSTANT_Methodref = 10 - final val CONSTANT_InterfaceMethodref = 11 - final val CONSTANT_NameAndType = 12 - - abstract class Name_Info(tag: Byte) extends PoolEntry(tag) { - def name_index: UShort - } - abstract class Ref_Info(tag: Byte) extends PoolEntry(tag) { - def class_index: UShort - def name_and_type_index: UShort - } - class Class_info(val name_index: UShort) extends Name_Info(CONSTANT_Class) { } - class Double_info(val value: Double) extends PoolEntry(CONSTANT_Double) { - override def width = 2 - } - class Fieldref_info(val class_index: UShort, val name_and_type_index: UShort) extends Ref_Info(CONSTANT_Fieldref) - class Float_info(val value: Float) extends PoolEntry(CONSTANT_Float) - class Integer_info(val value: Int) extends PoolEntry(CONSTANT_Integer) - class InterfaceMethodref_info(val class_index: UShort, val name_and_type_index: UShort) extends Ref_Info(CONSTANT_InterfaceMethodref) - class Long_info(val value: Long) extends PoolEntry(CONSTANT_Long) { - override def width = 2 - } - class Methodref_info(val class_index: UShort, val name_and_type_index: UShort) extends Ref_Info(CONSTANT_Methodref) - class NameAndType_info(val name_index: UShort, val descriptor_index: UShort) extends Name_Info(CONSTANT_NameAndType) { - override def toString = "NameAndType #%s:#%s;".format(name_index, descriptor_index) - } - class String_info(val string_index: UShort) extends PoolEntry(CONSTANT_String) { } - class Utf8_info(override val stringValue: String) extends PoolEntry(CONSTANT_Utf8) { - override def toString = ("Asciz " + stringValue).trim - } - - abstract class PoolEntry(tag: Byte) { - def width = 1 - def stringValue: String = sys.error("Not a String-valued constant pool entry: " + this) - override def toString = ( - getClass.getName.split("[.$]").last + "/" + tag - ) - } - object NoEntry extends PoolEntry(-1) { } -} - abstract class JvmInfo(attributes: Array[JvmAttributeInfo]) { // def flags: Short def name: String @@ -291,7 +18,15 @@ abstract class JvmInfo(attributes: Array[JvmAttributeInfo]) { val signature = attributes collectFirst { case x: SignatureAttr => x.value } getOrElse "" val innerClasses = attributes collectFirst { case x: InnerClassesAttr => x.value } getOrElse Array() } - +abstract class JvmAttributeInfo { + // attribute_info { + // u2 attribute_name_index; + // u4 attribute_length; + // u1 info[attribute_length]; + // } + def name: String + def value: Any +} class JvmClassInfo( val name: String, @@ -352,24 +87,9 @@ class JvmMemberInfo( if (hasSignature) toGenericString else toErasedString ) } - -abstract class JvmAttributeInfo { - def name: String - def value: Any -} -class GenericAttr(val name: String, val value: Array[Byte]) extends JvmAttributeInfo { - // attribute_info { - // u2 attribute_name_index; - // u4 attribute_length; - // u1 info[attribute_length]; - // } -} -class SignatureAttr(val value: String) extends JvmAttributeInfo { - def name = "Signature" -} -class InnerClassesAttr(val value: Array[JvmInnerClassInfo]) extends JvmAttributeInfo { - def name = "InnerClasses" -} +class GenericAttr(val name: String, val value: Array[Byte]) extends JvmAttributeInfo { } +class SignatureAttr(val value: String) extends JvmAttributeInfo { def name = "Signature" } +class InnerClassesAttr(val value: Array[JvmInnerClassInfo]) extends JvmAttributeInfo { def name = "InnerClasses" } // package foo { class Foo { class Bar } } // @@ -410,6 +130,44 @@ class JvmInnerClassInfo( } object JvmClassInfo { + class Builder(protected[this] val in: DataInput) extends ScalacClassfileModel { + def readInterface(): InterfaceInfo = nameAt(u2) + def readMember(): JvmMemberInfo = new JvmMemberInfo(u2.toShort, stringAt(u2), stringAt(u2), readAttributes()) + def readInnerClass(): JvmInnerClassInfo = new JvmInnerClassInfo(thisClass, nameAt(u2), nameAt(u2), stringAt(u2), u2.toShort) + + def readConstantPoolEntry(): Entry = (u1: @annotation.switch) match { + case CONSTANT_Utf8 => new Utf8_info(in.readUTF) + case CONSTANT_Integer => new Integer_info(in.readInt) + case CONSTANT_Float => new Float_info(in.readFloat) + case CONSTANT_Long => new Long_info(in.readLong) + case CONSTANT_Double => new Double_info(in.readDouble) + case CONSTANT_Class => new Class_info(u2) + case CONSTANT_String => new String_info(u2) + case CONSTANT_Fieldref => new Fieldref_info(u2, u2) + case CONSTANT_Methodref => new Methodref_info(u2, u2) + case CONSTANT_InterfaceMethodref => new InterfaceMethodref_info(u2, u2) + case CONSTANT_NameAndType => new NameAndType_info(u2, u2) + } + + // field_info attributes: + // ConstantValue (§4.7.2), Synthetic (§4.7.8), Signature (§4.7.9), Deprecated (§4.7.15), + // RuntimeVisibleAnnotations (§4.7.16) and RuntimeInvisibleAnnotations (§4.7.17). + // + // method_info attributes: + // Code (§4.7.3), Exceptions (§4.7.5), Synthetic (§4.7.8), Signature (§4.7.9), Deprecated (§4.7.15), + // RuntimeVisibleAnnotations (§4.7.16), RuntimeInvisibleAnnotations (§4.7.17), RuntimeVisibleParameterAnnotations (§4.7.18), + // RuntimeInvisibleParameterAnnotations (§4.7.19) and AnnotationDefault (§4.7.20). + def readAttribute(): AttributeInfo = { + val name = stringAt(u2) + val len = u4 + name match { + case "Signature" => new SignatureAttr(stringAt(u2)) + case "InnerClasses" => new InnerClassesAttr(readInnerClasses()) + case name => val bytes = new Array[Byte](len) ; in.readFully(bytes) ; new GenericAttr(name, bytes) + } + } + } + private def classFiles(path: String) = Directory(path).deepFiles filter (_ hasExtension "class") @@ -435,6 +193,6 @@ object JvmClassInfo { } def fromDataInput(in: DataInput): JvmClassInfo = { - new JvmClassInfoBuilder(in) parse + new Builder(in) parse } } -- cgit v1.2.3