3 ;;; Layout for instances and vtables
5 ;;; (c) 2009 Straylight/Edgeware
8 ;;;----- Licensing notice ---------------------------------------------------
10 ;;; This file is part of the Simple Object Definition system.
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 ;;;--------------------------------------------------------------------------
29 ;;; Effective slot objects.
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)
34 (initializer :initarg :initializer :type (or sod-initializer null)
35 :reader effective-slot-initializer))
37 "Describes a slot and how it's meant to be initialized.
39 Effective slot objects are usually attached to layouts."))
41 (defgeneric find-slot-initializer (class slot)
43 "Return the most specific initializer for SLOT, starting from CLASS."))
45 (defgeneric compute-effective-slot (class slot)
47 "Construct an effective slot from the supplied direct slot.
49 SLOT is a direct slot defined on CLASS or one of its superclasses.
50 (Metaclass initializers are handled using a different mechanism.)"))
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))))
58 (defmethod find-slot-initializer ((class sod-class) (slot sod-slot))
61 (sod-class-instance-initializers super)
62 :key #'sod-initializer-slot))
63 (sod-class-precedence-list class)))
65 (defmethod compute-effective-slot ((class sod-class) (slot sod-slot))
66 (make-instance 'effective-slot
69 :initializer (find-slot-initializer class slot)))
71 ;;;--------------------------------------------------------------------------
72 ;;; Instance layout objects.
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))
81 "The collection of effective SLOTS defined by an instance of CLASS."))
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)
88 (islots-slots islots))))
90 (defgeneric compute-islots (class subclass)
92 "Return ISLOTS containing EFFECTIVE-SLOTs for a particular CLASS.
94 Initializers for the slots should be taken from the most specific
95 superclass of SUBCLASS."))
99 (defclass vtable-pointer ()
100 ((class :initarg :class :type sod-class :reader vtable-pointer-class)
101 (chain-head :initarg :chain-head :type sod-class
102 :reader vtable-pointer-chain-head))
104 "A pointer to the vtable for CLASS corresponding to a particular CHAIN."))
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)))))
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))
119 "All of the instance layout for CLASS corresponding to a particular CHAIN.
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
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))))
132 (defgeneric compute-ichain (class chain)
134 "Return an ICHAIN for a particular CHAIN of CLASS's superclasses.
136 The CHAIN is a list of classes, with the least specific first -- so the
137 chain head is the first element."))
142 ((class :initarg :class :type sod-class :reader ilayout-class)
143 (ichains :initarg :ichains :type list :reader ilayout-ichains))
145 "All of the instance layout for a CLASS.
147 Consists of an ICHAIN for each distinct chain."))
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))))
155 (defgeneric compute-ilayout (class)
157 "Compute and return an instance layout for CLASS."))
159 ;;; Standard implementation.
161 (defmethod compute-islots ((class sod-class) (subclass sod-class))
162 (make-instance 'islots
165 :slots (mapcar (lambda (slot)
166 (compute-effective-slot subclass slot))
167 (sod-class-slots class))))
169 (defmethod compute-ichain ((class sod-class) chain)
170 (let* ((head (car chain))
171 (vtable-pointer (make-instance 'vtable-pointer
174 (islots (remove-if-not #'islots-slots
175 (mapcar (lambda (super)
176 (compute-islots super class))
178 (make-instance 'ichain
181 :body (cons vtable-pointer islots))))
183 (defmethod compute-ilayout ((class sod-class))
184 (make-instance 'ilayout
186 :ichains (mapcar (lambda (chain)
187 (compute-ichain class
189 (sod-class-chains class))))
191 ;;;--------------------------------------------------------------------------
192 ;;; Effective methods.
194 (defclass effective-method ()
195 ((message :initarg :message :type sod-message
196 :reader effective-method-message)
197 (class :initarg :class :type sod-class :reader effective-method-class))
199 "The effective method invoked by sending MESSAGE to an instance of CLASS.
201 This is not a useful class by itself. Message classes are expected to
202 define their own effective-method classes.
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
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))))
214 (defgeneric message-effective-method-class (message)
216 "Return the effective method class for the given MESSAGE."))
218 (defgeneric compute-sod-effective-method (message class)
220 "Return the effective method when a CLASS instance receives MESSAGE.
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."))
226 (defmethod compute-sod-effective-method
227 ((message sod-message) (class sod-class))
228 (let ((direct-methods (mapcan (lambda (super)
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)
238 :direct-methods direct-methods)))
240 ;;;--------------------------------------------------------------------------
245 (defclass method-entry ()
246 ((method :initarg :method :type effective-method
247 :reader method-entry-effective-method)
248 (chain-head :initarg :chain-head
250 :reader method-entry-chain-head))
252 "An entry point into an effective method.
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."))
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)))))
268 (defgeneric make-method-entry (effective-method chain-head)
270 "Return a METHOD-ENTRY for an EFFECTIVE-METHOD called via CHAIN-HEAD.
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.)"))
279 ((class :initarg :class :type sod-class :reader vtmsgs-class)
280 (subclass :initarg :subclass :type sod-class :reader vtmsgs-subclass)
281 (chain-head :initarg :chain-head :type sod-class
282 :reader vtmsgs-chain-head)
283 (entries :initarg :entries :type list :reader vtmsgs-entries))
285 "The message dispatch table for a particular CLASS.
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
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))))
298 (defgeneric compute-vtmsgs (class subclass chain-head)
300 "Return a VTMSGS object containing method entries for CLASS.
302 The CHAIN-HEAD describes which chain the method entries should be
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
311 (defclass class-pointer ()
312 ((class :initarg :class :type sod-class :reader class-pointer-class)
313 (chain-head :initarg :chain-head :type sod-class
314 :reader class-pointer-chain-head)
315 (metaclass :initarg :metaclass :type sod-class
316 :reader class-pointer-metaclass)
317 (meta-chain-head :initarg :meta-chain-head :type sod-class
318 :reader class-pointer-meta-chain-head))
320 "Represents a pointer to a class object for the instance's class.
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."))
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)))))
332 (defgeneric make-class-pointer (class chain-head metaclass meta-chain-head)
334 "Return a class pointer to a metaclass chain."))
338 (defclass base-offset ()
339 ((class :initarg :class :type sod-class :reader base-offset-class)
340 (chain-head :initarg :chain-head :type sod-class
341 :reader base-offset-chain-head))
343 "The offset of this chain to the ilayout base.
345 There's only one of these per vtable."))
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)))))
353 (defgeneric make-base-offset (class chain-head)
355 "Return the base offset object for CHAIN-HEAD ichain."))
359 (defclass chain-offset ()
360 ((class :initarg :class :type sod-class :reader chain-offset-class)
361 (chain-head :initarg :chain-head :type sod-class
362 :reader chain-offset-chain-head)
363 (target-head :initarg :target-head :type sod-class
364 :reader chain-offset-target-head))
366 "The offset from the CHAIN-HEAD ichain to the TARGET-HEAD ichain."))
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)))))
375 (defgeneric make-chain-offset (class chain-head target-head)
377 "Return the offset from CHAIN-HEAD to TARGET-HEAD."))
382 ((class :initarg :class :type sod-class :reader vtable-class)
383 (chain-head :initarg :chain-head :type sod-class
384 :reader vtable-chain-head)
385 (body :initarg :body :type list :reader vtable-body))
387 "VTABLEs hold all of the per-chain static information for a class.
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.
392 Vtables contain method entry pointers, pointers to class objects, and
393 the offset information used for cross-chain slot access."))
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))))
402 (defgeneric compute-vtable (class chain)
404 "Compute the vtable layout for a chain of CLASS.
406 The CHAIN is a list of classes, with the least specific first."))
408 (defgeneric compute-vtables (class)
410 "Compute the vtable layouts for CLASS.
412 Returns a list of VTABLE objects in the order of CLASS's chains."))
416 (defmethod compute-vtmsgs
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
428 :chain-head chain-head
429 :entries (mapcar #'make-entry
430 (sod-class-messages class)))))
432 (defmethod make-class-pointer
433 ((class sod-class) (chain-head sod-class)
434 (metaclass sod-class) (meta-chain-head sod-class))
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
445 :chain-head chain-head
446 :metaclass meta-chain-tail
447 :meta-chain-head meta-chain-head)))
449 (defmethod make-base-offset ((class sod-class) (chain-head sod-class))
450 (make-instance 'base-offset
452 :chain-head chain-head))
454 (defmethod make-chain-offset
455 ((class sod-class) (chain-head sod-class) (target-head sod-class))
456 (make-instance 'chain-offset
458 :chain-head chain-head
459 :target-head target-head))
461 ;; Special variables used by COMPUTE-VTABLE.
462 (defvar *done-metaclass-chains*)
463 (defvar *done-instance-chains*)
465 (defgeneric compute-vtable-items (class super chain-head emit)
467 "Emit vtable items for a superclass of CLASS.
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.
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:
476 * In a method on COMPUTE-VTABLE, bind a special variable to an empty
479 * In a method on this function, check the variable or hash table.
481 This function is the real business end of COMPUTE-VTABLE."))
483 (defmethod compute-vtable-items
484 ((class sod-class) (super sod-class) (chain-head sod-class)
487 ;; If this class introduces new metaclass chains, then emit pointers to
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)))
494 (dolist (metasuper-chain-head metasuper-chain-heads)
495 (unless (member metasuper-chain-head *done-metaclass-chains*)
496 (funcall emit (make-class-pointer class
499 metasuper-chain-head))
500 (push metasuper-chain-head *done-metaclass-chains*))))
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)))
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*))))
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))))
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)
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
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*))
539 ;; Write an offset to the instance base.
540 (emit (make-base-offset class chain-head))
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
552 (push sub done-superclasses))))
555 (make-instance 'vtable
557 :chain-head chain-head
558 :body (nreverse items)))))
560 (defgeneric compute-effective-methods (class)
562 "Return a list of all of the effective methods needed for CLASS.
564 The list needn't be in any particular order."))
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)))
573 (defmethod compute-vtables ((class sod-class))
574 (mapcar (lambda (chain)
575 (compute-vtable class (reverse chain)))
576 (sod-class-chains class)))
578 ;;;--------------------------------------------------------------------------
581 (defun islots-struct-tag (class)
582 (format nil "~A__islots" class))
584 (defun ichain-struct-tag (class chain-head)
585 (format nil "~A__ichain_~A" class(sod-class-nickname chain-head)))
587 (defun ilayout-struct-tag (class)
588 (format nil "~A__ilayout" class))
590 (defun vtmsgs-struct-tag (class super)
591 (format nil "~A__vtmsgs_~A" class (sod-class-nickname super)))
593 (defun vtable-struct-tag (class chain-head)
594 (format nil "~A__vt_~A" class (sod-class-nickname chain-head)))
596 (defun vtable-name (class chain-head)
597 (format nil "~A__vtable_~A" class (sod-class-nickname chain-head)))
599 ;;;--------------------------------------------------------------------------
602 (defclass hacky-effective-method (effective-method)
603 ((direct-methods :initarg :direct-methods)))
605 (defmethod print-object ((method hacky-effective-method) stream)
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)))
614 (defmethod message-effective-method-class ((message sod-message))
615 'hacky-effective-method)
617 (defmethod make-method-entry
618 ((method hacky-effective-method) (chain-head sod-class))
619 (make-instance 'method-entry
621 :chain-head chain-head))
623 ;;;----- That's all, folks --------------------------------------------------