From: shugo@... (Shugo Maeda) Date: 1997-12-10T02:04:13+09:00 Subject: [ruby-dev:1002] Object#bind --Multipart_Wed_Dec_10_02:11:39_1997-1 Content-Type: text/plain; charset=ISO-2022-JP 前田です。 ちょっと前にメソッドのオブジェクト化の話がありましたが, レシーバと結合されたメソッドを, obj.bind(:foo) で取り出せるようにするのはどうでしょう。 試しにちょっと実装してみたのでメールの末尾に添付します。 前に例に出た def function(func) proc { |*x| send(func, *x) } end と違って, rbc0> def foo rbc1> puts "foo" rbc1> end nil rbc0> foo foo nil rbc0> $foo = bind(:foo) # rbc0> def foo rbc1> puts "overriden" rbc1> end nil rbc0> foo overriden nil rbc0> $foo.call foo nil のようにメソッドがオーバーライドされても,bindした時点での メソッド定義が保存されるので,多少は意味があるような気がします。 # ちなみにbindという名前はちょっとbindingと紛らわしいような気もし # ますが,Satherのbindから連想しました。 unbound methodも同様に取り出せそうですが,考えないといけない問題が 多そうですね。 -- 前田 修吾 --Multipart_Wed_Dec_10_02:11:39_1997-1 Content-Type: application/octet-stream; type=patch Content-Disposition: attachment; filename="eval.c.diff" Content-Transfer-Encoding: 7bit --- eval.c.orig Tue Dec 9 23:51:49 1997 +++ eval.c Wed Dec 10 01:53:12 1997 @@ -484,6 +484,7 @@ static NODE *compile(); static VALUE rb_call(); +static VALUE rb_call0(); VALUE rb_apply(); VALUE rb_funcall2(); @@ -2929,14 +2930,11 @@ VALUE *argv; /* OK */ int scope; { - NODE *body, *b2; /* OK */ + NODE *body; int noex; ID id = mid; struct cache_entry *ent; - volatile VALUE result = Qnil; - int itr; enum node_type type; - static int tick; again: /* is it in the method cache? */ @@ -2955,16 +2953,6 @@ if (noex == NOEX_PRIVATE && scope == 0) return rb_undefined(recv, mid, argc, argv, CSTAT_NOEX); - switch (the_iter->iter) { - case ITER_PRE: - itr = ITER_CUR; - break; - case ITER_CUR: - default: - itr = ITER_NOT; - break; - } - type = nd_type(body); if (type == NODE_ZSUPER) { /* for re-scoped/renamed method */ @@ -2987,6 +2975,34 @@ goto again; } + return rb_call0(class, recv, mid, id, argc, argv, scope, body, type); +} + +static VALUE +rb_call0(class, recv, mid, id, argc, argv, scope, body, type) + VALUE class, recv; + ID mid, id; + int argc; /* OK */ + VALUE *argv; /* OK */ + int scope; + NODE *body; + enum node_type type; +{ + NODE *b2; /* OK */ + volatile VALUE result = Qnil; + int itr; + static int tick; + + switch (the_iter->iter) { + case ITER_PRE: + itr = ITER_CUR; + break; + case ITER_CUR: + default: + itr = ITER_NOT; + break; + } + if ((++tick & 0xfff) == 0 && stack_length() > STACK_LEVEL_MAX) Fatal("stack level too deep"); @@ -4359,6 +4375,94 @@ return result; } +VALUE cBoundMethod; +struct BOUND_METHOD { + VALUE class; + VALUE recv; + ID id; + NODE *body; +}; + +static void bm_mark(struct BOUND_METHOD *data) +{ + gc_mark(data->class); + gc_mark(data->recv); + gc_mark(data->body); +} + +static VALUE obj_bind(VALUE obj, VALUE vid) +{ + VALUE method; + VALUE class = CLASS_OF(obj); + ID mid, id; + NODE *body; + int noex; + struct cache_entry *ent; + enum node_type type; + struct BOUND_METHOD *data; + + if (TYPE(vid) == T_STRING) { + mid = rb_intern(RSTRING(vid)->ptr); + } + else { + mid = NUM2INT(vid); + } + id = mid; + + again: + /* is it in the method cache? */ + ent = cache + EXPR1(class, mid); + if (ent->mid == mid && ent->class == class) { + class = ent->origin; + id = ent->mid0; + body = ent->method; + } + else if ((body = rb_get_method_body(&class, &id, &noex)) == 0) { + return rb_undefined(obj, mid, 0, 0, 0); + } + + type = nd_type(body); + if (type == NODE_ZSUPER) { + /* for re-scoped/renamed method */ + mid = id; + if (RCLASS(class)->super == 0) { + /* origin is the Module, so need to scan superclass hierarchy. */ + struct RClass *cl = RCLASS(class); + + class = RBASIC(obj)->class; + while (class) { + if (RCLASS(class)->m_tbl == cl->m_tbl) + break; + class = RCLASS(class)->super; + } + } + else { + class = RCLASS(class)->super; + } + goto again; + } + + method = Data_Make_Struct(cBoundMethod, struct BOUND_METHOD, + bm_mark, 0, data); + data->class = class; + data->recv = obj; + data->id = id; + data->body = body; + + return method; +} + +static VALUE bmethod_call(int argc, VALUE *argv, VALUE method) +{ + struct BOUND_METHOD *data; + enum node_type type; + + Data_Get_Struct(method, struct BOUND_METHOD, data); + type = nd_type(data->body); + return rb_call0(data->class, data->recv, data->id, data->id, + argc, argv, 1, data->body, type); +} + void Init_Proc() { @@ -4371,6 +4475,11 @@ rb_define_global_function("proc", f_lambda, 0); rb_define_global_function("lambda", f_lambda, 0); rb_define_global_function("binding", f_binding, 0); + + cBoundMethod = rb_define_class("BoundMethod", cObject); + rb_undef_method(CLASS_OF(cBoundMethod), "new"); + rb_define_method(cBoundMethod, "call", bmethod_call, -1); + rb_define_method(mKernel, "bind", obj_bind, 1); } #ifdef THREAD --Multipart_Wed_Dec_10_02:11:39_1997-1--