From 1762bf4a60ebf8e88959fcf58b878a80581f7327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Sat, 28 Oct 2023 10:52:15 +0200 Subject: [PATCH] Fix code generation for null indexed argument value This commit fixes code generation when an indexed constructor argument value is null as the method is overloaded and need the value to be cast to `Object`. Closes gh-31508 --- .../BeanDefinitionPropertiesCodeGenerator.java | 18 +++++++++++++++++- ...DefinitionPropertiesCodeGeneratorTests.java | 16 +++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java index fce5383e7c1..a0133f88faf 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java @@ -174,7 +174,9 @@ class BeanDefinitionPropertiesCodeGenerator { Map indexedValues = constructorValues.getIndexedArgumentValues(); if (!indexedValues.isEmpty()) { indexedValues.forEach((index, valueHolder) -> { - CodeBlock valueCode = generateValue(valueHolder.getName(), valueHolder.getValue()); + Object value = valueHolder.getValue(); + CodeBlock valueCode = castIfNecessary(value == null, Object.class, + generateValue(valueHolder.getName(), value)); code.addStatement( "$L.getConstructorArgumentValues().addIndexedArgumentValue($L, $L)", BEAN_DEFINITION_VARIABLE, index, valueCode); @@ -346,6 +348,20 @@ class BeanDefinitionPropertiesCodeGenerator { } } + /** + * Cast the specified {@code valueCode} to the specified {@code castType} if + * the {@code castNecessary} is {@code true}. Otherwise return the valueCode + * as is. + * @param castNecessary whether a cast is necessary + * @param castType the type to cast to + * @param valueCode the code for the value + * @return the existing value or a form of {@code (CastType) valueCode} if a + * cast is necessary + */ + private CodeBlock castIfNecessary(boolean castNecessary, Class castType, CodeBlock valueCode) { + return (castNecessary ? CodeBlock.of("($T) $L", castType, valueCode) : valueCode); + } + static class PropertyNamesStack { private static final ThreadLocal> threadLocal = ThreadLocal.withInitial(ArrayDeque::new); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java index 8c736e25edf..bd58567226c 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java @@ -235,6 +235,18 @@ class BeanDefinitionPropertiesCodeGeneratorTests { }); } + @Test + void constructorArgumentValuesWhenIndexedNullValue() { + this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null); + compile((actual, compiled) -> { + ConstructorArgumentValues argumentValues = actual.getConstructorArgumentValues(); + Map values = argumentValues.getIndexedArgumentValues(); + assertThat(values.get(0)).satisfies(assertValueHolder(null, null, null)); + assertThat(values).hasSize(1); + assertThat(argumentValues.getGenericArgumentValues()).isEmpty(); + }); + } + @Test void constructorArgumentValuesWhenGenericValuesWithName() { this.beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(String.class); @@ -255,7 +267,9 @@ class BeanDefinitionPropertiesCodeGeneratorTests { }); } - private Consumer assertValueHolder(Object value, @Nullable Class type, @Nullable String name) { + private Consumer assertValueHolder( + @Nullable Object value, @Nullable Class type, @Nullable String name) { + return valueHolder -> { assertThat(valueHolder.getValue()).isEqualTo(value); assertThat(valueHolder.getType()).isEqualTo((type != null ? type.getName() : null));