1 ;;;; This file contains the optimization machinery for make-instance.
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; This software is derived from software originally released by
7 ;;;; Gerd Moellmann. Copyright and release statements follow. Later
8 ;;;; modifications to the software are in the public domain and are
9 ;;;; provided with absolutely no warranty. See the COPYING and
10 ;;;; CREDITS files for more information.
12 ;;; Copyright (C) 2002 Gerd Moellmann <gerd.moellmann@t-online.de>
13 ;;; All rights reserved.
15 ;;; Redistribution and use in source and binary forms, with or without
16 ;;; modification, are permitted provided that the following conditions
19 ;;; 1. Redistributions of source code must retain the above copyright
20 ;;; notice, this list of conditions and the following disclaimer.
21 ;;; 2. Redistributions in binary form must reproduce the above copyright
22 ;;; notice, this list of conditions and the following disclaimer in the
23 ;;; documentation and/or other materials provided with the distribution.
24 ;;; 3. The name of the author may not be used to endorse or promote
25 ;;; products derived from this software without specific prior written
28 ;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
29 ;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
30 ;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 ;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
32 ;;; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 ;;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
34 ;;; OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
35 ;;; BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36 ;;; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 ;;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
38 ;;; USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
45 ;;; Compiler macro for MAKE-INSTANCE, and load-time generation of
46 ;;; optimized instance constructor functions.
48 ;;; ********************
49 ;;; Entry Points ******
50 ;;; ********************
52 ;;; UPDATE-CTORS must be called when methods are added/removed,
53 ;;; classes are changed, etc., which affect instance creation.
55 ;;; PRECOMPILE-CTORS can be called to precompile constructor functions
56 ;;; for classes whose definitions are known at the time the function
61 ;;; ******************
63 ;;; ******************
65 (defun quote-plist-keys (plist)
66 (loop for
(key . more
) on plist by
#'cddr
68 (error "Not a property list: ~S" plist
)
71 and collect
(car more
)))
73 (defun plist-keys (plist &key test
)
74 (loop for
(key . more
) on plist by
#'cddr
76 (error "Not a property list: ~S" plist
)
77 else if
(or (null test
) (funcall test key
))
80 (defun plist-values (plist &key test
)
81 (loop for
(key . more
) on plist by
#'cddr
83 (error "Not a property list: ~S" plist
)
84 else if
(or (null test
) (funcall test
(car more
)))
87 (defun constant-class-arg-p (form)
89 (let ((constant (constant-form-value form
)))
90 (or (and (symbolp constant
)
91 (not (null (symbol-package constant
))))
94 (defun constant-symbol-p (form)
96 (let ((constant (constant-form-value form
)))
97 (and (symbolp constant
)
98 (not (null (symbol-package constant
)))))))
100 ;;; Somewhat akin to DEFAULT-INITARGS, but just collecting the defaulted
101 ;;; initargs for the call.
102 (defun ctor-default-initkeys (supplied-initargs class-default-initargs
)
103 (loop for
(key) in class-default-initargs
104 when
(eq (getf supplied-initargs key
'.not-there.
) '.not-there.
)
107 ;;; Like DEFAULT-INITARGS, but return a list that can be spliced into source,
108 ;;; instead of a list with values already evaluated.
109 (defun ctor-default-initargs (supplied-initargs class-default-initargs
)
110 (loop for
(key form fun
) in class-default-initargs
111 when
(eq (getf supplied-initargs key
'.not-there.
) '.not-there.
)
112 append
(list key
(if (constantp form
) form
`(funcall ,fun
)))
113 into default-initargs
115 (return (append supplied-initargs default-initargs
))))
117 ;;; *****************
119 ;;; *****************
121 ;;; Ctors are funcallable instances whose initial function is a
122 ;;; function computing an optimized constructor function when called.
123 ;;; When the optimized function is computed, the function of the
124 ;;; funcallable instance is set to it.
127 ;;; Type is either CTOR, for MAKE-INSTANCE, or ALLOCATOR, for ALLOCATE-INSTANCE
128 (!defstruct-with-alternate-metaclass ctor
129 :slot-names
(type class-or-name class initargs state safe-p
)
130 :boa-constructor %make-ctor
131 :superclass-name function
132 :metaclass-name static-classoid
133 :metaclass-constructor make-static-classoid
134 :dd-type funcallable-structure
135 :runtime-type-checks-p nil
)
137 ;;; All defined ctors.
138 (defglobal *all-ctors
* (make-hash-table :test
#'equal
140 (declaim (hash-table *all-ctors
*))
142 (defun make-ctor-parameter-list (ctor)
143 (plist-values (ctor-initargs ctor
) :test
(complement #'constantp
)))
145 ;;; Reset CTOR to use a default function that will compute an
146 ;;; optimized constructor function when called.
147 (defun install-initial-constructor (ctor &key force-p
)
148 (when (or force-p
(ctor-class ctor
))
149 (setf (ctor-class ctor
) nil
150 (ctor-state ctor
) 'initial
)
151 (setf (funcallable-instance-fun ctor
)
152 (ecase (ctor-type ctor
)
155 (install-optimized-constructor ctor
)
159 (install-optimized-allocator ctor
)
162 (defun make-ctor-function-name (class-name initargs safe-code-p
)
163 (labels ((arg-name (x)
165 ;; this list of types might look arbitrary but it is
166 ;; exactly the set of types descended into by EQUAL,
167 ;; which is the predicate used by globaldb to test for
170 (list (gensym "LIST-INITARG-"))
171 (string (gensym "STRING-INITARG-"))
172 (bit-vector (gensym "BIT-VECTOR-INITARG-"))
173 (pathname (gensym "PATHNAME-INITARG-"))
176 (let ((*gensym-counter
* 0))
177 (mapcar #'arg-name list
))))
178 (list* 'ctor class-name safe-code-p
(munge initargs
))))
180 (declaim (ftype (sfunction * function
)
181 ensure-ctor ensure-allocator
))
183 ;;; Keep this a separate function for testing.
184 (defun ensure-ctor (function-name class-name initargs safe-code-p
)
186 (or (gethash function-name
*all-ctors
*)
187 (make-ctor function-name class-name initargs safe-code-p
))))
189 ;;; Keep this a separate function for testing.
190 (defun make-ctor (function-name class-name initargs safe-p
)
191 (let ((ctor (%make-ctor
'ctor class-name nil initargs nil safe-p
)))
192 (install-initial-constructor ctor
:force-p t
)
193 (setf (gethash function-name
*all-ctors
*) ctor
)
196 (defun ensure-allocator (function-name class-name
)
198 (or (gethash function-name
*all-ctors
*)
199 (make-allocator function-name class-name
))))
201 (defun make-allocator (function-name class-name
)
202 (let ((ctor (%make-ctor
'allocator class-name nil nil nil nil
)))
203 (install-initial-constructor ctor
:force-p t
)
204 (setf (gethash function-name
*all-ctors
*) ctor
)
207 ;;; *****************
208 ;;; Inline CTOR cache
209 ;;; *****************
211 ;;; The cache starts out as a list of CTORs, sorted with the most recently
212 ;;; used CTORs near the head. If it expands too much, we switch to a vector
213 ;;; with a simple hashing scheme.
215 ;;; Find CTOR for KEY (which is a class or class name) in a list. If the CTOR
216 ;;; is in the list but not one of the 4 first ones, return a new list with the
217 ;;; found CTOR at the head. Thread-safe: the new list shares structure with
218 ;;; the old, but is not desctructively modified. Returning the old list for
219 ;;; hits close to the head reduces ping-ponging with multiple threads seeking
221 (defun find-ctor (key list
)
222 (labels ((walk (tail from-head depth
)
223 (declare (fixnum depth
))
225 (let ((ctor (car tail
)))
226 (if (eq (ctor-class-or-name ctor
) key
)
229 (nconc (list ctor
) (nreverse from-head
) (cdr tail
)))
233 (cons ctor from-head
)
234 (logand #xf
(1+ depth
)))))
238 (declaim (inline sxhash-symbol-or-class
))
239 (defun sxhash-symbol-or-class (x)
240 (cond ((symbolp x
) (sxhash x
))
241 ((std-instance-p x
) (sb-impl::std-instance-hash x
))
242 ((fsc-instance-p x
) (sb-impl::fsc-instance-hash x
))
244 (bug "Something strange where symbol or class expected."))))
246 ;;; Max number of CTORs kept in an inline list cache. Once this is
247 ;;; exceeded we switch to a table.
248 (defconstant +ctor-list-max-size
+ 12)
249 ;;; Max table size for CTOR cache. If the table fills up at this size
250 ;;; we keep the same size and drop 50% of the old entries.
251 (defconstant +ctor-table-max-size
+ (expt 2 8))
252 ;;; Even if there is space in the cache, if we cannot fit a new entry
253 ;;; with max this number of collisions we expand the table (if possible)
255 (defconstant +ctor-table-max-probe-depth
+ 5)
257 (defun make-ctor-table (size)
258 (declare (index size
))
259 (let ((real-size (power-of-two-ceiling size
)))
260 (if (< real-size
+ctor-table-max-size
+)
261 (values (make-array real-size
:initial-element nil
) nil
)
262 (values (make-array +ctor-table-max-size
+ :initial-element nil
) t
))))
264 (declaim (inline mix-ctor-hash
))
265 (defun mix-ctor-hash (hash base
)
266 (logand most-positive-fixnum
(+ hash base
1)))
268 (defun put-ctor (ctor table
)
269 (cond ((try-put-ctor ctor table
)
272 (expand-ctor-table ctor table
))))
274 ;;; Thread-safe: if two threads write to the same index in parallel, the other
275 ;;; result is just lost. This is not an issue as the CTORs are used as their
276 ;;; own keys. If both were EQ, we're good. If non-EQ, the next time the other
277 ;;; one is needed we just cache it again -- hopefully not getting stomped on
279 (defun try-put-ctor (ctor table
)
280 (declare (simple-vector table
) (optimize speed
))
281 (let* ((class (ctor-class-or-name ctor
))
282 (base (sxhash-symbol-or-class class
))
284 (mask (1- (length table
))))
285 (declare (fixnum base hash mask
))
286 (loop repeat
+ctor-table-max-probe-depth
+
287 do
(let* ((index (logand mask hash
))
288 (old (aref table index
)))
289 (cond ((and old
(neq class
(ctor-class-or-name old
)))
290 (setf hash
(mix-ctor-hash hash base
)))
292 (setf (aref table index
) ctor
)
293 (return-from try-put-ctor t
)))))
294 ;; Didn't fit, must expand
297 (defun get-ctor (class table
)
298 (declare (simple-vector table
) (optimize speed
))
299 (let* ((base (sxhash-symbol-or-class class
))
301 (mask (1- (length table
))))
302 (declare (fixnum base hash mask
))
303 (loop repeat
+ctor-table-max-probe-depth
+
304 do
(let* ((index (logand mask hash
))
305 (old (aref table index
)))
306 (if (and old
(eq class
(ctor-class-or-name old
)))
307 (return-from get-ctor old
)
308 (setf hash
(mix-ctor-hash hash base
)))))
312 ;;; Thread safe: the old table is read, but if another thread mutates
313 ;;; it while we're reading we still get a sane result -- either the old
314 ;;; or the new entry. The new table is locally allocated, so that's ok
316 (defun expand-ctor-table (ctor old
)
317 (declare (simple-vector old
))
318 (let* ((old-size (length old
))
319 (new-size (* 2 old-size
))
320 (drop-random-entries nil
))
323 (multiple-value-bind (new max-size-p
) (make-ctor-table new-size
)
324 (let ((action (if drop-random-entries
325 ;; Same logic as in method caches -- see comment
327 (randomly-punting-lambda (old-ctor)
328 (try-put-ctor old-ctor new
))
330 (unless (try-put-ctor old-ctor new
)
332 (setf drop-random-entries t
)
333 (setf new-size
(* 2 new-size
)))
335 (aver (try-put-ctor ctor new
))
336 (dotimes (i old-size
)
337 (let ((old-ctor (aref old i
)))
339 (funcall action old-ctor
))))
340 (return-from expand-ctor-table
(values ctor new
)))))))
342 (defun ctor-list-to-table (list)
343 (let ((table (make-ctor-table (length list
))))
345 (setf table
(nth-value 1 (put-ctor ctor table
))))
348 (declaim (ftype (function * (values function t
&optional
))
349 ensure-cached-ctor ensure-cached-allocator
))
351 (flet ((get-or-put-ctor (class store thunk
)
352 (declare (type function thunk
))
354 (multiple-value-bind (ctor list
) (find-ctor class store
)
357 (let ((ctor (funcall thunk
)))
358 (if (< (length list
) +ctor-list-max-size
+)
359 (values ctor
(cons ctor list
))
360 (values ctor
(ctor-list-to-table list
))))))
361 (let ((ctor (get-ctor class store
)))
364 (put-ctor (funcall thunk
) store
))))))
366 (defun ensure-cached-ctor (class-name store initargs safe-code-p
)
370 (if (typep class-name
'(or symbol class
))
371 (let ((name (make-ctor-function-name class-name initargs safe-code-p
)))
372 (ensure-ctor name class-name initargs safe-code-p
))
373 ;; Invalid first argument: let MAKE-INSTANCE worry about it.
374 (return-from ensure-cached-ctor
375 (values (lambda (&rest ctor-parameters
)
376 (collect ((initargs))
377 (doplist (key value
) initargs
379 (initargs (if (constantp value
)
381 (pop ctor-parameters
))))
382 (apply #'make-instance class-name
(initargs))))
385 (defun ensure-cached-allocator (class store
)
390 (let ((function-name (list 'ctor
'allocator class
)))
391 (declare (dynamic-extent function-name
))
393 (or (gethash function-name
*all-ctors
*)
394 (make-allocator (copy-list function-name
) class
))))
395 ;; Invalid first argument: let ALLOCATE-INSTANCE worry about it.
396 (return-from ensure-cached-allocator
398 (declare (notinline allocate-instance
))
399 (allocate-instance class
))
402 ;;; ***********************************************
403 ;;; Compile-Time Expansion of MAKE-INSTANCE *******
404 ;;; ***********************************************
406 (defvar *compiling-optimized-constructor
* nil
)
408 ;;; There are some MAKE-INSTANCE calls compiled prior to this macro definition.
409 ;;; While it would be trivial to move earlier, I'm not sure that it would
412 ;;; This used to be a compiler macro but compiler macros are invoked
413 ;;; before FOP compilation, while source transforms aren't, there's no
414 ;;; reason to optimize make-instance for top-level forms
415 (sb-c:define-source-transform make-instance
(&whole form
&rest args
&environment env
)
416 ;; Compiling an optimized constructor for a non-standard class means
417 ;; compiling a lambda with (MAKE-INSTANCE #<SOME-CLASS X> ...) in it
418 ;; -- need to make sure we don't recurse there.
419 (or (unless (or *compiling-optimized-constructor
*
421 (make-instance->constructor-call form
(safe-code-p env
)))
424 (sb-c:define-source-transform allocate-instance
(class &rest initargs
)
425 (if (or *compiling-optimized-constructor
*
428 (allocate-instance->constructor-call class
)))
430 ;;; Build an inline cache: a CONS, with the actual cache in the CDR.
431 (defun make-ctor-inline-cache-form
432 (ensure-ctor-name class-arg
&optional ensure-ctor-args ctor-args
)
433 `(locally (declare (disable-package-locks .cache. .class-arg. .store. .fun.
))
434 (binding* ((.cache.
(load-time-value (cons 'ctor-cache nil
)))
435 (.store.
(cdr .cache.
))
436 (.class-arg.
,class-arg
)
438 (,ensure-ctor-name .class-arg. .store.
,@ensure-ctor-args
)))
439 ;; Thread safe: if multiple threads hit this in parallel, the
440 ;; update from the other one is just lost -- no harm done,
441 ;; except for the need to redo the work next time.
442 (unless (eq .store. .new-store.
)
443 (setf (cdr .cache.
) .new-store.
))
444 (funcall .fun.
,@ctor-args
))))
446 (defun allocate-instance->constructor-call
(class-arg)
447 (flet ((make-allocator-form (class-or-name)
448 (sb-int:check-deprecated-type class-or-name
)
449 (let ((function-name (list 'ctor
'allocator class-or-name
)))
450 ;; Return code constructing a ctor at load time, which,
451 ;; when called, will set its funcallable instance
452 ;; function to an optimized constructor function.
453 `(funcall (load-time-value
454 (ensure-allocator ',function-name
',class-or-name
) t
)))))
457 (make-allocator-form class-arg
))
458 ((typep class-arg
'(cons (eql find-class
)
459 (cons (cons (eql quote
) (cons symbol null
)) null
)))
460 (let ((class-name (second (second class-arg
))))
461 (make-allocator-form class-name
)))
463 (make-ctor-inline-cache-form 'ensure-cached-allocator class-arg
)))))
465 (defun make-instance->constructor-call
(form safe-code-p
)
466 (destructuring-bind (class-arg &rest args
) (cdr form
)
467 (flet (;; Return the name of parameter number I of a constructor
470 (format-symbol *pcl-package
* ".P~D." i
))
471 ;; Check if CLASS-ARG is a constant symbol. Give up if
474 (and class-arg
(constant-class-arg-p class-arg
)))
475 ;; Check if ARGS are suitable for an optimized constructor.
476 ;; Return NIL from the outer function if not.
478 (loop for
(key . more
) on args by
#'cddr do
479 (when (or (null more
)
480 (not (constant-symbol-p key
))
481 (eq :allow-other-keys
(constant-form-value key
)))
482 (return-from make-instance-
>constructor-call nil
))))
483 (maybe-expand-constant (value)
485 (constant-form-value value
)
488 ;; Collect a plist of initargs and constant values/parameter names
489 ;; in INITARGS. Collect non-constant initialization forms in
491 (multiple-value-bind (keys initargs value-forms
)
492 (loop for
(key value
) on args by
#'cddr and i from
0
494 collect
(constant-form-value key
) into keys
495 collect
(constant-form-value key
) into initargs
498 collect
(maybe-expand-constant value
) into keys
499 and collect value into initargs
501 collect
(parameter-name i
) into keys
502 and collect
(parameter-name i
) into initargs
503 and collect value into value-forms
505 (return (values keys initargs value-forms
)))
508 (let* ((class-or-name (constant-form-value class-arg
))
509 (function-name (make-ctor-function-name class-or-name keys
511 (sb-int:check-deprecated-type class-or-name
)
512 ;; Return code constructing a ctor at load time, which,
513 ;; when called, will set its funcallable instance
514 ;; function to an optimized constructor function.
515 `(funcall (load-time-value
516 (ensure-ctor ',function-name
',class-or-name
',initargs
520 ((and class-arg
(not (constantp class-arg
)))
521 (make-ctor-inline-cache-form
522 'ensure-cached-ctor class-arg
`(',initargs
',safe-code-p
) value-forms
)))))))
524 ;;; **************************************************
525 ;;; Load-Time Constructor Function Generation *******
526 ;;; **************************************************
528 ;;; The system-supplied primary INITIALIZE-INSTANCE and
529 ;;; SHARED-INITIALIZE methods. One cannot initialize these variables
530 ;;; to the right values here because said functions don't exist yet
531 ;;; when this file is first loaded.
532 (defvar *the-system-ii-method
* nil
)
533 (defvar *the-system-si-method
* nil
)
535 (defun install-optimized-constructor (ctor)
537 (let* ((class-or-name (ctor-class-or-name ctor
))
538 (class (ensure-class-finalized
539 (if (symbolp class-or-name
)
540 (find-class class-or-name
)
542 ;; We can have a class with an invalid layout here. Such a class
543 ;; cannot have a LAYOUT-INVALID of (:FLUSH ...) or (:OBSOLETE
544 ;; ...), because part of the deal is that those only happen from
545 ;; FORCE-CACHE-FLUSHES, which create a new valid wrapper for the
546 ;; class. An invalid layout of T needs to be flushed, however.
547 (when (eq (layout-invalid (class-wrapper class
)) t
)
548 (%force-cache-flushes class
))
549 (setf (ctor-class ctor
) class
)
550 (pushnew (make-weak-pointer ctor
) (plist-value class
'ctors
)
551 :test
#'eq
:key
#'weak-pointer-value
)
552 (multiple-value-bind (form locations names optimizedp
)
553 (constructor-function-form ctor
)
554 (setf (funcallable-instance-fun ctor
)
556 (let ((*compiling-optimized-constructor
* t
))
557 (handler-bind ((compiler-note #'muffle-warning
))
558 (compile nil
`(lambda ,names
(declare #.
*optimize-speed
*)
561 (ctor-state ctor
) (if optimizedp
'optimized
'fallback
))))))
563 (defun install-optimized-allocator (ctor)
565 (let* ((class-or-name (ctor-class-or-name ctor
))
566 (class (ensure-class-finalized
567 (if (symbolp class-or-name
)
568 (find-class class-or-name
)
570 ;; We can have a class with an invalid layout here. Such a class
571 ;; cannot have a LAYOUT-INVALID of (:FLUSH ...) or (:OBSOLETE
572 ;; ...), because part of the deal is that those only happen from
573 ;; FORCE-CACHE-FLUSHES, which create a new valid wrapper for the
574 ;; class. An invalid layout of T needs to be flushed, however.
575 (when (eq (layout-invalid (class-wrapper class
)) t
)
576 (%force-cache-flushes class
))
577 (setf (ctor-class ctor
) class
)
578 (pushnew (make-weak-pointer ctor
) (plist-value class
'ctors
)
579 :test
#'eq
:key
#'weak-pointer-value
)
580 (multiple-value-bind (form optimizedp
)
581 (allocator-function-form ctor
)
582 (setf (funcallable-instance-fun ctor
)
583 (let ((*compiling-optimized-constructor
* t
))
584 (handler-bind ((compiler-note #'muffle-warning
))
586 (ctor-state ctor
) (if optimizedp
'optimized
'fallback
))))))
588 (defun allocator-function-form (ctor)
589 (let ((class (ctor-class ctor
)))
590 (if (and (not (structure-class-p class
))
591 (not (condition-class-p class
))
592 (singleton-p (compute-applicable-methods #'allocate-instance
595 (member (slot-definition-allocation x
)
596 '(:instance
:class
)))
597 (class-slots class
)))
598 (values (optimizing-allocator-generator ctor
) t
)
600 (declare #.
*optimize-speed
*
601 (notinline allocate-instance
))
602 (allocate-instance ,class
))
605 (defun constructor-function-form (ctor)
606 (let* ((class (ctor-class ctor
))
607 (proto (class-prototype class
))
608 (make-instance-methods
609 (compute-applicable-methods #'make-instance
(list class
)))
610 (allocate-instance-methods
611 (compute-applicable-methods #'allocate-instance
(list class
)))
612 ;; I stared at this in confusion for a while, thinking
613 ;; carefully about the possibility of the class prototype not
614 ;; being of sufficient discrimiating power, given the
615 ;; possibility of EQL-specialized methods on
616 ;; INITIALIZE-INSTANCE or SHARED-INITIALIZE. However, given
617 ;; that this is a constructor optimization, the user doesn't
618 ;; yet have the instance to create a method with such an EQL
621 ;; There remains the (theoretical) possibility of someone
622 ;; coming along with code of the form
624 ;; (defmethod initialize-instance :before ((o foo) ...)
625 ;; (eval `(defmethod shared-initialize :before ((o foo) ...) ...)))
627 ;; but probably we can afford not to worry about this too
628 ;; much for now. -- CSR, 2004-07-12
630 (compute-applicable-methods #'initialize-instance
(list proto
)))
632 (compute-applicable-methods #'shared-initialize
(list proto t
)))
634 (loop for slot in
(class-slots class
)
635 when
(cdr (compute-applicable-methods
636 #'(setf slot-value-using-class
)
637 (list nil class proto slot
)))
640 (loop for slot in
(class-slots class
)
641 when
(cdr (compute-applicable-methods
642 #'slot-boundp-using-class
643 (list class proto slot
)))
645 ;; Cannot initialize these variables earlier because the generic
646 ;; functions don't exist when PCL is built.
647 (when (null *the-system-si-method
*)
648 (setq *the-system-si-method
*
649 (find-method #'shared-initialize
650 () (list *the-class-slot-object
* *the-class-t
*)))
651 (setq *the-system-ii-method
*
652 (find-method #'initialize-instance
653 () (list *the-class-slot-object
*))))
654 ;; Note that when there are user-defined applicable methods on
655 ;; MAKE-INSTANCE and/or ALLOCATE-INSTANCE, these will show up
656 ;; together with the system-defined ones in what
657 ;; COMPUTE-APPLICABLE-METHODS returns.
658 (let ((maybe-invalid-initargs
662 (ctor-default-initkeys
663 (ctor-initargs ctor
) (class-default-initargs class
))
664 (plist-keys (ctor-initargs ctor
)))
665 (append ii-methods si-methods
) nil nil
))
666 (custom-make-instance
667 (not (null (cdr make-instance-methods
)))))
668 (if (and (not (structure-class-p class
))
669 (not (condition-class-p class
))
670 (not custom-make-instance
)
671 (null (cdr allocate-instance-methods
))
673 (member (slot-definition-allocation x
)
674 '(:instance
:class
)))
676 (not maybe-invalid-initargs
)
677 (not (hairy-around-or-nonstandard-primary-method-p
678 ii-methods
*the-system-ii-method
*))
679 (not (around-or-nonstandard-primary-method-p
680 si-methods
*the-system-si-method
*)))
681 (optimizing-generator ctor ii-methods si-methods setf-svuc-slots sbuc-slots
)
682 (fallback-generator ctor ii-methods si-methods
683 (or maybe-invalid-initargs custom-make-instance
))))))
685 (defun around-or-nonstandard-primary-method-p
686 (methods &optional standard-method
)
687 (loop with primary-checked-p
= nil
688 for method in methods
689 as qualifiers
= (if (consp method
)
690 (early-method-qualifiers method
)
691 (safe-method-qualifiers method
))
692 when
(or (eq :around
(car qualifiers
))
693 (and (null qualifiers
)
694 (not primary-checked-p
)
695 (not (null standard-method
))
696 (not (eq standard-method method
))))
698 when
(null qualifiers
) do
699 (setq primary-checked-p t
)))
701 (defun hairy-around-or-nonstandard-primary-method-p
702 (methods &optional standard-method
)
703 (loop with primary-checked-p
= nil
704 for method in methods
705 as qualifiers
= (if (consp method
)
706 (early-method-qualifiers method
)
707 (safe-method-qualifiers method
))
708 when
(or (and (eq :around
(car qualifiers
))
709 (not (simple-next-method-call-p method
)))
710 (and (null qualifiers
)
711 (not primary-checked-p
)
712 (not (null standard-method
))
713 (not (eq standard-method method
))))
715 when
(null qualifiers
) do
716 (setq primary-checked-p t
)))
718 (defun fallback-generator (ctor ii-methods si-methods use-make-instance
)
719 (declare (ignore ii-methods si-methods
))
720 (let ((class (ctor-class ctor
))
721 (lambda-list (make-ctor-parameter-list ctor
))
722 (initargs (ctor-initargs ctor
)))
723 (if use-make-instance
724 `(lambda ,lambda-list
725 (declare #.
*optimize-speed
*)
726 ;; The CTOR MAKE-INSTANCE optimization checks for
727 ;; *COMPILING-OPTIMIZED-CONSTRUCTOR* which is bound around
728 ;; compilation of the constructor, hence avoiding the
729 ;; possibility of endless recursion.
730 (make-instance ,class
,@(quote-plist-keys initargs
)))
731 (let ((defaults (class-default-initargs class
)))
733 (setf initargs
(ctor-default-initargs initargs defaults
)))
734 `(lambda ,lambda-list
735 (declare #.
*optimize-speed
*)
736 (fast-make-instance ,class
,@(quote-plist-keys initargs
)))))))
738 ;;; Not as good as the real optimizing generator, but faster than going
739 ;;; via MAKE-INSTANCE: 1 GF call less, and no need to check initargs.
740 (defun fast-make-instance (class &rest initargs
)
741 (declare #.
*optimize-speed
*)
742 (declare (dynamic-extent initargs
))
743 (let ((.instance.
(apply #'allocate-instance class initargs
)))
744 (apply #'initialize-instance .instance. initargs
)
747 (defun optimizing-generator
748 (ctor ii-methods si-methods setf-svuc-slots sbuc-slots
)
749 (multiple-value-bind (locations names body early-unbound-markers-p
)
750 (fake-initialization-emf ctor ii-methods si-methods
751 setf-svuc-slots sbuc-slots
)
752 (let ((wrapper (class-wrapper (ctor-class ctor
))))
754 `(lambda ,(make-ctor-parameter-list ctor
)
755 (declare #.
*optimize-speed
*)
757 (when (layout-invalid ,wrapper
)
758 (install-initial-constructor ,ctor
)
759 (return (funcall ,ctor
,@(make-ctor-parameter-list ctor
))))
760 ,(wrap-in-allocate-forms ctor body early-unbound-markers-p
)))
765 (defun optimizing-allocator-generator
767 (let ((wrapper (class-wrapper (ctor-class ctor
))))
769 (declare #.
*optimize-speed
*)
771 (when (layout-invalid ,wrapper
)
772 (install-initial-constructor ,ctor
)
773 (return (funcall ,ctor
)))
774 ,(wrap-in-allocate-forms ctor nil t
)))))
776 ;;; Return a form wrapped around BODY that allocates an instance constructed
777 ;;; by CTOR. EARLY-UNBOUND-MARKERS-P means slots may be accessed before we
778 ;;; have explicitly initialized them, requiring all slots to start as
779 ;;; +SLOT-UNBOUND+. The resulting form binds the local variables .INSTANCE. to
780 ;;; the instance, and .SLOTS. to the instance's slot vector around BODY.
781 (defun wrap-in-allocate-forms (ctor body early-unbound-markers-p
)
782 (let* ((class (ctor-class ctor
))
783 (wrapper (class-wrapper class
))
784 (allocation-function (raw-instance-allocator class
))
785 (slots-fetcher (slots-fetcher class
)))
786 (if (eq allocation-function
'allocate-standard-instance
)
787 `(let ((.instance.
(%make-standard-instance nil
#-compact-instance-header
0))
789 ,(layout-length wrapper
)
790 ,@(when early-unbound-markers-p
791 '(:initial-element
+slot-unbound
+)))))
792 (setf (%instance-layout .instance.
) ,wrapper
)
793 (setf (std-instance-slots .instance.
) .slots.
)
796 `(let* ((.instance.
(,allocation-function
,wrapper
))
797 (.slots.
(,slots-fetcher .instance.
)))
798 (declare (ignorable .slots.
))
802 ;;; Return a form for invoking METHOD with arguments from ARGS. As
803 ;;; can be seen in METHOD-FUNCTION-FROM-FAST-FUNCTION, method
804 ;;; functions look like (LAMBDA (ARGS NEXT-METHODS) ...). We could
805 ;;; call fast method functions directly here, but benchmarks show that
806 ;;; there's no speed to gain, so lets avoid the hair here.
807 (defmacro invoke-method
(method args
&optional next-methods
)
808 `(funcall ,(the function
(method-function method
)) ,args
,next-methods
))
810 ;;; Return a form that is sort of an effective method comprising all
811 ;;; calls to INITIALIZE-INSTANCE and SHARED-INITIALIZE that would
812 ;;; normally have taken place when calling MAKE-INSTANCE.
813 (defun fake-initialization-emf
814 (ctor ii-methods si-methods setf-svuc-slots sbuc-slots
)
815 (multiple-value-bind (ii-around ii-before ii-primary ii-after
)
816 (standard-sort-methods ii-methods
)
817 (declare (ignore ii-primary
))
818 (multiple-value-bind (si-around si-before si-primary si-after
)
819 (standard-sort-methods si-methods
)
820 (declare (ignore si-primary
))
821 (aver (null si-around
))
822 (let ((initargs (ctor-initargs ctor
))
823 ;; :BEFORE and :AROUND initialization methods, and SETF SVUC and
824 ;; SBUC methods can cause slots to be accessed before the we have
825 ;; touched them here, which requires the instance-vector to be
826 ;; initialized with +SLOT-UNBOUND+ to start with.
827 (early-unbound-markers-p (or ii-before si-before ii-around
828 setf-svuc-slots sbuc-slots
)))
830 (locations names bindings vars defaulting-initargs body
)
831 (slot-init-forms ctor
832 early-unbound-markers-p
833 setf-svuc-slots sbuc-slots
)
838 (declare (ignorable ,@vars
))
839 (flet ((initialize-it (.ii-args. .next-methods.
)
840 ;; This has all the :BEFORE and :AFTER methods,
841 ;; and BODY does what primary SI method would do.
842 (declare (ignore .next-methods.
))
843 (let* ((.instance.
(car .ii-args.
))
844 ,@(when (or si-before si-after
)
846 (list* .instance. t
(cdr .ii-args.
))))))
847 ,@(loop for method in ii-before
848 collect
`(invoke-method ,method .ii-args.
))
849 ,@(loop for method in si-before
850 collect
`(invoke-method ,method .si-args.
))
852 ,@(loop for method in si-after
853 collect
`(invoke-method ,method .si-args.
))
854 ,@(loop for method in ii-after
855 collect
`(invoke-method ,method .ii-args.
))
857 (declare (dynamic-extent #'initialize-it
))
859 ,@(if (or ii-before ii-after ii-around si-before si-after
)
860 `((list .instance.
,@(quote-plist-keys initargs
)
861 ,@defaulting-initargs
))
862 `((list .instance.
)))))
864 ;; If there are :AROUND methods, call them first -- they get
865 ;; the normal chaining, with #'INITIALIZE-IT standing in for
867 `(let ((.next-methods.
868 (list ,@(cdr ii-around
) #'initialize-it
)))
869 (declare (dynamic-extent .next-methods.
))
870 (invoke-method ,(car ii-around
) .ii-args. .next-methods.
))
872 `(initialize-it .ii-args. nil
)))))
873 early-unbound-markers-p
))))))
875 ;;; Return four values from APPLICABLE-METHODS: around methods, before
876 ;;; methods, the applicable primary method, and applicable after
877 ;;; methods. Before and after methods are sorted in the order they
879 (defun standard-sort-methods (applicable-methods)
880 (loop for method in applicable-methods
881 as qualifiers
= (if (consp method
)
882 (early-method-qualifiers method
)
883 (safe-method-qualifiers method
))
885 collect method into primary
886 else if
(eq :around
(car qualifiers
))
887 collect method into around
888 else if
(eq :after
(car qualifiers
))
889 collect method into after
890 else if
(eq :before
(car qualifiers
))
891 collect method into before
893 (return (values around before
(first primary
) (reverse after
)))))
895 (defmacro with-type-checked
((type safe-p
) &body body
)
897 ;; To handle FUNCTION types reasonable, we use SAFETY 3 and
898 ;; THE instead of e.g. CHECK-TYPE.
900 (declare (optimize (safety 3)))
901 (the ,type
(progn ,@body
)))
904 ;;; Return as multiple values bindings for default initialization arguments,
905 ;;; variable names, defaulting initargs and a body for initializing instance
906 ;;; and class slots of an object costructed by CTOR. The variable .SLOTS. is
907 ;;; assumed to bound to the instance's slot vector. EARLY-UNBOUND-MARKERS-P
908 ;;; means other code will initialize instance slots to +SLOT-UNBOUND+, and we
909 ;;; have to check if something has already set slots before we initialize
911 (defun slot-init-forms (ctor early-unbound-markers-p setf-svuc-slots sbuc-slots
)
912 (let* ((class (ctor-class ctor
))
913 (initargs (ctor-initargs ctor
))
914 (initkeys (plist-keys initargs
))
915 (safe-p (ctor-safe-p ctor
))
916 (wrapper (class-wrapper class
))
918 (make-array (layout-length wrapper
) :initial-element nil
))
921 (defaulting-initargs ())
922 (default-initargs (class-default-initargs class
))
924 (compute-initarg-locations
925 class
(append initkeys
(mapcar #'car default-initargs
)))))
926 (labels ((initarg-locations (initarg)
927 (cdr (assoc initarg initarg-locations
:test
#'eq
)))
928 (initializedp (location)
931 (assoc location class-inits
:test
#'eq
))
933 (not (null (aref slot-vector location
))))
934 (t (bug "Weird location in ~S" 'slot-init-forms
))))
935 (class-init (location kind val type slotd
)
936 (aver (consp location
))
937 (unless (initializedp location
)
938 (push (list location kind val type slotd
) class-inits
)))
939 (instance-init (location kind val type slotd
)
940 (aver (integerp location
))
941 (unless (initializedp location
)
942 (setf (aref slot-vector location
)
943 (list kind val type slotd
))))
944 (default-init-var-name (i)
945 (format-symbol *pcl-package
* ".D~D." i
))
946 (location-var-name (i)
947 (format-symbol *pcl-package
* ".L~D." i
)))
948 ;; Loop over supplied initargs and values and record which
949 ;; instance and class slots they initialize.
950 (loop for
(key value
) on initargs by
#'cddr
951 as kind
= (if (constantp value
) 'constant
'param
)
952 as locations
= (initarg-locations key
)
953 do
(loop for
(location type slotd
) in locations
954 do
(if (consp location
)
955 (class-init location kind value type slotd
)
956 (instance-init location kind value type slotd
))))
957 ;; Loop over default initargs of the class, recording
958 ;; initializations of slots that have not been initialized
959 ;; above. Default initargs which are not in the supplied
960 ;; initargs are treated as if they were appended to supplied
961 ;; initargs, that is, their values must be evaluated even
962 ;; if not actually used for initializing a slot.
963 (loop for
(key initform initfn
) in default-initargs and i from
0
964 unless
(member key initkeys
:test
#'eq
)
965 do
(let* ((kind (if (constantp initform
) 'constant
'var
))
966 (init (if (eq kind
'var
) initfn initform
)))
969 (push (list 'quote key
) defaulting-initargs
)
970 (push initform defaulting-initargs
))
972 (push (list 'quote key
) defaulting-initargs
)
973 (push (default-init-var-name i
) defaulting-initargs
)))
975 (let ((init-var (default-init-var-name i
)))
977 (push (cons init-var initfn
) default-inits
)))
978 (loop for
(location type slotd
) in
(initarg-locations key
)
979 do
(if (consp location
)
980 (class-init location kind init type slotd
)
981 (instance-init location kind init type slotd
)))))
982 ;; Loop over all slots of the class, filling in the rest from
984 (loop for slotd in
(class-slots class
)
985 as location
= (slot-definition-location slotd
)
986 as type
= (slot-definition-type slotd
)
987 as allocation
= (slot-definition-allocation slotd
)
988 as initfn
= (slot-definition-initfunction slotd
)
989 as initform
= (slot-definition-initform slotd
) do
990 (unless (or (eq allocation
:class
)
992 (initializedp location
))
993 (if (constantp initform
)
994 (instance-init location
'initform initform type slotd
)
995 (instance-init location
996 'initform
/initfn initfn type slotd
))))
997 ;; Generate the forms for initializing instance and class slots.
998 (let ((instance-init-forms
999 (loop for slot-entry across slot-vector and i from
0
1000 as
(kind value type slotd
) = slot-entry
1002 (flet ((setf-form (value-form)
1003 (if (member slotd setf-svuc-slots
:test
#'eq
)
1004 `(setf (slot-value-using-class
1005 ,class .instance.
,slotd
)
1007 `(setf (clos-slots-ref .slots.
,i
)
1008 (with-type-checked (,type
,safe-p
)
1011 (if (member slotd sbuc-slots
:test
#'eq
)
1012 `(not (slot-boundp-using-class
1013 ,class .instance.
,slotd
))
1014 `(eq (clos-slots-ref .slots.
,i
)
1018 (unless early-unbound-markers-p
1019 `(setf (clos-slots-ref .slots.
,i
)
1024 (setf-form `(funcall ,value
)))
1026 (if early-unbound-markers-p
1027 `(when ,(not-boundp-form)
1028 ,(setf-form `(funcall ,value
)))
1029 (setf-form `(funcall ,value
))))
1031 (if early-unbound-markers-p
1032 `(when ,(not-boundp-form)
1033 ,(setf-form `',(constant-form-value value
)))
1034 (setf-form `',(constant-form-value value
))))
1036 (setf-form `',(constant-form-value value
))))))))
1037 ;; we are not allowed to modify QUOTEd locations, so we can't
1038 ;; generate code like (setf (cdr ',location) arg). Instead,
1039 ;; we have to do (setf (cdr .L0.) arg) and arrange for .L0. to
1040 ;; be bound to the location.
1041 (multiple-value-bind (names locations class-init-forms
)
1045 for
(location kind value type slotd
) in class-inits
1048 (constant `',(constant-form-value value
))
1049 ((param var
) `,value
)
1050 (initfn `(funcall ,value
)))
1051 when
(member slotd setf-svuc-slots
:test
#'eq
)
1052 collect
`(setf (slot-value-using-class
1053 ,class .instance.
,slotd
)
1055 into class-init-forms
1057 (let ((name (location-var-name (incf i
))))
1059 (push location locations
)
1061 (with-type-checked (,type
,safe-p
)
1063 into class-init-forms
1064 finally
(return (values (nreverse names
)
1065 (nreverse locations
)
1067 (multiple-value-bind (vars bindings
)
1068 (loop for
(var . initfn
) in
(nreverse default-inits
)
1069 collect var into vars
1070 collect
`(,var
(funcall ,initfn
)) into bindings
1071 finally
(return (values vars bindings
)))
1072 (values locations names
1074 (nreverse defaulting-initargs
)
1075 `(,@(delete nil instance-init-forms
)
1076 ,@class-init-forms
))))))))
1078 ;;; Return an alist of lists (KEY (LOCATION . TYPE-SPECIFIER) ...)
1079 ;;; telling, for each key in INITKEYS, which locations the initarg
1080 ;;; initializes and the associated type with the location. CLASS is
1081 ;;; the class of the instance being initialized.
1082 (defun compute-initarg-locations (class initkeys
)
1083 (loop with slots
= (class-slots class
)
1084 for key in initkeys collect
1085 (loop for slot in slots
1086 if
(memq key
(slot-definition-initargs slot
))
1087 collect
(list (slot-definition-location slot
)
1088 (slot-definition-type slot
)
1092 collect slot into remaining-slots
1094 (setq slots remaining-slots
)
1095 (return (cons key locations
)))))
1098 ;;; *******************************
1099 ;;; External Entry Points ********
1100 ;;; *******************************
1102 (defun update-ctors (reason &key class name generic-function method
)
1103 (labels ((reset (class &optional initarg-caches-p
(ctorsp t
))
1105 (setf (plist-value class
'ctors
)
1108 (let ((ctor (weak-pointer-value weak
)))
1110 (install-initial-constructor ctor
)
1113 (plist-value class
'ctors
))))
1114 (when initarg-caches-p
1115 (dolist (cache '(mi-initargs ri-initargs
))
1116 (setf (plist-value class cache
) ())))
1117 (dolist (subclass (class-direct-subclasses class
))
1118 (reset subclass initarg-caches-p ctorsp
))))
1120 ;; CLASS must have been specified.
1121 (finalize-inheritance
1123 ;; NAME must have been specified.
1125 (loop for ctor being the hash-values of
*all-ctors
*
1126 when
(eq (ctor-class-or-name ctor
) name
)
1128 (when (ctor-class ctor
)
1129 (reset (ctor-class ctor
)))
1131 ;; GENERIC-FUNCTION and METHOD must have been specified.
1132 ((add-method remove-method
)
1133 (flet ((class-of-1st-method-param (method)
1134 (type-class (first (method-specializers method
)))))
1135 (case (generic-function-name generic-function
)
1136 ((make-instance allocate-instance
)
1137 ;; FIXME: I can't see a way of working out which classes a
1138 ;; given metaclass specializer are applicable to short of
1139 ;; iterating and testing with class-of. It would be good
1140 ;; to not invalidate caches of system classes at this
1141 ;; point (where it is not legal to define a method
1142 ;; applicable to them on system functions). -- CSR,
1144 (reset (find-class 'standard-object
) t t
))
1145 ((initialize-instance shared-initialize
)
1146 (reset (class-of-1st-method-param method
) t t
))
1147 ((reinitialize-instance)
1148 (reset (class-of-1st-method-param method
) t nil
))
1149 (t (when (or (eq (generic-function-name generic-function
)
1150 'slot-boundp-using-class
)
1151 (equal (generic-function-name generic-function
)
1152 '(setf slot-value-using-class
)))
1153 ;; this looks awfully expensive, but given that one
1154 ;; can specialize on the SLOTD argument, nothing is
1155 ;; safe. -- CSR, 2004-07-12
1156 (reset (find-class 'standard-object
))))))))))
1158 (defun precompile-ctors ()
1159 (loop for ctor being the hash-values of
*all-ctors
*
1160 unless
(ctor-class ctor
)
1162 (let ((class (find-class (ctor-class-or-name ctor
) nil
)))
1163 (when (and class
(class-finalized-p class
))
1164 (install-optimized-constructor ctor
)))))
1166 (defun maybe-call-ctor (class initargs
)
1167 (flet ((frob-initargs (ctor)
1168 (do ((ctail (ctor-initargs ctor
))
1171 ((or (null ctail
) (null itail
))
1172 (values (nreverse args
) (and (null ctail
) (null itail
))))
1173 (unless (eq (pop ctail
) (pop itail
))
1175 (let ((cval (pop ctail
))
1177 (if (constantp cval
)
1178 (unless (eql cval ival
)
1180 (push ival args
))))))
1181 (dolist (weak (plist-value class
'ctors
))
1182 (let ((ctor (weak-pointer-value weak
)))
1184 (eq (ctor-type ctor
) 'ctor
)
1185 (eq (ctor-state ctor
) 'optimized
))
1186 (multiple-value-bind (ctor-args matchp
)
1187 (frob-initargs ctor
)
1189 (return (apply ctor ctor-args
)))))))))
1191 ;;; FIXME: CHECK-FOO-INITARGS share most of their bodies.
1192 (defun check-mi-initargs (class initargs
)
1193 (let* ((class-proto (class-prototype class
))
1194 (keys (plist-keys initargs
))
1195 (cache (plist-value class
'mi-initargs
))
1196 (cached (assoc keys cache
:test
#'equal
))
1203 (list (list* 'allocate-instance class initargs
)
1204 (list* 'initialize-instance class-proto initargs
)
1205 (list* 'shared-initialize class-proto t initargs
))
1207 (setf (plist-value class
'mi-initargs
)
1208 (acons keys invalid cache
))
1211 ;; FIXME: should have an operation here, and maybe a set of
1213 (initarg-error class invalid-keys
))))
1215 (defun check-ri-initargs (instance initargs
)
1216 (let* ((class (class-of instance
))
1217 (keys (plist-keys initargs
))
1218 (cache (plist-value class
'ri-initargs
))
1219 (cached (assoc keys cache
:test
#'equal
))
1224 ;; FIXME: give CHECK-INITARGS-1 and friends a
1225 ;; more mnemonic name and (possibly) a nicer,
1226 ;; more orthogonal interface.
1229 (list (list* 'reinitialize-instance instance initargs
)
1230 (list* 'shared-initialize instance nil initargs
))
1232 (setf (plist-value class
'ri-initargs
)
1233 (acons keys invalid cache
))
1236 (initarg-error class invalid-keys
))))
1238 ;;; end of ctor.lisp