1 #!/usr/bin/env python 2 3 """ 4 Java bytecode conversion. Specification found at the following URL: 5 http://java.sun.com/docs/books/vmspec/2nd-edition/html/Instructions2.doc.html 6 7 NOTE: Synchronized constructs are not actually supported. 8 9 Copyright (C) 2004, 2005, 2006, 2011 Paul Boddie <paul@boddie.org.uk> 10 11 This program is free software; you can redistribute it and/or modify it under 12 the terms of the GNU Lesser General Public License as published by the Free 13 Software Foundation; either version 3 of the License, or (at your option) any 14 later version. 15 16 This program is distributed in the hope that it will be useful, but WITHOUT 17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 19 details. 20 21 You should have received a copy of the GNU Lesser General Public License along 22 with this program. If not, see <http://www.gnu.org/licenses/>. 23 """ 24 25 import classfile 26 from dis import cmp_op, opname # for access to Python bytecode values and operators 27 try: 28 from dis import opmap 29 except ImportError: 30 opmap = {} 31 for i in range(0, len(opname)): 32 opmap[opname[i]] = i 33 from UserDict import UserDict 34 import new 35 36 # Bytecode production classes. 37 38 class BytecodeWriter: 39 40 "A Python bytecode writer." 41 42 def __init__(self): 43 44 "Initialise the writer." 45 46 # A stack of loop start instructions corresponding to loop blocks. 47 self.loops = [] 48 49 # A stack of loop block or exception block start positions. 50 self.blocks = [] 51 52 # A stack of exception block handler pointers. 53 self.exception_handlers = [] 54 55 # A list of exception offset details. 56 self.exception_offsets = [] 57 58 # A dictionary mapping labels to jump instructions referencing such labels. 59 self.jumps = {} 60 61 # The output values, including "lazy" subvalues which will need evaluating. 62 self.output = [] 63 64 # The current Python bytecode instruction position. 65 self.position = 0 66 67 # Stack depth estimation. 68 self.stack_depth = 0 69 self.max_stack_depth = 0 70 71 # Local variable estimation. 72 self.max_locals = 0 73 74 # Mapping from values to indexes. 75 self.constants = {} 76 77 # Mapping from names to indexes. 78 # NOTE: This may be acquired from elsewhere. 79 #self.globals = {} 80 81 # Mapping from names to indexes. 82 self.names = {} 83 84 # A list of constants used as exception handler return addresses. 85 self.constants_for_exceptions = [] 86 87 # A list of external names. 88 self.external_names = [] 89 90 def get_bytecodes(self): 91 92 "Return the list of bytecodes written to the writer." 93 94 output = [] 95 for element in self.output: 96 if isinstance(element, LazySubValue): 97 value = element.value 98 else: 99 value = element 100 output.append(value) 101 return output 102 103 def get_output(self): 104 105 "Return the output of the writer as a string." 106 107 output = [] 108 for value in self.get_bytecodes(): 109 # NOTE: ValueError gets raised for bad values here. 110 output.append(chr(value)) 111 return "".join(output) 112 113 def get_constants(self): 114 115 """ 116 Return a list of constants with ordering significant to the code 117 employing them. 118 """ 119 120 l = self._get_list(self._invert(self.constants)) 121 result = [] 122 for i in l: 123 if isinstance(i, LazyValue): 124 result.append(i.get_value()) 125 else: 126 result.append(i) 127 return result 128 129 #def get_globals(self): 130 # return self._get_list(self._invert(self.globals)) 131 132 def get_names(self): 133 134 """ 135 Return a list of names with ordering significant to the code employing 136 them. 137 """ 138 139 return self._get_list(self._invert(self.names)) 140 141 def _invert(self, d): 142 143 """ 144 Return a new dictionary whose key-to-value mapping is in the inverse of 145 that found in 'd'. 146 """ 147 148 inverted = {} 149 for k, v in d.items(): 150 inverted[v] = k 151 return inverted 152 153 def _get_list(self, d): 154 155 """ 156 Traverse the dictionary 'd' returning a list whose values appear at the 157 position denoted by each value's key in 'd'. 158 """ 159 160 l = [] 161 for i in range(0, len(d.keys())): 162 l.append(d[i]) 163 return l 164 165 # Administrative methods. 166 167 def update_stack_depth(self, change): 168 169 """ 170 Given the stated 'change' in stack depth, update the maximum stack depth 171 where appropriate. 172 """ 173 174 self.stack_depth += change 175 if self.stack_depth > self.max_stack_depth: 176 self.max_stack_depth = self.stack_depth 177 178 def update_locals(self, index): 179 180 """ 181 Given the stated 'index' of a local variable, update the maximum local 182 variable index where appropriate. 183 """ 184 185 if index > self.max_locals: 186 self.max_locals = index 187 188 # Special methods. 189 190 def _write_value(self, value): 191 192 """ 193 Write the given 'value' at the current output position. 194 """ 195 196 if isinstance(value, LazyValue): 197 # NOTE: Assume a 16-bit value. 198 self.output.append(value.values[0]) 199 self.output.append(value.values[1]) 200 self.position += 2 201 elif value <= 0xffff: 202 self.output.append(value & 0xff) 203 self.output.append((value & 0xff00) >> 8) 204 self.position += 2 205 else: 206 # NOTE: EXTENDED_ARG not yet supported. 207 raise ValueError, value 208 209 def _rewrite_value(self, position, value): 210 211 """ 212 At the given output 'position', rewrite the given 'value'. 213 """ 214 215 # NOTE: Assume a 16-bit value. 216 if value <= 0xffff: 217 self.output[position] = (value & 0xff) 218 self.output[position + 1] = ((value & 0xff00) >> 8) 219 else: 220 # NOTE: EXTENDED_ARG not yet supported. 221 raise ValueError, value 222 223 # Higher level methods. 224 225 def use_external_name(self, name): 226 # NOTE: Remove array and object indicators. 227 self.external_names.append(name) 228 229 def setup_loop(self): 230 self.loops.append(self.position) 231 self.output.append(opmap["SETUP_LOOP"]) 232 self.position += 1 233 self._write_value(0) # To be filled in later 234 235 def end_loop(self): 236 current_loop_start = self.loops.pop() 237 current_loop_real_start = self.blocks.pop() 238 #print "<", self.blocks, current_loop_real_start 239 # Fix the iterator delta. 240 # NOTE: Using 3 as the assumed length of the FOR_ITER instruction. 241 self.jump_absolute(current_loop_real_start) 242 self._rewrite_value(current_loop_real_start + 1, self.position - current_loop_real_start - 3) 243 self.pop_block() 244 # Fix the loop delta. 245 # NOTE: Using 3 as the assumed length of the SETUP_LOOP instruction. 246 self._rewrite_value(current_loop_start + 1, self.position - current_loop_start - 3) 247 248 def jump_to_label(self, status, name): 249 # Record the instruction using the jump. 250 jump_instruction = self.position 251 if status is None: 252 self.jump_forward() 253 elif status: 254 self.jump_if_true() 255 else: 256 self.jump_if_false() 257 # Record the following instruction, too. 258 if not self.jumps.has_key(name): 259 self.jumps[name] = [] 260 self.jumps[name].append((jump_instruction, self.position)) 261 262 def start_label(self, name): 263 # Fill in all jump instructions. 264 for jump_instruction, following_instruction in self.jumps[name]: 265 self._rewrite_value(jump_instruction + 1, self.position - following_instruction) 266 del self.jumps[name] 267 268 def load_const_ret(self, value): 269 self.constants_for_exceptions.append(value) 270 self.load_const(value) 271 272 def ret(self, index): 273 self.load_fast(index) 274 275 # Previously, the constant stored on the stack by jsr/jsr_w was stored 276 # in a local variable. In the JVM, extracting the value from the local 277 # variable and jumping can be done at runtime. In the Python VM, any 278 # jump target must be known in advance and written into the bytecode. 279 280 for constant in self.constants_for_exceptions: 281 self.dup_top() # Stack: actual-address, actual-address 282 self.load_const(constant) # Stack: actual-address, actual-address, suggested-address 283 self.compare_op("==") # Stack: actual-address, result 284 self.jump_to_label(0, "const") 285 self.pop_top() # Stack: actual-address 286 self.pop_top() # Stack: 287 self.jump_absolute(constant) 288 self.start_label("const") 289 self.pop_top() # Stack: actual-address 290 291 # NOTE: If we get here, something is really wrong. 292 293 self.pop_top() # Stack: 294 295 def setup_except(self, target): 296 self.blocks.append(self.position) 297 self.exception_handlers.append(target) 298 #print "-", self.position, target 299 self.output.append(opmap["SETUP_EXCEPT"]) 300 self.position += 1 301 self._write_value(0) # To be filled in later 302 303 def setup_finally(self, target): 304 self.blocks.append(self.position) 305 self.exception_handlers.append(target) 306 #print "-", self.position, target 307 self.output.append(opmap["SETUP_FINALLY"]) 308 self.position += 1 309 self._write_value(0) # To be filled in later 310 311 def end_exception(self): 312 current_exception_start = self.blocks.pop() 313 # Convert the "lazy" absolute value. 314 current_exception_target = self.exception_handlers.pop() 315 # NOTE: Using 3 as the assumed length of the SETUP_* instruction. 316 self.exception_offsets.append((current_exception_start + 1, current_exception_target, current_exception_start)) 317 318 def end_exceptions(self): 319 for position, exception_target, exception_start in self.exception_offsets: 320 #print "*", exception_start, exception_target.get_value() 321 self._rewrite_value(position, exception_target.get_value() - exception_start - 3) 322 323 def start_handler(self, exc_name, class_file): 324 325 # Where handlers are begun, produce bytecode to test the type of 326 # the exception. 327 # NOTE: Since RAISE_VARARGS and END_FINALLY are not really documented, 328 # NOTE: we store the top of the stack and use it later to trigger the 329 # NOTE: magic processes when re-raising. 330 self.use_external_name(str(exc_name)) 331 332 self.rot_two() # Stack: raised-exception, exception 333 self.dup_top() # Stack: raised-exception, exception, exception 334 # Handled exceptions are wrapped before being thrown. 335 self.load_global("Exception") # Stack: raised-exception, exception, exception, Exception 336 self.compare_op("exception match") # Stack: raised-exception, exception, result 337 self.jump_to_label(0, "next") 338 self.pop_top() # Stack: raised-exception, exception 339 self.dup_top() # Stack: raised-exception, exception, exception 340 self.load_attr("args") # Stack: raised-exception, exception, args 341 self.load_const(0) # Stack: raised-exception, exception, args, 0 342 self.binary_subscr() # Stack: raised-exception, exception, exception-object 343 load_class_name(class_file, str(exc_name), self) 344 # Stack: raised-exception, exception, exception-object, handled-exception 345 self.load_global("isinstance") # Stack: raised-exception, exception, exception-object, handled-exception, isinstance 346 self.rot_three() # Stack: raised-exception, exception, isinstance, exception-object, handled-exception 347 self.call_function(2) # Stack: raised-exception, exception, result 348 self.jump_to_label(1, "handler") 349 self.start_label("next") 350 self.pop_top() # Stack: raised-exception, exception 351 self.rot_two() # Stack: exception, raised-exception 352 self.end_finally() 353 self.start_label("handler") 354 self.pop_top() # Stack: raised-exception, exception 355 356 # Complicated methods. 357 358 def load_const(self, value): 359 self.output.append(opmap["LOAD_CONST"]) 360 if not self.constants.has_key(value): 361 self.constants[value] = len(self.constants.keys()) 362 self.position += 1 363 self._write_value(self.constants[value]) 364 self.update_stack_depth(1) 365 366 def load_global(self, name): 367 self.output.append(opmap["LOAD_GLOBAL"]) 368 if not self.names.has_key(name): 369 self.names[name] = len(self.names.keys()) 370 self.position += 1 371 self._write_value(self.names[name]) 372 self.update_stack_depth(1) 373 374 def load_attr(self, name): 375 self.output.append(opmap["LOAD_ATTR"]) 376 if not self.names.has_key(name): 377 self.names[name] = len(self.names.keys()) 378 self.position += 1 379 self._write_value(self.names[name]) 380 381 def load_name(self, name): 382 self.output.append(opmap["LOAD_NAME"]) 383 if not self.names.has_key(name): 384 self.names[name] = len(self.names.keys()) 385 self.position += 1 386 self._write_value(self.names[name]) 387 self.update_stack_depth(1) 388 389 def load_fast(self, index): 390 self.output.append(opmap["LOAD_FAST"]) 391 self.position += 1 392 self._write_value(index) 393 self.update_stack_depth(1) 394 self.update_locals(index) 395 396 def store_attr(self, name): 397 self.output.append(opmap["STORE_ATTR"]) 398 if not self.names.has_key(name): 399 self.names[name] = len(self.names.keys()) 400 self.position += 1 401 self._write_value(self.names[name]) 402 self.update_stack_depth(-1) 403 404 def store_fast(self, index): 405 self.output.append(opmap["STORE_FAST"]) 406 self.position += 1 407 self._write_value(index) 408 self.update_stack_depth(-1) 409 self.update_locals(index) 410 411 def for_iter(self): 412 self.blocks.append(self.position) 413 #print ">", self.blocks 414 self.output.append(opmap["FOR_ITER"]) 415 self.position += 1 416 self._write_value(0) # To be filled in later 417 self.update_stack_depth(1) 418 419 def break_loop(self): 420 self.output.append(opmap["BREAK_LOOP"]) 421 self.position += 1 422 self.jump_absolute(self.blocks[-1]) 423 424 # Normal bytecode generators. 425 426 def get_iter(self): 427 self.output.append(opmap["GET_ITER"]) 428 self.position += 1 429 430 def jump_if_false(self, offset=0): 431 self.output.append(opmap["JUMP_IF_FALSE"]) 432 self.position += 1 433 self._write_value(offset) # May be filled in later 434 435 def jump_if_true(self, offset=0): 436 self.output.append(opmap["JUMP_IF_TRUE"]) 437 self.position += 1 438 self._write_value(offset) # May be filled in later 439 440 def jump_forward(self, offset=0): 441 self.output.append(opmap["JUMP_FORWARD"]) 442 self.position += 1 443 self._write_value(offset) # May be filled in later 444 445 def jump_absolute(self, address=0): 446 self.output.append(opmap["JUMP_ABSOLUTE"]) 447 self.position += 1 448 self._write_value(address) # May be filled in later 449 450 def build_tuple(self, count): 451 self.output.append(opmap["BUILD_TUPLE"]) 452 self.position += 1 453 self._write_value(count) 454 self.update_stack_depth(-(count - 1)) 455 456 def build_list(self, count): 457 self.output.append(opmap["BUILD_LIST"]) 458 self.position += 1 459 self._write_value(count) 460 self.update_stack_depth(-(count - 1)) 461 462 def pop_top(self): 463 self.output.append(opmap["POP_TOP"]) 464 self.position += 1 465 self.update_stack_depth(-1) 466 467 def dup_top(self): 468 self.output.append(opmap["DUP_TOP"]) 469 self.position += 1 470 self.update_stack_depth(1) 471 472 def dup_topx(self, count): 473 self.output.append(opmap["DUP_TOPX"]) 474 self.position += 1 475 self._write_value(count) 476 self.update_stack_depth(count) 477 478 def rot_two(self): 479 self.output.append(opmap["ROT_TWO"]) 480 self.position += 1 481 482 def rot_three(self): 483 self.output.append(opmap["ROT_THREE"]) 484 self.position += 1 485 486 def rot_four(self): 487 self.output.append(opmap["ROT_FOUR"]) 488 self.position += 1 489 490 def call_function(self, count): 491 self.output.append(opmap["CALL_FUNCTION"]) 492 self.position += 1 493 self._write_value(count) 494 self.update_stack_depth(-count) 495 496 def call_function_var(self, count): 497 self.output.append(opmap["CALL_FUNCTION_VAR"]) 498 self.position += 1 499 self._write_value(count) 500 self.update_stack_depth(-count-1) 501 502 def binary_subscr(self): 503 self.output.append(opmap["BINARY_SUBSCR"]) 504 self.position += 1 505 self.update_stack_depth(-1) 506 507 def binary_add(self): 508 self.output.append(opmap["BINARY_ADD"]) 509 self.position += 1 510 self.update_stack_depth(-1) 511 512 def binary_divide(self): 513 self.output.append(opmap["BINARY_DIVIDE"]) 514 self.position += 1 515 self.update_stack_depth(-1) 516 517 def binary_multiply(self): 518 self.output.append(opmap["BINARY_MULTIPLY"]) 519 self.position += 1 520 self.update_stack_depth(-1) 521 522 def binary_modulo(self): 523 self.output.append(opmap["BINARY_MODULO"]) 524 self.position += 1 525 self.update_stack_depth(-1) 526 527 def binary_subtract(self): 528 self.output.append(opmap["BINARY_SUBTRACT"]) 529 self.position += 1 530 self.update_stack_depth(-1) 531 532 def binary_and(self): 533 self.output.append(opmap["BINARY_AND"]) 534 self.position += 1 535 self.update_stack_depth(-1) 536 537 def binary_or(self): 538 self.output.append(opmap["BINARY_XOR"]) 539 self.position += 1 540 self.update_stack_depth(-1) 541 542 def binary_lshift(self): 543 self.output.append(opmap["BINARY_LSHIFT"]) 544 self.position += 1 545 self.update_stack_depth(-1) 546 547 def binary_rshift(self): 548 self.output.append(opmap["BINARY_RSHIFT"]) 549 self.position += 1 550 self.update_stack_depth(-1) 551 552 def binary_xor(self): 553 self.output.append(opmap["BINARY_XOR"]) 554 self.position += 1 555 self.update_stack_depth(-1) 556 557 def store_subscr(self): 558 self.output.append(opmap["STORE_SUBSCR"]) 559 self.position += 1 560 self.update_stack_depth(-3) 561 562 def unary_negative(self): 563 self.output.append(opmap["UNARY_NEGATIVE"]) 564 self.position += 1 565 566 def slice_0(self): 567 self.output.append(opmap["SLICE+0"]) 568 self.position += 1 569 570 def slice_1(self): 571 self.output.append(opmap["SLICE+1"]) 572 self.position += 1 573 574 def compare_op(self, op): 575 self.output.append(opmap["COMPARE_OP"]) 576 self.position += 1 577 self._write_value(list(cmp_op).index(op)) 578 self.update_stack_depth(-1) 579 580 def return_value(self): 581 self.output.append(opmap["RETURN_VALUE"]) 582 self.position += 1 583 self.update_stack_depth(-1) 584 585 def raise_varargs(self, count): 586 self.output.append(opmap["RAISE_VARARGS"]) 587 self.position += 1 588 self._write_value(count) 589 590 def pop_block(self): 591 self.output.append(opmap["POP_BLOCK"]) 592 self.position += 1 593 594 def end_finally(self): 595 self.output.append(opmap["END_FINALLY"]) 596 self.position += 1 597 598 def unpack_sequence(self, count): 599 self.output.append(opmap["UNPACK_SEQUENCE"]) 600 self.position += 1 601 self._write_value(count) 602 603 # Debugging. 604 605 def print_item(self): 606 self.output.append(opmap["PRINT_ITEM"]) 607 self.position += 1 608 609 # Utility classes and functions. 610 611 class LazyDict(UserDict): 612 def __getitem__(self, key): 613 if not self.data.has_key(key): 614 # NOTE: Assume 16-bit value. 615 self.data[key] = LazyValue(2) 616 return self.data[key] 617 def __setitem__(self, key, value): 618 if self.data.has_key(key): 619 existing_value = self.data[key] 620 if isinstance(existing_value, LazyValue): 621 existing_value.set_value(value) 622 return 623 self.data[key] = value 624 625 class LazyValue: 626 def __init__(self, nvalues): 627 self.values = [] 628 for i in range(0, nvalues): 629 self.values.append(LazySubValue()) 630 def set_value(self, value): 631 # NOTE: Assume at least 16-bit value. No "filling" performed. 632 if value <= 0xffff: 633 self.values[0].set_value(value & 0xff) 634 self.values[1].set_value((value & 0xff00) >> 8) 635 else: 636 # NOTE: EXTENDED_ARG not yet supported. 637 raise ValueError, value 638 def get_value(self): 639 value = 0 640 values = self.values[:] 641 for i in range(0, len(values)): 642 value = (value << 8) + values.pop().value 643 return value 644 645 class LazySubValue: 646 def __init__(self): 647 self.value = 0 648 def set_value(self, value): 649 self.value = value 650 651 def signed(value, limit): 652 653 """ 654 Return the signed integer from the unsigned 'value', where 'limit' (a value 655 one greater than the highest possible positive integer) is used to determine 656 whether a negative or positive result is produced. 657 """ 658 659 d, r = divmod(value, limit) 660 if d == 1: 661 mask = limit * 2 - 1 662 return -1 - (value ^ mask) 663 else: 664 return value 665 666 def signed1(value): 667 return signed(value, 0x80) 668 669 def signed2(value): 670 return signed(value, 0x8000) 671 672 def signed4(value): 673 return signed(value, 0x80000000) 674 675 def load_class_name(class_file, full_class_name, program): 676 this_class_name = str(class_file.this_class.get_python_name()) 677 this_class_parts = this_class_name.split(".") 678 class_parts = full_class_name.split(".") 679 680 # Only use the full path if different from this class's path. 681 682 if class_parts[:-1] != this_class_parts[:-1]: 683 program.use_external_name(full_class_name) 684 program.load_global(class_parts[0]) 685 for class_part in class_parts[1:]: 686 program.load_attr(class_part) # Stack: classref 687 else: 688 program.load_global(class_parts[-1]) 689 690 atypes_to_default_values = { 691 4 : 0, # bool (NOTE: Should be False.) 692 5 : u"", # char 693 6 : 0.0, # float 694 7 : 0.0, # double 695 8 : 0, # byte 696 9 : 0, # short 697 10: 0, # int 698 11: 0 # long 699 } 700 701 def get_default_for_atype(atype): 702 global atypes_to_default_values 703 return atypes_to_default_values.get(atype) 704 705 # Bytecode conversion. 706 707 class BytecodeReader: 708 709 "A generic Java bytecode reader." 710 711 def __init__(self, class_file): 712 713 """ 714 Initialise the reader with a 'class_file' containing essential 715 information for any bytecode inspection activity. 716 """ 717 718 self.class_file = class_file 719 self.position_mapping = LazyDict() 720 721 def process(self, method, program): 722 723 """ 724 Process the given 'method' (obtained from the class file), using the 725 given 'program' to write translated Python bytecode instructions. 726 """ 727 728 self.java_position = 0 729 self.in_finally = 0 730 self.method = method 731 732 # NOTE: Potentially unreliable way of getting necessary information. 733 734 code, exception_table = None, None 735 for attribute in method.attributes: 736 if isinstance(attribute, classfile.CodeAttributeInfo): 737 code, exception_table = attribute.code, attribute.exception_table 738 break 739 740 # Where no code was found, write a very simple placeholder routine. 741 # This is useful for interfaces and abstract classes. 742 # NOTE: Assess the correctness of doing this. An exception should really 743 # NOTE: be raised instead. 744 745 if code is None: 746 program.load_const(None) 747 program.return_value() 748 return 749 750 # Produce a structure which permits fast access to exception details. 751 752 exception_block_start = {} 753 exception_block_end = {} 754 exception_block_handler = {} 755 reversed_exception_table = exception_table[:] 756 reversed_exception_table.reverse() 757 758 # Later entries have wider coverage than earlier entries. 759 760 for exception in reversed_exception_table: 761 762 # NOTE: Strange case with javac from JDK 1.4 but not JDK 1.3: 763 # NOTE: start_pc == handler_pc 764 # Merge all finally handlers with the same handler location. 765 766 if exception.catch_type == 0 and exception_block_handler.get(exception.handler_pc, []) != []: 767 768 # Make a new definition. 769 770 new_exception = classfile.ExceptionInfo() 771 new_exception.catch_type = exception.catch_type 772 new_exception.handler_pc = exception.handler_pc 773 new_exception.end_pc = exception.end_pc 774 new_exception.start_pc = exception.start_pc 775 776 # Find the previous exception handler definition. 777 778 for previous_exception in exception_block_handler[exception.handler_pc][:]: 779 if previous_exception.catch_type == 0: 780 new_exception.end_pc = max(new_exception.end_pc, previous_exception.end_pc) 781 new_exception.start_pc = min(new_exception.start_pc, previous_exception.start_pc) 782 783 # Remove this exception from the lists. 784 785 exception_block_handler[previous_exception.handler_pc].remove(previous_exception) 786 exception_block_start[previous_exception.start_pc].remove(previous_exception) 787 exception_block_end[previous_exception.end_pc].remove(previous_exception) 788 break 789 790 # Use the new definition instead. 791 792 exception = new_exception 793 794 # Index start positions. 795 796 if not exception_block_start.has_key(exception.start_pc): 797 exception_block_start[exception.start_pc] = [] 798 exception_block_start[exception.start_pc].append(exception) 799 800 # Index end positions. 801 802 if not exception_block_end.has_key(exception.end_pc): 803 exception_block_end[exception.end_pc] = [] 804 exception_block_end[exception.end_pc].append(exception) 805 806 # Index handler positions. 807 808 if not exception_block_handler.has_key(exception.handler_pc): 809 exception_block_handler[exception.handler_pc] = [] 810 exception_block_handler[exception.handler_pc].append(exception) 811 812 # Process each instruction in the code. 813 814 while self.java_position < len(code): 815 self.position_mapping[self.java_position] = program.position 816 817 # Insert exception handling constructs. 818 819 block_starts = exception_block_start.get(self.java_position, []) 820 for exception in block_starts: 821 822 # Note that the absolute position is used. 823 824 if exception.catch_type == 0: 825 program.setup_finally(self.position_mapping[exception.handler_pc]) 826 else: 827 program.setup_except(self.position_mapping[exception.handler_pc]) 828 829 if block_starts: 830 self.in_finally = 0 831 832 # Insert exception handler details. 833 # NOTE: Ensure that pop_block is reachable by possibly inserting it at the start of finally handlers. 834 # NOTE: Insert a check for the correct exception at the start of each handler. 835 836 for exception in exception_block_handler.get(self.java_position, []): 837 program.end_exception() 838 if exception.catch_type == 0: 839 self.in_finally = 1 840 else: 841 program.start_handler(self.class_file.constants[exception.catch_type - 1].get_python_name(), self.class_file) 842 843 # Process the bytecode at the current position. 844 845 bytecode = ord(code[self.java_position]) 846 mnemonic, number_of_arguments = self.java_bytecodes[bytecode] 847 number_of_arguments = self.process_bytecode(mnemonic, number_of_arguments, code, program) 848 next_java_position = self.java_position + 1 + number_of_arguments 849 850 # Insert exception block end details. 851 852 for exception in exception_block_end.get(next_java_position, []): 853 854 # NOTE: Insert jump beyond handlers. 855 # NOTE: program.jump_forward/absolute(...) 856 # NOTE: Insert end finally at end of handlers as well as where "ret" occurs. 857 858 if exception.catch_type != 0: 859 program.pop_block() 860 861 # Only advance the JVM position after sneaking in extra Python 862 # instructions. 863 864 self.java_position = next_java_position 865 866 # Tidy up exceptions. 867 868 program.end_exceptions() 869 870 def process_bytecode(self, mnemonic, number_of_arguments, code, program): 871 872 """ 873 Process a bytecode instruction with the given 'mnemonic' and 874 'number_of_arguments'. The 'code' parameter contains the full method 875 code so that argument data can be inspected. The 'program' parameter is 876 used to produce a Python translation of the instruction. 877 """ 878 879 if number_of_arguments is not None: 880 arguments = [ord(b) for b in code[self.java_position + 1:self.java_position + 1 + number_of_arguments]] 881 882 # Call the handler. 883 884 getattr(self, mnemonic)(arguments, program) 885 return number_of_arguments 886 else: 887 # Call the handler. 888 889 return getattr(self, mnemonic)(code[self.java_position+1:], program) 890 891 java_bytecodes = { 892 # code : (mnemonic, number of following bytes, change in stack) 893 0 : ("nop", 0), 894 1 : ("aconst_null", 0), 895 2 : ("iconst_m1", 0), 896 3 : ("iconst_0", 0), 897 4 : ("iconst_1", 0), 898 5 : ("iconst_2", 0), 899 6 : ("iconst_3", 0), 900 7 : ("iconst_4", 0), 901 8 : ("iconst_5", 0), 902 9 : ("lconst_0", 0), 903 10 : ("lconst_1", 0), 904 11 : ("fconst_0", 0), 905 12 : ("fconst_1", 0), 906 13 : ("fconst_2", 0), 907 14 : ("dconst_0", 0), 908 15 : ("dconst_1", 0), 909 16 : ("bipush", 1), 910 17 : ("sipush", 2), 911 18 : ("ldc", 1), 912 19 : ("ldc_w", 2), 913 20 : ("ldc2_w", 2), 914 21 : ("iload", 1), 915 22 : ("lload", 1), 916 23 : ("fload", 1), 917 24 : ("dload", 1), 918 25 : ("aload", 1), 919 26 : ("iload_0", 0), 920 27 : ("iload_1", 0), 921 28 : ("iload_2", 0), 922 29 : ("iload_3", 0), 923 30 : ("lload_0", 0), 924 31 : ("lload_1", 0), 925 32 : ("lload_2", 0), 926 33 : ("lload_3", 0), 927 34 : ("fload_0", 0), 928 35 : ("fload_1", 0), 929 36 : ("fload_2", 0), 930 37 : ("fload_3", 0), 931 38 : ("dload_0", 0), 932 39 : ("dload_1", 0), 933 40 : ("dload_2", 0), 934 41 : ("dload_3", 0), 935 42 : ("aload_0", 0), 936 43 : ("aload_1", 0), 937 44 : ("aload_2", 0), 938 45 : ("aload_3", 0), 939 46 : ("iaload", 0), 940 47 : ("laload", 0), 941 48 : ("faload", 0), 942 49 : ("daload", 0), 943 50 : ("aaload", 0), 944 51 : ("baload", 0), 945 52 : ("caload", 0), 946 53 : ("saload", 0), 947 54 : ("istore", 1), 948 55 : ("lstore", 1), 949 56 : ("fstore", 1), 950 57 : ("dstore", 1), 951 58 : ("astore", 1), 952 59 : ("istore_0", 0), 953 60 : ("istore_1", 0), 954 61 : ("istore_2", 0), 955 62 : ("istore_3", 0), 956 63 : ("lstore_0", 0), 957 64 : ("lstore_1", 0), 958 65 : ("lstore_2", 0), 959 66 : ("lstore_3", 0), 960 67 : ("fstore_0", 0), 961 68 : ("fstore_1", 0), 962 69 : ("fstore_2", 0), 963 70 : ("fstore_3", 0), 964 71 : ("dstore_0", 0), 965 72 : ("dstore_1", 0), 966 73 : ("dstore_2", 0), 967 74 : ("dstore_3", 0), 968 75 : ("astore_0", 0), 969 76 : ("astore_1", 0), 970 77 : ("astore_2", 0), 971 78 : ("astore_3", 0), 972 79 : ("iastore", 0), 973 80 : ("lastore", 0), 974 81 : ("fastore", 0), 975 82 : ("dastore", 0), 976 83 : ("aastore", 0), 977 84 : ("bastore", 0), 978 85 : ("castore", 0), 979 86 : ("sastore", 0), 980 87 : ("pop", 0), 981 88 : ("pop2", 0), 982 89 : ("dup", 0), 983 90 : ("dup_x1", 0), 984 91 : ("dup_x2", 0), 985 92 : ("dup2", 0), 986 93 : ("dup2_x1", 0), 987 94 : ("dup2_x2", 0), 988 95 : ("swap", 0), 989 96 : ("iadd", 0), 990 97 : ("ladd", 0), 991 98 : ("fadd", 0), 992 99 : ("dadd", 0), 993 100 : ("isub", 0), 994 101 : ("lsub", 0), 995 102 : ("fsub", 0), 996 103 : ("dsub", 0), 997 104 : ("imul", 0), 998 105 : ("lmul", 0), 999 106 : ("fmul", 0), 1000 107 : ("dmul", 0), 1001 108 : ("idiv", 0), 1002 109 : ("ldiv", 0), 1003 110 : ("fdiv", 0), 1004 111 : ("ddiv", 0), 1005 112 : ("irem", 0), 1006 113 : ("lrem", 0), 1007 114 : ("frem", 0), 1008 115 : ("drem", 0), 1009 116 : ("ineg", 0), 1010 117 : ("lneg", 0), 1011 118 : ("fneg", 0), 1012 119 : ("dneg", 0), 1013 120 : ("ishl", 0), 1014 121 : ("lshl", 0), 1015 122 : ("ishr", 0), 1016 123 : ("lshr", 0), 1017 124 : ("iushr", 0), 1018 125 : ("lushr", 0), 1019 126 : ("iand", 0), 1020 127 : ("land", 0), 1021 128 : ("ior", 0), 1022 129 : ("lor", 0), 1023 130 : ("ixor", 0), 1024 131 : ("lxor", 0), 1025 132 : ("iinc", 2), 1026 133 : ("i2l", 0), 1027 134 : ("i2f", 0), 1028 135 : ("i2d", 0), 1029 136 : ("l2i", 0), 1030 137 : ("l2f", 0), 1031 138 : ("l2d", 0), 1032 139 : ("f2i", 0), 1033 140 : ("f2l", 0), 1034 141 : ("f2d", 0), 1035 142 : ("d2i", 0), 1036 143 : ("d2l", 0), 1037 144 : ("d2f", 0), 1038 145 : ("i2b", 0), 1039 146 : ("i2c", 0), 1040 147 : ("i2s", 0), 1041 148 : ("lcmp", 0), 1042 149 : ("fcmpl", 0), 1043 150 : ("fcmpg", 0), 1044 151 : ("dcmpl", 0), 1045 152 : ("dcmpg", 0), 1046 153 : ("ifeq", 2), 1047 154 : ("ifne", 2), 1048 155 : ("iflt", 2), 1049 156 : ("ifge", 2), 1050 157 : ("ifgt", 2), 1051 158 : ("ifle", 2), 1052 159 : ("if_icmpeq", 2), 1053 160 : ("if_icmpne", 2), 1054 161 : ("if_icmplt", 2), 1055 162 : ("if_icmpge", 2), 1056 163 : ("if_icmpgt", 2), 1057 164 : ("if_icmple", 2), 1058 165 : ("if_acmpeq", 2), 1059 166 : ("if_acmpne", 2), 1060 167 : ("goto", 2), 1061 168 : ("jsr", 2), 1062 169 : ("ret", 1), 1063 170 : ("tableswitch", None), # variable number of arguments 1064 171 : ("lookupswitch", None), # variable number of arguments 1065 172 : ("ireturn", 0), 1066 173 : ("lreturn", 0), 1067 174 : ("freturn", 0), 1068 175 : ("dreturn", 0), 1069 176 : ("areturn", 0), 1070 177 : ("return_", 0), 1071 178 : ("getstatic", 2), 1072 179 : ("putstatic", 2), 1073 180 : ("getfield", 2), 1074 181 : ("putfield", 2), 1075 182 : ("invokevirtual", 2), 1076 183 : ("invokespecial", 2), 1077 184 : ("invokestatic", 2), 1078 185 : ("invokeinterface", 4), 1079 187 : ("new", 2), 1080 188 : ("newarray", 1), 1081 189 : ("anewarray", 2), 1082 190 : ("arraylength", 0), 1083 191 : ("athrow", 0), 1084 192 : ("checkcast", 2), 1085 193 : ("instanceof", 2), 1086 194 : ("monitorenter", 0), 1087 195 : ("monitorexit", 0), 1088 196 : ("wide", None), # 3 or 5 arguments, stack changes according to modified element 1089 197 : ("multianewarray", 3), 1090 198 : ("ifnull", 2), 1091 199 : ("ifnonnull", 2), 1092 200 : ("goto_w", 4), 1093 201 : ("jsr_w", 4), 1094 } 1095 1096 class BytecodeDisassembler(BytecodeReader): 1097 1098 "A Java bytecode disassembler." 1099 1100 bytecode_methods = [spec[0] for spec in BytecodeReader.java_bytecodes.values()] 1101 1102 def __getattr__(self, name): 1103 if name in self.bytecode_methods: 1104 print "%5s %s" % (self.java_position, name), 1105 return self.generic 1106 else: 1107 raise AttributeError, name 1108 1109 def generic(self, arguments, program): 1110 print arguments 1111 1112 def lookupswitch(self, code, program): 1113 print "%5s lookupswitch" % (self.java_position,), 1114 d, r = divmod(self.java_position + 1, 4) 1115 to_boundary = (4 - r) % 4 1116 code = code[to_boundary:] 1117 default = classfile.s4(code[0:4]) 1118 npairs = classfile.s4(code[4:8]) 1119 print default, npairs 1120 return to_boundary + 8 + npairs * 8 1121 1122 def tableswitch(self, code, program): 1123 print "%5s tableswitch" % (self.java_position,), 1124 d, r = divmod(self.java_position + 1, 4) 1125 to_boundary = (4 - r) % 4 1126 code = code[to_boundary:] 1127 default = classfile.s4(code[0:4]) 1128 low = classfile.s4(code[4:8]) 1129 high = classfile.s4(code[8:12]) 1130 print default, low, high 1131 return to_boundary + 12 + (high - low + 1) * 4 1132 1133 class BytecodeDisassemblerProgram: 1134 position = 0 1135 def setup_except(self, target): 1136 print "(setup_except %s)" % target 1137 def setup_finally(self, target): 1138 print "(setup_finally %s)" % target 1139 def end_exception(self): 1140 print "(end_exception)" 1141 def end_exceptions(self): 1142 print "(end_exceptions)" 1143 def start_handler(self, exc_name, class_file): 1144 print "(start_handler %s)" % exc_name 1145 def pop_block(self): 1146 print "(pop_block)" 1147 def load_const(self, const): 1148 print "(load_const %s)" % const 1149 def return_value(self): 1150 print "(return_value)" 1151 1152 class BytecodeTranslator(BytecodeReader): 1153 1154 "A Java bytecode translator which uses a Python bytecode writer." 1155 1156 def aaload(self, arguments, program): 1157 # NOTE: No type checking performed. 1158 program.binary_subscr() 1159 1160 def aastore(self, arguments, program): 1161 # NOTE: No type checking performed. 1162 # Stack: arrayref, index, value 1163 program.rot_three() # Stack: value, arrayref, index 1164 program.store_subscr() 1165 1166 def aconst_null(self, arguments, program): 1167 program.load_const(None) 1168 1169 def aload(self, arguments, program): 1170 program.load_fast(arguments[0]) 1171 1172 def aload_0(self, arguments, program): 1173 program.load_fast(0) 1174 1175 def aload_1(self, arguments, program): 1176 program.load_fast(1) 1177 1178 def aload_2(self, arguments, program): 1179 program.load_fast(2) 1180 1181 def aload_3(self, arguments, program): 1182 program.load_fast(3) 1183 1184 def anewarray(self, arguments, program): 1185 # NOTE: Does not raise NegativeArraySizeException. 1186 # NOTE: Not using the index to type the list/array. 1187 index = (arguments[0] << 8) + arguments[1] 1188 type_name = self.class_file.constants[index - 1].get_python_name() 1189 default_value = classfile.get_default_for_type(type_name) 1190 self._newarray(program, type_name) 1191 1192 def _newarray(self, program, default_value): 1193 program.build_list(0) # Stack: count, list 1194 program.rot_two() # Stack: list, count 1195 program.setup_loop() 1196 program.load_global("range") 1197 program.load_const(0) # Stack: list, count, range, 0 1198 program.rot_three() # Stack: list, 0, count, range 1199 program.rot_three() # Stack: list, range, 0, count 1200 program.call_function(2) # Stack: list, range_list 1201 program.get_iter() # Stack: list, iter 1202 program.for_iter() # Stack: list, iter, value 1203 program.pop_top() # Stack: list, iter 1204 program.rot_two() # Stack: iter, list 1205 program.dup_top() # Stack: iter, list, list 1206 program.load_attr("append") # Stack: iter, list, append 1207 program.load_const(default_value) # Stack: iter, list, append, default 1208 program.call_function(1) # Stack: iter, list, default 1209 program.pop_top() # Stack: iter, list 1210 program.rot_two() # Stack: list, iter 1211 program.end_loop() # Back to for_iter above 1212 1213 def areturn(self, arguments, program): 1214 program.return_value() 1215 1216 def arraylength(self, arguments, program): 1217 program.load_global("len") # Stack: arrayref, len 1218 program.rot_two() # Stack: len, arrayref 1219 program.call_function(1) 1220 1221 def astore(self, arguments, program): 1222 program.store_fast(arguments[0]) 1223 1224 def astore_0(self, arguments, program): 1225 program.store_fast(0) 1226 1227 def astore_1(self, arguments, program): 1228 program.store_fast(1) 1229 1230 def astore_2(self, arguments, program): 1231 program.store_fast(2) 1232 1233 def astore_3(self, arguments, program): 1234 program.store_fast(3) 1235 1236 def athrow(self, arguments, program): 1237 # NOTE: NullPointerException not raised where null/None is found on the stack. 1238 # If this instruction appears in a finally handler, use end_finally instead. 1239 if self.in_finally: 1240 program.end_finally() 1241 else: 1242 # Wrap the exception in a Python exception. 1243 program.load_global("Exception") # Stack: objectref, Exception 1244 program.rot_two() # Stack: Exception, objectref 1245 program.call_function(1) # Stack: exception 1246 program.raise_varargs(1) 1247 # NOTE: This seems to put another object on the stack. 1248 1249 baload = aaload 1250 bastore = aastore 1251 1252 def bipush(self, arguments, program): 1253 program.load_const(signed1(arguments[0])) 1254 1255 caload = aaload 1256 castore = aastore 1257 1258 def checkcast(self, arguments, program): 1259 index = (arguments[0] << 8) + arguments[1] 1260 target_name = self.class_file.constants[index - 1].get_python_name() 1261 program.use_external_name(target_name) 1262 program.dup_top() # Stack: objectref, objectref 1263 program.load_const(None) # Stack: objectref, objectref, None 1264 program.compare_op("is") # Stack: objectref, result 1265 program.jump_to_label(1, "next") 1266 program.pop_top() # Stack: objectref 1267 program.dup_top() # Stack: objectref, objectref 1268 program.load_global("isinstance") # Stack: objectref, objectref, isinstance 1269 program.rot_two() # Stack: objectref, isinstance, objectref 1270 load_class_name(self.class_file, target_name, program) 1271 program.call_function(2) # Stack: objectref, result 1272 program.jump_to_label(1, "next") 1273 program.pop_top() # Stack: objectref 1274 program.pop_top() # Stack: 1275 program.use_external_name("java.lang.ClassCastException") 1276 load_class_name(self.class_file, "java.lang.ClassCastException", program) 1277 program.call_function(0) # Stack: exception 1278 # Wrap the exception in a Python exception. 1279 program.load_global("Exception") # Stack: exception, Exception 1280 program.rot_two() # Stack: Exception, exception 1281 program.call_function(1) # Stack: exception 1282 program.raise_varargs(1) 1283 # NOTE: This seems to put another object on the stack. 1284 program.start_label("next") 1285 program.pop_top() # Stack: objectref 1286 1287 def d2f(self, arguments, program): 1288 pass 1289 1290 def d2i(self, arguments, program): 1291 program.load_global("int") # Stack: value, int 1292 program.rot_two() # Stack: int, value 1293 program.call_function(1) # Stack: result 1294 1295 d2l = d2i # Preserving Java semantics 1296 1297 def dadd(self, arguments, program): 1298 # NOTE: No type checking performed. 1299 program.binary_add() 1300 1301 daload = aaload 1302 dastore = aastore 1303 1304 def dcmpg(self, arguments, program): 1305 # NOTE: No type checking performed. 1306 program.compare_op(">") 1307 1308 def dcmpl(self, arguments, program): 1309 # NOTE: No type checking performed. 1310 program.compare_op("<") 1311 1312 def dconst_0(self, arguments, program): 1313 program.load_const(0.0) 1314 1315 def dconst_1(self, arguments, program): 1316 program.load_const(1.0) 1317 1318 def ddiv(self, arguments, program): 1319 # NOTE: No type checking performed. 1320 program.binary_divide() 1321 1322 dload = aload 1323 dload_0 = aload_0 1324 dload_1 = aload_1 1325 dload_2 = aload_2 1326 dload_3 = aload_3 1327 1328 def dmul(self, arguments, program): 1329 # NOTE: No type checking performed. 1330 program.binary_multiply() 1331 1332 def dneg(self, arguments, program): 1333 # NOTE: No type checking performed. 1334 program.unary_negative() 1335 1336 def drem(self, arguments, program): 1337 # NOTE: No type checking performed. 1338 program.binary_modulo() 1339 1340 dreturn = areturn 1341 dstore = astore 1342 dstore_0 = astore_0 1343 dstore_1 = astore_1 1344 dstore_2 = astore_2 1345 dstore_3 = astore_3 1346 1347 def dsub(self, arguments, program): 1348 # NOTE: No type checking performed. 1349 program.binary_subtract() 1350 1351 def dup(self, arguments, program): 1352 program.dup_top() 1353 1354 def dup_x1(self, arguments, program): 1355 # Ignoring computational type categories. 1356 program.dup_top() 1357 program.rot_three() 1358 1359 def dup_x2(self, arguments, program): 1360 # Ignoring computational type categories. 1361 program.dup_top() 1362 program.rot_four() 1363 1364 dup2 = dup # Ignoring computational type categories 1365 dup2_x1 = dup_x1 # Ignoring computational type categories 1366 dup2_x2 = dup_x2 # Ignoring computational type categories 1367 1368 def f2d(self, arguments, program): 1369 pass # Preserving Java semantics 1370 1371 def f2i(self, arguments, program): 1372 program.load_global("int") # Stack: value, int 1373 program.rot_two() # Stack: int, value 1374 program.call_function(1) # Stack: result 1375 1376 f2l = f2i # Preserving Java semantics 1377 fadd = dadd 1378 faload = daload 1379 fastore = dastore 1380 fcmpg = dcmpg 1381 fcmpl = dcmpl 1382 fconst_0 = dconst_0 1383 fconst_1 = dconst_1 1384 1385 def fconst_2(self, arguments, program): 1386 program.load_const(2.0) 1387 1388 fdiv = ddiv 1389 fload = dload 1390 fload_0 = dload_0 1391 fload_1 = dload_1 1392 fload_2 = dload_2 1393 fload_3 = dload_3 1394 fmul = dmul 1395 fneg = dneg 1396 frem = drem 1397 freturn = dreturn 1398 fstore = dstore 1399 fstore_0 = dstore_0 1400 fstore_1 = dstore_1 1401 fstore_2 = dstore_2 1402 fstore_3 = dstore_3 1403 fsub = dsub 1404 1405 def getfield(self, arguments, program): 1406 index = (arguments[0] << 8) + arguments[1] 1407 target_name = self.class_file.constants[index - 1].get_python_name() 1408 # NOTE: Using the string version of the name which may contain incompatible characters. 1409 program.load_attr(str(target_name)) 1410 1411 def getstatic(self, arguments, program): 1412 index = (arguments[0] << 8) + arguments[1] 1413 target = self.class_file.constants[index - 1] 1414 target_name = target.get_python_name() 1415 1416 # Get the class name instead of the fully qualified name. 1417 1418 full_class_name = target.get_class().get_python_name() 1419 program.use_external_name(full_class_name) 1420 load_class_name(self.class_file, full_class_name, program) 1421 # NOTE: Using the string version of the name which may contain incompatible characters. 1422 program.load_attr(str(target_name)) 1423 1424 def goto(self, arguments, program): 1425 offset = signed2((arguments[0] << 8) + arguments[1]) 1426 java_absolute = self.java_position + offset 1427 program.jump_absolute(self.position_mapping[java_absolute]) 1428 1429 def goto_w(self, arguments, program): 1430 offset = signed4((arguments[0] << 24) + (arguments[1] << 16) + (arguments[2] << 8) + arguments[3]) 1431 java_absolute = self.java_position + offset 1432 program.jump_absolute(self.position_mapping[java_absolute]) 1433 1434 def i2b(self, arguments, program): 1435 pass 1436 1437 def i2c(self, arguments, program): 1438 pass 1439 1440 def i2d(self, arguments, program): 1441 program.load_global("float") # Stack: value, float 1442 program.rot_two() # Stack: float, value 1443 program.call_function(1) # Stack: result 1444 1445 i2f = i2d # Not distinguishing between float and double 1446 1447 def i2l(self, arguments, program): 1448 pass # Preserving Java semantics 1449 1450 def i2s(self, arguments, program): 1451 pass # Not distinguishing between int and short 1452 1453 iadd = fadd 1454 iaload = faload 1455 1456 def iand(self, arguments, program): 1457 # NOTE: No type checking performed. 1458 program.binary_and() 1459 1460 iastore = fastore 1461 1462 def iconst_m1(self, arguments, program): 1463 program.load_const(-1) 1464 1465 def iconst_0(self, arguments, program): 1466 program.load_const(0) 1467 1468 def iconst_1(self, arguments, program): 1469 program.load_const(1) 1470 1471 def iconst_2(self, arguments, program): 1472 program.load_const(2) 1473 1474 def iconst_3(self, arguments, program): 1475 program.load_const(3) 1476 1477 def iconst_4(self, arguments, program): 1478 program.load_const(4) 1479 1480 def iconst_5(self, arguments, program): 1481 program.load_const(5) 1482 1483 idiv = fdiv 1484 1485 def _if_xcmpx(self, arguments, program, op): 1486 offset = signed2((arguments[0] << 8) + arguments[1]) 1487 java_absolute = self.java_position + offset 1488 program.compare_op(op) 1489 program.jump_to_label(0, "next") # skip if false 1490 program.pop_top() 1491 program.jump_absolute(self.position_mapping[java_absolute]) 1492 program.start_label("next") 1493 program.pop_top() 1494 1495 def if_acmpeq(self, arguments, program): 1496 # NOTE: No type checking performed. 1497 self._if_xcmpx(arguments, program, "is") 1498 1499 def if_acmpne(self, arguments, program): 1500 # NOTE: No type checking performed. 1501 self._if_xcmpx(arguments, program, "is not") 1502 1503 def if_icmpeq(self, arguments, program): 1504 # NOTE: No type checking performed. 1505 self._if_xcmpx(arguments, program, "==") 1506 1507 def if_icmpne(self, arguments, program): 1508 # NOTE: No type checking performed. 1509 self._if_xcmpx(arguments, program, "!=") 1510 1511 def if_icmplt(self, arguments, program): 1512 # NOTE: No type checking performed. 1513 self._if_xcmpx(arguments, program, "<") 1514 1515 def if_icmpge(self, arguments, program): 1516 # NOTE: No type checking performed. 1517 self._if_xcmpx(arguments, program, ">=") 1518 1519 def if_icmpgt(self, arguments, program): 1520 # NOTE: No type checking performed. 1521 self._if_xcmpx(arguments, program, ">") 1522 1523 def if_icmple(self, arguments, program): 1524 # NOTE: No type checking performed. 1525 self._if_xcmpx(arguments, program, "<=") 1526 1527 def ifeq(self, arguments, program): 1528 # NOTE: No type checking performed. 1529 program.load_const(0) 1530 self._if_xcmpx(arguments, program, "==") 1531 1532 def ifne(self, arguments, program): 1533 # NOTE: No type checking performed. 1534 program.load_const(0) 1535 self._if_xcmpx(arguments, program, "!=") 1536 1537 def iflt(self, arguments, program): 1538 # NOTE: No type checking performed. 1539 program.load_const(0) 1540 self._if_xcmpx(arguments, program, "<") 1541 1542 def ifge(self, arguments, program): 1543 # NOTE: No type checking performed. 1544 program.load_const(0) 1545 self._if_xcmpx(arguments, program, ">=") 1546 1547 def ifgt(self, arguments, program): 1548 # NOTE: No type checking performed. 1549 program.load_const(0) 1550 self._if_xcmpx(arguments, program, ">") 1551 1552 def ifle(self, arguments, program): 1553 # NOTE: No type checking performed. 1554 program.load_const(0) 1555 self._if_xcmpx(arguments, program, "<=") 1556 1557 def ifnonnull(self, arguments, program): 1558 # NOTE: No type checking performed. 1559 program.load_const(None) 1560 self._if_xcmpx(arguments, program, "is not") 1561 1562 def ifnull(self, arguments, program): 1563 # NOTE: No type checking performed. 1564 program.load_const(None) 1565 self._if_xcmpx(arguments, program, "is") 1566 1567 def iinc(self, arguments, program): 1568 # NOTE: No type checking performed. 1569 program.load_fast(arguments[0]) 1570 program.load_const(arguments[1]) 1571 program.binary_add() 1572 program.store_fast(arguments[0]) 1573 1574 iload = fload 1575 iload_0 = fload_0 1576 iload_1 = fload_1 1577 iload_2 = fload_2 1578 iload_3 = fload_3 1579 imul = fmul 1580 ineg = fneg 1581 1582 def instanceof(self, arguments, program): 1583 index = (arguments[0] << 8) + arguments[1] 1584 target_name = self.class_file.constants[index - 1].get_python_name() 1585 program.use_external_name(target_name) 1586 program.load_global("isinstance") # Stack: objectref, isinstance 1587 program.rot_two() # Stack: isinstance, objectref 1588 load_class_name(self.class_file, target_name, program) 1589 program.call_function(2) # Stack: result 1590 1591 def _invoke(self, target_name, program): 1592 # NOTE: Using the string version of the name which may contain incompatible characters. 1593 program.load_attr(str(target_name)) # Stack: tuple, method 1594 program.rot_two() # Stack: method, tuple 1595 program.call_function_var(0) # Stack: result 1596 1597 def invokeinterface(self, arguments, program): 1598 # NOTE: This implementation does not perform the necessary checks for 1599 # NOTE: signature-based polymorphism. 1600 # NOTE: Java rules not specifically obeyed. 1601 index = (arguments[0] << 8) + arguments[1] 1602 # NOTE: "count" == nargs + 1, apparently. 1603 count = arguments[2] - 1 1604 target_name = self.class_file.constants[index - 1].get_python_name() 1605 # Stack: objectref, arg1, arg2, ... 1606 program.build_tuple(count) # Stack: objectref, tuple 1607 program.rot_two() # Stack: tuple, objectref 1608 # NOTE: The interface information is not used to discover the correct 1609 # NOTE: method. 1610 self._invoke(target_name, program) 1611 1612 def invokespecial(self, arguments, program): 1613 # NOTE: This implementation does not perform the necessary checks for 1614 # NOTE: signature-based polymorphism. 1615 # NOTE: Java rules not specifically obeyed. 1616 index = (arguments[0] << 8) + arguments[1] 1617 target = self.class_file.constants[index - 1] 1618 original_name = target.get_name() 1619 target_name = target.get_python_name() 1620 1621 # Get the number of parameters from the descriptor. 1622 1623 count = len(target.get_descriptor()[0]) 1624 1625 # First, we build a tuple of the reference and arguments. 1626 1627 program.build_tuple(count + 1) # Stack: tuple 1628 1629 # Get the class name instead of the fully qualified name. 1630 # NOTE: Not bothering with Object initialisation. 1631 1632 full_class_name = target.get_class().get_python_name() 1633 if full_class_name not in ("java.lang.Object", "java.lang.Exception"): 1634 program.use_external_name(full_class_name) 1635 load_class_name(self.class_file, full_class_name, program) 1636 self._invoke(target_name, program) 1637 1638 # Remove Python None return value. 1639 1640 if str(original_name) == "<init>": 1641 program.pop_top() 1642 1643 def invokestatic(self, arguments, program): 1644 # NOTE: This implementation does not perform the necessary checks for 1645 # NOTE: signature-based polymorphism. 1646 # NOTE: Java rules not specifically obeyed. 1647 index = (arguments[0] << 8) + arguments[1] 1648 target = self.class_file.constants[index - 1] 1649 target_name = target.get_python_name() 1650 1651 # Get the number of parameters from the descriptor. 1652 1653 count = len(target.get_descriptor()[0]) 1654 1655 # Stack: arg1, arg2, ... 1656 1657 program.build_tuple(count) # Stack: tuple 1658 1659 # Use the class to provide access to static methods. 1660 # Get the class name instead of the fully qualified name. 1661 1662 full_class_name = target.get_class().get_python_name() 1663 if full_class_name not in ("java.lang.Object", "java.lang.Exception"): 1664 program.use_external_name(full_class_name) 1665 load_class_name(self.class_file, full_class_name, program) 1666 self._invoke(target_name, program) 1667 1668 def invokevirtual (self, arguments, program): 1669 # NOTE: This implementation does not perform the necessary checks for 1670 # NOTE: signature-based polymorphism. 1671 # NOTE: Java rules not specifically obeyed. 1672 index = (arguments[0] << 8) + arguments[1] 1673 target = self.class_file.constants[index - 1] 1674 target_name = target.get_python_name() 1675 # Get the number of parameters from the descriptor. 1676 count = len(target.get_descriptor()[0]) 1677 # Stack: objectref, arg1, arg2, ... 1678 program.build_tuple(count) # Stack: objectref, tuple 1679 program.rot_two() # Stack: tuple, objectref 1680 self._invoke(target_name, program) 1681 1682 def ior(self, arguments, program): 1683 # NOTE: No type checking performed. 1684 program.binary_or() 1685 1686 irem = frem 1687 ireturn = freturn 1688 1689 def ishl(self, arguments, program): 1690 # NOTE: No type checking performed. 1691 # NOTE: Not verified. 1692 program.binary_lshift() 1693 1694 def ishr(self, arguments, program): 1695 # NOTE: No type checking performed. 1696 # NOTE: Not verified. 1697 program.binary_rshift() 1698 1699 istore = fstore 1700 istore_0 = fstore_0 1701 istore_1 = fstore_1 1702 istore_2 = fstore_2 1703 istore_3 = fstore_3 1704 isub = fsub 1705 iushr = ishr # Ignoring distinctions between arithmetic and logical shifts 1706 1707 def ixor(self, arguments, program): 1708 # NOTE: No type checking performed. 1709 program.binary_xor() 1710 1711 def jsr(self, arguments, program): 1712 offset = signed2((arguments[0] << 8) + arguments[1]) 1713 java_absolute = self.java_position + offset 1714 # Store the address of the next instruction. 1715 program.load_const_ret(self.position_mapping[self.java_position + 3]) 1716 program.jump_absolute(self.position_mapping[java_absolute]) 1717 1718 def jsr_w(self, arguments, program): 1719 offset = signed4((arguments[0] << 24) + (arguments[1] << 16) + (arguments[2] << 8) + arguments[3]) 1720 java_absolute = self.java_position + offset 1721 # Store the address of the next instruction. 1722 program.load_const_ret(self.position_mapping[self.java_position + 5]) 1723 program.jump_absolute(self.position_mapping[java_absolute]) 1724 1725 l2d = i2d 1726 l2f = i2f 1727 1728 def l2i(self, arguments, program): 1729 pass # Preserving Java semantics 1730 1731 ladd = iadd 1732 laload = iaload 1733 land = iand 1734 lastore = iastore 1735 1736 def lcmp(self, arguments, program): 1737 # NOTE: No type checking performed. 1738 program.dup_topx(2) # Stack: value1, value2, value1, value2 1739 program.compare_op(">") # Stack: value1, value2, result 1740 program.jump_to_label(0, "equals") 1741 # True - produce result and branch. 1742 program.pop_top() # Stack: value1, value2 1743 program.pop_top() # Stack: value1 1744 program.pop_top() # Stack: 1745 program.load_const(1) # Stack: 1 1746 program.jump_to_label(None, "next") 1747 # False - test equality. 1748 program.start_label("equals") 1749 program.pop_top() # Stack: value1, value2 1750 program.dup_topx(2) # Stack: value1, value2, value1, value2 1751 program.compare_op("==") # Stack: value1, value2, result 1752 program.jump_to_label(0, "less") 1753 # True - produce result and branch. 1754 program.pop_top() # Stack: value1, value2 1755 program.pop_top() # Stack: value1 1756 program.pop_top() # Stack: 1757 program.load_const(0) # Stack: 0 1758 program.jump_to_label(None, "next") 1759 # False - produce result. 1760 program.start_label("less") 1761 program.pop_top() # Stack: value1, value2 1762 program.pop_top() # Stack: value1 1763 program.pop_top() # Stack: 1764 program.load_const(-1) # Stack: -1 1765 program.start_label("next") 1766 1767 lconst_0 = iconst_0 1768 lconst_1 = iconst_1 1769 1770 def ldc(self, arguments, program): 1771 const = self.class_file.constants[arguments[0] - 1] 1772 if isinstance(const, classfile.StringInfo): 1773 program.use_external_name("java.lang.String") 1774 program.load_global("java") 1775 program.load_attr("lang") 1776 program.load_attr("String") 1777 program.load_const(const.get_value()) 1778 program.call_function(1) 1779 else: 1780 program.load_const(const.get_value()) 1781 1782 def ldc_w(self, arguments, program): 1783 const = self.class_file.constants[(arguments[0] << 8) + arguments[1] - 1] 1784 if isinstance(const, classfile.StringInfo): 1785 program.use_external_name("java.lang.String") 1786 program.load_global("java") 1787 program.load_attr("lang") 1788 program.load_attr("String") 1789 program.load_const(const.get_value()) 1790 program.call_function(1) 1791 else: 1792 program.load_const(const.get_value()) 1793 1794 ldc2_w = ldc_w 1795 ldiv = idiv 1796 lload = iload 1797 lload_0 = iload_0 1798 lload_1 = iload_1 1799 lload_2 = iload_2 1800 lload_3 = iload_3 1801 lmul = imul 1802 lneg = ineg 1803 1804 def lookupswitch(self, code, program): 1805 1806 # Find the offset to the next 4 byte boundary in the code. 1807 1808 d, r = divmod(self.java_position + 1, 4) 1809 to_boundary = (4 - r) % 4 1810 1811 # Get the pertinent arguments. 1812 1813 code = code[to_boundary:] 1814 default = classfile.s4(code[0:4]) 1815 npairs = classfile.s4(code[4:8]) 1816 1817 # Process the pairs. 1818 # NOTE: This is not the most optimal implementation. 1819 1820 pair_index = 8 1821 for pair in range(0, npairs): 1822 match = classfile.u4(code[pair_index:pair_index+4]) 1823 offset = classfile.s4(code[pair_index+4:pair_index+8]) 1824 # Calculate the branch target. 1825 java_absolute = self.java_position + offset 1826 # Generate branching code. 1827 program.dup_top() # Stack: key, key 1828 program.load_const(match) # Stack: key, key, match 1829 program.compare_op("==") # Stack: key, result 1830 program.jump_to_label(0, "end") 1831 program.pop_top() # Stack: key 1832 program.pop_top() # Stack: 1833 program.jump_absolute(self.position_mapping[java_absolute]) 1834 # Generate the label for the end of the branching code. 1835 program.start_label("end") 1836 program.pop_top() # Stack: key 1837 # Update the index. 1838 pair_index += 8 1839 1840 # Generate the default. 1841 1842 java_absolute = self.java_position + default 1843 program.jump_absolute(self.position_mapping[java_absolute]) 1844 return pair_index + to_boundary 1845 1846 lor = ior 1847 lrem = irem 1848 lreturn = ireturn 1849 lshl = ishl 1850 lshr = ishr 1851 lstore = istore 1852 lstore_0 = istore_0 1853 lstore_1 = istore_1 1854 lstore_2 = istore_2 1855 lstore_3 = istore_3 1856 lsub = isub 1857 lushr = iushr 1858 lxor = ixor 1859 1860 def monitorenter(self, arguments, program): 1861 # NOTE: To be implemented. 1862 pass 1863 1864 def monitorexit(self, arguments, program): 1865 # NOTE: To be implemented. 1866 pass 1867 1868 def multianewarray(self, arguments, program): 1869 index = (arguments[0] << 8) + arguments[1] 1870 dimensions = arguments[2] 1871 # Stack: count1, ..., countN-1, countN 1872 type_name = self.class_file.constants[index - 1].get_python_name() 1873 default_value = classfile.get_default_for_type(type_name) 1874 self._newarray(program, default_value) # Stack: count1, ..., countN-1, list 1875 for dimension in range(1, dimensions): 1876 program.rot_two() # Stack: count1, ..., list, countN-1 1877 program.build_list(0) # Stack: count1, ..., list, countN-1, new-list 1878 program.rot_three() # Stack: count1, ..., new-list, list, countN-1 1879 program.setup_loop() 1880 program.load_const(0) # Stack: count1, ..., new-list, list, countN-1, 0 1881 program.rot_two() # Stack: count1, ..., new-list, list, 0, countN-1 1882 program.load_global("range") # Stack: count1, ..., new-list, list, 0, countN-1, range 1883 program.rot_three() # Stack: count1, ..., new-list, list, range, 0, countN-1 1884 program.call_function(2) # Stack: count1, ..., new-list, list, range-list 1885 program.get_iter() # Stack: count1, ..., new-list, list, iter 1886 program.for_iter() # Stack: count1, ..., new-list, list, iter, value 1887 program.pop_top() # Stack: count1, ..., new-list, list, iter 1888 program.rot_three() # Stack: count1, ..., iter, new-list, list 1889 program.slice_0() # Stack: count1, ..., iter, new-list, list[:] 1890 program.dup_top() # Stack: count1, ..., iter, new-list, list[:], list[:] 1891 program.rot_three() # Stack: count1, ..., iter, list[:], new-list, list[:] 1892 program.rot_two() # Stack: count1, ..., iter, list[:], list[:], new-list 1893 program.dup_top() # Stack: count1, ..., iter, list[:], list[:], new-list, new-list 1894 program.load_attr("append") # Stack: count1, ..., iter, list[:], list[:], new-list, append 1895 program.rot_three() # Stack: count1, ..., iter, list[:], append, list[:], new-list 1896 program.rot_three() # Stack: count1, ..., iter, list[:], new-list, append, list[:] 1897 program.call_function(1) # Stack: count1, ..., iter, list[:], new-list, None 1898 program.pop_top() # Stack: count1, ..., iter, list[:], new-list 1899 program.rot_two() # Stack: count1, ..., iter, new-list, list[:] 1900 program.rot_three() # Stack: count1, ..., list[:], iter, new-list 1901 program.rot_three() # Stack: count1, ..., new-list, list[:], iter 1902 program.end_loop() # Stack: count1, ..., new-list, list[:], iter 1903 program.pop_top() # Stack: count1, ..., new-list 1904 1905 def new(self, arguments, program): 1906 # This operation is considered to be the same as the calling of the 1907 # initialisation method of the given class with no arguments. 1908 1909 index = (arguments[0] << 8) + arguments[1] 1910 target_name = self.class_file.constants[index - 1].get_python_name() 1911 program.use_external_name(target_name) 1912 1913 # NOTE: Using the string version of the name which may contain incompatible characters. 1914 program.load_global("object") 1915 program.load_attr("__new__") 1916 load_class_name(self.class_file, target_name, program) 1917 program.call_function(1) 1918 1919 def newarray(self, arguments, program): 1920 # NOTE: Does not raise NegativeArraySizeException. 1921 # NOTE: Not completely using the arguments to type the list/array. 1922 atype = arguments[0] 1923 default_value = get_default_for_atype(atype) 1924 self._newarray(program, default_value) 1925 1926 def nop(self, arguments, program): 1927 pass 1928 1929 def pop(self, arguments, program): 1930 program.pop_top() 1931 1932 pop2 = pop # ignoring Java stack value distinctions 1933 1934 def putfield(self, arguments, program): 1935 index = (arguments[0] << 8) + arguments[1] 1936 target_name = self.class_file.constants[index - 1].get_python_name() 1937 program.rot_two() 1938 # NOTE: Using the string version of the name which may contain incompatible characters. 1939 program.store_attr(str(target_name)) 1940 1941 def putstatic(self, arguments, program): 1942 index = (arguments[0] << 8) + arguments[1] 1943 target = self.class_file.constants[index - 1] 1944 target_name = target.get_python_name() 1945 1946 # Get the class name instead of the fully qualified name. 1947 1948 full_class_name = target.get_class().get_python_name() 1949 program.use_external_name(full_class_name) 1950 load_class_name(self.class_file, full_class_name, program) 1951 # NOTE: Using the string version of the name which may contain incompatible characters. 1952 program.store_attr(str(target_name)) 1953 1954 def ret(self, arguments, program): 1955 program.ret(arguments[0]) 1956 # Indicate that the finally handler is probably over. 1957 # NOTE: This is seemingly not guaranteed. 1958 self.in_finally = 0 1959 1960 def return_(self, arguments, program): 1961 program.load_const(None) 1962 program.return_value() 1963 1964 saload = laload 1965 sastore = lastore 1966 1967 def sipush(self, arguments, program): 1968 program.load_const(signed2((arguments[0] << 8) + arguments[1])) 1969 1970 def swap(self, arguments, program): 1971 program.rot_two() 1972 1973 def tableswitch(self, code, program): 1974 1975 # Find the offset to the next 4 byte boundary in the code. 1976 1977 d, r = divmod(self.java_position + 1, 4) 1978 to_boundary = (4 - r) % 4 1979 1980 # Get the pertinent arguments. 1981 1982 code = code[to_boundary:] 1983 default = classfile.s4(code[0:4]) 1984 low = classfile.s4(code[4:8]) 1985 high = classfile.s4(code[8:12]) 1986 1987 # Process the jump entries. 1988 # NOTE: This is not the most optimal implementation. 1989 1990 jump_index = 12 1991 for jump in range(low, high + 1): 1992 offset = classfile.s4(code[jump_index:jump_index + 4]) 1993 1994 # Calculate the branch target. 1995 1996 java_absolute = self.java_position + offset 1997 1998 # Generate branching code. 1999 2000 program.dup_top() # Stack: key, key 2001 program.load_const(jump) # Stack: key, key, jump 2002 program.compare_op("==") # Stack: key, result 2003 program.jump_to_label(0, "end") 2004 program.pop_top() # Stack: key 2005 program.pop_top() # Stack: 2006 program.jump_absolute(self.position_mapping[java_absolute]) 2007 2008 # Generate the label for the end of the branching code. 2009 2010 program.start_label("end") 2011 program.pop_top() # Stack: key 2012 2013 # Update the index. 2014 2015 jump_index += 4 2016 2017 # Generate the default. 2018 2019 java_absolute = self.java_position + default 2020 program.jump_absolute(self.position_mapping[java_absolute]) 2021 return jump_index + to_boundary 2022 2023 def wide(self, code, program): 2024 # NOTE: To be implemented. 2025 raise NotImplementedError, "wide" 2026 2027 def disassemble(class_file, method): 2028 disassembler = BytecodeDisassembler(class_file) 2029 disassembler.process(method, BytecodeDisassemblerProgram()) 2030 return disassembler 2031 2032 class ClassTranslator: 2033 2034 """ 2035 A class which provides a wrapper around a class file and the means to 2036 translate the represented class into a Python class. 2037 """ 2038 2039 def __init__(self, class_file): 2040 2041 "Initialise the object with the given 'class_file'." 2042 2043 self.class_file = class_file 2044 self.filename = "" 2045 2046 for attribute in self.class_file.attributes: 2047 if isinstance(attribute, classfile.SourceFileAttributeInfo): 2048 self.filename = str(attribute.get_name()) 2049 2050 def translate_method(self, method): 2051 2052 "Translate the given 'method' - an object obtained from the class file." 2053 2054 translator = BytecodeTranslator(self.class_file) 2055 writer = BytecodeWriter() 2056 try: 2057 translator.process(method, writer) 2058 except: 2059 print "Translation error in", str(self.class_file.this_class.get_name()), str(method.get_name()) 2060 disassemble(self.class_file, method) 2061 raise 2062 return translator, writer 2063 2064 def make_method(self, real_method_name, methods, global_names): 2065 2066 """ 2067 Make a dispatcher method with the given 'real_method_name', providing 2068 dispatch to the supplied type-sensitive 'methods', accessing the given 2069 'global_names' where necessary, and storing the new method in the 2070 class's namespace. 2071 """ 2072 2073 if real_method_name == "<init>": 2074 method_name = "__init__" 2075 else: 2076 method_name = real_method_name 2077 2078 # Where only one method exists, just make an alias. 2079 2080 if len(methods) == 1: 2081 method, fn = methods[0] 2082 self.namespace[method_name] = fn 2083 return 2084 2085 # Write a simple bytecode dispatching mechanism. 2086 2087 program = BytecodeWriter() 2088 2089 # Remember whether any of the methods are static. 2090 # NOTE: This should be an all or nothing situation. 2091 2092 method_is_static = 0 2093 2094 # NOTE: The code below should use dictionary-based dispatch for better performance. 2095 2096 for method, fn in methods: 2097 method_is_static = real_method_name != "<init>" and method_is_static or \ 2098 classfile.has_flags(method.access_flags, [classfile.STATIC]) 2099 2100 if method_is_static: 2101 program.load_fast(0) # Stack: arguments 2102 else: 2103 program.load_fast(1) # Stack: arguments 2104 2105 program.setup_loop() 2106 program.load_const(1) # Stack: arguments, 1 2107 2108 if method_is_static: 2109 program.store_fast(1) # Stack: arguments (found = 1) 2110 else: 2111 program.store_fast(2) # Stack: arguments (found = 1) 2112 2113 # Emit a list of parameter types. 2114 2115 descriptor_types = method.get_descriptor()[0] 2116 for descriptor_type in descriptor_types: 2117 base_type, object_type, array_type = descriptor_type 2118 python_type = classfile.descriptor_base_type_mapping[base_type] 2119 if python_type == "instance": 2120 # NOTE: This will need extending. 2121 python_type = object_type 2122 program.load_global(python_type) # Stack: arguments, type, ... 2123 program.build_list(len(descriptor_types)) 2124 # Stack: arguments, types 2125 # Make a map of arguments and types. 2126 program.load_const(None) # Stack: arguments, types, None 2127 program.rot_three() # Stack: None, arguments, types 2128 program.build_tuple(3) # Stack: tuple 2129 program.load_global("map") # Stack: tuple, map 2130 program.rot_two() # Stack: map, tuple 2131 program.call_function_var(0) # Stack: list (mapping arguments to types) 2132 # Loop over each pair. 2133 program.get_iter() # Stack: iter 2134 program.for_iter() # Stack: iter, (argument, type) 2135 program.unpack_sequence(2) # Stack: iter, type, argument 2136 program.dup_top() # Stack: iter, type, argument, argument 2137 program.load_const(None) # Stack: iter, type, argument, argument, None 2138 program.compare_op("is") # Stack: iter, type, argument, result 2139 # Missing argument? 2140 program.jump_to_label(0, "present") 2141 program.pop_top() # Stack: iter, type, argument 2142 program.pop_top() # Stack: iter, type 2143 program.pop_top() # Stack: iter 2144 program.load_const(0) # Stack: iter, 0 2145 2146 if method_is_static: 2147 program.store_fast(1) # Stack: iter (found = 0) 2148 else: 2149 program.store_fast(2) # Stack: iter (found = 0) 2150 2151 program.break_loop() 2152 # Argument was present. 2153 program.start_label("present") 2154 program.pop_top() # Stack: iter, type, argument 2155 program.rot_two() # Stack: iter, argument, type 2156 program.dup_top() # Stack: iter, argument, type, type 2157 program.load_const(None) # Stack: iter, argument, type, type, None 2158 program.compare_op("is") # Stack: iter, argument, type, result 2159 # Missing parameter type? 2160 program.jump_to_label(0, "present") 2161 program.pop_top() # Stack: iter, argument, type 2162 program.pop_top() # Stack: iter, argument 2163 program.pop_top() # Stack: iter 2164 program.load_const(0) # Stack: iter, 0 2165 2166 if method_is_static: 2167 program.store_fast(1) # Stack: iter (found = 0) 2168 else: 2169 program.store_fast(2) # Stack: iter (found = 0) 2170 2171 program.break_loop() 2172 # Parameter was present. 2173 program.start_label("present") 2174 program.pop_top() # Stack: iter, argument, type 2175 program.build_tuple(2) # Stack: iter, (argument, type) 2176 program.load_global("isinstance") # Stack: iter, (argument, type), isinstance 2177 program.rot_two() # Stack: iter, isinstance, (argument, type) 2178 program.call_function_var(0) # Stack: iter, result 2179 program.jump_to_label(1, "match") 2180 program.pop_top() # Stack: iter 2181 program.load_const(0) # Stack: iter, 0 2182 2183 if method_is_static: 2184 program.store_fast(1) # Stack: iter (found = 0) 2185 else: 2186 program.store_fast(2) # Stack: iter (found = 0) 2187 2188 program.break_loop() 2189 # Argument type and parameter type matched. 2190 program.start_label("match") 2191 program.pop_top() # Stack: iter 2192 program.end_loop() # Stack: 2193 # If all the parameters matched, call the method. 2194 2195 if method_is_static: 2196 program.load_fast(1) # Stack: match 2197 else: 2198 program.load_fast(2) # Stack: match 2199 2200 program.jump_to_label(0, "failed") 2201 # All the parameters matched. 2202 program.pop_top() # Stack: 2203 2204 if method_is_static: 2205 program.load_fast(0) # Stack: arguments 2206 program.load_global(str(self.class_file.this_class.get_python_name())) 2207 # Stack: arguments, class 2208 else: 2209 program.load_fast(1) # Stack: arguments 2210 program.load_fast(0) # Stack: arguments, self 2211 2212 program.load_attr(str(method.get_python_name())) 2213 # Stack: arguments, method 2214 program.rot_two() # Stack: method, arguments 2215 program.call_function_var(0) # Stack: result 2216 program.return_value() 2217 # Try the next method if arguments or parameters were missing or incorrect. 2218 program.start_label("failed") 2219 program.pop_top() # Stack: 2220 2221 # Raise an exception if nothing matched. 2222 # NOTE: Improve this. 2223 2224 program.load_const("No matching method") 2225 program.raise_varargs(1) 2226 program.load_const(None) 2227 program.return_value() 2228 2229 # Add the code as a method in the namespace. 2230 # NOTE: One actual parameter, flags as 71 apparently means that a list 2231 # NOTE: parameter is used in a method. 2232 2233 if method_is_static: 2234 nargs = 0 2235 else: 2236 nargs = 1 2237 nlocals = program.max_locals + 1 2238 2239 code = new.code(nargs, nlocals, program.max_stack_depth, 71, program.get_output(), 2240 tuple(program.get_constants()), tuple(program.get_names()), tuple(self.make_varnames(nlocals, method_is_static)), 2241 self.filename, method_name, 0, "") 2242 fn = new.function(code, global_names) 2243 2244 if method_is_static: 2245 fn = staticmethod(fn) 2246 2247 self.namespace[method_name] = fn 2248 2249 def process(self, global_names): 2250 2251 """ 2252 Process the class, storing it in the 'global_names' dictionary provided. 2253 Return a list of external names referenced by the class's methods. 2254 """ 2255 2256 self.namespace = {} 2257 2258 # Make the fields. 2259 2260 for field in self.class_file.fields: 2261 if classfile.has_flags(field.access_flags, [classfile.STATIC]): 2262 field_name = str(field.get_python_name()) 2263 self.namespace[field_name] = None 2264 2265 # Make the methods. 2266 2267 self.real_methods = {} 2268 external_names = [] 2269 2270 for method in self.class_file.methods: 2271 real_method_name = str(method.get_name()) 2272 method_name = str(method.get_python_name()) 2273 translator, writer = self.translate_method(method) 2274 2275 # Add external names to the master list. 2276 2277 for external_name in writer.external_names: 2278 if external_name not in external_names: 2279 external_names.append(external_name) 2280 2281 # Fix up special class initialisation methods and static methods. 2282 2283 method_is_static = real_method_name != "<init>" and classfile.has_flags(method.access_flags, [classfile.STATIC]) 2284 if method_is_static: 2285 nargs = len(method.get_descriptor()[0]) 2286 else: 2287 nargs = len(method.get_descriptor()[0]) + 1 2288 nlocals = writer.max_locals + 1 2289 flags = 67 2290 2291 # NOTE: Add line number table later. 2292 2293 code = new.code(nargs, nlocals, writer.max_stack_depth, flags, writer.get_output(), 2294 tuple(writer.get_constants()), tuple(writer.get_names()), 2295 tuple(self.make_varnames(nlocals, method_is_static)), self.filename, method_name, 0, "") 2296 2297 # NOTE: May need more globals. 2298 2299 fn = new.function(code, global_names) 2300 2301 # Fix up special class initialisation methods and static methods. 2302 2303 if method_is_static: 2304 fn = staticmethod(fn) 2305 2306 # Remember the real method name and the corresponding methods produced. 2307 2308 if not self.real_methods.has_key(real_method_name): 2309 self.real_methods[real_method_name] = [] 2310 self.real_methods[real_method_name].append((method, fn)) 2311 2312 # Add the method to the class's namespace. 2313 2314 self.namespace[method_name] = fn 2315 2316 # Add the base classes as external names, if appropriate. 2317 2318 external_names += [class_.get_python_name() for class_ in self.get_base_class_references()] 2319 2320 return external_names 2321 2322 def get_class(self, global_names, classes): 2323 2324 """ 2325 Get the Python class representing the underlying Java class, using the 2326 given 'global_names' to define dispatcher methods. 2327 """ 2328 2329 # Define superclasses. 2330 2331 bases = self.get_base_classes(classes) 2332 2333 # Define method dispatchers. 2334 2335 for real_method_name, methods in self.real_methods.items(): 2336 if real_method_name != "<clinit>": 2337 self.make_method(real_method_name, methods, global_names) 2338 2339 # Use only the last part of the fully qualified name. 2340 2341 full_class_name = str(self.class_file.this_class.get_python_name()) 2342 class_name = full_class_name.split(".")[-1] 2343 cls = new.classobj(class_name, bases, self.namespace) 2344 global_names[cls.__name__] = cls 2345 2346 return cls 2347 2348 def get_base_class_references(self): 2349 class_names = [] 2350 if self.class_file.super_class is not None: 2351 class_names.append(self.class_file.super_class) 2352 class_names += self.class_file.interfaces 2353 return class_names 2354 2355 def get_base_classes(self, classes): 2356 2357 """ 2358 Identify the superclass, obtaining it from the given 'classes' 2359 dictionary. Return a tuple containing all base classes (usually a single 2360 element tuple). 2361 """ 2362 2363 base_classes = [] 2364 2365 for class_ in self.get_base_class_references(): 2366 base_classes.append(self._get_class(class_, classes)) 2367 2368 return tuple(base_classes) 2369 2370 def _get_class(self, class_, classes): 2371 class_name = str(class_.get_name()) 2372 try: 2373 return classes[class_name] 2374 except KeyError: 2375 raise AttributeError, "Cannot find Java class '%s'" % class_name 2376 2377 def make_varnames(self, nlocals, method_is_static=0): 2378 2379 """ 2380 A utility method which invents variable names for the given number - 2381 'nlocals' - of local variables in a method. Returns a list of such 2382 variable names. 2383 2384 If the optional 'method_is_static' is set to true, do not use "self" as 2385 the first argument name. 2386 """ 2387 2388 if method_is_static: 2389 l = ["cls"] 2390 else: 2391 l = ["self"] 2392 for i in range(1, nlocals): 2393 l.append("_l%s" % i) 2394 return l[:nlocals] 2395 2396 # Test functions, useful for tracing generated bytecode operations. 2397 2398 def _map(*args): 2399 print args 2400 return apply(__builtins__.map, args) 2401 2402 def _isinstance(*args): 2403 print args 2404 return apply(__builtins__.isinstance, args) 2405 2406 if __name__ == "__main__": 2407 import sys 2408 import dis 2409 import java.lang 2410 global_names = globals() 2411 #global_names["isinstance"] = _isinstance 2412 #global_names["map"] = _map 2413 for filename in sys.argv[1:]: 2414 f = open(filename, "rb") 2415 c = classfile.ClassFile(f.read()) 2416 translator = ClassTranslator(c) 2417 external_names = translator.process(global_names) 2418 cls = translator.get_class(global_names) 2419 2420 # vim: tabstop=4 expandtab shiftwidth=4