Project Jigsaw: Module-load code outline


// Module-load code outline
// 28 January 2009
//
// Some interaction traces can be found at the end of this file
//
// TODO
//  - Work through local, private, permits cases
//  - Work through security and concurrency issues


package org.openjdk.jigsaw;


public class JigsawModuleSystem
    // aka ModuleClassLoaderPool, ModuleClassManager, ModuleClassLoaderFactory
{

    private ModuleLibrary lib;

    public JigsawModuleSystem(ModuleLibrary lib) {
        this.lib = lib;
    }

    public ModuleLibrary lib() {
        return lib;
    }

    // Our pool of module class loaders.  We use a weak set, so that
    // loaders can be garbage-collected when no longer in use.
    //
    private Set<JigsawClassLoader> loaders
        = new WeakSet<JigsawClassLoader>();

    // Map from module ids to module class loaders.  References to loaders
    // are weak, as above.
    //
    private Map<ModuleId,JigsawClassLoader> loaderForModule
        = new WeakValueHashMap<ModuleId,JigsawClassLoader>();

    // The "kernel" module
    //
    private Module kernelModule;

    // The "kernel" loader -- we retain a separate reference to it here since
    // kernelModule.getClassLoader() is just a ModuleClassLoader, and we need
    // the Jigsaw view of it
    //
    private JigsawClassLoader kernelLoader;

    // Invoked by the VM to initialize the module system
    // Returns the kernel module
    //
    private Module init(ModuleId mid, byte[] bs) {
        kernelLoader = findLoader(ModuleId mid);
        kernelModule = kernelLoader.defineModule(ModuleId mid, bs);
        return kernelModule;
    }

    // Invoked by the JRE launcher
    //
    public Class<?> findClass(ModuleIdQuery query, String name)
        throws ClassNotFoundException
    {
        ModuleId mid = lib.findModule(query);
        return kernelLoader.findClass(mid, name);
    }

    // Find a loader for the given module, or else create one
    //
    public JigsawClassLoader findLoader(ModuleId mid) {
        ld = loaderForModule.get(mid);
        if (ld == null) {
            ld = new JigsawClassLoader(this);
            loaders.add(ld);
            loaderForModule.put(mid, ld);
        }
        return ld;
    }

}


public final class JigsawClassLoader
    extends ModuleClassLoader
{

    private JigsawModuleSystem ms;

    private Map<String,Module> moduleForName
        = new HashMap<String,Module>();

    private Set<ModuleId> modules = new HashSet<ModuleId>();

    public JigsawClassLoader(JigsawModuleSystem ms) {
        this.ms = ms;
    }

    // ## Need a native version of this that doesn't throw CNFE
    private Class<?> findBootstrapClassOrNull(String name) {
        try {
            return findBootstrapClass(name);
        } catch (ClassNotFoundException x) {
            return null;
        }
    }

    /**
     * Primary entry point from VM
     */
    public Class<?> loadClass(String name, Module requestor)
        throws ClassNotFoundException
    {

        Class<?> c = null;

        // Check the loaded-class cache first.  The VM guarantees not to invoke
        // this method more than once for any given class name, but we still
        // need to check the cache manually in case this method is invoked by
        // user code.
        //
        if ((c = findLoadedClass(name)) != null)
            return c;

        // Check for a bootstrap class.  This is a temporary measure, until
        // such time as the bootstrap classes have themselves been modularized.
        //
        if ((c = findBootstrapClassOrNull(name)) != null)
            return c;

        // Find a supplier of the requested class
        //
        ModuleId supplier = ms.lib().findModuleForClass(className, requestor);
        if (supplier == null)
            throw new ClassNotFoundException(name);

        // Find a loader for the supplier, and use that to load the class
        //
        ClassLoader ld = null;
        if (modules.contains(supplier)) {
            // If supplier is already in this loader, then use this loader
            ld = this;
        } else {
            // Otherwise, ask our module-loader pool to find a loader for
            // the class, and then ask that loader to load the class
            ld = ms.findLoader(supplier);
            if (ld == null)
                throw new ClassNotFoundException(name);
        }
        return ld.findClass(supplier, name);

    }

    // Invoked by findClass, below, and by JigsawModuleSystem.init()
    //
    /* package */ Module defineModule(ModuleId mid, byte[] bs) {
        m = defineModule(mid, bs);
        moduleForName.put(mid.name(), m);
        modules.add(mid);
    }

    /* package */ Class<?> findClass(ModuleId mid, String name)
        throw ClassNotFoundException;
    {

        Class<?> c = findLoadedClass(className);
        if (c != null)
            return c;

        Module m = moduleForName.get(mid.name());
        if (m != null) {
            if (!m.id().equals(mid))
                throw new DuplicateModuleInLoaderException(); // ##
        } else {
            byte[] bs = ms.lib().readModuleInfo(mid);
            defineModule(mid, bs);
        }

        // ## Could check m's permits clause here (though we'd need the
        // ## requestor's name to be passed in), but the VM will do that
        // ## during linking (right?)

        byte[] bs = ms.lib().readClass(mid, className);
        return defineClass(m, className, bs);

    }

}


/* -- VM/Jigsaw interaction traces --

   Case 1: Load a class from a module not yet loaded

     Class C1 in module M1 refers to class C2
       Let L1 = C1.class.getClassLoader()
       VM invokes L1.loadClass("C2", M1)
         Let P = L1's JigsawModuleSystem
         Let ML = P's module library
         L1 invokes ML.findModuleForClass("C2", M1)
           ML searches M1's dependences for a module that contains C2
           ML returns MI2 = Module id of supplying module
         L1 invokes P.findLoader(MI2)
           P creates a new loader L2
           P adds MI2 -> L2 entry to its module-id-to-loader map
         L1 invokes L2.findClass(MI2, "C2")
           L2 reads M2's module-info bytes via ML
           L2 invokes defineModule(MI2, module-info bytes)
             VM creates M2 = new java.lang.reflect.Module(bytes, L2)
           L2 adds M2 to the set of L2's modules
           L2 adds MI2.name -> M2 entry to L2's name-to-module map
           L2 reads C2's class bytes via ML
           L2 invokes defineClass(MI2, "C2", class bytes), obtaining C2
         L1 returns C2
       VM links C2
     C1 proceeds to use C2

   Case 2: JRE launch sequence (very rough idea)

     Launcher initializes VM
       VM finds JRE kernel-module class files in library
       VM uses bootstrap loader to invoke JigsawModuleSystem.init
         This will create the kernel module, KM, and its loader, KL
       VM modifies all loaded classes so that:
         C.getClassLoader() == KL
         C.getModule() == KM
       (From this point forward the bootstrap loader is not used)
     Launcher invokes JigsawModuleSystem.findClass to find the main class, M
     Launcher invokes M.main()

 */