Skip to content

Commit 1f99808

Browse files
committed
Deduplicate reflection metadata
1 parent c26d43d commit 1f99808

File tree

5 files changed

+248
-56
lines changed

5 files changed

+248
-56
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayeredReflectionMetadataSingleton.java

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,55 @@
2424
*/
2525
package com.oracle.svm.core.hub;
2626

27+
import java.util.ArrayList;
28+
import java.util.Collections;
2729
import java.util.EnumSet;
30+
import java.util.HashMap;
31+
import java.util.List;
32+
import java.util.Map;
2833

2934
import org.graalvm.collections.EconomicMap;
3035
import org.graalvm.nativeimage.Platform;
3136
import org.graalvm.nativeimage.Platforms;
3237

3338
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
3439
import com.oracle.svm.core.hub.DynamicHub.ReflectionMetadata;
35-
import com.oracle.svm.core.imagelayer.BuildingImageLayerPredicate;
40+
import com.oracle.svm.core.imagelayer.BuildingInitialLayerPredicate;
41+
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader;
42+
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
3643
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
3744
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
3845
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
39-
import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
4046

4147
/**
4248
* This singleton stores the {@link ReflectionMetadata} of each {@link DynamicHub} across layers to
4349
* allow registering elements for reflection in extension layers too.
4450
*/
45-
@AutomaticallyRegisteredImageSingleton(onlyWith = BuildingImageLayerPredicate.class)
46-
public class LayeredReflectionMetadataSingleton implements MultiLayeredImageSingleton, UnsavedSingleton {
51+
@AutomaticallyRegisteredImageSingleton(onlyWith = BuildingInitialLayerPredicate.class)
52+
public class LayeredReflectionMetadataSingleton implements MultiLayeredImageSingleton {
53+
private static final String LAYERED_REFLECTION_METADATA_HUBS = "layered reflection metadata hubs";
54+
private static final String LAYERED_REFLECTION_METADATA_CLASS_FLAGS = "layered reflection metadata classFlags";
55+
4756
private final EconomicMap<Integer, ReflectionMetadata> reflectionMetadataMap = EconomicMap.create();
4857

58+
/**
59+
* The class flags registered in previous layers. This map is used to check if the class flags
60+
* in the current layer are the same as the previous layer. If they are the same and the rest of
61+
* the reflection metadata is empty, the class can be skipped. If the class flags of the current
62+
* layer are not a subset of the previous layer class flags, the new class flags become the
63+
* combination of both class flags through an or statement.
64+
*/
65+
@Platforms(Platform.HOSTED_ONLY.class) //
66+
private final Map<Integer, Integer> previousLayerClassFlags;
67+
68+
LayeredReflectionMetadataSingleton() {
69+
this(Map.of());
70+
}
71+
72+
LayeredReflectionMetadataSingleton(Map<Integer, Integer> previousLayerClassFlags) {
73+
this.previousLayerClassFlags = previousLayerClassFlags;
74+
}
75+
4976
@Platforms(Platform.HOSTED_ONLY.class)
5077
public static LayeredReflectionMetadataSingleton currentLayer() {
5178
return LayeredImageSingletonSupport.singleton().lookup(LayeredReflectionMetadataSingleton.class, false, true);
@@ -59,9 +86,26 @@ public static LayeredReflectionMetadataSingleton[] singletons() {
5986
public void setReflectionMetadata(DynamicHub hub, ReflectionMetadata reflectionMetadata) {
6087
/* GR-63472: Two different classes could have the same name in different class loaders */
6188
assert !reflectionMetadataMap.containsKey(hub.getTypeID()) : "The hub %s was added twice in the same layered reflection metadata".formatted(hub);
89+
if (isClassFlagsSubsetOfPreviousLayer(hub.getTypeID(), reflectionMetadata) && isReflectionMetadataEmpty(reflectionMetadata)) {
90+
return;
91+
}
6292
reflectionMetadataMap.put(hub.getTypeID(), reflectionMetadata);
6393
}
6494

95+
private boolean isClassFlagsSubsetOfPreviousLayer(int hub, ReflectionMetadata reflectionMetadata) {
96+
int previousLayerFlags = previousLayerClassFlags.getOrDefault(hub, 0);
97+
return getCombinedClassFlags(reflectionMetadata, previousLayerFlags) == previousLayerFlags;
98+
}
99+
100+
private static int getCombinedClassFlags(ReflectionMetadata reflectionMetadata, int previousLayerFlags) {
101+
return previousLayerFlags | reflectionMetadata.classFlags;
102+
}
103+
104+
private static boolean isReflectionMetadataEmpty(ReflectionMetadata reflectionMetadata) {
105+
return reflectionMetadata.fieldsEncodingIndex == -1 && reflectionMetadata.methodsEncodingIndex == -1 &&
106+
reflectionMetadata.constructorsEncodingIndex == -1 && reflectionMetadata.recordComponentsEncodingIndex == -1;
107+
}
108+
65109
public ReflectionMetadata getReflectionMetadata(DynamicHub hub) {
66110
return reflectionMetadataMap.get(hub.getTypeID());
67111
}
@@ -70,4 +114,46 @@ public ReflectionMetadata getReflectionMetadata(DynamicHub hub) {
70114
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
71115
return LayeredImageSingletonBuilderFlags.ALL_ACCESS;
72116
}
117+
118+
@Override
119+
public PersistFlags preparePersist(ImageSingletonWriter writer) {
120+
List<Integer> hubs = new ArrayList<>();
121+
List<Integer> classFlagsList = new ArrayList<>();
122+
123+
var cursor = reflectionMetadataMap.getEntries();
124+
while (cursor.advance()) {
125+
int hub = cursor.getKey();
126+
hubs.add(hub);
127+
classFlagsList.add(getCombinedClassFlags(cursor.getValue(), previousLayerClassFlags.getOrDefault(hub, 0)));
128+
}
129+
130+
for (var entry : previousLayerClassFlags.entrySet()) {
131+
if (!hubs.contains(entry.getKey())) {
132+
/*
133+
* If new class flags were written in this layer, the class flags from previous
134+
* layers need to be skipped.
135+
*/
136+
hubs.add(entry.getKey());
137+
classFlagsList.add(entry.getValue());
138+
}
139+
}
140+
141+
writer.writeIntList(LAYERED_REFLECTION_METADATA_HUBS, hubs);
142+
writer.writeIntList(LAYERED_REFLECTION_METADATA_CLASS_FLAGS, classFlagsList);
143+
144+
return PersistFlags.CREATE;
145+
}
146+
147+
@SuppressWarnings("unused")
148+
public static Object createFromLoader(ImageSingletonLoader loader) {
149+
List<Integer> hubs = loader.readIntList(LAYERED_REFLECTION_METADATA_HUBS);
150+
List<Integer> previousLayerClassFlags = loader.readIntList(LAYERED_REFLECTION_METADATA_CLASS_FLAGS);
151+
152+
Map<Integer, Integer> classDatas = new HashMap<>();
153+
for (int i = 0; i < hubs.size(); ++i) {
154+
classDatas.put(hubs.get(i), previousLayerClassFlags.get(i));
155+
}
156+
157+
return new LayeredReflectionMetadataSingleton(Collections.unmodifiableMap(classDatas));
158+
}
73159
}

substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,9 @@ struct StaticFinalFieldFoldingSingleton {
299299

300300
struct LayeredRuntimeMetadataSingleton {
301301
methods @0 :List(MethodId);
302-
fields @1 :List(FieldId);
302+
methodStates @1 :List(Bool);
303+
fields @2 :List(FieldId);
304+
fieldStates @3 :List(Bool);
303305
}
304306

305307
struct LayeredModule {

0 commit comments

Comments
 (0)