javaclass

Change of javaclass/classhook.py

166:fed5a5ceb0e6
javaclass/classhook.py
     1.1 --- a/javaclass/classhook.py	Sun Feb 13 01:33:26 2005 +0100
     1.2 +++ b/javaclass/classhook.py	Sun Feb 13 01:35:44 2005 +0100
     1.3 @@ -5,6 +5,7 @@
     1.4  from imp import PY_SOURCE, PKG_DIRECTORY, C_BUILTIN # import machinery magic
     1.5  import classfile, bytecode # Java class support
     1.6  import zipfile # for Java archive inspection
     1.7 +import sys
     1.8  
     1.9  # NOTE: Arbitrary constants pulled from thin air.
    1.10  
    1.11 @@ -236,14 +237,52 @@
    1.12          find_module method produces such a list.
    1.13          """
    1.14  
    1.15 +        loaded_module_names = []
    1.16 +        loaded_classes = {}
    1.17 +        main_module = self._load_module(name, stuff, loaded_module_names, loaded_classes)
    1.18 +
    1.19 +        # Initialise the loaded classes.
    1.20 +
    1.21 +        for module, classes in loaded_classes.items():
    1.22 +            self._init_classes(module, classes)
    1.23 +
    1.24 +        return main_module
    1.25 +
    1.26 +    def _filter_names(self, module_names, loaded_module_names):
    1.27 +        for module_name in loaded_module_names:
    1.28 +            try:
    1.29 +                i = module_names.index(module_name)
    1.30 +                del module_names[i]
    1.31 +            except ValueError:
    1.32 +                pass
    1.33 +
    1.34 +    def _load_module(self, name, stuff, loaded_module_names, loaded_classes):
    1.35 +        #print "_load_module", name, loaded_module_names
    1.36 +        loaded_module_names.append(name)
    1.37 +
    1.38 +        # Detect non-Java modules.
    1.39 +
    1.40 +        for stuff_item in stuff:
    1.41 +            archive, filename, info = stuff_item
    1.42 +            suffix, mode, datatype = info
    1.43 +            if datatype not in (JAVA_PACKAGE, JAVA_ARCHIVE):
    1.44 +                return ihooks.ModuleLoader.load_module(self, name, stuff_item)
    1.45 +
    1.46          # Set up the module.
    1.47          # A union of all locations is placed in the module's path.
    1.48  
    1.49 +        external_names = []
    1.50          module = self.hooks.add_module(name)
    1.51          module.__path__ = [item_filename for (item_archive, item_filename, item_info) in stuff]
    1.52  
    1.53 +        # Prepare a dictionary of globals.
    1.54 +
    1.55 +        global_names = module.__dict__
    1.56 +        global_names["__builtins__"] = __builtins__
    1.57 +
    1.58          # Just go into each package and find the class files.
    1.59  
    1.60 +        classes = {}
    1.61          for stuff_item in stuff:
    1.62  
    1.63              # Extract the details, delegating loading responsibility to the
    1.64 @@ -253,16 +292,8 @@
    1.65  
    1.66              archive, filename, info = stuff_item
    1.67              suffix, mode, datatype = info
    1.68 -            if datatype not in (JAVA_PACKAGE, JAVA_ARCHIVE):
    1.69 -                return ihooks.ModuleLoader.load_module(self, name, stuff_item)
    1.70 -
    1.71              #print "Loading", archive, filename, info
    1.72  
    1.73 -            # Prepare a dictionary of globals.
    1.74 -
    1.75 -            global_names = module.__dict__
    1.76 -            global_names["__builtins__"] = __builtins__
    1.77 -
    1.78              # Get the real filename.
    1.79  
    1.80              filename = self._get_path_in_archive(filename)
    1.81 @@ -270,115 +301,111 @@
    1.82  
    1.83              # Load the class files.
    1.84  
    1.85 -            class_files = {}
    1.86              for class_filename in self.hooks.matching(filename, os.extsep + "class", archive):
    1.87                  #print "Loading class", class_filename
    1.88                  s = self.hooks.read(class_filename, archive)
    1.89                  class_file = classfile.ClassFile(s)
    1.90 -                class_files[str(class_file.this_class.get_name())] = class_file
    1.91 -
    1.92 -            # Get an index of the class files.
    1.93 -
    1.94 -            class_file_index = class_files.keys()
    1.95 -
    1.96 -            # NOTE: Unnecessary sorting for test purposes.
    1.97 -
    1.98 -            class_file_index.sort()
    1.99 -
   1.100 -            # Now go through the classes arranging them in a safe loading order.
   1.101 -
   1.102 -            position = 0
   1.103 -            while position < len(class_file_index):
   1.104 -                class_name = class_file_index[position]
   1.105 -                super_class_name = str(class_files[class_name].super_class.get_name())
   1.106 -
   1.107 -                # Discover whether the superclass appears later.
   1.108 -
   1.109 -                try:
   1.110 -                    super_class_position = class_file_index.index(super_class_name)
   1.111 -                    if super_class_position > position:
   1.112 +                translator = bytecode.ClassTranslator(class_file)
   1.113 +                classes[str(class_file.this_class.get_name())] = translator
   1.114 +                external_names += translator.process(global_names)
   1.115  
   1.116 -                        # If the superclass appears later, swap this class and the
   1.117 -                        # superclass, then process the superclass.
   1.118 -
   1.119 -                        class_file_index[position] = super_class_name
   1.120 -                        class_file_index[super_class_position] = class_name
   1.121 -                        continue
   1.122 -
   1.123 -                except ValueError:
   1.124 -                    pass
   1.125 -
   1.126 -                position += 1
   1.127 +        # Record the classes found under the current module.
   1.128  
   1.129 -            # Process each class file, producing a genuine Python class.
   1.130 -            # Create the classes, but establish a proper initialisation order.
   1.131 -
   1.132 -            class_file_init_index = []
   1.133 -            class_file_init = {}
   1.134 +        loaded_classes[module] = classes
   1.135  
   1.136 -            for class_name in class_file_index:
   1.137 -                #print "* Class", class_name
   1.138 -                class_file = class_files[class_name]
   1.139 -                translator = bytecode.ClassTranslator(class_file)
   1.140 -                cls, external_names = translator.process(global_names)
   1.141 -                module.__dict__[cls.__name__] = cls
   1.142 -
   1.143 -                # Process external names.
   1.144 +        # Return modules used by external names.
   1.145  
   1.146 -                this_class_name_parts = class_file.this_class.get_python_name().split(".")
   1.147 -                this_class_module, this_class_name = this_class_name_parts[:-1], this_class_name_parts[-1]
   1.148 -
   1.149 -                for external_name in external_names:
   1.150 -                    #print "* Name", external_name
   1.151 -                    external_name_parts = external_name.split(".")
   1.152 -                    external_class_module, external_class_name = external_name_parts[:-1], external_name_parts[-1]
   1.153 -
   1.154 -                    # Names not local to this package need importing.
   1.155 -
   1.156 -                    if len(external_name_parts) > 1 and this_class_module != external_class_module:
   1.157 +        external_module_names = self._get_external_module_names(external_names)
   1.158  
   1.159 -                        external_module_name = ".".join(external_class_module)
   1.160 -                        #print "* Importing", external_module_name
   1.161 -                        obj = __import__(external_module_name, global_names, {}, [])
   1.162 -                        global_names[external_name_parts[0]] = obj
   1.163 -
   1.164 -                    # Names local to this package may affect initialisation order.
   1.165 -
   1.166 -                    elif external_class_name not in class_file_init_index:
   1.167 -                        try:
   1.168 -                            this_class_name_index = class_file_init_index.index(this_class_name)
   1.169 -
   1.170 -                            # Either insert this name before the current class's
   1.171 -                            # name.
   1.172 +        # Repeatedly load classes from referenced modules.
   1.173  
   1.174 -                            #print "* Inserting", external_class_name
   1.175 -                            class_file_init_index.insert(this_class_name_index, external_class_name)
   1.176 -
   1.177 -                        except ValueError:
   1.178 -
   1.179 -                            # Or add this name in anticipation of the current
   1.180 -                            # class's name appearing.
   1.181 -
   1.182 -                            #print "* Including", external_class_name
   1.183 -                            class_file_init_index.append(external_class_name)
   1.184 -
   1.185 -                # Add this class name to the initialisation index.
   1.186 +        self._filter_names(external_module_names, loaded_module_names)
   1.187 +        for module_name in external_module_names:
   1.188 +            if module_name not in loaded_module_names:
   1.189  
   1.190 -                if class_name not in class_file_init_index:
   1.191 -                    class_file_init_index.append(this_class_name)
   1.192 -                class_file_init[this_class_name] = (cls, class_file)
   1.193 -
   1.194 -            # Finally, call __clinit__ methods for all relevant classes.
   1.195 +                # Emulate the __import__ function, loading the requested module
   1.196 +                # but returning the top-level module.
   1.197  
   1.198 -            #print "** Initialisation order", class_file_init_index
   1.199 -            for class_name in class_file_init_index:
   1.200 -                cls, class_file = class_file_init[class_name]
   1.201 -                #print "**", cls, class_file
   1.202 -                if hasattr(cls, "__clinit__"):
   1.203 -                    eval(cls.__clinit__.func_code, global_names)
   1.204 +                self._import(module_name, global_names, loaded_module_names, loaded_classes)
   1.205  
   1.206          return module
   1.207  
   1.208 +    def _import(self, module_name, parent, loaded_module_names, loaded_classes):
   1.209 +
   1.210 +        # Where no Java-based submodules can be found, look for
   1.211 +        # Python modules instead.
   1.212 +
   1.213 +        new_stuff = self.find_module(module_name)
   1.214 +        #print "_", new_stuff
   1.215 +        if not new_stuff:
   1.216 +            new_module = __import__(module_name, parent)
   1.217 +            #print "P", new_module
   1.218 +            parent[module_name.split(".")[0]] = new_module
   1.219 +            return new_module
   1.220 +
   1.221 +        module_name_parts = module_name.split(".")
   1.222 +        path = []
   1.223 +        for module_name_part in module_name_parts:
   1.224 +            path.append(module_name_part)
   1.225 +            path_str = ".".join(path)
   1.226 +            if self.modules_dict().has_key(path_str):
   1.227 +
   1.228 +                # Add submodules to existing modules.
   1.229 +
   1.230 +                new_module = self.modules_dict()[path_str]
   1.231 +                parent = new_module.__dict__
   1.232 +                #print "-", path_str
   1.233 +
   1.234 +            else:
   1.235 +
   1.236 +                # Find submodules.
   1.237 +
   1.238 +                new_stuff = self.find_module(path_str)
   1.239 +                new_module = self._load_module(path_str, new_stuff, loaded_module_names, loaded_classes)
   1.240 +                #print "J", new_module
   1.241 +                #print "+", path_str, new_module
   1.242 +                parent[module_name_part] = new_module
   1.243 +                parent = new_module.__dict__
   1.244 +
   1.245 +        #print "->", new_module.__dict__.keys()
   1.246 +        return new_module
   1.247 +
   1.248 +    def _get_external_module_names(self, names):
   1.249 +        groups = self._get_names_grouped_by_module(names)
   1.250 +        if groups.has_key(""):
   1.251 +            del groups[""]
   1.252 +        return groups.keys()
   1.253 +
   1.254 +    def _get_names_grouped_by_module(self, names):
   1.255 +        groups = {}
   1.256 +        for name in names:
   1.257 +            module_name, class_name = self._get_module_and_class_names(name)
   1.258 +            if not groups.has_key(module_name):
   1.259 +                groups[module_name] = []
   1.260 +            groups[module_name].append(class_name)
   1.261 +        return groups
   1.262 +
   1.263 +    def _get_module_and_class_names(self, full_name):
   1.264 +        full_name_parts = full_name.split(".")
   1.265 +        class_name = full_name_parts[-1]
   1.266 +        module_name = ".".join(full_name_parts[:-1])
   1.267 +        return module_name, class_name
   1.268 +
   1.269 +    def _init_classes(self, module, classes):
   1.270 +        global_names = module.__dict__
   1.271 +
   1.272 +        # First, create the classes.
   1.273 +
   1.274 +        real_classes = []
   1.275 +        for name, translator in classes.items():
   1.276 +            real_classes.append(translator.get_class(global_names))
   1.277 +
   1.278 +        # Finally, call __clinit__ methods for all relevant classes.
   1.279 +
   1.280 +        for cls in real_classes:
   1.281 +            if hasattr(cls, "__clinit__"):
   1.282 +                eval(cls.__clinit__.func_code, global_names)
   1.283 +
   1.284  ihooks.ModuleImporter(loader=ClassLoader(hooks=ClassHooks())).install()
   1.285  
   1.286  # vim: tabstop=4 expandtab shiftwidth=4