Skip to content

Commit 524d519

Browse files
committed
[GR-41521] Improve Foreign exceptions in Espresso.
PullRequest: graal/13588
2 parents bbfb686 + 86b56a8 commit 524d519

File tree

17 files changed

+652
-144
lines changed

17 files changed

+652
-144
lines changed

espresso/src/com.oracle.truffle.espresso.polyglot/src/com/oracle/truffle/espresso/polyglot/ForeignException.java

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 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
@@ -45,8 +45,9 @@
4545
* Handy wrapper for foreign exceptions.
4646
*
4747
* <p>
48-
* Allows foreign exceptions to be caught in guest Java and provides seamless access to the message
49-
* and cause. {@link ForeignException} can only catch and wrap foreign exceptions.
48+
* Allows foreign exceptions to be caught in guest Java and provides seamless access to the message,
49+
* cause and a polyglot stacktrace containing all guest frames if any. {@link ForeignException} can
50+
* only catch and wrap foreign exceptions.
5051
*
5152
* <pre>
5253
* assert Interop.isException(foreignException);
@@ -58,6 +59,10 @@
5859
* throw e;
5960
* }
6061
* </pre>
62+
*
63+
* The raw foreign exception object which can serve polyglot messages e.g.
64+
* InteropLibrary#isException(rawForeignException) can be retrieved with
65+
* {@link ForeignException#getForeignExceptionObject()}.
6166
*
6267
* @see Interop#throwException(Object)
6368
* @since 21.0
@@ -70,12 +75,21 @@ private ForeignException() {
7075
throw new RuntimeException("No instance of ForeignException can be created directly");
7176
}
7277

78+
/**
79+
* Returns the original wrapped foreign exception. The returned instance should be used for
80+
* {@link Interop} messages.
81+
*
82+
* @return the wrapped foreign exception.
83+
*/
84+
public native Object getForeignExceptionObject();
85+
7386
@Override
7487
public String getMessage() {
75-
assert Interop.isException(this);
76-
if (Interop.hasExceptionMessage(this)) {
88+
Object rawForeignObject = getForeignExceptionObject();
89+
assert Interop.isException(rawForeignObject);
90+
if (Interop.hasExceptionMessage(rawForeignObject)) {
7791
try {
78-
Object message = Interop.getExceptionMessage(this);
92+
Object message = Interop.getExceptionMessage(rawForeignObject);
7993
return Interop.asString(message);
8094
} catch (UnsupportedMessageException e) {
8195
throw new AssertionError("Unexpected exception", e);
@@ -87,11 +101,12 @@ public String getMessage() {
87101
@SuppressWarnings("sync-override")
88102
@Override
89103
public Throwable getCause() {
90-
assert Interop.isException(this);
91-
if (Interop.hasExceptionCause(this)) {
92-
Object cause = null;
104+
Object rawForeignObject = getForeignExceptionObject();
105+
assert Interop.isException(rawForeignObject);
106+
if (Interop.hasExceptionCause(rawForeignObject)) {
107+
Object cause;
93108
try {
94-
cause = Interop.getExceptionCause(this);
109+
cause = Interop.getExceptionCause(rawForeignObject);
95110
} catch (UnsupportedMessageException e) {
96111
throw new AssertionError("Unexpected exception", e);
97112
}
@@ -105,14 +120,9 @@ public Throwable getCause() {
105120
return null;
106121
}
107122

108-
@Override
109-
public StackTraceElement[] getStackTrace() {
110-
return super.getStackTrace(); // empty
111-
}
112-
113123
/**
114-
* Unsupported, {@link ForeignException} instances are not writable therefore setting the stack
115-
* trace has no effect for them.
124+
* Unsupported, {@link ForeignException} instances are by definition foreign and thus
125+
* stacktraces should not be controllable by the guest.
116126
*/
117127
@Override
118128
public void setStackTrace(StackTraceElement[] stackTrace) {

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/Symbol.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -243,11 +243,13 @@ public static void ensureInitialized() {
243243

244244
// j.l.Throwable
245245
public static final Symbol<Name> backtrace = StaticSymbols.putName("backtrace");
246+
public static final Symbol<Name> stackTrace = StaticSymbols.putName("stackTrace");
246247
public static final Symbol<Name> cause = StaticSymbols.putName("cause");
247248
public static final Symbol<Name> depth = StaticSymbols.putName("depth");
248249
public static final Symbol<Name> fillInStackTrace = StaticSymbols.putName("fillInStackTrace");
249250
public static final Symbol<Name> fillInStackTrace0 = StaticSymbols.putName("fillInStackTrace0");
250251
public static final Symbol<Name> getMessage = StaticSymbols.putName("getMessage");
252+
public static final Symbol<Name> getCause = StaticSymbols.putName("getCause");
251253
public static final Symbol<Name> detailMessage = StaticSymbols.putName("detailMessage");
252254
public static final Symbol<Name> printStackTrace = StaticSymbols.putName("printStackTrace");
253255

@@ -932,6 +934,7 @@ public static void ensureInitialized() {
932934
public static final Symbol<Signature> _void_String_array = StaticSymbols.putSignature(Type._void, Type.java_lang_String_array);
933935
public static final Symbol<Signature> Class_String_boolean_ClassLoader = StaticSymbols.putSignature(Type.java_lang_Class, Type.java_lang_String, Type._boolean, Type.java_lang_ClassLoader);
934936

937+
public static final Symbol<Signature> Throwable = StaticSymbols.putSignature(Type.java_lang_Throwable);
935938
public static final Symbol<Signature> _void_Throwable = StaticSymbols.putSignature(Type._void, Type.java_lang_Throwable);
936939
public static final Symbol<Signature> StackTraceElement_array = StaticSymbols.putSignature(Type.java_lang_StackTraceElement_array);
937940
public static final Symbol<Signature> _void_String_Throwable = StaticSymbols.putSignature(Type._void, Type.java_lang_String, Type.java_lang_Throwable);

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Klass.java

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -43,6 +43,7 @@
4343
import com.oracle.truffle.api.dsl.Cached.Shared;
4444
import com.oracle.truffle.api.dsl.Fallback;
4545
import com.oracle.truffle.api.dsl.Specialization;
46+
import com.oracle.truffle.api.exception.AbstractTruffleException;
4647
import com.oracle.truffle.api.interop.ArityException;
4748
import com.oracle.truffle.api.interop.InteropLibrary;
4849
import com.oracle.truffle.api.interop.TruffleObject;
@@ -225,31 +226,39 @@ final Object invokeMember(String member,
225226
@Exclusive @Cached ToEspressoNode toEspressoNode)
226227
throws ArityException, UnknownIdentifierException, UnsupportedTypeException {
227228
Method[] candidates = lookupMethod.execute(this, member, true, true, arguments.length);
228-
if (candidates != null) {
229-
if (candidates.length == 1) {
230-
Method method = candidates[0];
231-
assert method.isStatic() && method.isPublic();
232-
assert member.startsWith(method.getNameAsString());
233-
if (!method.isVarargs()) {
234-
assert method.getParameterCount() == arguments.length;
235-
return invoke.execute(method, null, arguments);
236-
} else {
237-
CandidateMethodWithArgs matched = MethodArgsUtils.matchCandidate(method, arguments, method.resolveParameterKlasses(), toEspressoNode);
238-
if (matched != null) {
239-
matched = MethodArgsUtils.ensureVarArgsArrayCreated(matched, toEspressoNode);
229+
try {
230+
if (candidates != null) {
231+
if (candidates.length == 1) {
232+
Method method = candidates[0];
233+
assert method.isStatic() && method.isPublic();
234+
assert member.startsWith(method.getNameAsString());
235+
if (!method.isVarargs()) {
236+
assert method.getParameterCount() == arguments.length;
237+
return invoke.execute(method, null, arguments);
238+
} else {
239+
CandidateMethodWithArgs matched = MethodArgsUtils.matchCandidate(method, arguments, method.resolveParameterKlasses(), toEspressoNode);
240240
if (matched != null) {
241-
return invoke.execute(matched.getMethod(), null, matched.getConvertedArgs(), true);
241+
matched = MethodArgsUtils.ensureVarArgsArrayCreated(matched, toEspressoNode);
242+
if (matched != null) {
243+
return invoke.execute(matched.getMethod(), null, matched.getConvertedArgs(), true);
244+
}
242245
}
243246
}
244-
}
245-
} else {
246-
CandidateMethodWithArgs typeMatched = overloadSelector.execute(candidates, arguments);
247-
if (typeMatched != null) {
248-
return invoke.execute(typeMatched.getMethod(), null, typeMatched.getConvertedArgs(), true);
247+
} else {
248+
CandidateMethodWithArgs typeMatched = overloadSelector.execute(candidates, arguments);
249+
if (typeMatched != null) {
250+
return invoke.execute(typeMatched.getMethod(), null, typeMatched.getConvertedArgs(), true);
251+
}
249252
}
250253
}
254+
throw UnknownIdentifierException.create(member);
255+
} catch (EspressoException e) {
256+
if (e.getGuestException().getKlass() == getMeta().polyglot.ForeignException) {
257+
// rethrow the original foreign exception when leaving espresso interop
258+
throw (AbstractTruffleException) getMeta().java_lang_Throwable_backtrace.getObject(e.getGuestException()).rawForeignObject(getLanguage());
259+
}
260+
throw e;
251261
}
252-
throw UnknownIdentifierException.create(member);
253262
}
254263

255264
@SuppressWarnings("static-method")

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/InteropKlassesDispatch.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,7 @@
3030
import com.oracle.truffle.espresso.impl.ObjectKlass;
3131
import com.oracle.truffle.espresso.runtime.dispatch.BaseInterop;
3232
import com.oracle.truffle.espresso.runtime.dispatch.EspressoInterop;
33+
import com.oracle.truffle.espresso.runtime.dispatch.ForeignExceptionInterop;
3334
import com.oracle.truffle.espresso.runtime.dispatch.InterruptedExceptionInterop;
3435
import com.oracle.truffle.espresso.runtime.dispatch.IterableInterop;
3536
import com.oracle.truffle.espresso.runtime.dispatch.IteratorInterop;
@@ -72,6 +73,12 @@ public Class<?> resolveDispatch(Klass k) {
7273
} else if (k.isArray()) {
7374
result = EspressoInterop.class;
7475
} else {
76+
// ForeignException is not injected until post system init, meaning we can't
77+
// put in into the static dispatch pair mappings.
78+
if (k.getMeta().polyglot != null && k.getMeta().polyglot.ForeignException == k) {
79+
return ForeignExceptionInterop.class;
80+
}
81+
7582
exclusiveLoop: //
7683
for (Pair<ObjectKlass, Class<?>>[] exclusive : classes) {
7784
for (Pair<ObjectKlass, Class<?>> pair : exclusive) {

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -225,9 +225,12 @@ public Meta(EspressoContext context) {
225225

226226
java_lang_Throwable = knownKlass(Type.java_lang_Throwable);
227227
java_lang_Throwable_getStackTrace = java_lang_Throwable.requireDeclaredMethod(Name.getStackTrace, Signature.StackTraceElement_array);
228+
java_lang_Throwable_getMessage = java_lang_Throwable.requireDeclaredMethod(Name.getMessage, Signature.String);
229+
java_lang_Throwable_getCause = java_lang_Throwable.requireDeclaredMethod(Name.getCause, Signature.Throwable);
228230
HIDDEN_FRAMES = java_lang_Throwable.requireHiddenField(Name.HIDDEN_FRAMES);
229231
HIDDEN_EXCEPTION_WRAPPER = java_lang_Throwable.requireHiddenField(Name.HIDDEN_EXCEPTION_WRAPPER);
230232
java_lang_Throwable_backtrace = java_lang_Throwable.requireDeclaredField(Name.backtrace, Type.java_lang_Object);
233+
java_lang_Throwable_stackTrace = java_lang_Throwable.requireDeclaredField(Name.stackTrace, Type.java_lang_StackTraceElement_array);
231234
java_lang_Throwable_detailMessage = java_lang_Throwable.requireDeclaredField(Name.detailMessage, Type.java_lang_String);
232235
java_lang_Throwable_cause = java_lang_Throwable.requireDeclaredField(Name.cause, Type.java_lang_Throwable);
233236
if (getJavaVersion().java9OrLater()) {
@@ -1167,9 +1170,12 @@ private DiffVersionLoadHelper diff() {
11671170

11681171
public final ObjectKlass java_lang_Throwable;
11691172
public final Method java_lang_Throwable_getStackTrace;
1173+
public final Method java_lang_Throwable_getMessage;
1174+
public final Method java_lang_Throwable_getCause;
11701175
public final Field HIDDEN_FRAMES;
11711176
public final Field HIDDEN_EXCEPTION_WRAPPER;
11721177
public final Field java_lang_Throwable_backtrace;
1178+
public final Field java_lang_Throwable_stackTrace;
11731179
public final Field java_lang_Throwable_detailMessage;
11741180
public final Field java_lang_Throwable_cause;
11751181
public final Field java_lang_Throwable_depth;

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/EspressoRootNode.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,11 +26,13 @@
2626

2727
import com.oracle.truffle.api.CompilerDirectives;
2828
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
29+
import com.oracle.truffle.api.TruffleStackTraceElement;
2930
import com.oracle.truffle.api.frame.Frame;
3031
import com.oracle.truffle.api.frame.FrameDescriptor;
3132
import com.oracle.truffle.api.frame.VirtualFrame;
3233
import com.oracle.truffle.api.instrumentation.StandardTags;
3334
import com.oracle.truffle.api.interop.TruffleObject;
35+
import com.oracle.truffle.api.nodes.Node;
3436
import com.oracle.truffle.api.nodes.NodeUtil;
3537
import com.oracle.truffle.api.nodes.RootNode;
3638
import com.oracle.truffle.api.profiles.BranchProfile;
@@ -39,6 +41,7 @@
3941
import com.oracle.truffle.espresso.impl.Method;
4042
import com.oracle.truffle.espresso.meta.Meta;
4143
import com.oracle.truffle.espresso.runtime.EspressoContext;
44+
import com.oracle.truffle.espresso.runtime.ForeignStackTraceElementObject;
4245
import com.oracle.truffle.espresso.runtime.StaticObject;
4346
import com.oracle.truffle.espresso.substitutions.CallableFromNative;
4447
import com.oracle.truffle.espresso.substitutions.JavaSubstitution;
@@ -132,6 +135,12 @@ public String getQualifiedName() {
132135
return getMethod().getDeclaringKlass().getType() + "." + getMethod().getName() + getMethod().getRawSignature();
133136
}
134137

138+
@Override
139+
protected Object translateStackTraceElement(TruffleStackTraceElement element) {
140+
Node location = element.getLocation();
141+
return new ForeignStackTraceElementObject(getMethod(), location != null ? location.getEncapsulatingSourceSection() : getEncapsulatingSourceSection());
142+
}
143+
135144
@Override
136145
public final String toString() {
137146
return getQualifiedName();
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright (c) 2023, 2023, 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.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
package com.oracle.truffle.espresso.runtime;
25+
26+
import com.oracle.truffle.api.interop.InteropLibrary;
27+
import com.oracle.truffle.api.interop.TruffleObject;
28+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
29+
import com.oracle.truffle.api.library.ExportLibrary;
30+
import com.oracle.truffle.api.library.ExportMessage;
31+
import com.oracle.truffle.api.source.SourceSection;
32+
import com.oracle.truffle.espresso.impl.Method;
33+
34+
@ExportLibrary(InteropLibrary.class)
35+
public final class ForeignStackTraceElementObject implements TruffleObject {
36+
37+
private final Method method;
38+
private final SourceSection sourceSection;
39+
40+
public ForeignStackTraceElementObject(Method method, SourceSection sourceSection) {
41+
this.method = method;
42+
this.sourceSection = sourceSection;
43+
}
44+
45+
@ExportMessage
46+
@SuppressWarnings("static-method")
47+
boolean hasExecutableName() {
48+
return true;
49+
}
50+
51+
@ExportMessage
52+
Object getExecutableName() {
53+
return method.getNameAsString();
54+
}
55+
56+
@ExportMessage
57+
boolean hasSourceLocation() {
58+
return sourceSection != null;
59+
}
60+
61+
@ExportMessage
62+
SourceSection getSourceLocation() throws UnsupportedMessageException {
63+
if (sourceSection == null) {
64+
throw UnsupportedMessageException.create();
65+
} else {
66+
return sourceSection;
67+
}
68+
}
69+
70+
@ExportMessage
71+
@SuppressWarnings("static-method")
72+
boolean hasDeclaringMetaObject() {
73+
return true;
74+
}
75+
76+
@ExportMessage
77+
Object getDeclaringMetaObject() {
78+
return method.getDeclaringKlass().mirror();
79+
}
80+
}

0 commit comments

Comments
 (0)