diff --git a/ChangeLog b/ChangeLog index a4e351fd27..ea76bc6f2a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Wed Mar 16 22:03:15 2005 Shugo Maeda + + * eval.c (rb_add_event_hook): new function to add a hook function for + interpreter events. + Wed Mar 16 18:08:32 2005 Yukihiro Matsumoto * eval.c (rb_call0): reorganize "return" event post. diff --git a/README.EXT b/README.EXT index e5c4203832..aa9c27b51b 100644 --- a/README.EXT +++ b/README.EXT @@ -1001,6 +1001,32 @@ Starts execution of the interpreter. Specifies the name of the script ($0). +** Hooks for the Interpreter Events + + void rb_add_event_hook(rb_event_hook_func_t func, rb_event_t events) + +Adds a hook function for the specified interpreter events. +events should be Or'ed value of: + + RUBY_EVENT_LINE + RUBY_EVENT_CLASS + RUBY_EVENT_END + RUBY_EVENT_CALL + RUBY_EVENT_RETURN + RUBY_EVENT_C_CALL + RUBY_EVENT_C_RETURN + RUBY_EVENT_RAISE + RUBY_EVENT_ALL + +The definition of rb_event_hook_func_t is below: + + typedef void (*rb_event_hook_func_t)(rb_event_t event, NODE *node, + VALUE self, ID id, VALUE klass) + + int rb_remove_event_hook(rb_event_hook_func_t func) + +Removes the specified hook function. + Appendix C. Functions Available in extconf.rb These functions are available in extconf.rb: diff --git a/README.EXT.ja b/README.EXT.ja index 7a4cb5df2a..62ed8a3d98 100644 --- a/README.EXT.ja +++ b/README.EXT.ja @@ -1117,6 +1117,32 @@ void ruby_script(char *name) Rubyのスクリプト名($0)を設定する. +** インタプリタのイベントのフック + + void rb_add_event_hook(rb_event_hook_func_t func, rb_event_t events) + +指定されたインタプリタのイベントに対するフック関数を追加します. +eventsは以下の値のorでなければなりません: + + RUBY_EVENT_LINE + RUBY_EVENT_CLASS + RUBY_EVENT_END + RUBY_EVENT_CALL + RUBY_EVENT_RETURN + RUBY_EVENT_C_CALL + RUBY_EVENT_C_RETURN + RUBY_EVENT_RAISE + RUBY_EVENT_ALL + +rb_event_hook_func_tの定義は以下の通りです: + + typedef void (*rb_event_hook_func_t)(rb_event_t event, NODE *node, + VALUE self, ID id, VALUE klass) + + int rb_remove_event_hook(rb_event_hook_func_t func) + +指定されたフック関数を削除します. + Appendix C. extconf.rbで使える関数たち diff --git a/eval.c b/eval.c index c9ea17b905..e437deb574 100644 --- a/eval.c +++ b/eval.c @@ -1040,9 +1040,27 @@ static VALUE module_setup _((VALUE,NODE*)); static VALUE massign _((VALUE,NODE*,VALUE,int)); static void assign _((VALUE,NODE*,VALUE,int)); +typedef struct event_hook { + rb_event_hook_func_t func; + rb_event_t events; + struct event_hook *next; +} rb_event_hook_t; + +static rb_event_hook_t *event_hooks; + +#define EXEC_EVENT_HOOK(event, node, self, id, klass) \ + do { \ + rb_event_hook_t *hook; \ + \ + for (hook = event_hooks; hook; hook = hook->next) { \ + if (hook->events & event) \ + (*hook->func)(event, node, self, id, klass); \ + } \ + } while (0) + static VALUE trace_func = 0; static int tracing = 0; -static void call_trace_func _((char*,NODE*,VALUE,ID,VALUE)); +static void call_trace_func _((rb_event_t,NODE*,VALUE,ID,VALUE)); #if 0 #define SET_CURRENT_SOURCE() (ruby_sourcefile = ruby_current_node->nd_file, \ @@ -2415,6 +2433,42 @@ rb_obj_is_proc(proc) return Qfalse; } +void +rb_add_event_hook(rb_event_hook_func_t func, rb_event_t events) +{ + rb_event_hook_t *hook; + + hook = ALLOC(rb_event_hook_t); + hook->func = func; + hook->events = events; + hook->next = event_hooks; + event_hooks = hook; +} + +int +rb_remove_event_hook(rb_event_hook_func_t func) +{ + rb_event_hook_t *prev, *hook; + + prev = NULL; + hook = event_hooks; + while (hook) { + if (hook->func == func) { + if (prev) { + prev->next = hook->next; + } + else { + event_hooks = hook->next; + } + xfree(hook); + return 0; + } + prev = hook; + hook = hook->next; + } + return -1; +} + /* * call-seq: * set_trace_func(proc) => proc @@ -2463,19 +2517,53 @@ static VALUE set_trace_func(obj, trace) VALUE obj, trace; { + rb_event_hook_t *hook; + if (NIL_P(trace)) { trace_func = 0; + rb_remove_event_hook(call_trace_func); return Qnil; } if (!rb_obj_is_proc(trace)) { rb_raise(rb_eTypeError, "trace_func needs to be Proc"); } - return trace_func = trace; + trace_func = trace; + for (hook = event_hooks; hook; hook = hook->next) { + if (hook->func == call_trace_func) + return trace; + } + rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL); + return trace; +} + +static char * +get_event_name(rb_event_t event) +{ + switch (event) { + case RUBY_EVENT_LINE: + return "line"; + case RUBY_EVENT_CLASS: + return "class"; + case RUBY_EVENT_END: + return "end"; + case RUBY_EVENT_CALL: + return "call"; + case RUBY_EVENT_RETURN: + return "return"; + case RUBY_EVENT_C_CALL: + return "c-call"; + case RUBY_EVENT_C_RETURN: + return "c-return"; + case RUBY_EVENT_RAISE: + return "raise"; + default: + return "unknown"; + } } static void call_trace_func(event, node, self, id, klass) - char *event; + rb_event_t event; NODE *node; VALUE self; ID id; @@ -2485,6 +2573,7 @@ call_trace_func(event, node, self, id, klass) struct FRAME *prev; NODE *node_save; VALUE srcfile; + char *event_name; if (!trace_func) return; if (tracing) return; @@ -2519,7 +2608,8 @@ call_trace_func(event, node, self, id, klass) raised = thread_reset_raised(); if ((state = EXEC_TAG()) == 0) { srcfile = rb_str_new2(ruby_sourcefile?ruby_sourcefile:"(ruby)"); - proc_invoke(trace_func, rb_ary_new3(6, rb_str_new2(event), + event_name = get_event_name(event); + proc_invoke(trace_func, rb_ary_new3(6, rb_str_new2(event_name), srcfile, INT2FIX(ruby_sourceline), id?ID2SYM(id):Qnil, @@ -2686,8 +2776,8 @@ rb_eval(self, n) if (!node) RETURN(Qnil); ruby_current_node = node; - if (trace_func && (node->flags & NODE_NEWLINE)) { - call_trace_func("line", node, self, + if (node->flags & NODE_NEWLINE) { + EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self, ruby_frame->this_func, ruby_frame->this_class); } @@ -2783,11 +2873,9 @@ rb_eval(self, n) RETURN(ruby_errinfo); case NODE_IF: - if (trace_func) { - call_trace_func("line", node, self, - ruby_frame->this_func, - ruby_frame->this_class); - } + EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self, + ruby_frame->this_func, + ruby_frame->this_class); if (RTEST(rb_eval(self, node->nd_cond))) { node = node->nd_body; } @@ -2803,11 +2891,9 @@ rb_eval(self, n) if (nd_type(node) != NODE_WHEN) goto again; tag = node->nd_head; while (tag) { - if (trace_func) { - call_trace_func("line", tag, self, - ruby_frame->this_func, - ruby_frame->this_class); - } + EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self, + ruby_frame->this_func, + ruby_frame->this_class); if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) { VALUE v = rb_eval(self, tag->nd_head->nd_head); long i; @@ -2846,11 +2932,9 @@ rb_eval(self, n) } tag = node->nd_head; while (tag) { - if (trace_func) { - call_trace_func("line", tag, self, - ruby_frame->this_func, - ruby_frame->this_class); - } + EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self, + ruby_frame->this_func, + ruby_frame->this_class); if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) { VALUE v = rb_eval(self, tag->nd_head->nd_head); long i; @@ -3961,9 +4045,8 @@ module_setup(module, n) PUSH_CREF(module); PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { - if (trace_func) { - call_trace_func("class", n, ruby_cbase, ruby_frame->this_func, ruby_frame->this_class); - } + EXEC_EVENT_HOOK(RUBY_EVENT_CLASS, n, ruby_cbase, + ruby_frame->this_func, ruby_frame->this_class); result = rb_eval(ruby_cbase, node->nd_next); } POP_TAG(); @@ -3973,9 +4056,8 @@ module_setup(module, n) POP_CLASS(); ruby_frame = frame.tmp; - if (trace_func) { - call_trace_func("end", n, 0, ruby_frame->this_func, ruby_frame->this_class); - } + EXEC_EVENT_HOOK(RUBY_EVENT_END, n, 0, ruby_frame->this_func, + ruby_frame->this_class); if (state) JUMP_TAG(state); return result; @@ -4370,8 +4452,8 @@ rb_longjmp(tag, mesg) } rb_trap_restore_mask(); - if (trace_func && tag != TAG_FATAL) { - call_trace_func("raise", ruby_current_node, + if (tag != TAG_FATAL) { + EXEC_EVENT_HOOK(RUBY_EVENT_RAISE, ruby_current_node, ruby_frame->self, ruby_frame->this_func, ruby_frame->this_class); @@ -5585,8 +5667,9 @@ rb_call0(klass, recv, id, oid, argc, argv, body, nosuper) rb_bug("bad argc (%d) specified for `%s(%s)'", len, rb_class2name(klass), rb_id2name(id)); } - if (trace_func) { - call_trace_func("c-call", ruby_current_node, recv, id, klass); + if (event_hooks) { + EXEC_EVENT_HOOK(RUBY_EVENT_C_CALL, ruby_current_node, + recv, id, klass); trace_status = 1; /* cfunc */ } result = call_cfunc(body->nd_cfnc, recv, len, argc, argv); @@ -5714,8 +5797,8 @@ rb_call0(klass, recv, id, oid, argc, argv, body, nosuper) ruby_frame->argc = -(ruby_frame->argc - argc)-1; } - if (trace_func) { - call_trace_func("call", b2, recv, id, klass); + if (event_hooks) { + EXEC_EVENT_HOOK(RUBY_EVENT_CALL, b2, recv, id, klass); trace_status = 2; /* rfunc */ } result = rb_eval(recv, body); @@ -5732,10 +5815,10 @@ rb_call0(klass, recv, id, oid, argc, argv, body, nosuper) switch (trace_status) { case 0: break; /* none */ case 1: /* cfunc */ - call_trace_func("c-return", body, recv, id, klass); + EXEC_EVENT_HOOK(RUBY_EVENT_C_RETURN, body, recv, id, klass); break; case 2: /* rfunc */ - call_trace_func("return", body, recv, id, klass); + EXEC_EVENT_HOOK(RUBY_EVENT_RETURN, body, recv, id, klass); break; } switch (state) { diff --git a/node.h b/node.h index 6cd8b0e415..562efcc32f 100644 --- a/node.h +++ b/node.h @@ -365,6 +365,23 @@ VALUE rb_gvar_get _((struct global_entry *)); VALUE rb_gvar_set _((struct global_entry *, VALUE)); VALUE rb_gvar_defined _((struct global_entry *)); +typedef unsigned int rb_event_t; + +#define RUBY_EVENT_NONE 0x00 +#define RUBY_EVENT_LINE 0x01 +#define RUBY_EVENT_CLASS 0x02 +#define RUBY_EVENT_END 0x04 +#define RUBY_EVENT_CALL 0x08 +#define RUBY_EVENT_RETURN 0x10 +#define RUBY_EVENT_C_CALL 0x20 +#define RUBY_EVENT_C_RETURN 0x40 +#define RUBY_EVENT_RAISE 0x80 +#define RUBY_EVENT_ALL 0xff + +typedef void (*rb_event_hook_func_t)_((rb_event_t,NODE*,VALUE,ID,VALUE)); +void rb_add_event_hook(rb_event_hook_func_t,rb_event_t); +int rb_remove_event_hook(rb_event_hook_func_t); + #if defined(__cplusplus) } /* extern "C" { */ #endif