【问题标题】:Ruby - Automatically pass binding() to method like eval?Ruby - 自动将绑定()传递给像eval这样的方法?
【发布时间】:2022-06-11 10:14:30
【问题描述】:

Ruby 2.7.0

我正在写一些类似于“eval”方法的东西,我意识到似乎发生了一些神奇的事情。

Ruby 的 Kernel#eval 能够获得绑定,但不知何故,当未指定绑定时,它设法将调用者绑定作为默认值。

举个例子:

def my_eval(str)
  "?? how do I access #{str}"
  #foo  # <- NameError
end

foo = "BAR"

var = 'foo'
puts "EVAL: "+eval(var)
puts "MY_EVAL: "+my_eval(var)

我们用'var'调用eval,它是一个包含单词“foo”的字符串。不知何故,eval 能够访问调用者 local_variables,就好像它被神奇地用 'binding()' 调用了

my_eval 方法就没那么幸运了。如何获取调用者绑定或让绑定随方法调用自动发送,而无需要求该方法的所有用户将“binding()”添加到参数列表中?

或者这种魔法是否仅限于 Kernel#eval 并且无法复制?

编辑:

发现类似问题后:How can I get the binding from method_missing?

我使用 TracePoint(set_trace_func 的干净版本)提出了一个(稍微不那么脆弱的)答案。

但这需要在我的代码之上运行一个简单的调试器,这不仅令人担忧,而且大大降低了我的代码速度(简单的绑定跟踪运行速度慢了 4 倍!)。 ruby 是否有这种类型的内部反射而不使用跟踪器,或者可能将 my_eval 编写为 C 函数?

【问题讨论】:

  • 我发现了“binding_of_caller”gem,它依赖于 RubyVM::DebugInspector(至少对于 MRI),它是另一个具有 C 扩展的 gem,这让我开始怀疑这是做不到的。我很惊讶地看到 eval 可能在这方面作弊。 :(
  • 请注意,如果您创建一个方法def kernel_eval(str); eval(str); end; 并调用为kernel_eval(var) 结果是相同的,因为foo 在该范围内未定义。
  • @engineersmnky 正是这一点。那么我们如何让 kernel_eval 以与 eval 相同的方式获取调用者的范围呢? eval 只是一种方法。
  • 每一种编程语言都会有一些基本的操作无法在编程语言本身中解释。在 Ruby 中,其中包括直接与内存交互的Class#allocateModule#privateModule#protectedModule#publicModule#define_methodModule#define_singleton_method,以及各种*_eval*_exec 方法.例如,在其他语言中,考虑 Java 有重载运算符但没有重载运算符。
  • @DavidLjungMadisonStellar 另外,您可以查看 rubinius(最初是用 ruby​​ 编写的 ruby​​ VM)源代码,了解它们如何实现 Kernel#evalKernel#caller。虽然我没有花时间去追踪它,但它可以为您提供一个有用的方向。

标签: ruby scope


【解决方案1】:

好的,事实证明没有简单的答案。我在这里整理了一份清单:

Ruby: Binding of Caller Solutions

它分为以下几种可能性(在上面的链接中进行了扩展):

  1. 使用 binding_of_caller gem
  2. 使用一个真正鬼鬼祟祟的纯 ruby​​ 技巧,该技巧仅在您的方法不使用 args 时才有效(涉及 define_singleton_method,然后将 (>>) Object::method(:binding) 与您的方法组合)
  3. 使用 TracePoint API 获得速度惊人的纯 ruby​​ 解决方案
  4. 在 C 扩展中调用 rb_eval_string_protect("binding"),在 Ruby 3.2.0 之前,它将为您提供所有绑定(但接收器已损坏)
  5. 使用我精简的 C 扩展,从 binding_of_caller 所依赖的 gem 中提取重要部分 (debug_inspector)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-30
    • 1970-01-01
    • 2014-01-05
    • 1970-01-01
    • 2021-07-19
    • 1970-01-01
    相关资源
    最近更新 更多