--- /dev/null
+package org.simantics.scl.compiler.runtime;
+
+import gnu.trove.map.hash.THashMap;
+
+import java.util.Map;
+
+import org.simantics.scl.compiler.constants.Constant;
+
+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<String, byte[]> localClasses = new THashMap<String, byte[]>();
+ THashMap<String, RuntimeModule> runtimeModuleMap;
+ int transientPackageId = 0;
+ THashMap<Constant,Object> valueCache = new THashMap<Constant,Object>();
+
+ public ExpressionClassLoader(ClassLoader parent, THashMap<String, RuntimeModule> 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<String, byte[]> 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);
+ }
+
+ 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<Constant, Object> getConstantCache() {
+ return valueCache;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return this;
+ }
+}