1 ;;;; Testing the behavior of foreign calls trying to unwind the stack. Uses win32-stack-unwind.c.
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; While most of SBCL is derived from the CMU CL system, the test
7 ;;;; files (like this one) were written from scratch after the fork
10 ;;;; This software is in the public domain and is provided with
11 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
12 ;;;; more information.
14 #-win32 (exit :code 104) ;; This is extremely win32-specific.
15 #-x86 (exit :code 104) ;; And our AMD64 backend does not aim to support it.
17 (use-package :sb-alien)
19 ;;; Callbacks are not part of the exported interface yet -- when they are this can
21 (import 'sb-alien::alien-lambda)
23 ;;; XXX XXX this should change to use run-compiler.sh, now that we have it
24 (defun run-compiler ()
25 (let ((proc (run-program "gcc" '("win32-stack-unwind.c"
26 "-mno-cygwin" "-shared"
27 "-o" "win32-stack-unwind.dll")
29 (unless (zerop (process-exit-code proc))
30 (error "Bad exit code: ~S"
31 (process-exit-code proc)))))
35 (load-shared-object (truename "win32-stack-unwind.dll"))
38 (defvar *current-test-callback*)
40 (defparameter *test-callback-thunk*
41 (sb-alien::alien-callback
43 #'(lambda () (funcall *current-test-callback*))))
45 (defun establish-return-frame (callback)
46 "Establish an SEH frame for use as a target with PERFORM-TEST-UNWIND and invoke CALLBACK via FUNCALL"
47 ;; We don't use a LET here because we don't want to accidentally
48 ;; correct a blown binding stack pointer just yet.
49 (setf *current-test-callback* callback)
50 (alien-funcall (extern-alien "establish_return_frame"
51 (function void (* (function void))))
52 (alien-sap *test-callback-thunk*))
53 (makunbound '*current-test-callback*)
56 (defun perform-test-unwind ()
57 "Perform an RtlUnwind to the surrounding ESTABLISH-RETURN-FRAME frame."
58 (alien-funcall (extern-alien "perform_test_unwind" (function void))))
61 ;;; An attempt to detect and clean up latent fatalities in the
62 ;;; post-test environent.
64 (defmacro with-test-environment (args &body body)
65 (declare (ignore args))
66 (let ((old-bsp (gensym))
70 `(let ((*standard-input* *standard-input*))
71 (let ((,old-bsp (+ sb-vm::*binding-stack-pointer* 2))
72 (,old-cuwp sb-vm::*current-unwind-protect-block*)
73 (,old-ccb sb-vm:*current-catch-block*)
74 (,old-asp sb-vm::*alien-stack*))
76 (let ((result (progn ,@body))
78 (when (not (eql ,old-bsp sb-vm::*binding-stack-pointer*))
80 (format t "~A ~A~%" ,old-bsp sb-vm::*binding-stack-pointer*)
81 (push :bsp-fail extra-results))
82 (when (not (eql ,old-cuwp sb-vm::*current-unwind-protect-block*))
83 (push :cuwp-fail extra-results))
84 (when (not (eql ,old-ccb sb-vm:*current-catch-block*))
85 (push :ccb-fail extra-results))
86 (when (not (eql ,old-asp sb-vm::*alien-stack*))
87 (push :asp-fail extra-results))
88 (setf sb-vm::*current-unwind-protect-block* ,old-cuwp)
89 (setf sb-vm:*current-catch-block* ,old-ccb)
90 (setf sb-vm::*alien-stack* ,old-asp)
91 (list* result extra-results))
98 (with-test (:name #1=:base-case)
99 ;; Tests that the unwind test machinery works.
101 (with-test-environment ()
102 (establish-return-frame (lambda () (perform-test-unwind)))
104 (format t "~S result: ~S~%" #1# result)
105 (assert (eql :success (car result)))))
107 (with-test (:name #1=:special-binding)
108 ;; Tests that special bindings are undone properly during
111 (with-test-environment ()
112 (let ((foo :success))
113 (declare (special foo))
114 (establish-return-frame (lambda ()
116 (declare (special foo))
117 (perform-test-unwind))))
119 (format t "~S result: ~S~%" #1# result)
120 (assert (eql :success (car result)))))
122 (with-test (:name #1=:unwind-protect)
123 ;; Tests that unwind-protect forms are run during unwind.
125 (with-test-environment ()
127 (establish-return-frame (lambda ()
129 (perform-test-unwind)
130 (setf result :success))))
132 (format t "~S result: ~S~%" #1# result)
133 (assert (eql :success (car result)))))
135 (with-test (:name #1=:unwind-protect-nlx)
136 ;; Tests that unwind-protect forms that are run during unwind
137 ;; can do a non-local exit to abort the unwind.
139 (with-test-environment ()
141 (establish-return-frame (lambda ()
144 (perform-test-unwind)
146 (setf result :success)))
148 (format t "~S result: ~S~%" #1# result)
149 (assert (eql :success (car result)))))
151 (with-test (:name #1=:no-unwind)
152 ;; Basic smoke test of establish-return-frame.
154 (with-test-environment ()
155 (establish-return-frame (lambda ()))
157 (format t "~S result: ~S~%" #1# result)
158 (assert (eql :success (car result)))))
160 (with-test (:name #1=:no-unwind-error)
161 ;; Tests that EXCEPTION_BREAKPOINT is caught and handled
162 ;; correctly within callbacks.
164 (with-test-environment ()
165 (establish-return-frame (lambda ()
167 (some-undefined-function)
168 (undefined-function ()))))
170 (format t "~S result: ~S~%" #1# result)
171 (assert (eql :success (car result)))))
173 (with-test (:name #1=:unwind-foreign-frame)
174 ;; Tests that unwinding a foreign SEH frame isn't completely
177 (with-test-environment ()
179 (establish-return-frame (lambda () (return :success)))))))
180 (format t "~S result: ~S~%" #1# result)
181 (assert (eql :success (car result)))))
183 (with-test (:name #1=:unwind-protect-unwind-foreign-frame)
184 ;; Tests that an unwind-protect block is allowed to unwind
185 ;; past the original unwind target.
187 (with-test-environment ()
189 (establish-return-frame (lambda ()
191 (perform-test-unwind)
192 (return :success))))))))
193 (format t "~S result: ~S~%" #1# result)
194 (assert (eql :success (car result)))))
196 (with-test (:name #1=:unwind-error)
197 ;; Another test for unwinding an SEH frame.
199 (with-test-environment ()
201 (establish-return-frame (lambda ()
205 (format t "~S result: ~S~%" #1# result)
206 (assert (eql :success (car result)))))