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