| 1 | ;;; -*-lisp-*- |
| 2 | ;;; |
| 3 | ;;; Output for classes |
| 4 | ;;; |
| 5 | ;;; (c) 2009 Straylight/Edgeware |
| 6 | ;;; |
| 7 | |
| 8 | ;;;----- Licensing notice --------------------------------------------------- |
| 9 | ;;; |
| 10 | ;;; This file is part of the Sensible Object Design, an object system for C. |
| 11 | ;;; |
| 12 | ;;; SOD is free software; you can redistribute it and/or modify |
| 13 | ;;; it under the terms of the GNU General Public License as published by |
| 14 | ;;; the Free Software Foundation; either version 2 of the License, or |
| 15 | ;;; (at your option) any later version. |
| 16 | ;;; |
| 17 | ;;; SOD is distributed in the hope that it will be useful, |
| 18 | ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 20 | ;;; GNU General Public License for more details. |
| 21 | ;;; |
| 22 | ;;; You should have received a copy of the GNU General Public License |
| 23 | ;;; along with SOD; if not, write to the Free Software Foundation, |
| 24 | ;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 25 | |
| 26 | (cl:in-package #:sod) |
| 27 | |
| 28 | ;;;-------------------------------------------------------------------------- |
| 29 | ;;; Walking the layout tree. |
| 30 | |
| 31 | (defmethod hook-output :after ((class sod-class) reason sequencer) |
| 32 | "Register hooks for the class layout, direct methods, effective methods, |
| 33 | and vtables." |
| 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 | |
| 40 | (defmethod hook-output :after ((ilayout ilayout) reason sequencer) |
| 41 | "Register hooks for the layout's ichains." |
| 42 | (with-slots (ichains) ilayout |
| 43 | (dolist (ichain ichains) (hook-output ichain reason sequencer)))) |
| 44 | |
| 45 | (defmethod hook-output :after ((ichain ichain) reason sequencer) |
| 46 | "Register hooks for the ichain body's items." |
| 47 | (dolist (item (ichain-body ichain)) (hook-output item reason sequencer))) |
| 48 | |
| 49 | (defmethod hook-output :after ((islots islots) reason sequencer) |
| 50 | "Register hooks for the islots structure's individual slots." |
| 51 | (dolist (slot (islots-slots islots)) (hook-output slot reason sequencer))) |
| 52 | |
| 53 | (defmethod hook-output :after ((vtable vtable) reason sequencer) |
| 54 | "Register hooks for the vtable body's items." |
| 55 | (with-slots (body) vtable |
| 56 | (dolist (item body) (hook-output item reason sequencer)))) |
| 57 | |
| 58 | (defmethod hook-output :after ((vtmsgs vtmsgs) reason sequencer) |
| 59 | "Register hooks for the vtmsgs structure's individual method entries." |
| 60 | (with-slots (entries) vtmsgs |
| 61 | (dolist (entry entries) (hook-output entry reason sequencer)))) |
| 62 | |
| 63 | ;;;-------------------------------------------------------------------------- |
| 64 | ;;; Class declarations. |
| 65 | |
| 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 | |
| 197 | (defmethod hook-output ((class sod-class) (reason (eql :h)) sequencer) |
| 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." |
| 216 | |
| 217 | ;; Main output sequencing. |
| 218 | (sequence-output (stream sequencer) |
| 219 | |
| 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) |
| 227 | (class :methods :start) (class :methods :defs) |
| 228 | (class :methods) (class :methods :end) |
| 229 | (class :ichains :start) (class :ichains :end) |
| 230 | (class :ilayout :start) (class :ilayout :slots) (class :ilayout :end) |
| 231 | (class :conversions) |
| 232 | (class :message-macros) |
| 233 | (class :object) |
| 234 | (:classes :end)) |
| 235 | |
| 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))) |
| 241 | |
| 242 | ;; Maybe generate an islots structure. |
| 243 | (when (sod-class-slots class) |
| 244 | (sequence-output (stream sequencer) |
| 245 | ((class :islots :start) |
| 246 | (format stream "/* Instance slots. */~@ |
| 247 | struct ~A {~%" |
| 248 | (islots-struct-tag class))) |
| 249 | ((class :islots :end) |
| 250 | (format stream "};~2%")))) |
| 251 | |
| 252 | ;; Declare the direct methods. |
| 253 | (when (sod-class-methods class) |
| 254 | (sequence-output (stream sequencer) |
| 255 | ((class :methods :start) |
| 256 | (format stream "/* Direct methods. */~%")) |
| 257 | ((class :methods :end) |
| 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) |
| 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)))) |
| 268 | |
| 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. |
| 275 | (when (some (lambda (message) |
| 276 | (or (keyword-message-p message) |
| 277 | (varargs-message-p message))) |
| 278 | (sod-class-messages class)) |
| 279 | (one-off-output 'varargs-macros sequencer :early-decls |
| 280 | (lambda (stream) |
| 281 | (format stream |
| 282 | "~%SOD__VARARGS_MACROS_PREAMBLE~%")))) |
| 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. */~%") |
| 294 | (dolist (entry (vtmsgs-entries vtmsgs)) |
| 295 | (emit-message-macro class entry stream)) |
| 296 | (terpri stream)))))) |
| 297 | |
| 298 | (defmethod hook-output :after ((class sod-class) (reason (eql :h)) sequencer) |
| 299 | "Register hooks to output CLASS's direct slots and messages." |
| 300 | |
| 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. |
| 306 | (hook-output (car (sod-class-vtables class)) 'vtmsgs sequencer)) |
| 307 | |
| 308 | ;;;-------------------------------------------------------------------------- |
| 309 | ;;; Instance structure. |
| 310 | |
| 311 | (defmethod hook-output ((slot sod-slot) (reason (eql 'islots)) sequencer) |
| 312 | "Declare the member for an individual slot within an `islots' structure." |
| 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 | |
| 319 | (defmethod hook-output ((ilayout ilayout) (reason (eql :h)) sequencer) |
| 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 |
| 325 | (sequence-output (stream sequencer) |
| 326 | ((class :ilayout :start) |
| 327 | (format stream "/* Instance layout. */~@ |
| 328 | struct ~A {~%" |
| 329 | (ilayout-struct-tag class))) |
| 330 | ((class :ilayout :end) |
| 331 | (format stream "};~2%"))))) |
| 332 | |
| 333 | (defmethod hook-output :after ((ilayout ilayout) (reason (eql :h)) sequencer) |
| 334 | "Register hooks to write chain members into the overall class layout." |
| 335 | (dolist (ichain (ilayout-ichains ilayout)) |
| 336 | (hook-output ichain 'ilayout sequencer))) |
| 337 | |
| 338 | (defmethod hook-output ((ichain ichain) (reason (eql :h)) sequencer) |
| 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)." |
| 347 | (with-slots ((class %class) chain-head chain-tail) ichain |
| 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) |
| 356 | (format stream "/* Instance chain structure. */~@ |
| 357 | struct ~A {~%" |
| 358 | (ichain-struct-tag chain-tail chain-head))) |
| 359 | ((class :ichain chain-head :end) |
| 360 | (format stream "};~2%") |
| 361 | (format stream "/* Union of equivalent superclass chains. */~@ |
| 362 | union ~A {~@ |
| 363 | ~:{ struct ~A ~A;~%~}~ |
| 364 | };~2%" |
| 365 | (ichain-union-tag chain-tail chain-head) |
| 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. |
| 370 | (mapcar (lambda (super) |
| 371 | (list (ichain-struct-tag super chain-head) |
| 372 | (sod-class-nickname super))) |
| 373 | (sod-class-chain chain-tail)))))))) |
| 374 | |
| 375 | (defmethod hook-output ((ichain ichain) (reason (eql 'ilayout)) sequencer) |
| 376 | "Declare the member for a class chain within the class layout." |
| 377 | (with-slots ((class %class) chain-head chain-tail) ichain |
| 378 | (sequence-output (stream sequencer) |
| 379 | ((class :ilayout :slots) |
| 380 | (format stream " union ~A ~A;~%" |
| 381 | (ichain-union-tag chain-tail chain-head) |
| 382 | (sod-class-nickname chain-head)))))) |
| 383 | |
| 384 | (defmethod hook-output ((vtptr vtable-pointer) (reason (eql :h)) sequencer) |
| 385 | "Declare the member for a vtable pointer within an `ichain' structure." |
| 386 | (with-slots ((class %class) chain-head chain-tail) vtptr |
| 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))))))) |
| 392 | |
| 393 | (defmethod hook-output ((islots islots) (reason (eql :h)) sequencer) |
| 394 | "Declare the member for a class's `islots' within an `ichain' structure." |
| 395 | (with-slots ((class %class) subclass slots) islots |
| 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)))))))) |
| 403 | |
| 404 | ;;;-------------------------------------------------------------------------- |
| 405 | ;;; Vtable structure. |
| 406 | |
| 407 | (defmethod hook-output ((method sod-method) (reason (eql :h)) sequencer) |
| 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 | |
| 413 | (with-slots ((class %class)) method |
| 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)) |
| 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 {~%~ |
| 427 | ~{ unsigned ~A: 1;~%~}~ |
| 428 | };~2%" |
| 429 | (direct-method-suppliedp-struct-tag method) |
| 430 | (mapcar #'argument-name keys)))))))) |
| 431 | |
| 432 | (defmethod hook-output ((vtable vtable) (reason (eql :h)) sequencer) |
| 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." |
| 438 | (with-slots ((class %class) chain-head chain-tail) vtable |
| 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) |
| 447 | (format stream "/* Vtable structure. */~@ |
| 448 | struct ~A {~%" |
| 449 | (vtable-struct-tag chain-tail chain-head))) |
| 450 | ((class :vtable chain-head :end) |
| 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)))))) |
| 464 | (sequence-output (stream sequencer) |
| 465 | ((class :vtable-externs) |
| 466 | (format stream "~@<extern const union ~A ~2I~_~A;~:>~%" |
| 467 | (vtable-union-tag chain-tail chain-head) |
| 468 | (vtable-name class chain-head)))))) |
| 469 | |
| 470 | (defmethod hook-output ((vtmsgs vtmsgs) (reason (eql :h)) sequencer) |
| 471 | "Declare the member for a class's `vtmsgs' within a `vtable' structure." |
| 472 | (with-slots ((class %class) subclass chain-head chain-tail) vtmsgs |
| 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))))))) |
| 479 | |
| 480 | (defmethod hook-output ((vtmsgs vtmsgs) (reason (eql 'vtmsgs)) sequencer) |
| 481 | "Define the `vtmsgs' structure for a class's method entry functions." |
| 482 | (when (vtmsgs-entries vtmsgs) |
| 483 | (with-slots ((class %class) subclass) vtmsgs |
| 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) |
| 491 | (format stream "/* Messages protocol from class ~A */~@ |
| 492 | struct ~A {~%" |
| 493 | class |
| 494 | (vtmsgs-struct-tag subclass class))) |
| 495 | ((subclass :vtmsgs class :end) |
| 496 | (format stream "};~2%")))))) |
| 497 | |
| 498 | (defmethod hook-output ((entry method-entry) |
| 499 | (reason (eql 'vtmsgs)) sequencer) |
| 500 | "Declare the member for a method entry within a `vtmsgs' structure." |
| 501 | (let* ((method (method-entry-effective-method entry)) |
| 502 | (message (effective-method-message method)) |
| 503 | (class (effective-method-class method)) |
| 504 | (function-type (method-entry-function-type entry)) |
| 505 | (commented-type (commentify-function-type function-type)) |
| 506 | (pointer-type (make-pointer-type commented-type))) |
| 507 | (sequence-output (stream sequencer) |
| 508 | ((class :vtmsgs (sod-message-class message) :slots) |
| 509 | (pprint-logical-block (stream nil :prefix " " :suffix ";") |
| 510 | (pprint-c-type pointer-type stream (method-entry-slot-name entry))) |
| 511 | (terpri stream))))) |
| 512 | |
| 513 | (defmethod hook-output ((cptr class-pointer) (reason (eql :h)) sequencer) |
| 514 | "Declare the member for a class-chain pointer within a `vtmsgs' structure." |
| 515 | (with-slots ((class %class) chain-head metaclass meta-chain-head) cptr |
| 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)))))))) |
| 523 | |
| 524 | (defmethod hook-output ((boff base-offset) (reason (eql :h)) sequencer) |
| 525 | "Declare the member for the base offset within a `vtmsgs' structure." |
| 526 | (with-slots ((class %class) chain-head) boff |
| 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)))))) |
| 531 | |
| 532 | (defmethod hook-output ((choff chain-offset) (reason (eql :h)) sequencer) |
| 533 | "Declare the member for a cross-chain offset within a `vtmsgs' structure." |
| 534 | (with-slots ((class %class) chain-head target-head) choff |
| 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))))))) |
| 540 | |
| 541 | ;;;-------------------------------------------------------------------------- |
| 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 | ;;;-------------------------------------------------------------------------- |
| 577 | ;;; Implementation output. |
| 578 | |
| 579 | (export '*instance-class*) |
| 580 | (defvar-unbound *instance-class* |
| 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.") |
| 589 | |
| 590 | (defmethod hook-output ((class sod-class) (reason (eql :c)) sequencer) |
| 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 | |
| 606 | (sequence-output (stream sequencer) |
| 607 | |
| 608 | :constraint |
| 609 | ((:classes :start) |
| 610 | (class :banner) |
| 611 | (class :direct-methods :start) (class :direct-methods :end) |
| 612 | (class :effective-methods) |
| 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) |
| 627 | (format stream "};~2%")))) |
| 628 | |
| 629 | (defmethod hook-output :after ((class sod-class) (reason (eql :c)) sequencer) |
| 630 | "Register hooks to initialize the class object structure." |
| 631 | (let ((*instance-class* class)) |
| 632 | (hook-output (sod-class-ilayout (sod-class-metaclass class)) |
| 633 | 'class sequencer))) |
| 634 | |
| 635 | ;;;-------------------------------------------------------------------------- |
| 636 | ;;; Direct and effective methods. |
| 637 | |
| 638 | (defmethod hook-output ((method delegating-direct-method) |
| 639 | (reason (eql :c)) sequencer) |
| 640 | "Define the `CALL_NEXT_METHOD' macro around a `delegating-direct-method'." |
| 641 | (with-slots ((class %class) body) method |
| 642 | (unless body |
| 643 | (return-from hook-output)) |
| 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) |
| 651 | (format stream "#undef CALL_NEXT_METHOD~%")))) |
| 652 | (call-next-method)) |
| 653 | |
| 654 | (defmethod hook-output ((method sod-method) (reason (eql :c)) sequencer) |
| 655 | "Define a direct method function." |
| 656 | (with-slots ((class %class) role body message) method |
| 657 | (unless body |
| 658 | (return-from hook-output)) |
| 659 | (sequence-output (stream sequencer) |
| 660 | :constraint ((class :direct-methods :start) |
| 661 | (class :direct-method method :banner) |
| 662 | (class :direct-method method :start) |
| 663 | (class :direct-method method :body) |
| 664 | (class :direct-method method :end) |
| 665 | (class :direct-methods :end)) |
| 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)) |
| 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 | |
| 685 | (defmethod hook-output ((method basic-effective-method) |
| 686 | (reason (eql :c)) sequencer) |
| 687 | "Define an effective method's functions. |
| 688 | |
| 689 | Specifically, the method-entry functions and any auxiliary functions |
| 690 | needed to stitch everything together." |
| 691 | (with-slots ((class %class) functions) method |
| 692 | (sequence-output (stream sequencer) |
| 693 | ((class :effective-methods) |
| 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)) |
| 705 | (format stream "~{ unsigned ~A__suppliedp: 1;~%~}" |
| 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%"))) |
| 712 | (dolist (func functions) |
| 713 | (write func :stream stream :escape nil :circle nil)))))) |
| 714 | |
| 715 | ;;;-------------------------------------------------------------------------- |
| 716 | ;;; Vtables. |
| 717 | |
| 718 | (defmethod hook-output ((vtable vtable) (reason (eql :c)) sequencer) |
| 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." |
| 723 | (with-slots ((class %class) chain-head chain-tail) vtable |
| 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. */~@ |
| 731 | const union ~A ~A = { {~%" |
| 732 | chain-head |
| 733 | (vtable-union-tag chain-tail chain-head) |
| 734 | (vtable-name class chain-head))) |
| 735 | ((class :vtable chain-head :end) |
| 736 | (format stream "} };~2%"))))) |
| 737 | |
| 738 | (defmethod hook-output ((cptr class-pointer) (reason (eql :c)) sequencer) |
| 739 | "Drop a class pointer into a vtable definition." |
| 740 | (with-slots ((class %class) chain-head metaclass meta-chain-head) cptr |
| 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) |
| 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") |
| 751 | class |
| 752 | (sod-class-nickname meta-chain-head) |
| 753 | (sod-class-nickname metaclass)))))) |
| 754 | |
| 755 | (defmethod hook-output ((boff base-offset) (reason (eql :c)) sequencer) |
| 756 | "Drop a base offset into a vtable definition." |
| 757 | (with-slots ((class %class) chain-head) boff |
| 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) |
| 763 | (format stream " /* ~21@A = */ offsetof(struct ~A, ~A),~%" |
| 764 | "_base" |
| 765 | (ilayout-struct-tag class) |
| 766 | (sod-class-nickname chain-head)))))) |
| 767 | |
| 768 | (defmethod hook-output ((choff chain-offset) (reason (eql :c)) sequencer) |
| 769 | "Drop a cross-chain offset into a vtable definition." |
| 770 | (with-slots ((class %class) chain-head target-head) choff |
| 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) |
| 776 | (format stream " /* ~21@A = */ SOD_OFFSETDIFF(struct ~A, ~A, ~A),~%" |
| 777 | (format nil "_off_~A" (sod-class-nickname target-head)) |
| 778 | (ilayout-struct-tag class) |
| 779 | (sod-class-nickname chain-head) |
| 780 | (sod-class-nickname target-head)))))) |
| 781 | |
| 782 | (defmethod hook-output ((vtmsgs vtmsgs) (reason (eql :c)) sequencer) |
| 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." |
| 787 | (with-slots ((class %class) subclass chain-head) vtmsgs |
| 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 | |
| 800 | (defmethod hook-output ((entry method-entry) (reason (eql :c)) sequencer) |
| 801 | "Define a method-entry pointer in a vtable." |
| 802 | (with-slots ((method %method) chain-head chain-tail role) entry |
| 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) |
| 808 | (format stream " /* ~19@A = */ ~A,~%" |
| 809 | (method-entry-slot-name entry) |
| 810 | (method-entry-function-name method chain-head role))))))) |
| 811 | |
| 812 | ;;;-------------------------------------------------------------------------- |
| 813 | ;;; Filling in the class object. |
| 814 | |
| 815 | (defmethod hook-output ((ichain ichain) (reason (eql 'class)) sequencer) |
| 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." |
| 820 | (with-slots ((class %class) chain-head) ichain |
| 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 | |
| 832 | (defmethod hook-output ((islots islots) (reason (eql 'class)) sequencer) |
| 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." |
| 837 | (with-slots ((class %class)) islots |
| 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 | |
| 850 | (defmethod hook-output ((vtptr vtable-pointer) |
| 851 | (reason (eql 'class)) sequencer) |
| 852 | "Define a vtable pointer in a class object." |
| 853 | (with-slots ((class %class) chain-head chain-tail) vtptr |
| 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) |
| 859 | (format stream " /* ~17@A = */ &~A.~A,~%" |
| 860 | "_vt" |
| 861 | (vtable-name class chain-head) |
| 862 | (sod-class-nickname chain-tail)))))) |
| 863 | |
| 864 | (defgeneric output-class-initializer (slot instance stream) |
| 865 | (:documentation |
| 866 | "Define an individual slot in a class object.") |
| 867 | (:method ((slot sod-class-effective-slot) (instance sod-class) stream) |
| 868 | "If this slot has an initializer function, then call it; otherwise try to |
| 869 | find an initializer as usual." |
| 870 | (let ((func (effective-slot-initializer-function slot)) |
| 871 | (direct-slot (effective-slot-direct-slot slot))) |
| 872 | (if func |
| 873 | (format stream " /* ~15@A = */ ~A,~%" |
| 874 | (sod-slot-name direct-slot) |
| 875 | (funcall func instance)) |
| 876 | (call-next-method)))) |
| 877 | (:method ((slot effective-slot) (instance sod-class) stream) |
| 878 | "Initialize a class slot by looking up an applicable initializer." |
| 879 | (let ((init (find-class-initializer slot instance)) |
| 880 | (direct-slot (effective-slot-direct-slot slot))) |
| 881 | (format stream " /* ~15@A = */ ~A,~%" |
| 882 | (sod-slot-name direct-slot) |
| 883 | (sod-initializer-value init))))) |
| 884 | |
| 885 | (defmethod hook-output ((slot sod-class-effective-slot) |
| 886 | (reason (eql 'class)) sequencer) |
| 887 | "Write any necessary preparatory definitions for a class slot with a |
| 888 | computed initializer." |
| 889 | (let ((instance *instance-class*) |
| 890 | (func (effective-slot-prepare-function slot))) |
| 891 | (when func |
| 892 | (sequence-output (stream sequencer) |
| 893 | ((instance :object :prepare) |
| 894 | (funcall func instance stream))))) |
| 895 | (call-next-method)) |
| 896 | |
| 897 | (defmethod hook-output ((slot effective-slot) |
| 898 | (reason (eql 'class)) sequencer) |
| 899 | "Define a slot in a class object." |
| 900 | (with-slots ((class %class) (dslot slot)) slot |
| 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 | |
| 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 | |
| 1037 | ;;;----- That's all, folks -------------------------------------------------- |