javaclass

Changeset

22:50db0817ceeb
2004-11-10 Paul Boddie raw files shortlog changelog graph Reverted the ret instruction's implementation to use return addresses. Introduced finally handler detection to produce END_FINALLY rather than RAISE_VARARGS from athrow instructions. Fixed invokevirtual. Changed new and invokespecial to work better with the Python initialisation model, removing the __java_init__ method. Now, the new instruction yields a class reference which is then used accordingly by invokespecial. However, invokespecial is made aware of whether it is operating within an <init> method, although this may need further examination. Changed the translate, disassemble and BytecodeReader.process signatures to work on methods rather than separate code and exception table objects.
bytecode.py (file)
     1.1 --- a/bytecode.py	Wed Nov 10 15:55:39 2004 +0100
     1.2 +++ b/bytecode.py	Wed Nov 10 16:00:09 2004 +0100
     1.3 @@ -155,18 +155,32 @@
     1.4          del self.jumps[name]
     1.5  
     1.6      def load_const_ret(self, value):
     1.7 -        #self.constants_for_exceptions.append(value)
     1.8 -        #self.load_const(value)
     1.9 -        self.load_const(None)
    1.10 +        self.constants_for_exceptions.append(value)
    1.11 +        self.load_const(value)
    1.12  
    1.13      def ret(self, index):
    1.14          self.load_fast(index)
    1.15 -        self.end_finally()
    1.16 +        # Previously, the constant stored on the stack by jsr/jsr_w was stored
    1.17 +        # in a local variable. In the JVM, extracting the value from the local
    1.18 +        # variable and jumping can be done at runtime. In the Python VM, any
    1.19 +        # jump target must be known in advance and written into the bytecode.
    1.20 +        for constant in self.constants_for_exceptions:
    1.21 +            self.dup_top()              # Stack: actual-address, actual-address
    1.22 +            self.load_const(constant)   # Stack: actual-address, actual-address, suggested-address
    1.23 +            self.compare_op("==")       # Stack: actual-address, result
    1.24 +            self.jump_to_label(0, "const")
    1.25 +            self.pop_top()              # Stack: actual-address
    1.26 +            self.pop_top()              # Stack:
    1.27 +            self.jump_absolute(constant)
    1.28 +            self.start_label("const")
    1.29 +            self.pop_top()              # Stack: actual-address
    1.30 +        # NOTE: If we get here, something is really wrong.
    1.31 +        self.pop_top()              # Stack:
    1.32  
    1.33      def setup_except(self, target):
    1.34          self.blocks.append(self.position)
    1.35          self.exception_handlers.append(target)
    1.36 -        print "-", self.position, target
    1.37 +        #print "-", self.position, target
    1.38          self.output.append(opmap["SETUP_EXCEPT"])
    1.39          self.position += 1
    1.40          self._write_value(0) # To be filled in later
    1.41 @@ -174,7 +188,7 @@
    1.42      def setup_finally(self, target):
    1.43          self.blocks.append(self.position)
    1.44          self.exception_handlers.append(target)
    1.45 -        print "-", self.position, target
    1.46 +        #print "-", self.position, target
    1.47          self.output.append(opmap["SETUP_FINALLY"])
    1.48          self.position += 1
    1.49          self._write_value(0) # To be filled in later
    1.50 @@ -184,7 +198,7 @@
    1.51          # Convert the "lazy" absolute value.
    1.52          current_exception_target = self.exception_handlers.pop()
    1.53          target = current_exception_target.get_value()
    1.54 -        print "*", current_exception_start, target
    1.55 +        #print "*", current_exception_start, target
    1.56          # NOTE: Using 3 as the assumed length of the SETUP_* instruction.
    1.57          # NOTE: 8-bit limit.
    1.58          self.output[current_exception_start + 1] = target - current_exception_start - 3
    1.59 @@ -467,8 +481,14 @@
    1.60          self.class_file = class_file
    1.61          self.position_mapping = LazyDict()
    1.62  
    1.63 -    def process(self, code, exception_table, program):
    1.64 +    def process(self, method, program):
    1.65          self.java_position = 0
    1.66 +        self.in_finally = 0
    1.67 +        self.method = method
    1.68 +
    1.69 +        # NOTE: Not guaranteed.
    1.70 +        attribute = method.attributes[0]
    1.71 +        code, exception_table = attribute.code, attribute.exception_table
    1.72  
    1.73          # Produce a structure which permits fast access to exception details.
    1.74          exception_block_start = {}
    1.75 @@ -497,20 +517,23 @@
    1.76              self.position_mapping[self.java_position] = program.position
    1.77  
    1.78              # Insert exception handling constructs.
    1.79 -            for exception in exception_block_start.get(self.java_position, []):
    1.80 +            block_starts = exception_block_start.get(self.java_position, [])
    1.81 +            for exception in block_starts:
    1.82                  # Note that the absolute position is used.
    1.83                  if exception.catch_type == 0:
    1.84                      program.setup_finally(self.position_mapping[exception.handler_pc])
    1.85                  else:
    1.86                      program.setup_except(self.position_mapping[exception.handler_pc])
    1.87 +            if block_starts:
    1.88 +                self.in_finally = 0
    1.89  
    1.90              # Insert exception handler details.
    1.91              # NOTE: Ensure that pop_block is reachable by possibly inserting it at the start of finally handlers.
    1.92              # NOTE: Insert a check for the correct exception at the start of each handler.
    1.93              for exception in exception_block_handler.get(self.java_position, []):
    1.94                  program.end_exception()
    1.95 -                #if exception.catch_type == 0:
    1.96 -                #    program.pop_block()
    1.97 +                if exception.catch_type == 0:
    1.98 +                    self.in_finally = 1
    1.99  
   1.100              # Where handlers are begun, do not produce equivalent bytecode since
   1.101              # the first handler instruction typically involves saving a local
   1.102 @@ -864,8 +887,12 @@
   1.103  
   1.104      def athrow(self, arguments, program):
   1.105          # NOTE: NullPointerException not raised where null/None is found on the stack.
   1.106 -        program.dup_top()
   1.107 -        program.raise_varargs(1)
   1.108 +        # If this instruction appears in a finally handler, use end_finally instead.
   1.109 +        if self.in_finally:
   1.110 +            program.end_finally()
   1.111 +        else:
   1.112 +            program.dup_top()
   1.113 +            program.raise_varargs(1)
   1.114  
   1.115      baload = aaload
   1.116      bastore = aastore
   1.117 @@ -1223,10 +1250,46 @@
   1.118          target_name = target.get_python_name()
   1.119          # Get the number of parameters from the descriptor.
   1.120          count = len(target.get_descriptor()[0])
   1.121 -        # Stack: objectref, arg1, arg2, ...
   1.122 -        program.build_tuple(count)          # Stack: objectref, tuple
   1.123 -        program.rot_two()                   # Stack: tuple, objectref
   1.124 -        self._invoke(target_name, program)
   1.125 +
   1.126 +        # Check for the method name and invoke superclasses where appropriate.
   1.127 +        if str(self.method.get_name()) == "<init>":
   1.128 +            program.build_tuple(count + 1)  # Stack: tuple
   1.129 +            # NOTE: Assume that local 0 is always self.
   1.130 +            program.load_fast(0)            # Stack: tuple, objectref
   1.131 +            program.load_attr("__class__")  # Stack: tuple, classref
   1.132 +            program.load_attr("__bases__")  # Stack: tuple, bases
   1.133 +            program.dup_top()               # Stack: tuple, bases, bases
   1.134 +            program.load_global("len")      # Stack: tuple, bases, bases, len
   1.135 +            program.rot_two()               # Stack: tuple, bases, len, bases
   1.136 +            program.call_function(1)        # Stack: tuple, bases, #bases
   1.137 +            program.load_const(0)           # Stack: tuple, bases, #bases, 0
   1.138 +            program.compare_op("==")        # Stack: tuple, bases, result
   1.139 +            program.jump_to_label(1, "next")
   1.140 +            program.pop_top()               # Stack: tuple, bases
   1.141 +            program.load_const(0)           # Stack: tuple, bases, 0
   1.142 +            program.binary_subscr()         # Stack: tuple, bases[0]
   1.143 +            self._invoke(target_name, program)
   1.144 +            program.jump_to_label(None, "next2")
   1.145 +            program.start_label("next")
   1.146 +            program.pop_top()               # Stack: tuple, bases
   1.147 +            program.pop_top()               # Stack: tuple
   1.148 +            program.pop_top()               # Stack:
   1.149 +            program.start_label("next2")
   1.150 +
   1.151 +        elif str(target_name) == "__init__":
   1.152 +            # NOTE: Due to changes with the new instruction's implementation, the
   1.153 +            # NOTE: stack differs from that stated: objectref, arg1, arg2, ...
   1.154 +            # Stack: classref, arg1, arg2, ...
   1.155 +            program.build_tuple(count)          # Stack: classref, tuple
   1.156 +                                                # NOTE: Stack: objectref, tuple
   1.157 +            program.load_global("apply")        # Stack: classref, tuple, apply
   1.158 +            program.rot_three()                 # Stack: apply, classref, tuple
   1.159 +            program.call_function(2)
   1.160 +
   1.161 +        else:
   1.162 +            program.build_tuple(count)          # Stack: objectref, tuple
   1.163 +            program.rot_two()                   # Stack: tuple, objectref
   1.164 +            self._invoke(target_name, program)
   1.165  
   1.166      """
   1.167      def invokespecial(self, arguments, program):
   1.168 @@ -1279,7 +1342,19 @@
   1.169          program.load_attr("__class__")  # Stack: tuple, class
   1.170          self._invoke(target_name, program)
   1.171  
   1.172 -    invokevirtual = invokeinterface # Ignoring Java rules
   1.173 +    def invokevirtual (self, arguments, program):
   1.174 +        # NOTE: This implementation does not perform the necessary checks for
   1.175 +        # NOTE: signature-based polymorphism.
   1.176 +        # NOTE: Java rules not specifically obeyed.
   1.177 +        index = (arguments[0] << 8) + arguments[1]
   1.178 +        target = self.class_file.constants[index - 1]
   1.179 +        target_name = target.get_python_name()
   1.180 +        # Get the number of parameters from the descriptor.
   1.181 +        count = len(target.get_descriptor()[0])
   1.182 +        # Stack: objectref, arg1, arg2, ...
   1.183 +        program.build_tuple(count)          # Stack: objectref, tuple
   1.184 +        program.rot_two()                   # Stack: tuple, objectref
   1.185 +        self._invoke(target_name, program)
   1.186  
   1.187      def ior(self, arguments, program):
   1.188          # NOTE: No type checking performed.
   1.189 @@ -1453,7 +1528,10 @@
   1.190          target_name = self.class_file.constants[index - 1].get_name()
   1.191          # NOTE: Using the string version of the name which may contain incompatible characters.
   1.192          program.load_global(str(target_name))
   1.193 -        program.call_function(0)
   1.194 +        # NOTE: Unlike Java, we do not provide an object reference. Instead, a
   1.195 +        # NOTE: class reference is provided, and the invokespecial method's
   1.196 +        # NOTE: behaviour is changed.
   1.197 +        #program.call_function(0)
   1.198  
   1.199      def newarray(self, arguments, program):
   1.200          # NOTE: Does not raise NegativeArraySizeException.
   1.201 @@ -1485,6 +1563,9 @@
   1.202  
   1.203      def ret(self, arguments, program):
   1.204          program.ret(arguments[0])
   1.205 +        # Indicate that the finally handler is probably over.
   1.206 +        # NOTE: This is seemingly not guaranteed.
   1.207 +        self.in_finally = 0
   1.208  
   1.209      def return_(self, arguments, program):
   1.210          program.load_const(None)
   1.211 @@ -1537,14 +1618,14 @@
   1.212          # NOTE: To be implemented.
   1.213          return number_of_arguments
   1.214  
   1.215 -def disassemble(class_file, code, exception_table):
   1.216 +def disassemble(class_file, method):
   1.217      disassembler = BytecodeDisassembler(class_file)
   1.218 -    disassembler.process(code, exception_table, BytecodeDisassemblerProgram())
   1.219 +    disassembler.process(method, BytecodeDisassemblerProgram())
   1.220  
   1.221 -def translate(class_file, code, exception_table):
   1.222 +def translate(class_file, method):
   1.223      translator = BytecodeTranslator(class_file)
   1.224      writer = BytecodeWriter()
   1.225 -    translator.process(code, exception_table, writer)
   1.226 +    translator.process(method, writer)
   1.227      return translator, writer
   1.228  
   1.229  def make_varnames(nlocals):
   1.230 @@ -1553,9 +1634,6 @@
   1.231          l.append("_l%s" % i)
   1.232      return l[:nlocals]
   1.233  
   1.234 -def __java_init__(self, *args):
   1.235 -    print "<init>", args
   1.236 -
   1.237  if __name__ == "__main__":
   1.238      import sys
   1.239      from classfile import ClassFile
   1.240 @@ -1567,20 +1645,16 @@
   1.241          import dis, new
   1.242          namespace = {}
   1.243          for method in c.methods:
   1.244 -            attribute = method.attributes[0]
   1.245              nargs = len(method.get_descriptor()[0]) + 1
   1.246 -            t, w = translate(c, attribute.code, attribute.exception_table)
   1.247 +            t, w = translate(c, method)
   1.248              nlocals = w.max_locals + 1
   1.249              filename = str(c.attributes[0].get_name())
   1.250 -            method_name = str(method.get_name())
   1.251 -            if method_name == "<init>":
   1.252 -                method_name = "__init__"
   1.253 +            method_name = str(method.get_python_name())
   1.254              code = new.code(nargs, nlocals, w.max_stack_depth, 67, w.get_output(), tuple(w.get_constants()), tuple(w.get_names()),
   1.255                  tuple(make_varnames(nlocals)), filename, method_name, 0, "")
   1.256              # NOTE: May need more globals.
   1.257              fn = new.function(code, global_names)
   1.258              namespace[method_name] = fn
   1.259 -        namespace["__java_init__"] = __java_init__
   1.260          # NOTE: Define superclasses properly.
   1.261          cls = new.classobj(str(c.this_class.get_name()), (), namespace)
   1.262          global_names[cls.__name__] = cls