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