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