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)