gpt4 book ai didi

ruby - 模块嵌套在 instance_eval/exec 或 module_eval/exec 中

转载 作者:数据小太阳 更新时间:2023-10-29 06:54:12 25 4
gpt4 key购买 nike

我在尝试回答 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}

全部返回[]。为什么这些不能像上面那样工作?

附加问题

Mu is too short 提出了一个有趣的观点。如果这是正确的,那么 Module.nesting 将是依赖于文字上下文的方法和变量之一,例如 Method#source_location__FILE__。这种理解是否正确?如果是这样,有人可以提供这些依赖于文字上下文的方法/变量的 list 吗?我认为这对引用很有用。

最佳答案

警告:这有点长而且杂乱无章。由于文档有点薄,因此似乎有必要浏览一下 Ruby 源代码。如果您不关心香肠是如何制作的,请随意跳到最后。


1.9.2 Module.nestingeval.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 之外列表;然而,这比其他任何事情都更让人分心,它只是意味着您不会看到 evals 提到的内容。

所以现在的问题是“NODErb_vm_cref() 到底是什么?”。

如果您查看 node.h您会看到一堆用于各种 Ruby 关键字和语言结构的 NODE 常量:

  • NODE_BLOCK
  • NODE_BREAK
  • NODE_CLASS
  • NODE_MODULE
  • NODE_DSYM
  • ...

所以我猜 NODE是指令树中的一个节点。这很符合我的

Module.nesting seems to be more about talking to the parser

评论中的猜想。但无论如何我们都会继续前进。

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似乎是一个指令序列和 lfpdfp是帧指针:

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 Acref链接列表(过滤后生成 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 Acref不再是因为您已经点击了 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是这样说的:

Returns the list of Modules nested at the point of call.

我认为这个声明结合内部审查表明Module.nesting实际上取决于调用它的特定文字上下文。

如果任何在 Ruby 内部有更多经验的人有什么要补充的,我可以将其作为社区 wiki 交给 SO 社区。


更新:所有这些都适用于 class_eval以及它对 module_eval 的影响它也适用于 1.9.3 和 1.9.2。

关于ruby - 模块嵌套在 instance_eval/exec 或 module_eval/exec 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6337821/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com