Project

General

Profile

Feature #4085 » nested_methods-r29944-20101127.diff

a patch to change behavior of nested methods - shugo (Shugo Maeda), 11/27/2010 01:39 PM

View differences:

bootstraptest/test_method.rb
assert_equal '1', %q( class C
def m
def mm() 1 end
mm
end
end
C.new.m
C.new.mm )
C.new.m )
assert_equal '1', %q( class C
def m
def mm() 1 end
mm
end
end
instance_eval "C.new.m; C.new.mm" )
instance_eval "C.new.m" )
# method_missing
assert_equal ':m', %q( class C
compile.c
ADD_INSN1(ret, nd_line(node), putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
ADD_INSN1(ret, nd_line(node), putobject, ID2SYM(node->nd_mid));
ADD_INSN1(ret, nd_line(node), putiseq, iseqval);
ADD_SEND (ret, nd_line(node), ID2SYM(id_core_define_method), INT2FIX(3));
ADD_INSN1(ret, nd_line(node), putobject,
node->flags & NODE_FL_NESTED_DEF ? Qtrue : Qfalse);
ADD_SEND (ret, nd_line(node), ID2SYM(id_core_define_method), INT2FIX(4));
if (poped) {
ADD_INSN(ret, nd_line(node), pop);
include/ruby/ruby.h
#define RMODULE_M_TBL(m) RCLASS_M_TBL(m)
#define RMODULE_SUPER(m) RCLASS_SUPER(m)
#define RMODULE_IS_OVERLAYED FL_USER2
#define RMODULE_HAS_NESTED_METHODS FL_USER3
struct RFloat {
struct RBasic basic;
node.h
#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_FL_NESTED_DEF NODE_FL_CREF_OMOD_SHARED
#define NODE_TYPESHIFT 8
#define NODE_TYPEMASK (((VALUE)0x7f)<<NODE_TYPESHIFT)
parse.y
reduce_nodes(&body);
$$ = NEW_DEFN($2, $4, body, NOEX_PRIVATE);
nd_set_line($$, $<num>1);
if (in_def > 1 || in_single > 0)
$$->flags |= NODE_FL_NESTED_DEF;
/*%
$$ = dispatch3(def, $2, $4, $5);
%*/
test/rdoc/test_rdoc_text.rb
assert_equal expected, flush_left(text)
end
def formatter() RDoc::Markup::ToHtml.new end
def test_markup
def formatter() RDoc::Markup::ToHtml.new end
assert_equal "<p>\nhi\n</p>\n", markup('hi')
end
test/ruby/test_nested_method.rb
require 'test/unit'
class TestNestedMethod < Test::Unit::TestCase
def call_nested_method
def foo
return "foo"
end
return foo
end
def test_nested_method
assert_equal("foo", call_nested_method)
assert_raise(NoMethodError) { foo() }
end
def test_doubly_nested_method
def call_doubly_nested_method
def foo
return "foo"
end
return foo
end
assert_equal("foo", call_doubly_nested_method)
assert_raise(NoMethodError) { foo() }
end
end
vm.c
return self;
}
static VALUE
find_module_for_nested_methods(NODE *cref, VALUE klass)
{
VALUE iclass;
if (NIL_P(cref->nd_omod))
return Qnil;
iclass = rb_hash_lookup(cref->nd_omod, klass);
if (NIL_P(iclass))
return Qnil;
while (iclass) {
VALUE module = RBASIC(iclass)->klass;
if (FL_TEST(module, RMODULE_HAS_NESTED_METHODS)) {
return module;
}
iclass = RCLASS_SUPER(iclass);
}
return Qnil;
}
VALUE rb_iseq_clone(VALUE iseqval, VALUE newcbase);
static void
vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval,
vm_define_method(rb_thread_t *th, VALUE obj, ID id, VALUE iseqval, VALUE nested,
rb_num_t is_singleton, NODE *cref)
{
VALUE klass = cref->nd_clss;
VALUE target, defined_class;
rb_method_entry_t *me;
int noex = (int)cref->nd_visi;
int is_nested = RTEST(nested);
rb_iseq_t *miseq;
GetISeqPtr(iseqval, miseq);
......
noex = NOEX_PUBLIC;
}
if (is_nested && th->cfp->lfp == th->cfp->dfp) {
VALUE c;
if (TYPE(klass) == T_MODULE) {
c = rb_obj_class(th->cfp->self);
}
else {
c = klass;
}
if (cref->flags & NODE_FL_CREF_OMOD_SHARED) {
target = Qnil;
}
else {
target = find_module_for_nested_methods(cref, c);
}
if (NIL_P(target)) {
target = rb_module_new();
FL_SET(target, RMODULE_HAS_NESTED_METHODS);
rb_overlay_module(cref, c, target);
}
else {
me = search_method(target, id, Qnil, &defined_class);
if (me && me->def->type == VM_METHOD_TYPE_ISEQ &&
me->def->body.iseq == miseq) {
return;
}
}
noex = NOEX_PRIVATE;
}
else {
target = klass;
}
/* dup */
COPY_CREF(miseq->cref_stack, cref);
miseq->cref_stack->nd_visi = NOEX_PUBLIC;
miseq->klass = klass;
miseq->klass = target;
miseq->defined_method_id = id;
rb_add_method(klass, id, VM_METHOD_TYPE_ISEQ, miseq, noex);
rb_add_method(target, id, VM_METHOD_TYPE_ISEQ, miseq, noex);
if (!is_singleton && noex == NOEX_MODFUNC) {
rb_add_method(rb_singleton_class(klass), id, VM_METHOD_TYPE_ISEQ, miseq, NOEX_PUBLIC);
......
} while (0)
static VALUE
m_core_define_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval)
m_core_define_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval, VALUE nested)
{
REWIND_CFP({
vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, 0, rb_vm_cref());
vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, nested, 0, rb_vm_cref());
});
return Qnil;
}
......
m_core_define_singleton_method(VALUE self, VALUE cbase, VALUE sym, VALUE iseqval)
{
REWIND_CFP({
vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, 1, rb_vm_cref());
vm_define_method(GET_THREAD(), cbase, SYM2ID(sym), iseqval, Qfalse, 1, rb_vm_cref());
});
return Qnil;
}
......
rb_define_method_id(klass, id_core_set_method_alias, m_core_set_method_alias, 3);
rb_define_method_id(klass, id_core_set_variable_alias, m_core_set_variable_alias, 2);
rb_define_method_id(klass, id_core_undef_method, m_core_undef_method, 2);
rb_define_method_id(klass, id_core_define_method, m_core_define_method, 3);
rb_define_method_id(klass, id_core_define_method, m_core_define_method, 4);
rb_define_method_id(klass, id_core_define_singleton_method, m_core_define_singleton_method, 3);
rb_define_method_id(klass, id_core_set_postexe, m_core_set_postexe, 1);
rb_obj_freeze(fcore);
vm_core.h
} \
} while (0)
void rb_overlay_module(NODE*, VALUE, VALUE);
#if defined __GNUC__ && __GNUC__ >= 4
#pragma GCC visibility push(default)
#endif
vm_method.c
}
if (!RTEST(rb_class_inherited_p(klass, me->klass))) {
VALUE mod = rb_module_new();
rb_overlay_module(cref, klass, mod);
klass = mod;
if (FL_TEST(me->klass, RMODULE_HAS_NESTED_METHODS)) {
klass = me->klass;
}
else {
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);
(4-4/4)