Skip to content

Commit db23fce

Browse files
committed
[GR-20446] Implement rb_make_exception
PullRequest: truffleruby/2211
2 parents 9a234e0 + b4070bf commit db23fce

File tree

6 files changed

+87
-0
lines changed

6 files changed

+87
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Compatibility:
3030
* `Kernel#lambda` with no block in a method called with a block raises an exception (#2004, @ssnickolay).
3131
* Implemented `BigDecimal` as C extension to improve compatibility.
3232
* Comment lines can be placed between fluent dot now (#2004, @ssnickolay).
33+
* Implemented `rb_make_exception`.
3334

3435
Performance:
3536

lib/truffle/truffle/cext.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,10 @@ def rb_set_errinfo(error)
10531053
Truffle::Type.set_last_exception(error)
10541054
end
10551055

1056+
def rb_make_exception(args)
1057+
Truffle::ExceptionOperations.make_exception(args)
1058+
end
1059+
10561060
def rb_errinfo
10571061
$!
10581062
end

spec/ruby/optional/capi/exception_spec.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,49 @@
5555
-> { @s.rb_set_errinfo("error") }.should raise_error(TypeError)
5656
end
5757
end
58+
59+
describe "rb_make_exception" do
60+
it "returns a RuntimeError when given a String argument" do
61+
e = @s.rb_make_exception(["Message"])
62+
e.class.should == RuntimeError
63+
e.message.should == "Message"
64+
end
65+
66+
it "returns the exception when given an Exception argument" do
67+
exc = Exception.new
68+
e = @s.rb_make_exception([exc])
69+
e.should == exc
70+
end
71+
72+
it "returns the exception with the given class and message" do
73+
e = @s.rb_make_exception([Exception, "Message"])
74+
e.class.should == Exception
75+
e.message.should == "Message"
76+
end
77+
78+
it "returns the exception with the given class, message, and backtrace" do
79+
e = @s.rb_make_exception([Exception, "Message", ["backtrace 1"]])
80+
e.class.should == Exception
81+
e.message.should == "Message"
82+
e.backtrace.should == ["backtrace 1"]
83+
end
84+
85+
it "raises a TypeError for incorrect types" do
86+
-> { @s.rb_make_exception([nil]) }.should raise_error(TypeError)
87+
-> { @s.rb_make_exception([Object.new]) }.should raise_error(TypeError)
88+
obj = Object.new
89+
def obj.exception
90+
"not exception type"
91+
end
92+
-> { @s.rb_make_exception([obj]) }.should raise_error(TypeError)
93+
end
94+
95+
it "raises an ArgumentError for too many arguments" do
96+
-> { @s.rb_make_exception([Exception, "Message", ["backtrace 1"], "extra"]) }.should raise_error(ArgumentError)
97+
end
98+
99+
it "returns nil for empty arguments" do
100+
@s.rb_make_exception([]).should == nil
101+
end
102+
end
58103
end

spec/ruby/optional/capi/ext/exception_spec.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,21 @@ VALUE exception_spec_rb_set_errinfo(VALUE self, VALUE exc) {
3232
return Qnil;
3333
}
3434

35+
36+
VALUE exception_spec_rb_make_exception(VALUE self, VALUE ary) {
37+
int argc = RARRAY_LENINT(ary);
38+
VALUE *argv = RARRAY_PTR(ary);
39+
return rb_make_exception(argc, argv);
40+
}
41+
3542
void Init_exception_spec(void) {
3643
VALUE cls = rb_define_class("CApiExceptionSpecs", rb_cObject);
3744
rb_define_method(cls, "rb_exc_new", exception_spec_rb_exc_new, 1);
3845
rb_define_method(cls, "rb_exc_new2", exception_spec_rb_exc_new2, 1);
3946
rb_define_method(cls, "rb_exc_new3", exception_spec_rb_exc_new3, 1);
4047
rb_define_method(cls, "rb_exc_raise", exception_spec_rb_exc_raise, 1);
4148
rb_define_method(cls, "rb_set_errinfo", exception_spec_rb_set_errinfo, 1);
49+
rb_define_method(cls, "rb_make_exception", exception_spec_rb_make_exception, 1);
4250
}
4351

4452
#ifdef __cplusplus

src/main/c/cext/exception.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ void rb_bug(const char *fmt, ...) {
128128
rb_tr_error("rb_bug not yet implemented");
129129
}
130130

131+
VALUE rb_make_exception(int argc, const VALUE *argv) {
132+
return RUBY_CEXT_INVOKE("rb_make_exception", rb_ary_new4(argc, argv));
133+
}
134+
131135
void rb_tr_init_exception(void) {
132136
cext_rb_protect = polyglot_get_member(rb_tr_cext, "rb_protect");
133137
cext_rb_ensure = polyglot_get_member(rb_tr_cext, "rb_ensure");

src/main/ruby/truffleruby/core/truffle/exception_operations.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,31 @@ def self.build_exception_for_raise(exc, msg)
2929
end
3030
end
3131

32+
def self.make_exception(args)
33+
case args.size
34+
when 0
35+
nil
36+
when 1
37+
converted = Truffle::Type.rb_check_convert_type(args[0], String, :to_str)
38+
return RuntimeError.new(converted) unless Primitive.nil?(converted)
39+
call_exception(args[0])
40+
when 2
41+
call_exception(args[0], args[1])
42+
when 3
43+
exc = call_exception(args[0], args[1])
44+
exc.set_backtrace(args[2])
45+
exc
46+
else
47+
Truffle::Type.check_arity(args.size, 0, 3)
48+
end
49+
end
50+
51+
def self.call_exception(exc, *args)
52+
res = Truffle::Type.check_funcall(exc, :exception, args)
53+
raise TypeError, 'exception class/object expected' if Primitive.undefined?(res) || !Primitive.object_kind_of?(res, Exception)
54+
res
55+
end
56+
3257
# Avoid using #raise here to prevent infinite recursion
3358
def self.exception_class_object_expected!
3459
exc = ::TypeError.new('exception class/object expected')

0 commit comments

Comments
 (0)