1.0.15.7: threaded BIND and UNBIND improvements on x86
[sbcl/tcr.git] / tests / win32-foreign-stack-unwind.impure.lisp
blob2d78069b8a136dafb60736dd14d337b5ef1e5d9c
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
4 ;;;; more information.
5 ;;;;
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
8 ;;;; from CMU CL.
9 ;;;;
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 (quit :unix-status 104) ;; This is extremely win32-specific.
16 (use-package :sb-alien)
18 ;;; Callbacks are not part of the exported interface yet -- when they are this can
19 ;;; go away.
20 (import 'sb-alien::alien-lambda)
22 (defun run-compiler ()
23 (let ((proc (run-program "gcc" '("win32-stack-unwind.c"
24 "-mno-cygwin" "-shared"
25 "-o" "win32-stack-unwind.dll")
26 :search t)))
27 (unless (zerop (process-exit-code proc))
28 (error "Bad exit code: ~S"
29 (process-exit-code proc)))))
31 (run-compiler)
33 (load-shared-object "win32-stack-unwind.dll")
36 (defvar *current-test-callback*)
38 (defparameter *test-callback-thunk*
39 (sb-alien::alien-callback
40 (function void)
41 #'(lambda () (funcall *current-test-callback*))))
43 (defun establish-return-frame (callback)
44 "Establish an SEH frame for use as a target with PERFORM-TEST-UNWIND and invoke CALLBACK via FUNCALL"
45 ;; We don't use a LET here because we don't want to accidentally
46 ;; correct a blown binding stack pointer just yet.
47 (setf *current-test-callback* callback)
48 (alien-funcall (extern-alien "establish_return_frame"
49 (function void (* (function void))))
50 (alien-sap *test-callback-thunk*))
51 (makunbound '*current-test-callback*)
52 (values))
54 (defun perform-test-unwind ()
55 "Perform an RtlUnwind to the surrounding ESTABLISH-RETURN-FRAME frame."
56 (alien-funcall (extern-alien "perform_test_unwind" (function void))))
59 ;;; An attempt to detect and clean up latent fatalities in the
60 ;;; post-test environent.
62 (defmacro with-test-environment (args &body body)
63 (declare (ignore args))
64 (let ((old-bsp (gensym))
65 (old-cuwp (gensym))
66 (old-ccb (gensym))
67 (old-asp (gensym)))
68 `(let ((*standard-input* *standard-input*))
69 (let ((,old-bsp (+ sb-vm::*binding-stack-pointer* 2))
70 (,old-cuwp sb-vm::*current-unwind-protect-block*)
71 (,old-ccb sb-vm:*current-catch-block*)
72 (,old-asp sb-vm::*alien-stack*))
73 (handler-case
74 (let ((result (progn ,@body))
75 extra-results)
76 (when (not (eql ,old-bsp sb-vm::*binding-stack-pointer*))
77 #+(or)
78 (format t "~A ~A~%" ,old-bsp sb-vm::*binding-stack-pointer*)
79 (push :bsp-fail extra-results))
80 (when (not (eql ,old-cuwp sb-vm::*current-unwind-protect-block*))
81 (push :cuwp-fail extra-results))
82 (when (not (eql ,old-ccb sb-vm:*current-catch-block*))
83 (push :ccb-fail extra-results))
84 (when (not (eql ,old-asp sb-vm::*alien-stack*))
85 (push :asp-fail extra-results))
86 (setf sb-vm::*current-unwind-protect-block* ,old-cuwp)
87 (setf sb-vm:*current-catch-block* ,old-ccb)
88 (setf sb-vm::*alien-stack* ,old-asp)
89 (list* result extra-results))
90 (error ()
91 :error))))))
94 ;;; Test cases.
96 (with-test (:name #1=:base-case)
97 ;; Tests that the unwind test machinery works.
98 (let ((result
99 (with-test-environment ()
100 (establish-return-frame (lambda () (perform-test-unwind)))
101 :success)))
102 (format t "~S result: ~S~%" #1# result)
103 (assert (eql :success (car result)))))
105 (with-test (:name #1=:special-binding)
106 ;; Tests that special bindings are undone properly during
107 ;; unwind.
108 (let ((result
109 (with-test-environment ()
110 (let ((foo :success))
111 (declare (special foo))
112 (establish-return-frame (lambda ()
113 (let ((foo nil))
114 (declare (special foo))
115 (perform-test-unwind))))
116 foo))))
117 (format t "~S result: ~S~%" #1# result)
118 (assert (eql :success (car result)))))
120 (with-test (:name #1=:unwind-protect)
121 ;; Tests that unwind-protect forms are run during unwind.
122 (let ((result
123 (with-test-environment ()
124 (let (result)
125 (establish-return-frame (lambda ()
126 (unwind-protect
127 (perform-test-unwind)
128 (setf result :success))))
129 result))))
130 (format t "~S result: ~S~%" #1# result)
131 (assert (eql :success (car result)))))
133 (with-test (:name #1=:unwind-protect-nlx)
134 ;; Tests that unwind-protect forms that are run during unwind
135 ;; can do a non-local exit to abort the unwind.
136 (let ((result
137 (with-test-environment ()
138 (let (result)
139 (establish-return-frame (lambda ()
140 (block nil
141 (unwind-protect
142 (perform-test-unwind)
143 (return)))
144 (setf result :success)))
145 result))))
146 (format t "~S result: ~S~%" #1# result)
147 (assert (eql :success (car result)))))
149 (with-test (:name #1=:no-unwind)
150 ;; Basic smoke test of establish-return-frame.
151 (let ((result
152 (with-test-environment ()
153 (establish-return-frame (lambda ()))
154 :success)))
155 (format t "~S result: ~S~%" #1# result)
156 (assert (eql :success (car result)))))
158 (with-test (:name #1=:no-unwind-error)
159 ;; Tests that EXCEPTION_BREAKPOINT is caught and handled
160 ;; correctly within callbacks.
161 (let ((result
162 (with-test-environment ()
163 (establish-return-frame (lambda ()
164 (handler-case
165 (some-undefined-function)
166 (undefined-function ()))))
167 :success)))
168 (format t "~S result: ~S~%" #1# result)
169 (assert (eql :success (car result)))))
171 (with-test (:name #1=:unwind-foreign-frame)
172 ;; Tests that unwinding a foreign SEH frame isn't completely
173 ;; broken.
174 (let ((result
175 (with-test-environment ()
176 (block nil
177 (establish-return-frame (lambda () (return :success)))))))
178 (format t "~S result: ~S~%" #1# result)
179 (assert (eql :success (car result)))))
181 (with-test (:name #1=:unwind-protect-unwind-foreign-frame)
182 ;; Tests that an unwind-protect block is allowed to unwind
183 ;; past the original unwind target.
184 (let ((result
185 (with-test-environment ()
186 (block nil
187 (establish-return-frame (lambda ()
188 (unwind-protect
189 (perform-test-unwind)
190 (return :success))))))))
191 (format t "~S result: ~S~%" #1# result)
192 (assert (eql :success (car result)))))
194 (with-test (:name #1=:unwind-error)
195 ;; Another test for unwinding an SEH frame.
196 (let ((result
197 (with-test-environment ()
198 (handler-case
199 (establish-return-frame (lambda ()
200 (error "Foo!")))
201 (error ()
202 :success)))))
203 (format t "~S result: ~S~%" #1# result)
204 (assert (eql :success (car result)))))
206 ;;;; success!