]> gitweb.factorcode.org Git - factor.git/blob - basis/tools/walker/walker.factor
9775bdff81a057b3ae8180dfb2e23e25af38b8d3
[factor.git] / basis / tools / walker / walker.factor
1 ! Copyright (C) 2004, 2008 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: threads kernel namespaces continuations combinators
4 sequences math namespaces.private continuations.private
5 concurrency.messaging quotations kernel.private words
6 sequences.private assocs models models.filter arrays accessors
7 generic generic.standard definitions make ;
8 IN: tools.walker
9
10 SYMBOL: show-walker-hook ! ( status continuation thread -- )
11
12 ! Thread local in thread being walked
13 SYMBOL: walker-thread
14
15 ! Thread local in walker thread
16 SYMBOL: walking-thread
17 SYMBOL: walker-status
18 SYMBOL: walker-continuation
19 SYMBOL: walker-history
20
21 DEFER: start-walker-thread
22
23 : get-walker-thread ( -- status continuation thread )
24     walker-thread tget [
25         [ variables>> walker-status swap at ]
26         [ variables>> walker-continuation swap at ]
27         [ ] tri
28     ] [
29         f <model>
30         f <model>
31         2dup start-walker-thread
32     ] if* ;
33
34 : show-walker ( -- thread )
35     get-walker-thread
36     [ show-walker-hook get call ] keep ;
37
38 : after-break ( object -- )
39     {
40         { [ dup continuation? ] [ (continue) ] }
41         { [ dup quotation? ] [ call ] }
42         { [ dup not ] [ "Single stepping abandoned" rethrow ] }
43     } cond ;
44
45 : break ( -- )
46     continuation callstack >>call
47     show-walker send-synchronous
48     after-break ;
49
50 \ break t "break?" set-word-prop
51
52 : walk ( quot -- quot' )
53     \ break prefix [ break rethrow ] recover ;
54
55 GENERIC: add-breakpoint ( quot -- quot' )
56
57 M: callable add-breakpoint
58     dup [ break ] head? [ \ break prefix ] unless ;
59
60 M: array add-breakpoint
61     [ add-breakpoint ] map ;
62
63 M: object add-breakpoint ;
64
65 : (step-into-quot) ( quot -- ) add-breakpoint call ;
66
67 : (step-into-if) ( true false ? -- ) ? (step-into-quot) ;
68
69 : (step-into-dispatch) ( array n -- ) nth (step-into-quot) ;
70
71 : (step-into-execute) ( word -- )
72     {
73         { [ dup "step-into" word-prop ] [ "step-into" word-prop call ] }
74         { [ dup standard-generic? ] [ effective-method (step-into-execute) ] }
75         { [ dup hook-generic? ] [ effective-method (step-into-execute) ] }
76         { [ dup uses \ suspend swap member? ] [ execute break ] }
77         { [ dup primitive? ] [ execute break ] }
78         [ def>> (step-into-quot) ]
79     } cond ;
80
81 \ (step-into-execute) t "step-into?" set-word-prop
82
83 : (step-into-continuation) ( -- )
84     continuation callstack >>call break ;
85
86 : (step-into-call-next-method) ( class generic -- )
87     next-method-quot (step-into-quot) ;
88
89 ! Messages sent to walker thread
90 SYMBOL: step
91 SYMBOL: step-out
92 SYMBOL: step-into
93 SYMBOL: step-all
94 SYMBOL: step-into-all
95 SYMBOL: step-back
96 SYMBOL: abandon
97 SYMBOL: call-in
98
99 SYMBOL: +running+
100 SYMBOL: +suspended+
101 SYMBOL: +stopped+
102
103 : change-frame ( continuation quot -- continuation' )
104     #! Applies quot to innermost call frame of the
105     #! continuation.
106     >r clone r> [
107         >r clone r>
108         [
109             >r
110             [ innermost-frame-scan 1+ ]
111             [ innermost-frame-quot ] bi
112             r> call
113         ]
114         [ drop set-innermost-frame-quot ]
115         [ drop ]
116         2tri
117     ] curry change-call ; inline
118
119 : step-msg ( continuation -- continuation' )
120     [
121         2dup nth \ break = [
122             nip
123         ] [
124             swap 1+ cut [ break ] swap 3append
125         ] if
126     ] change-frame ;
127
128 : step-out-msg ( continuation -- continuation' )
129     [ nip \ break suffix ] change-frame ;
130
131 {
132     { call [ (step-into-quot) ] }
133     { (throw) [ drop (step-into-quot) ] }
134     { execute [ (step-into-execute) ] }
135     { if [ (step-into-if) ] }
136     { dispatch [ (step-into-dispatch) ] }
137     { continuation [ (step-into-continuation) ] }
138     { (call-next-method) [ (step-into-call-next-method) ] }
139 } [ "step-into" set-word-prop ] assoc-each
140
141 {
142     >n ndrop >c c>
143     continue continue-with
144     stop suspend (spawn)
145 } [
146     dup [ execute break ] curry
147     "step-into" set-word-prop
148 ] each
149
150 \ break [ break ] "step-into" set-word-prop
151
152 : step-into-msg ( continuation -- continuation' )
153     [
154         swap cut [
155             swap % unclip {
156                 { [ dup \ break eq? ] [ , ] }
157                 { [ dup quotation? ] [ add-breakpoint , \ break , ] }
158                 { [ dup array? ] [ add-breakpoint , \ break , ] }
159                 { [ dup word? ] [ literalize , \ (step-into-execute) , ] }
160                 [ , \ break , ]
161             } cond %
162         ] [ ] make
163     ] change-frame ;
164
165 : status ( -- symbol )
166     walker-status tget value>> ;
167
168 : set-status ( symbol -- )
169     walker-status tget set-model ;
170
171 : keep-running ( -- )
172     +running+ set-status ;
173
174 : walker-stopped ( -- )
175     +stopped+ set-status ;
176
177 : step-into-all-loop ( -- )
178     +running+ set-status
179     [ status +running+ eq? ] [
180         [
181             {
182                 { step [ f ] }
183                 { step-out [ f ] }
184                 { step-into [ f ] }
185                 { step-all [ f ] }
186                 { step-into-all [ f ] }
187                 { step-back [ f ] }
188                 { f [ +stopped+ set-status f ] }
189                 [
190                     [ walker-continuation tget set-model ]
191                     [ step-into-msg ] bi
192                 ]
193             } case
194         ] handle-synchronous
195     ] [ ] while ;
196
197 : step-back-msg ( continuation -- continuation' )
198     walker-history tget
199     [ pop* ]
200     [ [ nip pop ] unless-empty ] bi ;
201
202 : walker-suspended ( continuation -- continuation' )
203     +suspended+ set-status
204     [ status +suspended+ eq? ] [
205         dup walker-history tget push
206         dup walker-continuation tget set-model
207         [
208             {
209                 ! These are sent by the walker tool. We reply
210                 ! and keep cycling.
211                 { step [ step-msg keep-running ] }
212                 { step-out [ step-out-msg keep-running ] }
213                 { step-into [ step-into-msg keep-running ] }
214                 { step-all [ keep-running ] }
215                 { step-into-all [ step-into-all-loop ] }
216                 { abandon [ drop f keep-running ] }
217                 ! Pass quotation to debugged thread
218                 { call-in [ nip keep-running ] }
219                 ! Pass previous continuation to debugged thread
220                 { step-back [ step-back-msg ] }
221             } case f
222         ] handle-synchronous
223     ] [ ] while ;
224
225 : walker-loop ( -- )
226     +running+ set-status
227     [ status +stopped+ eq? not ] [
228         [
229             {
230                 ! ignore these commands while the thread is
231                 ! running
232                 { step [ f ] }
233                 { step-out [ f ] }
234                 { step-into [ f ] }
235                 { step-all [ f ] }
236                 { step-into-all [ step-into-all-loop f ] }
237                 { step-back [ f ] }
238                 { abandon [ f ] }
239                 { f [ walker-stopped f ] }
240                 ! thread hit a breakpoint and sent us the
241                 ! continuation, so we modify it and send it
242                 ! back.
243                 [ walker-suspended ]
244             } case
245         ] handle-synchronous
246     ] [ ] while ;
247
248 : associate-thread ( walker -- )
249     walker-thread tset
250     [ f walker-thread tget send-synchronous drop ]
251     self (>>exit-handler) ;
252
253 : start-walker-thread ( status continuation -- thread' )
254     self [
255         walking-thread tset
256         walker-continuation tset
257         walker-status tset
258         V{ } clone walker-history tset
259         walker-loop
260     ] 3curry
261     "Walker on " self name>> append spawn
262     [ associate-thread ] keep ;
263
264 ! For convenience
265 IN: syntax
266
267 : B ( -- ) break ;