From 3c22404bce604c815954745dbd311bb24a2b44cd Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 31 Aug 2022 15:19:31 +0200 Subject: [PATCH] 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 --- .../aot/RuntimeHintsAgentTests.java | 32 +++++++++++++++++-- .../aot/agent/InstrumentedBridgeMethods.java | 8 +++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/integration-tests/src/test/java/org/springframework/aot/RuntimeHintsAgentTests.java b/integration-tests/src/test/java/org/springframework/aot/RuntimeHintsAgentTests.java index a639b0ff83..aa2c42eb29 100644 --- a/integration-tests/src/test/java/org/springframework/aot/RuntimeHintsAgentTests.java +++ b/integration-tests/src/test/java/org/springframework/aot/RuntimeHintsAgentTests.java @@ -54,11 +54,14 @@ public class RuntimeHintsAgentTests { private static Method toStringMethod; + private static Method privateGreetMethod; + @BeforeAll public static void classSetup() throws NoSuchMethodException { defaultConstructor = String.class.getConstructor(); toStringMethod = String.class.getMethod("toString"); + privateGreetMethod = PrivateClass.class.getDeclaredMethod("greet"); } @@ -79,6 +82,7 @@ public class RuntimeHintsAgentTests { Class.forName("java.lang.String"); } catch (ClassNotFoundException e) { + throw new RuntimeException(e); } }, MethodReference.of(Class.class, "forName")), Arguments.of((Runnable) () -> String.class.getClasses(), MethodReference.of(Class.class, "getClasses")), @@ -87,6 +91,7 @@ public class RuntimeHintsAgentTests { String.class.getConstructor(); } catch (NoSuchMethodException e) { + throw new RuntimeException(e); } }, MethodReference.of(Class.class, "getConstructor")), Arguments.of((Runnable) () -> String.class.getConstructors(), MethodReference.of(Class.class, "getConstructors")), @@ -96,14 +101,16 @@ public class RuntimeHintsAgentTests { String.class.getDeclaredConstructor(); } catch (NoSuchMethodException e) { + throw new RuntimeException(e); } }, MethodReference.of(Class.class, "getDeclaredConstructor")), Arguments.of((Runnable) () -> String.class.getDeclaredConstructors(), MethodReference.of(Class.class, "getDeclaredConstructors")), Arguments.of((Runnable) () -> { try { - String.class.getDeclaredField("test"); + String.class.getDeclaredField("value"); } catch (NoSuchFieldException e) { + throw new RuntimeException(e); } }, MethodReference.of(Class.class, "getDeclaredField")), Arguments.of((Runnable) () -> String.class.getDeclaredFields(), MethodReference.of(Class.class, "getDeclaredFields")), @@ -112,12 +119,13 @@ public class RuntimeHintsAgentTests { String.class.getDeclaredMethod("toString"); } catch (NoSuchMethodException e) { + throw new RuntimeException(e); } }, MethodReference.of(Class.class, "getDeclaredMethod")), Arguments.of((Runnable) () -> String.class.getDeclaredMethods(), MethodReference.of(Class.class, "getDeclaredMethods")), Arguments.of((Runnable) () -> { try { - String.class.getField("test"); + String.class.getField("value"); } catch (NoSuchFieldException e) { } @@ -128,6 +136,7 @@ public class RuntimeHintsAgentTests { String.class.getMethod("toString"); } catch (NoSuchMethodException e) { + throw new RuntimeException(e); } }, MethodReference.of(Class.class, "getMethod")), Arguments.of((Runnable) () -> String.class.getMethods(), MethodReference.of(Class.class, "getMethods")), @@ -136,6 +145,7 @@ public class RuntimeHintsAgentTests { classLoader.loadClass("java.lang.String"); } catch (ClassNotFoundException e) { + throw new RuntimeException(e); } }, MethodReference.of(ClassLoader.class, "loadClass")), Arguments.of((Runnable) () -> { @@ -143,6 +153,7 @@ public class RuntimeHintsAgentTests { defaultConstructor.newInstance(); } catch (Exception e) { + throw new RuntimeException(e); } }, MethodReference.of(Constructor.class, "newInstance")), Arguments.of((Runnable) () -> { @@ -150,6 +161,15 @@ public class RuntimeHintsAgentTests { toStringMethod.invoke(""); } 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")) ); @@ -265,4 +285,12 @@ public class RuntimeHintsAgentTests { } + private static class PrivateClass { + + private String greet() { + return "hello"; + } + + } + } diff --git a/spring-core-test/src/main/java/org/springframework/aot/agent/InstrumentedBridgeMethods.java b/spring-core-test/src/main/java/org/springframework/aot/agent/InstrumentedBridgeMethods.java index 89c6168157..c3c2c61ab2 100644 --- a/spring-core-test/src/main/java/org/springframework/aot/agent/InstrumentedBridgeMethods.java +++ b/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 { Object result = null; + boolean accessibilityChanged = false; try { + if (!Modifier.isPublic(method.getModifiers())) { + method.setAccessible(true); + accessibilityChanged = true; + } result = method.invoke(object, arguments); } finally { RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_INVOKE) .onInstance(method).withArguments(object, arguments).returnValue(result).build(); RecordedInvocationsPublisher.publish(invocation); + if (accessibilityChanged) { + method.setAccessible(false); + } } return result; }