javaclass

Changeset

38:70d73e61fd75
2004-11-12 Paul Boddie raw files shortlog changelog graph Fixed loop handling somewhat. Added method dispatch code for the general method "aliases", although this may be incorrect (it does a linear search of methods as they are stored in the class file). Fixed invokevirtual's argument count. Added some additional instructions.
bytecode.py (file)
     1.1 --- a/bytecode.py	Fri Nov 12 01:28:34 2004 +0100
     1.2 +++ b/bytecode.py	Fri Nov 12 01:32:28 2004 +0100
     1.3 @@ -7,6 +7,7 @@
     1.4  NOTE: Synchronized constructs are not actually supported.
     1.5  """
     1.6  
     1.7 +import classfile
     1.8  from dis import opmap, cmp_op # for access to Python bytecode values and operators
     1.9  from UserDict import UserDict
    1.10  import new
    1.11 @@ -18,6 +19,9 @@
    1.12      "A Python bytecode writer."
    1.13  
    1.14      def __init__(self):
    1.15 +        # A stack of loop start instructions corresponding to loop blocks.
    1.16 +        self.loops = []
    1.17 +
    1.18          # A stack of loop block or exception block start positions.
    1.19          self.blocks = []
    1.20  
    1.21 @@ -119,19 +123,27 @@
    1.22              raise ValueError, value
    1.23  
    1.24      def setup_loop(self):
    1.25 -        self.blocks.append(self.position)
    1.26 +        self.loops.append(self.position)
    1.27          self.output.append(opmap["SETUP_LOOP"])
    1.28          self.position += 1
    1.29          self._write_value(0) # To be filled in later
    1.30  
    1.31      def end_loop(self):
    1.32 -        current_loop_start = self.blocks.pop()
    1.33 -        self.jump_absolute(current_loop_start)
    1.34 +        current_loop_start = self.loops.pop()
    1.35 +        current_loop_real_start = self.blocks.pop()
    1.36 +        #print "<", self.blocks, current_loop_real_start
    1.37 +        # Fix the iterator delta.
    1.38 +        # NOTE: Using 3 as the assumed length of the FOR_ITER instruction.
    1.39 +        # NOTE: 8-bit limit.
    1.40 +        self.jump_absolute(current_loop_real_start)
    1.41 +        self.output[current_loop_real_start + 1] = self.position - current_loop_real_start - 3
    1.42 +        self.output[current_loop_real_start + 2] = 0
    1.43 +        self.pop_block()
    1.44 +        # Fix the loop delta.
    1.45          # NOTE: Using 3 as the assumed length of the SETUP_LOOP instruction.
    1.46          # NOTE: 8-bit limit.
    1.47          self.output[current_loop_start + 1] = self.position - current_loop_start - 3
    1.48          self.output[current_loop_start + 2] = 0
    1.49 -        self.pop_block()
    1.50  
    1.51      def jump_to_label(self, status, name):
    1.52          # Record the instruction using the jump.
    1.53 @@ -272,15 +284,25 @@
    1.54          self.update_stack_depth(-1)
    1.55          self.update_locals(index)
    1.56  
    1.57 -    # Normal bytecode generators.
    1.58 -
    1.59      def for_iter(self):
    1.60          self.blocks.append(self.position)
    1.61 +        #print ">", self.blocks
    1.62          self.output.append(opmap["FOR_ITER"])
    1.63          self.position += 1
    1.64          self._write_value(0) # To be filled in later
    1.65          self.update_stack_depth(1)
    1.66  
    1.67 +    def break_loop(self):
    1.68 +        self.output.append(opmap["BREAK_LOOP"])
    1.69 +        self.position += 1
    1.70 +        self.jump_absolute(self.blocks[-1])
    1.71 +
    1.72 +    # Normal bytecode generators.
    1.73 +
    1.74 +    def get_iter(self):
    1.75 +        self.output.append(opmap["GET_ITER"])
    1.76 +        self.position += 1
    1.77 +
    1.78      def jump_if_false(self, offset=0):
    1.79          self.output.append(opmap["JUMP_IF_FALSE"])
    1.80          self.position += 1
    1.81 @@ -428,6 +450,11 @@
    1.82          self.output.append(opmap["END_FINALLY"])
    1.83          self.position += 1
    1.84  
    1.85 +    def unpack_sequence(self, count):
    1.86 +        self.output.append(opmap["UNPACK_SEQUENCE"])
    1.87 +        self.position += 1
    1.88 +        self._write_value(count)
    1.89 +
    1.90  # Utility classes and functions.
    1.91  
    1.92  class LazyDict(UserDict):
    1.93 @@ -1254,7 +1281,8 @@
    1.94          # NOTE: signature-based polymorphism.
    1.95          # NOTE: Java rules not specifically obeyed.
    1.96          index = (arguments[0] << 8) + arguments[1]
    1.97 -        count = arguments[2]
    1.98 +        # NOTE: "count" == nargs + 1, apparently.
    1.99 +        count = arguments[2] - 1
   1.100          target_name = self.class_file.constants[index - 1].get_python_name()
   1.101          # Stack: objectref, arg1, arg2, ...
   1.102          program.build_tuple(count)          # Stack: objectref, tuple
   1.103 @@ -1684,7 +1712,7 @@
   1.104          translator.process(method, writer)
   1.105          return translator, writer
   1.106  
   1.107 -    def make_method(self, method_name, methods, namespace):
   1.108 +    def make_method(self, method_name, methods, global_names, namespace):
   1.109          if method_name == "<init>":
   1.110              method_name = "__init__"
   1.111          # Where only one method exists, just make an alias.
   1.112 @@ -1692,17 +1720,118 @@
   1.113              method, fn = methods[0]
   1.114              namespace[method_name] = fn
   1.115              return
   1.116 -        return # for now
   1.117          # Find the maximum number of parameters involved.
   1.118          #maximum = max([len(method.get_descriptor()[0]) for method in methods])
   1.119 -        #program = BytecodeWriter()
   1.120 +        program = BytecodeWriter()
   1.121          # NOTE: The code below should use dictionary-based dispatch for better performance.
   1.122 -        #for method in methods:
   1.123 -        #    program.load_fast(1)    # Stack: arguments
   1.124 -        #    program.get_iter()      # Stack: arguments, iter
   1.125 -        #    program.for_iter()      # Stack: arguments, iter, argument
   1.126 -        #    program.dup_top()       # Stack: arguments, iter, argument, argument
   1.127 -        #    for parameter in method.get_descriptor()[0]:
   1.128 +        program.load_fast(1)                        # Stack: arguments
   1.129 +        for method, fn in methods:
   1.130 +            program.dup_top()                       # Stack: arguments, arguments
   1.131 +            program.load_const(1)
   1.132 +            program.store_fast(2)                   # found = 1
   1.133 +            program.setup_loop()
   1.134 +            # Emit a list of parameter types.
   1.135 +            descriptor_types = method.get_descriptor()[0]
   1.136 +            for descriptor_type in descriptor_types:
   1.137 +                base_type, object_type, array_type = descriptor_type
   1.138 +                python_type = classfile.descriptor_base_type_mapping[base_type]
   1.139 +                if python_type == "instance":
   1.140 +                    # NOTE: This will need extending.
   1.141 +                    python_type = object_type
   1.142 +                program.load_global(python_type)    # Stack: arguments, type, ...
   1.143 +            program.build_list(len(descriptor_types))
   1.144 +                                                    # Stack: arguments, types
   1.145 +            # Make a map of arguments and types.
   1.146 +            program.load_const(None)                # Stack: arguments, types, None
   1.147 +            program.rot_three()                     # Stack: None, arguments, types
   1.148 +            program.build_tuple(3)                  # Stack: tuple
   1.149 +            program.load_global("map")              # Stack: tuple, map
   1.150 +            program.rot_two()                       # Stack: map, tuple
   1.151 +            program.load_global("apply")            # Stack: map, tuple, apply
   1.152 +            program.rot_three()                     # Stack: apply, map, tuple
   1.153 +            program.call_function(2)                # Stack: tuple (mapping arguments to types)
   1.154 +            # Loop over each pair.
   1.155 +            program.get_iter()                      # Stack: iter
   1.156 +            program.for_iter()                      # Stack: iter, (argument, type)
   1.157 +            program.unpack_sequence(2)              # Stack: iter, type, argument
   1.158 +            program.dup_top()                       # Stack: iter, type, argument, argument
   1.159 +            program.load_const(None)                # Stack: iter, type, argument, argument, None
   1.160 +            program.compare_op("is")                # Stack: iter, type, argument, result
   1.161 +            # Missing argument?
   1.162 +            program.jump_to_label(0, "present")
   1.163 +            program.pop_top()                       # Stack: iter, type, argument
   1.164 +            program.pop_top()                       # Stack: iter, type
   1.165 +            program.pop_top()                       # Stack: iter
   1.166 +            program.load_const(0)
   1.167 +            program.store_fast(2)                   # found = 0
   1.168 +            program.break_loop()
   1.169 +            # Argument was present.
   1.170 +            program.start_label("present")
   1.171 +            program.pop_top()                       # Stack: iter, type, argument
   1.172 +            program.rot_two()                       # Stack: iter, argument, type
   1.173 +            program.dup_top()                       # Stack: iter, argument, type, type
   1.174 +            program.load_const(None)                # Stack: iter, argument, type, type, None
   1.175 +            program.compare_op("is")                # Stack: iter, argument, type, result
   1.176 +            # Missing parameter type?
   1.177 +            program.jump_to_label(0, "present")
   1.178 +            program.pop_top()                       # Stack: iter, argument, type
   1.179 +            program.pop_top()                       # Stack: iter, argument
   1.180 +            program.pop_top()                       # Stack: iter
   1.181 +            program.load_const(0)
   1.182 +            program.store_fast(2)                   # found = 0
   1.183 +            program.break_loop()
   1.184 +            # Parameter was present.
   1.185 +            program.start_label("present")
   1.186 +            program.pop_top()                       # Stack: iter, argument, type
   1.187 +            program.build_tuple(2)                  # Stack: iter, (argument, type)
   1.188 +            program.load_global("isinstance")       # Stack: iter, (argument, type), isinstance
   1.189 +            program.rot_two()                       # Stack: iter, isinstance, (argument, type)
   1.190 +            program.load_global("apply")            # Stack: iter, isinstance, (argument, type), apply
   1.191 +            program.rot_three()                     # Stack: iter, apply, isinstance, (argument, type)
   1.192 +            program.call_function(2)                # Stack: iter, result
   1.193 +            program.jump_to_label(1, "match")
   1.194 +            program.pop_top()                       # Stack: iter
   1.195 +            program.load_const(0)
   1.196 +            program.store_fast(2)                   # found = 0
   1.197 +            program.break_loop()
   1.198 +            # Argument type and parameter type matched.
   1.199 +            program.start_label("match")
   1.200 +            program.pop_top()                       # Stack: iter
   1.201 +            program.end_loop()                      # Stack: iter
   1.202 +            # If all the parameters matched, call the method.
   1.203 +            program.load_fast(2)                    # Stack: iter, match
   1.204 +            program.jump_to_label(0, "failed")
   1.205 +            # All the parameters matched.
   1.206 +            program.pop_top()                       # Stack: iter
   1.207 +            program.load_fast(1)                    # Stack: arguments
   1.208 +            program.load_fast(0)                    # Stack: arguments, self
   1.209 +            program.load_attr(str(method.get_python_name()))
   1.210 +                                                    # Stack: arguments, method
   1.211 +            program.rot_two()                       # Stack: method, arguments
   1.212 +            program.load_global("apply")            # Stack: method, arguments, apply
   1.213 +            program.rot_three()                     # Stack: apply, method, arguments
   1.214 +            program.call_function(2)                # Stack: result
   1.215 +            program.return_value()
   1.216 +            # Try the next method if arguments or parameters were missing or incorrect.
   1.217 +            program.start_label("failed")
   1.218 +            program.pop_top()                       # Stack: iter
   1.219 +            program.pop_top()                       # Stack:
   1.220 +        # Raise an exception if nothing matched.
   1.221 +        # NOTE: Improve this.
   1.222 +        program.load_const("No matching method")
   1.223 +        program.raise_varargs(1)
   1.224 +        program.load_const(None)
   1.225 +        program.return_value()
   1.226 +
   1.227 +        # Add the code as a method in the namespace.
   1.228 +        # NOTE: One actual parameter, flags as 71 apparently means that a list
   1.229 +        # NOTE: parameter is used in a method.
   1.230 +        nlocals = program.max_locals + 1
   1.231 +        code = new.code(1, nlocals, program.max_stack_depth, 71, program.get_output(),
   1.232 +            tuple(program.get_constants()), tuple(program.get_names()), tuple(self.make_varnames(nlocals)),
   1.233 +            self.filename, method_name, 0, "")
   1.234 +        fn = new.function(code, global_names)
   1.235 +        namespace[method_name] = fn
   1.236  
   1.237      def process(self, global_names):
   1.238          namespace = {}
   1.239 @@ -1714,7 +1843,7 @@
   1.240              method_name = str(method.get_python_name())
   1.241              # NOTE: Add line number table later.
   1.242              code = new.code(nargs, nlocals, w.max_stack_depth, 67, w.get_output(), tuple(w.get_constants()), tuple(w.get_names()),
   1.243 -                tuple(make_varnames(nlocals)), self.filename, method_name, 0, "")
   1.244 +                tuple(self.make_varnames(nlocals)), self.filename, method_name, 0, "")
   1.245              # NOTE: May need more globals.
   1.246              fn = new.function(code, global_names)
   1.247              namespace[method_name] = fn
   1.248 @@ -1729,26 +1858,35 @@
   1.249              bases = ()
   1.250          # Define method dispatchers.
   1.251          for real_method_name, methods in real_methods.items():
   1.252 -            self.make_method(real_method_name, methods, namespace)
   1.253 +            self.make_method(real_method_name, methods, global_names, namespace)
   1.254          cls = new.classobj(str(self.class_file.this_class.get_python_name()), bases, namespace)
   1.255          global_names[cls.__name__] = cls
   1.256          return cls
   1.257  
   1.258 -def make_varnames(nlocals):
   1.259 -    l = ["self"]
   1.260 -    for i in range(1, nlocals):
   1.261 -        l.append("_l%s" % i)
   1.262 -    return l[:nlocals]
   1.263 +    def make_varnames(self, nlocals):
   1.264 +        l = ["self"]
   1.265 +        for i in range(1, nlocals):
   1.266 +            l.append("_l%s" % i)
   1.267 +        return l[:nlocals]
   1.268 +
   1.269 +def _map(*args):
   1.270 +    print args
   1.271 +    return apply(__builtins__.map, args)
   1.272 +
   1.273 +def _isinstance(*args):
   1.274 +    print args
   1.275 +    return apply(__builtins__.isinstance, args)
   1.276  
   1.277  if __name__ == "__main__":
   1.278      import sys
   1.279 -    from classfile import ClassFile
   1.280      import dis
   1.281      global_names = {}
   1.282      global_names.update(__builtins__.__dict__)
   1.283 +    #global_names["isinstance"] = _isinstance
   1.284 +    #global_names["map"] = _map
   1.285      for filename in sys.argv[1:]:
   1.286          f = open(filename, "rb")
   1.287 -        c = ClassFile(f.read())
   1.288 +        c = classfile.ClassFile(f.read())
   1.289          translator = ClassTranslator(c)
   1.290          cls = translator.process(global_names)
   1.291