在instance_eval / exec或module_eval / exec中的Module.nesting
当我试图回答这个问题时,我提出了这个问题。 以下是预期的行为:
module A p Module.nesting end # => [A]
但以下内容:
A.instance_eval{p Module.nesting} A.instance_exec{p Module.nesting} A.module_eval{p Module.nesting} A.module_exec{p Module.nesting}
全部归还[]
。 为什么这些不能像上面这样工作?
补充问题
穆太短表示一个有趣的观点。 如果这是正确的,那么Module.nesting
将是依赖于文字上下文的方法和变量之一,如Method#source_location
, __FILE__
。 这种理解是否正确? 如果是这样,有人可以提供依赖于文字上下文的这些方法/变量的清单吗? 我认为这对参考有用。
警告 :这有点漫长。 由于文档有点薄,因此有必要浏览一下Ruby源代码。 如果你不关心如何制作香肠,请随意跳到最后。
1.9.2 Module.nesting
在eval.c
实现,如下所示:
static VALUE rb_mod_nesting(void) { VALUE ary = rb_ary_new(); const NODE *cref = rb_vm_cref(); while (cref && cref->nd_next) { VALUE klass = cref->nd_clss; if (!(cref->flags & NODE_FL_CREF_PUSHED_BY_EVAL) && !NIL_P(klass)) { rb_ary_push(ary, klass); } cref = cref->nd_next; } return ary; }
我不太清楚Ruby的内部结构,但是我像这样读取了while
循环:从cref
链表中提取所有与类类似的东西相关但不来自eval
的节点。 NODE_FL_CREF_PUSHED_BY_EVAL
位仅在此处设置:
/* block eval under the class/module context */ static VALUE yield_under(VALUE under, VALUE self, VALUE values)
更多的grepping和读取显示instance_eval
最终通过yield_under
。 我将检查instance_exec
, module_eval
和module_exec
作为读者练习。 在任何情况下,看起来instance_eval
都明确地从Module.nesting
列表中排除; 然而,这比其他任何东西都更令人分心,只是意味着你不会看到所提到的一些东西。
所以现在的问题是“ NODE
和rb_vm_cref()
到底是什么?”。
如果你查看node.h
你会看到各种Ruby关键字和语言结构的一堆NODE常量:
-
NODE_BLOCK
-
NODE_BREAK
-
NODE_CLASS
-
NODE_MODULE
-
NODE_DSYM
- …
所以我猜NODE
是指令树中的一个节点。 这与我的排队很好
Module.nesting
似乎更多的是与解析器交谈
评论中的猜想。 但无论如何我们会继续前进。
rb_vm_cref
函数只是rb_vm_cref
的包装器,它是vm_get_cref
的包装器。 什么是vm_get_cref0
? 一切都是这样的:
static NODE * vm_get_cref0(const rb_iseq_t *iseq, const VALUE *lfp, const VALUE *dfp) { while (1) { if (lfp == dfp) { return iseq->cref_stack; } else if (dfp[-1] != Qnil) { return (NODE *)dfp[-1]; } dfp = GET_PREV_DFP(dfp); } }
函数的所有三个参数直接来自此控制框架:
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
iseq
似乎是一个指令序列, lfp
和dfp
是帧指针:
VALUE *lfp; // cfp[6], local frame pointer VALUE *dfp; // cfp[7], dynamic frame pointer
cref_stack
的定义是相关的:
/* klass/module nest information stack (cref) */ NODE *cref_stack;
所以看起来你正在从rb_vm_cref
获得某种调用或嵌套堆栈。
现在回到手边的细节。 当你这样做:
module A p Module.nesting end
您将在cref
链接列表中进行module A
(已过滤以生成Module.nesting
结果数组),因为您尚未达到end
。 当你说这些:
A.instance_eval { puts Module.nesting } A.instance_exec { puts Module.nesting } A.module_eval { puts Module.nesting } A.module_exec { puts Module.nesting }
你不会再在cref
使用module A
,因为你已经从堆栈中击出了end
弹出的module A
但是,如果你这样做:
module A instance_eval { puts Module.nesting.inspect } instance_exec { puts Module.nesting.inspect } module_eval { puts Module.nesting.inspect } module_exec { puts Module.nesting.inspect } end
你会看到这个输出:
[A] [A] [A] [A]
因为module A
尚未关闭(并且已从cref
弹出)。
最后, Module.nesting
文档说明了这一点:
返回嵌套在调用点的模块列表。
我认为这个陈述与对内部的审查相结合表明, Module.nesting
确实取决于调用它的特定文字上下文。
如果在Ruby内部有更多经验的人有任何要添加的内容,我可以将其作为社区wiki交给SO社区。
更新 :所有这些都适用于class_eval
以及它对module_eval
class_eval
,它也适用于1.9.3以及1.9.2。