]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java
2e35427cdcda7014e7bacd12cb10c6c7de9312fa
[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.HashSet;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10
11 import org.objectweb.asm.ClassReader;
12 import org.objectweb.asm.ClassVisitor;
13 import org.objectweb.asm.commons.ClassRemapper;
14 import org.objectweb.asm.commons.Remapper;
15 import org.simantics.scl.compiler.constants.Constant;
16 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
17 import org.simantics.scl.compiler.elaboration.rules.TransformationRule;
18 import org.simantics.scl.compiler.environment.Environment;
19 import org.simantics.scl.compiler.environment.GlobalOnlyEnvironment;
20 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
21 import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy;
22 import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder;
23 import org.simantics.scl.compiler.internal.decompilation.DecompilerFactory;
24 import org.simantics.scl.compiler.internal.decompilation.IDecompiler;
25 import org.simantics.scl.compiler.module.ConcreteModule;
26 import org.simantics.scl.compiler.module.Module;
27 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
28 import org.simantics.scl.compiler.top.ValueNotFound;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import gnu.trove.map.hash.THashMap;
33
34 public class RuntimeModule {
35     private static final Logger LOGGER = LoggerFactory.getLogger(RuntimeModule.class);
36     
37     public static final boolean VALIDATE_CLASS_NAMES = true;
38     public static final boolean TRACE_CLASS_CREATION = true;
39     
40     Module module;
41     ModuleClassLoader classLoader;
42     THashMap<String, Object> valueCache = new THashMap<String, Object>(); 
43     TransientClassBuilder classBuilder;
44     RuntimeModuleMap parentModuleMap;
45     
46     class ModuleClassLoader extends ClassLoader implements MutableClassLoader {
47         //  Module module;
48         String moduleName;
49         THashMap<String, byte[]> localClasses = new THashMap<String, byte[]>(); 
50         //ModuleClassLoaderMap parentModules;
51         int transientPackageId = 0;
52         
53         public ModuleClassLoader(ClassLoader parent) {
54             super(parent);
55             this.moduleName = module.getName();
56         }
57         
58         public synchronized void addClass(String name, byte[] class_) {
59             if(TRACE_CLASS_CREATION)
60                 System.out.println("addClass " + name + " (" + class_.length + " bytes)");
61             if(VALIDATE_CLASS_NAMES)
62                 validateClassName(name);
63             localClasses.put(name, class_);
64         }
65         
66         public synchronized void addClasses(Map<String, byte[]> classes) {
67             if(TRACE_CLASS_CREATION)
68                 for(String name : classes.keySet())
69                     System.out.println("addClass " + name + " (" + classes.get(name).length + " bytes)");
70             if(VALIDATE_CLASS_NAMES)
71                 for(String name : classes.keySet())
72                     validateClassName(name);
73             localClasses.putAll(classes);
74         }
75         
76         private void validateClassName(String name) {
77             //System.out.println(name);
78             /*if(!name.startsWith(SCL_PACKAGE_PREFIX) || !extractClassLoaderId(name).equals(moduleName))
79                 throw new IllegalArgumentException("Class name " + name + " does not start with '" +
80                         SCL_PACKAGE_PREFIX + moduleName + "$'.");
81                         */
82         }
83         
84         @Override
85         public byte[] getBytes(String name) {
86             // Non-SCL classes are not handled here
87             if(!name.startsWith(SCL_PACKAGE_PREFIX))
88                 return null;
89
90             // Determine the id of the class loader which is responsible of the class
91             String requestedModuleName = extractClassLoaderId(name);
92
93             // Is class defined locally in this class loader?
94             if(requestedModuleName.equals(this.moduleName)) {
95                 String internalName = name.replace('.', '/');
96                 byte[] bytes = module.getClass(internalName);
97                 if(bytes != null)
98                     return bytes;
99                 return localClasses.get(internalName);
100             }
101             
102             // Find suitable class loader that has this class locally
103             {
104                 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
105                 if(parentModule == null)
106                     return null;
107
108                 // Find the class from the ancestor class loader
109                 return parentModule.classLoader.getBytes(name);
110             }
111         }
112
113         synchronized Class<?> getLocalClass(String name) throws ClassNotFoundException {
114             // Is class already loaded
115             Class<?> clazz = findLoadedClass(name);
116             if(clazz != null)
117                 return clazz;
118
119             // If we have bytecode for it, let's define the class
120             String internalName = name.replace('.', '/');
121             byte[] bytes = module.getClass(internalName);
122             if(bytes == null) {
123                 bytes = localClasses.get(internalName);
124                 if(bytes == null)
125                     throw new ClassNotFoundException(name);
126             }
127             if(SCLCompilerConfiguration.SHOW_DECOMPILED_BYTECODE && SCLCompilerConfiguration.debugFilter(moduleName))
128                 showDecompiledBytecode(internalName);
129             return defineClass(name, bytes, 0, bytes.length);
130         }
131         
132         private Class<?> getClass(String name) throws ClassNotFoundException {
133             
134             System.out.println(moduleName + ":getClass " + name);
135             
136             // If the class is not generated from SCL, use parent class loader
137             if(!name.startsWith(SCL_PACKAGE_PREFIX)) {
138                 try {
139                     return getParent().loadClass(name);
140                 } catch(ClassNotFoundException e) {
141                     for(RuntimeModule module : parentModuleMap.values())
142                         try {
143                             return module.classLoader.getParent().loadClass(name);
144                         } catch(ClassNotFoundException e2) {
145                         }
146                 }
147                 throw new ClassNotFoundException(name);
148             }
149             
150             // Determine the id of the class loader which is responsible of the class
151             String requestedModuleName = extractClassLoaderId(name);
152             
153             // Is class defined locally in this class loader?
154             if(requestedModuleName.equals(this.moduleName))
155                 return getLocalClass(name);
156             
157             // Find suitable class loader that has this class locally
158             {
159                 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
160                 if(parentModule == null) {
161                     LOGGER.error("requestedModuleName = " + requestedModuleName);
162                     LOGGER.error("this.moduleName = " + this.moduleName);
163                     throw new ClassNotFoundException(name);
164                 }
165
166                 // Find the class from the ancestor class loader
167                 return parentModule.classLoader.getLocalClass(name);
168             }
169         }
170         
171 //        protected Class<?> loadClass(String name, boolean resolve)
172 //                throws ClassNotFoundException
173 //            {
174 //                synchronized (getClassLoadingLock(name)) {
175 //                    // First, check if the class has already been loaded
176 //                    Class<?> c = findLoadedClass(name);
177 //                    if (c == null) {
178 //                        c = getClass(name);
179 //                    }
180 //                    if (resolve) {
181 //                        resolveClass(c);
182 //                    }
183 //                    return c;
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         @Override
196         public Class<?> loadClass(String name) throws ClassNotFoundException {
197             return super.loadClass(name);
198         }
199         
200         public Module getModule(String moduleName) {
201             //System.out.println("ModuleClassLoader.getModule(" + moduleName + ")");
202             if(moduleName.equals(this.moduleName))
203                 return module;
204             else {
205                 RuntimeModule parentModule = parentModuleMap.get(moduleName);
206                 if(parentModule == null)
207                     throw new RuntimeException("Didn't find module " + moduleName + ".");
208                 
209                 return parentModule.module;
210             }
211         }
212         
213         public String getModuleName() {
214             return moduleName;
215         }
216         
217         public synchronized String getFreshPackageName() {
218             return moduleName + "$" + (++transientPackageId);
219         }
220         
221         @Override
222         public THashMap<Constant, Object> getConstantCache() {
223             return null;
224         }
225         
226         @Override
227         public ClassLoader getClassLoader() {
228             return this;
229         }
230         
231         private void showDecompiledBytecode(String className) {
232             IDecompiler decompiler = DecompilerFactory.getDecompiler();
233             if(decompiler == null)
234                 return;
235             decompiler.decompile(this, className, new OutputStreamWriter(System.out));
236         }
237     }
238
239     
240     public Environment moduleEnvironment = new GlobalOnlyEnvironment() {
241         @Override
242         protected Collection<Module> getModules() {
243             ArrayList<Module> result = new ArrayList<Module>(parentModuleMap.size() + 1);
244             result.add(module);
245             for(RuntimeModule rm : parentModuleMap.values())
246                 result.add(rm.module);
247             return result;
248         }
249         @Override
250         protected Module getModule(String name) {
251             return classLoader.getModule(name);
252         }
253         @Override
254         public void collectRules(Collection<TransformationRule> rules) {
255         }
256         @Override
257         public List<Constant> getFieldAccessors(String name) {
258             // TODO Not clear if this is needed.
259             return null;
260         }
261     };
262     
263     public RuntimeModule(Module module, RuntimeModuleMap parentModuleMap,
264             ClassLoader parentClassLoader) {
265         if(parentClassLoader == null)
266             throw new NullPointerException();
267         this.module = module;
268         this.parentModuleMap = parentModuleMap;
269         this.classLoader = new ModuleClassLoader(parentClassLoader);
270         this.classBuilder = new TransientClassBuilder(classLoader,
271                 new JavaTypeTranslator(moduleEnvironment));
272     }
273
274     public Object getValue(String name) throws ValueNotFound {
275         // First try cache
276         if(valueCache.containsKey(name))
277             return valueCache.get(name);
278         
279         // Try to resolve the name
280         SCLValue valueConstructor = module.getValue(name);
281         if(valueConstructor == null)
282             throw new ValueNotFound(module.getName() + "/" + name);
283         
284         // Realize the value and cache it
285         Object value = valueConstructor.realizeValue(getClassBuilder());
286         valueCache.put(name, value);
287         return value;
288     }
289
290     private TransientClassBuilder getClassBuilder() {
291         return classBuilder;
292     }
293
294     public Module getModule() {
295         return module;
296     }
297
298     public MutableClassLoader getMutableClassLoader() {
299         return classLoader;
300     }
301  
302     public static String extractClassLoaderId(String className) {
303         int p = className.indexOf('$', MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH);
304         return JavaNamingPolicy.classNameToModuleName(p < 0 
305                 ? className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH) 
306                 : className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH, p));
307     }
308     
309     public void dispose() {
310         module.dispose();
311         module = null;
312         valueCache.clear();
313         parentModuleMap = null;
314         classLoader = null;
315         classBuilder = null;
316     }
317     
318     public class ClassNameRecordingRemapper extends Remapper {
319
320         private final Set<? super String> classNames;
321
322         public ClassNameRecordingRemapper(Set<? super String> classNames) {
323             this.classNames = classNames;
324         }
325         
326         @Override
327         public String map(String typeName) {
328             classNames.add(typeName);
329             return super.map(typeName);
330         }
331         
332         @Override
333         public String mapDesc(String desc) {
334             //classNames.add(desc);
335             return super.mapDesc(desc);
336         }
337         @Override
338         public String mapFieldName(String owner, String name, String desc) {
339             return super.mapFieldName(owner, name, desc);
340         }
341         @Override
342         public String mapInvokeDynamicMethodName(String name, String desc) {
343             // TODO Auto-generated method stub
344             return super.mapInvokeDynamicMethodName(name, desc);
345         }
346         @Override
347         public String mapMethodDesc(String desc) {
348             //classNames.add(desc);
349             return super.mapMethodDesc(desc);
350         }
351         @Override
352         public String mapMethodName(String owner, String name, String desc) {
353             // TODO Auto-generated method stub
354             return super.mapMethodName(owner, name, desc);
355         }
356         @Override
357         public String mapSignature(String signature, boolean typeSignature) {
358             //classNames.add(signature);
359             return super.mapSignature(signature, typeSignature);
360         }
361         @Override
362         public String mapType(String type) {
363             classNames.add(type);
364             return super.mapType(type);
365         }
366         @Override
367         public String[] mapTypes(String[] types) {
368             for(String type : types)
369                 classNames.add(type);
370             // TODO Auto-generated method stub
371             return super.mapTypes(types);
372         }
373         @Override
374         public Object mapValue(Object value) {
375             //classNames.add(value.toString());
376             // TODO Auto-generated method stub
377             return super.mapValue(value);
378         }
379     }
380     
381     public Set<String> classReferences(String className) {
382         try {
383             HashSet<String> referencedClasses = new HashSet<>();
384             ClassNameRecordingRemapper m = new ClassNameRecordingRemapper(referencedClasses);
385             ClassReader cr = new ClassReader(module.getClass(className));
386             int ASM5 = 5 << 16 | 0 << 8 | 0;
387             //TraceClassVisitor tcv = new TraceClassVisitor(null, new PrintWriter(System.err));
388             ClassVisitor cv1 = new ClassVisitor(ASM5) {};
389             ClassVisitor cv = new ClassRemapper(cv1, m);
390             cr.accept(cv, ClassReader.SKIP_DEBUG);
391             System.err.println(className + " refs: " + referencedClasses);
392             return referencedClasses;
393         } catch (Exception e) {
394             e.printStackTrace();
395         }
396         return null;
397     }
398     
399     public void loadReferences() {
400         ConcreteModule cm = (ConcreteModule)module;
401         try {
402             for(String className : cm.getClasses().keySet()) {
403                 Set<String> refs = classReferences(className);
404                 for(String s : refs) {
405                     String internalName = s.replace('/', '.');
406                     try {
407                         classLoader.loadClass(internalName);
408                     } catch (Throwable e) {
409                         e.printStackTrace();
410                     }
411                 }
412             }
413         } catch (Throwable e) {
414             e.printStackTrace();
415         }
416     }
417
418 }