chiark / gitweb /
78429ef93f5e4a7b6c538b003a1cfe162ca28db7
[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 Sensble 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 effective-method-message effective-method-class))
32 (defclass effective-method ()
33   ((message :initarg :message :type sod-message
34             :reader effective-method-message)
35    (class :initarg :class :type sod-class :reader effective-method-class))
36   (:documentation
37    "The behaviour invoked by sending a message to an instance of a class.
38
39    This class describes the behaviour when an instance of CLASS is sent
40    MESSAGE.
41
42    This is not a useful class by itself.  Message classes are expected to
43    define their own effective-method classes.
44
45    An effective method class must accept a `:direct-methods' initarg, which
46    will be a list of applicable methods sorted in most-to-least specific
47    order.  (Either that or you have to add an overriding method to
48    `compute-sod-effective-method'."))
49
50 (export 'message-effective-method-class)
51 (defgeneric message-effective-method-class (message)
52   (:documentation
53    "Return the effective method class for the given MESSAGE.
54
55    This function is invoked by `compute-sod-effective-method'."))
56
57 (export 'primary-method-class)
58 (defgeneric primary-method-class (message)
59   (:documentation
60    "Return the name of the primary direct method class for MESSAGE.
61
62    This protocol is used by `simple-message' subclasses."))
63
64 (export 'compute-sod-effective-method)
65 (defgeneric compute-sod-effective-method (message class)
66   (:documentation
67    "Return the effective method when a CLASS instance receives MESSAGE.
68
69    The default method constructs an instance of the message's chosen
70    `message-effective-method-class', passing the MESSAGE, the CLASS and the
71    list of applicable methods as initargs to `make-instance'."))
72
73 (export 'compute-effective-methods)
74 (defgeneric compute-effective-methods (class)
75   (:documentation
76    "Return a list of all of the effective methods needed for CLASS.
77
78    The list needn't be in any particular order."))
79
80 (export '(method-entry method-entry-effective-method
81           method-entry-chain-head method-entry-chain-tail))
82 (defclass method-entry ()
83   ((method :initarg :method :type effective-method
84            :reader method-entry-effective-method)
85    (chain-head :initarg :chain-head :type sod-class
86                :reader method-entry-chain-head)
87    (chain-tail :initarg :chain-tail :type sod-class
88                :reader method-entry-chain-tail)
89    (role :initarg :role :type (or :keyword null) :reader method-entry-role))
90   (:documentation
91    "An entry point into an effective method.
92
93    Specifically, this is the entry point to the effective METHOD invoked via
94    the vtable for the chain headed by CHAIN-HEAD, and serving the given ROLE.
95    The CHAIN-TAIL is the most specific class on this chain; this is useful
96    because we can reuse the types of method entries from superclasses on
97    non-primary chains.
98
99    Each effective method may have several different method entries, because
100    an effective method can be called via vtables attached to different
101    chains, and such calls will pass instance pointers which point to
102    different `ichain' structures within the overall instance layout; it's the
103    job of the method entry to adjust the instance pointers correctly for the
104    rest of the effective method.
105
106    A vtable can contain more than one entry for the same message.  Such
107    entries are distinguished by their roles.  A message always has an entry
108    with the `nil role.  No other roles are currently defined, though they may
109    be introduced by extensions.
110
111    The boundaries between a method entry and the effective method
112    is (intentionally) somewhat fuzzy.  In extreme cases, the effective method
113    may not exist at all as a distinct entity in the output because its
114    content is duplicated in all of the method entry functions.  This is left
115    up to the effective method protocol."))
116
117 (export 'make-method-entries)
118 (defgeneric make-method-entries (effective-method chain-head chain-tail)
119   (:documentation
120    "Return a list of `method-entry' objects for an EFFECTIVE-METHOD called
121    via CHAIN-HEAD.
122
123    There is no default method for this function.  (Maybe when the
124    effective-method/method-entry output protocol has settled down I'll know
125    what a sensible default action would be.)"))
126
127 ;;;--------------------------------------------------------------------------
128 ;;; Protocol for messages and direct-methods.
129
130 (export 'sod-message-argument-tail)
131 (defgeneric sod-message-argument-tail (message)
132   (:documentation
133    "Return the argument tail for the message, with invented argument names.
134
135    No `me' argument is prepended; any `:ellipsis' is left as it is."))
136
137 (export 'sod-message-no-varargs-tail)
138 (defgeneric sod-message-no-varargs-tail (message)
139   (:documentation
140    "Return the argument tail for the message with `:ellipsis' substituted.
141
142    As with `sod-message-argument-tail', no `me' argument is prepended.
143    However, an `:ellipsis' is replaced by an argument of type `va_list',
144    named `sod__ap'."))
145
146 (export 'sod-method-function-type)
147 (defgeneric sod-method-function-type (method)
148   (:documentation
149    "Return the C function type for the direct method.
150
151    This is called during initialization of a direct method object, and the
152    result is cached.
153
154    A default method is provided (by `basic-direct-method') which simply
155    prepends an appropriate `me' argument to the user-provided argument list.
156    Fancy method classes may need to override this behaviour."))
157
158 (export 'sod-method-next-method-type)
159 (defgeneric sod-method-next-method-type (method)
160   (:documentation
161    "Return the C function type for the next-method trampoline.
162
163    This is called during initialization of a direct method object, and the
164    result is cached.  It should return a function type, not a pointer type.
165
166    A default method is provided (by `delegating-direct-method') which should
167    do the right job.  Very fancy subclasses might need to do something
168    different."))
169
170 (export 'sod-method-function-name)
171 (defgeneric sod-method-function-name (method)
172   (:documentation
173    "Return the C function name for the direct method."))
174
175 (export 'varargs-message-p)
176 (defun varargs-message-p (message)
177   "Answer whether the MESSAGE accepts a variable-length argument list.
178
179    We need to jump through some extra hoops in order to cope with varargs
180    messages, so this is useful to know."
181   (member :ellipsis (sod-message-argument-tail message)))
182
183 ;;;--------------------------------------------------------------------------
184 ;;; Protocol for effective methods and method entries.
185
186 (export 'method-entry-function-type)
187 (defgeneric method-entry-function-type (entry)
188   (:documentation
189    "Return the C function type for a method entry."))
190
191 (export 'method-entry-slot-name)
192 (defgeneric method-entry-slot-name (entry)
193   (:documentation
194    "Return the `vtmsgs' slot name for a method entry.
195
196    The default method indirects through `method-entry-slot-name-by-role'."))
197
198 (defgeneric method-entry-slot-name-by-role (entry role name)
199   (:documentation "Easier implementation for `method-entry-slot-name'.")
200   (:method ((entry method-entry) (role (eql nil)) name) name))
201
202 (export 'effective-method-basic-argument-names)
203 (defgeneric effective-method-basic-argument-names (method)
204   (:documentation
205    "Return a list of argument names to be passed to direct methods.
206
207    The argument names are constructed from the message's arguments returned
208    by `sod-message-no-varargs-tail'.  The basic arguments are the ones
209    immediately derived from the programmer's explicitly stated arguments; the
210    `me' argument is not included, and neither are more exotic arguments added
211    as part of the method delegation protocol."))
212
213 ;;;--------------------------------------------------------------------------
214 ;;; Code generation.
215
216 ;;; Enhanced code-generator class.
217
218 (export '(method-codegen codegen-message codegen-class
219           codegen-method codegen-target))
220 (defclass method-codegen (codegen)
221   ((message :initarg :message :type sod-message :reader codegen-message)
222    (class :initarg :class :type sod-class :reader codegen-class)
223    (method :initarg :method :type effective-method :reader codegen-method)
224    (target :initarg :target :reader codegen-target))
225   (:documentation
226    "Augments CODEGEN with additional state regarding an effective method.
227
228    We store the effective method, and also its target class and owning
229    message, so that these values are readily available to the code-generating
230    functions."))
231
232 ;;; Protocol.
233
234 (export 'compute-effective-method-body)
235 (defgeneric compute-effective-method-body (method codegen target)
236   (:documentation
237    "Generates the body of an effective method.
238
239    Writes the function body to the code generator.  It can (obviously)
240    generate auxiliary functions if it needs to.
241
242    The arguments are as specified by the `sod-message-no-varargs-tail', with
243    an additional argument `sod__obj' of type pointer-to-ilayout.  The code
244    should deliver the result (if any) to the TARGET."))
245
246 (export 'simple-method-body)
247 (defgeneric simple-method-body (method codegen target)
248   (:documentation
249    "Generate the body of a simple effective method.
250
251    The function is invoked on an effective METHOD, with a CODEGEN to which it
252    should emit code delivering the method's value to TARGET."))
253
254 ;;; Additional instructions.
255
256 (export 'convert-to-ilayout)
257 (definst convert-to-ilayout (stream) (class chain-head expr)
258   (format stream "SOD_ILAYOUT(~@<~A, ~_~A, ~_~A~:>)"
259           class (sod-class-nickname chain-head) expr))
260
261 ;;; Utilities.
262
263 (export 'invoke-method)
264 (defun invoke-method (codegen target arguments-tail direct-method)
265   "Emit code to invoke DIRECT-METHOD, passing it ARGUMENTS-TAIL.
266
267    The code is generated in the context of CODEGEN, which can be any instance
268    of the `codegen' class -- it needn't be an instance of `method-codegen'.
269    The DIRECT-METHOD is called with the given ARGUMENTS-TAIL (a list of
270    argument expressions), preceded by a `me' argument of type pointer-to-
271    CLASS where CLASS is the class on which the method was defined.
272
273    If the message accepts a variable-length argument list then a copy of the
274    prevailing argument pointer is provided in place of the `:ellipsis'."
275
276   (let* ((message (sod-method-message direct-method))
277          (class (sod-method-class direct-method))
278          (function (sod-method-function-name direct-method))
279          (arguments (cons (format nil "&sod__obj->~A.~A"
280                                   (sod-class-nickname
281                                    (sod-class-chain-head class))
282                                   (sod-class-nickname class))
283                           arguments-tail)))
284     (if (varargs-message-p message)
285         (convert-stmts codegen target
286                        (c-type-subtype (sod-method-type direct-method))
287                        (lambda (var)
288                          (ensure-var codegen *sod-tmp-ap* (c-type va-list))
289                          (emit-inst codegen
290                                     (make-va-copy-inst *sod-tmp-ap*
291                                                        *sod-ap*))
292                          (deliver-expr codegen var
293                                        (make-call-inst function arguments))
294                          (emit-inst codegen
295                                     (make-va-end-inst *sod-tmp-ap*))))
296         (deliver-expr codegen target (make-call-inst function arguments)))))
297
298 (export 'ensure-ilayout-var)
299 (defun ensure-ilayout-var (codegen super)
300   "Define a variable `sod__obj' pointing to the class's ilayout structure.
301
302    CODEGEN is a `method-codegen'.  The class in question is CODEGEN's class,
303    i.e., the target class for the effective method.  SUPER is one of the
304    class's superclasses; it is assumed that `me' is a pointer to a SUPER
305    (i.e., to SUPER's ichain within the ilayout)."
306
307   (let* ((class (codegen-class codegen))
308          (super-head (sod-class-chain-head super)))
309     (ensure-var codegen "sod__obj"
310                 (c-type (* (struct (ilayout-struct-tag class))))
311                 (make-convert-to-ilayout-inst class super-head "me"))))
312
313 (export 'make-trampoline)
314 (defun make-trampoline (codegen super body)
315   "Construct a trampoline function and return its name.
316
317    CODEGEN is a `method-codegen'.  SUPER is a superclass of the CODEGEN
318    class.  We construct a new trampoline function (with an unimaginative
319    name) suitable for being passed to a direct method defined on SUPER as its
320    `next_method'.  In particular, it will have a `me' argument whose type is
321    pointer-to-SUPER.
322
323    The code of the function is generated by BODY, which will be invoked with
324    a single argument which is the TARGET to which it should deliver its
325    result.
326
327    The return value is the name of the generated function."
328
329   (let* ((message (codegen-message codegen))
330          (message-type (sod-message-type message))
331          (return-type (c-type-subtype message-type))
332          (raw-args (sod-message-argument-tail message))
333          (arguments (if (varargs-message-p message)
334                         (cons (make-argument *sod-ap*
335                                              (c-type va-list))
336                               (butlast raw-args))
337                         raw-args)))
338     (codegen-push codegen)
339     (ensure-ilayout-var codegen super)
340     (funcall body (codegen-target codegen))
341     (codegen-pop-function codegen (temporary-function)
342                           (c-type (fun (lisp return-type)
343                                        ("me" (* (class super)))
344                                        . arguments)))))
345
346 ;;;--------------------------------------------------------------------------
347 ;;; Method entry protocol.
348
349 (export 'effective-method-function-name)
350 (defgeneric effective-method-function-name (method)
351   (:documentation
352    "Returns the function name of an effective method."))
353
354 (export 'method-entry-function-name)
355 (defgeneric method-entry-function-name (method chain-head role)
356   (:documentation
357    "Returns the function name of a method entry.
358
359    The method entry is given as an effective method/chain-head/role triple,
360    rather than as a method entry object because we want the function name
361    before we've made the entry object."))
362
363 (export 'compute-method-entry-functions)
364 (defgeneric compute-method-entry-functions (method)
365   (:documentation
366    "Construct method entry functions.
367
368    Builds the effective method function (if there is one) and the necessary
369    method entries.  Returns a list of functions (i.e., `function-inst'
370    objects) which need to be defined in the generated source code."))
371
372 ;;;--------------------------------------------------------------------------
373 ;;; Invoking direct methods.
374
375 (export 'invoke-delegation-chain)
376 (defun invoke-delegation-chain (codegen target basic-tail chain kernel)
377   "Invoke a chain of delegating methods.
378
379    CODEGEN is a `method-codegen'.  BASIC-TAIL is a list of argument
380    expressions to provide to the methods.  The result of the delegation chain
381    will be delivered to TARGET.
382
383    The CHAIN is a list of method objects (it's intended to be used with
384    `delegating-direct-method' objects).  The behaviour is as follows.  The
385    first method in the chain is invoked with the necessary arguments (see
386    below) including a `next_method' pointer.  If KERNEL is nil and there are
387    no more methods in the chain then the `next_method' pointer will be null;
388    otherwise it will point to a `trampoline' function, whose behaviour is to
389    call the remaining methods on the chain as a delegation chain.  The method
390    may choose to call this function with its arguments.  It will finally
391    return a value, which will be delivered to the TARGET.
392
393    If the chain is empty, then the code generated by KERNEL (given a TARGET
394    argument) will be invoked.  It is an error if both CHAIN and KERNEL are
395    nil."
396
397   (let* ((message (codegen-message codegen))
398          (argument-tail (if (varargs-message-p message)
399                             (cons *sod-tmp-ap* basic-tail)
400                             basic-tail)))
401     (labels ((next-trampoline (method chain)
402                (if (or kernel chain)
403                    (make-trampoline codegen (sod-method-class method)
404                                     (lambda (target)
405                                       (invoke chain target)))
406                    0))
407              (invoke (chain target)
408                (if (null chain)
409                    (funcall kernel target)
410                    (let ((trampoline (next-trampoline (car chain)
411                                                       (cdr chain))))
412                      (invoke-method codegen target
413                                     (cons trampoline argument-tail)
414                                     (car chain))))))
415       (invoke chain target))))
416
417 ;;;----- That's all, folks --------------------------------------------------