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