From 55f97ecec555df8f06ad0cf2bcafab566350376c Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Fri, 2 May 2025 20:19:13 +0200 Subject: [PATCH 1/3] Move UnresolvedConfigurationCondition to com.oracle.svm.configure --- .../svm/configure/test/config/OmitPreviousConfigTests.java | 2 +- .../configure/test/config/ResourceConfigurationTest.java | 2 +- .../svm/configure/ConditionalConfigurationParser.java | 5 ++--- .../src/com/oracle/svm/configure/ConditionalElement.java | 2 -- .../src/com/oracle/svm/configure/ConfigurationBase.java | 2 -- .../svm/configure/LegacyReflectionConfigurationParser.java | 1 - .../svm/configure/LegacyResourceConfigurationParser.java | 1 - .../configure/LegacySerializationConfigurationParser.java | 1 - .../com/oracle/svm/configure/ProxyConfigurationParser.java | 1 - .../com/oracle/svm/configure/ReflectionMetadataParser.java | 1 - .../oracle/svm/configure/ResourceConfigurationParser.java | 1 - .../com/oracle/svm/configure/ResourceMetadataParser.java | 1 - .../oracle/svm/configure/SerializationMetadataParser.java | 1 - .../svm/configure}/UnresolvedConfigurationCondition.java | 4 +++- .../configure/config/ConfigurationConditionPrintable.java | 6 +++--- .../com/oracle/svm/configure/config/ConfigurationType.java | 3 +-- .../svm/configure/config/ParserConfigurationAdapter.java | 3 +-- .../configure/config/PredefinedClassesConfiguration.java | 3 +-- .../com/oracle/svm/configure/config/ProxyConfiguration.java | 3 +-- .../oracle/svm/configure/config/ResourceConfiguration.java | 2 +- .../svm/configure/config/SerializationConfiguration.java | 2 +- .../SerializationConfigurationLambdaCapturingType.java | 3 +-- .../configure/config/SerializationConfigurationType.java | 2 +- .../com/oracle/svm/configure/config/TypeConfiguration.java | 3 +-- .../conditional/ConditionalConfigurationComputer.java | 4 ++-- .../conditional/ConditionalConfigurationPredicate.java | 3 +-- .../config/conditional/ConfigurationConditionResolver.java | 3 +-- .../src/com/oracle/svm/configure/trace/JniProcessor.java | 2 +- .../com/oracle/svm/configure/trace/ReflectionProcessor.java | 2 +- .../oracle/svm/configure/trace/SerializationProcessor.java | 2 +- .../svm/hosted/reflect/NativeImageConditionResolver.java | 2 +- 31 files changed, 27 insertions(+), 46 deletions(-) rename {sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl => substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure}/UnresolvedConfigurationCondition.java (97%) 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 bdf714732a39..b21b261a7efd 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 @@ -35,12 +35,12 @@ import java.util.function.Function; import java.util.function.Predicate; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import org.junit.Assert; import org.junit.Test; import com.oracle.svm.configure.ConfigurationTypeDescriptor; import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationFileCollection; import com.oracle.svm.configure.config.ConfigurationMemberInfo; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberAccessibility; diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java index 7af57c99b3f6..1649fd56ed0e 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java @@ -35,13 +35,13 @@ import java.util.Locale; import org.graalvm.nativeimage.impl.ConfigurationCondition; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import org.junit.Assert; import org.junit.Test; import com.oracle.svm.configure.ConfigurationParserOption; import com.oracle.svm.configure.ResourceConfigurationParser; import com.oracle.svm.configure.ResourcesRegistry; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ResourceConfiguration; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java index 99955b356743..3afc61c8a394 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java @@ -24,13 +24,12 @@ */ package com.oracle.svm.configure; -import static org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition.TYPE_REACHABLE_KEY; -import static org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition.TYPE_REACHED_KEY; +import static com.oracle.svm.configure.UnresolvedConfigurationCondition.TYPE_REACHABLE_KEY; +import static com.oracle.svm.configure.UnresolvedConfigurationCondition.TYPE_REACHED_KEY; import java.util.EnumSet; import org.graalvm.collections.EconomicMap; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; public abstract class ConditionalConfigurationParser extends ConfigurationParser { public static final String CONDITIONAL_KEY = "condition"; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalElement.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalElement.java index 4daa1220cdab..c29dc4177530 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalElement.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalElement.java @@ -28,8 +28,6 @@ import java.util.Comparator; import java.util.function.Function; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - public record ConditionalElement(UnresolvedConfigurationCondition condition, T element) { public static > Comparator> comparator() { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationBase.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationBase.java index 35342fe6927a..fbfd7eb15425 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationBase.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationBase.java @@ -28,8 +28,6 @@ import java.util.EnumSet; import java.util.function.Consumer; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - import jdk.graal.compiler.util.json.JsonPrintable; import jdk.graal.compiler.util.json.JsonWriter; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyReflectionConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyReflectionConfigurationParser.java index f2e4b76395fa..1cab074429db 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyReflectionConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyReflectionConfigurationParser.java @@ -32,7 +32,6 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.util.TypeResult; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyResourceConfigurationParser.java index 240cc3febe4d..9a444c185864 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyResourceConfigurationParser.java @@ -32,7 +32,6 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.util.GlobUtils; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacySerializationConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacySerializationConfigurationParser.java index 5f4561627df3..dc383856bfb5 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacySerializationConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacySerializationConfigurationParser.java @@ -32,7 +32,6 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.util.LogUtils; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ProxyConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ProxyConfigurationParser.java index af4b9fa15b8b..5696bac54171 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ProxyConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ProxyConfigurationParser.java @@ -32,7 +32,6 @@ import java.util.stream.Collectors; import org.graalvm.collections.EconomicMap; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.util.TypeResult; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionMetadataParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionMetadataParser.java index 24452005fa7f..95565e1cfed7 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionMetadataParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionMetadataParser.java @@ -32,7 +32,6 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.util.TypeResult; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourceConfigurationParser.java index d5e8e7f9d954..e636ea5d43fb 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourceConfigurationParser.java @@ -32,7 +32,6 @@ import java.util.stream.Collectors; import org.graalvm.collections.EconomicMap; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.util.LocaleUtil; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourceMetadataParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourceMetadataParser.java index d24b246a85de..9b744d8bfc72 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourceMetadataParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourceMetadataParser.java @@ -28,7 +28,6 @@ import java.util.EnumSet; import org.graalvm.collections.EconomicMap; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/SerializationMetadataParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/SerializationMetadataParser.java index 9baaf7d08d68..16642fd36f55 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/SerializationMetadataParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/SerializationMetadataParser.java @@ -31,7 +31,6 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.util.LogUtils; diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/UnresolvedConfigurationCondition.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java similarity index 97% rename from sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/UnresolvedConfigurationCondition.java rename to substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java index fb7a182cda99..589bda936f15 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/UnresolvedConfigurationCondition.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java @@ -38,10 +38,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.graalvm.nativeimage.impl; +package com.oracle.svm.configure; import java.util.Objects; +import org.graalvm.nativeimage.impl.ConfigurationCondition; + /** * Represents a {@link ConfigurationCondition} during parsing before it is resolved in a context of * the classpath. diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationConditionPrintable.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationConditionPrintable.java index 561caaa71e5b..bef40b8f5f64 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationConditionPrintable.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationConditionPrintable.java @@ -25,12 +25,12 @@ package com.oracle.svm.configure.config; import static com.oracle.svm.configure.ConditionalConfigurationParser.CONDITIONAL_KEY; -import static org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition.TYPE_REACHABLE_KEY; -import static org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition.TYPE_REACHED_KEY; +import static com.oracle.svm.configure.UnresolvedConfigurationCondition.TYPE_REACHABLE_KEY; +import static com.oracle.svm.configure.UnresolvedConfigurationCondition.TYPE_REACHED_KEY; import java.io.IOException; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import jdk.graal.compiler.util.json.JsonWriter; 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 b45b154ef2d6..72eba03be80e 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 @@ -35,9 +35,8 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - import com.oracle.svm.configure.ConfigurationTypeDescriptor; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberAccessibility; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration; 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 38accaf198a7..7dc8b6f12c3a 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 @@ -26,10 +26,9 @@ import java.util.List; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - import com.oracle.svm.configure.ConfigurationTypeDescriptor; import com.oracle.svm.configure.ReflectionConfigurationParserDelegate; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberAccessibility; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration; import com.oracle.svm.util.TypeResult; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/PredefinedClassesConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/PredefinedClassesConfiguration.java index cbc36829cd9b..7c563d6ec6b4 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/PredefinedClassesConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/PredefinedClassesConfiguration.java @@ -36,13 +36,12 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - import com.oracle.svm.configure.ConfigurationBase; import com.oracle.svm.configure.ConfigurationFile; import com.oracle.svm.configure.ConfigurationParser; import com.oracle.svm.configure.ConfigurationParserOption; import com.oracle.svm.configure.PredefinedClassesConfigurationParser; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import jdk.graal.compiler.phases.common.LazyValue; import jdk.graal.compiler.util.Digest; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ProxyConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ProxyConfiguration.java index 3780ad9fe8ab..e9615fb7ddb7 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ProxyConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ProxyConfiguration.java @@ -32,13 +32,12 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - import com.oracle.svm.configure.ConditionalElement; import com.oracle.svm.configure.ConfigurationBase; import com.oracle.svm.configure.ConfigurationParser; import com.oracle.svm.configure.ConfigurationParserOption; import com.oracle.svm.configure.ProxyConfigurationParser; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import jdk.graal.compiler.util.json.JsonWriter; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index 61c884918041..bbb7f609ea2f 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -41,7 +41,6 @@ import java.util.regex.Pattern; import org.graalvm.nativeimage.impl.ConfigurationCondition; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.ConditionalElement; import com.oracle.svm.configure.ConfigurationBase; @@ -49,6 +48,7 @@ import com.oracle.svm.configure.ConfigurationParserOption; import com.oracle.svm.configure.ResourceConfigurationParser; import com.oracle.svm.configure.ResourcesRegistry; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.util.GlobUtils; import com.oracle.svm.util.NativeImageResourcePathRepresentation; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfiguration.java index 79186a3ed3cc..936a91fad1ef 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfiguration.java @@ -34,13 +34,13 @@ import java.util.concurrent.ConcurrentHashMap; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.ConditionalElement; import com.oracle.svm.configure.ConfigurationBase; import com.oracle.svm.configure.ConfigurationParser; import com.oracle.svm.configure.ConfigurationParserOption; import com.oracle.svm.configure.SerializationConfigurationParser; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import jdk.graal.compiler.java.LambdaUtils; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationLambdaCapturingType.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationLambdaCapturingType.java index 37a4a4db8e41..c4b618e7de69 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationLambdaCapturingType.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/SerializationConfigurationLambdaCapturingType.java @@ -27,9 +27,8 @@ import java.io.IOException; import java.util.Objects; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - import com.oracle.svm.configure.SerializationConfigurationParser; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import jdk.graal.compiler.util.json.JsonPrintable; import jdk.graal.compiler.util.json.JsonWriter; 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 141aa68d9346..f4c8c953002c 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 @@ -30,7 +30,7 @@ import java.io.IOException; import java.util.Objects; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import jdk.graal.compiler.util.json.JsonPrintable; import jdk.graal.compiler.util.json.JsonWriter; 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 d95ab41f07d8..55c0f4e504d4 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 @@ -33,8 +33,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - import com.oracle.svm.configure.ConditionalElement; import com.oracle.svm.configure.ConfigurationBase; import com.oracle.svm.configure.ConfigurationParser; @@ -42,6 +40,7 @@ import com.oracle.svm.configure.ConfigurationTypeDescriptor; import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.configure.ReflectionConfigurationParser; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import jdk.graal.compiler.util.json.JsonWriter; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationComputer.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationComputer.java index c3c703f750c9..87b524c06689 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationComputer.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationComputer.java @@ -32,10 +32,10 @@ import java.util.Map; import java.util.Set; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - import com.oracle.svm.configure.ConfigurationBase; import com.oracle.svm.configure.ConfigurationFile; +import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationSet; import com.oracle.svm.configure.filters.ComplexFilter; 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 338a4ffe2af4..6dc34c1071cc 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 @@ -26,10 +26,9 @@ import java.util.List; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - import com.oracle.svm.configure.ConditionalElement; import com.oracle.svm.configure.ConfigurationTypeDescriptor; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationPredefinedClass; import com.oracle.svm.configure.config.ConfigurationType; import com.oracle.svm.configure.config.PredefinedClassesConfiguration; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConfigurationConditionResolver.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConfigurationConditionResolver.java index e4f595b557ed..5070694d648d 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConfigurationConditionResolver.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConfigurationConditionResolver.java @@ -24,8 +24,7 @@ */ package com.oracle.svm.configure.config.conditional; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; - +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.util.TypeResult; public interface ConfigurationConditionResolver { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java index 7423c7896970..f29b8e980a7a 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java @@ -30,8 +30,8 @@ import java.util.List; import org.graalvm.collections.EconomicMap; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration; import com.oracle.svm.configure.config.ConfigurationMethod; import com.oracle.svm.configure.config.ConfigurationSet; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java index 117d4234bfdb..da8f0891dfcd 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java @@ -31,11 +31,11 @@ import java.util.List; import org.graalvm.collections.EconomicMap; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.ConfigurationTypeDescriptor; import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.configure.ProxyConfigurationTypeDescriptor; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberAccessibility; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration; import com.oracle.svm.configure.config.ConfigurationMethod; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java index 2b48e973f745..cfca9cf0715f 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java @@ -30,9 +30,9 @@ import java.util.List; import org.graalvm.collections.EconomicMap; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationSet; import com.oracle.svm.configure.config.SerializationConfiguration; import com.oracle.svm.configure.config.TypeConfiguration; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/NativeImageConditionResolver.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/NativeImageConditionResolver.java index 2912bd94558c..f132171c2934 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/NativeImageConditionResolver.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/NativeImageConditionResolver.java @@ -25,9 +25,9 @@ package com.oracle.svm.hosted.reflect; import org.graalvm.nativeimage.impl.ConfigurationCondition; -import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition; import com.oracle.svm.configure.ConfigurationTypeDescriptor; +import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; From e4f36524bbf045b8fdca3de80d7e086c70d612e9 Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Tue, 11 Mar 2025 11:47:24 +0100 Subject: [PATCH 2/3] Clarify type name handling from different sources --- substratevm/mx.substratevm/suite.py | 5 + .../test/config/ClassNameSupportTest.java | 87 +++++++ .../svm/configure/ClassNameSupport.java | 233 ++++++++++++++++++ .../ConditionalConfigurationParser.java | 15 +- .../svm/configure/ConfigurationParser.java | 6 +- .../ConfigurationTypeDescriptor.java | 24 -- ...egacySerializationConfigurationParser.java | 2 +- .../NamedConfigurationTypeDescriptor.java | 37 ++- .../ProxyConfigurationTypeDescriptor.java | 11 +- .../ReflectionConfigurationParser.java | 2 +- .../SerializationConfigurationParser.java | 11 +- .../UnresolvedConfigurationCondition.java | 33 +-- .../configure/config/ConfigurationType.java | 5 +- .../configure/config/TypeConfiguration.java | 5 - .../ConditionalConfigurationComputer.java | 2 +- .../svm/configure/trace/JniProcessor.java | 40 +-- .../configure/trace/ReflectionProcessor.java | 25 +- .../trace/SerializationProcessor.java | 9 +- .../svm/core/hub/ClassForNameSupport.java | 21 +- .../core/jni/access/JNIAccessibleClass.java | 7 +- .../jni/access/JNIReflectionDictionary.java | 20 +- .../config/ReflectionRegistryAdapter.java | 4 +- .../svm/hosted/config/RegistryAdapter.java | 18 +- .../svm/hosted/jni/JNIAccessFeature.java | 7 +- .../reflect/NativeImageConditionResolver.java | 6 +- 25 files changed, 503 insertions(+), 132 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ClassNameSupportTest.java create mode 100644 substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ClassNameSupport.java diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index cd6f46b871fd..3d9609bb3da5 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -1181,6 +1181,11 @@ "sdk:NATIVEIMAGE", "com.oracle.svm.configure", ], + "requiresConcealed": { + "jdk.internal.vm.ci": [ + "jdk.vm.ci.meta" + ], + }, "checkstyle": "com.oracle.svm.test", "workingSets": "SVM", "annotationProcessors": [ diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ClassNameSupportTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ClassNameSupportTest.java new file mode 100644 index 000000000000..19a2c5324e25 --- /dev/null +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ClassNameSupportTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025, 2025, 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.test.config; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.oracle.svm.configure.ClassNameSupport; +import com.oracle.svm.configure.test.AddExports; + +import jdk.vm.ci.meta.MetaUtil; + +@AddExports({"jdk.internal.vm.ci/jdk.vm.ci.meta"}) +public class ClassNameSupportTest { + @Test + public void testNameAliases() { + for (Class clazz : List.of(Object.class, int.class, EnclosingClass.class, EnclosingClass.InnerClass.class, EnclosingClass.StaticInnerClass.class, + new EnclosingClass().anonymousClass().getClass(), Object[].class, int[].class, EnclosingClass.InnerClass[].class)) { + testNameAliasesForClass(clazz); + } + } + + private static void testNameAliasesForClass(Class clazz) { + String typeName = clazz.getTypeName(); + String reflectionName = clazz.getName(); + + Assert.assertTrue(ClassNameSupport.isValidTypeName(typeName)); + Assert.assertTrue(ClassNameSupport.isValidReflectionName(reflectionName)); + + Assert.assertEquals(typeName, ClassNameSupport.reflectionNameToTypeName(reflectionName)); + Assert.assertEquals(reflectionName, ClassNameSupport.typeNameToReflectionName(typeName)); + + /* Primitive classes cannot be accessed through JNI */ + if (!clazz.isPrimitive()) { + String internalName = MetaUtil.toInternalName(reflectionName); + String jniName = internalName.startsWith("L") ? internalName.substring(1, internalName.length() - 1) : internalName; + + Assert.assertTrue(ClassNameSupport.isValidJNIName(jniName)); + + Assert.assertEquals(typeName, ClassNameSupport.jniNameToTypeName(jniName)); + Assert.assertEquals(reflectionName, ClassNameSupport.jniNameToReflectionName(jniName)); + Assert.assertEquals(jniName, ClassNameSupport.typeNameToJNIName(typeName)); + Assert.assertEquals(jniName, ClassNameSupport.reflectionNameToJNIName(reflectionName)); + } + } +} + +class EnclosingClass { + Object anonymousClass() { + return new Object() { + @Override + public String toString() { + return null; + } + }; + } + + class InnerClass { + } + + static class StaticInnerClass { + } +} diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ClassNameSupport.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ClassNameSupport.java new file mode 100644 index 000000000000..4a29faa0b648 --- /dev/null +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ClassNameSupport.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2025, 2025, 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; + +import jdk.vm.ci.meta.JavaKind; + +/** + * There isn't a single standard way of referring to classes by name in the Java ecosystem. In the + * context of Native Image reflection, there are three main ways of referring to a class: + * + *
    + *
  • The "type name": this is the result of calling {@code getTypeName()} on a {@code Class} + * object. This is a human-readable name and is the preferred way of specifying classes in JSON + * metadata files.
  • + *
  • The "reflection name": this is used for calls to {@link Class#forName(String)} and others + * using the same syntax. It is the binary name of the class except for array classes, where it is + * formed using the internal name of the class.
  • + *
  • The "JNI name": this is used for calls to {code FindClass} through JNI. This name is similar + * to the reflection name but uses '/' instead of '.' as package separator.
  • + *
+ * + * This class provides utility methods to be able to switch between those names and avoid confusion + * about which format a given string is encoded as. + * + * Here is a breakdown of the various names of different types of classes: + * + *
+ * | Type            | Type name           | Reflection name      | JNI name             |
+ * | --------------- | ------------------- | -------------------- | -------------------- |
+ * | Regular class   | package.ClassName   | package.ClassName    | package/ClassName    |
+ * | Primitive type  | type                | -                    | -                    |
+ * | Array type      | package.ClassName[] | [Lpackage.ClassName; | [Lpackage/ClassName; |
+ * | Primitive array | type[]              | [T                   | [T                   |
+ * | Inner class     | package.Outer$Inner | package.Outer$Inner  | package/Outer$Inner  |
+ * | Anonymous class | package.ClassName$1 | package.ClassName$1  | package/ClassName$1  |
+ * 
+ */ +public class ClassNameSupport { + public static String reflectionNameToTypeName(String reflectionName) { + if (!isValidReflectionName(reflectionName)) { + return reflectionName; + } + return reflectionNameToTypeNameUnchecked(reflectionName); + } + + public static String jniNameToTypeName(String jniName) { + if (!isValidJNIName(jniName)) { + return jniName; + } + return reflectionNameToTypeNameUnchecked(jniNameToReflectionNameUnchecked(jniName)); + } + + private static String reflectionNameToTypeNameUnchecked(String reflectionName) { + int arrayDimension = wrappingArrayDimension(reflectionName); + if (arrayDimension > 0) { + return arrayElementTypeToTypeName(reflectionName, arrayDimension) + "[]".repeat(arrayDimension); + } + return reflectionName; + } + + public static String typeNameToReflectionName(String typeName) { + if (!isValidTypeName(typeName)) { + return typeName; + } + return typeNameToReflectionNameUnchecked(typeName); + } + + public static String typeNameToJNIName(String typeName) { + if (!isValidTypeName(typeName)) { + return typeName; + } + return reflectionNameToJNINameUnchecked(typeNameToReflectionNameUnchecked(typeName)); + } + + private static String typeNameToReflectionNameUnchecked(String typeName) { + int arrayDimension = trailingArrayDimension(typeName); + if (arrayDimension > 0) { + return "[".repeat(arrayDimension) + typeNameToArrayElementType(typeName.substring(0, typeName.length() - arrayDimension * 2)); + } + return typeName; + } + + public static String jniNameToReflectionName(String jniName) { + if (!isValidJNIName(jniName)) { + return jniName; + } + return jniNameToReflectionNameUnchecked(jniName); + } + + private static String jniNameToReflectionNameUnchecked(String jniName) { + return jniName.replace('/', '.'); + } + + public static String reflectionNameToJNIName(String reflectionName) { + if (!isValidReflectionName(reflectionName)) { + return reflectionName; + } + return reflectionNameToJNINameUnchecked(reflectionName); + } + + private static String reflectionNameToJNINameUnchecked(String reflectionName) { + return reflectionName.replace('.', '/'); + } + + private static String arrayElementTypeToTypeName(String arrayElementType, int startIndex) { + char typeChar = arrayElementType.charAt(startIndex); + return switch (typeChar) { + case 'L' -> arrayElementType.substring(startIndex + 1, arrayElementType.length() - 1); + case 'B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z' -> JavaKind.fromPrimitiveOrVoidTypeChar(typeChar).getJavaName(); + default -> null; + }; + } + + private static String typeNameToArrayElementType(String typeName) { + Class primitiveType = forPrimitiveName(typeName); + if (primitiveType != null) { + return String.valueOf(JavaKind.fromJavaClass(primitiveType).getTypeChar()); + } + return "L" + typeName + ";"; + } + + public static boolean isValidTypeName(String name) { + return isValidFullyQualifiedClassName(name, 0, name.length() - trailingArrayDimension(name) * 2, '.'); + } + + public static boolean isValidReflectionName(String name) { + return isValidWrappingArraySyntaxName(name, '.'); + } + + public static boolean isValidJNIName(String name) { + return isValidWrappingArraySyntaxName(name, '/'); + } + + private static boolean isValidWrappingArraySyntaxName(String name, char packageSeparator) { + int arrayDimension = wrappingArrayDimension(name); + if (arrayDimension > 0) { + return isValidWrappingArrayElementType(name, arrayDimension, packageSeparator); + } + return isValidFullyQualifiedClassName(name, 0, name.length(), packageSeparator); + } + + private static boolean isValidWrappingArrayElementType(String name, int startIndex, char packageSeparator) { + if (startIndex == name.length()) { + return false; + } + return switch (name.charAt(startIndex)) { + case 'L' -> + name.charAt(name.length() - 1) == ';' && isValidFullyQualifiedClassName(name, startIndex + 1, name.length() - 1, packageSeparator); + case 'B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z' -> startIndex == name.length() - 1; + default -> false; + }; + } + + private static boolean isValidFullyQualifiedClassName(String name, int startIndex, int endIndex, char packageSeparator) { + int lastPackageSeparatorIndex = -1; + for (int i = startIndex; i < endIndex; ++i) { + char current = name.charAt(i); + if (current == packageSeparator) { + if (lastPackageSeparatorIndex == i - 1) { + return false; + } + lastPackageSeparatorIndex = i; + } else if (!Character.isJavaIdentifierPart(current)) { + return false; + } + } + return true; + } + + private static int wrappingArrayDimension(String name) { + int arrayDimension = 0; + while (arrayDimension < name.length() && name.charAt(arrayDimension) == '[') { + arrayDimension++; + } + return arrayDimension; + } + + private static int trailingArrayDimension(String name) { + int arrayDimension = 0; + while (endsWithTrailingArraySyntax(name, name.length() - arrayDimension * 2)) { + arrayDimension++; + } + return arrayDimension; + } + + private static boolean endsWithTrailingArraySyntax(String string, int endIndex) { + return endIndex >= "[]".length() && string.charAt(endIndex - 2) == '[' && string.charAt(endIndex - 1) == ']'; + } + + // Copied from java.lang.Class from JDK 22 + public static Class forPrimitiveName(String primitiveName) { + return switch (primitiveName) { + // Integral types + case "int" -> int.class; + case "long" -> long.class; + case "short" -> short.class; + case "char" -> char.class; + case "byte" -> byte.class; + + // Floating-point types + case "float" -> float.class; + case "double" -> double.class; + + // Other types + case "boolean" -> boolean.class; + case "void" -> void.class; + + default -> null; + }; + } +} diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java index 3afc61c8a394..54c9fd6fca3c 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java @@ -60,8 +60,8 @@ protected UnresolvedConfigurationCondition parseCondition(EconomicMap parseName(EconomicMap data, boolean treatAllNameEntriesAsType) { Object name = data.get(NAME_KEY); if (name != null) { - NamedConfigurationTypeDescriptor typeDescriptor = new NamedConfigurationTypeDescriptor(asString(name)); + NamedConfigurationTypeDescriptor typeDescriptor = NamedConfigurationTypeDescriptor.fromJSONName(asString(name)); return Optional.of(new TypeDescriptorWithOrigin(typeDescriptor, treatAllNameEntriesAsType)); } else { throw failOnSchemaError("must have type or name specified for an element"); @@ -252,7 +252,7 @@ protected static Optional parseName(EconomicMap parseTypeContents(Object typeObject) { if (typeObject instanceof String stringValue) { - return Optional.of(new NamedConfigurationTypeDescriptor(stringValue)); + return Optional.of(NamedConfigurationTypeDescriptor.fromJSONName(stringValue)); } else { EconomicMap type = asMap(typeObject, "type descriptor should be a string or object"); if (type.containsKey(PROXY_KEY)) { @@ -271,6 +271,6 @@ protected static Optional parseTypeContents(Object private static ProxyConfigurationTypeDescriptor getProxyDescriptor(Object proxyObject) { List proxyInterfaces = asList(proxyObject, "proxy interface content should be an interface list"); List proxyInterfaceNames = proxyInterfaces.stream().map(obj -> asString(obj, "proxy")).toList(); - return new ProxyConfigurationTypeDescriptor(proxyInterfaceNames); + return ProxyConfigurationTypeDescriptor.fromInterfaceTypeNames(proxyInterfaceNames); } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationTypeDescriptor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationTypeDescriptor.java index f8a089046917..322f91a97460 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationTypeDescriptor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationTypeDescriptor.java @@ -26,12 +26,7 @@ import java.util.Collection; -import org.graalvm.nativeimage.ImageInfo; - -import com.oracle.svm.util.LogUtils; - import jdk.graal.compiler.util.json.JsonPrintable; -import jdk.vm.ci.meta.MetaUtil; /** * Provides a representation of a Java type based on String type names. This is used to parse types @@ -43,18 +38,6 @@ * */ public interface ConfigurationTypeDescriptor extends Comparable, JsonPrintable { - static String canonicalizeTypeName(String typeName) { - if (typeName == null) { - return null; - } - String name = typeName; - if (name.indexOf('[') != -1) { - /* accept "int[][]", "java.lang.String[]" */ - name = MetaUtil.internalNameToJava(MetaUtil.toInternalName(name), true, true); - } - return name; - } - enum Kind { NAMED, PROXY @@ -71,11 +54,4 @@ enum Kind { * type. This is used to filter configurations based on a String-based class filter. */ Collection getAllQualifiedJavaNames(); - - static String checkQualifiedJavaName(String javaName) { - if (ImageInfo.inImageBuildtimeCode() && !(javaName.indexOf('/') == -1 || javaName.indexOf('/') > javaName.lastIndexOf('.'))) { - LogUtils.warning("Type descriptor requires qualified Java name, not internal representation: %s", javaName); - } - return canonicalizeTypeName(javaName); - } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacySerializationConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacySerializationConfigurationParser.java index dc383856bfb5..ce4a1e5c0dc5 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacySerializationConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacySerializationConfigurationParser.java @@ -95,7 +95,7 @@ protected void parseSerializationDescriptorObject(EconomicMap da Arrays.asList(CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY, CONDITIONAL_KEY)); } - NamedConfigurationTypeDescriptor targetSerializationClass = new NamedConfigurationTypeDescriptor(asString(data.get(NAME_KEY))); + NamedConfigurationTypeDescriptor targetSerializationClass = NamedConfigurationTypeDescriptor.fromJSONName(asString(data.get(NAME_KEY))); UnresolvedConfigurationCondition unresolvedCondition = parseCondition(data, false); var condition = conditionResolver.resolveCondition(unresolvedCondition); if (!condition.isPresent()) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/NamedConfigurationTypeDescriptor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/NamedConfigurationTypeDescriptor.java index 26e9555f3a9a..f5f94703cc1a 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/NamedConfigurationTypeDescriptor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/NamedConfigurationTypeDescriptor.java @@ -27,13 +27,30 @@ import java.io.IOException; import java.util.Collection; import java.util.Collections; +import java.util.Objects; import jdk.graal.compiler.util.json.JsonWriter; public record NamedConfigurationTypeDescriptor(String name) implements ConfigurationTypeDescriptor { - public NamedConfigurationTypeDescriptor(String name) { - this.name = ConfigurationTypeDescriptor.checkQualifiedJavaName(name); + public static NamedConfigurationTypeDescriptor fromJSONName(String jsonName) { + if (!ClassNameSupport.isValidTypeName(jsonName) && ClassNameSupport.isValidReflectionName(jsonName)) { + return fromReflectionName(jsonName); + } + return fromTypeName(jsonName); + } + + public static NamedConfigurationTypeDescriptor fromTypeName(String typeName) { + Objects.requireNonNull(typeName); + return new NamedConfigurationTypeDescriptor(typeName); + } + + public static NamedConfigurationTypeDescriptor fromReflectionName(String reflectionName) { + return fromTypeName(ClassNameSupport.reflectionNameToTypeName(reflectionName)); + } + + public static NamedConfigurationTypeDescriptor fromJNIName(String jniName) { + return fromTypeName(ClassNameSupport.jniNameToTypeName(jniName)); } @Override @@ -60,6 +77,22 @@ public int compareTo(ConfigurationTypeDescriptor other) { } } + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof NamedConfigurationTypeDescriptor that)) { + return false; + } + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } + @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/ProxyConfigurationTypeDescriptor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ProxyConfigurationTypeDescriptor.java index 2270167bdbad..7e3078710232 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ProxyConfigurationTypeDescriptor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ProxyConfigurationTypeDescriptor.java @@ -28,14 +28,21 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import jdk.graal.compiler.util.json.JsonPrinter; import jdk.graal.compiler.util.json.JsonWriter; public record ProxyConfigurationTypeDescriptor(List interfaceNames) implements ConfigurationTypeDescriptor { - public ProxyConfigurationTypeDescriptor(List interfaceNames) { - this.interfaceNames = interfaceNames.stream().map(ConfigurationTypeDescriptor::checkQualifiedJavaName).toList(); + public static ProxyConfigurationTypeDescriptor fromInterfaceTypeNames(List interfaceTypeNames) { + Objects.requireNonNull(interfaceTypeNames); + return new ProxyConfigurationTypeDescriptor(interfaceTypeNames); + } + + public static ProxyConfigurationTypeDescriptor fromInterfaceReflectionNames(List interfaceReflectionNames) { + return fromInterfaceTypeNames(interfaceReflectionNames.stream().map(ClassNameSupport::reflectionNameToTypeName).collect(Collectors.toList())); } @Override diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionConfigurationParser.java index b64a960f4d18..bfca434f7f11 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionConfigurationParser.java @@ -162,7 +162,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(), new NamedConfigurationTypeDescriptor(typeName), true); + TypeResult typeResult = delegate.resolveType(conditionResolver.alwaysTrue(), NamedConfigurationTypeDescriptor.fromJSONName(typeName), true); 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.configure/src/com/oracle/svm/configure/SerializationConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/SerializationConfigurationParser.java index 11bfb62b878f..cf9ba53e4301 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/SerializationConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/SerializationConfigurationParser.java @@ -33,8 +33,6 @@ import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; -import jdk.graal.compiler.util.json.JsonParserException; - public abstract class SerializationConfigurationParser extends ConditionalConfigurationParser { public static final String CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY = "customTargetConstructorClass"; @@ -67,12 +65,9 @@ protected void parseSerializationTypes(List listOfSerializationTypes, bo protected abstract void parseSerializationDescriptorObject(EconomicMap data, boolean lambdaCapturingType); protected void registerType(ConfigurationTypeDescriptor targetSerializationClass, C condition) { - if (targetSerializationClass instanceof NamedConfigurationTypeDescriptor namedClass) { - serializationSupport.register(condition, namedClass.name()); - } else if (targetSerializationClass instanceof ProxyConfigurationTypeDescriptor proxyClass) { - serializationSupport.registerProxyClass(condition, proxyClass.interfaceNames()); - } else { - throw new JsonParserException("Unknown configuration type descriptor: %s".formatted(targetSerializationClass.toString())); + switch (targetSerializationClass.getDescriptorType()) { + case NAMED -> serializationSupport.register(condition, ((NamedConfigurationTypeDescriptor) targetSerializationClass).name()); + case PROXY -> serializationSupport.registerProxyClass(condition, ((ProxyConfigurationTypeDescriptor) targetSerializationClass).interfaceNames()); } } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java index 589bda936f15..f31a57b932f0 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java @@ -49,26 +49,27 @@ * the classpath. */ public final class UnresolvedConfigurationCondition implements Comparable { - private static final UnresolvedConfigurationCondition JAVA_LANG_OBJECT_REACHED = new UnresolvedConfigurationCondition(Object.class.getTypeName(), true); + private static final UnresolvedConfigurationCondition JAVA_LANG_OBJECT_REACHED = new UnresolvedConfigurationCondition( + NamedConfigurationTypeDescriptor.fromTypeName(Object.class.getTypeName()), true); public static final String TYPE_REACHED_KEY = "typeReached"; public static final String TYPE_REACHABLE_KEY = "typeReachable"; - private final String typeName; + private final NamedConfigurationTypeDescriptor type; private final boolean runtimeChecked; - public static UnresolvedConfigurationCondition create(String typeName) { - return create(typeName, true); + public static UnresolvedConfigurationCondition create(NamedConfigurationTypeDescriptor type) { + return create(type, true); } - public static UnresolvedConfigurationCondition create(String typeName, boolean runtimeChecked) { - Objects.requireNonNull(typeName); - if (JAVA_LANG_OBJECT_REACHED.getTypeName().equals(typeName)) { + public static UnresolvedConfigurationCondition create(NamedConfigurationTypeDescriptor type, boolean runtimeChecked) { + Objects.requireNonNull(type); + if (JAVA_LANG_OBJECT_REACHED.getTypeName().equals(type.name())) { return JAVA_LANG_OBJECT_REACHED; } - return new UnresolvedConfigurationCondition(typeName, runtimeChecked); + return new UnresolvedConfigurationCondition(type, runtimeChecked); } - private UnresolvedConfigurationCondition(String typeName, boolean runtimeChecked) { - this.typeName = typeName; + private UnresolvedConfigurationCondition(NamedConfigurationTypeDescriptor type, boolean runtimeChecked) { + this.type = type; this.runtimeChecked = runtimeChecked; } @@ -77,7 +78,7 @@ public static UnresolvedConfigurationCondition alwaysTrue() { } public String getTypeName() { - return typeName; + return type.name(); } public boolean isRuntimeChecked() { @@ -85,7 +86,7 @@ public boolean isRuntimeChecked() { } public boolean isAlwaysTrue() { - return typeName.equals(JAVA_LANG_OBJECT_REACHED.getTypeName()); + return getTypeName().equals(JAVA_LANG_OBJECT_REACHED.getTypeName()); } @Override @@ -97,12 +98,12 @@ public boolean equals(Object o) { return false; } UnresolvedConfigurationCondition that = (UnresolvedConfigurationCondition) o; - return runtimeChecked == that.runtimeChecked && Objects.equals(typeName, that.typeName); + return runtimeChecked == that.runtimeChecked && Objects.equals(type, that.type); } @Override public int hashCode() { - return Objects.hash(typeName, runtimeChecked); + return Objects.hash(type, runtimeChecked); } @Override @@ -111,13 +112,13 @@ public int compareTo(UnresolvedConfigurationCondition o) { if (res != 0) { return res; } - return typeName.compareTo(o.typeName); + return type.compareTo(o.type); } @Override public String toString() { var field = runtimeChecked ? TYPE_REACHED_KEY : TYPE_REACHABLE_KEY; - return "[" + field + ": \"" + typeName + "\"" + "]"; + return "[" + field + ": \"" + getTypeName() + "\"" + "]"; } } 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 72eba03be80e..2439dc3488aa 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 @@ -28,6 +28,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; @@ -103,8 +104,8 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType private boolean serializable = false; public ConfigurationType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean includeAllElements) { - this.condition = condition; - this.typeDescriptor = typeDescriptor; + this.condition = Objects.requireNonNull(condition); + this.typeDescriptor = Objects.requireNonNull(typeDescriptor); allDeclaredClasses = allPublicClasses = allRecordComponents = allPermittedSubclasses = allNestMembers = allSigners = includeAllElements; allDeclaredFieldsAccess = allPublicFieldsAccess = allDeclaredMethodsAccess = allPublicMethodsAccess = allDeclaredConstructorsAccess = allPublicConstructorsAccess = includeAllElements ? ConfigurationMemberAccessibility.QUERIED 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 55c0f4e504d4..c8dc5ec6298a 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 @@ -38,7 +38,6 @@ import com.oracle.svm.configure.ConfigurationParser; import com.oracle.svm.configure.ConfigurationParserOption; import com.oracle.svm.configure.ConfigurationTypeDescriptor; -import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.configure.ReflectionConfigurationParser; import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; @@ -123,10 +122,6 @@ public void addOrMerge(ConfigurationType type) { }); } - public ConfigurationType getOrCreateType(UnresolvedConfigurationCondition condition, String qualifiedForNameString) { - return getOrCreateType(condition, new NamedConfigurationTypeDescriptor(qualifiedForNameString)); - } - public ConfigurationType getOrCreateType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor) { return types.computeIfAbsent(new ConditionalElement<>(condition, typeDescriptor), p -> new ConfigurationType(p.condition(), p.element(), true)); } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationComputer.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationComputer.java index 87b524c06689..4c0a45344343 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationComputer.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/ConditionalConfigurationComputer.java @@ -209,7 +209,7 @@ private ConfigurationSet inferConditionalConfiguration(Map node.configuration.equals(configurationToAdd)) : "The "; for (MethodCallNode node : list) { String className = node.methodInfo.getJavaDeclaringClassName(); - UnresolvedConfigurationCondition condition = UnresolvedConfigurationCondition.create(className); + UnresolvedConfigurationCondition condition = UnresolvedConfigurationCondition.create(NamedConfigurationTypeDescriptor.fromJSONName(className)); var resolvedCondition = ConfigurationConditionResolver.identityResolver().resolveCondition(condition); addConfigurationWithCondition(configurationSet, node.configuration, resolvedCondition.get()); } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java index f29b8e980a7a..1a3739b0ab5c 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java @@ -29,8 +29,12 @@ import java.util.List; +import com.oracle.svm.configure.config.ConfigurationType; import org.graalvm.collections.EconomicMap; +import com.oracle.svm.configure.ClassNameSupport; +import com.oracle.svm.configure.ConfigurationTypeDescriptor; +import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration; import com.oracle.svm.configure.config.ConfigurationMethod; @@ -39,7 +43,6 @@ import com.oracle.svm.util.LogUtils; import jdk.graal.compiler.phases.common.LazyValue; -import jdk.vm.ci.meta.MetaUtil; class JniProcessor extends AbstractProcessor { private final AccessAdvisor advisor; @@ -62,28 +65,29 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat LazyValue callerClassLazyValue = lazyValue(callerClass); // Special: FindClass and DefineClass take the class in question as a string argument if (function.equals("FindClass") || function.equals("DefineClass")) { - String lookupName = singleElement(args); - String internalName = (lookupName.charAt(0) != '[') ? ('L' + lookupName + ';') : lookupName; - String forNameString = MetaUtil.internalNameToJava(internalName, true, true); - LazyValue forNameStringLazyValue = lazyValue(forNameString); - if (!advisor.shouldIgnore(forNameStringLazyValue, callerClassLazyValue, entry)) { + String jniName = singleElement(args); + ConfigurationTypeDescriptor type = NamedConfigurationTypeDescriptor.fromJNIName(jniName); + LazyValue reflectionName = lazyValue(ClassNameSupport.jniNameToReflectionName(jniName)); + if (!advisor.shouldIgnore(reflectionName, callerClassLazyValue, entry)) { if (function.equals("FindClass")) { - if (!advisor.shouldIgnoreJniLookup(function, forNameStringLazyValue, lazyNull(), lazyNull(), callerClassLazyValue, entry)) { - configurationSet.getJniConfiguration().getOrCreateType(condition, forNameString); + if (!advisor.shouldIgnoreJniLookup(function, reflectionName, lazyNull(), lazyNull(), callerClassLazyValue, entry)) { + configurationSet.getJniConfiguration().getOrCreateType(condition, type); } - } else if (!AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(lookupName).matches()) { // DefineClass - LogUtils.warning("Unsupported JNI function DefineClass used to load class " + forNameString); + } else if (!AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(jniName).matches()) { // DefineClass + LogUtils.warning("Unsupported JNI function DefineClass used to load class " + jniName); } } return; } - String clazz = (String) entry.get("class"); - if (advisor.shouldIgnore(lazyValue(clazz), callerClassLazyValue, entry)) { + String className = (String) entry.get("class"); + ConfigurationTypeDescriptor clazz = NamedConfigurationTypeDescriptor.fromReflectionName(className); + if (advisor.shouldIgnore(lazyValue(className), callerClassLazyValue, entry)) { return; } - String declaringClass = (String) entry.get("declaring_class"); - String declaringClassOrClazz = (declaringClass != null) ? declaringClass : clazz; - ConfigurationMemberDeclaration declaration = (declaringClass != null) ? ConfigurationMemberDeclaration.DECLARED : ConfigurationMemberDeclaration.PRESENT; + boolean hasDeclaringClass = entry.containsKey("declaring_class"); + ConfigurationTypeDescriptor declaringClass = hasDeclaringClass ? NamedConfigurationTypeDescriptor.fromReflectionName((String) entry.get("declaring_class")) : null; + ConfigurationTypeDescriptor declaringClassOrClazz = hasDeclaringClass ? declaringClass : clazz; + ConfigurationMemberDeclaration declaration = hasDeclaringClass ? ConfigurationMemberDeclaration.DECLARED : ConfigurationMemberDeclaration.PRESENT; TypeConfiguration config = configurationSet.getJniConfiguration(); switch (function) { case "AllocObject": @@ -99,7 +103,7 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat expectSize(args, 2); String name = (String) args.get(0); String signature = (String) args.get(1); - if (!advisor.shouldIgnoreJniLookup(function, lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue, entry)) { + if (!advisor.shouldIgnoreJniLookup(function, lazyValue(className), lazyValue(name), lazyValue(signature), callerClassLazyValue, entry)) { config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration); if (!declaringClassOrClazz.equals(clazz)) { config.getOrCreateType(condition, clazz); @@ -112,7 +116,7 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat expectSize(args, 2); String name = (String) args.get(0); String signature = (String) args.get(1); - if (!advisor.shouldIgnoreJniLookup(function, lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue, entry)) { + if (!advisor.shouldIgnoreJniLookup(function, lazyValue(className), lazyValue(name), lazyValue(signature), callerClassLazyValue, entry)) { config.getOrCreateType(condition, declaringClassOrClazz).addField(name, declaration, false); if (!declaringClassOrClazz.equals(clazz)) { config.getOrCreateType(condition, clazz); @@ -124,7 +128,7 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat expectSize(args, 1); // exception message, ignore String name = ConfigurationMethod.CONSTRUCTOR_NAME; String signature = "(Ljava/lang/String;)V"; - if (!advisor.shouldIgnoreJniLookup(function, lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue, entry)) { + if (!advisor.shouldIgnoreJniLookup(function, lazyValue(className), lazyValue(name), lazyValue(signature), callerClassLazyValue, entry)) { config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration); assert declaringClassOrClazz.equals(clazz) : "Constructor can only be accessed via declaring class"; } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java index da8f0891dfcd..3b5af48d334e 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java @@ -102,22 +102,24 @@ public void processEntry(EconomicMap entry, ConfigurationSet con } if (!advisor.shouldIgnore(lazyValue(name), lazyValue(callerClass), entry) && !(isLoadClass && advisor.shouldIgnoreLoadClass(lazyValue(name), lazyValue(callerClass), entry))) { - configuration.getOrCreateType(condition, name); + configuration.getOrCreateType(condition, NamedConfigurationTypeDescriptor.fromReflectionName(name)); } return; } else if (function.equals("methodTypeDescriptor")) { List typeNames = singleElement(args); for (String type : typeNames) { if (!advisor.shouldIgnore(lazyValue(type), lazyValue(callerClass), copyWithUniqueEntry(entry, "ignoredDescriptorType", type))) { - configuration.getOrCreateType(condition, type); + configuration.getOrCreateType(condition, NamedConfigurationTypeDescriptor.fromReflectionName(type)); } } return; } ConfigurationTypeDescriptor clazz = descriptorForClass(entry.get("class")); - for (String className : clazz.getAllQualifiedJavaNames()) { - if (advisor.shouldIgnore(lazyValue(className), lazyValue(callerClass), copyWithUniqueEntry(entry, "ignoredClassName", className))) { - return; + if (clazz != null) { + for (String className : clazz.getAllQualifiedJavaNames()) { + if (advisor.shouldIgnore(lazyValue(className), lazyValue(callerClass), copyWithUniqueEntry(entry, "ignoredClassName", className))) { + return; + } } } ConfigurationMemberDeclaration declaration = ConfigurationMemberDeclaration.PUBLIC; @@ -265,7 +267,7 @@ public void processEntry(EconomicMap entry, ConfigurationSet con case "newInstance": { if (clazz.toString().equals("java.lang.reflect.Array")) { // reflective array // instantiation - configuration.getOrCreateType(condition, new NamedConfigurationTypeDescriptor((String) args.get(0))); + configuration.getOrCreateType(condition, descriptorForClass(args.get(0))); } else { configuration.getOrCreateType(condition, clazz).addMethod(ConfigurationMethod.CONSTRUCTOR_NAME, "()V", ConfigurationMemberDeclaration.DECLARED, ConfigurationMemberAccessibility.ACCESSED); @@ -293,10 +295,12 @@ public void processEntry(EconomicMap entry, ConfigurationSet con @SuppressWarnings("unchecked") private static ConfigurationTypeDescriptor descriptorForClass(Object clazz) { - if (clazz instanceof List) { - return new ProxyConfigurationTypeDescriptor((List) clazz); + if (clazz == null) { + return null; + } else if (clazz instanceof List) { + return ProxyConfigurationTypeDescriptor.fromInterfaceReflectionNames(((List) clazz)); } else { - return new NamedConfigurationTypeDescriptor((String) clazz); + return NamedConfigurationTypeDescriptor.fromReflectionName((String) clazz); } } @@ -306,7 +310,8 @@ private static void addFullyQualifiedDeclaredMethod(String descriptor, TypeConfi String qualifiedClass = descriptor.substring(0, classend); String methodName = descriptor.substring(classend + 1, sigbegin); String signature = descriptor.substring(sigbegin); - configuration.getOrCreateType(UnresolvedConfigurationCondition.alwaysTrue(), qualifiedClass).addMethod(methodName, signature, ConfigurationMemberDeclaration.DECLARED); + configuration.getOrCreateType(UnresolvedConfigurationCondition.alwaysTrue(), NamedConfigurationTypeDescriptor.fromReflectionName(qualifiedClass)) + .addMethod(methodName, signature, ConfigurationMemberDeclaration.DECLARED); } private void addDynamicProxy(List interfaceList, LazyValue callerClass, TypeConfiguration configuration, EconomicMap entry) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java index cfca9cf0715f..935ff334abf5 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java @@ -31,6 +31,7 @@ import org.graalvm.collections.EconomicMap; +import com.oracle.svm.configure.ClassNameSupport; import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationSet; @@ -66,12 +67,12 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat return; } - String className = (String) args.get(0); + String reflectionName = (String) args.get(0); - if (className.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) { - serializationConfiguration.registerLambdaCapturingClass(condition, className); + if (reflectionName.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) { + serializationConfiguration.registerLambdaCapturingClass(condition, reflectionName); } else { - reflectionConfiguration.getOrCreateType(condition, new NamedConfigurationTypeDescriptor(className)).setSerializable(); + reflectionConfiguration.getOrCreateType(condition, NamedConfigurationTypeDescriptor.fromReflectionName(reflectionName)).setSerializable(); } } else if ("SerializedLambda.readResolve".equals(function)) { expectSize(args, 1); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java index a7818355e12e..c5efadad75ae 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java @@ -36,6 +36,7 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.impl.ConfigurationCondition; +import com.oracle.svm.configure.ClassNameSupport; import com.oracle.svm.core.configure.ConditionalRuntimeValue; import com.oracle.svm.core.configure.RuntimeConditionSet; import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; @@ -237,14 +238,28 @@ private static Class forName(String className, ClassLoader classLoader, boole private Object forName0(String className, ClassLoader classLoader) { var conditional = knownClasses.get(className); Object result = conditional == null ? null : conditional.getValue(); - if (result == NEGATIVE_QUERY || className.endsWith("[]")) { + if (className.endsWith("[]")) { /* Querying array classes with their "TypeName[]" name always throws */ - result = new ClassNotFoundException(className); + result = NEGATIVE_QUERY; } if (result == null) { result = PredefinedClassesSupport.getLoadedForNameOrNull(className, classLoader); } - return result; + if (result == null && !ClassNameSupport.isValidReflectionName(className)) { + if (result == null && ClassNameSupport.isValidJNIName(className)) { + var jniAlias = knownClasses.get(ClassNameSupport.jniNameToReflectionName(className)); + if (jniAlias != null && jniAlias.getValue() != null) { + result = NEGATIVE_QUERY; + } + } + if (result == null && ClassNameSupport.isValidTypeName(className)) { + var typeAlias = knownClasses.get(ClassNameSupport.typeNameToReflectionName(className)); + if (typeAlias != null && typeAlias.getValue() != null) { + result = NEGATIVE_QUERY; + } + } + } + return result == NEGATIVE_QUERY ? new ClassNotFoundException(className) : result; } public int count() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleClass.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleClass.java index a7fa6b1b3a94..49bbdfad7dfb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleClass.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIAccessibleClass.java @@ -32,11 +32,10 @@ import org.graalvm.nativeimage.Platform.HOSTED_ONLY; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.configure.ClassNameSupport; import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.core.util.VMError; -import jdk.vm.ci.meta.MetaUtil; - /** * Information on a class that can be looked up and accessed via JNI. */ @@ -117,7 +116,7 @@ public JNIAccessibleMethod getMethod(JNIAccessibleMethodDescriptor descriptor) { return method; } - String getInternalName() { - return MetaUtil.toInternalName(classObject.getName()); + String getJNIName() { + return ClassNameSupport.reflectionNameToJNIName(classObject.getName()); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIReflectionDictionary.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIReflectionDictionary.java index 1600a5f7fdda..c81efd2b99f5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIReflectionDictionary.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jni/access/JNIReflectionDictionary.java @@ -41,6 +41,7 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; +import com.oracle.svm.configure.ClassNameSupport; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.heap.Heap; @@ -60,7 +61,6 @@ import jdk.graal.compiler.util.SignatureUtil; import jdk.graal.compiler.word.Word; import jdk.vm.ci.meta.JavaType; -import jdk.vm.ci.meta.MetaUtil; import jdk.vm.ci.meta.Signature; /** @@ -155,11 +155,7 @@ public JNIAccessibleClass addClassIfAbsent(Class classObj, Function, if (!classesByClassObject.containsKey(classObj)) { JNIAccessibleClass instance = mappingFunction.apply(classObj); classesByClassObject.put(classObj, instance); - String name = instance.getInternalName(); - if (name.charAt(0) == 'L') { // "Ljava/lang/Object;" -> "java/lang/Object" - assert name.charAt(name.length() - 1) == ';'; - name = name.substring(1, name.length() - 1); - } + String name = instance.getJNIName(); classesByName.put(name, instance); } return classesByClassObject.get(classObj); @@ -167,9 +163,7 @@ public JNIAccessibleClass addClassIfAbsent(Class classObj, Function, @Platforms(HOSTED_ONLY.class) public void addNegativeClassLookupIfAbsent(String typeName) { - String internalName = MetaUtil.toInternalName(typeName); - String queryName = internalName.startsWith("L") ? internalName.substring(1, internalName.length() - 1) : internalName; - classesByName.putIfAbsent(queryName, NEGATIVE_CLASS_LOOKUP); + classesByName.putIfAbsent(typeName, NEGATIVE_CLASS_LOOKUP); } @Platforms(HOSTED_ONLY.class) @@ -185,6 +179,14 @@ public Iterable getClasses() { public static Class getClassObjectByName(CharSequence name) { for (var dictionary : layeredSingletons()) { JNIAccessibleClass clazz = dictionary.classesByName.get(name); + if (clazz == null && !ClassNameSupport.isValidJNIName(name.toString())) { + if (clazz == null && ClassNameSupport.isValidReflectionName(name.toString()) && dictionary.classesByName.containsKey(ClassNameSupport.reflectionNameToJNIName(name.toString()))) { + clazz = NEGATIVE_CLASS_LOOKUP; + } + if (clazz == null && ClassNameSupport.isValidTypeName(name.toString()) && dictionary.classesByName.containsKey(ClassNameSupport.typeNameToJNIName(name.toString()))) { + clazz = NEGATIVE_CLASS_LOOKUP; + } + } clazz = checkClass(clazz, name); if (clazz != null) { return clazz.getClassObject(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java index f0679425fc82..e94f1c3c145b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java @@ -31,6 +31,7 @@ import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; +import com.oracle.svm.configure.ClassNameSupport; import com.oracle.svm.configure.ConfigurationTypeDescriptor; import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.hosted.ImageClassLoader; @@ -65,7 +66,8 @@ public TypeResult> resolveType(ConfigurationCondition condition, Config if (!result.isPresent() && typeDescriptor instanceof NamedConfigurationTypeDescriptor namedDescriptor) { Throwable classLookupException = result.getException(); if (classLookupException instanceof LinkageError) { - reflectionSupport.registerClassLookupException(condition, namedDescriptor.name(), classLookupException); + String reflectionName = ClassNameSupport.typeNameToReflectionName(namedDescriptor.name()); + reflectionSupport.registerClassLookupException(condition, reflectionName, classLookupException); } } return result; 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 abf03ac5e410..c07dbadf7a37 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 @@ -38,6 +38,7 @@ import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; +import com.oracle.svm.configure.ClassNameSupport; import com.oracle.svm.configure.ConfigurationTypeDescriptor; import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.configure.ProxyConfigurationTypeDescriptor; @@ -76,11 +77,11 @@ public void registerType(ConfigurationCondition condition, Class type) { public TypeResult> resolveType(ConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean allowPrimitives) { switch (typeDescriptor.getDescriptorType()) { case NAMED -> { - NamedConfigurationTypeDescriptor namedDescriptor = (NamedConfigurationTypeDescriptor) typeDescriptor; - TypeResult> result = resolveNamedType(namedDescriptor, allowPrimitives); + String reflectionName = ClassNameSupport.typeNameToReflectionName(((NamedConfigurationTypeDescriptor) typeDescriptor).name()); + TypeResult> result = resolveNamedType(reflectionName, allowPrimitives); if (!result.isPresent()) { if (throwMissingRegistrationErrors() && result.getException() instanceof ClassNotFoundException) { - registry.registerClassLookup(condition, namedDescriptor.name()); + registry.registerClassLookup(condition, reflectionName); } } return result; @@ -94,8 +95,8 @@ public TypeResult> resolveType(ConfigurationCondition condition, Config } } - private TypeResult> resolveNamedType(NamedConfigurationTypeDescriptor typeDescriptor, boolean allowPrimitives) { - TypeResult> result = classLoader.findClass(typeDescriptor.name(), allowPrimitives); + private TypeResult> resolveNamedType(String reflectionName, boolean allowPrimitives) { + TypeResult> result = classLoader.findClass(reflectionName, allowPrimitives); if (!result.isPresent() && result.getException() instanceof NoClassDefFoundError) { /* * In certain cases when the class name is identical to an existing class name except @@ -103,9 +104,9 @@ private TypeResult> resolveNamedType(NamedConfigurationTypeDescriptor t * `Class.forName` throws a `ClassNotFoundException`. */ try { - Class.forName(typeDescriptor.name()); + Class.forName(reflectionName); } catch (ClassNotFoundException notFoundException) { - result = TypeResult.forException(typeDescriptor.name(), notFoundException); + result = TypeResult.forException(reflectionName, notFoundException); } catch (Throwable t) { // ignore } @@ -115,7 +116,8 @@ private TypeResult> resolveNamedType(NamedConfigurationTypeDescriptor t private TypeResult> resolveProxyType(ProxyConfigurationTypeDescriptor typeDescriptor) { String typeName = typeDescriptor.toString(); - List>> interfaceResults = typeDescriptor.interfaceNames().stream().map(name -> resolveNamedType(new NamedConfigurationTypeDescriptor(name), false)).toList(); + List>> interfaceResults = typeDescriptor.interfaceNames().stream() + .map(interfaceTypeName -> resolveNamedType(ClassNameSupport.typeNameToReflectionName(interfaceTypeName), false)).toList(); List> interfaces = new ArrayList<>(); for (TypeResult> intf : interfaceResults) { if (!intf.isPresent()) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java index ac4291cdef5f..105f0e7e159f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java @@ -62,6 +62,7 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.svm.configure.ClassNameSupport; import com.oracle.svm.configure.ConfigurationFile; import com.oracle.svm.configure.ReflectionConfigurationParser; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; @@ -256,11 +257,11 @@ private void registerFields(boolean finalIsWritable, Field[] fields) { } @Override - public void registerClassLookup(ConfigurationCondition condition, String typeName) { + public void registerClassLookup(ConfigurationCondition condition, String jniName) { try { - register(condition, false, Class.forName(typeName)); + register(condition, false, Class.forName(ClassNameSupport.jniNameToReflectionName(jniName))); } catch (ClassNotFoundException e) { - newNegativeClassLookups.add(typeName); + newNegativeClassLookups.add(jniName); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/NativeImageConditionResolver.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/NativeImageConditionResolver.java index f132171c2934..14288fb74c68 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/NativeImageConditionResolver.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/NativeImageConditionResolver.java @@ -26,7 +26,7 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; -import com.oracle.svm.configure.ConfigurationTypeDescriptor; +import com.oracle.svm.configure.ClassNameSupport; import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.hosted.ImageClassLoader; @@ -44,8 +44,8 @@ public NativeImageConditionResolver(ImageClassLoader classLoader, ClassInitializ @Override public TypeResult resolveCondition(UnresolvedConfigurationCondition unresolvedCondition) { - String canonicalizedName = ConfigurationTypeDescriptor.canonicalizeTypeName(unresolvedCondition.getTypeName()); - TypeResult> clazz = classLoader.findClass(canonicalizedName); + String reflectionName = ClassNameSupport.typeNameToReflectionName(unresolvedCondition.getTypeName()); + TypeResult> clazz = classLoader.findClass(reflectionName); return clazz.map(type -> { /* * We don't want to track always reached types: we convert them into build-time From b66c3e80f560d8acd9830a54dd04fea0c91a0377 Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Tue, 11 Mar 2025 11:48:56 +0100 Subject: [PATCH 3/3] Include JNI reachability metadata with reflection --- .../native-image/ReachabilityMetadata.md | 18 ++- .../reachability-metadata-schema-v1.1.0.json | 5 + substratevm/CHANGELOG.md | 1 + .../test/config/OmitPreviousConfigTests.java | 7 +- .../ConditionalConfigurationParser.java | 2 +- .../svm/configure/ConfigurationFile.java | 2 +- .../configure/ConfigurationParserOption.java | 7 +- .../LegacyReflectionConfigurationParser.java | 31 ++-- .../ReflectionConfigurationParser.java | 37 +++-- ...ReflectionConfigurationParserDelegate.java | 24 +-- .../configure/ReflectionMetadataParser.java | 59 ++++--- .../UnresolvedConfigurationCondition.java | 50 ++---- .../config/ConfigurationFileCollection.java | 25 ++- .../configure/config/ConfigurationSet.java | 26 +-- .../configure/config/ConfigurationType.java | 9 +- .../config/ParserConfigurationAdapter.java | 62 +++++-- .../configure/config/TypeConfiguration.java | 23 +-- .../PartialConfigurationWithOrigins.java | 8 +- .../svm/configure/trace/AccessAdvisor.java | 3 +- .../svm/configure/trace/JniProcessor.java | 40 +++-- .../trace/SerializationProcessor.java | 1 - .../config/ConfigurationParserUtils.java | 17 +- .../svm/hosted/config/JNIRegistryAdapter.java | 151 ++++++++++++++++++ .../config/ReflectionRegistryAdapter.java | 109 +++++++++++-- .../svm/hosted/config/RegistryAdapter.java | 85 ++++++---- .../svm/hosted/jni/JNIAccessFeature.java | 16 +- .../svm/hosted/reflect/ReflectionFeature.java | 16 +- .../svm/hosted/webimage/WebImageFeature.java | 5 +- 28 files changed, 578 insertions(+), 261 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/JNIRegistryAdapter.java diff --git a/docs/reference-manual/native-image/ReachabilityMetadata.md b/docs/reference-manual/native-image/ReachabilityMetadata.md index 6c38282498a0..d40cf3c65ac9 100644 --- a/docs/reference-manual/native-image/ReachabilityMetadata.md +++ b/docs/reference-manual/native-image/ReachabilityMetadata.md @@ -131,8 +131,7 @@ The _reachability-metadata.json_ configuration contains a single object with one { "reflection":[], "resources":[], - "bundles":[], - "jni":[] + "bundles":[] } ``` @@ -376,12 +375,14 @@ jclass clazz = FindClass(env, "jni/accessed/Type"); ``` looks up the `jni.accessed.Type` class, which can then be used to instantiate `jni.accessed.Type`, invoke its methods or access its fields. -The metadata entry for the above call can *only* be provided via _reachability-metadata.json_. Specify the `type` entry in the `jni` field: +The metadata entry for the above call can *only* be provided via _reachability-metadata.json_. Specify +the `jniAccessible` field in the `type` entry in the `reflection` section: ```json { - "jni":[ + "reflection": [ { - "type": "jni.accessed.Type" + "type": "jni.accessed.Type", + "jniAccessibleType": true } ] } @@ -393,6 +394,7 @@ To access field values, we need to provide field names: ```json { "type": "jni.accessed.Type", + "jniAccessible": true, "fields": [{"name": "value"}] } ``` @@ -400,6 +402,7 @@ To access all fields one can use the following attributes: ```json { "type": "jni.accessed.Type", + "jniAccessible": true, "allDeclaredFields": true, "allPublicFields": true } @@ -410,6 +413,7 @@ To call Java methods from JNI, we must provide metadata for the method signature ```json { "type": "jni.accessed.Type", + "jniAccessible": true, "methods": [ {"name": "", "parameterTypes": ["", "", ""]}, {"name": "", "parameterTypes": ["", "", ""]} @@ -420,6 +424,7 @@ As a convenience, one can allow method invocation for groups of methods by addin ```json { "type": "jni.accessed.Type", + "jniAccessible": true, "allDeclaredConstructors": true, "allPublicConstructors": true, "allDeclaredMethods": true, @@ -429,7 +434,8 @@ As a convenience, one can allow method invocation for groups of methods by addin `allDeclaredConstructors` and `allDeclaredMethods` allow calls invocations of methods declared on a given type. `allPublicConstructors` and `allPublicMethods` allow invocations of all public methods defined on a type and all of its supertypes. -To allocate objects of a type with `AllocObject`, the metadata must be stored in the `reflection` section: +To allocate objects of a type with `AllocObject`, the `unsafeAllocated` field must be set, but the `jniAccessible` field +is not required: ```json { "reflection": [ diff --git a/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json b/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json index 065247bf3a09..327b34bca255 100644 --- a/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json +++ b/docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json @@ -207,6 +207,11 @@ "title": "Allow objects of this class to be serialized and deserialized", "type": "boolean", "default": false + }, + "jniAccessible": { + "title": "Register the type, including all registered fields and methods, for runtime JNI access", + "type": "boolean", + "default": false } }, "additionalProperties": false diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index f56dc8607403..71de48943e9e 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -18,6 +18,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-63494) Recurring callback support is no longer enabled by default. If this feature is needed, please specify `-H:+SupportRecurringCallback` at image build-time. * (GR-60209) New syntax for configuration of the [Foreign Function & Memory API](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/ForeignInterface.md) * (GR-64584) Experimental option `-H:+RelativeCodePointers` to significantly reduce relocation entries in position-independent executables and shared libraries. +* (GR-60238) JNI registration is now included as part of the `"reflection"` section of _reachability-metadata.json_ using the `"jniAccessible"` attribute. Registrations performed through the `"jni"` section of _reachability-metadata.json_ and through _jni-config.json_ will still be accepted and parsed correctly. ## GraalVM for JDK 24 (Internal Version 24.2.0) * (GR-59717) Added `DuringSetupAccess.registerObjectReachabilityHandler` to allow registering a callback that is executed when an object of a specified type is marked as reachable during heap scanning. 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 b21b261a7efd..f65d8d273616 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 @@ -97,7 +97,6 @@ public void testSameConfig() { ConfigurationSet config = loadTraceProcessorFromResourceDirectory(PREVIOUS_CONFIG_DIR_NAME, omittedConfig); config = config.copyAndSubtract(omittedConfig); - assertTrue(config.getJniConfiguration().isEmpty()); assertTrue(config.getReflectionConfiguration().isEmpty()); assertTrue(config.getProxyConfiguration().isEmpty()); assertTrue(config.getResourceConfiguration().isEmpty()); @@ -112,7 +111,7 @@ public void testConfigDifference() { config = config.copyAndSubtract(omittedConfig); doTestGeneratedTypeConfig(); - doTestTypeConfig(config.getJniConfiguration()); + doTestTypeConfig(config.getReflectionConfiguration()); doTestProxyConfig(config.getProxyConfiguration()); @@ -242,8 +241,8 @@ class TypeMethodsWithFlagsTest { final Map methodsThatMustExist = new HashMap<>(); final Map methodsThatMustNotExist = new HashMap<>(); - final TypeConfiguration previousConfig = new TypeConfiguration(""); - final TypeConfiguration currentConfig = new TypeConfiguration(""); + final TypeConfiguration previousConfig = new TypeConfiguration(); + final TypeConfiguration currentConfig = new TypeConfiguration(); TypeMethodsWithFlagsTest(ConfigurationMemberDeclaration methodKind) { this.methodKind = methodKind; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java index 54c9fd6fca3c..0001421597d0 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConditionalConfigurationParser.java @@ -78,7 +78,7 @@ protected UnresolvedConfigurationCondition parseCondition(EconomicMap data) { T clazz = result.get(); delegate.registerType(conditionResult.get(), clazz); - registerIfNotDefault(data, false, clazz, "allDeclaredConstructors", () -> delegate.registerDeclaredConstructors(condition, false, clazz)); - registerIfNotDefault(data, false, clazz, "allPublicConstructors", () -> delegate.registerPublicConstructors(condition, false, clazz)); - registerIfNotDefault(data, false, clazz, "allDeclaredMethods", () -> delegate.registerDeclaredMethods(condition, false, clazz)); - registerIfNotDefault(data, false, clazz, "allPublicMethods", () -> delegate.registerPublicMethods(condition, false, clazz)); - registerIfNotDefault(data, false, clazz, "allDeclaredFields", () -> delegate.registerDeclaredFields(condition, false, clazz)); - registerIfNotDefault(data, false, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, clazz)); + boolean jniAccessible = checkOption(ConfigurationParserOption.JNI_PARSER); + registerIfNotDefault(data, false, clazz, "allDeclaredConstructors", () -> delegate.registerDeclaredConstructors(condition, false, jniAccessible, clazz)); + registerIfNotDefault(data, false, clazz, "allPublicConstructors", () -> delegate.registerPublicConstructors(condition, false, jniAccessible, clazz)); + registerIfNotDefault(data, false, clazz, "allDeclaredMethods", () -> delegate.registerDeclaredMethods(condition, false, jniAccessible, clazz)); + registerIfNotDefault(data, false, clazz, "allPublicMethods", () -> delegate.registerPublicMethods(condition, false, jniAccessible, clazz)); + registerIfNotDefault(data, false, clazz, "allDeclaredFields", () -> delegate.registerDeclaredFields(condition, false, jniAccessible, clazz)); + registerIfNotDefault(data, false, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, jniAccessible, clazz)); registerIfNotDefault(data, isType, clazz, "allDeclaredClasses", () -> delegate.registerDeclaredClasses(queryCondition, clazz)); registerIfNotDefault(data, isType, clazz, "allRecordComponents", () -> delegate.registerRecordComponents(queryCondition, clazz)); registerIfNotDefault(data, isType, clazz, "allPermittedSubclasses", () -> delegate.registerPermittedSubclasses(queryCondition, clazz)); registerIfNotDefault(data, isType, clazz, "allNestMembers", () -> delegate.registerNestMembers(queryCondition, clazz)); registerIfNotDefault(data, isType, clazz, "allSigners", () -> delegate.registerSigners(queryCondition, clazz)); registerIfNotDefault(data, isType, clazz, "allPublicClasses", () -> delegate.registerPublicClasses(queryCondition, clazz)); - registerIfNotDefault(data, isType, clazz, "queryAllDeclaredConstructors", () -> delegate.registerDeclaredConstructors(queryCondition, true, clazz)); - registerIfNotDefault(data, isType, clazz, "queryAllPublicConstructors", () -> delegate.registerPublicConstructors(queryCondition, true, clazz)); - registerIfNotDefault(data, isType, clazz, "queryAllDeclaredMethods", () -> delegate.registerDeclaredMethods(queryCondition, true, clazz)); - registerIfNotDefault(data, isType, clazz, "queryAllPublicMethods", () -> delegate.registerPublicMethods(queryCondition, true, clazz)); + registerIfNotDefault(data, isType, clazz, "queryAllDeclaredConstructors", () -> delegate.registerDeclaredConstructors(queryCondition, true, jniAccessible, clazz)); + registerIfNotDefault(data, isType, clazz, "queryAllPublicConstructors", () -> delegate.registerPublicConstructors(queryCondition, true, jniAccessible, clazz)); + registerIfNotDefault(data, isType, clazz, "queryAllDeclaredMethods", () -> delegate.registerDeclaredMethods(queryCondition, true, jniAccessible, clazz)); + registerIfNotDefault(data, isType, clazz, "queryAllPublicMethods", () -> delegate.registerPublicMethods(queryCondition, true, jniAccessible, clazz)); if (isType) { /* * Fields cannot be registered as queried only by the user, we register them * unconditionally if the class is registered via "type". */ - delegate.registerDeclaredFields(queryCondition, true, clazz); - delegate.registerPublicFields(queryCondition, true, clazz); + delegate.registerDeclaredFields(queryCondition, true, jniAccessible, clazz); + delegate.registerPublicFields(queryCondition, true, jniAccessible, clazz); } registerIfNotDefault(data, false, clazz, "unsafeAllocated", () -> delegate.registerUnsafeAllocated(condition, clazz)); MapCursor cursor = data.getEntries(); @@ -129,13 +130,13 @@ protected void parseClass(EconomicMap data) { try { switch (name) { case "methods": - parseMethods(condition, false, asList(value, "Attribute 'methods' must be an array of method descriptors"), clazz); + parseMethods(condition, false, asList(value, "Attribute 'methods' must be an array of method descriptors"), clazz, jniAccessible); break; case "queriedMethods": - parseMethods(condition, true, asList(value, "Attribute 'queriedMethods' must be an array of method descriptors"), clazz); + parseMethods(condition, true, asList(value, "Attribute 'queriedMethods' must be an array of method descriptors"), clazz, jniAccessible); break; case "fields": - parseFields(condition, asList(value, "Attribute 'fields' must be an array of field descriptors"), clazz); + parseFields(condition, asList(value, "Attribute 'fields' must be an array of field descriptors"), clazz, jniAccessible); break; } } catch (LinkageError e) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionConfigurationParser.java index bfca434f7f11..c0410bc3b96c 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionConfigurationParser.java @@ -45,6 +45,7 @@ */ public abstract class ReflectionConfigurationParser extends ConditionalConfigurationParser { private static final String CONSTRUCTOR_NAME = ""; + private static final String PARAMETER_TYPES_KEY = "parameterTypes"; protected final ConfigurationConditionResolver conditionResolver; protected final ReflectionConfigurationParserDelegate delegate; @@ -59,14 +60,15 @@ public ReflectionConfigurationParser(ConfigurationConditionResolver condition protected EnumSet supportedOptions() { EnumSet base = super.supportedOptions(); base.add(ConfigurationParserOption.PRINT_MISSING_ELEMENTS); + base.add(ConfigurationParserOption.JNI_PARSER); return base; } - public static ReflectionConfigurationParser create(String combinedFileKey, boolean combinedFileSchema, + public static ReflectionConfigurationParser create(boolean combinedFileSchema, ConfigurationConditionResolver conditionResolver, ReflectionConfigurationParserDelegate delegate, EnumSet parserOptions) { if (combinedFileSchema) { - return new ReflectionMetadataParser<>(combinedFileKey, conditionResolver, delegate, parserOptions); + return new ReflectionMetadataParser<>(conditionResolver, delegate, parserOptions); } else { return new LegacyReflectionConfigurationParser<>(conditionResolver, delegate, parserOptions); } @@ -80,29 +82,31 @@ protected void parseClassArray(List classes) { protected abstract void parseClass(EconomicMap data); - protected void registerIfNotDefault(EconomicMap data, boolean defaultValue, T clazz, String propertyName, Runnable register) { + protected boolean registerIfNotDefault(EconomicMap data, boolean defaultValue, T clazz, String propertyName, Runnable register) { if (data.containsKey(propertyName) ? asBoolean(data.get(propertyName), propertyName) : defaultValue) { try { register.run(); + return true; } catch (LinkageError e) { handleMissingElement("Could not register " + delegate.getTypeName(clazz) + ": " + propertyName + " for reflection.", e); } } + return false; } - protected void parseFields(C condition, List fields, T clazz) { + protected void parseFields(C condition, List fields, T clazz, boolean jniAccessible) { for (Object field : fields) { - parseField(condition, asMap(field, "Elements of 'fields' array must be field descriptor objects"), clazz); + parseField(condition, asMap(field, "Elements of 'fields' array must be field descriptor objects"), clazz, jniAccessible); } } - private void parseField(C condition, EconomicMap data, T clazz) { + private void parseField(C condition, EconomicMap data, T clazz, boolean jniAccessible) { checkAttributes(data, "reflection field descriptor object", Collections.singleton("name"), Arrays.asList("allowWrite", "allowUnsafeAccess")); String fieldName = asString(data.get("name"), "name"); boolean allowWrite = data.containsKey("allowWrite") && asBoolean(data.get("allowWrite"), "allowWrite"); try { - delegate.registerField(condition, clazz, fieldName, allowWrite); + delegate.registerField(condition, clazz, fieldName, allowWrite, jniAccessible); } catch (NoSuchFieldException e) { handleMissingElement("Field " + formatField(clazz, fieldName) + " not found."); } catch (LinkageError e) { @@ -110,17 +114,16 @@ private void parseField(C condition, EconomicMap data, T clazz) } } - protected void parseMethods(C condition, boolean queriedOnly, List methods, T clazz) { + protected void parseMethods(C condition, boolean queriedOnly, List methods, T clazz, boolean jniAccessible) { for (Object method : methods) { - parseMethod(condition, queriedOnly, asMap(method, "Elements of 'methods' array must be method descriptor objects"), clazz); + parseMethod(condition, queriedOnly, asMap(method, "Elements of 'methods' array must be method descriptor objects"), clazz, jniAccessible); } } - private void parseMethod(C condition, boolean queriedOnly, EconomicMap data, T clazz) { - checkAttributes(data, "reflection method descriptor object", Collections.singleton("name"), Collections.singleton("parameterTypes")); - String methodName = asString(data.get("name"), "name"); + private void parseMethod(C condition, boolean queriedOnly, EconomicMap data, T clazz, boolean jniAccessible) { + String methodName = asString(data.get(NAME_KEY), NAME_KEY); List methodParameterTypes = null; - Object parameterTypes = data.get("parameterTypes"); + Object parameterTypes = data.get(PARAMETER_TYPES_KEY); if (parameterTypes != null) { methodParameterTypes = parseMethodParameters(clazz, methodName, asList(parameterTypes, "Attribute 'parameterTypes' must be a list of type names")); if (methodParameterTypes == null) { @@ -132,9 +135,9 @@ private void parseMethod(C condition, boolean queriedOnly, EconomicMap { void registerSigners(C condition, T type); - void registerPublicFields(C condition, boolean queriedOnly, T type); + void registerPublicFields(C condition, boolean queriedOnly, boolean jniAccessible, T type); - void registerDeclaredFields(C condition, boolean queriedOnly, T type); + void registerDeclaredFields(C condition, boolean queriedOnly, boolean jniAccessible, T type); - void registerPublicMethods(C condition, boolean queriedOnly, T type); + void registerPublicMethods(C condition, boolean queriedOnly, boolean jniAccessible, T type); - void registerDeclaredMethods(C condition, boolean queriedOnly, T type); + void registerDeclaredMethods(C condition, boolean queriedOnly, boolean jniAccessible, T type); - void registerPublicConstructors(C condition, boolean queriedOnly, T type); + void registerPublicConstructors(C condition, boolean queriedOnly, boolean jniAccessible, T type); - void registerDeclaredConstructors(C condition, boolean queriedOnly, T type); + void registerDeclaredConstructors(C condition, boolean queriedOnly, boolean jniAccessible, T type); - void registerField(C condition, T type, String fieldName, boolean allowWrite) throws NoSuchFieldException; + void registerField(C condition, T type, String fieldName, boolean allowWrite, boolean jniAccessible) throws NoSuchFieldException; - boolean registerAllMethodsWithName(C condition, boolean queriedOnly, T type, String methodName); + boolean registerAllMethodsWithName(C condition, boolean queriedOnly, boolean jniAccessible, T type, String methodName); - void registerMethod(C condition, boolean queriedOnly, T type, String methodName, List methodParameterTypes) throws NoSuchMethodException; + void registerMethod(C condition, boolean queriedOnly, T type, String methodName, List methodParameterTypes, boolean jniAccessible) throws NoSuchMethodException; - void registerConstructor(C condition, boolean queriedOnly, T type, List methodParameterTypes) throws NoSuchMethodException; + void registerConstructor(C condition, boolean queriedOnly, T type, List methodParameterTypes, boolean jniAccessible) throws NoSuchMethodException; - boolean registerAllConstructors(C condition, boolean queriedOnly, T type); + boolean registerAllConstructors(C condition, boolean queriedOnly, boolean jniAccessible, T type); void registerUnsafeAllocated(C condition, T clazz); void registerAsSerializable(C condition, T clazz); + void registerAsJniAccessed(C condition, T clazz); + String getTypeName(T type); String getSimpleName(T type); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionMetadataParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionMetadataParser.java index 95565e1cfed7..e63547bb28ea 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionMetadataParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionMetadataParser.java @@ -39,19 +39,17 @@ class ReflectionMetadataParser extends ReflectionConfigurationParser { private static final List OPTIONAL_REFLECT_METADATA_ATTRS = Arrays.asList(CONDITIONAL_KEY, "allDeclaredConstructors", "allPublicConstructors", "allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields", - "methods", "fields", "unsafeAllocated", "serializable"); + "methods", "fields", "unsafeAllocated", "serializable", "jniAccessible"); - private final String combinedFileKey; - - ReflectionMetadataParser(String combinedFileKey, ConfigurationConditionResolver conditionResolver, ReflectionConfigurationParserDelegate delegate, + ReflectionMetadataParser(ConfigurationConditionResolver conditionResolver, ReflectionConfigurationParserDelegate delegate, EnumSet parserOptions) { super(conditionResolver, delegate, parserOptions); - this.combinedFileKey = combinedFileKey; } @Override public void parseAndRegister(Object json, URI origin) { - Object reflectionJson = getFromGlobalFile(json, combinedFileKey); + String sectionName = checkOption(ConfigurationParserOption.JNI_PARSER) ? JNI_KEY : REFLECTION_KEY; + Object reflectionJson = getFromGlobalFile(json, sectionName); if (reflectionJson != null) { parseClassArray(asList(reflectionJson, "first level of document must be an array of class descriptors")); } @@ -87,29 +85,38 @@ protected void parseClass(EconomicMap data) { T clazz = result.get(); delegate.registerType(conditionResult.get(), clazz); + boolean jniParser = checkOption(ConfigurationParserOption.JNI_PARSER); delegate.registerDeclaredClasses(queryCondition, clazz); - delegate.registerRecordComponents(queryCondition, clazz); - delegate.registerPermittedSubclasses(queryCondition, clazz); - delegate.registerNestMembers(queryCondition, clazz); - delegate.registerSigners(queryCondition, clazz); delegate.registerPublicClasses(queryCondition, clazz); - delegate.registerDeclaredConstructors(queryCondition, true, clazz); - delegate.registerPublicConstructors(queryCondition, true, clazz); - delegate.registerDeclaredMethods(queryCondition, true, clazz); - delegate.registerPublicMethods(queryCondition, true, clazz); - delegate.registerDeclaredFields(queryCondition, true, clazz); - delegate.registerPublicFields(queryCondition, true, clazz); + if (!jniParser) { + delegate.registerRecordComponents(queryCondition, clazz); + delegate.registerPermittedSubclasses(queryCondition, clazz); + delegate.registerNestMembers(queryCondition, clazz); + delegate.registerSigners(queryCondition, clazz); + } + delegate.registerDeclaredConstructors(queryCondition, true, jniParser, clazz); + delegate.registerPublicConstructors(queryCondition, true, jniParser, clazz); + delegate.registerDeclaredMethods(queryCondition, true, jniParser, clazz); + delegate.registerPublicMethods(queryCondition, true, jniParser, clazz); + delegate.registerDeclaredFields(queryCondition, true, jniParser, clazz); + delegate.registerPublicFields(queryCondition, true, jniParser, clazz); + + boolean typeJniAccessible; + if (jniParser) { + typeJniAccessible = true; + } else { + registerIfNotDefault(data, false, clazz, "serializable", () -> delegate.registerAsSerializable(condition, clazz)); + typeJniAccessible = registerIfNotDefault(data, false, clazz, "jniAccessible", () -> delegate.registerAsJniAccessed(condition, clazz)); + } - registerIfNotDefault(data, false, clazz, "allDeclaredConstructors", () -> delegate.registerDeclaredConstructors(condition, false, clazz)); - registerIfNotDefault(data, false, clazz, "allPublicConstructors", () -> delegate.registerPublicConstructors(condition, false, clazz)); - registerIfNotDefault(data, false, clazz, "allDeclaredMethods", () -> delegate.registerDeclaredMethods(condition, false, clazz)); - registerIfNotDefault(data, false, clazz, "allPublicMethods", () -> delegate.registerPublicMethods(condition, false, clazz)); - registerIfNotDefault(data, false, clazz, "allDeclaredFields", () -> delegate.registerDeclaredFields(condition, false, clazz)); - registerIfNotDefault(data, false, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, clazz)); + registerIfNotDefault(data, false, clazz, "allDeclaredConstructors", () -> delegate.registerDeclaredConstructors(condition, false, typeJniAccessible, clazz)); + registerIfNotDefault(data, false, clazz, "allPublicConstructors", () -> delegate.registerPublicConstructors(condition, false, typeJniAccessible, clazz)); + registerIfNotDefault(data, false, clazz, "allDeclaredMethods", () -> delegate.registerDeclaredMethods(condition, false, typeJniAccessible, clazz)); + registerIfNotDefault(data, false, clazz, "allPublicMethods", () -> delegate.registerPublicMethods(condition, false, typeJniAccessible, clazz)); + registerIfNotDefault(data, false, clazz, "allDeclaredFields", () -> delegate.registerDeclaredFields(condition, false, typeJniAccessible, clazz)); + registerIfNotDefault(data, false, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, typeJniAccessible, clazz)); registerIfNotDefault(data, false, clazz, "unsafeAllocated", () -> delegate.registerUnsafeAllocated(condition, clazz)); - registerIfNotDefault(data, false, clazz, "serializable", () -> delegate.registerAsSerializable(condition, clazz)); - MapCursor cursor = data.getEntries(); while (cursor.advance()) { String name = cursor.getKey(); @@ -117,10 +124,10 @@ protected void parseClass(EconomicMap data) { try { switch (name) { case "methods": - parseMethods(condition, false, asList(value, "Attribute 'methods' must be an array of method descriptors"), clazz); + parseMethods(condition, false, asList(value, "Attribute 'methods' must be an array of method descriptors"), clazz, typeJniAccessible); break; case "fields": - parseFields(condition, asList(value, "Attribute 'fields' must be an array of field descriptors"), clazz); + parseFields(condition, asList(value, "Attribute 'fields' must be an array of field descriptors"), clazz, typeJniAccessible); break; } } catch (LinkageError e) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java index f31a57b932f0..641680a2b790 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/UnresolvedConfigurationCondition.java @@ -1,42 +1,26 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * The Universal Permissive License (UPL), Version 1.0 + * 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. * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both + * 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). * - * (a) the Software, and + * 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. * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationFileCollection.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationFileCollection.java index f2e6d10ccee1..615e24a482c6 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationFileCollection.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationFileCollection.java @@ -24,9 +24,6 @@ */ package com.oracle.svm.configure.config; -import static com.oracle.svm.configure.ConfigurationParser.JNI_KEY; -import static com.oracle.svm.configure.ConfigurationParser.REFLECTION_KEY; - import java.io.IOException; import java.net.URI; import java.nio.file.Files; @@ -52,9 +49,12 @@ public class ConfigurationFileCollection { private final EnumSet parserOptions; + private final EnumSet jniParserOptions; public ConfigurationFileCollection() { this.parserOptions = EnumSet.of(ConfigurationParserOption.STRICT_CONFIGURATION, ConfigurationParserOption.TREAT_ALL_TYPE_REACHABLE_CONDITIONS_AS_TYPE_REACHED); + this.jniParserOptions = parserOptions.clone(); + jniParserOptions.add(ConfigurationParserOption.JNI_PARSER); } public static final Function FAIL_ON_EXCEPTION = e -> e; @@ -158,12 +158,10 @@ public Set getPredefinedClassesConfigPaths() { return predefinedClassesConfigPaths; } - public TypeConfiguration loadJniConfig(Function exceptionHandler) throws Exception { - return loadTypeConfig(JNI_KEY, jniConfigPaths, exceptionHandler); - } - public TypeConfiguration loadReflectConfig(Function exceptionHandler) throws Exception { - return loadTypeConfig(REFLECTION_KEY, reflectConfigPaths, exceptionHandler); + TypeConfiguration reflectConfig = loadTypeConfig(ConfigurationFile.REFLECTION, reflectConfigPaths, exceptionHandler); + TypeConfiguration jniConfig = loadTypeConfig(ConfigurationFile.JNI, jniConfigPaths, exceptionHandler); + return reflectConfig.copyAndMerge(jniConfig); } public ProxyConfiguration loadProxyConfig(Function exceptionHandler) throws Exception { @@ -195,15 +193,16 @@ public SerializationConfiguration loadSerializationConfig(Function exceptionHandler, List> predefinedConfigClassDestinationDirs, Predicate predefinedConfigClassWithHashExclusionPredicate) throws Exception { - return new ConfigurationSet(loadReflectConfig(exceptionHandler), loadJniConfig(exceptionHandler), loadResourceConfig(exceptionHandler), loadProxyConfig(exceptionHandler), + return new ConfigurationSet(loadReflectConfig(exceptionHandler), loadResourceConfig(exceptionHandler), loadProxyConfig(exceptionHandler), loadSerializationConfig(exceptionHandler), loadPredefinedClassesConfig(predefinedConfigClassDestinationDirs, predefinedConfigClassWithHashExclusionPredicate, exceptionHandler)); } - private TypeConfiguration loadTypeConfig(String combinedFileKey, Collection uris, Function exceptionHandler) throws Exception { - TypeConfiguration configuration = new TypeConfiguration(combinedFileKey); - loadConfig(reachabilityMetadataPaths, configuration.createParser(true, parserOptions), exceptionHandler); - loadConfig(uris, configuration.createParser(false, parserOptions), exceptionHandler); + private TypeConfiguration loadTypeConfig(ConfigurationFile configurationKind, Collection uris, Function exceptionHandler) throws Exception { + TypeConfiguration configuration = new TypeConfiguration(); + var specificParserOptions = configurationKind == ConfigurationFile.JNI ? jniParserOptions : parserOptions; + loadConfig(reachabilityMetadataPaths, configuration.createParser(true, specificParserOptions), exceptionHandler); + loadConfig(uris, configuration.createParser(false, specificParserOptions), exceptionHandler); return configuration; } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationSet.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationSet.java index d9ea2142db33..1b41df51fe2c 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationSet.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationSet.java @@ -24,9 +24,6 @@ */ package com.oracle.svm.configure.config; -import static com.oracle.svm.configure.ConfigurationParser.JNI_KEY; -import static com.oracle.svm.configure.ConfigurationParser.REFLECTION_KEY; - import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; @@ -50,16 +47,14 @@ private interface Mutator { } private final TypeConfiguration reflectionConfiguration; - private final TypeConfiguration jniConfiguration; private final ResourceConfiguration resourceConfiguration; private final ProxyConfiguration proxyConfiguration; private final SerializationConfiguration serializationConfiguration; private final PredefinedClassesConfiguration predefinedClassesConfiguration; - public ConfigurationSet(TypeConfiguration reflectionConfiguration, TypeConfiguration jniConfiguration, ResourceConfiguration resourceConfiguration, ProxyConfiguration proxyConfiguration, + public ConfigurationSet(TypeConfiguration reflectionConfiguration, ResourceConfiguration resourceConfiguration, ProxyConfiguration proxyConfiguration, SerializationConfiguration serializationConfiguration, PredefinedClassesConfiguration predefinedClassesConfiguration) { this.reflectionConfiguration = reflectionConfiguration; - this.jniConfiguration = jniConfiguration; this.resourceConfiguration = resourceConfiguration; this.proxyConfiguration = proxyConfiguration; this.serializationConfiguration = serializationConfiguration; @@ -67,24 +62,23 @@ public ConfigurationSet(TypeConfiguration reflectionConfiguration, TypeConfigura } public ConfigurationSet(ConfigurationSet other) { - this(other.reflectionConfiguration.copy(), other.jniConfiguration.copy(), other.resourceConfiguration.copy(), other.proxyConfiguration.copy(), other.serializationConfiguration.copy(), + this(other.reflectionConfiguration.copy(), other.resourceConfiguration.copy(), other.proxyConfiguration.copy(), other.serializationConfiguration.copy(), other.predefinedClassesConfiguration.copy()); } @SuppressWarnings("unchecked") public ConfigurationSet() { - this(new TypeConfiguration(REFLECTION_KEY), new TypeConfiguration(JNI_KEY), new ResourceConfiguration(), new ProxyConfiguration(), new SerializationConfiguration(), + this(new TypeConfiguration(), new ResourceConfiguration(), new ProxyConfiguration(), new SerializationConfiguration(), new PredefinedClassesConfiguration(Collections.emptyList(), hash -> false)); } private ConfigurationSet mutate(ConfigurationSet other, Mutator mutator) { TypeConfiguration reflectionConfig = mutator.apply(this.reflectionConfiguration, other.reflectionConfiguration); - TypeConfiguration jniConfig = mutator.apply(this.jniConfiguration, other.jniConfiguration); ResourceConfiguration resourceConfig = mutator.apply(this.resourceConfiguration, other.resourceConfiguration); ProxyConfiguration proxyConfig = mutator.apply(this.proxyConfiguration, other.proxyConfiguration); SerializationConfiguration serializationConfig = mutator.apply(this.serializationConfiguration, other.serializationConfiguration); PredefinedClassesConfiguration predefinedClassesConfig = mutator.apply(this.predefinedClassesConfiguration, other.predefinedClassesConfiguration); - return new ConfigurationSet(reflectionConfig, jniConfig, resourceConfig, proxyConfig, serializationConfig, predefinedClassesConfig); + return new ConfigurationSet(reflectionConfig, resourceConfig, proxyConfig, serializationConfig, predefinedClassesConfig); } public ConfigurationSet copyAndMerge(ConfigurationSet other) { @@ -101,22 +95,17 @@ public ConfigurationSet copyAndIntersectWith(ConfigurationSet other) { public ConfigurationSet filter(ConditionalConfigurationPredicate filter) { TypeConfiguration reflectionConfig = this.reflectionConfiguration.copyAndFilter(filter); - TypeConfiguration jniConfig = this.jniConfiguration.copyAndFilter(filter); ResourceConfiguration resourceConfig = this.resourceConfiguration.copyAndFilter(filter); ProxyConfiguration proxyConfig = this.proxyConfiguration.copyAndFilter(filter); SerializationConfiguration serializationConfig = this.serializationConfiguration.copyAndFilter(filter); PredefinedClassesConfiguration predefinedClassesConfig = this.predefinedClassesConfiguration.copyAndFilter(filter); - return new ConfigurationSet(reflectionConfig, jniConfig, resourceConfig, proxyConfig, serializationConfig, predefinedClassesConfig); + return new ConfigurationSet(reflectionConfig, resourceConfig, proxyConfig, serializationConfig, predefinedClassesConfig); } public TypeConfiguration getReflectionConfiguration() { return reflectionConfiguration; } - public TypeConfiguration getJniConfiguration() { - return jniConfiguration; - } - public ResourceConfiguration getResourceConfiguration() { return resourceConfiguration; } @@ -140,9 +129,8 @@ public PredefinedClassesConfiguration getPredefinedClassesConfiguration() { return (T) proxyConfiguration; case RESOURCES: return (T) resourceConfiguration; - case JNI: - return (T) jniConfiguration; case REFLECTION: + case JNI: return (T) reflectionConfiguration; case SERIALIZATION: return (T) serializationConfiguration; @@ -212,7 +200,7 @@ public List writeConfiguration(Function configFil } public boolean isEmpty() { - return reflectionConfiguration.isEmpty() && jniConfiguration.isEmpty() && resourceConfiguration.isEmpty() && proxyConfiguration.isEmpty() && serializationConfiguration.isEmpty() && + return reflectionConfiguration.isEmpty() && resourceConfiguration.isEmpty() && proxyConfiguration.isEmpty() && serializationConfiguration.isEmpty() && predefinedClassesConfiguration.isEmpty(); } } 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 2439dc3488aa..6940a662077d 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 @@ -102,6 +102,7 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType private ConfigurationMemberAccessibility allDeclaredConstructorsAccess = ConfigurationMemberAccessibility.NONE; private ConfigurationMemberAccessibility allPublicConstructorsAccess = ConfigurationMemberAccessibility.NONE; private boolean serializable = false; + private boolean typeJniAccessible = false; public ConfigurationType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean includeAllElements) { this.condition = Objects.requireNonNull(condition); @@ -292,6 +293,7 @@ private void setFlagsFromOther(ConfigurationType other, BiPredicate methodParameterTypes) { + public void registerMethod(UnresolvedConfigurationCondition condition, boolean queriedOnly, ConfigurationType type, String methodName, List methodParameterTypes, + boolean jniAccessible) { checkArguments(condition.equals(type.getCondition()), "condition is already a part of the type"); + if (jniAccessible) { + type.setJniAccessible(); + } type.addMethod(methodName, ConfigurationMethod.toInternalParamsSignature(methodParameterTypes), ConfigurationMemberDeclaration.PRESENT, queriedOnly ? ConfigurationMemberAccessibility.QUERIED : ConfigurationMemberAccessibility.ACCESSED); } @Override - public void registerConstructor(UnresolvedConfigurationCondition condition, boolean queriedOnly, ConfigurationType type, List methodParameterTypes) { + public void registerConstructor(UnresolvedConfigurationCondition condition, boolean queriedOnly, ConfigurationType type, List methodParameterTypes, boolean jniAccessible) { checkArguments(condition.equals(type.getCondition()), "condition is already a part of the type"); + if (jniAccessible) { + type.setJniAccessible(); + } type.addMethod(ConfigurationMethod.CONSTRUCTOR_NAME, ConfigurationMethod.toInternalParamsSignature(methodParameterTypes), ConfigurationMemberDeclaration.PRESENT, queriedOnly ? ConfigurationMemberAccessibility.QUERIED : ConfigurationMemberAccessibility.ACCESSED); } @@ -136,38 +152,56 @@ public void registerSigners(UnresolvedConfigurationCondition condition, Configur } @Override - public void registerPublicFields(UnresolvedConfigurationCondition condition, boolean queriedOnly, ConfigurationType type) { + public void registerPublicFields(UnresolvedConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, ConfigurationType type) { checkArguments(condition.isAlwaysTrue() || condition.equals(type.getCondition()), "condition is already a part of the type"); + if (jniAccessible) { + type.setJniAccessible(); + } type.setAllPublicFields(queriedOnly ? ConfigurationMemberAccessibility.QUERIED : ConfigurationMemberAccessibility.ACCESSED); } @Override - public void registerDeclaredFields(UnresolvedConfigurationCondition condition, boolean queriedOnly, ConfigurationType type) { + public void registerDeclaredFields(UnresolvedConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, ConfigurationType type) { checkArguments(condition.isAlwaysTrue() || condition.equals(type.getCondition()), "condition is already a part of the type"); + if (jniAccessible) { + type.setJniAccessible(); + } type.setAllDeclaredFields(queriedOnly ? ConfigurationMemberAccessibility.QUERIED : ConfigurationMemberAccessibility.ACCESSED); } @Override - public void registerPublicMethods(UnresolvedConfigurationCondition condition, boolean queriedOnly, ConfigurationType type) { + public void registerPublicMethods(UnresolvedConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, ConfigurationType type) { checkArguments(condition.isAlwaysTrue() || condition.equals(type.getCondition()), "condition is already a part of the type"); + if (jniAccessible) { + type.setJniAccessible(); + } type.setAllPublicMethods(queriedOnly ? ConfigurationMemberAccessibility.QUERIED : ConfigurationMemberAccessibility.ACCESSED); } @Override - public void registerDeclaredMethods(UnresolvedConfigurationCondition condition, boolean queriedOnly, ConfigurationType type) { + public void registerDeclaredMethods(UnresolvedConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, ConfigurationType type) { checkArguments(condition.isAlwaysTrue() || condition.equals(type.getCondition()), "condition is already a part of the type"); + if (jniAccessible) { + type.setJniAccessible(); + } type.setAllDeclaredMethods(queriedOnly ? ConfigurationMemberAccessibility.QUERIED : ConfigurationMemberAccessibility.ACCESSED); } @Override - public void registerPublicConstructors(UnresolvedConfigurationCondition condition, boolean queriedOnly, ConfigurationType type) { + public void registerPublicConstructors(UnresolvedConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, ConfigurationType type) { checkArguments(condition.isAlwaysTrue() || condition.equals(type.getCondition()), "condition is already a part of the type"); + if (jniAccessible) { + type.setJniAccessible(); + } type.setAllPublicConstructors(queriedOnly ? ConfigurationMemberAccessibility.QUERIED : ConfigurationMemberAccessibility.ACCESSED); } @Override - public void registerDeclaredConstructors(UnresolvedConfigurationCondition condition, boolean queriedOnly, ConfigurationType type) { + public void registerDeclaredConstructors(UnresolvedConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, ConfigurationType type) { checkArguments(condition.isAlwaysTrue() || condition.equals(type.getCondition()), "condition is already a part of the type"); + if (jniAccessible) { + type.setJniAccessible(); + } type.setAllDeclaredConstructors(queriedOnly ? ConfigurationMemberAccessibility.QUERIED : ConfigurationMemberAccessibility.ACCESSED); } @@ -177,6 +211,12 @@ public void registerAsSerializable(UnresolvedConfigurationCondition condition, C type.setSerializable(); } + @Override + public void registerAsJniAccessed(UnresolvedConfigurationCondition condition, ConfigurationType type) { + checkArguments(condition.isAlwaysTrue() || condition.equals(type.getCondition()), "condition is already a part of the type"); + type.setJniAccessible(); + } + @Override public String getTypeName(ConfigurationType type) { return type.getTypeDescriptor().toString(); 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 c8dc5ec6298a..514ebd3602c1 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 @@ -25,10 +25,8 @@ package com.oracle.svm.configure.config; import java.io.IOException; -import java.util.ArrayList; import java.util.Comparator; import java.util.EnumSet; -import java.util.List; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -42,21 +40,18 @@ import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; +import jdk.graal.compiler.util.json.JsonPrinter; import jdk.graal.compiler.util.json.JsonWriter; public final class TypeConfiguration extends ConfigurationBase { private final ConcurrentMap, ConfigurationType> types = new ConcurrentHashMap<>(); - private final String combinedFileKey; - - public TypeConfiguration(String combinedFileKey) { - this.combinedFileKey = combinedFileKey; + public TypeConfiguration() { } public TypeConfiguration(TypeConfiguration other) { other.types.forEach((key, value) -> types.put(key, new ConfigurationType(value))); - this.combinedFileKey = other.combinedFileKey; } @Override @@ -135,22 +130,12 @@ 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::getTypeDescriptor).thenComparing(ConfigurationType::getCondition)); - - writer.append('[').indent(); - String prefix = ""; - for (ConfigurationType type : typesList) { - writer.append(prefix).newline(); - type.printJson(writer); - prefix = ","; - } - writer.unindent().newline().append(']'); + JsonPrinter.printCollection(writer, types.values(), Comparator.comparing(ConfigurationType::getTypeDescriptor).thenComparing(ConfigurationType::getCondition), ConfigurationType::printJson); } @Override public ConfigurationParser createParser(boolean combinedFileSchema, EnumSet parserOptions) { - return ReflectionConfigurationParser.create(combinedFileKey, combinedFileSchema, ConfigurationConditionResolver.identityResolver(), new ParserConfigurationAdapter(this), parserOptions); + return ReflectionConfigurationParser.create(combinedFileSchema, ConfigurationConditionResolver.identityResolver(), new ParserConfigurationAdapter(this), parserOptions); } @Override diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/PartialConfigurationWithOrigins.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/PartialConfigurationWithOrigins.java index 015944812bf6..8cc193c013cb 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/PartialConfigurationWithOrigins.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/conditional/PartialConfigurationWithOrigins.java @@ -180,6 +180,8 @@ private static void printConfigurationSet(JsonWriter writer, ConfigurationSet co private static void parseConfigurationSet(EconomicMap configJson, ConfigurationSet configurationSet, URI origin) throws IOException { MapCursor cursor = configJson.getEntries(); EnumSet parserOptions = EnumSet.of(ConfigurationParserOption.STRICT_CONFIGURATION, ConfigurationParserOption.TREAT_ALL_TYPE_REACHABLE_CONDITIONS_AS_TYPE_REACHED); + EnumSet jniParserOptions = parserOptions.clone(); + jniParserOptions.add(ConfigurationParserOption.JNI_PARSER); while (cursor.advance()) { String configName = cursor.getKey(); ConfigurationFile configType = ConfigurationFile.getByName(configName); @@ -188,10 +190,12 @@ private static void parseConfigurationSet(EconomicMap configJson, Con } if (configType == ConfigurationFile.REACHABILITY_METADATA) { for (ConfigurationFile file : ConfigurationFile.combinedFileConfigurations()) { - configurationSet.getConfiguration(file).createParser(true, parserOptions).parseAndRegister(cursor.getValue(), origin); + var specificParserOptions = file == ConfigurationFile.JNI ? jniParserOptions : parserOptions; + configurationSet.getConfiguration(file).createParser(true, specificParserOptions).parseAndRegister(cursor.getValue(), origin); } } else { - configurationSet.getConfiguration(configType).createParser(false, parserOptions).parseAndRegister(cursor.getValue(), origin); + var specificParserOptions = configType == ConfigurationFile.JNI ? jniParserOptions : parserOptions; + configurationSet.getConfiguration(configType).createParser(false, specificParserOptions).parseAndRegister(cursor.getValue(), origin); } } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/AccessAdvisor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/AccessAdvisor.java index 75e4f844707e..0e3b2bc0cc26 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/AccessAdvisor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/AccessAdvisor.java @@ -301,7 +301,8 @@ public boolean shouldIgnoreJniLookup(String jniFunction, LazyValue queri } public boolean shouldIgnoreResourceLookup(LazyValue resource, EconomicMap entry) { - boolean result = Set.of("META-INF/services/jdk.vm.ci.services.JVMCIServiceLocator", "META-INF/services/java.lang.System$LoggerFinder").contains(resource.get()); + boolean result = Set.of("META-INF/services/jdk.vm.ci.services.JVMCIServiceLocator", "META-INF/services/java.lang.System$LoggerFinder", + "META-INF/services/jdk.vm.ci.hotspot.HotSpotJVMCIBackendFactory").contains(resource.get()); if (result) { logIgnoredEntry("blocklisted resource", entry); } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java index 1a3739b0ab5c..52ea3ad303b3 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java @@ -29,7 +29,6 @@ import java.util.List; -import com.oracle.svm.configure.config.ConfigurationType; import org.graalvm.collections.EconomicMap; import com.oracle.svm.configure.ClassNameSupport; @@ -39,6 +38,7 @@ import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration; import com.oracle.svm.configure.config.ConfigurationMethod; import com.oracle.svm.configure.config.ConfigurationSet; +import com.oracle.svm.configure.config.ConfigurationType; import com.oracle.svm.configure.config.TypeConfiguration; import com.oracle.svm.util.LogUtils; @@ -71,7 +71,7 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat if (!advisor.shouldIgnore(reflectionName, callerClassLazyValue, entry)) { if (function.equals("FindClass")) { if (!advisor.shouldIgnoreJniLookup(function, reflectionName, lazyNull(), lazyNull(), callerClassLazyValue, entry)) { - configurationSet.getJniConfiguration().getOrCreateType(condition, type); + configurationSet.getReflectionConfiguration().getOrCreateType(condition, type).setJniAccessible(); } } else if (!AccessAdvisor.PROXY_CLASS_NAME_PATTERN.matcher(jniName).matches()) { // DefineClass LogUtils.warning("Unsupported JNI function DefineClass used to load class " + jniName); @@ -88,7 +88,8 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat ConfigurationTypeDescriptor declaringClass = hasDeclaringClass ? NamedConfigurationTypeDescriptor.fromReflectionName((String) entry.get("declaring_class")) : null; ConfigurationTypeDescriptor declaringClassOrClazz = hasDeclaringClass ? declaringClass : clazz; ConfigurationMemberDeclaration declaration = hasDeclaringClass ? ConfigurationMemberDeclaration.DECLARED : ConfigurationMemberDeclaration.PRESENT; - TypeConfiguration config = configurationSet.getJniConfiguration(); + TypeConfiguration config = configurationSet.getReflectionConfiguration(); + boolean makeTypeJniAccessible = true; switch (function) { case "AllocObject": expectSize(args, 0); @@ -104,7 +105,8 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat String name = (String) args.get(0); String signature = (String) args.get(1); if (!advisor.shouldIgnoreJniLookup(function, lazyValue(className), lazyValue(name), lazyValue(signature), callerClassLazyValue, entry)) { - config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration); + ConfigurationType type = getOrCreateJniAccessibleType(config, condition, declaringClassOrClazz); + type.addMethod(name, signature, declaration); if (!declaringClassOrClazz.equals(clazz)) { config.getOrCreateType(condition, clazz); } @@ -117,7 +119,8 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat String name = (String) args.get(0); String signature = (String) args.get(1); if (!advisor.shouldIgnoreJniLookup(function, lazyValue(className), lazyValue(name), lazyValue(signature), callerClassLazyValue, entry)) { - config.getOrCreateType(condition, declaringClassOrClazz).addField(name, declaration, false); + ConfigurationType type = getOrCreateJniAccessibleType(config, condition, declaringClassOrClazz); + type.addField(name, declaration, false); if (!declaringClassOrClazz.equals(clazz)) { config.getOrCreateType(condition, clazz); } @@ -129,26 +132,37 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat String name = ConfigurationMethod.CONSTRUCTOR_NAME; String signature = "(Ljava/lang/String;)V"; if (!advisor.shouldIgnoreJniLookup(function, lazyValue(className), lazyValue(name), lazyValue(signature), callerClassLazyValue, entry)) { - config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration); + ConfigurationType type = getOrCreateJniAccessibleType(config, condition, declaringClassOrClazz); + type.addMethod(name, signature, declaration); assert declaringClassOrClazz.equals(clazz) : "Constructor can only be accessed via declaring class"; } break; } case "ToReflectedField": - config = configurationSet.getReflectionConfiguration(); // fall through + makeTypeJniAccessible = false; + // fall through case "FromReflectedField": { expectSize(args, 1); String name = (String) args.get(0); - config.getOrCreateType(condition, declaringClassOrClazz).addField(name, declaration, false); + ConfigurationType type = config.getOrCreateType(condition, declaringClassOrClazz); + if (makeTypeJniAccessible) { + type.setJniAccessible(); + } + type.addField(name, declaration, false); break; } case "ToReflectedMethod": - config = configurationSet.getReflectionConfiguration(); // fall through + makeTypeJniAccessible = false; + // fall through case "FromReflectedMethod": { expectSize(args, 2); String name = (String) args.get(0); String signature = (String) args.get(1); - config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration); + ConfigurationType type = config.getOrCreateType(condition, declaringClassOrClazz); + if (makeTypeJniAccessible) { + type.setJniAccessible(); + } + type.addMethod(name, signature, declaration); break; } case "NewObjectArray": { @@ -160,4 +174,10 @@ void processEntry(EconomicMap entry, ConfigurationSet configurat } } + private static ConfigurationType getOrCreateJniAccessibleType(TypeConfiguration config, UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeName) { + ConfigurationType type = config.getOrCreateType(condition, typeName); + type.setJniAccessible(); + return type; + } + } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java index 935ff334abf5..f0278cd5ecfa 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/SerializationProcessor.java @@ -31,7 +31,6 @@ import org.graalvm.collections.EconomicMap; -import com.oracle.svm.configure.ClassNameSupport; import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.configure.UnresolvedConfigurationCondition; import com.oracle.svm.configure.config.ConfigurationSet; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java index bd9801dfbba5..9903e8b88a97 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.hosted.config; +import static com.oracle.svm.configure.ConfigurationParserOption.JNI_PARSER; import static com.oracle.svm.core.configure.ConfigurationFiles.Options.ReachabilityMetadataResources; import java.io.IOException; @@ -33,6 +34,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; +import java.util.EnumSet; import java.util.Enumeration; import java.util.List; import java.util.Spliterator; @@ -43,6 +45,8 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.ReflectionRegistry; +import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport; +import org.graalvm.nativeimage.impl.RuntimeProxyCreationSupport; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; import com.oracle.svm.configure.ConfigurationFile; @@ -54,18 +58,17 @@ import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.util.UserError; import com.oracle.svm.hosted.ImageClassLoader; -import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry; import jdk.graal.compiler.util.json.JsonParserException; public final class ConfigurationParserUtils { - public static ReflectionConfigurationParser> create(String combinedFileKey, boolean combinedFileSchema, - ConfigurationConditionResolver conditionResolver, ReflectionRegistry registry, ProxyRegistry proxyRegistry, - RuntimeSerializationSupport serializationSupport, ImageClassLoader imageClassLoader) { - return ReflectionConfigurationParser.create(combinedFileKey, combinedFileSchema, conditionResolver, - RegistryAdapter.create(registry, proxyRegistry, serializationSupport, imageClassLoader), - ConfigurationFiles.Options.getConfigurationParserOptions()); + public static ReflectionConfigurationParser> create(ConfigurationFile configurationKind, boolean combinedFileSchema, + ConfigurationConditionResolver conditionResolver, ReflectionRegistry registry, RuntimeProxyCreationSupport proxyRegistry, + RuntimeSerializationSupport serializationSupport, RuntimeJNIAccessSupport jniSupport, ImageClassLoader imageClassLoader) { + var additionalParserOptions = configurationKind == ConfigurationFile.JNI ? EnumSet.of(JNI_PARSER) : null; + return ReflectionConfigurationParser.create(combinedFileSchema, conditionResolver, RegistryAdapter.create(registry, proxyRegistry, serializationSupport, jniSupport, imageClassLoader), + ConfigurationFiles.Options.getConfigurationParserOptions(additionalParserOptions, null)); } /** diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/JNIRegistryAdapter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/JNIRegistryAdapter.java new file mode 100644 index 000000000000..f6ceb6919532 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/JNIRegistryAdapter.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2025, 2025, 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.hosted.config; + +import java.lang.reflect.Executable; +import java.lang.reflect.Field; +import java.util.List; + +import org.graalvm.nativeimage.impl.ConfigurationCondition; +import org.graalvm.nativeimage.impl.ReflectionRegistry; + +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.ImageClassLoader; + +public class JNIRegistryAdapter extends RegistryAdapter { + public JNIRegistryAdapter(ReflectionRegistry registry, ImageClassLoader classLoader) { + super(registry, classLoader); + } + + @Override + public void registerPublicClasses(ConfigurationCondition condition, Class type) { + registry.register(condition, type.getClasses()); + } + + @Override + public void registerDeclaredClasses(ConfigurationCondition condition, Class type) { + registry.register(condition, type.getDeclaredClasses()); + } + + @Override + public void registerRecordComponents(ConfigurationCondition condition, Class type) { + VMError.shouldNotReachHere("Record components cannot be accessed through JNI registrations"); + } + + @Override + public void registerPermittedSubclasses(ConfigurationCondition condition, Class type) { + VMError.shouldNotReachHere("Permitted subclasses cannot be accessed through JNI registrations"); + } + + @Override + public void registerNestMembers(ConfigurationCondition condition, Class type) { + VMError.shouldNotReachHere("Nest members cannot be accessed through JNI registrations"); + } + + @Override + public void registerSigners(ConfigurationCondition condition, Class type) { + VMError.shouldNotReachHere("Signers cannot be accessed through JNI registrations"); + } + + @Override + public void registerPublicFields(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { + ensureJniAccessible(jniAccessible); + super.registerPublicFields(condition, queriedOnly, false, type); + } + + @Override + public void registerDeclaredFields(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { + ensureJniAccessible(jniAccessible); + super.registerDeclaredFields(condition, queriedOnly, false, type); + } + + @Override + public void registerPublicMethods(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { + ensureJniAccessible(jniAccessible); + super.registerPublicMethods(condition, queriedOnly, false, type); + } + + @Override + public void registerDeclaredMethods(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { + ensureJniAccessible(jniAccessible); + super.registerDeclaredMethods(condition, queriedOnly, false, type); + } + + @Override + public void registerPublicConstructors(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { + ensureJniAccessible(jniAccessible); + super.registerPublicConstructors(condition, queriedOnly, false, type); + } + + @Override + public void registerDeclaredConstructors(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { + ensureJniAccessible(jniAccessible); + super.registerDeclaredConstructors(condition, queriedOnly, false, type); + } + + @Override + protected void registerField(ConfigurationCondition condition, boolean allowWrite, boolean jniAccessible, Field field) { + ensureJniAccessible(jniAccessible); + super.registerField(condition, allowWrite, true, field); + } + + @Override + protected void registerFieldNegativeQuery(ConfigurationCondition condition, boolean jniAccessible, Class type, String fieldName) { + ensureJniAccessible(jniAccessible); + super.registerFieldNegativeQuery(condition, true, type, fieldName); + } + + @Override + protected void registerExecutable(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Executable... executable) { + ensureJniAccessible(jniAccessible); + super.registerExecutable(condition, queriedOnly, true, executable); + } + + @Override + protected void registerMethodNegativeQuery(ConfigurationCondition condition, boolean jniAccessible, Class type, String methodName, List> methodParameterTypes) { + ensureJniAccessible(jniAccessible); + super.registerMethodNegativeQuery(condition, true, type, methodName, methodParameterTypes); + } + + @Override + protected void registerConstructorNegativeQuery(ConfigurationCondition condition, boolean jniAccessible, Class type, List> constructorParameterTypes) { + ensureJniAccessible(jniAccessible); + super.registerConstructorNegativeQuery(condition, true, type, constructorParameterTypes); + } + + @Override + public void registerAsSerializable(ConfigurationCondition condition, Class clazz) { + VMError.shouldNotReachHere("serializable cannot be set on JNI registrations"); + } + + @Override + public void registerAsJniAccessed(ConfigurationCondition condition, Class clazz) { + VMError.shouldNotReachHere("jniAccessible cannot be set on JNI registrations"); + } + + private static void ensureJniAccessible(boolean jniAccessible) { + VMError.guarantee(jniAccessible, "JNIRegistryAdapter can only be used for JNI queries"); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java index e94f1c3c145b..bf81e51df8fc 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java @@ -24,10 +24,16 @@ */ package com.oracle.svm.hosted.config; +import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors; + +import java.lang.reflect.Executable; +import java.lang.reflect.Field; import java.lang.reflect.Proxy; -import java.util.Arrays; +import java.util.List; import org.graalvm.nativeimage.impl.ConfigurationCondition; +import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport; +import org.graalvm.nativeimage.impl.RuntimeProxyCreationSupport; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; @@ -36,27 +42,28 @@ import com.oracle.svm.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.reflect.ReflectionDataBuilder; -import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry; import com.oracle.svm.util.TypeResult; public class ReflectionRegistryAdapter extends RegistryAdapter { private final RuntimeReflectionSupport reflectionSupport; - private final ProxyRegistry proxyRegistry; + private final RuntimeProxyCreationSupport proxyRegistry; private final RuntimeSerializationSupport serializationSupport; + private final RuntimeJNIAccessSupport jniSupport; - ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, ProxyRegistry proxyRegistry, RuntimeSerializationSupport serializationSupport, - ImageClassLoader classLoader) { + ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, RuntimeProxyCreationSupport proxyRegistry, RuntimeSerializationSupport serializationSupport, + RuntimeJNIAccessSupport jniSupport, ImageClassLoader classLoader) { super(reflectionSupport, classLoader); this.reflectionSupport = reflectionSupport; this.proxyRegistry = proxyRegistry; this.serializationSupport = serializationSupport; + this.jniSupport = jniSupport; } @Override public void registerType(ConfigurationCondition condition, Class type) { super.registerType(condition, type); if (Proxy.isProxyClass(type)) { - proxyRegistry.accept(condition, Arrays.stream(type.getInterfaces()).map(Class::getTypeName).toList()); + proxyRegistry.addProxyClass(condition, type.getInterfaces()); } } @@ -68,6 +75,9 @@ public TypeResult> resolveType(ConfigurationCondition condition, Config if (classLookupException instanceof LinkageError) { String reflectionName = ClassNameSupport.typeNameToReflectionName(namedDescriptor.name()); reflectionSupport.registerClassLookupException(condition, reflectionName, classLookupException); + } else if (throwMissingRegistrationErrors() && classLookupException instanceof ClassNotFoundException) { + String jniName = ClassNameSupport.typeNameToJNIName(namedDescriptor.name()); + jniSupport.registerClassLookup(condition, jniName); } } return result; @@ -104,37 +114,108 @@ public void registerSigners(ConfigurationCondition condition, Class type) { } @Override - public void registerPublicFields(ConfigurationCondition condition, boolean queriedOnly, Class type) { - ((ReflectionDataBuilder) reflectionSupport).registerAllFieldsQuery(condition, queriedOnly, type); + public void registerPublicFields(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { + if (queriedOnly && reflectionSupport instanceof ReflectionDataBuilder reflectionDataBuilder) { + reflectionDataBuilder.registerAllFieldsQuery(condition, true, type); + } else if (!queriedOnly) { + reflectionSupport.registerAllFields(condition, type); + if (jniAccessible) { + jniSupport.register(condition, false, type.getFields()); + } + } } @Override - public void registerDeclaredFields(ConfigurationCondition condition, boolean queriedOnly, Class type) { - ((ReflectionDataBuilder) reflectionSupport).registerAllDeclaredFieldsQuery(condition, queriedOnly, type); + public void registerDeclaredFields(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { + if (queriedOnly && reflectionSupport instanceof ReflectionDataBuilder reflectionDataBuilder) { + reflectionDataBuilder.registerAllDeclaredFieldsQuery(condition, true, type); + } else if (!queriedOnly) { + reflectionSupport.registerAllDeclaredFields(condition, type); + if (jniAccessible) { + jniSupport.register(condition, false, type.getDeclaredFields()); + } + } } @Override - public void registerPublicMethods(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public void registerPublicMethods(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { reflectionSupport.registerAllMethodsQuery(condition, queriedOnly, type); + if (!queriedOnly && jniAccessible) { + jniSupport.register(condition, false, type.getMethods()); + } } @Override - public void registerDeclaredMethods(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public void registerDeclaredMethods(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { reflectionSupport.registerAllDeclaredMethodsQuery(condition, queriedOnly, type); + if (!queriedOnly && jniAccessible) { + jniSupport.register(condition, false, type.getDeclaredMethods()); + } } @Override - public void registerPublicConstructors(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public void registerPublicConstructors(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { reflectionSupport.registerAllConstructorsQuery(condition, queriedOnly, type); + if (!queriedOnly && jniAccessible) { + jniSupport.register(condition, false, type.getConstructors()); + } } @Override - public void registerDeclaredConstructors(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public void registerDeclaredConstructors(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { reflectionSupport.registerAllDeclaredConstructorsQuery(condition, queriedOnly, type); + if (!queriedOnly && jniAccessible) { + jniSupport.register(condition, false, type.getDeclaredConstructors()); + } } @Override public void registerAsSerializable(ConfigurationCondition condition, Class clazz) { serializationSupport.register(condition, clazz); } + + @Override + public void registerAsJniAccessed(ConfigurationCondition condition, Class clazz) { + jniSupport.register(condition, clazz); + } + + @Override + protected void registerField(ConfigurationCondition condition, boolean allowWrite, boolean jniAccessible, Field field) { + super.registerField(condition, allowWrite, jniAccessible, field); + if (jniAccessible) { + jniSupport.register(condition, allowWrite, field); + } + } + + @Override + protected void registerFieldNegativeQuery(ConfigurationCondition condition, boolean jniAccessible, Class type, String fieldName) { + super.registerFieldNegativeQuery(condition, jniAccessible, type, fieldName); + if (jniAccessible) { + jniSupport.registerFieldLookup(condition, type, fieldName); + } + } + + @Override + protected void registerExecutable(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Executable... executable) { + super.registerExecutable(condition, queriedOnly, jniAccessible, executable); + if (jniAccessible) { + jniSupport.register(condition, queriedOnly, executable); + } + } + + @Override + protected void registerMethodNegativeQuery(ConfigurationCondition condition, boolean jniAccessible, Class type, String methodName, List> methodParameterTypes) { + super.registerMethodNegativeQuery(condition, jniAccessible, type, methodName, methodParameterTypes); + if (jniAccessible) { + jniSupport.registerMethodLookup(condition, type, methodName, getParameterTypes(methodParameterTypes)); + } + } + + @Override + protected void registerConstructorNegativeQuery(ConfigurationCondition condition, boolean jniAccessible, Class type, List> constructorParameterTypes) { + super.registerConstructorNegativeQuery(condition, jniAccessible, type, constructorParameterTypes); + if (jniAccessible) { + jniSupport.registerConstructorLookup(condition, type, getParameterTypes(constructorParameterTypes)); + } + } } 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 c07dbadf7a37..c07038ecdedb 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 @@ -27,6 +27,7 @@ import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors; import java.lang.reflect.Executable; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -35,6 +36,8 @@ import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.ReflectionRegistry; +import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport; +import org.graalvm.nativeimage.impl.RuntimeProxyCreationSupport; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; @@ -46,18 +49,19 @@ import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ImageClassLoader; -import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry; import com.oracle.svm.util.ClassUtil; import com.oracle.svm.util.TypeResult; public class RegistryAdapter implements ReflectionConfigurationParserDelegate> { - private final ReflectionRegistry registry; + protected final ReflectionRegistry registry; private final ImageClassLoader classLoader; - public static RegistryAdapter create(ReflectionRegistry registry, ProxyRegistry proxyRegistry, RuntimeSerializationSupport serializationSupport, - ImageClassLoader classLoader) { + public static RegistryAdapter create(ReflectionRegistry registry, RuntimeProxyCreationSupport proxyRegistry, RuntimeSerializationSupport serializationSupport, + RuntimeJNIAccessSupport jniSupport, ImageClassLoader classLoader) { if (registry instanceof RuntimeReflectionSupport) { - return new ReflectionRegistryAdapter((RuntimeReflectionSupport) registry, proxyRegistry, serializationSupport, classLoader); + return new ReflectionRegistryAdapter((RuntimeReflectionSupport) registry, proxyRegistry, serializationSupport, jniSupport, classLoader); + } else if (registry instanceof RuntimeJNIAccessSupport) { + return new JNIRegistryAdapter(registry, classLoader); } else { return new RegistryAdapter(registry, classLoader); } @@ -136,12 +140,10 @@ private TypeResult> resolveProxyType(ProxyConfigurationTypeDescriptor t @Override public void registerPublicClasses(ConfigurationCondition condition, Class type) { - registry.register(condition, type.getClasses()); } @Override public void registerDeclaredClasses(ConfigurationCondition condition, Class type) { - registry.register(condition, type.getDeclaredClasses()); } @Override @@ -161,59 +163,70 @@ public void registerSigners(ConfigurationCondition condition, Class type) { } @Override - public void registerPublicFields(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public void registerPublicFields(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { if (!queriedOnly) { registry.register(condition, false, type.getFields()); } } @Override - public void registerDeclaredFields(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public void registerDeclaredFields(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { if (!queriedOnly) { registry.register(condition, false, type.getDeclaredFields()); } } @Override - public void registerPublicMethods(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public void registerPublicMethods(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { registry.register(condition, queriedOnly, type.getMethods()); } @Override - public void registerDeclaredMethods(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public void registerDeclaredMethods(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { registry.register(condition, queriedOnly, type.getDeclaredMethods()); } @Override - public void registerPublicConstructors(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public void registerPublicConstructors(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { registry.register(condition, queriedOnly, type.getConstructors()); } @Override - public void registerDeclaredConstructors(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public void registerDeclaredConstructors(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { registry.register(condition, queriedOnly, type.getDeclaredConstructors()); } @Override - public void registerField(ConfigurationCondition condition, Class type, String fieldName, boolean allowWrite) throws NoSuchFieldException { + @SuppressWarnings("unused") + public final void registerField(ConfigurationCondition condition, Class type, String fieldName, boolean allowWrite, boolean jniAccessible) throws NoSuchFieldException { try { - registry.register(condition, allowWrite, type.getDeclaredField(fieldName)); + registerField(condition, allowWrite, jniAccessible, type.getDeclaredField(fieldName)); } catch (NoSuchFieldException e) { if (throwMissingRegistrationErrors()) { - registry.registerFieldLookup(condition, type, fieldName); + registerFieldNegativeQuery(condition, jniAccessible, type, fieldName); } else { throw e; } } } + @SuppressWarnings("unused") + protected void registerField(ConfigurationCondition condition, boolean allowWrite, boolean jniAccessible, Field field) { + registry.register(condition, allowWrite, field); + } + + @SuppressWarnings("unused") + protected void registerFieldNegativeQuery(ConfigurationCondition condition, boolean jniAccessible, Class type, String fieldName) { + registry.registerFieldLookup(condition, type, fieldName); + } + @Override - public boolean registerAllMethodsWithName(ConfigurationCondition condition, boolean queriedOnly, Class type, String methodName) { + public boolean registerAllMethodsWithName(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type, String methodName) { boolean found = false; Executable[] methods = type.getDeclaredMethods(); for (Executable method : methods) { if (method.getName().equals(methodName)) { - registerExecutable(condition, queriedOnly, method); + registerExecutable(condition, queriedOnly, jniAccessible, method); found = true; } } @@ -221,9 +234,9 @@ public boolean registerAllMethodsWithName(ConfigurationCondition condition, bool } @Override - public boolean registerAllConstructors(ConfigurationCondition condition, boolean queriedOnly, Class type) { + public boolean registerAllConstructors(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Class type) { Executable[] methods = type.getDeclaredConstructors(); - registerExecutable(condition, queriedOnly, methods); + registerExecutable(condition, queriedOnly, jniAccessible, methods); return methods.length > 0; } @@ -239,7 +252,8 @@ public void registerUnsafeAllocated(ConfigurationCondition condition, Class c } @Override - public void registerMethod(ConfigurationCondition condition, boolean queriedOnly, Class type, String methodName, List> methodParameterTypes) throws NoSuchMethodException { + public final void registerMethod(ConfigurationCondition condition, boolean queriedOnly, Class type, String methodName, List> methodParameterTypes, boolean jniAccessible) + throws NoSuchMethodException { try { Class[] parameterTypesArray = getParameterTypes(methodParameterTypes); Method method; @@ -261,10 +275,10 @@ public void registerMethod(ConfigurationCondition condition, boolean queriedOnly throw e; } } - registerExecutable(condition, queriedOnly, method); + registerExecutable(condition, queriedOnly, jniAccessible, method); } catch (NoSuchMethodException e) { if (throwMissingRegistrationErrors()) { - registry.registerMethodLookup(condition, type, methodName, getParameterTypes(methodParameterTypes)); + registerMethodNegativeQuery(condition, jniAccessible, type, methodName, methodParameterTypes); } else { throw e; } @@ -272,13 +286,14 @@ public void registerMethod(ConfigurationCondition condition, boolean queriedOnly } @Override - public void registerConstructor(ConfigurationCondition condition, boolean queriedOnly, Class type, List> methodParameterTypes) throws NoSuchMethodException { + public final void registerConstructor(ConfigurationCondition condition, boolean queriedOnly, Class type, List> methodParameterTypes, boolean jniAccessible) + throws NoSuchMethodException { Class[] parameterTypesArray = getParameterTypes(methodParameterTypes); try { - registerExecutable(condition, queriedOnly, type.getDeclaredConstructor(parameterTypesArray)); + registerExecutable(condition, queriedOnly, jniAccessible, type.getDeclaredConstructor(parameterTypesArray)); } catch (NoSuchMethodException e) { if (throwMissingRegistrationErrors()) { - registry.registerConstructorLookup(condition, type, getParameterTypes(methodParameterTypes)); + registerConstructorNegativeQuery(condition, jniAccessible, type, methodParameterTypes); } else { throw e; } @@ -289,13 +304,27 @@ static Class[] getParameterTypes(List> methodParameterTypes) { return methodParameterTypes.toArray(Class[]::new); } - private void registerExecutable(ConfigurationCondition condition, boolean queriedOnly, Executable... executable) { + @SuppressWarnings("unused") + protected void registerExecutable(ConfigurationCondition condition, boolean queriedOnly, boolean jniAccessible, Executable... executable) { registry.register(condition, queriedOnly, executable); } + @SuppressWarnings("unused") + protected void registerMethodNegativeQuery(ConfigurationCondition condition, boolean jniAccessible, Class type, String methodName, List> methodParameterTypes) { + registry.registerMethodLookup(condition, type, methodName, getParameterTypes(methodParameterTypes)); + } + + @SuppressWarnings("unused") + protected void registerConstructorNegativeQuery(ConfigurationCondition condition, boolean jniAccessible, Class type, List> constructorParameterTypes) { + registry.registerConstructorLookup(condition, type, getParameterTypes(constructorParameterTypes)); + } + @Override public void registerAsSerializable(ConfigurationCondition condition, Class clazz) { - /* Serializable has no effect on JNI registrations */ + } + + @Override + public void registerAsJniAccessed(ConfigurationCondition condition, Class clazz) { } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java index 105f0e7e159f..0d4be8aa5399 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.hosted.jni; -import static com.oracle.svm.configure.ConfigurationParser.JNI_KEY; - import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -107,6 +105,7 @@ import com.oracle.svm.hosted.meta.KnownOffsetsFeature; import com.oracle.svm.hosted.meta.MaterializedConstantFields; import com.oracle.svm.hosted.reflect.NativeImageConditionResolver; +import com.oracle.svm.hosted.reflect.ReflectionFeature; import com.oracle.svm.hosted.reflect.proxy.DynamicProxyFeature; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; import com.oracle.svm.util.ReflectionUtil; @@ -199,7 +198,7 @@ private void abortIfSealed() { @Override public List> getRequiredFeatures() { // Ensure that KnownOffsets is fully initialized before we access it - return List.of(KnownOffsetsFeature.class, DynamicProxyFeature.class); + return List.of(KnownOffsetsFeature.class, DynamicProxyFeature.class, ReflectionFeature.class); } @Override @@ -213,10 +212,11 @@ public void afterRegistration(AfterRegistrationAccess arg) { ConfigurationConditionResolver conditionResolver = new NativeImageConditionResolver(access.getImageClassLoader(), ClassInitializationSupport.singleton()); - ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(JNI_KEY, true, conditionResolver, runtimeSupport, null, null, + ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(ConfigurationFile.JNI, true, conditionResolver, runtimeSupport, null, null, null, access.getImageClassLoader()); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurationsFromCombinedFile(parser, access.getImageClassLoader(), "JNI"); - ReflectionConfigurationParser> legacyParser = ConfigurationParserUtils.create(null, false, conditionResolver, runtimeSupport, null, null, + ReflectionConfigurationParser> legacyParser = ConfigurationParserUtils.create(ConfigurationFile.JNI, false, conditionResolver, runtimeSupport, null, null, + null, access.getImageClassLoader()); loadedConfigurations += ConfigurationParserUtils.parseAndRegisterConfigurations(legacyParser, access.getImageClassLoader(), "JNI", ConfigurationFiles.Options.JNIConfigurationFiles, ConfigurationFiles.Options.JNIConfigurationResources, ConfigurationFile.JNI.getFileName()); @@ -424,11 +424,7 @@ private static JNIAccessibleClass addClass(Class classObj, DuringAnalysisAcce } return JNIReflectionDictionary.currentLayer().addClassIfAbsent(classObj, c -> { AnalysisType analysisClass = access.getMetaAccess().lookupJavaType(classObj); - if (analysisClass.isInterface() || (analysisClass.isInstanceClass() && analysisClass.isAbstract())) { - analysisClass.registerAsReachable("is accessed via JNI"); - } else { - analysisClass.registerAsInstantiated("is accessed via JNI"); - } + analysisClass.registerAsReachable("is accessed via JNI"); return new JNIAccessibleClass(classObj); }); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java index 4b1fef0101d4..55f5b5086272 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.hosted.reflect; -import static com.oracle.svm.configure.ConfigurationParser.REFLECTION_KEY; - import java.lang.invoke.MethodHandle; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; @@ -45,6 +43,8 @@ import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.AnnotationExtractor; import org.graalvm.nativeimage.impl.ConfigurationCondition; +import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport; +import org.graalvm.nativeimage.impl.RuntimeProxyCreationSupport; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import org.graalvm.nativeimage.impl.RuntimeSerializationSupport; @@ -87,7 +87,6 @@ import com.oracle.svm.hosted.meta.HostedMetaAccess; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.reflect.proxy.DynamicProxyFeature; -import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry; import com.oracle.svm.hosted.snippets.ReflectionPlugins; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.util.ModuleSupport; @@ -293,13 +292,14 @@ public void duringSetup(DuringSetupAccess a) { aUniverse = access.getUniverse(); var conditionResolver = new NativeImageConditionResolver(access.getImageClassLoader(), ClassInitializationSupport.singleton()); reflectionData.duringSetup(access.getMetaAccess(), aUniverse); - ProxyRegistry proxyRegistry = ImageSingletons.lookup(ProxyRegistry.class); + RuntimeProxyCreationSupport proxyRegistry = ImageSingletons.lookup(RuntimeProxyCreationSupport.class); RuntimeSerializationSupport serializationSupport = RuntimeSerializationSupport.singleton(); - ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(REFLECTION_KEY, true, conditionResolver, reflectionData, proxyRegistry, - serializationSupport, access.getImageClassLoader()); + RuntimeJNIAccessSupport jniSupport = ImageSingletons.lookup(RuntimeJNIAccessSupport.class); + ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(ConfigurationFile.REFLECTION, true, conditionResolver, reflectionData, proxyRegistry, + serializationSupport, jniSupport, access.getImageClassLoader()); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurationsFromCombinedFile(parser, access.getImageClassLoader(), "reflection"); - ReflectionConfigurationParser> legacyParser = ConfigurationParserUtils.create(null, false, conditionResolver, reflectionData, proxyRegistry, - serializationSupport, access.getImageClassLoader()); + ReflectionConfigurationParser> legacyParser = ConfigurationParserUtils.create(ConfigurationFile.REFLECTION, false, conditionResolver, reflectionData, + proxyRegistry, serializationSupport, jniSupport, access.getImageClassLoader()); loadedConfigurations += ConfigurationParserUtils.parseAndRegisterConfigurations(legacyParser, access.getImageClassLoader(), "reflection", ConfigurationFiles.Options.ReflectionConfigurationFiles, ConfigurationFiles.Options.ReflectionConfigurationResources, ConfigurationFile.REFLECTION.getFileName()); diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageFeature.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageFeature.java index 9140cff138b5..80b9ea3b3bdd 100644 --- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageFeature.java +++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageFeature.java @@ -56,6 +56,7 @@ import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.svm.configure.ConfigurationFile; import com.oracle.svm.configure.ReflectionConfigurationParser; import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver; import com.oracle.svm.core.c.ProjectHeaderFile; @@ -240,8 +241,8 @@ public void duringSetup(DuringSetupAccess a) { if (entryPointConfig != null) { ConfigurationConditionResolver conditionResolver = new NativeImageConditionResolver(access.getImageClassLoader(), ClassInitializationSupport.singleton()); - ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(null, false, conditionResolver, entryPointsData, null, null, - access.getImageClassLoader()); + ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(ConfigurationFile.REFLECTION, false, conditionResolver, entryPointsData, null, + null, null, access.getImageClassLoader()); try { parser.parseAndRegister(Path.of(entryPointConfig).toUri()); } catch (IOException ex) {