1 package org.simantics.scl.compiler.runtime;
3 import java.util.ArrayList;
4 import java.util.Collection;
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;
19 import gnu.trove.map.hash.THashMap;
21 public class RuntimeModule {
22 public static final boolean VALIDATE_CLASS_NAMES = true;
23 public static final boolean TRACE_CLASS_CREATION = false;
26 ModuleClassLoader classLoader;
27 THashMap<String, Object> valueCache = new THashMap<String, Object>();
28 TransientClassBuilder classBuilder;
29 RuntimeModuleMap parentModuleMap;
31 class ModuleClassLoader extends ClassLoader implements MutableClassLoader {
34 THashMap<String, byte[]> localClasses = new THashMap<String, byte[]>();
35 //ModuleClassLoaderMap parentModules;
36 int transientPackageId = 0;
38 public ModuleClassLoader(ClassLoader parent) {
40 this.moduleName = module.getName();
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_);
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);
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 + "$'.");
69 public byte[] getBytes(String name) {
70 // Non-SCL classes are not handled here
71 if(!name.startsWith(SCL_PACKAGE_PREFIX))
74 // Determine the id of the class loader which is responsible of the class
75 String requestedModuleName = extractClassLoaderId(name);
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);
83 return localClasses.get(internalName);
86 // Find suitable class loader that has this class locally
88 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
89 if(parentModule == null)
92 // Find the class from the ancestor class loader
93 return parentModule.classLoader.getBytes(name);
97 synchronized Class<?> getLocalClass(String name) throws ClassNotFoundException {
98 // Is class already loaded
99 Class<?> clazz = findLoadedClass(name);
103 // If we have bytecode for it, let's define the class
104 String internalName = name.replace('.', '/');
105 byte[] bytes = module.getClass(internalName);
107 bytes = localClasses.get(internalName);
109 throw new ClassNotFoundException(name);
111 return defineClass(name, bytes, 0, bytes.length);
114 private Class<?> getClass(String name) throws ClassNotFoundException {
115 //System.out.println("getClass " + name);
117 // If the class is not generated from SCL, use parent class loader
118 if(!name.startsWith(SCL_PACKAGE_PREFIX)) {
120 return getParent().loadClass(name);
121 } catch(ClassNotFoundException e) {
122 for(RuntimeModule module : parentModuleMap.values())
124 return module.classLoader.getParent().loadClass(name);
125 } catch(ClassNotFoundException e2) {
128 throw new ClassNotFoundException(name);
131 // Determine the id of the class loader which is responsible of the class
132 String requestedModuleName = extractClassLoaderId(name);
134 // Is class defined locally in this class loader?
135 if(requestedModuleName.equals(this.moduleName))
136 return getLocalClass(name);
138 // Find suitable class loader that has this class locally
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);
147 // Find the class from the ancestor class loader
148 return parentModule.classLoader.getLocalClass(name);
153 public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
154 Class<?> clazz = getClass(name);
160 public Module getModule(String moduleName) {
161 //System.out.println("ModuleClassLoader.getModule(" + moduleName + ")");
162 if(moduleName.equals(this.moduleName))
165 RuntimeModule parentModule = parentModuleMap.get(moduleName);
166 if(parentModule == null)
167 throw new RuntimeException("Didn't find module " + moduleName + ".");
169 return parentModule.module;
173 public String getModuleName() {
177 public synchronized String getFreshPackageName() {
178 return moduleName + "$" + (++transientPackageId);
182 public THashMap<Constant, Object> getConstantCache() {
187 public ClassLoader getClassLoader() {
193 public Environment moduleEnvironment = new GlobalOnlyEnvironment() {
195 protected Collection<Module> getModules() {
196 ArrayList<Module> result = new ArrayList<Module>(parentModuleMap.size() + 1);
198 for(RuntimeModule rm : parentModuleMap.values())
199 result.add(rm.module);
203 protected Module getModule(String name) {
204 return classLoader.getModule(name);
207 public void collectRules(Collection<TransformationRule> rules) {
210 public List<Constant> getFieldAccessors(String name) {
211 // TODO Not clear if this is needed.
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));
227 public Object getValue(String name) throws ValueNotFound {
229 if(valueCache.containsKey(name))
230 return valueCache.get(name);
232 // Try to resolve the name
233 SCLValue valueConstructor = module.getValue(name);
234 if(valueConstructor == null)
235 throw new ValueNotFound(module.getName() + "/" + name);
237 // Realize the value and cache it
238 Object value = valueConstructor.realizeValue(getClassBuilder());
239 valueCache.put(name, value);
243 private TransientClassBuilder getClassBuilder() {
247 public Module getModule() {
251 public MutableClassLoader getMutableClassLoader() {
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));
262 public void dispose() {
266 parentModuleMap = null;