1.1 --- a/bytecode.py Fri Nov 12 01:28:34 2004 +0100
1.2 +++ b/bytecode.py Fri Nov 12 01:32:28 2004 +0100
1.3 @@ -7,6 +7,7 @@
1.4 NOTE: Synchronized constructs are not actually supported.
1.5 """
1.6
1.7 +import classfile
1.8 from dis import opmap, cmp_op # for access to Python bytecode values and operators
1.9 from UserDict import UserDict
1.10 import new
1.11 @@ -18,6 +19,9 @@
1.12 "A Python bytecode writer."
1.13
1.14 def __init__(self):
1.15 + # A stack of loop start instructions corresponding to loop blocks.
1.16 + self.loops = []
1.17 +
1.18 # A stack of loop block or exception block start positions.
1.19 self.blocks = []
1.20
1.21 @@ -119,19 +123,27 @@
1.22 raise ValueError, value
1.23
1.24 def setup_loop(self):
1.25 - self.blocks.append(self.position)
1.26 + self.loops.append(self.position)
1.27 self.output.append(opmap["SETUP_LOOP"])
1.28 self.position += 1
1.29 self._write_value(0) # To be filled in later
1.30
1.31 def end_loop(self):
1.32 - current_loop_start = self.blocks.pop()
1.33 - self.jump_absolute(current_loop_start)
1.34 + current_loop_start = self.loops.pop()
1.35 + current_loop_real_start = self.blocks.pop()
1.36 + #print "<", self.blocks, current_loop_real_start
1.37 + # Fix the iterator delta.
1.38 + # NOTE: Using 3 as the assumed length of the FOR_ITER instruction.
1.39 + # NOTE: 8-bit limit.
1.40 + self.jump_absolute(current_loop_real_start)
1.41 + self.output[current_loop_real_start + 1] = self.position - current_loop_real_start - 3
1.42 + self.output[current_loop_real_start + 2] = 0
1.43 + self.pop_block()
1.44 + # Fix the loop delta.
1.45 # NOTE: Using 3 as the assumed length of the SETUP_LOOP instruction.
1.46 # NOTE: 8-bit limit.
1.47 self.output[current_loop_start + 1] = self.position - current_loop_start - 3
1.48 self.output[current_loop_start + 2] = 0
1.49 - self.pop_block()
1.50
1.51 def jump_to_label(self, status, name):
1.52 # Record the instruction using the jump.
1.53 @@ -272,15 +284,25 @@
1.54 self.update_stack_depth(-1)
1.55 self.update_locals(index)
1.56
1.57 - # Normal bytecode generators.
1.58 -
1.59 def for_iter(self):
1.60 self.blocks.append(self.position)
1.61 + #print ">", self.blocks
1.62 self.output.append(opmap["FOR_ITER"])
1.63 self.position += 1
1.64 self._write_value(0) # To be filled in later
1.65 self.update_stack_depth(1)
1.66
1.67 + def break_loop(self):
1.68 + self.output.append(opmap["BREAK_LOOP"])
1.69 + self.position += 1
1.70 + self.jump_absolute(self.blocks[-1])
1.71 +
1.72 + # Normal bytecode generators.
1.73 +
1.74 + def get_iter(self):
1.75 + self.output.append(opmap["GET_ITER"])
1.76 + self.position += 1
1.77 +
1.78 def jump_if_false(self, offset=0):
1.79 self.output.append(opmap["JUMP_IF_FALSE"])
1.80 self.position += 1
1.81 @@ -428,6 +450,11 @@
1.82 self.output.append(opmap["END_FINALLY"])
1.83 self.position += 1
1.84
1.85 + def unpack_sequence(self, count):
1.86 + self.output.append(opmap["UNPACK_SEQUENCE"])
1.87 + self.position += 1
1.88 + self._write_value(count)
1.89 +
1.90 # Utility classes and functions.
1.91
1.92 class LazyDict(UserDict):
1.93 @@ -1254,7 +1281,8 @@
1.94 # NOTE: signature-based polymorphism.
1.95 # NOTE: Java rules not specifically obeyed.
1.96 index = (arguments[0] << 8) + arguments[1]
1.97 - count = arguments[2]
1.98 + # NOTE: "count" == nargs + 1, apparently.
1.99 + count = arguments[2] - 1
1.100 target_name = self.class_file.constants[index - 1].get_python_name()
1.101 # Stack: objectref, arg1, arg2, ...
1.102 program.build_tuple(count) # Stack: objectref, tuple
1.103 @@ -1684,7 +1712,7 @@
1.104 translator.process(method, writer)
1.105 return translator, writer
1.106
1.107 - def make_method(self, method_name, methods, namespace):
1.108 + def make_method(self, method_name, methods, global_names, namespace):
1.109 if method_name == "<init>":
1.110 method_name = "__init__"
1.111 # Where only one method exists, just make an alias.
1.112 @@ -1692,17 +1720,118 @@
1.113 method, fn = methods[0]
1.114 namespace[method_name] = fn
1.115 return
1.116 - return # for now
1.117 # Find the maximum number of parameters involved.
1.118 #maximum = max([len(method.get_descriptor()[0]) for method in methods])
1.119 - #program = BytecodeWriter()
1.120 + program = BytecodeWriter()
1.121 # NOTE: The code below should use dictionary-based dispatch for better performance.
1.122 - #for method in methods:
1.123 - # program.load_fast(1) # Stack: arguments
1.124 - # program.get_iter() # Stack: arguments, iter
1.125 - # program.for_iter() # Stack: arguments, iter, argument
1.126 - # program.dup_top() # Stack: arguments, iter, argument, argument
1.127 - # for parameter in method.get_descriptor()[0]:
1.128 + program.load_fast(1) # Stack: arguments
1.129 + for method, fn in methods:
1.130 + program.dup_top() # Stack: arguments, arguments
1.131 + program.load_const(1)
1.132 + program.store_fast(2) # found = 1
1.133 + program.setup_loop()
1.134 + # Emit a list of parameter types.
1.135 + descriptor_types = method.get_descriptor()[0]
1.136 + for descriptor_type in descriptor_types:
1.137 + base_type, object_type, array_type = descriptor_type
1.138 + python_type = classfile.descriptor_base_type_mapping[base_type]
1.139 + if python_type == "instance":
1.140 + # NOTE: This will need extending.
1.141 + python_type = object_type
1.142 + program.load_global(python_type) # Stack: arguments, type, ...
1.143 + program.build_list(len(descriptor_types))
1.144 + # Stack: arguments, types
1.145 + # Make a map of arguments and types.
1.146 + program.load_const(None) # Stack: arguments, types, None
1.147 + program.rot_three() # Stack: None, arguments, types
1.148 + program.build_tuple(3) # Stack: tuple
1.149 + program.load_global("map") # Stack: tuple, map
1.150 + program.rot_two() # Stack: map, tuple
1.151 + program.load_global("apply") # Stack: map, tuple, apply
1.152 + program.rot_three() # Stack: apply, map, tuple
1.153 + program.call_function(2) # Stack: tuple (mapping arguments to types)
1.154 + # Loop over each pair.
1.155 + program.get_iter() # Stack: iter
1.156 + program.for_iter() # Stack: iter, (argument, type)
1.157 + program.unpack_sequence(2) # Stack: iter, type, argument
1.158 + program.dup_top() # Stack: iter, type, argument, argument
1.159 + program.load_const(None) # Stack: iter, type, argument, argument, None
1.160 + program.compare_op("is") # Stack: iter, type, argument, result
1.161 + # Missing argument?
1.162 + program.jump_to_label(0, "present")
1.163 + program.pop_top() # Stack: iter, type, argument
1.164 + program.pop_top() # Stack: iter, type
1.165 + program.pop_top() # Stack: iter
1.166 + program.load_const(0)
1.167 + program.store_fast(2) # found = 0
1.168 + program.break_loop()
1.169 + # Argument was present.
1.170 + program.start_label("present")
1.171 + program.pop_top() # Stack: iter, type, argument
1.172 + program.rot_two() # Stack: iter, argument, type
1.173 + program.dup_top() # Stack: iter, argument, type, type
1.174 + program.load_const(None) # Stack: iter, argument, type, type, None
1.175 + program.compare_op("is") # Stack: iter, argument, type, result
1.176 + # Missing parameter type?
1.177 + program.jump_to_label(0, "present")
1.178 + program.pop_top() # Stack: iter, argument, type
1.179 + program.pop_top() # Stack: iter, argument
1.180 + program.pop_top() # Stack: iter
1.181 + program.load_const(0)
1.182 + program.store_fast(2) # found = 0
1.183 + program.break_loop()
1.184 + # Parameter was present.
1.185 + program.start_label("present")
1.186 + program.pop_top() # Stack: iter, argument, type
1.187 + program.build_tuple(2) # Stack: iter, (argument, type)
1.188 + program.load_global("isinstance") # Stack: iter, (argument, type), isinstance
1.189 + program.rot_two() # Stack: iter, isinstance, (argument, type)
1.190 + program.load_global("apply") # Stack: iter, isinstance, (argument, type), apply
1.191 + program.rot_three() # Stack: iter, apply, isinstance, (argument, type)
1.192 + program.call_function(2) # Stack: iter, result
1.193 + program.jump_to_label(1, "match")
1.194 + program.pop_top() # Stack: iter
1.195 + program.load_const(0)
1.196 + program.store_fast(2) # found = 0
1.197 + program.break_loop()
1.198 + # Argument type and parameter type matched.
1.199 + program.start_label("match")
1.200 + program.pop_top() # Stack: iter
1.201 + program.end_loop() # Stack: iter
1.202 + # If all the parameters matched, call the method.
1.203 + program.load_fast(2) # Stack: iter, match
1.204 + program.jump_to_label(0, "failed")
1.205 + # All the parameters matched.
1.206 + program.pop_top() # Stack: iter
1.207 + program.load_fast(1) # Stack: arguments
1.208 + program.load_fast(0) # Stack: arguments, self
1.209 + program.load_attr(str(method.get_python_name()))
1.210 + # Stack: arguments, method
1.211 + program.rot_two() # Stack: method, arguments
1.212 + program.load_global("apply") # Stack: method, arguments, apply
1.213 + program.rot_three() # Stack: apply, method, arguments
1.214 + program.call_function(2) # Stack: result
1.215 + program.return_value()
1.216 + # Try the next method if arguments or parameters were missing or incorrect.
1.217 + program.start_label("failed")
1.218 + program.pop_top() # Stack: iter
1.219 + program.pop_top() # Stack:
1.220 + # Raise an exception if nothing matched.
1.221 + # NOTE: Improve this.
1.222 + program.load_const("No matching method")
1.223 + program.raise_varargs(1)
1.224 + program.load_const(None)
1.225 + program.return_value()
1.226 +
1.227 + # Add the code as a method in the namespace.
1.228 + # NOTE: One actual parameter, flags as 71 apparently means that a list
1.229 + # NOTE: parameter is used in a method.
1.230 + nlocals = program.max_locals + 1
1.231 + code = new.code(1, nlocals, program.max_stack_depth, 71, program.get_output(),
1.232 + tuple(program.get_constants()), tuple(program.get_names()), tuple(self.make_varnames(nlocals)),
1.233 + self.filename, method_name, 0, "")
1.234 + fn = new.function(code, global_names)
1.235 + namespace[method_name] = fn
1.236
1.237 def process(self, global_names):
1.238 namespace = {}
1.239 @@ -1714,7 +1843,7 @@
1.240 method_name = str(method.get_python_name())
1.241 # NOTE: Add line number table later.
1.242 code = new.code(nargs, nlocals, w.max_stack_depth, 67, w.get_output(), tuple(w.get_constants()), tuple(w.get_names()),
1.243 - tuple(make_varnames(nlocals)), self.filename, method_name, 0, "")
1.244 + tuple(self.make_varnames(nlocals)), self.filename, method_name, 0, "")
1.245 # NOTE: May need more globals.
1.246 fn = new.function(code, global_names)
1.247 namespace[method_name] = fn
1.248 @@ -1729,26 +1858,35 @@
1.249 bases = ()
1.250 # Define method dispatchers.
1.251 for real_method_name, methods in real_methods.items():
1.252 - self.make_method(real_method_name, methods, namespace)
1.253 + self.make_method(real_method_name, methods, global_names, namespace)
1.254 cls = new.classobj(str(self.class_file.this_class.get_python_name()), bases, namespace)
1.255 global_names[cls.__name__] = cls
1.256 return cls
1.257
1.258 -def make_varnames(nlocals):
1.259 - l = ["self"]
1.260 - for i in range(1, nlocals):
1.261 - l.append("_l%s" % i)
1.262 - return l[:nlocals]
1.263 + def make_varnames(self, nlocals):
1.264 + l = ["self"]
1.265 + for i in range(1, nlocals):
1.266 + l.append("_l%s" % i)
1.267 + return l[:nlocals]
1.268 +
1.269 +def _map(*args):
1.270 + print args
1.271 + return apply(__builtins__.map, args)
1.272 +
1.273 +def _isinstance(*args):
1.274 + print args
1.275 + return apply(__builtins__.isinstance, args)
1.276
1.277 if __name__ == "__main__":
1.278 import sys
1.279 - from classfile import ClassFile
1.280 import dis
1.281 global_names = {}
1.282 global_names.update(__builtins__.__dict__)
1.283 + #global_names["isinstance"] = _isinstance
1.284 + #global_names["map"] = _map
1.285 for filename in sys.argv[1:]:
1.286 f = open(filename, "rb")
1.287 - c = ClassFile(f.read())
1.288 + c = classfile.ClassFile(f.read())
1.289 translator = ClassTranslator(c)
1.290 cls = translator.process(global_names)
1.291