Deprecate RuntimeHintsAgent Java agent

This Java agent has been designed to instrument specific JDK calls for
reflective introspection and invocations. This is useful for testing
runtime hints in integration tests.

As of GraalVM 23, there is a new VM option that does this in a much more
efficient and precise fashion. Developers can use the
`-XX:MissingRegistrationReportingMode` with the "Warn" or "Exit" value.
The option will look for reachability metadata and emit logs in "Warn"
mode or immediately exit the application with in "Exit" mode.

This commit deprecates the `RuntimeHintsAgent` in favor of this new,
more reliable option.

See gh-33847
This commit is contained in:
Brian Clozel 2024-11-29 14:44:02 +01:00
parent 71362c953c
commit 0759129c14
4 changed files with 80 additions and 291 deletions

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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,13 +20,11 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.TypeReference;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
@ -67,8 +65,7 @@ enum InstrumentedMethod {
CLASS_GETCLASSES(Class.class, "getClasses", HintType.REFLECTION, CLASS_GETCLASSES(Class.class, "getClasses", HintType.REFLECTION,
invocation -> { invocation -> {
Class<?> thisClass = invocation.getInstance(); Class<?> thisClass = invocation.getInstance();
return reflection().onType(TypeReference.of(thisClass)) return reflection().onType(TypeReference.of(thisClass));
.withAnyMemberCategory(MemberCategory.DECLARED_CLASSES, MemberCategory.PUBLIC_CLASSES);
} }
), ),
@ -81,7 +78,7 @@ enum InstrumentedMethod {
if (constructor == null) { if (constructor == null) {
return runtimeHints -> false; return runtimeHints -> false;
} }
return reflection().onConstructor(constructor).introspect(); return reflection().onType(constructor.getDeclaringClass());
} }
), ),
@ -91,9 +88,7 @@ enum InstrumentedMethod {
CLASS_GETCONSTRUCTORS(Class.class, "getConstructors", HintType.REFLECTION, CLASS_GETCONSTRUCTORS(Class.class, "getConstructors", HintType.REFLECTION,
invocation -> { invocation -> {
Class<?> thisClass = invocation.getInstance(); Class<?> thisClass = invocation.getInstance();
return reflection().onType(TypeReference.of(thisClass)).withAnyMemberCategory( return reflection().onType(TypeReference.of(thisClass));
MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
} }
), ),
@ -103,7 +98,7 @@ enum InstrumentedMethod {
CLASS_GETDECLAREDCLASSES(Class.class, "getDeclaredClasses", HintType.REFLECTION, CLASS_GETDECLAREDCLASSES(Class.class, "getDeclaredClasses", HintType.REFLECTION,
invocation -> { invocation -> {
Class<?> thisClass = invocation.getInstance(); Class<?> thisClass = invocation.getInstance();
return reflection().onType(TypeReference.of(thisClass)).withMemberCategory(MemberCategory.DECLARED_CLASSES); return reflection().onType(TypeReference.of(thisClass));
} }
), ),
@ -116,9 +111,7 @@ enum InstrumentedMethod {
if (constructor == null) { if (constructor == null) {
return runtimeHints -> false; return runtimeHints -> false;
} }
TypeReference thisType = invocation.getInstanceTypeReference(); return reflection().onType(constructor.getDeclaringClass());
return reflection().onType(thisType).withMemberCategory(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS)
.or(reflection().onConstructor(constructor).introspect());
} }
), ),
@ -128,8 +121,7 @@ enum InstrumentedMethod {
CLASS_GETDECLAREDCONSTRUCTORS(Class.class, "getDeclaredConstructors", HintType.REFLECTION, CLASS_GETDECLAREDCONSTRUCTORS(Class.class, "getDeclaredConstructors", HintType.REFLECTION,
invocation -> { invocation -> {
Class<?> thisClass = invocation.getInstance(); Class<?> thisClass = invocation.getInstance();
return reflection().onType(TypeReference.of(thisClass)) return reflection().onType(TypeReference.of(thisClass));
.withAnyMemberCategory(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}), }),
/** /**
@ -141,9 +133,7 @@ enum InstrumentedMethod {
if (field == null) { if (field == null) {
return runtimeHints -> false; return runtimeHints -> false;
} }
TypeReference thisType = invocation.getInstanceTypeReference(); return reflection().onType(field.getDeclaringClass());
return reflection().onType(thisType).withMemberCategory(MemberCategory.DECLARED_FIELDS)
.or(reflection().onField(field));
} }
), ),
@ -153,7 +143,7 @@ enum InstrumentedMethod {
CLASS_GETDECLAREDFIELDS(Class.class, "getDeclaredFields", HintType.REFLECTION, CLASS_GETDECLAREDFIELDS(Class.class, "getDeclaredFields", HintType.REFLECTION,
invocation -> { invocation -> {
Class<?> thisClass = invocation.getInstance(); Class<?> thisClass = invocation.getInstance();
return reflection().onType(TypeReference.of(thisClass)).withMemberCategory(MemberCategory.DECLARED_FIELDS); return reflection().onType(TypeReference.of(thisClass));
} }
), ),
@ -166,10 +156,7 @@ enum InstrumentedMethod {
if (method == null) { if (method == null) {
return runtimeHints -> false; return runtimeHints -> false;
} }
TypeReference thisType = invocation.getInstanceTypeReference(); return reflection().onType(method.getDeclaringClass());
return reflection().onType(thisType)
.withAnyMemberCategory(MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_METHODS)
.or(reflection().onMethod(method).introspect());
} }
), ),
@ -179,8 +166,7 @@ enum InstrumentedMethod {
CLASS_GETDECLAREDMETHODS(Class.class, "getDeclaredMethods", HintType.REFLECTION, CLASS_GETDECLAREDMETHODS(Class.class, "getDeclaredMethods", HintType.REFLECTION,
invocation -> { invocation -> {
Class<?> thisClass = invocation.getInstance(); Class<?> thisClass = invocation.getInstance();
return reflection().onType(TypeReference.of(thisClass)) return reflection().onType(TypeReference.of(thisClass));
.withAnyMemberCategory(MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_METHODS);
} }
), ),
@ -194,10 +180,7 @@ enum InstrumentedMethod {
if (field == null) { if (field == null) {
return runtimeHints -> false; return runtimeHints -> false;
} }
TypeReference thisType = invocation.getInstanceTypeReference(); return reflection().onType(field.getDeclaringClass())
return reflection().onType(thisType).withMemberCategory(MemberCategory.PUBLIC_FIELDS)
.and(runtimeHints -> Modifier.isPublic(field.getModifiers()))
.or(reflection().onType(thisType).withMemberCategory(MemberCategory.DECLARED_FIELDS))
.or(reflection().onField(invocation.getReturnValue())); .or(reflection().onField(invocation.getReturnValue()));
}), }),
@ -207,8 +190,7 @@ enum InstrumentedMethod {
CLASS_GETFIELDS(Class.class, "getFields", HintType.REFLECTION, CLASS_GETFIELDS(Class.class, "getFields", HintType.REFLECTION,
invocation -> { invocation -> {
Class<?> thisClass = invocation.getInstance(); Class<?> thisClass = invocation.getInstance();
return reflection().onType(TypeReference.of(thisClass)) return reflection().onType(TypeReference.of(thisClass));
.withAnyMemberCategory(MemberCategory.PUBLIC_FIELDS, MemberCategory.DECLARED_FIELDS);
} }
), ),
@ -221,12 +203,7 @@ enum InstrumentedMethod {
if (method == null) { if (method == null) {
return runtimeHints -> false; return runtimeHints -> false;
} }
TypeReference thisType = invocation.getInstanceTypeReference(); return reflection().onType(method.getDeclaringClass());
return reflection().onType(thisType).withAnyMemberCategory(MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS)
.and(runtimeHints -> Modifier.isPublic(method.getModifiers()))
.or(reflection().onType(thisType).withAnyMemberCategory(MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_METHODS))
.or(reflection().onMethod(method).introspect())
.or(reflection().onMethod(method).invoke());
} }
), ),
@ -236,9 +213,7 @@ enum InstrumentedMethod {
CLASS_GETMETHODS(Class.class, "getMethods", HintType.REFLECTION, CLASS_GETMETHODS(Class.class, "getMethods", HintType.REFLECTION,
invocation -> { invocation -> {
Class<?> thisClass = invocation.getInstance(); Class<?> thisClass = invocation.getInstance();
return reflection().onType(TypeReference.of(thisClass)).withAnyMemberCategory( return reflection().onType(TypeReference.of(thisClass));
MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS,
MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS);
} }
), ),

View File

@ -39,7 +39,10 @@ import org.springframework.util.StringUtils;
* @author Brian Clozel * @author Brian Clozel
* @since 6.0 * @since 6.0
* @see InvocationsRecorderClassTransformer * @see InvocationsRecorderClassTransformer
* @deprecated as of 7.0 in favor of the {@code -XX:MissingRegistrationReportingMode=Warn} and
* {@code -XX:MissingRegistrationReportingMode=Exit} JVM flags with GraalVM.
*/ */
@Deprecated(forRemoval = true)
public final class RuntimeHintsAgent { public final class RuntimeHintsAgent {
private static boolean loaded = false; private static boolean loaded = false;

View File

@ -22,17 +22,20 @@ import java.util.Deque;
import org.springframework.aot.agent.RecordedInvocation; import org.springframework.aot.agent.RecordedInvocation;
import org.springframework.aot.agent.RecordedInvocationsListener; import org.springframework.aot.agent.RecordedInvocationsListener;
import org.springframework.aot.agent.RecordedInvocationsPublisher; import org.springframework.aot.agent.RecordedInvocationsPublisher;
import org.springframework.aot.agent.RuntimeHintsAgent;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* Invocations relevant to {@link RuntimeHints} recorded during the execution of a block * Invocations relevant to {@link RuntimeHints} recorded during the execution of a block
* of code instrumented by the {@link RuntimeHintsAgent}. * of code instrumented by the {@link org.springframework.aot.agent.RuntimeHintsAgent}.
* *
* @author Brian Clozel * @author Brian Clozel
* @since 6.0 * @since 6.0
* @deprecated as of 7.0 in favor of the {@code -XX:MissingRegistrationReportingMode=Warn} and
* {@code -XX:MissingRegistrationReportingMode=Exit} JVM flags with GraalVM.
*/ */
@Deprecated(forRemoval = true)
@SuppressWarnings("removal")
public final class RuntimeHintsRecorder { public final class RuntimeHintsRecorder {
private final RuntimeHintsInvocationsListener listener; private final RuntimeHintsInvocationsListener listener;
@ -49,7 +52,7 @@ public final class RuntimeHintsRecorder {
*/ */
public static synchronized RuntimeHintsInvocations record(Runnable action) { public static synchronized RuntimeHintsInvocations record(Runnable action) {
Assert.notNull(action, "Runnable action must not be null"); Assert.notNull(action, "Runnable action must not be null");
Assert.state(RuntimeHintsAgent.isLoaded(), "RuntimeHintsAgent must be loaded in the current JVM"); Assert.state(org.springframework.aot.agent.RuntimeHintsAgent.isLoaded(), "RuntimeHintsAgent must be loaded in the current JVM");
RuntimeHintsRecorder recorder = new RuntimeHintsRecorder(); RuntimeHintsRecorder recorder = new RuntimeHintsRecorder();
RecordedInvocationsPublisher.addListener(recorder.listener); RecordedInvocationsPublisher.addListener(recorder.listener);
try { try {

View File

@ -61,32 +61,26 @@ class InstrumentedMethodTests {
} }
@Test @Test
void classGetClassesShouldNotMatchReflectionOnType() { void classForNameShouldNotMatchWhenMissingReflectionOnType() {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_FORNAME)
.withArgument("java.lang.String").returnValue(String.class).build();
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_FORNAME, invocation);
}
@Test
void classGetClassesShouldMatchReflectionOnType() {
hints.reflection().registerType(String.class); hints.reflection().registerType(String.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETCLASSES, this.stringGetClasses);
}
@Test
void classGetClassesShouldMatchPublicClasses() {
hints.reflection().registerType(String.class, MemberCategory.PUBLIC_CLASSES);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCLASSES, this.stringGetClasses); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCLASSES, this.stringGetClasses);
} }
@Test @Test
void classGetClassesShouldMatchDeclaredClasses() { void classGetDeclaredClassesShouldMatchReflectionOnType() {
hints.reflection().registerType(String.class, MemberCategory.DECLARED_CLASSES); hints.reflection().registerType(String.class);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCLASSES, this.stringGetClasses);
}
@Test
void classGetDeclaredClassesShouldMatchDeclaredClassesHint() {
hints.reflection().registerType(String.class, MemberCategory.DECLARED_CLASSES);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCLASSES, this.stringGetDeclaredClasses); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCLASSES, this.stringGetDeclaredClasses);
} }
@Test @Test
void classGetDeclaredClassesShouldNotMatchPublicClassesHint() { void classGetDeclaredClassesShouldNotMatchWhenMissingReflectionOnType() {
hints.reflection().registerType(String.class, MemberCategory.PUBLIC_CLASSES);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDCLASSES, this.stringGetDeclaredClasses); assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDCLASSES, this.stringGetDeclaredClasses);
} }
@ -124,36 +118,28 @@ class InstrumentedMethodTests {
} }
@Test @Test
void classGetConstructorShouldMatchIntrospectPublicConstructorsHint() { void classGetConstructorShouldMatchTypeHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS); hints.reflection().registerType(String.class);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor);
} }
@Test
void classGetConstructorShouldNotMatchWhenMissingTypeHint() {
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor);
}
@Test @Test
void classGetConstructorShouldMatchInvokePublicConstructorsHint() { void classGetConstructorShouldMatchInvokePublicConstructorsHint() {
hints.reflection().registerType(String.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS); hints.reflection().registerType(String.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor);
} }
@Test
void classGetConstructorShouldMatchIntrospectDeclaredConstructorsHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor);
}
@Test @Test
void classGetConstructorShouldMatchInvokeDeclaredConstructorsHint() { void classGetConstructorShouldMatchInvokeDeclaredConstructorsHint() {
hints.reflection().registerType(String.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); hints.reflection().registerType(String.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor);
} }
@Test
void classGetConstructorShouldMatchIntrospectConstructorHint() {
hints.reflection().registerType(String.class,typeHint ->
typeHint.withConstructor(Collections.emptyList(), ExecutableMode.INTROSPECT));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor);
}
@Test @Test
void classGetConstructorShouldMatchInvokeConstructorHint() { void classGetConstructorShouldMatchInvokeConstructorHint() {
hints.reflection().registerType(String.class, typeHint -> hints.reflection().registerType(String.class, typeHint ->
@ -162,23 +148,22 @@ class InstrumentedMethodTests {
} }
@Test @Test
void classGetConstructorsShouldMatchIntrospectPublicConstructorsHint() { void classGetConstructorsShouldMatchTypeHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS); hints.reflection().registerType(String.class);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors);
} }
@Test
void classGetConstructorsShouldNotMatchWhemMissingTypeHint() {
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors);
}
@Test @Test
void classGetConstructorsShouldMatchInvokePublicConstructorsHint() { void classGetConstructorsShouldMatchInvokePublicConstructorsHint() {
hints.reflection().registerType(String.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS); hints.reflection().registerType(String.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors);
} }
@Test
void classGetConstructorsShouldMatchIntrospectDeclaredConstructorsHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors);
}
@Test @Test
void classGetConstructorsShouldMatchInvokeDeclaredConstructorsHint() { void classGetConstructorsShouldMatchInvokeDeclaredConstructorsHint() {
hints.reflection().registerType(String.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); hints.reflection().registerType(String.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
@ -186,36 +171,16 @@ class InstrumentedMethodTests {
} }
@Test @Test
void classGetConstructorsShouldNotMatchTypeReflectionHint() { void classGetDeclaredConstructorShouldMatchTypeHint() {
hints.reflection().registerType(String.class); hints.reflection().registerType(String.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors);
}
@Test
void classGetConstructorsShouldNotMatchConstructorReflectionHint() throws Exception {
hints.reflection().registerConstructor(String.class.getConstructor(), ExecutableMode.INVOKE);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors);
}
@Test
void classGetDeclaredConstructorShouldMatchIntrospectDeclaredConstructorsHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor);
} }
@Test @Test
void classGetDeclaredConstructorShouldNotMatchIntrospectPublicConstructorsHint() { void classGetDeclaredConstructorShouldNotMatchWhenMissingTypeHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor); assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor);
} }
@Test
void classGetDeclaredConstructorShouldMatchIntrospectConstructorHint() {
hints.reflection().registerType(String.class, typeHint ->
typeHint.withConstructor(TypeReference.listOf(byte[].class, byte.class), ExecutableMode.INTROSPECT));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor);
}
@Test @Test
void classGetDeclaredConstructorShouldMatchInvokeConstructorHint() { void classGetDeclaredConstructorShouldMatchInvokeConstructorHint() {
hints.reflection().registerType(String.class, typeHint -> hints.reflection().registerType(String.class, typeHint ->
@ -223,12 +188,6 @@ class InstrumentedMethodTests {
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor);
} }
@Test
void classGetDeclaredConstructorsShouldMatchIntrospectDeclaredConstructorsHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTORS, this.stringGetDeclaredConstructors);
}
@Test @Test
void classGetDeclaredConstructorsShouldMatchInvokeDeclaredConstructorsHint() { void classGetDeclaredConstructorsShouldMatchInvokeDeclaredConstructorsHint() {
hints.reflection().registerType(String.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); hints.reflection().registerType(String.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
@ -236,20 +195,13 @@ class InstrumentedMethodTests {
} }
@Test @Test
void classGetDeclaredConstructorsShouldNotMatchIntrospectPublicConstructorsHint() { void classGetDeclaredConstructorsShouldMatchTypeReflectionHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTORS, this.stringGetDeclaredConstructors);
}
@Test
void classGetDeclaredConstructorsShouldNotMatchTypeReflectionHint() {
hints.reflection().registerType(String.class); hints.reflection().registerType(String.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTORS, this.stringGetDeclaredConstructors); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTORS, this.stringGetDeclaredConstructors);
} }
@Test @Test
void classGetDeclaredConstructorsShouldNotMatchConstructorReflectionHint() throws Exception { void classGetDeclaredConstructorsShouldNotMatchWhenMissingTypeReflectionHint() {
hints.reflection().registerConstructor(String.class.getConstructor(), ExecutableMode.INVOKE);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTORS, this.stringGetDeclaredConstructors); assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTORS, this.stringGetDeclaredConstructors);
} }
@ -262,15 +214,6 @@ class InstrumentedMethodTests {
assertThatInvocationMatches(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE, invocation); assertThatInvocationMatches(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE, invocation);
} }
@Test
void constructorNewInstanceShouldNotMatchIntrospectHintOnConstructor() throws NoSuchMethodException {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE)
.onInstance(String.class.getConstructor()).returnValue("").build();
hints.reflection().registerType(String.class, typeHint ->
typeHint.withConstructor(Collections.emptyList(), ExecutableMode.INTROSPECT));
assertThatInvocationDoesNotMatch(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE, invocation);
}
} }
@Nested @Nested
@ -295,22 +238,8 @@ class InstrumentedMethodTests {
} }
@Test @Test
void classGetDeclaredMethodShouldMatchIntrospectDeclaredMethodsHint() { void classGetDeclaredMethodShouldMatchTypeReflectionHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_DECLARED_METHODS); hints.reflection().registerType(String.class);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod);
}
@Test
void classGetDeclaredMethodShouldNotMatchIntrospectPublicMethodsHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_METHODS);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod);
}
@Test
void classGetDeclaredMethodShouldMatchIntrospectMethodHint() {
List<TypeReference> parameterTypes = TypeReference.listOf(int.class, float.class);
hints.reflection().registerType(String.class, typeHint ->
typeHint.withMethod("scale", parameterTypes, ExecutableMode.INTROSPECT));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod);
} }
@ -322,12 +251,6 @@ class InstrumentedMethodTests {
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod);
} }
@Test
void classGetDeclaredMethodsShouldMatchIntrospectDeclaredMethodsHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_DECLARED_METHODS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHODS, this.stringGetScaleMethod);
}
@Test @Test
void classGetDeclaredMethodsShouldMatchInvokeDeclaredMethodsHint() { void classGetDeclaredMethodsShouldMatchInvokeDeclaredMethodsHint() {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETDECLAREDMETHODS).onInstance(String.class).build(); RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETDECLAREDMETHODS).onInstance(String.class).build();
@ -336,32 +259,8 @@ class InstrumentedMethodTests {
} }
@Test @Test
void classGetDeclaredMethodsShouldNotMatchIntrospectPublicMethodsHint() { void classGetMethodsShouldMatchTypeReflectionHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_METHODS);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDMETHODS, this.stringGetScaleMethod);
}
@Test
void classGetDeclaredMethodsShouldNotMatchTypeReflectionHint() {
hints.reflection().registerType(String.class); hints.reflection().registerType(String.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDMETHODS, this.stringGetScaleMethod);
}
@Test
void classGetDeclaredMethodsShouldNotMatchMethodReflectionHint() throws Exception {
hints.reflection().registerMethod(String.class.getMethod("toString"), ExecutableMode.INVOKE);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDMETHODS, this.stringGetScaleMethod);
}
@Test
void classGetMethodsShouldMatchIntrospectDeclaredMethodsHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_DECLARED_METHODS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHODS, this.stringGetMethods);
}
@Test
void classGetMethodsShouldMatchIntrospectPublicMethodsHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_METHODS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHODS, this.stringGetMethods); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHODS, this.stringGetMethods);
} }
@ -379,25 +278,13 @@ class InstrumentedMethodTests {
@Test @Test
void classGetMethodsShouldNotMatchForWrongType() { void classGetMethodsShouldNotMatchForWrongType() {
hints.reflection().registerType(Integer.class, MemberCategory.INTROSPECT_PUBLIC_METHODS); hints.reflection().registerType(Integer.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHODS, this.stringGetMethods); assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHODS, this.stringGetMethods);
} }
@Test @Test
void classGetMethodsShouldNotMatchTypeReflectionHint() { void classGetMethodShouldMatchReflectionTypeHint() {
hints.reflection().registerType(String.class); hints.reflection().registerType(String.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHODS, this.stringGetMethods);
}
@Test
void classGetMethodsShouldNotMatchMethodReflectionHint() throws Exception {
hints.reflection().registerMethod(String.class.getMethod("toString"), ExecutableMode.INVOKE);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHODS, this.stringGetMethods);
}
@Test
void classGetMethodShouldMatchIntrospectPublicMethodsHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_METHODS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod);
} }
@ -407,25 +294,6 @@ class InstrumentedMethodTests {
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod);
} }
@Test
void classGetMethodShouldNotMatchIntrospectDeclaredMethodsHint() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_DECLARED_METHODS);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod);
}
@Test
void classGetMethodShouldNotMatchInvokeDeclaredMethodsHint() {
hints.reflection().registerType(String.class, MemberCategory.INVOKE_DECLARED_METHODS);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod);
}
@Test
void classGetMethodShouldMatchIntrospectMethodHint() {
hints.reflection().registerType(String.class, typeHint ->
typeHint.withMethod("toString", Collections.emptyList(), ExecutableMode.INTROSPECT));
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod);
}
@Test @Test
void classGetMethodShouldMatchInvokeMethodHint() { void classGetMethodShouldMatchInvokeMethodHint() {
hints.reflection().registerType(String.class, typeHint -> hints.reflection().registerType(String.class, typeHint ->
@ -433,21 +301,9 @@ class InstrumentedMethodTests {
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod);
} }
@Test
void classGetMethodShouldNotMatchIntrospectPublicMethodsHintWhenPrivate() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_PUBLIC_METHODS);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetScaleMethod);
}
@Test
void classGetMethodShouldMatchIntrospectDeclaredMethodsHintWhenPrivate() {
hints.reflection().registerType(String.class, MemberCategory.INTROSPECT_DECLARED_METHODS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetScaleMethod);
}
@Test @Test
void classGetMethodShouldNotMatchForWrongType() { void classGetMethodShouldNotMatchForWrongType() {
hints.reflection().registerType(Integer.class, MemberCategory.INTROSPECT_PUBLIC_METHODS); hints.reflection().registerType(Integer.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod); assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod);
} }
@ -461,11 +317,10 @@ class InstrumentedMethodTests {
} }
@Test @Test
void methodInvokeShouldNotMatchIntrospectHintOnMethod() throws NoSuchMethodException { void methodInvokeShouldNotMatchReflectionTypeHint() throws NoSuchMethodException {
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.withMethod("toString", Collections.emptyList(), ExecutableMode.INTROSPECT));
assertThatInvocationDoesNotMatch(InstrumentedMethod.METHOD_INVOKE, invocation); assertThatInvocationDoesNotMatch(InstrumentedMethod.METHOD_INVOKE, invocation);
} }
@ -496,17 +351,11 @@ class InstrumentedMethodTests {
} }
@Test @Test
void classGetDeclaredFieldShouldMatchDeclaredFieldsHint() { void classGetDeclaredFieldShouldMatchTypeReflectionHint() {
hints.reflection().registerType(String.class, MemberCategory.DECLARED_FIELDS); hints.reflection().registerType(String.class);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDFIELD, this.stringGetDeclaredField); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDFIELD, this.stringGetDeclaredField);
} }
@Test
void classGetDeclaredFieldShouldNotMatchPublicFieldsHint() {
hints.reflection().registerType(String.class, typeHint -> typeHint.withMembers(MemberCategory.PUBLIC_FIELDS));
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDFIELD, this.stringGetDeclaredField);
}
@Test @Test
void classGetDeclaredFieldShouldMatchFieldHint() { void classGetDeclaredFieldShouldMatchFieldHint() {
hints.reflection().registerType(String.class, typeHint -> typeHint.withField("value")); hints.reflection().registerType(String.class, typeHint -> typeHint.withField("value"));
@ -514,41 +363,23 @@ class InstrumentedMethodTests {
} }
@Test @Test
void classGetDeclaredFieldsShouldMatchDeclaredFieldsHint() { void classGetDeclaredFieldsShouldMatchTypeReflectionHint() {
hints.reflection().registerType(String.class, MemberCategory.DECLARED_FIELDS); hints.reflection().registerType(String.class);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDFIELDS, this.stringGetDeclaredFields); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDFIELDS, this.stringGetDeclaredFields);
} }
@Test @Test
void classGetDeclaredFieldsShouldNotMatchPublicFieldsHint() { void classGetDeclaredFieldsShouldMatchFieldHint() throws Exception {
hints.reflection().registerType(String.class, MemberCategory.PUBLIC_FIELDS);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDFIELDS, this.stringGetDeclaredFields);
}
@Test
void classGetDeclaredFieldsShouldNotMatchTypeHint() {
hints.reflection().registerType(String.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDFIELDS, this.stringGetDeclaredFields);
}
@Test
void classGetDeclaredFieldsShouldNotMatchFieldHint() throws Exception {
hints.reflection().registerField(String.class.getDeclaredField("value")); hints.reflection().registerField(String.class.getDeclaredField("value"));
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETDECLAREDFIELDS, this.stringGetDeclaredFields); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDFIELDS, this.stringGetDeclaredFields);
} }
@Test @Test
void classGetFieldShouldMatchPublicFieldsHint() { void classGetFieldShouldMatchTypeReflectionHint() {
hints.reflection().registerType(PublicField.class, MemberCategory.PUBLIC_FIELDS); hints.reflection().registerType(PublicField.class);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETFIELD, this.getPublicField); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETFIELD, this.getPublicField);
} }
@Test
void classGetFieldShouldNotMatchDeclaredFieldsHint() {
hints.reflection().registerType(PublicField.class, MemberCategory.DECLARED_FIELDS);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETFIELD, this.getPublicField);
}
@Test @Test
void classGetFieldShouldMatchFieldHint() { void classGetFieldShouldMatchFieldHint() {
hints.reflection().registerType(PublicField.class, typeHint -> typeHint.withField("field")); hints.reflection().registerType(PublicField.class, typeHint -> typeHint.withField("field"));
@ -559,53 +390,30 @@ class InstrumentedMethodTests {
void classGetFieldShouldNotMatchPublicFieldsHintWhenPrivate() { void classGetFieldShouldNotMatchPublicFieldsHintWhenPrivate() {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETFIELD) RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETFIELD)
.onInstance(String.class).withArgument("value").returnValue(null).build(); .onInstance(String.class).withArgument("value").returnValue(null).build();
hints.reflection().registerType(String.class, MemberCategory.PUBLIC_FIELDS); hints.reflection().registerType(String.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETFIELD, invocation); assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETFIELD, invocation);
} }
@Test
void classGetFieldShouldMatchDeclaredFieldsHintWhenPrivate() throws NoSuchFieldException {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETFIELD)
.onInstance(String.class).withArgument("value").returnValue(String.class.getDeclaredField("value")).build();
hints.reflection().registerType(String.class, MemberCategory.DECLARED_FIELDS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETFIELD, invocation);
}
@Test @Test
void classGetFieldShouldNotMatchForWrongType() { void classGetFieldShouldNotMatchForWrongType() {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETFIELD) RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETFIELD)
.onInstance(String.class).withArgument("value").returnValue(null).build(); .onInstance(String.class).withArgument("value").returnValue(null).build();
hints.reflection().registerType(Integer.class, MemberCategory.DECLARED_FIELDS); hints.reflection().registerType(Integer.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETFIELD, invocation); assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETFIELD, invocation);
} }
@Test @Test
void classGetFieldsShouldMatchPublicFieldsHint() { void classGetFieldsShouldMatchReflectionHint() {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETFIELDS) RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETFIELDS)
.onInstance(PublicField.class).build(); .onInstance(PublicField.class).build();
hints.reflection().registerType(PublicField.class, MemberCategory.PUBLIC_FIELDS); hints.reflection().registerType(PublicField.class);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETFIELDS, invocation); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETFIELDS, invocation);
} }
@Test @Test
void classGetFieldsShouldMatchDeclaredFieldsHint() { void classGetFieldsShouldMatchTypeHint() {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETFIELDS)
.onInstance(PublicField.class).build();
hints.reflection().registerType(PublicField.class, MemberCategory.DECLARED_FIELDS);
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETFIELDS, invocation);
}
@Test
void classGetFieldsShouldNotMatchTypeHint() {
hints.reflection().registerType(String.class); hints.reflection().registerType(String.class);
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETFIELDS, this.stringGetFields); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETFIELDS, this.stringGetFields);
}
@Test
void classGetFieldsShouldNotMatchFieldHint() throws Exception {
hints.reflection().registerField(String.class.getDeclaredField("value"));
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETFIELDS, this.stringGetFields);
} }
} }
@ -634,7 +442,7 @@ class InstrumentedMethodTests {
void classGetResourceShouldMatchResourcePatternWhenAbsolute() { void classGetResourceShouldMatchResourcePatternWhenAbsolute() {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETRESOURCE) RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETRESOURCE)
.onInstance(InstrumentedMethodTests.class).withArgument("/some/path/resource.txt").build(); .onInstance(InstrumentedMethodTests.class).withArgument("/some/path/resource.txt").build();
hints.resources().registerPattern("some/*"); hints.resources().registerPattern("some/**");
assertThatInvocationMatches(InstrumentedMethod.CLASS_GETRESOURCE, invocation); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETRESOURCE, invocation);
} }
@ -655,11 +463,11 @@ class InstrumentedMethodTests {
} }
@Test @Test
void classGetResourceShouldNotMatchResourcePatternWhenExcluded() { void classGetResourceShouldMatchWhenGlobPattern() {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETRESOURCE) RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CLASS_GETRESOURCE)
.onInstance(InstrumentedMethodTests.class).withArgument("/some/path/resource.txt").build(); .onInstance(InstrumentedMethodTests.class).withArgument("/some/path/resource.txt").build();
hints.resources().registerPattern(resourceHint -> resourceHint.includes("some/*").excludes("some/path/*")); hints.resources().registerPattern(resourceHint -> resourceHint.includes("some/**"));
assertThatInvocationDoesNotMatch(InstrumentedMethod.CLASS_GETRESOURCE, invocation); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETRESOURCE, invocation);
} }
} }