Skip to content

Commit e6ef3cb

Browse files
committed
Fix stable method name formatter.
1 parent 0c18774 commit e6ef3cb

File tree

9 files changed

+105
-126
lines changed

9 files changed

+105
-126
lines changed

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/hotspot/test/LambdaStableNameTest.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,23 @@
2525

2626
package jdk.graal.compiler.hotspot.test;
2727

28-
import static org.junit.Assert.assertEquals;
2928
import static org.junit.Assert.assertNotEquals;
3029
import static org.junit.Assert.assertNotNull;
31-
import static org.junit.Assert.assertTrue;
32-
import static org.junit.Assert.fail;
3330

3431
import java.math.BigInteger;
3532
import java.util.function.Function;
3633

34+
import jdk.graal.compiler.core.test.GraalCompilerTest;
35+
import jdk.graal.compiler.java.StableMethodNameFormatter;
36+
import jdk.vm.ci.meta.ResolvedJavaMethod;
37+
import org.junit.Assert;
3738
import org.junit.Test;
3839

3940
import jdk.graal.compiler.java.LambdaUtils;
4041
import jdk.vm.ci.meta.ResolvedJavaType;
4142
import jdk.vm.ci.runtime.JVMCI;
4243

43-
public class LambdaStableNameTest {
44+
public class LambdaStableNameTest extends GraalCompilerTest {
4445

4546
@Test
4647
public void checkStableLamdaNameForRunnableAndAutoCloseable() {
@@ -51,15 +52,15 @@ public void checkStableLamdaNameForRunnableAndAutoCloseable() {
5152
Runnable r1 = s::hashCode;
5253
String r1Name = getLambdaName(r1.getClass());
5354

54-
assertEquals("The two stable lambda names should the same as they reference the same method and implement the same interface", r0Name, r1Name);
55+
Assert.assertEquals("The two stable lambda names should the same as they reference the same method and implement the same interface", r0Name, r1Name);
5556

5657
AutoCloseable ac = s::hashCode;
5758
String acName = getLambdaName(ac.getClass());
5859

5960
assertNotEquals("The two stable lambda names should not be the same as they reference the same method but implement different interfaces", r0Name, acName);
6061

6162
String myName = getClass().getName().replace('.', '/');
62-
assertEquals("The name known in 24.0 version is computed", "L" + myName + "$$Lambda.0x59cf38d78b5471f8ea57f1c28b37039c;", r0Name);
63+
Assert.assertEquals("The name known in 24.0 version is computed", "L" + myName + "$$Lambda.0x59cf38d78b5471f8ea57f1c28b37039c;", r0Name);
6364

6465
Function<String, Integer> f0 = (str) -> str.hashCode();
6566
String f0Name = getLambdaName(f0.getClass());
@@ -95,4 +96,14 @@ private static void assertLambdaName(String name) {
9596
BigInteger aValue = new BigInteger(hash, 16);
9697
assertNotNull("Hash can be parsed as a hex number: " + hash, aValue);
9798
}
99+
100+
@Test
101+
public void testStableMethodNameFormatter() {
102+
Runnable runnable = "foo"::hashCode;
103+
ResolvedJavaMethod method = getResolvedJavaMethod(runnable.getClass(), "run");
104+
StableMethodNameFormatter formatter = new StableMethodNameFormatter();
105+
String actualName = formatter.apply(method);
106+
String expectedName = getClass().getName() + LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING + "/0x59cf38d78b5471f8ea57f1c28b37039c.run()";
107+
Assert.assertEquals(expectedName, actualName);
108+
}
98109
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/ProfileReplaySupport.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import jdk.graal.compiler.debug.TTY;
5555
import jdk.graal.compiler.graph.Node;
5656
import jdk.graal.compiler.graph.NodeMap;
57-
import jdk.graal.compiler.java.LambdaUtils;
5857
import jdk.graal.compiler.java.StableMethodNameFormatter;
5958
import jdk.graal.compiler.nodes.ConstantNode;
6059
import jdk.graal.compiler.nodes.FrameState;
@@ -157,12 +156,12 @@ public static ProfileReplaySupport profileReplayPrologue(DebugContext debug, int
157156
StableProfileProvider profileProvider, TypeFilter profileSaveFilter) {
158157
if (SaveProfiles.getValue(debug.getOptions()) || LoadProfiles.getValue(debug.getOptions()) != null) {
159158
LambdaNameFormatter lambdaNameFormatter = new LambdaNameFormatter() {
160-
private final StableMethodNameFormatter stableFormatter = new StableMethodNameFormatter(true);
159+
private final StableMethodNameFormatter stableFormatter = new StableMethodNameFormatter();
161160

162161
@Override
163162
public boolean isLambda(ResolvedJavaMethod m) {
164163
// Include method handles here as well
165-
return LambdaUtils.isLambdaType(m.getDeclaringClass()) || StableMethodNameFormatter.isMethodHandle(m.getDeclaringClass());
164+
return m.getDeclaringClass().isHidden();
166165
}
167166

168167
@Override

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/CompilerInterfaceDeclarations.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import static jdk.graal.compiler.core.common.NativeImageSupport.inRuntimeCode;
3434
import static jdk.graal.compiler.hotspot.HotSpotReplacementsImpl.isGraalClass;
3535
import static jdk.graal.compiler.hotspot.replaycomp.proxy.CompilationProxy.wrapInvocationExceptions;
36-
import static jdk.graal.compiler.java.StableMethodNameFormatter.isMethodHandle;
3736

3837
import java.util.ArrayList;
3938
import java.util.Collections;
@@ -69,7 +68,6 @@
6968
import jdk.graal.compiler.hotspot.replaycomp.proxy.ProfilingInfoProxy;
7069
import jdk.graal.compiler.hotspot.replaycomp.proxy.SignatureProxy;
7170
import jdk.graal.compiler.hotspot.replaycomp.proxy.SpeculationLogProxy;
72-
import jdk.graal.compiler.java.LambdaUtils;
7371
import jdk.graal.compiler.options.ExcludeFromJacocoGeneratedReport;
7472
import jdk.vm.ci.code.CompiledCode;
7573
import jdk.vm.ci.code.InstalledCode;
@@ -78,6 +76,7 @@
7876
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
7977
import jdk.vm.ci.hotspot.HotSpotMemoryAccessProvider;
8078
import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant;
79+
import jdk.vm.ci.hotspot.HotSpotModifiers;
8180
import jdk.vm.ci.hotspot.HotSpotObjectConstant;
8281
import jdk.vm.ci.hotspot.HotSpotProfilingInfo;
8382
import jdk.vm.ci.hotspot.HotSpotResolvedJavaField;
@@ -792,8 +791,12 @@ public static CompilerInterfaceDeclarations build() {
792791
// Record calls to be able to format stable lambda names during replay (using StableMethodNameFormatter).
793792
HotSpotResolvedJavaMethod method = (HotSpotResolvedJavaMethod) receiver;
794793
List<MethodCallToRecord> calls = new ArrayList<>();
795-
if (LambdaUtils.isLambdaType(method.getDeclaringClass()) || isMethodHandle(method.getDeclaringClass())) {
794+
ResolvedJavaType holder = method.getDeclaringClass();
795+
if (holder.isHidden()) {
796796
ConstantPool constantPool = method.getConstantPool();
797+
calls.add(new MethodCallToRecord(holder, HotSpotResolvedObjectTypeProxy.getDeclaredConstructorsMethod, HotSpotResolvedObjectTypeProxy.getDeclaredConstructorsInvokable, null));
798+
calls.add(new MethodCallToRecord(holder, HotSpotResolvedObjectTypeProxy.getInterfacesMethod, HotSpotResolvedObjectTypeProxy.getInterfacesInvokable, null));
799+
calls.add(new MethodCallToRecord(holder, HotSpotResolvedObjectTypeProxy.getDeclaredMethodsBooleanMethod, HotSpotResolvedObjectTypeProxy.getDeclaredMethodsBooleanInvokable, new Object[]{false}));
797800
calls.add(new MethodCallToRecord(method, HotSpotResolvedJavaMethodProxy.getConstantPoolMethod, HotSpotResolvedJavaMethodProxy.getConstantPoolInvokable, null));
798801
for (BytecodeStream stream = new BytecodeStream(method.getCode()); stream.currentBCI() < stream.endBCI(); stream.next()) {
799802
int opcode = stream.currentBC();
@@ -817,6 +820,11 @@ public static CompilerInterfaceDeclarations build() {
817820
}
818821
return calls;
819822
})
823+
.setStrategy(HotSpotResolvedJavaMethodProxy.isBridgeMethod, MethodStrategy.DefaultValue)
824+
.setDefaultValueSupplier(HotSpotResolvedJavaMethodProxy.isBridgeMethod, (proxy, method, args, metaAccess) -> {
825+
ResolvedJavaMethod javaMethod = (ResolvedJavaMethod) proxy;
826+
return (javaMethod.getModifiers() & HotSpotModifiers.BRIDGE) != 0;
827+
})
820828
.register(declarations);
821829

822830
new RegistrationBuilder<>(Signature.class)

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/RecordedOperationPersistence.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ private static Map<String, Class<?>> createKnownClasses() {
207207
// Needed to deserialize array component types
208208
HotSpotResolvedJavaMethod.class, HotSpotResolvedJavaField.class, HotSpotResolvedObjectType.class,
209209
Object.class, ExceptionHandler.class, TriState.class, AllocatableValue.class, Value.class,
210+
ResolvedJavaMethod.class,
210211
// Needed to deserialize Field objects
211212
String.class,
212213
};

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/HotSpotResolvedJavaMethodProxy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public boolean isVarArgs() {
132132
return (boolean) handle(isVarArgsMethod, isVarArgsInvokable);
133133
}
134134

135-
private static final SymbolicMethod isBridgeMethod = method("isBridge");
135+
public static final SymbolicMethod isBridgeMethod = method("isBridge");
136136
private static final InvokableMethod isBridgeInvokable = (receiver, args) -> ((HotSpotResolvedJavaMethod) receiver).isBridge();
137137

138138
@Override

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/HotSpotResolvedJavaTypeProxy.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,8 @@ public final HotSpotResolvedObjectType getSuperclass() {
283283
return (HotSpotResolvedObjectType) handle(getSuperclassMethod, getSuperclassInvokable);
284284
}
285285

286-
private static final SymbolicMethod getInterfacesMethod = method("getInterfaces");
287-
private static final InvokableMethod getInterfacesInvokable = (receiver, args) -> ((HotSpotResolvedJavaType) receiver).getInterfaces();
286+
public static final SymbolicMethod getInterfacesMethod = method("getInterfaces");
287+
public static final InvokableMethod getInterfacesInvokable = (receiver, args) -> ((HotSpotResolvedJavaType) receiver).getInterfaces();
288288

289289
@Override
290290
public final HotSpotResolvedObjectType[] getInterfaces() {
@@ -414,8 +414,8 @@ public List<? extends ResolvedJavaRecordComponent> getRecordComponents() {
414414
return (List<ResolvedJavaRecordComponent>) handle(getRecordComponentsMethod, getRecordComponentsInvokable);
415415
}
416416

417-
private static final SymbolicMethod getDeclaredConstructorsMethod = method("getDeclaredConstructors");
418-
private static final InvokableMethod getDeclaredConstructorsInvokable = (receiver, args) -> ((HotSpotResolvedJavaType) receiver).getDeclaredConstructors();
417+
public static final SymbolicMethod getDeclaredConstructorsMethod = method("getDeclaredConstructors");
418+
public static final InvokableMethod getDeclaredConstructorsInvokable = (receiver, args) -> ((HotSpotResolvedJavaType) receiver).getDeclaredConstructors();
419419

420420
@Override
421421
public final ResolvedJavaMethod[] getDeclaredConstructors() {
@@ -447,8 +447,8 @@ public List<ResolvedJavaMethod> getAllMethods(boolean forceLink) {
447447
return (List<ResolvedJavaMethod>) handle(getAllMethodsMethod, getAllMethodsInvokable, forceLink);
448448
}
449449

450-
private static final SymbolicMethod getDeclaredMethodsBooleanMethod = method("getDeclaredMethods", boolean.class);
451-
private static final InvokableMethod getDeclaredMethodsBooleanInvokable = (receiver, args) -> ((HotSpotResolvedJavaType) receiver).getDeclaredMethods((boolean) args[0]);
450+
public static final SymbolicMethod getDeclaredMethodsBooleanMethod = method("getDeclaredMethods", boolean.class);
451+
public static final InvokableMethod getDeclaredMethodsBooleanInvokable = (receiver, args) -> ((HotSpotResolvedJavaType) receiver).getDeclaredMethods((boolean) args[0]);
452452

453453
@Override
454454
public final ResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) {

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/LambdaUtils.java

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -58,41 +58,28 @@ private LambdaUtils() {
5858
}
5959

6060
/**
61-
* Creates a stable name for a lambda by hashing all the invokes in the lambda. Lambda class
62-
* names are typically created based on an increasing atomic counter (e.g.
63-
* {@code Test$$Lambda$23}). A stable name is created by replacing the substring after
64-
* {@code "$$Lambda$"} with a hash of the method descriptor for each method invoked by the
65-
* lambda.
66-
*
67-
* Starting from JDK17, the lambda classes can have additional interfaces that lambda should
68-
* implement. This further means that lambda can have more than one public method (public and
69-
* not bridge).
70-
*
71-
* The scala lambda classes have by default one additional interface with one method. This
72-
* method has the same signature as the original one but with generalized parameters (all
73-
* parameters are Object types) and serves as a wrapper that casts parameters to specialized
74-
* types and calls an original method.
61+
* Creates a stable name for a lambda by replacing the unqualified name of the hidden class name
62+
* with a stable {@link #getSignature signature}.
7563
*
7664
* @param lambdaType the lambda type to analyze
7765
* @return stable name for the lambda class
7866
*/
7967
@SuppressWarnings("try")
8068
public static String findStableLambdaName(ResolvedJavaType lambdaType) {
81-
ResolvedJavaMethod[] lambdaProxyMethods = Arrays.stream(lambdaType.getDeclaredMethods(false)).filter(m -> !m.isBridge() && m.isPublic()).toArray(ResolvedJavaMethod[]::new);
82-
/*
83-
* Take only the first method to find invoked methods, because the result would be the same
84-
* for all other methods.
85-
*/
86-
List<JavaMethod> invokedMethods = findInvokedMethods(lambdaProxyMethods[0]);
87-
if (invokedMethods.isEmpty()) {
69+
final String lambdaName = lambdaType.getName();
70+
assert lambdaMatcher(lambdaName).find() : "Stable name should be created for lambda types: " + lambdaName;
71+
72+
Matcher matcher = lambdaMatcher(lambdaName);
73+
String signature = getSignature(lambdaType);
74+
if (signature == null) {
8875
StringBuilder sb = new StringBuilder();
8976
sb.append("Lambda without a target invoke: ").append(lambdaType.toClassName());
90-
for (ResolvedJavaMethod m : lambdaType.getDeclaredMethods(false)) {
91-
sb.append("\n Method: ").append(m);
77+
for (ResolvedJavaMethod method : lambdaType.getDeclaredMethods(false)) {
78+
sb.append("\n Method: ").append(method);
9279
}
9380
throw new JVMCIError(sb.toString());
9481
}
95-
return createStableLambdaName(lambdaType, invokedMethods);
82+
return matcher.replaceFirst(Matcher.quoteReplacement(LAMBDA_CLASS_NAME_SUBSTRING + ADDRESS_PREFIX + signature + ";"));
9683
}
9784

9885
/**
@@ -101,7 +88,7 @@ public static String findStableLambdaName(ResolvedJavaType lambdaType) {
10188
* @param method the method whose bytecode is parsed
10289
* @return the list of invoked methods
10390
*/
104-
public static List<JavaMethod> findInvokedMethods(ResolvedJavaMethod method) {
91+
private static List<JavaMethod> findInvokedMethods(ResolvedJavaMethod method) {
10592
ConstantPool constantPool = method.getConstantPool();
10693
List<JavaMethod> invokedMethods = new ArrayList<>();
10794
for (BytecodeStream stream = new BytecodeStream(method.getCode()); stream.currentBCI() < stream.endBCI(); stream.next()) {
@@ -142,11 +129,36 @@ public static boolean isLambdaName(String name) {
142129
return isLambdaClassName(name) && lambdaMatcher(name).find();
143130
}
144131

145-
private static String createStableLambdaName(ResolvedJavaType lambdaType, List<JavaMethod> invokedMethods) {
146-
final String lambdaName = lambdaType.getName();
147-
assert lambdaMatcher(lambdaName).find() : "Stable name should be created for lambda types: " + lambdaName;
132+
/**
133+
* Generates a signature for a given lambda type by hashing its composing parts. The signature
134+
* is generated based on the methods invoked in the lambda's bytecode, the constructor parameter
135+
* types, and the interfaces implemented by the lambda. Returns {@code null} if the lambda type
136+
* does not have any invoked methods.
137+
* <p>
138+
* Starting from JDK17, the lambda classes can have additional interfaces that lambda should
139+
* implement. This further means that lambda can have more than one public method (public and
140+
* not bridge).
141+
* <p>
142+
* The scala lambda classes have by default one additional interface with one method. This
143+
* method has the same signature as the original one but with generalized parameters (all
144+
* parameters are Object types) and serves as a wrapper that casts parameters to specialized
145+
* types and calls an original method.
146+
*
147+
* @param lambdaType the lambda type to generate a signature for
148+
* @return a 32-character hexadecimal string representing the lambda's signature or {@code null}
149+
* if the lambda type does not have any invoked methods
150+
*/
151+
public static String getSignature(ResolvedJavaType lambdaType) {
152+
ResolvedJavaMethod[] lambdaProxyMethods = Arrays.stream(lambdaType.getDeclaredMethods(false)).filter(m -> !m.isBridge() && m.isPublic()).toArray(ResolvedJavaMethod[]::new);
153+
/*
154+
* Take only the first method to find invoked methods, because the result would be the same
155+
* for all other methods.
156+
*/
157+
List<JavaMethod> invokedMethods = findInvokedMethods(lambdaProxyMethods[0]);
158+
if (invokedMethods.isEmpty()) {
159+
return null;
160+
}
148161

149-
Matcher m = lambdaMatcher(lambdaName);
150162
/* Generate lambda signature by hashing its composing parts. */
151163
StringBuilder sb = new StringBuilder();
152164
/* Append invoked methods. */
@@ -163,7 +175,7 @@ private static String createStableLambdaName(ResolvedJavaType lambdaType, List<J
163175
}
164176
String signature = Digest.digestAsHex(sb.toString());
165177
GraalError.guarantee(signature.length() == 32, "Expecting a 32 digits long hex value.");
166-
return m.replaceFirst(Matcher.quoteReplacement(LAMBDA_CLASS_NAME_SUBSTRING + ADDRESS_PREFIX + signature + ";"));
178+
return signature;
167179
}
168180

169181
private static Matcher lambdaMatcher(String value) {

0 commit comments

Comments
 (0)