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