Skip to content

Commit 860e870

Browse files
committed
Introduce serializable flag to reflection metadata
1 parent b233b1a commit 860e870

File tree

17 files changed

+106
-40
lines changed

17 files changed

+106
-40
lines changed

docs/reference-manual/native-image/ReachabilityMetadata.md

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ Computing metadata in code can be achieved in two ways:
123123
## Specifying Metadata with JSON
124124
125125
All metadata specified in the _reachability-metadata.json_ file that is located in any of the classpath entries at _META-INF/native-image/\<group.Id>\/\<artifactId>\/_.
126-
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).
126+
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).
127127
128128
A sample _reachability-metadata.json_ file can be found [in the sample section](#sample-reachability-metadata).
129129
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
132132
"reflection":[],
133133
"resources":[],
134134
"bundles":[],
135-
"serialization":[],
136135
"jni":[]
137136
}
138137
```
@@ -640,14 +639,15 @@ To create a custom constructor for serialization use:
640639
Proxy classes can only be registered for serialization via the JSON files.
641640

642641
### Serialization Metadata in JSON
643-
Serialization metadata is specified in the `serialization` section of _reachability-metadata.json_.
642+
Serialization metadata is specified in the `reflection` section of _reachability-metadata.json_.
644643

645644
To specify a regular `serialized.Type` use
646645
```json
647646
{
648-
"serialization": [
647+
"reflection": [
649648
{
650-
"type": "serialized.Type"
649+
"type": "serialized.Type",
650+
"serializable": true
651651
}
652652
]
653653
}
@@ -656,10 +656,11 @@ To specify a regular `serialized.Type` use
656656
To specify a proxy class for serialization, use the following entry:
657657
```json
658658
{
659-
"serialization": [
659+
"reflection": [
660660
{
661661
"type": {
662-
"proxy": ["FullyQualifiedInterface1", "...", "FullyQualifiedInterfaceN"]
662+
"proxy": ["FullyQualifiedInterface1", "...", "FullyQualifiedInterfaceN"],
663+
"serializable": true
663664
}
664665
}
665666
]
@@ -700,7 +701,8 @@ See below is a sample reachability metadata configuration that you can use in _r
700701
"allPublicFields": true,
701702
"allDeclaredMethods": true,
702703
"allPublicMethods": true,
703-
"unsafeAllocated": true
704+
"unsafeAllocated": true,
705+
"serializable": true
704706
}
705707
],
706708
"jni": [
@@ -736,11 +738,6 @@ See below is a sample reachability metadata configuration that you can use in _r
736738
"name": "fully.qualified.bundle.name",
737739
"locales": ["en", "de", "other_optional_locales"]
738740
}
739-
],
740-
"serialization": [
741-
{
742-
"type": "serialized.Type"
743-
}
744741
]
745742
}
746743
```

docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.0.0.json renamed to docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "https://json-schema.org/draft/2019-09/schema",
3-
"$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.0.0.json",
3+
"$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json",
44
"title": "JSON schema for the reachability metadata used by GraalVM Native Image",
55
"type": "object",
66
"default": {},
@@ -202,6 +202,11 @@
202202
"title": "Allow objects of this class to be instantiated with a call to jdk.internal.misc.Unsafe#allocateInstance or JNI's AllocObject",
203203
"type": "boolean",
204204
"default": false
205+
},
206+
"serializable": {
207+
"title": "Allow objects of this class to be serialized and deserialized",
208+
"type": "boolean",
209+
"default": false
205210
}
206211
},
207212
"additionalProperties": false

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ At runtime, premain runtime options are set along with main class' arguments in
2323
* (GR-59326) Ensure builder ForkJoin commonPool parallelism always respects NativeImageOptions.NumberOfThreads.
2424
* (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`.
2525
* (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.
26+
* (GR-60237) Include serialization JSON reachability metadata as part of reflection metadata by introducing the `"serializable"` flag for reflection entries.
2627

2728
## GraalVM for JDK 23 (Internal Version 24.1.0)
2829
* (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.

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType
105105
private ConfigurationMemberAccessibility allPublicMethodsAccess = ConfigurationMemberAccessibility.NONE;
106106
private ConfigurationMemberAccessibility allDeclaredConstructorsAccess = ConfigurationMemberAccessibility.NONE;
107107
private ConfigurationMemberAccessibility allPublicConstructorsAccess = ConfigurationMemberAccessibility.NONE;
108+
private boolean serializable = false;
108109

109110
public ConfigurationType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean includeAllElements) {
110111
this.condition = condition;
@@ -294,14 +295,15 @@ private void setFlagsFromOther(ConfigurationType other, BiPredicate<Boolean, Boo
294295
allPublicMethodsAccess = accessCombiner.apply(allPublicMethodsAccess, other.allPublicMethodsAccess);
295296
allDeclaredConstructorsAccess = accessCombiner.apply(allDeclaredConstructorsAccess, other.allDeclaredConstructorsAccess);
296297
allPublicConstructorsAccess = accessCombiner.apply(allPublicConstructorsAccess, other.allPublicConstructorsAccess);
298+
serializable = flagPredicate.test(serializable, other.serializable);
297299
}
298300

299301
private boolean isEmpty() {
300302
return methods == null && fields == null && allFlagsFalse();
301303
}
302304

303305
private boolean allFlagsFalse() {
304-
return !(allDeclaredClasses || allRecordComponents || allPermittedSubclasses || allNestMembers || allSigners || allPublicClasses ||
306+
return !(allDeclaredClasses || allRecordComponents || allPermittedSubclasses || allNestMembers || allSigners || allPublicClasses || serializable ||
305307
allDeclaredFieldsAccess != ConfigurationMemberAccessibility.NONE || allPublicFieldsAccess != ConfigurationMemberAccessibility.NONE ||
306308
allDeclaredMethodsAccess != ConfigurationMemberAccessibility.NONE || allPublicMethodsAccess != ConfigurationMemberAccessibility.NONE ||
307309
allDeclaredConstructorsAccess != ConfigurationMemberAccessibility.NONE || allPublicConstructorsAccess != ConfigurationMemberAccessibility.NONE);
@@ -465,6 +467,10 @@ public synchronized void setAllPublicConstructors(ConfigurationMemberAccessibili
465467
}
466468
}
467469

470+
public synchronized void setSerializable() {
471+
serializable = true;
472+
}
473+
468474
@Override
469475
public synchronized void printJson(JsonWriter writer) throws IOException {
470476
writer.appendObjectStart();
@@ -479,6 +485,7 @@ public synchronized void printJson(JsonWriter writer) throws IOException {
479485
printJsonBooleanIfSet(writer, allDeclaredConstructorsAccess == ConfigurationMemberAccessibility.ACCESSED, "allDeclaredConstructors");
480486
printJsonBooleanIfSet(writer, allPublicConstructorsAccess == ConfigurationMemberAccessibility.ACCESSED, "allPublicConstructors");
481487
printJsonBooleanIfSet(writer, unsafeAllocated, "unsafeAllocated");
488+
printJsonBooleanIfSet(writer, serializable, "serializable");
482489

483490
if (fields != null) {
484491
writer.appendSeparator().quote("fields").appendFieldSeparator();

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ParserConfigurationAdapter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ public void registerDeclaredConstructors(UnresolvedConfigurationCondition condit
173173
type.setAllDeclaredConstructors(queriedOnly ? ConfigurationMemberAccessibility.QUERIED : ConfigurationMemberAccessibility.ACCESSED);
174174
}
175175

176+
@Override
177+
public void registerAsSerializable(UnresolvedConfigurationCondition condition, ConfigurationType type) {
178+
VMError.guarantee(condition.isAlwaysTrue() || condition.equals(type.getCondition()), "condition is already a part of the type");
179+
type.setSerializable();
180+
}
181+
176182
@Override
177183
public String getTypeName(ConfigurationType type) {
178184
return type.getTypeDescriptor().toString();

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434

3535
import com.oracle.svm.configure.config.ConfigurationSet;
3636
import com.oracle.svm.configure.config.SerializationConfiguration;
37+
import com.oracle.svm.configure.config.TypeConfiguration;
38+
import com.oracle.svm.core.configure.NamedConfigurationTypeDescriptor;
3739

3840
import jdk.graal.compiler.java.LambdaUtils;
3941

@@ -55,6 +57,7 @@ void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configurationSe
5557
String function = (String) entry.get("function");
5658
List<?> args = (List<?>) entry.get("args");
5759
SerializationConfiguration serializationConfiguration = configurationSet.getSerializationConfiguration();
60+
TypeConfiguration reflectionConfiguration = configurationSet.getReflectionConfiguration();
5861

5962
if ("ObjectStreamClass.<init>".equals(function) || "ObjectInputStream.readClassDescriptor".equals(function)) {
6063
expectSize(args, 1);
@@ -68,7 +71,7 @@ void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configurationSe
6871
if (className.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) {
6972
serializationConfiguration.registerLambdaCapturingClass(condition, className);
7073
} else {
71-
serializationConfiguration.register(condition, className);
74+
reflectionConfiguration.getOrCreateType(condition, new NamedConfigurationTypeDescriptor(className)).setSerializable();
7275
}
7376
} else if ("SerializedLambda.readResolve".equals(function)) {
7477
expectSize(args, 1);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public static final class Options {
122122
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter());
123123

124124
@Option(help = "Resources describing reachability metadata needed for the program " +
125-
"https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.0.0.json", type = OptionType.User)//
125+
"https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json", type = OptionType.User)//
126126
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> ReachabilityMetadataResources = new HostedOptionKey<>(
127127
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter());
128128

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacyReflectionConfigurationParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ final class LegacyReflectionConfigurationParser<C, T> extends ReflectionConfigur
4141
"allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields",
4242
"allDeclaredClasses", "allRecordComponents", "allPermittedSubclasses", "allNestMembers", "allSigners",
4343
"allPublicClasses", "methods", "queriedMethods", "fields", CONDITIONAL_KEY,
44-
"queryAllDeclaredConstructors", "queryAllPublicConstructors", "queryAllDeclaredMethods", "queryAllPublicMethods", "unsafeAllocated");
44+
"queryAllDeclaredConstructors", "queryAllPublicConstructors", "queryAllDeclaredMethods", "queryAllPublicMethods", "unsafeAllocated", "serializable");
4545

4646
private final boolean treatAllNameEntriesAsType;
4747

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ public interface ReflectionConfigurationParserDelegate<C, T> {
7070

7171
void registerUnsafeAllocated(C condition, T clazz);
7272

73+
void registerAsSerializable(C condition, T clazz);
74+
7375
String getTypeName(T type);
7476

7577
String getSimpleName(T type);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionMetadataParser.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
class ReflectionMetadataParser<C, T> extends ReflectionConfigurationParser<C, T> {
3939
private static final List<String> OPTIONAL_REFLECT_METADATA_ATTRS = Arrays.asList(CONDITIONAL_KEY,
4040
"allDeclaredConstructors", "allPublicConstructors", "allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields",
41-
"methods", "fields", "unsafeAllocated");
41+
"methods", "fields", "unsafeAllocated", "serializable");
4242

4343
private final String combinedFileKey;
4444

@@ -107,6 +107,8 @@ protected void parseClass(EconomicMap<String, Object> data) {
107107
registerIfNotDefault(data, false, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, clazz));
108108
registerIfNotDefault(data, false, clazz, "unsafeAllocated", () -> delegate.registerUnsafeAllocated(condition, clazz));
109109

110+
registerIfNotDefault(data, false, clazz, "serializable", () -> delegate.registerAsSerializable(condition, clazz));
111+
110112
MapCursor<String, Object> cursor = data.getEntries();
111113
while (cursor.advance()) {
112114
String name = cursor.getKey();

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,15 @@
5757
import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry;
5858

5959
import jdk.graal.compiler.util.json.JsonParserException;
60+
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
6061

6162
public final class ConfigurationParserUtils {
6263

6364
public static ReflectionConfigurationParser<ConfigurationCondition, Class<?>> create(String combinedFileKey, boolean strictMetadata,
64-
ConfigurationConditionResolver<ConfigurationCondition> conditionResolver, ReflectionRegistry registry, ProxyRegistry proxyRegistry, ImageClassLoader imageClassLoader) {
65+
ConfigurationConditionResolver<ConfigurationCondition> conditionResolver, ReflectionRegistry registry, ProxyRegistry proxyRegistry,
66+
RuntimeSerializationSupport<ConfigurationCondition> serializationSupport, ImageClassLoader imageClassLoader) {
6567
return ReflectionConfigurationParser.create(combinedFileKey, strictMetadata, conditionResolver,
66-
RegistryAdapter.create(registry, proxyRegistry, imageClassLoader),
68+
RegistryAdapter.create(registry, proxyRegistry, serializationSupport, imageClassLoader),
6769
ConfigurationFiles.Options.StrictConfiguration.getValue(),
6870
ConfigurationFiles.Options.WarnAboutMissingReflectionOrJNIMetadataElements.getValue(), TreatAllNameEntriesAsType.getValue());
6971
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,28 @@
2727
import java.lang.reflect.Proxy;
2828
import java.util.Arrays;
2929

30-
import com.oracle.svm.hosted.reflect.ReflectionDataBuilder;
3130
import org.graalvm.nativeimage.impl.ConfigurationCondition;
3231
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
32+
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
3333

3434
import com.oracle.svm.core.TypeResult;
3535
import com.oracle.svm.core.configure.ConfigurationTypeDescriptor;
3636
import com.oracle.svm.core.configure.NamedConfigurationTypeDescriptor;
3737
import com.oracle.svm.hosted.ImageClassLoader;
38+
import com.oracle.svm.hosted.reflect.ReflectionDataBuilder;
3839
import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry;
3940

4041
public class ReflectionRegistryAdapter extends RegistryAdapter {
4142
private final RuntimeReflectionSupport reflectionSupport;
4243
private final ProxyRegistry proxyRegistry;
44+
private final RuntimeSerializationSupport<ConfigurationCondition> serializationSupport;
4345

44-
ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, ProxyRegistry proxyRegistry, ImageClassLoader classLoader) {
46+
ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, ProxyRegistry proxyRegistry, RuntimeSerializationSupport<ConfigurationCondition> serializationSupport,
47+
ImageClassLoader classLoader) {
4548
super(reflectionSupport, classLoader);
4649
this.reflectionSupport = reflectionSupport;
4750
this.proxyRegistry = proxyRegistry;
51+
this.serializationSupport = serializationSupport;
4852
}
4953

5054
@Override
@@ -126,4 +130,9 @@ public void registerPublicConstructors(ConfigurationCondition condition, boolean
126130
public void registerDeclaredConstructors(ConfigurationCondition condition, boolean queriedOnly, Class<?> type) {
127131
reflectionSupport.registerAllDeclaredConstructorsQuery(condition, queriedOnly, type);
128132
}
133+
134+
@Override
135+
public void registerAsSerializable(ConfigurationCondition condition, Class<?> clazz) {
136+
serializationSupport.register(condition, clazz);
137+
}
129138
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.graalvm.nativeimage.impl.ConfigurationCondition;
3737
import org.graalvm.nativeimage.impl.ReflectionRegistry;
3838
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
39+
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
3940

4041
import com.oracle.svm.core.TypeResult;
4142
import com.oracle.svm.core.configure.ConfigurationTypeDescriptor;
@@ -52,9 +53,10 @@ public class RegistryAdapter implements ReflectionConfigurationParserDelegate<Co
5253
private final ReflectionRegistry registry;
5354
private final ImageClassLoader classLoader;
5455

55-
public static RegistryAdapter create(ReflectionRegistry registry, ProxyRegistry proxyRegistry, ImageClassLoader classLoader) {
56+
public static RegistryAdapter create(ReflectionRegistry registry, ProxyRegistry proxyRegistry, RuntimeSerializationSupport<ConfigurationCondition> serializationSupport,
57+
ImageClassLoader classLoader) {
5658
if (registry instanceof RuntimeReflectionSupport) {
57-
return new ReflectionRegistryAdapter((RuntimeReflectionSupport) registry, proxyRegistry, classLoader);
59+
return new ReflectionRegistryAdapter((RuntimeReflectionSupport) registry, proxyRegistry, serializationSupport, classLoader);
5860
} else {
5961
return new RegistryAdapter(registry, classLoader);
6062
}
@@ -289,6 +291,11 @@ private void registerExecutable(ConfigurationCondition condition, boolean querie
289291
registry.register(condition, queriedOnly, executable);
290292
}
291293

294+
@Override
295+
public void registerAsSerializable(ConfigurationCondition condition, Class<?> clazz) {
296+
/* Serializable has no effect on JNI registrations */
297+
}
298+
292299
@Override
293300
public String getTypeName(Class<?> type) {
294301
return type.getTypeName();

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,10 @@ public void afterRegistration(AfterRegistrationAccess arg) {
207207

208208
ConfigurationConditionResolver<ConfigurationCondition> conditionResolver = new NativeImageConditionResolver(access.getImageClassLoader(),
209209
ClassInitializationSupport.singleton());
210-
ReflectionConfigurationParser<ConfigurationCondition, Class<?>> parser = ConfigurationParserUtils.create(JNI_KEY, true, conditionResolver, runtimeSupport, null, access.getImageClassLoader());
210+
ReflectionConfigurationParser<ConfigurationCondition, Class<?>> parser = ConfigurationParserUtils.create(JNI_KEY, true, conditionResolver, runtimeSupport, null, null,
211+
access.getImageClassLoader());
211212
loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurationsFromCombinedFile(parser, access.getImageClassLoader(), "JNI");
212-
ReflectionConfigurationParser<ConfigurationCondition, Class<?>> legacyParser = ConfigurationParserUtils.create(null, false, conditionResolver, runtimeSupport, null,
213+
ReflectionConfigurationParser<ConfigurationCondition, Class<?>> legacyParser = ConfigurationParserUtils.create(null, false, conditionResolver, runtimeSupport, null, null,
213214
access.getImageClassLoader());
214215
loadedConfigurations += ConfigurationParserUtils.parseAndRegisterConfigurations(legacyParser, access.getImageClassLoader(), "JNI",
215216
ConfigurationFiles.Options.JNIConfigurationFiles, ConfigurationFiles.Options.JNIConfigurationResources, ConfigurationFile.JNI.getFileName());

0 commit comments

Comments
 (0)