From: Nobuyoshi Nakada Date: 2007-08-23T08:03:19+09:00 Subject: [ruby-dev:31615] Re: catch without a tag argument なかだです。 At Thu, 23 Aug 2007 00:34:12 +0900, Tanaka Akira wrote in [ruby-dev:31609]: > matzruby にある、引数なしで catch が呼べる機能は 1.9 には入 > らないのでしょうか。 いろいろと変わってるようでそのままでは動きませんでした。 Index: eval_jump.ci =================================================================== --- eval_jump.ci (revision 13128) +++ eval_jump.ci (working copy) @@ -6,5 +6,6 @@ #include "eval_intern.h" -NORETURN(static VALUE rb_f_throw _((int, VALUE *))); +NORETURN(static VALUE rb_f_throw(int, VALUE *)); +int rb_vm_catch_p(VALUE dst, VALUE tag); /* @@ -24,25 +25,8 @@ rb_f_throw(int argc, VALUE *argv) { VALUE tag, value; - rb_thread_t *th = GET_THREAD(); - struct rb_vm_tag *tt = th->tag; rb_scan_args(argc, argv, "11", &tag, &value); - tag = ID2SYM(rb_to_id(tag)); - - while (tt) { - if (tt->tag == tag) { - tt->retval = value; - break; - } - tt = tt->prev; - } - if (!tt) { - rb_name_error(SYM2ID(tag), "uncaught throw `%s'", - rb_id2name(SYM2ID(tag))); - } - rb_trap_restore_mask(); - th->errinfo = tag; - JUMP_TAG(TAG_THROW); + rb_throw_obj(tag, value); #ifndef __GNUC__ return Qnil; /* not reached */ @@ -53,14 +37,11 @@ void rb_throw(const char *tag, VALUE val) { - VALUE argv[2]; - - argv[0] = ID2SYM(rb_intern(tag)); - argv[1] = val; - rb_f_throw(2, argv); + rb_throw_obj(ID2SYM(rb_intern(tag)), val); } /* * call-seq: - * catch(symbol) {| | block } > obj + * catch(symbol) { block } => obj + * catch() {|tag| block } => obj * * +catch+ executes its block. If a +throw+ is @@ -92,41 +73,22 @@ rb_throw(const char *tag, VALUE val) static VALUE -rb_f_catch(VALUE dmy, VALUE tag) +rb_f_catch(int argc, VALUE *argv) { - int state; - VALUE val = Qnil; /* OK */ - rb_thread_t *th = GET_THREAD(); - - tag = ID2SYM(rb_to_id(tag)); - PUSH_TAG(); + VALUE tag; - th->tag->tag = tag; - - if ((state = EXEC_TAG()) == 0) { - val = rb_yield_0(1, &tag); - } - else if (state == TAG_THROW && th->errinfo == tag) { - val = th->tag->retval; - th->errinfo = Qnil; - state = 0; + rb_scan_args(argc, argv, "01", &tag); + if (argc == 0) { + tag = Qundef; } - POP_TAG(); - if (state) - JUMP_TAG(state); - - return val; -} - -static VALUE -catch_i(VALUE tag) -{ - return rb_funcall(Qnil, rb_intern("catch"), 1, tag); + return rb_catch_obj(tag, rb_yield, Qnil); } VALUE -rb_catch(const char *tag, VALUE (*func)(), VALUE data) +rb_catch(const char *tag, VALUE (*func)(ANYARGS), VALUE data) { - return rb_iterate((VALUE (*)_((VALUE)))catch_i, ID2SYM(rb_intern(tag)), - func, data); + if (!tag) { + return rb_catch_obj(Qundef, func, data); + } + return rb_catch_obj(ID2SYM(rb_intern(tag)), func, data); } @@ -277,5 +239,5 @@ void Init_jump(void) { - rb_define_global_function("catch", rb_f_catch, 1); + rb_define_global_function("catch", rb_f_catch, -1); rb_define_global_function("throw", rb_f_throw, -1); rb_define_global_function("at_exit", rb_f_at_exit, 0); Index: vm.c =================================================================== --- vm.c (revision 13128) +++ vm.c (working copy) @@ -982,4 +995,76 @@ rb_iter_break() } +static inline int +catching_p(VALUE dst, VALUE tag) +{ + if (dst == tag) return Qtrue; + if (SPECIAL_CONST_P(dst)) return Qfalse; + if (BUILTIN_TYPE(dst) != T_NODE) return Qfalse; + if (GET_THROWOBJ_STATE(dst) != TAG_THROW) return Qfalse; + if (GET_THROWOBJ_VAL(dst) != tag) return Qfalse; + return Qtrue; +} + +void +rb_throw_obj(VALUE tag, VALUE value) +{ + rb_thread_t *th = GET_THREAD(); + struct rb_vm_tag *tt = th->tag; + + while (tt) { + if (catching_p(tt->tag, tag)) { + tag = tt->tag; + tt->retval = value; + break; + } + tt = tt->prev; + } + if (!tt) { + VALUE argv[2]; + argv[0] = rb_sprintf("uncaught throw `%s'", RSTRING_PTR(rb_obj_as_string(tag))); + argv[1] = tag; + rb_exc_raise(rb_class_new_instance(2, argv, rb_eNameError)); + } + rb_trap_restore_mask(); + th->errinfo = tag; + TH_JUMP_TAG(th, TAG_THROW); +#ifndef __GNUC__ + return Qnil; /* not reached */ +#endif +} + +VALUE +rb_catch_obj(VALUE tag, VALUE (*func)(ANYARGS), VALUE data) +{ + int state; + VALUE val = Qnil; /* OK */ + VALUE dest = tag; + rb_thread_t *th = GET_THREAD(); + + if (tag == Qundef) { + tag = rb_obj_alloc(rb_cObject); + dest = (VALUE)NEW_THROW_OBJECT(tag, GC_GUARDED_PTR_REF(*th->cfp->dfp), + TAG_THROW); + } + + TH_PUSH_TAG(th); + + th->tag->tag = dest; + + if ((state = EXEC_TAG()) == 0) { + val = (*func)(tag, data); + } + else if (state == TAG_THROW && th->errinfo == dest) { + val = th->tag->retval; + th->errinfo = Qnil; + state = 0; + } + TH_POP_TAG(); + if (state) + JUMP_TAG(state); + + return val; +} + /* optimization: redefine management */ Index: include/ruby/ruby.h =================================================================== --- include/ruby/ruby.h (revision 13128) +++ include/ruby/ruby.h (working copy) @@ -723,5 +723,7 @@ VALUE rb_rescue2(VALUE(*)(ANYARGS),VALUE VALUE rb_ensure(VALUE(*)(ANYARGS),VALUE,VALUE(*)(ANYARGS),VALUE); VALUE rb_catch(const char*,VALUE(*)(ANYARGS),VALUE); +VALUE rb_catch_obj(VALUE,VALUE(*)(ANYARGS),VALUE); NORETURN(void rb_throw(const char*,VALUE)); +NORETURN(void rb_throw_obj(VALUE,VALUE)); VALUE rb_require(const char*); -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦