1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.expression.spel.ast;
18
19 import java.lang.reflect.Array;
20 import java.lang.reflect.Constructor;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Modifier;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import org.springframework.asm.MethodVisitor;
27 import org.springframework.core.convert.TypeDescriptor;
28 import org.springframework.expression.AccessException;
29 import org.springframework.expression.ConstructorExecutor;
30 import org.springframework.expression.ConstructorResolver;
31 import org.springframework.expression.EvaluationContext;
32 import org.springframework.expression.EvaluationException;
33 import org.springframework.expression.TypeConverter;
34 import org.springframework.expression.TypedValue;
35 import org.springframework.expression.common.ExpressionUtils;
36 import org.springframework.expression.spel.CodeFlow;
37 import org.springframework.expression.spel.ExpressionState;
38 import org.springframework.expression.spel.SpelEvaluationException;
39 import org.springframework.expression.spel.SpelMessage;
40 import org.springframework.expression.spel.SpelNode;
41 import org.springframework.expression.spel.support.ReflectiveConstructorExecutor;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public class ConstructorReference extends SpelNodeImpl {
57
58 private boolean isArrayConstructor = false;
59
60 private SpelNodeImpl[] dimensions;
61
62
63
64
65
66 private volatile ConstructorExecutor cachedExecutor;
67
68
69
70
71
72
73 public ConstructorReference(int pos, SpelNodeImpl... arguments) {
74 super(pos, arguments);
75 this.isArrayConstructor = false;
76 }
77
78
79
80
81
82 public ConstructorReference(int pos, SpelNodeImpl[] dimensions, SpelNodeImpl... arguments) {
83 super(pos, arguments);
84 this.isArrayConstructor = true;
85 this.dimensions = dimensions;
86 }
87
88
89
90
91
92 @Override
93 public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
94 if (this.isArrayConstructor) {
95 return createArray(state);
96 }
97 else {
98 return createNewInstance(state);
99 }
100 }
101
102
103
104
105
106
107
108 private TypedValue createNewInstance(ExpressionState state) throws EvaluationException {
109 Object[] arguments = new Object[getChildCount() - 1];
110 List<TypeDescriptor> argumentTypes = new ArrayList<TypeDescriptor>(getChildCount() - 1);
111 for (int i = 0; i < arguments.length; i++) {
112 TypedValue childValue = this.children[i + 1].getValueInternal(state);
113 Object value = childValue.getValue();
114 arguments[i] = value;
115 argumentTypes.add(TypeDescriptor.forObject(value));
116 }
117
118 ConstructorExecutor executorToUse = this.cachedExecutor;
119 if (executorToUse != null) {
120 try {
121 return executorToUse.execute(state.getEvaluationContext(), arguments);
122 }
123 catch (AccessException ex) {
124
125
126
127
128
129
130
131
132
133
134 if (ex.getCause() instanceof InvocationTargetException) {
135
136 Throwable rootCause = ex.getCause().getCause();
137 if (rootCause instanceof RuntimeException) {
138 throw (RuntimeException) rootCause;
139 }
140 else {
141 String typeName = (String) this.children[0].getValueInternal(state).getValue();
142 throw new SpelEvaluationException(getStartPosition(), rootCause,
143 SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName,
144 FormatHelper.formatMethodForMessage("", argumentTypes));
145 }
146 }
147
148
149 this.cachedExecutor = null;
150 }
151 }
152
153
154 String typeName = (String) this.children[0].getValueInternal(state).getValue();
155 executorToUse = findExecutorForConstructor(typeName, argumentTypes, state);
156 try {
157 this.cachedExecutor = executorToUse;
158 if (this.cachedExecutor instanceof ReflectiveConstructorExecutor) {
159 this.exitTypeDescriptor = CodeFlow.toDescriptor(
160 ((ReflectiveConstructorExecutor) this.cachedExecutor).getConstructor().getDeclaringClass());
161
162 }
163 return executorToUse.execute(state.getEvaluationContext(), arguments);
164 }
165 catch (AccessException ex) {
166 throw new SpelEvaluationException(getStartPosition(), ex,
167 SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName,
168 FormatHelper.formatMethodForMessage("", argumentTypes));
169 }
170 }
171
172
173
174
175
176
177
178
179
180
181 private ConstructorExecutor findExecutorForConstructor(String typeName,
182 List<TypeDescriptor> argumentTypes, ExpressionState state)
183 throws SpelEvaluationException {
184
185 EvaluationContext evalContext = state.getEvaluationContext();
186 List<ConstructorResolver> ctorResolvers = evalContext.getConstructorResolvers();
187 if (ctorResolvers != null) {
188 for (ConstructorResolver ctorResolver : ctorResolvers) {
189 try {
190 ConstructorExecutor ce = ctorResolver.resolve(state.getEvaluationContext(), typeName, argumentTypes);
191 if (ce != null) {
192 return ce;
193 }
194 }
195 catch (AccessException ex) {
196 throw new SpelEvaluationException(getStartPosition(), ex,
197 SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typeName,
198 FormatHelper.formatMethodForMessage("", argumentTypes));
199 }
200 }
201 }
202 throw new SpelEvaluationException(getStartPosition(), SpelMessage.CONSTRUCTOR_NOT_FOUND, typeName,
203 FormatHelper.formatMethodForMessage("", argumentTypes));
204 }
205
206 @Override
207 public String toStringAST() {
208 StringBuilder sb = new StringBuilder("new ");
209 int index = 0;
210 sb.append(getChild(index++).toStringAST());
211 sb.append("(");
212 for (int i = index; i < getChildCount(); i++) {
213 if (i > index) {
214 sb.append(",");
215 }
216 sb.append(getChild(i).toStringAST());
217 }
218 sb.append(")");
219 return sb.toString();
220 }
221
222
223
224
225
226
227
228 private TypedValue createArray(ExpressionState state) throws EvaluationException {
229
230 Object intendedArrayType = getChild(0).getValue(state);
231 if (!(intendedArrayType instanceof String)) {
232 throw new SpelEvaluationException(getChild(0).getStartPosition(),
233 SpelMessage.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION,
234 FormatHelper.formatClassNameForMessage(intendedArrayType.getClass()));
235 }
236 String type = (String) intendedArrayType;
237 Class<?> componentType;
238 TypeCode arrayTypeCode = TypeCode.forName(type);
239 if (arrayTypeCode == TypeCode.OBJECT) {
240 componentType = state.findType(type);
241 }
242 else {
243 componentType = arrayTypeCode.getType();
244 }
245 Object newArray;
246 if (!hasInitializer()) {
247
248 for (SpelNodeImpl dimension : this.dimensions) {
249 if (dimension == null) {
250 throw new SpelEvaluationException(getStartPosition(), SpelMessage.MISSING_ARRAY_DIMENSION);
251 }
252 }
253 TypeConverter typeConverter = state.getEvaluationContext().getTypeConverter();
254
255
256 if (this.dimensions.length == 1) {
257 TypedValue o = this.dimensions[0].getTypedValue(state);
258 int arraySize = ExpressionUtils.toInt(typeConverter, o);
259 newArray = Array.newInstance(componentType, arraySize);
260 }
261 else {
262
263 int[] dims = new int[this.dimensions.length];
264 for (int d = 0; d < this.dimensions.length; d++) {
265 TypedValue o = this.dimensions[d].getTypedValue(state);
266 dims[d] = ExpressionUtils.toInt(typeConverter, o);
267 }
268 newArray = Array.newInstance(componentType, dims);
269 }
270 }
271 else {
272
273 if (this.dimensions.length > 1) {
274
275
276 throw new SpelEvaluationException(getStartPosition(),
277 SpelMessage.MULTIDIM_ARRAY_INITIALIZER_NOT_SUPPORTED);
278 }
279 TypeConverter typeConverter = state.getEvaluationContext().getTypeConverter();
280 InlineList initializer = (InlineList) getChild(1);
281
282 if (this.dimensions[0] != null) {
283 TypedValue dValue = this.dimensions[0].getTypedValue(state);
284 int i = ExpressionUtils.toInt(typeConverter, dValue);
285 if (i != initializer.getChildCount()) {
286 throw new SpelEvaluationException(getStartPosition(), SpelMessage.INITIALIZER_LENGTH_INCORRECT);
287 }
288 }
289
290 int arraySize = initializer.getChildCount();
291 newArray = Array.newInstance(componentType, arraySize);
292 if (arrayTypeCode == TypeCode.OBJECT) {
293 populateReferenceTypeArray(state, newArray, typeConverter, initializer, componentType);
294 }
295 else if (arrayTypeCode == TypeCode.INT) {
296 populateIntArray(state, newArray, typeConverter, initializer);
297 }
298 else if (arrayTypeCode == TypeCode.BOOLEAN) {
299 populateBooleanArray(state, newArray, typeConverter, initializer);
300 }
301 else if (arrayTypeCode == TypeCode.CHAR) {
302 populateCharArray(state, newArray, typeConverter, initializer);
303 }
304 else if (arrayTypeCode == TypeCode.LONG) {
305 populateLongArray(state, newArray, typeConverter, initializer);
306 }
307 else if (arrayTypeCode == TypeCode.SHORT) {
308 populateShortArray(state, newArray, typeConverter, initializer);
309 }
310 else if (arrayTypeCode == TypeCode.DOUBLE) {
311 populateDoubleArray(state, newArray, typeConverter, initializer);
312 }
313 else if (arrayTypeCode == TypeCode.FLOAT) {
314 populateFloatArray(state, newArray, typeConverter, initializer);
315 }
316 else if (arrayTypeCode == TypeCode.BYTE) {
317 populateByteArray(state, newArray, typeConverter, initializer);
318 }
319 else {
320 throw new IllegalStateException(arrayTypeCode.name());
321 }
322 }
323 return new TypedValue(newArray);
324 }
325
326 private void populateReferenceTypeArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
327 InlineList initializer, Class<?> componentType) {
328
329 TypeDescriptor toTypeDescriptor = TypeDescriptor.valueOf(componentType);
330 Object[] newObjectArray = (Object[]) newArray;
331 for (int i = 0; i < newObjectArray.length; i++) {
332 SpelNode elementNode = initializer.getChild(i);
333 Object arrayEntry = elementNode.getValue(state);
334 newObjectArray[i] = typeConverter.convertValue(arrayEntry,
335 TypeDescriptor.forObject(arrayEntry), toTypeDescriptor);
336 }
337 }
338
339 private void populateByteArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
340 InlineList initializer) {
341
342 byte[] newByteArray = (byte[]) newArray;
343 for (int i = 0; i < newByteArray.length; i++) {
344 TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
345 newByteArray[i] = ExpressionUtils.toByte(typeConverter, typedValue);
346 }
347 }
348
349 private void populateFloatArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
350 InlineList initializer) {
351
352 float[] newFloatArray = (float[]) newArray;
353 for (int i = 0; i < newFloatArray.length; i++) {
354 TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
355 newFloatArray[i] = ExpressionUtils.toFloat(typeConverter, typedValue);
356 }
357 }
358
359 private void populateDoubleArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
360 InlineList initializer) {
361
362 double[] newDoubleArray = (double[]) newArray;
363 for (int i = 0; i < newDoubleArray.length; i++) {
364 TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
365 newDoubleArray[i] = ExpressionUtils.toDouble(typeConverter, typedValue);
366 }
367 }
368
369 private void populateShortArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
370 InlineList initializer) {
371
372 short[] newShortArray = (short[]) newArray;
373 for (int i = 0; i < newShortArray.length; i++) {
374 TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
375 newShortArray[i] = ExpressionUtils.toShort(typeConverter, typedValue);
376 }
377 }
378
379 private void populateLongArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
380 InlineList initializer) {
381
382 long[] newLongArray = (long[]) newArray;
383 for (int i = 0; i < newLongArray.length; i++) {
384 TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
385 newLongArray[i] = ExpressionUtils.toLong(typeConverter, typedValue);
386 }
387 }
388
389 private void populateCharArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
390 InlineList initializer) {
391
392 char[] newCharArray = (char[]) newArray;
393 for (int i = 0; i < newCharArray.length; i++) {
394 TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
395 newCharArray[i] = ExpressionUtils.toChar(typeConverter, typedValue);
396 }
397 }
398
399 private void populateBooleanArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
400 InlineList initializer) {
401
402 boolean[] newBooleanArray = (boolean[]) newArray;
403 for (int i = 0; i < newBooleanArray.length; i++) {
404 TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
405 newBooleanArray[i] = ExpressionUtils.toBoolean(typeConverter, typedValue);
406 }
407 }
408
409 private void populateIntArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
410 InlineList initializer) {
411
412 int[] newIntArray = (int[]) newArray;
413 for (int i = 0; i < newIntArray.length; i++) {
414 TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
415 newIntArray[i] = ExpressionUtils.toInt(typeConverter, typedValue);
416 }
417 }
418
419 private boolean hasInitializer() {
420 return (getChildCount() > 1);
421 }
422
423 @Override
424 public boolean isCompilable() {
425 if (!(this.cachedExecutor instanceof ReflectiveConstructorExecutor) ||
426 this.exitTypeDescriptor == null) {
427 return false;
428 }
429
430 if (getChildCount() > 1) {
431 for (int c = 1, max = getChildCount();c < max; c++) {
432 if (!this.children[c].isCompilable()) {
433 return false;
434 }
435 }
436 }
437
438 ReflectiveConstructorExecutor executor = (ReflectiveConstructorExecutor) this.cachedExecutor;
439 Constructor<?> constructor = executor.getConstructor();
440 return (Modifier.isPublic(constructor.getModifiers()) &&
441 Modifier.isPublic(constructor.getDeclaringClass().getModifiers()));
442 }
443
444 @Override
445 public void generateCode(MethodVisitor mv, CodeFlow cf) {
446 ReflectiveConstructorExecutor executor = ((ReflectiveConstructorExecutor) this.cachedExecutor);
447 Constructor<?> constructor = executor.getConstructor();
448 String classSlashedDescriptor = constructor.getDeclaringClass().getName().replace('.', '/');
449 mv.visitTypeInsn(NEW, classSlashedDescriptor);
450 mv.visitInsn(DUP);
451
452 SpelNodeImpl[] arguments = new SpelNodeImpl[children.length-1];
453 System.arraycopy(children, 1, arguments, 0, children.length-1);
454 generateCodeForArguments(mv, cf, constructor, arguments);
455 mv.visitMethodInsn(INVOKESPECIAL, classSlashedDescriptor, "<init>",
456 CodeFlow.createSignatureDescriptor(constructor), false);
457 cf.pushDescriptor(this.exitTypeDescriptor);
458 }
459
460 }