3 ;;; Method combination protocol
5 ;;; (c) 2009 Straylight/Edgeware
8 ;;;----- Licensing notice ---------------------------------------------------
10 ;;; This file is part of the Sensible Object Design, an object system for C.
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.
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.
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.
28 ;;;--------------------------------------------------------------------------
29 ;;; Effective methods and entries.
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))
40 "The behaviour invoked by sending a message to an instance of a class.
42 This class describes the behaviour when an instance of CLASS is sent
45 This is not a useful class by itself. Message classes are expected to
46 define their own effective-method classes.
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
52 (export 'sod-message-receiver-type)
53 (defgeneric sod-message-receiver-type (message class)
55 "Return the type of the `me' argument in a MESSAGE received by CLASS.
57 Typically this will just be `CLASS *' or `const CLASS *'."))
59 (export 'sod-message-applicable-methods)
60 (defgeneric sod-message-applicable-methods (message class)
62 "Return a list of applicable methods for a MESSAGE.
64 The list contains all methods applicable for MESSAGE when sent to an
65 instance of CLASS, most specific first."))
67 (export 'sod-message-keyword-argument-lists)
68 (defgeneric sod-message-keyword-argument-lists
69 (message class direct-methods state)
71 "Returns a list of keyword argument lists to be merged.
73 This should return a list suitable for passing to `merge-keyword-lists',
74 i.e., each element should be a pair consisting of a function describing
75 the source of the argument list (returning location and description), and
76 a list of `argument' objects.
78 The MESSAGE is the message being processed; CLASS is a receiver class
79 under consideration; DIRECT-METHODS is the complete list of applicable
80 direct methods (most specific first); and STATE is an `inheritance-path-
81 reporter-state' object which can be used by the returned reporting
84 (export 'compute-effective-method-keyword-arguments)
85 (defun compute-effective-method-keyword-arguments
86 (message class direct-methods)
87 "Return a merged keyword argument list.
89 The returned list combines all of the applicable methods, provided as
90 DIRECT-METHODS, applicable to MESSAGE when received by an instance of
91 CLASS, possibly with other keywords as determined by `sod-keyword-
93 (let ((state (make-inheritance-path-reporter-state class)))
94 (merge-keyword-lists (lambda ()
97 "methods for message `~A' ~
98 applicable to class `~A'"
100 (sod-message-keyword-argument-lists message
105 (export 'sod-message-check-methods)
106 (defgeneric sod-message-check-methods (message class direct-methods)
108 "Check that the applicable methods for a MESSAGE are compatible.
110 Specifically, given the DIRECT-METHODS applicable for the message when
111 received by an instance of CLASS, signal errors if the methods don't
112 match the MESSAGE or each other."))
114 (export 'sod-message-effective-method-class)
115 (defgeneric sod-message-effective-method-class (message)
117 "Return the effective method class for the given MESSAGE.
119 This function is invoked by `compute-sod-effective-method'."))
121 (export 'primary-method-class)
122 (defgeneric primary-method-class (message)
124 "Return the name of the primary direct method class for MESSAGE.
126 This protocol is used by `simple-message' subclasses."))
128 (export 'compute-sod-effective-method)
129 (defgeneric compute-sod-effective-method (message class)
131 "Return the effective method when a CLASS instance receives MESSAGE.
133 The default method constructs an instance of the message's chosen
134 `sod-message-effective-method-class', passing the MESSAGE, the CLASS and
135 the list of applicable methods as initargs to `make-instance'."))
137 (export 'compute-effective-methods)
138 (defgeneric compute-effective-methods (class)
140 "Return a list of all of the effective methods needed for CLASS.
142 The list needn't be in any particular order."))
144 (export '(method-entry method-entry-effective-method
145 method-entry-chain-head method-entry-chain-tail
147 (defclass method-entry ()
148 ((%method :initarg :method :type effective-method
149 :reader method-entry-effective-method)
150 (chain-head :initarg :chain-head :type sod-class
151 :reader method-entry-chain-head)
152 (chain-tail :initarg :chain-tail :type sod-class
153 :reader method-entry-chain-tail)
154 (role :initarg :role :type (or keyword null) :reader method-entry-role))
156 "An entry point into an effective method.
158 Specifically, this is the entry point to the effective METHOD invoked via
159 the vtable for the chain headed by CHAIN-HEAD, and serving the given ROLE.
160 The CHAIN-TAIL is the most specific class on this chain; this is useful
161 because we can reuse the types of method entries from superclasses on
164 Each effective method may have several different method entries, because
165 an effective method can be called via vtables attached to different
166 chains, and such calls will pass instance pointers which point to
167 different `ichain' structures within the overall instance layout; it's the
168 job of the method entry to adjust the instance pointers correctly for the
169 rest of the effective method.
171 A vtable can contain more than one entry for the same message. Such
172 entries are distinguished by their roles. A message always has an entry
173 with the `nil role; in addition, a varargs message also has a `:valist'
174 role, which accepts a `va_list' argument in place of the variable argument
175 listNo other roles are currently defined, though they may be introduced by
178 The boundaries between a method entry and the effective method
179 is (intentionally) somewhat fuzzy. In extreme cases, the effective method
180 may not exist at all as a distinct entity in the output because its
181 content is duplicated in all of the method entry functions. This is left
182 up to the effective method protocol."))
184 (export 'make-method-entries)
185 (defgeneric make-method-entries (effective-method chain-head chain-tail)
187 "Return a list of `method-entry' objects for an EFFECTIVE-METHOD called
190 There is no default method for this function. (Maybe when the
191 effective-method/method-entry output protocol has settled down I'll know
192 what a sensible default action would be.)"))
194 ;;;--------------------------------------------------------------------------
195 ;;; Protocol for messages and direct-methods.
197 (export 'sod-message-argument-tail)
198 (defgeneric sod-message-argument-tail (message)
200 "Return the argument tail for the message, with invented argument names.
202 No `me' argument is prepended; any `:ellipsis' is left as it is."))
204 (export 'sod-method-description)
205 (defgeneric sod-method-description (method)
207 "Return an adjectival phrase describing METHOD.
209 The result will be placed into an error message reading something like
210 ``Conflicting definition of DESCRIPTION direct method `bogus'''. Two
211 direct methods which can coexist in the same class, defined on the same
212 message, should have differing descriptions."))
214 (export 'sod-method-function-type)
215 (defgeneric sod-method-function-type (method)
217 "Return the C function type for the direct method.
219 This is called during initialization of a direct method object, and the
222 A default method is provided (by `basic-direct-method') which simply
223 prepends an appropriate `me' argument to the user-provided argument list.
224 Fancy method classes may need to override this behaviour."))
226 (export 'sod-method-next-method-type)
227 (defgeneric sod-method-next-method-type (method)
229 "Return the C function type for the next-method trampoline.
231 This is called during initialization of a direct method object, and the
232 result is cached. It should return a function type, not a pointer type.
234 A default method is provided (by `delegating-direct-method') which should
235 do the right job. Very fancy subclasses might need to do something
238 (export 'sod-method-function-name)
239 (defgeneric sod-method-function-name (method)
241 "Return the C function name for the direct method."))
243 (export 'keyword-message-p)
244 (defun keyword-message-p (message)
245 "Answer whether the MESSAGE accepts a keyword arguments.
247 Dealing with keyword messages is rather fiddly, so this is useful to
249 (typep (sod-message-type message) 'c-keyword-function-type))
251 (export 'varargs-message-p)
252 (defun varargs-message-p (message)
253 "Answer whether the MESSAGE accepts a variable-length argument list.
255 We need to jump through some extra hoops in order to cope with varargs
256 messages, so this is useful to know."
257 (member :ellipsis (sod-message-argument-tail message)))
259 ;;;--------------------------------------------------------------------------
260 ;;; Protocol for effective methods and method entries.
262 (export 'method-entry-function-type)
263 (defgeneric method-entry-function-type (entry)
265 "Return the C function type for a method entry."))
267 (export 'method-entry-slot-name)
268 (defgeneric method-entry-slot-name (entry)
270 "Return the `vtmsgs' slot name for a method entry.
272 The default method indirects through `method-entry-slot-name-by-role'."))
274 (export 'method-entry-slot-name-by-role)
275 (defgeneric method-entry-slot-name-by-role (entry role name)
276 (:documentation "Easier implementation for `method-entry-slot-name'.")
277 (:method ((entry method-entry) (role (eql nil)) name) name)
278 (:method ((entry method-entry) (role (eql :valist)) name)
279 (format nil "~A__v" name)))
281 (export 'effective-method-basic-argument-names)
282 (defgeneric effective-method-basic-argument-names (method)
284 "Return a list of argument names to be passed to direct methods.
286 The argument names are constructed from the message's arguments returned
287 by `sod-message-argument-tail', with any ellipsis replaced by an explicit
288 `va_list' argument. The basic arguments are the ones immediately derived
289 from the programmer's explicitly stated arguments; the `me' argument is
290 not included, and neither are more exotic arguments added as part of the
291 method delegation protocol."))
293 (export 'effective-method-live-p)
294 (defgeneric effective-method-live-p (method)
296 "Returns true if the effective METHOD is live.
298 An effective method is `live' if it should actually have proper method
299 entry functions associated with it and stored in the class vtable. The
300 other possibility is that the method is `dead', in which case the function
301 pointers in the vtable are left null."))
303 ;;;--------------------------------------------------------------------------
306 ;;; Enhanced code-generator class.
308 (export '(method-codegen codegen-message codegen-class
309 codegen-method codegen-target))
310 (defclass method-codegen (codegen)
311 ((message :initarg :message :type sod-message :reader codegen-message)
312 (%class :initarg :class :type sod-class :reader codegen-class)
313 (%method :initarg :method :type effective-method :reader codegen-method)
314 (target :initarg :target :reader codegen-target))
316 "Augments CODEGEN with additional state regarding an effective method.
318 We store the effective method, and also its target class and owning
319 message, so that these values are readily available to the code-generating
324 (export 'compute-effective-method-body)
325 (defgeneric compute-effective-method-body (method codegen target)
327 "Generates the body of an effective method.
329 Writes the function body to the code generator. It can (obviously)
330 generate auxiliary functions if it needs to.
332 The arguments are as determined by agreement with the generic function
333 `compute-method-entry-functions'; usually this will be as specified by the
334 `sod-message-argument-tail', with any variable-argument tail reified to a
335 `va_list', and an additional argument `sod__obj' of type pointer-to-
336 ilayout. The code should deliver the result (if any) to the TARGET."))
338 (export 'simple-method-body)
339 (defgeneric simple-method-body (method codegen target)
341 "Generate the body of a simple effective method.
343 The function is invoked on an effective METHOD, with a CODEGEN to which it
344 should emit code delivering the method's value to TARGET."))
346 ;;; Additional instructions.
348 (definst convert-to-ilayout (stream :export t)
349 (%class chain-head %expr)
350 "Expression to convert EXPR to point to its enclosing `ilayout'.
352 Given a pointer EXPR which points into a direct instance of CLASS,
353 specifically to the `ichain' whose head class is CHAIN-HEAD, evaluate the
354 base address of the enclosing `ilayout' structure.
356 The output looks like:
358 SOD_ILAYOUT(CLASS, NICK, EXPR)"
359 (format stream "SOD_ILAYOUT(~@<~A, ~_~A, ~_~A~:>)"
360 class (sod-class-nickname chain-head) expr))
364 (defvar-unbound *keyword-struct-disposition*
365 "The current state of the keyword structure.
367 This can be one of three values.
369 * `:local' -- the structure itself is in a local variable `sod__kw'.
370 This is used in the top-level effective method.
372 * `:pointer' -- the structure is pointed to by the local variable
373 `sod__kw'. This is used by delegation-chain trampolines.
375 * `:null' -- there is in fact no structure because none of the
376 applicable methods actually define any keywords.")
378 (defun keyword-access (name &optional suffix)
379 "Return an lvalue designating a named member of the keyword struct.
381 If a non-nil SUFFIX is provided, then the member is named NAMESUFFIX."
383 (format nil "~A~A~A~@[~A~]" *sod-keywords* op name suffix)))
384 (ecase *keyword-struct-disposition*
386 (:pointer (mem "->")))))
388 (let ((kw-addr (format nil "&~A" *sod-keywords*)))
389 (defun keyword-struct-pointer ()
390 "Return a pointer to the keyword structure."
391 (ecase *keyword-struct-disposition*
393 (:pointer *sod-keywords*)
394 (:null *null-pointer*))))
396 (export 'invoke-method)
397 (defun invoke-method (codegen target arguments-tail direct-method)
398 "Emit code to invoke DIRECT-METHOD, passing it ARGUMENTS-TAIL.
400 The code is generated in the context of CODEGEN, which can be any instance
401 of the `codegen' class -- it needn't be an instance of `method-codegen'.
402 The DIRECT-METHOD is called with the given ARGUMENTS-TAIL (a list of
403 argument expressions), preceded by a `me' argument of type pointer-to-
404 CLASS where CLASS is the class on which the method was defined.
406 If the message accepts a variable-length argument list then a copy of the
407 prevailing argument pointer is provided in place of the `:ellipsis'."
409 (let* ((message (sod-method-message direct-method))
410 (class (sod-method-class direct-method))
411 (function (sod-method-function-name direct-method))
412 (type (sod-method-type direct-method))
413 (keywordsp (keyword-message-p message))
414 (keywords (and keywordsp (c-function-keywords type)))
415 (arguments (append (list (format nil "&sod__obj->~A.~A"
417 (sod-class-chain-head class))
418 (sod-class-nickname class)))
420 (mapcar (lambda (arg)
421 (let ((name (argument-name arg))
422 (default (argument-default arg)))
427 (keyword-access name)
429 (keyword-access name))))
431 (cond ((varargs-message-p message)
432 (convert-stmts codegen target (c-type-subtype type)
434 (ensure-var codegen *sod-tmp-ap* c-type-va-list)
435 (deliver-call codegen :void "va_copy"
436 *sod-tmp-ap* *sod-ap*)
437 (apply #'deliver-call codegen var
439 (deliver-call codegen :void "va_end"
442 (let ((tag (direct-method-suppliedp-struct-tag direct-method)))
443 (with-temporary-var (codegen spvar (c-type (struct tag)))
444 (dolist (arg keywords)
445 (let ((name (argument-name arg)))
446 (deliver-expr codegen (format nil "~A.~A" spvar name)
447 (keyword-access name "__suppliedp"))))
448 (setf arguments (list* (car arguments) spvar
450 (apply #'deliver-call codegen target function arguments))))
452 (apply #'deliver-call codegen target function arguments)))))
454 (export 'ensure-ilayout-var)
455 (defun ensure-ilayout-var (codegen super)
456 "Define a variable `sod__obj' pointing to the class's ilayout structure.
458 CODEGEN is a `method-codegen'. The class in question is CODEGEN's class,
459 i.e., the target class for the effective method. SUPER is one of the
460 class's superclasses; it is assumed that `me' is a pointer to a SUPER
461 (i.e., to SUPER's ichain within the ilayout)."
463 (let* ((class (codegen-class codegen))
464 (super-head (sod-class-chain-head super)))
465 (ensure-var codegen "sod__obj"
466 (c-type (* (struct (ilayout-struct-tag class))))
467 (make-convert-to-ilayout-inst class super-head "me"))))
469 (export 'make-trampoline)
470 (defun make-trampoline (codegen super body)
471 "Construct a trampoline function and return its name.
473 CODEGEN is a `method-codegen'. SUPER is a superclass of the CODEGEN
474 class. We construct a new trampoline function (with an unimaginative
475 name) suitable for being passed to a direct method defined on SUPER as its
476 `next_method'. In particular, it will have a `me' argument whose type is
479 The code of the function is generated by BODY, which will be invoked with
480 a single argument which is the TARGET to which it should deliver its
483 The return value is the name of the generated function."
485 (let* ((message (codegen-message codegen))
486 (message-type (sod-message-type message))
487 (message-class (sod-message-class message))
488 (method (codegen-method codegen))
489 (return-type (c-type-subtype message-type))
490 (raw-args (sod-message-argument-tail message))
491 (arguments (cond ((varargs-message-p message)
492 (cons (make-argument *sod-ap* c-type-va-list)
494 ((keyword-message-p message)
495 (cons (make-argument *sod-key-pointer*
496 (c-type (* (void :const))))
499 (*keyword-struct-disposition* (if (effective-method-keywords method)
501 (codegen-push codegen)
502 (ensure-ilayout-var codegen super)
503 (deliver-call codegen :void "SOD__IGNORE" "sod__obj")
504 (when (keyword-message-p message)
505 (if (eq *keyword-struct-disposition* :null)
506 (deliver-call codegen :void "SOD__IGNORE" *sod-key-pointer*)
507 (let ((tag (effective-method-keyword-struct-tag method)))
508 (ensure-var codegen *sod-keywords*
509 (c-type (* (struct tag :const)))
510 *sod-key-pointer*))))
511 (funcall body (codegen-target codegen))
512 (codegen-pop-function codegen (temporary-function)
513 (c-type (fun (lisp return-type)
514 ("me" (* (class super)))
516 "Delegation-chain trampoline ~:_~
517 for `~A.~A' ~:_on `~A'."
518 (sod-class-nickname message-class)
519 (sod-message-name message)
520 (effective-method-class method))))
522 ;;;--------------------------------------------------------------------------
523 ;;; Method entry protocol.
525 (export 'effective-method-function-name)
526 (defgeneric effective-method-function-name (method)
528 "Returns the function name of an effective method."))
530 (export 'method-entry-function-name)
531 (defgeneric method-entry-function-name (method chain-head role)
533 "Returns the function name of a method entry.
535 The method entry is given as an effective method/chain-head/role triple,
536 rather than as a method entry object because we want the function name
537 before we've made the entry object."))
539 (export 'compute-method-entry-functions)
540 (defgeneric compute-method-entry-functions (method)
542 "Construct method entry functions.
544 Builds the effective method function (if there is one) and the necessary
545 method entries. Returns a list of functions (i.e., `function-inst'
546 objects) which need to be defined in the generated source code."))
548 ;;;--------------------------------------------------------------------------
549 ;;; Invoking direct methods.
551 (export 'invoke-delegation-chain)
552 (defun invoke-delegation-chain (codegen target basic-tail chain kernel)
553 "Invoke a chain of delegating methods.
555 CODEGEN is a `method-codegen'. BASIC-TAIL is a list of argument
556 expressions to provide to the methods. The result of the delegation chain
557 will be delivered to TARGET.
559 The CHAIN is a list of method objects (it's intended to be used with
560 `delegating-direct-method' objects). The behaviour is as follows. The
561 first method in the chain is invoked with the necessary arguments (see
562 below) including a `next_method' pointer. If KERNEL is nil and there are
563 no more methods in the chain then the `next_method' pointer will be null;
564 otherwise it will point to a `trampoline' function, whose behaviour is to
565 call the remaining methods on the chain as a delegation chain. The method
566 may choose to call this function with its arguments. It will finally
567 return a value, which will be delivered to the TARGET.
569 If the chain is empty, then the code generated by KERNEL (given a TARGET
570 argument) will be invoked. It is an error if both CHAIN and KERNEL are
573 (let* ((message (codegen-message codegen))
574 (argument-tail (if (varargs-message-p message)
575 (cons *sod-tmp-ap* basic-tail)
577 (labels ((next-trampoline (method chain)
578 (if (or kernel chain)
579 (make-trampoline codegen (sod-method-class method)
581 (invoke chain target)))
583 (invoke (chain target)
585 (funcall kernel target)
586 (let ((trampoline (next-trampoline (car chain)
588 (tail (if (keyword-message-p message)
589 (cons (keyword-struct-pointer)
592 (invoke-method codegen target
593 (cons trampoline tail)
595 (invoke chain target))))
597 ;;;----- That's all, folks --------------------------------------------------