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