diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index eab7b903a71a..34e790a80211 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -1194,22 +1194,21 @@ private static boolean methodTypeFromDescriptor(JNIEnvironment jni, JNIObjectHan return true; } - /** - * This method should be intercepted when we are predefining a lambda class. This is the only - * spot in the lambda-class creation pipeline where we can get lambda-class bytecode so the - * class can be predefined. We do not want to predefine all lambda classes, but only the ones - * that are actually created at runtime, so we have a method that checks wheter the lambda - * should be predefined or not. - */ - private static boolean onMethodHandleClassFileInit(JNIEnvironment jni, JNIObjectHandle thread, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state) { + private record LambdaClassNameAndBytecode(String lambdaClassName, byte[] lambdaClassBytecode) { + } + + private static LambdaClassNameAndBytecode getLambdaClassNameAndBytecodeFromThread(JNIEnvironment jni, JNIObjectHandle thread, int bytecodeArgumentIndex) { + LambdaClassNameAndBytecode emptyLambdaClassNameAndBytecode = new LambdaClassNameAndBytecode(null, null); + String className = Support.fromJniString(jni, getObjectArgument(thread, 1)); + assert className != null; if (LambdaUtils.isLambdaClassName(className)) { if (shouldIgnoreLambdaClassForPredefinition(jni)) { - return true; + return emptyLambdaClassNameAndBytecode; } - JNIObjectHandle bytesArray = getObjectArgument(thread, 3); + JNIObjectHandle bytesArray = getObjectArgument(thread, bytecodeArgumentIndex); int length = jniFunctions().getGetArrayLength().invoke(jni, bytesArray); byte[] data = new byte[length]; @@ -1222,9 +1221,29 @@ private static boolean onMethodHandleClassFileInit(JNIEnvironment jni, JNIObject } className += Digest.digest(data); - tracer.traceCall("classloading", "onMethodHandleClassFileInit", null, null, null, null, state.getFullStackTraceOrNull(), className, data); + return new LambdaClassNameAndBytecode(className, data); } } + + return emptyLambdaClassNameAndBytecode; + } + + /** + * This method should be intercepted on JDK 24 or later when we are predefining a lambda class. + * We do not want to predefine all lambda classes, but only the ones that are actually created + * at runtime, so we have a method that checks whether the lambda should be predefined or not. + */ + private static boolean makeHiddenClassDefiner(JNIEnvironment jni, JNIObjectHandle thread, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state) { + LambdaClassNameAndBytecode lambdaClassNameAndBytecode = getLambdaClassNameAndBytecodeFromThread(jni, thread, 2); + return lambdaPredefinition(state, lambdaClassNameAndBytecode.lambdaClassName(), lambdaClassNameAndBytecode.lambdaClassBytecode()); + } + + private static boolean lambdaPredefinition(InterceptedState state, String className, byte[] data) { + if (className == null) { + return false; + } + + tracer.traceCall("classloading", "lambdaPredefinition", null, null, null, null, state.getFullStackTraceOrNull(), className, data); return true; } @@ -1923,7 +1942,8 @@ private static boolean allocateInstance(JNIEnvironment jni, JNIObjectHandle thre }; private static final BreakpointSpecification[] CLASS_PREDEFINITION_BREAKPOINT_SPECIFICATIONS = { - optionalBrk("java/lang/invoke/MethodHandles$Lookup$ClassFile", "", "(Ljava/lang/String;I[B)V", BreakpointInterceptor::onMethodHandleClassFileInit), + optionalBrk("java/lang/invoke/MethodHandles$Lookup", "makeHiddenClassDefiner", + "(Ljava/lang/String;[BZLjdk/internal/util/ClassFileDumper;I)Ljava/lang/invoke/MethodHandles$Lookup$ClassDefiner;", BreakpointInterceptor::makeHiddenClassDefiner) }; private static BreakpointSpecification brk(String className, String methodName, String signature, BreakpointHandler handler) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ClassLoadingProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ClassLoadingProcessor.java index 73b52ff16292..644237fa4427 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ClassLoadingProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ClassLoadingProcessor.java @@ -42,7 +42,7 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat String function = (String) entry.get("function"); List args = (List) entry.get("args"); - if ("onClassFileLoadHook".equals(function) || "onMethodHandleClassFileInit".equals(function)) { + if ("onClassFileLoadHook".equals(function) || "lambdaPredefinition".equals(function)) { expectSize(args, 2); String nameInfo = (String) args.get(0); byte[] classData = asBinary(args.get(1));