Advertencia: Esto es un poco largo. Un recorrido por el código fuente de Ruby parece necesario ya que la documentación es un poco escasa. Siéntase libre de saltar hasta el final si no le importa cómo se hace la salchicha.
La 1.9.2 Module.nesting
se implementa en eval.c
así:
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;
}
No sé las partes internas Ruby que bien pero leí el bucle while
así: extracto de la cref
vinculados enumere todos los nodos que están asociados con una cosa similar a una clase pero no provienen del eval
. El bit NODE_FL_CREF_PUSHED_BY_EVAL
sólo se establece aquí:
/* block eval under the class/module context */
static VALUE
yield_under(VALUE under, VALUE self, VALUE values)
Un poco más grepping y la lectura revela que instance_eval
termina yendo a través yield_under
. Dejaré el control de instance_exec
, module_eval
y module_exec
como ejercicios para el lector. En cualquier caso, parece que instance_eval
está explícitamente excluido de la lista Module.nesting
; esto es, sin embargo, más una distracción que otra cosa, solo significa que no verás algo que los evals mencionaron.
Así que ahora la pregunta es "¿qué son NODE
y rb_vm_cref()
todo?".
Si nos fijamos en node.h
verá un montón de constantes nodo para las diversas palabras clave y estructuras del lenguaje Ruby:
NODE_BLOCK
NODE_BREAK
NODE_CLASS
NODE_MODULE
NODE_DSYM
- ...
así que supongo que NODE
es un nodo en el árbol de instrucciones. Esto concuerda muy bien con mi
Module.nesting
parece ser más acerca de hablar con el analizador
conjeturas en el comentario. Pero seguiremos yendo de todos modos.
La función rb_vm_cref
es simplemente un contenedor para vm_get_cref
que es un contenedor para vm_get_cref0
. ¿De qué se trata vm_get_cref0
? Es todo acerca de esto:
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);
}
}
Los tres argumentos de la función vienen directamente de este marco de control:
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, th->cfp);
El iseq
parece ser una secuencia de instrucciones y la lfp
y dfp
son punteros de marco :
VALUE *lfp; // cfp[6], local frame pointer
VALUE *dfp; // cfp[7], dynamic frame pointer
La definición de cref_stack
es relevante:
/* klass/module nest information stack (cref) */
NODE *cref_stack;
Parece que está recibiendo algún tipo de llamada o pila de anidación de rb_vm_cref
.
Ahora volviendo a los detalles a mano. Al hacer esto:
module A
p Module.nesting
end
Tendrás module A
en la lista enlazada cref
(que se filtra para producir la matriz Module.nesting
resultado) ya que no ha golpeado el end
todavía. Cuando usted dice estas:
A.instance_eval { puts Module.nesting }
A.instance_exec { puts Module.nesting }
A.module_eval { puts Module.nesting }
A.module_exec { puts Module.nesting }
No tendrá module A
en cref
más porque ya ha golpeado el end
metimos module A
de la pila. Sin embargo, si usted hace esto:
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
Vas a ver esta salida:
[A]
[A]
[A]
[A]
porque el module A
no se ha cerrado (y se desprendió cref
) todavía.
Para terminar, el Module.nesting
documentation dice esto:
Devuelve la lista de módulos anidado en el punto de llamada.
creo que este estado combinado con el examen de las partes internas indica que Module.nesting
de hecho dependerá del contexto específico en el que literalmente se le llama.
Si alguien con más experiencia en las partes internas de Ruby tiene algo que agregar, puedo entregárselo a la comunidad de SO como wiki de la comunidad.
ACTUALIZACIÓN: Todo esto se aplica a class_eval
, así como a module_eval
y también se aplica a 1.9.3, así como a 1.9.2.
["Devuelve la lista de módulos anidados en el punto de llamada"] (http://ruby-doc.org/core/classes/Module.html#M000441) pero no hay ningún 'módulo abierto 'cuando dices 'A.module_eval' solo estás actuando en el contexto de' A'. 'Module.nesting' parece ser más acerca de hablar con el analizador que con el entorno de tiempo de ejecución de Ruby. –
@mu es demasiado corto Inspirado por su comentario, agregué a mi pregunta. – sawa