aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2022-04-04 15:06:47 -0400
committerStefan Monnier2022-04-04 15:06:47 -0400
commit1f4f6b956bee611ffa406b3851e5264ee74e3bfb (patch)
tree4d24cdef860e41b611c400492ef4a85b9a1b156a
parent6c4a4cc94e9fea809b518da9fe9e581a6031a6df (diff)
downloademacs-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.el104
-rw-r--r--lisp/emacs-lisp/oclosure.el20
-rw-r--r--test/lisp/emacs-lisp/oclosure-tests.el19
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.
270SLOTS are the slots residing in that class definition, and OPTIONS 271SLOTS 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'.
819Fills in OBJ's SLOT with VALUE." 824Fills 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.