]> git.eshelyaron.com Git - emacs.git/commitdiff
eieio-compat.el: Move to lisp/obsolete
authorStefan Monnier <monnier@iro.umontreal.ca>
Tue, 14 Dec 2021 22:01:34 +0000 (17:01 -0500)
committerStefan Monnier <monnier@iro.umontreal.ca>
Tue, 14 Dec 2021 22:01:34 +0000 (17:01 -0500)
The file only contains obsolete definitions, so it really belongs in
`lisp/obsolete`.  Moving it there will also signal a warning for those
people who run old `.elc` files using EIEIO and generated with
Emacs<25 and who otherwise might not know about the obsolescence of
some of the functions they use.

* lisp/emacs-lisp/eieio-compat.el: Move to ...
* lisp/obsolete/eieio-compat.el: ... here.

lisp/emacs-lisp/eieio-compat.el
lisp/obsolete/eieio-compat.el [new file with mode: 0644]

index 60b0638c63fb02cc3660222aac09350cfe7e8c8d..a5f375000924d2ee6c9ba1e8fc746a26ac8927c1 100644 (file)
@@ -5,6 +5,7 @@
 ;; Author: Eric M. Ludlam <zappo@gnu.org>
 ;; Keywords: OO, lisp
 ;; Package: eieio
+;; Obsolete-Since: 25.1
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/obsolete/eieio-compat.el b/lisp/obsolete/eieio-compat.el
new file mode 100644 (file)
index 0000000..60b0638
--- /dev/null
@@ -0,0 +1,277 @@
+;;; eieio-compat.el --- Compatibility with Older EIEIO versions  -*- lexical-binding:t -*-
+
+;; Copyright (C) 1995-1996, 1998-2021 Free Software Foundation, Inc.
+
+;; Author: Eric M. Ludlam <zappo@gnu.org>
+;; Keywords: OO, lisp
+;; Package: eieio
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Backward compatibility definition of old EIEIO functions in
+;; terms of newer equivalent.
+
+;; The main elements are the old EIEIO `defmethod' and `defgeneric' which are
+;; now implemented on top of cl-generic.  The differences we have to
+;; accommodate are:
+;; - EIEIO's :static methods (turned into a new `eieio--static' specializer).
+;; - EIEIO's support for `call-next-method' and `next-method-p' instead of
+;;   `cl-next-method-p' and `cl-call-next-method' (simple matter of renaming).
+;; - Different errors are signaled.
+;; - EIEIO's defgeneric does not reset the function.
+;; - EIEIO's no-next-method and no-applicable-method can't be aliases of
+;;   cl-generic's namesakes since they have different calling conventions,
+;;   which means that packages that (defmethod no-next-method ..) don't work.
+;; - EIEIO's `call-next-method' and `next-method-p' had dynamic scope whereas
+;;   cl-generic's `cl-next-method-p' and `cl-call-next-method' are lexically
+;;   scoped.
+
+;;; Code:
+
+(require 'eieio-core)
+(require 'cl-generic)
+
+(put 'eieio--defalias 'byte-hunk-handler
+     #'byte-compile-file-form-defalias) ;;(get 'defalias 'byte-hunk-handler)
+;;;###autoload
+(defun eieio--defalias (name body)
+  "Like `defalias', but with less side-effects.
+More specifically, it has no side-effects at all when the new function
+definition is the same (`eq') as the old one."
+  (cl-assert (not (symbolp body)))
+  (while (and (fboundp name) (symbolp (symbol-function name)))
+    ;; Follow aliases, so methods applied to obsolete aliases still work.
+    (setq name (symbol-function name)))
+  (unless (and (fboundp name)
+               (eq (symbol-function name) body))
+    (defalias name body)))
+
+;;;###autoload
+(defmacro defgeneric (method args &optional doc-string)
+  "Create a generic function METHOD.
+DOC-STRING is the base documentation for this class.  A generic
+function has no body, as its purpose is to decide which method body
+is appropriate to use.  Uses `defmethod' to create methods, and calls
+`defgeneric' for you.  With this implementation the ARGS are
+currently ignored.  You can use `defgeneric' to apply specialized
+top level documentation to a method."
+  (declare (doc-string 3) (obsolete cl-defgeneric "25.1")
+           (indent defun))
+  `(eieio--defalias ',method
+                    (eieio--defgeneric-init-form
+                     ',method
+                     ,(if doc-string (help-add-fundoc-usage doc-string args)))))
+
+;;;###autoload
+(defmacro defmethod (method &rest args)
+  "Create a new METHOD through `defgeneric' with ARGS.
+
+The optional second argument KEY is a specifier that
+modifies how the method is called, including:
+   :before  - Method will be called before the :primary
+   :primary - The default if not specified
+   :after   - Method will be called after the :primary
+   :static  - First arg could be an object or class
+The next argument is the ARGLIST.  The ARGLIST specifies the arguments
+to the method as with `defun'.  The first argument can have a type
+specifier, such as:
+  ((VARNAME CLASS) ARG2 ...)
+where VARNAME is the name of the local variable for the method being
+created.  The CLASS is a class symbol for a class made with `defclass'.
+A DOCSTRING comes after the ARGLIST, and is optional.
+All the rest of the args are the BODY of the method.  A method will
+return the value of the last form in the BODY.
+
+Summary:
+
+ (defmethod mymethod [:before | :primary | :after | :static]
+                     ((typearg class-name) arg2 &optional opt &rest rest)
+    \"doc-string\"
+     body)"
+  (declare (doc-string 3) (obsolete cl-defmethod "25.1")
+           (indent defun)
+           (debug
+            (&define                    ; this means we are defining something
+             [&name sexp]   ;Allow (setf ...) additionally to symbols.
+             ;; ^^ This is the methods symbol
+             [ &optional symbolp ]                ; this is key :before etc
+             cl-generic-method-args               ; arguments
+             [ &optional stringp ]                ; documentation string
+             def-body                             ; part to be debugged
+             )))
+  (let* ((key (if (keywordp (car args)) (pop args)))
+        (params (car args))
+        (arg1 (car params))
+         (fargs (if (consp arg1)
+                   (cons (car arg1) (cdr params))
+                 params))
+        (class (if (consp arg1) (nth 1 arg1)))
+         (code `(lambda ,fargs ,@(cdr args))))
+    `(progn
+       ;; Make sure there is a generic and the byte-compiler sees it.
+       (defgeneric ,method ,args)
+       (eieio--defmethod ',method ',key ',class #',code))))
+
+(defun eieio--generic-static-symbol-specializers (tag &rest _)
+  (cl-assert (or (null tag) (eieio--class-p tag)))
+  (when (eieio--class-p tag)
+    (let ((superclasses (eieio--generic-subclass-specializers tag))
+         (specializers ()))
+      (dolist (superclass superclasses)
+       (push superclass specializers)
+       (push `(eieio--static ,(cadr superclass)) specializers))
+      (nreverse specializers))))
+
+(cl-generic-define-generalizer eieio--generic-static-symbol-generalizer
+  ;; Give it a slightly higher priority than `subclass' so that the
+  ;; interleaved list comes before subclass's non-interleaved list.
+  61 (lambda (name &rest _) `(and (symbolp ,name) (cl--find-class ,name)))
+  #'eieio--generic-static-symbol-specializers)
+(cl-generic-define-generalizer eieio--generic-static-object-generalizer
+  ;; Give it a slightly higher priority than `class' so that the
+  ;; interleaved list comes before the class's non-interleaved list.
+  51 #'cl--generic-struct-tag
+  (lambda (tag &rest _)
+    (and (symbolp tag) (setq tag (cl--find-class tag))
+         (eieio--class-p tag)
+         (let ((superclasses (eieio--class-precedence-list tag))
+               (specializers ()))
+           (dolist (superclass superclasses)
+             (setq superclass (eieio--class-name superclass))
+             (push superclass specializers)
+             (push `(eieio--static ,superclass) specializers))
+           (nreverse specializers)))))
+
+(cl-defmethod cl-generic-generalizers ((_specializer (head eieio--static)))
+  (list eieio--generic-static-symbol-generalizer
+        eieio--generic-static-object-generalizer))
+
+;;;###autoload
+(defun eieio--defgeneric-init-form (method doc-string)
+  (if doc-string (put method 'function-documentation doc-string))
+  (if (memq method '(no-next-method no-applicable-method))
+      (symbol-function method)
+    (let ((generic (cl-generic-ensure-function method)))
+      (or (symbol-function (cl--generic-name generic))
+          (cl--generic-make-function generic)))))
+
+;;;###autoload
+(defun eieio--defmethod (method kind argclass code)
+  (setq kind (intern (downcase (symbol-name kind))))
+  (let* ((specializer (if (not (eq kind :static))
+                          (or argclass t)
+                        (setq kind nil)
+                        `(eieio--static ,argclass)))
+         (uses-cnm (not (memq kind '(:before :after))))
+         (specializers `((arg ,specializer)))
+         (code
+          ;; Backward compatibility for `no-next-method' and
+          ;; `no-applicable-method', which have slightly different calling
+          ;; convention than their cl-generic counterpart.
+          (pcase method
+            ('no-next-method
+             (setq method 'cl-no-next-method)
+             (setq specializers `(generic method ,@specializers))
+             (lambda (_generic _method &rest args) (apply code args)))
+            ('no-applicable-method
+             (setq method 'cl-no-applicable-method)
+             (setq specializers `(generic ,@specializers))
+             (lambda (generic arg &rest args)
+               (apply code arg (cl--generic-name generic) (cons arg args))))
+            (_ code))))
+    (cl-generic-define-method
+     method (unless (memq kind '(nil :primary)) (list kind))
+     specializers uses-cnm
+     (if uses-cnm
+         (let* ((docstring (documentation code 'raw))
+                (args (help-function-arglist code 'preserve-names))
+                (doc-only (if docstring
+                              (let ((split (help-split-fundoc docstring nil)))
+                                (if split (cdr split) docstring)))))
+           (lambda (cnm &rest args)
+             (:documentation
+              (help-add-fundoc-usage doc-only (cons 'cl-cnm args)))
+             (cl-letf (((symbol-function 'call-next-method) cnm)
+                       ((symbol-function 'next-method-p)
+                        (lambda () (cl--generic-isnot-nnm-p cnm))))
+               (apply code args))))
+       code))
+    ;; The old EIEIO code did not signal an error when there are methods
+    ;; applicable but only of the before/after kind.  So if we add a :before
+    ;; or :after, make sure there's a matching dummy primary.
+    (when (and (memq kind '(:before :after))
+               ;; FIXME: Use `cl-find-method'?
+               (not (cl-find-method method ()
+                                    (mapcar (lambda (arg)
+                                              (if (consp arg) (nth 1 arg) t))
+                                            specializers))))
+      (cl-generic-define-method method () specializers t
+                                (lambda (cnm &rest args)
+                                  (if (cl--generic-isnot-nnm-p cnm)
+                                      (apply cnm args)))))
+    method))
+
+;; Compatibility with code which tries to catch `no-method-definition' errors.
+(push 'no-method-definition (get 'cl-no-applicable-method 'error-conditions))
+
+(defun generic-p (fname) (not (null (cl--generic fname))))
+
+(defun no-next-method (&rest args)
+  (declare (obsolete cl-no-next-method "25.1"))
+  (apply #'cl-no-next-method 'unknown nil args))
+
+(defun no-applicable-method (object method &rest args)
+  (declare (obsolete cl-no-applicable-method "25.1"))
+  (apply #'cl-no-applicable-method method object args))
+
+(define-obsolete-function-alias 'call-next-method 'cl-call-next-method "25.1")
+(defun next-method-p ()
+  (declare (obsolete cl-next-method-p "25.1"))
+  ;; EIEIO's `next-method-p' just returned nil when called in an
+  ;; invalid context.
+  (message "next-method-p called outside of a primary or around method")
+  nil)
+
+;;;###autoload
+(defun eieio-defmethod (method args)
+  "Obsolete work part of an old version of the `defmethod' macro."
+  (declare (obsolete cl-defmethod "24.1"))
+  (eval `(defmethod ,method ,@args))
+  method)
+
+;;;###autoload
+(defun eieio-defgeneric (method doc-string)
+  "Obsolete work part of an old version of the `defgeneric' macro."
+  (declare (obsolete cl-defgeneric "24.1"))
+  (eval `(defgeneric ,method (x) ,@(if doc-string `(,doc-string))))
+  ;; Return the method
+  'method)
+
+;;;###autoload
+(defun eieio-defclass (cname superclasses slots options)
+  (declare (obsolete eieio-defclass-internal "25.1"))
+  (eval `(defclass ,cname ,superclasses ,slots ,@options)))
+
+
+;; Local Variables:
+;; generated-autoload-file: "eieio-loaddefs.el"
+;; End:
+
+(provide 'eieio-compat)
+
+;;; eieio-compat.el ends here