Skip to content

Commit 69765f3

Browse files
committed
Throw missing metadata exceptions
1 parent a6f3804 commit 69765f3

File tree

24 files changed

+1039
-322
lines changed

24 files changed

+1039
-322
lines changed

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java

Lines changed: 57 additions & 1 deletion
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,64 @@ 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+
5898
@SuppressWarnings("unused")
5999
default void registerClassLookupException(ConfigurationCondition condition, String typeName, Throwable t) {
60100
}
61101

102+
@SuppressWarnings("unused")
103+
default void registerNegativeClassLookup(ConfigurationCondition condition, String typeName) {
104+
}
105+
106+
@SuppressWarnings("unused")
107+
default void registerNegativeFieldLookup(ConfigurationCondition condition, Class<?> declaringClass, String fieldName) {
108+
}
109+
110+
@SuppressWarnings("unused")
111+
default void registerNegativeMethodLookup(ConfigurationCondition condition, Class<?> declaringClass, String methodName, Class<?>... parameterTypes) {
112+
}
113+
114+
@SuppressWarnings("unused")
115+
default void registerNegativeConstructorLookup(ConfigurationCondition condition, Class<?> declaringClass, Class<?>... parameterTypes) {
116+
}
117+
62118
}
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.metadata;
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 MissingReflectionMetadataException extends RuntimeException {
53+
private static final long serialVersionUID = 1L;
54+
55+
public MissingReflectionMetadataException(String message) {
56+
super(message);
57+
}
58+
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMetaAccess.java

Lines changed: 11 additions & 0 deletions
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) {

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@
4141
import java.util.function.Function;
4242

4343
import org.graalvm.compiler.debug.GraalError;
44+
import org.graalvm.nativeimage.ImageSingletons;
4445
import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;
46+
import org.graalvm.nativeimage.impl.ConfigurationCondition;
47+
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
4548
import org.graalvm.word.WordBase;
4649

4750
import com.oracle.graal.pointsto.BigBang;
@@ -577,6 +580,9 @@ protected void onReachable() {
577580
* of explicit registration of types for reflection.
578581
*/
579582
registerAsAllocated("All array types are marked as instantiated eagerly.");
583+
} else if (isAnnotation()) {
584+
/* getDeclaredMethods is called in the AnnotationType constructor */
585+
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredMethodsQuery(ConfigurationCondition.alwaysTrue(), true, getJavaClass());
580586
}
581587
ensureOnTypeReachableTaskDone();
582588
}

substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java

Lines changed: 10 additions & 41 deletions
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,6 +265,10 @@ 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
}
@@ -332,7 +303,7 @@ private static boolean handleGetField(JNIEnvironment jni, JNIObjectHandle thread
332303
declaring = nullHandle();
333304
}
334305
}
335-
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, result.notEqual(nullHandle()),
306+
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, true,
336307
state.getFullStackTraceOrNull(), fromJniString(jni, name));
337308
return true;
338309
}
@@ -395,12 +366,8 @@ private static boolean getConstructor(JNIEnvironment jni, JNIObjectHandle thread
395366
JNIObjectHandle callerClass = state.getDirectCallerClass();
396367
JNIObjectHandle self = getReceiver(thread);
397368
JNIObjectHandle paramTypesHandle = getObjectArgument(thread, 1);
398-
JNIObjectHandle result = Support.callObjectMethodL(jni, self, bp.method, paramTypesHandle);
399-
if (clearException(jni)) {
400-
result = nullHandle();
401-
}
402369
Object paramTypes = getClassArrayNames(jni, paramTypesHandle);
403-
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), nullHandle(), callerClass, bp.specification.methodName, nullHandle().notEqual(result), state.getFullStackTraceOrNull(),
370+
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), nullHandle(), callerClass, bp.specification.methodName, true, state.getFullStackTraceOrNull(),
404371
paramTypes);
405372
return true;
406373
}
@@ -431,7 +398,7 @@ private static boolean handleGetMethod(JNIEnvironment jni, JNIObjectHandle threa
431398
}
432399
String name = fromJniString(jni, nameHandle);
433400
Object paramTypes = getClassArrayNames(jni, paramTypesHandle);
434-
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, result.notEqual(nullHandle()),
401+
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, true,
435402
state.getFullStackTraceOrNull(), name, paramTypes);
436403
return true;
437404
}
@@ -1799,6 +1766,8 @@ private interface BreakpointHandler {
17991766
optionalBrk("java/lang/invoke/MethodType", "fromMethodDescriptorString",
18001767
"(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;",
18011768
BreakpointInterceptor::methodTypeFromDescriptor),
1769+
optionalBrk("java/lang/Class", "getRecordComponents", "()[Ljava/lang/reflect/RecordComponent;",
1770+
BreakpointInterceptor::getRecordComponents),
18021771
optionalBrk("java/lang/Class", "getPermittedSubclasses", "()[Ljava/lang/Class;",
18031772
BreakpointInterceptor::getPermittedSubclasses)
18041773
};

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ static ConfigurationType copyAndMerge(ConfigurationType type, ConfigurationType
9292
private Map<ConfigurationMethod, ConfigurationMemberInfo> methods;
9393

9494
private boolean allDeclaredClasses;
95+
private boolean allRecordComponents;
9596
private boolean allPermittedSubclasses;
9697
private boolean allPublicClasses;
9798
private boolean allDeclaredFields;
@@ -266,6 +267,7 @@ private void removeMethods(ConfigurationType other) {
266267
private void setFlagsFromOther(ConfigurationType other, BiPredicate<Boolean, Boolean> flagPredicate,
267268
BiFunction<ConfigurationMemberAccessibility, ConfigurationMemberAccessibility, ConfigurationMemberAccessibility> accessCombiner) {
268269
allDeclaredClasses = flagPredicate.test(allDeclaredClasses, other.allDeclaredClasses);
270+
allRecordComponents = flagPredicate.test(allRecordComponents, other.allRecordComponents);
269271
allPermittedSubclasses = flagPredicate.test(allPermittedSubclasses, other.allPermittedSubclasses);
270272
allPublicClasses = flagPredicate.test(allPublicClasses, other.allPublicClasses);
271273
allDeclaredFields = flagPredicate.test(allDeclaredFields, other.allDeclaredFields);
@@ -282,7 +284,7 @@ private boolean isEmpty() {
282284
}
283285

284286
private boolean allFlagsFalse() {
285-
return !(allDeclaredClasses || allPermittedSubclasses || allPublicClasses || allDeclaredFields || allPublicFields ||
287+
return !(allDeclaredClasses || allRecordComponents || allPermittedSubclasses || allPublicClasses || allDeclaredFields || allPublicFields ||
286288
allDeclaredMethodsAccess != ConfigurationMemberAccessibility.NONE || allPublicMethodsAccess != ConfigurationMemberAccessibility.NONE ||
287289
allDeclaredConstructorsAccess != ConfigurationMemberAccessibility.NONE || allPublicConstructorsAccess != ConfigurationMemberAccessibility.NONE);
288290
}
@@ -367,6 +369,10 @@ public synchronized void setAllDeclaredClasses() {
367369
allDeclaredClasses = true;
368370
}
369371

372+
public synchronized void setAllRecordComponents() {
373+
allRecordComponents = true;
374+
}
375+
370376
public synchronized void setAllPermittedSubclasses() {
371377
allPermittedSubclasses = true;
372378
}
@@ -430,6 +436,7 @@ public synchronized void printJson(JsonWriter writer) throws IOException {
430436
optionallyPrintJsonBoolean(writer, allDeclaredConstructorsAccess == ConfigurationMemberAccessibility.ACCESSED, "allDeclaredConstructors");
431437
optionallyPrintJsonBoolean(writer, allPublicConstructorsAccess == ConfigurationMemberAccessibility.ACCESSED, "allPublicConstructors");
432438
optionallyPrintJsonBoolean(writer, allDeclaredClasses, "allDeclaredClasses");
439+
optionallyPrintJsonBoolean(writer, allRecordComponents, "allRecordComponents");
433440
optionallyPrintJsonBoolean(writer, allPermittedSubclasses, "allPermittedSubclasses");
434441
optionallyPrintJsonBoolean(writer, allPublicClasses, "allPublicClasses");
435442
optionallyPrintJsonBoolean(writer, allDeclaredMethodsAccess == ConfigurationMemberAccessibility.QUERIED, "queryAllDeclaredMethods");
@@ -531,6 +538,10 @@ public static boolean haveAllDeclaredClasses(ConfigurationType type) {
531538
return type.allDeclaredClasses;
532539
}
533540

541+
public static boolean haveAllRecordComponents(ConfigurationType type) {
542+
return type.allRecordComponents;
543+
}
544+
534545
public static boolean haveAllPermittedSubclasses(ConfigurationType type) {
535546
return type.allPermittedSubclasses;
536547
}

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ParserConfigurationAdapter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ public void registerDeclaredClasses(ConfigurationType type) {
103103
type.setAllDeclaredClasses();
104104
}
105105

106+
@Override
107+
public void registerRecordComponents(ConfigurationType type) {
108+
type.setAllRecordComponents();
109+
}
110+
106111
@Override
107112
public void registerPermittedSubclasses(ConfigurationType type) {
108113
type.setAllPermittedSubclasses();

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ public void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configur
149149
configuration.getOrCreateType(condition, clazz).setAllDeclaredClasses();
150150
break;
151151
}
152+
case "getRecordComponents": {
153+
configuration.getOrCreateType(condition, clazz).setAllRecordComponents();
154+
break;
155+
}
152156
case "getPermittedSubclasses": {
153157
configuration.getOrCreateType(condition, clazz).setAllPermittedSubclasses();
154158
break;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public final class ReflectionConfigurationParser<T> extends ConfigurationParser
4848
private final ReflectionConfigurationParserDelegate<T> delegate;
4949
private static final List<String> OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS = Arrays.asList("allDeclaredConstructors", "allPublicConstructors",
5050
"allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields",
51-
"allDeclaredClasses", "allPermittedSubclasses", "allPublicClasses", "methods", "queriedMethods", "fields", CONDITIONAL_KEY,
51+
"allDeclaredClasses", "allRecordComponents", "allPermittedSubclasses", "allPublicClasses", "methods", "queriedMethods", "fields", CONDITIONAL_KEY,
5252
"queryAllDeclaredConstructors", "queryAllPublicConstructors", "queryAllDeclaredMethods", "queryAllPublicMethods", "unsafeAllocated");
5353

5454
public ReflectionConfigurationParser(ReflectionConfigurationParserDelegate<T> delegate) {
@@ -132,6 +132,11 @@ private void parseClass(EconomicMap<String, Object> data) {
132132
delegate.registerDeclaredClasses(clazz);
133133
}
134134
break;
135+
case "allRecordComponents":
136+
if (asBoolean(value, "allRecordComponents")) {
137+
delegate.registerRecordComponents(clazz);
138+
}
139+
break;
135140
case "allPermittedSubclasses":
136141
if (asBoolean(value, "allPermittedSubclasses")) {
137142
delegate.registerPermittedSubclasses(clazz);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public interface ReflectionConfigurationParserDelegate<T> {
4242

4343
void registerDeclaredClasses(T type);
4444

45+
void registerRecordComponents(T type);
46+
4547
void registerPermittedSubclasses(T type);
4648

4749
void registerPublicFields(T type);

0 commit comments

Comments
 (0)