diff options
| author | Stefan Monnier | 2022-04-04 15:06:47 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2022-04-04 15:06:47 -0400 |
| commit | 1f4f6b956bee611ffa406b3851e5264ee74e3bfb (patch) | |
| tree | 4d24cdef860e41b611c400492ef4a85b9a1b156a | |
| parent | 6c4a4cc94e9fea809b518da9fe9e581a6031a6df (diff) | |
| download | emacs-1f4f6b956bee611ffa406b3851e5264ee74e3bfb.tar.gz emacs-1f4f6b956bee611ffa406b3851e5264ee74e3bfb.zip | |
OClosure: add support for `slot-value`
* lisp/emacs-lisp/oclosure.el (oclosure--slot-index)
(oclosure--slot-value, oclosure--set-slot-value): New functions.
* lisp/emacs-lisp/eieio-core.el (eieio-oset, eieio-oref):
Consolidate the type test. Use `oclosure--(set-)slot-value`.
(eieio--validate-slot-value, eieio--validate-class-slot-value):
Don't presume `class` is an EIEIO class.
(eieio--class): Fix bogus `:type` info.
(eieio--object-class): Simplify.
(eieio--known-slot-name-p): New function.
(eieio-oref, eieio-oref-default, eieio-oset-default): Use it.
* test/lisp/emacs-lisp/oclosure-tests.el: Require `eieio`.
(oclosure-test): Make `name` field mutable.
(oclosure-test-slot-value): New test.
| -rw-r--r-- | lisp/emacs-lisp/eieio-core.el | 104 | ||||
| -rw-r--r-- | lisp/emacs-lisp/oclosure.el | 20 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/oclosure-tests.el | 19 |
3 files changed, 95 insertions, 48 deletions
diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el index ed1a28a24fb..d687289b22f 100644 --- a/lisp/emacs-lisp/eieio-core.el +++ b/lisp/emacs-lisp/eieio-core.el | |||
| @@ -92,7 +92,7 @@ Currently under control of this var: | |||
| 92 | (:copier nil)) | 92 | (:copier nil)) |
| 93 | children | 93 | children |
| 94 | initarg-tuples ;; initarg tuples list | 94 | initarg-tuples ;; initarg tuples list |
| 95 | (class-slots nil :type eieio--slot) | 95 | (class-slots nil :type (vector-of eieio--slot)) |
| 96 | class-allocation-values ;; class allocated value vector | 96 | class-allocation-values ;; class allocated value vector |
| 97 | default-object-cache ;; what a newly created object would look like. | 97 | default-object-cache ;; what a newly created object would look like. |
| 98 | ; This will speed up instantiation time as | 98 | ; This will speed up instantiation time as |
| @@ -130,10 +130,7 @@ Currently under control of this var: | |||
| 130 | class)) | 130 | class)) |
| 131 | 131 | ||
| 132 | (defsubst eieio--object-class (obj) | 132 | (defsubst eieio--object-class (obj) |
| 133 | (let ((tag (eieio--object-class-tag obj))) | 133 | (eieio--class-object (eieio--object-class-tag obj))) |
| 134 | (if eieio-backward-compatibility | ||
| 135 | (eieio--class-object tag) | ||
| 136 | tag))) | ||
| 137 | 134 | ||
| 138 | (defun class-p (x) | 135 | (defun class-p (x) |
| 139 | "Return non-nil if X is a valid class vector. | 136 | "Return non-nil if X is a valid class vector. |
| @@ -265,6 +262,10 @@ use '%s or turn off `eieio-backward-compatibility' instead" cname) | |||
| 265 | (defvar eieio--known-slot-names nil) | 262 | (defvar eieio--known-slot-names nil) |
| 266 | (defvar eieio--known-class-slot-names nil) | 263 | (defvar eieio--known-class-slot-names nil) |
| 267 | 264 | ||
| 265 | (defun eieio--known-slot-name-p (name) | ||
| 266 | (or (memq name eieio--known-slot-names) | ||
| 267 | (get name 'slot-name))) | ||
| 268 | |||
| 268 | (defun eieio-defclass-internal (cname superclasses slots options) | 269 | (defun eieio-defclass-internal (cname superclasses slots options) |
| 269 | "Define CNAME as a new subclass of SUPERCLASSES. | 270 | "Define CNAME as a new subclass of SUPERCLASSES. |
| 270 | SLOTS are the slots residing in that class definition, and OPTIONS | 271 | SLOTS are the slots residing in that class definition, and OPTIONS |
| @@ -704,13 +705,13 @@ an error." | |||
| 704 | nil | 705 | nil |
| 705 | ;; Trim off object IDX junk added in for the object index. | 706 | ;; Trim off object IDX junk added in for the object index. |
| 706 | (setq slot-idx (- slot-idx (eval-when-compile eieio--object-num-slots))) | 707 | (setq slot-idx (- slot-idx (eval-when-compile eieio--object-num-slots))) |
| 707 | (let* ((sd (aref (cl--class-slots class) | 708 | (let* ((sd (aref (eieio--class-slots class) |
| 708 | slot-idx)) | 709 | slot-idx)) |
| 709 | (st (cl--slot-descriptor-type sd))) | 710 | (st (cl--slot-descriptor-type sd))) |
| 710 | (cond | 711 | (cond |
| 711 | ((not (eieio--perform-slot-validation st value)) | 712 | ((not (eieio--perform-slot-validation st value)) |
| 712 | (signal 'invalid-slot-type | 713 | (signal 'invalid-slot-type |
| 713 | (list (eieio--class-name class) slot st value))) | 714 | (list (cl--class-name class) slot st value))) |
| 714 | ((alist-get :read-only (cl--slot-descriptor-props sd)) | 715 | ((alist-get :read-only (cl--slot-descriptor-props sd)) |
| 715 | (signal 'eieio-read-only (list (cl--class-name class) slot))))))) | 716 | (signal 'eieio-read-only (list (cl--class-name class) slot))))))) |
| 716 | 717 | ||
| @@ -725,7 +726,7 @@ an error." | |||
| 725 | slot-idx)))) | 726 | slot-idx)))) |
| 726 | (if (not (eieio--perform-slot-validation st value)) | 727 | (if (not (eieio--perform-slot-validation st value)) |
| 727 | (signal 'invalid-slot-type | 728 | (signal 'invalid-slot-type |
| 728 | (list (eieio--class-name class) slot st value)))))) | 729 | (list (cl--class-name class) slot st value)))))) |
| 729 | 730 | ||
| 730 | (defun eieio-barf-if-slot-unbound (value instance slotname fn) | 731 | (defun eieio-barf-if-slot-unbound (value instance slotname fn) |
| 731 | "Throw a signal if VALUE is a representation of an UNBOUND slot. | 732 | "Throw a signal if VALUE is a representation of an UNBOUND slot. |
| @@ -746,31 +747,35 @@ Argument FN is the function calling this verifier." | |||
| 746 | (ignore obj) | 747 | (ignore obj) |
| 747 | (pcase slot | 748 | (pcase slot |
| 748 | ((and (or `',name (and name (pred keywordp))) | 749 | ((and (or `',name (and name (pred keywordp))) |
| 749 | (guard (not (memq name eieio--known-slot-names)))) | 750 | (guard (not (eieio--known-slot-name-p name)))) |
| 750 | (macroexp-warn-and-return | 751 | (macroexp-warn-and-return |
| 751 | (format-message "Unknown slot `%S'" name) | 752 | (format-message "Unknown slot `%S'" name) |
| 752 | exp nil 'compile-only name)) | 753 | exp nil 'compile-only name)) |
| 753 | (_ exp)))) | 754 | (_ exp)))) |
| 755 | ;; FIXME: Make it a gv-expander such that the hash-table lookup is | ||
| 756 | ;; only performed once when used in `push' and friends? | ||
| 754 | (gv-setter eieio-oset)) | 757 | (gv-setter eieio-oset)) |
| 755 | (cl-check-type slot symbol) | 758 | (cl-check-type slot symbol) |
| 756 | (cl-check-type obj (or eieio-object class cl-structure-object)) | 759 | (cond |
| 757 | (let* ((class (cond ((symbolp obj) | 760 | ((cl-typep obj '(or eieio-object cl-structure-object)) |
| 758 | (error "eieio-oref called on a class: %s" obj) | 761 | (let* ((class (eieio--object-class obj)) |
| 759 | (eieio--full-class-object obj)) | 762 | (c (eieio--slot-name-index class slot))) |
| 760 | (t (eieio--object-class obj)))) | 763 | (if (not c) |
| 761 | (c (eieio--slot-name-index class slot))) | 764 | ;; It might be missing because it is a :class allocated slot. |
| 762 | (if (not c) | 765 | ;; Let's check that info out. |
| 763 | ;; It might be missing because it is a :class allocated slot. | 766 | (if (setq c (eieio--class-slot-name-index class slot)) |
| 764 | ;; Let's check that info out. | 767 | ;; Oref that slot. |
| 765 | (if (setq c (eieio--class-slot-name-index class slot)) | 768 | (aref (eieio--class-class-allocation-values class) c) |
| 766 | ;; Oref that slot. | 769 | ;; The slot-missing method is a cool way of allowing an object author |
| 767 | (aref (eieio--class-class-allocation-values class) c) | 770 | ;; to intercept missing slot definitions. Since it is also the LAST |
| 768 | ;; The slot-missing method is a cool way of allowing an object author | 771 | ;; thing called in this fn, its return value would be retrieved. |
| 769 | ;; to intercept missing slot definitions. Since it is also the LAST | 772 | (slot-missing obj slot 'oref)) |
| 770 | ;; thing called in this fn, its return value would be retrieved. | 773 | (eieio-barf-if-slot-unbound (aref obj c) obj slot 'oref)))) |
| 771 | (slot-missing obj slot 'oref)) | 774 | ((cl-typep obj 'oclosure) (oclosure--slot-value obj slot)) |
| 772 | (cl-check-type obj (or eieio-object cl-structure-object)) | 775 | (t |
| 773 | (eieio-barf-if-slot-unbound (aref obj c) obj slot 'oref)))) | 776 | (signal 'wrong-type-argument |
| 777 | (list '(or eieio-object cl-structure-object oclosure) obj))))) | ||
| 778 | |||
| 774 | 779 | ||
| 775 | 780 | ||
| 776 | (defun eieio-oref-default (class slot) | 781 | (defun eieio-oref-default (class slot) |
| @@ -782,7 +787,7 @@ Fills in CLASS's SLOT with its default value." | |||
| 782 | (ignore class) | 787 | (ignore class) |
| 783 | (pcase slot | 788 | (pcase slot |
| 784 | ((and (or `',name (and name (pred keywordp))) | 789 | ((and (or `',name (and name (pred keywordp))) |
| 785 | (guard (not (memq name eieio--known-slot-names)))) | 790 | (guard (not (eieio--known-slot-name-p name)))) |
| 786 | (macroexp-warn-and-return | 791 | (macroexp-warn-and-return |
| 787 | (format-message "Unknown slot `%S'" name) | 792 | (format-message "Unknown slot `%S'" name) |
| 788 | exp nil 'compile-only name)) | 793 | exp nil 'compile-only name)) |
| @@ -817,24 +822,29 @@ Fills in CLASS's SLOT with its default value." | |||
| 817 | (defun eieio-oset (obj slot value) | 822 | (defun eieio-oset (obj slot value) |
| 818 | "Do the work for the macro `oset'. | 823 | "Do the work for the macro `oset'. |
| 819 | Fills in OBJ's SLOT with VALUE." | 824 | Fills in OBJ's SLOT with VALUE." |
| 820 | (cl-check-type obj (or eieio-object cl-structure-object)) | ||
| 821 | (cl-check-type slot symbol) | 825 | (cl-check-type slot symbol) |
| 822 | (let* ((class (eieio--object-class obj)) | 826 | (cond |
| 823 | (c (eieio--slot-name-index class slot))) | 827 | ((cl-typep obj '(or eieio-object cl-structure-object)) |
| 824 | (if (not c) | 828 | (let* ((class (eieio--object-class obj)) |
| 825 | ;; It might be missing because it is a :class allocated slot. | 829 | (c (eieio--slot-name-index class slot))) |
| 826 | ;; Let's check that info out. | 830 | (if (not c) |
| 827 | (if (setq c | 831 | ;; It might be missing because it is a :class allocated slot. |
| 828 | (eieio--class-slot-name-index class slot)) | 832 | ;; Let's check that info out. |
| 829 | ;; Oset that slot. | 833 | (if (setq c |
| 830 | (progn | 834 | (eieio--class-slot-name-index class slot)) |
| 831 | (eieio--validate-class-slot-value class c value slot) | 835 | ;; Oset that slot. |
| 832 | (aset (eieio--class-class-allocation-values class) | 836 | (progn |
| 833 | c value)) | 837 | (eieio--validate-class-slot-value class c value slot) |
| 834 | ;; See oref for comment on `slot-missing' | 838 | (aset (eieio--class-class-allocation-values class) |
| 835 | (slot-missing obj slot 'oset value)) | 839 | c value)) |
| 836 | (eieio--validate-slot-value class c value slot) | 840 | ;; See oref for comment on `slot-missing' |
| 837 | (aset obj c value)))) | 841 | (slot-missing obj slot 'oset value)) |
| 842 | (eieio--validate-slot-value class c value slot) | ||
| 843 | (aset obj c value)))) | ||
| 844 | ((cl-typep obj 'oclosure) (oclosure--set-slot-value obj slot value)) | ||
| 845 | (t | ||
| 846 | (signal 'wrong-type-argument | ||
| 847 | (list '(or eieio-object cl-structure-object oclosure) obj))))) | ||
| 838 | 848 | ||
| 839 | (defun eieio-oset-default (class slot value) | 849 | (defun eieio-oset-default (class slot value) |
| 840 | "Do the work for the macro `oset-default'. | 850 | "Do the work for the macro `oset-default'. |
| @@ -844,7 +854,7 @@ Fills in the default value in CLASS' in SLOT with VALUE." | |||
| 844 | (ignore class value) | 854 | (ignore class value) |
| 845 | (pcase slot | 855 | (pcase slot |
| 846 | ((and (or `',name (and name (pred keywordp))) | 856 | ((and (or `',name (and name (pred keywordp))) |
| 847 | (guard (not (memq name eieio--known-slot-names)))) | 857 | (guard (not (eieio--known-slot-name-p name)))) |
| 848 | (macroexp-warn-and-return | 858 | (macroexp-warn-and-return |
| 849 | (format-message "Unknown slot `%S'" name) | 859 | (format-message "Unknown slot `%S'" name) |
| 850 | exp nil 'compile-only name)) | 860 | exp nil 'compile-only name)) |
| @@ -867,7 +877,7 @@ Fills in the default value in CLASS' in SLOT with VALUE." | |||
| 867 | (eieio--validate-class-slot-value class c value slot) | 877 | (eieio--validate-class-slot-value class c value slot) |
| 868 | (aset (eieio--class-class-allocation-values class) c | 878 | (aset (eieio--class-class-allocation-values class) c |
| 869 | value)) | 879 | value)) |
| 870 | (signal 'invalid-slot-name (list (eieio--class-name class) slot))) | 880 | (signal 'invalid-slot-name (list (cl--class-name class) slot))) |
| 871 | ;; `oset-default' on an instance-allocated slot is allowed by EIEIO but | 881 | ;; `oset-default' on an instance-allocated slot is allowed by EIEIO but |
| 872 | ;; not by CLOS and is mildly inconsistent with the :initform thingy, so | 882 | ;; not by CLOS and is mildly inconsistent with the :initform thingy, so |
| 873 | ;; it'd be nice to get rid of it. | 883 | ;; it'd be nice to get rid of it. |
diff --git a/lisp/emacs-lisp/oclosure.el b/lisp/emacs-lisp/oclosure.el index c37a5352a3a..3df64ad2806 100644 --- a/lisp/emacs-lisp/oclosure.el +++ b/lisp/emacs-lisp/oclosure.el | |||
| @@ -511,6 +511,26 @@ This has 2 uses: | |||
| 511 | "OClosure function to access a specific slot of an OClosure function." | 511 | "OClosure function to access a specific slot of an OClosure function." |
| 512 | index) | 512 | index) |
| 513 | 513 | ||
| 514 | (defun oclosure--slot-index (oclosure slotname) | ||
| 515 | (gethash slotname | ||
| 516 | (oclosure--class-index-table | ||
| 517 | (cl--find-class (oclosure-type oclosure))))) | ||
| 518 | |||
| 519 | (defun oclosure--slot-value (oclosure slotname) | ||
| 520 | (let ((class (cl--find-class (oclosure-type oclosure))) | ||
| 521 | (index (oclosure--slot-index oclosure slotname))) | ||
| 522 | (oclosure--get oclosure index | ||
| 523 | (oclosure--slot-mutable-p | ||
| 524 | (nth index (oclosure--class-slots class)))))) | ||
| 525 | |||
| 526 | (defun oclosure--set-slot-value (oclosure slotname value) | ||
| 527 | (let ((class (cl--find-class (oclosure-type oclosure))) | ||
| 528 | (index (oclosure--slot-index oclosure slotname))) | ||
| 529 | (unless (oclosure--slot-mutable-p | ||
| 530 | (nth index (oclosure--class-slots class))) | ||
| 531 | (signal 'setting-constant (list oclosure slotname))) | ||
| 532 | (oclosure--set value oclosure index))) | ||
| 533 | |||
| 514 | (defconst oclosure--mut-getter-prototype | 534 | (defconst oclosure--mut-getter-prototype |
| 515 | (oclosure-lambda (oclosure-accessor (type) (slot) (index)) (oclosure) | 535 | (oclosure-lambda (oclosure-accessor (type) (slot) (index)) (oclosure) |
| 516 | (oclosure--get oclosure index t))) | 536 | (oclosure--get oclosure index t))) |
diff --git a/test/lisp/emacs-lisp/oclosure-tests.el b/test/lisp/emacs-lisp/oclosure-tests.el index c72a9dbd7ad..d3e2b3870a6 100644 --- a/test/lisp/emacs-lisp/oclosure-tests.el +++ b/test/lisp/emacs-lisp/oclosure-tests.el | |||
| @@ -22,12 +22,13 @@ | |||
| 22 | (require 'ert) | 22 | (require 'ert) |
| 23 | (require 'oclosure) | 23 | (require 'oclosure) |
| 24 | (require 'cl-lib) | 24 | (require 'cl-lib) |
| 25 | (require 'eieio) | ||
| 25 | 26 | ||
| 26 | (oclosure-define (oclosure-test | 27 | (oclosure-define (oclosure-test |
| 27 | (:copier oclosure-test-copy) | 28 | (:copier oclosure-test-copy) |
| 28 | (:copier oclosure-test-copy1 (fst))) | 29 | (:copier oclosure-test-copy1 (fst))) |
| 29 | "Simple OClosure." | 30 | "Simple OClosure." |
| 30 | fst snd name) | 31 | fst snd (name :mutable t)) |
| 31 | 32 | ||
| 32 | (cl-defmethod oclosure-test-gen ((_x compiled-function)) "#<bytecode>") | 33 | (cl-defmethod oclosure-test-gen ((_x compiled-function)) "#<bytecode>") |
| 33 | 34 | ||
| @@ -123,4 +124,20 @@ | |||
| 123 | (should (equal (funcall f 5) 15)) | 124 | (should (equal (funcall f 5) 15)) |
| 124 | (should (equal (funcall f2 15) 68)))) | 125 | (should (equal (funcall f2 15) 68)))) |
| 125 | 126 | ||
| 127 | (ert-deftest oclosure-test-slot-value () | ||
| 128 | (require 'eieio) | ||
| 129 | (let ((ocl (oclosure-lambda | ||
| 130 | (oclosure-test (fst 'fst1) (snd 'snd1) (name 'name1)) | ||
| 131 | (x) | ||
| 132 | (list name fst snd x)))) | ||
| 133 | (should (equal 'fst1 (slot-value ocl 'fst))) | ||
| 134 | (should (equal 'snd1 (slot-value ocl 'snd))) | ||
| 135 | (should (equal 'name1 (slot-value ocl 'name))) | ||
| 136 | (setf (slot-value ocl 'name) 'new-name) | ||
| 137 | (should (equal 'new-name (slot-value ocl 'name))) | ||
| 138 | (should (equal '(new-name fst1 snd1 arg) (funcall ocl 'arg))) | ||
| 139 | (should-error (setf (slot-value ocl 'fst) 'new-fst) :type 'setting-constant) | ||
| 140 | (should (equal 'fst1 (slot-value ocl 'fst))) | ||
| 141 | )) | ||
| 142 | |||
| 126 | ;;; oclosure-tests.el ends here. | 143 | ;;; oclosure-tests.el ends here. |