[#1094] Re: [ruby-cvs] ruby, ruby/lib: * eval.c (ev_const_defined, ev_const_get), variable.c — Dave Thomas <dave@...>

> * eval.c (rb_mod_autoload, rb_mod_autoload_p): new method;

12 messages 2003/05/29
[#1095] Re: [ruby-cvs] ruby, ruby/lib: * eval.c (ev_const_defined, ev_const_get), variable.c — nobu.nokada@... 2003/05/29

Hi,

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));
 


In This Thread

Prev Next