saw*_*awa 8 ruby nested module class
当我试图回答这个问题时,我提出了这个问题.以下是预期的行为:
module A
p Module.nesting
end
# => [A]
Run Code Online (Sandbox Code Playgroud)
但是以下内容:
A.instance_eval{p Module.nesting}
A.instance_exec{p Module.nesting}
A.module_eval{p Module.nesting}
A.module_exec{p Module.nesting}
Run Code Online (Sandbox Code Playgroud)
所有的回报[].为什么这些不能像上面这样工作?
附加问题
穆太短表示一个有趣的观点.如果这是正确的,那么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;
}
Run Code Online (Sandbox Code Playgroud)
我不太了解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)
Run Code Online (Sandbox Code Playgroud)
更多的点击和阅读显示instance_eval最终会通过yield_under.我会留下检查instance_exec,module_eval并module_exec作为读者的练习.在任何情况下,它看起来instance_eval都是从Module.nesting列表中明确排除的; 然而,这比其他任何事情更令人分心,只是意味着你不会看到所提到的一些东西.
所以,现在的问题是"什么NODE和rb_vm_cref()怎么一回事?".
如果您查看,node.h您将看到各种Ruby关键字和语言结构的一堆NODE常量:
NODE_BLOCKNODE_BREAKNODE_CLASSNODE_MODULENODE_DSYM所以我猜这NODE是指令树中的一个节点.这与我的排队很好
Module.nesting似乎更多的是与解析器交谈
评论中的猜想.但无论如何我们会继续前进.
该rb_vm_cref函数只是一个包装器,vm_get_cref它是一个包装器vm_get_cref0.什么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);
}
}
Run Code Online (Sandbox Code Playgroud)
该函数的所有三个参数都直接来自此控制框架:
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
Run Code Online (Sandbox Code Playgroud)
的iseq似乎是一个指令序列和lfp和dfp是帧指针:
VALUE *lfp; // cfp[6], local frame pointer
VALUE *dfp; // cfp[7], dynamic frame pointer
Run Code Online (Sandbox Code Playgroud)
定义cref_stack是相关的:
/* klass/module nest information stack (cref) */
NODE *cref_stack;
Run Code Online (Sandbox Code Playgroud)
所以看起来你正在进行某种调用或嵌套堆栈rb_vm_cref.
现在回到手边的具体细节.当你这样做:
module A
p Module.nesting
end
Run Code Online (Sandbox Code Playgroud)
您将module A在cref链接列表中(已过滤以生成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 }
Run Code Online (Sandbox Code Playgroud)
你将不再拥有module A,cref因为你已经击中了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
Run Code Online (Sandbox Code Playgroud)
你会看到这个输出:
[A]
[A]
[A]
[A]
Run Code Online (Sandbox Code Playgroud)
因为module A尚未关闭(并弹出cref).
最后,Module.nesting文档说明了这一点:
返回嵌套在调用点的模块列表.
我认为这种说法结合对内部的审查表明,Module.nesting事实上确实取决于所谓的特定文字语境.
如果在Ruby内部有更多经验的人有任何要添加的内容,我可以将其作为社区维基交给SO社区.
更新:所有这一切都适用于class_eval它module_eval,它也适用于1.9.3以及1.9.2.