我可以覆盖用C编写的Ruby方法吗?

And*_*imm 8 c ruby overriding

是否有可能覆盖属于Ruby本身的方法,例如rb_error_frozen用C语言编写的Ruby代码?

背景:我想知道在修改冻结对象时是否可以使Ruby仅记录警告,而不是引发异常.这样,我可以记录各种状态修改,而不是在第一个修改时停止.

我主要是想用YARV来做这件事,但如果能让它变得更容易,我可以使用另一个实现.

是的,这是一个崭新的项目!不要在生产环境中尝试这个!

emb*_*oss 5

我只能说MRI/YARV,但我会试一试.如果已将C函数显式定义为Ruby对象上的方法,则只能覆盖Ruby中源自C的函数.例如,Kernel#extend在C中明确定义为

rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1);
Run Code Online (Sandbox Code Playgroud)

因为C函数rb_obj_extend已被"链接"(在引号中,因为我比喻说,我不是指C链接在这里)与Kernel#extendRuby世界中的方法,理论上你可以覆盖rb_obj_extend的行为,如果你覆盖Kernel#extend.

我会说,鉴于以下两个条件,你可以声称你实际上"覆盖"了一个rb_*C函数:

  • rb_*C函数已经与某些Ruby对象"链接"了,所以我们在Ruby世界中有一个句柄作为我们可以覆盖的钩子
  • 给定的rb_*方法仅用于这一点,正是为了这个目的,它在其他任何地方都没有被重用

现在,如果你看rb_error_frozen它,它既不满足这两个条件.它是C实现中的一个帮助器,这意味着它是从几个地方调用的.并且它没有与任何Ruby对象明确地"链接",因此你没有可以覆盖它的钩子.

但并非所有人都失去了.你不能直接覆盖rb_error_frozen,但你仍然可以尝试覆盖所有Ruby方法,其中rb_error_frozen冒泡到"Ruby表面".我的意思是你可以检查C源中rb_error_frozen使用的所有位置,并从这些地方尝试找到可能触发这些代码的每个Ruby方法.如果这是一个封闭的集合,您可以简单地覆盖所有这些方法,以"事实上覆盖" rb_error_frozen的行为.

然而,这只是一个拼凑的解决方案.如果有人决定再写一个C扩展名,他们再次rb_error_frozen直接打电话,你所有的辛苦工作都会丢失.

长话短说:如果已经明确定义为Ruby对象的某些方法的实现,则只能覆盖C函数,例如

rb_define_method(rb_cString, "gsub", rb_str_gsub, -1);
Run Code Online (Sandbox Code Playgroud)

你可以假设它只会用于那个目的.但即便如此,你也不是100%安全,有人仍然可以决定在C代码的其他部分重用该功能.


编辑:你说你只喜欢Ruby而不是在修改冻结对象时提出警告.我刚刚查看了源代码,看看是否可以覆盖所有rb_error_frozen被调用的地方.问题是rb_check_frozen- 它被称为对象被修改的任何地方(因为它应该是),并且它本身也会被调用rb_error_frozen.这种机制深深植根于C内部,并没有在任何地方的Ruby表面上发布,所以没有办法覆盖"提升行为"或至少没有任何不需要大量工作的方法.如果你想一分钟实际上是一件好事.如果可以简单地覆盖行为,那么实际上这可能被视为Ruby实现中的安全漏洞.冻结一个物体应该保证它无论如何都保持不可修改.