diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeReflection.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeReflection.java index bdc7321ecb6e..2d20addae50f 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeReflection.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeReflection.java @@ -69,7 +69,7 @@ public final class RuntimeReflection { * @since 19.0 */ public static void register(Class... classes) { - ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationCondition.objectReachable(), classes); + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationCondition.alwaysTrue(), classes); } /** @@ -80,7 +80,7 @@ public static void register(Class... classes) { * @since 19.0 */ public static void register(Executable... methods) { - ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationCondition.objectReachable(), methods); + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationCondition.alwaysTrue(), methods); } /** @@ -91,7 +91,7 @@ public static void register(Executable... methods) { * @since 19.0 */ public static void register(Field... fields) { - ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationCondition.objectReachable(), false, fields); + ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationCondition.alwaysTrue(), false, fields); } /** diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeSerialization.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeSerialization.java index 738d5e0fea9c..93e75536d8e2 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeSerialization.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeSerialization.java @@ -61,7 +61,7 @@ public final class RuntimeSerialization { * @since 21.3 */ public static void register(Class... classes) { - ImageSingletons.lookup(RuntimeSerializationSupport.class).register(ConfigurationCondition.objectReachable(), classes); + ImageSingletons.lookup(RuntimeSerializationSupport.class).register(ConfigurationCondition.alwaysTrue(), classes); } /** @@ -76,7 +76,7 @@ public static void register(Class... classes) { * @since 21.3 */ public static void registerWithTargetConstructorClass(Class clazz, Class customTargetConstructorClazz) { - ImageSingletons.lookup(RuntimeSerializationSupport.class).registerWithTargetConstructorClass(ConfigurationCondition.objectReachable(), clazz, customTargetConstructorClazz); + ImageSingletons.lookup(RuntimeSerializationSupport.class).registerWithTargetConstructorClass(ConfigurationCondition.alwaysTrue(), clazz, customTargetConstructorClazz); } private RuntimeSerialization() { 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 a1fa9d6d8538..72a76b2d2718 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 @@ -46,7 +46,7 @@ public final class ConfigurationCondition implements Comparable getMethodsMap(ConfigurationMem } void populateConfig() { - ConfigurationType oldType = new ConfigurationType(ConfigurationCondition.objectReachable(), getTypeName()); + ConfigurationType oldType = new ConfigurationType(ConfigurationCondition.alwaysTrue(), getTypeName()); setFlags(oldType); previousConfig.add(oldType); - ConfigurationType newType = new ConfigurationType(ConfigurationCondition.objectReachable(), getTypeName()); + ConfigurationType newType = new ConfigurationType(ConfigurationCondition.alwaysTrue(), getTypeName()); for (Map.Entry methodEntry : methodsThatMustExist.entrySet()) { newType.addMethod(methodEntry.getKey().getName(), methodEntry.getKey().getInternalSignature(), methodEntry.getValue()); } @@ -296,7 +298,7 @@ String getTypeName() { void doTest() { String name = getTypeName(); - ConfigurationType configurationType = currentConfig.get(ConfigurationCondition.objectReachable(), name); + ConfigurationType configurationType = currentConfig.get(ConfigurationCondition.alwaysTrue(), name); if (methodsThatMustExist.size() == 0) { Assert.assertNull("Generated configuration type " + name + " exists. Expected it to be cleared as it is empty.", configurationType); } else { 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 523ee4ac3057..808f84764b44 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 @@ -31,6 +31,7 @@ import java.util.LinkedList; import java.util.List; +import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.junit.Assert; import org.junit.Test; @@ -44,14 +45,15 @@ public class ResourceConfigurationTest { @Test public void anyResourceMatches() { ResourceConfiguration rc = new ResourceConfiguration(); - rc.addResourcePattern(".*/Resource.*txt$"); + ConfigurationCondition defaultCond = ConfigurationCondition.alwaysTrue(); + rc.addResourcePattern(defaultCond, ".*/Resource.*txt$"); Assert.assertTrue(rc.anyResourceMatches("com/my/app/Resource0.txt")); Assert.assertTrue(rc.anyResourceMatches("com/my/app/Resource1.txt")); Assert.assertTrue(rc.anyResourceMatches("/Resource2.txt")); Assert.assertTrue(rc.anyResourceMatches("/Resource3.txt")); - rc.ignoreResourcePattern(".*/Resource2.txt$"); + rc.ignoreResourcePattern(defaultCond, ".*/Resource2.txt$"); Assert.assertTrue(rc.anyResourceMatches("com/my/app/Resource0.txt")); Assert.assertTrue(rc.anyResourceMatches("com/my/app/Resource1.txt")); @@ -62,28 +64,20 @@ public void anyResourceMatches() { @Test public void printJson() { ResourceConfiguration rc = new ResourceConfiguration(); - rc.addResourcePattern(".*/Resource.*txt$"); - rc.ignoreResourcePattern(".*/Resource2.txt$"); + ConfigurationCondition defaultCond = ConfigurationCondition.alwaysTrue(); + rc.addResourcePattern(defaultCond, ".*/Resource.*txt$"); + rc.ignoreResourcePattern(defaultCond, ".*/Resource2.txt$"); PipedWriter pw = new PipedWriter(); JsonWriter jw = new JsonWriter(pw); try (PipedReader pr = new PipedReader()) { pr.connect(pw); - Thread writerThread = new Thread(new Runnable() { - - @Override - public void run() { - try { - rc.printJson(jw); - } catch (IOException e) { - Assert.fail(e.getMessage()); - } finally { - try { - jw.close(); - } catch (IOException e) { - } - } + Thread writerThread = new Thread(() -> { + try (JsonWriter w = jw) { + rc.printJson(w); + } catch (IOException e) { + Assert.fail(e.getMessage()); } }); @@ -93,17 +87,17 @@ public void run() { ResourcesRegistry registry = new ResourcesRegistry() { @Override - public void addResources(String pattern) { + public void addResources(ConfigurationCondition condition, String pattern) { addedResources.add(pattern); } @Override - public void ignoreResources(String pattern) { + public void ignoreResources(ConfigurationCondition condition, String pattern) { ignoredResources.add(pattern); } @Override - public void addResourceBundles(String name) { + public void addResourceBundles(ConfigurationCondition condition, String name) { } }; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationConditionPrintable.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationConditionPrintable.java index 9cb6d7164bb6..651f8a363a52 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationConditionPrintable.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationConditionPrintable.java @@ -35,7 +35,7 @@ final class ConfigurationConditionPrintable { static void printConditionAttribute(ConfigurationCondition condition, JsonWriter writer) throws IOException { - if (!condition.equals(ConfigurationCondition.objectReachable())) { + if (!condition.equals(ConfigurationCondition.alwaysTrue())) { writer.quote(CONDITIONAL_KEY).append(":{"); writer.quote(TYPE_REACHABLE_KEY).append(':').quote(condition.getTypeName()); writer.append("},").newline(); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationSet.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationSet.java index 92d5e54ddc5e..bf2340a05fc7 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationSet.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationSet.java @@ -30,7 +30,7 @@ import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.Set; @@ -112,7 +112,7 @@ public TypeConfiguration loadReflectConfig(Function exce public ProxyConfiguration loadProxyConfig(Function exceptionHandler) throws Exception { ProxyConfiguration proxyConfiguration = new ProxyConfiguration(); - loadConfig(proxyConfigPaths, new ProxyConfigurationParser(types -> proxyConfiguration.add(Arrays.asList(types)), true), exceptionHandler); + loadConfig(proxyConfigPaths, new ProxyConfigurationParser(types -> proxyConfiguration.add(types.getCondition(), new ArrayList<>(types.getElement())), true), exceptionHandler); return proxyConfiguration; } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ProxyConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ProxyConfiguration.java index db0f73893c1e..12f7af1d12bb 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ProxyConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ProxyConfiguration.java @@ -30,31 +30,34 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; +import org.graalvm.nativeimage.impl.ConfigurationCondition; + import com.oracle.svm.configure.ConfigurationBase; import com.oracle.svm.configure.json.JsonWriter; +import com.oracle.svm.core.configure.ConditionalElement; public class ProxyConfiguration implements ConfigurationBase { - private final ConcurrentHashMap.KeySetView, Boolean> interfaceLists = ConcurrentHashMap.newKeySet(); + private final ConcurrentHashMap.KeySetView>, Boolean> interfaceLists = ConcurrentHashMap.newKeySet(); public ProxyConfiguration() { } public ProxyConfiguration(ProxyConfiguration other) { - for (List interfaceList : other.interfaceLists) { - interfaceLists.add(new ArrayList<>(interfaceList)); + for (ConditionalElement> interfaceList : other.interfaceLists) { + interfaceLists.add(new ConditionalElement<>(interfaceList.getCondition(), new ArrayList<>(interfaceList.getElement()))); } } - public void add(List interfaceList) { - interfaceLists.add(interfaceList); + public void add(ConfigurationCondition condition, List interfaceList) { + interfaceLists.add(new ConditionalElement<>(condition, interfaceList)); } - public boolean contains(List interfaceList) { - return interfaceLists.contains(interfaceList); + public boolean contains(ConfigurationCondition condition, List interfaceList) { + return interfaceLists.contains(new ConditionalElement<>(condition, interfaceList)); } - public boolean contains(String... interfaces) { - return contains(Arrays.asList(interfaces)); + public boolean contains(ConfigurationCondition condition, String... interfaces) { + return contains(condition, Arrays.asList(interfaces)); } public void removeAll(ProxyConfiguration other) { @@ -63,29 +66,25 @@ public void removeAll(ProxyConfiguration other) { @Override public void printJson(JsonWriter writer) throws IOException { - List lists = new ArrayList<>(interfaceLists.size()); - for (List list : interfaceLists) { - lists.add(list.toArray(new String[0])); - } - lists.sort((a, b) -> { - int c = 0; - for (int i = 0; c == 0 && i < a.length && i < b.length; i++) { - c = a[i].compareTo(b[i]); - } - return (c != 0) ? c : (a.length - b.length); - }); + List>> lists = new ArrayList<>(interfaceLists.size()); + lists.addAll(interfaceLists); + lists.sort(ConditionalElement.comparator(ProxyConfiguration::compareList)); writer.append('['); writer.indent(); String prefix = ""; - for (String[] list : lists) { - writer.append(prefix).newline().append('['); + for (ConditionalElement> list : lists) { + writer.append(prefix).newline(); + writer.append('{').indent().newline(); + ConfigurationConditionPrintable.printConditionAttribute(list.getCondition(), writer); + writer.quote("interfaces").append(":").append('['); String typePrefix = ""; - for (String type : list) { + for (String type : list.getElement()) { writer.append(typePrefix).quote(type); typePrefix = ","; } writer.append(']'); + writer.append('}').unindent().newline(); prefix = ","; } writer.unindent().newline(); @@ -97,4 +96,14 @@ public boolean isEmpty() { return interfaceLists.isEmpty(); } + private static > int compareList(List l1, List l2) { + for (int i = 0; i < l1.size() && i < l2.size(); i++) { + int c = l1.get(i).compareTo(l2.get(i)); + if (c != 0) { + return c; + } + } + return l1.size() - l2.size(); + } + } 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 602f1fa5d61e..0ec183a4c191 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 @@ -25,44 +25,49 @@ package com.oracle.svm.configure.config; import java.io.IOException; -import java.util.Comparator; import java.util.concurrent.ConcurrentHashMap; 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.configure.json.JsonPrinter; import com.oracle.svm.configure.json.JsonWriter; +import com.oracle.svm.core.configure.ConditionalElement; import com.oracle.svm.core.configure.ResourcesRegistry; public class ResourceConfiguration implements ConfigurationBase { public static class ParserAdapter implements ResourcesRegistry { + private final ResourceConfiguration configuration; - public ParserAdapter(ResourceConfiguration configuration) { + ParserAdapter(ResourceConfiguration configuration) { this.configuration = configuration; } @Override - public void addResources(String pattern) { - configuration.addResourcePattern(pattern); + public void addResources(ConfigurationCondition condition, String pattern) { + configuration.addResourcePattern(condition, pattern); } @Override - public void ignoreResources(String pattern) { - configuration.ignoreResourcePattern(pattern); + public void ignoreResources(ConfigurationCondition condition, String pattern) { + configuration.ignoreResourcePattern(condition, pattern); } @Override - public void addResourceBundles(String name) { - configuration.addBundle(name); + public void addResourceBundles(ConfigurationCondition condition, String name) { + configuration.addBundle(condition, name); } + } - private final ConcurrentMap addedResources = new ConcurrentHashMap<>(); - private final ConcurrentMap ignoredResources = new ConcurrentHashMap<>(); - private final ConcurrentHashMap.KeySetView bundles = ConcurrentHashMap.newKeySet(); + private final ConcurrentMap, Pattern> addedResources = new ConcurrentHashMap<>(); + + private final ConcurrentMap, Pattern> ignoredResources = new ConcurrentHashMap<>(); + private final ConcurrentHashMap.KeySetView, Boolean> bundles = ConcurrentHashMap.newKeySet(); public ResourceConfiguration() { } @@ -79,16 +84,16 @@ public void removeAll(ResourceConfiguration other) { bundles.removeAll(other.bundles); } - public void addResourcePattern(String pattern) { - addedResources.computeIfAbsent(pattern, Pattern::compile); + public void addResourcePattern(ConfigurationCondition condition, String pattern) { + addedResources.computeIfAbsent(new ConditionalElement<>(condition, pattern), p -> Pattern.compile(p.getElement())); } - public void ignoreResourcePattern(String pattern) { - ignoredResources.computeIfAbsent(pattern, Pattern::compile); + public void ignoreResourcePattern(ConfigurationCondition condition, String pattern) { + ignoredResources.computeIfAbsent(new ConditionalElement<>(condition, pattern), p -> Pattern.compile(p.getElement())); } - public void addBundle(String bundle) { - bundles.add(bundle); + public void addBundle(ConfigurationCondition condition, String bundle) { + bundles.add(new ConditionalElement<>(condition, bundle)); } public boolean anyResourceMatches(String s) { @@ -109,8 +114,8 @@ public boolean anyResourceMatches(String s) { return false; } - public boolean anyBundleMatches(String s) { - return bundles.contains(s); + public boolean anyBundleMatches(ConfigurationCondition condition, String bundleName) { + return bundles.contains(new ConditionalElement<>(condition, bundleName)); } @Override @@ -118,15 +123,15 @@ public void printJson(JsonWriter writer) throws IOException { writer.append('{').indent().newline(); writer.quote("resources").append(':').append('{').newline(); writer.quote("includes").append(':'); - JsonPrinter.printCollection(writer, addedResources.keySet(), Comparator.naturalOrder(), (String p, JsonWriter w) -> w.append('{').quote("pattern").append(':').quote(p).append('}')); + JsonPrinter.printCollection(writer, addedResources.keySet(), ConditionalElement.comparator(), (p, w) -> conditionalElementJson(p, w, "pattern")); if (!ignoredResources.isEmpty()) { writer.append(',').newline(); writer.quote("excludes").append(':'); - JsonPrinter.printCollection(writer, ignoredResources.keySet(), Comparator.naturalOrder(), (String p, JsonWriter w) -> w.append('{').quote("pattern").append(':').quote(p).append('}')); + JsonPrinter.printCollection(writer, ignoredResources.keySet(), ConditionalElement.comparator(), (p, w) -> conditionalElementJson(p, w, "pattern")); } writer.append('}').append(',').newline(); writer.quote("bundles").append(':'); - JsonPrinter.printCollection(writer, bundles, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.append('{').quote("name").append(':').quote(p).append('}')); + JsonPrinter.printCollection(writer, bundles, ConditionalElement.comparator(), (p, w) -> conditionalElementJson(p, w, "name")); writer.unindent().newline().append('}'); } @@ -135,4 +140,10 @@ public boolean isEmpty() { return addedResources.isEmpty() && bundles.isEmpty(); } + private static void conditionalElementJson(ConditionalElement p, JsonWriter w, String elementName) throws IOException { + w.append('{').indent().newline(); + ConfigurationConditionPrintable.printConditionAttribute(p.getCondition(), w); + w.quote(elementName).append(':').quote(p.getElement()); + w.unindent().newline().append('}'); + } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java index 05c586079b4b..8ae0652b5b2d 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java @@ -56,7 +56,7 @@ public TypeConfiguration getConfiguration() { @Override @SuppressWarnings("fallthrough") void processEntry(Map entry) { - ConfigurationCondition condition = ConfigurationCondition.objectReachable(); + ConfigurationCondition condition = ConfigurationCondition.alwaysTrue(); boolean invalidResult = Boolean.FALSE.equals(entry.get("result")); if (invalidResult) { return; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java index 44a33d8a1621..40795fc9e4f7 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java @@ -73,7 +73,7 @@ public ResourceConfiguration getResourceConfiguration() { @SuppressWarnings("fallthrough") public void processEntry(Map entry) { boolean invalidResult = Boolean.FALSE.equals(entry.get("result")); - ConfigurationCondition condition = ConfigurationCondition.objectReachable(); + ConfigurationCondition condition = ConfigurationCondition.alwaysTrue(); if (invalidResult) { return; } @@ -90,7 +90,7 @@ public void processEntry(Map entry) { case "getSystemResources": String literal = singleElement(args); String regex = Pattern.quote(literal); - resourceConfiguration.addResourcePattern(regex); + resourceConfiguration.addResourcePattern(condition, regex); return; } String callerClass = (String) entry.get("caller_class"); @@ -238,12 +238,12 @@ public void processEntry(Map entry) { case "getBundleImplJDK8OrEarlier": { expectSize(args, 4); - resourceConfiguration.addBundle((String) args.get(0)); + resourceConfiguration.addBundle(condition, (String) args.get(0)); break; } case "getBundleImplJDK11OrLater": { expectSize(args, 5); - resourceConfiguration.addBundle((String) args.get(2)); + resourceConfiguration.addBundle(condition, (String) args.get(2)); break; } default: @@ -257,7 +257,7 @@ private void addFullyQualifiedDeclaredMethod(String descriptor) { String qualifiedClass = descriptor.substring(0, classend); String methodName = descriptor.substring(classend + 1, sigbegin); String signature = descriptor.substring(sigbegin); - configuration.getOrCreateType(ConfigurationCondition.objectReachable(), qualifiedClass).addMethod(methodName, signature, ConfigurationMemberKind.DECLARED); + configuration.getOrCreateType(ConfigurationCondition.alwaysTrue(), qualifiedClass).addMethod(methodName, signature, ConfigurationMemberKind.DECLARED); } private void addDynamicProxy(List interfaceList, LazyValue callerClass) { @@ -268,7 +268,7 @@ private void addDynamicProxy(List interfaceList, LazyValue callerClas return; } } - proxyConfiguration.add(interfaces); + proxyConfiguration.add(ConfigurationCondition.alwaysTrue(), interfaces); } private void addDynamicProxyUnchecked(List checkedInterfaceList, List uncheckedInterfaceList, LazyValue callerClass) { @@ -285,6 +285,6 @@ private void addDynamicProxyUnchecked(List checkedInterfaceList, List unch List interfaces = new ArrayList<>(); interfaces.addAll(checkedInterfaces); interfaces.addAll(uncheckedInterfaces); - proxyConfiguration.add(interfaces); + proxyConfiguration.add(ConfigurationCondition.alwaysTrue(), interfaces); } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java index 0c82ef50a231..df3a0df87b0c 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java @@ -48,7 +48,7 @@ public SerializationConfiguration getSerializationConfiguration() { @Override void processEntry(Map entry) { boolean invalidResult = Boolean.FALSE.equals(entry.get("result")); - ConfigurationCondition condition = ConfigurationCondition.objectReachable(); + ConfigurationCondition condition = ConfigurationCondition.alwaysTrue(); if (invalidResult) { return; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConditionalElement.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConditionalElement.java index db6ba6cf8ce1..c1b2c92d3082 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConditionalElement.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConditionalElement.java @@ -25,7 +25,9 @@ package com.oracle.svm.core.configure; +import java.util.Comparator; import java.util.Objects; +import java.util.function.Function; import org.graalvm.nativeimage.impl.ConfigurationCondition; @@ -63,4 +65,18 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(condition, element); } + + public static > Comparator> comparator() { + return (o1, o2) -> Comparator + .comparing((Function, T>) ConditionalElement::getElement) + .thenComparing(ConditionalElement::getCondition) + .compare(o1, o2); + } + + public static Comparator> comparator(Comparator elementComparator) { + return (o1, o2) -> Comparator + .comparing((Function, T>) ConditionalElement::getElement, elementComparator) + .thenComparing(ConditionalElement::getCondition) + .compare(o1, o2); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationParser.java index fb1945bba485..bde2876a617a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationParser.java @@ -156,7 +156,7 @@ protected ConfigurationCondition parseCondition(Map data) { warnOrFail("'" + TYPE_REACHABLE_KEY + "' should be of type string"); } } - return ConfigurationCondition.objectReachable(); + return ConfigurationCondition.alwaysTrue(); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationParser.java index 9d5f0fda5d59..8d0d409ff478 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationParser.java @@ -30,6 +30,9 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; import com.oracle.svm.core.util.json.JSONParser; @@ -41,9 +44,9 @@ * Parses JSON describing lists of interfaces and register them in the {@link DynamicProxyRegistry}. */ public final class ProxyConfigurationParser extends ConfigurationParser { - private final Consumer interfaceListConsumer; + private final Consumer>> interfaceListConsumer; - public ProxyConfigurationParser(Consumer interfaceListConsumer, boolean strictConfiguration) { + public ProxyConfigurationParser(Consumer>> interfaceListConsumer, boolean strictConfiguration) { super(strictConfiguration); this.interfaceListConsumer = interfaceListConsumer; } @@ -61,10 +64,10 @@ private void parseTopLevelArray(List proxyConfiguration) { for (Object proxyConfigurationObject : proxyConfiguration) { if (proxyConfigurationObject instanceof List) { foundInterfaceLists = true; - parseInterfaceList(asList(proxyConfigurationObject, "")); + parseInterfaceList(ConfigurationCondition.alwaysTrue(), asList(proxyConfigurationObject, "")); } else if (proxyConfigurationObject instanceof Map) { foundProxyConfigurationObjects = true; - parseWithPredicatedConfig(asMap(proxyConfigurationObject, "")); + parseWithConditionalConfig(asMap(proxyConfigurationObject, "")); } else { throw new JSONParserException("second level must be composed of either interface lists or proxy configuration objects"); } @@ -74,23 +77,20 @@ private void parseTopLevelArray(List proxyConfiguration) { } } - private void parseInterfaceList(List data) { - String[] interfaces = new String[data.size()]; - int i = 0; - for (Object value : data) { - interfaces[i] = asString(value); - i++; - } + private void parseInterfaceList(ConfigurationCondition condition, List data) { + List interfaces = data.stream().map(ConfigurationParser::asString).collect(Collectors.toList()); + try { - interfaceListConsumer.accept(interfaces); + interfaceListConsumer.accept(new ConditionalElement<>(condition, interfaces)); } catch (Exception e) { throw new JSONParserException(e.toString()); } } - private void parseWithPredicatedConfig(Map proxyConfigObject) { - checkAttributes(proxyConfigObject, "proxy descriptor object", Collections.singleton("interfaces")); - parseInterfaceList(asList(proxyConfigObject.get("interfaces"), "\"interfaces\" must be an array of fully qualified interface names")); + private void parseWithConditionalConfig(Map proxyConfigObject) { + checkAttributes(proxyConfigObject, "proxy descriptor object", Collections.singleton("interfaces"), Collections.singletonList(CONDITIONAL_KEY)); + ConfigurationCondition condition = parseCondition(proxyConfigObject); + parseInterfaceList(condition, asList(proxyConfigObject.get("interfaces"), "\"interfaces\" must be an array of fully qualified interface names")); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java index f0bdc1a11f9d..3bf6fba985c3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java @@ -86,6 +86,7 @@ private void parseClass(Map data) { TypeResult conditionResult = delegate.resolveCondition(parseCondition(data).getTypeName()); if (!conditionResult.isPresent()) { handleError("Could not resolve condition " + parseCondition(data).getTypeName() + " for reflection.", conditionResult.getException()); + return; } ConfigurationCondition condition = conditionResult.get(); @@ -227,7 +228,7 @@ private List parseMethodParameters(T clazz, String methodName, List t List result = new ArrayList<>(); for (Object type : types) { String typeName = asString(type, "types"); - TypeResult typeResult = delegate.resolveType(ConfigurationCondition.objectReachable(), typeName); + TypeResult typeResult = delegate.resolveType(ConfigurationCondition.alwaysTrue(), typeName); if (!typeResult.isPresent()) { handleError("Could not register method " + formatMethod(clazz, methodName) + " for reflection.", typeResult.getException()); return null; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index a737e95f458d..628bd0de1c32 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -29,14 +29,16 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.function.Consumer; +import java.util.function.BiConsumer; + +import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.util.json.JSONParser; public class ResourceConfigurationParser extends ConfigurationParser { private final ResourcesRegistry registry; - public ResourceConfigurationParser(ResourcesRegistry registry, boolean strictConfiguration) { + public ResourceConfigurationParser(ResourcesRegistry registry, boolean strictConfiguration) { super(strictConfiguration); this.registry = registry; } @@ -68,35 +70,36 @@ private void parseTopLevelObject(Map obj) { List includes = asList(includesObject, "Attribute 'includes' must be a list of resources"); for (Object object : includes) { - parseEntry(object, "pattern", registry::addResources, "resource descriptor object", "'includes' list"); + parseStringEntry(object, "pattern", registry::addResources, "resource descriptor object", "'includes' list"); } if (excludesObject != null) { List excludes = asList(excludesObject, "Attribute 'excludes' must be a list of resources"); for (Object object : excludes) { - parseEntry(object, "pattern", registry::ignoreResources, "resource descriptor object", "'excludes' list"); + parseStringEntry(object, "pattern", registry::ignoreResources, "resource descriptor object", "'excludes' list"); } } } else { // Old format: may be deprecated in future versions List resources = asList(resourcesObject, "Attribute 'resources' must be a list of resources"); for (Object object : resources) { - parseEntry(object, "pattern", registry::addResources, "resource descriptor object", "'resources' list"); + parseStringEntry(object, "pattern", registry::addResources, "resource descriptor object", "'resources' list"); } } } if (bundlesObject != null) { List bundles = asList(bundlesObject, "Attribute 'bundles' must be a list of bundles"); for (Object object : bundles) { - parseEntry(object, "name", registry::addResourceBundles, "bundle descriptor object", "'bundles' list"); + parseStringEntry(object, "name", registry::addResourceBundles, "bundle descriptor object", "'bundles' list"); } } } - private void parseEntry(Object data, String valueKey, Consumer resourceRegistry, String expectedType, String parentType) { + private void parseStringEntry(Object data, String valueKey, BiConsumer resourceRegistry, String expectedType, String parentType) { Map resource = asMap(data, "Elements of " + parentType + " must be a " + expectedType); - checkAttributes(resource, "resource and resource bundle descriptor object", Collections.singleton(valueKey)); + checkAttributes(resource, "resource and resource bundle descriptor object", Collections.singletonList(valueKey), Collections.singletonList(CONDITIONAL_KEY)); + ConfigurationCondition condition = parseCondition(resource); Object valueObject = resource.get(valueKey); String value = asString(valueObject, valueKey); - resourceRegistry.accept(value); + resourceRegistry.accept(condition, value); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java index afd44513c0e2..9be2e976f50b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java @@ -24,10 +24,12 @@ */ package com.oracle.svm.core.configure; +import org.graalvm.nativeimage.impl.ConfigurationCondition; + public interface ResourcesRegistry { - void addResources(String pattern); + void addResources(ConfigurationCondition condition, String pattern); - void ignoreResources(String pattern); + void ignoreResources(ConfigurationCondition condition, String pattern); - void addResourceBundles(String name); + void addResourceBundles(ConfigurationCondition condition, String name); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/CharsetSubstitutionsFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/CharsetSubstitutionsFeature.java index 0588c0864ad1..686e9aa10f63 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/CharsetSubstitutionsFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/CharsetSubstitutionsFeature.java @@ -24,16 +24,17 @@ */ package com.oracle.svm.core.jdk.localization; -import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.configure.ResourcesRegistry; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.impl.ConfigurationCondition; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.configure.ResourcesRegistry; @AutomaticFeature class CharsetSubstitutionsFeature implements Feature { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { - Class clazz = access.findClassByName("java.lang.CharacterName"); - access.registerReachabilityHandler(a -> ImageSingletons.lookup(ResourcesRegistry.class).addResources("java/lang/uniName.dat"), clazz); + ImageSingletons.lookup(ResourcesRegistry.class).addResources(ConfigurationCondition.create("java.lang.CharacterName"), "java/lang/uniName.dat"); } } 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 91d7bd4c3a5e..cd601c0dcc46 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 @@ -40,6 +40,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.RuntimeReflection; +import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.configure.ResourcesRegistry; import com.oracle.svm.core.util.VMError; @@ -91,7 +92,7 @@ public Map getBundleContentOf(Object bundle) { public void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale) { if (bundle instanceof PropertyResourceBundle) { String withLocale = control.toBundleName(bundleName, locale); - ImageSingletons.lookup(ResourcesRegistry.class).addResources(withLocale.replace('.', '/') + "\\.properties"); + ImageSingletons.lookup(ResourcesRegistry.class).addResources(ConfigurationCondition.alwaysTrue(), withLocale.replace('.', '/') + "\\.properties"); } else { RuntimeReflection.register(bundle.getClass()); RuntimeReflection.registerForReflectiveInstantiation(bundle.getClass()); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIRuntimeAccess.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIRuntimeAccess.java index c66bf3e7b4ad..9488f1aeca24 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIRuntimeAccess.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/JNIRuntimeAccess.java @@ -49,11 +49,11 @@ private JNIRuntimeAccess() { } public static void register(Class... classes) { - getSupport().register(ConfigurationCondition.objectReachable(), classes); + getSupport().register(ConfigurationCondition.alwaysTrue(), classes); } public static void register(Executable... methods) { - getSupport().register(ConfigurationCondition.objectReachable(), methods); + getSupport().register(ConfigurationCondition.alwaysTrue(), methods); } public static void register(Field... fields) { @@ -61,7 +61,7 @@ public static void register(Field... fields) { } public static void register(boolean finalIsWritable, Field... fields) { - getSupport().register(ConfigurationCondition.objectReachable(), finalIsWritable, fields); + getSupport().register(ConfigurationCondition.alwaysTrue(), finalIsWritable, fields); } private static JNIRuntimeAccessibilitySupport getSupport() { diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java index 826ae6ea7f80..40dbb859bd46 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java @@ -339,7 +339,7 @@ UserException error(String format, Object... args) { private static void registerJNIConfiguration(JNIRuntimeAccess.JNIRuntimeAccessibilitySupport registry, ImageClassLoader loader) { try (JNIConfigSource source = new JNIConfigSource(loader)) { Map> classes = new HashMap<>(); - ConfigurationCondition condition = ConfigurationCondition.objectReachable(); + ConfigurationCondition condition = ConfigurationCondition.alwaysTrue(); for (String line : source.lines) { source.lineNo++; String[] tokens = line.split(" "); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java index f01427ef6111..1ee6e315c3d2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java @@ -38,7 +38,7 @@ public abstract class ConditionalConfigurationRegistry { private final Map> pendingReachabilityHandlers = new ConcurrentHashMap<>(); protected void registerConditionalConfiguration(ConfigurationCondition condition, Runnable runnable) { - if (ConfigurationCondition.objectReachable().equals(condition)) { + if (ConfigurationCondition.alwaysTrue().equals(condition)) { /* analysis optimization to include new types as early as possible */ runnable.run(); } else { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConfigurationTypeResolver.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConfigurationTypeResolver.java new file mode 100644 index 000000000000..e4a323443b0b --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConfigurationTypeResolver.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted; + +import com.oracle.svm.core.TypeResult; +import com.oracle.svm.core.util.json.JSONParserException; + +import jdk.vm.ci.meta.MetaUtil; + +public final class ConfigurationTypeResolver { + private final String configurationType; + private final ImageClassLoader classLoader; + private final boolean allowIncompleteClasspath; + + public ConfigurationTypeResolver(String configurationType, ImageClassLoader classLoader, boolean allowIncompleteClasspath) { + this.configurationType = configurationType; + this.classLoader = classLoader; + this.allowIncompleteClasspath = allowIncompleteClasspath; + } + + public Class resolveType(String typeName) { + String name = typeName; + if (name.indexOf('[') != -1) { + /* accept "int[][]", "java.lang.String[]" */ + name = MetaUtil.internalNameToJava(MetaUtil.toInternalName(name), true, true); + } + TypeResult> typeResult = classLoader.findClass(name); + if (!typeResult.isPresent()) { + handleError("Could not resolve " + name + " for " + configurationType + "."); + } + return typeResult.get(); + } + + private void handleError(String message) { + if (allowIncompleteClasspath) { + System.err.println("Warning: " + message); + } else { + throw new JSONParserException(message + " To allow unresolvable " + configurationType + ", use option --allow-incomplete-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 976aac844952..606dbf6322a4 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 @@ -54,6 +54,7 @@ import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.configure.ConfigurationFile; @@ -119,28 +120,55 @@ public static class Options { private final Set excludedResourcePatterns = Collections.newSetFromMap(new ConcurrentHashMap<>()); private int loadedConfigurations; - private class ResourcesRegistryImpl implements ResourcesRegistry { + private class ResourcesRegistryImpl extends ConditionalConfigurationRegistry implements ResourcesRegistry { + private ConfigurationTypeResolver configurationTypeResolver; + + ResourcesRegistryImpl(ConfigurationTypeResolver configurationTypeResolver) { + this.configurationTypeResolver = configurationTypeResolver; + } + @Override - public void addResources(String pattern) { - UserError.guarantee(!sealed, "Resources added too late: %s", pattern); - resourcePatternWorkSet.add(pattern); + public void addResources(ConfigurationCondition condition, String pattern) { + if (configurationTypeResolver.resolveType(condition.getTypeName()) == null) { + return; + } + registerConditionalConfiguration(condition, () -> { + UserError.guarantee(!sealed, "Resources added too late: %s", pattern); + resourcePatternWorkSet.add(pattern); + }); } @Override - public void ignoreResources(String pattern) { - UserError.guarantee(!sealed, "Resources ignored too late: %s", pattern); - excludedResourcePatterns.add(pattern); + public void ignoreResources(ConfigurationCondition condition, String pattern) { + if (configurationTypeResolver.resolveType(condition.getTypeName()) == null) { + return; + } + registerConditionalConfiguration(condition, () -> { + UserError.guarantee(!sealed, "Resources ignored too late: %s", pattern); + + excludedResourcePatterns.add(pattern); + }); } @Override - public void addResourceBundles(String name) { - ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(name); + public void addResourceBundles(ConfigurationCondition condition, String name) { + if (configurationTypeResolver.resolveType(condition.getTypeName()) == null) { + return; + } + registerConditionalConfiguration(condition, () -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(name)); } } @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(ResourcesRegistry.class, new ResourcesRegistryImpl()); + public void afterRegistration(AfterRegistrationAccess a) { + FeatureImpl.AfterRegistrationAccessImpl access = (FeatureImpl.AfterRegistrationAccessImpl) a; + ImageClassLoader imageClassLoader = access.getImageClassLoader(); + ImageSingletons.add(ResourcesRegistry.class, + new ResourcesRegistryImpl(new ConfigurationTypeResolver("resource configuration", imageClassLoader, NativeImageOptions.AllowIncompleteClasspath.getValue()))); + } + + private static ResourcesRegistryImpl resourceRegistryImpl() { + return (ResourcesRegistryImpl) ImageSingletons.lookup(ResourcesRegistry.class); } @Override @@ -153,10 +181,12 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { resourcePatternWorkSet.addAll(Options.IncludeResources.getValue().values()); excludedResourcePatterns.addAll(Options.ExcludeResources.getValue().values()); + resourceRegistryImpl().flushConditionalConfiguration(access); } @Override public void duringAnalysis(DuringAnalysisAccess access) { + resourceRegistryImpl().flushConditionalConfiguration(access); if (resourcePatternWorkSet.isEmpty()) { return; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationAwt.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationAwt.java index 7654fa2a62a8..d3f4f829fb93 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationAwt.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JNIRegistrationAwt.java @@ -38,6 +38,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.InternalPlatform; import java.awt.GraphicsEnvironment; @@ -175,33 +176,33 @@ private static void registerFreeType(DuringAnalysisAccess access) { private static void registerColorProfiles(DuringAnalysisAccess duringAnalysisAccess) { ResourcesRegistry resourcesRegistry = ImageSingletons.lookup(ResourcesRegistry.class); - resourcesRegistry.addResources("sun.java2d.cmm.profiles.*"); + resourcesRegistry.addResources(ConfigurationCondition.alwaysTrue(), "sun.java2d.cmm.profiles.*"); } private static void registerFlavorMapProps(DuringAnalysisAccess duringAnalysisAccess) { ResourcesRegistry resourcesRegistry = ImageSingletons.lookup(ResourcesRegistry.class); - resourcesRegistry.addResources("sun.datatransfer.resources.flavormap.properties"); + resourcesRegistry.addResources(ConfigurationCondition.alwaysTrue(), "sun.datatransfer.resources.flavormap.properties"); } private static void registerRTFReaderCharsets(DuringAnalysisAccess duringAnalysisAccess) { ResourcesRegistry resourcesRegistry = ImageSingletons.lookup(ResourcesRegistry.class); - resourcesRegistry.addResources("javax.swing.text.rtf.charsets.*"); + resourcesRegistry.addResources(ConfigurationCondition.alwaysTrue(), "javax.swing.text.rtf.charsets.*"); } private static void registerOceanThemeIcons(DuringAnalysisAccess duringAnalysisAccess) { ResourcesRegistry resourcesRegistry = ImageSingletons.lookup(ResourcesRegistry.class); - resourcesRegistry.addResources("javax.swing.plaf.metal.icons.*"); - resourcesRegistry.addResources("javax.swing.plaf.basic.icons.*"); + resourcesRegistry.addResources(ConfigurationCondition.alwaysTrue(), "javax.swing.plaf.metal.icons.*"); + resourcesRegistry.addResources(ConfigurationCondition.alwaysTrue(), "javax.swing.plaf.basic.icons.*"); } private static void registerHtml32bdtd(DuringAnalysisAccess duringAnalysisAccess) { ResourcesRegistry resourcesRegistry = ImageSingletons.lookup(ResourcesRegistry.class); - resourcesRegistry.addResources("javax.swing.text.html.parser.html32.bdtd"); + resourcesRegistry.addResources(ConfigurationCondition.alwaysTrue(), "javax.swing.text.html.parser.html32.bdtd"); } private static void registerDefaultCSS(DuringAnalysisAccess duringAnalysisAccess) { ResourcesRegistry resourcesRegistry = ImageSingletons.lookup(ResourcesRegistry.class); - resourcesRegistry.addResources("javax.swing.text.html.default.css"); + resourcesRegistry.addResources(ConfigurationCondition.alwaysTrue(), "javax.swing.text.html.default.css"); } private static NativeLibraries getNativeLibraries(DuringAnalysisAccess access) { 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 b20cdc7538fe..db8411e5ba60 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 @@ -25,18 +25,20 @@ package com.oracle.svm.hosted.xml; -import com.oracle.svm.core.configure.ResourcesRegistry; -import com.oracle.svm.core.jdk.JNIRegistrationUtil; -import com.oracle.svm.hosted.FeatureImpl; -import com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.hosted.RuntimeReflection; +import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import com.oracle.svm.core.configure.ResourcesRegistry; +import com.oracle.svm.core.jdk.JNIRegistrationUtil; +import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization; public abstract class XMLParsersRegistration extends JNIRegistrationUtil { @@ -113,9 +115,9 @@ void registerResources() { classInitializationSupport.setConfigurationSealed(false); ResourcesRegistry resourcesRegistry = ImageSingletons.lookup(ResourcesRegistry.class); - resourcesRegistry.addResourceBundles("com.sun.org.apache.xml.internal.serializer.utils.SerializerMessages"); - resourcesRegistry.addResourceBundles("com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMessages"); - resourcesRegistry.addResources("com.sun.*.properties"); + resourcesRegistry.addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xml.internal.serializer.utils.SerializerMessages"); + resourcesRegistry.addResourceBundles(ConfigurationCondition.alwaysTrue(), "com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMessages"); + resourcesRegistry.addResources(ConfigurationCondition.alwaysTrue(), "com.sun.*.properties"); classInitializationSupport.setConfigurationSealed(true); } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java index 90d0ad28194e..d32ddb8c634b 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java @@ -26,20 +26,20 @@ import java.util.Collections; import java.util.List; -import java.util.function.Consumer; -import com.oracle.svm.core.configure.ConfigurationFile; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.configure.ConfigurationFile; import com.oracle.svm.core.configure.ConfigurationFiles; import com.oracle.svm.core.configure.ProxyConfigurationParser; import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; -import com.oracle.svm.core.util.UserError; +import com.oracle.svm.hosted.ConfigurationTypeResolver; import com.oracle.svm.hosted.FallbackFeature; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.ImageClassLoader; +import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.hosted.config.ConfigurationParserUtils; import com.oracle.svm.reflect.hosted.ReflectionFeature; import com.oracle.svm.reflect.proxy.DynamicProxySupport; @@ -60,29 +60,29 @@ public void duringSetup(DuringSetupAccess a) { ImageClassLoader imageClassLoader = access.getImageClassLoader(); DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(imageClassLoader.getClassLoader()); ImageSingletons.add(DynamicProxyRegistry.class, dynamicProxySupport); - - Consumer adapter = interfaceNames -> { - Class[] interfaces = new Class[interfaceNames.length]; - for (int i = 0; i < interfaceNames.length; i++) { - String className = interfaceNames[i]; - Class clazz = imageClassLoader.findClass(className).get(); - if (clazz == null) { - throw UserError.abort("Class " + className + " not found"); - } - if (!clazz.isInterface()) { - throw UserError.abort("The class \"" + className + "\" is not an interface."); - } - interfaces[i] = clazz; - } - /* The interfaces array can be empty. The java.lang.reflect.Proxy API allows it. */ - dynamicProxySupport.addProxyClass(interfaces); - }; - ProxyConfigurationParser parser = new ProxyConfigurationParser(adapter, ConfigurationFiles.Options.StrictConfiguration.getValue()); + ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("resource configuration", imageClassLoader, NativeImageOptions.AllowIncompleteClasspath.getValue()); + ProxyRegistry proxyRegistry = new ProxyRegistry(typeResolver, dynamicProxySupport, imageClassLoader); + ImageSingletons.add(ProxyRegistry.class, proxyRegistry); + ProxyConfigurationParser parser = new ProxyConfigurationParser(proxyRegistry, ConfigurationFiles.Options.StrictConfiguration.getValue()); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurations(parser, imageClassLoader, "dynamic proxy", ConfigurationFiles.Options.DynamicProxyConfigurationFiles, ConfigurationFiles.Options.DynamicProxyConfigurationResources, ConfigurationFile.DYNAMIC_PROXY.getFileName()); } + private static ProxyRegistry proxyRegistry() { + return ImageSingletons.lookup(ProxyRegistry.class); + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + proxyRegistry().flushConditionalConfiguration(access); + } + + @Override + public void duringAnalysis(DuringAnalysisAccess access) { + proxyRegistry().flushConditionalConfiguration(access); + } + @Override public void beforeCompilation(BeforeCompilationAccess access) { if (!ImageSingletons.contains(FallbackFeature.class)) { diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/ProxyRegistry.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/ProxyRegistry.java new file mode 100644 index 000000000000..3cbbe417a83b --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/ProxyRegistry.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.reflect.proxy.hosted; + +import com.oracle.svm.core.configure.ConditionalElement; +import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; +import com.oracle.svm.core.util.UserError; +import com.oracle.svm.hosted.ConditionalConfigurationRegistry; +import com.oracle.svm.hosted.ConfigurationTypeResolver; +import com.oracle.svm.hosted.ImageClassLoader; + +import java.util.List; +import java.util.function.Consumer; + +public class ProxyRegistry extends ConditionalConfigurationRegistry implements Consumer>> { + private final ConfigurationTypeResolver typeResolver; + private final DynamicProxyRegistry dynamicProxySupport; + private final ImageClassLoader imageClassLoader; + + public ProxyRegistry(ConfigurationTypeResolver typeResolver, DynamicProxyRegistry dynamicProxySupport, ImageClassLoader imageClassLoader) { + this.typeResolver = typeResolver; + this.dynamicProxySupport = dynamicProxySupport; + this.imageClassLoader = imageClassLoader; + } + + @Override + public void accept(ConditionalElement> proxies) { + if (typeResolver.resolveType(proxies.getCondition().getTypeName()) == null) { + return; + } + List interfaceNames = proxies.getElement(); + Class[] interfaces = new Class[interfaceNames.size()]; + for (int i = 0; i < interfaceNames.size(); i++) { + String className = interfaceNames.get(i); + Class clazz = imageClassLoader.findClass(className).get(); + if (clazz == null) { + throw UserError.abort("Class %s not found.", className); + } + if (!clazz.isInterface()) { + throw UserError.abort("The class %s is not an interface.", className); + } + interfaces[i] = clazz; + } + registerConditionalConfiguration(proxies.getCondition(), () -> { + /* The interfaces array can be empty. The java.lang.reflect.Proxy API allows it. */ + dynamicProxySupport.addProxyClass(interfaces); + }); + } +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java index c9b48172981f..ce31066570ff 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/serialize/hosted/SerializationFeature.java @@ -46,7 +46,6 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; -import com.oracle.svm.core.TypeResult; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.configure.ConfigurationFile; import com.oracle.svm.core.configure.ConfigurationFiles; @@ -55,20 +54,18 @@ import com.oracle.svm.core.jdk.RecordSupport; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; -import com.oracle.svm.core.util.json.JSONParserException; +import com.oracle.svm.hosted.ConditionalConfigurationRegistry; +import com.oracle.svm.hosted.ConfigurationTypeResolver; import com.oracle.svm.hosted.FallbackFeature; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.NativeImageOptions; -import com.oracle.svm.hosted.ConditionalConfigurationRegistry; import com.oracle.svm.hosted.config.ConfigurationParserUtils; import com.oracle.svm.reflect.hosted.ReflectionFeature; import com.oracle.svm.reflect.serialize.SerializationRegistry; import com.oracle.svm.reflect.serialize.SerializationSupport; import com.oracle.svm.util.ReflectionUtil; -import jdk.vm.ci.meta.MetaUtil; - @AutomaticFeature public class SerializationFeature implements Feature { private SerializationBuilder serializationBuilder; @@ -83,7 +80,7 @@ public List> getRequiredFeatures() { public void duringSetup(DuringSetupAccess a) { FeatureImpl.DuringSetupAccessImpl access = (FeatureImpl.DuringSetupAccessImpl) a; ImageClassLoader imageClassLoader = access.getImageClassLoader(); - SerializationTypeResolver typeResolver = new SerializationTypeResolver(imageClassLoader, NativeImageOptions.AllowIncompleteClasspath.getValue()); + ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("serialization configuration", imageClassLoader, NativeImageOptions.AllowIncompleteClasspath.getValue()); SerializationDenyRegistry serializationDenyRegistry = new SerializationDenyRegistry(typeResolver); serializationBuilder = new SerializationBuilder(serializationDenyRegistry, access, typeResolver); ImageSingletons.add(RuntimeSerializationSupport.class, serializationBuilder); @@ -131,44 +128,12 @@ static void println(String str) { } } -final class SerializationTypeResolver { - - private final ImageClassLoader classLoader; - private final boolean allowIncompleteClasspath; - - SerializationTypeResolver(ImageClassLoader classLoader, boolean allowIncompleteClasspath) { - this.classLoader = classLoader; - this.allowIncompleteClasspath = allowIncompleteClasspath; - } - - public Class resolveType(String typeName) { - String name = typeName; - if (name.indexOf('[') != -1) { - /* accept "int[][]", "java.lang.String[]" */ - name = MetaUtil.internalNameToJava(MetaUtil.toInternalName(name), true, true); - } - TypeResult> typeResult = classLoader.findClass(name); - if (!typeResult.isPresent()) { - handleError("Could not resolve " + name + " for serialization configuration."); - } - return typeResult.get(); - } - - private void handleError(String message) { - if (allowIncompleteClasspath) { - println("Warning: " + message); - } else { - throw new JSONParserException(message + " To allow unresolvable reflection configuration, use option -H:+AllowIncompleteClasspath"); - } - } -} - final class SerializationDenyRegistry implements RuntimeSerializationSupport { private final Map, Boolean> deniedClasses = new HashMap<>(); - private final SerializationTypeResolver typeResolver; + private final ConfigurationTypeResolver typeResolver; - SerializationDenyRegistry(SerializationTypeResolver typeResolver) { + SerializationDenyRegistry(ConfigurationTypeResolver typeResolver) { this.typeResolver = typeResolver; } @@ -212,11 +177,11 @@ final class SerializationBuilder extends ConditionalConfigurationRegistry implem private final SerializationSupport serializationSupport; private final SerializationDenyRegistry denyRegistry; - private final SerializationTypeResolver typeResolver; + private final ConfigurationTypeResolver typeResolver; private boolean sealed; - SerializationBuilder(SerializationDenyRegistry serializationDenyRegistry, FeatureImpl.DuringSetupAccessImpl access, SerializationTypeResolver typeResolver) { + SerializationBuilder(SerializationDenyRegistry serializationDenyRegistry, FeatureImpl.DuringSetupAccessImpl access, ConfigurationTypeResolver typeResolver) { try { Class reflectionFactoryClass = access.findClassByName(Package_jdk_internal_reflect.getQualifiedName() + ".ReflectionFactory"); Method getReflectionFactoryMethod = ReflectionUtil.lookupMethod(reflectionFactoryClass, "getReflectionFactory"); @@ -252,15 +217,8 @@ public void registerWithTargetConstructorClass(ConfigurationCondition condition, abortIfSealed(); Class conditionClass = typeResolver.resolveType(condition.getTypeName()); - String msg = "Cannot find condition class %s."; if (conditionClass == null) { - if (NativeImageOptions.AllowIncompleteClasspath.getValue()) { - // Checkstyle: stop - System.err.println("Warning: " + String.format(msg, condition.getTypeName())); - // Checkstyle: resume - } else { - throw UserError.abort(msg, condition.getTypeName()); - } + return; } Class serializationTargetClass = typeResolver.resolveType(targetClassName); diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceFileSystemProviderTest.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceFileSystemProviderTest.java index b5c4c02ef879..2e9d11e40583 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceFileSystemProviderTest.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/NativeImageResourceFileSystemProviderTest.java @@ -54,13 +54,15 @@ import java.util.Map; import java.util.Set; -import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.configure.ResourcesRegistry; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.junit.Assert; import org.junit.Test; +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.configure.ResourcesRegistry; + public class NativeImageResourceFileSystemProviderTest { private static final String RESOURCE_DIR = "/resources"; @@ -78,8 +80,8 @@ private static final class RegisterResourceFeature implements Feature { public void beforeAnalysis(BeforeAnalysisAccess access) { ResourcesRegistry registry = ImageSingletons.lookup(ResourcesRegistry.class); // Remove leading / for the resource patterns - registry.addResources(RESOURCE_FILE_1.substring(1)); - registry.addResources(RESOURCE_FILE_2.substring(1)); + registry.addResources(ConfigurationCondition.alwaysTrue(), RESOURCE_FILE_1.substring(1)); + registry.addResources(ConfigurationCondition.alwaysTrue(), RESOURCE_FILE_2.substring(1)); } }