--- /dev/null
+package org.simantics.scl.compiler.runtime;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import org.simantics.scl.compiler.constants.Constant;
+import org.simantics.scl.compiler.elaboration.modules.SCLValue;
+import org.simantics.scl.compiler.elaboration.rules.TransformationRule;
+import org.simantics.scl.compiler.environment.Environment;
+import org.simantics.scl.compiler.environment.GlobalOnlyEnvironment;
+import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
+import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy;
+import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder;
+import org.simantics.scl.compiler.module.Module;
+import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
+import org.simantics.scl.compiler.top.ValueNotFound;
+
+import com.strobel.assembler.metadata.Buffer;
+import com.strobel.assembler.metadata.ClasspathTypeLoader;
+import com.strobel.assembler.metadata.CompositeTypeLoader;
+import com.strobel.assembler.metadata.ITypeLoader;
+import com.strobel.decompiler.Decompiler;
+import com.strobel.decompiler.DecompilerSettings;
+import com.strobel.decompiler.PlainTextOutput;
+
+import gnu.trove.map.hash.THashMap;
+
+public class RuntimeModule {
+ public static final boolean VALIDATE_CLASS_NAMES = true;
+ public static final boolean TRACE_CLASS_CREATION = false;
+
+ Module module;
+ ModuleClassLoader classLoader;
+ THashMap<String, Object> valueCache = new THashMap<String, Object>();
+ TransientClassBuilder classBuilder;
+ RuntimeModuleMap parentModuleMap;
+
+ class ModuleClassLoader extends ClassLoader implements MutableClassLoader {
+ // Module module;
+ String moduleName;
+ THashMap<String, byte[]> localClasses = new THashMap<String, byte[]>();
+ //ModuleClassLoaderMap parentModules;
+ int transientPackageId = 0;
+
+ public ModuleClassLoader(ClassLoader parent) {
+ super(parent);
+ this.moduleName = module.getName();
+ }
+
+ 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) {
+ //System.out.println(name);
+ /*if(!name.startsWith(SCL_PACKAGE_PREFIX) || !extractClassLoaderId(name).equals(moduleName))
+ throw new IllegalArgumentException("Class name " + name + " does not start with '" +
+ SCL_PACKAGE_PREFIX + moduleName + "$'.");
+ */
+ }
+
+ 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 = extractClassLoaderId(name);
+
+ // Is class defined locally in this class loader?
+ if(requestedModuleName.equals(this.moduleName)) {
+ String internalName = name.replace('.', '/');
+ byte[] bytes = module.getClass(internalName);
+ if(bytes != null)
+ return bytes;
+ return localClasses.get(internalName);
+ }
+
+ // Find suitable class loader that has this class locally
+ {
+ RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
+ if(parentModule == null)
+ return null;
+
+ // Find the class from the ancestor class loader
+ return parentModule.classLoader.getBytes(name);
+ }
+ }
+
+ 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
+ String internalName = name.replace('.', '/');
+ byte[] bytes = module.getClass(internalName);
+ if(bytes == null) {
+ bytes = localClasses.get(internalName);
+ if(bytes == null)
+ throw new ClassNotFoundException(name);
+ }
+ if(SCLCompilerConfiguration.SHOW_LOADED_CLASSES_DISASSEMBLED) {
+ DecompilerSettings settings = DecompilerSettings.javaDefaults();
+ ITypeLoader typeLoader = new ITypeLoader() {
+ @Override
+ public boolean tryLoadType(String internalName, Buffer buffer) {
+ byte[] bytes = getBytes(internalName);
+ if(bytes != null) {
+ buffer.reset(bytes.length);
+ buffer.putByteArray(bytes, 0, bytes.length);
+ buffer.position(0);
+ return true;
+ }
+ else
+ return false;
+ }
+ };
+ settings.setTypeLoader(new CompositeTypeLoader(typeLoader, new ClasspathTypeLoader()));
+ OutputStreamWriter writer = new OutputStreamWriter(System.out);
+ PlainTextOutput output = new PlainTextOutput(writer);
+ Decompiler.decompile(name, output, settings);
+ try {
+ writer.flush();
+ } catch (IOException e) {
+ }
+ }
+ 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 : parentModuleMap.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 = extractClassLoaderId(name);
+
+ // Is class defined locally in this class loader?
+ if(requestedModuleName.equals(this.moduleName))
+ return getLocalClass(name);
+
+ // Find suitable class loader that has this class locally
+ {
+ RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
+ if(parentModule == null) {
+ System.err.println("requestedModuleName = " + requestedModuleName);
+ System.err.println("this.moduleName = " + this.moduleName);
+ 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 Module getModule(String moduleName) {
+ //System.out.println("ModuleClassLoader.getModule(" + moduleName + ")");
+ if(moduleName.equals(this.moduleName))
+ return module;
+ else {
+ RuntimeModule parentModule = parentModuleMap.get(moduleName);
+ if(parentModule == null)
+ throw new RuntimeException("Didn't find module " + moduleName + ".");
+
+ return parentModule.module;
+ }
+ }
+
+ public String getModuleName() {
+ return moduleName;
+ }
+
+ public synchronized String getFreshPackageName() {
+ return moduleName + "$" + (++transientPackageId);
+ }
+
+ @Override
+ public THashMap<Constant, Object> getConstantCache() {
+ return null;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return this;
+ }
+ }
+
+
+ public Environment moduleEnvironment = new GlobalOnlyEnvironment() {
+ @Override
+ protected Collection<Module> getModules() {
+ ArrayList<Module> result = new ArrayList<Module>(parentModuleMap.size() + 1);
+ result.add(module);
+ for(RuntimeModule rm : parentModuleMap.values())
+ result.add(rm.module);
+ return result;
+ }
+ @Override
+ protected Module getModule(String name) {
+ return classLoader.getModule(name);
+ }
+ @Override
+ public void collectRules(Collection<TransformationRule> rules) {
+ }
+ };
+
+ public RuntimeModule(Module module, RuntimeModuleMap parentModuleMap,
+ ClassLoader parentClassLoader) {
+ this.module = module;
+ this.parentModuleMap = parentModuleMap;
+ this.classLoader = new ModuleClassLoader(parentClassLoader);
+ this.classBuilder = new TransientClassBuilder(classLoader,
+ new JavaTypeTranslator(moduleEnvironment));
+ }
+
+ public Object getValue(String name) throws ValueNotFound {
+ // First try cache
+ if(valueCache.containsKey(name))
+ return valueCache.get(name);
+
+ // Try to resolve the name
+ SCLValue valueConstructor = module.getValue(name);
+ if(valueConstructor == null)
+ throw new ValueNotFound(module.getName() + "/" + name);
+
+ // Realize the value and cache it
+ Object value = valueConstructor.realizeValue(getClassBuilder());
+ valueCache.put(name, value);
+ return value;
+ }
+
+ private TransientClassBuilder getClassBuilder() {
+ return classBuilder;
+ }
+
+ public Module getModule() {
+ return module;
+ }
+
+ public MutableClassLoader getMutableClassLoader() {
+ return classLoader;
+ }
+
+ public static String extractClassLoaderId(String className) {
+ int p = className.indexOf('$', MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH);
+ return JavaNamingPolicy.classNameToModuleName(p < 0
+ ? className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH)
+ : className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH, p));
+ }
+
+ public void dispose() {
+ module.dispose();
+ module = null;
+ valueCache.clear();
+ parentModuleMap = null;
+ classLoader = null;
+ classBuilder = null;
+ }
+}
\ No newline at end of file