diff options
author | mame <mame@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-06-03 05:10:41 +0000 |
---|---|---|
committer | mame <mame@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-06-03 05:10:41 +0000 |
commit | cd0fec37281a72d402981894b764d27ab7d1fb39 (patch) | |
tree | 7798f0f28cbf31b54fd44887585c878c35277a76 | |
parent | 25d1891899be492dc64bb6852741d4f112feba64 (diff) |
Add TracePoint#parameters
It can be used to get the parameters' information of method and block.
There was no way to get block parameters.
It was possible but ineffective to get method parameters via Method
object: `tp.defined_class.method(tp.method_id).parameters`
TracePoint#parameters allows us to get the information easily.
[Feature #14694]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63562 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-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); |