Re: [1.8] terminated object / invalid inspect_tbl
From:
ts <decoux@...>
Date:
2003-05-28 16:09:43 UTC
List:
ruby-core #1093
>>>>> "t" == ts <decoux@moulon.inra.fr> 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));