Skip to content

[GR-57384] Preserve origin why resources are included in an image. #9546

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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."
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

/**
Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ static RuntimeResourceSupport<ConfigurationCondition> 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);

Expand All @@ -65,16 +65,16 @@ static RuntimeResourceSupport<ConfigurationCondition> 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);
}
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,22 +93,22 @@ public void printJson() {
ResourcesRegistry<UnresolvedConfigurationCondition> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,17 @@ public static class ParserAdapter implements ResourcesRegistry<UnresolvedConfigu
}

@Override
public void addResources(UnresolvedConfigurationCondition condition, String pattern) {
public void addResources(UnresolvedConfigurationCondition condition, String pattern, Object origin) {
configuration.classifyAndAddPattern(new ConditionalElement<>(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.");
}

Expand All @@ -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");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConfigurationCondition> isIncluded(Module module, String resourceName, URI resourceURI);
List<ConditionWithOrigin> 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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ final class LegacyResourceConfigurationParser<C> 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<String, Object> obj) {
private void parseTopLevelObject(EconomicMap<String, Object> obj, Object origin) {
Object resourcesObject = null;
Object bundlesObject = null;
Object globsObject = null;
Expand All @@ -55,13 +55,13 @@ private void parseTopLevelObject(EconomicMap<String, Object> obj) {
}

if (resourcesObject != null) {
parseResourcesObject(resourcesObject);
parseResourcesObject(resourcesObject, origin);
}
if (bundlesObject != null) {
parseBundlesObject(bundlesObject);
}
if (globsObject != null) {
parseGlobsObject(globsObject);
parseGlobsObject(globsObject, origin);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Object> resourcesObjectMap = (EconomicMap<String, Object>) resourcesObject;
checkAttributes(resourcesObjectMap, "resource descriptor object", Collections.singleton("includes"), Collections.singleton("excludes"));
Expand All @@ -74,7 +74,7 @@ protected void parseResourcesObject(Object resourcesObject) {

List<Object> 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) {
Expand All @@ -86,7 +86,7 @@ protected void parseResourcesObject(Object resourcesObject) {
} else { // Old format: may be deprecated in future versions
List<Object> 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");
}
}
}
Expand Down Expand Up @@ -146,10 +146,10 @@ private void parsePatternEntry(Object data, BiConsumer<C, String> resourceRegist
resourceRegistry.accept(resolvedConfigurationCondition.get(), value);
}

protected void parseGlobsObject(Object globsObject) {
protected void parseGlobsObject(Object globsObject, Object origin) {
List<Object> 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));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ final class ResourceMetadataParser<C> extends ResourceConfigurationParser<C> {
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ static ResourcesRegistry<ConfigurationCondition> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -124,9 +125,20 @@ public record ModuleResourceKey(Module module, String resource) {
*/
private long lastModifiedTime = INVALID_TIMESTAMP;

private GlobTrieNode<ConditionWithOrigin> resourcesTrieRoot;

Resources() {
}

public GlobTrieNode<ConditionWithOrigin> getResourcesTrieRoot() {
return resourcesTrieRoot;
}

@Platforms(Platform.HOSTED_ONLY.class)
public void setResourcesTrieRoot(GlobTrieNode<ConditionWithOrigin> resourcesTrieRoot) {
this.resourcesTrieRoot = resourcesTrieRoot;
}

public EconomicMap<ModuleResourceKey, ConditionalRuntimeValue<ResourceStorageEntryBase>> getResourceStorage() {
return resources;
}
Expand Down Expand Up @@ -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<ConditionWithOrigin> globsTrie = getResourcesTrieRoot();
if (CompressedGlobTrie.match(globsTrie, glob) ||
CompressedGlobTrie.match(globsTrie, canonicalGlob)) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,25 +144,26 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function<Str
if (bundle instanceof PropertyResourceBundle) {
String[] bundleNameWithModule = SubstrateUtil.split(bundleName, ":", 2);
String resourceName;
String origin = "Added for PropertyResourceBundle: " + bundleName;
if (bundleNameWithModule.length < 2) {
resourceName = toSlashSeparated(control.toBundleName(bundleName, locale)).concat(".properties");

Map<String, Set<Module>> packageToModules = ImageSingletons.lookup(ClassLoaderSupport.class).getPackageToModules();
Set<Module> 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> 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));
}
}
}
Expand Down
Loading