X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fpcl%2Fstd-class.lisp;h=975acc43bf1f12dd1be9cb5f869121e9e7aa13b6;hb=441dfe5655f1ec3ee96e7b17b7cb1c7a4a906117;hp=ab1406f027c53f7b703d72d81089b04a677b88e2;hpb=8fe977ca5d0d068f2641dd06d3743a4c218d5cc1;p=sbcl.git diff --git a/src/pcl/std-class.lisp b/src/pcl/std-class.lisp index ab1406f..975acc4 100644 --- a/src/pcl/std-class.lisp +++ b/src/pcl/std-class.lisp @@ -24,18 +24,20 @@ (in-package "SB-PCL") (defmethod slot-accessor-function ((slotd effective-slot-definition) type) - (ecase type - (reader (slot-definition-reader-function slotd)) - (writer (slot-definition-writer-function slotd)) - (boundp (slot-definition-boundp-function slotd)))) + (let ((info (slot-definition-info slotd))) + (ecase type + (reader (slot-info-reader info)) + (writer (slot-info-writer info)) + (boundp (slot-info-boundp info))))) (defmethod (setf slot-accessor-function) (function (slotd effective-slot-definition) type) - (ecase type - (reader (setf (slot-definition-reader-function slotd) function)) - (writer (setf (slot-definition-writer-function slotd) function)) - (boundp (setf (slot-definition-boundp-function slotd) function)))) + (let ((info (slot-definition-info slotd))) + (ecase type + (reader (setf (slot-info-reader info) function)) + (writer (setf (slot-info-writer info) function)) + (boundp (setf (slot-info-boundp info) function))))) (defconstant +slotd-reader-function-std-p+ 1) (defconstant +slotd-writer-function-std-p+ 2) @@ -69,8 +71,8 @@ (the fixnum (logand (the fixnum (lognot mask)) flags))))) value) -(defmethod initialize-internal-slot-functions ((slotd - effective-slot-definition)) +(defmethod initialize-internal-slot-functions + ((slotd effective-slot-definition)) (let* ((name (slot-value slotd 'name)) (class (slot-value slotd '%class))) (dolist (type '(reader writer boundp)) @@ -79,7 +81,34 @@ (writer '(setf slot-value-using-class)) (boundp 'slot-boundp-using-class))) (gf (gdefinition gf-name))) - (compute-slot-accessor-info slotd type gf))))) + ;; KLUDGE: this logic is cut'n'pasted from + ;; GET-ACCESSOR-METHOD-FUNCTION, which (for STD-CLASSes) is + ;; only called later, because it does things that can't be + ;; computed this early in class finalization; however, we need + ;; this bit as early as possible. -- CSR, 2009-11-05 + (setf (slot-accessor-std-p slotd type) + (let* ((std-method (standard-svuc-method type)) + (str-method (structure-svuc-method type)) + (types1 `((eql ,class) (class-eq ,class) (eql ,slotd))) + (types (if (eq type 'writer) `(t ,@types1) types1)) + (methods (compute-applicable-methods-using-types gf types))) + (null (cdr methods)))) + (setf (slot-accessor-function slotd type) + (lambda (&rest args) + (declare (dynamic-extent args)) + ;; FIXME: a tiny amount of wasted SLOT-ACCESSOR-STD-P + ;; work here (see KLUDGE comment above). + (let ((fun (compute-slot-accessor-info slotd type gf))) + (apply fun args)))))))) + +(defmethod finalize-internal-slot-functions ((slotd effective-slot-definition)) + (dolist (type '(reader writer boundp)) + (let* ((gf-name (ecase type + (reader 'slot-value-using-class) + (writer '(setf slot-value-using-class)) + (boundp 'slot-boundp-using-class))) + (gf (gdefinition gf-name))) + (compute-slot-accessor-info slotd type gf)))) ;;; CMUCL (Gerd PCL 2003-04-25) comment: ;;; @@ -98,12 +127,9 @@ (defmethod compute-slot-accessor-info ((slotd effective-slot-definition) type gf) (let* ((name (slot-value slotd 'name)) - (class (slot-value slotd '%class)) - (old-slotd (when (class-finalized-p class) - (find-slot-definition class name))) - (old-std-p (and old-slotd (slot-accessor-std-p old-slotd 'all)))) + (class (slot-value slotd '%class))) (multiple-value-bind (function std-p) - (if (eq *boot-state* 'complete) + (if (eq **boot-state** 'complete) (get-accessor-method-function gf type class slotd) (get-optimized-std-accessor-method-function class slotd type)) (setf (slot-accessor-std-p slotd type) std-p) @@ -176,18 +202,18 @@ ;;; This needs to be used recursively, in case a non-trivial user ;;; defined ADD/REMOVE-DIRECT-METHOD method ends up calling another ;;; function using the same lock. -(defvar *specializer-lock* (sb-thread::make-spinlock :name "Specializer lock")) +(defvar *specializer-lock* (sb-thread:make-mutex :name "Specializer lock")) (defmethod add-direct-method :around ((specializer specializer) method) ;; All the actions done under this lock are done in an order ;; that is safe to unwind at any point. - (sb-thread::with-recursive-spinlock (*specializer-lock*) + (sb-thread::with-recursive-system-lock (*specializer-lock*) (call-next-method))) (defmethod remove-direct-method :around ((specializer specializer) method) ;; All the actions done under this lock are done in an order ;; that is safe to unwind at any point. - (sb-thread::with-recursive-spinlock (*specializer-lock*) + (sb-thread::with-recursive-system-lock (*specializer-lock*) (call-next-method))) (defmethod add-direct-method ((specializer class) (method method)) @@ -218,7 +244,7 @@ ;; we behave as if we got just first or just after -- it's just ;; for update that we need to lock. (or (cdr cell) - (sb-thread::with-spinlock (*specializer-lock*) + (sb-thread:with-mutex (*specializer-lock*) (setf (cdr cell) (let (collect) (dolist (m (car cell)) @@ -277,7 +303,7 @@ (entry (gethash object (specializer-method-table specializer)))) (when entry (or (cdr entry) - (sb-thread::with-spinlock (*specializer-lock*) + (sb-thread:with-mutex (*specializer-lock*) (setf (cdr entry) (let (collect) (dolist (m (car entry)) @@ -336,33 +362,40 @@ (setf (gdefinition 'load-defclass) #'real-load-defclass) (defun ensure-class (name &rest args) - (apply #'ensure-class-using-class - (let ((class (find-class name nil))) - (when (and class (eq name (class-name class))) - ;; NAME is the proper name of CLASS, so redefine it - class)) - name - args)) + (with-world-lock () + (apply #'ensure-class-using-class + (let ((class (find-class name nil))) + (when (and class (eq name (class-name class))) + ;; NAME is the proper name of CLASS, so redefine it + class)) + name + args))) (defmethod ensure-class-using-class ((class null) name &rest args &key) - (multiple-value-bind (meta initargs) - (frob-ensure-class-args args) - (setf class (apply #'make-instance meta :name name initargs)) - (without-package-locks - (setf (find-class name) class)) - (set-class-type-translation class name) - class)) + (with-world-lock () + (multiple-value-bind (meta initargs) + (frob-ensure-class-args args) + (setf class (apply #'make-instance meta :name name initargs)) + (without-package-locks + (setf (find-class name) class)))) + ;; After boot (SETF FIND-CLASS) does this. + (unless (eq **boot-state** 'complete) + (%set-class-type-translation class name)) + class) (defmethod ensure-class-using-class ((class pcl-class) name &rest args &key) - (multiple-value-bind (meta initargs) - (frob-ensure-class-args args) - (unless (eq (class-of class) meta) - (apply #'change-class class meta initargs)) - (apply #'reinitialize-instance class initargs) - (without-package-locks - (setf (find-class name) class)) - (set-class-type-translation class name) - class)) + (with-world-lock () + (multiple-value-bind (meta initargs) + (frob-ensure-class-args args) + (unless (eq (class-of class) meta) + (apply #'change-class class meta initargs)) + (apply #'reinitialize-instance class initargs) + (without-package-locks + (setf (find-class name) class)))) + ;; After boot (SETF FIND-CLASS) does this. + (unless (eq **boot-state** 'complete) + (%set-class-type-translation class name)) + class) (defun frob-ensure-class-args (args) (let (metaclass metaclassp reversed-plist) @@ -387,6 +420,17 @@ (find-class metaclass))) (t *the-class-standard-class*)) (nreverse reversed-plist))))) + +;;; This is used to call initfunctions of :allocation :class slots. +(defun call-initfun (fun slotd safe) + (declare (function fun)) + (let ((value (funcall fun))) + (when safe + (let ((type (slot-definition-type slotd))) + (unless (or (eq t type) + (typep value type)) + (error 'type-error :expected-type type :datum value)))) + value)) (defmethod shared-initialize :after ((class std-class) slot-names &key @@ -406,8 +450,8 @@ super-class of the class ~S, ~ but the meta-classes ~S and ~S are incompatible. ~ Define a method for ~S to avoid this error.~@:>" - superclass class (class-of superclass) (class-of class) - 'validate-superclass))) + superclass class (class-of superclass) (class-of class) + 'validate-superclass))) (setf (slot-value class 'direct-superclasses) direct-superclasses)) (t (setq direct-superclasses (slot-value class 'direct-superclasses)))) @@ -424,6 +468,7 @@ (plist-value class 'direct-default-initargs))) (setf (plist-value class 'class-slot-cells) (let ((old-class-slot-cells (plist-value class 'class-slot-cells)) + (safe (safe-p class)) (collect '())) (dolist (dslotd direct-slots) (when (eq :class (slot-definition-allocation dslotd)) @@ -434,9 +479,10 @@ (eq t slot-names) (member name slot-names :test #'eq)) (let* ((initfunction (slot-definition-initfunction dslotd)) - (value (if initfunction - (funcall initfunction) - +slot-unbound+))) + (value + (if initfunction + (call-initfun initfunction dslotd safe) + +slot-unbound+))) (push (cons name value) collect)) (push old collect))))) (nreverse collect))) @@ -461,24 +507,25 @@ (flet ((compute-preliminary-cpl (root) (let ((*allow-forward-referenced-classes-in-cpl-p* t)) (compute-class-precedence-list root)))) - (without-package-locks - (unless (class-finalized-p class) - (let ((name (class-name class))) - ;; KLUDGE: This is fairly horrible. We need to make a - ;; full-fledged CLASSOID here, not just tell the compiler that - ;; some class is forthcoming, because there are legitimate - ;; questions one can ask of the type system, implemented in - ;; terms of CLASSOIDs, involving forward-referenced classes. So. - (let ((layout (make-wrapper 0 class))) - (setf (slot-value class 'wrapper) layout) - (let ((cpl (compute-preliminary-cpl class))) - (setf (layout-inherits layout) - (order-layout-inherits - (map 'simple-vector #'class-wrapper - (reverse (rest cpl)))))) - (register-layout layout :invalidate t) - (set-class-type-translation class (layout-classoid layout))))) - (mapc #'make-preliminary-layout (class-direct-subclasses class))))) + (with-world-lock () + (without-package-locks + (unless (class-finalized-p class) + (let ((name (class-name class))) + ;; KLUDGE: This is fairly horrible. We need to make a + ;; full-fledged CLASSOID here, not just tell the compiler that + ;; some class is forthcoming, because there are legitimate + ;; questions one can ask of the type system, implemented in + ;; terms of CLASSOIDs, involving forward-referenced classes. So. + (let ((layout (make-wrapper 0 class))) + (setf (slot-value class 'wrapper) layout) + (let ((cpl (compute-preliminary-cpl class))) + (setf (layout-inherits layout) + (order-layout-inherits + (map 'simple-vector #'class-wrapper + (reverse (rest cpl)))))) + (register-layout layout :invalidate t) + (%set-class-type-translation class (layout-classoid layout))))) + (mapc #'make-preliminary-layout (class-direct-subclasses class)))))) (defmethod shared-initialize :before ((class class) slot-names &key name) @@ -492,7 +539,7 @@ (defmethod reinitialize-instance :before ((class slot-class) &key direct-superclasses) (dolist (old-super (set-difference (class-direct-superclasses class) direct-superclasses)) (remove-direct-subclass old-super class)) - (remove-slot-accessors class (class-direct-slots class))) + (remove-slot-accessors class (class-direct-slots class))) (defmethod reinitialize-instance :after ((class slot-class) &rest initargs @@ -520,18 +567,22 @@ &key direct-slots direct-superclasses) (declare (ignore slot-names)) (let ((classoid (find-classoid (slot-value class 'name)))) - (with-slots (wrapper %class-precedence-list cpl-available-p - prototype (direct-supers direct-superclasses)) + (with-slots (wrapper + %class-precedence-list cpl-available-p finalized-p + prototype (direct-supers direct-superclasses) + plist) class (setf (slot-value class 'direct-slots) (mapcar (lambda (pl) (make-direct-slotd class pl)) - direct-slots)) - (setf (slot-value class 'finalized-p) t) - (setf (classoid-pcl-class classoid) class) - (setq direct-supers direct-superclasses) - (setq wrapper (classoid-layout classoid)) - (setq %class-precedence-list (compute-class-precedence-list class)) - (setq cpl-available-p t) + direct-slots) + finalized-p t + (classoid-pcl-class classoid) class + direct-supers direct-superclasses + wrapper (classoid-layout classoid) + %class-precedence-list (compute-class-precedence-list class) + cpl-available-p t + (getf plist 'direct-default-initargs) + (sb-kernel::condition-classoid-direct-default-initargs classoid)) (add-direct-subclasses class direct-superclasses) (let ((slots (compute-slots class))) (setf (slot-value class 'slots) slots) @@ -567,18 +618,19 @@ (defmethod compute-effective-slot-definition ((class condition-class) slot-name dslotds) - (let ((slotd (call-next-method))) - (setf (slot-definition-reader-function slotd) + (let* ((slotd (call-next-method)) + (info (slot-definition-info slotd))) + (setf (slot-info-reader info) (lambda (x) (handler-case (condition-reader-function x slot-name) ;; FIXME: FIND-SLOT-DEFAULT throws an error if the slot ;; is unbound; maybe it should be a CELL-ERROR of some ;; sort? (error () (values (slot-unbound class x slot-name)))))) - (setf (slot-definition-writer-function slotd) + (setf (slot-info-writer info) (lambda (v x) (condition-writer-function x v slot-name))) - (setf (slot-definition-boundp-function slotd) + (setf (slot-info-boundp info) (lambda (x) (multiple-value-bind (v c) (ignore-errors (condition-reader-function x slot-name)) @@ -596,7 +648,7 @@ (defmethod compute-slots :around ((class condition-class)) (let ((eslotds (call-next-method))) - (mapc #'initialize-internal-slot-functions eslotds) + (mapc #'finalize-internal-slot-functions eslotds) eslotds)) (defmethod shared-initialize :after @@ -609,51 +661,59 @@ (defun make-structure-class-defstruct-form (name direct-slots include) (let* ((conc-name (format-symbol *package* "~S structure class " name)) (constructor (format-symbol *package* "~Aconstructor" conc-name)) - (defstruct `(defstruct (,name - ,@(when include - `((:include ,(class-name include)))) - (:predicate nil) - (:conc-name ,conc-name) - (:constructor ,constructor ()) - (:copier nil)) - ,@(mapcar (lambda (slot) - `(,(slot-definition-name slot) - +slot-unbound+)) - direct-slots))) - (reader-names (mapcar (lambda (slotd) - (list 'slot-accessor name - (slot-definition-name slotd) - 'reader)) - direct-slots)) - (writer-names (mapcar (lambda (slotd) - (list 'slot-accessor name - (slot-definition-name slotd) - 'writer)) - direct-slots)) - (readers-init - (mapcar (lambda (slotd reader-name) - (let ((accessor + (included-name (class-name include)) + (included-slots + (when include + (mapcar #'dsd-name (dd-slots (find-defstruct-description included-name))))) + (old-slots nil) + (new-slots nil) + (reader-names nil) + (writer-names nil)) + (dolist (slotd (reverse direct-slots)) + (let* ((slot-name (slot-definition-name slotd)) + (initform (slot-definition-initform slotd)) + (type (slot-definition-type slotd)) + (desc `(,slot-name ,initform :type ,type))) + (push `(slot-accessor ,name ,slot-name reader) + reader-names) + (push `(slot-accessor ,name ,slot-name writer) + writer-names) + (if (member slot-name included-slots :test #'eq) + (push desc old-slots) + (push desc new-slots)))) + (let* ((defstruct `(defstruct (,name + ,@(when include + `((:include ,included-name + ,@old-slots))) + (:constructor ,constructor ()) + (:predicate nil) + (:conc-name ,conc-name) + (:copier nil)) + ,@new-slots)) + (readers-init + (mapcar (lambda (slotd reader-name) + (let ((accessor (slot-definition-defstruct-accessor-symbol slotd))) - `(defun ,reader-name (obj) - (declare (type ,name obj)) - (,accessor obj)))) - direct-slots reader-names)) - (writers-init - (mapcar (lambda (slotd writer-name) - (let ((accessor + `(defun ,reader-name (obj) + (declare (type ,name obj)) + (,accessor obj)))) + direct-slots reader-names)) + (writers-init + (mapcar (lambda (slotd writer-name) + (let ((accessor (slot-definition-defstruct-accessor-symbol slotd))) - `(defun ,writer-name (nv obj) - (declare (type ,name obj)) - (setf (,accessor obj) nv)))) - direct-slots writer-names)) - (defstruct-form - `(progn + `(defun ,writer-name (nv obj) + (declare (type ,name obj)) + (setf (,accessor obj) nv)))) + direct-slots writer-names)) + (defstruct-form + `(progn ,defstruct ,@readers-init ,@writers-init (cons nil nil)))) - (values defstruct-form constructor reader-names writer-names))) + (values defstruct-form constructor reader-names writer-names)))) (defun make-defstruct-allocation-function (name) ;; FIXME: Why don't we go class->layout->info == dd @@ -789,7 +849,8 @@ (defun class-has-a-forward-referenced-superclass-p (class) - (or (forward-referenced-class-p class) + (or (when (forward-referenced-class-p class) + class) (some #'class-has-a-forward-referenced-superclass-p (class-direct-superclasses class)))) @@ -797,16 +858,17 @@ ;;; or reinitialized. The class may or may not be finalized. (defun update-class (class finalizep) (without-package-locks - (when (or finalizep (class-finalized-p class)) - (update-cpl class (compute-class-precedence-list class)) - ;; This invocation of UPDATE-SLOTS, in practice, finalizes the - ;; class. - (update-slots class (compute-slots class)) - (update-gfs-of-class class) - (update-initargs class (compute-default-initargs class)) - (update-ctors 'finalize-inheritance :class class)) - (dolist (sub (class-direct-subclasses class)) - (update-class sub nil)))) + (with-world-lock () + (when (or finalizep (class-finalized-p class)) + (%update-cpl class (compute-class-precedence-list class)) + ;; This invocation of UPDATE-SLOTS, in practice, finalizes the + ;; class. + (%update-slots class (compute-slots class)) + (update-gfs-of-class class) + (setf (plist-value class 'default-initargs) (compute-default-initargs class)) + (update-ctors 'finalize-inheritance :class class)) + (dolist (sub (class-direct-subclasses class)) + (update-class sub nil))))) (define-condition cpl-protocol-violation (reference-condition error) ((class :initarg :class :reader cpl-protocol-violation-class) @@ -824,7 +886,20 @@ (find-class 'function) (cpl-protocol-violation-cpl c))))) -(defun update-cpl (class cpl) +(defun class-has-a-cpl-protocol-violation-p (class) + (labels ((find-in-superclasses (class classes) + (cond + ((null classes) nil) + ((eql class (car classes)) t) + (t (find-in-superclasses class (append (class-direct-superclasses (car classes)) (cdr classes))))))) + (let ((metaclass (class-of class))) + (cond + ((eql metaclass *the-class-standard-class*) + (find-in-superclasses (find-class 'function) (list class))) + ((eql metaclass *the-class-funcallable-standard-class*) + (not (find-in-superclasses (find-class 'function) (list class)))))))) + +(defun %update-cpl (class cpl) (when (eq (class-of class) *the-class-standard-class*) (when (find (find-class 'function) cpl) (error 'cpl-protocol-violation :class class :cpl cpl))) @@ -838,11 +913,11 @@ :key #'slot-definition-allocation) (return nil)))) ;; comment from the old CMU CL sources: - ;; Need to have the cpl setup before update-lisp-class-layout + ;; Need to have the cpl setup before %update-lisp-class-layout ;; is called on CMU CL. (setf (slot-value class '%class-precedence-list) cpl) (setf (slot-value class 'cpl-available-p) t) - (force-cache-flushes class)) + (%force-cache-flushes class)) (progn (setf (slot-value class '%class-precedence-list) cpl) (setf (slot-value class 'cpl-available-p) t))) @@ -858,82 +933,111 @@ (defun class-can-precede-p (class1 class2) (member class2 (class-can-precede-list class1) :test #'eq)) -(defun update-slots (class eslotds) - (let ((instance-slots ()) - (class-slots ())) - (dolist (eslotd eslotds) - (let ((alloc (slot-definition-allocation eslotd))) - (case alloc - (:instance (push eslotd instance-slots)) - (:class (push eslotd class-slots))))) - - ;; If there is a change in the shape of the instances then the - ;; old class is now obsolete. - (let* ((nlayout (mapcar #'slot-definition-name - (sort instance-slots #'< - :key #'slot-definition-location))) - (nslots (length nlayout)) - (nwrapper-class-slots (compute-class-slots class-slots)) - (owrapper (when (class-finalized-p class) - (class-wrapper class))) - (olayout (when owrapper - (wrapper-instance-slots-layout owrapper))) - (owrapper-class-slots (and owrapper (wrapper-class-slots owrapper))) +;;; This is called from %UPDATE-SLOTS to check if slot layouts are compatible. +;;; +;;; In addition to slot locations (implicit in the ordering of the slots), we +;;; must check classes: SLOT-INFO structures from old slotds may have been +;;; cached in permutation vectors, but new slotds have had new ones allocated +;;; to them. This is non-problematic for standard slotds, because we know the +;;; structure is compatible, but if a slot definition class changes, this can +;;; change the way SLOT-VALUE-USING-CLASS should dispatch. +;;; +;;; Also, if the slot has a non-standard allocation, we need to check that it +;;; doesn't change. +(defun slot-layouts-compatible-p + (oslotds new-instance-slotds new-class-slotds new-custom-slotds) + (multiple-value-bind (old-instance-slotds old-class-slotds old-custom-slotds) + (classify-slotds oslotds) + (and + ;; Instance slots: name, type, and class. + (dolist (o old-instance-slotds (not new-instance-slotds)) + (let ((n (pop new-instance-slotds))) + (unless (and n + (eq (slot-definition-name o) (slot-definition-name n)) + (eq (slot-definition-type o) (slot-definition-type n)) + (eq (class-of o) (class-of n))) + (return nil)))) + ;; Class slots: name and class. (FIXME: class slots not typechecked?) + (dolist (o old-class-slotds (not new-class-slotds)) + (let ((n (pop new-class-slotds))) + (unless (and n + (eq (slot-definition-name o) (slot-definition-name n)) + (eq (class-of n) (class-of o))) + (return nil)))) + ;; Custom slots: check name, type, allocation, and class. (FIXME: should we just punt?) + (dolist (o old-custom-slotds (not new-custom-slotds)) + (let ((n (pop new-custom-slotds))) + (unless (and n + (eq (slot-definition-name o) (slot-definition-name n)) + (eq (slot-definition-type o) (slot-definition-type n)) + (eq (slot-definition-allocation o) (slot-definition-allocation n)) + (eq (class-of o) (class-of n))) + (return nil))))))) + +(defun style-warn-about-duplicate-slots (class) + (do* ((slots (slot-value class 'slots) (cdr slots)) + (dupes nil)) + ((null slots) + (when dupes + (style-warn + "~@~@:>" + class dupes))) + (let* ((slot-name (slot-definition-name (car slots))) + (oslots (and (not (eq (symbol-package slot-name) + *pcl-package*)) + (remove-if + (lambda (slot-name-2) + (or (eq (symbol-package slot-name-2) + *pcl-package*) + (string/= slot-name slot-name-2))) + (cdr slots) + :key #'slot-definition-name)))) + (when oslots + (pushnew (cons slot-name + (mapcar #'slot-definition-name oslots)) + dupes + :test #'string= :key #'car))))) + +(defun %update-slots (class eslotds) + (multiple-value-bind (instance-slots class-slots custom-slots) + (classify-slotds eslotds) + (let* ((nslots (length instance-slots)) + (owrapper (when (class-finalized-p class) (class-wrapper class))) (nwrapper - (cond ((null owrapper) - (make-wrapper nslots class)) - ((and (equal nlayout olayout) - (not - (loop for o in owrapper-class-slots - for n in nwrapper-class-slots - do (unless (eq (car o) (car n)) (return t))))) - owrapper) - (t - ;; This will initialize the new wrapper to have the - ;; same state as the old wrapper. We will then have - ;; to change that. This may seem like wasted work - ;; (and it is), but the spec requires that we call - ;; MAKE-INSTANCES-OBSOLETE. - (make-instances-obsolete class) - (class-wrapper class))))) - - (update-lisp-class-layout class nwrapper) + (cond ((null owrapper) + (make-wrapper nslots class)) + ((slot-layouts-compatible-p (wrapper-slots owrapper) + instance-slots class-slots custom-slots) + owrapper) + (t + ;; This will initialize the new wrapper to have the + ;; same state as the old wrapper. We will then have + ;; to change that. This may seem like wasted work + ;; (and it is), but the spec requires that we call + ;; MAKE-INSTANCES-OBSOLETE. + (make-instances-obsolete class) + (class-wrapper class))))) + (%update-lisp-class-layout class nwrapper) (setf (slot-value class 'slots) eslotds + (wrapper-slots nwrapper) eslotds (wrapper-slot-table nwrapper) (make-slot-table class eslotds) - (wrapper-instance-slots-layout nwrapper) nlayout - (wrapper-class-slots nwrapper) nwrapper-class-slots (wrapper-length nwrapper) nslots (slot-value class 'wrapper) nwrapper) - (do* ((slots (slot-value class 'slots) (cdr slots)) - (dupes nil)) - ((null slots) - (when dupes - (style-warn - "~@~@:>" - class dupes))) - (let* ((slot (car slots)) - (oslots (remove (slot-definition-name slot) (cdr slots) - :test #'string/= - :key #'slot-definition-name))) - (when oslots - (pushnew (cons (slot-definition-name slot) - (mapcar #'slot-definition-name oslots)) - dupes - :test #'string= :key #'car)))) + (style-warn-about-duplicate-slots class) (setf (slot-value class 'finalized-p) t) (unless (eq owrapper nwrapper) - (maybe-update-standard-class-locations class))))) + (maybe-update-standard-slot-locations class))))) -(defun compute-class-slots (eslotds) - (let (collect) - (dolist (eslotd eslotds (nreverse collect)) - (let ((cell (assoc (slot-definition-name eslotd) - (class-slot-cells - (slot-definition-allocation-class eslotd))))) - (aver cell) - (push cell collect))))) +(defun update-gf-dfun (class gf) + (let ((*new-class* class) + (arg-info (gf-arg-info gf))) + (cond + ((special-case-for-compute-discriminating-function-p gf)) + ((gf-precompute-dfun-and-emf-p arg-info) + (multiple-value-bind (dfun cache info) (make-final-dfun-internal gf) + (update-dfun gf dfun cache info)))))) (defun update-gfs-of-class (class) (when (and (class-finalized-p class) @@ -951,9 +1055,6 @@ (declare (ignore ignore)) (update-gf-dfun class gf)) gf-table))))) - -(defun update-initargs (class inits) - (setf (plist-value class 'default-initargs) inits)) (defmethod compute-default-initargs ((class slot-class)) (let ((initargs (loop for c in (class-precedence-list class) @@ -1005,7 +1106,8 @@ (std-compute-slots class)) (defun std-compute-slots-around (class eslotds) - (let ((location -1)) + (let ((location -1) + (safe (safe-p class))) (dolist (eslotd eslotds eslotds) (setf (slot-definition-location eslotd) (case (slot-definition-allocation eslotd) @@ -1029,10 +1131,12 @@ c)))) (aver (consp cell)) (if (eq +slot-unbound+ (cdr cell)) - ;; We may have inherited an initfunction + ;; We may have inherited an initfunction FIXME: Is this + ;; really right? Is the initialization in + ;; SHARED-INITIALIZE (STD-CLASS) not enough? (let ((initfun (slot-definition-initfunction eslotd))) (if initfun - (rplacd cell (funcall initfun)) + (rplacd cell (call-initfun initfun eslotd safe)) cell)) cell))))) (unless (slot-definition-class eslotd) @@ -1058,14 +1162,14 @@ (defmethod compute-slots :around ((class structure-class)) (let ((eslotds (call-next-method))) - (mapc #'initialize-internal-slot-functions eslotds) + (mapc #'finalize-internal-slot-functions eslotds) eslotds)) (defmethod compute-effective-slot-definition ((class slot-class) name dslotds) - (declare (ignore name)) (let* ((initargs (compute-effective-slot-definition-initargs class dslotds)) - (class (apply #'effective-slot-definition-class class initargs))) - (apply #'make-instance class initargs))) + (class (apply #'effective-slot-definition-class class initargs)) + (slotd (apply #'make-instance class initargs))) + slotd)) (defmethod effective-slot-definition-class ((class std-class) &rest initargs) (declare (ignore initargs)) @@ -1084,7 +1188,6 @@ (allocation nil) (allocation-class nil) (type t) - (type-check-function nil) (documentation nil) (documentationp nil) (namep nil) @@ -1097,28 +1200,19 @@ (setq name (slot-definition-name slotd) namep t)) (unless initp - (when (slot-definition-initfunction slotd) + (awhen (slot-definition-initfunction slotd) (setq initform (slot-definition-initform slotd) - initfunction (slot-definition-initfunction slotd) + initfunction it initp t))) (unless documentationp - (when (%slot-definition-documentation slotd) - (setq documentation (%slot-definition-documentation slotd) + (awhen (%slot-definition-documentation slotd) + (setq documentation it documentationp t))) (unless allocp (setq allocation (slot-definition-allocation slotd) allocation-class (slot-definition-class slotd) allocp t)) (setq initargs (append (slot-definition-initargs slotd) initargs)) - (let ((fun (slot-definition-type-check-function slotd))) - (when fun - (setf type-check-function - (if type-check-function - (let ((old-function type-check-function)) - (lambda (value) - (funcall old-function value) - (funcall fun value))) - fun)))) (let ((slotd-type (slot-definition-type slotd))) (setq type (cond ((eq type t) slotd-type) @@ -1135,15 +1229,14 @@ :allocation allocation :allocation-class allocation-class :type type - 'type-check-function type-check-function :class class :documentation documentation))) (defmethod compute-effective-slot-definition-initargs :around ((class structure-class) direct-slotds) - (let ((slotd (car direct-slotds))) - (list* :defstruct-accessor-symbol - (slot-definition-defstruct-accessor-symbol slotd) + (let* ((slotd (car direct-slotds)) + (accessor (slot-definition-defstruct-accessor-symbol slotd))) + (list* :defstruct-accessor-symbol accessor :internal-reader-function (slot-definition-internal-reader-function slotd) :internal-writer-function @@ -1265,7 +1358,7 @@ ;;; :UNINITIALIZED))) ;;; ;;; Thanks to Gerd Moellmann for the explanation. -- CSR, 2002-10-29 -(defun force-cache-flushes (class) +(defun %force-cache-flushes (class) (let* ((owrapper (class-wrapper class))) ;; We only need to do something if the wrapper is still valid. If ;; the wrapper isn't valid, state will be FLUSH or OBSOLETE, and @@ -1280,49 +1373,40 @@ (eq (layout-invalid owrapper) t)) (let ((nwrapper (make-wrapper (layout-length owrapper) class))) - (setf (wrapper-instance-slots-layout nwrapper) - (wrapper-instance-slots-layout owrapper)) - (setf (wrapper-class-slots nwrapper) - (wrapper-class-slots owrapper)) + (setf (wrapper-slots nwrapper) + (wrapper-slots owrapper)) (setf (wrapper-slot-table nwrapper) (wrapper-slot-table owrapper)) - (with-pcl-lock - (update-lisp-class-layout class nwrapper) - (setf (slot-value class 'wrapper) nwrapper) - ;; Use :OBSOLETE instead of :FLUSH if any superclass has - ;; been obsoleted. - (if (find-if (lambda (x) - (and (consp x) (eq :obsolete (car x)))) - (layout-inherits owrapper) - :key #'layout-invalid) - (invalidate-wrapper owrapper :obsolete nwrapper) - (invalidate-wrapper owrapper :flush nwrapper))))))) - -(defun flush-cache-trap (owrapper nwrapper instance) - (declare (ignore owrapper)) - (set-wrapper instance nwrapper)) + (%update-lisp-class-layout class nwrapper) + (setf (slot-value class 'wrapper) nwrapper) + ;; Use :OBSOLETE instead of :FLUSH if any superclass has + ;; been obsoleted. + (if (find-if (lambda (x) + (and (consp x) (eq :obsolete (car x)))) + (layout-inherits owrapper) + :key #'layout-invalid) + (%invalidate-wrapper owrapper :obsolete nwrapper) + (%invalidate-wrapper owrapper :flush nwrapper)))))) ;;; MAKE-INSTANCES-OBSOLETE can be called by user code. It will cause ;;; the next access to the instance (as defined in 88-002R) to trap ;;; through the UPDATE-INSTANCE-FOR-REDEFINED-CLASS mechanism. (defmethod make-instances-obsolete ((class std-class)) - (let* ((owrapper (class-wrapper class)) - (nwrapper (make-wrapper (layout-length owrapper) - class))) - (unless (class-finalized-p class) - (if (class-has-a-forward-referenced-superclass-p class) - (return-from make-instances-obsolete class) - (update-cpl class (compute-class-precedence-list class)))) - (setf (wrapper-instance-slots-layout nwrapper) - (wrapper-instance-slots-layout owrapper)) - (setf (wrapper-class-slots nwrapper) - (wrapper-class-slots owrapper)) - (setf (wrapper-slot-table nwrapper) - (wrapper-slot-table owrapper)) - (with-pcl-lock - (update-lisp-class-layout class nwrapper) + (with-world-lock () + (let* ((owrapper (class-wrapper class)) + (nwrapper (make-wrapper (layout-length owrapper) + class))) + (unless (class-finalized-p class) + (if (class-has-a-forward-referenced-superclass-p class) + (return-from make-instances-obsolete class) + (%update-cpl class (compute-class-precedence-list class)))) + (setf (wrapper-slots nwrapper) + (wrapper-slots owrapper)) + (setf (wrapper-slot-table nwrapper) + (wrapper-slot-table owrapper)) + (%update-lisp-class-layout class nwrapper) (setf (slot-value class 'wrapper) nwrapper) - (invalidate-wrapper owrapper :obsolete nwrapper) + (%invalidate-wrapper owrapper :obsolete nwrapper) class))) (defmethod make-instances-obsolete ((class symbol)) @@ -1370,60 +1454,96 @@ "~@" (type-of (obsolete-structure-datum condition)))))) -(defun obsolete-instance-trap (owrapper nwrapper instance) +(defun %obsolete-instance-trap (owrapper nwrapper instance) (if (not (layout-for-std-class-p owrapper)) (if *in-obsolete-instance-trap* *the-wrapper-of-structure-object* - (let ((*in-obsolete-instance-trap* t)) - (error 'obsolete-structure :datum instance))) + (let ((*in-obsolete-instance-trap* t)) + (error 'obsolete-structure :datum instance))) (let* ((class (wrapper-class* nwrapper)) (copy (allocate-instance class)) ;??? allocate-instance ??? - (olayout (wrapper-instance-slots-layout owrapper)) - (nlayout (wrapper-instance-slots-layout nwrapper)) (oslots (get-slots instance)) (nslots (get-slots copy)) - (oclass-slots (wrapper-class-slots owrapper)) (added ()) (discarded ()) - (plist ())) + (plist ()) + (safe (safe-p class))) - ;; local --> local transfer value + ;; local --> local transfer value, check type ;; local --> shared discard value, discard slot ;; local --> -- discard slot - ;; shared --> local transfer value + ;; local --> custom XXX + + ;; shared --> local transfer value, check type ;; shared --> shared -- (cf SHARED-INITIALIZE :AFTER STD-CLASS) ;; shared --> -- discard value + ;; shared --> custom XXX + ;; -- --> local add slot ;; -- --> shared -- - - ;; Go through all the old local slots. - (let ((opos 0)) - (dolist (name olayout) - (let ((npos (posq name nlayout))) - (if npos - (setf (clos-slots-ref nslots npos) - (clos-slots-ref oslots opos)) - (progn - (push name discarded) - (unless (eq (clos-slots-ref oslots opos) +slot-unbound+) - (setf (getf plist name) (clos-slots-ref oslots opos)))))) - (incf opos))) - - ;; Go through all the old shared slots. - (dolist (oclass-slot-and-val oclass-slots) - (let ((name (car oclass-slot-and-val)) - (val (cdr oclass-slot-and-val))) - (let ((npos (posq name nlayout))) - (when npos - (setf (clos-slots-ref nslots npos) val))))) - - ;; Go through all the new local slots to compute the added slots. - (dolist (nlocal nlayout) - (unless (or (memq nlocal olayout) - (assq nlocal oclass-slots)) - (push nlocal added))) - - (swap-wrappers-and-slots instance copy) + ;; -- --> custom XXX + + (multiple-value-bind (new-instance-slots new-class-slots new-custom-slots) + (classify-slotds (wrapper-slots nwrapper)) + (declare (ignore new-class-slots)) + (multiple-value-bind (old-instance-slots old-class-slots old-custom-slots) + (classify-slotds (wrapper-slots owrapper)) + + (let ((layout (mapcar (lambda (slotd) + ;; Get the names only once. + (cons (slot-definition-name slotd) slotd)) + new-instance-slots))) + + (flet ((set-value (value cell) + (let ((name (car cell)) + (slotd (cdr cell))) + (when (and safe (neq value +slot-unbound+)) + (let ((type (slot-definition-type slotd))) + (assert + (typep value type) (value) + "~@" + name class value type))) + (setf (clos-slots-ref nslots (slot-definition-location slotd)) value + ;; Prune from the list now that it's been dealt with. + layout (remove cell layout))))) + + ;; Go through all the old local slots. + (dolist (old old-instance-slots) + (let* ((name (slot-definition-name old)) + (value (clos-slots-ref oslots (slot-definition-location old)))) + (unless (eq value +slot-unbound+) + (let ((new (assq name layout))) + (cond (new + (set-value value new)) + (t + (push name discarded) + (setf (getf plist name) value))))))) + + ;; Go through all the old shared slots. + (dolist (old old-class-slots) + (let* ((cell (slot-definition-location old)) + (name (car cell)) + (new (assq name layout))) + (when new + (set-value (cdr cell) new)))) + + ;; Go through all custom slots to find added ones. CLHS + ;; doesn't specify what to do about them, and neither does + ;; AMOP. We do want them to get initialized, though, so we + ;; list them in ADDED for the benefit of SHARED-INITIALIZE. + (dolist (new new-custom-slots) + (let* ((name (slot-definition-name new)) + (old (find name old-custom-slots :key #'slot-definition-name))) + (unless old + (push name added)))) + + ;; Go through all the remaining new local slots to compute the added slots. + (dolist (cell layout) + (push (car cell) added)))))) + + (%swap-wrappers-and-slots instance copy) (update-instance-for-redefined-class instance added @@ -1431,94 +1551,111 @@ plist) nwrapper))) -(defun change-class-internal (instance new-class initargs) +(defun %change-class (instance new-class initargs) (let* ((old-class (class-of instance)) (copy (allocate-instance new-class)) (new-wrapper (get-wrapper copy)) (old-wrapper (class-wrapper old-class)) - (old-layout (wrapper-instance-slots-layout old-wrapper)) - (new-layout (wrapper-instance-slots-layout new-wrapper)) (old-slots (get-slots instance)) (new-slots (get-slots copy)) - (old-class-slots (wrapper-class-slots old-wrapper))) - - ;; "The values of local slots specified by both the class CTO and - ;; CFROM are retained. If such a local slot was unbound, it - ;; remains unbound." - (let ((new-position 0)) - (dolist (new-slot new-layout) - (let ((old-position (posq new-slot old-layout))) - (when old-position - (setf (clos-slots-ref new-slots new-position) - (clos-slots-ref old-slots old-position)))) - (incf new-position))) - - ;; "The values of slots specified as shared in the class CFROM and - ;; as local in the class CTO are retained." - (dolist (slot-and-val old-class-slots) - (let ((position (posq (car slot-and-val) new-layout))) - (when position - (setf (clos-slots-ref new-slots position) (cdr slot-and-val))))) + (safe (safe-p new-class))) + (multiple-value-bind (new-instance-slots new-class-slots) + (classify-slotds (wrapper-slots new-wrapper)) + (multiple-value-bind (old-instance-slots old-class-slots) + (classify-slotds (wrapper-slots old-wrapper)) + + (flet ((set-value (value slotd) + (when safe + (assert (typep value (slot-definition-type slotd)) (value) + "~@" + (slot-definition-name slotd) old-class value + (slot-definition-type slotd) new-class)) + (setf (clos-slots-ref new-slots (slot-definition-location slotd)) value))) + + ;; "The values of local slots specified by both the class CTO and + ;; CFROM are retained. If such a local slot was unbound, it + ;; remains unbound." + (dolist (new new-instance-slots) + (let* ((name (slot-definition-name new)) + (old (find name old-instance-slots :key #'slot-definition-name))) + (when old + (set-value (clos-slots-ref old-slots (slot-definition-location old)) + new)))) + + ;; "The values of slots specified as shared in the class CFROM and + ;; as local in the class CTO are retained." + (dolist (old old-class-slots) + (let* ((slot-and-val (slot-definition-location old)) + (new (find (car slot-and-val) new-instance-slots + :key #'slot-definition-name))) + (when new + (set-value (cdr slot-and-val) new))))))) ;; Make the copy point to the old instance's storage, and make the ;; old instance point to the new storage. - (swap-wrappers-and-slots instance copy) + (%swap-wrappers-and-slots instance copy) (apply #'update-instance-for-different-class copy instance initargs) + instance)) (defmethod change-class ((instance standard-object) (new-class standard-class) &rest initargs) - (unless (class-finalized-p new-class) - (finalize-inheritance new-class)) - (let ((cpl (class-precedence-list new-class))) - (dolist (class cpl) - (macrolet - ((frob (class-name) - `(when (eq class (find-class ',class-name)) - (error 'metaobject-initialization-violation - :format-control "~@" - :format-arguments (list 'change-class ',class-name) - :references (list '(:amop :initialization ,class-name)))))) - (frob class) - (frob generic-function) - (frob method) - (frob slot-definition)))) - (change-class-internal instance new-class initargs)) + (with-world-lock () + (unless (class-finalized-p new-class) + (finalize-inheritance new-class)) + (let ((cpl (class-precedence-list new-class))) + (dolist (class cpl) + (macrolet + ((frob (class-name) + `(when (eq class (find-class ',class-name)) + (error 'metaobject-initialization-violation + :format-control "~@" + :format-arguments (list 'change-class ',class-name) + :references (list '(:amop :initialization ,class-name)))))) + (frob class) + (frob generic-function) + (frob method) + (frob slot-definition)))) + (%change-class instance new-class initargs))) (defmethod change-class ((instance forward-referenced-class) (new-class standard-class) &rest initargs) - (let ((cpl (class-precedence-list new-class))) - (dolist (class cpl - (error 'metaobject-initialization-violation - :format-control - "~@" - :format-arguments - (list 'change-class 'forward-referenced-class 'class) - :references - (list '(:amop :generic-function ensure-class-using-class) - '(:amop :initialization class)))) - (when (eq class (find-class 'class)) - (return nil)))) - (change-class-internal instance new-class initargs)) + (with-world-lock () + (let ((cpl (class-precedence-list new-class))) + (dolist (class cpl + (error 'metaobject-initialization-violation + :format-control + "~@" + :format-arguments + (list 'change-class 'forward-referenced-class 'class) + :references + (list '(:amop :generic-function ensure-class-using-class) + '(:amop :initialization class)))) + (when (eq class (find-class 'class)) + (return nil)))) + (%change-class instance new-class initargs))) (defmethod change-class ((instance funcallable-standard-object) (new-class funcallable-standard-class) &rest initargs) - (let ((cpl (class-precedence-list new-class))) - (dolist (class cpl) - (macrolet - ((frob (class-name) - `(when (eq class (find-class ',class-name)) - (error 'metaobject-initialization-violation - :format-control "~@" - :format-arguments (list 'change-class ',class-name) - :references (list '(:amop :initialization ,class-name)))))) - (frob class) - (frob generic-function) - (frob method) - (frob slot-definition)))) - (change-class-internal instance new-class initargs)) + (with-world-lock () + (let ((cpl (class-precedence-list new-class))) + (dolist (class cpl) + (macrolet + ((frob (class-name) + `(when (eq class (find-class ',class-name)) + (error 'metaobject-initialization-violation + :format-control "~@" + :format-arguments (list 'change-class ',class-name) + :references (list '(:amop :initialization ,class-name)))))) + (frob class) + (frob generic-function) + (frob method) + (frob slot-definition)))) + (%change-class instance new-class initargs))) (defmethod change-class ((instance standard-object) (new-class funcallable-standard-class)