(linenum→info "unix/slp.c:2238")

ruby/1.9.0/eval_jump.c

    1: /* -*-c-*- */
    2: /*
    3:  * from eval.c
    4:  */
    5: 
    6: #include "eval_intern.h"
    7: 
    8: NORETURN(static VALUE rb_f_throw _((int, VALUE *)));
    9: 
   10: /*
   11:  *  call-seq:
   12:  *     throw(symbol [, obj])
   13:  *
   14:  *  Transfers control to the end of the active +catch+ block
   15:  *  waiting for _symbol_. Raises +NameError+ if there
   16:  *  is no +catch+ block for the symbol. The optional second
   17:  *  parameter supplies a return value for the +catch+ block,
   18:  *  which otherwise defaults to +nil+. For examples, see
   19:  *  <code>Kernel::catch</code>.
   20:  */
   21: 
   22: static VALUE
   23: rb_f_throw(int argc, VALUE *argv)
   24: {
   25:     VALUE tag, value;
   26:     rb_thread_t *th = GET_THREAD();
   27:     struct rb_vm_tag *tt = th->tag;
   28: 
   29:     rb_scan_args(argc, argv, "11", &tag, &value);
   30:     while (tt) {
   31:         if (tt->tag == tag) {
   32:             tt->retval = value;
   33:             break;
   34:         }
   35:         tt = tt->prev;
   36:     }
   37:     if (!tt) {
   38:         VALUE desc = rb_inspect(tag);
   39:         rb_raise(rb_eArgError, "uncaught throw %s", RSTRING_PTR(desc));
   40:     }
   41:     rb_trap_restore_mask();
   42:     th->errinfo = NEW_THROW_OBJECT(tag, 0, TAG_THROW);
   43: 
   44:     JUMP_TAG(TAG_THROW);
   45: #ifndef __GNUC__
   46:     return Qnil;                /* not reached */
   47: #endif
   48: }
   49: 
   50: void
   51: rb_throw(const char *tag, VALUE val)
   52: {
   53:     VALUE argv[2];
   54: 
   55:     argv[0] = ID2SYM(rb_intern(tag));
   56:     argv[1] = val;
   57:     rb_f_throw(2, argv);
   58: }
   59: 
   60: void
   61: rb_throw_obj(VALUE tag, VALUE val)
   62: {
   63:     VALUE argv[2];
   64: 
   65:     argv[0] = tag;
   66:     argv[1] = val;
   67:     rb_f_throw(2, argv);
   68: }
   69: 
   70: /*
   71:  *  call-seq:
   72:  *     catch(symbol) {| | block }  > obj
   73:  *
   74:  *  +catch+ executes its block. If a +throw+ is
   75:  *  executed, Ruby searches up its stack for a +catch+ block
   76:  *  with a tag corresponding to the +throw+'s
   77:  *  _symbol_. If found, that block is terminated, and
   78:  *  +catch+ returns the value given to +throw+. If
   79:  *  +throw+ is not called, the block terminates normally, and
   80:  *  the value of +catch+ is the value of the last expression
   81:  *  evaluated. +catch+ expressions may be nested, and the
   82:  *  +throw+ call need not be in lexical scope.
   83:  *
   84:  *     def routine(n)
   85:  *       puts n
   86:  *       throw :done if n <= 0
   87:  *       routine(n-1)
   88:  *     end
   89:  *
   90:  *
   91:  *     catch(:done) { routine(3) }
   92:  *
   93:  *  <em>produces:</em>
   94:  *
   95:  *     3
   96:  *     2
   97:  *     1
   98:  *     0
   99:  */
  100: 
  101: static VALUE
  102: rb_f_catch(int argc, VALUE *argv)
  103: {
  104:     VALUE tag;
  105:     int state;
  106:     VALUE val = Qnil;           /* OK */
  107:     rb_thread_t *th = GET_THREAD();
  108: 
  109:     rb_scan_args(argc, argv, "01", &tag);
  110:     if (argc == 0) {
  111:         tag = rb_obj_alloc(rb_cObject);
  112:     }
  113:     PUSH_TAG();
  114: 
  115:     th->tag->tag = tag;
  116: 
  117:     if ((state = EXEC_TAG()) == 0) {
  118:         val = rb_yield_0(1, &tag);
  119:     }
  120:     else if (state == TAG_THROW && RNODE(th->errinfo)->u1.value == tag) {
  121:         val = th->tag->retval;
  122:         th->errinfo = Qnil;
  123:         state = 0;
  124:     }
  125:     POP_TAG();
  126:     if (state)
  127:         JUMP_TAG(state);
  128: 
  129:     return val;
  130: }
  131: 
  132: static VALUE
  133: catch_null_i(VALUE dmy)
  134: {
  135:     return rb_funcall(Qnil, rb_intern("catch"), 0, 0);
  136: }
  137: 
  138: static VALUE
  139: catch_i(VALUE tag)
  140: {
  141:     return rb_funcall(Qnil, rb_intern("catch"), 1, tag);
  142: }
  143: 
  144: VALUE
  145: rb_catch(const char *tag, VALUE (*func)(), VALUE data)
  146: {
  147:     if (!tag) {
  148:         return rb_iterate(catch_null_i, 0, func, data);
  149:     }
  150:     return rb_iterate(catch_i, ID2SYM(rb_intern(tag)), func, data);
  151: }
  152: 
  153: VALUE
  154: rb_catch_obj(VALUE tag, VALUE (*func)(), VALUE data)
  155: {
  156:     return rb_iterate((VALUE (*)_((VALUE)))catch_i, tag, func, data);
  157: }
  158: 
  159: 
  160: /* exit */
  161: 
  162: void
  163: rb_call_end_proc(VALUE data)
  164: {
  165:     rb_proc_call(data, rb_ary_new());
  166: }
  167: 
  168: /*
  169:  *  call-seq:
  170:  *     at_exit { block } -> proc
  171:  *
  172:  *  Converts _block_ to a +Proc+ object (and therefore
  173:  *  binds it at the point of call) and registers it for execution when
  174:  *  the program exits. If multiple handlers are registered, they are
  175:  *  executed in reverse order of registration.
  176:  *
  177:  *     def do_at_exit(str1)
  178:  *       at_exit { print str1 }
  179:  *     end
  180:  *     at_exit { puts "cruel world" }
  181:  *     do_at_exit("goodbye ")
  182:  *     exit
  183:  *
  184:  *  <em>produces:</em>
  185:  *
  186:  *     goodbye cruel world
  187:  */
  188: 
  189: static VALUE
  190: rb_f_at_exit(void)
  191: {
  192:     VALUE proc;
  193: 
  194:     if (!rb_block_given_p()) {
  195:         rb_raise(rb_eArgError, "called without a block");
  196:     }
  197:     proc = rb_block_proc();
  198:     rb_set_end_proc(rb_call_end_proc, proc);
  199:     return proc;
  200: }
  201: 
  202: struct end_proc_data {
  203:     void (*func) ();
  204:     VALUE data;
  205:     int safe;
  206:     struct end_proc_data *next;
  207: };
  208: 
  209: static struct end_proc_data *end_procs, *ephemeral_end_procs, *tmp_end_procs;
  210: 
  211: void
  212: rb_set_end_proc(void (*func)(VALUE), VALUE data)
  213: {
  214:     struct end_proc_data *link = ALLOC(struct end_proc_data);
  215:     struct end_proc_data **list;
  216:     rb_thread_t *th = GET_THREAD();
  217: 
  218:     if (th->top_wrapper) {
  219:         list = &ephemeral_end_procs;
  220:     }
  221:     else {
  222:         list = &end_procs;
  223:     }
  224:     link->next = *list;
  225:     link->func = func;
  226:     link->data = data;
  227:     link->safe = rb_safe_level();
  228:     *list = link;
  229: }
  230: 
  231: void
  232: rb_mark_end_proc(void)
  233: {
  234:     struct end_proc_data *link;
  235: 
  236:     link = end_procs;
  237:     while (link) {
  238:         rb_gc_mark(link->data);
  239:         link = link->next;
  240:     }
  241:     link = ephemeral_end_procs;
  242:     while (link) {
  243:         rb_gc_mark(link->data);
  244:         link = link->next;
  245:     }
  246:     link = tmp_end_procs;
  247:     while (link) {
  248:         rb_gc_mark(link->data);
  249:         link = link->next;
  250:     }
  251: }
  252: 
  253: void
  254: rb_exec_end_proc(void)
  255: {
  256:     struct end_proc_data *link, *tmp;
  257:     int status;
  258:     volatile int safe = rb_safe_level();
  259: 
  260:     while (ephemeral_end_procs) {
  261:         tmp_end_procs = link = ephemeral_end_procs;
  262:         ephemeral_end_procs = 0;
  263:         while (link) {
  264:             PUSH_TAG();
  265:             if ((status = EXEC_TAG()) == 0) {
  266:                 rb_set_safe_level_force(link->safe);
  267:                 (*link->func) (link->data);
  268:             }
  269:             POP_TAG();
  270:             if (status) {
  271:                 error_handle(status);
  272:             }
  273:             tmp = link;
  274:             tmp_end_procs = link = link->next;
  275:             free(tmp);
  276:         }
  277:     }
  278:     while (end_procs) {
  279:         tmp_end_procs = link = end_procs;
  280:         end_procs = 0;
  281:         while (link) {
  282:             PUSH_TAG();
  283:             if ((status = EXEC_TAG()) == 0) {
  284:                 rb_set_safe_level_force(link->safe);
  285:                 (*link->func) (link->data);
  286:             }
  287:             POP_TAG();
  288:             if (status) {
  289:                 error_handle(status);
  290:             }
  291:             tmp = link;
  292:             tmp_end_procs = link = link->next;
  293:             free(tmp);
  294:         }
  295:     }
  296:     rb_set_safe_level_force(safe);
  297: }
  298: 
  299: void
  300: Init_jump(void)
  301: {
  302:     rb_define_global_function("catch", rb_f_catch, -1);
  303:     rb_define_global_function("throw", rb_f_throw, -1);
  304:     rb_define_global_function("at_exit", rb_f_at_exit, 0);
  305: }
Syntax (Markdown)