From a180a4b70b97aa1b2ba1fa1b28b683e55173322a Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Thu, 11 May 2023 13:44:02 +0200 Subject: [PATCH 01/54] Move resource registration to beforeAnalysis phase --- .../oracle/svm/hosted/ResourcesFeature.java | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) 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 99f6b23593fe..61b121e9ffb4 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 @@ -83,7 +83,6 @@ import com.oracle.svm.core.option.LocatableMultiOptionValue; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.hosted.config.ConfigurationParserUtils; import com.oracle.svm.hosted.jdk.localization.LocalizationFeature; import com.oracle.svm.util.LogUtils; @@ -133,7 +132,7 @@ public static class Options { } private boolean sealed = false; - private final Set resourcePatternWorkSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private Set resourcePatternWorkSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final Set excludedResourcePatterns = Collections.newSetFromMap(new ConcurrentHashMap<>()); private int loadedConfigurations; private ImageClassLoader imageClassLoader; @@ -220,6 +219,31 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { resourcePatternWorkSet.addAll(Options.IncludeResources.getValue().values()); excludedResourcePatterns.addAll(Options.ExcludeResources.getValue().values()); + + if (!resourcePatternWorkSet.isEmpty()) { + FeatureImpl.BeforeAnalysisAccessImpl beforeAnalysisAccess = (FeatureImpl.BeforeAnalysisAccessImpl) access; + ResourcePattern[] includePatterns = compilePatterns(resourcePatternWorkSet); + if (MissingRegistrationUtils.throwMissingRegistrationErrors()) { + for (ResourcePattern resourcePattern : includePatterns) { + Resources.singleton().registerIncludePattern(resourcePattern.moduleName, resourcePattern.pattern.pattern()); + } + } + + ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns); + DebugContext debugContext = beforeAnalysisAccess.getDebugContext(); + ResourceCollectorImpl collector = new ResourceCollectorImpl(debugContext, includePatterns, excludePatterns); + try { + collector.prepareProgressReporter(); + ImageSingletons.lookup(ClassLoaderSupport.class).collectResources(collector); + } finally { + collector.shutDownProgressReporter(); + } + + // We set resourcePatternWorkSet to empty unmodifiable set, so we can be sure that it + // won't be populated in some later phase + resourcePatternWorkSet = Set.of(); + } + resourceRegistryImpl().flushConditionalConfiguration(access); } @@ -320,34 +344,6 @@ public void registerNegativeQuery(Module module, String resourceName) { } } - @Override - public void duringAnalysis(DuringAnalysisAccess access) { - resourceRegistryImpl().flushConditionalConfiguration(access); - if (resourcePatternWorkSet.isEmpty()) { - return; - } - - access.requireAnalysisIteration(); - - DuringAnalysisAccessImpl duringAnalysisAccess = ((DuringAnalysisAccessImpl) access); - ResourcePattern[] includePatterns = compilePatterns(resourcePatternWorkSet); - if (MissingRegistrationUtils.throwMissingRegistrationErrors()) { - for (ResourcePattern resourcePattern : includePatterns) { - Resources.singleton().registerIncludePattern(resourcePattern.moduleName, resourcePattern.pattern.pattern()); - } - } - ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns); - DebugContext debugContext = duringAnalysisAccess.getDebugContext(); - ResourceCollectorImpl collector = new ResourceCollectorImpl(debugContext, includePatterns, excludePatterns); - try { - collector.prepareProgressReporter(); - ImageSingletons.lookup(ClassLoaderSupport.class).collectResources(collector); - } finally { - collector.shutDownProgressReporter(); - } - resourcePatternWorkSet.clear(); - } - private ResourcePattern[] compilePatterns(Set patterns) { return patterns.stream() .filter(s -> s.length() > 0) From 252861c3db027c6eb4c4325588a824c9c27e8d01 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Thu, 11 May 2023 16:51:23 +0200 Subject: [PATCH 02/54] Try to speed resource traversing by introducing parallelism --- .../src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java | 1 + 1 file changed, 1 insertion(+) 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 37dfcb4134b8..1452c978ba52 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 @@ -106,6 +106,7 @@ private static Stream extractModuleLookupData(ModuleLayer la public void collectResources(ResourceCollector resourceCollector) { /* Collect resources from modules */ NativeImageClassLoaderSupport.allLayers(classLoaderSupport.moduleLayerForImageBuild).stream() + .parallel() .flatMap(ClassLoaderSupportImpl::extractModuleLookupData) .forEach(lookup -> collectResourceFromModule(resourceCollector, lookup)); From ee5f501558c3ea1ca0615d418f5491e02573de58 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 16 May 2023 12:43:27 +0200 Subject: [PATCH 03/54] Change function that public API uses to register resource --- .../hosted/RuntimeResourceAccess.java | 3 +-- .../impl/RuntimeResourceSupport.java | 2 ++ .../oracle/svm/hosted/ResourcesFeature.java | 24 +++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) 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 355a4f06e4f7..d6dd9c83a71d 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 @@ -69,8 +69,7 @@ public final class RuntimeResourceAccess { public static void addResource(Module module, String resourcePath) { Objects.requireNonNull(module); Objects.requireNonNull(resourcePath); - ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(ConfigurationCondition.alwaysTrue(), - withModuleName(module, Pattern.quote(resourcePath))); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(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 68242b311d69..3c18afe72df5 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 @@ -46,6 +46,8 @@ public interface RuntimeResourceSupport { void addResources(ConfigurationCondition condition, String pattern); + void addResources(Module module, String resourcePath); + void injectResource(Module module, String resourcePath, byte[] resourceContent); void ignoreResources(ConfigurationCondition condition, String pattern); 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 61b121e9ffb4..91f7b1905f80 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 @@ -27,12 +27,14 @@ import static com.oracle.svm.core.jdk.Resources.RESOURCES_INTERNAL_PATH_SEPARATOR; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URI; +import java.net.URL; import java.nio.file.FileSystem; import java.util.ArrayList; import java.util.Arrays; @@ -40,6 +42,7 @@ import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; @@ -155,6 +158,27 @@ public void addResources(ConfigurationCondition condition, String pattern) { }); } + @Override + public void addResources(Module module, String resourcePath) { + InputStream is; + boolean fromJar; + + if (module.isNamed()) { + try { + fromJar = new File(Objects.requireNonNull(module.getClassLoader().getResource(resourcePath)).getPath()).isDirectory(); + is = module.getResourceAsStream(resourcePath); + } catch (IOException e) { + // we ignore if user provided resource that doesn't exist + return; + } + } else { + fromJar = new File(Objects.requireNonNull(imageClassLoader.getClassLoader().getResource(resourcePath)).getPath()).isDirectory(); + is = imageClassLoader.getClassLoader().getResourceAsStream(resourcePath); + } + + Resources.registerResource(module, resourcePath, is, fromJar); + } + @Override public void injectResource(Module module, String resourcePath, byte[] resourceContent) { Resources.singleton().registerResource(module, resourcePath, resourceContent); From f3e59808baf5a81c365db52283868ecb77e6c634 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 19 May 2023 12:54:08 +0200 Subject: [PATCH 04/54] Add resource registration through API without patterns --- .../config/ResourceConfigurationTest.java | 4 +++ .../config/ResourceConfiguration.java | 25 +++++++++++-------- .../oracle/svm/hosted/ResourcesFeature.java | 4 +-- 3 files changed, 20 insertions(+), 13 deletions(-) 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 600b21440bb4..51f6e6cebf9b 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,6 +93,10 @@ public void addResources(ConfigurationCondition condition, String pattern) { addedResources.add(pattern); } + @Override + public void addResources(Module module, String resourcePath) { + } + @Override public void injectResource(Module module, String resourcePath, byte[] resourceContent) { } 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 803fbb86fa34..7824d1f51d2c 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 @@ -24,6 +24,16 @@ */ package com.oracle.svm.configure.config; +import com.oracle.svm.configure.ConfigurationBase; +import com.oracle.svm.core.configure.ConditionalElement; +import com.oracle.svm.core.configure.ConfigurationParser; +import com.oracle.svm.core.configure.ResourceConfigurationParser; +import com.oracle.svm.core.configure.ResourcesRegistry; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.core.util.json.JsonPrinter; +import com.oracle.svm.core.util.json.JsonWriter; +import org.graalvm.nativeimage.impl.ConfigurationCondition; + import java.io.IOException; import java.util.Collection; import java.util.Comparator; @@ -35,17 +45,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; -import org.graalvm.nativeimage.impl.ConfigurationCondition; - -import com.oracle.svm.configure.ConfigurationBase; -import com.oracle.svm.core.util.json.JsonPrinter; -import com.oracle.svm.core.util.json.JsonWriter; -import com.oracle.svm.core.configure.ConditionalElement; -import com.oracle.svm.core.configure.ConfigurationParser; -import com.oracle.svm.core.configure.ResourceConfigurationParser; -import com.oracle.svm.core.configure.ResourcesRegistry; -import com.oracle.svm.core.util.VMError; - public final class ResourceConfiguration extends ConfigurationBase { private static final String PROPERTY_BUNDLE = "java.util.PropertyResourceBundle"; @@ -63,6 +62,10 @@ public void addResources(ConfigurationCondition condition, String pattern) { configuration.addResourcePattern(condition, pattern); } + @Override + public void addResources(Module module, String resourcePath) { + } + @Override public void injectResource(Module module, String resourcePath, byte[] resourceContent) { VMError.shouldNotReachHere("Resource injection is only supported via Feature implementation"); 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 91f7b1905f80..b4459a800068 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 @@ -165,14 +165,14 @@ public void addResources(Module module, String resourcePath) { if (module.isNamed()) { try { - fromJar = new File(Objects.requireNonNull(module.getClassLoader().getResource(resourcePath)).getPath()).isDirectory(); + fromJar = Objects.requireNonNull(module.getClassLoader().getResource(resourcePath)).getPath().contains(".jar"); is = module.getResourceAsStream(resourcePath); } catch (IOException e) { // we ignore if user provided resource that doesn't exist return; } } else { - fromJar = new File(Objects.requireNonNull(imageClassLoader.getClassLoader().getResource(resourcePath)).getPath()).isDirectory(); + fromJar = Objects.requireNonNull(imageClassLoader.getClassLoader().getResource(resourcePath)).getPath().contains(".jar"); is = imageClassLoader.getClassLoader().getResourceAsStream(resourcePath); } From 7865a6cb671f6584d566b0c4cb7385505c29af06 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 19 May 2023 15:51:11 +0200 Subject: [PATCH 05/54] Try to open modules and decide whether resource comes from jar --- .../hosted/RuntimeResourceAccess.java | 9 +++--- .../oracle/svm/hosted/ResourcesFeature.java | 30 +++++++++++++++---- .../com/oracle/svm/util/ModuleSupport.java | 2 +- 3 files changed, 30 insertions(+), 11 deletions(-) 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 d6dd9c83a71d..885a7ab8564f 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 @@ -40,17 +40,16 @@ */ package org.graalvm.nativeimage.hosted; -import java.util.Arrays; -import java.util.Locale; -import java.util.Objects; -import java.util.regex.Pattern; - import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeResourceSupport; +import java.util.Arrays; +import java.util.Locale; +import java.util.Objects; + /** * This class can be used to register Java resources and ResourceBundles that should be accessible * at run time. 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 b4459a800068..4f24e2c0044c 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 @@ -27,7 +27,6 @@ import static com.oracle.svm.core.jdk.Resources.RESOURCES_INTERNAL_PATH_SEPARATOR; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; @@ -42,7 +41,6 @@ import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; @@ -89,6 +87,7 @@ import com.oracle.svm.hosted.config.ConfigurationParserUtils; import com.oracle.svm.hosted.jdk.localization.LocalizationFeature; import com.oracle.svm.util.LogUtils; +import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -165,15 +164,36 @@ public void addResources(Module module, String resourcePath) { if (module.isNamed()) { try { - fromJar = Objects.requireNonNull(module.getClassLoader().getResource(resourcePath)).getPath().contains(".jar"); + String resourcePackage = jdk.internal.module.Resources.toPackageName(resourcePath); + if (!resourcePackage.isEmpty()) { + if (!module.getPackages().contains(resourcePackage)) { + return; + } + ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, ResourcesFeature.class, module, resourcePackage); + } is = module.getResourceAsStream(resourcePath); + if (is == null) { + return; + } + + fromJar = false; } catch (IOException e) { // we ignore if user provided resource that doesn't exist return; } } else { - fromJar = Objects.requireNonNull(imageClassLoader.getClassLoader().getResource(resourcePath)).getPath().contains(".jar"); - is = imageClassLoader.getClassLoader().getResourceAsStream(resourcePath); + URL url = imageClassLoader.getClassLoader().getResource(resourcePath); + if (url != null) { + fromJar = url.getProtocol().equalsIgnoreCase("jar"); + try { + is = url.openStream(); + } catch (IOException e) { + // we ignore if user provided resource that doesn't exist + return; + } + } else { + return; + } } Resources.registerResource(module, resourcePath, is, fromJar); diff --git a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ModuleSupport.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ModuleSupport.java index 03ec51bdfa18..4b5738a6116d 100644 --- a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ModuleSupport.java +++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ModuleSupport.java @@ -131,7 +131,7 @@ public static void accessPackagesToClass(Access access, Class accessingClass, } @Platforms(Platform.HOSTED_ONLY.class) - private static void accessModuleByClass(Access access, Class accessingClass, Module declaringModule, String packageName) { + public static void accessModuleByClass(Access access, Class accessingClass, Module declaringModule, String packageName) { Module namedAccessingModule = null; if (accessingClass != null) { Module accessingModule = accessingClass.getModule(); From 705835d113619d35b7d75a2d76bc3522987f0a7b Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Thu, 25 May 2023 13:26:08 +0200 Subject: [PATCH 06/54] Replace CharsetSubstitution resource adding --- .../hosted/jdk/localization/CharsetSubstitutionsFeature.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java index 0d54073ac9ba..236d9d7c5d36 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java @@ -25,6 +25,7 @@ package com.oracle.svm.hosted.jdk.localization; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.configure.ResourcesRegistry; @@ -35,6 +36,6 @@ class CharsetSubstitutionsFeature implements InternalFeature { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - ImageSingletons.lookup(ResourcesRegistry.class).addResources(ConfigurationCondition.create("java.lang.CharacterName"), "java/lang/uniName.dat"); + access.registerReachabilityHandler((e) -> RuntimeResourceAccess.addResource(ModuleLayer.boot().findModule("java.base").get(), "java/lang/uniName.dat"), access.findClassByName("java.lang.CharacterName")); } } From eaafc3d8ba61e7a49282854f980c3d5014185b60 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Mon, 29 May 2023 12:07:10 +0200 Subject: [PATCH 07/54] Change addResource function to check whether the entry is directory or not --- .../hosted/RuntimeResourceAccess.java | 2 +- .../impl/RuntimeResourceSupport.java | 2 +- .../config/ResourceConfigurationTest.java | 4 +- .../config/ResourceConfiguration.java | 3 +- .../com/oracle/svm/core/jdk/Resources.java | 4 ++ .../oracle/svm/hosted/ResourcesFeature.java | 64 +++++++++++++------ .../CharsetSubstitutionsFeature.java | 8 +-- 7 files changed, 59 insertions(+), 28 deletions(-) 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 885a7ab8564f..76c43f86efd2 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).addResources(module, resourcePath); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(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 3c18afe72df5..2d370a511ffd 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 @@ -46,7 +46,7 @@ public interface RuntimeResourceSupport { void addResources(ConfigurationCondition condition, String pattern); - void addResources(Module module, String resourcePath); + void addResource(Module module, String resourcePath); void injectResource(Module module, String resourcePath, byte[] resourceContent); 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 51f6e6cebf9b..ef28c19bcd99 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 @@ -33,6 +33,7 @@ import java.util.List; import java.util.Locale; +import com.oracle.svm.core.util.VMError; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.junit.Assert; import org.junit.Test; @@ -94,7 +95,8 @@ public void addResources(ConfigurationCondition condition, String pattern) { } @Override - public void addResources(Module module, String resourcePath) { + public void addResource(Module module, String resourcePath) { + VMError.shouldNotReachHere("Unused function."); } @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 7824d1f51d2c..c7dcb46b78e1 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 @@ -63,7 +63,8 @@ public void addResources(ConfigurationCondition condition, String pattern) { } @Override - public void addResources(Module module, String resourcePath) { + public void addResource(Module module, String resourcePath) { + VMError.shouldNotReachHere("Unused function."); } @Override 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 00faeab2e2cb..1240eebf95d0 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 @@ -214,6 +214,10 @@ public void registerResource(Module module, String resourceName, InputStream is, addEntry(module, resourceName, false, inputStreamToByteArray(is), fromJar); } + @Platforms(Platform.HOSTED_ONLY.class) + public static void registerResource(Module module, String resourceName, boolean isDir, InputStream is, boolean fromJar) { + addEntry(module, resourceName, isDir, inputStreamToByteArray(is), fromJar); + } @Platforms(Platform.HOSTED_ONLY.class) public void registerDirectoryResource(String resourceDirName, String content) { registerDirectoryResource(null, resourceDirName, content, true); 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 4f24e2c0044c..871ef546062b 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 @@ -33,8 +33,11 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -47,6 +50,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; +import java.util.jar.JarFile; import java.util.regex.Pattern; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; @@ -157,10 +161,25 @@ public void addResources(ConfigurationCondition condition, String pattern) { }); } + private String urlToJarPath(URL url) { + return String.valueOf(url).split("jar:file:")[1].split("!")[0]; + } + + private boolean resourceIsDirectory(URL url, boolean fromJar, String resourcePath) throws IOException, URISyntaxException { + if (fromJar) { + try (JarFile jf = new JarFile(urlToJarPath(url))) { + return jf.getEntry(resourcePath).isDirectory(); + } + } else { + return Files.isDirectory(Path.of(url.toURI())); + } + } + @Override - public void addResources(Module module, String resourcePath) { + public void addResource(Module module, String resourcePath) { InputStream is; - boolean fromJar; + boolean fromJar = false; + boolean isDirectory = false; if (module.isNamed()) { try { @@ -171,32 +190,41 @@ public void addResources(Module module, String resourcePath) { } ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, ResourcesFeature.class, module, resourcePackage); } - is = module.getResourceAsStream(resourcePath); - if (is == null) { - return; - } - fromJar = false; + is = module.getResourceAsStream(resourcePath); } catch (IOException e) { - // we ignore if user provided resource that doesn't exist + // we should ignore if user failed to provide resource return; } } else { URL url = imageClassLoader.getClassLoader().getResource(resourcePath); - if (url != null) { - fromJar = url.getProtocol().equalsIgnoreCase("jar"); - try { - is = url.openStream(); - } catch (IOException e) { - // we ignore if user provided resource that doesn't exist - return; - } - } else { + if (url == null) { + // we should ignore if user failed to provide resource return; } + + fromJar = url.getProtocol().equalsIgnoreCase("jar"); + try { + isDirectory = resourceIsDirectory(url, fromJar, resourcePath); + } catch (IOException e) { + // we should ignore if user failed to provide resource + return; + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + + try { + is = url.openStream(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + if (is == null) { + return; } - Resources.registerResource(module, resourcePath, is, fromJar); + Resources.registerResource(module, resourcePath, isDirectory, is, fromJar); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java index 236d9d7c5d36..c863b5c6261b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java @@ -24,13 +24,9 @@ */ package com.oracle.svm.hosted.jdk.localization; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; -import org.graalvm.nativeimage.impl.ConfigurationCondition; - -import com.oracle.svm.core.configure.ResourcesRegistry; -import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; @AutomaticallyRegisteredFeature class CharsetSubstitutionsFeature implements InternalFeature { From 82fdd40da4af4fb3a1d90ff98a0743a913934342 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Mon, 29 May 2023 16:31:18 +0200 Subject: [PATCH 08/54] Extract resource adding from beforeAnalysis to duringSetup in TruffleBaseFeature --- .../src/com/oracle/svm/truffle/TruffleBaseFeature.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 75f89555ad7b..750d6e4d5cac 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 @@ -312,7 +312,6 @@ public void afterRegistration(AfterRegistrationAccess a) { initializeHomeFinder(); needsAllEncodings = invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "getNeedsAllEncodings", Collections.emptyList()); - // reinitialize language cache invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory", "reinitializeNativeImageState", Collections.emptyList()); @@ -449,6 +448,10 @@ public void duringSetup(DuringSetupAccess access) { if (Options.TruffleCheckPreinitializedFiles.getValue()) { access.registerObjectReplacer(new TruffleFileCheck(((FeatureImpl.DuringSetupAccessImpl) access).getHostVM().getClassInitializationSupport())); } + + if (needsAllEncodings) { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(ConfigurationCondition.alwaysTrue(), "org/graalvm/shadowed/org/jcodings/tables/.*bin$"); + } } void setGraalGraphObjectReplacer(GraalGraphObjectReplacer graalGraphObjectReplacer) { @@ -482,9 +485,6 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { config.registerSubtypeReachabilityHandler(TruffleBaseFeature::registerTruffleLibrariesAsInHeap, LibraryExport.class); - if (needsAllEncodings) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(ConfigurationCondition.alwaysTrue(), "org/graalvm/shadowed/org/jcodings/tables/.*bin$"); - } Class frameClass = config.findClassByName("com.oracle.truffle.api.impl.FrameWithoutBoxing"); config.registerFieldValueTransformer(config.findField(frameClass, "ASSERTIONS_ENABLED"), new AssertionStatusFieldTransformer(frameClass)); } From 5e717506506dc74a1109fb3120d13c36e0ce7f1f Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 30 May 2023 15:12:40 +0200 Subject: [PATCH 09/54] Extract bunndles registration from pattern based addResources --- .../oracle/svm/hosted/ResourcesFeature.java | 40 +++++++++++++++++++ .../hosted/xml/XMLParsersRegistration.java | 15 ++++++- .../svm/truffle/TruffleBaseFeature.java | 30 ++++++++------ 3 files changed, 71 insertions(+), 14 deletions(-) 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 871ef546062b..dc06d85b6fe3 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 @@ -68,6 +68,46 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeResourceSupport; +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; +import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; +import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionType; +import org.graalvm.compiler.phases.util.Providers; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; +import org.graalvm.nativeimage.impl.ConfigurationCondition; +import org.graalvm.nativeimage.impl.RuntimeResourceSupport; + +import com.oracle.svm.core.ClassLoaderSupport; +import com.oracle.svm.core.ClassLoaderSupport.ResourceCollector; +import com.oracle.svm.core.ParsingReason; +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.configure.ConfigurationFile; +import com.oracle.svm.core.configure.ConfigurationFiles; +import com.oracle.svm.core.configure.ResourceConfigurationParser; +import com.oracle.svm.core.configure.ResourcesRegistry; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.jdk.Resources; +import com.oracle.svm.core.jdk.resources.NativeImageResourceFileAttributes; +import com.oracle.svm.core.jdk.resources.NativeImageResourceFileAttributesView; +import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystem; +import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystemProvider; +import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.option.LocatableMultiOptionValue; +import com.oracle.svm.core.util.UserError; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.config.ConfigurationParserUtils; +import com.oracle.svm.hosted.jdk.localization.LocalizationFeature; +import com.oracle.svm.util.ModuleSupport; +import com.oracle.svm.util.ReflectionUtil; + +import jdk.vm.ci.meta.ResolvedJavaMethod; import com.oracle.svm.core.ClassLoaderSupport; import com.oracle.svm.core.ClassLoaderSupport.ResourceCollector; import com.oracle.svm.core.MissingRegistrationUtils; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/xml/XMLParsersRegistration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/xml/XMLParsersRegistration.java index ed1cb565df8f..13940b2be407 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/xml/XMLParsersRegistration.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/xml/XMLParsersRegistration.java @@ -135,7 +135,20 @@ void registerResources() { ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xml.internal.serializer.utils.SerializerMessages"); ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMessages"); - ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(ConfigurationCondition.alwaysTrue(), "com.sun.*.properties"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xml.internal.serializer.Encodings"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xml.internal.serializer.HTMLEntities"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xml.internal.serializer.XMLEntities"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xerces.internal.impl.xpath.regex.message"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xerces.internal.impl.msg.DOMMessages"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xerces.internal.impl.msg.DatatypeMessages"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xerces.internal.impl.msg.JAXPValidationMessages"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xerces.internal.impl.msg.SAXMessages"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xerces.internal.impl.msg.XIncludeMessages"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xerces.internal.impl.msg.XMLMessages"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xerces.internal.impl.msg.XMLSchemaMessages"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xerces.internal.impl.msg.XMLSerializerMessages"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xerces.internal.impl.msg.XPointerMessages"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xalan.internal.res.XSLTInfo"); classInitializationSupport.setConfigurationSealed(true); } 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 750d6e4d5cac..646f9bd974b3 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 @@ -229,8 +229,6 @@ public boolean getAsBoolean() { private final Set> registeredClasses = new HashSet<>(); private final Map, PossibleReplaceCandidatesSubtypeHandler> subtypeChecks = new HashMap<>(); private boolean profilingEnabled; - private boolean needsAllEncodings; - private Field uncachedDispatchField; private Field layoutInfoMapField; private Field layoutMapField; @@ -310,8 +308,12 @@ public void afterRegistration(AfterRegistrationAccess a) { initializeTruffleReflectively(imageClassLoader); initializeHomeFinder(); - needsAllEncodings = invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "getNeedsAllEncodings", + boolean needsAllEncodings = invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "getNeedsAllEncodings", Collections.emptyList()); + if (needsAllEncodings) { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(ConfigurationCondition.alwaysTrue(), "org/graalvm/shadowed/org/jcodings/tables/.*bin$"); + } + // reinitialize language cache invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory", "reinitializeNativeImageState", Collections.emptyList()); @@ -448,10 +450,6 @@ public void duringSetup(DuringSetupAccess access) { if (Options.TruffleCheckPreinitializedFiles.getValue()) { access.registerObjectReplacer(new TruffleFileCheck(((FeatureImpl.DuringSetupAccessImpl) access).getHostVM().getClassInitializationSupport())); } - - if (needsAllEncodings) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(ConfigurationCondition.alwaysTrue(), "org/graalvm/shadowed/org/jcodings/tables/.*bin$"); - } } void setGraalGraphObjectReplacer(GraalGraphObjectReplacer graalGraphObjectReplacer) { @@ -1181,7 +1179,8 @@ final class Target_com_oracle_truffle_api_staticobject_PodBasedStaticShape { @TargetClass(className = "com.oracle.truffle.api.staticobject.ArrayBasedStaticShape", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_api_staticobject_ArrayBasedStaticShape { - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = MapCleaner.class, isFinal = true) // + @Alias + @RecomputeFieldValue(kind = Kind.Custom, declClass = MapCleaner.class, isFinal = true) // static ConcurrentHashMap replacements; private static class MapCleaner implements FieldValueTransformerWithAvailability { @@ -1363,7 +1362,8 @@ final class Target_com_oracle_truffle_polyglot_LanguageCache { * verification in DisallowedImageHeapObjectFeature, so we also do the implicit reset using a * substitution. */ - @Alias @RecomputeFieldValue(kind = Kind.Reset) // + @Alias + @RecomputeFieldValue(kind = Kind.Reset) // private String languageHome; } @@ -1408,13 +1408,15 @@ final class Target_com_oracle_truffle_polyglot_InternalResourceCache_ResettableC @TargetClass(className = "com.oracle.truffle.object.CoreLocations$DynamicObjectFieldLocation", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_object_CoreLocations_DynamicObjectFieldLocation { - @Alias @RecomputeFieldValue(kind = Kind.AtomicFieldUpdaterOffset) // + @Alias + @RecomputeFieldValue(kind = Kind.AtomicFieldUpdaterOffset) // private long offset; } @TargetClass(className = "com.oracle.truffle.object.CoreLocations$DynamicLongFieldLocation", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_object_CoreLocations_DynamicLongFieldLocation { - @Alias @RecomputeFieldValue(kind = Kind.AtomicFieldUpdaterOffset) // + @Alias + @RecomputeFieldValue(kind = Kind.AtomicFieldUpdaterOffset) // private long offset; } @@ -1441,7 +1443,8 @@ final class Target_com_oracle_truffle_api_nodes_Node { @TargetClass(className = "com.oracle.truffle.api.nodes.NodeClassImpl", innerClass = "NodeFieldData", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_api_nodes_NodeClassImpl_NodeFieldData { - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetComputer.class, isFinal = true) // + @Alias + @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetComputer.class, isFinal = true) // private long offset; private static class OffsetComputer implements FieldValueTransformerWithAvailability { @@ -1467,7 +1470,8 @@ public Object transform(Object receiver, Object originalValue) { @TargetClass(className = "com.oracle.truffle.api.dsl.InlineSupport$UnsafeField", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_api_dsl_InlineSupport_UnsafeField { - @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetComputer.class, isFinal = true) // + @Alias + @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetComputer.class, isFinal = true) // private long offset; /* From 1468ef1695809914b0184a4394b53be7bf8e11e2 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 31 May 2023 12:32:46 +0200 Subject: [PATCH 10/54] Replace resource Bundles registration with non-pattern-based implementation --- ...ContentSubstitutedLocalizationSupport.java | 5 ++-- .../jdk/localization/LocalizationSupport.java | 25 ++++++++++++++----- .../OptimizedLocalizationSupport.java | 4 +-- .../jdk/localization/LocalizationFeature.java | 10 ++++---- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java index 9f918232531e..22bd8772fe5f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java @@ -67,8 +67,9 @@ public class BundleContentSubstitutedLocalizationSupport extends LocalizationSup private final Set existingBundles = ConcurrentHashMap.newKeySet(); - public BundleContentSubstitutedLocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset, List requestedPatterns, ForkJoinPool pool) { - super(defaultLocale, locales, defaultCharset); + public BundleContentSubstitutedLocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset, List requestedPatterns, ForkJoinPool pool, + ClassLoader appClassLoader) { + super(defaultLocale, locales, defaultCharset, appClassLoader); this.pool = pool; this.compressBundlesPatterns = parseCompressBundlePatterns(requestedPatterns); } 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 d6ab244df836..60cb49e26eeb 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 @@ -31,6 +31,7 @@ import java.util.IllformedLocaleException; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; import java.util.Set; @@ -69,11 +70,14 @@ public class LocalizationSupport { public final Charset defaultCharset; - public LocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset) { + private final ClassLoader appClassLoader; + + public LocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset, ClassLoader appClassLoader) { this.defaultLocale = defaultLocale; this.allLocales = locales.toArray(new Locale[0]); this.defaultCharset = defaultCharset; this.supportedLanguageTags = locales.stream().map(Locale::toString).collect(Collectors.toSet()); + this.appClassLoader = appClassLoader; } public boolean optimizedMode() { @@ -101,14 +105,23 @@ public Map getBundleContentOf(Object bundle) { public void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale) { if (bundle instanceof PropertyResourceBundle) { String[] bundleNameWithModule = SubstrateUtil.split(bundleName, ":", 2); - String resultingPattern; + String resourceName; if (bundleNameWithModule.length < 2) { - resultingPattern = control.toBundleName(bundleName, locale).replace('.', '/'); + resourceName = control.toBundleName(bundleName, locale).replace('.', '/'); + if (appClassLoader.getResource(resourceName) != null) { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(appClassLoader.getUnnamedModule(), resourceName); + } else { + for (Module m : ModuleLayer.boot().modules()) { + if (m.getClassLoader().getResource(resourceName) != null) { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName); + } + } + } } else { - String patternWithLocale = control.toBundleName(bundleNameWithModule[1], locale).replace('.', '/'); - resultingPattern = bundleNameWithModule[0] + ':' + patternWithLocale; + resourceName = control.toBundleName(bundleNameWithModule[1], locale).replace('.', '/'); + Optional module = ModuleLayer.boot().findModule(bundleNameWithModule[0]); + module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName)); } - ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(ConfigurationCondition.alwaysTrue(), resultingPattern + "\\.properties"); } else { registerRequiredReflectionAndResourcesForBundle(bundleName, Set.of(locale)); RuntimeReflection.register(bundle.getClass()); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java index 91187cdc9bd8..c00dabda73bd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java @@ -50,8 +50,8 @@ public class OptimizedLocalizationSupport extends LocalizationSupport { final Map, ResourceBundle> resourceBundles = new HashMap<>(); - public OptimizedLocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset) { - super(defaultLocale, locales, defaultCharset); + public OptimizedLocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset, ClassLoader appClassLoader) { + super(defaultLocale, locales, defaultCharset, appClassLoader); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index 573ef4ddb6d9..3a8155d49986 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -264,7 +264,7 @@ public void afterRegistration(AfterRegistrationAccess access) { throw UserError.abort(ex, "Invalid default charset %s", defaultCharsetOptionValue); } allLocales.add(defaultLocale); - support = selectLocalizationSupport(); + support = selectLocalizationSupport(access.getApplicationClassLoader()); ImageSingletons.add(LocalizationSupport.class, support); addCharsets(); @@ -322,14 +322,14 @@ private Object eagerlyInitializeBundles(Object object) { } @Platforms(Platform.HOSTED_ONLY.class) - private LocalizationSupport selectLocalizationSupport() { + private LocalizationSupport selectLocalizationSupport(ClassLoader appClassLoader) { if (optimizedMode) { - return new OptimizedLocalizationSupport(defaultLocale, allLocales, defaultCharset); + return new OptimizedLocalizationSupport(defaultLocale, allLocales, defaultCharset, appClassLoader); } else if (substituteLoadLookup) { List requestedPatterns = Options.LocalizationCompressBundles.getValue().values(); - return new BundleContentSubstitutedLocalizationSupport(defaultLocale, allLocales, defaultCharset, requestedPatterns, compressionPool); + return new BundleContentSubstitutedLocalizationSupport(defaultLocale, allLocales, defaultCharset, requestedPatterns, compressionPool, appClassLoader); } - return new LocalizationSupport(defaultLocale, allLocales, defaultCharset); + return new LocalizationSupport(defaultLocale, allLocales, defaultCharset, appClassLoader); } @Override From 55c9af1521df817f9fb88ec37fb8cbaefffc7931 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 31 May 2023 14:37:34 +0200 Subject: [PATCH 11/54] Add null pointer check for module classLoader --- .../oracle/svm/core/jdk/localization/LocalizationSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 60cb49e26eeb..6d20ccfaf51b 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 @@ -112,7 +112,7 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Locale local ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(appClassLoader.getUnnamedModule(), resourceName); } else { for (Module m : ModuleLayer.boot().modules()) { - if (m.getClassLoader().getResource(resourceName) != null) { + if (m.getClassLoader() != null && m.getClassLoader().getResource(resourceName) != null) { ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName); } } From 5d536128c916c089a66e44aa2651274dca879815 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Thu, 1 Jun 2023 11:44:08 +0200 Subject: [PATCH 12/54] Add .properties extension to resourceBundle name --- .../oracle/svm/core/jdk/localization/LocalizationSupport.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6d20ccfaf51b..2c366c52f7be 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 @@ -107,7 +107,7 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Locale local String[] bundleNameWithModule = SubstrateUtil.split(bundleName, ":", 2); String resourceName; if (bundleNameWithModule.length < 2) { - resourceName = control.toBundleName(bundleName, locale).replace('.', '/'); + resourceName = control.toBundleName(bundleName, locale).replace('.', '/').concat(".properties"); if (appClassLoader.getResource(resourceName) != null) { ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(appClassLoader.getUnnamedModule(), resourceName); } else { @@ -118,7 +118,7 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Locale local } } } else { - resourceName = control.toBundleName(bundleNameWithModule[1], locale).replace('.', '/'); + resourceName = control.toBundleName(bundleNameWithModule[1], locale).replace('.', '/').concat(".properties"); Optional module = ModuleLayer.boot().findModule(bundleNameWithModule[0]); module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName)); } From 5633755150af98043bef501e31ae77670b4f8465 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Thu, 8 Jun 2023 16:52:18 +0200 Subject: [PATCH 13/54] Add module lookup map --- .../oracle/svm/core/ClassLoaderSupport.java | 4 ++ ...ContentSubstitutedLocalizationSupport.java | 5 +-- .../jdk/localization/LocalizationSupport.java | 44 +++++++++++-------- .../OptimizedLocalizationSupport.java | 10 +++-- .../svm/hosted/ClassLoaderSupportImpl.java | 5 +++ .../oracle/svm/hosted/ResourcesFeature.java | 2 +- .../jdk/localization/LocalizationFeature.java | 22 +++++++--- 7 files changed, 59 insertions(+), 33 deletions(-) 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 4c7e0aaca4ad..4b01c0d63c48 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 @@ -29,7 +29,9 @@ import java.net.URI; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.ResourceBundle; +import java.util.Set; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -66,4 +68,6 @@ public interface ResourceCollector { public abstract void collectResources(ResourceCollector resourceCollector); public abstract List getResourceBundle(String bundleName, Locale locale); + + public abstract Map> getPackageToModules(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java index 22bd8772fe5f..9f918232531e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java @@ -67,9 +67,8 @@ public class BundleContentSubstitutedLocalizationSupport extends LocalizationSup private final Set existingBundles = ConcurrentHashMap.newKeySet(); - public BundleContentSubstitutedLocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset, List requestedPatterns, ForkJoinPool pool, - ClassLoader appClassLoader) { - super(defaultLocale, locales, defaultCharset, appClassLoader); + public BundleContentSubstitutedLocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset, List requestedPatterns, ForkJoinPool pool) { + super(defaultLocale, locales, defaultCharset); this.pool = pool; this.compressBundlesPatterns = parseCompressBundlePatterns(requestedPatterns); } 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 2c366c52f7be..5cfad84c4e4e 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 @@ -35,6 +35,7 @@ import java.util.PropertyResourceBundle; import java.util.ResourceBundle; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import jdk.graal.compiler.debug.GraalError; @@ -46,6 +47,7 @@ import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.nativeimage.impl.RuntimeResourceSupport; +import com.oracle.svm.core.ClassLoaderSupport; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.util.VMError; @@ -70,14 +72,11 @@ public class LocalizationSupport { public final Charset defaultCharset; - private final ClassLoader appClassLoader; - - public LocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset, ClassLoader appClassLoader) { + public LocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset) { this.defaultLocale = defaultLocale; this.allLocales = locales.toArray(new Locale[0]); this.defaultCharset = defaultCharset; this.supportedLanguageTags = locales.stream().map(Locale::toString).collect(Collectors.toSet()); - this.appClassLoader = appClassLoader; } public boolean optimizedMode() { @@ -102,25 +101,26 @@ public Map getBundleContentOf(Object bundle) { } @Platforms(Platform.HOSTED_ONLY.class) - public void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale) { - if (bundle instanceof PropertyResourceBundle) { + public void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale, Function> findModule) { + if (bundle instanceof PropertyResourceBundle prb) { String[] bundleNameWithModule = SubstrateUtil.split(bundleName, ":", 2); String resourceName; if (bundleNameWithModule.length < 2) { - resourceName = control.toBundleName(bundleName, locale).replace('.', '/').concat(".properties"); - if (appClassLoader.getResource(resourceName) != null) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(appClassLoader.getUnnamedModule(), resourceName); - } else { - for (Module m : ModuleLayer.boot().modules()) { - if (m.getClassLoader() != null && m.getClassLoader().getResource(resourceName) != null) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName); - } - } + // find module based on package name + Map> packageToModules = ImageSingletons.lookup(ClassLoaderSupport.class).getPackageToModules(); + Set modules = packageToModules.getOrDefault(packageName(bundleName), Collections.emptySet()); + + // there should be only one module but we will check all modules where given package + // is found + for (Module m : modules) { + resourceName = control.toBundleName(bundleName, prb.getLocale()).replace('.', '/').concat(".properties"); + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName); } } else { - resourceName = control.toBundleName(bundleNameWithModule[1], locale).replace('.', '/').concat(".properties"); - Optional module = ModuleLayer.boot().findModule(bundleNameWithModule[0]); - module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName)); + resourceName = control.toBundleName(bundleNameWithModule[1], prb.getLocale()).replace('.', '/').concat(".properties"); + Optional module = findModule.apply(bundleNameWithModule[0]); + String finalResourceName = resourceName; + module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, finalResourceName)); } } else { registerRequiredReflectionAndResourcesForBundle(bundleName, Set.of(locale)); @@ -130,6 +130,14 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Locale local } } + private static String packageName(String bundleName) { + int classSep = bundleName.lastIndexOf('.'); + if (classSep == -1) { + return ""; /* unnamed package */ + } + return bundleName.substring(0, classSep); + } + public String getResultingPattern(String bundleName, Locale locale) { String fixedBundleName = bundleName.replace("$", "\\$"); return getBundleName(fixedBundleName, locale); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java index c00dabda73bd..80c15a240b94 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java @@ -30,8 +30,10 @@ import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; +import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; +import java.util.function.Function; import java.util.spi.LocaleServiceProvider; import org.graalvm.collections.Pair; @@ -50,8 +52,8 @@ public class OptimizedLocalizationSupport extends LocalizationSupport { final Map, ResourceBundle> resourceBundles = new HashMap<>(); - public OptimizedLocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset, ClassLoader appClassLoader) { - super(defaultLocale, locales, defaultCharset, appClassLoader); + public OptimizedLocalizationSupport(Locale defaultLocale, Set locales, Charset defaultCharset) { + super(defaultLocale, locales, defaultCharset); } @Override @@ -89,7 +91,7 @@ public void prepareClassResourceBundle(String basename, Class bundleClass) { /*- Set the basename and locale to be consistent with JVM lookup process */ bundleNameField.set(bundle, basename); bundleLocaleField.set(bundle, locale); - prepareBundle(basename, bundle, locale); + prepareBundle(basename, bundle, locale, null); } catch (ReflectionUtil.ReflectionUtilError | ReflectiveOperationException e) { throw UserError.abort(e, "Failed to instantiated bundle from class %s, reason %s", bundleClass, e.getCause().getMessage()); } @@ -106,7 +108,7 @@ private static Locale extractLocale(Class bundleClass) { @Platforms(Platform.HOSTED_ONLY.class) @Override - public void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale) { + public void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale, Function> findModule) { bundle.keySet(); this.resourceBundles.put(Pair.create(bundleName, locale), bundle); } 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 1452c978ba52..a0adb54b7d19 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 @@ -283,6 +283,11 @@ public List getResourceBundle(String bundleSpec, Locale locale) return resourceBundles; } + @Override + public Map> getPackageToModules() { + return packageToModules; + } + private static String packageName(String bundleName) { int classSep = bundleName.lastIndexOf('.'); if (classSep == -1) { 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 dc06d85b6fe3..143219dd9c83 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 @@ -221,7 +221,7 @@ public void addResource(Module module, String resourcePath) { boolean fromJar = false; boolean isDirectory = false; - if (module.isNamed()) { + if (module != null && module.isNamed()) { try { String resourcePackage = jdk.internal.module.Resources.toPackageName(resourcePath); if (!resourcePackage.isEmpty()) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index 3a8155d49986..eb6594c83866 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -89,6 +89,7 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; +import com.oracle.svm.hosted.ImageClassLoader; import jdk.internal.access.SharedSecrets; import jdk.vm.ci.meta.ResolvedJavaField; @@ -162,6 +163,10 @@ public class LocalizationFeature implements InternalFeature { private Field langAliasesCacheField; private Field parentLocalesMapField; + @Platforms(Platform.HOSTED_ONLY.class) private ClassLoader classLoader; + + @Platforms(Platform.HOSTED_ONLY.class) private ImageClassLoader imageClassLoader; + public static class Options { @Option(help = "Comma separated list of bundles to be included into the image.", type = OptionType.User)// public static final HostedOptionKey IncludeResourceBundles = new HostedOptionKey<>(LocatableMultiOptionValue.Strings.buildWithCommaDelimiter()); @@ -264,7 +269,7 @@ public void afterRegistration(AfterRegistrationAccess access) { throw UserError.abort(ex, "Invalid default charset %s", defaultCharsetOptionValue); } allLocales.add(defaultLocale); - support = selectLocalizationSupport(access.getApplicationClassLoader()); + support = selectLocalizationSupport(); ImageSingletons.add(LocalizationSupport.class, support); addCharsets(); @@ -292,6 +297,9 @@ public void duringSetup(DuringSetupAccess a) { String reason = "All ResourceBundleControlProvider that are registered as services end up as objects in the image heap, and are therefore registered to be initialized at image build time"; ServiceLoader.load(ResourceBundleControlProvider.class).stream() .forEach(provider -> ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtBuildTime(provider.type(), reason)); + + this.classLoader = access.getApplicationClassLoader(); + this.imageClassLoader = access.getImageClassLoader(); } /** @@ -322,14 +330,14 @@ private Object eagerlyInitializeBundles(Object object) { } @Platforms(Platform.HOSTED_ONLY.class) - private LocalizationSupport selectLocalizationSupport(ClassLoader appClassLoader) { + private LocalizationSupport selectLocalizationSupport() { if (optimizedMode) { - return new OptimizedLocalizationSupport(defaultLocale, allLocales, defaultCharset, appClassLoader); + return new OptimizedLocalizationSupport(defaultLocale, allLocales, defaultCharset); } else if (substituteLoadLookup) { List requestedPatterns = Options.LocalizationCompressBundles.getValue().values(); - return new BundleContentSubstitutedLocalizationSupport(defaultLocale, allLocales, defaultCharset, requestedPatterns, compressionPool, appClassLoader); + return new BundleContentSubstitutedLocalizationSupport(defaultLocale, allLocales, defaultCharset, requestedPatterns, compressionPool); } - return new LocalizationSupport(defaultLocale, allLocales, defaultCharset, appClassLoader); + return new LocalizationSupport(defaultLocale, allLocales, defaultCharset); } @Override @@ -639,14 +647,14 @@ private void prepareBundle(String bundleName, ResourceBundle bundle, Locale loca */ for (ResourceBundle cur = bundle; cur != null; cur = SharedSecrets.getJavaUtilResourceBundleAccess().getParent(cur)) { /* Register all bundles with their corresponding locales */ - support.prepareBundle(bundleName, cur, cur.getLocale()); + support.prepareBundle(bundleName, cur, locale, this.imageClassLoader::findModule); } /* * Finally, register the requested bundle with requested locale (Requested might be more * specific than the actual bundle locale */ - support.prepareBundle(bundleName, bundle, locale); + support.prepareBundle(bundleName, bundle, locale, this.imageClassLoader::findModule); } @Platforms(Platform.HOSTED_ONLY.class) From fa166d80ba35a5c5379f8b6807a3c36308813769 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 9 Jun 2023 14:20:51 +0200 Subject: [PATCH 14/54] Add unnamed module case --- .../hosted/RuntimeResourceAccess.java | 2 +- .../impl/RuntimeResourceSupport.java | 2 +- .../jdk/localization/LocalizationSupport.java | 10 ++++- .../OptimizedLocalizationSupport.java | 6 +-- .../oracle/svm/hosted/ResourcesFeature.java | 40 ------------------- .../jdk/localization/LocalizationFeature.java | 4 +- 6 files changed, 15 insertions(+), 49 deletions(-) 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 76c43f86efd2..1e26a651c290 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 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 2d370a511ffd..6147690f249c 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 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 5cfad84c4e4e..d8c54dc823d5 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 @@ -101,11 +101,13 @@ public Map getBundleContentOf(Object bundle) { } @Platforms(Platform.HOSTED_ONLY.class) - public void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale, Function> findModule) { + public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule) { if (bundle instanceof PropertyResourceBundle prb) { String[] bundleNameWithModule = SubstrateUtil.split(bundleName, ":", 2); String resourceName; if (bundleNameWithModule.length < 2) { + resourceName = control.toBundleName(bundleName, prb.getLocale()).replace('.', '/').concat(".properties"); + // find module based on package name Map> packageToModules = ImageSingletons.lookup(ClassLoaderSupport.class).getPackageToModules(); Set modules = packageToModules.getOrDefault(packageName(bundleName), Collections.emptySet()); @@ -113,9 +115,13 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Locale local // there should be only one module but we will check all modules where given package // is found for (Module m : modules) { - resourceName = control.toBundleName(bundleName, prb.getLocale()).replace('.', '/').concat(".properties"); ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName); } + + // if didn't find resource in any module, we will try with unnamed module + if (modules.isEmpty()) { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(null, resourceName); + } } else { resourceName = control.toBundleName(bundleNameWithModule[1], prb.getLocale()).replace('.', '/').concat(".properties"); Optional module = findModule.apply(bundleNameWithModule[0]); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java index 80c15a240b94..30afc879b2af 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java @@ -91,7 +91,7 @@ public void prepareClassResourceBundle(String basename, Class bundleClass) { /*- Set the basename and locale to be consistent with JVM lookup process */ bundleNameField.set(bundle, basename); bundleLocaleField.set(bundle, locale); - prepareBundle(basename, bundle, locale, null); + prepareBundle(basename, bundle, null); } catch (ReflectionUtil.ReflectionUtilError | ReflectiveOperationException e) { throw UserError.abort(e, "Failed to instantiated bundle from class %s, reason %s", bundleClass, e.getCause().getMessage()); } @@ -108,9 +108,9 @@ private static Locale extractLocale(Class bundleClass) { @Platforms(Platform.HOSTED_ONLY.class) @Override - public void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale, Function> findModule) { + public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule) { bundle.keySet(); - this.resourceBundles.put(Pair.create(bundleName, locale), bundle); + this.resourceBundles.put(Pair.create(bundleName, bundle.getLocale()), bundle); } @Override 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 143219dd9c83..8007464845ba 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 @@ -68,46 +68,6 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeResourceSupport; -import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; -import org.graalvm.compiler.debug.DebugContext; -import org.graalvm.compiler.nodes.ValueNode; -import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; -import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; -import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; -import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; -import org.graalvm.compiler.options.Option; -import org.graalvm.compiler.options.OptionType; -import org.graalvm.compiler.phases.util.Providers; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; -import org.graalvm.nativeimage.impl.ConfigurationCondition; -import org.graalvm.nativeimage.impl.RuntimeResourceSupport; - -import com.oracle.svm.core.ClassLoaderSupport; -import com.oracle.svm.core.ClassLoaderSupport.ResourceCollector; -import com.oracle.svm.core.ParsingReason; -import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.configure.ConfigurationFile; -import com.oracle.svm.core.configure.ConfigurationFiles; -import com.oracle.svm.core.configure.ResourceConfigurationParser; -import com.oracle.svm.core.configure.ResourcesRegistry; -import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; -import com.oracle.svm.core.feature.InternalFeature; -import com.oracle.svm.core.jdk.Resources; -import com.oracle.svm.core.jdk.resources.NativeImageResourceFileAttributes; -import com.oracle.svm.core.jdk.resources.NativeImageResourceFileAttributesView; -import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystem; -import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystemProvider; -import com.oracle.svm.core.option.HostedOptionKey; -import com.oracle.svm.core.option.LocatableMultiOptionValue; -import com.oracle.svm.core.util.UserError; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.hosted.config.ConfigurationParserUtils; -import com.oracle.svm.hosted.jdk.localization.LocalizationFeature; -import com.oracle.svm.util.ModuleSupport; -import com.oracle.svm.util.ReflectionUtil; - -import jdk.vm.ci.meta.ResolvedJavaMethod; import com.oracle.svm.core.ClassLoaderSupport; import com.oracle.svm.core.ClassLoaderSupport.ResourceCollector; import com.oracle.svm.core.MissingRegistrationUtils; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index eb6594c83866..156a2e4aabd7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -647,14 +647,14 @@ private void prepareBundle(String bundleName, ResourceBundle bundle, Locale loca */ for (ResourceBundle cur = bundle; cur != null; cur = SharedSecrets.getJavaUtilResourceBundleAccess().getParent(cur)) { /* Register all bundles with their corresponding locales */ - support.prepareBundle(bundleName, cur, locale, this.imageClassLoader::findModule); + support.prepareBundle(bundleName, cur, this.imageClassLoader::findModule); } /* * Finally, register the requested bundle with requested locale (Requested might be more * specific than the actual bundle locale */ - support.prepareBundle(bundleName, bundle, locale, this.imageClassLoader::findModule); + support.prepareBundle(bundleName, bundle, this.imageClassLoader::findModule); } @Platforms(Platform.HOSTED_ONLY.class) From 2b762d9fb7eb189708c65b957f56715ae96cdae9 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 16 Jun 2023 10:32:23 +0200 Subject: [PATCH 15/54] Move resource adding to duringSetup when the RuntimeResourceSupport is added to singletones --- .../com/oracle/svm/truffle/TruffleBaseFeature.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 646f9bd974b3..8b6559f55062 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 @@ -239,6 +239,8 @@ public boolean getAsBoolean() { private Consumer markAsUnsafeAccessed; private final ConcurrentMap processedInlinedFields = new ConcurrentHashMap<>(); + private boolean needsAllEncodings; + private static void initializeTruffleReflectively(ClassLoader imageClassLoader) { invokeStaticMethod("com.oracle.truffle.api.impl.Accessor", "getTVMCI", Collections.emptyList()); invokeStaticMethod("com.oracle.truffle.polyglot.InternalResourceCache", "initializeNativeImageState", @@ -308,11 +310,8 @@ public void afterRegistration(AfterRegistrationAccess a) { initializeTruffleReflectively(imageClassLoader); initializeHomeFinder(); - boolean needsAllEncodings = invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "getNeedsAllEncodings", + needsAllEncodings = invokeStaticMethod("com.oracle.truffle.polyglot.LanguageCache", "getNeedsAllEncodings", Collections.emptyList()); - if (needsAllEncodings) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(ConfigurationCondition.alwaysTrue(), "org/graalvm/shadowed/org/jcodings/tables/.*bin$"); - } // reinitialize language cache invokeStaticMethod("com.oracle.truffle.api.library.LibraryFactory", "reinitializeNativeImageState", @@ -450,6 +449,10 @@ public void duringSetup(DuringSetupAccess access) { if (Options.TruffleCheckPreinitializedFiles.getValue()) { access.registerObjectReplacer(new TruffleFileCheck(((FeatureImpl.DuringSetupAccessImpl) access).getHostVM().getClassInitializationSupport())); } + + if (needsAllEncodings) { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResources(ConfigurationCondition.alwaysTrue(), "org/graalvm/shadowed/org/jcodings/tables/.*bin$"); + } } void setGraalGraphObjectReplacer(GraalGraphObjectReplacer graalGraphObjectReplacer) { From b4ca2f275c0d4fd4adbeaf5b933f8ec9f34a3d27 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 16 Jun 2023 16:08:21 +0200 Subject: [PATCH 16/54] Try to match resource pattern --- .../svm/hosted/ClassLoaderSupportImpl.java | 2 +- .../oracle/svm/hosted/ResourcesFeature.java | 60 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) 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 a0adb54b7d19..4b1fd4dd5744 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 @@ -89,7 +89,7 @@ protected boolean isNativeImageClassLoaderImpl(ClassLoader loader) { return false; } - private record ResourceLookupInfo(ResolvedModule resolvedModule, Module module) { + public record ResourceLookupInfo(ResolvedModule resolvedModule, Module module) { } private static Stream extractModuleLookupData(ModuleLayer layer) { 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 8007464845ba..46ee4072a032 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 @@ -29,6 +29,9 @@ import java.io.IOException; import java.io.InputStream; +import java.lang.module.ModuleReader; +import java.lang.module.ModuleReference; +import java.lang.module.ResolvedModule; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -42,6 +45,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Set; @@ -52,6 +56,7 @@ import java.util.concurrent.atomic.LongAdder; import java.util.jar.JarFile; import java.util.regex.Pattern; +import java.util.stream.Stream; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; import jdk.graal.compiler.debug.DebugContext; @@ -143,6 +148,8 @@ public static class Options { private int loadedConfigurations; private ImageClassLoader imageClassLoader; + private HashMap> registerIfReachable = new HashMap<>(); + private class ResourcesRegistryImpl extends ConditionalConfigurationRegistry implements ResourcesRegistry { private final ConfigurationTypeResolver configurationTypeResolver; @@ -150,11 +157,64 @@ private class ResourcesRegistryImpl extends ConditionalConfigurationRegistry imp this.configurationTypeResolver = configurationTypeResolver; } + private static Stream extractModuleLookupData(ModuleLayer layer) { + List data = new ArrayList<>(layer.configuration().modules().size()); + for (ResolvedModule m : layer.configuration().modules()) { + Module module = layer.findModule(m.name()).orElse(null); + ClassLoaderSupportImpl.ResourceLookupInfo info = new ClassLoaderSupportImpl.ResourceLookupInfo(m, module); + data.add(info); + } + return data.stream(); + } + + private List findReachableForPattern(String pattern) { + List founded = new ArrayList<>(); + ResourcePattern compiledPattern = compilePatterns(Set.of(pattern))[0]; + NativeImageClassLoaderSupport.allLayers(imageClassLoader.classLoaderSupport.moduleLayerForImageBuild).stream() + .parallel() + .flatMap(ResourcesRegistryImpl::extractModuleLookupData) + .forEach(info -> { + ModuleReference moduleReference = info.resolvedModule().reference(); + try (ModuleReader moduleReader = moduleReference.open()) { + List foundResources = moduleReader.list() + .filter(resourceName -> { + System.out.println("Module files: " + resourceName); + return compiledPattern.pattern.matcher(resourceName).matches(); + }) + .toList(); + founded.addAll(foundResources); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + for (Path classpathFile : imageClassLoader.classLoaderSupport.classpath()) { + System.out.println("Classpath file: " + classpathFile); + if (compiledPattern.pattern.matcher(classpathFile.toString()).matches()) { + founded.add(classpathFile.toString()); + } + } + + System.out.println(founded); + return founded; + } + @Override public void addResources(ConfigurationCondition condition, String pattern) { if (configurationTypeResolver.resolveConditionType(condition.getTypeName()) == null) { return; } + + if (!ConfigurationCondition.alwaysTrue().equals(condition)) { + registerIfReachable.put(condition.getTypeName(), findReachableForPattern(pattern)); + } + + try { + throw new Exception("a"); + } catch (Exception e) { + e.printStackTrace(); + } + registerConditionalConfiguration(condition, () -> { UserError.guarantee(!sealed, "Resources added too late: %s", pattern); resourcePatternWorkSet.add(pattern); From 8a7f024cc24dd1b16df58a348160815476d05e48 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 20 Jun 2023 17:27:19 +0200 Subject: [PATCH 17/54] Cover the case when resource has condition --- .../impl/ConfigurationCondition.java | 6 +- .../config/ResourceConfiguration.java | 21 ++-- .../oracle/svm/core/ClassLoaderSupport.java | 9 +- .../com/oracle/svm/core/jdk/Resources.java | 1 + .../jdk/localization/LocalizationSupport.java | 5 +- .../svm/hosted/ClassLoaderSupportImpl.java | 104 ++++++++++++----- .../oracle/svm/hosted/ResourcesFeature.java | 108 ++++++------------ .../CharsetSubstitutionsFeature.java | 6 +- .../jdk/localization/LocalizationFeature.java | 4 - .../svm/truffle/TruffleBaseFeature.java | 18 +-- 10 files changed, 147 insertions(+), 135 deletions(-) diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ConfigurationCondition.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ConfigurationCondition.java index 72a76b2d2718..e089491291de 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ConfigurationCondition.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ConfigurationCondition.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -50,6 +50,10 @@ public static ConfigurationCondition alwaysTrue() { return OBJECT_REACHABLE; } + public static boolean isAlwaysTrue(ConfigurationCondition condition) { + return ConfigurationCondition.alwaysTrue().equals(condition); + } + public static ConfigurationCondition create(String typeReachability) { Objects.requireNonNull(typeReachability); if (OBJECT_REACHABLE.typeName.equals(typeReachability)) { 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 c7dcb46b78e1..18cfeded7762 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 @@ -24,16 +24,6 @@ */ package com.oracle.svm.configure.config; -import com.oracle.svm.configure.ConfigurationBase; -import com.oracle.svm.core.configure.ConditionalElement; -import com.oracle.svm.core.configure.ConfigurationParser; -import com.oracle.svm.core.configure.ResourceConfigurationParser; -import com.oracle.svm.core.configure.ResourcesRegistry; -import com.oracle.svm.core.util.VMError; -import com.oracle.svm.core.util.json.JsonPrinter; -import com.oracle.svm.core.util.json.JsonWriter; -import org.graalvm.nativeimage.impl.ConfigurationCondition; - import java.io.IOException; import java.util.Collection; import java.util.Comparator; @@ -45,6 +35,17 @@ import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; +import org.graalvm.nativeimage.impl.ConfigurationCondition; + +import com.oracle.svm.configure.ConfigurationBase; +import com.oracle.svm.core.configure.ConditionalElement; +import com.oracle.svm.core.configure.ConfigurationParser; +import com.oracle.svm.core.configure.ResourceConfigurationParser; +import com.oracle.svm.core.configure.ResourcesRegistry; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.core.util.json.JsonPrinter; +import com.oracle.svm.core.util.json.JsonWriter; + public final class ResourceConfiguration extends ConfigurationBase { private static final String PROPERTY_BUNDLE = "java.util.PropertyResourceBundle"; 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 4b01c0d63c48..2b5fd7d4f431 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 @@ -35,6 +35,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.impl.ConfigurationCondition; @Platforms(Platform.HOSTED_ONLY.class) public abstract class ClassLoaderSupport { @@ -53,16 +54,20 @@ public boolean isNativeImageClassLoader(ClassLoader classLoader) { protected abstract boolean isNativeImageClassLoaderImpl(ClassLoader classLoader); public interface ResourceCollector { - - boolean isIncluded(Module module, String resourceName, URI resourceURI); + ConfigurationCondition isIncluded(Module module, String resourceName, URI resourceURI); void addResource(Module module, String resourceName, InputStream resourceStream, boolean fromJar); + void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition); + void addDirectoryResource(Module module, String dir, String content, boolean fromJar); void registerNegativeQuery(Module module, String resourceName); void registerIOException(Module module, String resourceName, IOException e, boolean linkAtBuildTime); + + void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition); + } public abstract void collectResources(ResourceCollector resourceCollector); 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 1240eebf95d0..85e2c519860b 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 @@ -218,6 +218,7 @@ public void registerResource(Module module, String resourceName, InputStream is, public static void registerResource(Module module, String resourceName, boolean isDir, InputStream is, boolean fromJar) { addEntry(module, resourceName, isDir, inputStreamToByteArray(is), fromJar); } + @Platforms(Platform.HOSTED_ONLY.class) public void registerDirectoryResource(String resourceDirName, String content) { registerDirectoryResource(null, resourceDirName, content, true); 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 d8c54dc823d5..e74248dc1ce1 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 @@ -137,11 +137,12 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function foundResources = moduleReader.list() - .filter(resourceName -> shouldIncludeEntry(info.module(), resourceCollector, resourceName, moduleReference.location().orElse(null), includeAll)) - .toList(); + List> resourcesFound = new ArrayList<>(); + moduleReader.list().forEach(resourceName -> { + ConfigurationCondition condition = shouldIncludeEntry(info.module(), resourceCollector, resourceName, moduleReference.location().orElse(null), includeAll); + if (condition != null) { + resourcesFound.add(Pair.create(condition, resourceName)); + } + }); - for (String resName : foundResources) { + for (Pair entry : resourcesFound) { + ConfigurationCondition condition = entry.getLeft(); + String resName = entry.getRight(); if (resName.endsWith("/")) { - resourceCollector.addDirectoryResource(info.module, resName, "", false); + if (ConfigurationCondition.isAlwaysTrue(condition)) { + resourceCollector.addDirectoryResource(info.module, resName, "", false); + } else { + resourceCollector.addResourceConditionally(info.module, resName, condition); + } continue; } Optional content = moduleReader.open(resName); @@ -145,19 +158,24 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso continue; } try (InputStream is = content.get()) { - resourceCollector.addResource(info.module, resName, is, false); + if (ConfigurationCondition.isAlwaysTrue(condition)) { + resourceCollector.addResource(info.module, resName, is, false); + } else { + resourceCollector.addResourceConditionally(info.module, resName, condition); + } } catch (IOException resourceException) { resourceCollector.registerIOException(info.module, resName, resourceException, LinkAtBuildTimeSupport.singleton().moduleLinkAtBuildTime(info.module.getName())); } } + } catch (IOException e) { throw VMError.shouldNotReachHere(e); } } - private static void scanDirectory(Path root, ResourceCollector collector, boolean includeAll) { - Map> matchedDirectoryResources = new HashMap<>(); - Set allEntries = new HashSet<>(); + private static void scanDirectory(Path root, ResourceCollector collector, boolean includeAll) throws IOException { + Map, List> matchedDirectoryResources = new HashMap<>(); + Set> allEntries = new HashSet<>(); ArrayDeque queue = new ArrayDeque<>(); queue.push(root); @@ -166,16 +184,19 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea /* Resources always use / as the separator, as do our resource inclusion patterns */ String relativeFilePath; + ConfigurationCondition condition; if (entry != root) { relativeFilePath = root.relativize(entry).toString().replace(File.separatorChar, RESOURCES_INTERNAL_PATH_SEPARATOR); - allEntries.add(relativeFilePath); + condition = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeAll); + allEntries.add(Pair.create(condition, relativeFilePath)); } else { relativeFilePath = ""; + condition = collector.isIncluded(null, relativeFilePath, Path.of(relativeFilePath).toUri()); } if (Files.isDirectory(entry)) { - if (shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeAll)) { - matchedDirectoryResources.put(relativeFilePath, new ArrayList<>()); + if (condition != null) { + matchedDirectoryResources.put(Pair.create(condition, relativeFilePath), new ArrayList<>()); } try (Stream pathStream = Files.list(entry)) { Stream filtered = pathStream; @@ -187,8 +208,13 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea collector.registerIOException(null, relativeFilePath, resourceException, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(relativeFilePath)); } } else { - if (shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeAll)) { + if (condition != null) { try (InputStream is = Files.newInputStream(entry)) { + if (ConfigurationCondition.isAlwaysTrue(condition)) { + collector.addResource(null, relativeFilePath, is, false); + } else { + collector.addResourceConditionally(null, relativeFilePath, condition); + } collector.addResource(null, relativeFilePath, is, false); } catch (IOException resourceException) { collector.registerIOException(null, relativeFilePath, resourceException, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(relativeFilePath)); @@ -197,20 +223,28 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea } } - for (String entry : allEntries) { - int last = entry.lastIndexOf(RESOURCES_INTERNAL_PATH_SEPARATOR); - String key = last == -1 ? "" : entry.substring(0, last); - List dirContent = matchedDirectoryResources.get(key); - if (dirContent != null && !dirContent.contains(entry)) { - dirContent.add(entry.substring(last + 1)); + for (Pair entry : allEntries) { + ConfigurationCondition condition = entry.getLeft(); + String entryName = entry.getRight(); + int last = entryName.lastIndexOf(RESOURCES_INTERNAL_PATH_SEPARATOR); + String key = last == -1 ? "" : entryName.substring(0, last); + List dirContent = matchedDirectoryResources.get(Pair.create(condition, key)); + if (dirContent != null && !dirContent.contains(entryName)) { + dirContent.add(entryName.substring(last + 1)); } else if (dirContent == null) { collector.registerNegativeQuery(null, key); } } - matchedDirectoryResources.forEach((dir, content) -> { + matchedDirectoryResources.forEach((entry, content) -> { + ConfigurationCondition condition = entry.getLeft(); + String dir = entry.getRight(); content.sort(Comparator.naturalOrder()); - collector.addDirectoryResource(null, dir, String.join(System.lineSeparator(), content), false); + if (ConfigurationCondition.isAlwaysTrue(condition)) { + collector.addDirectoryResource(null, dir, String.join(System.lineSeparator(), content), false); + } else { + collector.addDirectoryResourceConditionally(null, dir, condition); + } }); } @@ -222,14 +256,24 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i URI uri = jarPath.toUri(); if (entry.isDirectory()) { String dirName = entry.getName().substring(0, entry.getName().length() - 1); - if (shouldIncludeEntry(null, collector, dirName, uri, includeAll)) { + ConfigurationCondition condition = shouldIncludeEntry(null, collector, dirName, jarPath.toUri(), includeAll); + if (condition != null) { // Register the directory with empty content to preserve Java behavior - collector.addDirectoryResource(null, dirName, "", true); + if (ConfigurationCondition.isAlwaysTrue(condition)) { + collector.addDirectoryResource(null, dirName, "", true); + } else { + collector.addDirectoryResourceConditionally(null, dirName, condition); + } } } else { - if (shouldIncludeEntry(null, collector, entry.getName(), uri, includeAll)) { + ConfigurationCondition condition = shouldIncludeEntry(null, collector, entry.getName(), jarPath.toUri(), includeAll); + if (condition != null) { try (InputStream is = jf.getInputStream(entry)) { - collector.addResource(null, entry.getName(), is, true); + if (ConfigurationCondition.isAlwaysTrue(condition)) { + collector.addResource(null, entry.getName(), is, true); + } else { + collector.addResourceConditionally(null, entry.getName(), condition); + } } catch (IOException resourceException) { collector.registerIOException(null, entry.getName(), resourceException, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(entry.getName())); } @@ -239,9 +283,13 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i } } - private static boolean shouldIncludeEntry(Module module, ResourceCollector collector, String fileName, URI uri, boolean includeAll) { - var isIncluded = collector.isIncluded(module, fileName, uri); - return isIncluded || (includeAll && !(fileName.endsWith(".class") || fileName.endsWith(".jar"))); + private static ConfigurationCondition shouldIncludeEntry(Module module, ResourceCollector collector, String fileName, URI uri, boolean includeAll) { + ConfigurationCondition conditions = collector.isIncluded(module, fileName, uri); + if (includeAll && !(fileName.endsWith(".class") || fileName.endsWith(".jar"))) { + return ConfigurationCondition.alwaysTrue(); + } + + return conditions; } @Override 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 46ee4072a032..5fb1e7f66b40 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 @@ -29,9 +29,6 @@ import java.io.IOException; import java.io.InputStream; -import java.lang.module.ModuleReader; -import java.lang.module.ModuleReference; -import java.lang.module.ResolvedModule; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -45,7 +42,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Set; @@ -56,7 +52,7 @@ import java.util.concurrent.atomic.LongAdder; import java.util.jar.JarFile; import java.util.regex.Pattern; -import java.util.stream.Stream; +import java.util.stream.Collectors; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; import jdk.graal.compiler.debug.DebugContext; @@ -143,13 +139,12 @@ public static class Options { } private boolean sealed = false; - private Set resourcePatternWorkSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + private Set> resourcePatternWorkSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final Set excludedResourcePatterns = Collections.newSetFromMap(new ConcurrentHashMap<>()); private int loadedConfigurations; private ImageClassLoader imageClassLoader; - private HashMap> registerIfReachable = new HashMap<>(); - private class ResourcesRegistryImpl extends ConditionalConfigurationRegistry implements ResourcesRegistry { private final ConfigurationTypeResolver configurationTypeResolver; @@ -157,68 +152,17 @@ private class ResourcesRegistryImpl extends ConditionalConfigurationRegistry imp this.configurationTypeResolver = configurationTypeResolver; } - private static Stream extractModuleLookupData(ModuleLayer layer) { - List data = new ArrayList<>(layer.configuration().modules().size()); - for (ResolvedModule m : layer.configuration().modules()) { - Module module = layer.findModule(m.name()).orElse(null); - ClassLoaderSupportImpl.ResourceLookupInfo info = new ClassLoaderSupportImpl.ResourceLookupInfo(m, module); - data.add(info); - } - return data.stream(); - } - - private List findReachableForPattern(String pattern) { - List founded = new ArrayList<>(); - ResourcePattern compiledPattern = compilePatterns(Set.of(pattern))[0]; - NativeImageClassLoaderSupport.allLayers(imageClassLoader.classLoaderSupport.moduleLayerForImageBuild).stream() - .parallel() - .flatMap(ResourcesRegistryImpl::extractModuleLookupData) - .forEach(info -> { - ModuleReference moduleReference = info.resolvedModule().reference(); - try (ModuleReader moduleReader = moduleReference.open()) { - List foundResources = moduleReader.list() - .filter(resourceName -> { - System.out.println("Module files: " + resourceName); - return compiledPattern.pattern.matcher(resourceName).matches(); - }) - .toList(); - founded.addAll(foundResources); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - - for (Path classpathFile : imageClassLoader.classLoaderSupport.classpath()) { - System.out.println("Classpath file: " + classpathFile); - if (compiledPattern.pattern.matcher(classpathFile.toString()).matches()) { - founded.add(classpathFile.toString()); - } - } - - System.out.println(founded); - return founded; - } - @Override public void addResources(ConfigurationCondition condition, String pattern) { if (configurationTypeResolver.resolveConditionType(condition.getTypeName()) == null) { return; } - if (!ConfigurationCondition.alwaysTrue().equals(condition)) { - registerIfReachable.put(condition.getTypeName(), findReachableForPattern(pattern)); - } - try { - throw new Exception("a"); - } catch (Exception e) { - e.printStackTrace(); + resourcePatternWorkSet.add(Pair.create(condition, pattern)); + } catch (UnsupportedOperationException e) { + UserError.abort("Resource registration should be performed before beforeAnalysis phase."); } - - registerConditionalConfiguration(condition, () -> { - UserError.guarantee(!sealed, "Resources added too late: %s", pattern); - resourcePatternWorkSet.add(pattern); - }); } private String urlToJarPath(URL url) { @@ -348,19 +292,24 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurations(parser, imageClassLoader, "resource", ConfigurationFiles.Options.ResourceConfigurationFiles, ConfigurationFiles.Options.ResourceConfigurationResources, ConfigurationFile.RESOURCES.getFileName()); - - resourcePatternWorkSet.addAll(Options.IncludeResources.getValue().values()); + resourcePatternWorkSet.addAll(Options.IncludeResources.getValue() + .values() + .stream() + .map(e -> Pair.create(ConfigurationCondition.alwaysTrue(), e)) + .toList()); excludedResourcePatterns.addAll(Options.ExcludeResources.getValue().values()); if (!resourcePatternWorkSet.isEmpty()) { FeatureImpl.BeforeAnalysisAccessImpl beforeAnalysisAccess = (FeatureImpl.BeforeAnalysisAccessImpl) access; - ResourcePattern[] includePatterns = compilePatterns(resourcePatternWorkSet); + Set> includePatterns = resourcePatternWorkSet + .stream() + .map(e -> Pair.create(e.getLeft(), makeResourcePattern(e.getRight()))) + .collect(Collectors.toSet()); if (MissingRegistrationUtils.throwMissingRegistrationErrors()) { for (ResourcePattern resourcePattern : includePatterns) { Resources.singleton().registerIncludePattern(resourcePattern.moduleName, resourcePattern.pattern.pattern()); } } - ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns); DebugContext debugContext = beforeAnalysisAccess.getDebugContext(); ResourceCollectorImpl collector = new ResourceCollectorImpl(debugContext, includePatterns, excludePatterns); @@ -381,7 +330,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { private static final class ResourceCollectorImpl implements ResourceCollector { private final DebugContext debugContext; - private final ResourcePattern[] includePatterns; + private final Set> includePatterns; private final ResourcePattern[] excludePatterns; private static final int WATCHDOG_RESET_AFTER_EVERY_N_RESOURCES = 1000; @@ -423,7 +372,7 @@ private void shutDownProgressReporter() { } @Override - public boolean isIncluded(Module module, String resourceName, URI resource) { + public ConfigurationCondition isIncluded(Module module, String resourceName, URI resource) { this.currentlyProcessedEntry = resource.getScheme().equals("jrt") ? (resource + "/" + resourceName) : resource.toString(); this.reachedResourceEntries.increment(); @@ -439,20 +388,20 @@ public boolean isIncluded(Module module, String resourceName, URI resource) { continue; } if (rp.pattern.matcher(resourceName).matches() || rp.pattern.matcher(relativePathWithTrailingSlash).matches()) { - return false; + return null; } } - for (ResourcePattern rp : includePatterns) { - if (!rp.moduleNameMatches(moduleName)) { + for (Pair rp : includePatterns) { + if (!rp.getRight().moduleNameMatches(moduleName)) { continue; } - if (rp.pattern.matcher(resourceName).matches() || rp.pattern.matcher(relativePathWithTrailingSlash).matches()) { - return true; + if (rp.getRight().pattern.matcher(resourceName).matches() || rp.getRight().pattern.matcher(relativePathWithTrailingSlash).matches()) { + return rp.getLeft(); } } - return false; + return null; } @Override @@ -460,6 +409,11 @@ public void addResource(Module module, String resourceName, InputStream resource registerResource(debugContext, module, resourceName, resourceStream, fromJar); } + @Override + public void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition) { + access.registerReachabilityHandler(e -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourceName), access.findClassByName(condition.getTypeName())); + } + @Override public void addDirectoryResource(Module module, String dir, String content, boolean fromJar) { registerDirectoryResource(debugContext, module, dir, content, fromJar); @@ -474,6 +428,12 @@ public void registerIOException(Module module, String resourceName, IOException public void registerNegativeQuery(Module module, String resourceName) { Resources.singleton().registerNegativeQuery(module, resourceName); } + + // TODO if pass all gates - remove + @Override + public void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition) { + access.registerReachabilityHandler(e -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, dir), access.findClassByName(condition.getTypeName())); + } } private ResourcePattern[] compilePatterns(Set patterns) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java index c863b5c6261b..e7addb2ac95d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java @@ -24,14 +24,16 @@ */ package com.oracle.svm.hosted.jdk.localization; +import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; + import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; -import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; @AutomaticallyRegisteredFeature class CharsetSubstitutionsFeature implements InternalFeature { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - access.registerReachabilityHandler((e) -> RuntimeResourceAccess.addResource(ModuleLayer.boot().findModule("java.base").get(), "java/lang/uniName.dat"), access.findClassByName("java.lang.CharacterName")); + access.registerReachabilityHandler((e) -> RuntimeResourceAccess.addResource(ModuleLayer.boot().findModule("java.base").get(), + "java/lang/uniName.dat"), access.findClassByName("java.lang.CharacterName")); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index 156a2e4aabd7..1c7dc43f37b8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -162,9 +162,6 @@ public class LocalizationFeature implements InternalFeature { private Field localeObjectCacheMapField; private Field langAliasesCacheField; private Field parentLocalesMapField; - - @Platforms(Platform.HOSTED_ONLY.class) private ClassLoader classLoader; - @Platforms(Platform.HOSTED_ONLY.class) private ImageClassLoader imageClassLoader; public static class Options { @@ -298,7 +295,6 @@ public void duringSetup(DuringSetupAccess a) { ServiceLoader.load(ResourceBundleControlProvider.class).stream() .forEach(provider -> ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtBuildTime(provider.type(), reason)); - this.classLoader = access.getApplicationClassLoader(); this.imageClassLoader = access.getImageClassLoader(); } 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 8b6559f55062..5bc549275818 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 @@ -1182,8 +1182,7 @@ final class Target_com_oracle_truffle_api_staticobject_PodBasedStaticShape { @TargetClass(className = "com.oracle.truffle.api.staticobject.ArrayBasedStaticShape", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_api_staticobject_ArrayBasedStaticShape { - @Alias - @RecomputeFieldValue(kind = Kind.Custom, declClass = MapCleaner.class, isFinal = true) // + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = MapCleaner.class, isFinal = true) // static ConcurrentHashMap replacements; private static class MapCleaner implements FieldValueTransformerWithAvailability { @@ -1365,8 +1364,7 @@ final class Target_com_oracle_truffle_polyglot_LanguageCache { * verification in DisallowedImageHeapObjectFeature, so we also do the implicit reset using a * substitution. */ - @Alias - @RecomputeFieldValue(kind = Kind.Reset) // + @Alias @RecomputeFieldValue(kind = Kind.Reset) // private String languageHome; } @@ -1411,15 +1409,13 @@ final class Target_com_oracle_truffle_polyglot_InternalResourceCache_ResettableC @TargetClass(className = "com.oracle.truffle.object.CoreLocations$DynamicObjectFieldLocation", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_object_CoreLocations_DynamicObjectFieldLocation { - @Alias - @RecomputeFieldValue(kind = Kind.AtomicFieldUpdaterOffset) // + @Alias @RecomputeFieldValue(kind = Kind.AtomicFieldUpdaterOffset) // private long offset; } @TargetClass(className = "com.oracle.truffle.object.CoreLocations$DynamicLongFieldLocation", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_object_CoreLocations_DynamicLongFieldLocation { - @Alias - @RecomputeFieldValue(kind = Kind.AtomicFieldUpdaterOffset) // + @Alias @RecomputeFieldValue(kind = Kind.AtomicFieldUpdaterOffset) // private long offset; } @@ -1446,8 +1442,7 @@ final class Target_com_oracle_truffle_api_nodes_Node { @TargetClass(className = "com.oracle.truffle.api.nodes.NodeClassImpl", innerClass = "NodeFieldData", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_api_nodes_NodeClassImpl_NodeFieldData { - @Alias - @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetComputer.class, isFinal = true) // + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetComputer.class, isFinal = true) // private long offset; private static class OffsetComputer implements FieldValueTransformerWithAvailability { @@ -1473,8 +1468,7 @@ public Object transform(Object receiver, Object originalValue) { @TargetClass(className = "com.oracle.truffle.api.dsl.InlineSupport$UnsafeField", onlyWith = TruffleBaseFeature.IsEnabled.class) final class Target_com_oracle_truffle_api_dsl_InlineSupport_UnsafeField { - @Alias - @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetComputer.class, isFinal = true) // + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = OffsetComputer.class, isFinal = true) // private long offset; /* From 7a34bb3b4cbea2e2af5ba17a49deeb8e69d626cc Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 5 Jul 2023 10:00:51 +0200 Subject: [PATCH 18/54] Add conditional resource directory registration test --- substratevm/mx.substratevm/mx_substratevm.py | 10 +++++ .../oracle/svm/core/ClassLoaderSupport.java | 2 +- .../svm/hosted/ClassLoaderSupportImpl.java | 44 ++++++++++++------- .../oracle/svm/hosted/ResourcesFeature.java | 4 +- .../com.oracle.svm.test/resource-config.json | 13 ++++++ .../svm/test/NativeImageResourceTest.java | 31 +++++++++++++ 6 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/resource-config.json diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index a2013afb94e5..ace077d69a27 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -28,6 +28,7 @@ import tempfile from glob import glob from contextlib import contextmanager +from distutils.dir_util import mkpath, remove_tree # pylint: disable=no-name-in-module from os.path import join, exists, dirname import pipes from argparse import ArgumentParser @@ -475,9 +476,18 @@ def native_unittests_task(extra_build_args=None): # GR-24075 mx_unittest.add_global_ignore_glob('com.oracle.svm.test.ProcessPropertiesTest') + # add resources that are not in jar but in the separate directory + cp_entry_name = join(svmbuild_dir(), 'cpEntryDir') + mkpath(cp_entry_name) + for i in range(4): + with open(join(cp_entry_name, "resourcesFromDir", f'cond-resource{i}.txt'), 'w') as out: + out.write(f"Conditional file{i}" + '\n') + + additional_build_args = svm_experimental_options([ '-H:AdditionalSecurityProviders=com.oracle.svm.test.SecurityServiceTest$NoOpProvider', '-H:AdditionalSecurityServiceTypes=com.oracle.svm.test.SecurityServiceTest$JCACompliantNoOpService', + '-cp', cp_entry_name ]) if extra_build_args is not None: additional_build_args += extra_build_args 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 2b5fd7d4f431..a9ec9d579594 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 @@ -66,7 +66,7 @@ public interface ResourceCollector { void registerIOException(Module module, String resourceName, IOException e, boolean linkAtBuildTime); - void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition); + void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition, String content, boolean fromJar); } 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 d1dd62a5e2c1..39165aee4aa3 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 @@ -175,7 +175,8 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso private static void scanDirectory(Path root, ResourceCollector collector, boolean includeAll) throws IOException { Map, List> matchedDirectoryResources = new HashMap<>(); - Set> allEntries = new HashSet<>(); + Map> conditionsForDirectory = new HashMap<>(); + Set allEntries = new HashSet<>(); ArrayDeque queue = new ArrayDeque<>(); queue.push(root); @@ -188,15 +189,19 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea if (entry != root) { relativeFilePath = root.relativize(entry).toString().replace(File.separatorChar, RESOURCES_INTERNAL_PATH_SEPARATOR); condition = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeAll); - allEntries.add(Pair.create(condition, relativeFilePath)); + allEntries.add(relativeFilePath); } else { - relativeFilePath = ""; + relativeFilePath = String.valueOf(RESOURCES_INTERNAL_PATH_SEPARATOR); condition = collector.isIncluded(null, relativeFilePath, Path.of(relativeFilePath).toUri()); } if (Files.isDirectory(entry)) { if (condition != null) { matchedDirectoryResources.put(Pair.create(condition, relativeFilePath), new ArrayList<>()); + // add new condition for the directory (if that is the first condition, create + // list) + conditionsForDirectory.computeIfAbsent(relativeFilePath, k -> new ArrayList<>()); + conditionsForDirectory.get(relativeFilePath).add(condition); } try (Stream pathStream = Files.list(entry)) { Stream filtered = pathStream; @@ -215,7 +220,6 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea } else { collector.addResourceConditionally(null, relativeFilePath, condition); } - collector.addResource(null, relativeFilePath, is, false); } catch (IOException resourceException) { collector.registerIOException(null, relativeFilePath, resourceException, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(relativeFilePath)); } @@ -223,16 +227,21 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea } } - for (Pair entry : allEntries) { - ConfigurationCondition condition = entry.getLeft(); - String entryName = entry.getRight(); - int last = entryName.lastIndexOf(RESOURCES_INTERNAL_PATH_SEPARATOR); - String key = last == -1 ? "" : entryName.substring(0, last); - List dirContent = matchedDirectoryResources.get(Pair.create(condition, key)); - if (dirContent != null && !dirContent.contains(entryName)) { - dirContent.add(entryName.substring(last + 1)); - } else if (dirContent == null) { - collector.registerNegativeQuery(null, key); + for (String entry : allEntries) { + int last = entry.lastIndexOf(RESOURCES_INTERNAL_PATH_SEPARATOR); + String parentDirectory = last == -1 ? "" : entry.substring(0, last); + // one parent can be added under various condition, so we have to add its content for + // all conditions + List conditions = conditionsForDirectory.get(parentDirectory); + if (conditions != null) { + for (ConfigurationCondition condition : conditions) { + List dirContent = matchedDirectoryResources.get(Pair.create(condition, parentDirectory)); + if (dirContent != null && !dirContent.contains(entry)) { + dirContent.add(entry.substring(last + 1)); + } else if (dirContent == null) { + collector.registerNegativeQuery(null, key); + } + } } } @@ -240,10 +249,11 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea ConfigurationCondition condition = entry.getLeft(); String dir = entry.getRight(); content.sort(Comparator.naturalOrder()); + String contentName = String.join(System.lineSeparator(), content); if (ConfigurationCondition.isAlwaysTrue(condition)) { - collector.addDirectoryResource(null, dir, String.join(System.lineSeparator(), content), false); + collector.addDirectoryResource(null, dir, contentName, false); } else { - collector.addDirectoryResourceConditionally(null, dir, condition); + collector.addDirectoryResourceConditionally(null, dir, condition, contentName, false); } }); } @@ -262,7 +272,7 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i if (ConfigurationCondition.isAlwaysTrue(condition)) { collector.addDirectoryResource(null, dirName, "", true); } else { - collector.addDirectoryResourceConditionally(null, dirName, condition); + collector.addDirectoryResourceConditionally(null, dirName, condition, "", true); } } } else { 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 5fb1e7f66b40..73e905f2e847 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 @@ -431,8 +431,8 @@ public void registerNegativeQuery(Module module, String resourceName) { // TODO if pass all gates - remove @Override - public void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition) { - access.registerReachabilityHandler(e -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, dir), access.findClassByName(condition.getTypeName())); + public void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition, String content, boolean fromJar) { + access.registerReachabilityHandler(e -> addDirectoryResource(module, dir, content, fromJar), access.findClassByName(condition.getTypeName())); } } diff --git a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/resource-config.json b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/resource-config.json new file mode 100644 index 000000000000..bdba15d5f51b --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/resource-config.json @@ -0,0 +1,13 @@ +{ + "bundles": [], + "resources": { + "includes": [ + { + "condition": { + "typeReachable": "java.lang.String" + }, + "pattern": "resourcesFromDir/.*" + } + ] + } +} \ No newline at end of file diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java index 2afbe70d49cb..a1373e673186 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java @@ -33,18 +33,22 @@ import static com.oracle.svm.test.NativeImageResourceUtils.compareTwoURLs; import static com.oracle.svm.test.NativeImageResourceUtils.resourceNameToURL; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.StreamSupport; import org.junit.Assert; @@ -126,6 +130,33 @@ public void classGetDirectoryResource() { resourceNameToURL(nonCanonicalResourceDirectoryName, false); } + @Test + public void getConditionalDirectoryResource() throws IOException { + // check if resource is added conditionally + String directoryName = "/resourcesFromDir"; + URL directory = NativeImageResourceUtils.class.getResource(directoryName); + Assert.assertNotNull("Resource " + directory + " is not found!", directory); + + // check content of resource + List expected = IntStream.range(0, 4).mapToObj(i -> "cond-resource" + i + ".txt").toList(); + BufferedReader reader = new BufferedReader(new InputStreamReader(directory.openStream())); + List actual = new ArrayList<>(); + String line; + while ((line = reader.readLine()) != null) { + actual.add(line); + } + + for (String resource : expected) { + // check if resource contains expected content + Assert.assertTrue(actual.contains(resource)); + + // check if we can get resource which directory contains + String resourceName = directoryName + "/" + resource; + URL resourceUrl = NativeImageResourceUtils.class.getResource(resourceName); + Assert.assertNotNull("Cannot find resource: " + resourceName, resourceUrl); + } + } + /** *

* Access a resource using {@link URLClassLoader}. From 8b02c19a2975979fb0bdfd64e6b32536b916a85b Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 12 Jul 2023 18:10:21 +0200 Subject: [PATCH 19/54] Refactor adding content to directory resource --- .../svm/hosted/ClassLoaderSupportImpl.java | 41 ++++++++----------- .../com.oracle.svm.test/resource-config.json | 2 +- .../svm/test/NativeImageResourceTest.java | 6 --- 3 files changed, 17 insertions(+), 32 deletions(-) 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 39165aee4aa3..aa8c8da2351a 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 @@ -173,6 +173,20 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso } } + private static String makeDirContent(Set allEntries, String dir) { + List dirContent = new ArrayList<>(); + for (String entry : allEntries) { + int last = entry.lastIndexOf(RESOURCES_INTERNAL_PATH_SEPARATOR); + String parentDirectory = last == -1 ? "" : entry.substring(0, last); + if (parentDirectory.equals(dir)) { + dirContent.add(entry.substring(last + 1)); + } + } + + dirContent.sort(Comparator.naturalOrder()); + return String.join(System.lineSeparator(), dirContent); + } + private static void scanDirectory(Path root, ResourceCollector collector, boolean includeAll) throws IOException { Map, List> matchedDirectoryResources = new HashMap<>(); Map> conditionsForDirectory = new HashMap<>(); @@ -198,10 +212,6 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea if (Files.isDirectory(entry)) { if (condition != null) { matchedDirectoryResources.put(Pair.create(condition, relativeFilePath), new ArrayList<>()); - // add new condition for the directory (if that is the first condition, create - // list) - conditionsForDirectory.computeIfAbsent(relativeFilePath, k -> new ArrayList<>()); - conditionsForDirectory.get(relativeFilePath).add(condition); } try (Stream pathStream = Files.list(entry)) { Stream filtered = pathStream; @@ -227,29 +237,10 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea } } - for (String entry : allEntries) { - int last = entry.lastIndexOf(RESOURCES_INTERNAL_PATH_SEPARATOR); - String parentDirectory = last == -1 ? "" : entry.substring(0, last); - // one parent can be added under various condition, so we have to add its content for - // all conditions - List conditions = conditionsForDirectory.get(parentDirectory); - if (conditions != null) { - for (ConfigurationCondition condition : conditions) { - List dirContent = matchedDirectoryResources.get(Pair.create(condition, parentDirectory)); - if (dirContent != null && !dirContent.contains(entry)) { - dirContent.add(entry.substring(last + 1)); - } else if (dirContent == null) { - collector.registerNegativeQuery(null, key); - } - } - } - } - - matchedDirectoryResources.forEach((entry, content) -> { + matchedDirectoryResources.forEach(entry -> { ConfigurationCondition condition = entry.getLeft(); String dir = entry.getRight(); - content.sort(Comparator.naturalOrder()); - String contentName = String.join(System.lineSeparator(), content); + String contentName = makeDirContent(allEntries, dir); if (ConfigurationCondition.isAlwaysTrue(condition)) { collector.addDirectoryResource(null, dir, contentName, false); } else { diff --git a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/resource-config.json b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/resource-config.json index bdba15d5f51b..16146de3e580 100644 --- a/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/resource-config.json +++ b/substratevm/src/com.oracle.svm.test/src/META-INF/native-image/com.oracle.svm.test/resource-config.json @@ -6,7 +6,7 @@ "condition": { "typeReachable": "java.lang.String" }, - "pattern": "resourcesFromDir/.*" + "pattern": "resourcesFromDir" } ] } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java index a1373e673186..4f1158d86dde 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java @@ -147,13 +147,7 @@ public void getConditionalDirectoryResource() throws IOException { } for (String resource : expected) { - // check if resource contains expected content Assert.assertTrue(actual.contains(resource)); - - // check if we can get resource which directory contains - String resourceName = directoryName + "/" + resource; - URL resourceUrl = NativeImageResourceUtils.class.getResource(resourceName); - Assert.assertNotNull("Cannot find resource: " + resourceName, resourceUrl); } } From f2a4fbcdf26b23393ee03b869e2ac9a7ba303a31 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 18 Jul 2023 13:52:48 +0200 Subject: [PATCH 20/54] Replace pairs with records to get more semantic --- substratevm/mx.substratevm/mx_substratevm.py | 20 +++-- .../com/oracle/svm/core/jdk/Resources.java | 6 +- .../svm/hosted/ClassLoaderSupportImpl.java | 26 ++++--- .../oracle/svm/hosted/ResourcesFeature.java | 76 +++++++++++++------ .../svm/test/NativeImageResourceTest.java | 12 ++- .../svm/test/NativeImageResourceUtils.java | 2 + 6 files changed, 96 insertions(+), 46 deletions(-) diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index ace077d69a27..8159e089890b 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -476,13 +476,21 @@ def native_unittests_task(extra_build_args=None): # GR-24075 mx_unittest.add_global_ignore_glob('com.oracle.svm.test.ProcessPropertiesTest') - # add resources that are not in jar but in the separate directory - cp_entry_name = join(svmbuild_dir(), 'cpEntryDir') - mkpath(cp_entry_name) - for i in range(4): - with open(join(cp_entry_name, "resourcesFromDir", f'cond-resource{i}.txt'), 'w') as out: - out.write(f"Conditional file{i}" + '\n') + # add resources that are not in jar but in the separate directory + cp_entry_name = join(svmbuild_dir(), 'cpEntryDir') + resources_from_dir = join(cp_entry_name, 'resourcesFromDir') + simple_dir = join(cp_entry_name, 'simpleDir') + mkpath(cp_entry_name) + mkpath(resources_from_dir) + mkpath(simple_dir) + + for i in range(4): + with open(join(cp_entry_name, "resourcesFromDir", f'cond-resource{i}.txt'), 'w') as out: + out.write(f"Conditional file{i}" + '\n') + + with open(join(cp_entry_name, "simpleDir", f'simple-resource{i}.txt'), 'w') as out: + out.write(f"Simple file{i}" + '\n') additional_build_args = svm_experimental_options([ '-H:AdditionalSecurityProviders=com.oracle.svm.test.SecurityServiceTest$NoOpProvider', 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 85e2c519860b..9e8d191b5b2b 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 @@ -48,6 +48,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.MissingRegistrationUtils; import com.oracle.svm.core.SubstrateOptions; @@ -214,11 +215,6 @@ public void registerResource(Module module, String resourceName, InputStream is, addEntry(module, resourceName, false, inputStreamToByteArray(is), fromJar); } - @Platforms(Platform.HOSTED_ONLY.class) - public static void registerResource(Module module, String resourceName, boolean isDir, InputStream is, boolean fromJar) { - addEntry(module, resourceName, isDir, inputStreamToByteArray(is), fromJar); - } - @Platforms(Platform.HOSTED_ONLY.class) public void registerDirectoryResource(String resourceDirName, String content) { registerDirectoryResource(null, resourceDirName, content, true); 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 aa8c8da2351a..b81ee9b691d2 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 @@ -55,7 +55,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.graalvm.collections.Pair; import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.ClassLoaderSupport; @@ -74,6 +73,9 @@ public class ClassLoaderSupportImpl extends ClassLoaderSupport { private final Map> packageToModules; + private record ConditionalResource(ConfigurationCondition condition, String resourceName) { + } + public ClassLoaderSupportImpl(NativeImageClassLoaderSupport classLoaderSupport) { this.classLoaderSupport = classLoaderSupport; imageClassLoader = classLoaderSupport.getClassLoader(); @@ -132,17 +134,17 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso ModuleReference moduleReference = info.resolvedModule.reference(); try (ModuleReader moduleReader = moduleReference.open()) { var includeAll = classLoaderSupport.getJavaModuleNamesToInclude().contains(info.resolvedModule().name()); - List> resourcesFound = new ArrayList<>(); + List resourcesFound = new ArrayList<>(); moduleReader.list().forEach(resourceName -> { ConfigurationCondition condition = shouldIncludeEntry(info.module(), resourceCollector, resourceName, moduleReference.location().orElse(null), includeAll); if (condition != null) { - resourcesFound.add(Pair.create(condition, resourceName)); + resourcesFound.add(new ConditionalResource(condition, resourceName)); } }); - for (Pair entry : resourcesFound) { - ConfigurationCondition condition = entry.getLeft(); - String resName = entry.getRight(); + for (ConditionalResource entry : resourcesFound) { + ConfigurationCondition condition = entry.condition(); + String resName = entry.resourceName(); if (resName.endsWith("/")) { if (ConfigurationCondition.isAlwaysTrue(condition)) { resourceCollector.addDirectoryResource(info.module, resName, "", false); @@ -151,6 +153,7 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso } continue; } + Optional content = moduleReader.open(resName); if (content.isEmpty()) { /* This is to be resilient, but the resources returned by list() should exist */ @@ -188,9 +191,8 @@ private static String makeDirContent(Set allEntries, String dir) { } private static void scanDirectory(Path root, ResourceCollector collector, boolean includeAll) throws IOException { - Map, List> matchedDirectoryResources = new HashMap<>(); - Map> conditionsForDirectory = new HashMap<>(); - Set allEntries = new HashSet<>(); + List matchedDirectoryResources = new ArrayList<>(); + Set allEntries = new HashSet<>(); ArrayDeque queue = new ArrayDeque<>(); queue.push(root); @@ -211,7 +213,7 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea if (Files.isDirectory(entry)) { if (condition != null) { - matchedDirectoryResources.put(Pair.create(condition, relativeFilePath), new ArrayList<>()); + matchedDirectoryResources.add(new ConditionalResource(condition, relativeFilePath)); } try (Stream pathStream = Files.list(entry)) { Stream filtered = pathStream; @@ -238,8 +240,8 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea } matchedDirectoryResources.forEach(entry -> { - ConfigurationCondition condition = entry.getLeft(); - String dir = entry.getRight(); + ConfigurationCondition condition = entry.condition(); + String dir = entry.resourceName(); String contentName = makeDirContent(allEntries, dir); if (ConfigurationCondition.isAlwaysTrue(condition)) { collector.addDirectoryResource(null, dir, contentName, false); 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 73e905f2e847..836994221bd1 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 @@ -27,11 +27,13 @@ import static com.oracle.svm.core.jdk.Resources.RESOURCES_INTERNAL_PATH_SEPARATOR; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.net.JarURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -42,6 +44,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Set; @@ -53,6 +56,7 @@ import java.util.jar.JarFile; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; import jdk.graal.compiler.debug.DebugContext; @@ -140,7 +144,13 @@ public static class Options { private boolean sealed = false; - private Set> resourcePatternWorkSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private record ConditionalPattern(ConfigurationCondition condition, String pattern) { + } + + private record CompiledConditionalPattern(ConfigurationCondition condition, ResourcePattern compiledPattern) { + } + + private Set resourcePatternWorkSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final Set excludedResourcePatterns = Collections.newSetFromMap(new ConcurrentHashMap<>()); private int loadedConfigurations; private ImageClassLoader imageClassLoader; @@ -159,14 +169,18 @@ public void addResources(ConfigurationCondition condition, String pattern) { } try { - resourcePatternWorkSet.add(Pair.create(condition, pattern)); + resourcePatternWorkSet.add(new ConditionalPattern(condition, pattern)); } catch (UnsupportedOperationException e) { UserError.abort("Resource registration should be performed before beforeAnalysis phase."); } } private String urlToJarPath(URL url) { - return String.valueOf(url).split("jar:file:")[1].split("!")[0]; + try { + return ((JarURLConnection) url.openConnection()).getJarFileURL().getFile(); + } catch (IOException e) { + throw new RuntimeException(e); + } } private boolean resourceIsDirectory(URL url, boolean fromJar, String resourcePath) throws IOException, URISyntaxException { @@ -179,11 +193,20 @@ private boolean resourceIsDirectory(URL url, boolean fromJar, String resourcePat } } + private String getDirectoryContent(String dir) throws IOException { + try (Stream contentStream = Files.list(Path.of(dir))) { + List content = new ArrayList<>(contentStream.map(Path::toString).toList()); + content.sort(Comparator.naturalOrder()); + return String.join(System.lineSeparator(), content); + } + } + @Override public void addResource(Module module, String resourcePath) { InputStream is; - boolean fromJar = false; - boolean isDirectory = false; + boolean fromJar; + boolean isDirectory; + String content = ""; if (module != null && module.isNamed()) { try { @@ -196,6 +219,11 @@ public void addResource(Module module, String resourcePath) { } is = module.getResourceAsStream(resourcePath); + fromJar = false; + isDirectory = new File(resourcePath).isDirectory(); + if (isDirectory) { + content = getDirectoryContent(resourcePath); + } } catch (IOException e) { // we should ignore if user failed to provide resource return; @@ -207,28 +235,32 @@ public void addResource(Module module, String resourcePath) { return; } - fromJar = url.getProtocol().equalsIgnoreCase("jar"); try { + is = url.openStream(); + fromJar = url.getProtocol().equalsIgnoreCase("jar"); isDirectory = resourceIsDirectory(url, fromJar, resourcePath); + // if directory is from jar content should remain empty (same as in scanJar + // function from ClassLoaderSupportImpl) + if (isDirectory && !fromJar) { + content = getDirectoryContent(url.getPath()); + } } catch (IOException e) { // we should ignore if user failed to provide resource return; } catch (URISyntaxException e) { throw new RuntimeException(e); } - - try { - is = url.openStream(); - } catch (IOException e) { - throw new RuntimeException(e); - } } if (is == null) { return; } - Resources.registerResource(module, resourcePath, isDirectory, is, fromJar); + if (isDirectory) { + Resources.registerDirectoryResource(module, resourcePath, content); + } else { + Resources.registerResource(module, resourcePath, is, fromJar); + } } @Override @@ -295,15 +327,15 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { resourcePatternWorkSet.addAll(Options.IncludeResources.getValue() .values() .stream() - .map(e -> Pair.create(ConfigurationCondition.alwaysTrue(), e)) + .map(e -> new ConditionalPattern(ConfigurationCondition.alwaysTrue(), e)) .toList()); excludedResourcePatterns.addAll(Options.ExcludeResources.getValue().values()); if (!resourcePatternWorkSet.isEmpty()) { FeatureImpl.BeforeAnalysisAccessImpl beforeAnalysisAccess = (FeatureImpl.BeforeAnalysisAccessImpl) access; - Set> includePatterns = resourcePatternWorkSet + Set includePatterns = resourcePatternWorkSet .stream() - .map(e -> Pair.create(e.getLeft(), makeResourcePattern(e.getRight()))) + .map(e -> new CompiledConditionalPattern(e.condition(), makeResourcePattern(e.pattern()))) .collect(Collectors.toSet()); if (MissingRegistrationUtils.throwMissingRegistrationErrors()) { for (ResourcePattern resourcePattern : includePatterns) { @@ -330,7 +362,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { private static final class ResourceCollectorImpl implements ResourceCollector { private final DebugContext debugContext; - private final Set> includePatterns; + private final Set includePatterns; private final ResourcePattern[] excludePatterns; private static final int WATCHDOG_RESET_AFTER_EVERY_N_RESOURCES = 1000; @@ -341,7 +373,7 @@ private static final class ResourceCollectorImpl implements ResourceCollector { private volatile String currentlyProcessedEntry; ScheduledExecutorService scheduledExecutor; - private ResourceCollectorImpl(DebugContext debugContext, ResourcePattern[] includePatterns, ResourcePattern[] excludePatterns) { + private ResourceCollectorImpl(DebugContext debugContext, Set includePatterns, ResourcePattern[] excludePatterns) { this.debugContext = debugContext; this.includePatterns = includePatterns; this.excludePatterns = excludePatterns; @@ -392,12 +424,12 @@ public ConfigurationCondition isIncluded(Module module, String resourceName, URI } } - for (Pair rp : includePatterns) { - if (!rp.getRight().moduleNameMatches(moduleName)) { + for (CompiledConditionalPattern rp : includePatterns) { + if (!rp.compiledPattern().moduleNameMatches(moduleName)) { continue; } - if (rp.getRight().pattern.matcher(resourceName).matches() || rp.getRight().pattern.matcher(relativePathWithTrailingSlash).matches()) { - return rp.getLeft(); + if (rp.compiledPattern().pattern.matcher(resourceName).matches() || rp.compiledPattern().pattern.matcher(relativePathWithTrailingSlash).matches()) { + return rp.condition(); } } diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java index 4f1158d86dde..a4fe9af3cfbf 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceTest.java @@ -30,6 +30,7 @@ import static com.oracle.svm.test.NativeImageResourceUtils.RESOURCE_FILE_2; import static com.oracle.svm.test.NativeImageResourceUtils.RESOURCE_FILE_3; import static com.oracle.svm.test.NativeImageResourceUtils.RESOURCE_FILE_4; +import static com.oracle.svm.test.NativeImageResourceUtils.SIMPLE_RESOURCE_DIR; import static com.oracle.svm.test.NativeImageResourceUtils.compareTwoURLs; import static com.oracle.svm.test.NativeImageResourceUtils.resourceNameToURL; @@ -130,12 +131,21 @@ public void classGetDirectoryResource() { resourceNameToURL(nonCanonicalResourceDirectoryName, false); } + @Test + public void registeredResourceDirectoryHasContent() throws IOException { + URL directory = NativeImageResourceUtils.class.getResource(SIMPLE_RESOURCE_DIR); + Assert.assertNotNull("Resource " + SIMPLE_RESOURCE_DIR + " is not found!", directory); + + BufferedReader reader = new BufferedReader(new InputStreamReader(directory.openStream())); + Assert.assertNotNull("Resource" + SIMPLE_RESOURCE_DIR + " should have content", reader.readLine()); + } + @Test public void getConditionalDirectoryResource() throws IOException { // check if resource is added conditionally String directoryName = "/resourcesFromDir"; URL directory = NativeImageResourceUtils.class.getResource(directoryName); - Assert.assertNotNull("Resource " + directory + " is not found!", directory); + Assert.assertNotNull("Resource " + directoryName + " is not found!", directory); // check content of resource List expected = IntStream.range(0, 4).mapToObj(i -> "cond-resource" + i + ".txt").toList(); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceUtils.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceUtils.java index 8d07f56f7f91..7cd52b889c7a 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceUtils.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceUtils.java @@ -42,6 +42,7 @@ public class NativeImageResourceUtils { public static final String ROOT_DIRECTORY = "/"; public static final String RESOURCE_DIR = "/resources"; + public static final String SIMPLE_RESOURCE_DIR = "/simpleDir"; public static final String RESOURCE_EMPTY_DIR = RESOURCE_DIR + "/empty"; public static final String RESOURCE_FILE_1 = RESOURCE_DIR + "/resource-test1.txt"; public static final String RESOURCE_FILE_2 = RESOURCE_DIR + "/resource-test2.txt"; @@ -55,6 +56,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { // Remove leading / for the resource patterns Module resourceModule = TestFeature.class.getModule(); RuntimeResourceAccess.addResource(resourceModule, RESOURCE_DIR.substring(1)); + RuntimeResourceAccess.addResource(resourceModule, SIMPLE_RESOURCE_DIR.substring(1)); RuntimeResourceAccess.addResource(resourceModule, RESOURCE_EMPTY_DIR.substring(1)); RuntimeResourceAccess.addResource(resourceModule, RESOURCE_FILE_1.substring(1)); RuntimeResourceAccess.addResource(resourceModule, RESOURCE_FILE_2.substring(1)); From f49fe93daa6c6457fc9e6b68458a30e75095600f Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 19 Jul 2023 17:02:28 +0200 Subject: [PATCH 21/54] Get content for directories inside jar --- .../oracle/svm/hosted/ResourcesFeature.java | 68 ++++++++++++++++--- 1 file changed, 60 insertions(+), 8 deletions(-) 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 836994221bd1..aa25d248caf8 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 @@ -38,13 +38,16 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.Set; @@ -53,6 +56,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; +import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -193,12 +197,54 @@ private boolean resourceIsDirectory(URL url, boolean fromJar, String resourcePat } } - private String getDirectoryContent(String dir) throws IOException { - try (Stream contentStream = Files.list(Path.of(dir))) { - List content = new ArrayList<>(contentStream.map(Path::toString).toList()); - content.sort(Comparator.naturalOrder()); - return String.join(System.lineSeparator(), content); + private String getDirectoryContent(String path, boolean fromJar) throws IOException { + List content = new ArrayList<>(); + if (fromJar) { + try (JarFile jf = new JarFile(urlToJarPath(URI.create(path).toURL()))) { + String pathSeparator = FileSystems.getDefault().getSeparator(); + String directoryPath = path.split("!")[1]; + + // we are removing leading slash because jar entry names don't start with slash + if (directoryPath.startsWith(pathSeparator)) { + directoryPath = directoryPath.substring(1); + } + + Enumeration entries = jf.entries(); + while (entries.hasMoreElements()) { + String entry = entries.nextElement().getName(); + if (entry.startsWith(directoryPath)) { + String contentEntry = entry.substring(directoryPath.length()); + + // remove the leading slash + if (contentEntry.startsWith(pathSeparator)) { + contentEntry = contentEntry.substring(1); + } + + // prevent adding empty strings as a content + if (!contentEntry.isEmpty()) { + // get top level content only + int firstSlash = contentEntry.indexOf(pathSeparator); + if (firstSlash != -1) { + content.add(contentEntry.substring(0, firstSlash)); + } else { + content.add(contentEntry); + } + } + } + } + + } + } else { + try (Stream contentStream = Files.list(Path.of(path))) { + content = new ArrayList<>(contentStream + .map(Path::getFileName) + .map(Path::toString) + .toList()); + } } + + content.sort(Comparator.naturalOrder()); + return String.join(System.lineSeparator(), content); } @Override @@ -222,7 +268,7 @@ public void addResource(Module module, String resourcePath) { fromJar = false; isDirectory = new File(resourcePath).isDirectory(); if (isDirectory) { - content = getDirectoryContent(resourcePath); + content = getDirectoryContent(resourcePath, false); } } catch (IOException e) { // we should ignore if user failed to provide resource @@ -241,8 +287,8 @@ public void addResource(Module module, String resourcePath) { isDirectory = resourceIsDirectory(url, fromJar, resourcePath); // if directory is from jar content should remain empty (same as in scanJar // function from ClassLoaderSupportImpl) - if (isDirectory && !fromJar) { - content = getDirectoryContent(url.getPath()); + if (isDirectory) { + content = getDirectoryContent(fromJar ? url.toString() : Paths.get(url.toURI()).toString(), fromJar); } } catch (IOException e) { // we should ignore if user failed to provide resource @@ -261,6 +307,12 @@ public void addResource(Module module, String resourcePath) { } else { Resources.registerResource(module, resourcePath, is, fromJar); } + + try { + is.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override From a5ca553276c0ed00c8de1260499b54e717c51bab Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 28 Jul 2023 17:01:47 +0200 Subject: [PATCH 22/54] Add possiblity to register resource for various conditions --- .../oracle/svm/core/ClassLoaderSupport.java | 2 +- .../svm/hosted/ClassLoaderSupportImpl.java | 31 ++++++++++--------- .../oracle/svm/hosted/ResourcesFeature.java | 10 +++--- 3 files changed, 23 insertions(+), 20 deletions(-) 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 a9ec9d579594..85851ab218b3 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 @@ -54,7 +54,7 @@ public boolean isNativeImageClassLoader(ClassLoader classLoader) { protected abstract boolean isNativeImageClassLoaderImpl(ClassLoader classLoader); public interface ResourceCollector { - ConfigurationCondition isIncluded(Module module, String resourceName, URI resourceURI); + List isIncluded(Module module, String resourceName, URI resourceURI); void addResource(Module module, String resourceName, InputStream resourceStream, boolean fromJar); 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 b81ee9b691d2..fc8c85f7a941 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 @@ -136,8 +136,8 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso var includeAll = classLoaderSupport.getJavaModuleNamesToInclude().contains(info.resolvedModule().name()); List resourcesFound = new ArrayList<>(); moduleReader.list().forEach(resourceName -> { - ConfigurationCondition condition = shouldIncludeEntry(info.module(), resourceCollector, resourceName, moduleReference.location().orElse(null), includeAll); - if (condition != null) { + List conditions = shouldIncludeEntry(info.module, resourceCollector, resourceName, moduleReference.location().orElse(null), includeAll); + for (ConfigurationCondition condition : conditions) { resourcesFound.add(new ConditionalResource(condition, resourceName)); } }); @@ -192,7 +192,7 @@ private static String makeDirContent(Set allEntries, String dir) { private static void scanDirectory(Path root, ResourceCollector collector, boolean includeAll) throws IOException { List matchedDirectoryResources = new ArrayList<>(); - Set allEntries = new HashSet<>(); + Set allEntries = new HashSet<>(); ArrayDeque queue = new ArrayDeque<>(); queue.push(root); @@ -201,20 +201,21 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea /* Resources always use / as the separator, as do our resource inclusion patterns */ String relativeFilePath; - ConfigurationCondition condition; + List conditions; if (entry != root) { relativeFilePath = root.relativize(entry).toString().replace(File.separatorChar, RESOURCES_INTERNAL_PATH_SEPARATOR); - condition = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeAll); + conditions = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeAll); allEntries.add(relativeFilePath); } else { relativeFilePath = String.valueOf(RESOURCES_INTERNAL_PATH_SEPARATOR); - condition = collector.isIncluded(null, relativeFilePath, Path.of(relativeFilePath).toUri()); + conditions = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeAll); } if (Files.isDirectory(entry)) { - if (condition != null) { + for (ConfigurationCondition condition : conditions) { matchedDirectoryResources.add(new ConditionalResource(condition, relativeFilePath)); } + try (Stream pathStream = Files.list(entry)) { Stream filtered = pathStream; if (ClassUtil.CLASS_MODULE_PATH_EXCLUDE_DIRECTORIES_ROOT.equals(entry)) { @@ -225,7 +226,7 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea collector.registerIOException(null, relativeFilePath, resourceException, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(relativeFilePath)); } } else { - if (condition != null) { + for (ConfigurationCondition condition : conditions) { try (InputStream is = Files.newInputStream(entry)) { if (ConfigurationCondition.isAlwaysTrue(condition)) { collector.addResource(null, relativeFilePath, is, false); @@ -259,8 +260,8 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i URI uri = jarPath.toUri(); if (entry.isDirectory()) { String dirName = entry.getName().substring(0, entry.getName().length() - 1); - ConfigurationCondition condition = shouldIncludeEntry(null, collector, dirName, jarPath.toUri(), includeAll); - if (condition != null) { + List conditions = shouldIncludeEntry(null, collector, dirName, jarPath.toUri(), includeAll); + for (ConfigurationCondition condition : conditions) { // Register the directory with empty content to preserve Java behavior if (ConfigurationCondition.isAlwaysTrue(condition)) { collector.addDirectoryResource(null, dirName, "", true); @@ -269,8 +270,8 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i } } } else { - ConfigurationCondition condition = shouldIncludeEntry(null, collector, entry.getName(), jarPath.toUri(), includeAll); - if (condition != null) { + List conditions = shouldIncludeEntry(null, collector, entry.getName(), jarPath.toUri(), includeAll); + for (ConfigurationCondition condition : conditions) { try (InputStream is = jf.getInputStream(entry)) { if (ConfigurationCondition.isAlwaysTrue(condition)) { collector.addResource(null, entry.getName(), is, true); @@ -286,10 +287,10 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i } } - private static ConfigurationCondition shouldIncludeEntry(Module module, ResourceCollector collector, String fileName, URI uri, boolean includeAll) { - ConfigurationCondition conditions = collector.isIncluded(module, fileName, uri); + private static List shouldIncludeEntry(Module module, ResourceCollector collector, String fileName, URI uri, boolean includeAll) { + List conditions = collector.isIncluded(module, fileName, uri); if (includeAll && !(fileName.endsWith(".class") || fileName.endsWith(".jar"))) { - return ConfigurationCondition.alwaysTrue(); + return Collections.singletonList(ConfigurationCondition.alwaysTrue()); } return conditions; 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 aa25d248caf8..2fb02cfeb051 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 @@ -456,7 +456,9 @@ private void shutDownProgressReporter() { } @Override - public ConfigurationCondition isIncluded(Module module, String resourceName, URI resource) { + public List isIncluded(Module module, String resourceName, URI resource) { + // Possibly we can have multiple conditions for one resource + List conditions = new ArrayList<>(); this.currentlyProcessedEntry = resource.getScheme().equals("jrt") ? (resource + "/" + resourceName) : resource.toString(); this.reachedResourceEntries.increment(); @@ -472,7 +474,7 @@ public ConfigurationCondition isIncluded(Module module, String resourceName, URI continue; } if (rp.pattern.matcher(resourceName).matches() || rp.pattern.matcher(relativePathWithTrailingSlash).matches()) { - return null; + return conditions; // returns empty list } } @@ -481,11 +483,11 @@ public ConfigurationCondition isIncluded(Module module, String resourceName, URI continue; } if (rp.compiledPattern().pattern.matcher(resourceName).matches() || rp.compiledPattern().pattern.matcher(relativePathWithTrailingSlash).matches()) { - return rp.condition(); + conditions.add(rp.condition()); } } - return null; + return conditions; } @Override From 1f28251c66df1502b23146bc329d31e1ac974319 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 1 Aug 2023 17:23:52 +0200 Subject: [PATCH 23/54] Prevent registration of same resources multiple times --- .../com/oracle/svm/core/jdk/Resources.java | 18 ++++++++------ .../oracle/svm/hosted/ResourcesFeature.java | 24 +++++++++++++++++-- 2 files changed, 33 insertions(+), 9 deletions(-) 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 9e8d191b5b2b..2165af201c39 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 @@ -167,12 +167,14 @@ private void updateTimeStamp() { } @Platforms(Platform.HOSTED_ONLY.class) - private ResourceStorageEntryBase addEntry(Module module, String resourceName, ResourceStorageEntryBase newEntry, boolean isDirectory, boolean fromJar) { + private static void addEntry(Module module, String resourceName, boolean isDirectory, byte[] data, boolean fromJar) { + VMError.guarantee(!BuildPhaseProvider.isAnalysisFinished(), "Trying to add a resource entry after analysis."); Module m = module != null && module.isNamed() ? module : null; if (m != null) { m = RuntimeModuleSupport.instance().getRuntimeModuleForHostedModule(m); } + var resources = singleton().resources; synchronized (resources) { Pair key = createStorageKey(m, resourceName); ResourceStorageEntryBase entry = resources.get(key); @@ -180,14 +182,16 @@ private ResourceStorageEntryBase addEntry(Module module, String resourceName, Re entry = newEntry == null ? new ResourceStorageEntry(isDirectory, fromJar) : newEntry; updateTimeStamp(); resources.put(key, entry); + } else { + if (key.getLeft() != null) { + // if the entry already exists, and it comes from a module, it is the same entry + // that we registered at some point before + return; + } } - return entry; - } - } - private void addEntry(Module module, String resourceName, boolean isDirectory, byte[] data, boolean fromJar) { - ResourceStorageEntryBase entry = addEntry(module, resourceName, null, isDirectory, fromJar); - entry.getData().add(data); + entry.getData().add(data); + } } @Platforms(Platform.HOSTED_ONLY.class) 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 2fb02cfeb051..7ab2dffd6dc9 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 @@ -48,6 +48,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; @@ -490,6 +491,25 @@ public List isIncluded(Module module, String resourceNam return conditions; } + private final Set alreadyAddedResources = new HashSet<>(); + + private void registerResourceIfNeeded(boolean isDirectory, Object... arguments) { + Module module = (Module) arguments[0]; + String resourceName = (String) arguments[1]; + // we only do this if we are on the classPath + if ((module == null || !module.isNamed()) && !alreadyAddedResources.contains(resourceName)) { + if (isDirectory) { + String content = (String) arguments[2]; + boolean fromJar = (boolean) arguments[3]; + addDirectoryResource(module, resourceName, content, fromJar); + } else { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourceName); + } + + alreadyAddedResources.add(resourceName); + } + } + @Override public void addResource(Module module, String resourceName, InputStream resourceStream, boolean fromJar) { registerResource(debugContext, module, resourceName, resourceStream, fromJar); @@ -497,7 +517,7 @@ public void addResource(Module module, String resourceName, InputStream resource @Override public void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition) { - access.registerReachabilityHandler(e -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourceName), access.findClassByName(condition.getTypeName())); + access.registerReachabilityHandler(e -> registerResourceIfNeeded(false, module, resourceName), access.findClassByName(condition.getTypeName())); } @Override @@ -518,7 +538,7 @@ public void registerNegativeQuery(Module module, String resourceName) { // TODO if pass all gates - remove @Override public void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition, String content, boolean fromJar) { - access.registerReachabilityHandler(e -> addDirectoryResource(module, dir, content, fromJar), access.findClassByName(condition.getTypeName())); + access.registerReachabilityHandler(e -> registerResourceIfNeeded(true, module, dir, content, fromJar), access.findClassByName(condition.getTypeName())); } } From 130eb2ae0330daffd9d6f02d337b9cb88abc2488 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 11 Aug 2023 15:18:53 +0200 Subject: [PATCH 24/54] Resolve conflicts --- .../com/oracle/svm/core/jdk/Resources.java | 22 +++++++++++-------- ...ContentSubstitutedLocalizationSupport.java | 9 +++++--- .../jdk/localization/LocalizationSupport.java | 5 +++-- .../oracle/svm/hosted/ResourcesFeature.java | 13 ++++++----- 4 files changed, 29 insertions(+), 20 deletions(-) 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 2165af201c39..662acbcb47cb 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 @@ -48,7 +48,6 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.MissingRegistrationUtils; import com.oracle.svm.core.SubstrateOptions; @@ -167,20 +166,25 @@ private void updateTimeStamp() { } @Platforms(Platform.HOSTED_ONLY.class) - private static void addEntry(Module module, String resourceName, boolean isDirectory, byte[] data, boolean fromJar) { - + private void addEntry(Module module, String resourceName, boolean isDirectory, byte[] data, boolean fromJar, boolean isNegativeQuery) { VMError.guarantee(!BuildPhaseProvider.isAnalysisFinished(), "Trying to add a resource entry after analysis."); Module m = module != null && module.isNamed() ? module : null; if (m != null) { m = RuntimeModuleSupport.instance().getRuntimeModuleForHostedModule(m); } - var resources = singleton().resources; synchronized (resources) { Pair key = createStorageKey(m, resourceName); ResourceStorageEntryBase entry = resources.get(key); + if (isNegativeQuery) { + if (entry == null) { + resources.put(key, NEGATIVE_QUERY_MARKER); + } + return; + } + if (entry == null || entry == NEGATIVE_QUERY_MARKER) { - entry = newEntry == null ? new ResourceStorageEntry(isDirectory, fromJar) : newEntry; updateTimeStamp(); + entry = new ResourceStorageEntry(isDirectory, fromJar); resources.put(key, entry); } else { if (key.getLeft() != null) { @@ -211,12 +215,12 @@ public void registerResource(Module module, String resourceName, InputStream is) @Platforms(Platform.HOSTED_ONLY.class) public void registerResource(Module module, String resourceName, byte[] resourceContent) { - addEntry(module, resourceName, false, resourceContent, true); + addEntry(module, resourceName, false, resourceContent, true, false); } @Platforms(Platform.HOSTED_ONLY.class) public void registerResource(Module module, String resourceName, InputStream is, boolean fromJar) { - addEntry(module, resourceName, false, inputStreamToByteArray(is), fromJar); + addEntry(module, resourceName, false, inputStreamToByteArray(is), fromJar, false); } @Platforms(Platform.HOSTED_ONLY.class) @@ -241,7 +245,7 @@ public void registerDirectoryResource(Module module, String resourceDirName, Str * specified directory, separated with new line delimiter and joined into one string which * is later converted into a byte array and placed into the resources map. */ - addEntry(module, resourceDirName, true, content.getBytes(), fromJar); + addEntry(module, resourceDirName, true, content.getBytes(), fromJar, false); } @Platforms(Platform.HOSTED_ONLY.class) @@ -272,7 +276,7 @@ public void registerNegativeQuery(String resourceName) { @Platforms(Platform.HOSTED_ONLY.class) public void registerNegativeQuery(Module module, String resourceName) { - addEntry(module, resourceName, NEGATIVE_QUERY_MARKER, false, false); + addEntry(module, resourceName, false, null, false, true); } @Platforms(Platform.HOSTED_ONLY.class) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java index 9f918232531e..a43d7f15d982 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java @@ -30,10 +30,12 @@ import java.util.ListResourceBundle; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ForkJoinPool; +import java.util.function.Function; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -172,11 +174,11 @@ public boolean isNotIncluded(String bundleName) { } @Override - public void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale) { - super.prepareBundle(bundleName, bundle, locale); + public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule) { + super.prepareBundle(bundleName, bundle, null); /* Initialize ResourceBundle.keySet eagerly */ bundle.keySet(); - this.existingBundles.add(control.toBundleName(bundleName, locale)); + this.existingBundles.add(control.toBundleName(bundleName, bundle.getLocale())); } @Override @@ -184,4 +186,5 @@ public void prepareClassResourceBundle(String basename, Class bundleClass) { super.prepareClassResourceBundle(basename, bundleClass); this.existingBundles.add(bundleClass.getName()); } + } 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 e74248dc1ce1..1b905050e403 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 @@ -27,6 +27,7 @@ import java.nio.charset.Charset; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.IllformedLocaleException; import java.util.Locale; @@ -112,8 +113,8 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function> packageToModules = ImageSingletons.lookup(ClassLoaderSupport.class).getPackageToModules(); Set modules = packageToModules.getOrDefault(packageName(bundleName), Collections.emptySet()); - // there should be only one module but we will check all modules where given package - // is found + // there should be only one module, but we will check all modules where given + // package is found for (Module m : modules) { ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName); } 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 7ab2dffd6dc9..3a690dae798b 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 @@ -304,9 +304,9 @@ public void addResource(Module module, String resourcePath) { } if (isDirectory) { - Resources.registerDirectoryResource(module, resourcePath, content); + Resources.singleton().registerDirectoryResource(module, resourcePath, content); } else { - Resources.registerResource(module, resourcePath, is, fromJar); + Resources.singleton().registerResource(module, resourcePath, is, fromJar); } try { @@ -391,9 +391,11 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { .map(e -> new CompiledConditionalPattern(e.condition(), makeResourcePattern(e.pattern()))) .collect(Collectors.toSet()); if (MissingRegistrationUtils.throwMissingRegistrationErrors()) { - for (ResourcePattern resourcePattern : includePatterns) { - Resources.singleton().registerIncludePattern(resourcePattern.moduleName, resourcePattern.pattern.pattern()); - } + includePatterns.stream() + .map(pattern -> pattern.compiledPattern) + .forEach(resourcePattern -> { + Resources.singleton().registerIncludePattern(resourcePattern.moduleName, resourcePattern.pattern.pattern()); + }); } ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns); DebugContext debugContext = beforeAnalysisAccess.getDebugContext(); @@ -535,7 +537,6 @@ public void registerNegativeQuery(Module module, String resourceName) { Resources.singleton().registerNegativeQuery(module, resourceName); } - // TODO if pass all gates - remove @Override public void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition, String content, boolean fromJar) { access.registerReachabilityHandler(e -> registerResourceIfNeeded(true, module, dir, content, fromJar), access.findClassByName(condition.getTypeName())); From 18a179746b0b9631f41e9d0170cda4f543d6ba96 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 16 Aug 2023 11:04:59 +0200 Subject: [PATCH 25/54] Remove exceptions that could not be thrown from method signature --- .../BundleContentSubstitutedLocalizationSupport.java | 1 - .../src/com/oracle/svm/hosted/ClassLoaderSupportImpl.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java index a43d7f15d982..3543494941d0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java @@ -186,5 +186,4 @@ public void prepareClassResourceBundle(String basename, Class bundleClass) { super.prepareClassResourceBundle(basename, bundleClass); this.existingBundles.add(bundleClass.getName()); } - } 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 fc8c85f7a941..543145f3c1c3 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 @@ -190,7 +190,7 @@ private static String makeDirContent(Set allEntries, String dir) { return String.join(System.lineSeparator(), dirContent); } - private static void scanDirectory(Path root, ResourceCollector collector, boolean includeAll) throws IOException { + private static void scanDirectory(Path root, ResourceCollector collector, boolean includeAll) { List matchedDirectoryResources = new ArrayList<>(); Set allEntries = new HashSet<>(); From 08167efe4ce9ed37487f0ecd0c7eb0a78f05d81b Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 16 Aug 2023 11:20:33 +0200 Subject: [PATCH 26/54] Remove unused import from mx file --- substratevm/mx.substratevm/mx_substratevm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 8159e089890b..3c873193198e 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -28,7 +28,7 @@ import tempfile from glob import glob from contextlib import contextmanager -from distutils.dir_util import mkpath, remove_tree # pylint: disable=no-name-in-module +from distutils.dir_util import mkpath # pylint: disable=no-name-in-module from os.path import join, exists, dirname import pipes from argparse import ArgumentParser From 12aa181f033798b5b9c11455c11af0b65732bd5d Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 18 Aug 2023 17:12:48 +0200 Subject: [PATCH 27/54] Revert locale argument for prepareBundle function --- .../BundleContentSubstitutedLocalizationSupport.java | 6 +++--- .../svm/core/jdk/localization/LocalizationSupport.java | 8 ++++---- .../jdk/localization/OptimizedLocalizationSupport.java | 6 +++--- .../svm/hosted/jdk/localization/LocalizationFeature.java | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java index 3543494941d0..10014521a9ce 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java @@ -174,11 +174,11 @@ public boolean isNotIncluded(String bundleName) { } @Override - public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule) { - super.prepareBundle(bundleName, bundle, null); + public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule, Locale locale) { + super.prepareBundle(bundleName, bundle, null, locale); /* Initialize ResourceBundle.keySet eagerly */ bundle.keySet(); - this.existingBundles.add(control.toBundleName(bundleName, bundle.getLocale())); + this.existingBundles.add(control.toBundleName(bundleName, locale)); } @Override 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 1b905050e403..b7eda880c015 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 @@ -102,12 +102,12 @@ public Map getBundleContentOf(Object bundle) { } @Platforms(Platform.HOSTED_ONLY.class) - public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule) { - if (bundle instanceof PropertyResourceBundle prb) { + public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule, Locale locale) { + if (bundle instanceof PropertyResourceBundle) { String[] bundleNameWithModule = SubstrateUtil.split(bundleName, ":", 2); String resourceName; if (bundleNameWithModule.length < 2) { - resourceName = control.toBundleName(bundleName, prb.getLocale()).replace('.', '/').concat(".properties"); + resourceName = control.toBundleName(bundleName, locale).replace('.', '/').concat(".properties"); // find module based on package name Map> packageToModules = ImageSingletons.lookup(ClassLoaderSupport.class).getPackageToModules(); @@ -124,7 +124,7 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function module = findModule.apply(bundleNameWithModule[0]); String finalResourceName = resourceName; module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, finalResourceName)); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java index 30afc879b2af..15d185dff739 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java @@ -91,7 +91,7 @@ public void prepareClassResourceBundle(String basename, Class bundleClass) { /*- Set the basename and locale to be consistent with JVM lookup process */ bundleNameField.set(bundle, basename); bundleLocaleField.set(bundle, locale); - prepareBundle(basename, bundle, null); + prepareBundle(basename, bundle, null, locale); } catch (ReflectionUtil.ReflectionUtilError | ReflectiveOperationException e) { throw UserError.abort(e, "Failed to instantiated bundle from class %s, reason %s", bundleClass, e.getCause().getMessage()); } @@ -108,9 +108,9 @@ private static Locale extractLocale(Class bundleClass) { @Platforms(Platform.HOSTED_ONLY.class) @Override - public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule) { + public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule, Locale locale) { bundle.keySet(); - this.resourceBundles.put(Pair.create(bundleName, bundle.getLocale()), bundle); + this.resourceBundles.put(Pair.create(bundleName, locale), bundle); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index 1c7dc43f37b8..7aabef6e0565 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -643,14 +643,14 @@ private void prepareBundle(String bundleName, ResourceBundle bundle, Locale loca */ for (ResourceBundle cur = bundle; cur != null; cur = SharedSecrets.getJavaUtilResourceBundleAccess().getParent(cur)) { /* Register all bundles with their corresponding locales */ - support.prepareBundle(bundleName, cur, this.imageClassLoader::findModule); + support.prepareBundle(bundleName, cur, this.imageClassLoader::findModule, cur.getLocale()); } /* * Finally, register the requested bundle with requested locale (Requested might be more * specific than the actual bundle locale */ - support.prepareBundle(bundleName, bundle, this.imageClassLoader::findModule); + support.prepareBundle(bundleName, bundle, this.imageClassLoader::findModule, locale); } @Platforms(Platform.HOSTED_ONLY.class) From de5d3075ff723a50a684a4dbe40923c3500ce4f1 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 22 Aug 2023 11:58:36 +0200 Subject: [PATCH 28/54] Add missing throw when using VMError --- .../configure/test/config/ResourceConfigurationTest.java | 6 +++--- .../oracle/svm/configure/config/ResourceConfiguration.java | 2 +- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) 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 ef28c19bcd99..d4007bbee5e6 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 @@ -33,15 +33,15 @@ import java.util.List; import java.util.Locale; -import com.oracle.svm.core.util.VMError; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.junit.Assert; import org.junit.Test; import com.oracle.svm.configure.config.ResourceConfiguration; -import com.oracle.svm.core.util.json.JsonWriter; import com.oracle.svm.core.configure.ResourceConfigurationParser; import com.oracle.svm.core.configure.ResourcesRegistry; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.core.util.json.JsonWriter; public class ResourceConfigurationTest { @@ -96,7 +96,7 @@ public void addResources(ConfigurationCondition condition, String pattern) { @Override public void addResource(Module module, String resourcePath) { - VMError.shouldNotReachHere("Unused function."); + throw VMError.shouldNotReachHere("Unused function."); } @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 18cfeded7762..6a561ee133e7 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 @@ -65,7 +65,7 @@ public void addResources(ConfigurationCondition condition, String pattern) { @Override public void addResource(Module module, String resourcePath) { - VMError.shouldNotReachHere("Unused function."); + throw VMError.shouldNotReachHere("Unused function."); } @Override 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 3a690dae798b..5d4056630a8e 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 @@ -176,7 +176,7 @@ public void addResources(ConfigurationCondition condition, String pattern) { try { resourcePatternWorkSet.add(new ConditionalPattern(condition, pattern)); } catch (UnsupportedOperationException e) { - UserError.abort("Resource registration should be performed before beforeAnalysis phase."); + throw UserError.abort("Resource registration should be performed before beforeAnalysis phase."); } } From fb8105dddf4b1c160fd79a100dff1fa25681ab94 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 22 Aug 2023 12:00:59 +0200 Subject: [PATCH 29/54] Replace findModule with Class.class.getModule for java.base scenario --- .../hosted/jdk/localization/CharsetSubstitutionsFeature.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java index e7addb2ac95d..8da31a190df0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/CharsetSubstitutionsFeature.java @@ -33,7 +33,7 @@ class CharsetSubstitutionsFeature implements InternalFeature { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - access.registerReachabilityHandler((e) -> RuntimeResourceAccess.addResource(ModuleLayer.boot().findModule("java.base").get(), + access.registerReachabilityHandler((e) -> RuntimeResourceAccess.addResource(Class.class.getModule(), "java/lang/uniName.dat"), access.findClassByName("java.lang.CharacterName")); } } From 69646bce35ee5cea2b4423826349fbb581d5fdf9 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 22 Aug 2023 12:02:00 +0200 Subject: [PATCH 30/54] Remove comments from prepareBundle --- .../oracle/svm/core/jdk/localization/LocalizationSupport.java | 4 ---- 1 file changed, 4 deletions(-) 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 b7eda880c015..52a11c0c1ba4 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 @@ -109,17 +109,13 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function> packageToModules = ImageSingletons.lookup(ClassLoaderSupport.class).getPackageToModules(); Set modules = packageToModules.getOrDefault(packageName(bundleName), Collections.emptySet()); - // there should be only one module, but we will check all modules where given - // package is found for (Module m : modules) { ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, resourceName); } - // if didn't find resource in any module, we will try with unnamed module if (modules.isEmpty()) { ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(null, resourceName); } From 9c5990191a93069ed8fad1ab7c0c7805b243adca Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 22 Aug 2023 13:40:25 +0200 Subject: [PATCH 31/54] Add null check guard for findModule --- .../svm/core/jdk/localization/LocalizationSupport.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 52a11c0c1ba4..fa5cf7ae302a 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 @@ -120,10 +120,12 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function module = findModule.apply(bundleNameWithModule[0]); - String finalResourceName = resourceName; - module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, finalResourceName)); + if (findModule != null) { + resourceName = control.toBundleName(bundleNameWithModule[1], locale).replace('.', '/').concat(".properties"); + Optional module = findModule.apply(bundleNameWithModule[0]); + String finalResourceName = resourceName; + module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, finalResourceName)); + } } } else { registerRequiredReflectionAndResourcesForBundle(bundleName, Set.of(locale)); From 03854b2223d363e461674981c8d90a6a5e9045ec Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Mon, 2 Oct 2023 16:01:20 +0200 Subject: [PATCH 32/54] Remove redudant functions and improve duplicates checking --- .../com/oracle/svm/core/jdk/Resources.java | 43 +++------------ .../oracle/svm/hosted/ResourcesFeature.java | 55 ++++++------------- 2 files changed, 25 insertions(+), 73 deletions(-) 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 662acbcb47cb..d48590c4fad8 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 @@ -35,6 +35,7 @@ import java.util.Collections; import java.util.Date; import java.util.Enumeration; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -165,6 +166,8 @@ private void updateTimeStamp() { } } + private HashSet set = new HashSet(); + @Platforms(Platform.HOSTED_ONLY.class) private void addEntry(Module module, String resourceName, boolean isDirectory, byte[] data, boolean fromJar, boolean isNegativeQuery) { VMError.guarantee(!BuildPhaseProvider.isAnalysisFinished(), "Trying to add a resource entry after analysis."); @@ -194,25 +197,15 @@ private void addEntry(Module module, String resourceName, boolean isDirectory, b } } + if (set.contains(resourceName)) { + System.out.println("RESOURCE: " + resourceName + " in module: " + module + " from jar: " + fromJar); + System.out.println("IS NEGATIVE QUERY: " + isNegativeQuery); + } entry.getData().add(data); + set.add(resourceName); } } - @Platforms(Platform.HOSTED_ONLY.class) - public static void registerResource(String resourceName, InputStream is) { - singleton().registerResource(null, resourceName, is, true); - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void registerResource(String resourceName, InputStream is, boolean fromJar) { - registerResource(null, resourceName, is, fromJar); - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void registerResource(Module module, String resourceName, InputStream is) { - registerResource(module, resourceName, is, true); - } - @Platforms(Platform.HOSTED_ONLY.class) public void registerResource(Module module, String resourceName, byte[] resourceContent) { addEntry(module, resourceName, false, resourceContent, true, false); @@ -223,21 +216,6 @@ public void registerResource(Module module, String resourceName, InputStream is, addEntry(module, resourceName, false, inputStreamToByteArray(is), fromJar, false); } - @Platforms(Platform.HOSTED_ONLY.class) - public void registerDirectoryResource(String resourceDirName, String content) { - registerDirectoryResource(null, resourceDirName, content, true); - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void registerDirectoryResource(String resourceDirName, String content, boolean fromJar) { - registerDirectoryResource(null, resourceDirName, content, fromJar); - } - - @Platforms(Platform.HOSTED_ONLY.class) - public void registerDirectoryResource(Module module, String resourceDirName, String content) { - registerDirectoryResource(module, resourceDirName, content, true); - } - @Platforms(Platform.HOSTED_ONLY.class) public void registerDirectoryResource(Module module, String resourceDirName, String content, boolean fromJar) { /* @@ -248,11 +226,6 @@ public void registerDirectoryResource(Module module, String resourceDirName, Str addEntry(module, resourceDirName, true, content.getBytes(), fromJar, false); } - @Platforms(Platform.HOSTED_ONLY.class) - public void registerIOException(String resourceName, IOException e, boolean linkAtBuildTime) { - registerIOException(null, resourceName, e, linkAtBuildTime); - } - @Platforms(Platform.HOSTED_ONLY.class) public void registerIOException(Module module, String resourceName, IOException e, boolean linkAtBuildTime) { if (linkAtBuildTime) { 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 5d4056630a8e..e4311380b7a6 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 @@ -304,7 +304,7 @@ public void addResource(Module module, String resourcePath) { } if (isDirectory) { - Resources.singleton().registerDirectoryResource(module, resourcePath, content); + Resources.singleton().registerDirectoryResource(module, resourcePath, content, fromJar); } else { Resources.singleton().registerResource(module, resourcePath, is, fromJar); } @@ -495,36 +495,38 @@ public List isIncluded(Module module, String resourceNam private final Set alreadyAddedResources = new HashSet<>(); - private void registerResourceIfNeeded(boolean isDirectory, Object... arguments) { - Module module = (Module) arguments[0]; - String resourceName = (String) arguments[1]; + public void registerResourceIfNeeded(Module module, String resourceName) { // we only do this if we are on the classPath - if ((module == null || !module.isNamed()) && !alreadyAddedResources.contains(resourceName)) { - if (isDirectory) { - String content = (String) arguments[2]; - boolean fromJar = (boolean) arguments[3]; - addDirectoryResource(module, resourceName, content, fromJar); - } else { + if ((module == null || !module.isNamed())) { + if (!alreadyAddedResources.contains(resourceName)) { ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourceName); + alreadyAddedResources.add(resourceName); } - - alreadyAddedResources.add(resourceName); + } else { + // we should always try to register module entries (checked later in addEntries) + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourceName); } + } @Override public void addResource(Module module, String resourceName, InputStream resourceStream, boolean fromJar) { - registerResource(debugContext, module, resourceName, resourceStream, fromJar); + registerResourceIfNeeded(module, resourceName); } @Override public void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition) { - access.registerReachabilityHandler(e -> registerResourceIfNeeded(false, module, resourceName), access.findClassByName(condition.getTypeName())); + access.registerReachabilityHandler(e -> registerResourceIfNeeded(module, resourceName), access.findClassByName(condition.getTypeName())); } @Override public void addDirectoryResource(Module module, String dir, String content, boolean fromJar) { - registerDirectoryResource(debugContext, module, dir, content, fromJar); + registerResourceIfNeeded(module, dir); + } + + @Override + public void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition, String content, boolean fromJar) { + access.registerReachabilityHandler(e -> registerResourceIfNeeded(module, dir), access.findClassByName(condition.getTypeName())); } @Override @@ -536,11 +538,6 @@ public void registerIOException(Module module, String resourceName, IOException public void registerNegativeQuery(Module module, String resourceName) { Resources.singleton().registerNegativeQuery(module, resourceName); } - - @Override - public void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition, String content, boolean fromJar) { - access.registerReachabilityHandler(e -> registerResourceIfNeeded(true, module, dir, content, fromJar), access.findClassByName(condition.getTypeName())); - } } private ResourcePattern[] compilePatterns(Set patterns) { @@ -599,24 +596,6 @@ public void beforeCompilation(BeforeCompilationAccess access) { } } - @SuppressWarnings("try") - private static void registerResource(DebugContext debugContext, Module module, String resourceName, InputStream resourceStream, boolean fromJar) { - try (DebugContext.Scope s = debugContext.scope("registerResource")) { - String moduleNamePrefix = module == null ? "" : module.getName() + ":"; - debugContext.log(DebugContext.VERBOSE_LEVEL, "ResourcesFeature: registerResource: %s%s", moduleNamePrefix, resourceName); - Resources.singleton().registerResource(module, resourceName, resourceStream, fromJar); - } - } - - @SuppressWarnings("try") - private static void registerDirectoryResource(DebugContext debugContext, Module module, String dir, String content, boolean fromJar) { - try (DebugContext.Scope s = debugContext.scope("registerResource")) { - String moduleNamePrefix = module == null ? "" : module.getName() + ":"; - debugContext.log(DebugContext.VERBOSE_LEVEL, "ResourcesFeature: registerResource: %s%s", moduleNamePrefix, dir); - Resources.singleton().registerDirectoryResource(module, dir, content, fromJar); - } - } - @Override public void registerInvocationPlugins(Providers providers, SnippetReflectionProvider snippetReflection, GraphBuilderConfiguration.Plugins plugins, ParsingReason reason) { if (!reason.duringAnalysis() || reason == ParsingReason.JITCompilation) { From 9b9ed1feda8320fa78a883edee50d9e723516b79 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 6 Oct 2023 13:34:22 +0200 Subject: [PATCH 33/54] Register negative query directories --- .../src/com/oracle/svm/core/jdk/Resources.java | 10 +--------- .../svm/hosted/ClassLoaderSupportImpl.java | 4 ++++ .../com/oracle/svm/hosted/ResourcesFeature.java | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 9 deletions(-) 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 d48590c4fad8..3b5d615f4273 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 @@ -35,7 +35,6 @@ import java.util.Collections; import java.util.Date; import java.util.Enumeration; -import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -107,7 +106,7 @@ public record ModuleResourcePair(String module, String resource) { * specified in the configuration, but we do not want to throw directly (for example when we try * to check all the modules for a resource). */ - private static final ResourceStorageEntryBase MISSING_METADATA_MARKER = new ResourceStorageEntryBase(); + public static final ResourceStorageEntryBase MISSING_METADATA_MARKER = new ResourceStorageEntryBase(); /** * Embedding a resource into an image is counted as a modification. Since all resources are @@ -166,8 +165,6 @@ private void updateTimeStamp() { } } - private HashSet set = new HashSet(); - @Platforms(Platform.HOSTED_ONLY.class) private void addEntry(Module module, String resourceName, boolean isDirectory, byte[] data, boolean fromJar, boolean isNegativeQuery) { VMError.guarantee(!BuildPhaseProvider.isAnalysisFinished(), "Trying to add a resource entry after analysis."); @@ -197,12 +194,7 @@ private void addEntry(Module module, String resourceName, boolean isDirectory, b } } - if (set.contains(resourceName)) { - System.out.println("RESOURCE: " + resourceName + " in module: " + module + " from jar: " + fromJar); - System.out.println("IS NEGATIVE QUERY: " + isNegativeQuery); - } entry.getData().add(data); - set.add(resourceName); } } 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 543145f3c1c3..d9629d0926e5 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 @@ -216,6 +216,10 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea matchedDirectoryResources.add(new ConditionalResource(condition, relativeFilePath)); } + if (conditions.isEmpty()) { + collector.registerNegativeQuery(null, relativeFilePath); + } + try (Stream pathStream = Files.list(entry)) { Stream filtered = pathStream; if (ClassUtil.CLASS_MODULE_PATH_EXCLUDE_DIRECTORIES_ROOT.equals(entry)) { 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 e4311380b7a6..9d8015b75cee 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 @@ -94,6 +94,7 @@ import com.oracle.svm.core.jdk.resources.NativeImageResourceFileAttributesView; import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystem; import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystemProvider; +import com.oracle.svm.core.jdk.resources.ResourceStorageEntryBase; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.LocatableMultiOptionValue; import com.oracle.svm.core.util.UserError; @@ -583,6 +584,22 @@ boolean moduleNameMatches(String resourceContainerModuleName) { @Override public void afterAnalysis(AfterAnalysisAccess access) { sealed = true; + var entryIter = Resources.singleton().getResourceStorage().getEntries(); + while (entryIter.advance()) { + var key = entryIter.getKey(); + ResourceStorageEntryBase val = entryIter.getValue(); + String valStr; + if (val.isException()) { + valStr = val.getException().getClass().getName(); + } else if (val == Resources.NEGATIVE_QUERY_MARKER) { + valStr = "NEGATIVE_QUERY_MARKER"; + } else if (val == Resources.MISSING_METADATA_MARKER) { + valStr = "MISSING_METADATA_MARKER"; + } else { + valStr = "" + val.getData().stream().map(bytes -> bytes.length).reduce((a, b) -> a + b).get(); + } + System.out.println("RESOURCE: " + key.getRight() + ": " + valStr); + } } @Override From 167a72749cf3afa26fc5bb43d964fe740755c27a Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Mon, 9 Oct 2023 17:06:15 +0200 Subject: [PATCH 34/54] Fix package export from module --- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 9d8015b75cee..0096b24bdbe0 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 @@ -260,10 +260,9 @@ public void addResource(Module module, String resourcePath) { try { String resourcePackage = jdk.internal.module.Resources.toPackageName(resourcePath); if (!resourcePackage.isEmpty()) { - if (!module.getPackages().contains(resourcePackage)) { - return; + if (module.getPackages().contains(resourcePackage)) { + ModuleSupport.accessModuleByClass(ModuleSupport.Access.EXPORT, ResourcesFeature.class, module, resourcePackage); } - ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, ResourcesFeature.class, module, resourcePackage); } is = module.getResourceAsStream(resourcePath); From 620f8137d411510231c9b3ec8703d8ae7f74f077 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 10 Oct 2023 12:06:55 +0200 Subject: [PATCH 35/54] Remove redudant parameters from resource registration functions --- .../oracle/svm/core/ClassLoaderSupport.java | 5 +- .../svm/hosted/ClassLoaderSupportImpl.java | 43 ++-- .../oracle/svm/hosted/ResourcesFeature.java | 187 +++++++++--------- 3 files changed, 113 insertions(+), 122 deletions(-) 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 85851ab218b3..937dd94f0773 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 @@ -25,7 +25,6 @@ package com.oracle.svm.core; import java.io.IOException; -import java.io.InputStream; import java.net.URI; import java.util.List; import java.util.Locale; @@ -56,11 +55,11 @@ public boolean isNativeImageClassLoader(ClassLoader classLoader) { public interface ResourceCollector { List isIncluded(Module module, String resourceName, URI resourceURI); - void addResource(Module module, String resourceName, InputStream resourceStream, boolean fromJar); + void addResource(Module module, String resourceName); void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition); - void addDirectoryResource(Module module, String dir, String content, boolean fromJar); + void addDirectoryResource(Module module, String dir); void registerNegativeQuery(Module module, String resourceName); 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 d9629d0926e5..7b23c73ebad5 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 @@ -147,7 +147,7 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso String resName = entry.resourceName(); if (resName.endsWith("/")) { if (ConfigurationCondition.isAlwaysTrue(condition)) { - resourceCollector.addDirectoryResource(info.module, resName, "", false); + resourceCollector.addDirectoryResource(info.module, resName); } else { resourceCollector.addResourceConditionally(info.module, resName, condition); } @@ -160,14 +160,11 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso resourceCollector.registerNegativeQuery(info.module, resName); continue; } - try (InputStream is = content.get()) { - if (ConfigurationCondition.isAlwaysTrue(condition)) { - resourceCollector.addResource(info.module, resName, is, false); - } else { - resourceCollector.addResourceConditionally(info.module, resName, condition); - } - } catch (IOException resourceException) { - resourceCollector.registerIOException(info.module, resName, resourceException, LinkAtBuildTimeSupport.singleton().moduleLinkAtBuildTime(info.module.getName())); + + if (ConfigurationCondition.isAlwaysTrue(condition)) { + resourceCollector.addResource(info.module, resName); + } else { + resourceCollector.addResourceConditionally(info.module, resName, condition); } } @@ -231,14 +228,10 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea } } else { for (ConfigurationCondition condition : conditions) { - try (InputStream is = Files.newInputStream(entry)) { - if (ConfigurationCondition.isAlwaysTrue(condition)) { - collector.addResource(null, relativeFilePath, is, false); - } else { - collector.addResourceConditionally(null, relativeFilePath, condition); - } - } catch (IOException resourceException) { - collector.registerIOException(null, relativeFilePath, resourceException, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(relativeFilePath)); + if (ConfigurationCondition.isAlwaysTrue(condition)) { + collector.addResource(null, relativeFilePath); + } else { + collector.addResourceConditionally(null, relativeFilePath, condition); } } } @@ -249,7 +242,7 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea String dir = entry.resourceName(); String contentName = makeDirContent(allEntries, dir); if (ConfigurationCondition.isAlwaysTrue(condition)) { - collector.addDirectoryResource(null, dir, contentName, false); + collector.addDirectoryResource(null, dir); } else { collector.addDirectoryResourceConditionally(null, dir, condition, contentName, false); } @@ -268,7 +261,7 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i for (ConfigurationCondition condition : conditions) { // Register the directory with empty content to preserve Java behavior if (ConfigurationCondition.isAlwaysTrue(condition)) { - collector.addDirectoryResource(null, dirName, "", true); + collector.addDirectoryResource(null, dirName); } else { collector.addDirectoryResourceConditionally(null, dirName, condition, "", true); } @@ -276,14 +269,10 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i } else { List conditions = shouldIncludeEntry(null, collector, entry.getName(), jarPath.toUri(), includeAll); for (ConfigurationCondition condition : conditions) { - try (InputStream is = jf.getInputStream(entry)) { - if (ConfigurationCondition.isAlwaysTrue(condition)) { - collector.addResource(null, entry.getName(), is, true); - } else { - collector.addResourceConditionally(null, entry.getName(), condition); - } - } catch (IOException resourceException) { - collector.registerIOException(null, entry.getName(), resourceException, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(entry.getName())); + if (ConfigurationCondition.isAlwaysTrue(condition)) { + collector.addResource(null, entry.getName()); + } else { + collector.addResourceConditionally(null, entry.getName(), condition); } } } 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 0096b24bdbe0..d982b78dae0f 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 @@ -46,12 +46,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -163,6 +163,7 @@ private record CompiledConditionalPattern(ConfigurationCondition condition, Reso private class ResourcesRegistryImpl extends ConditionalConfigurationRegistry implements ResourcesRegistry { private final ConfigurationTypeResolver configurationTypeResolver; + private final Set alreadyAddedResources = new HashSet<>(); ResourcesRegistryImpl(ConfigurationTypeResolver configurationTypeResolver) { this.configurationTypeResolver = configurationTypeResolver; @@ -181,81 +182,17 @@ public void addResources(ConfigurationCondition condition, String pattern) { } } - private String urlToJarPath(URL url) { - try { - return ((JarURLConnection) url.openConnection()).getJarFileURL().getFile(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private boolean resourceIsDirectory(URL url, boolean fromJar, String resourcePath) throws IOException, URISyntaxException { - if (fromJar) { - try (JarFile jf = new JarFile(urlToJarPath(url))) { - return jf.getEntry(resourcePath).isDirectory(); - } - } else { - return Files.isDirectory(Path.of(url.toURI())); - } - } - - private String getDirectoryContent(String path, boolean fromJar) throws IOException { - List content = new ArrayList<>(); - if (fromJar) { - try (JarFile jf = new JarFile(urlToJarPath(URI.create(path).toURL()))) { - String pathSeparator = FileSystems.getDefault().getSeparator(); - String directoryPath = path.split("!")[1]; - - // we are removing leading slash because jar entry names don't start with slash - if (directoryPath.startsWith(pathSeparator)) { - directoryPath = directoryPath.substring(1); - } - - Enumeration entries = jf.entries(); - while (entries.hasMoreElements()) { - String entry = entries.nextElement().getName(); - if (entry.startsWith(directoryPath)) { - String contentEntry = entry.substring(directoryPath.length()); - - // remove the leading slash - if (contentEntry.startsWith(pathSeparator)) { - contentEntry = contentEntry.substring(1); - } - - // prevent adding empty strings as a content - if (!contentEntry.isEmpty()) { - // get top level content only - int firstSlash = contentEntry.indexOf(pathSeparator); - if (firstSlash != -1) { - content.add(contentEntry.substring(0, firstSlash)); - } else { - content.add(contentEntry); - } - } - } - } - - } - } else { - try (Stream contentStream = Files.list(Path.of(path))) { - content = new ArrayList<>(contentStream - .map(Path::getFileName) - .map(Path::toString) - .toList()); - } - } - - content.sort(Comparator.naturalOrder()); - return String.join(System.lineSeparator(), content); - } - + /* Adds single resource defined with its module and name */ @Override public void addResource(Module module, String resourcePath) { + if (!shouldRegisterResource(module, resourcePath)) { + return; + } + InputStream is; boolean fromJar; boolean isDirectory; String content = ""; - if (module != null && module.isNamed()) { try { String resourcePackage = jdk.internal.module.Resources.toPackageName(resourcePath); @@ -356,6 +293,88 @@ public void addResourceBundles(ConfigurationCondition condition, String basename } registerConditionalConfiguration(condition, () -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(basename, locales)); } + + public boolean shouldRegisterResource(Module module, String resourceName) { + // we only do this if we are on the classPath + if ((module == null || !module.isNamed())) { + if (!alreadyAddedResources.contains(resourceName)) { + alreadyAddedResources.add(resourceName); + return true; + } else { + return false; + } + } else { + // we should always try to register module entries (checked later in addEntries) + return true; + } + } + + private String urlToJarPath(URL url) { + try { + return ((JarURLConnection) url.openConnection()).getJarFileURL().getFile(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private boolean resourceIsDirectory(URL url, boolean fromJar, String resourcePath) throws IOException, URISyntaxException { + if (fromJar) { + try (JarFile jf = new JarFile(urlToJarPath(url))) { + return jf.getEntry(resourcePath).isDirectory(); + } + } else { + return Files.isDirectory(Path.of(url.toURI())); + } + } + + private String getDirectoryContent(String path, boolean fromJar) throws IOException { + Set content = new TreeSet<>(); + if (fromJar) { + try (JarFile jf = new JarFile(urlToJarPath(URI.create(path).toURL()))) { + String pathSeparator = FileSystems.getDefault().getSeparator(); + String directoryPath = path.split("!")[1]; + + // we are removing leading slash because jar entry names don't start with slash + if (directoryPath.startsWith(pathSeparator)) { + directoryPath = directoryPath.substring(1); + } + + Enumeration entries = jf.entries(); + while (entries.hasMoreElements()) { + String entry = entries.nextElement().getName(); + if (entry.startsWith(directoryPath)) { + String contentEntry = entry.substring(directoryPath.length()); + + // remove the leading slash + if (contentEntry.startsWith(pathSeparator)) { + contentEntry = contentEntry.substring(1); + } + + // prevent adding empty strings as a content + if (!contentEntry.isEmpty()) { + // get top level content only + int firstSlash = contentEntry.indexOf(pathSeparator); + if (firstSlash != -1) { + content.add(contentEntry.substring(0, firstSlash)); + } else { + content.add(contentEntry); + } + } + } + } + + } + } else { + try (Stream contentStream = Files.list(Path.of(path))) { + content = new TreeSet<>(contentStream + .map(Path::getFileName) + .map(Path::toString) + .toList()); + } + } + + return String.join(System.lineSeparator(), content); + } } @Override @@ -493,40 +512,24 @@ public List isIncluded(Module module, String resourceNam return conditions; } - private final Set alreadyAddedResources = new HashSet<>(); - - public void registerResourceIfNeeded(Module module, String resourceName) { - // we only do this if we are on the classPath - if ((module == null || !module.isNamed())) { - if (!alreadyAddedResources.contains(resourceName)) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourceName); - alreadyAddedResources.add(resourceName); - } - } else { - // we should always try to register module entries (checked later in addEntries) - ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourceName); - } - - } - @Override - public void addResource(Module module, String resourceName, InputStream resourceStream, boolean fromJar) { - registerResourceIfNeeded(module, resourceName); + public void addResource(Module module, String resourceName) { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourceName); } @Override public void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition) { - access.registerReachabilityHandler(e -> registerResourceIfNeeded(module, resourceName), access.findClassByName(condition.getTypeName())); + access.registerReachabilityHandler(e -> addResource(module, resourceName), access.findClassByName(condition.getTypeName())); } @Override - public void addDirectoryResource(Module module, String dir, String content, boolean fromJar) { - registerResourceIfNeeded(module, dir); + public void addDirectoryResource(Module module, String dir) { + ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, dir); } @Override public void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition, String content, boolean fromJar) { - access.registerReachabilityHandler(e -> registerResourceIfNeeded(module, dir), access.findClassByName(condition.getTypeName())); + access.registerReachabilityHandler(e -> addDirectoryResource(module, dir), access.findClassByName(condition.getTypeName())); } @Override From 2e769954c70500c599d25fae93f96007046fbd18 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 10 Oct 2023 13:51:36 +0200 Subject: [PATCH 36/54] Remove sufficient prints --- .../com/oracle/svm/hosted/ResourcesFeature.java | 17 ----------------- 1 file changed, 17 deletions(-) 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 d982b78dae0f..7c1f41f0e7bf 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 @@ -94,7 +94,6 @@ import com.oracle.svm.core.jdk.resources.NativeImageResourceFileAttributesView; import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystem; import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystemProvider; -import com.oracle.svm.core.jdk.resources.ResourceStorageEntryBase; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.LocatableMultiOptionValue; import com.oracle.svm.core.util.UserError; @@ -586,22 +585,6 @@ boolean moduleNameMatches(String resourceContainerModuleName) { @Override public void afterAnalysis(AfterAnalysisAccess access) { sealed = true; - var entryIter = Resources.singleton().getResourceStorage().getEntries(); - while (entryIter.advance()) { - var key = entryIter.getKey(); - ResourceStorageEntryBase val = entryIter.getValue(); - String valStr; - if (val.isException()) { - valStr = val.getException().getClass().getName(); - } else if (val == Resources.NEGATIVE_QUERY_MARKER) { - valStr = "NEGATIVE_QUERY_MARKER"; - } else if (val == Resources.MISSING_METADATA_MARKER) { - valStr = "MISSING_METADATA_MARKER"; - } else { - valStr = "" + val.getData().stream().map(bytes -> bytes.length).reduce((a, b) -> a + b).get(); - } - System.out.println("RESOURCE: " + key.getRight() + ": " + valStr); - } } @Override From 1635c2b5f46f32e681b270fd7692b3c792dffa73 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 11 Oct 2023 10:52:09 +0200 Subject: [PATCH 37/54] Cover case when same resource name can occur in multiple bundles --- .../oracle/svm/hosted/ResourcesFeature.java | 79 ++++++++++++------- 1 file changed, 52 insertions(+), 27 deletions(-) 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 7c1f41f0e7bf..6c9c58225396 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 @@ -211,44 +211,69 @@ public void addResource(Module module, String resourcePath) { // we should ignore if user failed to provide resource return; } - } else { - URL url = imageClassLoader.getClassLoader().getResource(resourcePath); - if (url == null) { - // we should ignore if user failed to provide resource + + if (is == null) { return; } + if (isDirectory) { + Resources.singleton().registerDirectoryResource(module, resourcePath, content, fromJar); + } else { + Resources.singleton().registerResource(module, resourcePath, is, fromJar); + } + try { - is = url.openStream(); - fromJar = url.getProtocol().equalsIgnoreCase("jar"); - isDirectory = resourceIsDirectory(url, fromJar, resourcePath); - // if directory is from jar content should remain empty (same as in scanJar - // function from ClassLoaderSupportImpl) - if (isDirectory) { - content = getDirectoryContent(fromJar ? url.toString() : Paths.get(url.toURI()).toString(), fromJar); - } + is.close(); } catch (IOException e) { + throw new RuntimeException(e); + } + + } else { + Enumeration urls; + try { + urls = imageClassLoader.getClassLoader().getResources(resourcePath); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (!urls.hasMoreElements()) { // we should ignore if user failed to provide resource return; - } catch (URISyntaxException e) { - throw new RuntimeException(e); } - } - if (is == null) { - return; - } + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + try { + is = url.openStream(); + fromJar = url.getProtocol().equalsIgnoreCase("jar"); + isDirectory = resourceIsDirectory(url, fromJar, resourcePath); + // if directory is from jar content should remain empty (same as in scanJar + // function from ClassLoaderSupportImpl) + if (isDirectory) { + content = getDirectoryContent(fromJar ? url.toString() : Paths.get(url.toURI()).toString(), fromJar); + } + } catch (IOException e) { + // we should ignore if user failed to provide resource + return; + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } - if (isDirectory) { - Resources.singleton().registerDirectoryResource(module, resourcePath, content, fromJar); - } else { - Resources.singleton().registerResource(module, resourcePath, is, fromJar); - } + if (is == null) { + return; + } - try { - is.close(); - } catch (IOException e) { - throw new RuntimeException(e); + if (isDirectory) { + Resources.singleton().registerDirectoryResource(module, resourcePath, content, fromJar); + } else { + Resources.singleton().registerResource(module, resourcePath, is, fromJar); + } + + try { + is.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } } From 52d9e72a94340c0b7930beb641cd07a113120be2 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 11 Oct 2023 16:50:28 +0200 Subject: [PATCH 38/54] Restructure code for better readability --- .../oracle/svm/hosted/ResourcesFeature.java | 166 +++++++++--------- 1 file changed, 80 insertions(+), 86 deletions(-) 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 6c9c58225396..ecc611896372 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 @@ -188,92 +188,10 @@ public void addResource(Module module, String resourcePath) { return; } - InputStream is; - boolean fromJar; - boolean isDirectory; - String content = ""; if (module != null && module.isNamed()) { - try { - String resourcePackage = jdk.internal.module.Resources.toPackageName(resourcePath); - if (!resourcePackage.isEmpty()) { - if (module.getPackages().contains(resourcePackage)) { - ModuleSupport.accessModuleByClass(ModuleSupport.Access.EXPORT, ResourcesFeature.class, module, resourcePackage); - } - } - - is = module.getResourceAsStream(resourcePath); - fromJar = false; - isDirectory = new File(resourcePath).isDirectory(); - if (isDirectory) { - content = getDirectoryContent(resourcePath, false); - } - } catch (IOException e) { - // we should ignore if user failed to provide resource - return; - } - - if (is == null) { - return; - } - - if (isDirectory) { - Resources.singleton().registerDirectoryResource(module, resourcePath, content, fromJar); - } else { - Resources.singleton().registerResource(module, resourcePath, is, fromJar); - } - - try { - is.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - + processResourceFromModule(module, resourcePath); } else { - Enumeration urls; - try { - urls = imageClassLoader.getClassLoader().getResources(resourcePath); - } catch (IOException e) { - throw new RuntimeException(e); - } - if (!urls.hasMoreElements()) { - // we should ignore if user failed to provide resource - return; - } - - while (urls.hasMoreElements()) { - URL url = urls.nextElement(); - try { - is = url.openStream(); - fromJar = url.getProtocol().equalsIgnoreCase("jar"); - isDirectory = resourceIsDirectory(url, fromJar, resourcePath); - // if directory is from jar content should remain empty (same as in scanJar - // function from ClassLoaderSupportImpl) - if (isDirectory) { - content = getDirectoryContent(fromJar ? url.toString() : Paths.get(url.toURI()).toString(), fromJar); - } - } catch (IOException e) { - // we should ignore if user failed to provide resource - return; - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - if (is == null) { - return; - } - - if (isDirectory) { - Resources.singleton().registerDirectoryResource(module, resourcePath, content, fromJar); - } else { - Resources.singleton().registerResource(module, resourcePath, is, fromJar); - } - - try { - is.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } + processResourceFromClasspath(module, resourcePath); } } @@ -319,8 +237,8 @@ public void addResourceBundles(ConfigurationCondition condition, String basename } public boolean shouldRegisterResource(Module module, String resourceName) { - // we only do this if we are on the classPath if ((module == null || !module.isNamed())) { + // we only do this if we are on the classPath if (!alreadyAddedResources.contains(resourceName)) { alreadyAddedResources.add(resourceName); return true; @@ -328,11 +246,87 @@ public boolean shouldRegisterResource(Module module, String resourceName) { return false; } } else { - // we should always try to register module entries (checked later in addEntries) + // always try to register module entries (duplicates checked later in addEntries) return true; } } + private void processResourceFromModule(Module module, String resourcePath) { + try { + String resourcePackage = jdk.internal.module.Resources.toPackageName(resourcePath); + if (!resourcePackage.isEmpty()) { + // if processing resource package, make sure that module exports that package + if (module.getPackages().contains(resourcePackage)) { + ModuleSupport.accessModuleByClass(ModuleSupport.Access.EXPORT, ResourcesFeature.class, module, resourcePackage); + } + } + + InputStream is = module.getResourceAsStream(resourcePath); + boolean isDirectory = new File(resourcePath).isDirectory(); + String content = ""; + if (isDirectory) { + content = getDirectoryContent(resourcePath, false); + } + + registerResource(module, resourcePath, isDirectory, false, is, content); + } catch (IOException e) { + // we should ignore if user failed to provide resource + } + } + + private void processResourceFromClasspath(Module module, String resourcePath) { + Enumeration urls; + try { + /* + * There is an edge case where same resource name can be present in multiple jars + * (different resources), so we are collecting all resources with given name in all + * jars on classpath + */ + urls = imageClassLoader.getClassLoader().getResources(resourcePath); + } catch (IOException e) { + throw new RuntimeException(e); + } + + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + try { + InputStream is = url.openStream(); + boolean fromJar = url.getProtocol().equalsIgnoreCase("jar"); + boolean isDirectory = resourceIsDirectory(url, fromJar, resourcePath); + String content = ""; + if (isDirectory) { + content = getDirectoryContent(fromJar ? url.toString() : Paths.get(url.toURI()).toString(), fromJar); + } + + registerResource(module, resourcePath, isDirectory, fromJar, is, content); + } catch (IOException e) { + // we should ignore if user failed to provide resource + return; + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + } + + private void registerResource(Module module, String resourcePath, boolean isDirectory, boolean fromJar, InputStream is, String content) { + if (is == null) { + return; + } + + if (isDirectory) { + Resources.singleton().registerDirectoryResource(module, resourcePath, content, fromJar); + } else { + Resources.singleton().registerResource(module, resourcePath, is, fromJar); + } + + try { + is.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /* Util functions for resource attributes calculations */ private String urlToJarPath(URL url) { try { return ((JarURLConnection) url.openConnection()).getJarFileURL().getFile(); From 0f90072de68d4d80543373f378afd7659fff86ad Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 11 Oct 2023 16:55:48 +0200 Subject: [PATCH 39/54] Add synchronization for alreadyAddedResources check --- .../oracle/svm/hosted/ResourcesFeature.java | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) 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 ecc611896372..fc8a0036f1b1 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 @@ -94,6 +94,7 @@ import com.oracle.svm.core.jdk.resources.NativeImageResourceFileAttributesView; import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystem; import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystemProvider; +import com.oracle.svm.core.jdk.resources.ResourceStorageEntryBase; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.LocatableMultiOptionValue; import com.oracle.svm.core.util.UserError; @@ -238,12 +239,16 @@ public void addResourceBundles(ConfigurationCondition condition, String basename public boolean shouldRegisterResource(Module module, String resourceName) { if ((module == null || !module.isNamed())) { - // we only do this if we are on the classPath - if (!alreadyAddedResources.contains(resourceName)) { - alreadyAddedResources.add(resourceName); - return true; - } else { - return false; + // addResources can be called from multiple threads so this should be synchronized + synchronized (alreadyAddedResources) { + // we only do this if we are on the classPath + if (!alreadyAddedResources.contains(resourceName)) { + alreadyAddedResources.add(resourceName); + return true; + } else { + return false; + } + } } else { // always try to register module entries (duplicates checked later in addEntries) @@ -604,6 +609,22 @@ boolean moduleNameMatches(String resourceContainerModuleName) { @Override public void afterAnalysis(AfterAnalysisAccess access) { sealed = true; + var entryIter = Resources.singleton().getResourceStorage().getEntries(); + while (entryIter.advance()) { + var key = entryIter.getKey(); + ResourceStorageEntryBase val = entryIter.getValue(); + String valStr; + if (val.isException()) { + valStr = val.getException().getClass().getName(); + } else if (val == Resources.NEGATIVE_QUERY_MARKER) { + valStr = "NEGATIVE_QUERY_MARKER"; + } else if (val == Resources.MISSING_METADATA_MARKER) { + valStr = "MISSING_METADATA_MARKER"; + } else { + valStr = "" + val.getData().stream().map(bytes -> bytes.length).reduce((a, b) -> a + b).get(); + } + System.out.println("RESOURCE: " + key.getRight() + ": " + valStr); + } } @Override From 17f84cc8d1462e12067d6d2d4f42c06bb045337b Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 11 Oct 2023 17:28:49 +0200 Subject: [PATCH 40/54] Cleanup code --- .../oracle/svm/core/ClassLoaderSupport.java | 5 - .../com/oracle/svm/core/jdk/Resources.java | 2 +- .../svm/hosted/ClassLoaderSupportImpl.java | 106 +++++------------- .../oracle/svm/hosted/ResourcesFeature.java | 29 ----- 4 files changed, 27 insertions(+), 115 deletions(-) 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 937dd94f0773..aa53efe3871a 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 @@ -59,14 +59,9 @@ public interface ResourceCollector { void addResourceConditionally(Module module, String resourceName, ConfigurationCondition condition); - void addDirectoryResource(Module module, String dir); - void registerNegativeQuery(Module module, String resourceName); void registerIOException(Module module, String resourceName, IOException e, boolean linkAtBuildTime); - - void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition, String content, boolean fromJar); - } public abstract void collectResources(ResourceCollector resourceCollector); 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 3b5d615f4273..f7ec625c6ad2 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 @@ -106,7 +106,7 @@ public record ModuleResourcePair(String module, String resource) { * specified in the configuration, but we do not want to throw directly (for example when we try * to check all the modules for a resource). */ - public static final ResourceStorageEntryBase MISSING_METADATA_MARKER = new ResourceStorageEntryBase(); + private static final ResourceStorageEntryBase MISSING_METADATA_MARKER = new ResourceStorageEntryBase(); /** * Embedding a resource into an image is counted as a modification. Since all resources are 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 7b23c73ebad5..3b61ab842bee 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 @@ -39,7 +39,6 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -116,7 +115,7 @@ public void collectResources(ResourceCollector resourceCollector) { .forEach(lookup -> collectResourceFromModule(resourceCollector, lookup)); /* Collect remaining resources from classpath */ - for (Path classpathFile : classLoaderSupport.classpath()) { + classLoaderSupport.classpath().stream().parallel().forEach(classpathFile -> { boolean includeAll = classLoaderSupport.getJavaPathsToInclude().contains(classpathFile); try { if (Files.isDirectory(classpathFile)) { @@ -127,13 +126,13 @@ public void collectResources(ResourceCollector resourceCollector) { } catch (IOException ex) { throw UserError.abort("Unable to handle classpath element '%s'. Make sure that all classpath entries are either directories or valid jar files.", classpathFile); } - } + }); } private void collectResourceFromModule(ResourceCollector resourceCollector, ResourceLookupInfo info) { ModuleReference moduleReference = info.resolvedModule.reference(); try (ModuleReader moduleReader = moduleReference.open()) { - var includeAll = classLoaderSupport.getJavaModuleNamesToInclude().contains(info.resolvedModule().name()); + boolean includeAll = 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), includeAll); @@ -146,11 +145,7 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso ConfigurationCondition condition = entry.condition(); String resName = entry.resourceName(); if (resName.endsWith("/")) { - if (ConfigurationCondition.isAlwaysTrue(condition)) { - resourceCollector.addDirectoryResource(info.module, resName); - } else { - resourceCollector.addResourceConditionally(info.module, resName, condition); - } + includeResource(resourceCollector, info.module, resName, condition); continue; } @@ -161,11 +156,7 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso continue; } - if (ConfigurationCondition.isAlwaysTrue(condition)) { - resourceCollector.addResource(info.module, resName); - } else { - resourceCollector.addResourceConditionally(info.module, resName, condition); - } + includeResource(resourceCollector, info.module, resName, condition); } } catch (IOException e) { @@ -173,24 +164,7 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso } } - private static String makeDirContent(Set allEntries, String dir) { - List dirContent = new ArrayList<>(); - for (String entry : allEntries) { - int last = entry.lastIndexOf(RESOURCES_INTERNAL_PATH_SEPARATOR); - String parentDirectory = last == -1 ? "" : entry.substring(0, last); - if (parentDirectory.equals(dir)) { - dirContent.add(entry.substring(last + 1)); - } - } - - dirContent.sort(Comparator.naturalOrder()); - return String.join(System.lineSeparator(), dirContent); - } - private static void scanDirectory(Path root, ResourceCollector collector, boolean includeAll) { - List matchedDirectoryResources = new ArrayList<>(); - Set allEntries = new HashSet<>(); - ArrayDeque queue = new ArrayDeque<>(); queue.push(root); while (!queue.isEmpty()) { @@ -198,21 +172,18 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea /* Resources always use / as the separator, as do our resource inclusion patterns */ String relativeFilePath; - List conditions; if (entry != root) { relativeFilePath = root.relativize(entry).toString().replace(File.separatorChar, RESOURCES_INTERNAL_PATH_SEPARATOR); - conditions = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeAll); - allEntries.add(relativeFilePath); } else { relativeFilePath = String.valueOf(RESOURCES_INTERNAL_PATH_SEPARATOR); - conditions = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeAll); } - if (Files.isDirectory(entry)) { - for (ConfigurationCondition condition : conditions) { - matchedDirectoryResources.add(new ConditionalResource(condition, relativeFilePath)); - } + List conditions = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeAll); + for (ConfigurationCondition condition : conditions) { + includeResource(collector, null, relativeFilePath, condition); + } + if (Files.isDirectory(entry)) { if (conditions.isEmpty()) { collector.registerNegativeQuery(null, relativeFilePath); } @@ -226,27 +197,8 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea } catch (IOException resourceException) { collector.registerIOException(null, relativeFilePath, resourceException, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(relativeFilePath)); } - } else { - for (ConfigurationCondition condition : conditions) { - if (ConfigurationCondition.isAlwaysTrue(condition)) { - collector.addResource(null, relativeFilePath); - } else { - collector.addResourceConditionally(null, relativeFilePath, condition); - } - } } } - - matchedDirectoryResources.forEach(entry -> { - ConfigurationCondition condition = entry.condition(); - String dir = entry.resourceName(); - String contentName = makeDirContent(allEntries, dir); - if (ConfigurationCondition.isAlwaysTrue(condition)) { - collector.addDirectoryResource(null, dir); - } else { - collector.addDirectoryResourceConditionally(null, dir, condition, contentName, false); - } - }); } private static void scanJar(Path jarPath, ResourceCollector collector, boolean includeAll) throws IOException { @@ -254,39 +206,33 @@ private static void scanJar(Path jarPath, ResourceCollector collector, boolean i Enumeration entries = jf.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); - URI uri = jarPath.toUri(); + String entryName = entry.getName(); if (entry.isDirectory()) { - String dirName = entry.getName().substring(0, entry.getName().length() - 1); - List conditions = shouldIncludeEntry(null, collector, dirName, jarPath.toUri(), includeAll); - for (ConfigurationCondition condition : conditions) { - // Register the directory with empty content to preserve Java behavior - if (ConfigurationCondition.isAlwaysTrue(condition)) { - collector.addDirectoryResource(null, dirName); - } else { - collector.addDirectoryResourceConditionally(null, dirName, condition, "", true); - } - } - } else { - List conditions = shouldIncludeEntry(null, collector, entry.getName(), jarPath.toUri(), includeAll); - for (ConfigurationCondition condition : conditions) { - if (ConfigurationCondition.isAlwaysTrue(condition)) { - collector.addResource(null, entry.getName()); - } else { - collector.addResourceConditionally(null, entry.getName(), condition); - } - } + entryName = entryName.substring(0, entry.getName().length() - 1); + } + + List conditions = shouldIncludeEntry(null, collector, entryName, jarPath.toUri(), includeAll); + for (ConfigurationCondition condition : conditions) { + includeResource(collector, null, entryName, condition); } } } } + private static void includeResource(ResourceCollector collector, Module module, String name, ConfigurationCondition condition) { + if (ConfigurationCondition.isAlwaysTrue(condition)) { + collector.addResource(module, name); + } else { + collector.addResourceConditionally(module, name, condition); + } + } + private static List shouldIncludeEntry(Module module, ResourceCollector collector, String fileName, URI uri, boolean includeAll) { - List conditions = collector.isIncluded(module, fileName, uri); if (includeAll && !(fileName.endsWith(".class") || fileName.endsWith(".jar"))) { return Collections.singletonList(ConfigurationCondition.alwaysTrue()); } - return conditions; + return collector.isIncluded(module, fileName, uri); } @Override 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 fc8a0036f1b1..2e45a088a2ca 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 @@ -94,7 +94,6 @@ import com.oracle.svm.core.jdk.resources.NativeImageResourceFileAttributesView; import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystem; import com.oracle.svm.core.jdk.resources.NativeImageResourceFileSystemProvider; -import com.oracle.svm.core.jdk.resources.ResourceStorageEntryBase; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.LocatableMultiOptionValue; import com.oracle.svm.core.util.UserError; @@ -458,7 +457,6 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { } private static final class ResourceCollectorImpl implements ResourceCollector { - private final DebugContext debugContext; private final Set includePatterns; private final ResourcePattern[] excludePatterns; @@ -471,7 +469,6 @@ private static final class ResourceCollectorImpl implements ResourceCollector { ScheduledExecutorService scheduledExecutor; private ResourceCollectorImpl(DebugContext debugContext, Set includePatterns, ResourcePattern[] excludePatterns) { - this.debugContext = debugContext; this.includePatterns = includePatterns; this.excludePatterns = excludePatterns; @@ -545,16 +542,6 @@ public void addResourceConditionally(Module module, String resourceName, Configu access.registerReachabilityHandler(e -> addResource(module, resourceName), access.findClassByName(condition.getTypeName())); } - @Override - public void addDirectoryResource(Module module, String dir) { - ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, dir); - } - - @Override - public void addDirectoryResourceConditionally(Module module, String dir, ConfigurationCondition condition, String content, boolean fromJar) { - access.registerReachabilityHandler(e -> addDirectoryResource(module, dir), access.findClassByName(condition.getTypeName())); - } - @Override public void registerIOException(Module module, String resourceName, IOException e, boolean linkAtBuildTime) { Resources.singleton().registerIOException(module, resourceName, e, linkAtBuildTime); @@ -609,22 +596,6 @@ boolean moduleNameMatches(String resourceContainerModuleName) { @Override public void afterAnalysis(AfterAnalysisAccess access) { sealed = true; - var entryIter = Resources.singleton().getResourceStorage().getEntries(); - while (entryIter.advance()) { - var key = entryIter.getKey(); - ResourceStorageEntryBase val = entryIter.getValue(); - String valStr; - if (val.isException()) { - valStr = val.getException().getClass().getName(); - } else if (val == Resources.NEGATIVE_QUERY_MARKER) { - valStr = "NEGATIVE_QUERY_MARKER"; - } else if (val == Resources.MISSING_METADATA_MARKER) { - valStr = "MISSING_METADATA_MARKER"; - } else { - valStr = "" + val.getData().stream().map(bytes -> bytes.length).reduce((a, b) -> a + b).get(); - } - System.out.println("RESOURCE: " + key.getRight() + ": " + valStr); - } } @Override From 45c27ed0c351ee4277591fba9c345c65763ec3ee Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Thu, 12 Oct 2023 18:20:39 +0200 Subject: [PATCH 41/54] Convert ResorucePattern class to record --- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) 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 2e45a088a2ca..625907df378c 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 @@ -571,14 +571,7 @@ private ResourcePattern makeResourcePattern(String rawPattern) { } } - private static final class ResourcePattern { - final String moduleName; - final Pattern pattern; - - private ResourcePattern(String moduleName, Pattern pattern) { - this.moduleName = moduleName; - this.pattern = pattern; - } + private record ResourcePattern(String moduleName, Pattern pattern) { boolean moduleNameMatches(String resourceContainerModuleName) { if (moduleName == null) { From 30e5c8613ca5a79ef9575cfebda9239673d70168 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Thu, 12 Oct 2023 18:22:49 +0200 Subject: [PATCH 42/54] Remove debugg context since it is not used anymore --- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 625907df378c..0f1de2be0283 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 @@ -439,8 +439,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { }); } ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns); - DebugContext debugContext = beforeAnalysisAccess.getDebugContext(); - ResourceCollectorImpl collector = new ResourceCollectorImpl(debugContext, includePatterns, excludePatterns); + ResourceCollectorImpl collector = new ResourceCollectorImpl(includePatterns, excludePatterns); try { collector.prepareProgressReporter(); ImageSingletons.lookup(ClassLoaderSupport.class).collectResources(collector); @@ -459,7 +458,6 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { private static final class ResourceCollectorImpl implements ResourceCollector { private final Set includePatterns; private final ResourcePattern[] excludePatterns; - private static final int WATCHDOG_RESET_AFTER_EVERY_N_RESOURCES = 1000; private static final int WATCHDOG_INITIAL_WARNING_AFTER_N_SECONDS = 60; private static final int WATCHDOG_WARNING_AFTER_EVERY_N_SECONDS = 20; @@ -468,7 +466,7 @@ private static final class ResourceCollectorImpl implements ResourceCollector { private volatile String currentlyProcessedEntry; ScheduledExecutorService scheduledExecutor; - private ResourceCollectorImpl(DebugContext debugContext, Set includePatterns, ResourcePattern[] excludePatterns) { + private ResourceCollectorImpl(Set includePatterns, ResourcePattern[] excludePatterns) { this.includePatterns = includePatterns; this.excludePatterns = excludePatterns; From c898d381f92c4c4a52c9a2db5a09497a1cefa61d Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Thu, 12 Oct 2023 18:33:51 +0200 Subject: [PATCH 43/54] Add explanation for shouldRegisterResource function --- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 0f1de2be0283..2cb1473355b2 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 @@ -236,6 +236,13 @@ public void addResourceBundles(ConfigurationCondition condition, String basename registerConditionalConfiguration(condition, () -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(basename, locales)); } + /* + * It is possible that one resource can be registered under different conditions + * (typeReachable). In some cases, few conditions will be satisfied, and we will try to + * register same resource for each satisfied condition. This function will check if the + * resource is already registered and prevent multiple registrations of same resource under + * different conditions + */ public boolean shouldRegisterResource(Module module, String resourceName) { if ((module == null || !module.isNamed())) { // addResources can be called from multiple threads so this should be synchronized From 0853a1c2be6048a0b6137ef6ba79816d05876e4b Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 13 Oct 2023 10:57:43 +0200 Subject: [PATCH 44/54] Add function that is never used just to pass gates --- .../src/com/oracle/svm/core/jdk/Resources.java | 5 +++++ 1 file changed, 5 insertions(+) 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 f7ec625c6ad2..c067c9a92506 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 @@ -198,6 +198,11 @@ private void addEntry(Module module, String resourceName, boolean isDirectory, b } } + @Platforms(Platform.HOSTED_ONLY.class) + public static void registerResource(String resourceName, InputStream is) { + singleton().registerResource(null, resourceName, is, true); + } + @Platforms(Platform.HOSTED_ONLY.class) public void registerResource(Module module, String resourceName, byte[] resourceContent) { addEntry(module, resourceName, false, resourceContent, true, false); From 56bdd98bbfb3780cc18460522b52136e4781b8ad Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 17 Oct 2023 14:35:46 +0200 Subject: [PATCH 45/54] Replace module export with module open access --- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 2cb1473355b2..50a83a082983 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 @@ -268,7 +268,8 @@ private void processResourceFromModule(Module module, String resourcePath) { if (!resourcePackage.isEmpty()) { // if processing resource package, make sure that module exports that package if (module.getPackages().contains(resourcePackage)) { - ModuleSupport.accessModuleByClass(ModuleSupport.Access.EXPORT, ResourcesFeature.class, module, resourcePackage); + // Use Access.OPEN to find ALL resources within resource package + ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, ResourcesFeature.class, module, resourcePackage); } } @@ -446,7 +447,7 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { }); } ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns); - ResourceCollectorImpl collector = new ResourceCollectorImpl(includePatterns, excludePatterns); + ResourceCollectorImpl collector = new ResourceCollectorImpl(includePatterns, excludePatterns, beforeAnalysisAccess); try { collector.prepareProgressReporter(); ImageSingletons.lookup(ClassLoaderSupport.class).collectResources(collector); @@ -468,15 +469,17 @@ private static final class ResourceCollectorImpl implements ResourceCollector { private static final int WATCHDOG_RESET_AFTER_EVERY_N_RESOURCES = 1000; private static final int WATCHDOG_INITIAL_WARNING_AFTER_N_SECONDS = 60; private static final int WATCHDOG_WARNING_AFTER_EVERY_N_SECONDS = 20; + private final FeatureImpl.BeforeAnalysisAccessImpl access; private final LongAdder reachedResourceEntries; private boolean initialReport; private volatile String currentlyProcessedEntry; ScheduledExecutorService scheduledExecutor; - private ResourceCollectorImpl(Set includePatterns, ResourcePattern[] excludePatterns) { + private ResourceCollectorImpl(Set includePatterns, ResourcePattern[] excludePatterns, FeatureImpl.BeforeAnalysisAccessImpl access) { this.includePatterns = includePatterns; this.excludePatterns = excludePatterns; + this.access = access; this.reachedResourceEntries = new LongAdder(); this.initialReport = true; this.currentlyProcessedEntry = null; From e1807e002d6f66de1045998d7fccb1d9939c0af7 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Fri, 20 Oct 2023 15:38:45 +0200 Subject: [PATCH 46/54] Update imports according to changes on master --- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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 50a83a082983..546a03e133a1 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 @@ -104,6 +104,15 @@ import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; +import jdk.compiler.graal.api.replacements.SnippetReflectionProvider; +import jdk.compiler.graal.nodes.ValueNode; +import jdk.compiler.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; +import jdk.compiler.graal.nodes.graphbuilderconf.GraphBuilderContext; +import jdk.compiler.graal.nodes.graphbuilderconf.InvocationPlugin; +import jdk.compiler.graal.nodes.graphbuilderconf.InvocationPlugins; +import jdk.compiler.graal.options.Option; +import jdk.compiler.graal.options.OptionType; +import jdk.compiler.graal.phases.util.Providers; import jdk.vm.ci.meta.ResolvedJavaMethod; /** From ec0913fa5862c7961d5fed14d1192e09d848f568 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 24 Oct 2023 13:46:06 +0200 Subject: [PATCH 47/54] Inline function and remove passing null for findModule function --- ...undleContentSubstitutedLocalizationSupport.java | 3 ++- .../localization/OptimizedLocalizationSupport.java | 14 ++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java index 10014521a9ce..1b01e1839001 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java @@ -51,6 +51,7 @@ import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; +import jdk.compiler.graal.debug.GraalError; import sun.util.resources.OpenListResourceBundle; import sun.util.resources.ParallelListResourceBundle; @@ -175,7 +176,7 @@ public boolean isNotIncluded(String bundleName) { @Override public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule, Locale locale) { - super.prepareBundle(bundleName, bundle, null, locale); + super.prepareBundle(bundleName, bundle, findModule, locale); /* Initialize ResourceBundle.keySet eagerly */ bundle.keySet(); this.existingBundles.add(control.toBundleName(bundleName, locale)); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java index 15d185dff739..b24464c5f603 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java @@ -30,15 +30,11 @@ import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; -import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; -import java.util.function.Function; import java.util.spi.LocaleServiceProvider; import org.graalvm.collections.Pair; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.util.UserError; import com.oracle.svm.util.ReflectionUtil; @@ -91,7 +87,8 @@ public void prepareClassResourceBundle(String basename, Class bundleClass) { /*- Set the basename and locale to be consistent with JVM lookup process */ bundleNameField.set(bundle, basename); bundleLocaleField.set(bundle, locale); - prepareBundle(basename, bundle, null, locale); + bundle.keySet(); + this.resourceBundles.put(Pair.create(basename, locale), bundle); } catch (ReflectionUtil.ReflectionUtilError | ReflectiveOperationException e) { throw UserError.abort(e, "Failed to instantiated bundle from class %s, reason %s", bundleClass, e.getCause().getMessage()); } @@ -106,13 +103,6 @@ private static Locale extractLocale(Class bundleClass) { return parseLocaleFromTag(name.substring(split + 1)); } - @Platforms(Platform.HOSTED_ONLY.class) - @Override - public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule, Locale locale) { - bundle.keySet(); - this.resourceBundles.put(Pair.create(bundleName, locale), bundle); - } - @Override public boolean shouldSubstituteLoadLookup(String className) { /*- All bundles are stored in the image heap as objects, no need to keep the content around */ From 9de65c8a0e3f97cb7c349a436eef5d84b07ccbce Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 24 Oct 2023 14:35:32 +0200 Subject: [PATCH 48/54] Avoid using synchronized block all the time --- .../svm/hosted/ClassLoaderSupportImpl.java | 4 ++-- .../com/oracle/svm/hosted/ResourcesFeature.java | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) 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 3b61ab842bee..ce50ed0fda14 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 @@ -93,7 +93,7 @@ protected boolean isNativeImageClassLoaderImpl(ClassLoader loader) { return false; } - public record ResourceLookupInfo(ResolvedModule resolvedModule, Module module) { + private record ResourceLookupInfo(ResolvedModule resolvedModule, Module module) { } private static Stream extractModuleLookupData(ModuleLayer layer) { @@ -110,8 +110,8 @@ private static Stream extractModuleLookupData(ModuleLayer la public void collectResources(ResourceCollector resourceCollector) { /* Collect resources from modules */ NativeImageClassLoaderSupport.allLayers(classLoaderSupport.moduleLayerForImageBuild).stream() - .parallel() .flatMap(ClassLoaderSupportImpl::extractModuleLookupData) + .parallel() .forEach(lookup -> collectResourceFromModule(resourceCollector, lookup)); /* Collect remaining resources from classpath */ 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 546a03e133a1..b6d4e8593db8 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 @@ -253,20 +253,29 @@ public void addResourceBundles(ConfigurationCondition condition, String basename * different conditions */ public boolean shouldRegisterResource(Module module, String resourceName) { + // we only do this if we are on the classPath if ((module == null || !module.isNamed())) { - // addResources can be called from multiple threads so this should be synchronized + /* + * This check is not thread safe! If the resource is not added yet, maybe some other + * thread is attempting to do it, so we have to perform same check again in + * synchronized block (in that case). Anyway this check will cut the case when we + * are sure that resource is added (so we don't need to enter synchronized block) + */ + if (alreadyAddedResources.contains(resourceName)) { + return false; + } + + // addResources can be called from multiple threads synchronized (alreadyAddedResources) { - // we only do this if we are on the classPath if (!alreadyAddedResources.contains(resourceName)) { alreadyAddedResources.add(resourceName); return true; } else { return false; } - } } else { - // always try to register module entries (duplicates checked later in addEntries) + // always try to register module entries (we will check duplicates in addEntries) return true; } } From 2da282fd7a716a62dd8fc0ba40266bd8f96941df Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 24 Oct 2023 14:56:02 +0200 Subject: [PATCH 49/54] Replace multiple calls of string transformation with function --- .../jdk/localization/LocalizationSupport.java | 15 ++++++++++----- .../src/com/oracle/svm/util/StringUtil.java | 8 ++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) 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 fa5cf7ae302a..47c52c0f8bfd 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 @@ -25,6 +25,9 @@ package com.oracle.svm.core.jdk.localization; +import static com.oracle.svm.util.StringUtil.toDotSeparated; +import static com.oracle.svm.util.StringUtil.toSlashSeparated; + import java.nio.charset.Charset; import java.util.Collection; import java.util.Collections; @@ -53,6 +56,8 @@ import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.util.VMError; +import jdk.compiler.graal.debug.GraalError; + /** * Holder for localization information that is computed during image generation and used at run * time. @@ -107,7 +112,7 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function> packageToModules = ImageSingletons.lookup(ClassLoaderSupport.class).getPackageToModules(); Set modules = packageToModules.getOrDefault(packageName(bundleName), Collections.emptySet()); @@ -121,7 +126,7 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function module = findModule.apply(bundleNameWithModule[0]); String finalResourceName = resourceName; module.ifPresent(m -> ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(m, finalResourceName)); @@ -136,7 +141,7 @@ public void prepareBundle(String bundleName, ResourceBundle bundle, Function strings) { return joinSingleQuoted(strings.toArray(new String[strings.size()])); } + + public static String toSlashSeparated(String string) { + return string.replace('.', '/'); + } + + public static String toDotSeparated(String string) { + return string.replace('/', '.'); + } } From 4b9c4d7839a1b5e63c81a6a46fbbd70bb1d14852 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 24 Oct 2023 15:36:01 +0200 Subject: [PATCH 50/54] Extract directory registration handling form registerResoruce function --- .../oracle/svm/hosted/ResourcesFeature.java | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) 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 b6d4e8593db8..bf411eb6d07f 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 @@ -27,7 +27,6 @@ import static com.oracle.svm.core.jdk.Resources.RESOURCES_INTERNAL_PATH_SEPARATOR; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; @@ -292,13 +291,13 @@ private void processResourceFromModule(Module module, String resourcePath) { } InputStream is = module.getResourceAsStream(resourcePath); - boolean isDirectory = new File(resourcePath).isDirectory(); - String content = ""; + boolean isDirectory = Files.isDirectory(Path.of(resourcePath)); if (isDirectory) { - content = getDirectoryContent(resourcePath, false); + String content = getDirectoryContent(resourcePath, false); + Resources.singleton().registerDirectoryResource(module, resourcePath, content, false); + } else { + registerResource(module, resourcePath, false, is); } - - registerResource(module, resourcePath, isDirectory, false, is, content); } catch (IOException e) { // we should ignore if user failed to provide resource } @@ -314,7 +313,7 @@ private void processResourceFromClasspath(Module module, String resourcePath) { */ urls = imageClassLoader.getClassLoader().getResources(resourcePath); } catch (IOException e) { - throw new RuntimeException(e); + throw VMError.shouldNotReachHere("getResources for resourcePath " + resourcePath + " failed", e); } while (urls.hasMoreElements()) { @@ -323,31 +322,27 @@ private void processResourceFromClasspath(Module module, String resourcePath) { InputStream is = url.openStream(); boolean fromJar = url.getProtocol().equalsIgnoreCase("jar"); boolean isDirectory = resourceIsDirectory(url, fromJar, resourcePath); - String content = ""; if (isDirectory) { - content = getDirectoryContent(fromJar ? url.toString() : Paths.get(url.toURI()).toString(), fromJar); + String content = getDirectoryContent(fromJar ? url.toString() : Paths.get(url.toURI()).toString(), fromJar); + Resources.singleton().registerDirectoryResource(module, resourcePath, content, fromJar); + } else { + registerResource(module, resourcePath, fromJar, is); } - - registerResource(module, resourcePath, isDirectory, fromJar, is, content); } catch (IOException e) { // we should ignore if user failed to provide resource return; } catch (URISyntaxException e) { - throw new RuntimeException(e); + throw VMError.shouldNotReachHere("resourceIsDirectory for resourcePath " + resourcePath + " failed", e); } } } - private void registerResource(Module module, String resourcePath, boolean isDirectory, boolean fromJar, InputStream is, String content) { + private void registerResource(Module module, String resourcePath, boolean fromJar, InputStream is) { if (is == null) { return; } - if (isDirectory) { - Resources.singleton().registerDirectoryResource(module, resourcePath, content, fromJar); - } else { - Resources.singleton().registerResource(module, resourcePath, is, fromJar); - } + Resources.singleton().registerResource(module, resourcePath, is, fromJar); try { is.close(); @@ -525,8 +520,6 @@ private void shutDownProgressReporter() { @Override public List isIncluded(Module module, String resourceName, URI resource) { - // Possibly we can have multiple conditions for one resource - List conditions = new ArrayList<>(); this.currentlyProcessedEntry = resource.getScheme().equals("jrt") ? (resource + "/" + resourceName) : resource.toString(); this.reachedResourceEntries.increment(); @@ -542,10 +535,12 @@ public List isIncluded(Module module, String resourceNam continue; } if (rp.pattern.matcher(resourceName).matches() || rp.pattern.matcher(relativePathWithTrailingSlash).matches()) { - return conditions; // returns empty list + return List.of(); // nothing should match excluded resource } } + // Possibly we can have multiple conditions for one resource + List conditions = new ArrayList<>(); for (CompiledConditionalPattern rp : includePatterns) { if (!rp.compiledPattern().moduleNameMatches(moduleName)) { continue; From f54f22cf4dc98787aab755619bb87111b9b54d3e Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 24 Oct 2023 18:02:59 +0200 Subject: [PATCH 51/54] Rebase and fix imports --- ...ContentSubstitutedLocalizationSupport.java | 3 +- .../jdk/localization/LocalizationSupport.java | 3 +- .../oracle/svm/hosted/ResourcesFeature.java | 28 ++++++------------- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java index 1b01e1839001..a178d797270b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java @@ -39,7 +39,6 @@ import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import jdk.graal.compiler.debug.GraalError; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -51,7 +50,7 @@ import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; -import jdk.compiler.graal.debug.GraalError; +import jdk.graal.compiler.debug.GraalError; import sun.util.resources.OpenListResourceBundle; import sun.util.resources.ParallelListResourceBundle; 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 47c52c0f8bfd..a036d9c5520a 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 @@ -42,7 +42,6 @@ import java.util.function.Function; import java.util.stream.Collectors; -import jdk.graal.compiler.debug.GraalError; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -56,7 +55,7 @@ import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.util.VMError; -import jdk.compiler.graal.debug.GraalError; +import jdk.graal.compiler.debug.GraalError; /** * Holder for localization information that is computed during image generation and used at run 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 bf411eb6d07f..8cc36bcf973e 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 @@ -62,16 +62,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; -import jdk.graal.compiler.debug.DebugContext; -import jdk.graal.compiler.nodes.ValueNode; -import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; -import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext; -import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugin; -import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins; -import jdk.graal.compiler.options.Option; -import jdk.graal.compiler.options.OptionType; -import jdk.graal.compiler.phases.util.Providers; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.RuntimeResourceAccess; import org.graalvm.nativeimage.impl.ConfigurationCondition; @@ -103,15 +93,15 @@ import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; -import jdk.compiler.graal.api.replacements.SnippetReflectionProvider; -import jdk.compiler.graal.nodes.ValueNode; -import jdk.compiler.graal.nodes.graphbuilderconf.GraphBuilderConfiguration; -import jdk.compiler.graal.nodes.graphbuilderconf.GraphBuilderContext; -import jdk.compiler.graal.nodes.graphbuilderconf.InvocationPlugin; -import jdk.compiler.graal.nodes.graphbuilderconf.InvocationPlugins; -import jdk.compiler.graal.options.Option; -import jdk.compiler.graal.options.OptionType; -import jdk.compiler.graal.phases.util.Providers; +import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; +import jdk.graal.compiler.nodes.ValueNode; +import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; +import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext; +import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugin; +import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins; +import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.options.OptionType; +import jdk.graal.compiler.phases.util.Providers; import jdk.vm.ci.meta.ResolvedJavaMethod; /** From 9f793ce233a5c07de40228df0ed35dba19ba0bcc Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Tue, 24 Oct 2023 19:42:31 +0200 Subject: [PATCH 52/54] Rename includeAll variable to includeCurrent --- .../svm/hosted/ClassLoaderSupportImpl.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) 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 ce50ed0fda14..433b0680c4b1 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 @@ -116,12 +116,12 @@ public void collectResources(ResourceCollector resourceCollector) { /* Collect remaining resources from classpath */ classLoaderSupport.classpath().stream().parallel().forEach(classpathFile -> { - boolean includeAll = classLoaderSupport.getJavaPathsToInclude().contains(classpathFile); + boolean includeCurrent = classLoaderSupport.getJavaPathsToInclude().contains(classpathFile); try { if (Files.isDirectory(classpathFile)) { - scanDirectory(classpathFile, resourceCollector, includeAll); + scanDirectory(classpathFile, resourceCollector, includeCurrent); } else if (ClasspathUtils.isJar(classpathFile)) { - scanJar(classpathFile, resourceCollector, includeAll); + scanJar(classpathFile, resourceCollector, includeCurrent); } } catch (IOException ex) { throw UserError.abort("Unable to handle classpath element '%s'. Make sure that all classpath entries are either directories or valid jar files.", classpathFile); @@ -132,10 +132,10 @@ public void collectResources(ResourceCollector resourceCollector) { private void collectResourceFromModule(ResourceCollector resourceCollector, ResourceLookupInfo info) { ModuleReference moduleReference = info.resolvedModule.reference(); try (ModuleReader moduleReader = moduleReference.open()) { - boolean includeAll = classLoaderSupport.getJavaModuleNamesToInclude().contains(info.resolvedModule().name()); + 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), includeAll); + List conditions = shouldIncludeEntry(info.module, resourceCollector, resourceName, moduleReference.location().orElse(null), includeCurrent); for (ConfigurationCondition condition : conditions) { resourcesFound.add(new ConditionalResource(condition, resourceName)); } @@ -164,7 +164,7 @@ private void collectResourceFromModule(ResourceCollector resourceCollector, Reso } } - private static void scanDirectory(Path root, ResourceCollector collector, boolean includeAll) { + private static void scanDirectory(Path root, ResourceCollector collector, boolean includeCurrent) { ArrayDeque queue = new ArrayDeque<>(); queue.push(root); while (!queue.isEmpty()) { @@ -178,7 +178,7 @@ 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(), includeAll); + List conditions = shouldIncludeEntry(null, collector, relativeFilePath, Path.of(relativeFilePath).toUri(), includeCurrent); for (ConfigurationCondition condition : conditions) { includeResource(collector, null, relativeFilePath, condition); } @@ -201,7 +201,7 @@ private static void scanDirectory(Path root, ResourceCollector collector, boolea } } - private static void scanJar(Path jarPath, ResourceCollector collector, boolean includeAll) throws IOException { + private static void scanJar(Path jarPath, ResourceCollector collector, boolean includeCurrent) throws IOException { try (JarFile jf = new JarFile(jarPath.toFile())) { Enumeration entries = jf.entries(); while (entries.hasMoreElements()) { @@ -211,7 +211,7 @@ 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(), includeAll); + List conditions = shouldIncludeEntry(null, collector, entryName, jarPath.toUri(), includeCurrent); for (ConfigurationCondition condition : conditions) { includeResource(collector, null, entryName, condition); } @@ -227,8 +227,8 @@ private static void includeResource(ResourceCollector collector, Module module, } } - private static List shouldIncludeEntry(Module module, ResourceCollector collector, String fileName, URI uri, boolean includeAll) { - if (includeAll && !(fileName.endsWith(".class") || fileName.endsWith(".jar"))) { + 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()); } From e006dd45360379ca6e28e90808d0aa45c1e64bd5 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 25 Oct 2023 10:55:19 +0200 Subject: [PATCH 53/54] Use Resources#registerIOException instead of ignoring exception --- .../src/com/oracle/svm/core/jdk/Resources.java | 4 ++-- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) 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 c067c9a92506..069e34e89dfe 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 @@ -227,9 +227,9 @@ public void registerDirectoryResource(Module module, String resourceDirName, Str public void registerIOException(Module module, String resourceName, IOException e, boolean linkAtBuildTime) { if (linkAtBuildTime) { if (SubstrateOptions.ThrowLinkAtBuildTimeIOExceptions.getValue()) { - throw new RuntimeException("Resource " + resourceName + " from module " + module.getName() + " produced an IOException.", e); + throw new RuntimeException("Resource " + resourceName + " from module " + moduleName(module) + " produced an IOException.", e); } else { - LogUtils.warning("Resource " + resourceName + " from module " + module.getName() + " produced the following IOException: " + e.getClass().getTypeName() + ": " + e.getMessage()); + LogUtils.warning("Resource " + resourceName + " from module " + moduleName(module) + " produced the following IOException: " + e.getClass().getTypeName() + ": " + e.getMessage()); } } Pair key = createStorageKey(module, resourceName); 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 8cc36bcf973e..f46a8f9c3d77 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 @@ -189,7 +189,7 @@ public void addResource(Module module, String resourcePath) { if (module != null && module.isNamed()) { processResourceFromModule(module, resourcePath); } else { - processResourceFromClasspath(module, resourcePath); + processResourceFromClasspath(resourcePath); } } @@ -289,11 +289,11 @@ private void processResourceFromModule(Module module, String resourcePath) { registerResource(module, resourcePath, false, is); } } catch (IOException e) { - // we should ignore if user failed to provide resource + Resources.singleton().registerIOException(module, resourcePath, e, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(resourcePath)); } } - private void processResourceFromClasspath(Module module, String resourcePath) { + private void processResourceFromClasspath(String resourcePath) { Enumeration urls; try { /* @@ -314,12 +314,12 @@ private void processResourceFromClasspath(Module module, String resourcePath) { boolean isDirectory = resourceIsDirectory(url, fromJar, resourcePath); if (isDirectory) { String content = getDirectoryContent(fromJar ? url.toString() : Paths.get(url.toURI()).toString(), fromJar); - Resources.singleton().registerDirectoryResource(module, resourcePath, content, fromJar); + Resources.singleton().registerDirectoryResource(null, resourcePath, content, fromJar); } else { - registerResource(module, resourcePath, fromJar, is); + registerResource(null, resourcePath, fromJar, is); } } catch (IOException e) { - // we should ignore if user failed to provide resource + Resources.singleton().registerIOException(null, resourcePath, e, LinkAtBuildTimeSupport.singleton().packageOrClassAtBuildTime(resourcePath)); return; } catch (URISyntaxException e) { throw VMError.shouldNotReachHere("resourceIsDirectory for resourcePath " + resourcePath + " failed", e); From b55218140f03522cafea2b746899f30eafa7a3ee Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Wed, 25 Oct 2023 17:29:41 +0200 Subject: [PATCH 54/54] Revert overridden prepareBundle function because it can be called from other places --- .../OptimizedLocalizationSupport.java | 16 ++++++++++++++-- .../jdk/localization/LocalizationFeature.java | 12 ++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java index b24464c5f603..5a8a29f4a1b4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java @@ -30,11 +30,15 @@ import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; +import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; +import java.util.function.Function; import java.util.spi.LocaleServiceProvider; import org.graalvm.collections.Pair; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.util.UserError; import com.oracle.svm.util.ReflectionUtil; @@ -87,13 +91,21 @@ public void prepareClassResourceBundle(String basename, Class bundleClass) { /*- Set the basename and locale to be consistent with JVM lookup process */ bundleNameField.set(bundle, basename); bundleLocaleField.set(bundle, locale); - bundle.keySet(); - this.resourceBundles.put(Pair.create(basename, locale), bundle); + + // override in this class does not use findModule + prepareBundle(basename, bundle, null, locale); } catch (ReflectionUtil.ReflectionUtilError | ReflectiveOperationException e) { throw UserError.abort(e, "Failed to instantiated bundle from class %s, reason %s", bundleClass, e.getCause().getMessage()); } } + @Platforms(Platform.HOSTED_ONLY.class) + @Override + public void prepareBundle(String bundleName, ResourceBundle bundle, Function> findModule, Locale locale) { + bundle.keySet(); + this.resourceBundles.put(Pair.create(bundleName, locale), bundle); + } + private static Locale extractLocale(Class bundleClass) { String name = bundleClass.getName(); int split = name.lastIndexOf('_'); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index 7aabef6e0565..89dc8f45fdcf 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -61,12 +61,6 @@ import java.util.stream.Collectors; import org.graalvm.collections.Pair; -import jdk.graal.compiler.nodes.ValueNode; -import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext; -import jdk.graal.compiler.nodes.graphbuilderconf.NodePlugin; -import jdk.graal.compiler.options.Option; -import jdk.graal.compiler.options.OptionStability; -import jdk.graal.compiler.options.OptionType; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -91,6 +85,12 @@ import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.ImageClassLoader; +import jdk.graal.compiler.nodes.ValueNode; +import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext; +import jdk.graal.compiler.nodes.graphbuilderconf.NodePlugin; +import jdk.graal.compiler.options.Option; +import jdk.graal.compiler.options.OptionStability; +import jdk.graal.compiler.options.OptionType; import jdk.internal.access.SharedSecrets; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod;