src/: Introduce a macro for defining on-demand slots.
[sod] / src / class-layout-impl.lisp
CommitLineData
dea4d055
MW
1;;; -*-lisp-*-
2;;;
3;;; Class layout protocol implementation
4;;;
5;;; (c) 2009 Straylight/Edgeware
6;;;
7
8;;;----- Licensing notice ---------------------------------------------------
9;;;
10;;; This file is part of the Sensble 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;;; Effective slots.
30
31(defmethod print-object ((slot effective-slot) stream)
32 (maybe-print-unreadable-object (slot stream :type t)
33 (format stream "~A~@[ = ~@_~A~]"
34 (effective-slot-direct-slot slot)
35 (effective-slot-initializer slot))))
36
37(defmethod find-slot-initializer ((class sod-class) (slot sod-slot))
38 (some (lambda (super)
39 (find slot
40 (sod-class-instance-initializers super)
41 :key #'sod-initializer-slot))
42 (sod-class-precedence-list class)))
43
44(defmethod compute-effective-slot ((class sod-class) (slot sod-slot))
45 (make-instance 'effective-slot
46 :slot slot
47 :class class
48 :initializer (find-slot-initializer class slot)))
49
50;;;--------------------------------------------------------------------------
51;;; Special-purpose slot objects.
52
53(export 'sod-class-slot)
54(defclass sod-class-slot (sod-slot)
55 ((initializer-function :initarg :initializer-function
56 :type (or symbol function)
57 :reader sod-slot-initializer-function)
58 (prepare-function :initarg :prepare-function :type (or symbol function)
59 :reader sod-slot-prepare-function))
60 (:documentation
3109662a 61 "Special class for slots defined on `SodClass'.
dea4d055
MW
62
63 These slots need class-specific initialization. It's easier to keep all
64 of the information (name, type, and how to initialize them) about these
65 slots in one place, so that's what we do here."))
66
67(defmethod shared-initialize :after
68 ((slot sod-class-slot) slot-names &key pset)
69 (declare (ignore slot-names))
70 (default-slot (slot 'initializer-function)
ea578bb4 71 (get-property pset :initializer-function :func nil))
dea4d055 72 (default-slot (slot 'prepare-function)
ea578bb4 73 (get-property pset :prepare-function :func nil)))
dea4d055
MW
74
75(export 'sod-class-effective-slot)
76(defclass sod-class-effective-slot (effective-slot)
77 ((initializer-function :initarg :initializer-function
78 :type (or symbol function)
79 :reader effective-slot-initializer-function)
80 (prepare-function :initarg :prepare-function :type (or symbol function)
81 :reader effective-slot-prepare-function))
82 (:documentation
3109662a 83 "Special class for slots defined on `SodClass'.
dea4d055
MW
84
85 This class ignores any explicit initializers and computes initializer
86 values using the slot's INIT-FUNC slot and a magical protocol during
87 metaclass instance construction."))
88
89(defmethod compute-effective-slot ((class sod-class) (slot sod-class-slot))
90 (make-instance 'sod-class-effective-slot
91 :class class :slot slot
92 :initializer-function (sod-slot-initializer-function slot)
93 :prepare-function (sod-slot-prepare-function slot)
94 :initializer (find-slot-initializer class slot)))
95
96;;;--------------------------------------------------------------------------
97;;; Effective methods.
98
99(defmethod print-object ((method effective-method) stream)
100 (maybe-print-unreadable-object (method stream :type t)
101 (format stream "~A ~A"
102 (effective-method-message method)
103 (effective-method-class method))))
104
105(defmethod print-object ((entry method-entry) stream)
106 (maybe-print-unreadable-object (entry stream :type t)
b426ab51 107 (format stream "~A:~A~@[ ~S~]"
dea4d055 108 (method-entry-effective-method entry)
b426ab51
MW
109 (sod-class-nickname (method-entry-chain-head entry))
110 (method-entry-role entry))))
dea4d055
MW
111
112(defmethod compute-sod-effective-method
113 ((message sod-message) (class sod-class))
114 (let ((direct-methods (mappend (lambda (super)
115 (remove message
116 (sod-class-methods super)
117 :key #'sod-method-message
118 :test-not #'eql))
119 (sod-class-precedence-list class))))
120 (make-instance (message-effective-method-class message)
121 :message message
122 :class class
123 :direct-methods direct-methods)))
124
125(defmethod compute-effective-methods ((class sod-class))
126 (mapcan (lambda (super)
127 (mapcar (lambda (message)
128 (compute-sod-effective-method message class))
129 (sod-class-messages super)))
130 (sod-class-precedence-list class)))
131
6e6b0958
MW
132(define-on-demand-slot sod-class effective-methods (class)
133 (compute-effective-methods class))
dea4d055
MW
134
135;;;--------------------------------------------------------------------------
136;;; Instance layout.
137
138;;; islots
139
140(defmethod print-object ((islots islots) stream)
141 (print-unreadable-object (islots stream :type t)
142 (format stream "~A <= ~A ~_~:<~@{~S~^ ~_~}~:>"
143 (islots-subclass islots)
144 (islots-class islots)
145 (islots-slots islots))))
146
147(defmethod compute-islots ((class sod-class) (subclass sod-class))
148 (make-instance 'islots
149 :class class
150 :subclass subclass
151 :slots (mapcar (lambda (slot)
152 (compute-effective-slot subclass slot))
153 (sod-class-slots class))))
154
155;;; vtable-pointer
156;;; Do we need a construction protocol here?
157
158(defmethod print-object ((vtp vtable-pointer) stream)
159 (print-unreadable-object (vtp stream :type t)
160 (format stream "~A:~A"
161 (vtable-pointer-class vtp)
162 (sod-class-nickname (vtable-pointer-chain-head vtp)))))
163
164;;; ichain
165
166(defmethod print-object ((ichain ichain) stream)
167 (print-unreadable-object (ichain stream :type t)
168 (format stream "~A:~A ~_~:<~@{~S~^ ~_~}~:>"
169 (ichain-class ichain)
170 (sod-class-nickname (ichain-head ichain))
171 (ichain-body ichain))))
172
173(defmethod compute-ichain ((class sod-class) chain)
174 (let* ((chain-head (car chain))
175 (chain-tail (find chain-head (mapcar #'car (sod-class-chains class))
176 :key #'sod-class-chain-head))
177 (vtable-pointer (make-instance 'vtable-pointer
178 :class class
179 :chain-head chain-head
180 :chain-tail chain-tail))
181 (islots (remove-if-not #'islots-slots
182 (mapcar (lambda (super)
183 (compute-islots super class))
184 chain))))
185 (make-instance 'ichain
186 :class class
187 :chain-head chain-head
188 :chain-tail chain-tail
189 :body (cons vtable-pointer islots))))
190
191;;; ilayout
192
193(defmethod print-object ((ilayout ilayout) stream)
194 (print-unreadable-object (ilayout stream :type t)
195 (format stream "~A ~_~:<~@{~S~^ ~_~}~:>"
196 (ilayout-class ilayout)
197 (ilayout-ichains ilayout))))
198
199(defmethod compute-ilayout ((class sod-class))
200 (make-instance 'ilayout
201 :class class
202 :ichains (mapcar (lambda (chain)
203 (compute-ichain class
204 (reverse chain)))
205 (sod-class-chains class))))
206
6e6b0958
MW
207(define-on-demand-slot sod-class %ilayout (class)
208 (compute-ilayout class))
dea4d055
MW
209
210;;;--------------------------------------------------------------------------
211;;; Vtable layout.
212
213;;; vtmsgs
214
215(defmethod print-object ((vtmsgs vtmsgs) stream)
216 (print-unreadable-object (vtmsgs stream :type t)
217 (format stream "~A <= ~A ~_~:<~@{~S~^ ~_~}~:>"
218 (vtmsgs-subclass vtmsgs)
219 (vtmsgs-class vtmsgs)
220 (vtmsgs-entries vtmsgs))))
221
222(defmethod compute-vtmsgs
223 ((class sod-class)
224 (subclass sod-class)
225 (chain-head sod-class)
226 (chain-tail sod-class))
b426ab51 227 (flet ((make-entries (message)
dea4d055
MW
228 (let ((method (find message
229 (sod-class-effective-methods subclass)
230 :key #'effective-method-message)))
b426ab51 231 (make-method-entries method chain-head chain-tail))))
dea4d055
MW
232 (make-instance 'vtmsgs
233 :class class
234 :subclass subclass
235 :chain-head chain-head
236 :chain-tail chain-tail
b426ab51 237 :entries (mapcan #'make-entries
dea4d055
MW
238 (sod-class-messages class)))))
239
240;;; class-pointer
241
242(defmethod print-object ((cptr class-pointer) stream)
243 (print-unreadable-object (cptr stream :type t)
244 (format stream "~A:~A"
245 (class-pointer-metaclass cptr)
246 (sod-class-nickname (class-pointer-meta-chain-head cptr)))))
247
248(defmethod make-class-pointer
249 ((class sod-class) (chain-head sod-class)
250 (metaclass sod-class) (meta-chain-head sod-class))
251
252 ;; Slightly tricky. We don't necessarily want a pointer to the metaclass,
253 ;; but to its most specific subclass on the given chain. Fortunately, CL
254 ;; is good at this game.
255 (let* ((meta-chains (sod-class-chains metaclass))
256 (meta-chain-tails (mapcar #'car meta-chains))
257 (meta-chain-tail (find meta-chain-head meta-chain-tails
258 :key #'sod-class-chain-head)))
259 (make-instance 'class-pointer
260 :class class
261 :chain-head chain-head
262 :metaclass meta-chain-tail
263 :meta-chain-head meta-chain-head)))
264
265;;; base-offset
266
267(defmethod print-object ((boff base-offset) stream)
268 (print-unreadable-object (boff stream :type t)
269 (format stream "~A:~A"
270 (base-offset-class boff)
271 (sod-class-nickname (base-offset-chain-head boff)))))
272
273(defmethod make-base-offset ((class sod-class) (chain-head sod-class))
274 (make-instance 'base-offset
275 :class class
276 :chain-head chain-head))
277
278;;; chain-offset
279
280(defmethod print-object ((choff chain-offset) stream)
281 (print-unreadable-object (choff stream :type t)
282 (format stream "~A:~A->~A"
283 (chain-offset-class choff)
284 (sod-class-nickname (chain-offset-chain-head choff))
285 (sod-class-nickname (chain-offset-target-head choff)))))
286
287(defmethod make-chain-offset
288 ((class sod-class) (chain-head sod-class) (target-head sod-class))
289 (make-instance 'chain-offset
290 :class class
291 :chain-head chain-head
292 :target-head target-head))
293
294;;; vtable
295
296(defmethod print-object ((vtable vtable) stream)
297 (print-unreadable-object (vtable stream :type t)
298 (format stream "~A:~A ~_~:<~@{~S~^ ~_~}~:>"
299 (vtable-class vtable)
300 (sod-class-nickname (vtable-chain-head vtable))
301 (vtable-body vtable))))
302
303;; Special variables used by `compute-vtable'.
304(defvar *done-metaclass-chains*)
305(defvar *done-instance-chains*)
306
307(defmethod compute-vtable-items
308 ((class sod-class) (super sod-class) (chain-head sod-class)
309 (chain-tail sod-class) (emit function))
310
311 ;; If this class introduces new metaclass chains, then emit pointers to
312 ;; them.
313 (let* ((metasuper (sod-class-metaclass super))
314 (metasuper-chains (sod-class-chains metasuper))
315 (metasuper-chain-heads (mapcar (lambda (chain)
316 (sod-class-chain-head (car chain)))
317 metasuper-chains)))
318 (dolist (metasuper-chain-head metasuper-chain-heads)
319 (unless (member metasuper-chain-head *done-metaclass-chains*)
320 (funcall emit (make-class-pointer class
321 chain-head
322 metasuper
323 metasuper-chain-head))
324 (push metasuper-chain-head *done-metaclass-chains*))))
325
326 ;; If there are new instance chains, then emit offsets to them.
327 (let* ((chains (sod-class-chains super))
328 (chain-heads (mapcar (lambda (chain)
329 (sod-class-chain-head (car chain)))
330 chains)))
331 (dolist (head chain-heads)
332 (unless (member head *done-instance-chains*)
333 (funcall emit (make-chain-offset class chain-head head))
334 (push head *done-instance-chains*))))
335
336 ;; Finally, if there are interesting methods, emit those too.
337 (when (sod-class-messages super)
338 (funcall emit (compute-vtmsgs super class chain-head chain-tail))))
339
340(defmethod compute-vtable ((class sod-class) (chain list))
341 (let* ((chain-head (car chain))
342 (chain-tail (find chain-head (mapcar #'car (sod-class-chains class))
343 :key #'sod-class-chain-head))
344 (*done-metaclass-chains* nil)
345 (*done-instance-chains* (list chain-head))
346 (done-superclasses nil)
347 (items nil))
348 (flet ((emit (item)
349 (push item items)))
350
351 ;; Find the root chain in the metaclass and write a pointer.
352 (let* ((metaclass (sod-class-metaclass class))
353 (metaclass-root (find-root-metaclass class))
354 (metaclass-root-head (sod-class-chain-head metaclass-root)))
355 (emit (make-class-pointer class chain-head metaclass
356 metaclass-root-head))
357 (push metaclass-root-head *done-metaclass-chains*))
358
359 ;; Write an offset to the instance base.
360 (emit (make-base-offset class chain-head))
361
362 ;; Now walk the chain. As we ascend the chain, scan the class
363 ;; precedence list of each class in reverse to ensure that we have
364 ;; everything interesting.
365 (dolist (super chain)
366 (dolist (sub (reverse (sod-class-precedence-list super)))
367 (unless (member sub done-superclasses)
368 (compute-vtable-items class
369 sub
370 chain-head
371 chain-tail
372 #'emit)
373 (push sub done-superclasses))))
374
375 ;; We're through.
376 (make-instance 'vtable
377 :class class
378 :chain-head chain-head
379 :chain-tail chain-tail
380 :body (nreverse items)))))
381
382(defmethod compute-vtables ((class sod-class))
383 (mapcar (lambda (chain)
384 (compute-vtable class (reverse chain)))
385 (sod-class-chains class)))
386
6e6b0958
MW
387(define-on-demand-slot sod-class vtables (class)
388 (compute-vtables class))
dea4d055
MW
389
390;;;----- That's all, folks --------------------------------------------------