我是C的新手,试图通过Ruby源代码了解更多信息。
当我从源代码编译Ruby时,似乎没有认识到我对方法定义所做的任何更改。但是,如果我添加一个新方法,比如说字符串,指向一个修改过的方法,新方法就可以正常工作。
# string.c
static VALUE
rb_str_empty(VALUE str)
{
return Qtrue;
}
...
rb_define_method(rb_cString, "empty?", rb_str_empty, 0);
rb_define_method(rb_cString, "my_empty?", rb_str_empty, 0);
然后在Ruby控制台中,我们看到新方法反映了新定义,但旧方法仍然可以像未修改方法一样工作。
$ irb
> "sdf".my_empty?
true
> "sdf".empty?
false
Ruby如何“保护”原始方法定义?我怎样才能让我的更改注册?
答案 0 :(得分:4)
当前版本的Ruby使用虚拟机。当你运行一些Ruby代码时,它首先被编译成字节码,然后这个字节码由虚拟机执行。 VM包括指令,用于分配变量,创建类,定义方法和(导入这种情况)调度方法。但是,对于某些常用方法,还有一些特殊优化的字节码指令可绕过正常的方法调度过程。 empty?
is one such method
您可以使用RubyVM::InstructionSequence.compile
(和disasm
来查看)查看一些Ruby代码的字节码。首先是“普通”方法调度(使用不存在的方法foo
):
> puts RubyVM::InstructionSequence.compile('"asdf".foo').disasm
== disasm: #<ISeq:<compiled>@<compiled>>================================
0000 trace 1 ( 1)
0002 putstring "asdf"
0004 opt_send_without_block <callinfo!mid:foo, argc:0, ARGS_SIMPLE>, <callcache>
0007 leave
opt_send_without_block
是方法调度说明,尝试调用foo
(mid
是“方法ID”)。
现在使用empty?
的优化字节码:
> puts RubyVM::InstructionSequence.compile('"asdf".empty?').disasm
== disasm: #<ISeq:<compiled>@<compiled>>================================
0000 trace 1 ( 1)
0002 putstring "asdf"
0004 opt_empty_p <callinfo!mid:empty?, argc:0, ARGS_SIMPLE>, <callcache>
0007 leave
opt_empty_p
是empty?
方法的专用字节码指令。
如果将此指令的源与source for the normal function implementing String#empty?
(您更改的函数)进行比较,您可以看到case of the receiver being a String指令代码复制了函数的代码,完全绕过该函数(在某些情况下,这些优化指令直接调用实现函数,绕过方法调度代码但使用相同的实现。)
该指令确实包含一个检查,以确保该方法尚未在Ruby中替换,但这显然不包括对此类C源的修改。
如果您使用send
,我认为您应该获得该函数的修改版本,因为这不会编译为优化指令:
'asdf'.send :empty?
如果您正在设置编辑和重新编译Ruby,那么您还应该能够在文件insns.def
中更改指令本身。此文件用于在构建过程中为指令创建代码。它不是C本身,但每个指令块的内容都只是普通的C.