The compiler can assist in optimizing calls to
particular, and despite the highly-dynamic nature of CLOS, compile-time
knowledge of the name of the slot being accessed permits precomputation
of much of the access (along with a branch to the slow path in case the
parameters of the access change between compile-time and run-time).
If the object being accessed is a required parameter to the method, where the parameter variable is unmodified in the method body, and the slot name is a compile-time constant, then fast slot access can be supported through permutation vectors.
(FIXME: what about the metaclasses of the object? Does it have to be standard-class, or can it be funcallable-standard-class? Surely structure-class objects could be completely optimized if the class definition and slot name are both known at compile-time.)
Permutation vectors are built up and maintained to associate a compile-time index associated with a slot name with an index into the slot vector for a class of objects. The permutation vector applicable to a given method call (FIXME: or effective method? set of classes? something else?) is passed to the method body, and slots are accessed by looking up the index to the slot vector in the permutation vector, then looking up the value from the slot vector. (FIXME: a diagram would help, if I understood this bit well enough to draw a diagram).
Subsequent redefinitions of classes or of methods on
slot-value-using-class cause an invalid index to be written into
the permutation vector, and the call falls back to a full call to
If the conditions for (structure or) permutation vector slot access
optimization are not met, optimization of
methods falls back to the same as for calls to
of methods, below.
A call to
slot-value with a compile-time constant slot
name argument is compiled into a call to a generic function
(sb-pcl::slot-accessor :global name
together with code providing load-time assurance (via
load-time-value) that the generic function is bound and has a
suitable accessor method. This generic function then benefits from the
same optimizations as ordinary accessors, described in
Accessor Discriminating Functions.
(FIXME: how does this get invalidated if we later add methods on
slot-value-using-class? Hm, maybe it isn't. I think this is
probably a bug, and that adding methods to
needs to invalidate accessor caches. Bah, humbug. Test code in
ex:buggycache, and note that I think that the analogous case
involving adding or removing methods from
compute-applicable-methods is handled correctly by
(defclass foo () ((a :initform 0))) (defun foo (x) (slot-value x 'a)) (foo (make-instance 'foo)) ; => 0 (defmethod slot-value-using-class :after ((class std-class) (object foo) (slotd standard-effective-slot-definition)) (print "hi")) (foo (make-instance 'foo)) ; => 0, no print (defclass bar (foo) ((a :initform 1))) (foo (make-instance 'bar)) ; => 1 and prints "hi" (foo (make-instance 'foo)) ; => 0, no print