paul@137 | 1 | #!/usr/bin/env python |
paul@137 | 2 | |
paul@137 | 3 | """ |
paul@137 | 4 | Java class file decoder. Specification found at the following URL: |
paul@137 | 5 | http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html |
paul@137 | 6 | """ |
paul@137 | 7 | |
paul@137 | 8 | import struct # for general decoding of class files |
paul@137 | 9 | |
paul@137 | 10 | # Utility functions. |
paul@137 | 11 | |
paul@137 | 12 | def u1(data): |
paul@137 | 13 | return struct.unpack(">B", data[0:1])[0] |
paul@137 | 14 | |
paul@137 | 15 | def u2(data): |
paul@137 | 16 | return struct.unpack(">H", data[0:2])[0] |
paul@137 | 17 | |
paul@137 | 18 | def s2(data): |
paul@137 | 19 | return struct.unpack(">h", data[0:2])[0] |
paul@137 | 20 | |
paul@137 | 21 | def u4(data): |
paul@137 | 22 | return struct.unpack(">L", data[0:4])[0] |
paul@137 | 23 | |
paul@137 | 24 | def s4(data): |
paul@137 | 25 | return struct.unpack(">l", data[0:4])[0] |
paul@137 | 26 | |
paul@137 | 27 | def s8(data): |
paul@137 | 28 | return struct.unpack(">q", data[0:8])[0] |
paul@137 | 29 | |
paul@137 | 30 | def f4(data): |
paul@137 | 31 | return struct.unpack(">f", data[0:4])[0] |
paul@137 | 32 | |
paul@137 | 33 | def f8(data): |
paul@137 | 34 | return struct.unpack(">d", data[0:8])[0] |
paul@137 | 35 | |
paul@137 | 36 | # Useful tables and constants. |
paul@137 | 37 | |
paul@137 | 38 | descriptor_base_type_mapping = { |
paul@137 | 39 | "B" : "int", |
paul@137 | 40 | "C" : "str", |
paul@137 | 41 | "D" : "float", |
paul@137 | 42 | "F" : "float", |
paul@137 | 43 | "I" : "int", |
paul@137 | 44 | "J" : "int", |
paul@137 | 45 | "L" : "object", |
paul@137 | 46 | "S" : "int", |
paul@137 | 47 | "Z" : "bool", |
paul@137 | 48 | "[" : "list" |
paul@137 | 49 | } |
paul@137 | 50 | |
paul@141 | 51 | type_names_to_default_values = { |
paul@141 | 52 | "int" : 0, |
paul@141 | 53 | "str" : u"", |
paul@141 | 54 | "float" : 0.0, |
paul@141 | 55 | "object" : None, |
paul@141 | 56 | "bool" : 0, # NOTE: Should be False. |
paul@141 | 57 | "list" : [] |
paul@141 | 58 | } |
paul@141 | 59 | |
paul@141 | 60 | def get_default_for_type(type_name): |
paul@141 | 61 | global type_names_to_default_values |
paul@141 | 62 | return type_names_to_default_values.get(type_name) |
paul@141 | 63 | |
paul@137 | 64 | PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, SUPER, SYNCHRONIZED, VOLATILE, TRANSIENT, NATIVE, INTERFACE, ABSTRACT, STRICT = \ |
paul@137 | 65 | 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800 |
paul@137 | 66 | |
paul@137 | 67 | def has_flags(flags, desired): |
paul@137 | 68 | desired_flags = reduce(lambda a, b: a | b, desired, 0) |
paul@137 | 69 | return (flags & desired_flags) == desired_flags |
paul@137 | 70 | |
paul@137 | 71 | # Useful mix-ins. |
paul@137 | 72 | |
paul@137 | 73 | class PythonMethodUtils: |
paul@137 | 74 | symbol_sep = "___" # was "$" |
paul@137 | 75 | type_sep = "__" # replaces "/" |
paul@137 | 76 | array_sep = "_array_" # was "[]" |
paul@137 | 77 | base_seps = ("_", "_") # was "<" and ">" |
paul@137 | 78 | |
paul@137 | 79 | def get_unqualified_python_name(self): |
paul@137 | 80 | name = self.get_name() |
paul@137 | 81 | if str(name) == "<init>": |
paul@137 | 82 | return "__init__" |
paul@137 | 83 | elif str(name) == "<clinit>": |
paul@137 | 84 | return "__clinit__" |
paul@137 | 85 | else: |
paul@137 | 86 | return str(name) |
paul@137 | 87 | |
paul@137 | 88 | def get_python_name(self): |
paul@137 | 89 | name = self.get_unqualified_python_name() |
paul@137 | 90 | if name == "__clinit__": |
paul@137 | 91 | return name |
paul@137 | 92 | return name + self.symbol_sep + self._get_descriptor_as_name() |
paul@137 | 93 | |
paul@137 | 94 | def _get_descriptor_as_name(self): |
paul@137 | 95 | l = [] |
paul@137 | 96 | for descriptor_type in self.get_descriptor()[0]: |
paul@137 | 97 | l.append(self._get_type_as_name(descriptor_type)) |
paul@137 | 98 | return self.symbol_sep.join(l) |
paul@137 | 99 | |
paul@137 | 100 | def _get_type_as_name(self, descriptor_type, s=""): |
paul@137 | 101 | base_type, object_type, array_type = descriptor_type |
paul@137 | 102 | if base_type == "L": |
paul@137 | 103 | return object_type.replace("/", self.type_sep) + s |
paul@137 | 104 | elif base_type == "[": |
paul@137 | 105 | return self._get_type_as_name(array_type, s + self.array_sep) |
paul@137 | 106 | else: |
paul@137 | 107 | return self.base_seps[0] + base_type + self.base_seps[1] + s |
paul@137 | 108 | |
paul@137 | 109 | class PythonNameUtils: |
paul@137 | 110 | def get_python_name(self): |
paul@137 | 111 | # NOTE: This may not be comprehensive. |
paul@137 | 112 | if not str(self.get_name()).startswith("["): |
paul@137 | 113 | return str(self.get_name()).replace("/", ".") |
paul@137 | 114 | else: |
paul@137 | 115 | return self._get_type_name( |
paul@137 | 116 | get_field_descriptor( |
paul@137 | 117 | str(self.get_name()) |
paul@137 | 118 | ) |
paul@137 | 119 | ).replace("/", ".") |
paul@137 | 120 | |
paul@137 | 121 | def _get_type_name(self, descriptor_type): |
paul@137 | 122 | base_type, object_type, array_type = descriptor_type |
paul@137 | 123 | if base_type == "L": |
paul@137 | 124 | return object_type |
paul@137 | 125 | elif base_type == "[": |
paul@137 | 126 | return self._get_type_name(array_type) |
paul@137 | 127 | else: |
paul@137 | 128 | return descriptor_base_type_mapping[base_type] |
paul@137 | 129 | |
paul@137 | 130 | class NameUtils: |
paul@137 | 131 | def get_name(self): |
paul@137 | 132 | if self.name_index != 0: |
paul@137 | 133 | return self.class_file.constants[self.name_index - 1] |
paul@137 | 134 | else: |
paul@137 | 135 | # Some name indexes are zero to indicate special conditions. |
paul@137 | 136 | return None |
paul@137 | 137 | |
paul@137 | 138 | class NameAndTypeUtils: |
paul@137 | 139 | def get_name(self): |
paul@137 | 140 | if self.name_and_type_index != 0: |
paul@137 | 141 | return self.class_file.constants[self.name_and_type_index - 1].get_name() |
paul@137 | 142 | else: |
paul@137 | 143 | # Some name indexes are zero to indicate special conditions. |
paul@137 | 144 | return None |
paul@137 | 145 | |
paul@137 | 146 | def get_field_descriptor(self): |
paul@137 | 147 | if self.name_and_type_index != 0: |
paul@137 | 148 | return self.class_file.constants[self.name_and_type_index - 1].get_field_descriptor() |
paul@137 | 149 | else: |
paul@137 | 150 | # Some name indexes are zero to indicate special conditions. |
paul@137 | 151 | return None |
paul@137 | 152 | |
paul@137 | 153 | def get_method_descriptor(self): |
paul@137 | 154 | if self.name_and_type_index != 0: |
paul@137 | 155 | return self.class_file.constants[self.name_and_type_index - 1].get_method_descriptor() |
paul@137 | 156 | else: |
paul@137 | 157 | # Some name indexes are zero to indicate special conditions. |
paul@137 | 158 | return None |
paul@137 | 159 | |
paul@137 | 160 | def get_class(self): |
paul@137 | 161 | return self.class_file.constants[self.class_index - 1] |
paul@137 | 162 | |
paul@137 | 163 | # Symbol parsing. |
paul@137 | 164 | |
paul@137 | 165 | def get_method_descriptor(s): |
paul@137 | 166 | assert s[0] == "(" |
paul@137 | 167 | params = [] |
paul@137 | 168 | s = s[1:] |
paul@137 | 169 | while s[0] != ")": |
paul@137 | 170 | parameter_descriptor, s = _get_parameter_descriptor(s) |
paul@137 | 171 | params.append(parameter_descriptor) |
paul@137 | 172 | if s[1] != "V": |
paul@137 | 173 | return_type, s = _get_field_type(s[1:]) |
paul@137 | 174 | else: |
paul@137 | 175 | return_type, s = None, s[1:] |
paul@137 | 176 | return params, return_type |
paul@137 | 177 | |
paul@137 | 178 | def get_field_descriptor(s): |
paul@137 | 179 | return _get_field_type(s)[0] |
paul@137 | 180 | |
paul@137 | 181 | def _get_parameter_descriptor(s): |
paul@137 | 182 | return _get_field_type(s) |
paul@137 | 183 | |
paul@137 | 184 | def _get_component_type(s): |
paul@137 | 185 | return _get_field_type(s) |
paul@137 | 186 | |
paul@137 | 187 | def _get_field_type(s): |
paul@137 | 188 | base_type, s = _get_base_type(s) |
paul@137 | 189 | object_type = None |
paul@137 | 190 | array_type = None |
paul@137 | 191 | if base_type == "L": |
paul@137 | 192 | object_type, s = _get_object_type(s) |
paul@137 | 193 | elif base_type == "[": |
paul@137 | 194 | array_type, s = _get_array_type(s) |
paul@137 | 195 | return (base_type, object_type, array_type), s |
paul@137 | 196 | |
paul@137 | 197 | def _get_base_type(s): |
paul@137 | 198 | if len(s) > 0: |
paul@137 | 199 | return s[0], s[1:] |
paul@137 | 200 | else: |
paul@137 | 201 | return None, s |
paul@137 | 202 | |
paul@137 | 203 | def _get_object_type(s): |
paul@137 | 204 | if len(s) > 0: |
paul@137 | 205 | s_end = s.find(";") |
paul@137 | 206 | assert s_end != -1 |
paul@137 | 207 | return s[:s_end], s[s_end+1:] |
paul@137 | 208 | else: |
paul@137 | 209 | return None, s |
paul@137 | 210 | |
paul@137 | 211 | def _get_array_type(s): |
paul@137 | 212 | if len(s) > 0: |
paul@137 | 213 | return _get_component_type(s) |
paul@137 | 214 | else: |
paul@137 | 215 | return None, s |
paul@137 | 216 | |
paul@137 | 217 | # Constant information. |
paul@137 | 218 | |
paul@137 | 219 | class ClassInfo(NameUtils, PythonNameUtils): |
paul@137 | 220 | def init(self, data, class_file): |
paul@137 | 221 | self.class_file = class_file |
paul@137 | 222 | self.name_index = u2(data[0:2]) |
paul@137 | 223 | return data[2:] |
paul@137 | 224 | |
paul@137 | 225 | class RefInfo(NameAndTypeUtils): |
paul@137 | 226 | def init(self, data, class_file): |
paul@137 | 227 | self.class_file = class_file |
paul@137 | 228 | self.class_index = u2(data[0:2]) |
paul@137 | 229 | self.name_and_type_index = u2(data[2:4]) |
paul@137 | 230 | return data[4:] |
paul@137 | 231 | |
paul@137 | 232 | class FieldRefInfo(RefInfo, PythonNameUtils): |
paul@137 | 233 | def get_descriptor(self): |
paul@137 | 234 | return RefInfo.get_field_descriptor(self) |
paul@137 | 235 | |
paul@137 | 236 | class MethodRefInfo(RefInfo, PythonMethodUtils): |
paul@137 | 237 | def get_descriptor(self): |
paul@137 | 238 | return RefInfo.get_method_descriptor(self) |
paul@137 | 239 | |
paul@137 | 240 | class InterfaceMethodRefInfo(MethodRefInfo): |
paul@137 | 241 | pass |
paul@137 | 242 | |
paul@137 | 243 | class NameAndTypeInfo(NameUtils, PythonNameUtils): |
paul@137 | 244 | def init(self, data, class_file): |
paul@137 | 245 | self.class_file = class_file |
paul@137 | 246 | self.name_index = u2(data[0:2]) |
paul@137 | 247 | self.descriptor_index = u2(data[2:4]) |
paul@137 | 248 | return data[4:] |
paul@137 | 249 | |
paul@137 | 250 | def get_field_descriptor(self): |
paul@137 | 251 | return get_field_descriptor(unicode(self.class_file.constants[self.descriptor_index - 1])) |
paul@137 | 252 | |
paul@137 | 253 | def get_method_descriptor(self): |
paul@137 | 254 | return get_method_descriptor(unicode(self.class_file.constants[self.descriptor_index - 1])) |
paul@137 | 255 | |
paul@137 | 256 | class Utf8Info: |
paul@137 | 257 | def init(self, data, class_file): |
paul@137 | 258 | self.class_file = class_file |
paul@137 | 259 | self.length = u2(data[0:2]) |
paul@137 | 260 | self.bytes = data[2:2+self.length] |
paul@137 | 261 | return data[2+self.length:] |
paul@137 | 262 | |
paul@137 | 263 | def __str__(self): |
paul@137 | 264 | return self.bytes |
paul@137 | 265 | |
paul@137 | 266 | def __unicode__(self): |
paul@137 | 267 | return unicode(self.bytes, "utf-8") |
paul@137 | 268 | |
paul@137 | 269 | def get_value(self): |
paul@137 | 270 | return str(self) |
paul@137 | 271 | |
paul@137 | 272 | class StringInfo: |
paul@137 | 273 | def init(self, data, class_file): |
paul@137 | 274 | self.class_file = class_file |
paul@137 | 275 | self.string_index = u2(data[0:2]) |
paul@137 | 276 | return data[2:] |
paul@137 | 277 | |
paul@137 | 278 | def __str__(self): |
paul@137 | 279 | return str(self.class_file.constants[self.string_index - 1]) |
paul@137 | 280 | |
paul@137 | 281 | def __unicode__(self): |
paul@137 | 282 | return unicode(self.class_file.constants[self.string_index - 1]) |
paul@137 | 283 | |
paul@137 | 284 | def get_value(self): |
paul@137 | 285 | return str(self) |
paul@137 | 286 | |
paul@137 | 287 | class SmallNumInfo: |
paul@137 | 288 | def init(self, data, class_file): |
paul@137 | 289 | self.class_file = class_file |
paul@137 | 290 | self.bytes = data[0:4] |
paul@137 | 291 | return data[4:] |
paul@137 | 292 | |
paul@137 | 293 | class IntegerInfo(SmallNumInfo): |
paul@137 | 294 | def get_value(self): |
paul@137 | 295 | return s4(self.bytes) |
paul@137 | 296 | |
paul@137 | 297 | class FloatInfo(SmallNumInfo): |
paul@137 | 298 | def get_value(self): |
paul@137 | 299 | return f4(self.bytes) |
paul@137 | 300 | |
paul@137 | 301 | class LargeNumInfo: |
paul@137 | 302 | def init(self, data, class_file): |
paul@137 | 303 | self.class_file = class_file |
paul@137 | 304 | self.high_bytes = data[0:4] |
paul@137 | 305 | self.low_bytes = data[4:8] |
paul@137 | 306 | return data[8:] |
paul@137 | 307 | |
paul@137 | 308 | class LongInfo(LargeNumInfo): |
paul@137 | 309 | def get_value(self): |
paul@137 | 310 | return s8(self.high_bytes + self.low_bytes) |
paul@137 | 311 | |
paul@137 | 312 | class DoubleInfo(LargeNumInfo): |
paul@137 | 313 | def get_value(self): |
paul@137 | 314 | return f8(self.high_bytes + self.low_bytes) |
paul@137 | 315 | |
paul@137 | 316 | # Other information. |
paul@137 | 317 | # Objects of these classes are generally aware of the class they reside in. |
paul@137 | 318 | |
paul@137 | 319 | class ItemInfo(NameUtils): |
paul@137 | 320 | def init(self, data, class_file): |
paul@137 | 321 | self.class_file = class_file |
paul@137 | 322 | self.access_flags = u2(data[0:2]) |
paul@137 | 323 | self.name_index = u2(data[2:4]) |
paul@137 | 324 | self.descriptor_index = u2(data[4:6]) |
paul@137 | 325 | self.attributes, data = self.class_file._get_attributes(data[6:]) |
paul@137 | 326 | return data |
paul@137 | 327 | |
paul@137 | 328 | class FieldInfo(ItemInfo, PythonNameUtils): |
paul@137 | 329 | def get_descriptor(self): |
paul@137 | 330 | return get_field_descriptor(unicode(self.class_file.constants[self.descriptor_index - 1])) |
paul@137 | 331 | |
paul@137 | 332 | class MethodInfo(ItemInfo, PythonMethodUtils): |
paul@137 | 333 | def get_descriptor(self): |
paul@137 | 334 | return get_method_descriptor(unicode(self.class_file.constants[self.descriptor_index - 1])) |
paul@137 | 335 | |
paul@137 | 336 | class AttributeInfo: |
paul@137 | 337 | def init(self, data, class_file): |
paul@137 | 338 | self.attribute_length = u4(data[0:4]) |
paul@137 | 339 | self.info = data[4:4+self.attribute_length] |
paul@137 | 340 | return data[4+self.attribute_length:] |
paul@137 | 341 | |
paul@137 | 342 | # NOTE: Decode the different attribute formats. |
paul@137 | 343 | |
paul@137 | 344 | class SourceFileAttributeInfo(AttributeInfo, NameUtils, PythonNameUtils): |
paul@137 | 345 | def init(self, data, class_file): |
paul@137 | 346 | self.class_file = class_file |
paul@137 | 347 | self.attribute_length = u4(data[0:4]) |
paul@137 | 348 | # Permit the NameUtils mix-in. |
paul@137 | 349 | self.name_index = self.sourcefile_index = u2(data[4:6]) |
paul@137 | 350 | return data[6:] |
paul@137 | 351 | |
paul@137 | 352 | class ConstantValueAttributeInfo(AttributeInfo): |
paul@137 | 353 | def init(self, data, class_file): |
paul@137 | 354 | self.class_file = class_file |
paul@137 | 355 | self.attribute_length = u4(data[0:4]) |
paul@137 | 356 | self.constant_value_index = u2(data[4:6]) |
paul@137 | 357 | assert 4+self.attribute_length == 6 |
paul@137 | 358 | return data[4+self.attribute_length:] |
paul@137 | 359 | |
paul@137 | 360 | def get_value(self): |
paul@137 | 361 | return self.class_file.constants[self.constant_value_index - 1].get_value() |
paul@137 | 362 | |
paul@137 | 363 | class CodeAttributeInfo(AttributeInfo): |
paul@137 | 364 | def init(self, data, class_file): |
paul@137 | 365 | self.class_file = class_file |
paul@137 | 366 | self.attribute_length = u4(data[0:4]) |
paul@137 | 367 | self.max_stack = u2(data[4:6]) |
paul@137 | 368 | self.max_locals = u2(data[6:8]) |
paul@137 | 369 | self.code_length = u4(data[8:12]) |
paul@137 | 370 | end_of_code = 12+self.code_length |
paul@137 | 371 | self.code = data[12:end_of_code] |
paul@137 | 372 | self.exception_table_length = u2(data[end_of_code:end_of_code+2]) |
paul@137 | 373 | self.exception_table = [] |
paul@137 | 374 | data = data[end_of_code + 2:] |
paul@137 | 375 | for i in range(0, self.exception_table_length): |
paul@137 | 376 | exception = ExceptionInfo() |
paul@137 | 377 | data = exception.init(data) |
paul@137 | 378 | self.exception_table.append(exception) |
paul@137 | 379 | self.attributes, data = self.class_file._get_attributes(data) |
paul@137 | 380 | return data |
paul@137 | 381 | |
paul@137 | 382 | class ExceptionsAttributeInfo(AttributeInfo): |
paul@137 | 383 | def init(self, data, class_file): |
paul@137 | 384 | self.class_file = class_file |
paul@137 | 385 | self.attribute_length = u4(data[0:4]) |
paul@137 | 386 | self.number_of_exceptions = u2(data[4:6]) |
paul@137 | 387 | self.exception_index_table = [] |
paul@137 | 388 | index = 6 |
paul@137 | 389 | for i in range(0, self.number_of_exceptions): |
paul@137 | 390 | self.exception_index_table.append(u2(data[index:index+2])) |
paul@137 | 391 | index += 2 |
paul@137 | 392 | return data[index:] |
paul@137 | 393 | |
paul@137 | 394 | def get_exception(self, i): |
paul@137 | 395 | exception_index = self.exception_index_table[i] |
paul@137 | 396 | return self.class_file.constants[exception_index - 1] |
paul@137 | 397 | |
paul@137 | 398 | class InnerClassesAttributeInfo(AttributeInfo): |
paul@137 | 399 | def init(self, data, class_file): |
paul@137 | 400 | self.class_file = class_file |
paul@137 | 401 | self.attribute_length = u4(data[0:4]) |
paul@137 | 402 | self.number_of_classes = u2(data[4:6]) |
paul@137 | 403 | self.classes = [] |
paul@137 | 404 | data = data[6:] |
paul@137 | 405 | for i in range(0, self.number_of_classes): |
paul@137 | 406 | inner_class = InnerClassInfo() |
paul@137 | 407 | data = inner_class.init(data, self.class_file) |
paul@137 | 408 | self.classes.append(inner_class) |
paul@137 | 409 | return data |
paul@137 | 410 | |
paul@137 | 411 | class SyntheticAttributeInfo(AttributeInfo): |
paul@137 | 412 | pass |
paul@137 | 413 | |
paul@137 | 414 | class LineNumberAttributeInfo(AttributeInfo): |
paul@137 | 415 | def init(self, data, class_file): |
paul@137 | 416 | self.class_file = class_file |
paul@137 | 417 | self.attribute_length = u4(data[0:4]) |
paul@137 | 418 | self.line_number_table_length = u2(data[4:6]) |
paul@137 | 419 | self.line_number_table = [] |
paul@137 | 420 | data = data[6:] |
paul@137 | 421 | for i in range(0, self.line_number_table_length): |
paul@137 | 422 | line_number = LineNumberInfo() |
paul@137 | 423 | data = line_number.init(data) |
paul@137 | 424 | self.line_number_table.append(line_number) |
paul@137 | 425 | return data |
paul@137 | 426 | |
paul@137 | 427 | class LocalVariableAttributeInfo(AttributeInfo): |
paul@137 | 428 | def init(self, data, class_file): |
paul@137 | 429 | self.class_file = class_file |
paul@137 | 430 | self.attribute_length = u4(data[0:4]) |
paul@137 | 431 | self.local_variable_table_length = u2(data[4:6]) |
paul@137 | 432 | self.local_variable_table = [] |
paul@137 | 433 | data = data[6:] |
paul@137 | 434 | for i in range(0, self.local_variable_table_length): |
paul@137 | 435 | local_variable = LocalVariableInfo() |
paul@137 | 436 | data = local_variable.init(data, self.class_file) |
paul@137 | 437 | self.local_variable_table.append(local_variable) |
paul@137 | 438 | return data |
paul@137 | 439 | |
paul@137 | 440 | class DeprecatedAttributeInfo(AttributeInfo): |
paul@137 | 441 | pass |
paul@137 | 442 | |
paul@137 | 443 | # Child classes of the attribute information classes. |
paul@137 | 444 | |
paul@137 | 445 | class ExceptionInfo: |
paul@137 | 446 | def init(self, data): |
paul@137 | 447 | self.start_pc = u2(data[0:2]) |
paul@137 | 448 | self.end_pc = u2(data[2:4]) |
paul@137 | 449 | self.handler_pc = u2(data[4:6]) |
paul@137 | 450 | self.catch_type = u2(data[6:8]) |
paul@137 | 451 | return data[8:] |
paul@137 | 452 | |
paul@137 | 453 | class InnerClassInfo(NameUtils): |
paul@137 | 454 | def init(self, data, class_file): |
paul@137 | 455 | self.class_file = class_file |
paul@137 | 456 | self.inner_class_info_index = u2(data[0:2]) |
paul@137 | 457 | self.outer_class_info_index = u2(data[2:4]) |
paul@137 | 458 | # Permit the NameUtils mix-in. |
paul@137 | 459 | self.name_index = self.inner_name_index = u2(data[4:6]) |
paul@137 | 460 | self.inner_class_access_flags = u2(data[6:8]) |
paul@137 | 461 | return data[8:] |
paul@137 | 462 | |
paul@137 | 463 | class LineNumberInfo: |
paul@137 | 464 | def init(self, data): |
paul@137 | 465 | self.start_pc = u2(data[0:2]) |
paul@137 | 466 | self.line_number = u2(data[2:4]) |
paul@137 | 467 | return data[4:] |
paul@137 | 468 | |
paul@137 | 469 | class LocalVariableInfo(NameUtils, PythonNameUtils): |
paul@137 | 470 | def init(self, data, class_file): |
paul@137 | 471 | self.class_file = class_file |
paul@137 | 472 | self.start_pc = u2(data[0:2]) |
paul@137 | 473 | self.length = u2(data[2:4]) |
paul@137 | 474 | self.name_index = u2(data[4:6]) |
paul@137 | 475 | self.descriptor_index = u2(data[6:8]) |
paul@137 | 476 | self.index = u2(data[8:10]) |
paul@137 | 477 | return data[10:] |
paul@137 | 478 | |
paul@137 | 479 | def get_descriptor(self): |
paul@137 | 480 | return get_field_descriptor(unicode(self.class_file.constants[self.descriptor_index - 1])) |
paul@137 | 481 | |
paul@137 | 482 | # Exceptions. |
paul@137 | 483 | |
paul@137 | 484 | class UnknownTag(Exception): |
paul@137 | 485 | pass |
paul@137 | 486 | |
paul@137 | 487 | class UnknownAttribute(Exception): |
paul@137 | 488 | pass |
paul@137 | 489 | |
paul@137 | 490 | # Abstractions for the main structures. |
paul@137 | 491 | |
paul@137 | 492 | class ClassFile: |
paul@137 | 493 | |
paul@137 | 494 | "A class representing a Java class file." |
paul@137 | 495 | |
paul@137 | 496 | def __init__(self, s): |
paul@137 | 497 | |
paul@137 | 498 | """ |
paul@137 | 499 | Process the given string 's', populating the object with the class |
paul@137 | 500 | file's details. |
paul@137 | 501 | """ |
paul@137 | 502 | |
paul@137 | 503 | self.constants, s = self._get_constants(s[8:]) |
paul@137 | 504 | self.access_flags, s = self._get_access_flags(s) |
paul@137 | 505 | self.this_class, s = self._get_this_class(s) |
paul@137 | 506 | self.super_class, s = self._get_super_class(s) |
paul@137 | 507 | self.interfaces, s = self._get_interfaces(s) |
paul@137 | 508 | self.fields, s = self._get_fields(s) |
paul@137 | 509 | self.methods, s = self._get_methods(s) |
paul@137 | 510 | self.attributes, s = self._get_attributes(s) |
paul@137 | 511 | |
paul@137 | 512 | def _decode_const(self, s): |
paul@137 | 513 | tag = u1(s[0:1]) |
paul@137 | 514 | if tag == 1: |
paul@137 | 515 | const = Utf8Info() |
paul@137 | 516 | elif tag == 3: |
paul@137 | 517 | const = IntegerInfo() |
paul@137 | 518 | elif tag == 4: |
paul@137 | 519 | const = FloatInfo() |
paul@137 | 520 | elif tag == 5: |
paul@137 | 521 | const = LongInfo() |
paul@137 | 522 | elif tag == 6: |
paul@137 | 523 | const = DoubleInfo() |
paul@137 | 524 | elif tag == 7: |
paul@137 | 525 | const = ClassInfo() |
paul@137 | 526 | elif tag == 8: |
paul@137 | 527 | const = StringInfo() |
paul@137 | 528 | elif tag == 9: |
paul@137 | 529 | const = FieldRefInfo() |
paul@137 | 530 | elif tag == 10: |
paul@137 | 531 | const = MethodRefInfo() |
paul@137 | 532 | elif tag == 11: |
paul@137 | 533 | const = InterfaceMethodRefInfo() |
paul@137 | 534 | elif tag == 12: |
paul@137 | 535 | const = NameAndTypeInfo() |
paul@137 | 536 | else: |
paul@137 | 537 | raise UnknownTag, tag |
paul@137 | 538 | |
paul@137 | 539 | # Initialise the constant object. |
paul@137 | 540 | |
paul@137 | 541 | s = const.init(s[1:], self) |
paul@137 | 542 | return const, s |
paul@137 | 543 | |
paul@137 | 544 | def _get_constants_from_table(self, count, s): |
paul@137 | 545 | l = [] |
paul@137 | 546 | # Have to skip certain entries specially. |
paul@137 | 547 | i = 1 |
paul@137 | 548 | while i < count: |
paul@137 | 549 | c, s = self._decode_const(s) |
paul@137 | 550 | l.append(c) |
paul@137 | 551 | # Add a blank entry after "large" entries. |
paul@137 | 552 | if isinstance(c, LargeNumInfo): |
paul@137 | 553 | l.append(None) |
paul@137 | 554 | i += 1 |
paul@137 | 555 | i += 1 |
paul@137 | 556 | return l, s |
paul@137 | 557 | |
paul@137 | 558 | def _get_items_from_table(self, cls, number, s): |
paul@137 | 559 | l = [] |
paul@137 | 560 | for i in range(0, number): |
paul@137 | 561 | f = cls() |
paul@137 | 562 | s = f.init(s, self) |
paul@137 | 563 | l.append(f) |
paul@137 | 564 | return l, s |
paul@137 | 565 | |
paul@137 | 566 | def _get_methods_from_table(self, number, s): |
paul@137 | 567 | return self._get_items_from_table(MethodInfo, number, s) |
paul@137 | 568 | |
paul@137 | 569 | def _get_fields_from_table(self, number, s): |
paul@137 | 570 | return self._get_items_from_table(FieldInfo, number, s) |
paul@137 | 571 | |
paul@137 | 572 | def _get_attribute_from_table(self, s): |
paul@137 | 573 | attribute_name_index = u2(s[0:2]) |
paul@137 | 574 | constant_name = self.constants[attribute_name_index - 1].bytes |
paul@137 | 575 | if constant_name == "SourceFile": |
paul@137 | 576 | attribute = SourceFileAttributeInfo() |
paul@137 | 577 | elif constant_name == "ConstantValue": |
paul@137 | 578 | attribute = ConstantValueAttributeInfo() |
paul@137 | 579 | elif constant_name == "Code": |
paul@137 | 580 | attribute = CodeAttributeInfo() |
paul@137 | 581 | elif constant_name == "Exceptions": |
paul@137 | 582 | attribute = ExceptionsAttributeInfo() |
paul@137 | 583 | elif constant_name == "InnerClasses": |
paul@137 | 584 | attribute = InnerClassesAttributeInfo() |
paul@137 | 585 | elif constant_name == "Synthetic": |
paul@137 | 586 | attribute = SyntheticAttributeInfo() |
paul@137 | 587 | elif constant_name == "LineNumberTable": |
paul@137 | 588 | attribute = LineNumberAttributeInfo() |
paul@137 | 589 | elif constant_name == "LocalVariableTable": |
paul@137 | 590 | attribute = LocalVariableAttributeInfo() |
paul@137 | 591 | elif constant_name == "Deprecated": |
paul@137 | 592 | attribute = DeprecatedAttributeInfo() |
paul@137 | 593 | else: |
paul@137 | 594 | raise UnknownAttribute, constant_name |
paul@137 | 595 | s = attribute.init(s[2:], self) |
paul@137 | 596 | return attribute, s |
paul@137 | 597 | |
paul@137 | 598 | def _get_attributes_from_table(self, number, s): |
paul@137 | 599 | attributes = [] |
paul@137 | 600 | for i in range(0, number): |
paul@137 | 601 | attribute, s = self._get_attribute_from_table(s) |
paul@137 | 602 | attributes.append(attribute) |
paul@137 | 603 | return attributes, s |
paul@137 | 604 | |
paul@137 | 605 | def _get_constants(self, s): |
paul@137 | 606 | count = u2(s[0:2]) |
paul@137 | 607 | return self._get_constants_from_table(count, s[2:]) |
paul@137 | 608 | |
paul@137 | 609 | def _get_access_flags(self, s): |
paul@137 | 610 | return u2(s[0:2]), s[2:] |
paul@137 | 611 | |
paul@137 | 612 | def _get_this_class(self, s): |
paul@137 | 613 | index = u2(s[0:2]) |
paul@137 | 614 | return self.constants[index - 1], s[2:] |
paul@137 | 615 | |
paul@137 | 616 | _get_super_class = _get_this_class |
paul@137 | 617 | |
paul@137 | 618 | def _get_interfaces(self, s): |
paul@137 | 619 | interfaces = [] |
paul@137 | 620 | number = u2(s[0:2]) |
paul@137 | 621 | s = s[2:] |
paul@137 | 622 | for i in range(0, number): |
paul@137 | 623 | index = u2(s[0:2]) |
paul@137 | 624 | interfaces.append(self.constants[index - 1]) |
paul@137 | 625 | s = s[2:] |
paul@137 | 626 | return interfaces, s |
paul@137 | 627 | |
paul@137 | 628 | def _get_fields(self, s): |
paul@137 | 629 | number = u2(s[0:2]) |
paul@137 | 630 | return self._get_fields_from_table(number, s[2:]) |
paul@137 | 631 | |
paul@137 | 632 | def _get_attributes(self, s): |
paul@137 | 633 | number = u2(s[0:2]) |
paul@137 | 634 | return self._get_attributes_from_table(number, s[2:]) |
paul@137 | 635 | |
paul@137 | 636 | def _get_methods(self, s): |
paul@137 | 637 | number = u2(s[0:2]) |
paul@137 | 638 | return self._get_methods_from_table(number, s[2:]) |
paul@137 | 639 | |
paul@137 | 640 | if __name__ == "__main__": |
paul@137 | 641 | import sys |
paul@137 | 642 | f = open(sys.argv[1], "rb") |
paul@137 | 643 | c = ClassFile(f.read()) |
paul@137 | 644 | f.close() |
paul@137 | 645 | |
paul@137 | 646 | # vim: tabstop=4 expandtab shiftwidth=4 |