From 4c988146bc79272181dcd1bdcc7007b08a425261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Mon, 13 Jan 2025 20:52:25 +0100 Subject: [PATCH] Specify generic type nullness in spring-expression See gh-34140 --- .../spel/ast/ConstructorReference.java | 6 +++--- .../spel/ast/FunctionReference.java | 10 +++++----- .../expression/spel/ast/MethodReference.java | 20 +++++++++---------- .../spel/support/ReflectionHelper.java | 16 ++++++++------- .../ReflectiveConstructorExecutor.java | 4 ++-- .../support/ReflectiveMethodExecutor.java | 4 ++-- 6 files changed, 31 insertions(+), 29 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java index 8196a2234bc..ba8d5ba74a9 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -124,7 +124,7 @@ public class ConstructorReference extends SpelNodeImpl { * @throws EvaluationException if there is a problem creating the object */ private TypedValue createNewInstance(ExpressionState state) throws EvaluationException { - Object[] arguments = new Object[getChildCount() - 1]; + @Nullable Object[] arguments = new Object[getChildCount() - 1]; List argumentTypes = new ArrayList<>(getChildCount() - 1); for (int i = 0; i < arguments.length; i++) { TypedValue childValue = this.children[i + 1].getValueInternal(state); @@ -359,7 +359,7 @@ public class ConstructorReference extends SpelNodeImpl { private Object createReferenceTypeArray(ExpressionState state, TypeConverter typeConverter, SpelNodeImpl[] children, Class componentType) { - Object[] array = (Object[]) Array.newInstance(componentType, children.length); + @Nullable Object[] array = (Object[]) Array.newInstance(componentType, children.length); TypeDescriptor targetType = TypeDescriptor.valueOf(componentType); for (int i = 0; i < array.length; i++) { Object value = children[i].getValue(state); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java index a054be747a6..acd2d129dbd 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -116,7 +116,7 @@ public class FunctionReference extends SpelNodeImpl { * @throws EvaluationException if there is any problem invoking the method */ private TypedValue executeFunctionViaMethod(ExpressionState state, Method method) throws EvaluationException { - Object[] functionArgs = getArguments(state); + @Nullable Object[] functionArgs = getArguments(state); if (!method.isVarArgs()) { int declaredParamCount = method.getParameterCount(); @@ -175,7 +175,7 @@ public class FunctionReference extends SpelNodeImpl { * @since 6.1 */ private TypedValue executeFunctionViaMethodHandle(ExpressionState state, MethodHandle methodHandle) throws EvaluationException { - Object[] functionArgs = getArguments(state); + @Nullable Object[] functionArgs = getArguments(state); MethodType declaredParams = methodHandle.type(); int spelParamCount = functionArgs.length; int declaredParamCount = declaredParams.parameterCount(); @@ -280,9 +280,9 @@ public class FunctionReference extends SpelNodeImpl { * Compute the arguments to the function, they are the children of this expression node. * @return an array of argument values for the function call */ - private Object[] getArguments(ExpressionState state) throws EvaluationException { + private @Nullable Object[] getArguments(ExpressionState state) throws EvaluationException { // Compute arguments to the function - Object[] arguments = new Object[getChildCount()]; + @Nullable Object[] arguments = new Object[getChildCount()]; for (int i = 0; i < arguments.length; i++) { arguments[i] = this.children[i].getValueInternal(state).getValue(); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java index 5f02f790717..51c26fa7f2f 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -90,7 +90,7 @@ public class MethodReference extends SpelNodeImpl { @Override protected ValueRef getValueRef(ExpressionState state) throws EvaluationException { - Object[] arguments = getArguments(state); + @Nullable Object[] arguments = getArguments(state); if (state.getActiveContextObject().getValue() == null) { throwIfNotNullSafe(getArgumentTypes(arguments)); return ValueRef.NullValueRef.INSTANCE; @@ -103,14 +103,14 @@ public class MethodReference extends SpelNodeImpl { EvaluationContext evaluationContext = state.getEvaluationContext(); Object value = state.getActiveContextObject().getValue(); TypeDescriptor targetType = state.getActiveContextObject().getTypeDescriptor(); - Object[] arguments = getArguments(state); + @Nullable Object[] arguments = getArguments(state); TypedValue result = getValueInternal(evaluationContext, value, targetType, arguments); updateExitTypeDescriptor(); return result; } private TypedValue getValueInternal(EvaluationContext evaluationContext, - @Nullable Object value, @Nullable TypeDescriptor targetType, Object[] arguments) { + @Nullable Object value, @Nullable TypeDescriptor targetType, @Nullable Object[] arguments) { List argumentTypes = getArgumentTypes(arguments); if (value == null) { @@ -167,8 +167,8 @@ public class MethodReference extends SpelNodeImpl { } } - private Object[] getArguments(ExpressionState state) { - Object[] arguments = new Object[getChildCount()]; + private @Nullable Object[] getArguments(ExpressionState state) { + @Nullable Object[] arguments = new Object[getChildCount()]; for (int i = 0; i < arguments.length; i++) { // Make the root object the active context again for evaluating the parameter expressions try { @@ -182,8 +182,8 @@ public class MethodReference extends SpelNodeImpl { return arguments; } - private List getArgumentTypes(Object... arguments) { - List descriptors = new ArrayList<>(arguments.length); + private List getArgumentTypes(@Nullable Object... arguments) { + List<@Nullable TypeDescriptor> descriptors = new ArrayList<>(arguments.length); for (Object argument : arguments) { descriptors.add(TypeDescriptor.forObject(argument)); } @@ -380,9 +380,9 @@ public class MethodReference extends SpelNodeImpl { private final @Nullable TypeDescriptor targetType; - private final Object[] arguments; + private final @Nullable Object[] arguments; - public MethodValueRef(ExpressionState state, Object[] arguments) { + public MethodValueRef(ExpressionState state, @Nullable Object[] arguments) { this.evaluationContext = state.getEvaluationContext(); this.value = state.getActiveContextObject().getValue(); this.targetType = state.getActiveContextObject().getTypeDescriptor(); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java index d038043d74b..863a0aebbd9 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -250,7 +250,7 @@ public abstract class ReflectionHelper { * @return {@code true} if some kind of conversion occurred on an argument * @throws SpelEvaluationException if a problem occurs during conversion */ - public static boolean convertAllArguments(TypeConverter converter, Object[] arguments, Method method) + public static boolean convertAllArguments(TypeConverter converter, @Nullable Object[] arguments, Method method) throws SpelEvaluationException { Integer varargsPosition = (method.isVarArgs() ? method.getParameterCount() - 1 : null); @@ -269,7 +269,8 @@ public abstract class ReflectionHelper { * @return {@code true} if some kind of conversion occurred on an argument * @throws EvaluationException if a problem occurs during conversion */ - static boolean convertArguments(TypeConverter converter, Object[] arguments, Executable executable, + @SuppressWarnings("NullAway") // Dataflow analysis limitation + static boolean convertArguments(TypeConverter converter, @Nullable Object[] arguments, Executable executable, @Nullable Integer varargsPosition) throws EvaluationException { boolean conversionOccurred = false; @@ -359,7 +360,8 @@ public abstract class ReflectionHelper { * @throws EvaluationException if a problem occurs during conversion * @since 6.1 */ - public static boolean convertAllMethodHandleArguments(TypeConverter converter, Object[] arguments, + @SuppressWarnings("NullAway") // Dataflow analysis limitation + public static boolean convertAllMethodHandleArguments(TypeConverter converter, @Nullable Object[] arguments, MethodHandle methodHandle, @Nullable Integer varargsPosition) throws EvaluationException { boolean conversionOccurred = false; @@ -453,7 +455,7 @@ public abstract class ReflectionHelper { * @param possibleArray an array object that may have the supplied value as the first element * @return true if the supplied value is the first entry in the array */ - private static boolean isFirstEntryInArray(Object value, @Nullable Object possibleArray) { + private static boolean isFirstEntryInArray(@Nullable Object value, @Nullable Object possibleArray) { if (possibleArray == null) { return false; } @@ -477,7 +479,7 @@ public abstract class ReflectionHelper { * @param args the arguments to be set up for the invocation * @return a repackaged array of arguments where any varargs setup has been performed */ - public static Object[] setupArgumentsForVarargsInvocation(Class[] requiredParameterTypes, Object... args) { + public static @Nullable Object[] setupArgumentsForVarargsInvocation(Class[] requiredParameterTypes, @Nullable Object... args) { Assert.notEmpty(requiredParameterTypes, "Required parameter types array must not be empty"); int parameterCount = requiredParameterTypes.length; @@ -491,7 +493,7 @@ public abstract class ReflectionHelper { // Check if repackaging is needed... if (parameterCount != argumentCount || !lastRequiredParameterType.isInstance(lastArgument)) { // Create an array for the leading arguments plus the varargs array argument. - Object[] newArgs = new Object[parameterCount]; + @Nullable Object[] newArgs = new Object[parameterCount]; // Copy all leading arguments to the new array, omitting the varargs array argument. System.arraycopy(args, 0, newArgs, 0, newArgs.length - 1); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java index b2122be8886..6e4d23f4d4e 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,7 +52,7 @@ public class ReflectiveConstructorExecutor implements ConstructorExecutor { } @Override - public TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException { + public TypedValue execute(EvaluationContext context, @Nullable Object... arguments) throws AccessException { try { ReflectionHelper.convertArguments( context.getTypeConverter(), arguments, this.ctor, this.varargsPosition); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java index e4addc87622..759139d6d43 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -102,7 +102,7 @@ public class ReflectiveMethodExecutor implements MethodExecutor { @Override - public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException { + public TypedValue execute(EvaluationContext context, Object target, @Nullable Object... arguments) throws AccessException { try { this.argumentConversionOccurred = ReflectionHelper.convertArguments( context.getTypeConverter(), arguments, this.originalMethod, this.varargsPosition);