javaclass

Changeset

53:543067f19564
2004-11-14 Paul Boddie raw files shortlog changelog graph Padded out comments and loops for readability.
bytecode.py (file)
     1.1 --- a/bytecode.py	Sun Nov 14 02:38:25 2004 +0100
     1.2 +++ b/bytecode.py	Sun Nov 14 19:36:24 2004 +0100
     1.3 @@ -177,10 +177,12 @@
     1.4  
     1.5      def ret(self, index):
     1.6          self.load_fast(index)
     1.7 +
     1.8          # Previously, the constant stored on the stack by jsr/jsr_w was stored
     1.9          # in a local variable. In the JVM, extracting the value from the local
    1.10          # variable and jumping can be done at runtime. In the Python VM, any
    1.11          # jump target must be known in advance and written into the bytecode.
    1.12 +
    1.13          for constant in self.constants_for_exceptions:
    1.14              self.dup_top()              # Stack: actual-address, actual-address
    1.15              self.load_const(constant)   # Stack: actual-address, actual-address, suggested-address
    1.16 @@ -191,7 +193,9 @@
    1.17              self.jump_absolute(constant)
    1.18              self.start_label("const")
    1.19              self.pop_top()              # Stack: actual-address
    1.20 +
    1.21          # NOTE: If we get here, something is really wrong.
    1.22 +
    1.23          self.pop_top()              # Stack:
    1.24  
    1.25      def setup_except(self, target):
    1.26 @@ -220,8 +224,10 @@
    1.27          self._rewrite_value(current_exception_start + 1, target - current_exception_start - 3)
    1.28  
    1.29      def start_handler(self, exc_name):
    1.30 +
    1.31          # Where handlers are begun, produce bytecode to test the type of
    1.32          # the exception.
    1.33 +
    1.34          self.dup_top()                      # Stack: exception, exception
    1.35          self.load_global(str(exc_name))     # Stack: exception, exception, handled-exception
    1.36          self.compare_op("exception match")  # Stack: exception, result
    1.37 @@ -551,6 +557,7 @@
    1.38          self.method = method
    1.39  
    1.40          # NOTE: Potentially unreliable way of getting necessary information.
    1.41 +
    1.42          code, exception_table = None, None
    1.43          for attribute in method.attributes:
    1.44              if isinstance(attribute, classfile.CodeAttributeInfo):
    1.45 @@ -560,6 +567,7 @@
    1.46              return
    1.47  
    1.48          # Produce a structure which permits fast access to exception details.
    1.49 +
    1.50          exception_block_start = {}
    1.51          exception_block_end = {}
    1.52          exception_block_handler = {}
    1.53 @@ -567,38 +575,51 @@
    1.54          reversed_exception_table.reverse()
    1.55  
    1.56          # Later entries have wider coverage than earlier entries.
    1.57 +
    1.58          for exception in reversed_exception_table:
    1.59 +
    1.60              # Index start positions.
    1.61 +
    1.62              if not exception_block_start.has_key(exception.start_pc):
    1.63                  exception_block_start[exception.start_pc] = []
    1.64              exception_block_start[exception.start_pc].append(exception)
    1.65 +
    1.66              # Index end positions.
    1.67 +
    1.68              if not exception_block_end.has_key(exception.end_pc):
    1.69                  exception_block_end[exception.end_pc] = []
    1.70              exception_block_end[exception.end_pc].append(exception)
    1.71 +
    1.72              # Index handler positions.
    1.73 +
    1.74              if not exception_block_handler.has_key(exception.handler_pc):
    1.75                  exception_block_handler[exception.handler_pc] = []
    1.76              exception_block_handler[exception.handler_pc].append(exception)
    1.77  
    1.78          # Process each instruction in the code.
    1.79 +
    1.80          while self.java_position < len(code):
    1.81              self.position_mapping[self.java_position] = program.position
    1.82  
    1.83              # Insert exception handling constructs.
    1.84 +
    1.85              block_starts = exception_block_start.get(self.java_position, [])
    1.86              for exception in block_starts:
    1.87 +
    1.88                  # Note that the absolute position is used.
    1.89 +
    1.90                  if exception.catch_type == 0:
    1.91                      program.setup_finally(self.position_mapping[exception.handler_pc])
    1.92                  else:
    1.93                      program.setup_except(self.position_mapping[exception.handler_pc])
    1.94 +
    1.95              if block_starts:
    1.96                  self.in_finally = 0
    1.97  
    1.98              # Insert exception handler details.
    1.99              # NOTE: Ensure that pop_block is reachable by possibly inserting it at the start of finally handlers.
   1.100              # NOTE: Insert a check for the correct exception at the start of each handler.
   1.101 +
   1.102              for exception in exception_block_handler.get(self.java_position, []):
   1.103                  program.end_exception()
   1.104                  if exception.catch_type == 0:
   1.105 @@ -607,21 +628,26 @@
   1.106                      program.start_handler(self.class_file.constants[exception.catch_type - 1].get_python_name())
   1.107  
   1.108              # Process the bytecode at the current position.
   1.109 +
   1.110              bytecode = ord(code[self.java_position])
   1.111              mnemonic, number_of_arguments = self.java_bytecodes[bytecode]
   1.112              number_of_arguments = self.process_bytecode(mnemonic, number_of_arguments, code, program)
   1.113              next_java_position = self.java_position + 1 + number_of_arguments
   1.114  
   1.115              # Insert exception block end details.
   1.116 +
   1.117              for exception in exception_block_end.get(next_java_position, []):
   1.118 +
   1.119                  # NOTE: Insert jump beyond handlers.
   1.120                  # NOTE: program.jump_forward/absolute(...)
   1.121                  # NOTE: Insert end finally at end of handlers as well as where "ret" occurs.
   1.122 +
   1.123                  if exception.catch_type != 0:
   1.124                      program.pop_block()
   1.125  
   1.126              # Only advance the JVM position after sneaking in extra Python
   1.127              # instructions.
   1.128 +
   1.129              self.java_position = next_java_position
   1.130  
   1.131      def process_bytecode(self, mnemonic, number_of_arguments, code, program):
   1.132 @@ -631,10 +657,12 @@
   1.133                  arguments.append(ord(code[self.java_position + 1 + j]))
   1.134  
   1.135              # Call the handler.
   1.136 +
   1.137              getattr(self, mnemonic)(arguments, program)
   1.138              return number_of_arguments
   1.139          else:
   1.140              # Call the handler.
   1.141 +
   1.142              return getattr(self, mnemonic)(code[self.java_position+1:], program)
   1.143  
   1.144      java_bytecodes = {
   1.145 @@ -1319,7 +1347,9 @@
   1.146          target = self.class_file.constants[index - 1]
   1.147          original_name = target.get_name()
   1.148          target_name = target.get_python_name()
   1.149 +
   1.150          # Get the number of parameters from the descriptor.
   1.151 +
   1.152          count = len(target.get_descriptor()[0])
   1.153  
   1.154          # The stack may contain one of the following patterns:
   1.155 @@ -1333,15 +1363,18 @@
   1.156          # method != __init__, objectref != self -> should not occur
   1.157  
   1.158          # First, we build a tuple of the reference and arguments.
   1.159 +
   1.160          program.build_tuple(count + 1)  # Stack: tuple
   1.161  
   1.162          # Then, we test the nature of the reference.
   1.163 +
   1.164          program.dup_top()               # Stack: tuple, tuple
   1.165          program.load_const(0)           # Stack: tuple, tuple, 0
   1.166          program.binary_subscr()         # Stack: tuple, reference
   1.167          program.dup_top()               # Stack: tuple, reference, reference
   1.168  
   1.169          # Is it self?
   1.170 +
   1.171          program.load_fast(0)            # Stack: tuple, reference, reference, self
   1.172          program.compare_op("is")        # Stack: tuple, reference, result
   1.173          program.jump_to_label(1, "is-self")
   1.174 @@ -1349,6 +1382,7 @@
   1.175  
   1.176          # Is another class or reference.
   1.177          # NOTE: Reference case not covered!
   1.178 +
   1.179          if str(original_name) == "<init>":
   1.180              program.rot_two()               # Stack: reference, tuple
   1.181              program.load_const(1)           # Stack: reference, tuple, 1
   1.182 @@ -1529,15 +1563,21 @@
   1.183      lneg = ineg
   1.184  
   1.185      def lookupswitch(self, arguments, program):
   1.186 +
   1.187          # Find the offset to the next 4 byte boundary in the code.
   1.188 +
   1.189          d, r = divmod(self.java_position, 4)
   1.190          to_boundary = (4 - r) % 4
   1.191 +
   1.192          # Get the pertinent arguments.
   1.193 +
   1.194          arguments = arguments[to_boundary:]
   1.195          default = (arguments[0] << 24) + (arguments[1] << 16) + (arguments[2] << 8) + arguments[3]
   1.196          npairs = (arguments[4] << 24) + (arguments[5] << 16) + (arguments[6] << 8) + arguments[7]
   1.197 +
   1.198          # Process the pairs.
   1.199          # NOTE: This is not the most optimal implementation.
   1.200 +
   1.201          pair_index = 8
   1.202          for pair in range(0, npairs):
   1.203              match = ((arguments[pair_index] << 24) + (arguments[pair_index + 1] << 16) +
   1.204 @@ -1559,7 +1599,9 @@
   1.205              program.pop_top()                                           # Stack: key
   1.206              # Update the index.
   1.207              pair_index += 8
   1.208 +
   1.209          # Generate the default.
   1.210 +
   1.211          java_absolute = self.java_position + default
   1.212          program.jump_absolute(self.position_mapping[java_absolute])
   1.213  
   1.214 @@ -1680,23 +1722,33 @@
   1.215          program.rot_two()
   1.216  
   1.217      def tableswitch(self, arguments, program):
   1.218 +
   1.219          # Find the offset to the next 4 byte boundary in the code.
   1.220 +
   1.221          d, r = divmod(self.java_position, 4)
   1.222          to_boundary = (4 - r) % 4
   1.223 +
   1.224          # Get the pertinent arguments.
   1.225 +
   1.226          arguments = arguments[to_boundary:]
   1.227          default = (arguments[0] << 24) + (arguments[1] << 16) + (arguments[2] << 8) + arguments[3]
   1.228          low = (arguments[4] << 24) + (arguments[5] << 16) + (arguments[6] << 8) + arguments[7]
   1.229          high = (arguments[8] << 24) + (arguments[9] << 16) + (arguments[10] << 8) + arguments[11]
   1.230 +
   1.231          # Process the jump entries.
   1.232          # NOTE: This is not the most optimal implementation.
   1.233 +
   1.234          jump_index = 8
   1.235          for jump in range(low, high + 1):
   1.236              offset = signed4((arguments[jump_index] << 24) + (arguments[jump_index + 1] << 16) +
   1.237                  (arguments[jump_index + 2] << 8) + arguments[jump_index + 3])
   1.238 +
   1.239              # Calculate the branch target.
   1.240 +
   1.241              java_absolute = self.java_position + offset
   1.242 +
   1.243              # Generate branching code.
   1.244 +
   1.245              program.dup_top()                                           # Stack: key, key
   1.246              program.load_const(jump)                                    # Stack: key, key, jump
   1.247              program.compare_op("==")                                    # Stack: key, result
   1.248 @@ -1704,12 +1756,18 @@
   1.249              program.pop_top()                                           # Stack: key
   1.250              program.pop_top()                                           # Stack:
   1.251              program.jump_absolute(self.position_mapping[java_absolute])
   1.252 +
   1.253              # Generate the label for the end of the branching code.
   1.254 +
   1.255              program.start_label("end")
   1.256              program.pop_top()                                           # Stack: key
   1.257 +
   1.258              # Update the index.
   1.259 +
   1.260              jump_index += 8
   1.261 +
   1.262          # Generate the default.
   1.263 +
   1.264          java_absolute = self.java_position + default
   1.265          program.jump_absolute(self.position_mapping[java_absolute])
   1.266  
   1.267 @@ -1755,15 +1813,21 @@
   1.268  
   1.269          if method_name == "<init>":
   1.270              method_name = "__init__"
   1.271 +
   1.272          # Where only one method exists, just make an alias.
   1.273 +
   1.274          if len(methods) == 1:
   1.275              method, fn = methods[0]
   1.276              namespace[method_name] = fn
   1.277              return
   1.278 +
   1.279          # Find the maximum number of parameters involved.
   1.280          #maximum = max([len(method.get_descriptor()[0]) for method in methods])
   1.281 +
   1.282          program = BytecodeWriter()
   1.283 +
   1.284          # NOTE: The code below should use dictionary-based dispatch for better performance.
   1.285 +
   1.286          program.load_fast(1)                        # Stack: arguments
   1.287          for method, fn in methods:
   1.288              program.setup_loop()
   1.289 @@ -1855,8 +1919,10 @@
   1.290              # Try the next method if arguments or parameters were missing or incorrect.
   1.291              program.start_label("failed")
   1.292              program.pop_top()                       # Stack: arguments
   1.293 +
   1.294          # Raise an exception if nothing matched.
   1.295          # NOTE: Improve this.
   1.296 +
   1.297          program.load_const("No matching method")
   1.298          program.raise_varargs(1)
   1.299          program.load_const(None)
   1.300 @@ -1865,6 +1931,7 @@
   1.301          # Add the code as a method in the namespace.
   1.302          # NOTE: One actual parameter, flags as 71 apparently means that a list
   1.303          # NOTE: parameter is used in a method.
   1.304 +
   1.305          nlocals = program.max_locals + 1
   1.306          code = new.code(1, nlocals, program.max_stack_depth, 71, program.get_output(),
   1.307              tuple(program.get_constants()), tuple(program.get_names()), tuple(self.make_varnames(nlocals)),
   1.308 @@ -1885,22 +1952,32 @@
   1.309              nlocals = w.max_locals + 1
   1.310              nargs = len(method.get_descriptor()[0]) + 1
   1.311              method_name = str(method.get_python_name())
   1.312 +
   1.313              # NOTE: Add line number table later.
   1.314 +
   1.315              code = new.code(nargs, nlocals, w.max_stack_depth, 67, w.get_output(), tuple(w.get_constants()), tuple(w.get_names()),
   1.316                  tuple(self.make_varnames(nlocals)), self.filename, method_name, 0, "")
   1.317 +
   1.318              # NOTE: May need more globals.
   1.319 +
   1.320              fn = new.function(code, global_names)
   1.321              namespace[method_name] = fn
   1.322              real_method_name = str(method.get_name())
   1.323              if not real_methods.has_key(real_method_name):
   1.324                  real_methods[real_method_name] = []
   1.325              real_methods[real_method_name].append((method, fn))
   1.326 +
   1.327          # Define superclasses.
   1.328 +
   1.329          bases = self.get_base_classes(global_names)
   1.330 +
   1.331          # Define method dispatchers.
   1.332 +
   1.333          for real_method_name, methods in real_methods.items():
   1.334              self.make_method(real_method_name, methods, global_names, namespace)
   1.335 +
   1.336          # Use only the last part of the fully qualified name.
   1.337 +
   1.338          full_class_name = str(self.class_file.this_class.get_python_name())
   1.339          class_name = full_class_name.split(".")[-1]
   1.340          cls = new.classobj(class_name, bases, namespace)