Browse Source

Fix RuntimeHintsAgent instrumentation for method invocation

Prior to this commit, the `RuntimeHintsAgent` would instrument
`Method.invoke` in a way that fails when invoked methods are not public.

This commit ensures that before delegating the invocation call, the
instrumentation makes the method accessible before delegating the call.

Fixes gh-29046
pull/29050/head
Brian Clozel 3 years ago
parent
commit
3c22404bce
  1. 32
      integration-tests/src/test/java/org/springframework/aot/RuntimeHintsAgentTests.java
  2. 8
      spring-core-test/src/main/java/org/springframework/aot/agent/InstrumentedBridgeMethods.java

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

@ -54,11 +54,14 @@ public class RuntimeHintsAgentTests {
private static Method toStringMethod; private static Method toStringMethod;
private static Method privateGreetMethod;
@BeforeAll @BeforeAll
public static void classSetup() throws NoSuchMethodException { public static void classSetup() throws NoSuchMethodException {
defaultConstructor = String.class.getConstructor(); defaultConstructor = String.class.getConstructor();
toStringMethod = String.class.getMethod("toString"); toStringMethod = String.class.getMethod("toString");
privateGreetMethod = PrivateClass.class.getDeclaredMethod("greet");
} }
@ -79,6 +82,7 @@ public class RuntimeHintsAgentTests {
Class.forName("java.lang.String"); Class.forName("java.lang.String");
} }
catch (ClassNotFoundException e) { catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} }
}, MethodReference.of(Class.class, "forName")), }, MethodReference.of(Class.class, "forName")),
Arguments.of((Runnable) () -> String.class.getClasses(), MethodReference.of(Class.class, "getClasses")), Arguments.of((Runnable) () -> String.class.getClasses(), MethodReference.of(Class.class, "getClasses")),
@ -87,6 +91,7 @@ public class RuntimeHintsAgentTests {
String.class.getConstructor(); String.class.getConstructor();
} }
catch (NoSuchMethodException e) { catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} }
}, MethodReference.of(Class.class, "getConstructor")), }, MethodReference.of(Class.class, "getConstructor")),
Arguments.of((Runnable) () -> String.class.getConstructors(), MethodReference.of(Class.class, "getConstructors")), Arguments.of((Runnable) () -> String.class.getConstructors(), MethodReference.of(Class.class, "getConstructors")),
@ -96,14 +101,16 @@ public class RuntimeHintsAgentTests {
String.class.getDeclaredConstructor(); String.class.getDeclaredConstructor();
} }
catch (NoSuchMethodException e) { catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} }
}, MethodReference.of(Class.class, "getDeclaredConstructor")), }, MethodReference.of(Class.class, "getDeclaredConstructor")),
Arguments.of((Runnable) () -> String.class.getDeclaredConstructors(), MethodReference.of(Class.class, "getDeclaredConstructors")), Arguments.of((Runnable) () -> String.class.getDeclaredConstructors(), MethodReference.of(Class.class, "getDeclaredConstructors")),
Arguments.of((Runnable) () -> { Arguments.of((Runnable) () -> {
try { try {
String.class.getDeclaredField("test"); String.class.getDeclaredField("value");
} }
catch (NoSuchFieldException e) { catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} }
}, MethodReference.of(Class.class, "getDeclaredField")), }, MethodReference.of(Class.class, "getDeclaredField")),
Arguments.of((Runnable) () -> String.class.getDeclaredFields(), MethodReference.of(Class.class, "getDeclaredFields")), Arguments.of((Runnable) () -> String.class.getDeclaredFields(), MethodReference.of(Class.class, "getDeclaredFields")),
@ -112,12 +119,13 @@ public class RuntimeHintsAgentTests {
String.class.getDeclaredMethod("toString"); String.class.getDeclaredMethod("toString");
} }
catch (NoSuchMethodException e) { catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} }
}, MethodReference.of(Class.class, "getDeclaredMethod")), }, MethodReference.of(Class.class, "getDeclaredMethod")),
Arguments.of((Runnable) () -> String.class.getDeclaredMethods(), MethodReference.of(Class.class, "getDeclaredMethods")), Arguments.of((Runnable) () -> String.class.getDeclaredMethods(), MethodReference.of(Class.class, "getDeclaredMethods")),
Arguments.of((Runnable) () -> { Arguments.of((Runnable) () -> {
try { try {
String.class.getField("test"); String.class.getField("value");
} }
catch (NoSuchFieldException e) { catch (NoSuchFieldException e) {
} }
@ -128,6 +136,7 @@ public class RuntimeHintsAgentTests {
String.class.getMethod("toString"); String.class.getMethod("toString");
} }
catch (NoSuchMethodException e) { catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} }
}, MethodReference.of(Class.class, "getMethod")), }, MethodReference.of(Class.class, "getMethod")),
Arguments.of((Runnable) () -> String.class.getMethods(), MethodReference.of(Class.class, "getMethods")), Arguments.of((Runnable) () -> String.class.getMethods(), MethodReference.of(Class.class, "getMethods")),
@ -136,6 +145,7 @@ public class RuntimeHintsAgentTests {
classLoader.loadClass("java.lang.String"); classLoader.loadClass("java.lang.String");
} }
catch (ClassNotFoundException e) { catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} }
}, MethodReference.of(ClassLoader.class, "loadClass")), }, MethodReference.of(ClassLoader.class, "loadClass")),
Arguments.of((Runnable) () -> { Arguments.of((Runnable) () -> {
@ -143,6 +153,7 @@ public class RuntimeHintsAgentTests {
defaultConstructor.newInstance(); defaultConstructor.newInstance();
} }
catch (Exception e) { catch (Exception e) {
throw new RuntimeException(e);
} }
}, MethodReference.of(Constructor.class, "newInstance")), }, MethodReference.of(Constructor.class, "newInstance")),
Arguments.of((Runnable) () -> { Arguments.of((Runnable) () -> {
@ -150,6 +161,15 @@ public class RuntimeHintsAgentTests {
toStringMethod.invoke(""); toStringMethod.invoke("");
} }
catch (Exception e) { catch (Exception e) {
throw new RuntimeException(e);
}
}, MethodReference.of(Method.class, "invoke")),
Arguments.of((Runnable) () -> {
try {
privateGreetMethod.invoke(new PrivateClass());
}
catch (Exception e) {
throw new RuntimeException(e);
} }
}, MethodReference.of(Method.class, "invoke")) }, MethodReference.of(Method.class, "invoke"))
); );
@ -265,4 +285,12 @@ public class RuntimeHintsAgentTests {
} }
private static class PrivateClass {
private String greet() {
return "hello";
}
}
} }

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

@ -337,13 +337,21 @@ public abstract class InstrumentedBridgeMethods {
public static Object methodinvoke(Method method, Object object, Object... arguments) throws InvocationTargetException, IllegalAccessException { public static Object methodinvoke(Method method, Object object, Object... arguments) throws InvocationTargetException, IllegalAccessException {
Object result = null; Object result = null;
boolean accessibilityChanged = false;
try { try {
if (!Modifier.isPublic(method.getModifiers())) {
method.setAccessible(true);
accessibilityChanged = true;
}
result = method.invoke(object, arguments); result = method.invoke(object, arguments);
} }
finally { finally {
RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_INVOKE) RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_INVOKE)
.onInstance(method).withArguments(object, arguments).returnValue(result).build(); .onInstance(method).withArguments(object, arguments).returnValue(result).build();
RecordedInvocationsPublisher.publish(invocation); RecordedInvocationsPublisher.publish(invocation);
if (accessibilityChanged) {
method.setAccessible(false);
}
} }
return result; return result;
} }

Loading…
Cancel
Save