1 package org.simantics.scl.compiler.runtime;
3 import java.io.OutputStreamWriter;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.HashSet;
11 import org.objectweb.asm.ClassReader;
12 import org.objectweb.asm.ClassVisitor;
13 import org.objectweb.asm.commons.ClassRemapper;
14 import org.objectweb.asm.commons.Remapper;
15 import org.simantics.scl.compiler.constants.Constant;
16 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
17 import org.simantics.scl.compiler.elaboration.rules.TransformationRule;
18 import org.simantics.scl.compiler.environment.Environment;
19 import org.simantics.scl.compiler.environment.GlobalOnlyEnvironment;
20 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
21 import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy;
22 import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder;
23 import org.simantics.scl.compiler.internal.decompilation.DecompilerFactory;
24 import org.simantics.scl.compiler.internal.decompilation.IDecompiler;
25 import org.simantics.scl.compiler.module.ConcreteModule;
26 import org.simantics.scl.compiler.module.Module;
27 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
28 import org.simantics.scl.compiler.top.ValueNotFound;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
32 import gnu.trove.map.hash.THashMap;
34 public class RuntimeModule {
35 private static final Logger LOGGER = LoggerFactory.getLogger(RuntimeModule.class);
37 public static final boolean VALIDATE_CLASS_NAMES = true;
38 public static final boolean TRACE_CLASS_CREATION = true;
41 ModuleClassLoader classLoader;
42 THashMap<String, Object> valueCache = new THashMap<String, Object>();
43 TransientClassBuilder classBuilder;
44 RuntimeModuleMap parentModuleMap;
46 class ModuleClassLoader extends ClassLoader implements MutableClassLoader {
49 THashMap<String, byte[]> localClasses = new THashMap<String, byte[]>();
50 //ModuleClassLoaderMap parentModules;
51 int transientPackageId = 0;
53 public ModuleClassLoader(ClassLoader parent) {
55 this.moduleName = module.getName();
58 public synchronized void addClass(String name, byte[] class_) {
59 if(TRACE_CLASS_CREATION)
60 System.out.println("addClass " + name + " (" + class_.length + " bytes)");
61 if(VALIDATE_CLASS_NAMES)
62 validateClassName(name);
63 localClasses.put(name, class_);
66 public synchronized void addClasses(Map<String, byte[]> classes) {
67 if(TRACE_CLASS_CREATION)
68 for(String name : classes.keySet())
69 System.out.println("addClass " + name + " (" + classes.get(name).length + " bytes)");
70 if(VALIDATE_CLASS_NAMES)
71 for(String name : classes.keySet())
72 validateClassName(name);
73 localClasses.putAll(classes);
76 private void validateClassName(String name) {
77 //System.out.println(name);
78 /*if(!name.startsWith(SCL_PACKAGE_PREFIX) || !extractClassLoaderId(name).equals(moduleName))
79 throw new IllegalArgumentException("Class name " + name + " does not start with '" +
80 SCL_PACKAGE_PREFIX + moduleName + "$'.");
85 public byte[] getBytes(String name) {
86 // Non-SCL classes are not handled here
87 if(!name.startsWith(SCL_PACKAGE_PREFIX))
90 // Determine the id of the class loader which is responsible of the class
91 String requestedModuleName = extractClassLoaderId(name);
93 // Is class defined locally in this class loader?
94 if(requestedModuleName.equals(this.moduleName)) {
95 String internalName = name.replace('.', '/');
96 byte[] bytes = module.getClass(internalName);
99 return localClasses.get(internalName);
102 // Find suitable class loader that has this class locally
104 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
105 if(parentModule == null)
108 // Find the class from the ancestor class loader
109 return parentModule.classLoader.getBytes(name);
113 synchronized Class<?> getLocalClass(String name) throws ClassNotFoundException {
114 // Is class already loaded
115 Class<?> clazz = findLoadedClass(name);
119 // If we have bytecode for it, let's define the class
120 String internalName = name.replace('.', '/');
121 byte[] bytes = module.getClass(internalName);
123 bytes = localClasses.get(internalName);
125 throw new ClassNotFoundException(name);
127 if(SCLCompilerConfiguration.SHOW_DECOMPILED_BYTECODE && SCLCompilerConfiguration.debugFilter(moduleName))
128 showDecompiledBytecode(internalName);
129 return defineClass(name, bytes, 0, bytes.length);
132 private Class<?> getClass(String name) throws ClassNotFoundException {
134 System.out.println(moduleName + ":getClass " + name);
136 // If the class is not generated from SCL, use parent class loader
137 if(!name.startsWith(SCL_PACKAGE_PREFIX)) {
139 return getParent().loadClass(name);
140 } catch(ClassNotFoundException e) {
141 for(RuntimeModule module : parentModuleMap.values())
143 return module.classLoader.getParent().loadClass(name);
144 } catch(ClassNotFoundException e2) {
147 throw new ClassNotFoundException(name);
150 // Determine the id of the class loader which is responsible of the class
151 String requestedModuleName = extractClassLoaderId(name);
153 // Is class defined locally in this class loader?
154 if(requestedModuleName.equals(this.moduleName))
155 return getLocalClass(name);
157 // Find suitable class loader that has this class locally
159 RuntimeModule parentModule = parentModuleMap.get(requestedModuleName);
160 if(parentModule == null) {
161 LOGGER.error("requestedModuleName = " + requestedModuleName);
162 LOGGER.error("this.moduleName = " + this.moduleName);
163 throw new ClassNotFoundException(name);
166 // Find the class from the ancestor class loader
167 return parentModule.classLoader.getLocalClass(name);
171 // protected Class<?> loadClass(String name, boolean resolve)
172 // throws ClassNotFoundException
174 // synchronized (getClassLoadingLock(name)) {
175 // // First, check if the class has already been loaded
176 // Class<?> c = findLoadedClass(name);
178 // c = getClass(name);
188 public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
189 Class<?> clazz = getClass(name);
196 public Class<?> loadClass(String name) throws ClassNotFoundException {
197 return super.loadClass(name);
200 public Module getModule(String moduleName) {
201 //System.out.println("ModuleClassLoader.getModule(" + moduleName + ")");
202 if(moduleName.equals(this.moduleName))
205 RuntimeModule parentModule = parentModuleMap.get(moduleName);
206 if(parentModule == null)
207 throw new RuntimeException("Didn't find module " + moduleName + ".");
209 return parentModule.module;
213 public String getModuleName() {
217 public synchronized String getFreshPackageName() {
218 return moduleName + "$" + (++transientPackageId);
222 public THashMap<Constant, Object> getConstantCache() {
227 public ClassLoader getClassLoader() {
231 private void showDecompiledBytecode(String className) {
232 IDecompiler decompiler = DecompilerFactory.getDecompiler();
233 if(decompiler == null)
235 decompiler.decompile(this, className, new OutputStreamWriter(System.out));
240 public Environment moduleEnvironment = new GlobalOnlyEnvironment() {
242 protected Collection<Module> getModules() {
243 ArrayList<Module> result = new ArrayList<Module>(parentModuleMap.size() + 1);
245 for(RuntimeModule rm : parentModuleMap.values())
246 result.add(rm.module);
250 protected Module getModule(String name) {
251 return classLoader.getModule(name);
254 public void collectRules(Collection<TransformationRule> rules) {
257 public List<Constant> getFieldAccessors(String name) {
258 // TODO Not clear if this is needed.
263 public RuntimeModule(Module module, RuntimeModuleMap parentModuleMap,
264 ClassLoader parentClassLoader) {
265 if(parentClassLoader == null)
266 throw new NullPointerException();
267 this.module = module;
268 this.parentModuleMap = parentModuleMap;
269 this.classLoader = new ModuleClassLoader(parentClassLoader);
270 this.classBuilder = new TransientClassBuilder(classLoader,
271 new JavaTypeTranslator(moduleEnvironment));
274 public Object getValue(String name) throws ValueNotFound {
276 if(valueCache.containsKey(name))
277 return valueCache.get(name);
279 // Try to resolve the name
280 SCLValue valueConstructor = module.getValue(name);
281 if(valueConstructor == null)
282 throw new ValueNotFound(module.getName() + "/" + name);
284 // Realize the value and cache it
285 Object value = valueConstructor.realizeValue(getClassBuilder());
286 valueCache.put(name, value);
290 private TransientClassBuilder getClassBuilder() {
294 public Module getModule() {
298 public MutableClassLoader getMutableClassLoader() {
302 public static String extractClassLoaderId(String className) {
303 int p = className.indexOf('$', MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH);
304 return JavaNamingPolicy.classNameToModuleName(p < 0
305 ? className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH)
306 : className.substring(MutableClassLoader.SCL_PACKAGE_PREFIX_LENGTH, p));
309 public void dispose() {
313 parentModuleMap = null;
318 public class ClassNameRecordingRemapper extends Remapper {
320 private final Set<? super String> classNames;
322 public ClassNameRecordingRemapper(Set<? super String> classNames) {
323 this.classNames = classNames;
327 public String map(String typeName) {
328 classNames.add(typeName);
329 return super.map(typeName);
333 public String mapDesc(String desc) {
334 //classNames.add(desc);
335 return super.mapDesc(desc);
338 public String mapFieldName(String owner, String name, String desc) {
339 return super.mapFieldName(owner, name, desc);
342 public String mapInvokeDynamicMethodName(String name, String desc) {
343 // TODO Auto-generated method stub
344 return super.mapInvokeDynamicMethodName(name, desc);
347 public String mapMethodDesc(String desc) {
348 //classNames.add(desc);
349 return super.mapMethodDesc(desc);
352 public String mapMethodName(String owner, String name, String desc) {
353 // TODO Auto-generated method stub
354 return super.mapMethodName(owner, name, desc);
357 public String mapSignature(String signature, boolean typeSignature) {
358 //classNames.add(signature);
359 return super.mapSignature(signature, typeSignature);
362 public String mapType(String type) {
363 classNames.add(type);
364 return super.mapType(type);
367 public String[] mapTypes(String[] types) {
368 for(String type : types)
369 classNames.add(type);
370 // TODO Auto-generated method stub
371 return super.mapTypes(types);
374 public Object mapValue(Object value) {
375 //classNames.add(value.toString());
376 // TODO Auto-generated method stub
377 return super.mapValue(value);
381 public Set<String> classReferences(String className) {
383 HashSet<String> referencedClasses = new HashSet<>();
384 ClassNameRecordingRemapper m = new ClassNameRecordingRemapper(referencedClasses);
385 ClassReader cr = new ClassReader(module.getClass(className));
386 int ASM5 = 5 << 16 | 0 << 8 | 0;
387 //TraceClassVisitor tcv = new TraceClassVisitor(null, new PrintWriter(System.err));
388 ClassVisitor cv1 = new ClassVisitor(ASM5) {};
389 ClassVisitor cv = new ClassRemapper(cv1, m);
390 cr.accept(cv, ClassReader.SKIP_DEBUG);
391 System.err.println(className + " refs: " + referencedClasses);
392 return referencedClasses;
393 } catch (Exception e) {
399 public void loadReferences() {
400 ConcreteModule cm = (ConcreteModule)module;
402 for(String className : cm.getClasses().keySet()) {
403 Set<String> refs = classReferences(className);
404 for(String s : refs) {
405 String internalName = s.replace('/', '.');
407 classLoader.loadClass(internalName);
408 } catch (Throwable e) {
413 } catch (Throwable e) {