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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With