diff options
Diffstat (limited to 'eval_jump.c')
| -rw-r--r-- | eval_jump.c | 257 |
1 files changed, 45 insertions, 212 deletions
diff --git a/eval_jump.c b/eval_jump.c index 948ac635be..6ee8ff4a6f 100644 --- a/eval_jump.c +++ b/eval_jump.c @@ -5,158 +5,6 @@ #include "eval_intern.h" -NORETURN(static VALUE rb_f_throw _((int, VALUE *))); - -/* - * call-seq: - * throw(symbol [, obj]) - * - * Transfers control to the end of the active +catch+ block - * waiting for _symbol_. Raises +NameError+ if there - * is no +catch+ block for the symbol. The optional second - * parameter supplies a return value for the +catch+ block, - * which otherwise defaults to +nil+. For examples, see - * <code>Kernel::catch</code>. - */ - -static VALUE -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); - while (tt) { - if (tt->tag == tag) { - tt->retval = value; - break; - } - tt = tt->prev; - } - if (!tt) { - VALUE desc = rb_inspect(tag); - rb_raise(rb_eArgError, "uncaught throw %s", RSTRING_PTR(desc)); - } - rb_trap_restore_mask(); - th->errinfo = NEW_THROW_OBJECT(tag, 0, TAG_THROW); - - JUMP_TAG(TAG_THROW); -#ifndef __GNUC__ - return Qnil; /* not reached */ -#endif -} - -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); -} - -void -rb_throw_obj(VALUE tag, VALUE val) -{ - VALUE argv[2]; - - argv[0] = tag; - argv[1] = val; - rb_f_throw(2, argv); -} - -/* - * call-seq: - * catch(symbol) {| | block } > obj - * - * +catch+ executes its block. If a +throw+ is - * executed, Ruby searches up its stack for a +catch+ block - * with a tag corresponding to the +throw+'s - * _symbol_. If found, that block is terminated, and - * +catch+ returns the value given to +throw+. If - * +throw+ is not called, the block terminates normally, and - * the value of +catch+ is the value of the last expression - * evaluated. +catch+ expressions may be nested, and the - * +throw+ call need not be in lexical scope. - * - * def routine(n) - * puts n - * throw :done if n <= 0 - * routine(n-1) - * end - * - * - * catch(:done) { routine(3) } - * - * <em>produces:</em> - * - * 3 - * 2 - * 1 - * 0 - */ - -static VALUE -rb_f_catch(int argc, VALUE *argv) -{ - VALUE tag; - int state; - VALUE val = Qnil; /* OK */ - rb_thread_t *th = GET_THREAD(); - - rb_scan_args(argc, argv, "01", &tag); - if (argc == 0) { - tag = rb_obj_alloc(rb_cObject); - } - PUSH_TAG(); - - th->tag->tag = tag; - - if ((state = EXEC_TAG()) == 0) { - val = rb_yield_0(1, &tag); - } - else if (state == TAG_THROW && RNODE(th->errinfo)->u1.value == tag) { - val = th->tag->retval; - th->errinfo = Qnil; - state = 0; - } - POP_TAG(); - if (state) - JUMP_TAG(state); - - return val; -} - -static VALUE -catch_null_i(VALUE dmy) -{ - return rb_funcall(Qnil, rb_intern("catch"), 0, 0); -} - -static VALUE -catch_i(VALUE tag) -{ - return rb_funcall(Qnil, rb_intern("catch"), 1, tag); -} - -VALUE -rb_catch(const char *tag, VALUE (*func)(), VALUE data) -{ - if (!tag) { - return rb_iterate(catch_null_i, 0, func, data); - } - return rb_iterate(catch_i, ID2SYM(rb_intern(tag)), func, data); -} - -VALUE -rb_catch_obj(VALUE tag, VALUE (*func)(), VALUE data) -{ - return rb_iterate((VALUE (*)_((VALUE)))catch_i, tag, func, data); -} - - /* exit */ void @@ -187,12 +35,12 @@ rb_call_end_proc(VALUE data) */ static VALUE -rb_f_at_exit(void) +rb_f_at_exit(VALUE _) { VALUE proc; if (!rb_block_given_p()) { - rb_raise(rb_eArgError, "called without a block"); + rb_raise(rb_eArgError, "called without a block"); } proc = rb_block_proc(); rb_set_end_proc(rb_call_end_proc, proc); @@ -200,13 +48,12 @@ rb_f_at_exit(void) } struct end_proc_data { - void (*func) (); + void (*func) (VALUE); VALUE data; - int safe; struct end_proc_data *next; }; -static struct end_proc_data *end_procs, *ephemeral_end_procs, *tmp_end_procs; +static struct end_proc_data *end_procs, *ephemeral_end_procs; void rb_set_end_proc(void (*func)(VALUE), VALUE data) @@ -216,15 +63,14 @@ rb_set_end_proc(void (*func)(VALUE), VALUE data) rb_thread_t *th = GET_THREAD(); if (th->top_wrapper) { - list = &ephemeral_end_procs; + list = &ephemeral_end_procs; } else { - list = &end_procs; + list = &end_procs; } link->next = *list; link->func = func; link->data = data; - link->safe = rb_safe_level(); *list = link; } @@ -235,71 +81,58 @@ rb_mark_end_proc(void) link = end_procs; while (link) { - rb_gc_mark(link->data); - link = link->next; + rb_gc_mark(link->data); + link = link->next; } link = ephemeral_end_procs; while (link) { - rb_gc_mark(link->data); - link = link->next; - } - link = tmp_end_procs; - while (link) { - rb_gc_mark(link->data); - link = link->next; + rb_gc_mark(link->data); + link = link->next; } } -void -rb_exec_end_proc(void) +static void +exec_end_procs_chain(struct end_proc_data *volatile *procs, VALUE *errp) { - struct end_proc_data *link, *tmp; - int status; - volatile int safe = rb_safe_level(); - - while (ephemeral_end_procs) { - tmp_end_procs = link = ephemeral_end_procs; - ephemeral_end_procs = 0; - while (link) { - PUSH_TAG(); - if ((status = EXEC_TAG()) == 0) { - rb_set_safe_level_force(link->safe); - (*link->func) (link->data); - } - POP_TAG(); - if (status) { - error_handle(status); - } - tmp = link; - tmp_end_procs = link = link->next; - free(tmp); - } + struct end_proc_data volatile endproc; + struct end_proc_data *link; + VALUE errinfo = *errp; + + while ((link = *procs) != 0) { + *procs = link->next; + endproc = *link; + SIZED_FREE(link); + (*endproc.func) (endproc.data); + *errp = errinfo; } - while (end_procs) { - tmp_end_procs = link = end_procs; - end_procs = 0; - while (link) { - PUSH_TAG(); - if ((status = EXEC_TAG()) == 0) { - rb_set_safe_level_force(link->safe); - (*link->func) (link->data); - } - POP_TAG(); - if (status) { - error_handle(status); - } - tmp = link; - tmp_end_procs = link = link->next; - free(tmp); - } +} + +static void +rb_ec_exec_end_proc(rb_execution_context_t * ec) +{ + enum ruby_tag_type state; + volatile VALUE errinfo = ec->errinfo; + volatile bool finished = false; + + while (!finished) { + EC_PUSH_TAG(ec); + if ((state = EC_EXEC_TAG()) == TAG_NONE) { + exec_end_procs_chain(&ephemeral_end_procs, &ec->errinfo); + exec_end_procs_chain(&end_procs, &ec->errinfo); + finished = true; + } + EC_POP_TAG(); + if (state != TAG_NONE) { + error_handle(ec, ec->errinfo, state); + if (!NIL_P(ec->errinfo)) errinfo = ec->errinfo; + } } - rb_set_safe_level_force(safe); + + ec->errinfo = errinfo; } void Init_jump(void) { - 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); } |
