3 ;;; Builtin module provides the root of the class graph
5 ;;; (c) 2009 Straylight/Edgeware
8 ;;;----- Licensing notice ---------------------------------------------------
10 ;;; This file is part of the Sensible Object Design, an object system for C.
12 ;;; SOD is free software; you can redistribute it and/or modify
13 ;;; it under the terms of the GNU General Public License as published by
14 ;;; the Free Software Foundation; either version 2 of the License, or
15 ;;; (at your option) any later version.
17 ;;; SOD is distributed in the hope that it will be useful,
18 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;;; GNU General Public License for more details.
22 ;;; You should have received a copy of the GNU General Public License
23 ;;; along with SOD; if not, write to the Free Software Foundation,
24 ;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 ;;;--------------------------------------------------------------------------
31 (defvar *class-slot-alist* nil)
33 (defun add-class-slot-function (name function)
34 "Attach a slot function to the `*class-slot-alist*'.
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.
41 Functions are are invoked in the order in which their names were first
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))))))
48 (defmacro define-class-slot
49 (name (class &optional stream) type init &body prepare)
50 "Define a new class slot.
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
57 class object, and STREAM bound to the output stream it should write on."
59 (with-gensyms (classvar)
60 `(add-class-slot-function
63 (make-sod-slot ,classvar ,name (c-type ,type)
64 (make-property-set :slot-class 'sod-class-slot
70 (lambda (,class ,stream)
73 ;;;--------------------------------------------------------------------------
74 ;;; Basic information.
76 (define-class-slot "name" (class) const-string
77 (prin1-to-string (sod-class-name class)))
79 (define-class-slot "nick" (class) const-string
80 (prin1-to-string (sod-class-nickname class)))
82 ;;;--------------------------------------------------------------------------
83 ;;; Instance allocation and initialization.
85 (define-class-slot "initsz" (class) size-t
86 (format nil "sizeof(struct ~A)" (ilayout-struct-tag class)))
88 (define-class-slot "imprint" (class stream)
89 (* (fun (* void) ("/*p*/" (* void))))
90 (format nil "~A__imprint" class)
91 (let ((ilayout (sod-class-ilayout class)))
93 /* Imprint raw memory with class `~A' instance structure. */
94 static void *~:*~A__imprint(void *p)
96 struct ~A *sod__obj = p;
98 ~:{sod__obj->~A.~A._vt = &~A.~A;~:^~% ~}
102 (ilayout-struct-tag class)
103 (mapcar (lambda (ichain)
104 (let* ((head (ichain-head ichain))
105 (tail (ichain-tail ichain)))
106 (list (sod-class-nickname head)
107 (sod-class-nickname tail)
108 (vtable-name class head)
109 (sod-class-nickname tail))))
110 (ilayout-ichains ilayout)))))
112 ;;;--------------------------------------------------------------------------
113 ;;; Superclass structure.
115 (define-class-slot "n_supers" (class) size-t
116 (length (sod-class-direct-superclasses class)))
118 (define-class-slot "supers" (class stream)
119 (* (* (class "SodClass" :const) :const))
120 (if (null (sod-class-direct-superclasses class)) 0
121 (format nil "~A__supers" class))
122 (let ((supers (sod-class-direct-superclasses class)))
125 /* Direct superclasses. */
126 static const SodClass *const ~A__supers[] = {
131 (define-class-slot "n_cpl" (class) size-t
132 (length (sod-class-precedence-list class)))
134 (define-class-slot "cpl" (class stream)
135 (* (* (class "SodClass" :const) :const))
136 (format nil "~A__cpl" class)
138 /* Class precedence list. */
139 static const SodClass *const ~A__cpl[] = {
142 class (sod-class-precedence-list class)))
144 ;;;--------------------------------------------------------------------------
147 (define-class-slot "link" (class) (* (class "SodClass" :const))
148 (aif (sod-class-chain-link class)
149 (format nil "~A__class" it)
152 (define-class-slot "head" (class) (* (class "SodClass" :const))
153 (format nil "~A__class" (sod-class-chain-head class)))
155 (define-class-slot "level" (class) size-t
156 (position class (reverse (sod-class-chain class))))
158 (define-class-slot "n_chains" (class) size-t
159 (length (sod-class-chains class)))
161 (define-class-slot "chains" (class stream) (* (struct "sod_chain" :const))
162 (format nil "~A__chains" class)
163 (let ((chains (sod-class-chains class)))
165 /* Chain structure. */
166 ~1@*~:{static const SodClass *const ~A__chain_~A[] = {
170 ~0@*static const struct sod_chain ~A__chains[] = {
172 /* n_classes = */ ~3@*~A,
173 /* classes = */ ~0@*~A__chain_~A,
174 /* off_ichain = */ ~4@*offsetof(struct ~A, ~A),
175 /* vt = */ (const struct sod_vtable *)&~A,
176 /* ichainsz = */ sizeof(struct ~A) }~:^,~%~}
179 (mapcar (lambda (chain) ;1
180 (let* ((head (sod-class-chain-head (car chain)))
181 (chain-nick (sod-class-nickname head)))
182 (list class chain-nick ;0 1
185 (ilayout-struct-tag class) chain-nick ;4 5
186 (vtable-name class head) ;6
187 (ichain-struct-tag (car chain) head)))) ;7
190 ;;;--------------------------------------------------------------------------
191 ;;; Class-specific layout.
193 (define-class-slot "off_islots" (class) size-t
194 (if (sod-class-slots class)
195 (format nil "offsetof(struct ~A, ~A)"
196 (ichain-struct-tag class (sod-class-chain-head class))
197 (sod-class-nickname class))
200 (define-class-slot "islotsz" (class) size-t
201 (if (sod-class-slots class)
202 (format nil "sizeof(struct ~A)"
203 (islots-struct-tag class))
206 ;;;--------------------------------------------------------------------------
207 ;;; Built-in methods.
211 (defclass lifecycle-message (standard-message)
214 (defclass lifecycle-effective-method (standard-effective-method)
217 (defmethod effective-method-live-p ((method lifecycle-effective-method))
220 (defgeneric lifecycle-method-kernel (method codegen target)
222 "Compute (into CODEGEN) the class-specific part of the METHOD.
224 The result, if any, needs to find its way to the TARGET, as usual."))
226 (defmethod simple-method-body
227 ((method lifecycle-effective-method) codegen target)
228 (invoke-delegation-chain codegen target
229 (effective-method-basic-argument-names method)
230 (effective-method-primary-methods method)
232 (lifecycle-method-kernel method
238 (defun declare-me (codegen class)
239 "Emit, to CODEGEN, a declaration of `me' as a pointer to CLASS.
241 The pointer refers to a part of the prevailing `sod__obj' object, which is
242 assumed to be a pointer to an appropriate `ilayout' structure."
243 (emit-decl codegen (make-var-inst "me" (c-type (* (class class)))
244 (format nil "&sod__obj->~A.~A"
246 (sod-class-chain-head class))
247 (sod-class-nickname class)))))
251 (defclass initialization-message (lifecycle-message)
254 (defclass initialization-effective-method (lifecycle-effective-method)
257 (defmethod sod-message-effective-method-class
258 ((message initialization-message))
259 'initialization-effective-method)
261 (defmethod lifecycle-method-kernel
262 ((method initialization-effective-method) codegen target)
263 (let* ((class (effective-method-class method))
264 (ilayout (sod-class-ilayout class))
265 (obj-tag (ilayout-struct-tag class))
266 (func-type (c-type (fun void ("sod__obj" (* (struct obj-tag))))))
267 (func-name (format nil "~A__init" class)))
269 ;; Start building the initialization function.
270 (codegen-push codegen)
272 (labels ((set-from-initializer (var type init)
273 ;; Store the value of INIT, which has the given TYPE, in VAR.
274 ;; INIT has the syntax of an initializer: declare and
275 ;; initialize a temporary, and then copy the result.
276 ;; Compilers seem to optimize this properly. Return the
277 ;; resulting code as an instruction.
278 (codegen-push codegen)
279 (emit-decl codegen (make-var-inst *sod-tmp-val* type init))
280 (deliver-expr codegen var *sod-tmp-val*)
281 (codegen-pop-block codegen)))
283 ;; Initialize the structure defined by the various superclasses, in
284 ;; reverse precedence order.
285 (dolist (super (reverse (sod-class-precedence-list class)))
286 (let* ((ichain (find (sod-class-chain-head super)
287 (ilayout-ichains ilayout)
289 (islots (find super (ichain-body ichain)
290 :test (lambda (class item)
291 (and (typep item 'islots)
292 (eq (islots-class item) class)))))
293 (frags (sod-class-initfrags super))
294 (this-class-focussed-p nil)
295 (isl (format nil "me->~A" (sod-class-nickname super))))
297 (flet ((focus-this-class ()
298 ;; Delayed initial preparation. Don't bother defining the
299 ;; `me' pointer if there's actually nothing to do.
300 (unless this-class-focussed-p
302 "Initialization for class `~A'." super)
303 (codegen-push codegen)
304 (declare-me codegen super)
305 (setf this-class-focussed-p t))))
307 ;; Work through each slot in turn.
308 (dolist (slot (and islots (islots-slots islots)))
309 (let ((dslot (effective-slot-direct-slot slot))
310 (init (effective-slot-initializer slot)))
313 (let* ((slot-type (sod-slot-type dslot))
314 (slot-default (sod-initializer-value init))
315 (target (format nil "~A.~A"
316 isl (sod-slot-name dslot)))
317 (initinst (set-from-initializer target
320 (emit-inst codegen initinst)))))
322 ;; Emit the class's initialization fragments.
324 (let ((used-me-p this-class-focussed-p))
327 (deliver-call codegen :void "SOD__IGNORE" "me")))
329 (codegen-push codegen)
330 (emit-inst codegen frag)
331 (emit-inst codegen (codegen-pop-block codegen))))
333 ;; If we opened a block to initialize this class then close it
335 (when this-class-focussed-p
336 (emit-inst codegen (codegen-pop-block codegen)))))))
338 ;; Done making the initialization function.
339 (codegen-pop-function codegen func-name func-type
340 "Instance initialization function ~:_~
344 (deliver-call codegen :void func-name "sod__obj")))
348 (defclass teardown-message (lifecycle-message)
351 (defclass teardown-effective-method (lifecycle-effective-method)
354 (defmethod sod-message-effective-method-class ((message teardown-message))
355 'teardown-effective-method)
357 (defmethod lifecycle-method-kernel
358 ((method teardown-effective-method) codegen target)
359 (let* ((class (effective-method-class method))
360 (obj-tag (ilayout-struct-tag class))
361 (func-type (c-type (fun void ("sod__obj" (* (struct obj-tag))))))
362 (func-name (format nil "~A__teardown" class)))
363 (codegen-push codegen)
364 (dolist (super (sod-class-precedence-list class))
365 (let ((frags (sod-class-tearfrags super)))
367 (emit-banner codegen "Teardown for class `~A'." super)
368 (codegen-push codegen)
369 (declare-me codegen super)
370 (deliver-call codegen :void "SOD__IGNORE" "me")
372 (codegen-push codegen)
373 (emit-inst codegen frag)
374 (emit-inst codegen (codegen-pop-block codegen)))
375 (emit-inst codegen (codegen-pop-block codegen)))))
376 (codegen-pop-function codegen func-name func-type
377 "Instance teardown function ~:_~
380 (deliver-call codegen :void
381 (format nil "~A__teardown" class) "sod__obj")
382 (deliver-expr codegen target 0)))
384 ;;;--------------------------------------------------------------------------
385 ;;; Bootstrapping the class graph.
387 (defun bootstrap-classes (module)
388 "Bootstrap the braid in MODULE.
390 This builds the fundamental recursive braid, where `SodObject' is an
391 instance of `SodClass', and `SodClass' is a subclass of `SodObject' (and
392 an instance of itself)."
393 (let* ((sod-object (make-sod-class "SodObject" nil
394 (make-property-set :nick 'obj)))
395 (sod-class (make-sod-class "SodClass" (list sod-object)
396 (make-property-set :nick 'cls)))
397 (classes (list sod-object sod-class)))
399 ;; Attach the built-in messages.
400 (make-sod-message sod-object "init"
401 (c-type (fun void :keys))
403 :message-class 'initialization-message))
404 (make-sod-message sod-object "teardown" (c-type (fun int))
405 (make-property-set :message-class 'teardown-message))
407 ;; Sort out the recursion.
408 (setf (slot-value sod-class 'chain-link) sod-object)
409 (dolist (class classes)
410 (setf (slot-value class 'metaclass) sod-class))
412 ;; Predeclare the class types.
413 (dolist (class classes)
414 (make-class-type (sod-class-name class)))
416 ;; Attach the class slots.
417 (dolist (slot *class-slot-alist*)
418 (funcall (cdr slot) sod-class))
420 ;; These classes are too closely intertwined. We must partially finalize
421 ;; them together by hand. This is cloned from `finalize-sod-class'.
422 (dolist (class classes)
423 (with-slots (class-precedence-list chain-head chain chains) class
424 (setf class-precedence-list (compute-cpl class))
425 (setf (values chain-head chain chains) (compute-chains class))))
428 (dolist (class classes)
429 (finalize-sod-class class)
430 (add-to-module module class))))
432 (export '*builtin-module*)
433 (defvar *builtin-module* nil
434 "The builtin module.")
436 (export 'make-builtin-module)
437 (defun make-builtin-module ()
438 "Construct the builtin module.
440 This involves constructing the braid (which is done in
441 `bootstrap-classes') and defining a few obvious type names which users
444 Returns the newly constructed module, and stores it in the variable
446 (let ((module (make-instance 'module
447 :name (make-pathname :name "SOD-BASE"
451 (with-module-environment (module)
452 (dolist (name '("va_list" "size_t" "ptrdiff_t" "wchar_t"))
453 (add-to-module module (make-instance 'type-item :name name)))
454 (flet ((header-name (name)
455 (concatenate 'string "\"" (string-downcase name) ".h\""))
456 (add-includes (reason &rest names)
457 (let ((text (with-output-to-string (out)
459 (format out "#include ~A~%" name)))))
460 (add-to-module module
461 (make-instance 'code-fragment-item
466 (add-includes :c (header-name "sod"))
467 (add-includes :h "<stddef.h>"))
468 (bootstrap-classes module))
469 (setf *builtin-module* module)))
471 (define-clear-the-decks builtin-module
472 (unless *builtin-module* (make-builtin-module)))
474 ;;;----- That's all, folks --------------------------------------------------