javaclass

Changeset

35:630f6b688af0
2004-11-11 Paul Boddie raw files shortlog changelog graph Fixed invokespecial for foreign object constructors. Moved much of the main program into the ClassTranslator class, introducing trivial aliases for each method name mapping to only one "real" method.
bytecode.py (file)
     1.1 --- a/bytecode.py	Thu Nov 11 22:56:50 2004 +0100
     1.2 +++ b/bytecode.py	Thu Nov 11 22:59:18 2004 +0100
     1.3 @@ -9,6 +9,7 @@
     1.4  
     1.5  from dis import opmap, cmp_op # for access to Python bytecode values and operators
     1.6  from UserDict import UserDict
     1.7 +import new
     1.8  
     1.9  # Bytecode production classes.
    1.10  
    1.11 @@ -1266,6 +1267,7 @@
    1.12          # NOTE: Java rules not specifically obeyed.
    1.13          index = (arguments[0] << 8) + arguments[1]
    1.14          target = self.class_file.constants[index - 1]
    1.15 +        original_name = target.get_name()
    1.16          target_name = target.get_python_name()
    1.17          # Get the number of parameters from the descriptor.
    1.18          count = len(target.get_descriptor()[0])
    1.19 @@ -1297,7 +1299,7 @@
    1.20  
    1.21          # Is another class or reference.
    1.22          # NOTE: Reference case not covered!
    1.23 -        if str(target_name) == "__init__":
    1.24 +        if str(original_name) == "<init>":
    1.25              program.rot_two()               # Stack: reference, tuple
    1.26              program.load_const(1)           # Stack: reference, tuple, 1
    1.27              program.slice_1()               # Stack: reference, tuple[1:]
    1.28 @@ -1671,11 +1673,66 @@
    1.29      disassembler = BytecodeDisassembler(class_file)
    1.30      disassembler.process(method, BytecodeDisassemblerProgram())
    1.31  
    1.32 -def translate(class_file, method):
    1.33 -    translator = BytecodeTranslator(class_file)
    1.34 -    writer = BytecodeWriter()
    1.35 -    translator.process(method, writer)
    1.36 -    return translator, writer
    1.37 +class ClassTranslator:
    1.38 +    def __init__(self, class_file):
    1.39 +        self.class_file = class_file
    1.40 +        self.filename = str(self.class_file.attributes[0].get_name())
    1.41 +
    1.42 +    def translate_method(self, method):
    1.43 +        translator = BytecodeTranslator(self.class_file)
    1.44 +        writer = BytecodeWriter()
    1.45 +        translator.process(method, writer)
    1.46 +        return translator, writer
    1.47 +
    1.48 +    def make_method(self, method_name, methods, namespace):
    1.49 +        if method_name == "<init>":
    1.50 +            method_name = "__init__"
    1.51 +        # Where only one method exists, just make an alias.
    1.52 +        if len(methods) == 1:
    1.53 +            method, fn = methods[0]
    1.54 +            namespace[method_name] = fn
    1.55 +            return
    1.56 +        return # for now
    1.57 +        # Find the maximum number of parameters involved.
    1.58 +        #maximum = max([len(method.get_descriptor()[0]) for method in methods])
    1.59 +        #program = BytecodeWriter()
    1.60 +        # NOTE: The code below should use dictionary-based dispatch for better performance.
    1.61 +        #for method in methods:
    1.62 +        #    program.load_fast(1)    # Stack: arguments
    1.63 +        #    program.get_iter()      # Stack: arguments, iter
    1.64 +        #    program.for_iter()      # Stack: arguments, iter, argument
    1.65 +        #    program.dup_top()       # Stack: arguments, iter, argument, argument
    1.66 +        #    for parameter in method.get_descriptor()[0]:
    1.67 +
    1.68 +    def process(self, global_names):
    1.69 +        namespace = {}
    1.70 +        real_methods = {}
    1.71 +        for method in self.class_file.methods:
    1.72 +            t, w = self.translate_method(method)
    1.73 +            nlocals = w.max_locals + 1
    1.74 +            nargs = len(method.get_descriptor()[0]) + 1
    1.75 +            method_name = str(method.get_python_name())
    1.76 +            # NOTE: Add line number table later.
    1.77 +            code = new.code(nargs, nlocals, w.max_stack_depth, 67, w.get_output(), tuple(w.get_constants()), tuple(w.get_names()),
    1.78 +                tuple(make_varnames(nlocals)), self.filename, method_name, 0, "")
    1.79 +            # NOTE: May need more globals.
    1.80 +            fn = new.function(code, global_names)
    1.81 +            namespace[method_name] = fn
    1.82 +            real_method_name = str(method.get_name())
    1.83 +            if not real_methods.has_key(real_method_name):
    1.84 +                real_methods[real_method_name] = []
    1.85 +            real_methods[real_method_name].append((method, fn))
    1.86 +        # NOTE: Define superclasses properly.
    1.87 +        if str(self.class_file.super_class.get_name()) not in ("java/lang/Object", "java/lang/Exception"):
    1.88 +            bases = (global_names[str(self.class_file.super_class.get_python_name())],)
    1.89 +        else:
    1.90 +            bases = ()
    1.91 +        # Define method dispatchers.
    1.92 +        for real_method_name, methods in real_methods.items():
    1.93 +            self.make_method(real_method_name, methods, namespace)
    1.94 +        cls = new.classobj(str(self.class_file.this_class.get_python_name()), bases, namespace)
    1.95 +        global_names[cls.__name__] = cls
    1.96 +        return cls
    1.97  
    1.98  def make_varnames(nlocals):
    1.99      l = ["self"]
   1.100 @@ -1686,30 +1743,13 @@
   1.101  if __name__ == "__main__":
   1.102      import sys
   1.103      from classfile import ClassFile
   1.104 +    import dis
   1.105      global_names = {}
   1.106      global_names.update(__builtins__.__dict__)
   1.107      for filename in sys.argv[1:]:
   1.108          f = open(filename, "rb")
   1.109          c = ClassFile(f.read())
   1.110 -        import dis, new
   1.111 -        namespace = {}
   1.112 -        for method in c.methods:
   1.113 -            nargs = len(method.get_descriptor()[0]) + 1
   1.114 -            t, w = translate(c, method)
   1.115 -            nlocals = w.max_locals + 1
   1.116 -            filename = str(c.attributes[0].get_name())
   1.117 -            method_name = str(method.get_python_name())
   1.118 -            code = new.code(nargs, nlocals, w.max_stack_depth, 67, w.get_output(), tuple(w.get_constants()), tuple(w.get_names()),
   1.119 -                tuple(make_varnames(nlocals)), filename, method_name, 0, "")
   1.120 -            # NOTE: May need more globals.
   1.121 -            fn = new.function(code, global_names)
   1.122 -            namespace[method_name] = fn
   1.123 -        # NOTE: Define superclasses properly.
   1.124 -        if str(c.super_class.get_name()) not in ("java/lang/Object", "java/lang/Exception"):
   1.125 -            bases = (global_names[str(c.super_class.get_python_name())],)
   1.126 -        else:
   1.127 -            bases = ()
   1.128 -        cls = new.classobj(str(c.this_class.get_python_name()), bases, namespace)
   1.129 -        global_names[cls.__name__] = cls
   1.130 +        translator = ClassTranslator(c)
   1.131 +        cls = translator.process(global_names)
   1.132  
   1.133  # vim: tabstop=4 expandtab shiftwidth=4