chiark / gitweb /
src/class-finalize-{proto,impl}.lisp (finalize-sod-class): Add `:around'.
[sod] / src / class-finalize-impl.lisp
index 5abcc6e90359ef9169899f1ebbc4362d0a95260c..32bc29b812037e8663fc19fb67b89e5177da8248 100644 (file)
@@ -48,6 +48,22 @@ (cl:in-package #:sod)
 ;; Superclass Linearization for Dylan' for more detail.
 ;; http://www.webcom.com/haahr/dylan/linearization-oopsla96.html
 
+;;; Utilities.
+
+(export 'merge-class-lists)
+(defun merge-class-lists (lists pick)
+  "Merge the LISTS of classes, using PICK to break ties.
+
+   This is a convenience wrapper around the main `merge-lists' function.
+   Given that class linearizations (almost?) always specify a custom
+   tiebreaker function, this isn't a keyword argument.  Also, this wrapper
+   provides a standard presentation function so that any errors are presented
+   properly."
+  (merge-lists lists
+              :pick pick
+              :present (lambda (class)
+                         (format nil "`~A'" (sod-class-name class)))))
+
 ;;; Tiebreaker functions.
 
 (defun clos-tiebreaker (candidates so-far)
@@ -117,11 +133,11 @@ (defun clos-cpl (class)
               (remove-duplicates (cons class
                                        (mappend #'superclasses
                                                 direct-supers))))))
-    (merge-lists (mapcar (lambda (class)
-                          (cons class
-                                (sod-class-direct-superclasses class)))
-                        (superclasses class))
-                :pick #'clos-tiebreaker)))
+    (merge-class-lists
+     (mapcar (lambda (class)
+              (cons class (sod-class-direct-superclasses class)))
+            (superclasses class))
+     #'clos-tiebreaker)))
 
 (export 'dylan-cpl)
 (defun dylan-cpl (class)
@@ -143,9 +159,10 @@ (defun dylan-cpl (class)
    you're going to lose anyway."
 
   (let ((direct-supers (sod-class-direct-superclasses class)))
-    (merge-lists (cons (cons class direct-supers)
-                      (mapcar #'sod-class-precedence-list direct-supers))
-                :pick #'clos-tiebreaker)))
+    (merge-class-lists
+     (cons (cons class direct-supers)
+          (mapcar #'sod-class-precedence-list direct-supers))
+     #'clos-tiebreaker)))
 
 (export 'c3-cpl)
 (defun c3-cpl (class)
@@ -162,8 +179,8 @@ (defun c3-cpl (class)
 
   (let* ((direct-supers (sod-class-direct-superclasses class))
         (cpls (mapcar #'sod-class-precedence-list direct-supers)))
-    (merge-lists (cons (cons class direct-supers) cpls)
-                :pick (lambda (candidates so-far)
+    (merge-class-lists (cons (cons class direct-supers) cpls)
+                      (lambda (candidates so-far)
                         (declare (ignore so-far))
                         (c3-tiebreaker candidates cpls)))))
 
@@ -224,13 +241,14 @@ (defun l*loops-cpl (class)
    precedence order i.e., the direct-superclasses list orderings."
 
   (let ((dfs (flavors-cpl class)))
-    (cons class (merge-lists (mapcar #'sod-class-precedence-list
+    (cons class
+         (merge-class-lists (mapcar #'sod-class-precedence-list
                                     (sod-class-direct-superclasses class))
-                            :pick (lambda (candidates so-far)
-                                    (declare (ignore so-far))
-                                    (dolist (class dfs)
-                                      (when (member class candidates)
-                                        (return class))))))))
+                            (lambda (candidates so-far)
+                              (declare (ignore so-far))
+                              (dolist (class dfs)
+                                (when (member class candidates)
+                                  (return class))))))))
 
 ;;; Default function.
 
@@ -403,81 +421,81 @@ (defmethod check-sod-class ((class sod-class))
 ;;;--------------------------------------------------------------------------
 ;;; Finalization.
 
-(defmethod finalize-sod-class ((class sod-class))
+(defmethod finalize-sod-class :around ((class sod-class))
+  "Common functionality for `finalize-sod-class'.
 
-  ;; CLONE-AND-HACK WARNING: Note that `bootstrap-classes' has a (very brief)
-  ;; clone of the CPL and chain establishment code.  If the interface changes
-  ;; then `bootstrap-classes' will need to be changed too.
+     * If an attempt to finalize the CLASS has been made before, then we
+       don't try again.  Similarly, attempts to finalize a class recursively
+       will fail.
 
+     * A condition handler is established to keep track of whether any errors
+       are signalled during finalization.  The CLASS is only marked as
+       successfully finalized if no (unhandled) errors are encountered."
   (with-default-error-location (class)
     (ecase (sod-class-state class)
       ((nil)
 
-       ;; If this fails, mark the class as a loss.
+       ;; If this fails, leave the class marked as a loss.
        (setf (slot-value class 'state) :broken)
 
-       ;; Set up the metaclass if it's not been set already.  This is delayed
-       ;; to give bootstrapping a chance to set up metaclass and superclass
-       ;; circularities.
-       (default-slot (class 'metaclass) (guess-metaclass class))
-
-       ;; Finalize all of the superclasses.  There's some special pleading
-       ;; here to make bootstrapping work: we don't try to finalize the
-       ;; metaclass if we're a root class (no direct superclasses -- because
-       ;; in that case the metaclass will have to be a subclass of us!), or
-       ;; if it's equal to us.  This is enough to tie the knot at the top of
-       ;; the class graph.
-       (with-slots (name direct-superclasses metaclass) class
-        (dolist (super direct-superclasses)
-          (finalize-sod-class super))
-        (unless (or (null direct-superclasses)
-                    (eq class metaclass))
-          (finalize-sod-class metaclass)))
-
-       ;; Stash the class's type.
-       (setf (slot-value class '%type)
-            (make-class-type (sod-class-name class)))
-
-       ;; Clobber the lists of items if they've not been set.
-       (dolist (slot '(slots instance-initializers class-initializers
-                      messages methods))
-        (unless (slot-boundp class slot)
-          (setf (slot-value class slot) nil)))
-
-       ;; If the CPL hasn't been done yet, compute it.
-       (with-slots (class-precedence-list) class
-        (unless (slot-boundp class 'class-precedence-list)
-          (setf class-precedence-list (compute-cpl class))))
-
-       ;; Check that the class is fairly sane.
-       (check-sod-class class)
-
-       ;; Determine the class's layout.
-       (with-slots (chain-head chain chains) class
-        (setf (values chain-head chain chains) (compute-chains class)))
-
-       ;; Done.
+       ;; Invoke the finalization method proper.
+       (call-next-method)
        (setf (slot-value class 'state) :finalized)
        t)
 
+      ;; If the class is broken, we're not going to be able to fix it now.
       (:broken
        nil)
 
+      ;; If we already finalized it, there's no point doing it again.
       (:finalized
        t))))
 
-(flet ((check-class-is-finalized (class)
-        (unless (eq (sod-class-state class) :finalized)
-          (error "Class ~S is not finalized" class))))
-  (macrolet ((define-layout-slot (slot (class) &body body)
-              `(define-on-demand-slot sod-class ,slot (,class)
-                 (check-class-is-finalized ,class)
-                 ,@body)))
-    (define-layout-slot %ilayout (class)
-      (compute-ilayout class))
-    (define-layout-slot effective-methods (class)
-      (compute-effective-methods class))
-    (define-layout-slot vtables (class)
-      (compute-vtables class))))
+(defmethod finalize-sod-class ((class sod-class))
+
+  ;; CLONE-AND-HACK WARNING: Note that `bootstrap-classes' has a (very brief)
+  ;; clone of the CPL and chain establishment code.  If the interface changes
+  ;; then `bootstrap-classes' will need to be changed too.
+
+  ;; Set up the metaclass if it's not been set already.  This is delayed
+  ;; to give bootstrapping a chance to set up metaclass and superclass
+  ;; circularities.
+  (default-slot (class 'metaclass) (guess-metaclass class))
+
+  ;; Finalize all of the superclasses.  There's some special pleading here to
+  ;; make bootstrapping work: we don't try to finalize the metaclass if we're
+  ;; a root class (no direct superclasses -- because in that case the
+  ;; metaclass will have to be a subclass of us!), or if it's equal to us.
+  ;; This is enough to tie the knot at the top of the class graph.
+  (with-slots (name direct-superclasses metaclass) class
+    (dolist (super direct-superclasses)
+      (finalize-sod-class super))
+    (unless (or (null direct-superclasses)
+               (eq class metaclass))
+      (finalize-sod-class metaclass)))
+
+  ;; Stash the class's type.
+  (setf (slot-value class '%type)
+       (make-class-type (sod-class-name class)))
+
+  ;; Clobber the lists of items if they've not been set.
+  (dolist (slot '(slots instance-initializers class-initializers
+                 messages methods))
+    (unless (slot-boundp class slot)
+      (setf (slot-value class slot) nil)))
+
+  ;; If the CPL hasn't been done yet, compute it.
+  (with-slots (class-precedence-list) class
+    (unless (slot-boundp class 'class-precedence-list)
+      (setf class-precedence-list (compute-cpl class))))
+
+  ;; Check that the class is fairly sane.
+  (check-sod-class class)
+
+  ;; Determine the class's layout.
+  (setf (values (slot-value class 'chain-head)
+               (slot-value class 'chain)
+               (slot-value class 'chains))
+       (compute-chains class)))
 
 ;;;----- That's all, folks --------------------------------------------------