javaclass

bytecode.py

9:7adb0ed5b58e
2004-11-08 Paul Boddie Added missing bytecode translations.
     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 from dis import opmap, cmp_op # for access to Python bytecode values and operators    11 from UserDict import UserDict    12     13 # Bytecode production classes.    14     15 class BytecodeWriter:    16     17     "A Python bytecode writer."    18     19     def __init__(self):    20         self.loops = []    21         self.jumps = {}    22         self.output = []    23         self.position = 0    24     25         # Stack depth estimation.    26         self.stack_depth = 0    27         self.max_stack_depth = 0    28     29         # Mapping from values to indexes.    30         self.constants = {}    31     32         # Mapping from names to indexes.    33         # NOTE: This may be acquired from elsewhere.    34         #self.globals = {}    35     36         # Mapping from names to indexes.    37         self.names = {}    38     39         # A list of constants used as exception handler return addresses.    40         self.constants_for_exceptions = []    41     42     def get_output(self):    43         output = []    44         for element in self.output:    45             if isinstance(element, LazySubValue):    46                 value = element.value    47             else:    48                 value = element    49             output.append(chr(value))    50         return "".join(output)    51     52     def get_constants(self):    53         l = self._get_list(self._invert(self.constants))    54         result = []    55         for i in l:    56             if isinstance(i, LazyValue):    57                 result.append(i.get_value())    58             else:    59                 result.append(i)    60         return result    61     62     #def get_globals(self):    63     #    return self._get_list(self._invert(self.globals))    64     65     def get_names(self):    66         return self._get_list(self._invert(self.names))    67     68     def _invert(self, d):    69         inverted = {}    70         for k, v in d.items():    71             inverted[v] = k    72         return inverted    73     74     def _get_list(self, d):    75         l = []    76         for i in range(0, len(d.keys())):    77             l.append(d[i])    78         return l    79     80     # Administrative methods.    81     82     def update_stack_depth(self, change):    83         self.stack_depth += change    84         if self.stack_depth > self.max_stack_depth:    85             self.max_stack_depth = self.stack_depth    86     87     # Special methods.    88     89     def _write_value(self, value):    90         if isinstance(value, LazyValue):    91             # NOTE: Assume a 16-bit value.    92             self.output.append(value.values[0])    93             self.output.append(value.values[1])    94         elif value <= 0xffff:    95             self.output.append(value & 0xff)    96             self.output.append((value & 0xff00) >> 8)    97             self.position += 2    98         else:    99             # NOTE: EXTENDED_ARG not yet supported.   100             raise ValueError, value   101    102     def setup_loop(self):   103         self.loops.push(self.position)   104         self.output.append(opmap["SETUP_LOOP"])   105         self.position += 1   106         self._write_value(0) # To be filled in later   107    108     def end_loop(self):   109         current_loop_start = self.loops.pop()   110         self.jump_absolute(current_loop_start)   111         self.output[current_loop_start + 1] = self.position   112         self.pop_block()   113    114     def jump_to_label(self, status, name):   115         # Record the instruction using the jump.   116         jump_instruction = self.position   117         if status is None:   118             self.jump_forward()   119         elif status:   120             self.jump_if_true()   121         else:   122             self.jump_if_false()   123         # Record the following instruction, too.   124         if not self.jumps.has_key(name):   125             self.jumps[name] = []   126         self.jumps[name].append((jump_instruction, self.position))   127    128     def start_label(self, name):   129         # Fill in all jump instructions.   130         for jump_instruction, following_instruction in self.jumps[name]:   131             self.output[jump_instruction + 1] = self.position - following_instruction   132         del self.jumps[name]   133    134     def load_const_ret(self, value):   135         self.constants_for_exceptions.append(value)   136         self.load_const(value)   137    138     def ret(self, index):   139         # Previously, the constant stored on the stack by jsr/jsr_w was stored   140         # in a local variable. In the JVM, extracting the value from the local   141         # variable and jumping can be done at runtime. In the Python VM, any   142         # jump target must be known in advance and written into the bytecode.   143         self.load_fast(index)   144         for constant in self.constants_for_exceptions:   145             self.dup_top()              # Stack: actual-address, actual-address   146             self.load_const(constant)   # Stack: actual-address, actual-address, suggested-address   147             self.compare_op("==")       # Stack: actual-address, result   148             self.jump_to_label(0, "const")   149             self.jump_absolute(constant)   150             self.start_label("const")   151             self.pop_top()              # Stack: actual-address   152    153     # Complicated methods.   154    155     def load_const(self, value):   156         self.output.append(opmap["LOAD_CONST"])   157         if not self.constants.has_key(value):   158             self.constants[value] = len(self.constants.keys())   159         self.position += 1   160         self._write_value(self.constants[value])   161         self.update_stack_depth(1)   162    163     def load_global(self, name):   164         self.output.append(opmap["LOAD_GLOBAL"])   165         if not self.names.has_key(name):   166             self.names[name] = len(self.names.keys())   167         self.position += 1   168         self._write_value(self.names[name])   169         self.update_stack_depth(1)   170    171     def load_attr(self, name):   172         self.output.append(opmap["LOAD_ATTR"])   173         if not self.names.has_key(name):   174             self.names[name] = len(self.names.keys())   175         self.position += 1   176         self._write_value(self.names[name])   177    178     def load_name(self, name):   179         self.output.append(opmap["LOAD_NAME"])   180         if not self.names.has_key(name):   181             self.names[name] = len(self.names.keys())   182         self.position += 1   183         self._write_value(self.names[name])   184         self.update_stack_depth(1)   185    186     def load_fast(self, index):   187         self.output.append(opmap["LOAD_FAST"])   188         self.position += 1   189         self._write_value(index)   190         self.update_stack_depth(1)   191    192     def store_attr(self, name):   193         self.output.append(opmap["STORE_ATTR"])   194         if not self.names.has_key(name):   195             self.names[name] = len(self.names.keys())   196         self.position += 1   197         self._write_value(self.names[name])   198         self.update_stack_depth(-1)   199    200     def store_fast(self, index):   201         self.output.append(opmap["STORE_FAST"])   202         self.position += 1   203         self._write_value(index)   204         self.update_stack_depth(-1)   205    206     # Normal bytecode generators.   207    208     def for_iter(self):   209         self.loops.push(self.position)   210         self.output.append(opmap["FOR_ITER"])   211         self.position += 1   212         self._write_value(0) # To be filled in later   213         self.update_stack_depth(1)   214    215     def jump_if_false(self, offset=0):   216         self.output.append(opmap["JUMP_IF_FALSE"])   217         self.position += 1   218         self._write_value(offset) # May be filled in later   219    220     def jump_if_true(self, offset=0):   221         self.output.append(opmap["JUMP_IF_TRUE"])   222         self.position += 1   223         self._write_value(offset) # May be filled in later   224    225     def jump_forward(self, offset=0):   226         self.output.append(opmap["JUMP_FORWARD"])   227         self.position += 1   228         self._write_value(offset) # May be filled in later   229    230     def jump_absolute(self, address=0):   231         self.output.append(opmap["JUMP_ABSOLUTE"])   232         self.position += 1   233         self._write_value(address) # May be filled in later   234    235     def build_tuple(self, count):   236         self.output.append(opmap["BUILD_TUPLE"])   237         self.position += 1   238         self._write_value(count)   239         self.update_stack_depth(-(count - 1))   240    241     def build_list(self, count):   242         self.output.append(opmap["BUILD_LIST"])   243         self.position += 1   244         self._write_value(count)   245         self.update_stack_depth(-(count - 1))   246    247     def pop_top(self):   248         self.output.append(opmap["POP_TOP"])   249         self.position += 1   250         self.update_stack_depth(-1)   251    252     def dup_top(self):   253         self.output.append(opmap["DUP_TOP"])   254         self.position += 1   255         self.update_stack_depth(1)   256    257     def rot_two(self):   258         self.output.append(opmap["ROT_TWO"])   259         self.position += 1   260    261     def rot_three(self):   262         self.output.append(opmap["ROT_THREE"])   263         self.position += 1   264    265     def rot_four(self):   266         self.output.append(opmap["ROT_FOUR"])   267         self.position += 1   268    269     def call_function(self, count):   270         self.output.append(opmap["CALL_FUNCTION"])   271         self.position += 1   272         self._write_value(count)   273         self.update_stack_depth(-count)   274    275     def binary_subscr(self):   276         self.output.append(opmap["BINARY_SUBSCR"])   277         self.position += 1   278         self.update_stack_depth(-1)   279    280     def binary_add(self):   281         self.output.append(opmap["BINARY_ADD"])   282         self.position += 1   283         self.update_stack_depth(-1)   284    285     def binary_divide(self):   286         self.output.append(opmap["BINARY_DIVIDE"])   287         self.position += 1   288         self.update_stack_depth(-1)   289    290     def binary_multiply(self):   291         self.output.append(opmap["BINARY_MULTIPLY"])   292         self.position += 1   293         self.update_stack_depth(-1)   294    295     def binary_modulo(self):   296         self.output.append(opmap["BINARY_MODULO"])   297         self.position += 1   298         self.update_stack_depth(-1)   299    300     def binary_subtract(self):   301         self.output.append(opmap["BINARY_SUBTRACT"])   302         self.position += 1   303         self.update_stack_depth(-1)   304    305     def binary_and(self):   306         self.output.append(opmap["BINARY_AND"])   307         self.position += 1   308         self.update_stack_depth(-1)   309    310     def binary_or(self):   311         self.output.append(opmap["BINARY_XOR"])   312         self.position += 1   313         self.update_stack_depth(-1)   314    315     def binary_lshift(self):   316         self.output.append(opmap["BINARY_LSHIFT"])   317         self.position += 1   318         self.update_stack_depth(-1)   319    320     def binary_rshift(self):   321         self.output.append(opmap["BINARY_RSHIFT"])   322         self.position += 1   323         self.update_stack_depth(-1)   324    325     def binary_xor(self):   326         self.output.append(opmap["BINARY_XOR"])   327         self.position += 1   328         self.update_stack_depth(-1)   329    330     def compare_op(self, op):   331         self.output.append(opmap["COMPARE_OP"])   332         self.position += 1   333         self._write_value(list(cmp_op).index(op))   334         self.update_stack_depth(-1)   335    336     def return_value(self):   337         self.output.append(opmap["RETURN_VALUE"])   338         self.position += 1   339         self.update_stack_depth(-1)   340    341     def raise_varargs(self, count):   342         self.output.append(opmap["RAISE_VARARGS"])   343         self.position += 1   344         self._write_value(count)   345    346     def pop_block(self):   347         self.output.append(opmap["POP_BLOCK"])   348         self.position += 1   349    350 # Utility classes and functions.   351    352 class LazyDict(UserDict):   353     def __getitem__(self, key):   354         if not self.data.has_key(key):   355             # NOTE: Assume 16-bit value.   356             self.data[key] = LazyValue(2)   357         return self.data[key]   358     def __setitem__(self, key, value):   359         if self.data.has_key(key):   360             existing_value = self.data[key]   361             if isinstance(existing_value, LazyValue):   362                 existing_value.set_value(value)   363                 return   364         self.data[key] = value   365    366 class LazyValue:   367     def __init__(self, nvalues):   368         self.values = []   369         for i in range(0, nvalues):   370             self.values.append(LazySubValue())   371     def set_value(self, value):   372         # NOTE: Assume at least 16-bit value. No "filling" performed.   373         if value <= 0xffff:   374             self.values[0].set_value(value & 0xff)   375             self.values[1].set_value((value & 0xff00) >> 8)   376         else:   377             # NOTE: EXTENDED_ARG not yet supported.   378             raise ValueError, value   379     def get_value(self):   380         value = 0   381         for i in range(0, len(self.values)):   382             value = (value << 8) + self.values.pop().value   383         return value   384    385 class LazySubValue:   386     def __init__(self):   387         self.value = 0   388     def set_value(self, value):   389         self.value = value   390    391 def signed(value, limit):   392    393     """   394     Return the signed integer from the unsigned 'value', where 'limit' (a value   395     one greater than the highest possible positive integer) is used to determine   396     whether a negative or positive result is produced.   397     """   398    399     d, r = divmod(value, limit)   400     if d == 1:   401         mask = limit * 2 - 1   402         return -1 - (value ^ mask)   403     else:   404         return value   405    406 def signed2(value):   407     return signed(value, 0x8000)   408    409 def signed4(value):   410     return signed(value, 0x80000000)   411    412 # Bytecode conversion.   413    414 class BytecodeReader:   415    416     "A generic Java bytecode reader."   417    418     def __init__(self, class_file):   419         self.class_file = class_file   420         self.position_mapping = LazyDict()   421    422     def process(self, code, program):   423         self.java_position = 0   424         while self.java_position < len(code):   425             self.position_mapping[self.java_position] = program.position   426             bytecode = ord(code[self.java_position])   427             mnemonic, number_of_arguments = self.java_bytecodes[bytecode]   428             self.process_bytecode(mnemonic, number_of_arguments, code, program)   429    430     def process_bytecode(self, mnemonic, number_of_arguments, code, program):   431         if number_of_arguments is not None:   432             arguments = []   433             for j in range(0, number_of_arguments):   434                 arguments.append(ord(code[self.java_position + 1 + j]))   435    436             # Call the handler.   437             getattr(self, mnemonic)(arguments, program)   438         else:   439             # Call the handler.   440             number_of_arguments = getattr(self, mnemonic)(code[self.java_position+1:], program)   441    442         self.java_position = self.java_position + 1 + number_of_arguments   443    444     java_bytecodes = {   445         # code : (mnemonic, number of following bytes, change in stack)   446         0 : ("nop", 0),   447         1 : ("aconst_null", 0),   448         2 : ("iconst_m1", 0),   449         3 : ("iconst_0", 0),   450         4 : ("iconst_1", 0),   451         5 : ("iconst_2", 0),   452         6 : ("iconst_3", 0),   453         7 : ("iconst_4", 0),   454         8 : ("iconst_5", 0),   455         9 : ("lconst_0", 0),   456         10 : ("lconst_1", 0),   457         11 : ("fconst_0", 0),   458         12 : ("fconst_1", 0),   459         13 : ("fconst_2", 0),   460         14 : ("dconst_0", 0),   461         15 : ("dconst_1", 0),   462         16 : ("bipush", 1),   463         17 : ("sipush", 2),   464         18 : ("ldc", 1),   465         19 : ("ldc_w", 2),   466         20 : ("ldc2_w", 2),   467         21 : ("iload", 1),   468         22 : ("lload", 1),   469         23 : ("fload", 1),   470         24 : ("dload", 1),   471         25 : ("aload", 1),   472         26 : ("iload_0", 0),   473         27 : ("iload_1", 0),   474         28 : ("iload_2", 0),   475         29 : ("iload_3", 0),   476         30 : ("lload_0", 0),   477         31 : ("lload_1", 0),   478         32 : ("lload_2", 0),   479         33 : ("lload_3", 0),   480         34 : ("fload_0", 0),   481         35 : ("fload_1", 0),   482         36 : ("fload_2", 0),   483         37 : ("fload_3", 0),   484         38 : ("dload_0", 0),   485         39 : ("dload_1", 0),   486         40 : ("dload_2", 0),   487         41 : ("dload_3", 0),   488         42 : ("aload_0", 0),   489         43 : ("aload_1", 0),   490         44 : ("aload_2", 0),   491         45 : ("aload_3", 0),   492         46 : ("iaload", 0),   493         47 : ("laload", 0),   494         48 : ("faload", 0),   495         49 : ("daload", 0),   496         50 : ("aaload", 0),   497         51 : ("baload", 0),   498         52 : ("caload", 0),   499         53 : ("saload", 0),   500         54 : ("istore", 1),   501         55 : ("lstore", 1),   502         56 : ("fstore", 1),   503         57 : ("dstore", 1),   504         58 : ("astore", 1),   505         59 : ("istore_0", 0),   506         60 : ("istore_1", 0),   507         61 : ("istore_2", 0),   508         62 : ("istore_3", 0),   509         63 : ("lstore_0", 0),   510         64 : ("lstore_1", 0),   511         65 : ("lstore_2", 0),   512         66 : ("lstore_3", 0),   513         67 : ("fstore_0", 0),   514         68 : ("fstore_1", 0),   515         69 : ("fstore_2", 0),   516         70 : ("fstore_3", 0),   517         71 : ("dstore_0", 0),   518         72 : ("dstore_1", 0),   519         73 : ("dstore_2", 0),   520         74 : ("dstore_3", 0),   521         75 : ("astore_0", 0),   522         76 : ("astore_1", 0),   523         77 : ("astore_2", 0),   524         78 : ("astore_3", 0),   525         79 : ("iastore", 0),   526         80 : ("lastore", 0),   527         81 : ("fastore", 0),   528         82 : ("dastore", 0),   529         83 : ("aastore", 0),   530         84 : ("bastore", 0),   531         85 : ("castore", 0),   532         86 : ("sastore", 0),   533         87 : ("pop", 0),   534         88 : ("pop2", 0),   535         89 : ("dup", 0),   536         90 : ("dup_x1", 0),   537         91 : ("dup_x2", 0),   538         92 : ("dup2", 0),   539         93 : ("dup2_x1", 0),   540         94 : ("dup2_x2", 0),   541         95 : ("swap", 0),   542         96 : ("iadd", 0),   543         97 : ("ladd", 0),   544         98 : ("fadd", 0),   545         99 : ("dadd", 0),   546         100 : ("isub", 0),   547         101 : ("lsub", 0),   548         102 : ("fsub", 0),   549         103 : ("dsub", 0),   550         104 : ("imul", 0),   551         105 : ("lmul", 0),   552         106 : ("fmul", 0),   553         107 : ("dmul", 0),   554         108 : ("idiv", 0),   555         109 : ("ldiv", 0),   556         110 : ("fdiv", 0),   557         111 : ("ddiv", 0),   558         112 : ("irem", 0),   559         113 : ("lrem", 0),   560         114 : ("frem", 0),   561         115 : ("drem", 0),   562         116 : ("ineg", 0),   563         117 : ("lneg", 0),   564         118 : ("fneg", 0),   565         119 : ("dneg", 0),   566         120 : ("ishl", 0),   567         121 : ("lshl", 0),   568         122 : ("ishr", 0),   569         123 : ("lshr", 0),   570         124 : ("iushr", 0),   571         125 : ("lushr", 0),   572         126 : ("iand", 0),   573         127 : ("land", 0),   574         128 : ("ior", 0),   575         129 : ("lor", 0),   576         130 : ("ixor", 0),   577         131 : ("lxor", 0),   578         132 : ("iinc", 2),   579         133 : ("i2l", 0),   580         134 : ("i2f", 0),   581         135 : ("i2d", 0),   582         136 : ("l2i", 0),   583         137 : ("l2f", 0),   584         138 : ("l2d", 0),   585         139 : ("f2i", 0),   586         140 : ("f2l", 0),   587         141 : ("f2d", 0),   588         142 : ("d2i", 0),   589         143 : ("d2l", 0),   590         144 : ("d2f", 0),   591         145 : ("i2b", 0),   592         146 : ("i2c", 0),   593         147 : ("i2s", 0),   594         148 : ("lcmp", 0),   595         149 : ("fcmpl", 0),   596         150 : ("fcmpg", 0),   597         151 : ("dcmpl", 0),   598         152 : ("dcmpg", 0),   599         153 : ("ifeq", 2),   600         154 : ("ifne", 2),   601         155 : ("iflt", 2),   602         156 : ("ifge", 2),   603         157 : ("ifgt", 2),   604         158 : ("ifle", 2),   605         159 : ("if_icmpeq", 2),   606         160 : ("if_icmpne", 2),   607         161 : ("if_icmplt", 2),   608         162 : ("if_icmpge", 2),   609         163 : ("if_icmpgt", 2),   610         164 : ("if_icmple", 2),   611         165 : ("if_acmpeq", 2),   612         166 : ("if_acmpne", 2),   613         167 : ("goto", 2),   614         168 : ("jsr", 2),   615         169 : ("ret", 1),   616         170 : ("tableswitch", None), # variable number of arguments   617         171 : ("lookupswitch", None), # variable number of arguments   618         172 : ("ireturn", 0),   619         173 : ("lreturn", 0),   620         174 : ("freturn", 0),   621         175 : ("dreturn", 0),   622         176 : ("areturn", 0),   623         177 : ("return_", 0),   624         178 : ("getstatic", 2),   625         179 : ("putstatic", 2),   626         180 : ("getfield", 2),   627         181 : ("putfield", 2),   628         182 : ("invokevirtual", 2),   629         183 : ("invokespecial", 2),   630         184 : ("invokestatic", 2),   631         185 : ("invokeinterface", 4),   632         187 : ("new", 2),   633         188 : ("newarray", 1),   634         189 : ("anewarray", 2),   635         190 : ("arraylength", 0),   636         191 : ("athrow", 0),   637         192 : ("checkcast", 2),   638         193 : ("instanceof", 2),   639         194 : ("monitorenter", 0),   640         195 : ("monitorexit", 0),   641         196 : ("wide", None), # 3 or 5 arguments, stack changes according to modified element   642         197 : ("multianewarray", 3),   643         198 : ("ifnull", 2),   644         199 : ("ifnonnull", 2),   645         200 : ("goto_w", 4),   646         201 : ("jsr_w", 4),   647         }   648    649 class BytecodeDisassembler(BytecodeReader):   650    651     "A Java bytecode disassembler."   652    653     bytecode_methods = [spec[0] for spec in BytecodeReader.java_bytecodes.values()]   654    655     def __getattr__(self, name):   656         if name in self.bytecode_methods:   657             print name,   658             return self.generic   659         else:   660             raise AttributeError, name   661    662     def generic(self, arguments, program):   663         print arguments   664    665 class BytecodeDisassemblerProgram:   666     position = 0   667    668 class BytecodeTranslator(BytecodeReader):   669    670     "A Java bytecode translator which uses a Python bytecode writer."   671    672     def aaload(self, arguments, program):   673         # NOTE: No type checking performed.   674         program.binary_subscr()   675    676     def aastore(self, arguments, program):   677         # NOTE: No type checking performed.   678         # Stack: arrayref, index, value   679         program.rot_three() # Stack: value, arrayref, index   680         program.store_subscr()   681    682     def aconst_null(self, arguments, program):   683         program.load_global(None)   684    685     def aload(self, arguments, program):   686         program.load_fast(arguments[0])   687    688     def aload_0(self, arguments, program):   689         program.load_fast(0)   690    691     def aload_1(self, arguments, program):   692         program.load_fast(1)   693    694     def aload_2(self, arguments, program):   695         program.load_fast(2)   696    697     def aload_3(self, arguments, program):   698         program.load_fast(3)   699    700     def anewarray(self, arguments, program):   701         # NOTE: Does not raise NegativeArraySizeException.   702         # NOTE: Not using the index to type the list/array.   703         index = (arguments[0] << 8) + arguments[1]   704         self._newarray(program)   705    706     def _newarray(self, program):   707         program.build_list()        # Stack: count, list   708         program.rot_two()           # Stack: list, count   709         program.setup_loop()   710         program.load_global("range")   711         program.load_const(0)       # Stack: list, count, range, 0   712         program.rot_three()         # Stack: list, 0, count, range   713         program.rot_three()         # Stack: list, range, 0, count   714         program.call_function(2)    # Stack: list, range_list   715         program.get_iter()          # Stack: list, iter   716         program.for_iter()          # Stack: list, iter, value   717         program.pop_top()           # Stack: list, iter   718         program.rot_two()           # Stack: iter, list   719         program.dup_top()           # Stack: iter, list, list   720         program.load_attr("append") # Stack: iter, list, append   721         program.load_global(None)   # Stack: iter, list, append, None   722         program.call_function(1)    # Stack: iter, list, None   723         program.pop_top()           # Stack: iter, list   724         program.rot_two()           # Stack: list, iter   725         program.end_loop()          # Back to for_iter above   726    727     def areturn(self, arguments, program):   728         program.return_value()   729    730     def arraylength(self, arguments, program):   731         program.load_global("len")  # Stack: arrayref, len   732         program.rot_two()           # Stack: len, arrayref   733         program.call_function(1)   734    735     def astore(self, arguments, program):   736         program.store_fast(arguments[0])   737    738     def astore_0(self, arguments, program):   739         program.store_fast(0)   740    741     def astore_1(self, arguments, program):   742         program.store_fast(1)   743    744     def astore_2(self, arguments, program):   745         program.store_fast(2)   746    747     def astore_3(self, arguments, program):   748         program.store_fast(3)   749    750     def athrow(self, arguments, program):   751         # NOTE: NullPointerException not raised where null/None is found on the stack.   752         program.raise_varargs(1)   753    754     baload = aaload   755     bastore = aastore   756    757     def bipush(self, arguments, program):   758         program.load_const(arguments[0])   759    760     caload = aaload   761     castore = aastore   762    763     def checkcast(self, arguments, program):   764         index = (arguments[0] << 8) + arguments[1]   765         target_name = self.class_file.constants[index - 1].get_name()   766         target_components = target_name.split("/")   767    768         program.dup_top()                   # Stack: objectref, objectref   769         program.load_global("isinstance")   # Stack: objectref, objectref, isinstance   770         program.rot_two()                   # Stack: objectref, isinstance, objectref   771         program.load_global(target_components[0])   772         for target_component in target_components[1:]:   773             program.load_attr(target_component)   774         program.call_function(2)            # Stack: objectref   775    776     def d2f(self, arguments, program):   777         pass   778    779     def d2i(self, arguments, program):   780         program.load_global("int")  # Stack: value, int   781         program.rot_two()           # Stack: int, value   782         program.call_function(1)    # Stack: result   783    784     d2l = d2i # Preserving Java semantics   785    786     def dadd(self, arguments, program):   787         # NOTE: No type checking performed.   788         program.binary_add()   789    790     daload = aaload   791     dastore = aastore   792    793     def dcmpg(self, arguments, program):   794         # NOTE: No type checking performed.   795         program.compare_op(">")   796    797     def dcmpl(self, arguments, program):   798         # NOTE: No type checking performed.   799         program.compare_op("<")   800    801     def dconst_0(self, arguments, program):   802         program.load_const(0.0)   803    804     def dconst_1(self, arguments, program):   805         program.load_const(1.0)   806    807     def ddiv(self, arguments, program):   808         # NOTE: No type checking performed.   809         program.binary_divide()   810    811     dload = aload   812     dload_0 = aload_0   813     dload_1 = aload_1   814     dload_2 = aload_2   815     dload_3 = aload_3   816    817     def dmul(self, arguments, program):   818         # NOTE: No type checking performed.   819         program.binary_multiply()   820    821     def dneg(self, arguments, program):   822         # NOTE: No type checking performed.   823         program.unary_negative()   824    825     def drem(self, arguments, program):   826         # NOTE: No type checking performed.   827         program.binary_modulo()   828    829     dreturn = areturn   830     dstore = astore   831     dstore_0 = astore_0   832     dstore_1 = astore_1   833     dstore_2 = astore_2   834     dstore_3 = astore_3   835    836     def dsub(self, arguments, program):   837         # NOTE: No type checking performed.   838         program.binary_subtract()   839    840     def dup(self, arguments, program):   841         program.dup_top()   842    843     def dup_x1(self, arguments, program):   844         # Ignoring computational type categories.   845         program.dup_top()   846         program.rot_three()   847    848     def dup_x2(self, arguments, program):   849         # Ignoring computational type categories.   850         program.dup_top()   851         program.rot_four()   852    853     dup2 = dup # Ignoring computational type categories   854     dup2_x1 = dup_x1 # Ignoring computational type categories   855     dup2_x2 = dup_x2 # Ignoring computational type categories   856    857     def f2d(self, arguments, program):   858         pass # Preserving Java semantics   859    860     def f2i(self, arguments, program):   861         program.load_global("int")  # Stack: value, int   862         program.rot_two()           # Stack: int, value   863         program.call_function(1)    # Stack: result   864    865     f2l = f2i # Preserving Java semantics   866     fadd = dadd   867     faload = daload   868     fastore = dastore   869     fcmpg = dcmpg   870     fcmpl = dcmpl   871     fconst_0 = dconst_0   872     fconst_1 = dconst_1   873    874     def fconst_2(self, arguments, program):   875         program.load_const(2.0)   876    877     fdiv = ddiv   878     fload = dload   879     fload_0 = dload_0   880     fload_1 = dload_1   881     fload_2 = dload_2   882     fload_3 = dload_3   883     fmul = dmul   884     fneg = dneg   885     frem = drem   886     freturn = dreturn   887     fstore = dstore   888     fstore_0 = dstore_0   889     fstore_1 = dstore_1   890     fstore_2 = dstore_2   891     fstore_3 = dstore_3   892     fsub = dsub   893    894     def getfield(self, arguments, program):   895         index = (arguments[0] << 8) + arguments[1]   896         target_name = self.class_file.constants[index - 1].get_name()   897         # NOTE: Using the string version of the name which may contain incompatible characters.   898         program.load_attr(str(target_name))   899    900     def getstatic(self, arguments, program):   901         index = (arguments[0] << 8) + arguments[1]   902         target_name = self.class_file.constants[index - 1].get_name()   903         program.load_name("self")   904         program.load_attr("__class__")   905         # NOTE: Using the string version of the name which may contain incompatible characters.   906         program.load_attr(str(target_name))   907    908     def goto(self, arguments, program):   909         offset = signed2((arguments[0] << 8) + arguments[1])   910         java_absolute = self.java_position + offset   911         program.jump_absolute(self.position_mapping[java_absolute])   912    913     def goto_w(self, arguments, program):   914         offset = signed4((arguments[0] << 24) + (arguments[1] << 16) + (arguments[2] << 8) + arguments[3])   915         java_absolute = self.java_position + offset   916         program.jump_absolute(self.position_mapping[java_absolute])   917    918     def i2b(self, arguments, program):   919         pass   920    921     def i2c(self, arguments, program):   922         program.load_global("chr")  # Stack: value, chr   923         program.rot_two()           # Stack: chr, value   924         program.call_function(1)    # Stack: result   925    926     def i2d(self, arguments, program):   927         program.load_global("float")    # Stack: value, float   928         program.rot_two()               # Stack: float, value   929         program.call_function(1)        # Stack: result   930    931     i2f = i2d # Not distinguishing between float and double   932    933     def i2l(self, arguments, program):   934         pass # Preserving Java semantics   935    936     def i2s(self, arguments, program):   937         pass # Not distinguishing between int and short   938    939     iadd = fadd   940     iaload = faload   941    942     def iand(self, arguments, program):   943         # NOTE: No type checking performed.   944         program.binary_and()   945    946     iastore = fastore   947    948     def iconst_m1(self, arguments, program):   949         program.load_const(-1)   950    951     def iconst_0(self, arguments, program):   952         program.load_const(0)   953    954     def iconst_1(self, arguments, program):   955         program.load_const(1)   956    957     def iconst_2(self, arguments, program):   958         program.load_const(2)   959    960     def iconst_3(self, arguments, program):   961         program.load_const(3)   962    963     def iconst_4(self, arguments, program):   964         program.load_const(4)   965    966     def iconst_5(self, arguments, program):   967         program.load_const(5)   968    969     idiv = fdiv   970    971     def _if_xcmpx(self, arguments, program, op):   972         offset = signed2((arguments[0] << 8) + arguments[1])   973         java_absolute = self.java_position + offset   974         program.compare_op(op)   975         program.jump_to_label(0, "next") # skip if false   976         program.goto(offset)   977         program.start_label("next")   978    979     def if_acmpeq(self, arguments, program):   980         # NOTE: No type checking performed.   981         self._if_xcmpx(arguments, program, "is")   982    983     def if_acmpne(self, arguments, program):   984         # NOTE: No type checking performed.   985         self._if_xcmpx(arguments, program, "is not")   986    987     def if_icmpeq(self, arguments, program):   988         # NOTE: No type checking performed.   989         self._if_xcmpx(arguments, program, "==")   990    991     def if_icmpne(self, arguments, program):   992         # NOTE: No type checking performed.   993         self._if_xcmpx(arguments, program, "!=")   994    995     def if_icmplt(self, arguments, program):   996         # NOTE: No type checking performed.   997         self._if_xcmpx(arguments, program, "<")   998    999     def if_icmpge(self, arguments, program):  1000         # NOTE: No type checking performed.  1001         self._if_xcmpx(arguments, program, ">=")  1002   1003     def if_icmpgt(self, arguments, program):  1004         # NOTE: No type checking performed.  1005         self._if_xcmpx(arguments, program, ">")  1006   1007     def if_icmple(self, arguments, program):  1008         # NOTE: No type checking performed.  1009         self._if_xcmpx(arguments, program, "<=")  1010   1011     def ifeq(self, arguments, program):  1012         # NOTE: No type checking performed.  1013         program.load_const(0)  1014         self._if_xcmpx(arguments, program, "==")  1015   1016     def ifne(self, arguments, program):  1017         # NOTE: No type checking performed.  1018         program.load_const(0)  1019         self._if_xcmpx(arguments, program, "!=")  1020   1021     def iflt(self, arguments, program):  1022         # NOTE: No type checking performed.  1023         program.load_const(0)  1024         self._if_xcmpx(arguments, program, "<")  1025   1026     def ifge(self, arguments, program):  1027         # NOTE: No type checking performed.  1028         program.load_const(0)  1029         self._if_xcmpx(arguments, program, ">=")  1030   1031     def ifgt(self, arguments, program):  1032         # NOTE: No type checking performed.  1033         program.load_const(0)  1034         self._if_xcmpx(arguments, program, ">")  1035   1036     def ifle(self, arguments, program):  1037         # NOTE: No type checking performed.  1038         program.load_const(0)  1039         self._if_xcmpx(arguments, program, "<=")  1040   1041     def ifnonnull(self, arguments, program):  1042         # NOTE: No type checking performed.  1043         program.load_const(None)  1044         self._if_xcmpx(arguments, program, "is not")  1045   1046     def ifnull(self, arguments, program):  1047         # NOTE: No type checking performed.  1048         program.load_const(None)  1049         self._if_xcmpx(arguments, program, "is")  1050   1051     def iinc(self, arguments, program):  1052         # NOTE: No type checking performed.  1053         program.load_fast(arguments[0])  1054         program.load_const(arguments[1])  1055         program.binary_add()  1056   1057     iload = fload  1058     iload_0 = fload_0  1059     iload_1 = fload_1  1060     iload_2 = fload_2  1061     iload_3 = fload_3  1062     imul = fmul  1063     ineg = fneg  1064   1065     def instanceof(self, arguments, program):  1066         index = (arguments[0] << 8) + arguments[1]  1067         target_name = self.class_file.constants[index - 1].get_name()  1068         target_components = target_name.split("/")  1069   1070         program.load_global("isinstance")   # Stack: objectref, isinstance  1071         program.rot_two()                   # Stack: isinstance, objectref  1072         program.load_global(target_components[0])  1073         for target_component in target_components[1:]:  1074             program.load_attr(target_component)  1075         program.call_function(2)            # Stack: result  1076   1077     def _invoke(self, target_name, program):  1078         # NOTE: Using the string version of the name which may contain incompatible characters.  1079         program.load_attr(str(target_name)) # Stack: tuple, method  1080         program.rot_two()                   # Stack: method, tuple  1081         program.load_global("apply")        # Stack: method, tuple, apply  1082         program.rot_three()                 # Stack: apply, method, tuple  1083         program.call_function(2)  1084   1085     def invokeinterface(self, arguments, program):  1086         # NOTE: This implementation does not perform the necessary checks for  1087         # NOTE: signature-based polymorphism.  1088         # NOTE: Java rules not specifically obeyed.  1089         index = (arguments[0] << 8) + arguments[1]  1090         count = arguments[2]  1091         target_name = self.class_file.constants[index - 1].get_name()  1092         # Stack: objectref, arg1, arg2, ...  1093         program.build_tuple(count)          # Stack: objectref, tuple  1094         program.rot_two()                   # Stack: tuple, objectref  1095         self._invoke(target_name, program)  1096   1097     def invokespecial(self, arguments, program):  1098         # NOTE: This implementation does not perform the necessary checks for  1099         # NOTE: signature-based polymorphism.  1100         # NOTE: Java rules not specifically obeyed.  1101         index = (arguments[0] << 8) + arguments[1]  1102         target = self.class_file.constants[index - 1]  1103         target_name = target.get_name()  1104         # Get the number of parameters from the descriptor.  1105         count = len(target.get_descriptor()[0])  1106         # Stack: objectref, arg1, arg2, ...  1107         program.build_tuple(count + 1)  # Stack: tuple  1108         # Use the class to provide access to static methods.  1109         program.load_name("self")       # Stack: tuple, self  1110         program.load_attr("__class__")  # Stack: tuple, class  1111         program.load_attr("__bases__")  # Stack: tuple, base-classes  1112         program.dup_top()               # Stack: tuple, base-classes, base-classes  1113         program.load_global("len")      # Stack: tuple, base-classes, base-classes, len  1114         program.rot_two()               # Stack: tuple, base-classes, len, base-classes  1115         program.call_function(1)        # Stack: tuple, base-classes, count  1116         program.load_const(0)           # Stack: tuple, base-classes, count, 0  1117         program.compare_op("==")        # Stack: tuple, base-classes, result  1118         program.jump_to_label(1, "next")  1119         program.pop_top()               # Stack: tuple, base-classes  1120         program.load_const(0)           # Stack: tuple, base-classes, 0  1121         program.binary_subscr()         # Stack: tuple, superclass  1122         self._invoke(target_name, program)  1123         program.jump_to_label(None, "next2")  1124         program.start_label("next")  1125         program.pop_top()               # Stack: tuple, base-classes  1126         program.pop_top()               # Stack: tuple  1127         program.pop_top()               # Stack:  1128         program.start_label("next2")  1129   1130     def invokestatic(self, arguments, program):  1131         # NOTE: This implementation does not perform the necessary checks for  1132         # NOTE: signature-based polymorphism.  1133         # NOTE: Java rules not specifically obeyed.  1134         index = (arguments[0] << 8) + arguments[1]  1135         target = self.class_file.constants[index - 1]  1136         target_name = target.get_name()  1137         # Get the number of parameters from the descriptor.  1138         count = len(target.get_descriptor()[0])  1139         # Stack: arg1, arg2, ...  1140         program.build_tuple(count)      # Stack: tuple  1141         # Use the class to provide access to static methods.  1142         program.load_name("self")       # Stack: tuple, self  1143         program.load_attr("__class__")  # Stack: tuple, class  1144         self._invoke(target_name, program)  1145   1146     invokevirtual = invokeinterface # Ignoring Java rules  1147   1148     def ior(self, arguments, program):  1149         # NOTE: No type checking performed.  1150         program.binary_or()  1151   1152     irem = frem  1153     ireturn = freturn  1154   1155     def ishl(self, arguments, program):  1156         # NOTE: No type checking performed.  1157         # NOTE: Not verified.  1158         program.binary_lshift()  1159   1160     def ishr(self, arguments, program):  1161         # NOTE: No type checking performed.  1162         # NOTE: Not verified.  1163         program.binary_rshift()  1164   1165     istore = fstore  1166     istore_0 = fstore_0  1167     istore_1 = fstore_1  1168     istore_2 = fstore_2  1169     istore_3 = fstore_3  1170     isub = fsub  1171     iushr = ishr # Ignoring distinctions between arithmetic and logical shifts  1172   1173     def ixor(self, arguments, program):  1174         # NOTE: No type checking performed.  1175         program.binary_xor()  1176   1177     def jsr(self, arguments, program):  1178         offset = signed2((arguments[0] << 8) + arguments[1])  1179         java_absolute = self.java_position + offset  1180         # Store the address of the next instruction.  1181         program.load_const_ret(self.position_mapping[self.java_position + 3])  1182         program.jump_absolute(self.position_mapping[java_absolute])  1183   1184     def jsr_w(self, arguments, program):  1185         offset = signed4((arguments[0] << 24) + (arguments[1] << 16) + (arguments[2] << 8) + arguments[3])  1186         java_absolute = self.java_position + offset  1187         # Store the address of the next instruction.  1188         program.load_const_ret(self.position_mapping[self.java_position + 5])  1189         program.jump_absolute(self.position_mapping[java_absolute])  1190   1191     l2d = i2d  1192     l2f = i2f  1193   1194     def l2i(self, arguments, program):  1195         pass # Preserving Java semantics  1196   1197     ladd = iadd  1198     laload = iaload  1199     land = iand  1200     lastore = iastore  1201   1202     def lcmp(self, arguments, program):  1203         # NOTE: No type checking performed.  1204         program.dup_topx(2)                 # Stack: value1, value2, value1, value2  1205         program.compare_op(">")             # Stack: value1, value2, result  1206         program.jump_to_label(0, "equals")  1207         # True - produce result and branch.  1208         program.pop_top()                   # Stack: value1, value2  1209         program.pop_top()                   # Stack: value1  1210         program.pop_top()                   # Stack:  1211         program.load_const(1)               # Stack: 1  1212         program.jump_to_label(None, "next")  1213         # False - test equality.  1214         program.start_label("equals")  1215         program.pop_top()                   # Stack: value1, value2  1216         program.dup_topx(2)                 # Stack: value1, value2, value1, value2  1217         program.compare_op("==")            # Stack: value1, value2, result  1218         program.jump_to_label(0, "less")  1219         # True - produce result and branch.  1220         program.pop_top()                   # Stack: value1, value2  1221         program.pop_top()                   # Stack: value1  1222         program.pop_top()                   # Stack:  1223         program.load_const(0)               # Stack: 0  1224         program.jump_to_label(None, "next")  1225         # False - produce result.  1226         program.start_label("less")  1227         program.pop_top()                   # Stack: value1, value2  1228         program.pop_top()                   # Stack: value1  1229         program.pop_top()                   # Stack:  1230         program.load_const(-1)              # Stack: -1  1231         program.start_label("next")  1232   1233     lconst_0 = iconst_0  1234     lconst_1 = iconst_1  1235   1236     def ldc(self, arguments, program):  1237         program.load_const(self.class_file.constants[arguments[0] - 1])  1238   1239     def ldc_w(self, arguments, program):  1240         program.load_const(self.class_file.constants[(arguments[0] << 8) + arguments[1] - 1])  1241   1242     ldc2_w = ldc_w  1243     ldiv = idiv  1244     lload = iload  1245     lload_0 = iload_0  1246     lload_1 = iload_1  1247     lload_2 = iload_2  1248     lload_3 = iload_3  1249     lmul = imul  1250     lneg = ineg  1251   1252     def lookupswitch(self, arguments, program):  1253         # Find the offset to the next 4 byte boundary in the code.  1254         d, r = divmod(self.java_position, 4)  1255         to_boundary = (4 - r) % 4  1256         # Get the pertinent arguments.  1257         arguments = arguments[to_boundary:]  1258         default = (arguments[0] << 24) + (arguments[1] << 16) + (arguments[2] << 8) + arguments[3]  1259         npairs = (arguments[4] << 24) + (arguments[5] << 16) + (arguments[6] << 8) + arguments[7]  1260         # Process the pairs.  1261         # NOTE: This is not the most optimal implementation.  1262         pair_index = 8  1263         for pair in range(0, npairs):  1264             match = ((arguments[pair_index] << 24) + (arguments[pair_index + 1] << 16) +  1265                 (arguments[pair_index + 2] << 8) + arguments[pair_index + 3])  1266             offset = signed4((arguments[pair_index + 4] << 24) + (arguments[pair_index + 5] << 16) +  1267                 (arguments[pair_index + 6] << 8) + arguments[pair_index + 7])  1268             # Calculate the branch target.  1269             java_absolute = self.java_position + offset  1270             # Generate branching code.  1271             program.dup_top()                                           # Stack: key, key  1272             program.load_const(match)                                   # Stack: key, key, match  1273             program.compare_op("==")                                    # Stack: key, result  1274             program.jump_to_label(0, "end")  1275             program.pop_top()                                           # Stack: key  1276             program.pop_top()                                           # Stack:  1277             program.jump_absolute(self.position_mapping[java_absolute])  1278             # Generate the label for the end of the branching code.  1279             program.start_label("end")  1280             program.pop_top()                                           # Stack: key  1281             # Update the index.  1282             pair_index += 8  1283         # Generate the default.  1284         java_absolute = self.java_position + default  1285         program.jump_absolute(self.position_mapping[java_absolute])  1286   1287     lor = ior  1288     lrem = irem  1289     lreturn = ireturn  1290     lshl = ishl  1291     lshr = ishr  1292     lstore = istore  1293     lstore_0 = istore_0  1294     lstore_1 = istore_1  1295     lstore_2 = istore_2  1296     lstore_3 = istore_3  1297     lsub = isub  1298     lushr = iushr  1299     lxor = ixor  1300   1301     def monitorenter(self, arguments, program):  1302         # NOTE: To be implemented.  1303         pass  1304   1305     def monitorexit(self, arguments, program):  1306         # NOTE: To be implemented.  1307         pass  1308   1309     def multianewarray(self, arguments, program):  1310         # NOTE: To be implemented.  1311         pass  1312   1313     def new(self, arguments, program):  1314         # This operation is considered to be the same as the calling of the  1315         # initialisation method of the given class with no arguments.  1316         index = (arguments[0] << 8) + arguments[1]  1317         target_name = self.class_file.constants[index - 1].get_name()  1318         # NOTE: Using the string version of the name which may contain incompatible characters.  1319         program.load_global(str(target_name))  1320         program.call_function(0)  1321   1322     def newarray(self, arguments, program):  1323         # NOTE: Does not raise NegativeArraySizeException.  1324         # NOTE: Not using the arguments to type the list/array.  1325         self._newarray(program)  1326   1327     def nop(self, arguments, program):  1328         pass  1329   1330     def pop(self, arguments, program):  1331         program.pop_top()  1332   1333     pop2 = pop # ignoring Java stack value distinctions  1334   1335     def putfield(self, arguments, program):  1336         index = (arguments[0] << 8) + arguments[1]  1337         target_name = self.class_file.constants[index - 1].get_name()  1338         program.rot_two()  1339         # NOTE: Using the string version of the name which may contain incompatible characters.  1340         program.store_attr(str(target_name))  1341   1342     def putstatic(self, arguments, program):  1343         index = (arguments[0] << 8) + arguments[1]  1344         target_name = self.class_file.constants[index - 1].get_name()  1345         program.load_name("self")  1346         program.load_attr("__class__")  1347         # NOTE: Using the string version of the name which may contain incompatible characters.  1348         program.store_attr(str(target_name))  1349   1350     def ret(self, arguments, program):  1351         program.ret(arguments[0])  1352   1353     def return_(self, arguments, program):  1354         program.load_const(None)  1355         program.return_value()  1356   1357     saload = laload  1358     sastore = lastore  1359   1360     def sipush(self, arguments, program):  1361         program.load_const((arguments[0] << 8) + arguments[1])  1362   1363     def swap(self, arguments, program):  1364         program.rot_two()  1365   1366     def tableswitch(self, arguments, program):  1367         # Find the offset to the next 4 byte boundary in the code.  1368         d, r = divmod(self.java_position, 4)  1369         to_boundary = (4 - r) % 4  1370         # Get the pertinent arguments.  1371         arguments = arguments[to_boundary:]  1372         default = (arguments[0] << 24) + (arguments[1] << 16) + (arguments[2] << 8) + arguments[3]  1373         low = (arguments[4] << 24) + (arguments[5] << 16) + (arguments[6] << 8) + arguments[7]  1374         high = (arguments[8] << 24) + (arguments[9] << 16) + (arguments[10] << 8) + arguments[11]  1375         # Process the jump entries.  1376         # NOTE: This is not the most optimal implementation.  1377         jump_index = 8  1378         for jump in range(low, high + 1):  1379             offset = signed4((arguments[jump_index] << 24) + (arguments[jump_index + 1] << 16) +  1380                 (arguments[jump_index + 2] << 8) + arguments[jump_index + 3])  1381             # Calculate the branch target.  1382             java_absolute = self.java_position + offset  1383             # Generate branching code.  1384             program.dup_top()                                           # Stack: key, key  1385             program.load_const(jump)                                    # Stack: key, key, jump  1386             program.compare_op("==")                                    # Stack: key, result  1387             program.jump_to_label(0, "end")  1388             program.pop_top()                                           # Stack: key  1389             program.pop_top()                                           # Stack:  1390             program.jump_absolute(self.position_mapping[java_absolute])  1391             # Generate the label for the end of the branching code.  1392             program.start_label("end")  1393             program.pop_top()                                           # Stack: key  1394             # Update the index.  1395             jump_index += 8  1396         # Generate the default.  1397         java_absolute = self.java_position + default  1398         program.jump_absolute(self.position_mapping[java_absolute])  1399   1400     def wide(self, code, program):  1401         # NOTE: To be implemented.  1402         return number_of_arguments  1403   1404 def disassemble(class_file, code):  1405     disassembler = BytecodeDisassembler(class_file)  1406     disassembler.process(code, BytecodeDisassemblerProgram())  1407   1408 def translate(class_file, code):  1409     translator = BytecodeTranslator(class_file)  1410     writer = BytecodeWriter()  1411     translator.process(code, writer)  1412     return writer  1413   1414 if __name__ == "__main__":  1415     import sys  1416     from classfile import ClassFile  1417     f = open(sys.argv[1])  1418     c = ClassFile(f.read())  1419   1420 # vim: tabstop=4 expandtab shiftwidth=4