Неожиданное значение __callee__ при включении модуля — это ошибка Ruby?

При вызове через метод, созданный alias_method, __callee__ игнорирует имя старого метода (здесь xxx) и возвращает имя нового метода, как показано ниже:

class Foo
  def xxx() __callee__ end
  alias_method :foo, :xxx
end

Foo.new.foo # => :foo

Это поведение сохраняется, даже если xxx наследуется от суперкласса:

class Sup
  def xxx() __callee__ end
end

class Bar < Sup
  alias_method :bar, :xxx
end

Bar.new.bar # => :bar

Учитывая оба вышеизложенных, я ожидаю, что такое же поведение будет сохраняться, когда xxx включается через модуль. Однако это не так:

module Mod
  def xxx() __callee__ end
end

class Baz
  include Mod
  alias_method :baz, :xxx
end

Baz.new.baz # => :xxx

Я ожидаю, что возвращаемое значение будет :baz, а не :xxx.


Приведенный выше код был выполнен с использованием Ruby 2.3.1p112. Это ошибка в реализации __callee__? Или, может быть, alias_method? А если нет, то может ли кто-нибудь объяснить, почему включение модуля ведет себя по-другому?


ОБНОВЛЕНИЕ 1

Я отправил это в систему отслеживания ошибок Ruby, чтобы попытаться найти ответ.


ОБНОВЛЕНИЕ 2

Судя по всему, я не единственный, кто удивлен этой проблемой. Интересно, является ли редакция 50728 (которая предназначалась для решения Ошибка 11046: __callee__ возвращает неверное имя метода в потерянном процессе) может быть связано.


person user513951    schedule 09.02.2016    source источник
comment
Очень интересно, точно менял между 2.2 и 2.3. То же самое для __method__.   -  person Nils Landt    schedule 09.02.2016
comment
@NilsLandt Можете ли вы привести пример поведения __method__? Я заменил __callee__ на __method__ и получил xxx во всех трех случаях. Отличается ли он в Ruby 2.2?   -  person user513951    schedule 09.02.2016


Ответы (2)


Вы можете увидеть разницу между __callee__ и __method__ в модуле ядра Ruby.

Разница в вызовах prev_frame_callee() и prev_frame_func() соответственно. Я нашел эти определения функций по адресу http://rxr.whitequark.org/mri/source/eval.c

Короче говоря, Foo и Bar немедленно вызывают псевдонимы foo и bar (которые являются именами для xxx), в то время как Baz должен найти Mod и вызвать xxx из Mod. __method__ ищет идентификатор исходного вызываемого метода, а __callee__ ищет идентификатор вызываемого метода, ближайший к вызову __callee__. Это лучше видно в eval.c в строках с 848 по 906: обратите внимание на разницу в двух методах в ответных вызовах, похожих на <something> -> called_id и <something> -> def->original_id.

Также, если вы посмотрите на ядро ​​версии 1.9.3, вы увидите, что эти два метода изначально были одинаковыми. Итак, в какой-то момент между ними произошло целенаправленное изменение.

person fkantner    schedule 19.05.2016

Это была ошибка, и она была закрыта 3 дня назад этим примечанием:

Кажется, исправлено r56592.

person user513951    schedule 20.01.2017