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()
*/