Commit 4e4bfb08 authored by Hiroshi Mimaki's avatar Hiroshi Mimaki

Merge pull request #2640 from mruby-Forum/v1.1.0

mruby-1.1.0
parents 5c6d6309 b473043a
...@@ -20,3 +20,14 @@ Original Authors "mruby developers" are: ...@@ -20,3 +20,14 @@ Original Authors "mruby developers" are:
Tatsuhiko Kubo Tatsuhiko Kubo
Takeshi Watanabe Takeshi Watanabe
Yuki Kurihara Yuki Kurihara
specified non-profit corporation mruby Forum
Kazuaki Tanaka
Hiromasa Ishii
Hiroshi Mimaki
Satoshi Odawara
Mitsubishi Electric Micro-Computer Application Software Co.,Ltd.
Hiroyuki Matsuzaki
Yuhei Okazaki
Manycolors, Inc.
Shota Nakano
Yuichi Osawa
...@@ -75,6 +75,16 @@ MRuby.each_target do |target| ...@@ -75,6 +75,16 @@ MRuby.each_target do |target|
FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose } FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose }
end end
depfiles += [ install_path ] depfiles += [ install_path ]
elsif target == MRuby.targets['host-debug']
unless MRuby.targets['host'].gems.map {|g| g.bins}.include?([bin])
install_path = MRuby.targets['host-debug'].exefile("#{MRUBY_ROOT}/bin/#{bin}")
file install_path => exec do |t|
FileUtils.rm_f t.name, { :verbose => $verbose }
FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose }
end
depfiles += [ install_path ]
end
else else
depfiles += [ exec ] depfiles += [ exec ]
end end
......
...@@ -83,6 +83,31 @@ MRuby::Build.new do |conf| ...@@ -83,6 +83,31 @@ MRuby::Build.new do |conf|
# conf.enable_bintest # conf.enable_bintest
end end
MRuby::Build.new('host-debug') do |conf|
# load specific toolchain settings
# Gets set by the VS command prompts.
if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
toolchain :visualcpp
else
toolchain :gcc
end
enable_debug
# include the default GEMs
conf.gembox 'default'
# C compiler settings
conf.cc.defines = %w(ENABLE_DEBUG)
# Generate mruby debugger command (require mruby-eval)
conf.gem :core => "mruby-bin-debugger"
# bintest
# conf.enable_bintest
end
# Define cross build settings # Define cross build settings
# MRuby::CrossBuild.new('32bit') do |conf| # MRuby::CrossBuild.new('32bit') do |conf|
# toolchain :gcc # toolchain :gcc
......
# How to use mruby debugger mrdb
copyright (c) 2014 Specified Non-Profit Coorporation mruby Forum
## 1.Summary
This file documents the method for using the mruby debugger 'mrdb'
## 2 debugging with mrdb
## 2.1 Building mrdb
The trunk of the mruby source tree can be checked out with following command/
```bash
$ git clone https://github.com/Kumikomi-Ruby/forum-mruby.git mruby
```
Run make command
```bash
$ cd mruby
$ make
```
By default, make command will install debugger files into mruby/bin.
You can add the path for mrdb on your host environment with following command
```bash
$ echo "export PATH=\$PATH:MRUBY_ROOT/bin" >> ~/.bashrc
$ source ~/.bashrc
```
*MRUBY_ROOT is the directory in which mruby source cords will be installed.
To confirm mrdb was instaleed properly,run mrdb with --version option
```bash
$ mrdb --version
mruby 1.1.0 (2014-11-19)
```
## 2.2 Bassic Operation
### 2.2.1 debugging mruby script file(rb file) with mrdb
To invoke mruby debugger, just type mrdb.
To specify the script file ,
```bash
$ mrdb [option] file name
```
For example : Debugging sample.rb
```bash
$ mrdb sample.rb
```
You can excute shell commands listed below
|command|description|
|:-:|:--|
|run|execute programs|
|step|execute stepping|
|continue|execute continuing program|
|break|configure the breaking point|
|delete|deleting the breaking points|
|disable|disabling the breaking points|
|enable|enabling the breaking points|
|info breakpoints|showing list of the breaking points|
|print|eavaluating and printing the values of the mruby expressions in the script|
|list|displaying the source cords|
|help|showing help|
|quit|terminating the mruby debugger|
### 2.2.2 debugging mruby binary file(mrb file) with mrdb
You can debugg the mruby binary files
#### 2.2.2.1 debuggg the binary files
* notice
To debugg mruby binary files, you need to compile mruby files with option -g.
```bash
$ mrbc -g sample.rb
```
You can debugg the mruby binary files with following command and the option -b.
```bash
$ mrdb -b sample.mrb
```
Then you can execute all debugger shell commands.
#### break command
you can give any breakpoint to stop the program by specifiyng the line number and method name.
And the breakpoint list will be displayed after you finished to set the breakpoint succesfully.
Usage:
```
break [file:]lineno
b [file:]lineno
break [class:]method
b [class:]method
```
The breakpoint will be numbered in serial order from 1.The number which was given to deleted breakpoint will not be given to another breakpoint again.
You can give multiple breakpoints to specified the line number and method.
Be ware that breakpoint command will not check the validity of the class name and method name.
You can see the current breakpoint information by following options.
breakpoint breakpoint number : file name. line number
breakpoint breakpoint number : [class name,] method name
#### continue command
Usage:
```
continue [N]
c [N]
```
N: the next breakpoint number
Resuming the program and will stop the program at breakpoint at N (N-1 breakpoint will be ignored)
When you run continue command without any specifying N ,Program will be stopped at next breakpoint.
Example:
```
(foo.rb:1) continue 3
```
Resuming the program and stopping the program at the third breakpoint.
#### delete command
Deleting specified breakpoint
Usage:
```
delete [breakpointno]
d [breakpointno]
```
breakpointno: breakpoint number
Example:
```
(foo.rb:1) delete
```
Deleting all brealpoint
```
(foo.rb:1) delete 1 3
```
Deleting the breakpoint 1 and 3
#### diable command
Disabling the specified breakpoint
Usage:
```
disable [breakpointno]
dis [breakpointno]
```
brealpointno: breakpoint number
Example:
```
(foo.rb:1) disable
```
Desabling all brealpoint
```
(foo.rb:1) disable 1 3
```
Disabling the breakpoint 1 and 3
#### enable command
Enababling the specified breakpoint
Usage:
```
enable [breakpointno]
e [breakpointno]
```
brealpointno: breakpoint number
Example:
```
(foo.rb:1) enable
```
Enabling all brealpoint
```
(foo.rb:1) enable 1 3
```
Enabling the breakpoint 1 and 3
#### eval command
Evaluating the string as source code and printing the value.
Same as print command, please see print command.
#### help command
Displaying the help message.
Usage:
```
help [comand]
h [command]
```
Typing help without any option will displays the command list.
#### info breakpoints command
Displaying the specified breakpoint information.
Usage:
```
info breakpoints [breakpointno]
i b [breakpointno]
```
breakpointno: breakpoint number
Typing "info breakpoints" without ant option will display all breakpoint information.
Example
```
(sample.rb:1) info breakpoints
Num Type Enb What
1 breakpoint y at sample.rb:3 -> file name,line number
2 breakpoint n in Sample_class:sample_class_method -> [class:]method name
3 breakpoint y in sample_global_method
```
Displaying specified the breakpoint number
```
(foo.rb:1) info breakpoints 1 3
Num Type Enb What
1 breakpoint y at sample.rb:3
3 breakpoint y in sample_global_method
```
#### list command
Displaying the cords of the source file.
Usage:
```
list [filename:]first[,last]
l [filename]:first[,last]
```
first: the opening row number
last : the closing row number
When you specify first , but not specify option "last" , you will get 10 rows.
When you don not specify both of first and last, you will next 10 rows.
Example:
```
Specifying file name and first row number
sample.rb:1) list sample2.rb:5
```
Specifying file name and first and last row number
```
(sample.rb:1) list sample2.rb:6,7
```
#### print command
Evaluating the string as source code and printing the value.
Usage:
```
print [expr]
p [expr]
```
expr: expression
To specify the expression is indispensableness.
The displayed expressions will be numbered in serial order from 1.
If an exception occurs, the exception information will be displayed and the debugging will be continued.
Example:
```
(sample.rb:1) print 1+2
$1 = 3
(sample.rb:1) print self
$2 = main
```
below is the case of the exception:
```
(sample.rb:1) print (1+2
$1 = SyntaxError: line 1: syntax error, unexpected $end, expecting ')'
```
#### quit command
Quitting the debugger.
Usage:
```
quit
q
```
#### run command
Running the program and stopping at the first breakpoint.
Usage:
```
run
r
```
#### step command
Running the program step by step.
When the method and the block will be invoked, the program will be stop at the first row.
The program which is developed by C language will be ignored.
period
...@@ -359,6 +359,8 @@ MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self); ...@@ -359,6 +359,8 @@ MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self);
#define ISALPHA(c) (ISASCII(c) && isalpha((int)(unsigned char)(c))) #define ISALPHA(c) (ISASCII(c) && isalpha((int)(unsigned char)(c)))
#define ISDIGIT(c) (ISASCII(c) && isdigit((int)(unsigned char)(c))) #define ISDIGIT(c) (ISASCII(c) && isdigit((int)(unsigned char)(c)))
#define ISXDIGIT(c) (ISASCII(c) && isxdigit((int)(unsigned char)(c))) #define ISXDIGIT(c) (ISASCII(c) && isxdigit((int)(unsigned char)(c)))
#define ISBLANK(c) (ISASCII(c) && isblank((int)(unsigned char)(c)))
#define ISCNTRL(c) (ISASCII(c) && iscntrl((int)(unsigned char)(c)))
#define TOUPPER(c) (ISASCII(c) ? toupper((int)(unsigned char)(c)) : (c)) #define TOUPPER(c) (ISASCII(c) ? toupper((int)(unsigned char)(c)) : (c))
#define TOLOWER(c) (ISASCII(c) ? tolower((int)(unsigned char)(c)) : (c)) #define TOLOWER(c) (ISASCII(c) ? tolower((int)(unsigned char)(c)) : (c))
#endif #endif
......
...@@ -10,15 +10,15 @@ ...@@ -10,15 +10,15 @@
#define MRUBY_RUBY_VERSION "1.9" #define MRUBY_RUBY_VERSION "1.9"
#define MRUBY_RUBY_ENGINE "mruby" #define MRUBY_RUBY_ENGINE "mruby"
#define MRUBY_VERSION "1.0.1" #define MRUBY_VERSION "1.1.0"
#define MRUBY_RELEASE_MAJOR 1 #define MRUBY_RELEASE_MAJOR 1
#define MRUBY_RELEASE_MINOR 0 #define MRUBY_RELEASE_MINOR 1
#define MRUBY_RELEASE_TEENY 1 #define MRUBY_RELEASE_TEENY 1
#define MRUBY_RELEASE_NO 10001 #define MRUBY_RELEASE_NO 10101
#define MRUBY_RELEASE_DATE "2014-01-10" #define MRUBY_RELEASE_DATE "2014-11-19"
#define MRUBY_RELEASE_YEAR 2014 #define MRUBY_RELEASE_YEAR 2014
#define MRUBY_RELEASE_MONTH 1 #define MRUBY_RELEASE_MONTH 11
#define MRUBY_RELEASE_DAY 10 #define MRUBY_RELEASE_DAY 19
#define MRUBY_BIRTH_YEAR 2010 #define MRUBY_BIRTH_YEAR 2010
......
...@@ -4,6 +4,6 @@ MRuby::GemBox.new do |conf| ...@@ -4,6 +4,6 @@ MRuby::GemBox.new do |conf|
Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x| Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x|
g = File.basename File.dirname x g = File.basename File.dirname x
conf.gem :core => g unless g =~ /^mruby-(print|sprintf)$/ conf.gem :core => g unless g =~ /^mruby-(print|sprintf|bin-debugger)$/
end end
end end
require 'open3'
require 'tempfile'
class BinTest_MrubyBinDebugger
@debug1=false
@debug2=true
@debug3=true
def self.test(rubysource, testcase)
script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb'])
# .rb
script.write rubysource
script.flush
# compile
`./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
# add mrdb quit
testcase << {:cmd=>"quit"}
stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n"
["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd|
o, s = Open3.capture2(cmd, :stdin_data => stdin_data)
exp_vals = testcase.map{|t| t.fetch(:exp, nil)}
unexp_vals = testcase.map{|t| t.fetch(:unexp, nil)}
if @debug1
o.split("\n").each_with_index do |i,actual|
p [i,actual]
end
end
# compare actual / expected
o.split("\n").each do |actual|
next if actual.empty?
exp = exp_vals.shift
if @debug2
a = true
a = actual.include?(exp) unless exp.nil?
p [actual, exp] unless a
end
assert_true actual.include?(exp) unless exp.nil?
end
# compare actual / unexpected
o.split("\n").each do |actual|
next if actual.empty?
unexp = unexp_vals.shift
if @debug3
a = false
a = actual.include?(unexp) unless unexp.nil?
p [actual, unexp] if a
end
assert_false actual.include?(unexp) unless unexp.nil?
end
end
end
end
INVCMD = "invalid command"
assert('mruby-bin-debugger(mrdb) command line') do
# ruby source
src = "foo = 'foo'\n"
str = ""
103.times {
str += "1234567890"
}
cmd = "p a=#{str}"
# test case
BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1023], :unexp=>'command line too long.'}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1024], :unexp=>'command line too long.'}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>cmd[0...1025], :exp=>'command line too long.'}])
end
assert('mruby-bin-debugger(mrdb) command: "break"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"b", :unexp=>INVCMD}
tc << {:cmd=>"br", :unexp=>INVCMD}
tc << {:cmd=>"brea", :unexp=>INVCMD}
tc << {:cmd=>"break", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"bl", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"breaka", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "continue"') do
# ruby source
src = "foo = 'foo'\n"
# test case
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"c", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"co", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continu", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continue", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"cn", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"continuee", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "delete"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"d 1", :unexp=>INVCMD}
tc << {:cmd=>"de 1", :unexp=>INVCMD}
tc << {:cmd=>"delet 1", :unexp=>INVCMD}
tc << {:cmd=>"delete 1", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"dd 1", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"deletee 1", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "disable"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"dis", :unexp=>INVCMD}
tc << {:cmd=>"disa", :unexp=>INVCMD}
tc << {:cmd=>"disabl", :unexp=>INVCMD}
tc << {:cmd=>"disable", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"di", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disb", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"disablee", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "enable"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"en", :unexp=>INVCMD}
tc << {:cmd=>"ena", :unexp=>INVCMD}
tc << {:cmd=>"enabl", :unexp=>INVCMD}
tc << {:cmd=>"enable", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enb", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"enablee", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "eval"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"ev", :unexp=>INVCMD}
tc << {:cmd=>"eva", :unexp=>INVCMD}
tc << {:cmd=>"eval", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"e", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evl", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"evall", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "help"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"h", :unexp=>INVCMD}
tc << {:cmd=>"he", :unexp=>INVCMD}
tc << {:cmd=>"hel", :unexp=>INVCMD}
tc << {:cmd=>"help", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"hl", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"helpp", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "info breakpoints"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"i b", :unexp=>INVCMD}
tc << {:cmd=>"in b", :unexp=>INVCMD}
tc << {:cmd=>"i br", :unexp=>INVCMD}
tc << {:cmd=>"inf breakpoint", :unexp=>INVCMD}
tc << {:cmd=>"info breakpoints", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ii b", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"i bb", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"infoo breakpoints", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"info breakpointss", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "list"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"l", :unexp=>INVCMD}
tc << {:cmd=>"li", :unexp=>INVCMD}
tc << {:cmd=>"lis", :unexp=>INVCMD}
tc << {:cmd=>"list", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ll", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"listt", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "print"') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"p", :unexp=>INVCMD}
tc << {:cmd=>"pr", :unexp=>INVCMD}
tc << {:cmd=>"prin", :unexp=>INVCMD}
tc << {:cmd=>"print", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"pp", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"printt", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "quit"') do
# ruby source
src = "foo = 'foo'\n"
# test case
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"q", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qu", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qui", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quit", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"qq", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"quitt", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "run"') do
# ruby source
src = "foo = 'foo'\n"
# test case
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"r", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ru", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"run", :unexp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"rr", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"runn", :exp=>INVCMD}])
end
assert('mruby-bin-debugger(mrdb) command: "step"') do
# ruby source
src = <<"SRC"
while true
foo = 'foo'
end
SRC
# test case
tc = []
tc << {:cmd=>"s", :unexp=>INVCMD}
tc << {:cmd=>"st", :unexp=>INVCMD}
tc << {:cmd=>"ste", :unexp=>INVCMD}
tc << {:cmd=>"step", :unexp=>INVCMD}
BinTest_MrubyBinDebugger.test(src, tc)
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"ss", :exp=>INVCMD}])
BinTest_MrubyBinDebugger.test(src, [{:cmd=>"stepp", :exp=>INVCMD}])
end
require 'open3'
require 'tempfile'
class BinTest_MrubyBinDebugger
@debug1=false
@debug2=true
def self.test(rubysource, testcase)
script, bin = Tempfile.new(['test', '.rb']), Tempfile.new(['test', '.mrb'])
# .rb
script.write rubysource
script.flush
# compile
`./bin/mrbc -g -o "#{bin.path}" "#{script.path}"`
# add mrdb quit
testcase << {:cmd=>"quit"}
stdin_data = testcase.map{|t| t[:cmd]}.join("\n") << "\n"
["bin/mrdb #{script.path}","bin/mrdb -b #{bin.path}"].each do |cmd|
o, s = Open3.capture2(cmd, :stdin_data => stdin_data)
exp_vals = testcase.map{|t| t.fetch(:exp, nil)}
=begin
if @debug1
o.split("\n").each_with_index do |i,actual|
p [i,actual]
end
end
# compare actual / expected
o.split("\n").each do |actual|
next if actual.empty?
exp = exp_vals.shift
if @debug2
a = true
a = actual.include?(exp) unless exp.nil?
p [actual, exp] unless a
end
assert_true actual.include?(exp) unless exp.nil?
end
=end
idx = 0
exp_vals.each do |exp|
next if exp.nil?
idx = o.index(exp, idx)
assert_false idx.nil?
break unless idx
idx += 1
end
end
end
end
assert('mruby-bin-debugger(print) invalid arguments') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"p", :exp=>"Parameter not specified."}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) nomal') do
# ruby source
src = <<"SRC"
foo = 'foo'
bar = foo
baz = bar
SRC
# test case
tc = []
tc << {:cmd=>"s"}
tc << {:cmd=>"p (1+2)", :exp=>'$1 = 3'}
tc << {:cmd=>"p foo", :exp=>'$2 = "foo"'}
tc << {:cmd=>"p foo*=2", :exp=>'$3 = "foofoo"'}
tc << {:cmd=>"s"}
tc << {:cmd=>"p bar", :exp=>'$4 = "foofoo"'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) error') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"p (1+2", :exp=>'$1 = SyntaxError'}
tc << {:cmd=>"p bar", :exp=>'$2 = NoMethodError'}
BinTest_MrubyBinDebugger.test(src, tc)
end
# Kernel#instance_eval(string) does't work multiple statements.
=begin
assert('mruby-bin-debugger(print) multiple statements') do
# ruby source
src = <<"SRC"
x = 0
y = 0
z = 0
SRC
# test case
tc = []
tc << {:cmd=>"s",}
tc << {:cmd=>"p x=1;x+=2", :exp=>"3"}
tc << {:cmd=>"s",}
tc << {:cmd=>"p x", :exp=>"3"}
BinTest_MrubyBinDebugger.test(src, tc)
end
=end
assert('mruby-bin-debugger(print) scope:top') do
# ruby source (bp is break point)
src = "bp=nil\n"
# test case
tc = []
tc << {:cmd=>"p self", :exp=>'$1 = main'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) scope:class') do
# ruby source (bp is break point)
src = <<"SRC"
class TestClassScope
bp = nil
end
SRC
# test case
tc = []
tc << {:cmd=>"s"}
tc << {:cmd=>"p self", :exp=>'$1 = TestClassScope'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) scope:module') do
# ruby source (bp is break point)
src = <<"SRC"
class TestModuleScope
bp = nil
end
SRC
# test case
tc = []
tc << {:cmd=>"s"}
tc << {:cmd=>"p self", :exp=>'$1 = TestModuleScope'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) scope:instance method') do
# ruby source (bp is break point)
src = <<"SRC"
class TestMethodScope
def m
bp = nil
end
end
TestMethodScope.new.m
SRC
tc = []
tc << {:cmd=>"b 3"}
tc << {:cmd=>"r"}
tc << {:cmd=>"p self", :exp=>'$1 = #<TestMethodScope:'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) scope:class method') do
# ruby source (bp is break point)
src = <<"SRC"
class TestClassMethodScope
def self.cm
bp = nil
end
end
TestClassMethodScope.cm
SRC
tc = []
tc << {:cmd=>"b 3"}
tc << {:cmd=>"r"}
tc << {:cmd=>"p self", :exp=>'$1 = TestClassMethodScope'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) scope:block') do
# ruby source (bp is break point)
src = <<"SRC"
1.times do
bp = nil
end
class TestBlockScope
1.times do
bp = nil
end
def m
1.times do
bp = nil
end
end
end
TestBlockScope.new.m
SRC
tc = []
tc << {:cmd=>"b 2"}
tc << {:cmd=>"b 6"}
tc << {:cmd=>"b 10"}
tc << {:cmd=>"c"}
tc << {:cmd=>"p self", :exp=>'$1 = main'}
tc << {:cmd=>"c"}
tc << {:cmd=>"p self", :exp=>'$2 = TestBlockScope'}
tc << {:cmd=>"c"}
tc << {:cmd=>"p self", :exp=>'$3 = #<TestBlockScope:'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) same name:local variabe') do
# ruby source (bp is break point)
src = <<"SRC"
lv = 'top'
class TestLocalVariableName
lv = 'class'
def m
lv = 'instance method'
bp = nil
end
bp = nil
end
TestLocalVariableName.new.m
bp = nil
SRC
tc = []
tc << {:cmd=>"b 6"}
tc << {:cmd=>"b 8"}
tc << {:cmd=>"b 11"}
tc << {:cmd=>"r"}
tc << {:cmd=>"p lv", :exp=>'$1 = "class"'}
tc << {:cmd=>"c"}
tc << {:cmd=>"p lv", :exp=>'$2 = "instance method"'}
tc << {:cmd=>"c"}
tc << {:cmd=>"p lv", :exp=>'$3 = "top"'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) same name:instance variabe') do
# ruby source (bp is break point)
src = <<"SRC"
@iv = 'top'
class TestInstanceVariableName
def initialize(v)
@iv = v
end
def m
bp = nil
end
end
i1 = TestInstanceVariableName.new('instance1')
i2 = TestInstanceVariableName.new('instance2')
i1.m
i2.m
bp = nil
SRC
tc = []
tc << {:cmd=>"b 7"}
tc << {:cmd=>"b 14"}
tc << {:cmd=>"r"}
tc << {:cmd=>"p @iv", :exp=>'$1 = "instance1"'}
tc << {:cmd=>"c"}
tc << {:cmd=>"p @iv", :exp=>'$2 = "instance2"'}
tc << {:cmd=>"c"}
tc << {:cmd=>"p @iv", :exp=>'$3 = "top"'}
BinTest_MrubyBinDebugger.test(src, tc)
end
# Kernel#instance_eval(string) does't work const.
=begin
assert('mruby-bin-debugger(print) same name:const') do
# ruby source (bp is break point)
src = <<"SRC"
CONST='top'
class TestConstNameSuperClass
CONST='super class'
def m
bp = nil
end
end
class TestConstNameSubClass < TestConstNameSuperClass
CONST='sub class'
def m
bp = nil
end
end
TestConstNameSuperClass.new.m()
TestConstNameSubClass.new.m()
bp = nil
SRC
# todo: wait for 'break' to be implimented
tc = []
9.times { tc << {:cmd=>"s"} }
tc << {:cmd=>"p CONST", :exp=>"super class"}
3.times { tc << {:cmd=>"s"} }
tc << {:cmd=>"p CONST", :exp=>"sub class"}
1.times { tc << {:cmd=>"s"} }
tc << {:cmd=>"p CONST", :exp=>"top"}
BinTest_MrubyBinDebugger.test(src, tc)
end
=end
assert('mruby-bin-debugger(print) Literal:Numeric') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>"p 100", :exp=>'$1 = 100'}
tc << {:cmd=>"p -0b100", :exp=>'$2 = -4'}
tc << {:cmd=>"p +0100", :exp=>'$3 = 64'}
tc << {:cmd=>"p 0x100", :exp=>'$4 = 256'}
tc << {:cmd=>"p 1_234", :exp=>'$5 = 1234'}
tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000.to_s}"}
tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000.to_s}"}
tc << {:cmd=>"p 3.14", :exp=>'$8 = 3.14'}
tc << {:cmd=>"p -12.3", :exp=>'$9 = -12.3'}
tc << {:cmd=>"p +12.000", :exp=>'$10 = 12.0'}
tc << {:cmd=>"p 1e4", :exp=>'$11 = 10000.0'}
tc << {:cmd=>"p -0.1e-2", :exp=>'$12 = -0.001'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Literal:String') do
# ruby source
src = <<"SRC"
foo = 'foo'
bar = "bar"
baz = "baz"
SRC
# test case
tc = []
tc << {:cmd=>"s"}
tc << {:cmd=>"s"}
tc << {:cmd=>'p "str"', :exp=>'$1 = "str"'}
tc << {:cmd=>'p "s\tt\rr\n"', :exp=>'$2 = "s\\tt\\rr\\n"'}
tc << {:cmd=>'p "\C-a\C-z"', :exp=>'$3 = "\\001\\032"'}
tc << {:cmd=>'p "#{foo+bar}"', :exp=>'$4 = "foobar"'}
tc << {:cmd=>'p \'str\'', :exp=>'$5 = "str"'}
tc << {:cmd=>'p \'s\\tt\\rr\\n\'', :exp=>'$6 = "s\\\\tt\\\\rr\\\\n"'}
tc << {:cmd=>'p \'\\C-a\\C-z\'', :exp=>'$7 = "\\\\C-a\\\\C-z"'}
tc << {:cmd=>'p \'#{foo+bar}\'', :exp=>'$8 = "#{foo+bar}"'}
tc << {:cmd=>'p %!str!', :exp=>'$9 = "str"'}
tc << {:cmd=>'p %!s\tt\rr\n!', :exp=>'$10 = "s\\tt\\rr\\n"'}
tc << {:cmd=>'p %!\C-a\C-z!', :exp=>'$11 = "\\001\\032"'}
tc << {:cmd=>'p %!#{foo+bar}!', :exp=>'$12 = "foobar"'}
tc << {:cmd=>'p %Q!str!', :exp=>'$13 = "str"'}
tc << {:cmd=>'p %Q!s\tt\rr\n!', :exp=>'$14 = "s\\tt\\rr\\n"'}
tc << {:cmd=>'p %Q!\C-a\C-z!', :exp=>'$15 = "\\001\\032"'}
tc << {:cmd=>'p %Q!#{foo+bar}!', :exp=>'$16 = "foobar"'}
tc << {:cmd=>'p %q!str!', :exp=>'$17 = "str"'}
tc << {:cmd=>'p %q!s\\tt\\rr\\n!', :exp=>'$18 = "s\\\\tt\\\\rr\\\\n"'}
tc << {:cmd=>'p %q!\\C-a\\C-z!', :exp=>'$19 = "\\\\C-a\\\\C-z"'}
tc << {:cmd=>'p %q!#{foo+bar}!', :exp=>'$20 = "#{foo+bar}"'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Literal:Array') do
# ruby source
src = <<"SRC"
foo = 'foo'
bar = "bar"
baz = "baz"
SRC
# test case
tc = []
tc << {:cmd=>"s"}
tc << {:cmd=>"s"}
tc << {:cmd=>'p []', :exp=>'$1 = []'}
tc << {:cmd=>'p [ 5, 12, 8, 10, ]', :exp=>'$2 = [5, 12, 8, 10]'}
tc << {:cmd=>'p [1,2.5,"#{foo+bar}"]', :exp=>'$3 = [1, 2.5, "foobar"]'}
tc << {:cmd=>'p %w[3.14 A\ &\ B #{foo}]', :exp=>'$4 = ["3.14", "A & B", "#{foo}"]'}
tc << {:cmd=>'p %W[3.14 A\ &\ B #{foo}]', :exp=>'$5 = ["3.14", "A & B", "foo"]'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Literal:Hash') do
# ruby source
src = <<"SRC"
foo = 'foo'
bar = "bar"
baz = "baz"
SRC
# test case
tc = []
tc << {:cmd=>"s"}
tc << {:cmd=>"s"}
tc << {:cmd=>'p {}', :exp=>'$1 = {}'}
tc << {:cmd=>'p {"one"=>1,"two"=>2}', :exp=>'$2 = {"one"=>1, "two"=>2}'}
tc << {:cmd=>'p {:eins=>"1", :zwei=>"2", }', :exp=>'$3 = {:eins=>"1", :zwei=>"2"}'}
tc << {:cmd=>'p {uno:"one", dos: 2}', :exp=>'$4 = {:uno=>"one", :dos=>2}'}
tc << {:cmd=>'p {"one"=>1, :zwei=>2, tres:3}', :exp=>'$5 = {"one"=>1, :zwei=>2, :tres=>3}'}
tc << {:cmd=>'p {:foo=>"#{foo}",:bar=>"#{bar}"}', :exp=>'$6 = {:foo=>"foo", :bar=>"bar"}'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Literal:Range') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>'p 1..10', :exp=>'$1 = 1..10'}
tc << {:cmd=>'p 1...10', :exp=>'$2 = 1...10'}
tc << {:cmd=>'p 100..10', :exp=>'$3 = 100..10'}
tc << {:cmd=>'p 1 ... 10', :exp=>'$4 = 1...10'}
tc << {:cmd=>'p "1" .. "9"', :exp=>'$5 = "1".."9"'}
tc << {:cmd=>'p "A" ... "Z"', :exp=>'$6 = "A"..."Z"'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Literal:Symbol') do
# ruby source
src = <<"SRC"
foo = 'foo'
bar = "bar"
baz = "baz"
SRC
# test case
tc = []
tc << {:cmd=>"s"}
tc << {:cmd=>"s"}
tc << {:cmd=>'p :sym', :exp=>'$1 = :sym'}
tc << {:cmd=>'p :"sd"', :exp=>'$2 = :sd'}
tc << {:cmd=>"p :'ss'", :exp=>'$3 = :ss'}
tc << {:cmd=>'p :"123"', :exp=>'$4 = :"123"'}
tc << {:cmd=>'p :"#{foo} baz"', :exp=>'$5 = :"foo baz"'}
tc << {:cmd=>'p %s!symsym!', :exp=>'$6 = :symsym'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Unary operation') do
# ruby source
src = "foo = 'foo'\n"
# test case
tc = []
tc << {:cmd=>'p +10', :exp=>'$1 = 10'}
tc << {:cmd=>'p -100', :exp=>'$2 = -100'}
tc << {:cmd=>'p !true', :exp=>'$3 = false'}
tc << {:cmd=>'p !false', :exp=>'$4 = true'}
tc << {:cmd=>'p !nil', :exp=>'$5 = true'}
tc << {:cmd=>'p !1', :exp=>'$6 = false'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Binary operation') do
# ruby source
src = <<"SRC"
CONST = 100
a,b,c = 1, 5, 8
foo,bar,baz = 'foo','bar','baz'
ary = []
SRC
# test case
tc = []
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'p a+1', :exp=>'$1 = 2'}
tc << {:cmd=>'p 2-b', :exp=>'$2 = -3'}
tc << {:cmd=>'p c * 3', :exp=>'$3 = 24'}
tc << {:cmd=>'p a/b', :exp=>'$4 = 0.2'}
tc << {:cmd=>'p c%b', :exp=>'$5 = 3'}
tc << {:cmd=>'p 2**10', :exp=>'$6 = 1024'}
tc << {:cmd=>'p ~3', :exp=>'$7 = -4'}
tc << {:cmd=>'p 1<<2', :exp=>'$8 = 4'}
tc << {:cmd=>'p 64>>5', :exp=>'$9 = 2'}
tc << {:cmd=>'p a|c', :exp=>'$10 = 9'}
tc << {:cmd=>'p a&b', :exp=>'$11 = 1'}
tc << {:cmd=>'p a^b', :exp=>'$12 = 4'}
tc << {:cmd=>'p a>b', :exp=>'$13 = false'}
tc << {:cmd=>'p a<b', :exp=>'$14 = true'}
tc << {:cmd=>'p b>=5', :exp=>'$15 = true'}
tc << {:cmd=>'p b<=5', :exp=>'$16 = true'}
tc << {:cmd=>'p "A"<=>"B"', :exp=>'$17 = -1'}
tc << {:cmd=>'p "A"=="B"', :exp=>'$18 = false'}
tc << {:cmd=>'p "A"==="B"', :exp=>'$19 = false'}
tc << {:cmd=>'p "A"!="B"', :exp=>'$20 = true'}
tc << {:cmd=>'p false || true', :exp=>'$21 = true'}
tc << {:cmd=>'p false && true', :exp=>'$22 = false'}
tc << {:cmd=>'p not nil', :exp=>'$23 = true'}
tc << {:cmd=>'p false or true', :exp=>'$24 = true'}
tc << {:cmd=>'p false and true', :exp=>'$25 = false'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Ternary operation') do
# ruby source
src = <<"SRC"
CONST = 100
a,b,c = 1, 5, -10
foo,bar,baz = 'foo','bar','baz'
ary = []
SRC
# test case
tc = []
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'p (a < b) ? a : b', :exp=>'$1 = 1'}
tc << {:cmd=>'p (a > b) ? a : b', :exp=>'$2 = 5'}
tc << {:cmd=>'p true ? "true" : "false"', :exp=>'$3 = "true"'}
tc << {:cmd=>'p false ? "true" : "false"', :exp=>'$4 = "false"'}
tc << {:cmd=>'p nil ? "true" : "false"', :exp=>'$5 = "false"'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Substitution:simple') do
# ruby source
src = <<"SRC"
CONST = 100
a,b,c = 1, 5, -10
foo,bar,baz = 'foo','bar','baz'
ary = []
SRC
# test case
tc = []
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'p a=2', :exp=>'$1 = 2'}
tc << {:cmd=>'p foo=[foo,bar,baz]', :exp=>'$2 = ["foo", "bar", "baz"]'}
tc << {:cmd=>'p undefined=-1', :exp=>'$3 = -1'}
tc << {:cmd=>'p "#{undefined}"', :exp=>'$4 = NoMethodError'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Substitution:self') do
# ruby source
src = <<"SRC"
CONST = 100
a,b,c = 1, 5, -10
foo,bar,baz = 'foo','bar','baz'
ary = []
SRC
# test case
tc = []
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'}
tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'}
tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'}
tc << {:cmd=>'p a/=4', :exp=>'$4 = 2.5'}
tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'}
tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'}
tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'}
tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2.5 5 18"'}
tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'}
tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'}
tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'}
tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'}
tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'}
tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'}
tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = NoMethodError'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Substitution:multiple') do
# ruby source
src = <<"SRC"
CONST = 100
a,b,c = 1, 5, -10
foo,bar,baz = 'foo','bar','baz'
ary = []
SRC
# test case
tc = []
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'p a,b=[10,20]', :exp=>'$1 = [10, 20]'}
tc << {:cmd=>'p [a,b,c]', :exp=>'$2 = [10, 20, -10]'}
tc << {:cmd=>'p foo,bar=["FOO","BAR","BAZ"]', :exp=>'$3 = ["FOO", "BAR", "BAZ"]'}
tc << {:cmd=>'p [foo,bar,baz]', :exp=>'$4 = ["FOO", "BAR", "baz"]'}
tc << {:cmd=>'p a,foo=foo,a', :exp=>'$5 = ["FOO", 10]'}
tc << {:cmd=>'p [a,foo]', :exp=>'$6 = ["FOO", 10]'}
# tc << {:cmd=>'p a,*b=[123, 456, 789]'}
# tc << {:cmd=>'p [a,b]', :exp=>'[123, [456, 789]]'}
BinTest_MrubyBinDebugger.test(src, tc)
end
assert('mruby-bin-debugger(print) Substitution:self') do
# ruby source
src = <<"SRC"
CONST = 100
a,b,c = 1, 5, -10
foo,bar,baz = 'foo','bar','baz'
ary = []
SRC
# test case
tc = []
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'s'}
tc << {:cmd=>'p a+=9', :exp=>'$1 = 10'}
tc << {:cmd=>'p b-=c', :exp=>'$2 = 15'}
tc << {:cmd=>'p bar*=2', :exp=>'$3 = "barbar"'}
tc << {:cmd=>'p a/=4', :exp=>'$4 = 2.5'}
tc << {:cmd=>'p c%=4', :exp=>'$5 = 2'}
tc << {:cmd=>'p b&=0b0101', :exp=>'$6 = 5'}
tc << {:cmd=>'p c|=0x10', :exp=>'$7 = 18'}
tc << {:cmd=>'p "#{a} #{b} #{c}"', :exp=>'$8 = "2.5 5 18"'}
tc << {:cmd=>'p "#{foo}#{bar}#{baz}"', :exp=>'$9 = "foobarbarbaz"'}
tc << {:cmd=>'p a,b,c=[10,20,30]',:exp=>'$10 = [10, 20, 30]'}
tc << {:cmd=>'p [a,b,c]', :exp=>'$11 = [10, 20, 30]'}
tc << {:cmd=>'p a,b=b,a', :exp=>'$12 = [20, 10]'}
tc << {:cmd=>'p [a,b]', :exp=>'$13 = [20, 10]'}
tc << {:cmd=>'p undefined=-1', :exp=>'$14 = -1'}
tc << {:cmd=>'p "#{undefined}"', :exp=>'$15 = NoMethodError'}
BinTest_MrubyBinDebugger.test(src, tc)
end
MRuby::Gem::Specification.new('mruby-bin-debugger') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'mruby debuggeer command'
spec.add_dependency('mruby-eval', :core => 'mruby-eval')
spec.bins = %w(mrdb)
end
/*
** apibreak.c
**
*/
#include <string.h>
#include "mruby.h"
#include "mruby/irep.h"
#include "mrdb.h"
#include "mruby/debug.h"
#include "mruby/opcode.h"
#include "mruby/class.h"
#include "mruby/proc.h"
#include "mruby/variable.h"
#include "mrdberror.h"
#include "apibreak.h"
#define MAX_BREAKPOINTNO (MAX_BREAKPOINT * 1024)
#define MRB_DEBUG_BP_FILE_OK (0x0001)
#define MRB_DEBUG_BP_LINENO_OK (0x0002)
static uint16_t
check_lineno( mrb_irep_debug_info_file *info_file, uint16_t lineno )
{
uint32_t count = info_file->line_entry_count;
uint16_t l_idx;
if( info_file->line_type == mrb_debug_line_ary ) {
for (l_idx = 0; l_idx < count; ++l_idx) {
if(lineno == info_file->lines.ary[l_idx]) {
return lineno;
}
}
} else {
for (l_idx = 0; l_idx < count; ++l_idx) {
if(lineno == info_file->lines.flat_map[l_idx].line) {
return lineno;
}
}
}
return 0;
}
static int32_t
get_break_index( mrb_debug_context *dbg, int32_t bpno )
{
uint32_t i;
int32_t index;
char hit = FALSE;
for(i = 0 ; i < dbg->bpnum; i++) {
if(dbg->bp[i].bpno == bpno) {
hit = TRUE;
index = i;
break;
}
}
if(hit == FALSE) {
return MRB_DEBUG_BREAK_INVALID_NO;
}
return index;
}
static void
free_breakpoint( mrb_state *mrb, mrb_debug_breakpoint *bp )
{
switch(bp->type) {
case MRB_DEBUG_BPTYPE_LINE:
mrb_free(mrb, (void*)bp->point.linepoint.file);
break;
case MRB_DEBUG_BPTYPE_METHOD:
mrb_free(mrb, (void*)bp->point.methodpoint.method_name);
if(bp->point.methodpoint.class_name != NULL) {
mrb_free(mrb, (void*)bp->point.methodpoint.class_name);
}
break;
default:
break;
}
}
static uint16_t
check_file_lineno( struct mrb_irep *irep, const char *file, uint16_t lineno )
{
mrb_irep_debug_info_file *info_file;
uint16_t result = 0;
uint16_t f_idx;
uint16_t fix_lineno;
uint16_t i;
for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
info_file = irep->debug_info->files[f_idx];
if(!strcmp(info_file->filename, file)) {
result = MRB_DEBUG_BP_FILE_OK;
fix_lineno = check_lineno( info_file, lineno );
if(fix_lineno != 0) {
return result | MRB_DEBUG_BP_LINENO_OK;
}
}
for ( i=0; i < irep->rlen; ++i ) {
result |= check_file_lineno(irep->reps[i], file, lineno);
if(result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
return result;
}
}
}
return result;
}
static const char*
get_class_name( mrb_state *mrb, struct RClass *class_obj )
{
struct RClass *outer;
mrb_sym class_sym;
outer = mrb_class_outer_module(mrb, class_obj);
class_sym = mrb_class_sym(mrb, class_obj, outer);
return mrb_sym2name(mrb, class_sym);
}
static int32_t
compare_break_method( mrb_state *mrb, mrb_debug_breakpoint *bp, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc )
{
const char* class_name;
const char* method_name;
struct RProc* m;
struct RClass* sc;
const char* sn;
mrb_sym ssym;
mrb_debug_methodpoint *method_p;
mrb_bool is_defined;
method_name = mrb_sym2name(mrb, method_sym);
method_p = &bp->point.methodpoint;
if(strcmp(method_p->method_name, method_name) == 0) {
class_name = get_class_name(mrb, class_obj);
if(class_name == NULL) {
if(method_p->class_name == NULL) {
return bp->bpno;
}
}
else if(method_p->class_name != NULL) {
m = mrb_method_search_vm(mrb, &class_obj, method_sym);
if(m == NULL) {
return MRB_DEBUG_OK;
}
if(MRB_PROC_CFUNC_P(m)) {
*isCfunc = TRUE;
}
is_defined = mrb_class_defined(mrb, method_p->class_name);
if(is_defined == FALSE) {
return MRB_DEBUG_OK;
}
sc = mrb_class_get(mrb, method_p->class_name);
ssym = mrb_symbol(mrb_check_intern_cstr(mrb, method_p->method_name));
m = mrb_method_search_vm(mrb, &sc, ssym);
if(m == NULL) {
return MRB_DEBUG_OK;
}
class_name = get_class_name(mrb, class_obj);
sn = get_class_name(mrb, sc);
if(strcmp(sn, class_name) == 0) {
return bp->bpno;
}
}
}
return MRB_DEBUG_OK;
}
int32_t
mrb_debug_set_break_line( mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t lineno)
{
int32_t index;
char* set_file;
uint16_t result;
if((mrb == NULL)||(dbg == NULL)||(file == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
if(dbg->bpnum >= MAX_BREAKPOINT) {
return MRB_DEBUG_BREAK_NUM_OVER;
}
if(dbg->next_bpno > MAX_BREAKPOINTNO) {
return MRB_DEBUG_BREAK_NO_OVER;
}
/* file and lineno check (line type mrb_debug_line_ary only.) */
result = check_file_lineno( dbg->root_irep, file, lineno );
if(result == 0) {
return MRB_DEBUG_BREAK_INVALID_FILE;
}else if(result == MRB_DEBUG_BP_FILE_OK) {
return MRB_DEBUG_BREAK_INVALID_LINENO;
}
set_file = mrb_malloc(mrb, strlen(file) + 1);
if(set_file == NULL) {
return MRB_DEBUG_NOBUF;
}
index = dbg->bpnum;
dbg->bp[index].bpno = dbg->next_bpno;
dbg->next_bpno++;
dbg->bp[index].enable = TRUE;
dbg->bp[index].type = MRB_DEBUG_BPTYPE_LINE;
dbg->bp[index].point.linepoint.lineno = lineno;
dbg->bpnum++;
strncpy(set_file, file, strlen(file) + 1);
dbg->bp[index].point.linepoint.file = set_file;
return dbg->bp[index].bpno;
}
int32_t
mrb_debug_set_break_method( mrb_state *mrb, mrb_debug_context *dbg, const char *class_name, const char *method_name )
{
int32_t index;
char* set_class;
char* set_method;
if((mrb == NULL) || (dbg == NULL) || (method_name == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
if(dbg->bpnum >= MAX_BREAKPOINT) {
return MRB_DEBUG_BREAK_NUM_OVER;
}
if(dbg->next_bpno > MAX_BREAKPOINTNO) {
return MRB_DEBUG_BREAK_NO_OVER;
}
if(class_name != NULL) {
set_class = mrb_malloc(mrb, strlen(class_name) + 1);
if(set_class == NULL) {
return MRB_DEBUG_NOBUF;
}
strncpy(set_class, class_name, strlen(class_name) + 1);
}
else {
set_class = NULL;
}
set_method = mrb_malloc(mrb, strlen(method_name) + 1);
if(set_method == NULL) {
if(set_class != NULL) {
mrb_free(mrb, (void*)set_class);
}
return MRB_DEBUG_NOBUF;
}
strncpy(set_method, method_name, strlen(method_name) + 1);
index = dbg->bpnum;
dbg->bp[index].bpno = dbg->next_bpno;
dbg->next_bpno++;
dbg->bp[index].enable = TRUE;
dbg->bp[index].type = MRB_DEBUG_BPTYPE_METHOD;
dbg->bp[index].point.methodpoint.method_name = set_method;
dbg->bp[index].point.methodpoint.class_name = set_class;
dbg->bpnum++;
return dbg->bp[index].bpno;
}
int32_t
mrb_debug_get_breaknum( mrb_state *mrb, mrb_debug_context *dbg )
{
if((mrb == NULL) || (dbg == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
return dbg->bpnum;
}
int32_t
mrb_debug_get_break_all( mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, mrb_debug_breakpoint *bp )
{
uint32_t get_size = 0;
if((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
if(dbg->bpnum >= size) {
get_size = size;
}
else {
get_size = dbg->bpnum;
}
memcpy(bp, dbg->bp, sizeof(mrb_debug_breakpoint) * get_size);
return get_size;
}
int32_t
mrb_debug_get_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp )
{
uint32_t index;
if((mrb == NULL) || (dbg == NULL) || (bp == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
index = get_break_index(dbg, bpno);
if(index == MRB_DEBUG_BREAK_INVALID_NO) {
return MRB_DEBUG_BREAK_INVALID_NO;
}
bp->bpno = dbg->bp[index].bpno;
bp->enable = dbg->bp[index].enable;
bp->point = dbg->bp[index].point;
bp->type = dbg->bp[index].type;
return 0;
}
int32_t
mrb_debug_delete_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno )
{
uint32_t i;
int32_t index;
if((mrb == NULL) ||(dbg == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
index = get_break_index(dbg, bpno);
if(index == MRB_DEBUG_BREAK_INVALID_NO) {
return MRB_DEBUG_BREAK_INVALID_NO;
}
free_breakpoint(mrb, &dbg->bp[index]);
for(i = index ; i < dbg->bpnum; i++) {
if(dbg->bp[i + 1].type == MRB_DEBUG_BPTYPE_NONE) {
memset(&dbg->bp[i], 0, sizeof(mrb_debug_breakpoint));
}
else {
memcpy(&dbg->bp[i], &dbg->bp[i + 1], sizeof(mrb_debug_breakpoint));
}
}
dbg->bpnum--;
return MRB_DEBUG_OK;
}
int32_t
mrb_debug_delete_break_all( mrb_state *mrb, mrb_debug_context *dbg )
{
uint32_t i;
if((mrb == NULL) || (dbg == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
for(i = 0 ; i < dbg->bpnum ; i++) {
free_breakpoint(mrb, &dbg->bp[i]);
}
dbg->bpnum = 0;
return MRB_DEBUG_OK;
}
int32_t
mrb_debug_enable_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno )
{
int32_t index = 0;
if((mrb == NULL) || (dbg == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
index = get_break_index(dbg, bpno);
if(index == MRB_DEBUG_BREAK_INVALID_NO) {
return MRB_DEBUG_BREAK_INVALID_NO;
}
dbg->bp[index].enable = TRUE;
return MRB_DEBUG_OK;
}
int32_t
mrb_debug_enable_break_all( mrb_state *mrb, mrb_debug_context *dbg )
{
uint32_t i;
if((mrb == NULL) || (dbg == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
for(i = 0 ; i < dbg->bpnum; i++) {
dbg->bp[i].enable = TRUE;
}
return MRB_DEBUG_OK;
}
int32_t
mrb_debug_disable_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno )
{
int32_t index = 0;
if((mrb == NULL) || (dbg == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
index = get_break_index(dbg, bpno);
if(index == MRB_DEBUG_BREAK_INVALID_NO) {
return MRB_DEBUG_BREAK_INVALID_NO;
}
dbg->bp[index].enable = FALSE;
return MRB_DEBUG_OK;
}
int32_t
mrb_debug_disable_break_all( mrb_state *mrb, mrb_debug_context *dbg )
{
uint32_t i;
if((mrb == NULL) || (dbg == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
for(i = 0 ; i < dbg->bpnum; i++) {
dbg->bp[i].enable = FALSE;
}
return MRB_DEBUG_OK;
}
static mrb_bool
check_start_pc_for_line( mrb_irep *irep, mrb_code *pc, uint16_t line )
{
if( pc > irep->iseq ) {
if( line == mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq - 1))) {
return FALSE;
}
}
return TRUE;
}
int32_t
mrb_debug_check_breakpoint_line( mrb_state *mrb, mrb_debug_context *dbg, const char *file, uint16_t line )
{
mrb_debug_breakpoint *bp;
mrb_debug_linepoint *line_p;
int i;
if((mrb == NULL) || (dbg == NULL) || (file == NULL) || (line <= 0)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
if(!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
return MRB_DEBUG_OK;
}
bp = dbg->bp;
for(i=0; i<MAX_BREAKPOINT; i++) {
switch (bp->type) {
case MRB_DEBUG_BPTYPE_LINE:
if(bp->enable == TRUE) {
line_p = &bp->point.linepoint;
if((strcmp(line_p->file, file) == 0) && (line_p->lineno == line)) {
return bp->bpno;
}
}
break;
case MRB_DEBUG_BPTYPE_METHOD:
break;
case MRB_DEBUG_BPTYPE_NONE:
default:
return MRB_DEBUG_OK;
}
bp++;
}
return MRB_DEBUG_OK;
}
int32_t
mrb_debug_check_breakpoint_method( mrb_state *mrb, mrb_debug_context *dbg, struct RClass *class_obj, mrb_sym method_sym, mrb_bool* isCfunc )
{
mrb_debug_breakpoint *bp;
int32_t bpno;
int i;
if((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
bp = dbg->bp;
for(i=0; i<MAX_BREAKPOINT; i++) {
if(bp->type == MRB_DEBUG_BPTYPE_METHOD) {
if(bp->enable == TRUE) {
bpno = compare_break_method(mrb, bp, class_obj, method_sym, isCfunc);
if(bpno > 0) {
return bpno;
}
}
}
else if(bp->type == MRB_DEBUG_BPTYPE_NONE) {
break;
}
bp++;
}
return 0;
}
/*
** apibreak.h
**
*/
#ifndef APIBREAK_H_
#define APIBREAK_H_
#include "mruby.h"
#include "mrdb.h"
int32_t mrb_debug_set_break_line( mrb_state *, mrb_debug_context *, const char *, uint16_t );
int32_t mrb_debug_set_break_method( mrb_state *, mrb_debug_context *, const char *, const char * );
int32_t mrb_debug_get_breaknum( mrb_state *, mrb_debug_context * );
int32_t mrb_debug_get_break_all( mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint bp[]);
int32_t mrb_debug_get_break( mrb_state *, mrb_debug_context *, uint32_t, mrb_debug_breakpoint * );
int32_t mrb_debug_delete_break( mrb_state *, mrb_debug_context *, uint32_t );
int32_t mrb_debug_delete_break_all( mrb_state *, mrb_debug_context * );
int32_t mrb_debug_enable_break( mrb_state *, mrb_debug_context *, uint32_t );
int32_t mrb_debug_enable_break_all( mrb_state *, mrb_debug_context * );
int32_t mrb_debug_disable_break( mrb_state *, mrb_debug_context *, uint32_t );
int32_t mrb_debug_disable_break_all( mrb_state *, mrb_debug_context * );
int32_t mrb_debug_check_breakpoint_line( mrb_state *, mrb_debug_context *, const char *, uint16_t );
int32_t mrb_debug_check_breakpoint_method( mrb_state *, mrb_debug_context *, struct RClass *, mrb_sym, mrb_bool* );
#endif /* APIBREAK_H_ */
/*
* apilist.c
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "mrdb.h"
#include "mrdberror.h"
#include "apilist.h"
#include "mruby/compile.h"
#include "mruby/irep.h"
#include "mruby/debug.h"
#define LINE_BUF_SIZE MAX_COMMAND_LINE
typedef struct source_file {
char *path;
uint16_t lineno;
FILE *fp;
} source_file;
static void
source_file_free(mrb_state *mrb, source_file *file)
{
if (file != NULL) {
if (file->path != NULL) {
mrb_free(mrb, file->path);
}
if (file->fp != NULL) {
fclose(file->fp);
file->fp = NULL;
}
mrb_free(mrb, file);
}
}
static char*
build_path(mrb_state *mrb, const char *dir, const char *base)
{
int len;
char *path = NULL;
len = strlen(base) + 1;
if (strcmp(dir, ".")) {
len += strlen(dir) + strlen("/");
}
if ((path = mrb_malloc(mrb, len)) != NULL) {
memset(path, 0, len);
if (strcmp(dir, ".")) {
strcat(path, dir);
strcat(path, "/");
}
strcat(path, base);
}
return path;
}
static char*
dirname(mrb_state *mrb, const char *path)
{
size_t len;
char *p, *dir;
if (path == NULL) {
return NULL;
}
p = strrchr(path, '/');
len = p != NULL ? p - path : strlen(path);
if ((dir = mrb_malloc(mrb, len + 1)) != NULL) {
strncpy(dir, path, len);
}
return dir;
}
static source_file*
source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename)
{
source_file *file = NULL;
if ((file = mrb_malloc(mrb, sizeof(source_file))) == NULL) {
return NULL;
}
memset(file, '\0', sizeof(source_file));
file->fp = fopen(filename, "rb");
if (file->fp == NULL) {
source_file_free(mrb, file);
return NULL;
}
file->lineno = 1;
file->path = mrb_malloc(mrb, strlen(filename) + 1);
strcpy(file->path, filename);
return file;
}
static mrb_bool
remove_newlines(char *s, FILE *fp)
{
char c, *p;
size_t len;
if ((len = strlen(s)) == 0) {
return FALSE;
}
p = s + len - 1;
if (*p != '\r' && *p != '\n') {
return FALSE;
}
if (*p == '\r') {
/* peek the next character and skip '\n' */
if ((unsigned char)(c = fgetc(fp)) != '\n') {
ungetc(c, fp);
}
}
/* remove trailing newline characters */
while (s <= p && (*p == '\r' || *p == '\n')) {
*p-- = '\0';
}
return TRUE;
}
static void
show_lines(source_file *file, uint16_t line_min, uint16_t line_max)
{
char buf[LINE_BUF_SIZE];
int show_lineno = 1, found_newline = 0, is_printed = 0;
if (file->fp == NULL) {
return;
}
while (fgets(buf, sizeof(buf), file->fp) != NULL) {
found_newline = remove_newlines(buf, file->fp);
if (line_min <= file->lineno) {
if (show_lineno) {
printf("%-8d", file->lineno);
}
show_lineno = found_newline;
printf(found_newline ? "%s\n" : "%s", buf);
is_printed = 1;
}
if (found_newline) {
if (line_max < ++file->lineno) {
break;
}
}
}
if (is_printed && !found_newline) {
printf("\n");
}
}
char*
mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, const char *filename)
{
int i;
FILE *fp;
const char *search_path[3];
char *path = NULL;
search_path[0] = srcpath;
search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->root_irep, 0));
search_path[2] = ".";
for (i = 0; i < 3; i++) {
if (search_path[i] == NULL) {
continue;
}
if ((path = build_path(mrb, search_path[i], filename)) == NULL) {
continue;
}
if ((fp = fopen(path, "rb")) == NULL) {
mrb_free(mrb, path);
path = NULL;
continue;
}
fclose(fp);
break;
}
return path;
}
int32_t
mrb_debug_list(mrb_state *mrb, mrb_debug_context *dbg, char *filename, uint16_t line_min, uint16_t line_max)
{
char *ext;
source_file *file;
if (mrb == NULL || dbg == NULL || filename == NULL) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
ext = strrchr(filename, '.');
if (ext == NULL || strcmp(ext, ".rb")) {
printf("List command only supports .rb file.\n");
return MRB_DEBUG_INVALID_ARGUMENT;
}
if (line_min > line_max) {
return MRB_DEBUG_INVALID_ARGUMENT;
}
if ((file = source_file_new(mrb, dbg, filename)) != NULL) {
show_lines(file, line_min, line_max);
source_file_free(mrb, file);
return MRB_DEBUG_OK;
}
else {
printf("Invalid source file named %s.\n", filename);
return MRB_DEBUG_INVALID_ARGUMENT;
}
}
/*
* apilist.h
*/
#ifndef APILIST_H_
#define APILIST_H_
#include "mruby.h"
#include "mrdb.h"
int32_t mrb_debug_list(mrb_state *, mrb_debug_context *, char *, uint16_t, uint16_t);
char* mrb_debug_get_source(mrb_state *, mrdb_state *, const char *, const char *);
#endif /* APILIST_H_ */
/*
** apiprint.c
**
*/
#include <string.h>
#include "mrdb.h"
#include "mruby/value.h"
#include "mruby/class.h"
#include "mruby/compile.h"
#include "mruby/error.h"
#include "mruby/numeric.h"
#include "mruby/string.h"
#include "apiprint.h"
static void
mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len)
{
mrbc_context *c;
c = mrbc_context_new(mrb);
c->no_exec = TRUE;
c->capture_errors = TRUE;
c->filename = (char*)dbg->prvfile;
c->lineno = dbg->prvline;
/* Load program */
mrb_load_nstring_cxt(mrb, expr, len, c);
mrbc_context_free(mrb, c);
}
mrb_value
mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc)
{
void *tmp;
mrb_value ruby_code;
mrb_value s;
mrb_value v;
mrb_value recv;
/* disable code_fetch_hook */
tmp = mrb->code_fetch_hook;
mrb->code_fetch_hook = NULL;
mrdb_check_syntax(mrb, dbg, expr, len);
if (mrb->exc) {
v = mrb_obj_value(mrb->exc);
mrb->exc = 0;
}
else {
/*
* begin
* expr
* rescue => e
* e
* end
*/
ruby_code = mrb_str_new_cstr(mrb, "begin\n");
ruby_code = mrb_str_cat(mrb, ruby_code, expr, len);
ruby_code = mrb_str_cat_cstr(mrb, ruby_code, "\nrescue => e\ne\nend");
recv = dbg->regs[0];
v = mrb_funcall(mrb, recv, "instance_eval", 1, ruby_code);
}
if (exc) {
*exc = mrb_obj_is_kind_of(mrb, v, mrb->eException_class);
}
s = mrb_funcall(mrb, v, "inspect", 0);
/* enable code_fetch_hook */
mrb->code_fetch_hook = tmp;
return s;
}
/*
* apiprint.h
*/
#ifndef APIPRINT_H_
#define APIPRINT_H_
#include "mruby.h"
#include "mrdb.h"
mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*);
#endif /* APIPRINT_H_ */
/*
** cmdbreak.c
**
*/
#include <ctype.h>
#include <string.h>
#include "mruby.h"
#include "mruby/dump.h"
#include "mruby/debug.h"
#include "mruby/string.h"
#include "mrdb.h"
#include "mrdberror.h"
#include "apibreak.h"
#define BREAK_SET_MSG_LINE "Breakpoint %d: file %s, line %d.\n"
#define BREAK_SET_MSG_METHOD "Breakpoint %d: method %s.\n"
#define BREAK_SET_MSG_CLASS_METHOD "Breakpoint %d: class %s, method %s.\n"
#define BREAK_INFO_MSG_HEADER "Num Type Enb What"
#define BREAK_INFO_MSG_LINEBREAK "%-8ubreakpoint %s at %s:%u\n"
#define BREAK_INFO_MSG_METHODBREAK "%-8ubreakpoint %s in %s:%s\n"
#define BREAK_INFO_MSG_METHODBREAK_NOCLASS "%-8ubreakpoint %s in %s\n"
#define BREAK_INFO_MSG_ENABLE "y"
#define BREAK_INFO_MSG_DISABLE "n"
#define BREAK_ERR_MSG_INVALIDARG "Internal error."
#define BREAK_ERR_MSG_BLANK "Try \'help break\' for more information."
#define BREAK_ERR_MSG_RANGEOVER "The line number range is from 1 to 65535."
#define BREAK_ERR_MSG_NUMOVER "Exceeded the setable number of breakpoint."
#define BREAK_ERR_MSG_NOOVER "Breakno is over the available number.Please 'quit' and restart mrdb."
#define BREAK_ERR_MSG_INVALIDSTR "String \'%s\' is invalid.\n"
#define BREAK_ERR_MSG_INVALIDLINENO "Line %d in file \"%s\" is unavailable.\n"
#define BREAK_ERR_MSG_INVALIDCLASS "Class name \'%s\' is invalid.\n"
#define BREAK_ERR_MSG_INVALIDMETHOD "Method name \'%s\' is invalid.\n"
#define BREAK_ERR_MSG_INVALIDFILE "Source file named \"%s\" is unavailable.\n"
#define BREAK_ERR_MSG_INVALIDBPNO "warning: bad breakpoint number at or near '%s'\n"
#define BREAK_ERR_MSG_INVALIDBPNO_INFO "Args must be numbers variables."
#define BREAK_ERR_MSG_NOBPNO "No breakpoint number %d.\n"
#define BREAK_ERR_MSG_NOBPNO_INFO "No breakpoint matching '%d'\n"
#define BREAK_ERR_MSG_NOBPNO_INFOALL "No breakpoints."
#define LINENO_MAX_DIGIT 6
#define BPNO_LETTER_NUM 9
typedef int32_t (*all_command_func)(mrb_state *, mrb_debug_context *);
typedef int32_t (*select_command_func)(mrb_state *, mrb_debug_context *, uint32_t);
static void
print_api_common_error(int32_t error)
{
switch(error) {
case MRB_DEBUG_INVALID_ARGUMENT:
puts(BREAK_ERR_MSG_INVALIDARG);
break;
default:
break;
}
}
#undef STRTOUL
#define STRTOUL(ul,s) \
ul = 0; \
for(int i=0; ISDIGIT(s[i]); i++) ul = 10*ul + (s[i] -'0');
static int32_t
parse_breakpoint_no(char* args)
{
char* ps = args;
uint32_t l;
if((*ps == '0')||(strlen(ps) >= BPNO_LETTER_NUM)) {
return 0;
}
while( !(ISBLANK(*ps)||ISCNTRL(*ps)) ) {
if(!ISDIGIT(*ps)) {
return 0;
}
ps++;
}
STRTOUL(l, args);
return l;
}
static mrb_bool
exe_set_command_all(mrb_state *mrb, mrdb_state *mrdb, all_command_func func)
{
int32_t ret = MRB_DEBUG_OK;
if(mrdb->wcnt == 1) {
ret = func(mrb, mrdb->dbg);
print_api_common_error(ret);
return TRUE;
}
return FALSE;
}
static void
exe_set_command_select(mrb_state *mrb, mrdb_state *mrdb, select_command_func func)
{
char* ps;
int32_t ret = MRB_DEBUG_OK;
int32_t bpno = 0;
int32_t i;
for(i=1; i<mrdb->wcnt; i++) {
ps = mrdb->words[i];
bpno = parse_breakpoint_no(ps);
if(bpno == 0) {
printf(BREAK_ERR_MSG_INVALIDBPNO, ps);
break;
}
ret = func(mrb, mrdb->dbg, (uint32_t)bpno);
if(ret == MRB_DEBUG_BREAK_INVALID_NO) {
printf(BREAK_ERR_MSG_NOBPNO, bpno);
}
else if(ret != MRB_DEBUG_OK) {
print_api_common_error(ret);
}
}
}
mrb_debug_bptype
check_bptype(char* args)
{
char* ps = args;
if(ISBLANK(*ps)||ISCNTRL(*ps)) {
puts(BREAK_ERR_MSG_BLANK);
return MRB_DEBUG_BPTYPE_NONE;
}
if(!ISDIGIT(*ps)) {
return MRB_DEBUG_BPTYPE_METHOD;
}
while( !(ISBLANK(*ps)||ISCNTRL(*ps)) ) {
if(!ISDIGIT(*ps)) {
printf(BREAK_ERR_MSG_INVALIDSTR, args);
return MRB_DEBUG_BPTYPE_NONE;
}
ps++;
}
if((*args == '0')||(strlen(args) >= LINENO_MAX_DIGIT)) {
puts(BREAK_ERR_MSG_RANGEOVER);
return MRB_DEBUG_BPTYPE_NONE;
}
return MRB_DEBUG_BPTYPE_LINE;
}
static void
print_breakpoint(mrb_debug_breakpoint *bp)
{
char* enable_letter[] = {BREAK_INFO_MSG_DISABLE, BREAK_INFO_MSG_ENABLE};
if(bp->type == MRB_DEBUG_BPTYPE_LINE) {
printf(BREAK_INFO_MSG_LINEBREAK,
bp->bpno, enable_letter[bp->enable], bp->point.linepoint.file, bp->point.linepoint.lineno);
}
else {
if(bp->point.methodpoint.class_name == NULL) {
printf(BREAK_INFO_MSG_METHODBREAK_NOCLASS,
bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.method_name);
}
else {
printf(BREAK_INFO_MSG_METHODBREAK,
bp->bpno, enable_letter[bp->enable], bp->point.methodpoint.class_name, bp->point.methodpoint.method_name);
}
}
}
static void
info_break_all(mrb_state *mrb, mrdb_state *mrdb)
{
int32_t bpnum = 0;
int32_t i = 0;
int32_t ret = MRB_DEBUG_OK;
mrb_debug_breakpoint *bp_list;
bpnum = mrb_debug_get_breaknum(mrb, mrdb->dbg);
if(bpnum < 0) {
print_api_common_error(bpnum);
return;
}
else if(bpnum == 0) {
puts(BREAK_ERR_MSG_NOBPNO_INFOALL);
return;
}
bp_list = (mrb_debug_breakpoint*)mrb_malloc(mrb, bpnum * sizeof(mrb_debug_breakpoint));
ret = mrb_debug_get_break_all(mrb, mrdb->dbg, (uint32_t)bpnum, bp_list);
if(ret < 0) {
print_api_common_error(ret);
return;
}
puts(BREAK_INFO_MSG_HEADER);
for(i = 0 ; i < bpnum ; i++) {
print_breakpoint(&bp_list[i]);
}
mrb_free(mrb, bp_list);
}
static void
info_break_select(mrb_state *mrb, mrdb_state *mrdb)
{
int32_t ret = MRB_DEBUG_OK;
int32_t bpno = 0;
char* ps = mrdb->command;
mrb_debug_breakpoint bp;
mrb_bool isFirst = TRUE;
int32_t i;
for(i=2; i<mrdb->wcnt; i++) {
ps = mrdb->words[i];
bpno = parse_breakpoint_no(ps);
if(bpno == 0) {
puts(BREAK_ERR_MSG_INVALIDBPNO_INFO);
break;
}
ret = mrb_debug_get_break(mrb, mrdb->dbg, bpno, &bp);
if(ret == MRB_DEBUG_BREAK_INVALID_NO) {
printf(BREAK_ERR_MSG_NOBPNO_INFO, bpno);
break;
}
else if(ret != MRB_DEBUG_OK) {
print_api_common_error(ret);
break;
}
else if(isFirst == TRUE) {
isFirst = FALSE;
puts(BREAK_INFO_MSG_HEADER);
}
print_breakpoint(&bp);
}
}
mrb_debug_bptype
parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
{
mrb_debug_context *dbg = mrdb->dbg;
char *args;
char *body;
mrb_debug_bptype type;
uint32_t l;
if(mrdb->wcnt <= 1) {
puts(BREAK_ERR_MSG_BLANK);
return MRB_DEBUG_BPTYPE_NONE;
}
args = mrdb->words[1];
if((body = strchr(args, ':')) == NULL) {
body = args;
type = check_bptype(body);
} else {
if(body == args) {
printf(BREAK_ERR_MSG_INVALIDSTR, args);
return MRB_DEBUG_BPTYPE_NONE;
}
*body = '\0';
type = check_bptype(++body);
}
switch(type) {
case MRB_DEBUG_BPTYPE_LINE:
STRTOUL(l, body);
if( l <= 65535 ) {
*line = l;
*file = (body == args)? mrb_debug_get_filename(dbg->irep, (uint32_t)(dbg->pc - dbg->irep->iseq)): args;
} else {
puts(BREAK_ERR_MSG_RANGEOVER);
type = MRB_DEBUG_BPTYPE_NONE;
}
break;
case MRB_DEBUG_BPTYPE_METHOD:
if(body == args) {
/* method only */
if( ISUPPER(*body)||ISLOWER(*body)||(*body == '_') ) {
*method = body;
*cname = NULL;
} else {
printf(BREAK_ERR_MSG_INVALIDMETHOD, args);
type = MRB_DEBUG_BPTYPE_NONE;
}
} else {
if( ISUPPER(*args) ) {
switch(*body) {
case '@': case '$': case '?': case '.': case ',': case ':':
case ';': case '#': case '\\': case '\'': case '\"':
printf(BREAK_ERR_MSG_INVALIDMETHOD, body);
type = MRB_DEBUG_BPTYPE_NONE;
break;
default:
*method = body;
*cname = args;
break;
}
} else {
printf(BREAK_ERR_MSG_INVALIDCLASS, args);
type = MRB_DEBUG_BPTYPE_NONE;
}
}
break;
case MRB_DEBUG_BPTYPE_NONE:
default:
break;
}
return type;
}
dbgcmd_state
dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb)
{
mrb_debug_bptype type;
mrb_debug_context *dbg = mrdb->dbg;
const char *file = NULL;
uint32_t line = 0;
char *cname = NULL;
char *method = NULL;
int32_t ret;
type = parse_breakcommand(mrdb, &file, &line, &cname, &method);
switch (type) {
case MRB_DEBUG_BPTYPE_LINE:
ret = mrb_debug_set_break_line(mrb, dbg, file, line);
break;
case MRB_DEBUG_BPTYPE_METHOD:
ret = mrb_debug_set_break_method(mrb, dbg, cname, method);
break;
case MRB_DEBUG_BPTYPE_NONE:
default:
return DBGST_PROMPT;
}
if (ret >= 0) {
if (type == MRB_DEBUG_BPTYPE_LINE) {
printf(BREAK_SET_MSG_LINE, ret, file, line);
} else if ((type == MRB_DEBUG_BPTYPE_METHOD)&&(cname == NULL)) {
printf(BREAK_SET_MSG_METHOD, ret, method);
} else {
printf(BREAK_SET_MSG_CLASS_METHOD, ret, cname, method);
}
} else {
switch (ret) {
case MRB_DEBUG_BREAK_INVALID_LINENO:
printf(BREAK_ERR_MSG_INVALIDLINENO, line, file);
break;
case MRB_DEBUG_BREAK_INVALID_FILE:
printf(BREAK_ERR_MSG_INVALIDFILE, file);
break;
case MRB_DEBUG_BREAK_NUM_OVER:
puts(BREAK_ERR_MSG_NUMOVER);
break;
case MRB_DEBUG_BREAK_NO_OVER:
puts(BREAK_ERR_MSG_NOOVER);
break;
case MRB_DEBUG_INVALID_ARGUMENT:
puts(BREAK_ERR_MSG_INVALIDARG);
break;
case MRB_DEBUG_NOBUF:
puts("T.B.D.");
break;
default:
break;
}
}
return DBGST_PROMPT;
}
dbgcmd_state
dbgcmd_info_break(mrb_state *mrb, mrdb_state *mrdb)
{
if(mrdb->wcnt == 2) {
info_break_all(mrb, mrdb);
}
else {
info_break_select(mrb, mrdb);
}
return DBGST_PROMPT;
}
dbgcmd_state
dbgcmd_delete(mrb_state *mrb, mrdb_state *mrdb)
{
mrb_bool ret = FALSE;
ret = exe_set_command_all(mrb, mrdb, mrb_debug_delete_break_all);
if(ret != TRUE) {
exe_set_command_select(mrb, mrdb, mrb_debug_delete_break);
}
return DBGST_PROMPT;
}
dbgcmd_state
dbgcmd_enable(mrb_state *mrb, mrdb_state *mrdb)
{
mrb_bool ret = FALSE;
ret = exe_set_command_all(mrb, mrdb, mrb_debug_enable_break_all);
if(ret != TRUE) {
exe_set_command_select(mrb, mrdb, mrb_debug_enable_break);
}
return DBGST_PROMPT;
}
dbgcmd_state
dbgcmd_disable(mrb_state *mrb, mrdb_state *mrdb)
{
mrb_bool ret = FALSE;
ret = exe_set_command_all(mrb, mrdb, mrb_debug_disable_break_all);
if(ret != TRUE) {
exe_set_command_select(mrb, mrdb, mrb_debug_disable_break);
}
return DBGST_PROMPT;
}
/*
** cmdmisc.c - mruby debugger miscellaneous command functions
**
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "apilist.h"
#include "mruby/compile.h"
typedef struct help_msg {
const char *cmd1;
const char *cmd2;
const char *short_msg;
const char *long_msg;
} help_msg;
static help_msg help_msg_list[] = {
{
"b[reak]", NULL, "Set breakpoint",
"Usage: break [file:]line\n"
" break [class:]method\n"
"\n"
"Set breakpoint at specified line or method.\n"
"If \'[file:]line\' is specified, break at start of code for that line (in a file).\n"
"If \'[class:]method\' is specified, break at start of code for that method (of the class).\n"
},
{
"c[ontinue]", NULL, "Continue program being debugged",
"Usage: continue [N]\n"
"\n"
"Continue program stopped by a breakpoint.\n"
"If N, which is non negative value, is passed,\n"
"proceed program until the N-th breakpoint is comming.\n"
"If N is not passed, N is assumed 1.\n"
},
{
"d[elete]", NULL, "Delete some breakpoints",
"Usage: delete [bpno1 [bpno2 [... [bpnoN]]]]\n"
"\n"
"Delete some breakpoints.\n"
"Arguments are breakpoint numbers with spaces in between.\n"
"To delete all breakpoints, give no argument.\n"
},
{
"dis[able]", NULL, "Disable some breakpoints",
"Usage: disable [bpno1 [bpno2 [... [bpnoN]]]]\n"
"\n"
"Disable some breakpoints.\n"
"Arguments are breakpoint numbers with spaces in between.\n"
"To disable all breakpoints, give no argument.\n"
},
{
"en[able]", NULL, "Enable some breakpoints",
"Usage: enable [bpno1 [bpno2 [... [bpnoN]]]]\n"
"\n"
"Enable some breakpoints.\n"
"Arguments are breakpoint numbers with spaces in between.\n"
"To enable all breakpoints, give no argument.\n"
},
{
"ev[al]", NULL, "Evaluate expression",
"Usage: eval expr\n"
"\n"
"It evaluates and prints the value of the mruby expression.\n"
"This is equivalent to the \'print\' command.\n"
},
{
"h[elp]", NULL, "Print this help",
"Usage: help [command]\n"
"\n"
"With no arguments, help displays a short list of commands.\n"
"With a command name as help argument, help displays how to use that command.\n"
},
{
"i[nfo]", "b[reakpoints]", "Status of breakpoints",
"Usage: info breakpoints [bpno1 [bpno2 [... [bpnoN]]]]\n"
"\n"
"Status of specified breakpoints (all user-settable breakpoints if no argument).\n"
"Arguments are breakpoint numbers with spaces in between.\n"
},
{
"l[ist]", NULL, "List specified line",
"Usage: list\n"
" list first[,last]\n"
" list filename:first[,last]\n"
"\n"
"Print lines from a source file.\n"
"\n"
"With first and last, list prints lines from first to last.\n"
"When last is empty, it stands for ten lines away from first.\n"
"With filename, list prints lines in the specified source file.\n"
},
{
"p[rint]", NULL, "Print value of expression",
"Usage: print expr\n"
"\n"
"It evaluates and prints the value of the mruby expression.\n"
"This is equivalent to the \'eval\' command.\n"
},
{
"q[uit]", NULL, "Exit mrdb",
"Usage: quit\n"
"\n"
"Exit mrdb.\n"
},
{
"r[un]", NULL, "Start debugged program",
"Usage: run\n"
"\n"
"Start debugged program.\n"
},
{
"s[tep]", NULL, "Step program until it reaches a different source line",
"Usage: step\n"
"\n"
"Step program until it reaches a different source line.\n"
},
{ NULL, NULL, NULL, NULL }
};
typedef struct listcmd_parser_state {
mrb_bool parse_error;
mrb_bool has_line_min;
mrb_bool has_line_max;
char *filename;
uint16_t line_min;
uint16_t line_max;
} listcmd_parser_state;
static listcmd_parser_state*
listcmd_parser_state_new(mrb_state *mrb)
{
listcmd_parser_state *st = mrb_malloc(mrb, sizeof(listcmd_parser_state));
memset(st, 0, sizeof(listcmd_parser_state));
return st;
}
static void
listcmd_parser_state_free(mrb_state *mrb, listcmd_parser_state *st)
{
if (st != NULL) {
if (st->filename != NULL) {
mrb_free(mrb, st->filename);
}
mrb_free(mrb, st);
}
}
static mrb_bool
parse_uint(char **sp, uint16_t *n)
{
char *p;
int i;
if (*sp == NULL || **sp == '\0') {
return FALSE;
}
for (p = *sp; *p != '\0' && ISDIGIT(*p); p++) ;
if (p != *sp && (i = atoi(*sp)) >= 0) {
*n = (uint16_t)i;
*sp = p;
return TRUE;
}
return FALSE;
}
static mrb_bool
skip_char(char **sp, char c)
{
if (*sp != NULL && **sp == c) {
++*sp;
return TRUE;
}
return FALSE;
}
static mrb_bool
parse_lineno(mrb_state *mrb, char **sp, listcmd_parser_state *st)
{
if (*sp == NULL || **sp == '\0') {
return FALSE;
}
st->has_line_min = FALSE;
st->has_line_max = FALSE;
if (parse_uint(sp, &st->line_min)) {
st->has_line_min = TRUE;
}
else {
return FALSE;
}
if (skip_char(sp, ',')) {
if (parse_uint(sp, &st->line_max)) {
st->has_line_max = TRUE;
}
else {
st->parse_error = TRUE;
return FALSE;
}
}
return TRUE;
}
static mrb_bool
parse_filename(mrb_state *mrb, char **sp, listcmd_parser_state *st)
{
char *p;
int len;
if (st->filename != NULL) {
mrb_free(mrb, st->filename);
st->filename = NULL;
}
if ((p = strchr(*sp, ':')) != NULL) {
len = p - *sp;
}
else {
len = strlen(*sp);
}
if (len > 0) {
st->filename = mrb_malloc(mrb, len + 1);
strncpy(st->filename, *sp, len);
st->filename[len] = '\0';
*sp += len;
return TRUE;
}
else {
return FALSE;
}
}
char*
replace_ext(mrb_state *mrb, const char *filename, const char *ext)
{
size_t len;
char *p, *s;
if (filename == NULL) {
return NULL;
}
if ((p = strrchr(filename, '.')) != NULL && strchr(p, '/') == NULL) {
len = p - filename;
}
else {
len = strlen(filename);
}
if ((s = mrb_malloc(mrb, len + strlen(ext) + 1)) != NULL) {
memset(s, '\0', len + strlen(ext) + 1);
strncpy(s, filename, len);
strcat(s, ext);
}
return s;
}
static mrb_bool
parse_listcmd_args(mrb_state *mrb, mrdb_state *mrdb, listcmd_parser_state *st)
{
char *p;
switch (mrdb->wcnt) {
case 2:
p = mrdb->words[1];
/* mrdb->words[1] ::= <lineno> | <filename> ':' <lineno> | <filename> */
if (!parse_lineno(mrb, &p, st)) {
if (parse_filename(mrb, &p, st)) {
if (skip_char(&p, ':')) {
if (!parse_lineno(mrb, &p, st)) {
st->parse_error = TRUE;
}
}
}
else {
st->parse_error = TRUE;
}
}
if (*p != '\0') {
st->parse_error = TRUE;
}
break;
case 1:
case 0:
/* do nothing */
break;
default:
st->parse_error = TRUE;
printf("too many arguments\n");
break;
}
if (!st->parse_error) {
if (!st->has_line_min) {
st->line_min = (!st->filename && mrdb->dbg->prvline > 0) ? mrdb->dbg->prvline : 1;
}
if (!st->has_line_max) {
st->line_max = st->line_min + 9;
}
if (st->filename == NULL) {
if (mrdb->dbg->prvfile && strcmp(mrdb->dbg->prvfile, "-")) {
st->filename = replace_ext(mrb, mrdb->dbg->prvfile, ".rb");
}
}
}
if (st->parse_error || st->filename == NULL) {
return FALSE;
}
return TRUE;
}
static mrb_bool
check_cmd_pattern(const char *pattern, const char *cmd)
{
char *lbracket, *rbracket, *p, *q;
if (pattern == NULL && cmd == NULL) {
return TRUE;
}
if (pattern == NULL || cmd == NULL) {
return FALSE;
}
if((lbracket = strchr(pattern, '[')) == NULL) {
return !strcmp(pattern, cmd);
}
if ((rbracket = strchr(pattern, ']')) == NULL) {
return FALSE;
}
if (strncmp(pattern, cmd, lbracket - pattern)) {
return FALSE;
}
p = lbracket + 1;
q = (char *)cmd + (lbracket - pattern);
for ( ; p < rbracket && *q != '\0'; p++, q++) {
if (*p != *q) {
break;
}
}
return *q == '\0';
}
static help_msg*
get_help_msg(char *cmd1, char *cmd2)
{
help_msg *p;
if (cmd1 == NULL) {
return NULL;
}
for (p = help_msg_list; p->cmd1 != NULL; p++) {
if (check_cmd_pattern(p->cmd1, cmd1) && check_cmd_pattern(p->cmd2, cmd2)) {
return p;
}
}
return NULL;
}
static mrb_bool
show_short_help(void)
{
help_msg *p;
printf("Commands\n");
for (p = help_msg_list; p->cmd1 != NULL; p++) {
if (p->cmd2 == NULL) {
printf(" %s -- %s\n", p->cmd1, p->short_msg);
}
else {
printf(" %s %s -- %s\n", p->cmd1, p->cmd2, p->short_msg);
}
}
return TRUE;
}
static mrb_bool
show_long_help(char *cmd1, char *cmd2)
{
help_msg *help;
if ((help = get_help_msg(cmd1, cmd2)) == NULL) {
return FALSE;
}
printf("%s", help->long_msg);
return TRUE;
}
dbgcmd_state
dbgcmd_list(mrb_state *mrb, mrdb_state *mrdb)
{
char *filename;
listcmd_parser_state *st = listcmd_parser_state_new(mrb);
if (parse_listcmd_args(mrb, mrdb, st)) {
if ((filename = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, st->filename)) == NULL) {
filename = st->filename;
}
mrb_debug_list(mrb, mrdb->dbg, filename, st->line_min, st->line_max);
listcmd_parser_state_free(mrb, st);
if (filename != NULL && filename != st->filename) {
mrb_free(mrb, filename);
}
}
return DBGST_PROMPT;
}
dbgcmd_state
dbgcmd_help(mrb_state *mrb, mrdb_state *mrdb)
{
mrb_bool is_valid;
int i;
switch (mrdb->wcnt) {
case 0:
case 1:
is_valid = show_short_help();
break;
case 2:
is_valid = show_long_help(mrdb->words[1], NULL);
break;
case 3:
is_valid = show_long_help(mrdb->words[1], mrdb->words[2]);
break;
default:
is_valid = FALSE;
break;
}
if (!is_valid) {
printf("Invalid command \"");
for (i = 1; i < mrdb->wcnt; i++) {
printf("%s%s", i == 1 ? "" : " ", mrdb->words[i]);
}
printf("\". Try \"help\".\n");
}
return DBGST_PROMPT;
}
dbgcmd_state
dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb)
{
switch (mrdb->dbg->xm) {
case DBG_RUN:
case DBG_STEP:
case DBG_NEXT:
while (1) {
char c;
int buf;
printf("The program is running. Exit anyway? (y or n) ");
if ((buf = getchar()) == EOF) {
mrdb->dbg->xm = DBG_QUIT;
break;
}
c = buf;
while (buf != '\n' && (buf = getchar()) != EOF) ;
if (c == 'y' || c == 'Y') {
mrdb->dbg->xm = DBG_QUIT;
break;
}
else if (c == 'n' || c == 'N') {
break;
}
else {
printf("Please answer y or n.\n");
}
}
break;
default:
mrdb->dbg->xm = DBG_QUIT;
break;
}
if (mrdb->dbg->xm == DBG_QUIT) {
struct RClass *exc;
exc = mrb_define_class(mrb, "DebuggerExit", mrb_class_get(mrb, "Exception"));
mrb_raise(mrb, exc, "Exit mrdb.");
}
return DBGST_PROMPT;
}
/*
** cmdprint.c - mruby debugger print command functions
**
*/
#include <string.h>
#include "mrdb.h"
#include "mruby/value.h"
#include "mruby/class.h"
#include "mruby/compile.h"
#include "mruby/error.h"
#include "mruby/numeric.h"
#include "mruby/string.h"
#include "apiprint.h"
dbgcmd_state
dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb)
{
mrb_value expr;
mrb_value result;
mrb_value s;
uint8_t wcnt;
int ai;
if (mrdb->wcnt <= 1) {
puts("Parameter not specified.");
return DBGST_PROMPT;
}
ai = mrb_gc_arena_save(mrb);
/* eval expr */
expr = mrb_str_new_cstr(mrb, NULL);
for (wcnt=1; wcnt<mrdb->wcnt; wcnt++) {
expr = mrb_str_cat_cstr(mrb, expr, " ");
expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]);
}
result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL);
/* $print_no = result */
s = mrb_str_cat_cstr(mrb, result, "\0");
printf("$%lu = %s\n", (unsigned long)mrdb->print_no++, RSTRING_PTR(s));
if (mrdb->print_no == 0) {
mrdb->print_no = 1;
}
mrb_gc_arena_restore(mrb, ai);
return DBGST_PROMPT;
}
dbgcmd_state
dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb)
{
return dbgcmd_print(mrb, mrdb);
}
/*
** cmdrun.c - mruby debugger run command functions
**
*/
#include "mruby/opcode.h"
#include "mrdb.h"
dbgcmd_state
dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb)
{
mrb_debug_context *dbg = mrdb->dbg;
if( dbg->xm == DBG_INIT ){
dbg->xm = DBG_RUN;
} else {
dbg->xm = DBG_QUIT;
if( dbg->xphase == DBG_PHASE_RUNNING ){
struct RClass *exc;
puts("Start it from the beginning.");
exc = mrb_define_class(mrb, "DebuggerRestart", mrb_class_get(mrb, "Exception"));
mrb_raise(mrb, exc, "Restart mrdb.");
}
}
return DBGST_RESTART;
}
dbgcmd_state
dbgcmd_continue(mrb_state *mrb, mrdb_state *mrdb)
{
mrb_debug_context *dbg = mrdb->dbg;
int ccnt = 1;
if( mrdb->wcnt > 1 ){
sscanf(mrdb->words[1], "%d", &ccnt);
}
dbg->ccnt = (uint16_t)(ccnt > 0 ? ccnt : 1); /* count of continue */
if( dbg->xphase == DBG_PHASE_AFTER_RUN ){
puts("The program is not running.");
dbg->xm = DBG_QUIT;
} else {
dbg->xm = DBG_RUN;
}
return DBGST_CONTINUE;
}
dbgcmd_state
dbgcmd_step(mrb_state *mrb, mrdb_state *mrdb)
{
mrdb->dbg->xm = DBG_STEP;
return DBGST_CONTINUE;
}
/*
** mrdb.c - mruby debugger
**
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "mruby.h"
#include "mruby/dump.h"
#include "mruby/debug.h"
#include "mruby/class.h"
#include "mruby/opcode.h"
#include "mruby/variable.h"
#include "mrdb.h"
#include "apibreak.h"
#include "apilist.h"
void mrb_show_version(mrb_state *);
void mrb_show_copyright(mrb_state *);
void mrdb_state_free(mrb_state *);
static mrb_debug_context *_debug_context = NULL;
static mrdb_state *_mrdb_state = NULL;
struct _args {
FILE *rfp;
char* fname;
char* srcpath;
int argc;
char** argv;
mrb_bool mrbfile : 1;
};
typedef struct debug_command {
const char *cmd1;
const char *cmd2;
uint8_t len1;
uint8_t len2;
uint8_t div;
debug_command_id id;
debug_command_func func;
} debug_command;
static const debug_command debug_command_list[] = {
{"break", NULL, 1, 0, 0, DBGCMD_BREAK, dbgcmd_break}, /* b[reak] */
{"continue", NULL, 1, 0, 0, DBGCMD_CONTINUE, dbgcmd_continue}, /* c[ontinue] */
{"delete", NULL, 1, 0, 1, DBGCMD_DELETE, dbgcmd_delete}, /* d[elete] */
{"disable", NULL, 3, 0, 1, DBGCMD_DISABLE, dbgcmd_disable}, /* dis[able] */
{"enable", NULL, 2, 0, 1, DBGCMD_ENABLE, dbgcmd_enable}, /* en[able] */
{"eval", NULL, 2, 0, 0, DBGCMD_EVAL, dbgcmd_eval}, /* ev[al] */
{"help", NULL, 1, 0, 1, DBGCMD_HELP, dbgcmd_help}, /* h[elp] */
{"info", "breakpoints", 1, 1, 1, DBGCMD_INFO_BREAK, dbgcmd_info_break}, /* i[nfo] b[reakpoints] */
{"list", NULL, 1, 0, 1, DBGCMD_LIST, dbgcmd_list}, /* l[ist] */
{"print", NULL, 1, 0, 0, DBGCMD_PRINT, dbgcmd_print}, /* p[rint] */
{"quit", NULL, 1, 0, 0, DBGCMD_QUIT, dbgcmd_quit}, /* q[uit] */
{"run", NULL, 1, 0, 0, DBGCMD_RUN, dbgcmd_run}, /* r[un] */
{"step", NULL, 1, 0, 1, DBGCMD_STEP, dbgcmd_step}, /* s[tep] */
{NULL}
};
static void
usage(const char *name)
{
static const char *const usage_msg[] = {
"switches:",
"-b load and execute RiteBinary (mrb) file",
"-d specify source directory",
"--version print the version",
"--copyright print the copyright",
NULL
};
const char *const *p = usage_msg;
printf("Usage: %s [switches] programfile\n", name);
while (*p) {
printf(" %s\n", *p++);
}
}
static int
parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
{
char **origargv = argv;
static const struct _args args_zero = { 0 };
*args = args_zero;
for (argc--,argv++; argc > 0; argc--,argv++) {
char *item;
if (argv[0][0] != '-') break;
item = argv[0] + 1;
switch (*item++) {
case 'b':
args->mrbfile = TRUE;
break;
case 'd':
if (item[0]) {
goto append_srcpath;
}
else if (argc > 1) {
argc--; argv++;
item = argv[0];
append_srcpath:
if (!args->srcpath) {
size_t buflen;
char *buf;
buflen = strlen(item) + 1;
buf = (char *)mrb_malloc(mrb, buflen);
memcpy(buf, item, buflen);
args->srcpath = buf;
}
else {
size_t srcpathlen;
size_t itemlen;
srcpathlen = strlen(args->srcpath);
itemlen = strlen(item);
args->srcpath =
(char *)mrb_realloc(mrb, args->srcpath, srcpathlen + itemlen + 2);
args->srcpath[srcpathlen] = '\n';
memcpy(args->srcpath + srcpathlen + 1, item, itemlen + 1);
}
}
else {
printf("%s: No path specified for -d\n", *origargv);
return EXIT_SUCCESS;
}
break;
case '-':
if (strcmp((*argv) + 2, "version") == 0) {
mrb_show_version(mrb);
exit(EXIT_SUCCESS);
}
else if (strcmp((*argv) + 2, "copyright") == 0) {
mrb_show_copyright(mrb);
exit(EXIT_SUCCESS);
}
default:
return EXIT_FAILURE;
}
}
if (args->rfp == NULL) {
if (*argv == NULL) {
printf("%s: Program file not specified.\n", *origargv);
return EXIT_FAILURE;
}
else {
args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r");
if (args->rfp == NULL) {
printf("%s: Cannot open program file. (%s)\n", *origargv, *argv);
return EXIT_FAILURE;
}
args->fname = argv[0];
argc--; argv++;
}
}
args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1));
memcpy(args->argv, argv, (argc+1) * sizeof(char*));
args->argc = argc;
return EXIT_SUCCESS;
}
static void
cleanup(mrb_state *mrb, struct _args *args)
{
if (args->rfp)
fclose(args->rfp);
if (args->srcpath)
mrb_free(mrb, args->srcpath);
if (args->argv)
mrb_free(mrb, args->argv);
mrdb_state_free(mrb);
mrb_close(mrb);
}
static mrb_debug_context*
mrb_debug_context_new(mrb_state *mrb)
{
mrb_debug_context *dbg = mrb_malloc(mrb, sizeof(mrb_debug_context));
memset(dbg, 0, sizeof(mrb_debug_context));
dbg->xm = DBG_INIT;
dbg->xphase = DBG_PHASE_BEFORE_RUN;
dbg->next_bpno = 1;
return dbg;
}
mrb_debug_context*
mrb_debug_context_get(mrb_state *mrb)
{
if (!_debug_context) {
_debug_context = mrb_debug_context_new(mrb);
}
return _debug_context;
}
void
mrb_debug_context_set(mrb_debug_context *dbg)
{
_debug_context = dbg;
}
void
mrb_debug_context_free(mrb_state *mrb)
{
if (_debug_context) {
mrb_free(mrb, _debug_context);
_debug_context = NULL;
}
}
static mrdb_state*
mrdb_state_new(mrb_state *mrb)
{
mrdb_state *mrdb = mrb_malloc(mrb, sizeof(mrb_state));
memset(mrdb, 0, sizeof(mrb_state));
mrdb->dbg = mrb_debug_context_get(mrb);
mrdb->command = mrb_malloc(mrb, MAX_COMMAND_LINE+1);
mrdb->print_no = 1;
return mrdb;
}
mrdb_state*
mrdb_state_get(mrb_state *mrb)
{
if (!_mrdb_state) {
_mrdb_state = mrdb_state_new(mrb);
}
return _mrdb_state;
}
void
mrdb_state_set(mrdb_state *mrdb)
{
_mrdb_state = mrdb;
}
void
mrdb_state_free(mrb_state *mrb)
{
mrb_debug_context_free(mrb);
if (_mrdb_state) {
mrb_free(mrb, _mrdb_state->command);
mrb_free(mrb, _mrdb_state);
_mrdb_state = NULL;
}
}
static char*
get_command(mrb_state *mrb, mrdb_state *mrdb)
{
int i;
int c;
for (i=0; i<MAX_COMMAND_LINE; i++) {
if ((c=getchar()) == EOF || c == '\n') break;
mrdb->command[i] = c;
}
if (i == 0 && feof(stdin)) {
clearerr(stdin);
strcpy(mrdb->command, "quit");
i += strlen("quit");
}
if (i == MAX_COMMAND_LINE) {
for ( ; (c=getchar()) != EOF && c !='\n'; i++) ;
}
if (i > MAX_COMMAND_LINE) {
printf("command line too long.\n");
i = 0; /* discard command data */
}
mrdb->command[i] = '\0';
return mrdb->command;
}
static char*
pick_out_word(mrb_state *mrb, char **pp)
{
char *ps;
for (ps=*pp; ISBLANK(*ps); ps++) ;
if (*ps == '\0') {
return NULL;
}
if (*ps == '\"' || *ps == '\'') {
*pp = strchr(ps+1, *ps);
if (*pp) (*pp)++;
}
else {
*pp = strpbrk(ps, " \t");
}
if (!*pp) {
*pp = ps + strlen(ps);
}
if (**pp != '\0') {
**pp = '\0';
(*pp)++;
}
return ps;
}
static debug_command*
parse_command(mrb_state *mrb, mrdb_state *mrdb, char *buf)
{
debug_command *cmd = NULL;
char *p = buf;
size_t wlen;
/* get word #1 */
mrdb->words[0] = pick_out_word(mrb, &p);
if (!mrdb->words[0]) {
return NULL;
}
mrdb->wcnt = 1;
/* set remain parameter */
for ( ; *p && ISBLANK(*p); p++) ;
if (*p) {
mrdb->words[mrdb->wcnt++] = p;
}
/* check word #1 */
for (cmd=(debug_command*)debug_command_list; cmd->cmd1; cmd++) {
wlen = strlen(mrdb->words[0]);
if (wlen >= cmd->len1 &&
strncmp(mrdb->words[0], cmd->cmd1, wlen) == 0) {
break;
}
}
if (cmd->cmd2) {
if (mrdb->wcnt > 1) {
/* get word #2 */
mrdb->words[1] = pick_out_word(mrb, &p);
if (mrdb->words[1]) {
/* update remain parameter */
for ( ; *p && ISBLANK(*p); p++) ;
if (*p) {
mrdb->words[mrdb->wcnt++] = p;
}
}
}
/* check word #1,#2 */
for ( ; cmd->cmd1; cmd++) {
wlen = strlen(mrdb->words[0]);
if (wlen < cmd->len1 ||
strncmp(mrdb->words[0], cmd->cmd1, wlen)) {
continue;
}
if (!cmd->cmd2) break; /* word #1 only */
if (mrdb->wcnt == 1) continue; /* word #2 not specified */
wlen = strlen(mrdb->words[1]);
if (wlen >= cmd->len2 &&
strncmp(mrdb->words[1], cmd->cmd2, wlen) == 0) {
break; /* word #1 and #2 */
}
}
}
/* divide remain parameters */
if (cmd->cmd1 && cmd->div) {
p = mrdb->words[--mrdb->wcnt];
for ( ; mrdb->wcnt<MAX_COMMAND_WORD; mrdb->wcnt++) {
mrdb->words[mrdb->wcnt] = pick_out_word(mrb, &p);
if (!mrdb->words[mrdb->wcnt]) {
break;
}
}
}
return cmd->cmd1 ? cmd : NULL;
}
static void
print_info_stopped_break(mrb_state *mrb, mrdb_state *mrdb)
{
mrb_debug_breakpoint bp;
int32_t ret;
uint16_t lineno;
const char *file;
const char *method_name;
const char *class_name;
ret = mrb_debug_get_break(mrb, mrdb->dbg, mrdb->dbg->stopped_bpno, &bp);
if(ret == 0) {
switch(bp.type) {
case MRB_DEBUG_BPTYPE_LINE:
file = bp.point.linepoint.file;
lineno = bp.point.linepoint.lineno;
printf("Breakpoint %d, at %s:%d\n", bp.bpno, file, lineno);
break;
case MRB_DEBUG_BPTYPE_METHOD:
method_name = bp.point.methodpoint.method_name;
class_name = bp.point.methodpoint.class_name;
if(class_name == NULL) {
printf("Breakpoint %d, %s\n", bp.bpno, method_name);
}
else {
printf("Breakpoint %d, %s:%s\n", bp.bpno, class_name, method_name);
}
if(mrdb->dbg->isCfunc) {
printf("Stopped before calling the C function.\n");
}
break;
default:
break;
}
}
}
static void
print_info_stopped_step_next(mrb_state *mrb, mrdb_state *mrdb)
{
const char* file = mrdb->dbg->prvfile;
uint16_t lineno = mrdb->dbg->prvline;
printf("%s:%d\n", file, lineno);
}
static void
print_info_stopped_code(mrb_state *mrb, mrdb_state *mrdb)
{
char* file = mrb_debug_get_source(mrb, mrdb, mrdb->srcpath, mrdb->dbg->prvfile);
uint16_t lineno = mrdb->dbg->prvline;
if(file != NULL) {
mrb_debug_list(mrb, mrdb->dbg, file, lineno, lineno);
mrb_free(mrb, file);
}
}
static void
print_info_stopped(mrb_state *mrb, mrdb_state *mrdb)
{
switch(mrdb->dbg->bm) {
case BRK_BREAK:
print_info_stopped_break(mrb, mrdb);
print_info_stopped_code(mrb, mrdb);
break;
case BRK_STEP:
case BRK_NEXT:
print_info_stopped_step_next(mrb, mrdb);
print_info_stopped_code(mrb, mrdb);
break;
default:
break;
}
}
static debug_command*
get_and_parse_command(mrb_state *mrb, mrdb_state *mrdb)
{
debug_command *cmd = NULL;
char *p;
int i;
while (!cmd) {
for (p=NULL; !p || *p=='\0'; ) {
printf("(%s:%d) ", mrdb->dbg->prvfile, mrdb->dbg->prvline);
p = get_command(mrb, mrdb);
}
cmd = parse_command(mrb, mrdb, p);
#ifdef _DBG_MRDB_PARSER_
for (i=0; i<mrdb->wcnt; i++) {
printf("%d: %s\n", i, mrdb->words[i]);
}
#endif
if (!cmd) {
printf("invalid command (");
for (i=0; i<mrdb->wcnt; i++) {
if (i>0) {
printf(" ");
}
printf("%s", mrdb->words[i]);
}
puts(")");
}
}
return cmd;
}
static const int32_t
check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
{
struct RClass* c;
mrb_sym sym;
int32_t bpno;
mrb_bool isCfunc;
mrb_debug_context *dbg = mrb_debug_context_get(mrb);
isCfunc = FALSE;
bpno = dbg->method_bpno;
dbg->method_bpno = 0;
switch(GET_OPCODE(*pc)) {
case OP_SEND:
case OP_SENDB:
c = mrb_class(mrb, regs[GETARG_A(*pc)]);
sym = irep->syms[GETARG_B(*pc)];
break;
case OP_SUPER:
c = mrb->c->ci->target_class->super;
sym = mrb->c->ci->mid;
break;
default:
sym = 0;
break;
}
if(sym != 0) {
dbg->method_bpno = mrb_debug_check_breakpoint_method(mrb, dbg, c, sym, &isCfunc);
if(isCfunc) {
bpno = dbg->method_bpno;
dbg->method_bpno = 0;
}
}
dbg->isCfunc = isCfunc;
return bpno;
}
static void
mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *regs)
{
const char *file;
int32_t line;
int32_t bpno;
mrb_debug_context *dbg = mrb_debug_context_get(mrb);
mrb_assert(dbg);
dbg->irep = irep;
dbg->pc = pc;
dbg->regs = regs;
if(dbg->xphase == DBG_PHASE_RESTART) {
dbg->root_irep = irep;
dbg->prvfile = NULL;
dbg->prvline = 0;
dbg->xm = DBG_RUN;
dbg->xphase = DBG_PHASE_RUNNING;
}
file = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq));
line = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq));
switch (dbg->xm) {
case DBG_STEP:
case DBG_NEXT: // temporary
if (dbg->prvfile == file && dbg->prvline == line) {
return;
}
dbg->method_bpno = 0;
dbg->bm = BRK_STEP;
break;
case DBG_RUN:
bpno = check_method_breakpoint(mrb, irep, pc, regs);
if (bpno > 0) {
dbg->stopped_bpno = bpno;
dbg->bm = BRK_BREAK;
break;
}
if (dbg->prvfile != file || dbg->prvline != line) {
bpno = mrb_debug_check_breakpoint_line(mrb, dbg, file, line);
if (bpno > 0) {
dbg->stopped_bpno = bpno;
dbg->bm = BRK_BREAK;
break;
}
}
dbg->prvfile = file;
dbg->prvline = line;
return;
case DBG_INIT:
dbg->root_irep = irep;
dbg->bm = BRK_INIT;
if (!file || line < 0) {
puts("Cannot get debugging information.");
}
break;
default:
return;
}
dbg->prvfile = file;
dbg->prvline = line;
if(dbg->bm == BRK_BREAK && --dbg->ccnt > 0) {
return;
}
dbg->break_hook(mrb, dbg);
dbg->xphase = DBG_PHASE_RUNNING;
}
static mrdb_exemode
mrb_debug_break_hook(mrb_state *mrb, mrb_debug_context *dbg)
{
debug_command *cmd;
dbgcmd_state st = DBGST_CONTINUE;
mrdb_state *mrdb = mrdb_state_get(mrb);
print_info_stopped(mrb, mrdb);
while (1) {
cmd = get_and_parse_command(mrb, mrdb);
mrb_assert(cmd);
st = cmd->func(mrb, mrdb);
if( (st == DBGST_CONTINUE) || (st == DBGST_RESTART) ) break;
}
return dbg->xm;
}
int
main(int argc, char **argv)
{
mrb_state *mrb = mrb_open();
int n = -1;
struct _args args;
mrb_value v;
mrdb_state *mrdb;
mrdb_state *mrdb_backup;
mrb_debug_context* dbg_backup;
debug_command *cmd;
if (mrb == NULL) {
fputs("Invalid mrb_state, exiting mruby\n", stderr);
return EXIT_FAILURE;
}
l_restart:
/* parse command parameters */
n = parse_args(mrb, argc, argv, &args);
if (n == EXIT_FAILURE || args.rfp == NULL) {
cleanup(mrb, &args);
usage(argv[0]);
return n;
}
/* initialize debugger information */
mrdb = mrdb_state_get(mrb);
mrb_assert(mrdb && mrdb->dbg);
mrdb->srcpath = args.srcpath;
if(mrdb->dbg->xm == DBG_QUIT) {
mrdb->dbg->xphase = DBG_PHASE_RESTART;
}
else {
mrdb->dbg->xphase = DBG_PHASE_BEFORE_RUN;
}
mrdb->dbg->xm = DBG_INIT;
mrdb->dbg->ccnt = 1;
/* setup hook functions */
mrb->code_fetch_hook = mrb_code_fetch_hook;
mrdb->dbg->break_hook = mrb_debug_break_hook;
if (args.mrbfile) { /* .mrb */
v = mrb_load_irep_file(mrb, args.rfp);
}
else { /* .rb */
mrbc_context *cc = mrbc_context_new(mrb);
mrbc_filename(mrb, cc, args.fname);
v = mrb_load_file_cxt(mrb, args.rfp, cc);
mrbc_context_free(mrb, cc);
}
if (mrdb->dbg->xm == DBG_QUIT && !mrb_undef_p(v) && mrb->exc) {
const char *classname = mrb_obj_classname(mrb, mrb_obj_value(mrb->exc));
if (!strcmp(classname, "DebuggerExit")) {
cleanup(mrb, &args);
return 0;
}
if (!strcmp(classname, "DebuggerRestart")) {
mrdb_backup = mrdb_state_get(mrb);
dbg_backup = mrb_debug_context_get(mrb);
mrdb_state_set(NULL);
mrb_debug_context_set(NULL);
cleanup(mrb, &args);
mrb = mrb_open();
mrdb_state_set(mrdb_backup);
mrb_debug_context_set(dbg_backup);
goto l_restart;
}
}
puts("mruby application exited.");
mrdb->dbg->xphase = DBG_PHASE_AFTER_RUN;
if (!mrb_undef_p(v)) {
if (mrb->exc) {
mrb_print_error(mrb);
}
else {
printf(" => ");
mrb_p(mrb, v);
}
}
mrdb->dbg->prvfile = "-";
mrdb->dbg->prvline = 0;
while (1) {
cmd = get_and_parse_command(mrb, mrdb);
mrb_assert(cmd);
if (cmd->id == DBGCMD_QUIT) {
break;
}
if( cmd->func(mrb, mrdb) == DBGST_RESTART ) goto l_restart;
}
cleanup(mrb, &args);
return 0;
}
/*
** mrdb.h - mruby debugger
**
*/
#ifndef MRDB_H
#define MRDB_H
#include "mruby.h"
#include "mrdbconf.h"
#ifdef _MSC_VER
# define __func__ __FUNCTION__
#endif
#define MAX_COMMAND_WORD (16)
typedef enum debug_command_id {
DBGCMD_RUN,
DBGCMD_CONTINUE,
DBGCMD_NEXT,
DBGCMD_STEP,
DBGCMD_BREAK,
DBGCMD_INFO_BREAK,
DBGCMD_WATCH,
DBGCMD_INFO_WATCH,
DBGCMD_ENABLE,
DBGCMD_DISABLE,
DBGCMD_DELETE,
DBGCMD_PRINT,
DBGCMD_DISPLAY,
DBGCMD_INFO_DISPLAY,
DBGCMD_DELETE_DISPLAY,
DBGCMD_EVAL,
DBGCMD_BACKTRACE,
DBGCMD_LIST,
DBGCMD_HELP,
DBGCMD_QUIT,
DBGCMD_UNKNOWN
} debug_command_id;
typedef enum dbgcmd_state {
DBGST_CONTINUE,
DBGST_PROMPT,
DBGST_COMMAND_ERROR,
DBGST_MAX,
DBGST_RESTART
} dbgcmd_state;
typedef enum mrdb_exemode {
DBG_INIT,
DBG_RUN,
DBG_STEP,
DBG_NEXT,
DBG_QUIT,
} mrdb_exemode;
typedef enum mrdb_exephase {
DBG_PHASE_BEFORE_RUN,
DBG_PHASE_RUNNING,
DBG_PHASE_AFTER_RUN,
DBG_PHASE_RESTART,
} mrdb_exephase;
typedef enum mrdb_brkmode {
BRK_INIT,
BRK_BREAK,
BRK_STEP,
BRK_NEXT,
BRK_QUIT,
} mrdb_brkmode;
typedef enum {
MRB_DEBUG_BPTYPE_NONE,
MRB_DEBUG_BPTYPE_LINE,
MRB_DEBUG_BPTYPE_METHOD,
} mrb_debug_bptype;
struct mrb_irep;
struct mrbc_context;
struct mrb_debug_context;
typedef struct mrb_debug_linepoint {
const char *file;
uint16_t lineno;
} mrb_debug_linepoint;
typedef struct mrb_debug_methodpoint {
const char *class_name;
const char *method_name;
} mrb_debug_methodpoint;
typedef struct mrb_debug_breakpoint {
uint32_t bpno;
uint8_t enable;
mrb_debug_bptype type;
union point {
mrb_debug_linepoint linepoint;
mrb_debug_methodpoint methodpoint;
} point;
} mrb_debug_breakpoint;
typedef struct mrb_debug_context {
struct mrb_irep *root_irep;
struct mrb_irep *irep;
mrb_code *pc;
mrb_value *regs;
const char *prvfile;
int32_t prvline;
mrdb_exemode xm;
mrdb_exephase xphase;
mrdb_brkmode bm;
int16_t bmi;
uint16_t ccnt;
uint16_t scnt;
mrb_debug_breakpoint bp[MAX_BREAKPOINT];
uint32_t bpnum;
int32_t next_bpno;
int32_t method_bpno;
int32_t stopped_bpno;
mrb_bool isCfunc;
mrdb_exemode (*break_hook)(mrb_state *mrb, struct mrb_debug_context *dbg);
} mrb_debug_context;
typedef struct mrdb_state {
char *command;
uint8_t wcnt;
uint8_t pi;
char *words[MAX_COMMAND_WORD];
const char *srcpath;
uint32_t print_no;
mrb_debug_context *dbg;
} mrdb_state;
typedef dbgcmd_state (*debug_command_func)(mrb_state*, mrdb_state*);
/* cmdrun.c */
dbgcmd_state dbgcmd_run(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_continue(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_step(mrb_state*, mrdb_state*);
/* cmdbreak.c */
dbgcmd_state dbgcmd_break(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_info_break(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_delete(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_enable(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_disable(mrb_state*, mrdb_state*);
/* cmdprint.c */
dbgcmd_state dbgcmd_print(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_eval(mrb_state*, mrdb_state*);
/* cmdmisc.c */
dbgcmd_state dbgcmd_list(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_help(mrb_state*, mrdb_state*);
dbgcmd_state dbgcmd_quit(mrb_state*, mrdb_state*);
#endif
/*
** mrdbconf.h - mruby debugger configuration
**
*/
#ifndef MRDBCONF_H
#define MRDBCONF_H
/* configuration options: */
/* maximum size for command buffer */
#define MAX_COMMAND_LINE 1024
/* maximum number of setable breakpoint */
#define MAX_BREAKPOINT 5
#endif
/*
** mrdberror.h - mruby debugger error code
**
*/
#ifndef MRDBERROR_H
#define MRDBERROR_H
#define MRB_DEBUG_OK (0)
#define MRB_DEBUG_NOBUF (-1)
#define MRB_DEBUG_INVALID_ARGUMENT (-2)
#define MRB_DEBUG_BREAK_INVALID_LINENO (-11)
#define MRB_DEBUG_BREAK_INVALID_FILE (-12)
#define MRB_DEBUG_BREAK_INVALID_NO (-13)
#define MRB_DEBUG_BREAK_NUM_OVER (-14)
#define MRB_DEBUG_BREAK_NO_OVER (-15)
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment