5454import org .graalvm .nativeimage .Platforms ;
5555import org .graalvm .nativeimage .impl .ConfigurationCondition ;
5656
57+ import com .oracle .svm .core .AlwaysInline ;
5758import com .oracle .svm .core .BuildPhaseProvider ;
5859import com .oracle .svm .core .ClassLoaderSupport .ConditionWithOrigin ;
5960import com .oracle .svm .core .MissingRegistrationUtils ;
6566import com .oracle .svm .core .feature .AutomaticallyRegisteredFeature ;
6667import com .oracle .svm .core .feature .InternalFeature ;
6768import com .oracle .svm .core .imagelayer .ImageLayerBuildingSupport ;
68- import com .oracle .svm .core .jdk .resources .MissingResourceRegistrationError ;
6969import com .oracle .svm .core .jdk .resources .MissingResourceRegistrationUtils ;
7070import com .oracle .svm .core .jdk .resources .ResourceExceptionEntry ;
7171import com .oracle .svm .core .jdk .resources .ResourceStorageEntry ;
@@ -428,54 +428,47 @@ private static boolean wasAlreadyInCanonicalForm(String resourceName, String can
428428 return resourceName .equals (canonicalResourceName ) || removeTrailingSlash (resourceName ).equals (canonicalResourceName );
429429 }
430430
431- public static ResourceStorageEntryBase getAtRuntime (String name , boolean throwOnMissing ) {
432- return getAtRuntime (null , name , throwOnMissing );
431+ public static ResourceStorageEntryBase getAtRuntime (String name ) {
432+ return getAtRuntime (null , name , false );
433433 }
434434
435435 /**
436- * If {@code throwOnMissing} is false, we have to distinguish an entry that was in the metadata
437- * from one that was not, so the caller can correctly throw the
438- * {@link MissingResourceRegistrationError}. This is needed because different modules can be
439- * tried on the same resource name, causing an unexpected exception if we throw directly.
436+ * Looks up a resource from {@code module} with name {@code resourceName}.
437+ * <p>
438+ * The {@code probe} parameter indicates whether the caller is probing for the existence of a
439+ * resource. If {@code probe} is true, failed resource lookups return will not throw missing
440+ * registration errors and may instead return {@link #MISSING_METADATA_MARKER}.
441+ * <p>
442+ * Tracing note: When this method is used for probing, only successful metadata matches will be
443+ * traced. If a probing result is {@link #MISSING_METADATA_MARKER}, the caller must explicitly
444+ * trace the missing metadata.
440445 */
441- public static ResourceStorageEntryBase getAtRuntime (Module module , String resourceName , boolean throwOnMissing ) {
446+ public static ResourceStorageEntryBase getAtRuntime (Module module , String resourceName , boolean probe ) {
442447 VMError .guarantee (ImageInfo .inImageRuntimeCode (), "This function should be used only at runtime." );
443448 String canonicalResourceName = NativeImageResourcePathRepresentation .toCanonicalForm (resourceName );
444449 String moduleName = moduleName (module );
445450 ConditionalRuntimeValue <ResourceStorageEntryBase > entry = getEntry (module , canonicalResourceName );
446451 if (entry == null ) {
447452 if (MissingRegistrationUtils .throwMissingRegistrationErrors ()) {
448- for (var r : layeredSingletons ()) {
449- MapCursor <RequestedPattern , RuntimeConditionSet > cursor = r .requestedPatterns .getEntries ();
450- while (cursor .advance ()) {
451- RequestedPattern moduleResourcePair = cursor .getKey ();
452- if (Objects .equals (moduleName , moduleResourcePair .module ) &&
453- ((matchResource (moduleResourcePair .resource , resourceName ) || matchResource (moduleResourcePair .resource , canonicalResourceName )) &&
454- cursor .getValue ().satisfied ())) {
455- return null ;
456- }
457- }
458-
459- String glob = GlobUtils .transformToTriePath (resourceName , moduleName );
460- String canonicalGlob = GlobUtils .transformToTriePath (canonicalResourceName , moduleName );
461- GlobTrieNode <ConditionWithOrigin > globsTrie = r .getResourcesTrieRoot ();
462- if (CompressedGlobTrie .match (globsTrie , glob ) ||
463- CompressedGlobTrie .match (globsTrie , canonicalGlob )) {
464- return null ;
465- }
466- return missingMetadata (module , canonicalGlob , throwOnMissing );
453+ if (missingResourceMatchesIncludePattern (resourceName , moduleName ) || missingResourceMatchesIncludePattern (canonicalResourceName , moduleName )) {
454+ // This resource name matches a pattern/glob from the provided metadata, but no
455+ // resource with the name actually exists. Do not report missing metadata.
456+ traceResource (resourceName , moduleName );
457+ return null ;
467458 }
468-
469- return missingMetadata (module , resourceName , throwOnMissing );
459+ traceResourceMissingMetadata ( resourceName , moduleName , probe );
460+ return missingMetadata (module , resourceName , probe );
470461 } else {
462+ // NB: Without exact reachability metadata, resource include patterns are not
463+ // stored in the image heap, so we cannot reliably identify if the resource was
464+ // included at build time. Assume it is missing.
465+ traceResourceMissingMetadata (resourceName , moduleName , probe );
471466 return null ;
472467 }
473468 }
474- if (MetadataTracer .Options .MetadataTracingSupport .getValue () && MetadataTracer .singleton ().enabled ()) {
475- MetadataTracer .singleton ().traceResource (resourceName , moduleName );
476- }
469+ traceResource (resourceName , moduleName );
477470 if (!entry .getConditions ().satisfied ()) {
478- return missingMetadata (module , resourceName , throwOnMissing );
471+ return missingMetadata (module , resourceName , probe );
479472 }
480473
481474 ResourceStorageEntryBase unconditionalEntry = entry .getValue ();
@@ -503,6 +496,51 @@ public static ResourceStorageEntryBase getAtRuntime(Module module, String resour
503496 return unconditionalEntry ;
504497 }
505498
499+ @ AlwaysInline ("tracing should fold away when disabled" )
500+ private static void traceResource (String resourceName , String moduleName ) {
501+ if (MetadataTracer .enabled ()) {
502+ MetadataTracer .singleton ().traceResource (resourceName , moduleName );
503+ }
504+ }
505+
506+ @ AlwaysInline ("tracing should fold away when disabled" )
507+ private static void traceResourceMissingMetadata (String resourceName , String moduleName ) {
508+ traceResourceMissingMetadata (resourceName , moduleName , false );
509+ }
510+
511+ @ AlwaysInline ("tracing should fold away when disabled" )
512+ private static void traceResourceMissingMetadata (String resourceName , String moduleName , boolean probe ) {
513+ if (MetadataTracer .enabled () && !probe ) {
514+ // Do not trace missing metadata for probing queries, otherwise we'll trace an entry for
515+ // every module. The caller is responsible for tracing missing entries if it uses
516+ // probing.
517+ MetadataTracer .singleton ().traceResource (resourceName , moduleName );
518+ }
519+ }
520+
521+ /**
522+ * Checks whether the given missing resource is matched by a pattern/glob registered at build
523+ * time. In such a case, we should not report missing metadata.
524+ */
525+ private static boolean missingResourceMatchesIncludePattern (String resourceName , String moduleName ) {
526+ VMError .guarantee (MissingRegistrationUtils .throwMissingRegistrationErrors (), "include patterns are only stored in the image with exact reachability metadata" );
527+ String glob = GlobUtils .transformToTriePath (resourceName , moduleName );
528+ for (var r : layeredSingletons ()) {
529+ MapCursor <RequestedPattern , RuntimeConditionSet > cursor = r .requestedPatterns .getEntries ();
530+ while (cursor .advance ()) {
531+ RequestedPattern moduleResourcePair = cursor .getKey ();
532+ if (Objects .equals (moduleName , moduleResourcePair .module ) && matchResource (moduleResourcePair .resource , resourceName ) && cursor .getValue ().satisfied ()) {
533+ return true ;
534+ }
535+ }
536+
537+ if (CompressedGlobTrie .match (r .getResourcesTrieRoot (), glob )) {
538+ return true ;
539+ }
540+ }
541+ return false ;
542+ }
543+
506544 private static ConditionalRuntimeValue <ResourceStorageEntryBase > getEntry (Module module , String canonicalResourceName ) {
507545 for (var r : layeredSingletons ()) {
508546 ConditionalRuntimeValue <ResourceStorageEntryBase > entry = r .resources .get (createStorageKey (module , canonicalResourceName ));
@@ -513,8 +551,8 @@ private static ConditionalRuntimeValue<ResourceStorageEntryBase> getEntry(Module
513551 return null ;
514552 }
515553
516- private static ResourceStorageEntryBase missingMetadata (Module module , String resourceName , boolean throwOnMissing ) {
517- if (throwOnMissing ) {
554+ private static ResourceStorageEntryBase missingMetadata (Module module , String resourceName , boolean probe ) {
555+ if (! probe ) {
518556 MissingResourceRegistrationUtils .reportResourceAccess (module , resourceName );
519557 }
520558 return MISSING_METADATA_MARKER ;
@@ -544,42 +582,45 @@ public static URL createURL(Module module, String resourceName) {
544582 return urls .hasMoreElements () ? urls .nextElement () : null ;
545583 }
546584
547- public static InputStream createInputStream (String resourceName ) {
548- return createInputStream (null , resourceName );
549- }
550-
551585 /* Avoid pulling in the URL class when only an InputStream is needed. */
552586 public static InputStream createInputStream (Module module , String resourceName ) {
553587 if (resourceName == null ) {
554588 return null ;
555589 }
590+ ResourceStorageEntryBase entry = findResourceForInputStream (module , resourceName );
591+ if (entry == MISSING_METADATA_MARKER ) {
592+ traceResourceMissingMetadata (resourceName , moduleName (module ));
593+ MissingResourceRegistrationUtils .reportResourceAccess (module , resourceName );
594+ return null ;
595+ } else if (entry == null ) {
596+ return null ;
597+ }
598+ List <byte []> data = entry .getData ();
599+ return data .isEmpty () ? null : new ByteArrayInputStream (data .get (0 ));
600+ }
556601
557- ResourceStorageEntryBase entry = getAtRuntime ( module , resourceName , false );
558- boolean isInMetadata = entry != MISSING_METADATA_MARKER ;
559- if (moduleName (module ) == null && (entry == MISSING_METADATA_MARKER || entry == null )) {
602+ private static ResourceStorageEntryBase findResourceForInputStream ( Module module , String resourceName ) {
603+ ResourceStorageEntryBase result = getAtRuntime ( module , resourceName , true ) ;
604+ if (moduleName (module ) == null && (result == MISSING_METADATA_MARKER || result == null )) {
560605 /*
561606 * If module is not specified or is an unnamed module and entry was not found as
562607 * classpath-resource we have to search for the resource in all modules in the image.
563608 */
564609 for (Module m : RuntimeModuleSupport .singleton ().getBootLayer ().modules ()) {
565- entry = getAtRuntime (m , resourceName , false );
610+ ResourceStorageEntryBase entry = getAtRuntime (m , resourceName , true );
566611 if (entry != MISSING_METADATA_MARKER ) {
567- isInMetadata = true ;
568- }
569- if (entry != null && entry != MISSING_METADATA_MARKER ) {
570- break ;
612+ if (entry != null ) {
613+ // resource found
614+ return entry ;
615+ } else {
616+ // found a negative query. remember this result but keep trying in case some
617+ // other module supplies an actual resource.
618+ result = null ;
619+ }
571620 }
572621 }
573622 }
574-
575- if (!isInMetadata ) {
576- MissingResourceRegistrationUtils .reportResourceAccess (module , resourceName );
577- }
578- if (entry == null || entry == MISSING_METADATA_MARKER ) {
579- return null ;
580- }
581- List <byte []> data = entry .getData ();
582- return data .isEmpty () ? null : new ByteArrayInputStream (data .get (0 ));
623+ return result ;
583624 }
584625
585626 public static Enumeration <URL > createURLs (String resourceName ) {
@@ -595,23 +636,24 @@ public static Enumeration<URL> createURLs(Module module, String resourceName) {
595636
596637 List <URL > resourcesURLs = new ArrayList <>();
597638 String canonicalResourceName = NativeImageResourcePathRepresentation .toCanonicalForm (resourceName );
598- boolean shouldAppendTrailingSlash = hasTrailingSlash (resourceName );
639+ if (hasTrailingSlash (resourceName )) {
640+ canonicalResourceName += "/" ;
641+ }
599642
600643 /* If moduleName was unspecified we have to consider all modules in the image */
601644 if (moduleName (module ) == null ) {
602645 for (Module m : RuntimeModuleSupport .singleton ().getBootLayer ().modules ()) {
603- ResourceStorageEntryBase entry = getAtRuntime (m , resourceName , false );
604- if (entry == MISSING_METADATA_MARKER ) {
605- continue ;
646+ ResourceStorageEntryBase entry = getAtRuntime (m , resourceName , true );
647+ if (entry != MISSING_METADATA_MARKER ) {
648+ missingMetadata = false ;
649+ addURLEntries (resourcesURLs , (ResourceStorageEntry ) entry , m , canonicalResourceName );
606650 }
607- missingMetadata = false ;
608- addURLEntries (resourcesURLs , (ResourceStorageEntry ) entry , m , shouldAppendTrailingSlash ? canonicalResourceName + '/' : canonicalResourceName );
609651 }
610652 }
611- ResourceStorageEntryBase explicitEntry = getAtRuntime (module , resourceName , false );
653+ ResourceStorageEntryBase explicitEntry = getAtRuntime (module , resourceName , true );
612654 if (explicitEntry != MISSING_METADATA_MARKER ) {
613655 missingMetadata = false ;
614- addURLEntries (resourcesURLs , (ResourceStorageEntry ) explicitEntry , module , shouldAppendTrailingSlash ? canonicalResourceName + '/' : canonicalResourceName );
656+ addURLEntries (resourcesURLs , (ResourceStorageEntry ) explicitEntry , module , canonicalResourceName );
615657 }
616658
617659 if (missingMetadata ) {
0 commit comments