]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/RuntimeModule.java
Simupedia tuning step 2
[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 = false;
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             Class<?> clazz = findLoadedClass(name);
135             if(clazz != null)
136                 return clazz;
137             
138             // If the class is not generated from SCL, use parent class loader
139             if(!name.startsWith(SCL_PACKAGE_PREFIX)) {
140                 try {
141                     return getParent().loadClass(name);
142                 } catch(ClassNotFoundException e) {
143                     for(RuntimeModule module : parentModuleMap.values())
144                         try {
145                             return module.classLoader.getParent().loadClass(name);
146                         } catch(ClassNotFoundException e2) {
147                         }
148                 }
149                 throw new ClassNotFoundException(name);
150             }
151             
152             // Determine the id of the class loader which is responsible of the class
153             String requestedModuleName = extractClassLoaderId(name);
154             
155             // Is class defined locally in this class loader?
156             if(requestedModuleName.equals(this.moduleName))
157                 return getLocalClass(name);
158             
159             // Find suitable class loader that has this class locally
160             {
161                 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
162                 if(parentModule == null) {
163                     LOGGER.error("requestedModuleName = " + requestedModuleName);
164                     LOGGER.error("this.moduleName = " + this.moduleName);
165                     throw new ClassNotFoundException(name);
166                 }
167
168                 // Find the class from the ancestor class loader
169                 return parentModule.classLoader.getLocalClass(name);
170             }
171         }
172         
173 //        protected Class<?> loadClass(String name, boolean resolve)
174 //                throws ClassNotFoundException
175 //            {
176 //                synchronized (getClassLoadingLock(name)) {
177 //                    // First, check if the class has already been loaded
178 //                    Class<?> c = findLoadedClass(name);
179 //                    if (c == null) {
180 //                        c = getClass(name);
181 //                    }
182 //                    if (resolve) {
183 //                        resolveClass(c);
184 //                    }
185 //                    return c;
186 //                }
187 //            }
188         
189         @Override
190         public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
191             Class<?> clazz = getClass(name);
192             if (resolve)
193                 resolveClass(clazz);
194             return clazz;
195         }
196         
197         @Override
198         public Class<?> loadClass(String name) throws ClassNotFoundException {
199             return super.loadClass(name);
200         }
201         
202         public Module getModule(String moduleName) {
203             //System.out.println("ModuleClassLoader.getModule(" + moduleName + ")");
204             if(moduleName.equals(this.moduleName))
205                 return module;
206             else {
207                 RuntimeModule parentModule = parentModuleMap.get(moduleName);
208                 if(parentModule == null)
209                     throw new RuntimeException("Didn't find module " + moduleName + ".");
210                 
211                 return parentModule.module;
212             }
213         }
214         
215         public String getModuleName() {
216             return moduleName;
217         }
218         
219         public synchronized String getFreshPackageName() {
220             return moduleName + "$" + (++transientPackageId);
221         }
222         
223         @Override
224         public THashMap<Constant, Object> getConstantCache() {
225             return null;
226         }
227         
228         @Override
229         public ClassLoader getClassLoader() {
230             return this;
231         }
232         
233         private void showDecompiledBytecode(String className) {
234             IDecompiler decompiler = DecompilerFactory.getDecompiler();
235             if(decompiler == null)
236                 return;
237             decompiler.decompile(this, className, new OutputStreamWriter(System.out));
238         }
239     }
240
241     
242     public Environment moduleEnvironment = new GlobalOnlyEnvironment() {
243         @Override
244         protected Collection<Module> getModules() {
245             ArrayList<Module> result = new ArrayList<Module>(parentModuleMap.size() + 1);
246             result.add(module);
247             for(RuntimeModule rm : parentModuleMap.values())
248                 result.add(rm.module);
249             return result;
250         }
251         @Override
252         protected Module getModule(String name) {
253             return classLoader.getModule(name);
254         }
255         @Override
256         public void collectRules(Collection<TransformationRule> rules) {
257         }
258         @Override
259         public List<Constant> getFieldAccessors(String name) {
260             // TODO Not clear if this is needed.
261             return null;
262         }
263     };
264     
265     public RuntimeModule(Module module, RuntimeModuleMap parentModuleMap,
266             ClassLoader parentClassLoader) {
267         if(parentClassLoader == null)
268             throw new NullPointerException();
269         this.module = module;
270         this.parentModuleMap = parentModuleMap;
271         this.classLoader = new ModuleClassLoader(parentClassLoader);
272         this.classBuilder = new TransientClassBuilder(classLoader,
273                 new JavaTypeTranslator(moduleEnvironment));
274     }
275
276     public Object getValue(String name) throws ValueNotFound {
277         // First try cache
278         if(valueCache.containsKey(name))
279             return valueCache.get(name);
280         
281         // Try to resolve the name
282         SCLValue valueConstructor = module.getValue(name);
283         if(valueConstructor == null)
284             throw new ValueNotFound(module.getName() + "/" + name);
285         
286         // Realize the value and cache it
287         Object value = valueConstructor.realizeValue(getClassBuilder());
288         valueCache.put(name, value);
289         return value;
290     }
291
292     private TransientClassBuilder getClassBuilder() {
293         return classBuilder;
294     }
295
296     public Module getModule() {
297         return module;
298     }
299
300     public MutableClassLoader getMutableClassLoader() {
301         return classLoader;
302     }
303  
304     public static String extractClassLoaderId(String className) {
305         int p = className.indexOf('$', MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH);
306         return JavaNamingPolicy.classNameToModuleName(p < 0 
307                 ? className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH) 
308                 : className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH, p));
309     }
310     
311     public void dispose() {
312         module.dispose();
313         module = null;
314         valueCache.clear();
315         parentModuleMap = null;
316         classLoader = null;
317         classBuilder = null;
318     }
319     
320     public class ClassNameRecordingRemapper extends Remapper {
321
322         private final Set<? super String> classNames;
323
324         public ClassNameRecordingRemapper(Set<? super String> classNames) {
325             this.classNames = classNames;
326         }
327         
328         @Override
329         public String map(String typeName) {
330             classNames.add(typeName);
331             return super.map(typeName);
332         }
333         
334         @Override
335         public String mapDesc(String desc) {
336             //classNames.add(desc);
337             return super.mapDesc(desc);
338         }
339         @Override
340         public String mapFieldName(String owner, String name, String desc) {
341             return super.mapFieldName(owner, name, desc);
342         }
343         @Override
344         public String mapInvokeDynamicMethodName(String name, String desc) {
345             // TODO Auto-generated method stub
346             return super.mapInvokeDynamicMethodName(name, desc);
347         }
348         @Override
349         public String mapMethodDesc(String desc) {
350             //classNames.add(desc);
351             return super.mapMethodDesc(desc);
352         }
353         @Override
354         public String mapMethodName(String owner, String name, String desc) {
355             // TODO Auto-generated method stub
356             return super.mapMethodName(owner, name, desc);
357         }
358         @Override
359         public String mapSignature(String signature, boolean typeSignature) {
360             //classNames.add(signature);
361             return super.mapSignature(signature, typeSignature);
362         }
363         @Override
364         public String mapType(String type) {
365             classNames.add(type);
366             return super.mapType(type);
367         }
368         @Override
369         public String[] mapTypes(String[] types) {
370             for(String type : types)
371                 classNames.add(type);
372             // TODO Auto-generated method stub
373             return super.mapTypes(types);
374         }
375         @Override
376         public Object mapValue(Object value) {
377             //classNames.add(value.toString());
378             // TODO Auto-generated method stub
379             return super.mapValue(value);
380         }
381     }
382     
383     public Set<String> classReferences(String className) {
384         try {
385             HashSet<String> referencedClasses = new HashSet<>();
386             ClassNameRecordingRemapper m = new ClassNameRecordingRemapper(referencedClasses);
387             ClassReader cr = new ClassReader(module.getClass(className));
388             int ASM5 = 5 << 16 | 0 << 8 | 0;
389             //TraceClassVisitor tcv = new TraceClassVisitor(null, new PrintWriter(System.err));
390             ClassVisitor cv1 = new ClassVisitor(ASM5) {};
391             ClassVisitor cv = new ClassRemapper(cv1, m);
392             cr.accept(cv, ClassReader.SKIP_DEBUG);
393             return referencedClasses;
394         } catch (Exception e) {
395             e.printStackTrace();
396         }
397         return null;
398     }
399     
400     public void loadClasses() {
401         if(module instanceof ConcreteModule) {
402             ConcreteModule cm = (ConcreteModule)module;
403             try {
404                 for(String className : cm.getClasses().keySet()) {
405                     Set<String> refs = classReferences(className);
406                     for(String s : refs) {
407                         String internalName = s.replace('/', '.');
408                         try {
409                             classLoader.getClass(internalName);
410                         } catch (Throwable e) {
411                             e.printStackTrace();
412                         }
413                     }
414                 }
415             } catch (Throwable e) {
416                 e.printStackTrace();
417             }
418         }
419     }
420
421 }