# HG changeset patch # User Paul Boddie # Date 1102637099 -3600 # Node ID 85e3410a7de9c9f5f3d7f171c77770cd594198e0 # Parent eb71e53b14ce7256592d9115d557024eed494087 Moved class name loading (in translated instructions) into a top-level function in the bytecode module. Added initialisation order processing to classhook. diff -r eb71e53b14ce -r 85e3410a7de9 bytecode.py --- a/bytecode.py Thu Dec 09 20:51:18 2004 +0100 +++ b/bytecode.py Fri Dec 10 01:04:59 2004 +0100 @@ -284,14 +284,14 @@ # NOTE: Using 3 as the assumed length of the SETUP_* instruction. self._rewrite_value(current_exception_start + 1, target - current_exception_start - 3) - def start_handler(self, exc_name): + def start_handler(self, exc_name, class_file): # Where handlers are begun, produce bytecode to test the type of # the exception. # NOTE: Since RAISE_VARARGS and END_FINALLY are not really documented, # NOTE: we store the top of the stack and use it later to trigger the # NOTE: magic processes when re-raising. - self.use_external_name(exc_name) + self.use_external_name(str(exc_name)) self.rot_two() # Stack: raised-exception, exception self.dup_top() # Stack: raised-exception, exception, exception @@ -304,7 +304,8 @@ self.load_attr("args") # Stack: raised-exception, exception, args self.load_const(0) # Stack: raised-exception, exception, args, 0 self.binary_subscr() # Stack: raised-exception, exception, exception-object - self.load_global(str(exc_name)) # Stack: raised-exception, exception, exception-object, handled-exception + load_class_name(class_file, str(exc_name), self) + # Stack: raised-exception, exception, exception-object, handled-exception self.load_global("isinstance") # Stack: raised-exception, exception, exception-object, handled-exception, isinstance self.rot_three() # Stack: raised-exception, exception, isinstance, exception-object, handled-exception self.call_function(2) # Stack: raised-exception, exception, result @@ -632,6 +633,21 @@ def signed4(value): return signed(value, 0x80000000) +def load_class_name(class_file, full_class_name, program): + this_class_name = str(class_file.this_class.get_python_name()) + this_class_parts = this_class_name.split(".") + class_parts = full_class_name.split(".") + + # Only use the full path if different from this class's path. + + if class_parts[:-1] != this_class_parts[:-1]: + program.use_external_name(full_class_name) + program.load_global(class_parts[0]) + for class_part in class_parts[1:]: + program.load_attr(class_part) # Stack: classref + else: + program.load_global(class_parts[-1]) + # Bytecode conversion. class BytecodeReader: @@ -736,7 +752,7 @@ if exception.catch_type == 0: self.in_finally = 1 else: - program.start_handler(self.class_file.constants[exception.catch_type - 1].get_python_name()) + program.start_handler(self.class_file.constants[exception.catch_type - 1].get_python_name(), self.class_file) # Process the bytecode at the current position. @@ -1034,7 +1050,7 @@ print "(setup_finally %s)" % target def end_exception(self): print "(end_exception)" - def start_handler(self, exc_name): + def start_handler(self, exc_name, class_file): print "(start_handler %s)" % exc_name def pop_block(self): print "(pop_block)" @@ -1043,17 +1059,6 @@ "A Java bytecode translator which uses a Python bytecode writer." - def _load_class_name(self, full_class_name, program): - this_class_name = str(self.class_file.this_class.get_python_name()) - class_parts = full_class_name.split(".") - if full_class_name != this_class_name: - program.use_external_name(full_class_name) - program.load_global(class_parts[0]) - for class_part in class_parts[1:]: - program.load_attr(class_part) # Stack: classref - else: - program.load_global(class_parts[-1]) - def aaload(self, arguments, program): # NOTE: No type checking performed. program.binary_subscr() @@ -1158,17 +1163,30 @@ index = (arguments[0] << 8) + arguments[1] target_name = self.class_file.constants[index - 1].get_python_name() program.use_external_name(target_name) - - # NOTE: Using the string version of the name which may contain incompatible characters. - target_components = str(target_name).split("/") - + program.dup_top() # Stack: objectref, objectref + program.load_const(None) # Stack: objectref, objectref, None + program.compare_op("is") # Stack: objectref, result + program.jump_to_label(1, "next") + program.pop_top() # Stack: objectref program.dup_top() # Stack: objectref, objectref program.load_global("isinstance") # Stack: objectref, objectref, isinstance program.rot_two() # Stack: objectref, isinstance, objectref - program.load_global(target_components[0]) - for target_component in target_components[1:]: - program.load_attr(target_component) - program.call_function(2) # Stack: objectref + load_class_name(self.class_file, target_name, program) + program.call_function(2) # Stack: objectref, result + program.jump_to_label(1, "next") + program.pop_top() # Stack: objectref + program.pop_top() # Stack: + program.use_external_name("java.lang.ClassCastException") + load_class_name(self.class_file, "java.lang.ClassCastException", program) + program.call_function(0) # Stack: exception + # Wrap the exception in a Python exception. + program.load_global("Exception") # Stack: exception, Exception + program.rot_two() # Stack: Exception, exception + program.call_function(1) # Stack: exception + program.raise_varargs(1) + # NOTE: This seems to put another object on the stack. + program.start_label("next") + program.pop_top() # Stack: objectref def d2f(self, arguments, program): pass @@ -1302,7 +1320,8 @@ # Get the class name instead of the fully qualified name. full_class_name = target.get_class().get_python_name() - self._load_class_name(full_class_name, program) + program.use_external_name(full_class_name) + load_class_name(self.class_file, full_class_name, program) # NOTE: Using the string version of the name which may contain incompatible characters. program.load_attr(str(target_name)) @@ -1468,15 +1487,9 @@ index = (arguments[0] << 8) + arguments[1] target_name = self.class_file.constants[index - 1].get_python_name() program.use_external_name(target_name) - - # NOTE: Using the string version of the name which may contain incompatible characters. - target_components = str(target_name).split("/") - program.load_global("isinstance") # Stack: objectref, isinstance program.rot_two() # Stack: isinstance, objectref - program.load_global(target_components[0]) - for target_component in target_components[1:]: - program.load_attr(target_component) + load_class_name(self.class_file, target_name, program) program.call_function(2) # Stack: result def _invoke(self, target_name, program): @@ -1522,7 +1535,8 @@ full_class_name = target.get_class().get_python_name() if full_class_name not in ("java.lang.Object", "java.lang.Exception"): - self._load_class_name(full_class_name, program) + program.use_external_name(full_class_name) + load_class_name(self.class_file, full_class_name, program) self._invoke(target_name, program) # Remove Python None return value. @@ -1551,7 +1565,8 @@ full_class_name = target.get_class().get_python_name() if full_class_name not in ("java.lang.Object", "java.lang.Exception"): - self._load_class_name(full_class_name, program) + program.use_external_name(full_class_name) + load_class_name(self.class_file, full_class_name, program) self._invoke(target_name, program) def invokevirtual (self, arguments, program): @@ -1666,7 +1681,7 @@ program.load_const(const.get_value()) program.call_function(1) else: - program.load_const(const) + program.load_const(const.get_value()) def ldc_w(self, arguments, program): const = self.class_file.constants[(arguments[0] << 8) + arguments[1] - 1] @@ -1678,7 +1693,7 @@ program.load_const(const.get_value()) program.call_function(1) else: - program.load_const(const) + program.load_const(const.get_value()) ldc2_w = ldc_w ldiv = idiv @@ -1800,7 +1815,7 @@ # NOTE: Using the string version of the name which may contain incompatible characters. program.load_global("object") program.load_attr("__new__") - self._load_class_name(target_name, program) + load_class_name(self.class_file, target_name, program) program.call_function(1) def newarray(self, arguments, program): @@ -1831,7 +1846,8 @@ # Get the class name instead of the fully qualified name. full_class_name = target.get_class().get_python_name() - self._load_class_name(full_class_name, program) + program.use_external_name(full_class_name) + load_class_name(self.class_file, full_class_name, program) # NOTE: Using the string version of the name which may contain incompatible characters. program.store_attr(str(target_name)) diff -r eb71e53b14ce -r 85e3410a7de9 classhook.py --- a/classhook.py Thu Dec 09 20:51:18 2004 +0100 +++ b/classhook.py Fri Dec 10 01:04:59 2004 +0100 @@ -260,11 +260,6 @@ global_names = module.__dict__ global_names["__builtins__"] = __builtins__ - # Process each class file, producing a genuine Python class. - - class_files = [] - classes = [] - # Get the real filename. filename = self._get_path_in_archive(filename) @@ -312,27 +307,69 @@ position += 1 - class_files = [class_files[class_name] for class_name in class_file_index] + # Process each class file, producing a genuine Python class. + # Create the classes, but establish a proper initialisation order. - for class_file in class_files: + class_file_init_index = [] + class_file_init = {} + + for class_name in class_file_index: + print "* Class", class_name + class_file = class_files[class_name] translator = bytecode.ClassTranslator(class_file) cls, external_names = translator.process(global_names) module.__dict__[cls.__name__] = cls - classes.append((cls, class_file)) + + # Process external names. - # Import the local names. + this_class_name_parts = class_file.this_class.get_python_name().split(".") + this_class_module, this_class_name = this_class_name_parts[:-1], this_class_name_parts[-1] for external_name in external_names: + print "* Name", external_name external_name_parts = external_name.split(".") - if len(external_name_parts) > 1: - external_module_name = ".".join(external_name_parts[:-1]) + external_class_module, external_class_name = external_name_parts[:-1], external_name_parts[-1] + + # Names not local to this package need importing. + + if len(external_name_parts) > 1 and this_class_module != external_class_module: + + external_module_name = ".".join(external_class_module) print "* Importing", external_module_name obj = __import__(external_module_name, global_names, {}, []) global_names[external_name_parts[0]] = obj + # Names local to this package may affect initialisation order. + + elif external_class_name not in class_file_init_index: + try: + this_class_name_index = class_file_init_index.index(this_class_name) + + # Either insert this name before the current class's + # name. + + print "* Inserting", external_class_name + class_file_init_index.insert(this_class_name_index, external_class_name) + + except ValueError: + + # Or add this name in anticipation of the current + # class's name appearing. + + print "* Including", external_class_name + class_file_init_index.append(external_class_name) + + # Add this class name to the initialisation index. + + if class_name not in class_file_init_index: + class_file_init_index.append(this_class_name) + class_file_init[this_class_name] = (cls, class_file) + # Finally, call __clinit__ methods for all relevant classes. - for cls, class_file in classes: + print "** Initialisation order", class_file_init_index + for class_name in class_file_init_index: + cls, class_file = class_file_init[class_name] print "**", cls, class_file if hasattr(cls, "__clinit__"): eval(cls.__clinit__.func_code, global_names)