diff options
-rw-r--r-- | method.h | 2 | ||||
-rw-r--r-- | proc.c | 8 | ||||
-rw-r--r-- | spec/ruby/core/tracepoint/parameters_spec.rb | 21 | ||||
-rw-r--r-- | test/ruby/test_settracefunc.rb | 39 | ||||
-rw-r--r-- | vm_trace.c | 49 |
5 files changed, 115 insertions, 4 deletions
@@ -218,4 +218,6 @@ void rb_method_entry_copy(rb_method_entry_t *dst, const rb_method_entry_t *src); void rb_scope_visibility_set(rb_method_visibility_t); +VALUE rb_unnamed_parameters(int arity); + #endif /* RUBY_METHOD_H */ @@ -1148,8 +1148,8 @@ rb_proc_location(VALUE self) return iseq_location(rb_proc_get_iseq(self, 0)); } -static VALUE -unnamed_parameters(int arity) +VALUE +rb_unnamed_parameters(int arity) { VALUE a, param = rb_ary_new2((arity < 0) ? -arity : arity); int n = (arity < 0) ? ~arity : arity; @@ -1183,7 +1183,7 @@ rb_proc_parameters(VALUE self) int is_proc; const rb_iseq_t *iseq = rb_proc_get_iseq(self, &is_proc); if (!iseq) { - return unnamed_parameters(rb_proc_arity(self)); + return rb_unnamed_parameters(rb_proc_arity(self)); } return rb_iseq_parameters(iseq, is_proc); } @@ -2567,7 +2567,7 @@ rb_method_parameters(VALUE method) { const rb_iseq_t *iseq = rb_method_iseq(method); if (!iseq) { - return unnamed_parameters(method_arity(method)); + return rb_unnamed_parameters(method_arity(method)); } return rb_iseq_parameters(iseq, 0); } diff --git a/spec/ruby/core/tracepoint/parameters_spec.rb b/spec/ruby/core/tracepoint/parameters_spec.rb new file mode 100644 index 0000000000..4728b0a448 --- /dev/null +++ b/spec/ruby/core/tracepoint/parameters_spec.rb @@ -0,0 +1,21 @@ +require_relative '../../spec_helper' + +describe 'TracePoint#parameters' do + it 'returns the parameters of block' do + f = proc {|x, y, z| } + parameters = nil + TracePoint.new(:b_call) {|tp| parameters = tp.parameters }.enable do + f.call + parameters.should == [[:opt, :x], [:opt, :y], [:opt, :z]] + end + end + + it 'returns the parameters of lambda block' do + f = lambda {|x, y, z| } + parameters = nil + TracePoint.new(:b_call) {|tp| parameters = tp.parameters }.enable do + f.call(1, 2, 3) + parameters.should == [[:req, :x], [:req, :y], [:req, :z]] + end + end +end diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index 7157d8c482..fe2201a30e 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -703,6 +703,45 @@ class TestSetTraceFunc < Test::Unit::TestCase assert_equal(false, trace.enabled?) end + def parameter_test(a, b, c) + yield + end + + def test_tracepoint_parameters + trace = TracePoint.new(:line, :class, :end, :call, :return, :b_call, :b_return, :c_call, :c_return, :raise){|tp| + next if !target_thread? + next if tp.path != __FILE__ + case tp.event + when :call, :return + assert_equal([[:req, :a], [:req, :b], [:req, :c]], tp.parameters) + when :b_call, :b_return + next if tp.parameters == [] + if tp.parameters.first == [:opt, :x] + assert_equal([[:opt, :x], [:opt, :y], [:opt, :z]], tp.parameters) + else + assert_equal([[:req, :p], [:req, :q], [:req, :r]], tp.parameters) + end + when :c_call, :c_return + assert_equal([[:req]], tp.parameters) if tp.method_id == :getbyte + when :line, :class, :end, :raise + assert_raise(RuntimeError) { tp.parameters } + end + } + obj = Object.new + trace.enable{ + parameter_test(1, 2, 3) {|x, y, z| + } + lambda {|p, q, r| }.call(4, 5, 6) + "".getbyte(0) + class << obj + end + begin + raise + rescue + end + } + end + def method_test_tracepoint_return_value obj obj end diff --git a/vm_trace.c b/vm_trace.c index bae4649cf7..10ee1ca18e 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -805,6 +805,45 @@ fill_id_and_klass(rb_trace_arg_t *trace_arg) } VALUE +rb_tracearg_parameters(rb_trace_arg_t *trace_arg) +{ + switch(trace_arg->event) { + case RUBY_EVENT_CALL: + case RUBY_EVENT_RETURN: + case RUBY_EVENT_B_CALL: + case RUBY_EVENT_B_RETURN: { + const rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(trace_arg->ec, trace_arg->cfp); + if (cfp) { + int is_proc = 0; + if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_BLOCK && !VM_FRAME_LAMBDA_P(cfp)) { + is_proc = 1; + } + return rb_iseq_parameters(cfp->iseq, is_proc); + } + break; + } + case RUBY_EVENT_C_CALL: + case RUBY_EVENT_C_RETURN: { + fill_id_and_klass(trace_arg); + if (trace_arg->klass && trace_arg->id) { + const rb_method_entry_t *me; + VALUE iclass = Qnil; + me = rb_method_entry_without_refinements(trace_arg->klass, trace_arg->id, &iclass); + return rb_unnamed_parameters(rb_method_entry_arity(me)); + } + break; + } + case RUBY_EVENT_RAISE: + case RUBY_EVENT_LINE: + case RUBY_EVENT_CLASS: + case RUBY_EVENT_END: + rb_raise(rb_eRuntimeError, "not supported by this event"); + break; + } + return Qnil; +} + +VALUE rb_tracearg_method_id(rb_trace_arg_t *trace_arg) { fill_id_and_klass(trace_arg); @@ -920,6 +959,15 @@ tracepoint_attr_path(VALUE tpval) } /* + * Return the parameters of the method or block that the current hook belongs to + */ +static VALUE +tracepoint_attr_parameters(VALUE tpval) +{ + return rb_tracearg_parameters(get_trace_arg()); +} + +/* * Return the name at the definition of the method being called */ static VALUE @@ -1502,6 +1550,7 @@ Init_vm_trace(void) rb_define_method(rb_cTracePoint, "event", tracepoint_attr_event, 0); rb_define_method(rb_cTracePoint, "lineno", tracepoint_attr_lineno, 0); rb_define_method(rb_cTracePoint, "path", tracepoint_attr_path, 0); + rb_define_method(rb_cTracePoint, "parameters", tracepoint_attr_parameters, 0); rb_define_method(rb_cTracePoint, "method_id", tracepoint_attr_method_id, 0); rb_define_method(rb_cTracePoint, "callee_id", tracepoint_attr_callee_id, 0); rb_define_method(rb_cTracePoint, "defined_class", tracepoint_attr_defined_class, 0); |