Argspec hoặc arity của hàm bytecode trong Emacs 24


8

Tôi có mã kiểm tra tính chất của một hàm. Tôi sử dụng nó để xác định xem các đối số tùy chọn được thêm vào trong các phiên bản gần đây của gói có hiện diện hay không. Nó gọi subr-aritycác hàm dựng sẵn và phân tích cú pháp đối số của các đối tượng mã byte và lambdas.

(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)))
  ))

Điều này đã hoạt động tốt cho đến Emacs 23. Trong Emacs 24.3 trên Ubuntu 14.04, nó hoạt động tốt đối với một số chức năng, nhưng không phải cho các chức năng khác.

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

Rõ ràng là định dạng mã byte đã thay đổi theo cách không được phản ánh trong hướng dẫn .

(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)]

Làm thế nào tôi có thể truy cập một cách đáng tin cậy danh sách đối số của một đối tượng mã byte? Chỉ cần biết arity sẽ làm gì, tôi không quan tâm đến tên đối số. Chính xác hơn, tôi muốn biết có bao nhiêu đối số là bắt buộc và có bao nhiêu đối số là tùy chọn, hoặc trong các điều khoản khác, tôi muốn cùng một thông tin mà tôi nhận được từ đó subr-arity. Tất nhiên mã của tôi phải đối phó với cả mã byte kiểu cũ và kiểu mới, vì vậy tôi cần biết không chỉ đào ở đâu mà còn khi nào đào ở đâu.


Tiếp tuyến: có thể bạn vừa xóa phần này để cắt bớt ví dụ, nhưng bạn có thể muốn thêm hỗ trợ cho các lần đóng trong của mình function-argspec.
Malabarba

Gilles, bạn có một phiên bản hoàn chỉnh của function-argspecchức năng của bạn ở đâu đó, bao gồm các hàm bytecode và các bao đóng không?
Jordon Biondo

@JordonBiondo Tôi đã thêm nó như một câu trả lời ở đây.
Gilles 'SO- ngừng trở nên xấu xa'

Câu trả lời:


8

Chỉnh sửa: Woo! Tôi đã tìm thấy một hàm sẽ lấy danh sách đối số bình thường hoặc phiên bản số nguyên và trả về phần nào chữ ký: byte-compile-arglist-signaturein bytecomp.el!

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

Trả lời ban đầu:

Tôi hy vọng người khác có thể bấm nút xem liệu điều này có được ghi lại ở đâu đó không nhưng đây là những gì tôi học được bằng cách đọc exec_byte_codebên trong bytecode.c trong nguồn Emacs.

Số bạn thấy được sử dụng để tính toán argspec khi mã byte thực sự đang được chạy, tôi giả sử điều này cho hiệu suất, nó thực sự khá thông minh.

Tôi đã viết mã này để chỉ cho bạn cách tính độ phức tạp của hàm cho số đó:

(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))))

Chúng ta có thể thấy ở đây rằng nếu chúng ta chạy arity-infovới 1283, chúng ta sẽ nhận được như sau:

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

mà bạn có thể thấy mô tả mức độ vc-print-log-internalhoàn hảo, 5 tổng số đối số, 3 yêu cầu, 2 tùy chọn, không cho phép & nghỉ ngơi.

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

Làm tốt lắm. [ký tự điền]
vẽ

2

Theo yêu cầu, đây là việc tôi thực hiện function-argspecfunction-arity. Tôi đã sử dụng giải pháp ban đầu của Jordon Biondo cho mã hóa 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))))))))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.