Skip to content

Commit 20f8bb7

Browse files
committed
[GR-67606] Refactor UnregisteredForeignStubException into a missing registration error
PullRequest: graal/21521
2 parents 45efd6b + 59532ea commit 20f8bb7

File tree

5 files changed

+103
-32
lines changed

5 files changed

+103
-32
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ meth public abstract void fatalError()
232232
meth public abstract void flush()
233233
meth public abstract void log(org.graalvm.nativeimage.c.type.CCharPointer,org.graalvm.word.UnsignedWord)
234234

235+
CLSS public final org.graalvm.nativeimage.MissingForeignRegistrationError
236+
cons public init(java.lang.String)
237+
supr java.lang.LinkageError
238+
235239
CLSS public final org.graalvm.nativeimage.MissingJNIRegistrationError
236240
cons public init(java.lang.String,java.lang.Class<?>,java.lang.Class<?>,java.lang.String,java.lang.String)
237241
meth public java.lang.Class<?> getDeclaringClass()
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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;
42+
43+
/**
44+
* This error is thrown when a query through the Foreign Function and Memory (FFM) API did not get
45+
* appropriately registered at build-time.
46+
*
47+
* @since 25.0
48+
*/
49+
@SuppressWarnings("serial")
50+
public final class MissingForeignRegistrationError extends LinkageError {
51+
public MissingForeignRegistrationError(String message) {
52+
super(message);
53+
}
54+
}

substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,18 @@
3131
import java.lang.foreign.MemorySegment;
3232
import java.lang.foreign.MemorySegment.Scope;
3333
import java.lang.invoke.MethodHandle;
34+
import java.lang.invoke.MethodType;
3435
import java.util.HashMap;
3536
import java.util.Locale;
3637
import java.util.Map;
38+
import java.util.Set;
3739
import java.util.function.BiConsumer;
3840

3941
import org.graalvm.collections.EconomicMap;
4042
import org.graalvm.collections.EconomicSet;
4143
import org.graalvm.collections.Pair;
4244
import org.graalvm.nativeimage.ImageSingletons;
45+
import org.graalvm.nativeimage.MissingForeignRegistrationError;
4346
import org.graalvm.nativeimage.Platform;
4447
import org.graalvm.nativeimage.Platforms;
4548
import org.graalvm.nativeimage.c.function.CFunctionPointer;
@@ -49,6 +52,7 @@
4952

5053
import com.oracle.svm.core.ForeignSupport;
5154
import com.oracle.svm.core.FunctionPointerHolder;
55+
import com.oracle.svm.core.MissingRegistrationUtils;
5256
import com.oracle.svm.core.OS;
5357
import com.oracle.svm.core.SubstrateOptions;
5458
import com.oracle.svm.core.SubstrateUtil;
@@ -176,15 +180,15 @@ public void registerSafeArenaAccessorMethod(ResolvedJavaMethod method) {
176180
CFunctionPointer getDowncallStubPointer(NativeEntryPointInfo nep) {
177181
FunctionPointerHolder holder = downcallStubs.get(nep);
178182
if (holder == null) {
179-
throw new UnregisteredForeignStubException(nep);
183+
throw MissingForeignRegistrationUtils.reportDowncall(nep);
180184
}
181185
return holder.functionPointer;
182186
}
183187

184188
CFunctionPointer getUpcallStubPointer(JavaEntryPointInfo jep) {
185189
FunctionPointerHolder holder = upcallStubs.get(jep);
186190
if (holder == null) {
187-
throw new UnregisteredForeignStubException(jep);
191+
throw MissingForeignRegistrationUtils.reportUpcall(jep);
188192
}
189193
return holder.functionPointer;
190194
}
@@ -259,24 +263,32 @@ void freeTrampoline(long addr) {
259263
}
260264
}
261265

262-
@SuppressWarnings("serial")
263-
public static class UnregisteredForeignStubException extends RuntimeException {
264-
265-
UnregisteredForeignStubException(NativeEntryPointInfo nep) {
266-
super(generateMessage(nep));
266+
public static class MissingForeignRegistrationUtils extends MissingRegistrationUtils {
267+
public static MissingForeignRegistrationError reportDowncall(NativeEntryPointInfo nep) {
268+
MissingForeignRegistrationError mfre = new MissingForeignRegistrationError(foreignRegistrationMessage("downcall", nep.methodType()));
269+
report(mfre);
270+
return mfre;
267271
}
268272

269-
UnregisteredForeignStubException(JavaEntryPointInfo jep) {
270-
super(generateMessage(jep));
273+
public static MissingForeignRegistrationError reportUpcall(JavaEntryPointInfo jep) {
274+
MissingForeignRegistrationError mfre = new MissingForeignRegistrationError(foreignRegistrationMessage("upcall", jep.cMethodType()));
275+
report(mfre);
276+
return mfre;
271277
}
272278

273-
private static String generateMessage(NativeEntryPointInfo nep) {
274-
return "Cannot perform downcall with leaf type " + nep.methodType() + " as it was not registered at compilation time.";
279+
private static String foreignRegistrationMessage(String failedAction, MethodType methodType) {
280+
return registrationMessage("perform " + failedAction + " with leaf type", methodType.toString(), "", "", "foreign", "foreign");
275281
}
276282

277-
private static String generateMessage(JavaEntryPointInfo jep) {
278-
return "Cannot perform upcall with leaf type " + jep.cMethodType() + " as it was not registered at compilation time.";
283+
private static void report(MissingForeignRegistrationError exception) {
284+
StackTraceElement responsibleClass = getResponsibleClass(exception, foreignEntryPoints);
285+
MissingRegistrationUtils.report(exception, responsibleClass);
279286
}
287+
288+
private static final Map<String, Set<String>> foreignEntryPoints = Map.of(
289+
"jdk.internal.foreign.abi.AbstractLinker", Set.of(
290+
"downcallHandle",
291+
"upcallStub"));
280292
}
281293

282294
/**

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/MissingRegistrationUtils.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.ArrayList;
3434
import java.util.Arrays;
3535
import java.util.List;
36+
import java.util.Map;
3637
import java.util.Set;
3738
import java.util.concurrent.ConcurrentHashMap;
3839
import java.util.concurrent.atomic.AtomicReference;
@@ -221,6 +222,24 @@ protected static String interfacesString(Class<?>[] classes) {
221222
.collect(Collectors.joining(",", "[", "]"));
222223
}
223224

225+
protected static StackTraceElement getResponsibleClass(Throwable t, Map<String, Set<String>> entryPoints) {
226+
StackTraceElement[] stackTrace = t.getStackTrace();
227+
boolean returnNext = false;
228+
for (StackTraceElement stackTraceElement : stackTrace) {
229+
if (entryPoints.getOrDefault(stackTraceElement.getClassName(), Set.of()).contains(stackTraceElement.getMethodName())) {
230+
/*
231+
* Multiple functions with the same name can be called in succession, like the
232+
* Class.forName caller-sensitive adapters. We skip those until we find a method
233+
* that is not a monitored reflection entry point.
234+
*/
235+
returnNext = true;
236+
} else if (returnNext) {
237+
return stackTraceElement;
238+
}
239+
}
240+
return null;
241+
}
242+
224243
public static final class ExitException extends Error {
225244
@Serial//
226245
private static final long serialVersionUID = -3638940737396726143L;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MissingReflectionRegistrationUtils.java

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ private static String reflectionError(String failedAction, String elementDescrip
157157
}
158158

159159
private static void report(MissingReflectionRegistrationError exception) {
160-
StackTraceElement responsibleClass = getResponsibleClass(exception);
160+
StackTraceElement responsibleClass = getResponsibleClass(exception, reflectionEntryPoints);
161161
MissingRegistrationUtils.report(exception, responsibleClass);
162162
}
163163

@@ -197,22 +197,4 @@ private static void report(MissingReflectionRegistrationError exception) {
197197
"sun.misc.Unsafe", Set.of("allocateInstance"),
198198
/* For jdk.internal.misc.Unsafe.allocateInstance(), which is intrinsified */
199199
SubstrateAllocationSnippets.class.getName(), Set.of("slowPathHubOrUnsafeInstantiationError"));
200-
201-
private static StackTraceElement getResponsibleClass(Throwable t) {
202-
StackTraceElement[] stackTrace = t.getStackTrace();
203-
boolean returnNext = false;
204-
for (StackTraceElement stackTraceElement : stackTrace) {
205-
if (reflectionEntryPoints.getOrDefault(stackTraceElement.getClassName(), Set.of()).contains(stackTraceElement.getMethodName())) {
206-
/*
207-
* Multiple functions with the same name can be called in succession, like the
208-
* Class.forName caller-sensitive adapters. We skip those until we find a method
209-
* that is not a monitored reflection entry point.
210-
*/
211-
returnNext = true;
212-
} else if (returnNext) {
213-
return stackTraceElement;
214-
}
215-
}
216-
return null;
217-
}
218200
}

0 commit comments

Comments
 (0)