1
0
mirror of synced 2026-01-12 00:42:56 +00:00
Interlisp.medley/clos/std-class.lisp
2021-03-08 21:12:00 -08:00

998 lines
43 KiB
Common Lisp

;;;-*- Package: CLOS; Syntax: Common-Lisp; Base: 10 -*-
;;; File converted on 10-Apr-91 22:24:19 from source std-class
;;;. Original source {dsk}<usr>local>users>welch>lisp>clos>rev4>il-format>std-class.;4 created 20-Feb-91 13:07:14
;;;. Copyright (c) 1991 by Venue
(in-package "CLOS")
(define-gf-predicate classp class)
(define-gf-predicate standard-class-p standard-class)
(define-gf-predicate forward-referenced-class-p forward-referenced-class)
(defmethod shared-initialize :after ((object documentation-mixin)
slot-names &key documentation)
(declare (ignore slot-names))
(setf (plist-value object 'documentation)
documentation))
(defmethod documentation (object &optional doc-type)
(cl:documentation object doc-type))
(defmethod (setf documentation)
(new-value object &optional doc-type)
(declare (ignore new-value doc-type))
(error "Can't change the documentation of ~S." object))
(defmethod documentation ((object documentation-mixin)
&optional doc-type)
(declare (ignore doc-type))
(car (plist-value object 'documentation)))
(defmethod (setf documentation)
(new-value (object documentation-mixin)
&optional doc-type)
(declare (ignore doc-type))
(setf (plist-value object 'documentation)
new-value))
(defmethod documentation ((slotd standard-slot-definition)
&optional doc-type)
(declare (ignore doc-type))
(slot-value slotd 'documentation))
(defmethod (setf documentation)
(new-value (slotd standard-slot-definition)
&optional doc-type)
(declare (ignore doc-type))
(setf (slot-value slotd 'documentation)
new-value))
(defmethod documentation ((method standard-method) &optional doc-type)
(declare (ignore doc-type))
(plist-value method 'documentation))
(defmethod (setf documentation)
(new-value (method standard-method)
&optional doc-type)
(declare (ignore doc-type))
(setf (plist-value method 'documentation) new-value))
;;; Various class accessors that are a little more complicated than can be done with automatically
;;; generated reader methods.
(defmethod class-wrapper ((class clos-class))
(with-slots (wrapper)
class
(let ((w? wrapper))
(if (consp w?)
(let ((new (make-wrapper class)))
(setf (wrapper-instance-slots-layout new)
(car w?)
(wrapper-class-slots new)
(cdr w?))
(setq wrapper new))
w?))))
(defmethod class-precedence-list ((class clos-class))
(unless (class-finalized-p class)
(finalize-inheritance class))
(with-slots (class-precedence-list)
class
class-precedence-list))
(defmethod class-finalized-p ((class clos-class))
(with-slots (wrapper)
class
(not (null wrapper))))
(defmethod class-prototype ((class std-class))
(with-slots (prototype)
class
(or prototype (setq prototype (allocate-instance class)))))
(defmethod class-direct-default-initargs ((class std-class))
(plist-value class 'direct-default-initargs))
(defmethod class-default-initargs ((class std-class))
(plist-value class 'default-initargs))
(defmethod class-constructors ((class std-class))
(plist-value class 'constructors))
(defmethod class-slot-cells ((class std-class))
(plist-value class 'class-slot-cells))
;;; Class accessors that are even a little bit more complicated than those above. These have a
;;; protocol for updating them, we must implement that protocol. Maintaining the direct subclasses
;;; backpointers. The update methods are here, the values are read by an automatically generated
;;; reader method.
(defmethod add-direct-subclass ((class class)
(subclass class))
(with-slots (direct-subclasses)
class
(pushnew subclass direct-subclasses)
subclass))
(defmethod remove-direct-subclass ((class class)
(subclass class))
(with-slots (direct-subclasses)
class
(setq direct-subclasses (remove subclass direct-subclasses))
subclass))
;;; Maintaining the direct-methods and direct-generic-functions backpointers. There are four generic
;;; functions involved, each has one method for the class case and another method for the damned EQL
;;; specializers. All of these are specified methods and appear in their specified place in the
;;; class graph. ADD-METHOD-ON-SPECIALIZER REMOVE-METHOD-ON-SPECIALIZER SPECIALIZER-METHODS
;;; SPECIALIZER-GENERIC-FUNCTIONS In each case, we maintain one value which is a cons. The car is
;;; the list methods. The cdr is a list of the generic functions. The cdr is always computed
;;; lazily.
(defmethod add-method-on-specializer ((method method)
(specializer class))
(with-slots (direct-methods)
specializer
(setf (car direct-methods)
(adjoin method (car direct-methods))
(cdr direct-methods)
nil))
method)
(defmethod remove-method-on-specializer ((method method)
(specializer class))
(with-slots (direct-methods)
specializer
(setf (car direct-methods)
(remove method (car direct-methods))
(cdr direct-methods)
nil))
method)
(defmethod specializer-methods ((specializer class))
(with-slots (direct-methods)
specializer
(car direct-methods)))
(defmethod specializer-generic-functions ((specializer class))
(with-slots (direct-methods)
specializer
(or (cdr direct-methods)
(setf (cdr direct-methods)
(gathering1 (collecting-once)
(dolist (m (car direct-methods))
(gather1 (method-generic-function m))))))))
;;; This hash table is used to store the direct methods and direct generic functions of EQL
;;; specializers. Each value in the table is the cons.
(defvar *eql-specializer-methods* (make-hash-table :test #'eql))
(defmethod add-method-on-specializer ((method method)
(specializer eql-specializer))
(let* ((object (eql-specializer-object specializer))
(entry (gethash object *eql-specializer-methods*)))
(unless entry
(setq entry (setf (gethash object *eql-specializer-methods*)
(cons nil nil))))
(setf (car entry)
(adjoin method (car entry))
(cdr entry)
nil)
method))
(defmethod remove-method-on-specializer ((method method)
(specializer eql-specializer))
(let* ((object (eql-specializer-object specializer))
(entry (gethash object *eql-specializer-methods*)))
(when entry
(setf (car entry)
(remove method (car entry))
(cdr entry)
nil))
method))
(defmethod specializer-methods ((specializer eql-specializer))
(car (gethash (eql-specializer-object specializer)
*eql-specializer-methods*)))
(defmethod specializer-generic-functions ((specializer eql-specializer))
(let* ((object (eql-specializer-object specializer))
(entry (gethash object *eql-specializer-methods*)))
(when entry
(or (cdr entry)
(setf (cdr entry)
(gathering1 (collecting-once)
(dolist (m (car entry))
(gather1 (method-generic-function m)))))))))
(defun real-load-defclass (name metaclass-name supers slots other accessors)
(do-standard-defsetfs-for-defclass accessors)
; ***
(apply #'ensure-class name :metaclass metaclass-name :direct-superclasses supers :direct-slots
slots :definition-source `((defclass ,name ()
())
,(load-truename))
other))
(defun ensure-class (name &rest all)
(apply #'ensure-class-using-class name (find-class name nil)
all))
(defmethod ensure-class-using-class (name (class null)
&rest args &key)
(multiple-value-bind (meta initargs)
(ensure-class-values class args)
(setf class (apply #'make-instance meta :name name initargs)
(find-class name)
class)
(inform-type-system-about-class class name)
; ***
class))
(defmethod ensure-class-using-class (name (class clos-class)
&rest args &key)
(multiple-value-bind (meta initargs)
(ensure-class-values class args)
(unless (eq (class-of class)
meta)
(change-class class meta))
(apply #'reinitialize-instance class initargs)
(inform-type-system-about-class class name)
; ***
class))
(defun ensure-class-values (class args)
(let* ((initargs (copy-list args))
(unsupplied (list 1))
(supplied-meta (getf initargs :metaclass unsupplied))
(supplied-supers (getf initargs :direct-superclasses unsupplied))
(supplied-slots (getf initargs :direct-slots unsupplied))
(meta (cond ((neq supplied-meta unsupplied)
(find-class supplied-meta))
((or (null class)
(forward-referenced-class-p class))
*the-class-standard-class*)
(t (class-of class))))
(proto (class-prototype meta)))
(flet ((fix-super (s)
(cond ((classp s)
s)
((not (legal-class-name-p s))
(error "~S is not a class or a legal class name." s))
(t (or (find-class s nil)
(setf (find-class s)
(make-instance 'forward-referenced-class :name s)))))))
(loop (unless (remf initargs :metaclass)
(return)))
(loop (unless (remf initargs :direct-superclasses)
(return)))
(loop (unless (remf initargs :direct-slots)
(return)))
(values meta (list* :direct-superclasses (and (neq supplied-supers unsupplied)
(mapcar #'fix-super supplied-supers)
)
:direct-slots
(and (neq supplied-slots unsupplied)
supplied-slots)
initargs)))))
;;;
(defmethod shared-initialize :before ((class std-class)
slot-names &key direct-superclasses)
(declare (ignore slot-names))
;; *** error checking
)
(defmethod shared-initialize :after ((class std-class)
slot-names
&key (direct-superclasses
nil direct-superclasses-p)
(direct-slots nil direct-slots-p)
(direct-default-initargs
nil direct-default-initargs-p))
(declare (ignore slot-names))
(setq direct-superclasses (if direct-superclasses-p
(setf (slot-value class 'direct-superclasses)
(or direct-superclasses
(list *the-class-standard-object*)
))
(slot-value class 'direct-superclasses)))
(setq direct-slots (if direct-slots-p
(setf (slot-value class 'direct-slots)
(mapcar #'(lambda (pl)
(make-direct-slotd class pl))
direct-slots))
(slot-value class 'direct-slots)))
(if direct-default-initargs-p
(setf (plist-value class 'direct-default-initargs)
direct-default-initargs)
(setq direct-default-initargs
(plist-value class 'direct-default-initargs)))
(setf (plist-value class 'class-slot-cells)
(gathering1 (collecting)
(dolist (dslotd direct-slots)
(when (eq (slotd-allocation dslotd)
class)
(let ((initfunction (slotd-initfunction dslotd)))
(gather1 (cons (slotd-name dslotd)
(if initfunction
(funcall initfunction)
*slot-unbound*))))))))
(add-direct-subclasses class direct-superclasses)
(add-slot-accessors class direct-slots))
(defmethod reinitialize-instance :before ((class std-class)
&key direct-superclasses direct-slots
direct-default-initargs)
(declare (ignore direct-default-initargs))
(remove-direct-subclasses class (class-direct-superclasses class))
(remove-slot-accessors class (class-direct-slots class)))
(defmethod reinitialize-instance :after ((class std-class)
&rest initargs &key)
(update-class class nil)
(map-dependents class #'(lambda (dependent)
(apply #'update-dependent class dependent initargs))))
(defun add-slot-accessors (class dslotds)
(fix-slot-accessors class dslotds 'add))
(defun remove-slot-accessors (class dslotds)
(fix-slot-accessors class dslotds 'remove))
(defun fix-slot-accessors (class dslotds add/remove)
(flet ((fix (gfspec name r/w)
(let ((gf (ensure-generic-function gfspec)))
(case r/w
(r (if (eq add/remove 'add)
(add-reader-method class gf name)
(remove-reader-method class gf)))
(w (if (eq add/remove 'add)
(add-writer-method class gf name)
(remove-writer-method class gf)))))))
(dolist (dslotd dslotds)
(let ((slot-name (slotd-name dslotd)))
(dolist (r (slotd-readers dslotd))
(fix r slot-name 'r))
(dolist (w (slotd-writers dslotd))
(fix w slot-name 'w))))))
(defun add-direct-subclasses (class new)
(dolist (n new)
(unless (memq class (class-direct-subclasses class))
(add-direct-subclass n class))))
(defun remove-direct-subclasses (class new)
(let ((old (class-direct-superclasses class)))
(dolist (o (set-difference old new))
(remove-direct-subclass o class))))
;;;
(defmethod finalize-inheritance ((class std-class))
(update-class class t))
;;; Called by :after reinitialize instance whenever a class is reinitialized. The class may or may
;;; not be finalized.
(defun update-class (class finalizep)
(when (or finalizep (class-finalized-p class))
(let* ((dsupers (class-direct-superclasses class))
(dslotds (class-direct-slots class))
(dinits (class-direct-default-initargs class))
(cpl (compute-class-precedence-list class dsupers))
(eslotds (compute-slots class cpl dslotds))
(inits (compute-default-initargs class cpl dinits)))
(update-cpl class cpl)
(update-slots class cpl eslotds)
(update-dinits class dinits)
(update-inits class inits)
(update-constructors class)))
(unless finalizep
(dolist (sub (class-direct-subclasses class))
(update-class sub nil))))
(defun update-cpl (class cpl)
(when (class-finalized-p class)
(unless (equal (class-precedence-list class)
cpl)
(force-cache-flushes class)))
(setf (slot-value class 'class-precedence-list)
cpl))
(defun update-slots (class cpl eslotds)
(multiple-value-bind (nlayout nwrapper-class-slots)
(compute-storage-info cpl eslotds)
;; If there is a change in the shape of the instances then the old class is now obsolete.
(let* ((owrapper (class-wrapper class))
(olayout (and owrapper (wrapper-instance-slots-layout owrapper)))
(owrapper-class-slots (and owrapper (wrapper-class-slots owrapper)))
(nwrapper (cond ((null owrapper)
(make-wrapper class))
((and (equal nlayout olayout)
(not (iterate ((o (list-elements owrapper-class-slots))
(n (list-elements nwrapper-class-slots)))
(unless (eq (car o)
(car n))
(return t)))))
owrapper)
(t
;; This will initialize the new wrapper to have the same state as
;; the old wrapper. We will then have to change that. This may
;; seem like wasted work (it is), but the spec requires that we
;; call make-instances-obsolete.
(make-instances-obsolete class)
(class-wrapper class)))))
(with-slots (wrapper no-of-instance-slots slots)
class
(setf no-of-instance-slots (length nlayout)
slots eslotds (wrapper-instance-slots-layout nwrapper)
nlayout
(wrapper-class-slots nwrapper)
nwrapper-class-slots wrapper nwrapper))
(dolist (eslotd eslotds)
(setf (slotd-class eslotd)
class)
(setf (slotd-instance-index eslotd)
(instance-slot-index nwrapper (slotd-name eslotd)))))))
(defun compute-storage-info (cpl eslotds)
(let ((instance nil)
(class nil))
(dolist (eslotd eslotds)
(let ((alloc (slotd-allocation eslotd)))
(cond ((eq alloc :instance)
(push eslotd instance))
((classp alloc)
(push eslotd class)))))
(values (compute-layout cpl instance)
(compute-class-slots class))))
(defun compute-layout (cpl instance-eslotds)
(let* ((names (gathering1 (collecting)
(dolist (eslotd instance-eslotds)
(when (eq (slotd-allocation eslotd)
:instance)
(gather1 (slotd-name eslotd))))))
(order nil))
(labels ((rwalk (tail)
(when tail
(rwalk (cdr tail))
(dolist (ss (class-slots (car tail)))
(let ((n (slotd-name ss)))
(when (memq n names)
(setq order (cons n order)
names
(remove n names))))))))
(rwalk cpl)
(reverse (append names order)))))
(defun compute-class-slots (eslotds)
(gathering1 (collecting)
(dolist (eslotd eslotds)
(gather1 (assoc (slotd-name eslotd)
(class-slot-cells (slotd-allocation eslotd)))))))
(defun update-dinits (class dinits)
(setf (plist-value class 'direct-default-initargs)
(remove-invalid dinits (class-slots class))))
(defun update-inits (class inits)
(setf (plist-value class 'default-initargs)
(remove-invalid inits (class-slots class))))
;; bug: :default-initargs aren't updated with slots are removed, so
;; update-inits removes initargs that don't have corresponding slots.
(defun remove-invalid (inits slotds &aux (return nil))
(dolist (element inits)
(dolist (slotd slotds)
(if (member (car element) (slot-value slotd 'initargs))
(pushnew element return))))
return)
(defmethod compute-default-initargs ((class std-class)
cpl direct)
(labels ((walk (tail)
(if (null tail)
nil
(let ((c (pop tail)))
(append (if (eq c class)
direct
(class-direct-default-initargs c))
(walk tail))))))
(let ((initargs (walk cpl)))
(delete-duplicates initargs
:test #'eq :key #'car :from-end t))))
;;; Protocols for constructing direct and effective slot definitions.
(defmethod direct-slot-definition-class ((class std-class)
initargs)
(declare (ignore initargs))
(find-class 'standard-direct-slot-definition))
(defun make-direct-slotd (class initargs)
(let ((initargs (list* :class class initargs)))
(apply #'make-instance (direct-slot-definition-class class initargs)
initargs)))
;;;
(defmethod compute-slots ((class std-class)
cpl class-direct-slots)
;; As specified, we must call COMPUTE-EFFECTIVE-SLOT-DEFINITION once for each different slot
;; name we find in our superclasses. Each call receives the class and a list of the dslotds
;; with that name. The list is in most-specific-first order.
(let ((name-dslotds-alist nil))
(labels ((collect-one-class (dslotds)
(dolist (d dslotds)
(let* ((name (slotd-name d))
(entry (assq name name-dslotds-alist)))
(if entry
(push d (cdr entry))
(push (list name d)
name-dslotds-alist))))))
(collect-one-class class-direct-slots)
(dolist (c (cdr cpl))
(collect-one-class (class-direct-slots c)))
(mapcar #'(lambda (direct)
(compute-effective-slot-definition class (nreverse (cdr direct)))
)
name-dslotds-alist))))
(defmethod compute-effective-slot-definition ((class std-class)
dslotds)
(let* ((initargs (compute-effective-slot-definition-initargs class dslotds))
(class (effective-slot-definition-class class initargs)))
(apply #'make-instance class initargs)))
(defmethod effective-slot-definition-class ((class std-class)
initargs)
(declare (ignore initargs))
(find-class 'standard-effective-slot-definition))
(defmethod compute-effective-slot-definition-initargs ((class std-class)
direct-slotds)
(let* ((name nil)
(initfunction nil)
(initform nil)
(initargs nil)
(allocation nil)
(type t)
(namep nil)
(initp nil)
(allocp nil))
(dolist (slotd direct-slotds)
(when slotd
(unless namep
(setq name (slotd-name slotd)
namep t))
(unless initp
(when (slotd-initfunction slotd)
(setq initform (slotd-initform slotd)
initfunction
(slotd-initfunction slotd)
initp t)))
(unless allocp
(setq allocation (slotd-allocation slotd)
allocp t))
(setq initargs (append (slotd-initargs slotd)
initargs))
(let ((slotd-type (slotd-type slotd)))
(setq type (cond ((null type)
slotd-type)
((subtypep type slotd-type)
type)
(t `(and ,type ,slotd-type)))))))
(list :name name :initform initform :initfunction initfunction :initargs initargs
:allocation allocation :type type)))
;;; NOTE: For bootstrapping considerations, these can't use make-instance to make the method object.
;;; They have to use make-a-method which is a specially bootstrapped mechanism for making standard
;;; methods.
(defmethod add-reader-method ((class std-class)
generic-function slot-name)
(let* ((name (class-name class))
(method (make-a-method 'standard-reader-method nil (list (or name 'standard-object))
(list class)
(make-reader-method-function class slot-name)
"automatically generated reader method" slot-name)))
(add-method generic-function method)))
(defmethod add-writer-method ((class std-class)
generic-function slot-name)
(let* ((name (class-name class))
(method (make-a-method 'standard-writer-method nil (list 'new-value (or name
'
standard-object
))
(list *the-class-t* class)
(make-writer-method-function class slot-name)
"automatically generated writer method" slot-name)))
(add-method generic-function method)))
(defmethod remove-reader-method ((class std-class)
generic-function)
(let ((method (get-method generic-function nil (list class)
nil)))
(when method (remove-method generic-function method))))
(defmethod remove-writer-method ((class std-class)
generic-function)
(let ((method (get-method generic-function nil (list *the-class-t* class)
nil)))
(when method (remove-method generic-function method))))
;;; make-reader-method-function and make-write-method function are NOT part of the standard
;;; protocol. They are however useful, CLOS makes uses makes use of them internally and documents
;;; them for CLOS users. *** This needs work to make type testing by the writer functions which ***
;;; do type testing faster. The idea would be to have one constructor *** for each possible type
;;; test. In order to do this it would be nice *** to have help from inform-type-system-about-class
;;; and friends. *** There is a subtle bug here which is going to have to be fixed. *** Namely, the
;;; simplistic use of the template has to be fixed. We *** have to give the optimize-slot-value
;;; method the user might have *** defined for this metclass a chance to run.
(defmethod make-reader-method-function ((class standard-class)
slot-name)
(make-std-reader-method-function slot-name))
(defmethod make-writer-method-function ((class standard-class)
slot-name)
(make-std-writer-method-function slot-name))
(defun make-std-reader-method-function (slot-name)
#'(lambda (instance)
(slot-value instance slot-name)))
(defun make-std-writer-method-function (slot-name)
#'(lambda (nv instance)
(setf (slot-value instance slot-name)
nv)))
; inform-type-system-about-class
; make-type-predicate
;;; These are NOT part of the standard protocol. They are internal mechanism which CLOS uses to
;;; *try* and tell the type system about class definitions. In a more fully integrated
;;; implementation of CLOS, the type system would know about class objects and class names in a more
;;; fundamental way and the mechanism used to inform the type system about new classes would be
;;; different.
(defmethod inform-type-system-about-class ((class std-class)
name)
(let ((predicate-name (make-type-predicate-name name)))
(setf (symbol-function predicate-name)
(make-type-predicate name))
(do-satisfies-deftype name predicate-name)
(setf (gethash name lisp::*typep-hash-table*)
predicate-name))) ;makes typep significantly faster...
(defun make-type-predicate (name)
#'(lambda (x)
(not (null (memq (find-class name)
(cond ((std-instance-p x)
(class-precedence-list (std-instance-class x)))
((fsc-instance-p x)
(class-precedence-list (fsc-instance-class x)))))))))
;;; These 4 definitions appear here for bootstrapping reasons. Logically, they should be in the
;;; construct file. For documentation purposes, a copy of these definitions appears in the
;;; construct file. If you change one of the definitions here, be sure to change the copy there.
(defvar *initialization-generic-functions* (list #'make-instance #'default-initargs
#'allocate-instance #'initialize-instance
#'shared-initialize))
(defmethod maybe-update-constructors ((generic-function generic-function)
(method method))
(when (memq generic-function *initialization-generic-functions*)
(labels ((recurse (class)
(update-constructors class)
(dolist (subclass (class-direct-subclasses class))
(recurse subclass))))
(when (classp (car (method-specializers method)))
(recurse (car (method-specializers method)))))))
(defmethod update-constructors ((class std-class))
(dolist (cons (class-constructors class))
(install-lazy-constructor-installer cons)))
(defmethod update-constructors ((class class))
nil)
(defmethod compatible-meta-class-change-p (class proto-new-class)
(eq (class-of class)
(class-of proto-new-class)))
(defmethod check-super-metaclass-compatibility ((class t)
(new-super t))
(unless (eq (class-of class)
(class-of new-super))
(error "The class ~S was specified as a~%super-class of the class ~S;~%~
but the meta-classes ~S and~%~S are incompatible." new-super class (class-of new-super)
(class-of class))))
;;;
(defun force-cache-flushes (class)
(let* ((owrapper (class-wrapper class))
(state (wrapper-state owrapper)))
;; We only need to do something if the state is still T. If the state isn't T, it will
;; be FLUSH or OBSOLETE, and both of those will already be doing what we want. In
;; particular, we must be sure we never change an OBSOLETE into a FLUSH since OBSOLETE
;; means do what FLUSH does and then some.
(when (eq state 't)
(let ((nwrapper (make-wrapper class)))
(setf (wrapper-instance-slots-layout nwrapper)
(wrapper-instance-slots-layout owrapper))
(setf (wrapper-class-slots nwrapper)
(wrapper-class-slots owrapper))
(without-interrupts (setf (slot-value class 'wrapper)
nwrapper)
(invalidate-wrapper owrapper 'flush nwrapper))
(update-constructors class)))))
; ??? ***
(defun flush-cache-trap (owrapper nwrapper instance)
(declare (ignore owrapper))
(set-wrapper instance nwrapper))
;;; make-instances-obsolete can be called by user code. It will cause the next access to the
;;; instance (as defined in 88-002R) to trap through the update-instance-for-redefined-class
;;; mechanism.
(defmethod make-instances-obsolete ((class std-class))
(let ((owrapper (class-wrapper class))
(nwrapper (make-wrapper class)))
(setf (wrapper-instance-slots-layout nwrapper)
(wrapper-instance-slots-layout owrapper))
(setf (wrapper-class-slots nwrapper)
(wrapper-class-slots owrapper))
(without-interrupts (setf (slot-value class 'wrapper)
nwrapper)
(invalidate-wrapper owrapper 'obsolete nwrapper)
class)))
(defmethod make-instances-obsolete ((class symbol))
(make-instances-obsolete (find-class class)))
;;; obsolete-instance-trap is the internal trap that is called when we see an obsolete instance.
;;; The times when it is called are: - when the instance is involved in method lookup - when
;;; attempting to access a slot of an instance It is not called by class-of, wrapper-of, or any of
;;; the low-level instance access macros. Of course these times when it is called are an internal
;;; implementation detail of CLOS and are not part of the documented description of when the obsolete
;;; instance update happens. The documented description is as it appears in 88-002R. This has to
;;; return the new wrapper, so it counts on all the methods on obsolete-instance-trap-internal to
;;; return the new wrapper. It also does a little internal error checking to make sure that the
;;; traps are only happening when they should, and that the trap methods are computing apropriate
;;; new wrappers.
(defun obsolete-instance-trap (owrapper nwrapper instance)
;; local --> local transfer local --> shared discard local --> --
;; discard shared --> local transfer shared --> shared discard shared --> --
;; discard -- --> local add -- --> shared --
(let* ((class (wrapper-class nwrapper))
(guts (allocate-instance class))
; ??? allocate-instance ???
(olayout (wrapper-instance-slots-layout owrapper))
(nlayout (wrapper-instance-slots-layout nwrapper))
(oslots (get-slots instance))
(nslots (get-slots guts))
(oclass-slots (wrapper-class-slots owrapper))
(added nil)
(discarded nil)
(plist nil))
;; Go through all the old local slots.
(iterate ((name (list-elements olayout))
(opos (interval :from 0)))
(let ((npos (posq name nlayout)))
(if npos
(setf (svref nslots npos)
(svref oslots opos))
(progn (push name discarded)
(unless (eq (svref oslots opos)
*slot-unbound*)
(setf (getf plist name)
(svref oslots opos)))))))
;; Go through all the old shared slots.
(iterate ((oclass-slot-and-val (list-elements oclass-slots)))
(let ((name (car oclass-slot-and-val))
(val (cdr oclass-slot-and-val)))
(let ((npos (posq name nlayout)))
(if npos
(setf (svref nslots npos)
(cdr oclass-slot-and-val))
(progn (push name discarded)
(unless (eq val *slot-unbound*)
(setf (getf plist name)
val)))))))
;; Go through all the new local slots to compute the added slots.
(dolist (nlocal nlayout)
(unless (or (memq nlocal olayout)
(assq nlocal oclass-slots))
(push nlocal added)))
(without-interrupts (set-wrapper instance nwrapper)
(set-slots instance nslots))
(update-instance-for-redefined-class instance added discarded plist)
nwrapper))
;;;
(defmacro change-class-internal (wrapper-fetcher slots-fetcher alloc)
`(let* ((old-class (class-of instance))
(copy (,alloc old-class))
(guts (,alloc new-class))
(new-wrapper (,wrapper-fetcher guts))
(old-wrapper (class-wrapper old-class))
(old-layout (wrapper-instance-slots-layout old-wrapper))
(new-layout (wrapper-instance-slots-layout new-wrapper))
(old-slots (,slots-fetcher instance))
(new-slots (,slots-fetcher guts))
(old-class-slots (wrapper-class-slots old-wrapper)))
;; "The values of local slots specified by both the class Cto and Cfrom are retained.
;; If such a local slot was unbound, it remains unbound."
(iterate ((new-slot (list-elements new-layout))
(new-position (interval :from 0)))
(let ((old-position (position new-slot old-layout :test #'eq)))
(when old-position
(setf (svref new-slots new-position)
(svref old-slots old-position)))))
;; "The values of slots specified as shared in the class Cfrom and as local in the
;; class Cto are retained."
(iterate ((slot-and-val (list-elements old-class-slots)))
(let ((position (position (car slot-and-val)
new-layout :test #'eq)))
(when position
(setf (svref new-slots position)
(cdr slot-and-val)))))
;; Make the copy point to the old instance's storage, and make the old instance point
;; to the new storage.
(without-interrupts (setf (,slots-fetcher copy)
old-slots)
(setf (,wrapper-fetcher instance)
new-wrapper)
(setf (,slots-fetcher instance)
new-slots))
(update-instance-for-different-class copy instance)
instance))
(defmethod change-class ((instance standard-object)
(new-class standard-class))
(unless (std-instance-p instance)
(error "Can't change the class of ~S to ~S~@
because it isn't already an instance with metaclass~%~S." instance new-class
'standard-class))
(change-class-internal std-instance-wrapper std-instance-slots allocate-instance))
(defmethod change-class ((instance standard-object)
(new-class funcallable-standard-class))
(unless (fsc-instance-p instance)
(error "Can't change the class of ~S to ~S~@
because it isn't already an instance with metaclass~%~S." instance new-class
'funcallable-standard-class))
(change-class-internal fsc-instance-wrapper fsc-instance-slots allocate-instance))
(defmethod change-class ((instance t)
(new-class-name symbol))
(change-class instance (find-class new-class-name)))
;;; The metaclass BUILT-IN-CLASS This metaclass is something of a weird creature. By this point,
;;; all instances of it which will exist have been created, and no instance is ever created by
;;; calling MAKE-INSTANCE. But, there are other parts of the protcol we must follow and those
;;; definitions appear here.
(defmethod shared-initialize :before ((class built-in-class)
slot-names &rest initargs)
(declare (ignore slot-names))
(error "Attempt to initialize or reinitialize a built in class."))
(defmethod class-direct-slots ((class built-in-class))
nil)
(defmethod class-slots ((class built-in-class))
nil)
(defmethod class-direct-default-initargs ((class built-in-class))
nil)
(defmethod class-default-initargs ((class built-in-class))
nil)
(defmethod check-super-metaclass-compatibility ((c class)
(s built-in-class))
(or (eq s *the-class-t*)
(error "~S cannot have ~S as a super.~%~
The class ~S is the only built in class that can be a~%~
superclass of a standard class." c s *the-class-t*)))
;;;
(defmethod check-super-metaclass-compatibility ((c std-class)
(f forward-referenced-class))
't)
;;;
(defmethod add-dependent ((metaobject dependent-update-mixin)
dependent)
(pushnew dependent (plist-value metaobject 'dependents)))
(defmethod remove-dependent ((metaobject dependent-update-mixin)
dependent)
(setf (plist-value metaobject 'dependents)
(delete dependent (plist-value metaobject 'dependents))))
(defmethod map-dependents ((metaobject dependent-update-mixin)
function)
(dolist (dependent (plist-value metaobject 'dependents))
(funcall function dependent)))