Skip to content

Commit 737af06

Browse files
committed
Throw missing reflection registration errors
1 parent a6f3804 commit 737af06

File tree

29 files changed

+1318
-324
lines changed

29 files changed

+1318
-324
lines changed

Diff for: sdk/src/org.graalvm.nativeimage/snapshot.sigtest

+14
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ meth public static <%0 extends java.lang.Enum<{%%0}>> {%%0} valueOf(java.lang.Cl
3737
supr java.lang.Object
3838
hfds name,ordinal
3939

40+
CLSS public java.lang.Error
41+
cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean)
42+
cons public init()
43+
cons public init(java.lang.String)
44+
cons public init(java.lang.String,java.lang.Throwable)
45+
cons public init(java.lang.Throwable)
46+
supr java.lang.Throwable
47+
hfds serialVersionUID
48+
4049
CLSS public java.lang.Exception
4150
cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean)
4251
cons public init()
@@ -222,6 +231,11 @@ meth public abstract void fatalError()
222231
meth public abstract void flush()
223232
meth public abstract void log(org.graalvm.nativeimage.c.type.CCharPointer,org.graalvm.word.UnsignedWord)
224233

234+
CLSS public final org.graalvm.nativeimage.MissingReflectionRegistrationError
235+
cons public init(java.lang.String)
236+
supr java.lang.Error
237+
hfds serialVersionUID
238+
225239
CLSS public abstract interface org.graalvm.nativeimage.ObjectHandle
226240
intf org.graalvm.word.ComparableWord
227241

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package org.graalvm.nativeimage;
42+
43+
/**
44+
* This exception is thrown when a reflective query (such as
45+
* {@link Class#getMethod(String, Class[])}) tries to access an element that was not present in the
46+
* reflection metadata used to build the image. This exception will also be thrown if the requested
47+
* element doesn't exist on the classpath used to build the image and the arguments to the query
48+
* were not registered in the reflection metadata. This ensures composability of the metadata (i.e.
49+
* a change in the reachability of an element will not cause changes in the behavior of the
50+
* application).
51+
*/
52+
public final class MissingReflectionRegistrationError extends Error {
53+
private static final long serialVersionUID = 1L;
54+
55+
public MissingReflectionRegistrationError(String message) {
56+
super(message);
57+
}
58+
}

Diff for: sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java

+65-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -55,8 +55,72 @@ default void register(ConfigurationCondition condition, Class<?>... classes) {
5555

5656
void register(ConfigurationCondition condition, boolean finalIsWritable, Field... fields);
5757

58+
default void registerAllMethodsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz) {
59+
register(condition, queriedOnly, clazz.getMethods());
60+
}
61+
62+
default void registerAllDeclaredMethodsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz) {
63+
register(condition, queriedOnly, clazz.getDeclaredMethods());
64+
}
65+
66+
default void registerAllFieldsQuery(ConfigurationCondition condition, Class<?> clazz) {
67+
register(condition, false, clazz.getFields());
68+
}
69+
70+
default void registerAllDeclaredFieldsQuery(ConfigurationCondition condition, Class<?> clazz) {
71+
register(condition, false, clazz.getDeclaredFields());
72+
}
73+
74+
default void registerAllConstructorsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz) {
75+
register(condition, queriedOnly, clazz.getConstructors());
76+
}
77+
78+
default void registerAllDeclaredConstructorsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz) {
79+
register(condition, queriedOnly, clazz.getDeclaredConstructors());
80+
}
81+
82+
default void registerAllClassesQuery(ConfigurationCondition condition, Class<?> clazz) {
83+
register(condition, clazz.getClasses());
84+
}
85+
86+
default void registerAllDeclaredClassesQuery(ConfigurationCondition condition, Class<?> clazz) {
87+
register(condition, clazz.getDeclaredClasses());
88+
}
89+
90+
@SuppressWarnings("unused")
91+
default void registerAllRecordComponentsQuery(ConfigurationCondition condition, Class<?> clazz) {
92+
}
93+
94+
@SuppressWarnings("unused")
95+
default void registerAllPermittedSubclassesQuery(ConfigurationCondition condition, Class<?> clazz) {
96+
}
97+
98+
@SuppressWarnings("unused")
99+
default void registerAllNestMembersQuery(ConfigurationCondition condition, Class<?> clazz) {
100+
}
101+
102+
@SuppressWarnings("unused")
103+
default void registerAllSignersQuery(ConfigurationCondition condition, Class<?> clazz) {
104+
}
105+
58106
@SuppressWarnings("unused")
59107
default void registerClassLookupException(ConfigurationCondition condition, String typeName, Throwable t) {
60108
}
61109

110+
@SuppressWarnings("unused")
111+
default void registerNegativeClassLookup(ConfigurationCondition condition, String typeName) {
112+
}
113+
114+
@SuppressWarnings("unused")
115+
default void registerNegativeFieldLookup(ConfigurationCondition condition, Class<?> declaringClass, String fieldName) {
116+
}
117+
118+
@SuppressWarnings("unused")
119+
default void registerNegativeMethodLookup(ConfigurationCondition condition, Class<?> declaringClass, String methodName, Class<?>... parameterTypes) {
120+
}
121+
122+
@SuppressWarnings("unused")
123+
default void registerNegativeConstructorLookup(ConfigurationCondition condition, Class<?> declaringClass, Class<?>... parameterTypes) {
124+
}
125+
62126
}

Diff for: substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandaloneHost.java

+4
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ public void onTypeReachable(AnalysisType type) {
8080
*/
8181
}
8282

83+
@Override
84+
public void onTypeInstantiated(AnalysisType newValue) {
85+
}
86+
8387
@Override
8488
public GraphBuilderPhase.Instance createGraphBuilderPhase(HostedProviders providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts,
8589
IntrinsicContext initialIntrinsicContext) {

Diff for: substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java

+7
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ public void checkType(ResolvedJavaType type, AnalysisUniverse universe) {
152152
*/
153153
public abstract void onTypeReachable(AnalysisType newValue);
154154

155+
/**
156+
* Run initialization tasks for a newly instantiated {@link AnalysisType}.
157+
*
158+
* @param newValue the type to initialize
159+
*/
160+
public abstract void onTypeInstantiated(AnalysisType newValue);
161+
155162
/**
156163
* Check if an {@link AnalysisType} is initialized.
157164
*/

Diff for: substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccess.java

+11
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ public AnalysisType lookupJavaType(Class<?> clazz) {
5252
return (AnalysisType) super.lookupJavaType(clazz);
5353
}
5454

55+
@Override
56+
public AnalysisType[] lookupJavaTypes(Class<?>[] classes) {
57+
AnalysisType[] result = new AnalysisType[classes.length];
58+
59+
for (int i = 0; i < result.length; ++i) {
60+
result[i] = this.lookupJavaType(classes[i]);
61+
}
62+
63+
return result;
64+
}
65+
5566
public Optional<AnalysisType> optionalLookupJavaType(Class<?> clazz) {
5667
AnalysisType result = (AnalysisType) getTypeCacheEntry(clazz);
5768
if (result != null) {

Diff for: substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java

+1
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ public void onFieldAccessed(AnalysisField field) {
692692
}
693693

694694
public void onTypeInstantiated(AnalysisType type, UsageKind usage) {
695+
hostVM.onTypeInstantiated(type);
695696
bb.onTypeInstantiated(type, usage);
696697
}
697698

Diff for: substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java

+23-42
Original file line numberDiff line numberDiff line change
@@ -213,40 +213,7 @@ private static boolean forName(JNIEnvironment jni, JNIObjectHandle thread, Break
213213
if (className == null) {
214214
return false; /* No point in tracing this. */
215215
}
216-
217-
boolean classLoaderValid = true;
218-
WordPointer classLoaderPtr = StackValue.get(WordPointer.class);
219-
if (bp.method == agent.handles().javaLangClassForName3) {
220-
assert thread.notEqual(nullHandle()) || Support.jvmtiVersion() != JvmtiInterface.JVMTI_VERSION_19 : "JDK-8292657";
221-
classLoaderValid = (jvmtiFunctions().GetLocalObject().invoke(jvmtiEnv(), thread, 0, 2, classLoaderPtr) == JvmtiError.JVMTI_ERROR_NONE);
222-
} else {
223-
classLoaderPtr.write(nullHandle());
224-
if (callerClass.notEqual(nullHandle())) {
225-
/*
226-
* NOTE: we use our direct caller class, but this class might be skipped over by
227-
* Class.forName(nameOnly) in its security stackwalk for @CallerSensitive, leading
228-
* to different behavior of our call and the original call.
229-
*/
230-
classLoaderValid = (jvmtiFunctions().GetClassLoader().invoke(jvmtiEnv(), callerClass, classLoaderPtr) == JvmtiError.JVMTI_ERROR_NONE);
231-
}
232-
}
233-
Object result = Tracer.UNKNOWN_VALUE;
234-
if (classLoaderValid) {
235-
/*
236-
* Even if the original call requested class initialization, disable it because
237-
* recursion checks keep us from seeing events of interest during initialization.
238-
*/
239-
int initialize = 0;
240-
Support.callStaticObjectMethodLIL(jni, bp.clazz, agent.handles().javaLangClassForName3, name, initialize, classLoaderPtr.read());
241-
JNIObjectHandle exception = handleException(jni, true);
242-
/*
243-
* To throw the right exceptions at run time, we need to ensure that the image builder
244-
* sees them, so we trace all calls except those that throw a ClassNotFoundException.
245-
*/
246-
result = exception.equal(nullHandle()) || !jniFunctions().getIsInstanceOf().invoke(jni, exception, agent.handles().javaLangClassNotFoundException);
247-
}
248-
249-
traceReflectBreakpoint(jni, bp.clazz, nullHandle(), callerClass, bp.specification.methodName, result, state.getFullStackTraceOrNull(), className);
216+
traceReflectBreakpoint(jni, bp.clazz, nullHandle(), callerClass, bp.specification.methodName, null, state.getFullStackTraceOrNull(), className);
250217
return true;
251218
}
252219

@@ -298,10 +265,22 @@ private static boolean getDeclaredClasses(JNIEnvironment jni, JNIObjectHandle th
298265
return handleGetClasses(jni, thread, bp, state);
299266
}
300267

268+
private static boolean getRecordComponents(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
269+
return handleGetClasses(jni, thread, bp, state);
270+
}
271+
301272
private static boolean getPermittedSubclasses(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
302273
return handleGetClasses(jni, thread, bp, state);
303274
}
304275

276+
private static boolean getNestMembers(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
277+
return handleGetClasses(jni, thread, bp, state);
278+
}
279+
280+
private static boolean getSigners(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
281+
return handleGetClasses(jni, thread, bp, state);
282+
}
283+
305284
private static boolean handleGetClasses(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
306285
JNIObjectHandle callerClass = state.getDirectCallerClass();
307286
JNIObjectHandle self = getReceiver(thread);
@@ -332,7 +311,7 @@ private static boolean handleGetField(JNIEnvironment jni, JNIObjectHandle thread
332311
declaring = nullHandle();
333312
}
334313
}
335-
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, result.notEqual(nullHandle()),
314+
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, true,
336315
state.getFullStackTraceOrNull(), fromJniString(jni, name));
337316
return true;
338317
}
@@ -395,12 +374,8 @@ private static boolean getConstructor(JNIEnvironment jni, JNIObjectHandle thread
395374
JNIObjectHandle callerClass = state.getDirectCallerClass();
396375
JNIObjectHandle self = getReceiver(thread);
397376
JNIObjectHandle paramTypesHandle = getObjectArgument(thread, 1);
398-
JNIObjectHandle result = Support.callObjectMethodL(jni, self, bp.method, paramTypesHandle);
399-
if (clearException(jni)) {
400-
result = nullHandle();
401-
}
402377
Object paramTypes = getClassArrayNames(jni, paramTypesHandle);
403-
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), nullHandle(), callerClass, bp.specification.methodName, nullHandle().notEqual(result), state.getFullStackTraceOrNull(),
378+
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), nullHandle(), callerClass, bp.specification.methodName, true, state.getFullStackTraceOrNull(),
404379
paramTypes);
405380
return true;
406381
}
@@ -431,7 +406,7 @@ private static boolean handleGetMethod(JNIEnvironment jni, JNIObjectHandle threa
431406
}
432407
String name = fromJniString(jni, nameHandle);
433408
Object paramTypes = getClassArrayNames(jni, paramTypesHandle);
434-
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, result.notEqual(nullHandle()),
409+
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, true,
435410
state.getFullStackTraceOrNull(), name, paramTypes);
436411
return true;
437412
}
@@ -1799,8 +1774,14 @@ private interface BreakpointHandler {
17991774
optionalBrk("java/lang/invoke/MethodType", "fromMethodDescriptorString",
18001775
"(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;",
18011776
BreakpointInterceptor::methodTypeFromDescriptor),
1777+
optionalBrk("java/lang/Class", "getRecordComponents", "()[Ljava/lang/reflect/RecordComponent;",
1778+
BreakpointInterceptor::getRecordComponents),
18021779
optionalBrk("java/lang/Class", "getPermittedSubclasses", "()[Ljava/lang/Class;",
1803-
BreakpointInterceptor::getPermittedSubclasses)
1780+
BreakpointInterceptor::getPermittedSubclasses),
1781+
optionalBrk("java/lang/Class", "getNestMembers", "()[Ljava/lang/Class;",
1782+
BreakpointInterceptor::getNestMembers),
1783+
optionalBrk("java/lang/Class", "getSigners", "()[Ljava/lang/Object;",
1784+
BreakpointInterceptor::getSigners)
18041785
};
18051786

18061787
private static boolean allocateInstance(JNIEnvironment jni, JNIObjectHandle thread, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state) {

0 commit comments

Comments
 (0)