Skip to content

Commit 91f5943

Browse files
committed
Fix "circular causes" error at raising exception
1 parent 7febb68 commit 91f5943

File tree

5 files changed

+102
-7
lines changed

5 files changed

+102
-7
lines changed

spec/ruby/core/kernel/raise_spec.rb

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,65 @@
203203
e.cause.should == e1
204204
end
205205
end
206+
207+
it "re-raises a previously rescued exception that doesn't have a cause and isn't a cause of any other exception with setting a cause implicitly" do
208+
begin
209+
begin
210+
raise "Error 1"
211+
rescue => e1
212+
begin
213+
raise "Error 2"
214+
rescue => e2
215+
raise "Error 3"
216+
end
217+
end
218+
rescue => e
219+
e.message.should == "Error 3"
220+
e.cause.should == e2
221+
end
222+
end
223+
224+
it "re-raises a previously rescued exception that doesn't have a cause and is a cause of other exception without setting a cause implicitly" do
225+
begin
226+
begin
227+
raise "Error 1"
228+
rescue => e1
229+
begin
230+
raise "Error 2"
231+
rescue => e2
232+
e1.cause.should == nil
233+
e2.cause.should == e1
234+
raise e1
235+
end
236+
end
237+
rescue => e
238+
e.should == e1
239+
e.cause.should == nil
240+
end
241+
end
242+
243+
it "re-raises a previously rescued exception that has a cause but isn't a cause of any other exception without setting a cause implicitly" do
244+
begin
245+
begin
246+
raise "Error 1"
247+
rescue => e1
248+
begin
249+
raise "Error 2"
250+
rescue => e2
251+
begin
252+
raise "Error 3", cause: RuntimeError.new("Error 4")
253+
rescue => e3
254+
e2.cause.should == e1
255+
e3.cause.should_not == e2
256+
raise e2
257+
end
258+
end
259+
end
260+
rescue => e
261+
e.should == e2
262+
e.cause.should == e1
263+
end
264+
end
206265
end
207266

208267
describe "Kernel#raise" do

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010
package org.truffleruby.core.exception;
1111

12+
import com.oracle.truffle.api.dsl.Fallback;
1213
import com.oracle.truffle.api.interop.InteropLibrary;
1314
import com.oracle.truffle.api.library.CachedLibrary;
1415
import com.oracle.truffle.api.object.Shape;
@@ -368,4 +369,35 @@ Object getRaiseException(RubyException exception) {
368369

369370
}
370371

372+
@Primitive(name = "exception_used_as_a_cause?")
373+
public abstract static class IsUsedAsACauseNode extends PrimitiveArrayArgumentsNode {
374+
375+
@Specialization
376+
boolean isUsedAsACause(RubyException exception) {
377+
return exception.usedAsACause;
378+
}
379+
380+
@Fallback
381+
boolean isUsedAsACauseForeignException(Object exception) {
382+
return false;
383+
}
384+
385+
}
386+
387+
@Primitive(name = "exception_used_as_a_cause!")
388+
public abstract static class UsedAsACauseNode extends PrimitiveArrayArgumentsNode {
389+
390+
@Specialization
391+
Object usedAsACause(RubyException exception) {
392+
exception.usedAsACause = true;
393+
return nil;
394+
}
395+
396+
@Fallback
397+
Object usedAsACauseForeignException(Object exception) {
398+
return nil;
399+
}
400+
401+
}
402+
371403
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public class RubyException extends RubyDynamicObject implements ObjectGraphNode
5050
public Object backtraceLocations = null;
5151
/** null (not set), RubyArray of Strings, or nil (empty) */
5252
public Object customBacktrace = null;
53+
public boolean usedAsACause = false;
5354

5455
public RubyException(RubyClass rubyClass, Shape shape, Object message, Backtrace backtrace, Object cause) {
5556
super(rubyClass, shape);

src/main/ruby/truffleruby/core/kernel.rb

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -698,25 +698,29 @@ def raise(exc = undefined, msg = undefined, ctx = nil, cause: undefined, **kwarg
698698
msg = kwargs
699699
end
700700

701+
exc = Truffle::ExceptionOperations.build_exception_for_raise(exc, msg)
702+
exc.set_backtrace(ctx) if ctx
703+
Primitive.exception_capture_backtrace(exc, 1) unless Truffle::ExceptionOperations.backtrace?(exc)
704+
701705
if cause_given
702706
unless Primitive.is_a?(cause, ::Exception) || Primitive.nil?(cause)
703707
Truffle::ExceptionOperations.exception_object_expected!
704708
end
705709
else
706-
cause = $!
710+
if Primitive.nil?(exc.cause) && !Primitive.exception_used_as_a_cause?(exc)
711+
cause = $!
712+
else
713+
cause = nil
714+
end
707715
end
708716

709-
exc = Truffle::ExceptionOperations.build_exception_for_raise(exc, msg)
710-
711-
exc.set_backtrace(ctx) if ctx
712-
Primitive.exception_capture_backtrace(exc, 1) unless Truffle::ExceptionOperations.backtrace?(exc)
713-
714717
if !Primitive.nil?(cause) && (cause_given || Primitive.nil?(exc.cause)) && !Primitive.equal?(cause, exc)
715718
if Truffle::ExceptionOperations.circular_cause?(cause, exc)
716719
raise ArgumentError, 'circular causes'
717720
end
718721

719722
Primitive.exception_set_cause exc, cause
723+
Primitive.exception_used_as_a_cause!(cause)
720724
end
721725
end
722726

test/mri/excludes/TestException.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
exclude :test_cause_raised_in_rescue, "cause should not be overwritten by reraise."
44
exclude :test_cause_thread_with_cause, "NoMethodError: undefined method `message' for nil:NilClass"
55
exclude :test_circular_cause, "Expected #<RuntimeError: error 2> to be nil."
6-
exclude :test_circular_cause_handle, "Exception(ArgumentError) with message matches to /circular cause/."
76
exclude :test_errinfo_encoding_in_debug, "Expected \"\" to include \"#<Module:0x608>::Cエラー\"."
87
exclude :test_errinfo_in_debug, "needs investigation"
98
exclude :test_exception_in_ensure_with_next, "NameError: uninitialized constant TestException::RubyVM"

0 commit comments

Comments
 (0)