javaclass

bytecode.py

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