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