1 package org.simantics.scl.compiler.runtime;
3 import java.io.IOException;
4 import java.io.OutputStreamWriter;
5 import java.util.ArrayList;
6 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.module.Module;
18 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
19 import org.simantics.scl.compiler.top.ValueNotFound;
21 import com.strobel.assembler.metadata.Buffer;
22 import com.strobel.assembler.metadata.ClasspathTypeLoader;
23 import com.strobel.assembler.metadata.CompositeTypeLoader;
24 import com.strobel.assembler.metadata.ITypeLoader;
25 import com.strobel.decompiler.Decompiler;
26 import com.strobel.decompiler.DecompilerSettings;
27 import com.strobel.decompiler.PlainTextOutput;
29 import gnu.trove.map.hash.THashMap;
31 public class RuntimeModule {
32 public static final boolean VALIDATE_CLASS_NAMES = true;
33 public static final boolean TRACE_CLASS_CREATION = false;
36 ModuleClassLoader classLoader;
37 THashMap<String, Object> valueCache = new THashMap<String, Object>();
38 TransientClassBuilder classBuilder;
39 RuntimeModuleMap parentModuleMap;
41 class ModuleClassLoader extends ClassLoader implements MutableClassLoader {
44 THashMap<String, byte[]> localClasses = new THashMap<String, byte[]>();
45 //ModuleClassLoaderMap parentModules;
46 int transientPackageId = 0;
48 public ModuleClassLoader(ClassLoader parent) {
50 this.moduleName = module.getName();
53 public synchronized void addClass(String name, byte[] class_) {
54 if(TRACE_CLASS_CREATION)
55 System.out.println("addClass " + name + " (" + class_.length + " bytes)");
56 if(VALIDATE_CLASS_NAMES)
57 validateClassName(name);
58 localClasses.put(name, class_);
61 public synchronized void addClasses(Map<String, byte[]> classes) {
62 if(TRACE_CLASS_CREATION)
63 for(String name : classes.keySet())
64 System.out.println("addClass " + name + " (" + classes.get(name).length + " bytes)");
65 if(VALIDATE_CLASS_NAMES)
66 for(String name : classes.keySet())
67 validateClassName(name);
68 localClasses.putAll(classes);
71 private void validateClassName(String name) {
72 //System.out.println(name);
73 /*if(!name.startsWith(SCL_PACKAGE_PREFIX) || !extractClassLoaderId(name).equals(moduleName))
74 throw new IllegalArgumentException("Class name " + name + " does not start with '" +
75 SCL_PACKAGE_PREFIX + moduleName + "$'.");
79 public byte[] getBytes(String name) {
80 // Non-SCL classes are not handled here
81 if(!name.startsWith(SCL_PACKAGE_PREFIX))
84 // Determine the id of the class loader which is responsible of the class
85 String requestedModuleName = extractClassLoaderId(name);
87 // Is class defined locally in this class loader?
88 if(requestedModuleName.equals(this.moduleName)) {
89 String internalName = name.replace('.', '/');
90 byte[] bytes = module.getClass(internalName);
93 return localClasses.get(internalName);
96 // Find suitable class loader that has this class locally
98 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
99 if(parentModule == null)
102 // Find the class from the ancestor class loader
103 return parentModule.classLoader.getBytes(name);
107 synchronized Class<?> getLocalClass(String name) throws ClassNotFoundException {
108 // Is class already loaded
109 Class<?> clazz = findLoadedClass(name);
113 // If we have bytecode for it, let's define the class
114 String internalName = name.replace('.', '/');
115 byte[] bytes = module.getClass(internalName);
117 bytes = localClasses.get(internalName);
119 throw new ClassNotFoundException(name);
121 if(SCLCompilerConfiguration.SHOW_LOADED_CLASSES_DISASSEMBLED) {
122 DecompilerSettings settings = DecompilerSettings.javaDefaults();
123 ITypeLoader typeLoader = new ITypeLoader() {
125 public boolean tryLoadType(String internalName, Buffer buffer) {
126 byte[] bytes = getBytes(internalName);
128 buffer.reset(bytes.length);
129 buffer.putByteArray(bytes, 0, bytes.length);
137 settings.setTypeLoader(new CompositeTypeLoader(typeLoader, new ClasspathTypeLoader()));
138 OutputStreamWriter writer = new OutputStreamWriter(System.out);
139 PlainTextOutput output = new PlainTextOutput(writer);
140 Decompiler.decompile(name, output, settings);
143 } catch (IOException e) {
146 return defineClass(name, bytes, 0, bytes.length);
149 private Class<?> getClass(String name) throws ClassNotFoundException {
150 //System.out.println("getClass " + name);
152 // If the class is not generated from SCL, use parent class loader
153 if(!name.startsWith(SCL_PACKAGE_PREFIX)) {
155 return getParent().loadClass(name);
156 } catch(ClassNotFoundException e) {
157 for(RuntimeModule module : parentModuleMap.values())
159 return module.classLoader.getParent().loadClass(name);
160 } catch(ClassNotFoundException e2) {
163 throw new ClassNotFoundException(name);
166 // Determine the id of the class loader which is responsible of the class
167 String requestedModuleName = extractClassLoaderId(name);
169 // Is class defined locally in this class loader?
170 if(requestedModuleName.equals(this.moduleName))
171 return getLocalClass(name);
173 // Find suitable class loader that has this class locally
175 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
176 if(parentModule == null) {
177 System.err.println("requestedModuleName = " + requestedModuleName);
178 System.err.println("this.moduleName = " + this.moduleName);
179 throw new ClassNotFoundException(name);
182 // Find the class from the ancestor class loader
183 return parentModule.classLoader.getLocalClass(name);
188 public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
189 Class<?> clazz = getClass(name);
195 public Module getModule(String moduleName) {
196 //System.out.println("ModuleClassLoader.getModule(" + moduleName + ")");
197 if(moduleName.equals(this.moduleName))
200 RuntimeModule parentModule = parentModuleMap.get(moduleName);
201 if(parentModule == null)
202 throw new RuntimeException("Didn't find module " + moduleName + ".");
204 return parentModule.module;
208 public String getModuleName() {
212 public synchronized String getFreshPackageName() {
213 return moduleName + "$" + (++transientPackageId);
217 public THashMap<Constant, Object> getConstantCache() {
222 public ClassLoader getClassLoader() {
228 public Environment moduleEnvironment = new GlobalOnlyEnvironment() {
230 protected Collection<Module> getModules() {
231 ArrayList<Module> result = new ArrayList<Module>(parentModuleMap.size() + 1);
233 for(RuntimeModule rm : parentModuleMap.values())
234 result.add(rm.module);
238 protected Module getModule(String name) {
239 return classLoader.getModule(name);
242 public void collectRules(Collection<TransformationRule> rules) {
246 public RuntimeModule(Module module, RuntimeModuleMap parentModuleMap,
247 ClassLoader parentClassLoader) {
248 this.module = module;
249 this.parentModuleMap = parentModuleMap;
250 this.classLoader = new ModuleClassLoader(parentClassLoader);
251 this.classBuilder = new TransientClassBuilder(classLoader,
252 new JavaTypeTranslator(moduleEnvironment));
255 public Object getValue(String name) throws ValueNotFound {
257 if(valueCache.containsKey(name))
258 return valueCache.get(name);
260 // Try to resolve the name
261 SCLValue valueConstructor = module.getValue(name);
262 if(valueConstructor == null)
263 throw new ValueNotFound(module.getName() + "/" + name);
265 // Realize the value and cache it
266 Object value = valueConstructor.realizeValue(getClassBuilder());
267 valueCache.put(name, value);
271 private TransientClassBuilder getClassBuilder() {
275 public Module getModule() {
279 public MutableClassLoader getMutableClassLoader() {
283 public static String extractClassLoaderId(String className) {
284 int p = className.indexOf('$', MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH);
285 return JavaNamingPolicy.classNameToModuleName(p < 0
286 ? className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH)
287 : className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH, p));
290 public void dispose() {
294 parentModuleMap = null;