3535import java .util .Map ;
3636import java .util .Objects ;
3737import java .util .concurrent .ConcurrentHashMap ;
38+ import java .util .function .BiConsumer ;
3839import java .util .function .BiFunction ;
3940import java .util .function .Consumer ;
40- import java .util .stream .Stream ;
4141
42+ import org .graalvm .nativeimage .AnnotationAccess ;
4243import org .graalvm .nativeimage .ImageSingletons ;
4344import org .graalvm .word .Pointer ;
4445
5455import com .oracle .svm .core .feature .AutomaticallyRegisteredFeature ;
5556import com .oracle .svm .core .feature .InternalFeature ;
5657import com .oracle .svm .core .graal .code .CGlobalDataInfo ;
58+ import com .oracle .svm .core .imagelayer .DynamicImageLayerInfo ;
5759import com .oracle .svm .core .imagelayer .ImageLayerBuildingSupport ;
5860import com .oracle .svm .core .imagelayer .LoadImageSingletonFactory ;
5961import com .oracle .svm .core .layeredimagesingleton .ApplicationLayerOnlyImageSingleton ;
6365import com .oracle .svm .core .layeredimagesingleton .LayeredImageSingleton ;
6466import com .oracle .svm .core .layeredimagesingleton .LayeredImageSingletonBuilderFlags ;
6567import com .oracle .svm .core .layeredimagesingleton .LayeredImageSingletonSupport ;
68+ import com .oracle .svm .core .layeredimagesingleton .MultiLayeredAllowNullEntries ;
6669import com .oracle .svm .core .layeredimagesingleton .MultiLayeredImageSingleton ;
6770import com .oracle .svm .core .layeredimagesingleton .UnsavedSingleton ;
71+ import com .oracle .svm .core .util .UserError ;
6872import com .oracle .svm .core .util .VMError ;
6973import com .oracle .svm .hosted .FeatureImpl ;
7074import com .oracle .svm .hosted .c .CGlobalDataFeature ;
7478import com .oracle .svm .util .ReflectionUtil ;
7579
7680import jdk .graal .compiler .api .replacements .SnippetReflectionProvider ;
81+ import jdk .graal .compiler .debug .GraalError ;
7782import jdk .graal .compiler .nodes .ConstantNode ;
7883import jdk .graal .compiler .nodes .ValueNode ;
7984import jdk .graal .compiler .nodes .graphbuilderconf .GraphBuilderConfiguration ;
@@ -166,6 +171,12 @@ public void duringSetup(DuringSetupAccess access) {
166171 LayeredImageHeapObjectAdder .singleton ().registerObjectAdder (this ::addInitialObjects );
167172 }
168173
174+ static void checkAllowNullEntries (Class <?> key ) {
175+ boolean nullEntriesAllowed = AnnotationAccess .isAnnotationPresent (key , MultiLayeredAllowNullEntries .class );
176+ UserError .guarantee (nullEntriesAllowed ,
177+ "This MultiLayeredSingleton requires an entry to be installed in every layer. Please see the javadoc within MultiLayeredAllowNullEntries for more details." );
178+ }
179+
169180 /**
170181 * This method needs to be called after all image singletons are registered. Currently, some
171182 * singletons are registered in
@@ -229,6 +240,8 @@ public void processRegisteredSingletons(AnalysisUniverse universe) {
229240 Class <?> key = slotInfo .keyClass ();
230241 if (ImageSingletons .contains (key )) {
231242 multiLayerEmbeddedRoots .add (layeredImageSingletonSupport .runtimeLookup (key ));
243+ } else {
244+ checkAllowNullEntries (key );
232245 }
233246 /*
234247 * Within the application layer there will be an array created to hold all
@@ -262,6 +275,9 @@ public void processRegisteredSingletons(AnalysisUniverse universe) {
262275 if (!multiLayerEmbeddedRoots .isEmpty ()) {
263276 multiLayerEmbeddedRootsRegistration .accept (multiLayerEmbeddedRoots .toArray ());
264277 }
278+ } else {
279+ // GR-58631
280+ throw GraalError .unimplemented ("More work needed for 3+ layer support" );
265281 }
266282 }
267283
@@ -273,15 +289,46 @@ public void beforeCompilation(BeforeCompilationAccess access) {
273289
274290 ImageHeapObjectArray createMultiLayerArray (Class <?> key , AnalysisType arrayType , SnippetReflectionProvider snippetReflectionProvider ) {
275291 List <Integer > priorIds = getCrossLayerSingletonMappingInfo ().getPriorLayerObjectIDs (key );
276- Stream <JavaConstant > values = priorIds .stream ().map (priorId -> loader .getOrCreateConstant (priorId ));
292+ var layerInfo = DynamicImageLayerInfo .singleton ();
293+
294+ final JavaConstant [] elements = new JavaConstant [layerInfo .numLayers ];
295+ BiConsumer <JavaConstant , Integer > installElement = (priorConstant , index ) -> {
296+ assert elements [index ] == null : elements [index ];
297+ elements [index ] = priorConstant ;
298+ };
277299
300+ // first install prior singletons
301+ priorIds .forEach (priorId -> {
302+ var priorConstant = loader .getOrCreateConstant (priorId );
303+ int index = 0 ;
304+ assert layerInfo .numLayers == 2 : "incompatible with 3+ layers" ;
305+ installElement .accept (priorConstant , index );
306+ });
307+
308+ // next install current singleton
278309 if (ImageSingletons .contains (key )) {
279310 var singleton = LayeredImageSingletonSupport .singleton ().runtimeLookup (key );
280311 JavaConstant singletonConstant = snippetReflectionProvider .forObject (singleton );
281- values = Stream .concat (values , Stream .of (singletonConstant ));
312+ installElement .accept (singletonConstant , layerInfo .layerNumber );
313+ }
314+
315+ // finally fill any missing holes
316+ boolean holesPresent = false ;
317+ for (int i = 0 ; i < layerInfo .numLayers ; i ++) {
318+ if (elements [i ] == null ) {
319+ holesPresent = true ;
320+ installElement .accept (JavaConstant .NULL_POINTER , i );
321+ }
322+ }
323+
324+ if (holesPresent ) {
325+ /*
326+ * Once more validate that null entries are allowed. However, any issues are expected to
327+ * be caught before this point.
328+ */
329+ checkAllowNullEntries (key );
282330 }
283331
284- Object [] elements = values .toArray ();
285332 return ImageHeapObjectArray .createUnbackedImageHeapArray (arrayType , elements );
286333 }
287334
@@ -477,6 +524,15 @@ private LoadImageSingletonData getImageSingletonInfo(Class<?> keyClass, SlotReco
477524 return result ;
478525 }
479526
527+ if (result .kind == SlotRecordKind .MULTI_LAYERED_SINGLETON ) {
528+ if (!ImageSingletons .contains (keyClass )) {
529+ /*
530+ * If the singleton doesn't exist, ensure this is allowed.
531+ */
532+ LoadImageSingletonFeature .checkAllowNullEntries (keyClass );
533+ }
534+ }
535+
480536 SlotInfo priorSlotInfo = priorKeyToSlotInfoMap .get (keyClass );
481537 if (priorSlotInfo != null && priorSlotInfo .recordKind () != kind ) {
482538 VMError .shouldNotReachHere ("A singleton cannot implement both %s and %s" , priorSlotInfo .recordKind (), kind );
0 commit comments