javaclass

Annotated bytecode.py

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