rbx-trepanning 0.0.1-universal-rubinius

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. data/ChangeLog +376 -0
  2. data/LICENSE +25 -0
  3. data/NEWS +2 -0
  4. data/README.textile +28 -0
  5. data/Rakefile +165 -0
  6. data/THANKS +14 -0
  7. data/app/breakpoint.rb +218 -0
  8. data/app/breakpoint.rbc +3564 -0
  9. data/app/brkptmgr.rb +138 -0
  10. data/app/brkptmgr.rbc +2827 -0
  11. data/app/default.rb +61 -0
  12. data/app/default.rbc +1011 -0
  13. data/app/display.rb +35 -0
  14. data/app/display.rbc +968 -0
  15. data/app/frame.rb +98 -0
  16. data/app/frame.rbc +1808 -0
  17. data/app/irb.rb +112 -0
  18. data/app/irb.rbc +2111 -0
  19. data/app/iseq.rb +95 -0
  20. data/app/iseq.rbc +1801 -0
  21. data/app/method.rb +173 -0
  22. data/app/method.rbc +2492 -0
  23. data/app/mock.rb +13 -0
  24. data/app/mock.rbc +398 -0
  25. data/app/options.rb +123 -0
  26. data/app/options.rbc +2183 -0
  27. data/app/run.rb +86 -0
  28. data/app/run.rbc +1244 -0
  29. data/app/util.rb +49 -0
  30. data/app/util.rbc +1144 -0
  31. data/app/validate.rb +30 -0
  32. data/app/validate.rbc +676 -0
  33. data/bin/trepan.compiled.rbc +1043 -0
  34. data/bin/trepanx +63 -0
  35. data/bin/trepanx.compiled.rbc +985 -0
  36. data/interface/base_intf.rb +95 -0
  37. data/interface/base_intf.rbc +1742 -0
  38. data/interface/script.rb +104 -0
  39. data/interface/script.rbc +1642 -0
  40. data/interface/user.rb +91 -0
  41. data/interface/user.rbc +1418 -0
  42. data/io/base_io.rb +94 -0
  43. data/io/base_io.rbc +1404 -0
  44. data/io/input.rb +112 -0
  45. data/io/input.rbc +1979 -0
  46. data/io/null_output.rb +42 -0
  47. data/io/null_output.rbc +730 -0
  48. data/io/string_array.rb +156 -0
  49. data/io/string_array.rbc +2466 -0
  50. data/lib/trepanning.rb +398 -0
  51. data/lib/trepanning.rbc +6661 -0
  52. data/processor/breakpoint.rb +161 -0
  53. data/processor/command/alias.rb +55 -0
  54. data/processor/command/backtrace.rb +46 -0
  55. data/processor/command/base/cmd.rb +124 -0
  56. data/processor/command/base/subcmd.rb +213 -0
  57. data/processor/command/base/submgr.rb +179 -0
  58. data/processor/command/base/subsubcmd.rb +103 -0
  59. data/processor/command/base/subsubmgr.rb +184 -0
  60. data/processor/command/break.rb +100 -0
  61. data/processor/command/continue.rb +82 -0
  62. data/processor/command/delete.rb +30 -0
  63. data/processor/command/directory.rb +43 -0
  64. data/processor/command/disassemble.rb +103 -0
  65. data/processor/command/down.rb +54 -0
  66. data/processor/command/eval.rb +31 -0
  67. data/processor/command/exit.rb +58 -0
  68. data/processor/command/finish.rb +78 -0
  69. data/processor/command/frame.rb +89 -0
  70. data/processor/command/help.rb +146 -0
  71. data/processor/command/info.rb +28 -0
  72. data/processor/command/info_subcmd/breakpoints.rb +75 -0
  73. data/processor/command/info_subcmd/file.rb +153 -0
  74. data/processor/command/info_subcmd/method.rb +71 -0
  75. data/processor/command/info_subcmd/program.rb +59 -0
  76. data/processor/command/info_subcmd/variables.rb +40 -0
  77. data/processor/command/irb.rb +96 -0
  78. data/processor/command/kill.rb +70 -0
  79. data/processor/command/list.rb +296 -0
  80. data/processor/command/next.rb +66 -0
  81. data/processor/command/nexti.rb +59 -0
  82. data/processor/command/pr.rb +38 -0
  83. data/processor/command/ps.rb +40 -0
  84. data/processor/command/restart.rb +60 -0
  85. data/processor/command/set.rb +47 -0
  86. data/processor/command/set_subcmd/auto.rb +28 -0
  87. data/processor/command/set_subcmd/auto_subcmd/dis.rb +33 -0
  88. data/processor/command/set_subcmd/auto_subcmd/eval.rb +54 -0
  89. data/processor/command/set_subcmd/auto_subcmd/irb.rb +34 -0
  90. data/processor/command/set_subcmd/auto_subcmd/list.rb +34 -0
  91. data/processor/command/set_subcmd/basename.rb +26 -0
  92. data/processor/command/set_subcmd/debug.rb +27 -0
  93. data/processor/command/set_subcmd/debug_subcmd/dbgr.rb +36 -0
  94. data/processor/command/set_subcmd/debug_subcmd/skip.rb +23 -0
  95. data/processor/command/set_subcmd/debug_subcmd/step.rb +23 -0
  96. data/processor/command/set_subcmd/different.rb +60 -0
  97. data/processor/command/set_subcmd/hidelevel.rb +63 -0
  98. data/processor/command/set_subcmd/kernelstep.rb +61 -0
  99. data/processor/command/set_subcmd/max.rb +29 -0
  100. data/processor/command/set_subcmd/max_subcmd/list.rb +49 -0
  101. data/processor/command/set_subcmd/max_subcmd/stack.rb +50 -0
  102. data/processor/command/set_subcmd/max_subcmd/string.rb +54 -0
  103. data/processor/command/set_subcmd/max_subcmd/width.rb +49 -0
  104. data/processor/command/set_subcmd/substitute.rb +25 -0
  105. data/processor/command/set_subcmd/substitute_subcmd/path.rb +56 -0
  106. data/processor/command/set_subcmd/trace.rb +37 -0
  107. data/processor/command/set_subcmd/trace_subcmd/print.rb +57 -0
  108. data/processor/command/show.rb +27 -0
  109. data/processor/command/show_subcmd/alias.rb +43 -0
  110. data/processor/command/show_subcmd/args.rb +26 -0
  111. data/processor/command/show_subcmd/auto.rb +28 -0
  112. data/processor/command/show_subcmd/auto_subcmd/dis.rb +37 -0
  113. data/processor/command/show_subcmd/auto_subcmd/eval.rb +28 -0
  114. data/processor/command/show_subcmd/auto_subcmd/irb.rb +23 -0
  115. data/processor/command/show_subcmd/auto_subcmd/list.rb +22 -0
  116. data/processor/command/show_subcmd/basename.rb +22 -0
  117. data/processor/command/show_subcmd/debug.rb +27 -0
  118. data/processor/command/show_subcmd/debug_subcmd/dbgr.rb +21 -0
  119. data/processor/command/show_subcmd/debug_subcmd/skip.rb +22 -0
  120. data/processor/command/show_subcmd/debug_subcmd/step.rb +22 -0
  121. data/processor/command/show_subcmd/different.rb +27 -0
  122. data/processor/command/show_subcmd/hidelevel.rb +42 -0
  123. data/processor/command/show_subcmd/kernelstep.rb +37 -0
  124. data/processor/command/show_subcmd/max.rb +30 -0
  125. data/processor/command/show_subcmd/max_subcmd/list.rb +38 -0
  126. data/processor/command/show_subcmd/max_subcmd/stack.rb +36 -0
  127. data/processor/command/show_subcmd/max_subcmd/string.rb +42 -0
  128. data/processor/command/show_subcmd/max_subcmd/width.rb +37 -0
  129. data/processor/command/show_subcmd/trace.rb +29 -0
  130. data/processor/command/show_subcmd/trace_subcmd/print.rb +38 -0
  131. data/processor/command/source.rb +83 -0
  132. data/processor/command/step.rb +41 -0
  133. data/processor/command/tbreak.rb +19 -0
  134. data/processor/command/unalias.rb +44 -0
  135. data/processor/command/up.rb +87 -0
  136. data/processor/default.rb +56 -0
  137. data/processor/disassemble.rb +32 -0
  138. data/processor/eval.rb +96 -0
  139. data/processor/frame.rb +211 -0
  140. data/processor/help.rb +72 -0
  141. data/processor/hook.rb +133 -0
  142. data/processor/load_cmds.rb +101 -0
  143. data/processor/location.rb +128 -0
  144. data/processor/main.rb +394 -0
  145. data/processor/mock.rb +137 -0
  146. data/processor/msg.rb +28 -0
  147. data/processor/running.rb +230 -0
  148. data/processor/stepping.rb +115 -0
  149. data/processor/subcmd.rb +160 -0
  150. data/processor/validate.rb +355 -0
  151. data/test/data/enable.right +36 -0
  152. data/test/data/fname-with-blank.cmd +6 -0
  153. data/test/data/fname-with-blank.right +1 -0
  154. data/test/data/quit-Xdebug.right +3 -0
  155. data/test/data/quit.cmd +5 -0
  156. data/test/data/quit.right +0 -0
  157. data/test/example/fname with blank.rb +1 -0
  158. data/test/example/gcd-xx.rb +18 -0
  159. data/test/example/gcd.rb +19 -0
  160. data/test/example/gcd1.rb +24 -0
  161. data/test/example/null.rb +1 -0
  162. data/test/example/thread1.rb +3 -0
  163. data/test/functional/fn_helper.rb +112 -0
  164. data/test/functional/test-break-name.rb +52 -0
  165. data/test/functional/test-break.rb +51 -0
  166. data/test/functional/test-finish.rb +70 -0
  167. data/test/functional/test-fn_helper.rb +43 -0
  168. data/test/functional/test-list.rb +55 -0
  169. data/test/functional/test-next-bug.rb +49 -0
  170. data/test/functional/test-next.rb +101 -0
  171. data/test/functional/test-step.rb +272 -0
  172. data/test/functional/test-step2.rb +35 -0
  173. data/test/functional/test-tbreak.rb +41 -0
  174. data/test/integration/file-diff.rb +89 -0
  175. data/test/integration/helper.rb +78 -0
  176. data/test/integration/test-fname-with-blank.rb +12 -0
  177. data/test/integration/test-quit.rb +25 -0
  178. data/test/unit/cmd-helper.rb +46 -0
  179. data/test/unit/test-app-brkpt.rb +30 -0
  180. data/test/unit/test-app-brkptmgr.rb +51 -0
  181. data/test/unit/test-app-iseq.rb +49 -0
  182. data/test/unit/test-app-method.rb +54 -0
  183. data/test/unit/test-app-options.rb +61 -0
  184. data/test/unit/test-app-run.rb +16 -0
  185. data/test/unit/test-app-util.rb +28 -0
  186. data/test/unit/test-app-validate.rb +18 -0
  187. data/test/unit/test-base-subcmd.rb +61 -0
  188. data/test/unit/test-bin-trepanx.rb +48 -0
  189. data/test/unit/test-cmd-alias.rb +49 -0
  190. data/test/unit/test-cmd-break.rb +23 -0
  191. data/test/unit/test-cmd-exit.rb +27 -0
  192. data/test/unit/test-cmd-help.rb +101 -0
  193. data/test/unit/test-cmd-kill.rb +48 -0
  194. data/test/unit/test-intf-user.rb +46 -0
  195. data/test/unit/test-io-input.rb +27 -0
  196. data/test/unit/test-proc-eval.rb +37 -0
  197. data/test/unit/test-proc-frame.rb +79 -0
  198. data/test/unit/test-proc-help.rb +16 -0
  199. data/test/unit/test-proc-hook.rb +30 -0
  200. data/test/unit/test-proc-load_cmds.rb +41 -0
  201. data/test/unit/test-proc-location.rb +48 -0
  202. data/test/unit/test-proc-main.rb +96 -0
  203. data/test/unit/test-proc-validate.rb +91 -0
  204. data/test/unit/test-subcmd-help.rb +51 -0
  205. metadata +337 -0
@@ -0,0 +1,230 @@
1
+ # Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
2
+ require 'rubygems'; require 'require_relative'
3
+ require 'set'
4
+ ## require_relative '../app/core'
5
+ class Trepan
6
+ class CmdProcessor
7
+
8
+
9
+ attr_accessor :ignore_file_re # Hash[file_re] -> String
10
+ # action. File re's we don't want
11
+ # to stop while stepping. Like
12
+ # ignore_methods. Skipping kernel methods
13
+ # is handled this way.
14
+ attr_accessor :ignore_methods # Hash[CompiledMethod] -> String:
15
+ # action. Methods we don't want to
16
+ # ever stop while stepping. action
17
+ # is 'step' or 'next'.
18
+ attr_accessor :stop_condition # String or nil. When not nil
19
+ # this has to eval non-nil
20
+ # in order to stop.
21
+ attr_accessor :stop_events # Set or nil. If not nil, only
22
+ # events in this set will be
23
+ # considered for stopping. This is
24
+ # like core.step_events (which
25
+ # could be used instead), but it is
26
+ # a set of event names rather than
27
+ # a bitmask and it is intended to
28
+ # be more temporarily changed via
29
+ # "step>" or "step!" commands.
30
+ attr_accessor :step_count
31
+ attr_accessor :to_method
32
+
33
+ # # Does whatever needs to be done to set to continue program
34
+ # # execution.
35
+ # # FIXME: turn line_number into a condition.
36
+
37
+ def continue(how_to_continue)
38
+ @next_thread = nil
39
+ @step_count = -1 # No more event stepping
40
+ if 'step-finish' == how_to_continue
41
+ step_to_return_or_yield
42
+ how_to_continue = 'step'
43
+ end
44
+ @return_to_program = how_to_continue
45
+ end
46
+
47
+ # Does whatever setup needs to be done to set to ignore stepping
48
+ # to the finish of the current method. Elsewhere in
49
+ # "skipping_step?" we do the checking.
50
+ def finish(level_count=0, opts={})
51
+ step_to_return_or_yield
52
+ continue('finish')
53
+ @next_thread = @current_thread
54
+
55
+ # # Try high-speed (run-time-assisted) method
56
+ # @frame.trace_off = true # No more tracing in this frame
57
+ # @frame.return_stop = true # don't need to
58
+ end
59
+
60
+ def step_finish
61
+ step_to_return_or_yield
62
+ continue('step')
63
+ @next_thread = @current_thread
64
+ end
65
+
66
+ # # Does whatever needs to be done to set to "next" program
67
+ # # execution.
68
+ # def next(step_count=1, opts={})
69
+ # step(step_count, opts)
70
+ # @next_thread = Thread.current
71
+ # end
72
+
73
+ # Does whatever needs to be done to set to step program
74
+ # execution.
75
+ def step(how_to_continue, step_count=1, opts={}, condition=nil)
76
+ continue(how_to_continue)
77
+ @step_count = step_count
78
+ @different_pos = opts[:different_pos] if
79
+ opts.keys.member?(:different_pos)
80
+ @stop_condition = condition
81
+ @stop_events = opts[:stop_events] if
82
+ opts.keys.member?(:stop_events)
83
+ @to_method = opts[:to_method]
84
+ end
85
+
86
+ def quit(cmd='exit')
87
+ @next_level = 32000 # I'm guessing the stack size can't
88
+ # ever reach this
89
+ @next_thread = nil
90
+ @step_count = -1 # No more event stepping
91
+ @leave_cmd_loop = true # Break out of the processor command loop.
92
+ @settings[:autoirb] = false
93
+ @cmdloop_prehooks.delete_by_name('autoirb')
94
+ @commands['exit'].run([cmd])
95
+ end
96
+
97
+ def parse_next_step_suffix(step_cmd)
98
+ opts = {}
99
+ case step_cmd[-1..-1]
100
+ when '-'
101
+ opts[:different_pos] = false
102
+ when '+'
103
+ opts[:different_pos] = 'nostack'
104
+ when '='
105
+ opts[:different_pos] = true
106
+ when '!'
107
+ opts[:stop_events] = Set.new(%w(raise))
108
+ when '<'
109
+ opts[:stop_events] = Set.new(%w(c-return return))
110
+ when '>'
111
+ opts[:stop_events] = Set.new(%w(c-call call))
112
+ if step_cmd.size > 1 && step_cmd[-2..-2] == '<'
113
+ opts[:stop_events] = Set.new(%w(c-call c-return call return))
114
+ else
115
+ opts[:stop_events] = Set.new(%w(c-call call))
116
+ end
117
+ end
118
+ return opts
119
+ end
120
+
121
+ def running_initialize
122
+ @ignore_file_re = {}
123
+ @ignore_methods = {}
124
+
125
+ @step_count = 0
126
+ @stop_condition = nil
127
+ @stop_events = nil
128
+ @to_method = nil
129
+ end
130
+
131
+ # If we are not in some kind of steppable event, return
132
+ # false. If we are in a steppable event, update step state
133
+ # and return true if the step count is 0 and other conditions
134
+ # like the @settings[:different] are met.
135
+ def stepping_skip?
136
+
137
+ debug_loc = "#{frame.vm_location.describe} #{frame.line}" if
138
+ @settings[:debugskip]
139
+
140
+ if @step_count < 0
141
+ # We may eventually stop for some other reason, but it's not
142
+ # because we were stepping here.
143
+ msg "skip: step_count < 0 #{debug_loc}" if @settings[:debugskip]
144
+ return false
145
+ end
146
+
147
+ @ignore_file_re.each_pair do |file_re, action|
148
+ if frame.vm_location.method.active_path =~ file_re
149
+ @return_to_program = action
150
+ msg "skip re: #{debug_loc}" if @settings[:debugskip]
151
+ return true
152
+ end
153
+ end
154
+
155
+ ms = frame.method.scope
156
+ @ignore_methods.each do |m, val|
157
+ # Guard against crap put into @ignore_methods
158
+ unless m.respond_to?(:scope)
159
+ @ignore_methods.delete(m)
160
+ next
161
+ end
162
+ if ms == m.scope
163
+ msg "skip ignore method: #{debug_loc}" if @settings[:debugskip]
164
+ @return_to_program = val
165
+ return true
166
+ end
167
+ end
168
+
169
+ # Only skip on these kinds of events
170
+ unless %w(step-call line).include?(@event)
171
+ msg "skip non-line: #{@event} #{debug_loc}" if @settings[:debugskip]
172
+ return false
173
+ end
174
+
175
+ # We are in some kind of stepping event, so do whatever we
176
+ # do to record that we've hit the step. Whether we decide
177
+ # to stop, will be done after recording the step took place.
178
+ @step_count -= 1 unless step_count == 0
179
+ new_pos = [@frame.file, @frame.line,
180
+ @stack_size, @current_thread, @event]
181
+
182
+
183
+ # Decide whether this step is skippable.
184
+ should_skip = false
185
+
186
+ if @settings[:debugskip]
187
+ msg("last: #{@last_pos.inspect}, ")
188
+ msg("new: #{new_pos.inspect}")
189
+ msg("skip: #{should_skip.inspect}, event: #{@event}")
190
+ end
191
+
192
+ @last_pos[2] = new_pos[2] if 'nostack' == @different_pos
193
+ condition_met = @step_count == 0
194
+
195
+ # if @stop_condition
196
+ # puts 'stop_cond' if @settings[:'debugskip']
197
+ # debug_eval_no_errmsg(@stop_condition)
198
+ # elsif @to_method
199
+ # puts "method #{@frame.method} #{@to_method}" if
200
+ # @settings[:'debugskip']
201
+ # @frame.method == @to_method
202
+ # else
203
+ # puts 'uncond' if @settings[:'debugskip']
204
+ # true
205
+ # end
206
+
207
+ # msg("condition_met: #{condition_met}, last: #{@last_pos}, " +
208
+ # "new: #{new_pos}, different #{@different_pos.inspect}") if
209
+ # @settings[:'debugskip']
210
+
211
+ should_skip = ((@last_pos[0..3] == new_pos[0..3] && @different_pos) ||
212
+ !condition_met)
213
+
214
+ msg("should_skip: #{should_skip}, #{debug_loc}") if @settings[:debugskip]
215
+
216
+ @last_pos = new_pos
217
+
218
+ unless should_skip
219
+ # Set up the default values for the
220
+ # next time we consider step skipping.
221
+ @different_pos = @settings[:different]
222
+ # @stop_events = nil
223
+ end
224
+
225
+ @return_to_program = 'step' if should_skip && !@return_to_program
226
+ return should_skip
227
+ end
228
+
229
+ end
230
+ end
@@ -0,0 +1,115 @@
1
+ require 'rubygems'; require 'require_relative'
2
+ require_relative '../app/iseq'
3
+ class Trepan
4
+ class CmdProcessor
5
+ include Trepanning::ISeq
6
+
7
+ # It might be interesting to allow stepping within a parent frame
8
+ def step_over_by(step, frame=@top_frame)
9
+
10
+ f = frame
11
+
12
+ ip = -1
13
+
14
+ meth = f.method
15
+ possible_line = f.line + step
16
+ fin_ip = meth.first_ip_on_line_after(possible_line, f.next_ip)
17
+
18
+ if fin_ip <= -1
19
+ return step_to_parent('line')
20
+ end
21
+
22
+ set_breakpoints_between(meth, f.next_ip, fin_ip)
23
+ end
24
+
25
+ def step_to_return_or_yield
26
+ f = @frame
27
+ unless f
28
+ msg 'Unable to find frame to finish'
29
+ return
30
+ end
31
+
32
+ meth = f.method
33
+ ip = -1
34
+ fin_ip = meth.lines.last
35
+
36
+ set_breakpoints_on_return_between(meth, f.next_ip, fin_ip)
37
+ end
38
+
39
+ def step_to_parent(event='return')
40
+ f = parent_frame
41
+ unless f
42
+ errmsg "Unable to find parent frame to step to next"
43
+ return nil
44
+ end
45
+ meth = f.method
46
+ ip = f.ip
47
+
48
+ bp = Trepanning::Breakpoint.for_ip(meth, ip,
49
+ {:event => event, :temp => true})
50
+ bp.scoped!(parent_frame.scope)
51
+ bp.activate
52
+
53
+ return bp
54
+ end
55
+
56
+ # Sets temporary breakpoints in met between start_ip and fin_ip.
57
+ # We also set a temporary breakpoint in the caller.
58
+ def set_breakpoints_between(meth, start_ip, fin_ip)
59
+ opts = {:event => 'line', :temp => true}
60
+ ips = goto_between(meth, start_ip, fin_ip)
61
+ bps = []
62
+
63
+ if ips.kind_of? Fixnum
64
+ if ips == -1
65
+ errmsg "No place to step to"
66
+ return nil
67
+ elsif ips == -2
68
+ bps << step_to_parent(event='line')
69
+ ips = []
70
+ else
71
+ ips = [ips]
72
+ end
73
+ end
74
+
75
+ msg "temp ips: #{ips.inspect}" if settings[:debugstep]
76
+ ips.each do |ip|
77
+ bp = Trepanning::Breakpoint.for_ip(meth, ip, opts)
78
+ bp.scoped!(@frame.scope)
79
+ bp.activate
80
+ bps << bp
81
+ end
82
+ first_bp = bps[0]
83
+ bps[1..-1].each do |bp|
84
+ first_bp.related_with(bp)
85
+ end
86
+ return first_bp
87
+ end
88
+
89
+ def set_breakpoints_on_return_between(meth, start_ip, fin_ip)
90
+ ips = yield_or_return_between(meth, start_ip, fin_ip)
91
+ if ips.empty?
92
+ errmsg '"ret" or "yield_stack" opcode not found'
93
+ return []
94
+ end
95
+
96
+ bp1 = Trepanning::Breakpoint.for_ip(meth, ips[0],
97
+ { :event => 'return',
98
+ :temp => true})
99
+ bp1.scoped!(@frame.scope)
100
+ bp1.activate
101
+ result = [bp1]
102
+
103
+ 1.upto(ips.size-1) do |i|
104
+ bp2 = Trepanning::Breakpoint.for_ip(meth, ips[i],
105
+ { :event => 'return',
106
+ :temp => true})
107
+ bp2.scoped!(@frame.scope)
108
+ bp1.related_with(bp2)
109
+ bp2.activate
110
+ result << bp2
111
+ end
112
+ return result
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,160 @@
1
+ # Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
2
+ # gdb-like subcommand processing.
3
+
4
+ class Trepan
5
+ class Subcmd
6
+
7
+ attr_reader :subcmds
8
+ def initialize(cmd)
9
+ @cmd = cmd
10
+ @subcmds = {}
11
+ @cmdlist = []
12
+ end
13
+
14
+ # Find subcmd in self.subcmds
15
+ def lookup(subcmd_prefix, use_regexp=true)
16
+ compare = if use_regexp
17
+ lambda{|name| name.to_s =~ /^#{subcmd_prefix}/}
18
+ else
19
+ lambda{|name| 0 == name.to_s.index(subcmd_prefix)}
20
+ end
21
+ @subcmds.each do |subcmd_name, subcmd|
22
+ if compare.call(subcmd_name) &&
23
+ subcmd_prefix.size >= subcmd.class.const_get(:MIN_ABBREV)
24
+ return subcmd
25
+ end
26
+ end
27
+ return nil
28
+ end
29
+
30
+ # Show short help for a subcommand.
31
+ def short_help(subcmd_cb, subcmd_name, label=false)
32
+ entry = self.lookup(subcmd_name)
33
+ if entry
34
+ if label
35
+ prefix = entry.name
36
+ else
37
+ prefix = ''
38
+ end
39
+ if entry.respond_to?(:short_help)
40
+ prefix += ' -- ' if prefix
41
+ @proc.msg(prefix + entry.short_help)
42
+ end
43
+ else
44
+ @proc.undefined_subcmd("help", subcmd_name)
45
+ end
46
+ end
47
+
48
+ # Add subcmd to the available subcommands for this object.
49
+ # It will have the supplied docstring, and subcmd_cb will be called
50
+ # when we want to run the command. min_len is the minimum length
51
+ # allowed to abbreviate the command. in_list indicates with the
52
+ # show command will be run when giving a list of all sub commands
53
+ # of this object. Some commands have long output like "show commands"
54
+ # so we might not want to show that.
55
+ def add(subcmd_cb, subcmd_name=nil)
56
+ subcmd_name ||= subcmd_cb.name
57
+ @subcmds[subcmd_name.to_s] = subcmd_cb
58
+
59
+ # We keep a list of subcommands to assist command completion
60
+ @cmdlist << subcmd_name
61
+ end
62
+
63
+ # Run subcmd_name with args using obj for the environent
64
+ def run( subcmd_name, arg)
65
+ entry=lookup(subcmd_name)
66
+ if entry
67
+ entry['callback'].send(arg)
68
+ else
69
+ @proc.undefined_cmd(entry.__class__.name, subcmd_name)
70
+ end
71
+ end
72
+
73
+ # help for subcommands
74
+ # Note: format of help is compatible with ddd.
75
+ def help(*args)
76
+
77
+ msg args
78
+ subcmd_prefix = args[0]
79
+ if not subcmd_prefix or subcmd_prefix.size == 0
80
+ @proc.msg(self.doc)
81
+ @proc.msg("\nList of %s subcommands:\n" % [@name])
82
+ @list.each do |subcmd_name|
83
+ subcmd_helper(subcmd_name, obj, true, true)
84
+ end
85
+
86
+ entry = lookup(subcmd_prefix)
87
+ if entry and entry.respond_to? :help
88
+ entry.help(args)
89
+ else
90
+ @proc.errmsg("Unknown 'help %s' subcommand %s" %
91
+ [@name, subcmd_prefix])
92
+ end
93
+ end
94
+ end
95
+
96
+ def list
97
+ @subcmds.keys.sort
98
+ end
99
+
100
+ # Error message when a subcommand doesn't exist.
101
+ def undefined_subcmd(cmd, subcmd)
102
+ @proc.errmsg('Undefined "%s" command: "%s". Try "help".' %
103
+ [cmd, subcmd])
104
+ end
105
+ end
106
+ end
107
+
108
+ # When invoked as main program, invoke the debugger on a script
109
+ if __FILE__ == $0
110
+
111
+ require 'rubygems'; require 'require_relative'
112
+ require_relative 'mock'
113
+ require_relative 'command/base/cmd'
114
+
115
+ class Trepan::TestCommand < Trepan::Command
116
+
117
+ HELP = 'Help string string for testing'
118
+ CATEGORY = 'data'
119
+ MIN_ARGS = 0
120
+ MAX_ARGS = 5
121
+ NAME_ALIASES = %w(test)
122
+
123
+ def initialize(proc); @proc = proc end
124
+
125
+ def run(args); puts 'test command run' end
126
+ end
127
+
128
+ class TestTestingSubcommand
129
+ HELP = 'Help string for test testing subcommand'
130
+
131
+ def initialize; @name = 'testing' end
132
+
133
+ SHORT_HELP = 'This is short help for test testing'
134
+ MIN_ABREV = 4
135
+ IN_LIST = true
136
+ def run(args); puts 'test testing run' end
137
+ end
138
+
139
+ d = MockDebugger::MockDebugger.new
140
+ testcmd = Trepan::TestCommand.new(nil)
141
+ # testcmd.debugger = d
142
+ # testcmd.proc = d.core.processor
143
+ # testcmdMgr = Subcmd.new('test', testcmd)
144
+ # testsub = TestTestingSubcommand.new
145
+ # testcmdMgr.add(testsub)
146
+
147
+ # %w(tes test testing testing1).each do |prefix|
148
+ # x = testcmdMgr.lookup(prefix)
149
+ # puts x ? x.name : 'Non'
150
+ # end
151
+
152
+ # testcmdMgr.short_help(testcmd, 'testing')
153
+ # testcmdMgr.short_help(testcmd, 'test', true)
154
+ # testcmdMgr.short_help(testcmd, 'tes')
155
+ # puts testcmdMgr.list()
156
+ # testsub2 = TestTestingSubcommand.new
157
+ # testsub2.name = 'foobar'
158
+ # testcmdMgr.add(testsub2)
159
+ # puts testcmdMgr.list()
160
+ end