]> gitweb.factorcode.org Git - factor.git/blob - extra/tools/walker/walker.factor
4d1a4da6b13194870856240ac82a156db2f06863
[factor.git] / extra / 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 arrays accessors
7 generic generic.standard ;
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         [ thread-variables walker-status swap at ]
26         [ thread-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 over set-continuation-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) ? (step-into-quot) ;
68
69 : (step-into-dispatch) 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 primitive? ] [ execute break ] }
76         { [ t ] [ word-def (step-into-quot) ] }
77     } cond ;
78
79 \ (step-into-execute) t "step-into?" set-word-prop
80
81 : (step-into-continuation)
82     continuation callstack >>call break ;
83
84 ! Messages sent to walker thread
85 SYMBOL: step
86 SYMBOL: step-out
87 SYMBOL: step-into
88 SYMBOL: step-all
89 SYMBOL: step-into-all
90 SYMBOL: step-back
91 SYMBOL: detach
92 SYMBOL: abandon
93 SYMBOL: call-in
94
95 SYMBOL: +running+
96 SYMBOL: +suspended+
97 SYMBOL: +stopped+
98
99 : change-frame ( continuation quot -- continuation' )
100     #! Applies quot to innermost call frame of the
101     #! continuation.
102     >r clone r> [
103         >r clone r>
104         [
105             >r
106             [ innermost-frame-scan 1+ ]
107             [ innermost-frame-quot ] bi
108             r> call
109         ]
110         [ drop set-innermost-frame-quot ]
111         [ drop ]
112         2tri
113     ] curry change-call ; inline
114
115 : step-msg ( continuation -- continuation' )
116     [
117         2dup nth \ break = [
118             nip
119         ] [
120             swap 1+ cut [ break ] swap 3append
121         ] if
122     ] change-frame ;
123
124 : step-out-msg ( continuation -- continuation' )
125     [ nip \ break suffix ] change-frame ;
126
127 {
128     { call [ (step-into-quot) ] }
129     { (throw) [ drop (step-into-quot) ] }
130     { execute [ (step-into-execute) ] }
131     { if [ (step-into-if) ] }
132     { dispatch [ (step-into-dispatch) ] }
133     { continuation [ (step-into-continuation) ] }
134 } [ "step-into" set-word-prop ] assoc-each
135
136 {
137     >n ndrop >c c>
138     continue continue-with
139     stop yield suspend sleep (spawn)
140     suspend
141 } [
142     dup [ execute break ] curry
143     "step-into" set-word-prop
144 ] each
145
146 \ break [ break ] "step-into" set-word-prop
147
148 : step-into-msg ( continuation -- continuation' )
149     [
150         swap cut [
151             swap % unclip {
152                 { [ dup \ break eq? ] [ , ] }
153                 { [ dup quotation? ] [ add-breakpoint , \ break , ] }
154                 { [ dup array? ] [ add-breakpoint , \ break , ] }
155                 { [ dup word? ] [ literalize , \ (step-into-execute) , ] }
156                 { [ t ] [ , \ break , ] }
157             } cond %
158         ] [ ] make
159     ] change-frame ;
160
161 : status ( -- symbol )
162     walker-status tget model-value ;
163
164 : set-status ( symbol -- )
165     walker-status tget set-model ;
166
167 : keep-running ( -- )
168     +running+ set-status ;
169
170 : walker-stopped ( -- )
171     +stopped+ set-status
172     [ status +stopped+ eq? ]
173     [ [ drop f ] handle-synchronous ]
174     [ ] while ;
175
176 : step-into-all-loop ( -- )
177     +running+ set-status
178     [ status +running+ eq? ] [
179         [
180             {
181                 { step [ f ] }
182                 { step-out [ f ] }
183                 { step-into [ f ] }
184                 { step-all [ f ] }
185                 { step-into-all [ f ] }
186                 { step-back [ f ] }
187                 { f [ +stopped+ set-status f ] }
188                 [
189                     [ walker-continuation tget set-model ]
190                     [ step-into-msg ] bi
191                 ]
192             } case
193         ] handle-synchronous
194     ] [ ] while ;
195
196 : step-back-msg ( continuation -- continuation' )
197     walker-history tget
198     [ pop* ]
199     [ dup empty? [ drop ] [ nip pop ] if ] bi ;
200
201 : walker-suspended ( continuation -- continuation' )
202     +suspended+ set-status
203     [ status +suspended+ eq? ] [
204         dup walker-history tget push
205         dup walker-continuation tget set-model
206         [
207             {
208                 ! These are sent by the walker tool. We reply
209                 ! and keep cycling.
210                 { step [ step-msg keep-running ] }
211                 { step-out [ step-out-msg keep-running ] }
212                 { step-into [ step-into-msg keep-running ] }
213                 { step-all [ keep-running ] }
214                 { step-into-all [ step-into-all-loop ] }
215                 { abandon [ drop f keep-running ] }
216                 ! Pass quotation to debugged thread
217                 { call-in [ nip keep-running ] }
218                 ! Pass previous continuation to debugged thread
219                 { step-back [ step-back-msg ] }
220             } case f
221         ] handle-synchronous
222     ] [ ] while ;
223
224 : walker-loop ( -- )
225     +running+ set-status
226     [ status +stopped+ eq? not ] [
227         [
228             {
229                 ! ignore these commands while the thread is
230                 ! running
231                 { step [ f ] }
232                 { step-out [ f ] }
233                 { step-into [ f ] }
234                 { step-all [ f ] }
235                 { step-into-all [ step-into-all-loop f ] }
236                 { step-back [ f ] }
237                 { abandon [ f ] }
238                 { f [ walker-stopped f ] }
239                 ! thread hit a breakpoint and sent us the
240                 ! continuation, so we modify it and send it
241                 ! back.
242                 [ walker-suspended ]
243             } case
244         ] handle-synchronous
245     ] [ ] while ;
246
247 : associate-thread ( walker -- )
248     walker-thread tset
249     [ f walker-thread tget send-synchronous drop ]
250     self set-thread-exit-handler ;
251
252 : start-walker-thread ( status continuation -- thread' )
253     self [
254         walking-thread tset
255         walker-continuation tset
256         walker-status tset
257         V{ } clone walker-history tset
258         walker-loop
259     ] 3curry
260     "Walker on " self thread-name append spawn
261     [ associate-thread ] keep ;