diff options
author | shugo <shugo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-08-02 11:34:19 +0000 |
---|---|---|
committer | shugo <shugo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2012-08-02 11:34:19 +0000 |
commit | 19ddfc2483bb82cfb241a58d4b25956f7b79d1ad (patch) | |
tree | c5eecb286e12abe5d5201d4fe2c6bee9efdf5a31 /eval.c | |
parent | 319088e9c7ae9836efd242592ea80c9794a45002 (diff) | |
download | ruby-19ddfc2483bb82cfb241a58d4b25956f7b79d1ad.tar.gz |
* eval.c (rb_mod_using): new method Module#using. [experimental]
* eval.c (rb_mod_refine): new method Module#refine. [experimental]
* eval.c (f_using): new method Kernel#using. [experimental]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36596 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'eval.c')
-rw-r--r-- | eval.c | 197 |
1 files changed, 197 insertions, 0 deletions
@@ -23,6 +23,8 @@ NORETURN(void rb_raise_jump(VALUE)); +NODE *rb_vm_get_cref(const rb_iseq_t *, const VALUE *); + VALUE rb_eLocalJumpError; VALUE rb_eSysStackError; @@ -1025,6 +1027,180 @@ rb_mod_prepend(int argc, VALUE *argv, VALUE module) } void +rb_overlay_module(NODE *cref, VALUE klass, VALUE module) +{ + VALUE iclass, c, superclass = klass; + + Check_Type(klass, T_CLASS); + Check_Type(module, T_MODULE); + if (NIL_P(cref->nd_omod)) { + cref->nd_omod = rb_hash_new(); + rb_funcall(cref->nd_omod, rb_intern("compare_by_identity"), 0); + } + else { + if (cref->flags & NODE_FL_CREF_OMOD_SHARED) { + cref->nd_omod = rb_hash_dup(cref->nd_omod); + cref->flags &= ~NODE_FL_CREF_OMOD_SHARED; + } + if (!NIL_P(c = rb_hash_lookup(cref->nd_omod, klass))) { + superclass = c; + while (c && TYPE(c) == T_ICLASS) { + if (RBASIC(c)->klass == module) { + /* already overlayed module */ + return; + } + c = RCLASS_SUPER(c); + } + } + } + FL_SET(module, RMODULE_IS_OVERLAYED); + c = iclass = rb_include_class_new(module, superclass); + module = RCLASS_SUPER(module); + while (module) { + FL_SET(module, RMODULE_IS_OVERLAYED); + c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c)); + module = RCLASS_SUPER(module); + } + rb_hash_aset(cref->nd_omod, klass, iclass); + rb_clear_cache_by_class(klass); +} + +static int +using_module_i(VALUE klass, VALUE module, VALUE arg) +{ + NODE *cref = (NODE *) arg; + + rb_overlay_module(cref, klass, module); + return ST_CONTINUE; +} + +void +rb_using_module(NODE *cref, VALUE module) +{ + ID id_overlayed_modules; + VALUE overlayed_modules; + + Check_Type(module, T_MODULE); + CONST_ID(id_overlayed_modules, "__overlayed_modules__"); + overlayed_modules = rb_attr_get(module, id_overlayed_modules); + if (NIL_P(overlayed_modules)) return; + rb_hash_foreach(overlayed_modules, using_module_i, (VALUE) cref); +} + +/* + * call-seq: + * using(module) -> self + * + * Import class refinements from <i>module</i> into the receiver. + */ + +static VALUE +rb_mod_using(VALUE self, VALUE module) +{ + NODE *cref = rb_vm_cref(); + ID id_using_modules; + VALUE using_modules; + + CONST_ID(id_using_modules, "__using_modules__"); + using_modules = rb_attr_get(self, id_using_modules); + if (NIL_P(using_modules)) { + using_modules = rb_hash_new(); + rb_funcall(using_modules, rb_intern("compare_by_identity"), 0); + rb_ivar_set(self, id_using_modules, using_modules); + } + rb_hash_aset(using_modules, module, Qtrue); + rb_using_module(cref, module); + rb_funcall(module, rb_intern("used"), 1, self); + return self; +} + +void rb_redefine_opt_method(VALUE, ID); + +static VALUE +refinement_module_method_added(VALUE mod, VALUE mid) +{ + ID id = SYM2ID(mid); + ID id_refined_class; + VALUE klass; + + CONST_ID(id_refined_class, "__refined_class__"); + klass = rb_ivar_get(mod, id_refined_class); + rb_redefine_opt_method(klass, id); + return Qnil; +} + +static VALUE +refinement_module_include(int argc, VALUE *argv, VALUE module) +{ + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = th->cfp; + rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(th); + VALUE result = rb_mod_include(argc, argv, module); + NODE *cref; + ID id_refined_class; + VALUE klass, c; + + CONST_ID(id_refined_class, "__refined_class__"); + klass = rb_attr_get(module, id_refined_class); + while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) { + if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq) && + (cref = rb_vm_get_cref(cfp->iseq, cfp->ep)) && + !NIL_P(cref->nd_omod) && + !NIL_P(c = rb_hash_lookup(cref->nd_omod, klass))) { + while (argc--) { + VALUE mod = argv[argc]; + if (rb_class_inherited_p(module, mod)) { + RCLASS_SUPER(c) = + rb_include_class_new(mod, RCLASS_SUPER(c)); + } + } + break; + } + cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + } + return result; +} + +/* + * call-seq: + * refine(klass) { block } -> self + * + * Refine <i>klass</i> in the receiver. + */ + +static VALUE +rb_mod_refine(VALUE module, VALUE klass) +{ + NODE *cref = rb_vm_cref(); + VALUE mod; + ID id_overlayed_modules, id_refined_class; + VALUE overlayed_modules; + + Check_Type(klass, T_CLASS); + CONST_ID(id_overlayed_modules, "__overlayed_modules__"); + overlayed_modules = rb_attr_get(module, id_overlayed_modules); + if (NIL_P(overlayed_modules)) { + overlayed_modules = rb_hash_new(); + rb_funcall(overlayed_modules, rb_intern("compare_by_identity"), 0); + rb_ivar_set(module, id_overlayed_modules, overlayed_modules); + } + mod = rb_hash_aref(overlayed_modules, klass); + if (NIL_P(mod)) { + mod = rb_module_new(); + CONST_ID(id_refined_class, "__refined_class__"); + rb_ivar_set(mod, id_refined_class, klass); + rb_define_singleton_method(mod, "method_added", + refinement_module_method_added, 1); + rb_define_singleton_method(mod, "include", + refinement_module_include, -1); + rb_overlay_module(cref, klass, mod); + rb_hash_aset(overlayed_modules, klass, mod); + } + rb_mod_module_eval(0, NULL, mod); + return mod; +} + +void rb_obj_call_init(VALUE obj, int argc, VALUE *argv) { PASS_PASSED_BLOCK(); @@ -1134,6 +1310,23 @@ top_include(int argc, VALUE *argv, VALUE self) return rb_mod_include(argc, argv, rb_cObject); } +/* + * call-seq: + * using(module) -> self + * + * Import class refinements from <i>module</i> into the scope where + * <code>using</code> is called. + */ + +static VALUE +f_using(VALUE self, VALUE module) +{ + NODE *cref = rb_vm_cref(); + + rb_using_module(cref, module); + return self; +} + static VALUE * errinfo_place(rb_thread_t *th) { @@ -1298,6 +1491,8 @@ Init_eval(void) rb_define_private_method(rb_cModule, "include", rb_mod_include, -1); rb_define_private_method(rb_cModule, "prepend_features", rb_mod_prepend_features, 1); rb_define_private_method(rb_cModule, "prepend", rb_mod_prepend, -1); + rb_define_private_method(rb_cModule, "using", rb_mod_using, 1); + rb_define_private_method(rb_cModule, "refine", rb_mod_refine, 1); rb_undef_method(rb_cClass, "module_function"); @@ -1309,6 +1504,8 @@ Init_eval(void) rb_define_singleton_method(rb_vm_top_self(), "include", top_include, -1); + rb_define_global_function("using", f_using, 1); + rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1); rb_define_global_function("trace_var", rb_f_trace_var, -1); /* in variable.c */ |