package org.simantics.scl.compiler.runtime; import java.util.Map; import org.simantics.scl.compiler.constants.Constant; import gnu.trove.map.hash.THashMap; public class ExpressionClassLoader extends ClassLoader implements MutableClassLoader { public static final boolean VALIDATE_CLASS_NAMES = true; public static final boolean TRACE_CLASS_CREATION = false; String basePackageName; THashMap localClasses = new THashMap(); THashMap runtimeModuleMap; int transientPackageId = 0; THashMap valueCache = new THashMap(); public ExpressionClassLoader(ClassLoader parent, THashMap runtimeModuleMap, String basePackageName) { super(parent); this.basePackageName = basePackageName; this.runtimeModuleMap = runtimeModuleMap; } public synchronized void addClass(String name, byte[] class_) { if(TRACE_CLASS_CREATION) System.out.println("addClass " + name + " (" + class_.length + " bytes)"); if(VALIDATE_CLASS_NAMES) validateClassName(name); localClasses.put(name, class_); } public synchronized void addClasses(Map classes) { if(TRACE_CLASS_CREATION) for(String name : classes.keySet()) System.out.println("addClass " + name + " (" + classes.get(name).length + " bytes)"); if(VALIDATE_CLASS_NAMES) for(String name : classes.keySet()) validateClassName(name); localClasses.putAll(classes); } private void validateClassName(String name) { /*if(!name.startsWith(SCL_PACKAGE_PREFIX) || !extractClassLoaderId(name).equals(basePackageName)) throw new IllegalArgumentException("Class name " + name + " does not start with '" + SCL_PACKAGE_PREFIX + basePackageName + "$'.");*/ } private synchronized Class getLocalClass(String name) throws ClassNotFoundException { // Is class already loaded Class clazz = findLoadedClass(name); if(clazz != null) return clazz; // If we have bytecode for it, let's define the class byte[] bytes = localClasses.get(name.replace('.', '/')); if(bytes == null) throw new ClassNotFoundException(name); return defineClass(name, bytes, 0, bytes.length); } @Override public byte[] getBytes(String name) { // Non-SCL classes are not handled here if(!name.startsWith(SCL_PACKAGE_PREFIX)) return null; // Determine the id of the class loader which is responsible of the class String requestedModuleName = RuntimeModule.extractClassLoaderId(name); // Is class defined locally in this class loader? if(requestedModuleName.equals(basePackageName)) { String internalName = name.replace('.', '/'); byte[] bytes = localClasses.get(internalName); if(bytes != null) return bytes; return localClasses.get(internalName); } // Find suitable class loader that has this class locally { RuntimeModule parentModule = runtimeModuleMap.get(requestedModuleName); if(parentModule == null) return null; // Find the class from the ancestor class loader return parentModule.classLoader.getBytes(name); } } private Class getClass(String name) throws ClassNotFoundException { //System.out.println("getClass " + name); // If the class is not generated from SCL, use parent class loader if(!name.startsWith(SCL_PACKAGE_PREFIX)) { try { return getParent().loadClass(name); } catch(ClassNotFoundException e) { for(RuntimeModule module : runtimeModuleMap.values()) try { return module.classLoader.getParent().loadClass(name); } catch(ClassNotFoundException e2) { } } throw new ClassNotFoundException(name); } // Determine the id of the class loader which is responsible of the class String requestedModuleName = RuntimeModule.extractClassLoaderId(name); // Is class defined locally in this class loader? if(requestedModuleName.equals(basePackageName)) return getLocalClass(name); // Find suitable class loader that has this class locally else { RuntimeModule parentModule = runtimeModuleMap.get(requestedModuleName); if(parentModule == null) throw new ClassNotFoundException(name); // Find the class from the ancestor class loader return parentModule.classLoader.getLocalClass(name); } } @Override public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class clazz = getClass(name); if (resolve) resolveClass(clazz); return clazz; } public synchronized String getFreshPackageName() { return basePackageName + "$" + (++transientPackageId); } @Override public THashMap getConstantCache() { return valueCache; } @Override public ClassLoader getClassLoader() { return this; } }