3 ;;; Method combination protocol
5 ;;; (c) 2009 Straylight/Edgeware
8 ;;;----- Licensing notice ---------------------------------------------------
10 ;;; This file is part of the Sensble 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 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))
37 "The behaviour invoked by sending a message to an instance of a class.
39 This class describes the behaviour when an instance of CLASS is sent
42 This is not a useful class by itself. Message classes are expected to
43 define their own effective-method classes.
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'."))
50 (export 'message-effective-method-class)
51 (defgeneric message-effective-method-class (message)
53 "Return the effective method class for the given MESSAGE.
55 This function is invoked by `compute-sod-effective-method'."))
57 (export 'primary-method-class)
58 (defgeneric primary-method-class (message)
60 "Return the name of the primary direct method class for MESSAGE.
62 This protocol is used by `simple-message' subclasses."))
64 (export 'compute-sod-effective-method)
65 (defgeneric compute-sod-effective-method (message class)
67 "Return the effective method when a CLASS instance receives MESSAGE.
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'."))
73 (export 'compute-effective-methods)
74 (defgeneric compute-effective-methods (class)
76 "Return a list of all of the effective methods needed for CLASS.
78 The list needn't be in any particular order."))
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))
91 "An entry point into an effective method.
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
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.
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.
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."))
117 (export 'make-method-entries)
118 (defgeneric make-method-entries (effective-method chain-head chain-tail)
120 "Return a list of `method-entry' objects for an EFFECTIVE-METHOD called
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.)"))
127 ;;;--------------------------------------------------------------------------
128 ;;; Protocol for messages and direct-methods.
130 (export 'sod-message-argument-tail)
131 (defgeneric sod-message-argument-tail (message)
133 "Return the argument tail for the message, with invented argument names.
135 No `me' argument is prepended; any `:ellipsis' is left as it is."))
137 (export 'sod-message-no-varargs-tail)
138 (defgeneric sod-message-no-varargs-tail (message)
140 "Return the argument tail for the message with `:ellipsis' substituted.
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',
146 (export 'sod-method-function-type)
147 (defgeneric sod-method-function-type (method)
149 "Return the C function type for the direct method.
151 This is called during initialization of a direct method object, and the
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."))
158 (export 'sod-method-next-method-type)
159 (defgeneric sod-method-next-method-type (method)
161 "Return the C function type for the next-method trampoline.
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.
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
170 (export 'sod-method-function-name)
171 (defgeneric sod-method-function-name (method)
173 "Return the C function name for the direct method."))
175 (export 'varargs-message-p)
176 (defun varargs-message-p (message)
177 "Answer whether the MESSAGE accepts a variable-length argument list.
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)))
183 ;;;--------------------------------------------------------------------------
184 ;;; Protocol for effective methods and method entries.
186 (export 'method-entry-function-type)
187 (defgeneric method-entry-function-type (entry)
189 "Return the C function type for a method entry."))
191 (export 'method-entry-slot-name)
192 (defgeneric method-entry-slot-name (entry)
194 "Return the `vtmsgs' slot name for a method entry.
196 The default method indirects through `method-entry-slot-name-by-role'."))
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))
202 (export 'effective-method-basic-argument-names)
203 (defgeneric effective-method-basic-argument-names (method)
205 "Return a list of argument names to be passed to direct methods.
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."))
213 ;;;--------------------------------------------------------------------------
216 ;;; Enhanced code-generator class.
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))
226 "Augments CODEGEN with additional state regarding an effective method.
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
234 (export 'compute-effective-method-body)
235 (defgeneric compute-effective-method-body (method codegen target)
237 "Generates the body of an effective method.
239 Writes the function body to the code generator. It can (obviously)
240 generate auxiliary functions if it needs to.
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."))
246 (export 'simple-method-body)
247 (defgeneric simple-method-body (method codegen target)
249 "Generate the body of a simple effective method.
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."))
254 ;;; Additional instructions.
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))
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.
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.
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'."
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"
281 (sod-class-chain-head class))
282 (sod-class-nickname class))
284 (if (varargs-message-p message)
285 (convert-stmts codegen target
286 (c-type-subtype (sod-method-type direct-method))
288 (ensure-var codegen *sod-tmp-ap* (c-type va-list))
290 (make-va-copy-inst *sod-tmp-ap*
292 (deliver-expr codegen var
293 (make-call-inst function arguments))
295 (make-va-end-inst *sod-tmp-ap*))))
296 (deliver-expr codegen target (make-call-inst function arguments)))))
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.
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)."
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"))))
313 (export 'make-trampoline)
314 (defun make-trampoline (codegen super body)
315 "Construct a trampoline function and return its name.
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
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
327 The return value is the name of the generated function."
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*
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)))
346 ;;;--------------------------------------------------------------------------
347 ;;; Method entry protocol.
349 (export 'effective-method-function-name)
350 (defgeneric effective-method-function-name (method)
352 "Returns the function name of an effective method."))
354 (export 'method-entry-function-name)
355 (defgeneric method-entry-function-name (method chain-head role)
357 "Returns the function name of a method entry.
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."))
363 (export 'compute-method-entry-functions)
364 (defgeneric compute-method-entry-functions (method)
366 "Construct method entry functions.
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."))
372 ;;;--------------------------------------------------------------------------
373 ;;; Invoking direct methods.
375 (export 'invoke-delegation-chain)
376 (defun invoke-delegation-chain (codegen target basic-tail chain kernel)
377 "Invoke a chain of delegating methods.
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.
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.
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
397 (let* ((message (codegen-message codegen))
398 (argument-tail (if (varargs-message-p message)
399 (cons *sod-tmp-ap* basic-tail)
401 (labels ((next-trampoline (method chain)
402 (if (or kernel chain)
403 (make-trampoline codegen (sod-method-class method)
405 (invoke chain target)))
407 (invoke (chain target)
409 (funcall kernel target)
410 (let ((trampoline (next-trampoline (car chain)
412 (invoke-method codegen target
413 (cons trampoline argument-tail)
415 (invoke chain target))))
417 ;;;----- That's all, folks --------------------------------------------------