红宝石猴子修补陷阱
我正在寻找一些例子来说明为什么在 ruby 中扩展基类不是一个好主意。我需要向一些人展示为什么它是一种需要小心使用的武器。
有什么恐怖故事可以分享吗?
I'm looking for examples of why it's not a good idea to extend base classes in ruby. I need to show some people why it's a weapon to be wielded carefully.
Any horror stories you can share?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
有一个非常著名的猴子补丁严重错误的例子 大约 2.5 年前在 Rubinius。
这个案例的有趣之处在于,违规代码和受害者都是非常明显且非常不寻常的。通常,犯罪者是一些 PHP 脚本小子编写的代码,他沉迷于他的 1337 元编程 h4X0r 技能。失败模式是一个简单的ArgumentError异常,因为原始方法和monkeypatch有不同的数量。
然而,在这种情况下,罪魁祸首是 stdlib (
mathn
) 中的一个库,而故障模式是 Rubinius VM 完全崩溃。那么,发生了什么?好吧,
mathn
猴子修补了Fixnum
类并更改了Fixnum
算术的工作方式。特别是,它改变了几个核心方法的结果和类型。例如:问题当然是在 Rubinius 中,整个 Ruby 编译器、整个 Ruby 内核、大部分 Ruby 核心库、Rubinius VM 的某些部分以及 Rubinius 基础设施的其他部分,都是用 Ruby 编写的。当然,所有这些都到处使用 Fixnum 算术。
Hash
类是用 Ruby 编写的,它使用Fixnum
算术来计算哈希桶的大小、计算哈希函数等。Array
是用 Ruby 编写的,需要计算元素大小和数组长度。 FFI 库是用 Ruby 编写的,需要计算内存地址(!)和结构大小。 Rubinius 的许多部分假设它们可以执行一些Fixnum
算术,然后将结果作为指针或int
传递给某些 C 函数。而且由于 Ruby 不支持任何类型的选择器命名空间或类装箱或类似的东西(尽管 Ruby 2.0 中计划有类似的东西),一旦某些随机用户代码需要
mathn
库,所有这些片段会惊人地爆炸,因为突然间,Fixnum 操作的结果不再是 Fixnum(与机器 int 基本相同) /code> 并且可以这样传递),而是一个Rational
(这是一个成熟的 Ruby 对象)。基本上,会发生的情况是,某些代码
需要“mathn”
(或者您将其输入到 IRb 中),然后虚拟机就会立即死亡。在这种情况下,解决方案是编译器的安全数学插件:当编译器检测到它正在编译 Rubinius 的内核或其他核心部分时,它会自动重写对
Fixnum< 的调用/code> 方法调用这些方法的私有不可变副本。 [注意:我认为在当前版本的 Rubinius 中,问题是通过不同的方式解决的。]
There was a pretty famous example of monkey-patching going horribly wrong about 2.5 years ago in Rubinius.
The interesting thing about this case is that both the offending code and the victim were highly visible and highly unusual. Usually, the offender is some piece of code written by some PHP script kiddy who got drunk on his 1337 metaprogramming h4X0r skillz. And the failure mode is a simple
ArgumentError
exception, because the original method and the monkeypatch have different arity.However, in this case, the offender was a library in the stdlib (
mathn
) and the failure mode was the Rubinius VM completely blowing up.So, what happened? Well,
mathn
monkeypatches theFixnum
class and changes howFixnum
arithmetic works. In particular, it changes both the results and the types of several core methods. E.g.:The problem is of course that in Rubinius, the entire Ruby compiler, the entire Ruby kernel, large parts of the Ruby core library, some parts of the Rubinius VM and other parts of the Rubinius infrastructure, are all written in Ruby. And of course, all of those use
Fixnum
arithmetic all over the place.The
Hash
class is written in Ruby and it usesFixnum
arithmetic to compute the size of the hash buckets, compute the hash function and so on.Array
is written in Ruby and needs to compute element sizes and array lengths. The FFI library is written in Ruby and needs to compute memory addresses(!) and structure sizes. Many parts of Rubinius assume that they can do someFixnum
arithmetic and then pass the result to some C function as a pointer orint
.And since Ruby doesn't support any kind of selector namespacing or class boxing or similar (although something like that is planned for Ruby 2.0), as soon as some random user code requires the
mathn
library, all of those pieces just spectacularly explode, because all of a sudden, the result of aFixnum
operation is no longer aFixnum
(which is basically identical to a machineint
and can be passed around as such), but aRational
(which is a full-fledged Ruby object).Basically, what would happen, is that some code would
require 'mathn'
(or you would type that into IRb), and immediately the VM would just die.The solution, in this case, was the safe math plugin for the compiler: when the compiler detects that it is compiling the kernel or other core parts of Rubinius, it automatically rewrites calls to
Fixnum
methods into calls to private immutable copies of those methods. [Note: I think in current versions of Rubinius, the problem is solved in a different way.]失败的三连胜;或者,如何为 Ruby 1.8.7 修补 Rails 2.0 有一个 Rails 的示例(这是一个经过严格审查的大型项目),因为他们对
String
进行了 Monkeypatched 来添加方法 <代码>字符。The Trifecta of FAIL; or, how to patch Rails 2.0 for Ruby 1.8.7 has an example of Rails (which is a large, well-scrutinized project) causing problems because they monkeypatched
String
to add the methodchars
.一个明显的陷阱是名称冲突 - 如果两个或多个包为行为不同的方法选择相同的名称。
One obvious pitfall would be name collisions - if two or more packages choose the same name for a method that behaves differently.