950db2b54bb0d9dcb400f5a6fe416ebe3d6bd61a
[sod] / src / class-layout-impl.lisp
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
61 "Special class for slots defined on `SodClass'.
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)
71 (get-property pset :initializer-function :func nil))
72 (default-slot (slot 'prepare-function)
73 (get-property pset :prepare-function :func nil)))
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
83 "Special class for slots defined on `SodClass'.
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)
107 (format stream "~A:~A~@[ ~S~]"
108 (method-entry-effective-method entry)
109 (sod-class-nickname (method-entry-chain-head entry))
110 (method-entry-role entry))))
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
132 (defmethod slot-unbound
133 (clos-class (class sod-class) (slot-name (eql 'effective-methods)))
134 (declare (ignore clos-class))
135 (setf (slot-value class 'effective-methods)
136 (compute-effective-methods class)))
137
138 ;;;--------------------------------------------------------------------------
139 ;;; Instance layout.
140
141 ;;; islots
142
143 (defmethod print-object ((islots islots) stream)
144 (print-unreadable-object (islots stream :type t)
145 (format stream "~A <= ~A ~_~:<~@{~S~^ ~_~}~:>"
146 (islots-subclass islots)
147 (islots-class islots)
148 (islots-slots islots))))
149
150 (defmethod compute-islots ((class sod-class) (subclass sod-class))
151 (make-instance 'islots
152 :class class
153 :subclass subclass
154 :slots (mapcar (lambda (slot)
155 (compute-effective-slot subclass slot))
156 (sod-class-slots class))))
157
158 ;;; vtable-pointer
159 ;;; Do we need a construction protocol here?
160
161 (defmethod print-object ((vtp vtable-pointer) stream)
162 (print-unreadable-object (vtp stream :type t)
163 (format stream "~A:~A"
164 (vtable-pointer-class vtp)
165 (sod-class-nickname (vtable-pointer-chain-head vtp)))))
166
167 ;;; ichain
168
169 (defmethod print-object ((ichain ichain) stream)
170 (print-unreadable-object (ichain stream :type t)
171 (format stream "~A:~A ~_~:<~@{~S~^ ~_~}~:>"
172 (ichain-class ichain)
173 (sod-class-nickname (ichain-head ichain))
174 (ichain-body ichain))))
175
176 (defmethod compute-ichain ((class sod-class) chain)
177 (let* ((chain-head (car chain))
178 (chain-tail (find chain-head (mapcar #'car (sod-class-chains class))
179 :key #'sod-class-chain-head))
180 (vtable-pointer (make-instance 'vtable-pointer
181 :class class
182 :chain-head chain-head
183 :chain-tail chain-tail))
184 (islots (remove-if-not #'islots-slots
185 (mapcar (lambda (super)
186 (compute-islots super class))
187 chain))))
188 (make-instance 'ichain
189 :class class
190 :chain-head chain-head
191 :chain-tail chain-tail
192 :body (cons vtable-pointer islots))))
193
194 ;;; ilayout
195
196 (defmethod print-object ((ilayout ilayout) stream)
197 (print-unreadable-object (ilayout stream :type t)
198 (format stream "~A ~_~:<~@{~S~^ ~_~}~:>"
199 (ilayout-class ilayout)
200 (ilayout-ichains ilayout))))
201
202 (defmethod compute-ilayout ((class sod-class))
203 (make-instance 'ilayout
204 :class class
205 :ichains (mapcar (lambda (chain)
206 (compute-ichain class
207 (reverse chain)))
208 (sod-class-chains class))))
209
210 (defmethod slot-unbound
211 (clos-class (class sod-class) (slot-name (eql 'ilayout)))
212 (declare (ignore clos-class))
213 (setf (slot-value class 'ilayout)
214 (compute-ilayout class)))
215
216 ;;;--------------------------------------------------------------------------
217 ;;; Vtable layout.
218
219 ;;; vtmsgs
220
221 (defmethod print-object ((vtmsgs vtmsgs) stream)
222 (print-unreadable-object (vtmsgs stream :type t)
223 (format stream "~A <= ~A ~_~:<~@{~S~^ ~_~}~:>"
224 (vtmsgs-subclass vtmsgs)
225 (vtmsgs-class vtmsgs)
226 (vtmsgs-entries vtmsgs))))
227
228 (defmethod compute-vtmsgs
229 ((class sod-class)
230 (subclass sod-class)
231 (chain-head sod-class)
232 (chain-tail sod-class))
233 (flet ((make-entries (message)
234 (let ((method (find message
235 (sod-class-effective-methods subclass)
236 :key #'effective-method-message)))
237 (make-method-entries method chain-head chain-tail))))
238 (make-instance 'vtmsgs
239 :class class
240 :subclass subclass
241 :chain-head chain-head
242 :chain-tail chain-tail
243 :entries (mapcan #'make-entries
244 (sod-class-messages class)))))
245
246 ;;; class-pointer
247
248 (defmethod print-object ((cptr class-pointer) stream)
249 (print-unreadable-object (cptr stream :type t)
250 (format stream "~A:~A"
251 (class-pointer-metaclass cptr)
252 (sod-class-nickname (class-pointer-meta-chain-head cptr)))))
253
254 (defmethod make-class-pointer
255 ((class sod-class) (chain-head sod-class)
256 (metaclass sod-class) (meta-chain-head sod-class))
257
258 ;; Slightly tricky. We don't necessarily want a pointer to the metaclass,
259 ;; but to its most specific subclass on the given chain. Fortunately, CL
260 ;; is good at this game.
261 (let* ((meta-chains (sod-class-chains metaclass))
262 (meta-chain-tails (mapcar #'car meta-chains))
263 (meta-chain-tail (find meta-chain-head meta-chain-tails
264 :key #'sod-class-chain-head)))
265 (make-instance 'class-pointer
266 :class class
267 :chain-head chain-head
268 :metaclass meta-chain-tail
269 :meta-chain-head meta-chain-head)))
270
271 ;;; base-offset
272
273 (defmethod print-object ((boff base-offset) stream)
274 (print-unreadable-object (boff stream :type t)
275 (format stream "~A:~A"
276 (base-offset-class boff)
277 (sod-class-nickname (base-offset-chain-head boff)))))
278
279 (defmethod make-base-offset ((class sod-class) (chain-head sod-class))
280 (make-instance 'base-offset
281 :class class
282 :chain-head chain-head))
283
284 ;;; chain-offset
285
286 (defmethod print-object ((choff chain-offset) stream)
287 (print-unreadable-object (choff stream :type t)
288 (format stream "~A:~A->~A"
289 (chain-offset-class choff)
290 (sod-class-nickname (chain-offset-chain-head choff))
291 (sod-class-nickname (chain-offset-target-head choff)))))
292
293 (defmethod make-chain-offset
294 ((class sod-class) (chain-head sod-class) (target-head sod-class))
295 (make-instance 'chain-offset
296 :class class
297 :chain-head chain-head
298 :target-head target-head))
299
300 ;;; vtable
301
302 (defmethod print-object ((vtable vtable) stream)
303 (print-unreadable-object (vtable stream :type t)
304 (format stream "~A:~A ~_~:<~@{~S~^ ~_~}~:>"
305 (vtable-class vtable)
306 (sod-class-nickname (vtable-chain-head vtable))
307 (vtable-body vtable))))
308
309 ;; Special variables used by `compute-vtable'.
310 (defvar *done-metaclass-chains*)
311 (defvar *done-instance-chains*)
312
313 (defmethod compute-vtable-items
314 ((class sod-class) (super sod-class) (chain-head sod-class)
315 (chain-tail sod-class) (emit function))
316
317 ;; If this class introduces new metaclass chains, then emit pointers to
318 ;; them.
319 (let* ((metasuper (sod-class-metaclass super))
320 (metasuper-chains (sod-class-chains metasuper))
321 (metasuper-chain-heads (mapcar (lambda (chain)
322 (sod-class-chain-head (car chain)))
323 metasuper-chains)))
324 (dolist (metasuper-chain-head metasuper-chain-heads)
325 (unless (member metasuper-chain-head *done-metaclass-chains*)
326 (funcall emit (make-class-pointer class
327 chain-head
328 metasuper
329 metasuper-chain-head))
330 (push metasuper-chain-head *done-metaclass-chains*))))
331
332 ;; If there are new instance chains, then emit offsets to them.
333 (let* ((chains (sod-class-chains super))
334 (chain-heads (mapcar (lambda (chain)
335 (sod-class-chain-head (car chain)))
336 chains)))
337 (dolist (head chain-heads)
338 (unless (member head *done-instance-chains*)
339 (funcall emit (make-chain-offset class chain-head head))
340 (push head *done-instance-chains*))))
341
342 ;; Finally, if there are interesting methods, emit those too.
343 (when (sod-class-messages super)
344 (funcall emit (compute-vtmsgs super class chain-head chain-tail))))
345
346 (defmethod compute-vtable ((class sod-class) (chain list))
347 (let* ((chain-head (car chain))
348 (chain-tail (find chain-head (mapcar #'car (sod-class-chains class))
349 :key #'sod-class-chain-head))
350 (*done-metaclass-chains* nil)
351 (*done-instance-chains* (list chain-head))
352 (done-superclasses nil)
353 (items nil))
354 (flet ((emit (item)
355 (push item items)))
356
357 ;; Find the root chain in the metaclass and write a pointer.
358 (let* ((metaclass (sod-class-metaclass class))
359 (metaclass-root (find-root-metaclass class))
360 (metaclass-root-head (sod-class-chain-head metaclass-root)))
361 (emit (make-class-pointer class chain-head metaclass
362 metaclass-root-head))
363 (push metaclass-root-head *done-metaclass-chains*))
364
365 ;; Write an offset to the instance base.
366 (emit (make-base-offset class chain-head))
367
368 ;; Now walk the chain. As we ascend the chain, scan the class
369 ;; precedence list of each class in reverse to ensure that we have
370 ;; everything interesting.
371 (dolist (super chain)
372 (dolist (sub (reverse (sod-class-precedence-list super)))
373 (unless (member sub done-superclasses)
374 (compute-vtable-items class
375 sub
376 chain-head
377 chain-tail
378 #'emit)
379 (push sub done-superclasses))))
380
381 ;; We're through.
382 (make-instance 'vtable
383 :class class
384 :chain-head chain-head
385 :chain-tail chain-tail
386 :body (nreverse items)))))
387
388 (defmethod compute-vtables ((class sod-class))
389 (mapcar (lambda (chain)
390 (compute-vtable class (reverse chain)))
391 (sod-class-chains class)))
392
393 (defmethod slot-unbound
394 (clos-class (class sod-class) (slot-name (eql 'vtables)))
395 (declare (ignore clos-class))
396 (setf (slot-value class 'vtables)
397 (compute-vtables class)))
398
399 ;;;----- That's all, folks --------------------------------------------------