1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.expression.spel.standard;
18
19 import java.io.File;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.net.URL;
23 import java.net.URLClassLoader;
24 import java.util.Map;
25 import java.util.concurrent.atomic.AtomicInteger;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import org.springframework.asm.ClassWriter;
31 import org.springframework.asm.MethodVisitor;
32 import org.springframework.asm.Opcodes;
33 import org.springframework.expression.Expression;
34 import org.springframework.expression.spel.CodeFlow;
35 import org.springframework.expression.spel.CompiledExpression;
36 import org.springframework.expression.spel.SpelParserConfiguration;
37 import org.springframework.expression.spel.ast.SpelNodeImpl;
38 import org.springframework.util.ClassUtils;
39 import org.springframework.util.ConcurrentReferenceHashMap;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public class SpelCompiler implements Opcodes {
69
70 private static final Log logger = LogFactory.getLog(SpelCompiler.class);
71
72
73
74 private static final Map<ClassLoader, SpelCompiler> compilers =
75 new ConcurrentReferenceHashMap<ClassLoader, SpelCompiler>();
76
77
78
79 private final ChildClassLoader ccl;
80
81
82 private final AtomicInteger suffixId = new AtomicInteger(1);
83
84
85 private SpelCompiler(ClassLoader classloader) {
86 this.ccl = new ChildClassLoader(classloader);
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100 public CompiledExpression compile(SpelNodeImpl expression) {
101 if (expression.isCompilable()) {
102 if (logger.isDebugEnabled()) {
103 logger.debug("SpEL: compiling " + expression.toStringAST());
104 }
105 Class<? extends CompiledExpression> clazz = createExpressionClass(expression);
106 if (clazz != null) {
107 try {
108 return clazz.newInstance();
109 }
110 catch (Throwable ex) {
111 throw new IllegalStateException("Failed to instantiate CompiledExpression", ex);
112 }
113 }
114 }
115
116 if (logger.isDebugEnabled()) {
117 logger.debug("SpEL: unable to compile " + expression.toStringAST());
118 }
119 return null;
120 }
121
122 private int getNextSuffix() {
123 return this.suffixId.incrementAndGet();
124 }
125
126
127
128
129
130
131
132
133 @SuppressWarnings("unchecked")
134 private Class<? extends CompiledExpression> createExpressionClass(SpelNodeImpl expressionToCompile) {
135
136 String clazzName = "spel/Ex" + getNextSuffix();
137 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS|ClassWriter.COMPUTE_FRAMES);
138 cw.visit(V1_5, ACC_PUBLIC, clazzName, null, "org/springframework/expression/spel/CompiledExpression", null);
139
140
141 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
142 mv.visitCode();
143 mv.visitVarInsn(ALOAD, 0);
144 mv.visitMethodInsn(INVOKESPECIAL, "org/springframework/expression/spel/CompiledExpression",
145 "<init>", "()V", false);
146 mv.visitInsn(RETURN);
147 mv.visitMaxs(1, 1);
148 mv.visitEnd();
149
150
151 mv = cw.visitMethod(ACC_PUBLIC, "getValue",
152 "(Ljava/lang/Object;Lorg/springframework/expression/EvaluationContext;)Ljava/lang/Object;", null,
153 new String[ ]{"org/springframework/expression/EvaluationException"});
154 mv.visitCode();
155
156 CodeFlow cf = new CodeFlow(clazzName, cw);
157
158
159 try {
160 expressionToCompile.generateCode(mv, cf);
161 }
162 catch (IllegalStateException ex) {
163 if (logger.isDebugEnabled()) {
164 logger.debug(expressionToCompile.getClass().getSimpleName() +
165 ".generateCode opted out of compilation: " + ex.getMessage());
166 }
167 return null;
168 }
169
170 CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor());
171 if ("V".equals(cf.lastDescriptor())) {
172 mv.visitInsn(ACONST_NULL);
173 }
174 mv.visitInsn(ARETURN);
175
176 mv.visitMaxs(0, 0);
177 mv.visitEnd();
178 cw.visitEnd();
179
180 cf.finish();
181
182 byte[] data = cw.toByteArray();
183
184
185 return (Class<? extends CompiledExpression>) this.ccl.defineClass(clazzName.replaceAll("/", "."), data);
186 }
187
188
189
190
191
192
193
194
195
196 public static SpelCompiler getCompiler(ClassLoader classLoader) {
197 ClassLoader clToUse = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
198 synchronized (compilers) {
199 SpelCompiler compiler = compilers.get(clToUse);
200 if (compiler == null) {
201 compiler = new SpelCompiler(clToUse);
202 compilers.put(clToUse, compiler);
203 }
204 return compiler;
205 }
206 }
207
208
209
210
211
212
213
214 public static boolean compile(Expression expression) {
215 return (expression instanceof SpelExpression && ((SpelExpression) expression).compileExpression());
216 }
217
218
219
220
221
222
223 public static void revertToInterpreted(Expression expression) {
224 if (expression instanceof SpelExpression) {
225 ((SpelExpression) expression).revertToInterpreted();
226 }
227 }
228
229
230
231
232
233
234
235
236 @SuppressWarnings("unused")
237 private static void dump(String expressionText, String name, byte[] bytecode) {
238 String nameToUse = name.replace('.', '/');
239 String dir = (nameToUse.indexOf('/') != -1 ? nameToUse.substring(0, nameToUse.lastIndexOf('/')) : "");
240 String dumpLocation = null;
241 try {
242 File tempFile = File.createTempFile("tmp", null);
243 dumpLocation = tempFile + File.separator + nameToUse + ".class";
244 tempFile.delete();
245 File f = new File(tempFile, dir);
246 f.mkdirs();
247
248 if (logger.isDebugEnabled()) {
249 logger.debug("Expression '" + expressionText + "' compiled code dumped to " + dumpLocation);
250 }
251 f = new File(dumpLocation);
252 FileOutputStream fos = new FileOutputStream(f);
253 fos.write(bytecode);
254 fos.flush();
255 fos.close();
256 }
257 catch (IOException ex) {
258 throw new IllegalStateException(
259 "Unexpected problem dumping class " + nameToUse + " into " + dumpLocation, ex);
260 }
261 }
262
263
264
265
266
267 public static class ChildClassLoader extends URLClassLoader {
268
269 private static final URL[] NO_URLS = new URL[0];
270
271 public ChildClassLoader(ClassLoader classloader) {
272 super(NO_URLS, classloader);
273 }
274
275 public Class<?> defineClass(String name, byte[] bytes) {
276 return super.defineClass(name, bytes, 0, bytes.length);
277 }
278 }
279
280 }