Commit | Line | Data |
---|---|---|
1f1d88f5 MW |
1 | ;;; -*-lisp-*- |
2 | ;;; | |
3 | ;;; Layout for instances and vtables | |
4 | ;;; | |
5 | ;;; (c) 2009 Straylight/Edgeware | |
6 | ;;; | |
7 | ||
8 | ;;;----- Licensing notice --------------------------------------------------- | |
9 | ;;; | |
10 | ;;; This file is part of the Simple Object Definition system. | |
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 slot objects. | |
30 | ||
31 | (defclass effective-slot () | |
32 | ((class :initarg :class :type sod-slot :reader effective-slot-class) | |
33 | (slot :initarg :slot :type sod-slot :reader effective-slot-direct-slot) | |
77027cca | 34 | (initializer :initarg :initializer :type (or sod-initializer null) |
1f1d88f5 MW |
35 | :reader effective-slot-initializer)) |
36 | (:documentation | |
37 | "Describes a slot and how it's meant to be initialized. | |
38 | ||
39 | Effective slot objects are usually attached to layouts.")) | |
40 | ||
41 | (defgeneric find-slot-initializer (class slot) | |
42 | (:documentation | |
43 | "Return the most specific initializer for SLOT, starting from CLASS.")) | |
44 | ||
45 | (defgeneric compute-effective-slot (class slot) | |
46 | (:documentation | |
47 | "Construct an effective slot from the supplied direct slot. | |
48 | ||
49 | SLOT is a direct slot defined on CLASS or one of its superclasses. | |
50 | (Metaclass initializers are handled using a different mechanism.)")) | |
51 | ||
52 | (defmethod print-object ((slot effective-slot) stream) | |
53 | (maybe-print-unreadable-object (slot stream :type t) | |
54 | (format stream "~A~@[ = ~@_~A~]" | |
55 | (effective-slot-direct-slot slot) | |
56 | (effective-slot-initializer slot)))) | |
57 | ||
58 | (defmethod find-slot-initializer ((class sod-class) (slot sod-slot)) | |
59 | (some (lambda (super) | |
60 | (find slot | |
61 | (sod-class-instance-initializers super) | |
62 | :key #'sod-initializer-slot)) | |
63 | (sod-class-precedence-list class))) | |
64 | ||
65 | (defmethod compute-effective-slot ((class sod-class) (slot sod-slot)) | |
66 | (make-instance 'effective-slot | |
67 | :slot slot | |
68 | :class class | |
69 | :initializer (find-slot-initializer class slot))) | |
70 | ||
71 | ;;;-------------------------------------------------------------------------- | |
72 | ;;; Instance layout objects. | |
73 | ||
74 | ;;; islots | |
75 | ||
76 | (defclass islots () | |
77 | ((class :initarg :class :type sod-class :reader islots-class) | |
78 | (subclass :initarg :subclass :type sod-class :reader islots-subclass) | |
79 | (slots :initarg :slots :type list :reader islots-slots)) | |
80 | (:documentation | |
81 | "The collection of effective SLOTS defined by an instance of CLASS.")) | |
82 | ||
83 | (defmethod print-object ((islots islots) stream) | |
84 | (print-unreadable-object (islots stream :type t) | |
85 | (format stream "~A <= ~A ~_~:<~@{~S~^ ~_~}~:>" | |
86 | (islots-subclass islots) | |
87 | (islots-class islots) | |
88 | (islots-slots islots)))) | |
89 | ||
90 | (defgeneric compute-islots (class subclass) | |
91 | (:documentation | |
92 | "Return ISLOTS containing EFFECTIVE-SLOTs for a particular CLASS. | |
93 | ||
94 | Initializers for the slots should be taken from the most specific | |
95 | superclass of SUBCLASS.")) | |
96 | ||
97 | ;;; vtable-pointer | |
98 | ||
99 | (defclass vtable-pointer () | |
100 | ((class :initarg :class :type sod-class :reader vtable-pointer-class) | |
77027cca | 101 | (chain-head :initarg :chain-head :type sod-class |
1f1d88f5 MW |
102 | :reader vtable-pointer-chain-head)) |
103 | (:documentation | |
104 | "A pointer to the vtable for CLASS corresponding to a particular CHAIN.")) | |
105 | ||
106 | (defmethod print-object ((vtp vtable-pointer) stream) | |
107 | (print-unreadable-object (vtp stream :type t) | |
108 | (format stream "~A:~A" | |
109 | (vtable-pointer-class vtp) | |
110 | (sod-class-nickname (vtable-pointer-chain-head vtp))))) | |
111 | ||
112 | ;;; ichain | |
113 | ||
114 | (defclass ichain () | |
115 | ((class :initarg :class :type sod-class :reader ichain-class) | |
116 | (chain-head :initarg :chain-head :type sod-class :reader ichain-head) | |
117 | (body :initarg :body :type list :reader ichain-body)) | |
118 | (:documentation | |
119 | "All of the instance layout for CLASS corresponding to a particular CHAIN. | |
120 | ||
121 | The BODY is a list of things to include in the finished structure. By | |
122 | default, it contains a VTABLE-POINTER and ISLOTS for each class in the | |
123 | chain.")) | |
124 | ||
125 | (defmethod print-object ((ichain ichain) stream) | |
126 | (print-unreadable-object (ichain stream :type t) | |
127 | (format stream "~A:~A ~_~:<~@{~S~^ ~_~}~:>" | |
128 | (ichain-class ichain) | |
129 | (sod-class-nickname (ichain-head ichain)) | |
130 | (ichain-body ichain)))) | |
131 | ||
132 | (defgeneric compute-ichain (class chain) | |
133 | (:documentation | |
134 | "Return an ICHAIN for a particular CHAIN of CLASS's superclasses. | |
135 | ||
136 | The CHAIN is a list of classes, with the least specific first -- so the | |
137 | chain head is the first element.")) | |
138 | ||
139 | ;;; ilayout | |
140 | ||
141 | (defclass ilayout () | |
142 | ((class :initarg :class :type sod-class :reader ilayout-class) | |
143 | (ichains :initarg :ichains :type list :reader ilayout-ichains)) | |
144 | (:documentation | |
145 | "All of the instance layout for a CLASS. | |
146 | ||
147 | Consists of an ICHAIN for each distinct chain.")) | |
148 | ||
149 | (defmethod print-object ((ilayout ilayout) stream) | |
150 | (print-unreadable-object (ilayout stream :type t) | |
151 | (format stream "~A ~_~:<~@{~S~^ ~_~}~:>" | |
152 | (ilayout-class ilayout) | |
153 | (ilayout-ichains ilayout)))) | |
154 | ||
155 | (defgeneric compute-ilayout (class) | |
156 | (:documentation | |
157 | "Compute and return an instance layout for CLASS.")) | |
158 | ||
159 | ;;; Standard implementation. | |
160 | ||
161 | (defmethod compute-islots ((class sod-class) (subclass sod-class)) | |
162 | (make-instance 'islots | |
163 | :class class | |
164 | :subclass subclass | |
165 | :slots (mapcar (lambda (slot) | |
166 | (compute-effective-slot subclass slot)) | |
167 | (sod-class-slots class)))) | |
168 | ||
169 | (defmethod compute-ichain ((class sod-class) chain) | |
170 | (let* ((head (car chain)) | |
171 | (vtable-pointer (make-instance 'vtable-pointer | |
172 | :class class | |
173 | :chain-head head)) | |
174 | (islots (remove-if-not #'islots-slots | |
175 | (mapcar (lambda (super) | |
176 | (compute-islots super class)) | |
177 | chain)))) | |
178 | (make-instance 'ichain | |
179 | :class class | |
180 | :chain-head head | |
181 | :body (cons vtable-pointer islots)))) | |
182 | ||
183 | (defmethod compute-ilayout ((class sod-class)) | |
184 | (make-instance 'ilayout | |
185 | :class class | |
186 | :ichains (mapcar (lambda (chain) | |
187 | (compute-ichain class | |
188 | (reverse chain))) | |
189 | (sod-class-chains class)))) | |
190 | ||
191 | ;;;-------------------------------------------------------------------------- | |
192 | ;;; Effective methods. | |
193 | ||
194 | (defclass effective-method () | |
77027cca | 195 | ((message :initarg :message :type sod-message |
1f1d88f5 | 196 | :reader effective-method-message) |
77027cca | 197 | (class :initarg :class :type sod-class :reader effective-method-class)) |
1f1d88f5 MW |
198 | (:documentation |
199 | "The effective method invoked by sending MESSAGE to an instance of CLASS. | |
200 | ||
201 | This is not a useful class by itself. Message classes are expected to | |
202 | define their own effective-method classes. | |
203 | ||
204 | An effective method class must accept a :DIRECT-METHODS initarg, which | |
205 | will be a list of applicable methods sorted in most-to-least specific | |
206 | order.")) | |
207 | ||
208 | (defmethod print-object ((method effective-method) stream) | |
209 | (maybe-print-unreadable-object (method stream :type t) | |
210 | (format stream "~A ~A" | |
211 | (effective-method-message method) | |
212 | (effective-method-class method)))) | |
213 | ||
214 | (defgeneric message-effective-method-class (message) | |
215 | (:documentation | |
216 | "Return the effective method class for the given MESSAGE.")) | |
217 | ||
218 | (defgeneric compute-sod-effective-method (message class) | |
219 | (:documentation | |
220 | "Return the effective method when a CLASS instance receives MESSAGE. | |
221 | ||
222 | The default method constructs an instance of the message's chosen | |
223 | MESSAGE-EFFECTIVE-METHOD-CLASS, passing the MESSAGE, the CLASS and the | |
224 | list of applicable methods as initargs to MAKE-INSTANCE.")) | |
225 | ||
226 | (defmethod compute-sod-effective-method | |
227 | ((message sod-message) (class sod-class)) | |
228 | (let ((direct-methods (mapcan (lambda (super) | |
229 | (let ((method | |
230 | (find message | |
231 | (sod-class-methods super) | |
232 | :key #'sod-method-message))) | |
233 | (and method (list method)))) | |
234 | (sod-class-precedence-list class)))) | |
235 | (make-instance (message-effective-method-class message) | |
236 | :message message | |
237 | :class class | |
238 | :direct-methods direct-methods))) | |
239 | ||
240 | ;;;-------------------------------------------------------------------------- | |
241 | ;;; Vtable layout. | |
242 | ||
243 | ;;; method-entry | |
244 | ||
245 | (defclass method-entry () | |
77027cca | 246 | ((method :initarg :method :type effective-method |
1f1d88f5 MW |
247 | :reader method-entry-effective-method) |
248 | (chain-head :initarg :chain-head | |
249 | :type sod-class | |
250 | :reader method-entry-chain-head)) | |
251 | (:documentation | |
252 | "An entry point into an effective method. | |
253 | ||
254 | Calls to an effective method via different vtable chains will have their | |
255 | `me' pointers pointing to different ichains within the instance layout. | |
256 | Rather than (necessarily) duplicating the entire effective method for each | |
257 | chain, we insert an entry veneer (the method entry) to fix up the pointer. | |
258 | Exactly how it does this is up to the effective method -- and duplication | |
259 | under some circumstances is probably a reasonable approach -- e.g., if the | |
260 | effective method is just going to call a direct method immediately.")) | |
261 | ||
262 | (defmethod print-object ((entry method-entry) stream) | |
263 | (maybe-print-unreadable-object (entry stream :type t) | |
264 | (format stream "~A:~A" | |
265 | (method-entry-effective-method entry) | |
266 | (sod-class-nickname (method-entry-chain-head entry))))) | |
267 | ||
268 | (defgeneric make-method-entry (effective-method chain-head) | |
269 | (:documentation | |
270 | "Return a METHOD-ENTRY for an EFFECTIVE-METHOD called via CHAIN-HEAD. | |
271 | ||
272 | There is no default method for this function. (Maybe when the | |
273 | effective-method/method-entry output protocol has settled down I'll know | |
274 | what a sensible default action would be.)")) | |
275 | ||
276 | ;;; vtmsgs | |
277 | ||
278 | (defclass vtmsgs () | |
279 | ((class :initarg :class :type sod-class :reader vtmsgs-class) | |
280 | (subclass :initarg :subclass :type sod-class :reader vtmsgs-subclass) | |
77027cca | 281 | (chain-head :initarg :chain-head :type sod-class |
1f1d88f5 MW |
282 | :reader vtmsgs-chain-head) |
283 | (entries :initarg :entries :type list :reader vtmsgs-entries)) | |
284 | (:documentation | |
285 | "The message dispatch table for a particular CLASS. | |
286 | ||
287 | The BODY contains a list of effective method objects for the messages | |
288 | defined on CLASS, customized for calling from the chain headed by | |
289 | CHAIN-HEAD.")) | |
290 | ||
291 | (defmethod print-object ((vtmsgs vtmsgs) stream) | |
292 | (print-unreadable-object (vtmsgs stream :type t) | |
293 | (format stream "~A <= ~A ~_~:<~@{~S~^ ~_~}~:>" | |
294 | (vtmsgs-subclass vtmsgs) | |
295 | (vtmsgs-class vtmsgs) | |
296 | (vtmsgs-entries vtmsgs)))) | |
297 | ||
298 | (defgeneric compute-vtmsgs (class subclass chain-head) | |
299 | (:documentation | |
300 | "Return a VTMSGS object containing method entries for CLASS. | |
301 | ||
302 | The CHAIN-HEAD describes which chain the method entries should be | |
303 | constructed for. | |
304 | ||
305 | The default method simply calls MAKE-METHOD-ENTRY for each of the methods | |
306 | and wraps a VTMSGS object around them. This ought to be enough for almost | |
307 | all purposes.")) | |
308 | ||
309 | ;;; class-pointer | |
310 | ||
311 | (defclass class-pointer () | |
77027cca MW |
312 | ((class :initarg :class :type sod-class :reader class-pointer-class) |
313 | (chain-head :initarg :chain-head :type sod-class | |
1f1d88f5 | 314 | :reader class-pointer-chain-head) |
77027cca | 315 | (metaclass :initarg :metaclass :type sod-class |
1f1d88f5 | 316 | :reader class-pointer-metaclass) |
77027cca | 317 | (meta-chain-head :initarg :meta-chain-head :type sod-class |
1f1d88f5 MW |
318 | :reader class-pointer-meta-chain-head)) |
319 | (:documentation | |
320 | "Represents a pointer to a class object for the instance's class. | |
321 | ||
322 | A class instance can have multiple chains. It may be useful to find any | |
323 | of those chains from an instance of the class. Therefore the vtable | |
324 | stores a pointer to each separate chain of the class instance.")) | |
325 | ||
326 | (defmethod print-object ((cptr class-pointer) stream) | |
327 | (print-unreadable-object (cptr stream :type t) | |
328 | (format stream "~A:~A" | |
329 | (class-pointer-metaclass cptr) | |
330 | (sod-class-nickname (class-pointer-meta-chain-head cptr))))) | |
331 | ||
332 | (defgeneric make-class-pointer (class chain-head metaclass meta-chain-head) | |
333 | (:documentation | |
334 | "Return a class pointer to a metaclass chain.")) | |
335 | ||
336 | ;;; base-offset | |
337 | ||
338 | (defclass base-offset () | |
339 | ((class :initarg :class :type sod-class :reader base-offset-class) | |
77027cca | 340 | (chain-head :initarg :chain-head :type sod-class |
1f1d88f5 MW |
341 | :reader base-offset-chain-head)) |
342 | (:documentation | |
343 | "The offset of this chain to the ilayout base. | |
344 | ||
345 | There's only one of these per vtable.")) | |
346 | ||
347 | (defmethod print-object ((boff base-offset) stream) | |
348 | (print-unreadable-object (boff stream :type t) | |
349 | (format stream "~A:~A" | |
350 | (base-offset-class boff) | |
351 | (sod-class-nickname (base-offset-chain-head boff))))) | |
352 | ||
353 | (defgeneric make-base-offset (class chain-head) | |
354 | (:documentation | |
355 | "Return the base offset object for CHAIN-HEAD ichain.")) | |
356 | ||
357 | ;;; chain-offset | |
358 | ||
359 | (defclass chain-offset () | |
360 | ((class :initarg :class :type sod-class :reader chain-offset-class) | |
77027cca | 361 | (chain-head :initarg :chain-head :type sod-class |
1f1d88f5 | 362 | :reader chain-offset-chain-head) |
77027cca | 363 | (target-head :initarg :target-head :type sod-class |
1f1d88f5 MW |
364 | :reader chain-offset-target-head)) |
365 | (:documentation | |
366 | "The offset from the CHAIN-HEAD ichain to the TARGET-HEAD ichain.")) | |
367 | ||
368 | (defmethod print-object ((choff chain-offset) stream) | |
369 | (print-unreadable-object (choff stream :type t) | |
370 | (format stream "~A:~A->~A" | |
371 | (chain-offset-class choff) | |
372 | (sod-class-nickname (chain-offset-chain-head choff)) | |
373 | (sod-class-nickname (chain-offset-target-head choff))))) | |
374 | ||
375 | (defgeneric make-chain-offset (class chain-head target-head) | |
376 | (:documentation | |
377 | "Return the offset from CHAIN-HEAD to TARGET-HEAD.")) | |
378 | ||
379 | ;;; vtable | |
380 | ||
381 | (defclass vtable () | |
382 | ((class :initarg :class :type sod-class :reader vtable-class) | |
77027cca | 383 | (chain-head :initarg :chain-head :type sod-class |
1f1d88f5 MW |
384 | :reader vtable-chain-head) |
385 | (body :initarg :body :type list :reader vtable-body)) | |
386 | (:documentation | |
387 | "VTABLEs hold all of the per-chain static information for a class. | |
388 | ||
389 | There is one vtable for each chain of each class. The vtables for a class | |
390 | are prefixes of the corresponding chains of its subclasses. | |
391 | ||
392 | Vtables contain method entry pointers, pointers to class objects, and | |
393 | the offset information used for cross-chain slot access.")) | |
394 | ||
395 | (defmethod print-object ((vtable vtable) stream) | |
396 | (print-unreadable-object (vtable stream :type t) | |
397 | (format stream "~A:~A ~_~:<~@{~S~^ ~_~}~:>" | |
398 | (vtable-class vtable) | |
399 | (sod-class-nickname (vtable-chain-head vtable)) | |
400 | (vtable-body vtable)))) | |
401 | ||
402 | (defgeneric compute-vtable (class chain) | |
403 | (:documentation | |
404 | "Compute the vtable layout for a chain of CLASS. | |
405 | ||
406 | The CHAIN is a list of classes, with the least specific first.")) | |
407 | ||
408 | (defgeneric compute-vtables (class) | |
409 | (:documentation | |
410 | "Compute the vtable layouts for CLASS. | |
411 | ||
412 | Returns a list of VTABLE objects in the order of CLASS's chains.")) | |
413 | ||
414 | ;;; Implementation. | |
415 | ||
416 | (defmethod compute-vtmsgs | |
417 | ((class sod-class) | |
418 | (subclass sod-class) | |
419 | (chain-head sod-class)) | |
420 | (flet ((make-entry (message) | |
421 | (let ((method (find message | |
422 | (sod-class-effective-methods subclass) | |
423 | :key #'effective-method-message))) | |
424 | (make-method-entry method chain-head)))) | |
425 | (make-instance 'vtmsgs | |
426 | :class class | |
427 | :subclass subclass | |
428 | :chain-head chain-head | |
429 | :entries (mapcar #'make-entry | |
430 | (sod-class-messages class))))) | |
431 | ||
432 | (defmethod make-class-pointer | |
433 | ((class sod-class) (chain-head sod-class) | |
434 | (metaclass sod-class) (meta-chain-head sod-class)) | |
435 | ||
436 | ;; Slightly tricky. We don't necessarily want a pointer to the metaclass, | |
437 | ;; but to its most specific subclass on the given chain. Fortunately, CL | |
438 | ;; is good at this game. | |
439 | (let* ((meta-chains (sod-class-chains metaclass)) | |
440 | (meta-chain-tails (mapcar #'car meta-chains)) | |
441 | (meta-chain-tail (find meta-chain-head meta-chain-tails | |
442 | :key #'sod-class-chain-head))) | |
443 | (make-instance 'class-pointer | |
444 | :class class | |
445 | :chain-head chain-head | |
446 | :metaclass meta-chain-tail | |
447 | :meta-chain-head meta-chain-head))) | |
448 | ||
449 | (defmethod make-base-offset ((class sod-class) (chain-head sod-class)) | |
450 | (make-instance 'base-offset | |
451 | :class class | |
452 | :chain-head chain-head)) | |
453 | ||
454 | (defmethod make-chain-offset | |
455 | ((class sod-class) (chain-head sod-class) (target-head sod-class)) | |
456 | (make-instance 'chain-offset | |
457 | :class class | |
458 | :chain-head chain-head | |
459 | :target-head target-head)) | |
460 | ||
461 | ;; Special variables used by COMPUTE-VTABLE. | |
462 | (defvar *done-metaclass-chains*) | |
463 | (defvar *done-instance-chains*) | |
464 | ||
465 | (defgeneric compute-vtable-items (class super chain-head emit) | |
466 | (:documentation | |
467 | "Emit vtable items for a superclass of CLASS. | |
468 | ||
469 | This function is called for each superclass SUPER of CLASS reached on the | |
470 | chain headed by CHAIN-HEAD. The function should call EMIT for each | |
471 | vtable item it wants to write. | |
472 | ||
473 | The right way to check to see whether items have already been emitted | |
474 | (e.g., has an offset to some other chain been emitted?) is as follows: | |
475 | ||
476 | * In a method on COMPUTE-VTABLE, bind a special variable to an empty | |
477 | list or hash table. | |
478 | ||
479 | * In a method on this function, check the variable or hash table. | |
480 | ||
481 | This function is the real business end of COMPUTE-VTABLE.")) | |
482 | ||
483 | (defmethod compute-vtable-items | |
484 | ((class sod-class) (super sod-class) (chain-head sod-class) | |
485 | (emit function)) | |
486 | ||
487 | ;; If this class introduces new metaclass chains, then emit pointers to | |
488 | ;; them. | |
489 | (let* ((metasuper (sod-class-metaclass super)) | |
490 | (metasuper-chains (sod-class-chains metasuper)) | |
491 | (metasuper-chain-heads (mapcar (lambda (chain) | |
492 | (sod-class-chain-head (car chain))) | |
493 | metasuper-chains))) | |
494 | (dolist (metasuper-chain-head metasuper-chain-heads) | |
495 | (unless (member metasuper-chain-head *done-metaclass-chains*) | |
496 | (funcall emit (make-class-pointer class | |
497 | chain-head | |
498 | metasuper | |
499 | metasuper-chain-head)) | |
500 | (push metasuper-chain-head *done-metaclass-chains*)))) | |
501 | ||
502 | ;; If there are new instance chains, then emit offsets to them. | |
503 | (let* ((chains (sod-class-chains super)) | |
504 | (chain-heads (mapcar (lambda (chain) | |
505 | (sod-class-chain-head (car chain))) | |
506 | chains))) | |
507 | (dolist (head chain-heads) | |
508 | (unless (member head *done-instance-chains*) | |
509 | (funcall emit (make-chain-offset class chain-head head)) | |
510 | (push head *done-instance-chains*)))) | |
511 | ||
512 | ;; Finally, if there are interesting methods, emit those too. | |
513 | (when (sod-class-messages super) | |
514 | (funcall emit (compute-vtmsgs super class chain-head)))) | |
515 | ||
516 | (defmethod compute-vtable ((class sod-class) (chain list)) | |
517 | (let* ((chain-head (car chain)) | |
518 | (*done-metaclass-chains* nil) | |
519 | (*done-instance-chains* (list chain-head)) | |
520 | (done-superclasses nil) | |
521 | (items nil)) | |
522 | (flet ((emit (item) | |
523 | (push item items))) | |
524 | ||
525 | ;; Find the root chain in the metaclass and write a pointer. | |
526 | (let* ((metaclass (sod-class-metaclass class)) | |
527 | (metaclass-chains (sod-class-chains metaclass)) | |
528 | (metaclass-chain-heads (mapcar (lambda (chain) | |
529 | (sod-class-chain-head | |
530 | (car chain))) | |
531 | metaclass-chains)) | |
532 | (metaclass-root-chain (find-if-not | |
533 | #'sod-class-direct-superclasses | |
534 | metaclass-chain-heads))) | |
535 | (emit (make-class-pointer class chain-head | |
536 | metaclass metaclass-root-chain)) | |
537 | (push metaclass-root-chain *done-metaclass-chains*)) | |
538 | ||
539 | ;; Write an offset to the instance base. | |
540 | (emit (make-base-offset class chain-head)) | |
541 | ||
542 | ;; Now walk the chain. As we ascend the chain, scan the class | |
543 | ;; precedence list of each class in reverse to ensure that we have | |
544 | ;; everything interesting. | |
545 | (dolist (super chain) | |
546 | (dolist (sub (reverse (sod-class-precedence-list super))) | |
547 | (unless (member sub done-superclasses) | |
548 | (compute-vtable-items class | |
549 | sub | |
550 | chain-head | |
551 | #'emit) | |
552 | (push sub done-superclasses)))) | |
553 | ||
554 | ;; We're through. | |
555 | (make-instance 'vtable | |
556 | :class class | |
557 | :chain-head chain-head | |
558 | :body (nreverse items))))) | |
559 | ||
560 | (defgeneric compute-effective-methods (class) | |
561 | (:documentation | |
562 | "Return a list of all of the effective methods needed for CLASS. | |
563 | ||
564 | The list needn't be in any particular order.")) | |
565 | ||
566 | (defmethod compute-effective-methods ((class sod-class)) | |
567 | (mapcan (lambda (super) | |
568 | (mapcar (lambda (message) | |
569 | (compute-sod-effective-method message class)) | |
570 | (sod-class-messages super))) | |
571 | (sod-class-precedence-list class))) | |
572 | ||
573 | (defmethod compute-vtables ((class sod-class)) | |
574 | (mapcar (lambda (chain) | |
575 | (compute-vtable class (reverse chain))) | |
576 | (sod-class-chains class))) | |
577 | ||
578 | ;;;-------------------------------------------------------------------------- | |
579 | ;;; Names of things. | |
580 | ||
581 | (defun islots-struct-tag (class) | |
582 | (format nil "~A__islots" class)) | |
583 | ||
584 | (defun ichain-struct-tag (class chain-head) | |
585 | (format nil "~A__ichain_~A" class(sod-class-nickname chain-head))) | |
586 | ||
587 | (defun ilayout-struct-tag (class) | |
588 | (format nil "~A__ilayout" class)) | |
589 | ||
590 | (defun vtmsgs-struct-tag (class super) | |
591 | (format nil "~A__vtmsgs_~A" class (sod-class-nickname super))) | |
592 | ||
593 | (defun vtable-struct-tag (class chain-head) | |
594 | (format nil "~A__vt_~A" class (sod-class-nickname chain-head))) | |
595 | ||
596 | (defun vtable-name (class chain-head) | |
597 | (format nil "~A__vtable_~A" class (sod-class-nickname chain-head))) | |
598 | ||
599 | ;;;-------------------------------------------------------------------------- | |
600 | ;;; Hacks for now. | |
601 | ||
602 | (defclass hacky-effective-method (effective-method) | |
603 | ((direct-methods :initarg :direct-methods))) | |
604 | ||
605 | (defmethod print-object ((method hacky-effective-method) stream) | |
606 | (if *print-escape* | |
607 | (print-unreadable-object (method stream :type t) | |
608 | (format stream "~A ~_~A ~_~:<~@{~S~^ ~_~}~:>" | |
609 | (effective-method-message method) | |
610 | (effective-method-class method) | |
611 | (slot-value method 'direct-methods))) | |
612 | (call-next-method))) | |
613 | ||
614 | (defmethod message-effective-method-class ((message sod-message)) | |
615 | 'hacky-effective-method) | |
616 | ||
617 | (defmethod make-method-entry | |
618 | ((method hacky-effective-method) (chain-head sod-class)) | |
619 | (make-instance 'method-entry | |
620 | :method method | |
621 | :chain-head chain-head)) | |
622 | ||
623 | ;;;----- That's all, folks -------------------------------------------------- |