Bug #1852 » hash_merged.diff
| array.c | ||
|---|---|---|
| st_index_t h; | ||
| VALUE n; | ||
| h = rb_hash_start(RARRAY_LEN(ary)); | ||
| if (recur) { | ||
| rb_raise(rb_eArgError, "recursive key for hash"); | ||
| h = rb_hash_uint(h, NUM2LONG(rb_hash(rb_cArray))); | ||
| } | ||
| h = rb_hash_start(RARRAY_LEN(ary)); | ||
| for (i=0; i<RARRAY_LEN(ary); i++) { | ||
| n = rb_hash(RARRAY_PTR(ary)[i]); | ||
| h = rb_hash_uint(h, NUM2LONG(n)); | ||
| else { | ||
| for (i=0; i<RARRAY_LEN(ary); i++) { | ||
| n = rb_hash(RARRAY_PTR(ary)[i]); | ||
| h = rb_hash_uint(h, NUM2LONG(n)); | ||
| } | ||
| } | ||
| h = rb_hash_end(h); | ||
| return LONG2FIX(h); | ||
| ... | ... | |
| static VALUE | ||
| rb_ary_hash(VALUE ary) | ||
| { | ||
| return rb_exec_recursive(recursive_hash, ary, 0); | ||
| return rb_exec_recursive_outer(recursive_hash, ary, 0); | ||
| } | ||
| /* | ||
| hash.c | ||
|---|---|---|
| { | ||
| st_index_t hval; | ||
| if (recur) { | ||
| rb_raise(rb_eArgError, "recursive key for hash"); | ||
| } | ||
| if (!RHASH(hash)->ntbl) | ||
| return LONG2FIX(0); | ||
| hval = RHASH(hash)->ntbl->num_entries; | ||
| rb_hash_foreach(hash, hash_i, (VALUE)&hval); | ||
| if (recur) | ||
| hval = rb_hash_end(rb_hash_uint(rb_hash_start(rb_hash(rb_cHash)), hval)); | ||
| else | ||
| rb_hash_foreach(hash, hash_i, (VALUE)&hval); | ||
| return INT2FIX(hval); | ||
| } | ||
| ... | ... | |
| static VALUE | ||
| rb_hash_hash(VALUE hash) | ||
| { | ||
| return rb_exec_recursive(recursive_hash, hash, 0); | ||
| return rb_exec_recursive_outer(recursive_hash, hash, 0); | ||
| } | ||
| static int | ||
| include/ruby/intern.h | ||
|---|---|---|
| void rb_thread_atfork_before_exec(void); | ||
| VALUE rb_exec_recursive(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE); | ||
| VALUE rb_exec_recursive_paired(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE,VALUE); | ||
| VALUE rb_exec_recursive_outer(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE); | ||
| /* file.c */ | ||
| VALUE rb_file_s_expand_path(int, VALUE *); | ||
| VALUE rb_file_expand_path(VALUE, VALUE); | ||
| include/ruby/ruby.h | ||
|---|---|---|
| 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); | ||
| VALUE rb_catch_func(VALUE,VALUE(*)(ANYARGS),VALUE); | ||
| NORETURN(void rb_throw(const char*,VALUE)); | ||
| NORETURN(void rb_throw_obj(VALUE,VALUE)); | ||
| range.c | ||
|---|---|---|
| st_index_t hash = EXCL(range); | ||
| VALUE v; | ||
| if (recur) { | ||
| rb_raise(rb_eArgError, "recursive key for hash"); | ||
| } | ||
| hash = rb_hash_start(hash); | ||
| v = rb_hash(RANGE_BEG(range)); | ||
| hash = rb_hash_uint(hash, NUM2LONG(v)); | ||
| v = rb_hash(RANGE_END(range)); | ||
| hash = rb_hash_uint(hash, NUM2LONG(v)); | ||
| if (!recur) { | ||
| v = rb_hash(RANGE_BEG(range)); | ||
| hash = rb_hash_uint(hash, NUM2LONG(v)); | ||
| v = rb_hash(RANGE_END(range)); | ||
| hash = rb_hash_uint(hash, NUM2LONG(v)); | ||
| } | ||
| hash = rb_hash_uint(hash, EXCL(range) << 24); | ||
| hash = rb_hash_end(hash); | ||
| ... | ... | |
| static VALUE | ||
| range_hash(VALUE range) | ||
| { | ||
| return rb_exec_recursive(recursive_hash, range, 0); | ||
| return rb_exec_recursive_outer(recursive_hash, range, 0); | ||
| } | ||
| static void | ||
| struct.c | ||
|---|---|---|
| st_index_t h; | ||
| VALUE n; | ||
| if (recur) { | ||
| rb_raise(rb_eArgError, "recursive key for hash"); | ||
| } | ||
| h = rb_hash_start(rb_hash(rb_obj_class(s))); | ||
| for (i = 0; i < RSTRUCT_LEN(s); i++) { | ||
| n = rb_hash(RSTRUCT_PTR(s)[i]); | ||
| h = rb_hash_uint(h, NUM2LONG(n)); | ||
| if (!recur) { | ||
| for (i = 0; i < RSTRUCT_LEN(s); i++) { | ||
| n = rb_hash(RSTRUCT_PTR(s)[i]); | ||
| h = rb_hash_uint(h, NUM2LONG(n)); | ||
| } | ||
| } | ||
| h = rb_hash_end(h); | ||
| return INT2FIX(h); | ||
| ... | ... | |
| static VALUE | ||
| rb_struct_hash(VALUE s) | ||
| { | ||
| return rb_exec_recursive(recursive_hash, s, 0); | ||
| return rb_exec_recursive_outer(recursive_hash, s, 0); | ||
| } | ||
| /* | ||
| thread.c | ||
|---|---|---|
| rb_hash_delete(list, obj); | ||
| } | ||
| struct exec_recursive_params { | ||
| VALUE (*func) (VALUE, VALUE, int); | ||
| VALUE list; | ||
| VALUE obj; | ||
| VALUE objid; | ||
| VALUE pairid; | ||
| VALUE arg; | ||
| }; | ||
| static VALUE | ||
| exec_recursive_i(VALUE tag, struct exec_recursive_params *p) | ||
| { | ||
| VALUE result = Qundef; | ||
| int state; | ||
| recursive_push(p->list, p->objid, p->pairid); | ||
| PUSH_TAG(); | ||
| if ((state = EXEC_TAG()) == 0) { | ||
| result = (*p->func) (p->obj, p->arg, Qfalse); | ||
| } | ||
| POP_TAG(); | ||
| recursive_pop(p->list, p->objid, p->pairid); | ||
| if (state) | ||
| JUMP_TAG(state); | ||
| return result; | ||
| } | ||
| /* | ||
| * Calls func(obj, arg, recursive), where recursive is non-zero if the | ||
| * current method is called recursively on obj, or on the pair <obj, pairid> | ||
| * If outer is 0, then the innermost func will be called with recursive set | ||
| * to Qtrue, otherwise the outermost func will be called. In the latter case, | ||
| * all inner func are short-circuited by throw. | ||
| * Implementation details: the value thrown is the recursive list which is | ||
| * proper to the current method and unlikely to be catched anywhere else. | ||
| * list[recursive_key] is used as a flag for the outermost call. | ||
| */ | ||
| static VALUE | ||
| exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE arg) | ||
| exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE arg, int outer) | ||
| { | ||
| VALUE list = recursive_list_access(); | ||
| VALUE objid = rb_obj_id(obj); | ||
| struct exec_recursive_params p; | ||
| p.list = recursive_list_access(); | ||
| p.objid = rb_obj_id(obj); | ||
| int outermost = outer && !recursive_check(p.list, ID2SYM(recursive_key), 0); | ||
| if (recursive_check(list, objid, pairid)) { | ||
| if (recursive_check(p.list, p.objid, pairid)) { | ||
| if (outer && !outermost) { | ||
| rb_throw_obj(p.list, p.list); | ||
| } | ||
| return (*func) (obj, arg, Qtrue); | ||
| } | ||
| else { | ||
| VALUE result = Qundef; | ||
| int state; | ||
| recursive_push(list, objid, pairid); | ||
| PUSH_TAG(); | ||
| if ((state = EXEC_TAG()) == 0) { | ||
| result = (*func) (obj, arg, Qfalse); | ||
| p.func = func; | ||
| p.obj = obj; | ||
| p.pairid = pairid; | ||
| p.arg = arg; | ||
| if (outermost) { | ||
| recursive_push(p.list, ID2SYM(recursive_key), 0); | ||
| result = rb_catch_func(p.list, exec_recursive_i, (VALUE)&p); | ||
| recursive_pop(p.list, ID2SYM(recursive_key), 0); | ||
| if (result == p.list) { | ||
| result = (*func) (obj, arg, Qtrue); | ||
| } | ||
| } | ||
| else { | ||
| result = exec_recursive_i(0, &p); | ||
| } | ||
| POP_TAG(); | ||
| recursive_pop(list, objid, pairid); | ||
| if (state) | ||
| JUMP_TAG(state); | ||
| return result; | ||
| } | ||
| } | ||
| ... | ... | |
| VALUE | ||
| rb_exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg) | ||
| { | ||
| return exec_recursive(func, obj, 0, arg); | ||
| return exec_recursive(func, obj, 0, arg, 0); | ||
| } | ||
| /* | ||
| * Calls func(obj, arg, recursive), where recursive is non-zero if the | ||
| * current method is called recursively on the pair <obj, paired_obj> | ||
| * (in that order) | ||
| * current method is called recursively on the ordered pair <obj, paired_obj> | ||
| */ | ||
| VALUE | ||
| rb_exec_recursive_paired(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg) | ||
| { | ||
| return exec_recursive(func, obj, rb_obj_id(paired_obj), arg); | ||
| return exec_recursive(func, obj, rb_obj_id(paired_obj), arg, 0); | ||
| } | ||
| /* | ||
| * If recursion is detected on the current method and obj, the outermost | ||
| * func will be called with (obj, arg, Qtrue). All inner func will be | ||
| * short-circuited using throw. | ||
| */ | ||
| VALUE | ||
| rb_exec_recursive_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg) | ||
| { | ||
| return exec_recursive(func, obj, 0, arg, 1); | ||
| } | ||
| /* tracer */ | ||
| vm_eval.c | ||
|---|---|---|
| rb_throw_obj(ID2SYM(rb_intern(tag)), val); | ||
| } | ||
| VALUE | ||
| rb_catch_func(VALUE tag, VALUE (*func)(), VALUE data) | ||
| { | ||
| int state; | ||
| volatile VALUE val = Qnil; /* OK */ | ||
| rb_thread_t *th = GET_THREAD(); | ||
| rb_control_frame_t *saved_cfp = th->cfp; | ||
| PUSH_TAG(); | ||
| th->tag->tag = tag; | ||
| if ((state = EXEC_TAG()) == 0) { | ||
| val = (*func)(tag, data); | ||
| } | ||
| else if (state == TAG_THROW && RNODE(th->errinfo)->u1.value == tag) { | ||
| th->cfp = saved_cfp; | ||
| val = th->tag->retval; | ||
| th->errinfo = Qnil; | ||
| state = 0; | ||
| } | ||
| POP_TAG(); | ||
| if (state) | ||
| JUMP_TAG(state); | ||
| return val; | ||
| } | ||
| static VALUE | ||
| f_catch_i(VALUE tag, VALUE data) { | ||
| return rb_yield_0(1, &tag); | ||
| } | ||
| /* | ||
| * call-seq: | ||
| * catch([arg]) {|tag| block } => obj | ||
| ... | ... | |
| rb_f_catch(int argc, VALUE *argv) | ||
| { | ||
| VALUE tag; | ||
| int state; | ||
| volatile VALUE val = Qnil; /* OK */ | ||
| rb_thread_t *th = GET_THREAD(); | ||
| rb_control_frame_t *saved_cfp = th->cfp; | ||
| if (argc == 0) { | ||
| tag = rb_obj_alloc(rb_cObject); | ||
| ... | ... | |
| else { | ||
| rb_scan_args(argc, argv, "01", &tag); | ||
| } | ||
| 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) { | ||
| th->cfp = saved_cfp; | ||
| val = th->tag->retval; | ||
| th->errinfo = Qnil; | ||
| state = 0; | ||
| } | ||
| POP_TAG(); | ||
| if (state) | ||
| JUMP_TAG(state); | ||
| return val; | ||
| return rb_catch_func(tag, f_catch_i, 0); | ||
| } | ||
| static VALUE | ||