Skip to content

Commit

Permalink
Revisit RuntimeHints API
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bclozel committed Nov 29, 2024
1 parent fec2ed5 commit 71362c9
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 146 deletions.
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -30,7 +30,10 @@ public enum ExecutableMode {

/**
* Only retrieving the {@link Executable} and its metadata is required.
* @deprecated with no replacement since introspection is included
* when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}.
*/
@Deprecated(since= "7.0", forRemoval = true)
INTROSPECT,

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -32,32 +32,62 @@
public enum MemberCategory {

/**
* A category that represents public {@linkplain Field fields}.
* A category that represents introspection on public {@linkplain Field fields}.
* @deprecated with no replacement since introspection is included
* when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}.
* Use {@link #INVOKE_PUBLIC_FIELDS} if getting/setting field values is required.
* @see Class#getFields()
*/
@Deprecated(since = "7.0", forRemoval = true)
PUBLIC_FIELDS,

/**
* A category that represents {@linkplain Class#getDeclaredFields() declared
* A category that represents introspection on {@linkplain Class#getDeclaredFields() declared
* fields}: all fields defined by the class but not inherited fields.
* @deprecated with no replacement since introspection is included
* when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}.
* Use {@link #INVOKE_DECLARED_FIELDS} if getting/setting field values is required.
* @see Class#getDeclaredFields()
*/
@Deprecated(since = "7.0", forRemoval = true)
DECLARED_FIELDS,

/**
* A category that represents getting/setting values on public {@linkplain Field fields}.
* @see Field#get(Object)
* @see Field#set(Object, Object)
* @since 7.0
*/
INVOKE_PUBLIC_FIELDS,

/**
* A category that represents getting/setting values on declared {@linkplain Field fields}.
* @see Field#get(Object)
* @see Field#set(Object, Object)
* @since 7.0
*/
INVOKE_DECLARED_FIELDS,

/**
* A category that defines public {@linkplain Constructor constructors} can
* be introspected but not invoked.
* @deprecated with no replacement since introspection is included
* when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}.
* @see Class#getConstructors()
* @see ExecutableMode#INTROSPECT
*/
@Deprecated(since = "7.0", forRemoval = true)
INTROSPECT_PUBLIC_CONSTRUCTORS,

/**
* A category that defines {@linkplain Class#getDeclaredConstructors() all
* constructors} can be introspected but not invoked.
* @deprecated with no replacement since introspection is included
* when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}.
* @see Class#getDeclaredConstructors()
* @see ExecutableMode#INTROSPECT
*/
@Deprecated(since = "7.0", forRemoval = true)
INTROSPECT_DECLARED_CONSTRUCTORS,

/**
Expand All @@ -79,17 +109,23 @@ public enum MemberCategory {
/**
* A category that defines public {@linkplain Method methods}, including
* inherited ones, can be introspected but not invoked.
* @deprecated with no replacement since introspection is added by default
* when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}.
* @see Class#getMethods()
* @see ExecutableMode#INTROSPECT
*/
@Deprecated(since = "7.0", forRemoval = true)
INTROSPECT_PUBLIC_METHODS,

/**
* A category that defines {@linkplain Class#getDeclaredMethods() all
* methods}, excluding inherited ones, can be introspected but not invoked.
* @deprecated with no replacement since introspection is added by default
* when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}.
* @see Class#getDeclaredMethods()
* @see ExecutableMode#INTROSPECT
*/
@Deprecated(since = "7.0", forRemoval = true)
INTROSPECT_DECLARED_METHODS,

/**
Expand All @@ -114,7 +150,10 @@ public enum MemberCategory {
* <p>Contrary to other categories, this does not register any particular
* reflection for inner classes but rather makes sure they are available
* via a call to {@link Class#getClasses}.
* @deprecated with no replacement since introspection is included
* when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}.
*/
@Deprecated(since = "7.0", forRemoval = true)
PUBLIC_CLASSES,

/**
Expand All @@ -123,7 +162,10 @@ public enum MemberCategory {
* <p>Contrary to other categories, this does not register any particular
* reflection for inner classes but rather makes sure they are available
* via a call to {@link Class#getDeclaredClasses}.
* @deprecated with no replacement since introspection is included
* when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}.
*/
@Deprecated(since = "7.0", forRemoval = true)
DECLARED_CLASSES

}
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ public Predicate<RuntimeHints> withAnyMemberCategory(MemberCategory... memberCat
}


@SuppressWarnings("removal")
public abstract static class ExecutableHintPredicate<T extends Executable> implements Predicate<RuntimeHints> {

protected final T executable;
Expand Down Expand Up @@ -299,6 +300,7 @@ static boolean includes(ExecutableHint hint, String name,
}


@SuppressWarnings("removal")
public static class ConstructorHintPredicate extends ExecutableHintPredicate<Constructor<?>> {

ConstructorHintPredicate(Constructor<?> constructor) {
Expand All @@ -308,28 +310,17 @@ public static class ConstructorHintPredicate extends ExecutableHintPredicate<Con
@Override
public boolean test(RuntimeHints runtimeHints) {
return (new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass()))
.withAnyMemberCategory(getPublicMemberCategories())
.and(hints -> Modifier.isPublic(this.executable.getModifiers())))
.or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())).withAnyMemberCategory(getDeclaredMemberCategories()))
.and(hints -> this.executableMode == ExecutableMode.INTROSPECT))
.or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass()))
.withMemberCategory(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS)
.and(hints -> Modifier.isPublic(this.executable.getModifiers()))
.and(hints -> this.executableMode == ExecutableMode.INVOKE))
.or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass()))
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)
.and(hints -> this.executableMode == ExecutableMode.INVOKE))
.or(exactMatch()).test(runtimeHints);
}

MemberCategory[] getPublicMemberCategories() {
if (this.executableMode == ExecutableMode.INTROSPECT) {
return new MemberCategory[] { MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS };
}
return new MemberCategory[] { MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS };
}

MemberCategory[] getDeclaredMemberCategories() {
if (this.executableMode == ExecutableMode.INTROSPECT) {
return new MemberCategory[] { MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS };
}
return new MemberCategory[] { MemberCategory.INVOKE_DECLARED_CONSTRUCTORS };
}

@Override
Predicate<RuntimeHints> exactMatch() {
return hints -> {
Expand All @@ -343,6 +334,7 @@ Predicate<RuntimeHints> exactMatch() {
}


@SuppressWarnings("removal")
public static class MethodHintPredicate extends ExecutableHintPredicate<Method> {

MethodHintPredicate(Method method) {
Expand All @@ -352,31 +344,18 @@ public static class MethodHintPredicate extends ExecutableHintPredicate<Method>
@Override
public boolean test(RuntimeHints runtimeHints) {
return (new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass()))
.withAnyMemberCategory(getPublicMemberCategories())
.and(hints -> Modifier.isPublic(this.executable.getModifiers())))
.or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass()))
.withAnyMemberCategory(getDeclaredMemberCategories())
.and(hints -> !Modifier.isPublic(this.executable.getModifiers())))
.and(hints -> this.executableMode == ExecutableMode.INTROSPECT))
.or((new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass()))
.withMemberCategory(MemberCategory.INVOKE_PUBLIC_METHODS)
.and(hints -> Modifier.isPublic(this.executable.getModifiers()))
.and(hints -> this.executableMode == ExecutableMode.INVOKE)))
.or((new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass()))
.withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)
.and(hints -> !Modifier.isPublic(this.executable.getModifiers()))
.and(hints -> this.executableMode == ExecutableMode.INVOKE)))
.or(exactMatch()).test(runtimeHints);
}

MemberCategory[] getPublicMemberCategories() {
if (this.executableMode == ExecutableMode.INTROSPECT) {
return new MemberCategory[] { MemberCategory.INTROSPECT_PUBLIC_METHODS,
MemberCategory.INVOKE_PUBLIC_METHODS };
}
return new MemberCategory[] { MemberCategory.INVOKE_PUBLIC_METHODS };
}

MemberCategory[] getDeclaredMemberCategories() {

if (this.executableMode == ExecutableMode.INTROSPECT) {
return new MemberCategory[] { MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.INVOKE_DECLARED_METHODS };
}
return new MemberCategory[] { MemberCategory.INVOKE_DECLARED_METHODS };
}

@Override
Predicate<RuntimeHints> exactMatch() {
return hints -> {
Expand All @@ -394,31 +373,40 @@ public static class FieldHintPredicate implements Predicate<RuntimeHints> {

private final Field field;

@Nullable
private ExecutableMode executableMode;

FieldHintPredicate(Field field) {
this.field = field;
}

/**
* Refine the current predicate to only match if an invocation hint is registered for this field.
* @return the refined {@link RuntimeHints} predicate
* @since 7.0
*/
public FieldHintPredicate invocation() {
this.executableMode = ExecutableMode.INVOKE;
return this;
}

@Override
public boolean test(RuntimeHints runtimeHints) {
TypeHint typeHint = runtimeHints.reflection().getTypeHint(this.field.getDeclaringClass());
if (typeHint == null) {
return false;
}
return memberCategoryMatch(typeHint) || exactMatch(typeHint);
}

private boolean memberCategoryMatch(TypeHint typeHint) {
if (Modifier.isPublic(this.field.getModifiers())) {
return typeHint.getMemberCategories().contains(MemberCategory.PUBLIC_FIELDS);
if (typeHint != null) {
if (this.executableMode == ExecutableMode.INVOKE) {
if (Modifier.isPublic(this.field.getModifiers())) {
return typeHint.getMemberCategories().contains(MemberCategory.INVOKE_PUBLIC_FIELDS);
}
else {
return typeHint.getMemberCategories().contains(MemberCategory.INVOKE_DECLARED_FIELDS);
}
}
else {
return true;
}
}
else {
return typeHint.getMemberCategories().contains(MemberCategory.DECLARED_FIELDS);
}
}

private boolean exactMatch(TypeHint typeHint) {
return typeHint.fields().anyMatch(fieldHint ->
this.field.getName().equals(fieldHint.getName()));
return false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;

import org.springframework.aot.hint.ResourceHints;
import org.springframework.aot.hint.ResourcePatternHint;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentLruCache;

/**
* Generator of {@link ResourceHints} predicates, testing whether the given hints
Expand All @@ -39,7 +38,7 @@
*/
public class ResourceHintsPredicates {

private static final ConcurrentLruCache<ResourcePatternHint, Pattern> CACHED_RESOURCE_PATTERNS = new ConcurrentLruCache<>(32, ResourcePatternHint::toRegex);
private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

ResourceHintsPredicates() {
}
Expand Down Expand Up @@ -100,26 +99,18 @@ public Predicate<RuntimeHints> forResource(String resourceName) {
return hints -> {
AggregatedResourcePatternHints aggregatedResourcePatternHints = AggregatedResourcePatternHints.of(
hints.resources());
boolean isExcluded = aggregatedResourcePatternHints.excludes().stream().anyMatch(excluded ->
CACHED_RESOURCE_PATTERNS.get(excluded).matcher(resourceNameToUse).matches());
if (isExcluded) {
return false;
}
return aggregatedResourcePatternHints.includes().stream().anyMatch(included ->
CACHED_RESOURCE_PATTERNS.get(included).matcher(resourceNameToUse).matches());
PATH_MATCHER.match(included.getPattern(), resourceNameToUse));
};
}

private record AggregatedResourcePatternHints(List<ResourcePatternHint> includes, List<ResourcePatternHint> excludes) {
private record AggregatedResourcePatternHints(List<ResourcePatternHint> includes) {

static AggregatedResourcePatternHints of(ResourceHints resourceHints) {
List<ResourcePatternHint> includes = new ArrayList<>();
List<ResourcePatternHint> excludes = new ArrayList<>();
resourceHints.resourcePatternHints().forEach(resourcePatternHint -> {
includes.addAll(resourcePatternHint.getIncludes());
excludes.addAll(resourcePatternHint.getExcludes());
});
return new AggregatedResourcePatternHints(includes, excludes);
resourceHints.resourcePatternHints().forEach(resourcePatternHint ->
includes.addAll(resourcePatternHint.getIncludes()));
return new AggregatedResourcePatternHints(includes);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* @author Phillip Webb
* @since 6.0
*/
@SuppressWarnings("removal")
class ExecutableHintTests {

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -25,6 +25,7 @@
*
* @author Stephane Nicoll
*/
@SuppressWarnings("removal")
class ExecutableModeTests {

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
* @author Stephane Nicoll
* @author Sebastien Deleuze
*/
@SuppressWarnings("removal")
class ReflectionHintsTests {

private final ReflectionHints reflectionHints = new ReflectionHints();
Expand Down
Loading

0 comments on commit 71362c9

Please sign in to comment.