Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-47365] Throw missing registration errors for proxy classes #7060

Merged
merged 1 commit into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions substratevm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, "<comma-separated-config-files>");
private static final String proxyConfigResourcesOption = SubstrateOptionsParser.commandArgument(ConfigurationFiles.Options.DynamicProxyConfigurationResources,
"<comma-separated-config-resources>");

public static final Pattern PROXY_CLASS_NAME_PATTERN = Pattern.compile(".*\\$Proxy[0-9]+");

static final class ProxyCacheKey {
Expand Down Expand Up @@ -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);
Expand Down