javaclass

Changeset

71:3cf7dae6544b
2004-11-21 Paul Boddie raw files shortlog changelog graph Fixed ldc* implementations. Added external name identification so that such names can be imported into the namespace where the code is run. Introduced more sophisticated resolution of class names where they are used; for example in the new and *static implementations.
bytecode.py (file)
     1.1 --- a/bytecode.py	Sun Nov 21 23:22:28 2004 +0100
     1.2 +++ b/bytecode.py	Sun Nov 21 23:28:28 2004 +0100
     1.3 @@ -57,6 +57,9 @@
     1.4          # A list of constants used as exception handler return addresses.
     1.5          self.constants_for_exceptions = []
     1.6  
     1.7 +        # A list of external names.
     1.8 +        self.external_names = []
     1.9 +
    1.10      def get_output(self):
    1.11          output = []
    1.12          for element in self.output:
    1.13 @@ -132,6 +135,12 @@
    1.14              # NOTE: EXTENDED_ARG not yet supported.
    1.15              raise ValueError, value
    1.16  
    1.17 +    # Higher level methods.
    1.18 +
    1.19 +    def use_external_name(self, name):
    1.20 +        # NOTE: Remove array and object indicators.
    1.21 +        self.external_names.append(name)
    1.22 +
    1.23      def setup_loop(self):
    1.24          self.loops.append(self.position)
    1.25          self.output.append(opmap["SETUP_LOOP"])
    1.26 @@ -230,6 +239,7 @@
    1.27          # NOTE: Since RAISE_VARARGS and END_FINALLY are not really documented,
    1.28          # NOTE: we store the top of the stack and use it later to trigger the
    1.29          # NOTE: magic processes when re-raising.
    1.30 +        self.use_external_name(exc_name)
    1.31  
    1.32          self.rot_two()                      # Stack: raised-exception, exception
    1.33          self.dup_top()                      # Stack: raised-exception, exception, exception
    1.34 @@ -953,6 +963,17 @@
    1.35  
    1.36      "A Java bytecode translator which uses a Python bytecode writer."
    1.37  
    1.38 +    def _load_class_name(self, full_class_name, program):
    1.39 +        this_class_name = str(self.class_file.this_class.get_python_name())
    1.40 +        class_parts = full_class_name.split(".")
    1.41 +        if full_class_name != this_class_name:
    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      def aaload(self, arguments, program):
    1.50          # NOTE: No type checking performed.
    1.51          program.binary_subscr()
    1.52 @@ -1056,6 +1077,8 @@
    1.53      def checkcast(self, arguments, program):
    1.54          index = (arguments[0] << 8) + arguments[1]
    1.55          target_name = self.class_file.constants[index - 1].get_python_name()
    1.56 +        program.use_external_name(target_name)
    1.57 +
    1.58          # NOTE: Using the string version of the name which may contain incompatible characters.
    1.59          target_components = str(target_name).split("/")
    1.60  
    1.61 @@ -1193,11 +1216,13 @@
    1.62  
    1.63      def getstatic(self, arguments, program):
    1.64          index = (arguments[0] << 8) + arguments[1]
    1.65 -        target_name = self.class_file.constants[index - 1].get_python_name()
    1.66 +        target = self.class_file.constants[index - 1]
    1.67 +        target_name = target.get_python_name()
    1.68 +
    1.69          # Get the class name instead of the fully qualified name.
    1.70 -        full_class_name = str(self.class_file.this_class.get_python_name())
    1.71 -        class_name = full_class_name.split(".")[-1]
    1.72 -        program.load_global(class_name) # Stack: classref
    1.73 +
    1.74 +        full_class_name = target.get_class().get_python_name()
    1.75 +        self._load_class_name(full_class_name, program)
    1.76          # NOTE: Using the string version of the name which may contain incompatible characters.
    1.77          program.load_attr(str(target_name))
    1.78  
    1.79 @@ -1364,6 +1389,8 @@
    1.80      def instanceof(self, arguments, program):
    1.81          index = (arguments[0] << 8) + arguments[1]
    1.82          target_name = self.class_file.constants[index - 1].get_python_name()
    1.83 +        program.use_external_name(target_name)
    1.84 +
    1.85          # NOTE: Using the string version of the name which may contain incompatible characters.
    1.86          target_components = str(target_name).split("/")
    1.87  
    1.88 @@ -1403,19 +1430,25 @@
    1.89          target = self.class_file.constants[index - 1]
    1.90          original_name = target.get_name()
    1.91          target_name = target.get_python_name()
    1.92 +
    1.93          # Get the number of parameters from the descriptor.
    1.94 +
    1.95          count = len(target.get_descriptor()[0])
    1.96 +
    1.97          # First, we build a tuple of the reference and arguments.
    1.98 -        program.build_tuple(count + 1)      # Stack: tuple
    1.99 +
   1.100 +        program.build_tuple(count + 1)          # Stack: tuple
   1.101 +
   1.102          # Get the class name instead of the fully qualified name.
   1.103 -        # NOTE: Do proper resolution of classes,
   1.104          # NOTE: Not bothering with Object initialisation.
   1.105 +
   1.106          full_class_name = target.get_class().get_python_name()
   1.107          if full_class_name not in ("java.lang.Object", "java.lang.Exception"):
   1.108 -            class_name = full_class_name.split(".")[-1]
   1.109 -            program.load_global(class_name) # Stack: tuple, classref
   1.110 +            self._load_class_name(full_class_name, program)
   1.111              self._invoke(target_name, program)
   1.112 +
   1.113          # Remove Python None return value.
   1.114 +
   1.115          if str(original_name) == "<init>":
   1.116              program.pop_top()
   1.117  
   1.118 @@ -1426,17 +1459,21 @@
   1.119          index = (arguments[0] << 8) + arguments[1]
   1.120          target = self.class_file.constants[index - 1]
   1.121          target_name = target.get_python_name()
   1.122 +
   1.123          # Get the number of parameters from the descriptor.
   1.124 +
   1.125          count = len(target.get_descriptor()[0])
   1.126 +
   1.127          # Stack: arg1, arg2, ...
   1.128 -        program.build_tuple(count)          # Stack: tuple
   1.129 +
   1.130 +        program.build_tuple(count)              # Stack: tuple
   1.131 +
   1.132          # Use the class to provide access to static methods.
   1.133          # Get the class name instead of the fully qualified name.
   1.134 -        # NOTE: Do proper resolution of classes,
   1.135 +
   1.136          full_class_name = target.get_class().get_python_name()
   1.137          if full_class_name not in ("java.lang.Object", "java.lang.Exception"):
   1.138 -            class_name = full_class_name.split(".")[-1]
   1.139 -            program.load_global(class_name) # Stack: tuple, classref
   1.140 +            self._load_class_name(full_class_name, program)
   1.141              self._invoke(target_name, program)
   1.142  
   1.143      def invokevirtual (self, arguments, program):
   1.144 @@ -1544,22 +1581,24 @@
   1.145      def ldc(self, arguments, program):
   1.146          const = self.class_file.constants[arguments[0] - 1]
   1.147          if isinstance(const, classfile.StringInfo):
   1.148 +            program.use_external_name("java.lang.String")
   1.149              program.load_global("java")
   1.150              program.load_attr("lang")
   1.151              program.load_attr("String")
   1.152              program.load_const(const.get_value())
   1.153 -            program.call_function(2)
   1.154 +            program.call_function(1)
   1.155          else:
   1.156              program.load_const(const)
   1.157  
   1.158      def ldc_w(self, arguments, program):
   1.159          const = self.class_file.constants[(arguments[0] << 8) + arguments[1] - 1]
   1.160          if isinstance(const, classfile.StringInfo):
   1.161 +            program.use_external_name("java.lang.String")
   1.162              program.load_global("java")
   1.163              program.load_attr("lang")
   1.164              program.load_attr("String")
   1.165              program.load_const(const.get_value())
   1.166 -            program.call_function(2)
   1.167 +            program.call_function(1)
   1.168          else:
   1.169              program.load_const(const)
   1.170  
   1.171 @@ -1675,12 +1714,15 @@
   1.172      def new(self, arguments, program):
   1.173          # This operation is considered to be the same as the calling of the
   1.174          # initialisation method of the given class with no arguments.
   1.175 +
   1.176          index = (arguments[0] << 8) + arguments[1]
   1.177          target_name = self.class_file.constants[index - 1].get_python_name()
   1.178 +        program.use_external_name(target_name)
   1.179 +
   1.180          # NOTE: Using the string version of the name which may contain incompatible characters.
   1.181          program.load_global("object")
   1.182          program.load_attr("__new__")
   1.183 -        program.load_global(str(target_name))
   1.184 +        self._load_class_name(target_name, program)
   1.185          program.call_function(1)
   1.186  
   1.187      def newarray(self, arguments, program):
   1.188 @@ -1705,12 +1747,13 @@
   1.189  
   1.190      def putstatic(self, arguments, program):
   1.191          index = (arguments[0] << 8) + arguments[1]
   1.192 -        target_name = self.class_file.constants[index - 1].get_python_name()
   1.193 +        target = self.class_file.constants[index - 1]
   1.194 +        target_name = target.get_python_name()
   1.195 +
   1.196          # Get the class name instead of the fully qualified name.
   1.197 -        # NOTE: Need to find the class that declared the field being accessed.
   1.198 -        full_class_name = str(self.class_file.this_class.get_python_name())
   1.199 -        class_name = full_class_name.split(".")[-1]
   1.200 -        program.load_global(class_name) # Stack: classref
   1.201 +
   1.202 +        full_class_name = target.get_class().get_python_name()
   1.203 +        self._load_class_name(full_class_name, program)
   1.204          # NOTE: Using the string version of the name which may contain incompatible characters.
   1.205          program.store_attr(str(target_name))
   1.206  
   1.207 @@ -1863,6 +1906,7 @@
   1.208                  program.store_fast(2)               # Stack: arguments (found = 1)
   1.209  
   1.210              # Emit a list of parameter types.
   1.211 +
   1.212              descriptor_types = method.get_descriptor()[0]
   1.213              for descriptor_type in descriptor_types:
   1.214                  base_type, object_type, array_type = descriptor_type
   1.215 @@ -2001,6 +2045,8 @@
   1.216  
   1.217          """
   1.218          Process the class, storing it in the 'global_names' dictionary provided.
   1.219 +        Return a tuple containing the class and a list of external names
   1.220 +        referenced by the class's methods.
   1.221          """
   1.222  
   1.223          namespace = {}
   1.224 @@ -2015,11 +2061,19 @@
   1.225          # Make the methods.
   1.226  
   1.227          real_methods = {}
   1.228 +        external_names = []
   1.229 +
   1.230          for method in self.class_file.methods:
   1.231              real_method_name = str(method.get_name())
   1.232              method_name = str(method.get_python_name())
   1.233  
   1.234 -            t, w = self.translate_method(method)
   1.235 +            translator, writer = self.translate_method(method)
   1.236 +
   1.237 +            # Add external names to the master list.
   1.238 +
   1.239 +            for external_name in writer.external_names:
   1.240 +                if external_name not in external_names:
   1.241 +                    external_names.append(external_name)
   1.242  
   1.243              # Fix up special class initialisation methods and static methods.
   1.244  
   1.245 @@ -2028,12 +2082,13 @@
   1.246                  nargs = len(method.get_descriptor()[0])
   1.247              else:
   1.248                  nargs = len(method.get_descriptor()[0]) + 1
   1.249 -            nlocals = w.max_locals + 1
   1.250 +            nlocals = writer.max_locals + 1
   1.251              flags = 67
   1.252  
   1.253              # NOTE: Add line number table later.
   1.254  
   1.255 -            code = new.code(nargs, nlocals, w.max_stack_depth, flags, w.get_output(), tuple(w.get_constants()), tuple(w.get_names()),
   1.256 +            code = new.code(nargs, nlocals, writer.max_stack_depth, flags, writer.get_output(),
   1.257 +                tuple(writer.get_constants()), tuple(writer.get_names()),
   1.258                  tuple(self.make_varnames(nlocals, method_is_static)), self.filename, method_name, 0, "")
   1.259  
   1.260              # NOTE: May need more globals.
   1.261 @@ -2072,7 +2127,7 @@
   1.262          cls = new.classobj(class_name, bases, namespace)
   1.263          global_names[cls.__name__] = cls
   1.264  
   1.265 -        return cls
   1.266 +        return cls, external_names
   1.267  
   1.268      def get_base_classes(self, global_names):
   1.269  
   1.270 @@ -2143,6 +2198,6 @@
   1.271          f = open(filename, "rb")
   1.272          c = classfile.ClassFile(f.read())
   1.273          translator = ClassTranslator(c)
   1.274 -        cls = translator.process(global_names)
   1.275 +        cls, external_names = translator.process(global_names)
   1.276  
   1.277  # vim: tabstop=4 expandtab shiftwidth=4