Argspec o arity di una funzione bytecode in Emacs 24


8

Ho un codice che testa l'arità di una funzione. Lo uso per determinare se sono presenti argomenti opzionali aggiunti nelle ultime versioni di un pacchetto. Richiede subr-arityfunzioni integrate e analizza l'arglista di oggetti bytecode e lambda.

(defun function-argspec (func)
  (if (symbolp func) (setq func (indirect-function func)))
  (cond
   ((byte-code-function-p func)
    (aref func 0))
   ((and (consp func)
         (eq (car func) 'lambda)
         (consp (cdr func)))
    (car (cdr func)))
  ))

Questo ha funzionato bene fino a Emacs 23. In Emacs 24.3 su Ubuntu 14.04, funziona bene per alcune funzioni, ma non per altre.

(function-argspec 'revert-buffer)
(&optional ignore-auto noconfirm preserve-modes)
(require 'vc)
vc
(function-argspec 'vc-print-log-internal)
1283

Evidentemente il formato del bytecode è cambiato in un modo che non si riflette nel manuale .

(symbol-function 'vc-print-log-internal)
#[1283 \301\211\302\301\211\203\211@\303!\203\304\262A\266\202\202\210\203'\305>\202*\306>??\262\2036\307\2027\310\262\311
\312\313\314\315\316
$\317"\320\321%\312\322\323\315\316#\324"\325\326%\312\327\330\315\316!\331"\332\333%\312\334\335\315\316%\336"\325\337%&\262\207 [vc-log-short-style nil *vc-change-log* file-directory-p t directory file short long vc-log-internal-common make-byte-code 1028 \304\305\303\301\205\300\302&\207 vconcat vector [vc-call-backend print-log] 12 

(fn BK BUF TYPE-ARG FILES-ARG) 771 \303\300\301\302$\207 [vc-print-log-setup-buttons] 8 

(fn BK FILES-ARG RET) 257 \301\302\300#\207 [vc-call-backend show-log-entry] 5 

(fn BK) 514 \305\300\301\302\303\304%\207 [vc-print-log-internal] 

(fn IGNORE-AUTO NOCONFIRM)] 28 

(fn BACKEND FILES WORKING-REVISION &optional IS-START-REVISION LIMIT)]

Come posso accedere in modo affidabile all'elenco degli argomenti di un oggetto bytecode? Solo sapendo che avrebbe fatto l'arità, non mi importa dei nomi degli argomenti. Più precisamente, voglio sapere quanti argomenti sono obbligatori e quanti argomenti sono opzionali o, in altri termini, voglio le stesse informazioni da cui ottengo subr-arity. Ovviamente il mio codice deve far fronte al bytecode sia vecchio che nuovo, quindi devo sapere non solo dove scavare ma anche quando scavare dove.


Tangenziale: forse hai appena rimosso questa parte per tagliare l'esempio, ma potresti voler aggiungere il supporto per le chiusure nel tuo function-argspec.
Malabarba,

Gilles, hai una versione finalizzata della tua function-argspecfunzione da qualche parte, comprese le funzioni bytecode e le chiusure?
Jordon Biondo,

@JordonBiondo L'ho aggiunto come risposta qui.
Gilles 'SO- smetti di essere malvagio' il

Risposte:


8

Modifica: Woo! Ho trovato una funzione che prenderà la normale lista di argomenti o la versione intera e restituirà in qualche modo una firma: byte-compile-arglist-signaturein bytecomp.el!

(byte-compile-arglist-signature 1283) ;; => (3 . 5)

Risposta iniziale:

Spero che qualcun altro possa chiedere se questo è documentato o meno da qualche parte, ma questo è quello che ho imparato leggendo exec_byte_codeall'interno di bytecode.c nella fonte di Emacs.

Il numero che vedi viene usato per calcolare l'argspec quando il codice byte viene effettivamente eseguito, presumo che per prestazioni, in realtà è abbastanza intelligente.

Ho scritto questo codice per mostrarti come calcolare l'arità di una funzione dato quel numero:

(defun arity-info (byte-code-int)
  (let* ((required  (logand byte-code-int 127))
         (total-named  (lsh byte-code-int -8))
         (optional (- total-named required))
         (allow-rest  (if (not (zerop (logand byte-code-int 128))) "yes" "no")))
    (list
     (cons 'required required)
     (cons 'total-named total-named)
     (cons 'optional optional)
     (cons 'allow-rest allow-rest))))

Possiamo vedere qui che se corriamo arity-infocon 1283 otteniamo quanto segue:

((required . 3) (total-named . 5) (optional . 2) (allow-rest . "no"))

che puoi vedere descrive l'arità di vc-print-log-internalperfettamente, 5 argomenti totali, 3 obbligatori, 2 opzionali, non consentono e riposano.

(vc-print-log-internal BACKEND FILES WORKING-REVISION &optional IS-START-REVISION LIMIT)

Buon lavoro. [filler chars]
Ha

2

Su richiesta, ecco la mia implementazione di function-argspece function-arity. Ho usato la soluzione originale di Jordon Biondo per il bytecode Emacs 24.

(cond
 ;; XEmacs
 ((fboundp 'compiled-function-arglist)
  (defalias 'emacsen-compiled-function-arglist 'compiled-function-arglist))
 ;; GNU Emacs
 (t
  (defun emacsen-make-up-number-arglist (start end tail)
    (while (< start end)
      (setq end (1- end))
      (setq tail (cons (intern (format "a%d" end)) tail)))
    tail)
  (defun emacsen-compiled-function-arglist (func)
    (let ((a (aref func 0)))
      (if (integerp a)
          ;; An integer encoding the arity. Encountered in Emacs 24.3.
          ;; /emacs/971/argspec-or-arity-of-a-bytecode-function-in-emacs-24/973#973
          (let ((arglist (if (zerop (logand a 128))
                             nil
                           '(&rest rest)))
                (mandatory (logand a 127))
                (nonrest (lsh a -8)))
            (if (> nonrest mandatory)
                (setq arglist (cons '&optional (emacsen-make-up-number-arglist mandatory nonrest arglist))))
            (emacsen-make-up-number-arglist 0 mandatory arglist))
        ;; Otherwise: this is the arglist. The only format I've seen up to GNU 23.
        a)))))

(defun function-argspec (func)
  "Return a function's argument list.
For byte-compiled functions in Emacs >=24, some information may be lost as the
byte compiler sometimes erases argument names. In this case, fake argument names
are reconstructed."
  (if (symbolp func) (setq func (indirect-function func)))
  (cond
   ((subrp func)
    (let ((docstring (documentation func)))
      (save-match-data
        (if (string-match "\n.*\\'" docstring)
            (let ((form (read (match-string 0 docstring))))
              (cdr form))
          nil))))
   ((byte-code-function-p func)
    (emacsen-compiled-function-arglist func))
   ((and (consp func)
         (eq (car func) 'lambda)
         (consp (cdr func)))
    (car (cdr func)))
   ((and (consp func)
         (eq (car func) 'closure)
         (consp (cdr func))
         (consp (cdr (cdr func))))
    (car (cdr (cdr func))))
   (t (signal 'wrong-type-argument
              (list 'functionp func)))))

(defun function-arity (func)
  "Return a function's arity as (MIN . MAX).
Return minimum and maximum number of args allowed for SUBR.
The returned value is a pair (MIN . MAX).  MIN is the minimum number
of args.  MAX is the maximum number or the symbol `many', for a
function with `&rest' args, or `unevalled' for a special form.

This function is like `subr-arity', but also works with user-defined
and byte-code functions. Symbols are dereferenced through
`indirect-function'."
  ;; TODO: keyword support
  (if (symbolp func) (setq func (indirect-function func)))
  (cond
   ((subrp func)
    (subr-arity func))
   (t
    (let ((mandatory 0) (optional 0) (rest nil)
          (where 'mandatory))
      (when (and (consp func) (eq 'macro (car func)))
        (setq func (cdr func))
        (setq rest 'unevalled))
      (let ((argspec (function-argspec func)))
        (dolist (arg argspec)
          (cond
           ((eq arg '&optional) (setq where 'optional))
           ((eq arg '&rest) (unless rest (setq rest 'many)))
           (t (set where (+ (symbol-value where) 1)))))
        (cons mandatory (or rest (+ mandatory optional))))))))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.