Remove some test noise. A drop in the ocean unfortunately.
[sbcl.git] / src / code / target-defstruct.lisp
blob494fa245013704ab725bf1bfe139f8d1dfe1e84d
1 ;;;; This software is part of the SBCL system. See the README file for
2 ;;;; more information.
3 ;;;;
4 ;;;; This software is derived from the CMU CL system, which was
5 ;;;; written at Carnegie Mellon University and released into the
6 ;;;; public domain. The software is in the public domain and is
7 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
8 ;;;; files for more information.
10 (in-package "SB!KERNEL")
12 (/show0 "target-defstruct.lisp 12")
14 ;;;; structure frobbing primitives
16 ;;; Allocate a new instance with LENGTH data slots.
17 (defun %make-instance (length)
18 (declare (type index length))
19 (%make-instance length))
21 ;;; Given an instance, return its length.
22 (defun %instance-length (instance)
23 (declare (type instance instance))
24 (%instance-length instance))
26 ;;; Return the value from the INDEXth slot of INSTANCE. This is SETFable.
27 (defun %instance-ref (instance index)
28 (%instance-ref instance index))
30 ;;; Set the INDEXth slot of INSTANCE to NEW-VALUE.
31 (defun %instance-set (instance index new-value)
32 (setf (%instance-ref instance index) new-value))
34 ;;; Normally IR2 converted, definition needed for interpreted structure
35 ;;; constructors only.
36 #!+sb-eval
37 (defun %make-structure-instance (dd slot-specs &rest slot-values)
38 (let ((instance (%make-instance (dd-instance-length dd))))
39 (setf (%instance-layout instance) (dd-layout-or-lose dd))
40 (mapc (lambda (spec value)
41 (destructuring-bind (raw-type . index) (cdr spec)
42 (macrolet ((make-case ()
43 `(ecase raw-type
44 ((t)
45 (setf (%instance-ref instance index) value))
46 ,@(mapcar
47 (lambda (rsd)
48 `(,(raw-slot-data-raw-type rsd)
49 (setf (,(raw-slot-data-accessor-name rsd)
50 instance index)
51 value)))
52 *raw-slot-data-list*))))
53 (make-case))))
54 slot-specs slot-values)
55 instance))
57 (defun %raw-instance-ref/word (instance index)
58 (declare (type index index))
59 (%raw-instance-ref/word instance index))
60 (defun %raw-instance-set/word (instance index new-value)
61 (declare (type index index)
62 (type sb!vm:word new-value))
63 (%raw-instance-set/word instance index new-value))
65 (defun %raw-instance-ref/single (instance index)
66 (declare (type index index))
67 (%raw-instance-ref/single instance index))
68 (defun %raw-instance-set/single (instance index new-value)
69 (declare (type index index)
70 (type single-float new-value))
71 (%raw-instance-set/single instance index new-value))
73 (defun %raw-instance-ref/double (instance index)
74 (declare (type index index))
75 (%raw-instance-ref/double instance index))
76 (defun %raw-instance-set/double (instance index new-value)
77 (declare (type index index)
78 (type double-float new-value))
79 (%raw-instance-set/double instance index new-value))
81 (defun %raw-instance-ref/complex-single (instance index)
82 (declare (type index index))
83 (%raw-instance-ref/complex-single instance index))
84 (defun %raw-instance-set/complex-single (instance index new-value)
85 (declare (type index index)
86 (type (complex single-float) new-value))
87 (%raw-instance-set/complex-single instance index new-value))
89 (defun %raw-instance-ref/complex-double (instance index)
90 (declare (type index index))
91 (%raw-instance-ref/complex-double instance index))
92 (defun %raw-instance-set/complex-double (instance index new-value)
93 (declare (type index index)
94 (type (complex double-float) new-value))
95 (%raw-instance-set/complex-double instance index new-value))
97 (defun %instance-layout (instance)
98 (%instance-layout instance))
100 (defun %set-instance-layout (instance new-value)
101 (%set-instance-layout instance new-value))
103 (defun %make-funcallable-instance (len)
104 (%make-funcallable-instance len))
106 (defun funcallable-instance-p (x)
107 (funcallable-instance-p x))
109 (deftype funcallable-instance ()
110 `(satisfies funcallable-instance-p))
112 (defun %funcallable-instance-info (fin i)
113 (%funcallable-instance-info fin i))
115 (defun %set-funcallable-instance-info (fin i new-value)
116 (%set-funcallable-instance-info fin i new-value))
118 (defun funcallable-instance-fun (fin)
119 (%funcallable-instance-function fin))
121 (defun (setf funcallable-instance-fun) (new-value fin)
122 (setf (%funcallable-instance-function fin) new-value))
124 ;;;; target-only parts of the DEFSTRUCT top level code
126 ;;; A list of hooks designating functions of one argument, the
127 ;;; classoid, to be called when a defstruct is evaluated.
128 (!defvar *defstruct-hooks* nil)
130 ;;; the part of %DEFSTRUCT which makes sense only on the target SBCL
132 (defun %target-defstruct (dd)
133 (declare (type defstruct-description dd))
135 (/show0 "entering %TARGET-DEFSTRUCT")
137 (when (dd-doc dd)
138 (setf (fdocumentation (dd-name dd) 'structure)
139 (dd-doc dd)))
141 (let* ((classoid (find-classoid (dd-name dd)))
142 (layout (classoid-layout classoid)))
143 (declare (ignorable layout))
144 #!+interleaved-raw-slots
145 ;; Make a vector of EQUALP slots comparators, indexed by (- word-index data-start).
146 ;; This has to be assigned to something regardless of whether there are
147 ;; raw slots just in case someone mutates a layout which had raw
148 ;; slots into one which does not - although that would probably crash
149 ;; unless no instances exist or all raw slots miraculously contained
150 ;; bits which were the equivalent of valid Lisp descriptors.
152 ;; It's not worth adding a #-interleaved-raw-slots case to this optimization
153 ;; because every architecture can be made to use the new approach.
154 (setf (layout-equalp-tests layout)
155 (if (zerop (layout-untagged-bitmap layout))
157 ;; The initial element of NIL means "do not compare".
158 ;; Ignored words (comparator = NIL) fall into two categories:
159 ;; - pseudo-ignored, which get compared by their
160 ;; predecessor word, as for complex-double-float,
161 ;; - internal padding words which are truly ignored.
162 ;; Other words are compared as tagged if the comparator is 0,
163 ;; or as untagged if the comparator is a type-specific function.
164 (let ((comparators
165 ;; If data-start is 1, subtract 1 because we don't need
166 ;; a comparator for the LAYOUT slot.
167 (make-array (- (dd-length dd) sb!vm:instance-data-start)
168 :initial-element nil)))
169 (dolist (slot (dd-slots dd) comparators)
170 ;; -1 because LAYOUT (slot index 0) has no comparator stored.
171 (setf (aref comparators
172 (- (dsd-index slot) sb!vm:instance-data-start))
173 (let ((raw-type (dsd-raw-type slot)))
174 (if (eq raw-type t)
175 0 ; means recurse using EQUALP
176 (raw-slot-data-comparer
177 (raw-slot-data-or-lose raw-type)))))))))
179 (dolist (fun *defstruct-hooks*)
180 (funcall fun classoid)))
182 (/show0 "leaving %TARGET-DEFSTRUCT")
183 (values))
185 ;;; Copy any old kind of structure.
186 (defun copy-structure (structure)
187 #!+sb-doc
188 "Return a copy of STRUCTURE with the same (EQL) slot values."
189 (declare (type structure-object structure))
190 (let ((layout (%instance-layout structure)))
191 (when (layout-invalid layout)
192 (error "attempt to copy an obsolete structure:~% ~S" structure))
193 (let ((res (%make-instance (%instance-length structure)))
194 (len (layout-length layout)))
195 (declare (type index len))
196 #!-interleaved-raw-slots
197 (let ((nuntagged (layout-n-untagged-slots layout)))
198 ;; Copy ordinary slots including the layout.
199 (dotimes (i (- len nuntagged))
200 (declare (type index i))
201 (setf (%instance-ref res i) (%instance-ref structure i)))
202 ;; Copy raw slots.
203 (dotimes (i nuntagged)
204 (declare (type index i))
205 (setf (%raw-instance-ref/word res i)
206 (%raw-instance-ref/word structure i))))
207 #!+interleaved-raw-slots
208 (let ((metadata (layout-untagged-bitmap layout)))
209 ;; Don't assume that %INSTANCE-REF can access the layout.
210 (setf (%instance-layout res) (%instance-layout structure))
211 ;; With interleaved slots, the only difference between %instance-ref
212 ;; and %raw-instance-ref/word is the storage class of the VOP operands.
213 ;; Since x86(-64) doesn't partition the register set, the bitmap test
214 ;; could be skipped if we wanted to copy everything as raw.
215 (macrolet ((copy-loop (raw-p &optional step)
216 `(do ((i sb!vm:instance-data-start (1+ i)))
217 ((>= i len))
218 (declare (index i))
219 (if ,raw-p
220 (setf (%raw-instance-ref/word res i)
221 (%raw-instance-ref/word structure i))
222 (setf (%instance-ref res i)
223 (%instance-ref structure i)))
224 ,step)))
225 (cond ((zerop metadata) ; no untagged slots.
226 (copy-loop nil))
227 ;; The fixnum case uses fixnum operations for ODDP and ASH.
228 ((fixnump metadata) ; shift and mask is faster than logbitp
229 (copy-loop (oddp (truly-the fixnum metadata))
230 (setq metadata (ash metadata -1))))
231 (t ; bignum - use LOGBITP to avoid consing more bignums
232 (copy-loop (logbitp i metadata))))))
233 res)))
236 ;; Do an EQUALP comparison on the raw slots (only, not the normal slots) of a
237 ;; structure.
238 #!-interleaved-raw-slots
239 (defun raw-instance-slots-equalp (layout x y)
240 ;; This implementation sucks, but hopefully EQUALP on raw structures
241 ;; won't be a major bottleneck for anyone. It'd be tempting to do
242 ;; all this with %RAW-INSTANCE-REF/WORD and bitwise comparisons, but
243 ;; that'll fail in some cases. For example -0.0 and 0.0 are EQUALP
244 ;; but have different bit patterns. -- JES, 2007-08-21
245 (loop for dsd in (dd-slots (layout-info layout))
246 for raw-type = (dsd-raw-type dsd)
247 for rsd = (unless (eql raw-type t)
248 (find raw-type
249 *raw-slot-data-list*
250 :key 'raw-slot-data-raw-type))
251 always (or (not rsd)
252 (funcall (raw-slot-data-comparer rsd) (dsd-index dsd) x y))))
254 ;;; default PRINT-OBJECT method
256 (defun %default-structure-pretty-print (structure stream name dd)
257 (pprint-logical-block (stream nil :prefix "#S(" :suffix ")")
258 (prin1 name stream)
259 (let ((remaining-slots (dd-slots dd)))
260 (when remaining-slots
261 (write-char #\space stream)
262 ;; CMU CL had (PPRINT-INDENT :BLOCK 2 STREAM) here,
263 ;; but I can't see why. -- WHN 20000205
264 (pprint-newline :linear stream)
265 (loop (pprint-pop)
266 (let ((slot (pop remaining-slots)))
267 (write-char #\: stream)
268 (output-symbol-name (symbol-name (dsd-name slot)) stream)
269 (write-char #\space stream)
270 (pprint-newline :miser stream)
271 (output-object (funcall (dsd-accessor-name slot) structure)
272 stream)
273 (when (null remaining-slots)
274 (return))
275 (write-char #\space stream)
276 (pprint-newline :linear stream)))))))
278 (defun %default-structure-ugly-print (structure stream name dd)
279 (descend-into (stream)
280 (write-string "#S(" stream)
281 (prin1 name stream)
282 (do ((index 0 (1+ index))
283 (limit (or (and (not *print-readably*) *print-length*)
284 most-positive-fixnum))
285 (remaining-slots (dd-slots dd) (cdr remaining-slots)))
286 ((or (null remaining-slots) (>= index limit))
287 (write-string (if remaining-slots " ...)" ")") stream))
288 (declare (type index index))
289 (write-string " :" stream)
290 (let ((slot (first remaining-slots)))
291 (output-symbol-name (symbol-name (dsd-name slot)) stream)
292 (write-char #\space stream)
293 (output-object (funcall (dsd-accessor-name slot) structure)
294 stream)))))
296 (defun default-structure-print (structure stream depth)
297 (declare (ignore depth))
298 (if (funcallable-instance-p structure)
299 (print-unreadable-object (structure stream :identity t :type t))
300 (let* ((layout (%instance-layout structure))
301 (dd (layout-info layout))
302 (name (classoid-name (layout-classoid layout))))
303 (cond ((not dd)
304 ;; FIXME? this branch may be unnecessary as a consequence
305 ;; of change f02bee325920166b69070e4735a8a3f295f8edfd which
306 ;; stopped the badness is a stronger way. It should be the case
307 ;; that absence of a DD can't happen unless the classoid is absent.
308 ;; KLUDGE: during PCL build debugging, we can sometimes
309 ;; attempt to print out a PCL object (with null LAYOUT-INFO).
310 (pprint-logical-block (stream nil :prefix "#<" :suffix ">")
311 (prin1 name stream)
312 (write-char #\space stream)
313 (write-string "(no LAYOUT-INFO)" stream)))
314 ((not (dd-slots dd))
315 ;; the structure type doesn't count as a component for *PRINT-LEVEL*
316 ;; processing. We can likewise elide the logical block processing,
317 ;; since all we have to print is the type name. -- CSR, 2004-10-05
318 (write-string "#S(" stream)
319 (prin1 name stream)
320 (write-char #\) stream))
322 (funcall (if *print-pretty*
323 #'%default-structure-pretty-print
324 #'%default-structure-ugly-print)
325 structure stream name dd))))))
327 (def!method print-object ((x structure-object) stream)
328 (default-structure-print x stream *current-level-in-print*))
330 ;; This generates a sexpr that can be recognized as having a particular
331 ;; shape so that the dumping mechanism can decide if it is or is not
332 ;; necessary to run that code through the main compiler - FASL operations
333 ;; can be used in the case that all slots are preserved.
334 ;; In particular, there are no gensyms in the result, so that calling this
335 ;; twice on the same object yields the same list as compared by EQUAL.
336 (defun structure-obj-slot-saving-forms (struct slot-names slot-names-p)
337 (let* ((layout (%instance-layout struct))
338 (dd (layout-info layout)))
339 (mapcan (lambda (dsd)
340 (when (or (not slot-names-p) (memq (dsd-name dsd) slot-names))
341 (let ((index (dsd-index dsd))
342 (raw-type (dsd-raw-type dsd)))
343 (if (eq raw-type t)
344 `((%instance-ref ,struct ,index)
345 ,(let ((val (%instance-ref struct index)))
346 (if (and (or (listp val) (symbolp val))
347 (not (member val '(nil t))))
348 (list 'quote val)
349 val)))
350 (let ((accessor (raw-slot-data-accessor-name
351 (raw-slot-data-or-lose raw-type))))
352 `((,accessor ,struct ,index)
353 ,(funcall accessor struct index)))))))
354 (dd-slots dd))))
356 ;; Return T if CREATION-FORM and INIT-FORM would have the identical effect
357 ;; as :SB-JUST-DUMP-IT-NORMALLY for STRUCT. MAKE-LOAD-FORM-SAVING-SLOTS can't
358 ;; merely return the magic token (when possible) because a user application
359 ;; could call MAKE-LOAD-FORM-SAVING-SLOTS to obtain forms that can be evaluated
360 ;; or otherwise examined. So instead we scan the code and detect whether it is
361 ;; identical to what was returned from a trivial use of M-L-F-S-S.
362 (defun canonical-slot-saving-forms-p (struct creation-form init-form)
363 (and (sb!c::canonical-instance-maker-form-p creation-form)
364 (typep init-form '(cons (eql setf)))
365 (eq (cadr (cadr (cadr creation-form))) (class-name (class-of struct)))
366 (= (length (dd-slots (layout-info (%instance-layout struct))))
367 (ash (list-length (cdr init-form)) -1))
368 (flet ((eq-quoted-p (a b)
369 (and (typep a '(cons (eql quote) (cons t null)))
370 (typep b '(cons (eql quote) (cons t null)))
371 (eq (cadr a) (cadr b)))))
372 ;; Naively, EQUALP would almost work to compare the slot assignments,
373 ;; but we must not get stuck in circular lists, so traverse by hand.
374 (loop for (expect-place expect-value)
375 on (structure-obj-slot-saving-forms struct nil nil) by #'cddr
376 for (actual-place actual-value) on (cdr init-form) by #'cddr
377 always (and (equal actual-place expect-place)
378 ;; Use EQL, not EQ. Values come from the identical
379 ;; struct, but reading a raw slot can create new
380 ;; pointers. For QUOTE forms, EQ is ok.
381 (or (eql actual-value expect-value)
382 (eq-quoted-p actual-value expect-value)))))))
384 (/show0 "target-defstruct.lisp end of file")