Модуль.вложенность в экземпляр eval / exec или модуль 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 ответов
предупреждение: это немного длинный бред. Немного тур по исходному коду 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
. The 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_eval
и module_exec
в качестве упражнения для читателя. В любом случае, это выглядит как instance_eval
исключена из Module.nesting
список; это, однако, больше отвлекает, чем что-либо еще, это просто означает, что вы не увидите что-то эвалс упомянул.
Итак, теперь вопрос: "Что такое NODE
и rb_vm_cref()
все о?".
если вы посмотрите в node.h
вы увидите кучу констант узлов для различных ключевых слов Ruby и языковых структур:
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
кажется, последовательность инструкций и lfp
и dfp
указатели кадра:
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 A
на cref
связанный список (что фильтруется для получения 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 }
у вас не будет module A
на cref
больше, потому что вы уже попали в 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
документация говорит:
возвращает список модулей, вложенных в точке вызова.
я думаю, что это заявление в сочетании с обзором внутренних органов указывает на то, что Module.nesting
фактически зависит от конкретного буквального контекста, в котором он вызывается.
если у кого-то с большим опытом работы в Ruby internals есть что добавить, я могу передать это сообществу SO как сообществу вики.
обновление: все это относится и к class_eval
так же, как и module_eval
и это также относится к 1.9.3, а также к 1.9.2.