Ruby ruby 方法的寻找路径?

hhuai · 2012年03月20日 · 最后由 clc3123 回复于 2012年03月21日 · 3427 次阅读

今天有点蛋疼的想验证一把ruby寻找方法的搜索路径, 例

module M; end
class B; end
class C <B; include M; end
c = C.new

那么c.send(:to_s)搜索路径应该是怎么样的呢? 我认为完整的路径为: c -> c.engienclass -> C -> M -> B ->Object->Kernel

本想debug一把,不知道为何下了断点进不去。翻了下代码看了看,觉得怎么略过了engienclass和module,求解?

search_method(VALUE klass, ID id)
{
    st_data_t body;
    if (!klass) {
    return 0;
    }

    while (!st_lookup(RCLASS_M_TBL(klass), id, &body)) {
    klass = RCLASS_SUPER(klass);
    if (!klass) {
        return 0;
    }
    }

    return (rb_method_entry_t *)body;
}

共收到 14 条回复

楼主是对的,如果1.9,kernel 后还有个 BasicObject

楼主可以看 ruby元编程 p27

#1楼 @vkill 多谢提醒,但这个不是主要疑问。 你看那个方法寻找时,一直都在找klass的super class, 根本没看到怎么进engienclass和module中搜寻的。

惭愧,看不懂c。。。

@hhui 并没有跳过engienclass和module. 简单来说就是,Ruby在include和define method的时候,已经对klass做了处理,已经串起来了。具体你可以看看这两个函数:

vm_define_method
rb_include_module

@hhuai 略说一下就是: 1 .如果你对c定义了一个单例方法,如:

def c.x
  'ooO'
end


这时,c对象的klass已经不是类C,而是成了c.engienclass。并且 c.engienclass的super则指向类C。

2.对于C include M: Ruby会为模块M建立一个代理类,(姑且名为M-proxy). 这时,C的super就不是B了,而是M-proxy,M-proxy的super则是B。注意,这个M-proxy类的实例变量表和方法表都指向了模块M的实例变量表和方法表 。所以,在方法搜索时,依然也会查找M的方法表。

这样,这个链条就串起来了。所以说,并没有跳过engienclass和module.

s.class.ancestors .join(" ~> ")
=> "C -> M -> B -> Object -> Kernel -> BasicObject"

#6楼 @Ddl1st 在C语言层面 Ruby在ancestors中做了一点处理~。如果super是模块代理类(IClass),则取代理类的class,在这里即是M。

在Ruby层面 如你的结果显示,我们直接认为M是C类的superclass就行了。

就学习Ruby语言来说,则完全可以忽略C语言层面的东西,因为主要目的是学习Ruby而不是C。:) 如果需要扩展Ruby的C实现,则有必要学习一下Ruby的C实现。@hhuai 是不是在搞Ruby的jit吧?

#5楼 @skandhas 这些东西哪里有介绍啊?

#8楼 @clc3123 有 Ruby Hacking Guide http://thinkinginruby.group.iteye.com/group/wiki/1262-chinese-version-of-the-ruby-hacking-guide 不过已经很老了,也没翻译完。如果日文好~ 你就直接肯日文。:) 是以Ruby1.7.3为base,老~,十分的老。

个人如果感兴趣,就下载ruby1.9的源码~,自己看源码。这应该是最好的途径

还是重申:就学习Ruby语言来说,则完全可以忽略C语言层面的东西 :>

#5楼 @skandhas 第1点我有时间再自己探究一下。

2.对于C include M: 即然这样的话,那么为什么在ruby中调用C.superclass 为什么还是B呢。这样不一致的处理,貌似有点混乱。莫非这个engienclass和M-proxy的类型不是T_CLASS?

jit想搞,但进度有点慢,一步一步来,先把整个架构搞清楚。

#10楼 @hhuai superclass内部也是进行了处理,跳过了M-proxy。因为M-proxy的类型是T_ICLASS,而不是T_ClASS. rb_class_superclass的一段代码:

while (TYPE(super) == T_ICLASS) {
super = RCLASS_SUPER(super);
}


在取superclass的时候,跳过了T_ICLASS。所以C.superclass还是B。

engienclass是T_ClASS,但是它的flags 加上了 FL_SINGLETON 这个标志位。

#11楼 @skandhas 谢了啊,刚用xcode走了一遍,的确是这样的。I的意思是啥,inject吗?

#12楼 @hhuai "I" 按照 青木峰郎的说法,应该是include的意思。

#9楼 @skandhas 谢谢,收藏之

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册