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