From 57101f072d09130a2ebf2ac3269ba51c4086bc55 Mon Sep 17 00:00:00 2001 From: Loic Ottet Date: Wed, 19 Jul 2023 14:48:30 +0200 Subject: [PATCH] Throw missing registration errors for proxy classes --- substratevm/CHANGELOG.md | 1 + .../MissingReflectionRegistrationUtils.java | 25 +++++++++++++++++-- .../reflect/proxy/DynamicProxySupport.java | 14 ++--------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index b193a081f039..4dc27b2a5144 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -9,6 +9,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-39406) Simulation of class initializer: Class initializer of classes that are not marked for initialization at image build time are simulated at image build time to avoid executing them at image run time. * (GR-46392) Add `--parallelism` option to control how many threads are used by the build process. * (GR-46392) Add build resources section to the build output that shows the memory and thread limits of the build process. +* (GR-47365) Throw `MissingReflectionRegistrationError` when attempting to create a proxy class without having it registered at build-time, instead of a `VMError`. ## Version 23.0.0 * (GR-40187) Report invalid use of SVM specific classes on image class- or module-path as error. As a temporary workaround, `-H:+AllowDeprecatedBuilderClassesOnImageClasspath` allows turning the error into a warning. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MissingReflectionRegistrationUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MissingReflectionRegistrationUtils.java index 03a4e364d29e..19e697c736a9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MissingReflectionRegistrationUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MissingReflectionRegistrationUtils.java @@ -29,6 +29,8 @@ import java.lang.reflect.Executable; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; import java.util.Map; import java.util.Set; import java.util.StringJoiner; @@ -110,10 +112,28 @@ public static void forBulkQuery(Class declaringClass, String methodName) { report(exception); } + public static void forProxy(Class... interfaces) { + MissingReflectionRegistrationError exception = new MissingReflectionRegistrationError(errorMessage("access the proxy class inheriting", + Arrays.toString(Arrays.stream(interfaces).map(Class::getTypeName).toArray()), + "The order of interfaces used to create proxies matters.", "dynamic-proxy"), + Proxy.class, null, null, interfaces); + report(exception); + /* + * If report doesn't throw, we throw the exception anyway since this is a Native + * Image-specific error that is unrecoverable in any case. + */ + throw exception; + } + private static String errorMessage(String failedAction, String elementDescriptor) { + return errorMessage(failedAction, elementDescriptor, null, "reflection"); + } + + private static String errorMessage(String failedAction, String elementDescriptor, String note, String helpLink) { return "The program tried to reflectively " + failedAction + " " + elementDescriptor + - " without it being registered for runtime reflection. Add it to the reflection metadata to solve this problem. " + - "See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection for help."; + " without it being registered for runtime reflection. Add " + elementDescriptor + " to the " + helpLink + " metadata to solve this problem. " + + (note != null ? "Note: " + note + " " : "") + + "See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#" + helpLink + " for help."; } private static final int CONTEXT_LINES = 4; @@ -213,6 +233,7 @@ private static void printLine(StringBuilder sb, Object object) { "newInstance"), Method.class.getTypeName(), Set.of("invoke"), Constructor.class.getTypeName(), Set.of("newInstance"), + Proxy.class.getTypeName(), Set.of("getProxyClass", "newProxyInstance"), "java.lang.reflect.ReflectAccess", Set.of("newInstance"), "jdk.internal.access.JavaLangAccess", Set.of("getDeclaredPublicMethods"), "sun.misc.Unsafe", Set.of("allocateInstance"), 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 4ebaaec3b8f2..b1a3d5f075af 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 @@ -38,23 +38,17 @@ import org.graalvm.nativeimage.hosted.RuntimeReflection; import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.configure.ConfigurationFiles; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.PredefinedClassesSupport; import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; -import com.oracle.svm.core.option.SubstrateOptionsParser; +import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils; import com.oracle.svm.core.util.ImageHeapMap; -import com.oracle.svm.core.util.VMError; import com.oracle.svm.util.ClassUtil; import com.oracle.svm.util.LogUtils; import com.oracle.svm.util.ReflectionUtil; public class DynamicProxySupport implements DynamicProxyRegistry { - private static final String proxyConfigFilesOption = SubstrateOptionsParser.commandArgument(ConfigurationFiles.Options.DynamicProxyConfigurationFiles, ""); - private static final String proxyConfigResourcesOption = SubstrateOptionsParser.commandArgument(ConfigurationFiles.Options.DynamicProxyConfigurationResources, - ""); - public static final Pattern PROXY_CLASS_NAME_PATTERN = Pattern.compile(".*\\$Proxy[0-9]+"); static final class ProxyCacheKey { @@ -179,11 +173,7 @@ public Class getProxyClass(ClassLoader loader, Class... interfaces) { ProxyCacheKey key = new ProxyCacheKey(interfaces); Object clazzOrError = proxyCache.get(key); if (clazzOrError == null) { - throw VMError.unsupportedFeature("Proxy class defined by the following sequence of interfaces " + Arrays.toString(interfaces) + " not found. " + - "Proxy classes need to be defined at image build time by specifying the list of interfaces that they implement. " + - "To define proxy classes use " + proxyConfigFilesOption + " and " + proxyConfigResourcesOption + " options. " + - "Note: The order of interfaces used to create proxies matters. " + - "Proxies with the same set of interfaces specified in a different order do not resolve to the same class and thus require individual configuration entries."); + MissingReflectionRegistrationUtils.forProxy(interfaces); } if (clazzOrError instanceof Throwable) { throw new GraalError((Throwable) clazzOrError);