Skip to content

Commit

Permalink
* eval.c (rb_mod_using): new method Module#using. [experimental]
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
shugo committed Aug 2, 2012
1 parent 319088e commit 19ddfc2
Show file tree
Hide file tree
Showing 19 changed files with 699 additions and 55 deletions.
8 changes: 8 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
Thu Aug 2 20:32:29 2012 Shugo Maeda <shugo@ruby-lang.org>

* 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]

Thu Aug 2 20:08:02 2012 Shugo Maeda <shugo@ruby-lang.org>

* class.c, insns.def, method.h, proc.c, vm.c, vm_core.h, vm_eval.c,
Expand Down
6 changes: 3 additions & 3 deletions class.c
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,8 @@ rb_define_module_id_under(VALUE outer, ID id)
return module;
}

static VALUE
include_class_new(VALUE module, VALUE super)
VALUE
rb_include_class_new(VALUE module, VALUE super)
{
VALUE klass = class_alloc(T_ICLASS, rb_cClass);

Expand Down Expand Up @@ -703,7 +703,7 @@ include_modules_at(VALUE klass, VALUE c, VALUE module)
break;
}
}
c = RCLASS_SUPER(c) = include_class_new(module, RCLASS_SUPER(c));
c = RCLASS_SUPER(c) = rb_include_class_new(module, RCLASS_SUPER(c));
if (RMODULE_M_TBL(module) && RMODULE_M_TBL(module)->num_entries)
changed = 1;
skip:
Expand Down
197 changes: 197 additions & 0 deletions eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -1024,6 +1026,180 @@ rb_mod_prepend(int argc, VALUE *argv, VALUE module)
return 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)
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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");

Expand All @@ -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 */
Expand Down
6 changes: 6 additions & 0 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,12 @@ gc_mark_children(rb_objspace_t *objspace, VALUE ptr, int lev)
ptr = (VALUE)obj->as.node.u2.node;
goto again;

case NODE_CREF:
gc_mark(objspace, obj->as.node.nd_omod, lev);
gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev);
ptr = (VALUE)obj->as.node.u3.node;
goto again;

default: /* unlisted NODE */
if (is_pointer_to_heap(objspace, obj->as.node.u1.node)) {
gc_mark(objspace, (VALUE)obj->as.node.u1.node, lev);
Expand Down
1 change: 1 addition & 0 deletions include/ruby/intern.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ VALUE rb_define_class_id_under(VALUE, ID, VALUE);
VALUE rb_module_new(void);
VALUE rb_define_module_id(ID);
VALUE rb_define_module_id_under(VALUE, ID);
VALUE rb_include_class_new(VALUE, VALUE);
VALUE rb_mod_included_modules(VALUE);
VALUE rb_mod_include_p(VALUE, VALUE);
VALUE rb_mod_ancestors(VALUE);
Expand Down
1 change: 1 addition & 0 deletions include/ruby/ruby.h
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,7 @@ struct RClass {
#define RMODULE_CONST_TBL(m) RCLASS_CONST_TBL(m)
#define RMODULE_M_TBL(m) RCLASS_M_TBL(m)
#define RMODULE_SUPER(m) RCLASS_SUPER(m)
#define RMODULE_IS_OVERLAYED FL_USER2

struct RFloat {
struct RBasic basic;
Expand Down
16 changes: 8 additions & 8 deletions insns.def
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ getclassvariable
()
(VALUE val)
{
NODE *cref = vm_get_cref(GET_ISEQ(), GET_EP());
val = rb_cvar_get(vm_get_cvar_base(cref), id);
NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP());
val = rb_cvar_get(vm_get_cvar_base(cref, GET_CFP()), id);
}

/**
Expand All @@ -198,8 +198,8 @@ setclassvariable
(VALUE val)
()
{
NODE *cref = vm_get_cref(GET_ISEQ(), GET_EP());
rb_cvar_set(vm_get_cvar_base(cref), id, val);
NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP());
rb_cvar_set(vm_get_cvar_base(cref, GET_CFP()), id, val);
}

/**
Expand Down Expand Up @@ -777,8 +777,8 @@ defined
break;
case DEFINED_CVAR:
{
NODE *cref = vm_get_cref(GET_ISEQ(), GET_EP());
klass = vm_get_cvar_base(cref);
NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP());
klass = vm_get_cvar_base(cref, GET_CFP());
if (rb_cvar_defined(klass, SYM2ID(obj))) {
expr_type = "class variable";
}
Expand Down Expand Up @@ -970,6 +970,7 @@ defineclass
klass, 0, VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()),
class_iseq->iseq_encoded, GET_SP(),
class_iseq->local_size, 0);
rb_vm_using_modules(class_iseq->cref_stack, klass);
RESTORE_REGS();

INC_VM_STATE_VERSION();
Expand Down Expand Up @@ -1044,12 +1045,11 @@ invokesuper
while (ip && !ip->klass) {
ip = ip->parent_iseq;
}
again:
me = rb_method_entry(klass, id, &klass);
if (me && me->def->type == VM_METHOD_TYPE_ISEQ &&
me->def->body.iseq == ip) {
klass = RCLASS_SUPER(klass);
goto again;
me = rb_method_entry_get_with_omod(Qnil, klass, id, &klass);
}

CALL_METHOD(num, blockptr, flag, id, me, recv, klass);
Expand Down
13 changes: 9 additions & 4 deletions iseq.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,17 +199,20 @@ set_relation(rb_iseq_t *iseq, const VALUE parent)
/* set class nest stack */
if (type == ISEQ_TYPE_TOP) {
/* toplevel is private */
iseq->cref_stack = NEW_BLOCK(rb_cObject);
iseq->cref_stack = NEW_CREF(rb_cObject);
iseq->cref_stack->nd_omod = Qnil;
iseq->cref_stack->nd_visi = NOEX_PRIVATE;
if (th->top_wrapper) {
NODE *cref = NEW_BLOCK(th->top_wrapper);
NODE *cref = NEW_CREF(th->top_wrapper);
cref->nd_omod = Qnil;
cref->nd_visi = NOEX_PRIVATE;
cref->nd_next = iseq->cref_stack;
iseq->cref_stack = cref;
}
}
else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) {
iseq->cref_stack = NEW_BLOCK(0); /* place holder */
iseq->cref_stack = NEW_CREF(0); /* place holder */
iseq->cref_stack->nd_omod = Qnil;
}
else if (RTEST(parent)) {
rb_iseq_t *piseq;
Expand Down Expand Up @@ -1653,7 +1656,9 @@ rb_iseq_clone(VALUE iseqval, VALUE newcbase)
iseq1->local_iseq = iseq1;
}
if (newcbase) {
iseq1->cref_stack = NEW_BLOCK(newcbase);
iseq1->cref_stack = NEW_CREF(newcbase);
iseq1->cref_stack->nd_omod = iseq0->cref_stack->nd_omod;
iseq1->cref_stack->nd_visi = iseq0->cref_stack->nd_visi;
if (iseq0->cref_stack->nd_next) {
iseq1->cref_stack->nd_next = iseq0->cref_stack->nd_next;
}
Expand Down
3 changes: 2 additions & 1 deletion method.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ void rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc,
rb_method_entry_t *rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *option, rb_method_flag_t noex);
rb_method_entry_t *rb_method_entry(VALUE klass, ID id, VALUE *define_class_ptr);

rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, ID id, VALUE *define_class_ptr);
rb_method_entry_t *rb_method_entry_get_with_omod(VALUE omod, VALUE klass, ID id, VALUE *define_class_ptr);
rb_method_entry_t *rb_method_entry_get_without_cache(VALUE klass, VALUE omod, ID id, VALUE *define_class_ptr);
rb_method_entry_t *rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *, rb_method_flag_t noex);

int rb_method_entry_arity(const rb_method_entry_t *me);
Expand Down
Loading

0 comments on commit 19ddfc2

Please sign in to comment.