Skip to content

Commit

Permalink
Polish SpEL internals
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrannen committed Mar 24, 2024
1 parent 8736ca0 commit 5bec072
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private enum IndexedType {ARRAY, LIST, MAP, STRING, OBJECT}
private IndexedType indexedType;

@Nullable
private String originalPrimitiveExitTypeDescriptor;
private volatile String originalPrimitiveExitTypeDescriptor;

@Nullable
private volatile String arrayTypeDescriptor;
Expand Down Expand Up @@ -373,131 +373,6 @@ public String toStringAST() {
}


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
Expand All @@ -511,16 +386,6 @@ private void setExitTypeDescriptor(String descriptor) {
}
}

@SuppressWarnings("unchecked")
private <T> T convertValue(TypeConverter converter, @Nullable Object value, Class<T> 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 {

Expand All @@ -541,7 +406,7 @@ private class ArrayIndexingValueRef implements ValueRef {

@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));
}

Expand All @@ -556,6 +421,142 @@ public void setValue(@Nullable Object newValue) {
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> T convertValue(TypeConverter converter, @Nullable Object value, Class<T> 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;
}

}


Expand Down Expand Up @@ -789,20 +790,20 @@ private void growCollectionIfNecessary() {
}
}

@Override
public boolean isWritable() {
return true;
}

@Nullable
private Constructor<?> getDefaultConstructor(Class<?> type) {
private static Constructor<?> getDefaultConstructor(Class<?> type) {
try {
return ReflectionUtils.accessibleConstructor(type);
}
catch (Throwable ex) {
return null;
}
}

@Override
public boolean isWritable() {
return true;
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
* for reading and possibly also for writing on a target instance.
*
* <p>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
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,11 @@ public void registerMethodFilter(Class<?> type, MethodFilter filter) throws Ille
*/
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;
Expand Down Expand Up @@ -425,8 +425,8 @@ private List<MethodResolver> initMethodResolvers() {
return resolvers;
}

private static <T> void addBeforeDefault(List<T> resolvers, T resolver) {
resolvers.add(resolvers.size() - 1, resolver);
private static <T> void addBeforeDefault(List<T> list, T element) {
list.add(list.size() - 1, element);
}

}

0 comments on commit 5bec072

Please sign in to comment.