1 ;;;; This file is for testing UNWIND-TO-FRAME-AND-CALL, used for
2 ;;;; implementing RESTART-FRAME and RETURN-FROM-FRAME in the debugger.
4 ;;;; This software is part of the SBCL system. See the README file for
7 ;;;; While most of SBCL is derived from the CMU CL system, the test
8 ;;;; files (like this one) were written from scratch after the fork
11 ;;;; This software is in the public domain and is provided with
12 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
13 ;;;; more information.
15 ;;; The debugger doesn't have any native knowledge of the interpreter
16 (when (eq sb-ext:*evaluator-mode* :interpret)
17 (sb-ext:exit :code 104))
19 (declaim (optimize debug))
21 (defun return-from-frame (frame-name &rest values)
22 (let ((frame (sb-di::top-frame)))
23 (loop until (equal frame-name
24 (sb-debug::frame-call frame))
25 do (setf frame (sb-di::frame-down frame)))
27 (assert (sb-debug::frame-has-debug-tag-p frame))
28 (sb-debug::unwind-to-frame-and-call frame
30 (values-list values)))))
32 (defun restart-frame (frame-name)
33 (let ((frame (sb-di::top-frame)))
34 (loop until (equal (sb-debug::frame-call frame)
36 do (setf frame (sb-di::frame-down frame)))
38 (assert (sb-debug::frame-has-debug-tag-p frame))
39 (let* ((call-list (sb-debug::frame-call-as-list frame))
40 (fun (fdefinition (car call-list))))
41 (sb-debug::unwind-to-frame-and-call frame
43 (apply fun (cdr call-list)))))))
51 ;;;; Test RESTART-FRAME
53 (define-condition restart-condition () ())
57 (defun restart/special (*foo*)
61 (signal 'restart-condition))
64 (defun restart/optional-special (&optional (*foo* 1))
68 (signal 'restart-condition))
71 (defun restart/normal (foo)
75 (signal 'restart-condition))
78 (defun test-restart (name)
83 (handler-bind ((restart-condition (lambda (c)
85 (restart-frame name))))
86 (assert (eql (funcall name 1) 1))
87 (assert (eql *count* 2))))
88 ;; Check that the binding stack was correctly unwound.
89 (assert (eql *foo* 'x))))
91 (with-test (:name (:restart-frame :special) :fails-on :win32)
92 (test-restart 'restart/special))
94 (with-test (:name (:restart-frame :optional-special) :fails-on :win32)
95 (test-restart 'restart/optional-special))
97 (with-test (:name (:restart-frame :normal) :fails-on :win32)
98 (test-restart 'restart/normal))
101 ;;;; Test RETURN-FROM-FRAME with normal functions
103 (define-condition return-condition () ())
105 (defun return/special (*foo*)
108 (signal 'return-condition))
111 (defun return/optional-special (&optional (*foo* 1))
114 (signal 'return-condition))
117 (defun return/normal (foo)
120 (signal 'return-condition))
124 (signal 'return-condition))
126 (defun return/catch (foo)
131 (defun test-return (name)
135 (handler-bind ((return-condition (lambda (c)
137 (return-from-frame name 1 2 3 4))))
138 (assert (equal (multiple-value-list (funcall name 0))
140 ;; Check that the binding stack was correctly unwound.
141 (assert (eql *foo* 'x))))
143 (with-test (:name (:return-from-frame :special) :fails-on :win32)
144 (test-return 'return/special))
146 (with-test (:name (:return-from-frame :optional-special) :fails-on :win32)
147 (test-return 'return/optional-special))
149 (with-test (:name (:return-from-frame :normal) :fails-on :win32)
150 (test-return 'return/normal))
152 (defun throw-y () (throw 'y 'y))
154 ;; Check that *CURRENT-CATCH-BLOCK* was correctly restored.
155 (with-test (:name :current-catch-block-restored :fails-on :win32)
156 (assert (eql (catch 'y
157 (test-return 'return/catch)
162 ;;;; Test RETURN-FROM-FRAME with local functions
164 (define-condition in-a () ())
165 (define-condition in-b () ())
174 (setf *a* (multiple-value-list (a)))
175 (setf *b* (multiple-value-list (b)))))
177 (defun hairy-locals ()
179 (flet ((a (&optional *c*)
185 ;; Ensure that A and B actually appear in the backtrace; the
186 ;; compiler for some reason likes to optimize away single-use
187 ;; local functions with hairy lambda-lists even on high debug
190 (setf *b* (b :*c* :good))
192 (setf *a* (multiple-value-list (a :good)))
193 (setf *b* (multiple-value-list (b :*c* :good))))))
195 (defun test-locals (name)
196 (handler-bind ((in-a (lambda (c)
198 (return-from-frame `(flet a :in ,name) 'x 'y)))
201 (return-from-frame `(flet b :in ,name) 'z))))
203 ;; We're intentionally not testing for returning a different amount
204 ;; of values than the local functions are normally returning. It's
205 ;; hard to think of practical cases where that'd be useful, but
206 ;; allowing it (as in the old fully CATCH-based implementation of
207 ;; UNWIND-TO-FRAME-AND-CALL) will make it harder for the compiler to
211 (assert (equal *a* '(x y)))
212 (assert (equal *b* '(z))))
213 (assert (eql *foo* 'x))))
215 (with-test (:name (:return-from-frame :local-function) :fails-on :win32)
216 (test-locals 'locals))
218 (with-test (:name (:return-from-frame :hairy-local-function) :fails-on :win32)
219 (test-locals 'hairy-locals))
222 ;;;; Test RETURN-FROM-FRAME with anonymous functions
224 (define-condition anon-condition () ())
226 (defparameter *anon-1*
228 (signal 'anon-condition)
231 (defparameter *anon-2*
233 (signal 'anon-condition)
236 (defun make-anon-3 ()
237 (let ((a (lambda (foo)
238 (signal 'anon-condition)
243 (defun make-anon-4 ()
244 (let ((a (lambda (*foo*)
245 (signal 'anon-condition)
250 (defparameter *anon-3* (make-anon-3))
251 (defparameter *anon-4* (make-anon-4))
253 (defun test-anon (fun var-name &optional in)
254 (handler-bind ((anon-condition (lambda (c)
257 `(lambda (,var-name) ,@(when in `(:in ,in)))
261 (assert (equal (multiple-value-list (funcall fun 1))
263 (assert (eql *foo* 'y)))
264 (assert (eql *foo* 'x)))))
266 (with-test (:name (:return-from-frame :anonymous :toplevel) :fails-on :win32)
267 (test-anon *anon-1* 'foo (namestring *load-truename*)))
269 (with-test (:name (:return-from-frame :anonymous :toplevel-special)
271 (test-anon *anon-2* '*foo* (namestring *load-truename*)))
273 (with-test (:name (:return-from-frame :anonymous) :fails-on :win32)
274 (test-anon *anon-3* 'foo 'make-anon-3))
276 (with-test (:name (:return-from-frame :anonymous :special) :fails-on :win32)
277 (test-anon *anon-4* '*foo* 'make-anon-4))
280 ;;;; Test that unwind cleanups are executed
282 (defvar *unwind-state* nil)
283 (defvar *signal* nil)
288 (signal 'return-condition))
289 (push :unwind-1 *unwind-state*)))
294 (push :unwind-2 *unwind-state*)))
296 (defun test-unwind (fun wanted)
297 (handler-bind ((return-condition (lambda (c)
299 (return-from-frame fun
301 (dolist (*signal* (list nil t))
303 (*unwind-state* nil))
306 (assert (equal (multiple-value-list (funcall fun))
309 (assert (equal *unwind-state* wanted))
310 (assert (eql *foo* 'y)))
311 (assert (eql *foo* 'x))))))
313 (with-test (:name :test-unwind-1 :fails-on :win32)
314 (test-unwind 'unwind-1 '(:unwind-1)))
315 (with-test (:name :test-unwind-2 :fails-on :win32)
316 (test-unwind 'unwind-2 '(:unwind-2 :unwind-1)))
318 ;;; Regression in 1.0.10.47 reported by James Knight
323 (declaim (inline inline-fun))
324 (defun inline-fun (tla)
328 (defun foo (predicate)
329 (funcall predicate 2))
332 (let ((blah (foo #'inline-fun)))
335 (with-test (:name (:debug-instrumentation :inline/xep))