Skip to content

Commit bb637c9

Browse files
committed
Fix cloning an exception
PullRequest: truffleruby/552
2 parents 5667e6c + d7f724c commit bb637c9

File tree

8 files changed

+61
-23
lines changed

8 files changed

+61
-23
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Bug fixes:
66
characters (#1543).
77
* Fixed an issue with `String#{encode,encode!}` double-processing strings
88
using XML conversion options and a new destination encoding (#1545).
9+
* Fixed a bug where a raised cloned exception would be caught as the
10+
original exception (#1542).
911

1012
Compatibility:
1113

spec/ruby/core/exception/exception_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,22 @@
6666
e2.message.should == "message"
6767
end
6868

69+
it "when raised will be rescued as the new exception" do
70+
begin
71+
begin
72+
raised_first = StandardError.new('first')
73+
raise raised_first
74+
rescue => caught_first
75+
raised_second = raised_first.exception('second')
76+
raise raised_second
77+
end
78+
rescue => caught_second
79+
end
80+
81+
raised_first.should == caught_first
82+
raised_second.should == caught_second
83+
end
84+
6985
class CustomArgumentError < StandardError
7086
attr_reader :val
7187
def initialize(val)

src/main/java/org/truffleruby/core/VMPrimitiveNodes.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,21 +218,22 @@ public static abstract class VMRaiseExceptionNode extends PrimitiveArrayArgument
218218
public DynamicObject vmRaiseException(DynamicObject exception, boolean internal,
219219
@Cached("createBinaryProfile()") ConditionProfile reRaiseProfile) {
220220
final Backtrace backtrace = Layouts.EXCEPTION.getBacktrace(exception);
221-
if (reRaiseProfile.profile(backtrace != null && backtrace.getTruffleException() != null)) {
222-
// We need to rethrow the existing TruffleException, otherwise we would lose the
221+
if (reRaiseProfile.profile(backtrace != null && backtrace.getRaiseException() != null)) {
222+
// We need to rethrow the existing RaiseException, otherwise we would lose the
223223
// TruffleStackTrace stored in it.
224-
throw (RaiseException) backtrace.getTruffleException();
224+
assert backtrace.getRaiseException().getException() == exception;
225+
throw backtrace.getRaiseException();
225226
} else {
226227
throw new RaiseException(getContext(), exception, internal);
227228
}
228229
}
229230

230231
public static void reRaiseException(RubyContext context, DynamicObject exception) {
231232
final Backtrace backtrace = Layouts.EXCEPTION.getBacktrace(exception);
232-
if (backtrace != null && backtrace.getTruffleException() != null) {
233-
// We need to rethrow the existing TruffleException, otherwise we would lose the
233+
if (backtrace != null && backtrace.getRaiseException() != null) {
234+
// We need to rethrow the existing RaiseException, otherwise we would lose the
234235
// TruffleStackTrace stored in it.
235-
throw (RaiseException) backtrace.getTruffleException();
236+
throw backtrace.getRaiseException();
236237
} else {
237238
throw new RaiseException(context, exception, false);
238239
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,12 @@ public Object initializeCopySelfIsSameAsFrom(DynamicObject self, DynamicObject f
7777

7878
@Specialization(guards = { "self != from", "isRubyException(from)" })
7979
public Object initializeCopy(DynamicObject self, DynamicObject from) {
80-
Layouts.EXCEPTION.setBacktrace(self, Layouts.EXCEPTION.getBacktrace(from));
80+
Backtrace backtrace = Layouts.EXCEPTION.getBacktrace(from);
81+
if (backtrace != null) {
82+
Layouts.EXCEPTION.setBacktrace(self, backtrace.copy(getContext(), self));
83+
} else {
84+
Layouts.EXCEPTION.setBacktrace(self, backtrace);
85+
}
8186
Layouts.EXCEPTION.setFormatter(self, Layouts.EXCEPTION.getFormatter(from));
8287
Layouts.EXCEPTION.setMessage(self, Layouts.EXCEPTION.getMessage(from));
8388

src/main/java/org/truffleruby/core/kernel/KernelNodes.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,7 @@ private DynamicObject innerCallerLocations(int omit, int length) {
310310
final Backtrace backtrace = getContext().getCallStack().getBacktrace(this, omitted);
311311
final int limit = (length == GetBacktraceException.UNLIMITED) ? GetBacktraceException.UNLIMITED : omitted + length;
312312

313-
backtrace.setTruffleException(new GetBacktraceException(this, limit));
314-
315-
int locationsCount = backtrace.getActivations().length;
313+
int locationsCount = backtrace.getActivations(new GetBacktraceException(this, limit)).length;
316314

317315
if (length != GetBacktraceException.UNLIMITED && length < locationsCount) {
318316
locationsCount = length;

src/main/java/org/truffleruby/core/thread/ThreadManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ private static void setException(RubyContext context, DynamicObject thread, Dyna
280280

281281
// We materialize the backtrace eagerly here, as the exception escapes the thread and needs
282282
// to capture the backtrace from this thread.
283-
final TruffleException truffleException = Layouts.EXCEPTION.getBacktrace(exception).getTruffleException();
283+
final TruffleException truffleException = Layouts.EXCEPTION.getBacktrace(exception).getRaiseException();
284284
if (truffleException != null) {
285285
TruffleStackTraceElement.fillIn((Throwable) truffleException);
286286
}

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

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.truffleruby.core.exception.GetBacktraceException;
2222
import org.truffleruby.language.CallStackManager;
2323
import org.truffleruby.language.arguments.RubyArguments;
24+
import org.truffleruby.language.control.RaiseException;
2425
import org.truffleruby.language.methods.InternalMethod;
2526

2627
import java.util.ArrayList;
@@ -30,7 +31,7 @@ public class Backtrace {
3031

3132
private final Node location;
3233
private SourceSection sourceLocation;
33-
private TruffleException truffleException;
34+
private RaiseException raiseException;
3435
private Activation[] activations;
3536
private final int omitted;
3637
private final Throwable javaThrowable;
@@ -43,6 +44,20 @@ public Backtrace(Node location, SourceSection sourceLocation, int omitted, Throw
4344
this.javaThrowable = javaThrowable;
4445
}
4546

47+
public Backtrace copy(RubyContext context, DynamicObject exception) {
48+
Backtrace copy = new Backtrace(location, sourceLocation, omitted, javaThrowable);
49+
// A Backtrace is 1-1-1 with a RaiseException and a Ruby exception
50+
RaiseException newRaiseException = new RaiseException(context, exception, this.raiseException.isInternalError());
51+
// Copy the TruffleStackTrace
52+
TruffleStackTraceElement.fillIn(this.raiseException);
53+
assert this.raiseException.getCause() != null;
54+
newRaiseException.initCause(this.raiseException.getCause());
55+
// Another way would be to copy the activations (copy.activations = getActivations()), but
56+
// then the TruffleStrackTrace would be inconsistent.
57+
copy.setRaiseException(newRaiseException);
58+
return copy;
59+
}
60+
4661
public Node getLocation() {
4762
return location;
4863
}
@@ -51,22 +66,23 @@ public SourceSection getSourceLocation() {
5166
return sourceLocation;
5267
}
5368

54-
public TruffleException getTruffleException() {
55-
return truffleException;
69+
public RaiseException getRaiseException() {
70+
return raiseException;
5671
}
5772

58-
public void setTruffleException(TruffleException truffleException) {
59-
assert this.truffleException == null : "the TruffleException of a Backtrace must not be set again, otherwise the original backtrace is lost";
60-
this.truffleException = truffleException;
73+
public void setRaiseException(RaiseException raiseException) {
74+
assert this.raiseException == null : "the RaiseException of a Backtrace must not be set again, otherwise the original backtrace is lost";
75+
this.raiseException = raiseException;
6176
}
6277

63-
@TruffleBoundary
6478
public Activation[] getActivations() {
79+
return getActivations(this.raiseException);
80+
}
81+
82+
@TruffleBoundary
83+
public Activation[] getActivations(TruffleException truffleException) {
6584
if (this.activations == null) {
66-
final TruffleException truffleException;
67-
if (this.truffleException != null) {
68-
truffleException = this.truffleException;
69-
} else {
85+
if (truffleException == null) {
7086
truffleException = new GetBacktraceException(location, GetBacktraceException.UNLIMITED);
7187
}
7288

src/main/java/org/truffleruby/language/control/RaiseException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public RaiseException(RubyContext context, DynamicObject exception, boolean inte
4242

4343
final Backtrace backtrace = Layouts.EXCEPTION.getBacktrace(exception);
4444
if (backtrace != null) { // The backtrace could be null if for example a user backtrace was passed to Kernel#raise
45-
backtrace.setTruffleException(this);
45+
backtrace.setRaiseException(this);
4646
}
4747

4848
assert !isSyntaxError() || getSourceLocation() != null;

0 commit comments

Comments
 (0)