X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fpcl%2Fstd-class.lisp;h=847d38920dcb07d792aae39587b25988335ca08a;hb=119d1c157e519573074720b7897a9fa918329ac5;hp=553e17d3294fc47ed0cf2832e43b7601d81659c3;hpb=fb03344c5e8388e0b16512f1cb662d8cf5d13972;p=sbcl.git diff --git a/src/pcl/std-class.lisp b/src/pcl/std-class.lisp index 553e17d..847d389 100644 --- a/src/pcl/std-class.lisp +++ b/src/pcl/std-class.lisp @@ -173,15 +173,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-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-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)))) 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,15 +214,19 @@ (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. @@ -215,12 +244,17 @@ (let* ((object (specializer-object specializer)) (table (specializer-method-table specializer)) (entry (gethash object table))) + ;; This table is shared between multiple specializers, but + ;; no worries as (at least for the time being) our hash-tables + ;; are thread safe. (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))) method)) (defmethod remove-direct-method ((specializer specializer-with-object) @@ -228,8 +262,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 +279,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,13 +324,14 @@ (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))) @@ -309,9 +348,7 @@ (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) + (frob-ensure-class-args args) (setf class (apply #'make-instance meta :name name initargs)) (without-package-locks (setf (find-class name) class)) @@ -320,7 +357,7 @@ (defmethod ensure-class-using-class ((class pcl-class) name &rest args &key) (multiple-value-bind (meta initargs) - (ensure-class-values class args) + (frob-ensure-class-args args) (unless (eq (class-of class) meta) (apply #'change-class class meta initargs)) (apply #'reinitialize-instance class initargs) @@ -329,34 +366,29 @@ (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))))) - -(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))))) (defmethod shared-initialize :after ((class std-class) slot-names &key @@ -410,27 +442,10 @@ (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)))) + (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) (make-preliminary-layout class)) @@ -455,15 +470,7 @@ ;; 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 ((classoid (or (let ((layout (slot-value class 'wrapper))) - (when layout (layout-classoid layout))) - #+nil - (find-classoid name nil) - (make-standard-classoid - :name (if (symbolp name) name nil)))) - (layout (make-wrapper 0 class))) - (setf (layout-classoid layout) classoid) - (setf (classoid-pcl-class classoid) class) + (let ((layout (make-wrapper 0 class))) (setf (slot-value class 'wrapper) layout) (let ((cpl (compute-preliminary-cpl class))) (setf (layout-inherits layout) @@ -471,7 +478,7 @@ (map 'simple-vector #'class-wrapper (reverse (rest cpl)))))) (register-layout layout :invalidate t) - (setf (classoid-layout classoid) layout)))) + (set-class-type-translation class (layout-classoid layout))))) (mapc #'make-preliminary-layout (class-direct-subclasses class))))) @@ -527,7 +534,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 @@ -648,7 +657,8 @@ (values defstruct-form constructor reader-names writer-names))) (defun make-defstruct-allocation-function (class) - (let ((dd (get-structure-dd (class-name class)))) + ;; FIXME: Why don't we go class->layout->info == dd + (let ((dd (find-defstruct-description (class-name class)))) (lambda () (sb-kernel::%make-instance-with-layout (sb-kernel::compiler-layout-or-lose (dd-name dd)))))) @@ -706,10 +716,13 @@ (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 (class-name class))) + (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))) @@ -728,33 +741,30 @@ (fix-slot-accessors class dslotds 'remove)) (defun fix-slot-accessors (class dslotds add/remove) - (flet ((fix (gfspec name r/w) + (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) (remove-reader-method class gf))) (w (if (eq add/remove 'add) - (add-writer-method class gf name) + (add-writer-method class gf name doc) (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) @@ -784,16 +794,16 @@ ;;; 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)))) + (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)))) (define-condition cpl-protocol-violation (reference-condition error) ((class :initarg :class :reader cpl-protocol-violation-class) @@ -884,13 +894,31 @@ (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-instance-slots-layout nwrapper) nlayout + (wrapper-class-slots nwrapper) nwrapper-class-slots + (layout-length nwrapper) nslots + (slot-value class 'wrapper) nwrapper) + (setf (layout-slot-table nwrapper) (make-slot-table class eslotds)) + (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) @@ -1054,6 +1082,7 @@ (allocation nil) (allocation-class nil) (type t) + (type-check-function nil) (documentation nil) (documentationp nil) (namep nil) @@ -1079,6 +1108,15 @@ 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) @@ -1095,6 +1133,7 @@ :allocation allocation :allocation-class allocation-class :type type + 'type-check-function type-check-function :class class :documentation documentation))) @@ -1116,38 +1155,43 @@ (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) (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))) (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) (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))) -(defmethod add-boundp-method ((class slot-class) generic-function slot-name) +(defmethod add-boundp-method ((class slot-class) generic-function slot-name slot-documentation) (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" + (or slot-documentation "automatically generated boundp method") slot-name))) (defmethod remove-reader-method ((class slot-class) generic-function) @@ -1163,9 +1207,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 @@ -1177,13 +1222,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))) @@ -1228,7 +1273,7 @@ ;; 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)) @@ -1255,7 +1300,7 @@ ;;; 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) + (nwrapper (make-wrapper (layout-length owrapper) class))) (unless (class-finalized-p class) (if (class-has-a-forward-referenced-superclass-p class) @@ -1317,7 +1362,7 @@ (type-of (obsolete-structure-datum condition)))))) (defun obsolete-instance-trap (owrapper nwrapper instance) - (if (not (pcl-instance-p 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)) @@ -1519,7 +1564,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)) ())