From 5bec072dcb9782c4fcc6c8407d9d55463128c1ed Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Sun, 24 Mar 2024 17:07:42 +0100 Subject: [PATCH] Polish SpEL internals --- .../expression/spel/ast/Indexer.java | 287 +++++++++--------- .../support/ReflectivePropertyAccessor.java | 4 +- .../support/StandardEvaluationContext.java | 14 +- 3 files changed, 153 insertions(+), 152 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java index 5ead18c349..0e888782af 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java @@ -82,7 +82,7 @@ public class Indexer extends SpelNodeImpl { private IndexedType indexedType; @Nullable - private String originalPrimitiveExitTypeDescriptor; + private volatile String originalPrimitiveExitTypeDescriptor; @Nullable private volatile String arrayTypeDescriptor; @@ -373,131 +373,6 @@ public class Indexer extends SpelNodeImpl { } - private void setArrayElement(TypeConverter converter, Object ctx, int idx, @Nullable Object newValue, - Class arrayComponentType) throws EvaluationException { - - if (arrayComponentType == boolean.class) { - boolean[] array = (boolean[]) ctx; - checkAccess(array.length, idx); - array[idx] = convertValue(converter, newValue, boolean.class); - } - else if (arrayComponentType == byte.class) { - byte[] array = (byte[]) ctx; - checkAccess(array.length, idx); - array[idx] = convertValue(converter, newValue, byte.class); - } - else if (arrayComponentType == char.class) { - char[] array = (char[]) ctx; - checkAccess(array.length, idx); - array[idx] = convertValue(converter, newValue, char.class); - } - else if (arrayComponentType == double.class) { - double[] array = (double[]) ctx; - checkAccess(array.length, idx); - array[idx] = convertValue(converter, newValue, double.class); - } - else if (arrayComponentType == float.class) { - float[] array = (float[]) ctx; - checkAccess(array.length, idx); - array[idx] = convertValue(converter, newValue, float.class); - } - else if (arrayComponentType == int.class) { - int[] array = (int[]) ctx; - checkAccess(array.length, idx); - array[idx] = convertValue(converter, newValue, int.class); - } - else if (arrayComponentType == long.class) { - long[] array = (long[]) ctx; - checkAccess(array.length, idx); - array[idx] = convertValue(converter, newValue, long.class); - } - else if (arrayComponentType == short.class) { - short[] array = (short[]) ctx; - checkAccess(array.length, idx); - array[idx] = convertValue(converter, newValue, short.class); - } - else { - Object[] array = (Object[]) ctx; - checkAccess(array.length, idx); - array[idx] = convertValue(converter, newValue, arrayComponentType); - } - } - - private Object accessArrayElement(Object ctx, int idx) throws SpelEvaluationException { - Class arrayComponentType = ctx.getClass().componentType(); - if (arrayComponentType == boolean.class) { - boolean[] array = (boolean[]) ctx; - checkAccess(array.length, idx); - setExitTypeDescriptor("Z"); - this.arrayTypeDescriptor = "[Z"; - return array[idx]; - } - else if (arrayComponentType == byte.class) { - byte[] array = (byte[]) ctx; - checkAccess(array.length, idx); - setExitTypeDescriptor("B"); - this.arrayTypeDescriptor = "[B"; - return array[idx]; - } - else if (arrayComponentType == char.class) { - char[] array = (char[]) ctx; - checkAccess(array.length, idx); - setExitTypeDescriptor("C"); - this.arrayTypeDescriptor = "[C"; - return array[idx]; - } - else if (arrayComponentType == double.class) { - double[] array = (double[]) ctx; - checkAccess(array.length, idx); - setExitTypeDescriptor("D"); - this.arrayTypeDescriptor = "[D"; - return array[idx]; - } - else if (arrayComponentType == float.class) { - float[] array = (float[]) ctx; - checkAccess(array.length, idx); - setExitTypeDescriptor("F"); - this.arrayTypeDescriptor = "[F"; - return array[idx]; - } - else if (arrayComponentType == int.class) { - int[] array = (int[]) ctx; - checkAccess(array.length, idx); - setExitTypeDescriptor("I"); - this.arrayTypeDescriptor = "[I"; - return array[idx]; - } - else if (arrayComponentType == long.class) { - long[] array = (long[]) ctx; - checkAccess(array.length, idx); - setExitTypeDescriptor("J"); - this.arrayTypeDescriptor = "[J"; - return array[idx]; - } - else if (arrayComponentType == short.class) { - short[] array = (short[]) ctx; - checkAccess(array.length, idx); - setExitTypeDescriptor("S"); - this.arrayTypeDescriptor = "[S"; - return array[idx]; - } - else { - Object[] array = (Object[]) ctx; - checkAccess(array.length, idx); - Object retValue = array[idx]; - setExitTypeDescriptor(CodeFlow.toDescriptor(arrayComponentType)); - this.arrayTypeDescriptor = CodeFlow.toDescriptor(array.getClass()); - return retValue; - } - } - - private void checkAccess(int arrayLength, int index) throws SpelEvaluationException { - if (index >= arrayLength) { - throw new SpelEvaluationException(getStartPosition(), SpelMessage.ARRAY_INDEX_OUT_OF_BOUNDS, - arrayLength, index); - } - } - private void setExitTypeDescriptor(String descriptor) { // If this indexer would return a primitive - and yet it is also marked // null-safe - then the exit type descriptor must be promoted to the box @@ -511,16 +386,6 @@ public class Indexer extends SpelNodeImpl { } } - @SuppressWarnings("unchecked") - private T convertValue(TypeConverter converter, @Nullable Object value, Class targetType) { - T result = (T) converter.convertValue( - value, TypeDescriptor.forObject(value), TypeDescriptor.valueOf(targetType)); - if (result == null) { - throw new IllegalStateException("Null conversion result for index [" + value + "]"); - } - return result; - } - private class ArrayIndexingValueRef implements ValueRef { @@ -541,7 +406,7 @@ public class Indexer extends SpelNodeImpl { @Override public TypedValue getValue() { - Object arrayElement = accessArrayElement(this.array, this.index); + Object arrayElement = getArrayElement(this.array, this.index); return new TypedValue(arrayElement, this.typeDescriptor.elementTypeDescriptor(arrayElement)); } @@ -556,6 +421,142 @@ public class Indexer extends SpelNodeImpl { public boolean isWritable() { return true; } + + private Object getArrayElement(Object ctx, int idx) throws SpelEvaluationException { + Class arrayComponentType = ctx.getClass().componentType(); + if (arrayComponentType == boolean.class) { + boolean[] array = (boolean[]) ctx; + checkAccess(array.length, idx); + setExitTypeDescriptor("Z"); + Indexer.this.arrayTypeDescriptor = "[Z"; + return array[idx]; + } + else if (arrayComponentType == byte.class) { + byte[] array = (byte[]) ctx; + checkAccess(array.length, idx); + setExitTypeDescriptor("B"); + Indexer.this.arrayTypeDescriptor = "[B"; + return array[idx]; + } + else if (arrayComponentType == char.class) { + char[] array = (char[]) ctx; + checkAccess(array.length, idx); + setExitTypeDescriptor("C"); + Indexer.this.arrayTypeDescriptor = "[C"; + return array[idx]; + } + else if (arrayComponentType == double.class) { + double[] array = (double[]) ctx; + checkAccess(array.length, idx); + setExitTypeDescriptor("D"); + Indexer.this.arrayTypeDescriptor = "[D"; + return array[idx]; + } + else if (arrayComponentType == float.class) { + float[] array = (float[]) ctx; + checkAccess(array.length, idx); + setExitTypeDescriptor("F"); + Indexer.this.arrayTypeDescriptor = "[F"; + return array[idx]; + } + else if (arrayComponentType == int.class) { + int[] array = (int[]) ctx; + checkAccess(array.length, idx); + setExitTypeDescriptor("I"); + Indexer.this.arrayTypeDescriptor = "[I"; + return array[idx]; + } + else if (arrayComponentType == long.class) { + long[] array = (long[]) ctx; + checkAccess(array.length, idx); + setExitTypeDescriptor("J"); + Indexer.this.arrayTypeDescriptor = "[J"; + return array[idx]; + } + else if (arrayComponentType == short.class) { + short[] array = (short[]) ctx; + checkAccess(array.length, idx); + setExitTypeDescriptor("S"); + Indexer.this.arrayTypeDescriptor = "[S"; + return array[idx]; + } + else { + Object[] array = (Object[]) ctx; + checkAccess(array.length, idx); + Object retValue = array[idx]; + Indexer.this.exitTypeDescriptor = CodeFlow.toDescriptor(arrayComponentType); + Indexer.this.arrayTypeDescriptor = CodeFlow.toDescriptor(array.getClass()); + return retValue; + } + } + + private void setArrayElement(TypeConverter converter, Object ctx, int idx, @Nullable Object newValue, + Class arrayComponentType) throws EvaluationException { + + if (arrayComponentType == boolean.class) { + boolean[] array = (boolean[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, boolean.class); + } + else if (arrayComponentType == byte.class) { + byte[] array = (byte[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, byte.class); + } + else if (arrayComponentType == char.class) { + char[] array = (char[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, char.class); + } + else if (arrayComponentType == double.class) { + double[] array = (double[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, double.class); + } + else if (arrayComponentType == float.class) { + float[] array = (float[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, float.class); + } + else if (arrayComponentType == int.class) { + int[] array = (int[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, int.class); + } + else if (arrayComponentType == long.class) { + long[] array = (long[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, long.class); + } + else if (arrayComponentType == short.class) { + short[] array = (short[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, short.class); + } + else { + Object[] array = (Object[]) ctx; + checkAccess(array.length, idx); + array[idx] = convertValue(converter, newValue, arrayComponentType); + } + } + + private void checkAccess(int arrayLength, int index) throws SpelEvaluationException { + if (index >= arrayLength) { + throw new SpelEvaluationException(getStartPosition(), SpelMessage.ARRAY_INDEX_OUT_OF_BOUNDS, + arrayLength, index); + } + } + + @SuppressWarnings("unchecked") + private static T convertValue(TypeConverter converter, @Nullable Object value, Class targetType) { + T result = (T) converter.convertValue( + value, TypeDescriptor.forObject(value), TypeDescriptor.valueOf(targetType)); + if (result == null) { + throw new IllegalStateException("Null conversion result for index [" + value + "]"); + } + return result; + } + } @@ -789,8 +790,13 @@ public class Indexer extends SpelNodeImpl { } } + @Override + public boolean isWritable() { + return true; + } + @Nullable - private Constructor getDefaultConstructor(Class type) { + private static Constructor getDefaultConstructor(Class type) { try { return ReflectionUtils.accessibleConstructor(type); } @@ -798,11 +804,6 @@ public class Indexer extends SpelNodeImpl { return null; } } - - @Override - public boolean isWritable() { - return true; - } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index 0d03779441..52ca54de86 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -57,7 +57,7 @@ import org.springframework.util.StringUtils; * for reading and possibly also for writing on a target instance. * *

A property can be referenced through a public getter method (when being read) - * or a public setter method (when being written), and also as a public field. + * or a public setter method (when being written), and also through a public field. * * @author Andy Clement * @author Juergen Hoeller @@ -87,7 +87,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { /** - * Create a new property accessor for reading as well writing. + * Create a new property accessor for reading as well as writing. * @see #ReflectivePropertyAccessor(boolean) */ public ReflectivePropertyAccessor() { diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java index 5df60b3bd0..864395a887 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java @@ -381,11 +381,11 @@ public class StandardEvaluationContext implements EvaluationContext { */ public void applyDelegatesTo(StandardEvaluationContext evaluationContext) { // Triggers initialization for default delegates - evaluationContext.setConstructorResolvers(new ArrayList<>(this.getConstructorResolvers())); - evaluationContext.setMethodResolvers(new ArrayList<>(this.getMethodResolvers())); - evaluationContext.setPropertyAccessors(new ArrayList<>(this.getPropertyAccessors())); - evaluationContext.setTypeLocator(this.getTypeLocator()); - evaluationContext.setTypeConverter(this.getTypeConverter()); + evaluationContext.setConstructorResolvers(new ArrayList<>(getConstructorResolvers())); + evaluationContext.setMethodResolvers(new ArrayList<>(getMethodResolvers())); + evaluationContext.setPropertyAccessors(new ArrayList<>(getPropertyAccessors())); + evaluationContext.setTypeLocator(getTypeLocator()); + evaluationContext.setTypeConverter(getTypeConverter()); evaluationContext.beanResolver = this.beanResolver; evaluationContext.operatorOverloader = this.operatorOverloader; @@ -425,8 +425,8 @@ public class StandardEvaluationContext implements EvaluationContext { return resolvers; } - private static void addBeforeDefault(List resolvers, T resolver) { - resolvers.add(resolvers.size() - 1, resolver); + private static void addBeforeDefault(List list, T element) { + list.add(list.size() - 1, element); } }