1.0.10.49: deadline refinements
[sbcl.git] / src / pcl / slots-boot.lisp
blobb2b1f21dcf2aa5a0651b2e0a6b6731e33e2a0b94
1 ;;;; This software is part of the SBCL system. See the README file for
2 ;;;; more information.
4 ;;;; This software is derived from software originally released by Xerox
5 ;;;; Corporation. Copyright and release statements follow. Later modifications
6 ;;;; to the software are in the public domain and are provided with
7 ;;;; absolutely no warranty. See the COPYING and CREDITS files for more
8 ;;;; information.
10 ;;;; copyright information from original PCL sources:
11 ;;;;
12 ;;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
13 ;;;; All rights reserved.
14 ;;;;
15 ;;;; Use and copying of this software and preparation of derivative works based
16 ;;;; upon this software are permitted. Any distribution of this software or
17 ;;;; derivative works must comply with all applicable United States export
18 ;;;; control laws.
19 ;;;;
20 ;;;; This software is made available AS IS, and Xerox Corporation makes no
21 ;;;; warranty about the software, its performance or its conformity to any
22 ;;;; specification.
24 (in-package "SB-PCL")
26 (let ((reader-specializers '(slot-object))
27 (writer-specializers '(t slot-object)))
28 (defun ensure-accessor (type fun-name slot-name)
29 (unless (fboundp fun-name)
30 (multiple-value-bind (lambda-list specializers method-class initargs doc)
31 (ecase type
32 ;; FIXME: change SLOT-OBJECT here to T to get SLOT-MISSING
33 ;; behaviour for non-slot-objects too?
34 (reader
35 (values '(object) reader-specializers 'global-reader-method
36 (make-std-reader-method-function 'slot-object slot-name)
37 "automatically-generated reader method"))
38 (writer
39 (values '(new-value object) writer-specializers
40 'global-writer-method
41 (make-std-writer-method-function 'slot-object slot-name)
42 "automatically-generated writer method"))
43 (boundp
44 (values '(object) reader-specializers 'global-boundp-method
45 (make-std-boundp-method-function 'slot-object slot-name)
46 "automatically-generated boundp method")))
47 (let ((gf (ensure-generic-function fun-name :lambda-list lambda-list)))
48 (add-method gf (make-a-method method-class
49 () lambda-list specializers
50 initargs doc :slot-name slot-name)))))
52 ;; KLUDGE: this is maybe PCL bootstrap mechanism #6 or #7, invented
53 ;; by CSR in June 2007. Making the bootstrap sane is getting higher
54 ;; on the "TODO: URGENT" list.
55 (defun !fix-ensure-accessor-specializers ()
56 (setf reader-specializers (mapcar #'find-class reader-specializers))
57 (setf writer-specializers (mapcar #'find-class writer-specializers))))
59 (defmacro accessor-slot-value (object slot-name &environment env)
60 (aver (constantp slot-name env))
61 (let* ((slot-name (constant-form-value slot-name env))
62 (reader-name (slot-reader-name slot-name)))
63 `(let ((.ignore. (load-time-value
64 (ensure-accessor 'reader ',reader-name ',slot-name))))
65 (declare (ignore .ignore.))
66 (truly-the (values t &optional)
67 (funcall #',reader-name ,object)))))
69 (defmacro accessor-set-slot-value (object slot-name new-value &environment env)
70 (aver (constantp slot-name env))
71 (setq object (macroexpand object env))
72 (let* ((slot-name (constant-form-value slot-name env))
73 (bind-object (unless (or (constantp new-value env) (atom new-value))
74 (let* ((object-var (gensym))
75 (bind `((,object-var ,object))))
76 (setf object object-var)
77 bind)))
78 (writer-name (slot-writer-name slot-name))
79 (form
80 `(let ((.ignore.
81 (load-time-value
82 (ensure-accessor 'writer ',writer-name ',slot-name)))
83 (.new-value. ,new-value))
84 (declare (ignore .ignore.))
85 (funcall #',writer-name .new-value. ,object)
86 .new-value.)))
87 (if bind-object
88 `(let ,bind-object ,form)
89 form)))
91 (defmacro accessor-slot-boundp (object slot-name &environment env)
92 (aver (constantp slot-name env))
93 (let* ((slot-name (constant-form-value slot-name env))
94 (boundp-name (slot-boundp-name slot-name)))
95 `(let ((.ignore. (load-time-value
96 (ensure-accessor 'boundp ',boundp-name ',slot-name))))
97 (declare (ignore .ignore.))
98 (funcall #',boundp-name ,object))))
100 (defun make-structure-slot-boundp-function (slotd)
101 (declare (ignore slotd))
102 (lambda (object)
103 (declare (ignore object))
106 (define-condition instance-structure-protocol-error
107 (reference-condition error)
108 ((slotd :initarg :slotd :reader instance-structure-protocol-error-slotd)
109 (fun :initarg :fun :reader instance-structure-protocol-error-fun))
110 (:report
111 (lambda (c s)
112 (format s "~@<The slot ~S has neither ~S nor ~S ~
113 allocation, so it can't be ~A by the default ~
114 ~S method.~@:>"
115 (instance-structure-protocol-error-slotd c)
116 :instance :class
117 (cond
118 ((member (instance-structure-protocol-error-fun c)
119 '(slot-value-using-class slot-boundp-using-class))
120 "read")
121 (t "written"))
122 (instance-structure-protocol-error-fun c)))))
124 (defun instance-structure-protocol-error (slotd fun)
125 (error 'instance-structure-protocol-error
126 :slotd slotd :fun fun
127 :references (list `(:amop :generic-function ,fun)
128 '(:amop :section (5 5 3)))))
130 (defun get-optimized-std-accessor-method-function (class slotd name)
131 (cond
132 ((structure-class-p class)
133 (ecase name
134 (reader (slot-definition-internal-reader-function slotd))
135 (writer (slot-definition-internal-writer-function slotd))
136 (boundp (make-structure-slot-boundp-function slotd))))
137 ((condition-class-p class)
138 (ecase name
139 (reader (slot-definition-reader-function slotd))
140 (writer (slot-definition-writer-function slotd))
141 (boundp (slot-definition-boundp-function slotd))))
143 (let* ((fsc-p (cond ((standard-class-p class) nil)
144 ((funcallable-standard-class-p class) t)
145 ((std-class-p class)
146 ;; Shouldn't be using the optimized-std-accessors
147 ;; in this case.
148 #+nil (format t "* warning: ~S ~S~% ~S~%"
149 name slotd class)
150 nil)
151 (t (error "~S is not a STANDARD-CLASS." class))))
152 (slot-name (slot-definition-name slotd))
153 (location (slot-definition-location slotd))
154 (function (ecase name
155 (reader #'make-optimized-std-reader-method-function)
156 (writer #'make-optimized-std-writer-method-function)
157 (boundp #'make-optimized-std-boundp-method-function)))
158 ;; KLUDGE: we need this slightly hacky calling convention
159 ;; for these functions for bootstrapping reasons: see
160 ;; !BOOTSTRAP-MAKE-SLOT-DEFINITION in braid.lisp. -- CSR,
161 ;; 2004-07-12
162 (value (funcall function fsc-p slotd slot-name location)))
163 (declare (type function function))
164 (values value (slot-definition-location slotd))))))
166 (defun make-optimized-std-reader-method-function
167 (fsc-p slotd slot-name location)
168 (declare #.*optimize-speed*)
169 (set-fun-name
170 (etypecase location
171 (fixnum
172 (if fsc-p
173 (lambda (instance)
174 (check-obsolete-instance instance)
175 (let ((value (clos-slots-ref (fsc-instance-slots instance)
176 location)))
177 (if (eq value +slot-unbound+)
178 (values
179 (slot-unbound (class-of instance) instance slot-name))
180 value)))
181 (lambda (instance)
182 (check-obsolete-instance instance)
183 (let ((value (clos-slots-ref (std-instance-slots instance)
184 location)))
185 (if (eq value +slot-unbound+)
186 (values
187 (slot-unbound (class-of instance) instance slot-name))
188 value)))))
189 (cons
190 (lambda (instance)
191 (check-obsolete-instance instance)
192 (let ((value (cdr location)))
193 (if (eq value +slot-unbound+)
194 (values (slot-unbound (class-of instance) instance slot-name))
195 value))))
196 (null
197 (lambda (instance)
198 (instance-structure-protocol-error slotd 'slot-value-using-class))))
199 `(reader ,slot-name)))
201 (defun make-optimized-std-writer-method-function
202 (fsc-p slotd slot-name location)
203 (declare #.*optimize-speed*)
204 (let* ((safe-p (and slotd
205 (slot-definition-class slotd)
206 (safe-p (slot-definition-class slotd))))
207 (writer-fun (etypecase location
208 (fixnum
209 (if fsc-p
210 (lambda (nv instance)
211 (check-obsolete-instance instance)
212 (setf (clos-slots-ref (fsc-instance-slots instance)
213 location)
214 nv))
215 (lambda (nv instance)
216 (check-obsolete-instance instance)
217 (setf (clos-slots-ref (std-instance-slots instance)
218 location)
219 nv))))
220 (cons
221 (lambda (nv instance)
222 (check-obsolete-instance instance)
223 (setf (cdr location) nv)))
224 (null
225 (lambda (nv instance)
226 (declare (ignore nv instance))
227 (instance-structure-protocol-error
228 slotd
229 '(setf slot-value-using-class))))))
230 (checking-fun (lambda (new-value instance)
231 ;; If we have a TYPE-CHECK-FUNCTION, call it.
232 (let* (;; Note that the class of INSTANCE here is not
233 ;; neccessarily the SLOT-DEFINITION-CLASS of
234 ;; the SLOTD passed to M-O-S-W-M-F, since it's
235 ;; e.g. possible for a subclass to define a
236 ;; slot of the same name but with no accessors.
237 ;; So we need to fetch the right type check function
238 ;; from the wrapper instead of just closing over it.
239 (wrapper (valid-wrapper-of instance))
240 (type-check-function
241 (cadr (find-slot-cell wrapper slot-name))))
242 (declare (type (or function null) type-check-function))
243 (when type-check-function
244 (funcall type-check-function new-value)))
245 ;; Then call the real writer.
246 (funcall writer-fun new-value instance))))
247 (set-fun-name (if safe-p
248 checking-fun
249 writer-fun)
250 `(writer ,slot-name))))
252 (defun make-optimized-std-boundp-method-function
253 (fsc-p slotd slot-name location)
254 (declare #.*optimize-speed*)
255 (set-fun-name
256 (etypecase location
257 (fixnum (if fsc-p
258 (lambda (instance)
259 (check-obsolete-instance instance)
260 (not (eq (clos-slots-ref (fsc-instance-slots instance)
261 location)
262 +slot-unbound+)))
263 (lambda (instance)
264 (check-obsolete-instance instance)
265 (not (eq (clos-slots-ref (std-instance-slots instance)
266 location)
267 +slot-unbound+)))))
268 (cons (lambda (instance)
269 (check-obsolete-instance instance)
270 (not (eq (cdr location) +slot-unbound+))))
271 (null
272 (lambda (instance)
273 (instance-structure-protocol-error slotd 'slot-boundp-using-class))))
274 `(boundp ,slot-name)))
276 (defun make-optimized-structure-slot-value-using-class-method-function
277 (function)
278 (declare (type function function))
279 (lambda (class object slotd)
280 (declare (ignore class slotd))
281 (funcall function object)))
283 (defun make-optimized-structure-setf-slot-value-using-class-method-function
284 (function)
285 (declare (type function function))
286 (lambda (nv class object slotd)
287 (declare (ignore class slotd))
288 (funcall function nv object)))
290 (defun make-optimized-structure-slot-boundp-using-class-method-function ()
291 (lambda (class object slotd)
292 (declare (ignore class object slotd))
295 (defun get-optimized-std-slot-value-using-class-method-function
296 (class slotd name)
297 (cond
298 ((structure-class-p class)
299 (ecase name
300 (reader (make-optimized-structure-slot-value-using-class-method-function
301 (slot-definition-internal-reader-function slotd)))
302 (writer (make-optimized-structure-setf-slot-value-using-class-method-function
303 (slot-definition-internal-writer-function slotd)))
304 (boundp (make-optimized-structure-slot-boundp-using-class-method-function))))
305 ((condition-class-p class)
306 (ecase name
307 (reader
308 (let ((fun (slot-definition-reader-function slotd)))
309 (declare (type function fun))
310 (lambda (class object slotd)
311 (declare (ignore class slotd))
312 (funcall fun object))))
313 (writer
314 (let ((fun (slot-definition-writer-function slotd)))
315 (declare (type function fun))
316 (lambda (new-value class object slotd)
317 (declare (ignore class slotd))
318 (funcall fun new-value object))))
319 (boundp
320 (let ((fun (slot-definition-boundp-function slotd)))
321 (declare (type function fun))
322 (lambda (class object slotd)
323 (declare (ignore class slotd))
324 (funcall fun object))))))
326 (let* ((fsc-p (cond ((standard-class-p class) nil)
327 ((funcallable-standard-class-p class) t)
328 (t (error "~S is not a standard-class" class))))
329 (function
330 (ecase name
331 (reader
332 #'make-optimized-std-slot-value-using-class-method-function)
333 (writer
334 #'make-optimized-std-setf-slot-value-using-class-method-function)
335 (boundp
336 #'make-optimized-std-slot-boundp-using-class-method-function))))
337 (declare (type function function))
338 (values (funcall function fsc-p slotd)
339 (slot-definition-location slotd))))))
341 (defun make-optimized-std-slot-value-using-class-method-function (fsc-p slotd)
342 (declare #.*optimize-speed*)
343 (let ((location (slot-definition-location slotd))
344 (slot-name (slot-definition-name slotd)))
345 (etypecase location
346 (fixnum (if fsc-p
347 (lambda (class instance slotd)
348 (declare (ignore slotd))
349 (check-obsolete-instance instance)
350 (let ((value (clos-slots-ref (fsc-instance-slots instance)
351 location)))
352 (if (eq value +slot-unbound+)
353 (values (slot-unbound class instance slot-name))
354 value)))
355 (lambda (class instance slotd)
356 (declare (ignore slotd))
357 (check-obsolete-instance instance)
358 (let ((value (clos-slots-ref (std-instance-slots instance)
359 location)))
360 (if (eq value +slot-unbound+)
361 (values (slot-unbound class instance slot-name))
362 value)))))
363 (cons (lambda (class instance slotd)
364 (declare (ignore slotd))
365 (check-obsolete-instance instance)
366 (let ((value (cdr location)))
367 (if (eq value +slot-unbound+)
368 (values (slot-unbound class instance slot-name))
369 value))))
370 (null
371 (lambda (class instance slotd)
372 (declare (ignore class instance))
373 (instance-structure-protocol-error slotd 'slot-value-using-class))))))
375 (defun make-optimized-std-setf-slot-value-using-class-method-function
376 (fsc-p slotd)
377 (declare #.*optimize-speed*)
378 (let ((location (slot-definition-location slotd))
379 (type-check-function
380 (when (and slotd
381 (slot-definition-class slotd)
382 (safe-p (slot-definition-class slotd)))
383 (slot-definition-type-check-function slotd))))
384 (macrolet ((make-mf-lambda (&body body)
385 `(lambda (nv class instance slotd)
386 (declare (ignore class slotd))
387 (check-obsolete-instance instance)
388 ,@body))
389 (make-mf-lambdas (&body body)
390 ;; Having separate lambdas for the NULL / not-NULL cases of
391 ;; TYPE-CHECK-FUNCTION is done to avoid runtime overhead
392 ;; for CLOS typechecking when it's not in use.
393 `(if type-check-function
394 (make-mf-lambda
395 (funcall (the function type-check-function) nv)
396 ,@body)
397 (make-mf-lambda
398 ,@body))))
399 (etypecase location
400 (fixnum
401 (if fsc-p
402 (make-mf-lambdas
403 (setf (clos-slots-ref (fsc-instance-slots instance) location)
404 nv))
405 (make-mf-lambdas
406 (setf (clos-slots-ref (std-instance-slots instance) location)
407 nv))))
408 (cons
409 (make-mf-lambdas (setf (cdr location) nv)))
410 (null (lambda (nv class instance slotd)
411 (declare (ignore nv class instance))
412 (instance-structure-protocol-error
413 slotd '(setf slot-value-using-class))))))))
415 (defun make-optimized-std-slot-boundp-using-class-method-function
416 (fsc-p slotd)
417 (declare #.*optimize-speed*)
418 (let ((location (slot-definition-location slotd)))
419 (etypecase location
420 (fixnum
421 (if fsc-p
422 (lambda (class instance slotd)
423 (declare (ignore class slotd))
424 (check-obsolete-instance instance)
425 (not (eq (clos-slots-ref (fsc-instance-slots instance) location)
426 +slot-unbound+)))
427 (lambda (class instance slotd)
428 (declare (ignore class slotd))
429 (check-obsolete-instance instance)
430 (not (eq (clos-slots-ref (std-instance-slots instance) location)
431 +slot-unbound+)))))
432 (cons (lambda (class instance slotd)
433 (declare (ignore class slotd))
434 (check-obsolete-instance instance)
435 (not (eq (cdr location) +slot-unbound+))))
436 (null
437 (lambda (class instance slotd)
438 (declare (ignore class instance))
439 (instance-structure-protocol-error slotd
440 'slot-boundp-using-class))))))
442 (defun get-accessor-from-svuc-method-function (class slotd sdfun name)
443 (macrolet ((emf-funcall (emf &rest args)
444 `(invoke-effective-method-function ,emf nil
445 :required-args ,args)))
446 (set-fun-name
447 (case name
448 (reader (lambda (instance)
449 (emf-funcall sdfun class instance slotd)))
450 (writer (lambda (nv instance)
451 (emf-funcall sdfun nv class instance slotd)))
452 (boundp (lambda (instance)
453 (emf-funcall sdfun class instance slotd))))
454 `(,name ,(class-name class) ,(slot-definition-name slotd)))))
456 (defun make-std-reader-method-function (class-or-name slot-name)
457 (declare (ignore class-or-name))
458 (let* ((initargs (copy-tree
459 (make-method-function
460 (lambda (instance)
461 (pv-binding1 ((bug "Please report this")
462 (instance) (instance-slots))
463 (instance-read-internal
464 .pv. instance-slots 0
465 (slot-value instance slot-name))))))))
466 (setf (getf (getf initargs 'plist) :slot-name-lists)
467 (list (list nil slot-name)))
468 initargs))
470 (defun make-std-writer-method-function (class-or-name slot-name)
471 (let* ((class (when (eq *boot-state* 'complete)
472 (if (typep class-or-name 'class)
473 class-or-name
474 (find-class class-or-name nil))))
475 (safe-p (and class
476 (safe-p class)))
477 (check-fun (lambda (new-value instance)
478 (let* ((class (class-of instance))
479 (slotd (find-slot-definition class slot-name))
480 (type-check-function
481 (when slotd
482 (slot-definition-type-check-function slotd))))
483 (when type-check-function
484 (funcall type-check-function new-value)))))
485 (initargs (copy-tree
486 (if safe-p
487 (make-method-function
488 (lambda (nv instance)
489 (funcall check-fun nv instance)
490 (pv-binding1 ((bug "Please report this")
491 (instance) (instance-slots))
492 (instance-write-internal
493 .pv. instance-slots 0 nv
494 (setf (slot-value instance slot-name) nv)))))
495 (make-method-function
496 (lambda (nv instance)
497 (pv-binding1 ((bug "Please report this")
498 (instance) (instance-slots))
499 (instance-write-internal
500 .pv. instance-slots 0 nv
501 (setf (slot-value instance slot-name) nv)))))))))
502 (setf (getf (getf initargs 'plist) :slot-name-lists)
503 (list nil (list nil slot-name)))
504 initargs))
506 (defun make-std-boundp-method-function (class-or-name slot-name)
507 (declare (ignore class-or-name))
508 (let* ((initargs (copy-tree
509 (make-method-function
510 (lambda (instance)
511 (pv-binding1 ((bug "Please report this")
512 (instance) (instance-slots))
513 (instance-boundp-internal
514 .pv. instance-slots 0
515 (slot-boundp instance slot-name))))))))
516 (setf (getf (getf initargs 'plist) :slot-name-lists)
517 (list (list nil slot-name)))
518 initargs))
520 ;;;; FINDING SLOT DEFINITIONS
522 ;;; Historical PCL found slot definitions by iterating over
523 ;;; CLASS-SLOTS, which is O(N) for number of slots, and moreover
524 ;;; requires a GF call (for SLOT-DEFINITION-NAME) for each slot in
525 ;;; list up to the desired one.
527 ;;; Current SBCL hashes the effective slot definitions, and some
528 ;;; information pulled out from them into a simple-vector, with bucket
529 ;;; chains made out of plists keyed by the slot names. This fixes
530 ;;; gives O(1) performance, and avoid the GF calls.
532 ;;; MAKE-SLOT-TABLE constructs the hashed vector out of a list of
533 ;;; effective slot definitions and the class they pertain to, and
534 ;;; FIND-SLOT-DEFINITION knows how to look up slots in that vector.
536 ;;; The only bit of cleverness in the implementation is to make the
537 ;;; vectors fairly tight, but always longer then 0 elements:
539 ;;; -- We don't want to waste huge amounts of space no these vectors,
540 ;;; which are mostly required by things like SLOT-VALUE with a
541 ;;; variable slot name, so a constant extension over the minimum
542 ;;; size seems like a good choise.
544 ;;; -- As long as the vector always has a length > 0
545 ;;; FIND-SLOT-DEFINITION doesn't need to handle the rare case of an
546 ;;; empty vector separately: it just returns a NIL.
548 ;;; In addition to the slot-definition we also store the slot-location
549 ;;; and type-check function for instances of standard metaclasses, so
550 ;;; that SLOT-VALUE &co using variable slot names can get at them
551 ;;; without additional GF calls.
553 ;;; Notes:
554 ;;; It would be probably better to store the vector in wrapper
555 ;;; instead: one less memory indirection, one less CLOS slot
556 ;;; access to get at it.
558 ;;; It would also be nice to have STANDARD-INSTANCE-STRUCTURE-P
559 ;;; generic instead of checking versus STANDARD-CLASS and
560 ;;; FUNCALLABLE-STANDARD-CLASS.
562 (defun find-slot-definition (class slot-name)
563 (dolist (slotd (class-slots class))
564 (when (eq slot-name (slot-definition-name slotd))
565 (return slotd))))
567 (defun find-slot-cell (wrapper slot-name)
568 (declare (symbol slot-name))
569 (let* ((vector (layout-slot-table wrapper))
570 (index (rem (sxhash slot-name) (length vector))))
571 (declare (simple-vector vector) (index index)
572 (optimize (sb-c::insert-array-bounds-checks 0)))
573 (do ((plist (the list (svref vector index)) (cdr plist)))
574 ((not (consp plist)))
575 (let ((key (car plist)))
576 (setf plist (cdr plist))
577 (when (eq key slot-name)
578 (return (car plist)))))))
580 (defun make-slot-table (class slots &optional bootstrap)
581 (let* ((n (+ (length slots) 2))
582 (vector (make-array n :initial-element nil))
583 (save-slot-location-p
584 (or bootstrap
585 (when (eq 'complete *boot-state*)
586 (let ((metaclass (class-of class)))
587 (or (eq metaclass *the-class-standard-class*)
588 (eq metaclass *the-class-funcallable-standard-class*))))))
589 (save-type-check-function-p
590 (unless bootstrap
591 (and (eq 'complete *boot-state*) (safe-p class)))))
592 (flet ((add-to-vector (name slot)
593 (declare (symbol name)
594 (optimize (sb-c::insert-array-bounds-checks 0)))
595 (let ((index (rem (sxhash name) n)))
596 (setf (svref vector index)
597 (list* name (list* (when save-slot-location-p
598 (if bootstrap
599 (early-slot-definition-location slot)
600 (slot-definition-location slot)))
601 (when save-type-check-function-p
602 (slot-definition-type-check-function slot))
603 slot)
604 (svref vector index))))))
605 (if (eq 'complete *boot-state*)
606 (dolist (slot slots)
607 (add-to-vector (slot-definition-name slot) slot))
608 (dolist (slot slots)
609 (add-to-vector (early-slot-definition-name slot) slot))))
610 vector))