Skip to content

Commit

Permalink
Create lambda names format consistent with the JDK lambda names format.
Browse files Browse the repository at this point in the history
  • Loading branch information
sstanoje committed Nov 7, 2023
1 parent ebc75fa commit 63466df
Show file tree
Hide file tree
Showing 7 changed files with 18 additions and 19 deletions.
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 @@ -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.
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 @@ -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)
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

0 comments on commit 63466df

Please sign in to comment.