Revisit RuntimeHints API

The `RuntimeHints` API mainly reflects what is needed to write GraalVM
reachability metadata. The latest GraalVM version simplified its
format. This commit applies relevant simplifications as parts of it are
not needed anymore.

The new metadata format implies methods, constructors and fields
introspection as soon as a reflection hint is registered for a type. As
a result, `ExecutableMode.INTROSPECT`, and all `MemberCategory` values
except `MemberCategory.INVOKE_*` are being deprecated.
They have no replacement, as registering a type hint is enough.
In practice, it is enough to replace this:

```
hints.reflection().registerType(MyType.class, MemberCategory.DECLARED_FIELDS);
```

By this:
```
hints.reflection().registerType(MyType.class);
```

As for `MemberCategory.PUBLIC_FIELDS` and `MemberCategory.DECLARED_FIELDS`,
values were replaced by `INVOKE_PUBLIC_FIELDS` and
`INVOKE_DECLARED_FIELDS` to make their original intent clearer and align
with the rest of the API. Note, if you were using those values for
reflection only, you can safely remove those hints in favor of a simple
type hint.

See gh-33847
This commit is contained in:
Brian Clozel
2024-11-29 14:43:59 +01:00
parent fec2ed5540
commit 71362c953c
12 changed files with 173 additions and 147 deletions

View File

@@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Phillip Webb
* @since 6.0
*/
@SuppressWarnings("removal")
class ExecutableHintTests {
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
@@ -25,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Stephane Nicoll
*/
@SuppressWarnings("removal")
class ExecutableModeTests {
@Test

View File

@@ -37,6 +37,7 @@ import static org.mockito.Mockito.verifyNoInteractions;
* @author Stephane Nicoll
* @author Sebastien Deleuze
*/
@SuppressWarnings("removal")
class ReflectionHintsTests {
private final ReflectionHints reflectionHints = new ReflectionHints();

View File

@@ -117,7 +117,7 @@ class ResourceHintsTests {
@Test
void registerPatternWithIncludesAndExcludes() {
this.resourceHints.registerPattern(resourceHint ->
resourceHint.includes("com/example/*.properties").excludes("com/example/to-ignore.properties"));
resourceHint.includes("com/example/*.properties"));
assertThat(this.resourceHints.resourcePatternHints()).singleElement().satisfies(patternOf(
List.of("/", "com", "com/example", "com/example/*.properties"),
List.of("com/example/to-ignore.properties")));
@@ -198,10 +198,7 @@ class ResourceHintsTests {
}
private Consumer<ResourcePatternHints> patternOf(List<String> includes, List<String> excludes) {
return pattern -> {
assertThat(pattern.getIncludes()).map(ResourcePatternHint::getPattern).containsExactlyInAnyOrderElementsOf(includes);
assertThat(pattern.getExcludes()).map(ResourcePatternHint::getPattern).containsExactlyElementsOf(excludes);
};
return pattern -> assertThat(pattern.getIncludes()).map(ResourcePatternHint::getPattern).containsExactlyInAnyOrderElementsOf(includes);
}
static class Nested {

View File

@@ -47,11 +47,9 @@ class RuntimeHintsTests {
@Test
void resourceHintWithClass() {
this.hints.resources().registerType(String.class);
assertThat(this.hints.resources().resourcePatternHints()).singleElement().satisfies(resourceHint -> {
assertThat(resourceHint.getIncludes()).map(ResourcePatternHint::getPattern)
.containsExactlyInAnyOrder("/", "java", "java/lang", "java/lang/String.class");
assertThat(resourceHint.getExcludes()).isEmpty();
});
assertThat(this.hints.resources().resourcePatternHints()).singleElement().satisfies(resourceHint ->
assertThat(resourceHint.getIncludes()).map(ResourcePatternHint::getPattern)
.containsExactlyInAnyOrder("/", "java", "java/lang", "java/lang/String.class"));
}
@Test

View File

@@ -31,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
*
* @author Stephane Nicoll
*/
@SuppressWarnings("removal")
class TypeHintTests {
@Test
@@ -169,9 +170,8 @@ class TypeHintTests {
void builtWithAppliesMemberCategories() {
TypeHint.Builder builder = new TypeHint.Builder(TypeReference.of(String.class));
assertThat(builder.build().getMemberCategories()).isEmpty();
TypeHint.builtWith(MemberCategory.DECLARED_CLASSES, MemberCategory.DECLARED_FIELDS).accept(builder);
assertThat(builder.build().getMemberCategories()).containsExactlyInAnyOrder(MemberCategory.DECLARED_CLASSES,
MemberCategory.DECLARED_FIELDS);
TypeHint.builtWith(MemberCategory.DECLARED_FIELDS).accept(builder);
assertThat(builder.build().getMemberCategories()).containsExactly(MemberCategory.DECLARED_FIELDS);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -40,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
*
* @author Brian Clozel
*/
@SuppressWarnings("removal")
class ReflectionHintsPredicatesTests {
private static Constructor<?> privateConstructor;
@@ -160,6 +161,12 @@ class ReflectionHintsPredicatesTests {
assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).introspect());
}
@Test
void constructorIntrospectionMatchesTypeHint() {
runtimeHints.reflection().registerType(SampleClass.class);
assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect());
}
@Test
void constructorIntrospectionMatchesConstructorHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
@@ -230,22 +237,16 @@ class ReflectionHintsPredicatesTests {
}
@Test
void privateConstructorIntrospectionMatchesConstructorHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
typeHint.withConstructor(TypeReference.listOf(String.class), ExecutableMode.INTROSPECT));
void privateConstructorIntrospectionMatchesTypeHint() {
runtimeHints.reflection().registerType(SampleClass.class);
assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect());
}
@Test
void privateConstructorIntrospectionDoesNotMatchIntrospectPublicConstructors() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS);
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).introspect());
}
@Test
void privateConstructorIntrospectionDoesNotMatchInvokePublicConstructors() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).introspect());
void privateConstructorIntrospectionMatchesConstructorHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
typeHint.withConstructor(TypeReference.listOf(String.class), ExecutableMode.INTROSPECT));
assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect());
}
@Test
@@ -303,6 +304,12 @@ class ReflectionHintsPredicatesTests {
@Nested
class ReflectionOnMethod {
@Test
void methodIntrospectionMatchesTypeHint() {
runtimeHints.reflection().registerType(SampleClass.class);
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect());
}
@Test
void methodIntrospectionMatchesMethodHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
@@ -328,18 +335,6 @@ class ReflectionHintsPredicatesTests {
assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect());
}
@Test
void methodIntrospectionDoesNotMatchIntrospectDeclaredMethods() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INTROSPECT_DECLARED_METHODS);
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").introspect());
}
@Test
void methodIntrospectionDoesNotMatchInvokeDeclaredMethods() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INVOKE_DECLARED_METHODS);
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").introspect());
}
@Test
void methodInvocationDoesNotMatchMethodHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
@@ -379,22 +374,16 @@ class ReflectionHintsPredicatesTests {
}
@Test
void privateMethodIntrospectionMatchesMethodHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
typeHint.withMethod("privateMethod", Collections.emptyList(), ExecutableMode.INTROSPECT));
void privateMethodIntrospectionMatchesTypeHint() {
runtimeHints.reflection().registerType(SampleClass.class);
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect());
}
@Test
void privateMethodIntrospectionDoesNotMatchIntrospectPublicMethods() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INTROSPECT_PUBLIC_METHODS);
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").introspect());
}
@Test
void privateMethodIntrospectionDoesNotMatchInvokePublicMethods() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INVOKE_PUBLIC_METHODS);
assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").introspect());
void privateMethodIntrospectionMatchesMethodHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint ->
typeHint.withMethod("privateMethod", Collections.emptyList(), ExecutableMode.INTROSPECT));
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect());
}
@Test
@@ -464,15 +453,15 @@ class ReflectionHintsPredicatesTests {
}
@Test
void fieldReflectionMatchesFieldHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField"));
void fieldReflectionMatchesTypeHint() {
runtimeHints.reflection().registerType(SampleClass.class);
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField"));
}
@Test
void fieldReflectionDoesNotMatchNonRegisteredFielddHint() {
void fieldReflectionMatchesFieldHint() {
runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField"));
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "privateField"));
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField"));
}
@Test
@@ -482,9 +471,21 @@ class ReflectionHintsPredicatesTests {
}
@Test
void fieldReflectionDoesNotMatchDeclaredFieldsHint() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.DECLARED_FIELDS);
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField"));
void fieldInvocationMatchesPublicFieldsHint() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INVOKE_PUBLIC_FIELDS);
assertPredicateMatches(reflection.onField(SampleClass.class, "publicField").invocation());
}
@Test
void fieldInvocationDoesNotMatchTypeHint() {
runtimeHints.reflection().registerType(SampleClass.class);
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField").invocation());
}
@Test
void privateFieldReflectionMatchesTypeHint() {
runtimeHints.reflection().registerType(SampleClass.class);
assertPredicateMatches(reflection.onField(SampleClass.class, "privateField"));
}
@Test
@@ -493,18 +494,24 @@ class ReflectionHintsPredicatesTests {
assertPredicateMatches(reflection.onField(SampleClass.class, "privateField"));
}
@Test
void privateFieldReflectionDoesNotMatchPublicFieldsHint() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.PUBLIC_FIELDS);
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "privateField"));
}
@Test
void privateFieldReflectionMatchesDeclaredFieldsHint() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.DECLARED_FIELDS);
assertPredicateMatches(reflection.onField(SampleClass.class, "privateField"));
}
@Test
void privateFieldInvocationMatchesDeclaredFieldsHint() {
runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INVOKE_DECLARED_FIELDS);
assertPredicateMatches(reflection.onField(SampleClass.class, "privateField").invocation());
}
@Test
void privateFieldInvocationDoesNotMatchTypeHint() {
runtimeHints.reflection().registerType(SampleClass.class);
assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "privateField").invocation());
}
}
private void assertPredicateMatches(Predicate<RuntimeHints> predicate) {

View File

@@ -140,11 +140,8 @@ class FilePatternResourceHintsRegistrarTests {
}
private Consumer<ResourcePatternHints> includes(String... patterns) {
return hint -> {
assertThat(hint.getIncludes().stream().map(ResourcePatternHint::getPattern))
.containsExactlyInAnyOrder(patterns);
assertThat(hint.getExcludes()).isEmpty();
};
return hint -> assertThat(hint.getIncludes().stream().map(ResourcePatternHint::getPattern))
.containsExactlyInAnyOrder(patterns);
}
}