mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* eval.c, node.h, thread.c, yarvcore.[ch], eval_intern.h:
support set_trace_func (incomplete. id and klass don't be passed). And support Thread#set_trace_func which hook only specified thread and Thread#add_trace_func which add new trace func instead of replace old one. C level API was modified. See thread.c (logic) and yarvcore.h (data structures). * vm.c, vm_macro.def: add hook points. * compile.c, insns.def: fix "trace" instruction. * iseq.c, vm_macro.h: add compile option "trace_instruction". * test/ruby/test_settracefunc.rb: hook "c-return" of set_trace_func. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12195 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
		
							parent
							
								
									2dd91facca
								
							
						
					
					
						commit
						a73894337a
					
				
					 15 changed files with 468 additions and 309 deletions
				
			
		
							
								
								
									
										18
									
								
								ChangeLog
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								ChangeLog
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,3 +1,21 @@
 | 
			
		|||
Thu Apr 19 18:37:49 2007  Koichi Sasada  <ko1@atdot.net>
 | 
			
		||||
 | 
			
		||||
	* eval.c, node.h, thread.c, yarvcore.[ch], eval_intern.h:
 | 
			
		||||
	  support set_trace_func (incomplete.  id and klass
 | 
			
		||||
	  don't be passed).  And support Thread#set_trace_func
 | 
			
		||||
	  which hook only specified thread and Thread#add_trace_func
 | 
			
		||||
	  which add new trace func instead of replace old one.
 | 
			
		||||
	  C level API was modified.  See thread.c (logic) and
 | 
			
		||||
	  yarvcore.h (data structures).
 | 
			
		||||
 | 
			
		||||
	* vm.c, vm_macro.def: add hook points.
 | 
			
		||||
 | 
			
		||||
	* compile.c, insns.def: fix "trace" instruction.
 | 
			
		||||
 | 
			
		||||
	* iseq.c, vm_macro.h: add compile option "trace_instruction".
 | 
			
		||||
 | 
			
		||||
	* test/ruby/test_settracefunc.rb: hook "c-return" of set_trace_func.
 | 
			
		||||
 | 
			
		||||
Thu Apr 19 17:46:36 2007  Koichi Sasada  <ko1@atdot.net>
 | 
			
		||||
 | 
			
		||||
	* lib/optparse.rb: fix to override conv proc.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										18
									
								
								compile.c
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								compile.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -162,7 +162,19 @@ rb_iseq_compile(VALUE self, NODE *node)
 | 
			
		|||
	    ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, 0, end);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
	    COMPILE(ret, "scoped node", node->nd_body);
 | 
			
		||||
	    if (iseq->type == ISEQ_TYPE_CLASS) {
 | 
			
		||||
		ADD_TRACE(ret, nd_line(node), RUBY_EVENT_CLASS);
 | 
			
		||||
		COMPILE(ret, "scoped node", node->nd_body);
 | 
			
		||||
		ADD_TRACE(ret, nd_line(node), RUBY_EVENT_END);
 | 
			
		||||
	    }
 | 
			
		||||
	    else if (iseq->type == ISEQ_TYPE_METHOD) {
 | 
			
		||||
		ADD_TRACE(ret, nd_line(node), RUBY_EVENT_CALL);
 | 
			
		||||
		COMPILE(ret, "scoped node", node->nd_body);
 | 
			
		||||
		ADD_TRACE(ret, nd_line(node), RUBY_EVENT_RETURN);
 | 
			
		||||
	    }
 | 
			
		||||
	    else {
 | 
			
		||||
		COMPILE(ret, "scoped node", node->nd_body);
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
| 
						 | 
				
			
			@ -2433,6 +2445,10 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
 | 
			
		|||
 | 
			
		||||
    type = nd_type(node);
 | 
			
		||||
 | 
			
		||||
    if (node->flags & NODE_NEWLINE) {
 | 
			
		||||
	ADD_TRACE(ret, nd_line(node), RUBY_EVENT_LINE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
 | 
			
		||||
      case NODE_METHOD:{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -157,6 +157,11 @@ r_value(VALUE value)
 | 
			
		|||
           new_insn_send(iseq, line, \
 | 
			
		||||
                         (VALUE)id, (VALUE)argc, (VALUE)block, (VALUE)flag))
 | 
			
		||||
 | 
			
		||||
#define ADD_TRACE(seq, line, event) \
 | 
			
		||||
  if (iseq->compile_data->option->trace_instruction) { \
 | 
			
		||||
      ADD_INSN1(seq, line, trace, INT2FIX(event)); \
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
/* add label */
 | 
			
		||||
#define ADD_LABEL(seq, label) \
 | 
			
		||||
  ADD_ELEM(seq, (LINK_ELEMENT *)label)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										274
									
								
								eval.c
									
										
									
									
									
								
							
							
						
						
									
										274
									
								
								eval.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -40,29 +40,6 @@ static VALUE eval _((VALUE, VALUE, VALUE, char *, int));
 | 
			
		|||
static VALUE rb_yield_0 _((VALUE, VALUE, VALUE, int, int));
 | 
			
		||||
static VALUE rb_call(VALUE, VALUE, ID, int, const VALUE *, int);
 | 
			
		||||
 | 
			
		||||
static void rb_clear_trace_func(void);
 | 
			
		||||
 | 
			
		||||
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 void call_trace_func _((rb_event_t, NODE *, VALUE, ID, VALUE));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "eval_error.h"
 | 
			
		||||
#include "eval_method.h"
 | 
			
		||||
#include "eval_safe.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -162,8 +139,8 @@ ruby_finalize_1(void)
 | 
			
		|||
{
 | 
			
		||||
    signal(SIGINT, SIG_DFL);
 | 
			
		||||
    GET_THREAD()->errinfo = 0;
 | 
			
		||||
    rb_gc_call_finalizer_at_exit();
 | 
			
		||||
    rb_clear_trace_func();
 | 
			
		||||
    rb_gc_call_finalizer_at_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
| 
						 | 
				
			
			@ -446,219 +423,6 @@ rb_frozen_class_p(VALUE klass)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef C_ALLOCA
 | 
			
		||||
# define TMP_PROTECT NODE * volatile tmp__protect_tmp=0
 | 
			
		||||
# define TMP_ALLOC(n)							\
 | 
			
		||||
    (tmp__protect_tmp = NEW_NODE(NODE_ALLOCA,				\
 | 
			
		||||
				 ALLOC_N(VALUE,n),tmp__protect_tmp,n),	\
 | 
			
		||||
     (void*)tmp__protect_tmp->nd_head)
 | 
			
		||||
#else
 | 
			
		||||
# define TMP_PROTECT typedef int foobazzz
 | 
			
		||||
# define TMP_ALLOC(n) ALLOCA_N(VALUE,n)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define MATCH_DATA *rb_svar(node->nd_cnt)
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
rb_add_event_hook(func, events)
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
rb_clear_trace_func(void)
 | 
			
		||||
{
 | 
			
		||||
    /* TODO: fix me */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  call-seq:
 | 
			
		||||
 *     set_trace_func(proc)    => proc
 | 
			
		||||
 *     set_trace_func(nil)     => nil
 | 
			
		||||
 *  
 | 
			
		||||
 *  Establishes _proc_ as the handler for tracing, or disables
 | 
			
		||||
 *  tracing if the parameter is +nil+. _proc_ takes up
 | 
			
		||||
 *  to six parameters: an event name, a filename, a line number, an
 | 
			
		||||
 *  object id, a binding, and the name of a class. _proc_ is
 | 
			
		||||
 *  invoked whenever an event occurs. Events are: <code>c-call</code>
 | 
			
		||||
 *  (call a C-language routine), <code>c-return</code> (return from a
 | 
			
		||||
 *  C-language routine), <code>call</code> (call a Ruby method),
 | 
			
		||||
 *  <code>class</code> (start a class or module definition),
 | 
			
		||||
 *  <code>end</code> (finish a class or module definition),
 | 
			
		||||
 *  <code>line</code> (execute code on a new line), <code>raise</code>
 | 
			
		||||
 *  (raise an exception), and <code>return</code> (return from a Ruby
 | 
			
		||||
 *  method). Tracing is disabled within the context of _proc_.
 | 
			
		||||
 *
 | 
			
		||||
 *      class Test
 | 
			
		||||
 *	def test
 | 
			
		||||
 *	  a = 1
 | 
			
		||||
 *	  b = 2
 | 
			
		||||
 *	end
 | 
			
		||||
 *      end
 | 
			
		||||
 *
 | 
			
		||||
 *      set_trace_func proc { |event, file, line, id, binding, classname|
 | 
			
		||||
 *	   printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
 | 
			
		||||
 *      }
 | 
			
		||||
 *      t = Test.new
 | 
			
		||||
 *      t.test
 | 
			
		||||
 *
 | 
			
		||||
 *	  line prog.rb:11               false
 | 
			
		||||
 *      c-call prog.rb:11        new    Class
 | 
			
		||||
 *      c-call prog.rb:11 initialize   Object
 | 
			
		||||
 *    c-return prog.rb:11 initialize   Object
 | 
			
		||||
 *    c-return prog.rb:11        new    Class
 | 
			
		||||
 *	  line prog.rb:12               false
 | 
			
		||||
 *  	  call prog.rb:2        test     Test
 | 
			
		||||
 *	  line prog.rb:3        test     Test
 | 
			
		||||
 *	  line prog.rb:4        test     Test
 | 
			
		||||
 *      return prog.rb:4        test     Test
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
set_trace_func(VALUE obj, VALUE trace)
 | 
			
		||||
{
 | 
			
		||||
    rb_event_hook_t *hook;
 | 
			
		||||
 | 
			
		||||
    if (NIL_P(trace)) {
 | 
			
		||||
	rb_clear_trace_func();
 | 
			
		||||
	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");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* register trace func */
 | 
			
		||||
    /* 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(rb_event_t event, NODE *node, VALUE self, ID id, VALUE klass)
 | 
			
		||||
{
 | 
			
		||||
    /* TODO: fix me */
 | 
			
		||||
#if 0
 | 
			
		||||
    int state, raised;
 | 
			
		||||
    NODE *node_save;
 | 
			
		||||
    VALUE srcfile;
 | 
			
		||||
    char *event_name;
 | 
			
		||||
 | 
			
		||||
    if (!trace_func)
 | 
			
		||||
	return;
 | 
			
		||||
    if (tracing)
 | 
			
		||||
	return;
 | 
			
		||||
    if (id == ID_ALLOCATOR)
 | 
			
		||||
	return;
 | 
			
		||||
    if (!node && ruby_sourceline == 0)
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    if (!(node_save = ruby_current_node)) {
 | 
			
		||||
	node_save = NEW_BEGIN(0);
 | 
			
		||||
    }
 | 
			
		||||
    tracing = 1;
 | 
			
		||||
 | 
			
		||||
    if (node) {
 | 
			
		||||
	ruby_current_node = node;
 | 
			
		||||
	ruby_sourcefile = node->nd_file;
 | 
			
		||||
	ruby_sourceline = nd_line(node);
 | 
			
		||||
    }
 | 
			
		||||
    if (klass) {
 | 
			
		||||
	if (TYPE(klass) == T_ICLASS) {
 | 
			
		||||
	    klass = RBASIC(klass)->klass;
 | 
			
		||||
	}
 | 
			
		||||
	else if (FL_TEST(klass, FL_SINGLETON)) {
 | 
			
		||||
	    klass = self;
 | 
			
		||||
	}
 | 
			
		||||
    }
 | 
			
		||||
    PUSH_TAG(PROT_NONE);
 | 
			
		||||
    raised = thread_reset_raised(th);
 | 
			
		||||
    if ((state = EXEC_TAG()) == 0) {
 | 
			
		||||
	srcfile = rb_str_new2(ruby_sourcefile ? ruby_sourcefile : "(ruby)");
 | 
			
		||||
	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,
 | 
			
		||||
					    self ? rb_binding_new() : Qnil,
 | 
			
		||||
					    klass ? klass : Qnil), Qundef, 0);
 | 
			
		||||
    }
 | 
			
		||||
    if (raised)
 | 
			
		||||
	thread_set_raised(th);
 | 
			
		||||
    POP_TAG();
 | 
			
		||||
 | 
			
		||||
    tracing = 0;
 | 
			
		||||
    ruby_current_node = node_save;
 | 
			
		||||
    SET_CURRENT_SOURCE();
 | 
			
		||||
    if (state)
 | 
			
		||||
	JUMP_TAG(state);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  call-seq:
 | 
			
		||||
 *     obj.respond_to?(symbol, include_private=false) => true or false
 | 
			
		||||
| 
						 | 
				
			
			@ -884,19 +648,11 @@ NORETURN(static void rb_longjmp _((int, VALUE)));
 | 
			
		|||
static VALUE make_backtrace _((void));
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
rb_longjmp(tag, mesg)
 | 
			
		||||
    int tag;
 | 
			
		||||
    VALUE mesg;
 | 
			
		||||
rb_longjmp(int tag, VALUE mesg)
 | 
			
		||||
{
 | 
			
		||||
    VALUE at;
 | 
			
		||||
    rb_thread_t *th = GET_THREAD();
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    //while (th->cfp->pc == 0 || th->cfp->iseq == 0) {
 | 
			
		||||
    //th->cfp++;
 | 
			
		||||
    //}
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    if (thread_set_raised(th)) {
 | 
			
		||||
	th->errinfo = exception_error;
 | 
			
		||||
	JUMP_TAG(TAG_FATAL);
 | 
			
		||||
| 
						 | 
				
			
			@ -943,7 +699,8 @@ rb_longjmp(tag, mesg)
 | 
			
		|||
 | 
			
		||||
    rb_trap_restore_mask();
 | 
			
		||||
    if (tag != TAG_FATAL) {
 | 
			
		||||
	/* EXEC_EVENT_HOOK(RUBY_EVENT_RAISE ...) */
 | 
			
		||||
	EXEC_EVENT_HOOK(th, RUBY_EVENT_RAISE, th->cfp->self,
 | 
			
		||||
			0 /* TODO: id */, 0 /* TODO: klass */);
 | 
			
		||||
    }
 | 
			
		||||
    thread_reset_raised(th);
 | 
			
		||||
    JUMP_TAG(tag);
 | 
			
		||||
| 
						 | 
				
			
			@ -1889,18 +1646,29 @@ rb_frame_self(void)
 | 
			
		|||
const char *
 | 
			
		||||
rb_sourcefile(void)
 | 
			
		||||
{
 | 
			
		||||
    rb_iseq_t *iseq = GET_THREAD()->cfp->iseq;
 | 
			
		||||
    if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
 | 
			
		||||
	return RSTRING_PTR(iseq->filename);
 | 
			
		||||
    rb_thread_t *th = GET_THREAD();
 | 
			
		||||
    rb_control_frame_t *cfp = th_get_ruby_level_cfp(th, th->cfp);
 | 
			
		||||
 | 
			
		||||
    if (cfp) {
 | 
			
		||||
	return RSTRING_PTR(cfp->iseq->filename);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
	return "";
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
rb_sourceline(void)
 | 
			
		||||
{
 | 
			
		||||
    rb_thread_t *th = GET_THREAD();
 | 
			
		||||
    return th_get_sourceline(th->cfp);
 | 
			
		||||
    rb_control_frame_t *cfp = th_get_ruby_level_cfp(th, th->cfp);
 | 
			
		||||
 | 
			
		||||
    if (cfp) {
 | 
			
		||||
	return th_get_sourceline(cfp);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
	return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
| 
						 | 
				
			
			@ -2991,8 +2759,6 @@ Init_eval(void)
 | 
			
		|||
    rb_define_global_function("trace_var", rb_f_trace_var, -1);	/* in variable.c */
 | 
			
		||||
    rb_define_global_function("untrace_var", rb_f_untrace_var, -1);	/* in variable.c */
 | 
			
		||||
 | 
			
		||||
    rb_define_global_function("set_trace_func", set_trace_func, 1);
 | 
			
		||||
 | 
			
		||||
    rb_define_virtual_variable("$SAFE", safe_getter, safe_setter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -239,4 +239,30 @@ void rb_thread_terminate_all(void);
 | 
			
		|||
 | 
			
		||||
#define ruby_cbase() th_get_cbase(GET_THREAD())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* tracer */
 | 
			
		||||
static void inline
 | 
			
		||||
exec_event_hooks(rb_event_hook_t *hook, rb_event_flag_t flag, VALUE self, ID id, VALUE klass)
 | 
			
		||||
{
 | 
			
		||||
    while (hook) {
 | 
			
		||||
	(*hook->func)(flag, hook->data, self, id, klass);
 | 
			
		||||
	hook = hook->next;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define EXEC_EVENT_HOOK(th, flag, self, id, klass) do { \
 | 
			
		||||
    rb_event_flag_t wait_event__ = th->event_flags; \
 | 
			
		||||
    if (UNLIKELY(wait_event__)) { \
 | 
			
		||||
	VALUE self__ = (self), klass__ = (klass); \
 | 
			
		||||
	ID id__ = (id); \
 | 
			
		||||
	if (wait_event__ & flag) { \
 | 
			
		||||
	    exec_event_hooks(th->event_hooks, flag, self__, id__, klass__); \
 | 
			
		||||
	} \
 | 
			
		||||
	if (wait_event__ & RUBY_EVENT_VM) { \
 | 
			
		||||
	    exec_event_hooks(th->vm->event_hooks, flag, self__, id__, klass__); \
 | 
			
		||||
	} \
 | 
			
		||||
    } \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* EVAL_INTERN_H_INCLUDED */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								insns.def
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								insns.def
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -843,7 +843,6 @@ definemethod
 | 
			
		|||
		       get_cref(GET_ISEQ(), GET_LFP()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
  @c setting
 | 
			
		||||
  @e make alias (if v_p is Qtrue, make valias)
 | 
			
		||||
| 
						 | 
				
			
			@ -1020,15 +1019,12 @@ postexe
 | 
			
		|||
 */
 | 
			
		||||
DEFINE_INSN
 | 
			
		||||
trace
 | 
			
		||||
(num_t flag, VALUE args)
 | 
			
		||||
(num_t nf)
 | 
			
		||||
()
 | 
			
		||||
()
 | 
			
		||||
{
 | 
			
		||||
    /* TODO: trace instruction design */
 | 
			
		||||
    if (th->vm->trace_flag & flag) {
 | 
			
		||||
	/* */
 | 
			
		||||
	args = Qnil;
 | 
			
		||||
    }
 | 
			
		||||
    rb_event_flag_t flag = nf;
 | 
			
		||||
    EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* TODO: id, klass */);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**********************************************************/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								iseq.c
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								iseq.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -200,6 +200,7 @@ static rb_compile_option_t COMPILE_OPTION_DEFAULT = {
 | 
			
		|||
    OPT_OPERANDS_UNIFICATION, /* int operands_unification; */
 | 
			
		||||
    OPT_INSTRUCTIONS_UNIFICATION, /* int instructions_unification; */
 | 
			
		||||
    OPT_STACK_CACHING, /* int stack_caching; */
 | 
			
		||||
    OPT_TRACE_INSTRUCTION,
 | 
			
		||||
};
 | 
			
		||||
static const rb_compile_option_t COMPILE_OPTION_FALSE;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -227,6 +228,7 @@ make_compile_option(rb_compile_option_t *option, VALUE opt)
 | 
			
		|||
	SET_COMPILE_OPTION(option, opt, operands_unification);
 | 
			
		||||
	SET_COMPILE_OPTION(option, opt, instructions_unification);
 | 
			
		||||
	SET_COMPILE_OPTION(option, opt, stack_caching);
 | 
			
		||||
	SET_COMPILE_OPTION(option, opt, trace_instruction);
 | 
			
		||||
#undef SET_COMPILE_OPTION
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								node.h
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								node.h
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -387,23 +387,6 @@ 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,8 @@ class TestSetTraceFunc < Test::Unit::TestCase
 | 
			
		|||
    eval("class Foo; end")
 | 
			
		||||
    set_trace_func nil
 | 
			
		||||
 | 
			
		||||
    assert_equal(["c-return", 18, :set_trace_func, TestSetTraceFunc],
 | 
			
		||||
                 events.shift)     # TODO
 | 
			
		||||
    assert_equal(["line", 19, :test_event, TestSetTraceFunc],
 | 
			
		||||
                 events.shift)     # a = 1
 | 
			
		||||
    assert_equal(["line", 20, :test_event, TestSetTraceFunc],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										289
									
								
								thread.c
									
										
									
									
									
								
							
							
						
						
									
										289
									
								
								thread.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -2393,6 +2393,289 @@ rb_exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* tracer */
 | 
			
		||||
 | 
			
		||||
static rb_event_hook_t *
 | 
			
		||||
alloc_event_fook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
 | 
			
		||||
{
 | 
			
		||||
    rb_event_hook_t *hook = ALLOC(rb_event_hook_t);
 | 
			
		||||
    hook->func = func;
 | 
			
		||||
    hook->flag = events;
 | 
			
		||||
    hook->data = data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
thread_reset_event_flags(rb_thread_t *th)
 | 
			
		||||
{
 | 
			
		||||
    rb_event_hook_t *hook = th->event_hooks;
 | 
			
		||||
    rb_event_flag_t flag = th->event_flags & RUBY_EVENT_VM;
 | 
			
		||||
 | 
			
		||||
    while (hook) {
 | 
			
		||||
	flag |= hook->flag;
 | 
			
		||||
	hook = hook->next;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
rb_thread_add_event_hook(rb_thread_t *th,
 | 
			
		||||
			 rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
 | 
			
		||||
{
 | 
			
		||||
    rb_event_hook_t *hook = alloc_event_fook(func, events, data);
 | 
			
		||||
    hook->next = th->event_hooks;
 | 
			
		||||
    th->event_hooks = hook;
 | 
			
		||||
    thread_reset_event_flags(th);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
set_threads_event_flags_i(st_data_t key, st_data_t val, st_data_t flag)
 | 
			
		||||
{
 | 
			
		||||
    VALUE thval = key;
 | 
			
		||||
    rb_thread_t *th;
 | 
			
		||||
    GetThreadPtr(thval, th);
 | 
			
		||||
 | 
			
		||||
    if (flag) {
 | 
			
		||||
	th->event_flags |= RUBY_EVENT_VM;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
	th->event_flags &= (~RUBY_EVENT_VM);
 | 
			
		||||
    }
 | 
			
		||||
    return ST_CONTINUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
set_threads_event_flags(int flag)
 | 
			
		||||
{
 | 
			
		||||
    st_foreach(GET_VM()->living_threads, set_threads_event_flags_i, (st_data_t) flag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
 | 
			
		||||
{
 | 
			
		||||
    rb_event_hook_t *hook = alloc_event_fook(func, events, data);
 | 
			
		||||
    rb_vm_t *vm = GET_VM();
 | 
			
		||||
 | 
			
		||||
    hook->next = vm->event_hooks;
 | 
			
		||||
    vm->event_hooks = hook;
 | 
			
		||||
 | 
			
		||||
    set_threads_event_flags(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
remove_event_hook(rb_event_hook_t **root, rb_event_hook_func_t func)
 | 
			
		||||
{
 | 
			
		||||
    rb_event_hook_t *prev = NULL, *hook = *root;
 | 
			
		||||
 | 
			
		||||
    while (hook) {
 | 
			
		||||
	if (func == 0 || hook->func == func) {
 | 
			
		||||
	    if (prev) {
 | 
			
		||||
		prev->next = hook->next;
 | 
			
		||||
	    }
 | 
			
		||||
	    else {
 | 
			
		||||
		*root = hook->next;
 | 
			
		||||
	    }
 | 
			
		||||
	    xfree(hook);
 | 
			
		||||
	}
 | 
			
		||||
	prev = hook;
 | 
			
		||||
	hook = hook->next;
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
rb_thread_remove_event_hook(rb_thread_t *th, rb_event_hook_func_t func)
 | 
			
		||||
{
 | 
			
		||||
    remove_event_hook(&th->event_hooks, func);
 | 
			
		||||
    thread_reset_event_flags(th);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
rb_remove_event_hook(rb_event_hook_func_t func)
 | 
			
		||||
{
 | 
			
		||||
    rb_vm_t *vm = GET_VM();
 | 
			
		||||
    rb_event_hook_t *hook = vm->event_hooks;
 | 
			
		||||
    int ret = remove_event_hook(&vm->event_hooks, func);
 | 
			
		||||
 | 
			
		||||
    if (hook != NULL && vm->event_hooks == NULL) {
 | 
			
		||||
	set_threads_event_flags(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
clear_trace_func_i(st_data_t key, st_data_t val, st_data_t flag)
 | 
			
		||||
{
 | 
			
		||||
    rb_thread_t *th;
 | 
			
		||||
    GetThreadPtr((VALUE)key, th);
 | 
			
		||||
    rb_thread_remove_event_hook(th, 0);
 | 
			
		||||
    return ST_CONTINUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
rb_clear_trace_func(void)
 | 
			
		||||
{
 | 
			
		||||
    st_foreach(GET_VM()->living_threads, clear_trace_func_i, (st_data_t) 0);
 | 
			
		||||
    rb_remove_event_hook(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  call-seq:
 | 
			
		||||
 *     set_trace_func(proc)    => proc
 | 
			
		||||
 *     set_trace_func(nil)     => nil
 | 
			
		||||
 *  
 | 
			
		||||
 *  Establishes _proc_ as the handler for tracing, or disables
 | 
			
		||||
 *  tracing if the parameter is +nil+. _proc_ takes up
 | 
			
		||||
 *  to six parameters: an event name, a filename, a line number, an
 | 
			
		||||
 *  object id, a binding, and the name of a class. _proc_ is
 | 
			
		||||
 *  invoked whenever an event occurs. Events are: <code>c-call</code>
 | 
			
		||||
 *  (call a C-language routine), <code>c-return</code> (return from a
 | 
			
		||||
 *  C-language routine), <code>call</code> (call a Ruby method),
 | 
			
		||||
 *  <code>class</code> (start a class or module definition),
 | 
			
		||||
 *  <code>end</code> (finish a class or module definition),
 | 
			
		||||
 *  <code>line</code> (execute code on a new line), <code>raise</code>
 | 
			
		||||
 *  (raise an exception), and <code>return</code> (return from a Ruby
 | 
			
		||||
 *  method). Tracing is disabled within the context of _proc_.
 | 
			
		||||
 *
 | 
			
		||||
 *      class Test
 | 
			
		||||
 *	def test
 | 
			
		||||
 *	  a = 1
 | 
			
		||||
 *	  b = 2
 | 
			
		||||
 *	end
 | 
			
		||||
 *      end
 | 
			
		||||
 *
 | 
			
		||||
 *      set_trace_func proc { |event, file, line, id, binding, classname|
 | 
			
		||||
 *	   printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
 | 
			
		||||
 *      }
 | 
			
		||||
 *      t = Test.new
 | 
			
		||||
 *      t.test
 | 
			
		||||
 *
 | 
			
		||||
 *	  line prog.rb:11               false
 | 
			
		||||
 *      c-call prog.rb:11        new    Class
 | 
			
		||||
 *      c-call prog.rb:11 initialize   Object
 | 
			
		||||
 *    c-return prog.rb:11 initialize   Object
 | 
			
		||||
 *    c-return prog.rb:11        new    Class
 | 
			
		||||
 *	  line prog.rb:12               false
 | 
			
		||||
 *  	  call prog.rb:2        test     Test
 | 
			
		||||
 *	  line prog.rb:3        test     Test
 | 
			
		||||
 *	  line prog.rb:4        test     Test
 | 
			
		||||
 *      return prog.rb:4        test     Test
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALUE klass);
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
set_trace_func(VALUE obj, VALUE trace)
 | 
			
		||||
{
 | 
			
		||||
    rb_vm_t *vm = GET_VM();
 | 
			
		||||
    rb_remove_event_hook(call_trace_func);
 | 
			
		||||
 | 
			
		||||
    if (NIL_P(trace)) {
 | 
			
		||||
	return Qnil;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!rb_obj_is_proc(trace)) {
 | 
			
		||||
	rb_raise(rb_eTypeError, "trace_func needs to be Proc");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL, trace);
 | 
			
		||||
    return trace;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
thread_add_trace_func(rb_thread_t *th, VALUE trace)
 | 
			
		||||
{
 | 
			
		||||
    if (!rb_obj_is_proc(trace)) {
 | 
			
		||||
	rb_raise(rb_eTypeError, "trace_func needs to be Proc");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rb_thread_add_event_hook(th, call_trace_func, RUBY_EVENT_ALL, trace);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
thread_add_trace_func_m(VALUE obj, VALUE trace)
 | 
			
		||||
{
 | 
			
		||||
    rb_thread_t *th;
 | 
			
		||||
    GetThreadPtr(obj, th);
 | 
			
		||||
    thread_add_trace_func(th, trace);
 | 
			
		||||
    return trace;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
thread_set_trace_func_m(VALUE obj, VALUE trace)
 | 
			
		||||
{
 | 
			
		||||
    rb_thread_t *th;
 | 
			
		||||
    GetThreadPtr(obj, th);
 | 
			
		||||
    rb_thread_remove_event_hook(th, call_trace_func);
 | 
			
		||||
 | 
			
		||||
    if (!NIL_P(trace)) {
 | 
			
		||||
	return Qnil;
 | 
			
		||||
    }
 | 
			
		||||
    thread_add_trace_func(th, trace);
 | 
			
		||||
    return trace;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *
 | 
			
		||||
get_event_name(rb_event_flag_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(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
 | 
			
		||||
{
 | 
			
		||||
    rb_thread_t *th = GET_THREAD();
 | 
			
		||||
    int state, raised;
 | 
			
		||||
    VALUE eventname = rb_str_new2(get_event_name(event));
 | 
			
		||||
    VALUE filename = rb_str_new2(rb_sourcefile());
 | 
			
		||||
    int line = rb_sourceline();
 | 
			
		||||
 | 
			
		||||
    if (th->tracing) {
 | 
			
		||||
	return;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
	th->tracing = 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    raised = thread_reset_raised(th);
 | 
			
		||||
 | 
			
		||||
    PUSH_TAG();
 | 
			
		||||
    if ((state = EXEC_TAG()) == 0) {
 | 
			
		||||
	proc_invoke(proc, rb_ary_new3(6,
 | 
			
		||||
				      eventname, filename, INT2FIX(line),
 | 
			
		||||
				      id ? ID2SYM(id) : Qnil,
 | 
			
		||||
				      self ? rb_binding_new() : Qnil,
 | 
			
		||||
				      klass ? klass : Qnil), Qundef, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (raised) {
 | 
			
		||||
	thread_set_raised(th);
 | 
			
		||||
    }
 | 
			
		||||
    POP_TAG();
 | 
			
		||||
 | 
			
		||||
    th->tracing = 0;
 | 
			
		||||
    if (state) {
 | 
			
		||||
	JUMP_TAG(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  +Thread+ encapsulates the behavior of a thread of
 | 
			
		||||
| 
						 | 
				
			
			@ -2488,6 +2771,12 @@ Init_Thread(void)
 | 
			
		|||
    rb_define_method(rb_cCont, "[]", rb_cont_call, -1);
 | 
			
		||||
    rb_define_global_function("callcc", rb_callcc, 0);
 | 
			
		||||
 | 
			
		||||
    /* trace */
 | 
			
		||||
    rb_define_global_function("set_trace_func", set_trace_func, 1);
 | 
			
		||||
    rb_define_method(rb_cThread, "set_trace_func", thread_set_trace_func_m, 1);
 | 
			
		||||
    rb_define_method(rb_cThread, "add_trace_func", thread_add_trace_func_m, 1);
 | 
			
		||||
 | 
			
		||||
    /* init thread core */
 | 
			
		||||
    Init_native_thread();
 | 
			
		||||
    {
 | 
			
		||||
	/* main thread setting */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										35
									
								
								vm.c
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								vm.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -534,25 +534,28 @@ th_call0(rb_thread_t *th, VALUE klass, VALUE recv,
 | 
			
		|||
	  break;
 | 
			
		||||
      }
 | 
			
		||||
      case NODE_CFUNC: {
 | 
			
		||||
	  rb_control_frame_t *reg_cfp = th->cfp;
 | 
			
		||||
	  rb_control_frame_t *cfp =
 | 
			
		||||
	    push_frame(th, 0, FRAME_MAGIC_CFUNC,
 | 
			
		||||
		       recv, (VALUE)blockptr, 0, reg_cfp->sp, 0, 1);
 | 
			
		||||
	  EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
 | 
			
		||||
	  {
 | 
			
		||||
	      rb_control_frame_t *reg_cfp = th->cfp;
 | 
			
		||||
	      rb_control_frame_t *cfp =
 | 
			
		||||
		push_frame(th, 0, FRAME_MAGIC_CFUNC,
 | 
			
		||||
			   recv, (VALUE)blockptr, 0, reg_cfp->sp, 0, 1);
 | 
			
		||||
 | 
			
		||||
	  cfp->callee_id = oid;
 | 
			
		||||
	  cfp->method_id = id;
 | 
			
		||||
	  cfp->method_klass = klass;
 | 
			
		||||
	      cfp->callee_id = oid;
 | 
			
		||||
	      cfp->method_id = id;
 | 
			
		||||
	      cfp->method_klass = klass;
 | 
			
		||||
 | 
			
		||||
	  val = call_cfunc(body->nd_cfnc, recv, body->nd_argc, argc, argv);
 | 
			
		||||
	      val = call_cfunc(body->nd_cfnc, recv, body->nd_argc, argc, argv);
 | 
			
		||||
 | 
			
		||||
	  if (reg_cfp != th->cfp + 1) {
 | 
			
		||||
	      SDR2(reg_cfp);
 | 
			
		||||
	      SDR2(th->cfp-5);
 | 
			
		||||
	      rb_bug("cfp consistency error - call0");
 | 
			
		||||
	      th->cfp = reg_cfp;
 | 
			
		||||
	      if (reg_cfp != th->cfp + 1) {
 | 
			
		||||
		  SDR2(reg_cfp);
 | 
			
		||||
		  SDR2(th->cfp-5);
 | 
			
		||||
		  rb_bug("cfp consistency error - call0");
 | 
			
		||||
		  th->cfp = reg_cfp;
 | 
			
		||||
	      }
 | 
			
		||||
	      pop_frame(th);
 | 
			
		||||
	  }
 | 
			
		||||
	  pop_frame(th);
 | 
			
		||||
 | 
			
		||||
	  EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass);
 | 
			
		||||
	  break;
 | 
			
		||||
      }
 | 
			
		||||
      case NODE_ATTRSET:{
 | 
			
		||||
| 
						 | 
				
			
			@ -1472,6 +1475,8 @@ yarv_init_redefined_flag(void)
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "vm_evalbody.ci"
 | 
			
		||||
 | 
			
		||||
/*                  finish
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										35
									
								
								vm_macro.def
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								vm_macro.def
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -56,23 +56,28 @@ MACRO macro_eval_setup_send_arguments(num, blockptr, flag, blockiseq)
 | 
			
		|||
 | 
			
		||||
MACRO macro_eval_invoke_cfunc(num, id, recv, klass, mn, blockptr)
 | 
			
		||||
{
 | 
			
		||||
    rb_control_frame_t *cfp =
 | 
			
		||||
      push_frame(th, 0, FRAME_MAGIC_CFUNC,
 | 
			
		||||
		 recv, (VALUE) blockptr, 0, GET_SP(), 0, 1);
 | 
			
		||||
    cfp->callee_id = id; /* TODO */
 | 
			
		||||
    cfp->method_id = id;
 | 
			
		||||
    cfp->method_klass = klass;
 | 
			
		||||
    
 | 
			
		||||
    reg_cfp->sp -= num + 1;
 | 
			
		||||
    EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass);
 | 
			
		||||
    {
 | 
			
		||||
	rb_control_frame_t *cfp =
 | 
			
		||||
	  push_frame(th, 0, FRAME_MAGIC_CFUNC,
 | 
			
		||||
		     recv, (VALUE) blockptr, 0, GET_SP(), 0, 1);
 | 
			
		||||
	cfp->callee_id = id; /* TODO */
 | 
			
		||||
	cfp->method_id = id;
 | 
			
		||||
	cfp->method_klass = klass;
 | 
			
		||||
 | 
			
		||||
    val = call_cfunc(mn->nd_cfnc, recv, mn->nd_argc, num, reg_cfp->sp + 1);
 | 
			
		||||
    if (reg_cfp != th->cfp + 1) {
 | 
			
		||||
	SDR2(reg_cfp);
 | 
			
		||||
	SDR2(th->cfp-5);
 | 
			
		||||
	rb_bug("cfp consistency error - send");
 | 
			
		||||
	th->cfp = reg_cfp;
 | 
			
		||||
	reg_cfp->sp -= num + 1;
 | 
			
		||||
 | 
			
		||||
	val = call_cfunc(mn->nd_cfnc, recv, mn->nd_argc, num, reg_cfp->sp + 1);
 | 
			
		||||
 | 
			
		||||
	if (reg_cfp != th->cfp + 1) {
 | 
			
		||||
	    SDR2(reg_cfp);
 | 
			
		||||
	    SDR2(th->cfp-5);
 | 
			
		||||
	    rb_bug("cfp consistency error - send");
 | 
			
		||||
	    th->cfp = reg_cfp;
 | 
			
		||||
	}
 | 
			
		||||
	pop_frame(th);
 | 
			
		||||
    }
 | 
			
		||||
    pop_frame(th);
 | 
			
		||||
    EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MACRO macro_eval_invoke_func(niseqval, recv, klass, blockptr, num)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,7 @@
 | 
			
		|||
 | 
			
		||||
/* VM running option */
 | 
			
		||||
#define OPT_CHECKED_RUN              1
 | 
			
		||||
#define OPT_TRACE_INSTRUCTION        1
 | 
			
		||||
 | 
			
		||||
/* at compile */
 | 
			
		||||
#define OPT_INLINE_CONST_CACHE       1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								yarvcore.c
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								yarvcore.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -167,6 +167,15 @@ vm_mark_each_thread_func(st_data_t key, st_data_t value, st_data_t dummy)
 | 
			
		|||
    return ST_CONTINUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
mark_event_hooks(rb_event_hook_t *hook)
 | 
			
		||||
{
 | 
			
		||||
    while (hook) {
 | 
			
		||||
	rb_gc_mark(hook->data);
 | 
			
		||||
	hook = hook->next;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
vm_mark(void *ptr)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +190,8 @@ vm_mark(void *ptr)
 | 
			
		|||
	MARK_UNLESS_NULL(vm->mark_object_ary);
 | 
			
		||||
	MARK_UNLESS_NULL(vm->last_status);
 | 
			
		||||
	MARK_UNLESS_NULL(vm->loaded_features);
 | 
			
		||||
 | 
			
		||||
	mark_event_hooks(vm->event_hooks);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MARK_REPORT_LEAVE("vm");
 | 
			
		||||
| 
						 | 
				
			
			@ -289,6 +300,8 @@ thread_mark(void *ptr)
 | 
			
		|||
				 (VALUE *)(&th->machine_regs) +
 | 
			
		||||
				 sizeof(th->machine_regs) / sizeof(VALUE));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mark_event_hooks(th->event_hooks);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MARK_UNLESS_NULL(th->stat_insn_usage);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										32
									
								
								yarvcore.h
									
										
									
									
									
								
							
							
						
						
									
										32
									
								
								yarvcore.h
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -186,6 +186,7 @@ typedef struct rb_compile_option_struct {
 | 
			
		|||
    int operands_unification;
 | 
			
		||||
    int instructions_unification;
 | 
			
		||||
    int stack_caching;
 | 
			
		||||
    int trace_instruction;
 | 
			
		||||
} rb_compile_option_t;
 | 
			
		||||
 | 
			
		||||
struct iseq_compile_data {
 | 
			
		||||
| 
						 | 
				
			
			@ -304,6 +305,29 @@ struct rb_iseq_struct {
 | 
			
		|||
 | 
			
		||||
typedef struct rb_iseq_struct rb_iseq_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
 | 
			
		||||
#define RUBY_EVENT_VM      0x100
 | 
			
		||||
 | 
			
		||||
typedef unsigned int rb_event_flag_t;
 | 
			
		||||
typedef void (*rb_event_hook_func_t)(rb_event_flag_t, VALUE data, VALUE, ID, VALUE klass);
 | 
			
		||||
 | 
			
		||||
typedef struct rb_event_hook_struct {
 | 
			
		||||
    rb_event_flag_t flag;
 | 
			
		||||
    rb_event_hook_func_t func;
 | 
			
		||||
    VALUE data;
 | 
			
		||||
    struct rb_event_hook_struct *next;
 | 
			
		||||
} rb_event_hook_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define GetVMPtr(obj, ptr) \
 | 
			
		||||
  Data_Get_Struct(obj, rb_vm_t, ptr)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -332,6 +356,9 @@ typedef struct rb_vm_struct {
 | 
			
		|||
    /* signal */
 | 
			
		||||
    rb_atomic_t signal_buff[RUBY_NSIG];
 | 
			
		||||
    rb_atomic_t bufferd_signal_size;
 | 
			
		||||
 | 
			
		||||
    /* hook */
 | 
			
		||||
    rb_event_hook_t *event_hooks;
 | 
			
		||||
} rb_vm_t;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -456,6 +483,11 @@ struct rb_thread_struct
 | 
			
		|||
    /* statistics data for profiler */
 | 
			
		||||
    VALUE stat_insn_usage;
 | 
			
		||||
 | 
			
		||||
    /* tracer */
 | 
			
		||||
    rb_event_hook_t *event_hooks;
 | 
			
		||||
    rb_event_flag_t event_flags;
 | 
			
		||||
    int tracing;
 | 
			
		||||
 | 
			
		||||
    /* misc */
 | 
			
		||||
    int method_missing_reason;
 | 
			
		||||
    int abort_on_exception;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue