From c8d9b02062ff8d4c9aeac8b5121cb78827031e49 Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Mon, 15 Apr 2024 13:56:30 +0200 Subject: [PATCH] Integrate proxy configuration into reflection configuration --- .../native-image/ReachabilityMetadata.md | 36 ++++---- substratevm/CHANGELOG.md | 1 + .../svm/agent/BreakpointInterceptor.java | 90 +++++++++---------- .../test/config/OmitPreviousConfigTests.java | 14 +-- .../configure/config/ConfigurationMethod.java | 1 + .../configure/config/ConfigurationType.java | 9 +- .../config/ParserConfigurationAdapter.java | 9 +- .../configure/config/TypeConfiguration.java | 10 +-- .../ConditionalConfigurationPredicate.java | 2 +- .../configure/trace/ReflectionProcessor.java | 47 ++++++---- .../core/configure/ConfigurationFiles.java | 4 +- .../core/configure/ConfigurationParser.java | 29 ++++-- .../ConfigurationTypeDescriptor.java | 64 +++++-------- .../NamedConfigurationTypeDescriptor.java | 67 ++++++++++++++ .../ProxyConfigurationTypeDescriptor.java | 74 +++++++++++++++ .../ReflectionConfigurationParser.java | 17 ++-- ...ReflectionConfigurationParserDelegate.java | 2 +- .../SerializationConfigurationParser.java | 13 ++- .../core/jdk/proxy/DynamicProxyRegistry.java | 2 +- .../reflect/proxy/DynamicProxySupport.java | 6 +- .../config/ConfigurationParserUtils.java | 5 +- .../config/ReflectionRegistryAdapter.java | 27 ++++-- .../svm/hosted/config/RegistryAdapter.java | 56 +++++++++--- .../svm/hosted/jni/JNIAccessFeature.java | 5 +- .../reflect/NativeImageConditionResolver.java | 4 +- .../svm/hosted/reflect/ReflectionFeature.java | 12 ++- .../reflect/proxy/DynamicProxyFeature.java | 9 -- .../hosted/reflect/proxy/ProxyRegistry.java | 2 +- 28 files changed, 411 insertions(+), 206 deletions(-) rename substratevm/src/{com.oracle.svm.configure/src/com/oracle/svm/configure/config => com.oracle.svm.core/src/com/oracle/svm/core/configure}/ConfigurationTypeDescriptor.java (61%) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/NamedConfigurationTypeDescriptor.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationTypeDescriptor.java diff --git a/docs/reference-manual/native-image/ReachabilityMetadata.md b/docs/reference-manual/native-image/ReachabilityMetadata.md index 7bde933e8699..db165b0411ea 100644 --- a/docs/reference-manual/native-image/ReachabilityMetadata.md +++ b/docs/reference-manual/native-image/ReachabilityMetadata.md @@ -323,22 +323,26 @@ The following methods are evaluated at build time when called with constant argu ### Dynamic Proxy Metadata in JSON -Dynamic proxy metadata should be specified in a _proxy-config.json_ file and conform to the JSON schema defined in -[proxy-config-schema-v1.0.0.json](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/proxy-config-schema-v1.0.0.json). -The schema also includes further details and explanations how this configuration works. Here is the example of the proxy-config.json: +Dynamic proxy metadata should be specified as part of a _reflect-config.json_ file by adding `"proxy"`-type entries, conforming to the JSON schema defined in [config-type-schema-v1.1.0.json](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/config-type-schema-v1.0.0.json). +It enables you to register members of a proxy class for reflection the same way as it would be done for a named class. +The order in which interfaces are given matters and the interfaces will be passed in the same order to generate the proxy class. +The schema also includes further details and explanations how this configuration works. +Here is an example of dynamic proxy metadata in reflect-config.json: ```json [ { "condition": { "typeReachable": "" }, - "interfaces": [ - "IA", - "IB" - ] + "type": { "proxy": [ + "IA", + "IB" + ]} } ] ``` +Contents of _proxy-config.json_ files will still be parsed and honored by Native Image, but this file is now deprecated +and the [Native Image agent](AutomaticMetadataCollection.md) outputs proxy metadata to reflect-config.json. ## Serialization Java can serialize any class that implements the `Serializable` interface. @@ -388,6 +392,14 @@ The schema also includes further details and explanations how this configuration }, "type": "", "customTargetConstructorClass": "" + }, + { + "condition": { + "typeReachable": "" + }, + "type": { + "proxy": ["", ""] + } } ], "lambdaCapturingTypes": [ @@ -397,15 +409,7 @@ The schema also includes further details and explanations how this configuration }, "name": "" } - ], - "proxies": [ - { - "condition": { - "typeReachable": "" - }, - "interfaces": ["", ""] - } - ] + ] } ``` diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index 4966ef7a6dba..d6cf8815318c 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -22,6 +22,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-51172) Add support to catch OutOfMemoryError exceptions on native image if there is no memory left. * (GR-43837) `--report-unsupported-elements-at-runtime` is now enabled by default and the option is deprecated. * (GR-53359) Provide the `.debug_gdb_scripts` section that triggers auto-loading of `svmhelpers.py` in GDB. Remove single and double quotes from `ClassLoader.nameAndId` in the debuginfo. +* (GR-47365) Include dynamic proxy metadata in the reflection metadata with the syntax `"type": { "proxy": [] }`. This allows members of proxy classes to be accessed reflectively. `proxy-config.json` is now deprecated but will still be honored. ## GraalVM for JDK 22 (Internal Version 24.0.0) * (GR-48304) Red Hat added support for the JFR event ThreadAllocationStatistics. diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index b78b18871598..4e9c6cd214cf 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -191,8 +191,8 @@ private static void traceBreakpoint(JNIEnvironment env, String context, JNIObjec if (tracer != null) { tracer.traceCall(context, function, - getClassNameOr(env, clazz, null, Tracer.UNKNOWN_VALUE), - getClassNameOr(env, declaringClass, null, Tracer.UNKNOWN_VALUE), + getClassOrProxyInterfaceNames(env, clazz), + getClassOrProxyInterfaceNames(env, declaringClass), getClassNameOr(env, callerClass, null, Tracer.UNKNOWN_VALUE), result, stackTrace, @@ -210,6 +210,39 @@ private static void traceBreakpoint(JNIEnvironment env, String context, JNIObjec } } + /** + * If the given class is a proxy, returns an array containing the names of its implemented + * interfaces, else returns the class name. This prevents classes with arbitrary names from + * being exposed outside the agent, since those names only make sense within a single execution + * of the program. + * + * @param env JNI environment of the thread running the JVMTI callback. + * @param clazz Handle to the class. + * @return The interface, or the original class if it is not a proxy or implements multiple + * interfaces. + */ + private static Object getClassOrProxyInterfaceNames(JNIEnvironment env, JNIObjectHandle clazz) { + if (clazz.equal(nullHandle())) { + return null; + } + + boolean isProxy = Support.callStaticBooleanMethodL(env, agent.handles().getJavaLangReflectProxy(env), agent.handles().getJavaLangReflectProxyIsProxyClass(env), clazz); + if (Support.clearException(env)) { + return Tracer.UNKNOWN_VALUE; + } + + if (!isProxy) { + return getClassNameOr(env, clazz, null, Tracer.UNKNOWN_VALUE); + } + + JNIObjectHandle interfaces = Support.callObjectMethod(env, clazz, agent.handles().javaLangClassGetInterfaces); + if (Support.clearException(env)) { + return Tracer.UNKNOWN_VALUE; + } + + return getClassArrayNames(env, interfaces); + } + private static boolean forName(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle name = getObjectArgument(thread, 0); @@ -232,7 +265,7 @@ private static boolean getDeclaredFields(JNIEnvironment jni, JNIObjectHandle thr private static boolean handleGetFields(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) { JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getReceiver(thread); - traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), nullHandle(), callerClass, bp.specification.methodName, null, state.getFullStackTraceOrNull()); + traceReflectBreakpoint(jni, self, nullHandle(), callerClass, bp.specification.methodName, null, state.getFullStackTraceOrNull()); return true; } @@ -256,8 +289,7 @@ private static boolean handleGetMethods(JNIEnvironment jni, JNIObjectHandle thre JNIObjectHandle callerClass = state.getDirectCallerClass(); JNIObjectHandle self = getReceiver(thread); /* When reflection metadata tracking is disabled, all methods are considered invoked */ - traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), nullHandle(), callerClass, bp.specification.methodName, trackReflectionMetadata ? null : true, - state.getFullStackTraceOrNull()); + traceReflectBreakpoint(jni, self, nullHandle(), callerClass, bp.specification.methodName, trackReflectionMetadata ? null : true, state.getFullStackTraceOrNull()); return true; } @@ -315,8 +347,7 @@ private static boolean handleGetField(JNIEnvironment jni, JNIObjectHandle thread declaring = nullHandle(); } } - traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, name.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), fromJniString(jni, name)); + traceReflectBreakpoint(jni, self, declaring, callerClass, bp.specification.methodName, name.notEqual(nullHandle()), state.getFullStackTraceOrNull(), fromJniString(jni, name)); return true; } @@ -379,8 +410,7 @@ private static boolean getConstructor(JNIEnvironment jni, JNIObjectHandle thread JNIObjectHandle self = getReceiver(thread); JNIObjectHandle paramTypesHandle = getObjectArgument(thread, 1); Object paramTypes = getClassArrayNames(jni, paramTypesHandle); - traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), nullHandle(), callerClass, bp.specification.methodName, true, state.getFullStackTraceOrNull(), - paramTypes); + traceReflectBreakpoint(jni, self, nullHandle(), callerClass, bp.specification.methodName, true, state.getFullStackTraceOrNull(), paramTypes); return true; } @@ -410,8 +440,7 @@ private static boolean handleGetMethod(JNIEnvironment jni, JNIObjectHandle threa } String name = fromJniString(jni, nameHandle); Object paramTypes = getClassArrayNames(jni, paramTypesHandle); - traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, - nameHandle.notEqual(nullHandle()), state.getFullStackTraceOrNull(), name, paramTypes); + traceReflectBreakpoint(jni, self, declaring, callerClass, bp.specification.methodName, nameHandle.notEqual(nullHandle()), state.getFullStackTraceOrNull(), name, paramTypes); return true; } @@ -476,8 +505,7 @@ private static boolean handleInvokeMethod(JNIEnvironment jni, JNIObjectHandle th } Object paramTypes = getClassArrayNames(jni, paramTypesHandle); - traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, declaring), getClassOrSingleProxyInterface(jni, declaring), callerClass, "invokeMethod", declaring.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), name, paramTypes); + traceReflectBreakpoint(jni, declaring, declaring, callerClass, "invokeMethod", declaring.notEqual(nullHandle()), state.getFullStackTraceOrNull(), name, paramTypes); /* * Calling Class.newInstance through Method.invoke should register the class for reflective @@ -524,8 +552,7 @@ private static boolean handleInvokeConstructor(JNIEnvironment jni, @SuppressWarn } Object paramTypes = getClassArrayNames(jni, paramTypesHandle); - traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, declaring), getClassOrSingleProxyInterface(jni, declaring), callerClass, "invokeConstructor", declaring.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), paramTypes); + traceReflectBreakpoint(jni, declaring, declaring, callerClass, "invokeConstructor", declaring.notEqual(nullHandle()), state.getFullStackTraceOrNull(), paramTypes); return true; } @@ -1521,39 +1548,6 @@ private static JNIMethodId resolveBreakpointMethod(JNIEnvironment jni, JNIObject return method; } - /** - * If the given class is a proxy implementing a single interface, returns this interface. This - * prevents classes with arbitrary names from being exposed outside the agent, since those names - * only make sense within a single execution of the program. - * - * @param env JNI environment of the thread running the JVMTI callback. - * @param clazz Handle to the class. - * @return The interface, or the original class if it is not a proxy or implements multiple - * interfaces. - */ - public static JNIObjectHandle getClassOrSingleProxyInterface(JNIEnvironment env, JNIObjectHandle clazz) { - boolean isProxy = Support.callStaticBooleanMethodL(env, agent.handles().getJavaLangReflectProxy(env), agent.handles().getJavaLangReflectProxyIsProxyClass(env), clazz); - if (Support.clearException(env) || !isProxy) { - return clazz; - } - - JNIObjectHandle interfaces = Support.callObjectMethod(env, clazz, agent.handles().javaLangClassGetInterfaces); - if (Support.clearException(env) || interfaces.equal(nullHandle())) { - return clazz; - } - - int interfacesLength = Support.jniFunctions().getGetArrayLength().invoke(env, interfaces); - guarantee(!Support.clearException(env)); - if (interfacesLength != 1) { - return clazz; - } - - JNIObjectHandle iface = Support.jniFunctions().getGetObjectArrayElement().invoke(env, interfaces, 0); - guarantee(!Support.clearException(env) && iface.notEqual(nullHandle())); - - return iface; - } - private static void bindNativeBreakpoint(JNIEnvironment jni, NativeBreakpoint bp, CodePointer originalAddress, WordPointer newAddressPtr) { assert !recursive.get(); bp.replacedFunction = originalAddress; 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 0d5b6bb266b5..d81c90485d36 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 @@ -52,6 +52,8 @@ import com.oracle.svm.configure.config.ResourceConfiguration; import com.oracle.svm.configure.config.SerializationConfiguration; import com.oracle.svm.configure.config.TypeConfiguration; +import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; +import com.oracle.svm.core.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.core.util.VMError; public class OmitPreviousConfigTests { @@ -142,8 +144,8 @@ private static void doTestTypeConfig(TypeConfiguration typeConfig) { } private static void doTestExpectedMissingTypes(TypeConfiguration typeConfig) { - Assert.assertNull(typeConfig.get(UnresolvedConfigurationCondition.alwaysTrue(), "FlagTestA")); - Assert.assertNull(typeConfig.get(UnresolvedConfigurationCondition.alwaysTrue(), "FlagTestB")); + Assert.assertNull(typeConfig.get(UnresolvedConfigurationCondition.alwaysTrue(), new NamedConfigurationTypeDescriptor("FlagTestA"))); + Assert.assertNull(typeConfig.get(UnresolvedConfigurationCondition.alwaysTrue(), new NamedConfigurationTypeDescriptor("FlagTestB"))); } private static void doTestTypeFlags(TypeConfiguration typeConfig) { @@ -201,7 +203,7 @@ private static void doTestSerializationConfig(SerializationConfiguration seriali } private static ConfigurationType getConfigTypeOrFail(TypeConfiguration typeConfig, String typeName) { - ConfigurationType type = typeConfig.get(UnresolvedConfigurationCondition.alwaysTrue(), typeName); + ConfigurationType type = typeConfig.get(UnresolvedConfigurationCondition.alwaysTrue(), new NamedConfigurationTypeDescriptor(typeName)); Assert.assertNotNull(type); return type; } @@ -289,14 +291,14 @@ void setFlags(ConfigurationType config) { } } - String getTypeName() { - return TEST_CLASS_NAME_PREFIX + "_" + methodKind.name(); + ConfigurationTypeDescriptor getTypeName() { + return new NamedConfigurationTypeDescriptor(TEST_CLASS_NAME_PREFIX + "_" + methodKind.name()); } void doTest() { TypeConfiguration currentConfigWithoutPrevious = currentConfig.copyAndSubtract(previousConfig); - String name = getTypeName(); + ConfigurationTypeDescriptor name = getTypeName(); ConfigurationType configurationType = currentConfigWithoutPrevious.get(UnresolvedConfigurationCondition.alwaysTrue(), name); if (methodsThatMustExist.size() == 0) { Assert.assertNull("Generated configuration type " + name + " exists. Expected it to be cleared as it is empty.", configurationType); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMethod.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMethod.java index 19460cbf111d..4b1f5aa8be1c 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMethod.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMethod.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Objects; +import com.oracle.svm.core.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.core.util.json.JsonPrintable; import com.oracle.svm.core.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 0cb7d83e9cc3..da1742248b6a 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 @@ -39,6 +39,7 @@ import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberAccessibility; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration; +import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; import com.oracle.svm.core.util.json.JsonPrintable; import com.oracle.svm.core.util.json.JsonPrinter; import com.oracle.svm.core.util.json.JsonWriter; @@ -103,10 +104,6 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType private ConfigurationMemberAccessibility allDeclaredConstructorsAccess = ConfigurationMemberAccessibility.NONE; private ConfigurationMemberAccessibility allPublicConstructorsAccess = ConfigurationMemberAccessibility.NONE; - public ConfigurationType(UnresolvedConfigurationCondition condition, String qualifiedJavaName, boolean includeAllElements) { - this(condition, new NamedConfigurationTypeDescriptor(qualifiedJavaName), includeAllElements); - } - public ConfigurationType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean includeAllElements) { this.condition = condition; this.typeDescriptor = typeDescriptor; @@ -445,8 +442,8 @@ public synchronized void setAllPublicConstructors(ConfigurationMemberAccessibili public synchronized void printJson(JsonWriter writer) throws IOException { writer.append('{').indent().newline(); ConfigurationConditionPrintable.printConditionAttribute(condition, writer); - /* GR-50385: Replace with "type" (and flip boolean entries below) */ - writer.quote("name").append(":"); + /* GR-50385: Flip boolean entries below when "type" includes them by default. */ + writer.quote("type").append(":"); typeDescriptor.printJson(writer); optionallyPrintJsonBoolean(writer, allDeclaredFields, "allDeclaredFields"); 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 a86b81663201..0f119a041824 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 @@ -31,6 +31,7 @@ import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberAccessibility; import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberDeclaration; import com.oracle.svm.core.TypeResult; +import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; import com.oracle.svm.core.configure.ReflectionConfigurationParserDelegate; import com.oracle.svm.core.util.VMError; @@ -43,10 +44,10 @@ public ParserConfigurationAdapter(TypeConfiguration configuration) { } @Override - public TypeResult resolveType(UnresolvedConfigurationCondition condition, String typeName, boolean allowPrimitives, boolean includeAllElements) { - ConfigurationType type = configuration.get(condition, typeName); - ConfigurationType result = type != null ? type : new ConfigurationType(condition, typeName, includeAllElements); - return TypeResult.forType(typeName, result); + public TypeResult resolveType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean allowPrimitives, boolean includeAllElements) { + ConfigurationType type = configuration.get(condition, typeDescriptor); + ConfigurationType result = type != null ? type : new ConfigurationType(condition, typeDescriptor, includeAllElements); + return TypeResult.forType(typeDescriptor.toString(), result); } @Override 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 51afbd82fb60..54ceb576be6f 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,6 +38,8 @@ import com.oracle.svm.core.configure.ConditionalElement; import com.oracle.svm.core.configure.ConfigurationConditionResolver; import com.oracle.svm.core.configure.ConfigurationParser; +import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; +import com.oracle.svm.core.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.core.configure.ReflectionConfigurationParser; import com.oracle.svm.core.util.VMError; import com.oracle.svm.core.util.json.JsonWriter; @@ -94,11 +96,7 @@ protected void removeIf(Predicate predicate) { types.entrySet().removeIf(entry -> predicate.testIncludedType(entry.getKey(), entry.getValue())); } - public ConfigurationType get(UnresolvedConfigurationCondition condition, String qualifiedJavaName) { - return get(condition, new NamedConfigurationTypeDescriptor(qualifiedJavaName)); - } - - private ConfigurationType get(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor) { + public ConfigurationType get(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor) { return types.get(new ConditionalElement<>(condition, typeDescriptor)); } @@ -124,7 +122,7 @@ public ConfigurationType getOrCreateType(UnresolvedConfigurationCondition condit return getOrCreateType(condition, new NamedConfigurationTypeDescriptor(qualifiedForNameString)); } - private ConfigurationType getOrCreateType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor) { + public ConfigurationType getOrCreateType(UnresolvedConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor) { return types.computeIfAbsent(new ConditionalElement<>(condition, typeDescriptor), p -> new ConfigurationType(p.condition(), p.element(), false)); } 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 eb29d3454c62..58218747b54c 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 @@ -31,7 +31,6 @@ import com.oracle.svm.configure.config.ConfigurationPredefinedClass; import com.oracle.svm.configure.config.ConfigurationType; -import com.oracle.svm.configure.config.ConfigurationTypeDescriptor; import com.oracle.svm.configure.config.PredefinedClassesConfiguration; import com.oracle.svm.configure.config.ProxyConfiguration; import com.oracle.svm.configure.config.ResourceConfiguration; @@ -41,6 +40,7 @@ import com.oracle.svm.configure.config.TypeConfiguration; import com.oracle.svm.configure.filters.ComplexFilter; import com.oracle.svm.core.configure.ConditionalElement; +import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; public class ConditionalConfigurationPredicate implements TypeConfiguration.Predicate, ProxyConfiguration.Predicate, ResourceConfiguration.Predicate, SerializationConfiguration.Predicate, PredefinedClassesConfiguration.Predicate { 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 8620f8027835..c46c5d73558b 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 @@ -38,10 +38,12 @@ 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.ProxyConfiguration; import com.oracle.svm.configure.config.ResourceConfiguration; import com.oracle.svm.configure.config.SignatureUtil; import com.oracle.svm.configure.config.TypeConfiguration; +import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; +import com.oracle.svm.core.configure.NamedConfigurationTypeDescriptor; +import com.oracle.svm.core.configure.ProxyConfigurationTypeDescriptor; import jdk.graal.compiler.phases.common.LazyValue; import jdk.vm.ci.meta.MetaUtil; @@ -107,13 +109,15 @@ public void processEntry(EconomicMap entry, ConfigurationSet configur } return; } - String clazz = (String) entry.get("class"); - if (advisor.shouldIgnore(lazyValue(clazz), lazyValue(callerClass))) { - return; + ConfigurationTypeDescriptor clazz = descriptorForClass(entry.get("class")); + for (String className : clazz.getAllQualifiedJavaNames()) { + if (advisor.shouldIgnore(lazyValue(className), lazyValue(callerClass))) { + return; + } } ConfigurationMemberDeclaration declaration = ConfigurationMemberDeclaration.PUBLIC; ConfigurationMemberAccessibility accessibility = Boolean.TRUE.equals(entry.get("result")) ? ConfigurationMemberAccessibility.ACCESSED : ConfigurationMemberAccessibility.QUERIED; - String clazzOrDeclaringClass = entry.containsKey("declaring_class") ? (String) entry.get("declaring_class") : clazz; + ConfigurationTypeDescriptor clazzOrDeclaringClass = entry.containsKey("declaring_class") ? descriptorForClass(entry.get("declaring_class")) : clazz; switch (function) { case "getDeclaredFields": { configuration.getOrCreateType(condition, clazz).setAllDeclaredFields(); @@ -227,17 +231,17 @@ public void processEntry(EconomicMap entry, ConfigurationSet configur case "getProxyClass": { expectSize(args, 2); - addDynamicProxy((List) args.get(1), lazyValue(callerClass), configurationSet.getProxyConfiguration()); + addDynamicProxy((List) args.get(1), lazyValue(callerClass), configuration); break; } case "newProxyInstance": { expectSize(args, 3); - addDynamicProxy((List) args.get(1), lazyValue(callerClass), configurationSet.getProxyConfiguration()); + addDynamicProxy((List) args.get(1), lazyValue(callerClass), configuration); break; } case "newMethodHandleProxyInstance": { expectSize(args, 1); - addDynamicProxyUnchecked((List) args.get(0), Collections.singletonList("sun.invoke.WrapperInstance"), lazyValue(callerClass), configurationSet.getProxyConfiguration()); + addDynamicProxyUnchecked((List) args.get(0), Collections.singletonList("sun.invoke.WrapperInstance"), lazyValue(callerClass), configuration); break; } @@ -249,8 +253,9 @@ public void processEntry(EconomicMap entry, ConfigurationSet configur } case "newInstance": { - if (clazz.equals("java.lang.reflect.Array")) { // reflective array instantiation - configuration.getOrCreateType(condition, (String) args.get(0)); + if (clazz.toString().equals("java.lang.reflect.Array")) { // reflective array + // instantiation + configuration.getOrCreateType(condition, new NamedConfigurationTypeDescriptor((String) args.get(0))); } else { configuration.getOrCreateType(condition, clazz).addMethod(ConfigurationMethod.CONSTRUCTOR_NAME, "()V", ConfigurationMemberDeclaration.DECLARED, ConfigurationMemberAccessibility.ACCESSED); @@ -276,6 +281,15 @@ public void processEntry(EconomicMap entry, ConfigurationSet configur } } + @SuppressWarnings("unchecked") + private static ConfigurationTypeDescriptor descriptorForClass(Object clazz) { + if (clazz instanceof List) { + return new ProxyConfigurationTypeDescriptor(((List) clazz).toArray(String[]::new)); + } else { + return new NamedConfigurationTypeDescriptor((String) clazz); + } + } + private static void addFullyQualifiedDeclaredMethod(String descriptor, TypeConfiguration configuration) { int sigbegin = descriptor.indexOf('('); int classend = descriptor.lastIndexOf('.', sigbegin - 1); @@ -285,18 +299,17 @@ private static void addFullyQualifiedDeclaredMethod(String descriptor, TypeConfi configuration.getOrCreateType(UnresolvedConfigurationCondition.alwaysTrue(), qualifiedClass).addMethod(methodName, signature, ConfigurationMemberDeclaration.DECLARED); } - private void addDynamicProxy(List interfaceList, LazyValue callerClass, ProxyConfiguration proxyConfiguration) { - @SuppressWarnings("unchecked") - List interfaces = (List) interfaceList; - for (String iface : interfaces) { + private void addDynamicProxy(List interfaceList, LazyValue callerClass, TypeConfiguration configuration) { + ConfigurationTypeDescriptor typeDescriptor = descriptorForClass(interfaceList); + for (String iface : typeDescriptor.getAllQualifiedJavaNames()) { if (advisor.shouldIgnore(lazyValue(iface), callerClass)) { return; } } - proxyConfiguration.add(UnresolvedConfigurationCondition.alwaysTrue(), interfaces); + configuration.getOrCreateType(UnresolvedConfigurationCondition.alwaysTrue(), typeDescriptor); } - private void addDynamicProxyUnchecked(List checkedInterfaceList, List uncheckedInterfaceList, LazyValue callerClass, ProxyConfiguration proxyConfiguration) { + private void addDynamicProxyUnchecked(List checkedInterfaceList, List uncheckedInterfaceList, LazyValue callerClass, TypeConfiguration configuration) { @SuppressWarnings("unchecked") List checkedInterfaces = (List) checkedInterfaceList; for (String iface : checkedInterfaces) { @@ -310,6 +323,6 @@ private void addDynamicProxyUnchecked(List checkedInterfaceList, List unch List interfaces = new ArrayList<>(); interfaces.addAll(checkedInterfaces); interfaces.addAll(uncheckedInterfaces); - proxyConfiguration.add(UnresolvedConfigurationCondition.alwaysTrue(), interfaces); + configuration.getOrCreateType(UnresolvedConfigurationCondition.alwaysTrue(), descriptorForClass(interfaces)); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java index 695a99266ae0..c6b7d5aeedc7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationFiles.java @@ -67,10 +67,10 @@ public static final class Options { public static final HostedOptionKey ReflectionConfigurationResources = new HostedOptionKey<>(LocatableMultiOptionValue.Strings.buildWithCommaDelimiter()); @OptionMigrationMessage("Use a proxy-config.json in your META-INF/native-image// directory instead.")// - @Option(help = "file:doc-files/ProxyConfigurationFilesHelp.txt", type = OptionType.User)// + @Option(help = "file:doc-files/ProxyConfigurationFilesHelp.txt", type = OptionType.User, deprecated = true)// @BundleMember(role = BundleMember.Role.Input)// public static final HostedOptionKey DynamicProxyConfigurationFiles = new HostedOptionKey<>(LocatableMultiOptionValue.Paths.buildWithCommaDelimiter()); - @Option(help = "Resources describing program elements to be made available for reflection (see ProxyConfigurationFiles).", type = OptionType.User)// + @Option(help = "Resources describing program elements to be made available for reflection (see ProxyConfigurationFiles).", type = OptionType.User, deprecated = true)// public static final HostedOptionKey DynamicProxyConfigurationResources = new HostedOptionKey<>(LocatableMultiOptionValue.Strings.buildWithCommaDelimiter()); @OptionMigrationMessage("Use a serialization-config.json in your META-INF/native-image// directory instead.")// diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationParser.java index 68aa4015b216..3f92724cb8ec 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationParser.java @@ -68,6 +68,7 @@ public static InputStream openStream(URI uri) throws IOException { public static final String CONDITIONAL_KEY = "condition"; public static final String NAME_KEY = "name"; public static final String TYPE_KEY = "type"; + public static final String PROXY_KEY = "proxy"; private final Map> seenUnknownAttributesByType = new HashMap<>(); private final boolean strictSchema; @@ -134,7 +135,7 @@ protected void checkAttributes(EconomicMap map, String type, Col } } - protected void checkHasExactlyOneAttribute(EconomicMap map, String type, Collection alternativeAttributes) { + public static void checkHasExactlyOneAttribute(EconomicMap map, String type, Collection alternativeAttributes) { boolean attributeFound = false; for (String key : map.getKeys()) { if (alternativeAttributes.contains(key)) { @@ -216,13 +217,15 @@ protected UnresolvedConfigurationCondition parseCondition(EconomicMap parseType(EconomicMap data) { + protected static Optional parseType(EconomicMap data) { Object typeObject = data.get(TYPE_KEY); Object name = data.get(NAME_KEY); if (typeObject != null) { return parseTypeContents(typeObject); } else if (name != null) { - return Optional.of(asString(name)); + return Optional.of(new NamedConfigurationTypeDescriptor(asString(name))); } else { throw failOnSchemaError("must have type or name specified for an element"); } } - protected static Optional parseTypeContents(Object typeObject) { + protected static Optional parseTypeContents(Object typeObject) { if (typeObject instanceof String stringValue) { - return Optional.of(stringValue); + return Optional.of(new NamedConfigurationTypeDescriptor(stringValue)); } else { + EconomicMap type = asMap(typeObject, "type descriptor should be a string or object"); + if (type.containsKey(PROXY_KEY)) { + checkHasExactlyOneAttribute(type, "type descriptor object", Set.of(PROXY_KEY)); + return Optional.of(getProxyDescriptor(type.get(PROXY_KEY))); + } /* * We return if we find a future version of a type descriptor (as a JSON object) instead * of failing parsing. */ - asMap(typeObject, "type descriptor should be a string or object"); return Optional.empty(); } } + + private static ProxyConfigurationTypeDescriptor getProxyDescriptor(Object proxyObject) { + List proxyInterfaces = asList(proxyObject, "proxy interface content should be an interface list"); + String[] proxyInterfaceNames = proxyInterfaces.stream().map(obj -> asString(obj, "proxy")).toArray(String[]::new); + return new ProxyConfigurationTypeDescriptor(proxyInterfaceNames); + } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationTypeDescriptor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationTypeDescriptor.java similarity index 61% rename from substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationTypeDescriptor.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationTypeDescriptor.java index 6a04d90b6ec2..2709a86d81bf 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationTypeDescriptor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ConfigurationTypeDescriptor.java @@ -22,14 +22,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.configure.config; +package com.oracle.svm.core.configure; -import java.io.IOException; import java.util.Collection; -import java.util.Collections; import com.oracle.svm.core.util.json.JsonPrintable; -import com.oracle.svm.core.util.json.JsonWriter; + +import jdk.vm.ci.meta.MetaUtil; /** * Provides a representation of a Java type based on String type names. This is used to parse types @@ -40,8 +39,21 @@ * */ 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; + NAMED, + PROXY } Kind getDescriptorType(); @@ -56,44 +68,8 @@ enum Kind { */ Collection getAllQualifiedJavaNames(); - static void checkQualifiedJavaName(String javaName) { - assert javaName.indexOf('/') == -1 : "Requires qualified Java name, not internal representation"; - assert !javaName.startsWith("[") : "Requires Java source array syntax, for example java.lang.String[]"; - } -} - -record NamedConfigurationTypeDescriptor(String name) implements ConfigurationTypeDescriptor { - - public NamedConfigurationTypeDescriptor { - ConfigurationTypeDescriptor.checkQualifiedJavaName(name); - } - - @Override - public Kind getDescriptorType() { - return Kind.NAMED; - } - - @Override - public String toString() { - return name; - } - - @Override - public Collection getAllQualifiedJavaNames() { - return Collections.singleton(name); - } - - @Override - public int compareTo(ConfigurationTypeDescriptor other) { - if (other instanceof NamedConfigurationTypeDescriptor namedOther) { - return name.compareTo(namedOther.name); - } else { - return getDescriptorType().compareTo(other.getDescriptorType()); - } - } - - @Override - public void printJson(JsonWriter writer) throws IOException { - writer.quote(name); + static String checkQualifiedJavaName(String javaName) { + assert javaName.indexOf('/') == -1 || javaName.indexOf('/') > javaName.lastIndexOf('.') : "Requires qualified Java name, not internal representation: %s".formatted(javaName); + return canonicalizeTypeName(javaName); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/NamedConfigurationTypeDescriptor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/NamedConfigurationTypeDescriptor.java new file mode 100644 index 000000000000..86b33152bf47 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/NamedConfigurationTypeDescriptor.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.configure; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; + +import com.oracle.svm.core.util.json.JsonWriter; + +public record NamedConfigurationTypeDescriptor(String name) implements ConfigurationTypeDescriptor { + + public NamedConfigurationTypeDescriptor(String name) { + this.name = ConfigurationTypeDescriptor.checkQualifiedJavaName(name); + } + + @Override + public Kind getDescriptorType() { + return Kind.NAMED; + } + + @Override + public String toString() { + return name; + } + + @Override + public Collection getAllQualifiedJavaNames() { + return Collections.singleton(name); + } + + @Override + public int compareTo(ConfigurationTypeDescriptor other) { + if (other instanceof NamedConfigurationTypeDescriptor namedOther) { + return name.compareTo(namedOther.name); + } else { + return getDescriptorType().compareTo(other.getDescriptorType()); + } + } + + @Override + public void printJson(JsonWriter writer) throws IOException { + writer.quote(name); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationTypeDescriptor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationTypeDescriptor.java new file mode 100644 index 000000000000..0d56fa6cff63 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ProxyConfigurationTypeDescriptor.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.configure; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import com.oracle.svm.core.reflect.proxy.DynamicProxySupport; +import com.oracle.svm.core.util.json.JsonPrinter; +import com.oracle.svm.core.util.json.JsonWriter; + +public record ProxyConfigurationTypeDescriptor(String[] interfaceNames) implements ConfigurationTypeDescriptor { + + public ProxyConfigurationTypeDescriptor(String[] interfaceNames) { + this.interfaceNames = Arrays.stream(interfaceNames).map(ConfigurationTypeDescriptor::checkQualifiedJavaName).toArray(String[]::new); + } + + @Override + public Kind getDescriptorType() { + return Kind.PROXY; + } + + @Override + public String toString() { + return DynamicProxySupport.proxyTypeDescriptor(interfaceNames); + } + + @Override + public Collection getAllQualifiedJavaNames() { + return Set.of(interfaceNames); + } + + @Override + public int compareTo(ConfigurationTypeDescriptor other) { + if (other instanceof ProxyConfigurationTypeDescriptor proxyOther) { + return Arrays.compare(interfaceNames, proxyOther.interfaceNames); + } else { + return getDescriptorType().compareTo(other.getDescriptorType()); + } + } + + @Override + public void printJson(JsonWriter writer) throws IOException { + writer.append("{").indent().newline(); + writer.quote("proxy").append(":"); + JsonPrinter.printCollection(writer, List.of(interfaceNames), null, (String p, JsonWriter w) -> w.quote(p)); + writer.unindent().newline().append("}"); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java index 4068ff1497a5..1cc31be00d3e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java @@ -83,9 +83,15 @@ private void parseClass(EconomicMap data) { if (!conditionResult.isPresent()) { return; } + C condition = conditionResult.get(); - Optional className = parseType(data); - if (className.isEmpty()) { + /* + * Classes registered using the old ("class") syntax will require elements (fields, methods, + * constructors, ...) to be registered for runtime queries, whereas the new ("type") syntax + * will automatically register all elements as queried. + */ + Optional type = parseType(data); + if (type.isEmpty()) { return; } @@ -93,10 +99,9 @@ private void parseClass(EconomicMap data) { * Even if primitives cannot be queried through Class.forName, they can be registered to * allow getDeclaredMethods() and similar bulk queries at run time. */ - C condition = conditionResult.get(); - TypeResult result = delegate.resolveType(condition, className.get(), true, false); + TypeResult result = delegate.resolveType(condition, type.get(), true, false); if (!result.isPresent()) { - handleMissingElement("Could not resolve class " + className.get() + " for reflection configuration.", result.getException()); + handleMissingElement("Could not resolve " + type.get() + " for reflection configuration.", result.getException()); return; } T clazz = result.get(); @@ -281,7 +286,7 @@ private List parseMethodParameters(T clazz, String methodName, List t List result = new ArrayList<>(); for (Object type : types) { String typeName = asString(type, "types"); - TypeResult typeResult = delegate.resolveType(conditionResolver.alwaysTrue(), typeName, true, false); + TypeResult typeResult = delegate.resolveType(conditionResolver.alwaysTrue(), new NamedConfigurationTypeDescriptor(typeName), true, false); if (!typeResult.isPresent()) { handleMissingElement("Could not register method " + formatMethod(clazz, methodName) + " for reflection.", typeResult.getException()); return null; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java index c561d8a509da..25547387027c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java @@ -30,7 +30,7 @@ public interface ReflectionConfigurationParserDelegate { - TypeResult resolveType(C condition, String typeName, boolean allowPrimitives, boolean includeAllElements); + TypeResult resolveType(C condition, ConfigurationTypeDescriptor typeDescriptor, boolean allowPrimitives, boolean includeAllElements); void registerType(C condition, T type); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/SerializationConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/SerializationConfigurationParser.java index 0441d9202757..5bfca8dc53a0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/SerializationConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/SerializationConfigurationParser.java @@ -106,18 +106,25 @@ private void parseSerializationDescriptorObject(EconomicMap data return; } - Optional targetSerializationClass; + Optional targetSerializationClass; targetSerializationClass = parseType(data); if (targetSerializationClass.isEmpty()) { return; } if (lambdaCapturingType) { - serializationSupport.registerLambdaCapturingClass(condition.get(), targetSerializationClass.get()); + String className = ((NamedConfigurationTypeDescriptor) targetSerializationClass.get()).name(); + serializationSupport.registerLambdaCapturingClass(condition.get(), className); } else { Object optionalCustomCtorValue = data.get(CUSTOM_TARGET_CONSTRUCTOR_CLASS_KEY); String customTargetConstructorClass = optionalCustomCtorValue != null ? asString(optionalCustomCtorValue) : null; - serializationSupport.registerWithTargetConstructorClass(condition.get(), targetSerializationClass.get(), customTargetConstructorClass); + if (targetSerializationClass.get() instanceof NamedConfigurationTypeDescriptor namedClass) { + serializationSupport.registerWithTargetConstructorClass(condition.get(), namedClass.name(), customTargetConstructorClass); + } else if (targetSerializationClass.get() instanceof ProxyConfigurationTypeDescriptor proxyClass) { + serializationSupport.registerProxyClass(condition.get(), Arrays.asList(proxyClass.interfaceNames())); + } else { + throw new JSONParserException("Unknown configuration type descriptor: %s".formatted(targetSerializationClass.toString())); + } } } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/proxy/DynamicProxyRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/proxy/DynamicProxyRegistry.java index 731aa5f5e9f3..183d95c6f8a9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/proxy/DynamicProxyRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/proxy/DynamicProxyRegistry.java @@ -33,5 +33,5 @@ public interface DynamicProxyRegistry extends RuntimeProxyCreationSupport { Class getProxyClass(ClassLoader loader, Class... interfaces); @Platforms(Platform.HOSTED_ONLY.class) - Class createProxyClassForSerialization(Class... interfaces); + Class getProxyClassHosted(Class... interfaces); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java index d6b0086b4ec1..ed8d5f191333 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java @@ -141,7 +141,7 @@ private static Object createProxyClass(Class[] interfaces) { @Override @Platforms(Platform.HOSTED_ONLY.class) - public Class createProxyClassForSerialization(Class... interfaces) { + public Class getProxyClassHosted(Class... interfaces) { final Class[] intfs = interfaces.clone(); return createProxyClassFromImplementedInterfaces(intfs); } @@ -222,4 +222,8 @@ private static void describeLoaderChain(StringBuilder b, ClassLoader loader) { l = l.getParent(); } } + + public static String proxyTypeDescriptor(String... interfaceNames) { + return "Proxy[" + String.join(", ", interfaceNames) + "]"; + } } 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 e9bd0fd71e56..400dced8dab3 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 @@ -49,15 +49,16 @@ import com.oracle.svm.core.option.LocatableMultiOptionValue; 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( - ConfigurationConditionResolver conditionResolver, ReflectionRegistry registry, ImageClassLoader imageClassLoader) { + ConfigurationConditionResolver conditionResolver, ReflectionRegistry registry, ProxyRegistry proxyRegistry, ImageClassLoader imageClassLoader) { return new ReflectionConfigurationParser<>(conditionResolver, - RegistryAdapter.create(registry, imageClassLoader), + RegistryAdapter.create(registry, proxyRegistry, imageClassLoader), ConfigurationFiles.Options.StrictConfiguration.getValue(), ConfigurationFiles.Options.WarnAboutMissingReflectionOrJNIMetadataElements.getValue()); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java index fbe6f8cc7821..f29eb05d5f5d 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 @@ -26,31 +26,46 @@ import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors; +import java.lang.reflect.Proxy; +import java.util.Arrays; import java.util.List; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import com.oracle.svm.core.TypeResult; +import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; +import com.oracle.svm.core.configure.NamedConfigurationTypeDescriptor; import com.oracle.svm.hosted.ImageClassLoader; +import com.oracle.svm.hosted.reflect.proxy.ProxyRegistry; public class ReflectionRegistryAdapter extends RegistryAdapter { private final RuntimeReflectionSupport reflectionSupport; + private final ProxyRegistry proxyRegistry; - ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, ImageClassLoader classLoader) { + ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, ProxyRegistry proxyRegistry, ImageClassLoader classLoader) { super(reflectionSupport, classLoader); this.reflectionSupport = reflectionSupport; + this.proxyRegistry = proxyRegistry; } @Override - public TypeResult> resolveType(ConfigurationCondition condition, String typeName, boolean allowPrimitives, boolean includeAllElements) { - TypeResult> result = super.resolveType(condition, typeName, allowPrimitives, includeAllElements); - if (!result.isPresent()) { + 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()); + } + } + + @Override + public TypeResult> resolveType(ConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean allowPrimitives, boolean includeAllElements) { + TypeResult> result = super.resolveType(condition, typeDescriptor, allowPrimitives, includeAllElements); + if (!result.isPresent() && typeDescriptor instanceof NamedConfigurationTypeDescriptor namedDescriptor) { Throwable classLookupException = result.getException(); if (classLookupException instanceof LinkageError) { - reflectionSupport.registerClassLookupException(condition, typeName, classLookupException); + reflectionSupport.registerClassLookupException(condition, namedDescriptor.name(), classLookupException); } else if (throwMissingRegistrationErrors() && classLookupException instanceof ClassNotFoundException) { - reflectionSupport.registerClassLookup(condition, typeName); + reflectionSupport.registerClassLookup(condition, namedDescriptor.name()); } } 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 dc08e634cde8..d8c484c14143 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,26 +27,33 @@ import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.ReflectionRegistry; import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; import com.oracle.svm.core.TypeResult; +import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; +import com.oracle.svm.core.configure.NamedConfigurationTypeDescriptor; +import com.oracle.svm.core.configure.ProxyConfigurationTypeDescriptor; import com.oracle.svm.core.configure.ReflectionConfigurationParserDelegate; +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 jdk.vm.ci.meta.MetaUtil; - public class RegistryAdapter implements ReflectionConfigurationParserDelegate> { private final ReflectionRegistry registry; private final ImageClassLoader classLoader; - public static RegistryAdapter create(ReflectionRegistry registry, ImageClassLoader classLoader) { + public static RegistryAdapter create(ReflectionRegistry registry, ProxyRegistry proxyRegistry, ImageClassLoader classLoader) { if (registry instanceof RuntimeReflectionSupport) { - return new ReflectionRegistryAdapter((RuntimeReflectionSupport) registry, classLoader); + return new ReflectionRegistryAdapter((RuntimeReflectionSupport) registry, proxyRegistry, classLoader); } else { return new RegistryAdapter(registry, classLoader); } @@ -63,18 +70,41 @@ public void registerType(ConfigurationCondition condition, Class type) { } @Override - public TypeResult> resolveType(ConfigurationCondition condition, String typeName, boolean allowPrimitives, boolean includeAllElements) { - String name = canonicalizeTypeName(typeName); - return classLoader.findClass(name, allowPrimitives); + public TypeResult> resolveType(ConfigurationCondition condition, ConfigurationTypeDescriptor typeDescriptor, boolean allowPrimitives, boolean includeAllElements) { + switch (typeDescriptor.getDescriptorType()) { + case NAMED -> { + return resolveNamedType(((NamedConfigurationTypeDescriptor) typeDescriptor), allowPrimitives); + } + case PROXY -> { + return resolveProxyType((ProxyConfigurationTypeDescriptor) typeDescriptor); + } + default -> { + throw VMError.shouldNotReachHere("Unknown type descriptor kind: %s", typeDescriptor.getDescriptorType()); + } + } } - public static String canonicalizeTypeName(String typeName) { - String name = typeName; - if (name.indexOf('[') != -1) { - /* accept "int[][]", "java.lang.String[]" */ - name = MetaUtil.internalNameToJava(MetaUtil.toInternalName(name), true, true); + private TypeResult> resolveNamedType(NamedConfigurationTypeDescriptor typeDescriptor, boolean allowPrimitives) { + return classLoader.findClass(typeDescriptor.name(), allowPrimitives); + } + + private TypeResult> resolveProxyType(ProxyConfigurationTypeDescriptor typeDescriptor) { + String typeName = typeDescriptor.toString(); + List>> interfaceResults = Arrays.stream(typeDescriptor.interfaceNames()).map(name -> resolveNamedType(new NamedConfigurationTypeDescriptor(name), false)).toList(); + List> interfaces = new ArrayList<>(); + for (TypeResult> intf : interfaceResults) { + if (!intf.isPresent()) { + return TypeResult.forException(typeName, intf.getException()); + } + interfaces.add(intf.get()); + } + try { + DynamicProxyRegistry proxyRegistry = ImageSingletons.lookup(DynamicProxyRegistry.class); + Class proxyClass = proxyRegistry.getProxyClassHosted(interfaces.toArray(Class[]::new)); + return TypeResult.forType(typeName, proxyClass); + } catch (Throwable t) { + return TypeResult.forException(typeName, t); } - return name; } @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 a79f36b4dc2f..f19a46514ddc 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 @@ -98,6 +98,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.proxy.DynamicProxyFeature; import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; import com.oracle.svm.util.ReflectionUtil; @@ -185,7 +186,7 @@ private void abortIfSealed() { @Override public List> getRequiredFeatures() { // Ensure that KnownOffsets is fully initialized before we access it - return List.of(KnownOffsetsFeature.class); + return List.of(KnownOffsetsFeature.class, DynamicProxyFeature.class); } @Override @@ -199,7 +200,7 @@ public void afterRegistration(AfterRegistrationAccess arg) { ConfigurationConditionResolver conditionResolver = new NativeImageConditionResolver(access.getImageClassLoader(), ClassInitializationSupport.singleton()); - ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(conditionResolver, runtimeSupport, access.getImageClassLoader()); + ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(conditionResolver, runtimeSupport, null, access.getImageClassLoader()); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurations(parser, access.getImageClassLoader(), "JNI", ConfigurationFiles.Options.JNIConfigurationFiles, ConfigurationFiles.Options.JNIConfigurationResources, ConfigurationFile.JNI.getFileName()); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/NativeImageConditionResolver.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/NativeImageConditionResolver.java index cdf0ae9c0624..27565d7fe248 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 @@ -29,9 +29,9 @@ import com.oracle.svm.core.TypeResult; import com.oracle.svm.core.configure.ConfigurationConditionResolver; +import com.oracle.svm.core.configure.ConfigurationTypeDescriptor; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; -import com.oracle.svm.hosted.config.RegistryAdapter; public class NativeImageConditionResolver implements ConfigurationConditionResolver { private final ImageClassLoader classLoader; @@ -44,7 +44,7 @@ public NativeImageConditionResolver(ImageClassLoader classLoader, ClassInitializ @Override public TypeResult resolveCondition(UnresolvedConfigurationCondition unresolvedCondition) { - String canonicalizedName = RegistryAdapter.canonicalizeTypeName(unresolvedCondition.getTypeName()); + String canonicalizedName = ConfigurationTypeDescriptor.canonicalizeTypeName(unresolvedCondition.getTypeName()); TypeResult> clazz = classLoader.findClass(canonicalizedName); return clazz.map(type -> { /* 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 1317ba6ed320..27d3c3c42631 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 @@ -31,6 +31,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -39,6 +40,7 @@ import org.graalvm.nativeimage.AnnotationAccess; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.CFunctionPointer; +import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.impl.AnnotationExtractor; import org.graalvm.nativeimage.impl.ConfigurationCondition; @@ -82,6 +84,8 @@ import com.oracle.svm.hosted.meta.HostedField; 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; @@ -250,6 +254,11 @@ private MethodPointer asMethodPointer(ResolvedJavaMethod method) { return new MethodPointer(aMethod); } + @Override + public List> getRequiredFeatures() { + return Collections.singletonList(DynamicProxyFeature.class); + } + @Override public void afterRegistration(AfterRegistrationAccess access) { ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, null, false, "java.base", "jdk.internal.reflect"); @@ -267,7 +276,8 @@ public void duringSetup(DuringSetupAccess a) { aUniverse = access.getUniverse(); var conditionResolver = new NativeImageConditionResolver(access.getImageClassLoader(), ClassInitializationSupport.singleton()); reflectionData.duringSetup(access.getMetaAccess(), aUniverse); - ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(conditionResolver, reflectionData, access.getImageClassLoader()); + ProxyRegistry proxyRegistry = ImageSingletons.lookup(ProxyRegistry.class); + ReflectionConfigurationParser> parser = ConfigurationParserUtils.create(conditionResolver, reflectionData, proxyRegistry, access.getImageClassLoader()); loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurations(parser, access.getImageClassLoader(), "reflection", ConfigurationFiles.Options.ReflectionConfigurationFiles, ConfigurationFiles.Options.ReflectionConfigurationResources, ConfigurationFile.REFLECTION.getFileName()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java index 6dc88f832362..e4c64e099cd4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/DynamicProxyFeature.java @@ -25,11 +25,8 @@ package com.oracle.svm.hosted.reflect.proxy; import java.lang.reflect.Field; -import java.util.Collections; -import java.util.List; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.graalvm.nativeimage.impl.RuntimeProxyCreationSupport; @@ -48,18 +45,12 @@ import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import com.oracle.svm.hosted.config.ConfigurationParserUtils; import com.oracle.svm.hosted.reflect.NativeImageConditionResolver; -import com.oracle.svm.hosted.reflect.ReflectionFeature; @AutomaticallyRegisteredFeature public final class DynamicProxyFeature implements InternalFeature { private int loadedConfigurations; private Field proxyCacheField; - @Override - public List> getRequiredFeatures() { - return Collections.singletonList(ReflectionFeature.class); - } - @Override public void duringSetup(DuringSetupAccess a) { DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/ProxyRegistry.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/ProxyRegistry.java index 525fd02273b9..1a896f12aae9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/ProxyRegistry.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/proxy/ProxyRegistry.java @@ -57,7 +57,7 @@ public void accept(ConfigurationCondition condition, List proxies) { public Class createProxyClassForSerialization(List proxies) { Class[] interfaces = checkIfInterfacesAreValid(proxies); if (interfaces != null) { - return dynamicProxySupport.createProxyClassForSerialization(interfaces); + return dynamicProxySupport.getProxyClassHosted(interfaces); } return null;