ruby – 在instance_eval / exec或module_eval / exec中的Module.nesting

前端之家收集整理的这篇文章主要介绍了ruby – 在instance_eval / exec或module_eval / exec中的Module.nesting前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
当我试图回答 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__.这种理解是否正确?如果是这样,有人可以提供依赖于文字上下文的这些方法/变量的清单吗?我认为这对参考有用.

解决方法

警告:这有点漫长.由于文档有点薄,因此有必要浏览一下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 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似乎是一个指令序列,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链接列表中进行模块A(已过滤以生成Module.nesting结果数组),因为您尚未达到目的.当你说这些:

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中拥有模块A了,因为你已经从堆栈中击出了最终弹出的模块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]

因为模块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相关文章