]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / runtime / RuntimeModule.java
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 (file)
index 0000000..c5904b3
--- /dev/null
@@ -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<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