doc/runtime.tex, lib/sod.3: Restructure the runtime library reference.
[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
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
7f2917d2
MW
72 `sod-message-effective-method-class', passing the MESSAGE, the CLASS and
73 the list of applicable methods as initargs to `make-instance'."))
dea4d055
MW
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 ()
4b8e5c03
MW
85 ((%method :initarg :method :type effective-method
86 :reader method-entry-effective-method)
dea4d055
MW
87 (chain-head :initarg :chain-head :type sod-class
88 :reader method-entry-chain-head)
89 (chain-tail :initarg :chain-tail :type sod-class
b426ab51 90 :reader method-entry-chain-tail)
8e45f824 91 (role :initarg :role :type (or keyword null) :reader method-entry-role))
dea4d055
MW
92 (:documentation
93 "An entry point into an effective method.
94
b426ab51
MW
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.
dea4d055
MW
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
b426ab51
MW
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
bf8aadd7
MW
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.
b426ab51 114
dea4d055
MW
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
b426ab51
MW
121(export 'make-method-entries)
122(defgeneric make-method-entries (effective-method chain-head chain-tail)
dea4d055 123 (:documentation
b426ab51
MW
124 "Return a list of `method-entry' objects for an EFFECTIVE-METHOD called
125 via CHAIN-HEAD.
dea4d055
MW
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
dea4d055
MW
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
43073476
MW
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
dea4d055
MW
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
b426ab51
MW
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'.")
bf8aadd7
MW
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)))
b426ab51 206
dea4d055
MW
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
43073476
MW
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."))
dea4d055
MW
218
219;;;--------------------------------------------------------------------------
220;;; Code generation.
221
222;;; Enhanced code-generator class.
223
224(export '(method-codegen codegen-message codegen-class
225 codegen-method codegen-target))
226(defclass method-codegen (codegen)
227 ((message :initarg :message :type sod-message :reader codegen-message)
4b8e5c03
MW
228 (%class :initarg :class :type sod-class :reader codegen-class)
229 (%method :initarg :method :type effective-method :reader codegen-method)
dea4d055
MW
230 (target :initarg :target :reader codegen-target))
231 (:documentation
232 "Augments CODEGEN with additional state regarding an effective method.
233
234 We store the effective method, and also its target class and owning
235 message, so that these values are readily available to the code-generating
236 functions."))
237
238;;; Protocol.
239
240(export 'compute-effective-method-body)
241(defgeneric compute-effective-method-body (method codegen target)
242 (:documentation
243 "Generates the body of an effective method.
244
245 Writes the function body to the code generator. It can (obviously)
246 generate auxiliary functions if it needs to.
247
43073476
MW
248 The arguments are as determined by agreement with the generic function
249 `compute-method-entry-functions'; usually this will be as specified by the
250 `sod-message-argument-tail', with any variable-argument tail reified to a
251 `va_list', and an additional argument `sod__obj' of type pointer-to-
252 ilayout. The code should deliver the result (if any) to the TARGET."))
dea4d055
MW
253
254(export 'simple-method-body)
255(defgeneric simple-method-body (method codegen target)
256 (:documentation
257 "Generate the body of a simple effective method.
258
259 The function is invoked on an effective METHOD, with a CODEGEN to which it
260 should emit code delivering the method's value to TARGET."))
261
262;;; Additional instructions.
263
4b8e5c03
MW
264;; HACK: use gensyms for the `class' and `expr' slots to avoid leaking the
265;; slot names, because `expr' is exported by our package, and `class' is
266;; actually from the `common-lisp' package.
267(definst convert-to-ilayout (stream :export t)
268 (#1=#:class chain-head #2=#:expr)
dea4d055 269 (format stream "SOD_ILAYOUT(~@<~A, ~_~A, ~_~A~:>)"
4b8e5c03 270 #1# (sod-class-nickname chain-head) #2#))
dea4d055
MW
271
272;;; Utilities.
273
43073476
MW
274(defvar *keyword-struct-disposition* :unset
275 "The current state of the keyword structure.
276
277 This can be one of four values.
278
279 * `:unset' -- the top-level default, mostly because I can't leave it
280 unbound and write this documentation. Nothing that matters should see
281 this state.
282
283 * `:local' -- the structure itself is in a local variable `sod__kw'.
284 This is used in the top-level effective method.
285
286 * `:pointer' -- the structure is pointed to by the local variable
287 `sod__kw'. This is used by delegation-chain trampolines.
288
289 * `:null' -- there is in fact no structure because none of the
290 applicable methods actually define any keywords.")
291
292(defun keyword-access (name &optional suffix)
293 "Return an lvalue designating a named member of the keyword struct.
294
295 If a non-nil SUFFIX is provided, then the member is named NAMESUFFIX."
296 (flet ((mem (op)
297 (format nil "~A~A~A~@[~A~]" *sod-keywords* op name suffix)))
298 (ecase *keyword-struct-disposition*
299 (:local (mem "."))
300 (:pointer (mem "->")))))
301
302(let ((kw-addr (format nil "&~A" *sod-keywords*)))
303 (defun keyword-struct-pointer ()
304 "Return a pointer to the keyword structure."
305 (ecase *keyword-struct-disposition*
306 (:local kw-addr)
307 (:pointer *sod-keywords*)
308 (:null *null-pointer*))))
309
dea4d055
MW
310(export 'invoke-method)
311(defun invoke-method (codegen target arguments-tail direct-method)
312 "Emit code to invoke DIRECT-METHOD, passing it ARGUMENTS-TAIL.
313
314 The code is generated in the context of CODEGEN, which can be any instance
315 of the `codegen' class -- it needn't be an instance of `method-codegen'.
316 The DIRECT-METHOD is called with the given ARGUMENTS-TAIL (a list of
317 argument expressions), preceded by a `me' argument of type pointer-to-
318 CLASS where CLASS is the class on which the method was defined.
319
320 If the message accepts a variable-length argument list then a copy of the
2bbe0f1d 321 prevailing argument pointer is provided in place of the `:ellipsis'."
dea4d055
MW
322
323 (let* ((message (sod-method-message direct-method))
324 (class (sod-method-class direct-method))
325 (function (sod-method-function-name direct-method))
43073476
MW
326 (type (sod-method-type direct-method))
327 (keywordsp (keyword-message-p message))
328 (keywords (and keywordsp (c-function-keywords type)))
329 (arguments (append (list (format nil "&sod__obj->~A.~A"
330 (sod-class-nickname
331 (sod-class-chain-head class))
332 (sod-class-nickname class)))
333 arguments-tail
334 (mapcar (lambda (arg)
335 (let ((name (argument-name arg))
336 (default (argument-default arg)))
337 (if default
338 (make-cond-inst
339 (keyword-access name
340 "__suppliedp")
341 (keyword-access name)
342 default)
343 (keyword-access name))))
344 keywords))))
345 (cond ((varargs-message-p message)
346 (convert-stmts codegen target (c-type-subtype type)
347 (lambda (var)
348 (ensure-var codegen *sod-tmp-ap* c-type-va-list)
349 (deliver-call codegen :void "va_copy"
350 *sod-tmp-ap* *sod-ap*)
351 (apply #'deliver-call codegen var
352 function arguments)
353 (deliver-call codegen :void "va_end"
354 *sod-tmp-ap*))))
355 (keywords
356 (let ((tag (direct-method-suppliedp-struct-tag direct-method)))
357 (with-temporary-var (codegen spvar (c-type (struct tag)))
358 (dolist (arg keywords)
359 (let ((name (argument-name arg)))
360 (deliver-expr codegen (format nil "~A.~A" spvar name)
361 (keyword-access name "__suppliedp"))))
362 (setf arguments (list* (car arguments) spvar
363 (cdr arguments)))
364 (apply #'deliver-call codegen target function arguments))))
365 (t
366 (apply #'deliver-call codegen target function arguments)))))
dea4d055
MW
367
368(export 'ensure-ilayout-var)
369(defun ensure-ilayout-var (codegen super)
370 "Define a variable `sod__obj' pointing to the class's ilayout structure.
371
372 CODEGEN is a `method-codegen'. The class in question is CODEGEN's class,
373 i.e., the target class for the effective method. SUPER is one of the
374 class's superclasses; it is assumed that `me' is a pointer to a SUPER
375 (i.e., to SUPER's ichain within the ilayout)."
376
377 (let* ((class (codegen-class codegen))
378 (super-head (sod-class-chain-head super)))
379 (ensure-var codegen "sod__obj"
380 (c-type (* (struct (ilayout-struct-tag class))))
381 (make-convert-to-ilayout-inst class super-head "me"))))
382
383(export 'make-trampoline)
384(defun make-trampoline (codegen super body)
385 "Construct a trampoline function and return its name.
386
387 CODEGEN is a `method-codegen'. SUPER is a superclass of the CODEGEN
388 class. We construct a new trampoline function (with an unimaginative
389 name) suitable for being passed to a direct method defined on SUPER as its
390 `next_method'. In particular, it will have a `me' argument whose type is
391 pointer-to-SUPER.
392
393 The code of the function is generated by BODY, which will be invoked with
394 a single argument which is the TARGET to which it should deliver its
395 result.
396
397 The return value is the name of the generated function."
398
399 (let* ((message (codegen-message codegen))
400 (message-type (sod-message-type message))
7de8c666
MW
401 (message-class (sod-message-class message))
402 (method (codegen-method codegen))
dea4d055 403 (return-type (c-type-subtype message-type))
f5d75f56 404 (raw-args (sod-message-argument-tail message))
43073476
MW
405 (arguments (cond ((varargs-message-p message)
406 (cons (make-argument *sod-ap* c-type-va-list)
407 (butlast raw-args)))
408 ((keyword-message-p message)
409 (cons (make-argument *sod-key-pointer*
410 (c-type (* (void :const))))
411 raw-args))))
412 (*keyword-struct-disposition* t))
dea4d055
MW
413 (codegen-push codegen)
414 (ensure-ilayout-var codegen super)
43073476
MW
415 (when (and (keyword-message-p message)
416 (not (eq *keyword-struct-disposition* :null)))
417 (let ((tag (effective-method-keyword-struct-tag method)))
418 (ensure-var codegen *sod-keywords* (c-type (* (struct tag :const)))
419 *sod-key-pointer*)))
dea4d055
MW
420 (funcall body (codegen-target codegen))
421 (codegen-pop-function codegen (temporary-function)
422 (c-type (fun (lisp return-type)
423 ("me" (* (class super)))
7de8c666
MW
424 . arguments))
425 "Delegation-chain trampoline ~:_~
426 for `~A.~A' ~:_on `~A'."
427 (sod-class-nickname message-class)
428 (sod-message-name message)
429 (effective-method-class method))))
dea4d055
MW
430
431;;;--------------------------------------------------------------------------
432;;; Method entry protocol.
433
434(export 'effective-method-function-name)
435(defgeneric effective-method-function-name (method)
436 (:documentation
437 "Returns the function name of an effective method."))
438
439(export 'method-entry-function-name)
b426ab51 440(defgeneric method-entry-function-name (method chain-head role)
dea4d055
MW
441 (:documentation
442 "Returns the function name of a method entry.
443
b426ab51
MW
444 The method entry is given as an effective method/chain-head/role triple,
445 rather than as a method entry object because we want the function name
446 before we've made the entry object."))
dea4d055
MW
447
448(export 'compute-method-entry-functions)
449(defgeneric compute-method-entry-functions (method)
450 (:documentation
451 "Construct method entry functions.
452
453 Builds the effective method function (if there is one) and the necessary
454 method entries. Returns a list of functions (i.e., `function-inst'
455 objects) which need to be defined in the generated source code."))
456
457;;;--------------------------------------------------------------------------
458;;; Invoking direct methods.
459
460(export 'invoke-delegation-chain)
461(defun invoke-delegation-chain (codegen target basic-tail chain kernel)
462 "Invoke a chain of delegating methods.
463
464 CODEGEN is a `method-codegen'. BASIC-TAIL is a list of argument
465 expressions to provide to the methods. The result of the delegation chain
466 will be delivered to TARGET.
467
468 The CHAIN is a list of method objects (it's intended to be used with
469 `delegating-direct-method' objects). The behaviour is as follows. The
470 first method in the chain is invoked with the necessary arguments (see
471 below) including a `next_method' pointer. If KERNEL is nil and there are
472 no more methods in the chain then the `next_method' pointer will be null;
473 otherwise it will point to a `trampoline' function, whose behaviour is to
474 call the remaining methods on the chain as a delegation chain. The method
475 may choose to call this function with its arguments. It will finally
476 return a value, which will be delivered to the TARGET.
477
478 If the chain is empty, then the code generated by KERNEL (given a TARGET
479 argument) will be invoked. It is an error if both CHAIN and KERNEL are
480 nil."
481
482 (let* ((message (codegen-message codegen))
43073476
MW
483 (argument-tail (cond ((varargs-message-p message)
484 (cons *sod-tmp-ap* basic-tail))
485 ((keyword-message-p message)
486 (cons (keyword-struct-pointer) basic-tail))
487 (t basic-tail))))
dea4d055
MW
488 (labels ((next-trampoline (method chain)
489 (if (or kernel chain)
490 (make-trampoline codegen (sod-method-class method)
491 (lambda (target)
492 (invoke chain target)))
944caf84 493 *null-pointer*))
dea4d055
MW
494 (invoke (chain target)
495 (if (null chain)
496 (funcall kernel target)
bf090e02
MW
497 (let ((trampoline (next-trampoline (car chain)
498 (cdr chain))))
dea4d055
MW
499 (invoke-method codegen target
500 (cons trampoline argument-tail)
501 (car chain))))))
502 (invoke chain target))))
503
504;;;----- That's all, folks --------------------------------------------------