]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java
Make it possible to debug SCL compiler in production builds
[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.OutputStreamWriter;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.List;
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.LoggingOutputStream;
17 import org.simantics.scl.compiler.internal.codegen.utils.LoggingOutputStream.LogLevel;
18 import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder;
19 import org.simantics.scl.compiler.internal.decompilation.DecompilerFactory;
20 import org.simantics.scl.compiler.internal.decompilation.IDecompiler;
21 import org.simantics.scl.compiler.module.Module;
22 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
23 import org.simantics.scl.compiler.top.ValueNotFound;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import gnu.trove.map.hash.THashMap;
28
29 public class RuntimeModule {
30     private static final Logger LOGGER = LoggerFactory.getLogger(RuntimeModule.class);
31     
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                 LOGGER.info("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                     LOGGER.info("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             //LOGGER.info(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         @Override
80         public byte[] getBytes(String name) {
81             // Non-SCL classes are not handled here
82             if(!name.startsWith(SCL_PACKAGE_PREFIX))
83                 return null;
84
85             // Determine the id of the class loader which is responsible of the class
86             String requestedModuleName = extractClassLoaderId(name);
87
88             // Is class defined locally in this class loader?
89             if(requestedModuleName.equals(this.moduleName)) {
90                 String internalName = name.replace('.', '/');
91                 byte[] bytes = module.getClass(internalName);
92                 if(bytes != null)
93                     return bytes;
94                 return localClasses.get(internalName);
95             }
96             
97             // Find suitable class loader that has this class locally
98             {
99                 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
100                 if(parentModule == null)
101                     return null;
102
103                 // Find the class from the ancestor class loader
104                 return parentModule.classLoader.getBytes(name);
105             }
106         }
107
108         synchronized Class<?> getLocalClass(String name) throws ClassNotFoundException {
109             // Is class already loaded
110             Class<?> clazz = findLoadedClass(name);
111             if(clazz != null)
112                 return clazz;
113
114             // If we have bytecode for it, let's define the class
115             String internalName = name.replace('.', '/');
116             byte[] bytes = module.getClass(internalName);
117             if(bytes == null) {
118                 bytes = localClasses.get(internalName);
119                 if(bytes == null)
120                     throw new ClassNotFoundException(name);
121             }
122             if(SCLCompilerConfiguration.SHOW_DECOMPILED_BYTECODE && SCLCompilerConfiguration.debugFilter(moduleName))
123                 showDecompiledBytecode(internalName);
124             return defineClass(name, bytes, 0, bytes.length);
125         }
126         
127         private Class<?> getClass(String name) throws ClassNotFoundException {
128             //LOGGER.info("getClass " + name);
129             
130             // If the class is not generated from SCL, use parent class loader
131             if(!name.startsWith(SCL_PACKAGE_PREFIX)) {
132                 try {
133                     return getParent().loadClass(name);
134                 } catch(ClassNotFoundException e) {
135                     for(RuntimeModule module : parentModuleMap.values())
136                         try {
137                             return module.classLoader.getParent().loadClass(name);
138                         } catch(ClassNotFoundException e2) {
139                         }
140                 }
141                 throw new ClassNotFoundException(name);
142             }
143             
144             // Determine the id of the class loader which is responsible of the class
145             String requestedModuleName = extractClassLoaderId(name);
146             
147             // Is class defined locally in this class loader?
148             if(requestedModuleName.equals(this.moduleName))
149                 return getLocalClass(name);
150             
151             // Find suitable class loader that has this class locally
152             {
153                 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
154                 if(parentModule == null) {
155                     LOGGER.error("requestedModuleName = " + requestedModuleName);
156                     LOGGER.error("this.moduleName = " + this.moduleName);
157                     throw new ClassNotFoundException(name);
158                 }
159
160                 // Find the class from the ancestor class loader
161                 return parentModule.classLoader.getLocalClass(name);
162             }
163         }
164         
165         @Override
166         public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
167             Class<?> clazz = getClass(name);
168             if (resolve)
169                 resolveClass(clazz);
170             return clazz;
171         }
172         
173         public Module getModule(String moduleName) {
174             //LOGGER.info("ModuleClassLoader.getModule(" + moduleName + ")");
175             if(moduleName.equals(this.moduleName))
176                 return module;
177             else {
178                 RuntimeModule parentModule = parentModuleMap.get(moduleName);
179                 if(parentModule == null)
180                     throw new RuntimeException("Didn't find module " + moduleName + ".");
181                 
182                 return parentModule.module;
183             }
184         }
185         
186         public String getModuleName() {
187             return moduleName;
188         }
189         
190         public synchronized String getFreshPackageName() {
191             return moduleName + "$" + (++transientPackageId);
192         }
193         
194         @Override
195         public THashMap<Constant, Object> getConstantCache() {
196             return null;
197         }
198         
199         @Override
200         public ClassLoader getClassLoader() {
201             return this;
202         }
203         
204         private void showDecompiledBytecode(String className) {
205             IDecompiler decompiler = DecompilerFactory.getDecompiler();
206             if(decompiler == null)
207                 return;
208             decompiler.decompile(this, className, new OutputStreamWriter(new LoggingOutputStream(LOGGER, LogLevel.INFO)));
209         }
210     }
211
212     
213     public Environment moduleEnvironment = new GlobalOnlyEnvironment() {
214         @Override
215         protected Collection<Module> getModules() {
216             ArrayList<Module> result = new ArrayList<Module>(parentModuleMap.size() + 1);
217             result.add(module);
218             for(RuntimeModule rm : parentModuleMap.values())
219                 result.add(rm.module);
220             return result;
221         }
222         @Override
223         protected Module getModule(String name) {
224             return classLoader.getModule(name);
225         }
226         @Override
227         public void collectRules(Collection<TransformationRule> rules) {
228         }
229         @Override
230         public List<Constant> getFieldAccessors(String name) {
231             // TODO Not clear if this is needed.
232             return null;
233         }
234     };
235     
236     public RuntimeModule(Module module, RuntimeModuleMap parentModuleMap,
237             ClassLoader parentClassLoader) {
238         if(parentClassLoader == null)
239             throw new NullPointerException();
240         this.module = module;
241         this.parentModuleMap = parentModuleMap;
242         this.classLoader = new ModuleClassLoader(parentClassLoader);
243         this.classBuilder = new TransientClassBuilder(classLoader,
244                 new JavaTypeTranslator(moduleEnvironment));
245     }
246
247     public Object getValue(String name) throws ValueNotFound {
248         // First try cache
249         if(valueCache.containsKey(name))
250             return valueCache.get(name);
251         
252         // Try to resolve the name
253         SCLValue valueConstructor = module.getValue(name);
254         if(valueConstructor == null)
255             throw new ValueNotFound(module.getName() + "/" + name);
256         
257         // Realize the value and cache it
258         Object value = valueConstructor.realizeValue(getClassBuilder());
259         valueCache.put(name, value);
260         return value;
261     }
262
263     private TransientClassBuilder getClassBuilder() {
264         return classBuilder;
265     }
266
267     public Module getModule() {
268         return module;
269     }
270
271     public MutableClassLoader getMutableClassLoader() {
272         return classLoader;
273     }
274  
275     public static String extractClassLoaderId(String className) {
276         int p = className.indexOf('$', MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH);
277         return JavaNamingPolicy.classNameToModuleName(p < 0 
278                 ? className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH) 
279                 : className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH, p));
280     }
281     
282     public void dispose() {
283         module.dispose();
284         module = null;
285         valueCache.clear();
286         parentModuleMap = null;
287         classLoader = null;
288         classBuilder = null;
289     }
290 }