Skip to content

Commit 039ff63

Browse files
committed
Load lambda types in the application layer by reusing lambda serialization code
1 parent 1413977 commit 039ff63

File tree

8 files changed

+248
-126
lines changed

8 files changed

+248
-126
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
@@ -102,6 +102,8 @@ public class ImageLayerSnapshotUtil {
102102
public static final String INTERFACES_TAG = "interfaces";
103103
public static final String WRAPPED_TYPE_TAG = "wrapped type";
104104
public static final String GENERATED_SERIALIZATION_TAG = "generated serialization";
105+
public static final String LAMBDA_TYPE_TAG = "lambda type";
106+
public static final String CAPTURING_CLASS_TAG = "capturing class";
105107
public static final String RAW_DECLARING_CLASS_TAG = "raw declaring class";
106108
public static final String RAW_TARGET_CONSTRUCTOR_CLASS_TAG = "raw target constructor class";
107109
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;
@@ -208,10 +209,15 @@ protected void prepareConstantRelinking(EconomicMap<String, Object> constantData
208209
@Override
209210
protected boolean delegateProcessing(String constantType, Object constantValue, List<Object> constantData, Object[] values, int i) {
210211
if (constantType.equals(METHOD_POINTER_TAG)) {
211-
AnalysisType methodPointerType = metaAccess.lookupJavaType(MethodPointer.class);
212-
int mid = (int) constantValue;
213-
AnalysisMethod method = getAnalysisMethod(mid);
214-
values[i] = new RelocatableConstant(new MethodPointer(method), methodPointerType);
212+
AnalysisFuture<JavaConstant> task = new AnalysisFuture<>(() -> {
213+
AnalysisType methodPointerType = metaAccess.lookupJavaType(MethodPointer.class);
214+
int mid = (int) constantValue;
215+
AnalysisMethod method = getAnalysisMethod(mid);
216+
RelocatableConstant constant = new RelocatableConstant(new MethodPointer(method), methodPointerType);
217+
values[i] = constant;
218+
return constant;
219+
});
220+
values[i] = task;
215221
return true;
216222
} else if (constantType.equals(C_ENTRY_POINT_LITERAL_CODE_POINTER)) {
217223
AnalysisType cEntryPointerLiteralPointerType = metaAccess.lookupJavaType(CEntryPointLiteralCodePointer.class);

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,45 @@
2525
package com.oracle.svm.hosted.heap;
2626

2727
import static com.oracle.graal.pointsto.heap.ImageLayerLoader.get;
28+
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CAPTURING_CLASS_TAG;
2829
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FACTORY_TAG;
2930
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.GENERATED_SERIALIZATION_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;
3335
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.THROW_ALLOCATED_OBJECT_TAG;
3436
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_METHOD_TAG;
3537
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_TYPE_TAG;
38+
import static com.oracle.svm.hosted.lambda.LambdaParser.createMethodGraph;
39+
import static com.oracle.svm.hosted.lambda.LambdaParser.getLambdaClassFromConstantNode;
3640

3741
import java.lang.reflect.Constructor;
42+
import java.util.Map;
43+
import java.util.concurrent.ConcurrentHashMap;
3844

3945
import org.graalvm.collections.EconomicMap;
4046

47+
import com.oracle.graal.pointsto.BigBang;
4148
import com.oracle.graal.pointsto.heap.ImageLayerLoader;
4249
import com.oracle.graal.pointsto.heap.ImageLayerLoaderHelper;
50+
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
4351
import com.oracle.svm.core.reflect.serialize.SerializationSupport;
4452
import com.oracle.svm.hosted.code.FactoryMethodSupport;
53+
import com.oracle.svm.hosted.lambda.LambdaParser;
4554
import com.oracle.svm.hosted.reflect.serialize.SerializationFeature;
4655
import com.oracle.svm.util.ReflectionUtil;
4756

57+
import jdk.graal.compiler.graph.iterators.NodeIterable;
58+
import jdk.graal.compiler.nodes.ConstantNode;
59+
import jdk.graal.compiler.nodes.StructuredGraph;
60+
import jdk.graal.compiler.options.OptionValues;
4861
import jdk.internal.reflect.ReflectionFactory;
62+
import jdk.vm.ci.meta.ResolvedJavaMethod;
4963

5064
public class SVMImageLayerLoaderHelper extends ImageLayerLoaderHelper {
65+
private final Map<Class<?>, Boolean> capturingClasses = new ConcurrentHashMap<>();
66+
5167
public SVMImageLayerLoaderHelper(ImageLayerLoader imageLayerLoader) {
5268
super(imageLayerLoader);
5369
}
@@ -70,11 +86,45 @@ protected boolean loadType(EconomicMap<String, Object> typeData, int tid) {
7086
Class<?> constructorAccessor = serializationSupport.getSerializationConstructorAccessor(rawDeclaringClass, rawTargetConstructorClass).getClass();
7187
imageLayerLoader.getMetaAccess().lookupJavaType(constructorAccessor);
7288
return true;
89+
} else if (wrappedType.equals(LAMBDA_TYPE_TAG)) {
90+
String capturingClassName = get(typeData, CAPTURING_CLASS_TAG);
91+
Class<?> capturingClass = imageLayerLoader.lookupClass(false, capturingClassName);
92+
loadLambdaTypes(capturingClass);
93+
return true;
7394
}
7495

7596
return super.loadType(typeData, tid);
7697
}
7798

99+
/**
100+
* Load all lambda types of the given capturing class. Each method of the capturing class is
101+
* parsed (see {@link LambdaParser#createMethodGraph(ResolvedJavaMethod, OptionValues)}). The
102+
* lambda types can then be found in the constant nodes of the graphs.
103+
*/
104+
private void loadLambdaTypes(Class<?> capturingClass) {
105+
AnalysisUniverse universe = imageLayerLoader.getUniverse();
106+
capturingClasses.computeIfAbsent(capturingClass, key -> {
107+
LambdaParser.allExecutablesDeclaredInClass(universe.getOriginalMetaAccess().lookupJavaType(capturingClass))
108+
.filter(m -> m.getCode() != null)
109+
.forEach(m -> loadLambdaTypes(m, universe.getBigbang()));
110+
return true;
111+
});
112+
}
113+
114+
private static void loadLambdaTypes(ResolvedJavaMethod m, BigBang bigBang) {
115+
StructuredGraph graph = createMethodGraph(m, bigBang.getOptions());
116+
117+
NodeIterable<ConstantNode> constantNodes = ConstantNode.getConstantNodes(graph);
118+
119+
for (ConstantNode cNode : constantNodes) {
120+
Class<?> lambdaClass = getLambdaClassFromConstantNode(cNode);
121+
122+
if (lambdaClass != null) {
123+
bigBang.getMetaAccess().lookupJavaType(lambdaClass);
124+
}
125+
}
126+
}
127+
78128
@Override
79129
protected boolean loadMethod(EconomicMap<String, Object> methodData, int mid) {
80130
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
@@ -75,7 +75,6 @@
7575
import jdk.graal.compiler.util.ObjectCopierInputStream;
7676
import jdk.graal.compiler.util.ObjectCopierOutputStream;
7777
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
78-
import jdk.vm.ci.meta.ResolvedJavaMethod;
7978

8079
public class SVMImageLayerSnapshotUtil extends ImageLayerSnapshotUtil {
8180
public static final String GENERATED_SERIALIZATION = "jdk.internal.reflect.GeneratedSerializationConstructorAccessor";
@@ -174,7 +173,7 @@ public String getMethodIdentifier(AnalysisMethod method) {
174173
return getGeneratedSerializationName(declaringClass) + ":" + method.getName();
175174
}
176175
if (method.wrapped instanceof FactoryMethod factoryMethod) {
177-
ResolvedJavaMethod targetConstructor = factoryMethod.getTargetConstructor();
176+
AnalysisMethod targetConstructor = method.getUniverse().lookup(factoryMethod.getTargetConstructor());
178177
return addModuleName(targetConstructor.getDeclaringClass().toJavaName(true) + getQualifiedName(method), moduleName);
179178
}
180179
if (method.wrapped instanceof IncompatibleClassChangeFallbackMethod) {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
*/
2525
package com.oracle.svm.hosted.heap;
2626

27+
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CAPTURING_CLASS_TAG;
2728
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FACTORY_TAG;
2829
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.GENERATED_SERIALIZATION_TAG;
30+
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.LAMBDA_TYPE_TAG;
2931
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RAW_DECLARING_CLASS_TAG;
3032
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RAW_TARGET_CONSTRUCTOR_CLASS_TAG;
3133
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TARGET_CONSTRUCTOR_TAG;
@@ -43,6 +45,8 @@
4345
import com.oracle.svm.core.reflect.serialize.SerializationSupport;
4446
import com.oracle.svm.hosted.code.FactoryMethod;
4547

48+
import jdk.graal.compiler.java.LambdaUtils;
49+
4650
public class SVMImageLayerWriterHelper extends ImageLayerWriterHelper {
4751
public SVMImageLayerWriterHelper(ImageLayerWriter imageLayerWriter) {
4852
super(imageLayerWriter);
@@ -55,6 +59,9 @@ protected void persistType(AnalysisType type, EconomicMap<String, Object> typeMa
5559
var key = SerializationSupport.singleton().getKeyFromConstructorAccessorClass(type.getJavaClass());
5660
typeMap.put(RAW_DECLARING_CLASS_TAG, key.getDeclaringClass().getName());
5761
typeMap.put(RAW_TARGET_CONSTRUCTOR_CLASS_TAG, key.getTargetConstructorClass().getName());
62+
} else if (LambdaUtils.isLambdaType(type)) {
63+
typeMap.put(WRAPPED_TYPE_TAG, LAMBDA_TYPE_TAG);
64+
typeMap.put(CAPTURING_CLASS_TAG, LambdaUtils.capturingClass(type.toJavaName()));
5865
}
5966
super.persistType(type, typeMap);
6067
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.hosted.lambda;
26+
27+
import java.lang.reflect.Member;
28+
import java.util.Arrays;
29+
import java.util.stream.Stream;
30+
31+
import com.oracle.graal.pointsto.phases.NoClassInitializationPlugin;
32+
import com.oracle.graal.pointsto.util.GraalAccess;
33+
import com.oracle.svm.util.ClassUtil;
34+
35+
import jdk.graal.compiler.debug.DebugContext;
36+
import jdk.graal.compiler.java.BytecodeParser;
37+
import jdk.graal.compiler.java.GraphBuilderPhase;
38+
import jdk.graal.compiler.java.LambdaUtils;
39+
import jdk.graal.compiler.nodes.ConstantNode;
40+
import jdk.graal.compiler.nodes.StructuredGraph;
41+
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
42+
import jdk.graal.compiler.nodes.graphbuilderconf.IntrinsicContext;
43+
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins;
44+
import jdk.graal.compiler.nodes.spi.CoreProviders;
45+
import jdk.graal.compiler.options.OptionValues;
46+
import jdk.graal.compiler.phases.OptimisticOptimizations;
47+
import jdk.graal.compiler.phases.tiers.HighTierContext;
48+
import jdk.graal.compiler.printer.GraalDebugHandlersFactory;
49+
import jdk.graal.compiler.replacements.MethodHandlePlugin;
50+
import jdk.vm.ci.meta.Constant;
51+
import jdk.vm.ci.meta.JavaConstant;
52+
import jdk.vm.ci.meta.ResolvedJavaField;
53+
import jdk.vm.ci.meta.ResolvedJavaMethod;
54+
import jdk.vm.ci.meta.ResolvedJavaType;
55+
56+
public class LambdaParser {
57+
/**
58+
* Create a {@link StructuredGraph} using {@link LambdaGraphBuilderPhase.LambdaBytecodeParser},
59+
* a simple {@link BytecodeParser}.
60+
*/
61+
@SuppressWarnings("try")
62+
public static StructuredGraph createMethodGraph(ResolvedJavaMethod method, OptionValues options) {
63+
GraphBuilderPhase lambdaParserPhase = new LambdaParser.LambdaGraphBuilderPhase();
64+
DebugContext.Description description = new DebugContext.Description(method, ClassUtil.getUnqualifiedName(method.getClass()) + ":" + method.getName());
65+
DebugContext debug = new DebugContext.Builder(options, new GraalDebugHandlersFactory(GraalAccess.getOriginalSnippetReflection())).description(description).build();
66+
67+
HighTierContext context = new HighTierContext(GraalAccess.getOriginalProviders(), null, OptimisticOptimizations.NONE);
68+
StructuredGraph graph = new StructuredGraph.Builder(debug.getOptions(), debug)
69+
.method(method)
70+
.recordInlinedMethods(false)
71+
.build();
72+
try (DebugContext.Scope ignored = debug.scope("ParsingToMaterializeLambdas")) {
73+
lambdaParserPhase.apply(graph, context);
74+
} catch (Throwable e) {
75+
throw debug.handle(e);
76+
}
77+
return graph;
78+
}
79+
80+
public static Stream<? extends ResolvedJavaMethod> allExecutablesDeclaredInClass(ResolvedJavaType t) {
81+
return Stream.concat(Stream.concat(
82+
Arrays.stream(t.getDeclaredMethods(false)),
83+
Arrays.stream(t.getDeclaredConstructors(false))),
84+
t.getClassInitializer() == null ? Stream.empty() : Stream.of(t.getClassInitializer()));
85+
}
86+
87+
/**
88+
* Get the lambda class in the constant if it is a {@code DirectMethodHandle}, by getting the
89+
* declaring class of the {@code member} field.
90+
*/
91+
public static Class<?> getLambdaClassFromConstantNode(ConstantNode constantNode) {
92+
Constant constant = constantNode.getValue();
93+
Class<?> lambdaClass = getLambdaClassFromMemberField(constant);
94+
95+
if (lambdaClass == null) {
96+
return null;
97+
}
98+
99+
return LambdaUtils.isLambdaClass(lambdaClass) ? lambdaClass : null;
100+
}
101+
102+
private static Class<?> getLambdaClassFromMemberField(Constant constant) {
103+
ResolvedJavaType constantType = GraalAccess.getOriginalProviders().getMetaAccess().lookupJavaType((JavaConstant) constant);
104+
105+
if (constantType == null) {
106+
return null;
107+
}
108+
109+
ResolvedJavaField[] fields = constantType.getInstanceFields(true);
110+
ResolvedJavaField targetField = null;
111+
for (ResolvedJavaField field : fields) {
112+
if (field.getName().equals("member")) {
113+
targetField = field;
114+
break;
115+
}
116+
}
117+
118+
if (targetField == null) {
119+
return null;
120+
}
121+
122+
JavaConstant fieldValue = GraalAccess.getOriginalProviders().getConstantReflection().readFieldValue(targetField, (JavaConstant) constant);
123+
Member memberField = GraalAccess.getOriginalProviders().getSnippetReflection().asObject(Member.class, fieldValue);
124+
return memberField.getDeclaringClass();
125+
}
126+
127+
static class LambdaGraphBuilderPhase extends GraphBuilderPhase {
128+
LambdaGraphBuilderPhase() {
129+
super(buildLambdaParserConfig());
130+
}
131+
132+
LambdaGraphBuilderPhase(GraphBuilderConfiguration config) {
133+
super(config);
134+
}
135+
136+
private static GraphBuilderConfiguration buildLambdaParserConfig() {
137+
GraphBuilderConfiguration.Plugins plugins = new GraphBuilderConfiguration.Plugins(new InvocationPlugins());
138+
plugins.setClassInitializationPlugin(new NoClassInitializationPlugin());
139+
plugins.prependNodePlugin(new MethodHandlePlugin(GraalAccess.getOriginalProviders().getConstantReflection().getMethodHandleAccess(), false));
140+
return GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true);
141+
}
142+
143+
@Override
144+
public GraphBuilderPhase copyWithConfig(GraphBuilderConfiguration config) {
145+
return new LambdaGraphBuilderPhase(config);
146+
}
147+
148+
static class LambdaBytecodeParser extends BytecodeParser {
149+
protected LambdaBytecodeParser(Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
150+
super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext);
151+
}
152+
}
153+
154+
@Override
155+
protected Instance createInstance(CoreProviders providers, GraphBuilderConfiguration instanceGBConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) {
156+
return new Instance(providers, instanceGBConfig, optimisticOpts, initialIntrinsicContext) {
157+
@Override
158+
protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
159+
return new LambdaBytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext);
160+
}
161+
};
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)