使用Ruby的TracePoint获取方法参数

我可以使用TracePoint API访问Ruby方法的参数:

def foo(foo_arg) end trace = TracePoint.trace(:call, :c_call) do |tp| tp.disable case tp.method_id when :foo, :sub method = eval("method(:#{tp.method_id})", tp.binding) method.parameters.each do |p| puts "#{p.last}: #{tp.binding.local_variable_get(p.last)}" end end tp.enable end trace.enable foo(10) # => foo_arg: 10 

但是,当我尝试使用ac方法调用时,我收到一个错误。

 "foo".sub(/(f)/) { $1.upcase } script.rb:20:in `method': undefined method `sub' for class `Object' (NameError) from script.rb:20:in `' from script.rb:8:in `eval' from script.rb:8:in `block in ' from script.rb:20:in `' 

这似乎是因为使用C方法调用时返回的绑定与常规Ruby方法调用之间的差异。

在Ruby的情况下, tp.self等于tp.binding.eval("self")main但是在C情况下tp.self"foo"tp.binding.eval("self")main 。 有没有办法将参数传递给使用TracePoint的方法用于Ruby和C定义的方法?

当你指出你的问题并且在ruby文档中记录时 , tp.self返回一个跟踪对象,它有一个你正在寻找的method方法。 我想你应该用

method = tp.self.method(tp.method_id)

代替

method = eval("method(:#{tp.method_id})", tp.binding)

更新 。 关于你最后一段的一些解释。 tp.self在第一种情况下(当你调用foo )是指向main ,因为你在主上下文中定义了foo方法,并且它在第二种情况下指向String对象,因为sub在那里被定义。 但是tp.binding.eval("self")在两种情况下都返回main ,因为它返回一个调用上下文(不是你期望的’define’上下文),并且在两种情况下它都是main

更新(回复评论)我认为这样做的唯一方法是修补补丁sub和你感兴趣的所有其他方法。 代码示例:

 class String alias_method :old_sub, :sub def sub(*args, &block) old_sub(*args, &block) end end trace = TracePoint.trace(:call, :c_call) do |tp| tp.disable case tp.method_id when :sub method = tp.self.method(tp.method_id) puts method.parameters.inspect end tp.enable end trace.enable "foo".sub(/(f)/) { |s| s.upcase } 

一个很大的缺点是您不能在原始块中使用$1, $2, ... vars。 正如这里指出的那样,没有办法让它起作用。 但是,您仍然可以使用块参数(在我的示例中为s )。