diff options
| author | akater | 2021-07-12 14:15:54 +0000 |
|---|---|---|
| committer | Stefan Monnier | 2021-07-16 15:40:08 -0400 |
| commit | 109c27341e35fae778b95e0eb5d4d72927bf4ea8 (patch) | |
| tree | 02033cc7f712d8f10935aa108455d7b4b6188e77 | |
| parent | 24a8cc5e707affad345e085b6fe8c778559533f6 (diff) | |
| download | emacs-109c27341e35fae778b95e0eb5d4d72927bf4ea8.tar.gz emacs-109c27341e35fae778b95e0eb5d4d72927bf4ea8.zip | |
EIEIO: Prevent excessive evaluation of :initform
* lisp/emacs-lisp/eieio.el (initialize-instance):
Do not evaluate initform of a slot when initarg for the slot is provided,
according to the following secitons of CLHS:
- Object Creation and Initialization
- Initialization Arguments
- Defaulting of Initialization Arguments
- Rules for Initialization Arguments
* test/lisp/emacs-lisp/eieio-etests/eieio-tests.el:
Add corresponding tests
Fix a typo
| -rw-r--r-- | lisp/emacs-lisp/eieio.el | 35 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/eieio-tests/eieio-tests.el | 16 |
2 files changed, 36 insertions, 15 deletions
diff --git a/lisp/emacs-lisp/eieio.el b/lisp/emacs-lisp/eieio.el index 1c8c372aaef..b31ea42a99b 100644 --- a/lisp/emacs-lisp/eieio.el +++ b/lisp/emacs-lisp/eieio.el | |||
| @@ -53,6 +53,7 @@ | |||
| 53 | (message eieio-version)) | 53 | (message eieio-version)) |
| 54 | 54 | ||
| 55 | (require 'eieio-core) | 55 | (require 'eieio-core) |
| 56 | (eval-when-compile (require 'subr-x)) | ||
| 56 | 57 | ||
| 57 | 58 | ||
| 58 | ;;; Defining a new class | 59 | ;;; Defining a new class |
| @@ -740,31 +741,37 @@ Called from the constructor routine." | |||
| 740 | "Construct the new object THIS based on SLOTS.") | 741 | "Construct the new object THIS based on SLOTS.") |
| 741 | 742 | ||
| 742 | (cl-defmethod initialize-instance ((this eieio-default-superclass) | 743 | (cl-defmethod initialize-instance ((this eieio-default-superclass) |
| 743 | &optional slots) | 744 | &optional args) |
| 744 | "Construct the new object THIS based on SLOTS. | 745 | "Construct the new object THIS based on SLOTS. |
| 745 | SLOTS is a tagged list where odd numbered elements are tags, and | 746 | ARGS is a property list where odd numbered elements are tags, and |
| 746 | even numbered elements are the values to store in the tagged slot. | 747 | even numbered elements are the values to store in the tagged slot. |
| 747 | If you overload the `initialize-instance', there you will need to | 748 | If you overload the `initialize-instance', there you will need to |
| 748 | call `shared-initialize' yourself, or you can call `call-next-method' | 749 | call `shared-initialize' yourself, or you can call `call-next-method' |
| 749 | to have this constructor called automatically. If these steps are | 750 | to have this constructor called automatically. If these steps are |
| 750 | not taken, then new objects of your class will not have their values | 751 | not taken, then new objects of your class will not have their values |
| 751 | dynamically set from SLOTS." | 752 | dynamically set from ARGS." |
| 752 | ;; First, see if any of our defaults are `lambda', and | ||
| 753 | ;; re-evaluate them and apply the value to our slots. | ||
| 754 | (let* ((this-class (eieio--object-class this)) | 753 | (let* ((this-class (eieio--object-class this)) |
| 754 | (initargs args) | ||
| 755 | (slots (eieio--class-slots this-class))) | 755 | (slots (eieio--class-slots this-class))) |
| 756 | (dotimes (i (length slots)) | 756 | (dotimes (i (length slots)) |
| 757 | ;; For each slot, see if we need to evaluate it. | 757 | ;; For each slot, see if we need to evaluate its initform. |
| 758 | (let* ((slot (aref slots i)) | 758 | (let* ((slot (aref slots i)) |
| 759 | (slot-name (eieio-slot-descriptor-name slot)) | ||
| 759 | (initform (cl--slot-descriptor-initform slot))) | 760 | (initform (cl--slot-descriptor-initform slot))) |
| 760 | ;; Those slots whose initform is constant already have the right | 761 | (unless (or (when-let ((initarg |
| 761 | ;; value set in the default-object. | 762 | (car (rassq slot-name |
| 762 | (unless (macroexp-const-p initform) | 763 | (eieio--class-initarg-tuples |
| 763 | ;; FIXME: We should be able to just do (aset this (+ i <cst>) dflt)! | 764 | this-class))))) |
| 764 | (eieio-oset this (cl--slot-descriptor-name slot) | 765 | (plist-get initargs initarg)) |
| 765 | (eval initform t)))))) | 766 | ;; Those slots whose initform is constant already have |
| 766 | ;; Shared initialize will parse our slots for us. | 767 | ;; the right value set in the default-object. |
| 767 | (shared-initialize this slots)) | 768 | (macroexp-const-p initform)) |
| 769 | ;; FIXME: Use `aset' instead of `eieio-oset', relying on that | ||
| 770 | ;; vector returned by `eieio--class-slots' | ||
| 771 | ;; should be congruent with the object itself. | ||
| 772 | (eieio-oset this slot-name (eval initform t)))))) | ||
| 773 | ;; Shared initialize will parse our args for us. | ||
| 774 | (shared-initialize this args)) | ||
| 768 | 775 | ||
| 769 | (cl-defgeneric slot-missing (object slot-name _operation &optional _new-value) | 776 | (cl-defgeneric slot-missing (object slot-name _operation &optional _new-value) |
| 770 | "Method invoked when an attempt to access a slot in OBJECT fails. | 777 | "Method invoked when an attempt to access a slot in OBJECT fails. |
diff --git a/test/lisp/emacs-lisp/eieio-tests/eieio-tests.el b/test/lisp/emacs-lisp/eieio-tests/eieio-tests.el index 11ffc115f7e..3ec42343443 100644 --- a/test/lisp/emacs-lisp/eieio-tests/eieio-tests.el +++ b/test/lisp/emacs-lisp/eieio-tests/eieio-tests.el | |||
| @@ -574,7 +574,21 @@ METHOD is the method that was attempting to be called." | |||
| 574 | (setf (get-slot-3 eitest-t1) 'setf-emu) | 574 | (setf (get-slot-3 eitest-t1) 'setf-emu) |
| 575 | (should (eq (get-slot-3 eitest-t1) 'setf-emu)) | 575 | (should (eq (get-slot-3 eitest-t1) 'setf-emu)) |
| 576 | ;; Roll back | 576 | ;; Roll back |
| 577 | (setf (get-slot-3 eitest-t1) 'emu)) | 577 | (setf (get-slot-3 eitest-t1) 'emu) |
| 578 | (defvar eieio-tests-initform-was-evaluated) | ||
| 579 | (defclass eieio-tests-initform-not-evaluated-when-initarg-is-present () | ||
| 580 | ((slot-with-initarg-and-initform | ||
| 581 | :initarg :slot-with-initarg-and-initform | ||
| 582 | :initform (setf eieio-tests-initform-was-evaluated t)))) | ||
| 583 | (setq eieio-tests-initform-was-evaluated nil) | ||
| 584 | (make-instance | ||
| 585 | 'eieio-tests-initform-not-evaluated-when-initarg-is-present) | ||
| 586 | (should eieio-tests-initform-was-evaluated) | ||
| 587 | (setq eieio-tests-initform-was-evaluated nil) | ||
| 588 | (make-instance | ||
| 589 | 'eieio-tests-initform-not-evaluated-when-initarg-is-present | ||
| 590 | :slot-with-initarg-and-initform t) | ||
| 591 | (should-not eieio-tests-initform-was-evaluated)) | ||
| 578 | 592 | ||
| 579 | (defvar eitest-t2 nil) | 593 | (defvar eitest-t2 nil) |
| 580 | (ert-deftest eieio-test-26-default-inheritance () | 594 | (ert-deftest eieio-test-26-default-inheritance () |