From: ts Date: 2003-05-29T01:09:43+09:00 Subject: Re: [1.8] terminated object / invalid inspect_tbl >>>>> "t" == ts writes: t> It's a toy actually : it don't work yet with alias and it need some other t> modifications a better version : it must work with operator, class local variable (if one day they are implemented), and alias (but I've never understood alias) svg% ruby -e 'def &:hook(a); b = super; p "#{self} #{a} #{b}";end; 12 & 24' "12 24 8" svg% Guy Decoux diff -u ../ruby/class.c ruby/class.c --- ../ruby/class.c 2003-05-19 09:11:48.000000000 +0200 +++ ruby/class.c 2003-05-27 14:16:10.000000000 +0200 @@ -695,6 +695,24 @@ } void +rb_define_hook(klass, name, func, argc) + VALUE klass; + const char *name; + VALUE (*func)(); + int argc; +{ + ID id = rb_id_hook(rb_intern(name)); + int ex = NOEX_PUBLIC; + + + if (BUILTIN_TYPE(klass) == T_CLASS) { + ex |= NOEX_CFUNC; + } + rb_add_method(klass, id, NEW_CFUNC(func, argc), ex); +} + + +void rb_define_protected_method(klass, name, func, argc) VALUE klass; const char *name; @@ -777,6 +795,16 @@ } void +rb_define_singleton_hook(obj, name, func, argc) + VALUE obj; + const char *name; + VALUE (*func)(); + int argc; +{ + rb_define_hook(rb_singleton_class(obj), name, func, argc); +} + +void rb_define_module_function(module, name, func, argc) VALUE module; const char *name; diff -u ../ruby/env.h ruby/env.h --- ../ruby/env.h 2003-01-16 08:34:01.000000000 +0100 +++ ruby/env.h 2003-05-25 16:32:33.000000000 +0200 @@ -19,6 +19,8 @@ VALUE *argv; ID last_func; ID orig_func; + ID orig_id; + VALUE orig_class; VALUE last_class; VALUE cbase; struct FRAME *prev; diff -u ../ruby/eval.c ruby/eval.c --- ../ruby/eval.c 2003-05-28 15:05:55.000000000 +0200 +++ ruby/eval.c 2003-05-28 16:06:56.000000000 +0200 @@ -106,6 +106,7 @@ static VALUE rb_cUnboundMethod; static VALUE umethod_bind _((VALUE, VALUE)); static VALUE rb_mod_define_method _((int, VALUE*, VALUE)); +static VALUE rb_mod_define_hook _((int, VALUE*, VALUE)); static int scope_vmode; #define SCOPE_PUBLIC 0 @@ -198,6 +199,7 @@ VALUE origin; /* where method defined */ NODE *method; int noex; + int hook; }; static struct cache_entry cache[CACHE_SIZE]; @@ -278,6 +280,9 @@ } if (OBJ_FROZEN(klass)) rb_error_frozen("class/module"); rb_clear_cache_by_id(mid); + if (rb_is_hook_id(mid)) { + rb_clear_cache_by_id(rb_hook_to_id(mid)); + } body = NEW_METHOD(node, noex); st_insert(RCLASS(klass)->m_tbl, mid, (st_data_t)body); if (node && mid != ID_ALLOCATOR && ruby_running) { @@ -344,6 +349,7 @@ ent->mid = ent->mid0 = id; ent->noex = 0; ent->method = 0; + ent->hook = 0; return 0; } @@ -353,6 +359,7 @@ ent = cache + EXPR1(klass, id); ent->klass = klass; ent->noex = body->nd_noex; + ent->hook = 0; if (noexp) *noexp = body->nd_noex; body = body->nd_body; if (nd_type(body) == NODE_FBODY) { @@ -432,6 +439,14 @@ return mod; } +static VALUE +rb_mod_remove_hook(mod, name) + VALUE mod, name; +{ + remove_method(mod, rb_id_hook(rb_to_id(name))); + return mod; +} + void rb_disable_super(klass, name) VALUE klass; @@ -591,16 +606,18 @@ static struct FRAME *top_frame; static struct SCOPE *top_scope; -#define PUSH_FRAME() do { \ - struct FRAME _frame; \ - _frame.prev = ruby_frame; \ - _frame.tmp = 0; \ - _frame.node = ruby_current_node; \ - _frame.iter = ruby_iter->iter; \ - _frame.cbase = ruby_frame->cbase; \ - _frame.argc = 0; \ - _frame.argv = 0; \ - _frame.flags = FRAME_ALLOCA; \ +#define PUSH_FRAME() do { \ + struct FRAME _frame; \ + _frame.prev = ruby_frame; \ + _frame.tmp = 0; \ + _frame.node = ruby_current_node; \ + _frame.iter = ruby_iter->iter; \ + _frame.cbase = ruby_frame->cbase; \ + _frame.orig_class = ruby_frame->orig_class; \ + _frame.orig_id = ruby_frame->orig_id; \ + _frame.argc = 0; \ + _frame.argv = 0; \ + _frame.flags = FRAME_ALLOCA; \ ruby_frame = &_frame #define POP_FRAME() \ @@ -954,6 +971,8 @@ static VALUE rb_call _((VALUE,VALUE,ID,int,const VALUE*,int)); static VALUE module_setup _((VALUE,NODE*)); +static VALUE rb_call_hook _((VALUE,VALUE,ID,ID,int,const VALUE*)); + static VALUE massign _((VALUE,NODE*,VALUE,int)); static void assign _((VALUE,NODE*,VALUE,int)); @@ -1810,6 +1829,14 @@ return mod; } +static VALUE +rb_mod_undef_hook(mod, name) + VALUE mod, name; +{ + rb_undef(mod, rb_id_hook(rb_to_id(name))); + return mod; +} + void rb_alias(klass, name, def) VALUE klass; @@ -1863,6 +1890,14 @@ return mod; } +static VALUE +rb_mod_alias_hook(mod, newname, oldname) + VALUE mod, newname, oldname; +{ + rb_alias(mod, rb_id_hook(rb_to_id(newname)), rb_id_hook(rb_to_id(oldname))); + return mod; +} + static NODE* copy_node_scope(node, rval) NODE *node; @@ -3015,9 +3050,23 @@ PUSH_ITER(ruby_iter->iter?ITER_PRE:ITER_NOT); SET_CURRENT_SOURCE(); - result = rb_call(RCLASS(ruby_frame->last_class)->super, - ruby_frame->self, ruby_frame->orig_func, - argc, argv, 3); + result = Qundef; + if (rb_is_hook_id(ruby_frame->orig_func)) { + result = rb_call_hook(RCLASS(ruby_frame->last_class)->super, + ruby_frame->self, 0, + ruby_frame->orig_func, + argc, argv); + if (result == Qundef) { + result = rb_call(ruby_frame->orig_class, + ruby_frame->self, + ruby_frame->orig_id, argc, argv, 3); + } + } + if (result == Qundef) { + result = rb_call(RCLASS(ruby_frame->last_class)->super, + ruby_frame->self, + ruby_frame->orig_func, argc, argv, 3); + } POP_ITER(); } break; @@ -4015,6 +4064,8 @@ struct BLOCK * volatile block; struct SCOPE * volatile old_scope; struct FRAME frame; + VALUE orig_class; + ID orig_id; NODE *cnode = ruby_current_node; int state; static unsigned serial = 1; @@ -4028,7 +4079,11 @@ block = ruby_block; frame = block->frame; frame.prev = ruby_frame; + orig_class = ruby_frame->orig_class; + orig_id = ruby_frame->orig_id; ruby_frame = &(frame); + ruby_frame->orig_class = orig_class; + ruby_frame->orig_id = orig_id; old_cref = (VALUE)ruby_cref; ruby_cref = (NODE*)ruby_frame->cbase; old_wrapper = ruby_wrapper; @@ -5028,6 +5083,54 @@ } static VALUE +rb_call_hook(klass, recv, oid, mid, argc, argv) + VALUE klass, recv; + ID oid, mid; + int argc; /* OK */ + const VALUE *argv; /* OK */ +{ + NODE *body; /* OK */ + int noex; + VALUE res = Qnil; + ID id = mid; + VALUE oklass = klass; + struct cache_entry *ent, *ent_orig; + + ent = cache + EXPR1(klass, mid); + if (oid) { + ent_orig = cache + EXPR1(klass, oid); + } + if (ent->mid == mid && ent->klass == klass) { + if (!ent->method) { + if (oid) { + ent_orig->hook = 2; + } + return Qundef; + } + klass = ent->origin; + id = ent->mid0; + noex = ent->noex; + body = ent->method; + } + else if ((body = rb_get_method_body(&klass, &id, &noex)) == 0) { + if (oid) { + ent_orig->hook = 2; + } + return Qundef; + } + if (oid) { + ruby_frame->orig_class = oklass; + ruby_frame->orig_id = oid; + ent_orig->hook = 1; + } + else { + ruby_frame->orig_class = ruby_frame->prev->orig_class; + ruby_frame->orig_id = ruby_frame->prev->orig_id; + } + return rb_call0(klass, recv, mid, id, argc, argv, body, noex & NOEX_NOSUPER); +} + +static VALUE rb_call(klass, recv, mid, argc, argv, scope) VALUE klass, recv; ID mid; @@ -5037,6 +5140,7 @@ { NODE *body; /* OK */ int noex; + VALUE oklass = klass, res; ID id = mid; struct cache_entry *ent; @@ -5078,7 +5182,15 @@ } } - return rb_call0(klass, recv, mid, id, argc, argv, body, noex & NOEX_NOSUPER); + res = Qundef; + ent = cache + EXPR1(oklass, mid); + if (ent->hook < 2 && scope != 3 && rb_has_hook()) { + res = rb_call_hook(oklass, recv, mid, rb_id_hook(id), argc, argv); + } + if (res == Qundef) { + res = rb_call0(klass, recv, mid, id, argc, argv, body, noex & NOEX_NOSUPER); + } + return res; } VALUE @@ -6493,6 +6605,10 @@ rb_define_private_method(rb_cModule, "undef_method", rb_mod_undef_method, 1); rb_define_private_method(rb_cModule, "alias_method", rb_mod_alias_method, 2); rb_define_private_method(rb_cModule, "define_method", rb_mod_define_method, -1); + rb_define_private_method(rb_cModule, "remove_hook", rb_mod_remove_hook, 1); + rb_define_private_method(rb_cModule, "undef_hook", rb_mod_undef_hook, 1); + rb_define_private_method(rb_cModule, "alias_hook", rb_mod_alias_hook, 2); + rb_define_private_method(rb_cModule, "define_hook", rb_mod_define_hook, -1); rb_define_singleton_method(rb_cModule, "nesting", rb_mod_nesting, 0); rb_define_singleton_method(rb_cModule, "constants", rb_mod_s_constants, 0); @@ -7566,6 +7682,19 @@ return body; } +static VALUE +rb_mod_define_hook(argc, argv, mod) + int argc; + VALUE *argv; + VALUE mod; +{ + if (argc != 1) { + rb_raise(rb_eArgError, "invalid number of arguments"); + } + argv[0] = ID2SYM(rb_id_hook(rb_to_id(argv[0]))); + return rb_mod_define_method(argc, argv, mod); +} + void Init_Proc() { diff -u ../ruby/intern.h ruby/intern.h --- ../ruby/intern.h 2003-05-26 15:47:05.000000000 +0200 +++ ruby/intern.h 2003-05-28 16:12:44.000000000 +0200 @@ -119,6 +119,7 @@ void rb_define_protected_method _((VALUE, const char*, VALUE (*)(ANYARGS), int)); void rb_define_private_method _((VALUE, const char*, VALUE (*)(ANYARGS), int)); void rb_define_singleton_method _((VALUE, const char*, VALUE(*)(ANYARGS), int)); +void rb_define_singleton_hook _((VALUE, const char*, VALUE(*)(ANYARGS), int)); VALUE rb_singleton_class _((VALUE)); /* compar.c */ int rb_cmpint _((VALUE, VALUE, VALUE)); @@ -321,6 +322,10 @@ VALUE rb_lastline_get _((void)); void rb_lastline_set _((VALUE)); VALUE rb_sym_all_symbols _((void)); +ID rb_id_hook _((ID)); +ID rb_hook_to_id _((ID)); +int rb_has_hook _((void)); +int rb_is_hook_id _((ID)); /* process.c */ int rb_proc_exec _((const char*)); VALUE rb_f_exec _((int,VALUE*)); diff -u ../ruby/parse.y ruby/parse.y --- ../ruby/parse.y 2003-05-26 15:47:07.000000000 +0200 +++ ruby/parse.y 2003-05-28 17:28:41.000000000 +0200 @@ -42,6 +42,7 @@ #define ID_INTERNAL ID_JUNK #define is_notop_id(id) ((id)>tLAST_TOKEN) +#define is_hook_id(id) ((((long)id)<0)) #define is_local_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL) #define is_global_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_GLOBAL) #define is_instance_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_INSTANCE) @@ -161,6 +162,7 @@ static int local_id(); static ID *local_tbl(); static ID internal_id(); +static ID op_hook(); static struct RVarmap *dyna_push(); static void dyna_pop(); @@ -385,6 +387,9 @@ stmt : kALIAS fitem {lex_state = EXPR_FNAME;} fitem { + if (rb_is_hook_id($2) != rb_is_hook_id($4)) { + yyerror("can't alias hook with a method"); + } $$ = NEW_ALIAS($2, $4); } | kALIAS tGVAR tGVAR @@ -868,7 +873,7 @@ | op { lex_state = EXPR_END; - $$ = $1; + $$ = op_hook($1); } | reswords { @@ -3277,10 +3282,46 @@ #define IS_ARG() (lex_state == EXPR_ARG || lex_state == EXPR_CMDARG) static int +check_hook() +{ + char *hook = "hook"; + register int c; + + for (c = 0; c < strlen(hook); ++c) { + if (nextc() != hook[c]) { + goto error; + } + } + c = nextc(); + if (is_identchar(c)) { + error: + yyerror("expected ':hook'"); + return 0; + } + pushback(c); + return 1; +} + +static ID +op_hook(id) + ID id; +{ + int c = nextc(); + if (c == ':') { + if (check_hook()) { + return rb_id_hook(id); + } + } + pushback(c); + return id; +} + +static int yylex() { register int c; int space_seen = 0; + int found_hook = 0; int cmd_state; if (lex_strterm) { @@ -4316,6 +4357,12 @@ } while (is_identchar(c)); if ((c == '!' || c == '?') && is_identchar(tok()[0]) && !peek('=')) { tokadd(c); + c = nextc(); + } + found_hook = 0; + if (lex_state == EXPR_FNAME && c == ':' && + is_identchar(tok()[0]) && !peek(':')) { + found_hook = check_hook(); } else { pushback(c); @@ -4349,6 +4396,13 @@ result = tIDENTIFIER; tokadd(c); tokfix(); + c = nextc(); + if (c == ':') { + found_hook = check_hook(); + } + else { + pushback(c); + } } else { pushback(c); @@ -4362,7 +4416,7 @@ } } - if (lex_state != EXPR_DOT) { + if (lex_state != EXPR_DOT && !found_hook) { struct kwtable *kw; /* See if it is a reserved word. */ @@ -4408,8 +4462,11 @@ } } yylval.id = rb_intern(tok()); - if (is_local_id(yylval.id) && - ((dyna_in_block() && rb_dvar_defined(yylval.id)) || local_id(yylval.id))) { + if (found_hook) { + yylval.id = rb_id_hook(yylval.id); + } + else if (is_local_id(yylval.id) && + ((dyna_in_block() && rb_dvar_defined(yylval.id)) || local_id(yylval.id))) { lex_state = EXPR_END; } return result; @@ -5755,11 +5812,14 @@ static st_table *sym_tbl; static st_table *sym_rev_tbl; +static ID id_allocate; + void Init_sym() { sym_tbl = st_init_strtable_with_size(200); sym_rev_tbl = st_init_numtable_with_size(200); + id_allocate = rb_intern("allocate"); } static ID last_id = tLAST_TOKEN; @@ -5852,6 +5912,9 @@ { char *name; + if (rb_is_hook_id(id)) { + return "\"hooked method\""; + } if (id < tLAST_TOKEN) { int i = 0; @@ -6002,3 +6065,40 @@ special_local_set('_', val); } } + +static int has_hook = 0; + +ID +rb_id_hook(id) + ID id; +{ + has_hook = Qtrue; + if (id == ID_ALLOCATOR) { + id = id_allocate; + } + return -id; +} + +ID +rb_hook_to_id(id) + ID id; +{ + if (id == -1*id_allocate) { + return ID_ALLOCATOR; + } + return -id; +} + +int +rb_has_hook() +{ + return has_hook; +} + +int +rb_is_hook_id(id) + ID id; +{ + if (is_hook_id(id)) return Qtrue; + return Qfalse; +} diff -u ../ruby/ruby.h ruby/ruby.h --- ../ruby/ruby.h 2003-05-26 15:47:07.000000000 +0200 +++ ruby/ruby.h 2003-05-28 11:48:45.000000000 +0200 @@ -480,6 +480,7 @@ #define RUBY_METHOD_FUNC(func) ((VALUE (*)(ANYARGS))func) void rb_define_method _((VALUE,const char*,VALUE(*)(ANYARGS),int)); +void rb_define_hook _((VALUE,const char*,VALUE(*)(ANYARGS),int)); void rb_define_module_function _((VALUE,const char*,VALUE(*)(ANYARGS),int)); void rb_define_global_function _((const char*,VALUE(*)(ANYARGS),int));