当我试图回答this时,我提出了这个问题。以下是预期的行为:
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__
。这种理解是否正确?如果是这样,有人可以提供依赖于文字上下文的这些方法/变量的清单吗?我认为这对参考有用。
答案 0 :(得分:7)
警告:这有点漫无边际。由于文档有点薄,因此有必要浏览一下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)
更多点击和阅读表明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
函数只是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);
}
}
该函数的所有三个参数直接来自此控制框架:
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
您在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 }
你不再在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
你会看到这个输出:
[A]
[A]
[A]
[A]
因为module A
尚未关闭(并暂时关闭cref
)。
要完成,Module.nesting
documentation说明了这一点:
返回嵌套在调用点的模块列表。
我认为这个陈述与对内部的审查相结合表明Module.nesting
确实取决于它被称为的特定文字语境。
如果在Ruby内部有更多经验的人有任何需要添加的内容,我可以将其作为社区维基传递给SO社区。 p>
更新:所有这些都适用于class_eval
以及module_eval
,它也适用于1.9.3以及1.9.2