summaryrefslogtreecommitdiff
path: root/eval.c
diff options
context:
space:
mode:
authorshugo <shugo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-08-02 11:34:19 +0000
committershugo <shugo@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-08-02 11:34:19 +0000
commit19ddfc2483bb82cfb241a58d4b25956f7b79d1ad (patch)
treec5eecb286e12abe5d5201d4fe2c6bee9efdf5a31 /eval.c
parent319088e9c7ae9836efd242592ea80c9794a45002 (diff)
* 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.c197
1 files changed, 197 insertions, 0 deletions
diff --git a/eval.c b/eval.c
index 14e0c5ce53..58735fda59 100644
--- a/eval.c
+++ b/eval.c
@@ -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 */