1 ;;;;------------------------------------------------------------------
3 ;;;; Copyright (C) 2001, 2003-2005,
4 ;;;; Department of Computer Science, University of Tromso, Norway.
6 ;;;; For distribution policy, see the accompanying file COPYING.
8 ;;;; Filename: restarts.lisp
10 ;;;; Author: Frode Vatvedt Fjeld <frodef@acm.org>
11 ;;;; Created at: Tue Oct 28 09:27:13 2003
13 ;;;; $Id: restarts.lisp,v 1.6 2005/05/05 20:51:51 ffjeld Exp $
15 ;;;;------------------------------------------------------------------
17 (require :muerte
/basic-macros
)
18 (provide :muerte
/restarts
)
22 (defmacro restart-bind
(restart-specs &body body
)
23 (if (null restart-specs
)
25 (let ((restart-spec (car restart-specs
))
26 (rest-specs (cdr restart-specs
)))
27 (destructuring-bind (name function
&key interactive-function
31 `(with-basic-restart (,name
,function
,interactive-function
32 ,test-function
,report-function
)
33 (restart-bind ,rest-specs
,@body
))))))
35 (defun dynamic-context->basic-restart
(context)
36 (assert (< (%run-time-context-slot nil
'stack-bottom
)
38 (%run-time-context-slot nil
'stack-top
)))
39 (assert (eq (load-global-constant restart-tag
)
40 (stack-frame-ref nil context
1 :lisp
)))
41 (let ((x (- (%run-time-context-slot nil
'stack-top
) context
)))
42 (assert (below x
#x1000000
))
43 (with-inline-assembly (:returns
:eax
)
44 (:compile-form
(:result-mode
:eax
) x
)
46 (:movb
#.
(movitz::tag
:basic-restart
) :al
))))
48 (defun basic-restart->dynamic-context
(basic-restart)
49 (check-type basic-restart basic-restart
)
50 (with-inline-assembly (:returns
:eax
)
51 (:compile-form
(:result-mode
:eax
) basic-restart
)
53 (:locally
(:movl
(:edi
(:edi-offset stack-top
)) :ecx
))
56 (:leal
(:eax
:ecx
) :eax
)))
58 (define-simple-typep (basic-restart basic-restart-p
) (x)
59 (with-inline-assembly (:returns
:boolean-zf
=1)
60 (:compile-form
(:result-mode
:eax
) x
)
61 (:cmpb
#.
(movitz::tag
:basic-restart
) :al
)
64 (:locally
(:movl
(:edi
(:edi-offset stack-top
)) :ecx
))
65 (:locally
(:movl
(:edi
(:edi-offset dynamic-env
)) :ebx
))
70 (:globally
(:movl
(:edi
(:edi-offset restart-tag
)) :ebx
))
72 (:locally
(:movl
(:edi
(:edi-offset stack-top
)) :ecx
))
73 (:cmpl
:ebx
(:eax
:ecx
4))
76 (defun restart-name (restart)
79 (stack-frame-ref nil
(basic-restart->dynamic-context restart
)
82 (defun restart-function (restart)
85 (stack-frame-ref nil
(basic-restart->dynamic-context restart
)
88 (defun restart-interactive-function (restart)
91 (stack-frame-ref nil
(basic-restart->dynamic-context restart
)
94 (defun restart-test (restart)
97 (stack-frame-ref nil
(basic-restart->dynamic-context restart
)
100 (defun restart-format-control (restart)
103 (stack-frame-ref nil
(basic-restart->dynamic-context restart
)
106 (defun restart-args (restart)
109 (stack-frame-ref nil
(basic-restart->dynamic-context restart
)
112 (defun invoke-restart (restart-designator &rest arguments
)
113 (declare (dynamic-extent arguments
))
114 (let ((restart (find-restart restart-designator
)))
117 (let ((function (restart-function restart
)))
120 (apply function arguments
))
122 (exact-throw (basic-restart->dynamic-context restart
)
124 ((with-simple-restart)
125 (values nil t
))))))))
126 (t (error 'control-error
127 :format-control
"Can't invoke invalid restart ~S."
128 :format-arguments
(list restart-designator
))))))
130 (defun invoke-restart-interactively (restart-designator)
131 (let ((restart (find-restart restart-designator
)))
134 (let ((interactive-function (restart-interactive-function restart
)))
135 (etypecase interactive-function
137 (apply 'invoke-restart restart
(funcall interactive-function
)))
139 (invoke-restart restart
)))))
140 (t (error 'control-error
141 :format-control
"Can't interactively invoke invalid restart ~S."
142 :format-arguments
(list restart-designator
))))))
144 (defun find-restart-from-context (specifier context
&optional condition
)
145 (declare (ignore condition
))
150 (with-each-dynamic-context (context)
151 ((:basic-restart context name
)
152 (when (eq name specifier
)
153 (return (dynamic-context->basic-restart context
))))))))
155 (defun find-restart (specifier &optional condition
)
156 (find-restart-from-context specifier
(current-dynamic-context) condition
))
158 (defun find-restart-by-index (index &optional
(context (current-dynamic-context)))
160 (with-each-dynamic-context (context)
161 ((:basic-restart context
)
162 (when (= counter index
)
163 (return (dynamic-context->basic-restart context
)))
166 (defun compute-restarts (&optional condition
)
167 (declare (ignore condition
))
168 (let (computed-restarts)
169 (with-each-dynamic-context ()
170 ((:basic-restart context name
)
171 (declare (ignore name
))
172 (push (dynamic-context->basic-restart context
)
174 (nreverse computed-restarts
)))
176 (defun map-active-restarts (&optional function
(context (current-dynamic-context)))
177 "Map function over each active restart, ordered by the innermost one first.
178 The function is called with two arguments: the restart and the index.
179 Returns the number of restarts."
181 (with-each-dynamic-context (context)
182 ((:basic-restart context
)
184 (funcall function
(dynamic-context->basic-restart context
) index
))
188 (defmethod print-object ((restart restart
) stream
)
191 (print-unreadable-object (restart stream
:type nil
:identity t
)
192 (format stream
"Restart ~W" (restart-name restart
))))
193 ((not *print-escape
*)
196 (if (not (restart-format-control restart
))
197 (write (restart-name restart
) :stream stream
)
198 (apply 'format stream
199 (restart-format-control restart
)
200 (restart-args restart
)))))))
205 (defun abort (&optional condition
)
206 (let ((r (find-restart 'abort condition
)))
207 (when r
(invoke-restart r
))
208 (formatted-error 'control-error
"Abort failed.")))
211 (let ((r (find-restart 'continue
)))
213 (invoke-restart r
))))
215 (defun muffle-warning (&optional condition
)
216 (let ((r (find-restart 'muffle-warning condition
)))
217 (when r
(invoke-restart r
))
218 (formatted-error 'control-error
"Muffle-warning failed.")))