Commit | Line | Data |
---|---|---|
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 | ||
28 | ;;;-------------------------------------------------------------------------- | |
6e2d4b52 MW |
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 | ||
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 | ||
220 | :constraint | |
1f1d88f5 MW |
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 MW |
540 | |
541 | ;;;-------------------------------------------------------------------------- | |
00d59354 MW |
542 | ;;; Static instance declarations. |
543 | ||
544 | (export 'declare-static-instance) | |
545 | (defgeneric declare-static-instance (instance stream) | |
546 | (:documentation | |
547 | "Write a declaration for the static INSTANCE to STREAM. | |
548 | ||
549 | Note that, according to whether the instance is external or private, this | |
550 | may be written as part of the `:h' or `:c' reasons.")) | |
551 | (defmethod declare-static-instance (instance stream) | |
552 | (with-slots ((class %class) name externp constp) instance | |
553 | (format stream "~:[static~;extern~] ~:[~;const ~]struct ~ | |
554 | ~A ~A__instance;~%~ | |
555 | #define ~A (&~A__instance.~A.~A)~%" | |
556 | externp constp (ilayout-struct-tag class) name | |
557 | name name (sod-class-nickname (sod-class-chain-head class)) | |
558 | (sod-class-nickname class)))) | |
559 | ||
560 | (defmethod hook-output | |
561 | ((instance static-instance) (reason (eql :h)) sequencer) | |
562 | "Write an `extern' declaration for an external static instance." | |
563 | (with-slots (externp) instance | |
564 | (when externp | |
565 | (one-off-output 'static-instances-banner sequencer | |
566 | '(:static-instances :start) | |
567 | (lambda (stream) | |
568 | (banner "Public static instances" stream))) | |
569 | (one-off-output 'static-instances-end sequencer | |
570 | '(:static-instances :end) | |
571 | #'terpri) | |
572 | (sequence-output (stream sequencer) | |
573 | (:static-instances | |
574 | (declare-static-instance instance stream)))))) | |
575 | ||
576 | ;;;-------------------------------------------------------------------------- | |
3be8c2bf MW |
577 | ;;; Implementation output. |
578 | ||
6e2d4b52 | 579 | (export '*instance-class*) |
944bf936 | 580 | (defvar-unbound *instance-class* |
4b856491 MW |
581 | "The class currently being output. |
582 | ||
583 | This is bound during the `hook-output' traversal of a class layout for | |
584 | `:c' output, since some of the objects traversed actually `belong' to | |
585 | superclasses and there's no other way to find out what the reference class | |
586 | actually is. | |
587 | ||
588 | It may be bound at other times.") | |
3be8c2bf | 589 | |
7d8d3a16 | 590 | (defmethod hook-output ((class sod-class) (reason (eql :c)) sequencer) |
6b875a6d MW |
591 | "Write the skeleton of a class definition. |
592 | ||
593 | Most of the work is done by other methods. | |
594 | ||
595 | * The direct methods are defined by the `sod-method' objects. | |
596 | ||
597 | * The effective method functions and related structures are defined by | |
598 | the effective method objects. | |
599 | ||
600 | * The vtable structures are initialized by the vtable objects and their | |
601 | component items. | |
602 | ||
603 | * The class structure and its associated tables are initialized by the | |
604 | metaclass's layout objects." | |
605 | ||
3be8c2bf MW |
606 | (sequence-output (stream sequencer) |
607 | ||
608 | :constraint | |
609 | ((:classes :start) | |
610 | (class :banner) | |
611 | (class :direct-methods :start) (class :direct-methods :end) | |
a07d8d00 | 612 | (class :effective-methods) |
3be8c2bf MW |
613 | (class :vtables :start) (class :vtables :end) |
614 | (class :object :prepare) (class :object :start) (class :object :end) | |
615 | (:classes :end)) | |
616 | ||
617 | ((class :banner) | |
618 | (banner (format nil "Class ~A" class) stream)) | |
619 | ||
620 | ((class :object :start) | |
621 | (format stream "~ | |
622 | /* The class object. */ | |
623 | const struct ~A ~A__classobj = {~%" | |
624 | (ilayout-struct-tag (sod-class-metaclass class)) | |
625 | class)) | |
626 | ((class :object :end) | |
7d8d3a16 | 627 | (format stream "};~2%")))) |
3be8c2bf | 628 | |
7d8d3a16 | 629 | (defmethod hook-output :after ((class sod-class) (reason (eql :c)) sequencer) |
6b875a6d | 630 | "Register hooks to initialize the class object structure." |
3be8c2bf | 631 | (let ((*instance-class* class)) |
dea4d055 | 632 | (hook-output (sod-class-ilayout (sod-class-metaclass class)) |
6e2d4b52 | 633 | 'class sequencer))) |
3be8c2bf MW |
634 | |
635 | ;;;-------------------------------------------------------------------------- | |
9ec578d9 | 636 | ;;; Direct and effective methods. |
3be8c2bf | 637 | |
7d8d3a16 MW |
638 | (defmethod hook-output ((method delegating-direct-method) |
639 | (reason (eql :c)) sequencer) | |
6b875a6d | 640 | "Define the `CALL_NEXT_METHOD' macro around a `delegating-direct-method'." |
4b8e5c03 | 641 | (with-slots ((class %class) body) method |
3be8c2bf | 642 | (unless body |
dea4d055 | 643 | (return-from hook-output)) |
3be8c2bf MW |
644 | (sequence-output (stream sequencer) |
645 | ((class :direct-method method :start) | |
646 | (format stream "#define CALL_NEXT_METHOD (next_method(~{~A~^, ~}))~%" | |
647 | (mapcar #'argument-name | |
648 | (c-function-arguments (sod-method-next-method-type | |
649 | method))))) | |
650 | ((class :direct-method method :end) | |
7d8d3a16 MW |
651 | (format stream "#undef CALL_NEXT_METHOD~%")))) |
652 | (call-next-method)) | |
3be8c2bf | 653 | |
7d8d3a16 | 654 | (defmethod hook-output ((method sod-method) (reason (eql :c)) sequencer) |
6b875a6d | 655 | "Define a direct method function." |
7de8c666 | 656 | (with-slots ((class %class) role body message) method |
3be8c2bf | 657 | (unless body |
dea4d055 | 658 | (return-from hook-output)) |
3be8c2bf MW |
659 | (sequence-output (stream sequencer) |
660 | :constraint ((class :direct-methods :start) | |
7de8c666 | 661 | (class :direct-method method :banner) |
3be8c2bf MW |
662 | (class :direct-method method :start) |
663 | (class :direct-method method :body) | |
664 | (class :direct-method method :end) | |
665 | (class :direct-methods :end)) | |
7de8c666 MW |
666 | ((class :direct-method method :banner) |
667 | (format-banner-comment stream "Direct ~@[~(~A~) ~]method ~:_~ | |
668 | on `~A.~A' ~:_defined by `~A'." | |
669 | role | |
670 | (sod-class-nickname | |
671 | (sod-message-class message)) | |
672 | (sod-message-name message) | |
673 | class) | |
674 | (fresh-line stream)) | |
3be8c2bf MW |
675 | ((class :direct-method method :body) |
676 | (pprint-c-type (sod-method-function-type method) | |
677 | stream | |
678 | (sod-method-function-name method)) | |
679 | (format stream "~&{~%") | |
680 | (write body :stream stream :pretty nil :escape nil) | |
681 | (format stream "~&}~%")) | |
682 | ((class :direct-method method :end) | |
683 | (terpri stream))))) | |
684 | ||
7d8d3a16 MW |
685 | (defmethod hook-output ((method basic-effective-method) |
686 | (reason (eql :c)) sequencer) | |
6b875a6d MW |
687 | "Define an effective method's functions. |
688 | ||
689 | Specifically, the method-entry functions and any auxiliary functions | |
690 | needed to stitch everything together." | |
4b8e5c03 | 691 | (with-slots ((class %class) functions) method |
dea4d055 MW |
692 | (sequence-output (stream sequencer) |
693 | ((class :effective-methods) | |
43073476 MW |
694 | (let* ((keys (effective-method-keywords method)) |
695 | (message (effective-method-message method)) | |
696 | (msg-class (sod-message-class message))) | |
697 | (when keys | |
698 | (format-banner-comment stream "Keyword argument structure ~:_~ | |
699 | for `~A.~A' ~:_on class `~A'." | |
700 | (sod-class-nickname msg-class) | |
701 | (sod-message-name message) | |
702 | class) | |
703 | (format stream "~&struct ~A {~%" | |
704 | (effective-method-keyword-struct-tag method)) | |
fd040f06 | 705 | (format stream "~{ unsigned ~A__suppliedp: 1;~%~}" |
43073476 MW |
706 | (mapcar #'argument-name keys)) |
707 | (dolist (key keys) | |
708 | (write-string " " stream) | |
709 | (pprint-c-type (argument-type key) stream (argument-name key)) | |
710 | (format stream ";~%")) | |
711 | (format stream "};~2%"))) | |
dea4d055 MW |
712 | (dolist (func functions) |
713 | (write func :stream stream :escape nil :circle nil)))))) | |
714 | ||
3be8c2bf | 715 | ;;;-------------------------------------------------------------------------- |
a07d8d00 MW |
716 | ;;; Vtables. |
717 | ||
7d8d3a16 | 718 | (defmethod hook-output ((vtable vtable) (reason (eql :c)) sequencer) |
6b875a6d MW |
719 | "Define a vtable structure. |
720 | ||
721 | Here we just provide the outermost structure. It gets filled in by the | |
722 | vtable object's body items." | |
4b8e5c03 | 723 | (with-slots ((class %class) chain-head chain-tail) vtable |
a07d8d00 MW |
724 | (sequence-output (stream sequencer) |
725 | :constraint ((class :vtables :start) | |
726 | (class :vtable chain-head :start) | |
727 | (class :vtable chain-head :end) | |
728 | (class :vtables :end)) | |
729 | ((class :vtable chain-head :start) | |
730 | (format stream "/* Vtable for ~A chain. */~@ | |
c2438e62 | 731 | const union ~A ~A = { {~%" |
a07d8d00 | 732 | chain-head |
c2438e62 | 733 | (vtable-union-tag chain-tail chain-head) |
9ec578d9 | 734 | (vtable-name class chain-head))) |
a07d8d00 | 735 | ((class :vtable chain-head :end) |
c2438e62 | 736 | (format stream "} };~2%"))))) |
a07d8d00 | 737 | |
7d8d3a16 | 738 | (defmethod hook-output ((cptr class-pointer) (reason (eql :c)) sequencer) |
6b875a6d | 739 | "Drop a class pointer into a vtable definition." |
4b8e5c03 | 740 | (with-slots ((class %class) chain-head metaclass meta-chain-head) cptr |
a07d8d00 MW |
741 | (sequence-output (stream sequencer) |
742 | :constraint ((class :vtable chain-head :start) | |
743 | (class :vtable chain-head :class-pointer metaclass) | |
744 | (class :vtable chain-head :end)) | |
745 | ((class :vtable chain-head :class-pointer metaclass) | |
9ec578d9 MW |
746 | (format stream " /* ~21@A = */ &~A__classobj.~A.~A,~%" |
747 | (if (sod-class-direct-superclasses meta-chain-head) | |
748 | (format nil "_cls_~A" | |
749 | (sod-class-nickname meta-chain-head)) | |
750 | "_class") | |
fc5d9486 | 751 | class |
a07d8d00 MW |
752 | (sod-class-nickname meta-chain-head) |
753 | (sod-class-nickname metaclass)))))) | |
754 | ||
7d8d3a16 | 755 | (defmethod hook-output ((boff base-offset) (reason (eql :c)) sequencer) |
6b875a6d | 756 | "Drop a base offset into a vtable definition." |
4b8e5c03 | 757 | (with-slots ((class %class) chain-head) boff |
a07d8d00 MW |
758 | (sequence-output (stream sequencer) |
759 | :constraint ((class :vtable chain-head :start) | |
760 | (class :vtable chain-head :base-offset) | |
761 | (class :vtable chain-head :end)) | |
762 | ((class :vtable chain-head :base-offset) | |
9ec578d9 MW |
763 | (format stream " /* ~21@A = */ offsetof(struct ~A, ~A),~%" |
764 | "_base" | |
a07d8d00 MW |
765 | (ilayout-struct-tag class) |
766 | (sod-class-nickname chain-head)))))) | |
767 | ||
7d8d3a16 | 768 | (defmethod hook-output ((choff chain-offset) (reason (eql :c)) sequencer) |
6b875a6d | 769 | "Drop a cross-chain offset into a vtable definition." |
4b8e5c03 | 770 | (with-slots ((class %class) chain-head target-head) choff |
a07d8d00 MW |
771 | (sequence-output (stream sequencer) |
772 | :constraint ((class :vtable chain-head :start) | |
773 | (class :vtable chain-head :chain-offset target-head) | |
774 | (class :vtable chain-head :end)) | |
775 | ((class :vtable chain-head :chain-offset target-head) | |
9ec578d9 MW |
776 | (format stream " /* ~21@A = */ SOD_OFFSETDIFF(struct ~A, ~A, ~A),~%" |
777 | (format nil "_off_~A" (sod-class-nickname target-head)) | |
a07d8d00 MW |
778 | (ilayout-struct-tag class) |
779 | (sod-class-nickname chain-head) | |
780 | (sod-class-nickname target-head)))))) | |
781 | ||
7d8d3a16 | 782 | (defmethod hook-output ((vtmsgs vtmsgs) (reason (eql :c)) sequencer) |
6b875a6d MW |
783 | "Define the method entry pointers for a superclass's messages. |
784 | ||
785 | We only provide the outer structure. It gets filled in by the | |
786 | `method-entry' objects." | |
4b8e5c03 | 787 | (with-slots ((class %class) subclass chain-head) vtmsgs |
a07d8d00 MW |
788 | (sequence-output (stream sequencer) |
789 | :constraint ((subclass :vtable chain-head :start) | |
790 | (subclass :vtable chain-head :vtmsgs class :start) | |
791 | (subclass :vtable chain-head :vtmsgs class :slots) | |
792 | (subclass :vtable chain-head :vtmsgs class :end) | |
793 | (subclass :vtable chain-head :end)) | |
794 | ((subclass :vtable chain-head :vtmsgs class :start) | |
795 | (format stream " { /* Method entries for ~A messages. */~%" | |
796 | class)) | |
797 | ((subclass :vtable chain-head :vtmsgs class :end) | |
798 | (format stream " },~%"))))) | |
799 | ||
7d8d3a16 | 800 | (defmethod hook-output ((entry method-entry) (reason (eql :c)) sequencer) |
6b875a6d | 801 | "Define a method-entry pointer in a vtable." |
4b8e5c03 | 802 | (with-slots ((method %method) chain-head chain-tail role) entry |
a07d8d00 MW |
803 | (let* ((message (effective-method-message method)) |
804 | (class (effective-method-class method)) | |
805 | (super (sod-message-class message))) | |
806 | (sequence-output (stream sequencer) | |
807 | ((class :vtable chain-head :vtmsgs super :slots) | |
9ec578d9 | 808 | (format stream " /* ~19@A = */ ~A,~%" |
b426ab51 MW |
809 | (method-entry-slot-name entry) |
810 | (method-entry-function-name method chain-head role))))))) | |
a07d8d00 MW |
811 | |
812 | ;;;-------------------------------------------------------------------------- | |
3be8c2bf MW |
813 | ;;; Filling in the class object. |
814 | ||
7d8d3a16 | 815 | (defmethod hook-output ((ichain ichain) (reason (eql 'class)) sequencer) |
6b875a6d MW |
816 | "Define an instance chain of a class object. |
817 | ||
818 | Here we only provide the outer structure. It gets filled in by the | |
819 | `ichain' object's body items." | |
4b8e5c03 | 820 | (with-slots ((class %class) chain-head) ichain |
3be8c2bf MW |
821 | (sequence-output (stream sequencer) |
822 | :constraint ((*instance-class* :object :start) | |
823 | (*instance-class* :object chain-head :ichain :start) | |
824 | (*instance-class* :object chain-head :ichain :end) | |
825 | (*instance-class* :object :end)) | |
826 | ((*instance-class* :object chain-head :ichain :start) | |
827 | (format stream " { { /* ~A ichain */~%" | |
828 | (sod-class-nickname chain-head))) | |
829 | ((*instance-class* :object chain-head :ichain :end) | |
830 | (format stream " } },~%"))))) | |
831 | ||
7d8d3a16 | 832 | (defmethod hook-output ((islots islots) (reason (eql 'class)) sequencer) |
6b875a6d MW |
833 | "Define an instance's slots in a class object. |
834 | ||
835 | Here we only provide the outer structure. It gets filled in by the | |
836 | individual slot objects." | |
4b8e5c03 | 837 | (with-slots ((class %class)) islots |
3be8c2bf MW |
838 | (let ((chain-head (sod-class-chain-head class))) |
839 | (sequence-output (stream sequencer) | |
840 | :constraint ((*instance-class* :object chain-head :ichain :start) | |
841 | (*instance-class* :object class :slots :start) | |
842 | (*instance-class* :object class :slots) | |
843 | (*instance-class* :object class :slots :end) | |
844 | (*instance-class* :object chain-head :ichain :end)) | |
845 | ((*instance-class* :object class :slots :start) | |
846 | (format stream " { /* Class ~A */~%" class)) | |
847 | ((*instance-class* :object class :slots :end) | |
848 | (format stream " },~%")))))) | |
849 | ||
7d8d3a16 MW |
850 | (defmethod hook-output ((vtptr vtable-pointer) |
851 | (reason (eql 'class)) sequencer) | |
6b875a6d | 852 | "Define a vtable pointer in a class object." |
4b8e5c03 | 853 | (with-slots ((class %class) chain-head chain-tail) vtptr |
3be8c2bf MW |
854 | (sequence-output (stream sequencer) |
855 | :constraint ((*instance-class* :object chain-head :ichain :start) | |
856 | (*instance-class* :object chain-head :vtable) | |
857 | (*instance-class* :object chain-head :ichain :end)) | |
858 | ((*instance-class* :object chain-head :vtable) | |
c2438e62 MW |
859 | (format stream " /* ~17@A = */ &~A.~A,~%" |
860 | "_vt" | |
861 | (vtable-name class chain-head) | |
862 | (sod-class-nickname chain-tail)))))) | |
3be8c2bf | 863 | |
3be8c2bf | 864 | (defgeneric output-class-initializer (slot instance stream) |
6b875a6d MW |
865 | (:documentation |
866 | "Define an individual slot in a class object.") | |
3be8c2bf | 867 | (:method ((slot sod-class-effective-slot) (instance sod-class) stream) |
6b875a6d MW |
868 | "If this slot has an initializer function, then call it; otherwise try to |
869 | find an initializer as usual." | |
9ec578d9 MW |
870 | (let ((func (effective-slot-initializer-function slot)) |
871 | (direct-slot (effective-slot-direct-slot slot))) | |
3be8c2bf | 872 | (if func |
9ec578d9 MW |
873 | (format stream " /* ~15@A = */ ~A,~%" |
874 | (sod-slot-name direct-slot) | |
875 | (funcall func instance)) | |
3be8c2bf MW |
876 | (call-next-method)))) |
877 | (:method ((slot effective-slot) (instance sod-class) stream) | |
6b875a6d | 878 | "Initialize a class slot by looking up an applicable initializer." |
9ec578d9 MW |
879 | (let ((init (find-class-initializer slot instance)) |
880 | (direct-slot (effective-slot-direct-slot slot))) | |
a888e3ac MW |
881 | (format stream " /* ~15@A = */ ~A,~%" |
882 | (sod-slot-name direct-slot) | |
883 | (sod-initializer-value init))))) | |
3be8c2bf | 884 | |
7d8d3a16 MW |
885 | (defmethod hook-output ((slot sod-class-effective-slot) |
886 | (reason (eql 'class)) sequencer) | |
6b875a6d MW |
887 | "Write any necessary preparatory definitions for a class slot with a |
888 | computed initializer." | |
3be8c2bf MW |
889 | (let ((instance *instance-class*) |
890 | (func (effective-slot-prepare-function slot))) | |
891 | (when func | |
892 | (sequence-output (stream sequencer) | |
893 | ((instance :object :prepare) | |
7d8d3a16 MW |
894 | (funcall func instance stream))))) |
895 | (call-next-method)) | |
3be8c2bf | 896 | |
7d8d3a16 MW |
897 | (defmethod hook-output ((slot effective-slot) |
898 | (reason (eql 'class)) sequencer) | |
6b875a6d | 899 | "Define a slot in a class object." |
4b8e5c03 | 900 | (with-slots ((class %class) (dslot slot)) slot |
3be8c2bf MW |
901 | (let ((instance *instance-class*) |
902 | (super (sod-slot-class dslot))) | |
903 | (sequence-output (stream sequencer) | |
904 | ((instance :object super :slots) | |
905 | (output-class-initializer slot instance stream)))))) | |
906 | ||
00d59354 MW |
907 | ;;;-------------------------------------------------------------------------- |
908 | ;;; Static instances. | |
909 | ||
910 | (export '*static-instance*) | |
911 | (defvar-unbound *static-instance* | |
912 | "The static instance currently being output. | |
913 | ||
914 | This is bound during the `hook-output' traversal of a static instance for | |
915 | `:c', since the slots traversed need to be able to look up initializers | |
916 | from the static instance definition.") | |
917 | ||
918 | (defmethod hook-output ((instance static-instance) | |
919 | (reason (eql :c)) sequencer) | |
920 | "Write a static instance definition." | |
921 | (with-slots (externp) instance | |
922 | (one-off-output 'static-instances-banner sequencer | |
923 | '(:static-instances :start) | |
924 | (lambda (stream) | |
925 | (banner "Static instance definitions" stream))) | |
926 | (unless externp | |
927 | (one-off-output 'static-instances-forward sequencer | |
928 | '(:static-instances :start) | |
929 | (lambda (stream) | |
930 | (format stream "/* Forward declarations. */~%"))) | |
931 | (one-off-output 'static-instances-forward-gap sequencer | |
932 | '(:static-instances :gap) | |
933 | #'terpri) | |
934 | (sequence-output (stream sequencer) | |
935 | ((:static-instances :decls) | |
936 | (declare-static-instance instance stream)))))) | |
937 | ||
938 | (defmethod hook-output ((class sod-class) | |
939 | (reason (eql 'static-instance)) sequencer) | |
940 | "Output the framing around a static instance initializer." | |
941 | (let ((instance *static-instance*)) | |
942 | (with-slots ((class %class) name externp constp) instance | |
943 | (sequence-output (stream sequencer) | |
944 | :constraint ((:static-instances :gap) | |
945 | (*static-instance* :start) | |
946 | (*static-instance* :end) | |
947 | (:static-instances :end)) | |
948 | ((*static-instance* :start) | |
949 | (format stream "/* Static instance `~A'. */~%~ | |
950 | ~:[static ~;~]~:[~;const ~]~ | |
951 | struct ~A ~A__instance = {~%" | |
952 | name | |
953 | externp constp | |
954 | (ilayout-struct-tag class) name)) | |
955 | ((*static-instance* :end) | |
956 | (format stream "};~2%")))))) | |
957 | ||
958 | (defmethod hook-output ((ichain ichain) | |
959 | (reason (eql 'static-instance)) sequencer) | |
960 | "Output the initializer for an ichain." | |
961 | (with-slots ((class %class) chain-head chain-tail) ichain | |
962 | (sequence-output (stream sequencer) | |
963 | :constraint ((*static-instance* :start) | |
964 | (*static-instance* :ichain chain-head :start) | |
965 | (*static-instance* :ichain chain-head :end) | |
966 | (*static-instance* :end)) | |
967 | ((*static-instance* :ichain chain-head :start) | |
968 | (format stream " { { /* ~A ichain */~%" | |
969 | (sod-class-nickname chain-head))) | |
970 | ((*static-instance* :ichain chain-head :end) | |
971 | (format stream " } },~%"))))) | |
972 | ||
973 | (defmethod hook-output ((islots islots) | |
974 | (reason (eql 'static-instance)) sequencer) | |
975 | "Initialize a static instance's slots." | |
976 | (with-slots ((class %class)) islots | |
977 | (let ((chain-head (sod-class-chain-head class))) | |
978 | (sequence-output (stream sequencer) | |
979 | :constraint | |
980 | ((*static-instance* :ichain chain-head :start) | |
981 | (*static-instance* :slots class :start) | |
982 | (*static-instance* :slots class) | |
983 | (*static-instance* :slots class :end) | |
984 | (*static-instance* :ichain chain-head :end)) | |
985 | ((*static-instance* :slots class :start) | |
986 | (format stream " { /* Class ~A */~%" class)) | |
987 | ((*static-instance* :slots class :end) | |
988 | (format stream " },~%")))))) | |
989 | ||
990 | (defmethod hook-output ((vtptr vtable-pointer) | |
991 | (reason (eql 'static-instance)) sequencer) | |
992 | "Initialize a vtable pointer in a static instance.." | |
993 | (with-slots ((class %class) chain-head chain-tail) vtptr | |
994 | (sequence-output (stream sequencer) | |
995 | :constraint ((*static-instance* :ichain chain-head :start) | |
996 | (*static-instance* :vtable chain-head) | |
997 | (*static-instance* :ichain chain-head :end)) | |
998 | ((*static-instance* :vtable chain-head) | |
999 | (format stream " /* ~17@A = */ &~A.~A,~%" | |
1000 | "_vt" | |
1001 | (vtable-name class chain-head) | |
1002 | (sod-class-nickname chain-tail)))))) | |
1003 | ||
1004 | (export 'output-static-instance-initializer) | |
1005 | (defgeneric output-static-instance-initializer (instance slot stream) | |
1006 | (:documentation | |
1007 | "Output an initializer for an effective SLOT in a static INSTANCE.")) | |
1008 | (defmethod output-static-instance-initializer ((instance static-instance) | |
1009 | (slot effective-slot) | |
1010 | stream) | |
1011 | (let* ((direct-slot (effective-slot-direct-slot slot)) | |
1012 | (init (or (find direct-slot | |
1013 | (static-instance-initializers instance) | |
1014 | :key #'sod-initializer-slot) | |
1015 | (effective-slot-initializer slot)))) | |
1016 | (format stream " /* ~15@A = */ ~A,~%" | |
1017 | (sod-slot-name direct-slot) | |
1018 | (sod-initializer-value init)))) | |
1019 | ||
1020 | (defmethod hook-output ((slot effective-slot) | |
1021 | (reason (eql 'static-instance)) sequencer) | |
1022 | "Initialize a slot in a static instance." | |
1023 | (with-slots ((class %class) initializers) *static-instance* | |
1024 | (with-slots ((dslot slot)) slot | |
1025 | (let ((super (sod-slot-class dslot)) | |
1026 | (instance *static-instance*)) | |
1027 | (sequence-output (stream sequencer) | |
1028 | ((instance :slots super) | |
1029 | (output-static-instance-initializer instance slot stream))))))) | |
1030 | ||
1031 | (defmethod hook-output :after | |
1032 | ((instance static-instance) (reason (eql :c)) sequencer) | |
1033 | (with-slots ((class %class)) instance | |
1034 | (let ((*static-instance* instance)) | |
1035 | (hook-output class 'static-instance sequencer)))) | |
1036 | ||
1f1d88f5 | 1037 | ;;;----- That's all, folks -------------------------------------------------- |