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