diff --git a/vm_method.c b/vm_method.c index cea26cdc3184ae..869f9b673a890e 100644 --- a/vm_method.c +++ b/vm_method.c @@ -6,22 +6,58 @@ #define CACHE_MASK 0x7ff #define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK) -static void rb_vm_check_redefinition_opt_method(const rb_method_entry_t *me); +#define ruby_running (GET_VM()->running) + +/*****************************************************************************/ +/* RENAME SECTION: change names initially only for this file */ +/* TD: apply renames within method.h and source-code-wide, then remove those + defines (or keep as a rename-legend) */ + +#define mdef_t rb_method_definition_t +#define ment_t rb_method_entry_t +#define mtyp_t rb_method_type_t +#define mflg_t rb_method_flag_t + +#define ment_sweep rb_sweep_method_entry +#define ment_free rb_free_method_entry +#define ment_eq rb_method_entry_eq + +#define class_method_make rb_ment_make +#define class_method_add rb_add_method +#define class_method_add_cfunc rb_add_method_cfunc +#define class_method_copy rb_method_entry_set + +#define class_method_lookup rb_method_entry +#define class_method_lookup_uncached rb_method_entry_get_without_cache +#define class_ment_lookup search_method + +#define class_allocator_define rb_define_alloc_func +#define class_allocator_undef rb_undef_alloc_func +#define class_allocator_get rb_get_alloc_func + +#define unlinked_ment_entry unlinked_method_entry_list_entry + +/* END RENAME SECTION */ +/*****************************************************************************/ + +static void rb_vm_check_redefinition_opt_method(const ment_t *me); static ID object_id, respond_to_missing; static ID removed, singleton_removed, undefined, singleton_undefined; static ID added, singleton_added, attached; +/*****************************************************************************/ +/* METHOD ENTRY CACHE */ +/*****************************************************************************/ + struct cache_entry { /* method hash table. */ VALUE filled_version; /* filled state version */ ID mid; /* method's id */ VALUE klass; /* receiver's class */ - rb_method_entry_t *me; + ment_t *me; }; static struct cache_entry cache[CACHE_SIZE]; -#define ruby_running (GET_VM()->running) -/* int ruby_running = 0; */ static void vm_clear_global_method_cache(void) @@ -43,13 +79,13 @@ rb_clear_cache(void) } static void -rb_clear_cache_for_undef(VALUE klass, ID id) +rb_clear_cache_for_undef(VALUE klass, ID mid) { rb_vm_change_state(); } static void -rb_clear_cache_by_id(ID id) +rb_clear_cache_by_id(ID mid) { rb_vm_change_state(); } @@ -60,6 +96,10 @@ rb_clear_cache_by_class(VALUE klass) rb_vm_change_state(); } +/*****************************************************************************/ +/* SPECIAL METHODS */ +/*****************************************************************************/ + VALUE rb_f_notimplement(int argc, VALUE *argv, VALUE obj) { @@ -67,40 +107,213 @@ rb_f_notimplement(int argc, VALUE *argv, VALUE obj) } static void -rb_define_notimplement_method_id(VALUE mod, ID id, rb_method_flag_t noex) +rb_define_notimplement_method_id(VALUE mod, ID mid, mflg_t noex) { - rb_add_method(mod, id, VM_METHOD_TYPE_NOTIMPLEMENTED, 0, noex); + class_method_add(mod, mid, VM_METHOD_TYPE_NOTIMPLEMENTED, 0, noex); } +/*****************************************************************************/ +/* ALLOCATOR FUNCTIONS */ +/*****************************************************************************/ + void -rb_add_method_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, rb_method_flag_t noex) +class_allocator_define(VALUE klass, VALUE (*func)(VALUE)) { - if (func != rb_f_notimplement) { - rb_method_cfunc_t opt; - opt.func = func; - opt.argc = argc; - rb_add_method(klass, mid, VM_METHOD_TYPE_CFUNC, &opt, noex); +/* defines the allocation method for a class */ + + Check_Type(klass, T_CLASS); + class_method_add_cfunc(rb_singleton_class(klass), ID_ALLOCATOR, + func, 0, NOEX_PRIVATE); +} + +void +class_allocator_undef(VALUE klass) +{ +/* un-defines the allocation method for a class */ + + Check_Type(klass, T_CLASS); + class_method_add(rb_singleton_class(klass), ID_ALLOCATOR, VM_METHOD_TYPE_UNDEF, 0, NOEX_UNDEF); +} + +rb_alloc_func_t +class_allocator_get(VALUE klass) +{ +/* returns the allocation method for a class */ + + ment_t *me; + Check_Type(klass, T_CLASS); + me = class_method_lookup(CLASS_OF(klass), ID_ALLOCATOR); + + if (me && me->def && me->def->type == VM_METHOD_TYPE_CFUNC) { + return (rb_alloc_func_t)me->def->body.cfunc.func; } else { - rb_define_notimplement_method_id(klass, mid, noex); + return 0; } } +static inline ID +class_allocator_deprication(VALUE klass, ID mid, mtyp_t type) +{ +/* checks for definition of allocate, returns altered mid after warning */ + + if (FL_TEST(klass, FL_SINGLETON) && + type == VM_METHOD_TYPE_CFUNC && + mid == rb_intern("allocate")) { + /* issue: use rb_warning to honor -v */ + rb_warn("defining %s.allocate is deprecated; use class_allocator_define()", + rb_class2name(rb_ivar_get(klass, attached))); + mid = ID_ALLOCATOR; + } + + return mid; +} + +/*****************************************************************************/ +/* MDEF - METHOD DEFINITION */ +/*****************************************************************************/ + static void -rb_unlink_method_entry(rb_method_entry_t *me) +mdef_init_as_attr(mdef_t *def, void *opts) { - struct unlinked_method_entry_list_entry *ume = ALLOC(struct unlinked_method_entry_list_entry); +/* processing for mdef_new, method type ATTRSET/IVAR */ + + rb_thread_t *th; + rb_control_frame_t *cfp; + int line; + + def->body.attr.id = (ID)opts; + def->body.attr.location = Qfalse; + th = GET_THREAD(); + cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp); + if (cfp && (line = rb_vm_get_sourceline(cfp))) { + VALUE location = rb_ary_new3(2, cfp->iseq->filename, INT2FIX(line)); + def->body.attr.location = rb_ary_freeze(location); + } +} + +mdef_t * +mdef_new(ID mid, mtyp_t type, void *opts) +{ +/* creates a new mdef object (struct)*/ + + mdef_t *def = ALLOC(mdef_t); + def->type = type; + def->original_id = mid; + def->alias_count = 0; + + switch (type) { + case VM_METHOD_TYPE_ISEQ: + def->body.iseq = (rb_iseq_t *)opts; + break; + case VM_METHOD_TYPE_CFUNC: + def->body.cfunc = *(rb_method_cfunc_t *)opts; + break; + case VM_METHOD_TYPE_ATTRSET: + case VM_METHOD_TYPE_IVAR: + mdef_init_as_attr(def, opts); + break; + case VM_METHOD_TYPE_BMETHOD: + def->body.proc = (VALUE)opts; + break; + case VM_METHOD_TYPE_NOTIMPLEMENTED: + def->body.cfunc.func = rb_f_notimplement; + def->body.cfunc.argc = -1; + break; + case VM_METHOD_TYPE_OPTIMIZED: + def->body.optimize_type = (enum method_optimized_type)opts; + break; + case VM_METHOD_TYPE_ZSUPER: + case VM_METHOD_TYPE_UNDEF: + break; + default: + rb_bug("mdef_new: unsupported method type (%d)\n", type); + } + return def; +} + +static int +mdef_eq(const mdef_t *d1, const mdef_t *d2) +{ +/* determines if two mdef are equal */ + + if (d1 == d2) return 1; + if (!d1 || !d2) return 0; + if (d1->type != d2->type) { + return 0; + } + switch (d1->type) { + case VM_METHOD_TYPE_ISEQ: + return d1->body.iseq == d2->body.iseq; + case VM_METHOD_TYPE_CFUNC: + return + d1->body.cfunc.func == d2->body.cfunc.func && + d1->body.cfunc.argc == d2->body.cfunc.argc; + case VM_METHOD_TYPE_ATTRSET: + case VM_METHOD_TYPE_IVAR: + return d1->body.attr.id == d2->body.attr.id; + case VM_METHOD_TYPE_BMETHOD: + return RTEST(rb_equal(d1->body.proc, d2->body.proc)); + case VM_METHOD_TYPE_MISSING: + return d1->original_id == d2->original_id; + case VM_METHOD_TYPE_ZSUPER: + case VM_METHOD_TYPE_NOTIMPLEMENTED: + case VM_METHOD_TYPE_UNDEF: + return 1; + case VM_METHOD_TYPE_OPTIMIZED: + return d1->body.optimize_type == d2->body.optimize_type; + default: + rb_bug("rb_method_entry_eq: unsupported method type (%d)\n", d1->type); + return 0; + } +} + +/*****************************************************************************/ +/* MENT - METHOD ENTRY */ +/*****************************************************************************/ + +static ment_t * +ment_new(ID mid, mdef_t *def, mflg_t noex) +{ +/* creates a new ment object (struct) */ + +/* issue: avoid passing "mid", as it exists already within mdef */ +/* issue: clarify usage of "mid", always me->called_id == mdef->original_id? */ + + ment_t *me = ALLOC(ment_t); + + me->flag = NOEX_WITH_SAFE(noex); + me->mark = 0; + me->called_id = mid; + me->klass = 0; /* not yet assigned to a class */ + me->def = def; + if (def) def->alias_count++; + + return me; +} + +static void +ment_unlink(ment_t *me) +{ +/* places an unused ment into the unlinked-list */ +/* TD: verify, possibly rename to "unused_ment_list" */ + + struct unlinked_ment_entry *ume; + ume = ALLOC(struct unlinked_ment_entry); ume->me = me; ume->next = GET_VM()->unlinked_method_entry_list; GET_VM()->unlinked_method_entry_list = ume; } void -rb_sweep_method_entry(void *pvm) +ment_sweep(void *pvm) { +/* frees (deletes permanently) all unused(unlinked) ment */ + rb_vm_t *vm = pvm; - struct unlinked_method_entry_list_entry *ume = vm->unlinked_method_entry_list, *prev_ume = 0, *curr_ume; + struct unlinked_ment_entry *ume = vm->unlinked_method_entry_list, *prev_ume = 0, *curr_ume; + /* TD: document, possibly refactor */ while (ume) { if (ume->me->mark) { ume->me->mark = 0; @@ -108,7 +321,7 @@ rb_sweep_method_entry(void *pvm) ume = ume->next; } else { - rb_free_method_entry(ume->me); + ment_free(ume->me); if (prev_ume == 0) { vm->unlinked_method_entry_list = ume->next; @@ -125,9 +338,11 @@ rb_sweep_method_entry(void *pvm) } void -rb_free_method_entry(rb_method_entry_t *me) +ment_free(ment_t *me) { - rb_method_definition_t *def = me->def; +/* frees the memory of a method entry, deleting it permanently */ + + mdef_t *def = me->def; if (def) { if (def->alias_count == 0) { @@ -141,104 +356,44 @@ rb_free_method_entry(rb_method_entry_t *me) xfree(me); } -static int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2); - -static rb_method_entry_t * -rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, - rb_method_definition_t *def, rb_method_flag_t noex) +int +ment_eq(const ment_t *m1, const ment_t *m2) { - rb_method_entry_t *me; - st_table *mtbl; - st_data_t data; +/* determine if two ment are equal */ - if (NIL_P(klass)) { - klass = rb_cObject; - } - if (rb_safe_level() >= 4 && - (klass == rb_cObject || !OBJ_UNTRUSTED(klass))) { - rb_raise(rb_eSecurityError, "Insecure: can't define method"); - } - if (!FL_TEST(klass, FL_SINGLETON) && - type != VM_METHOD_TYPE_NOTIMPLEMENTED && - type != VM_METHOD_TYPE_ZSUPER && - (mid == rb_intern("initialize") || mid == rb_intern("initialize_copy"))) { - noex = NOEX_PRIVATE | noex; - } - else if (FL_TEST(klass, FL_SINGLETON) && - type == VM_METHOD_TYPE_CFUNC && - mid == rb_intern("allocate")) { - rb_warn("defining %s.allocate is deprecated; use rb_define_alloc_func()", - rb_class2name(rb_ivar_get(klass, attached))); - mid = ID_ALLOCATOR; - } + return mdef_eq(m1->def, m2->def); +} - rb_check_frozen(klass); - mtbl = RCLASS_M_TBL(klass); - - /* check re-definition */ - if (st_lookup(mtbl, mid, &data)) { - rb_method_entry_t *old_me = (rb_method_entry_t *)data; - rb_method_definition_t *old_def = old_me->def; - - if (rb_method_definition_eq(old_def, def)) return old_me; - rb_vm_check_redefinition_opt_method(old_me); - - if (RTEST(ruby_verbose) && - type != VM_METHOD_TYPE_UNDEF && - old_def->alias_count == 0 && - old_def->type != VM_METHOD_TYPE_UNDEF && - old_def->type != VM_METHOD_TYPE_ZSUPER) { - rb_iseq_t *iseq = 0; - - rb_warning("method redefined; discarding old %s", rb_id2name(mid)); - switch (old_def->type) { - case VM_METHOD_TYPE_ISEQ: - iseq = old_def->body.iseq; - break; - case VM_METHOD_TYPE_BMETHOD: - iseq = rb_proc_get_iseq(old_def->body.proc, 0); - break; - default: - break; - } - if (iseq && !NIL_P(iseq->filename)) { - int line = iseq->insn_info_table ? rb_iseq_first_lineno(iseq) : 0; - rb_compile_warning(RSTRING_PTR(iseq->filename), line, - "previous definition of %s was here", - rb_id2name(old_def->original_id)); - } - } +static int +ment_has_mdef(ment_t *me, mdef_t *def) +{ +/* tests if ment has the given mdef */ - rb_unlink_method_entry(old_me); - } + if (!me) return FALSE; + + return mdef_eq(me->def, def) ? TRUE : FALSE; +} - me = ALLOC(rb_method_entry_t); +/*****************************************************************************/ +/* METHOD DEFINITION AND ENTRY CREATION */ +/*****************************************************************************/ - rb_clear_cache_by_id(mid); +#define VISI_CHECK(x,f) (((x)&NOEX_MASK) == (f)) +/* TD: verify, macro seems redundant. code directly. */ - me->flag = NOEX_WITH_SAFE(noex); - me->mark = 0; - me->called_id = mid; - me->klass = klass; - me->def = def; - if (def) def->alias_count++; +static VALUE +class_ment_flagtest(VALUE klass, ID mid, mflg_t noex) +{ +/* ??? tests the flag of a modules ment */ - /* check mid */ - if (klass == rb_cObject && mid == idInitialize) { - rb_warn("redefining Object#initialize may cause infinite loop"); - } - /* check mid */ - if (mid == object_id || mid == id__send__) { - if (type == VM_METHOD_TYPE_ISEQ) { - rb_warn("redefining `%s' may cause serious problems", rb_id2name(mid)); - } + const ment_t *me = class_method_lookup(klass, mid); + if (me && VISI_CHECK(me->flag, noex)) { + return Qtrue; } - - st_insert(mtbl, mid, (st_data_t) me); - - return me; + return Qfalse; } +/* TD: refactor to code, leave only "hook_id = singleton_##hook;" as a macro */ #define CALL_METHOD_HOOK(klass, hook, mid) do { \ const VALUE arg = ID2SYM(mid); \ VALUE recv_class = (klass); \ @@ -250,167 +405,305 @@ rb_method_entry_make(VALUE klass, ID mid, rb_method_type_t type, rb_funcall2(recv_class, hook_id, 1, &arg); \ } while (0) -static void -method_added(VALUE klass, ID mid) +static ment_t * +class_ment_get(VALUE klass, ID mid) { - if (mid != ID_ALLOCATOR && ruby_running) { - CALL_METHOD_HOOK(klass, added, mid); - } +/* gets the ment by mid. Does *not* do a lookup in the class hierarchy */ + + ment_t *me = 0; + st_lookup(RCLASS_M_TBL(klass), mid, (st_data_t*)me); + return me; } -rb_method_entry_t * -rb_add_method(VALUE klass, ID mid, rb_method_type_t type, void *opts, rb_method_flag_t noex) +static ment_t* +class_ment_lookup(VALUE klass, ID mid) { - rb_thread_t *th; - rb_control_frame_t *cfp; - int line; - rb_method_entry_t *me = rb_method_entry_make(klass, mid, type, 0, noex); - rb_method_definition_t *def = ALLOC(rb_method_definition_t); - me->def = def; - def->type = type; - def->original_id = mid; - def->alias_count = 0; - switch (type) { - case VM_METHOD_TYPE_ISEQ: - def->body.iseq = (rb_iseq_t *)opts; - break; - case VM_METHOD_TYPE_CFUNC: - def->body.cfunc = *(rb_method_cfunc_t *)opts; - break; - case VM_METHOD_TYPE_ATTRSET: - case VM_METHOD_TYPE_IVAR: - def->body.attr.id = (ID)opts; - def->body.attr.location = Qfalse; - th = GET_THREAD(); - cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp); - if (cfp && (line = rb_vm_get_sourceline(cfp))) { - VALUE location = rb_ary_new3(2, cfp->iseq->filename, INT2FIX(line)); - def->body.attr.location = rb_ary_freeze(location); +/* low level internal function, does a method lookup within the class's + inheritance chain */ +/* issue: st_lookup does *not* a lookup, possibly rename to "get") */ + + st_data_t body; + if (!klass) { + return 0; + } + /* TD: refactor to while(klass), remove above if */ + while (!st_lookup(RCLASS_M_TBL(klass), mid, &body)) { + klass = RCLASS_SUPER(klass); + if (!klass) { + return 0; } - break; - case VM_METHOD_TYPE_BMETHOD: - def->body.proc = (VALUE)opts; - break; - case VM_METHOD_TYPE_NOTIMPLEMENTED: - def->body.cfunc.func = rb_f_notimplement; - def->body.cfunc.argc = -1; - break; - case VM_METHOD_TYPE_OPTIMIZED: - def->body.optimize_type = (enum method_optimized_type)opts; - break; - case VM_METHOD_TYPE_ZSUPER: - case VM_METHOD_TYPE_UNDEF: - break; - default: - rb_bug("rb_add_method: unsupported method type (%d)\n", type); } - if (type != VM_METHOD_TYPE_UNDEF) { - method_added(klass, mid); + + return (ment_t *)body; +} + +/* + * search method entry without the method cache. + * + * if you need method entry with method cache (normal case), use + * class_method_lookup() simply. + */ +ment_t * +class_method_lookup_uncached(VALUE klass, ID mid) +{ + ment_t *me = class_ment_lookup(klass, mid); + +/* TD: document this code */ + if (ruby_running) { + struct cache_entry *ent; + ent = cache + EXPR1(klass, mid); + ent->filled_version = GET_VM_STATE_VERSION(); + ent->klass = klass; + + if (UNDEFINED_METHOD_ENTRY_P(me)) { + ent->mid = mid; + ent->me = 0; + me = 0; + } + else { + ent->mid = mid; + ent->me = me; + } } + return me; } -rb_method_entry_t * -rb_method_entry_set(VALUE klass, ID mid, const rb_method_entry_t *me, rb_method_flag_t noex) +ment_t * +class_method_lookup(VALUE klass, ID mid) { - rb_method_type_t type = me->def ? me->def->type : VM_METHOD_TYPE_UNDEF; - rb_method_entry_t *newme = rb_method_entry_make(klass, mid, type, me->def, noex); - method_added(klass, mid); - return newme; +/* the high level method lookup function, uses method lookup cache to retrieve + the ment from a given class. */ + + struct cache_entry *ent; + + ent = cache + EXPR1(klass, mid); + if (ent->filled_version == GET_VM_STATE_VERSION() && + ent->mid == mid && ent->klass == klass) { + return ent->me; + } + + return class_method_lookup_uncached(klass, mid); } -void -rb_define_alloc_func(VALUE klass, VALUE (*func)(VALUE)) +static ment_t * +class_ment_add(VALUE klass, ment_t *me) { - Check_Type(klass, T_CLASS); - rb_add_method_cfunc(rb_singleton_class(klass), ID_ALLOCATOR, - func, 0, NOEX_PRIVATE); +/* adds a ment to a class, without check if it's already exists */ + + mtyp_t type = me->def->type; + ID mid = me->called_id; + + /* set initialize or initialize_copy to private */ + if (!FL_TEST(klass, FL_SINGLETON) && + type != VM_METHOD_TYPE_NOTIMPLEMENTED && + type != VM_METHOD_TYPE_ZSUPER && + (mid == rb_intern("initialize") || mid == rb_intern("initialize_copy"))) { + me->flag = NOEX_PRIVATE | me->flag; + } + + rb_clear_cache_by_id(mid); + + me->klass = klass; + st_insert(RCLASS_M_TBL(klass), mid, (st_data_t) me); + + if (type != VM_METHOD_TYPE_UNDEF && mid != ID_ALLOCATOR && ruby_running) { + CALL_METHOD_HOOK(klass, added, mid); + } + return me; } -void -rb_undef_alloc_func(VALUE klass) +static ment_t * +class_mdef_add(VALUE klass, ID mid, mdef_t *def, mflg_t noex ) { - Check_Type(klass, T_CLASS); - rb_add_method(rb_singleton_class(klass), ID_ALLOCATOR, VM_METHOD_TYPE_UNDEF, 0, NOEX_UNDEF); +/* adds a mdef to a class, without check if it's already exists */ + + return class_ment_add(klass, ment_new(mid, def, noex)); } -rb_alloc_func_t -rb_get_alloc_func(VALUE klass) +static ment_t * +class_method_redefine(VALUE klass, ID mid, mtyp_t type, mdef_t *def, mflg_t noex) { - rb_method_entry_t *me; - Check_Type(klass, T_CLASS); - me = rb_method_entry(CLASS_OF(klass), ID_ALLOCATOR); +/* processing subjecting method redefinition */ + + ment_t *old_me = class_ment_get(klass, mid); + mdef_t *old_def = old_me->def; + + rb_vm_check_redefinition_opt_method(old_me); + + if (RTEST(ruby_verbose) && + type != VM_METHOD_TYPE_UNDEF && + old_def->alias_count == 0 && + old_def->type != VM_METHOD_TYPE_UNDEF && + old_def->type != VM_METHOD_TYPE_ZSUPER) { + rb_iseq_t *iseq = 0; + + rb_warning("method redefined; discarding old %s", rb_id2name(mid)); + switch (old_def->type) { + case VM_METHOD_TYPE_ISEQ: + iseq = old_def->body.iseq; + break; + case VM_METHOD_TYPE_BMETHOD: + iseq = rb_proc_get_iseq(old_def->body.proc, 0); + break; + default: + break; + } + if (iseq && !NIL_P(iseq->filename)) { + int line = iseq->insn_info_table ? rb_iseq_first_lineno(iseq) : 0; + rb_compile_warning(RSTRING_PTR(iseq->filename), line, + "previous definition of %s was here", + rb_id2name(old_def->original_id)); + } + } - if (me && me->def && me->def->type == VM_METHOD_TYPE_CFUNC) { - return (rb_alloc_func_t)me->def->body.cfunc.func; + if (klass == rb_cObject && mid == idInitialize) { + /* issue: use rb_warning to honor -v */ + rb_warn("redefining Object#initialize may cause infinite loop"); } - else { - return 0; + + if (mid == object_id || mid == id__send__) { + if (type == VM_METHOD_TYPE_ISEQ) { + /* issue: use rb_warning to honor -v */ + rb_warn("redefining `%s' may cause serious problems", rb_id2name(mid)); + } } + + ment_unlink(old_me); + + return class_mdef_add(klass, mid, def, noex); + } -static rb_method_entry_t* -search_method(VALUE klass, ID id) +static ment_t * +class_method_make(VALUE klass, ID mid, mtyp_t type, mdef_t *def, mflg_t noex) { - st_data_t body; - if (!klass) { - return 0; +/* retrieves the ment for the given mdef from the klass + creates new ment if not available */ + + ment_t *me, *old_me; + + if (NIL_P(klass)) { + klass = rb_cObject; } - while (!st_lookup(RCLASS_M_TBL(klass), id, &body)) { - klass = RCLASS_SUPER(klass); - if (!klass) { - return 0; - } + mid = class_allocator_deprication(klass, mid, type); + + /* issue: possibly after "return old_me", as frozen has no relevance if + existent ment is returned */ + rb_check_frozen(klass); + + old_me = class_ment_get(klass, mid); + if (old_me && ment_has_mdef(old_me, def)) + return old_me; + + /* definition or redefinition */ + + if (rb_safe_level() >= 4 && + (klass == rb_cObject || !OBJ_UNTRUSTED(klass))) { + rb_raise(rb_eSecurityError, "Insecure: can't define method"); + } + + if (old_me) + return class_method_redefine(klass, mid, type, def, noex); + + return class_mdef_add(klass, mid, def, noex); +} + +ment_t * +class_method_add(VALUE klass, ID mid, mtyp_t type, void *opts, mflg_t noex) +{ +/* adds a newly created mdef via a newly created me to a class */ + + mdef_t *def = mdef_new(mid, type, opts); + ment_t *me = class_method_make(klass, mid, type, def, noex); + return me; +} + +ment_t * +class_method_copy(VALUE klass, ID mid, const ment_t *me, mflg_t noex) +{ +/* adds a ment to a class, by copying the given me->def */ + +/* TD: possibly move setting of "VM_METHOD_TYPE_UNDEF" int _ment_make */ + + mtyp_t type = me->def ? me->def->type : VM_METHOD_TYPE_UNDEF; + ment_t *newme = class_method_make(klass, mid, type, me->def, noex); + return newme; +} + +void +class_method_add_cfunc(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc, mflg_t noex) +{ +/* specialized version of class_method_add - for C functions */ + +/* issue: should possibly return me */ + +/* TD: notimplemented logic belongs possibly in class_method_add or mdef_new, + new function "ment_init_notimplemented" */ + + if (func != rb_f_notimplement) { + rb_method_cfunc_t opt; + opt.func = func; + opt.argc = argc; + class_method_add(klass, mid, VM_METHOD_TYPE_CFUNC, &opt, noex); + } + else { + rb_define_notimplement_method_id(klass, mid, noex); } - - return (rb_method_entry_t *)body; } -/* - * search method entry without the method cache. - * - * if you need method entry with method cache (normal case), use - * rb_method_entry() simply. - */ -rb_method_entry_t * -rb_method_entry_get_without_cache(VALUE klass, ID id) -{ - rb_method_entry_t *me = search_method(klass, id); +/*****************************************************************************/ +/* RUBY LEVEL METHODS */ +/*****************************************************************************/ - if (ruby_running) { - struct cache_entry *ent; - ent = cache + EXPR1(klass, id); - ent->filled_version = GET_VM_STATE_VERSION(); - ent->klass = klass; +void +rb_attr(VALUE klass, ID mid, int read, int write, int ex) +{ + const char *name; + ID attriv; + VALUE aname; + mflg_t noex; - if (UNDEFINED_METHOD_ENTRY_P(me)) { - ent->mid = id; - ent->me = 0; - me = 0; + if (!ex) { + noex = NOEX_PUBLIC; + } + else { + if (SCOPE_TEST(NOEX_PRIVATE)) { + noex = NOEX_PRIVATE; + rb_warning((SCOPE_CHECK(NOEX_MODFUNC)) ? + "attribute accessor as module_function" : + "private attribute?"); + } + else if (SCOPE_TEST(NOEX_PROTECTED)) { + noex = NOEX_PROTECTED; } else { - ent->mid = id; - ent->me = me; + noex = NOEX_PUBLIC; } } - return me; + if (!rb_is_local_id(mid) && !rb_is_const_id(mid)) { + rb_name_error(mid, "invalid attribute name `%s'", rb_id2name(mid)); + } + name = rb_id2name(mid); + if (!name) { + rb_raise(rb_eArgError, "argument needs to be symbol or string"); + } + aname = rb_sprintf("@%s", name); + rb_enc_copy(aname, rb_id2str(mid)); + attriv = rb_intern_str(aname); + if (read) { + class_method_add(klass, mid, VM_METHOD_TYPE_IVAR, (void *)attriv, noex); + } + if (write) { + class_method_add(klass, rb_id_attrset(mid), VM_METHOD_TYPE_ATTRSET, (void *)attriv, noex); + } } -rb_method_entry_t * -rb_method_entry(VALUE klass, ID id) -{ - struct cache_entry *ent; - - ent = cache + EXPR1(klass, id); - if (ent->filled_version == GET_VM_STATE_VERSION() && - ent->mid == id && ent->klass == klass) { - return ent->me; - } +/* rb_attr called from class.c */ - return rb_method_entry_get_without_cache(klass, id); -} +/*----------*/ static void remove_method(VALUE klass, ID mid) @@ -426,11 +719,12 @@ remove_method(VALUE klass, ID mid) } rb_check_frozen(klass); if (mid == object_id || mid == id__send__ || mid == idInitialize) { + /* issue: use rb_warning to honor -v */ rb_warn("removing `%s' may cause serious problems", rb_id2name(mid)); } if (!st_lookup(RCLASS_M_TBL(klass), mid, &data) || - !(me = (rb_method_entry_t *)data) || + !(me = (ment_t *)data) || (!me->def || me->def->type == VM_METHOD_TYPE_UNDEF)) { rb_name_error(mid, "method `%s' not defined in %s", rb_id2name(mid), rb_class2name(klass)); @@ -440,7 +734,7 @@ remove_method(VALUE klass, ID mid) rb_vm_check_redefinition_opt_method(me); rb_clear_cache_for_undef(klass, mid); - rb_unlink_method_entry(me); + ment_unlink(me); CALL_METHOD_HOOK(klass, removed, mid); } @@ -476,118 +770,12 @@ rb_mod_remove_method(int argc, VALUE *argv, VALUE mod) return mod; } -#undef rb_disable_super -#undef rb_enable_super - -void -rb_disable_super(VALUE klass, const char *name) -{ - /* obsolete - no use */ -} - -void -rb_enable_super(VALUE klass, const char *name) -{ - rb_warning("rb_enable_super() is obsolete"); -} - -static void -rb_export_method(VALUE klass, ID name, rb_method_flag_t noex) -{ - rb_method_entry_t *me; - - if (klass == rb_cObject) { - rb_secure(4); - } - - me = search_method(klass, name); - if (!me && TYPE(klass) == T_MODULE) { - me = search_method(rb_cObject, name); - } - - if (UNDEFINED_METHOD_ENTRY_P(me)) { - rb_print_undef(klass, name, 0); - } - - if (me->flag != noex) { - rb_vm_check_redefinition_opt_method(me); - - if (klass == me->klass) { - me->flag = noex; - } - else { - rb_add_method(klass, name, VM_METHOD_TYPE_ZSUPER, 0, noex); - } - } -} - -int -rb_method_boundp(VALUE klass, ID id, int ex) -{ - rb_method_entry_t *me = rb_method_entry(klass, id); - - if (me != 0) { - if ((ex & ~NOEX_RESPONDS) && (me->flag & NOEX_PRIVATE)) { - return FALSE; - } - if (!me->def) return 0; - if (me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) { - if (ex & NOEX_RESPONDS) return 2; - return 0; - } - return 1; - } - return 0; -} - -void -rb_attr(VALUE klass, ID id, int read, int write, int ex) -{ - const char *name; - ID attriv; - VALUE aname; - rb_method_flag_t noex; - - if (!ex) { - noex = NOEX_PUBLIC; - } - else { - if (SCOPE_TEST(NOEX_PRIVATE)) { - noex = NOEX_PRIVATE; - rb_warning((SCOPE_CHECK(NOEX_MODFUNC)) ? - "attribute accessor as module_function" : - "private attribute?"); - } - else if (SCOPE_TEST(NOEX_PROTECTED)) { - noex = NOEX_PROTECTED; - } - else { - noex = NOEX_PUBLIC; - } - } - - if (!rb_is_local_id(id) && !rb_is_const_id(id)) { - rb_name_error(id, "invalid attribute name `%s'", rb_id2name(id)); - } - name = rb_id2name(id); - if (!name) { - rb_raise(rb_eArgError, "argument needs to be symbol or string"); - } - aname = rb_sprintf("@%s", name); - rb_enc_copy(aname, rb_id2str(id)); - attriv = rb_intern_str(aname); - if (read) { - rb_add_method(klass, id, VM_METHOD_TYPE_IVAR, (void *)attriv, noex); - } - if (write) { - rb_add_method(klass, rb_id_attrset(id), VM_METHOD_TYPE_ATTRSET, (void *)attriv, noex); - } -} +/*----------*/ void -rb_undef(VALUE klass, ID id) +rb_undef(VALUE klass, ID mid) { - rb_method_entry_t *me; + ment_t *me; if (NIL_P(klass)) { rb_raise(rb_eTypeError, "no class to undef method"); @@ -596,14 +784,14 @@ rb_undef(VALUE klass, ID id) rb_secure(4); } if (rb_safe_level() >= 4 && !OBJ_UNTRUSTED(klass)) { - rb_raise(rb_eSecurityError, "Insecure: can't undef `%s'", rb_id2name(id)); + rb_raise(rb_eSecurityError, "Insecure: can't undef `%s'", rb_id2name(mid)); } rb_frozen_class_p(klass); - if (id == object_id || id == id__send__ || id == idInitialize) { - rb_warn("undefining `%s' may cause serious problems", rb_id2name(id)); + if (mid == object_id || mid == id__send__ || mid == idInitialize) { + rb_warn("undefining `%s' may cause serious problems", rb_id2name(mid)); } - me = search_method(klass, id); + me = class_ment_lookup(klass, mid); if (UNDEFINED_METHOD_ENTRY_P(me)) { const char *s0 = " class"; @@ -622,13 +810,13 @@ rb_undef(VALUE klass, ID id) else if (TYPE(c) == T_MODULE) { s0 = " module"; } - rb_name_error(id, "undefined method `%s' for%s `%s'", - rb_id2name(id), s0, rb_class2name(c)); + rb_name_error(mid, "undefined method `%s' for%s `%s'", + rb_id2name(mid), s0, rb_class2name(c)); } - rb_add_method(klass, id, VM_METHOD_TYPE_UNDEF, 0, NOEX_PUBLIC); + class_method_add(klass, mid, VM_METHOD_TYPE_UNDEF, 0, NOEX_PUBLIC); - CALL_METHOD_HOOK(klass, undefined, id); + CALL_METHOD_HOOK(klass, undefined, mid); } /* @@ -684,6 +872,27 @@ rb_mod_undef_method(int argc, VALUE *argv, VALUE mod) return mod; } +/*----------*/ + +int +rb_method_boundp(VALUE klass, ID mid, int ex) +{ + ment_t *me = class_method_lookup(klass, mid); + + if (me != 0) { + if ((ex & ~NOEX_RESPONDS) && (me->flag & NOEX_PRIVATE)) { + return FALSE; + } + if (!me->def) return 0; + if (me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) { + if (ex & NOEX_RESPONDS) return 2; + return 0; + } + return 1; + } + return 0; +} + /* * call-seq: * mod.method_defined?(symbol) -> true or false @@ -721,20 +930,6 @@ rb_mod_method_defined(VALUE mod, VALUE mid) } -#define VISI_CHECK(x,f) (((x)&NOEX_MASK) == (f)) - -static VALUE -check_definition(VALUE mod, ID mid, rb_method_flag_t noex) -{ - const rb_method_entry_t *me; - me = rb_method_entry(mod, mid); - if (me) { - if (VISI_CHECK(me->flag, noex)) - return Qtrue; - } - return Qfalse; -} - /* * call-seq: * mod.public_method_defined?(symbol) -> true or false @@ -766,7 +961,7 @@ rb_mod_public_method_defined(VALUE mod, VALUE mid) { ID id = rb_check_id(mid); if (!id) return Qfalse; - return check_definition(mod, id, NOEX_PUBLIC); + return class_ment_flagtest(mod, id, NOEX_PUBLIC); } /* @@ -800,7 +995,7 @@ rb_mod_private_method_defined(VALUE mod, VALUE mid) { ID id = rb_check_id(mid); if (!id) return Qfalse; - return check_definition(mod, id, NOEX_PRIVATE); + return class_ment_flagtest(mod, id, NOEX_PRIVATE); } /* @@ -834,55 +1029,17 @@ rb_mod_protected_method_defined(VALUE mod, VALUE mid) { ID id = rb_check_id(mid); if (!id) return Qfalse; - return check_definition(mod, id, NOEX_PROTECTED); -} - -int -rb_method_entry_eq(const rb_method_entry_t *m1, const rb_method_entry_t *m2) -{ - return rb_method_definition_eq(m1->def, m2->def); + return class_ment_flagtest(mod, id, NOEX_PROTECTED); } -static int -rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2) -{ - if (d1 == d2) return 1; - if (!d1 || !d2) return 0; - if (d1->type != d2->type) { - return 0; - } - switch (d1->type) { - case VM_METHOD_TYPE_ISEQ: - return d1->body.iseq == d2->body.iseq; - case VM_METHOD_TYPE_CFUNC: - return - d1->body.cfunc.func == d2->body.cfunc.func && - d1->body.cfunc.argc == d2->body.cfunc.argc; - case VM_METHOD_TYPE_ATTRSET: - case VM_METHOD_TYPE_IVAR: - return d1->body.attr.id == d2->body.attr.id; - case VM_METHOD_TYPE_BMETHOD: - return RTEST(rb_equal(d1->body.proc, d2->body.proc)); - case VM_METHOD_TYPE_MISSING: - return d1->original_id == d2->original_id; - case VM_METHOD_TYPE_ZSUPER: - case VM_METHOD_TYPE_NOTIMPLEMENTED: - case VM_METHOD_TYPE_UNDEF: - return 1; - case VM_METHOD_TYPE_OPTIMIZED: - return d1->body.optimize_type == d2->body.optimize_type; - default: - rb_bug("rb_method_entry_eq: unsupported method type (%d)\n", d1->type); - return 0; - } -} +/*----------*/ void -rb_alias(VALUE klass, ID name, ID def) +rb_alias(VALUE klass, ID mid_alias, ID mid) { VALUE target_klass = klass; - rb_method_entry_t *orig_me; - rb_method_flag_t flag = NOEX_UNDEF; + ment_t *orig_me; + mflg_t flag = NOEX_UNDEF; if (NIL_P(klass)) { rb_raise(rb_eTypeError, "no class to make alias"); @@ -894,23 +1051,23 @@ rb_alias(VALUE klass, ID name, ID def) } again: - orig_me = search_method(klass, def); + orig_me = class_ment_lookup(klass, mid); if (UNDEFINED_METHOD_ENTRY_P(orig_me)) { if ((TYPE(klass) != T_MODULE) || - (orig_me = search_method(rb_cObject, def), UNDEFINED_METHOD_ENTRY_P(orig_me))) { - rb_print_undef(klass, def, 0); + (orig_me = class_ment_lookup(rb_cObject, mid), UNDEFINED_METHOD_ENTRY_P(orig_me))) { + rb_print_undef(klass, mid, 0); } } if (orig_me->def->type == VM_METHOD_TYPE_ZSUPER) { klass = RCLASS_SUPER(klass); - def = orig_me->def->original_id; + mid = orig_me->def->original_id; flag = orig_me->flag; goto again; } if (flag == NOEX_UNDEF) flag = orig_me->flag; - rb_method_entry_set(target_klass, name, orig_me, flag); + class_method_copy(target_klass, mid_alias, orig_me, flag); } /* @@ -942,6 +1099,8 @@ rb_mod_alias_method(VALUE mod, VALUE newname, VALUE oldname) return mod; } +/*----------*/ + static void secure_visibility(VALUE self) { @@ -952,7 +1111,37 @@ secure_visibility(VALUE self) } static void -set_method_visibility(VALUE self, int argc, VALUE *argv, rb_method_flag_t ex) +rb_export_method(VALUE klass, ID mid, mflg_t noex) +{ + ment_t *me; + + if (klass == rb_cObject) { + rb_secure(4); + } + + me = class_ment_lookup(klass, mid); + if (!me && TYPE(klass) == T_MODULE) { + me = class_ment_lookup(rb_cObject, mid); + } + + if (UNDEFINED_METHOD_ENTRY_P(me)) { + rb_print_undef(klass, mid, 0); + } + + if (me->flag != noex) { + rb_vm_check_redefinition_opt_method(me); + + if (klass == me->klass) { + me->flag = noex; + } + else { + class_method_add(klass, mid, VM_METHOD_TYPE_ZSUPER, 0, noex); + } + } +} + +static void +set_method_visibility(VALUE self, int argc, VALUE *argv, mflg_t ex) { int i; secure_visibility(self); @@ -1139,8 +1328,8 @@ static VALUE rb_mod_modfunc(int argc, VALUE *argv, VALUE module) { int i; - ID id; - const rb_method_entry_t *me; + ID mid; + const ment_t *me; if (TYPE(module) != T_MODULE) { rb_raise(rb_eTypeError, "module_function must be called for modules"); @@ -1157,14 +1346,14 @@ rb_mod_modfunc(int argc, VALUE *argv, VALUE module) for (i = 0; i < argc; i++) { VALUE m = module; - id = rb_to_id(argv[i]); + mid = rb_to_id(argv[i]); for (;;) { - me = search_method(m, id); + me = class_ment_lookup(m, mid); if (me == 0) { - me = search_method(rb_cObject, id); + me = class_ment_lookup(rb_cObject, mid); } if (UNDEFINED_METHOD_ENTRY_P(me)) { - rb_print_undef(module, id, 0); + rb_print_undef(module, mid, 0); } if (me->def->type != VM_METHOD_TYPE_ZSUPER) { break; /* normal case: need not to follow 'super' link */ @@ -1173,52 +1362,54 @@ rb_mod_modfunc(int argc, VALUE *argv, VALUE module) if (!m) break; } - rb_method_entry_set(rb_singleton_class(module), id, me, NOEX_PUBLIC); + class_method_copy(rb_singleton_class(module), mid, me, NOEX_PUBLIC); } return module; } +/*----------*/ + int -rb_method_basic_definition_p(VALUE klass, ID id) +rb_method_basic_definition_p(VALUE klass, ID mid) { - const rb_method_entry_t *me = rb_method_entry(klass, id); + const ment_t *me = class_method_lookup(klass, mid); if (me && (me->flag & NOEX_BASIC)) return 1; return 0; } static inline int -basic_obj_respond_to(VALUE obj, ID id, int pub) +basic_obj_respond_to(VALUE obj, ID mid, int pub) { VALUE klass = CLASS_OF(obj); - switch (rb_method_boundp(klass, id, pub|NOEX_RESPONDS)) { + switch (rb_method_boundp(klass, mid, pub|NOEX_RESPONDS)) { case 2: return FALSE; case 0: - return RTEST(rb_funcall(obj, respond_to_missing, 2, ID2SYM(id), pub ? Qfalse : Qtrue)); + return RTEST(rb_funcall(obj, respond_to_missing, 2, ID2SYM(mid), pub ? Qfalse : Qtrue)); default: return TRUE; } } int -rb_obj_respond_to(VALUE obj, ID id, int priv) +rb_obj_respond_to(VALUE obj, ID mid, int priv) { VALUE klass = CLASS_OF(obj); if (rb_method_basic_definition_p(klass, idRespond_to)) { - return basic_obj_respond_to(obj, id, !RTEST(priv)); + return basic_obj_respond_to(obj, mid, !RTEST(priv)); } else { - return RTEST(rb_funcall(obj, idRespond_to, priv ? 2 : 1, ID2SYM(id), Qtrue)); + return RTEST(rb_funcall(obj, idRespond_to, priv ? 2 : 1, ID2SYM(mid), Qtrue)); } } int -rb_respond_to(VALUE obj, ID id) +rb_respond_to(VALUE obj, ID mid) { - return rb_obj_respond_to(obj, id, FALSE); + return rb_obj_respond_to(obj, mid, FALSE); } @@ -1267,6 +1458,25 @@ obj_respond_to_missing(VALUE obj, VALUE mid, VALUE priv) return Qfalse; } +/*****************************************************************************/ +/* DIVERSE FUNCTIONS */ +/*****************************************************************************/ + +#undef rb_disable_super +#undef rb_enable_super + +void +rb_disable_super(VALUE klass, const char *name) +{ + rb_warning("rb_disable_super() is obsolete"); +} + +void +rb_enable_super(VALUE klass, const char *name) +{ + rb_warning("rb_enable_super() is obsolete"); +} + void Init_eval_method(void) { @@ -1305,3 +1515,23 @@ Init_eval_method(void) respond_to_missing = rb_intern("respond_to_missing?"); } +/* TD: rename plan + +method_entry | mentry | ment | me +method_definition | mdefinition | mdef | md +method_table | mtable | mtbl | mt +method_id | midentifier | mid | mi + +The term "method" refers usually to: + * A mdef entered via a ment into the mtbl of a class + * class->mtbl[mid]->mdef + +Functions are grouped by the type (structure, object) they affect. Whenever +possible, first parameter is a pointer to such type. Examples: + +rb_mtbl_ = functions affecting a method table +rb_ment_ = functions affecting a method entry +rb_mtbl_ e.g. rb_mtbl_add(mtbl_t *mtbl, ) +rb_mdef_ e.g. rb_mdef_new + +*/