chiark / gitweb /
src/class-output.lisp: Leave `*instance-class*' unbound at top-level.
[sod] / src / class-output.lisp
CommitLineData
1f1d88f5
MW
1;;; -*-lisp-*-
2;;;
dea4d055 3;;; Output for classes
1f1d88f5
MW
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.
1f1d88f5
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
6e2d4b52
MW
28;;;--------------------------------------------------------------------------
29;;; Walking the layout tree.
30
7d8d3a16 31(defmethod hook-output :after ((class sod-class) reason sequencer)
6b875a6d
MW
32 "Register hooks for the class layout, direct methods, effective methods,
33 and vtables."
6e2d4b52
MW
34 (with-slots ((ilayout %ilayout) vtables methods effective-methods) class
35 (hook-output ilayout reason sequencer)
36 (dolist (method methods) (hook-output method reason sequencer))
37 (dolist (method effective-methods) (hook-output method reason sequencer))
38 (dolist (vtable vtables) (hook-output vtable reason sequencer))))
39
7d8d3a16 40(defmethod hook-output :after ((ilayout ilayout) reason sequencer)
6b875a6d 41 "Register hooks for the layout's ichains."
6e2d4b52
MW
42 (with-slots (ichains) ilayout
43 (dolist (ichain ichains) (hook-output ichain reason sequencer))))
44
7d8d3a16 45(defmethod hook-output :after ((ichain ichain) reason sequencer)
6b875a6d 46 "Register hooks for the ichain body's items."
1224dfb0 47 (dolist (item (ichain-body ichain)) (hook-output item reason sequencer)))
6e2d4b52 48
7d8d3a16 49(defmethod hook-output :after ((islots islots) reason sequencer)
6b875a6d 50 "Register hooks for the islots structure's individual slots."
1224dfb0 51 (dolist (slot (islots-slots islots)) (hook-output slot reason sequencer)))
6e2d4b52 52
7d8d3a16 53(defmethod hook-output :after ((vtable vtable) reason sequencer)
6b875a6d 54 "Register hooks for the vtable body's items."
6e2d4b52
MW
55 (with-slots (body) vtable
56 (dolist (item body) (hook-output item reason sequencer))))
57
7d8d3a16 58(defmethod hook-output :after ((vtmsgs vtmsgs) reason sequencer)
6b875a6d 59 "Register hooks for the vtmsgs structure's individual method entries."
6e2d4b52
MW
60 (with-slots (entries) vtmsgs
61 (dolist (entry entries) (hook-output entry reason sequencer))))
62
1f1d88f5 63;;;--------------------------------------------------------------------------
6e409901 64;;; Class declarations.
1f1d88f5 65
4818ff76
MW
66(export 'emit-class-typedef)
67(defgeneric emit-class-typedef (class stream)
68 (:documentation
69 "Emit a `typedef' for the CLASS's C class type to the output STREAM.
70
71 By default, this will be an alias for the class's home `ichain'
72 structure."))
73(defmethod emit-class-typedef ((class sod-class) stream)
74 (format stream "typedef struct ~A ~A;~%"
75 (ichain-struct-tag class (sod-class-chain-head class)) class))
76
77(export 'emit-class-object-decl)
78(defgeneric emit-class-object-decl (class stream)
79 (:documentation
80 "Emit the declaration and macros for the CLASS's class object.
81
82 This includes the main declaration, and the convenience macros for
83 referring to the class object's individual chains. Write everything to
84 the output STREAM."))
85(defmethod emit-class-object-decl ((class sod-class) stream)
86 (let ((metaclass (sod-class-metaclass class))
87 (metaroot (find-root-metaclass class)))
88
89 ;; Output the actual class object declaration, and the special
90 ;; `...__class' macro for the root-metaclass chain.
91 (format stream "/* The class object. */~@
92 extern const struct ~A ~A__classobj;~@
93 #define ~:*~A__class (&~:*~A__classobj.~A.~A)~%"
94 (ilayout-struct-tag metaclass) class
95 (sod-class-nickname (sod-class-chain-head metaroot))
96 (sod-class-nickname metaroot))
97
98 ;; Write the uglier `...__cls_...' macros for the class object's other
99 ;; chains, if any.
100 (dolist (chain (sod-class-chains metaclass))
101 (let ((tail (car chain)))
102 (unless (eq tail metaroot)
103 (format stream "#define ~A__cls_~A (&~2:*~A__classobj.~A.~A)~%"
104 class (sod-class-nickname (sod-class-chain-head tail))
105 (sod-class-nickname tail)))))
106 (terpri stream)))
107
108(export 'emit-class-conversion-macro)
109(defgeneric emit-class-conversion-macro (class super stream)
110 (:documentation
111 "Emit a macro for converting an instance of CLASS to an instance of SUPER.
112
113 By default this is named `CLASS__CONV_SPR'. In-chain upcasts are just a
114 trivial pointer cast, which any decent compiler will elide; cross-chain
115 upcasts use the `SOD_XCHAIN' macro. Write the macro to the output
116 STREAM."))
117(defmethod emit-class-conversion-macro
118 ((class sod-class) (super sod-class) stream)
119 (let ((super-head (sod-class-chain-head super)))
120 (format stream "#define ~:@(~A__CONV_~A~)(_obj) ((~A *)~
121 ~:[SOD_XCHAIN(~A, (_obj))~;(_obj)~])~%"
122 class (sod-class-nickname super) super
123 (eq super-head (sod-class-chain-head class))
124 (sod-class-nickname super-head))))
125
126(export 'emit-message-macro-defn)
127(defgeneric emit-message-macro-defn
128 (class entry varargsp me in-names out-names stream)
129 (:documentation
130 "Output a message macro for invoking a method ENTRY, with given arguments.
131
132 The default method on `emit-message-macro' calcualates the necessary
133 argument lists and calls this function to actually write the necessary
134 `#define' line to the stream. The intended division of responsibilities
135 is that `emit-message-macro' handles the peculiarities of marshalling the
136 arguments to the method entry function, while `emit-message-macro-defn'
137 concerns itself with navigating the vtable to find the right function in
138 the first place.")
139 (:method :around ((class sod-class) (entry method-entry)
140 varargsp me in-names out-names
141 stream)
142 (when varargsp (format stream "#ifdef SOD__HAVE_VARARGS_MACROS~%"))
143 (call-next-method)
144 (when varargsp (format stream "#endif~%"))))
145(defmethod emit-message-macro-defn ((class sod-class) (entry method-entry)
146 varargsp me in-names out-names
147 stream)
148 (format stream "#define ~A(~{~A~^, ~}) (~A)->_vt->~A.~A(~{~A~^, ~})~%"
149 (message-macro-name class entry)
150 in-names
151 me
152 (sod-class-nickname class)
153 (method-entry-slot-name entry)
154 out-names))
155
156(export 'emit-message-macro)
157(defgeneric emit-message-macro (class entry stream)
158 (:documentation
159 "Write a macro for invoking the method ENTRY on an instance of CLASS.
160
161 The default behaviour is quite complicated, particular when varargs or
162 keyword messages are involved."))
163(defmethod emit-message-macro ((class sod-class) (entry method-entry) stream)
164 (when (some (lambda (message)
165 (or (keyword-message-p message)
166 (varargs-message-p message)))
167 (sod-class-messages class)))
168 (let* ((type (method-entry-function-type entry))
169 (args (c-function-arguments type))
170 (in-names nil) (out-names nil) (varargsp nil) (me "me"))
171 (do ((args args (cdr args)))
172 ((endp args))
173 (let* ((raw-name (princ-to-string (argument-name (car args))))
174 (name (if (find raw-name
175 (list "_vt"
176 (sod-class-nickname class)
177 (method-entry-slot-name entry))
178 :test #'string=)
179 (format nil "sod__a_~A" raw-name)
180 raw-name)))
181 (cond ((and (cdr args) (eq (cadr args) :ellipsis))
182 (setf varargsp t)
183 (unless in-names (setf me "SOD__CAR(__VA_ARGS__)"))
184 (push (format nil "/*~A*/ ..." name) in-names)
185 (push "__VA_ARGS__" out-names)
186 (return))
187 (t
188 (push name in-names)
189 (push name out-names)))))
190 (when varargsp (format stream "#ifdef SOD__HAVE_VARARGS_MACROS~%"))
191 (emit-message-macro-defn class entry varargsp me
192 (nreverse in-names)
193 (nreverse out-names)
194 stream)
195 (when varargsp (format stream "#endif~%"))))
196
7d8d3a16 197(defmethod hook-output ((class sod-class) (reason (eql :h)) sequencer)
6b875a6d
MW
198 "Write the skeleton of a class declaration.
199
200 Most of the work is done by other functions.
201
202 * The class type is defined by `emit-class-typedef'.
203
204 * The class object is declared by `emit-class-object-decl'.
205
206 * The upcast conversion macros are defined by `emit-class-conversion-
207 macro'.
208
209 * The message invocation macros are defined by `emit-message-macro'.
210
211 * The class instance structure itself is constructed by the `ilayout'
212 object.
213
214 * The various vtable structures are constructed by the `vtable'
215 objects."
1f1d88f5
MW
216
217 ;; Main output sequencing.
218 (sequence-output (stream sequencer)
219
1f1d88f5
MW
220 :constraint
221 ((:classes :start)
222 (class :banner)
223 (class :islots :start) (class :islots :slots) (class :islots :end)
224 (class :vtmsgs :start) (class :vtmsgs :end)
225 (class :vtables :start) (class :vtables :end)
226 (class :vtable-externs) (class :vtable-externs-after)
43073476
MW
227 (class :methods :start) (class :methods :defs)
228 (class :methods) (class :methods :end)
1f1d88f5
MW
229 (class :ichains :start) (class :ichains :end)
230 (class :ilayout :start) (class :ilayout :slots) (class :ilayout :end)
231 (class :conversions)
6bc944c3 232 (class :message-macros)
ddee4bb1 233 (class :object)
1f1d88f5
MW
234 (:classes :end))
235
4818ff76
MW
236 (:typedefs (emit-class-typedef class stream))
237 ((class :banner) (banner (format nil "Class ~A" class) stream))
238 ((class :vtable-externs-after) (terpri stream))
239 ((class :vtable-externs) (format stream "/* Vtable structures. */~%"))
240 ((class :object) (emit-class-object-decl class stream)))
1f1d88f5
MW
241
242 ;; Maybe generate an islots structure.
243 (when (sod-class-slots class)
1f1d88f5
MW
244 (sequence-output (stream sequencer)
245 ((class :islots :start)
a07d8d00 246 (format stream "/* Instance slots. */~@
ddee4bb1
MW
247 struct ~A {~%"
248 (islots-struct-tag class)))
1f1d88f5
MW
249 ((class :islots :end)
250 (format stream "};~2%"))))
251
252 ;; Declare the direct methods.
253 (when (sod-class-methods class)
1f1d88f5 254 (sequence-output (stream sequencer)
ddee4bb1
MW
255 ((class :methods :start)
256 (format stream "/* Direct methods. */~%"))
257 ((class :methods :end)
1f1d88f5
MW
258 (terpri stream))))
259
260 ;; Provide upcast macros which do the right thing.
261 (when (sod-class-direct-superclasses class)
262 (sequence-output (stream sequencer)
263 ((class :conversions)
4818ff76
MW
264 (format stream "/* Conversion macros. */~%")
265 (dolist (super (cdr (sod-class-precedence-list class)))
266 (emit-class-conversion-macro class super stream))
267 (terpri stream))))
1f1d88f5 268
6bc944c3
MW
269 ;; Provide convenience macros for sending the newly defined messages. (The
270 ;; macros work on all subclasses too.)
271 ;;
272 ;; We need each message's method entry type for this, so we need to dig it
273 ;; out of the vtmsgs structure. Indeed, the vtmsgs for this class contains
274 ;; entries for precisely the messages we want to make macros for.
43073476
MW
275 (when (some (lambda (message)
276 (or (keyword-message-p message)
277 (varargs-message-p message)))
278 (sod-class-messages class))
e674612e
MW
279 (one-off-output 'varargs-macros sequencer :early-decls
280 (lambda (stream)
281 (format stream
282 "~%SOD__VARARGS_MACROS_PREAMBLE~%"))))
6bc944c3
MW
283 (when (sod-class-messages class)
284 (sequence-output (stream sequencer)
285 ((class :message-macros)
286 (let* ((vtable (find (sod-class-chain-head class)
287 (sod-class-vtables class)
288 :key #'vtable-chain-head))
289 (vtmsgs (find-if (lambda (item)
290 (and (typep item 'vtmsgs)
291 (eql (vtmsgs-class item) class)))
292 (vtable-body vtable))))
293 (format stream "/* Message invocation macros. */~%")
6bc944c3 294 (dolist (entry (vtmsgs-entries vtmsgs))
4818ff76 295 (emit-message-macro class entry stream))
7d8d3a16
MW
296 (terpri stream))))))
297
298(defmethod hook-output :after ((class sod-class) (reason (eql :h)) sequencer)
6b875a6d 299 "Register hooks to output CLASS's direct slots and messages."
6bc944c3 300
7d8d3a16
MW
301 ;; Output a structure member definition for each instance slot.
302 (dolist (slot (sod-class-slots class))
303 (hook-output slot 'islots sequencer))
304
305 ;; Generate a vtmsgs structure for all superclasses.
6e2d4b52 306 (hook-output (car (sod-class-vtables class)) 'vtmsgs sequencer))
1f1d88f5
MW
307
308;;;--------------------------------------------------------------------------
309;;; Instance structure.
310
7d8d3a16 311(defmethod hook-output ((slot sod-slot) (reason (eql 'islots)) sequencer)
6b875a6d 312 "Declare the member for an individual slot within an `islots' structure."
1f1d88f5
MW
313 (sequence-output (stream sequencer)
314 (((sod-slot-class slot) :islots :slots)
315 (pprint-logical-block (stream nil :prefix " " :suffix ";")
316 (pprint-c-type (sod-slot-type slot) stream (sod-slot-name slot)))
317 (terpri stream))))
318
7d8d3a16 319(defmethod hook-output ((ilayout ilayout) (reason (eql :h)) sequencer)
6b875a6d
MW
320 "Define the structure for a class layout.
321
322 Here we just provide the outermost structure. It gets filled in by
323 the `ichains' objects and their body items."
324 (with-slots ((class %class) ichains) ilayout
1f1d88f5
MW
325 (sequence-output (stream sequencer)
326 ((class :ilayout :start)
a07d8d00 327 (format stream "/* Instance layout. */~@
ddee4bb1
MW
328 struct ~A {~%"
329 (ilayout-struct-tag class)))
1f1d88f5 330 ((class :ilayout :end)
7d8d3a16
MW
331 (format stream "};~2%")))))
332
333(defmethod hook-output :after ((ilayout ilayout) (reason (eql :h)) sequencer)
6b875a6d 334 "Register hooks to write chain members into the overall class layout."
7d8d3a16
MW
335 (dolist (ichain (ilayout-ichains ilayout))
336 (hook-output ichain 'ilayout sequencer)))
1f1d88f5 337
7d8d3a16 338(defmethod hook-output ((ichain ichain) (reason (eql :h)) sequencer)
6b875a6d
MW
339 "Define the layout structure for a particular chain of a class.
340
341 A member of this class is dropped into the `ilayout' structure by the
342 corresponding method for the `ilayout' reason.
343
344 We define both the chain structure of the class, and a union of it with
345 all of its in-chain superclasses (so as to invoke the common-prefix
346 rule)."
4b8e5c03 347 (with-slots ((class %class) chain-head chain-tail) ichain
ddee4bb1
MW
348 (when (eq class chain-tail)
349 (sequence-output (stream sequencer)
350 :constraint ((class :ichains :start)
351 (class :ichain chain-head :start)
352 (class :ichain chain-head :slots)
353 (class :ichain chain-head :end)
354 (class :ichains :end))
355 ((class :ichain chain-head :start)
a07d8d00 356 (format stream "/* Instance chain structure. */~@
ddee4bb1
MW
357 struct ~A {~%"
358 (ichain-struct-tag chain-tail chain-head)))
359 ((class :ichain chain-head :end)
360 (format stream "};~2%")
a07d8d00
MW
361 (format stream "/* Union of equivalent superclass chains. */~@
362 union ~A {~@
ddee4bb1
MW
363 ~:{ struct ~A ~A;~%~}~
364 };~2%"
365 (ichain-union-tag chain-tail chain-head)
dea4d055
MW
366
367 ;; Make sure the most specific class is first: only the
368 ;; first element of a union can be statically initialized in
369 ;; C90.
ddee4bb1
MW
370 (mapcar (lambda (super)
371 (list (ichain-struct-tag super chain-head)
372 (sod-class-nickname super)))
373 (sod-class-chain chain-tail))))))))
1f1d88f5 374
7d8d3a16 375(defmethod hook-output ((ichain ichain) (reason (eql 'ilayout)) sequencer)
6b875a6d 376 "Declare the member for a class chain within the class layout."
4b8e5c03 377 (with-slots ((class %class) chain-head chain-tail) ichain
1f1d88f5
MW
378 (sequence-output (stream sequencer)
379 ((class :ilayout :slots)
ddee4bb1
MW
380 (format stream " union ~A ~A;~%"
381 (ichain-union-tag chain-tail chain-head)
1f1d88f5
MW
382 (sod-class-nickname chain-head))))))
383
7d8d3a16 384(defmethod hook-output ((vtptr vtable-pointer) (reason (eql :h)) sequencer)
6b875a6d 385 "Declare the member for a vtable pointer within an `ichain' structure."
4b8e5c03 386 (with-slots ((class %class) chain-head chain-tail) vtptr
64fd357d
MW
387 (when (eq class chain-tail)
388 (sequence-output (stream sequencer)
389 ((class :ichain chain-head :slots)
390 (format stream " const struct ~A *_vt;~%"
391 (vtable-struct-tag chain-tail chain-head)))))))
1f1d88f5 392
7d8d3a16 393(defmethod hook-output ((islots islots) (reason (eql :h)) sequencer)
6b875a6d 394 "Declare the member for a class's `islots' within an `ichain' structure."
4b8e5c03 395 (with-slots ((class %class) subclass slots) islots
64fd357d
MW
396 (let ((head (sod-class-chain-head class)))
397 (when (eq head (sod-class-chain-head subclass))
398 (sequence-output (stream sequencer)
399 ((subclass :ichain (sod-class-chain-head class) :slots)
400 (format stream " struct ~A ~A;~%"
401 (islots-struct-tag class)
402 (sod-class-nickname class))))))))
1f1d88f5
MW
403
404;;;--------------------------------------------------------------------------
405;;; Vtable structure.
406
7d8d3a16 407(defmethod hook-output ((method sod-method) (reason (eql :h)) sequencer)
6b875a6d
MW
408 "Emit declarations for a direct method.
409
410 We declare the direct method function, and, if necessary, the `suppliedp'
411 structure for its keyword arguments."
412
4b8e5c03 413 (with-slots ((class %class)) method
ddee4bb1
MW
414 (sequence-output (stream sequencer)
415 ((class :methods)
416 (let ((type (sod-method-function-type method)))
417 (princ "extern " stream)
418 (pprint-c-type (commentify-function-type type) stream
419 (sod-method-function-name method))
43073476
MW
420 (format stream ";~%")))
421 ((class :methods :defs)
422 (let* ((type (sod-method-type method))
423 (keys (and (typep type 'c-keyword-function-type)
424 (c-function-keywords type))))
425 (when keys
426 (format stream "struct ~A {~%~
fd040f06 427 ~{ unsigned ~A: 1;~%~}~
43073476
MW
428 };~2%"
429 (direct-method-suppliedp-struct-tag method)
430 (mapcar #'argument-name keys))))))))
ddee4bb1 431
7d8d3a16 432(defmethod hook-output ((vtable vtable) (reason (eql :h)) sequencer)
6b875a6d
MW
433 "Define the structure for a vtable.
434
435 We define the vtable structure of the class, and a union of it with all of
436 its in-chain superclasses (so as to invoke the common-prefix rule). We
437 also declare the vtable object, defined by the corresponding `:c' method."
4b8e5c03 438 (with-slots ((class %class) chain-head chain-tail) vtable
ddee4bb1
MW
439 (when (eq class chain-tail)
440 (sequence-output (stream sequencer)
441 :constraint ((class :vtables :start)
442 (class :vtable chain-head :start)
443 (class :vtable chain-head :slots)
444 (class :vtable chain-head :end)
445 (class :vtables :end))
446 ((class :vtable chain-head :start)
a07d8d00 447 (format stream "/* Vtable structure. */~@
ddee4bb1
MW
448 struct ~A {~%"
449 (vtable-struct-tag chain-tail chain-head)))
450 ((class :vtable chain-head :end)
c2438e62
MW
451 (format stream "};~2%")
452 (format stream "/* Union of equivalent superclass vtables. */~@
453 union ~A {~@
454 ~:{ struct ~A ~A;~%~}~
455 };~2%"
456 (vtable-union-tag chain-tail chain-head)
457
458 ;; As for the ichain union, make sure the most specific
459 ;; class is first.
460 (mapcar (lambda (super)
461 (list (vtable-struct-tag super chain-head)
462 (sod-class-nickname super)))
463 (sod-class-chain chain-tail))))))
1f1d88f5 464 (sequence-output (stream sequencer)
1f1d88f5 465 ((class :vtable-externs)
7c3bae74 466 (format stream "~@<extern const union ~A ~2I~_~A;~:>~%"
c2438e62 467 (vtable-union-tag chain-tail chain-head)
7c3bae74 468 (vtable-name class chain-head))))))
1f1d88f5 469
7d8d3a16 470(defmethod hook-output ((vtmsgs vtmsgs) (reason (eql :h)) sequencer)
6b875a6d 471 "Declare the member for a class's `vtmsgs' within a `vtable' structure."
4b8e5c03 472 (with-slots ((class %class) subclass chain-head chain-tail) vtmsgs
64fd357d
MW
473 (when (eq subclass chain-tail)
474 (sequence-output (stream sequencer)
475 ((subclass :vtable chain-head :slots)
476 (format stream " struct ~A ~A;~%"
477 (vtmsgs-struct-tag subclass class)
478 (sod-class-nickname class)))))))
1f1d88f5 479
7d8d3a16 480(defmethod hook-output ((vtmsgs vtmsgs) (reason (eql 'vtmsgs)) sequencer)
6b875a6d 481 "Define the `vtmsgs' structure for a class's method entry functions."
1f1d88f5 482 (when (vtmsgs-entries vtmsgs)
4b8e5c03 483 (with-slots ((class %class) subclass) vtmsgs
1f1d88f5
MW
484 (sequence-output (stream sequencer)
485 :constraint ((subclass :vtmsgs :start)
486 (subclass :vtmsgs class :start)
487 (subclass :vtmsgs class :slots)
488 (subclass :vtmsgs class :end)
489 (subclass :vtmsgs :end))
490 ((subclass :vtmsgs class :start)
a07d8d00 491 (format stream "/* Messages protocol from class ~A */~@
ddee4bb1
MW
492 struct ~A {~%"
493 class
494 (vtmsgs-struct-tag subclass class)))
1f1d88f5
MW
495 ((subclass :vtmsgs class :end)
496 (format stream "};~2%"))))))
497
7d8d3a16
MW
498(defmethod hook-output ((entry method-entry)
499 (reason (eql 'vtmsgs)) sequencer)
6b875a6d 500 "Declare the member for a method entry within a `vtmsgs' structure."
ddee4bb1
MW
501 (let* ((method (method-entry-effective-method entry))
502 (message (effective-method-message method))
1f1d88f5 503 (class (effective-method-class method))
9ec578d9
MW
504 (function-type (method-entry-function-type entry))
505 (commented-type (commentify-function-type function-type))
506 (pointer-type (make-pointer-type commented-type)))
1f1d88f5
MW
507 (sequence-output (stream sequencer)
508 ((class :vtmsgs (sod-message-class message) :slots)
509 (pprint-logical-block (stream nil :prefix " " :suffix ";")
b426ab51 510 (pprint-c-type pointer-type stream (method-entry-slot-name entry)))
1f1d88f5
MW
511 (terpri stream)))))
512
7d8d3a16 513(defmethod hook-output ((cptr class-pointer) (reason (eql :h)) sequencer)
6b875a6d 514 "Declare the member for a class-chain pointer within a `vtmsgs' structure."
4b8e5c03 515 (with-slots ((class %class) chain-head metaclass meta-chain-head) cptr
64fd357d
MW
516 (when (eq chain-head (sod-class-chain-head class))
517 (sequence-output (stream sequencer)
518 ((class :vtable chain-head :slots)
519 (format stream " const ~A *~:[_class~;~:*_cls_~A~];~%"
520 metaclass
521 (and (sod-class-direct-superclasses meta-chain-head)
522 (sod-class-nickname meta-chain-head))))))))
1f1d88f5 523
7d8d3a16 524(defmethod hook-output ((boff base-offset) (reason (eql :h)) sequencer)
6b875a6d 525 "Declare the member for the base offset within a `vtmsgs' structure."
4b8e5c03 526 (with-slots ((class %class) chain-head) boff
64fd357d
MW
527 (when (eq chain-head (sod-class-chain-head class))
528 (sequence-output (stream sequencer)
529 ((class :vtable chain-head :slots)
530 (write-line " size_t _base;" stream))))))
1f1d88f5 531
7d8d3a16 532(defmethod hook-output ((choff chain-offset) (reason (eql :h)) sequencer)
6b875a6d 533 "Declare the member for a cross-chain offset within a `vtmsgs' structure."
4b8e5c03 534 (with-slots ((class %class) chain-head target-head) choff
64fd357d
MW
535 (when (eq chain-head (sod-class-chain-head class))
536 (sequence-output (stream sequencer)
537 ((class :vtable chain-head :slots)
538 (format stream " ptrdiff_t _off_~A;~%"
539 (sod-class-nickname target-head)))))))
1f1d88f5 540
3be8c2bf
MW
541;;;--------------------------------------------------------------------------
542;;; Implementation output.
543
6e2d4b52 544(export '*instance-class*)
944bf936 545(defvar-unbound *instance-class*
4b856491
MW
546 "The class currently being output.
547
548 This is bound during the `hook-output' traversal of a class layout for
549 `:c' output, since some of the objects traversed actually `belong' to
550 superclasses and there's no other way to find out what the reference class
551 actually is.
552
553 It may be bound at other times.")
3be8c2bf 554
7d8d3a16 555(defmethod hook-output ((class sod-class) (reason (eql :c)) sequencer)
6b875a6d
MW
556 "Write the skeleton of a class definition.
557
558 Most of the work is done by other methods.
559
560 * The direct methods are defined by the `sod-method' objects.
561
562 * The effective method functions and related structures are defined by
563 the effective method objects.
564
565 * The vtable structures are initialized by the vtable objects and their
566 component items.
567
568 * The class structure and its associated tables are initialized by the
569 metaclass's layout objects."
570
3be8c2bf
MW
571 (sequence-output (stream sequencer)
572
573 :constraint
574 ((:classes :start)
575 (class :banner)
576 (class :direct-methods :start) (class :direct-methods :end)
a07d8d00 577 (class :effective-methods)
3be8c2bf
MW
578 (class :vtables :start) (class :vtables :end)
579 (class :object :prepare) (class :object :start) (class :object :end)
580 (:classes :end))
581
582 ((class :banner)
583 (banner (format nil "Class ~A" class) stream))
584
585 ((class :object :start)
586 (format stream "~
587/* The class object. */
588const struct ~A ~A__classobj = {~%"
589 (ilayout-struct-tag (sod-class-metaclass class))
590 class))
591 ((class :object :end)
7d8d3a16 592 (format stream "};~2%"))))
3be8c2bf 593
7d8d3a16 594(defmethod hook-output :after ((class sod-class) (reason (eql :c)) sequencer)
6b875a6d 595 "Register hooks to initialize the class object structure."
3be8c2bf 596 (let ((*instance-class* class))
dea4d055 597 (hook-output (sod-class-ilayout (sod-class-metaclass class))
6e2d4b52 598 'class sequencer)))
3be8c2bf
MW
599
600;;;--------------------------------------------------------------------------
9ec578d9 601;;; Direct and effective methods.
3be8c2bf 602
7d8d3a16
MW
603(defmethod hook-output ((method delegating-direct-method)
604 (reason (eql :c)) sequencer)
6b875a6d 605 "Define the `CALL_NEXT_METHOD' macro around a `delegating-direct-method'."
4b8e5c03 606 (with-slots ((class %class) body) method
3be8c2bf 607 (unless body
dea4d055 608 (return-from hook-output))
3be8c2bf
MW
609 (sequence-output (stream sequencer)
610 ((class :direct-method method :start)
611 (format stream "#define CALL_NEXT_METHOD (next_method(~{~A~^, ~}))~%"
612 (mapcar #'argument-name
613 (c-function-arguments (sod-method-next-method-type
614 method)))))
615 ((class :direct-method method :end)
7d8d3a16
MW
616 (format stream "#undef CALL_NEXT_METHOD~%"))))
617 (call-next-method))
3be8c2bf 618
7d8d3a16 619(defmethod hook-output ((method sod-method) (reason (eql :c)) sequencer)
6b875a6d 620 "Define a direct method function."
7de8c666 621 (with-slots ((class %class) role body message) method
3be8c2bf 622 (unless body
dea4d055 623 (return-from hook-output))
3be8c2bf
MW
624 (sequence-output (stream sequencer)
625 :constraint ((class :direct-methods :start)
7de8c666 626 (class :direct-method method :banner)
3be8c2bf
MW
627 (class :direct-method method :start)
628 (class :direct-method method :body)
629 (class :direct-method method :end)
630 (class :direct-methods :end))
7de8c666
MW
631 ((class :direct-method method :banner)
632 (format-banner-comment stream "Direct ~@[~(~A~) ~]method ~:_~
633 on `~A.~A' ~:_defined by `~A'."
634 role
635 (sod-class-nickname
636 (sod-message-class message))
637 (sod-message-name message)
638 class)
639 (fresh-line stream))
3be8c2bf
MW
640 ((class :direct-method method :body)
641 (pprint-c-type (sod-method-function-type method)
642 stream
643 (sod-method-function-name method))
644 (format stream "~&{~%")
645 (write body :stream stream :pretty nil :escape nil)
646 (format stream "~&}~%"))
647 ((class :direct-method method :end)
648 (terpri stream)))))
649
7d8d3a16
MW
650(defmethod hook-output ((method basic-effective-method)
651 (reason (eql :c)) sequencer)
6b875a6d
MW
652 "Define an effective method's functions.
653
654 Specifically, the method-entry functions and any auxiliary functions
655 needed to stitch everything together."
4b8e5c03 656 (with-slots ((class %class) functions) method
dea4d055
MW
657 (sequence-output (stream sequencer)
658 ((class :effective-methods)
43073476
MW
659 (let* ((keys (effective-method-keywords method))
660 (message (effective-method-message method))
661 (msg-class (sod-message-class message)))
662 (when keys
663 (format-banner-comment stream "Keyword argument structure ~:_~
664 for `~A.~A' ~:_on class `~A'."
665 (sod-class-nickname msg-class)
666 (sod-message-name message)
667 class)
668 (format stream "~&struct ~A {~%"
669 (effective-method-keyword-struct-tag method))
fd040f06 670 (format stream "~{ unsigned ~A__suppliedp: 1;~%~}"
43073476
MW
671 (mapcar #'argument-name keys))
672 (dolist (key keys)
673 (write-string " " stream)
674 (pprint-c-type (argument-type key) stream (argument-name key))
675 (format stream ";~%"))
676 (format stream "};~2%")))
dea4d055
MW
677 (dolist (func functions)
678 (write func :stream stream :escape nil :circle nil))))))
679
a07d8d00
MW
680;;;--------------------------------------------------------------------------
681;;; Vtables.
682
7d8d3a16 683(defmethod hook-output ((vtable vtable) (reason (eql :c)) sequencer)
6b875a6d
MW
684 "Define a vtable structure.
685
686 Here we just provide the outermost structure. It gets filled in by the
687 vtable object's body items."
4b8e5c03 688 (with-slots ((class %class) chain-head chain-tail) vtable
a07d8d00
MW
689 (sequence-output (stream sequencer)
690 :constraint ((class :vtables :start)
691 (class :vtable chain-head :start)
692 (class :vtable chain-head :end)
693 (class :vtables :end))
694 ((class :vtable chain-head :start)
695 (format stream "/* Vtable for ~A chain. */~@
c2438e62 696 const union ~A ~A = { {~%"
a07d8d00 697 chain-head
c2438e62 698 (vtable-union-tag chain-tail chain-head)
9ec578d9 699 (vtable-name class chain-head)))
a07d8d00 700 ((class :vtable chain-head :end)
c2438e62 701 (format stream "} };~2%")))))
a07d8d00 702
7d8d3a16 703(defmethod hook-output ((cptr class-pointer) (reason (eql :c)) sequencer)
6b875a6d 704 "Drop a class pointer into a vtable definition."
4b8e5c03 705 (with-slots ((class %class) chain-head metaclass meta-chain-head) cptr
a07d8d00
MW
706 (sequence-output (stream sequencer)
707 :constraint ((class :vtable chain-head :start)
708 (class :vtable chain-head :class-pointer metaclass)
709 (class :vtable chain-head :end))
710 ((class :vtable chain-head :class-pointer metaclass)
9ec578d9
MW
711 (format stream " /* ~21@A = */ &~A__classobj.~A.~A,~%"
712 (if (sod-class-direct-superclasses meta-chain-head)
713 (format nil "_cls_~A"
714 (sod-class-nickname meta-chain-head))
715 "_class")
fc5d9486 716 class
a07d8d00
MW
717 (sod-class-nickname meta-chain-head)
718 (sod-class-nickname metaclass))))))
719
7d8d3a16 720(defmethod hook-output ((boff base-offset) (reason (eql :c)) sequencer)
6b875a6d 721 "Drop a base offset into a vtable definition."
4b8e5c03 722 (with-slots ((class %class) chain-head) boff
a07d8d00
MW
723 (sequence-output (stream sequencer)
724 :constraint ((class :vtable chain-head :start)
725 (class :vtable chain-head :base-offset)
726 (class :vtable chain-head :end))
727 ((class :vtable chain-head :base-offset)
9ec578d9
MW
728 (format stream " /* ~21@A = */ offsetof(struct ~A, ~A),~%"
729 "_base"
a07d8d00
MW
730 (ilayout-struct-tag class)
731 (sod-class-nickname chain-head))))))
732
7d8d3a16 733(defmethod hook-output ((choff chain-offset) (reason (eql :c)) sequencer)
6b875a6d 734 "Drop a cross-chain offset into a vtable definition."
4b8e5c03 735 (with-slots ((class %class) chain-head target-head) choff
a07d8d00
MW
736 (sequence-output (stream sequencer)
737 :constraint ((class :vtable chain-head :start)
738 (class :vtable chain-head :chain-offset target-head)
739 (class :vtable chain-head :end))
740 ((class :vtable chain-head :chain-offset target-head)
9ec578d9
MW
741 (format stream " /* ~21@A = */ SOD_OFFSETDIFF(struct ~A, ~A, ~A),~%"
742 (format nil "_off_~A" (sod-class-nickname target-head))
a07d8d00
MW
743 (ilayout-struct-tag class)
744 (sod-class-nickname chain-head)
745 (sod-class-nickname target-head))))))
746
7d8d3a16 747(defmethod hook-output ((vtmsgs vtmsgs) (reason (eql :c)) sequencer)
6b875a6d
MW
748 "Define the method entry pointers for a superclass's messages.
749
750 We only provide the outer structure. It gets filled in by the
751 `method-entry' objects."
4b8e5c03 752 (with-slots ((class %class) subclass chain-head) vtmsgs
a07d8d00
MW
753 (sequence-output (stream sequencer)
754 :constraint ((subclass :vtable chain-head :start)
755 (subclass :vtable chain-head :vtmsgs class :start)
756 (subclass :vtable chain-head :vtmsgs class :slots)
757 (subclass :vtable chain-head :vtmsgs class :end)
758 (subclass :vtable chain-head :end))
759 ((subclass :vtable chain-head :vtmsgs class :start)
760 (format stream " { /* Method entries for ~A messages. */~%"
761 class))
762 ((subclass :vtable chain-head :vtmsgs class :end)
763 (format stream " },~%")))))
764
7d8d3a16 765(defmethod hook-output ((entry method-entry) (reason (eql :c)) sequencer)
6b875a6d 766 "Define a method-entry pointer in a vtable."
4b8e5c03 767 (with-slots ((method %method) chain-head chain-tail role) entry
a07d8d00
MW
768 (let* ((message (effective-method-message method))
769 (class (effective-method-class method))
770 (super (sod-message-class message)))
771 (sequence-output (stream sequencer)
772 ((class :vtable chain-head :vtmsgs super :slots)
9ec578d9 773 (format stream " /* ~19@A = */ ~A,~%"
b426ab51
MW
774 (method-entry-slot-name entry)
775 (method-entry-function-name method chain-head role)))))))
a07d8d00 776
3be8c2bf
MW
777;;;--------------------------------------------------------------------------
778;;; Filling in the class object.
779
7d8d3a16 780(defmethod hook-output ((ichain ichain) (reason (eql 'class)) sequencer)
6b875a6d
MW
781 "Define an instance chain of a class object.
782
783 Here we only provide the outer structure. It gets filled in by the
784 `ichain' object's body items."
4b8e5c03 785 (with-slots ((class %class) chain-head) ichain
3be8c2bf
MW
786 (sequence-output (stream sequencer)
787 :constraint ((*instance-class* :object :start)
788 (*instance-class* :object chain-head :ichain :start)
789 (*instance-class* :object chain-head :ichain :end)
790 (*instance-class* :object :end))
791 ((*instance-class* :object chain-head :ichain :start)
792 (format stream " { { /* ~A ichain */~%"
793 (sod-class-nickname chain-head)))
794 ((*instance-class* :object chain-head :ichain :end)
795 (format stream " } },~%")))))
796
7d8d3a16 797(defmethod hook-output ((islots islots) (reason (eql 'class)) sequencer)
6b875a6d
MW
798 "Define an instance's slots in a class object.
799
800 Here we only provide the outer structure. It gets filled in by the
801 individual slot objects."
4b8e5c03 802 (with-slots ((class %class)) islots
3be8c2bf
MW
803 (let ((chain-head (sod-class-chain-head class)))
804 (sequence-output (stream sequencer)
805 :constraint ((*instance-class* :object chain-head :ichain :start)
806 (*instance-class* :object class :slots :start)
807 (*instance-class* :object class :slots)
808 (*instance-class* :object class :slots :end)
809 (*instance-class* :object chain-head :ichain :end))
810 ((*instance-class* :object class :slots :start)
811 (format stream " { /* Class ~A */~%" class))
812 ((*instance-class* :object class :slots :end)
813 (format stream " },~%"))))))
814
7d8d3a16
MW
815(defmethod hook-output ((vtptr vtable-pointer)
816 (reason (eql 'class)) sequencer)
6b875a6d 817 "Define a vtable pointer in a class object."
4b8e5c03 818 (with-slots ((class %class) chain-head chain-tail) vtptr
3be8c2bf
MW
819 (sequence-output (stream sequencer)
820 :constraint ((*instance-class* :object chain-head :ichain :start)
821 (*instance-class* :object chain-head :vtable)
822 (*instance-class* :object chain-head :ichain :end))
823 ((*instance-class* :object chain-head :vtable)
c2438e62
MW
824 (format stream " /* ~17@A = */ &~A.~A,~%"
825 "_vt"
826 (vtable-name class chain-head)
827 (sod-class-nickname chain-tail))))))
3be8c2bf 828
3be8c2bf 829(defgeneric output-class-initializer (slot instance stream)
6b875a6d
MW
830 (:documentation
831 "Define an individual slot in a class object.")
3be8c2bf 832 (:method ((slot sod-class-effective-slot) (instance sod-class) stream)
6b875a6d
MW
833 "If this slot has an initializer function, then call it; otherwise try to
834 find an initializer as usual."
9ec578d9
MW
835 (let ((func (effective-slot-initializer-function slot))
836 (direct-slot (effective-slot-direct-slot slot)))
3be8c2bf 837 (if func
9ec578d9
MW
838 (format stream " /* ~15@A = */ ~A,~%"
839 (sod-slot-name direct-slot)
840 (funcall func instance))
3be8c2bf
MW
841 (call-next-method))))
842 (:method ((slot effective-slot) (instance sod-class) stream)
6b875a6d 843 "Initialize a class slot by looking up an applicable initializer."
9ec578d9
MW
844 (let ((init (find-class-initializer slot instance))
845 (direct-slot (effective-slot-direct-slot slot)))
a888e3ac
MW
846 (format stream " /* ~15@A = */ ~A,~%"
847 (sod-slot-name direct-slot)
848 (sod-initializer-value init)))))
3be8c2bf 849
7d8d3a16
MW
850(defmethod hook-output ((slot sod-class-effective-slot)
851 (reason (eql 'class)) sequencer)
6b875a6d
MW
852 "Write any necessary preparatory definitions for a class slot with a
853 computed initializer."
3be8c2bf
MW
854 (let ((instance *instance-class*)
855 (func (effective-slot-prepare-function slot)))
856 (when func
857 (sequence-output (stream sequencer)
858 ((instance :object :prepare)
7d8d3a16
MW
859 (funcall func instance stream)))))
860 (call-next-method))
3be8c2bf 861
7d8d3a16
MW
862(defmethod hook-output ((slot effective-slot)
863 (reason (eql 'class)) sequencer)
6b875a6d 864 "Define a slot in a class object."
4b8e5c03 865 (with-slots ((class %class) (dslot slot)) slot
3be8c2bf
MW
866 (let ((instance *instance-class*)
867 (super (sod-slot-class dslot)))
868 (sequence-output (stream sequencer)
869 ((instance :object super :slots)
870 (output-class-initializer slot instance stream))))))
871
1f1d88f5 872;;;----- That's all, folks --------------------------------------------------