Skip to content

Commit ce6e183

Browse files
committed
[GR-51664] Print non-internal PolyglotException type name.
PullRequest: graal/19584
2 parents 7a98162 + 3cbd93d commit ce6e183

File tree

9 files changed

+183
-17
lines changed

9 files changed

+183
-17
lines changed

sdk/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ This changelog summarizes major changes between GraalVM SDK versions. The main f
44

55
## Version 25.0.0
66
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.
7+
* GR-51664 Improved `PolyglotException#toString` and `PolyglotException#printStackTrace`.
8+
* The short description returned by `PolyglotException#toString` now starts with the qualified name of the metaobject of the guest exception, if the exception represents a guest exception that has a metaobject. Otherwise, it starts with the qualified name of the `PolyglotException` class.
9+
* `PolyglotException#printStackTrace` now always starts with the string returned by `PolyglotException#toString()` like for regular Java Throwable objects.
710

811
## Version 24.2.0
912
* GR-54905 When using Truffle NFI with the Panama backend, native access must now be granted to the Truffle module instead of the NFI Panama module. Use the `--enable-native-access=org.graalvm.truffle` Java command line option to enable the native access for the NFI Panama backend.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ meth public int hashCode()
407407
meth public java.lang.Iterable<org.graalvm.polyglot.PolyglotException$StackFrame> getPolyglotStackTrace()
408408
meth public java.lang.StackTraceElement[] getStackTrace()
409409
meth public java.lang.String getMessage()
410+
meth public java.lang.String toString()
410411
meth public java.lang.Throwable asHostException()
411412
meth public java.lang.Throwable fillInStackTrace()
412413
meth public org.graalvm.polyglot.SourceSection getSourceLocation()

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/PolyglotException.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, 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
@@ -115,6 +115,26 @@ public final class PolyglotException extends RuntimeException {
115115
super.setStackTrace(getStackTrace());
116116
}
117117

118+
/**
119+
* Returns a short description of this exception. The result is the concatenation of:
120+
* <ul>
121+
* <li>the qualified name of the metaobject of the guest exception, if this object represents
122+
* one, and it has a metaobject. Otherwise, the {@linkplain Class#getName() name} of the
123+
* {@link PolyglotException} class.
124+
* <li>": " (a colon and a space)
125+
* <li>the result of invoking this object's {@link #getMessage} method
126+
* </ul>
127+
* If {@code getMessage} returns {@code null}, then just the class name is returned.
128+
*
129+
* @return a string representation of this exception.
130+
*
131+
* @since 25.0
132+
*/
133+
@Override
134+
public String toString() {
135+
return dispatch.toString(impl);
136+
}
137+
118138
/**
119139
* Prints host and guest language stack frames to the standard {@link System#err error output}.
120140
*

sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, 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
@@ -877,6 +877,8 @@ protected AbstractExceptionDispatch(AbstractPolyglotImpl engineImpl) {
877877

878878
public abstract void onCreate(Object receiver, RuntimeException polyglotException);
879879

880+
public abstract String toString(Object receiver);
881+
880882
public abstract void printStackTrace(Object receiver, PrintStream s);
881883

882884
public abstract void printStackTrace(Object receiver, PrintWriter s);

truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/InstrumentationTestLanguage.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import java.io.OutputStreamWriter;
4646
import java.io.PrintStream;
4747
import java.io.PrintWriter;
48+
import java.io.Serial;
4849
import java.util.AbstractSet;
4950
import java.util.ArrayDeque;
5051
import java.util.ArrayList;
@@ -1587,13 +1588,15 @@ private RuntimeException createException() {
15871588
@ExportLibrary(InteropLibrary.class)
15881589
public static class TestLanguageException extends AbstractTruffleException {
15891590

1590-
private static final long serialVersionUID = 2709459650157465163L;
1591+
@Serial private static final long serialVersionUID = 2709459650157465163L;
15911592

15921593
private final String type;
1594+
private final transient Object metaObject;
15931595

15941596
TestLanguageException(String type, String message, ThrowNode throwNode) {
15951597
super(message, throwNode);
15961598
this.type = type;
1599+
this.metaObject = new InstrumentationMetaObject(this, type);
15971600
}
15981601

15991602
@ExportMessage
@@ -1611,6 +1614,16 @@ ExceptionType getExceptionType() {
16111614
return ExceptionType.RUNTIME_ERROR;
16121615
}
16131616

1617+
@ExportMessage
1618+
boolean hasMetaObject() {
1619+
return true;
1620+
}
1621+
1622+
@ExportMessage
1623+
Object getMetaObject() {
1624+
return metaObject;
1625+
}
1626+
16141627
@ExportMessage
16151628
@SuppressWarnings("unused")
16161629
Object toDisplayString(boolean allowSideEffects) {

truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/PolyglotExceptionTest.java

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@
5353
import java.io.IOException;
5454
import java.io.NotSerializableException;
5555
import java.io.ObjectOutputStream;
56+
import java.io.PrintStream;
5657
import java.io.PrintWriter;
58+
import java.io.Serial;
5759
import java.lang.reflect.Constructor;
5860
import java.lang.reflect.InvocationTargetException;
5961
import java.lang.reflect.Method;
@@ -107,6 +109,114 @@
107109

108110
public class PolyglotExceptionTest extends AbstractPolyglotTest {
109111

112+
@ExportLibrary(InteropLibrary.class)
113+
@SuppressWarnings("static-method")
114+
static class TestGuestErrorWithMetaObject extends AbstractTruffleException {
115+
116+
@Serial private static final long serialVersionUID = -7127537039713589511L;
117+
118+
private final String exceptionMessage;
119+
private final transient Object metaObject;
120+
121+
TestGuestErrorWithMetaObject(String exceptionMessage) {
122+
super(exceptionMessage);
123+
this.exceptionMessage = exceptionMessage;
124+
this.metaObject = new TestMetaObject(this, "MetaObjectName", "metaobject.MetaObjectName");
125+
}
126+
127+
@ExportMessage
128+
boolean hasExceptionMessage() {
129+
return exceptionMessage != null;
130+
}
131+
132+
@ExportMessage
133+
Object getExceptionMessage() throws UnsupportedMessageException {
134+
if (exceptionMessage != null) {
135+
return exceptionMessage;
136+
} else {
137+
throw UnsupportedMessageException.create();
138+
}
139+
}
140+
141+
@ExportMessage
142+
boolean hasMetaObject() {
143+
return true;
144+
}
145+
146+
@ExportMessage
147+
Object getMetaObject() {
148+
return metaObject;
149+
}
150+
151+
}
152+
153+
@ExportLibrary(InteropLibrary.class)
154+
@SuppressWarnings("static-method")
155+
static final class TestMetaObject implements TruffleObject {
156+
157+
private final Object original;
158+
private final String name;
159+
private final String qualifiedName;
160+
161+
TestMetaObject(Object original, String name, String qualifiedName) {
162+
this.original = original;
163+
this.name = name;
164+
this.qualifiedName = qualifiedName;
165+
}
166+
167+
@ExportMessage
168+
boolean isMetaObject() {
169+
return true;
170+
}
171+
172+
@ExportMessage
173+
Object getMetaQualifiedName() {
174+
return qualifiedName;
175+
}
176+
177+
@ExportMessage
178+
Object getMetaSimpleName() {
179+
return name;
180+
}
181+
182+
@ExportMessage
183+
@TruffleBoundary
184+
boolean isMetaInstance(Object instance) {
185+
return original.equals(instance);
186+
}
187+
}
188+
189+
@Test
190+
public void testExceptionPrinting() throws IOException {
191+
try (Context context1 = Context.create()) {
192+
TestGuestError testGuestError = new TestGuestError();
193+
testGuestError.exceptionMessage = "Test exception message";
194+
PolyglotException pe = context1.asValue(testGuestError).as(PolyglotException.class);
195+
assertStackTraceStart(pe, PolyglotException.class.getName() + ": Test exception message");
196+
TestGuestErrorWithMetaObject testGuestErrorWithMetaObject = new TestGuestErrorWithMetaObject("Test exception message");
197+
pe = context1.asValue(testGuestErrorWithMetaObject).as(PolyglotException.class);
198+
assertStackTraceStart(pe, "metaobject.MetaObjectName: Test exception message");
199+
pe = Assert.assertThrows("ExceptionMessage", PolyglotException.class, () -> context1.eval("instrumentation-test-language", "THROW(NPE, ExceptionMessage)"));
200+
assertStackTraceStart(pe, "NPE: ExceptionMessage");
201+
202+
context1.close(true);
203+
pe = Assert.assertThrows("Context execution was cancelled.", PolyglotException.class, () -> context1.eval("instrumentation-test-language", "STATEMENT"));
204+
assertStackTraceStart(pe, PolyglotException.class.getName() + ": Context execution was cancelled.");
205+
} catch (PolyglotException pe) {
206+
if (!pe.isCancelled()) {
207+
throw pe;
208+
}
209+
}
210+
}
211+
212+
private static void assertStackTraceStart(PolyglotException pe, String expected) throws IOException {
213+
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); PrintStream printStream = new PrintStream(byteArrayOutputStream)) {
214+
pe.printStackTrace(printStream);
215+
String stackTrace = byteArrayOutputStream.toString();
216+
Assert.assertEquals(expected, stackTrace.substring(0, stackTrace.indexOf(System.lineSeparator())));
217+
}
218+
}
219+
110220
@ExportLibrary(InteropLibrary.class)
111221
@SuppressWarnings("serial")
112222
static class TestGuestError extends AbstractTruffleException {

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionDispatch.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ public void printStackTrace(Object receiver, PrintWriter s) {
107107
((PolyglotExceptionImpl) receiver).printStackTrace(s);
108108
}
109109

110+
@Override
111+
public String toString(Object receiver) {
112+
return ((PolyglotExceptionImpl) receiver).toStringImpl();
113+
}
114+
110115
@Override
111116
public StackTraceElement[] getStackTrace(Object receiver) {
112117
return ((PolyglotExceptionImpl) receiver).getStackTrace();

truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ final class PolyglotExceptionImpl {
9696
private final boolean interrupted;
9797
private final int exitStatus;
9898
private final Object guestObject;
99+
private final String qualifiedName;
99100
private final String message;
100101

101102
PolyglotExceptionImpl(PolyglotEngineImpl engine, PolyglotContextImpl.State polyglotContextState, boolean polyglotContextResourceExhausted, int exitCode, Throwable original) {
@@ -131,6 +132,7 @@ final class PolyglotExceptionImpl {
131132
creationStackTrace = null;
132133
}
133134
Error resourceLimitError = getResourceLimitError(engine, exception);
135+
String exceptionQualifiedName = null;
134136
String exceptionMessage = null;
135137
InteropLibrary interop;
136138
if (allowInterop && (interop = InteropLibrary.getUncached()).isException(exception)) {
@@ -148,6 +150,9 @@ final class PolyglotExceptionImpl {
148150
if (interop.hasExceptionMessage(exception)) {
149151
exceptionMessage = interop.asString(interop.getExceptionMessage(exception));
150152
}
153+
if (interop.hasMetaObject(exception)) {
154+
exceptionQualifiedName = interop.asString(interop.getMetaQualifiedName(interop.getMetaObject(exception)));
155+
}
151156
if (interop.hasSourceLocation(exception)) {
152157
this.sourceLocation = newSourceSection(interop.getSourceLocation(exception));
153158
} else {
@@ -180,7 +185,6 @@ final class PolyglotExceptionImpl {
180185
*/
181186
this.cancelled = cancelInducedTruffleOrInterruptException || (exception instanceof CancelExecution);
182187
this.resourceExhausted = resourceLimitError != null || (cancelInducedTruffleOrInterruptException && polyglotContextResourceExhausted);
183-
this.interrupted = interruptException && !this.cancelled;
184188
this.syntaxError = false;
185189
this.incompleteSource = false;
186190
com.oracle.truffle.api.source.SourceSection location = null;
@@ -197,6 +201,7 @@ final class PolyglotExceptionImpl {
197201
this.exitStatus = 0;
198202
this.guestObject = null;
199203
}
204+
this.interrupted = interruptException && !this.cancelled && !this.exit;
200205
this.internal = !interrupted && !cancelled && !resourceExhausted && !exit && !truffleException;
201206
if (exception instanceof CancelExecution) {
202207
location = ((CancelExecution) exception).getSourceLocation();
@@ -220,15 +225,16 @@ final class PolyglotExceptionImpl {
220225
} else {
221226
this.message = null;
222227
}
228+
qualifiedName = exceptionQualifiedName;
223229
}
224230

225231
private static Error getResourceLimitError(PolyglotEngineImpl engine, Throwable e) {
226232
if (e instanceof CancelExecution) {
227233
return ((CancelExecution) e).isResourceLimit() ? (Error) e : null;
228234
} else if (isHostException(engine, e)) {
229-
Throwable toCheck = engine.host.toHostResourceError(e);
235+
Error toCheck = engine.host.toHostResourceError(e);
230236
assert toCheck == null || toCheck instanceof StackOverflowError || toCheck instanceof OutOfMemoryError;
231-
return (Error) toCheck;
237+
return toCheck;
232238
} else if (e instanceof StackOverflowError || e instanceof OutOfMemoryError) {
233239
return (Error) e;
234240
}
@@ -282,11 +288,11 @@ public Throwable asHostException() {
282288
return engine.host.unboxHostException(exception);
283289
}
284290

285-
public void printStackTrace(PrintWriter s) {
291+
void printStackTrace(PrintWriter s) {
286292
printStackTrace(new WrappedPrintWriter(s));
287293
}
288294

289-
public void printStackTrace(PrintStream s) {
295+
void printStackTrace(PrintStream s) {
290296
printStackTrace(new WrappedPrintStream(s));
291297
}
292298

@@ -300,11 +306,7 @@ private void printStackTrace(PrintStreamOrWriter s) {
300306
return;
301307
}
302308
// Print our stack trace
303-
if (isInternalError() || getMessage() == null || getMessage().isEmpty()) {
304-
s.println(api);
305-
} else {
306-
s.println(getMessage());
307-
}
309+
s.println(toStringImpl());
308310

309311
materialize();
310312
int languageIdLength = 0; // java
@@ -334,6 +336,16 @@ private void printStackTrace(PrintStreamOrWriter s) {
334336
}
335337
}
336338

339+
String toStringImpl() {
340+
if (isInternalError() && (guestFrames == null || guestFrames.isEmpty())) {
341+
return api.getClass().getName() + ": " + exception.toString();
342+
} else {
343+
String s = (qualifiedName != null ? qualifiedName : api.getClass().getName());
344+
String m = getMessage();
345+
return (m != null) ? (s + ": " + m) : s;
346+
}
347+
}
348+
337349
public String getMessage() {
338350
return message;
339351
}

vm/tests/all/agentscript/agent-embedding.test

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ OK 2 times!
4949
>[1] java ${SUN_MISC_UNSAFE_OPTION} --enable-native-access=org.graalvm.truffle -ea -Dtruffle.class.path.append=${TMP_DIR}/instrument.jar -cp ${TMP_DIR} EmbeddingRegisterSymbols primitives
5050
\[engine\] The internal option -Dtruffle\.class\.path\.append option is deprecated.*
5151

52-
Exception in thread "main" Stop: 0
52+
Exception in thread "main" org.graalvm.polyglot.PolyglotException: Stop: 0
5353
.*at.*js.*insight.js.*
5454
.*at.*js.*fib.*Unnamed.*
5555
.*at.*js.*fib.*Unnamed.*
@@ -64,7 +64,7 @@ Exception in thread "main" Stop: 0
6464
>[1] java ${SUN_MISC_UNSAFE_OPTION} --enable-native-access=org.graalvm.truffle -ea -Dtruffle.class.path.append=${TMP_DIR}/instrument.jar -cp ${TMP_DIR} EmbeddingRegisterSymbols object
6565
\[engine\] The internal option -Dtruffle\.class\.path\.append option is deprecated.*
6666

67-
Exception in thread "main" Stop: 0
67+
Exception in thread "main" org.graalvm.polyglot.PolyglotException: Stop: 0
6868
.*at.*js.*insight.js.*
6969
.*at.*js.*fib.*Unnamed.*
7070
.*at.*js.*fib.*Unnamed.*
@@ -81,7 +81,7 @@ Exception in thread "main" Stop: 0
8181
# ##################################################################
8282
>[1] java ${SUN_MISC_UNSAFE_OPTION} --enable-native-access=org.graalvm.truffle -ea --module-path ${TMP_DIR}/instrument.jar:${GRAALVM_HOME}/tools/insight -cp ${TMP_DIR} EmbeddingRegisterSymbols primitives
8383

84-
Exception in thread "main" Stop: 0
84+
Exception in thread "main" org.graalvm.polyglot.PolyglotException: Stop: 0
8585
.*at.*js.*insight.js.*
8686
.*at.*js.*fib.*Unnamed.*
8787
.*at.*js.*fib.*Unnamed.*
@@ -95,7 +95,7 @@ Exception in thread "main" Stop: 0
9595
.*at.*EmbeddingRegisterSymbols.main.*
9696
>[1] java ${SUN_MISC_UNSAFE_OPTION} --enable-native-access=org.graalvm.truffle -ea --module-path ${TMP_DIR}/instrument.jar:${GRAALVM_HOME}/tools/insight -cp ${TMP_DIR} EmbeddingRegisterSymbols object
9797

98-
Exception in thread "main" Stop: 0
98+
Exception in thread "main" org.graalvm.polyglot.PolyglotException: Stop: 0
9999
.*at.*js.*insight.js.*
100100
.*at.*js.*fib.*Unnamed.*
101101
.*at.*js.*fib.*Unnamed.*

0 commit comments

Comments
 (0)