X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fcompiler%2Fruntime%2FRuntimeModule.java;fp=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fcompiler%2Fruntime%2FRuntimeModule.java;h=c5904b3c44fdb8f35427aa548e4c7165e877c932;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java new file mode 100644 index 000000000..c5904b3c4 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java @@ -0,0 +1,298 @@ +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 valueCache = new THashMap(); + TransientClassBuilder classBuilder; + RuntimeModuleMap parentModuleMap; + + class ModuleClassLoader extends ClassLoader implements MutableClassLoader { + // Module module; + String moduleName; + THashMap localClasses = new THashMap(); + //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 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 getConstantCache() { + return null; + } + + @Override + public ClassLoader getClassLoader() { + return this; + } + } + + + public Environment moduleEnvironment = new GlobalOnlyEnvironment() { + @Override + protected Collection getModules() { + ArrayList result = new ArrayList(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 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