From f53346cbfd2d12a575026f9bd7553e083406b0ef Mon Sep 17 00:00:00 2001 From: Strahinja Stanojevic Date: Mon, 17 Oct 2022 16:42:30 +0200 Subject: [PATCH 1/2] Lambda class predefinition support --- .gitignore | 1 + .../jdk/graal/compiler/java/LambdaUtils.java | 53 +++++++- sdk/mx.sdk/mx_sdk_benchmark.py | 5 +- .../mx_substratevm_benchmark.py | 36 +++++- substratevm/mx.substratevm/suite.py | 1 + .../pointsto/reports/CallTreePrinter.java | 2 +- .../svm/agent/BreakpointInterceptor.java | 87 ++++++++++++- .../agent/NativeImageAgentJNIHandleSet.java | 32 +++++ .../trace/ClassLoadingProcessor.java | 2 +- .../core/hub/PredefinedClassesSupport.java | 122 ++++++++++++++++++ .../jdk/Target_java_lang_ClassLoader.java | 6 + .../serialize/SerializationSupport.java | 13 +- .../svm/hosted/ClassPredefinitionFeature.java | 67 +++++++++- .../src/com/oracle/svm/hosted/SVMHost.java | 3 + .../lambda/StableLambdaProxyNameFeature.java | 2 +- .../serialize/SerializationFeature.java | 3 +- vm/mx.vm/mx_vm_benchmark.py | 4 + 17 files changed, 419 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index b1b19b6cdbbe..d31700544dfb 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ *.swo *.swp *.zip +*.bgv .DS_Store .checkstyle .classpath diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/LambdaUtils.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/LambdaUtils.java index 52a7fd119eda..dd8da491e779 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/LambdaUtils.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/LambdaUtils.java @@ -125,13 +125,20 @@ public static String findStableLambdaName(ClassInitializationPlugin cip, Provide return createStableLambdaName(lambdaType, invokedMethods); } + /** + * Checks if the passed type is lambda class type based on set flags and the type name + * + * @param type type to be checked + * @return true if the passed type is lambda type, false otherwise + */ + public static boolean isLambdaType(ResolvedJavaType type) { String typeName = type.getName(); return type.isFinalFlagSet() && isLambdaName(typeName); } public static boolean isLambdaName(String name) { - return name.contains(LAMBDA_CLASS_NAME_SUBSTRING) && lambdaMatcher(name).find(); + return isLambdaClassName(name) && lambdaMatcher(name).find(); } private static String createStableLambdaName(ResolvedJavaType lambdaType, List targetMethods) { @@ -157,17 +164,59 @@ public static String toHex(byte[] data) { return r.toString(); } + /** + * Hashing a passed string parameter using SHA-1 hashing algorithm + * + * @param value string to be hashed + * @return hexadecimal hashed value of the passed string parameter + */ public static String digest(String value) { + return digest(value.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Hashing a passed byte array parameter using SHA-1 hashing algorithm + * + * @param bytes byte array to be hashed + * @return hexadecimal hashed value of the passed byte array parameter + */ + public static String digest(byte[] bytes) { try { MessageDigest md = MessageDigest.getInstance("SHA-1"); - md.update(value.getBytes(StandardCharsets.UTF_8)); + md.update(bytes); return toHex(md.digest()); } catch (NoSuchAlgorithmException ex) { throw new JVMCIError(ex); } } + /** + * Extracts lambda capturing class name from the lambda class name + * + * @param className name of the lambda class + * @return name of the lambda capturing class + */ public static String capturingClass(String className) { return className.split(LambdaUtils.SERIALIZATION_TEST_LAMBDA_CLASS_SPLIT_PATTERN)[0]; } + + /** + * Checks if the passed class is lambda class + * + * @param clazz class to be checked + * @return true if the clazz is lambda class, false instead + */ + public static boolean isLambdaClass(Class clazz) { + return isLambdaClassName(clazz.getName()); + } + + /** + * Checks if the passed class name is lambda class name + * + * @param className name of the class + * @return true if the className is lambda class name, false instead + */ + public static boolean isLambdaClassName(String className) { + return className.contains(LAMBDA_CLASS_NAME_SUBSTRING); + } } diff --git a/sdk/mx.sdk/mx_sdk_benchmark.py b/sdk/mx.sdk/mx_sdk_benchmark.py index dd9425bc410e..4029d71014b0 100644 --- a/sdk/mx.sdk/mx_sdk_benchmark.py +++ b/sdk/mx.sdk/mx_sdk_benchmark.py @@ -221,7 +221,10 @@ def extra_agentlib_options(self, benchmark, args, image_run_args): The returned options are added to the agentlib:native-image-agent option list. The config-output-dir is configured by the benchmark runner and cannot be overridden. """ - return [] + + # All Renaissance Spark benchmarks require lambda class predefinition, so we need this additional option that + # is used for the class predefinition feature. See GR-37506 + return ['experimental-class-define-support'] if (benchmark in ['chi-square', 'gauss-mix', 'movie-lens', 'page-rank']) else [] def extra_profile_run_arg(self, benchmark, args, image_run_args, should_strip_run_args): """Returns all arguments passed to the profiling run. diff --git a/substratevm/mx.substratevm/mx_substratevm_benchmark.py b/substratevm/mx.substratevm/mx_substratevm_benchmark.py index a9c61de525bb..eb6a20f4268a 100644 --- a/substratevm/mx.substratevm/mx_substratevm_benchmark.py +++ b/substratevm/mx.substratevm/mx_substratevm_benchmark.py @@ -59,7 +59,19 @@ def list_jars(path): force_buildtime_init_slf4j_1_7_73 = '--initialize-at-build-time=org.slf4j,org.apache.log4j' +force_buildtime_init_slf4j_1_7_73_spark = '--initialize-at-build-time=org.apache.logging.slf4j.Log4jLoggerFactory,\ +org.apache.logging.slf4j.SLF4JServiceProvider,org.apache.logging.slf4j.Log4jMarkerFactory,org.apache.logging.slf4j.Log4jMDCAdapter,\ +org.apache.logging.log4j,org.apache.logging.log4j,org.apache.logging.log4j.core.util.WatchManager,org.apache.logging.log4j.core.config.xml.XmlConfiguration, \ +org.apache.logging.log4j.core.config.AbstractConfiguration,org.apache.logging.log4j.util.ServiceLoaderUtil,org.slf4j.LoggerFactory' +force_buildtime_init_netty_4_1_72 = '--initialize-at-build-time=io.netty.util.internal.logging' +force_runtime_init_slf4j_1_7_73 = '--initialize-at-run-time=org.apache.log4j.LogManager' force_runtime_init_netty_4_1_72 = '--initialize-at-run-time=io.netty.channel.unix,io.netty.channel.epoll,io.netty.handler.codec.http2,io.netty.handler.ssl,io.netty.internal.tcnative,io.netty.util.internal.logging.Log4JLogger' +force_runtime_init_netty_4_1_72_spark = '--initialize-at-run-time=io.netty.buffer.AbstractByteBufAllocator\ +io.netty.channel.AbstractChannelHandlerContext,io.netty.channel.ChannelInitializer,io.netty.channel.ChannelOutboundBuffer,\ +io.netty.util.internal.SystemPropertyUtil,io.netty.channel.AbstractChannel,io.netty.util.internal.PlatformDependent,\ +io.netty.util.internal.InternalThreadLocalMap,io.netty.channel.socket.nio.SelectorProviderUtil,io.netty.util.concurrent.DefaultPromise, \ +io.netty.util.NetUtil,io.netty.channel.DefaultChannelPipeline,io.netty.util.concurrent.FastThreadLocalThread,io.netty.util.internal.StringUtil, \ +io.netty.util.internal.PlatformDependent0,io.netty.util,io.netty.bootstrap,io.netty.channel,io.netty.buffer,io.netty.resolver,io.netty.handler.codec.CodecOutputList' _RENAISSANCE_EXTRA_IMAGE_BUILD_ARGS = { 'als' : [ '--report-unsupported-elements-at-runtime', @@ -69,7 +81,11 @@ def list_jars(path): 'chi-square' : [ '--report-unsupported-elements-at-runtime', force_buildtime_init_slf4j_1_7_73, - force_runtime_init_netty_4_1_72 + force_buildtime_init_slf4j_1_7_73_spark, + force_buildtime_init_netty_4_1_72, + force_runtime_init_netty_4_1_72, + force_runtime_init_netty_4_1_72_spark, + force_runtime_init_slf4j_1_7_73 ], 'finagle-chirper' : [ force_buildtime_init_slf4j_1_7_73, @@ -87,7 +103,11 @@ def list_jars(path): 'movie-lens' : [ '--report-unsupported-elements-at-runtime', force_buildtime_init_slf4j_1_7_73, - force_runtime_init_netty_4_1_72 + force_buildtime_init_slf4j_1_7_73_spark, + force_buildtime_init_netty_4_1_72, + force_runtime_init_netty_4_1_72, + force_runtime_init_netty_4_1_72_spark, + force_runtime_init_slf4j_1_7_73 ], 'dec-tree' : [ '--report-unsupported-elements-at-runtime', @@ -97,7 +117,11 @@ def list_jars(path): 'page-rank' : [ '--report-unsupported-elements-at-runtime', force_buildtime_init_slf4j_1_7_73, - force_runtime_init_netty_4_1_72 + force_buildtime_init_slf4j_1_7_73_spark, + force_buildtime_init_netty_4_1_72, + force_runtime_init_netty_4_1_72, + force_runtime_init_netty_4_1_72_spark, + force_runtime_init_slf4j_1_7_73 ], 'naive-bayes' : [ '--report-unsupported-elements-at-runtime', @@ -107,7 +131,11 @@ def list_jars(path): 'gauss-mix' : [ '--report-unsupported-elements-at-runtime', force_buildtime_init_slf4j_1_7_73, - force_runtime_init_netty_4_1_72 + force_buildtime_init_slf4j_1_7_73_spark, + force_buildtime_init_netty_4_1_72, + force_runtime_init_netty_4_1_72, + force_runtime_init_netty_4_1_72_spark, + force_runtime_init_slf4j_1_7_73 ], 'neo4j-analytics': [ '--report-unsupported-elements-at-runtime', diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 51fdf8e58150..bdf63b0dea04 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -305,6 +305,7 @@ "jdk.internal.reflect", "jdk.internal.vm", "jdk.internal.util", + "jdk.internal.org.objectweb.asm", ], "java.management": [ "com.sun.jmx.mbeanserver", diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java index d1e53852971f..4fa32e7ca391 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/reports/CallTreePrinter.java @@ -336,7 +336,7 @@ public Set classesSet(boolean packageNameOnly) { String name = method.getDeclaringClass().toJavaName(true); if (packageNameOnly) { name = packagePrefix(name); - if (name.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) { + if (LambdaUtils.isLambdaClassName(name)) { /* Also strip synthetic package names added for lambdas. */ name = packagePrefix(name); } 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 3e8b5be55a58..43d0f5d61b7d 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 @@ -61,6 +61,9 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; +import com.oracle.svm.core.jni.headers.JNIMode; +import jdk.graal.compiler.core.common.NumUtil; +import jdk.graal.compiler.java.LambdaUtils; import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.function.CEntryPoint; @@ -77,6 +80,7 @@ import org.graalvm.nativeimage.c.type.WordPointer; import org.graalvm.word.WordFactory; +import com.oracle.svm.agent.stackaccess.EagerlyLoadedJavaStackAccess; import com.oracle.svm.agent.stackaccess.InterceptedState; import com.oracle.svm.agent.tracing.core.Tracer; import com.oracle.svm.configure.trace.AccessAdvisor; @@ -102,8 +106,6 @@ import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiInterface; import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiLocationFormat; -import jdk.graal.compiler.core.common.NumUtil; - /** * Intercepts events of interest via breakpoints in Java code. *

@@ -961,11 +963,72 @@ private static boolean methodTypeFromDescriptor(JNIEnvironment jni, JNIObjectHan } /** - * We have to find a class that captures a lambda function so it can be registered by the agent. - * We have to get a SerializedLambda instance first. After that we get a lambda capturing class - * from that instance using JNIHandleSet#getFieldId to get field id and JNIObjectHandle#invoke - * on to get that field value. We get a name of the capturing class and tell the agent to - * register it. + * 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) { + String className = Support.fromJniString(jni, getObjectArgument(thread, 1)); + + if (LambdaUtils.isLambdaClassName(className)) { + if (shouldIgnoreLambdaClassForPredefinition(jni)) { + return true; + } + + JNIObjectHandle bytesArray = getObjectArgument(thread, 3); + int length = jniFunctions().getGetArrayLength().invoke(jni, bytesArray); + byte[] data = new byte[length]; + + CCharPointer bytesArrayCharPointer = jni.getFunctions().getGetByteArrayElements().invoke(jni, bytesArray, WordFactory.nullPointer()); + if (bytesArrayCharPointer.isNonNull()) { + try { + CTypeConversion.asByteBuffer(bytesArrayCharPointer, length).get(data); + } finally { + jni.getFunctions().getReleaseByteArrayElements().invoke(jni, bytesArray, bytesArrayCharPointer, JNIMode.JNI_ABORT()); + } + + className += LambdaUtils.digest(data); + tracer.traceCall("classloading", "onMethodHandleClassFileInit", null, null, null, null, state.getFullStackTraceOrNull(), className, data); + } + } + return true; + } + + /** + * This method is used to check whether a lambda class should be predefined or not. Only lambdas that are created at runtime should + * be predefined, and we should ignore the others. This method checks if the specific sequence of methods exists in the stacktrace + * and base on that decides if the lambda class should be ignored. + */ + private static boolean shouldIgnoreLambdaClassForPredefinition(JNIEnvironment env) { + JNIMethodId[] stackTraceMethodIds = EagerlyLoadedJavaStackAccess.stackAccessSupplier().get().getFullStackTraceOrNull(); + JNIMethodId javaLangInvokeCallSiteMakeSite = agent.handles().getJavaLangInvokeCallSiteMakeSite(env); + JNIMethodId javaLangInvokeMethodHandleNativesLinkCallSiteImpl = agent.handles().getJavaLangInvokeMethodHandleNativesLinkCallSiteImpl(env); + JNIMethodId javaLangInvokeMethodHandleNativesLinkCallSite = agent.handles().getJavaLangInvokeMethodHandleNativesLinkCallSite(env); + + /* + * Sequence {@code java.lang.invoke.CallSite.makeSite}, {@code + * java.lang.invoke.MethodHandleNatives.linkCallSiteImpl}, {@code + * java.lang.invoke.MethodHandleNatives.linkCallSite} in the stacktrace indicates that + * lambda class won't be created at runtime on the Native Image, so it should not be + * registered for predefiniton. + */ + for (int i = 0; i < stackTraceMethodIds.length - 2; i++) { + if (stackTraceMethodIds[i] == javaLangInvokeCallSiteMakeSite && + stackTraceMethodIds[i + 1] == javaLangInvokeMethodHandleNativesLinkCallSiteImpl && + stackTraceMethodIds[i + 2] == javaLangInvokeMethodHandleNativesLinkCallSite) { + return true; + } + } + + return false; + } + + /** + * We have to find a class that captures a lambda function, so it can be registered by the + * agent. We have to get a SerializedLambda instance first. After that we get a lambda capturing + * class from that instance using JNIHandleSet#getFieldId to get field id and + * JNIObjectHandle#invoke on to get that field value. We get a name of the capturing class and + * tell the agent to register it. */ private static boolean serializedLambdaReadResolve(JNIEnvironment jni, JNIObjectHandle thread, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state) { JNIObjectHandle serializedLambdaInstance = getReceiver(thread); @@ -1283,6 +1346,12 @@ public static void onVMInit(JvmtiEnv jvmti, JNIEnvironment jni) { System.arraycopy(BREAKPOINT_SPECIFICATIONS, 0, breakpointSpecifications, 0, BREAKPOINT_SPECIFICATIONS.length); System.arraycopy(REFLECTION_ACCESS_BREAKPOINT_SPECIFICATIONS, 0, breakpointSpecifications, BREAKPOINT_SPECIFICATIONS.length, REFLECTION_ACCESS_BREAKPOINT_SPECIFICATIONS.length); } + if (experimentalClassDefineSupport) { + BreakpointSpecification[] existingBreakpointSpecifications = breakpointSpecifications; + breakpointSpecifications = Arrays.copyOf(existingBreakpointSpecifications, existingBreakpointSpecifications.length + CLASS_PREDEFINITION_BREAKPOINT_SPECIFICATIONS.length); + System.arraycopy(CLASS_PREDEFINITION_BREAKPOINT_SPECIFICATIONS, 0, breakpointSpecifications, existingBreakpointSpecifications.length, + CLASS_PREDEFINITION_BREAKPOINT_SPECIFICATIONS.length); + } for (BreakpointSpecification br : breakpointSpecifications) { JNIObjectHandle clazz = nullHandle(); if (lastClassName != null && lastClassName.equals(br.className)) { @@ -1652,6 +1721,10 @@ private static boolean allocateInstance(JNIEnvironment jni, JNIObjectHandle thre brk("java/lang/reflect/Constructor", "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;", BreakpointInterceptor::invokeConstructor), }; + private static final BreakpointSpecification[] CLASS_PREDEFINITION_BREAKPOINT_SPECIFICATIONS = { + brk("java/lang/invoke/MethodHandles$Lookup$ClassFile", "", "(Ljava/lang/String;I[B)V", BreakpointInterceptor::onMethodHandleClassFileInit), + }; + private static BreakpointSpecification brk(String className, String methodName, String signature, BreakpointHandler handler) { return new BreakpointSpecification(className, methodName, signature, handler, false); } diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java index cd32aba734f1..884686b4f5e0 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java @@ -91,6 +91,10 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet { final JNIMethodId javaLangModuleGetName; + private JNIMethodId javaLangInvokeCallSiteMakeSite = WordFactory.nullPointer(); + private JNIMethodId javaLangInvokeMethodHandleNativesLinkCallSiteImpl = WordFactory.nullPointer(); + private JNIMethodId javaLangInvokeMethodHandleNativesLinkCallSite = WordFactory.nullPointer(); + NativeImageAgentJNIHandleSet(JNIEnvironment env) { super(env); javaLangClass = newClassGlobalRef(env, "java/lang/Class"); @@ -263,4 +267,32 @@ public JNIMethodId getJavaUtilResourceBundleGetLocale(JNIEnvironment env) { } return javaUtilResourceBundleGetLocale; } + + public JNIMethodId getJavaLangInvokeCallSiteMakeSite(JNIEnvironment env) { + if (javaLangInvokeCallSiteMakeSite.isNull()) { + JNIObjectHandle javaLangInvokeCallSite = findClass(env, "java/lang/invoke/CallSite"); + javaLangInvokeCallSiteMakeSite = getMethodId(env, javaLangInvokeCallSite, "makeSite", + "(Ljava/lang/invoke/MethodHandle;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/invoke/CallSite;", true); + } + return javaLangInvokeCallSiteMakeSite; + } + + public JNIMethodId getJavaLangInvokeMethodHandleNativesLinkCallSiteImpl(JNIEnvironment env) { + if (javaLangInvokeMethodHandleNativesLinkCallSiteImpl.isNull()) { + JNIObjectHandle javaLangInvokeMethodHandleNatives = findClass(env, "java/lang/invoke/MethodHandleNatives"); + javaLangInvokeMethodHandleNativesLinkCallSiteImpl = getMethodId(env, javaLangInvokeMethodHandleNatives, "linkCallSiteImpl", + "(Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;", + true); + } + return javaLangInvokeMethodHandleNativesLinkCallSiteImpl; + } + + public JNIMethodId getJavaLangInvokeMethodHandleNativesLinkCallSite(JNIEnvironment env) { + if (javaLangInvokeMethodHandleNativesLinkCallSite.isNull()) { + JNIObjectHandle javaLangInvokeMethodHandleNatives = findClass(env, "java/lang/invoke/MethodHandleNatives"); + javaLangInvokeMethodHandleNativesLinkCallSite = getMethodIdOptional(env, javaLangInvokeMethodHandleNatives, "linkCallSite", + "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;", true); + } + return javaLangInvokeMethodHandleNativesLinkCallSite; + } } 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 8076cdafebe4..3e9b0bbd3c90 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 configurationSe String function = (String) entry.get("function"); List args = (List) entry.get("args"); - if ("onClassFileLoadHook".equals(function)) { + if ("onClassFileLoadHook".equals(function) || "onMethodHandleClassFileInit".equals(function)) { expectSize(args, 2); String nameInfo = (String) args.get(0); byte[] classData = asBinary(args.get(1)); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java index 0f95929f6057..44435f77afd1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java @@ -25,6 +25,8 @@ */ package com.oracle.svm.core.hub; +import java.io.Serializable; +import java.lang.reflect.Method; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.ProtectionDomain; @@ -32,6 +34,13 @@ import java.util.Set; import java.util.concurrent.locks.ReentrantLock; +import com.oracle.svm.core.reflect.serialize.SerializationSupport; +import jdk.graal.compiler.java.LambdaUtils; +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; import org.graalvm.collections.EconomicMap; import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.options.Option; @@ -45,6 +54,7 @@ import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ClassUtil; +import org.graalvm.nativeimage.hosted.RuntimeReflection; public final class PredefinedClassesSupport { public static final class Options { @@ -107,10 +117,51 @@ public static void registerClass(String hash, Class clazz) { Class existing = singleton().predefinedClassesByHash.putIfAbsent(hash, clazz); if (existing != clazz) { VMError.guarantee(existing == null, "Can define only one class per hash"); + /* + * Predefined lambda classes do not have an addressHash at the end. They are in the form + * capturingClass$$Lambda$stableHash instead of + * capturingClass$$Lambda$stableHash/addressHash. The only way to register a predefined + * lambdas for serialization or reflection is here, where we have an actual predefined + * class as an instance of {@code java.lang.Class} at build time. + */ + if (LambdaUtils.isLambdaClass(clazz)) { + registerLambdaForReflection(clazz); + } singleton().predefinedClasses.add(clazz); } } + @Platforms(Platform.HOSTED_ONLY.class) + private static void registerLambdaForReflection(Class lambdaClass) { + /* + * When {@code java.lang.invoke.InnerClassLambdaMetafactory} builds a call site for a lambda + * class, it uses either static field LAMBDA_INSTANCE$ or lambda constructor. Since we are + * generating lambda classes at runtime, we need to register that field or a lambda + * constructor for reflection. + */ + try { + RuntimeReflection.register(lambdaClass.getDeclaredField("LAMBDA_INSTANCE$")); + } catch (NoSuchFieldException ignored) { + RuntimeReflection.register(lambdaClass.getDeclaredConstructors()); + } + + /* + * In some cases, predefined lambda should be serialized. We have to register proper method + * for this. We cannot do this in {@code + * com.oracle.svm.hosted.reflect.serialize.SerializationFeature}, since we cannot extract + * lambda-class information from the capturing class. + */ + if (Serializable.class.isAssignableFrom(lambdaClass) && + SerializationSupport.isLambdaCapturingClassRegistered(LambdaUtils.capturingClass(lambdaClass.getName()))) { + try { + Method serializeLambdaMethod = lambdaClass.getDeclaredMethod("writeReplace"); + RuntimeReflection.register(serializeLambdaMethod); + } catch (NoSuchMethodException e) { + throw VMError.shouldNotReachHere("Serializable lambda class must contain the writeReplace method."); + } + } + } + /** * Register a class that cannot be loaded via a byte array of its class data, only with * {@link #loadClass(ClassLoader, ProtectionDomain, Class)} or {@link #loadClassIfNotLoaded}. @@ -260,4 +311,75 @@ public static Set> getConfigurationPredefinedClasses() { return set; // excludes internal classes such as proxy classes } } + + @Platforms(Platform.HOSTED_ONLY.class) + public static Class maybeAdjustLambdaNestHost(String className, Class javaClass, ClassLoader classLoader, Class originalNestHost) { + Class lambdaNestHost = originalNestHost; + /* + * Predefined lambda classes do not contain the address hash of the name at this point. + * Their names look like capturingClass$$Lambda$stableUniqueHash. Because we cut off the + * address hash at the end of the lambda name, the nest host for predefined lambda would be + * lambda itself which is incorrect. The nest host for lambda class should be the nest host + * of it's capturing class. + * + * When {@code java.lang.invoke.InnerClassLambdaMetafactory} tries to define a hidden class, + * it ends up in {@code java.lang.invoke.MethodHandles#defineClass}. This method requires + * that the nest host of the class we define is the same as the nest host of the lookup + * class. For predefined classes, that won't be the case, so we need to re-calculate the + * nest host for them. + */ + if (LambdaUtils.isLambdaClassName(className) && PredefinedClassesSupport.isPredefined(javaClass)) { + Class capturingClass; + + try { + capturingClass = Class.forName(LambdaUtils.capturingClass(className), false, classLoader); + } catch (Throwable e) { + throw new RuntimeException(e); + } + + lambdaNestHost = capturingClass.getNestHost(); + } + + return lambdaNestHost; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public static byte[] changeLambdaClassName(byte[] data, String oldName, String newName) { + ClassReader cr = new ClassReader(data); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + + cr.accept(new ClassVisitor(Opcodes.ASM5, cw) { + // Change lambda class name in the bytecode + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + super.visit(version, access, newName, signature, superName, interfaces); + } + + // Change all class references in the lambda class bytecode + @Override + public MethodVisitor visitMethod(int access, String originalName, String desc, String signature, String[] exceptions) { + return new MethodVisitor(Opcodes.ASM5, super.visitMethod(access, originalName, desc, signature, exceptions)) { + @Override + public void visitTypeInsn(int opcode, String type) { + String name = type.equals(oldName) ? newName : type; + super.visitTypeInsn(opcode, name); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String methodName, String descriptor, boolean isInterface) { + String name = owner.equals(oldName) ? newName : owner; + super.visitMethodInsn(opcode, name, methodName, descriptor, isInterface); + } + + @Override + public void visitFieldInsn(int opcode, String owner, String fieldName, String descriptor) { + String name = owner.equals(oldName) ? newName : owner; + super.visitFieldInsn(opcode, name, fieldName, descriptor); + } + }; + } + }, ClassReader.EXPAND_FRAMES); + + return cw.toByteArray(); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java index 4e4c9fbf35c1..96b1f40d66f3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ClassLoader.java @@ -36,6 +36,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; +import jdk.graal.compiler.java.LambdaUtils; + import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Delete; @@ -320,6 +322,10 @@ private static Class defineClass1(ClassLoader loader, String name, byte[] b, @Substitute @SuppressWarnings("unused") private static Class defineClass0(ClassLoader loader, Class lookup, String name, byte[] b, int off, int len, ProtectionDomain pd, boolean initialize, int flags, Object classData) { + if (LambdaUtils.isLambdaClassName(name)) { + String newName = name + LambdaUtils.digest(b); + return PredefinedClassesSupport.loadClass(loader, newName.replace('/', '.'), b, off, b.length, null); + } throw VMError.unsupportedFeature("Defining hidden classes at runtime is not supported."); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java index 5a119ecee4f7..9cda3631ed78 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java @@ -130,12 +130,23 @@ public Object addConstructorAccessor(Class declaringClass, Class targetCon } @Platforms(Platform.HOSTED_ONLY.class) private final Set> classes = ConcurrentHashMap.newKeySet(); + @Platforms(Platform.HOSTED_ONLY.class) private static final Set lambdaCapturingClasses = ConcurrentHashMap.newKeySet(); @Platforms(Platform.HOSTED_ONLY.class) public void registerSerializationTargetClass(Class serializationTargetClass) { classes.add(serializationTargetClass); } + @Platforms(Platform.HOSTED_ONLY.class) + public static void registerLambdaCapturingClass(String lambdaCapturingClass) { + lambdaCapturingClasses.add(lambdaCapturingClass); + } + + @Platforms(Platform.HOSTED_ONLY.class) + public static boolean isLambdaCapturingClassRegistered(String lambdaCapturingClass) { + return lambdaCapturingClasses.contains(lambdaCapturingClass); + } + @Override @Platforms(Platform.HOSTED_ONLY.class) public boolean isRegisteredForSerialization(Class cl) { @@ -146,7 +157,7 @@ public boolean isRegisteredForSerialization(Class cl) { public Object getSerializationConstructorAccessor(Class rawDeclaringClass, Class rawTargetConstructorClass) { Class declaringClass = rawDeclaringClass; - if (declaringClass.getName().contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) { + if (LambdaUtils.isLambdaClass(declaringClass)) { declaringClass = SerializedLambda.class; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassPredefinitionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassPredefinitionFeature.java index a2547d91af36..d99ffac99c47 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassPredefinitionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassPredefinitionFeature.java @@ -34,29 +34,51 @@ import java.util.Map; import java.util.stream.Collectors; +import jdk.graal.compiler.java.LambdaUtils; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.FieldVisitor; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.hosted.RuntimeClassInitialization; import com.oracle.svm.core.configure.ConfigurationFile; import com.oracle.svm.core.configure.ConfigurationFiles; import com.oracle.svm.core.configure.PredefinedClassesConfigurationParser; import com.oracle.svm.core.configure.PredefinedClassesRegistry; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.hub.PredefinedClassesSupport; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.AfterRegistrationAccessImpl; import com.oracle.svm.hosted.config.ConfigurationParserUtils; +import com.oracle.svm.hosted.reflect.ReflectionFeature; +import com.oracle.svm.util.ModuleSupport; import jdk.internal.org.objectweb.asm.ClassReader; import jdk.internal.org.objectweb.asm.ClassWriter; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; + @AutomaticallyRegisteredFeature public class ClassPredefinitionFeature implements InternalFeature { private final Map nameToRecord = new HashMap<>(); private boolean sealed = false; + @Override + public List> getRequiredFeatures() { + /* + * We are registering all predefined lambda classes for reflection. If the reflection + * feature is not executed before this feature, class RuntimeReflectionSupport won't be + * present in the ImageSingletons. + */ + return List.of(ReflectionFeature.class); + } + @Override public void afterRegistration(AfterRegistrationAccess arg) { ImageSingletons.add(PredefinedClassesSupport.class, new PredefinedClassesSupport()); @@ -72,6 +94,7 @@ public void afterRegistration(AfterRegistrationAccess arg) { ConfigurationParserUtils.parseAndRegisterConfigurations(parser, access.getImageClassLoader(), "class predefinition", ConfigurationFiles.Options.PredefinedClassesConfigurationFiles, ConfigurationFiles.Options.PredefinedClassesConfigurationResources, ConfigurationFile.PREDEFINED_CLASSES_NAME.getFileName()); + ModuleSupport.accessPackagesToClass(ModuleSupport.Access.EXPORT, PredefinedClassesSupport.class, false, "java.base", "jdk.internal.org.objectweb.asm"); } @Override @@ -133,9 +156,21 @@ public void add(String nameInfo, String providedHash, URI baseUri) { try (InputStream in = PredefinedClassesConfigurationParser.openClassdataStream(baseUri, providedHash)) { data = in.readAllBytes(); } + // Compute our own hash code, the files could have been messed with. String hash = PredefinedClassesSupport.hash(data, 0, data.length); + if (LambdaUtils.isLambdaClassName(nameInfo)) { + /** + * We have to cut off calculated hash since lambda's name in the bytecode does + * not contain it. Also, the name in the bytecode must match the name we load + * class with, so we have to update it in the bytecode. + */ + data = PredefinedClassesSupport.changeLambdaClassName(data, nameInfo.substring(0, nameInfo.indexOf(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING) + + LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING.length()), nameInfo); + data = makeLambdaInstanceFieldAndConstructorPublic(data); + } + /* * Compute a "canonical hash" that does not incorporate debug information such as * source file names, line numbers, local variable names, etc. @@ -280,4 +315,34 @@ public void addPendingSupertype(PredefinedClass record) { pendingSupertypes.add(record); } } + + /** + * We need to make field LAMBDA_INSTANCE$ and lambda class constructor public in order to be + * able to predefine lambda classes so that {@code java.lang.invoke.InnerClassLambdaMetafactory} + * can access them. + */ + private static byte[] makeLambdaInstanceFieldAndConstructorPublic(byte[] classBytes) { + ClassReader cr = new ClassReader(classBytes); + ClassWriter cw = new ClassWriter(cr, 0); + + cr.accept(new ClassVisitor(Opcodes.ASM5, cw) { + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + if (name.equals("LAMBDA_INSTANCE$")) { + return super.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, name, descriptor, signature, value); + } + return super.visitField(access, name, descriptor, signature, value); + } + + @Override + public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) { + if (name.equals("")) { + return super.visitMethod(ACC_PUBLIC, name, descriptor, signature, exceptions); + } + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + }, 0); + + return cw.toByteArray(); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 8705ac5b5703..eda9fe67c34f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -86,6 +86,7 @@ import com.oracle.svm.core.heap.UnknownClass; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.HubType; +import com.oracle.svm.core.hub.PredefinedClassesSupport; import com.oracle.svm.core.hub.ReferenceType; import com.oracle.svm.core.jdk.InternalVMMethod; import com.oracle.svm.core.jdk.LambdaFormHiddenMethod; @@ -415,6 +416,8 @@ private DynamicHub createHub(AnalysisType type) { boolean isLambdaFormHidden = type.isAnnotationPresent(LambdaFormHiddenMethod.class); boolean isLinked = type.isLinked(); + nestHost = PredefinedClassesSupport.maybeAdjustLambdaNestHost(className, javaClass, classLoader, nestHost); + return new DynamicHub(javaClass, className, computeHubType(type), computeReferenceType(type), superHub, componentHub, sourceFileName, modifiers, hubClassLoader, isHidden, isRecord, nestHost, assertionStatus, type.hasDefaultMethods(), type.declaresDefaultMethods(), isSealed, isVMInternal, isLambdaFormHidden, isLinked, simpleBinaryName, getDeclaringClass(javaClass), getSignature(javaClass)); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/StableLambdaProxyNameFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/StableLambdaProxyNameFeature.java index 45f57bbb0add..855c1508184e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/StableLambdaProxyNameFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/StableLambdaProxyNameFeature.java @@ -68,7 +68,7 @@ private static boolean checkLambdaNames(List types) { Set lambdaNames = new HashSet<>(); types.stream() .map(AnalysisType::getName) - .filter(x -> x.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) + .filter(LambdaUtils::isLambdaClassName) .forEach(name -> { if (lambdaNames.contains(name)) { throw new AssertionError("Duplicate lambda name: " + name); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java index 9e87866535ba..724952f45132 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java @@ -201,7 +201,7 @@ private static Class getLambdaClassFromConstantNode(ConstantNode constantNode return null; } - return lambdaClass.getName().contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING) ? lambdaClass : null; + return LambdaUtils.isLambdaClass(lambdaClass) ? lambdaClass : null; } private static void registerLambdasFromConstantNodesInGraph(StructuredGraph graph, SerializationBuilder serializationBuilder) { @@ -479,6 +479,7 @@ public void registerLambdaCapturingClass(ConfigurationCondition condition, Strin ImageSingletons.lookup(SerializationFeature.class).capturingClasses.add(lambdaCapturingClass); RuntimeReflection.register(lambdaCapturingClass); RuntimeReflection.register(ReflectionUtil.lookupMethod(lambdaCapturingClass, "$deserializeLambda$", SerializedLambda.class)); + SerializationSupport.registerLambdaCapturingClass(lambdaCapturingClassName); }); } diff --git a/vm/mx.vm/mx_vm_benchmark.py b/vm/mx.vm/mx_vm_benchmark.py index 26a99694e3d6..83e40148631c 100644 --- a/vm/mx.vm/mx_vm_benchmark.py +++ b/vm/mx.vm/mx_vm_benchmark.py @@ -898,6 +898,10 @@ def run_stage_agent(self, config, stages): agentlib_options = ['native-image-agent=config-output-dir=' + str(config.config_dir)] + config.extra_agentlib_options hotspot_vm_args += ['-agentlib:' + ','.join(agentlib_options)] + # Native Image has the following option enabled by default. In order to create lambda classes in the same way + # during the agent run and image run, we need this option for the agent too. + hotspot_vm_args += ['-Djdk.internal.lambda.disableEagerInitialization=true'] + # Jargraal is very slow with the agent, and libgraal is usually not built for Native Image benchmarks. Therefore, don't use the GraalVM compiler. hotspot_vm_args += ['-XX:-UseJVMCICompiler'] From d84c35d1851443f2bd25363837289ebb24d7593c Mon Sep 17 00:00:00 2001 From: sstanoje Date: Wed, 31 Jan 2024 10:36:24 +0100 Subject: [PATCH 2/2] Style update --- .../src/jdk/graal/compiler/java/LambdaUtils.java | 12 ++++++------ .../oracle/svm/agent/BreakpointInterceptor.java | 15 +++++++++------ .../svm/core/hub/PredefinedClassesSupport.java | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/LambdaUtils.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/LambdaUtils.java index dd8da491e779..f1a365bae592 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/LambdaUtils.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/LambdaUtils.java @@ -126,7 +126,7 @@ public static String findStableLambdaName(ClassInitializationPlugin cip, Provide } /** - * Checks if the passed type is lambda class type based on set flags and the type name + * Checks if the passed type is lambda class type based on set flags and the type name. * * @param type type to be checked * @return true if the passed type is lambda type, false otherwise @@ -165,7 +165,7 @@ public static String toHex(byte[] data) { } /** - * Hashing a passed string parameter using SHA-1 hashing algorithm + * Hashing a passed string parameter using SHA-1 hashing algorithm. * * @param value string to be hashed * @return hexadecimal hashed value of the passed string parameter @@ -175,7 +175,7 @@ public static String digest(String value) { } /** - * Hashing a passed byte array parameter using SHA-1 hashing algorithm + * Hashing a passed byte array parameter using SHA-1 hashing algorithm. * * @param bytes byte array to be hashed * @return hexadecimal hashed value of the passed byte array parameter @@ -191,7 +191,7 @@ public static String digest(byte[] bytes) { } /** - * Extracts lambda capturing class name from the lambda class name + * Extracts lambda capturing class name from the lambda class name. * * @param className name of the lambda class * @return name of the lambda capturing class @@ -201,7 +201,7 @@ public static String capturingClass(String className) { } /** - * Checks if the passed class is lambda class + * Checks if the passed class is lambda class. * * @param clazz class to be checked * @return true if the clazz is lambda class, false instead @@ -211,7 +211,7 @@ public static boolean isLambdaClass(Class clazz) { } /** - * Checks if the passed class name is lambda class name + * Checks if the passed class name is lambda class name. * * @param className name of the class * @return true if the className is lambda class name, false instead 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 43d0f5d61b7d..ee11c2f4a8b3 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 @@ -963,9 +963,11 @@ private static boolean methodTypeFromDescriptor(JNIEnvironment jni, JNIObjectHan } /** - * 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. + * 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) { String className = Support.fromJniString(jni, getObjectArgument(thread, 1)); @@ -995,9 +997,10 @@ private static boolean onMethodHandleClassFileInit(JNIEnvironment jni, JNIObject } /** - * This method is used to check whether a lambda class should be predefined or not. Only lambdas that are created at runtime should - * be predefined, and we should ignore the others. This method checks if the specific sequence of methods exists in the stacktrace - * and base on that decides if the lambda class should be ignored. + * This method is used to check whether a lambda class should be predefined or not. Only lambdas + * that are created at runtime should be predefined, and we should ignore the others. This + * method checks if the specific sequence of methods exists in the stacktrace and base on that + * decides if the lambda class should be ignored. */ private static boolean shouldIgnoreLambdaClassForPredefinition(JNIEnvironment env) { JNIMethodId[] stackTraceMethodIds = EagerlyLoadedJavaStackAccess.stackAccessSupplier().get().getFullStackTraceOrNull(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java index 44435f77afd1..d08704f7f2e9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/PredefinedClassesSupport.java @@ -152,7 +152,7 @@ private static void registerLambdaForReflection(Class lambdaClass) { * lambda-class information from the capturing class. */ if (Serializable.class.isAssignableFrom(lambdaClass) && - SerializationSupport.isLambdaCapturingClassRegistered(LambdaUtils.capturingClass(lambdaClass.getName()))) { + SerializationSupport.isLambdaCapturingClassRegistered(LambdaUtils.capturingClass(lambdaClass.getName()))) { try { Method serializeLambdaMethod = lambdaClass.getDeclaredMethod("writeReplace"); RuntimeReflection.register(serializeLambdaMethod);