javaclass

Changeset

85:85e3410a7de9
2004-12-10 Paul Boddie raw files shortlog changelog graph Moved class name loading (in translated instructions) into a top-level function in the bytecode module. Added initialisation order processing to classhook.
bytecode.py (file) classhook.py (file)
     1.1 --- a/bytecode.py	Thu Dec 09 20:51:18 2004 +0100
     1.2 +++ b/bytecode.py	Fri Dec 10 01:04:59 2004 +0100
     1.3 @@ -284,14 +284,14 @@
     1.4          # NOTE: Using 3 as the assumed length of the SETUP_* instruction.
     1.5          self._rewrite_value(current_exception_start + 1, target - current_exception_start - 3)
     1.6  
     1.7 -    def start_handler(self, exc_name):
     1.8 +    def start_handler(self, exc_name, class_file):
     1.9  
    1.10          # Where handlers are begun, produce bytecode to test the type of
    1.11          # the exception.
    1.12          # NOTE: Since RAISE_VARARGS and END_FINALLY are not really documented,
    1.13          # NOTE: we store the top of the stack and use it later to trigger the
    1.14          # NOTE: magic processes when re-raising.
    1.15 -        self.use_external_name(exc_name)
    1.16 +        self.use_external_name(str(exc_name))
    1.17  
    1.18          self.rot_two()                      # Stack: raised-exception, exception
    1.19          self.dup_top()                      # Stack: raised-exception, exception, exception
    1.20 @@ -304,7 +304,8 @@
    1.21          self.load_attr("args")              # Stack: raised-exception, exception, args
    1.22          self.load_const(0)                  # Stack: raised-exception, exception, args, 0
    1.23          self.binary_subscr()                # Stack: raised-exception, exception, exception-object
    1.24 -        self.load_global(str(exc_name))     # Stack: raised-exception, exception, exception-object, handled-exception
    1.25 +        load_class_name(class_file, str(exc_name), self)
    1.26 +                                            # Stack: raised-exception, exception, exception-object, handled-exception
    1.27          self.load_global("isinstance")      # Stack: raised-exception, exception, exception-object, handled-exception, isinstance
    1.28          self.rot_three()                    # Stack: raised-exception, exception, isinstance, exception-object, handled-exception
    1.29          self.call_function(2)               # Stack: raised-exception, exception, result
    1.30 @@ -632,6 +633,21 @@
    1.31  def signed4(value):
    1.32      return signed(value, 0x80000000)
    1.33  
    1.34 +def load_class_name(class_file, full_class_name, program):
    1.35 +    this_class_name = str(class_file.this_class.get_python_name())
    1.36 +    this_class_parts = this_class_name.split(".")
    1.37 +    class_parts = full_class_name.split(".")
    1.38 +
    1.39 +    # Only use the full path if different from this class's path.
    1.40 +
    1.41 +    if class_parts[:-1] != this_class_parts[:-1]:
    1.42 +        program.use_external_name(full_class_name)
    1.43 +        program.load_global(class_parts[0])
    1.44 +        for class_part in class_parts[1:]:
    1.45 +            program.load_attr(class_part)   # Stack: classref
    1.46 +    else:
    1.47 +        program.load_global(class_parts[-1])
    1.48 +
    1.49  # Bytecode conversion.
    1.50  
    1.51  class BytecodeReader:
    1.52 @@ -736,7 +752,7 @@
    1.53                  if exception.catch_type == 0:
    1.54                      self.in_finally = 1
    1.55                  else:
    1.56 -                    program.start_handler(self.class_file.constants[exception.catch_type - 1].get_python_name())
    1.57 +                    program.start_handler(self.class_file.constants[exception.catch_type - 1].get_python_name(), self.class_file)
    1.58  
    1.59              # Process the bytecode at the current position.
    1.60  
    1.61 @@ -1034,7 +1050,7 @@
    1.62          print "(setup_finally %s)" % target
    1.63      def end_exception(self):
    1.64          print "(end_exception)"
    1.65 -    def start_handler(self, exc_name):
    1.66 +    def start_handler(self, exc_name, class_file):
    1.67          print "(start_handler %s)" % exc_name
    1.68      def pop_block(self):
    1.69          print "(pop_block)"
    1.70 @@ -1043,17 +1059,6 @@
    1.71  
    1.72      "A Java bytecode translator which uses a Python bytecode writer."
    1.73  
    1.74 -    def _load_class_name(self, full_class_name, program):
    1.75 -        this_class_name = str(self.class_file.this_class.get_python_name())
    1.76 -        class_parts = full_class_name.split(".")
    1.77 -        if full_class_name != this_class_name:
    1.78 -            program.use_external_name(full_class_name)
    1.79 -            program.load_global(class_parts[0])
    1.80 -            for class_part in class_parts[1:]:
    1.81 -                program.load_attr(class_part)   # Stack: classref
    1.82 -        else:
    1.83 -            program.load_global(class_parts[-1])
    1.84 -
    1.85      def aaload(self, arguments, program):
    1.86          # NOTE: No type checking performed.
    1.87          program.binary_subscr()
    1.88 @@ -1158,17 +1163,30 @@
    1.89          index = (arguments[0] << 8) + arguments[1]
    1.90          target_name = self.class_file.constants[index - 1].get_python_name()
    1.91          program.use_external_name(target_name)
    1.92 -
    1.93 -        # NOTE: Using the string version of the name which may contain incompatible characters.
    1.94 -        target_components = str(target_name).split("/")
    1.95 -
    1.96 +        program.dup_top()                   # Stack: objectref, objectref
    1.97 +        program.load_const(None)            # Stack: objectref, objectref, None
    1.98 +        program.compare_op("is")            # Stack: objectref, result
    1.99 +        program.jump_to_label(1, "next")
   1.100 +        program.pop_top()                   # Stack: objectref
   1.101          program.dup_top()                   # Stack: objectref, objectref
   1.102          program.load_global("isinstance")   # Stack: objectref, objectref, isinstance
   1.103          program.rot_two()                   # Stack: objectref, isinstance, objectref
   1.104 -        program.load_global(target_components[0])
   1.105 -        for target_component in target_components[1:]:
   1.106 -            program.load_attr(target_component)
   1.107 -        program.call_function(2)            # Stack: objectref
   1.108 +        load_class_name(self.class_file, target_name, program)
   1.109 +        program.call_function(2)            # Stack: objectref, result
   1.110 +        program.jump_to_label(1, "next")
   1.111 +        program.pop_top()                   # Stack: objectref
   1.112 +        program.pop_top()                   # Stack:
   1.113 +        program.use_external_name("java.lang.ClassCastException")
   1.114 +        load_class_name(self.class_file, "java.lang.ClassCastException", program)
   1.115 +        program.call_function(0)            # Stack: exception
   1.116 +        # Wrap the exception in a Python exception.
   1.117 +        program.load_global("Exception")    # Stack: exception, Exception
   1.118 +        program.rot_two()                   # Stack: Exception, exception
   1.119 +        program.call_function(1)            # Stack: exception
   1.120 +        program.raise_varargs(1)
   1.121 +        # NOTE: This seems to put another object on the stack.
   1.122 +        program.start_label("next")
   1.123 +        program.pop_top()                   # Stack: objectref
   1.124  
   1.125      def d2f(self, arguments, program):
   1.126          pass
   1.127 @@ -1302,7 +1320,8 @@
   1.128          # Get the class name instead of the fully qualified name.
   1.129  
   1.130          full_class_name = target.get_class().get_python_name()
   1.131 -        self._load_class_name(full_class_name, program)
   1.132 +        program.use_external_name(full_class_name)
   1.133 +        load_class_name(self.class_file, full_class_name, program)
   1.134          # NOTE: Using the string version of the name which may contain incompatible characters.
   1.135          program.load_attr(str(target_name))
   1.136  
   1.137 @@ -1468,15 +1487,9 @@
   1.138          index = (arguments[0] << 8) + arguments[1]
   1.139          target_name = self.class_file.constants[index - 1].get_python_name()
   1.140          program.use_external_name(target_name)
   1.141 -
   1.142 -        # NOTE: Using the string version of the name which may contain incompatible characters.
   1.143 -        target_components = str(target_name).split("/")
   1.144 -
   1.145          program.load_global("isinstance")   # Stack: objectref, isinstance
   1.146          program.rot_two()                   # Stack: isinstance, objectref
   1.147 -        program.load_global(target_components[0])
   1.148 -        for target_component in target_components[1:]:
   1.149 -            program.load_attr(target_component)
   1.150 +        load_class_name(self.class_file, target_name, program)
   1.151          program.call_function(2)            # Stack: result
   1.152  
   1.153      def _invoke(self, target_name, program):
   1.154 @@ -1522,7 +1535,8 @@
   1.155  
   1.156          full_class_name = target.get_class().get_python_name()
   1.157          if full_class_name not in ("java.lang.Object", "java.lang.Exception"):
   1.158 -            self._load_class_name(full_class_name, program)
   1.159 +            program.use_external_name(full_class_name)
   1.160 +            load_class_name(self.class_file, full_class_name, program)
   1.161              self._invoke(target_name, program)
   1.162  
   1.163          # Remove Python None return value.
   1.164 @@ -1551,7 +1565,8 @@
   1.165  
   1.166          full_class_name = target.get_class().get_python_name()
   1.167          if full_class_name not in ("java.lang.Object", "java.lang.Exception"):
   1.168 -            self._load_class_name(full_class_name, program)
   1.169 +            program.use_external_name(full_class_name)
   1.170 +            load_class_name(self.class_file, full_class_name, program)
   1.171              self._invoke(target_name, program)
   1.172  
   1.173      def invokevirtual (self, arguments, program):
   1.174 @@ -1666,7 +1681,7 @@
   1.175              program.load_const(const.get_value())
   1.176              program.call_function(1)
   1.177          else:
   1.178 -            program.load_const(const)
   1.179 +            program.load_const(const.get_value())
   1.180  
   1.181      def ldc_w(self, arguments, program):
   1.182          const = self.class_file.constants[(arguments[0] << 8) + arguments[1] - 1]
   1.183 @@ -1678,7 +1693,7 @@
   1.184              program.load_const(const.get_value())
   1.185              program.call_function(1)
   1.186          else:
   1.187 -            program.load_const(const)
   1.188 +            program.load_const(const.get_value())
   1.189  
   1.190      ldc2_w = ldc_w
   1.191      ldiv = idiv
   1.192 @@ -1800,7 +1815,7 @@
   1.193          # NOTE: Using the string version of the name which may contain incompatible characters.
   1.194          program.load_global("object")
   1.195          program.load_attr("__new__")
   1.196 -        self._load_class_name(target_name, program)
   1.197 +        load_class_name(self.class_file, target_name, program)
   1.198          program.call_function(1)
   1.199  
   1.200      def newarray(self, arguments, program):
   1.201 @@ -1831,7 +1846,8 @@
   1.202          # Get the class name instead of the fully qualified name.
   1.203  
   1.204          full_class_name = target.get_class().get_python_name()
   1.205 -        self._load_class_name(full_class_name, program)
   1.206 +        program.use_external_name(full_class_name)
   1.207 +        load_class_name(self.class_file, full_class_name, program)
   1.208          # NOTE: Using the string version of the name which may contain incompatible characters.
   1.209          program.store_attr(str(target_name))
   1.210  
     2.1 --- a/classhook.py	Thu Dec 09 20:51:18 2004 +0100
     2.2 +++ b/classhook.py	Fri Dec 10 01:04:59 2004 +0100
     2.3 @@ -260,11 +260,6 @@
     2.4              global_names = module.__dict__
     2.5              global_names["__builtins__"] = __builtins__
     2.6  
     2.7 -            # Process each class file, producing a genuine Python class.
     2.8 -
     2.9 -            class_files = []
    2.10 -            classes = []
    2.11 -
    2.12              # Get the real filename.
    2.13  
    2.14              filename = self._get_path_in_archive(filename)
    2.15 @@ -312,27 +307,69 @@
    2.16  
    2.17                  position += 1
    2.18  
    2.19 -            class_files = [class_files[class_name] for class_name in class_file_index]
    2.20 +            # Process each class file, producing a genuine Python class.
    2.21 +            # Create the classes, but establish a proper initialisation order.
    2.22  
    2.23 -            for class_file in class_files:
    2.24 +            class_file_init_index = []
    2.25 +            class_file_init = {}
    2.26 +
    2.27 +            for class_name in class_file_index:
    2.28 +                print "* Class", class_name
    2.29 +                class_file = class_files[class_name]
    2.30                  translator = bytecode.ClassTranslator(class_file)
    2.31                  cls, external_names = translator.process(global_names)
    2.32                  module.__dict__[cls.__name__] = cls
    2.33 -                classes.append((cls, class_file))
    2.34 +
    2.35 +                # Process external names.
    2.36  
    2.37 -                # Import the local names.
    2.38 +                this_class_name_parts = class_file.this_class.get_python_name().split(".")
    2.39 +                this_class_module, this_class_name = this_class_name_parts[:-1], this_class_name_parts[-1]
    2.40  
    2.41                  for external_name in external_names:
    2.42 +                    print "* Name", external_name
    2.43                      external_name_parts = external_name.split(".")
    2.44 -                    if len(external_name_parts) > 1:
    2.45 -                        external_module_name = ".".join(external_name_parts[:-1])
    2.46 +                    external_class_module, external_class_name = external_name_parts[:-1], external_name_parts[-1]
    2.47 +
    2.48 +                    # Names not local to this package need importing.
    2.49 +
    2.50 +                    if len(external_name_parts) > 1 and this_class_module != external_class_module:
    2.51 +
    2.52 +                        external_module_name = ".".join(external_class_module)
    2.53                          print "* Importing", external_module_name
    2.54                          obj = __import__(external_module_name, global_names, {}, [])
    2.55                          global_names[external_name_parts[0]] = obj
    2.56  
    2.57 +                    # Names local to this package may affect initialisation order.
    2.58 +
    2.59 +                    elif external_class_name not in class_file_init_index:
    2.60 +                        try:
    2.61 +                            this_class_name_index = class_file_init_index.index(this_class_name)
    2.62 +
    2.63 +                            # Either insert this name before the current class's
    2.64 +                            # name.
    2.65 +
    2.66 +                            print "* Inserting", external_class_name
    2.67 +                            class_file_init_index.insert(this_class_name_index, external_class_name)
    2.68 +
    2.69 +                        except ValueError:
    2.70 +
    2.71 +                            # Or add this name in anticipation of the current
    2.72 +                            # class's name appearing.
    2.73 +
    2.74 +                            print "* Including", external_class_name
    2.75 +                            class_file_init_index.append(external_class_name)
    2.76 +
    2.77 +                # Add this class name to the initialisation index.
    2.78 +
    2.79 +                if class_name not in class_file_init_index:
    2.80 +                    class_file_init_index.append(this_class_name)
    2.81 +                class_file_init[this_class_name] = (cls, class_file)
    2.82 +
    2.83              # Finally, call __clinit__ methods for all relevant classes.
    2.84  
    2.85 -            for cls, class_file in classes:
    2.86 +            print "** Initialisation order", class_file_init_index
    2.87 +            for class_name in class_file_init_index:
    2.88 +                cls, class_file = class_file_init[class_name]
    2.89                  print "**", cls, class_file
    2.90                  if hasattr(cls, "__clinit__"):
    2.91                      eval(cls.__clinit__.func_code, global_names)