Skip to content

Commit 7a26b47

Browse files
committed
Introduce serializable flag to reflection metadata
1 parent f6d7b16 commit 7a26b47

File tree

16 files changed

+94
-37
lines changed

16 files changed

+94
-37
lines changed

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

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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
```
@@ -637,14 +636,15 @@ To create a custom constructor for serialization use:
637636
Proxy classes can only be registered for serialization via the JSON files.
638637

639638
### Serialization Metadata in JSON
640-
Serialization metadata is specified in the `serialization` section of _reachability-metadata.json_.
639+
Serialization metadata is specified in the `reflection` section of _reachability-metadata.json_.
641640

642641
To specify a regular `serialized.Type` use
643642
```json
644643
{
645-
"serialization": [
644+
"reflection": [
646645
{
647-
"type": "serialized.Type"
646+
"type": "serialized.Type",
647+
"serializable": true
648648
}
649649
]
650650
}
@@ -653,10 +653,11 @@ To specify a regular `serialized.Type` use
653653
To specify a proxy class for serialization, use the following entry:
654654
```json
655655
{
656-
"serialization": [
656+
"reflection": [
657657
{
658658
"type": {
659-
"proxy": ["FullyQualifiedInterface1", "...", "FullyQualifiedInterfaceN"]
659+
"proxy": ["FullyQualifiedInterface1", "...", "FullyQualifiedInterfaceN"],
660+
"serializable": true
660661
}
661662
}
662663
]
@@ -695,7 +696,8 @@ See below is a sample reachability metadata configuration that you can use in _r
695696
"allDeclaredFields": true,
696697
"allPublicFields": true,
697698
"allDeclaredMethods": true,
698-
"allPublicMethods": true
699+
"allPublicMethods": true,
700+
"serializable": true
699701
}
700702
],
701703
"jni": [
@@ -731,11 +733,6 @@ See below is a sample reachability metadata configuration that you can use in _r
731733
"name": "fully.qualified.bundle.name",
732734
"locales": ["en", "de", "other_optional_locales"]
733735
}
734-
],
735-
"serialization": [
736-
{
737-
"type": "serialized.Type"
738-
}
739736
]
740737
}
741738
```

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
@@ -20,6 +20,7 @@ At runtime, premain runtime options are set along with main class' arguments in
2020
* (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`.
2121
* (GR-60234) Remove `"customTargetConstructorClass"` field from the serialization JSON metadata. All possible constructors are now registered by default when registering a type for serialization.
2222
* (GR-60235) Remove `"unsafeAllocated"` field from the reflection JSON metadata. All reflectively-accessible types are now automatically registered for unsafe allocation.
23+
* (GR-60237) Include serialization JSON reachability metadata as part of reflection metadata by introducing the `"serializable"` flag for reflection entries.
2324

2425
## GraalVM for JDK 23 (Internal Version 24.1.0)
2526
* (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
@@ -104,6 +104,7 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType
104104
private ConfigurationMemberAccessibility allPublicMethodsAccess = ConfigurationMemberAccessibility.NONE;
105105
private ConfigurationMemberAccessibility allDeclaredConstructorsAccess = ConfigurationMemberAccessibility.NONE;
106106
private ConfigurationMemberAccessibility allPublicConstructorsAccess = ConfigurationMemberAccessibility.NONE;
107+
private boolean serializable = false;
107108

108109
public ConfigurationType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean includeAllElements) {
109110
this.condition = condition;
@@ -284,14 +285,15 @@ private void setFlagsFromOther(ConfigurationType other, BiPredicate<Boolean, Boo
284285
allPublicMethodsAccess = accessCombiner.apply(allPublicMethodsAccess, other.allPublicMethodsAccess);
285286
allDeclaredConstructorsAccess = accessCombiner.apply(allDeclaredConstructorsAccess, other.allDeclaredConstructorsAccess);
286287
allPublicConstructorsAccess = accessCombiner.apply(allPublicConstructorsAccess, other.allPublicConstructorsAccess);
288+
serializable = flagPredicate.test(serializable, other.serializable);
287289
}
288290

289291
private boolean isEmpty() {
290292
return methods == null && fields == null && allFlagsFalse();
291293
}
292294

293295
private boolean allFlagsFalse() {
294-
return !(allDeclaredClasses || allRecordComponents || allPermittedSubclasses || allNestMembers || allSigners || allPublicClasses ||
296+
return !(allDeclaredClasses || allRecordComponents || allPermittedSubclasses || allNestMembers || allSigners || allPublicClasses || serializable ||
295297
allDeclaredFieldsAccess != ConfigurationMemberAccessibility.NONE || allPublicFieldsAccess != ConfigurationMemberAccessibility.NONE ||
296298
allDeclaredMethodsAccess != ConfigurationMemberAccessibility.NONE || allPublicMethodsAccess != ConfigurationMemberAccessibility.NONE ||
297299
allDeclaredConstructorsAccess != ConfigurationMemberAccessibility.NONE || allPublicConstructorsAccess != ConfigurationMemberAccessibility.NONE);
@@ -451,6 +453,10 @@ public synchronized void setAllPublicConstructors(ConfigurationMemberAccessibili
451453
}
452454
}
453455

456+
public synchronized void setSerializable() {
457+
serializable = true;
458+
}
459+
454460
@Override
455461
public synchronized void printJson(JsonWriter writer) throws IOException {
456462
writer.appendObjectStart();
@@ -464,6 +470,7 @@ public synchronized void printJson(JsonWriter writer) throws IOException {
464470
printJsonBooleanIfSet(writer, allPublicMethodsAccess == ConfigurationMemberAccessibility.ACCESSED, "allPublicMethods");
465471
printJsonBooleanIfSet(writer, allDeclaredConstructorsAccess == ConfigurationMemberAccessibility.ACCESSED, "allDeclaredConstructors");
466472
printJsonBooleanIfSet(writer, allPublicConstructorsAccess == ConfigurationMemberAccessibility.ACCESSED, "allPublicConstructors");
473+
printJsonBooleanIfSet(writer, serializable, "serializable");
467474

468475
if (fields != null) {
469476
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
@@ -167,6 +167,12 @@ public void registerDeclaredConstructors(UnresolvedConfigurationCondition condit
167167
type.setAllDeclaredConstructors(queriedOnly ? ConfigurationMemberAccessibility.QUERIED : ConfigurationMemberAccessibility.ACCESSED);
168168
}
169169

170+
@Override
171+
public void registerAsSerializable(UnresolvedConfigurationCondition condition, ConfigurationType type) {
172+
VMError.guarantee(condition.isAlwaysTrue() || condition.equals(type.getCondition()), "condition is already a part of the type");
173+
type.setSerializable();
174+
}
175+
170176
@Override
171177
public String getTypeName(ConfigurationType type) {
172178
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/LegacyReflectionConfigurationParser.java

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

4747
private final boolean treatAllNameEntriesAsType;
4848

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
@@ -68,6 +68,8 @@ public interface ReflectionConfigurationParserDelegate<C, T> {
6868

6969
boolean registerAllConstructors(C condition, boolean queriedOnly, T type);
7070

71+
void registerAsSerializable(C condition, T clazz);
72+
7173
String getTypeName(T type);
7274

7375
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
@@ -39,7 +39,7 @@
3939
class ReflectionMetadataParser<C, T> extends ReflectionConfigurationParser<C, T> {
4040
private static final List<String> OPTIONAL_REFLECT_METADATA_ATTRS = Arrays.asList(CONDITIONAL_KEY,
4141
"allDeclaredConstructors", "allPublicConstructors", "allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields",
42-
"methods", "fields", "unsafeAllocated");
42+
"methods", "fields", "unsafeAllocated", "serializable");
4343

4444
private final String combinedFileKey;
4545

@@ -113,6 +113,8 @@ protected void parseClass(EconomicMap<String, Object> data) {
113113
registerIfNotDefault(data, false, clazz, "allDeclaredFields", () -> delegate.registerDeclaredFields(condition, false, clazz));
114114
registerIfNotDefault(data, false, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, clazz));
115115

116+
registerIfNotDefault(data, false, clazz, "serializable", () -> delegate.registerAsSerializable(condition, clazz));
117+
116118
MapCursor<String, Object> cursor = data.getEntries();
117119
while (cursor.advance()) {
118120
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
}

0 commit comments

Comments
 (0)