1 package org.simantics.scl.compiler.runtime;
3 import java.io.OutputStreamWriter;
4 import java.util.ArrayList;
5 import java.util.Collection;
9 import org.simantics.scl.compiler.constants.Constant;
10 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
11 import org.simantics.scl.compiler.elaboration.rules.TransformationRule;
12 import org.simantics.scl.compiler.environment.Environment;
13 import org.simantics.scl.compiler.environment.GlobalOnlyEnvironment;
14 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
15 import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy;
16 import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder;
17 import org.simantics.scl.compiler.internal.decompilation.DecompilerFactory;
18 import org.simantics.scl.compiler.internal.decompilation.IDecompiler;
19 import org.simantics.scl.compiler.module.Module;
20 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
21 import org.simantics.scl.compiler.top.ValueNotFound;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
25 import gnu.trove.map.hash.THashMap;
27 public class RuntimeModule {
28 private static final Logger LOGGER = LoggerFactory.getLogger(RuntimeModule.class);
30 public static final boolean VALIDATE_CLASS_NAMES = true;
31 public static final boolean TRACE_CLASS_CREATION = false;
34 ModuleClassLoader classLoader;
35 THashMap<String, Object> valueCache = new THashMap<String, Object>();
36 TransientClassBuilder classBuilder;
37 RuntimeModuleMap parentModuleMap;
39 class ModuleClassLoader extends ClassLoader implements MutableClassLoader {
42 THashMap<String, byte[]> localClasses = new THashMap<String, byte[]>();
43 //ModuleClassLoaderMap parentModules;
44 int transientPackageId = 0;
46 public ModuleClassLoader(ClassLoader parent) {
48 this.moduleName = module.getName();
51 public synchronized void addClass(String name, byte[] class_) {
52 if(TRACE_CLASS_CREATION)
53 System.out.println("addClass " + name + " (" + class_.length + " bytes)");
54 if(VALIDATE_CLASS_NAMES)
55 validateClassName(name);
56 localClasses.put(name, class_);
59 public synchronized void addClasses(Map<String, byte[]> classes) {
60 if(TRACE_CLASS_CREATION)
61 for(String name : classes.keySet())
62 System.out.println("addClass " + name + " (" + classes.get(name).length + " bytes)");
63 if(VALIDATE_CLASS_NAMES)
64 for(String name : classes.keySet())
65 validateClassName(name);
66 localClasses.putAll(classes);
69 private void validateClassName(String name) {
70 //System.out.println(name);
71 /*if(!name.startsWith(SCL_PACKAGE_PREFIX) || !extractClassLoaderId(name).equals(moduleName))
72 throw new IllegalArgumentException("Class name " + name + " does not start with '" +
73 SCL_PACKAGE_PREFIX + moduleName + "$'.");
78 public byte[] getBytes(String name) {
79 // Non-SCL classes are not handled here
80 if(!name.startsWith(SCL_PACKAGE_PREFIX))
83 // Determine the id of the class loader which is responsible of the class
84 String requestedModuleName = extractClassLoaderId(name);
86 // Is class defined locally in this class loader?
87 if(requestedModuleName.equals(this.moduleName)) {
88 String internalName = name.replace('.', '/');
89 byte[] bytes = module.getClass(internalName);
92 return localClasses.get(internalName);
95 // Find suitable class loader that has this class locally
97 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
98 if(parentModule == null)
101 // Find the class from the ancestor class loader
102 return parentModule.classLoader.getBytes(name);
106 synchronized Class<?> getLocalClass(String name) throws ClassNotFoundException {
107 // Is class already loaded
108 Class<?> clazz = findLoadedClass(name);
112 // If we have bytecode for it, let's define the class
113 String internalName = name.replace('.', '/');
114 byte[] bytes = module.getClass(internalName);
116 bytes = localClasses.get(internalName);
118 throw new ClassNotFoundException(name);
120 if(SCLCompilerConfiguration.SHOW_DECOMPILED_BYTECODE && SCLCompilerConfiguration.debugFilter(moduleName))
121 showDecompiledBytecode(internalName);
122 return defineClass(name, bytes, 0, bytes.length);
125 private Class<?> getClass(String name) throws ClassNotFoundException {
126 //System.out.println("getClass " + name);
128 // If the class is not generated from SCL, use parent class loader
129 if(!name.startsWith(SCL_PACKAGE_PREFIX)) {
131 return getParent().loadClass(name);
132 } catch(ClassNotFoundException e) {
133 for(RuntimeModule module : parentModuleMap.values())
135 return module.classLoader.getParent().loadClass(name);
136 } catch(ClassNotFoundException e2) {
139 throw new ClassNotFoundException(name);
142 // Determine the id of the class loader which is responsible of the class
143 String requestedModuleName = extractClassLoaderId(name);
145 // Is class defined locally in this class loader?
146 if(requestedModuleName.equals(this.moduleName))
147 return getLocalClass(name);
149 // Find suitable class loader that has this class locally
151 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
152 if(parentModule == null) {
153 LOGGER.error("requestedModuleName = " + requestedModuleName);
154 LOGGER.error("this.moduleName = " + this.moduleName);
155 throw new ClassNotFoundException(name);
158 // Find the class from the ancestor class loader
159 return parentModule.classLoader.getLocalClass(name);
164 public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
165 Class<?> clazz = getClass(name);
171 public Module getModule(String moduleName) {
172 //System.out.println("ModuleClassLoader.getModule(" + moduleName + ")");
173 if(moduleName.equals(this.moduleName))
176 RuntimeModule parentModule = parentModuleMap.get(moduleName);
177 if(parentModule == null)
178 throw new RuntimeException("Didn't find module " + moduleName + ".");
180 return parentModule.module;
184 public String getModuleName() {
188 public synchronized String getFreshPackageName() {
189 return moduleName + "$" + (++transientPackageId);
193 public THashMap<Constant, Object> getConstantCache() {
198 public ClassLoader getClassLoader() {
202 private void showDecompiledBytecode(String className) {
203 IDecompiler decompiler = DecompilerFactory.getDecompiler();
204 if(decompiler == null)
206 decompiler.decompile(this, className, new OutputStreamWriter(System.out));
211 public Environment moduleEnvironment = new GlobalOnlyEnvironment() {
213 protected Collection<Module> getModules() {
214 ArrayList<Module> result = new ArrayList<Module>(parentModuleMap.size() + 1);
216 for(RuntimeModule rm : parentModuleMap.values())
217 result.add(rm.module);
221 protected Module getModule(String name) {
222 return classLoader.getModule(name);
225 public void collectRules(Collection<TransformationRule> rules) {
228 public List<Constant> getFieldAccessors(String name) {
229 // TODO Not clear if this is needed.
234 public RuntimeModule(Module module, RuntimeModuleMap parentModuleMap,
235 ClassLoader parentClassLoader) {
236 if(parentClassLoader == null)
237 throw new NullPointerException();
238 this.module = module;
239 this.parentModuleMap = parentModuleMap;
240 this.classLoader = new ModuleClassLoader(parentClassLoader);
241 this.classBuilder = new TransientClassBuilder(classLoader,
242 new JavaTypeTranslator(moduleEnvironment));
245 public Object getValue(String name) throws ValueNotFound {
247 if(valueCache.containsKey(name))
248 return valueCache.get(name);
250 // Try to resolve the name
251 SCLValue valueConstructor = module.getValue(name);
252 if(valueConstructor == null)
253 throw new ValueNotFound(module.getName() + "/" + name);
255 // Realize the value and cache it
256 Object value = valueConstructor.realizeValue(getClassBuilder());
257 valueCache.put(name, value);
261 private TransientClassBuilder getClassBuilder() {
265 public Module getModule() {
269 public MutableClassLoader getMutableClassLoader() {
273 public static String extractClassLoaderId(String className) {
274 int p = className.indexOf('$', MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH);
275 return JavaNamingPolicy.classNameToModuleName(p < 0
276 ? className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH)
277 : className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH, p));
280 public void dispose() {
284 parentModuleMap = null;