2011-06-14 24 views
8

我想出了這個問題,當我試圖回答this。以下是預期的行爲:Module.nesting內instance_eval/exec或module_eval/exec

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__。這種理解是否正確?如果是這樣,有人可以提供依賴於文字上下文的這些方法/變量的清單嗎?我認爲這對參考很有用。

+1

[「返回嵌套在調用點的模塊列表」](http://ruby-doc.org/core/classes/Module.html#M000441),但是當您說時沒有打開的「模塊」 'A.module_eval'你只是在'A'的背景下行事。 'Module.nesting'似乎更多地是與Ruby運行時環境交談的解析器。 – 2011-06-14 01:59:18

+0

@mu太短由你的評論啓發,我加入到我的問題。 – sawa 2011-06-14 02:17:12

回答

7

警告:這有點漫長。由於文檔有點簡單,瀏覽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_evalmodule_exec作爲讀者的練習。在任何情況下,看起來instance_eval明確排除在Module.nesting列表中;然而,這比其他任何事都更讓人分心,它只是意味着你不會看到提及的事物。

所以現在的問題是「什麼是NODErb_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似乎是一個指令序列和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 } 

你不會有crefmodule A了,因爲你已經打了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社區。


UPDATE:所有這一切都適用於class_eval以及它對module_eval,它也適用於1.9.3以及它對1.9.2。

+0

感謝您的印象深刻和詳細的解釋。維基可能是一個好主意。 – sawa 2011-06-14 05:10:20