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