1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

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
This commit is contained in:
mame 2018-06-03 05:10:41 +00:00
parent 25d1891899
commit cd0fec3728
5 changed files with 115 additions and 4 deletions

View file

@ -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 */

8
proc.c
View file

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

View file

@ -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

View file

@ -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

View file

@ -804,6 +804,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)
{
@ -919,6 +958,15 @@ tracepoint_attr_path(VALUE tpval)
return rb_tracearg_path(get_trace_arg());
}
/*
* 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
*/
@ -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);