summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorknu <knu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-04-19 10:29:59 +0000
committerknu <knu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2008-04-19 10:29:59 +0000
commit932ac5d35ddc129b209161a6ae4d3356979554af (patch)
tree7612d3bf8de7df4da8bd84d43ef8e3dd28e51b94
parent10d1584e918c4236621435cf90bd0e0fbceafda2 (diff)
* eval.c (rb_exec_recursive): New internal function to help
perform recursive operation; backported from 1.9. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@16080 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog5
-rw-r--r--eval.c103
-rw-r--r--intern.h1
3 files changed, 109 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 335abea7bd..8c1cbb3d6c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Sat Apr 19 19:26:09 2008 Akinori MUSHA <knu@iDaemons.org>
+
+ * intern.h, eval.c (rb_exec_recursive): New internal function to
+ help perform recursive operation; backported from 1.9.
+
Sat Apr 19 18:42:04 2008 Akinori MUSHA <knu@iDaemons.org>
* intern.h, hash.c (rb_hash_lookup): New internal function to
diff --git a/eval.c b/eval.c
index fbca6a0fec..7b2b929bd2 100644
--- a/eval.c
+++ b/eval.c
@@ -13043,6 +13043,109 @@ thgroup_add(group, thread)
}
+/* variables for recursive traversals */
+static ID recursive_key;
+
+static VALUE
+recursive_check(hash, obj)
+ VALUE hash;
+ VALUE obj;
+{
+ if (NIL_P(hash) || TYPE(hash) != T_HASH) {
+ return Qfalse;
+ }
+ else {
+ VALUE list = rb_hash_aref(hash, ID2SYM(rb_frame_last_func()));
+
+ if (NIL_P(list) || TYPE(list) != T_HASH)
+ return Qfalse;
+ if (NIL_P(rb_hash_lookup(list, obj)))
+ return Qfalse;
+ return Qtrue;
+ }
+}
+
+static VALUE
+recursive_push(hash, obj)
+ VALUE hash;
+ VALUE obj;
+{
+ VALUE list, sym;
+
+ sym = ID2SYM(rb_frame_last_func());
+ if (NIL_P(hash) || TYPE(hash) != T_HASH) {
+ hash = rb_hash_new();
+ rb_thread_local_aset(rb_thread_current(), recursive_key, hash);
+ list = Qnil;
+ }
+ else {
+ list = rb_hash_aref(hash, sym);
+ }
+ if (NIL_P(list) || TYPE(list) != T_HASH) {
+ list = rb_hash_new();
+ rb_hash_aset(hash, sym, list);
+ }
+ rb_hash_aset(list, obj, Qtrue);
+ return hash;
+}
+
+static void
+recursive_pop(hash, obj)
+ VALUE hash;
+ VALUE obj;
+{
+ VALUE list, sym;
+
+ sym = ID2SYM(rb_frame_last_func());
+ if (NIL_P(hash) || TYPE(hash) != T_HASH) {
+ VALUE symname;
+ VALUE thrname;
+ symname = rb_inspect(sym);
+ thrname = rb_inspect(rb_thread_current());
+
+ rb_raise(rb_eTypeError, "invalid inspect_tbl hash for %s in %s",
+ StringValuePtr(symname), StringValuePtr(thrname));
+ }
+ list = rb_hash_aref(hash, sym);
+ if (NIL_P(list) || TYPE(list) != T_HASH) {
+ VALUE symname = rb_inspect(sym);
+ VALUE thrname = rb_inspect(rb_thread_current());
+ rb_raise(rb_eTypeError, "invalid inspect_tbl list for %s in %s",
+ StringValuePtr(symname), StringValuePtr(thrname));
+ }
+ rb_hash_delete(list, obj);
+}
+
+VALUE
+rb_exec_recursive(func, obj, arg)
+ VALUE (*func) _((VALUE, VALUE, int));
+ VALUE obj;
+ VALUE arg;
+{
+ VALUE hash = rb_thread_local_aref(rb_thread_current(), recursive_key);
+ VALUE objid = rb_obj_id(obj);
+
+ if (recursive_check(hash, objid)) {
+ return (*func) (obj, arg, Qtrue);
+ }
+ else {
+ VALUE result = Qundef;
+ int state;
+
+ hash = recursive_push(hash, objid);
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ result = (*func) (obj, arg, Qfalse);
+ }
+ POP_TAG();
+ recursive_pop(hash, objid);
+ if (state)
+ JUMP_TAG(state);
+ return result;
+ }
+}
+
+
/*
* +Thread+ encapsulates the behavior of a thread of
* execution, including the main thread of the Ruby script.
diff --git a/intern.h b/intern.h
index aa07ba9e0a..f4f5b56aca 100644
--- a/intern.h
+++ b/intern.h
@@ -233,6 +233,7 @@ VALUE rb_thread_main _((void));
VALUE rb_thread_local_aref _((VALUE, ID));
VALUE rb_thread_local_aset _((VALUE, ID, VALUE));
void rb_thread_atfork _((void));
+VALUE rb_exec_recursive _((VALUE(*)(VALUE, VALUE, int),VALUE,VALUE));
VALUE rb_funcall_rescue __((VALUE, ID, int, ...));
/* file.c */
VALUE rb_file_s_expand_path _((int, VALUE *));