我是C的新手,试图通过Ruby源代码来学习更多知识。
当我从源代码编译Ruby时,它似乎没有意识到我对方法定义所做的任何更改。但是,如果我添加了一个新方法,比如string,指向一个修改过的方法,那么新方法就会像预期的那样工作。
# 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?
falseRuby是如何“保护”原始方法定义的?我怎样才能登记变更?
发布于 2016-05-13 22:17:51
当前版本的Ruby使用虚拟机。当您运行一些Ruby代码时,它首先被编译成字节码,然后这个字节码由虚拟机执行。VM包括诸如分配变量、创建类、定义方法和(针对这种情况的重要)调度方法等方面的指令。但是,对于一些常用的方法,还有一些特殊的优化字节码指令可以绕过正常的方法分派过程。就是这样的一种方法。
您可以使用RubyVM::InstructionSequence.compile (和要查看的disasm )来查看字节码。首先是“正常”方法分派(使用不存在的方法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 leaveopt_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 leaveopt_empty_p是empty?方法的专用字节码指令。
如果将此指令的源代码与String#empty? (您更改的函数)进行比较,您可以看到在接收方为字符串的情况中,指令代码重复了函数的代码,完全绕过了该函数(在某些情况下,这些优化的指令直接调用实现函数,绕过方法分派代码,但使用相同的实现)。
该指令确实包括一个检查,以确保该方法在Ruby中没有被替换,但这显然不包括对C源代码的修改,如这里所示。
如果使用send,我认为您应该得到修改后的函数版本,因为这不能编译到优化的指令:
'asdf'.send :empty?如果设置了编辑和重新编译Ruby,您还应该能够在文件insns.def中修改指令本身。此文件用于在生成过程中为指令创建代码。这不是C本身,但每个指令块的内容只是普通的C。
https://stackoverflow.com/questions/37218612
复制相似问题