Изменился ли формат байт-кода для сигнатур функций?

(defun magit-max-args-internal (function)
    "Return the maximum number of arguments accepted by FUNCTION."
    (if (symbolp function)
        (setq function (symbol-function function)))
    (if (subrp function)
        (let ((max (cdr (subr-arity function))))
          (if (eq 'many max)
              most-positive-fixnum
            max))
      (if (eq 'macro (car-safe function))
          (setq function (cdr function)))
      (let ((arglist (if (byte-code-function-p function)
                         (aref function 0) ; <--------- format changed
                       (cadr function))))
        (if (memq '&rest arglist)
            most-positive-fixnum
          (length (remq '&optional arglist))))))

Мне пришлось перекомпилировать magit.el и обнаружил эту проблему в их коде. Если я правильно следую коду, то они искали арность функции, но вместо этого они получали какое-то «странное» число. Есть идеи, что случилось?


Кроме того, этот пост: Elisp get function arity? предлагает лучшее решение (которое отлично справляется с этой задачей, ответ Андреаса Рёлера Так что я, вероятно, попытаюсь предложить это magit сопровождающим.


person Community    schedule 01.10.2013    source источник
comment
Воспользовавшись этой возможностью, вы, вероятно, захотите спросить, зачем им вообще нужно определять арность функции во время выполнения. Это странная вещь, и обычно это хороший признак того, что вы делаете что-то неправильно.   -  person lunaryorn    schedule 01.10.2013
comment
@lunaryorn Я вроде как вижу причину. Похоже, они пытаются обслужить некоторые старые версии некоторых встроенных модулей, которые изменили количество аргументов. Потребуются некоторые усилия, чтобы раскопать более ранние версии delete-directory, но, взглянув на них, есть большая вероятность, что у него когда-то было другое количество аргументов, аргумент trash, означающий перемещение в корзину, вероятно, появится позже, чем сама функция.   -  person    schedule 02.10.2013
comment
Да, delete-directory изначально имел только то, что сейчас является его первым аргументом. Когда ожидается, что код будет работать для разных версий Emacs, нет ничего необычного в том, чтобы вызывать одну и ту же функцию с другой последовательностью вызовов. Проверка арности - это один из способов попытаться с этим справиться, но иногда количество аргументов не различается, но различаются их типы или значения. Другие подходы: (а) проверка версии Emacs и (б) попытка различных вызовов внутри conditon-case или ignore-errors.   -  person Drew    schedule 02.10.2013
comment
@wvxvw: Вы упомянули встроенные модули. FWIW, если они хотят проверить только арность встроенного модуля, они могут сделать это напрямую, используя subr-arity. (Возможно, вы имели в виду что-то отличное от встроенного - возможно, вы имели в виду функцию, предоставляемую ванильным кодом?)   -  person Drew    schedule 02.10.2013
comment
@Drew Ага, я не имел в виду C-only, весь код, который говорит о части GNU Emacs. Почему help-function-arglist не лучший вариант? Кажется, прямолинейно? (Чтобы проверить версию Emacs, вам нужно будет вести исторический список функций с их сигнатурами, что кажется менее общим).   -  person    schedule 02.10.2013
comment
@wvxvw: Я не предполагал, что help-function-arglist - плохой вариант. Я только хотел сказать, что (а) да, как вы сказали, нет предопределенной функции Emacs для получения арности, (б) да, как вы сказали, бывают случаи, когда проверка арности полезна, и (в) использование Обработка condition-case и wrong-number-of-arguments - это еще один способ решения проблемы измененной последовательности вызовов. help-function-arglist - еще один подход, но он недоступен в некоторых старых версиях Emacs.   -  person Drew    schedule 02.10.2013
comment
@Drew Похоже, что это уже было исправлено, у меня только что была ранняя версия magit. github.com/magit/magit/issues/975 для использования в будущем.   -  person    schedule 02.10.2013
comment
@wvxvw Я все еще не вижу причин. Как указал Стефан в своем ответе, им лучше сначала просто попытаться вызвать функцию со всеми аргументами, а затем постепенно перейти к вызовам с меньшим количеством аргументов, если поступит сигнал wrong-number-of-arguments.   -  person lunaryorn    schedule 02.10.2013
comment
@lunaryorn ну, это их работа, я на самом деле не отвечаю за то, как они это делают ... Но если бы это было до меня, я бы не делал этого так, и особенно не в этой функции: у нее есть необязательные аргументы , поэтому в конечном итоге он может работать с меньшим количеством аргументов, чем с новой версией, поэтому вы можете ошибочно полагать, что используете старую версию. И это удаляет каталоги! Подумайте, что бы произошло, если бы вы поверили, что переместили каталог в корзину, но на самом деле вы просто удалили его навсегда ... это не то место, где я хотел бы попробовать и посмотреть, что произойдет :)   -  person    schedule 02.10.2013
comment
@wvxvw Хм, я не уверен, что могу следить за тобой. Я не понимаю, почему необязательные аргументы актуальны. Имхо они не меняют игру. Сначала вы вызываете (delete-directory "foo" t), чтобы попытаться выполнить прямое рекурсивное удаление, и возвращаетесь к ручному рекурсивному удалению на основе варианта delete-directory с одним аргументом, если wrong-number-of-arguments сигнализируется. Что здесь могло пойти не так ?! На самом деле, это гораздо более очевидно и предсказуемо, чем подсчет аргументов, который с гораздо большей вероятностью будет опровергнут необязательными аргументами ...   -  person lunaryorn    schedule 02.10.2013
comment
@lunaryorn, вы совершенно не хотите сначала пытаться выполнить прямое удаление. Поверьте мне ... Что, если ошибка не та, которую вы думаете? Что, если ошибка возникла из-за состояния гонки? И так далее. Я имею в виду, что с этим может быть масса разных проблем, и особенно те, о которых я не могу думать в данный момент, но их правдоподобия достаточно, чтобы напугать меня :) Что, если с помощью этого метода проб и ошибок вы удалили самые ценные воспоминания кого-то из них? их ПК?   -  person    schedule 02.10.2013
comment
@wvxvw Я все еще не слежу за вашей инсценировкой. Весь смысл delete-directory в том, чтобы удалить каталог. Вызов его дважды в худшем случае вызовет ошибку во втором вызове, потому что каталог больше не существует. Но в любом случае Emacs будет сигнализировать wrong-number-of-arguments тогда и только тогда, когда функция была вызвана с неправильным числом аргументов. С выражением condition-case, например в ответе Стефана, будет обнаружена только эта ошибка. Все остальные ошибки распространяются и не вызывают повторного вызова delete-directory.   -  person lunaryorn    schedule 02.10.2013
comment
Как бы то ни было, мы больше этого не делаем.   -  person tarsius    schedule 30.12.2013


Ответы (1)


Действительно, это «число в (aref байт-код 0)» было введено для lexical-binding. Лучшее решение - выбросить magit-max-args-internal и использовать вместо него (condition-case nil (delete-directory <args>) (wrong-number-of-arguments (delete-directory <fewerargs>)).

person Stefan    schedule 02.10.2013