trepanning 0.1.2 → 0.1.3
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.
- data/ChangeLog +354 -0
- data/NEWS +21 -0
- data/Rakefile +27 -20
- data/app/cmd_parse.kpeg +20 -4
- data/app/cmd_parse.rb +11 -10
- data/app/cmd_parser.rb +119 -55
- data/app/complete.rb +1 -0
- data/app/core.rb +3 -3
- data/app/disassemble.rb +13 -3
- data/app/file.rb +2 -1
- data/app/frame.rb +3 -1
- data/app/mock.rb +3 -0
- data/app/options.rb +48 -31
- data/app/util.rb +50 -0
- data/interface/base_intf.rb +4 -0
- data/interface/client.rb +4 -0
- data/interface/script.rb +1 -1
- data/interface/server.rb +4 -0
- data/interface/user.rb +5 -0
- data/io/input.rb +3 -2
- data/io/null_output.rb +7 -1
- data/processor/breakpoint.rb +3 -2
- data/processor/command/base/subcmd.rb +1 -1
- data/processor/command/base/submgr.rb +4 -1
- data/processor/command/base/subsubcmd.rb +2 -2
- data/processor/command/base/subsubmgr.rb +1 -1
- data/processor/command/break.rb +7 -3
- data/processor/command/complete.rb +1 -0
- data/processor/command/continue.rb +1 -1
- data/processor/command/disassemble.rb +1 -1
- data/processor/command/edit.rb +35 -14
- data/processor/command/enable.rb +5 -3
- data/processor/command/eval.rb +35 -14
- data/processor/command/exit.rb +2 -0
- data/processor/command/help.rb +0 -9
- data/processor/command/help/command.txt +37 -27
- data/processor/command/help/examples.txt +16 -0
- data/processor/command/help/suffixes.txt +17 -0
- data/processor/command/info.rb +1 -1
- data/processor/command/info_subcmd/args.rb +7 -13
- data/processor/command/info_subcmd/breakpoints.rb +8 -2
- data/processor/command/info_subcmd/frame.rb +2 -0
- data/processor/command/info_subcmd/globals.rb +63 -0
- data/processor/command/info_subcmd/iseq.rb +3 -1
- data/processor/command/info_subcmd/locals.rb +16 -15
- data/processor/command/{show_subcmd → info_subcmd}/macro.rb +7 -7
- data/processor/command/info_subcmd/program.rb +2 -0
- data/processor/command/info_subcmd/registers.rb +5 -1
- data/processor/command/info_subcmd/registers_subcmd/dfp.rb +2 -3
- data/processor/command/info_subcmd/registers_subcmd/helper.rb +8 -9
- data/processor/command/info_subcmd/registers_subcmd/lfp.rb +10 -5
- data/processor/command/info_subcmd/registers_subcmd/pc.rb +9 -4
- data/processor/command/info_subcmd/registers_subcmd/sp.rb +4 -5
- data/processor/command/info_subcmd/ruby.rb +3 -1
- data/processor/command/info_subcmd/source.rb +78 -0
- data/processor/command/info_subcmd/stack.rb +23 -0
- data/processor/command/kill.rb +4 -6
- data/processor/command/list.rb +118 -120
- data/processor/command/macro.rb +1 -1
- data/processor/command/parsetree.rb +56 -0
- data/processor/command/pp.rb +40 -0
- data/processor/command/pr.rb +1 -2
- data/processor/command/quit.rb +2 -1
- data/processor/command/set_subcmd/abbrev.rb +24 -0
- data/processor/command/set_subcmd/auto_subcmd/eval.rb +1 -2
- data/processor/command/set_subcmd/auto_subcmd/irb.rb +2 -3
- data/processor/command/set_subcmd/auto_subcmd/list.rb +2 -3
- data/processor/command/set_subcmd/highlight.rb +8 -2
- data/processor/command/set_subcmd/reload.rb +41 -0
- data/processor/command/set_subcmd/timer.rb +8 -18
- data/processor/command/set_subcmd/trace.rb +2 -2
- data/processor/command/set_subcmd/trace_subcmd/buffer.rb +2 -2
- data/processor/command/set_subcmd/trace_subcmd/print.rb +3 -3
- data/processor/command/{irb.rb → shell.rb} +9 -6
- data/processor/command/show_subcmd/abbrev.rb +19 -0
- data/processor/command/show_subcmd/directories.rb +21 -0
- data/processor/command/show_subcmd/hidelevel.rb +1 -1
- data/processor/command/show_subcmd/highlight.rb +2 -1
- data/processor/command/show_subcmd/reload.rb +17 -0
- data/processor/command/show_subcmd/timer.rb +17 -0
- data/processor/command/show_subcmd/trace_subcmd/buffer.rb +1 -1
- data/processor/command/source.rb +15 -14
- data/processor/command/tbreak.rb +20 -0
- data/processor/command/watchg.rb +114 -0
- data/processor/default.rb +43 -41
- data/processor/display.rb +3 -2
- data/processor/eval.rb +5 -3
- data/processor/eventbuf.rb +3 -2
- data/processor/frame.rb +12 -3
- data/processor/hook.rb +3 -2
- data/processor/load_cmds.rb +186 -179
- data/processor/location.rb +154 -159
- data/processor/main.rb +44 -16
- data/processor/mock.rb +0 -11
- data/processor/msg.rb +3 -1
- data/processor/running.rb +3 -2
- data/processor/validate.rb +25 -4
- data/processor/virtual.rb +32 -0
- data/test/data/debugger-stop.right +1 -0
- data/test/data/fname-with-blank.right +1 -0
- data/test/example/gcd.rb +1 -0
- data/test/functional/{test-trace-var.rb → test-watchg.rb} +15 -4
- data/test/unit/cmd-helper.rb +0 -3
- data/test/unit/test-app-cmd_parser.rb +2 -2
- data/test/unit/test-app-file.rb +1 -0
- data/test/unit/test-app-frame.rb +1 -1
- data/test/unit/test-app-util.rb +21 -0
- data/test/unit/test-base-cmd.rb +4 -6
- data/test/unit/test-base-subcmd.rb +1 -4
- data/test/unit/test-base-submgr.rb +1 -2
- data/test/unit/test-base-subsubcmd.rb +0 -4
- data/test/unit/test-cmd-edit.rb +33 -0
- data/test/unit/test-cmd-parse_list_cmd.rb +33 -0
- data/test/unit/test-completion.rb +1 -1
- data/test/unit/test-proc-frame.rb +4 -1
- data/test/unit/test-proc-load_cmds.rb +2 -1
- data/test/unit/test-proc-location.rb +9 -26
- data/test/unit/test-proc-main.rb +1 -4
- data/test/unit/test-proc-validate.rb +28 -18
- data/test/unit/test-subcmd-help.rb +0 -4
- data/trepanning.gemspec +1 -1
- metadata +27 -10
- data/processor/command/set_subcmd/trace_subcmd/var.rb +0 -57
data/processor/hook.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
1
|
+
# Copyright (C) 2010, 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
require_relative 'virtual'
|
|
2
3
|
class Trepan
|
|
3
|
-
class CmdProcessor
|
|
4
|
+
class CmdProcessor < VirtualCmdProcessor
|
|
4
5
|
# Command processor hooks.
|
|
5
6
|
attr_reader :autoirb_hook
|
|
6
7
|
attr_reader :autolist_hook
|
data/processor/load_cmds.rb
CHANGED
|
@@ -6,208 +6,215 @@ require 'tmpdir'
|
|
|
6
6
|
# builtin and user directories.
|
|
7
7
|
# Sets @commands, @aliases, @macros
|
|
8
8
|
require_relative '../app/complete'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
9
|
+
require_relative 'virtual'
|
|
10
|
+
class Trepan::CmdProcessor < Trepan::VirtualCmdProcessor
|
|
11
|
+
|
|
12
|
+
attr_reader :aliases # Hash[String] of command names
|
|
13
|
+
# indexed by alias name
|
|
14
|
+
attr_reader :commands # Hash[String] of command objects
|
|
15
|
+
# indexed by name
|
|
16
|
+
attr_reader :macros # Hash[String] of Proc objects
|
|
17
|
+
# indexed by macro name.
|
|
18
|
+
attr_reader :leading_str # leading part of string. Used in
|
|
19
|
+
# command completion
|
|
20
|
+
|
|
21
|
+
# "initialize" for multi-file class. Called from main.rb's "initialize".
|
|
22
|
+
def load_cmds_initialize
|
|
23
|
+
@commands = {}
|
|
24
|
+
@aliases = {}
|
|
25
|
+
@macros = {}
|
|
26
|
+
|
|
27
|
+
cmd_dirs = [ File.join(File.dirname(__FILE__), 'command') ]
|
|
28
|
+
cmd_dirs << @settings[:user_cmd_dir] if @settings[:user_cmd_dir]
|
|
29
|
+
cmd_dirs.each do |cmd_dir|
|
|
30
|
+
load_debugger_commands(cmd_dir) if File.directory?(cmd_dir)
|
|
30
31
|
end
|
|
32
|
+
end
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
# 'require' would not be effective here
|
|
46
|
-
load file_or_dir
|
|
47
|
-
else
|
|
48
|
-
return false
|
|
49
|
-
end
|
|
50
|
-
Trepan::Command.constants.grep(/.Command$/).each do |command|
|
|
51
|
-
setup_command(command)
|
|
34
|
+
# Loads in debugger commands by require'ing each ruby file in the
|
|
35
|
+
# 'command' directory. Then a new instance of each class of the
|
|
36
|
+
# form Trepan::xxCommand is added to @commands and that array
|
|
37
|
+
# is returned.
|
|
38
|
+
def load_debugger_commands(file_or_dir)
|
|
39
|
+
if File.directory?(file_or_dir)
|
|
40
|
+
dir = File.expand_path(file_or_dir)
|
|
41
|
+
# change $0 so it doesn't get in the way of __FILE__ = $0
|
|
42
|
+
old_dollar0 = $0
|
|
43
|
+
$0 = ''
|
|
44
|
+
Dir.glob(File.join(dir, '*.rb')).each do |rb|
|
|
45
|
+
# We use require so that multiple calls have no effect.
|
|
46
|
+
require rb
|
|
52
47
|
end
|
|
53
|
-
|
|
48
|
+
$0 = old_dollar0
|
|
49
|
+
elsif File.readable?(file_or_dir)
|
|
50
|
+
# We use load in case we are reloading.
|
|
51
|
+
# 'require' would not be effective here
|
|
52
|
+
load file_or_dir
|
|
53
|
+
else
|
|
54
|
+
return false
|
|
55
|
+
end
|
|
56
|
+
# Instantiate each Command class found by the above require(s).
|
|
57
|
+
Trepan::Command.constants.grep(/.Command$/).each do |command|
|
|
58
|
+
setup_command(command)
|
|
54
59
|
end
|
|
60
|
+
return true
|
|
61
|
+
end
|
|
55
62
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
end
|
|
63
|
+
def load_debugger_command(command_file)
|
|
64
|
+
return unless File.readable?(command_file)
|
|
65
|
+
load command_file
|
|
66
|
+
Trepan::Command.constants.grep(/.Command$/).each do |command|
|
|
67
|
+
setup_command(command)
|
|
62
68
|
end
|
|
69
|
+
end
|
|
63
70
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
end
|
|
71
|
+
# Looks up cmd_array[0] in @commands and runs that. We do lots of
|
|
72
|
+
# validity testing on cmd_array.
|
|
73
|
+
def run_cmd(cmd_array)
|
|
74
|
+
unless cmd_array.is_a?(Array)
|
|
75
|
+
errmsg "run_cmd argument should be an Array, got: #{cmd_array.class}"
|
|
76
|
+
return
|
|
77
|
+
end
|
|
78
|
+
if cmd_array.detect{|item| !item.is_a?(String)}
|
|
79
|
+
errmsg "run_cmd argument Array should only contain strings. " +
|
|
80
|
+
"Got #{cmd_array.inspect}"
|
|
81
|
+
return
|
|
82
|
+
end
|
|
83
|
+
if cmd_array.empty?
|
|
84
|
+
errmsg "run_cmd Array should have at least one item. " +
|
|
85
|
+
"Got: #{cmd_array.inspect}"
|
|
86
|
+
return
|
|
87
|
+
end
|
|
88
|
+
cmd_name = cmd_array[0]
|
|
89
|
+
if @commands.member?(cmd_name)
|
|
90
|
+
@commands[cmd_name].run(cmd_array)
|
|
85
91
|
end
|
|
92
|
+
end
|
|
86
93
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
end
|
|
94
|
+
def save_commands(opts)
|
|
95
|
+
save_filename = opts[:filename] ||
|
|
96
|
+
File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname(['trepanning-save', '.txt'], nil))
|
|
97
|
+
begin
|
|
98
|
+
save_file = File.open(save_filename, 'w')
|
|
99
|
+
rescue => exc
|
|
100
|
+
errmsg("Can't open #{save_filename} for writing.")
|
|
101
|
+
errmsg("System reports: #{exc.inspect}")
|
|
102
|
+
return nil
|
|
103
|
+
end
|
|
104
|
+
save_file.puts "#\n# Commands to restore trepanning environment\n#\n"
|
|
105
|
+
@commands.each do |cmd_name, cmd_obj|
|
|
106
|
+
cmd_obj.save_command if cmd_obj.respond_to?(:save_command)
|
|
107
|
+
next unless cmd_obj.is_a?(Trepan::SubcommandMgr)
|
|
108
|
+
cmd_obj.subcmds.subcmds.each do |subcmd_name, subcmd_obj|
|
|
109
|
+
save_file.puts subcmd_obj.save_command if
|
|
110
|
+
subcmd_obj.respond_to?(:save_command)
|
|
111
|
+
next unless subcmd_obj.is_a?(Trepan::SubSubcommandMgr)
|
|
112
|
+
subcmd_obj.subcmds.subcmds.each do |subsubcmd_name, subsubcmd_obj|
|
|
113
|
+
save_file.puts subsubcmd_obj.save_command if
|
|
114
|
+
subsubcmd_obj.respond_to?(:save_command)
|
|
109
115
|
end
|
|
110
116
|
end
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
117
|
+
end
|
|
118
|
+
save_file.puts "!FileUtils.rm #{save_filename.inspect}" if
|
|
119
|
+
opts[:erase]
|
|
120
|
+
save_file.close
|
|
121
|
+
|
|
122
|
+
return save_filename
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Instantiate a Trepan::Command and extract info: the NAME, ALIASES
|
|
126
|
+
# and store the command in @commands.
|
|
127
|
+
def setup_command(command)
|
|
128
|
+
# Note: there is probably a non-eval way to instantiate the
|
|
129
|
+
# command, but I don't know it. And eval works.
|
|
130
|
+
klass = self.instance_eval("Trepan::Command::#{command}")
|
|
131
|
+
cmd = klass.send(:new, self)
|
|
132
|
+
|
|
133
|
+
# Add to list of commands and aliases.
|
|
134
|
+
cmd_name = klass.const_get(:NAME)
|
|
135
|
+
if klass.constants.member?(:ALIASES)
|
|
136
|
+
aliases= klass.const_get(:ALIASES)
|
|
137
|
+
aliases.each {|a| @aliases[a] = cmd_name}
|
|
138
|
+
end
|
|
139
|
+
@commands[cmd_name] = cmd
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Handle initial completion. We draw from the commands, aliases,
|
|
143
|
+
# and macros for completion. However we won't include aliases which
|
|
144
|
+
# are prefixes of other commands.
|
|
145
|
+
def complete(str, last_token)
|
|
146
|
+
@leading_str = str
|
|
147
|
+
next_blank_pos, token = Trepan::Complete.next_token(str, 0)
|
|
148
|
+
return [''] if token.empty? && !last_token.empty?
|
|
149
|
+
match_pairs = Trepan::Complete.complete_token_with_next(@commands,
|
|
150
|
+
token)
|
|
151
|
+
match_hash = {}
|
|
152
|
+
match_pairs.each do |pair|
|
|
153
|
+
match_hash[pair[0]] = pair[1]
|
|
154
|
+
end
|
|
155
|
+
alias_pairs = Trepan::Complete.
|
|
156
|
+
complete_token_filtered_with_next(@aliases, token, match_hash,
|
|
157
|
+
@commands)
|
|
158
|
+
match_pairs += alias_pairs
|
|
159
|
+
if str[next_blank_pos..-1].empty?
|
|
160
|
+
return match_pairs.map{|pair| pair[0]}.sort
|
|
161
|
+
else
|
|
162
|
+
alias_pairs.each do |pair|
|
|
145
163
|
match_hash[pair[0]] = pair[1]
|
|
146
164
|
end
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
165
|
+
end
|
|
166
|
+
if match_pairs.size > 1
|
|
167
|
+
# FIXME: figure out what to do here.
|
|
168
|
+
# Matched multiple items in the middle of the string
|
|
169
|
+
# We can't handle this so do nothing.
|
|
170
|
+
return []
|
|
171
|
+
# return match_pairs.map do |name, cmd|
|
|
172
|
+
# ["#{name} #{args[1..-1].join(' ')}"]
|
|
173
|
+
# end
|
|
174
|
+
end
|
|
175
|
+
# match_pairs.size == 1
|
|
176
|
+
next_complete(str, next_blank_pos, match_pairs[0][1], last_token)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def next_complete(str, next_blank_pos, cmd, last_token)
|
|
180
|
+
next_blank_pos, token = Trepan::Complete.next_token(str, next_blank_pos)
|
|
181
|
+
return [] if token.empty? && !last_token.empty?
|
|
182
|
+
|
|
183
|
+
if cmd.respond_to?(:complete_token_with_next)
|
|
184
|
+
match_pairs = cmd.complete_token_with_next(token)
|
|
185
|
+
return [] if match_pairs.empty?
|
|
186
|
+
if str[next_blank_pos..-1].rstrip.empty? &&
|
|
187
|
+
(token.empty? || token == last_token)
|
|
188
|
+
return match_pairs.map { |completion, junk| completion }
|
|
153
189
|
else
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
end
|
|
158
|
-
if match_pairs.size > 1
|
|
159
|
-
# FIXME: figure out what to do here.
|
|
160
|
-
# Matched multiple items in the middle of the string
|
|
161
|
-
# We can't handle this so do nothing.
|
|
162
|
-
return []
|
|
163
|
-
# return match_pairs.map do |name, cmd|
|
|
164
|
-
# ["#{name} #{args[1..-1].join(' ')}"]
|
|
165
|
-
# end
|
|
166
|
-
end
|
|
167
|
-
# match_pairs.size == 1
|
|
168
|
-
next_complete(str, next_blank_pos, match_pairs[0][1], last_token)
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
def next_complete(str, next_blank_pos, cmd, last_token)
|
|
172
|
-
# debugger if 8 == next_blank_pos
|
|
173
|
-
next_blank_pos, token = Trepan::Complete.next_token(str, next_blank_pos)
|
|
174
|
-
return [] if token.empty? && !last_token.empty?
|
|
175
|
-
|
|
176
|
-
if cmd.respond_to?(:complete_token_with_next)
|
|
177
|
-
match_pairs = cmd.complete_token_with_next(token)
|
|
178
|
-
return [] if match_pairs.empty?
|
|
179
|
-
if str[next_blank_pos..-1].rstrip.empty? &&
|
|
180
|
-
(token.empty? || token == last_token)
|
|
181
|
-
return match_pairs.map { |completion, junk| completion }
|
|
182
|
-
else
|
|
183
|
-
if match_pairs.size == 1
|
|
184
|
-
return next_complete(str, next_blank_pos, match_pairs[0][1],
|
|
185
|
-
last_token)
|
|
186
|
-
else
|
|
187
|
-
# FIXME: figure out what to do here.
|
|
188
|
-
# Matched multiple items in the middle of the string
|
|
189
|
-
# We can't handle this so do nothing.
|
|
190
|
-
return []
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
elsif cmd.respond_to?(:complete)
|
|
194
|
-
matches = cmd.complete(token)
|
|
195
|
-
return [] if matches.empty?
|
|
196
|
-
if str[next_blank_pos..-1].rstrip.empty? &&
|
|
197
|
-
(token.empty? || token == last_token)
|
|
198
|
-
return matches
|
|
190
|
+
if match_pairs.size == 1
|
|
191
|
+
return next_complete(str, next_blank_pos, match_pairs[0][1],
|
|
192
|
+
last_token)
|
|
199
193
|
else
|
|
200
194
|
# FIXME: figure out what to do here.
|
|
201
195
|
# Matched multiple items in the middle of the string
|
|
202
196
|
# We can't handle this so do nothing.
|
|
203
197
|
return []
|
|
204
198
|
end
|
|
199
|
+
end
|
|
200
|
+
elsif cmd.respond_to?(:complete)
|
|
201
|
+
matches = cmd.complete(token)
|
|
202
|
+
return [] if matches.empty?
|
|
203
|
+
if str[next_blank_pos..-1].rstrip.empty? &&
|
|
204
|
+
(token.empty? || token == last_token)
|
|
205
|
+
return matches
|
|
205
206
|
else
|
|
207
|
+
# FIXME: figure out what to do here.
|
|
208
|
+
# Matched multiple items in the middle of the string
|
|
209
|
+
# We can't handle this so do nothing.
|
|
206
210
|
return []
|
|
207
211
|
end
|
|
212
|
+
else
|
|
213
|
+
return []
|
|
208
214
|
end
|
|
209
215
|
end
|
|
210
216
|
end
|
|
217
|
+
|
|
211
218
|
if __FILE__ == $0
|
|
212
219
|
class Trepan::CmdProcessor
|
|
213
220
|
def initialize(core, settings={})
|
|
@@ -235,7 +242,7 @@ if __FILE__ == $0
|
|
|
235
242
|
cmdproc.run_cmd('foo') # Invalid - not an Array
|
|
236
243
|
cmdproc.run_cmd([]) # Invalid - empty Array
|
|
237
244
|
cmdproc.run_cmd(['list', 5]) # Invalid - nonstring arg
|
|
238
|
-
p cmdproc.complete("d")
|
|
239
|
-
p cmdproc.complete("sho d")
|
|
240
|
-
p cmdproc.complete('')
|
|
245
|
+
p cmdproc.complete("d", 'd')
|
|
246
|
+
p cmdproc.complete("sho d", 'd')
|
|
247
|
+
p cmdproc.complete('', '')
|
|
241
248
|
end
|
data/processor/location.rb
CHANGED
|
@@ -1,183 +1,180 @@
|
|
|
1
1
|
# Copyright (C) 2010, 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
require 'rubygems'
|
|
2
3
|
require 'linecache'
|
|
3
4
|
require 'pathname' # For cleanpath
|
|
4
5
|
require_relative 'msg'
|
|
5
6
|
require_relative '../app/frame'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
include Frame
|
|
7
|
+
require_relative 'virtual'
|
|
8
|
+
class Trepan::CmdProcessor < Trepan::VirtualCmdProcessor
|
|
9
|
+
include Trepan::Frame
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Return the text to the current source line.
|
|
27
|
-
# FIXME: loc_and_text should call this rather than the other
|
|
28
|
-
# way around.
|
|
29
|
-
def current_source_text
|
|
30
|
-
opts = {:reload_on_change => @reload_on_change}
|
|
31
|
-
junk1, junk2, text, found_line =
|
|
32
|
-
loc_and_text('', frame, frame.source_location[0],
|
|
33
|
-
frame.source_container, opts)
|
|
34
|
-
text
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def resolve_file_with_dir(path_suffix)
|
|
38
|
-
@settings[:directory].split(/:/).each do |dir|
|
|
39
|
-
dir =
|
|
40
|
-
if '$cwd' == dir
|
|
41
|
-
Dir.pwd
|
|
42
|
-
elsif '$cdir' == dir
|
|
43
|
-
RubyVM::OS_STARTUP_DIR
|
|
44
|
-
else
|
|
45
|
-
dir
|
|
46
|
-
end
|
|
47
|
-
next unless dir && File.directory?(dir)
|
|
48
|
-
try_file = File.join(dir, path_suffix)
|
|
49
|
-
return try_file if File.readable?(try_file)
|
|
50
|
-
end
|
|
51
|
-
nil
|
|
11
|
+
def canonic_container(container)
|
|
12
|
+
[container[0], canonic_file(container[1])]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def canonic_file(filename, resolve=true)
|
|
16
|
+
# For now we want resolved filenames
|
|
17
|
+
if @settings[:basename]
|
|
18
|
+
File.basename(filename)
|
|
19
|
+
elsif resolve
|
|
20
|
+
filename = LineCache::map_file(filename)
|
|
21
|
+
File.expand_path(Pathname.new(filename).cleanpath.to_s)
|
|
22
|
+
else
|
|
23
|
+
filename
|
|
52
24
|
end
|
|
25
|
+
end
|
|
53
26
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
27
|
+
# Return the text to the current source line.
|
|
28
|
+
# FIXME: loc_and_text should call this rather than the other
|
|
29
|
+
# way around.
|
|
30
|
+
def current_source_text
|
|
31
|
+
opts = {:reload_on_change => @reload}
|
|
32
|
+
junk1, junk2, text, found_line =
|
|
33
|
+
loc_and_text('', frame, frame.source_location[0],
|
|
34
|
+
frame.source_container, opts)
|
|
35
|
+
text
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def resolve_file_with_dir(path_suffix)
|
|
39
|
+
@settings[:directory].split(/:/).each do |dir|
|
|
40
|
+
dir =
|
|
41
|
+
if '$cwd' == dir
|
|
42
|
+
Dir.pwd
|
|
43
|
+
elsif '$cdir' == dir
|
|
44
|
+
RubyVM::OS_STARTUP_DIR
|
|
45
|
+
else
|
|
46
|
+
dir
|
|
71
47
|
end
|
|
72
|
-
|
|
73
|
-
|
|
48
|
+
next unless dir && File.directory?(dir)
|
|
49
|
+
try_file = File.join(dir, path_suffix)
|
|
50
|
+
return try_file if File.readable?(try_file)
|
|
74
51
|
end
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
line_no = frame.source_location[0]
|
|
95
|
-
filename = source_container[1]
|
|
96
|
-
loc += " via #{canonic_file(filename)}:#{line_no}"
|
|
97
|
-
text = line_at(filename, line_no, opts)
|
|
98
|
-
found_line = false
|
|
99
|
-
end
|
|
100
|
-
else
|
|
101
|
-
container = source_container[1]
|
|
102
|
-
map_file, map_line = LineCache::map_file_line(container, line_no)
|
|
103
|
-
if [container, line_no] != [map_file, map_line]
|
|
104
|
-
loc += " remapped #{canonic_file(map_file)}:#{map_line}"
|
|
52
|
+
nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Get line +line_number+ from file named +filename+. Return ''
|
|
56
|
+
# if there was a problem. Leading blanks are stripped off.
|
|
57
|
+
def line_at(filename, line_number,
|
|
58
|
+
opts = {
|
|
59
|
+
:reload_on_change => @settings[:reload],
|
|
60
|
+
:output => @settings[:highlight]
|
|
61
|
+
})
|
|
62
|
+
line = LineCache::getline(filename, line_number, opts)
|
|
63
|
+
|
|
64
|
+
unless line
|
|
65
|
+
# Try using search directories (set with command "directory")
|
|
66
|
+
if filename[0..0] != File::SEPARATOR
|
|
67
|
+
try_filename = resolve_file_with_dir(filename)
|
|
68
|
+
if try_filename &&
|
|
69
|
+
line = LineCache::getline(try_filename, line_number, opts)
|
|
70
|
+
LineCache::remap_file(filename, try_filename)
|
|
105
71
|
end
|
|
106
|
-
|
|
107
|
-
text = line_at(container, line_no, opts)
|
|
108
72
|
end
|
|
109
|
-
[loc, line_no, text, found_line]
|
|
110
73
|
end
|
|
74
|
+
line ? line.lstrip.chomp : line
|
|
75
|
+
end
|
|
111
76
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
source_container
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
else
|
|
129
|
-
(EVENT2ICON[@event] || @event)
|
|
130
|
-
end
|
|
131
|
-
@line_no = frame_line
|
|
132
|
-
|
|
133
|
-
loc = source_location_info(source_container, @line_no, @frame)
|
|
134
|
-
loc, @line_no, text, found_line =
|
|
135
|
-
loc_and_text(loc, @frame, @line_no, source_container)
|
|
136
|
-
|
|
137
|
-
ip_str = @frame.iseq ? " @#{frame.pc_offset}" : ''
|
|
138
|
-
msg "#{ev} (#{loc}#{ip_str})"
|
|
139
|
-
|
|
140
|
-
if %w(return c-return).member?(@event)
|
|
141
|
-
retval = Trepan::Frame.value_returned(@frame, @event)
|
|
142
|
-
msg 'R=> %s' % retval.inspect
|
|
77
|
+
def loc_and_text(loc, frame, line_no, source_container,
|
|
78
|
+
opts = {
|
|
79
|
+
:reload_on_change => @settings[:reload],
|
|
80
|
+
:output => @settings[:highlight]
|
|
81
|
+
})
|
|
82
|
+
found_line = true
|
|
83
|
+
## FIXME: condition is too long.
|
|
84
|
+
if source_container[0] == 'string' && frame.iseq && frame.iseq.eval_source
|
|
85
|
+
file = LineCache::map_iseq(frame.iseq)
|
|
86
|
+
text = LineCache::getline(frame.iseq, line_no, opts)
|
|
87
|
+
loc += " remapped #{canonic_file(file)}:#{line_no}"
|
|
88
|
+
elsif source_container[0] != 'file'
|
|
89
|
+
via = loc
|
|
90
|
+
while source_container[0] != 'file' && frame.prev do
|
|
91
|
+
frame = frame.prev
|
|
92
|
+
source_container = frame_container(frame, false)
|
|
143
93
|
end
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
94
|
+
if source_container[0] == 'file'
|
|
95
|
+
line_no = frame.source_location[0]
|
|
96
|
+
filename = source_container[1]
|
|
97
|
+
loc += " via #{canonic_file(filename)}:#{line_no}"
|
|
98
|
+
text = line_at(filename, line_no, opts)
|
|
99
|
+
found_line = false
|
|
148
100
|
end
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
101
|
+
else
|
|
102
|
+
container = source_container[1]
|
|
103
|
+
map_file, map_line = LineCache::map_file_line(container, line_no)
|
|
104
|
+
if [container, line_no] != [map_file, map_line]
|
|
105
|
+
loc += " remapped #{canonic_file(map_file)}:#{map_line}"
|
|
153
106
|
end
|
|
107
|
+
|
|
108
|
+
text = line_at(container, line_no, opts)
|
|
109
|
+
end
|
|
110
|
+
[loc, line_no, text, found_line]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def print_location
|
|
114
|
+
if %w(c-call call).member?(@event)
|
|
115
|
+
# FIXME: Fix Ruby so we don't need this workaround?
|
|
116
|
+
# See also where.rb
|
|
117
|
+
opts = {}
|
|
118
|
+
opts[:class] = @core.hook_arg if
|
|
119
|
+
'CFUNC' == @frame.type && @core.hook_arg && 0 == @frame_index
|
|
120
|
+
msg format_stack_call(@frame, opts)
|
|
121
|
+
elsif 'raise' == @event
|
|
122
|
+
msg @core.hook_arg.inspect if @core.hook_arg # Exception object
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
text = nil
|
|
126
|
+
source_container = frame_container(@frame, false)
|
|
127
|
+
ev = if @event.nil? || 0 != @frame_index
|
|
128
|
+
' '
|
|
129
|
+
else
|
|
130
|
+
(EVENT2ICON[@event] || @event)
|
|
131
|
+
end
|
|
132
|
+
@line_no = frame_line
|
|
133
|
+
|
|
134
|
+
loc = source_location_info(source_container, @line_no, @frame)
|
|
135
|
+
loc, @line_no, text, found_line =
|
|
136
|
+
loc_and_text(loc, @frame, @line_no, source_container)
|
|
137
|
+
|
|
138
|
+
ip_str = @frame.iseq ? " @#{frame.pc_offset}" : ''
|
|
139
|
+
msg "#{ev} (#{loc}#{ip_str})"
|
|
140
|
+
|
|
141
|
+
if %w(return c-return).member?(@event)
|
|
142
|
+
retval = Trepan::Frame.value_returned(@frame, @event)
|
|
143
|
+
msg 'R=> %s' % retval.inspect
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
if text && !text.strip.empty?
|
|
147
|
+
msg text
|
|
148
|
+
@line_no -= 1
|
|
149
|
+
end
|
|
150
|
+
unless found_line
|
|
151
|
+
# Can't find source line, so give assembly as consolation.
|
|
152
|
+
# This great idea comes from the Rubinius reference debugger.
|
|
153
|
+
run_command('disassemble')
|
|
154
154
|
end
|
|
155
|
-
|
|
156
|
-
def source_location_info(source_container, line_no, frame)
|
|
157
|
-
filename = source_container[1]
|
|
158
|
-
## FIXME: condition is too long.
|
|
159
|
-
canonic_filename =
|
|
160
|
-
if 'string' == source_container[0] && frame.iseq &&
|
|
161
|
-
frame.iseq.eval_source
|
|
162
|
-
eval_str = frame.iseq.eval_source
|
|
163
|
-
'eval "' + safe_repr(eval_str.gsub(/\n/,';'), 15) + '"'
|
|
164
|
-
else
|
|
165
|
-
canonic_file(filename)
|
|
166
|
-
end
|
|
167
|
-
loc = "#{canonic_filename}:#{line_no}"
|
|
168
|
-
return loc
|
|
169
|
-
end # source_location_info
|
|
170
|
-
|
|
171
155
|
end
|
|
156
|
+
|
|
157
|
+
def source_location_info(source_container, line_no, frame)
|
|
158
|
+
filename = source_container[1]
|
|
159
|
+
## FIXME: condition is too long.
|
|
160
|
+
canonic_filename =
|
|
161
|
+
if 'string' == source_container[0] && frame.iseq &&
|
|
162
|
+
frame.iseq.eval_source
|
|
163
|
+
eval_str = frame.iseq.eval_source
|
|
164
|
+
'eval "' + safe_repr(eval_str.gsub(/\n/,';'), 15) + '"'
|
|
165
|
+
else
|
|
166
|
+
canonic_file(filename, false)
|
|
167
|
+
end
|
|
168
|
+
loc = "#{canonic_filename}:#{line_no}"
|
|
169
|
+
return loc
|
|
170
|
+
end # source_location_info
|
|
172
171
|
end
|
|
173
172
|
|
|
174
|
-
if __FILE__ == $0 && caller.size == 0
|
|
173
|
+
if __FILE__ == $0 && caller.size == 0
|
|
175
174
|
# Demo it.
|
|
176
175
|
require 'thread_frame'
|
|
177
176
|
require_relative 'frame'
|
|
178
177
|
require_relative '../app/mock'
|
|
179
|
-
require_relative 'main' # Have to include before defining CmdProcessor!
|
|
180
|
-
# FIXME
|
|
181
178
|
class Trepan::CmdProcessor
|
|
182
179
|
def errmsg(msg)
|
|
183
180
|
puts msg
|
|
@@ -193,15 +190,13 @@ if __FILE__ == $0 && caller.size == 0 && ARGV.size > 0
|
|
|
193
190
|
proc.frame_setup(RubyVM::ThreadFrame.current)
|
|
194
191
|
proc.frame_initialize
|
|
195
192
|
|
|
196
|
-
proc.location_initialize
|
|
197
193
|
puts proc.canonic_file(__FILE__)
|
|
198
|
-
proc.
|
|
194
|
+
proc.settings[:basename] = true
|
|
199
195
|
puts proc.canonic_file(__FILE__)
|
|
200
196
|
puts proc.current_source_text
|
|
201
197
|
xx = eval <<-END
|
|
202
198
|
proc.frame_initialize
|
|
203
199
|
proc.frame_setup(RubyVM::ThreadFrame.current)
|
|
204
|
-
proc.
|
|
205
|
-
proc.current_source_text
|
|
200
|
+
puts proc.current_source_text
|
|
206
201
|
END
|
|
207
202
|
end
|