(and (symbolp constant)
(not (null (symbol-package constant)))))))
-;;; somewhat akin to DEFAULT-INITARGS (SLOT-CLASS T T), but just
-;;; collecting the defaulted initargs for the call.
+;;; Somewhat akin to DEFAULT-INITARGS, but just collecting the defaulted
+;;; initargs for the call.
(defun ctor-default-initkeys (supplied-initargs class-default-initargs)
(loop for (key) in class-default-initargs
when (eq (getf supplied-initargs key '.not-there.) '.not-there.)
collect key))
+
+;;; Like DEFAULT-INITARGS, but return a list that can be spliced into source,
+;;; instead of a list with values already evaluated.
+(defun ctor-default-initargs (supplied-initargs class-default-initargs)
+ (loop for (key form fun) in class-default-initargs
+ when (eq (getf supplied-initargs key '.not-there.) '.not-there.)
+ append (list key (if (constantp form) form `(funcall ,fun)))
+ into default-initargs
+ finally
+ (return (append supplied-initargs default-initargs))))
\f
;;; *****************
;;; CTORS *********
;;; funcallable instance is set to it.
;;;
(!defstruct-with-alternate-metaclass ctor
- :slot-names (function-name class-or-name class initargs safe-p)
+ :slot-names (function-name class-or-name class initargs state safe-p)
:boa-constructor %make-ctor
:superclass-name function
:metaclass-name static-classoid
;;; optimized constructor function when called.
(defun install-initial-constructor (ctor &key force-p)
(when (or force-p (ctor-class ctor))
- (let ((*installing-ctor* t))
- (setf (ctor-class ctor) nil)
- (setf (funcallable-instance-fun ctor)
- #'(lambda (&rest args)
- (install-optimized-constructor ctor)
- (apply ctor args)))
- (setf (%funcallable-instance-info ctor 1)
- (ctor-function-name ctor)))))
+ (setf (ctor-class ctor) nil
+ (ctor-state ctor) 'initial)
+ (setf (funcallable-instance-fun ctor)
+ #'(lambda (&rest args)
+ (install-optimized-constructor ctor)
+ (apply ctor args)))
+ (setf (%funcallable-instance-info ctor 1)
+ (ctor-function-name ctor))))
(defun make-ctor-function-name (class-name initargs safe-code-p)
- (list* 'ctor class-name safe-code-p initargs))
+ (labels ((arg-name (x)
+ (typecase x
+ ;; this list of types might look arbitrary but it is
+ ;; exactly the set of types descended into by EQUAL,
+ ;; which is the predicate used by globaldb to test for
+ ;; name equality.
+ (list (gensym "LIST-INITARG-"))
+ (string (gensym "STRING-INITARG-"))
+ (bit-vector (gensym "BIT-VECTOR-INITARG-"))
+ (pathname (gensym "PATHNAME-INITARG-"))
+ (t x)))
+ (munge (list)
+ (let ((*gensym-counter* 0))
+ (mapcar #'arg-name list))))
+ (list* 'ctor class-name safe-code-p (munge initargs))))
;;; Keep this a separate function for testing.
(defun ensure-ctor (function-name class-name initargs safe-code-p)
- (unless (fboundp function-name)
- (make-ctor function-name class-name initargs safe-code-p)))
+ (with-world-lock ()
+ (if (fboundp function-name)
+ (the ctor (fdefinition function-name))
+ (make-ctor function-name class-name initargs safe-code-p))))
;;; Keep this a separate function for testing.
(defun make-ctor (function-name class-name initargs safe-p)
(without-package-locks ; for (setf symbol-function)
- (let ((ctor (%make-ctor function-name class-name nil initargs safe-p)))
- (push ctor *all-ctors*)
- (setf (fdefinition function-name) ctor)
- (install-initial-constructor ctor :force-p t)
- ctor)))
+ (let ((ctor (%make-ctor function-name class-name nil initargs nil safe-p)))
+ (install-initial-constructor ctor :force-p t)
+ (push ctor *all-ctors*)
+ (setf (fdefinition function-name) ctor)
+ ctor)))
\f
;;; *****************
;;; Inline CTOR cache
(setf table (nth-value 1 (put-ctor ctor table))))
table))
-(defun ctor-for-caching (class-name initargs safe-code-p)
- (let ((name (make-ctor-function-name class-name initargs safe-code-p)))
- (or (ensure-ctor name class-name initargs safe-code-p)
- (fdefinition name))))
-
(defun ensure-cached-ctor (class-name store initargs safe-code-p)
- (if (listp store)
- (multiple-value-bind (ctor list) (find-ctor class-name store)
- (if ctor
- (values ctor list)
- (let ((ctor (ctor-for-caching class-name initargs safe-code-p)))
- (if (< (length list) +ctor-list-max-size+)
- (values ctor (cons ctor list))
- (values ctor (ctor-list-to-table list))))))
- (let ((ctor (get-ctor class-name store)))
- (if ctor
- (values ctor store)
- (put-ctor (ctor-for-caching class-name initargs safe-code-p)
- store)))))
+ (flet ((maybe-ctor-for-caching ()
+ (if (typep class-name '(or symbol class))
+ (let ((name (make-ctor-function-name class-name initargs safe-code-p)))
+ (ensure-ctor name class-name initargs safe-code-p))
+ ;; Invalid first argument: let MAKE-INSTANCE worry about it.
+ (return-from ensure-cached-ctor
+ (values (lambda (&rest ctor-parameters)
+ (let (mi-initargs)
+ (doplist (key value) initargs
+ (push key mi-initargs)
+ (push (if (constantp value)
+ value
+ (pop ctor-parameters))
+ mi-initargs))
+ (apply #'make-instance class-name (nreverse mi-initargs))))
+ store)))))
+ (if (listp store)
+ (multiple-value-bind (ctor list) (find-ctor class-name store)
+ (if ctor
+ (values ctor list)
+ (let ((ctor (maybe-ctor-for-caching)))
+ (if (< (length list) +ctor-list-max-size+)
+ (values ctor (cons ctor list))
+ (values ctor (ctor-list-to-table list))))))
+ (let ((ctor (get-ctor class-name store)))
+ (if ctor
+ (values ctor store)
+ (put-ctor (maybe-ctor-for-caching) store))))))
\f
;;; ***********************************************
;;; Compile-Time Expansion of MAKE-INSTANCE *******
(define-compiler-macro make-instance (&whole form &rest args &environment env)
(declare (ignore args))
- ;; Compiling an optimized constructor for a non-standard class means compiling a
- ;; lambda with (MAKE-INSTANCE #<SOME-CLASS X> ...) in it -- need
- ;; to make sure we don't recurse there.
+ ;; Compiling an optimized constructor for a non-standard class means
+ ;; compiling a lambda with (MAKE-INSTANCE #<SOME-CLASS X> ...) in it
+ ;; -- need to make sure we don't recurse there.
(or (unless *compiling-optimized-constructor*
(make-instance->constructor-call form (safe-code-p env)))
form))
;; Return the name of parameter number I of a constructor
;; function.
(parameter-name (i)
- (let ((ps #(.p0. .p1. .p2. .p3. .p4. .p5.)))
- (if (array-in-bounds-p ps i)
- (aref ps i)
- (format-symbol *pcl-package* ".P~D." i))))
+ (format-symbol *pcl-package* ".P~D." i))
;; Check if CLASS-ARG is a constant symbol. Give up if
;; not.
(constant-class-p ()
(setf (info :function :where-from function-name) :defined)
(when (info :function :assumed-type function-name)
(setf (info :function :assumed-type function-name) nil)))
- ;; Return code constructing a ctor at load time, which, when
- ;; called, will set its funcallable instance function to an
- ;; optimized constructor function.
+ ;; Return code constructing a ctor at load time, which,
+ ;; when called, will set its funcallable instance
+ ;; function to an optimized constructor function.
`(locally
(declare (disable-package-locks ,function-name))
(let ((.x. (load-time-value
(function (&rest t) t))
,function-name))
(funcall (function ,function-name) ,@value-forms))))
- (when class-arg
- ;; Build an inline cache: a CONS, with the actual cache in the CDR.
+ (when (and class-arg (not (constantp class-arg)))
+ ;; Build an inline cache: a CONS, with the actual cache
+ ;; in the CDR.
`(locally (declare (disable-package-locks .cache. .class-arg. .store. .fun.
make-instance))
(let* ((.cache. (load-time-value (cons 'ctor-cache nil)))
(.class-arg. ,class-arg))
(multiple-value-bind (.fun. .new-store.)
(ensure-cached-ctor .class-arg. .store. ',initargs ',safe-code-p)
- ;; Thread safe: if multiple threads hit this in paralle, the update
- ;; from the other one is just lost -- no harm done, except for the
- ;; need to redo the work next time.
+ ;; Thread safe: if multiple threads hit this in
+ ;; parallel, the update from the other one is
+ ;; just lost -- no harm done, except for the need
+ ;; to redo the work next time.
(unless (eq .store. .new-store.)
(setf (cdr .cache.) .new-store.))
(funcall (truly-the function .fun.) ,@value-forms))))))))))
(%force-cache-flushes class))
(setf (ctor-class ctor) class)
(pushnew ctor (plist-value class 'ctors) :test #'eq)
- (setf (funcallable-instance-fun ctor)
- (multiple-value-bind (form locations names)
- (constructor-function-form ctor)
+ (multiple-value-bind (form locations names optimizedp)
+ (constructor-function-form ctor)
+ (setf (funcallable-instance-fun ctor)
(apply
(let ((*compiling-optimized-constructor* t))
- (compile nil `(lambda ,names ,form)))
- locations))))))
+ (handler-bind ((compiler-note #'muffle-warning))
+ (compile nil `(lambda ,names ,form))))
+ locations)
+ (ctor-state ctor) (if optimizedp 'optimized 'fallback))))))
(defun constructor-function-form (ctor)
(let* ((class (ctor-class ctor))
(compute-applicable-methods #'initialize-instance (list proto)))
(si-methods
(compute-applicable-methods #'shared-initialize (list proto t)))
- (setf-svuc-slots-methods
+ (setf-svuc-slots
(loop for slot in (class-slots class)
- collect (compute-applicable-methods
- #'(setf slot-value-using-class)
- (list nil class proto slot))))
- (sbuc-slots-methods
+ when (cdr (compute-applicable-methods
+ #'(setf slot-value-using-class)
+ (list nil class proto slot)))
+ collect slot))
+ (sbuc-slots
(loop for slot in (class-slots class)
- collect (compute-applicable-methods
- #'slot-boundp-using-class
- (list class proto slot)))))
+ when (cdr (compute-applicable-methods
+ #'slot-boundp-using-class
+ (list class proto slot)))
+ collect slot)))
;; Cannot initialize these variables earlier because the generic
;; functions don't exist when PCL is built.
(when (null *the-system-si-method*)
;; MAKE-INSTANCE and/or ALLOCATE-INSTANCE, these will show up
;; together with the system-defined ones in what
;; COMPUTE-APPLICABLE-METHODS returns.
- (if (and (not (structure-class-p class))
- (not (condition-class-p class))
- (null (cdr make-instance-methods))
- (null (cdr allocate-instance-methods))
- (every (lambda (x)
- (member (slot-definition-allocation x)
- '(:instance :class)))
- (class-slots class))
- (null (check-initargs-1
- class
- (append
- (ctor-default-initkeys
- (ctor-initargs ctor) (class-default-initargs class))
- (plist-keys (ctor-initargs ctor)))
- (append ii-methods si-methods) nil nil))
- (not (around-or-nonstandard-primary-method-p
- ii-methods *the-system-ii-method*))
- (not (around-or-nonstandard-primary-method-p
- si-methods *the-system-si-method*))
- ;; the instance structure protocol goes through
- ;; slot-value(-using-class) and friends (actually just
- ;; (SETF SLOT-VALUE-USING-CLASS) and
- ;; SLOT-BOUNDP-USING-CLASS), so if there are non-standard
- ;; applicable methods we can't shortcircuit them.
- (every (lambda (x) (= (length x) 1)) setf-svuc-slots-methods)
- (every (lambda (x) (= (length x) 1)) sbuc-slots-methods))
- (optimizing-generator ctor ii-methods si-methods)
- (fallback-generator ctor ii-methods si-methods))))
+ (let ((maybe-invalid-initargs
+ (check-initargs-1
+ class
+ (append
+ (ctor-default-initkeys
+ (ctor-initargs ctor) (class-default-initargs class))
+ (plist-keys (ctor-initargs ctor)))
+ (append ii-methods si-methods) nil nil))
+ (custom-make-instance
+ (not (null (cdr make-instance-methods)))))
+ (if (and (not (structure-class-p class))
+ (not (condition-class-p class))
+ (not custom-make-instance)
+ (null (cdr allocate-instance-methods))
+ (every (lambda (x)
+ (member (slot-definition-allocation x)
+ '(:instance :class)))
+ (class-slots class))
+ (not maybe-invalid-initargs)
+ (not (hairy-around-or-nonstandard-primary-method-p
+ ii-methods *the-system-ii-method*))
+ (not (around-or-nonstandard-primary-method-p
+ si-methods *the-system-si-method*)))
+ (optimizing-generator ctor ii-methods si-methods setf-svuc-slots sbuc-slots)
+ (fallback-generator ctor ii-methods si-methods
+ (or maybe-invalid-initargs custom-make-instance))))))
(defun around-or-nonstandard-primary-method-p
(methods &optional standard-method)
when (null qualifiers) do
(setq primary-checked-p t)))
-(defun fallback-generator (ctor ii-methods si-methods)
+(defun hairy-around-or-nonstandard-primary-method-p
+ (methods &optional standard-method)
+ (loop with primary-checked-p = nil
+ for method in methods
+ as qualifiers = (if (consp method)
+ (early-method-qualifiers method)
+ (safe-method-qualifiers method))
+ when (or (and (eq :around (car qualifiers))
+ (not (simple-next-method-call-p method)))
+ (and (null qualifiers)
+ (not primary-checked-p)
+ (not (null standard-method))
+ (not (eq standard-method method))))
+ return t
+ when (null qualifiers) do
+ (setq primary-checked-p t)))
+
+(defun fallback-generator (ctor ii-methods si-methods use-make-instance)
(declare (ignore ii-methods si-methods))
- `(lambda ,(make-ctor-parameter-list ctor)
- ;; The CTOR MAKE-INSTANCE optimization only kicks in when the
- ;; first argument to MAKE-INSTANCE is a constant symbol: by
- ;; calling it with a class, as here, we inhibit the optimization,
- ;; so removing the possibility of endless recursion. -- CSR,
- ;; 2004-07-12
- (make-instance ,(ctor-class ctor)
- ,@(quote-plist-keys (ctor-initargs ctor)))))
-
-(defun optimizing-generator (ctor ii-methods si-methods)
- (multiple-value-bind (locations names body before-method-p)
- (fake-initialization-emf ctor ii-methods si-methods)
+ (let ((class (ctor-class ctor))
+ (lambda-list (make-ctor-parameter-list ctor))
+ (initargs (ctor-initargs ctor)))
+ (if use-make-instance
+ `(lambda ,lambda-list
+ (declare #.*optimize-speed*)
+ ;; The CTOR MAKE-INSTANCE optimization checks for
+ ;; *COMPILING-OPTIMIZED-CONSTRUCTOR* which is bound around
+ ;; compilation of the constructor, hence avoiding the
+ ;; possibility of endless recursion.
+ (make-instance ,class ,@(quote-plist-keys initargs)))
+ (let ((defaults (class-default-initargs class)))
+ (when defaults
+ (setf initargs (ctor-default-initargs initargs defaults)))
+ `(lambda ,lambda-list
+ (declare #.*optimize-speed*)
+ (fast-make-instance ,class ,@(quote-plist-keys initargs)))))))
+
+;;; Not as good as the real optimizing generator, but faster than going
+;;; via MAKE-INSTANCE: 1 GF call less, and no need to check initargs.
+(defun fast-make-instance (class &rest initargs)
+ (declare #.*optimize-speed*)
+ (declare (dynamic-extent initargs))
+ (let ((.instance. (apply #'allocate-instance class initargs)))
+ (apply #'initialize-instance .instance. initargs)
+ .instance.))
+
+(defun optimizing-generator
+ (ctor ii-methods si-methods setf-svuc-slots sbuc-slots)
+ (multiple-value-bind (locations names body early-unbound-markers-p)
+ (fake-initialization-emf ctor ii-methods si-methods
+ setf-svuc-slots sbuc-slots)
(let ((wrapper (class-wrapper (ctor-class ctor))))
(values
`(lambda ,(make-ctor-parameter-list ctor)
(when (layout-invalid ,wrapper)
(install-initial-constructor ,ctor)
(return (funcall ,ctor ,@(make-ctor-parameter-list ctor))))
- ,(wrap-in-allocate-forms ctor body before-method-p)))
+ ,(wrap-in-allocate-forms ctor body early-unbound-markers-p)))
locations
- names))))
-
-;;; Return a form wrapped around BODY that allocates an instance
-;;; constructed by CTOR. BEFORE-METHOD-P set means we have to run
-;;; before-methods, in which case we initialize instance slots to
-;;; +SLOT-UNBOUND+. The resulting form binds the local variables
-;;; .INSTANCE. to the instance, and .SLOTS. to the instance's slot
-;;; vector around BODY.
-(defun wrap-in-allocate-forms (ctor body before-method-p)
+ names
+ t))))
+
+;;; Return a form wrapped around BODY that allocates an instance constructed
+;;; by CTOR. EARLY-UNBOUND-MARKERS-P means slots may be accessed before we
+;;; have explicitly initialized them, requiring all slots to start as
+;;; +SLOT-UNBOUND+. The resulting form binds the local variables .INSTANCE. to
+;;; the instance, and .SLOTS. to the instance's slot vector around BODY.
+(defun wrap-in-allocate-forms (ctor body early-unbound-markers-p)
(let* ((class (ctor-class ctor))
(wrapper (class-wrapper class))
(allocation-function (raw-instance-allocator class))
(get-instance-hash-code)))
(.slots. (make-array
,(layout-length wrapper)
- ,@(when before-method-p
- '(:initial-element +slot-unbound+)))))
+ ,@(when early-unbound-markers-p
+ '(:initial-element +slot-unbound+)))))
(setf (std-instance-wrapper .instance.) ,wrapper)
(setf (std-instance-slots .instance.) .slots.)
,body
;;; functions look like (LAMBDA (ARGS NEXT-METHODS) ...). We could
;;; call fast method functions directly here, but benchmarks show that
;;; there's no speed to gain, so lets avoid the hair here.
-(defmacro invoke-method (method args)
- `(funcall ,(method-function method) ,args ()))
+(defmacro invoke-method (method args &optional next-methods)
+ `(funcall ,(the function (method-function method)) ,args ,next-methods))
;;; Return a form that is sort of an effective method comprising all
;;; calls to INITIALIZE-INSTANCE and SHARED-INITIALIZE that would
;;; normally have taken place when calling MAKE-INSTANCE.
-(defun fake-initialization-emf (ctor ii-methods si-methods)
+(defun fake-initialization-emf
+ (ctor ii-methods si-methods setf-svuc-slots sbuc-slots)
(multiple-value-bind (ii-around ii-before ii-primary ii-after)
(standard-sort-methods ii-methods)
(declare (ignore ii-primary))
(multiple-value-bind (si-around si-before si-primary si-after)
(standard-sort-methods si-methods)
(declare (ignore si-primary))
- (aver (and (null ii-around) (null si-around)))
- (let ((initargs (ctor-initargs ctor)))
- (multiple-value-bind (locations names bindings vars defaulting-initargs body)
- (slot-init-forms ctor (or ii-before si-before))
+ (aver (null si-around))
+ (let ((initargs (ctor-initargs ctor))
+ ;; :BEFORE and :AROUND initialization methods, and SETF SVUC and
+ ;; SBUC methods can cause slots to be accessed before the we have
+ ;; touched them here, which requires the instance-vector to be
+ ;; initialized with +SLOT-UNBOUND+ to start with.
+ (early-unbound-markers-p (or ii-before si-before ii-around
+ setf-svuc-slots sbuc-slots)))
+ (multiple-value-bind
+ (locations names bindings vars defaulting-initargs body)
+ (slot-init-forms ctor
+ early-unbound-markers-p
+ setf-svuc-slots sbuc-slots)
(values
locations
names
`(let ,bindings
(declare (ignorable ,@vars))
- (let (,@(when (or ii-before ii-after)
- `((.ii-args.
- (list .instance. ,@(quote-plist-keys initargs) ,@defaulting-initargs))))
- ,@(when (or si-before si-after)
- `((.si-args.
- (list .instance. t ,@(quote-plist-keys initargs) ,@defaulting-initargs)))))
- ,@(loop for method in ii-before
- collect `(invoke-method ,method .ii-args.))
- ,@(loop for method in si-before
- collect `(invoke-method ,method .si-args.))
- ,@body
- ,@(loop for method in si-after
- collect `(invoke-method ,method .si-args.))
- ,@(loop for method in ii-after
- collect `(invoke-method ,method .ii-args.))))
- (or ii-before si-before)))))))
+ (flet ((initialize-it (.ii-args. .next-methods.)
+ ;; This has all the :BEFORE and :AFTER methods,
+ ;; and BODY does what primary SI method would do.
+ (declare (ignore .next-methods.))
+ (let* ((.instance. (car .ii-args.))
+ ,@(when (or si-before si-after)
+ `((.si-args.
+ (list* .instance. t (cdr .ii-args.))))))
+ ,@(loop for method in ii-before
+ collect `(invoke-method ,method .ii-args.))
+ ,@(loop for method in si-before
+ collect `(invoke-method ,method .si-args.))
+ ,@body
+ ,@(loop for method in si-after
+ collect `(invoke-method ,method .si-args.))
+ ,@(loop for method in ii-after
+ collect `(invoke-method ,method .ii-args.))
+ .instance.)))
+ (declare (dynamic-extent #'initialize-it))
+ (let ((.ii-args.
+ ,@(if (or ii-before ii-after ii-around si-before si-after)
+ `((list .instance. ,@(quote-plist-keys initargs)
+ ,@defaulting-initargs))
+ `((list .instance.)))))
+ ,(if ii-around
+ ;; If there are :AROUND methods, call them first -- they get
+ ;; the normal chaining, with #'INITIALIZE-IT standing in for
+ ;; the rest.
+ `(let ((.next-methods.
+ (list ,@(cdr ii-around) #'initialize-it)))
+ (declare (dynamic-extent .next-methods.))
+ (invoke-method ,(car ii-around) .ii-args. .next-methods.))
+ ;; The simple case.
+ `(initialize-it .ii-args. nil)))))
+ early-unbound-markers-p))))))
;;; Return four values from APPLICABLE-METHODS: around methods, before
;;; methods, the applicable primary method, and applicable after
(the ,type (progn ,@body)))
`(progn ,@body)))
-;;; Return as multiple values bindings for default initialization
-;;; arguments, variable names, defaulting initargs and a body for
-;;; initializing instance and class slots of an object costructed by
-;;; CTOR. The variable .SLOTS. is assumed to bound to the instance's
-;;; slot vector. BEFORE-METHOD-P T means before-methods will be
-;;; called, which means that 1) other code will initialize instance
-;;; slots to +SLOT-UNBOUND+ before the before-methods are run, and
-;;; that we have to check if these before-methods have set slots.
-(defun slot-init-forms (ctor before-method-p)
+;;; Return as multiple values bindings for default initialization arguments,
+;;; variable names, defaulting initargs and a body for initializing instance
+;;; and class slots of an object costructed by CTOR. The variable .SLOTS. is
+;;; assumed to bound to the instance's slot vector. EARLY-UNBOUND-MARKERS-P
+;;; means other code will initialize instance slots to +SLOT-UNBOUND+, and we
+;;; have to check if something has already set slots before we initialize
+;;; them.
+(defun slot-init-forms (ctor early-unbound-markers-p setf-svuc-slots sbuc-slots)
(let* ((class (ctor-class ctor))
(initargs (ctor-initargs ctor))
(initkeys (plist-keys initargs))
(safe-p (ctor-safe-p ctor))
+ (wrapper (class-wrapper class))
(slot-vector
- (make-array (layout-length (class-wrapper class))
- :initial-element nil))
+ (make-array (layout-length wrapper) :initial-element nil))
(class-inits ())
(default-inits ())
(defaulting-initargs ())
((integerp location)
(not (null (aref slot-vector location))))
(t (bug "Weird location in ~S" 'slot-init-forms))))
- (class-init (location kind val type)
+ (class-init (location kind val type slotd)
(aver (consp location))
(unless (initializedp location)
- (push (list location kind val type) class-inits)))
- (instance-init (location kind val type)
+ (push (list location kind val type slotd) class-inits)))
+ (instance-init (location kind val type slotd)
(aver (integerp location))
(unless (initializedp location)
- (setf (aref slot-vector location) (list kind val type))))
+ (setf (aref slot-vector location)
+ (list kind val type slotd))))
(default-init-var-name (i)
- (let ((ps #(.d0. .d1. .d2. .d3. .d4. .d5.)))
- (if (array-in-bounds-p ps i)
- (aref ps i)
- (format-symbol *pcl-package* ".D~D." i))))
+ (format-symbol *pcl-package* ".D~D." i))
(location-var-name (i)
- (let ((ls #(.l0. .l1. .l2. .l3. .l4. .l5.)))
- (if (array-in-bounds-p ls i)
- (aref ls i)
- (format-symbol *pcl-package* ".L~D." i)))))
+ (format-symbol *pcl-package* ".L~D." i)))
;; Loop over supplied initargs and values and record which
;; instance and class slots they initialize.
(loop for (key value) on initargs by #'cddr
as kind = (if (constantp value) 'constant 'param)
as locations = (initarg-locations key)
- do (loop for (location . type) in locations
+ do (loop for (location type slotd) in locations
do (if (consp location)
- (class-init location kind value type)
- (instance-init location kind value type))))
+ (class-init location kind value type slotd)
+ (instance-init location kind value type slotd))))
;; Loop over default initargs of the class, recording
;; initializations of slots that have not been initialized
;; above. Default initargs which are not in the supplied
(let ((init-var (default-init-var-name i)))
(setq init init-var)
(push (cons init-var initfn) default-inits)))
- (loop for (location . type) in (initarg-locations key)
+ (loop for (location type slotd) in (initarg-locations key)
do (if (consp location)
- (class-init location kind init type)
- (instance-init location kind init type)))))
+ (class-init location kind init type slotd)
+ (instance-init location kind init type slotd)))))
;; Loop over all slots of the class, filling in the rest from
;; slot initforms.
(loop for slotd in (class-slots class)
(null initfn)
(initializedp location))
(if (constantp initform)
- (instance-init location 'initform initform type)
- (instance-init location 'initform/initfn initfn type))))
+ (instance-init location 'initform initform type slotd)
+ (instance-init location
+ 'initform/initfn initfn type slotd))))
;; Generate the forms for initializing instance and class slots.
(let ((instance-init-forms
(loop for slot-entry across slot-vector and i from 0
- as (kind value type) = slot-entry collect
- (ecase kind
- ((nil)
- (unless before-method-p
- `(setf (clos-slots-ref .slots. ,i) +slot-unbound+)))
- ((param var)
- `(setf (clos-slots-ref .slots. ,i)
- (with-type-checked (,type ,safe-p)
- ,value)))
- (initfn
- `(setf (clos-slots-ref .slots. ,i)
- (with-type-checked (,type ,safe-p)
- (funcall ,value))))
- (initform/initfn
- (if before-method-p
- `(when (eq (clos-slots-ref .slots. ,i)
- +slot-unbound+)
- (setf (clos-slots-ref .slots. ,i)
- (with-type-checked (,type ,safe-p)
- (funcall ,value))))
- `(setf (clos-slots-ref .slots. ,i)
- (with-type-checked (,type ,safe-p)
- (funcall ,value)))))
- (initform
- (if before-method-p
- `(when (eq (clos-slots-ref .slots. ,i)
- +slot-unbound+)
- (setf (clos-slots-ref .slots. ,i)
- (with-type-checked (,type ,safe-p)
- ',(constant-form-value value))))
- `(setf (clos-slots-ref .slots. ,i)
- (with-type-checked (,type ,safe-p)
- ',(constant-form-value value)))))
- (constant
- `(setf (clos-slots-ref .slots. ,i)
- (with-type-checked (,type ,safe-p)
- ',(constant-form-value value))))))))
+ as (kind value type slotd) = slot-entry
+ collect
+ (flet ((setf-form (value-form)
+ (if (member slotd setf-svuc-slots :test #'eq)
+ `(setf (slot-value-using-class
+ ,class .instance. ,slotd)
+ ,value-form)
+ `(setf (clos-slots-ref .slots. ,i)
+ (with-type-checked (,type ,safe-p)
+ ,value-form))))
+ (not-boundp-form ()
+ (if (member slotd sbuc-slots :test #'eq)
+ `(not (slot-boundp-using-class
+ ,class .instance. ,slotd))
+ `(eq (clos-slots-ref .slots. ,i)
+ +slot-unbound+))))
+ (ecase kind
+ ((nil)
+ (unless early-unbound-markers-p
+ `(setf (clos-slots-ref .slots. ,i)
+ +slot-unbound+)))
+ ((param var)
+ (setf-form value))
+ (initfn
+ (setf-form `(funcall ,value)))
+ (initform/initfn
+ (if early-unbound-markers-p
+ `(when ,(not-boundp-form)
+ ,(setf-form `(funcall ,value)))
+ (setf-form `(funcall ,value))))
+ (initform
+ (if early-unbound-markers-p
+ `(when ,(not-boundp-form)
+ ,(setf-form `',(constant-form-value value)))
+ (setf-form `',(constant-form-value value))))
+ (constant
+ (setf-form `',(constant-form-value value))))))))
;; we are not allowed to modify QUOTEd locations, so we can't
;; generate code like (setf (cdr ',location) arg). Instead,
;; we have to do (setf (cdr .L0.) arg) and arrange for .L0. to
;; be bound to the location.
(multiple-value-bind (names locations class-init-forms)
- (loop for (location kind value type) in class-inits
- for i upfrom 0
- for name = (location-var-name i)
- collect name into names
- collect location into locations
- collect `(setf (cdr ,name)
- (with-type-checked (,type ,safe-p)
- ,(case kind
- (constant `',(constant-form-value value))
- ((param var) `,value)
- (initfn `(funcall ,value)))))
+ (loop with names
+ with locations
+ with i = -1
+ for (location kind value type slotd) in class-inits
+ for init-form
+ = (case kind
+ (constant `',(constant-form-value value))
+ ((param var) `,value)
+ (initfn `(funcall ,value)))
+ when (member slotd setf-svuc-slots :test #'eq)
+ collect `(setf (slot-value-using-class
+ ,class .instance. ,slotd)
+ ,init-form)
+ into class-init-forms
+ else collect
+ (let ((name (location-var-name (incf i))))
+ (push name names)
+ (push location locations)
+ `(setf (cdr ,name)
+ (with-type-checked (,type ,safe-p)
+ ,init-form)))
into class-init-forms
- finally (return (values names locations class-init-forms)))
+ finally (return (values (nreverse names)
+ (nreverse locations)
+ class-init-forms)))
(multiple-value-bind (vars bindings)
(loop for (var . initfn) in (nreverse default-inits)
collect var into vars
for key in initkeys collect
(loop for slot in slots
if (memq key (slot-definition-initargs slot))
- collect (cons (slot-definition-location slot)
- (slot-definition-type slot))
+ collect (list (slot-definition-location slot)
+ (slot-definition-type slot)
+ slot)
into locations
else
collect slot into remaining-slots
;;; *******************************
(defun update-ctors (reason &key class name generic-function method)
- (labels ((reset (class &optional ri-cache-p (ctorsp t))
+ (labels ((reset (class &optional initarg-caches-p (ctorsp t))
(when ctorsp
(dolist (ctor (plist-value class 'ctors))
(install-initial-constructor ctor)))
- (when ri-cache-p
- (setf (plist-value class 'ri-initargs) ()))
+ (when initarg-caches-p
+ (dolist (cache '(mi-initargs ri-initargs))
+ (setf (plist-value class cache) ())))
(dolist (subclass (class-direct-subclasses class))
- (reset subclass ri-cache-p ctorsp))))
+ (reset subclass initarg-caches-p ctorsp))))
(ecase reason
;; CLASS must have been specified.
(finalize-inheritance
(flet ((class-of-1st-method-param (method)
(type-class (first (method-specializers method)))))
(case (generic-function-name generic-function)
- ((make-instance allocate-instance
- initialize-instance shared-initialize)
+ ((make-instance allocate-instance)
+ ;; FIXME: I can't see a way of working out which classes a
+ ;; given metaclass specializer are applicable to short of
+ ;; iterating and testing with class-of. It would be good
+ ;; to not invalidate caches of system classes at this
+ ;; point (where it is not legal to define a method
+ ;; applicable to them on system functions). -- CSR,
+ ;; 2010-07-13
+ (reset (find-class 'standard-object) t t))
+ ((initialize-instance shared-initialize)
(reset (class-of-1st-method-param method) t t))
((reinitialize-instance)
(reset (class-of-1st-method-param method) t nil))
(when (and class (class-finalized-p class))
(install-optimized-constructor ctor))))))
+(defun maybe-call-ctor (class initargs)
+ (flet ((frob-initargs (ctor)
+ (do ((ctail (ctor-initargs ctor))
+ (itail initargs)
+ (args nil))
+ ((or (null ctail) (null itail))
+ (values (nreverse args) (and (null ctail) (null itail))))
+ (unless (eq (pop ctail) (pop itail))
+ (return nil))
+ (let ((cval (pop ctail))
+ (ival (pop itail)))
+ (if (constantp cval)
+ (unless (eql cval ival)
+ (return nil))
+ (push ival args))))))
+ (dolist (ctor (plist-value class 'ctors))
+ (when (eq (ctor-state ctor) 'optimized)
+ (multiple-value-bind (ctor-args matchp)
+ (frob-initargs ctor)
+ (when matchp
+ (return (apply ctor ctor-args))))))))
+
+;;; FIXME: CHECK-FOO-INITARGS share most of their bodies.
+(defun check-mi-initargs (class initargs)
+ (let* ((class-proto (class-prototype class))
+ (keys (plist-keys initargs))
+ (cache (plist-value class 'mi-initargs))
+ (cached (assoc keys cache :test #'equal))
+ (invalid-keys
+ (if (consp cached)
+ (cdr cached)
+ (let ((invalid
+ (check-initargs-1
+ class initargs
+ (list (list* 'allocate-instance class initargs)
+ (list* 'initialize-instance class-proto initargs)
+ (list* 'shared-initialize class-proto t initargs))
+ t nil)))
+ (setf (plist-value class 'mi-initargs)
+ (acons keys invalid cache))
+ invalid))))
+ (when invalid-keys
+ ;; FIXME: should have an operation here, and maybe a set of
+ ;; valid keys.
+ (error 'initarg-error :class class :initargs invalid-keys))))
+
(defun check-ri-initargs (instance initargs)
(let* ((class (class-of instance))
(keys (plist-keys initargs))
- (cached (assoc keys (plist-value class 'ri-initargs)
- :test #'equal))
+ (cache (plist-value class 'ri-initargs))
+ (cached (assoc keys cache :test #'equal))
(invalid-keys
(if (consp cached)
(cdr cached)
(list* 'shared-initialize instance nil initargs))
t nil)))
(setf (plist-value class 'ri-initargs)
- (acons keys invalid cached))
+ (acons keys invalid cache))
invalid))))
(when invalid-keys
(error 'initarg-error :class class :initargs invalid-keys))))