54
54
import org .graalvm .nativeimage .Platforms ;
55
55
import org .graalvm .nativeimage .impl .ConfigurationCondition ;
56
56
57
+ import com .oracle .svm .core .AlwaysInline ;
57
58
import com .oracle .svm .core .BuildPhaseProvider ;
58
59
import com .oracle .svm .core .ClassLoaderSupport .ConditionWithOrigin ;
59
60
import com .oracle .svm .core .MissingRegistrationUtils ;
65
66
import com .oracle .svm .core .feature .AutomaticallyRegisteredFeature ;
66
67
import com .oracle .svm .core .feature .InternalFeature ;
67
68
import com .oracle .svm .core .imagelayer .ImageLayerBuildingSupport ;
68
- import com .oracle .svm .core .jdk .resources .MissingResourceRegistrationError ;
69
69
import com .oracle .svm .core .jdk .resources .MissingResourceRegistrationUtils ;
70
70
import com .oracle .svm .core .jdk .resources .ResourceExceptionEntry ;
71
71
import com .oracle .svm .core .jdk .resources .ResourceStorageEntry ;
@@ -428,54 +428,47 @@ private static boolean wasAlreadyInCanonicalForm(String resourceName, String can
428
428
return resourceName .equals (canonicalResourceName ) || removeTrailingSlash (resourceName ).equals (canonicalResourceName );
429
429
}
430
430
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 );
433
433
}
434
434
435
435
/**
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.
440
445
*/
441
- public static ResourceStorageEntryBase getAtRuntime (Module module , String resourceName , boolean throwOnMissing ) {
446
+ public static ResourceStorageEntryBase getAtRuntime (Module module , String resourceName , boolean probe ) {
442
447
VMError .guarantee (ImageInfo .inImageRuntimeCode (), "This function should be used only at runtime." );
443
448
String canonicalResourceName = NativeImageResourcePathRepresentation .toCanonicalForm (resourceName );
444
449
String moduleName = moduleName (module );
445
450
ConditionalRuntimeValue <ResourceStorageEntryBase > entry = getEntry (module , canonicalResourceName );
446
451
if (entry == null ) {
447
452
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 ;
467
458
}
468
-
469
- return missingMetadata (module , resourceName , throwOnMissing );
459
+ traceResourceMissingMetadata ( resourceName , moduleName , probe );
460
+ return missingMetadata (module , resourceName , probe );
470
461
} 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 );
471
466
return null ;
472
467
}
473
468
}
474
- if (MetadataTracer .Options .MetadataTracingSupport .getValue () && MetadataTracer .singleton ().enabled ()) {
475
- MetadataTracer .singleton ().traceResource (resourceName , moduleName );
476
- }
469
+ traceResource (resourceName , moduleName );
477
470
if (!entry .getConditions ().satisfied ()) {
478
- return missingMetadata (module , resourceName , throwOnMissing );
471
+ return missingMetadata (module , resourceName , probe );
479
472
}
480
473
481
474
ResourceStorageEntryBase unconditionalEntry = entry .getValue ();
@@ -503,6 +496,51 @@ public static ResourceStorageEntryBase getAtRuntime(Module module, String resour
503
496
return unconditionalEntry ;
504
497
}
505
498
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
+
506
544
private static ConditionalRuntimeValue <ResourceStorageEntryBase > getEntry (Module module , String canonicalResourceName ) {
507
545
for (var r : layeredSingletons ()) {
508
546
ConditionalRuntimeValue <ResourceStorageEntryBase > entry = r .resources .get (createStorageKey (module , canonicalResourceName ));
@@ -513,8 +551,8 @@ private static ConditionalRuntimeValue<ResourceStorageEntryBase> getEntry(Module
513
551
return null ;
514
552
}
515
553
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 ) {
518
556
MissingResourceRegistrationUtils .reportResourceAccess (module , resourceName );
519
557
}
520
558
return MISSING_METADATA_MARKER ;
@@ -544,42 +582,45 @@ public static URL createURL(Module module, String resourceName) {
544
582
return urls .hasMoreElements () ? urls .nextElement () : null ;
545
583
}
546
584
547
- public static InputStream createInputStream (String resourceName ) {
548
- return createInputStream (null , resourceName );
549
- }
550
-
551
585
/* Avoid pulling in the URL class when only an InputStream is needed. */
552
586
public static InputStream createInputStream (Module module , String resourceName ) {
553
587
if (resourceName == null ) {
554
588
return null ;
555
589
}
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
+ }
556
601
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 )) {
560
605
/*
561
606
* If module is not specified or is an unnamed module and entry was not found as
562
607
* classpath-resource we have to search for the resource in all modules in the image.
563
608
*/
564
609
for (Module m : RuntimeModuleSupport .singleton ().getBootLayer ().modules ()) {
565
- entry = getAtRuntime (m , resourceName , false );
610
+ ResourceStorageEntryBase entry = getAtRuntime (m , resourceName , true );
566
611
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
+ }
571
620
}
572
621
}
573
622
}
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 ;
583
624
}
584
625
585
626
public static Enumeration <URL > createURLs (String resourceName ) {
@@ -595,23 +636,24 @@ public static Enumeration<URL> createURLs(Module module, String resourceName) {
595
636
596
637
List <URL > resourcesURLs = new ArrayList <>();
597
638
String canonicalResourceName = NativeImageResourcePathRepresentation .toCanonicalForm (resourceName );
598
- boolean shouldAppendTrailingSlash = hasTrailingSlash (resourceName );
639
+ if (hasTrailingSlash (resourceName )) {
640
+ canonicalResourceName += "/" ;
641
+ }
599
642
600
643
/* If moduleName was unspecified we have to consider all modules in the image */
601
644
if (moduleName (module ) == null ) {
602
645
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 );
606
650
}
607
- missingMetadata = false ;
608
- addURLEntries (resourcesURLs , (ResourceStorageEntry ) entry , m , shouldAppendTrailingSlash ? canonicalResourceName + '/' : canonicalResourceName );
609
651
}
610
652
}
611
- ResourceStorageEntryBase explicitEntry = getAtRuntime (module , resourceName , false );
653
+ ResourceStorageEntryBase explicitEntry = getAtRuntime (module , resourceName , true );
612
654
if (explicitEntry != MISSING_METADATA_MARKER ) {
613
655
missingMetadata = false ;
614
- addURLEntries (resourcesURLs , (ResourceStorageEntry ) explicitEntry , module , shouldAppendTrailingSlash ? canonicalResourceName + '/' : canonicalResourceName );
656
+ addURLEntries (resourcesURLs , (ResourceStorageEntry ) explicitEntry , module , canonicalResourceName );
615
657
}
616
658
617
659
if (missingMetadata ) {
0 commit comments