Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -141,7 +142,7 @@ private static String createStableLambdaName(ResolvedJavaType lambdaType, List<R
Matcher m = lambdaMatcher(lambdaName);
StringBuilder sb = new StringBuilder();
targetMethods.forEach((targetMethod) -> 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) {
Expand Down
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This changelog summarizes major changes to GraalVM Native Image.
* (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-20827): Introduce a dedicated caller-saved branch target register for software CFI implementations.
* (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.
Expand Down
2 changes: 1 addition & 1 deletion substratevm/mx.substratevm/testhello.py
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
* <p>
* 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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private static boolean checkLambdaNames(List<AnalysisType> 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.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -108,16 +105,13 @@ public void explicitToStringAccess() {
@Test
public void lambdaToString() {
// HotSpot lambda classes have names like "pkg.HostClass$$Lambda$69/0x<hex-id>".
// We strip the "/0x<hex-id>" suffix from the name.
// On SVM, they are named "pkg.HostClass$$Lambda$<unique-hex-digest>", which we preserve.
// On SVM, they are named "pkg.HostClass$$Lambda$/0x<unique-hex-digest>", which we preserve.
final Pattern allowedClassNamePattern = Pattern.compile("^[A-Za-z.$\\d]+$");
final Supplier<String> 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());
}

Expand Down