Browse Source

Use consistent registration API in TypeHint.Builder

This commit adapts the registration of fields, constructors, and methods
to provide the same convenience than the reflection-based one available
in ReflectionHints.

See gh-29011
pull/29012/head
Stephane Nicoll 3 years ago
parent
commit
7ca57b7e80
  1. 32
      spring-core-test/src/test/java/org/springframework/aot/agent/InstrumentedMethodTests.java
  2. 54
      spring-core/src/main/java/org/springframework/aot/hint/TypeHint.java
  3. 167
      spring-core/src/test/java/org/springframework/aot/hint/TypeHintTests.java
  4. 39
      spring-core/src/test/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicatesTests.java
  5. 8
      spring-core/src/test/java/org/springframework/aot/nativex/FileNativeConfigurationWriterTests.java
  6. 46
      spring-core/src/test/java/org/springframework/aot/nativex/ReflectionHintsWriterTests.java

32
spring-core-test/src/test/java/org/springframework/aot/agent/InstrumentedMethodTests.java

@ -151,15 +151,15 @@ class InstrumentedMethodTests {
@Test @Test
void classGetConstructorShouldMatchInstrospectConstructorHint() { void classGetConstructorShouldMatchInstrospectConstructorHint() {
hints.reflection().registerType(String.class, typeHint -> typeHint.withConstructor(Collections.emptyList(), hints.reflection().registerType(String.class,typeHint ->
constructorHint -> constructorHint.withMode(ExecutableMode.INTROSPECT))); typeHint.withConstructor(Collections.emptyList(), ExecutableMode.INTROSPECT));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor);
} }
@Test @Test
void classGetConstructorShouldMatchInvokeConstructorHint() { void classGetConstructorShouldMatchInvokeConstructorHint() {
hints.reflection().registerType(String.class, typeHint -> typeHint.withConstructor(Collections.emptyList(), hints.reflection().registerType(String.class, typeHint ->
constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE))); typeHint.withConstructor(Collections.emptyList(),ExecutableMode.INVOKE));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor);
} }
@ -201,15 +201,15 @@ class InstrumentedMethodTests {
@Test @Test
void classGetDeclaredConstructorShouldMatchInstrospectConstructorHint() { void classGetDeclaredConstructorShouldMatchInstrospectConstructorHint() {
hints.reflection().registerType(String.class, typeHint -> typeHint.withConstructor(TypeReference.listOf(byte[].class, byte.class), hints.reflection().registerType(String.class, typeHint ->
constructorHint -> constructorHint.withMode(ExecutableMode.INTROSPECT))); typeHint.withConstructor(TypeReference.listOf(byte[].class, byte.class), ExecutableMode.INTROSPECT));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor);
} }
@Test @Test
void classGetDeclaredConstructorShouldMatchInvokeConstructorHint() { void classGetDeclaredConstructorShouldMatchInvokeConstructorHint() {
hints.reflection().registerType(String.class, typeHint -> typeHint.withConstructor(TypeReference.listOf(byte[].class, byte.class), hints.reflection().registerType(String.class, typeHint ->
constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE))); typeHint.withConstructor(TypeReference.listOf(byte[].class, byte.class), ExecutableMode.INVOKE));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor);
} }
@ -237,7 +237,7 @@ class InstrumentedMethodTests {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE) RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE)
.onInstance(String.class.getConstructor()).returnValue("").build(); .onInstance(String.class.getConstructor()).returnValue("").build();
hints.reflection().registerType(String.class, typeHint -> hints.reflection().registerType(String.class, typeHint ->
typeHint.withConstructor(Collections.emptyList(), constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE))); typeHint.withConstructor(Collections.emptyList(), ExecutableMode.INVOKE));
assertThatInvocationMatches(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE, invocation); assertThatInvocationMatches(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE, invocation);
} }
@ -246,7 +246,7 @@ class InstrumentedMethodTests {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE) RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE)
.onInstance(String.class.getConstructor()).returnValue("").build(); .onInstance(String.class.getConstructor()).returnValue("").build();
hints.reflection().registerType(String.class, typeHint -> hints.reflection().registerType(String.class, typeHint ->
typeHint.withConstructor(Collections.emptyList(), constructorHint -> constructorHint.withMode(ExecutableMode.INTROSPECT))); typeHint.withConstructor(Collections.emptyList(), ExecutableMode.INTROSPECT));
assertThatInvocationDoesNotMatch(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE, invocation); assertThatInvocationDoesNotMatch(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE, invocation);
} }
@ -285,7 +285,7 @@ class InstrumentedMethodTests {
void classGetDeclaredMethodShouldMatchIntrospectMethodHint() { void classGetDeclaredMethodShouldMatchIntrospectMethodHint() {
List<TypeReference> parameterTypes = TypeReference.listOf(int.class, float.class); List<TypeReference> parameterTypes = TypeReference.listOf(int.class, float.class);
hints.reflection().registerType(String.class, typeHint -> hints.reflection().registerType(String.class, typeHint ->
typeHint.withMethod("scale", parameterTypes, methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT))); typeHint.withMethod("scale", parameterTypes, ExecutableMode.INTROSPECT));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod);
} }
@ -293,7 +293,7 @@ class InstrumentedMethodTests {
void classGetDeclaredMethodShouldMatchInvokeMethodHint() { void classGetDeclaredMethodShouldMatchInvokeMethodHint() {
List<TypeReference> parameterTypes = TypeReference.listOf(int.class, float.class); List<TypeReference> parameterTypes = TypeReference.listOf(int.class, float.class);
hints.reflection().registerType(String.class, typeHint -> hints.reflection().registerType(String.class, typeHint ->
typeHint.withMethod("scale", parameterTypes, methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); typeHint.withMethod("scale", parameterTypes, ExecutableMode.INVOKE));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod);
} }
@ -378,14 +378,14 @@ class InstrumentedMethodTests {
@Test @Test
void classGetMethodShouldMatchIntrospectMethodHint() { void classGetMethodShouldMatchIntrospectMethodHint() {
hints.reflection().registerType(String.class, typeHint -> hints.reflection().registerType(String.class, typeHint ->
typeHint.withMethod("toString", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT))); typeHint.withMethod("toString", Collections.emptyList(), ExecutableMode.INTROSPECT));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod);
} }
@Test @Test
void classGetMethodShouldMatchInvokeMethodHint() { void classGetMethodShouldMatchInvokeMethodHint() {
hints.reflection().registerType(String.class, typeHint -> hints.reflection().registerType(String.class, typeHint ->
typeHint.withMethod("toString", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); typeHint.withMethod("toString", Collections.emptyList(), ExecutableMode.INVOKE));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod);
} }
@ -412,7 +412,7 @@ class InstrumentedMethodTests {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_INVOKE) RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_INVOKE)
.onInstance(String.class.getMethod("startsWith", String.class)).withArguments("testString", new Object[] { "test" }).build(); .onInstance(String.class.getMethod("startsWith", String.class)).withArguments("testString", new Object[] { "test" }).build();
hints.reflection().registerType(String.class, typeHint -> typeHint.withMethod("startsWith", hints.reflection().registerType(String.class, typeHint -> typeHint.withMethod("startsWith",
TypeReference.listOf(String.class), methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); TypeReference.listOf(String.class), ExecutableMode.INVOKE));
assertThatInvocationMatches(InstrumentedMethod.METHOD_INVOKE, invocation); assertThatInvocationMatches(InstrumentedMethod.METHOD_INVOKE, invocation);
} }
@ -421,7 +421,7 @@ class InstrumentedMethodTests {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_INVOKE) RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_INVOKE)
.onInstance(String.class.getMethod("toString")).withArguments("", new Object[0]).build(); .onInstance(String.class.getMethod("toString")).withArguments("", new Object[0]).build();
hints.reflection().registerType(String.class, typeHint -> hints.reflection().registerType(String.class, typeHint ->
typeHint.withMethod("toString", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT))); typeHint.withMethod("toString", Collections.emptyList(), ExecutableMode.INTROSPECT));
assertThatInvocationDoesNotMatch(InstrumentedMethod.METHOD_INVOKE, invocation); assertThatInvocationDoesNotMatch(InstrumentedMethod.METHOD_INVOKE, invocation);
} }

54
spring-core/src/main/java/org/springframework/aot/hint/TypeHint.java

@ -173,6 +173,16 @@ public final class TypeHint implements ConditionalHint {
return this; return this;
} }
/**
* Register the need for reflection on the field with the specified name,
* enabling write access.
* @param name the name of the field
* @return {@code this}, to facilitate method chaining
*/
public Builder withField(String name) {
return withField(name, fieldHint -> {});
}
/** /**
* Register the need for reflection on the constructor with the specified * Register the need for reflection on the constructor with the specified
* parameter types. * parameter types.
@ -189,6 +199,27 @@ public final class TypeHint implements ConditionalHint {
return this; return this;
} }
/**
* Register the need for reflection on the constructor with the specified
* parameter types, using the specified {@link ExecutableMode}.
* @param parameterTypes the parameter types of the constructor
* @param mode the requested mode
* @return {@code this}, to facilitate method chaining
*/
public Builder withConstructor(List<TypeReference> parameterTypes, ExecutableMode mode) {
return withConstructor(parameterTypes, constructorHint -> constructorHint.withMode(mode));
}
/**
* Register the need for reflection on the constructor with the specified
* parameter types, enabling {@link ExecutableMode#INVOKE}.
* @param parameterTypes the parameter types of the constructor
* @return {@code this}, to facilitate method chaining
*/
public Builder withConstructor(List<TypeReference> parameterTypes) {
return withConstructor(parameterTypes, ExecutableMode.INVOKE);
}
/** /**
* Register the need for reflection on the method with the specified name * Register the need for reflection on the method with the specified name
* and parameter types. * and parameter types.
@ -205,6 +236,29 @@ public final class TypeHint implements ConditionalHint {
return this; return this;
} }
/**
* Register the need for reflection on the method with the specified name
* and parameter types, using the specified {@link ExecutableMode}.
* @param name the name of the method
* @param parameterTypes the parameter types of the constructor
* @param mode the requested mode
* @return {@code this}, to facilitate method chaining
*/
public Builder withMethod(String name, List<TypeReference> parameterTypes, ExecutableMode mode) {
return withMethod(name, parameterTypes, methodHint -> methodHint.withMode(mode));
}
/**
* Register the need for reflection on the method with the specified name
* and parameter types, enabling {@link ExecutableMode#INVOKE}.
* @param name the name of the method
* @param parameterTypes the parameter types of the constructor
* @return {@code this}, to facilitate method chaining
*/
public Builder withMethod(String name, List<TypeReference> parameterTypes) {
return withMethod(name, parameterTypes, ExecutableMode.INVOKE);
}
/** /**
* Adds the specified {@linkplain MemberCategory member categories}. * Adds the specified {@linkplain MemberCategory member categories}.
* @param memberCategories the categories to apply * @param memberCategories the categories to apply

167
spring-core/src/test/java/org/springframework/aot/hint/TypeHintTests.java

@ -17,6 +17,7 @@
package org.springframework.aot.hint; package org.springframework.aot.hint;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -54,16 +55,38 @@ class TypeHintTests {
} }
@Test @Test
void createWithField() { void createWithFieldAllowsWriteByDefault() {
TypeHint hint = TypeHint.of(TypeReference.of(String.class)) assertFieldHint(TypeHint.of(TypeReference.of(String.class))
.withField("value", fieldHint -> fieldHint.allowWrite(true)).build(); .withField("value"), fieldHint -> {
assertThat(hint.fields()).singleElement().satisfies(fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("value"); assertThat(fieldHint.getName()).isEqualTo("value");
assertThat(fieldHint.isAllowWrite()).isTrue(); assertThat(fieldHint.isAllowWrite()).isTrue();
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse(); assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
}); });
} }
@Test
void createWithFieldAndEmptyCustomizerAppliesConsistentDefault() {
assertFieldHint(TypeHint.of(TypeReference.of(String.class))
.withField("value", fieldHint -> {}), fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("value");
assertThat(fieldHint.isAllowWrite()).isTrue();
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
});
}
@Test
void createWithFieldAndCustomizerAppliesCustomization() {
assertFieldHint(TypeHint.of(TypeReference.of(String.class))
.withField("value", fieldHint -> {
fieldHint.allowWrite(false);
fieldHint.allowUnsafeAccess(true);
}), fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("value");
assertThat(fieldHint.isAllowWrite()).isFalse();
assertThat(fieldHint.isAllowUnsafeAccess()).isTrue();
});
}
@Test @Test
void createWithFieldReuseBuilder() { void createWithFieldReuseBuilder() {
Builder builder = TypeHint.of(TypeReference.of(String.class)); Builder builder = TypeHint.of(TypeReference.of(String.class));
@ -72,33 +95,69 @@ class TypeHintTests {
fieldHint.allowWrite(true); fieldHint.allowWrite(true);
fieldHint.allowUnsafeAccess(false); fieldHint.allowUnsafeAccess(false);
}); });
TypeHint hint = builder.build(); assertFieldHint(builder, fieldHint -> {
assertThat(hint.fields()).singleElement().satisfies(fieldHint -> {
assertThat(fieldHint.getName()).isEqualTo("value"); assertThat(fieldHint.getName()).isEqualTo("value");
assertThat(fieldHint.isAllowWrite()).isTrue(); assertThat(fieldHint.isAllowWrite()).isTrue();
assertThat(fieldHint.isAllowUnsafeAccess()).isFalse(); assertThat(fieldHint.isAllowUnsafeAccess()).isFalse();
}); });
} }
void assertFieldHint(Builder builder, Consumer<FieldHint> fieldHint) {
TypeHint hint = builder.build();
assertThat(hint.fields()).singleElement().satisfies(fieldHint);
assertThat(hint.constructors()).isEmpty();
assertThat(hint.methods()).isEmpty();
assertThat(hint.getMemberCategories()).isEmpty();
}
@Test @Test
void createWithConstructor() { void createWithConstructor() {
List<TypeReference> parameterTypes = TypeReference.listOf(byte[].class, int.class); List<TypeReference> parameterTypes = TypeReference.listOf(byte[].class, int.class);
TypeHint hint = TypeHint.of(TypeReference.of(String.class)).withConstructor(parameterTypes, assertConstructorHint(TypeHint.of(TypeReference.of(String.class))
constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE)).build(); .withConstructor(parameterTypes), constructorHint -> {
assertThat(hint.constructors()).singleElement().satisfies(constructorHint -> {
assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes); assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes);
assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE);
}); });
} }
@Test
void createWithConstructorAndMode() {
List<TypeReference> parameterTypes = TypeReference.listOf(byte[].class, int.class);
assertConstructorHint(TypeHint.of(TypeReference.of(String.class))
.withConstructor(parameterTypes, ExecutableMode.INTROSPECT), constructorHint -> {
assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes);
assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT);
});
}
@Test
void createWithConstructorAndEmptyCustomizerAppliesConsistentDefault() {
List<TypeReference> parameterTypes = TypeReference.listOf(byte[].class, int.class);
assertConstructorHint(TypeHint.of(TypeReference.of(String.class))
.withConstructor(parameterTypes, constructorHint -> {}), constructorHint -> {
assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes);
assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE);
});
}
@Test
void createWithConstructorAndCustomizerAppliesCustomization() {
List<TypeReference> parameterTypes = TypeReference.listOf(byte[].class, int.class);
assertConstructorHint(TypeHint.of(TypeReference.of(String.class))
.withConstructor(parameterTypes, constructorHint ->
constructorHint.withMode(ExecutableMode.INTROSPECT)), constructorHint -> {
assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes);
assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT);
});
}
@Test @Test
void createConstructorReuseBuilder() { void createConstructorReuseBuilder() {
List<TypeReference> parameterTypes = TypeReference.listOf(byte[].class, int.class); List<TypeReference> parameterTypes = TypeReference.listOf(byte[].class, int.class);
Builder builder = TypeHint.of(TypeReference.of(String.class)).withConstructor(parameterTypes, Builder builder = TypeHint.of(TypeReference.of(String.class))
constructorHint -> constructorHint.withMode(ExecutableMode.INTROSPECT)); .withConstructor(parameterTypes, ExecutableMode.INTROSPECT);
TypeHint hint = builder.withConstructor(parameterTypes, constructorHint -> assertConstructorHint(builder.withConstructor(parameterTypes, constructorHint ->
constructorHint.withMode(ExecutableMode.INVOKE)).build(); constructorHint.withMode(ExecutableMode.INVOKE)), constructorHint -> {
assertThat(hint.constructors()).singleElement().satisfies(constructorHint -> {
assertThat(constructorHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); assertThat(constructorHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE);
}); });
@ -109,34 +168,74 @@ class TypeHintTests {
List<TypeReference> parameterTypes = TypeReference.listOf(byte[].class, int.class); List<TypeReference> parameterTypes = TypeReference.listOf(byte[].class, int.class);
Builder builder = TypeHint.of(TypeReference.of(String.class)).withConstructor(parameterTypes, Builder builder = TypeHint.of(TypeReference.of(String.class)).withConstructor(parameterTypes,
constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE)); constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE));
TypeHint hint = builder.withConstructor(parameterTypes, constructorHint -> assertConstructorHint(builder.withConstructor(parameterTypes, constructorHint ->
constructorHint.withMode(ExecutableMode.INTROSPECT)).build(); constructorHint.withMode(ExecutableMode.INTROSPECT)), constructorHint -> {
assertThat(hint.constructors()).singleElement().satisfies(constructorHint -> {
assertThat(constructorHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); assertThat(constructorHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE);
}); });
} }
void assertConstructorHint(Builder builder, Consumer<ExecutableHint> constructorHint) {
TypeHint hint = builder.build();
assertThat(hint.fields()).isEmpty();
assertThat(hint.constructors()).singleElement().satisfies(constructorHint);
assertThat(hint.methods()).isEmpty();
assertThat(hint.getMemberCategories()).isEmpty();
}
@Test @Test
void createWithMethod() { void createWithMethod() {
List<TypeReference> parameterTypes = List.of(TypeReference.of(char[].class)); List<TypeReference> parameterTypes = List.of(TypeReference.of(char[].class));
TypeHint hint = TypeHint.of(TypeReference.of(String.class)).withMethod("valueOf", parameterTypes, assertMethodHint(TypeHint.of(TypeReference.of(String.class))
methodHint -> methodHint.withMode(ExecutableMode.INVOKE)).build(); .withMethod("valueOf", parameterTypes), methodHint -> {
assertThat(hint.methods()).singleElement().satisfies(methodHint -> {
assertThat(methodHint.getName()).isEqualTo("valueOf"); assertThat(methodHint.getName()).isEqualTo("valueOf");
assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE);
}); });
} }
@Test
void createWithMethodAndMode() {
List<TypeReference> parameterTypes = List.of(TypeReference.of(char[].class));
assertMethodHint(TypeHint.of(TypeReference.of(String.class))
.withMethod("valueOf", parameterTypes, ExecutableMode.INTROSPECT), methodHint -> {
assertThat(methodHint.getName()).isEqualTo("valueOf");
assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT);
});
}
@Test
void createWithMethodAndEmptyCustomizerAppliesConsistentDefault() {
List<TypeReference> parameterTypes = List.of(TypeReference.of(char[].class));
assertMethodHint(TypeHint.of(TypeReference.of(String.class))
.withMethod("valueOf", parameterTypes, methodHint -> {}), methodHint -> {
assertThat(methodHint.getName()).isEqualTo("valueOf");
assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE);
});
}
@Test
void createWithMethodAndCustomizerAppliesCustomization() {
List<TypeReference> parameterTypes = List.of(TypeReference.of(char[].class));
assertMethodHint(TypeHint.of(TypeReference.of(String.class))
.withMethod("valueOf", parameterTypes, methodHint ->
methodHint.withMode(ExecutableMode.INTROSPECT)), methodHint -> {
assertThat(methodHint.getName()).isEqualTo("valueOf");
assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT);
});
}
@Test @Test
void createWithMethodReuseBuilder() { void createWithMethodReuseBuilder() {
List<TypeReference> parameterTypes = TypeReference.listOf(char[].class); List<TypeReference> parameterTypes = TypeReference.listOf(char[].class);
Builder builder = TypeHint.of(TypeReference.of(String.class)).withMethod("valueOf", parameterTypes, Builder builder = TypeHint.of(TypeReference.of(String.class))
methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT)); .withMethod("valueOf", parameterTypes, ExecutableMode.INTROSPECT);
TypeHint hint = builder.withMethod("valueOf", parameterTypes, assertMethodHint(builder.withMethod("valueOf", parameterTypes,
methodHint -> methodHint.withMode(ExecutableMode.INVOKE)).build(); methodHint -> methodHint.withMode(ExecutableMode.INVOKE)), methodHint -> {
assertThat(hint.methods()).singleElement().satisfies(methodHint -> {
assertThat(methodHint.getName()).isEqualTo("valueOf"); assertThat(methodHint.getName()).isEqualTo("valueOf");
assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE);
@ -146,17 +245,25 @@ class TypeHintTests {
@Test @Test
void createWithMethodReuseBuilderAndApplyExecutableModePrecedence() { void createWithMethodReuseBuilderAndApplyExecutableModePrecedence() {
List<TypeReference> parameterTypes = TypeReference.listOf(char[].class); List<TypeReference> parameterTypes = TypeReference.listOf(char[].class);
Builder builder = TypeHint.of(TypeReference.of(String.class)).withMethod("valueOf", parameterTypes, Builder builder = TypeHint.of(TypeReference.of(String.class))
methodHint -> methodHint.withMode(ExecutableMode.INVOKE)); .withMethod("valueOf", parameterTypes, ExecutableMode.INVOKE);
TypeHint hint = builder.withMethod("valueOf", parameterTypes, assertMethodHint(builder.withMethod("valueOf", parameterTypes,
methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT)).build(); methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT)), methodHint -> {
assertThat(hint.methods()).singleElement().satisfies(methodHint -> {
assertThat(methodHint.getName()).isEqualTo("valueOf"); assertThat(methodHint.getName()).isEqualTo("valueOf");
assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes);
assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE);
}); });
} }
void assertMethodHint(Builder builder, Consumer<ExecutableHint> methodHint) {
TypeHint hint = builder.build();
assertThat(hint.fields()).isEmpty();
assertThat(hint.constructors()).isEmpty();
assertThat(hint.methods()).singleElement().satisfies(methodHint);
assertThat(hint.getMemberCategories()).isEmpty();
}
@Test @Test
void createWithMemberCategory() { void createWithMemberCategory() {
TypeHint hint = TypeHint.of(TypeReference.of(String.class)) TypeHint hint = TypeHint.of(TypeReference.of(String.class))

39
spring-core/src/test/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicatesTests.java

@ -157,7 +157,7 @@ class ReflectionHintsPredicatesTests {
@Test @Test
void constructorIntrospectionMatchesConstructorHint() { void constructorIntrospectionMatchesConstructorHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
typeHint.withConstructor(Collections.emptyList(), constructorHint -> {})); typeHint.withConstructor(Collections.emptyList(), ExecutableMode.INTROSPECT));
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect()); assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect());
} }
@ -192,16 +192,14 @@ class ReflectionHintsPredicatesTests {
@Test @Test
void constructorInvocationDoesNotMatchConstructorHint() { void constructorInvocationDoesNotMatchConstructorHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint. runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.
withConstructor(Collections.emptyList(), constructorHint -> withConstructor(Collections.emptyList(), ExecutableMode.INTROSPECT));
constructorHint.withMode(ExecutableMode.INTROSPECT)));
assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).invoke()); assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).invoke());
} }
@Test @Test
void constructorInvocationMatchesConstructorInvocationHint() { void constructorInvocationMatchesConstructorInvocationHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint. runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.
withConstructor(Collections.emptyList(), constructorHint -> withConstructor(Collections.emptyList(), ExecutableMode.INVOKE));
constructorHint.withMode(ExecutableMode.INVOKE)));
assertPredicateMatches(reflection.onConstructor(publicConstructor).invoke()); assertPredicateMatches(reflection.onConstructor(publicConstructor).invoke());
} }
@ -236,7 +234,7 @@ class ReflectionHintsPredicatesTests {
@Test @Test
void privateConstructorIntrospectionMatchesConstructorHint() { void privateConstructorIntrospectionMatchesConstructorHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
typeHint.withConstructor(TypeReference.listOf(String.class), constructorHint -> {})); typeHint.withConstructor(TypeReference.listOf(String.class), ExecutableMode.INTROSPECT));
assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect()); assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect());
} }
@ -271,16 +269,14 @@ class ReflectionHintsPredicatesTests {
@Test @Test
void privateConstructorInvocationDoesNotMatchConstructorHint() { void privateConstructorInvocationDoesNotMatchConstructorHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
typeHint.withConstructor(TypeReference.listOf(String.class), constructorHint -> typeHint.withConstructor(TypeReference.listOf(String.class), ExecutableMode.INTROSPECT));
constructorHint.withMode(ExecutableMode.INTROSPECT)));
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke()); assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke());
} }
@Test @Test
void privateConstructorInvocationMatchesConstructorInvocationHint() { void privateConstructorInvocationMatchesConstructorInvocationHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
typeHint.withConstructor(TypeReference.listOf(String.class), typeHint.withConstructor(TypeReference.listOf(String.class), ExecutableMode.INVOKE));
constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE)));
assertPredicateMatches(reflection.onConstructor(privateConstructor).invoke()); assertPredicateMatches(reflection.onConstructor(privateConstructor).invoke());
} }
@ -319,8 +315,8 @@ class ReflectionHintsPredicatesTests {
@Test @Test
void methodIntrospectionMatchesMethodHint() { void methodIntrospectionMatchesMethodHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), methodHint -> { runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
})); typeHint.withMethod("publicMethod", Collections.emptyList(), ExecutableMode.INTROSPECT));
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect());
} }
@ -350,15 +346,15 @@ class ReflectionHintsPredicatesTests {
@Test @Test
void methodInvocationDoesNotMatchMethodHint() { void methodInvocationDoesNotMatchMethodHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT))); typeHint.withMethod("publicMethod", Collections.emptyList(), ExecutableMode.INTROSPECT));
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").invoke()); assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").invoke());
} }
@Test @Test
void methodInvocationMatchesMethodInvocationHint() { void methodInvocationMatchesMethodInvocationHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); typeHint.withMethod("publicMethod", Collections.emptyList(), ExecutableMode.INVOKE));
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").invoke()); assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").invoke());
} }
@ -388,8 +384,8 @@ class ReflectionHintsPredicatesTests {
@Test @Test
void privateMethodIntrospectionMatchesMethodHint() { void privateMethodIntrospectionMatchesMethodHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> { runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
})); typeHint.withMethod("privateMethod", Collections.emptyList(), ExecutableMode.INTROSPECT));
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect());
} }
@ -419,14 +415,15 @@ class ReflectionHintsPredicatesTests {
@Test @Test
void privateMethodInvocationDoesNotMatchMethodHint() { void privateMethodInvocationDoesNotMatchMethodHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT))); typeHint.withMethod("privateMethod", Collections.emptyList(), ExecutableMode.INTROSPECT));
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke()); assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke());
} }
@Test @Test
void privateMethodInvocationMatchesMethodInvocationHint() { void privateMethodInvocationMatchesMethodInvocationHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
typeHint.withMethod("privateMethod", Collections.emptyList(), ExecutableMode.INVOKE));
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").invoke()); assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").invoke());
} }

8
spring-core/src/test/java/org/springframework/aot/nativex/FileNativeConfigurationWriterTests.java

@ -111,11 +111,9 @@ public class FileNativeConfigurationWriterTests {
fieldBuilder.allowWrite(true); fieldBuilder.allowWrite(true);
fieldBuilder.allowUnsafeAccess(true); fieldBuilder.allowUnsafeAccess(true);
}) })
.withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), constructorHint -> .withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), ExecutableMode.INTROSPECT)
constructorHint.withMode(ExecutableMode.INTROSPECT)) .withMethod("setDefaultCharset", TypeReference.listOf(Charset.class))
.withMethod("setDefaultCharset", TypeReference.listOf(Charset.class), ctorBuilder -> {}) .withMethod("getDefaultCharset", Collections.emptyList(), ExecutableMode.INTROSPECT);
.withMethod("getDefaultCharset", Collections.emptyList(), constructorHint ->
constructorHint.withMode(ExecutableMode.INTROSPECT));
}); });
generator.write(hints); generator.write(hints);
assertEquals(""" assertEquals("""

46
spring-core/src/test/java/org/springframework/aot/nativex/ReflectionHintsWriterTests.java

@ -49,26 +49,22 @@ public class ReflectionHintsWriterTests {
@Test @Test
void one() throws JSONException { void one() throws JSONException {
ReflectionHints hints = new ReflectionHints(); ReflectionHints hints = new ReflectionHints();
hints.registerType(StringDecoder.class, builder -> { hints.registerType(StringDecoder.class, builder -> builder
builder .onReachableType(TypeReference.of(String.class))
.onReachableType(TypeReference.of(String.class)) .withMembers(MemberCategory.PUBLIC_FIELDS, MemberCategory.DECLARED_FIELDS,
.withMembers(MemberCategory.PUBLIC_FIELDS, MemberCategory.DECLARED_FIELDS, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS,
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.PUBLIC_CLASSES, MemberCategory.DECLARED_CLASSES)
MemberCategory.PUBLIC_CLASSES, MemberCategory.DECLARED_CLASSES) .withField("DEFAULT_CHARSET", fieldBuilder -> fieldBuilder.allowWrite(false))
.withField("DEFAULT_CHARSET", fieldBuilder -> fieldBuilder.allowWrite(false)) .withField("defaultCharset", fieldBuilder -> {
.withField("defaultCharset", fieldBuilder -> { fieldBuilder.allowWrite(true);
fieldBuilder.allowWrite(true); fieldBuilder.allowUnsafeAccess(true);
fieldBuilder.allowUnsafeAccess(true); })
}) .withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), ExecutableMode.INTROSPECT)
.withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), constructorHint -> .withMethod("setDefaultCharset", List.of(TypeReference.of(Charset.class)))
constructorHint.withMode(ExecutableMode.INTROSPECT)) .withMethod("getDefaultCharset", Collections.emptyList(), ExecutableMode.INTROSPECT));
.withMethod("setDefaultCharset", List.of(TypeReference.of(Charset.class)), ctorBuilder -> {})
.withMethod("getDefaultCharset", Collections.emptyList(), constructorHint ->
constructorHint.withMode(ExecutableMode.INTROSPECT));
});
assertEquals(""" assertEquals("""
[ [
{ {
@ -120,7 +116,7 @@ public class ReflectionHintsWriterTests {
void queriedMethods() throws JSONException { void queriedMethods() throws JSONException {
ReflectionHints hints = new ReflectionHints(); ReflectionHints hints = new ReflectionHints();
hints.registerType(Integer.class, builder -> builder.withMethod("parseInt", hints.registerType(Integer.class, builder -> builder.withMethod("parseInt",
TypeReference.listOf(String.class), b -> b.withMode(ExecutableMode.INTROSPECT))); TypeReference.listOf(String.class), ExecutableMode.INTROSPECT));
assertEquals(""" assertEquals("""
[ [
@ -141,7 +137,7 @@ public class ReflectionHintsWriterTests {
void methods() throws JSONException { void methods() throws JSONException {
ReflectionHints hints = new ReflectionHints(); ReflectionHints hints = new ReflectionHints();
hints.registerType(Integer.class, builder -> builder.withMethod("parseInt", hints.registerType(Integer.class, builder -> builder.withMethod("parseInt",
TypeReference.listOf(String.class), b -> b.withMode(ExecutableMode.INVOKE))); TypeReference.listOf(String.class), ExecutableMode.INVOKE));
assertEquals(""" assertEquals("""
[ [
@ -162,7 +158,7 @@ public class ReflectionHintsWriterTests {
void methodWithInnerClassParameter() throws JSONException { void methodWithInnerClassParameter() throws JSONException {
ReflectionHints hints = new ReflectionHints(); ReflectionHints hints = new ReflectionHints();
hints.registerType(Integer.class, builder -> builder.withMethod("test", hints.registerType(Integer.class, builder -> builder.withMethod("test",
TypeReference.listOf(Inner.class), b -> b.withMode(ExecutableMode.INVOKE))); TypeReference.listOf(Inner.class), ExecutableMode.INVOKE));
assertEquals(""" assertEquals("""
[ [
@ -183,9 +179,9 @@ public class ReflectionHintsWriterTests {
void methodAndQueriedMethods() throws JSONException { void methodAndQueriedMethods() throws JSONException {
ReflectionHints hints = new ReflectionHints(); ReflectionHints hints = new ReflectionHints();
hints.registerType(Integer.class, builder -> builder.withMethod("parseInt", hints.registerType(Integer.class, builder -> builder.withMethod("parseInt",
TypeReference.listOf(String.class), b -> b.withMode(ExecutableMode.INVOKE))); TypeReference.listOf(String.class), ExecutableMode.INVOKE));
hints.registerType(Integer.class, builder -> builder.withMethod("parseInt", hints.registerType(Integer.class, builder -> builder.withMethod("parseInt",
TypeReference.listOf(String.class, int.class), b -> b.withMode(ExecutableMode.INTROSPECT))); TypeReference.listOf(String.class, int.class), ExecutableMode.INTROSPECT));
assertEquals(""" assertEquals("""
[ [

Loading…
Cancel
Save