Skip to content

Commit 26b725c

Browse files
committed
Load lambda types in the application layer
1 parent 4e691ab commit 26b725c

File tree

6 files changed

+121
-8
lines changed

6 files changed

+121
-8
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ public class ImageLayerSnapshotUtil {
9999
public static final String INTERFACES_TAG = "interfaces";
100100
public static final String WRAPPED_TYPE_TAG = "wrapped type";
101101
public static final String GENERATED_SERIALIZATION_TAG = "generated serialization";
102+
public static final String LAMBDA_TYPE_TAG = "lambda type";
103+
public static final String HOLDER_CLASS_TAG = "holder class";
102104
public static final String RAW_DECLARING_CLASS_TAG = "raw declaring class";
103105
public static final String RAW_TARGET_CONSTRUCTOR_CLASS_TAG = "raw target constructor class";
104106
public static final String CONSTANTS_TAG = "constants";

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/bootstrap/BootstrapMethodConfiguration.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ public record BootstrapMethodRecord(int bci, int cpi, ResolvedJavaMethod method)
6969
private final ConcurrentMap<BootstrapMethodRecord, BootstrapMethodInfo> bootstrapMethodInfoCache = new ConcurrentHashMap<>();
7070
private final Set<Executable> indyBuildTimeAllowList;
7171
private final Set<Executable> condyBuildTimeAllowList;
72+
private final Method metafactory;
73+
private final Method altMetafactory;
7274

7375
public static BootstrapMethodConfiguration singleton() {
7476
return ImageSingletons.lookup(BootstrapMethodConfiguration.class);
@@ -79,10 +81,10 @@ public BootstrapMethodConfiguration() {
7981
* Bootstrap method used for Lambdas. Executing this method at run time implies defining
8082
* hidden class at run time, which is unsupported.
8183
*/
82-
Method metafactory = ReflectionUtil.lookupMethod(LambdaMetafactory.class, "metafactory", MethodHandles.Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class,
84+
metafactory = ReflectionUtil.lookupMethod(LambdaMetafactory.class, "metafactory", MethodHandles.Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class,
8385
MethodType.class);
8486
/* Alternate version of LambdaMetafactory.metafactory. */
85-
Method altMetafactory = ReflectionUtil.lookupMethod(LambdaMetafactory.class, "altMetafactory", MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class);
87+
altMetafactory = ReflectionUtil.lookupMethod(LambdaMetafactory.class, "altMetafactory", MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class);
8688

8789
/*
8890
* Bootstrap method used to optimize String concatenation. Executing it at run time
@@ -143,6 +145,10 @@ public boolean isIndyAllowedAtBuildTime(Executable method) {
143145
return method != null && indyBuildTimeAllowList.contains(method);
144146
}
145147

148+
public boolean isMetafactory(Executable method) {
149+
return method != null && (method.equals(metafactory) || method.equals(altMetafactory));
150+
}
151+
146152
/**
147153
* Check if the provided method is allowed to be executed at build time.
148154
*/

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import com.oracle.graal.pointsto.meta.AnalysisMethod;
6464
import com.oracle.graal.pointsto.meta.AnalysisType;
6565
import com.oracle.graal.pointsto.util.AnalysisError;
66+
import com.oracle.graal.pointsto.util.AnalysisFuture;
6667
import com.oracle.svm.core.SubstrateOptions;
6768
import com.oracle.svm.core.TypeResult;
6869
import com.oracle.svm.core.classinitialization.ClassInitializationInfo;
@@ -228,10 +229,15 @@ protected void prepareConstantRelinking(EconomicMap<String, Object> constantData
228229
@Override
229230
protected boolean delegateProcessing(String constantType, Object constantValue, List<Object> constantData, Object[] values, int i) {
230231
if (constantType.equals(METHOD_POINTER_TAG)) {
231-
AnalysisType methodPointerType = metaAccess.lookupJavaType(MethodPointer.class);
232-
int mid = (int) constantValue;
233-
AnalysisMethod method = getAnalysisMethod(mid);
234-
values[i] = new RelocatableConstant(new MethodPointer(method), methodPointerType);
232+
AnalysisFuture<JavaConstant> task = new AnalysisFuture<>(() -> {
233+
AnalysisType methodPointerType = metaAccess.lookupJavaType(MethodPointer.class);
234+
int mid = (int) constantValue;
235+
AnalysisMethod method = getAnalysisMethod(mid);
236+
RelocatableConstant constant = new RelocatableConstant(new MethodPointer(method), methodPointerType);
237+
values[i] = constant;
238+
return constant;
239+
});
240+
values[i] = task;
235241
return true;
236242
} else if (constantType.equals(C_ENTRY_POINT_LITERAL_CODE_POINTER)) {
237243
AnalysisType cEntryPointerLiteralPointerType = metaAccess.lookupJavaType(CEntryPointLiteralCodePointer.class);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoaderHelper.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import static com.oracle.graal.pointsto.heap.ImageLayerLoader.get;
2828
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FACTORY_TAG;
2929
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.GENERATED_SERIALIZATION_TAG;
30+
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.HOLDER_CLASS_TAG;
31+
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.LAMBDA_TYPE_TAG;
3032
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RAW_DECLARING_CLASS_TAG;
3133
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RAW_TARGET_CONSTRUCTOR_CLASS_TAG;
3234
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TARGET_CONSTRUCTOR_TAG;
@@ -35,19 +37,42 @@
3537
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_TYPE_TAG;
3638

3739
import java.lang.reflect.Constructor;
40+
import java.lang.reflect.Executable;
41+
import java.util.Arrays;
42+
import java.util.Objects;
43+
import java.util.Optional;
44+
import java.util.stream.Stream;
3845

3946
import org.graalvm.collections.EconomicMap;
47+
import org.graalvm.nativeimage.AnnotationAccess;
4048

4149
import com.oracle.graal.pointsto.heap.ImageLayerLoader;
4250
import com.oracle.graal.pointsto.heap.ImageLayerLoaderHelper;
51+
import com.oracle.graal.pointsto.infrastructure.OriginalMethodProvider;
52+
import com.oracle.graal.pointsto.meta.AnalysisMethod;
53+
import com.oracle.graal.pointsto.meta.AnalysisType;
54+
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
55+
import com.oracle.svm.core.annotate.TargetClass;
56+
import com.oracle.svm.core.bootstrap.BootstrapMethodConfiguration;
4357
import com.oracle.svm.core.reflect.serialize.SerializationSupport;
58+
import com.oracle.svm.core.util.VMError;
4459
import com.oracle.svm.hosted.code.FactoryMethodSupport;
4560
import com.oracle.svm.hosted.reflect.serialize.SerializationFeature;
61+
import com.oracle.svm.hosted.substitute.SubstitutionMethod;
4662
import com.oracle.svm.util.ReflectionUtil;
4763

64+
import jdk.graal.compiler.serviceprovider.JavaVersionUtil;
4865
import jdk.internal.reflect.ReflectionFactory;
66+
import jdk.vm.ci.meta.ConstantPool;
67+
import jdk.vm.ci.meta.JavaConstant;
4968

5069
public class SVMImageLayerLoaderHelper extends ImageLayerLoaderHelper {
70+
private static final Class<?> DIRECT_METHOD_HANDLE_STATIC_ACCESSOR_CLASS = ReflectionUtil.lookupClass(false, "java.lang.invoke.DirectMethodHandle$StaticAccessor");
71+
private static final Class<?> DIRECT_METHOD_HANDLE_CONSTRUCTOR_CLASS = ReflectionUtil.lookupClass(false, "java.lang.invoke.DirectMethodHandle$Constructor");
72+
private static final String STATIC_BASE_FIELD_NAME = "staticBase";
73+
private static final String INSTANCE_CLASS_FIELD_NAME = "instanceClass";
74+
private static final int INVOKE_DYNAMIC_OPCODE = 186;
75+
5176
public SVMImageLayerLoaderHelper(ImageLayerLoader imageLayerLoader) {
5277
super(imageLayerLoader);
5378
}
@@ -70,11 +95,77 @@ protected boolean loadType(EconomicMap<String, Object> typeData, int tid) {
7095
Class<?> constructorAccessor = serializationSupport.getSerializationConstructorAccessor(rawDeclaringClass, rawTargetConstructorClass).getClass();
7196
imageLayerLoader.getMetaAccess().lookupJavaType(constructorAccessor);
7297
return true;
98+
} else if (wrappedType.equals(LAMBDA_TYPE_TAG)) {
99+
String holderClassName = get(typeData, HOLDER_CLASS_TAG);
100+
Class<?> holderClass = imageLayerLoader.lookupClass(false, holderClassName);
101+
loadLambdaTypes(holderClass);
102+
return true;
73103
}
74104

75105
return super.loadType(typeData, tid);
76106
}
77107

108+
/**
109+
* The constant pool index of bootstrap method is not stable in different JVM instances, so the
110+
* only solution is to load all lambda types of the given holder class.
111+
*/
112+
private void loadLambdaTypes(Class<?> holderClass) {
113+
AnalysisUniverse universe = imageLayerLoader.getUniverse();
114+
AnalysisType type = universe.getBigbang().getMetaAccess().lookupJavaType(holderClass);
115+
boolean isSubstitution = AnnotationAccess.isAnnotationPresent(holderClass, TargetClass.class);
116+
ConstantPool constantPool = getConstantPool(type, isSubstitution);
117+
int index = JavaVersionUtil.JAVA_SPEC > 21 ? 0 : -1;
118+
ConstantPool.BootstrapMethodInvocation bootstrap;
119+
while ((bootstrap = getBootstrap(constantPool, index)) != null) {
120+
if (BootstrapMethodConfiguration.singleton().isMetafactory(OriginalMethodProvider.getJavaMethod(bootstrap.getMethod()))) {
121+
constantPool.loadReferencedType(index, INVOKE_DYNAMIC_OPCODE);
122+
JavaConstant test = constantPool.lookupAppendix(index, INVOKE_DYNAMIC_OPCODE);
123+
Object appendix = universe.getSnippetReflection().asObject(Object.class, test);
124+
125+
Class<?> potentialLambdaClass;
126+
if (DIRECT_METHOD_HANDLE_STATIC_ACCESSOR_CLASS.isInstance(appendix)) {
127+
potentialLambdaClass = ReflectionUtil.readField(DIRECT_METHOD_HANDLE_STATIC_ACCESSOR_CLASS, STATIC_BASE_FIELD_NAME, appendix);
128+
} else if (DIRECT_METHOD_HANDLE_CONSTRUCTOR_CLASS.isInstance(appendix)) {
129+
potentialLambdaClass = ReflectionUtil.readField(DIRECT_METHOD_HANDLE_CONSTRUCTOR_CLASS, INSTANCE_CLASS_FIELD_NAME, appendix);
130+
} else {
131+
throw VMError.shouldNotReachHere("Unexpected appendix %s", appendix);
132+
}
133+
universe.getBigbang().getMetaAccess().lookupJavaType(potentialLambdaClass);
134+
}
135+
if (JavaVersionUtil.JAVA_SPEC > 21) {
136+
index++;
137+
} else {
138+
index--;
139+
}
140+
}
141+
}
142+
143+
/**
144+
* A default and substitution class have two different constant pools. The constant pool can
145+
* only be fetched through the methods of the class, so we iterate over the methods and the
146+
* constructors and take the first constant pool that matches the current class.
147+
*/
148+
private static ConstantPool getConstantPool(AnalysisType type, boolean isSubstitution) {
149+
Stream<AnalysisMethod> candidates = Stream.concat(Arrays.stream(type.getDeclaredMethods(false)), Arrays.stream(type.getDeclaredConstructors(false)));
150+
Optional<ConstantPool> cp = candidates.map(method -> {
151+
Executable javaMethod = method.getJavaMethod();
152+
if (((javaMethod != null && AnnotationAccess.isAnnotationPresent(javaMethod.getDeclaringClass(), TargetClass.class)) || (method.wrapped instanceof SubstitutionMethod)) == isSubstitution) {
153+
return method.getConstantPool();
154+
}
155+
return null;
156+
}).filter(Objects::nonNull).findAny();
157+
assert cp.isPresent() : String.format("No constant pool was found in the %s class.", isSubstitution ? "substitution" : "default");
158+
return cp.get();
159+
}
160+
161+
private static ConstantPool.BootstrapMethodInvocation getBootstrap(ConstantPool constantPool, int index) {
162+
try {
163+
return constantPool.lookupBootstrapMethodInvocation(index, INVOKE_DYNAMIC_OPCODE);
164+
} catch (IndexOutOfBoundsException e) {
165+
return null;
166+
}
167+
}
168+
78169
@Override
79170
protected boolean loadMethod(EconomicMap<String, Object> methodData, int mid) {
80171
String wrappedMethod = get(methodData, WRAPPED_METHOD_TAG);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@
7272
import jdk.graal.compiler.nodes.EncodedGraph;
7373
import jdk.graal.compiler.util.ObjectCopier;
7474
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
75-
import jdk.vm.ci.meta.ResolvedJavaMethod;
7675

7776
public class SVMImageLayerSnapshotUtil extends ImageLayerSnapshotUtil {
7877
public static final String GENERATED_SERIALIZATION = "jdk.internal.reflect.GeneratedSerializationConstructorAccessor";
@@ -171,7 +170,7 @@ public String getMethodIdentifier(AnalysisMethod method) {
171170
return getGeneratedSerializationName(declaringClass) + ":" + method.getName();
172171
}
173172
if (method.wrapped instanceof FactoryMethod factoryMethod) {
174-
ResolvedJavaMethod targetConstructor = factoryMethod.getTargetConstructor();
173+
AnalysisMethod targetConstructor = method.getUniverse().lookup(factoryMethod.getTargetConstructor());
175174
return addModuleName(targetConstructor.getDeclaringClass().toJavaName(true) + getQualifiedName(method), moduleName);
176175
}
177176
if (method.wrapped instanceof IncompatibleClassChangeFallbackMethod) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_ID_TAG;
2828
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.C_ENTRY_POINT_LITERAL_CODE_POINTER;
29+
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.HOLDER_CLASS_TAG;
2930
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.HUB_IDENTITY_HASH_CODE_TAG;
3031
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_KEYS;
3132
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_OBJECTS;
@@ -40,11 +41,13 @@
4041
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INITIALIZED_AT_BUILD_TIME_TAG;
4142
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INITIALIZED_NO_TRACKING_TAG;
4243
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_NO_INITIALIZER_NO_TRACKING_TAG;
44+
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.LAMBDA_TYPE_TAG;
4345
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.LOCATION_TAG;
4446
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.METHOD_POINTER_TAG;
4547
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.OBJECT_OFFSET_TAG;
4648
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STATIC_OBJECT_FIELDS_TAG;
4749
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STATIC_PRIMITIVE_FIELDS_TAG;
50+
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_TYPE_TAG;
4851

4952
import java.util.ArrayList;
5053
import java.util.Arrays;
@@ -92,6 +95,7 @@
9295
import com.oracle.svm.hosted.reflect.proxy.ProxySubstitutionType;
9396
import com.oracle.svm.util.LogUtils;
9497

98+
import jdk.graal.compiler.java.LambdaUtils;
9599
import jdk.vm.ci.meta.JavaConstant;
96100
import jdk.vm.ci.meta.ResolvedJavaMethod;
97101
import jdk.vm.ci.meta.ResolvedJavaType;
@@ -149,6 +153,11 @@ protected void persistType(AnalysisType type, EconomicMap<String, Object> typeMa
149153
}
150154
}
151155

156+
if (LambdaUtils.isLambdaType(type)) {
157+
typeMap.put(WRAPPED_TYPE_TAG, LAMBDA_TYPE_TAG);
158+
typeMap.put(HOLDER_CLASS_TAG, LambdaUtils.capturingClass(type.toJavaName()));
159+
}
160+
152161
super.persistType(type, typeMap);
153162
}
154163

0 commit comments

Comments
 (0)