From 6b4e66aebd2a82d74246dc64d5a47cd86fd92b1f Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Tue, 26 Nov 2024 10:21:41 +0100 Subject: [PATCH] Introduce serializable flag to reflection metadata --- .../native-image/ReachabilityMetadata.md | 23 ++++++++----------- ... reachability-metadata-schema-v1.1.0.json} | 7 +++++- substratevm/CHANGELOG.md | 1 + .../configure/config/ConfigurationType.java | 9 +++++++- .../config/ParserConfigurationAdapter.java | 6 +++++ .../trace/SerializationProcessor.java | 5 +++- .../core/configure/ConfigurationFiles.java | 2 +- .../LegacyReflectionConfigurationParser.java | 2 +- ...ReflectionConfigurationParserDelegate.java | 2 ++ .../configure/ReflectionMetadataParser.java | 4 +++- .../config/ConfigurationParserUtils.java | 6 +++-- .../config/ReflectionRegistryAdapter.java | 13 +++++++++-- .../svm/hosted/config/RegistryAdapter.java | 11 +++++++-- .../svm/hosted/jni/JNIAccessFeature.java | 5 ++-- .../svm/hosted/reflect/ReflectionFeature.java | 6 +++-- .../reflect/proxy/DynamicProxyFeature.java | 22 ++++++++++++++---- .../serialize/SerializationFeature.java | 22 +++++++++++++----- 17 files changed, 106 insertions(+), 40 deletions(-) rename docs/reference-manual/native-image/assets/{reachability-metadata-schema-v1.0.0.json => reachability-metadata-schema-v1.1.0.json} (98%) diff --git a/docs/reference-manual/native-image/ReachabilityMetadata.md b/docs/reference-manual/native-image/ReachabilityMetadata.md index f1d349eadfdd..6c38282498a0 100644 --- a/docs/reference-manual/native-image/ReachabilityMetadata.md +++ b/docs/reference-manual/native-image/ReachabilityMetadata.md @@ -123,7 +123,7 @@ Computing metadata in code can be achieved in two ways: ## Specifying Metadata with JSON All metadata specified in the _reachability-metadata.json_ file that is located in any of the classpath entries at _META-INF/native-image/\\/\\/_. -The JSON schema for the reachability metadata is defined in [reachability-metadata-schema-v1.0.0.json](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.0.0.json). +The JSON schema for the reachability metadata is defined in [reachability-metadata-schema-v1.1.0.json](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json). A sample _reachability-metadata.json_ file can be found [in the sample section](#sample-reachability-metadata). The _reachability-metadata.json_ configuration contains a single object with one field for each type of metadata. Each field in the top-level object contains an array of *metadata entries*: @@ -132,7 +132,6 @@ The _reachability-metadata.json_ configuration contains a single object with one "reflection":[], "resources":[], "bundles":[], - "serialization":[], "jni":[] } ``` @@ -640,14 +639,15 @@ To create a custom constructor for serialization use: Proxy classes can only be registered for serialization via the JSON files. ### Serialization Metadata in JSON -Serialization metadata is specified in the `serialization` section of _reachability-metadata.json_. +Serialization metadata is specified in the `reflection` section of _reachability-metadata.json_. To specify a regular `serialized.Type` use ```json { - "serialization": [ + "reflection": [ { - "type": "serialized.Type" + "type": "serialized.Type", + "serializable": true } ] } @@ -656,10 +656,11 @@ To specify a regular `serialized.Type` use To specify a proxy class for serialization, use the following entry: ```json { - "serialization": [ + "reflection": [ { "type": { - "proxy": ["FullyQualifiedInterface1", "...", "FullyQualifiedInterfaceN"] + "proxy": ["FullyQualifiedInterface1", "...", "FullyQualifiedInterfaceN"], + "serializable": true } } ] @@ -700,7 +701,8 @@ See below is a sample reachability metadata configuration that you can use in _r "allPublicFields": true, "allDeclaredMethods": true, "allPublicMethods": true, - "unsafeAllocated": true + "unsafeAllocated": true, + "serializable": true } ], "jni": [ @@ -736,11 +738,6 @@ See below is a sample reachability metadata configuration that you can use in _r "name": "fully.qualified.bundle.name", "locales": ["en", "de", "other_optional_locales"] } - ], - "serialization": [ - { - "type": "serialized.Type" - } ] } ``` diff --git a/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.0.0.json b/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json similarity index 98% rename from docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.0.0.json rename to docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json index a3a75dd1d5e1..065247bf3a09 100644 --- a/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.0.0.json +++ b/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2019-09/schema", - "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.0.0.json", + "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json", "title": "JSON schema for the reachability metadata used by GraalVM Native Image", "type": "object", "default": {}, @@ -202,6 +202,11 @@ "title": "Allow objects of this class to be instantiated with a call to jdk.internal.misc.Unsafe#allocateInstance or JNI's AllocObject", "type": "boolean", "default": false + }, + "serializable": { + "title": "Allow objects of this class to be serialized and deserialized", + "type": "boolean", + "default": false } }, "additionalProperties": false diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 5ddf1ff5179c..ad49fdae0dc5 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -23,6 +23,7 @@ At runtime, premain runtime options are set along with main class' arguments in * (GR-59326) Ensure builder ForkJoin commonPool parallelism always respects NativeImageOptions.NumberOfThreads. * (GR-60081) Native Image now targets `armv8.1-a` by default on AArch64. Use `-march=compatibility` for best compatibility or `-march=native` for best performance if the native executable is deployed on the same machine or on a machine with the same CPU features. To list all available machine types, use `-march=list`. * (GR-60234) Remove `"customTargetConstructorClass"` field from the serialization JSON metadata. All possible constructors are now registered by default when registering a type for serialization. `RuntimeSerialization.registerWithTargetConstructorClass` is now deprecated. +* (GR-60237) Include serialization JSON reachability metadata as part of reflection metadata by introducing the `"serializable"` flag for reflection entries. ## GraalVM for JDK 23 (Internal Version 24.1.0) * (GR-51520) The old class initialization strategy, which was deprecated in GraalVM for JDK 22, is removed. The option `StrictImageHeap` no longer has any effect. diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java index 8d5e620eb639..5f7577524ad1 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java @@ -105,6 +105,7 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType private ConfigurationMemberAccessibility allPublicMethodsAccess = ConfigurationMemberAccessibility.NONE; private ConfigurationMemberAccessibility allDeclaredConstructorsAccess = ConfigurationMemberAccessibility.NONE; private ConfigurationMemberAccessibility allPublicConstructorsAccess = ConfigurationMemberAccessibility.NONE; + private boolean serializable = false; public ConfigurationType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean includeAllElements) { this.condition = condition; @@ -294,6 +295,7 @@ private void setFlagsFromOther(ConfigurationType other, BiPredicate entry, ConfigurationSet configurationSe String function = (String) entry.get("function"); List args = (List) entry.get("args"); SerializationConfiguration serializationConfiguration = configurationSet.getSerializationConfiguration(); + TypeConfiguration reflectionConfiguration = configurationSet.getReflectionConfiguration(); if ("ObjectStreamClass.".equals(function) || "ObjectInputStream.readClassDescriptor".equals(function)) { expectSize(args, 1); @@ -68,7 +71,7 @@ void processEntry(EconomicMap entry, ConfigurationSet configurationSe if (className.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) { serializationConfiguration.registerLambdaCapturingClass(condition, className); } else { - serializationConfiguration.register(condition, className); + reflectionConfiguration.getOrCreateType(condition, new NamedConfigurationTypeDescriptor(className)).setSerializable(); } } else if ("SerializedLambda.readResolve".equals(function)) { expectSize(args, 1); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java index 53bfa1a703ea..ed156431cb91 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java @@ -122,7 +122,7 @@ public static final class Options { AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter()); @Option(help = "Resources describing reachability metadata needed for the program " + - "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.0.0.json", type = OptionType.User)// + "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json", type = OptionType.User)// public static final HostedOptionKey ReachabilityMetadataResources = new HostedOptionKey<>( AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter()); 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 a123418dc670..37cd6b903a53 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 @@ -41,7 +41,7 @@ final class LegacyReflectionConfigurationParser extends ReflectionConfigur "allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields", "allDeclaredClasses", "allRecordComponents", "allPermittedSubclasses", "allNestMembers", "allSigners", "allPublicClasses", "methods", "queriedMethods", "fields", CONDITIONAL_KEY, - "queryAllDeclaredConstructors", "queryAllPublicConstructors", "queryAllDeclaredMethods", "queryAllPublicMethods", "unsafeAllocated"); + "queryAllDeclaredConstructors", "queryAllPublicConstructors", "queryAllDeclaredMethods", "queryAllPublicMethods", "unsafeAllocated", "serializable"); private final boolean treatAllNameEntriesAsType; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java index 748262afabb7..73463542d5db 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java @@ -70,6 +70,8 @@ public interface ReflectionConfigurationParserDelegate { void registerUnsafeAllocated(C condition, T clazz); + void registerAsSerializable(C condition, T clazz); + String getTypeName(T type); String getSimpleName(T type); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionMetadataParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionMetadataParser.java index 33bd0028be6c..ffa54d572be1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionMetadataParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionMetadataParser.java @@ -38,7 +38,7 @@ class ReflectionMetadataParser extends ReflectionConfigurationParser { private static final List OPTIONAL_REFLECT_METADATA_ATTRS = Arrays.asList(CONDITIONAL_KEY, "allDeclaredConstructors", "allPublicConstructors", "allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields", - "methods", "fields", "unsafeAllocated"); + "methods", "fields", "unsafeAllocated", "serializable"); private final String combinedFileKey; @@ -107,6 +107,8 @@ protected void parseClass(EconomicMap data) { registerIfNotDefault(data, false, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, clazz)); registerIfNotDefault(data, false, clazz, "unsafeAllocated", () -> delegate.registerUnsafeAllocated(condition, clazz)); + registerIfNotDefault(data, false, clazz, "serializable", () -> delegate.registerAsSerializable(condition, clazz)); + MapCursor cursor = data.getEntries(); while (cursor.advance()) { String name = cursor.getKey(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java index 1d04ab086955..c1af19efa6ff 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java @@ -57,13 +57,15 @@ import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry; import jdk.graal.compiler.util.json.JsonParserException; +import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; public final class ConfigurationParserUtils { public static ReflectionConfigurationParser> create(String combinedFileKey, boolean strictMetadata, - ConfigurationConditionResolver conditionResolver, ReflectionRegistry registry, ProxyRegistry proxyRegistry, ImageClassLoader imageClassLoader) { + ConfigurationConditionResolver conditionResolver, ReflectionRegistry registry, ProxyRegistry proxyRegistry, + RuntimeSerializationSupport serializationSupport, ImageClassLoader imageClassLoader) { return ReflectionConfigurationParser.create(combinedFileKey, strictMetadata, conditionResolver, - RegistryAdapter.create(registry, proxyRegistry, imageClassLoader), + RegistryAdapter.create(registry, proxyRegistry, serializationSupport, imageClassLoader), ConfigurationFiles.Options.StrictConfiguration.getValue(), ConfigurationFiles.Options.WarnAboutMissingReflectionOrJNIMetadataElements.getValue(), TreatAllNameEntriesAsType.getValue()); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java index c2b13640d2f6..58b71e955769 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java @@ -27,24 +27,28 @@ import java.lang.reflect.Proxy; import java.util.Arrays; -import com.oracle.svm.hosted.reflect.ReflectionDataBuilder; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; +import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; import com.oracle.svm.core.TypeResult; import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; import com.oracle.svm.core.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.hosted.ImageClassLoader; +import com.oracle.svm.hosted.reflect.ReflectionDataBuilder; import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry; public class ReflectionRegistryAdapter extends RegistryAdapter { private final RuntimeReflectionSupport reflectionSupport; private final ProxyRegistry proxyRegistry; + private final RuntimeSerializationSupport serializationSupport; - ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, ProxyRegistry proxyRegistry, ImageClassLoader classLoader) { + ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, ProxyRegistry proxyRegistry, RuntimeSerializationSupport serializationSupport, + ImageClassLoader classLoader) { super(reflectionSupport, classLoader); this.reflectionSupport = reflectionSupport; this.proxyRegistry = proxyRegistry; + this.serializationSupport = serializationSupport; } @Override @@ -126,4 +130,9 @@ public void registerPublicConstructors(ConfigurationCondition condition, boolean public void registerDeclaredConstructors(ConfigurationCondition condition, boolean queriedOnly, Class type) { reflectionSupport.registerAllDeclaredConstructorsQuery(condition, queriedOnly, type); } + + @Override + public void registerAsSerializable(ConfigurationCondition condition, Class clazz) { + serializationSupport.register(condition, clazz); + } } 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 76ea23a764d6..6766d62d1593 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 @@ -36,6 +36,7 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.ReflectionRegistry; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; +import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; import com.oracle.svm.core.TypeResult; import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; @@ -52,9 +53,10 @@ public class RegistryAdapter implements ReflectionConfigurationParserDelegate serializationSupport, + ImageClassLoader classLoader) { if (registry instanceof RuntimeReflectionSupport) { - return new ReflectionRegistryAdapter((RuntimeReflectionSupport) registry, proxyRegistry, classLoader); + return new ReflectionRegistryAdapter((RuntimeReflectionSupport) registry, proxyRegistry, serializationSupport, classLoader); } else { return new RegistryAdapter(registry, classLoader); } @@ -289,6 +291,11 @@ private void registerExecutable(ConfigurationCondition condition, boolean querie registry.register(condition, queriedOnly, executable); } + @Override + public void registerAsSerializable(ConfigurationCondition condition, Class clazz) { + /* Serializable has no effect on JNI registrations */ + } + @Override public String getTypeName(Class type) { return type.getTypeName(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java index c79b30e0ab31..f0526da38fe7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java @@ -207,9 +207,10 @@ public void afterRegistration(AfterRegistrationAccess arg) { ConfigurationConditionResolver conditionResolver = new NativeImageConditionResolver(access.getImageClassLoader(), ClassInitializationSupport.singleton()); - ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(JNI_KEY, true, conditionResolver, runtimeSupport, null, access.getImageClassLoader()); + ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(JNI_KEY, true, conditionResolver, runtimeSupport, null, null, + access.getImageClassLoader()); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurationsFromCombinedFile(parser, access.getImageClassLoader(), "JNI"); - ReflectionConfigurationParser> legacyParser = ConfigurationParserUtils.create(null, false, conditionResolver, runtimeSupport, null, + ReflectionConfigurationParser> legacyParser = ConfigurationParserUtils.create(null, false, conditionResolver, runtimeSupport, null, null, access.getImageClassLoader()); loadedConfigurations += ConfigurationParserUtils.parseAndRegisterConfigurations(legacyParser, access.getImageClassLoader(), "JNI", ConfigurationFiles.Options.JNIConfigurationFiles, ConfigurationFiles.Options.JNIConfigurationResources, ConfigurationFile.JNI.getFileName()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java index c4bd43670b13..0d788afc7dad 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java @@ -46,6 +46,7 @@ import org.graalvm.nativeimage.impl.AnnotationExtractor; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; +import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; import com.oracle.graal.pointsto.ObjectScanner; import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; @@ -292,11 +293,12 @@ public void duringSetup(DuringSetupAccess a) { var conditionResolver = new NativeImageConditionResolver(access.getImageClassLoader(), ClassInitializationSupport.singleton()); reflectionData.duringSetup(access.getMetaAccess(), aUniverse); ProxyRegistry proxyRegistry = ImageSingletons.lookup(ProxyRegistry.class); + RuntimeSerializationSupport serializationSupport = RuntimeSerializationSupport.singleton(); ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(REFLECTION_KEY, true, conditionResolver, reflectionData, proxyRegistry, - access.getImageClassLoader()); + serializationSupport, access.getImageClassLoader()); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurationsFromCombinedFile(parser, access.getImageClassLoader(), "reflection"); ReflectionConfigurationParser> legacyParser = ConfigurationParserUtils.create(null, false, conditionResolver, reflectionData, proxyRegistry, - access.getImageClassLoader()); + serializationSupport, access.getImageClassLoader()); loadedConfigurations += ConfigurationParserUtils.parseAndRegisterConfigurations(legacyParser, access.getImageClassLoader(), "reflection", ConfigurationFiles.Options.ReflectionConfigurationFiles, ConfigurationFiles.Options.ReflectionConfigurationResources, ConfigurationFile.REFLECTION.getFileName()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java index b8367d0ffe59..04e96e716b3f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java @@ -39,6 +39,7 @@ import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; import com.oracle.svm.core.reflect.proxy.DynamicProxySupport; import com.oracle.svm.hosted.FallbackFeature; +import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.ImageClassLoader; @@ -50,18 +51,29 @@ public final class DynamicProxyFeature implements InternalFeature { private int loadedConfigurations; private Field proxyCacheField; + private ProxyRegistry proxyRegistry; @Override - public void duringSetup(DuringSetupAccess a) { - DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; - + public void afterRegistration(AfterRegistrationAccess a) { + FeatureImpl.AfterRegistrationAccessImpl access = (FeatureImpl.AfterRegistrationAccessImpl) a; ImageClassLoader imageClassLoader = access.getImageClassLoader(); - ConfigurationConditionResolver conditionResolver = new NativeImageConditionResolver(imageClassLoader, ClassInitializationSupport.singleton()); DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(); ImageSingletons.add(DynamicProxyRegistry.class, dynamicProxySupport); ImageSingletons.add(RuntimeProxyCreationSupport.class, dynamicProxySupport); - ProxyRegistry proxyRegistry = new ProxyRegistry(dynamicProxySupport, imageClassLoader); + proxyRegistry = new ProxyRegistry(dynamicProxySupport, imageClassLoader); + /* + * ImageSingletons registration has to happen after registration to be available for + * SerializationFeature + */ ImageSingletons.add(ProxyRegistry.class, proxyRegistry); + } + + @Override + public void duringSetup(DuringSetupAccess a) { + DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; + ImageClassLoader imageClassLoader = access.getImageClassLoader(); + ConfigurationConditionResolver conditionResolver = new NativeImageConditionResolver(imageClassLoader, ClassInitializationSupport.singleton()); + ProxyConfigurationParser parser = new ProxyConfigurationParser<>(conditionResolver, ConfigurationFiles.Options.StrictConfiguration.getValue(), proxyRegistry); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurations(parser, imageClassLoader, "dynamic proxy", ConfigurationFiles.Options.DynamicProxyConfigurationFiles, ConfigurationFiles.Options.DynamicProxyConfigurationResources, diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java index c878db324403..271f8268ac16 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java @@ -103,6 +103,7 @@ public class SerializationFeature implements InternalFeature { final Set> capturingClasses = ConcurrentHashMap.newKeySet(); private SerializationBuilder serializationBuilder; + private SerializationDenyRegistry serializationDenyRegistry; private int loadedConfigurations; @Override @@ -111,15 +112,24 @@ public List> getRequiredFeatures() { } @Override - public void duringSetup(DuringSetupAccess a) { - FeatureImpl.DuringSetupAccessImpl access = (FeatureImpl.DuringSetupAccessImpl) a; + public void afterRegistration(AfterRegistrationAccess a) { + FeatureImpl.AfterRegistrationAccessImpl access = (FeatureImpl.AfterRegistrationAccessImpl) a; ImageClassLoader imageClassLoader = access.getImageClassLoader(); - ConfigurationConditionResolver conditionResolver = new NativeImageConditionResolver(imageClassLoader, ClassInitializationSupport.singleton()); ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("serialization configuration", imageClassLoader); - SerializationDenyRegistry serializationDenyRegistry = new SerializationDenyRegistry(typeResolver); + serializationDenyRegistry = new SerializationDenyRegistry(typeResolver); serializationBuilder = new SerializationBuilder(serializationDenyRegistry, access, typeResolver, ImageSingletons.lookup(ProxyRegistry.class)); + /* + * The serialization builder registration has to happen after registration so the + * ReflectionFeature can access it when creating parsers during setup. + */ ImageSingletons.add(RuntimeSerializationSupport.class, serializationBuilder); + } + @Override + public void duringSetup(DuringSetupAccess a) { + FeatureImpl.DuringSetupAccessImpl access = (FeatureImpl.DuringSetupAccessImpl) a; + ImageClassLoader imageClassLoader = access.getImageClassLoader(); + ConfigurationConditionResolver conditionResolver = new NativeImageConditionResolver(imageClassLoader, ClassInitializationSupport.singleton()); Boolean strictConfiguration = ConfigurationFiles.Options.StrictConfiguration.getValue(); SerializationConfigurationParser parser = SerializationConfigurationParser.create(true, conditionResolver, serializationBuilder, @@ -270,7 +280,7 @@ final class SerializationBuilder extends ConditionalConfigurationRegistry implem final SerializationSupport serializationSupport; private final SerializationDenyRegistry denyRegistry; private final ConfigurationTypeResolver typeResolver; - private final FeatureImpl.DuringSetupAccessImpl access; + private final FeatureImpl.AfterRegistrationAccessImpl access; private final Method disableSerialConstructorChecks; private final Method superHasAccessibleConstructor; private final Method packageEquals; @@ -278,7 +288,7 @@ final class SerializationBuilder extends ConditionalConfigurationRegistry implem private final ProxyRegistry proxyRegistry; private List pendingConstructorRegistrations; - SerializationBuilder(SerializationDenyRegistry serializationDenyRegistry, FeatureImpl.DuringSetupAccessImpl access, ConfigurationTypeResolver typeResolver, ProxyRegistry proxyRegistry) { + SerializationBuilder(SerializationDenyRegistry serializationDenyRegistry, FeatureImpl.AfterRegistrationAccessImpl access, ConfigurationTypeResolver typeResolver, ProxyRegistry proxyRegistry) { this.access = access; Class classDataSlotClazz = access.findClassByName("java.io.ObjectStreamClass$ClassDataSlot"); this.descField = ReflectionUtil.lookupField(classDataSlotClazz, "desc");