Skip to content

Commit

Permalink
Throw missing reflection registration errors
Browse files Browse the repository at this point in the history
  • Loading branch information
loicottet committed Mar 7, 2023
1 parent a6f3804 commit 737af06
Show file tree
Hide file tree
Showing 29 changed files with 1,318 additions and 324 deletions.
14 changes: 14 additions & 0 deletions sdk/src/org.graalvm.nativeimage/snapshot.sigtest
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ meth public static <%0 extends java.lang.Enum<{%%0}>> {%%0} valueOf(java.lang.Cl
supr java.lang.Object
hfds name,ordinal

CLSS public java.lang.Error
cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean)
cons public init()
cons public init(java.lang.String)
cons public init(java.lang.String,java.lang.Throwable)
cons public init(java.lang.Throwable)
supr java.lang.Throwable
hfds serialVersionUID

CLSS public java.lang.Exception
cons protected init(java.lang.String,java.lang.Throwable,boolean,boolean)
cons public init()
Expand Down Expand Up @@ -222,6 +231,11 @@ meth public abstract void fatalError()
meth public abstract void flush()
meth public abstract void log(org.graalvm.nativeimage.c.type.CCharPointer,org.graalvm.word.UnsignedWord)

CLSS public final org.graalvm.nativeimage.MissingReflectionRegistrationError
cons public init(java.lang.String)
supr java.lang.Error
hfds serialVersionUID

CLSS public abstract interface org.graalvm.nativeimage.ObjectHandle
intf org.graalvm.word.ComparableWord

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.graalvm.nativeimage;

/**
* This exception is thrown when a reflective query (such as
* {@link Class#getMethod(String, Class[])}) tries to access an element that was not present in the
* reflection metadata used to build the image. This exception will also be thrown if the requested
* element doesn't exist on the classpath used to build the image and the arguments to the query
* were not registered in the reflection metadata. This ensures composability of the metadata (i.e.
* a change in the reachability of an element will not cause changes in the behavior of the
* application).
*/
public final class MissingReflectionRegistrationError extends Error {
private static final long serialVersionUID = 1L;

public MissingReflectionRegistrationError(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -55,8 +55,72 @@ default void register(ConfigurationCondition condition, Class<?>... classes) {

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

default void registerAllMethodsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz) {
register(condition, queriedOnly, clazz.getMethods());
}

default void registerAllDeclaredMethodsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz) {
register(condition, queriedOnly, clazz.getDeclaredMethods());
}

default void registerAllFieldsQuery(ConfigurationCondition condition, Class<?> clazz) {
register(condition, false, clazz.getFields());
}

default void registerAllDeclaredFieldsQuery(ConfigurationCondition condition, Class<?> clazz) {
register(condition, false, clazz.getDeclaredFields());
}

default void registerAllConstructorsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz) {
register(condition, queriedOnly, clazz.getConstructors());
}

default void registerAllDeclaredConstructorsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz) {
register(condition, queriedOnly, clazz.getDeclaredConstructors());
}

default void registerAllClassesQuery(ConfigurationCondition condition, Class<?> clazz) {
register(condition, clazz.getClasses());
}

default void registerAllDeclaredClassesQuery(ConfigurationCondition condition, Class<?> clazz) {
register(condition, clazz.getDeclaredClasses());
}

@SuppressWarnings("unused")
default void registerAllRecordComponentsQuery(ConfigurationCondition condition, Class<?> clazz) {
}

@SuppressWarnings("unused")
default void registerAllPermittedSubclassesQuery(ConfigurationCondition condition, Class<?> clazz) {
}

@SuppressWarnings("unused")
default void registerAllNestMembersQuery(ConfigurationCondition condition, Class<?> clazz) {
}

@SuppressWarnings("unused")
default void registerAllSignersQuery(ConfigurationCondition condition, Class<?> clazz) {
}

@SuppressWarnings("unused")
default void registerClassLookupException(ConfigurationCondition condition, String typeName, Throwable t) {
}

@SuppressWarnings("unused")
default void registerNegativeClassLookup(ConfigurationCondition condition, String typeName) {
}

@SuppressWarnings("unused")
default void registerNegativeFieldLookup(ConfigurationCondition condition, Class<?> declaringClass, String fieldName) {
}

@SuppressWarnings("unused")
default void registerNegativeMethodLookup(ConfigurationCondition condition, Class<?> declaringClass, String methodName, Class<?>... parameterTypes) {
}

@SuppressWarnings("unused")
default void registerNegativeConstructorLookup(ConfigurationCondition condition, Class<?> declaringClass, Class<?>... parameterTypes) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ public void onTypeReachable(AnalysisType type) {
*/
}

@Override
public void onTypeInstantiated(AnalysisType newValue) {
}

@Override
public GraphBuilderPhase.Instance createGraphBuilderPhase(HostedProviders providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts,
IntrinsicContext initialIntrinsicContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ public void checkType(ResolvedJavaType type, AnalysisUniverse universe) {
*/
public abstract void onTypeReachable(AnalysisType newValue);

/**
* Run initialization tasks for a newly instantiated {@link AnalysisType}.
*
* @param newValue the type to initialize
*/
public abstract void onTypeInstantiated(AnalysisType newValue);

/**
* Check if an {@link AnalysisType} is initialized.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ public AnalysisType lookupJavaType(Class<?> clazz) {
return (AnalysisType) super.lookupJavaType(clazz);
}

@Override
public AnalysisType[] lookupJavaTypes(Class<?>[] classes) {
AnalysisType[] result = new AnalysisType[classes.length];

for (int i = 0; i < result.length; ++i) {
result[i] = this.lookupJavaType(classes[i]);
}

return result;
}

public Optional<AnalysisType> optionalLookupJavaType(Class<?> clazz) {
AnalysisType result = (AnalysisType) getTypeCacheEntry(clazz);
if (result != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,7 @@ public void onFieldAccessed(AnalysisField field) {
}

public void onTypeInstantiated(AnalysisType type, UsageKind usage) {
hostVM.onTypeInstantiated(type);
bb.onTypeInstantiated(type, usage);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,40 +213,7 @@ private static boolean forName(JNIEnvironment jni, JNIObjectHandle thread, Break
if (className == null) {
return false; /* No point in tracing this. */
}

boolean classLoaderValid = true;
WordPointer classLoaderPtr = StackValue.get(WordPointer.class);
if (bp.method == agent.handles().javaLangClassForName3) {
assert thread.notEqual(nullHandle()) || Support.jvmtiVersion() != JvmtiInterface.JVMTI_VERSION_19 : "JDK-8292657";
classLoaderValid = (jvmtiFunctions().GetLocalObject().invoke(jvmtiEnv(), thread, 0, 2, classLoaderPtr) == JvmtiError.JVMTI_ERROR_NONE);
} else {
classLoaderPtr.write(nullHandle());
if (callerClass.notEqual(nullHandle())) {
/*
* NOTE: we use our direct caller class, but this class might be skipped over by
* Class.forName(nameOnly) in its security stackwalk for @CallerSensitive, leading
* to different behavior of our call and the original call.
*/
classLoaderValid = (jvmtiFunctions().GetClassLoader().invoke(jvmtiEnv(), callerClass, classLoaderPtr) == JvmtiError.JVMTI_ERROR_NONE);
}
}
Object result = Tracer.UNKNOWN_VALUE;
if (classLoaderValid) {
/*
* Even if the original call requested class initialization, disable it because
* recursion checks keep us from seeing events of interest during initialization.
*/
int initialize = 0;
Support.callStaticObjectMethodLIL(jni, bp.clazz, agent.handles().javaLangClassForName3, name, initialize, classLoaderPtr.read());
JNIObjectHandle exception = handleException(jni, true);
/*
* To throw the right exceptions at run time, we need to ensure that the image builder
* sees them, so we trace all calls except those that throw a ClassNotFoundException.
*/
result = exception.equal(nullHandle()) || !jniFunctions().getIsInstanceOf().invoke(jni, exception, agent.handles().javaLangClassNotFoundException);
}

traceReflectBreakpoint(jni, bp.clazz, nullHandle(), callerClass, bp.specification.methodName, result, state.getFullStackTraceOrNull(), className);
traceReflectBreakpoint(jni, bp.clazz, nullHandle(), callerClass, bp.specification.methodName, null, state.getFullStackTraceOrNull(), className);
return true;
}

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

private static boolean getRecordComponents(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleGetClasses(jni, thread, bp, state);
}

private static boolean getPermittedSubclasses(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleGetClasses(jni, thread, bp, state);
}

private static boolean getNestMembers(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleGetClasses(jni, thread, bp, state);
}

private static boolean getSigners(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleGetClasses(jni, thread, bp, state);
}

private static boolean handleGetClasses(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIObjectHandle self = getReceiver(thread);
Expand Down Expand Up @@ -332,7 +311,7 @@ private static boolean handleGetField(JNIEnvironment jni, JNIObjectHandle thread
declaring = nullHandle();
}
}
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, result.notEqual(nullHandle()),
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, true,
state.getFullStackTraceOrNull(), fromJniString(jni, name));
return true;
}
Expand Down Expand Up @@ -395,12 +374,8 @@ private static boolean getConstructor(JNIEnvironment jni, JNIObjectHandle thread
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIObjectHandle self = getReceiver(thread);
JNIObjectHandle paramTypesHandle = getObjectArgument(thread, 1);
JNIObjectHandle result = Support.callObjectMethodL(jni, self, bp.method, paramTypesHandle);
if (clearException(jni)) {
result = nullHandle();
}
Object paramTypes = getClassArrayNames(jni, paramTypesHandle);
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), nullHandle(), callerClass, bp.specification.methodName, nullHandle().notEqual(result), state.getFullStackTraceOrNull(),
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), nullHandle(), callerClass, bp.specification.methodName, true, state.getFullStackTraceOrNull(),
paramTypes);
return true;
}
Expand Down Expand Up @@ -431,7 +406,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, result.notEqual(nullHandle()),
traceReflectBreakpoint(jni, getClassOrSingleProxyInterface(jni, self), getClassOrSingleProxyInterface(jni, declaring), callerClass, bp.specification.methodName, true,
state.getFullStackTraceOrNull(), name, paramTypes);
return true;
}
Expand Down Expand Up @@ -1799,8 +1774,14 @@ private interface BreakpointHandler {
optionalBrk("java/lang/invoke/MethodType", "fromMethodDescriptorString",
"(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;",
BreakpointInterceptor::methodTypeFromDescriptor),
optionalBrk("java/lang/Class", "getRecordComponents", "()[Ljava/lang/reflect/RecordComponent;",
BreakpointInterceptor::getRecordComponents),
optionalBrk("java/lang/Class", "getPermittedSubclasses", "()[Ljava/lang/Class;",
BreakpointInterceptor::getPermittedSubclasses)
BreakpointInterceptor::getPermittedSubclasses),
optionalBrk("java/lang/Class", "getNestMembers", "()[Ljava/lang/Class;",
BreakpointInterceptor::getNestMembers),
optionalBrk("java/lang/Class", "getSigners", "()[Ljava/lang/Object;",
BreakpointInterceptor::getSigners)
};

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

0 comments on commit 737af06

Please sign in to comment.