lib/sod-hosted.c (sod_makev): Use two statements rather than tricky expression.
[sod] / src / builtin.lisp
CommitLineData
dea4d055
MW
1;;; -*-lisp-*-
2;;;
3;;; Builtin module provides the root of the class graph
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.
dea4d055
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;;;--------------------------------------------------------------------------
29;;; Infrastructure.
30
31(defvar *class-slot-alist* nil)
32
33(defun add-class-slot-function (name function)
ea578bb4 34 "Attach a slot function to the `*class-slot-alist*'.
dea4d055
MW
35
36 The FUNCTION is invoked with one argument, which is a `sod-class' object
37 to which it should add a slot. If a function with the same NAME is
38 already defined then that function is replaced; otherwise a new name/
39 function pair is defined.
40
41 Functions are are invoked in the order in which their names were first
42 added."
43
44 (aif (assoc name *class-slot-alist* :test #'string=)
45 (setf (cdr it) function)
46 (asetf *class-slot-alist* (append it (list (cons name function))))))
47
48(defmacro define-class-slot
49 (name (class &optional stream) type init &body prepare)
50 "Define a new class slot.
51
ea578bb4
MW
52 The slot will be called NAME (a string) and will be of TYPE (which should
53 be a type S-expression). The slot's (static) initializer will be
54 constructed by printing the value of INIT, which is evaluated with CLASS
55 bound to the class object being constructed. If any PREPARE forms are
56 provided, then they are evaluated as a progn, with CLASS bound to the
dea4d055
MW
57 class object, and STREAM bound to the output stream it should write on."
58
59 (with-gensyms (classvar)
60 `(add-class-slot-function
61 ',name
62 (lambda (,classvar)
63 (make-sod-slot ,classvar ,name (c-type ,type)
52a79ab8 64 (make-property-set :slot-class 'sod-class-slot
dea4d055
MW
65 :initializer-function
66 (lambda (,class)
67 ,init)
68 ,@(and prepare
69 `(:prepare-function
70 (lambda (,class ,stream)
71 ,@prepare)))))))))
72
73;;;--------------------------------------------------------------------------
74;;; Basic information.
75
76(define-class-slot "name" (class) const-string
77 (prin1-to-string (sod-class-name class)))
78
79(define-class-slot "nick" (class) const-string
80 (prin1-to-string (sod-class-nickname class)))
81
82;;;--------------------------------------------------------------------------
83;;; Instance allocation and initialization.
84
85(define-class-slot "initsz" (class) size-t
86 (format nil "sizeof(struct ~A)" (ilayout-struct-tag class)))
87
8c2c58ae
MW
88(define-class-slot "align" (class) size-t
89 (format nil "SOD__ALIGNOF(struct ~A)" (ilayout-struct-tag class)))
90
dea4d055
MW
91(define-class-slot "imprint" (class stream)
92 (* (fun (* void) ("/*p*/" (* void))))
93 (format nil "~A__imprint" class)
94 (let ((ilayout (sod-class-ilayout class)))
95 (format stream "~&~:
7de8c666
MW
96/* Imprint raw memory with class `~A' instance structure. */
97static void *~:*~A__imprint(void *p)
dea4d055
MW
98{
99 struct ~A *sod__obj = p;
100
c2438e62 101 ~:{sod__obj->~A.~A._vt = &~A.~A;~:^~% ~}
dea4d055
MW
102 return (p);
103}~2%"
104 class
105 (ilayout-struct-tag class)
106 (mapcar (lambda (ichain)
107 (let* ((head (ichain-head ichain))
108 (tail (ichain-tail ichain)))
109 (list (sod-class-nickname head)
110 (sod-class-nickname tail)
c2438e62
MW
111 (vtable-name class head)
112 (sod-class-nickname tail))))
dea4d055
MW
113 (ilayout-ichains ilayout)))))
114
dea4d055
MW
115;;;--------------------------------------------------------------------------
116;;; Superclass structure.
117
118(define-class-slot "n_supers" (class) size-t
119 (length (sod-class-direct-superclasses class)))
120
121(define-class-slot "supers" (class stream)
122 (* (* (class "SodClass" :const) :const))
123 (if (null (sod-class-direct-superclasses class)) 0
124 (format nil "~A__supers" class))
125 (let ((supers (sod-class-direct-superclasses class)))
126 (when supers
127 (format stream "~&~:
128/* Direct superclasses. */
129static const SodClass *const ~A__supers[] = {
130 ~{~A__class~^,~% ~}
131};~2%"
132 class supers))))
133
134(define-class-slot "n_cpl" (class) size-t
135 (length (sod-class-precedence-list class)))
136
137(define-class-slot "cpl" (class stream)
138 (* (* (class "SodClass" :const) :const))
139 (format nil "~A__cpl" class)
140 (format stream "~&~:
141/* Class precedence list. */
142static const SodClass *const ~A__cpl[] = {
143 ~{~A__class~^,~% ~}
144};~2%"
145 class (sod-class-precedence-list class)))
146
147;;;--------------------------------------------------------------------------
148;;; Chain structure.
149
150(define-class-slot "link" (class) (* (class "SodClass" :const))
151 (aif (sod-class-chain-link class)
152 (format nil "~A__class" it)
153 0))
154
155(define-class-slot "head" (class) (* (class "SodClass" :const))
156 (format nil "~A__class" (sod-class-chain-head class)))
157
158(define-class-slot "level" (class) size-t
159 (position class (reverse (sod-class-chain class))))
160
161(define-class-slot "n_chains" (class) size-t
162 (length (sod-class-chains class)))
163
164(define-class-slot "chains" (class stream) (* (struct "sod_chain" :const))
165 (format nil "~A__chains" class)
166 (let ((chains (sod-class-chains class)))
167 (format stream "~&~:
168/* Chain structure. */
169~1@*~:{static const SodClass *const ~A__chain_~A[] = {
170 ~{~A__class~^,~% ~}
171};~:^~2%~}
172
173~0@*static const struct sod_chain ~A__chains[] = {
9ec578d9
MW
174~:{ { ~
175 /* n_classes = */ ~3@*~A,
176 /* classes = */ ~0@*~A__chain_~A,
177 /* off_ichain = */ ~4@*offsetof(struct ~A, ~A),
178 /* vt = */ (const struct sod_vtable *)&~A,
179 /* ichainsz = */ sizeof(struct ~A) }~:^,~%~}
dea4d055
MW
180};~2%"
181 class ;0
182 (mapcar (lambda (chain) ;1
183 (let* ((head (sod-class-chain-head (car chain)))
184 (chain-nick (sod-class-nickname head)))
9ec578d9
MW
185 (list class chain-nick ;0 1
186 (reverse chain) ;2
187 (length chain) ;3
188 (ilayout-struct-tag class) chain-nick ;4 5
189 (vtable-name class head) ;6
190 (ichain-struct-tag (car chain) head)))) ;7
dea4d055
MW
191 chains))))
192
193;;;--------------------------------------------------------------------------
194;;; Class-specific layout.
195
196(define-class-slot "off_islots" (class) size-t
9ec578d9
MW
197 (if (sod-class-slots class)
198 (format nil "offsetof(struct ~A, ~A)"
199 (ichain-struct-tag class (sod-class-chain-head class))
200 (sod-class-nickname class))
201 "0"))
dea4d055
MW
202
203(define-class-slot "islotsz" (class) size-t
9ec578d9
MW
204 (if (sod-class-slots class)
205 (format nil "sizeof(struct ~A)"
206 (islots-struct-tag class))
207 "0"))
dea4d055
MW
208
209;;;--------------------------------------------------------------------------
a142609c
MW
210;;; Built-in methods.
211
212;; Common protocol.
213
214(defclass lifecycle-message (standard-message)
215 ())
216
217(defclass lifecycle-effective-method (standard-effective-method)
218 ())
219
220(defmethod effective-method-live-p ((method lifecycle-effective-method))
221 t)
222
223(defgeneric lifecycle-method-kernel (method codegen target)
224 (:documentation
225 "Compute (into CODEGEN) the class-specific part of the METHOD.
226
227 The result, if any, needs to find its way to the TARGET, as usual."))
228
229(defmethod simple-method-body
230 ((method lifecycle-effective-method) codegen target)
231 (invoke-delegation-chain codegen target
232 (effective-method-basic-argument-names method)
233 (effective-method-primary-methods method)
234 (lambda (target)
235 (lifecycle-method-kernel method
236 codegen
237 target))))
238
27ec3825
MW
239;; Utilities.
240
241(defun declare-me (codegen class)
242 "Emit, to CODEGEN, a declaration of `me' as a pointer to CLASS.
243
244 The pointer refers to a part of the prevailing `sod__obj' object, which is
245 assumed to be a pointer to an appropriate `ilayout' structure."
246 (emit-decl codegen (make-var-inst "me" (c-type (* (class class)))
247 (format nil "&sod__obj->~A.~A"
248 (sod-class-nickname
249 (sod-class-chain-head class))
250 (sod-class-nickname class)))))
251
b2983f35
MW
252(defun collect-initarg-keywords (class)
253 "Return a list of keyword arguments corresponding to CLASS's initargs.
254
255 For each distinct name among the initargs defined on CLASS and its
256 superclasses, return a single `argument' object containing the (agreed
257 common) type, and the (unique, if present) default value from the most
258 specific defining superclass.
259
260 The arguments are not returned in any especially meaningful order."
261
262 (let ((map (make-hash-table :test #'equal))
263 (default-map (make-hash-table :test #'equal))
264 (list nil))
265 (dolist (super (sod-class-precedence-list class))
266 (dolist (initarg (sod-class-initargs super))
267 (let ((name (sod-initarg-name initarg))
268 (default (sod-initarg-default initarg)))
269 (unless (gethash name default-map)
270 (when (or default (not (gethash name map)))
271 (setf (gethash name map) (sod-initarg-argument initarg)))
272 (when default
273 (setf (gethash name default-map) t))))))
274 (maphash (lambda (key value)
275 (declare (ignore key))
276 (push value list))
277 map)
278 list))
279
280(definst suppliedp-struct (stream) (flags var)
281 (format stream
fd040f06 282 "~@<struct { ~2I~_~{unsigned ~A: 1;~^ ~_~} ~I~_} ~A;~:>"
b2983f35
MW
283 flags var))
284
a142609c
MW
285;; Initialization.
286
287(defclass initialization-message (lifecycle-message)
288 ())
289
290(defclass initialization-effective-method (lifecycle-effective-method)
291 ())
292
293(defmethod sod-message-effective-method-class
294 ((message initialization-message))
295 'initialization-effective-method)
296
1ec06509
MW
297(defmethod sod-message-keyword-argument-lists
298 ((message initialization-message) (class sod-class) direct-methods state)
b2983f35 299 (append (call-next-method)
4b64aeef 300 (mapcan (lambda (class)
84b9d17a
MW
301 (let* ((initargs (sod-class-initargs class))
302 (map (make-hash-table))
303 (arglist (mapcar
304 (lambda (initarg)
305 (let ((arg (sod-initarg-argument
306 initarg)))
307 (setf (gethash arg map) initarg)
308 arg))
309 initargs)))
4b64aeef 310 (and initargs
84b9d17a
MW
311 (list (cons (lambda (arg)
312 (info-with-location
313 (gethash arg map)
314 "Type `~A' from initarg ~
315 in class `~A' (here)"
316 (argument-type arg) class)
317 (report-inheritance-path
318 state class))
319 arglist)))))
1ec06509 320 (sod-class-precedence-list class))))
b2983f35 321
a142609c
MW
322(defmethod lifecycle-method-kernel
323 ((method initialization-effective-method) codegen target)
324 (let* ((class (effective-method-class method))
b2983f35 325 (keywords (collect-initarg-keywords class))
a142609c
MW
326 (ilayout (sod-class-ilayout class))
327 (obj-tag (ilayout-struct-tag class))
b2983f35
MW
328 (kw-tag (effective-method-keyword-struct-tag method))
329 (kw-tail (and keywords
330 (list (make-argument
331 "sod__kw"
332 (c-type (* (struct kw-tag :const)))))))
333 (func-type (c-type (fun void
334 ("sod__obj" (* (struct obj-tag)))
335 . kw-tail)))
336 (func-name (format nil "~A__init" class))
337 (done-setup-p nil))
a142609c
MW
338
339 ;; Start building the initialization function.
340 (codegen-push codegen)
341
342 (labels ((set-from-initializer (var type init)
343 ;; Store the value of INIT, which has the given TYPE, in VAR.
344 ;; INIT has the syntax of an initializer: declare and
345 ;; initialize a temporary, and then copy the result.
346 ;; Compilers seem to optimize this properly. Return the
347 ;; resulting code as an instruction.
348 (codegen-push codegen)
349 (emit-decl codegen (make-var-inst *sod-tmp-val* type init))
350 (deliver-expr codegen var *sod-tmp-val*)
b2983f35
MW
351 (codegen-pop-block codegen))
352 (setup ()
353 ;; Do any necessary one-time initialization required to set up
354 ;; the environment for the initialization code.
355 (unless done-setup-p
356
357 ;; Extract the keyword arguments into local variables.
358 (when keywords
359 (emit-decl codegen
360 (make-suppliedp-struct-inst
361 (mapcar #'argument-name keywords)
362 "suppliedp"))
363 (emit-banner codegen "Collect the keyword arguments.")
364 (dolist (arg keywords)
365 (let* ((name (argument-name arg))
366 (type (argument-type arg))
367 (default (argument-default arg))
368 (kwvar (format nil "sod__kw->~A" name))
369 (kwset (make-set-inst name kwvar))
370 (suppliedp (format nil "suppliedp.~A" name)))
371 (emit-decl codegen (make-var-inst name type))
372 (deliver-expr codegen suppliedp
373 (format nil "sod__kw->~A__suppliedp"
374 name))
375 (emit-inst
376 codegen
377 (if default
378 (make-if-inst suppliedp kwset
379 (set-from-initializer name
380 type
381 default))
382 kwset))))
383
384 (deliver-call codegen :void
385 "SOD__IGNORE" "suppliedp")
386 (dolist (arg keywords)
387 (deliver-call codegen :void
388 "SOD__IGNORE" (argument-name arg))))
389
390 (setf done-setup-p t))))
a142609c 391
27ec3825
MW
392 ;; Initialize the structure defined by the various superclasses, in
393 ;; reverse precedence order.
394 (dolist (super (reverse (sod-class-precedence-list class)))
395 (let* ((ichain (find (sod-class-chain-head super)
396 (ilayout-ichains ilayout)
397 :key #'ichain-head))
398 (islots (find super (ichain-body ichain)
399 :test (lambda (class item)
400 (and (typep item 'islots)
401 (eq (islots-class item) class)))))
a42893dd 402 (frags (sod-class-initfrags super))
27ec3825
MW
403 (this-class-focussed-p nil)
404 (isl (format nil "me->~A" (sod-class-nickname super))))
405
406 (flet ((focus-this-class ()
407 ;; Delayed initial preparation. Don't bother defining the
408 ;; `me' pointer if there's actually nothing to do.
b2983f35 409 (setup)
27ec3825
MW
410 (unless this-class-focussed-p
411 (emit-banner codegen
412 "Initialization for class `~A'." super)
413 (codegen-push codegen)
414 (declare-me codegen super)
415 (setf this-class-focussed-p t))))
416
417 ;; Work through each slot in turn.
418 (dolist (slot (and islots (islots-slots islots)))
419 (let ((dslot (effective-slot-direct-slot slot))
b2983f35
MW
420 (init (effective-slot-initializer slot))
421 (initargs (effective-slot-initargs slot)))
422 (when (or init initargs)
27ec3825
MW
423 (focus-this-class)
424 (let* ((slot-type (sod-slot-type dslot))
27ec3825
MW
425 (target (format nil "~A.~A"
426 isl (sod-slot-name dslot)))
5568c760
MW
427 (initinst (and init
428 (set-from-initializer
429 target slot-type
430 (sod-initializer-value init)))))
b2983f35
MW
431
432 ;; If there are applicable initialization arguments,
433 ;; check to see whether they were supplied.
434 (dolist (initarg (reverse (remove-duplicates
435 initargs
436 :key #'sod-initarg-name
dea6ee94
MW
437 :test #'string=
438 :from-end t)))
b2983f35
MW
439 (let ((arg-name (sod-initarg-name initarg)))
440 (setf initinst (make-if-inst
441 (format nil "suppliedp.~A" arg-name)
442 (make-set-inst target arg-name)
443 initinst))))
444
27ec3825
MW
445 (emit-inst codegen initinst)))))
446
a42893dd
MW
447 ;; Emit the class's initialization fragments.
448 (when frags
449 (let ((used-me-p this-class-focussed-p))
450 (focus-this-class)
451 (unless used-me-p
452 (deliver-call codegen :void "SOD__IGNORE" "me")))
453 (dolist (frag frags)
454 (codegen-push codegen)
455 (emit-inst codegen frag)
456 (emit-inst codegen (codegen-pop-block codegen))))
457
27ec3825
MW
458 ;; If we opened a block to initialize this class then close it
459 ;; again.
460 (when this-class-focussed-p
461 (emit-inst codegen (codegen-pop-block codegen)))))))
a142609c
MW
462
463 ;; Done making the initialization function.
464 (codegen-pop-function codegen func-name func-type
465 "Instance initialization function ~:_~
466 for class `~A'."
467 class)
468
b2983f35
MW
469 (apply #'deliver-call codegen :void func-name
470 "sod__obj" (and keywords (list (keyword-struct-pointer))))))
a142609c 471
a42893dd
MW
472;; Teardown.
473
474(defclass teardown-message (lifecycle-message)
475 ())
476
477(defclass teardown-effective-method (lifecycle-effective-method)
478 ())
479
480(defmethod sod-message-effective-method-class ((message teardown-message))
481 'teardown-effective-method)
482
483(defmethod lifecycle-method-kernel
484 ((method teardown-effective-method) codegen target)
485 (let* ((class (effective-method-class method))
486 (obj-tag (ilayout-struct-tag class))
487 (func-type (c-type (fun void ("sod__obj" (* (struct obj-tag))))))
488 (func-name (format nil "~A__teardown" class)))
489 (codegen-push codegen)
490 (dolist (super (sod-class-precedence-list class))
491 (let ((frags (sod-class-tearfrags super)))
492 (when frags
493 (emit-banner codegen "Teardown for class `~A'." super)
494 (codegen-push codegen)
495 (declare-me codegen super)
496 (deliver-call codegen :void "SOD__IGNORE" "me")
497 (dolist (frag frags)
498 (codegen-push codegen)
499 (emit-inst codegen frag)
500 (emit-inst codegen (codegen-pop-block codegen)))
501 (emit-inst codegen (codegen-pop-block codegen)))))
502 (codegen-pop-function codegen func-name func-type
503 "Instance teardown function ~:_~
504 for class `~A'."
505 class)
506 (deliver-call codegen :void
507 (format nil "~A__teardown" class) "sod__obj")
508 (deliver-expr codegen target 0)))
509
a142609c 510;;;--------------------------------------------------------------------------
dea4d055
MW
511;;; Bootstrapping the class graph.
512
513(defun bootstrap-classes (module)
ea578bb4
MW
514 "Bootstrap the braid in MODULE.
515
516 This builds the fundamental recursive braid, where `SodObject' is an
517 instance of `SodClass', and `SodClass' is a subclass of `SodObject' (and
518 an instance of itself)."
dea4d055 519 (let* ((sod-object (make-sod-class "SodObject" nil
73eceea6
MW
520 (make-property-set :nick 'obj
521 :%bootstrapping t)))
dea4d055 522 (sod-class (make-sod-class "SodClass" (list sod-object)
73eceea6
MW
523 (make-property-set :nick 'cls
524 :%bootstrapping t)))
dea4d055
MW
525 (classes (list sod-object sod-class)))
526
a142609c
MW
527 ;; Attach the built-in messages.
528 (make-sod-message sod-object "init"
529 (c-type (fun void :keys))
530 (make-property-set
531 :message-class 'initialization-message))
a42893dd
MW
532 (make-sod-message sod-object "teardown" (c-type (fun int))
533 (make-property-set :message-class 'teardown-message))
a142609c 534
dea4d055
MW
535 ;; Sort out the recursion.
536 (setf (slot-value sod-class 'chain-link) sod-object)
537 (dolist (class classes)
538 (setf (slot-value class 'metaclass) sod-class))
539
540 ;; Predeclare the class types.
541 (dolist (class classes)
542 (make-class-type (sod-class-name class)))
543
544 ;; Attach the class slots.
545 (dolist (slot *class-slot-alist*)
546 (funcall (cdr slot) sod-class))
547
548 ;; These classes are too closely intertwined. We must partially finalize
549 ;; them together by hand. This is cloned from `finalize-sod-class'.
550 (dolist (class classes)
551 (with-slots (class-precedence-list chain-head chain chains) class
552 (setf class-precedence-list (compute-cpl class))
553 (setf (values chain-head chain chains) (compute-chains class))))
554
555 ;; Done.
556 (dolist (class classes)
e45a106d
MW
557 (unless (finalize-sod-class class)
558 (error "Failed to finalize built-in class"))
dea4d055
MW
559 (add-to-module module class))))
560
73c17a81 561(export '*builtin-module*)
e7d43325 562(defvar-unbound *builtin-module*
ea578bb4
MW
563 "The builtin module.")
564
73c17a81 565(export 'make-builtin-module)
dea4d055 566(defun make-builtin-module ()
ea578bb4
MW
567 "Construct the builtin module.
568
9ec578d9
MW
569 This involves constructing the braid (which is done in
570 `bootstrap-classes') and defining a few obvious type names which users
571 will find handy.
ea578bb4
MW
572
573 Returns the newly constructed module, and stores it in the variable
574 `*builtin-module*'."
dea4d055
MW
575 (let ((module (make-instance 'module
576 :name (make-pathname :name "SOD-BASE"
577 :type "SOD"
578 :case :common)
ea578bb4 579 :state nil)))
9ec578d9 580 (with-module-environment (module)
9ec578d9
MW
581 (flet ((header-name (name)
582 (concatenate 'string "\"" (string-downcase name) ".h\""))
583 (add-includes (reason &rest names)
584 (let ((text (with-output-to-string (out)
585 (dolist (name names)
586 (format out "#include ~A~%" name)))))
587 (add-to-module module
588 (make-instance 'code-fragment-item
589 :reason reason
590 :constraints nil
591 :name :includes
592 :fragment text)))))
593 (add-includes :c (header-name "sod"))
594 (add-includes :h "<stddef.h>"))
595 (bootstrap-classes module))
ea578bb4 596 (setf *builtin-module* module)))
dea4d055 597
54c01772 598(define-clear-the-decks builtin-module
e7d43325 599 (unless (boundp '*builtin-module*) (make-builtin-module)))
54c01772 600
dea4d055 601;;;----- That's all, folks --------------------------------------------------