better encapsulation support in generic functions
[sbcl.git] / src / pcl / methods.lisp
index 2d63c9b..6d37569 100644 (file)
           ;; System lock because interrupts need to be disabled as
           ;; well: it would be bad to unwind and leave the gf in an
           ;; inconsistent state.
-          (sb-thread::with-recursive-system-spinlock (lock)
+          (sb-thread::with-recursive-system-lock (lock)
             (let ((existing (get-method generic-function
                                         qualifiers
                                         specializers
                        (warn "~@<Invalid qualifiers for ~S method combination ~
                               in method ~S:~2I~_~S.~@:>"
                              mc-name method qualifiers))))))
-
               (unless skip-dfun-update-p
                 (update-ctors 'add-method
                               :generic-function generic-function
       ;; System lock because interrupts need to be disabled as well:
       ;; it would be bad to unwind and leave the gf in an inconsistent
       ;; state.
-      (sb-thread::with-recursive-system-spinlock (lock)
+      (sb-thread::with-recursive-system-lock (lock)
         (let* ((specializers (method-specializers method))
                (methods (generic-function-methods generic-function))
                (new-methods (remove method methods)))
             ;; 7.6.4 point 5 probably entails that if any method says
             ;; &allow-other-keys then the gf should be construed to
             ;; accept any key.
-            (let ((allowp (or gf.allowp
-                              (find '&allow-other-keys methods
-                                    :test #'find
-                                    :key #'method-lambda-list))))
-              (setf (info :function :type name)
+            (let* ((allowp (or gf.allowp
+                               (find '&allow-other-keys methods
+                                     :test #'find
+                                     :key #'method-lambda-list)))
+                   (ftype
                     (specifier-type
                      `(function
                        (,@(mapcar tfun gf.required)
                                       (remove-duplicates
                                        (nconc
                                         (mapcan #'function-keywords methods)
-                                        (mapcar #'keywordicate gf.keys))))))
+                                        (mapcar #'keyword-spec-name gf.keys))))))
                                 (when all-keys
                                   (setq keysp t)
                                   `(&key ,@all-keys))))
                               `(&key))
                           ,@(when allowp
                               `(&allow-other-keys)))
-                       *))
+                       *))))
+              (setf (info :function :type name) ftype
                     (info :function :where-from name) :defined-method
-                    (gf-info-needs-update gf) nil))))))
-    (values)))
+                    (gf-info-needs-update gf) nil)
+              ftype)))))))
 \f
 (defun compute-applicable-methods-function (generic-function arguments)
   (values (compute-applicable-methods-using-types
                         (make-dfun-lambda-list nargs applyp)
                         (make-fast-method-call-lambda-list nargs applyp))))
       (multiple-value-bind (cfunction constants)
-          (get-fun1 `(lambda
+          (get-fun1 `(named-lambda (gf-dispatch ,name)
                       ,arglist
                       ,@(unless function-p
                           `((declare (ignore .pv. .next-method-call.))))
 
 (defun slot-value-using-class-dfun (class object slotd)
   (declare (ignore class))
-  (function-funcall (slot-definition-reader-function slotd) object))
+  (funcall (slot-info-reader (slot-definition-info slotd)) object))
 
 (defun setf-slot-value-using-class-dfun (new-value class object slotd)
   (declare (ignore class))
-  (function-funcall (slot-definition-writer-function slotd) new-value object))
+  (funcall (slot-info-writer (slot-definition-info slotd)) new-value object))
 
 (defun slot-boundp-using-class-dfun (class object slotd)
   (declare (ignore class))
-  (function-funcall (slot-definition-boundp-function slotd) object))
+  (funcall (slot-info-boundp (slot-definition-info slotd)) object))
 
 (defun special-case-for-compute-discriminating-function-p (gf)
   (or (eq gf #'slot-value-using-class)
       (eq gf #'(setf slot-value-using-class))
       (eq gf #'slot-boundp-using-class)))
 
+;;; this is the normal function for computing the discriminating
+;;; function of a standard-generic-function
 (let (initial-print-object-cache)
-  (defmethod compute-discriminating-function ((gf standard-generic-function))
+  (defun standard-compute-discriminating-function (gf)
     (let ((dfun-state (slot-value gf 'dfun-state)))
-      (when (special-case-for-compute-discriminating-function-p gf)
-        ;; if we have a special case for
-        ;; COMPUTE-DISCRIMINATING-FUNCTION, then (at least for the
-        ;; special cases implemented as of 2006-05-09) any information
-        ;; in the cache is misplaced.
-        (aver (null dfun-state)))
-      (typecase dfun-state
-        (null
-         (when (eq gf #'compute-applicable-methods)
-           (update-all-c-a-m-gf-info gf))
-         (cond
-           ((eq gf #'slot-value-using-class)
-            (update-slot-value-gf-info gf 'reader)
-            #'slot-value-using-class-dfun)
-           ((eq gf #'(setf slot-value-using-class))
-            (update-slot-value-gf-info gf 'writer)
-            #'setf-slot-value-using-class-dfun)
-           ((eq gf #'slot-boundp-using-class)
-            (update-slot-value-gf-info gf 'boundp)
-            #'slot-boundp-using-class-dfun)
-           ;; KLUDGE: PRINT-OBJECT is not a special-case in the sense
-           ;; of having a desperately special discriminating function.
-           ;; However, it is important that the machinery for printing
-           ;; conditions for stack and heap exhaustion, and the
-           ;; restarts offered by the debugger, work without consuming
-           ;; many extra resources.  This way (testing by name of GF
-           ;; rather than by identity) was the only way I found to get
-           ;; this to bootstrap, given that the PRINT-OBJECT generic
-           ;; function is only set up later, in
-           ;; SRC;PCL;PRINT-OBJECT.LISP.  -- CSR, 2008-06-09
-           ((eq (slot-value gf 'name) 'print-object)
-            (let ((nkeys (nth-value 3 (get-generic-fun-info gf))))
-              (cond ((/= nkeys 1)
-                     ;; KLUDGE: someone has defined a method
-                     ;; specialized on the second argument: punt.
-                     (setf initial-print-object-cache nil)
-                     (make-initial-dfun gf))
-                    (initial-print-object-cache
-                     (multiple-value-bind (dfun cache info)
-                         (make-caching-dfun gf (copy-cache initial-print-object-cache))
-                       (set-dfun gf dfun cache info)))
-                    ;; the relevant PRINT-OBJECT methods get defined
-                    ;; late, by delayed DEF!METHOD.  We mustn't cache
-                    ;; the effective method for our classes earlier
-                    ;; than the relevant PRINT-OBJECT methods are
-                    ;; defined...
-                    ((boundp 'sb-impl::*delayed-def!method-args*)
-                     (make-initial-dfun gf))
-                    (t (multiple-value-bind (dfun cache info)
-                           (make-final-dfun-internal
-                            gf
-                            (mapcar (lambda (x) (list (find-class x)))
-                                    '(sb-kernel::control-stack-exhausted
-                                      sb-kernel::binding-stack-exhausted
-                                      sb-kernel::alien-stack-exhausted
-                                      sb-kernel::heap-exhausted-error
-                                      restart)))
-                         (setq initial-print-object-cache cache)
-                         (set-dfun gf dfun (copy-cache cache) info))))))
-           ((gf-precompute-dfun-and-emf-p (slot-value gf 'arg-info))
-            (make-final-dfun gf))
-           (t
-            (make-initial-dfun gf))))
-        (function dfun-state)
-        (cons (car dfun-state))))))
+          (when (special-case-for-compute-discriminating-function-p gf)
+            ;; if we have a special case for
+            ;; COMPUTE-DISCRIMINATING-FUNCTION, then (at least for the
+            ;; special cases implemented as of 2006-05-09) any information
+            ;; in the cache is misplaced.
+            (aver (null dfun-state)))
+          (typecase dfun-state
+            (null
+             (when (eq gf #'compute-applicable-methods)
+               (update-all-c-a-m-gf-info gf))
+             (cond
+               ((eq gf #'slot-value-using-class)
+                (update-slot-value-gf-info gf 'reader)
+                #'slot-value-using-class-dfun)
+               ((eq gf #'(setf slot-value-using-class))
+                (update-slot-value-gf-info gf 'writer)
+                #'setf-slot-value-using-class-dfun)
+               ((eq gf #'slot-boundp-using-class)
+                (update-slot-value-gf-info gf 'boundp)
+                #'slot-boundp-using-class-dfun)
+               ;; KLUDGE: PRINT-OBJECT is not a special-case in the sense
+               ;; of having a desperately special discriminating function.
+               ;; However, it is important that the machinery for printing
+               ;; conditions for stack and heap exhaustion, and the
+               ;; restarts offered by the debugger, work without consuming
+               ;; many extra resources.  This way (testing by name of GF
+               ;; rather than by identity) was the only way I found to get
+               ;; this to bootstrap, given that the PRINT-OBJECT generic
+               ;; function is only set up later, in
+               ;; SRC;PCL;PRINT-OBJECT.LISP.  -- CSR, 2008-06-09
+               ((eq (slot-value gf 'name) 'print-object)
+                (let ((nkeys (nth-value 3 (get-generic-fun-info gf))))
+                  (cond ((/= nkeys 1)
+                         ;; KLUDGE: someone has defined a method
+                         ;; specialized on the second argument: punt.
+                         (setf initial-print-object-cache nil)
+                         (make-initial-dfun gf))
+                        (initial-print-object-cache
+                         (multiple-value-bind (dfun cache info)
+                             (make-caching-dfun gf (copy-cache initial-print-object-cache))
+                           (set-dfun gf dfun cache info)))
+                        ;; the relevant PRINT-OBJECT methods get defined
+                        ;; late, by delayed DEF!METHOD.  We mustn't cache
+                        ;; the effective method for our classes earlier
+                        ;; than the relevant PRINT-OBJECT methods are
+                        ;; defined...
+                        ((boundp 'sb-impl::*delayed-def!method-args*)
+                         (make-initial-dfun gf))
+                        (t (multiple-value-bind (dfun cache info)
+                               (make-final-dfun-internal
+                                gf
+                                (mapcar (lambda (x) (list (find-class x)))
+                                        '(sb-kernel::control-stack-exhausted
+                                          sb-kernel::binding-stack-exhausted
+                                          sb-kernel::alien-stack-exhausted
+                                          sb-kernel::heap-exhausted-error
+                                          restart)))
+                             (setq initial-print-object-cache cache)
+                             (set-dfun gf dfun (copy-cache cache) info))))))
+               ((gf-precompute-dfun-and-emf-p (slot-value gf 'arg-info))
+                (make-final-dfun gf))
+               (t
+                (make-initial-dfun gf))))
+            (function dfun-state)
+            (cons (car dfun-state))))))
+
+;;; in general we need to support SBCL's encapsulation for generic
+;;; functions: the default implementation of encapsulation changes the
+;;; identity of the function bound to a name, which breaks anything
+;;; class-based, so we implement the encapsulation ourselves in the
+;;; discriminating function.
+(defun sb-impl::encapsulate-generic-function (gf type body)
+  (push (cons type body) (generic-function-encapsulations gf))
+  (reinitialize-instance gf))
+(defun sb-impl::unencapsulate-generic-function (gf type)
+  (setf (generic-function-encapsulations gf)
+        (remove type (generic-function-encapsulations gf)
+                :key #'car :count 1))
+  (reinitialize-instance gf))
+(defun sb-impl::encapsulated-generic-function-p (gf type)
+  (position type (generic-function-encapsulations gf) :key #'car))
+(defun standard-compute-discriminating-function-with-encapsulations (gf encs)
+  (if (null encs)
+      (standard-compute-discriminating-function gf)
+      (let ((inner (standard-compute-discriminating-function-with-encapsulations
+                    gf (cdr encs)))
+            (body (cdar encs)))
+        (lambda (&rest args)
+          (let ((sb-int:arg-list args)
+                (sb-int:basic-definition inner))
+            (declare (special sb-int:arg-list sb-int:basic-definition))
+            (eval body))))))
+(defmethod compute-discriminating-function ((gf standard-generic-function))
+  (standard-compute-discriminating-function-with-encapsulations
+   gf (generic-function-encapsulations gf)))
 \f
 (defmethod (setf class-name) (new-value class)
   (let ((classoid (wrapper-classoid (class-wrapper class))))
   ;; PARSE-LAMBDA-LIST to something handier.
   (multiple-value-bind (required optional restp rest keyp keys allowp
                         auxp aux morep more-context more-count)
-      (parse-lambda-list lambda-list)
+      (parse-lambda-list lambda-list :silent t)
     (declare (ignore restp keyp auxp aux morep))
     (declare (ignore more-context more-count))
     (values required optional rest keys allowp)))