Ruby ruby 方法的寻找路径?

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

今天有点蛋疼的想验证一把 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;
}

楼主是对的,如果 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 谢谢,收藏之

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