aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--method.h2
-rw-r--r--proc.c8
-rw-r--r--spec/ruby/core/tracepoint/parameters_spec.rb21
-rw-r--r--test/ruby/test_settracefunc.rb39
-rw-r--r--vm_trace.c49
5 files changed, 115 insertions, 4 deletions
diff --git a/method.h b/method.h
index 3e424fbc02..9e0f5e7c47 100644
--- a/method.h
+++ b/method.h
@@ -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 */
diff --git a/proc.c b/proc.c
index 420dc60256..358672c018 100644
--- a/proc.c
+++ b/proc.c
@@ -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);