1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
/* NSC -- new Scala compiler
* Copyright 2005-2011 LAMP/EPFL
* @author Paul Phillips
*/
package scala.reflect.internal
import java.io.{ DataInput, InputStream, DataInputStream, ByteArrayInputStream, BufferedInputStream, FileInputStream }
import scala.tools.nsc.io.{ Directory }
import scala.reflect.NameTransformer.decode
import scala.tools.util.StringOps.trimTrailingSpace
import ConstantPool._
import ClassfileConstants._
abstract class JvmInfo(attributes: Array[JvmAttributeInfo]) {
// def flags: Short
def name: String
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,
val superName: String,
val interfaces: Array[String],
val fields: Array[JvmMemberInfo],
val methods: Array[JvmMemberInfo],
attributes: Array[JvmAttributeInfo]
) extends JvmInfo(attributes) {
def members = fields ++ methods sortBy (_.decodedName)
def memberDescriptors = members map (_.toErasedString)
def memberSignatures = members filter (_.hasSignature) map (_.toGenericString)
def descriptorsString = if (memberDescriptors.nonEmpty) memberDescriptors.mkString("\n-- Member Descriptors --\n", "\n", "\n") else ""
def signaturesString = if (memberSignatures.nonEmpty) memberSignatures.mkString("\n-- Member Signatures --\n", "\n", "\n") else ""
def innersString = if (innerClasses.isEmpty) "" else innerClasses.mkString("\n-- Inner Classes --\n", "\n", "\n")
def membersString = descriptorsString + signaturesString
def extendsString = if (superName == "") "" else " extends " + superName
def implementsString = if (interfaces.isEmpty) "" else interfaces.mkString("Implements: ", ", ", "")
private def group(label: String, xs: Traversable[(String, String)]) =
xs map { case (name, value) => line(label, name, value) } mkString "\n"
private def line(label: String, name: String, data: String) =
trimTrailingSpace(" %-15s %30s %s".format(label, name, data))
override def toString = (
List(
"class " + name + extendsString,
if (signature == "") "" else line("class sig", "", signature),
group("interface", interfaces map (x => (("", x)))),
(innerClasses map (ic => line(ic.kind, ic.innerName, ic.nestString))).sorted.mkString("\n"),
group("descriptor", members map (x => (x.name, x.descriptor))),
group("signature", members filter (_.hasSignature) map (x => (x.name, x.signature)))
) map trimTrailingSpace filterNot (_ == "") mkString ("", "\n", "\n")
)
}
// method_info or field_info {
// u2 access_flags;
// u2 name_index;
// u2 descriptor_index;
// u2 attributes_count;
// attribute_info attributes[attributes_count];
// }
class JvmMemberInfo(
val flags: Short,
val name: String,
val descriptor: String,
attributes: Array[JvmAttributeInfo]
) extends JvmInfo(attributes) {
final def isAbstract = (flags & JAVA_ACC_ABSTRACT) != 0
final def isAnnotated = (flags & JAVA_ACC_ANNOTATION) != 0
final def isFinal = (flags & JAVA_ACC_FINAL) != 0
final def isPrivate = (flags & JAVA_ACC_PRIVATE) != 0
final def isProtected = (flags & JAVA_ACC_PROTECTED) != 0
final def isPublic = (flags & JAVA_ACC_PUBLIC) != 0
final def isStatic = (flags & JAVA_ACC_STATIC) != 0
final def isSynthetic = (flags & JAVA_ACC_SYNTHETIC) != 0
final def isVarargs = (flags & JAVA_ACC_VARARGS) != 0
// method only
final def isBridge = (flags & JAVA_ACC_BRIDGE) != 0
// field only
final def isEnum = (flags & JAVA_ACC_ENUM) != 0
final def isTransient = (flags & JAVA_ACC_TRANSIENT) != 0
def isMethod = descriptor startsWith "(" // )
def isField = !isMethod
def scalaFlags = toScalaMethodFlags(flags)
def decodedName = decode(name)
def hasSignature = signature != ""
def toErasedString = "%-30s %s".format(decodedName, descriptor)
def toGenericString = "%-30s %s".format(decodedName, signature)
override def toString = (
if (hasSignature) toGenericString else toErasedString
)
}
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 } }
//
// javap would say
// Bar = class foo.Foo$Bar of class foo.Foo
// which is translated as
// innerClass = foo.Foo$Bar
// outerClass = foo.Foo
// innerName = Bar
class JvmInnerClassInfo(
thisClass: String, // classfile which is being parsed
val innerClass: String, // the full name of the inner/nested class
val outerClass: String, // the full name of the outer class - must be a prefix of innerClass
val innerName: String, // the simple name of the inner class - should (?) be a suffix of innerClass
val flags: Short // flags
) {
val isEntryOfEnclosingClass = !isAnonymousClass && (innerClass == thisClass)
val isEntryOfNestedClass = !isAnonymousClass && (outerClass == thisClass)
def isTopLevelClass = outerClass == ""
def isAnonymousClass = innerName == ""
def isMemberClass = !isTopLevelClass
def kind = (
if (isEntryOfEnclosingClass) "inner/enclosing"
else if (isEntryOfNestedClass) "inner/nested"
else if (isAnonymousClass) "inner/anon"
else "inner"
)
def nestString = (
if (isEntryOfEnclosingClass) "enclosing class: " + outerClass
else if (isEntryOfNestedClass) "member class: " + innerClass
else if (isAnonymousClass) "anonymous class: " + innerClass
else innerClass + " in " + outerClass
)
override def toString = innerName + "=" + nestString
}
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")
def classInfoMap(path: String): Map[String, JvmClassInfo] = {
classFiles(path) map (f => (f.path, JvmClassInfo fromFile f.jfile)) toMap
}
def classInfoList(path: String): List[(String, JvmClassInfo)] = {
classInfoMap(path).toList sortBy (_._1)
}
def fromFile(file: java.io.File) =
fromStream(new BufferedInputStream(new FileInputStream(file)))
def fromBytes(bytes: Array[Byte]) =
fromStream(new ByteArrayInputStream(bytes))
def fromPath(path: String) =
fromStream(new BufferedInputStream(new FileInputStream(path)))
def fromStream(in0: InputStream) = {
val in = new DataInputStream(in0)
try fromDataInput(in) finally in.close()
}
def fromDataInput(in: DataInput): JvmClassInfo = {
new Builder(in) parse
}
}
|