2011-06-14 3 views
8

this에 응답하려고했을 때이 질문이 떠 올랐습니다.instance_eval/exec 또는 module_eval/exec 내의 Module.nesting

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.nestingMethod#source_location, __FILE__과 같은 문자 적 ​​문맥에 의존하는 방법과 변수 중 하나가 될 것입니다. 이 이해가 맞습니까? 그렇다면 누군가가 문자 적 ​​맥락에 의존하는이 방법들/변수들의 목록을 제공 할 수 있습니까? 나는 그것이 참조 용으로 유용하다고 생각한다.

+1

[ "호출 지점에 중첩 된 모듈 목록을 반환합니다]"(http://ruby-doc.org/core/classes/Module.html#M000441)하지만 말할 때 열린 '모듈'이 없습니다. 'A.module_eval' 당신은'A'의 맥락에서 행동하고 있습니다. 'Module.nesting'은 루비 실행 환경보다 파서와 더 많이 대화하는 것 같습니다. –

+0

@mu가 너무 짧습니다. 귀하의 의견에 영감을 받아, 제 질문에 추가했습니다. – sawa

답변

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

내가 아니라 그 루비 내부를 모르지만 나는 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_evalyield_under을 겪고 결국 않음을 알 수있다. 독자의 연습 문제로는 instance_exec, module_evalmodule_exec을 남겨 두겠습니다. 어쨌든 은 Module.nesting 목록에서 명시 적으로 제외됩니다. 그러나 이것은 다른 어떤 것보다 산만 함보다 더 중요합니다. 그것은 단지 여러분이 언급 한 진리를 보지 못하게된다는 것을 의미합니다.

이제 질문은 "NODErb_vm_cref()은 무엇에 관한 것입니까?"입니다.

당신이 node.h에서 보면 당신은 다양한 루비 키워드와 언어 구조에 대한 NODE 상수의 무리 볼 수 있습니다 :

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

그래서 NODE은 명령어 트리의 노드입니다. 최대 잘 내

Module.nesting이 라인은 주석에 파서에

추측을 이야기에 대한 자세한 것 같다. 하지만 어쨌든 우리는 계속 갈 것입니다.

rb_vm_cref 함수는 vm_get_cref0에 대한 래퍼 인 vm_get_cref에 대한 래퍼입니다. 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 

당신은 아직 end을 명중하지 않은으로합니다 (Module.nesting 결과 배열을 생산하기 위해 여과 있음) cref 링크 된 목록에 module A을해야합니다. 당신이이 말을 할 때 : 이미 end가 스택에서 module A을 팝 히트했기 때문에

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에서 module A이 없습니다. 그러나, 당신이 할 경우이 다음 module A가 폐쇄 (그리고 cref을 튀어)되지 않았기 때문에

[A] 
[A] 
[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 

.

이 모듈의 목록이 호출의 시점에서 중첩 반환

Module.nesting documentation이 말한다, 마무리합니다.

이 문장은 내부 검토를 통해 Module.nesting이 실제로 호출되는 특정 문자 컨텍스트에 의존 함을 나타냅니다.

Ruby 내부에서 더 많은 경험을 가진 사람이 있으면 추가 할 항목이 있으므로 커뮤니티 위키로 SO 커뮤니티에 넘겨 줄 수 있습니다.


UPDATE :이 모든뿐만 아니라이 module_eval에처럼 class_eval에 적용하고 또한 그것뿐만 아니라이 1.9.2에처럼 1.9.3에 적용됩니다.

+0

인상 깊고 자세한 설명에 감사드립니다. 위키는 좋은 생각 일 수 있습니다. – sawa

관련 문제