Skip to content

Commit 43d39d4

Browse files
committed
Enforce need for type reflection in RuntimeHintsAgent
Prior to this commit, the AOT infrastructure would rely on the fact that native runtime reflection on a type would only consider methods/fields/constructors that had specific hints contributed. When listing them through the reflection API on the type, the native image would only return those for which we had hints contributed. This behavior will soon change in GraalVM and will better align with the JVM behavior: when asking for all declared methods on a type in a native image, we should get all existing methods, not just the ones registered previously in the native image. This commit aligns the behavior of the `RuntimeHintsAgent` and removes the now misleading predicates as a consequence. Closes spring-projectsgh-29205
1 parent ce46170 commit 43d39d4

File tree

6 files changed

+18
-158
lines changed

6 files changed

+18
-158
lines changed

integration-tests/src/test/java/org/springframework/aot/RuntimeHintsAgentTests.java

-16
Original file line numberDiff line numberDiff line change
@@ -164,22 +164,6 @@ private static Stream<Arguments> instrumentedReflectionMethods() {
164164
throw new RuntimeException(e);
165165
}
166166
}, MethodReference.of(Method.class, "invoke")),
167-
Arguments.of((Runnable) () -> {
168-
try {
169-
toStringMethod.getAnnotations();
170-
}
171-
catch (Exception e) {
172-
throw new RuntimeException(e);
173-
}
174-
}, MethodReference.of(Method.class, "getAnnotations")),
175-
Arguments.of((Runnable) () -> {
176-
try {
177-
toStringMethod.getParameterTypes();
178-
}
179-
catch (Exception e) {
180-
throw new RuntimeException(e);
181-
}
182-
}, MethodReference.of(Method.class, "getParameterTypes")),
183167
Arguments.of((Runnable) () -> {
184168
try {
185169
privateGreetMethod.invoke(new PrivateClass());

spring-core-test/src/main/java/org/springframework/aot/agent/InstrumentedBridgeMethods.java

-27
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.io.IOException;
2020
import java.io.InputStream;
21-
import java.lang.annotation.Annotation;
2221
import java.lang.reflect.Constructor;
2322
import java.lang.reflect.Field;
2423
import java.lang.reflect.InvocationHandler;
@@ -336,32 +335,6 @@ public static Object constructornewInstance(Constructor<?> constructor, Object..
336335
* Bridge methods for java.lang.reflect.Method
337336
*/
338337

339-
public static Annotation[] methodgetAnnotations(Method method) {
340-
Annotation[] result = null;
341-
try {
342-
result = method.getAnnotations();
343-
}
344-
finally {
345-
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_GETANNOTATIONS)
346-
.onInstance(method).returnValue(result).build();
347-
RecordedInvocationsPublisher.publish(invocation);
348-
}
349-
return result;
350-
}
351-
352-
public static Class<?>[] methodgetParameterTypes(Method method) {
353-
Class<?>[] result = null;
354-
try {
355-
result = method.getParameterTypes();
356-
}
357-
finally {
358-
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_GETPARAMETERTYPES)
359-
.onInstance(method).returnValue(result).build();
360-
RecordedInvocationsPublisher.publish(invocation);
361-
}
362-
return result;
363-
}
364-
365338
public static Object methodinvoke(Method method, Object object, Object... arguments) throws InvocationTargetException, IllegalAccessException {
366339
Object result = null;
367340
boolean accessibilityChanged = false;

spring-core-test/src/main/java/org/springframework/aot/agent/InstrumentedMethod.java

+6-21
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ enum InstrumentedMethod {
9393
Class<?> thisClass = invocation.getInstance();
9494
return reflection().onType(TypeReference.of(thisClass)).withAnyMemberCategory(
9595
MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
96-
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)
97-
.or(reflection().onType(TypeReference.of(thisClass)).withAnyConstructor());
96+
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
9897
}
9998
),
10099

@@ -130,8 +129,7 @@ enum InstrumentedMethod {
130129
invocation -> {
131130
Class<?> thisClass = invocation.getInstance();
132131
return reflection().onType(TypeReference.of(thisClass))
133-
.withAnyMemberCategory(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)
134-
.or(reflection().onType(TypeReference.of(thisClass)).withAnyConstructor());
132+
.withAnyMemberCategory(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
135133
}),
136134

137135
/**
@@ -155,8 +153,7 @@ enum InstrumentedMethod {
155153
CLASS_GETDECLAREDFIELDS(Class.class, "getDeclaredFields", HintType.REFLECTION,
156154
invocation -> {
157155
Class<?> thisClass = invocation.getInstance();
158-
return reflection().onType(TypeReference.of(thisClass)).withMemberCategory(MemberCategory.DECLARED_FIELDS)
159-
.or(reflection().onType(TypeReference.of(thisClass)).withAnyField());
156+
return reflection().onType(TypeReference.of(thisClass)).withMemberCategory(MemberCategory.DECLARED_FIELDS);
160157
}
161158
),
162159

@@ -183,8 +180,7 @@ enum InstrumentedMethod {
183180
invocation -> {
184181
Class<?> thisClass = invocation.getInstance();
185182
return reflection().onType(TypeReference.of(thisClass))
186-
.withAnyMemberCategory(MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_METHODS)
187-
.or(reflection().onType(TypeReference.of(thisClass)).withAnyMethod());
183+
.withAnyMemberCategory(MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_METHODS);
188184
}
189185
),
190186

@@ -211,8 +207,7 @@ enum InstrumentedMethod {
211207
invocation -> {
212208
Class<?> thisClass = invocation.getInstance();
213209
return reflection().onType(TypeReference.of(thisClass))
214-
.withAnyMemberCategory(MemberCategory.PUBLIC_FIELDS, MemberCategory.DECLARED_FIELDS)
215-
.or(reflection().onType(TypeReference.of(thisClass)).withAnyField());
210+
.withAnyMemberCategory(MemberCategory.PUBLIC_FIELDS, MemberCategory.DECLARED_FIELDS);
216211
}
217212
),
218213

@@ -242,8 +237,7 @@ enum InstrumentedMethod {
242237
Class<?> thisClass = invocation.getInstance();
243238
return reflection().onType(TypeReference.of(thisClass)).withAnyMemberCategory(
244239
MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS,
245-
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS)
246-
.or(reflection().onType(TypeReference.of(thisClass)).withAnyMethod());
240+
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS);
247241
}
248242
),
249243

@@ -265,15 +259,6 @@ enum InstrumentedMethod {
265259
CONSTRUCTOR_NEWINSTANCE(Constructor.class, "newInstance", HintType.REFLECTION,
266260
invocation -> reflection().onConstructor(invocation.getInstance()).invoke()),
267261

268-
/**
269-
* {@link Method#getParameterTypes()}.
270-
*/
271-
METHOD_GETANNOTATIONS(Method.class, "getAnnotations", HintType.REFLECTION,
272-
invocation -> reflection().onMethod(invocation.getInstance())),
273-
274-
METHOD_GETPARAMETERTYPES(Method.class, "getParameterTypes", HintType.REFLECTION,
275-
invocation -> reflection().onMethod(invocation.getInstance())),
276-
277262
/**
278263
* {@link Method#invoke(Object, Object...)}.
279264
*/

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

+12-35
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.aot.agent;
1818

19-
import java.lang.reflect.Method;
2019
import java.lang.reflect.Proxy;
2120
import java.util.Collections;
2221
import java.util.Comparator;
@@ -193,9 +192,9 @@ void classGetConstructorsShouldNotMatchTypeReflectionHint() {
193192
}
194193

195194
@Test
196-
void classGetConstructorsShouldMatchConstructorReflectionHint() throws Exception {
195+
void classGetConstructorsShouldNotMatchConstructorReflectionHint() throws Exception {
197196
hints.reflection().registerConstructor(String.class.getConstructor(), ExecutableMode.INVOKE);
198-
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors);
197+
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors);
199198
}
200199

201200
@Test
@@ -249,9 +248,9 @@ void classGetDeclaredConstructorsShouldNotMatchTypeReflectionHint() {
249248
}
250249

251250
@Test
252-
void classGetDeclaredConstructorsShouldMatchConstructorReflectionHint() throws Exception {
251+
void classGetDeclaredConstructorsShouldNotMatchConstructorReflectionHint() throws Exception {
253252
hints.reflection().registerConstructor(String.class.getConstructor(), ExecutableMode.INVOKE);
254-
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTORS, this.stringGetDeclaredConstructors);
253+
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTORS, this.stringGetDeclaredConstructors);
255254
}
256255

257256
@Test
@@ -349,9 +348,9 @@ void classGetDeclaredMethodsShouldNotMatchTypeReflectionHint() {
349348
}
350349

351350
@Test
352-
void classGetDeclaredMethodsShouldMatchMethodReflectionHint() throws Exception {
351+
void classGetDeclaredMethodsShouldNotMatchMethodReflectionHint() throws Exception {
353352
hints.reflection().registerMethod(String.class.getMethod("toString"), ExecutableMode.INVOKE);
354-
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHODS, this.stringGetScaleMethod);
353+
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDMETHODS, this.stringGetScaleMethod);
355354
}
356355

357356
@Test
@@ -391,9 +390,9 @@ void classGetMethodsShouldNotMatchTypeReflectionHint() {
391390
}
392391

393392
@Test
394-
void classGetMethodsShouldMatchMethodReflectionHint() throws Exception {
393+
void classGetMethodsShouldNotMatchMethodReflectionHint() throws Exception {
395394
hints.reflection().registerMethod(String.class.getMethod("toString"), ExecutableMode.INVOKE);
396-
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHODS, this.stringGetMethods);
395+
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHODS, this.stringGetMethods);
397396
}
398397

399398
@Test
@@ -452,28 +451,6 @@ void classGetMethodShouldNotMatchForWrongType() {
452451
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod);
453452
}
454453

455-
@Test
456-
void methodGetAnnotationsShouldMatchIntrospectHintOnMethod() throws NoSuchMethodException {
457-
Method toString = String.class.getMethod("toString");
458-
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_GETANNOTATIONS)
459-
.onInstance(toString).withArguments("testString")
460-
.returnValue(toString.getAnnotations()).build();
461-
hints.reflection().registerType(String.class, typeHint -> typeHint.withMethod("toString",
462-
Collections.emptyList(), ExecutableMode.INTROSPECT));
463-
assertThatInvocationMatches(InstrumentedMethod.METHOD_GETANNOTATIONS, invocation);
464-
}
465-
466-
@Test
467-
void methodGetParameterTypesShouldMatchIntrospectHintOnMethod() throws NoSuchMethodException {
468-
Method toString = String.class.getMethod("toString");
469-
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_GETPARAMETERTYPES)
470-
.onInstance(toString).withArguments("testString")
471-
.returnValue(toString.getParameterTypes()).build();
472-
hints.reflection().registerType(String.class, typeHint -> typeHint.withMethod("toString",
473-
Collections.emptyList(), ExecutableMode.INTROSPECT));
474-
assertThatInvocationMatches(InstrumentedMethod.METHOD_GETPARAMETERTYPES, invocation);
475-
}
476-
477454
@Test
478455
void methodInvokeShouldMatchInvokeHintOnMethod() throws NoSuchMethodException {
479456
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_INVOKE)
@@ -555,9 +532,9 @@ void classGetDeclaredFieldsShouldNotMatchTypeHint() {
555532
}
556533

557534
@Test
558-
void classGetDeclaredFieldsShouldMatchFieldHint() throws Exception {
535+
void classGetDeclaredFieldsShouldNotMatchFieldHint() throws Exception {
559536
hints.reflection().registerField(String.class.getDeclaredField("value"));
560-
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDFIELDS, this.stringGetDeclaredFields);
537+
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDFIELDS, this.stringGetDeclaredFields);
561538
}
562539

563540
@Test
@@ -626,9 +603,9 @@ void classGetFieldsShouldNotMatchTypeHint() {
626603
}
627604

628605
@Test
629-
void classGetFieldsShouldMatchFieldHint() throws Exception {
606+
void classGetFieldsShouldNotMatchFieldHint() throws Exception {
630607
hints.reflection().registerField(String.class.getDeclaredField("value"));
631-
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETFIELDS, this.stringGetFields);
608+
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETFIELDS, this.stringGetFields);
632609
}
633610

634611
}

spring-core/src/main/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicates.java

-23
Original file line numberDiff line numberDiff line change
@@ -243,29 +243,6 @@ public Predicate<RuntimeHints> withAnyMemberCategory(MemberCategory... memberCat
243243
.anyMatch(memberCategory -> getTypeHint(hints).getMemberCategories().contains(memberCategory)));
244244
}
245245

246-
/**
247-
* Refine the current predicate to only match if a hint is present for any of its constructors.
248-
* @return the refined {@link RuntimeHints} predicate
249-
*/
250-
public Predicate<RuntimeHints> withAnyConstructor() {
251-
return this.and(hints -> getTypeHint(hints).constructors().findAny().isPresent());
252-
}
253-
254-
/**
255-
* Refine the current predicate to only match if a hint is present for any of its methods.
256-
* @return the refined {@link RuntimeHints} predicate
257-
*/
258-
public Predicate<RuntimeHints> withAnyMethod() {
259-
return this.and(hints -> getTypeHint(hints).methods().findAny().isPresent());
260-
}
261-
262-
/**
263-
* Refine the current predicate to only match if a hint is present for any of its fields.
264-
* @return the refined {@link RuntimeHints} predicate
265-
*/
266-
public Predicate<RuntimeHints> withAnyField() {
267-
return this.and(hints -> getTypeHint(hints).fields().findAny().isPresent());
268-
}
269246
}
270247

271248
public abstract static class ExecutableHintPredicate<T extends Executable> implements Predicate<RuntimeHints> {

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

-36
Original file line numberDiff line numberDiff line change
@@ -296,18 +296,6 @@ void privateConstructorInvocationMatchesInvokeDeclaredConstructors() {
296296
assertPredicateMatches(reflection.onConstructor(privateConstructor).invoke());
297297
}
298298

299-
@Test
300-
void reflectionOnAnyConstructorDoesNotMatchTypeReflection() {
301-
runtimeHints.reflection().registerType(SampleClass.class);
302-
assertPredicateDoesNotMatch(reflection.onType(SampleClass.class).withAnyConstructor());
303-
}
304-
305-
@Test
306-
void reflectionOnAnyConstructorMatchesConstructorReflection() {
307-
runtimeHints.reflection().registerConstructor(publicConstructor, ExecutableMode.INVOKE);
308-
assertPredicateMatches(reflection.onType(SampleClass.class).withAnyConstructor());
309-
}
310-
311299
}
312300

313301
@Nested
@@ -457,18 +445,6 @@ void privateMethodInvocationMatchesInvokeDeclaredMethods() {
457445
assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").invoke());
458446
}
459447

460-
@Test
461-
void reflectionOnAnyMethodDoesNotMatchTypeReflection() {
462-
runtimeHints.reflection().registerType(SampleClass.class);
463-
assertPredicateDoesNotMatch(reflection.onType(SampleClass.class).withAnyMethod());
464-
}
465-
466-
@Test
467-
void reflectionOnAnyMethodMatchesMethodReflection() {
468-
runtimeHints.reflection().registerMethod(publicMethod, ExecutableMode.INVOKE);
469-
assertPredicateMatches(reflection.onType(SampleClass.class).withAnyMethod());
470-
}
471-
472448
}
473449

474450
@Nested
@@ -527,18 +503,6 @@ void privateFieldReflectionMatchesDeclaredFieldsHint() {
527503
assertPredicateMatches(reflection.onField(SampleClass.class, "privateField"));
528504
}
529505

530-
@Test
531-
void reflectionOnAnyFieldDoesNotMatchTypeReflection() {
532-
runtimeHints.reflection().registerType(SampleClass.class);
533-
assertPredicateDoesNotMatch(reflection.onType(SampleClass.class).withAnyField());
534-
}
535-
536-
@Test
537-
void reflectionOnAnyFieldMatchesFieldReflection() {
538-
runtimeHints.reflection().registerField(publicField);
539-
assertPredicateMatches(reflection.onType(SampleClass.class).withAnyField());
540-
}
541-
542506
}
543507

544508
private void assertPredicateMatches(Predicate<RuntimeHints> predicate) {

0 commit comments

Comments
 (0)