Project

General

Profile

Feature #4085 » refinements-r29944-20101127.diff

a patch to support Refinements - shugo (Shugo Maeda), 11/27/2010 01:39 PM

View differences:

class.c
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);
......
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:
eval.c
VALUE rb_binding_new(void);
NORETURN(void rb_raise_jump(VALUE));
NODE *rb_vm_get_cref(const rb_iseq_t *, const VALUE *, const VALUE *);
ID rb_frame_callee(void);
VALUE rb_eLocalJumpError;
VALUE rb_eSysStackError;
......
}
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;
int i;
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);
}
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->lfp, cfp->dfp)) &&
!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, 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();
......
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>use</code> is called.
*/
static VALUE
f_using(VALUE self, VALUE module)
{
NODE *cref = rb_vm_cref();
rb_using_module(cref, module);
return self;
}
VALUE rb_f_trace_var();
VALUE rb_f_untrace_var();
......
rb_define_private_method(rb_cModule, "append_features", rb_mod_append_features, 1);
rb_define_private_method(rb_cModule, "extend_object", rb_mod_extend_object, 1);
rb_define_private_method(rb_cModule, "include", rb_mod_include, -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");
......
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 */
gc.c
ptr = (VALUE)obj->as.node.u2.node;
goto again;
case NODE_CREF:
gc_mark(objspace, obj->as.node.u0.value, 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);
include/ruby/intern.h
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);
include/ruby/ruby.h
#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;
insns.def
()
(VALUE val)
{
NODE * const cref = vm_get_cref(GET_ISEQ(), GET_LFP(), GET_DFP());
val = rb_cvar_get(vm_get_cvar_base(cref), id);
NODE * const cref = rb_vm_get_cref(GET_ISEQ(), GET_LFP(), GET_DFP());
val = rb_cvar_get(vm_get_cvar_base(cref, GET_CFP()), id);
}
/**
......
(VALUE val)
()
{
NODE * const cref = vm_get_cref(GET_ISEQ(), GET_LFP(), GET_DFP());
rb_cvar_set(vm_get_cvar_base(cref), id, val);
NODE * const cref = rb_vm_get_cref(GET_ISEQ(), GET_LFP(), GET_DFP());
rb_cvar_set(vm_get_cvar_base(cref, GET_CFP()), id, val);
}
/**
......
VM_FRAME_MAGIC_CLASS, klass, 0, (VALUE) GET_BLOCK_PTR(),
class_iseq->iseq_encoded, GET_SP(), 0,
class_iseq->local_size);
rb_vm_using_modules(class_iseq->cref_stack, klass);
RESTORE_REGS();
INC_VM_STATE_VERSION();
......
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);
iseq.c
/* set class nest stack */
if (type == ISEQ_TYPE_TOP) {
/* toplevel is private */
iseq->cref_stack = NEW_BLOCK(rb_cObject);
iseq->cref_stack->nd_file = 0;
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);
cref->nd_file = 0;
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->nd_file = 0;
iseq->cref_stack = NEW_CREF(0); /* place holder */
iseq->cref_stack->nd_omod = Qnil;
}
else if (RTEST(parent)) {
rb_iseq_t *piseq;
......
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;
}
method.h
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);
node.h
#define NODE_COLON2 NODE_COLON2
NODE_COLON3,
#define NODE_COLON3 NODE_COLON3
NODE_CREF,
#define NODE_CREF NODE_CREF
NODE_DOT2,
#define NODE_DOT2 NODE_DOT2
NODE_DOT3,
......
typedef struct RNode {
unsigned long flags;
char *nd_file;
union {
char *file;
VALUE value;
} u0;
union {
struct RNode *node;
ID id;
......
#define RNODE(obj) (R_CAST(RNode)(obj))
/* 0..4:T_TYPES, 5:FL_MARK, 6:reserved, 7:NODE_FL_NEWLINE */
/* 0..4:T_TYPES, 5:FL_MARK, 6:NODE_FL_CREF_OMOD_SHARED, 7:NODE_FL_NEWLINE */
#define NODE_FL_NEWLINE (((VALUE)1)<<7)
#define NODE_FL_CREF_PUSHED_BY_EVAL NODE_FL_NEWLINE
#define NODE_FL_CREF_OMOD_SHARED (((VALUE)1)<<6)
#define NODE_TYPESHIFT 8
#define NODE_TYPEMASK (((VALUE)0x7f)<<NODE_TYPESHIFT)
......
#define nd_set_line(n,l) \
RNODE(n)->flags=((RNODE(n)->flags&~(-1<<NODE_LSHIFT))|(((l)&NODE_LMASK)<<NODE_LSHIFT))
#define nd_file u0.file
#define nd_omod u0.value
#define nd_head u1.node
#define nd_alen u2.argc
#define nd_next u3.node
......
#define NEW_MODULE(n,b) NEW_NODE(NODE_MODULE,n,NEW_SCOPE(0,b),0)
#define NEW_COLON2(c,i) NEW_NODE(NODE_COLON2,c,i,0)
#define NEW_COLON3(i) NEW_NODE(NODE_COLON3,0,i,0)
#define NEW_CREF(a) NEW_NODE(NODE_CREF,a,0,0)
#define NEW_DOT2(b,e) NEW_NODE(NODE_DOT2,b,e,0)
#define NEW_DOT3(b,e) NEW_NODE(NODE_DOT3,b,e,0)
#define NEW_SELF() NEW_NODE(NODE_SELF,0,0,0)
object.c
rb_define_private_method(rb_cClass, "inherited", rb_obj_dummy, 1);
rb_define_private_method(rb_cModule, "included", rb_obj_dummy, 1);
rb_define_private_method(rb_cModule, "extended", rb_obj_dummy, 1);
rb_define_private_method(rb_cModule, "used", rb_obj_dummy, 1);
rb_define_private_method(rb_cModule, "method_added", rb_obj_dummy, 1);
rb_define_private_method(rb_cModule, "method_removed", rb_obj_dummy, 1);
rb_define_private_method(rb_cModule, "method_undefined", rb_obj_dummy, 1);
test/ruby/test_refinement.rb
require 'test/unit'
class TestRefinement < Test::Unit::TestCase
class Foo
def x
return "Foo#x"
end
def y
return "Foo#y"
end
def call_x
return x
end
end
module FooExt
refine Foo do
def x
return "FooExt#x"
end
def y
return "FooExt#y " + super
end
def z
return "FooExt#z"
end
end
end
module FooExt2
refine Foo do
def x
return "FooExt2#x"
end
def y
return "FooExt2#y " + super
end
def z
return "FooExt2#z"
end
end
end
class FooSub < Foo
def x
return "FooSub#x"
end
def y
return "FooSub#y " + super
end
end
class FooExtClient
using FooExt
def self.invoke_x_on(foo)
return foo.x
end
def self.invoke_y_on(foo)
return foo.y
end
def self.invoke_z_on(foo)
return foo.z
end
def self.invoke_call_x_on(foo)
return foo.call_x
end
end
class FooExtClient2
using FooExt
using FooExt2
def self.invoke_y_on(foo)
return foo.y
end
end
def test_override
foo = Foo.new
assert_equal("Foo#x", foo.x)
assert_equal("FooExt#x", FooExtClient.invoke_x_on(foo))
assert_equal("Foo#x", foo.x)
end
def test_super
foo = Foo.new
assert_equal("Foo#y", foo.y)
assert_equal("FooExt#y Foo#y", FooExtClient.invoke_y_on(foo))
assert_equal("Foo#y", foo.y)
end
def test_super_chain
foo = Foo.new
assert_equal("Foo#y", foo.y)
assert_equal("FooExt2#y FooExt#y Foo#y", FooExtClient2.invoke_y_on(foo))
assert_equal("Foo#y", foo.y)
end
def test_new_method
foo = Foo.new
assert_raise(NoMethodError) { foo.z }
assert_equal("FooExt#z", FooExtClient.invoke_z_on(foo))
assert_raise(NoMethodError) { foo.z }
end
def test_no_local_rebinding
foo = Foo.new
assert_equal("Foo#x", foo.call_x)
assert_equal("Foo#x", FooExtClient.invoke_call_x_on(foo))
assert_equal("Foo#x", foo.call_x)
end
def test_subclass_is_prior
sub = FooSub.new
assert_equal("FooSub#x", sub.x)
assert_equal("FooSub#x", FooExtClient.invoke_x_on(sub))
assert_equal("FooSub#x", sub.x)
end
def test_subclass_is_prior
sub = FooSub.new
assert_equal("FooSub#x", sub.x)
assert_equal("FooSub#x", FooExtClient.invoke_x_on(sub))
assert_equal("FooSub#x", sub.x)
end
def test_super_in_subclass
sub = FooSub.new
assert_equal("FooSub#y Foo#y", sub.y)
# not "FooSub#y FooExt#y Foo#y"
assert_equal("FooSub#y Foo#y", FooExtClient.invoke_y_on(sub))
assert_equal("FooSub#y Foo#y", sub.y)
end
def test_new_method_on_subclass
sub = FooSub.new
assert_raise(NoMethodError) { sub.z }
assert_equal("FooExt#z", FooExtClient.invoke_z_on(sub))
assert_raise(NoMethodError) { sub.z }
end
def test_module_eval
foo = Foo.new
assert_equal("Foo#x", foo.x)
assert_equal("FooExt#x", FooExt.module_eval { foo.x })
assert_equal("Foo#x", foo.x)
end
def test_instance_eval
foo = Foo.new
ext_client = FooExtClient.new
assert_equal("Foo#x", foo.x)
assert_equal("FooExt#x", ext_client.instance_eval { foo.x })
assert_equal("Foo#x", foo.x)
end
def test_override_builtin_method
m = Module.new {
refine Fixnum do
def /(other) quo(other) end
end
}
assert_equal(0, 1 / 2)
assert_equal(Rational(1, 2), m.module_eval { 1 / 2 })
assert_equal(0, 1 / 2)
end
def test_return_value_of_refine
mod = nil
result = nil
m = Module.new {
result = refine(Object) {
mod = self
}
}
assert_equal mod, result
end
def test_refine_same_class_twice
result1 = nil
result2 = nil
result3 = nil
m = Module.new {
result1 = refine(Fixnum) {
def foo; return "foo" end
}
result2 = refine(Fixnum) {
def bar; return "bar" end
}
result3 = refine(String) {
def baz; return "baz" end
}
}
assert_equal("foo", m.module_eval { 1.foo })
assert_equal("bar", m.module_eval { 1.bar })
assert_equal(result1, result2)
assert_not_equal(result1, result3)
end
def test_respond_to?
m = Module.new {
refine Fixnum do
def foo; "foo"; end
end
}
assert_equal(false, 1.respond_to?(:foo))
assert_equal(true, m.module_eval { 1.respond_to?(:foo) })
assert_equal(false, 1.respond_to?(:foo))
end
def test_builtin_method_no_local_rebinding
m = Module.new {
refine String do
def <=>(other) return 0 end
end
}
assert_equal(false, m.module_eval { "1" >= "2" })
m2 = Module.new {
refine Array do
def each
super do |i|
yield 2 * i
end
end
end
}
a = [1, 2, 3]
assert_equal(1, m2.module_eval { a.min })
end
def test_module_inclusion
m1 = Module.new {
def foo
"m1#foo"
end
def bar
"m1#bar"
end
}
m2 = Module.new {
def bar
"m2#bar"
end
def baz
"m2#baz"
end
}
m3 = Module.new {
def baz
"m3#baz"
end
}
include_proc = Proc.new {
include m3, m2
}
m = Module.new {
refine String do
include m1
module_eval(&include_proc)
def call_foo
foo
end
def call_bar
bar
end
def call_baz
baz
end
end
def self.call_foo(s)
s.foo
end
def self.call_bar(s)
s.bar
end
def self.call_baz(s)
s.baz
end
}
assert_equal("m1#foo", m.module_eval { "abc".foo })
assert_equal("m2#bar", m.module_eval { "abc".bar })
assert_equal("m3#baz", m.module_eval { "abc".baz })
assert_equal("m1#foo", m.module_eval { "abc".call_foo })
assert_equal("m2#bar", m.module_eval { "abc".call_bar })
assert_equal("m3#baz", m.module_eval { "abc".call_baz })
assert_equal("m1#foo", m.call_foo("abc"))
assert_equal("m2#bar", m.call_bar("abc"))
assert_equal("m3#baz", m.call_baz("abc"))
end
end
vm.c
{
rb_thread_t *th = GET_THREAD();
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
return vm_get_cref(cfp->iseq, cfp->lfp, cfp->dfp);
if (!cfp) return NULL;
return rb_vm_get_cref(cfp->iseq, cfp->lfp, cfp->dfp);
}
#if 0
vm_eval.c
rb_id2name(mid), type, (void *)recv, flags, klass);
}
}
return rb_method_entry(klass, mid, defined_class_ptr);
return rb_method_entry_get_with_omod(Qnil, klass, mid, defined_class_ptr);
}
static inline int
......
}
cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);
cref->flags |= NODE_FL_CREF_PUSHED_BY_EVAL;
rb_vm_using_modules(cref, under);
if (values == Qundef) {
return vm_yield_with_cref(th, 1, &self, cref);
......
else {
SafeStringValue(src);
}
rb_vm_using_modules(cref, under);
return eval_string_with_cref(self, src, Qnil, cref, file, line);
}
vm_insnhelper.c
}
}
static NODE *
vm_get_cref(const rb_iseq_t *iseq, const VALUE *lfp, const VALUE *dfp)
NODE *
rb_vm_get_cref(const rb_iseq_t *iseq, const VALUE *lfp, const VALUE *dfp)
{
NODE *cref = vm_get_cref0(iseq, lfp, dfp);
if (cref == 0) {
rb_bug("vm_get_cref: unreachable");
rb_bug("rb_vm_get_cref: unreachable");
}
return cref;
}
......
vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr)
{
rb_control_frame_t *cfp = vm_get_ruby_level_caller_cfp(th, th->cfp);
NODE *cref = NEW_BLOCK(klass);
cref->nd_file = 0;
NODE *cref = NEW_CREF(klass);
cref->nd_omod = Qnil;
cref->nd_visi = noex;
if (blockptr) {
......
else if (cfp) {
cref->nd_next = vm_get_cref0(cfp->iseq, cfp->lfp, cfp->dfp);
}
/* TODO: why cref->nd_next is 1? */
if (cref->nd_next && cref->nd_next != (void *) 1 &&
!NIL_P(cref->nd_next->nd_omod)) {
COPY_CREF_OMOD(cref, cref->nd_next);
}
return cref;
}
......
static inline VALUE
vm_get_cbase(const rb_iseq_t *iseq, const VALUE *lfp, const VALUE *dfp)
{
NODE *cref = vm_get_cref(iseq, lfp, dfp);
NODE *cref = rb_vm_get_cref(iseq, lfp, dfp);
VALUE klass = Qundef;
while (cref) {
......
static inline VALUE
vm_get_const_base(const rb_iseq_t *iseq, const VALUE *lfp, const VALUE *dfp)
{
NODE *cref = vm_get_cref(iseq, lfp, dfp);
NODE *cref = rb_vm_get_cref(iseq, lfp, dfp);
VALUE klass = Qundef;
while (cref) {
......
}
static inline VALUE
vm_get_iclass(rb_control_frame_t *cfp, VALUE klass)
{
if (TYPE(klass) == T_MODULE &&
FL_TEST(klass, RMODULE_IS_OVERLAYED) &&
TYPE(cfp->klass) == T_ICLASS &&
RBASIC(cfp->klass)->klass == klass) {
return cfp->klass;
}
else {
return klass;
}
}
static inline VALUE
vm_get_ev_const(rb_thread_t *th, const rb_iseq_t *iseq,
VALUE orig_klass, ID id, int is_defined)
{
......
if (orig_klass == Qnil) {
/* in current lexical scope */
const NODE *cref = vm_get_cref(iseq, th->cfp->lfp, th->cfp->dfp);
const NODE *cref = rb_vm_get_cref(iseq, th->cfp->lfp, th->cfp->dfp);
const NODE *root_cref = NULL;
VALUE klass = orig_klass;
......
/* search self */
if (root_cref && !NIL_P(root_cref->nd_clss)) {
klass = root_cref->nd_clss;
klass = vm_get_iclass(th->cfp, root_cref->nd_clss);
}
else {
klass = CLASS_OF(th->cfp->self);
......
}
static inline VALUE
vm_get_cvar_base(NODE *cref)
vm_get_cvar_base(NODE *cref, rb_control_frame_t *cfp)
{
VALUE klass;
......
}
}
klass = cref->nd_clss;
klass = vm_get_iclass(cfp, cref->nd_clss);
if (NIL_P(klass)) {
rb_raise(rb_eTypeError, "no class variables available");
......
return Qundef;
}
void rb_using_module(NODE *cref, VALUE module);
static int
vm_using_module_i(VALUE module, VALUE value, VALUE arg)
{
NODE *cref = (NODE *) arg;
rb_using_module(cref, module);
return ST_CONTINUE;
}
static void
rb_vm_using_modules(NODE *cref, VALUE klass)
{
ID id_using_modules;
VALUE using_modules;
CONST_ID(id_using_modules, "__using_modules__");
using_modules = rb_attr_get(klass, id_using_modules);
switch (TYPE(klass)) {
case T_CLASS:
if (NIL_P(using_modules)) {
VALUE super = rb_class_real(RCLASS_SUPER(klass));
using_modules = rb_attr_get(super, id_using_modules);
if (!NIL_P(using_modules)) {
using_modules = rb_hash_dup(using_modules);
rb_ivar_set(klass, id_using_modules, using_modules);
}
}
break;
case T_MODULE:
rb_using_module(cref, klass);
break;
}
if (!NIL_P(using_modules)) {
rb_hash_foreach(using_modules, vm_using_module_i,
(VALUE) cref);
}
}
vm_insnhelper.h
/* deal with control flow 2: method/iterator */
/**********************************************************/
#define COPY_CREF_OMOD(c1, c2) do { \
c1->nd_omod = c2->nd_omod; \
if (!NIL_P(c2->nd_omod)) { \
c1->flags |= NODE_FL_CREF_OMOD_SHARED; \
c2->flags |= NODE_FL_CREF_OMOD_SHARED; \
} \
} while (0)
#define COPY_CREF(c1, c2) do { \
NODE *__tmp_c2 = (c2); \
COPY_CREF_OMOD(c1, __tmp_c2); \
c1->nd_clss = __tmp_c2->nd_clss; \
c1->nd_visi = __tmp_c2->nd_visi;\
c1->nd_next = __tmp_c2->nd_next; \
vm_method.c
#define CACHE_SIZE 0x800
#define CACHE_MASK 0x7ff
#define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK)
#define EXPR1(c,o,m) ((((c)>>3)^((o)>>3)^(m))&CACHE_MASK)
static void rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me);
......
struct cache_entry { /* method hash table. */
ID mid; /* method's id */
VALUE klass; /* receiver's class */
VALUE omod; /* overlay modules */
rb_method_entry_t *me;
VALUE defined_class;
};
......
}
static rb_method_entry_t*
search_method(VALUE klass, ID id, VALUE *defined_class_ptr)
search_method(VALUE klass, ID id, VALUE omod, VALUE *defined_class_ptr)
{
st_data_t body;
VALUE iclass, skipped_class = Qnil;
if (!klass) {
return 0;
}
if (!NIL_P(omod) && !NIL_P(iclass = rb_hash_lookup(omod, klass))) {
klass = iclass;
}
while (!st_lookup(RCLASS_M_TBL(klass), id, &body)) {
klass = RCLASS_SUPER(klass);
if (!NIL_P(omod) && klass != skipped_class &&
!NIL_P(iclass = rb_hash_lookup(omod, klass))) {
skipped_class = klass;
klass = iclass;
}
if (!klass) {
return 0;
}
......
* rb_method_entry() simply.
*/
rb_method_entry_t *
rb_method_entry_get_without_cache(VALUE klass, ID id, VALUE *defined_class_ptr)
rb_method_entry_get_without_cache(VALUE klass, VALUE omod, ID id, VALUE *defined_class_ptr)
{
VALUE defined_class;
VALUE iclass, defined_class;
rb_method_entry_t *me;
me = search_method(klass, id, &defined_class);
me = search_method(klass, id, omod, &defined_class);
if (ruby_running) {
struct cache_entry *ent;
ent = cache + EXPR1(klass, id);
ent = cache + EXPR1(klass, omod, id);
ent->klass = klass;
ent->omod = omod;
ent->defined_class = defined_class;
if (UNDEFINED_METHOD_ENTRY_P(me)) {
......
}
rb_method_entry_t *
rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr)
rb_method_entry_get_with_omod(VALUE omod, VALUE klass, ID id,
VALUE *defined_class_ptr)
{
struct cache_entry *ent;
ent = cache + EXPR1(klass, id);
if (ent->mid == id && ent->klass == klass) {
ent = cache + EXPR1(klass, omod, id);
if (ent->mid == id && ent->klass == klass && ent->omod == omod) {
if (defined_class_ptr)
*defined_class_ptr = ent->defined_class;
return ent->me;
}
return rb_method_entry_get_without_cache(klass, id, defined_class_ptr);
return rb_method_entry_get_without_cache(klass, omod, id,
defined_class_ptr);
}
rb_method_entry_t *
rb_method_entry(VALUE klass, ID id, VALUE *defined_class_ptr)
{
struct cache_entry *ent;
NODE *cref = rb_vm_cref();
VALUE omod = Qnil;
if (cref && !NIL_P(cref->nd_omod)) {
omod = cref->nd_omod;
}
return rb_method_entry_get_with_omod(omod, klass, id, defined_class_ptr);
}
static void
......
rb_secure(4);
}
me = search_method(klass, name, &defined_class);
me = search_method(klass, name, Qnil, &defined_class);
if (!me && TYPE(klass) == T_MODULE) {
me = search_method(rb_cObject, name, &defined_class);
me = search_method(rb_cObject, name, Qnil, &defined_class);
}
if (UNDEFINED_METHOD_ENTRY_P(me)) {
......
rb_undef(VALUE klass, ID id)
{
rb_method_entry_t *me;
NODE *cref = rb_vm_cref();
VALUE omod = Qnil;
if (NIL_P(klass)) {
rb_raise(rb_eTypeError, "no class to undef method");
......
rb_warn("undefining `%s' may cause serious problems", rb_id2name(id));
}
me = search_method(klass, id, 0);
if (cref && !NIL_P(cref->nd_omod)) {
omod = cref->nd_omod;
}
me = search_method(klass, id, omod, 0);
if (UNDEFINED_METHOD_ENTRY_P(me)) {
const char *s0 = " class";
......
rb_id2name(id), s0, rb_class2name(c));
}
if (!RTEST(rb_class_inherited_p(klass, me->klass))) {
VALUE mod = rb_module_new();
rb_overlay_module(cref, klass, mod);
klass = mod;
}
rb_add_method(klass, id, VM_METHOD_TYPE_UNDEF, 0, NOEX_PUBLIC);
CALL_METHOD_HOOK(klass, undefined, id);
......
}
again:
orig_me = search_method(klass, def, 0);
orig_me = search_method(klass, def, Qnil, 0);
if (UNDEFINED_METHOD_ENTRY_P(orig_me)) {
if ((TYPE(klass) != T_MODULE) ||
(orig_me = search_method(rb_cObject, def, 0),
(orig_me = search_method(rb_cObject, def, Qnil, 0),
UNDEFINED_METHOD_ENTRY_P(orig_me))) {
rb_print_undef(klass, def, 0);
}
......
id = rb_to_id(argv[i]);
for (;;) {
me = search_method(m, id, 0);
me = search_method(m, id, Qnil, 0);
if (me == 0) {
me = search_method(rb_cObject, id, 0);
me = search_method(rb_cObject, id, Qnil, 0);
}
if (UNDEFINED_METHOD_ENTRY_P(me)) {
rb_print_undef(module, id, 0);
......
}
void
rb_redefine_opt_method(VALUE klass, ID mid)
{
st_data_t key, data;
rb_method_entry_t *me = 0;
if (!st_lookup(RCLASS_M_TBL(klass), mid, &data) ||
!(me = (rb_method_entry_t *)data) ||
(!me->def || me->def->type == VM_METHOD_TYPE_UNDEF)) {
return;
}
rb_vm_check_redefinition_opt_method(me);
}
void
Init_eval_method(void)
{
#undef rb_intern
(3-3/4)