Skip to content

Commit 2b2a7f8

Browse files
committed
[GR-17066] Get the TruffleStackTrace of non-Ruby exceptions in ExceptionTranslatingNode.
PullRequest: truffleruby/941
2 parents 241c516 + 1927cee commit 2b2a7f8

File tree

4 files changed

+61
-11
lines changed

4 files changed

+61
-11
lines changed

src/main/java/org/truffleruby/core/exception/CoreExceptions.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,13 @@ public DynamicObject runtimeError(String fullMessage, Node currentNode, Throwabl
246246
return ExceptionOperations.createRubyException(context, exceptionClass, errorMessage, currentNode, javaThrowable);
247247
}
248248

249+
@TruffleBoundary
250+
public DynamicObject runtimeError(String fullMessage, Backtrace backtrace) {
251+
DynamicObject exceptionClass = context.getCoreLibrary().getRuntimeErrorClass();
252+
DynamicObject errorMessage = StringOperations.createString(context, StringOperations.encodeRope(fullMessage, UTF8Encoding.INSTANCE));
253+
return ExceptionOperations.createRubyException(context, exceptionClass, errorMessage, backtrace);
254+
}
255+
249256
// SystemStackError
250257

251258
@TruffleBoundary

src/main/java/org/truffleruby/core/exception/ExceptionOperations.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public static DynamicObject createSystemCallError(RubyContext context, DynamicOb
7171

7272
// because the factory is not constant
7373
@TruffleBoundary
74-
private static DynamicObject createRubyException(RubyContext context, DynamicObject rubyClass, Object message, Backtrace backtrace) {
74+
public static DynamicObject createRubyException(RubyContext context, DynamicObject rubyClass, Object message, Backtrace backtrace) {
7575
final DynamicObject cause = ThreadGetExceptionNode.getLastException(context);
7676
context.getCoreExceptions().showExceptionIfDebug(rubyClass, message, backtrace);
7777
return Layouts.CLASS.getInstanceFactory(rubyClass).newInstance(Layouts.EXCEPTION.build(message, null, backtrace, cause));

src/main/java/org/truffleruby/language/backtrace/Backtrace.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,24 @@ public Backtrace(Node location, SourceSection sourceLocation, int omitted, Throw
4545
this.javaThrowable = javaThrowable;
4646
}
4747

48+
public Backtrace(TruffleException exception) {
49+
this.location = exception.getLocation();
50+
this.sourceLocation = exception.getSourceLocation();
51+
this.omitted = 0;
52+
this.javaThrowable = null;
53+
54+
this.activations = getActivations((Throwable) exception);
55+
}
56+
57+
public Backtrace(Throwable exception) {
58+
this.location = null;
59+
this.sourceLocation = null;
60+
this.omitted = 0;
61+
this.javaThrowable = null;
62+
63+
this.activations = getActivations(exception);
64+
}
65+
4866
public Backtrace copy(RubyContext context, DynamicObject exception) {
4967
Backtrace copy = new Backtrace(location, sourceLocation, omitted, javaThrowable);
5068
// A Backtrace is 1-1-1 with a RaiseException and a Ruby exception
@@ -81,15 +99,15 @@ public Activation[] getActivations() {
8199
}
82100

83101
@TruffleBoundary
84-
public Activation[] getActivations(TruffleException truffleException) {
102+
public Activation[] getActivations(Throwable truffleException) {
85103
if (this.activations == null) {
86104
if (truffleException == null) {
87105
truffleException = new GetBacktraceException(location, GetBacktraceException.UNLIMITED);
88106
}
89107

90108
// The stacktrace is computed here if it was not already computed and stored in the
91109
// TruffleException with TruffleStackTraceElement.fillIn().
92-
final List<TruffleStackTraceElement> stackTrace = TruffleStackTrace.getStackTrace((Throwable) truffleException);
110+
final List<TruffleStackTraceElement> stackTrace = TruffleStackTrace.getStackTrace(truffleException);
93111

94112
final List<Activation> activations = new ArrayList<>();
95113
final RubyContext context = RubyLanguage.getCurrentContext();

src/main/java/org/truffleruby/language/methods/ExceptionTranslatingNode.java

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1313
import com.oracle.truffle.api.TruffleException;
14+
import com.oracle.truffle.api.TruffleStackTrace;
1415
import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
1516
import com.oracle.truffle.api.frame.VirtualFrame;
1617
import com.oracle.truffle.api.nodes.ControlFlowException;
@@ -19,6 +20,7 @@
1920
import org.truffleruby.Layouts;
2021
import org.truffleruby.language.RubyGuards;
2122
import org.truffleruby.language.RubyNode;
23+
import org.truffleruby.language.backtrace.Backtrace;
2224
import org.truffleruby.language.backtrace.BacktraceFormatter;
2325
import org.truffleruby.language.backtrace.BacktraceFormatter.FormattingFlags;
2426
import org.truffleruby.language.control.JavaException;
@@ -261,13 +263,19 @@ private DynamicObject translateThrowable(Throwable throwable) {
261263

262264
final StringBuilder builder = new StringBuilder();
263265
boolean firstException = true;
266+
Backtrace lastBacktrace = null;
264267

265268
while (t != null) {
266269
if (t.getClass().getSimpleName().equals("LazyStackTrace")) {
267270
// Truffle's lazy stracktrace support, not a real exception
268271
break;
269272
}
270273

274+
if (lastBacktrace != null) {
275+
appendTruffleStackTrace(builder, lastBacktrace);
276+
lastBacktrace = null;
277+
}
278+
271279
if (!firstException) {
272280
builder.append("Caused by:\n");
273281
}
@@ -285,13 +293,17 @@ private DynamicObject translateThrowable(Throwable throwable) {
285293
// Java exception, print it formatted like a Ruby exception
286294
final String message = t.getMessage();
287295
builder.append(message != null ? message : "<no message>");
288-
289296
builder.append(" (").append(t.getClass().getSimpleName()).append(")\n");
290297

291-
// Print the first 10 lines of backtrace
292-
final StackTraceElement[] stackTrace = t.getStackTrace();
293-
for (int i = 0; i < Math.min(stackTrace.length, 10); i++) {
294-
stackTraceElementToRuby(builder, stackTrace[i]);
298+
if (t instanceof TruffleException) {
299+
lastBacktrace = new Backtrace((TruffleException) t);
300+
} else {
301+
// Print the first 10 lines of the Java stacktrace
302+
appendJavaStackTrace(t, builder, 10);
303+
304+
if (TruffleStackTrace.getStackTrace(t) != null) {
305+
lastBacktrace = new Backtrace(t);
306+
}
295307
}
296308
}
297309

@@ -302,11 +314,24 @@ private DynamicObject translateThrowable(Throwable throwable) {
302314
// When printing the backtrace of the exception, make it clear it's not a cause
303315
builder.append("Translated to internal error");
304316

305-
return coreExceptions().runtimeError(builder.toString(), this, throwable);
317+
if (lastBacktrace != null) {
318+
return coreExceptions().runtimeError(builder.toString(), lastBacktrace);
319+
} else {
320+
return coreExceptions().runtimeError(builder.toString(), this, throwable);
321+
}
322+
}
323+
324+
private void appendTruffleStackTrace(StringBuilder builder, Backtrace backtrace) {
325+
final BacktraceFormatter formatter = new BacktraceFormatter(getContext(), EnumSet.noneOf(FormattingFlags.class));
326+
final String formattedBacktrace = formatter.formatBacktrace(null, backtrace);
327+
builder.append(formattedBacktrace).append('\n');
306328
}
307329

308-
private void stackTraceElementToRuby(StringBuilder builder, StackTraceElement stackTraceElement) {
309-
builder.append('\t').append("from ").append(stackTraceElement).append('\n');
330+
private void appendJavaStackTrace(Throwable t, StringBuilder builder, int limit) {
331+
final StackTraceElement[] stackTrace = t.getStackTrace();
332+
for (int i = 0; i < Math.min(stackTrace.length, limit); i++) {
333+
builder.append('\t').append("from ").append(stackTrace[i]).append('\n');
334+
}
310335
}
311336

312337
}

0 commit comments

Comments
 (0)