diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/LambdaStableNameTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/LambdaStableNameTest.java index 5d4a1c851abd4..c22d936a56c1f 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/LambdaStableNameTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/LambdaStableNameTest.java @@ -69,20 +69,20 @@ public void checkStableLamdaNameForRunnableAndAutoCloseable() { assertEquals("Both stable lambda names are the same as they reference the same method", name, acName); String myName = Type.getInternalName(getClass()); - assertEquals("The name known in 19.3 version is computed", "L" + myName + "$$Lambda$0a7a1b7da3e20b4eff3f548c6ba3e47a0c3be612;", name); + assertEquals("The name known in 24.0 version is computed", "L" + myName + "$$Lambda.0x0a7a1b7da3e20b4eff3f548c6ba3e47a0c3be612;", name); } private static void assertLambdaName(String name) { String expectedPrefix = "L" + LambdaStableNameTest.class.getCanonicalName().replace('.', '/') + - "$$Lambda$"; + LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING; if (!name.startsWith(expectedPrefix)) { fail("Expecting " + expectedPrefix + " as prefix in lambda class name: " + name); } assertTrue("semicolon at the end", name.endsWith(";")); - int last = name.lastIndexOf('$'); + int index = name.indexOf(LambdaUtils.ADDRESS_PREFIX); - String hash = name.substring(last + 1, name.length() - 1); + String hash = name.substring(index + LambdaUtils.ADDRESS_PREFIX.length(), name.length() - 1); BigInteger aValue = new BigInteger(hash, 16); assertNotNull("Hash can be parsed as a hex number: " + hash, aValue); 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 b508c9bf884c9..27cecb8c68f21 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 @@ -60,6 +60,7 @@ public final class LambdaUtils { public static final String LAMBDA_CLASS_NAME_SUBSTRING = "$$Lambda"; public static final String SERIALIZATION_TEST_LAMBDA_CLASS_SUBSTRING = "$$Lambda"; public static final String SERIALIZATION_TEST_LAMBDA_CLASS_SPLIT_PATTERN = "\\$\\$Lambda"; + public static final String ADDRESS_PREFIX = ".0x"; private static GraphBuilderConfiguration buildLambdaParserConfig(ClassInitializationPlugin cip) { GraphBuilderConfiguration.Plugins plugins = new GraphBuilderConfiguration.Plugins(new InvocationPlugins()); @@ -141,7 +142,7 @@ private static String createStableLambdaName(ResolvedJavaType lambdaType, List sb.append(targetMethod.format("%H.%n(%P)%R"))); - return m.replaceFirst(Matcher.quoteReplacement("$$Lambda$" + digest(sb.toString()) + ";")); + return m.replaceFirst(Matcher.quoteReplacement(LAMBDA_CLASS_NAME_SUBSTRING + ADDRESS_PREFIX + digest(sb.toString()) + ";")); } private static Matcher lambdaMatcher(String value) { diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index bddc1ee578fb1..19f7ea97b0ac5 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -10,6 +10,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-49221) Support for thread dumps can now be enabled with `--enable-monitoring=threaddump`. The option `-H:±DumpThreadStacksOnSignal` is deprecated and marked for removal. * (GR-48579) Options ParseOnce, ParseOnceJIT, and InlineBeforeAnalysis are deprecated and no longer have any effect. * (GR-39407) Add support for the `NATIVE_IMAGE_OPTIONS` environment variable, which allows users and tools to pass additional arguments via the environment. Similar to `JAVA_TOOL_OPTIONS`, the value of the environment variable is prepended to the options supplied to `native-image`. +* (GR-47937) Make the lambda-class name format in Native-Image consistent with the JDK name format. ## GraalVM for JDK 21 (Internal Version 23.1.0) * (GR-35746) Lower the default aligned chunk size from 1 MB to 512 KB for the serial and epsilon GCs, reducing memory usage and image size in many cases. diff --git a/substratevm/mx.substratevm/testhello.py b/substratevm/mx.substratevm/testhello.py index 654ed93e1db51..3798845b17773 100644 --- a/substratevm/mx.substratevm/testhello.py +++ b/substratevm/mx.substratevm/testhello.py @@ -831,7 +831,7 @@ def test(): checker.check(exec_string, skip_fails=False) exec_string = execute("backtrace 3") rexp = [r"#0%shello\.Hello::lambda\$(static\$)?0%s %s at hello/Hello\.java:210"%(spaces_pattern, no_param_types_pattern, no_arg_values_pattern), - r"#1%s%s in hello\.Hello\$\$Lambda\$(%s/0x)?%s::get%s at hello/Hello\.java:238"%(spaces_pattern, address_pattern, digits_pattern, hex_digits_pattern, wildcard_pattern), + r"#1%s%s in hello\.Hello\$\$Lambda((\$%s/0x)|(\$)|(\.0x|/0x))?%s::get%s at hello/Hello\.java:238"%(spaces_pattern, address_pattern, digits_pattern, hex_digits_pattern, wildcard_pattern), r"#2%shello\.Hello::main%s %s at hello/Hello\.java:238"%(spaces_pattern, param_types_pattern, arg_values_pattern)] checker = Checker('backtrace in lambda', rexp) checker.check(exec_string, skip_fails=False) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaProxyRenamingSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaProxyRenamingSubstitutionProcessor.java index 58b93a1a82074..b83a0841c6cdf 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaProxyRenamingSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/lambda/LambdaProxyRenamingSubstitutionProcessor.java @@ -45,12 +45,13 @@ /** * This substitution replaces all lambda proxy types with types that have a stable names. The name * is formed from the signature of the target method that the lambda is calling. - * + *

* NOTE: there is a particular case in which names are not stable. If multiple lambda proxies have a * same target in a same class they are indistinguishable in bytecode. Then their stable names get * appended with a unique number for that class. To make this corner case truly stable, analysis * must be run in the single-threaded mode. */ + public class LambdaProxyRenamingSubstitutionProcessor extends SubstitutionProcessor { private final BigBang bb; @@ -66,7 +67,7 @@ public class LambdaProxyRenamingSubstitutionProcessor extends SubstitutionProces @Override public ResolvedJavaType lookup(ResolvedJavaType type) { - if (LambdaUtils.isLambdaType(type)) { + if (LambdaUtils.isLambdaType(type) && !type.getClass().equals(LambdaSubstitutionType.class)) { return getSubstitution(type); } else { return type; @@ -100,14 +101,16 @@ private LambdaSubstitutionType getSubstitution(ResolvedJavaType original) { */ private String findUniqueLambdaProxyName(String lambdaTargetName) { synchronized (uniqueLambdaProxyNames) { - String newStableName = lambdaTargetName; - CharSequence stableNameBase = lambdaTargetName.subSequence(0, lambdaTargetName.length() - 1); + String stableNameBase = lambdaTargetName.substring(0, lambdaTargetName.length() - 1); + String newStableName = stableNameBase + "0;"; + int i = 1; while (uniqueLambdaProxyNames.contains(newStableName)) { - newStableName = stableNameBase + "_" + i + ";"; + newStableName = stableNameBase + i + ";"; i += 1; } uniqueLambdaProxyNames.add(newStableName); + return newStableName; } } 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 99470a7f54c9a..45f57bbb0add5 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 @@ -60,7 +60,7 @@ private static boolean checkLambdaNames(List types) { throw new AssertionError("Expensive check: should only run with assertions enabled."); } /* There should be no random lambda names visible to the analysis. */ - if (types.stream().anyMatch(LambdaUtils::isLambdaType)) { + if (types.stream().anyMatch(type -> LambdaUtils.isLambdaType(type) && type.getWrapped().getClass() != LambdaSubstitutionType.class)) { throw new AssertionError("All lambda proxies should be substituted."); } diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostObjectToStringTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostObjectToStringTest.java index 5ebc5743bc4c3..d8466298b6bb9 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostObjectToStringTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/HostObjectToStringTest.java @@ -40,10 +40,7 @@ */ package com.oracle.truffle.api.test.polyglot; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.lang.reflect.InvocationHandler; @@ -108,16 +105,13 @@ public void explicitToStringAccess() { @Test public void lambdaToString() { // HotSpot lambda classes have names like "pkg.HostClass$$Lambda$69/0x". - // We strip the "/0x" suffix from the name. - // On SVM, they are named "pkg.HostClass$$Lambda$", which we preserve. + // On SVM, they are named "pkg.HostClass$$Lambda$/0x", which we preserve. final Pattern allowedClassNamePattern = Pattern.compile("^[A-Za-z.$\\d]+$"); final Supplier supplier = () -> "ignored"; setupEnv(HostAccess.EXPLICIT); Value value = context.asValue(supplier); String string = value.toString(); - assertThat(string, not(containsString("/"))); - assertThat(string, not(containsString("0x"))); assertTrue(string, allowedClassNamePattern.matcher(string).matches()); }