From b9af87124c29d4b32a44603005f3dd13a89ce653 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Tue, 20 Aug 2024 14:16:08 -0700 Subject: [PATCH] Preserve origin why resources are included in an image --- .../embedded-resources-schema-v1.1.0.json | 60 +++++++ .../hosted/RuntimeResourceAccess.java | 4 +- .../impl/RuntimeResourceSupport.java | 16 +- substratevm/CHANGELOG.md | 1 + .../config/ResourceConfigurationTest.java | 8 +- .../config/ResourceConfiguration.java | 8 +- .../oracle/svm/core/ClassLoaderSupport.java | 9 +- .../LegacyResourceConfigurationParser.java | 8 +- .../ResourceConfigurationParser.java | 10 +- .../configure/ResourceMetadataParser.java | 2 +- .../svm/core/configure/ResourcesRegistry.java | 6 +- .../com/oracle/svm/core/jdk/Resources.java | 14 +- .../jdk/localization/LocalizationSupport.java | 7 +- .../CompressedGlobTrie.java | 162 +++++++++--------- .../CompressedGlobTrie/DoubleStarNode.java | 2 +- .../CompressedGlobTrie/GlobTrieNode.java | 64 ++++--- .../CompressedGlobTrie/LiteralNode.java | 2 +- .../CompressedGlobTrie/StarTrieNode.java | 2 +- .../svm/hosted/ClassLoaderSupportImpl.java | 35 ++-- .../svm/hosted/EmbeddedResourceExporter.java | 13 +- .../svm/hosted/EmbeddedResourcesInfo.java | 24 ++- .../oracle/svm/hosted/ResourcesFeature.java | 88 +++++----- .../svm/truffle/TruffleBaseFeature.java | 2 +- 23 files changed, 322 insertions(+), 225 deletions(-) create mode 100644 docs/reference-manual/native-image/assets/embedded-resources-schema-v1.1.0.json diff --git a/docs/reference-manual/native-image/assets/embedded-resources-schema-v1.1.0.json b/docs/reference-manual/native-image/assets/embedded-resources-schema-v1.1.0.json new file mode 100644 index 000000000000..cd91d23aa2ac --- /dev/null +++ b/docs/reference-manual/native-image/assets/embedded-resources-schema-v1.1.0.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/embedded-resources-schema-v1.0.0.json", + "default": [], + "items": { + "properties": { + "name": { + "type": "string", + "title": "Name of the resource that was registered" + }, + "module": { + "type": "string", + "title": "Module of the resource that was registered" + }, + "is_directory": { + "type": "boolean", + "default": false, + "title": "Describes whether the registered resource is a directory or not" + }, + "is_missing": { + "type": "boolean", + "default": false, + "title": "Describes whether the resource is missing on the system or not" + }, + "entries": { + "default": [], + "items": { + "properties": { + "origin": { + "type": "string", + "title": "Resource path" + }, + "registration_origin": { + "type": "string", + "title": "Configuration file or other registration origin that is responsible for including this resource in the native image" + }, + "size": { + "type": "integer", + "title": "Size of the resource expressed in bytes" + } + }, + "additionalProperties": false, + "type": "object", + "title": "Source of the resource defined with name and module properties" + }, + "type": "array", + "title": "List of sources for the resource defined with name and module properties" + } + }, + "required": [ + "name", + "entries" + ], + "additionalProperties": false, + "type": "object", + "title": "Resource that was registered" + }, + "type": "array", + "title": "JSON schema for the embedded-resources.json that shows all resources that were registered." +} diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java index bcfe6e015e61..6d7b6cd60398 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java @@ -68,7 +68,7 @@ public final class RuntimeResourceAccess { public static void addResource(Module module, String resourcePath) { Objects.requireNonNull(module); Objects.requireNonNull(resourcePath); - ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourcePath); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourcePath, "Manually added via RuntimeResourceAccess"); } /** @@ -83,7 +83,7 @@ public static void addResource(Module module, String resourcePath, byte[] resour Objects.requireNonNull(module); Objects.requireNonNull(resourcePath); Objects.requireNonNull(resourceContent); - ImageSingletons.lookup(RuntimeResourceSupport.class).injectResource(module, resourcePath, resourceContent); + ImageSingletons.lookup(RuntimeResourceSupport.class).injectResource(module, resourcePath, resourceContent, "Manually added via RuntimeResourceAccess"); ImageSingletons.lookup(RuntimeResourceSupport.class).addCondition(ConfigurationCondition.alwaysTrue(), module, resourcePath); } diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java index f852d23ba999..f0c2273cbf95 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java @@ -52,9 +52,9 @@ static RuntimeResourceSupport singleton() { return ImageSingletons.lookup(RuntimeResourceSupport.class); } - void addResources(C condition, String pattern); + void addResources(C condition, String pattern, Object origin); - void addGlob(C condition, String module, String glob); + void addGlob(C condition, String module, String glob, Object origin); void ignoreResources(C condition, String pattern); @@ -65,16 +65,16 @@ static RuntimeResourceSupport singleton() { /* Following functions are used only from features */ void addCondition(ConfigurationCondition configurationCondition, Module module, String resourcePath); - void addResourceEntry(Module module, String resourcePath); + void addResourceEntry(Module module, String resourcePath, Object origin); - default void addResource(Module module, String resourcePath) { - addResource(ConfigurationCondition.alwaysTrue(), module, resourcePath); + default void addResource(Module module, String resourcePath, Object origin) { + addResource(ConfigurationCondition.alwaysTrue(), module, resourcePath, origin); } - default void addResource(ConfigurationCondition condition, Module module, String resourcePath) { - addResourceEntry(module, resourcePath); + default void addResource(ConfigurationCondition condition, Module module, String resourcePath, Object origin) { + addResourceEntry(module, resourcePath, origin); addCondition(condition, module, resourcePath); } - void injectResource(Module module, String resourcePath, byte[] resourceContent); + void injectResource(Module module, String resourcePath, byte[] resourceContent, Object origin); } diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 5d2a60fad48c..efb21d961880 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -7,6 +7,7 @@ This changelog summarizes major changes to GraalVM Native Image. The warning is planned to be replaced by an error in GraalVM for JDK 25. * (GR-48384) Added a GDB Python script (`gdb-debughelpers.py`) to improve the Native Image debugging experience. * (GR-49517) Add support for emitting Windows x64 unwind info. This enables stack walking in native tooling such as debuggers and profilers. +* (GR-57384) Preserve the origin of a resource included in a native image. The information is included in the report produced by -H:+GenerateEmbeddedResourcesFile. ## GraalVM for JDK 23 (Internal Version 24.1.0) * (GR-51520) The old class initialization strategy, which was deprecated in GraalVM for JDK 22, is removed. The option `StrictImageHeap` no longer has any effect. diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java index 4b7804503cff..3be095f32d42 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java @@ -93,22 +93,22 @@ public void printJson() { ResourcesRegistry registry = new ResourcesRegistry<>() { @Override - public void addResources(UnresolvedConfigurationCondition condition, String pattern) { + public void addResources(UnresolvedConfigurationCondition condition, String pattern, Object origin) { addedResources.add(pattern); } @Override - public void addGlob(UnresolvedConfigurationCondition condition, String module, String glob) { + public void addGlob(UnresolvedConfigurationCondition condition, String module, String glob, Object origin) { throw VMError.shouldNotReachHere("Unused function."); } @Override - public void addResourceEntry(Module module, String resourcePath) { + public void addResourceEntry(Module module, String resourcePath, Object origin) { throw VMError.shouldNotReachHere("Unused function."); } @Override - public void injectResource(Module module, String resourcePath, byte[] resourceContent) { + public void injectResource(Module module, String resourcePath, byte[] resourceContent, Object origin) { } @Override diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index ff85e702cf74..eb409453bf04 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -66,17 +66,17 @@ public static class ParserAdapter implements ResourcesRegistry(condition, pattern)); } @Override - public void addGlob(UnresolvedConfigurationCondition condition, String module, String glob) { + public void addGlob(UnresolvedConfigurationCondition condition, String module, String glob, Object origin) { configuration.addedGlobs.add(new ConditionalElement<>(condition, new ResourceEntry(glob, module))); } @Override - public void addResourceEntry(Module module, String resourcePath) { + public void addResourceEntry(Module module, String resourcePath, Object origin) { throw VMError.shouldNotReachHere("Unused function."); } @@ -86,7 +86,7 @@ public void addCondition(ConfigurationCondition condition, Module module, String } @Override - public void injectResource(Module module, String resourcePath, byte[] resourceContent) { + public void injectResource(Module module, String resourcePath, byte[] resourceContent, Object origin) { VMError.shouldNotReachHere("Resource injection is only supported via Feature implementation"); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderSupport.java index b86d7b2b99f3..3f3a411b3a13 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderSupport.java @@ -52,12 +52,15 @@ public boolean isNativeImageClassLoader(ClassLoader classLoader) { protected abstract boolean isNativeImageClassLoaderImpl(ClassLoader classLoader); + public record ConditionWithOrigin(ConfigurationCondition condition, Object origin) { + } + public interface ResourceCollector { - List isIncluded(Module module, String resourceName, URI resourceURI); + List isIncluded(Module module, String resourceName, URI resourceURI); - void addResourceEntry(Module module, String resourceName); + void addResourceEntry(Module module, String resourceName, Object origin); - void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition); + void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition, Object origin); void registerNegativeQuery(Module module, String resourceName); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacyResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacyResourceConfigurationParser.java index 0f680cb44899..ef4979778352 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacyResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacyResourceConfigurationParser.java @@ -36,10 +36,10 @@ final class LegacyResourceConfigurationParser extends ResourceConfigurationPa @Override public void parseAndRegister(Object json, URI origin) { - parseTopLevelObject(asMap(json, "first level of document must be an object")); + parseTopLevelObject(asMap(json, "first level of document must be an object"), origin); } - private void parseTopLevelObject(EconomicMap obj) { + private void parseTopLevelObject(EconomicMap obj, Object origin) { Object resourcesObject = null; Object bundlesObject = null; Object globsObject = null; @@ -55,13 +55,13 @@ private void parseTopLevelObject(EconomicMap obj) { } if (resourcesObject != null) { - parseResourcesObject(resourcesObject); + parseResourcesObject(resourcesObject, origin); } if (bundlesObject != null) { parseBundlesObject(bundlesObject); } if (globsObject != null) { - parseGlobsObject(globsObject); + parseGlobsObject(globsObject, origin); } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index f1927c8c0c9e..3389995ed577 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -65,7 +65,7 @@ protected void parseBundlesObject(Object bundlesObject) { } @SuppressWarnings("unchecked") - protected void parseResourcesObject(Object resourcesObject) { + protected void parseResourcesObject(Object resourcesObject, Object origin) { if (resourcesObject instanceof EconomicMap) { // New format EconomicMap resourcesObjectMap = (EconomicMap) resourcesObject; checkAttributes(resourcesObjectMap, "resource descriptor object", Collections.singleton("includes"), Collections.singleton("excludes")); @@ -74,7 +74,7 @@ protected void parseResourcesObject(Object resourcesObject) { List includes = asList(includesObject, "Attribute 'includes' must be a list of resources"); for (Object object : includes) { - parsePatternEntry(object, registry::addResources, "'includes' list"); + parsePatternEntry(object, (condition, pattern) -> registry.addResources(condition, pattern, origin), "'includes' list"); } if (excludesObject != null) { @@ -86,7 +86,7 @@ protected void parseResourcesObject(Object resourcesObject) { } else { // Old format: may be deprecated in future versions List resources = asList(resourcesObject, "Attribute 'resources' must be a list of resources"); for (Object object : resources) { - parsePatternEntry(object, registry::addResources, "'resources' list"); + parsePatternEntry(object, (condition, pattern) -> registry.addResources(condition, pattern, origin), "'resources' list"); } } } @@ -146,10 +146,10 @@ private void parsePatternEntry(Object data, BiConsumer resourceRegist resourceRegistry.accept(resolvedConfigurationCondition.get(), value); } - protected void parseGlobsObject(Object globsObject) { + protected void parseGlobsObject(Object globsObject, Object origin) { List globs = asList(globsObject, "Attribute 'globs' must be a list of glob patterns"); for (Object object : globs) { - parseGlobEntry(object, registry::addGlob); + parseGlobEntry(object, (condition, module, glob) -> registry.addGlob(condition, module, glob, origin)); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceMetadataParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceMetadataParser.java index 2e1b4720ed4a..279ecb9f8039 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceMetadataParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceMetadataParser.java @@ -35,7 +35,7 @@ final class ResourceMetadataParser extends ResourceConfigurationParser { public void parseAndRegister(Object json, URI origin) { Object resourcesJson = getFromGlobalFile(json, RESOURCES_KEY); if (resourcesJson != null) { - parseGlobsObject(resourcesJson); + parseGlobsObject(resourcesJson, origin); } Object bundlesJson = getFromGlobalFile(json, BUNDLES_KEY); if (bundlesJson != null) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java index 89ce92951e1b..18cec696bc89 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java @@ -45,8 +45,10 @@ static ResourcesRegistry singleton() { * {@link RuntimeResourceSupport} they are also needed here for legacy code that accesses them * reflectively. */ - @Override - void addResources(C condition, String pattern); + @Deprecated + default void addResources(C condition, String pattern) { + addResources(condition, pattern, "unknown"); + } @Override void ignoreResources(C condition, String pattern); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java index 3f8ff5b094e9..f20a1dc22b77 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Resources.java @@ -50,6 +50,7 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.BuildPhaseProvider; +import com.oracle.svm.core.ClassLoaderSupport.ConditionWithOrigin; import com.oracle.svm.core.MissingRegistrationUtils; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.configure.ConditionalRuntimeValue; @@ -124,9 +125,20 @@ public record ModuleResourceKey(Module module, String resource) { */ private long lastModifiedTime = INVALID_TIMESTAMP; + private GlobTrieNode resourcesTrieRoot; + Resources() { } + public GlobTrieNode getResourcesTrieRoot() { + return resourcesTrieRoot; + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setResourcesTrieRoot(GlobTrieNode resourcesTrieRoot) { + this.resourcesTrieRoot = resourcesTrieRoot; + } + public EconomicMap> getResourceStorage() { return resources; } @@ -337,7 +349,7 @@ public ResourceStorageEntryBase getAtRuntime(Module module, String resourceName, String glob = GlobUtils.transformToTriePath(resourceName, moduleName); String canonicalGlob = GlobUtils.transformToTriePath(canonicalResourceName, moduleName); - GlobTrieNode globsTrie = ImageSingletons.lookup(GlobTrieNode.class); + GlobTrieNode globsTrie = getResourcesTrieRoot(); if (CompressedGlobTrie.match(globsTrie, glob) || CompressedGlobTrie.match(globsTrie, canonicalGlob)) { return null; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index e2f964b2c563..12f37426c207 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -144,6 +144,7 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function modules = packageToModules.getOrDefault(packageName(bundleName), Collections.emptySet()); for (Module m : modules) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName, origin); } if (modules.isEmpty()) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(null, resourceName); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(null, resourceName, origin); } } else { if (findModule != null) { resourceName = toSlashSeparated(control.toBundleName(bundleNameWithModule[1], locale)).concat(".properties"); Optional module = findModule.apply(bundleNameWithModule[0]); String finalResourceName = resourceName; - module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, finalResourceName)); + module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, finalResourceName, origin)); } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/CompressedGlobTrie.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/CompressedGlobTrie.java index a2ec81d5eb19..6dc93f7c95b5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/CompressedGlobTrie.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/CompressedGlobTrie.java @@ -57,27 +57,27 @@ * structure will first check if the given glob can be matched with some glob that already exists in * the structure (NOTE: possibly more than one glob pattern from the structure can match the new * pattern). When that is not the case, a new pattern will be added using - * {@link CompressedGlobTrieBuilder#addNewBranch(GlobTrieNode, List, int, String)}. This function - * will attempt identical matching (where wildcards have no special semantics) to move as deep in - * the structure as possible. Once it cannot proceed with the existing branches, the function will - * append rest of the pattern as the new branch. At the end, each node (not necessarily leaf) that - * represents end of the glob, can store additional content. + * {@link CompressedGlobTrieBuilder#addNewBranch}. This function will attempt identical matching + * (where wildcards have no special semantics) to move as deep in the structure as possible. Once it + * cannot proceed with the existing branches, the function will append rest of the pattern as the + * new branch. At the end, each node (not necessarily leaf) that represents end of the glob, can + * store additional content. * * When created, the structure can be used to either check if the given text can be matched with * globs from the structure (using {@link #match(GlobTrieNode, String)}) or to fetch all additional * content from globs that can match given text (using - * {@link #getAdditionalContentIfMatched(GlobTrieNode, String)}). + * {@link #getHostedOnlyContentIfMatched(GlobTrieNode, String)}). */ public class CompressedGlobTrie { - public record GlobWithInfo(String pattern, String additionalContent) { + public record GlobWithInfo(String pattern, C additionalContent) { } /* * Data transfer object that points to the next matched child instance and its index in the * pattern parts list */ - private record MatchedNode(GlobTrieNode child, int lastMatchedChildIndex) { + private record MatchedNode(GlobTrieNode child, int lastMatchedChildIndex) { } /* @@ -93,17 +93,17 @@ private record SquashedParts(String squashedPart, int numberOfSquashedParts) { } @Platforms(Platform.HOSTED_ONLY.class) - public static class CompressedGlobTrieBuilder { + public static class CompressedGlobTrieBuilder { /** * Builds an immutable CompressedGlobTrie structure from glob patterns given in any order * with possibly additional context. */ - public static GlobTrieNode build(List patterns) { - GlobTrieNode root = new GlobTrieNode(); + public static GlobTrieNode build(List> patterns) { + GlobTrieNode root = new GlobTrieNode<>(); /* classify patterns in groups */ - List doubleStarPatterns = new ArrayList<>(); - List starPatterns = new ArrayList<>(); - List noStarPatterns = new ArrayList<>(); + List> doubleStarPatterns = new ArrayList<>(); + List> starPatterns = new ArrayList<>(); + List> noStarPatterns = new ArrayList<>(); List invalidPatterns = classifyPatterns(patterns, doubleStarPatterns, starPatterns, noStarPatterns); if (!invalidPatterns.isEmpty()) { @@ -140,24 +140,24 @@ private static String unescapePossibleWildcards(String pattern) { return unescapedPattern; } - private static void addPattern(GlobTrieNode root, GlobWithInfo pattern) { + private static void addPattern(GlobTrieNode root, GlobWithInfo pattern) { String unescapedPattern = unescapePossibleWildcards(pattern.pattern()); - List parts = getPatternParts(unescapedPattern); + List> parts = getPatternParts(unescapedPattern); /* * if this pattern can be matched with some other existing pattern, we should just * update content of leafs */ - List reachableNodes = new ArrayList<>(); + List> reachableNodes = new ArrayList<>(); getAllPatterns(root, parts, 0, reachableNodes); if (!reachableNodes.isEmpty()) { /* new pattern is a part of some existing patterns in the existing Trie */ - for (GlobTrieNode node : reachableNodes) { + for (GlobTrieNode node : reachableNodes) { /* * Both pattern and additionalContent are already present in the trie, so we can * skip this pattern */ - if (node.getAdditionalContent().stream().anyMatch(c -> c.equals(pattern.additionalContent()))) { + if (node.getHostedOnlyContent().stream().anyMatch(c -> c.equals(pattern.additionalContent()))) { return; } } @@ -166,14 +166,14 @@ private static void addPattern(GlobTrieNode root, GlobWithInfo pattern) { addPattern(root, parts, 0, pattern.additionalContent()); } - private static void addPattern(GlobTrieNode root, List parts, int i, String additionalInfo) { + private static void addPattern(GlobTrieNode root, List> parts, int i, C additionalInfo) { if (patternReachedEnd(i, parts)) { root.setLeaf(); - root.addAdditionalContent(additionalInfo); + root.addHostedOnlyContent(additionalInfo); return; } - GlobTrieNode nextPart = parts.get(i); + GlobTrieNode nextPart = parts.get(i); boolean canProceed = simplePatternMatch(root, nextPart); if (canProceed) { /* we had progress, try to match rest of the pattern */ @@ -188,16 +188,16 @@ private static void addPattern(GlobTrieNode root, List parts, int addNewBranch(root, parts, i, additionalInfo); } - private static void addNewBranch(GlobTrieNode root, List parts, int i, String additionalInfo) { + private static void addNewBranch(GlobTrieNode root, List> parts, int i, C additionalInfo) { /* sanity check */ if (parts.isEmpty() || i >= parts.size()) { return; } - GlobTrieNode newNode = null; + GlobTrieNode newNode = null; /* we matched pattern parts until i-th pattern part, so just add rest */ for (int j = i; j < parts.size(); j++) { - GlobTrieNode part = parts.get(j); + GlobTrieNode part = parts.get(j); if (newNode == null) { newNode = root.addChild(part.getContent(), part); continue; @@ -208,15 +208,15 @@ private static void addNewBranch(GlobTrieNode root, List parts, in /* mark end of pattern and populate additional info */ newNode.setLeaf(); - newNode.addAdditionalContent(additionalInfo); + newNode.addHostedOnlyContent(additionalInfo); } - private static List classifyPatterns(List patterns, - List doubleStar, - List singleStar, - List noStar) { + private static List classifyPatterns(List> patterns, + List> doubleStar, + List> singleStar, + List> noStar) { List invalidPatterns = new ArrayList<>(); - for (GlobWithInfo patternWithInfo : patterns) { + for (GlobWithInfo patternWithInfo : patterns) { /* validate patterns */ String error = validatePattern(patternWithInfo.pattern()); if (!error.isEmpty()) { @@ -237,7 +237,7 @@ private static List classifyPatterns(List patterns, return invalidPatterns; } - private static int comparePatterns(GlobWithInfo n1, GlobWithInfo n2) { + private static int comparePatterns(GlobWithInfo n1, GlobWithInfo n2) { String s1 = n1.pattern(); String s2 = n2.pattern(); @@ -343,7 +343,7 @@ private static int comparePatterns(GlobWithInfo n1, GlobWithInfo n2) { /** * Trims the Trie and makes it unmodifiable. */ - public static void finalize(GlobTrieNode root) { + public static void finalize(GlobTrieNode root) { root.trim(); } @@ -351,26 +351,26 @@ public static void finalize(GlobTrieNode root) { * Returns list of information from all glob patterns that could match given text. */ @Platforms(Platform.HOSTED_ONLY.class) - public static List getAdditionalContentIfMatched(GlobTrieNode root, String text) { - List matchedNodes = new ArrayList<>(); + public static List getHostedOnlyContentIfMatched(GlobTrieNode root, String text) { + List> matchedNodes = new ArrayList<>(); getAllPatterns(root, getPatternParts(text), 0, matchedNodes); if (matchedNodes.isEmpty()) { /* text cannot be matched */ return null; } - List additionalContexts = new ArrayList<>(); - matchedNodes.forEach(node -> additionalContexts.addAll(node.getAdditionalContent())); + List additionalContexts = new ArrayList<>(); + matchedNodes.forEach(node -> additionalContexts.addAll(node.getHostedOnlyContent())); return additionalContexts; } /** * Returns whether given text can be matched with any glob pattern in the Trie or not. */ - public static boolean match(GlobTrieNode root, String text) { + public static boolean match(GlobTrieNode root, String text) { /* in this case text is a plain text without special meanings, so stars must be escaped */ String escapedText = escapeAllStars(text); - List tmp = new ArrayList<>(); + List> tmp = new ArrayList<>(); getAllPatterns(root, getPatternParts(escapedText), 0, tmp); return !tmp.isEmpty(); } @@ -382,7 +382,7 @@ private static String escapeAllStars(String text) { private static final Pattern threeConsecutiveStarsRegex = Pattern.compile(".*[*]{3,}.*"); private static final Pattern emptyLevelsRegex = Pattern.compile(".*/{2,}.*"); - public static String validatePattern(String pattern) { + public static String validatePattern(String pattern) { StringBuilder sb = new StringBuilder(); if (pattern.isEmpty()) { @@ -417,8 +417,8 @@ public static String validatePattern(String pattern) { } // check if pattern contains ** without previous Literal parent. Example: */**/... or **/... - List patternParts = getPatternParts(pattern); - for (GlobTrieNode part : patternParts) { + List> patternParts = getPatternParts(pattern); + for (GlobTrieNode part : patternParts) { if (part instanceof LiteralNode) { break; } @@ -441,14 +441,14 @@ public static String validatePattern(String pattern) { * Returns list of glob pattern parts that will represent nodes in final Trie. This function is * used as a helper function in tests as well, and therefore must remain public. */ - public static List getPatternParts(String glob) { + public static List> getPatternParts(String glob) { String pattern = !glob.endsWith("/") ? glob : glob.substring(0, glob.length() - 1); - List parts = new ArrayList<>(); + List> parts = new ArrayList<>(); /* we are splitting patterns on levels */ List levels = Arrays.stream(pattern.split(LEVEL_IDENTIFIER)).toList(); for (String level : levels) { if (level.equals(STAR_STAR)) { - DoubleStarNode tmp = new DoubleStarNode(); + DoubleStarNode tmp = new DoubleStarNode<>(); tmp.setNewLevel(); parts.add(tmp); continue; @@ -456,7 +456,7 @@ public static List getPatternParts(String glob) { if (level.equals(STAR)) { /* special case when * is alone on one level */ - StarTrieNode tmp = new StarTrieNode(true); + StarTrieNode tmp = new StarTrieNode<>(true); tmp.setNewLevel(); parts.add(tmp); continue; @@ -470,13 +470,13 @@ public static List getPatternParts(String glob) { * something/a*b*c*d/else */ - List thisLevelParts = new ArrayList<>(); + List> thisLevelParts = new ArrayList<>(); StringBuilder currentPart = new StringBuilder(); StarCollectorMode currentMode = StarCollectorMode.NORMAL; for (char c : level.toCharArray()) { currentPart.append(c); if (c == STAR.charAt(0) && currentMode == StarCollectorMode.NORMAL) { - thisLevelParts.add(new StarTrieNode(currentPart.toString())); + thisLevelParts.add(new StarTrieNode<>(currentPart.toString())); currentPart.setLength(0); } @@ -485,14 +485,14 @@ public static List getPatternParts(String glob) { if (!currentPart.isEmpty()) { /* this level ends with some literal node */ - thisLevelParts.add(new LiteralNode(currentPart.toString())); + thisLevelParts.add(new LiteralNode<>(currentPart.toString())); } thisLevelParts.get(0).setNewLevel(); parts.addAll(thisLevelParts); continue; } - LiteralNode tmp = new LiteralNode(level); + LiteralNode tmp = new LiteralNode<>(level); tmp.setNewLevel(); parts.add(tmp); } @@ -505,7 +505,7 @@ private enum StarCollectorMode { ESCAPE } - private static void getAllPatterns(GlobTrieNode node, List parts, int i, List matches) { + private static void getAllPatterns(GlobTrieNode node, List> parts, int i, List> matches) { if (patternReachedEnd(i, parts)) { if (node.isLeaf()) { matches.add(node); @@ -515,9 +515,9 @@ private static void getAllPatterns(GlobTrieNode node, List parts, } /* get ** successors that could extend check */ - DoubleStarNode doubleStar = node.getDoubleStarNode(); + DoubleStarNode doubleStar = node.getDoubleStarNode(); if (doubleStar != null) { - for (MatchedNode child : getAllAvailablePaths(doubleStar, parts, i)) { + for (MatchedNode child : getAllAvailablePaths(doubleStar, parts, i)) { getAllPatterns(child.child(), parts, child.lastMatchedChildIndex() + 1, matches); } } @@ -525,12 +525,12 @@ private static void getAllPatterns(GlobTrieNode node, List parts, /* get * nodes that could match next level */ SquashedParts sp = getThisLevel(parts, i); for (var child : node.getChildrenWithStar()) { - for (GlobTrieNode c : matchOneLevel(child, sp.squashedPart())) { + for (GlobTrieNode c : matchOneLevel(child, sp.squashedPart())) { getAllPatterns(c, parts, i + sp.numberOfSquashedParts() + 1, matches); } } - GlobTrieNode part = parts.get(i); + GlobTrieNode part = parts.get(i); if (part instanceof StarTrieNode || part instanceof DoubleStarNode) { /* * next part is not simple text, and we didn't match it before, so we can't proceed @@ -542,7 +542,7 @@ private static void getAllPatterns(GlobTrieNode node, List parts, } /* we don't have wildcards anymore, so try simple match */ - GlobTrieNode child = node.getChild(part.getContent()); + GlobTrieNode child = node.getChild(part.getContent()); if (child == null) { return; } @@ -551,11 +551,11 @@ private static void getAllPatterns(GlobTrieNode node, List parts, getAllPatterns(child, parts, i + 1, matches); } - private static List getAllAvailablePaths(DoubleStarNode node, List parts, int i) { - List successors = new ArrayList<>(); + private static List> getAllAvailablePaths(DoubleStarNode node, List> parts, int i) { + List> successors = new ArrayList<>(); /* maybe ** is leaf, so we should cover this pattern */ if (node.isLeaf()) { - return List.of(new MatchedNode(node, parts.size())); + return List.of(new MatchedNode<>(node, parts.size())); } /* checks if we skip some part (on index j) can we match with some child of node */ @@ -563,16 +563,16 @@ private static List getAllAvailablePaths(DoubleStarNode node, List< /* in case next part contains many stars, squash them into one node */ SquashedParts sp = getThisLevel(parts, j); /* checks if any child of current node can match next part */ - for (StarTrieNode child : node.getChildrenWithStar()) { + for (StarTrieNode child : node.getChildrenWithStar()) { int finalJ = j; /* we can match next level with more than one pattern */ successors.addAll(matchOneLevel(child, sp.squashedPart()) .stream() - .map(c -> new MatchedNode(c, finalJ + sp.numberOfSquashedParts())) + .map(c -> new MatchedNode<>(c, finalJ + sp.numberOfSquashedParts())) .toList()); } - GlobTrieNode part = parts.get(j); + GlobTrieNode part = parts.get(j); if (part instanceof StarTrieNode) { /* * we are only checking trivial parts because we processed star nodes, and we don't @@ -581,13 +581,13 @@ private static List getAllAvailablePaths(DoubleStarNode node, List< continue; } - GlobTrieNode child = node.getChild(part.getContent()); + GlobTrieNode child = node.getChild(part.getContent()); /* * we are checking only parts that begins new level because in pattern a*b*c, last c is * LiteralNode, but it is a part of complex StarTrieNode */ if (part.isNewLevel() && child != null) { - successors.add(new MatchedNode(child, j)); + successors.add(new MatchedNode<>(child, j)); } } @@ -608,7 +608,7 @@ private static int getIndexOfFirstUnescapedStar(String level) { return -1; } - private static List matchOneLevel(StarTrieNode node, String wholeLevel) { + private static List> matchOneLevel(StarTrieNode node, String wholeLevel) { if (node.isMatchingWholeLevel()) { return List.of(node); } @@ -632,8 +632,8 @@ private static List matchOneLevel(StarTrieNode node, String wholeL return List.of(node); } - List potentialChildren = new ArrayList<>(); - for (LiteralNode child : node.getChildrenWithLiteral()) { + List> potentialChildren = new ArrayList<>(); + for (LiteralNode child : node.getChildrenWithLiteral()) { if (child.isNewLevel()) { // we won't try matching with this child since it is not on same level // example: a*/c => c is a child of * but it is on the next level, so it can't be @@ -654,7 +654,7 @@ private static List matchOneLevel(StarTrieNode node, String wholeL /* * we need to go in deeper recursion with children that also contains star (like: a* -> b*) */ - for (StarTrieNode child : node.getChildrenWithStar()) { + for (StarTrieNode child : node.getChildrenWithStar()) { if (child.isNewLevel()) { // we won't try matching with this child since it is not on same level // example: a*/c* => c* is a child of a* but it is on the next level, so it can't be @@ -663,7 +663,7 @@ private static List matchOneLevel(StarTrieNode node, String wholeL } /* if there is a repetition of certain * node we must check paths through all of them */ - StarTrieNode tmpChild = child; + StarTrieNode tmpChild = child; while (true) { String childContent = tmpChild.getContent(); String childPrefix = childContent.substring(0, childContent.indexOf(STAR.charAt(0))); @@ -673,21 +673,21 @@ private static List matchOneLevel(StarTrieNode node, String wholeL } /* check if current node has child with same content on same level */ - GlobTrieNode potentialChild = tmpChild.getChildFromSameLevel(tmpChild.getContent()); + GlobTrieNode potentialChild = tmpChild.getChildFromSameLevel(tmpChild.getContent()); if (potentialChild == null) { /* we don't have more repeated star nodes so just quit the loop */ break; } /* move to next node that is repeated */ - tmpChild = (StarTrieNode) potentialChild; + tmpChild = (StarTrieNode) potentialChild; } } return potentialChildren; } - private static boolean simplePatternMatch(GlobTrieNode root, GlobTrieNode part) { + private static boolean simplePatternMatch(GlobTrieNode root, GlobTrieNode part) { if (root instanceof StarTrieNode || root instanceof DoubleStarNode || part instanceof StarTrieNode || part instanceof DoubleStarNode) { return false; } @@ -695,12 +695,12 @@ private static boolean simplePatternMatch(GlobTrieNode root, GlobTrieNode part) return root.getChild(part.getContent()) != null; } - private static SquashedParts getThisLevel(List parts, int begin) { + private static SquashedParts getThisLevel(List> parts, int begin) { StringBuilder sb = new StringBuilder(parts.get(begin).getContent()); int numberOfSquashedParts = 0; /* collect all parts until we hit beginning of the new level */ for (int i = begin + 1; i < parts.size(); i++) { - GlobTrieNode nextPart = parts.get(i); + GlobTrieNode nextPart = parts.get(i); if (nextPart.isNewLevel()) { break; } @@ -712,20 +712,20 @@ private static SquashedParts getThisLevel(List parts, int begin) { return new SquashedParts(sb.toString(), numberOfSquashedParts); } - private static boolean patternReachedEnd(int index, List parts) { + private static boolean patternReachedEnd(int index, List> parts) { return index >= parts.size(); } - public static void removeNodes(GlobTrieNode head, Predicate shouldRemove) { - List contentToRemove = head.getAdditionalContent().stream().filter(shouldRemove).toList(); - head.removeAdditionalContent(contentToRemove); + public static void removeNodes(GlobTrieNode head, Predicate shouldRemove) { + List contentToRemove = head.getHostedOnlyContent().stream().filter(shouldRemove).toList(); + head.removeHostedOnlyContent(contentToRemove); - List childrenToRemove = new ArrayList<>(); - for (GlobTrieNode child : head.getChildren()) { + List> childrenToRemove = new ArrayList<>(); + for (GlobTrieNode child : head.getChildren()) { removeNodes(child, shouldRemove); /* leaf without additional content should be removed */ - if (child.isLeaf() && child.getAdditionalContent().isEmpty()) { + if (child.isLeaf() && child.getHostedOnlyContent().isEmpty()) { if (child.getChildren().isEmpty()) { /* if it is the last node remove it physically */ childrenToRemove.add(child); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/DoubleStarNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/DoubleStarNode.java index 866ad465b0c4..d96f9819b3ad 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/DoubleStarNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/DoubleStarNode.java @@ -25,7 +25,7 @@ package com.oracle.svm.core.jdk.resources.CompressedGlobTrie; -final class DoubleStarNode extends GlobTrieNode { +final class DoubleStarNode extends GlobTrieNode { DoubleStarNode() { super(STAR_STAR); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/GlobTrieNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/GlobTrieNode.java index 36e8ebc1eac8..dce604f0ed3c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/GlobTrieNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/GlobTrieNode.java @@ -31,9 +31,13 @@ import java.util.Map; import java.util.Set; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.heap.UnknownObjectField; -public class GlobTrieNode { +public class GlobTrieNode { protected static final String STAR = "*"; protected static final String STAR_STAR = "**"; protected static final String LEVEL_IDENTIFIER = "/"; @@ -41,18 +45,27 @@ public class GlobTrieNode { private String content; @UnknownObjectField(fullyQualifiedTypes = {"java.util.HashMap", "java.util.ImmutableCollections$MapN", "java.util.ImmutableCollections$Map1"}) // - private Map children; + private Map> children; private boolean isLeaf; private boolean isNewLevel; - @UnknownObjectField(fullyQualifiedTypes = {"java.util.HashSet", "java.util.ImmutableCollections$SetN", "java.util.ImmutableCollections$Set12"}) // - private Set additionalContent; + + /* + * While the Trie data structure is general-purpose, this field is used to store information + * that we know must not leak into an image for the only current use case of the Trie: this + * field stores the source and origin of resources, which are often absolute paths of the image + * build machine. + */ + @Platforms(Platform.HOSTED_ONLY.class) // + private Set hostedOnlyContent; protected GlobTrieNode() { content = ""; children = new HashMap<>(); isLeaf = false; isNewLevel = false; - additionalContent = new HashSet<>(); + if (SubstrateUtil.HOSTED) { + hostedOnlyContent = new HashSet<>(); + } } protected GlobTrieNode(String content) { @@ -84,27 +97,30 @@ public String getContent() { return content; } - protected Set getAdditionalContent() { - return additionalContent; + @Platforms(Platform.HOSTED_ONLY.class) // + protected Set getHostedOnlyContent() { + return hostedOnlyContent; } - protected void removeAdditionalContent(List ac) { - additionalContent.removeAll(ac); + @Platforms(Platform.HOSTED_ONLY.class) // + protected void removeHostedOnlyContent(List ac) { + hostedOnlyContent.removeAll(ac); } - protected void addAdditionalContent(String ac) { - this.additionalContent.add(ac); + @Platforms(Platform.HOSTED_ONLY.class) // + protected void addHostedOnlyContent(C ac) { + this.hostedOnlyContent.add(ac); } - public List getChildren() { + public List> getChildren() { return children.values().stream().toList(); } - protected GlobTrieNode getChild(String child) { + protected GlobTrieNode getChild(String child) { return children.get(child); } - protected void removeChildren(List childKeys) { + protected void removeChildren(List> childKeys) { for (var child : childKeys) { /* * we need exact name of the child key in order to delete it. In case when we have a @@ -117,11 +133,11 @@ protected void removeChildren(List childKeys) { } } - protected GlobTrieNode getChildFromSameLevel(String child) { + protected GlobTrieNode getChildFromSameLevel(String child) { return children.get(child + SAME_LEVEL_IDENTIFIER); } - protected GlobTrieNode addChild(String child, GlobTrieNode childValue) { + protected GlobTrieNode addChild(String child, GlobTrieNode childValue) { StringBuilder sb = new StringBuilder(child); // to make difference between a*b* (represented as: a* -> b*#) @@ -135,23 +151,23 @@ protected GlobTrieNode addChild(String child, GlobTrieNode childValue) { return children.get(sb.toString()); } - protected List getChildrenWithStar() { + protected List> getChildrenWithStar() { return this.getChildren().stream() .filter(node -> node instanceof StarTrieNode) - .map(node -> (StarTrieNode) node) + .map(node -> (StarTrieNode) node) .toList(); } - protected List getChildrenWithLiteral() { + protected List> getChildrenWithLiteral() { return this.getChildren() .stream() .filter(node -> node instanceof LiteralNode) - .map(node -> (LiteralNode) node) + .map(node -> (LiteralNode) node) .toList(); } - protected DoubleStarNode getDoubleStarNode() { - return (DoubleStarNode) getChild(STAR_STAR); + protected DoubleStarNode getDoubleStarNode() { + return (DoubleStarNode) getChild(STAR_STAR); } /** @@ -160,11 +176,11 @@ protected DoubleStarNode getDoubleStarNode() { * the structure. */ protected void trim() { - for (GlobTrieNode child : children.values()) { + for (GlobTrieNode child : children.values()) { child.trim(); } - additionalContent = Set.copyOf(additionalContent); + hostedOnlyContent = Set.copyOf(hostedOnlyContent); children = Map.copyOf(children); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/LiteralNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/LiteralNode.java index e91a3f79f795..4f48cabc1252 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/LiteralNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/LiteralNode.java @@ -25,7 +25,7 @@ package com.oracle.svm.core.jdk.resources.CompressedGlobTrie; -final class LiteralNode extends GlobTrieNode { +final class LiteralNode extends GlobTrieNode { LiteralNode(String content) { super(content); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/StarTrieNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/StarTrieNode.java index abf22b1ae5ad..6e163f425677 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/StarTrieNode.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/StarTrieNode.java @@ -25,7 +25,7 @@ package com.oracle.svm.core.jdk.resources.CompressedGlobTrie; -final class StarTrieNode extends GlobTrieNode { +final class StarTrieNode extends GlobTrieNode { private final boolean matchingWholeLevel; StarTrieNode(String content) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java index c77697474944..2c87c1d0be94 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java @@ -72,7 +72,7 @@ public class ClassLoaderSupportImpl extends ClassLoaderSupport { private final Map> packageToModules; - private record ConditionalResource(ConfigurationCondition condition, String resourceName) { + private record ConditionalResource(ConfigurationCondition condition, String resourceName, Object origin) { } public ClassLoaderSupportImpl(NativeImageClassLoaderSupport classLoaderSupport) { @@ -135,17 +135,16 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso boolean includeCurrent = classLoaderSupport.getJavaModuleNamesToInclude().contains(info.resolvedModule().name()); List resourcesFound = new ArrayList<>(); moduleReader.list().forEach(resourceName -> { - List conditions = shouldIncludeEntry(info.module, resourceCollector, resourceName, moduleReference.location().orElse(null), includeCurrent); - for (ConfigurationCondition condition : conditions) { - resourcesFound.add(new ConditionalResource(condition, resourceName)); + var conditionsWithOrigins = shouldIncludeEntry(info.module, resourceCollector, resourceName, moduleReference.location().orElse(null), includeCurrent); + for (var conditionWithOrigin : conditionsWithOrigins) { + resourcesFound.add(new ConditionalResource(conditionWithOrigin.condition(), resourceName, conditionWithOrigin.origin())); } }); for (ConditionalResource entry : resourcesFound) { - ConfigurationCondition condition = entry.condition(); String resName = entry.resourceName(); if (resName.endsWith("/")) { - includeResource(resourceCollector, info.module, resName, condition); + includeResource(resourceCollector, info.module, resName, entry.condition(), entry.origin()); continue; } @@ -156,7 +155,7 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso continue; } - includeResource(resourceCollector, info.module, resName, condition); + includeResource(resourceCollector, info.module, resName, entry.condition(), entry.origin()); } } catch (IOException e) { @@ -178,13 +177,13 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea relativeFilePath = String.valueOf(RESOURCES_INTERNAL_PATH_SEPARATOR); } - List conditions = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeCurrent); - for (ConfigurationCondition condition : conditions) { - includeResource(collector, null, relativeFilePath, condition); + var conditionsWithOrigins = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeCurrent); + for (var conditionWithOrigin : conditionsWithOrigins) { + includeResource(collector, null, relativeFilePath, conditionWithOrigin.condition(), conditionWithOrigin.origin()); } if (Files.isDirectory(entry)) { - if (conditions.isEmpty()) { + if (conditionsWithOrigins.isEmpty()) { collector.registerNegativeQuery(null, relativeFilePath); } @@ -211,21 +210,21 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i entryName = entryName.substring(0, entry.getName().length() - 1); } - List conditions = shouldIncludeEntry(null, collector, entryName, jarPath.toUri(), includeCurrent); - for (ConfigurationCondition condition : conditions) { - includeResource(collector, null, entryName, condition); + var conditionsWithOrigins = shouldIncludeEntry(null, collector, entryName, jarPath.toUri(), includeCurrent); + for (var conditionWithOrigin : conditionsWithOrigins) { + includeResource(collector, null, entryName, conditionWithOrigin.condition(), conditionWithOrigin.origin()); } } } } - private static void includeResource(ResourceCollector collector, Module module, String name, ConfigurationCondition condition) { - collector.addResourceConditionally(module, name, condition); + private static void includeResource(ResourceCollector collector, Module module, String name, ConfigurationCondition condition, Object origin) { + collector.addResourceConditionally(module, name, condition, origin); } - private static List shouldIncludeEntry(Module module, ResourceCollector collector, String fileName, URI uri, boolean includeCurrent) { + private static List shouldIncludeEntry(Module module, ResourceCollector collector, String fileName, URI uri, boolean includeCurrent) { if (includeCurrent && !(fileName.endsWith(".class") || fileName.endsWith(".jar"))) { - return Collections.singletonList(ConfigurationCondition.alwaysTrue()); + return Collections.singletonList(new ConditionWithOrigin(ConfigurationCondition.alwaysTrue(), "Include all")); } return collector.isIncluded(module, fileName, uri); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourceExporter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourceExporter.java index bab9b935c97b..48cb3886c1e3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourceExporter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourceExporter.java @@ -41,6 +41,7 @@ import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.jdk.resources.ResourceStorageEntryBase; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.EmbeddedResourcesInfo.SourceAndOrigin; import com.oracle.svm.util.LogUtils; import jdk.graal.compiler.util.json.JsonPrinter; @@ -49,7 +50,7 @@ @Platforms(Platform.HOSTED_ONLY.class) public class EmbeddedResourceExporter { - public record SourceSizePair(String source, int size) { + public record SourceSizePair(String source, Object origin, int size) { } public record ResourceReportEntry(Module module, String resourceName, List entries, boolean isDirectory, boolean isMissing) { @@ -92,12 +93,14 @@ private static void sourceElement(SourceSizePair p, JsonWriter w) throws IOExcep w.appendObjectStart().newline(); w.appendKeyValue("origin", p.source()).appendSeparator(); w.newline(); + w.appendKeyValue("registration_origin", p.origin()).appendSeparator(); + w.newline(); w.appendKeyValue("size", p.size()); w.newline().appendObjectEnd(); w.unindent(); } - private static List getResourceReportEntryList(ConcurrentHashMap> collection) { + private static List getResourceReportEntryList(ConcurrentHashMap> collection) { if (collection.isEmpty()) { LogUtils.warning("Attempting to write information about resources without data being collected. " + "Either the GenerateEmbeddedResourcesFile hosted option is disabled " + @@ -113,7 +116,7 @@ private static List getResourceReportEntryList(ConcurrentHa String resourceName = key.resource(); ResourceStorageEntryBase storageEntry = resourceStorage.get(key).getValueUnconditionally(); - List registeredEntrySources = collection.get(key); + List registeredEntrySources = collection.get(key); if (registeredEntrySources == null && storageEntry != NEGATIVE_QUERY_MARKER) { throw VMError.shouldNotReachHere("Resource: " + resourceName + @@ -128,9 +131,9 @@ private static List getResourceReportEntryList(ConcurrentHa List sources = new ArrayList<>(); for (int i = 0; i < registeredEntrySources.size(); i++) { - String source = registeredEntrySources.get(i); + SourceAndOrigin sourceAndOrigin = registeredEntrySources.get(i); int size = storageEntry.getData().get(i).length; - sources.add(new SourceSizePair(source, size)); + sources.add(new SourceSizePair(sourceAndOrigin.source(), sourceAndOrigin.origin(), size)); } boolean isDirectory = storageEntry.isDirectory(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourcesInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourcesInfo.java index 4dd8ddadccdf..bdebe91e0240 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourcesInfo.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/EmbeddedResourcesInfo.java @@ -37,9 +37,12 @@ @Platforms(Platform.HOSTED_ONLY.class) public class EmbeddedResourcesInfo { - private final ConcurrentHashMap> registeredResources = new ConcurrentHashMap<>(); + record SourceAndOrigin(String source, Object origin) { + } + + private final ConcurrentHashMap> registeredResources = new ConcurrentHashMap<>(); - public ConcurrentHashMap> getRegisteredResources() { + public ConcurrentHashMap> getRegisteredResources() { return registeredResources; } @@ -47,7 +50,7 @@ public static EmbeddedResourcesInfo singleton() { return ImageSingletons.lookup(EmbeddedResourcesInfo.class); } - public void declareResourceAsRegistered(Module module, String resource, String source) { + public void declareResourceAsRegistered(Module module, String resource, String source, Object origin) { if (!ImageSingletons.lookup(ResourcesFeature.class).collectEmbeddedResourcesInfo()) { return; } @@ -55,8 +58,8 @@ public void declareResourceAsRegistered(Module module, String resource, String s Resources.ModuleResourceKey key = Resources.createStorageKey(module, resource); registeredResources.compute(key, (k, v) -> { if (v == null) { - ArrayList newValue = new ArrayList<>(); - newValue.add(source); + ArrayList newValue = new ArrayList<>(); + newValue.add(new SourceAndOrigin(source, origin)); return newValue; } @@ -67,8 +70,15 @@ public void declareResourceAsRegistered(Module module, String resource, String s * so we have to perform same check here, to avoid duplicates when collecting * information about resource. */ - if (!v.contains(source)) { - v.add(source); + boolean found = false; + for (var existing : v) { + if (existing.source.equals(source)) { + found = true; + break; + } + } + if (!found) { + v.add(new SourceAndOrigin(source, origin)); } return v; }); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index a00d502d8635..b7971900bc4d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -60,15 +60,14 @@ import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeResourceSupport; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.core.BuildArtifacts; import com.oracle.svm.core.ClassLoaderSupport; +import com.oracle.svm.core.ClassLoaderSupport.ConditionWithOrigin; import com.oracle.svm.core.ClassLoaderSupport.ResourceCollector; import com.oracle.svm.core.MissingRegistrationUtils; import com.oracle.svm.core.ParsingReason; import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.TypeResult; import com.oracle.svm.core.configure.ConfigurationConditionResolver; import com.oracle.svm.core.configure.ConfigurationFile; import com.oracle.svm.core.configure.ConfigurationFiles; @@ -154,16 +153,16 @@ public static class Options { private static final String EMBEDDED_RESOURCES_FILE_NAME = "embedded-resources.json"; @Option(help = "Create a " + EMBEDDED_RESOURCES_FILE_NAME + " file in the build directory. The output conforms to the JSON schema located at: " + - "docs/reference-manual/native-image/assets/embedded-resources-schema-v1.0.0.json", type = OptionType.User)// + "docs/reference-manual/native-image/assets/embedded-resources-schema-v1.1.0.json", type = OptionType.User)// public static final HostedOptionKey GenerateEmbeddedResourcesFile = new HostedOptionKey<>(false); } private boolean sealed = false; - private record ConditionalPattern(ConfigurationCondition condition, String pattern) { + private record ConditionalPattern(ConfigurationCondition condition, String pattern, Object origin) { } - private record CompiledConditionalPattern(ConfigurationCondition condition, ResourcePattern compiledPattern) { + private record CompiledConditionalPattern(ConfigurationCondition condition, ResourcePattern compiledPattern, Object origin) { } private Set resourcePatternWorkSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); @@ -180,19 +179,19 @@ private class ResourcesRegistryImpl extends ConditionalConfigurationRegistry imp } @Override - public void addResources(ConfigurationCondition condition, String pattern) { + public void addResources(ConfigurationCondition condition, String pattern, Object origin) { try { - resourcePatternWorkSet.add(new ConditionalPattern(condition, pattern)); + resourcePatternWorkSet.add(new ConditionalPattern(condition, pattern, origin)); } catch (UnsupportedOperationException e) { throw UserError.abort("Resource registration should be performed before beforeAnalysis phase."); } } @Override - public void addGlob(ConfigurationCondition condition, String module, String glob) { + public void addGlob(ConfigurationCondition condition, String module, String glob, Object origin) { String canonicalGlob = Resources.toCanonicalForm(glob); String resolvedGlob = GlobUtils.transformToTriePath(canonicalGlob, module); - globWorkSet.add(new ConditionalPattern(condition, resolvedGlob)); + globWorkSet.add(new ConditionalPattern(condition, resolvedGlob, origin)); } @Override @@ -205,21 +204,21 @@ public void addCondition(ConfigurationCondition condition, Module module, String /* Adds single resource defined with its module and name */ @Override - public void addResourceEntry(Module module, String resourcePath) { + public void addResourceEntry(Module module, String resourcePath, Object origin) { if (!shouldRegisterResource(module, resourcePath)) { return; } if (module != null && module.isNamed()) { - processResourceFromModule(module, resourcePath); + processResourceFromModule(module, resourcePath, origin); } else { - processResourceFromClasspath(resourcePath); + processResourceFromClasspath(resourcePath, origin); } } @Override - public void injectResource(Module module, String resourcePath, byte[] resourceContent) { - EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(module, resourcePath, "INJECTED"); + public void injectResource(Module module, String resourcePath, byte[] resourceContent, Object origin) { + EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(module, resourcePath, "INJECTED", origin); Resources.singleton().registerResource(module, resourcePath, resourceContent); } @@ -282,7 +281,7 @@ public boolean shouldRegisterResource(Module module, String resourceName) { } } - private void processResourceFromModule(Module module, String resourcePath) { + private void processResourceFromModule(Module module, String resourcePath, Object origin) { try { String resourcePackage = jdk.internal.module.Resources.toPackageName(resourcePath); if (!resourcePackage.isEmpty()) { @@ -305,14 +304,14 @@ private void processResourceFromModule(Module module, String resourcePath) { var resolvedModule = module.getLayer().configuration().findModule(module.getName()); if (resolvedModule.isPresent()) { Optional location = resolvedModule.get().reference().location(); - location.ifPresent(uri -> EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(module, resourcePath, uri.toString())); + location.ifPresent(uri -> EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(module, resourcePath, uri.toString(), origin)); } } catch (IOException e) { Resources.singleton().registerIOException(module, resourcePath, e, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(resourcePath)); } } - private void processResourceFromClasspath(String resourcePath) { + private void processResourceFromClasspath(String resourcePath, Object origin) { Enumeration urls; try { /* @@ -348,7 +347,7 @@ private void processResourceFromClasspath(String resourcePath) { } String source = ResourcesUtils.getResourceSource(url, resourcePath, fromJar); - EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(null, resourcePath, source); + EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(null, resourcePath, source, origin); } catch (IOException e) { Resources.singleton().registerIOException(null, resourcePath, e, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(resourcePath)); return; @@ -410,28 +409,26 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { ConfigurationFile.RESOURCES.getFileName()); /* prepare globs for resource registration */ - List patternsWithInfo = globWorkSet + List> patternsWithInfo = globWorkSet .stream() - .map(entry -> new CompressedGlobTrie.GlobWithInfo(entry.pattern(), entry.condition().getType().getName())).toList(); - GlobTrieNode trie = CompressedGlobTrie.CompressedGlobTrieBuilder.build(patternsWithInfo); - ImageSingletons.add(GlobTrieNode.class, trie); + .map(entry -> new CompressedGlobTrie.GlobWithInfo<>(entry.pattern(), new ConditionWithOrigin(entry.condition(), entry.origin()))).toList(); + GlobTrieNode trie = CompressedGlobTrie.CompressedGlobTrieBuilder.build(patternsWithInfo); + Resources.singleton().setResourcesTrieRoot(trie); /* prepare regex patterns for resource registration */ resourcePatternWorkSet.addAll(Options.IncludeResources.getValue() - .values() - .stream() - .map(e -> new ConditionalPattern(ConfigurationCondition.alwaysTrue(), e)) + .getValuesWithOrigins() + .map(e -> new ConditionalPattern(ConfigurationCondition.alwaysTrue(), e.value(), e.origin())) .toList()); Set includePatterns = resourcePatternWorkSet .stream() - .map(e -> new CompiledConditionalPattern(e.condition(), makeResourcePattern(e.pattern()))) + .map(e -> new CompiledConditionalPattern(e.condition(), makeResourcePattern(e.pattern()), e.origin())) .collect(Collectors.toSet()); excludedResourcePatterns.addAll(Options.ExcludeResources.getValue().values()); ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns); - FeatureImpl.BeforeAnalysisAccessImpl beforeAnalysisAccess = (FeatureImpl.BeforeAnalysisAccessImpl) access; - ResourceCollectorImpl collector = new ResourceCollectorImpl(includePatterns, excludePatterns, beforeAnalysisAccess.getImageClassLoader()); + ResourceCollectorImpl collector = new ResourceCollectorImpl(includePatterns, excludePatterns); /* * register all included patterns in Resources singleton (if we are throwing * MissingRegistrationErrors), so they can be queried at runtime to detect missing entries @@ -473,16 +470,14 @@ private static final class ResourceCollectorImpl extends ConditionalConfiguratio private boolean initialReport; private volatile String currentlyProcessedEntry; ScheduledExecutorService scheduledExecutor; - ConfigurationConditionResolver conditionResolver; - private ResourceCollectorImpl(Set includePatterns, ResourcePattern[] excludePatterns, ImageClassLoader imageClassLoader) { + private ResourceCollectorImpl(Set includePatterns, ResourcePattern[] excludePatterns) { this.includePatterns = includePatterns; this.excludePatterns = excludePatterns; this.reachedResourceEntries = new LongAdder(); this.initialReport = true; this.currentlyProcessedEntry = null; - this.conditionResolver = new NativeImageConditionResolver(imageClassLoader, ClassInitializationSupport.singleton()); } private void prepareProgressReporter() { @@ -506,7 +501,7 @@ private void shutDownProgressReporter() { } @Override - public List isIncluded(Module module, String resourceName, URI resource) { + public List isIncluded(Module module, String resourceName, URI resource) { this.currentlyProcessedEntry = resource.getScheme().equals("jrt") ? (resource + "/" + resourceName) : resource.toString(); this.reachedResourceEntries.increment(); @@ -531,13 +526,13 @@ public List isIncluded(Module module, String resourceNam } /* Possibly we can have multiple conditions for one resource */ - List conditions = new ArrayList<>(); + List conditions = new ArrayList<>(); for (CompiledConditionalPattern rp : includePatterns) { if (!rp.compiledPattern().moduleNameMatches(moduleName)) { continue; } if (rp.compiledPattern().pattern.matcher(resourceName).matches() || rp.compiledPattern().pattern.matcher(relativePathWithTrailingSlash).matches()) { - conditions.add(rp.condition()); + conditions.add(new ConditionWithOrigin(rp.condition(), rp.origin())); } } @@ -547,29 +542,24 @@ public List isIncluded(Module module, String resourceNam return conditions; } - private List getConditionsFromGlobTrie(Module module, String resourceName) { + private static List getConditionsFromGlobTrie(Module module, String resourceName) { String pattern = GlobUtils.transformToTriePath(resourceName, module == null ? "" : module.getName()); - List types = CompressedGlobTrie.getAdditionalContentIfMatched(ImageSingletons.lookup(GlobTrieNode.class), pattern); + List types = CompressedGlobTrie.getHostedOnlyContentIfMatched(Resources.singleton().getResourcesTrieRoot(), pattern); if (types == null) { return Collections.emptyList(); } - - return types.stream() - .map(type -> UnresolvedConfigurationCondition.create(type, false)) - .map(conditionResolver::resolveCondition) - .map(TypeResult::get) - .toList(); + return types; } @Override - public void addResourceEntry(Module module, String resourceName) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceEntry(module, resourceName); + public void addResourceEntry(Module module, String resourceName, Object origin) { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceEntry(module, resourceName, origin); } @Override - public void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition) { + public void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition, Object origin) { registerConditionalConfiguration(condition, cnd -> { - addResourceEntry(module, resourceName); + addResourceEntry(module, resourceName, origin); ImageSingletons.lookup(RuntimeResourceSupport.class).addCondition(cnd, module, resourceName); }); } @@ -581,7 +571,7 @@ public void registerIOException(Module module, String resourceName, IOException @Override public void registerNegativeQuery(Module module, String resourceName) { - EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(module, resourceName, ""); + EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(module, resourceName, "", ""); Resources.singleton().registerNegativeQuery(module, resourceName); } @@ -638,8 +628,8 @@ public void afterAnalysis(AfterAnalysisAccess access) { } /* prepare resources GlobTrie for runtime */ - GlobTrieNode root = ImageSingletons.lookup(GlobTrieNode.class); - CompressedGlobTrie.removeNodes(root, (type) -> !access.isReachable(access.findClassByName(type))); + GlobTrieNode root = Resources.singleton().getResourcesTrieRoot(); + CompressedGlobTrie.removeNodes(root, (conditionWithOrigin) -> !access.isReachable(conditionWithOrigin.condition().getType())); CompressedGlobTrie.finalize(root); } diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java index 2bdf75b81b42..25274cbe50a9 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java @@ -516,7 +516,7 @@ public void duringSetup(DuringSetupAccess a) { } if (needsAllEncodings) { - RuntimeResourceSupport.singleton().addResources(ConfigurationCondition.alwaysTrue(), "org/graalvm/shadowed/org/jcodings/tables/.*bin$"); + RuntimeResourceSupport.singleton().addResources(ConfigurationCondition.alwaysTrue(), "org/graalvm/shadowed/org/jcodings/tables/.*bin$", "Truffle needsAllEncodings flag is set"); } }