javaclass

Changeset

59:d27d8758c4d3
2004-11-15 Paul Boddie raw files shortlog changelog graph Replaced apply usage with CALL_FUNCTION_VAR. Changed *static instruction translations to use the class name rather than self.__class__ although this is likely to be insufficient. Added static field definition and support for the <clinit> method as a class method in Python classes. Changed superclass loading to hopefully get sibling classes directly from the global namespace. Adopted globals() in the bytecode test program for the global namespace. Fixed classhook to avoid "restricted mode" issues with the global namespace used, added calling of __clinit__ (the Python equivalent of <clinit>), and introduced improved processing of classes by ordering them according to superclass dependencies.
bytecode.py (file) classhook.py (file)
     1.1 --- a/bytecode.py	Mon Nov 15 00:54:28 2004 +0100
     1.2 +++ b/bytecode.py	Mon Nov 15 01:00:53 2004 +0100
     1.3 @@ -377,6 +377,12 @@
     1.4          self._write_value(count)
     1.5          self.update_stack_depth(-count)
     1.6  
     1.7 +    def call_function_var(self, count):
     1.8 +        self.output.append(opmap["CALL_FUNCTION_VAR"])
     1.9 +        self.position += 1
    1.10 +        self._write_value(count)
    1.11 +        self.update_stack_depth(-count-1)
    1.12 +
    1.13      def binary_subscr(self):
    1.14          self.output.append(opmap["BINARY_SUBSCR"])
    1.15          self.position += 1
    1.16 @@ -1140,8 +1146,10 @@
    1.17      def getstatic(self, arguments, program):
    1.18          index = (arguments[0] << 8) + arguments[1]
    1.19          target_name = self.class_file.constants[index - 1].get_python_name()
    1.20 -        program.load_name("self")
    1.21 -        program.load_attr("__class__")
    1.22 +        # Get the class name instead of the fully qualified name.
    1.23 +        full_class_name = str(self.class_file.this_class.get_python_name())
    1.24 +        class_name = full_class_name.split(".")[-1]
    1.25 +        program.load_global(class_name) # Stack: classref
    1.26          # NOTE: Using the string version of the name which may contain incompatible characters.
    1.27          program.load_attr(str(target_name))
    1.28  
    1.29 @@ -1322,9 +1330,7 @@
    1.30          # NOTE: Using the string version of the name which may contain incompatible characters.
    1.31          program.load_attr(str(target_name)) # Stack: tuple, method
    1.32          program.rot_two()                   # Stack: method, tuple
    1.33 -        program.load_global("apply")        # Stack: method, tuple, apply
    1.34 -        program.rot_three()                 # Stack: apply, method, tuple
    1.35 -        program.call_function(2)
    1.36 +        program.call_function_var(0)        # Stack: result
    1.37  
    1.38      def invokeinterface(self, arguments, program):
    1.39          # NOTE: This implementation does not perform the necessary checks for
    1.40 @@ -1347,6 +1353,7 @@
    1.41          target = self.class_file.constants[index - 1]
    1.42          original_name = target.get_name()
    1.43          target_name = target.get_python_name()
    1.44 +        method_name = self.method.get_name()
    1.45  
    1.46          # Get the number of parameters from the descriptor.
    1.47  
    1.48 @@ -1371,11 +1378,11 @@
    1.49          program.dup_top()               # Stack: tuple, tuple
    1.50          program.load_const(0)           # Stack: tuple, tuple, 0
    1.51          program.binary_subscr()         # Stack: tuple, reference
    1.52 -        program.dup_top()               # Stack: tuple, reference, reference
    1.53  
    1.54          # Is it self?
    1.55  
    1.56 -        program.load_fast(0)            # Stack: tuple, reference, reference, self
    1.57 +        program.dup_top()               # Stack: tuple, reference, reference
    1.58 +        program.load_fast(0)            # Stack: tuple, reference, reference, self|cls
    1.59          program.compare_op("is")        # Stack: tuple, reference, result
    1.60          program.jump_to_label(1, "is-self")
    1.61          program.pop_top()               # Stack: tuple, reference
    1.62 @@ -1387,9 +1394,7 @@
    1.63              program.rot_two()               # Stack: reference, tuple
    1.64              program.load_const(1)           # Stack: reference, tuple, 1
    1.65              program.slice_1()               # Stack: reference, tuple[1:]
    1.66 -            program.load_global("apply")    # Stack: reference, tuple, apply
    1.67 -            program.rot_three()             # Stack: apply, reference, tuple
    1.68 -            program.call_function(2)
    1.69 +            program.call_function_var(0)    # Stack: result
    1.70              # NOTE: Combinations of new, dup tend to produce interfering extra
    1.71              # NOTE: class references.
    1.72              program.rot_two()               # Stack: objectref, classref
    1.73 @@ -1400,6 +1405,7 @@
    1.74              program.jump_to_label(None, "done")
    1.75  
    1.76          # Is self.
    1.77 +
    1.78          program.start_label("is-self")
    1.79          program.pop_top()               # Stack: tuple, reference
    1.80          program.pop_top()               # Stack: tuple
    1.81 @@ -1426,6 +1432,7 @@
    1.82          program.pop_top()               # Stack: tuple, bases
    1.83          program.pop_top()               # Stack: tuple
    1.84          program.pop_top()               # Stack:
    1.85 +
    1.86          program.start_label("done")
    1.87  
    1.88      def invokestatic(self, arguments, program):
    1.89 @@ -1440,8 +1447,11 @@
    1.90          # Stack: arg1, arg2, ...
    1.91          program.build_tuple(count)      # Stack: tuple
    1.92          # Use the class to provide access to static methods.
    1.93 -        program.load_name("self")       # Stack: tuple, self
    1.94 -        program.load_attr("__class__")  # Stack: tuple, class
    1.95 +        # Get the class name instead of the fully qualified name.
    1.96 +        # NOTE: Need to find the class that declared the field being accessed.
    1.97 +        full_class_name = str(self.class_file.this_class.get_python_name())
    1.98 +        class_name = full_class_name.split(".")[-1]
    1.99 +        program.load_global(class_name) # Stack: tuple, classref
   1.100          self._invoke(target_name, program)
   1.101  
   1.102      def invokevirtual (self, arguments, program):
   1.103 @@ -1697,8 +1707,11 @@
   1.104      def putstatic(self, arguments, program):
   1.105          index = (arguments[0] << 8) + arguments[1]
   1.106          target_name = self.class_file.constants[index - 1].get_python_name()
   1.107 -        program.load_name("self")
   1.108 -        program.load_attr("__class__")
   1.109 +        # Get the class name instead of the fully qualified name.
   1.110 +        # NOTE: Need to find the class that declared the field being accessed.
   1.111 +        full_class_name = str(self.class_file.this_class.get_python_name())
   1.112 +        class_name = full_class_name.split(".")[-1]
   1.113 +        program.load_global(class_name) # Stack: classref
   1.114          # NOTE: Using the string version of the name which may contain incompatible characters.
   1.115          program.store_attr(str(target_name))
   1.116  
   1.117 @@ -1851,9 +1864,7 @@
   1.118              program.build_tuple(3)                  # Stack: arguments, tuple
   1.119              program.load_global("map")              # Stack: arguments, tuple, map
   1.120              program.rot_two()                       # Stack: arguments, map, tuple
   1.121 -            program.load_global("apply")            # Stack: arguments, map, tuple, apply
   1.122 -            program.rot_three()                     # Stack: arguments, apply, map, tuple
   1.123 -            program.call_function(2)                # Stack: arguments, list (mapping arguments to types)
   1.124 +            program.call_function_var(0)            # Stack: arguments, list (mapping arguments to types)
   1.125              # Loop over each pair.
   1.126              program.get_iter()                      # Stack: arguments, iter
   1.127              program.for_iter()                      # Stack: arguments, iter, (argument, type)
   1.128 @@ -1890,9 +1901,7 @@
   1.129              program.build_tuple(2)                  # Stack: arguments, iter, (argument, type)
   1.130              program.load_global("isinstance")       # Stack: arguments, iter, (argument, type), isinstance
   1.131              program.rot_two()                       # Stack: arguments, iter, isinstance, (argument, type)
   1.132 -            program.load_global("apply")            # Stack: arguments, iter, isinstance, (argument, type), apply
   1.133 -            program.rot_three()                     # Stack: arguments, iter, apply, isinstance, (argument, type)
   1.134 -            program.call_function(2)                # Stack: arguments, iter, result
   1.135 +            program.call_function_var(0)            # Stack: arguments, iter, result
   1.136              program.jump_to_label(1, "match")
   1.137              program.pop_top()                       # Stack: arguments, iter
   1.138              program.load_const(0)                   # Stack: arguments, iter, 0
   1.139 @@ -1912,9 +1921,7 @@
   1.140              program.load_attr(str(method.get_python_name()))
   1.141                                                      # Stack: arguments, arguments, method
   1.142              program.rot_two()                       # Stack: arguments, method, arguments
   1.143 -            program.load_global("apply")            # Stack: arguments, method, arguments, apply
   1.144 -            program.rot_three()                     # Stack: arguments, apply, method, arguments
   1.145 -            program.call_function(2)                # Stack: arguments, result
   1.146 +            program.call_function_var(0)            # Stack: arguments, result
   1.147              program.return_value()
   1.148              # Try the next method if arguments or parameters were missing or incorrect.
   1.149              program.start_label("failed")
   1.150 @@ -1946,27 +1953,56 @@
   1.151          """
   1.152  
   1.153          namespace = {}
   1.154 +
   1.155 +        # Make the fields.
   1.156 +
   1.157 +        for field in self.class_file.fields:
   1.158 +            if classfile.has_flags(field.access_flags, [classfile.STATIC]):
   1.159 +                field_name = str(field.get_python_name())
   1.160 +                namespace[field_name] = None
   1.161 +
   1.162 +        # Make the methods.
   1.163 +
   1.164          real_methods = {}
   1.165          for method in self.class_file.methods:
   1.166 +            real_method_name = str(method.get_name())
   1.167              t, w = self.translate_method(method)
   1.168 +
   1.169 +            # Fix up special class initialisation methods.
   1.170 +
   1.171 +            if real_method_name == "<clinit>":
   1.172 +                flags = 3
   1.173 +            else:
   1.174 +                flags = 67
   1.175              nlocals = w.max_locals + 1
   1.176              nargs = len(method.get_descriptor()[0]) + 1
   1.177 +
   1.178              method_name = str(method.get_python_name())
   1.179  
   1.180              # NOTE: Add line number table later.
   1.181  
   1.182 -            code = new.code(nargs, nlocals, w.max_stack_depth, 67, w.get_output(), tuple(w.get_constants()), tuple(w.get_names()),
   1.183 +            code = new.code(nargs, nlocals, w.max_stack_depth, flags, w.get_output(), tuple(w.get_constants()), tuple(w.get_names()),
   1.184                  tuple(self.make_varnames(nlocals)), self.filename, method_name, 0, "")
   1.185  
   1.186              # NOTE: May need more globals.
   1.187  
   1.188              fn = new.function(code, global_names)
   1.189 -            namespace[method_name] = fn
   1.190 -            real_method_name = str(method.get_name())
   1.191 +
   1.192 +            # Remember the real method name and the corresponding methods produced.
   1.193 +
   1.194              if not real_methods.has_key(real_method_name):
   1.195                  real_methods[real_method_name] = []
   1.196              real_methods[real_method_name].append((method, fn))
   1.197  
   1.198 +            # Fix up special class initialisation methods.
   1.199 +
   1.200 +            if real_method_name == "<clinit>":
   1.201 +                fn = classmethod(fn)
   1.202 +
   1.203 +            # Add the method to the class's namespace.
   1.204 +
   1.205 +            namespace[method_name] = fn
   1.206 +
   1.207          # Define superclasses.
   1.208  
   1.209          bases = self.get_base_classes(global_names)
   1.210 @@ -1974,7 +2010,8 @@
   1.211          # Define method dispatchers.
   1.212  
   1.213          for real_method_name, methods in real_methods.items():
   1.214 -            self.make_method(real_method_name, methods, global_names, namespace)
   1.215 +            if real_method_name != "<clinit>":
   1.216 +                self.make_method(real_method_name, methods, global_names, namespace)
   1.217  
   1.218          # Use only the last part of the fully qualified name.
   1.219  
   1.220 @@ -1982,6 +2019,7 @@
   1.221          class_name = full_class_name.split(".")[-1]
   1.222          cls = new.classobj(class_name, bases, namespace)
   1.223          global_names[cls.__name__] = cls
   1.224 +
   1.225          return cls
   1.226  
   1.227      def get_base_classes(self, global_names):
   1.228 @@ -1999,15 +2037,16 @@
   1.229          else:
   1.230              full_class_name = str(self.class_file.super_class.get_python_name())
   1.231              class_name_parts = full_class_name.split(".")
   1.232 +            class_name = class_name_parts[-1]
   1.233              class_module_name = ".".join(class_name_parts[:-1])
   1.234              if class_module_name == "":
   1.235 -                class_module_name = "__this__"
   1.236 -            class_name = class_name_parts[-1]
   1.237 -            print "Importing", class_module_name, class_name
   1.238 -            obj = __import__(class_module_name, global_names, {}, [])
   1.239 -            for class_name_part in class_name_parts[1:] or [class_name]:
   1.240 -                print "*", obj, class_name_part
   1.241 -                obj = getattr(obj, class_name_part)
   1.242 +                obj = global_names[class_name]
   1.243 +            else:
   1.244 +                print "Importing", class_module_name, class_name
   1.245 +                obj = __import__(class_module_name, global_names, {}, [])
   1.246 +                for class_name_part in class_name_parts[1:] or [class_name]:
   1.247 +                    print "*", obj, class_name_part
   1.248 +                    obj = getattr(obj, class_name_part)
   1.249              return (obj,)
   1.250  
   1.251      def make_varnames(self, nlocals):
   1.252 @@ -2034,8 +2073,7 @@
   1.253  if __name__ == "__main__":
   1.254      import sys
   1.255      import dis
   1.256 -    global_names = {}
   1.257 -    global_names.update(__builtins__.__dict__)
   1.258 +    global_names = globals()
   1.259      #global_names["isinstance"] = _isinstance
   1.260      #global_names["map"] = _map
   1.261      for filename in sys.argv[1:]:
     2.1 --- a/classhook.py	Mon Nov 15 00:54:28 2004 +0100
     2.2 +++ b/classhook.py	Mon Nov 15 01:00:53 2004 +0100
     2.3 @@ -88,26 +88,68 @@
     2.4  
     2.5          print "Loading", file, filename, info
     2.6  
     2.7 -        # Prepare a dictionary of globals.
     2.8 -
     2.9 -        global_names = {}
    2.10 -        global_names.update(__builtins__.__dict__)
    2.11 -
    2.12          # Set up the module.
    2.13  
    2.14          module = self.hooks.add_module(name)
    2.15          module.__path__ = [filename]
    2.16  
    2.17 +        # Prepare a dictionary of globals.
    2.18 +
    2.19 +        global_names = module.__dict__
    2.20 +        global_names["__builtins__"] = __builtins__
    2.21 +
    2.22          # Process each class file, producing a genuine Python class.
    2.23  
    2.24          class_files = []
    2.25          classes = []
    2.26 +
    2.27 +        # Load the class files.
    2.28 +
    2.29 +        class_files = {}
    2.30          for class_filename in glob.glob(os.path.join(filename, "*" + os.extsep + "class")):
    2.31 -            print "Importing class", class_filename
    2.32 +            print "Loading class", class_filename
    2.33              f = open(class_filename, "rb")
    2.34              s = f.read()
    2.35              f.close()
    2.36              class_file = classfile.ClassFile(s)
    2.37 +            class_files[str(class_file.this_class.get_name())] = class_file
    2.38 +
    2.39 +        # Get an index of the class files.
    2.40 +
    2.41 +        class_file_index = class_files.keys()
    2.42 +
    2.43 +        # NOTE: Unnecessary sorting for test purposes.
    2.44 +
    2.45 +        class_file_index.sort()
    2.46 +
    2.47 +        # Now go through the classes arranging them in a safe loading order.
    2.48 +
    2.49 +        position = 0
    2.50 +        while position < len(class_file_index):
    2.51 +            class_name = class_file_index[position]
    2.52 +            super_class_name = str(class_files[class_name].super_class.get_name())
    2.53 +
    2.54 +            # Discover whether the superclass appears later.
    2.55 +
    2.56 +            try:
    2.57 +                super_class_position = class_file_index.index(super_class_name)
    2.58 +                if super_class_position > position:
    2.59 +
    2.60 +                    # If the superclass appears later, swap this class and the
    2.61 +                    # superclass, then process the superclass.
    2.62 +
    2.63 +                    class_file_index[position] = super_class_name
    2.64 +                    class_file_index[super_class_position] = class_name
    2.65 +                    continue
    2.66 +
    2.67 +            except ValueError:
    2.68 +                pass
    2.69 +
    2.70 +            position += 1
    2.71 +
    2.72 +        class_files = [class_files[class_name] for class_name in class_file_index]
    2.73 +
    2.74 +        for class_file in class_files:
    2.75              translator = bytecode.ClassTranslator(class_file)
    2.76              cls = translator.process(global_names)
    2.77              module.__dict__[cls.__name__] = cls
    2.78 @@ -115,9 +157,9 @@
    2.79  
    2.80          # Finally, call __clinit__ methods for all relevant classes.
    2.81  
    2.82 -        #for cls in classes:
    2.83 -        #    if hasattr(cls, "__clinit__"):
    2.84 -        #        cls.__clinit__()
    2.85 +        for cls in classes:
    2.86 +            if hasattr(cls, "__clinit__"):
    2.87 +                cls.__clinit__()
    2.88  
    2.89          return module
    2.90