在instance_eval / exec或module_eval / exec中模块 [英] Module.nesting within instance_eval/exec or module_eval/exec

查看:89
本文介绍了在instance_eval / exec或module_eval / exec中模块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我尝试回答时,我想出了这个问题。以下是预期行为:

 模块A 
p Module.nesting
end
# => [A]

但以下内容:

  A.instance_eval {p Module.nesting} 
A.instance_exec {p Module.nesting}
A.module_eval {p Module.nesting}
.module_exec {p Module.nesting}

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


$ b

太短建议一个有趣的点。如果这是正确的,那么 Module.nesting 将是依赖于文本上下文的方法和变量之一,例如 Method#source_location __ FILE __ 。这个理解是正确的吗?如果是这样,有人可以提供依赖于文字上下文的这些方法/变量的清单吗?

:这是一个漫长的。有一点通过Ruby源代码的游览似乎是必要的,因为文档有点薄。






1.9.2 如下所示,在 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内部很好, c $ c> while 循环像这样:从 cref 中提取所有与类样东西相关联的节点,但didn' t来自 eval NODE_FL_CREF_PUSHED_BY_EVAL 位仅在此处设置:

  / *在类/模块上下文中* / 
static VALUE
yield_under(VALUE,VALUE self,VALUE values)

有一点更令人兴奋,阅读显示 instance_eval 最终会通过 yield_under 。我将离开检查 instance_exec module_eval module_exec 作为练习的读者。在任何情况下,看起来像 Module.nesting 列表中明确排除 instance_eval 然而,这是一个分心,比任何其他,它只是意味着你不会看到的东西evals提到。



现在的问题是什么是 NODE rb_vm_cref()所有关于?。



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




  • 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],本地框架指针
VALUE * dfp; // cfp [7],动态帧指针

cref_stack 是相关的:

  / * klass /模块嵌套信息栈(cref)* / 
NODE * cref_stack;

所以看起来你得到某种类型的调用或嵌套堆栈 rb_vm_cref






现在回到手边的细节。执行此操作时:

 模块A 
p Module.nesting
end

您将在 cref中有模块A 结果数组),因为你没有击中 end 链接列表(被过滤产生 Module.nesting / code>。当你说这些:

  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 ,因为你已经打过 end 弹出模块A 。但是,如果您这样做:

 模块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 确实取决于它被调用的具体文字上下文。



如果任何人在Ruby内部有更多的经验有什么要添加我可以交给这个作为社区wiki。






UPDATE :所有这一切适用于 class_eval 以及 module_eval ,它也适用于1.9.3以及1.9.2 。


I came up with this question when I was trying to answer this. The following is an expected behaviour:

module A
  p Module.nesting
end
# => [A]

But the following:

A.instance_eval{p Module.nesting}
A.instance_exec{p Module.nesting}
A.module_eval{p Module.nesting}
A.module_exec{p Module.nesting}

all return []. Why do these not work as the above?

Additional Question

Mu is too short suggested an interesting point. If that is correct, then Module.nesting would be one of the methods and variables that are dependent on the literal context like Method#source_location, __FILE__. Is this understanding correct? If so, can someone provide the inventory of these methods/variables that are dependent on the literal context? I think it would be useful for reference.

解决方案

Warning: This is a little long a rambling. A bit of a tour through the Ruby source code seems necessary as the documentation is a bit thin. Feel free to skip to the end if you don't care about how sausage is made.


The 1.9.2 Module.nesting is implemented in eval.c like this:

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;
}

I don't know the Ruby internals that well but I read the while loop like this: extract from the cref linked list all the nodes that are associated with a class-like thing but didn't come from eval. The NODE_FL_CREF_PUSHED_BY_EVAL bit is only set in here:

/* block eval under the class/module context */
static VALUE
yield_under(VALUE under, VALUE self, VALUE values)

A bit more grepping and reading reveals that instance_eval does end up going through yield_under. I'll leave checking instance_exec, module_eval, and module_exec as exercises for the reader. In any case, it looks like instance_eval is explicitly excluded from the Module.nesting list; this is, however, more of a distraction than anything else, it just means that you won't see something the evals mentioned.

So now the question is "what are NODE and rb_vm_cref() all about?".

If you look in node.h you'll see a bunch of NODE constants for the various Ruby keywords and language structures:

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

so I'd guess that NODE is a node in the instruction tree. This lines up nicely with my

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

conjecture in the comment. But we'll keep going anyway.

The rb_vm_cref function is just a wrapper for vm_get_cref which is a wrapper for vm_get_cref0. What is vm_get_cref0 all about? It is all about this:

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);
    }
}

All three arguments to the function come straight out of this control frame:

rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);

The iseq appears to be an instruction sequence and the lfp and dfp are frame pointers:

VALUE *lfp;                 // cfp[6], local frame pointer
VALUE *dfp;                 // cfp[7], dynamic frame pointer

The definition of cref_stack is relevant:

/* klass/module nest information stack (cref) */
NODE *cref_stack;

So it looks like you're getting some sort of call or nesting stack out of rb_vm_cref.


Now back to the specifics at hand. When you do this:

module A
  p Module.nesting
end

You'll have module A in the cref linked list (which is filtered to produce the Module.nesting result array) as you haven't hit the end yet. When you say these:

A.instance_eval { puts Module.nesting }
A.instance_exec { puts Module.nesting }
A.module_eval   { puts Module.nesting }
A.module_exec   { puts Module.nesting }

You won't have module A in cref anymore because you've already hit the end popped module A off the stack. However, if you do this:

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

You'll see this output:

[A]
[A]
[A]
[A]

because the module A hasn't been closed (and popped off cref) yet.

To finish off, the Module.nesting documentation says this:

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

I think this statement combined with the review of the internals indicates that Module.nesting does in fact depend on the specific literal context in which it is called.

If anyone with more experience in the Ruby internals has anything to add I can hand this over to the SO community as a community wiki.


UPDATE: All of this applies to class_eval as well as it does to module_eval and it also applies to 1.9.3 as well as it does to 1.9.2.

这篇关于在instance_eval / exec或module_eval / exec中模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆