diff --git a/docs/reference-manual/native-image/ReachabilityMetadata.md b/docs/reference-manual/native-image/ReachabilityMetadata.md index 31ac83047959..1f7d435432d4 100644 --- a/docs/reference-manual/native-image/ReachabilityMetadata.md +++ b/docs/reference-manual/native-image/ReachabilityMetadata.md @@ -72,7 +72,7 @@ The JSON file consists of entries that tell Native Image the elements to include For example, Java reflection metadata is specified in `reflect-config.json`, and a sample entry looks like: ```json { - "name": "Foo" + "type": "Foo" } ``` @@ -147,7 +147,7 @@ Integer.class.getMethod("parseInt", params2); ### Specifying Reflection Metadata in JSON Reflection metadata should be specified in a _reflect-config.json_ file and conform to the JSON schema defined in -[reflect-config-schema-v1.0.0.json](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reflect-config-schema-v1.0.0.json). +[reflect-config-schema-v1.1.0.json](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reflect-config-schema-v1.1.0.json). The schema also includes further details and explanations how this configuration works. Here is the example of the reflect-config.json: ```json [ @@ -155,7 +155,7 @@ The schema also includes further details and explanations how this configuration "condition": { "typeReachable": "" }, - "name": "", + "type": "", "methods": [ {"name": "", "parameterTypes": [""]} ], @@ -199,7 +199,7 @@ looks up the `java.lang.String` class, which can then be used, for example, to i The generated metadata entry for the above call would look like: ```json { - "name": "java.lang.String" + "type": "java.lang.String" } ``` @@ -209,7 +209,7 @@ It is not possible to specify JNI metadata in code. ### JNI Metadata in JSON JNI metadata should be specified in a _jni-config.json_ file and conform to the JSON schema defined in -[jni-config-schema-v1.0.0.json](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/jni-config-schema-v1.0.0.json). +[jni-config-schema-v1.1.0.json](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/jni-config-schema-v1.1.0.json). The schema also includes further details and explanations how this configuration works. The example of jni-config.json is the same as the example of reflect-config.json described above. @@ -377,7 +377,7 @@ Proxy classes can only be registered for serialization via the JSON files. ### Serialization Metadata in JSON Serialization metadata should be specified in a _serialization-config.json_ file and conform to the JSON schema defined in -[serialization-config-schema-v1.0.0.json](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/serialization-config-schema-v1.0.0.json). +[serialization-config-schema-v1.1.0.json](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/serialization-config-schema-v1.1.0.json). The schema also includes further details and explanations how this configuration works. Here is the example of the serialization-config.json: ```json { @@ -386,7 +386,7 @@ The schema also includes further details and explanations how this configuration "condition": { "typeReachable": "" }, - "name": "", + "type": "", "customTargetConstructorClass": "" } ], diff --git a/docs/reference-manual/native-image/assets/config-condition-schema-v1.0.0.json b/docs/reference-manual/native-image/assets/config-condition-schema-v1.0.0.json new file mode 100644 index 000000000000..6f986ac9629f --- /dev/null +++ b/docs/reference-manual/native-image/assets/config-condition-schema-v1.0.0.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/config-condition-schema-v1.0.0.json", + "title": "JSON schema for the conditions used in GraalVM Native Image configuration files", + "properties": { + "typeReachable": { + "type": "string", + "title": "Fully qualified name of a class that must be reachable in order to register the type for reflection" + } + }, + "required": [ + "typeReachable" + ], + "additionalProperties": false, + "type": "object" +} \ No newline at end of file diff --git a/docs/reference-manual/native-image/assets/config-type-schema-v1.0.0.json b/docs/reference-manual/native-image/assets/config-type-schema-v1.0.0.json new file mode 100644 index 000000000000..16a50dc888a4 --- /dev/null +++ b/docs/reference-manual/native-image/assets/config-type-schema-v1.0.0.json @@ -0,0 +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/config-type-schema-v1.0.0.json", + "type": "string", + "title": "JSON schema for the type descriptors GraalVM Native Image configuration files use" +} \ No newline at end of file diff --git a/docs/reference-manual/native-image/assets/jni-config-schema-v1.1.0.json b/docs/reference-manual/native-image/assets/jni-config-schema-v1.1.0.json new file mode 100644 index 000000000000..902ba0049252 --- /dev/null +++ b/docs/reference-manual/native-image/assets/jni-config-schema-v1.1.0.json @@ -0,0 +1,194 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/jni-config-schema-v1.1.0.json", + "default": [], + "items": { + "properties": { + "condition": { + "$ref": "config-condition-schema-v1.0.0.json", + "title": "Condition under which the class should be registered for access through JNI" + }, + "type": { + "$ref": "config-type-schema-v1.0.0.json", + "title": "Type descriptor of the class that should be registered for access through JNI" + }, + "name": { + "deprecated": true, + "type": "string", + "title": "Name of the class that should be registered for access through JNI" + }, + "methods": { + "default": [], + "items": { + "properties": { + "name": { + "type": "string", + "title": "Method name that should be registered for this class" + }, + "parameterTypes": { + "default": [], + "items": { + "type": "string", + "title": "List of the method's parameter types" + }, + "type": "array" + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "type": "object", + "title": "List of methods from this class that are registered for access through JNI" + }, + "type": "array", + "title": "List of methods that should be registered for the class declared in " + }, + "queriedMethods": { + "deprecated": true, + "default": [], + "items": { + "properties": { + "name": { + "type": "string", + "title": "Method name that are queried for this class" + }, + "parameterTypes": { + "default": [], + "items": { + "type": "string", + "title": "List of types for the parameters of the this method" + }, + "type": "array", + "title": "List of methods to register for this class that are only looked up but not invoked." + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "type": "object" + }, + "type": "array", + "title": "List of methods that are queried for the class declared in " + }, + "fields": { + "default": [], + "items": { + "properties": { + "name": { + "type": "string", + "title": "Name of the field that should be registered for access through JNI" + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "type": "object" + }, + "type": "array", + "title": "List of fields that should be registered for the class declared in " + }, + "allDeclaredClasses": { + "deprecated": true, + "default": false, + "type": "boolean", + "title": "Register classes which would be returned by the java.lang.Class#getDeclaredClasses call" + }, + "allDeclaredMethods": { + "default": false, + "type": "boolean", + "title": "Register methods which would be returned by the java.lang.Class#getDeclaredMethods call" + }, + "allDeclaredFields": { + "default": false, + "type": "boolean", + "title": "Register fields which would be returned by the java.lang.Class#getDeclaredFields call" + }, + "allDeclaredConstructors": { + "default": false, + "type": "boolean", + "title": "Register constructors which would be returned by the java.lang.Class#getDeclaredConstructors call" + }, + "allPublicClasses": { + "deprecated": true, + "default": false, + "type": "boolean", + "title": "Register all public classes which would be returned by the java.lang.Class#getClasses call" + }, + "allPublicMethods": { + "default": false, + "type": "boolean", + "title": "Register all public methods which would be returned by the java.lang.Class#getMethods call" + }, + "allPublicFields": { + "default": false, + "type": "boolean", + "title": "Register all public fields which would be returned by the java.lang.Class#getFields call" + }, + "allPublicConstructors": { + "default": false, + "type": "boolean", + "title": "Register all public constructors which would be returned by the java.lang.Class#getConstructors call" + }, + "allRecordComponents": { + "deprecated": true, + "default": false, + "type": "boolean", + "title": "Register record components which would be returned by the java.lang.Class#getRecordComponents call" + }, + "allPermittedSubclasses": { + "deprecated": true, + "default": false, + "type": "boolean", + "title": "Register permitted subclasses which would be returned by the java.lang.Class#getPermittedSubclasses call" + }, + "allNestMembers": { + "deprecated": true, + "default": false, + "type": "boolean", + "title": "Register nest members which would be returned by the java.lang.Class#getNestMembers call" + }, + "allSigners": { + "deprecated": true, + "default": false, + "type": "boolean", + "title": "Register signers which would be returned by the java.lang.Class#getSigners call" + }, + "queryAllDeclaredMethods": { + "deprecated": true, + "default": false, + "type": "boolean", + "title": "Register methods which would be returned by the java.lang.Class#getDeclaredMethods call but only for lookup" + }, + "queryAllDeclaredConstructors": { + "deprecated": true, + "default": false, + "type": "boolean", + "title": "Register constructors which would be returned by the java.lang.Class#getDeclaredConstructors call but only for lookup" + }, + "queryAllPublicMethods": { + "deprecated": true, + "default": false, + "type": "boolean", + "title": "Register all public methods which would be returned by the java.lang.Class#getMethods call but only for lookup" + }, + "queryAllPublicConstructors": { + "deprecated": true, + "default": false, + "type": "boolean", + "title": "Register all public constructors which would be returned by the java.lang.Class#getConstructors call but only for lookup" + }, + "unsafeAllocated": { + "default": false, + "type": "boolean", + "title": "Allow objects of this class to be instantiated with a call to jdk.internal.misc.Unsafe#allocateInstance" + } + }, + "additionalProperties": false, + "type": "object" + }, + "type": "array", + "title": "JSON schema for the JNI configuration that GraalVM Native Image uses" +} \ No newline at end of file diff --git a/docs/reference-manual/native-image/assets/reflect-config-schema-v1.1.0.json b/docs/reference-manual/native-image/assets/reflect-config-schema-v1.1.0.json new file mode 100644 index 000000000000..a0ec24e62285 --- /dev/null +++ b/docs/reference-manual/native-image/assets/reflect-config-schema-v1.1.0.json @@ -0,0 +1,183 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reflect-config-schema-v1.1.0.json", + "default": [], + "items": { + "properties": { + "condition": { + "$ref": "config-condition-schema-v1.0.0.json", + "title": "Condition under which the class should be registered for reflection" + }, + "type": { + "$ref": "config-type-schema-v1.0.0.json", + "title": "Type descriptor of the class that should be registered for reflection" + }, + "name": { + "deprecated": true, + "type": "string", + "title": "Name of the class that should be registered for reflection" + }, + "methods": { + "default": [], + "items": { + "properties": { + "name": { + "type": "string", + "title": "Method name that should be registered for this class" + }, + "parameterTypes": { + "default": [], + "items": { + "type": "string", + "title": "List of the method's parameter types" + }, + "type": "array" + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "type": "object", + "title": "List of methods from this class that are registered for reflection" + }, + "type": "array", + "title": "List of methods that should be registered for the type declared in " + }, + "queriedMethods": { + "default": [], + "items": { + "properties": { + "name": { + "type": "string", + "title": "Method name that are queried for this class" + }, + "parameterTypes": { + "default": [], + "items": { + "type": "string", + "title": "List of the method's parameter types" + }, + "type": "array", + "title": "List of methods to register for this class that are only looked up but not invoked." + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "type": "object" + }, + "type": "array", + "title": "List of methods that are queried for the type declared in " + }, + "fields": { + "default": [], + "items": { + "properties": { + "name": { + "type": "string", + "title": "Name of the field that should be registered for reflection" + } + }, + "required": [ + "name" + ], + "additionalProperties": false, + "type": "object" + }, + "type": "array", + "title": "List of class fields that can be looked up, read, or modified for the type declared in " + }, + "allDeclaredClasses": { + "default": false, + "type": "boolean", + "title": "Register classes which would be returned by the java.lang.Class#getDeclaredClasses call" + }, + "allDeclaredMethods": { + "default": false, + "type": "boolean", + "title": "Register methods which would be returned by the java.lang.Class#getDeclaredMethods call" + }, + "allDeclaredFields": { + "default": false, + "type": "boolean", + "title": "Register fields which would be returned by the java.lang.Class#getDeclaredFields call" + }, + "allDeclaredConstructors": { + "default": false, + "type": "boolean", + "title": "Register constructors which would be returned by the java.lang.Class#getDeclaredConstructors call" + }, + "allPublicClasses": { + "default": false, + "type": "boolean", + "title": "Register all public classes which would be returned by the java.lang.Class#getClasses call" + }, + "allPublicMethods": { + "default": false, + "type": "boolean", + "title": "Register all public methods which would be returned by the java.lang.Class#getMethods call" + }, + "allPublicFields": { + "default": false, + "type": "boolean", + "title": "Register all public fields which would be returned by the java.lang.Class#getFields call" + }, + "allPublicConstructors": { + "default": false, + "type": "boolean", + "title": "Register all public constructors which would be returned by the java.lang.Class#getConstructors call" + }, + "allRecordComponents": { + "default": false, + "type": "boolean", + "title": "Register record components which would be returned by the java.lang.Class#getRecordComponents call" + }, + "allPermittedSubclasses": { + "default": false, + "type": "boolean", + "title": "Register permitted subclasses which would be returned by the java.lang.Class#getPermittedSubclasses call" + }, + "allNestMembers": { + "default": false, + "type": "boolean", + "title": "Register nest members which would be returned by the java.lang.Class#getNestMembers call" + }, + "allSigners": { + "default": false, + "type": "boolean", + "title": "Register signers which would be returned by the java.lang.Class#getSigners call" + }, + "queryAllDeclaredMethods": { + "default": false, + "type": "boolean", + "title": "Register methods which would be returned by the java.lang.Class#getDeclaredMethods call but only for lookup" + }, + "queryAllDeclaredConstructors": { + "default": false, + "type": "boolean", + "title": "Register constructors which would be returned by the java.lang.Class#getDeclaredConstructors call but only for lookup" + }, + "queryAllPublicMethods": { + "default": false, + "type": "boolean", + "title": "Register all public methods which would be returned by the java.lang.Class#getMethods call but only for lookup" + }, + "queryAllPublicConstructors": { + "default": false, + "type": "boolean", + "title": "Register all public constructors which would be returned by the java.lang.Class#getConstructors call but only for lookup" + }, + "unsafeAllocated": { + "default": false, + "type": "boolean", + "title": "Allow objects of this class to be instantiated with a call to jdk.internal.misc.Unsafe#allocateInstance" + } + }, + "additionalProperties": false, + "type": "object" + }, + "type": "array", + "title": "JSON schema for the reflection configuration that GraalVM Native Image uses" +} \ No newline at end of file diff --git a/docs/reference-manual/native-image/assets/serialization-config-schema-v1.1.0.json b/docs/reference-manual/native-image/assets/serialization-config-schema-v1.1.0.json new file mode 100644 index 000000000000..225ec59cffcf --- /dev/null +++ b/docs/reference-manual/native-image/assets/serialization-config-schema-v1.1.0.json @@ -0,0 +1,111 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/serialization-config-schema-v1.1.0.json", + "default": { + "types": [], + "lambdaCapturingTypes": [], + "proxies": [] + }, + "properties": { + "types": { + "default": [], + "items": { + "properties": { + "condition": { + "$ref": "config-condition-schema-v1.0.0.json", + "title": "Condition under which the class should be registered for serialization" + }, + "name": { + "deprecated": true, + "type": "string", + "title": "Fully qualified class name of the class that should be serialized" + }, + "type": { + "$ref": "config-type-schema-v1.0.0.json", + "title": "Type descriptor of the class that should be registered for serialization" + }, + "customTargetConstructorClass": { + "type": "string", + "title": "Fully qualified class name of the class whose constructor should be used to serialize the class" + } + }, + "additionalProperties": false, + "type": "object" + }, + "title": "Each entry in enables serializing and deserializing objects of the class given by ", + "type": "array" + }, + "lambdaCapturingTypes": { + "default": [], + "items": { + "properties": { + "condition": { + "properties": { + "typeReachable": { + "type": "string", + "title": "Fully qualified class name of the class that must be reachable in order to serialize lambda classes that are captured by the class referred in " + } + }, + "required": [ + "typeReachable" + ], + "additionalProperties": false, + "type": "object" + }, + "name": { + "type": "string", + "title": "Fully qualified class name of the class that captures lambda classes that should be either serialized or deserialized" + } + }, + "additionalProperties": false, + "type": "object" + }, + "title": "Each entry in enables lambda serialization. All lambdas declared in the methods of the class given by can be serialized and deserialized.", + "type": "array" + }, + "proxies": { + "default": [], + "items": { + "properties": { + "condition": { + "properties": { + "typeReachable": { + "type": "string", + "title": "Fully qualified class name of the class that must be reachable in order to serialize proxy classes that implements interfaces from " + } + }, + "required": [ + "typeReachable" + ], + "additionalProperties": false, + "type": "object" + }, + "interfaces": { + "items": { + "type": "string", + "title": "Fully qualified name of an interface that is implemented by the proxy that should be serialized" + }, + "type": "array", + "title": "List of interfaces that are implemented by the proxy class that should be serialized" + } + }, + "required": [ + "interfaces" + ], + "additionalProperties": false, + "type": "object", + "title": "Proxy that should be registered for serialization" + }, + "title": "Each entry in enables the proxy serialization by providing an interface list that a proxy implements.", + "type": "array" + } + }, + "required": [ + "types", + "lambdaCapturingTypes", + "proxies" + ], + "additionalProperties": false, + "type": "object", + "title": "JSON schema for the serialization-config that GraalVM Native Image uses" +} \ No newline at end of file diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index a13df8a0bb47..9309318267e6 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -10,6 +10,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-47109) Together with Red Hat, we added support for JFR event throttling and the event `ObjectAllocationSample`. * (GR-52030) Add a stable name for `Proxy` types in Native Image. The name `$Proxy[id]` is replaced by `$Proxy.s[hashCode]` where `hashCode` is computed using the names of the `Proxy` interfaces, the name of the class loader and the name of the module if it is not a dynamic module. * (GR-47712) Using the `--static` option without the `--libc=musl` option causes the build process to fail (and reports the appropriate error). Static linking is currently only supported with musl. +* (GR-50434) Introduce a `"type"` field in reflection and JNI configuration files to support more than simple named types. ## GraalVM for JDK 22 (Internal Version 24.0.0) * (GR-48304) Red Hat added support for the JFR event ThreadAllocationStatistics. diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/OmitPreviousConfigTests.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/OmitPreviousConfigTests.java index 361845eeaf99..0d5b6bb266b5 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/OmitPreviousConfigTests.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/OmitPreviousConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -260,11 +260,11 @@ Map getMethodsMap(Configura } void populateConfig() { - ConfigurationType oldType = new ConfigurationType(UnresolvedConfigurationCondition.alwaysTrue(), getTypeName()); + ConfigurationType oldType = new ConfigurationType(UnresolvedConfigurationCondition.alwaysTrue(), getTypeName(), true); setFlags(oldType); previousConfig.add(oldType); - ConfigurationType newType = new ConfigurationType(UnresolvedConfigurationCondition.alwaysTrue(), getTypeName()); + ConfigurationType newType = new ConfigurationType(UnresolvedConfigurationCondition.alwaysTrue(), getTypeName(), true); for (Map.Entry methodEntry : methodsThatMustExist.entrySet()) { newType.addMethod(methodEntry.getKey().getName(), methodEntry.getKey().getInternalSignature(), methodEntry.getValue()); } 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 ea2b7ec525d2..19460cbf111d 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ public static boolean isConstructorName(String name) { public static String toInternalParamsSignature(List types) { StringBuilder sb = new StringBuilder("("); for (ConfigurationType type : types) { - sb.append(MetaUtil.toInternalName(type.getQualifiedJavaName())); + sb.append(MetaUtil.toInternalName(((NamedConfigurationTypeDescriptor) type.getTypeDescriptor()).name())); } sb.append(')'); // we are missing the return type, so this is only a partial signature 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 bfce9e7d7f64..0cb7d83e9cc3 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,8 +60,7 @@ static ConfigurationType copyAndSubtract(ConfigurationType type, ConfigurationTy return copy; } - assert type.getCondition().equals(subtractType.getCondition()); - assert type.getQualifiedJavaName().equals(subtractType.getQualifiedJavaName()); + assert type.sameTypeAndCondition(subtractType); copy.removeAll(subtractType); return copy.isEmpty() ? null : copy; @@ -73,8 +72,7 @@ static ConfigurationType copyAndIntersect(ConfigurationType type, ConfigurationT return copy; } - assert type.getCondition().equals(toIntersect.getCondition()); - assert type.getQualifiedJavaName().equals(toIntersect.getQualifiedJavaName()); + assert type.sameTypeAndCondition(toIntersect); copy.intersectWith(toIntersect); return copy; } @@ -86,7 +84,7 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType } private final UnresolvedConfigurationCondition condition; - private final String qualifiedJavaName; + private final ConfigurationTypeDescriptor typeDescriptor; private Map fields; private Map methods; @@ -105,17 +103,23 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType private ConfigurationMemberAccessibility allDeclaredConstructorsAccess = ConfigurationMemberAccessibility.NONE; private ConfigurationMemberAccessibility allPublicConstructorsAccess = ConfigurationMemberAccessibility.NONE; - public ConfigurationType(UnresolvedConfigurationCondition condition, String qualifiedJavaName) { - assert qualifiedJavaName.indexOf('/') == -1 : "Requires qualified Java name, not internal representation"; - assert !qualifiedJavaName.startsWith("[") : "Requires Java source array syntax, for example java.lang.String[]"; + public ConfigurationType(UnresolvedConfigurationCondition condition, String qualifiedJavaName, boolean includeAllElements) { + this(condition, new NamedConfigurationTypeDescriptor(qualifiedJavaName), includeAllElements); + } + + public ConfigurationType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean includeAllElements) { this.condition = condition; - this.qualifiedJavaName = qualifiedJavaName; + this.typeDescriptor = typeDescriptor; + allDeclaredClasses = allPublicClasses = allDeclaredFields = allPublicFields = allRecordComponents = allPermittedSubclasses = allNestMembers = allSigners = includeAllElements; + allDeclaredMethodsAccess = allPublicMethodsAccess = allDeclaredConstructorsAccess = allPublicConstructorsAccess = includeAllElements + ? ConfigurationMemberAccessibility.QUERIED + : ConfigurationMemberAccessibility.NONE; } ConfigurationType(ConfigurationType other, UnresolvedConfigurationCondition condition) { // Our object is not yet published, so it is sufficient to take only the other object's lock synchronized (other) { - qualifiedJavaName = other.qualifiedJavaName; + typeDescriptor = other.typeDescriptor; this.condition = condition; mergeFrom(other); } @@ -126,13 +130,16 @@ public ConfigurationType(UnresolvedConfigurationCondition condition, String qual } void mergeFrom(ConfigurationType other) { - assert condition.equals(other.condition); - assert qualifiedJavaName.equals(other.qualifiedJavaName); + assert sameTypeAndCondition(other); mergeFlagsFrom(other); mergeFieldsFrom(other); mergeMethodsFrom(other); } + private boolean sameTypeAndCondition(ConfigurationType otherType) { + return condition.equals(otherType.condition) && typeDescriptor.equals(otherType.typeDescriptor); + } + private void mergeFlagsFrom(ConfigurationType other) { setFlagsFromOther(other, (our, their) -> our || their, ConfigurationMemberAccessibility::combine); } @@ -232,8 +239,7 @@ private void intersectMethods(ConfigurationType other) { } private void removeAll(ConfigurationType other) { - assert condition.equals(other.condition); - assert qualifiedJavaName.equals(other.qualifiedJavaName); + assert sameTypeAndCondition(other); removeFlags(other); removeFields(other); removeMethods(other); @@ -293,8 +299,8 @@ private boolean allFlagsFalse() { allDeclaredConstructorsAccess != ConfigurationMemberAccessibility.NONE || allPublicConstructorsAccess != ConfigurationMemberAccessibility.NONE); } - public String getQualifiedJavaName() { - return qualifiedJavaName; + public ConfigurationTypeDescriptor getTypeDescriptor() { + return typeDescriptor; } public synchronized void addField(String name, ConfigurationMemberDeclaration declaration, boolean finalButWritable) { @@ -439,7 +445,9 @@ public synchronized void setAllPublicConstructors(ConfigurationMemberAccessibili public synchronized void printJson(JsonWriter writer) throws IOException { writer.append('{').indent().newline(); ConfigurationConditionPrintable.printConditionAttribute(condition, writer); - writer.quote("name").append(':').quote(qualifiedJavaName); + /* GR-50385: Replace with "type" (and flip boolean entries below) */ + writer.quote("name").append(":"); + typeDescriptor.printJson(writer); optionallyPrintJsonBoolean(writer, allDeclaredFields, "allDeclaredFields"); optionallyPrintJsonBoolean(writer, allPublicFields, "allPublicFields"); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationTypeDescriptor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationTypeDescriptor.java new file mode 100644 index 000000000000..6a04d90b6ec2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationTypeDescriptor.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.configure.config; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; + +import com.oracle.svm.core.util.json.JsonPrintable; +import com.oracle.svm.core.util.json.JsonWriter; + +/** + * Provides a representation of a Java type based on String type names. This is used to parse types + * in configuration files. The supported types are: + * + *
    + *
  • Named types: regular Java types described by their fully qualified name.
  • + *
+ */ +public interface ConfigurationTypeDescriptor extends Comparable, JsonPrintable { + enum Kind { + NAMED; + } + + Kind getDescriptorType(); + + @Override + String toString(); + + /** + * Returns the qualified names of all named Java types (excluding proxy classes, lambda classes + * and similar anonymous classes) required for this type descriptor to properly describe its + * type. This is used to filter configurations based on a String-based class filter. + */ + Collection getAllQualifiedJavaNames(); + + static void checkQualifiedJavaName(String javaName) { + assert javaName.indexOf('/') == -1 : "Requires qualified Java name, not internal representation"; + assert !javaName.startsWith("[") : "Requires Java source array syntax, for example java.lang.String[]"; + } +} + +record NamedConfigurationTypeDescriptor(String name) implements ConfigurationTypeDescriptor { + + public NamedConfigurationTypeDescriptor { + ConfigurationTypeDescriptor.checkQualifiedJavaName(name); + } + + @Override + public Kind getDescriptorType() { + return Kind.NAMED; + } + + @Override + public String toString() { + return name; + } + + @Override + public Collection getAllQualifiedJavaNames() { + return Collections.singleton(name); + } + + @Override + public int compareTo(ConfigurationTypeDescriptor other) { + if (other instanceof NamedConfigurationTypeDescriptor namedOther) { + return name.compareTo(namedOther.name); + } else { + return getDescriptorType().compareTo(other.getDescriptorType()); + } + } + + @Override + public void printJson(JsonWriter writer) throws IOException { + writer.quote(name); + } +} diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ParserConfigurationAdapter.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ParserConfigurationAdapter.java index fc050fbe872b..a86b81663201 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ParserConfigurationAdapter.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ParserConfigurationAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,9 +43,9 @@ public ParserConfigurationAdapter(TypeConfiguration configuration) { } @Override - public TypeResult resolveType(UnresolvedConfigurationCondition condition, String typeName, boolean allowPrimitives) { + public TypeResult resolveType(UnresolvedConfigurationCondition condition, String typeName, boolean allowPrimitives, boolean includeAllElements) { ConfigurationType type = configuration.get(condition, typeName); - ConfigurationType result = type != null ? type : new ConfigurationType(condition, typeName); + ConfigurationType result = type != null ? type : new ConfigurationType(condition, typeName, includeAllElements); return TypeResult.forType(typeName, result); } @@ -170,7 +170,7 @@ public void registerDeclaredConstructors(UnresolvedConfigurationCondition condit @Override public String getTypeName(ConfigurationType type) { - return type.getQualifiedJavaName(); + return type.getTypeDescriptor().toString(); } @Override 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 21f5d6f0bbc4..53fa0e36acf7 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 @@ -67,6 +67,7 @@ public UnresolvedConfigurationCondition getCondition() { public void printJson(JsonWriter writer) throws IOException { writer.append('{').indent().newline(); ConfigurationConditionPrintable.printConditionAttribute(condition, writer); + /* GR-50385: Replace with "type" */ writer.quote(SerializationConfigurationParser.NAME_KEY).append(':').quote(qualifiedJavaName); if (qualifiedCustomTargetConstructorJavaName != null) { writer.append(',').newline(); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/TypeConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/TypeConfiguration.java index b9aa5d048f1b..51afbd82fb60 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/TypeConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/TypeConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ public final class TypeConfiguration extends ConfigurationBase { - private final ConcurrentMap, ConfigurationType> types = new ConcurrentHashMap<>(); + private final ConcurrentMap, ConfigurationType> types = new ConcurrentHashMap<>(); public TypeConfiguration() { } @@ -95,18 +95,22 @@ protected void removeIf(Predicate predicate) { } public ConfigurationType get(UnresolvedConfigurationCondition condition, String qualifiedJavaName) { - return types.get(new ConditionalElement<>(condition, qualifiedJavaName)); + return get(condition, new NamedConfigurationTypeDescriptor(qualifiedJavaName)); + } + + private ConfigurationType get(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor) { + return types.get(new ConditionalElement<>(condition, typeDescriptor)); } public void add(ConfigurationType type) { - ConfigurationType previous = types.putIfAbsent(new ConditionalElement<>(type.getCondition(), type.getQualifiedJavaName()), type); + ConfigurationType previous = types.putIfAbsent(new ConditionalElement<>(type.getCondition(), type.getTypeDescriptor()), type); if (previous != null && previous != type) { VMError.shouldNotReachHere("Cannot replace existing type " + previous + " with " + type); } } public void addOrMerge(ConfigurationType type) { - types.compute(new ConditionalElement<>(type.getCondition(), type.getQualifiedJavaName()), (key, value) -> { + types.compute(new ConditionalElement<>(type.getCondition(), type.getTypeDescriptor()), (key, value) -> { if (value == null) { return type; } else { @@ -117,7 +121,11 @@ public void addOrMerge(ConfigurationType type) { } public ConfigurationType getOrCreateType(UnresolvedConfigurationCondition condition, String qualifiedForNameString) { - return types.computeIfAbsent(new ConditionalElement<>(condition, qualifiedForNameString), p -> new ConfigurationType(p.condition(), p.element())); + return getOrCreateType(condition, new NamedConfigurationTypeDescriptor(qualifiedForNameString)); + } + + private ConfigurationType getOrCreateType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor) { + return types.computeIfAbsent(new ConditionalElement<>(condition, typeDescriptor), p -> new ConfigurationType(p.condition(), p.element(), false)); } @Override @@ -130,7 +138,7 @@ public void mergeConditional(UnresolvedConfigurationCondition condition, TypeCon @Override public void printJson(JsonWriter writer) throws IOException { List typesList = new ArrayList<>(this.types.values()); - typesList.sort(Comparator.comparing(ConfigurationType::getQualifiedJavaName).thenComparing(ConfigurationType::getCondition)); + typesList.sort(Comparator.comparing(ConfigurationType::getTypeDescriptor).thenComparing(ConfigurationType::getCondition)); writer.append('['); String prefix = ""; @@ -171,7 +179,6 @@ public int hashCode() { public interface Predicate { - boolean testIncludedType(ConditionalElement conditionalElement, ConfigurationType type); - + boolean testIncludedType(ConditionalElement conditionalElement, ConfigurationType type); } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationPredicate.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationPredicate.java index 49702dd12202..eb29d3454c62 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationPredicate.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,11 @@ import java.util.List; import java.util.regex.Pattern; +import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; + import com.oracle.svm.configure.config.ConfigurationPredefinedClass; import com.oracle.svm.configure.config.ConfigurationType; +import com.oracle.svm.configure.config.ConfigurationTypeDescriptor; import com.oracle.svm.configure.config.PredefinedClassesConfiguration; import com.oracle.svm.configure.config.ProxyConfiguration; import com.oracle.svm.configure.config.ResourceConfiguration; @@ -49,8 +52,8 @@ public ConditionalConfigurationPredicate(ComplexFilter filter) { } @Override - public boolean testIncludedType(ConditionalElement conditionalElement, ConfigurationType type) { - return !filter.includes(conditionalElement.condition().getTypeName()) || !filter.includes(type.getQualifiedJavaName()); + public boolean testIncludedType(ConditionalElement conditionalElement, ConfigurationType type) { + return testTypeDescriptor(conditionalElement.condition(), type.getTypeDescriptor()); } @Override @@ -82,4 +85,16 @@ public boolean testLambdaSerializationType(SerializationConfigurationLambdaCaptu public boolean testPredefinedClass(ConfigurationPredefinedClass clazz) { return clazz.getNameInfo() != null && !filter.includes(clazz.getNameInfo()); } + + private boolean testTypeDescriptor(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor) { + if (!filter.includes(condition.getTypeName())) { + return true; + } + for (String typeName : typeDescriptor.getAllQualifiedJavaNames()) { + if (!filter.includes(typeName)) { + return true; + } + } + return false; + } } 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 ff7323be53a6..2c6f5fa18570 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 @@ -95,11 +95,13 @@ public static final class Options { "Use a resource-config.json in your META-INF/native-image// directory instead."}, type = OptionType.User)// public static final HostedOptionKey ResourceConfigurationResources = new HostedOptionKey<>(LocatableMultiOptionValue.Strings.buildWithCommaDelimiter()); - @Option(help = {"Files describing program elements to be made accessible via JNI (for syntax, see ReflectionConfigurationFiles)", + @Option(help = {"Files describing program elements to be made accessible via JNI according to the schema at " + + "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/jni-config-schema-v1.1.0.json", "Use a jni-config.json in your META-INF/native-image// directory instead."}, type = OptionType.User)// @BundleMember(role = BundleMember.Role.Input)// public static final HostedOptionKey JNIConfigurationFiles = new HostedOptionKey<>(LocatableMultiOptionValue.Paths.buildWithCommaDelimiter()); - @Option(help = {"Resources describing program elements to be made accessible via JNI (see JNIConfigurationFiles).", + @Option(help = {"Resources describing program elements to be made accessible via JNI according to the schema at " + + "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/jni-config-schema-v1.1.0.json", "Use a jni-config.json in your META-INF/native-image// directory instead."}, type = OptionType.User)// public static final HostedOptionKey JNIConfigurationResources = new HostedOptionKey<>(LocatableMultiOptionValue.Strings.buildWithCommaDelimiter()); 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 beefc34476a4..370b680d1a6c 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -130,6 +130,23 @@ protected void checkAttributes(EconomicMap map, String type, Col } } + protected void checkHasExactlyOneAttribute(EconomicMap map, String type, Collection alternativeAttributes) { + boolean attributeFound = false; + for (String key : map.getKeys()) { + if (alternativeAttributes.contains(key)) { + if (attributeFound) { + String message = "Exactly one of [" + String.join(", ", alternativeAttributes) + "] must be set in " + type; + throw new JSONParserException(message); + } + attributeFound = true; + } + } + if (!attributeFound) { + String message = "Exactly one of [" + String.join(", ", alternativeAttributes) + "] must be set in " + type; + throw new JSONParserException(message); + } + } + /** * Used to warn about schema errors in configuration files. Should never be used if the type is * missing. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java index 2632343f0a5a..95edc68f319e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,7 @@ public final class ReflectionConfigurationParser extends ConfigurationPars private final ConfigurationConditionResolver conditionResolver; private final ReflectionConfigurationParserDelegate delegate; - private static final List OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS = Arrays.asList("allDeclaredConstructors", "allPublicConstructors", + private static final List OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS = Arrays.asList("name", "type", "allDeclaredConstructors", "allPublicConstructors", "allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields", "allDeclaredClasses", "allRecordComponents", "allPermittedSubclasses", "allNestMembers", "allSigners", "allPublicClasses", "methods", "queriedMethods", "fields", CONDITIONAL_KEY, @@ -75,22 +75,43 @@ private void parseClassArray(List classes) { } private void parseClass(EconomicMap data) { - checkAttributes(data, "reflection class descriptor object", Collections.singleton("name"), OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS); - - Object classObject = data.get("name"); - String className = asString(classObject, "name"); + checkAttributes(data, "reflection class descriptor object", Collections.emptyList(), OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS); + checkHasExactlyOneAttribute(data, "reflection class descriptor object", List.of("name", "type")); TypeResult conditionResult = conditionResolver.resolveCondition(parseCondition(data)); if (!conditionResult.isPresent()) { return; } + String className; + Object typeObject = data.get("type"); + /* + * Classes registered using the old ("class") syntax will require elements (fields, methods, + * constructors, ...) to be registered for runtime queries, whereas the new ("type") syntax + * will automatically register all elements as queried. + */ + if (typeObject != null) { + if (typeObject instanceof String stringValue) { + className = stringValue; + } else { + /* + * We warn if we find a future version of a type descriptor (as a JSON object) + * instead of failing parsing. + */ + asMap(typeObject, "type descriptor should be a string or object"); + handleMissingElement("Unsupported type descriptor of type object"); + return; + } + } else { + className = asString(data.get("name"), "class name should be a string"); + } + /* * Even if primitives cannot be queried through Class.forName, they can be registered to * allow getDeclaredMethods() and similar bulk queries at run time. */ C condition = conditionResult.get(); - TypeResult result = delegate.resolveType(condition, className, true); + TypeResult result = delegate.resolveType(condition, className, true, false); if (!result.isPresent()) { handleMissingElement("Could not resolve class " + className + " for reflection configuration.", result.getException()); return; @@ -277,7 +298,7 @@ private List parseMethodParameters(T clazz, String methodName, List t List result = new ArrayList<>(); for (Object type : types) { String typeName = asString(type, "types"); - TypeResult typeResult = delegate.resolveType(conditionResolver.alwaysTrue(), typeName, true); + TypeResult typeResult = delegate.resolveType(conditionResolver.alwaysTrue(), typeName, true, false); if (!typeResult.isPresent()) { handleMissingElement("Could not register method " + formatMethod(clazz, methodName) + " for reflection.", typeResult.getException()); return null; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java index b4b29c58c525..c561d8a509da 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ public interface ReflectionConfigurationParserDelegate { - TypeResult resolveType(C condition, String typeName, boolean allowPrimitives); + TypeResult resolveType(C condition, String typeName, boolean allowPrimitives, boolean includeAllElements); void registerType(C condition, T type); 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 5a89eddef9e8..8f8e3361428e 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 @@ -39,6 +39,7 @@ public class SerializationConfigurationParser extends ConfigurationParser { public static final String NAME_KEY = "name"; + public static final String TYPE_KEY = "type"; public static final String CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY = "customTargetConstructorClass"; private static final String SERIALIZATION_TYPES_KEY = "types"; private static final String LAMBDA_CAPTURING_SERIALIZATION_TYPES_KEY = "lambdaCapturingTypes"; @@ -96,7 +97,8 @@ private void parseSerializationDescriptorObject(EconomicMap data if (lambdaCapturingType) { checkAttributes(data, "serialization descriptor object", Collections.singleton(NAME_KEY), Collections.singleton(CONDITIONAL_KEY)); } else { - checkAttributes(data, "serialization descriptor object", Collections.singleton(NAME_KEY), Arrays.asList(CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY, CONDITIONAL_KEY)); + 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)); } UnresolvedConfigurationCondition unresolvedCondition = parseCondition(data); @@ -104,7 +106,23 @@ private void parseSerializationDescriptorObject(EconomicMap data if (!condition.isPresent()) { return; } - String targetSerializationClass = asString(data.get(NAME_KEY)); + + String targetSerializationClass; + Object typeObject = data.get(TYPE_KEY); + if (typeObject != null) { + if (typeObject instanceof String stringValue) { + targetSerializationClass = stringValue; + } else { + /* + * We return if we find a future version of a type descriptor (as a JSON object) + * instead of failing parsing. + */ + asMap(typeObject, "type descriptor should be a string or object"); + return; + } + } else { + targetSerializationClass = asString(data.get(NAME_KEY)); + } if (lambdaCapturingType) { serializationSupport.registerLambdaCapturingClass(condition.get(), targetSerializationClass); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/doc-files/ReflectionConfigurationFilesHelp.txt b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/doc-files/ReflectionConfigurationFilesHelp.txt index f55641cebe0a..1e2a5871659b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/doc-files/ReflectionConfigurationFilesHelp.txt +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/doc-files/ReflectionConfigurationFilesHelp.txt @@ -3,7 +3,7 @@ Use a reflect-config.json in your META-INF/native-image// d The JSON object schema is described at: - https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reflect-config-schema-v1.0.0.json + https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/reflect-config-schema-v1.1.0.json Example: diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/doc-files/SerializationConfigurationFilesHelp.txt b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/doc-files/SerializationConfigurationFilesHelp.txt index e3116ec4a51b..b6509022c68f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/doc-files/SerializationConfigurationFilesHelp.txt +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/doc-files/SerializationConfigurationFilesHelp.txt @@ -3,7 +3,7 @@ Use a serialization-config.json in your META-INF/native-image//> resolveType(ConfigurationCondition condition, String typeName, boolean allowPrimitives) { - TypeResult> result = super.resolveType(condition, typeName, allowPrimitives); + public TypeResult> resolveType(ConfigurationCondition condition, String typeName, boolean allowPrimitives, boolean includeAllElements) { + TypeResult> result = super.resolveType(condition, typeName, allowPrimitives, includeAllElements); if (!result.isPresent()) { Throwable classLookupException = result.getException(); if (classLookupException instanceof LinkageError) { 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 18dcdeeaceaf..dc08e634cde8 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,7 +63,7 @@ public void registerType(ConfigurationCondition condition, Class type) { } @Override - public TypeResult> resolveType(ConfigurationCondition condition, String typeName, boolean allowPrimitives) { + public TypeResult> resolveType(ConfigurationCondition condition, String typeName, boolean allowPrimitives, boolean includeAllElements) { String name = canonicalizeTypeName(typeName); return classLoader.findClass(name, allowPrimitives); }