2424 */
2525package com .oracle .svm .core .hub ;
2626
27+ import java .util .ArrayList ;
28+ import java .util .Collections ;
2729import java .util .EnumSet ;
30+ import java .util .HashMap ;
31+ import java .util .List ;
32+ import java .util .Map ;
2833
2934import org .graalvm .collections .EconomicMap ;
3035import org .graalvm .nativeimage .Platform ;
3136import org .graalvm .nativeimage .Platforms ;
3237
3338import com .oracle .svm .core .feature .AutomaticallyRegisteredImageSingleton ;
3439import 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 ;
3643import com .oracle .svm .core .layeredimagesingleton .LayeredImageSingletonBuilderFlags ;
3744import com .oracle .svm .core .layeredimagesingleton .LayeredImageSingletonSupport ;
3845import 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}
0 commit comments