|
24 | 24 | */
|
25 | 25 | package com.oracle.svm.hosted.methodhandles;
|
26 | 26 |
|
| 27 | +import java.lang.reflect.Field; |
27 | 28 | import java.lang.reflect.Method;
|
28 | 29 | import java.util.HashSet;
|
29 | 30 | import java.util.List;
|
|
47 | 48 | * the {@code LambdaForm} which they were compiled from.
|
48 | 49 | */
|
49 | 50 | public class MethodHandleInvokerRenamingSubstitutionProcessor extends SubstitutionProcessor {
|
50 |
| - private static final Class<?> LAMBDA_FORM_CLASS = ReflectionUtil.lookupClass(false, "java.lang.invoke.LambdaForm"); |
51 | 51 | private static final Method CLASS_GET_CLASS_DATA_METHOD = ReflectionUtil.lookupMethod(Class.class, "getClassData");
|
| 52 | + private static final Class<?> LAMBDA_FORM_CLASS = ReflectionUtil.lookupClass(false, "java.lang.invoke.LambdaForm"); |
| 53 | + private static final Field LAMBDA_FORM_CUSTOMIZED_FIELD = ReflectionUtil.lookupField(LAMBDA_FORM_CLASS, "customized"); |
| 54 | + private static final Class<?> DIRECT_METHOD_HANDLE_CLASS = ReflectionUtil.lookupClass(false, "java.lang.invoke.DirectMethodHandle"); |
| 55 | + private static final Method DIRECT_METHOD_HANDLE_INTERNAL_MEMBER_NAME_METHOD = ReflectionUtil.lookupMethod(DIRECT_METHOD_HANDLE_CLASS, "internalMemberName"); |
52 | 56 |
|
53 | 57 | /*
|
54 | 58 | * We currently only replace the invokers of direct method handles which have a simpler
|
@@ -88,15 +92,39 @@ public ResolvedJavaType resolve(ResolvedJavaType type) {
|
88 | 92 |
|
89 | 93 | private ResolvedJavaType getSubstitution(ResolvedJavaType type) {
|
90 | 94 | return typeSubstitutions.computeIfAbsent(type, original -> {
|
| 95 | + Object lambdaForm; |
| 96 | + Object customizedMemberName = null; |
91 | 97 | try {
|
92 | 98 | Class<?> clazz = OriginalClassProvider.getJavaClass(original);
|
93 | 99 | Object classData = CLASS_GET_CLASS_DATA_METHOD.invoke(clazz);
|
94 |
| - VMError.guarantee(LAMBDA_FORM_CLASS.isInstance(classData)); |
95 |
| - int hash = classData.hashCode(); |
96 |
| - return new MethodHandleInvokerSubstitutionType(original, findUniqueName(hash)); |
| 100 | + if (LAMBDA_FORM_CLASS.isInstance(classData)) { |
| 101 | + lambdaForm = classData; |
| 102 | + } else if (classData instanceof List<?> list && list.size() == 2) { |
| 103 | + lambdaForm = list.get(0); |
| 104 | + Object customizedHandle = list.get(1); |
| 105 | + VMError.guarantee(LAMBDA_FORM_CLASS.isInstance(lambdaForm) && DIRECT_METHOD_HANDLE_CLASS.isInstance(customizedHandle) && |
| 106 | + LAMBDA_FORM_CUSTOMIZED_FIELD.get(lambdaForm) == customizedHandle, "Expected classData to contain LambdaForm and its customization: %s", classData); |
| 107 | + customizedMemberName = DIRECT_METHOD_HANDLE_INTERNAL_MEMBER_NAME_METHOD.invoke(customizedHandle); |
| 108 | + } else { |
| 109 | + throw VMError.shouldNotReachHere("Unexpected classData: %s", classData); |
| 110 | + } |
97 | 111 | } catch (ReflectiveOperationException e) {
|
98 | 112 | throw VMError.shouldNotReachHere(e);
|
99 | 113 | }
|
| 114 | + /* |
| 115 | + * LambdaForm.hashCode() is not stable between image builds because it incorporates |
| 116 | + * identity hash codes of objects such as those of Class<?> that don't override |
| 117 | + * hashCode(). For that reason, we compute a hash code from LambdaForm.toString(). It |
| 118 | + * might also not be perfectly unique because the string contains unqualified class |
| 119 | + * names and can contain string representations of constraints that may be arbitrary |
| 120 | + * objects, but it should typically be distinct and stable. |
| 121 | + */ |
| 122 | + int hash = lambdaForm.toString().hashCode(); |
| 123 | + if (customizedMemberName != null) { |
| 124 | + /* MemberName.hashCode() also includes identity hash codes of Class<?> objects. */ |
| 125 | + hash = hash * 31 + customizedMemberName.toString().hashCode(); |
| 126 | + } |
| 127 | + return new MethodHandleInvokerSubstitutionType(original, findUniqueName(hash)); |
100 | 128 | });
|
101 | 129 | }
|
102 | 130 |
|
|
0 commit comments