查看Ruby Under a Microscope的源代码
←
Ruby Under a Microscope
跳转到:
导航
、
搜索
因为以下原因,你没有权限编辑本页:
您刚才请求的操作只有这个用户组中的用户才能使用:
用户
您可以查看并复制此页面的源代码:
== 方法查找和常量查找 == * moule 也是 class,但是移除了 iv_index_tbl, allocator 等,因为模块没有对象级别的属性和方法。 * include 一个模块,本质上是拷贝该模块的 RClass 结构体形成一个新副本,然后作为类的新超类,模块副本加入了祖先继承链。include 的核心逻辑在 ruby.c 里的 rb_include_module 和 include_modules_at 函数里。复制发生在 rb_include_class_new 函数。 <pre> VALUE rb_include_class_new(VALUE module, VALUE super) { VALUE klass = class_alloc(T_ICLASS, rb_cClass); if (BUILTIN_TYPE(module) == T_ICLASS) { module = RBASIC(module)->klass; } if (!RCLASS_IV_TBL(module)) { RCLASS_IV_TBL(module) = st_init_numtable(); } if (!RCLASS_CONST_TBL(module)) { RCLASS_CONST_TBL(module) = rb_id_table_create(0); } RCLASS_IV_TBL(klass) = RCLASS_IV_TBL(module); RCLASS_CONST_TBL(klass) = RCLASS_CONST_TBL(module); RCLASS_M_TBL(OBJ_WB_UNPROTECT(klass)) = RCLASS_M_TBL(OBJ_WB_UNPROTECT(RCLASS_ORIGIN(module))); /* TODO: unprotected? */ RCLASS_SET_SUPER(klass, super); if (RB_TYPE_P(module, T_ICLASS)) { RBASIC_SET_CLASS(klass, RBASIC(module)->klass); } else { RBASIC_SET_CLASS(klass, module); } OBJ_INFECT(klass, module); OBJ_INFECT(klass, super); return (VALUE)klass; } static int include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super); void rb_include_module(VALUE klass, VALUE module) { int changed = 0; rb_frozen_class_p(klass); Check_Type(module, T_MODULE); OBJ_INFECT(klass, module); changed = include_modules_at(klass, RCLASS_ORIGIN(klass), module, TRUE); if (changed < 0) rb_raise(rb_eArgError, "cyclic include detected"); } static enum rb_id_table_iterator_result add_refined_method_entry_i(ID key, VALUE value, void *data) { rb_add_refined_method_entry((VALUE)data, key); return ID_TABLE_CONTINUE; } static int include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super) { VALUE p, iclass; int method_changed = 0, constant_changed = 0; struct rb_id_table *const klass_m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(klass)); while (module) { int superclass_seen = FALSE; struct rb_id_table *tbl; if (RCLASS_ORIGIN(module) != module) goto skip; if (klass_m_tbl && klass_m_tbl == RCLASS_M_TBL(module)) return -1; /* ignore if the module included already in superclasses */ for (p = RCLASS_SUPER(klass); p; p = RCLASS_SUPER(p)) { int type = BUILTIN_TYPE(p); if (type == T_ICLASS) { if (RCLASS_M_TBL(p) == RCLASS_M_TBL(module)) { if (!superclass_seen) { c = p; /* move insertion point */ } goto skip; } } else if (type == T_CLASS) { if (!search_super) break; superclass_seen = TRUE; } } iclass = rb_include_class_new(module, RCLASS_SUPER(c)); c = RCLASS_SET_SUPER(c, iclass); { VALUE m = module; if (BUILTIN_TYPE(m) == T_ICLASS) m = RBASIC(m)->klass; rb_module_add_to_subclasses_list(m, iclass); } if (FL_TEST(klass, RMODULE_IS_REFINEMENT)) { VALUE refined_class = rb_refinement_module_get_refined_class(klass); rb_id_table_foreach(RMODULE_M_TBL(module), add_refined_method_entry_i, (void *)refined_class); FL_SET(c, RMODULE_INCLUDED_INTO_REFINEMENT); } tbl = RMODULE_M_TBL(module); if (tbl && rb_id_table_size(tbl)) method_changed = 1; tbl = RMODULE_CONST_TBL(module); if (tbl && rb_id_table_size(tbl)) constant_changed = 1; skip: module = RCLASS_SUPER(module); } if (method_changed) rb_clear_method_cache_by_class(klass); if (constant_changed) rb_clear_constant_cache(); return method_changed; } </pre> 在代码最后,如果发现方法有变化,就清空方法缓存,如果常量有变化,就清空常量缓存。参考 http://ju.outofmemory.cn/entry/135587 * Ruby 的方法缓存包含两层:全局的方法缓存,用于缓存接收者和实现类之间的映射,因为方法查找是要遍历整个继承链的,缓存可以加速这个调用。其次是内联方法缓存,缓存 Ruby 已经执行的已编译的 YARV 指令信息,这样可以避免查找,加速的原理和 clojure 的 direct linking 技术是一样的。无论是定义新方法、include 模块或者其他类似的操作, Ruby 都会去清空这两个缓冲。 * 多次include 不同模块,最近 include 的模块作为直接超类向上延伸。 * 模块也可以 include 模块,规则与类 include 模块一致,也是副本插入作为超类,作为目标类和原始超类之间新的超类。 * Module prepend 例子: <pre> module Professor def name "Prof. #{super}" end end class Mathematician attr_accessor :name prepend Professor end p = Mathematician.new p.name = 'Johann Carl Friedrich Gauss' p p.name </pre> * prepend 虽然仍然会将 Professor 设置为 Mathematician 的新超类,但是同时会拷贝一份 Mathematician 作为 Mathematician 原生类(Origin class),将这个原生类作为 Professor 的超类,这就可以解释为什么 Professor#name 的 super 能调用到 Mathematician 的 name 方法。参考 http://ju.outofmemory.cn/entry/135588 * 修改已被 include 模块,比如增加方法,所有 include 该模块的类都将包含新方法,因为共享 m_tbl 方法表,Ruby 在 include 的时候拷贝的只是 RClass struct,不拷贝底层的方法表 ,看下面例子: <pre> module Professor def letcures ; end end class Mathematician attr_accessor :first_name attr_accessor :last_name include Professor end p = Mathematician.new p.first_name = 'hello' p.last_name = 'world' p p.methods.sort #open Professor, adds new method module Professor def classroom; end end p p.methods.sort </pre> * 但是修改已被 include 模块中 include 的其他模块,不会影响插入到 include 类的已经被拷贝的模块副本,也就不会增加或者删除方法。 * 当创建一个 class或者模块的时候 ,其实是新建了一层词法作用域,Ruby 用两个指针来标示: nd_clss ,指向当前作用域对应的模块或者类;nd_next 指向父层或者上下文的词法作用域。形成一个作用域链条。 * 常量的查找跟方法的查找类似,只是方法的查找是通过祖先连(super) 来查找,而常量是通过迭代词法作用域链(nd_next)来查找。 * Ruby 优先通过词法作用域来查找常量: <pre> class SuperClass FIND_ME = "Found in Superclass" end module ParentLexicalScope FIND_ME = "Found in ParentLexicalScope" module ChildLexicalScope class SubClass < SuperClass p FIND_ME end end end </pre> 输出 "Found in ParentLexicalScope" * 真实的 Ruby 常量查找还需要加入 autoload 关键字: 『检索词法作用域链 -> 为了个作用域的类检查 autoload -> 检索超类链 -> 为每个超类检查 autoload -> 调用 const_missing。』。 * 关于 autoload http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
返回到
Ruby Under a Microscope
。
个人工具
登录
名字空间
页面
讨论
变换
查看
阅读
查看源代码
查看历史
操作
搜索
导航
首页
社区专页
新闻动态
最近更改
随机页面
帮助
工具箱
链入页面
相关更改
特殊页面