]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/runtime/ExpressionClassLoader.java
Merge remote-tracking branch 'origin/master' into private/antti2
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / runtime / ExpressionClassLoader.java
1 package org.simantics.scl.compiler.runtime;
2
3 import java.util.Map;
4
5 import org.simantics.scl.compiler.constants.Constant;
6
7 import gnu.trove.map.hash.THashMap;
8
9 public class ExpressionClassLoader extends ClassLoader implements MutableClassLoader {
10     public static final boolean VALIDATE_CLASS_NAMES = true;
11     public static final boolean TRACE_CLASS_CREATION = false;
12     
13     String basePackageName;
14     public THashMap<String, byte[]> localClasses = new THashMap<String, byte[]>(); 
15     THashMap<String, RuntimeModule> runtimeModuleMap;
16     int transientPackageId = 0;
17     THashMap<Constant,Object> valueCache = new THashMap<Constant,Object>(); 
18     
19     public ExpressionClassLoader(ClassLoader parent, THashMap<String, RuntimeModule> runtimeModuleMap, String basePackageName) {
20         super(parent);
21         this.basePackageName = basePackageName;
22         this.runtimeModuleMap = runtimeModuleMap;
23     }
24     
25     public synchronized void addClass(String name, byte[] class_) {
26         if(TRACE_CLASS_CREATION)
27             System.out.println("addClass " + name + " (" + class_.length + " bytes)");
28         if(VALIDATE_CLASS_NAMES)
29             validateClassName(name);
30         localClasses.put(name, class_);
31     }
32     
33     public synchronized void addClasses(Map<String, byte[]> classes) {
34         if(TRACE_CLASS_CREATION)
35             for(String name : classes.keySet())
36                 System.out.println("addClass " + name + " (" + classes.get(name).length + " bytes)");
37         if(VALIDATE_CLASS_NAMES)
38             for(String name : classes.keySet())
39                 validateClassName(name);
40         localClasses.putAll(classes);
41     }
42     
43     private void validateClassName(String name) {
44         /*if(!name.startsWith(SCL_PACKAGE_PREFIX) || !extractClassLoaderId(name).equals(basePackageName))
45             throw new IllegalArgumentException("Class name " + name + " does not start with '" +
46                     SCL_PACKAGE_PREFIX + basePackageName + "$'.");*/
47     }
48     
49     private synchronized Class<?> getLocalClass(String name) throws ClassNotFoundException {
50         // Is class already loaded
51         Class<?> clazz = findLoadedClass(name);
52         if(clazz != null)
53             return clazz;
54
55         // If we have bytecode for it, let's define the class
56         byte[] bytes = localClasses.get(name.replace('.', '/'));
57         if(bytes == null)
58             throw new ClassNotFoundException(name);
59
60         clazz = defineClass(name, bytes, 0, bytes.length);
61         resolveClass(clazz);
62         
63         return clazz;
64         
65     }
66     
67     @Override
68     public byte[] getBytes(String name) {
69         // Non-SCL classes are not handled here
70         if(!name.startsWith(SCL_PACKAGE_PREFIX))
71             return null;
72
73         // Determine the id of the class loader which is responsible of the class
74         String requestedModuleName = RuntimeModule.extractClassLoaderId(name);
75
76         // Is class defined locally in this class loader?
77         if(requestedModuleName.equals(basePackageName)) {
78             String internalName = name.replace('.', '/');
79             byte[] bytes = localClasses.get(internalName);
80             if(bytes != null)
81                 return bytes;
82             return localClasses.get(internalName);
83         }
84         
85         // Find suitable class loader that has this class locally
86         {
87             RuntimeModule parentModule = runtimeModuleMap.get(requestedModuleName);
88             if(parentModule == null)
89                 return null;
90
91             // Find the class from the ancestor class loader
92             return parentModule.classLoader.getBytes(name);
93         }
94     }
95     
96     private Class<?> getClass(String name) throws ClassNotFoundException {
97         //System.out.println("getClass " + name);
98         
99         // If the class is not generated from SCL, use parent class loader
100         if(!name.startsWith(SCL_PACKAGE_PREFIX)) {
101             try {
102                 return getParent().loadClass(name);
103             } catch(ClassNotFoundException e) {
104                 for(RuntimeModule module : runtimeModuleMap.values())
105                     try {
106                         return module.classLoader.getParent().loadClass(name);
107                     } catch(ClassNotFoundException e2) {
108                     }
109             }
110             throw new ClassNotFoundException(name);
111         }
112         
113         // Determine the id of the class loader which is responsible of the class
114         String requestedModuleName = RuntimeModule.extractClassLoaderId(name);
115         
116         // Is class defined locally in this class loader?
117         if(requestedModuleName.equals(basePackageName))
118             return getLocalClass(name);
119
120         // Find suitable class loader that has this class locally
121         else {
122             RuntimeModule parentModule = runtimeModuleMap.get(requestedModuleName);
123             if(parentModule == null)
124                 throw new ClassNotFoundException(name);
125
126             // Find the class from the ancestor class loader
127             return parentModule.classLoader.getLocalClass(name);
128         }
129     }
130     
131     @Override
132     public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
133         Class<?> clazz = getClass(name);
134         if (resolve)
135             resolveClass(clazz);
136         return clazz;
137     }
138     
139     public synchronized String getFreshPackageName() {
140         return basePackageName + "$" + (++transientPackageId);
141     }
142
143     @Override
144     public THashMap<Constant, Object> getConstantCache() {
145         return valueCache;
146     }
147
148     @Override
149     public ClassLoader getClassLoader() {
150         return this;
151     }
152 }