diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JsonPrettyWriter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JsonPrettyWriter.java index 7c6cb66264dc..15f1f8abf2bd 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JsonPrettyWriter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JsonPrettyWriter.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.io.Writer; +import java.nio.file.Path; /** * Subclass of {@link JsonWriter} that pretty-prints its output. More specifically, a newline will @@ -42,6 +43,10 @@ public JsonPrettyWriter(Writer writer) { super(writer); } + public JsonPrettyWriter(Path path) throws IOException { + super(path); + } + @Override public JsonWriter appendObjectStart() throws IOException { return super.appendObjectStart().indent().newline(); @@ -53,6 +58,17 @@ public JsonWriter appendObjectEnd() throws IOException { return super.appendObjectEnd(); } + @Override + public JsonWriter appendArrayStart() throws IOException { + return super.appendArrayStart().indent().newline(); + } + + @Override + public JsonWriter appendArrayEnd() throws IOException { + unindent().newline(); + return super.appendArrayEnd(); + } + @Override public JsonWriter appendFieldSeparator() throws IOException { return super.appendFieldSeparator().append(' '); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JsonPrinter.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JsonPrinter.java index cef066f2dc0b..949554a9a9de 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JsonPrinter.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JsonPrinter.java @@ -55,6 +55,11 @@ public interface JsonPrinter { * @see JsonWriter#print(Object) */ static void printCollection(JsonWriter writer, Collection collection, Comparator comparator, JsonPrinter elementPrinter) throws IOException { + if (collection.isEmpty()) { + writer.append("[]"); + return; + } + Collection ordered = collection; if (comparator != null) { ordered = new ArrayList<>(collection); diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgent.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgent.java index 14c74345636e..22f58014f64f 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgent.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgent.java @@ -75,6 +75,7 @@ import com.oracle.svm.configure.trace.AccessAdvisor; import com.oracle.svm.configure.trace.TraceProcessor; import com.oracle.svm.core.configure.ConfigurationFile; +import com.oracle.svm.core.configure.PredefinedClassesConfigurationParser; import com.oracle.svm.core.jni.headers.JNIEnvironment; import com.oracle.svm.core.jni.headers.JNIJavaVM; import com.oracle.svm.core.jni.headers.JNIObjectHandle; @@ -86,6 +87,8 @@ import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEventCallbacks; import com.oracle.svm.jvmtiagentbase.jvmti.JvmtiInterface; +import jdk.graal.compiler.phases.common.LazyValue; + public final class NativeImageAgent extends JvmtiAgentBase { private static final String AGENT_NAME = "native-image-agent"; private static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC"); @@ -338,7 +341,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c tracingResultWriter = new ConfigurationWithOriginsWriter(configWithOriginsTracer); } } else { - Path[] predefinedClassDestDirs = {Files.createDirectories(configOutputDirPath.resolve(ConfigurationFile.PREDEFINED_CLASSES_AGENT_EXTRACTED_SUBDIR))}; + List> predefinedClassDestDirs = List.of(PredefinedClassesConfigurationParser.directorySupplier(configOutputDirPath)); Function handler = e -> { if (e instanceof NoSuchFileException) { warn("file " + ((NoSuchFileException) e).getFile() + " for merging could not be found, skipping"); @@ -543,9 +546,11 @@ private void writeConfigurationFiles() { throw unexpectedlyModified(configOutputLockFilePath); } expectUnmodified(configOutputLockFilePath); - if (!mostRecent.equals(expectedConfigModifiedBefore)) { - throw unexpectedlyModified(configOutputDirPath); - } + /* + * Checking for the modification of the whole configuration directory is not possible + * since predefined classes configuration outputs folders and files during the agent + * run. + */ Path[] targetFilePaths = new Path[tempFilePaths.size()]; for (int i = 0; i < tempFilePaths.size(); i++) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/command/ConfigurationGenerateCommand.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/command/ConfigurationGenerateCommand.java index 53e688d55772..b19a215fe8c6 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/command/ConfigurationGenerateCommand.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/command/ConfigurationGenerateCommand.java @@ -46,9 +46,11 @@ import com.oracle.svm.configure.filters.HierarchyFilterNode; import com.oracle.svm.configure.trace.AccessAdvisor; import com.oracle.svm.configure.trace.TraceProcessor; -import com.oracle.svm.core.configure.ConfigurationFile; +import com.oracle.svm.core.configure.PredefinedClassesConfigurationParser; import com.oracle.svm.util.LogUtils; +import jdk.graal.compiler.phases.common.LazyValue; + public class ConfigurationGenerateCommand extends ConfigurationCommand { @Override public String getName() { @@ -264,14 +266,12 @@ protected static void generate(Iterator argumentsIterator, boolean accep try { omittedConfigurationSet = omittedCollection.loadConfigurationSet(ConfigurationFileCollection.FAIL_ON_EXCEPTION, null, null); - List predefinedClassDestDirs = new ArrayList<>(); + List> predefinedClassDestDirs = new ArrayList<>(); for (URI pathUri : outputCollection.getPredefinedClassesConfigPaths()) { - Path subdir = Files.createDirectories(Paths.get(pathUri).getParent().resolve(ConfigurationFile.PREDEFINED_CLASSES_AGENT_EXTRACTED_SUBDIR)); - subdir = Files.createDirectories(subdir); - predefinedClassDestDirs.add(subdir); + predefinedClassDestDirs.add(PredefinedClassesConfigurationParser.directorySupplier(Paths.get(pathUri).getParent())); } Predicate shouldExcludeClassesWithHash = omittedConfigurationSet.getPredefinedClassesConfiguration()::containsClassWithHash; - configurationSet = inputCollection.loadConfigurationSet(ConfigurationFileCollection.FAIL_ON_EXCEPTION, predefinedClassDestDirs.toArray(new Path[0]), shouldExcludeClassesWithHash); + configurationSet = inputCollection.loadConfigurationSet(ConfigurationFileCollection.FAIL_ON_EXCEPTION, predefinedClassDestDirs, shouldExcludeClassesWithHash); } catch (IOException e) { throw e; } catch (Throwable t) { 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 259d861e285c..d9a4eda47730 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 @@ -37,9 +37,9 @@ final class ConfigurationConditionPrintable { static void printConditionAttribute(UnresolvedConfigurationCondition condition, JsonWriter writer) throws IOException { if (!condition.isAlwaysTrue()) { - writer.quote(CONDITIONAL_KEY).append(":{"); - writer.quote(condition.isRuntimeChecked() ? TYPE_REACHED_KEY : TYPE_REACHABLE_KEY).append(':').quote(condition.getTypeName()); - writer.append("},").newline(); + writer.quote(CONDITIONAL_KEY).appendFieldSeparator().appendObjectStart(); + writer.quote(condition.isRuntimeChecked() ? TYPE_REACHED_KEY : TYPE_REACHABLE_KEY).appendFieldSeparator().quote(condition.getTypeName()); + writer.appendObjectEnd().appendSeparator(); } } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationFileCollection.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationFileCollection.java index cf2fc9046ad6..a6310afb98db 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationFileCollection.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationFileCollection.java @@ -35,6 +35,7 @@ import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Function; @@ -44,6 +45,7 @@ import com.oracle.svm.core.configure.ConfigurationFile; import com.oracle.svm.core.configure.ConfigurationParser; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.phases.common.LazyValue; public class ConfigurationFileCollection { public static final Function FAIL_ON_EXCEPTION = e -> e; @@ -161,7 +163,7 @@ public ProxyConfiguration loadProxyConfig(Function excep return proxyConfiguration; } - public PredefinedClassesConfiguration loadPredefinedClassesConfig(Path[] classDestinationDirs, Predicate shouldExcludeClassesWithHash, + public PredefinedClassesConfiguration loadPredefinedClassesConfig(List> classDestinationDirs, Predicate shouldExcludeClassesWithHash, Function exceptionHandler) throws Exception { PredefinedClassesConfiguration predefinedClassesConfiguration = new PredefinedClassesConfiguration(classDestinationDirs, shouldExcludeClassesWithHash); loadConfig(predefinedClassesConfigPaths, predefinedClassesConfiguration.createParser(false), exceptionHandler); @@ -182,7 +184,7 @@ public SerializationConfiguration loadSerializationConfig(Function exceptionHandler, Path[] predefinedConfigClassDestinationDirs, + public ConfigurationSet loadConfigurationSet(Function exceptionHandler, List> predefinedConfigClassDestinationDirs, Predicate predefinedConfigClassWithHashExclusionPredicate) throws Exception { return new ConfigurationSet(loadReflectConfig(exceptionHandler), loadJniConfig(exceptionHandler), loadResourceConfig(exceptionHandler), loadProxyConfig(exceptionHandler), loadSerializationConfig(exceptionHandler), diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMethod.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMethod.java index 858d2bcc52dc..e66390d2733a 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMethod.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMethod.java @@ -25,6 +25,7 @@ package com.oracle.svm.configure.config; import java.io.IOException; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -85,15 +86,10 @@ public boolean isConstructor() { @Override public void printJson(JsonWriter writer) throws IOException { - writer.append('{'); - writer.quote("name").append(':').quote(name).append(','); - writer.quote("parameterTypes").append(":["); - String prefix = ""; - for (String type : SignatureUtil.toParameterTypes(internalSignature)) { - writer.append(prefix).quote(type); - prefix = ","; - } - writer.append("] }"); + writer.appendObjectStart(); + writer.quote("name").appendFieldSeparator().quote(name).appendSeparator(); + writer.quote("parameterTypes").appendFieldSeparator().print(Arrays.asList(SignatureUtil.toParameterTypes(internalSignature))); + writer.appendObjectEnd(); } @Override 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 6c5539f2ee3b..5e3211f488b7 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 @@ -40,6 +40,7 @@ import com.oracle.svm.core.configure.ConfigurationFile; import com.oracle.svm.core.util.VMError; +import jdk.graal.compiler.util.json.JsonPrettyWriter; import jdk.graal.compiler.util.json.JsonPrintable; import jdk.graal.compiler.util.json.JsonWriter; @@ -71,9 +72,10 @@ public ConfigurationSet(ConfigurationSet other) { other.predefinedClassesConfiguration.copy()); } + @SuppressWarnings("unchecked") public ConfigurationSet() { this(new TypeConfiguration(REFLECTION_KEY), new TypeConfiguration(JNI_KEY), new ResourceConfiguration(), new ProxyConfiguration(), new SerializationConfiguration(), - new PredefinedClassesConfiguration(new Path[0], hash -> false)); + new PredefinedClassesConfiguration(Collections.emptyList(), hash -> false)); } private ConfigurationSet mutate(ConfigurationSet other, Mutator mutator) { @@ -161,8 +163,8 @@ public static List writeConfigurationToAllPaths(Function writeConfigurationToAllPaths(Function configurationBase && configurationBase.isEmpty()) { + /* Do not add an empty field when there are no entries */ + continue; + } if (first) { first = false; } else { - writer.appendSeparator().newline(); + writer.appendSeparator(); } if (!configFile.equals(ConfigurationFile.RESOURCES)) { /* @@ -193,7 +199,7 @@ public static List writeConfigurationToAllPaths(Function accessedMethods = getMethodsByAccessibility(ConfigurationMemberAccessibility.ACCESSED); if (!accessedMethods.isEmpty()) { - writer.append(',').newline().quote("methods").append(':'); + writer.appendSeparator().quote("methods").appendFieldSeparator(); JsonPrinter.printCollection(writer, accessedMethods, Comparator.comparing(ConfigurationMethod::getName).thenComparing(Comparator.nullsFirst(Comparator.comparing(ConfigurationMethod::getInternalSignature))), @@ -489,7 +487,7 @@ public synchronized void printJson(JsonWriter writer) throws IOException { } } - writer.unindent().newline().append('}'); + writer.appendObjectEnd(); } private Set getMethodsByAccessibility(ConfigurationMemberAccessibility accessibility) { @@ -497,11 +495,11 @@ private Set getMethodsByAccessibility(ConfigurationMemberAc } private static void printField(Map.Entry entry, JsonWriter w) throws IOException { - w.append('{').quote("name").append(':').quote(entry.getKey()); + w.appendObjectStart().quote("name").appendFieldSeparator().quote(entry.getKey()); if (entry.getValue().isFinalButWritable()) { - w.append(", ").quote("allowWrite").append(':').append("true"); + w.appendSeparator().quote("allowWrite").appendFieldSeparator().append("true"); } - w.append('}'); + w.appendObjectEnd(); } private static void printJsonBooleanIfSet(JsonWriter writer, boolean predicate, String attribute) throws IOException { @@ -511,7 +509,7 @@ private static void printJsonBooleanIfSet(JsonWriter writer, boolean predicate, } private static void printJsonBoolean(JsonWriter writer, boolean value, String attribute) throws IOException { - writer.append(',').newline().quote(attribute).append(":").append(Boolean.toString(value)); + writer.appendSeparator().quote(attribute).appendFieldSeparator().append(Boolean.toString(value)); } private void removeFields(ConfigurationMemberDeclaration declaration, ConfigurationMemberAccessibility accessibility) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/PredefinedClassesConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/PredefinedClassesConfiguration.java index a1ebb57a9e39..2539272cb0c0 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/PredefinedClassesConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/PredefinedClassesConfiguration.java @@ -31,9 +31,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import jdk.graal.compiler.phases.common.LazyValue; import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.ConfigurationBase; @@ -46,11 +48,11 @@ import jdk.graal.compiler.util.json.JsonWriter; public final class PredefinedClassesConfiguration extends ConfigurationBase { - private final Path[] classDestinationDirs; + private final List> classDestinationDirs; private final ConcurrentMap classes = new ConcurrentHashMap<>(); private final java.util.function.Predicate shouldExcludeClassWithHash; - public PredefinedClassesConfiguration(Path[] classDestinationDirs, java.util.function.Predicate shouldExcludeClassWithHash) { + public PredefinedClassesConfiguration(List> classDestinationDirs, java.util.function.Predicate shouldExcludeClassWithHash) { this.classDestinationDirs = classDestinationDirs; this.shouldExcludeClassWithHash = shouldExcludeClassWithHash; } @@ -98,9 +100,9 @@ public void add(String nameInfo, byte[] classData) { return; } if (classDestinationDirs != null) { - for (Path dir : classDestinationDirs) { + for (LazyValue dir : classDestinationDirs) { try { - Files.write(dir.resolve(getFileName(hash)), classData); + Files.write(dir.get().resolve(getFileName(hash)), classData); } catch (IOException e) { throw new RuntimeException(e); } @@ -121,11 +123,11 @@ public void add(String nameInfo, String hash, URI baseUri) { } catch (Exception ignored) { localBaseDir = null; } - for (Path destDir : classDestinationDirs) { - if (!destDir.equals(localBaseDir)) { + for (LazyValue destDir : classDestinationDirs) { + if (!destDir.get().equals(localBaseDir)) { try { String fileName = getFileName(hash); - Path target = destDir.resolve(fileName); + Path target = destDir.get().resolve(fileName); if (baseUri != null) { try (InputStream is = PredefinedClassesConfigurationParser.openClassdataStream(baseUri, hash)) { Files.copy(is, target, StandardCopyOption.REPLACE_EXISTING); 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 24176fe86d56..50b7ac8199f1 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 @@ -352,19 +352,19 @@ public void printLegacyJson(JsonWriter writer) throws IOException { } void printResourcesJson(JsonWriter writer) throws IOException { - writer.quote(RESOURCES_KEY).append(':').append('{').indent().newline(); - writer.quote("includes").append(':'); + writer.quote(RESOURCES_KEY).appendFieldSeparator().appendObjectStart(); + writer.quote("includes").appendFieldSeparator(); JsonPrinter.printCollection(writer, addedResources, ConditionalElement.comparator(), ResourceConfiguration::conditionalRegexElementJson); if (!ignoredResources.isEmpty()) { - writer.append(',').newline(); - writer.quote("excludes").append(':'); + writer.appendSeparator(); + writer.quote("excludes").appendFieldSeparator(); JsonPrinter.printCollection(writer, ignoredResources.keySet(), ConditionalElement.comparator(), ResourceConfiguration::conditionalRegexElementJson); } - writer.unindent().newline().append('}'); + writer.appendObjectEnd(); } void printBundlesJson(JsonWriter writer) throws IOException { - writer.quote(BUNDLES_KEY).append(':'); + writer.quote(BUNDLES_KEY).appendFieldSeparator(); JsonPrinter.printCollection(writer, bundles.keySet(), ConditionalElement.comparator(), (p, w) -> printResourceBundle(bundles.get(p), w)); } @@ -379,23 +379,23 @@ public ConfigurationParser createParser(boolean strictMetadata) { } private static void printResourceBundle(BundleConfiguration config, JsonWriter writer) throws IOException { - writer.append('{').indent().newline(); + writer.appendObjectStart(); ConfigurationConditionPrintable.printConditionAttribute(config.condition, writer); - writer.quote("name").append(':').quote(config.baseName); + writer.quote("name").appendFieldSeparator().quote(config.baseName); if (!config.locales.isEmpty()) { - writer.append(',').newline().quote("locales").append(":"); + writer.appendSeparator().quote("locales").appendFieldSeparator(); JsonPrinter.printCollection(writer, config.locales, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); } if (!config.classNames.isEmpty()) { - writer.append(',').newline().quote("classNames").append(":"); + writer.appendSeparator().quote("classNames").appendFieldSeparator(); JsonPrinter.printCollection(writer, config.classNames, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); } - writer.unindent().newline().append('}'); + writer.appendObjectEnd(); } @Override public boolean isEmpty() { - return addedResources.isEmpty() && bundles.isEmpty(); + return addedResources.isEmpty() && bundles.isEmpty() && addedGlobs.isEmpty(); } @Override @@ -414,20 +414,20 @@ public boolean supportsCombinedFile() { private static void conditionalGlobElementJson(ConditionalElement p, JsonWriter w) throws IOException { String pattern = p.element().pattern(); String module = p.element().module(); - w.append('{').indent().newline(); + w.appendObjectStart(); ConfigurationConditionPrintable.printConditionAttribute(p.condition(), w); if (module != null) { - w.quote("module").append(':').quote(module).append(',').newline(); + w.quote("module").appendFieldSeparator().quote(module).appendSeparator(); } - w.quote("glob").append(':').quote(pattern); - w.unindent().newline().append('}'); + w.quote("glob").appendFieldSeparator().quote(pattern); + w.appendObjectEnd(); } private static void conditionalRegexElementJson(ConditionalElement p, JsonWriter w) throws IOException { - w.append('{').indent().newline(); + w.appendObjectStart(); ConfigurationConditionPrintable.printConditionAttribute(p.condition(), w); - w.quote("pattern").append(':').quote(p.element()); - w.unindent().newline().append('}'); + w.quote("pattern").appendFieldSeparator().quote(p.element()); + w.appendObjectEnd(); } public interface Predicate { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfiguration.java index 350ff72d715f..438fe8f50ab5 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfiguration.java @@ -27,7 +27,7 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -42,7 +42,7 @@ import com.oracle.svm.core.configure.SerializationConfigurationParser; import jdk.graal.compiler.java.LambdaUtils; -import jdk.graal.compiler.util.json.JsonPrintable; +import jdk.graal.compiler.util.json.JsonPrinter; import jdk.graal.compiler.util.json.JsonWriter; public final class SerializationConfiguration extends ConfigurationBase @@ -111,26 +111,22 @@ public boolean contains(UnresolvedConfigurationCondition condition, String seria @Override public void printJson(JsonWriter writer) throws IOException { List listOfCapturedClasses = new ArrayList<>(serializations); - Collections.sort(listOfCapturedClasses); - printSerializationClasses(writer, listOfCapturedClasses); + JsonPrinter.printCollection(writer, listOfCapturedClasses, Comparator.naturalOrder(), SerializationConfigurationType::printJson); } @Override public void printLegacyJson(JsonWriter writer) throws IOException { - writer.append('{').indent().newline(); + writer.appendObjectStart(); List listOfCapturedClasses = new ArrayList<>(serializations); - Collections.sort(listOfCapturedClasses); - writer.quote("types").append(":"); - printSerializationClasses(writer, listOfCapturedClasses); - writer.append(",").newline(); + writer.quote("types").appendFieldSeparator(); + JsonPrinter.printCollection(writer, listOfCapturedClasses, Comparator.naturalOrder(), SerializationConfigurationType::printLegacyJson); + writer.appendSeparator(); List listOfCapturingClasses = new ArrayList<>(lambdaSerializationCapturingTypes); - listOfCapturingClasses.sort(new SerializationConfigurationLambdaCapturingType.SerializationConfigurationLambdaCapturingTypesComparator()); - writer.quote("lambdaCapturingTypes").append(":"); - printSerializationClasses(writer, listOfCapturingClasses); - writer.append(",").newline().quote("proxies").append(":"); + writer.quote("lambdaCapturingTypes").appendFieldSeparator(); + JsonPrinter.printCollection(writer, listOfCapturingClasses, Comparator.naturalOrder(), SerializationConfigurationLambdaCapturingType::printJson); + writer.appendSeparator().quote("proxies").appendFieldSeparator(); printProxies(writer); - writer.unindent().newline(); - writer.append('}'); + writer.appendObjectEnd(); } @Override @@ -148,26 +144,6 @@ private void printProxies(JsonWriter writer) throws IOException { ProxyConfiguration.printProxyInterfaces(writer, lists); } - private static void printSerializationClasses(JsonWriter writer, List serializationConfigurationTypes) throws IOException { - writer.append('['); - writer.indent(); - - printSerializationTypes(serializationConfigurationTypes, writer); - - writer.unindent().newline(); - writer.append("]"); - } - - private static void printSerializationTypes(List serializationConfigurationTypes, JsonWriter writer) throws IOException { - String prefix = ""; - - for (JsonPrintable type : serializationConfigurationTypes) { - writer.append(prefix).newline(); - type.printJson(writer); - prefix = ","; - } - } - @Override public void registerIncludingAssociatedClasses(UnresolvedConfigurationCondition condition, Class clazz) { register(condition, clazz); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationLambdaCapturingType.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationLambdaCapturingType.java index d8bb8e3912c2..0e7798618d1e 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationLambdaCapturingType.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationLambdaCapturingType.java @@ -25,7 +25,6 @@ package com.oracle.svm.configure.config; import java.io.IOException; -import java.util.Comparator; import java.util.Objects; import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; @@ -35,7 +34,7 @@ import jdk.graal.compiler.util.json.JsonPrintable; import jdk.graal.compiler.util.json.JsonWriter; -public class SerializationConfigurationLambdaCapturingType implements JsonPrintable { +public class SerializationConfigurationLambdaCapturingType implements JsonPrintable, Comparable { private final UnresolvedConfigurationCondition condition; private final String qualifiedJavaName; @@ -82,15 +81,12 @@ public int hashCode() { return Objects.hash(condition, qualifiedJavaName); } - public static final class SerializationConfigurationLambdaCapturingTypesComparator implements Comparator { - - @Override - public int compare(SerializationConfigurationLambdaCapturingType o1, SerializationConfigurationLambdaCapturingType o2) { - int compareName = o1.qualifiedJavaName.compareTo(o2.qualifiedJavaName); - if (compareName != 0) { - return compareName; - } - return o1.condition.compareTo(o2.condition); + @Override + public int compareTo(SerializationConfigurationLambdaCapturingType other) { + int compareName = qualifiedJavaName.compareTo(other.qualifiedJavaName); + if (compareName != 0) { + return compareName; } + return condition.compareTo(other.condition); } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationType.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationType.java index d5531b26c13d..21a96a62368f 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationType.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationType.java @@ -66,15 +66,23 @@ public UnresolvedConfigurationCondition getCondition() { @Override public void printJson(JsonWriter writer) throws IOException { - writer.append('{').indent().newline(); + printJson(writer, SerializationConfigurationParser.TYPE_KEY); + } + + public void printLegacyJson(JsonWriter writer) throws IOException { + printJson(writer, SerializationConfigurationParser.NAME_KEY); + } + + private void printJson(JsonWriter writer, String key) throws IOException { + writer.appendObjectStart(); ConfigurationConditionPrintable.printConditionAttribute(condition, writer); - writer.quote(SerializationConfigurationParser.TYPE_KEY).append(':').quote(qualifiedJavaName); + writer.quote(key).appendFieldSeparator().quote(qualifiedJavaName); if (qualifiedCustomTargetConstructorJavaName != null) { - writer.append(',').newline(); - writer.quote(SerializationConfigurationParser.CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY).append(':') + writer.appendSeparator(); + writer.quote(SerializationConfigurationParser.CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY).appendFieldSeparator() .quote(qualifiedCustomTargetConstructorJavaName); } - writer.unindent().newline().append('}'); + writer.appendObjectEnd(); } @Override diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/AccessAdvisor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/AccessAdvisor.java index 31e5719d0ba0..c92f55613062 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/AccessAdvisor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/AccessAdvisor.java @@ -84,6 +84,7 @@ public final class AccessAdvisor { internalCallerFilter.addOrGetChildren("java.time.**", ConfigurationFilter.Inclusion.Exclude); internalCallerFilter.addOrGetChildren("java.util.**", ConfigurationFilter.Inclusion.Exclude); internalCallerFilter.addOrGetChildren("java.util.concurrent.atomic.*", ConfigurationFilter.Inclusion.Include); // Atomic*FieldUpdater + internalCallerFilter.addOrGetChildren("java.util.concurrent.atomic.AtomicReference", ConfigurationFilter.Inclusion.Exclude); // AtomicReference. internalCallerFilter.addOrGetChildren("java.util.Collections", ConfigurationFilter.Inclusion.Include); // java.util.Collections.zeroLengthArray // LogRecord.readObject looks up resource bundles internalCallerFilter.addOrGetChildren("java.util.logging.LogRecord", ConfigurationFilter.Inclusion.Include); @@ -212,30 +213,54 @@ public boolean shouldIgnore(LazyValue queriedClass, LazyValue ca return shouldIgnore(queriedClass, callerClass, true); } - public boolean shouldIgnoreJniMethodLookup(LazyValue queriedClass, LazyValue name, LazyValue signature, LazyValue callerClass) { + record JNICallDescriptor(String declaringClass, String name, String signature, boolean required) { + public boolean matches(String otherDeclaringClass, String otherName, String otherSignature) { + return (declaringClass == null || declaringClass.equals(otherDeclaringClass)) && + (otherName == null || name.equals(otherName)) && + (otherSignature == null || signature.equals(otherSignature)); + } + } + + private static final JNICallDescriptor[] JNI_STARTUP_SEQUENCE = new JNICallDescriptor[]{ + new JNICallDescriptor("sun.launcher.LauncherHelper", "getApplicationClass", "()Ljava/lang/Class;", true), + new JNICallDescriptor("java.lang.Class", "getCanonicalName", "()Ljava/lang/String;", false), + new JNICallDescriptor("java.lang.String", "lastIndexOf", "(I)I", false), + new JNICallDescriptor("java.lang.String", "substring", "(I)Ljava/lang/String;", false), + new JNICallDescriptor("java.lang.System", "getProperty", "(Ljava/lang/String;)Ljava/lang/String;", false), + new JNICallDescriptor("java.lang.System", "setProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false), + new JNICallDescriptor(null, "main", "([Ljava/lang/String;)V", true), + }; + + public boolean shouldIgnoreJniLookup(LazyValue queriedClass, LazyValue name, LazyValue signature, LazyValue callerClass) { assert !shouldIgnore(queriedClass, callerClass) : "must have been checked before"; if (!heuristicsEnabled) { return false; } - // Heuristic to ignore this sequence during startup: - // 1. Lookup of LauncherHelper.getApplicationClass() - // 2. Lookup of Class.getCanonicalName() -- only on Darwin - // 3. Lookup of application's main(String[]) - if ("sun.launcher.LauncherHelper".equals(queriedClass.get())) { - if (launchPhase == 0 && "getApplicationClass".equals(name.get()) && "()Ljava/lang/Class;".equals(signature.get())) { - launchPhase = 1; + if (launchPhase >= 0) { + JNICallDescriptor expectedCall = JNI_STARTUP_SEQUENCE[launchPhase]; + while (!expectedCall.matches(queriedClass.get(), name.get(), signature.get())) { + if ("sun.launcher.LauncherHelper".equals(queriedClass.get())) { + return true; + } + if (!expectedCall.required) { + launchPhase++; + expectedCall = JNI_STARTUP_SEQUENCE[launchPhase]; + } else { + launchPhase = -1; + return false; + } } - return true; - } - if (launchPhase == 1 && "getCanonicalName".equals(name.get()) && "()Ljava/lang/String;".equals(signature.get())) { - launchPhase = 2; - return true; - } - if (launchPhase > 0) { - launchPhase = -1; - if ("main".equals(name.get()) && "([Ljava/lang/String;)V".equals(signature.get())) { - return true; + if (name.get() != null && signature.get() != null) { + /* + * We ignore class lookups before field/method lookups but don't skip to the next + * query + */ + launchPhase++; + } + if (launchPhase == JNI_STARTUP_SEQUENCE.length) { + launchPhase = -1; } + return true; } /* * NOTE: JVM invocations cannot be reliably filtered with callerClass == null because these @@ -245,6 +270,10 @@ public boolean shouldIgnoreJniMethodLookup(LazyValue queriedClass, LazyV return false; } + public static boolean shouldIgnoreResourceLookup(LazyValue resource) { + return resource.get().equals("META-INF/services/jdk.vm.ci.services.JVMCIServiceLocator"); + } + public boolean shouldIgnoreLoadClass(LazyValue queriedClass, LazyValue callerClass) { assert !shouldIgnore(queriedClass, callerClass) : "must have been checked before"; if (!heuristicsEnabled) { 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 f6fb770cd8f2..8c973e41fc6b 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 @@ -24,6 +24,7 @@ */ package com.oracle.svm.configure.trace; +import static com.oracle.svm.configure.trace.LazyValueUtils.lazyNull; import static com.oracle.svm.configure.trace.LazyValueUtils.lazyValue; import java.util.List; @@ -64,9 +65,12 @@ void processEntry(EconomicMap entry, ConfigurationSet configurationSe String lookupName = singleElement(args); String internalName = (lookupName.charAt(0) != '[') ? ('L' + lookupName + ';') : lookupName; String forNameString = MetaUtil.internalNameToJava(internalName, true, true); - if (!advisor.shouldIgnore(lazyValue(forNameString), callerClassLazyValue)) { + LazyValue forNameStringLazyValue = lazyValue(forNameString); + if (!advisor.shouldIgnore(forNameStringLazyValue, callerClassLazyValue)) { if (function.equals("FindClass")) { - configurationSet.getJniConfiguration().getOrCreateType(condition, forNameString); + if (!advisor.shouldIgnoreJniLookup(forNameStringLazyValue, lazyNull(), lazyNull(), callerClassLazyValue)) { + configurationSet.getJniConfiguration().getOrCreateType(condition, forNameString); + } } else if (!AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(lookupName).matches()) { // DefineClass LogUtils.warning("Unsupported JNI function DefineClass used to load class " + forNameString); } @@ -95,7 +99,7 @@ void processEntry(EconomicMap entry, ConfigurationSet configurationSe expectSize(args, 2); String name = (String) args.get(0); String signature = (String) args.get(1); - if (!advisor.shouldIgnoreJniMethodLookup(lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) { + if (!advisor.shouldIgnoreJniLookup(lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) { config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration); if (!declaringClassOrClazz.equals(clazz)) { config.getOrCreateType(condition, clazz); @@ -107,9 +111,12 @@ void processEntry(EconomicMap entry, ConfigurationSet configurationSe case "GetStaticFieldID": { expectSize(args, 2); String name = (String) args.get(0); - config.getOrCreateType(condition, declaringClassOrClazz).addField(name, declaration, false); - if (!declaringClassOrClazz.equals(clazz)) { - config.getOrCreateType(condition, clazz); + String signature = (String) args.get(1); + if (!advisor.shouldIgnoreJniLookup(lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) { + config.getOrCreateType(condition, declaringClassOrClazz).addField(name, declaration, false); + if (!declaringClassOrClazz.equals(clazz)) { + config.getOrCreateType(condition, clazz); + } } break; } @@ -117,7 +124,7 @@ void processEntry(EconomicMap entry, ConfigurationSet configurationSe expectSize(args, 1); // exception message, ignore String name = ConfigurationMethod.CONSTRUCTOR_NAME; String signature = "(Ljava/lang/String;)V"; - if (!advisor.shouldIgnoreJniMethodLookup(lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) { + if (!advisor.shouldIgnoreJniLookup(lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) { config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration); assert declaringClassOrClazz.equals(clazz) : "Constructor can only be accessed via declaring class"; } 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 e81dfdad9763..9788d607c0bc 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 @@ -37,6 +37,7 @@ import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration; import com.oracle.svm.configure.config.ConfigurationMethod; import com.oracle.svm.configure.config.ConfigurationSet; +import com.oracle.svm.configure.config.ConfigurationType; import com.oracle.svm.configure.config.ResourceConfiguration; import com.oracle.svm.configure.config.SignatureUtil; import com.oracle.svm.configure.config.TypeConfiguration; @@ -78,12 +79,16 @@ public void processEntry(EconomicMap entry, ConfigurationSet configur expectSize(args, 2); String module = (String) args.get(0); String resource = (String) args.get(1); - resourceConfiguration.addGlobPattern(condition, resource, module); + if (!AccessAdvisor.shouldIgnoreResourceLookup(lazyValue(resource))) { + resourceConfiguration.addGlobPattern(condition, resource, module); + } return; } case "getResource", "getSystemResource", "getSystemResourceAsStream", "getResources", "getSystemResources" -> { String literal = singleElement(args); - resourceConfiguration.addGlobPattern(condition, literal, null); + if (!AccessAdvisor.shouldIgnoreResourceLookup(lazyValue(literal))) { + resourceConfiguration.addGlobPattern(condition, literal, null); + } return; } } @@ -203,8 +208,10 @@ public void processEntry(EconomicMap entry, ConfigurationSet configur if (parameterTypes == null) { // tolerated and equivalent to no parameter types parameterTypes = Collections.emptyList(); } - configuration.getOrCreateType(condition, clazzOrDeclaringClass).addMethod(name, SignatureUtil.toInternalSignature(parameterTypes), declaration, accessibility); - if (!clazzOrDeclaringClass.equals(clazz)) { + if (accessibility == ConfigurationMemberAccessibility.ACCESSED) { + configuration.getOrCreateType(condition, clazzOrDeclaringClass).addMethod(name, SignatureUtil.toInternalSignature(parameterTypes), declaration, accessibility); + } + if (accessibility == ConfigurationMemberAccessibility.QUERIED || (!trackReflectionMetadata && !clazzOrDeclaringClass.equals(clazz))) { configuration.getOrCreateType(condition, clazz); } break; @@ -225,7 +232,10 @@ public void processEntry(EconomicMap entry, ConfigurationSet configur } String signature = SignatureUtil.toInternalSignature(parameterTypes); assert clazz.equals(clazzOrDeclaringClass) : "Constructor can only be accessed via declaring class"; - configuration.getOrCreateType(condition, clazzOrDeclaringClass).addMethod(ConfigurationMethod.CONSTRUCTOR_NAME, signature, declaration, accessibility); + ConfigurationType configurationType = configuration.getOrCreateType(condition, clazzOrDeclaringClass); + if (accessibility == ConfigurationMemberAccessibility.ACCESSED) { + configurationType.addMethod(ConfigurationMethod.CONSTRUCTOR_NAME, signature, declaration, accessibility); + } break; } @@ -284,7 +294,7 @@ public void processEntry(EconomicMap entry, ConfigurationSet configur @SuppressWarnings("unchecked") private static ConfigurationTypeDescriptor descriptorForClass(Object clazz) { if (clazz instanceof List) { - return new ProxyConfigurationTypeDescriptor(((List) clazz).toArray(String[]::new)); + return new ProxyConfigurationTypeDescriptor((List) clazz); } else { return new NamedConfigurationTypeDescriptor((String) clazz); } 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 93f4d559f21c..23730c8a4ca2 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 @@ -265,12 +265,9 @@ private static JsonParserException failOnSchemaError(String message) { protected record TypeDescriptorWithOrigin(ConfigurationTypeDescriptor typeDescriptor, boolean definedAsType) { } - protected static Optional parseTypeOrName(EconomicMap data, boolean treatAllNameEntriesAsType) { - Object typeObject = data.get(TYPE_KEY); + protected static Optional parseName(EconomicMap data, boolean treatAllNameEntriesAsType) { Object name = data.get(NAME_KEY); - if (typeObject != null) { - return parseTypeContents(typeObject).map(typeDescriptor -> new TypeDescriptorWithOrigin(typeDescriptor, true)); - } else if (name != null) { + if (name != null) { NamedConfigurationTypeDescriptor typeDescriptor = new NamedConfigurationTypeDescriptor(asString(name)); return Optional.of(new TypeDescriptorWithOrigin(typeDescriptor, treatAllNameEntriesAsType)); } else { @@ -298,7 +295,7 @@ protected static Optional parseTypeContents(Object private static ProxyConfigurationTypeDescriptor getProxyDescriptor(Object proxyObject) { List proxyInterfaces = asList(proxyObject, "proxy interface content should be an interface list"); - String[] proxyInterfaceNames = proxyInterfaces.stream().map(obj -> asString(obj, "proxy")).toArray(String[]::new); + List proxyInterfaceNames = proxyInterfaces.stream().map(obj -> asString(obj, "proxy")).toList(); return new ProxyConfigurationTypeDescriptor(proxyInterfaceNames); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacyReflectionConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacyReflectionConfigurationParser.java index 6a7a40795332..a123418dc670 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacyReflectionConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacyReflectionConfigurationParser.java @@ -26,7 +26,6 @@ import java.net.URI; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Optional; @@ -38,7 +37,7 @@ final class LegacyReflectionConfigurationParser extends ReflectionConfigurationParser { - private static final List OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS = Arrays.asList("name", "type", "allDeclaredConstructors", "allPublicConstructors", + private static final List OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS = Arrays.asList("allDeclaredConstructors", "allPublicConstructors", "allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields", "allDeclaredClasses", "allRecordComponents", "allPermittedSubclasses", "allNestMembers", "allSigners", "allPublicClasses", "methods", "queriedMethods", "fields", CONDITIONAL_KEY, @@ -59,10 +58,9 @@ public void parseAndRegister(Object json, URI origin) { @Override protected void parseClass(EconomicMap data) { - checkAttributes(data, "reflection class descriptor object", Collections.emptyList(), OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS); - checkHasExactlyOneAttribute(data, "reflection class descriptor object", List.of(NAME_KEY, TYPE_KEY)); + checkAttributes(data, "reflection class descriptor object", List.of(NAME_KEY), OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS); - Optional type = parseTypeOrName(data, treatAllNameEntriesAsType); + Optional type = parseName(data, treatAllNameEntriesAsType); if (type.isEmpty()) { return; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacySerializationConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacySerializationConfigurationParser.java index b03876c7ed44..46c92f8b389c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacySerializationConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacySerializationConfigurationParser.java @@ -28,7 +28,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Optional; import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; @@ -85,18 +84,11 @@ protected void parseSerializationDescriptorObject(EconomicMap da if (lambdaCapturingType) { checkAttributes(data, "serialization descriptor object", Collections.singleton(NAME_KEY), Collections.singleton(CONDITIONAL_KEY)); } else { - checkAttributes(data, "serialization descriptor object", Collections.emptySet(), Arrays.asList(TYPE_KEY, NAME_KEY, CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY, CONDITIONAL_KEY)); - checkHasExactlyOneAttribute(data, "serialization descriptor object", List.of(TYPE_KEY, NAME_KEY)); + checkAttributes(data, "serialization descriptor object", Collections.singleton(NAME_KEY), Arrays.asList(CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY, CONDITIONAL_KEY)); } - Optional target = parseTypeOrName(data, false); - if (target.isEmpty()) { - return; - } - ConfigurationTypeDescriptor targetSerializationClass = target.get().typeDescriptor(); - boolean definedAsType = target.get().definedAsType(); - - UnresolvedConfigurationCondition unresolvedCondition = parseCondition(data, definedAsType); + ConfigurationTypeDescriptor targetSerializationClass = new NamedConfigurationTypeDescriptor(asString(data.get(NAME_KEY))); + UnresolvedConfigurationCondition unresolvedCondition = parseCondition(data, false); var condition = conditionResolver.resolveCondition(unresolvedCondition); if (!condition.isPresent()) { return; @@ -111,7 +103,7 @@ protected void parseSerializationDescriptorObject(EconomicMap da if (targetSerializationClass instanceof NamedConfigurationTypeDescriptor namedClass) { serializationSupport.registerWithTargetConstructorClass(condition.get(), namedClass.name(), customTargetConstructorClass); } else if (targetSerializationClass instanceof ProxyConfigurationTypeDescriptor proxyClass) { - serializationSupport.registerProxyClass(condition.get(), Arrays.asList(proxyClass.interfaceNames())); + serializationSupport.registerProxyClass(condition.get(), proxyClass.interfaceNames()); } else { throw new JsonParserException("Unknown configuration type descriptor: %s".formatted(targetSerializationClass.toString())); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/PredefinedClassesConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/PredefinedClassesConfigurationParser.java index f477fd180829..532db43ab1b5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/PredefinedClassesConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/PredefinedClassesConfigurationParser.java @@ -29,11 +29,14 @@ import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import org.graalvm.collections.EconomicMap; +import jdk.graal.compiler.phases.common.LazyValue; import jdk.graal.compiler.util.json.JsonParserException; public class PredefinedClassesConfigurationParser extends ConfigurationParser { @@ -121,4 +124,14 @@ private void parseClass(URI baseUri, EconomicMap data) { String nameInfo = asNullableString(data.get("nameInfo"), "nameInfo"); registry.add(nameInfo, hash, baseUri); } + + public static LazyValue directorySupplier(Path root) { + return new LazyValue<>(() -> { + try { + return Files.createDirectories(root.resolve(ConfigurationFile.PREDEFINED_CLASSES_AGENT_EXTRACTED_SUBDIR)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationTypeDescriptor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationTypeDescriptor.java index 77b539e8a66a..c60f589852c9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationTypeDescriptor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationTypeDescriptor.java @@ -28,17 +28,16 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Set; import com.oracle.svm.core.reflect.proxy.DynamicProxySupport; import jdk.graal.compiler.util.json.JsonPrinter; import jdk.graal.compiler.util.json.JsonWriter; -public record ProxyConfigurationTypeDescriptor(String[] interfaceNames) implements ConfigurationTypeDescriptor { +public record ProxyConfigurationTypeDescriptor(List interfaceNames) implements ConfigurationTypeDescriptor { - public ProxyConfigurationTypeDescriptor(String[] interfaceNames) { - this.interfaceNames = Arrays.stream(interfaceNames).map(ConfigurationTypeDescriptor::checkQualifiedJavaName).toArray(String[]::new); + public ProxyConfigurationTypeDescriptor(List interfaceNames) { + this.interfaceNames = interfaceNames.stream().map(ConfigurationTypeDescriptor::checkQualifiedJavaName).toList(); } @Override @@ -48,18 +47,18 @@ public Kind getDescriptorType() { @Override public String toString() { - return DynamicProxySupport.proxyTypeDescriptor(interfaceNames); + return DynamicProxySupport.proxyTypeDescriptor(interfaceNames.toArray(String[]::new)); } @Override public Collection getAllQualifiedJavaNames() { - return Set.of(interfaceNames); + return interfaceNames; } @Override public int compareTo(ConfigurationTypeDescriptor other) { if (other instanceof ProxyConfigurationTypeDescriptor proxyOther) { - return Arrays.compare(interfaceNames, proxyOther.interfaceNames); + return Arrays.compare(interfaceNames.toArray(String[]::new), proxyOther.interfaceNames.toArray(String[]::new)); } else { return getDescriptorType().compareTo(other.getDescriptorType()); } @@ -67,9 +66,9 @@ public int compareTo(ConfigurationTypeDescriptor other) { @Override public void printJson(JsonWriter writer) throws IOException { - writer.append("{").indent().newline(); - writer.quote("proxy").append(":"); - JsonPrinter.printCollection(writer, List.of(interfaceNames), null, (String p, JsonWriter w) -> w.quote(p)); - writer.unindent().newline().append("}"); + writer.appendObjectStart(); + writer.quote("proxy").appendFieldSeparator(); + JsonPrinter.printCollection(writer, interfaceNames, null, (String p, JsonWriter w) -> w.quote(p)); + writer.appendObjectEnd(); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/SerializationConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/SerializationConfigurationParser.java index 87570bd767c9..e34d651a3030 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/SerializationConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/SerializationConfigurationParser.java @@ -25,7 +25,6 @@ */ package com.oracle.svm.core.configure; -import java.util.Arrays; import java.util.List; import org.graalvm.collections.EconomicMap; @@ -68,7 +67,7 @@ protected void registerType(ConfigurationTypeDescriptor targetSerializationClass if (targetSerializationClass instanceof NamedConfigurationTypeDescriptor namedClass) { serializationSupport.registerWithTargetConstructorClass(condition, namedClass.name(), customTargetConstructorClass); } else if (targetSerializationClass instanceof ProxyConfigurationTypeDescriptor proxyClass) { - serializationSupport.registerProxyClass(condition, Arrays.asList(proxyClass.interfaceNames())); + serializationSupport.registerProxyClass(condition, proxyClass.interfaceNames()); } else { throw new JsonParserException("Unknown configuration type descriptor: %s".formatted(targetSerializationClass.toString())); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java index 51f5de9d428e..76ea23a764d6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java @@ -30,7 +30,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.graalvm.nativeimage.ImageSingletons; @@ -114,7 +113,7 @@ private TypeResult> resolveNamedType(NamedConfigurationTypeDescriptor t private TypeResult> resolveProxyType(ProxyConfigurationTypeDescriptor typeDescriptor) { String typeName = typeDescriptor.toString(); - List>> interfaceResults = Arrays.stream(typeDescriptor.interfaceNames()).map(name -> resolveNamedType(new NamedConfigurationTypeDescriptor(name), false)).toList(); + List>> interfaceResults = typeDescriptor.interfaceNames().stream().map(name -> resolveNamedType(new NamedConfigurationTypeDescriptor(name), false)).toList(); List> interfaces = new ArrayList<>(); for (TypeResult> intf : interfaceResults) { if (!intf.isPresent()) {