chiark / gitweb /
src/: Lift keyword-argument protocol from effective methods to messages.
[sod] / src / method-proto.lisp
1 ;;; -*-lisp-*-
2 ;;;
3 ;;; Method combination protocol
4 ;;;
5 ;;; (c) 2009 Straylight/Edgeware
6 ;;;
7
8 ;;;----- Licensing notice ---------------------------------------------------
9 ;;;
10 ;;; This file is part of the Sensible Object Design, an object system for C.
11 ;;;
12 ;;; SOD is free software; you can redistribute it and/or modify
13 ;;; it under the terms of the GNU General Public License as published by
14 ;;; the Free Software Foundation; either version 2 of the License, or
15 ;;; (at your option) any later version.
16 ;;;
17 ;;; SOD is distributed in the hope that it will be useful,
18 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;;; GNU General Public License for more details.
21 ;;;
22 ;;; You should have received a copy of the GNU General Public License
23 ;;; along with SOD; if not, write to the Free Software Foundation,
24 ;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26 (cl:in-package #:sod)
27
28 ;;;--------------------------------------------------------------------------
29 ;;; Effective methods and entries.
30
31 (export '(effective-method
32           effective-method-message effective-method-class
33           effective-method-keywords))
34 (defclass effective-method ()
35   ((message :initarg :message :type sod-message
36             :reader effective-method-message)
37    (%class :initarg :class :type sod-class :reader effective-method-class)
38    (keywords :type list :reader effective-method-keywords))
39   (:documentation
40    "The behaviour invoked by sending a message to an instance of a class.
41
42    This class describes the behaviour when an instance of CLASS is sent
43    MESSAGE.
44
45    This is not a useful class by itself.  Message classes are expected to
46    define their own effective-method classes.
47
48    An effective method class may accept a `:direct-methods' initarg, which
49    will be a list of applicable methods sorted in most-to-least specific
50    order."))
51
52 (export 'sod-message-applicable-methods)
53 (defgeneric sod-message-applicable-methods (message class)
54   (:documentation
55    "Return a list of applicable methods for a MESSAGE.
56
57    The list contains all methods applicable for MESSAGE when sent to an
58    instance of CLASS, most specific first."))
59
60 (export 'sod-message-keyword-argument-lists)
61 (defgeneric sod-message-keyword-argument-lists
62     (message class direct-methods state)
63   (:documentation
64    "Returns a list of keyword argument lists to be merged.
65
66    This should return a list suitable for passing to `merge-keyword-lists',
67    i.e., each element should be a pair consisting of a function describing
68    the source of the argument list (returning location and description), and
69    a list of `argument' objects.
70
71    The MESSAGE is the message being processed; CLASS is a receiver class
72    under consideration; DIRECT-METHODS is the complete list of applicable
73    direct methods (most specific first); and STATE is an `inheritance-path-
74    reporter-state' object which can be used by the returned reporting
75    functions."))
76
77 (export 'compute-effective-method-keyword-arguments)
78 (defun compute-effective-method-keyword-arguments
79     (message class direct-methods)
80   "Return a merged keyword argument list.
81
82    The returned list combines all of the applicable methods, provided as
83    DIRECT-METHODS, applicable to MESSAGE when received by an instance of
84    CLASS, possibly with other keywords as determined by `sod-keyword-
85    argument-lists'."
86   (let ((state (make-inheritance-path-reporter-state class)))
87     (merge-keyword-lists (lambda ()
88                            (values class
89                                    (format nil
90                                            "methods for message `~A' ~
91                                             applicable to class `~A'"
92                                            message class)))
93                          (sod-message-keyword-argument-lists message
94                                                              class
95                                                              direct-methods
96                                                              state))))
97
98 (export 'sod-message-effective-method-class)
99 (defgeneric sod-message-effective-method-class (message)
100   (:documentation
101    "Return the effective method class for the given MESSAGE.
102
103    This function is invoked by `compute-sod-effective-method'."))
104
105 (export 'primary-method-class)
106 (defgeneric primary-method-class (message)
107   (:documentation
108    "Return the name of the primary direct method class for MESSAGE.
109
110    This protocol is used by `simple-message' subclasses."))
111
112 (export 'compute-sod-effective-method)
113 (defgeneric compute-sod-effective-method (message class)
114   (:documentation
115    "Return the effective method when a CLASS instance receives MESSAGE.
116
117    The default method constructs an instance of the message's chosen
118    `sod-message-effective-method-class', passing the MESSAGE, the CLASS and
119    the list of applicable methods as initargs to `make-instance'."))
120
121 (export 'compute-effective-methods)
122 (defgeneric compute-effective-methods (class)
123   (:documentation
124    "Return a list of all of the effective methods needed for CLASS.
125
126    The list needn't be in any particular order."))
127
128 (export '(method-entry method-entry-effective-method
129           method-entry-chain-head method-entry-chain-tail))
130 (defclass method-entry ()
131   ((%method :initarg :method :type effective-method
132             :reader method-entry-effective-method)
133    (chain-head :initarg :chain-head :type sod-class
134                :reader method-entry-chain-head)
135    (chain-tail :initarg :chain-tail :type sod-class
136                :reader method-entry-chain-tail)
137    (role :initarg :role :type (or keyword null) :reader method-entry-role))
138   (:documentation
139    "An entry point into an effective method.
140
141    Specifically, this is the entry point to the effective METHOD invoked via
142    the vtable for the chain headed by CHAIN-HEAD, and serving the given ROLE.
143    The CHAIN-TAIL is the most specific class on this chain; this is useful
144    because we can reuse the types of method entries from superclasses on
145    non-primary chains.
146
147    Each effective method may have several different method entries, because
148    an effective method can be called via vtables attached to different
149    chains, and such calls will pass instance pointers which point to
150    different `ichain' structures within the overall instance layout; it's the
151    job of the method entry to adjust the instance pointers correctly for the
152    rest of the effective method.
153
154    A vtable can contain more than one entry for the same message.  Such
155    entries are distinguished by their roles.  A message always has an entry
156    with the `nil role; in addition, a varargs message also has a `:valist'
157    role, which accepts a `va_list' argument in place of the variable argument
158    listNo other roles are currently defined, though they may be introduced by
159    extensions.
160
161    The boundaries between a method entry and the effective method
162    is (intentionally) somewhat fuzzy.  In extreme cases, the effective method
163    may not exist at all as a distinct entity in the output because its
164    content is duplicated in all of the method entry functions.  This is left
165    up to the effective method protocol."))
166
167 (export 'make-method-entries)
168 (defgeneric make-method-entries (effective-method chain-head chain-tail)
169   (:documentation
170    "Return a list of `method-entry' objects for an EFFECTIVE-METHOD called
171    via CHAIN-HEAD.
172
173    There is no default method for this function.  (Maybe when the
174    effective-method/method-entry output protocol has settled down I'll know
175    what a sensible default action would be.)"))
176
177 ;;;--------------------------------------------------------------------------
178 ;;; Protocol for messages and direct-methods.
179
180 (export 'sod-message-argument-tail)
181 (defgeneric sod-message-argument-tail (message)
182   (:documentation
183    "Return the argument tail for the message, with invented argument names.
184
185    No `me' argument is prepended; any `:ellipsis' is left as it is."))
186
187 (export 'sod-method-description)
188 (defgeneric sod-method-description (method)
189   (:documentation
190    "Return an adjectival phrase describing METHOD.
191
192     The result will be placed into an error message reading something like
193     ``Conflicting definition of DESCRIPTION direct method `bogus'''.  Two
194     direct methods which can coexist in the same class, defined on the same
195     message, should have differing descriptions."))
196
197 (export 'sod-method-function-type)
198 (defgeneric sod-method-function-type (method)
199   (:documentation
200    "Return the C function type for the direct method.
201
202    This is called during initialization of a direct method object, and the
203    result is cached.
204
205    A default method is provided (by `basic-direct-method') which simply
206    prepends an appropriate `me' argument to the user-provided argument list.
207    Fancy method classes may need to override this behaviour."))
208
209 (export 'sod-method-next-method-type)
210 (defgeneric sod-method-next-method-type (method)
211   (:documentation
212    "Return the C function type for the next-method trampoline.
213
214    This is called during initialization of a direct method object, and the
215    result is cached.  It should return a function type, not a pointer type.
216
217    A default method is provided (by `delegating-direct-method') which should
218    do the right job.  Very fancy subclasses might need to do something
219    different."))
220
221 (export 'sod-method-function-name)
222 (defgeneric sod-method-function-name (method)
223   (:documentation
224    "Return the C function name for the direct method."))
225
226 (export 'keyword-message-p)
227 (defun keyword-message-p (message)
228   "Answer whether the MESSAGE accepts a keyword arguments.
229
230    Dealing with keyword messages is rather fiddly, so this is useful to
231    know."
232   (typep (sod-message-type message) 'c-keyword-function-type))
233
234 (export 'varargs-message-p)
235 (defun varargs-message-p (message)
236   "Answer whether the MESSAGE accepts a variable-length argument list.
237
238    We need to jump through some extra hoops in order to cope with varargs
239    messages, so this is useful to know."
240   (member :ellipsis (sod-message-argument-tail message)))
241
242 ;;;--------------------------------------------------------------------------
243 ;;; Protocol for effective methods and method entries.
244
245 (export 'method-entry-function-type)
246 (defgeneric method-entry-function-type (entry)
247   (:documentation
248    "Return the C function type for a method entry."))
249
250 (export 'method-entry-slot-name)
251 (defgeneric method-entry-slot-name (entry)
252   (:documentation
253    "Return the `vtmsgs' slot name for a method entry.
254
255    The default method indirects through `method-entry-slot-name-by-role'."))
256
257 (defgeneric method-entry-slot-name-by-role (entry role name)
258   (:documentation "Easier implementation for `method-entry-slot-name'.")
259   (:method ((entry method-entry) (role (eql nil)) name) name)
260   (:method ((entry method-entry) (role (eql :valist)) name)
261     (format nil "~A__v" name)))
262
263 (export 'effective-method-basic-argument-names)
264 (defgeneric effective-method-basic-argument-names (method)
265   (:documentation
266    "Return a list of argument names to be passed to direct methods.
267
268    The argument names are constructed from the message's arguments returned
269    by `sod-message-argument-tail', with any ellipsis replaced by an explicit
270    `va_list' argument.  The basic arguments are the ones immediately derived
271    from the programmer's explicitly stated arguments; the `me' argument is
272    not included, and neither are more exotic arguments added as part of the
273    method delegation protocol."))
274
275 (export 'effective-method-live-p)
276 (defgeneric effective-method-live-p (method)
277   (:documentation
278    "Returns true if the effective METHOD is live.
279
280    An effective method is `live' if it should actually have proper method entry
281    functions associated with it and stored in the class vtable.  The other
282    possibility is that the method is `dead', in which case the function
283    pointers in the vtable are left null."))
284
285 ;;;--------------------------------------------------------------------------
286 ;;; Code generation.
287
288 ;;; Enhanced code-generator class.
289
290 (export '(method-codegen codegen-message codegen-class
291           codegen-method codegen-target))
292 (defclass method-codegen (codegen)
293   ((message :initarg :message :type sod-message :reader codegen-message)
294    (%class :initarg :class :type sod-class :reader codegen-class)
295    (%method :initarg :method :type effective-method :reader codegen-method)
296    (target :initarg :target :reader codegen-target))
297   (:documentation
298    "Augments CODEGEN with additional state regarding an effective method.
299
300    We store the effective method, and also its target class and owning
301    message, so that these values are readily available to the code-generating
302    functions."))
303
304 ;;; Protocol.
305
306 (export 'compute-effective-method-body)
307 (defgeneric compute-effective-method-body (method codegen target)
308   (:documentation
309    "Generates the body of an effective method.
310
311    Writes the function body to the code generator.  It can (obviously)
312    generate auxiliary functions if it needs to.
313
314    The arguments are as determined by agreement with the generic function
315    `compute-method-entry-functions'; usually this will be as specified by the
316    `sod-message-argument-tail', with any variable-argument tail reified to a
317    `va_list', and an additional argument `sod__obj' of type pointer-to-
318    ilayout.  The code should deliver the result (if any) to the TARGET."))
319
320 (export 'simple-method-body)
321 (defgeneric simple-method-body (method codegen target)
322   (:documentation
323    "Generate the body of a simple effective method.
324
325    The function is invoked on an effective METHOD, with a CODEGEN to which it
326    should emit code delivering the method's value to TARGET."))
327
328 ;;; Additional instructions.
329
330 ;; HACK: use gensyms for the `class' and `expr' slots to avoid leaking the
331 ;; slot names, because `expr' is exported by our package, and `class' is
332 ;; actually from the `common-lisp' package.
333 (definst convert-to-ilayout (stream :export t)
334     (#1=#:class chain-head #2=#:expr)
335   (format stream "SOD_ILAYOUT(~@<~A, ~_~A, ~_~A~:>)"
336           #1# (sod-class-nickname chain-head) #2#))
337
338 ;;; Utilities.
339
340 (defvar-unbound *keyword-struct-disposition*
341   "The current state of the keyword structure.
342
343    This can be one of three values.
344
345      * `:local' -- the structure itself is in a local variable `sod__kw'.
346        This is used in the top-level effective method.
347
348      * `:pointer' -- the structure is pointed to by the local variable
349        `sod__kw'.  This is used by delegation-chain trampolines.
350
351      * `:null' -- there is in fact no structure because none of the
352        applicable methods actually define any keywords.")
353
354 (defun keyword-access (name &optional suffix)
355   "Return an lvalue designating a named member of the keyword struct.
356
357    If a non-nil SUFFIX is provided, then the member is named NAMESUFFIX."
358   (flet ((mem (op)
359            (format nil "~A~A~A~@[~A~]" *sod-keywords* op name suffix)))
360     (ecase *keyword-struct-disposition*
361       (:local (mem "."))
362       (:pointer (mem "->")))))
363
364 (let ((kw-addr (format nil "&~A" *sod-keywords*)))
365   (defun keyword-struct-pointer ()
366     "Return a pointer to the keyword structure."
367     (ecase *keyword-struct-disposition*
368       (:local kw-addr)
369       (:pointer *sod-keywords*)
370       (:null *null-pointer*))))
371
372 (export 'invoke-method)
373 (defun invoke-method (codegen target arguments-tail direct-method)
374   "Emit code to invoke DIRECT-METHOD, passing it ARGUMENTS-TAIL.
375
376    The code is generated in the context of CODEGEN, which can be any instance
377    of the `codegen' class -- it needn't be an instance of `method-codegen'.
378    The DIRECT-METHOD is called with the given ARGUMENTS-TAIL (a list of
379    argument expressions), preceded by a `me' argument of type pointer-to-
380    CLASS where CLASS is the class on which the method was defined.
381
382    If the message accepts a variable-length argument list then a copy of the
383    prevailing argument pointer is provided in place of the `:ellipsis'."
384
385   (let* ((message (sod-method-message direct-method))
386          (class (sod-method-class direct-method))
387          (function (sod-method-function-name direct-method))
388          (type (sod-method-type direct-method))
389          (keywordsp (keyword-message-p message))
390          (keywords (and keywordsp (c-function-keywords type)))
391          (arguments (append (list (format nil "&sod__obj->~A.~A"
392                                           (sod-class-nickname
393                                            (sod-class-chain-head class))
394                                           (sod-class-nickname class)))
395                             arguments-tail
396                             (mapcar (lambda (arg)
397                                       (let ((name (argument-name arg))
398                                             (default (argument-default arg)))
399                                         (if default
400                                             (make-cond-inst
401                                              (keyword-access name
402                                                              "__suppliedp")
403                                              (keyword-access name)
404                                              default)
405                                             (keyword-access name))))
406                                     keywords))))
407     (cond ((varargs-message-p message)
408            (convert-stmts codegen target (c-type-subtype type)
409                           (lambda (var)
410                             (ensure-var codegen *sod-tmp-ap* c-type-va-list)
411                             (deliver-call codegen :void "va_copy"
412                                           *sod-tmp-ap* *sod-ap*)
413                             (apply #'deliver-call codegen var
414                                    function arguments)
415                             (deliver-call codegen :void "va_end"
416                                           *sod-tmp-ap*))))
417           (keywords
418            (let ((tag (direct-method-suppliedp-struct-tag direct-method)))
419              (with-temporary-var (codegen spvar (c-type (struct tag)))
420                (dolist (arg keywords)
421                  (let ((name (argument-name arg)))
422                    (deliver-expr codegen (format nil "~A.~A" spvar name)
423                                  (keyword-access name "__suppliedp"))))
424                (setf arguments (list* (car arguments) spvar
425                                       (cdr arguments)))
426                (apply #'deliver-call codegen target function arguments))))
427           (t
428            (apply #'deliver-call codegen target function arguments)))))
429
430 (export 'ensure-ilayout-var)
431 (defun ensure-ilayout-var (codegen super)
432   "Define a variable `sod__obj' pointing to the class's ilayout structure.
433
434    CODEGEN is a `method-codegen'.  The class in question is CODEGEN's class,
435    i.e., the target class for the effective method.  SUPER is one of the
436    class's superclasses; it is assumed that `me' is a pointer to a SUPER
437    (i.e., to SUPER's ichain within the ilayout)."
438
439   (let* ((class (codegen-class codegen))
440          (super-head (sod-class-chain-head super)))
441     (ensure-var codegen "sod__obj"
442                 (c-type (* (struct (ilayout-struct-tag class))))
443                 (make-convert-to-ilayout-inst class super-head "me"))))
444
445 (export 'make-trampoline)
446 (defun make-trampoline (codegen super body)
447   "Construct a trampoline function and return its name.
448
449    CODEGEN is a `method-codegen'.  SUPER is a superclass of the CODEGEN
450    class.  We construct a new trampoline function (with an unimaginative
451    name) suitable for being passed to a direct method defined on SUPER as its
452    `next_method'.  In particular, it will have a `me' argument whose type is
453    pointer-to-SUPER.
454
455    The code of the function is generated by BODY, which will be invoked with
456    a single argument which is the TARGET to which it should deliver its
457    result.
458
459    The return value is the name of the generated function."
460
461   (let* ((message (codegen-message codegen))
462          (message-type (sod-message-type message))
463          (message-class (sod-message-class message))
464          (method (codegen-method codegen))
465          (return-type (c-type-subtype message-type))
466          (raw-args (sod-message-argument-tail message))
467          (arguments (cond ((varargs-message-p message)
468                            (cons (make-argument *sod-ap* c-type-va-list)
469                                  (butlast raw-args)))
470                           ((keyword-message-p message)
471                            (cons (make-argument *sod-key-pointer*
472                                                 (c-type (* (void :const))))
473                                  raw-args))))
474          (*keyword-struct-disposition* (if (effective-method-keywords method)
475                                            :pointer :null)))
476     (codegen-push codegen)
477     (ensure-ilayout-var codegen super)
478     (when (keyword-message-p message)
479       (if (eq *keyword-struct-disposition* :null)
480           (deliver-call codegen :void "SOD__IGNORE" *sod-key-pointer*)
481           (let ((tag (effective-method-keyword-struct-tag method)))
482             (ensure-var codegen *sod-keywords*
483                         (c-type (* (struct tag :const)))
484                         *sod-key-pointer*))))
485     (funcall body (codegen-target codegen))
486     (codegen-pop-function codegen (temporary-function)
487                           (c-type (fun (lisp return-type)
488                                        ("me" (* (class super)))
489                                        . arguments))
490                           "Delegation-chain trampoline ~:_~
491                            for `~A.~A' ~:_on `~A'."
492                           (sod-class-nickname message-class)
493                           (sod-message-name message)
494                           (effective-method-class method))))
495
496 ;;;--------------------------------------------------------------------------
497 ;;; Method entry protocol.
498
499 (export 'effective-method-function-name)
500 (defgeneric effective-method-function-name (method)
501   (:documentation
502    "Returns the function name of an effective method."))
503
504 (export 'method-entry-function-name)
505 (defgeneric method-entry-function-name (method chain-head role)
506   (:documentation
507    "Returns the function name of a method entry.
508
509    The method entry is given as an effective method/chain-head/role triple,
510    rather than as a method entry object because we want the function name
511    before we've made the entry object."))
512
513 (export 'compute-method-entry-functions)
514 (defgeneric compute-method-entry-functions (method)
515   (:documentation
516    "Construct method entry functions.
517
518    Builds the effective method function (if there is one) and the necessary
519    method entries.  Returns a list of functions (i.e., `function-inst'
520    objects) which need to be defined in the generated source code."))
521
522 ;;;--------------------------------------------------------------------------
523 ;;; Invoking direct methods.
524
525 (export 'invoke-delegation-chain)
526 (defun invoke-delegation-chain (codegen target basic-tail chain kernel)
527   "Invoke a chain of delegating methods.
528
529    CODEGEN is a `method-codegen'.  BASIC-TAIL is a list of argument
530    expressions to provide to the methods.  The result of the delegation chain
531    will be delivered to TARGET.
532
533    The CHAIN is a list of method objects (it's intended to be used with
534    `delegating-direct-method' objects).  The behaviour is as follows.  The
535    first method in the chain is invoked with the necessary arguments (see
536    below) including a `next_method' pointer.  If KERNEL is nil and there are
537    no more methods in the chain then the `next_method' pointer will be null;
538    otherwise it will point to a `trampoline' function, whose behaviour is to
539    call the remaining methods on the chain as a delegation chain.  The method
540    may choose to call this function with its arguments.  It will finally
541    return a value, which will be delivered to the TARGET.
542
543    If the chain is empty, then the code generated by KERNEL (given a TARGET
544    argument) will be invoked.  It is an error if both CHAIN and KERNEL are
545    nil."
546
547   (let* ((message (codegen-message codegen))
548          (argument-tail (if (varargs-message-p message)
549                             (cons *sod-tmp-ap* basic-tail)
550                             basic-tail)))
551     (labels ((next-trampoline (method chain)
552                (if (or kernel chain)
553                    (make-trampoline codegen (sod-method-class method)
554                                     (lambda (target)
555                                       (invoke chain target)))
556                    *null-pointer*))
557              (invoke (chain target)
558                (if (null chain)
559                    (funcall kernel target)
560                    (let ((trampoline (next-trampoline (car chain)
561                                                       (cdr chain)))
562                          (tail (if (keyword-message-p message)
563                                    (cons (keyword-struct-pointer)
564                                          argument-tail)
565                                    argument-tail)))
566                      (invoke-method codegen target
567                                     (cons trampoline tail)
568                                     (car chain))))))
569       (invoke chain target))))
570
571 ;;;----- That's all, folks --------------------------------------------------