static int clone_method(ID mid, NODE *body, VALUE nklass) { NODE *fbody = body->nd_body; if (fbody) { VALUE nbody; switch (nd_type(fbody)) { case NODE_SCOPE: fbody = rb_copy_node_scope(fbody, ruby_cref); break; case NODE_BMETHOD: nbody = rb_block_dup(fbody->nd_cval, nklass, (VALUE)ruby_cref); fbody = NEW_BMETHOD(nbody); break; case NODE_DMETHOD: nbody = rb_method_dup(fbody->nd_cval, nklass, (VALUE)ruby_cref); fbody = NEW_DMETHOD(nbody); break; } } st_insert(RCLASS(nklass)->m_tbl, mid, (st_data_t)NEW_METHOD(fbody, body->nd_noex)); return ST_CONTINUE; }
void rb_alias(VALUE klass, ID name, ID def) { NODE *orig_fbody, *node, *method; VALUE singleton = 0; st_data_t data; rb_frozen_class_p(klass); if (klass == rb_cObject) { rb_secure(4); } orig_fbody = search_method(klass, def, 0); if (!orig_fbody || !orig_fbody->nd_body) { if (TYPE(klass) == T_MODULE) { orig_fbody = search_method(rb_cObject, def, 0); } } if (!orig_fbody || !orig_fbody->nd_body) { rb_print_undef(klass, def, 0); } if (FL_TEST(klass, FL_SINGLETON)) { singleton = rb_iv_get(klass, "__attached__"); } orig_fbody->nd_cnt++; if (st_lookup(RCLASS_M_TBL(klass), name, &data)) { node = (NODE *)data; if (node) { if (RTEST(ruby_verbose) && node->nd_cnt == 0 && node->nd_body) { rb_warning("discarding old %s", rb_id2name(name)); } if (nd_type(node->nd_body->nd_body) == NODE_CFUNC) { rb_vm_check_redefinition_opt_method(node); } } } st_insert(RCLASS_M_TBL(klass), name, (st_data_t) NEW_FBODY( method = NEW_METHOD(orig_fbody->nd_body->nd_body, orig_fbody->nd_body->nd_clss, NOEX_WITH_SAFE(orig_fbody->nd_body->nd_noex)), def)); method->nd_file = (void *)def; rb_clear_cache_by_id(name); if (!ruby_running) return; if (singleton) { rb_funcall(singleton, singleton_added, 1, ID2SYM(name)); } else { rb_funcall(klass, added, 1, ID2SYM(name)); } }
static int clone_method(ID mid, NODE *body, struct clone_method_data *data) { if (body == 0) { st_insert(data->tbl, mid, 0); } else { st_insert(data->tbl, mid, (st_data_t) NEW_FBODY( NEW_METHOD(body->nd_body->nd_body, data->klass, /* TODO */ body->nd_body->nd_noex), 0)); } return ST_CONTINUE; }
void rb_add_method(VALUE klass, ID mid, NODE * node, int noex) { NODE *body; if (NIL_P(klass)) { klass = rb_cObject; } if (rb_safe_level() >= 4 && (klass == rb_cObject || !OBJ_TAINTED(klass))) { rb_raise(rb_eSecurityError, "Insecure: can't define method"); } if (!FL_TEST(klass, FL_SINGLETON) && node && nd_type(node) != NODE_ZSUPER && (mid == rb_intern("initialize") || mid == rb_intern("initialize_copy"))) { noex = NOEX_PRIVATE | noex; } else if (FL_TEST(klass, FL_SINGLETON) && node && nd_type(node) == NODE_CFUNC && mid == rb_intern("allocate")) { rb_warn ("defining %s.allocate is deprecated; use rb_define_alloc_func()", rb_class2name(rb_iv_get(klass, "__attached__"))); mid = ID_ALLOCATOR; } if (OBJ_FROZEN(klass)) { rb_error_frozen("class/module"); } rb_clear_cache_by_id(mid); /* * NODE_METHOD (NEW_METHOD(body, klass, vis)): * nd_body : method body // (2) // mark * nd_clss : klass // (1) // mark * nd_noex : visibility // (3) * * NODE_FBODY (NEW_FBODY(method, alias)): * nd_body : method (NODE_METHOD) // (2) // mark * nd_oid : original id // (1) * nd_cnt : alias count // (3) */ if (node) { body = NEW_FBODY(NEW_METHOD(node, klass, NOEX_WITH_SAFE(noex)), 0); } else { body = 0; } { /* check re-definition */ st_data_t data; NODE *old_node; if (st_lookup(RCLASS_M_TBL(klass), mid, &data)) { old_node = (NODE *)data; if (old_node) { if (nd_type(old_node->nd_body->nd_body) == NODE_CFUNC) { rb_vm_check_redefinition_opt_method(old_node); } if (RTEST(ruby_verbose) && node && old_node->nd_cnt == 0 && old_node->nd_body) { rb_warning("method redefined; discarding old %s", rb_id2name(mid)); } } } if (klass == rb_cObject && node && mid == idInitialize) { rb_warn("redefining Object#initialize may cause infinite loop"); } if (mid == object_id || mid == __send__) { if (node && nd_type(node) == RUBY_VM_METHOD_NODE) { rb_warn("redefining `%s' may cause serious problem", rb_id2name(mid)); } } } st_insert(RCLASS_M_TBL(klass), mid, (st_data_t) body); if (node && mid != ID_ALLOCATOR && ruby_running) { if (FL_TEST(klass, FL_SINGLETON)) { rb_funcall(rb_iv_get(klass, "__attached__"), singleton_added, 1, ID2SYM(mid)); } else { rb_funcall(klass, added, 1, ID2SYM(mid)); } } }
/* * call-seq: * class.add_method(id, node or iseq, noex) #=> nil * * Adds the method as an instance method to the given class. * * To add a singleton method to a class, add the method to its singleton * class. */ static VALUE module_add_method(VALUE klass, VALUE id, VALUE node, VALUE noex) { NODE * n = 0; if(rb_safe_level() >= 2) { /* adding a method with the wrong node type can cause a crash */ rb_raise(rb_eSecurityError, "Insecure: can't add method"); } #if RUBY_VERSION_CODE >= 192 if(rb_obj_is_kind_of(node, rb_cISeq)) { rb_iseq_t *iseqdat = iseq_check(node); set_cref_stack(iseqdat, klass, noex); iseqdat->klass = klass; iseqdat->defined_method_id = SYM2ID(id); #ifdef HAVE_RB_ADD_METHOD rb_add_method(klass, SYM2ID(id), VM_METHOD_TYPE_ISEQ, iseqdat, NUM2INT(noex)); #else rb_funcall(rb_mRubyVMFrozenCore, rb_intern("core#define_method"), 3, klass, id, node); /* TODO: noex */ #endif return Qnil; } #elif RUBY_VERSION_CODE >= 190 if(rb_obj_is_kind_of(node, rb_cISeq)) { rb_iseq_t *iseqdat = iseq_check(node); /* TODO: any restrictions on what kinds of iseqs we can add here? */ set_cref_stack(iseqdat, klass, noex); iseqdat->klass = klass; iseqdat->defined_method_id = SYM2ID(id); n = NEW_METHOD(iseqdat->self, klass, NUM2INT(noex)); goto add_node; } #endif if(!rb_obj_is_kind_of(node, rb_cNode)) { rb_raise( rb_eTypeError, "Expected Node for 2nd parameter, got %s", rb_class2name(CLASS_OF(n))); } Data_Get_Struct(node, NODE, n); #if RUBY_VERSION_CODE >= 192 rb_raise(rb_eRuntimeError, "Unable to add node on this version of ruby"); #elif RUBY_VERSION_CODE >= 190 if(nd_type(n) != NODE_METHOD) { rb_raise( rb_eTypeError, "Expected METHOD node, got %s", rb_class2name(CLASS_OF(n))); } { rb_iseq_t *iseqdat = iseq_check((VALUE)n->nd_body); set_cref_stack(iseqdat, klass, noex); iseqdat->klass = klass; iseqdat->defined_method_id = SYM2ID(id); n = NEW_METHOD(iseqdat->self, klass, NUM2INT(noex)); } add_node: #endif #if RUBY_VERSION_CODE >= 192 rb_raise(rb_eRuntimeError, "Unable to add node on this version of ruby"); #else /* TODO: if noex is NOEX_MODFUNC, add this method as a module function * (that is, both as an instance and singleton method) */ rb_add_method(klass, SYM2ID(id), n, NUM2INT(noex)); return Qnil; #endif }