Commit cdd72d9c authored by take_cheeze's avatar take_cheeze Committed by Yukihiro "Matz" Matsumoto

Implement `mrb_protect`, `mrb_ensure`, `mrb_rescue`, `mrb_rescue_exceptions`.

(`mrb_rescue_exceptions` is mruby implementation of `rb_rescue2`.)
Closes #2844, closes #2837.
parent 2e4bc2de
......@@ -29,6 +29,14 @@ MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_va
/* declaration for fail method */
MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
MRB_API mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state);
MRB_API mrb_value mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
mrb_func_t ensure, mrb_value e_data);
MRB_API mrb_value mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
mrb_func_t rescue, mrb_value r_data);
MRB_API mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
mrb_func_t rescue, mrb_value r_data, ...);
#if defined(__cplusplus)
} /* extern "C" { */
#endif
......
MRuby::Gem::Specification.new('mruby-error') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'extensional error handling'
if build.cxx_abi_enabled?
@objs << build.compile_as_cxx("#{spec.dir}/src/exception.c", "#{spec.build_dir}/src/exception.cxx")
@objs.delete_if { |v| v == objfile("#{spec.build_dir}/src/exception") }
end
end
#include <stdarg.h>
#include "mruby.h"
#include "mruby/throw.h"
#include "mruby/error.h"
MRB_API mrb_value
mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state)
{
struct mrb_jmpbuf *prev_jmp = mrb->jmp;
struct mrb_jmpbuf c_jmp;
mrb_value result = mrb_nil_value();
if (state) { *state = FALSE; }
MRB_TRY(&c_jmp) {
mrb->jmp = &c_jmp;
result = body(mrb, data);
mrb->jmp = prev_jmp;
} MRB_CATCH(&c_jmp) {
mrb->jmp = prev_jmp;
mrb->exc = NULL;
if (state) { *state = TRUE; }
} MRB_END_EXC(&c_jmp);
mrb_gc_protect(mrb, result);
return result;
}
MRB_API mrb_value
mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t ensure, mrb_value e_data)
{
struct mrb_jmpbuf *prev_jmp = mrb->jmp;
struct mrb_jmpbuf c_jmp;
mrb_value result;
MRB_TRY(&c_jmp) {
mrb->jmp = &c_jmp;
result = body(mrb, b_data);
mrb->jmp = prev_jmp;
} MRB_CATCH(&c_jmp) {
mrb->jmp = prev_jmp;
ensure(mrb, e_data);
MRB_THROW(mrb->jmp); /* rethrow catched exceptions */
} MRB_END_EXC(&c_jmp);
ensure(mrb, e_data);
mrb_gc_protect(mrb, result);
return result;
}
MRB_API mrb_value
mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
mrb_func_t rescue, mrb_value r_data)
{
return mrb_rescue_exceptions(mrb, body, b_data, rescue, r_data, mrb->eStandardError_class, NULL);
}
MRB_API mrb_value
mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data, ...)
{
struct mrb_jmpbuf *prev_jmp = mrb->jmp;
struct mrb_jmpbuf c_jmp;
mrb_value result;
va_list excs;
struct RClass *cls;
mrb_bool error_matched = FALSE;
MRB_TRY(&c_jmp) {
mrb->jmp = &c_jmp;
result = body(mrb, b_data);
mrb->jmp = prev_jmp;
} MRB_CATCH(&c_jmp) {
mrb->jmp = prev_jmp;
va_start(excs, r_data);
while((cls = va_arg(excs, struct RClass*))) {
if (mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), cls)) {
error_matched = TRUE;
break;
}
}
va_end(excs);
if (!error_matched) { MRB_THROW(mrb->jmp); }
mrb->exc = NULL;
result = rescue(mrb, r_data);
} MRB_END_EXC(&c_jmp);
mrb_gc_protect(mrb, result);
return result;
}
void
mrb_mruby_error_gem_init(mrb_state *mrb)
{
}
void
mrb_mruby_error_gem_final(mrb_state *mrb)
{
}
#include "mruby.h"
#include "mruby/error.h"
#include "mruby/array.h"
static mrb_value
protect_cb(mrb_state *mrb, mrb_value b)
{
return mrb_yield_argv(mrb, b, 0, NULL);
}
static mrb_value
run_protect(mrb_state *mrb, mrb_value self)
{
mrb_value b;
mrb_value ret[2];
mrb_bool state;
mrb_get_args(mrb, "&", &b);
ret[0] = mrb_protect(mrb, protect_cb, b, &state);
ret[1] = mrb_bool_value(state);
return mrb_ary_new_from_values(mrb, 2, ret);
}
static mrb_value
run_ensure(mrb_state *mrb, mrb_value self)
{
mrb_value b, e;
mrb_get_args(mrb, "oo", &b, &e);
return mrb_ensure(mrb, protect_cb, b, protect_cb, e);
}
static mrb_value
run_rescue(mrb_state *mrb, mrb_value self)
{
mrb_value b, r;
mrb_get_args(mrb, "oo", &b, &r);
return mrb_rescue(mrb, protect_cb, b, protect_cb, r);
}
static mrb_value
run_rescue_exceptions(mrb_state *mrb, mrb_value self)
{
mrb_value b, r;
mrb_get_args(mrb, "oo", &b, &r);
return mrb_rescue_exceptions(mrb, protect_cb, b, protect_cb, r, E_TYPE_ERROR, NULL);
}
void
mrb_mruby_error_gem_test(mrb_state *mrb)
{
struct RClass *cls;
cls = mrb_define_class(mrb, "ExceptionTest", mrb->object_class);
mrb_define_module_function(mrb, cls, "mrb_protect", run_protect, MRB_ARGS_NONE() | MRB_ARGS_BLOCK());
mrb_define_module_function(mrb, cls, "mrb_ensure", run_ensure, MRB_ARGS_REQ(2));
mrb_define_module_function(mrb, cls, "mrb_rescue", run_rescue, MRB_ARGS_REQ(2));
mrb_define_module_function(mrb, cls, "mrb_rescue_exceptions", run_rescue_exceptions, MRB_ARGS_REQ(2));
}
assert 'mrb_protect' do
assert_equal ['test', false] do
ExceptionTest.mrb_protect { 'test' }
end
assert_equal [nil, true] do
ExceptionTest.mrb_protect { raise 'test' }
end
end
assert 'mrb_ensure' do
a = false
assert_equal 'test' do
ExceptionTest.mrb_ensure Proc.new { 'test' }, Proc.new { a = true }
end
assert_true a
a = false
assert_raise RuntimeError do
ExceptionTest.mrb_ensure Proc.new { raise 'test' }, Proc.new { a = true }
end
assert_true a
end
assert 'mrb_rescue' do
assert_equal 'test' do
ExceptionTest.mrb_rescue Proc.new { 'test' }, Proc.new {}
end
class CustomExp < Exception
end
assert_raise CustomExp do
ExceptionTest.mrb_rescue Proc.new { raise CustomExp.new 'test' }, Proc.new { 'rescue' }
end
assert_equal 'rescue' do
ExceptionTest.mrb_rescue Proc.new { raise 'test' }, Proc.new { 'rescue' }
end
end
assert 'mrb_rescue_exceptions' do
assert_equal 'test' do
ExceptionTest.mrb_rescue_exceptions Proc.new { 'test' }, Proc.new {}
end
assert_raise RangeError do
ExceptionTest.mrb_rescue_exceptions Proc.new { raise RangeError.new 'test' }, Proc.new { 'rescue' }
end
assert_equal 'rescue' do
ExceptionTest.mrb_rescue_exceptions Proc.new { raise TypeError.new 'test' }, Proc.new { 'rescue' }
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment