--- /dev/null
+;;; -*-lisp-*-
+;;;
+;;; Class layout protocol implementation
+;;;
+;;; (c) 2009 Straylight/Edgeware
+;;;
+
+;;;----- Licensing notice ---------------------------------------------------
+;;;
+;;; This file is part of the Sensble Object Design, an object system for C.
+;;;
+;;; SOD is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 2 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; SOD is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with SOD; if not, write to the Free Software Foundation,
+;;; Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+(cl:in-package #:sod)
+
+;;;--------------------------------------------------------------------------
+;;; Effective slots.
+
+(defmethod print-object ((slot effective-slot) stream)
+ (maybe-print-unreadable-object (slot stream :type t)
+ (format stream "~A~@[ = ~@_~A~]"
+ (effective-slot-direct-slot slot)
+ (effective-slot-initializer slot))))
+
+(defmethod find-slot-initializer ((class sod-class) (slot sod-slot))
+ (some (lambda (super)
+ (find slot
+ (sod-class-instance-initializers super)
+ :key #'sod-initializer-slot))
+ (sod-class-precedence-list class)))
+
+(defmethod compute-effective-slot ((class sod-class) (slot sod-slot))
+ (make-instance 'effective-slot
+ :slot slot
+ :class class
+ :initializer (find-slot-initializer class slot)))
+
+;;;--------------------------------------------------------------------------
+;;; Special-purpose slot objects.
+
+(export 'sod-class-slot)
+(defclass sod-class-slot (sod-slot)
+ ((initializer-function :initarg :initializer-function
+ :type (or symbol function)
+ :reader sod-slot-initializer-function)
+ (prepare-function :initarg :prepare-function :type (or symbol function)
+ :reader sod-slot-prepare-function))
+ (:documentation
+ "Special class for slots defined on SodClass.
+
+ These slots need class-specific initialization. It's easier to keep all
+ of the information (name, type, and how to initialize them) about these
+ slots in one place, so that's what we do here."))
+
+(defmethod shared-initialize :after
+ ((slot sod-class-slot) slot-names &key pset)
+ (declare (ignore slot-names))
+ (default-slot (slot 'initializer-function)
+ (get-property pset :initializer-function t nil))
+ (default-slot (slot 'prepare-function)
+ (get-property pset :prepare-function t nil)))
+
+(export 'sod-class-effective-slot)
+(defclass sod-class-effective-slot (effective-slot)
+ ((initializer-function :initarg :initializer-function
+ :type (or symbol function)
+ :reader effective-slot-initializer-function)
+ (prepare-function :initarg :prepare-function :type (or symbol function)
+ :reader effective-slot-prepare-function))
+ (:documentation
+ "Special class for slots defined on SodClass.
+
+ This class ignores any explicit initializers and computes initializer
+ values using the slot's INIT-FUNC slot and a magical protocol during
+ metaclass instance construction."))
+
+(defmethod compute-effective-slot ((class sod-class) (slot sod-class-slot))
+ (make-instance 'sod-class-effective-slot
+ :class class :slot slot
+ :initializer-function (sod-slot-initializer-function slot)
+ :prepare-function (sod-slot-prepare-function slot)
+ :initializer (find-slot-initializer class slot)))
+
+;;;--------------------------------------------------------------------------
+;;; Effective methods.
+
+(defmethod print-object ((method effective-method) stream)
+ (maybe-print-unreadable-object (method stream :type t)
+ (format stream "~A ~A"
+ (effective-method-message method)
+ (effective-method-class method))))
+
+(defmethod print-object ((entry method-entry) stream)
+ (maybe-print-unreadable-object (entry stream :type t)
+ (format stream "~A:~A"
+ (method-entry-effective-method entry)
+ (sod-class-nickname (method-entry-chain-head entry)))))
+
+(defmethod compute-sod-effective-method
+ ((message sod-message) (class sod-class))
+ (let ((direct-methods (mappend (lambda (super)
+ (remove message
+ (sod-class-methods super)
+ :key #'sod-method-message
+ :test-not #'eql))
+ (sod-class-precedence-list class))))
+ (make-instance (message-effective-method-class message)
+ :message message
+ :class class
+ :direct-methods direct-methods)))
+
+(defmethod compute-effective-methods ((class sod-class))
+ (mapcan (lambda (super)
+ (mapcar (lambda (message)
+ (compute-sod-effective-method message class))
+ (sod-class-messages super)))
+ (sod-class-precedence-list class)))
+
+(defmethod slot-unbound
+ (clos-class (class sod-class) (slot-name (eql 'effective-methods)))
+ (setf (slot-value class 'effective-methods)
+ (compute-effective-methods class)))
+
+;;;--------------------------------------------------------------------------
+;;; Instance layout.
+
+;;; islots
+
+(defmethod print-object ((islots islots) stream)
+ (print-unreadable-object (islots stream :type t)
+ (format stream "~A <= ~A ~_~:<~@{~S~^ ~_~}~:>"
+ (islots-subclass islots)
+ (islots-class islots)
+ (islots-slots islots))))
+
+(defmethod compute-islots ((class sod-class) (subclass sod-class))
+ (make-instance 'islots
+ :class class
+ :subclass subclass
+ :slots (mapcar (lambda (slot)
+ (compute-effective-slot subclass slot))
+ (sod-class-slots class))))
+
+;;; vtable-pointer
+;;; Do we need a construction protocol here?
+
+(defmethod print-object ((vtp vtable-pointer) stream)
+ (print-unreadable-object (vtp stream :type t)
+ (format stream "~A:~A"
+ (vtable-pointer-class vtp)
+ (sod-class-nickname (vtable-pointer-chain-head vtp)))))
+
+;;; ichain
+
+(defmethod print-object ((ichain ichain) stream)
+ (print-unreadable-object (ichain stream :type t)
+ (format stream "~A:~A ~_~:<~@{~S~^ ~_~}~:>"
+ (ichain-class ichain)
+ (sod-class-nickname (ichain-head ichain))
+ (ichain-body ichain))))
+
+(defmethod compute-ichain ((class sod-class) chain)
+ (let* ((chain-head (car chain))
+ (chain-tail (find chain-head (mapcar #'car (sod-class-chains class))
+ :key #'sod-class-chain-head))
+ (vtable-pointer (make-instance 'vtable-pointer
+ :class class
+ :chain-head chain-head
+ :chain-tail chain-tail))
+ (islots (remove-if-not #'islots-slots
+ (mapcar (lambda (super)
+ (compute-islots super class))
+ chain))))
+ (make-instance 'ichain
+ :class class
+ :chain-head chain-head
+ :chain-tail chain-tail
+ :body (cons vtable-pointer islots))))
+
+;;; ilayout
+
+(defmethod print-object ((ilayout ilayout) stream)
+ (print-unreadable-object (ilayout stream :type t)
+ (format stream "~A ~_~:<~@{~S~^ ~_~}~:>"
+ (ilayout-class ilayout)
+ (ilayout-ichains ilayout))))
+
+(defmethod compute-ilayout ((class sod-class))
+ (make-instance 'ilayout
+ :class class
+ :ichains (mapcar (lambda (chain)
+ (compute-ichain class
+ (reverse chain)))
+ (sod-class-chains class))))
+
+(defmethod slot-unbound
+ (clos-class (class sod-class) (slot-name (eql 'ilayout)))
+ (setf (slot-value class 'ilayout)
+ (compute-ilayout class)))
+
+;;;--------------------------------------------------------------------------
+;;; Vtable layout.
+
+;;; vtmsgs
+
+(defmethod print-object ((vtmsgs vtmsgs) stream)
+ (print-unreadable-object (vtmsgs stream :type t)
+ (format stream "~A <= ~A ~_~:<~@{~S~^ ~_~}~:>"
+ (vtmsgs-subclass vtmsgs)
+ (vtmsgs-class vtmsgs)
+ (vtmsgs-entries vtmsgs))))
+
+(defmethod compute-vtmsgs
+ ((class sod-class)
+ (subclass sod-class)
+ (chain-head sod-class)
+ (chain-tail sod-class))
+ (flet ((make-entry (message)
+ (let ((method (find message
+ (sod-class-effective-methods subclass)
+ :key #'effective-method-message)))
+ (make-method-entry method chain-head chain-tail))))
+ (make-instance 'vtmsgs
+ :class class
+ :subclass subclass
+ :chain-head chain-head
+ :chain-tail chain-tail
+ :entries (mapcar #'make-entry
+ (sod-class-messages class)))))
+
+;;; class-pointer
+
+(defmethod print-object ((cptr class-pointer) stream)
+ (print-unreadable-object (cptr stream :type t)
+ (format stream "~A:~A"
+ (class-pointer-metaclass cptr)
+ (sod-class-nickname (class-pointer-meta-chain-head cptr)))))
+
+(defmethod make-class-pointer
+ ((class sod-class) (chain-head sod-class)
+ (metaclass sod-class) (meta-chain-head sod-class))
+
+ ;; Slightly tricky. We don't necessarily want a pointer to the metaclass,
+ ;; but to its most specific subclass on the given chain. Fortunately, CL
+ ;; is good at this game.
+ (let* ((meta-chains (sod-class-chains metaclass))
+ (meta-chain-tails (mapcar #'car meta-chains))
+ (meta-chain-tail (find meta-chain-head meta-chain-tails
+ :key #'sod-class-chain-head)))
+ (make-instance 'class-pointer
+ :class class
+ :chain-head chain-head
+ :metaclass meta-chain-tail
+ :meta-chain-head meta-chain-head)))
+
+;;; base-offset
+
+(defmethod print-object ((boff base-offset) stream)
+ (print-unreadable-object (boff stream :type t)
+ (format stream "~A:~A"
+ (base-offset-class boff)
+ (sod-class-nickname (base-offset-chain-head boff)))))
+
+(defmethod make-base-offset ((class sod-class) (chain-head sod-class))
+ (make-instance 'base-offset
+ :class class
+ :chain-head chain-head))
+
+;;; chain-offset
+
+(defmethod print-object ((choff chain-offset) stream)
+ (print-unreadable-object (choff stream :type t)
+ (format stream "~A:~A->~A"
+ (chain-offset-class choff)
+ (sod-class-nickname (chain-offset-chain-head choff))
+ (sod-class-nickname (chain-offset-target-head choff)))))
+
+(defmethod make-chain-offset
+ ((class sod-class) (chain-head sod-class) (target-head sod-class))
+ (make-instance 'chain-offset
+ :class class
+ :chain-head chain-head
+ :target-head target-head))
+
+;;; vtable
+
+(defmethod print-object ((vtable vtable) stream)
+ (print-unreadable-object (vtable stream :type t)
+ (format stream "~A:~A ~_~:<~@{~S~^ ~_~}~:>"
+ (vtable-class vtable)
+ (sod-class-nickname (vtable-chain-head vtable))
+ (vtable-body vtable))))
+
+;; Special variables used by `compute-vtable'.
+(defvar *done-metaclass-chains*)
+(defvar *done-instance-chains*)
+
+(defmethod compute-vtable-items
+ ((class sod-class) (super sod-class) (chain-head sod-class)
+ (chain-tail sod-class) (emit function))
+
+ ;; If this class introduces new metaclass chains, then emit pointers to
+ ;; them.
+ (let* ((metasuper (sod-class-metaclass super))
+ (metasuper-chains (sod-class-chains metasuper))
+ (metasuper-chain-heads (mapcar (lambda (chain)
+ (sod-class-chain-head (car chain)))
+ metasuper-chains)))
+ (dolist (metasuper-chain-head metasuper-chain-heads)
+ (unless (member metasuper-chain-head *done-metaclass-chains*)
+ (funcall emit (make-class-pointer class
+ chain-head
+ metasuper
+ metasuper-chain-head))
+ (push metasuper-chain-head *done-metaclass-chains*))))
+
+ ;; If there are new instance chains, then emit offsets to them.
+ (let* ((chains (sod-class-chains super))
+ (chain-heads (mapcar (lambda (chain)
+ (sod-class-chain-head (car chain)))
+ chains)))
+ (dolist (head chain-heads)
+ (unless (member head *done-instance-chains*)
+ (funcall emit (make-chain-offset class chain-head head))
+ (push head *done-instance-chains*))))
+
+ ;; Finally, if there are interesting methods, emit those too.
+ (when (sod-class-messages super)
+ (funcall emit (compute-vtmsgs super class chain-head chain-tail))))
+
+(defmethod compute-vtable ((class sod-class) (chain list))
+ (let* ((chain-head (car chain))
+ (chain-tail (find chain-head (mapcar #'car (sod-class-chains class))
+ :key #'sod-class-chain-head))
+ (*done-metaclass-chains* nil)
+ (*done-instance-chains* (list chain-head))
+ (done-superclasses nil)
+ (items nil))
+ (flet ((emit (item)
+ (push item items)))
+
+ ;; Find the root chain in the metaclass and write a pointer.
+ (let* ((metaclass (sod-class-metaclass class))
+ (metaclass-root (find-root-metaclass class))
+ (metaclass-root-head (sod-class-chain-head metaclass-root)))
+ (emit (make-class-pointer class chain-head metaclass
+ metaclass-root-head))
+ (push metaclass-root-head *done-metaclass-chains*))
+
+ ;; Write an offset to the instance base.
+ (emit (make-base-offset class chain-head))
+
+ ;; Now walk the chain. As we ascend the chain, scan the class
+ ;; precedence list of each class in reverse to ensure that we have
+ ;; everything interesting.
+ (dolist (super chain)
+ (dolist (sub (reverse (sod-class-precedence-list super)))
+ (unless (member sub done-superclasses)
+ (compute-vtable-items class
+ sub
+ chain-head
+ chain-tail
+ #'emit)
+ (push sub done-superclasses))))
+
+ ;; We're through.
+ (make-instance 'vtable
+ :class class
+ :chain-head chain-head
+ :chain-tail chain-tail
+ :body (nreverse items)))))
+
+(defmethod compute-vtables ((class sod-class))
+ (mapcar (lambda (chain)
+ (compute-vtable class (reverse chain)))
+ (sod-class-chains class)))
+
+(defmethod slot-unbound
+ (clos-class (class sod-class) (slot-name (eql 'vtables)))
+ (setf (slot-value class 'vtables)
+ (compute-vtables class)))
+
+;;;----- That's all, folks --------------------------------------------------