src/method-{proto,impl}.lisp: Abstract out the receiver type.
[sod] / src / method-proto.lisp
index 629e8a7..ed15ff2 100644 (file)
    will be a list of applicable methods sorted in most-to-least specific
    order."))
 
    will be a list of applicable methods sorted in most-to-least specific
    order."))
 
+(export 'sod-message-receiver-type)
+(defgeneric sod-message-receiver-type (message class)
+  (:documentation
+   "Return the type of the `me' argument in a MESSAGE received by CLASS.
+
+   Typically this will just be `CLASS *'."))
+
+(export 'sod-message-applicable-methods)
+(defgeneric sod-message-applicable-methods (message class)
+  (:documentation
+   "Return a list of applicable methods for a MESSAGE.
+
+   The list contains all methods applicable for MESSAGE when sent to an
+   instance of CLASS, most specific first."))
+
+(export 'sod-message-keyword-argument-lists)
+(defgeneric sod-message-keyword-argument-lists
+    (message class direct-methods state)
+  (:documentation
+   "Returns a list of keyword argument lists to be merged.
+
+   This should return a list suitable for passing to `merge-keyword-lists',
+   i.e., each element should be a pair consisting of a function describing
+   the source of the argument list (returning location and description), and
+   a list of `argument' objects.
+
+   The MESSAGE is the message being processed; CLASS is a receiver class
+   under consideration; DIRECT-METHODS is the complete list of applicable
+   direct methods (most specific first); and STATE is an `inheritance-path-
+   reporter-state' object which can be used by the returned reporting
+   functions."))
+
+(export 'compute-effective-method-keyword-arguments)
+(defun compute-effective-method-keyword-arguments
+    (message class direct-methods)
+  "Return a merged keyword argument list.
+
+   The returned list combines all of the applicable methods, provided as
+   DIRECT-METHODS, applicable to MESSAGE when received by an instance of
+   CLASS, possibly with other keywords as determined by `sod-keyword-
+   argument-lists'."
+  (let ((state (make-inheritance-path-reporter-state class)))
+    (merge-keyword-lists (lambda ()
+                          (values class
+                                  (format nil
+                                          "methods for message `~A' ~
+                                           applicable to class `~A'"
+                                          message class)))
+                        (sod-message-keyword-argument-lists message
+                                                            class
+                                                            direct-methods
+                                                            state))))
+
+(export 'sod-message-check-methods)
+(defgeneric sod-message-check-methods (message class direct-methods)
+  (:documentation
+   "Check that the applicable methods for a MESSAGE are compatible.
+
+   Specifically, given the DIRECT-METHODS applicable for the message when
+   received by an instance of CLASS, signal errors if the methods don't
+   match the MESSAGE or each other."))
+
 (export 'sod-message-effective-method-class)
 (defgeneric sod-message-effective-method-class (message)
   (:documentation
 (export 'sod-message-effective-method-class)
 (defgeneric sod-message-effective-method-class (message)
   (:documentation
    The list needn't be in any particular order."))
 
 (export '(method-entry method-entry-effective-method
    The list needn't be in any particular order."))
 
 (export '(method-entry method-entry-effective-method
-         method-entry-chain-head method-entry-chain-tail))
+         method-entry-chain-head method-entry-chain-tail
+         method-entry-role))
 (defclass method-entry ()
   ((%method :initarg :method :type effective-method
            :reader method-entry-effective-method)
 (defclass method-entry ()
   ((%method :initarg :method :type effective-method
            :reader method-entry-effective-method)
 
    No `me' argument is prepended; any `:ellipsis' is left as it is."))
 
 
    No `me' argument is prepended; any `:ellipsis' is left as it is."))
 
-(export 'sod-message-no-varargs-tail)
-(defgeneric sod-message-no-varargs-tail (message)
+(export 'sod-method-description)
+(defgeneric sod-method-description (method)
   (:documentation
   (:documentation
-   "Return the argument tail for the message with `:ellipsis' substituted.
+   "Return an adjectival phrase describing METHOD.
 
 
-   As with `sod-message-argument-tail', no `me' argument is prepended.
-   However, an `:ellipsis' is replaced by an argument of type `va_list',
-   named `sod__ap'."))
+    The result will be placed into an error message reading something like
+    ``Conflicting definition of DESCRIPTION direct method `bogus'''.  Two
+    direct methods which can coexist in the same class, defined on the same
+    message, should have differing descriptions."))
 
 (export 'sod-method-function-type)
 (defgeneric sod-method-function-type (method)
 
 (export 'sod-method-function-type)
 (defgeneric sod-method-function-type (method)
 
    The default method indirects through `method-entry-slot-name-by-role'."))
 
 
    The default method indirects through `method-entry-slot-name-by-role'."))
 
+(export 'method-entry-slot-name-by-role)
 (defgeneric method-entry-slot-name-by-role (entry role name)
   (:documentation "Easier implementation for `method-entry-slot-name'.")
   (:method ((entry method-entry) (role (eql nil)) name) name)
 (defgeneric method-entry-slot-name-by-role (entry role name)
   (:documentation "Easier implementation for `method-entry-slot-name'.")
   (:method ((entry method-entry) (role (eql nil)) name) name)
    not included, and neither are more exotic arguments added as part of the
    method delegation protocol."))
 
    not included, and neither are more exotic arguments added as part of the
    method delegation protocol."))
 
+(export 'effective-method-live-p)
+(defgeneric effective-method-live-p (method)
+  (:documentation
+   "Returns true if the effective METHOD is live.
+
+   An effective method is `live' if it should actually have proper method
+   entry functions associated with it and stored in the class vtable.  The
+   other possibility is that the method is `dead', in which case the function
+   pointers in the vtable are left null."))
+
 ;;;--------------------------------------------------------------------------
 ;;; Code generation.
 
 ;;;--------------------------------------------------------------------------
 ;;; Code generation.
 
 
 ;;; Additional instructions.
 
 
 ;;; Additional instructions.
 
-;; HACK: use gensyms for the `class' and `expr' slots to avoid leaking the
-;; slot names, because `expr' is exported by our package, and `class' is
-;; actually from the `common-lisp' package.
 (definst convert-to-ilayout (stream :export t)
 (definst convert-to-ilayout (stream :export t)
-    (#1=#:class chain-head #2=#:expr)
+    (%class chain-head %expr)
   (format stream "SOD_ILAYOUT(~@<~A, ~_~A, ~_~A~:>)"
   (format stream "SOD_ILAYOUT(~@<~A, ~_~A, ~_~A~:>)"
-         #1# (sod-class-nickname chain-head) #2#))
+         class (sod-class-nickname chain-head) expr))
 
 ;;; Utilities.
 
 
 ;;; Utilities.
 
-(defvar *keyword-struct-disposition* :unset
+(defvar-unbound *keyword-struct-disposition*
   "The current state of the keyword structure.
 
   "The current state of the keyword structure.
 
-   This can be one of four values.
-
-     * `:unset' -- the top-level default, mostly because I can't leave it
-       unbound and write this documentation.  Nothing that matters should see
-       this state.
+   This can be one of three values.
 
      * `:local' -- the structure itself is in a local variable `sod__kw'.
        This is used in the top-level effective method.
 
      * `:local' -- the structure itself is in a local variable `sod__kw'.
        This is used in the top-level effective method.
                          ((keyword-message-p message)
                           (cons (make-argument *sod-key-pointer*
                                                (c-type (* (void :const))))
                          ((keyword-message-p message)
                           (cons (make-argument *sod-key-pointer*
                                                (c-type (* (void :const))))
-                                raw-args))))
-        (*keyword-struct-disposition* t))
+                                raw-args))
+                         (t raw-args)))
+        (*keyword-struct-disposition* (if (effective-method-keywords method)
+                                          :pointer :null)))
     (codegen-push codegen)
     (ensure-ilayout-var codegen super)
     (codegen-push codegen)
     (ensure-ilayout-var codegen super)
-    (when (and (keyword-message-p message)
-              (not (eq *keyword-struct-disposition* :null)))
-      (let ((tag (effective-method-keyword-struct-tag method)))
-       (ensure-var codegen *sod-keywords* (c-type (* (struct tag :const)))
-                   *sod-key-pointer*)))
+    (deliver-call codegen :void "SOD__IGNORE" "sod__obj")
+    (when (keyword-message-p message)
+      (if (eq *keyword-struct-disposition* :null)
+         (deliver-call codegen :void "SOD__IGNORE" *sod-key-pointer*)
+         (let ((tag (effective-method-keyword-struct-tag method)))
+           (ensure-var codegen *sod-keywords*
+                       (c-type (* (struct tag :const)))
+                       *sod-key-pointer*))))
     (funcall body (codegen-target codegen))
     (codegen-pop-function codegen (temporary-function)
                          (c-type (fun (lisp return-type)
     (funcall body (codegen-target codegen))
     (codegen-pop-function codegen (temporary-function)
                          (c-type (fun (lisp return-type)
    nil."
 
   (let* ((message (codegen-message codegen))
    nil."
 
   (let* ((message (codegen-message codegen))
-        (argument-tail (cond ((varargs-message-p message)
-                              (cons *sod-tmp-ap* basic-tail))
-                             ((keyword-message-p message)
-                              (cons (keyword-struct-pointer) basic-tail))
-                             (t basic-tail))))
+        (argument-tail (if (varargs-message-p message)
+                           (cons *sod-tmp-ap* basic-tail)
+                           basic-tail)))
     (labels ((next-trampoline (method chain)
               (if (or kernel chain)
                   (make-trampoline codegen (sod-method-class method)
     (labels ((next-trampoline (method chain)
               (if (or kernel chain)
                   (make-trampoline codegen (sod-method-class method)
               (if (null chain)
                   (funcall kernel target)
                   (let ((trampoline (next-trampoline (car chain)
               (if (null chain)
                   (funcall kernel target)
                   (let ((trampoline (next-trampoline (car chain)
-                                                     (cdr chain))))
+                                                     (cdr chain)))
+                        (tail (if (keyword-message-p message)
+                                  (cons (keyword-struct-pointer)
+                                        argument-tail)
+                                  argument-tail)))
                     (invoke-method codegen target
                     (invoke-method codegen target
-                                   (cons trampoline argument-tail)
+                                   (cons trampoline tail)
                                    (car chain))))))
       (invoke chain target))))
 
                                    (car chain))))))
       (invoke chain target))))