2011-06-14 15 views
8

Se me ocurrió esta pregunta cuando estaba tratando de responder this. El siguiente es un comportamiento esperado:Module.nesting dentro de instance_eval/exec o module_eval/exec

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

Pero el siguiente:

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

toda la vuelta []. ¿Por qué estos no funcionan como lo anterior?

pregunta adicional

MU es demasiado corto sugirió un punto interesante. Si eso es correcto, entonces Module.nesting sería uno de los métodos y variables que dependen del contexto literal como Method#source_location, __FILE__. ¿Es este entendimiento correcto? Si es así, ¿alguien puede proporcionar el inventario de estos métodos/variables que dependen del contexto literal? Creo que sería útil como referencia.

+1

["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. –

+0

@mu es demasiado corto Inspirado por su comentario, agregué a mi pregunta. – sawa

Respuesta

7

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.

+0

Gracias por la explicación impresionante y detallada. Wiki podría ser una buena idea. – sawa

Cuestiones relacionadas