]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java
Added info on backup location to documentation backup.
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / runtime / RuntimeModule.java
1 package org.simantics.scl.compiler.runtime;
2
3 import java.io.IOException;
4 import java.io.OutputStreamWriter;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.Map;
8
9 import org.simantics.scl.compiler.constants.Constant;
10 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
11 import org.simantics.scl.compiler.elaboration.rules.TransformationRule;
12 import org.simantics.scl.compiler.environment.Environment;
13 import org.simantics.scl.compiler.environment.GlobalOnlyEnvironment;
14 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
15 import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy;
16 import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder;
17 import org.simantics.scl.compiler.module.Module;
18 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
19 import org.simantics.scl.compiler.top.ValueNotFound;
20
21 import com.strobel.assembler.metadata.Buffer;
22 import com.strobel.assembler.metadata.ClasspathTypeLoader;
23 import com.strobel.assembler.metadata.CompositeTypeLoader;
24 import com.strobel.assembler.metadata.ITypeLoader;
25 import com.strobel.decompiler.Decompiler;
26 import com.strobel.decompiler.DecompilerSettings;
27 import com.strobel.decompiler.PlainTextOutput;
28
29 import gnu.trove.map.hash.THashMap;
30
31 public class RuntimeModule {
32     public static final boolean VALIDATE_CLASS_NAMES = true;
33     public static final boolean TRACE_CLASS_CREATION = false;
34     
35     Module module;
36     ModuleClassLoader classLoader;
37     THashMap<String, Object> valueCache = new THashMap<String, Object>(); 
38     TransientClassBuilder classBuilder;
39     RuntimeModuleMap parentModuleMap;
40     
41     class ModuleClassLoader extends ClassLoader implements MutableClassLoader {
42         //  Module module;
43         String moduleName;
44         THashMap<String, byte[]> localClasses = new THashMap<String, byte[]>(); 
45         //ModuleClassLoaderMap parentModules;
46         int transientPackageId = 0;
47         
48         public ModuleClassLoader(ClassLoader parent) {
49             super(parent);
50             this.moduleName = module.getName();
51         }
52         
53         public synchronized void addClass(String name, byte[] class_) {
54             if(TRACE_CLASS_CREATION)
55                 System.out.println("addClass " + name + " (" + class_.length + " bytes)");
56             if(VALIDATE_CLASS_NAMES)
57                 validateClassName(name);
58             localClasses.put(name, class_);
59         }
60         
61         public synchronized void addClasses(Map<String, byte[]> classes) {
62             if(TRACE_CLASS_CREATION)
63                 for(String name : classes.keySet())
64                     System.out.println("addClass " + name + " (" + classes.get(name).length + " bytes)");
65             if(VALIDATE_CLASS_NAMES)
66                 for(String name : classes.keySet())
67                     validateClassName(name);
68             localClasses.putAll(classes);
69         }
70         
71         private void validateClassName(String name) {
72             //System.out.println(name);
73             /*if(!name.startsWith(SCL_PACKAGE_PREFIX) || !extractClassLoaderId(name).equals(moduleName))
74                 throw new IllegalArgumentException("Class name " + name + " does not start with '" +
75                         SCL_PACKAGE_PREFIX + moduleName + "$'.");
76                         */
77         }
78         
79         public byte[] getBytes(String name) {
80             // Non-SCL classes are not handled here
81             if(!name.startsWith(SCL_PACKAGE_PREFIX))
82                 return null;
83
84             // Determine the id of the class loader which is responsible of the class
85             String requestedModuleName = extractClassLoaderId(name);
86
87             // Is class defined locally in this class loader?
88             if(requestedModuleName.equals(this.moduleName)) {
89                 String internalName = name.replace('.', '/');
90                 byte[] bytes = module.getClass(internalName);
91                 if(bytes != null)
92                     return bytes;
93                 return localClasses.get(internalName);
94             }
95             
96             // Find suitable class loader that has this class locally
97             {
98                 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
99                 if(parentModule == null)
100                     return null;
101
102                 // Find the class from the ancestor class loader
103                 return parentModule.classLoader.getBytes(name);
104             }
105         }
106         
107         synchronized Class<?> getLocalClass(String name) throws ClassNotFoundException {
108             // Is class already loaded
109             Class<?> clazz = findLoadedClass(name);
110             if(clazz != null)
111                 return clazz;
112
113             // If we have bytecode for it, let's define the class
114             String internalName = name.replace('.', '/');
115             byte[] bytes = module.getClass(internalName);
116             if(bytes == null) {
117                 bytes = localClasses.get(internalName);
118                 if(bytes == null)
119                     throw new ClassNotFoundException(name);
120             }
121             if(SCLCompilerConfiguration.SHOW_LOADED_CLASSES_DISASSEMBLED) {
122                 DecompilerSettings settings = DecompilerSettings.javaDefaults();
123                 ITypeLoader typeLoader = new ITypeLoader() {
124                     @Override
125                     public boolean tryLoadType(String internalName, Buffer buffer) {
126                         byte[] bytes = getBytes(internalName);
127                         if(bytes != null) {
128                             buffer.reset(bytes.length);
129                             buffer.putByteArray(bytes, 0, bytes.length);
130                             buffer.position(0);
131                             return true;
132                         }
133                         else
134                             return false;
135                     }
136                 };
137                 settings.setTypeLoader(new CompositeTypeLoader(typeLoader, new ClasspathTypeLoader()));
138                 OutputStreamWriter writer = new OutputStreamWriter(System.out);
139                 PlainTextOutput output = new PlainTextOutput(writer);
140                 Decompiler.decompile(name, output, settings);
141                 try {
142                     writer.flush();
143                 } catch (IOException e) {
144                 }
145             }
146             return defineClass(name, bytes, 0, bytes.length);
147         }
148         
149         private Class<?> getClass(String name) throws ClassNotFoundException {
150             //System.out.println("getClass " + name);
151             
152             // If the class is not generated from SCL, use parent class loader
153             if(!name.startsWith(SCL_PACKAGE_PREFIX)) {
154                 try {
155                     return getParent().loadClass(name);
156                 } catch(ClassNotFoundException e) {
157                     for(RuntimeModule module : parentModuleMap.values())
158                         try {
159                             return module.classLoader.getParent().loadClass(name);
160                         } catch(ClassNotFoundException e2) {
161                         }
162                 }
163                 throw new ClassNotFoundException(name);
164             }
165             
166             // Determine the id of the class loader which is responsible of the class
167             String requestedModuleName = extractClassLoaderId(name);
168             
169             // Is class defined locally in this class loader?
170             if(requestedModuleName.equals(this.moduleName))
171                 return getLocalClass(name);
172             
173             // Find suitable class loader that has this class locally
174             {
175                 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
176                 if(parentModule == null) {
177                     System.err.println("requestedModuleName = " + requestedModuleName);
178                     System.err.println("this.moduleName = " + this.moduleName);
179                     throw new ClassNotFoundException(name);
180                 }
181
182                 // Find the class from the ancestor class loader
183                 return parentModule.classLoader.getLocalClass(name);
184             }
185         }
186         
187         @Override
188         public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
189             Class<?> clazz = getClass(name);
190             if (resolve)
191                 resolveClass(clazz);
192             return clazz;
193         }
194         
195         public Module getModule(String moduleName) {
196             //System.out.println("ModuleClassLoader.getModule(" + moduleName + ")");
197             if(moduleName.equals(this.moduleName))
198                 return module;
199             else {
200                 RuntimeModule parentModule = parentModuleMap.get(moduleName);
201                 if(parentModule == null)
202                     throw new RuntimeException("Didn't find module " + moduleName + ".");
203                 
204                 return parentModule.module;
205             }
206         }
207         
208         public String getModuleName() {
209             return moduleName;
210         }
211         
212         public synchronized String getFreshPackageName() {
213             return moduleName + "$" + (++transientPackageId);
214         }
215         
216         @Override
217         public THashMap<Constant, Object> getConstantCache() {
218             return null;
219         }
220         
221         @Override
222         public ClassLoader getClassLoader() {
223             return this;
224         }
225     }
226
227     
228     public Environment moduleEnvironment = new GlobalOnlyEnvironment() {
229         @Override
230         protected Collection<Module> getModules() {
231             ArrayList<Module> result = new ArrayList<Module>(parentModuleMap.size() + 1);
232             result.add(module);
233             for(RuntimeModule rm : parentModuleMap.values())
234                 result.add(rm.module);
235             return result;
236         }
237         @Override
238         protected Module getModule(String name) {
239             return classLoader.getModule(name);
240         }
241         @Override
242         public void collectRules(Collection<TransformationRule> rules) {
243         }
244     };
245     
246     public RuntimeModule(Module module, RuntimeModuleMap parentModuleMap,
247             ClassLoader parentClassLoader) {
248         this.module = module;
249         this.parentModuleMap = parentModuleMap;
250         this.classLoader = new ModuleClassLoader(parentClassLoader);
251         this.classBuilder = new TransientClassBuilder(classLoader,
252                 new JavaTypeTranslator(moduleEnvironment));
253     }
254
255     public Object getValue(String name) throws ValueNotFound {
256         // First try cache
257         if(valueCache.containsKey(name))
258             return valueCache.get(name);
259         
260         // Try to resolve the name
261         SCLValue valueConstructor = module.getValue(name);
262         if(valueConstructor == null)
263             throw new ValueNotFound(module.getName() + "/" + name);
264         
265         // Realize the value and cache it
266         Object value = valueConstructor.realizeValue(getClassBuilder());
267         valueCache.put(name, value);
268         return value;
269     }
270
271     private TransientClassBuilder getClassBuilder() {
272         return classBuilder;
273     }
274
275     public Module getModule() {
276         return module;
277     }
278
279     public MutableClassLoader getMutableClassLoader() {
280         return classLoader;
281     }
282  
283     public static String extractClassLoaderId(String className) {
284         int p = className.indexOf('$', MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH);
285         return JavaNamingPolicy.classNameToModuleName(p < 0 
286                 ? className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH) 
287                 : className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH, p));
288     }
289     
290     public void dispose() {
291         module.dispose();
292         module = null;
293         valueCache.clear();
294         parentModuleMap = null;
295         classLoader = null;
296         classBuilder = null;
297     }
298 }