javaclass

Changeset

15:bd70c69bd9ae
2004-11-09 Paul Boddie raw files shortlog changelog graph Added local variable usage tracking. Changed the invokespecial implementation, since it probably has very little to do with superclass method invocation specifically. Extended the test program to try and assemble an entire class. Attempted to fix block gymnastics for exceptions.
bytecode.py (file)
     1.1 --- a/bytecode.py	Tue Nov 09 18:45:57 2004 +0100
     1.2 +++ b/bytecode.py	Tue Nov 09 18:52:06 2004 +0100
     1.3 @@ -36,6 +36,9 @@
     1.4          self.stack_depth = 0
     1.5          self.max_stack_depth = 0
     1.6  
     1.7 +        # Local variable estimation.
     1.8 +        self.max_locals = 0
     1.9 +
    1.10          # Mapping from values to indexes.
    1.11          self.constants = {}
    1.12  
    1.13 @@ -94,6 +97,10 @@
    1.14          if self.stack_depth > self.max_stack_depth:
    1.15              self.max_stack_depth = self.stack_depth
    1.16  
    1.17 +    def update_locals(self, index):
    1.18 +        if index > self.max_locals:
    1.19 +            self.max_locals = index
    1.20 +
    1.21      # Special methods.
    1.22  
    1.23      def _write_value(self, value):
    1.24 @@ -157,6 +164,7 @@
    1.25      def setup_except(self, target):
    1.26          self.blocks.append(self.position)
    1.27          self.exception_handlers.append(target)
    1.28 +        print "-", self.position, target
    1.29          self.output.append(opmap["SETUP_EXCEPT"])
    1.30          self.position += 1
    1.31          self._write_value(0) # To be filled in later
    1.32 @@ -164,6 +172,7 @@
    1.33      def setup_finally(self, target):
    1.34          self.blocks.append(self.position)
    1.35          self.exception_handlers.append(target)
    1.36 +        print "-", self.position, target
    1.37          self.output.append(opmap["SETUP_FINALLY"])
    1.38          self.position += 1
    1.39          self._write_value(0) # To be filled in later
    1.40 @@ -173,6 +182,7 @@
    1.41          # Convert the "lazy" absolute value.
    1.42          current_exception_target = self.exception_handlers.pop()
    1.43          target = current_exception_target.get_value()
    1.44 +        print "*", current_exception_start, target
    1.45          # NOTE: Using 3 as the assumed length of the SETUP_* instruction.
    1.46          # NOTE: 8-bit limit.
    1.47          self.output[current_exception_start + 1] = target - current_exception_start - 3
    1.48 @@ -217,6 +227,7 @@
    1.49          self.position += 1
    1.50          self._write_value(index)
    1.51          self.update_stack_depth(1)
    1.52 +        self.update_locals(index)
    1.53  
    1.54      def store_attr(self, name):
    1.55          self.output.append(opmap["STORE_ATTR"])
    1.56 @@ -231,6 +242,7 @@
    1.57          self.position += 1
    1.58          self._write_value(index)
    1.59          self.update_stack_depth(-1)
    1.60 +        self.update_locals(index)
    1.61  
    1.62      # Normal bytecode generators.
    1.63  
    1.64 @@ -490,14 +502,21 @@
    1.65                  else:
    1.66                      program.setup_except(self.position_mapping[exception.handler_pc])
    1.67  
    1.68 -            # Insert exception handler end details.
    1.69 +            # Insert exception block end details.
    1.70              for exception in exception_block_end.get(self.java_position, []):
    1.71                  # NOTE: Insert jump beyond handlers.
    1.72                  # NOTE: program.jump_forward/absolute(...)
    1.73 +                # NOTE: Insert end finally at end of handlers as well as where "ret" occurs.
    1.74 +                if exception.catch_type != 0:
    1.75 +                    program.pop_block()
    1.76 +
    1.77 +            # Insert exception handler details.
    1.78 +            # NOTE: Ensure that pop_block is reachable by possibly inserting it at the start of finally handlers.
    1.79 +            # NOTE: Insert a check for the correct exception at the start of each handler.
    1.80 +            for exception in exception_block_handler.get(self.java_position, []):
    1.81                  program.end_exception()
    1.82 -                # NOTE: Insert a check for the correct exception at the start of each handler.
    1.83 -                # NOTE: Insert end finally at end of handlers as well as where "ret" occurs.
    1.84 -                # NOTE: Ensure that pop_block is reachable by possibly inserting it at the start of finally handlers.
    1.85 +                if exception.catch_type == 0:
    1.86 +                    program.pop_block()
    1.87  
    1.88              # Where handlers are begun, do not produce equivalent bytecode since
    1.89              # the first handler instruction typically involves saving a local
    1.90 @@ -510,10 +529,6 @@
    1.91              number_of_arguments = self.process_bytecode(mnemonic, number_of_arguments, code, program)
    1.92              next_java_position = self.java_position + 1 + number_of_arguments
    1.93  
    1.94 -            # Insert exception handler end instructions.
    1.95 -            for exception in exception_block_end.get(next_java_position, []):
    1.96 -                program.pop_block()
    1.97 -
    1.98              # Only advance the JVM position after sneaking in extra Python
    1.99              # instructions.
   1.100              self.java_position = next_java_position
   1.101 @@ -1072,7 +1087,7 @@
   1.102          java_absolute = self.java_position + offset
   1.103          program.compare_op(op)
   1.104          program.jump_to_label(0, "next") # skip if false
   1.105 -        program.goto(offset)
   1.106 +        program.jump_absolute(self.position_mapping[java_absolute])
   1.107          program.start_label("next")
   1.108  
   1.109      def if_acmpeq(self, arguments, program):
   1.110 @@ -1204,6 +1219,21 @@
   1.111          # Get the number of parameters from the descriptor.
   1.112          count = len(target.get_descriptor()[0])
   1.113          # Stack: objectref, arg1, arg2, ...
   1.114 +        program.build_tuple(count)          # Stack: objectref, tuple
   1.115 +        program.rot_two()                   # Stack: tuple, objectref
   1.116 +        self._invoke(target_name, program)
   1.117 +
   1.118 +    """
   1.119 +    def invokespecial(self, arguments, program):
   1.120 +        # NOTE: This implementation does not perform the necessary checks for
   1.121 +        # NOTE: signature-based polymorphism.
   1.122 +        # NOTE: Java rules not specifically obeyed.
   1.123 +        index = (arguments[0] << 8) + arguments[1]
   1.124 +        target = self.class_file.constants[index - 1]
   1.125 +        target_name = target.get_python_name()
   1.126 +        # Get the number of parameters from the descriptor.
   1.127 +        count = len(target.get_descriptor()[0])
   1.128 +        # Stack: objectref, arg1, arg2, ...
   1.129          program.build_tuple(count + 1)  # Stack: tuple
   1.130          # Use the class to provide access to static methods.
   1.131          program.load_name("self")       # Stack: tuple, self
   1.132 @@ -1226,6 +1256,7 @@
   1.133          program.pop_top()               # Stack: tuple
   1.134          program.pop_top()               # Stack:
   1.135          program.start_label("next2")
   1.136 +    """
   1.137  
   1.138      def invokestatic(self, arguments, program):
   1.139          # NOTE: This implementation does not perform the necessary checks for
   1.140 @@ -1511,10 +1542,38 @@
   1.141      translator.process(code, exception_table, writer)
   1.142      return translator, writer
   1.143  
   1.144 +def make_varnames(nlocals):
   1.145 +    l = ["self"]
   1.146 +    for i in range(1, nlocals):
   1.147 +        l.append("_l%s" % i)
   1.148 +    return l[:nlocals]
   1.149 +
   1.150 +def __java_init__(self, *args):
   1.151 +    print "<init>", args
   1.152 +
   1.153  if __name__ == "__main__":
   1.154      import sys
   1.155      from classfile import ClassFile
   1.156      f = open(sys.argv[1])
   1.157      c = ClassFile(f.read())
   1.158 +    import dis, new
   1.159 +    namespace = {}
   1.160 +    for method in c.methods:
   1.161 +        attribute = method.attributes[0]
   1.162 +        nargs = len(method.get_descriptor()[0]) + 1
   1.163 +        t, w = translate(c, attribute.code, attribute.exception_table)
   1.164 +        nlocals = w.max_locals + 1
   1.165 +        filename = str(c.attributes[0].get_name())
   1.166 +        method_name = str(method.get_name())
   1.167 +        if method_name == "<init>":
   1.168 +            method_name = "__init__"
   1.169 +        code = new.code(nargs, nlocals, w.max_stack_depth, 67, w.get_output(), tuple(w.get_constants()), tuple(w.get_names()),
   1.170 +            tuple(make_varnames(nlocals)), filename, method_name, 0, "")
   1.171 +        # NOTE: May need more globals.
   1.172 +        fn = new.function(code, __builtins__.__dict__)
   1.173 +        namespace[method_name] = fn
   1.174 +    namespace["__java_init__"] = __java_init__
   1.175 +    # NOTE: Define superclasses properly.
   1.176 +    cls = new.classobj(str(c.this_class.get_name()), (), namespace)
   1.177  
   1.178  # vim: tabstop=4 expandtab shiftwidth=4