1.1 --- a/bytecode.py Thu Dec 09 02:18:13 2004 +0100
1.2 +++ b/bytecode.py Thu Dec 09 19:44:31 2004 +0100
1.3 @@ -19,6 +19,9 @@
1.4 "A Python bytecode writer."
1.5
1.6 def __init__(self):
1.7 +
1.8 + "Initialise the writer."
1.9 +
1.10 # A stack of loop start instructions corresponding to loop blocks.
1.11 self.loops = []
1.12
1.13 @@ -61,6 +64,9 @@
1.14 self.external_names = []
1.15
1.16 def get_output(self):
1.17 +
1.18 + "Return the output of the writer as a string."
1.19 +
1.20 output = []
1.21 for element in self.output:
1.22 if isinstance(element, LazySubValue):
1.23 @@ -72,6 +78,12 @@
1.24 return "".join(output)
1.25
1.26 def get_constants(self):
1.27 +
1.28 + """
1.29 + Return a list of constants with ordering significant to the code
1.30 + employing them.
1.31 + """
1.32 +
1.33 l = self._get_list(self._invert(self.constants))
1.34 result = []
1.35 for i in l:
1.36 @@ -85,15 +97,33 @@
1.37 # return self._get_list(self._invert(self.globals))
1.38
1.39 def get_names(self):
1.40 +
1.41 + """
1.42 + Return a list of names with ordering significant to the code employing
1.43 + them.
1.44 + """
1.45 +
1.46 return self._get_list(self._invert(self.names))
1.47
1.48 def _invert(self, d):
1.49 +
1.50 + """
1.51 + Return a new dictionary whose key-to-value mapping is in the inverse of
1.52 + that found in 'd'.
1.53 + """
1.54 +
1.55 inverted = {}
1.56 for k, v in d.items():
1.57 inverted[v] = k
1.58 return inverted
1.59
1.60 def _get_list(self, d):
1.61 +
1.62 + """
1.63 + Traverse the dictionary 'd' returning a list whose values appear at the
1.64 + position denoted by each value's key in 'd'.
1.65 + """
1.66 +
1.67 l = []
1.68 for i in range(0, len(d.keys())):
1.69 l.append(d[i])
1.70 @@ -102,17 +132,34 @@
1.71 # Administrative methods.
1.72
1.73 def update_stack_depth(self, change):
1.74 +
1.75 + """
1.76 + Given the stated 'change' in stack depth, update the maximum stack depth
1.77 + where appropriate.
1.78 + """
1.79 +
1.80 self.stack_depth += change
1.81 if self.stack_depth > self.max_stack_depth:
1.82 self.max_stack_depth = self.stack_depth
1.83
1.84 def update_locals(self, index):
1.85 +
1.86 + """
1.87 + Given the stated 'index' of a local variable, update the maximum local
1.88 + variable index where appropriate.
1.89 + """
1.90 +
1.91 if index > self.max_locals:
1.92 self.max_locals = index
1.93
1.94 # Special methods.
1.95
1.96 def _write_value(self, value):
1.97 +
1.98 + """
1.99 + Write the given 'value' at the current output position.
1.100 + """
1.101 +
1.102 if isinstance(value, LazyValue):
1.103 # NOTE: Assume a 16-bit value.
1.104 self.output.append(value.values[0])
1.105 @@ -127,6 +174,11 @@
1.106 raise ValueError, value
1.107
1.108 def _rewrite_value(self, position, value):
1.109 +
1.110 + """
1.111 + At the given output 'position', rewrite the given 'value'.
1.112 + """
1.113 +
1.114 # NOTE: Assume a 16-bit value.
1.115 if value <= 0xffff:
1.116 self.output[position] = (value & 0xff)
1.117 @@ -587,10 +639,22 @@
1.118 "A generic Java bytecode reader."
1.119
1.120 def __init__(self, class_file):
1.121 +
1.122 + """
1.123 + Initialise the reader with a 'class_file' containing essential
1.124 + information for any bytecode inspection activity.
1.125 + """
1.126 +
1.127 self.class_file = class_file
1.128 self.position_mapping = LazyDict()
1.129
1.130 def process(self, method, program):
1.131 +
1.132 + """
1.133 + Process the given 'method' (obtained from the class file), using the
1.134 + given 'program' to write translated Python bytecode instructions.
1.135 + """
1.136 +
1.137 self.java_position = 0
1.138 self.in_finally = 0
1.139 self.method = method
1.140 @@ -602,7 +666,15 @@
1.141 if isinstance(attribute, classfile.CodeAttributeInfo):
1.142 code, exception_table = attribute.code, attribute.exception_table
1.143 break
1.144 +
1.145 + # Where no code was found, write a very simple placeholder routine.
1.146 + # This is useful for interfaces and abstract classes.
1.147 + # NOTE: Assess the correctness of doing this. An exception should really
1.148 + # NOTE: be raised instead.
1.149 +
1.150 if code is None:
1.151 + program.load_const(None)
1.152 + program.return_value()
1.153 return
1.154
1.155 # Produce a structure which permits fast access to exception details.
1.156 @@ -690,6 +762,14 @@
1.157 self.java_position = next_java_position
1.158
1.159 def process_bytecode(self, mnemonic, number_of_arguments, code, program):
1.160 +
1.161 + """
1.162 + Process a bytecode instruction with the given 'mnemonic' and
1.163 + 'number_of_arguments'. The 'code' parameter contains the full method
1.164 + code so that argument data can be inspected. The 'program' parameter is
1.165 + used to produce a Python translation of the instruction.
1.166 + """
1.167 +
1.168 if number_of_arguments is not None:
1.169 arguments = []
1.170 for j in range(0, number_of_arguments):
1.171 @@ -1888,7 +1968,8 @@
1.172 # NOTE: The code below should use dictionary-based dispatch for better performance.
1.173
1.174 for method, fn in methods:
1.175 - method_is_static = real_method_name != "<init>" and method_is_static or classfile.has_flags(method.access_flags, [classfile.STATIC])
1.176 + method_is_static = real_method_name != "<init>" and method_is_static or \
1.177 + classfile.has_flags(method.access_flags, [classfile.STATIC])
1.178
1.179 if method_is_static:
1.180 program.load_fast(0) # Stack: arguments
1.181 @@ -2178,6 +2259,8 @@
1.182 l.append("_l%s" % i)
1.183 return l[:nlocals]
1.184
1.185 +# Test functions, useful for tracing generated bytecode operations.
1.186 +
1.187 def _map(*args):
1.188 print args
1.189 return apply(__builtins__.map, args)