Skip to content

Commit 9bd7810

Browse files
committed
Adapt Truffle to queried-only methods
1 parent 4af56ac commit 9bd7810

File tree

10 files changed

+160
-24
lines changed

10 files changed

+160
-24
lines changed

sdk/src/org.graalvm.nativeimage/snapshot.sigtest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ meth public java.lang.Class<?> getDeclaringClass()
257257
meth public java.lang.Class<?> getElementType()
258258
meth public java.lang.Class<?>[] getParameterTypes()
259259
meth public java.lang.String getElementName()
260+
meth public static boolean isInvocable(java.lang.reflect.Executable)
260261
supr java.lang.LinkageError
261262
hfds declaringClass,elementName,elementType,parameterTypes,serialVersionUID
262263

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/MissingReflectionRegistrationError.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,12 @@
4242

4343
import java.io.Serial;
4444
import java.lang.reflect.Constructor;
45+
import java.lang.reflect.Executable;
4546
import java.lang.reflect.Field;
4647
import java.lang.reflect.Method;
4748

49+
import org.graalvm.nativeimage.impl.ReflectionIntrospector;
50+
4851
/**
4952
* This exception is thrown when a reflective query (such as
5053
* {@link Class#getMethod(String, Class[])}) tries to access an element that was not <a href=
@@ -152,4 +155,14 @@ public String getElementName() {
152155
public Class<?>[] getParameterTypes() {
153156
return parameterTypes;
154157
}
158+
159+
/**
160+
* @return Whether the executable can be invoked without throwing a
161+
* {@link MissingReflectionRegistrationError}.
162+
*
163+
* @since 25.1
164+
*/
165+
public static boolean isInvocable(Executable executable) {
166+
return !ImageSingletons.contains(ReflectionIntrospector.class) || ImageSingletons.lookup(ReflectionIntrospector.class).isInvocable(executable);
167+
}
155168
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2025, 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.impl;
42+
43+
import java.lang.reflect.Executable;
44+
45+
/**
46+
* Provides an interface to query whether an executable has invocation capabilities, or was only
47+
* registered for reflective queries.
48+
*/
49+
public interface ReflectionIntrospector {
50+
boolean isInvocable(Executable executable);
51+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.reflect;
26+
27+
import com.oracle.svm.core.SubstrateUtil;
28+
import com.oracle.svm.core.reflect.target.Target_java_lang_reflect_Constructor;
29+
import com.oracle.svm.core.reflect.target.Target_java_lang_reflect_Method;
30+
import org.graalvm.nativeimage.impl.ReflectionIntrospector;
31+
32+
import java.lang.reflect.Constructor;
33+
import java.lang.reflect.Executable;
34+
import java.lang.reflect.Method;
35+
36+
public class ReflectionIntrospectorImpl implements ReflectionIntrospector {
37+
@Override
38+
public boolean isInvocable(Executable executable) {
39+
switch (executable) {
40+
case Method method -> {
41+
Target_java_lang_reflect_Method methodSubstitution = SubstrateUtil.cast(method, Target_java_lang_reflect_Method.class);
42+
return methodSubstitution.methodAccessor != null || methodSubstitution.methodAccessorFromMetadata != null;
43+
}
44+
case Constructor<?> constructor -> {
45+
Target_java_lang_reflect_Constructor constructorSubstitution = SubstrateUtil.cast(constructor, Target_java_lang_reflect_Constructor.class);
46+
return constructorSubstitution.constructorAccessor != null || constructorSubstitution.constructorAccessorFromMetadata != null;
47+
}
48+
}
49+
}
50+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/serialize/SerializationSupport.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@
2525
*/
2626
package com.oracle.svm.core.reflect.serialize;
2727

28-
import static com.oracle.svm.core.SubstrateOptions.ThrowMissingRegistrationErrors;
29-
3028
import java.io.Serializable;
3129
import java.lang.invoke.SerializedLambda;
3230
import java.lang.reflect.Constructor;
@@ -284,14 +282,8 @@ public static Object getSerializationConstructorAccessor(Class<?> serializationT
284282
}
285283

286284
String targetConstructorClassName = targetConstructorClass.getName();
287-
if (ThrowMissingRegistrationErrors.hasBeenSet()) {
288-
MissingSerializationRegistrationUtils.reportSerialization(declaringClass,
289-
"type '" + declaringClass.getTypeName() + "' with target constructor class '" + targetConstructorClassName + "'");
290-
} else {
291-
throw VMError.unsupportedFeature("SerializationConstructorAccessor class not found for declaringClass: " + declaringClass.getName() +
292-
" (targetConstructorClass: " + targetConstructorClassName + "). Usually adding " + declaringClass.getName() +
293-
" to serialization-config.json fixes the problem.");
294-
}
285+
MissingSerializationRegistrationUtils.reportSerialization(declaringClass,
286+
"type '" + declaringClass.getTypeName() + "' with target constructor class '" + targetConstructorClassName + "'");
295287
return null;
296288
}
297289

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Constructor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public final class Target_java_lang_reflect_Constructor {
7878
*/
7979
@Inject //
8080
@RecomputeFieldValue(kind = Kind.Reset) //
81-
Target_jdk_internal_reflect_ConstructorAccessor constructorAccessorFromMetadata;
81+
public Target_jdk_internal_reflect_ConstructorAccessor constructorAccessorFromMetadata;
8282

8383
@Alias
8484
@TargetElement(name = CONSTRUCTOR_NAME)

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_java_lang_reflect_Method.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public final class Target_java_lang_reflect_Method {
117117
*/
118118
@Inject //
119119
@RecomputeFieldValue(kind = Kind.Reset) //
120-
Target_jdk_internal_reflect_MethodAccessor methodAccessorFromMetadata;
120+
public Target_jdk_internal_reflect_MethodAccessor methodAccessorFromMetadata;
121121

122122
@Inject //
123123
@RecomputeFieldValue(kind = Kind.Custom, declClass = LayerIdComputer.class) //

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
4747
import org.graalvm.nativeimage.hosted.RuntimeReflection;
4848
import org.graalvm.nativeimage.impl.AnnotationExtractor;
49+
import org.graalvm.nativeimage.impl.ReflectionIntrospector;
4950
import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
5051
import org.graalvm.nativeimage.impl.RuntimeProxyRegistrySupport;
5152
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
@@ -73,6 +74,7 @@
7374
import com.oracle.svm.core.meta.MethodPointer;
7475
import com.oracle.svm.core.meta.MethodRef;
7576
import com.oracle.svm.core.reflect.ReflectionAccessorHolder;
77+
import com.oracle.svm.core.reflect.ReflectionIntrospectorImpl;
7678
import com.oracle.svm.core.reflect.SubstrateAccessor;
7779
import com.oracle.svm.core.reflect.SubstrateConstructorAccessor;
7880
import com.oracle.svm.core.reflect.SubstrateMethodAccessor;
@@ -310,6 +312,8 @@ public void afterRegistration(AfterRegistrationAccess access) {
310312
ImageSingletons.add(RuntimeReflectionSupport.class, reflectionData);
311313
ImageSingletons.add(ReflectionHostedSupport.class, reflectionData);
312314

315+
ImageSingletons.add(ReflectionIntrospector.class, new ReflectionIntrospectorImpl());
316+
313317
/*
314318
* Querying Object members is allowed to enable these accesses on array classes, since those
315319
* don't define any additional members.

truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostInteropReflect.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -187,20 +187,14 @@ static boolean isModifiable(HostObject object, Class<?> clazz, String name, bool
187187
static boolean isInvokable(HostObject object, Class<?> clazz, String name, boolean onlyStatic) {
188188
HostClassDesc classDesc = HostClassDesc.forClass(object.context, clazz);
189189
HostMethodDesc foundMethod = classDesc.lookupMethod(name, onlyStatic);
190-
if (foundMethod != null) {
191-
return true;
192-
} else if (isSignature(name)) {
193-
foundMethod = classDesc.lookupMethodBySignature(name, onlyStatic);
194-
if (foundMethod != null) {
195-
return true;
196-
}
197-
} else if (isJNIName(name)) {
198-
foundMethod = classDesc.lookupMethodByJNIName(name, onlyStatic);
199-
if (foundMethod != null) {
200-
return true;
190+
if (foundMethod == null) {
191+
if (isSignature(name)) {
192+
foundMethod = classDesc.lookupMethodBySignature(name, onlyStatic);
193+
} else if (isJNIName(name)) {
194+
foundMethod = classDesc.lookupMethodByJNIName(name, onlyStatic);
201195
}
202196
}
203-
return false;
197+
return foundMethod != null && foundMethod.isInvocable();
204198
}
205199

206200
@TruffleBoundary

truffle/src/com.oracle.truffle.host/src/com/oracle/truffle/host/HostMethodDesc.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ boolean isInternal() {
7777

7878
abstract boolean isConstructor();
7979

80+
abstract boolean isInvocable();
81+
8082
abstract static class SingleMethod extends HostMethodDesc {
8183

8284
static final int[] EMTPY_SCOPED_PARAMETERS = new int[0];
@@ -143,6 +145,25 @@ public boolean isOnlyVisibleFromJniName() {
143145

144146
public abstract Executable getReflectionMethod();
145147

148+
private static final Method isInvocableMethod = TruffleOptions.AOT ? getIsInvocableMethod() : null;
149+
150+
private static Method getIsInvocableMethod() {
151+
try {
152+
return Class.forName("org.graalvm.nativeimage.MissingReflectionRegistrationError").getMethod("isInvocable", Executable.class);
153+
} catch (ClassNotFoundException | NoSuchMethodException e) {
154+
throw new RuntimeException(e);
155+
}
156+
}
157+
158+
@Override
159+
boolean isInvocable() {
160+
try {
161+
return !TruffleOptions.AOT || (boolean) isInvocableMethod.invoke(null, getReflectionMethod());
162+
} catch (IllegalAccessException | InvocationTargetException e) {
163+
throw new RuntimeException(e);
164+
}
165+
}
166+
146167
public final boolean isVarArgs() {
147168
return varArgs;
148169
}
@@ -598,6 +619,16 @@ public boolean isInternal() {
598619
}
599620
return true;
600621
}
622+
623+
@Override
624+
boolean isInvocable() {
625+
for (SingleMethod overload : overloads) {
626+
if (!overload.isInvocable()) {
627+
return false;
628+
}
629+
}
630+
return true;
631+
}
601632
}
602633

603634
}

0 commit comments

Comments
 (0)