1 package org.simantics.scl.compiler.runtime;
3 import java.util.ArrayList;
4 import java.util.Collection;
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;
18 import gnu.trove.map.hash.THashMap;
20 public class RuntimeModule {
21 public static final boolean VALIDATE_CLASS_NAMES = true;
22 public static final boolean TRACE_CLASS_CREATION = false;
25 ModuleClassLoader classLoader;
26 THashMap<String, Object> valueCache = new THashMap<String, Object>();
27 TransientClassBuilder classBuilder;
28 RuntimeModuleMap parentModuleMap;
30 class ModuleClassLoader extends ClassLoader implements MutableClassLoader {
33 THashMap<String, byte[]> localClasses = new THashMap<String, byte[]>();
34 //ModuleClassLoaderMap parentModules;
35 int transientPackageId = 0;
37 public ModuleClassLoader(ClassLoader parent) {
39 this.moduleName = module.getName();
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_);
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);
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 + "$'.");
68 public byte[] getBytes(String name) {
69 // Non-SCL classes are not handled here
70 if(!name.startsWith(SCL_PACKAGE_PREFIX))
73 // Determine the id of the class loader which is responsible of the class
74 String requestedModuleName = extractClassLoaderId(name);
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);
82 return localClasses.get(internalName);
85 // Find suitable class loader that has this class locally
87 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
88 if(parentModule == null)
91 // Find the class from the ancestor class loader
92 return parentModule.classLoader.getBytes(name);
96 synchronized Class<?> getLocalClass(String name) throws ClassNotFoundException {
97 // Is class already loaded
98 Class<?> clazz = findLoadedClass(name);
102 // If we have bytecode for it, let's define the class
103 String internalName = name.replace('.', '/');
104 byte[] bytes = module.getClass(internalName);
106 bytes = localClasses.get(internalName);
108 throw new ClassNotFoundException(name);
110 return defineClass(name, bytes, 0, bytes.length);
113 private Class<?> getClass(String name) throws ClassNotFoundException {
114 //System.out.println("getClass " + name);
116 // If the class is not generated from SCL, use parent class loader
117 if(!name.startsWith(SCL_PACKAGE_PREFIX)) {
119 return getParent().loadClass(name);
120 } catch(ClassNotFoundException e) {
121 for(RuntimeModule module : parentModuleMap.values())
123 return module.classLoader.getParent().loadClass(name);
124 } catch(ClassNotFoundException e2) {
127 throw new ClassNotFoundException(name);
130 // Determine the id of the class loader which is responsible of the class
131 String requestedModuleName = extractClassLoaderId(name);
133 // Is class defined locally in this class loader?
134 if(requestedModuleName.equals(this.moduleName))
135 return getLocalClass(name);
137 // Find suitable class loader that has this class locally
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);
146 // Find the class from the ancestor class loader
147 return parentModule.classLoader.getLocalClass(name);
152 public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
153 Class<?> clazz = getClass(name);
159 public Module getModule(String moduleName) {
160 //System.out.println("ModuleClassLoader.getModule(" + moduleName + ")");
161 if(moduleName.equals(this.moduleName))
164 RuntimeModule parentModule = parentModuleMap.get(moduleName);
165 if(parentModule == null)
166 throw new RuntimeException("Didn't find module " + moduleName + ".");
168 return parentModule.module;
172 public String getModuleName() {
176 public synchronized String getFreshPackageName() {
177 return moduleName + "$" + (++transientPackageId);
181 public THashMap<Constant, Object> getConstantCache() {
186 public ClassLoader getClassLoader() {
192 public Environment moduleEnvironment = new GlobalOnlyEnvironment() {
194 protected Collection<Module> getModules() {
195 ArrayList<Module> result = new ArrayList<Module>(parentModuleMap.size() + 1);
197 for(RuntimeModule rm : parentModuleMap.values())
198 result.add(rm.module);
202 protected Module getModule(String name) {
203 return classLoader.getModule(name);
206 public void collectRules(Collection<TransformationRule> rules) {
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));
219 public Object getValue(String name) throws ValueNotFound {
221 if(valueCache.containsKey(name))
222 return valueCache.get(name);
224 // Try to resolve the name
225 SCLValue valueConstructor = module.getValue(name);
226 if(valueConstructor == null)
227 throw new ValueNotFound(module.getName() + "/" + name);
229 // Realize the value and cache it
230 Object value = valueConstructor.realizeValue(getClassBuilder());
231 valueCache.put(name, value);
235 private TransientClassBuilder getClassBuilder() {
239 public Module getModule() {
243 public MutableClassLoader getMutableClassLoader() {
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));
254 public void dispose() {
258 parentModuleMap = null;