X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fpcl%2Fstd-class.lisp;h=b1a60df4485fd3674bf98d42f4da7d454b5d0ef7;hb=4d31006db24db375cdb83a5726d66c524b36689c;hp=3acc5db71b5d32d3ace15e3adcc618295abb0891;hpb=603af6830a9c242adcec3fe3549e74e04cbe3228;p=sbcl.git diff --git a/src/pcl/std-class.lisp b/src/pcl/std-class.lisp index 3acc5db..b1a60df 100644 --- a/src/pcl/std-class.lisp +++ b/src/pcl/std-class.lisp @@ -69,8 +69,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,6 +79,33 @@ (writer '(setf slot-value-using-class)) (boundp 'slot-boundp-using-class))) (gf (gdefinition gf-name))) + ;; 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) + ;; 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)) + (let* ((name (slot-value slotd 'name))) + (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,17 +125,13 @@ (defmethod compute-slot-accessor-info ((slotd effective-slot-definition) type gf) (let* ((name (slot-value slotd 'name)) - (class (slot-value slotd '%class)) - (old-slotd (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) - (setf (slot-accessor-function slotd type) function)) - (when (and old-slotd (not (eq old-std-p (slot-accessor-std-p slotd 'all)))) - (push (cons class name) *pv-table-cache-update-info*)))) + (setf (slot-accessor-function slotd type) function)))) (defmethod slot-definition-allocation ((slotd structure-slot-definition)) :instance) @@ -151,7 +174,7 @@ ;;; here, the values are read by an automatically generated reader method. (defmethod add-direct-subclass ((class class) (subclass class)) (with-slots (direct-subclasses) class - (pushnew subclass direct-subclasses) + (pushnew subclass direct-subclasses :test #'eq) subclass)) (defmethod remove-direct-subclass ((class class) (subclass class)) (with-slots (direct-subclasses) class @@ -173,15 +196,40 @@ ;;; In each case, we maintain one value which is a cons. The car is the list ;;; methods. The cdr is a list of the generic functions. The cdr is always ;;; computed lazily. + +;;; 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")) + +(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-system-spinlock (*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-system-spinlock (*specializer-lock*) + (call-next-method))) + (defmethod add-direct-method ((specializer class) (method method)) - (with-slots (direct-methods) specializer - (setf (car direct-methods) (adjoin method (car direct-methods)) ;PUSH - (cdr direct-methods) ())) + (let ((cell (slot-value specializer 'direct-methods))) + ;; We need to first smash the CDR, because a parallel read may + ;; be in progress, and because if an interrupt catches us we + ;; need to have a consistent state. + (setf (cdr cell) () + (car cell) (adjoin method (car cell) :test #'eq))) method) + (defmethod remove-direct-method ((specializer class) (method method)) - (with-slots (direct-methods) specializer - (setf (car direct-methods) (remove method (car direct-methods)) - (cdr direct-methods) ())) + (let ((cell (slot-value specializer 'direct-methods))) + ;; We need to first smash the CDR, because a parallel read may + ;; be in progress, and because if an interrupt catches us we + ;; need to have a consistent state. + (setf (cdr cell) () + (car cell) (remove method (car cell)))) method) (defmethod specializer-direct-methods ((specializer class)) @@ -189,20 +237,26 @@ (car direct-methods))) (defmethod specializer-direct-generic-functions ((specializer class)) - (with-slots (direct-methods) specializer - (or (cdr direct-methods) - (setf (cdr direct-methods) - (let (collect) - (dolist (m (car direct-methods)) - ;; the old PCL code used COLLECTING-ONCE which used - ;; #'EQ to check for newness - (pushnew (method-generic-function m) collect :test #'eq)) - (nreverse collect)))))) + (let ((cell (slot-value specializer 'direct-methods))) + ;; If an ADD/REMOVE-METHOD is in progress, no matter: either + ;; 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*) + (setf (cdr cell) + (let (collect) + (dolist (m (car cell)) + ;; the old PCL code used COLLECTING-ONCE which used + ;; #'EQ to check for newness + (pushnew (method-generic-function m) collect :test #'eq)) + (nreverse collect))))))) ;;; This hash table is used to store the direct methods and direct generic ;;; functions of EQL specializers. Each value in the table is the cons. -(defvar *eql-specializer-methods* (make-hash-table :test 'eql)) -(defvar *class-eq-specializer-methods* (make-hash-table :test 'eq)) +;;; +;;; These tables are shared between threads, so they need to be synchronized. +(defvar *eql-specializer-methods* (make-hash-table :test 'eql :synchronized t)) +(defvar *class-eq-specializer-methods* (make-hash-table :test 'eq :synchronized t)) (defmethod specializer-method-table ((specializer eql-specializer)) *eql-specializer-methods*) @@ -216,11 +270,13 @@ (table (specializer-method-table specializer)) (entry (gethash object table))) (unless entry - (setq entry - (setf (gethash object table) - (cons nil nil)))) - (setf (car entry) (adjoin method (car entry)) - (cdr entry) ()) + (setf entry + (setf (gethash object table) (cons nil nil)))) + ;; We need to first smash the CDR, because a parallel read may + ;; be in progress, and because if an interrupt catches us we + ;; need to have a consistent state. + (setf (cdr entry) () + (car entry) (adjoin method (car entry) :test #'eq)) method)) (defmethod remove-direct-method ((specializer specializer-with-object) @@ -228,8 +284,11 @@ (let* ((object (specializer-object specializer)) (entry (gethash object (specializer-method-table specializer)))) (when entry - (setf (car entry) (remove method (car entry)) - (cdr entry) ())) + ;; We need to first smash the CDR, because a parallel read may + ;; be in progress, and because if an interrupt catches us we + ;; need to have a consistent state. + (setf (cdr entry) () + (car entry) (remove method (car entry)))) method)) (defmethod specializer-direct-methods ((specializer specializer-with-object)) @@ -242,11 +301,12 @@ (entry (gethash object (specializer-method-table specializer)))) (when entry (or (cdr entry) - (setf (cdr entry) - (let (collect) - (dolist (m (car entry)) - (pushnew (method-generic-function m) collect :test #'eq)) - (nreverse collect))))))) + (sb-thread::with-spinlock (*specializer-lock*) + (setf (cdr entry) + (let (collect) + (dolist (m (car entry)) + (pushnew (method-generic-function m) collect :test #'eq)) + (nreverse collect)))))))) (defun map-specializers (function) (map-all-classes (lambda (class) @@ -286,83 +346,94 @@ (constantly (make-member-type :members (list (specializer-object specl)))))) (defun real-load-defclass (name metaclass-name supers slots other - readers writers slot-names source-location) + readers writers slot-names source-location safe-p) (with-single-package-locked-error (:symbol name "defining ~S as a class") (%compiler-defclass name readers writers slot-names) (let ((res (apply #'ensure-class name :metaclass metaclass-name :direct-superclasses supers :direct-slots slots :definition-source source-location + 'safe-p safe-p other))) res))) (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) - (ensure-class-values class args) - #+nil - (set-class-type-translation (class-prototype meta) name) - (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) - (ensure-class-values class 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)) - -(defun fix-super (s) - (cond ((classp s) s) - ((not (legal-class-name-p s)) - (error "~S is not a class or a legal class name." s)) - (t - (or (find-class s nil) - (ensure-class s :metaclass 'forward-referenced-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 ensure-class-values (class initargs) +(defun frob-ensure-class-args (args) (let (metaclass metaclassp reversed-plist) - (doplist (key val) initargs - (cond ((eq key :metaclass) - (setf metaclass val - metaclassp key)) - (t - (when (eq key :direct-superclasses) - (setf val (mapcar #'fix-super val))) - (setf reversed-plist (list* val key reversed-plist))))) - (values (cond (metaclassp - (if (classp metaclass) - metaclass - (find-class metaclass))) - ((or (null class) (forward-referenced-class-p class)) - *the-class-standard-class*) - (t - (class-of class))) - (nreverse reversed-plist)))) - + (flet ((frob-superclass (s) + (cond + ((classp s) s) + ((legal-class-name-p s) + (or (find-class s nil) + (ensure-class s :metaclass 'forward-referenced-class))) + (t (error "Not a class or a legal class name: ~S." s))))) + (doplist (key val) args + (cond ((eq key :metaclass) + (unless metaclassp + (setf metaclass val metaclassp key))) + (t + (when (eq key :direct-superclasses) + (setf val (mapcar #'frob-superclass val))) + (setf reversed-plist (list* val key reversed-plist))))) + (values (cond (metaclassp + (if (classp metaclass) + metaclass + (find-class metaclass))) + (t *the-class-standard-class*)) + (nreverse reversed-plist))))) + +(defun call-initfun (fun slotd safe) + (declare (function fun)) + (let ((value (funcall fun))) + (when safe + (let ((typecheck (slot-definition-type-check-function slotd))) + (when typecheck + (funcall (the function typecheck) value)))) + value)) (defmethod shared-initialize :after ((class std-class) slot-names &key (direct-superclasses nil direct-superclasses-p) (direct-slots nil direct-slots-p) - (direct-default-initargs nil direct-default-initargs-p)) + (direct-default-initargs nil direct-default-initargs-p) + definition-source) (cond (direct-superclasses-p (setq direct-superclasses (or direct-superclasses @@ -375,8 +446,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)))) @@ -393,6 +464,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)) @@ -401,37 +473,21 @@ (old (assoc name old-class-slot-cells))) (if (or (not old) (eq t slot-names) - (member name 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))) (add-direct-subclasses class direct-superclasses) - (update-class class nil) - (do* ((slots (slot-value class 'slots) (cdr slots)) - (dupes nil)) - ((null slots) (when dupes - (style-warn - ;; FIXME: the indentation request ("~4I") - ;; below appears not to do anything. Finding - ;; out why would be nice. -- CSR, 2003-04-24 - "~@~@:>" - 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)))) - (add-slot-accessors class direct-slots) + (if (class-finalized-p class) + ;; required by AMOP, "Reinitialization of Class Metaobjects" + (finalize-inheritance class) + (update-class class nil)) + (add-slot-accessors class direct-slots definition-source) (make-preliminary-layout class)) (defmethod shared-initialize :after ((class forward-referenced-class) @@ -447,23 +503,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)))) - (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) @@ -504,7 +562,7 @@ (defmethod shared-initialize :after ((class condition-class) slot-names &key direct-slots direct-superclasses) (declare (ignore slot-names)) - (let ((classoid (find-classoid (class-name class)))) + (let ((classoid (find-classoid (slot-value class 'name)))) (with-slots (wrapper %class-precedence-list cpl-available-p prototype (direct-supers direct-superclasses)) class @@ -518,7 +576,9 @@ (setq %class-precedence-list (compute-class-precedence-list class)) (setq cpl-available-p t) (add-direct-subclasses class direct-superclasses) - (setf (slot-value class 'slots) (compute-slots class)))) + (let ((slots (compute-slots class))) + (setf (slot-value class 'slots) slots) + (setf (layout-slot-table wrapper) (make-slot-table class slots))))) ;; Comment from Gerd's PCL, 2003-05-15: ;; ;; We don't ADD-SLOT-ACCESSORS here because we don't want to @@ -532,7 +592,7 @@ ;; remove slot accessors but never put them back. I've added a ;; REINITIALIZE-INSTANCE :AFTER (CONDITION-CLASS) method, but what ;; was meant to happen? -- CSR, 2005-11-18 - (update-pv-table-cache-info class)) + ) (defmethod direct-slot-definition-class ((class condition-class) &rest initargs) @@ -579,7 +639,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 @@ -592,72 +652,84 @@ (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 (class) - (let ((dd (get-structure-dd (class-name class)))) - (lambda () - (sb-kernel::%make-instance-with-layout - (sb-kernel::compiler-layout-or-lose (dd-name dd)))))) +(defun make-defstruct-allocation-function (name) + ;; FIXME: Why don't we go class->layout->info == dd + (let ((dd (find-defstruct-description name))) + (ecase (dd-type dd) + (structure + (%make-structure-instance-allocator dd nil)) + (funcallable-structure + (%make-funcallable-structure-instance-allocator dd nil))))) (defmethod shared-initialize :after ((class structure-class) slot-names &key (direct-superclasses nil direct-superclasses-p) (direct-slots nil direct-slots-p) - direct-default-initargs) + direct-default-initargs + definition-source) (declare (ignore slot-names direct-default-initargs)) (if direct-superclasses-p (setf (slot-value class 'direct-superclasses) (or direct-superclasses (setq direct-superclasses - (and (not (eq (class-name class) 'structure-object)) + (and (not (eq (slot-value class 'name) 'structure-object)) (list *the-class-structure-object*))))) (setq direct-superclasses (slot-value class 'direct-superclasses))) - (let* ((name (class-name class)) + (let* ((name (slot-value class 'name)) (from-defclass-p (slot-value class 'from-defclass-p)) (defstruct-p (or from-defclass-p (not (structure-type-p name))))) (if direct-slots-p @@ -692,18 +764,22 @@ (setf (slot-value class 'defstruct-form) defstruct-form) (setf (slot-value class 'defstruct-constructor) constructor))) (setf (slot-value class 'defstruct-constructor) - (make-defstruct-allocation-function class))) + ;; KLUDGE: not class; in fixup.lisp, can't access slots + ;; outside methods yet. + (make-defstruct-allocation-function name))) (add-direct-subclasses class direct-superclasses) (setf (slot-value class '%class-precedence-list) (compute-class-precedence-list class)) (setf (slot-value class 'cpl-available-p) t) - (setf (slot-value class 'slots) (compute-slots class)) - (let ((lclass (find-classoid (class-name class)))) - (setf (classoid-pcl-class lclass) class) - (setf (slot-value class 'wrapper) (classoid-layout lclass))) + (let ((slots (compute-slots class))) + (setf (slot-value class 'slots) slots) + (let* ((lclass (find-classoid (slot-value class 'name))) + (layout (classoid-layout lclass))) + (setf (classoid-pcl-class lclass) class) + (setf (slot-value class 'wrapper) layout) + (setf (layout-slot-table layout) (make-slot-table class slots)))) (setf (slot-value class 'finalized-p) t) - (update-pv-table-cache-info class) - (add-slot-accessors class direct-slots))) + (add-slot-accessors class direct-slots definition-source))) (defmethod direct-slot-definition-class ((class structure-class) &rest initargs) (declare (ignore initargs)) @@ -712,40 +788,37 @@ (defmethod finalize-inheritance ((class structure-class)) nil) ; always finalized -(defun add-slot-accessors (class dslotds) - (fix-slot-accessors class dslotds 'add)) +(defun add-slot-accessors (class dslotds &optional source-location) + (fix-slot-accessors class dslotds 'add source-location)) (defun remove-slot-accessors (class dslotds) (fix-slot-accessors class dslotds 'remove)) -(defun fix-slot-accessors (class dslotds add/remove) - (flet ((fix (gfspec name r/w) +(defun fix-slot-accessors (class dslotds add/remove &optional source-location) + (flet ((fix (gfspec name r/w doc) (let ((gf (cond ((eq add/remove 'add) - (if (fboundp gfspec) - (without-package-locks - (ensure-generic-function gfspec)) + (or (find-generic-function gfspec nil) (ensure-generic-function gfspec :lambda-list (case r/w (r '(object)) (w '(new-value object)))))) - ((generic-function-p (and (fboundp gfspec) - (fdefinition gfspec))) - (without-package-locks - (ensure-generic-function gfspec)))))) + (t + (find-generic-function gfspec nil))))) (when gf (case r/w (r (if (eq add/remove 'add) - (add-reader-method class gf name) + (add-reader-method class gf name doc source-location) (remove-reader-method class gf))) (w (if (eq add/remove 'add) - (add-writer-method class gf name) + (add-writer-method class gf name doc source-location) (remove-writer-method class gf)))))))) (dolist (dslotd dslotds) - (let ((slot-name (slot-definition-name dslotd))) + (let ((slot-name (slot-definition-name dslotd)) + (slot-doc (%slot-definition-documentation dslotd))) (dolist (r (slot-definition-readers dslotd)) - (fix r slot-name 'r)) + (fix r slot-name 'r slot-doc)) (dolist (w (slot-definition-writers dslotd)) - (fix w slot-name 'w)))))) + (fix w slot-name 'w slot-doc)))))) (defun add-direct-subclasses (class supers) (dolist (super supers) @@ -775,16 +848,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) @@ -802,7 +876,7 @@ (find-class 'function) (cpl-protocol-violation-cpl c))))) -(defun update-cpl (class cpl) +(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))) @@ -816,11 +890,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))) @@ -830,13 +904,13 @@ (when cpl (let ((first (car cpl))) (dolist (c (cdr cpl)) - (pushnew c (slot-value first 'can-precede-list)))) + (pushnew c (slot-value first 'can-precede-list) :test #'eq))) (update-class-can-precede-p (cdr cpl)))) (defun class-can-precede-p (class1 class2) - (member class2 (class-can-precede-list class1))) + (member class2 (class-can-precede-list class1) :test #'eq)) -(defun update-slots (class eslotds) +(defun %update-slots (class eslotds) (let ((instance-slots ()) (class-slots ())) (dolist (eslotd eslotds) @@ -875,17 +949,34 @@ (make-instances-obsolete class) (class-wrapper class))))) - (with-slots (wrapper slots) class - (update-lisp-class-layout class nwrapper) - (setf slots eslotds - (wrapper-instance-slots-layout nwrapper) nlayout - (wrapper-class-slots nwrapper) nwrapper-class-slots - (wrapper-no-of-instance-slots nwrapper) nslots - wrapper nwrapper)) + (%update-lisp-class-layout class nwrapper) + (setf (slot-value class 'slots) 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)))) (setf (slot-value class 'finalized-p) t) (unless (eq owrapper nwrapper) - (update-pv-table-cache-info class) - (maybe-update-standard-class-locations class))))) + (maybe-update-standard-slot-locations class))))) (defun compute-class-slots (eslotds) (let (collect) @@ -896,12 +987,21 @@ (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) (let ((cpl (class-precedence-list class))) - (or (member *the-class-slot-class* cpl) + (or (member *the-class-slot-class* cpl :test #'eq) (member *the-class-standard-effective-slot-definition* - cpl)))) + cpl :test #'eq)))) (let ((gf-table (make-hash-table :test 'eq))) (labels ((collect-gfs (class) (dolist (gf (specializer-direct-generic-functions class)) @@ -912,9 +1012,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) @@ -966,7 +1063,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) @@ -990,10 +1088,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) @@ -1019,7 +1119,7 @@ (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) @@ -1045,6 +1145,7 @@ (allocation nil) (allocation-class nil) (type t) + (type-check-function nil) (documentation nil) (documentationp nil) (namep nil) @@ -1057,19 +1158,29 @@ (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)) + (declare (function old-function fun)) + (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) @@ -1086,6 +1197,7 @@ :allocation allocation :allocation-class allocation-class :type type + 'type-check-function type-check-function :class class :documentation documentation))) @@ -1107,39 +1219,47 @@ (declare (ignore direct-slot initargs)) (find-class 'standard-reader-method)) -(defmethod add-reader-method ((class slot-class) generic-function slot-name) +(defmethod add-reader-method ((class slot-class) generic-function slot-name slot-documentation source-location) (add-method generic-function (make-a-method 'standard-reader-method () (list (or (class-name class) 'object)) (list class) (make-reader-method-function class slot-name) - "automatically generated reader method" - slot-name))) + (or slot-documentation "automatically generated reader method") + :slot-name slot-name + :object-class class + :method-class-function #'reader-method-class + :definition-source source-location))) (defmethod writer-method-class ((class slot-class) direct-slot &rest initargs) (declare (ignore direct-slot initargs)) (find-class 'standard-writer-method)) -(defmethod add-writer-method ((class slot-class) generic-function slot-name) +(defmethod add-writer-method ((class slot-class) generic-function slot-name slot-documentation source-location) (add-method generic-function (make-a-method 'standard-writer-method () (list 'new-value (or (class-name class) 'object)) (list *the-class-t* class) (make-writer-method-function class slot-name) - "automatically generated writer method" - slot-name))) + (or slot-documentation "automatically generated writer method") + :slot-name slot-name + :object-class class + :method-class-function #'writer-method-class + :definition-source source-location))) -(defmethod add-boundp-method ((class slot-class) generic-function slot-name) +(defmethod add-boundp-method ((class slot-class) generic-function slot-name slot-documentation source-location) (add-method generic-function - (make-a-method 'standard-boundp-method + (make-a-method (constantly (find-class 'standard-boundp-method)) + class () (list (or (class-name class) 'object)) (list class) (make-boundp-method-function class slot-name) - "automatically generated boundp method" - slot-name))) + (or slot-documentation "automatically generated boundp method") + :slot-name slot-name + :definition-source source-location))) (defmethod remove-reader-method ((class slot-class) generic-function) (let ((method (get-method generic-function () (list class) nil))) @@ -1154,9 +1274,10 @@ (let ((method (get-method generic-function () (list class) nil))) (when method (remove-method generic-function method)))) -;;; MAKE-READER-METHOD-FUNCTION and MAKE-WRITE-METHOD function are NOT -;;; part of the standard protocol. They are however useful, PCL makes -;;; use of them internally and documents them for PCL users. +;;; MAKE-READER-METHOD-FUNCTION and MAKE-WRITER-METHOD-FUNCTION +;;; function are NOT part of the standard protocol. They are however +;;; useful; PCL makes use of them internally and documents them for +;;; PCL users. (FIXME: but SBCL certainly doesn't) ;;; ;;; *** This needs work to make type testing by the writer functions which ;;; *** do type testing faster. The idea would be to have one constructor @@ -1168,13 +1289,13 @@ ;;; *** defined for this metaclass a chance to run. (defmethod make-reader-method-function ((class slot-class) slot-name) - (make-std-reader-method-function (class-name class) slot-name)) + (make-std-reader-method-function class slot-name)) (defmethod make-writer-method-function ((class slot-class) slot-name) - (make-std-writer-method-function (class-name class) slot-name)) + (make-std-writer-method-function class slot-name)) (defmethod make-boundp-method-function ((class slot-class) slot-name) - (make-std-boundp-method-function (class-name class) slot-name)) + (make-std-boundp-method-function class slot-name)) (defmethod compatible-meta-class-change-p (class proto-new-class) (eq (class-of class) (class-of proto-new-class))) @@ -1206,7 +1327,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 @@ -1219,47 +1340,46 @@ ;; good style. There has to be a better way! -- CSR, ;; 2002-10-29 (eq (layout-invalid owrapper) t)) - (let ((nwrapper (make-wrapper (wrapper-no-of-instance-slots owrapper) + (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)) - (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)) + (setf (wrapper-slot-table nwrapper) + (wrapper-slot-table owrapper)) + (%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 (wrapper-no-of-instance-slots 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)) - (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-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)) + (%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)) @@ -1307,12 +1427,12 @@ "~@" (type-of (obsolete-structure-datum condition)))))) -(defun obsolete-instance-trap (owrapper nwrapper instance) - (if (not (pcl-instance-p 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)) @@ -1360,7 +1480,7 @@ (assq nlocal oclass-slots)) (push nlocal added))) - (swap-wrappers-and-slots instance copy) + (%swap-wrappers-and-slots instance copy) (update-instance-for-redefined-class instance added @@ -1368,7 +1488,7 @@ 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)) @@ -1399,63 +1519,67 @@ ;; 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) @@ -1510,7 +1634,11 @@ ;; FILE-STREAM and STRING-STREAM (as they have the same ;; layout-depthoid). Is there any way we can provide a useful ;; error message? -- CSR, 2005-05-03 - (eq s *the-class-file-stream*) (eq s *the-class-string-stream*))) + (eq s *the-class-file-stream*) (eq s *the-class-string-stream*) + ;; This probably shouldn't be mixed in with certain other + ;; classes, too, but it seems to work both with STANDARD-OBJECT + ;; and FUNCALLABLE-STANDARD-OBJECT + (eq s *the-class-sequence*))) ;;; Some necessary methods for FORWARD-REFERENCED-CLASS (defmethod class-direct-slots ((class forward-referenced-class)) ()) @@ -1528,7 +1656,7 @@ t) (defmethod add-dependent ((metaobject dependent-update-mixin) dependent) - (pushnew dependent (plist-value metaobject 'dependents))) + (pushnew dependent (plist-value metaobject 'dependents) :test #'eq)) (defmethod remove-dependent ((metaobject dependent-update-mixin) dependent) (setf (plist-value metaobject 'dependents)