summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--.travis.yml17
-rw-r--r--.yardopts17
-rw-r--r--AUTHORS16
-rw-r--r--CONTRIBUTING.md13
-rw-r--r--INSTALL48
-rw-r--r--MITL2
-rw-r--r--README.md93
-rw-r--r--Rakefile36
-rw-r--r--TODO7
-rw-r--r--benchmark/bm_ao_render.rb (renamed from benchmark/ao-render.rb)7
-rw-r--r--benchmark/bm_app_lc_fizzbuzz.rb52
-rw-r--r--[-rwxr-xr-x]benchmark/bm_fib.rb (renamed from benchmark/fib39.rb)3
-rw-r--r--benchmark/bm_so_lists.rb2
-rw-r--r--benchmark/build_config_boxing.rb28
-rw-r--r--benchmark/build_config_cc.rb13
-rw-r--r--benchmark/plot.gpl5
-rw-r--r--build_config.rb41
-rw-r--r--doc/guides/compile.md (renamed from doc/compile/README.md)261
-rw-r--r--doc/guides/debugger.md370
-rw-r--r--doc/guides/gc-arena-howto.md177
-rw-r--r--doc/guides/mrbconf.md157
-rw-r--r--doc/guides/mrbgems.md (renamed from doc/mrbgems/README.md)153
-rw-r--r--doc/language/Core.md1590
-rw-r--r--doc/language/README.md9
-rwxr-xr-xdoc/language/generator.rb15
-rw-r--r--doc/language/mrbdoc/lib/mrbdoc_analyze.rb231
-rw-r--r--doc/language/mrbdoc/lib/mrbdoc_docu.rb118
-rwxr-xr-xdoc/language/mrbdoc/mrbdoc.rb38
-rw-r--r--doc/mrbconf/README.md38
-rw-r--r--examples/targets/build_config_ArduinoDue.rb9
-rw-r--r--examples/targets/build_config_chipKITMax32.rb2
-rw-r--r--include/mrbconf.h32
-rw-r--r--include/mruby.h1076
-rw-r--r--include/mruby/array.h119
-rw-r--r--include/mruby/boxing_nan.h93
-rw-r--r--include/mruby/boxing_no.h48
-rw-r--r--include/mruby/boxing_word.h116
-rw-r--r--include/mruby/class.h54
-rw-r--r--include/mruby/common.h67
-rw-r--r--include/mruby/compile.h71
-rw-r--r--include/mruby/data.h37
-rw-r--r--include/mruby/debug.h23
-rw-r--r--include/mruby/dump.h88
-rw-r--r--include/mruby/error.h70
-rw-r--r--include/mruby/gc.h71
-rw-r--r--include/mruby/hash.h59
-rw-r--r--include/mruby/irep.h20
-rw-r--r--include/mruby/khash.h34
-rw-r--r--include/mruby/numeric.h69
-rw-r--r--include/mruby/object.h39
-rw-r--r--include/mruby/opcode.h10
-rw-r--r--include/mruby/proc.h33
-rw-r--r--include/mruby/range.h28
-rw-r--r--include/mruby/string.h192
-rw-r--r--include/mruby/throw.h (renamed from src/mrb_throw.h)2
-rw-r--r--include/mruby/value.h527
-rw-r--r--include/mruby/variable.h61
-rw-r--r--include/mruby/version.h98
-rw-r--r--lib/mruby/source.rb30
-rwxr-xr-xminirake17
-rw-r--r--mrbgems/default.gembox25
-rw-r--r--mrbgems/full-core.gembox6
-rw-r--r--mrbgems/mruby-array-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-array-ext/mrblib/array.rb87
-rw-r--r--mrbgems/mruby-array-ext/src/array.c15
-rw-r--r--mrbgems/mruby-array-ext/test/array.rb17
-rw-r--r--mrbgems/mruby-bin-debugger/bintest/mrdb.rb286
-rw-r--r--mrbgems/mruby-bin-debugger/bintest/print.rb701
-rw-r--r--mrbgems/mruby-bin-debugger/mrbgem.rake9
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c516
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h26
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c235
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h14
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c78
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h13
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c428
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c501
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c58
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c62
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c759
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h165
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h16
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h20
-rw-r--r--mrbgems/mruby-bin-mirb/mrbgem.rake20
-rw-r--r--mrbgems/mruby-bin-mirb/tools/mirb/mirb.c223
-rw-r--r--mrbgems/mruby-bin-mrbc/mrbgem.rake16
-rw-r--r--mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c (renamed from tools/mrbc/mrbc.c)41
-rw-r--r--mrbgems/mruby-bin-mruby-config/mrbgem.rake2
-rw-r--r--mrbgems/mruby-bin-mruby-config/mruby-config8
-rw-r--r--mrbgems/mruby-bin-mruby-config/mruby-config.bat12
-rw-r--r--mrbgems/mruby-bin-mruby/bintest/mruby.rb20
-rw-r--r--mrbgems/mruby-bin-mruby/mrbgem.rake1
-rw-r--r--mrbgems/mruby-bin-mruby/tools/mruby/mruby.c38
-rw-r--r--mrbgems/mruby-bin-strip/bintest/mruby-strip.rb34
-rw-r--r--mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c130
-rw-r--r--mrbgems/mruby-compiler/bintest/mrbc.rb (renamed from bintest/mrbc.rb)4
-rw-r--r--mrbgems/mruby-compiler/core/codegen.c (renamed from src/codegen.c)708
-rw-r--r--mrbgems/mruby-compiler/core/keywords (renamed from src/keywords)0
-rw-r--r--mrbgems/mruby-compiler/core/lex.def (renamed from src/lex.def)92
-rw-r--r--mrbgems/mruby-compiler/core/node.h (renamed from src/node.h)6
-rw-r--r--mrbgems/mruby-compiler/core/parse.y (renamed from src/parse.y)636
-rw-r--r--mrbgems/mruby-compiler/mrbgem.rake40
-rw-r--r--mrbgems/mruby-enum-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-enum-ext/mrblib/enum.rb56
-rw-r--r--mrbgems/mruby-enum-lazy/mrblib/lazy.rb43
-rw-r--r--mrbgems/mruby-enumerator/mrbgem.rake1
-rw-r--r--mrbgems/mruby-enumerator/mrblib/enumerator.rb162
-rw-r--r--mrbgems/mruby-enumerator/test/enumerator.rb1
-rw-r--r--mrbgems/mruby-error/mrbgem.rake10
-rw-r--r--mrbgems/mruby-error/src/exception.c100
-rw-r--r--mrbgems/mruby-error/test/exception.c59
-rw-r--r--mrbgems/mruby-error/test/exception.rb55
-rw-r--r--mrbgems/mruby-eval/mrbgem.rake2
-rw-r--r--mrbgems/mruby-eval/src/eval.c142
-rw-r--r--mrbgems/mruby-eval/test/eval.rb53
-rw-r--r--mrbgems/mruby-exit/src/mruby-exit.c2
-rw-r--r--mrbgems/mruby-fiber/src/fiber.c50
-rw-r--r--mrbgems/mruby-hash-ext/mrbgem.rake4
-rw-r--r--mrbgems/mruby-hash-ext/mrblib/hash.rb180
-rw-r--r--mrbgems/mruby-hash-ext/src/hash-ext.c6
-rw-r--r--mrbgems/mruby-hash-ext/test/hash.rb121
-rw-r--r--mrbgems/mruby-kernel-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-kernel-ext/src/kernel.c152
-rw-r--r--mrbgems/mruby-kernel-ext/test/kernel.rb35
-rw-r--r--mrbgems/mruby-math/src/math.c85
-rw-r--r--mrbgems/mruby-math/test/math.rb16
-rw-r--r--mrbgems/mruby-numeric-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb12
-rw-r--r--mrbgems/mruby-numeric-ext/src/numeric_ext.c2
-rw-r--r--mrbgems/mruby-numeric-ext/test/numeric.rb19
-rw-r--r--mrbgems/mruby-object-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-object-ext/src/object.c12
-rw-r--r--mrbgems/mruby-objectspace/src/mruby_objectspace.c24
-rw-r--r--mrbgems/mruby-objectspace/test/objectspace.rb4
-rw-r--r--mrbgems/mruby-print/src/print.c33
-rw-r--r--mrbgems/mruby-proc-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-proc-ext/mrblib/proc.rb4
-rw-r--r--mrbgems/mruby-proc-ext/src/proc.c122
-rw-r--r--mrbgems/mruby-proc-ext/test/proc.c6
-rw-r--r--mrbgems/mruby-proc-ext/test/proc.rb15
-rw-r--r--mrbgems/mruby-random/src/mt19937ar.c6
-rw-r--r--mrbgems/mruby-random/src/random.c27
-rw-r--r--mrbgems/mruby-random/src/random.h4
-rw-r--r--mrbgems/mruby-range-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-range-ext/src/range.c4
-rw-r--r--mrbgems/mruby-sprintf/src/kernel.c2
-rw-r--r--mrbgems/mruby-sprintf/src/sprintf.c138
-rw-r--r--mrbgems/mruby-sprintf/test/sprintf.rb1
-rw-r--r--mrbgems/mruby-string-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-string-ext/mrblib/string.rb157
-rw-r--r--mrbgems/mruby-string-ext/src/string.c314
-rw-r--r--mrbgems/mruby-string-ext/test/string.rb324
-rw-r--r--mrbgems/mruby-string-utf8/mrbgem.rake6
-rw-r--r--mrbgems/mruby-string-utf8/src/string.c743
-rw-r--r--mrbgems/mruby-string-utf8/test/string.rb109
-rw-r--r--mrbgems/mruby-struct/mrblib/struct.rb36
-rw-r--r--mrbgems/mruby-struct/src/struct.c262
-rw-r--r--mrbgems/mruby-struct/test/struct.rb2
-rw-r--r--mrbgems/mruby-symbol-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-symbol-ext/mrblib/symbol.rb25
-rw-r--r--mrbgems/mruby-symbol-ext/src/symbol.c37
-rw-r--r--mrbgems/mruby-symbol-ext/test/symbol.rb7
-rw-r--r--mrbgems/mruby-test/README.md (renamed from test/README.md)0
-rw-r--r--mrbgems/mruby-test/driver.c (renamed from test/driver.c)91
-rw-r--r--mrbgems/mruby-test/init_mrbtest.c38
-rw-r--r--mrbgems/mruby-test/mrbgem.rake174
-rw-r--r--mrbgems/mruby-time/src/time.c65
-rw-r--r--mrbgems/mruby-time/test/time.rb8
-rw-r--r--mrbgems/mruby-toplevel-ext/mrbgem.rake2
-rw-r--r--mrblib/array.rb59
-rw-r--r--mrblib/class.rb23
-rw-r--r--mrblib/enum.rb62
-rw-r--r--mrblib/error.rb18
-rw-r--r--mrblib/hash.rb51
-rw-r--r--mrblib/init_mrblib.c4
-rw-r--r--mrblib/kernel.rb15
-rw-r--r--mrblib/mrblib.rake2
-rw-r--r--mrblib/numeric.rb50
-rw-r--r--mrblib/range.rb14
-rw-r--r--mrblib/string.rb126
-rw-r--r--mruby-source.gemspec18
-rw-r--r--src/array.c139
-rw-r--r--src/backtrace.c243
-rw-r--r--src/class.c786
-rw-r--r--src/codedump.c453
-rw-r--r--src/compar.c2
-rw-r--r--src/debug.c16
-rw-r--r--src/dump.c295
-rw-r--r--src/enum.c2
-rw-r--r--src/error.c115
-rw-r--r--src/error.h2
-rw-r--r--src/etc.c133
-rw-r--r--src/fmt_fp.c371
-rw-r--r--src/gc.c798
-rw-r--r--src/hash.c154
-rw-r--r--src/init.c7
-rw-r--r--src/kernel.c189
-rw-r--r--src/load.c391
-rw-r--r--src/mruby_core.rake63
-rw-r--r--src/numeric.c274
-rw-r--r--src/object.c65
-rw-r--r--src/opcode.h2
-rw-r--r--src/pool.c15
-rw-r--r--src/print.c58
-rw-r--r--src/proc.c107
-rw-r--r--src/range.c26
-rw-r--r--src/state.c88
-rw-r--r--src/string.c1384
-rw-r--r--src/symbol.c146
-rw-r--r--src/value_array.h2
-rw-r--r--src/variable.c156
-rw-r--r--src/version.c8
-rw-r--r--src/vm.c353
-rw-r--r--tasks/benchmark.rake91
-rw-r--r--tasks/mrbgem_spec.rake97
-rw-r--r--tasks/mrbgems.rake23
-rw-r--r--tasks/mrbgems_test.rake147
-rw-r--r--tasks/mruby_build.rake93
-rw-r--r--tasks/mruby_build_commands.rake6
-rw-r--r--tasks/mruby_build_gem.rake28
-rw-r--r--tasks/ruby_ext.rake11
-rw-r--r--tasks/toolchains/android.rake216
-rw-r--r--tasks/toolchains/androideabi.rake130
-rw-r--r--tasks/toolchains/gcc.rake2
-rw-r--r--tasks/toolchains/visualcpp.rake2
-rw-r--r--test/assert.rb9
-rw-r--r--test/bintest.rb20
-rw-r--r--test/init_mrbtest.c23
-rw-r--r--test/mrbtest.rake71
-rw-r--r--test/no_mrb_open_test_dummy.rb2
-rw-r--r--test/t/class.rb18
-rw-r--r--test/t/ensure.rb54
-rw-r--r--test/t/float.rb22
-rw-r--r--test/t/hash.rb9
-rw-r--r--test/t/integer.rb28
-rw-r--r--test/t/module.rb315
-rw-r--r--test/t/numeric.rb8
-rw-r--r--test/t/proc.rb8
-rw-r--r--test/t/string.rb198
-rw-r--r--test/t/syntax.rb102
-rw-r--r--tools/mrbc/mrbc.rake14
-rw-r--r--travis_config.rb17
243 files changed, 16769 insertions, 9295 deletions
diff --git a/.gitignore b/.gitignore
index 75f473258..800680821 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,9 @@
*.bak
*.d
*.o
+/benchmark/**/*.dat
+/benchmark/*.pdf
+/benchmark/*.png
*.orig
*.pdb
*.rej
@@ -17,4 +20,6 @@ cscope.out
/src/y.tab.c
/bin
/build
-/lib
+/mruby-source-*.gem
+doc/api
+.yardoc
diff --git a/.travis.yml b/.travis.yml
index f7be58c8a..50feac2e0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,18 @@
-# no installation...
+language: c
-install:
- - sudo apt-get -q install gperf
+sudo: false
+
+matrix:
+ include:
+ - os: linux
+ sudo: 9000
+ - os: osx
+ osx_image: xcode7.1
+
+addons:
+ apt:
+ packages:
+ - gperf
env: MRUBY_CONFIG=travis_config.rb
script: "./minirake all test"
diff --git a/.yardopts b/.yardopts
new file mode 100644
index 000000000..27f6d59a1
--- /dev/null
+++ b/.yardopts
@@ -0,0 +1,17 @@
+--markup markdown
+--plugin mruby
+--plugin coderay
+--output-dir doc/api
+
+src/**/*.c
+mrblib/**/*.rb
+include/**/*.h
+
+mrbgems/*/src/**/*.c
+mrbgems/*/mrblib/**/*.rb
+mrbgems/*/include/**/*.h
+-
+AUTHORS
+MITL
+CONTRIBUTING.md
+doc/guides/*.md
diff --git a/AUTHORS b/AUTHORS
index 555cf7743..180fd752e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,6 +1,6 @@
Original Authors "mruby developers" are:
Yukihiro Matsumoto
- FUKUOKA CSK CORPORATION
+ SCSK KYUSHU CORPORATION
Kyushu Institute of Technology
Network Applied Communication Laboratory, Inc.
Daniel Bovensiepen
@@ -20,3 +20,17 @@ Original Authors "mruby developers" are:
Tatsuhiko Kubo
Takeshi Watanabe
Yuki Kurihara
+ specified non-profit corporation mruby Forum
+ Kazuaki Tanaka
+ Hiromasa Ishii
+ Hiroshi Mimaki
+ Satoshi Odawara
+ Mitsubishi Electric Micro-Computer Application Software Co.,Ltd.
+ Ralph Desir(Mav7)
+ Hiroyuki Matsuzaki
+ Yuhei Okazaki
+ Manycolors, Inc.
+ Shota Nakano
+ Yuichi Osawa
+ Terence Lee
+ Zachary Scott
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b8db5a7dc..6bababb89 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -14,6 +14,8 @@ things in mind before submitting your pull request:
* Don't mix several features or bug-fixes in one pull request
* Create a meaningful commit message
* Explain your change (i.e. with a link to the issue you are fixing)
+* Use mrbgem to provide non ISO features (classes, modules and methods) unless
+ you have a special reason to implement them in the core
## Coding conventions
@@ -31,8 +33,9 @@ mruby should be highly portable to other systems and compilers. For this it is
recommended to keep your code as close as possible to the C99 standard
(http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf).
-Although we target C99, Visual C++ is also an important target for mruby. For this
-reason a declaration of a local variable has to be at the beginning of a scope block.
+Although we target C99, Visual C++ is also an important target for mruby. For
+this reason a declaration of a local variable has to be at the beginning of a
+scope block.
#### Reduce library dependencies to a minimum
@@ -42,7 +45,7 @@ on-demand.
#### Don't use C++ style comments
- /* This is the prefered comment style */
+ /* This is the preferred comment style */
Use C++ style comments only for temporary comment e.g. commenting out some code lines.
@@ -56,8 +59,8 @@ Use C++ style comments only for temporary comment e.g. commenting out some code
### Ruby code
-Parts of the standard library of mruby are written in the Ruby programming language
-itself. Please note the following hints for your Ruby code:
+Parts of the standard library of mruby are written in the Ruby programming
+language itself. Please note the following hints for your Ruby code:
#### Comply with the Ruby standard (ISO/IEC 30170:2012)
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index 865ea218e..000000000
--- a/INSTALL
+++ /dev/null
@@ -1,48 +0,0 @@
-* Prerequisites
-
- 1. Make sure you have bison (http://www.gnu.org/software/bison/) installed in your system.
- 2. Make sure you have ruby installed in your system (required to build).
-
-* Compilation and Installation
-
- 1. Run make in the top directory.
-
- This command will create a build directory with a directory for the host environment
- and one for each crossbuild environment based on the settings in the build_config.rb
- file.
-
- Assuming a default build, each of the environment directories will have the following
- important directories:
-
- * bin - The binary executables for this environment
- * lib - The libraries for this environment
-
- You can find the header files in the include directory at the root of the project.
-
- You can directly invoke 'minirake' with the following command:
-
- $ ruby ./minirake
-
-If an error occurs when compiling mruby, it will be helpful for others if you
-send a detailed report to the developers that includes the error log, machine,
-and OS type.
-
-
-* Adding existing gems
-
-Gems from the [list of mruby gems](http://www.mruby.org/libraries/) can be added by adding
-their respective GitHub URLs to build_config.rb. For example, to add implementations of the
-File and IO Ruby core classes to mruby, insert the following in build_config.rb under the
-comment section `Use mrbgems`:
-
- conf.gem :git => '[email protected]:iij/mruby-io.git', :branch => 'master'
-
- Afterwards, re-run:
-
- ruby ./minirake
-
-
-* Porting to other platforms
-
-
-That's all.
diff --git a/MITL b/MITL
index 59400498f..11898018b 100644
--- a/MITL
+++ b/MITL
@@ -1,4 +1,4 @@
-Copyright (c) 2014 mruby developers
+Copyright (c) 2015 mruby developers
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
diff --git a/README.md b/README.md
index dd6d9af22..a4d9ae00f 100644
--- a/README.md
+++ b/README.md
@@ -1,52 +1,42 @@
-[![Build Status](https://travis-ci.org/mruby/mruby.png?branch=master)](https://travis-ci.org/mruby/mruby)
+[![Build Status][build-status-img]][travis-ci]
-## What's mruby
+## What is mruby
-mruby is the lightweight implementation of the Ruby language complying to (part of)
-the [ISO standard](http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579).
-Its syntax is Ruby 1.9 compatible.
+mruby is the lightweight implementation of the Ruby language complying to (part
+of) the [ISO standard][ISO-standard]. Its syntax is Ruby 1.9 compatible.
-mruby can be linked and embedded within your application. We provide the interpreter program "mruby" and
-the interactive mruby shell "mirb" as examples. You can also compile Ruby programs into compiled byte code
-using the mruby compiler "mrbc". All those tools reside in the "bin" directory. "mrbc" is also able to
-generate compiled byte code in a C source file, see the "mrbtest" program under the "test" directory
-for an example.
-
-This achievement was sponsored by the Regional Innovation Creation R&D Programs of
-the Ministry of Economy, Trade and Industry of Japan.
+mruby can be linked and embedded within your application. We provide the
+interpreter program "mruby" and the interactive mruby shell "mirb" as examples.
+You can also compile Ruby programs into compiled byte code using the mruby
+compiler "mrbc". All those tools reside in the "bin" directory. "mrbc" is
+also able to generate compiled byte code in a C source file, see the "mrbtest"
+program under the "test" directory for an example.
+This achievement was sponsored by the Regional Innovation Creation R&D Programs
+of the Ministry of Economy, Trade and Industry of Japan.
## How to get mruby
-The stable version 1.0.0 of mruby can be downloaded via the following URL:
-
- https://github.com/mruby/mruby/archive/1.0.0.zip
-
-The latest development version of mruby can be downloaded via the following URL:
+The stable version 1.2.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.2.0.zip](https://github.com/mruby/mruby/archive/1.2.0.zip)
- https://github.com/mruby/mruby/zipball/master
+The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master)
The trunk of the mruby source tree can be checked out with the
following command:
$ git clone https://github.com/mruby/mruby.git
-
## mruby home-page
-The URL of the mruby home-page is:
-
- http://www.mruby.org/
-
+The URL of the mruby home-page is: [http://www.mruby.org](http://www.mruby.org).
## Mailing list
-To subscribe to the mruby mailing list....[T.B.D.]
-
+We don't have mailing list, but you can use [GitHub issues](https://github.com/mruby/mruby).
## How to compile and install (mruby and gems)
-See the INSTALL file.
+See the [doc/guides/compile.md](doc/guides/compile.md) file.
## Running Tests
@@ -58,33 +48,16 @@ Or
$ ruby ./minirake test
-## Customization
+## How to customize mruby (mrbgems)
mruby contains a package manager called *mrbgems*. To create extensions
-in C and/or Ruby you should create a *GEM*. You will find a complete
-documentation with examples under *examples/mrbgems*.
+in C and/or Ruby you should create a *GEM*. For a documentation of how to
+use mrbgems consult the file [doc/guides/mrbgems.md](doc/guides/mrbgems.md). For example code of
+how to use mrbgems look into the folder *examples/mrbgems/*.
## License
-Copyright (c) 2014 mruby developers
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
+mruby is released under the [MIT License](MITL).
## Note for License
@@ -92,20 +65,26 @@ mruby has chosen a MIT License due to its permissive license allowing
developers to target various environments such as embedded systems.
However, the license requires the display of the copyright notice and license
information in manuals for instance. Doing so for big projects can be
-complicated or troublesome.
-This is why mruby has decided to display "mruby developers" as the copyright name
-to make it simple conventionally.
+complicated or troublesome. This is why mruby has decided to display "mruby
+developers" as the copyright name to make it simple conventionally.
In the future, mruby might ask you to distribute your new code
(that you will commit,) under the MIT License as a member of
"mruby developers" but contributors will keep their copyright.
(We did not intend for contributors to transfer or waive their copyrights,
- Actual copyright holder name (contributors) will be listed in the AUTHORS file.)
+Actual copyright holder name (contributors) will be listed in the AUTHORS
+file.)
Please ask us if you want to distribute your code under another license.
## How to Contribute
-See the [contribution guidelines](https://github.com/mruby/mruby/blob/master/CONTRIBUTING.md)
-then send a pull request to <http://github.com/mruby/mruby>. We consider you have granted
-non-exclusive right to your contributed code under MIT license. If you want to be named
-as one of mruby developers, please include an update to the AUTHORS file in your pull request.
+See the [contribution guidelines][contribution-guidelines], and then send a pull
+request to <http://github.com/mruby/mruby>. We consider you have granted
+non-exclusive right to your contributed code under MIT license. If you want to
+be named as one of mruby developers, please include an update to the AUTHORS
+file in your pull request.
+
+[ISO-standard]: http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579
+[build-status-img]: https://travis-ci.org/mruby/mruby.svg?branch=master
+[contribution-guidelines]: CONTRIBUTING.md
+[travis-ci]: https://travis-ci.org/mruby/mruby
diff --git a/Rakefile b/Rakefile
index 135c7a6a2..3021bcc8e 100644
--- a/Rakefile
+++ b/Rakefile
@@ -3,6 +3,7 @@
# basic build file for mruby
MRUBY_ROOT = File.dirname(File.expand_path(__FILE__))
MRUBY_BUILD_HOST_IS_CYGWIN = RUBY_PLATFORM.include?('cygwin')
+MRUBY_BUILD_HOST_IS_OPENBSD = RUBY_PLATFORM.include?('openbsd')
# load build systems
load "#{MRUBY_ROOT}/tasks/ruby_ext.rake"
@@ -21,19 +22,17 @@ end
# load custom rules
load "#{MRUBY_ROOT}/src/mruby_core.rake"
load "#{MRUBY_ROOT}/mrblib/mrblib.rake"
-load "#{MRUBY_ROOT}/tools/mrbc/mrbc.rake"
load "#{MRUBY_ROOT}/tasks/mrbgems.rake"
load "#{MRUBY_ROOT}/tasks/libmruby.rake"
-load "#{MRUBY_ROOT}/tasks/mrbgems_test.rake"
-load "#{MRUBY_ROOT}/test/mrbtest.rake"
+load "#{MRUBY_ROOT}/tasks/benchmark.rake"
##############################
# generic build targets, rules
task :default => :all
-bin_path = "#{MRUBY_ROOT}/bin"
+bin_path = ENV['INSTALL_DIR'] || "#{MRUBY_ROOT}/bin"
FileUtils.mkdir_p bin_path, { :verbose => $verbose }
depfiles = MRuby.targets['host'].bins.map do |bin|
@@ -52,7 +51,11 @@ MRuby.each_target do |target|
gems.map do |gem|
current_dir = gem.dir.relative_path_from(Dir.pwd)
relative_from_root = gem.dir.relative_path_from(MRUBY_ROOT)
- current_build_dir = "#{build_dir}/#{relative_from_root}"
+ current_build_dir = File.expand_path "#{build_dir}/#{relative_from_root}"
+
+ if current_build_dir !~ /^#{build_dir}/
+ current_build_dir = "#{build_dir}/mrbgems/#{gem.name}"
+ end
gem.bins.each do |bin|
exec = exefile("#{build_dir}/bin/#{bin}")
@@ -68,13 +71,23 @@ MRuby.each_target do |target|
end
if target == MRuby.targets['host']
- install_path = MRuby.targets['host'].exefile("#{MRUBY_ROOT}/bin/#{bin}")
+ install_path = MRuby.targets['host'].exefile("#{bin_path}/#{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 ]
+ elsif target == MRuby.targets['host-debug']
+ unless MRuby.targets['host'].gems.map {|g| g.bins}.include?([bin])
+ install_path = MRuby.targets['host-debug'].exefile("#{bin_path}/#{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
depfiles += [ exec ]
end
@@ -101,9 +114,9 @@ task :all => depfiles do
end
desc "run all mruby tests"
-task :test => ["all"] + MRuby.targets.values.map { |t| t.build_mrbtest_lib_only? ? t.libfile("#{t.build_dir}/test/mrbtest") : t.exefile("#{t.build_dir}/test/mrbtest") } do
+task :test => ["all"] do
MRuby.each_target do
- run_test unless build_mrbtest_lib_only?
+ run_test if test_enabled?
end
end
@@ -126,5 +139,10 @@ end
desc 'generate document'
task :doc do
- load "#{MRUBY_ROOT}/doc/language/generator.rb"
+ begin
+ sh "mrbdoc"
+ rescue
+ puts "ERROR: To generate documents, you should install yard-mruby gem."
+ puts " $ gem install yard-mruby"
+ end
end
diff --git a/TODO b/TODO
index d3cb483c5..3e195f99b 100644
--- a/TODO
+++ b/TODO
@@ -1,11 +1,10 @@
Things to do (Things that are not done yet)
-* Special variables ($1,$2..)
+* special variables ($1,$2..)
* super in aliased methods
+* multi-assignment decomposing
+* keyword arguments in def statement
Things to improve (Done but things to fix)
-* Hash (Reduce size. Use khash or save the order)
-* stringEx (Delete encoding、delete CODERANGE、delete everything except UTF-8 or ASCII)
* Make additions as they are noticed.
-
diff --git a/benchmark/ao-render.rb b/benchmark/bm_ao_render.rb
index 793804a7b..8212c3a13 100644
--- a/benchmark/ao-render.rb
+++ b/benchmark/bm_ao_render.rb
@@ -1,13 +1,12 @@
# AO render benchmark
# Original program (C) Syoyo Fujita in Javascript (and other languages)
-# http://lucille.atso-net.jp/blog/?p=642
-# http://lucille.atso-net.jp/blog/?p=711
+# https://code.google.com/p/aobench/
# Ruby(yarv2llvm) version by Hideki Miura
# mruby version by Hideki Miura
#
-IMAGE_WIDTH = 256
-IMAGE_HEIGHT = 256
+IMAGE_WIDTH = 64
+IMAGE_HEIGHT = 64
NSUBSAMPLES = 2
NAO_SAMPLES = 8
diff --git a/benchmark/bm_app_lc_fizzbuzz.rb b/benchmark/bm_app_lc_fizzbuzz.rb
new file mode 100644
index 000000000..26283cc3f
--- /dev/null
+++ b/benchmark/bm_app_lc_fizzbuzz.rb
@@ -0,0 +1,52 @@
+#
+# FizzBuzz program using only lambda calculus
+#
+# This program is quoted from
+# "Understanding Computation" by Tom Stuart
+# http://computationbook.com/
+#
+# You can understand why this program works fine by reading this book.
+#
+
+solution = -> k { -> f { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][k][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> l { -> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[l][f[x]] } }] } }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[m][n]][-> x { -> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[f[-> n { -> p { -> x { p[n[p][x]] } } }[m]][n]][m][x] }][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]] } } }][-> p { -> x { p[x] } }][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] } }]][-> n { -> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[p[p[p[p[p[p[p[p[p[p[x]]]]]]]]]]]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[x]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> b { b }[-> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]]][-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> n { -> p { -> x { p[n[p][x]] } } }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]]]][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> n { -> l { -> x { -> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> l { -> x { -> g { -> b { b }[-> p { p[-> x { -> y { x } }] }[l]][x][-> y { g[f[-> l { -> p { p[-> x { -> y { y } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][x][g]][-> l { -> p { p[-> x { -> y { x } }] }[-> p { p[-> x { -> y { y } }] }[l]] }[l]][y] }] } } } }][l][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }[-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][x]][-> l { -> x { -> x { -> y { -> f { f[x][y] } } }[-> x { -> y { y } }][-> x { -> y { -> f { f[x][y] } } }[x][l]] } }] } }[-> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }[-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]]][-> x { -> y { -> f { f[x][y] } } }[-> x { -> y { x } }][-> x { -> y { x } }]][-> x { f[-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { -> n { -> p { -> x { p[n[p][x]] } } }[f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n]][x] }][-> p { -> x { x } }] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]][x] }]][-> f { -> x { f[-> y { x[x][y] }] }[-> x { f[-> y { x[x][y] }] }] }[-> f { -> m { -> n { -> b { b }[-> m { -> n { -> n { n[-> x { -> x { -> y { y } } }][-> x { -> y { x } }] }[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]] } }[n][m]][-> x { f[-> m { -> n { n[-> n { -> p { p[-> x { -> y { x } }] }[n[-> p { -> x { -> y { -> f { f[x][y] } } }[-> p { p[-> x { -> y { y } }] }[p]][-> n { -> p { -> x { p[n[p][x]] } } }[-> p { p[-> x { -> y { y } }] }[p]]] }][-> x { -> y { -> f { f[x][y] } } }[-> p { -> x { x } }][-> p { -> x { x } }]]] }][m] } }[m][n]][n][x] }][m] } } }][n][-> m { -> n { n[-> m { -> n { n[-> n { -> p { -> x { p[n[p][x]] } } }][m] } }[m]][-> p { -> x { x } }] } }[-> p { -> x { p[p[x]] } }][-> p { -> x { p[p[p[p[p[x]]]]] } }]]] } }][n]]]] }]
+
+FIRST = -> l { LEFT[RIGHT[l]] }
+IF = -> b { b }
+LEFT = -> p { p[-> x { -> y { x } } ] }
+RIGHT = -> p { p[-> x { -> y { y } } ] }
+IS_EMPTY = LEFT
+REST = -> l { RIGHT[RIGHT[l]] }
+
+def to_integer(proc)
+ proc[-> n { n + 1 }][0]
+end
+
+def to_boolean(proc)
+ IF[proc][true][false]
+end
+
+def to_array(proc)
+ array = []
+
+ until to_boolean(IS_EMPTY[proc])
+ array.push(FIRST[proc])
+ proc = REST[proc]
+ end
+
+ array
+end
+
+def to_char(c)
+ '0123456789BFiuz'.slice(to_integer(c))
+end
+
+def to_string(s)
+ to_array(s).map { |c| to_char(c) }.join
+end
+
+answer = to_array(solution).map do |p|
+ to_string(p)
+end
+
+answer_str = answer.to_a
+# puts answer_str
diff --git a/benchmark/fib39.rb b/benchmark/bm_fib.rb
index d5565b779..4b395f9cc 100755..100644
--- a/benchmark/fib39.rb
+++ b/benchmark/bm_fib.rb
@@ -1,8 +1,7 @@
-# Fib 39
def fib n
return n if n < 2
fib(n-2) + fib(n-1)
end
-puts fib(39)
+puts fib(37)
diff --git a/benchmark/bm_so_lists.rb b/benchmark/bm_so_lists.rb
index f8d26797a..e8f4a2a5f 100644
--- a/benchmark/bm_so_lists.rb
+++ b/benchmark/bm_so_lists.rb
@@ -40,7 +40,7 @@ end
i = 0
while i<NUM
- i+=1
+ i += 1
result = test_lists()
end
diff --git a/benchmark/build_config_boxing.rb b/benchmark/build_config_boxing.rb
new file mode 100644
index 000000000..b478c9005
--- /dev/null
+++ b/benchmark/build_config_boxing.rb
@@ -0,0 +1,28 @@
+MRuby::Build.new do |conf|
+ toolchain :gcc
+end
+
+MRuby::Build.new('no_boxing') do |conf|
+ toolchain :gcc
+
+ conf.gembox 'default'
+end
+
+MRuby::Build.new('word_boxing') do |conf|
+ toolchain :gcc
+
+ conf.gembox 'default'
+ conf.compilers.each do |c|
+ c.defines += %w(MRB_WORD_BOXING)
+ end
+end
+
+MRuby::Build.new('nan_boxing') do |conf|
+ toolchain :gcc
+
+ conf.gembox 'default'
+ conf.compilers.each do |c|
+ c.defines += %w(MRB_NAN_BOXING)
+ end
+end
+
diff --git a/benchmark/build_config_cc.rb b/benchmark/build_config_cc.rb
new file mode 100644
index 000000000..56d725bc7
--- /dev/null
+++ b/benchmark/build_config_cc.rb
@@ -0,0 +1,13 @@
+MRuby::Build.new do |conf|
+ toolchain :gcc
+end
+
+MRuby::Build.new('gcc') do |conf|
+ toolchain :gcc
+ conf.gembox 'default'
+end
+
+MRuby::Build.new('clang') do |conf|
+ toolchain :clang
+ conf.gembox 'default'
+end
diff --git a/benchmark/plot.gpl b/benchmark/plot.gpl
new file mode 100644
index 000000000..725e2ec1c
--- /dev/null
+++ b/benchmark/plot.gpl
@@ -0,0 +1,5 @@
+set yrange [0:]
+set terminal pngcairo font 'Sans, 8' lw 1 size 1400,1024
+set xtics rotate by -45
+set style histogram errorbars gap 2 lw 1
+set style fill solid border -1
diff --git a/build_config.rb b/build_config.rb
index 795251486..6b54359fe 100644
--- a/build_config.rb
+++ b/build_config.rb
@@ -21,7 +21,6 @@ MRuby::Build.new do |conf|
# include the default GEMs
conf.gembox 'default'
-
# C compiler settings
# conf.cc do |cc|
# cc.command = ENV['CC'] || 'gcc'
@@ -83,6 +82,46 @@ MRuby::Build.new do |conf|
# conf.enable_bintest
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(MRB_ENABLE_DEBUG_HOOK)
+
+ # Generate mruby debugger command (require mruby-eval)
+ conf.gem :core => "mruby-bin-debugger"
+
+ # bintest
+ # conf.enable_bintest
+end
+
+MRuby::Build.new('test') do |conf|
+ # Gets set by the VS command prompts.
+ if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+ toolchain :visualcpp
+ else
+ toolchain :gcc
+ end
+
+ enable_debug
+ conf.enable_bintest
+ conf.enable_test
+
+ conf.gembox 'default'
+end
+
# Define cross build settings
# MRuby::CrossBuild.new('32bit') do |conf|
# toolchain :gcc
diff --git a/doc/compile/README.md b/doc/guides/compile.md
index 4309dcdc4..a1c89882a 100644
--- a/doc/compile/README.md
+++ b/doc/guides/compile.md
@@ -1,6 +1,6 @@
# Compile
-mruby is using Rake to compile and cross-compile all libraries and
+mruby uses Rake to compile and cross-compile all libraries and
binaries.
## Prerequisites
@@ -22,10 +22,11 @@ Optional:
Inside of the root directory of the mruby source a file exists
called *build_config.rb*. This file contains the build configuration
of mruby and looks like this for example:
-
- MRuby::Build.new do |conf|
- toolchain :gcc
- end
+```ruby
+MRuby::Build.new do |conf|
+ toolchain :gcc
+end
+```
All tools necessary to compile mruby can be set or modified here. In case
you want to maintain an additional *build_config.rb* you can define a
@@ -49,29 +50,33 @@ configure the build environment for specific compiler infrastructures.
#### GCC
Toolchain configuration for the GNU C Compiler.
-
- toolchain :gcc
+```ruby
+toolchain :gcc
+```
#### clang
Toolchain configuration for the LLVM C Compiler clang. Mainly equal to the
GCC toolchain.
-
- toolchain :clang
+```ruby
+toolchain :clang
+```
#### Visual Studio 2010, 2012 and 2013
Toolchain configuration for Visual Studio on Windows. If you use the
[Visual Studio Command Prompt](http://msdn.microsoft.com/en-us/library/ms229859\(v=vs.110\).aspx),
you normally do not have to specify this manually, since it gets automatically detected by our build process.
-
- toolchain :visualcpp
+```
+toolchain :visualcpp
+```
#### Android
Toolchain configuration for Android.
-
- toolchain :androideabi
+```ruby
+toolchain :android
+```
Requires the custom standalone Android NDK and the toolchain path
in ```ANDROID_STANDALONE_TOOLCHAIN```.
@@ -84,123 +89,132 @@ process. The following tools can be selected:
* mirb (mruby interactive shell)
To select them declare conf.gem as follows:
-
- conf.gem "#{root}/mrbgems/mruby-bin-mruby"
-
- conf.gem "#{root}/mrbgems/mruby-bin-mirb"
+```ruby
+conf.gem "#{root}/mrbgems/mruby-bin-mruby"
+conf.gem "#{root}/mrbgems/mruby-bin-mirb"
+```
### File Separator
Some environments require a different file separator character. It is possible to
set the character via ```conf.file_separator```.
-
- conf.file_separator = '/'
+```ruby
+conf.file_separator = '/'
+```
### C Compiler
Configuration of the C compiler binary, flags and include paths.
-
- conf.cc do |cc|
- cc.command = ...
- cc.flags = ...
- cc.include_paths = ...
- cc.defines = ...
- cc.option_include_path = ...
- cc.option_define = ...
- cc.compile_options = ...
- end
+```ruby
+conf.cc do |cc|
+ cc.command = ...
+ cc.flags = ...
+ cc.include_paths = ...
+ cc.defines = ...
+ cc.option_include_path = ...
+ cc.option_define = ...
+ cc.compile_options = ...
+end
+```
C Compiler has header searcher to detect installed library.
If you need a include path of header file use ```search_header_path```:
-
- # Searches ```iconv.h```.
- # If found it will return include path of the header file.
- # Otherwise it will return nil .
- fail 'iconv.h not found' unless conf.cc.search_header_path 'iconv.h'
+```ruby
+# Searches ```iconv.h```.
+# If found it will return include path of the header file.
+# Otherwise it will return nil .
+fail 'iconv.h not found' unless conf.cc.search_header_path 'iconv.h'
+```
If you need a full file name of header file use ```search_header```:
-
- # Searches ```iconv.h```.
- # If found it will return full path of the header file.
- # Otherwise it will return nil .
- iconv_h = conf.cc.search_header 'iconv.h'
- print "iconv.h found: #{iconv_h}\n"
+```ruby
+# Searches ```iconv.h```.
+# If found it will return full path of the header file.
+# Otherwise it will return nil .
+iconv_h = conf.cc.search_header 'iconv.h'
+print "iconv.h found: #{iconv_h}\n"
+```
Header searcher uses compiler's ```include_paths``` by default.
When you are using GCC toolchain (including clang toolchain since its base is gcc toolchain)
it will use compiler specific include paths too. (For example ```/usr/local/include```, ```/usr/include```)
If you need a special header search paths define a singleton method ```header_search_paths``` to C compiler:
-
- def conf.cc.header_search_paths
- ['/opt/local/include'] + include_paths
- end
-
+```ruby
+def conf.cc.header_search_paths
+ ['/opt/local/include'] + include_paths
+end
+```
### Linker
Configuration of the Linker binary, flags and library paths.
-
- conf.linker do |linker|
- linker.command = ...
- linker.flags = ...
- linker.flags_before_libraries = ...
- linker.libraries = ...
- linker.flags_after_libraries = ...
- linker.library_paths = ....
- linker.option_library = ...
- linker.option_library_path = ...
- linker.link_options = ...
- end
+```ruby
+conf.linker do |linker|
+ linker.command = ...
+ linker.flags = ...
+ linker.flags_before_libraries = ...
+ linker.libraries = ...
+ linker.flags_after_libraries = ...
+ linker.library_paths = ....
+ linker.option_library = ...
+ linker.option_library_path = ...
+ linker.link_options = ...
+end
+```
### Archiver
Configuration of the Archiver binary and flags.
-
- conf.archiver do |archiver|
- archiver.command = ...
- archiver.archive_options = ...
- end
+```ruby
+conf.archiver do |archiver|
+ archiver.command = ...
+ archiver.archive_options = ...
+end
+```
### Parser Generator
Configuration of the Parser Generator binary and flags.
-
- conf.yacc do |yacc|
- yacc.command = ...
- yacc.compile_options = ...
- end
+```ruby
+conf.yacc do |yacc|
+ yacc.command = ...
+ yacc.compile_options = ...
+end
+```
### GPerf
Configuration of the GPerf binary and flags.
-
- conf.gperf do |gperf|
- gperf.command = ...
- gperf.compile_options = ...
- end
+```ruby
+conf.gperf do |gperf|
+ gperf.command = ...
+ gperf.compile_options = ...
+end
+```
### File Extensions
-
- conf.exts do |exts|
- exts.object = ...
- exts.executable = ...
- exts.library = ...
- end
+```ruby
+conf.exts do |exts|
+ exts.object = ...
+ exts.executable = ...
+ exts.library = ...
+end
+```
### Mrbgems
Integrate GEMs in the build process.
-
- # Integrate GEM with additional configuration
- conf.gem 'path/to/gem' do |g|
- g.cc.flags << ...
- end
-
- # Integrate GEM without additional configuration
- conf.gem 'path/to/another/gem'
-
+```ruby
+# Integrate GEM with additional configuration
+conf.gem 'path/to/gem' do |g|
+ g.cc.flags << ...
+end
+
+# Integrate GEM without additional configuration
+conf.gem 'path/to/another/gem'
+```
See doc/mrbgems/README.md for more option about mrbgems.
@@ -209,8 +223,9 @@ See doc/mrbgems/README.md for more option about mrbgems.
Configuration Mrbtest build process.
If you want mrbtest.a only, You should set ```conf.build_mrbtest_lib_only```
-
- conf.build_mrbtest_lib_only
+```ruby
+conf.build_mrbtest_lib_only
+```
### Bintest
@@ -220,8 +235,9 @@ See ```mruby-bin-*/bintest/*.rb``` if you need examples.
If you want a temporary files use `tempfile` module of CRuby instead of ```/tmp/```.
You can enable it with following:
-
- conf.enable_bintest
+```ruby
+conf.enable_bintest
+```
### C++ ABI
@@ -229,25 +245,28 @@ mruby can use C++ exception to raise exception internally.
It is called C++ ABI mode.
By using C++ exception it can release C++ stack object correctly.
Whenever you mix C++ code C++ ABI mode would be enabled automatically.
-If you need to enable C++ ABI mode explicity add the following:
-
- conf.enable_cxx_abi
+If you need to enable C++ ABI mode explicitly add the following:
+```ruby
+conf.enable_cxx_abi
+```
#### C++ exception disabling.
If you need to force C++ exception disable
(For example using a compiler option to disable C++ exception)
add following:
-
- conf.disable_cxx_exception
+```ruby
+conf.disable_cxx_exception
+```
Note that it must be called before ```enable_cxx_abi``` or ```gem``` method.
### Debugging mode
To enable debugging mode add the following:
-
- conf.enable_debug
+```ruby
+conf.enable_debug
+```
When debugging mode is enabled
* Macro ```MRB_DEBUG``` would be defined.
@@ -263,13 +282,14 @@ achieve this the *build_config.rb* needs to contain an instance of
```MRuby::CrossBuild```. This instance defines the compilation
tools and flags for the target platform. An example could look
like this:
+```ruby
+MRuby::CrossBuild.new('32bit') do |conf|
+ toolchain :gcc
- MRuby::CrossBuild.new('32bit') do |conf|
- toolchain :gcc
-
- conf.cc.flags << "-m32"
- conf.linker.flags << "-m32"
- end
+ conf.cc.flags << "-m32"
+ conf.linker.flags << "-m32"
+end
+```
All configuration options of ```MRuby::Build``` can also be used
in ```MRuby::CrossBuild```.
@@ -278,15 +298,16 @@ in ```MRuby::CrossBuild```.
In cross compilation, you can run ```mrbtest``` on emulator if
you have it by changing configuration of test runner.
-
- conf.test_runner do |t|
- t.command = ... # set emulator. this value must be non nil or false
- t.flags = ... # set flags of emulator
-
- def t.run(bin) # override `run` if you need to change the behavior of it
- ... # `bin` is the full path of mrbtest
- end
- end
+```ruby
+conf.test_runner do |t|
+ t.command = ... # set emulator. this value must be non nil or false
+ t.flags = ... # set flags of emulator
+
+ def t.run(bin) # override `run` if you need to change the behavior of it
+ ... # `bin` is the full path of mrbtest
+ end
+end
+```
## Build process
@@ -388,7 +409,7 @@ like this:
An extra directory is created for the target platform. In case you
compile for *i386* a directory called *i386* is created under the
-build direcotry.
+build directory.
The cross compilation workflow starts in the same way as the normal
compilation by compiling all *native* libraries and binaries.
@@ -438,12 +459,14 @@ To build a minimal mruby library you need to use the Cross Compiling
feature due to the reason that there are functions (i.e. stdio) which
can't be disabled for the main build.
- MRuby::CrossBuild.new('Minimal') do |conf|
- toolchain :gcc
+```ruby
+MRuby::CrossBuild.new('Minimal') do |conf|
+ toolchain :gcc
- conf.cc.defines = %w(DISABLE_STDIO)
- conf.bins = []
- end
+ conf.cc.defines = %w(MRB_DISABLE_STDIO)
+ conf.bins = []
+end
+```
This configuration defines a cross compile build called 'Minimal' which
is using the GCC and compiles for the host machine. It also disables
diff --git a/doc/guides/debugger.md b/doc/guides/debugger.md
new file mode 100644
index 000000000..366701c88
--- /dev/null
+++ b/doc/guides/debugger.md
@@ -0,0 +1,370 @@
+# How to Use the mruby Debugger
+
+copyright (c) 2014 Specified Non-Profit Corporation mruby Forum
+
+## 1. Summary
+
+This file documents the mruby debugger ('mrdb') methods.
+
+## 2 Debugging with mrdb
+
+## 2.1 Building mrdb
+
+The trunk of the mruby source tree, with the most recent mrdb, can be checked out with the following command:
+
+```bash
+$ git clone https://github.com/mruby/mruby.git
+```
+
+To run the `make` command:
+
+```bash
+$ cd mruby
+$ make
+```
+
+By default, the `make` command will install the debugger files into mruby/bin.
+
+You can add the path for mrdb on your host environment with the following command:
+
+```bash
+$ echo "export PATH=\$PATH:MRUBY_ROOT/bin" >> ~/.bashrc
+$ source ~/.bashrc
+```
+
+`*MRUBY_ROOT` is the directory in which mruby source code will be installed.
+
+To confirm mrdb was installed properly, run mrdb with the `--version` option:
+
+```bash
+$ mrdb --version
+mruby 1.2.0 (2014-11-17)
+```
+
+## 2.2 Basic Operation
+
+### 2.2.1 Debugging mruby Script Files (rb file) with mrdb
+
+To invoke the 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 execute the 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|evaluating 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 Files (mrb file) with mrdb
+
+You can debug the mruby binary files.
+
+#### 2.2.2.1 Debugging the binary files
+
+* notice
+To debug mruby binary files, you need to compile mruby files with option `-g`.
+
+```bash
+$ mrbc -g sample.rb
+```
+
+You can debug 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 use any breakpoint to stop the program by specifying the line number and method name.
+The breakpoint list will be displayed after you have set the breakpoint successfully.
+
+Usage:
+
+```
+break [file:]linenum
+b [file:]linenum
+break [class:]method
+b [class:]method
+```
+
+The breakpoint will be ordered in serial from 1.
+The number, which was given to the deleted breakpoint, will never 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 get the current breakpoint information by the 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
+
+When resuming the program, it will stop at breakpoint N (N-1 breakpoint will be ignored).
+
+When you run the `continue` command without specifying N, the program will be stopped at the next breakpoint.
+
+Example:
+
+```
+(foo.rb:1) continue 3
+```
+
+This will resume the program and stop it at the third breakpoint.
+
+#### Delete Command
+
+This will delete the specified breakpoint.
+
+Usage:
+
+```
+delete [breakpoint-no]
+d [breakpoint-no]
+```
+
+breakpoint-no: breakpoint number
+
+Example:
+
+```
+(foo.rb:1) delete
+```
+
+This will delete all of the breakpoints.
+
+```
+(foo.rb:1) delete 1 3
+```
+
+This will delete the breakpoint at 1 and 3.
+
+#### Disable Command
+
+This will disable the specified breakpoint.
+
+Usage:
+
+```
+disable [breakpoint-no]
+dis [breakpoint-no]
+```
+
+reappointing: breakpoint number
+
+Example:
+
+```
+(foo.rb:1) disable
+```
+
+Use `disable` if you would like to disable all of the breakpoints.
+
+```
+(foo.rb:1) disable 1 3
+```
+
+This will disable the breakpoints at 1 and 3.
+
+#### Enable Command
+
+This will enable the specified breakpoints.
+
+Usage:
+
+```
+enable [breakpoint-no]
+e [breakpoint-no]
+```
+
+breakpoint-no: breakpoint number
+
+Example:
+
+```
+(foo.rb:1) enable
+```
+
+Enabling all breakpoints
+```
+(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 [command]
+h [command]
+```
+
+Typing `help` without any options will display the command list.
+
+#### Info Breakpoints Command
+
+Displaying the specified breakpoint information.
+
+Usage:
+
+```
+info breakpoints [breakpoint-no]
+i b [breakpoint-no]
+```
+
+breakpoint-no: 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 the specified 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
+
+To display the code 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 the `first`, but not the `last` option, you will receive 10 rows.
+When you do not specify both the `first` and `last` options, you will receive the next 10 rows.
+
+Example:
+
+```
+Specifying file name and first row number
+sample.rb:1) list sample2.rb:5
+```
+
+Specifying the file name and the 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
+
+The expression is mandatory.
+The displayed expressions will be serially ordered 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
+
+This will run the program step by step.
+When the method and the block are invoked, the program will be stop at the first row.
+The program, which is developed in C, will be ignored.
diff --git a/doc/guides/gc-arena-howto.md b/doc/guides/gc-arena-howto.md
new file mode 100644
index 000000000..d6659c831
--- /dev/null
+++ b/doc/guides/gc-arena-howto.md
@@ -0,0 +1,177 @@
+# How to use `mrb_gc_arena_save()`/`mrb_gc_arena_restore()`/`mrb_gc_protect()`
+
+_This is an English translation of [Matz's blog post][matz blog post]
+written in Japanese._
+_Some parts are updated to reflect recent changes._
+[matz blog post]: http://www.rubyist.net/~matz/20130731.html
+
+When you are extending mruby using C language, you may encounter
+mysterious "arena overflow error" or memory leak or very slow
+execution speed. This is an error indicating overflow of "GC arena"
+implementing "conservative GC".
+
+GC (garbage collector) must ensure that object is "alive", in other
+words, that it is referenced by somewhere from program. This can be
+determined by checking if the object can be directly or indirectly
+referenced by root. The local variables, global variables and
+constants etc are root.
+
+If program execution is performed inside mruby VM, there is nothing to
+worry about because GC can access all roots owned by VM.
+
+The problem arises when executing C functions. The object referenced
+by C variable is also "alive", but mruby GC cannot aware of this, so
+it might mistakenly recognize the objects referenced by only C
+variables as dead.
+
+This can be a fatal bug if the GC tries to collect a live object.
+
+In CRuby, we scan C stack area, and use C variable as root to check
+whether object is alive or not. Of course, because we are accessing C
+stack just as memory region, we never know it is an integer or a
+pointer. We workaround this by assuming that if it looks like a
+pointer, then assume it as a pointer. We call it "conservative".
+
+By the way, CRuby's "conservative GC" has some problems.
+
+The biggest problem is we have no way to access to the stack area in
+portable way. Therefore, we cannot use this method if we'd like to
+implement highly portable runtime, like mruby.
+
+So we came up with an another plan to implement "conservative GC" in mruby.
+
+Again, the problem is when an object which was created in C function, becomes
+no longer referenced in the Ruby world, and cannot be treated as garbage.
+
+In mruby, we recognize all objects created in C function are alive.
+Then we have no problem such as confusing a live object as dead.
+
+This means that because we cannot collect truly dead object, we may
+lose efficiency, but as a trade-off the GC itself is highly portable.
+We can say goodbye to the problem that GC deletes live objects due to
+optimization which sometimes occurs in CRuby.
+
+According to this idea, we have a table, called "GC arena", which
+remembers objects created in C function.
+
+The arena is stack structure, when C function execution is returned to mruby
+VM, all objects registered in the arena are popped.
+
+This works very well, but can cause another problem: "arena overflow error" or
+memory leak.
+
+As of this writing, mruby automatically extend arena to remember
+objects (See `MRB_GC_FIXED_ARENA` and `MRB_GC_ARENA_SIZE` in
+doc/guides/mrbconf.md).
+
+If you create many objects in C functions, memory usage will increase, since
+GC never kick in. This memory usage may look like memory leak, but will also
+make execution slower as more memory will need to be allocated.
+
+With the build time configuration, you can limit the maximum size of
+arena (e.g., 100). Then if you create many objects, arena overflows,
+thus you will get an "arena overflow error".
+
+To workaround these problems, we have `mrb_gc_arena_save()` and
+`mrb_gc_arena_restore()` functions.
+
+`int mrb_gc_arena_save(mrb)` returns the current position of the stack
+top of GC arena, and `void mrb_gc_arena_restore(mrb, idx)` sets the
+stack top position to back to given `idx`.
+
+We can use them like this:
+
+```c
+int arena_idx = mrb_gc_arena_save(mrb);
+
+// ...create objects...
+mrb_gc_arena_restore(mrb, arena_idx);
+
+```
+
+In mruby, C function calls are surrounded by this save/restore, but we
+can further optimize memory usage by surrounding save/restore, and can
+avoid creating arena overflow bugs.
+
+Let's take a real example. Here is the source code of `Array#inspect`:
+
+```c
+static mrb_value
+inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list)
+{
+ mrb_int i;
+ mrb_value s, arystr;
+ char head[] = { '[' };
+ char sep[] = { ',', ' ' };
+ char tail[] = { ']' };
+
+ /* check recursive */
+ for(i=0; i<RARRAY_LEN(list); i++) {
+ if (mrb_obj_equal(mrb, ary, RARRAY_PTR(list)[i])) {
+ return mrb_str_new(mrb, "[...]", 5);
+ }
+ }
+
+ mrb_ary_push(mrb, list, ary);
+
+ arystr = mrb_str_buf_new(mrb, 64);
+ mrb_str_buf_cat(mrb, arystr, head, sizeof(head));
+
+ for(i=0; i<RARRAY_LEN(ary); i++) {
+ int ai = mrb_gc_arena_save(mrb);
+
+ if (i > 0) {
+ mrb_str_buf_cat(mrb, arystr, sep, sizeof(sep));
+ }
+ if (mrb_array_p(RARRAY_PTR(ary)[i])) {
+ s = inspect_ary(mrb, RARRAY_PTR(ary)[i], list);
+ }
+ else {
+ s = mrb_inspect(mrb, RARRAY_PTR(ary)[i]);
+ }
+ mrb_str_buf_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s));
+ mrb_gc_arena_restore(mrb, ai);
+ }
+
+ mrb_str_buf_cat(mrb, arystr, tail, sizeof(tail));
+ mrb_ary_pop(mrb, list);
+
+ return arystr;
+}
+```
+
+This is a real example, so a little bit complicated, but bear with me.
+The essence of `Array#inspect` is that after stringifying each element
+of array using `inspect` method, we join them together so that we can
+get `inspect` representation of the entire array.
+
+After the `inspect` representation is created, we no longer require the
+individual string representation. This means that we don't have to register
+these temporal objects into GC arena.
+
+Therefore, in order to keep the arena size small; the `ary_inspect()` function
+will do the following:
+
+* save the position of the stack top using `mrb_gc_arena_save()`.
+* get `inspect` representation of each element.
+* append it to the constructing entire `inspect` representation of array.
+* restore stack top position using `mrb_gc_arena_restore()`.
+
+Please note that the final `inspect` representation of entire array
+was created before the call of `mrb_gc_arena_restore()`. Otherwise,
+required temporal object may be deleted by GC.
+
+We may have a usecase where after creating many temporal objects, we'd
+like to keep some of them. In this case, we cannot use the same idea
+in `ary_inspect()` like appending objects to existing one.
+Instead, after `mrb_gc_arena_restore()`, we must re-register the objects we
+want to keep in the arena using `mrb_gc_protect(mrb, obj)`.
+Use `mrb_gc_protect()` with caution because it could also lead to an "arena
+overflow error".
+
+We must also mention that when `mrb_funcall` is called in top level, the return
+value is also registered to GC arena, so repeated use of `mrb_funcall` may
+eventually lead to an "arena overflow error".
+
+Use `mrb_gc_arena_save()` and `mrb_gc_arena_restore()` or possible use of
+`mrb_gc_protect()` to workaround this.
diff --git a/doc/guides/mrbconf.md b/doc/guides/mrbconf.md
new file mode 100644
index 000000000..fabd7208b
--- /dev/null
+++ b/doc/guides/mrbconf.md
@@ -0,0 +1,157 @@
+# mruby configuration macros.
+
+## How to use these macros.
+You can use mrbconfs with following ways:
+* Write them in `mrbconf.h`.
+ * Using compiler flags is preferred when building a cross binaries or multiple mruby binaries
+ since it's easier to use different mrbconf per each `MRuby::Build`.
+ * Most flags can be enabled by just commenting in.
+* Pass them as compiler flags.
+ * Make sure you pass the same flags to all compilers since some mrbconf(e.g., `MRB_GC_FIXED_ARENA`)
+ changes `struct` layout and cause memory access error when C and other language(e.g., C++) is mixed.
+
+## stdio setting.
+`MRB_DISABLE_STDIO`
+* When defined `<stdio.h>` functions won't be used.
+* Some features will be disabled when this is enabled:
+ * `mrb_irep` load/dump from/to file.
+ * Compiling mruby script from file.
+ * Printing features in **src/print.c**.
+
+## Debug macros.
+`MRB_ENABLE_DEBUG_HOOK`
+* When defined code fetch hook and debug OP hook will be enabled.
+* When using any of the hook set function pointer `code_fetch_hook` and/or `debug_op_hook` of `mrb_state`.
+* Fetch hook will be called before any OP.
+* Debug OP hook will be called when dispatching `OP_DEBUG`.
+
+`MRB_DEBUG`
+* When defined `mrb_assert*` macro will be defined with macros from `<assert.h>`.
+* Could be enabled via `enable_debug` method of `MRuby::Build`.
+
+## Stack configuration
+
+`MRB_STACK_EXTEND_DOUBLING`
+* If defined doubles the stack size when extending it.
+* Else extends stack with `MRB_STACK_GROWTH`.
+
+`MRB_STACK_GROWTH`
+* Default value is `128`.
+* Used in stack extending.
+* Ignored when `MRB_STACK_EXTEND_DOUBLING` is defined.
+
+`MRB_STACK_MAX`
+* Default value is `0x40000 - MRB_STACK_GROWTH`.
+* Raises `RuntimeError` when stack size exceeds this value.
+
+## Primitive type configuration.
+
+`MRB_USE_FLOAT`
+* When defined single precision floating point type(C type `float`) is used as `mrb_float`.
+* Else double precision floating point type(C type `double`) is used as `mrb_float`.
+
+`MRB_INT16`
+* When defined `int16_t` will be defined as `mrb_int`.
+* Conflicts with `MRB_INT64`.
+
+`MRB_INT64`
+* When defined `int64_t` will be defined as `mrb_int`.
+* Conflicts with `MRB_INT16`.
+* When `MRB_INT16` or `MRB_INT64` isn't defined `int`(most of the times 32-bit integer)
+will be defined as `mrb_int`.
+
+## Garbage collector configuration.
+
+`MRB_GC_STRESS`
+* When defined full GC is emitted per each `RBasic` allocation.
+* Mainly used in memory manager debugging.
+
+`MRB_GC_TURN_OFF_GENERATIONAL`
+* When defined turns generational GC by default.
+
+`MRB_GC_FIXED_ARENA`
+* When defined used fixed size GC arena.
+* Raises `RuntimeError` when this is defined and GC arena size exceeds `MRB_GC_ARENA_SIZE`.
+* Useful tracking unnecessary mruby object allocation.
+
+`MRB_GC_ARENA_SIZE`
+* Default value is `100`.
+* Ignored when `MRB_GC_FIXED_ARENA` isn't defined.
+* Defines fixed GC arena size.
+
+`MRB_HEAP_PAGE_SIZE`
+* Defines value is `1024`.
+* Specifies number of `RBasic` per each heap page.
+
+## Memory pool configuration.
+
+`POOL_ALIGNMENT`
+* Default value is `4`.
+* If you're allocating data types that requires alignment more than default value define the
+largest value of required alignment.
+
+`POOL_PAGE_SIZE`
+* Default value is `16000`.
+* Specifies page size of pool page.
+* Smaller the value is increases memory overhead.
+
+## State atexit configuration.
+
+`MRB_FIXED_STATE_ATEXIT_STACK`
+* If defined enables fixed size `mrb_state` atexit stack.
+* Raises `RuntimeError` when `mrb_state_atexit` call count to same `mrb_state` exceeds
+`MRB_FIXED_STATE_ATEXIT_STACK_SIZE`'s value.
+
+`MRB_FIXED_STATE_ATEXIT_STACK_SIZE`
+* Default value is `5`.
+* If `MRB_FIXED_STATE_ATEXIT_STACK` isn't defined this macro is ignored.
+
+## `mrb_value` configuration.
+
+`MRB_ENDIAN_BIG`
+* If defined compiles mruby for big endian machines.
+* Used in `MRB_NAN_BOXING`.
+* Some mrbgem use this mrbconf.
+
+`MRB_NAN_BOXING`
+* If defined represent `mrb_value` in boxed `double`.
+* Conflicts with `MRB_USE_FLOAT`.
+
+`MRB_WORD_BOXING`
+* If defined represent `mrb_value` as a word.
+* If defined `Float` will be a mruby object with `RBasic`.
+
+## Instance variable configuration.
+`MRB_USE_IV_SEGLIST`
+* If defined enable segmented list in instance variable table instead of khash.
+* Segmented list is a linked list of key and value segments.
+* It will linear search instead of hash search.
+
+`MRB_SEGMENT_SIZE`
+* Default value is `4`.
+* Specifies size of each segment in segment list.
+* Ignored when `MRB_USE_IV_SEGLIST` isn't defined.
+
+`MRB_IVHASH_INIT_SIZE`
+* Default value is `8`.
+* Specifies initial size for instance variable table.
+* Ignored when `MRB_USE_IV_SEGLIST` is defined.
+
+## Other configuration.
+`MRB_UTF8_STRING`
+* Adds UTF-8 encoding support to character-oriented String instance methods.
+* If it isn't defined, they only support the US-ASCII encoding.
+
+`MRB_FUNCALL_ARGC_MAX`
+* Default value is `16`.
+* Specifies 4th argument(`argc`) max value of `mrb_funcall`.
+* Raises `ArgumentError` when the `argc` argument is bigger then this value `mrb_funcall`.
+
+`KHASH_DEFAULT_SIZE`
+* Default value is `32`.
+* Specifies default size of khash table bucket.
+* Used in `kh_init_ ## name` function.
+
+`MRB_STR_BUF_MIN_SIZE`
+* Default value is `128`.
+* Specifies initial capacity of `RString` created by `mrb_str_buf_new` function..
diff --git a/doc/mrbgems/README.md b/doc/guides/mrbgems.md
index 231914905..f75231f71 100644
--- a/doc/mrbgems/README.md
+++ b/doc/guides/mrbgems.md
@@ -10,20 +10,30 @@ build configuration (i.e. *build_config.rb*), mrbgems will be activated and the
extension integrated.
To add a GEM into the *build_config.rb* add the following line for example:
-
- conf.gem '/path/to/your/gem/dir'
+```ruby
+conf.gem '/path/to/your/gem/dir'
+```
You can also use a relative path which would be relative from the mruby root:
-
- conf.gem 'examples/mrbgems/ruby_extension_example'
+```ruby
+conf.gem 'examples/mrbgems/ruby_extension_example'
+```
A remote GIT repository location for a GEM is also supported:
+```ruby
+conf.gem :git => 'https://github.com/masuidrive/mrbgems-example.git', :branch => 'master'
+conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master'
+conf.gem :bitbucket => 'mruby/mrbgems-example', :branch => 'master'
+```
- conf.gem :git => 'https://github.com/masuidrive/mrbgems-example.git', :branch => 'master'
+To use mrbgem from [mgem-list](https://github.com/mruby/mgem-list) use `:mgem` option:
+```ruby
+conf.gem :mgem => 'mruby-yaml'
+conf.gem :mgem => 'yaml' # 'mruby-' prefix could be omitted
+```
- conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master'
-
- conf.gem :bitbucket => 'mruby/mrbgems-example', :branch => 'master'
+If there is missing dependencies, mrbgem dependencies solver will reference
+mrbgem from core or mgem-list.
To pull all gems from remote GIT repository on build, call ```./minirake -p```,
or ```./minirake --pull-gems```.
@@ -41,11 +51,12 @@ via `config.gem`, but wrapped in an `MRuby::GemBox` object. GemBoxes are
loaded into mruby via `config.gembox 'boxname'`.
Below we have created a GemBox containing *mruby-time* and *mrbgems-example*:
-
- MRuby::GemBox.new do |conf|
- conf.gem "#{root}/mrbgems/mruby-time"
- conf.gem :github => 'masuidrive/mrbgems-example'
- end
+```ruby
+MRuby::GemBox.new do |conf|
+ conf.gem "#{root}/mrbgems/mruby-time"
+ conf.gem :github => 'masuidrive/mrbgems-example'
+end
+```
As mentioned, the GemBox uses the same conventions as `MRuby::Build`. The GemBox
must be saved with a *.gembox* extension inside the *mrbgems* directory to to be
@@ -54,16 +65,17 @@ picked up by mruby.
To use this example GemBox, we save it as `custom.gembox` inside the *mrbgems*
directory in mruby, and add the following to our *build_config.rb* file inside
the build block:
-
- conf.gembox 'custom'
-
+```ruby
+conf.gembox 'custom'
+```
This will cause the *custom* GemBox to be read in during the build process,
adding *mruby-time* and *mrbgems-example* to the build.
If you want, you can put GemBox outside of mruby directory. In that case you must
-specify absolute path like below.
-
- conf.gembox "#{ENV["HOME"]}/mygemboxes/custom"
+specify an absolute path like below.
+```ruby
+conf.gembox "#{ENV["HOME"]}/mygemboxes/custom"
+```
There are two GemBoxes that ship with mruby: [default](../../mrbgems/default.gembox)
and [full-core](../../mrbgems/full-core.gembox). The [default](../../mrbgems/default.gembox) GemBox
@@ -98,11 +110,13 @@ to compile C and Ruby files. *README.md* is a short description of your GEM.
mrbgems expects a specification file called *mrbgem.rake* inside of your
GEM directory. A typical GEM specification could look like this for example:
-
- MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec|
- spec.license = 'MIT'
- spec.author = 'mruby developers'
- end
+```ruby
+MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Example mrbgem using C and ruby'
+end
+```
The mrbgems build process will use this specification to compile Object and Ruby
files. The compilation results will be added to *lib/libmruby.a*. This file exposes
@@ -115,29 +129,32 @@ information purpose:
* `spec.author` or `spec.authors` (Developer name or a list of them)
* `spec.version` (Current version)
* `spec.description` (Detailed description)
-* `spec.summary` (Short summary)
+* `spec.summary`
+ * One line short description of mrbgem.
+ * Printed in build summary of rake when set.
* `spec.homepage` (Homepage)
* `spec.requirements` (External requirements as information for user)
-The license and author properties are required in every GEM!
+The `license` and `author` properties are required in every GEM!
In case your GEM is depending on other GEMs please use
`spec.add_dependency(gem, *requirements[, default_get_info])` like:
+```ruby
+MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
- MRuby::Gem::Specification.new('c_and_ruby_extension_example') do |spec|
- spec.license = 'MIT'
- spec.author = 'mruby developers'
-
- # Add GEM dependency mruby-parser.
- # The version must be between 1.0.0 and 1.5.2 .
- spec.add_dependency('mruby-parser', '>= 1.0.0', '<= 1.5.2')
+ # Add GEM dependency mruby-parser.
+ # The version must be between 1.0.0 and 1.5.2 .
+ spec.add_dependency('mruby-parser', '>= 1.0.0', '<= 1.5.2')
- # Use any version of mruby-uv from github.
- spec.add_dependency('mruby-uv', '>= 0.0.0', :github => 'mattn/mruby-uv')
+ # Use any version of mruby-uv from github.
+ spec.add_dependency('mruby-uv', '>= 0.0.0', :github => 'mattn/mruby-uv')
- # Use latest mruby-onig-regexp from github. (version requirements can be ignored)
- spec.add_dependency('mruby-onig-regexp', :github => 'mattn/mruby-onig-regexp')
- end
+ # Use latest mruby-onig-regexp from github. (version requirements can be omitted)
+ spec.add_dependency('mruby-onig-regexp', :github => 'mattn/mruby-onig-regexp')
+end
+```
The version requirements and default gem information are optional.
@@ -166,32 +183,37 @@ If you have conflicting GEMs use the following method:
* The `requirements` argument is same as in `add_dependency` method.
like following code:
-
- MRuby::Gem::Specification.new 'some-regexp-binding' do |spec|
- spec.license = 'BSD'
- spec.author = 'John Doe'
-
- spec.add_conflict 'mruby-onig-regexp', '> 0.0.0'
- spec.add_conflict 'mruby-hs-regexp'
- spec.add_conflict 'mruby-pcre-regexp'
- spec.add_conflict 'mruby-regexp-pcre'
- end
+```ruby
+MRuby::Gem::Specification.new 'some-regexp-binding' do |spec|
+ spec.license = 'BSD'
+ spec.author = 'John Doe'
+
+ spec.add_conflict 'mruby-onig-regexp', '> 0.0.0'
+ spec.add_conflict 'mruby-hs-regexp'
+ spec.add_conflict 'mruby-pcre-regexp'
+ spec.add_conflict 'mruby-regexp-pcre'
+end
+```
In case your GEM has more complex build requirements you can use
the following options additionally inside of your GEM specification:
-* `spec.cflags` (C compiler flags)
-* `spec.mruby_cflags` (global C compiler flags for everything)
-* `spec.mruby_ldflags` (global linker flags for everything)
-* `spec.mruby_libs` (global libraries for everything)
-* `spec.mruby_includes` (global includes for everything)
+* `spec.cc.flags` (C compiler flags)
+* `spec.cc.defines` (C compiler defines)
+* `spec.cc.include_paths` (C compiler include paths)
+* `spec.linker.flags` (Linker flags)
+* `spec.linker.libraries` (Linker libraries)
+* `spec.linker.library_paths` (Linker additional library path)
+* `spec.bins` (Generate binary file)
* `spec.rbfiles` (Ruby files to compile)
* `spec.objs` (Object files to compile)
* `spec.test_rbfiles` (Ruby test files for integration into mrbtest)
* `spec.test_objs` (Object test files for integration into mrbtest)
* `spec.test_preload` (Initialization files for mrbtest)
-### include_paths and depencency
+You also can use `spec.mruby.cc` and `spec.mruby.linker` to add extra global parameters for compiler and linker.
+
+### include_paths and dependency
Your GEM can export include paths to another GEMs that depends on your GEM.
By default, `/...absolute path.../{GEM_NAME}/include` will be exported.
@@ -215,12 +237,13 @@ mrbgems expects that you have implemented a C method called
`mrb_YOURGEMNAME_gem_init(mrb_state)`. `YOURGEMNAME` will be replaced
by the name of your GEM. If you call your GEM *c_extension_example*, your
initialisation method could look like this:
-
- void
- mrb_c_extension_example_gem_init(mrb_state* mrb) {
- struct RClass *class_cextension = mrb_define_module(mrb, "CExtension");
- mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE());
- }
+```C
+void
+mrb_c_extension_example_gem_init(mrb_state* mrb) {
+ struct RClass *class_cextension = mrb_define_module(mrb, "CExtension");
+ mrb_define_class_method(mrb, class_cextension, "c_method", mrb_c_method, MRB_ARGS_NONE());
+}
+```
### Finalize
@@ -229,10 +252,12 @@ mrbgems expects that you have implemented a C method called
by the name of your GEM. If you call your GEM *c_extension_example*, your
finalizer method could look like this:
- void
- mrb_c_extension_example_gem_final(mrb_state* mrb) {
- free(someone);
- }
+```C
+void
+mrb_c_extension_example_gem_final(mrb_state* mrb) {
+ free(someone);
+}
+```
### Example
diff --git a/doc/language/Core.md b/doc/language/Core.md
deleted file mode 100644
index d7331e7f3..000000000
--- a/doc/language/Core.md
+++ /dev/null
@@ -1,1590 +0,0 @@
-# Core Classes
-
-## Array
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.12 | n/a | src/array.c
-
-### Class Methods
-
-#### []
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.4.1 | src/array.c | mrb_ary_s_create
-
-### Methods
-
-#### *
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.2 | src/array.c | mrb_ary_times
-
-#### +
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.1 | src/array.c | mrb_ary_plus
-
-#### <<
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.3 | src/array.c | mrb_ary_push_m
-
-#### []
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.4 | src/array.c | mrb_ary_aget
-
-#### []=
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.5 | src/array.c | mrb_ary_aset
-
-#### __ary_cmp
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/array.c | mrb_ary_cmp
-
-#### __ary_eq
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/array.c | mrb_ary_eq
-
-#### clear
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.6 | src/array.c | mrb_ary_clear
-
-#### concat
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.8 | src/array.c | mrb_ary_concat_m
-
-#### delete_at
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.9 | src/array.c | mrb_ary_delete_at
-
-#### empty?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.12 | src/array.c | mrb_ary_empty_p
-
-#### first
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.13 | src/array.c | mrb_ary_first
-
-#### index
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.14 | src/array.c | mrb_ary_index_m
-
-#### initialize_copy
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.16 | src/array.c | mrb_ary_replace_m
-
-#### join
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.17 | src/array.c | mrb_ary_join_m
-
-#### last
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.18 | src/array.c | mrb_ary_last
-
-#### length
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.19 | src/array.c | mrb_ary_size
-
-#### pop
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.21 | src/array.c | mrb_ary_pop
-
-#### push
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.22 | src/array.c | mrb_ary_push_m
-
-#### replace
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.23 | src/array.c | mrb_ary_replace_m
-
-#### reverse
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.24 | src/array.c | mrb_ary_reverse
-
-#### reverse!
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.25 | src/array.c | mrb_ary_reverse_bang
-
-#### rindex
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.26 | src/array.c | mrb_ary_rindex_m
-
-#### shift
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.27 | src/array.c | mrb_ary_shift
-
-#### size
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.28 | src/array.c | mrb_ary_size
-
-#### slice
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.29 | src/array.c | mrb_ary_aget
-
-#### unshift
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.12.5.30 | src/array.c | mrb_ary_unshift_m
-
-## Exception
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.22 | n/a | src/error.c
-
-### Class Methods
-
-#### exception
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/class.c | mrb_instance_new
-
-### Methods
-
-#### ==
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/error.c | exc_equal
-
-#### backtrace
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/backtrace.c | mrb_exc_backtrace
-
-#### exception
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/error.c | exc_exception
-
-#### initialize
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/error.c | exc_initialize
-
-#### inspect
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/error.c | exc_inspect
-
-#### message
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/error.c | exc_message
-
-#### to_s
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/error.c | exc_to_s
-
-## FalseClass
-
-ISO Code | Mixins | Source File
---- | --- | ---
-n/a | n/a | src/object.c
-
-### Methods
-
-#### &
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.6.3.1 | src/object.c | false_and
-
-#### ^
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.6.3.2 | src/object.c | false_xor
-
-#### inspect
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/object.c | false_to_s
-
-#### to_s
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.6.3.3 | src/object.c | false_to_s
-
-#### |
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.6.3.4 | src/object.c | false_or
-
-## Fixnum
-
-ISO Code | Mixins | Source File
---- | --- | ---
-n/a | n/a | src/numeric.c
-
-### Methods
-
-#### %
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.5 | src/numeric.c | fix_mod
-
-#### &
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.9 | src/numeric.c | fix_and
-
-#### *
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.3 | src/numeric.c | fix_mul
-
-#### +
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.1 | src/numeric.c | fix_plus
-
-#### -
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.2 | src/numeric.c | fix_minus
-
-#### <<
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.12 | src/numeric.c | fix_lshift
-
-#### ==
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.7 | src/numeric.c | fix_equal
-
-#### >>
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.13 | src/numeric.c | fix_rshift
-
-#### ^
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.11 | src/numeric.c | fix_xor
-
-#### divmod
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.30 | src/numeric.c | fix_divmod
-
-#### eql?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.16 | src/numeric.c | fix_eql
-
-#### hash
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.18 | src/numeric.c | flo_hash
-
-#### inspect
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/numeric.c | fix_to_s
-
-#### to_f
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.23 | src/numeric.c | fix_to_f
-
-#### to_s
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.25 | src/numeric.c | fix_to_s
-
-#### |
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.10 | src/numeric.c | fix_or
-
-#### ~
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.8 | src/numeric.c | fix_rev
-
-## Float
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.9 | n/a | src/numeric.c
-
-### Methods
-
-#### %
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.5 | src/numeric.c | flo_mod
-
-#### *
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.3 | src/numeric.c | flo_mul
-
-#### +
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.1 | src/numeric.c | flo_plus
-
-#### -
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.2 | src/numeric.c | flo_minus
-
-#### ==
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.7 | src/numeric.c | flo_eq
-
-#### ceil
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.8 | src/numeric.c | flo_ceil
-
-#### divmod
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/numeric.c | flo_divmod
-
-#### eql?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.16 | src/numeric.c | flo_eql
-
-#### finite?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.9 | src/numeric.c | flo_finite_p
-
-#### floor
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.10 | src/numeric.c | flo_floor
-
-#### infinite?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.11 | src/numeric.c | flo_infinite_p
-
-#### inspect
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/numeric.c | flo_to_s
-
-#### nan?
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/numeric.c | flo_nan_p
-
-#### round
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.12 | src/numeric.c | flo_round
-
-#### to_f
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.13 | src/numeric.c | flo_to_f
-
-#### to_i
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.14 | src/numeric.c | flo_truncate
-
-#### to_int
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/numeric.c | flo_truncate
-
-#### to_s
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.16 | src/numeric.c | flo_to_s
-
-#### truncate
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.15 | src/numeric.c | flo_truncate
-
-## Hash
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.13 | n/a | src/hash.c
-
-### Methods
-
-#### []
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.2 | src/hash.c | mrb_hash_aget
-
-#### []=
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.3 | src/hash.c | mrb_hash_aset
-
-#### __delete
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.8 | src/hash.c | mrb_hash_delete
-
-#### clear
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.4 | src/hash.c | mrb_hash_clear
-
-#### default
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.5 | src/hash.c | mrb_hash_default
-
-#### default=
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.6 | src/hash.c | mrb_hash_set_default
-
-#### default_proc
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.7 | src/hash.c | mrb_hash_default_proc
-
-#### default_proc=
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.7 | src/hash.c | mrb_hash_set_default_proc
-
-#### dup
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/hash.c | mrb_hash_dup
-
-#### empty?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.12 | src/hash.c | mrb_hash_empty_p
-
-#### has_key?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.13 | src/hash.c | mrb_hash_has_key
-
-#### has_value?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.14 | src/hash.c | mrb_hash_has_value
-
-#### include?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.15 | src/hash.c | mrb_hash_has_key
-
-#### initialize
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.16 | src/hash.c | mrb_hash_init
-
-#### key?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.18 | src/hash.c | mrb_hash_has_key
-
-#### keys
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.19 | src/hash.c | mrb_hash_keys
-
-#### length
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.20 | src/hash.c | mrb_hash_size_m
-
-#### member?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.21 | src/hash.c | mrb_hash_has_key
-
-#### shift
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.24 | src/hash.c | mrb_hash_shift
-
-#### size
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.25 | src/hash.c | mrb_hash_size_m
-
-#### store
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.26 | src/hash.c | mrb_hash_aset
-
-#### to_hash
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.29 | src/hash.c | mrb_hash_to_hash
-
-#### value?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.27 | src/hash.c | mrb_hash_has_value
-
-#### values
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.13.4.28 | src/hash.c | mrb_hash_values
-
-## Integer
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.8 | n/a | src/numeric.c
-
-### Methods
-
-#### to_i
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.24 | src/numeric.c | int_to_i
-
-#### to_int
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/numeric.c | int_to_i
-
-## NilClass
-
-ISO Code | Mixins | Source File
---- | --- | ---
-n/a | n/a | src/object.c
-
-### Methods
-
-#### &
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.4.3.1 | src/object.c | false_and
-
-#### ^
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.4.3.2 | src/object.c | false_xor
-
-#### inspect
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/object.c | nil_inspect
-
-#### nil?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.4.3.4 | src/object.c | mrb_true
-
-#### to_s
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.4.3.5 | src/object.c | nil_to_s
-
-#### |
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.4.3.3 | src/object.c | false_or
-
-## Numeric
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.7 | n/a | src/numeric.c
-
-### Methods
-
-#### **
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/numeric.c | num_pow
-
-#### /
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.8.3.4 | src/numeric.c | num_div
-
-#### <=>
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.9.3.6 | src/numeric.c | num_cmp
-
-#### quo
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.7.4.5 | src/numeric.c | num_div
-
-## Proc
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.17 | n/a | src/proc.c
-
-### Methods
-
-#### arity
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/proc.c | mrb_proc_arity
-
-#### initialize
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/proc.c | mrb_proc_initialize
-
-#### initialize_copy
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/proc.c | mrb_proc_init_copy
-
-## Range
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.14 | n/a | src/range.c
-
-### Methods
-
-#### ==
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.1 | src/range.c | mrb_range_eq
-
-#### ===
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.2 | src/range.c | mrb_range_include
-
-#### begin
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.3 | src/range.c | mrb_range_beg
-
-#### end
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.5 | src/range.c | mrb_range_end
-
-#### eql?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.14 | src/range.c | range_eql
-
-#### exclude_end?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.6 | src/range.c | mrb_range_excl
-
-#### first
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.7 | src/range.c | mrb_range_beg
-
-#### include?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.8 | src/range.c | mrb_range_include
-
-#### initialize
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.9 | src/range.c | mrb_range_initialize
-
-#### initialize_copy
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.15 | src/range.c | range_initialize_copy
-
-#### inspect
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.13 | src/range.c | range_inspect
-
-#### last
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.10 | src/range.c | mrb_range_end
-
-#### member?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.11 | src/range.c | mrb_range_include
-
-#### to_s
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.14.4.12 | src/range.c | range_to_s
-
-## RuntimeError
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.28 | n/a | src/error.c
-
-## ScriptError
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.37 | n/a | src/error.c
-
-## StandardError
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.23 | n/a | src/error.c
-
-## String
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.10 | n/a | src/string.c
-
-### Methods
-
-#### *
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.5 | src/string.c | mrb_str_times
-
-#### +
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.4 | src/string.c | mrb_str_plus_m
-
-#### <=>
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.1 | src/string.c | mrb_str_cmp_m
-
-#### ==
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.2 | src/string.c | mrb_str_equal_m
-
-#### []
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.6 | src/string.c | mrb_str_aref_m
-
-#### bytes
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/string.c | mrb_str_bytes
-
-#### bytesize
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/string.c | mrb_str_bytesize
-
-#### capitalize
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.7 | src/string.c | mrb_str_capitalize
-
-#### capitalize!
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.8 | src/string.c | mrb_str_capitalize_bang
-
-#### chomp
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.9 | src/string.c | mrb_str_chomp
-
-#### chomp!
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.10 | src/string.c | mrb_str_chomp_bang
-
-#### chop
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.11 | src/string.c | mrb_str_chop
-
-#### chop!
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.12 | src/string.c | mrb_str_chop_bang
-
-#### downcase
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.13 | src/string.c | mrb_str_downcase
-
-#### downcase!
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.14 | src/string.c | mrb_str_downcase_bang
-
-#### empty?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.16 | src/string.c | mrb_str_empty_p
-
-#### eql?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.17 | src/string.c | mrb_str_eql
-
-#### hash
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.20 | src/string.c | mrb_str_hash_m
-
-#### include?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.21 | src/string.c | mrb_str_include
-
-#### index
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.22 | src/string.c | mrb_str_index_m
-
-#### initialize
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.23 | src/string.c | mrb_str_init
-
-#### initialize_copy
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.24 | src/string.c | mrb_str_replace
-
-#### inspect
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.46 | src/string.c | mrb_str_inspect
-
-#### intern
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.25 | src/string.c | mrb_str_intern
-
-#### length
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.26 | src/string.c | mrb_str_size
-
-#### replace
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.28 | src/string.c | mrb_str_replace
-
-#### reverse
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.29 | src/string.c | mrb_str_reverse
-
-#### reverse!
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.30 | src/string.c | mrb_str_reverse_bang
-
-#### rindex
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.31 | src/string.c | mrb_str_rindex_m
-
-#### size
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.33 | src/string.c | mrb_str_size
-
-#### slice
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.34 | src/string.c | mrb_str_aref_m
-
-#### split
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.35 | src/string.c | mrb_str_split_m
-
-#### to_f
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.38 | src/string.c | mrb_str_to_f
-
-#### to_i
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.39 | src/string.c | mrb_str_to_i
-
-#### to_s
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.40 | src/string.c | mrb_str_to_s
-
-#### to_str
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/string.c | mrb_str_to_s
-
-#### to_sym
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.41 | src/string.c | mrb_str_intern
-
-#### upcase
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.42 | src/string.c | mrb_str_upcase
-
-#### upcase!
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.10.5.43 | src/string.c | mrb_str_upcase_bang
-
-## Symbol
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.11 | n/a | src/symbol.c
-
-### Methods
-
-#### <=>
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/symbol.c | sym_cmp
-
-#### ===
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.11.3.1 | src/symbol.c | sym_equal
-
-#### id2name
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.11.3.2 | src/symbol.c | mrb_sym_to_s
-
-#### inspect
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.11.3.5 | src/symbol.c | sym_inspect
-
-#### to_s
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.11.3.3 | src/symbol.c | mrb_sym_to_s
-
-#### to_sym
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.11.3.4 | src/symbol.c | sym_to_sym
-
-## SyntaxError
-
-ISO Code | Mixins | Source File
---- | --- | ---
-15.2.38 | n/a | src/error.c
-
-## TrueClass
-
-ISO Code | Mixins | Source File
---- | --- | ---
-n/a | n/a | src/object.c
-
-### Methods
-
-#### &
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.5.3.1 | src/object.c | true_and
-
-#### ^
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.5.3.2 | src/object.c | true_xor
-
-#### inspect
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/object.c | true_to_s
-
-#### to_s
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.5.3.3 | src/object.c | true_to_s
-
-#### |
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.2.5.3.4 | src/object.c | true_or
-
-# Core Modules
-
-## Comparable
-
-ISO Code | Source File
---- | ---
-15.3.3 | src/compar.c
-
-## Enumerable
-
-ISO Code | Source File
---- | ---
-15.3.2 | src/enum.c
-
-## GC
-
-ISO Code | Source File
---- | ---
-n/a | src/gc.c
-
-### Class Methods
-
-#### disable
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/gc.c | gc_disable
-
-#### enable
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/gc.c | gc_enable
-
-#### generational_mode
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/gc.c | gc_generational_mode_get
-
-#### generational_mode=
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/gc.c | gc_generational_mode_set
-
-#### interval_ratio
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/gc.c | gc_interval_ratio_get
-
-#### interval_ratio=
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/gc.c | gc_interval_ratio_set
-
-#### start
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/gc.c | gc_start
-
-#### step_ratio
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/gc.c | gc_step_ratio_get
-
-#### step_ratio=
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/gc.c | gc_step_ratio_set
-
-#### test
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/gc.c | gc_test
-
-## Kernel
-
-ISO Code | Source File
---- | ---
-15.3.1 | src/kernel.c
-
-### Class Methods
-
-#### block_given?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.2.2 | src/kernel.c | mrb_f_block_given_p_m
-
-#### global_variables
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.2.4 | src/kernel.c | mrb_f_global_variables
-
-#### iterator?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.2.5 | src/kernel.c | mrb_f_block_given_p_m
-
-#### local_variables
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.2.7 | src/kernel.c | mrb_local_variables
-
-#### raise
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.2.12 | src/kernel.c | mrb_f_raise
-
-### Methods
-
-#### !=
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/kernel.c | mrb_obj_not_equal_m
-
-#### ==
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.1 | src/kernel.c | mrb_obj_equal_m
-
-#### ===
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.2 | src/kernel.c | mrb_equal_m
-
-#### __case_eqq
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/kernel.c | mrb_obj_ceqq
-
-#### __id__
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.3 | src/kernel.c | mrb_obj_id_m
-
-#### __send__
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.4 | src/kernel.c | mrb_f_send
-
-#### block_given?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.6 | src/kernel.c | mrb_f_block_given_p_m
-
-#### class
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.7 | src/kernel.c | mrb_obj_class_m
-
-#### clone
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.8 | src/kernel.c | mrb_obj_clone
-
-#### define_singleton_method
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/kernel.c | mod_define_singleton_method
-
-#### dup
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.9 | src/kernel.c | mrb_obj_dup
-
-#### eql?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.10 | src/kernel.c | mrb_obj_equal_m
-
-#### equal?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.11 | src/kernel.c | mrb_obj_equal_m
-
-#### extend
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.13 | src/kernel.c | mrb_obj_extend_m
-
-#### global_variables
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.14 | src/kernel.c | mrb_f_global_variables
-
-#### hash
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.15 | src/kernel.c | mrb_obj_hash
-
-#### initialize_copy
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.16 | src/kernel.c | mrb_obj_init_copy
-
-#### inspect
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.17 | src/kernel.c | mrb_obj_inspect
-
-#### instance_eval
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.18 | src/kernel.c | mrb_obj_instance_eval
-
-#### instance_of?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.19 | src/kernel.c | obj_is_instance_of
-
-#### instance_variable_defined?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.20 | src/kernel.c | mrb_obj_ivar_defined
-
-#### instance_variable_get
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.21 | src/kernel.c | mrb_obj_ivar_get
-
-#### instance_variable_set
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.22 | src/kernel.c | mrb_obj_ivar_set
-
-#### instance_variables
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.23 | src/kernel.c | mrb_obj_instance_variables
-
-#### is_a?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.24 | src/kernel.c | mrb_obj_is_kind_of_m
-
-#### iterator?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.25 | src/kernel.c | mrb_f_block_given_p_m
-
-#### kind_of?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.26 | src/kernel.c | mrb_obj_is_kind_of_m
-
-#### local_variables
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.28 | src/kernel.c | mrb_local_variables
-
-#### methods
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.31 | src/kernel.c | mrb_obj_methods_m
-
-#### nil?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.32 | src/kernel.c | mrb_false
-
-#### object_id
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.33 | src/kernel.c | mrb_obj_id_m
-
-#### private_methods
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.36 | src/kernel.c | mrb_obj_private_methods
-
-#### protected_methods
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.37 | src/kernel.c | mrb_obj_protected_methods
-
-#### public_methods
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.38 | src/kernel.c | mrb_obj_public_methods
-
-#### raise
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.40 | src/kernel.c | mrb_f_raise
-
-#### remove_instance_variable
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.41 | src/kernel.c | mrb_obj_remove_instance_variable
-
-#### respond_to?
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.43 | src/kernel.c | obj_respond_to
-
-#### send
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.44 | src/kernel.c | mrb_f_send
-
-#### singleton_class
-
-ISO Code | Source File | C Function
---- | --- | ---
-n/a | src/kernel.c | mrb_singleton_class
-
-#### singleton_methods
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.45 | src/kernel.c | mrb_obj_singleton_methods_m
-
-#### to_s
-
-ISO Code | Source File | C Function
---- | --- | ---
-15.3.1.3.46 | src/kernel.c | mrb_any_to_s
-
diff --git a/doc/language/README.md b/doc/language/README.md
deleted file mode 100644
index 24dd598ac..000000000
--- a/doc/language/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Language
-
-mruby is an implementation of the Ruby programming language.
-These documents are describing the language features and libraries
-which are provided together with mruby.
-
-## Built-In Class and Modules
-
-see *doc/language/Core.md*
diff --git a/doc/language/generator.rb b/doc/language/generator.rb
deleted file mode 100755
index c5bab1f84..000000000
--- a/doc/language/generator.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'pty'
-
-c_dir = File.dirname(__FILE__)
-MRUBY_ROOT = File.expand_path("#{c_dir}/../..")
-DOC_DIR = File.expand_path(c_dir)
-
-cmd = "ruby #{DOC_DIR}/mrbdoc/mrbdoc.rb #{MRUBY_ROOT} #{DOC_DIR} false"
-IO.popen(cmd, "r+") do |io|
- io.close_write
- while line = io.gets
- puts line
- end
-end
diff --git a/doc/language/mrbdoc/lib/mrbdoc_analyze.rb b/doc/language/mrbdoc/lib/mrbdoc_analyze.rb
deleted file mode 100644
index 94f368c08..000000000
--- a/doc/language/mrbdoc/lib/mrbdoc_analyze.rb
+++ /dev/null
@@ -1,231 +0,0 @@
-class MRBDoc
- SRC_DIR = 'src'
- MRBLIB_DIR = 'mrblib'
-
- def analyze_code dir, &block
- @mrb_files = {}
- @dir = File.expand_path(dir)
-
- block.call "MRBDOC\tanalyze #{@dir}"
-
- analyze(dir) do |progress|
- block.call progress
- end
- end
-
- def each_file(&block); @mrb_files.each {|k,v| block.call k,v}; end
-
- def find_c_func(c_func_name)
- each_file do |file_name, file|
- c_func = file.c_funcs(c_func_name)
- return c_func unless c_func.nil?
- end
- {}
- end
-
- def find_c_file(rb_obj_name, c_func_name)
- last_file_name_match = ''
- each_file do |file_name, file|
- c_func = file.c_funcs(c_func_name)
- if c_func and file.rb_class(rb_obj_name) or file.rb_module(rb_obj_name)
- return file_name
- elsif c_func
- last_file_name_match = file_name
- end
- end
- last_file_name_match
- end
-
- def find_c_file_by_class(name)
- each_file do |file_name, file|
- rb_class = file.rb_class(name)
- return file_name unless rb_class.nil?
- end
- 'nil'
- end
-
- def find_c_file_by_module(name)
- each_file do |file_name, file|
- rb_module = file.rb_module(name)
- return file_name unless rb_module.nil?
- end
- 'nil'
- end
-
- private
-
- def analyze dir, &block
- collect_all_files dir, &block
- end
-
- def collect_all_files dir, &block
- l = lambda {|f| block.call " - #{f.name}"}
- collect_files(src_code_dir(dir), /\.c$/, &l)
- collect_files(mrb_code_dir(dir), /\.rb$/, &l)
- end
-
- def collect_files dir, rxp, &block
- Dir.foreach(dir) do |file|
- next unless file =~ rxp
-
- file_path = "#{dir}/#{file}"
- mrb_file = MRBFile.new "#{file_path}"
- @mrb_files["#{file_path}"] = mrb_file
-
- block.call mrb_file
- end
- end
-
- def src_code_dir dir; File.expand_path SRC_DIR, dir; end
- def mrb_code_dir dir; File.expand_path MRBLIB_DIR, dir; end
-end
-
-class MRBFile
- attr_reader :name
- attr_reader :file
-
- def initialize mrb_file
- @file = mrb_file
- @name = File.basename file
- @c_funcs = {}
- @rb_class_c_def = {}
- @rb_method_c_def = {}
- @rb_class_method_c_def = {}
- @rb_module_c_def = {}
- @last_line = nil
- @assignments = {}
-
- @assignments['mrb->object_class'] = 'Object'
- @assignments['mrb->kernel_module'] = 'Kernel'
- @assignments['mrb->module_class'] = 'Module'
- @assignments['mrb->nil_class'] = 'NilClass'
- @assignments['mrb->true_class'] = 'TrueClass'
- @assignments['mrb->class_class'] = 'Class'
-
- analyze
- end
-
- def each_class &block
- @rb_class_c_def.each do |class_name, class_hsh|
- block.call class_name, class_hsh
- end
- end
-
- def each_method name, &block
- @rb_method_c_def.each do |met_name, met_hsh|
- met_name_tmp = met_name.sub /^#{name}_/, ''
- block.call met_name_tmp, met_hsh if met_hsh[:rb_class] == name
- end
- end
-
- def each_class_method name, &block
- @rb_class_method_c_def.each do |met_name, met_hsh|
- met_name_tmp = met_name.sub /^#{name}_/, ''
- block.call met_name_tmp, met_hsh if met_hsh[:rb_class] == name
- end
- end
-
- def each_module &block
- @rb_module_c_def.each do |module_name, module_hsh|
- block.call module_name, module_hsh
- end
- end
-
- def each_core_object &block
- each_class {|n| block.call n}
- each_module {|n| block.call n}
- end
-
- def c_funcs c_func_name; @c_funcs[c_func_name]; end
- def rb_class rb_class_name; @rb_class_c_def[rb_class_name]; end
- def rb_module rb_module_name; @rb_module_c_def[rb_module_name]; end
-
- private
-
- def analyze
- File.open(file).each_line.each_with_index do |line, idx|
- line_no = idx.succ
- if c_file?
- analyze_c_line line, line_no
- elsif rb_file?
- analyze_rb_line line, line_no
- else
- raise ArgumentError.new "#{file} is a not supported file type"
- end
- @last_line = line.strip
- end
- end
-
- def c_file?; (name =~ /\.c$/); end
- def rb_file?; (name =~ /\.rb$/); end
-
- RXP_C_VAR = /\s*([^\s]*?)\s*?/
- RXP_C_STR = /\s*?\"(.*?)\"\s*?/
- #RXP_C_ISO = /\s*\;\s*[\/\*]*\s*.*?([15\.]{0,3}[0-9\.]*)\s*[\\\\\*]*/
- RXP_C_ISO = /\s*;\s*[\/\*]*[\sa-zA-Z]*([\d\.]*)[\sa-zA-Z]*[\*\/]*/
-
- def analyze_c_line line, line_no
- case line.strip
- when /^([a-zA-Z\_][a-zA-Z\_0-9]*?)\((.*?)\)\s*?$/
- # assuming c method definition
- @c_funcs[$1] = {:line_no => line_no, :args => $2, :return => @last_line}
- when /mrb_define_class\(.*?\,#{RXP_C_STR}\,#{RXP_C_VAR}\)#{RXP_C_ISO}/
- # assuming ruby class definition in c
- class_name = $1.clone
- iso = $3.clone
- iso.strip!
- @rb_class_c_def[class_name] = {:c_object => $2, :iso => iso}
- assigns = line.split '='
- if assigns.size > 1
- assigns[0..-2].each do |v|
- @assignments[v.strip] = class_name
- end
- end
- when /mrb_define_module\(.*?\,#{RXP_C_STR}\)#{RXP_C_ISO}/
- # assuming ruby class definition in c
- module_name = $1.clone
- iso = $2.clone
- iso.strip!
- @rb_module_c_def[module_name] = {:iso => iso}
- assigns = line.split '='
- if assigns.size > 1
- assigns[0..-2].each do |v|
- @assignments[v.strip] = module_name
- end
- end
- when /mrb_define_method\(.*?\,#{RXP_C_VAR}\,#{RXP_C_STR}\,#{RXP_C_VAR}\,#{RXP_C_VAR}\)#{RXP_C_ISO}/
- # assuming ruby method definition in c
- name = $1.clone
- name = resolve_obj(name)
- iso = $5.clone
- iso.strip!
- @rb_method_c_def["#{name}_#{$2}"] = {:c_func => $3, :args => $4, :rb_class => name, :iso => iso}
- when /mrb_define_class_method\(.*?\,#{RXP_C_VAR}\,#{RXP_C_STR}\,#{RXP_C_VAR}\,#{RXP_C_VAR}\)#{RXP_C_ISO}/
- # assuming ruby class method definition in c
- class_name = $1.clone
- class_name = resolve_obj(class_name)
- iso = $5.clone
- iso.strip!
- @rb_class_method_c_def["#{class_name}_#{$2}"] = {:c_func => $3, :args => $4, :rb_class => class_name, :iso => iso}
- when /mrb_name_class\(.*?\,#{RXP_C_VAR}\,\s*mrb_intern\(.*?,#{RXP_C_STR}\)\)#{RXP_C_ISO}/
- class_name = $2.clone
- iso = $3.clone
- iso.strip!
- @rb_class_c_def[class_name] = {:c_object => $1, :iso => iso}
- @assignments[$1] = class_name
- when /mrb_include_module\(.*?\,#{RXP_C_VAR}\,\s*mrb_class_get\(.*?\,#{RXP_C_STR}\)\)/
- class_name = resolve_obj($1)
- mod = $2.clone
- @rb_class_c_def[class_name][:include] = [] unless @rb_class_c_def[class_name].has_key? :include
- @rb_class_c_def[class_name][:include] << mod
- end
- end
-
- def analyze_rb_line line, line_no
-
- end
-
- def resolve_obj c_var
- @assignments[c_var]
- end
-end
diff --git a/doc/language/mrbdoc/lib/mrbdoc_docu.rb b/doc/language/mrbdoc/lib/mrbdoc_docu.rb
deleted file mode 100644
index f6f327804..000000000
--- a/doc/language/mrbdoc/lib/mrbdoc_docu.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-class MRBDoc
- def write_documentation dir, cfg, &block
- block.call "MRBDOC\twrite to #{File.expand_path(dir)}"
-
- write(dir, cfg) do |progress|
- block.call progress
- end
- end
-
- private
-
- def write dir, cfg
- File.open(File.expand_path('Core.md', dir), 'wb+') do |io|
- print_core_classes(io, cfg)
- print_core_modules(io, cfg)
- end
- end
-
- def get_core_list id
- core_list = {}
- each_file do |file_path, mrb_file|
- mrb_file.send(id) do |name, cls_hsh|
- core_list[name] = {:data => cls_hsh, :methods => {}, :class_methods => {}}
- mrb_file.each_method name do |met_name, met_hsh|
- core_list[name][:methods][met_name] = met_hsh
- end
- mrb_file.each_class_method name do |met_name, met_hsh|
- core_list[name][:class_methods][met_name] = met_hsh
- end
- end
- end
- core_list
- end
-
- def print_core_classes(io, cfg)
- core_list = get_core_list :each_class
- io.puts "# Core Classes\n\n"
- core_list.sort.each do |name, hsh|
- file = find_c_file_by_class(name)
- file = file.split("#{@dir}/")[1]
- iso = hsh[:data][:iso]
- iso = 'n/a' if iso.nil? or iso == ''
- mixins = hsh[:data][:include].join(', ') unless hsh[:data][:include].nil?
- mixins = 'n/a' if mixins.nil? or mixins == ''
-
- io.puts <<CLASS
-## #{name}
-
-ISO Code | Mixins | Source File
---- | --- | ---
-#{iso} | #{mixins} | #{file}
-
-CLASS
- print_class_methods(io, hsh, cfg)
- print_methods(io, hsh, cfg)
- end
- end
-
- def print_core_modules(io, cfg)
- core_list = get_core_list :each_module
- io.puts "# Core Modules\n\n"
- core_list.sort.each do |name, hsh|
- file = find_c_file_by_module(name)
- file = file.split("#{@dir}/")[1]
- iso = hsh[:data][:iso]
- iso = 'n/a' if iso.nil? or iso == ''
-
- io.puts <<CLASS
-## #{name}
-
-ISO Code | Source File
---- | ---
-#{iso} | #{file}
-
-CLASS
- print_class_methods(io, hsh, cfg)
- print_methods(io, hsh, cfg)
- end
- end
-
- def print_methods(io, hsh, cfg)
- return unless hsh[:methods].size > 0
- io.puts "### Methods\n\n"
- hsh[:methods].sort.each do |met_name, met_hsh|
- print_method(io, met_name, met_hsh, cfg)
- end
- end
-
- def print_class_methods(io, hsh, cfg)
- return unless hsh[:class_methods].size > 0
- io.puts "### Class Methods\n\n"
- hsh[:class_methods].sort.each do |met_name, met_hsh|
- print_method(io, met_name, met_hsh, cfg)
- end
- end
-
- def print_method(io, met_name, met_hsh, cfg)
- if cfg[:print_line_no]
- line_no_head = ' | Line'
- line_no = " | #{find_c_func(met_hsh[:c_func])[:line_no]}"
- else
- line_no, line_no_head = '', ''
- end
- file = find_c_file(met_hsh[:rb_class], met_hsh[:c_func])
- file = file.split("#{@dir}/")[1]
- iso = met_hsh[:iso]
- iso = 'n/a' if iso.nil? or iso == ''
-
- io.puts <<METHOD
-#### #{met_name}
-
-ISO Code | Source File | C Function#{line_no_head}
---- | --- | ---
-#{iso} | #{file} | #{met_hsh[:c_func]}#{line_no}
-
-METHOD
- end
-end
diff --git a/doc/language/mrbdoc/mrbdoc.rb b/doc/language/mrbdoc/mrbdoc.rb
deleted file mode 100755
index cafdf112a..000000000
--- a/doc/language/mrbdoc/mrbdoc.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env ruby
-
-$: << File.dirname(__FILE__) + '/lib'
-
-require 'mrbdoc_analyze'
-require 'mrbdoc_docu'
-
-MRUBY_ROOT = ARGV[0]
-DOC_ROOT = ARGV[1]
-_WRITE_LINE_NO = ARGV[2]
-STDOUT.sync = true
-
-raise ArgumentError.new 'mruby root missing!' if MRUBY_ROOT.nil?
-raise ArgumentError.new 'doc root missing!' if DOC_ROOT.nil?
-
-if _WRITE_LINE_NO.nil?
- WRITE_LINE_NO = true
-else
- case _WRITE_LINE_NO
- when 'true'
- WRITE_LINE_NO = true
- when 'false'
- WRITE_LINE_NO = false
- else
- raise ArgumentError.new 'Line no parameter has to be false or true!'
- end
-end
-
-mrbdoc = MRBDoc.new
-
-mrbdoc.analyze_code MRUBY_ROOT do |progress|
- puts progress
-end
-
-cfg = {:print_line_no => WRITE_LINE_NO}
-mrbdoc.write_documentation DOC_ROOT, cfg do |progress|
- puts progress
-end
diff --git a/doc/mrbconf/README.md b/doc/mrbconf/README.md
deleted file mode 100644
index 930e185da..000000000
--- a/doc/mrbconf/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# mruby configuration macros.
-
-## stdio setting.
-`ENABLE_STDIO`
-* Will be defined automatically if `DISABLE_STDIO` isn't defined.
-* Uses `<stdio.h>` functions.
-
-`DISABLE_STDIO`
-* When defined `<stdio.h>` functions won't be used.
-
-## Debug macros.
-`ENABLE_DEBUG`
-* When defined code fetch hook and debug OP hook will be enabled.
-* When using any of the hook set function pointer `code_fetch_hook` and/or `debug_op_hook` of `mrb_state`.
-* Fetch hook will be called before any OP.
-* Debug OP hook will be called when dispatching `OP_DEBUG`.
-
-`DISABLE_DEBUG`
-* Will be define automatically if `ENABLE_DEBUG` isn't defined.
-
-`MRB_DEBUG`
-* When defined `mrb_assert*` macro will be defined with macros from `<assert.h>`.
-* Could be enabled via `enable_debug` method of `MRuby::Build`.
-
-## Stack configuration
-
-`MRB_STACK_EXTEND_DOUBLING`
-* If defined doubles the stack size when extending it.
-* Else extends stack with `MRB_STACK_GROWTH`.
-
-`MRB_STACK_GROWTH`
-* Default value is `128`.
-* Used in stack extending.
-* Ignored when `MRB_STACK_EXTEND_DOUBLING` is defined.
-
-`MRB_STACK_MAX`
-* Default value is `0x40000 - MRB_STACK_GROWTH`.
-* Raises `RuntimeError` when stack size exceeds this value.
diff --git a/examples/targets/build_config_ArduinoDue.rb b/examples/targets/build_config_ArduinoDue.rb
index eadcfdd43..740dcfa02 100644
--- a/examples/targets/build_config_ArduinoDue.rb
+++ b/examples/targets/build_config_ArduinoDue.rb
@@ -21,11 +21,16 @@ end
MRuby::CrossBuild.new("ArduinoDue") do |conf|
toolchain :gcc
- # Mac OS X
+ # Mac OS X, Arduino IDE <= 1.5.6
# ARDUINO_PATH = '/Applications/Arduino.app/Contents/Resources/Java'
+ # Mac OS X, Arduino IDE >= 1.5.7
+ # ARDUINO_PATH = '/Applications/Arduino.app/Contents/Java'
# GNU Linux
ARDUINO_PATH = '/opt/arduino'
+ # Arduino IDE <= 1.5.6
BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/g++_arm_none_eabi/bin"
+ # Arduino IDE >= 1.5.7
+ # BIN_PATH = "#{ARDUINO_PATH}/hardware/tools/gcc-arm-none-eabi-4.8.3-2014q1/bin"
SAM_PATH = "#{ARDUINO_PATH}/hardware/arduino/sam"
TARGET_PATH = "#{SAM_PATH}/variants/arduino_due_x"
@@ -45,7 +50,7 @@ MRuby::CrossBuild.new("ArduinoDue") do |conf|
cc.defines << %w(KHASH_DEFAULT_SIZE=8)
cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20)
cc.defines << %w(MRB_GC_STRESS)
- #cc.defines << %w(DISABLE_STDIO) #if you dont need stdio.
+ #cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio.
#cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
end
diff --git a/examples/targets/build_config_chipKITMax32.rb b/examples/targets/build_config_chipKITMax32.rb
index e3a122f43..c3a146e4e 100644
--- a/examples/targets/build_config_chipKITMax32.rb
+++ b/examples/targets/build_config_chipKITMax32.rb
@@ -47,7 +47,7 @@ MRuby::CrossBuild.new("chipKITMax32") do |conf|
cc.defines << %w(KHASH_DEFAULT_SIZE=8)
cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20)
cc.defines << %w(MRB_GC_STRESS)
- #cc.defines << %w(DISABLE_STDIO) #if you dont need stdio.
+ #cc.defines << %w(MRB_DISABLE_STDIO) #if you dont need stdio.
#cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
end
diff --git a/include/mrbconf.h b/include/mrbconf.h
index ac33ff0bf..ab5dd1a03 100644
--- a/include/mrbconf.h
+++ b/include/mrbconf.h
@@ -26,6 +26,9 @@
/* represent mrb_value as a word (natural unit of data for the processor) */
//#define MRB_WORD_BOXING
+/* string class to handle UTF-8 encoding */
+//#define MRB_UTF8_STRING
+
/* argv max size in mrb_funcall */
//#define MRB_FUNCALL_ARGC_MAX 16
@@ -38,6 +41,13 @@
/* initial size for IV khash; ignored when MRB_USE_IV_SEGLIST is set */
//#define MRB_IVHASH_INIT_SIZE 8
+/* if _etext and _edata available, mruby can reduce memory used by symbols */
+//#define MRB_USE_ETEXT_EDATA
+
+/* do not use __init_array_start to determine readonly data section;
+ effective only when MRB_USE_ETEXT_EDATA is defined */
+//#define MRB_NO_INIT_ARRAY_START
+
/* turn off generational GC by default */
//#define MRB_GC_TURN_OFF_GENERATIONAL
@@ -65,23 +75,25 @@
/* fixed size state atexit stack */
//#define MRB_FIXED_STATE_ATEXIT_STACK
-/* -DDISABLE_XXXX to drop following features */
-//#define DISABLE_STDIO /* use of stdio */
+/* -DMRB_DISABLE_XXXX to drop following features */
+//#define MRB_DISABLE_STDIO /* use of stdio */
-/* -DENABLE_XXXX to enable following features */
-//#define ENABLE_DEBUG /* hooks for debugger */
+/* -DMRB_ENABLE_XXXX to enable following features */
+//#define MRB_ENABLE_DEBUG_HOOK /* hooks for debugger */
/* end of configuration */
-/* define ENABLE_XXXX from DISABLE_XXX */
-#ifndef DISABLE_STDIO
-#define ENABLE_STDIO
+/* define MRB_DISABLE_XXXX from DISABLE_XXX (for compatibility) */
+#ifdef DISABLE_STDIO
+#define MRB_DISABLE_STDIO
#endif
-#ifndef ENABLE_DEBUG
-#define DISABLE_DEBUG
+
+/* define MRB_ENABLE_XXXX from ENABLE_XXX (for compatibility) */
+#ifdef ENABLE_DEBUG
+#define MRB_ENABLE_DEBUG_HOOK
#endif
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
# include <stdio.h>
#endif
diff --git a/include/mruby.h b/include/mruby.h
index 7b9f1b428..1ea0156e8 100644
--- a/include/mruby.h
+++ b/include/mruby.h
@@ -1,7 +1,7 @@
/*
** mruby - An embeddable Ruby implementation
**
-** Copyright (c) mruby developers 2010-2014
+** Copyright (c) mruby developers 2010-2015
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
@@ -28,30 +28,43 @@
#ifndef MRUBY_H
#define MRUBY_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#include "mrbconf.h"
-#include "mruby/value.h"
-#include "mruby/version.h"
+#include "mruby/common.h"
+#include <mruby/value.h>
+#include <mruby/gc.h>
+#include <mruby/version.h>
+
+/**
+ * MRuby C API entry point
+ */
+MRB_BEGIN_DECL
typedef uint32_t mrb_code;
+
+/**
+ * Required arguments signature type.
+ */
typedef uint32_t mrb_aspec;
+
struct mrb_irep;
struct mrb_state;
+/**
+ * Function pointer type of custom allocator used in @see mrb_open_allocf.
+ *
+ * The function pointing it must behave similarly as realloc except:
+ * - If ptr is NULL it must allocate new space.
+ * - If s is NULL, ptr must be freed.
+ *
+ * See @see mrb_default_allocf for the default implementation.
+ */
typedef void* (*mrb_allocf) (struct mrb_state *mrb, void*, size_t, void *ud);
-#ifndef MRB_GC_ARENA_SIZE
-#define MRB_GC_ARENA_SIZE 100
-#endif
-
#ifndef MRB_FIXED_STATE_ATEXIT_STACK_SIZE
#define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5
#endif
@@ -98,12 +111,6 @@ struct mrb_context {
struct RFiber *fib;
};
-enum gc_state {
- GC_STATE_NONE = 0,
- GC_STATE_MARK,
- GC_STATE_SWEEP
-};
-
struct mrb_jmpbuf;
typedef void (*mrb_atexit_func)(struct mrb_state*);
@@ -112,6 +119,7 @@ typedef struct mrb_state {
struct mrb_jmpbuf *jmp;
mrb_allocf allocf; /* memory allocation function */
+ void *allocf_ud; /* auxiliary data of allocf */
struct mrb_context *c;
struct mrb_context *root_c;
@@ -136,37 +144,15 @@ typedef struct mrb_state {
struct RClass *symbol_class;
struct RClass *kernel_module;
- struct heap_page *heaps; /* heaps for GC */
- struct heap_page *sweeps;
- struct heap_page *free_heaps;
- size_t live; /* count of live objects */
-#ifdef MRB_GC_FIXED_ARENA
- struct RBasic *arena[MRB_GC_ARENA_SIZE]; /* GC protection array */
-#else
- struct RBasic **arena; /* GC protection array */
- int arena_capa;
-#endif
- int arena_idx;
-
- enum gc_state gc_state; /* state of gc */
- int current_white_part; /* make white object by white_part */
- struct RBasic *gray_list; /* list of gray objects to be traversed incrementally */
- struct RBasic *atomic_gray_list; /* list of objects to be traversed atomically */
- size_t gc_live_after_mark;
- size_t gc_threshold;
- int gc_interval_ratio;
- int gc_step_ratio;
- mrb_bool gc_disabled:1;
- mrb_bool gc_full:1;
- mrb_bool is_generational_gc_mode:1;
- mrb_bool out_of_memory:1;
- size_t majorgc_old_threshold;
struct alloca_header *mems;
+ mrb_gc gc;
mrb_sym symidx;
- struct kh_n2s *name2sym; /* symbol table */
+ struct kh_n2s *name2sym; /* symbol hash */
+ struct symbol_name *symtbl; /* symbol table */
+ size_t symcapa;
-#ifdef ENABLE_DEBUG
+#ifdef MRB_ENABLE_DEBUG_HOOK
void (*code_fetch_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs);
void (*debug_op_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs);
#endif
@@ -185,78 +171,577 @@ typedef struct mrb_state {
mrb_int atexit_stack_len;
} mrb_state;
-#if __STDC_VERSION__ >= 201112L
-# define mrb_noreturn _Noreturn
-#elif defined __GNUC__ && !defined __STRICT_ANSI__
-# define mrb_noreturn __attribute__((noreturn))
-#elif defined _MSC_VER
-# define mrb_noreturn __declspec(noreturn)
-#else
-# define mrb_noreturn
-#endif
typedef mrb_value (*mrb_func_t)(mrb_state *mrb, mrb_value);
-struct RClass *mrb_define_class(mrb_state *, const char*, struct RClass*);
-struct RClass *mrb_define_module(mrb_state *, const char*);
-mrb_value mrb_singleton_class(mrb_state*, mrb_value);
-void mrb_include_module(mrb_state*, struct RClass*, struct RClass*);
-
-void mrb_define_method(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec);
-void mrb_define_class_method(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec);
-void mrb_define_singleton_method(mrb_state*, struct RObject*, const char*, mrb_func_t, mrb_aspec);
-void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec);
-void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_value);
-void mrb_undef_method(mrb_state*, struct RClass*, const char*);
-void mrb_undef_class_method(mrb_state*, struct RClass*, const char*);
-mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv);
-#define mrb_class_new_instance(mrb,argc,argv,c) mrb_obj_new(mrb,c,argc,argv)
-mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv);
-struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super);
-struct RClass * mrb_module_new(mrb_state *mrb);
-mrb_bool mrb_class_defined(mrb_state *mrb, const char *name);
-struct RClass * mrb_class_get(mrb_state *mrb, const char *name);
-struct RClass * mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name);
-struct RClass * mrb_module_get(mrb_state *mrb, const char *name);
-struct RClass * mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name);
-
-mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj);
-mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method);
-mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid);
-struct RClass * mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super);
-struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name);
-
-/* required arguments */
+
+/**
+ * Defines a new class.
+ *
+ * If you're creating a gem it may look something like this:
+ *
+ * !!!c
+ * void mrb_example_gem_init(mrb_state* mrb) {
+ * struct RClass *example_class;
+ * example_class = mrb_define_class(mrb, "Example_Class", mrb->object_class);
+ * }
+ *
+ * void mrb_example_gem_final(mrb_state* mrb) {
+ * //free(TheAnimals);
+ * }
+ *
+ * @param [mrb_state *] mrb The current mruby state.
+ * @param [const char *] name The name of the defined class.
+ * @param [struct RClass *] super The new class parent.
+ * @return [struct RClass *] Reference to the newly defined class.
+ * @see mrb_define_class_under
+ */
+MRB_API struct RClass *mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super);
+
+/**
+ * Defines a new module.
+ *
+ * @param [mrb_state *] mrb_state* The current mruby state.
+ * @param [const char *] char* The name of the module.
+ * @return [struct RClass *] Reference to the newly defined module.
+ */
+MRB_API struct RClass *mrb_define_module(mrb_state *, const char*);
+MRB_API mrb_value mrb_singleton_class(mrb_state*, mrb_value);
+
+/**
+ * Include a module in another class or module.
+ * Equivalent to:
+ *
+ * module B
+ * include A
+ * end
+ * @param [mrb_state *] mrb_state* The current mruby state.
+ * @param [struct RClass *] RClass* A reference to module or a class.
+ * @param [struct RClass *] RClass* A reference to the module to be included.
+ */
+MRB_API void mrb_include_module(mrb_state*, struct RClass*, struct RClass*);
+
+/**
+ * Prepends a module in another class or module.
+ *
+ * Equivalent to:
+ * module B
+ * prepend A
+ * end
+ * @param [mrb_state *] mrb_state* The current mruby state.
+ * @param [struct RClass *] RClass* A reference to module or a class.
+ * @param [struct RClass *] RClass* A reference to the module to be prepended.
+ */
+MRB_API void mrb_prepend_module(mrb_state*, struct RClass*, struct RClass*);
+
+/**
+ * Defines a global function in ruby.
+ *
+ * If you're creating a gem it may look something like this
+ *
+ * Example:
+ *
+ * !!!c
+ * mrb_value example_method(mrb_state* mrb, mrb_value self)
+ * {
+ * puts("Executing example command!");
+ * return self;
+ * }
+ *
+ * void mrb_example_gem_init(mrb_state* mrb)
+ * {
+ * mrb_define_method(mrb, mrb->kernel_module, "example_method", example_method, MRB_ARGS_NONE());
+ * }
+ *
+ * @param [mrb_state *] mrb The MRuby state reference.
+ * @param [struct RClass *] cla The class pointer where the method will be defined.
+ * @param [const char *] name The name of the method being defined.
+ * @param [mrb_func_t] func The function pointer to the method definition.
+ * @param [mrb_aspec] aspec The method parameters declaration.
+ */
+MRB_API void mrb_define_method(mrb_state *mrb, struct RClass *cla, const char *name, mrb_func_t func, mrb_aspec aspec);
+
+/**
+ * Defines a class method.
+ *
+ * Example:
+ *
+ * # Ruby style
+ * class Foo
+ * def Foo.bar
+ * end
+ * end
+ * // C style
+ * mrb_value bar_method(mrb_state* mrb, mrb_value self){
+ * return mrb_nil_value();
+ * }
+ * void mrb_example_gem_init(mrb_state* mrb){
+ * struct RClass *foo;
+ * foo = mrb_define_class(mrb, "Foo", mrb->object_class);
+ * mrb_define_class_method(mrb, foo, "bar", bar_method, MRB_ARGS_NONE());
+ * }
+ * @param [mrb_state *] mrb_state* The MRuby state reference.
+ * @param [struct RClass *] RClass* The class where the class method will be defined.
+ * @param [const char *] char* The name of the class method being defined.
+ * @param [mrb_func_t] mrb_func_t The function pointer to the class method definition.
+ * @param [mrb_aspec] mrb_aspec The method parameters declaration.
+ */
+MRB_API void mrb_define_class_method(mrb_state *, struct RClass *, const char *, mrb_func_t, mrb_aspec);
+MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char*, mrb_func_t, mrb_aspec);
+
+/**
+ * Defines a module fuction.
+ *
+ * Example:
+ *
+ * # Ruby style
+ * module Foo
+ * def Foo.bar
+ * end
+ * end
+ * // C style
+ * mrb_value bar_method(mrb_state* mrb, mrb_value self){
+ * return mrb_nil_value();
+ * }
+ * void mrb_example_gem_init(mrb_state* mrb){
+ * struct RClass *foo;
+ * foo = mrb_define_module(mrb, "Foo");
+ * mrb_define_module_function(mrb, foo, "bar", bar_method, MRB_ARGS_NONE());
+ * }
+ * @param [mrb_state *] mrb_state* The MRuby state reference.
+ * @param [struct RClass *] RClass* The module where the module function will be defined.
+ * @param [const char *] char* The name of the module function being defined.
+ * @param [mrb_func_t] mrb_func_t The function pointer to the module function definition.
+ * @param [mrb_aspec] mrb_aspec The method parameters declaration.
+ */
+MRB_API void mrb_define_module_function(mrb_state*, struct RClass*, const char*, mrb_func_t, mrb_aspec);
+
+/**
+ * Defines a constant.
+ *
+ * Example:
+ *
+ * # Ruby style
+ * class ExampleClass
+ * AGE = 22
+ * end
+ * // C style
+ * #include <stdio.h>
+ * #include <mruby.h>
+ *
+ * void
+ * mrb_example_gem_init(mrb_state* mrb){
+ * mrb_define_const(mrb, mrb->kernel_module, "AGE", mrb_fixnum_value(22));
+ * }
+ *
+ * mrb_value
+ * mrb_example_gem_final(mrb_state* mrb){
+ * }
+ * @param [mrb_state *] mrb_state* The MRuby state reference.
+ * @param [struct RClass *] RClass* A class or module the constant is defined in.
+ * @param [const char *] name The name of the constant being defined.
+ * @param [mrb_value] mrb_value The value for the constant.
+ */
+MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, mrb_value);
+
+/**
+ * Undefines a method.
+ *
+ * Example:
+ *
+ * # Ruby style
+ *
+ * class ExampleClassA
+ * def example_method
+ * "example"
+ * end
+ * end
+ * ExampleClassA.new.example_method # => example
+ *
+ * class ExampleClassB < ExampleClassA
+ * undef_method :example_method
+ * end
+ *
+ * ExampleClassB.new.example_method # => undefined method 'example_method' for ExampleClassB (NoMethodError)
+ *
+ * // C style
+ * #include <stdio.h>
+ * #include <mruby.h>
+ *
+ * mrb_value
+ * mrb_example_method(mrb_state *mrb){
+ * return mrb_str_new_cstr(mrb, "example");
+ * }
+ *
+ * void
+ * mrb_example_gem_init(mrb_state* mrb){
+ * struct RClass *example_class_a;
+ * struct RClass *example_class_b;
+ * struct RClass *example_class_c;
+ *
+ * example_class_a = mrb_define_class(mrb, "ExampleClassA", mrb->object_class);
+ * mrb_define_method(mrb, example_class_a, "example_method", mrb_example_method, MRB_ARGS_NONE());
+ * example_class_b = mrb_define_class(mrb, "ExampleClassB", example_class_a);
+ * example_class_c = mrb_define_class(mrb, "ExampleClassC", example_class_b);
+ * mrb_undef_method(mrb, example_class_c, "example_method");
+ * }
+ *
+ * mrb_example_gem_final(mrb_state* mrb){
+ * }
+ * @param [mrb_state*] mrb_state* The mruby state reference.
+ * @param [struct RClass*] RClass* A class the method will be undefined from.
+ * @param [const char*] constchar* The name of the method to be undefined.
+ */
+MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*);
+
+/**
+ * Undefine a class method.
+ * Example:
+ *
+ * # Ruby style
+ * class ExampleClass
+ * def self.example_method
+ * "example"
+ * end
+ * end
+ *
+ * ExampleClass.example_method
+ *
+ * // C style
+ * #include <stdio.h>
+ * #include <mruby.h>
+ *
+ * mrb_value
+ * mrb_example_method(mrb_state *mrb){
+ * return mrb_str_new_cstr(mrb, "example");
+ * }
+ *
+ * void
+ * mrb_example_gem_init(mrb_state* mrb){
+ * struct RClass *example_class;
+ * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
+ * mrb_define_class_method(mrb, example_class, "example_method", mrb_example_method, MRB_ARGS_NONE());
+ * mrb_undef_class_method(mrb, example_class, "example_method");
+ * }
+ *
+ * void
+ * mrb_example_gem_final(mrb_state* mrb){
+ * }
+ * @param [mrb_state*] mrb_state* The mruby state reference.
+ * @param [RClass*] RClass* A class the class method will be undefined from.
+ * @param [constchar*] constchar* The name of the class method to be undefined.
+ */
+MRB_API void mrb_undef_class_method(mrb_state*, struct RClass*, const char*);
+
+/**
+ * Initialize a new object instace of c class.
+ *
+ * Example:
+ *
+ * # Ruby style
+ * class ExampleClass
+ * end
+ *
+ * p ExampleClass # => #<ExampleClass:0x9958588>
+ * // C style
+ * #include <stdio.h>
+ * #include <mruby.h>
+ *
+ * void
+ * mrb_example_gem_init(mrb_state* mrb) {
+ * struct RClass *example_class;
+ * mrb_value obj;
+ * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class); # => class ExampleClass; end
+ * obj = mrb_obj_new(mrb, example_class, 0, NULL); # => ExampleClass.new
+ * mrb_p(mrb, obj); // => Kernel#p
+ * }
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [RClass*] c Reference to the class of the new object.
+ * @param [mrb_int] argc Number of arguments in argv
+ * @param [const mrb_value *] argv Array of mrb_value to initialize the object
+ * @return [mrb_value] The newly initialized object
+ */
+MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv);
+
+/** @see mrb_obj_new */
+MRB_INLINE mrb_value mrb_class_new_instance(mrb_state *mrb, mrb_int argc, const mrb_value *argv, struct RClass *c)
+{
+ return mrb_obj_new(mrb,c,argc,argv);
+}
+
+MRB_API mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv);
+
+/**
+ * Creates a new instance of Class, Class.
+ *
+ * Example:
+ *
+ * void
+ * mrb_example_gem_init(mrb_state* mrb) {
+ * struct RClass *example_class;
+ *
+ * mrb_value obj;
+ * example_class = mrb_class_new(mrb, mrb->object_class);
+ * obj = mrb_obj_new(mrb, example_class, 0, NULL); // => #<#<Class:0x9a945b8>:0x9a94588>
+ * mrb_p(mrb, obj); // => Kernel#p
+ * }
+ *
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [struct RClass *] super The super class or parent.
+ * @return [struct RClass *] Reference to the new class.
+ */
+MRB_API struct RClass * mrb_class_new(mrb_state *mrb, struct RClass *super);
+
+/**
+ * Creates a new module, Module.
+ *
+ * Example:
+ * void
+ * mrb_example_gem_init(mrb_state* mrb) {
+ * struct RClass *example_module;
+ *
+ * example_module = mrb_module_new(mrb);
+ * }
+ *
+ * @param [mrb_state*] mrb The current mruby state.
+ * @return [struct RClass *] Reference to the new module.
+ */
+MRB_API struct RClass * mrb_module_new(mrb_state *mrb);
+
+/**
+ * Returns an mrb_bool. True if class was defined, and false if the class was not defined.
+ *
+ * Example:
+ * void
+ * mrb_example_gem_init(mrb_state* mrb) {
+ * struct RClass *example_class;
+ * mrb_bool cd;
+ *
+ * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
+ * cd = mrb_class_defined(mrb, "ExampleClass");
+ *
+ * // If mrb_class_defined returns 1 then puts "True"
+ * // If mrb_class_defined returns 0 then puts "False"
+ * if (cd == 1){
+ * puts("True");
+ * }
+ * else {
+ * puts("False");
+ * }
+ * }
+ *
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [const char *] name A string representing the name of the class.
+ * @return [mrb_bool] A boolean value.
+ */
+MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name);
+
+/**
+ * Gets a class.
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [const char *] name The name of the class.
+ * @return [struct RClass *] A reference to the class.
+*/
+MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name);
+
+/**
+ * Gets a child class.
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [struct RClass *] outer The name of the parent class.
+ * @param [const char *] name The name of the class.
+ * @return [struct RClass *] A reference to the class.
+*/
+MRB_API struct RClass * mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name);
+
+/**
+ * Gets a module.
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [const char *] name The name of the module.
+ * @return [struct RClass *] A reference to the module.
+*/
+MRB_API struct RClass * mrb_module_get(mrb_state *mrb, const char *name);
+
+/**
+ * Gets a module defined under another module.
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [struct RClass *] outer The name of the outer module.
+ * @param [const char *] name The name of the module.
+ * @return [struct RClass *] A reference to the module.
+*/
+MRB_API struct RClass * mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name);
+MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
+
+/**
+ * Duplicate an object.
+ *
+ * Equivalent to:
+ * Object#dup
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [mrb_value] obj Object to be duplicate.
+ * @return [mrb_value] The newly duplicated object.
+ */
+MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj);
+MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method);
+
+/**
+ * Returns true if obj responds to the given method. If the method was defined for that
+ * class it returns true, it returns false otherwise.
+ *
+ * Example:
+ * # Ruby style
+ * class ExampleClass
+ * def example_method
+ * end
+ * end
+ *
+ * ExampleClass.new.respond_to?(:example_method) # => true
+ *
+ * // C style
+ * void
+ * mrb_example_gem_init(mrb_state* mrb) {
+ * struct RClass *example_class;
+ * mrb_sym mid;
+ * mrb_bool obj_resp;
+ *
+ * example_class = mrb_define_class(mrb, "ExampleClass", mrb->object_class);
+ * mrb_define_method(mrb, example_class, "example_method", exampleMethod, MRB_ARGS_NONE());
+ * mid = mrb_intern_str(mrb, mrb_str_new_cstr(mrb, "example_method" ));
+ * obj_resp = mrb_obj_respond_to(mrb, example_class, mid); // => 1(true in Ruby world)
+ *
+ * // If mrb_obj_respond_to returns 1 then puts "True"
+ * // If mrb_obj_respond_to returns 0 then puts "False"
+ * if (obj_resp == 1) {
+ * puts("True");
+ * }
+ * else if (obj_resp == 0) {
+ * puts("False");
+ * }
+ * }
+ *
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [struct RClass *] c A reference to a class.
+ * @param [mrb_sym] mid A symbol referencing a method id.
+ * @return [mrb_bool] A boolean value.
+ */
+MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid);
+
+/**
+ * Defines a new class under a given module
+ *
+ * @param [mrb_state*] mrb The current mruby state.
+ * @param [struct RClass *] outer Reference to the module under which the new class will be defined
+ * @param [const char *] name The name of the defined class
+ * @param [struct RClass *] super The new class parent
+ * @return [struct RClass *] Reference to the newly defined class
+ * @see mrb_define_class
+ */
+MRB_API struct RClass * mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super);
+
+MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name);
+
+/**
+ * Function requires n arguments.
+ *
+ * @param n
+ * The number of required arguments.
+ */
#define MRB_ARGS_REQ(n) ((mrb_aspec)((n)&0x1f) << 18)
-/* optional arguments */
+
+/**
+ * Funtion takes n optional arguments
+ *
+ * @param n
+ * The number of optional arguments.
+ */
#define MRB_ARGS_OPT(n) ((mrb_aspec)((n)&0x1f) << 13)
-/* mandatory and optinal arguments */
+
+/**
+ * Funtion takes n1 mandatory arguments and n2 optional arguments
+ *
+ * @param n1
+ * The number of required arguments.
+ * @param n2
+ * The number of optional arguments.
+ */
#define MRB_ARGS_ARG(n1,n2) (MRB_ARGS_REQ(n1)|MRB_ARGS_OPT(n2))
-/* rest argument */
+/** rest argument */
#define MRB_ARGS_REST() ((mrb_aspec)(1 << 12))
-/* required arguments after rest */
+
+/** required arguments after rest */
#define MRB_ARGS_POST(n) ((mrb_aspec)((n)&0x1f) << 7)
-/* keyword arguments (n of keys, kdict) */
+
+/** keyword arguments (n of keys, kdict) */
#define MRB_ARGS_KEY(n1,n2) ((mrb_aspec)((((n1)&0x1f) << 2) | ((n2)?(1<<1):0)))
-/* block argument */
+
+/**
+ * Function takes a block argument
+ */
#define MRB_ARGS_BLOCK() ((mrb_aspec)1)
-/* accept any number of arguments */
+/**
+ * Function accepts any number of arguments
+ */
#define MRB_ARGS_ANY() MRB_ARGS_REST()
-/* accept no arguments */
-#define MRB_ARGS_NONE() ((mrb_aspec)0)
-/* compatibility macros; will be removed */
-#define ARGS_REQ(n) MRB_ARGS_REQ(n)
-#define ARGS_OPT(n) MRB_ARGS_OPT(n)
-#define ARGS_REST() MRB_ARGS_REST()
-#define ARGS_POST(n) MRB_ARGS_POST()
-#define ARGS_KEY(n1,n2) MRB_ARGS_KEY(n1,n2)
-#define ARGS_BLOCK() MRB_ARGS_BLOCK()
-#define ARGS_ANY() MRB_ARGS_ANY()
-#define ARGS_NONE() MRB_ARGS_NONE()
+/**
+ * Function accepts no arguments
+ */
+#define MRB_ARGS_NONE() ((mrb_aspec)0)
-mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...);
+/**
+ * Format specifiers for {mrb_get_args} function
+ *
+ * Must be a C string composed of the following format specifiers:
+ *
+ * | char | Ruby type | C types | Notes |
+ * |:----:|----------------|-------------------|----------------------------------------------------|
+ * | `o` | {Object} | {mrb_value} | Could be used to retrieve any type of argument |
+ * | `C` | {Class}/{Module} | {mrb_value} | |
+ * | `S` | {String} | {mrb_value} | when `!` follows, the value may be `nil` |
+ * | `A` | {Array} | {mrb_value} | when `!` follows, the value may be `nil` |
+ * | `H` | {Hash} | {mrb_value} | when `!` follows, the value may be `nil` |
+ * | `s` | {String} | char *, {mrb_int} | Receive two arguments; `s!` gives (`NULL`,`0`) for `nil` |
+ * | `z` | {String} | char * | `NULL` terminated string; `z!` gives `NULL` for `nil` |
+ * | `a` | {Array} | {mrb_value} *, {mrb_int} | Receive two arguments; `a!` gives (`NULL`,`0`) for `nil` |
+ * | `f` | {Float} | {mrb_float} | |
+ * | `i` | {Integer} | {mrb_int} | |
+ * | `b` | boolean | {mrb_bool} | |
+ * | `n` | {Symbol} | {mrb_sym} | |
+ * | `&` | block | {mrb_value} | |
+ * | `*` | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array. |
+ * | &vert; | optional | | After this spec following specs would be optional. |
+ * | `?` | optional given | {mrb_bool} | `TRUE` if preceding argument is given. Used to check optional argument is given. |
+ *
+ * @see mrb_get_args
+ */
+typedef const char *mrb_args_format;
+
+/**
+ * Retrieve arguments from mrb_state.
+ *
+ * When applicable, implicit conversions (such as `to_str`, `to_ary`, `to_hash`) are
+ * applied to received arguments.
+ * Used inside a function of mrb_func_t type.
+ *
+ * @param mrb The current MRuby state.
+ * @param format [mrb_args_format] is a list of format specifiers
+ * @param ... The passing variadic arguments must be a pointer of retrieving type.
+ * @return the number of arguments retrieved.
+ * @see mrb_args_format
+ */
+MRB_API mrb_int mrb_get_args(mrb_state *mrb, mrb_args_format format, ...);
+
+static inline mrb_sym
+mrb_get_mid(mrb_state *mrb) /* get method symbol */
+{
+ return mrb->c->ci->mid;
+}
+
+static inline int
+mrb_get_argc(mrb_state *mrb) /* get argc */
+{
+ return mrb->c->ci->argc;
+}
/* `strlen` for character string literals (use with caution or `strlen` instead)
Adjacent string literals are concatenated in C/C++ in translation phase 6.
@@ -266,105 +751,240 @@ mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...);
*/
#define mrb_strlen_lit(lit) (sizeof(lit "") - 1)
-mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...);
-mrb_value mrb_funcall_argv(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*);
-mrb_value mrb_funcall_with_block(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*, mrb_value);
-mrb_sym mrb_intern_cstr(mrb_state*,const char*);
-mrb_sym mrb_intern(mrb_state*,const char*,size_t);
-mrb_sym mrb_intern_static(mrb_state*,const char*,size_t);
+/**
+ * Call existing ruby functions.
+ *
+ * #include <stdio.h>
+ * #include <mruby.h>
+ * #include "mruby/compile.h"
+ *
+ * int
+ * main()
+ * {
+ * mrb_int i = 99;
+ * mrb_state *mrb = mrb_open();
+ *
+ * if (!mrb) { }
+ * FILE *fp = fopen("test.rb","r");
+ * mrb_value obj = mrb_load_file(mrb,fp);
+ * mrb_funcall(mrb, obj, "method_name", 1, mrb_fixnum_value(i));
+ * fclose(fp);
+ * mrb_close(mrb);
+ * }
+ * @param [mrb_state*] mrb_state* The current mruby state.
+ * @param [mrb_value] mrb_value A reference to an mruby value.
+ * @param [const char*] const char* The name of the method.
+ * @param [mrb_int] mrb_int The number of arguments the method has.
+ * @param [...] ... Variadic values(not type safe!).
+ * @return [mrb_value] mrb_value mruby function value.
+ */
+MRB_API mrb_value mrb_funcall(mrb_state*, mrb_value, const char*, mrb_int,...);
+/**
+ * Call existing ruby functions. This is basically the type safe version of mrb_funcall.
+ *
+ * #include <stdio.h>
+ * #include <mruby.h>
+ * #include "mruby/compile.h"
+ * int
+ * main()
+ * {
+ * mrb_int i = 99;
+ * mrb_state *mrb = mrb_open();
+ *
+ * if (!mrb) { }
+ * mrb_sym m_sym = mrb_intern_cstr(mrb, "method_name"); // Symbol for method.
+ *
+ * FILE *fp = fopen("test.rb","r");
+ * mrb_value obj = mrb_load_file(mrb,fp);
+ * mrb_funcall_argv(mrb, obj, m_sym, 1, &obj); // Calling ruby function from test.rb.
+ * fclose(fp);
+ * mrb_close(mrb);
+ * }
+ * @param [mrb_state*] mrb_state* The current mruby state.
+ * @param [mrb_value] mrb_value A reference to an mruby value.
+ * @param [mrb_sym] mrb_sym The symbol representing the method.
+ * @param [mrb_int] mrb_int The number of arguments the method has.
+ * @param [const mrb_value*] mrb_value* Pointer to the object.
+ * @return [mrb_value] mrb_value mruby function value.
+ * @see mrb_funcall
+ */
+MRB_API mrb_value mrb_funcall_argv(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*);
+/**
+ * Call existing ruby functions with a block.
+ */
+MRB_API mrb_value mrb_funcall_with_block(mrb_state*, mrb_value, mrb_sym, mrb_int, const mrb_value*, mrb_value);
+/**
+ * Create a symbol
+ *
+ * # Ruby style:
+ * :pizza # => :pizza
+ *
+ * // C style:
+ * mrb_sym m_sym = mrb_intern_cstr(mrb, "pizza"); // => :pizza
+ * @param [mrb_state*] mrb_state* The current mruby state.
+ * @param [const char*] const char* The name of the method.
+ * @return [mrb_sym] mrb_sym A symbol.
+ */
+MRB_API mrb_sym mrb_intern_cstr(mrb_state*,const char*);
+MRB_API mrb_sym mrb_intern(mrb_state*,const char*,size_t);
+MRB_API mrb_sym mrb_intern_static(mrb_state*,const char*,size_t);
#define mrb_intern_lit(mrb, lit) mrb_intern_static(mrb, lit, mrb_strlen_lit(lit))
-mrb_sym mrb_intern_str(mrb_state*,mrb_value);
-mrb_value mrb_check_intern_cstr(mrb_state*,const char*);
-mrb_value mrb_check_intern(mrb_state*,const char*,size_t);
-mrb_value mrb_check_intern_str(mrb_state*,mrb_value);
-const char *mrb_sym2name(mrb_state*,mrb_sym);
-const char *mrb_sym2name_len(mrb_state*,mrb_sym,mrb_int*);
-mrb_value mrb_sym2str(mrb_state*,mrb_sym);
-
-void *mrb_malloc(mrb_state*, size_t); /* raise RuntimeError if no mem */
-void *mrb_calloc(mrb_state*, size_t, size_t); /* ditto */
-void *mrb_realloc(mrb_state*, void*, size_t); /* ditto */
-void *mrb_realloc_simple(mrb_state*, void*, size_t); /* return NULL if no memory available */
-void *mrb_malloc_simple(mrb_state*, size_t); /* return NULL if no memory available */
-struct RBasic *mrb_obj_alloc(mrb_state*, enum mrb_vtype, struct RClass*);
-void mrb_free(mrb_state*, void*);
-
-mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len);
-mrb_value mrb_str_new_cstr(mrb_state*, const char*);
-mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len);
+MRB_API mrb_sym mrb_intern_str(mrb_state*,mrb_value);
+MRB_API mrb_value mrb_check_intern_cstr(mrb_state*,const char*);
+MRB_API mrb_value mrb_check_intern(mrb_state*,const char*,size_t);
+MRB_API mrb_value mrb_check_intern_str(mrb_state*,mrb_value);
+MRB_API const char *mrb_sym2name(mrb_state*,mrb_sym);
+MRB_API const char *mrb_sym2name_len(mrb_state*,mrb_sym,mrb_int*);
+MRB_API mrb_value mrb_sym2str(mrb_state*,mrb_sym);
+
+MRB_API void *mrb_malloc(mrb_state*, size_t); /* raise RuntimeError if no mem */
+MRB_API void *mrb_calloc(mrb_state*, size_t, size_t); /* ditto */
+MRB_API void *mrb_realloc(mrb_state*, void*, size_t); /* ditto */
+MRB_API void *mrb_realloc_simple(mrb_state*, void*, size_t); /* return NULL if no memory available */
+MRB_API void *mrb_malloc_simple(mrb_state*, size_t); /* return NULL if no memory available */
+MRB_API struct RBasic *mrb_obj_alloc(mrb_state*, enum mrb_vtype, struct RClass*);
+MRB_API void mrb_free(mrb_state*, void*);
+
+MRB_API mrb_value mrb_str_new(mrb_state *mrb, const char *p, size_t len);
+
+/**
+ * Turns a C string into a Ruby string value.
+ */
+MRB_API mrb_value mrb_str_new_cstr(mrb_state*, const char*);
+MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len);
#define mrb_str_new_lit(mrb, lit) mrb_str_new_static(mrb, (lit), mrb_strlen_lit(lit))
-mrb_state* mrb_open(void);
-mrb_state* mrb_open_allocf(mrb_allocf, void *ud);
-void mrb_close(mrb_state*);
-
-mrb_value mrb_top_self(mrb_state *);
-mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value);
-mrb_value mrb_toplevel_run(mrb_state*, struct RProc*);
-mrb_value mrb_context_run(mrb_state*, struct RProc*, mrb_value, unsigned int);
-
-void mrb_p(mrb_state*, mrb_value);
-mrb_int mrb_obj_id(mrb_value obj);
-mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name);
-
-mrb_bool mrb_obj_eq(mrb_state*, mrb_value, mrb_value);
-mrb_bool mrb_obj_equal(mrb_state*, mrb_value, mrb_value);
-mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
-mrb_value mrb_Integer(mrb_state *mrb, mrb_value val);
-mrb_value mrb_Float(mrb_state *mrb, mrb_value val);
-mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj);
-mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
-
-void mrb_garbage_collect(mrb_state*);
-void mrb_full_gc(mrb_state*);
-void mrb_incremental_gc(mrb_state *);
-int mrb_gc_arena_save(mrb_state*);
-void mrb_gc_arena_restore(mrb_state*,int);
-void mrb_gc_mark(mrb_state*,struct RBasic*);
+#ifdef _WIN32
+char* mrb_utf8_from_locale(const char *p, size_t len);
+char* mrb_locale_from_utf8(const char *p, size_t len);
+#define mrb_locale_free(p) free(p)
+#define mrb_utf8_free(p) free(p)
+#else
+#define mrb_utf8_from_locale(p, l) (p)
+#define mrb_locale_from_utf8(p, l) (p)
+#define mrb_locale_free(p)
+#define mrb_utf8_free(p)
+#endif
+
+/**
+ * Creates new mrb_state.
+ *
+ * @return
+ * Pointer to the newly created mrb_state.
+ */
+MRB_API mrb_state* mrb_open(void);
+
+/**
+ * Create new mrb_state with custom allocators.
+ *
+ * @param f
+ * Reference to the allocation function.
+ * @param ud
+ * User data will be passed to custom allocator f.
+ * If user data isn't required just pass NULL.
+ * @return
+ * Pointer to the newly created mrb_state.
+ */
+MRB_API mrb_state* mrb_open_allocf(mrb_allocf f, void *ud);
+
+/**
+ * Create new mrb_state with just the MRuby core
+ *
+ * @param f
+ * Reference to the allocation function.
+ * Use mrb_default_allocf for the default
+ * @param ud
+ * User data will be passed to custom allocator f.
+ * If user data isn't required just pass NULL.
+ * @return
+ * Pointer to the newly created mrb_state.
+ */
+MRB_API mrb_state* mrb_open_core(mrb_allocf f, void *ud);
+
+/**
+ * Closes and frees a mrb_state.
+ *
+ * @param mrb
+ * Pointer to the mrb_state to be closed.
+ */
+MRB_API void mrb_close(mrb_state *mrb);
+
+/**
+ * The default allocation function.
+ *
+ * @see mrb_allocf
+ */
+MRB_API void* mrb_default_allocf(mrb_state*, void*, size_t, void*);
+
+MRB_API mrb_value mrb_top_self(mrb_state *);
+MRB_API mrb_value mrb_run(mrb_state*, struct RProc*, mrb_value);
+MRB_API mrb_value mrb_toplevel_run(mrb_state*, struct RProc*);
+MRB_API mrb_value mrb_context_run(mrb_state*, struct RProc*, mrb_value, unsigned int);
+
+MRB_API void mrb_p(mrb_state*, mrb_value);
+MRB_API mrb_int mrb_obj_id(mrb_value obj);
+MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name);
+
+MRB_API mrb_bool mrb_obj_eq(mrb_state*, mrb_value, mrb_value);
+MRB_API mrb_bool mrb_obj_equal(mrb_state*, mrb_value, mrb_value);
+MRB_API mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
+MRB_API mrb_value mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base);
+MRB_API mrb_value mrb_Integer(mrb_state *mrb, mrb_value val);
+MRB_API mrb_value mrb_Float(mrb_state *mrb, mrb_value val);
+MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj);
+MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
+
+MRB_API void mrb_garbage_collect(mrb_state*);
+MRB_API void mrb_full_gc(mrb_state*);
+MRB_API void mrb_incremental_gc(mrb_state *);
+MRB_API int mrb_gc_arena_save(mrb_state*);
+MRB_API void mrb_gc_arena_restore(mrb_state*,int);
+MRB_API void mrb_gc_mark(mrb_state*,struct RBasic*);
#define mrb_gc_mark_value(mrb,val) do {\
- if (MRB_TT_HAS_BASIC_P(mrb_type(val))) mrb_gc_mark((mrb), mrb_basic_ptr(val)); \
+ if (!mrb_immediate_p(val)) mrb_gc_mark((mrb), mrb_basic_ptr(val)); \
} while (0)
-void mrb_field_write_barrier(mrb_state *, struct RBasic*, struct RBasic*);
+MRB_API void mrb_field_write_barrier(mrb_state *, struct RBasic*, struct RBasic*);
#define mrb_field_write_barrier_value(mrb, obj, val) do{\
- if (MRB_TT_HAS_BASIC_P(mrb_type(val))) mrb_field_write_barrier((mrb), (obj), mrb_basic_ptr(val)); \
+ if (!mrb_immediate_p(val)) mrb_field_write_barrier((mrb), (obj), mrb_basic_ptr(val)); \
} while (0)
-void mrb_write_barrier(mrb_state *, struct RBasic*);
-
-mrb_value mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method);
-mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj);
-const char * mrb_obj_classname(mrb_state *mrb, mrb_value obj);
-struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj);
-mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c);
-mrb_value mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method);
-mrb_bool mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c);
-mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value self);
-mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self);
-
-/* need to include <ctype.h> to use these macros */
+MRB_API void mrb_write_barrier(mrb_state *, struct RBasic*);
+
+MRB_API mrb_value mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method);
+MRB_API mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj);
+MRB_API const char * mrb_obj_classname(mrb_state *mrb, mrb_value obj);
+MRB_API struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj);
+MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c);
+MRB_API mrb_value mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method);
+MRB_API mrb_bool mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c);
+MRB_API mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value self);
+MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self);
+
#ifndef ISPRINT
-#define ISASCII(c) (!(((int)(unsigned char)(c)) & ~0x7f))
-#define ISPRINT(c) (ISASCII(c) && isprint((int)(unsigned char)(c)))
-#define ISSPACE(c) (ISASCII(c) && isspace((int)(unsigned char)(c)))
-#define ISUPPER(c) (ISASCII(c) && isupper((int)(unsigned char)(c)))
-#define ISLOWER(c) (ISASCII(c) && islower((int)(unsigned char)(c)))
-#define ISALNUM(c) (ISASCII(c) && isalnum((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 ISXDIGIT(c) (ISASCII(c) && isxdigit((int)(unsigned char)(c)))
-#define TOUPPER(c) (ISASCII(c) ? toupper((int)(unsigned char)(c)) : (c))
-#define TOLOWER(c) (ISASCII(c) ? tolower((int)(unsigned char)(c)) : (c))
+#define ISASCII(c) ((unsigned)(c) <= 0x7f)
+#define ISPRINT(c) (((unsigned)(c) - 0x20) < 0x5f)
+#define ISSPACE(c) ((c) == ' ' || (unsigned)(c) - '\t' < 5)
+#define ISUPPER(c) (((unsigned)(c) - 'A') < 26)
+#define ISLOWER(c) (((unsigned)(c) - 'a') < 26)
+#define ISALPHA(c) ((((unsigned)(c) | 0x20) - 'a') < 26)
+#define ISDIGIT(c) (((unsigned)(c) - '0') < 10)
+#define ISXDIGIT(c) (ISDIGIT(c) || ((unsigned)(c) | 0x20) - 'a' < 6)
+#define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c))
+#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#define ISCNTRL(c) ((unsigned)(c) < 0x20 || (c) == 0x7f)
+#define TOUPPER(c) (ISLOWER(c) ? ((c) & 0x5f) : (c))
+#define TOLOWER(c) (ISUPPER(c) ? ((c) | 0x20) : (c))
#endif
-mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, long len);
-mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc);
+MRB_API mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len);
+MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc);
-mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg);
-mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...);
-mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...);
-void mrb_warn(mrb_state *mrb, const char *fmt, ...);
-mrb_noreturn void mrb_bug(mrb_state *mrb, const char *fmt, ...);
-void mrb_print_backtrace(mrb_state *mrb);
-void mrb_print_error(mrb_state *mrb);
+MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg);
+MRB_API mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...);
+MRB_API mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...);
+MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...);
+MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *fmt, ...);
+MRB_API void mrb_print_backtrace(mrb_state *mrb);
+MRB_API void mrb_print_error(mrb_state *mrb);
/* macros to get typical exception objects
note:
@@ -382,20 +1002,27 @@ void mrb_print_error(mrb_state *mrb);
#define E_SYNTAX_ERROR (mrb_class_get(mrb, "SyntaxError"))
#define E_LOCALJUMP_ERROR (mrb_class_get(mrb, "LocalJumpError"))
#define E_REGEXP_ERROR (mrb_class_get(mrb, "RegexpError"))
+#define E_SYSSTACK_ERROR (mrb_class_get(mrb, "SystemStackError"))
#define E_NOTIMP_ERROR (mrb_class_get(mrb, "NotImplementedError"))
#define E_FLOATDOMAIN_ERROR (mrb_class_get(mrb, "FloatDomainError"))
#define E_KEY_ERROR (mrb_class_get(mrb, "KeyError"))
-mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg);
-mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv);
-mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c);
+MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg);
+MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv);
+MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c);
+
+/* mrb_gc_protect() leaves the object in the arena */
+MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj);
+/* mrb_gc_register() keeps the object from GC. */
+MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj);
+/* mrb_gc_unregister() removes the object from GC root. */
+MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj);
-void mrb_gc_protect(mrb_state *mrb, mrb_value obj);
-mrb_value mrb_to_int(mrb_state *mrb, mrb_value val);
+MRB_API mrb_value mrb_to_int(mrb_state *mrb, mrb_value val);
#define mrb_int(mrb, val) mrb_fixnum(mrb_to_int(mrb, val))
-void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t);
+MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t);
typedef enum call_type {
CALL_PUBLIC,
@@ -404,29 +1031,50 @@ typedef enum call_type {
CALL_TYPE_MAX
} call_type;
-void mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2);
-const char *mrb_class_name(mrb_state *mrb, struct RClass* klass);
-void mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val);
+MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2);
+MRB_API const char *mrb_class_name(mrb_state *mrb, struct RClass* klass);
+MRB_API void mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val);
+
+MRB_API mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id);
+
+MRB_API mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid);
+MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c);
+
-mrb_value mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id);
+/*
+ * Resume a Fiber
+ *
+ * @mrbgem mruby-fiber
+ */
+MRB_API mrb_value mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int argc, const mrb_value *argv);
-mrb_bool mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid);
-mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c);
+/*
+ * Yield a Fiber
+ *
+ * @mrbgem mruby-fiber
+ */
+MRB_API mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int argc, const mrb_value *argv);
-/* fiber functions (you need to link mruby-fiber mrbgem to use) */
-mrb_value mrb_fiber_yield(mrb_state *mrb, mrb_int argc, const mrb_value *argv);
+/*
+ * FiberError reference
+ *
+ * @mrbgem mruby-fiber
+ */
#define E_FIBER_ERROR (mrb_class_get(mrb, "FiberError"))
/* memory pool implementation */
typedef struct mrb_pool mrb_pool;
-struct mrb_pool* mrb_pool_open(mrb_state*);
-void mrb_pool_close(struct mrb_pool*);
-void* mrb_pool_alloc(struct mrb_pool*, size_t);
-void* mrb_pool_realloc(struct mrb_pool*, void*, size_t oldlen, size_t newlen);
-mrb_bool mrb_pool_can_realloc(struct mrb_pool*, void*, size_t);
-void* mrb_alloca(mrb_state *mrb, size_t);
+MRB_API struct mrb_pool* mrb_pool_open(mrb_state*);
+MRB_API void mrb_pool_close(struct mrb_pool*);
+MRB_API void* mrb_pool_alloc(struct mrb_pool*, size_t);
+MRB_API void* mrb_pool_realloc(struct mrb_pool*, void*, size_t oldlen, size_t newlen);
+MRB_API mrb_bool mrb_pool_can_realloc(struct mrb_pool*, void*, size_t);
+MRB_API void* mrb_alloca(mrb_state *mrb, size_t);
-void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func);
+MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func);
+
+MRB_API void mrb_show_version(mrb_state *mrb);
+MRB_API void mrb_show_copyright(mrb_state *mrb);
#ifdef MRB_DEBUG
#include <assert.h>
@@ -443,10 +1091,8 @@ void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func);
#define mrb_static_assert(exp, str) mrb_assert(exp)
#endif
-mrb_value mrb_format(mrb_state *mrb, const char *format, ...);
+MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...);
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_H */
diff --git a/include/mruby/array.h b/include/mruby/array.h
index 5fbf71174..bd78db066 100644
--- a/include/mruby/array.h
+++ b/include/mruby/array.h
@@ -7,9 +7,13 @@
#ifndef MRUBY_ARRAY_H
#define MRUBY_ARRAY_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+
+/*
+ * Array class
+ */
+MRB_BEGIN_DECL
+
typedef struct mrb_shared_array {
int refcnt;
@@ -32,28 +36,95 @@ struct RArray {
#define RARRAY(v) ((struct RArray*)(mrb_ptr(v)))
#define RARRAY_LEN(a) (RARRAY(a)->len)
-#define RARRAY_PTR(a) (RARRAY(a)->ptr)
+#define RARRAY_PTR(a) ((const mrb_value*)RARRAY(a)->ptr)
#define MRB_ARY_SHARED 256
+#define ARY_SHARED_P(a) ((a)->flags & MRB_ARY_SHARED)
+#define ARY_SET_SHARED_FLAG(a) ((a)->flags |= MRB_ARY_SHARED)
+#define ARY_UNSET_SHARED_FLAG(a) ((a)->flags &= ~MRB_ARY_SHARED)
-void mrb_ary_modify(mrb_state*, struct RArray*);
void mrb_ary_decref(mrb_state*, mrb_shared_array*);
-mrb_value mrb_ary_new_capa(mrb_state*, mrb_int);
-mrb_value mrb_ary_new(mrb_state *mrb);
-mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals);
-mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr);
-void mrb_ary_concat(mrb_state*, mrb_value, mrb_value);
-mrb_value mrb_ary_splat(mrb_state*, mrb_value);
-void mrb_ary_push(mrb_state*, mrb_value, mrb_value);
-mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary);
-mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n);
-void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val);
-void mrb_ary_replace(mrb_state *mrb, mrb_value a, mrb_value b);
-mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self);
-mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item);
-mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset);
-mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self);
-mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self);
-mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep);
+MRB_API void mrb_ary_modify(mrb_state*, struct RArray*);
+MRB_API mrb_value mrb_ary_new_capa(mrb_state*, mrb_int);
+
+/*
+ * Initializes a new array.
+ *
+ * Equivalent to:
+ *
+ * Array.new
+ *
+ * @param mrb The mruby state reference.
+ * @return The initialized array
+ */
+MRB_API mrb_value mrb_ary_new(mrb_state *mrb);
+
+MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals);
+MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr);
+MRB_API void mrb_ary_concat(mrb_state*, mrb_value, mrb_value);
+MRB_API mrb_value mrb_ary_splat(mrb_state*, mrb_value);
+
+/*
+ * Pushes value into array.
+ *
+ * Equivalent to:
+ *
+ * ary << value
+ *
+ * @param mrb The mruby state reference.
+ * @param ary The array in which the value will be pushed
+ * @param value The value to be pushed into array
+ */
+MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value array, mrb_value value);
+
+/*
+ * Pops the last element from the array.
+ *
+ * Equivalent to:
+ *
+ * ary.pop
+ *
+ * @param mrb The mruby state reference.
+ * @param ary The array from which the value will be poped.
+ * @return The poped value.
+ */
+MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary);
+
+/*
+ * Returns a reference to an element of the array on the given index.
+ *
+ * Equivalent to:
+ *
+ * ary[n]
+ *
+ * @param mrb The mruby state reference.
+ * @param ary The target array.
+ * @param n The array index being referenced
+ * @return The referenced value.
+ */
+MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n);
+
+/*
+ * Sets a value on an array at the given index
+ *
+ * Equivalent to:
+ *
+ * ary[n] = val
+ *
+ * @param mrb The mruby state reference.
+ * @param ary The target array.
+ * @param n The array index being referenced.
+ * @param val The value being setted.
+ */
+MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val);
+
+MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value a, mrb_value b);
+MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self);
+MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item);
+MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset);
+MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self);
+MRB_API mrb_value mrb_ary_clear(mrb_state *mrb, mrb_value self);
+MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep);
+MRB_API mrb_value mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int len);
static inline mrb_int
mrb_ary_len(mrb_state *mrb, mrb_value ary)
@@ -63,8 +134,6 @@ mrb_ary_len(mrb_state *mrb, mrb_value ary)
return RARRAY_LEN(ary);
}
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_ARRAY_H */
diff --git a/include/mruby/boxing_nan.h b/include/mruby/boxing_nan.h
new file mode 100644
index 000000000..b71c4b746
--- /dev/null
+++ b/include/mruby/boxing_nan.h
@@ -0,0 +1,93 @@
+/*
+** mruby/boxing_nan.h - nan boxing mrb_value definition
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_BOXING_NAN_H
+#define MRUBY_BOXING_NAN_H
+
+#ifdef MRB_USE_FLOAT
+# error ---->> MRB_NAN_BOXING and MRB_USE_FLOAT conflict <<----
+#endif
+
+#ifdef MRB_INT64
+# error ---->> MRB_NAN_BOXING and MRB_INT64 conflict <<----
+#endif
+
+#define MRB_FIXNUM_SHIFT 0
+#define MRB_TT_HAS_BASIC MRB_TT_OBJECT
+
+#ifdef MRB_ENDIAN_BIG
+#define MRB_ENDIAN_LOHI(a,b) a b
+#else
+#define MRB_ENDIAN_LOHI(a,b) b a
+#endif
+
+/* value representation by nan-boxing:
+ * float : FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF
+ * object: 111111111111TTTT TTPPPPPPPPPPPPPP PPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPP
+ * int : 1111111111110001 0000000000000000 IIIIIIIIIIIIIIII IIIIIIIIIIIIIIII
+ * sym : 1111111111110001 0100000000000000 SSSSSSSSSSSSSSSS SSSSSSSSSSSSSSSS
+ * In order to get enough bit size to save TT, all pointers are shifted 2 bits
+ * in the right direction. Also, TTTTTT is the mrb_vtype + 1;
+ */
+typedef struct mrb_value {
+ union {
+ mrb_float f;
+ union {
+ void *p;
+ struct {
+ MRB_ENDIAN_LOHI(
+ uint32_t ttt;
+ ,union {
+ mrb_int i;
+ mrb_sym sym;
+ };
+ )
+ };
+ } value;
+ };
+} mrb_value;
+
+#define mrb_float_pool(mrb,f) mrb_float_value(mrb,f)
+
+#define mrb_tt(o) ((enum mrb_vtype)(((o).value.ttt & 0xfc000)>>14)-1)
+#define mrb_type(o) ((uint32_t)0xfff00000 < (o).value.ttt ? mrb_tt(o) : MRB_TT_FLOAT)
+#define mrb_ptr(o) ((void*)((((uintptr_t)0x3fffffffffff)&((uintptr_t)((o).value.p)))<<2))
+#define mrb_float(o) (o).f
+#define mrb_cptr(o) mrb_ptr(o)
+#define mrb_fixnum(o) (o).value.i
+#define mrb_symbol(o) (o).value.sym
+
+#define BOXNAN_SET_VALUE(o, tt, attr, v) do {\
+ switch (tt) {\
+ case MRB_TT_FALSE:\
+ case MRB_TT_TRUE:\
+ case MRB_TT_UNDEF:\
+ case MRB_TT_FIXNUM:\
+ case MRB_TT_SYMBOL: (o).attr = (v); break;\
+ default: (o).value.i = 0; (o).value.p = (void*)((uintptr_t)(o).value.p | (((uintptr_t)(v))>>2)); break;\
+ }\
+ (o).value.ttt = (0xfff00000|(((tt)+1)<<14));\
+} while (0)
+
+#define SET_FLOAT_VALUE(mrb,r,v) do { \
+ if (v != v) { \
+ (r).value.ttt = 0x7ff80000; \
+ (r).value.i = 0; \
+ } else { \
+ (r).f = v; \
+ }} while(0)
+
+#define SET_NIL_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_FALSE, value.i, 0)
+#define SET_FALSE_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_FALSE, value.i, 1)
+#define SET_TRUE_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_TRUE, value.i, 1)
+#define SET_BOOL_VALUE(r,b) BOXNAN_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1)
+#define SET_INT_VALUE(r,n) BOXNAN_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n))
+#define SET_SYM_VALUE(r,v) BOXNAN_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v))
+#define SET_OBJ_VALUE(r,v) BOXNAN_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v))
+#define SET_CPTR_VALUE(mrb,r,v) BOXNAN_SET_VALUE(r, MRB_TT_CPTR, value.p, v)
+#define SET_UNDEF_VALUE(r) BOXNAN_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0)
+
+#endif /* MRUBY_BOXING_NAN_H */
diff --git a/include/mruby/boxing_no.h b/include/mruby/boxing_no.h
new file mode 100644
index 000000000..7ee44a934
--- /dev/null
+++ b/include/mruby/boxing_no.h
@@ -0,0 +1,48 @@
+/*
+** mruby/boxing_no.h - unboxed mrb_value definition
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_BOXING_NO_H
+#define MRUBY_BOXING_NO_H
+
+#define MRB_FIXNUM_SHIFT 0
+#define MRB_TT_HAS_BASIC MRB_TT_OBJECT
+
+typedef struct mrb_value {
+ union {
+ mrb_float f;
+ void *p;
+ mrb_int i;
+ mrb_sym sym;
+ } value;
+ enum mrb_vtype tt;
+} mrb_value;
+
+#define mrb_float_pool(mrb,f) mrb_float_value(mrb,f)
+
+#define mrb_ptr(o) (o).value.p
+#define mrb_cptr(o) mrb_ptr(o)
+#define mrb_float(o) (o).value.f
+#define mrb_fixnum(o) (o).value.i
+#define mrb_symbol(o) (o).value.sym
+#define mrb_type(o) (o).tt
+
+#define BOXNIX_SET_VALUE(o, ttt, attr, v) do {\
+ (o).tt = ttt;\
+ (o).attr = v;\
+} while (0)
+
+#define SET_NIL_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_FALSE, value.i, 0)
+#define SET_FALSE_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_FALSE, value.i, 1)
+#define SET_TRUE_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_TRUE, value.i, 1)
+#define SET_BOOL_VALUE(r,b) BOXNIX_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1)
+#define SET_INT_VALUE(r,n) BOXNIX_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n))
+#define SET_FLOAT_VALUE(mrb,r,v) BOXNIX_SET_VALUE(r, MRB_TT_FLOAT, value.f, (v))
+#define SET_SYM_VALUE(r,v) BOXNIX_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v))
+#define SET_OBJ_VALUE(r,v) BOXNIX_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v))
+#define SET_CPTR_VALUE(mrb,r,v) BOXNIX_SET_VALUE(r, MRB_TT_CPTR, value.p, v)
+#define SET_UNDEF_VALUE(r) BOXNIX_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0)
+
+#endif /* MRUBY_BOXING_NO_H */
diff --git a/include/mruby/boxing_word.h b/include/mruby/boxing_word.h
new file mode 100644
index 000000000..8754087a3
--- /dev/null
+++ b/include/mruby/boxing_word.h
@@ -0,0 +1,116 @@
+/*
+** mruby/boxing_word.h - word boxing mrb_value definition
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_BOXING_WORD_H
+#define MRUBY_BOXING_WORD_H
+
+#if defined(MRB_INT16)
+# error MRB_INT16 is too small for MRB_WORD_BOXING.
+#endif
+
+struct RFloat {
+ MRB_OBJECT_HEADER;
+ mrb_float f;
+};
+
+struct RCptr {
+ MRB_OBJECT_HEADER;
+ void *p;
+};
+
+#define MRB_FIXNUM_SHIFT 1
+#define MRB_TT_HAS_BASIC MRB_TT_FLOAT
+
+enum mrb_special_consts {
+ MRB_Qnil = 0,
+ MRB_Qfalse = 2,
+ MRB_Qtrue = 4,
+ MRB_Qundef = 6,
+};
+
+#define MRB_FIXNUM_FLAG 0x01
+#define MRB_SYMBOL_FLAG 0x0e
+#define MRB_SPECIAL_SHIFT 8
+
+typedef union mrb_value {
+ union {
+ void *p;
+ struct {
+ unsigned int i_flag : MRB_FIXNUM_SHIFT;
+ mrb_int i : (MRB_INT_BIT - MRB_FIXNUM_SHIFT);
+ };
+ struct {
+ unsigned int sym_flag : MRB_SPECIAL_SHIFT;
+ mrb_sym sym : (sizeof(mrb_sym) * CHAR_BIT);
+ };
+ struct RBasic *bp;
+ struct RFloat *fp;
+ struct RCptr *vp;
+ } value;
+ unsigned long w;
+} mrb_value;
+
+MRB_API mrb_value mrb_word_boxing_cptr_value(struct mrb_state*, void*);
+MRB_API mrb_value mrb_word_boxing_float_value(struct mrb_state*, mrb_float);
+MRB_API mrb_value mrb_word_boxing_float_pool(struct mrb_state*, mrb_float);
+
+#define mrb_float_pool(mrb,f) mrb_word_boxing_float_pool(mrb,f)
+
+#define mrb_ptr(o) (o).value.p
+#define mrb_cptr(o) (o).value.vp->p
+#define mrb_float(o) (o).value.fp->f
+#define mrb_fixnum(o) (o).value.i
+#define mrb_symbol(o) (o).value.sym
+
+static inline enum mrb_vtype
+mrb_type(mrb_value o)
+{
+ switch (o.w) {
+ case MRB_Qfalse:
+ case MRB_Qnil:
+ return MRB_TT_FALSE;
+ case MRB_Qtrue:
+ return MRB_TT_TRUE;
+ case MRB_Qundef:
+ return MRB_TT_UNDEF;
+ }
+ if (o.value.i_flag == MRB_FIXNUM_FLAG) {
+ return MRB_TT_FIXNUM;
+ }
+ if (o.value.sym_flag == MRB_SYMBOL_FLAG) {
+ return MRB_TT_SYMBOL;
+ }
+ return o.value.bp->tt;
+}
+
+#define mrb_bool(o) ((o).w != MRB_Qnil && (o).w != MRB_Qfalse)
+#define mrb_fixnum_p(o) ((o).value.i_flag == MRB_FIXNUM_FLAG)
+#define mrb_undef_p(o) ((o).w == MRB_Qundef)
+#define mrb_nil_p(o) ((o).w == MRB_Qnil)
+
+#define BOXWORD_SET_VALUE(o, ttt, attr, v) do {\
+ switch (ttt) {\
+ case MRB_TT_FALSE: (o).w = (v) ? MRB_Qfalse : MRB_Qnil; break;\
+ case MRB_TT_TRUE: (o).w = MRB_Qtrue; break;\
+ case MRB_TT_UNDEF: (o).w = MRB_Qundef; break;\
+ case MRB_TT_FIXNUM: (o).value.i_flag = MRB_FIXNUM_FLAG; (o).attr = (v); break;\
+ case MRB_TT_SYMBOL: (o).value.sym_flag = MRB_SYMBOL_FLAG; (o).attr = (v); break;\
+ default: (o).w = 0; (o).attr = (v); if ((o).value.bp) (o).value.bp->tt = ttt; break;\
+ }\
+} while (0)
+
+#define SET_FLOAT_VALUE(mrb,r,v) r = mrb_word_boxing_float_value(mrb, v)
+#define SET_CPTR_VALUE(mrb,r,v) r = mrb_word_boxing_cptr_value(mrb, v)
+#define SET_NIL_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_FALSE, value.i, 0)
+#define SET_FALSE_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_FALSE, value.i, 1)
+#define SET_TRUE_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_TRUE, value.i, 1)
+#define SET_BOOL_VALUE(r,b) BOXWORD_SET_VALUE(r, b ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1)
+#define SET_INT_VALUE(r,n) BOXWORD_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n))
+#define SET_SYM_VALUE(r,v) BOXWORD_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v))
+#define SET_OBJ_VALUE(r,v) BOXWORD_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v))
+#define SET_UNDEF_VALUE(r) BOXWORD_SET_VALUE(r, MRB_TT_UNDEF, value.i, 0)
+
+#endif /* MRUBY_BOXING_WORD_H */
diff --git a/include/mruby/class.h b/include/mruby/class.h
index 3c4915dc4..246e82e59 100644
--- a/include/mruby/class.h
+++ b/include/mruby/class.h
@@ -7,9 +7,12 @@
#ifndef MRUBY_CLASS_H
#define MRUBY_CLASS_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+
+/**
+ * Class class
+ */
+MRB_BEGIN_DECL
struct RClass {
MRB_OBJECT_HEADER;
@@ -28,7 +31,7 @@ mrb_class(mrb_state *mrb, mrb_value v)
{
switch (mrb_type(v)) {
case MRB_TT_FALSE:
- if (v.value.i)
+ if (mrb_fixnum(v))
return mrb->false_class;
return mrb->nil_class;
case MRB_TT_TRUE:
@@ -48,30 +51,39 @@ mrb_class(mrb_state *mrb, mrb_value v)
}
}
-#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~0xff) | (char)tt)
-#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & 0xff)
+// TODO: figure out where to put user flags
+#define MRB_FLAG_IS_PREPENDED (1 << 19)
+#define MRB_FLAG_IS_ORIGIN (1 << 20)
+#define MRB_CLASS_ORIGIN(c) do {\
+ if (c->flags & MRB_FLAG_IS_PREPENDED) {\
+ c = c->super;\
+ while (!(c->flags & MRB_FLAG_IS_ORIGIN)) {\
+ c = c->super;\
+ }\
+ }\
+} while (0)
+#define MRB_INSTANCE_TT_MASK (0xFF)
+#define MRB_SET_INSTANCE_TT(c, tt) c->flags = ((c->flags & ~MRB_INSTANCE_TT_MASK) | (char)tt)
+#define MRB_INSTANCE_TT(c) (enum mrb_vtype)(c->flags & MRB_INSTANCE_TT_MASK)
-struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*);
-struct RClass* mrb_define_module_id(mrb_state*, mrb_sym);
-struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym);
-struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym);
-void mrb_define_method_vm(mrb_state*, struct RClass*, mrb_sym, mrb_value);
-void mrb_define_method_raw(mrb_state*, struct RClass*, mrb_sym, struct RProc *);
-void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec);
-void mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b);
+MRB_API struct RClass* mrb_define_class_id(mrb_state*, mrb_sym, struct RClass*);
+MRB_API struct RClass* mrb_define_module_id(mrb_state*, mrb_sym);
+MRB_API struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym);
+MRB_API struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym);
+MRB_API void mrb_define_method_raw(mrb_state*, struct RClass*, mrb_sym, struct RProc *);
+MRB_API void mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec);
+MRB_API void mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b);
-struct RClass *mrb_class_outer_module(mrb_state*, struct RClass *);
-struct RProc *mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym);
-struct RProc *mrb_method_search(mrb_state*, struct RClass*, mrb_sym);
+MRB_API struct RClass *mrb_class_outer_module(mrb_state*, struct RClass *);
+MRB_API struct RProc *mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym);
+MRB_API struct RProc *mrb_method_search(mrb_state*, struct RClass*, mrb_sym);
-struct RClass* mrb_class_real(struct RClass* cl);
+MRB_API struct RClass* mrb_class_real(struct RClass* cl);
void mrb_gc_mark_mt(mrb_state*, struct RClass*);
size_t mrb_gc_mark_mt_size(mrb_state*, struct RClass*);
void mrb_gc_free_mt(mrb_state*, struct RClass*);
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_CLASS_H */
diff --git a/include/mruby/common.h b/include/mruby/common.h
new file mode 100644
index 000000000..3745c58ea
--- /dev/null
+++ b/include/mruby/common.h
@@ -0,0 +1,67 @@
+/*
+**"common.h - mruby common platform definition"
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_COMMON_H
+#define MRUBY_COMMON_H
+
+
+#ifdef __cplusplus
+# define MRB_BEGIN_DECL extern "C" {
+# define MRB_END_DECL }
+#else
+/** Start declarations in C mode */
+# define MRB_BEGIN_DECL
+/** End declarations in C mode */
+# define MRB_END_DECL
+#endif
+
+/**
+ * Shared compiler macros
+ */
+MRB_BEGIN_DECL
+
+/** Declare a function that never returns. */
+#if __STDC_VERSION__ >= 201112L
+# define mrb_noreturn _Noreturn
+#elif defined __GNUC__ && !defined __STRICT_ANSI__
+# define mrb_noreturn __attribute__((noreturn))
+#elif defined _MSC_VER
+# define mrb_noreturn __declspec(noreturn)
+#else
+# define mrb_noreturn
+#endif
+
+/** Mark a function as deprecated. */
+#if defined __GNUC__ && !defined __STRICT_ANSI__
+# define mrb_deprecated __attribute__((deprecated))
+#elif defined _MSC_VER
+# define mrb_deprecated __declspec(deprecated)
+#else
+# define mrb_deprecated
+#endif
+
+/** Declare a function as always inlined. */
+#if defined(_MSC_VER)
+# define MRB_INLINE static __inline
+#else
+# define MRB_INLINE static inline
+#endif
+
+
+/** Declare a public MRuby API function. */
+#if defined(MRB_BUILD_AS_DLL)
+#if defined(MRB_CORE) || defined(MRB_LIB)
+# define MRB_API __declspec(dllexport)
+#else
+# define MRB_API __declspec(dllimport)
+#endif
+#else
+# define MRB_API extern
+#endif
+
+MRB_END_DECL
+
+#endif /* MRUBY_COMMON_H */
diff --git a/include/mruby/compile.h b/include/mruby/compile.h
index 7f896e1fd..8b813b1c1 100644
--- a/include/mruby/compile.h
+++ b/include/mruby/compile.h
@@ -7,11 +7,14 @@
#ifndef MRUBY_COMPILE_H
#define MRUBY_COMPILE_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+
+/**
+ * MRuby Compiler
+ */
+MRB_BEGIN_DECL
-#include "mruby.h"
+#include <mruby.h>
struct mrb_jmpbuf;
@@ -28,14 +31,16 @@ typedef struct mrbc_context {
mrb_bool capture_errors:1;
mrb_bool dump_result:1;
mrb_bool no_exec:1;
+ mrb_bool keep_lv:1;
+ mrb_bool no_optimize:1;
} mrbc_context;
-mrbc_context* mrbc_context_new(mrb_state *mrb);
-void mrbc_context_free(mrb_state *mrb, mrbc_context *cxt);
-const char *mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s);
-void mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*partial_hook)(struct mrb_parser_state*), void*data);
+MRB_API mrbc_context* mrbc_context_new(mrb_state *mrb);
+MRB_API void mrbc_context_free(mrb_state *mrb, mrbc_context *cxt);
+MRB_API const char *mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s);
+MRB_API void mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*partial_hook)(struct mrb_parser_state*), void*data);
-mrb_value mrb_toplevel_run_keep(mrb_state*, struct RProc*, unsigned int);
+MRB_API mrb_value mrb_toplevel_run_keep(mrb_state*, struct RProc*, unsigned int);
/* AST node structure */
typedef struct mrb_ast_node {
@@ -53,8 +58,8 @@ enum mrb_lex_state_enum {
EXPR_CMDARG, /* newline significant, +/- is an operator. */
EXPR_MID, /* newline significant, +/- is an operator. */
EXPR_FNAME, /* ignore newline, no reserved words. */
- EXPR_DOT, /* right after `.' or `::', no reserved words. */
- EXPR_CLASS, /* immediate after `class', no here document. */
+ EXPR_DOT, /* right after '.' or '::', no reserved words. */
+ EXPR_CLASS, /* immediate after 'class', no here document. */
EXPR_VALUE, /* alike EXPR_BEG but label is disallowed. */
EXPR_MAX_STATE
};
@@ -107,7 +112,7 @@ struct mrb_parser_state {
struct mrb_pool *pool;
mrb_ast_node *cells;
const char *s, *send;
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
FILE *f;
#endif
mrbc_context *cxt;
@@ -142,6 +147,7 @@ struct mrb_parser_state {
size_t nwarn;
mrb_ast_node *tree;
+ mrb_bool no_optimize:1;
mrb_bool capture_errors:1;
struct mrb_parser_message error_buffer[10];
struct mrb_parser_message warn_buffer[10];
@@ -153,35 +159,32 @@ struct mrb_parser_state {
struct mrb_jmpbuf* jmp;
};
-struct mrb_parser_state* mrb_parser_new(mrb_state*);
-void mrb_parser_free(struct mrb_parser_state*);
-void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*);
+MRB_API struct mrb_parser_state* mrb_parser_new(mrb_state*);
+MRB_API void mrb_parser_free(struct mrb_parser_state*);
+MRB_API void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*);
-void mrb_parser_set_filename(struct mrb_parser_state*, char const*);
-char const* mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx);
+MRB_API void mrb_parser_set_filename(struct mrb_parser_state*, char const*);
+MRB_API char const* mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx);
/* utility functions */
-#ifdef ENABLE_STDIO
-struct mrb_parser_state* mrb_parse_file(mrb_state*,FILE*,mrbc_context*);
+#ifndef MRB_DISABLE_STDIO
+MRB_API struct mrb_parser_state* mrb_parse_file(mrb_state*,FILE*,mrbc_context*);
#endif
-struct mrb_parser_state* mrb_parse_string(mrb_state*,const char*,mrbc_context*);
-struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,int,mrbc_context*);
-struct RProc* mrb_generate_code(mrb_state*, struct mrb_parser_state*);
+MRB_API struct mrb_parser_state* mrb_parse_string(mrb_state*,const char*,mrbc_context*);
+MRB_API struct mrb_parser_state* mrb_parse_nstring(mrb_state*,const char*,int,mrbc_context*);
+MRB_API struct RProc* mrb_generate_code(mrb_state*, struct mrb_parser_state*);
/* program load functions */
-#ifdef ENABLE_STDIO
-mrb_value mrb_load_file(mrb_state*,FILE*);
+#ifndef MRB_DISABLE_STDIO
+MRB_API mrb_value mrb_load_file(mrb_state*,FILE*);
+MRB_API mrb_value mrb_load_file_cxt(mrb_state*,FILE*, mrbc_context *cxt);
#endif
-mrb_value mrb_load_string(mrb_state *mrb, const char *s);
-mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, int len);
-#ifdef ENABLE_STDIO
-mrb_value mrb_load_file_cxt(mrb_state*,FILE*, mrbc_context *cxt);
-#endif
-mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *cxt);
-mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *cxt);
+MRB_API mrb_value mrb_load_string(mrb_state *mrb, const char *s);
+MRB_API mrb_value mrb_load_nstring(mrb_state *mrb, const char *s, int len);
+MRB_API mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *cxt);
+MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *cxt);
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+/** @} */
+MRB_END_DECL
#endif /* MRUBY_COMPILE_H */
diff --git a/include/mruby/data.h b/include/mruby/data.h
index 8b1b5edb7..590470528 100644
--- a/include/mruby/data.h
+++ b/include/mruby/data.h
@@ -7,12 +7,23 @@
#ifndef MRUBY_DATA_H
#define MRUBY_DATA_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+/**
+ * Custom C wrapped data.
+ *
+ * Defining Ruby wrappers around native objects.
+ */
+MRB_BEGIN_DECL
+
+/**
+ * Custom data type description.
+ */
typedef struct mrb_data_type {
+ /** data type name */
const char *struct_name;
+
+ /** data type release function pointer */
void (*dfree)(mrb_state *mrb, void*);
} mrb_data_type;
@@ -23,7 +34,7 @@ struct RData {
void *data;
};
-struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass, void *datap, const mrb_data_type *type);
+MRB_API struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass, void *datap, const mrb_data_type *type);
#define Data_Wrap_Struct(mrb,klass,type,ptr)\
mrb_data_object_alloc(mrb,klass,ptr,type)
@@ -37,10 +48,10 @@ struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass, void *
#define RDATA(obj) ((struct RData *)(mrb_ptr(obj)))
#define DATA_PTR(d) (RDATA(d)->data)
#define DATA_TYPE(d) (RDATA(d)->type)
-void mrb_data_check_type(mrb_state *mrb, mrb_value, const mrb_data_type*);
-void *mrb_data_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*);
+MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value, const mrb_data_type*);
+MRB_API void *mrb_data_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*);
#define DATA_GET_PTR(mrb,obj,dtype,type) (type*)mrb_data_get_ptr(mrb,obj,dtype)
-void *mrb_data_check_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*);
+MRB_API void *mrb_data_check_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*);
#define DATA_CHECK_GET_PTR(mrb,obj,dtype,type) (type*)mrb_data_check_get_ptr(mrb,obj,dtype)
/* obsolete functions and macros */
@@ -51,8 +62,14 @@ void *mrb_data_check_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_type*);
*(void**)&sval = mrb_data_get_ptr(mrb, obj, type); \
} while (0)
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+static inline void
+mrb_data_init(mrb_value v, void *ptr, const mrb_data_type *type)
+{
+ mrb_assert(mrb_type(v) == MRB_TT_DATA);
+ DATA_PTR(v) = ptr;
+ DATA_TYPE(v) = type;
+}
+
+MRB_END_DECL
#endif /* MRUBY_DATA_H */
diff --git a/include/mruby/debug.h b/include/mruby/debug.h
index a56321d42..44f84e49c 100644
--- a/include/mruby/debug.h
+++ b/include/mruby/debug.h
@@ -7,9 +7,12 @@
#ifndef MRUBY_DEBUG_H
#define MRUBY_DEBUG_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+
+/**
+ * MRuby Debugging.
+ */
+MRB_BEGIN_DECL
typedef enum mrb_debug_line_type {
mrb_debug_line_ary = 0,
@@ -44,22 +47,20 @@ typedef struct mrb_irep_debug_info {
* get line from irep's debug info and program counter
* @return returns NULL if not found
*/
-const char *mrb_debug_get_filename(mrb_irep *irep, uint32_t pc);
+MRB_API const char *mrb_debug_get_filename(mrb_irep *irep, uint32_t pc);
/*
* get line from irep's debug info and program counter
* @return returns -1 if not found
*/
-int32_t mrb_debug_get_line(mrb_irep *irep, uint32_t pc);
+MRB_API int32_t mrb_debug_get_line(mrb_irep *irep, uint32_t pc);
-mrb_irep_debug_info_file *mrb_debug_info_append_file(
+MRB_API mrb_irep_debug_info_file *mrb_debug_info_append_file(
mrb_state *mrb, mrb_irep *irep,
uint32_t start_pos, uint32_t end_pos);
-mrb_irep_debug_info *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep);
-void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d);
+MRB_API mrb_irep_debug_info *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep);
+MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d);
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_DEBUG_H */
diff --git a/include/mruby/dump.h b/include/mruby/dump.h
index fc79f1f4b..165a20b3f 100644
--- a/include/mruby/dump.h
+++ b/include/mruby/dump.h
@@ -7,22 +7,30 @@
#ifndef MRUBY_DUMP_H
#define MRUBY_DUMP_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-#include "mruby.h"
-#include "mruby/irep.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include "common.h"
-int mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t *bin_size);
-#ifdef ENABLE_STDIO
-int mrb_dump_irep_binary(mrb_state*, mrb_irep*, int, FILE*);
-int mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep*, int, FILE *f, const char *initname);
+/**
+ * Dumping compiled mruby script.
+ */
+MRB_BEGIN_DECL
+
+#define DUMP_DEBUG_INFO 1
+#define DUMP_ENDIAN_BIG 2
+#define DUMP_ENDIAN_LIL 4
+#define DUMP_ENDIAN_NAT 6
+#define DUMP_ENDIAN_MASK 6
+
+int mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size);
+#ifndef MRB_DISABLE_STDIO
+int mrb_dump_irep_binary(mrb_state*, mrb_irep*, uint8_t, FILE*);
+int mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep*, uint8_t flags, FILE *f, const char *initname);
mrb_irep *mrb_read_irep_file(mrb_state*, FILE*);
-mrb_value mrb_load_irep_file(mrb_state*,FILE*);
-mrb_value mrb_load_irep_file_cxt(mrb_state*, FILE*, mrbc_context*);
+MRB_API mrb_value mrb_load_irep_file(mrb_state*,FILE*);
+MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state*, FILE*, mrbc_context*);
#endif
-mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*);
+MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*);
/* dump/load error code
*
@@ -42,24 +50,26 @@ mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*);
#define MRB_DUMP_NULL_SYM_LEN 0xFFFF
/* Rite Binary File header */
-#define RITE_BINARY_IDENTIFIER "RITE"
-#define RITE_BINARY_FORMAT_VER "0002"
+#define RITE_BINARY_IDENT "RITE"
+#define RITE_BINARY_IDENT_LIL "ETIR"
+#define RITE_BINARY_FORMAT_VER "0003"
#define RITE_COMPILER_NAME "MATZ"
#define RITE_COMPILER_VERSION "0000"
#define RITE_VM_VER "0000"
#define RITE_BINARY_EOF "END\0"
-#define RITE_SECTION_IREP_IDENTIFIER "IREP"
-#define RITE_SECTION_LINENO_IDENTIFIER "LINE"
-#define RITE_SECTION_DEBUG_IDENTIFIER "DBG\0"
-#define RITE_SECTION_LV_IDENTIFIER "LVAR"
+#define RITE_SECTION_IREP_IDENT "IREP"
+#define RITE_SECTION_LINENO_IDENT "LINE"
+#define RITE_SECTION_DEBUG_IDENT "DBG\0"
+#define RITE_SECTION_LV_IDENT "LVAR"
#define MRB_DUMP_DEFAULT_STR_LEN 128
+#define MRB_DUMP_ALIGNMENT sizeof(uint32_t)
/* binary header */
struct rite_binary_header {
- uint8_t binary_identify[4]; /* Binary Identifier */
+ uint8_t binary_ident[4]; /* Binary Identifier */
uint8_t binary_version[4]; /* Binary Format Version */
uint8_t binary_crc[2]; /* Binary CRC */
uint8_t binary_size[4]; /* Binary Size */
@@ -69,7 +79,7 @@ struct rite_binary_header {
/* section header */
#define RITE_SECTION_HEADER \
- uint8_t section_identify[4]; \
+ uint8_t section_ident[4]; \
uint8_t section_size[4]
struct rite_section_header {
@@ -100,6 +110,17 @@ struct rite_binary_footer {
RITE_SECTION_HEADER;
};
+static inline int
+bigendian_p()
+{
+ int i;
+ char *p;
+
+ i = 1;
+ p = (char*)&i;
+ return p[0]?0:1;
+}
+
static inline size_t
uint8_to_bin(uint8_t s, uint8_t *bin)
{
@@ -125,6 +146,16 @@ uint32_to_bin(uint32_t l, uint8_t *bin)
return sizeof(uint32_t);
}
+static inline size_t
+uint32l_to_bin(uint32_t l, uint8_t *bin)
+{
+ bin[3] = (l >> 24) & 0xff;
+ bin[2] = (l >> 16) & 0xff;
+ bin[1] = (l >> 8) & 0xff;
+ bin[0] = l & 0xff;
+ return sizeof(uint32_t);
+}
+
static inline uint32_t
bin_to_uint32(const uint8_t *bin)
{
@@ -134,6 +165,15 @@ bin_to_uint32(const uint8_t *bin)
(uint32_t)bin[3];
}
+static inline uint32_t
+bin_to_uint32l(const uint8_t *bin)
+{
+ return (uint32_t)bin[3] << 24 |
+ (uint32_t)bin[2] << 16 |
+ (uint32_t)bin[1] << 8 |
+ (uint32_t)bin[0];
+}
+
static inline uint16_t
bin_to_uint16(const uint8_t *bin)
{
@@ -147,11 +187,9 @@ bin_to_uint8(const uint8_t *bin)
return (uint8_t)bin[0];
}
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
-/* crc.c */
+/** @internal crc.c */
uint16_t
calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc);
diff --git a/include/mruby/error.h b/include/mruby/error.h
index 4d37f1701..bb67e7bd8 100644
--- a/include/mruby/error.h
+++ b/include/mruby/error.h
@@ -1,5 +1,5 @@
/*
-** error.h - Exception class
+** mruby/error.h - Exception class
**
** See Copyright Notice in mruby.h
*/
@@ -7,25 +7,63 @@
#ifndef MRUBY_ERROR_H
#define MRUBY_ERROR_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
-void mrb_sys_fail(mrb_state *mrb, const char *mesg);
-mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str);
+/**
+ * MRuby error handling.
+ */
+MRB_BEGIN_DECL
+
+struct RException {
+ MRB_OBJECT_HEADER;
+ struct iv_tbl *iv;
+};
+
+#define mrb_exc_ptr(v) ((struct RException*)mrb_ptr(v))
+
+MRB_API void mrb_sys_fail(mrb_state *mrb, const char *mesg);
+MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str);
#define mrb_exc_new_str_lit(mrb, c, lit) mrb_exc_new_str(mrb, c, mrb_str_new_lit(mrb, lit))
-mrb_value mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv);
-void mrb_exc_print(mrb_state *mrb, struct RObject *exc);
-void mrb_print_backtrace(mrb_state *mrb);
-mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc);
-mrb_value mrb_get_backtrace(mrb_state *mrb);
-mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, const char *fmt, ...);
+MRB_API mrb_value mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv);
+MRB_API mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value exc);
+MRB_API mrb_value mrb_get_backtrace(mrb_state *mrb);
+MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, const char *fmt, ...);
/* declaration for fail method */
-mrb_value mrb_f_raise(mrb_state*, mrb_value);
+MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
+
+/**
+ * Protect
+ *
+ * @mrbgem mruby-error
+ */
+MRB_API mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state);
+
+/**
+ * Ensure
+ *
+ * @mrbgem mruby-error
+ */
+MRB_API mrb_value mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
+ mrb_func_t ensure, mrb_value e_data);
+
+/**
+ * Rescue
+ *
+ * @mrbgem mruby-error
+ */
+MRB_API mrb_value mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
+ mrb_func_t rescue, mrb_value r_data);
+
+/**
+ * Rescue exception
+ *
+ * @mrbgem mruby-error
+ */
+MRB_API mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
+ mrb_func_t rescue, mrb_value r_data,
+ mrb_int len, struct RClass **classes);
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_ERROR_H */
diff --git a/include/mruby/gc.h b/include/mruby/gc.h
index 02714be8e..81950f169 100644
--- a/include/mruby/gc.h
+++ b/include/mruby/gc.h
@@ -1,5 +1,5 @@
/*
-** gc.h - garbage collector for mruby
+** mruby/gc.h - garbage collector for mruby
**
** See Copyright Notice in mruby.h
*/
@@ -7,16 +7,71 @@
#ifndef MRUBY_GC_H
#define MRUBY_GC_H
-#if defined(__cplusplus)
-extern "C" {
+#include "common.h"
+
+/**
+ * Uncommon memory management stuffs.
+ */
+MRB_BEGIN_DECL
+
+
+struct mrb_state;
+
+typedef void (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data);
+void mrb_objspace_each_objects(struct mrb_state *mrb, mrb_each_object_callback *callback, void *data);
+MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c);
+
+#ifndef MRB_GC_ARENA_SIZE
+#define MRB_GC_ARENA_SIZE 100
#endif
-typedef void (mrb_each_object_callback)(mrb_state *mrb, struct RBasic *obj, void *data);
-void mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data);
-void mrb_free_context(mrb_state *mrb, struct mrb_context *c);
+typedef enum {
+ MRB_GC_STATE_ROOT = 0,
+ MRB_GC_STATE_MARK,
+ MRB_GC_STATE_SWEEP
+} mrb_gc_state;
-#if defined(__cplusplus)
-} /* extern "C" { */
+typedef struct mrb_heap_page {
+ struct RBasic *freelist;
+ struct mrb_heap_page *prev;
+ struct mrb_heap_page *next;
+ struct mrb_heap_page *free_next;
+ struct mrb_heap_page *free_prev;
+ mrb_bool old:1;
+ void *objects[];
+} mrb_heap_page;
+
+typedef struct mrb_gc {
+ mrb_heap_page *heaps; /* heaps for GC */
+ mrb_heap_page *sweeps;
+ mrb_heap_page *free_heaps;
+ size_t live; /* count of live objects */
+#ifdef MRB_GC_FIXED_ARENA
+ struct RBasic *arena[MRB_GC_ARENA_SIZE]; /* GC protection array */
+#else
+ struct RBasic **arena; /* GC protection array */
+ int arena_capa;
#endif
+ int arena_idx;
+
+ mrb_gc_state state; /* state of gc */
+ int current_white_part; /* make white object by white_part */
+ struct RBasic *gray_list; /* list of gray objects to be traversed incrementally */
+ struct RBasic *atomic_gray_list; /* list of objects to be traversed atomically */
+ size_t live_after_mark;
+ size_t threshold;
+ int interval_ratio;
+ int step_ratio;
+ mrb_bool disabled :1;
+ mrb_bool full :1;
+ mrb_bool generational :1;
+ mrb_bool out_of_memory :1;
+ size_t majorgc_old_threshold;
+} mrb_gc;
+
+MRB_API mrb_bool
+mrb_object_dead_p(struct mrb_state *mrb, struct RBasic *object);
+
+MRB_END_DECL
#endif /* MRUBY_GC_H */
diff --git a/include/mruby/hash.h b/include/mruby/hash.h
index 6a8b11373..1d00a33ab 100644
--- a/include/mruby/hash.h
+++ b/include/mruby/hash.h
@@ -7,9 +7,12 @@
#ifndef MRUBY_HASH_H
#define MRUBY_HASH_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+
+/**
+ * Hash class
+ */
+MRB_BEGIN_DECL
struct RHash {
MRB_OBJECT_HEADER;
@@ -20,24 +23,48 @@ struct RHash {
#define mrb_hash_ptr(v) ((struct RHash*)(mrb_ptr(v)))
#define mrb_hash_value(p) mrb_obj_value((void*)(p))
-mrb_value mrb_hash_new_capa(mrb_state*, int);
-mrb_value mrb_hash_new(mrb_state *mrb);
+MRB_API mrb_value mrb_hash_new_capa(mrb_state*, int);
+
+/*
+ * Initializes a new hash.
+ */
+MRB_API mrb_value mrb_hash_new(mrb_state *mrb);
+
+/*
+ * Sets a keys and values to hashes.
+ */
+MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val);
+
+/*
+ * Gets a value from a key.
+ */
+MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key);
-void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val);
-mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key);
-mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def);
-mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key);
-mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash);
-mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
-mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self);
-mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
+MRB_API mrb_value mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def);
+
+/*
+ * Deletes hash key and value pair.
+ */
+MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key);
+
+/*
+ * Gets an array of keys.
+ */
+MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash);
+MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
+MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self);
+
+/*
+ * Clears the hash.
+ */
+MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
/* RHASH_TBL allocates st_table if not available. */
#define RHASH(obj) ((struct RHash*)(mrb_ptr(obj)))
#define RHASH_TBL(h) (RHASH(h)->ht)
#define RHASH_IFNONE(h) mrb_iv_get(mrb, (h), mrb_intern_lit(mrb, "ifnone"))
#define RHASH_PROCDEFAULT(h) RHASH_IFNONE(h)
-struct kh_ht * mrb_hash_tbl(mrb_state *mrb, mrb_value hash);
+MRB_API struct kh_ht * mrb_hash_tbl(mrb_state *mrb, mrb_value hash);
#define MRB_HASH_PROC_DEFAULT 256
#define MRB_RHASH_PROCDEFAULT_P(h) (RHASH(h)->flags & MRB_HASH_PROC_DEFAULT)
@@ -47,8 +74,6 @@ void mrb_gc_mark_hash(mrb_state*, struct RHash*);
size_t mrb_gc_mark_hash_size(mrb_state*, struct RHash*);
void mrb_gc_free_hash(mrb_state*, struct RHash*);
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_HASH_H */
diff --git a/include/mruby/irep.h b/include/mruby/irep.h
index 3d6b5efc7..8922f4b76 100644
--- a/include/mruby/irep.h
+++ b/include/mruby/irep.h
@@ -7,11 +7,13 @@
#ifndef MRUBY_IREP_H
#define MRUBY_IREP_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+#include <mruby/compile.h>
-#include "mruby/compile.h"
+/**
+ * Compiled mruby scripts.
+ */
+MRB_BEGIN_DECL
enum irep_pool_type {
IREP_TT_STRING,
@@ -46,15 +48,13 @@ typedef struct mrb_irep {
#define MRB_ISEQ_NO_FREE 1
-mrb_irep *mrb_add_irep(mrb_state *mrb);
-mrb_value mrb_load_irep(mrb_state*, const uint8_t*);
-mrb_value mrb_load_irep_cxt(mrb_state*, const uint8_t*, mrbc_context*);
+MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb);
+MRB_API mrb_value mrb_load_irep(mrb_state*, const uint8_t*);
+MRB_API mrb_value mrb_load_irep_cxt(mrb_state*, const uint8_t*, mrbc_context*);
void mrb_irep_free(mrb_state*, struct mrb_irep*);
void mrb_irep_incref(mrb_state*, struct mrb_irep*);
void mrb_irep_decref(mrb_state*, struct mrb_irep*);
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_IREP_H */
diff --git a/include/mruby/khash.h b/include/mruby/khash.h
index d2501dd52..9c40c6b80 100644
--- a/include/mruby/khash.h
+++ b/include/mruby/khash.h
@@ -4,16 +4,19 @@
** See Copyright Notice in mruby.h
*/
-#ifndef KHASH_H
-#define KHASH_H
+#ifndef MRUBY_KHASH_H
+#define MRUBY_KHASH_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-#include "mruby.h"
#include <string.h>
+#include <mruby.h>
+#include "common.h"
+
+/**
+ * khash definitions used in mruby's hash table.
+ */
+MRB_BEGIN_DECL
+
typedef uint32_t khint_t;
typedef khint_t khiter_t;
@@ -148,20 +151,23 @@ kh_fill_flags(uint8_t *p, uint8_t c, size_t len)
new_n_buckets = KHASH_MIN_SIZE; \
khash_power2(new_n_buckets); \
{ \
+ kh_##name##_t hh; \
uint8_t *old_ed_flags = h->ed_flags; \
khkey_t *old_keys = h->keys; \
khval_t *old_vals = h->vals; \
khint_t old_n_buckets = h->n_buckets; \
khint_t i; \
- h->n_buckets = new_n_buckets; \
- kh_alloc_##name(mrb, h); \
+ hh.n_buckets = new_n_buckets; \
+ kh_alloc_##name(mrb, &hh); \
/* relocate */ \
for (i=0 ; i<old_n_buckets ; i++) { \
if (!__ac_iseither(old_ed_flags, i)) { \
- khint_t k = kh_put_##name(mrb, h, old_keys[i], NULL); \
- if (kh_is_map) kh_value(h,k) = old_vals[i]; \
+ khint_t k = kh_put_##name(mrb, &hh, old_keys[i], NULL); \
+ if (kh_is_map) kh_value(&hh,k) = old_vals[i]; \
} \
} \
+ /* copy hh to h */ \
+ *h = hh; \
mrb_free(mrb, old_keys); \
} \
} \
@@ -263,8 +269,6 @@ static inline khint_t __ac_X31_hash_string(const char *s)
typedef const char *kh_cstr_t;
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
-#endif /* KHASH_H */
+#endif /* MRUBY_KHASH_H */
diff --git a/include/mruby/numeric.h b/include/mruby/numeric.h
index fe4f3b264..6a7c172a6 100644
--- a/include/mruby/numeric.h
+++ b/include/mruby/numeric.h
@@ -7,33 +7,76 @@
#ifndef MRUBY_NUMERIC_H
#define MRUBY_NUMERIC_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+
+/**
+ * Numeric class and it's sub-classes.
+ *
+ * Integer, Float and Fixnum
+ */
+MRB_BEGIN_DECL
#define POSFIXABLE(f) ((f) <= MRB_INT_MAX)
#define NEGFIXABLE(f) ((f) >= MRB_INT_MIN)
#define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f))
-mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value val);
-
-mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base);
+MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value val);
+MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base);
+/* ArgumentError if format string doesn't match /%(\.[0-9]+)?[aAeEfFgG]/ */
+MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x, const char *fmt);
+MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value x);
mrb_value mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y);
mrb_value mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y);
mrb_value mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y);
mrb_value mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y);
-mrb_float mrb_to_flo(mrb_state *mrb, mrb_value x);
#define MRB_UINT_MAKE2(n) uint ## n ## _t
#define MRB_UINT_MAKE(n) MRB_UINT_MAKE2(n)
#define mrb_uint MRB_UINT_MAKE(MRB_INT_BIT)
-#ifdef MRB_WORD_BOXING
-# define MRB_INT_OVERFLOW_MASK ((mrb_uint)1 << (MRB_INT_BIT - 1 - MRB_FIXNUM_SHIFT))
+#define MRB_INT_OVERFLOW_MASK ((mrb_uint)1 << (MRB_INT_BIT - 1 - MRB_FIXNUM_SHIFT))
+
+/* Idea from Potion: https://github.com/perl11/potion (MIT) */
+#if (defined(__clang__) && ((__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ >= 4))) \
+ || (defined(__GNUC__) && __GNUC__ >= 5)
+
+static inline mrb_bool
+mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum)
+{
+ mrb_bool of;
+
+#ifdef MRB_INT64
+ long long val;
+ of = __builtin_saddll_overflow(augend, addend, &val) ||
+#else
+ int val;
+ of = __builtin_sadd_overflow(augend, addend, &val) ||
+#endif
+ (val > MRB_INT_MAX) || (val < MRB_INT_MIN);
+
+ *sum = (mrb_int) val;
+ return of;
+}
+
+static inline mrb_bool
+mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference)
+{
+ mrb_bool of;
+
+#ifdef MRB_INT64
+ long long val;
+ of = __builtin_ssubll_overflow(minuend, subtrahend, &val) ||
#else
-# define MRB_INT_OVERFLOW_MASK ((mrb_uint)1 << (MRB_INT_BIT - 1))
+ int val;
+ of = __builtin_ssub_overflow(minuend, subtrahend, &val) ||
#endif
+ (val > MRB_INT_MAX) || (val < MRB_INT_MIN);
+
+ *difference = (mrb_int) val;
+ return of;
+}
+#else
static inline mrb_bool
mrb_int_add_overflow(mrb_int augend, mrb_int addend, mrb_int *sum)
@@ -55,13 +98,13 @@ mrb_int_sub_overflow(mrb_int minuend, mrb_int subtrahend, mrb_int *difference)
return !!(((x ^ z) & (~y ^ z)) & MRB_INT_OVERFLOW_MASK);
}
+#endif
+
#undef MRB_INT_OVERFLOW_MASK
#undef mrb_uint
#undef MRB_UINT_MAKE
#undef MRB_UINT_MAKE2
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_NUMERIC_H */
diff --git a/include/mruby/object.h b/include/mruby/object.h
new file mode 100644
index 000000000..9fbfe34f3
--- /dev/null
+++ b/include/mruby/object.h
@@ -0,0 +1,39 @@
+/*
+** mruby/object.h - mruby object definition
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_OBJECT_H
+#define MRUBY_OBJECT_H
+
+#define MRB_OBJECT_HEADER \
+ enum mrb_vtype tt:8;\
+ uint32_t color:3;\
+ uint32_t flags:21;\
+ struct RClass *c;\
+ struct RBasic *gcnext
+
+#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & flag)
+
+
+struct RBasic {
+ MRB_OBJECT_HEADER;
+};
+#define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v)))
+
+struct RObject {
+ MRB_OBJECT_HEADER;
+ struct iv_tbl *iv;
+};
+#define mrb_obj_ptr(v) ((struct RObject*)(mrb_ptr(v)))
+
+#define mrb_immediate_p(x) (mrb_type(x) < MRB_TT_HAS_BASIC)
+#define mrb_special_const_p(x) mrb_immediate_p(x)
+
+struct RFiber {
+ MRB_OBJECT_HEADER;
+ struct mrb_context *cxt;
+};
+
+#endif /* MRUBY_OBJECT_H */
diff --git a/include/mruby/opcode.h b/include/mruby/opcode.h
index c8a47c273..9dfa7f75d 100644
--- a/include/mruby/opcode.h
+++ b/include/mruby/opcode.h
@@ -1,14 +1,14 @@
/*
-** opcode.h - RiteVM operation codes
+** mruby/opcode.h - RiteVM operation codes
**
** See Copyright Notice in mruby.h
*/
-#ifndef OPCODE_H
-#define OPCODE_H
+#ifndef MRUBY_OPCODE_H
+#define MRUBY_OPCODE_H
#define MAXARG_Bx (0xffff)
-#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */
+#define MAXARG_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */
/* instructions: packed 32 bit */
/* ------------------------------- */
@@ -157,4 +157,4 @@ enum {
#define OP_R_BREAK 1
#define OP_R_RETURN 2
-#endif /* OPCODE_H */
+#endif /* MRUBY_OPCODE_H */
diff --git a/include/mruby/proc.h b/include/mruby/proc.h
index 39f5eca22..24f0f319f 100644
--- a/include/mruby/proc.h
+++ b/include/mruby/proc.h
@@ -7,11 +7,13 @@
#ifndef MRUBY_PROC_H
#define MRUBY_PROC_H
-#include "mruby/irep.h"
+#include "common.h"
+#include <mruby/irep.h>
-#if defined(__cplusplus)
-extern "C" {
-#endif
+/**
+ * Proc class
+ */
+MRB_BEGIN_DECL
struct REnv {
MRB_OBJECT_HEADER;
@@ -20,7 +22,8 @@ struct REnv {
ptrdiff_t cioff;
};
-#define MRB_ENV_STACK_LEN(e) ((e)->flags)
+#define MRB_SET_ENV_STACK_LEN(e,len) (e)->flags = (unsigned int)(len)
+#define MRB_ENV_STACK_LEN(e) ((mrb_int)(e)->flags)
#define MRB_ENV_UNSHARE_STACK(e) ((e)->cioff = -1)
#define MRB_ENV_STACK_SHARED_P(e) ((e)->cioff >= 0)
@@ -37,7 +40,7 @@ struct RProc {
/* aspec access */
#define MRB_ASPEC_REQ(a) (((a) >> 18) & 0x1f)
#define MRB_ASPEC_OPT(a) (((a) >> 13) & 0x1f)
-#define MRB_ASPEC_REST(a) ((a) & (1<<12))
+#define MRB_ASPEC_REST(a) (((a) >> 12) & 0x1)
#define MRB_ASPEC_POST(a) (((a) >> 7) & 0x1f)
#define MRB_ASPEC_KEY(a) (((a) >> 2) & 0x1f)
#define MRB_ASPEC_KDICT(a) ((a) & (1<<1))
@@ -51,23 +54,23 @@ struct RProc {
#define mrb_proc_ptr(v) ((struct RProc*)(mrb_ptr(v)))
struct RProc *mrb_proc_new(mrb_state*, mrb_irep*);
-struct RProc *mrb_proc_new_cfunc(mrb_state*, mrb_func_t);
struct RProc *mrb_closure_new(mrb_state*, mrb_irep*);
-struct RProc *mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals);
+MRB_API struct RProc *mrb_proc_new_cfunc(mrb_state*, mrb_func_t);
+MRB_API struct RProc *mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals);
void mrb_proc_copy(struct RProc *a, struct RProc *b);
/* implementation of #send method */
-mrb_value mrb_f_send(mrb_state *mrb, mrb_value self);
+MRB_API mrb_value mrb_f_send(mrb_state *mrb, mrb_value self);
/* following functions are defined in mruby-proc-ext so please include it when using */
-struct RProc *mrb_proc_new_cfunc_with_env(mrb_state*, mrb_func_t, mrb_int, const mrb_value*);
-mrb_value mrb_cfunc_env_get(mrb_state*, mrb_int);
+MRB_API struct RProc *mrb_proc_new_cfunc_with_env(mrb_state*, mrb_func_t, mrb_int, const mrb_value*);
+MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state*, mrb_int);
+/* old name */
+#define mrb_cfunc_env_get(mrb, idx) mrb_proc_cfunc_env_get(mrb, idx)
-#include "mruby/khash.h"
+#include <mruby/khash.h>
KHASH_DECLARE(mt, mrb_sym, struct RProc*, TRUE)
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_PROC_H */
diff --git a/include/mruby/range.h b/include/mruby/range.h
index 61beb2319..cf42ce133 100644
--- a/include/mruby/range.h
+++ b/include/mruby/range.h
@@ -7,9 +7,12 @@
#ifndef MRUBY_RANGE_H
#define MRUBY_RANGE_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+
+/**
+ * Range class
+ */
+MRB_BEGIN_DECL
typedef struct mrb_range_edges {
mrb_value beg;
@@ -25,12 +28,21 @@ struct RRange {
#define mrb_range_ptr(v) ((struct RRange*)(mrb_ptr(v)))
#define mrb_range_value(p) mrb_obj_value((void*)(p))
-mrb_value mrb_range_new(mrb_state*, mrb_value, mrb_value, mrb_bool);
-mrb_bool mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len);
+/*
+ * Initializes a Range.
+ *
+ * If the third parameter is FALSE then it includes the last value in the range.
+ * If the third parameter is TRUE then it excludes the last value in the range.
+ *
+ * @param start the beginning value.
+ * @param end the ending value.
+ * @param exclude represents the inclusion or exclusion of the last value.
+ */
+MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end, mrb_bool exclude);
+
+MRB_API mrb_bool mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len);
mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int));
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_RANGE_H */
diff --git a/include/mruby/string.h b/include/mruby/string.h
index f8a1fa7bd..a66ba70ff 100644
--- a/include/mruby/string.h
+++ b/include/mruby/string.h
@@ -7,9 +7,12 @@
#ifndef MRUBY_STRING_H
#define MRUBY_STRING_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+
+/**
+ * String class
+ */
+MRB_BEGIN_DECL
extern const char mrb_digitmap[];
@@ -59,74 +62,153 @@ struct RString {
#define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE)
#define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE)
+#define RSTR_FROZEN_P(s) ((s)->flags & MRB_STR_FROZEN)
+#define RSTR_SET_FROZEN_FLAG(s) ((s)->flags |= MRB_STR_FROZEN)
+#define RSTR_UNSET_FROZEN_FLAG(s) ((s)->flags &= ~MRB_STR_FROZEN)
+
+/*
+ * Returns a pointer from a Ruby string
+ */
#define mrb_str_ptr(s) ((struct RString*)(mrb_ptr(s)))
#define RSTRING(s) mrb_str_ptr(s)
#define RSTRING_PTR(s) RSTR_PTR(RSTRING(s))
#define RSTRING_EMBED_LEN(s) RSTR_ENBED_LEN(RSTRING(s))
-#define RSTRING_LEN(s) RSTR_LEN(RSTRING(s))
+#define RSTRING_LEN(s) RSTR_LEN(RSTRING(s))
#define RSTRING_CAPA(s) RSTR_CAPA(RSTRING(s))
#define RSTRING_END(s) (RSTRING_PTR(s) + RSTRING_LEN(s))
mrb_int mrb_str_strlen(mrb_state*, struct RString*);
#define MRB_STR_SHARED 1
#define MRB_STR_NOFREE 2
-#define MRB_STR_EMBED 4
-#define MRB_STR_EMBED_LEN_MASK 0xf8
-#define MRB_STR_EMBED_LEN_SHIFT 3
+#define MRB_STR_FROZEN 4
+#define MRB_STR_EMBED 8
+#define MRB_STR_EMBED_LEN_MASK 0x1f0
+#define MRB_STR_EMBED_LEN_SHIFT 4
void mrb_gc_free_str(mrb_state*, struct RString*);
-void mrb_str_modify(mrb_state*, struct RString*);
-void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
-mrb_value mrb_str_plus(mrb_state*, mrb_value, mrb_value);
-mrb_value mrb_ptr_to_str(mrb_state *, void*);
-mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj);
-mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len);
-mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
-mrb_value mrb_string_type(mrb_state *mrb, mrb_value str);
-mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str);
-mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa);
-
-char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr);
-char *mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr);
-mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str);
-mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self);
-mrb_value mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck);
-double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck);
-mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str);
-mrb_int mrb_str_hash(mrb_state *mrb, mrb_value str);
-mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str);
-mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2);
-mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str);
-mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len);
-mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr);
-mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2);
+MRB_API void mrb_str_modify(mrb_state*, struct RString*);
+MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
+
+/*
+ * Adds two strings together.
+ */
+MRB_API mrb_value mrb_str_plus(mrb_state*, mrb_value, mrb_value);
+
+/*
+ * Converts pointer into a Ruby string.
+ */
+MRB_API mrb_value mrb_ptr_to_str(mrb_state *, void*);
+
+/*
+ * Returns an object as a Ruby string.
+ */
+MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj);
+
+/*
+ * Resizes the string's length.
+ */
+MRB_API mrb_value mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len);
+
+/*
+ * Returns a sub string.
+ */
+MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
+
+/*
+ * Returns a Ruby string type.
+ */
+MRB_API mrb_value mrb_string_type(mrb_state *mrb, mrb_value str);
+
+MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str);
+MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa);
+
+MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr);
+MRB_API const char *mrb_string_value_ptr(mrb_state *mrb, mrb_value str);
+MRB_API mrb_int mrb_string_value_len(mrb_state *mrb, mrb_value str);
+
+/*
+ * Duplicates a string object.
+ */
+MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str);
+
+/*
+ * Returns a symbol from a passed in string.
+ */
+MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self);
+
+MRB_API mrb_value mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck);
+MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck);
+
+/*
+ * Returns a converted string type.
+ */
+MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str);
+
+/*
+ * Returns true if the strings match and false if the strings don't match.
+ */
+MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2);
+
+/*
+ * Returns a concated string comprised of a Ruby string and a C string.
+ *
+ * @see mrb_str_cat_cstr
+ */
+MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len);
+
+/*
+ * Returns a concated string comprised of a Ruby string and a C string.
+ *
+ * @see mrb_str_cat
+ */
+MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr);
+MRB_API mrb_value mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2);
#define mrb_str_cat_lit(mrb, str, lit) mrb_str_cat(mrb, str, lit, mrb_strlen_lit(lit))
-mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2);
-int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2);
-char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str);
+/*
+ * Adds str2 to the end of str1.
+ */
+MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2);
+
+/*
+ * Returns 0 if both Ruby strings are equal. Returns a value < 0 if Ruby str1 is less than Ruby str2. Returns a value > 0 if Ruby str2 is greater than Ruby str1.
+ */
+MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2);
+
+/*
+ * Returns a newly allocated C string from a Ruby string.
+ * This is an utility function to pass a Ruby string to C library functions.
+ *
+ * - Returned string does not contain any NUL characters (but terminator).
+ * - It raises an ArgumentError exception if Ruby string contains
+ * NUL characters.
+ * - Retured string will be freed automatically on next GC.
+ * - Caller can modify returned string without affecting Ruby string
+ * (e.g. it can be used for mkstemp(3)).
+ *
+ * @param [mrb_state *] mrb The current mruby state.
+ * @param [mrb_value] str Ruby string. Must be an instance of String.
+ * @return [char *] A newly allocated C string.
+ */
+MRB_API char *mrb_str_to_cstr(mrb_state *mrb, mrb_value str);
+
mrb_value mrb_str_pool(mrb_state *mrb, mrb_value str);
+mrb_int mrb_str_hash(mrb_state *mrb, mrb_value str);
+mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str);
+
+/*
+ * Returns a printable version of str, surrounded by quote marks, with special characters escaped.
+ */
+mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str);
+
+void mrb_noregexp(mrb_state *mrb, mrb_value self);
+void mrb_regexp_check(mrb_state *mrb, mrb_value obj);
/* For backward compatibility */
-static inline mrb_value
-mrb_str_cat2(mrb_state *mrb, mrb_value str, const char *ptr) {
- return mrb_str_cat_cstr(mrb, str, ptr);
-}
-
-static inline mrb_value
-mrb_str_buf_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
-{
- return mrb_str_cat(mrb, str, ptr, len);
-}
-
-static inline mrb_value
-mrb_str_buf_append(mrb_state *mrb, mrb_value str, mrb_value str2)
-{
- return mrb_str_cat_str(mrb, str, str2);
-}
-
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+#define mrb_str_cat2(mrb, str, ptr) mrb_str_cat_cstr(mrb, str, ptr)
+#define mrb_str_buf_cat(mrb, str, ptr, len) mrb_str_cat(mrb, str, ptr, len)
+#define mrb_str_buf_append(mrb, str, str2) mrb_str_cat_str(mrb, str, str2)
+
+MRB_END_DECL
#endif /* MRUBY_STRING_H */
diff --git a/src/mrb_throw.h b/include/mruby/throw.h
index 3c7407a8d..7f720d512 100644
--- a/src/mrb_throw.h
+++ b/include/mruby/throw.h
@@ -1,5 +1,5 @@
/*
-** mrb_throw.h - mruby exception throwing handler
+** mruby/throw.h - mruby exception throwing handler
**
** See Copyright Notice in mruby.h
*/
diff --git a/include/mruby/value.h b/include/mruby/value.h
index 98af9626d..04e5644d2 100644
--- a/include/mruby/value.h
+++ b/include/mruby/value.h
@@ -1,5 +1,5 @@
/*
-** mruby/value.h - mrb_value definition
+** mruby/value.h - mruby value definitions
**
** See Copyright Notice in mruby.h
*/
@@ -7,209 +7,68 @@
#ifndef MRUBY_VALUE_H
#define MRUBY_VALUE_H
-#ifdef MRB_USE_FLOAT
- typedef float mrb_float;
-# define mrb_float_to_str(buf, i) sprintf(buf, "%.7e", i)
-# define str_to_mrb_float(buf) strtof(buf, NULL)
-#else
- typedef double mrb_float;
-# define mrb_float_to_str(buf, i) sprintf(buf, "%.16e", i)
-# define str_to_mrb_float(buf) strtod(buf, NULL)
-#endif
+#include "common.h"
+
+/**
+ * MRuby Value definition functions and macros.
+ */
+MRB_BEGIN_DECL
+
+typedef uint32_t mrb_sym;
+typedef uint8_t mrb_bool;
+struct mrb_state;
#if defined(MRB_INT16) && defined(MRB_INT64)
# error "You can't define MRB_INT16 and MRB_INT64 at the same time."
#endif
#if defined(MRB_INT64)
-# ifdef MRB_NAN_BOXING
-# error Cannot use NaN boxing when mrb_int is 64bit
-# else
- typedef int64_t mrb_int;
-# define MRB_INT_BIT 64
-# ifdef MRB_WORD_BOXING
-# define MRB_INT_MIN (INT64_MIN>>MRB_FIXNUM_SHIFT)
-# define MRB_INT_MAX (INT64_MAX>>MRB_FIXNUM_SHIFT)
-# else
-# define MRB_INT_MIN INT64_MIN
-# define MRB_INT_MAX INT64_MAX
-# endif
-# define PRIdMRB_INT PRId64
-# define PRIiMRB_INT PRIi64
-# define PRIoMRB_INT PRIo64
-# define PRIxMRB_INT PRIx64
-# define PRIXMRB_INT PRIX64
-# endif
+ typedef int64_t mrb_int;
+# define MRB_INT_BIT 64
+# define MRB_INT_MIN (INT64_MIN>>MRB_FIXNUM_SHIFT)
+# define MRB_INT_MAX (INT64_MAX>>MRB_FIXNUM_SHIFT)
#elif defined(MRB_INT16)
-# ifdef MRB_WORD_BOXING
-# error "MRB_INT16 is too small for MRB_WORD_BOXING."
-# endif
typedef int16_t mrb_int;
# define MRB_INT_BIT 16
-# define MRB_INT_MIN INT16_MIN
-# define MRB_INT_MAX INT16_MAX
+# define MRB_INT_MIN (INT16_MIN>>MRB_FIXNUM_SHIFT)
+# define MRB_INT_MAX (INT16_MAX>>MRB_FIXNUM_SHIFT)
#else
typedef int32_t mrb_int;
# define MRB_INT_BIT 32
-# ifdef MRB_WORD_BOXING
-# define MRB_INT_MIN (INT32_MIN>>MRB_FIXNUM_SHIFT)
-# define MRB_INT_MAX (INT32_MAX>>MRB_FIXNUM_SHIFT)
-# else
-# define MRB_INT_MIN INT32_MIN
-# define MRB_INT_MAX INT32_MAX
-# endif
-# define PRIdMRB_INT PRId32
-# define PRIiMRB_INT PRIi32
-# define PRIoMRB_INT PRIo32
-# define PRIxMRB_INT PRIx32
-# define PRIXMRB_INT PRIX32
+# define MRB_INT_MIN (INT32_MIN>>MRB_FIXNUM_SHIFT)
+# define MRB_INT_MAX (INT32_MAX>>MRB_FIXNUM_SHIFT)
#endif
-typedef short mrb_sym;
-#ifdef _MSC_VER
+#ifdef MRB_USE_FLOAT
+ typedef float mrb_float;
+# define str_to_mrb_float(buf) strtof(buf, NULL)
+#else
+ typedef double mrb_float;
+# define str_to_mrb_float(buf) strtod(buf, NULL)
+#endif
+
+#if defined _MSC_VER && _MSC_VER < 1900
# ifndef __cplusplus
# define inline __inline
# endif
-# if _MSC_VER < 1900
-# define snprintf _snprintf
-# endif
+# include <stdarg.h>
+MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg);
+MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...);
+# define vsnprintf(s, n, format, arg) mrb_msvc_vsnprintf(s, n, format, arg)
+# define snprintf(s, n, format, ...) mrb_msvc_snprintf(s, n, format, __VA_ARGS__)
# if _MSC_VER < 1800
# include <float.h>
# define isfinite(n) _finite(n)
# define isnan _isnan
# define isinf(n) (!_finite(n) && !_isnan(n))
# define signbit(n) (_copysign(1.0, (n)) < 0.0)
-# define strtoll _strtoi64
# define strtof (float)strtod
-# define PRId32 "I32d"
-# define PRIi32 "I32i"
-# define PRIo32 "I32o"
-# define PRIx32 "I32x"
-# define PRIX32 "I32X"
-# define PRId64 "I64d"
-# define PRIi64 "I64i"
-# define PRIo64 "I64o"
-# define PRIx64 "I64x"
-# define PRIX64 "I64X"
-static unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
+static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
# define INFINITY (*(float *)&IEEE754_INFINITY_BITS_SINGLE)
# define NAN ((float)(INFINITY - INFINITY))
-# else
-# include <inttypes.h>
# endif
-#else
-# include <inttypes.h>
-#endif
-
-typedef uint8_t mrb_bool;
-struct mrb_state;
-
-#if defined(MRB_NAN_BOXING)
-
-#ifdef MRB_USE_FLOAT
-# error ---->> MRB_NAN_BOXING and MRB_USE_FLOAT conflict <<----
-#endif
-
-#ifdef MRB_INT64
-# error ---->> MRB_NAN_BOXING and MRB_INT64 conflict <<----
-#endif
-
-enum mrb_vtype {
- MRB_TT_FALSE = 1, /* 1 */
- MRB_TT_FREE, /* 2 */
- MRB_TT_TRUE, /* 3 */
- MRB_TT_FIXNUM, /* 4 */
- MRB_TT_SYMBOL, /* 5 */
- MRB_TT_UNDEF, /* 6 */
- MRB_TT_FLOAT, /* 7 */
- MRB_TT_CPTR, /* 8 */
- MRB_TT_OBJECT, /* 9 */
- MRB_TT_CLASS, /* 10 */
- MRB_TT_MODULE, /* 11 */
- MRB_TT_ICLASS, /* 12 */
- MRB_TT_SCLASS, /* 13 */
- MRB_TT_PROC, /* 14 */
- MRB_TT_ARRAY, /* 15 */
- MRB_TT_HASH, /* 16 */
- MRB_TT_STRING, /* 17 */
- MRB_TT_RANGE, /* 18 */
- MRB_TT_EXCEPTION, /* 19 */
- MRB_TT_FILE, /* 20 */
- MRB_TT_ENV, /* 21 */
- MRB_TT_DATA, /* 22 */
- MRB_TT_FIBER, /* 23 */
- MRB_TT_MAXDEFINE /* 24 */
-};
-
-#define MRB_TT_HAS_BASIC MRB_TT_OBJECT
-
-#ifdef MRB_ENDIAN_BIG
-#define MRB_ENDIAN_LOHI(a,b) a b
-#else
-#define MRB_ENDIAN_LOHI(a,b) b a
#endif
-typedef struct mrb_value {
- union {
- mrb_float f;
- union {
- void *p;
- struct {
- MRB_ENDIAN_LOHI(
- uint32_t ttt;
- ,union {
- mrb_int i;
- mrb_sym sym;
- };
- )
- };
- } value;
- };
-} mrb_value;
-
-/* value representation by nan-boxing:
- * float : FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF
- * object: 111111111111TTTT TTPPPPPPPPPPPPPP PPPPPPPPPPPPPPPP PPPPPPPPPPPPPPPP
- * int : 1111111111110001 0000000000000000 IIIIIIIIIIIIIIII IIIIIIIIIIIIIIII
- * sym : 1111111111110001 0100000000000000 SSSSSSSSSSSSSSSS SSSSSSSSSSSSSSSS
- * In order to get enough bit size to save TT, all pointers are shifted 2 bits
- * in the right direction.
- */
-#define mrb_tt(o) ((enum mrb_vtype)(((o).value.ttt & 0xfc000)>>14))
-#define mrb_mktt(tt) (0xfff00000|((tt)<<14))
-#define mrb_type(o) ((uint32_t)0xfff00000 < (o).value.ttt ? mrb_tt(o) : MRB_TT_FLOAT)
-#define mrb_ptr(o) ((void*)((((uintptr_t)0x3fffffffffff)&((uintptr_t)((o).value.p)))<<2))
-#define mrb_float(o) (o).f
-
-#define MRB_SET_VALUE(o, tt, attr, v) do {\
- (o).value.ttt = mrb_mktt(tt);\
- switch (tt) {\
- case MRB_TT_FALSE:\
- case MRB_TT_TRUE:\
- case MRB_TT_UNDEF:\
- case MRB_TT_FIXNUM:\
- case MRB_TT_SYMBOL: (o).attr = (v); break;\
- default: (o).value.i = 0; (o).value.p = (void*)((uintptr_t)(o).value.p | (((uintptr_t)(v))>>2)); break;\
- }\
-} while (0)
-
-static inline mrb_value
-mrb_float_value(struct mrb_state *mrb, mrb_float f)
-{
- mrb_value v;
-
- if (f != f) {
- v.value.ttt = 0x7ff80000;
- v.value.i = 0;
- } else {
- v.f = f;
- }
- return v;
-}
-#define mrb_float_pool(mrb,f) mrb_float_value(mrb,f)
-
-#else
-
enum mrb_vtype {
MRB_TT_FALSE = 0, /* 0 */
MRB_TT_FREE, /* 1 */
@@ -237,214 +96,81 @@ enum mrb_vtype {
MRB_TT_MAXDEFINE /* 23 */
};
-#if defined(MRB_WORD_BOXING)
+#include <mruby/object.h>
-#include <limits.h>
-#define MRB_TT_HAS_BASIC MRB_TT_FLOAT
+#ifdef MRB_DOCUMENTATION_BLOCK
-enum mrb_special_consts {
- MRB_Qnil = 0,
- MRB_Qfalse = 2,
- MRB_Qtrue = 4,
- MRB_Qundef = 6,
-};
-
-#define MRB_FIXNUM_FLAG 0x01
-#define MRB_FIXNUM_SHIFT 1
-#define MRB_SYMBOL_FLAG 0x0e
-#define MRB_SPECIAL_SHIFT 8
-
-typedef union mrb_value {
- union {
- void *p;
- struct {
- unsigned int i_flag : MRB_FIXNUM_SHIFT;
- mrb_int i : (MRB_INT_BIT - MRB_FIXNUM_SHIFT);
- };
- struct {
- unsigned int sym_flag : MRB_SPECIAL_SHIFT;
- int sym : (sizeof(mrb_sym) * CHAR_BIT);
- };
- struct RBasic *bp;
- struct RFloat *fp;
- struct RCptr *vp;
- } value;
- unsigned long w;
-} mrb_value;
-
-#define mrb_ptr(o) (o).value.p
-#define mrb_float(o) (o).value.fp->f
-
-#define MRB_SET_VALUE(o, ttt, attr, v) do {\
- (o).w = 0;\
- (o).attr = (v);\
- switch (ttt) {\
- case MRB_TT_FALSE: (o).w = (v) ? MRB_Qfalse : MRB_Qnil; break;\
- case MRB_TT_TRUE: (o).w = MRB_Qtrue; break;\
- case MRB_TT_UNDEF: (o).w = MRB_Qundef; break;\
- case MRB_TT_FIXNUM: (o).value.i_flag = MRB_FIXNUM_FLAG; break;\
- case MRB_TT_SYMBOL: (o).value.sym_flag = MRB_SYMBOL_FLAG; break;\
- default: if ((o).value.bp) (o).value.bp->tt = ttt; break;\
- }\
-} while (0)
-
-mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f);
-mrb_value mrb_float_pool(struct mrb_state *mrb, mrb_float f);
-
-#else /* No MRB_xxx_BOXING */
-
-#define MRB_TT_HAS_BASIC MRB_TT_OBJECT
-
-typedef struct mrb_value {
- union {
- mrb_float f;
- void *p;
- mrb_int i;
- mrb_sym sym;
- } value;
- enum mrb_vtype tt;
-} mrb_value;
-
-#define mrb_type(o) (o).tt
-#define mrb_ptr(o) (o).value.p
-#define mrb_float(o) (o).value.f
-
-#define MRB_SET_VALUE(o, ttt, attr, v) do {\
- (o).tt = ttt;\
- (o).attr = v;\
-} while (0)
-
-static inline mrb_value
-mrb_float_value(struct mrb_state *mrb, mrb_float f)
-{
- mrb_value v;
- (void) mrb;
-
- MRB_SET_VALUE(v, MRB_TT_FLOAT, value.f, f);
- return v;
-}
-#define mrb_float_pool(mrb,f) mrb_float_value(mrb,f)
-
-#endif /* no boxing */
+/**
+ * @abstract
+ * MRuby value boxing.
+ *
+ * Actual implementation depends on configured boxing type.
+ *
+ * @see mruby/boxing_no.h Default boxing representation
+ * @see mruby/boxing_word.h Word representation
+ * @see mruby/boxing_nan.h Boxed double representation
+ */
+typedef void mrb_value;
#endif
-#ifdef MRB_WORD_BOXING
-
-#define mrb_cptr(o) (o).value.vp->p
-#define mrb_fixnum_p(o) ((o).value.i_flag == MRB_FIXNUM_FLAG)
-#define mrb_undef_p(o) ((o).w == MRB_Qundef)
-#define mrb_nil_p(o) ((o).w == MRB_Qnil)
-#define mrb_bool(o) ((o).w != MRB_Qnil && (o).w != MRB_Qfalse)
-
+#if defined(MRB_NAN_BOXING)
+#include "boxing_nan.h"
+#elif defined(MRB_WORD_BOXING)
+#include "boxing_word.h"
#else
+#include "boxing_no.h"
+#endif
-#define mrb_cptr(o) mrb_ptr(o)
+#ifndef mrb_fixnum_p
#define mrb_fixnum_p(o) (mrb_type(o) == MRB_TT_FIXNUM)
+#endif
+#ifndef mrb_undef_p
#define mrb_undef_p(o) (mrb_type(o) == MRB_TT_UNDEF)
-#define mrb_nil_p(o) (mrb_type(o) == MRB_TT_FALSE && !(o).value.i)
+#endif
+#ifndef mrb_nil_p
+#define mrb_nil_p(o) (mrb_type(o) == MRB_TT_FALSE && !mrb_fixnum(o))
+#endif
+#ifndef mrb_bool
#define mrb_bool(o) (mrb_type(o) != MRB_TT_FALSE)
-
-#endif /* no boxing */
-
-#define mrb_fixnum(o) (o).value.i
-#define mrb_symbol(o) (o).value.sym
+#endif
#define mrb_float_p(o) (mrb_type(o) == MRB_TT_FLOAT)
#define mrb_symbol_p(o) (mrb_type(o) == MRB_TT_SYMBOL)
#define mrb_array_p(o) (mrb_type(o) == MRB_TT_ARRAY)
#define mrb_string_p(o) (mrb_type(o) == MRB_TT_STRING)
#define mrb_hash_p(o) (mrb_type(o) == MRB_TT_HASH)
#define mrb_cptr_p(o) (mrb_type(o) == MRB_TT_CPTR)
+#define mrb_exception_p(o) (mrb_type(o) == MRB_TT_EXCEPTION)
#define mrb_test(o) mrb_bool(o)
-mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value);
-
-#define MRB_OBJECT_HEADER \
- enum mrb_vtype tt:8;\
- uint32_t color:3;\
- uint32_t flags:21;\
- struct RClass *c;\
- struct RBasic *gcnext
-
-/* white: 011, black: 100, gray: 000 */
-#define MRB_GC_GRAY 0
-#define MRB_GC_WHITE_A 1
-#define MRB_GC_WHITE_B (1 << 1)
-#define MRB_GC_BLACK (1 << 2)
-#define MRB_GC_WHITES (MRB_GC_WHITE_A | MRB_GC_WHITE_B)
-#define MRB_GC_COLOR_MASK 7
-
-#define paint_gray(o) ((o)->color = MRB_GC_GRAY)
-#define paint_black(o) ((o)->color = MRB_GC_BLACK)
-#define paint_white(o) ((o)->color = MRB_GC_WHITES)
-#define paint_partial_white(s, o) ((o)->color = (s)->current_white_part)
-#define is_gray(o) ((o)->color == MRB_GC_GRAY)
-#define is_white(o) ((o)->color & MRB_GC_WHITES)
-#define is_black(o) ((o)->color & MRB_GC_BLACK)
-#define is_dead(s, o) (((o)->color & other_white_part(s) & MRB_GC_WHITES) || (o)->tt == MRB_TT_FREE)
-#define flip_white_part(s) ((s)->current_white_part = other_white_part(s))
-#define other_white_part(s) ((s)->current_white_part ^ MRB_GC_WHITES)
-
-struct RBasic {
- MRB_OBJECT_HEADER;
-};
-#define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v)))
-/* obsolete macro mrb_basic; will be removed soon */
-#define mrb_basic(v) mrb_basic_ptr(v)
-
-struct RObject {
- MRB_OBJECT_HEADER;
- struct iv_tbl *iv;
-};
-#define mrb_obj_ptr(v) ((struct RObject*)(mrb_ptr(v)))
-/* obsolete macro mrb_object; will be removed soon */
-#define mrb_object(o) mrb_obj_ptr(o)
-#define mrb_immediate_p(x) (mrb_type(x) <= MRB_TT_CPTR)
-#define mrb_special_const_p(x) mrb_immediate_p(x)
+MRB_API mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value);
-struct RFiber {
- MRB_OBJECT_HEADER;
- struct mrb_context *cxt;
-};
-
-#ifdef MRB_WORD_BOXING
-struct RFloat {
- MRB_OBJECT_HEADER;
- mrb_float f;
-};
-
-struct RCptr {
- MRB_OBJECT_HEADER;
- void *p;
-};
-
-static inline enum mrb_vtype
-mrb_type(mrb_value o)
+/*
+ * Returns a float in Ruby.
+ */
+MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f)
{
- switch (o.w) {
- case MRB_Qfalse:
- case MRB_Qnil:
- return MRB_TT_FALSE;
- case MRB_Qtrue:
- return MRB_TT_TRUE;
- case MRB_Qundef:
- return MRB_TT_UNDEF;
- }
- if (o.value.i_flag == MRB_FIXNUM_FLAG) {
- return MRB_TT_FIXNUM;
- }
- if (o.value.sym_flag == MRB_SYMBOL_FLAG) {
- return MRB_TT_SYMBOL;
- }
- return o.value.bp->tt;
+ mrb_value v;
+ (void) mrb;
+ SET_FLOAT_VALUE(mrb, v, f);
+ return v;
}
-#endif /* MRB_WORD_BOXING */
static inline mrb_value
-mrb_fixnum_value(mrb_int i)
+mrb_cptr_value(struct mrb_state *mrb, void *p)
{
mrb_value v;
+ (void) mrb;
+ SET_CPTR_VALUE(mrb,v,p);
+ return v;
+}
- MRB_SET_VALUE(v, MRB_TT_FIXNUM, value.i, i);
+/*
+ * Returns a fixnum in Ruby.
+ */
+MRB_INLINE mrb_value mrb_fixnum_value(mrb_int i)
+{
+ mrb_value v;
+ SET_INT_VALUE(v, i);
return v;
}
@@ -452,8 +178,7 @@ static inline mrb_value
mrb_symbol_value(mrb_sym i)
{
mrb_value v;
-
- MRB_SET_VALUE(v, MRB_TT_SYMBOL, value.sym, i);
+ SET_SYM_VALUE(v, i);
return v;
}
@@ -461,58 +186,49 @@ static inline mrb_value
mrb_obj_value(void *p)
{
mrb_value v;
- struct RBasic *b = (struct RBasic*)p;
-
- MRB_SET_VALUE(v, b->tt, value.p, p);
+ SET_OBJ_VALUE(v, (struct RBasic*)p);
return v;
}
-#ifdef MRB_WORD_BOXING
-mrb_value
-mrb_cptr_value(struct mrb_state *mrb, void *p);
-#else
-static inline mrb_value
-mrb_cptr_value(struct mrb_state *mrb, void *p)
+
+/*
+ * Get a nil mrb_value object.
+ *
+ * @return
+ * nil mrb_value object reference.
+ */
+MRB_INLINE mrb_value mrb_nil_value(void)
{
mrb_value v;
- (void) mrb;
-
- MRB_SET_VALUE(v, MRB_TT_CPTR, value.p, p);
+ SET_NIL_VALUE(v);
return v;
}
-#endif
-/* obsolete macros; will be removed */
-#define MRB_TT_VOIDP MRB_TT_CPTR
-#define mrb_voidp_value(m,p) mrb_cptr_value((m),(p))
-#define mrb_voidp(o) mrb_cptr(o)
-#define mrb_voidp_p(o) mrb_cptr_p(o)
-#define MRB_TT_HAS_BASIC_P(tt) ((tt) >= MRB_TT_HAS_BASIC)
-
-static inline mrb_value
-mrb_false_value(void)
+/*
+ * Returns false in Ruby.
+ */
+MRB_INLINE mrb_value mrb_false_value(void)
{
mrb_value v;
-
- MRB_SET_VALUE(v, MRB_TT_FALSE, value.i, 1);
+ SET_FALSE_VALUE(v);
return v;
}
-static inline mrb_value
-mrb_nil_value(void)
+/*
+ * Returns true in Ruby.
+ */
+MRB_INLINE mrb_value mrb_true_value(void)
{
mrb_value v;
-
- MRB_SET_VALUE(v, MRB_TT_FALSE, value.i, 0);
+ SET_TRUE_VALUE(v);
return v;
}
static inline mrb_value
-mrb_true_value(void)
+mrb_bool_value(mrb_bool boolean)
{
mrb_value v;
-
- MRB_SET_VALUE(v, MRB_TT_TRUE, value.i, 1);
+ SET_BOOL_VALUE(v, boolean);
return v;
}
@@ -520,18 +236,33 @@ static inline mrb_value
mrb_undef_value(void)
{
mrb_value v;
-
- MRB_SET_VALUE(v, MRB_TT_UNDEF, value.i, 0);
+ SET_UNDEF_VALUE(v);
return v;
}
-static inline mrb_value
-mrb_bool_value(mrb_bool boolean)
+#ifdef MRB_USE_ETEXT_EDATA
+extern char _etext[];
+#ifdef MRB_NO_INIT_ARRAY_START
+extern char _edata[];
+
+static inline mrb_bool
+mrb_ro_data_p(const char *p)
{
- mrb_value v;
+ return _etext < p && p < _edata;
+}
+#else
+extern char __init_array_start[];
- MRB_SET_VALUE(v, boolean ? MRB_TT_TRUE : MRB_TT_FALSE, value.i, 1);
- return v;
+static inline mrb_bool
+mrb_ro_data_p(const char *p)
+{
+ return _etext < p && p < (char*)&__init_array_start;
}
+#endif
+#else
+# define mrb_ro_data_p(p) FALSE
+#endif
+
+MRB_END_DECL
#endif /* MRUBY_VALUE_H */
diff --git a/include/mruby/variable.h b/include/mruby/variable.h
index 68a4e5889..15068039a 100644
--- a/include/mruby/variable.h
+++ b/include/mruby/variable.h
@@ -7,9 +7,12 @@
#ifndef MRUBY_VARIABLE_H
#define MRUBY_VARIABLE_H
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include "common.h"
+
+/**
+ * Functions to access mruby variables.
+ */
+MRB_BEGIN_DECL
typedef struct global_variable {
int counter;
@@ -34,36 +37,38 @@ mrb_value mrb_vm_cv_get(mrb_state*, mrb_sym);
void mrb_vm_cv_set(mrb_state*, mrb_sym, mrb_value);
mrb_value mrb_vm_const_get(mrb_state*, mrb_sym);
void mrb_vm_const_set(mrb_state*, mrb_sym, mrb_value);
-mrb_value mrb_const_get(mrb_state*, mrb_value, mrb_sym);
-void mrb_const_set(mrb_state*, mrb_value, mrb_sym, mrb_value);
-mrb_bool mrb_const_defined(mrb_state*, mrb_value, mrb_sym);
-void mrb_const_remove(mrb_state*, mrb_value, mrb_sym);
+MRB_API mrb_value mrb_const_get(mrb_state*, mrb_value, mrb_sym);
+MRB_API void mrb_const_set(mrb_state*, mrb_value, mrb_sym, mrb_value);
+MRB_API mrb_bool mrb_const_defined(mrb_state*, mrb_value, mrb_sym);
+MRB_API void mrb_const_remove(mrb_state*, mrb_value, mrb_sym);
-mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym);
-void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
-mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym);
-void mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
-mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym);
-void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v);
-mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym);
-mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym);
-void mrb_iv_copy(mrb_state *mrb, mrb_value dst, mrb_value src);
-int mrb_const_defined_at(mrb_state *mrb, struct RClass *klass, mrb_sym id);
+MRB_API mrb_bool mrb_iv_p(mrb_state *mrb, mrb_sym sym);
+MRB_API void mrb_iv_check(mrb_state *mrb, mrb_sym sym);
+MRB_API mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym);
+MRB_API void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
+MRB_API mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym);
+MRB_API void mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
+MRB_API mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym);
+MRB_API void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v);
+MRB_API mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym);
+MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym);
+MRB_API void mrb_iv_copy(mrb_state *mrb, mrb_value dst, mrb_value src);
+MRB_API mrb_bool mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id);
+MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym);
+MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val);
+MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym);
+MRB_API mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym);
+MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_value v);
+MRB_API void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v);
+MRB_API mrb_bool mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym);
+mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*);
mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod);
mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self);
-mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym);
-void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value val);
-void mrb_gv_remove(mrb_state *mrb, mrb_sym sym);
mrb_value mrb_obj_instance_variables(mrb_state*, mrb_value);
-mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*);
-mrb_sym mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer);
mrb_value mrb_mod_class_variables(mrb_state*, mrb_value);
mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym);
-mrb_value mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym);
-void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_value v);
-void mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v);
mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym);
-mrb_bool mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym);
+mrb_sym mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer);
/* GC functions */
void mrb_gc_mark_gv(mrb_state*);
@@ -72,8 +77,6 @@ void mrb_gc_mark_iv(mrb_state*, struct RObject*);
size_t mrb_gc_mark_iv_size(mrb_state*, struct RObject*);
void mrb_gc_free_iv(mrb_state*, struct RObject*);
-#if defined(__cplusplus)
-} /* extern "C" { */
-#endif
+MRB_END_DECL
#endif /* MRUBY_VARIABLE_H */
diff --git a/include/mruby/version.h b/include/mruby/version.h
index d451dfb31..20c39ec79 100644
--- a/include/mruby/version.h
+++ b/include/mruby/version.h
@@ -1,34 +1,110 @@
+/*
+** mruby/version.h - mruby version definition
+**
+** See Copyright Notice in mruby.h
+*/
+
#ifndef MRUBY_VERSION_H
#define MRUBY_VERSION_H
+#include "common.h"
+
+/**
+ * mruby version definition macros
+ */
+MRB_BEGIN_DECL
+
+/*
+ * A passed in expression.
+ */
+#define MRB_STRINGIZE0(expr) #expr
+
+/*
+ * Passes in an expression to MRB_STRINGIZE0.
+ */
+#define MRB_STRINGIZE(expr) MRB_STRINGIZE0(expr)
+
+/*
+ * The version of Ruby used by mruby.
+ */
#define MRUBY_RUBY_VERSION "1.9"
+
+/*
+ * Ruby engine.
+ */
#define MRUBY_RUBY_ENGINE "mruby"
-#define MRUBY_VERSION "1.0.0"
+/*
+ * Major release version number.
+ */
#define MRUBY_RELEASE_MAJOR 1
-#define MRUBY_RELEASE_MINOR 0
-#define MRUBY_RELEASE_TEENY 1
-#define MRUBY_RELEASE_NO 10001
-#define MRUBY_RELEASE_DATE "2014-01-10"
-#define MRUBY_RELEASE_YEAR 2014
-#define MRUBY_RELEASE_MONTH 1
-#define MRUBY_RELEASE_DAY 10
+/*
+ * Minor release version number.
+ */
+#define MRUBY_RELEASE_MINOR 2
+
+/*
+ * Tiny release version number.
+ */
+#define MRUBY_RELEASE_TEENY 0
+
+/*
+ * The mruby version.
+ */
+#define MRUBY_VERSION MRB_STRINGIZE(MRUBY_RELEASE_MAJOR) "." MRB_STRINGIZE(MRUBY_RELEASE_MINOR) "." MRB_STRINGIZE(MRUBY_RELEASE_TEENY)
+
+/*
+ * Release number.
+ */
+#define MRUBY_RELEASE_NO (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY)
+
+/*
+ * Release year.
+ */
+#define MRUBY_RELEASE_YEAR 2015
+
+/*
+ * Release month.
+ */
+#define MRUBY_RELEASE_MONTH 11
+
+/*
+ * Release day.
+ */
+#define MRUBY_RELEASE_DAY 17
+
+/*
+ * Release date as a string.
+ */
+#define MRUBY_RELEASE_DATE MRB_STRINGIZE(MRUBY_RELEASE_YEAR) "-" MRB_STRINGIZE(MRUBY_RELEASE_MONTH) "-" MRB_STRINGIZE(MRUBY_RELEASE_DAY)
+
+/*
+ * The year mruby was first created.
+ */
#define MRUBY_BIRTH_YEAR 2010
+/*
+ * MRuby's authors.
+ */
#define MRUBY_AUTHOR "mruby developers"
-#define MRB_STRINGIZE0(expr) #expr
-#define MRB_STRINGIZE(expr) MRB_STRINGIZE0(expr)
-
+/*
+ * mruby's version, and release date.
+ */
#define MRUBY_DESCRIPTION \
"mruby " MRUBY_VERSION \
" (" MRUBY_RELEASE_DATE ") " \
+/*
+ * mruby's copyright information.
+ */
#define MRUBY_COPYRIGHT \
"mruby - Copyright (c) " \
MRB_STRINGIZE(MRUBY_BIRTH_YEAR)"-" \
MRB_STRINGIZE(MRUBY_RELEASE_YEAR)" " \
MRUBY_AUTHOR \
+MRB_END_DECL
+
#endif /* MRUBY_VERSION_H */
diff --git a/lib/mruby/source.rb b/lib/mruby/source.rb
new file mode 100644
index 000000000..5819a322b
--- /dev/null
+++ b/lib/mruby/source.rb
@@ -0,0 +1,30 @@
+require "pathname"
+
+module MRuby
+ module Source
+ # MRuby's source root directory
+ ROOT = Pathname.new(File.expand_path('../../../',__FILE__))
+
+ # Reads a constant defined at version.h
+ MRUBY_READ_VERSION_CONSTANT = Proc.new { |name| ROOT.join('include','mruby','version.h').read.match(/^#define #{name} +"?([\w\. ]+)"?$/)[1] }
+
+ MRUBY_RUBY_VERSION = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_VERSION']
+ MRUBY_RUBY_ENGINE = MRUBY_READ_VERSION_CONSTANT['MRUBY_RUBY_ENGINE']
+
+ MRUBY_RELEASE_MAJOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MAJOR'])
+ MRUBY_RELEASE_MINOR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MINOR'])
+ MRUBY_RELEASE_TEENY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_TEENY'])
+
+ MRUBY_VERSION = [MRUBY_RELEASE_MAJOR,MRUBY_RELEASE_MINOR,MRUBY_RELEASE_TEENY].join('.')
+ MRUBY_RELEASE_NO = (MRUBY_RELEASE_MAJOR * 100 * 100 + MRUBY_RELEASE_MINOR * 100 + MRUBY_RELEASE_TEENY)
+
+ MRUBY_RELEASE_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_YEAR'])
+ MRUBY_RELEASE_MONTH = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_MONTH'])
+ MRUBY_RELEASE_DAY = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_RELEASE_DAY'])
+ MRUBY_RELEASE_DATE = [MRUBY_RELEASE_YEAR,MRUBY_RELEASE_MONTH,MRUBY_RELEASE_DAY].join('.')
+
+ MRUBY_BIRTH_YEAR = Integer(MRUBY_READ_VERSION_CONSTANT['MRUBY_BIRTH_YEAR'])
+
+ MRUBY_AUTHOR = MRUBY_READ_VERSION_CONSTANT['MRUBY_AUTHOR']
+ end
+end
diff --git a/minirake b/minirake
index bdaef0b87..eb219b934 100755
--- a/minirake
+++ b/minirake
@@ -237,7 +237,8 @@ module MiniRake
# Time stamp for file task.
def timestamp
- File::stat(name.to_s).mtime
+ stat = File::stat(name.to_s)
+ stat.directory? ? Time.at(0) : stat.mtime
end
end
@@ -254,12 +255,14 @@ module MiniRake
# Declare a set of files tasks to create the given directories on
# demand.
- def directory(dir)
- path = []
- Sys.split_all(dir).each do |p|
- path << p
- FileTask.define_task(File.join(path)) do |t|
- Sys.makedirs(t.name)
+ def directory(args, &block)
+ MiniRake::FileTask.define_task(args) do |t|
+ block.call(t) unless block.nil?
+ dir = args.is_a?(Hash) ? args.keys.first : args
+ (dir.split(File::SEPARATOR) + ['']).inject do |acc, part|
+ (acc + File::SEPARATOR).tap do |d|
+ Dir.mkdir(d) unless File.exists? d
+ end + part
end
end
end
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox
index 06609d9e7..0960ba979 100644
--- a/mrbgems/default.gembox
+++ b/mrbgems/default.gembox
@@ -14,34 +14,34 @@ MRuby::GemBox.new do |conf|
# Use standard Struct class
conf.gem :core => "mruby-struct"
- # Use extensional Enumerable module
+ # Use Enumerable module extension
conf.gem :core => "mruby-enum-ext"
- # Use extensional String class
+ # Use String class extension
conf.gem :core => "mruby-string-ext"
- # Use extensional Numeric class
+ # Use Numeric class extension
conf.gem :core => "mruby-numeric-ext"
- # Use extensional Array class
+ # Use Array class extension
conf.gem :core => "mruby-array-ext"
- # Use extensional Hash class
+ # Use Hash class extension
conf.gem :core => "mruby-hash-ext"
- # Use extensional Range class
+ # Use Range class extension
conf.gem :core => "mruby-range-ext"
- # Use extensional Proc class
+ # Use Proc class extension
conf.gem :core => "mruby-proc-ext"
- # Use extensional Symbol class
+ # Use Symbol class extension
conf.gem :core => "mruby-symbol-ext"
# Use Random class
conf.gem :core => "mruby-random"
- # Use extensional Object class
+ # Use Object class extension
conf.gem :core => "mruby-object-ext"
# Use ObjectSpace class
@@ -56,7 +56,7 @@ MRuby::GemBox.new do |conf|
# Use Enumerable::Lazy class (require mruby-enumerator)
conf.gem :core => "mruby-enum-lazy"
- # Use extended toplevel object (main) methods
+ # Use toplevel object (main) methods extension
conf.gem :core => "mruby-toplevel-ext"
# Generate mirb command
@@ -68,6 +68,9 @@ MRuby::GemBox.new do |conf|
# Generate mruby-strip command
conf.gem :core => "mruby-bin-strip"
- # Use extensional Kernel module
+ # Use Kernel module extension
conf.gem :core => "mruby-kernel-ext"
+
+ # Use mruby-compiler to build other mrbgems
+ conf.gem :core => "mruby-compiler"
end
diff --git a/mrbgems/full-core.gembox b/mrbgems/full-core.gembox
index 4a6ebbc80..9a5b7081b 100644
--- a/mrbgems/full-core.gembox
+++ b/mrbgems/full-core.gembox
@@ -2,8 +2,8 @@ MRuby::GemBox.new do |conf|
conf.gem :core => "mruby-sprintf"
conf.gem :core => "mruby-print"
- Dir.glob("#{root}/mrbgems/mruby-*") do |x|
- g = File.basename(x)
- conf.gem :core => g unless g =~ /^mruby-(print|sprintf)$/
+ Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x|
+ g = File.basename File.dirname x
+ conf.gem :core => g unless g =~ /^mruby-(print|sprintf|bin-debugger|test)$/
end
end
diff --git a/mrbgems/mruby-array-ext/mrbgem.rake b/mrbgems/mruby-array-ext/mrbgem.rake
index e4b5938c7..882caf1ab 100644
--- a/mrbgems/mruby-array-ext/mrbgem.rake
+++ b/mrbgems/mruby-array-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-array-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extensional Array class'
+ spec.summary = 'Array class extension'
end
diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb
index fd80fa0bb..f19581cdc 100644
--- a/mrbgems/mruby-array-ext/mrblib/array.rb
+++ b/mrbgems/mruby-array-ext/mrblib/array.rb
@@ -1,6 +1,31 @@
class Array
##
# call-seq:
+ # Array.try_convert(obj) -> array or nil
+ #
+ # Tries to convert +obj+ into an array, using +to_ary+ method.
+ # converted array or +nil+ if +obj+ cannot be converted for any reason.
+ # This method can be used to check if an argument is an array.
+ #
+ # Array.try_convert([1]) #=> [1]
+ # Array.try_convert("1") #=> nil
+ #
+ # if tmp = Array.try_convert(arg)
+ # # the argument is an array
+ # elsif tmp = String.try_convert(arg)
+ # # the argument is a string
+ # end
+ #
+ def self.try_convert(obj)
+ if obj.respond_to?(:to_ary)
+ obj.to_ary
+ else
+ nil
+ end
+ end
+
+ ##
+ # call-seq:
# ary.uniq! -> ary or nil
# ary.uniq! { |item| ... } -> ary or nil
#
@@ -217,7 +242,7 @@ class Array
# [ "a", "b", "c" ].compact! #=> nil
#
def compact!
- result = self.select { |e| e != nil }
+ result = self.select { |e| !e.nil? }
if result.size == self.size
nil
else
@@ -262,7 +287,7 @@ class Array
#
def fetch(n=nil, ifnone=NONE, &block)
- warn "block supersedes default value argument" if n != nil && ifnone != NONE && block
+ warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block
idx = n
if idx < 0
@@ -312,51 +337,51 @@ class Array
#
def fill(arg0=nil, arg1=nil, arg2=nil, &block)
- if arg0 == nil && arg1 == nil && arg2 == nil && !block
+ if arg0.nil? && arg1.nil? && arg2.nil? && !block
raise ArgumentError, "wrong number of arguments (0 for 1..3)"
end
beg = len = 0
ary = []
if block
- if arg0 == nil && arg1 == nil && arg2 == nil
+ if arg0.nil? && arg1.nil? && arg2.nil?
# ary.fill { |index| block } -> ary
beg = 0
len = self.size
- elsif arg0 != nil && arg0.kind_of?(Range)
+ elsif !arg0.nil? && arg0.kind_of?(Range)
# ary.fill(range) { |index| block } -> ary
beg = arg0.begin
beg += self.size if beg < 0
len = arg0.end
len += self.size if len < 0
len += 1 unless arg0.exclude_end?
- elsif arg0 != nil
+ elsif !arg0.nil?
# ary.fill(start [, length] ) { |index| block } -> ary
beg = arg0
beg += self.size if beg < 0
- if arg1 == nil
+ if arg1.nil?
len = self.size
else
len = arg0 + arg1
end
end
else
- if arg0 != nil && arg1 == nil && arg2 == nil
+ if !arg0.nil? && arg1.nil? && arg2.nil?
# ary.fill(obj) -> ary
beg = 0
len = self.size
- elsif arg0 != nil && arg1 != nil && arg1.kind_of?(Range)
+ elsif !arg0.nil? && !arg1.nil? && arg1.kind_of?(Range)
# ary.fill(obj, range ) -> ary
beg = arg1.begin
beg += self.size if beg < 0
len = arg1.end
len += self.size if len < 0
len += 1 unless arg1.exclude_end?
- elsif arg0 != nil && arg1 != nil
+ elsif !arg0.nil? && !arg1.nil?
# ary.fill(obj, start [, length]) -> ary
beg = arg1
beg += self.size if beg < 0
- if arg2 == nil
+ if arg2.nil?
len = self.size
else
len = beg + arg2
@@ -582,7 +607,7 @@ class Array
elsif v == true
satisfied = true
smaller = true
- elsif v == false || v == nil
+ elsif v == false || v.nil?
smaller = false
end
if smaller
@@ -681,4 +706,42 @@ class Array
return nil if self.size == result.size
self.replace(result)
end
+
+ ##
+ # call-seq:
+ # ary.index(val) -> int or nil
+ # ary.index {|item| block } -> int or nil
+ #
+ # Returns the _index_ of the first object in +ary+ such that the object is
+ # <code>==</code> to +obj+.
+ #
+ # If a block is given instead of an argument, returns the _index_ of the
+ # first object for which the block returns +true+. Returns +nil+ if no
+ # match is found.
+ #
+ # ISO 15.2.12.5.14
+ def index(val=NONE, &block)
+ return to_enum(:find_index, val) if !block && val == NONE
+
+ if block
+ idx = 0
+ self.each do |*e|
+ return idx if block.call(*e)
+ idx += 1
+ end
+ else
+ return self.__ary_index(val)
+ end
+ nil
+ end
+
+ ##
+ # call-seq:
+ # ary.to_ary -> ary
+ #
+ # Returns +self+.
+ #
+ def to_ary
+ self
+ end
end
diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c
index d69f0ac44..d5c96e2cc 100644
--- a/mrbgems/mruby-array-ext/src/array.c
+++ b/mrbgems/mruby-array-ext/src/array.c
@@ -1,8 +1,8 @@
-#include "mruby.h"
-#include "mruby/value.h"
-#include "mruby/array.h"
-#include "mruby/range.h"
-#include "mruby/hash.h"
+#include <mruby.h>
+#include <mruby/value.h>
+#include <mruby/array.h>
+#include <mruby/range.h>
+#include <mruby/hash.h>
/*
* call-seq:
@@ -113,8 +113,9 @@ mrb_ary_values_at(mrb_state *mrb, mrb_value self)
* Returns the result of interpreting <i>aray</i> as an array of
* <tt>[key, value]</tt> paris.
*
- * [[:foo, :bar], [1, 2]].to_h
- * # => {:foo => :bar, 1 => 2}
+ * [[:foo, :bar], [1, 2]].to_h
+ * # => {:foo => :bar, 1 => 2}
+ *
*/
static mrb_value
diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb
index 8c919f7e0..f17cb80e1 100644
--- a/mrbgems/mruby-array-ext/test/array.rb
+++ b/mrbgems/mruby-array-ext/test/array.rb
@@ -1,6 +1,13 @@
##
# Array(Ext) Test
+assert("Array.try_convert") do
+ assert_nil Array.try_convert(0)
+ assert_nil Array.try_convert(nil)
+ assert_equal [], Array.try_convert([])
+ assert_equal [1,2,3], Array.try_convert([1,2,3])
+end
+
assert("Array#assoc") do
s1 = [ "colors", "red", "blue", "green" ]
s2 = [ "letters", "a", "b", "c" ]
@@ -293,3 +300,13 @@ assert('Array#to_h') do
assert_raise(TypeError) { [1].to_h }
assert_raise(ArgumentError) { [[1]].to_h }
end
+
+assert("Array#index (block)") do
+ assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
+ assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 }
+end
+
+assert("Array#to_ary") do
+ assert_equal [], [].to_ary
+ assert_equal [1,2,3], [1,2,3].to_ary
+end
diff --git a/mrbgems/mruby-bin-debugger/bintest/mrdb.rb b/mrbgems/mruby-bin-debugger/bintest/mrdb.rb
new file mode 100644
index 000000000..ae40f0a46
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/bintest/mrdb.rb
@@ -0,0 +1,286 @@
+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
diff --git a/mrbgems/mruby-bin-debugger/bintest/print.rb b/mrbgems/mruby-bin-debugger/bintest/print.rb
new file mode 100644
index 000000000..e9d85f333
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/bintest/print.rb
@@ -0,0 +1,701 @@
+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
+
diff --git a/mrbgems/mruby-bin-debugger/mrbgem.rake b/mrbgems/mruby-bin-debugger/mrbgem.rake
new file mode 100644
index 000000000..764f431af
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/mrbgem.rake
@@ -0,0 +1,9 @@
+MRuby::Gem::Specification.new('mruby-bin-debugger') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'mruby debugger command'
+
+ spec.add_dependency('mruby-eval', :core => 'mruby-eval')
+
+ spec.bins = %w(mrdb)
+end
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
new file mode 100644
index 000000000..67b4e8422
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
@@ -0,0 +1,516 @@
+/*
+** 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);
+
+ 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);
+ strncpy(set_class, class_name, strlen(class_name) + 1);
+ }
+ else {
+ set_class = NULL;
+ }
+
+ set_method = mrb_malloc(mrb, strlen(method_name) + 1);
+
+ 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((i + 1) == dbg->bpnum) {
+ 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;
+ uint32_t 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<dbg->bpnum; 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;
+ uint32_t i;
+
+ if((mrb == NULL) || (dbg == NULL) || (class_obj == NULL)) {
+ return MRB_DEBUG_INVALID_ARGUMENT;
+ }
+
+ bp = dbg->bp;
+ for(i=0; i<dbg->bpnum; 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;
+}
+
+
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h
new file mode 100644
index 000000000..3c7e41e52
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.h
@@ -0,0 +1,26 @@
+/*
+** 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_ */
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
new file mode 100644
index 000000000..1c77d1ef8
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
@@ -0,0 +1,235 @@
+/*
+ * 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) + sizeof("/") - 1;
+ }
+
+ path = mrb_malloc(mrb, len);
+ 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);
+
+ dir = mrb_malloc(mrb, len + 1);
+ strncpy(dir, path, len);
+ dir[len] = '\0';
+
+ return dir;
+}
+
+static source_file*
+source_file_new(mrb_state *mrb, mrb_debug_context *dbg, char *filename)
+{
+ source_file *file = NULL;
+
+ file = mrb_malloc(mrb, sizeof(source_file));
+
+ 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)
+{
+ int c;
+ char *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 ((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;
+ }
+
+ mrb_free(mrb, (void *)search_path[1]);
+
+ 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;
+ }
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h
new file mode 100644
index 000000000..6c4107885
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.h
@@ -0,0 +1,14 @@
+/*
+ * 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_ */
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
new file mode 100644
index 000000000..a9c895b55
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
@@ -0,0 +1,78 @@
+/*
+** 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)(struct mrb_state *, struct mrb_irep *, mrb_code *, mrb_value *);
+ 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_lit(mrb, "begin\n");
+ ruby_code = mrb_str_cat(mrb, ruby_code, expr, len);
+ ruby_code = mrb_str_cat_lit(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;
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
new file mode 100644
index 000000000..e256f6262
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
@@ -0,0 +1,13 @@
+/*
+ * 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_ */
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
new file mode 100644
index 000000000..57c1d5682
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
@@ -0,0 +1,428 @@
+/*
+** 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) { \
+ int i; \
+ ul = 0; \
+ for(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)
+{
+ const 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 = strrchr(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;
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
new file mode 100644
index 000000000..b4aeb62d4
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
@@ -0,0 +1,501 @@
+/*
+** 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 coming.\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);
+ }
+
+ s = mrb_malloc(mrb, len + strlen(ext) + 1);
+ 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);
+
+ if (filename != NULL && filename != st->filename) {
+ mrb_free(mrb, filename);
+ }
+ listcmd_parser_state_free(mrb, st);
+ }
+
+ 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) ");
+ fflush(stdout);
+
+ 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;
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
new file mode 100644
index 000000000..cca636711
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
@@ -0,0 +1,58 @@
+/*
+** 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_lit(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_lit(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);
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
new file mode 100644
index 000000000..2030c08b6
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
@@ -0,0 +1,62 @@
+/*
+** 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;
+}
+
+dbgcmd_state
+dbgcmd_next(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrdb->dbg->xm = DBG_NEXT;
+ mrdb->dbg->prvci = mrb->c->ci;
+ return DBGST_CONTINUE;
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
new file mode 100644
index 000000000..a5cf3be54
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
@@ -0,0 +1,759 @@
+/*
+** 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 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] */
+ {"next", NULL, 1, 0, 1, DBGCMD_NEXT, dbgcmd_next}, /* n[ext] */
+ {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_debug_delete_break_all(mrb, _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(mrdb_state));
+
+ memset(mrdb, 0, sizeof(mrdb_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 += sizeof("quit") - 1;
+ }
+
+ 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);
+ fflush(stdout);
+ 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 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->prvci = NULL;
+ 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:
+ if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
+ return;
+ }
+ dbg->method_bpno = 0;
+ dbg->bm = BRK_STEP;
+ break;
+
+ case DBG_NEXT:
+ if (!file || (dbg->prvfile == file && dbg->prvline == line)) {
+ return;
+ }
+ if((uint32_t)(dbg->prvci) < (uint32_t)(mrb->c->ci)) {
+ return;
+ }
+ dbg->prvci = NULL;
+ dbg->method_bpno = 0;
+ dbg->bm = BRK_NEXT;
+ 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;
+
+ l_restart:
+
+ if (mrb == NULL) {
+ fputs("Invalid mrb_state, exiting mruby\n", stderr);
+ return EXIT_FAILURE;
+ }
+
+ /* 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;
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
new file mode 100644
index 000000000..5ac12c1cd
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
@@ -0,0 +1,165 @@
+/*
+** 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;
+ mrb_callinfo *prvci;
+
+ 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*);
+dbgcmd_state dbgcmd_next(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
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
new file mode 100644
index 000000000..f17f9c57d
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
@@ -0,0 +1,16 @@
+/*
+** 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
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h
new file mode 100644
index 000000000..c7812b0d6
--- /dev/null
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdberror.h
@@ -0,0 +1,20 @@
+/*
+** 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
+
diff --git a/mrbgems/mruby-bin-mirb/mrbgem.rake b/mrbgems/mruby-bin-mirb/mrbgem.rake
index c4374b47e..a74871d81 100644
--- a/mrbgems/mruby-bin-mirb/mrbgem.rake
+++ b/mrbgems/mruby-bin-mirb/mrbgem.rake
@@ -6,12 +6,28 @@ MRuby::Gem::Specification.new('mruby-bin-mirb') do |spec|
if spec.build.cc.search_header_path 'readline/readline.h'
spec.cc.defines << "ENABLE_READLINE"
if spec.build.cc.search_header_path 'termcap.h'
- spec.linker.libraries << 'termcap'
+ if MRUBY_BUILD_HOST_IS_CYGWIN || MRUBY_BUILD_HOST_IS_OPENBSD
+ if spec.build.cc.search_header_path 'termcap.h'
+ if MRUBY_BUILD_HOST_IS_CYGWIN then
+ spec.linker.libraries << 'ncurses'
+ else
+ spec.linker.libraries << 'termcap'
+ end
+ end
+ end
+ end
+ if RUBY_PLATFORM.include?('netbsd')
+ spec.linker.libraries << 'edit'
+ else
+ spec.linker.libraries << 'readline'
+ if spec.build.cc.search_header_path 'curses.h'
+ spec.linker.libraries << 'ncurses'
+ end
end
- spec.linker.libraries << 'readline'
elsif spec.build.cc.search_header_path 'linenoise.h'
spec.cc.defines << "ENABLE_LINENOISE"
end
spec.bins = %w(mirb)
+ spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
end
diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
index dece361a5..bdba2d2eb 100644
--- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
+++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
@@ -11,6 +11,9 @@
#include <stdio.h>
#include <ctype.h>
+#include <signal.h>
+#include <setjmp.h>
+
#ifdef ENABLE_READLINE
#include <readline/readline.h>
#include <readline/history.h>
@@ -29,31 +32,76 @@
#define MIRB_USING_HISTORY()
#endif
+#ifndef _WIN32
+#define MIRB_SIGSETJMP(env) sigsetjmp(env, 1)
+#define MIRB_SIGLONGJMP(env, val) siglongjmp(env, val)
+#define SIGJMP_BUF sigjmp_buf
+#else
+#define MIRB_SIGSETJMP(env) setjmp(env)
+#define MIRB_SIGLONGJMP(env, val) longjmp(env, val)
+#define SIGJMP_BUF jmp_buf
+#endif
+
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/proc.h>
+#include <mruby/compile.h>
+#include <mruby/string.h>
+
#ifdef ENABLE_READLINE
-#include <limits.h>
-static const char *history_file_name = ".mirb_history";
-char history_path[PATH_MAX];
+
+static const char history_file_name[] = ".mirb_history";
+
+static char *
+get_history_path(mrb_state *mrb)
+{
+ char *path = NULL;
+ const char *home = getenv("HOME");
+
+#ifdef _WIN32
+ if (home != NULL) {
+ home = getenv("USERPROFILE");
+ }
#endif
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/proc.h"
-#include "mruby/compile.h"
-#include "mruby/string.h"
+ if (home != NULL) {
+ int len = snprintf(NULL, 0, "%s/%s", home, history_file_name);
+ if (len >= 0) {
+ size_t size = len + 1;
+ path = (char *)mrb_malloc_simple(mrb, size);
+ if (path != NULL) {
+ int n = snprintf(path, size, "%s/%s", home, history_file_name);
+ if (n != len) {
+ mrb_free(mrb, path);
+ path = NULL;
+ }
+ }
+ }
+ }
+
+ return path;
+}
+
+#endif
static void
p(mrb_state *mrb, mrb_value obj, int prompt)
{
- obj = mrb_funcall(mrb, obj, "inspect", 0);
+ mrb_value val;
+
+ val = mrb_funcall(mrb, obj, "inspect", 0);
if (prompt) {
if (!mrb->exc) {
fputs(" => ", stdout);
}
else {
- obj = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0);
+ val = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0);
}
}
- fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout);
+ if (!mrb_string_p(val)) {
+ val = mrb_obj_as_string(mrb, obj);
+ }
+ fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout);
putc('\n', stdout);
}
@@ -76,7 +124,7 @@ is_code_block_open(struct mrb_parser_state *parser)
/* check if parser error are available */
if (0 < parser->nerr) {
- const char *unexpected_end = "syntax error, unexpected $end";
+ const char unexpected_end[] = "syntax error, unexpected $end";
const char *message = parser->error_buffer[0].message;
/* a parser error occur, we have to check if */
@@ -84,7 +132,7 @@ is_code_block_open(struct mrb_parser_state *parser)
/* a different issue which we have to show to */
/* the user */
- if (strncmp(message, unexpected_end, strlen(unexpected_end)) == 0) {
+ if (strncmp(message, unexpected_end, sizeof(unexpected_end) - 1) == 0) {
code_block_open = TRUE;
}
else if (strcmp(message, "syntax error, unexpected keyword_end") == 0) {
@@ -158,10 +206,8 @@ is_code_block_open(struct mrb_parser_state *parser)
return code_block_open;
}
-void mrb_show_version(mrb_state *);
-void mrb_show_copyright(mrb_state *);
-
struct _args {
+ FILE *rfp;
mrb_bool verbose : 1;
int argc;
char** argv;
@@ -200,7 +246,7 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
switch (*item++) {
case 'v':
if (!args->verbose) mrb_show_version(mrb);
- args->verbose = 1;
+ args->verbose = TRUE;
break;
case '-':
if (strcmp((*argv) + 2, "version") == 0) {
@@ -208,7 +254,7 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
exit(EXIT_SUCCESS);
}
else if (strcmp((*argv) + 2, "verbose") == 0) {
- args->verbose = 1;
+ args->verbose = TRUE;
break;
}
else if (strcmp((*argv) + 2, "copyright") == 0) {
@@ -219,12 +265,30 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args)
return EXIT_FAILURE;
}
}
+
+ if (args->rfp == NULL) {
+ if (*argv != NULL) {
+ args->rfp = fopen(argv[0], "r");
+ if (args->rfp == NULL) {
+ printf("Cannot open program file. (%s)\n", *argv);
+ return EXIT_FAILURE;
+ }
+ 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);
+ mrb_free(mrb, args->argv);
mrb_close(mrb);
}
@@ -246,6 +310,7 @@ print_cmdline(int code_block_open)
else {
printf("> ");
}
+ fflush(stdout);
}
#endif
@@ -274,6 +339,23 @@ check_keyword(const char *buf, const char *word)
return 1;
}
+
+#ifndef ENABLE_READLINE
+volatile sig_atomic_t input_canceled = 0;
+void
+ctrl_c_handler(int signo)
+{
+ input_canceled = 1;
+}
+#else
+SIGJMP_BUF ctrl_c_buf;
+void
+ctrl_c_handler(int signo)
+{
+ MIRB_SIGLONGJMP(ctrl_c_buf, 1);
+}
+#endif
+
int
main(int argc, char **argv)
{
@@ -281,16 +363,19 @@ main(int argc, char **argv)
char last_code_line[1024] = { 0 };
#ifndef ENABLE_READLINE
int last_char;
- int char_index;
+ size_t char_index;
#else
- char *home = NULL;
+ char *history_path;
+ char* line;
#endif
mrbc_context *cxt;
struct mrb_parser_state *parser;
mrb_state *mrb;
mrb_value result;
struct _args args;
+ mrb_value ARGV;
int n;
+ int i;
mrb_bool code_block_open = FALSE;
int ai;
unsigned int stack_keep = 0;
@@ -301,7 +386,6 @@ main(int argc, char **argv)
fputs("Invalid mrb interpreter, exiting mirb\n", stderr);
return EXIT_FAILURE;
}
- mrb_define_global_const(mrb, "ARGV", mrb_ary_new_capa(mrb, 0));
n = parse_args(mrb, argc, argv, &args);
if (n == EXIT_FAILURE) {
@@ -310,36 +394,51 @@ main(int argc, char **argv)
return n;
}
+ ARGV = mrb_ary_new_capa(mrb, args.argc);
+ for (i = 0; i < args.argc; i++) {
+ char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
+ if (utf8) {
+ mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
+ mrb_utf8_free(utf8);
+ }
+ }
+ mrb_define_global_const(mrb, "ARGV", ARGV);
+
+#ifdef ENABLE_READLINE
+ history_path = get_history_path(mrb);
+ if (history_path == NULL) {
+ fputs("failed to get history path\n", stderr);
+ mrb_close(mrb);
+ return EXIT_FAILURE;
+ }
+
+ MIRB_USING_HISTORY();
+ MIRB_READ_HISTORY(history_path);
+#endif
+
print_hint();
cxt = mrbc_context_new(mrb);
- cxt->capture_errors = 1;
+ cxt->capture_errors = TRUE;
cxt->lineno = 1;
mrbc_filename(mrb, cxt, "(mirb)");
- if (args.verbose) cxt->dump_result = 1;
+ if (args.verbose) cxt->dump_result = TRUE;
ai = mrb_gc_arena_save(mrb);
-#ifdef ENABLE_READLINE
- MIRB_USING_HISTORY();
- home = getenv("HOME");
-#ifdef _WIN32
- if (!home)
- home = getenv("USERPROFILE");
-#endif
- if (home) {
- strcpy(history_path, home);
- strcat(history_path, "/");
- strcat(history_path, history_file_name);
- MIRB_READ_HISTORY(history_path);
- }
-#endif
+ while (TRUE) {
+ char *utf8;
+ if (args.rfp) {
+ if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL)
+ goto done;
+ break;
+ }
- while (TRUE) {
#ifndef ENABLE_READLINE
print_cmdline(code_block_open);
+ signal(SIGINT, ctrl_c_handler);
char_index = 0;
while ((last_char = getchar()) != '\n') {
if (last_char == EOF) break;
@@ -349,6 +448,15 @@ main(int argc, char **argv)
}
last_code_line[char_index++] = last_char;
}
+ signal(SIGINT, SIG_DFL);
+ if (input_canceled) {
+ ruby_code[0] = '\0';
+ last_code_line[0] = '\0';
+ code_block_open = FALSE;
+ puts("^C");
+ input_canceled = 0;
+ continue;
+ }
if (last_char == EOF) {
fputs("\n", stdout);
break;
@@ -357,7 +465,19 @@ main(int argc, char **argv)
last_code_line[char_index++] = '\n';
last_code_line[char_index] = '\0';
#else
- char* line = MIRB_READLINE(code_block_open ? "* " : "> ");
+ if (MIRB_SIGSETJMP(ctrl_c_buf) == 0) {
+ ;
+ }
+ else {
+ ruby_code[0] = '\0';
+ last_code_line[0] = '\0';
+ code_block_open = FALSE;
+ puts("^C");
+ }
+ signal(SIGINT, ctrl_c_handler);
+ line = MIRB_READLINE(code_block_open ? "* " : "> ");
+ signal(SIGINT, SIG_DFL);
+
if (line == NULL) {
printf("\n");
break;
@@ -372,6 +492,8 @@ main(int argc, char **argv)
free(line);
#endif
+done:
+
if (code_block_open) {
if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) {
fputs("concatenated input string too long\n", stderr);
@@ -386,13 +508,21 @@ main(int argc, char **argv)
strcpy(ruby_code, last_code_line);
}
+ utf8 = mrb_utf8_from_locale(ruby_code, -1);
+ if (!utf8) abort();
+
/* parse code */
parser = mrb_parser_new(mrb);
- parser->s = ruby_code;
- parser->send = ruby_code + strlen(ruby_code);
+ if (parser == NULL) {
+ fputs("create parser state error\n", stderr);
+ break;
+ }
+ parser->s = utf8;
+ parser->send = utf8 + strlen(utf8);
parser->lineno = cxt->lineno;
mrb_parser_parse(parser, cxt);
code_block_open = is_code_block_open(parser);
+ mrb_utf8_free(utf8);
if (code_block_open) {
/* no evaluation of code */
@@ -405,6 +535,11 @@ main(int argc, char **argv)
else {
/* generate bytecode */
struct RProc *proc = mrb_generate_code(mrb, parser);
+ if (proc == NULL) {
+ fputs("codegen error\n", stderr);
+ mrb_parser_free(parser);
+ break;
+ }
if (args.verbose) {
mrb_codedump_all(mrb, proc);
@@ -436,12 +571,14 @@ main(int argc, char **argv)
mrb_parser_free(parser);
cxt->lineno++;
}
- mrbc_context_free(mrb, cxt);
- mrb_close(mrb);
#ifdef ENABLE_READLINE
MIRB_WRITE_HISTORY(history_path);
+ mrb_free(mrb, history_path);
#endif
+ mrbc_context_free(mrb, cxt);
+ mrb_close(mrb);
+
return 0;
}
diff --git a/mrbgems/mruby-bin-mrbc/mrbgem.rake b/mrbgems/mruby-bin-mrbc/mrbgem.rake
new file mode 100644
index 000000000..e710b5a49
--- /dev/null
+++ b/mrbgems/mruby-bin-mrbc/mrbgem.rake
@@ -0,0 +1,16 @@
+MRuby::Gem::Specification.new 'mruby-bin-mrbc' do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'mruby compiler executable'
+
+ spec.add_dependency 'mruby-compiler', :core => 'mruby-compiler'
+
+ exec = exefile("#{build.build_dir}/bin/mrbc")
+ mrbc_objs = Dir.glob("#{spec.dir}/tools/mrbc/*.c").map { |f| objfile(f.pathmap("#{spec.build_dir}/tools/mrbc/%n")) }.flatten
+
+ file exec => mrbc_objs + [libfile("#{build.build_dir}/lib/libmruby_core")] do |t|
+ build.linker.run t.name, t.prerequisites
+ end
+
+ build.bins << 'mrbc' unless build.bins.find { |v| v == 'mrbc' }
+end
diff --git a/tools/mrbc/mrbc.c b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
index 52e762a50..580c2e25b 100644
--- a/tools/mrbc/mrbc.c
+++ b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
@@ -1,24 +1,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/compile.h"
-#include "mruby/dump.h"
-#include "mruby/proc.h"
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/dump.h>
+#include <mruby/proc.h>
#define RITEBIN_EXT ".mrb"
#define C_EXT ".c"
-#if defined(__cplusplus)
-extern "C" {
-void mrb_show_version(mrb_state *);
-void mrb_show_copyright(mrb_state *);
-}
-#else
-void mrb_show_version(mrb_state *);
-void mrb_show_copyright(mrb_state *);
-#endif
-
struct mrbc_args {
int argc;
char **argv;
@@ -28,7 +18,7 @@ struct mrbc_args {
const char *initname;
mrb_bool check_syntax : 1;
mrb_bool verbose : 1;
- mrb_bool debug_info : 1;
+ unsigned int flags : 4;
};
static void
@@ -41,6 +31,8 @@ usage(const char *name)
"-v print version number, then turn on verbose mode",
"-g produce debugging information",
"-B<symbol> binary <symbol> output in C language format",
+ "-e generate little endian iseq data",
+ "-E generate big endian iseq data",
"--verbose run at verbose mode",
"--version print the version",
"--copyright print the copyright",
@@ -124,7 +116,13 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
args->verbose = TRUE;
break;
case 'g':
- args->debug_info = TRUE;
+ args->flags |= DUMP_DEBUG_INFO;
+ break;
+ case 'E':
+ args->flags = DUMP_ENDIAN_BIG | (args->flags & ~DUMP_ENDIAN_MASK);
+ break;
+ case 'e':
+ args->flags = DUMP_ENDIAN_LIL | (args->flags & ~DUMP_ENDIAN_MASK);
break;
case 'h':
return -1;
@@ -153,14 +151,17 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
break;
}
}
+ if (args->verbose && args->initname && (args->flags & DUMP_ENDIAN_MASK) == 0) {
+ fprintf(stderr, "%s: generating %s endian C file. specify -e/-E for cross compiling.\n",
+ args->prog, bigendian_p() ? "big" : "little");
+ }
return i;
}
static void
cleanup(mrb_state *mrb, struct mrbc_args *args)
{
- if (args->outfile)
- mrb_free(mrb, (void*)args->outfile);
+ mrb_free(mrb, (void*)args->outfile);
mrb_close(mrb);
}
@@ -232,13 +233,13 @@ dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, st
mrb_irep *irep = proc->body.irep;
if (args->initname) {
- n = mrb_dump_irep_cfunc(mrb, irep, args->debug_info, wfp, args->initname);
+ n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname);
if (n == MRB_DUMP_INVALID_ARGUMENT) {
fprintf(stderr, "%s: invalid C language symbol name\n", args->initname);
}
}
else {
- n = mrb_dump_irep_binary(mrb, irep, args->debug_info, wfp);
+ n = mrb_dump_irep_binary(mrb, irep, args->flags, wfp);
}
if (n != MRB_DUMP_OK) {
fprintf(stderr, "%s: error in mrb dump (%s) %d\n", args->prog, outfile, n);
diff --git a/mrbgems/mruby-bin-mruby-config/mrbgem.rake b/mrbgems/mruby-bin-mruby-config/mrbgem.rake
index 7e5f685f0..73f971a91 100644
--- a/mrbgems/mruby-bin-mruby-config/mrbgem.rake
+++ b/mrbgems/mruby-bin-mruby-config/mrbgem.rake
@@ -23,7 +23,7 @@ MRuby.each_target do
FileUtils.copy "#{File.dirname(__FILE__)}/#{mruby_config}", t.name
config = Hash[open("#{build_dir}/lib/libmruby.flags.mak").read.split("\n").map {|x| a = x.split(/\s*=\s*/, 2); [a[0], a[1].gsub('\\"', '"') ]}]
IO.write(t.name, File.open(t.name) {|f|
- f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LDFLAGS|MRUBY_LIBS)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"}
+ f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"}
})
FileUtils.chmod(0755, t.name)
end
diff --git a/mrbgems/mruby-bin-mruby-config/mruby-config b/mrbgems/mruby-bin-mruby-config/mruby-config
index f346eb719..fcc3fabe3 100644
--- a/mrbgems/mruby-bin-mruby-config/mruby-config
+++ b/mrbgems/mruby-bin-mruby-config/mruby-config
@@ -4,12 +4,14 @@ while [ $# -gt 0 ]; do
case $1 in
--cflags) echo MRUBY_CFLAGS;;
--ldflags) echo MRUBY_LDFLAGS;;
+ --ldflags-before-libs) echo MRUBY_LDFLAGS_BEFORE_LIBS;;
--libs) echo MRUBY_LIBS;;
--help) echo "Usage: mruby-config [switches]"
echo " switches:"
- echo " --cflags print flags passed to compiler"
- echo " --ldflags print flags passed to linker"
- echo " --libs print linked libraries"
+ echo " --cflags print flags passed to compiler"
+ echo " --ldflags print flags passed to linker"
+ echo " --ldflags-before-libs print flags passwd to linker before linked libraries"
+ echo " --libs print linked libraries"
exit 0;;
esac
shift
diff --git a/mrbgems/mruby-bin-mruby-config/mruby-config.bat b/mrbgems/mruby-bin-mruby-config/mruby-config.bat
index c306630c6..212dbdd77 100644
--- a/mrbgems/mruby-bin-mruby-config/mruby-config.bat
+++ b/mrbgems/mruby-bin-mruby-config/mruby-config.bat
@@ -5,6 +5,7 @@ shift
if "%0" equ "" goto :eof
if "%0" equ "--cflags" goto cflags
if "%0" equ "--ldflags" goto ldflags
+if "%0" equ "--ldflags-before-libs" goto ldflagsbeforelibs
if "%0" equ "--libs" goto libs
if "%0" equ "--help" goto showhelp
echo Invalid Option
@@ -22,9 +23,14 @@ goto top
echo MRUBY_LDFLAGS
goto top
+:ldflagsbeforelibs
+echo MRUBY_LDFLAGS_BEFORE_LIBS
+goto top
+
:showhelp
echo Usage: mruby-config [switches]
echo switches:
-echo --cflags print flags passed to compiler
-echo --ldflags print flags passed to linker
-echo --libs print linked libraries
+echo --cflags print flags passed to compiler
+echo --ldflags print flags passed to linker
+echo --ldflags-before-libs print flags passwd to linker before linked libraries
+echo --libs print linked libraries
diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
index de211c1ba..01fc94632 100644
--- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb
+++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
@@ -1,17 +1,17 @@
require 'tempfile'
assert('regression for #1564') do
- o = `bin/mruby -e '<<' 2>&1`
+ o = `#{cmd('mruby')} -e #{shellquote('<<')} 2>&1`
assert_equal o, "-e:1:2: syntax error, unexpected tLSHFT\n"
- o = `bin/mruby -e '<<-' 2>&1`
+ o = `#{cmd('mruby')} -e #{shellquote('<<-')} 2>&1`
assert_equal o, "-e:1:3: syntax error, unexpected tLSHFT\n"
end
assert('regression for #1572') do
script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb')
- system "echo 'p \"ok\"' > #{script.path}"
- system "bin/mrbc -g -o #{bin.path} #{script.path}"
- o = `bin/mruby -b #{bin.path}`.strip
+ File.write script.path, 'p "ok"'
+ system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}"
+ o = `#{cmd('mruby')} -b #{bin.path}`.strip
assert_equal o, '"ok"'
end
@@ -21,14 +21,14 @@ assert '$0 value' do
# .rb script
script.write "p $0\n"
script.flush
- assert_equal "\"#{script.path}\"", `./bin/mruby "#{script.path}"`.chomp
+ assert_equal "\"#{script.path}\"", `#{cmd('mruby')} "#{script.path}"`.chomp
# .mrb file
- `./bin/mrbc -o "#{bin.path}" "#{script.path}"`
- assert_equal "\"#{bin.path}\"", `./bin/mruby -b "#{bin.path}"`.chomp
+ `#{cmd('mrbc')} -o "#{bin.path}" "#{script.path}"`
+ assert_equal "\"#{bin.path}\"", `#{cmd('mruby')} -b "#{bin.path}"`.chomp
# one liner
- assert_equal '"-e"', `./bin/mruby -e 'p $0'`.chomp
+ assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp
end
assert '__END__', '8.6' do
@@ -42,5 +42,5 @@ __END__
p 'legend'
EOS
script.flush
- assert_equal "\"test\"\n\"fin\"\n", `./bin/mruby #{script.path}`
+ assert_equal "\"test\"\n\"fin\"\n", `#{cmd('mruby')} #{script.path}`
end
diff --git a/mrbgems/mruby-bin-mruby/mrbgem.rake b/mrbgems/mruby-bin-mruby/mrbgem.rake
index 4e2f6a142..ba7fad1fa 100644
--- a/mrbgems/mruby-bin-mruby/mrbgem.rake
+++ b/mrbgems/mruby-bin-mruby/mrbgem.rake
@@ -3,4 +3,5 @@ MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec|
spec.author = 'mruby developers'
spec.summary = 'mruby command'
spec.bins = %w(mruby)
+ spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
end
diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
index 73617b162..ab6c6688f 100644
--- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
+++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
@@ -1,27 +1,25 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/compile.h"
-#include "mruby/dump.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/compile.h>
+#include <mruby/dump.h>
+#include <mruby/variable.h>
-#ifndef ENABLE_STDIO
+#ifdef MRB_DISABLE_STDIO
static void
p(mrb_state *mrb, mrb_value obj)
{
- obj = mrb_funcall(mrb, obj, "inspect", 0);
- fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout);
+ mrb_value val = mrb_inspect(mrb, obj);
+
+ fwrite(RSTRING_PTR(val), RSTRING_LEN(val), 1, stdout);
putc('\n', stdout);
}
#else
#define p(mrb,obj) mrb_p(mrb,obj)
#endif
-void mrb_show_version(mrb_state *);
-void mrb_show_copyright(mrb_state *);
-
struct _args {
FILE *rfp;
char* cmdline;
@@ -161,10 +159,9 @@ cleanup(mrb_state *mrb, struct _args *args)
{
if (args->rfp && args->rfp != stdin)
fclose(args->rfp);
- if (args->cmdline && !args->fname)
+ if (!args->fname)
mrb_free(mrb, args->cmdline);
- if (args->argv)
- mrb_free(mrb, args->argv);
+ mrb_free(mrb, args->argv);
mrb_close(mrb);
}
@@ -194,7 +191,11 @@ main(int argc, char **argv)
ARGV = mrb_ary_new_capa(mrb, args.argc);
for (i = 0; i < args.argc; i++) {
- mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, args.argv[i]));
+ char* utf8 = mrb_utf8_from_locale(args.argv[i], -1);
+ if (utf8) {
+ mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8));
+ mrb_utf8_free(utf8);
+ }
}
mrb_define_global_const(mrb, "ARGV", ARGV);
@@ -207,7 +208,7 @@ main(int argc, char **argv)
/* Set $0 */
zero_sym = mrb_intern_lit(mrb, "$0");
if (args.rfp) {
- char *cmdline;
+ const char *cmdline;
cmdline = args.cmdline ? args.cmdline : "-";
mrbc_filename(mrb, c, cmdline);
mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline));
@@ -225,7 +226,10 @@ main(int argc, char **argv)
v = mrb_load_file_cxt(mrb, args.rfp, c);
}
else {
- v = mrb_load_string_cxt(mrb, args.cmdline, c);
+ char* utf8 = mrb_utf8_from_locale(args.cmdline, -1);
+ if (!utf8) abort();
+ v = mrb_load_string_cxt(mrb, utf8, c);
+ mrb_utf8_free(utf8);
}
mrbc_context_free(mrb, c);
diff --git a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb b/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
index 770ea0638..bb664a2b1 100644
--- a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
+++ b/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
@@ -1,22 +1,22 @@
require 'tempfile'
assert('no files') do
- o = `bin/mruby-strip 2>&1`
+ o = `#{cmd('mruby-strip')} 2>&1`
assert_equal 1, $?.exitstatus
assert_equal "no files to strip", o.split("\n")[0]
end
assert('file not found') do
- o = `bin/mruby-strip not_found.mrb 2>&1`
+ o = `#{cmd('mruby-strip')} not_found.mrb 2>&1`
assert_equal 1, $?.exitstatus
- assert_equal "can't open file not_found.mrb\n", o
+ assert_equal "can't open file for reading not_found.mrb\n", o
end
assert('not irep file') do
t = Tempfile.new('script.rb')
t.write 'p test\n'
t.flush
- o = `bin/mruby-strip #{t.path} 2>&1`
+ o = `#{cmd('mruby-strip')} #{t.path} 2>&1`
assert_equal 1, $?.exitstatus
assert_equal "can't read irep file #{t.path}\n", o
end
@@ -26,15 +26,15 @@ assert('success') do
Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb')
script_file.write "p 'test'\n"
script_file.flush
- `bin/mrbc -g -o #{compiled1.path} #{script_file.path}`
- `bin/mrbc -g -o #{compiled2.path} #{script_file.path}`
+ `#{cmd('mrbc')} -g -o #{compiled1.path} #{script_file.path}`
+ `#{cmd('mrbc')} -g -o #{compiled2.path} #{script_file.path}`
- o = `bin/mruby-strip #{compiled1.path}`
+ o = `#{cmd('mruby-strip')} #{compiled1.path}`
assert_equal 0, $?.exitstatus
assert_equal "", o
- assert_equal `bin/mruby #{script_file.path}`, `bin/mruby -b #{compiled1.path}`
+ assert_equal `#{cmd('mruby')} #{script_file.path}`, `#{cmd('mruby')} -b #{compiled1.path}`
- o = `bin/mruby-strip #{compiled1.path} #{compiled2.path}`
+ o = `#{cmd('mruby-strip')} #{compiled1.path} #{compiled2.path}`
assert_equal 0, $?.exitstatus
assert_equal "", o
end
@@ -44,12 +44,12 @@ assert('check debug section') do
Tempfile.new('script.rb'), Tempfile.new('c1.mrb'), Tempfile.new('c2.mrb')
script_file.write "p 'test'\n"
script_file.flush
- `bin/mrbc -o #{without_debug.path} #{script_file.path}`
- `bin/mrbc -g -o #{with_debug.path} #{script_file.path}`
+ `#{cmd('mrbc')} -o #{without_debug.path} #{script_file.path}`
+ `#{cmd('mrbc')} -g -o #{with_debug.path} #{script_file.path}`
assert_true with_debug.size >= without_debug.size
- `bin/mruby-strip #{with_debug.path}`
+ `#{cmd('mruby-strip')} #{with_debug.path}`
assert_equal without_debug.size, with_debug.size
end
@@ -62,12 +62,12 @@ a += b
p Kernel.local_variables
EOS
script_file.flush
- `bin/mrbc -o #{with_lv.path} #{script_file.path}`
- `bin/mrbc -o #{without_lv.path} #{script_file.path}`
+ `#{cmd('mrbc')} -o #{with_lv.path} #{script_file.path}`
+ `#{cmd('mrbc')} -o #{without_lv.path} #{script_file.path}`
- `bin/mruby-strip -l #{without_lv.path}`
+ `#{cmd('mruby-strip')} -l #{without_lv.path}`
assert_true without_lv.size < with_lv.size
- assert_equal '[:a, :b]', `bin/mruby -b #{with_lv.path}`.chomp
- assert_equal '[]', `bin/mruby -b #{without_lv.path}`.chomp
+ assert_equal '[:a, :b]', `#{cmd('mruby')} -b #{with_lv.path}`.chomp
+ assert_equal '[]', `#{cmd('mruby')} -b #{without_lv.path}`.chomp
end
diff --git a/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c b/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
index f209fa060..1fd2bc5eb 100644
--- a/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
+++ b/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
@@ -1,11 +1,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/dump.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/dump.h>
struct strip_args {
+ int argc_start;
+ int argc;
+ char **argv;
mrb_bool lvar;
};
@@ -28,23 +31,25 @@ irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
static void
print_usage(const char *f)
{
- printf("Usage: %s [options] irepfiles\n", f);
- printf("options:\n");
+ printf("Usage: %s [switches] irepfiles\n", f);
+ printf("switches:\n");
printf(" -l, --lvar remove LVAR section too.\n");
}
static int
parse_args(int argc, char **argv, struct strip_args *args)
{
- static const struct strip_args initial_args = {0};
int i;
- *args = initial_args;
+ args->argc_start = 0;
+ args->argc = argc;
+ args->argv = argv;
+ args->lvar = FALSE;
for (i = 1; i < argc; ++i) {
- size_t const len = strlen(argv[i]);
+ const size_t len = strlen(argv[i]);
if (len >= 2 && argv[i][0] == '-') {
- switch(argv[i][1]) {
+ switch (argv[i][1]) {
case 'l':
args->lvar = TRUE;
break;
@@ -56,22 +61,75 @@ parse_args(int argc, char **argv, struct strip_args *args)
default:
return -1;
}
- } else {
+ }
+ else {
break;
}
}
+ args->argc_start = i;
return i;
}
+static int
+strip(mrb_state *mrb, struct strip_args *args)
+{
+ int i;
+
+ for (i = args->argc_start; i < args->argc; ++i) {
+ char *filename;
+ FILE *rfile;
+ mrb_irep *irep;
+ FILE *wfile;
+ int dump_result;
+
+ filename = args->argv[i];
+ rfile = fopen(filename, "rb");
+ if (rfile == NULL) {
+ fprintf(stderr, "can't open file for reading %s\n", filename);
+ return EXIT_FAILURE;
+ }
+
+ irep = mrb_read_irep_file(mrb, rfile);
+ fclose(rfile);
+ if (irep == NULL) {
+ fprintf(stderr, "can't read irep file %s\n", filename);
+ return EXIT_FAILURE;
+ }
+
+ /* clear lv if --lvar is enabled */
+ if (args->lvar) {
+ irep_remove_lv(mrb, irep);
+ }
+
+ wfile = fopen(filename, "wb");
+ if (wfile == NULL) {
+ fprintf(stderr, "can't open file for writing %s\n", filename);
+ mrb_irep_decref(mrb, irep);
+ return EXIT_FAILURE;
+ }
+
+ /* debug flag must always be false */
+ dump_result = mrb_dump_irep_binary(mrb, irep, FALSE, wfile);
+
+ fclose(wfile);
+ mrb_irep_decref(mrb, irep);
+
+ if (dump_result != MRB_DUMP_OK) {
+ fprintf(stderr, "error occurred during dumping %s\n", filename);
+ return EXIT_FAILURE;
+ }
+ }
+ return EXIT_SUCCESS;
+}
+
int
main(int argc, char **argv)
{
struct strip_args args;
- int args_result, i, dump_result;
- FILE **files;
- mrb_irep **ireps;
+ int args_result;
mrb_state *mrb;
+ int ret;
if (argc <= 1) {
printf("no files to strip\n");
@@ -84,48 +142,14 @@ main(int argc, char **argv)
print_usage(argv[0]);
return EXIT_FAILURE;
}
-
- files = (FILE**)malloc(sizeof(FILE*) * argc);
- for (i = args_result; i < argc; ++i) {
- files[i] = fopen(argv[i], "rb");
-
- if (!files[i]) {
- fprintf(stderr, "can't open file %s\n", argv[i]);
- return EXIT_FAILURE;
- }
- }
-
- mrb = mrb_open();
-
- ireps = (mrb_irep**)malloc(sizeof(mrb_irep*) * argc);
- for (i = args_result; i < argc; ++i) {
- ireps[i] = mrb_read_irep_file(mrb, files[i]);
- if (!ireps[i]) {
- fprintf(stderr, "can't read irep file %s\n", argv[i]);
- return EXIT_FAILURE;
- }
- fclose(files[i]);
- files[i] = fopen(argv[i], "wb");
- if (!ireps[i]) {
- fprintf(stderr, "can't reopen irep file %s\n", argv[i]);
- return EXIT_FAILURE;
- }
+ mrb = mrb_open_core(mrb_default_allocf, NULL);
+ if (mrb == NULL) {
+ fputs("Invalid mrb_state, exiting mruby-strip\n", stderr);
+ return EXIT_FAILURE;
}
- for (i = args_result; i < argc; ++i) {
- /* clear lv if --lvar is enabled */
- if (args.lvar) {
- irep_remove_lv(mrb, ireps[i]);
- }
-
- /* debug flag must be alway false */
- dump_result = mrb_dump_irep_binary(mrb, ireps[i], FALSE, files[i]);
- if (dump_result != MRB_DUMP_OK) {
- fprintf(stderr, "error occur when dumping %s", argv[i]);
- return EXIT_FAILURE;
- }
- }
+ ret = strip(mrb, &args);
mrb_close(mrb);
- return EXIT_SUCCESS;
+ return ret;
}
diff --git a/bintest/mrbc.rb b/mrbgems/mruby-compiler/bintest/mrbc.rb
index b016378a1..e4dc6a9a8 100644
--- a/bintest/mrbc.rb
+++ b/mrbgems/mruby-compiler/bintest/mrbc.rb
@@ -6,7 +6,7 @@ assert('Compiling multiple files without new line in last line. #2361') do
a.flush
b.write('module B; end')
b.flush
- result = `bin/mrbc -c -o #{out.path} #{a.path} #{b.path} 2>&1`
- assert_equal "bin/mrbc:#{a.path}:Syntax OK", result.chomp
+ result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} #{b.path} 2>&1`
+ assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
assert_equal 0, $?.exitstatus
end
diff --git a/src/codegen.c b/mrbgems/mruby-compiler/core/codegen.c
index 03c752826..c56c1550c 100644
--- a/src/codegen.c
+++ b/mrbgems/mruby-compiler/core/codegen.c
@@ -8,16 +8,16 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/compile.h"
-#include "mruby/proc.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
-#include "mruby/debug.h"
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/proc.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include <mruby/debug.h>
#include "node.h"
-#include "mruby/opcode.h"
-#include "mruby/re.h"
-#include "mrb_throw.h"
+#include <mruby/opcode.h>
+#include <mruby/re.h>
+#include <mruby/throw.h>
typedef mrb_ast_node node;
typedef struct mrb_parser_state parser_state;
@@ -81,10 +81,11 @@ static struct loopinfo *loop_push(codegen_scope *s, enum looptype t);
static void loop_break(codegen_scope *s, node *tree);
static void loop_pop(codegen_scope *s, int val);
-static void gen_assignment(codegen_scope *s, node *node, int sp, int val);
+static void gen_assignment(codegen_scope *s, node *tree, int sp, int val);
static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val);
static void codegen(codegen_scope *s, node *tree, int val);
+static void raise_error(codegen_scope *s, const char *msg);
static void
codegen_error(codegen_scope *s, const char *message)
@@ -95,7 +96,7 @@ codegen_error(codegen_scope *s, const char *message)
mrb_pool_close(s->mpool);
s = tmp;
}
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
if (s->filename && s->lineno) {
fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message);
}
@@ -161,11 +162,19 @@ genop(codegen_scope *s, mrb_code i)
#define NOVAL 0
#define VAL 1
+static mrb_bool
+no_optimize(codegen_scope *s)
+{
+ if (s && s->parser && s->parser->no_optimize)
+ return TRUE;
+ return FALSE;
+}
+
static int
genop_peep(codegen_scope *s, mrb_code i, int val)
{
/* peephole optimization */
- if (s->lastlabel != s->pc && s->pc > 0) {
+ if (!no_optimize(s) && s->lastlabel != s->pc && s->pc > 0) {
mrb_code i0 = s->iseq[s->pc-1];
int c1 = GET_OPCODE(i);
int c0 = GET_OPCODE(i0);
@@ -179,13 +188,18 @@ genop_peep(codegen_scope *s, mrb_code i, int val)
if (val) break;
switch (c0) {
case OP_MOVE:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0) && GETARG_A(i) >= s->nlocals) {
+ if (GETARG_A(i) == GETARG_A(i0)) {
+ /* skip overriden OP_MOVE */
+ s->pc--;
+ s->iseq[s->pc] = i;
+ }
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) {
/* skip swapping OP_MOVE */
return 0;
}
if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0));
- return 0;
+ s->pc--;
+ return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val);
}
break;
case OP_LOADI:
@@ -318,10 +332,9 @@ genop_peep(codegen_scope *s, mrb_code i, int val)
}
case OP_STRCAT:
if (c0 == OP_STRING) {
- int i = GETARG_Bx(i0);
+ mrb_value v = s->irep->pool[GETARG_Bx(i0)];
- if (mrb_type(s->irep->pool[i]) == MRB_TT_STRING &&
- RSTRING_LEN(s->irep->pool[i]) == 0) {
+ if (mrb_string_p(v) && RSTRING_LEN(v) == 0) {
s->pc--;
return 0;
}
@@ -362,7 +375,7 @@ dispatch(codegen_scope *s, int pc)
case OP_ONERR:
break;
default:
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
fprintf(stderr, "bug: dispatch on non JMP op\n");
#endif
scope_error(s);
@@ -398,7 +411,18 @@ push_(codegen_scope *s)
nregs_update;
}
+static void
+push_n_(codegen_scope *s, size_t n)
+{
+ if (s->sp+n > 511) {
+ codegen_error(s, "too complex expression");
+ }
+ s->sp+=n;
+ nregs_update;
+}
+
#define push() push_(s)
+#define push_n(n) push_n_(s,n)
#define pop_(s) ((s)->sp--)
#define pop() pop_(s)
#define pop_n(n) (s->sp-=(n))
@@ -552,6 +576,10 @@ for_body(codegen_scope *s, node *tree)
codegen(s, tree->cdr->car, VAL);
/* generate loop-block */
s = scope_new(s->mrb, s, NULL);
+ if (s == NULL) {
+ raise_error(prev, "unexpected scope");
+ }
+
push(); /* push for a block parameter */
lp = loop_push(s, LOOP_FOR);
@@ -559,12 +587,11 @@ for_body(codegen_scope *s, node *tree)
/* generate loop variable */
n2 = tree->car;
+ genop(s, MKOP_Ax(OP_ENTER, 0x40000));
if (n2->car && !n2->car->cdr && !n2->cdr) {
- genop(s, MKOP_Ax(OP_ENTER, 0x40000));
gen_assignment(s, n2->car->car, 1, NOVAL);
}
else {
- genop(s, MKOP_Ax(OP_ENTER, 0x40000));
gen_vmassignment(s, n2, 1, VAL);
}
codegen(s, tree->cdr->cdr->car, VAL);
@@ -589,6 +616,10 @@ lambda_body(codegen_scope *s, node *tree, int blk)
mrb_code c;
codegen_scope *parent = s;
s = scope_new(s->mrb, s, tree->car);
+ if (s == NULL) {
+ raise_error(parent, "unexpected scope");
+ }
+
s->mscope = !blk;
if (blk) {
@@ -674,6 +705,9 @@ static int
scope_body(codegen_scope *s, node *tree, int val)
{
codegen_scope *scope = scope_new(s->mrb, s, tree->car);
+ if (scope == NULL) {
+ raise_error(s, "unexpected scope");
+ }
codegen(scope, tree->cdr, VAL);
if (!s->iseq) {
@@ -746,10 +780,10 @@ gen_values(codegen_scope *s, node *t, int val)
codegen(s, t->car, VAL);
pop(); pop();
if (is_splat) {
- genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
}
else {
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
}
t = t->cdr;
while (t) {
@@ -825,34 +859,34 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val)
push();pop();
pop_n(n+1);
{
- mrb_int len;
- const char *name = mrb_sym2name_len(s->mrb, sym, &len);
+ mrb_int symlen;
+ const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
- if (!noop && len == 1 && name[0] == '+') {
+ if (!noop && symlen == 1 && symname[0] == '+') {
genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val);
}
- else if (!noop && len == 1 && name[0] == '-') {
+ else if (!noop && symlen == 1 && symname[0] == '-') {
genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val);
}
- else if (!noop && len == 1 && name[0] == '*') {
+ else if (!noop && symlen == 1 && symname[0] == '*') {
genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n));
}
- else if (!noop && len == 1 && name[0] == '/') {
+ else if (!noop && symlen == 1 && symname[0] == '/') {
genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n));
}
- else if (!noop && len == 1 && name[0] == '<') {
+ else if (!noop && symlen == 1 && symname[0] == '<') {
genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
}
- else if (!noop && len == 2 && name[0] == '<' && name[1] == '=') {
+ else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=') {
genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
}
- else if (!noop && len == 1 && name[0] == '>') {
+ else if (!noop && symlen == 1 && symname[0] == '>') {
genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
}
- else if (!noop && len == 2 && name[0] == '>' && name[1] == '=') {
+ else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=') {
genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
}
- else if (!noop && len == 2 && name[0] == '=' && name[1] == '=') {
+ else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=') {
genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
}
else {
@@ -871,19 +905,19 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val)
}
static void
-gen_assignment(codegen_scope *s, node *node, int sp, int val)
+gen_assignment(codegen_scope *s, node *tree, int sp, int val)
{
int idx;
- int type = (intptr_t)node->car;
+ int type = (intptr_t)tree->car;
- node = node->cdr;
+ tree = tree->cdr;
switch ((intptr_t)type) {
case NODE_GVAR:
- idx = new_sym(s, sym(node));
+ idx = new_sym(s, sym(tree));
genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
break;
case NODE_LVAR:
- idx = lv_idx(s, sym(node));
+ idx = lv_idx(s, sym(tree));
if (idx > 0) {
if (idx != sp) {
genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
@@ -895,7 +929,7 @@ gen_assignment(codegen_scope *s, node *node, int sp, int val)
codegen_scope *up = s->prev;
while (up) {
- idx = lv_idx(up, sym(node));
+ idx = lv_idx(up, sym(tree));
if (idx > 0) {
genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
break;
@@ -906,37 +940,45 @@ gen_assignment(codegen_scope *s, node *node, int sp, int val)
}
break;
case NODE_IVAR:
- idx = new_sym(s, sym(node));
+ idx = new_sym(s, sym(tree));
genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
break;
case NODE_CVAR:
- idx = new_sym(s, sym(node));
+ idx = new_sym(s, sym(tree));
genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
break;
case NODE_CONST:
- idx = new_sym(s, sym(node));
+ idx = new_sym(s, sym(tree));
genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
break;
case NODE_COLON2:
- idx = new_sym(s, sym(node->cdr));
+ idx = new_sym(s, sym(tree->cdr));
genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
push();
- codegen(s, node->car, VAL);
+ codegen(s, tree->car, VAL);
pop_n(2);
genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val);
break;
case NODE_CALL:
push();
- gen_call(s, node, attrsym(s, sym(node->cdr->car)), sp, NOVAL);
+ gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL);
pop();
if (val) {
genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
}
break;
+ case NODE_MASGN:
+ gen_vmassignment(s, tree->car, sp, val);
+ break;
+
+ /* splat without assignment */
+ case NODE_NIL:
+ break;
+
default:
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
printf("unknown lhs %d\n", type);
#endif
break;
@@ -971,9 +1013,12 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
}
if (val) {
genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
- push();
}
- pop();
+ else {
+ pop();
+ }
+ push_n(post);
+ pop_n(post);
genop(s, MKOP_ABC(OP_APOST, cursp(), n, post));
n = 1;
if (t->car) { /* rest */
@@ -987,9 +1032,7 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
n++;
}
}
- }
- else {
- pop();
+ push();
}
}
@@ -1095,6 +1138,7 @@ readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_boo
mrb_int result = 0;
int n;
+ mrb_assert(base >= 2 && base <= 36);
if (*p == '+') p++;
while (p < e) {
char c = *p;
@@ -1108,23 +1152,21 @@ readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_boo
codegen_error(s, "malformed readint input");
}
- if(base > 0) {
- if (neg) {
- if ((MRB_INT_MIN + n)/base > result) {
- *overflow = TRUE;
- return 0;
- }
- result *= base;
- result -= n;
+ if (neg) {
+ if ((MRB_INT_MIN + n)/base > result) {
+ *overflow = TRUE;
+ return 0;
}
- else {
- if ((MRB_INT_MAX - n)/base < result) {
- *overflow = TRUE;
- return 0;
- }
- result *= base;
- result += n;
+ result *= base;
+ result -= n;
+ }
+ else {
+ if ((MRB_INT_MAX - n)/base < result) {
+ *overflow = TRUE;
+ return 0;
}
+ result *= base;
+ result += n;
}
p++;
}
@@ -1139,7 +1181,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (!tree) return;
- if (s->irep && s->pc > 0 && s->filename_index != tree->filename_index) {
+ if (s->irep && s->filename_index != tree->filename_index) {
s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc);
s->debug_start_pos = s->pc;
@@ -1288,6 +1330,17 @@ codegen(codegen_scope *s, node *tree, int val)
int pos1, pos2;
node *e = tree->cdr->cdr->car;
+ switch ((intptr_t)tree->car->car) {
+ case NODE_TRUE:
+ case NODE_INT:
+ case NODE_STR:
+ codegen(s, tree->cdr->car, val);
+ return;
+ case NODE_FALSE:
+ case NODE_NIL:
+ codegen(s, e, val);
+ return;
+ }
codegen(s, tree->car, VAL);
pop();
pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL);
@@ -1430,12 +1483,17 @@ codegen(codegen_scope *s, node *tree, int val)
int pos = cursp();
genop(s, MKOP_A(OP_LOADNIL, cursp()));
if (pos3) dispatch_linked(s, pos3);
- pop();
+ if (head) pop();
genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
push();
}
- else if (pos3) {
- dispatch_linked(s, pos3);
+ else {
+ if (pos3) {
+ dispatch_linked(s, pos3);
+ }
+ if (head) {
+ pop();
+ }
}
}
break;
@@ -1586,8 +1644,14 @@ codegen(codegen_scope *s, node *tree, int val)
}
}
if (t->car) { /* rest (len - pre - post) */
- int rn = len - post - n;
+ int rn;
+ if (len < post + n) {
+ rn = 0;
+ }
+ else {
+ rn = len - post - n;
+ }
genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn));
gen_assignment(s, t->car, cursp(), NOVAL);
n += rn;
@@ -1611,6 +1675,9 @@ codegen(codegen_scope *s, node *tree, int val)
/* variable rhs */
codegen(s, t, VAL);
gen_vmassignment(s, tree->car, rhs, val);
+ if (!val) {
+ pop();
+ }
}
}
break;
@@ -1873,7 +1940,7 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_GVAR:
- {
+ if (val) {
int sym = new_sym(s, sym(tree));
genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
@@ -1882,7 +1949,7 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_IVAR:
- {
+ if (val) {
int sym = new_sym(s, sym(tree));
genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
@@ -1891,7 +1958,7 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_CVAR:
- {
+ if (val) {
int sym = new_sym(s, sym(tree));
genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
@@ -1904,7 +1971,9 @@ codegen(codegen_scope *s, node *tree, int val)
int sym = new_sym(s, sym(tree));
genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
- push();
+ if (val) {
+ push();
+ }
}
break;
@@ -1913,7 +1982,7 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_BACK_REF:
- {
+ if (val) {
char buf[2] = { '$' };
mrb_value str;
int sym;
@@ -1927,7 +1996,7 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_NTH_REF:
- {
+ if (val) {
int sym;
mrb_state *mrb = s->mrb;
mrb_value fix = mrb_fixnum_value((intptr_t)tree);
@@ -2069,6 +2138,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (val) {
node *n = tree;
+ if (!n) break;
codegen(s, n->car, VAL);
n = n->cdr;
while (n) {
@@ -2157,7 +2227,8 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_REGX:
if (val) {
char *p1 = (char*)tree->car;
- char *p2 = (char*)tree->cdr;
+ char *p2 = (char*)tree->cdr->car;
+ char *p3 = (char*)tree->cdr->cdr;
int ai = mrb_gc_arena_save(s->mrb);
int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1));
@@ -2167,11 +2238,22 @@ codegen(codegen_scope *s, node *tree, int val)
genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
push();
genop(s, MKOP_ABx(OP_STRING, cursp(), off));
- if (p2) {
+ if (p2 || p3) {
push();
- off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ if (p2) {
+ off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ } else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
argc++;
+ if (p3) {
+ push();
+ off = new_lit(s, mrb_str_new(s->mrb, p3, 1));
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ argc++;
+ pop();
+ }
pop();
}
pop();
@@ -2214,7 +2296,6 @@ codegen(codegen_scope *s, node *tree, int val)
}
if (n->cdr) {
char *p2 = (char*)n->cdr;
- int off;
push();
off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
@@ -2288,7 +2369,7 @@ codegen(codegen_scope *s, node *tree, int val)
{
int a = new_msym(s, sym(tree->car));
int b = new_msym(s, sym(tree->cdr));
- int c = new_msym(s, mrb_intern_lit(s->mrb,"alias_method"));
+ int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method"));
genop(s, MKOP_A(OP_TCLASS, cursp()));
push();
@@ -2471,7 +2552,7 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
mrb_pool *pool = mrb_pool_open(mrb);
codegen_scope *p = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope));
- if (!p) return 0;
+ if (!p) return NULL;
*p = codegen_scope_zero;
p->mrb = mrb;
p->mpool = pool;
@@ -2612,13 +2693,17 @@ loop_break(codegen_scope *s, node *tree)
}
loop = s->loop;
- while (loop->type == LOOP_BEGIN) {
+ while (loop && loop->type == LOOP_BEGIN) {
genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL);
loop = loop->prev;
}
- while (loop->type == LOOP_RESCUE) {
+ while (loop && loop->type == LOOP_RESCUE) {
loop = loop->prev;
}
+ if (!loop) {
+ codegen_error(s, "unexpected break");
+ }
+
if (loop->type == LOOP_NORMAL) {
int tmp;
@@ -2648,454 +2733,7 @@ loop_pop(codegen_scope *s, int val)
if (val) push();
}
-#ifdef ENABLE_STDIO
-static int
-print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre)
-{
- size_t i;
-
- if (n == 0) return 0;
-
- for (i=0; i+1<irep->nlocals; i++) {
- if (irep->lv[i].r == n) {
- mrb_sym sym = irep->lv[i].name;
- if (pre) printf(" ");
- printf("R%d:%s", (int)n, mrb_sym2name(mrb, sym));
- return 1;
- }
- }
- return 0;
-}
-
-#define RA 1
-#define RB 2
-#define RAB 3
-
-static void
-print_lv(mrb_state *mrb, mrb_irep *irep, mrb_code c, int r)
-{
- int pre = 0;
-
- if (!irep->lv
- || ((!(r & RA) || GETARG_A(c) >= irep->nlocals)
- && (!(r & RB) || GETARG_B(c) >= irep->nlocals))) {
- printf("\n");
- return;
- }
- printf("\t; ");
- if (r & RA) {
- pre = print_r(mrb, irep, GETARG_A(c), 0);
- }
- if (r & RB) {
- print_r(mrb, irep, GETARG_B(c), pre);
- }
- printf("\n");
-}
-#endif
-
-static void
-codedump(mrb_state *mrb, mrb_irep *irep)
-{
-#ifdef ENABLE_STDIO
- int i;
- int ai;
- mrb_code c;
- const char *file = NULL, *next_file;
- int32_t line;
-
- if (!irep) return;
- printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", irep,
- irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen);
-
- for (i = 0; i < (int)irep->ilen; i++) {
- ai = mrb_gc_arena_save(mrb);
-
- next_file = mrb_debug_get_filename(irep, i);
- if (next_file && file != next_file) {
- printf("file: %s\n", next_file);
- file = next_file;
- }
- line = mrb_debug_get_line(irep, i);
- if (line < 0) {
- printf(" ");
- }
- else {
- printf("%5d ", line);
- }
-
- printf("%03d ", i);
- c = irep->iseq[i];
- switch (GET_OPCODE(c)) {
- case OP_NOP:
- printf("OP_NOP\n");
- break;
- case OP_MOVE:
- printf("OP_MOVE\tR%d\tR%d", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_LOADL:
- {
- mrb_value v = irep->pool[GETARG_Bx(c)];
- mrb_value s = mrb_inspect(mrb, v);
- printf("OP_LOADL\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s));
- }
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADI:
- printf("OP_LOADI\tR%d\t%d", GETARG_A(c), GETARG_sBx(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADSYM:
- printf("OP_LOADSYM\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADNIL:
- printf("OP_LOADNIL\tR%d\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADSELF:
- printf("OP_LOADSELF\tR%d\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADT:
- printf("OP_LOADT\tR%d\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_LOADF:
- printf("OP_LOADF\tR%d\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETGLOBAL:
- printf("OP_GETGLOBAL\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SETGLOBAL:
- printf("OP_SETGLOBAL\t:%s\tR%d",
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
- GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETCONST:
- printf("OP_GETCONST\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SETCONST:
- printf("OP_SETCONST\t:%s\tR%d",
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
- GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETMCNST:
- printf("OP_GETMCNST\tR%d\tR%d::%s", GETARG_A(c), GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_SETMCNST:
- printf("OP_SETMCNST\tR%d::%s\tR%d", GETARG_A(c)+1,
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
- GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETIV:
- printf("OP_GETIV\tR%d\t%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SETIV:
- printf("OP_SETIV\t%s\tR%d",
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
- GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETUPVAR:
- printf("OP_GETUPVAR\tR%d\t%d\t%d",
- GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SETUPVAR:
- printf("OP_SETUPVAR\tR%d\t%d\t%d",
- GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_GETCV:
- printf("OP_GETCV\tR%d\t%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SETCV:
- printf("OP_SETCV\t%s\tR%d",
- mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
- GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_JMP:
- printf("OP_JMP\t\t%03d\n", i+GETARG_sBx(c));
- break;
- case OP_JMPIF:
- printf("OP_JMPIF\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
- break;
- case OP_JMPNOT:
- printf("OP_JMPNOT\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
- break;
- case OP_SEND:
- printf("OP_SEND\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_SENDB:
- printf("OP_SENDB\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_TAILCALL:
- printf("OP_TAILCALL\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_SUPER:
- printf("OP_SUPER\tR%d\t%d\n", GETARG_A(c),
- GETARG_C(c));
- break;
- case OP_ARGARY:
- printf("OP_ARGARY\tR%d\t%d:%d:%d:%d", GETARG_A(c),
- (GETARG_Bx(c)>>10)&0x3f,
- (GETARG_Bx(c)>>9)&0x1,
- (GETARG_Bx(c)>>4)&0x1f,
- (GETARG_Bx(c)>>0)&0xf);
- print_lv(mrb, irep, c, RA);
- break;
-
- case OP_ENTER:
- printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n",
- (GETARG_Ax(c)>>18)&0x1f,
- (GETARG_Ax(c)>>13)&0x1f,
- (GETARG_Ax(c)>>12)&0x1,
- (GETARG_Ax(c)>>7)&0x1f,
- (GETARG_Ax(c)>>2)&0x1f,
- (GETARG_Ax(c)>>1)&0x1,
- GETARG_Ax(c) & 0x1);
- break;
- case OP_RETURN:
- printf("OP_RETURN\tR%d", GETARG_A(c));
- switch (GETARG_B(c)) {
- case OP_R_NORMAL:
- case OP_R_RETURN:
- printf("\treturn"); break;
- case OP_R_BREAK:
- printf("\tbreak"); break;
- default:
- printf("\tbroken"); break;
- break;
- }
- print_lv(mrb, irep, c, RA);
- break;
- case OP_BLKPUSH:
- printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d", GETARG_A(c),
- (GETARG_Bx(c)>>10)&0x3f,
- (GETARG_Bx(c)>>9)&0x1,
- (GETARG_Bx(c)>>4)&0x1f,
- (GETARG_Bx(c)>>0)&0xf);
- print_lv(mrb, irep, c, RA);
- break;
-
- case OP_LAMBDA:
- printf("OP_LAMBDA\tR%d\tI(%+d)\t%d", GETARG_A(c), GETARG_b(c)+1, GETARG_c(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_RANGE:
- printf("OP_RANGE\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_METHOD:
- printf("OP_METHOD\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
- print_lv(mrb, irep, c, RA);
- break;
-
- case OP_ADD:
- printf("OP_ADD\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_ADDI:
- printf("OP_ADDI\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_SUB:
- printf("OP_SUB\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_SUBI:
- printf("OP_SUBI\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_MUL:
- printf("OP_MUL\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_DIV:
- printf("OP_DIV\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_LT:
- printf("OP_LT\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_LE:
- printf("OP_LE\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_GT:
- printf("OP_GT\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_GE:
- printf("OP_GE\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
- case OP_EQ:
- printf("OP_EQ\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
- break;
-
- case OP_STOP:
- printf("OP_STOP\n");
- break;
-
- case OP_ARRAY:
- printf("OP_ARRAY\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_ARYCAT:
- printf("OP_ARYCAT\tR%d\tR%d", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_ARYPUSH:
- printf("OP_ARYPUSH\tR%d\tR%d", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_AREF:
- printf("OP_AREF\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_APOST:
- printf("OP_APOST\tR%d\t%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_STRING:
- {
- mrb_value v = irep->pool[GETARG_Bx(c)];
- mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v)));
- printf("OP_STRING\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s));
- }
- print_lv(mrb, irep, c, RA);
- break;
- case OP_STRCAT:
- printf("OP_STRCAT\tR%d\tR%d", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_HASH:
- printf("OP_HASH\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
- print_lv(mrb, irep, c, RAB);
- break;
-
- case OP_OCLASS:
- printf("OP_OCLASS\tR%d", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_CLASS:
- printf("OP_CLASS\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_MODULE:
- printf("OP_MODULE\tR%d\t:%s", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_EXEC:
- printf("OP_EXEC\tR%d\tI(%+d)", GETARG_A(c), GETARG_Bx(c)+1);
- print_lv(mrb, irep, c, RA);
- break;
- case OP_SCLASS:
- printf("OP_SCLASS\tR%d\tR%d", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
- break;
- case OP_TCLASS:
- printf("OP_TCLASS\tR%d", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_ERR:
- {
- mrb_value v = irep->pool[GETARG_Bx(c)];
- mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v)));
- printf("OP_ERR\t%s\n", RSTRING_PTR(s));
- }
- break;
- case OP_EPUSH:
- printf("OP_EPUSH\t:I(%+d)\n", GETARG_Bx(c)+1);
- break;
- case OP_ONERR:
- printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c));
- break;
- case OP_RESCUE:
- printf("OP_RESCUE\tR%d", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_RAISE:
- printf("OP_RAISE\tR%d", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_POPERR:
- printf("OP_POPERR\t%d", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
- break;
- case OP_EPOP:
- printf("OP_EPOP\t%d\n", GETARG_A(c));
- break;
-
- default:
- printf("OP_unknown %d\t%d\t%d\t%d\n", GET_OPCODE(c),
- GETARG_A(c), GETARG_B(c), GETARG_C(c));
- break;
- }
- mrb_gc_arena_restore(mrb, ai);
- }
- printf("\n");
-#endif
-}
-
-static void
-codedump_recur(mrb_state *mrb, mrb_irep *irep)
-{
- size_t i;
-
- codedump(mrb, irep);
- for (i=0; i<irep->rlen; i++) {
- codedump_recur(mrb, irep->reps[i]);
- }
-}
-
-void
-mrb_codedump_all(mrb_state *mrb, struct RProc *proc)
-{
- codedump_recur(mrb, proc->body.irep);
-}
-
-struct RProc*
+MRB_API struct RProc*
mrb_generate_code(mrb_state *mrb, parser_state *p)
{
codegen_scope *scope = scope_new(mrb, 0, 0);
diff --git a/src/keywords b/mrbgems/mruby-compiler/core/keywords
index 9cb86608c..9cb86608c 100644
--- a/src/keywords
+++ b/mrbgems/mruby-compiler/core/keywords
diff --git a/src/lex.def b/mrbgems/mruby-compiler/core/lex.def
index ea456a843..58e302965 100644
--- a/src/lex.def
+++ b/mrbgems/mruby-compiler/core/lex.def
@@ -1,5 +1,5 @@
-/* ANSI-C code produced by gperf version 3.0.3 */
-/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' src/keywords */
+/* ANSI-C code produced by gperf version 3.0.4 */
+/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' /home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
@@ -28,13 +28,13 @@
#error "gperf generated tables don't work with this execution character set. Please report a bug to <[email protected]>."
#endif
-#line 1 "src/keywords"
+#line 1 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;};
const struct kwtable *mrb_reserved_word(const char *, unsigned int);
static const struct kwtable *reserved_word(const char *, unsigned int);
#define mrb_reserved_word(str, len) reserved_word(str, len)
-#line 8 "src/keywords"
+#line 8 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
struct kwtable;
#define TOTAL_KEYWORDS 40
@@ -100,7 +100,7 @@ hash (register const char *str, register unsigned int len)
#ifdef __GNUC__
__inline
-#ifdef __GNUC_STDC_INLINE__
+#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__
__attribute__ ((__gnu_inline__))
#endif
#endif
@@ -110,87 +110,87 @@ mrb_reserved_word (register const char *str, register unsigned int len)
static const struct kwtable wordlist[] =
{
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
-#line 18 "src/keywords"
+#line 18 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"break", {keyword_break, keyword_break}, EXPR_MID},
-#line 23 "src/keywords"
+#line 23 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"else", {keyword_else, keyword_else}, EXPR_BEG},
-#line 33 "src/keywords"
+#line 33 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"nil", {keyword_nil, keyword_nil}, EXPR_END},
-#line 26 "src/keywords"
+#line 26 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG},
-#line 25 "src/keywords"
+#line 25 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"end", {keyword_end, keyword_end}, EXPR_END},
-#line 42 "src/keywords"
+#line 42 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"then", {keyword_then, keyword_then}, EXPR_BEG},
-#line 34 "src/keywords"
+#line 34 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"not", {keyword_not, keyword_not}, EXPR_ARG},
-#line 27 "src/keywords"
+#line 27 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"false", {keyword_false, keyword_false}, EXPR_END},
-#line 40 "src/keywords"
+#line 40 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"self", {keyword_self, keyword_self}, EXPR_END},
-#line 24 "src/keywords"
+#line 24 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE},
-#line 37 "src/keywords"
+#line 37 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID},
-#line 43 "src/keywords"
+#line 43 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"true", {keyword_true, keyword_true}, EXPR_END},
-#line 46 "src/keywords"
+#line 46 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"until", {keyword_until, modifier_until}, EXPR_VALUE},
-#line 45 "src/keywords"
+#line 45 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"unless", {keyword_unless, modifier_unless}, EXPR_VALUE},
-#line 39 "src/keywords"
+#line 39 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"return", {keyword_return, keyword_return}, EXPR_MID},
-#line 21 "src/keywords"
+#line 21 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"def", {keyword_def, keyword_def}, EXPR_FNAME},
-#line 16 "src/keywords"
+#line 16 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"and", {keyword_and, keyword_and}, EXPR_VALUE},
-#line 22 "src/keywords"
+#line 22 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"do", {keyword_do, keyword_do}, EXPR_BEG},
-#line 49 "src/keywords"
+#line 49 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"yield", {keyword_yield, keyword_yield}, EXPR_ARG},
-#line 28 "src/keywords"
+#line 28 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"for", {keyword_for, keyword_for}, EXPR_VALUE},
-#line 44 "src/keywords"
+#line 44 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"undef", {keyword_undef, keyword_undef}, EXPR_FNAME},
-#line 35 "src/keywords"
+#line 35 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"or", {keyword_or, keyword_or}, EXPR_VALUE},
-#line 30 "src/keywords"
+#line 30 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"in", {keyword_in, keyword_in}, EXPR_VALUE},
-#line 47 "src/keywords"
+#line 47 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"when", {keyword_when, keyword_when}, EXPR_VALUE},
-#line 38 "src/keywords"
+#line 38 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"retry", {keyword_retry, keyword_retry}, EXPR_END},
-#line 29 "src/keywords"
+#line 29 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"if", {keyword_if, modifier_if}, EXPR_VALUE},
-#line 19 "src/keywords"
+#line 19 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"case", {keyword_case, keyword_case}, EXPR_VALUE},
-#line 36 "src/keywords"
+#line 36 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"redo", {keyword_redo, keyword_redo}, EXPR_END},
-#line 32 "src/keywords"
+#line 32 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"next", {keyword_next, keyword_next}, EXPR_MID},
-#line 41 "src/keywords"
+#line 41 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"super", {keyword_super, keyword_super}, EXPR_ARG},
-#line 31 "src/keywords"
+#line 31 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"module", {keyword_module, keyword_module}, EXPR_VALUE},
-#line 17 "src/keywords"
+#line 17 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"begin", {keyword_begin, keyword_begin}, EXPR_BEG},
-#line 12 "src/keywords"
+#line 12 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END},
-#line 11 "src/keywords"
+#line 11 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END},
-#line 10 "src/keywords"
+#line 10 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END},
-#line 14 "src/keywords"
+#line 14 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"END", {keyword_END, keyword_END}, EXPR_END},
-#line 15 "src/keywords"
+#line 15 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"alias", {keyword_alias, keyword_alias}, EXPR_FNAME},
-#line 13 "src/keywords"
+#line 13 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END},
{""},
-#line 20 "src/keywords"
+#line 20 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"class", {keyword_class, keyword_class}, EXPR_CLASS},
{""}, {""},
-#line 48 "src/keywords"
+#line 48 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
{"while", {keyword_while, modifier_while}, EXPR_VALUE}
};
@@ -208,5 +208,5 @@ mrb_reserved_word (register const char *str, register unsigned int len)
}
return 0;
}
-#line 50 "src/keywords"
+#line 50 "/home/matz/work/mruby/mrbgems/mruby-compiler/core/keywords"
diff --git a/src/node.h b/mrbgems/mruby-compiler/core/node.h
index 532a8323a..4f3669aca 100644
--- a/src/node.h
+++ b/mrbgems/mruby-compiler/core/node.h
@@ -4,8 +4,8 @@
** See Copyright Notice in mruby.h
*/
-#ifndef NODE_H
-#define NODE_H
+#ifndef MRUBY_COMPILER_NODE_H
+#define MRUBY_COMPILER_NODE_H
enum node_type {
NODE_METHOD,
@@ -114,4 +114,4 @@ enum node_type {
NODE_LAST
};
-#endif /* NODE_H */
+#endif /* MRUBY_COMPILER_NODE_H */
diff --git a/src/parse.y b/mrbgems/mruby-compiler/core/parse.y
index 2c7e788d9..b08775cc7 100644
--- a/src/parse.y
+++ b/mrbgems/mruby-compiler/core/parse.y
@@ -6,8 +6,9 @@
%{
#undef PARSER_DEBUG
-
-#define YYDEBUG 1
+#ifdef PARSER_DEBUG
+# define YYDEBUG 1
+#endif
#define YYERROR_VERBOSE 1
/*
* Force yacc to use our memory management. This is a little evil because
@@ -21,12 +22,12 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/compile.h"
-#include "mruby/proc.h"
-#include "mruby/error.h"
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/proc.h>
+#include <mruby/error.h>
+#include <mruby/throw.h>
#include "node.h"
-#include "mrb_throw.h"
#define YYLEX_PARAM p
@@ -62,6 +63,12 @@ typedef unsigned int stack_type;
#define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack)
#define SET_LINENO(c,n) ((c)->lineno = (n))
+#define NODE_LINENO(c,n) do {\
+ if (n) {\
+ (c)->filename_index = (n)->filename_index;\
+ (c)->lineno = (n)->lineno;\
+ }\
+} while (0)
#define sym(x) ((mrb_sym)(intptr_t)(x))
#define nsym(x) ((node*)(intptr_t)(x))
@@ -195,6 +202,7 @@ parser_strndup(parser_state *p, const char *s, size_t len)
b[len] = '\0';
return b;
}
+#undef strndup
#define strndup(s,len) parser_strndup(p, s, len)
static char*
@@ -231,10 +239,12 @@ local_nest(parser_state *p)
static void
local_unnest(parser_state *p)
{
- p->locals = p->locals->cdr;
+ if (p->locals) {
+ p->locals = p->locals->cdr;
+ }
}
-static int
+static mrb_bool
local_var_p(parser_state *p, mrb_sym sym)
{
node *l = p->locals;
@@ -242,20 +252,18 @@ local_var_p(parser_state *p, mrb_sym sym)
while (l) {
node *n = l->car;
while (n) {
- if (sym(n->car) == sym) return 1;
+ if (sym(n->car) == sym) return TRUE;
n = n->cdr;
}
l = l->cdr;
}
- return 0;
+ return FALSE;
}
static void
local_add_f(parser_state *p, mrb_sym sym)
{
- if (p->locals->car && !p->locals->car->car) {
- p->locals->car->car = nsym(sym);
- } else {
+ if (p->locals) {
p->locals->car = push(p->locals->car, nsym(sym));
}
}
@@ -268,19 +276,26 @@ local_add(parser_state *p, mrb_sym sym)
}
}
+static node*
+locals_node(parser_state *p)
+{
+ return p->locals ? p->locals->car : NULL;
+}
+
/* (:scope (vars..) (prog...)) */
static node*
new_scope(parser_state *p, node *body)
{
- return cons((node*)NODE_SCOPE, cons(p->locals->car, body));
+ return cons((node*)NODE_SCOPE, cons(locals_node(p), body));
}
/* (:begin prog...) */
static node*
new_begin(parser_state *p, node *body)
{
- if (body)
+ if (body) {
return list2((node*)NODE_BEGIN, body);
+ }
return cons((node*)NODE_BEGIN, 0);
}
@@ -395,14 +410,20 @@ new_self(parser_state *p)
static node*
new_call(parser_state *p, node *a, mrb_sym b, node *c)
{
- return list4((node*)NODE_CALL, a, nsym(b), c);
+ node *n = list4((node*)NODE_CALL, a, nsym(b), c);
+ NODE_LINENO(n, a);
+ return n;
}
/* (:fcall self mid args) */
static node*
new_fcall(parser_state *p, mrb_sym b, node *c)
{
- return list4((node*)NODE_FCALL, new_self(p), nsym(b), c);
+ node *n = new_self(p);
+ NODE_LINENO(n, c);
+ n = list4((node*)NODE_FCALL, n, nsym(b), c);
+ NODE_LINENO(n, c);
+ return n;
}
/* (:super . c) */
@@ -592,35 +613,35 @@ new_undef(parser_state *p, mrb_sym sym)
static node*
new_class(parser_state *p, node *c, node *s, node *b)
{
- return list4((node*)NODE_CLASS, c, s, cons(p->locals->car, b));
+ return list4((node*)NODE_CLASS, c, s, cons(locals_node(p), b));
}
/* (:sclass obj body) */
static node*
new_sclass(parser_state *p, node *o, node *b)
{
- return list3((node*)NODE_SCLASS, o, cons(p->locals->car, b));
+ return list3((node*)NODE_SCLASS, o, cons(locals_node(p), b));
}
/* (:module module body) */
static node*
new_module(parser_state *p, node *m, node *b)
{
- return list3((node*)NODE_MODULE, m, cons(p->locals->car, b));
+ return list3((node*)NODE_MODULE, m, cons(locals_node(p), b));
}
/* (:def m lv (arg . body)) */
static node*
new_def(parser_state *p, mrb_sym m, node *a, node *b)
{
- return list5((node*)NODE_DEF, nsym(m), p->locals->car, a, b);
+ return list5((node*)NODE_DEF, nsym(m), locals_node(p), a, b);
}
/* (:sdef obj m lv (arg . body)) */
static node*
new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b)
{
- return list6((node*)NODE_SDEF, o, nsym(m), p->locals->car, a, b);
+ return list6((node*)NODE_SDEF, o, nsym(m), locals_node(p), a, b);
}
/* (:arg . sym) */
@@ -658,14 +679,14 @@ new_block_arg(parser_state *p, node *a)
static node*
new_block(parser_state *p, node *a, node *b)
{
- return list4((node*)NODE_BLOCK, p->locals->car, a, b);
+ return list4((node*)NODE_BLOCK, locals_node(p), a, b);
}
/* (:lambda arg body) */
static node*
new_lambda(parser_state *p, node *a, node *b)
{
- return list4((node*)NODE_LAMBDA, p->locals->car, a, b);
+ return list4((node*)NODE_LAMBDA, locals_node(p), a, b);
}
/* (:asgn lhs rhs) */
@@ -740,9 +761,9 @@ new_dsym(parser_state *p, node *a)
/* (:str . (a . a)) */
static node*
-new_regx(parser_state *p, const char *p1, const char* p2)
+new_regx(parser_state *p, const char *p1, const char* p2, const char* p3)
{
- return cons((node*)NODE_REGX, cons((node*)p1, (node*)p2));
+ return cons((node*)NODE_REGX, cons((node*)p1, cons((node*)p2, (node*)p3)));
}
/* (:dregx . a) */
@@ -831,19 +852,24 @@ call_with_block(parser_state *p, node *a, node *b)
{
node *n;
- if (a->car == (node*)NODE_SUPER ||
- a->car == (node*)NODE_ZSUPER) {
+ switch ((enum node_type)(intptr_t)a->car) {
+ case NODE_SUPER:
+ case NODE_ZSUPER:
if (!a->cdr) a->cdr = cons(0, b);
else {
args_with_block(p, a->cdr, b);
}
- }
- else {
+ break;
+ case NODE_CALL:
+ case NODE_FCALL:
n = a->cdr->cdr->cdr;
if (!n->car) n->car = cons(0, b);
else {
args_with_block(p, n->car, b);
}
+ break;
+ default:
+ break;
}
}
@@ -1001,25 +1027,27 @@ heredoc_end(parser_state *p)
const struct vtable *vars;
}
-%token
+%token <num>
keyword_class
keyword_module
keyword_def
- keyword_undef
keyword_begin
+ keyword_if
+ keyword_unless
+ keyword_while
+ keyword_until
+ keyword_for
+
+%token
+ keyword_undef
keyword_rescue
keyword_ensure
keyword_end
- keyword_if
- keyword_unless
keyword_then
keyword_elsif
keyword_else
keyword_case
keyword_when
- keyword_while
- keyword_until
- keyword_for
keyword_break
keyword_next
keyword_redo
@@ -1138,17 +1166,6 @@ heredoc_end(parser_state *p)
%right tPOW
%right '!' '~' tUPLUS
-%nonassoc idNULL
-%nonassoc idRespond_to
-%nonassoc idIFUNC
-%nonassoc idCFUNC
-%nonassoc id_core_set_method_alias
-%nonassoc id_core_set_variable_alias
-%nonassoc id_core_undef_method
-%nonassoc id_core_define_method
-%nonassoc id_core_define_singleton_method
-%nonassoc id_core_set_postexe
-
%token tLAST_TOKEN
%%
@@ -1159,6 +1176,7 @@ program : {
top_compstmt
{
p->tree = new_scope(p, $2);
+ NODE_LINENO(p->tree, $2);
}
;
@@ -1175,6 +1193,7 @@ top_stmts : none
| top_stmt
{
$$ = new_begin(p, $1);
+ NODE_LINENO($$, $1);
}
| top_stmts terms top_stmt
{
@@ -1206,6 +1225,7 @@ bodystmt : compstmt
{
if ($2) {
$$ = new_rescue(p, $1, $2, $3);
+ NODE_LINENO($$, $1);
}
else if ($3) {
yywarn(p, "else without rescue is useless");
@@ -1238,10 +1258,11 @@ stmts : none
| stmt
{
$$ = new_begin(p, $1);
+ NODE_LINENO($$, $1);
}
| stmts terms stmt
{
- $$ = push($1, newline_node($3));
+ $$ = push($1, newline_node($3));
}
| error stmt
{
@@ -1259,7 +1280,7 @@ stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym
}
| stmt modifier_if expr_value
{
- $$ = new_if(p, cond($3), $1, 0);
+ $$ = new_if(p, cond($3), $1, 0);
}
| stmt modifier_unless expr_value
{
@@ -1453,7 +1474,7 @@ mlhs : mlhs_basic
mlhs_inner : mlhs_basic
| tLPAREN mlhs_inner rparen
{
- $$ = list1($2);
+ $$ = $2;
}
;
@@ -1502,7 +1523,7 @@ mlhs_basic : mlhs_list
mlhs_item : mlhs_node
| tLPAREN mlhs_inner rparen
{
- $$ = $2;
+ $$ = new_masgn(p, $2, NULL);
}
;
@@ -1891,6 +1912,7 @@ aref_args : none
| args trailer
{
$$ = $1;
+ NODE_LINENO($$, $1);
}
| args ',' assocs trailer
{
@@ -1899,6 +1921,7 @@ aref_args : none
| assocs trailer
{
$$ = cons(new_hash(p, $1), 0);
+ NODE_LINENO($$, $1);
}
;
@@ -1917,36 +1940,44 @@ opt_call_args : none
| args ','
{
$$ = cons($1,0);
+ NODE_LINENO($$, $1);
}
| args ',' assocs ','
{
$$ = cons(push($1, new_hash(p, $3)), 0);
+ NODE_LINENO($$, $1);
}
| assocs ','
{
$$ = cons(list1(new_hash(p, $1)), 0);
+ NODE_LINENO($$, $1);
}
;
call_args : command
{
$$ = cons(list1($1), 0);
+ NODE_LINENO($$, $1);
}
| args opt_block_arg
{
$$ = cons($1, $2);
+ NODE_LINENO($$, $1);
}
| assocs opt_block_arg
{
$$ = cons(list1(new_hash(p, $1)), $2);
+ NODE_LINENO($$, $1);
}
| args ',' assocs opt_block_arg
{
$$ = cons(push($1, new_hash(p, $3)), $4);
+ NODE_LINENO($$, $1);
}
| block_arg
{
$$ = cons(0, $1);
+ NODE_LINENO($$, $1);
}
;
@@ -1980,10 +2011,12 @@ opt_block_arg : ',' block_arg
args : arg_value
{
$$ = cons($1, 0);
+ NODE_LINENO($$, $1);
}
| tSTAR arg_value
{
$$ = cons(new_splat(p, $2), 0);
+ NODE_LINENO($$, $2);
}
| args ',' arg_value
{
@@ -2030,23 +2063,23 @@ primary : literal
}
| keyword_begin
{
- $<stack>1 = p->cmdarg_stack;
+ $<stack>$ = p->cmdarg_stack;
p->cmdarg_stack = 0;
}
bodystmt
keyword_end
{
- p->cmdarg_stack = $<stack>1;
+ p->cmdarg_stack = $<stack>2;
$$ = $3;
}
| tLPAREN_ARG
{
- $<stack>1 = p->cmdarg_stack;
+ $<stack>$ = p->cmdarg_stack;
p->cmdarg_stack = 0;
}
expr {p->lstate = EXPR_ENDARG;} rparen
{
- p->cmdarg_stack = $<stack>1;
+ p->cmdarg_stack = $<stack>2;
$$ = $3;
}
| tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen
@@ -2068,10 +2101,12 @@ primary : literal
| tLBRACK aref_args ']'
{
$$ = new_array(p, $2);
+ NODE_LINENO($$, $2);
}
| tLBRACE assoc_list '}'
{
$$ = new_hash(p, $2);
+ NODE_LINENO($$, $2);
}
| keyword_return
{
@@ -2114,11 +2149,17 @@ primary : literal
p->lpar_beg = ++p->paren_nest;
}
f_larglist
+ {
+ $<stack>$ = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
lambda_body
{
p->lpar_beg = $<num>2;
- $$ = new_lambda(p, $3, $4);
+ $$ = new_lambda(p, $3, $5);
local_unnest(p);
+ p->cmdarg_stack = $<stack>4;
+ CMDARG_LEXPOP();
}
| keyword_if expr_value then
compstmt
@@ -2126,6 +2167,7 @@ primary : literal
keyword_end
{
$$ = new_if(p, cond($2), $4, $5);
+ SET_LINENO($$, $1);
}
| keyword_unless expr_value then
compstmt
@@ -2133,18 +2175,21 @@ primary : literal
keyword_end
{
$$ = new_unless(p, cond($2), $4, $5);
+ SET_LINENO($$, $1);
}
| keyword_while {COND_PUSH(1);} expr_value do {COND_POP();}
compstmt
keyword_end
{
$$ = new_while(p, cond($3), $6);
+ SET_LINENO($$, $1);
}
| keyword_until {COND_PUSH(1);} expr_value do {COND_POP();}
compstmt
keyword_end
{
$$ = new_until(p, cond($3), $6);
+ SET_LINENO($$, $1);
}
| keyword_case expr_value opt_terms
case_body
@@ -2164,11 +2209,9 @@ primary : literal
keyword_end
{
$$ = new_for(p, $2, $5, $8);
+ SET_LINENO($$, $1);
}
| keyword_class
- {
- $<num>$ = p->lineno;
- }
cpath superclass
{
if (p->in_def || p->in_single)
@@ -2178,14 +2221,11 @@ primary : literal
bodystmt
keyword_end
{
- $$ = new_class(p, $3, $4, $6);
- SET_LINENO($$, $<num>2);
- local_resume(p, $<nd>5);
+ $$ = new_class(p, $2, $3, $5);
+ SET_LINENO($$, $1);
+ local_resume(p, $<nd>4);
}
| keyword_class
- {
- $<num>$ = p->lineno;
- }
tLSHFT expr
{
$<num>$ = p->in_def;
@@ -2199,16 +2239,13 @@ primary : literal
bodystmt
keyword_end
{
- $$ = new_sclass(p, $4, $8);
- SET_LINENO($$, $<num>2);
- local_resume(p, $<nd>7->car);
- p->in_def = $<num>5;
- p->in_single = (int)(intptr_t)$<nd>7->cdr;
+ $$ = new_sclass(p, $3, $7);
+ SET_LINENO($$, $1);
+ local_resume(p, $<nd>6->car);
+ p->in_def = $<num>4;
+ p->in_single = (int)(intptr_t)$<nd>6->cdr;
}
| keyword_module
- {
- $<num>$ = p->lineno;
- }
cpath
{
if (p->in_def || p->in_single)
@@ -2218,42 +2255,50 @@ primary : literal
bodystmt
keyword_end
{
- $$ = new_module(p, $3, $5);
- SET_LINENO($$, $<num>2);
- local_resume(p, $<nd>4);
+ $$ = new_module(p, $2, $4);
+ SET_LINENO($$, $1);
+ local_resume(p, $<nd>3);
}
| keyword_def fname
{
+ $<stack>$ = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
+ {
p->in_def++;
$<nd>$ = local_switch(p);
- $<stack>1 = p->cmdarg_stack;
- p->cmdarg_stack = 0;
}
f_arglist
bodystmt
keyword_end
{
- $$ = new_def(p, $2, $4, $5);
- local_resume(p, $<nd>3);
+ $$ = new_def(p, $2, $5, $6);
+ SET_LINENO($$, $1);
+ local_resume(p, $<nd>4);
p->in_def--;
- p->cmdarg_stack = $<stack>1;
+ p->cmdarg_stack = $<stack>3;
}
- | keyword_def singleton dot_or_colon {p->lstate = EXPR_FNAME;} fname
+ | keyword_def singleton dot_or_colon
+ {
+ p->lstate = EXPR_FNAME;
+ $<stack>$ = p->cmdarg_stack;
+ p->cmdarg_stack = 0;
+ }
+ fname
{
p->in_single++;
p->lstate = EXPR_ENDFN; /* force for args */
$<nd>$ = local_switch(p);
- $<stack>1 = p->cmdarg_stack;
- p->cmdarg_stack = 0;
}
f_arglist
bodystmt
keyword_end
{
$$ = new_sdef(p, $2, $5, $7, $8);
+ SET_LINENO($$, $1);
local_resume(p, $<nd>6);
p->in_single--;
- p->cmdarg_stack = $<stack>1;
+ p->cmdarg_stack = $<stack>4;
}
| keyword_break
{
@@ -2442,12 +2487,10 @@ opt_block_param : none
block_param_def : '|' opt_bv_decl '|'
{
- local_add_f(p, 0);
$$ = 0;
}
| tOROP
{
- local_add_f(p, 0);
$$ = 0;
}
| '|' block_param opt_bv_decl '|'
@@ -2904,7 +2947,7 @@ backref : tNTH_REF
| tBACK_REF
;
-superclass : term
+superclass : /* term */
{
$$ = 0;
}
@@ -2916,12 +2959,12 @@ superclass : term
expr_value term
{
$$ = $3;
- }
+ } /*
| error term
{
yyerrok;
$$ = 0;
- }
+ } */
;
f_arglist : '(' f_args rparen
@@ -3169,6 +3212,7 @@ assoc_list : none
assocs : assoc
{
$$ = list1($1);
+ NODE_LINENO($$, $1);
}
| assocs ',' assoc
{
@@ -3255,7 +3299,7 @@ yyerror(parser_state *p, const char *s)
int n;
if (! p->capture_errors) {
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
if (p->filename) {
fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
}
@@ -3291,7 +3335,7 @@ yywarn(parser_state *p, const char *s)
int n;
if (! p->capture_errors) {
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
if (p->filename) {
fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
}
@@ -3362,7 +3406,7 @@ nextc(parser_state *p)
cons_free(tmp);
}
else {
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
if (p->f) {
if (feof(p->f)) goto eof;
c = fgetc(p->f);
@@ -3429,6 +3473,7 @@ peekc_n(parser_state *p, int n)
do {
c0 = nextc(p);
if (c0 == -1) return c0; /* do not skip partial EOF */
+ if (c0 >= 0) --p->column;
list = push(list, (node*)(intptr_t)c0);
} while(n--);
if (p->pb) {
@@ -3452,7 +3497,7 @@ peeks(parser_state *p, const char *s)
{
int len = strlen(s);
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
if (p->f) {
int n = 0;
while (*s) {
@@ -3566,10 +3611,13 @@ toklast(parser_state *p)
static void
tokfix(parser_state *p)
{
- if (p->bidx >= MRB_PARSER_BUF_SIZE) {
+ int i = p->bidx, imax = MRB_PARSER_BUF_SIZE - 1;
+
+ if (i > imax) {
+ i = imax;
yyerror(p, "string too long (truncated)");
}
- p->buf[p->bidx] = '\0';
+ p->buf[i] = '\0';
}
static const char*
@@ -3806,7 +3854,7 @@ parse_string(parser_state *p)
newtok(p);
while ((c = nextc(p)) != end || nest_level != 0) {
if (hinf && (c == '\n' || c < 0)) {
- int line_head;
+ mrb_bool line_head;
tokadd(p, '\n');
tokfix(p);
p->lineno++;
@@ -3940,22 +3988,26 @@ parse_string(parser_state *p)
if (type & STR_FUNC_REGEXP) {
int f = 0;
- int c;
+ int re_opt;
char *s = strndup(tok(p), toklen(p));
char flags[3];
char *flag = flags;
+ char enc = '\0';
+ char *encp;
char *dup;
newtok(p);
- while (c = nextc(p), c >= 0 && ISALPHA(c)) {
- switch (c) {
+ while (re_opt = nextc(p), re_opt >= 0 && ISALPHA(re_opt)) {
+ switch (re_opt) {
case 'i': f |= 1; break;
case 'x': f |= 2; break;
case 'm': f |= 4; break;
- default: tokadd(p, c); break;
+ case 'u': f |= 16; break;
+ case 'n': f |= 32; break;
+ default: tokadd(p, re_opt); break;
}
}
- pushback(p, c);
+ pushback(p, re_opt);
if (toklen(p)) {
char msg[128];
tokfix(p);
@@ -3967,12 +4019,20 @@ parse_string(parser_state *p)
if (f & 1) *flag++ = 'i';
if (f & 2) *flag++ = 'x';
if (f & 4) *flag++ = 'm';
- dup = strndup(flags, (size_t)(flag - flags));
+ if (f & 16) enc = 'u';
+ if (f & 32) enc = 'n';
}
- else {
+ if (flag > flags) {
+ dup = strndup(flags, (size_t)(flag - flags));
+ } else {
dup = NULL;
}
- yylval.nd = new_regx(p, s, dup);
+ if (enc) {
+ encp = strndup(&enc, 1);
+ } else {
+ encp = NULL;
+ }
+ yylval.nd = new_regx(p, s, dup, encp);
return tREGEXP;
}
@@ -4082,10 +4142,9 @@ parser_yylex(parser_state *p)
retry:
last_state = p->lstate;
switch (c = nextc(p)) {
- case '\0': /* NUL */
case '\004': /* ^D */
case '\032': /* ^Z */
- return 0;
+ case '\0': /* NUL */
case -1: /* end of script. */
if (p->heredocs_from_nextline)
goto maybe_heredoc;
@@ -4113,7 +4172,9 @@ parser_yylex(parser_state *p)
p->lineno++;
p->column = 0;
if (p->parsing_heredoc != NULL) {
- return parse_string(p);
+ if (p->lex_strterm) {
+ return parse_string(p);
+ }
}
goto retry;
default:
@@ -4165,7 +4226,7 @@ parser_yylex(parser_state *p)
}
pushback(p, c);
if (IS_SPCARG(c)) {
- yywarning(p, "`*' interpreted as argument prefix");
+ yywarning(p, "'*' interpreted as argument prefix");
c = tSTAR;
}
else if (IS_BEG()) {
@@ -4416,7 +4477,7 @@ parser_yylex(parser_state *p)
}
pushback(p, c);
if (IS_SPCARG(c)) {
- yywarning(p, "`&' interpreted as argument prefix");
+ yywarning(p, "'&' interpreted as argument prefix");
c = tAMPER;
}
else if (IS_BEG()) {
@@ -4722,7 +4783,7 @@ parser_yylex(parser_state *p)
nondigit = c;
break;
- case '_': /* `_' in number just ignored */
+ case '_': /* '_' in number just ignored */
if (nondigit) goto decode_num;
nondigit = c;
break;
@@ -4737,7 +4798,7 @@ parser_yylex(parser_state *p)
pushback(p, c);
if (nondigit) {
trailing_uc:
- yyerror_i(p, "trailing `%c' in number", nondigit);
+ yyerror_i(p, "trailing '%c' in number", nondigit);
}
tokfix(p);
if (is_float) {
@@ -4763,6 +4824,7 @@ parser_yylex(parser_state *p)
case ')':
case ']':
p->paren_nest--;
+ /* fall through */
case '}':
COND_LEXPOP();
CMDARG_LEXPOP();
@@ -5079,7 +5141,14 @@ parser_yylex(parser_state *p)
pushback(p, c);
if (last_state == EXPR_FNAME) goto gvar;
tokfix(p);
- yylval.nd = new_nth_ref(p, atoi(tok(p)));
+ {
+ unsigned long n = strtoul(tok(p), NULL, 10);
+ if (n > INT_MAX) {
+ yyerror_i(p, "capture group index must be <= %d", INT_MAX);
+ return 0;
+ }
+ yylval.nd = new_nth_ref(p, (int)n);
+ }
return tNTH_REF;
default:
@@ -5087,6 +5156,7 @@ parser_yylex(parser_state *p)
pushback(p, c);
return '$';
}
+ /* fall through */
case '0':
tokadd(p, '$');
}
@@ -5111,10 +5181,10 @@ parser_yylex(parser_state *p)
}
else if (isdigit(c)) {
if (p->bidx == 1) {
- yyerror_i(p, "`@%c' is not allowed as an instance variable name", c);
+ yyerror_i(p, "'@%c' is not allowed as an instance variable name", c);
}
else {
- yyerror_i(p, "`@@%c' is not allowed as a class variable name", c);
+ yyerror_i(p, "'@@%c' is not allowed as a class variable name", c);
}
return 0;
}
@@ -5130,7 +5200,7 @@ parser_yylex(parser_state *p)
default:
if (!identchar(c)) {
- yyerror_i(p, "Invalid char `\\x%02X' in expression", c);
+ yyerror_i(p, "Invalid char '\\x%02X' in expression", c);
goto retry;
}
@@ -5216,6 +5286,7 @@ parser_yylex(parser_state *p)
kw = mrb_reserved_word(tok(p), toklen(p));
if (kw) {
enum mrb_lex_state_enum state = p->lstate;
+ yylval.num = p->lineno;
p->lstate = kw->state;
if (state == EXPR_FNAME) {
yylval.id = intern_cstr(kw->name);
@@ -5279,12 +5350,8 @@ parser_yylex(parser_state *p)
static int
yylex(void *lval, parser_state *p)
{
- int t;
-
p->ylval = lval;
- t = parser_yylex(p);
-
- return t;
+ return parser_yylex(p);
}
static void
@@ -5302,6 +5369,7 @@ parser_init_cxt(parser_state *p, mrbc_context *cxt)
}
}
p->capture_errors = cxt->capture_errors;
+ p->no_optimize = cxt->no_optimize;
if (cxt->partial_hook) {
p->cxt = cxt;
}
@@ -5330,7 +5398,7 @@ parser_update_cxt(parser_state *p, mrbc_context *cxt)
void mrb_codedump_all(mrb_state*, struct RProc*);
void mrb_parser_dump(mrb_state *mrb, node *tree, int offset);
-void
+MRB_API void
mrb_parser_parse(parser_state *p, mrbc_context *c)
{
struct mrb_jmpbuf buf;
@@ -5363,7 +5431,7 @@ mrb_parser_parse(parser_state *p, mrbc_context *c)
MRB_END_EXC(p->jmp);
}
-parser_state*
+MRB_API parser_state*
mrb_parser_new(mrb_state *mrb)
{
mrb_pool *pool;
@@ -5371,16 +5439,16 @@ mrb_parser_new(mrb_state *mrb)
static const parser_state parser_state_zero = { 0 };
pool = mrb_pool_open(mrb);
- if (!pool) return 0;
+ if (!pool) return NULL;
p = (parser_state *)mrb_pool_alloc(pool, sizeof(parser_state));
- if (!p) return 0;
+ if (!p) return NULL;
*p = parser_state_zero;
p->mrb = mrb;
p->pool = pool;
p->s = p->send = NULL;
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
p->f = NULL;
#endif
@@ -5405,28 +5473,25 @@ mrb_parser_new(mrb_state *mrb)
return p;
}
-void
+MRB_API void
mrb_parser_free(parser_state *p) {
mrb_pool_close(p->pool);
}
-mrbc_context*
+MRB_API mrbc_context*
mrbc_context_new(mrb_state *mrb)
{
- mrbc_context *c;
-
- c = (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context));
- return c;
+ return (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context));
}
-void
+MRB_API void
mrbc_context_free(mrb_state *mrb, mrbc_context *cxt)
{
mrb_free(mrb, cxt->syms);
mrb_free(mrb, cxt);
}
-const char*
+MRB_API const char*
mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s)
{
if (s) {
@@ -5439,14 +5504,14 @@ mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s)
return c->filename;
}
-void
+MRB_API void
mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*func)(struct mrb_parser_state*), void *data)
{
c->partial_hook = func;
c->partial_data = data;
}
-void
+MRB_API void
mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
{
mrb_sym sym;
@@ -5474,7 +5539,7 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
p->filename_table[p->filename_table_length - 1] = sym;
}
-char const*
+MRB_API char const*
mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) {
if (idx >= p->filename_table_length) { return NULL; }
else {
@@ -5482,14 +5547,14 @@ mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) {
}
}
-#ifdef ENABLE_STDIO
-parser_state*
+#ifndef MRB_DISABLE_STDIO
+MRB_API parser_state*
mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c)
{
parser_state *p;
p = mrb_parser_new(mrb);
- if (!p) return 0;
+ if (!p) return NULL;
p->s = p->send = NULL;
p->f = f;
@@ -5498,13 +5563,13 @@ mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c)
}
#endif
-parser_state*
+MRB_API parser_state*
mrb_parse_nstring(mrb_state *mrb, const char *s, int len, mrbc_context *c)
{
parser_state *p;
p = mrb_parser_new(mrb);
- if (!p) return 0;
+ if (!p) return NULL;
p->s = s;
p->send = s + len;
@@ -5512,7 +5577,7 @@ mrb_parse_nstring(mrb_state *mrb, const char *s, int len, mrbc_context *c)
return p;
}
-parser_state*
+MRB_API parser_state*
mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c)
{
return mrb_parse_nstring(mrb, s, strlen(s), c);
@@ -5558,7 +5623,12 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
if (c->target_class) {
target = c->target_class;
}
- keep = c->slen + 1;
+ if (c->keep_lv) {
+ keep = c->slen + 1;
+ }
+ else {
+ c->keep_lv = TRUE;
+ }
}
proc->target_class = target;
if (mrb->c->ci) {
@@ -5569,49 +5639,50 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c)
return v;
}
-#ifdef ENABLE_STDIO
-mrb_value
+#ifndef MRB_DISABLE_STDIO
+MRB_API mrb_value
mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c)
{
return load_exec(mrb, mrb_parse_file(mrb, f, c), c);
}
-mrb_value
+MRB_API mrb_value
mrb_load_file(mrb_state *mrb, FILE *f)
{
return mrb_load_file_cxt(mrb, f, NULL);
}
#endif
-mrb_value
+MRB_API mrb_value
mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c)
{
return load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c);
}
-mrb_value
+MRB_API mrb_value
mrb_load_nstring(mrb_state *mrb, const char *s, int len)
{
return mrb_load_nstring_cxt(mrb, s, len, NULL);
}
-mrb_value
+MRB_API mrb_value
mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *c)
{
return mrb_load_nstring_cxt(mrb, s, strlen(s), c);
}
-mrb_value
+MRB_API mrb_value
mrb_load_string(mrb_state *mrb, const char *s)
{
return mrb_load_string_cxt(mrb, s, NULL);
}
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
static void
-dump_prefix(int offset)
+dump_prefix(node *tree, int offset)
{
+ printf("%05d ", tree->lineno);
while (offset--) {
putc(' ', stdout);
putc(' ', stdout);
@@ -5632,15 +5703,15 @@ dump_recur(mrb_state *mrb, node *tree, int offset)
void
mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
{
-#ifdef ENABLE_STDIO
- int n;
+#ifndef MRB_DISABLE_STDIO
+ int nodetype;
if (!tree) return;
again:
- dump_prefix(offset);
- n = (int)(intptr_t)tree->car;
+ dump_prefix(tree, offset);
+ nodetype = (int)(intptr_t)tree->car;
tree = tree->cdr;
- switch (n) {
+ switch (nodetype) {
case NODE_BEGIN:
printf("NODE_BEGIN:\n");
dump_recur(mrb, tree, offset+1);
@@ -5649,7 +5720,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_RESCUE:
printf("NODE_RESCUE:\n");
if (tree->car) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("body:\n");
mrb_parser_dump(mrb, tree->car, offset+2);
}
@@ -5657,22 +5728,22 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
if (tree->car) {
node *n2 = tree->car;
- dump_prefix(offset+1);
+ dump_prefix(n2, offset+1);
printf("rescue:\n");
while (n2) {
node *n3 = n2->car;
if (n3->car) {
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
printf("handle classes:\n");
dump_recur(mrb, n3->car, offset+3);
}
if (n3->cdr->car) {
- dump_prefix(offset+2);
+ dump_prefix(n3, offset+2);
printf("exc_var:\n");
mrb_parser_dump(mrb, n3->cdr->car, offset+3);
}
if (n3->cdr->cdr->car) {
- dump_prefix(offset+2);
+ dump_prefix(n3, offset+2);
printf("rescue body:\n");
mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3);
}
@@ -5681,7 +5752,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
}
tree = tree->cdr;
if (tree->car) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("else:\n");
mrb_parser_dump(mrb, tree->car, offset+2);
}
@@ -5689,10 +5760,10 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_ENSURE:
printf("NODE_ENSURE:\n");
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("body:\n");
mrb_parser_dump(mrb, tree->car, offset+2);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("ensure:\n");
mrb_parser_dump(mrb, tree->cdr->cdr, offset+2);
break;
@@ -5704,62 +5775,62 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_BLOCK:
block:
printf("NODE_BLOCK:\n");
- tree = tree->cdr;
- if (tree->car) {
- node *n = tree->car;
+ tree = tree->cdr;
+ if (tree->car) {
+ node *n = tree->car;
- if (n->car) {
- dump_prefix(offset+1);
- printf("mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(offset+1);
- printf("optional args:\n");
- {
- node *n2 = n->car;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("mandatory args:\n");
+ dump_recur(mrb, n->car, offset+2);
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("optional args:\n");
+ {
+ node *n2 = n->car;
- while (n2) {
- dump_prefix(offset+2);
- printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
- mrb_parser_dump(mrb, n2->car->cdr, 0);
- n2 = n2->cdr;
+ while (n2) {
+ dump_prefix(n2, offset+2);
+ printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
+ mrb_parser_dump(mrb, n2->car->cdr, 0);
+ n2 = n2->cdr;
+ }
}
}
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
+ }
+ n = n->cdr;
+ if (n->car) {
+ dump_prefix(n, offset+1);
+ printf("post mandatory args:\n");
+ dump_recur(mrb, n->car, offset+2);
+ }
+ n = n->cdr;
+ if (n) {
+ dump_prefix(n, offset+1);
+ printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
+ }
}
- n = n->cdr;
- if (n->car) {
- dump_prefix(offset+1);
- printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
- }
- n = n->cdr;
- if (n->car) {
- dump_prefix(offset+1);
- printf("post mandatory args:\n");
- dump_recur(mrb, n->car, offset+2);
- }
- n = n->cdr;
- if (n) {
- dump_prefix(offset+1);
- printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
- }
- }
- dump_prefix(offset+1);
- printf("body:\n");
- mrb_parser_dump(mrb, tree->cdr->car, offset+2);
- break;
+ dump_prefix(tree, offset+1);
+ printf("body:\n");
+ mrb_parser_dump(mrb, tree->cdr->car, offset+2);
+ break;
case NODE_IF:
printf("NODE_IF:\n");
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("cond:\n");
mrb_parser_dump(mrb, tree->car, offset+2);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("then:\n");
mrb_parser_dump(mrb, tree->cdr->car, offset+2);
if (tree->cdr->cdr->car) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("else:\n");
mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2);
}
@@ -5784,10 +5855,10 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
}
tree = tree->cdr;
while (tree) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("case:\n");
dump_recur(mrb, tree->car->car, offset+2);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("body:\n");
mrb_parser_dump(mrb, tree->car->cdr, offset+2);
tree = tree->cdr;
@@ -5796,47 +5867,47 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_WHILE:
printf("NODE_WHILE:\n");
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("cond:\n");
mrb_parser_dump(mrb, tree->car, offset+2);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("body:\n");
mrb_parser_dump(mrb, tree->cdr, offset+2);
break;
case NODE_UNTIL:
printf("NODE_UNTIL:\n");
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("cond:\n");
mrb_parser_dump(mrb, tree->car, offset+2);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("body:\n");
mrb_parser_dump(mrb, tree->cdr, offset+2);
break;
case NODE_FOR:
printf("NODE_FOR:\n");
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("var:\n");
{
node *n2 = tree->car;
if (n2->car) {
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
printf("pre:\n");
dump_recur(mrb, n2->car, offset+3);
}
n2 = n2->cdr;
if (n2) {
if (n2->car) {
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
printf("rest:\n");
mrb_parser_dump(mrb, n2->car, offset+3);
}
n2 = n2->cdr;
if (n2) {
if (n2->car) {
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
printf("post:\n");
dump_recur(mrb, n2->car, offset+3);
}
@@ -5844,11 +5915,11 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
}
}
tree = tree->cdr;
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("in:\n");
mrb_parser_dump(mrb, tree->car, offset+2);
tree = tree->cdr;
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("do:\n");
mrb_parser_dump(mrb, tree->car, offset+2);
break;
@@ -5857,15 +5928,17 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
printf("NODE_SCOPE:\n");
{
node *n2 = tree->car;
+ mrb_bool first_lval = TRUE;
if (n2 && (n2->car || n2->cdr)) {
- dump_prefix(offset+1);
+ dump_prefix(n2, offset+1);
printf("local variables:\n");
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
while (n2) {
if (n2->car) {
- if (n2 != tree->car) printf(", ");
+ if (!first_lval) printf(", ");
printf("%s", mrb_sym2name(mrb, sym(n2->car)));
+ first_lval = FALSE;
}
n2 = n2->cdr;
}
@@ -5880,17 +5953,17 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_CALL:
printf("NODE_CALL:\n");
mrb_parser_dump(mrb, tree->car, offset+1);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("method='%s' (%d)\n",
mrb_sym2name(mrb, sym(tree->cdr->car)),
(int)(intptr_t)tree->cdr->car);
tree = tree->cdr->cdr->car;
if (tree) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("args:\n");
dump_recur(mrb, tree->car, offset+2);
if (tree->cdr) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("block:\n");
mrb_parser_dump(mrb, tree->cdr, offset+2);
}
@@ -5912,14 +5985,12 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_COLON2:
printf("NODE_COLON2:\n");
mrb_parser_dump(mrb, tree->car, offset+1);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("::%s\n", mrb_sym2name(mrb, sym(tree->cdr)));
break;
case NODE_COLON3:
- printf("NODE_COLON3:\n");
- dump_prefix(offset+1);
- printf("::%s\n", mrb_sym2name(mrb, sym(tree)));
+ printf("NODE_COLON3: ::%s\n", mrb_sym2name(mrb, sym(tree)));
break;
case NODE_ARRAY:
@@ -5930,10 +6001,10 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_HASH:
printf("NODE_HASH:\n");
while (tree) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("key:\n");
mrb_parser_dump(mrb, tree->car->car, offset+2);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("value:\n");
mrb_parser_dump(mrb, tree->car->cdr, offset+2);
tree = tree->cdr;
@@ -5947,33 +6018,33 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_ASGN:
printf("NODE_ASGN:\n");
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("lhs:\n");
mrb_parser_dump(mrb, tree->car, offset+2);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("rhs:\n");
mrb_parser_dump(mrb, tree->cdr, offset+2);
break;
case NODE_MASGN:
printf("NODE_MASGN:\n");
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("mlhs:\n");
{
node *n2 = tree->car;
if (n2->car) {
- dump_prefix(offset+2);
+ dump_prefix(tree, offset+2);
printf("pre:\n");
dump_recur(mrb, n2->car, offset+3);
}
n2 = n2->cdr;
if (n2) {
if (n2->car) {
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
printf("rest:\n");
if (n2->car == (node*)-1) {
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
printf("(empty)\n");
}
else {
@@ -5983,25 +6054,25 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
n2 = n2->cdr;
if (n2) {
if (n2->car) {
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
printf("post:\n");
dump_recur(mrb, n2->car, offset+3);
}
}
}
}
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("rhs:\n");
mrb_parser_dump(mrb, tree->cdr, offset+2);
break;
case NODE_OP_ASGN:
printf("NODE_OP_ASGN:\n");
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("lhs:\n");
mrb_parser_dump(mrb, tree->car, offset+2);
tree = tree->cdr;
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car);
tree = tree->cdr;
mrb_parser_dump(mrb, tree->car, offset+1);
@@ -6010,11 +6081,11 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_SUPER:
printf("NODE_SUPER:\n");
if (tree) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("args:\n");
dump_recur(mrb, tree->car, offset+2);
if (tree->cdr) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("block:\n");
mrb_parser_dump(mrb, tree->cdr, offset+2);
}
@@ -6075,10 +6146,10 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_MATCH:
printf("NODE_MATCH:\n");
- dump_prefix(offset + 1);
+ dump_prefix(tree, offset + 1);
printf("lhs:\n");
mrb_parser_dump(mrb, tree->car, offset + 2);
- dump_prefix(offset + 1);
+ dump_prefix(tree, offset + 1);
printf("rhs:\n");
mrb_parser_dump(mrb, tree->cdr, offset + 2);
break;
@@ -6138,9 +6209,9 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_DREGX:
printf("NODE_DREGX\n");
dump_recur(mrb, tree->car, offset+1);
- dump_prefix(offset);
+ dump_prefix(tree, offset);
printf("tail: %s\n", (char*)tree->cdr->cdr->car);
- dump_prefix(offset);
+ dump_prefix(tree, offset);
printf("opt: %s\n", (char*)tree->cdr->cdr->cdr);
break;
@@ -6185,24 +6256,24 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_CLASS:
printf("NODE_CLASS:\n");
if (tree->car->car == (node*)0) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
}
else if (tree->car->car == (node*)1) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
}
else {
mrb_parser_dump(mrb, tree->car->car, offset+1);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
}
if (tree->cdr->car) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("super:\n");
mrb_parser_dump(mrb, tree->cdr->car, offset+2);
}
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("body:\n");
mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2);
break;
@@ -6210,19 +6281,19 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_MODULE:
printf("NODE_MODULE:\n");
if (tree->car->car == (node*)0) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
}
else if (tree->car->car == (node*)1) {
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
}
else {
mrb_parser_dump(mrb, tree->car->car, offset+1);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr)));
}
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("body:\n");
mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2);
break;
@@ -6230,27 +6301,29 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_SCLASS:
printf("NODE_SCLASS:\n");
mrb_parser_dump(mrb, tree->car, offset+1);
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("body:\n");
mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2);
break;
case NODE_DEF:
printf("NODE_DEF:\n");
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf("%s\n", mrb_sym2name(mrb, sym(tree->car)));
tree = tree->cdr;
{
node *n2 = tree->car;
+ mrb_bool first_lval = TRUE;
if (n2 && (n2->car || n2->cdr)) {
- dump_prefix(offset+1);
+ dump_prefix(n2, offset+1);
printf("local variables:\n");
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
while (n2) {
if (n2->car) {
- if (n2 != tree->car) printf(", ");
+ if (!first_lval) printf(", ");
printf("%s", mrb_sym2name(mrb, sym(n2->car)));
+ first_lval = FALSE;
}
n2 = n2->cdr;
}
@@ -6262,19 +6335,19 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
node *n = tree->car;
if (n->car) {
- dump_prefix(offset+1);
+ dump_prefix(n, offset+1);
printf("mandatory args:\n");
dump_recur(mrb, n->car, offset+2);
}
n = n->cdr;
if (n->car) {
- dump_prefix(offset+1);
+ dump_prefix(n, offset+1);
printf("optional args:\n");
{
node *n2 = n->car;
while (n2) {
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
mrb_parser_dump(mrb, n2->car->cdr, 0);
n2 = n2->cdr;
@@ -6283,19 +6356,18 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
}
n = n->cdr;
if (n->car) {
- dump_prefix(offset+1);
+ dump_prefix(n, offset+1);
printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
}
n = n->cdr;
if (n->car) {
- dump_prefix(offset+1);
+ dump_prefix(n, offset+1);
printf("post mandatory args:\n");
dump_recur(mrb, n->car, offset+2);
}
- n = n->cdr;
- if (n) {
- dump_prefix(offset+1);
- printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
+ if (n->cdr) {
+ dump_prefix(n, offset+1);
+ printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr)));
}
}
mrb_parser_dump(mrb, tree->cdr->car, offset+1);
@@ -6305,26 +6377,26 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
printf("NODE_SDEF:\n");
mrb_parser_dump(mrb, tree->car, offset+1);
tree = tree->cdr;
- dump_prefix(offset+1);
+ dump_prefix(tree, offset+1);
printf(":%s\n", mrb_sym2name(mrb, sym(tree->car)));
tree = tree->cdr->cdr;
if (tree->car) {
node *n = tree->car;
if (n->car) {
- dump_prefix(offset+1);
+ dump_prefix(n, offset+1);
printf("mandatory args:\n");
dump_recur(mrb, n->car, offset+2);
}
n = n->cdr;
if (n->car) {
- dump_prefix(offset+1);
+ dump_prefix(n, offset+1);
printf("optional args:\n");
{
node *n2 = n->car;
while (n2) {
- dump_prefix(offset+2);
+ dump_prefix(n2, offset+2);
printf("%s=", mrb_sym2name(mrb, sym(n2->car->car)));
mrb_parser_dump(mrb, n2->car->cdr, 0);
n2 = n2->cdr;
@@ -6333,18 +6405,18 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
}
n = n->cdr;
if (n->car) {
- dump_prefix(offset+1);
+ dump_prefix(n, offset+1);
printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car)));
}
n = n->cdr;
if (n->car) {
- dump_prefix(offset+1);
+ dump_prefix(n, offset+1);
printf("post mandatory args:\n");
dump_recur(mrb, n->car, offset+2);
}
n = n->cdr;
if (n) {
- dump_prefix(offset+1);
+ dump_prefix(n, offset+1);
printf("blk=&%s\n", mrb_sym2name(mrb, sym(n)));
}
}
@@ -6363,7 +6435,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
default:
- printf("node type: %d (0x%x)\n", (int)n, (int)n);
+ printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype);
break;
}
#endif
diff --git a/mrbgems/mruby-compiler/mrbgem.rake b/mrbgems/mruby-compiler/mrbgem.rake
new file mode 100644
index 000000000..3a22762fa
--- /dev/null
+++ b/mrbgems/mruby-compiler/mrbgem.rake
@@ -0,0 +1,40 @@
+MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'mruby compiler library'
+
+ current_dir = spec.dir
+ current_build_dir = spec.build_dir
+
+ lex_def = "#{current_dir}/core/lex.def"
+ core_objs = Dir.glob("#{current_dir}/core/*.c").map { |f|
+ next nil if build.cxx_abi_enabled? and f =~ /(codegen).c$/
+ objfile(f.pathmap("#{current_build_dir}/core/%n"))
+ }.compact
+
+ if build.cxx_abi_enabled?
+ core_objs <<
+ build.compile_as_cxx("#{current_build_dir}/core/y.tab.c", "#{current_build_dir}/core/y.tab.cxx",
+ objfile("#{current_build_dir}/y.tab"), ["#{current_dir}/core"]) <<
+ build.compile_as_cxx("#{current_dir}/core/codegen.c", "#{current_build_dir}/core/codegen.cxx")
+ else
+ core_objs << objfile("#{current_build_dir}/core/y.tab")
+ file objfile("#{current_build_dir}/core/y.tab") => "#{current_build_dir}/core/y.tab.c" do |t|
+ cc.run t.name, t.prerequisites.first, [], ["#{current_dir}/core"]
+ end
+ end
+ file objfile("#{current_build_dir}/core/y.tab") => lex_def
+
+ # Parser
+ file "#{current_build_dir}/core/y.tab.c" => ["#{current_dir}/core/parse.y"] do |t|
+ yacc.run t.name, t.prerequisites.first
+ end
+
+ # Lexical analyzer
+ file lex_def => "#{current_dir}/core/keywords" do |t|
+ gperf.run t.name, t.prerequisites.first
+ end
+
+ file libfile("#{build.build_dir}/lib/libmruby_core") => core_objs
+ build.libmruby << core_objs
+end
diff --git a/mrbgems/mruby-enum-ext/mrbgem.rake b/mrbgems/mruby-enum-ext/mrbgem.rake
index 0c9d88fa2..d5816b80f 100644
--- a/mrbgems/mruby-enum-ext/mrbgem.rake
+++ b/mrbgems/mruby-enum-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-enum-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extensional Enumerable module'
+ spec.summary = 'Enumerable module extension'
end
diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb
index b13d00a84..ed1943f30 100644
--- a/mrbgems/mruby-enum-ext/mrblib/enum.rb
+++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb
@@ -37,7 +37,7 @@ module Enumerable
# a.drop_while {|i| i < 3 } #=> [3, 4, 5, 0]
def drop_while(&block)
- return to_enum :drop_while unless block_given?
+ return to_enum :drop_while unless block
ary, state = [], false
self.each do |*val|
@@ -77,13 +77,13 @@ module Enumerable
# Passes elements to the block until the block returns +nil+ or +false+,
# then stops iterating and returns an array of all prior elements.
#
- # If no block is given, an enumerator is returned instead.
+ # If no block is given, an enumerator is returned instead.
+ #
+ # a = [1, 2, 3, 4, 5, 0]
+ # a.take_while {|i| i < 3 } #=> [1, 2]
#
- # a = [1, 2, 3, 4, 5, 0]
- # a.take_while {|i| i < 3 } #=> [1, 2]
-
def take_while(&block)
- return to_enum :take_while unless block_given?
+ return to_enum :take_while unless block
ary = []
self.each do |*val|
@@ -94,13 +94,12 @@ module Enumerable
end
##
- # call-seq:
- # enum.each_cons(n) {...} -> nil
- #
# Iterates the given block for each array of consecutive <n>
# elements.
#
- # e.g.:
+ # @return [nil]
+ #
+ # @example
# (1..10).each_cons(3) {|a| p a}
# # outputs below
# [1, 2, 3]
@@ -116,6 +115,7 @@ module Enumerable
raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
raise ArgumentError, "invalid size" if n <= 0
+ return to_enum(:each_cons,n) unless block
ary = []
n = n.to_int
self.each do |*val|
@@ -126,12 +126,11 @@ module Enumerable
end
##
- # call-seq:
- # enum.each_slice(n) {...} -> nil
- #
# Iterates the given block for each slice of <n> elements.
#
- # e.g.:
+ # @return [nil]
+ #
+ # @example
# (1..10).each_slice(3) {|a| p a}
# # outputs below
# [1, 2, 3]
@@ -143,6 +142,7 @@ module Enumerable
raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
raise ArgumentError, "invalid slice size" if n <= 0
+ return to_enum(:each_slice,n) unless block
ary = []
n = n.to_int
self.each do |*val|
@@ -164,10 +164,10 @@ module Enumerable
# block, and values are arrays of elements in <i>enum</i>
# corresponding to the key.
#
- # (1..6).group_by {|i| i%3} #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
-
+ # (1..6).group_by {|i| i%3} #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
+ #
def group_by(&block)
- return to_enum :group_by unless block_given?
+ return to_enum :group_by unless block
h = {}
self.each do |*val|
@@ -189,7 +189,7 @@ module Enumerable
# If no block is given, an enumerator is returned instead.
def sort_by(&block)
- return to_enum :sort_by unless block_given?
+ return to_enum :sort_by unless block
ary = []
orig = []
@@ -275,7 +275,7 @@ module Enumerable
# [1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4]
# [[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100]
def flat_map(&block)
- return to_enum :flat_map unless block_given?
+ return to_enum :flat_map unless block
ary = []
self.each do |*e|
@@ -303,7 +303,7 @@ module Enumerable
# %w[albatross dog horse].max_by {|x| x.length } #=> "albatross"
def max_by(&block)
- return to_enum :max_by unless block_given?
+ return to_enum :max_by unless block
first = true
max = nil
@@ -337,7 +337,7 @@ module Enumerable
# %w[albatross dog horse].min_by {|x| x.length } #=> "dog"
def min_by(&block)
- return to_enum :min_by unless block_given?
+ return to_enum :min_by unless block
first = true
min = nil
@@ -411,7 +411,7 @@ module Enumerable
# %w(albatross dog horse).minmax_by { |x| x.length } #=> ["dog", "albatross"]
def minmax_by(&block)
- return to_enum :minmax_by unless block_given?
+ return to_enum :minmax_by unless block
max = nil
max_cmp = nil
@@ -515,9 +515,9 @@ module Enumerable
#
def each_with_object(obj=nil, &block)
- raise ArgumentError, "wrong number of arguments (0 for 1)" if obj == nil
+ raise ArgumentError, "wrong number of arguments (0 for 1)" if obj.nil?
- return to_enum(:each_with_object, obj) unless block_given?
+ return to_enum(:each_with_object, obj) unless block
self.each {|*val| block.call(val.__svalue, obj) }
obj
@@ -542,7 +542,7 @@ module Enumerable
#
def reverse_each(&block)
- return to_enum :reverse_each unless block_given?
+ return to_enum :reverse_each unless block
ary = self.to_a
i = ary.size - 1
@@ -574,10 +574,10 @@ module Enumerable
#
def cycle(n=nil, &block)
- return to_enum(:cycle, n) if !block_given? && n == nil
+ return to_enum(:cycle, n) if !block && n.nil?
ary = []
- if n == nil
+ if n.nil?
self.each do|*val|
ary.push val
block.call(*val)
@@ -623,7 +623,7 @@ module Enumerable
#
def find_index(val=NONE, &block)
- return to_enum(:find_index, val) if !block_given? && val == NONE
+ return to_enum(:find_index, val) if !block && val == NONE
idx = 0
if block
diff --git a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
index 2ffeb1808..8ce363c6d 100644
--- a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
+++ b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
@@ -1,30 +1,29 @@
-# = Enumerable#lazy implementation
-#
-# Enumerable#lazy returns an instance of Enumerable::Lazy.
-# You can use it just like as normal Enumerable object,
-# except these methods act as 'lazy':
-#
-# - map collect
-# - select find_all
-# - reject
-# - grep
-# - drop
-# - drop_while
-# - take_while
-# - flat_map collect_concat
-# - zip
-#
-# == Acknowledgements
-#
-# Based on https://github.com/yhara/enumerable-lazy
-# Inspired by https://github.com/antimon2/enumerable_lz
-# http://jp.rubyist.net/magazine/?0034-Enumerable_lz (ja)
-
module Enumerable
+
+ # = Enumerable#lazy implementation
+ #
+ # Enumerable#lazy returns an instance of Enumerable::Lazy.
+ # You can use it just like as normal Enumerable object,
+ # except these methods act as 'lazy':
+ #
+ # - map collect
+ # - select find_all
+ # - reject
+ # - grep
+ # - drop
+ # - drop_while
+ # - take_while
+ # - flat_map collect_concat
+ # - zip
def lazy
Lazy.new(self)
end
+ # == Acknowledgements
+ #
+ # Based on https://github.com/yhara/enumerable-lazy
+ # Inspired by https://github.com/antimon2/enumerable_lz
+ # http://jp.rubyist.net/magazine/?0034-Enumerable_lz (ja)
class Lazy < Enumerator
def initialize(obj, &block)
super(){|yielder|
diff --git a/mrbgems/mruby-enumerator/mrbgem.rake b/mrbgems/mruby-enumerator/mrbgem.rake
index abcc54e7a..8757a15ea 100644
--- a/mrbgems/mruby-enumerator/mrbgem.rake
+++ b/mrbgems/mruby-enumerator/mrbgem.rake
@@ -2,5 +2,6 @@ MRuby::Gem::Specification.new('mruby-enumerator') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.add_dependency('mruby-fiber', :core => 'mruby-fiber')
+ spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext'
spec.summary = 'Enumerator class'
end
diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
index 58996adc8..9abaca38a 100644
--- a/mrbgems/mruby-enumerator/mrblib/enumerator.rb
+++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
@@ -6,92 +6,91 @@
# A class which allows both internal and external iteration.
#
# An Enumerator can be created by the following methods.
-# - Kernel#to_enum
-# - Kernel#enum_for
-# - Enumerator.new
+# - {Kernel#to_enum}
+# - {Kernel#enum_for}
+# - {Enumerator#initialize Enumerator.new}
#
# Most methods have two forms: a block form where the contents
# are evaluated for each item in the enumeration, and a non-block form
# which returns a new Enumerator wrapping the iteration.
#
-# enumerator = %w(one two three).each
-# puts enumerator.class # => Enumerator
+# enumerator = %w(one two three).each
+# puts enumerator.class # => Enumerator
#
-# enumerator.each_with_object("foo") do |item, obj|
-# puts "#{obj}: #{item}"
-# end
+# enumerator.each_with_object("foo") do |item, obj|
+# puts "#{obj}: #{item}"
+# end
#
-# # foo: one
-# # foo: two
-# # foo: three
+# # foo: one
+# # foo: two
+# # foo: three
#
-# enum_with_obj = enumerator.each_with_object("foo")
-# puts enum_with_obj.class # => Enumerator
+# enum_with_obj = enumerator.each_with_object("foo")
+# puts enum_with_obj.class # => Enumerator
#
-# enum_with_obj.each do |item, obj|
-# puts "#{obj}: #{item}"
-# end
+# enum_with_obj.each do |item, obj|
+# puts "#{obj}: #{item}"
+# end
#
-# # foo: one
-# # foo: two
-# # foo: three
+# # foo: one
+# # foo: two
+# # foo: three
#
# This allows you to chain Enumerators together. For example, you
# can map a list's elements to strings containing the index
# and the element as a string via:
#
-# puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
-# # => ["0:foo", "1:bar", "2:baz"]
+# puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" }
+# # => ["0:foo", "1:bar", "2:baz"]
#
# An Enumerator can also be used as an external iterator.
# For example, Enumerator#next returns the next value of the iterator
# or raises StopIteration if the Enumerator is at the end.
#
-# e = [1,2,3].each # returns an enumerator object.
-# puts e.next # => 1
-# puts e.next # => 2
-# puts e.next # => 3
-# puts e.next # raises StopIteration
+# e = [1,2,3].each # returns an enumerator object.
+# puts e.next # => 1
+# puts e.next # => 2
+# puts e.next # => 3
+# puts e.next # raises StopIteration
#
# You can use this to implement an internal iterator as follows:
#
-# def ext_each(e)
-# while true
-# begin
-# vs = e.next_values
-# rescue StopIteration
-# return $!.result
+# def ext_each(e)
+# while true
+# begin
+# vs = e.next_values
+# rescue StopIteration
+# return $!.result
+# end
+# y = yield(*vs)
+# e.feed y
+# end
# end
-# y = yield(*vs)
-# e.feed y
-# end
-# end
#
-# o = Object.new
+# o = Object.new
#
-# def o.each
-# puts yield
-# puts yield(1)
-# puts yield(1, 2)
-# 3
-# end
+# def o.each
+# puts yield
+# puts yield(1)
+# puts yield(1, 2)
+# 3
+# end
#
-# # use o.each as an internal iterator directly.
-# puts o.each {|*x| puts x; [:b, *x] }
-# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
+# # use o.each as an internal iterator directly.
+# puts o.each {|*x| puts x; [:b, *x] }
+# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
+#
+# # convert o.each to an external iterator for
+# # implementing an internal iterator.
+# puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] }
+# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
#
-# # convert o.each to an external iterator for
-# # implementing an internal iterator.
-# puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] }
-# # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
-
class Enumerator
include Enumerable
##
- # call-seq:
- # Enumerator.new(size = nil) { |yielder| ... }
- # Enumerator.new(obj, method = :each, *args)
+ # @overload initialize(size = nil, &block)
+ # @overload initialize(obj, method = :each, *args)
#
# Creates a new Enumerator object, which can be used as an
# Enumerable.
@@ -100,15 +99,15 @@ class Enumerator
# which a "yielder" object, given as block parameter, can be used to
# yield a value by calling the +yield+ method (aliased as +<<+):
#
- # fib = Enumerator.new do |y|
- # a = b = 1
- # loop do
- # y << a
- # a, b = b, a + b
+ # fib = Enumerator.new do |y|
+ # a = b = 1
+ # loop do
+ # y << a
+ # a, b = b, a + b
+ # end
# end
- # end
#
- # p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
+ # p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
#
def initialize(obj=nil, meth=:each, *args, &block)
if block_given?
@@ -188,8 +187,7 @@ class Enumerator
#
# If no block is given, returns a new Enumerator.
#
- # === Example
- #
+ # @example
# to_three = Enumerator.new do |y|
# 3.times do |x|
# y << x
@@ -549,10 +547,6 @@ class Enumerator
end
end
-class StopIteration < IndexError
- attr_accessor :result
-end
-
module Kernel
##
# call-seq:
@@ -586,27 +580,27 @@ module Kernel
#
# Here is such an example, with parameter passing and a sizing block:
#
- # module Enumerable
- # # a generic method to repeat the values of any enumerable
- # def repeat(n)
- # raise ArgumentError, "#{n} is negative!" if n < 0
- # unless block_given?
- # return to_enum(__method__, n) do # __method__ is :repeat here
- # sz = size # Call size and multiply by n...
- # sz * n if sz # but return nil if size itself is nil
+ # module Enumerable
+ # # a generic method to repeat the values of any enumerable
+ # def repeat(n)
+ # raise ArgumentError, "#{n} is negative!" if n < 0
+ # unless block_given?
+ # return to_enum(__method__, n) do # __method__ is :repeat here
+ # sz = size # Call size and multiply by n...
+ # sz * n if sz # but return nil if size itself is nil
+ # end
+ # end
+ # each do |*val|
+ # n.times { yield *val }
# end
- # end
- # each do |*val|
- # n.times { yield *val }
# end
# end
- # end
#
- # %i[hello world].repeat(2) { |w| puts w }
- # # => Prints 'hello', 'hello', 'world', 'world'
- # enum = (1..14).repeat(3)
- # # => returns an Enumerator when called without a block
- # enum.first(4) # => [1, 1, 1, 2]
+ # %i[hello world].repeat(2) { |w| puts w }
+ # # => Prints 'hello', 'hello', 'world', 'world'
+ # enum = (1..14).repeat(3)
+ # # => returns an Enumerator when called without a block
+ # enum.first(4) # => [1, 1, 1, 2]
#
def to_enum(meth=:each, *args)
Enumerator.new self, meth, *args
diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb
index 0e3ddcba9..2e45dae4b 100644
--- a/mrbgems/mruby-enumerator/test/enumerator.rb
+++ b/mrbgems/mruby-enumerator/test/enumerator.rb
@@ -427,7 +427,6 @@ assert 'modifying existing methods' do
assert_equal i, e.next
i += 1
}
- assert_nil loop_ret
end
assert 'Integral#times' do
diff --git a/mrbgems/mruby-error/mrbgem.rake b/mrbgems/mruby-error/mrbgem.rake
new file mode 100644
index 000000000..b8281b17e
--- /dev/null
+++ b/mrbgems/mruby-error/mrbgem.rake
@@ -0,0 +1,10 @@
+MRuby::Gem::Specification.new('mruby-error') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'extensional error handling'
+
+ if build.cxx_abi_enabled?
+ @objs << build.compile_as_cxx("#{spec.dir}/src/exception.c", "#{spec.build_dir}/src/exception.cxx")
+ @objs.delete_if { |v| v == objfile("#{spec.build_dir}/src/exception") }
+ end
+end
diff --git a/mrbgems/mruby-error/src/exception.c b/mrbgems/mruby-error/src/exception.c
new file mode 100644
index 000000000..170abb699
--- /dev/null
+++ b/mrbgems/mruby-error/src/exception.c
@@ -0,0 +1,100 @@
+#include <mruby.h>
+#include <mruby/throw.h>
+#include <mruby/error.h>
+
+MRB_API mrb_value
+mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state)
+{
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ mrb_value result = mrb_nil_value();
+
+ if (state) { *state = FALSE; }
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ result = body(mrb, data);
+ mrb->jmp = prev_jmp;
+ } MRB_CATCH(&c_jmp) {
+ mrb->jmp = prev_jmp;
+ result = mrb_obj_value(mrb->exc);
+ mrb->exc = NULL;
+ if (state) { *state = TRUE; }
+ } MRB_END_EXC(&c_jmp);
+
+ mrb_gc_protect(mrb, result);
+ return result;
+}
+
+MRB_API mrb_value
+mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t ensure, mrb_value e_data)
+{
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ mrb_value result;
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ result = body(mrb, b_data);
+ mrb->jmp = prev_jmp;
+ } MRB_CATCH(&c_jmp) {
+ mrb->jmp = prev_jmp;
+ ensure(mrb, e_data);
+ MRB_THROW(mrb->jmp); /* rethrow catched exceptions */
+ } MRB_END_EXC(&c_jmp);
+
+ ensure(mrb, e_data);
+ mrb_gc_protect(mrb, result);
+ return result;
+}
+
+MRB_API mrb_value
+mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data,
+ mrb_func_t rescue, mrb_value r_data)
+{
+ return mrb_rescue_exceptions(mrb, body, b_data, rescue, r_data, 1, &mrb->eStandardError_class);
+}
+
+MRB_API mrb_value
+mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data,
+ mrb_int len, struct RClass **classes)
+{
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+ struct mrb_jmpbuf c_jmp;
+ mrb_value result;
+ mrb_bool error_matched = FALSE;
+ mrb_int i;
+
+ MRB_TRY(&c_jmp) {
+ mrb->jmp = &c_jmp;
+ result = body(mrb, b_data);
+ mrb->jmp = prev_jmp;
+ } MRB_CATCH(&c_jmp) {
+ mrb->jmp = prev_jmp;
+
+ for (i = 0; i < len; ++i) {
+ if (mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), classes[i])) {
+ error_matched = TRUE;
+ break;
+ }
+ }
+
+ if (!error_matched) { MRB_THROW(mrb->jmp); }
+
+ mrb->exc = NULL;
+ result = rescue(mrb, r_data);
+ } MRB_END_EXC(&c_jmp);
+
+ mrb_gc_protect(mrb, result);
+ return result;
+}
+
+void
+mrb_mruby_error_gem_init(mrb_state *mrb)
+{
+}
+
+void
+mrb_mruby_error_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-error/test/exception.c b/mrbgems/mruby-error/test/exception.c
new file mode 100644
index 000000000..4de0e9607
--- /dev/null
+++ b/mrbgems/mruby-error/test/exception.c
@@ -0,0 +1,59 @@
+#include <mruby.h>
+#include <mruby/error.h>
+#include <mruby/array.h>
+
+static mrb_value
+protect_cb(mrb_state *mrb, mrb_value b)
+{
+ return mrb_yield_argv(mrb, b, 0, NULL);
+}
+
+static mrb_value
+run_protect(mrb_state *mrb, mrb_value self)
+{
+ mrb_value b;
+ mrb_value ret[2];
+ mrb_bool state;
+ mrb_get_args(mrb, "&", &b);
+ ret[0] = mrb_protect(mrb, protect_cb, b, &state);
+ ret[1] = mrb_bool_value(state);
+ return mrb_ary_new_from_values(mrb, 2, ret);
+}
+
+static mrb_value
+run_ensure(mrb_state *mrb, mrb_value self)
+{
+ mrb_value b, e;
+ mrb_get_args(mrb, "oo", &b, &e);
+ return mrb_ensure(mrb, protect_cb, b, protect_cb, e);
+}
+
+static mrb_value
+run_rescue(mrb_state *mrb, mrb_value self)
+{
+ mrb_value b, r;
+ mrb_get_args(mrb, "oo", &b, &r);
+ return mrb_rescue(mrb, protect_cb, b, protect_cb, r);
+}
+
+static mrb_value
+run_rescue_exceptions(mrb_state *mrb, mrb_value self)
+{
+ mrb_value b, r;
+ struct RClass *cls[1];
+ mrb_get_args(mrb, "oo", &b, &r);
+ cls[0] = E_TYPE_ERROR;
+ return mrb_rescue_exceptions(mrb, protect_cb, b, protect_cb, r, 1, cls);
+}
+
+void
+mrb_mruby_error_gem_test(mrb_state *mrb)
+{
+ struct RClass *cls;
+
+ cls = mrb_define_class(mrb, "ExceptionTest", mrb->object_class);
+ mrb_define_module_function(mrb, cls, "mrb_protect", run_protect, MRB_ARGS_NONE() | MRB_ARGS_BLOCK());
+ mrb_define_module_function(mrb, cls, "mrb_ensure", run_ensure, MRB_ARGS_REQ(2));
+ mrb_define_module_function(mrb, cls, "mrb_rescue", run_rescue, MRB_ARGS_REQ(2));
+ mrb_define_module_function(mrb, cls, "mrb_rescue_exceptions", run_rescue_exceptions, MRB_ARGS_REQ(2));
+}
diff --git a/mrbgems/mruby-error/test/exception.rb b/mrbgems/mruby-error/test/exception.rb
new file mode 100644
index 000000000..0bbc2a0e7
--- /dev/null
+++ b/mrbgems/mruby-error/test/exception.rb
@@ -0,0 +1,55 @@
+assert 'mrb_protect' do
+ # no failure in protect returns [result, false]
+ assert_equal ['test', false] do
+ ExceptionTest.mrb_protect { 'test' }
+ end
+ # failure in protect returns [exception, true]
+ result = ExceptionTest.mrb_protect { raise 'test' }
+ assert_kind_of RuntimeError, result[0]
+ assert_true result[1]
+end
+
+assert 'mrb_ensure' do
+ a = false
+ assert_equal 'test' do
+ ExceptionTest.mrb_ensure Proc.new { 'test' }, Proc.new { a = true }
+ end
+ assert_true a
+
+ a = false
+ assert_raise RuntimeError do
+ ExceptionTest.mrb_ensure Proc.new { raise 'test' }, Proc.new { a = true }
+ end
+ assert_true a
+end
+
+assert 'mrb_rescue' do
+ assert_equal 'test' do
+ ExceptionTest.mrb_rescue Proc.new { 'test' }, Proc.new {}
+ end
+
+ class CustomExp < Exception
+ end
+
+ assert_raise CustomExp do
+ ExceptionTest.mrb_rescue Proc.new { raise CustomExp.new 'test' }, Proc.new { 'rescue' }
+ end
+
+ assert_equal 'rescue' do
+ ExceptionTest.mrb_rescue Proc.new { raise 'test' }, Proc.new { 'rescue' }
+ end
+end
+
+assert 'mrb_rescue_exceptions' do
+ assert_equal 'test' do
+ ExceptionTest.mrb_rescue_exceptions Proc.new { 'test' }, Proc.new {}
+ end
+
+ assert_raise RangeError do
+ ExceptionTest.mrb_rescue_exceptions Proc.new { raise RangeError.new 'test' }, Proc.new { 'rescue' }
+ end
+
+ assert_equal 'rescue' do
+ ExceptionTest.mrb_rescue_exceptions Proc.new { raise TypeError.new 'test' }, Proc.new { 'rescue' }
+ end
+end
diff --git a/mrbgems/mruby-eval/mrbgem.rake b/mrbgems/mruby-eval/mrbgem.rake
index 7c6acc534..cb8835b32 100644
--- a/mrbgems/mruby-eval/mrbgem.rake
+++ b/mrbgems/mruby-eval/mrbgem.rake
@@ -2,4 +2,6 @@ MRuby::Gem::Specification.new('mruby-eval') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'standard Kernel#eval method'
+
+ add_dependency 'mruby-compiler', :core => 'mruby-compiler'
end
diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c
index daf5b2a1e..eef1f5046 100644
--- a/mrbgems/mruby-eval/src/eval.c
+++ b/mrbgems/mruby-eval/src/eval.c
@@ -1,27 +1,36 @@
-#include "mruby.h"
-#include "mruby/compile.h"
-#include "mruby/irep.h"
-#include "mruby/proc.h"
-#include "mruby/opcode.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/irep.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
static struct mrb_irep *
get_closure_irep(mrb_state *mrb, int level)
{
- struct REnv *e = mrb->c->ci[-1].proc->env;
+ struct mrb_context *c = mrb->c;
+ struct REnv *e = c->ci[-1].proc->env;
struct RProc *proc;
if (level == 0) {
- return mrb->c->ci[-1].proc->body.irep;
+ proc = c->ci[-1].proc;
+ if (MRB_PROC_CFUNC_P(proc)) {
+ return NULL;
+ }
+ return proc->body.irep;
}
-
+
while (--level) {
e = (struct REnv*)e->c;
if (!e) return NULL;
}
if (!e) return NULL;
- proc = mrb->c->cibase[e->cioff].proc;
+ proc = c->cibase[e->cioff].proc;
+ if (!proc || MRB_PROC_CFUNC_P(proc)) {
+ return NULL;
+ }
return proc->body.irep;
}
@@ -33,7 +42,7 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
int pos;
for (level = 0; (virep = get_closure_irep(mrb, level)); level++) {
- if (virep->lv == NULL) {
+ if (!virep || virep->lv == NULL) {
continue;
}
for (pos = 0; pos < virep->nlocals - 1; pos++) {
@@ -46,20 +55,46 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
return 0;
}
+static mrb_bool
+potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
+{
+ if (v >= nlocals) return FALSE;
+ /* skip arguments */
+ if (v < argc+1) return FALSE;
+ return TRUE;
+}
static void
patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest)
{
size_t i;
mrb_code c;
-
- for (i = 0; i < irep->rlen; i++) {
- patch_irep(mrb, irep->reps[i], bnest + 1);
- }
+ int argc = 0;
for (i = 0; i < irep->ilen; i++) {
c = irep->iseq[i];
switch(GET_OPCODE(c)){
+ case OP_ENTER:
+ {
+ mrb_aspec ax = GETARG_Ax(c);
+ /* extra 1 means a slot for block */
+ argc = MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1;
+ }
+ break;
+
+ case OP_EPUSH:
+ patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1);
+ break;
+
+ case OP_LAMBDA:
+ {
+ int arg_c = GETARG_c(c);
+ if (arg_c & OP_L_CAPTURE) {
+ patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1);
+ }
+ }
+ break;
+
case OP_SEND:
if (GETARG_C(c) != 0) {
break;
@@ -75,7 +110,7 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest)
case OP_MOVE:
/* src part */
- if (GETARG_B(c) < irep->nlocals) {
+ if (potential_upvar_p(irep->lv, GETARG_B(c), argc, irep->nlocals)) {
mrb_code arg = search_variable(mrb, irep->lv[GETARG_B(c) - 1].name, bnest);
if (arg != 0) {
/* must replace */
@@ -83,7 +118,7 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest)
}
}
/* dst part */
- if (GETARG_A(c) < irep->nlocals) {
+ if (potential_upvar_p(irep->lv, GETARG_A(c), argc, irep->nlocals)) {
mrb_code arg = search_variable(mrb, irep->lv[GETARG_A(c) - 1].name, bnest);
if (arg != 0) {
/* must replace */
@@ -102,6 +137,7 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, cha
struct mrb_parser_state *p;
struct RProc *proc;
struct REnv *e;
+ struct mrb_context *c = mrb->c;
if (!mrb_nil_p(binding)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "Binding of eval must be nil.");
@@ -112,11 +148,15 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, cha
if (file) {
mrbc_filename(mrb, cxt, file);
}
+ cxt->capture_errors = TRUE;
+ cxt->no_optimize = TRUE;
+
+ p = mrb_parse_nstring(mrb, s, len, cxt);
- p = mrb_parser_new(mrb);
- p->s = s;
- p->send = s + len;
- mrb_parser_parse(p, cxt);
+ /* only occur when memory ran out */
+ if (!p) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "Failed to create parser state.");
+ }
if (0 < p->nerr) {
/* parse error */
@@ -129,22 +169,29 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, cha
}
proc = mrb_generate_code(mrb, p);
- e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci[-1].proc->env);
- e->mid = mrb->c->ci[-1].mid;
- e->cioff = mrb->c->ci - mrb->c->cibase - 1;
- e->stack = mrb->c->ci->stackent;
- mrb->c->ci->env = e;
+ if (proc == NULL) {
+ /* codegen error */
+ mrb_parser_free(p);
+ mrbc_context_free(mrb, cxt);
+ mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error");
+ }
+ if (c->ci[-1].proc->target_class) {
+ proc->target_class = c->ci[-1].proc->target_class;
+ }
+ e = c->ci[-1].proc->env;
+ if (!e) e = c->ci[-1].env;
+ e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)e);
+ e->mid = c->ci[-1].mid;
+ e->cioff = c->ci - c->cibase - 1;
+ e->stack = c->ci->stackent;
+ MRB_SET_ENV_STACK_LEN(e, c->ci[-1].proc->body.irep->nlocals);
+ c->ci->env = e;
proc->env = e;
patch_irep(mrb, proc->body.irep, 0);
mrb_parser_free(p);
mrbc_context_free(mrb, cxt);
- if (proc == NULL) {
- /* codegen error */
- mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error");
- }
-
return proc;
}
@@ -170,10 +217,43 @@ f_eval(mrb_state *mrb, mrb_value self)
return ret;
}
+mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self);
+
+#define CI_ACC_SKIP -1
+
+static mrb_value
+f_instance_eval(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_context *c = mrb->c;
+ mrb_value b;
+ mrb_int argc; mrb_value *argv;
+
+ mrb_get_args(mrb, "*&", &argv, &argc, &b);
+
+ if (mrb_nil_p(b)) {
+ char *s;
+ mrb_int len;
+ char *file = NULL;
+ mrb_int line = 1;
+ mrb_value cv;
+
+ mrb_get_args(mrb, "s|zi", &s, &len, &file, &line);
+ c->ci->acc = CI_ACC_SKIP;
+ cv = mrb_singleton_class(mrb, self);
+ c->ci->target_class = mrb_class_ptr(cv);
+ return mrb_run(mrb, create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line), self);
+ }
+ else {
+ mrb_get_args(mrb, "&", &b);
+ return mrb_obj_instance_eval(mrb, self);
+ }
+}
+
void
mrb_mruby_eval_gem_init(mrb_state* mrb)
{
- mrb_define_module_function(mrb, mrb->kernel_module, "eval", f_eval, MRB_ARGS_REQ(1) | MRB_ARGS_OPT(3));
+ mrb_define_module_function(mrb, mrb->kernel_module, "eval", f_eval, MRB_ARGS_ARG(1, 3));
+ mrb_define_method(mrb, mrb->kernel_module, "instance_eval", f_instance_eval, MRB_ARGS_ARG(1, 2));
}
void
diff --git a/mrbgems/mruby-eval/test/eval.rb b/mrbgems/mruby-eval/test/eval.rb
index c0338d644..17860b132 100644
--- a/mrbgems/mruby-eval/test/eval.rb
+++ b/mrbgems/mruby-eval/test/eval.rb
@@ -32,6 +32,11 @@ assert('Kernel.eval', '15.3.1.2.3') do
}.call
c
}
+ assert_equal(2) {
+ a = 10
+ Kernel.eval 'def f(a); b=a.send(:+, 1); end'
+ f(1)
+ }
end
assert('Kernel#eval', '15.3.1.3.12') do
@@ -44,3 +49,51 @@ assert('rest arguments of eval') do
Kernel.eval('[\'test\', __FILE__, __LINE__]', nil, 'test.rb', 10)
end
end
+
+assert 'eval syntax error' do
+ assert_raise(SyntaxError) do
+ eval 'p "test'
+ end
+end
+
+assert('String instance_eval') do
+ obj = Object.new
+ obj.instance_variable_set :@test, 'test'
+ assert_raise(ArgumentError) { obj.instance_eval(0) { } }
+ assert_raise(ArgumentError) { obj.instance_eval('0', 'test', 0, 'test') }
+ assert_equal(['test.rb', 10]) { obj.instance_eval('[__FILE__, __LINE__]', 'test.rb', 10)}
+ assert_equal('test') { obj.instance_eval('@test') }
+ assert_equal('test') { obj.instance_eval { @test } }
+end
+
+assert('Kernel.#eval(string) context') do
+ class TestEvalConstScope
+ EVAL_CONST_CLASS = 'class'
+ def const_string
+ eval 'EVAL_CONST_CLASS'
+ end
+ end
+ obj = TestEvalConstScope.new
+ assert_raise(NameError) { eval 'EVAL_CONST_CLASS' }
+ assert_equal('class') { obj.const_string }
+end
+
+assert('Object#instance_eval with begin-rescue-ensure execution order') do
+ class HellRaiser
+ def raise_hell
+ order = [:enter_raise_hell]
+ begin
+ order.push :begin
+ self.instance_eval("raise 'error'")
+ rescue
+ order.push :rescue
+ ensure
+ order.push :ensure
+ end
+ order
+ end
+ end
+
+ hell_raiser = HellRaiser.new
+ assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell)
+end
diff --git a/mrbgems/mruby-exit/src/mruby-exit.c b/mrbgems/mruby-exit/src/mruby-exit.c
index 726dfd7c4..3e147f80b 100644
--- a/mrbgems/mruby-exit/src/mruby-exit.c
+++ b/mrbgems/mruby-exit/src/mruby-exit.c
@@ -1,5 +1,5 @@
#include <stdlib.h>
-#include "mruby.h"
+#include <mruby.h>
static mrb_value
f_exit(mrb_state *mrb, mrb_value self)
diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c
index 9bd15f4b6..c140b6eac 100644
--- a/mrbgems/mruby-fiber/src/fiber.c
+++ b/mrbgems/mruby-fiber/src/fiber.c
@@ -1,7 +1,7 @@
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
#define fiber_ptr(o) ((struct RFiber*)mrb_ptr(o))
@@ -69,6 +69,7 @@ fiber_init(mrb_state *mrb, mrb_value self)
struct RProc *p;
mrb_callinfo *ci;
mrb_value blk;
+ size_t slen;
mrb_get_args(mrb, "&", &blk);
@@ -85,10 +86,28 @@ fiber_init(mrb_state *mrb, mrb_value self)
c = f->cxt;
/* initialize VM stack */
- c->stbase = (mrb_value *)mrb_calloc(mrb, FIBER_STACK_INIT_SIZE, sizeof(mrb_value));
- c->stend = c->stbase + FIBER_STACK_INIT_SIZE;
+ slen = FIBER_STACK_INIT_SIZE;
+ if (p->body.irep->nregs > slen) {
+ slen += p->body.irep->nregs;
+ }
+ c->stbase = (mrb_value *)mrb_malloc(mrb, slen*sizeof(mrb_value));
+ c->stend = c->stbase + slen;
c->stack = c->stbase;
+#ifdef MRB_NAN_BOXING
+ {
+ mrb_value *p = c->stbase;
+ mrb_value *pend = c->stend;
+
+ while (p < pend) {
+ SET_NIL_VALUE(*p);
+ p++;
+ }
+ }
+#else
+ memset(c->stbase, 0, slen * sizeof(mrb_value));
+#endif
+
/* copy receiver from a block */
c->stack[0] = mrb->c->stack[0];
@@ -148,7 +167,7 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
}
}
if (resume && c->status == MRB_FIBER_TRANSFERRED) {
- mrb_raise(mrb, E_FIBER_ERROR, "resuming transfered fiber");
+ mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
}
if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMING) {
mrb_raise(mrb, E_FIBER_ERROR, "double resume");
@@ -209,6 +228,13 @@ fiber_resume(mrb_state *mrb, mrb_value self)
return fiber_switch(mrb, self, len, a, TRUE);
}
+/* resume thread with given arguments */
+MRB_API mrb_value
+mrb_fiber_resume(mrb_state *mrb, mrb_value fib, mrb_int len, const mrb_value *a)
+{
+ return fiber_switch(mrb, fib, len, a, TRUE);
+}
+
/*
* call-seq:
* fiber.alive? -> true or false
@@ -239,8 +265,8 @@ fiber_eq(mrb_state *mrb, mrb_value self)
* call-seq:
* fiber.transfer(args, ...) -> obj
*
- * Transfers control to reciever fiber of the method call.
- * Unlike <code>resume</code> the reciever wouldn't be pushed to call
+ * Transfers control to receiver fiber of the method call.
+ * Unlike <code>resume</code> the receiver wouldn't be pushed to call
* stack of fibers. Instead it will switch to the call stack of
* transferring fiber.
* When resuming a fiber that was transferred to another fiber it would
@@ -261,6 +287,7 @@ fiber_transfer(mrb_state *mrb, mrb_value self)
mrb->c = c;
c->status = MRB_FIBER_RUNNING;
MARK_CONTEXT_MODIFY(c);
+ mrb_write_barrier(mrb, (struct RBasic*)c->fib);
return fiber_result(mrb, a, len);
}
@@ -271,7 +298,9 @@ fiber_transfer(mrb_state *mrb, mrb_value self)
return fiber_switch(mrb, self, len, a, FALSE);
}
-mrb_value
+/* yield values to the caller fiber */
+/* mrb_fiber_yield() must be called as `return mrb_fiber_yield(...)` */
+MRB_API mrb_value
mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a)
{
struct mrb_context *c = mrb->c;
@@ -291,6 +320,7 @@ mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a)
mrb->c = c->prev;
c->prev = NULL;
MARK_CONTEXT_MODIFY(mrb->c);
+ mrb_write_barrier(mrb, (struct RBasic*)c->fib);
return fiber_result(mrb, a, len);
}
diff --git a/mrbgems/mruby-hash-ext/mrbgem.rake b/mrbgems/mruby-hash-ext/mrbgem.rake
index e1ce8e767..f01033a6c 100644
--- a/mrbgems/mruby-hash-ext/mrbgem.rake
+++ b/mrbgems/mruby-hash-ext/mrbgem.rake
@@ -1,5 +1,7 @@
MRuby::Gem::Specification.new('mruby-hash-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extensional Hash class'
+ spec.summary = 'Hash class extension'
+ spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext'
+ spec.add_dependency 'mruby-array-ext', :core => 'mruby-array-ext'
end
diff --git a/mrbgems/mruby-hash-ext/mrblib/hash.rb b/mrbgems/mruby-hash-ext/mrblib/hash.rb
index 9da08dc3a..2727e1f65 100644
--- a/mrbgems/mruby-hash-ext/mrblib/hash.rb
+++ b/mrbgems/mruby-hash-ext/mrblib/hash.rb
@@ -5,6 +5,82 @@ class Hash
##
# call-seq:
+ # Hash[ key, value, ... ] -> new_hash
+ # Hash[ [ [key, value], ... ] ] -> new_hash
+ # Hash[ object ] -> new_hash
+ #
+ # Creates a new hash populated with the given objects.
+ #
+ # Similar to the literal `{ _key_ => _value_, ... }`. In the first
+ # form, keys and values occur in pairs, so there must be an even number of
+ # arguments.
+ #
+ # The second and third form take a single argument which is either an array
+ # of key-value pairs or an object convertible to a hash.
+ #
+ # Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200}
+ # Hash[ [ ["a", 100], ["b", 200] ] ] #=> {"a"=>100, "b"=>200}
+ # Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200}
+ #
+
+ def self.[](*object)
+ length = object.length
+ if length == 1
+ o = object[0]
+ if o.respond_to?(:to_hash)
+ h = Hash.new
+ object[0].to_hash.each { |k, v| h[k] = v }
+ return h
+ elsif o.respond_to?(:to_a)
+ h = Hash.new
+ o.to_a.each do |i|
+ raise ArgumentError, "wrong element type #{i.class} (expected array)" unless i.respond_to?(:to_a)
+ k, v = nil
+ case i.size
+ when 2
+ k = i[0]
+ v = i[1]
+ when 1
+ k = i[0]
+ else
+ raise ArgumentError, "invalid number of elements (#{i.size} for 1..2)"
+ end
+ h[k] = v
+ end
+ return h
+ end
+ end
+ unless length % 2 == 0
+ raise ArgumentError, 'odd number of arguments for Hash'
+ end
+ h = Hash.new
+ 0.step(length - 2, 2) do |i|
+ h[object[i]] = object[i + 1]
+ end
+ h
+ end
+
+ ##
+ # call-seq:
+ # Hash.try_convert(obj) -> hash or nil
+ #
+ # Try to convert <i>obj</i> into a hash, using to_hash method.
+ # Returns converted hash or nil if <i>obj</i> cannot be converted
+ # for any reason.
+ #
+ # Hash.try_convert({1=>2}) # => {1=>2}
+ # Hash.try_convert("1=>2") # => nil
+ #
+ def self.try_convert(obj)
+ if obj.respond_to?(:to_hash)
+ obj.to_hash
+ else
+ nil
+ end
+ end
+
+ ##
+ # call-seq:
# hsh.merge!(other_hash) -> hsh
# hsh.merge!(other_hash){|key, oldval, newval| block} -> hsh
#
@@ -62,18 +138,18 @@ class Hash
#
# <em>produces:</em>
#
- # prog.rb:2:in `fetch': key not found (KeyError)
+ # prog.rb:2:in 'fetch': key not found (KeyError)
# from prog.rb:2
#
def fetch(key, none=NONE, &block)
unless self.key?(key)
if block
- block.call
+ block.call(key)
elsif none != NONE
none
else
- raise RuntimeError, "Key not found: #{key}"
+ raise KeyError, "Key not found: #{key}"
end
else
self[key]
@@ -99,7 +175,7 @@ class Hash
self.each do |k, v|
self.delete(k) if block.call(k, v)
- end
+ end
self
end
@@ -193,4 +269,100 @@ class Hash
def to_h
self
end
+
+ ##
+ # call-seq:
+ # hash < other -> true or false
+ #
+ # Returns <code>true</code> if <i>hash</i> is subset of
+ # <i>other</i>.
+ #
+ # h1 = {a:1, b:2}
+ # h2 = {a:1, b:2, c:3}
+ # h1 < h2 #=> true
+ # h2 < h1 #=> false
+ # h1 < h1 #=> false
+ #
+ def <(hash)
+ begin
+ hash = hash.to_hash
+ rescue NoMethodError
+ raise TypeError, "can't convert #{hash.class} to Hash"
+ end
+ size < hash.size and all? {|key, val|
+ hash.key?(key) and hash[key] == val
+ }
+ end
+
+ ##
+ # call-seq:
+ # hash <= other -> true or false
+ #
+ # Returns <code>true</code> if <i>hash</i> is subset of
+ # <i>other</i> or equals to <i>other</i>.
+ #
+ # h1 = {a:1, b:2}
+ # h2 = {a:1, b:2, c:3}
+ # h1 <= h2 #=> true
+ # h2 <= h1 #=> false
+ # h1 <= h1 #=> true
+ #
+ def <=(hash)
+ begin
+ hash = hash.to_hash
+ rescue NoMethodError
+ raise TypeError, "can't convert #{hash.class} to Hash"
+ end
+ size <= hash.size and all? {|key, val|
+ hash.key?(key) and hash[key] == val
+ }
+ end
+
+ ##
+ # call-seq:
+ # hash > other -> true or false
+ #
+ # Returns <code>true</code> if <i>other</i> is subset of
+ # <i>hash</i>.
+ #
+ # h1 = {a:1, b:2}
+ # h2 = {a:1, b:2, c:3}
+ # h1 > h2 #=> false
+ # h2 > h1 #=> true
+ # h1 > h1 #=> false
+ #
+ def >(hash)
+ begin
+ hash = hash.to_hash
+ rescue NoMethodError
+ raise TypeError, "can't convert #{hash.class} to Hash"
+ end
+ size > hash.size and hash.all? {|key, val|
+ key?(key) and self[key] == val
+ }
+ end
+
+ ##
+ # call-seq:
+ # hash >= other -> true or false
+ #
+ # Returns <code>true</code> if <i>other</i> is subset of
+ # <i>hash</i> or equals to <i>hash</i>.
+ #
+ # h1 = {a:1, b:2}
+ # h2 = {a:1, b:2, c:3}
+ # h1 >= h2 #=> false
+ # h2 >= h1 #=> true
+ # h1 >= h1 #=> true
+ #
+ def >=(hash)
+ begin
+ hash = hash.to_hash
+ rescue NoMethodError
+ raise TypeError, "can't convert #{hash.class} to Hash"
+ end
+ size >= hash.size and hash.all? {|key, val|
+ key?(key) and self[key] == val
+ }
+ end
end
diff --git a/mrbgems/mruby-hash-ext/src/hash-ext.c b/mrbgems/mruby-hash-ext/src/hash-ext.c
index bce842cb4..9560829b6 100644
--- a/mrbgems/mruby-hash-ext/src/hash-ext.c
+++ b/mrbgems/mruby-hash-ext/src/hash-ext.c
@@ -4,9 +4,9 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/hash.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
/*
* call-seq:
diff --git a/mrbgems/mruby-hash-ext/test/hash.rb b/mrbgems/mruby-hash-ext/test/hash.rb
index 2bc5b911a..8b6b2e5b9 100644
--- a/mrbgems/mruby-hash-ext/test/hash.rb
+++ b/mrbgems/mruby-hash-ext/test/hash.rb
@@ -1,6 +1,50 @@
##
# Hash(Ext) Test
+assert('Hash.[] Hash') do
+ a = Hash['a_key' => 'a_value']
+
+ assert_equal({'a_key' => 'a_value'}, a)
+end
+
+assert('Hash.[] [ [ ["b_key", "b_value" ] ] ]') do
+ a = Hash[ [ ['b_key', 'b_value'] ] ]
+
+ assert_equal({'b_key' => 'b_value'}, a)
+
+ a = Hash[ [ ] ]
+
+ assert_equal({}, a)
+
+ assert_raise(ArgumentError) do
+ Hash[ [ ['b_key', 'b_value', 'b_over'] ] ]
+ end
+
+ assert_raise(ArgumentError) do
+ Hash[ [ [] ] ]
+ end
+end
+
+assert('Hash.[] "c_key", "c_value"') do
+ a = Hash['c_key', 'c_value', 'd_key', 1]
+
+ assert_equal({'c_key' => 'c_value', 'd_key' => 1}, a)
+
+ a = Hash[]
+
+ assert_equal({}, a)
+
+ assert_raise(ArgumentError) do
+ Hash['d_key']
+ end
+end
+
+assert('Hash.try_convert') do
+ assert_nil Hash.try_convert(nil)
+ assert_nil Hash.try_convert("{1=>2}")
+ assert_equal({1=>2}, Hash.try_convert({1=>2}))
+end
+
assert('Hash#merge!') do
a = { 'abc_key' => 'abc_value', 'cba_key' => 'cba_value' }
b = { 'cba_key' => 'XXX', 'xyz_key' => 'xyz_value' }
@@ -37,10 +81,9 @@ assert('Hash#fetch') do
assert_equal "feline", h.fetch("cat")
assert_equal "mickey", h.fetch("mouse", "mickey")
assert_equal "minny", h.fetch("mouse"){"minny"}
- begin
+ assert_equal "mouse", h.fetch("mouse"){|k| k}
+ assert_raise(KeyError) do
h.fetch("gnu")
- rescue => e
- assert_kind_of(StandardError, e);
end
end
@@ -121,3 +164,75 @@ assert("Hash#to_h") do
assert_equal Hash, h.to_h.class
assert_equal h, h.to_h
end
+
+assert('Hash#<') do
+ h1 = {a:1, b:2}
+ h2 = {a:1, b:2, c:3}
+
+ assert_false(h1 < h1)
+ assert_true(h1 < h2)
+ assert_false(h2 < h1)
+ assert_false(h2 < h2)
+
+ h1 = {a:1}
+ h2 = {a:2}
+
+ assert_false(h1 < h1)
+ assert_false(h1 < h2)
+ assert_false(h2 < h1)
+ assert_false(h2 < h2)
+end
+
+assert('Hash#<=') do
+ h1 = {a:1, b:2}
+ h2 = {a:1, b:2, c:3}
+
+ assert_true(h1 <= h1)
+ assert_true(h1 <= h2)
+ assert_false(h2 <= h1)
+ assert_true(h2 <= h2)
+
+ h1 = {a:1}
+ h2 = {a:2}
+
+ assert_true(h1 <= h1)
+ assert_false(h1 <= h2)
+ assert_false(h2 <= h1)
+ assert_true(h2 <= h2)
+end
+
+assert('Hash#>=') do
+ h1 = {a:1, b:2}
+ h2 = {a:1, b:2, c:3}
+
+ assert_true(h1 >= h1)
+ assert_false(h1 >= h2)
+ assert_true(h2 >= h1)
+ assert_true(h2 >= h2)
+
+ h1 = {a:1}
+ h2 = {a:2}
+
+ assert_true(h1 >= h1)
+ assert_false(h1 >= h2)
+ assert_false(h2 >= h1)
+ assert_true(h2 >= h2)
+end
+
+assert('Hash#>') do
+ h1 = {a:1, b:2}
+ h2 = {a:1, b:2, c:3}
+
+ assert_false(h1 > h1)
+ assert_false(h1 > h2)
+ assert_true(h2 > h1)
+ assert_false(h2 > h2)
+
+ h1 = {a:1}
+ h2 = {a:2}
+
+ assert_false(h1 > h1)
+ assert_false(h1 > h2)
+ assert_false(h2 > h1)
+ assert_false(h2 > h2)
+end
diff --git a/mrbgems/mruby-kernel-ext/mrbgem.rake b/mrbgems/mruby-kernel-ext/mrbgem.rake
index ab610c02b..fcb3a83b0 100644
--- a/mrbgems/mruby-kernel-ext/mrbgem.rake
+++ b/mrbgems/mruby-kernel-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-kernel-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extensional Kernel module'
+ spec.summary = 'Kernel module extension'
end
diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c
index a6ecd72bd..74aa555d0 100644
--- a/mrbgems/mruby-kernel-ext/src/kernel.c
+++ b/mrbgems/mruby-kernel-ext/src/kernel.c
@@ -1,5 +1,7 @@
-#include "mruby.h"
-#include "mruby/error.h"
+#include <mruby.h>
+#include <mruby/error.h>
+#include <mruby/array.h>
+#include <mruby/hash.h>
/*
* call-seq:
@@ -21,6 +23,147 @@ mrb_f_method(mrb_state *mrb, mrb_value self)
return mrb_nil_value();
}
+/*
+ * call-seq:
+ * Integer(arg,base=0) -> integer
+ *
+ * Converts <i>arg</i> to a <code>Fixnum</code>.
+ * Numeric types are converted directly (with floating point numbers
+ * being truncated). <i>base</i> (0, or between 2 and 36) is a base for
+ * integer string representation. If <i>arg</i> is a <code>String</code>,
+ * when <i>base</i> is omitted or equals to zero, radix indicators
+ * (<code>0</code>, <code>0b</code>, and <code>0x</code>) are honored.
+ * In any case, strings should be strictly conformed to numeric
+ * representation. This behavior is different from that of
+ * <code>String#to_i</code>. Non string values will be converted using
+ * <code>to_int</code>, and <code>to_i</code>. Passing <code>nil</code>
+ * raises a TypeError.
+ *
+ * Integer(123.999) #=> 123
+ * Integer("0x1a") #=> 26
+ * Integer(Time.new) #=> 1204973019
+ * Integer("0930", 10) #=> 930
+ * Integer("111", 2) #=> 7
+ * Integer(nil) #=> TypeError
+ */
+static mrb_value
+mrb_f_integer(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg;
+ mrb_int base = 0;
+
+ mrb_get_args(mrb, "o|i", &arg, &base);
+ return mrb_convert_to_integer(mrb, arg, base);
+}
+
+/*
+ * call-seq:
+ * Float(arg) -> float
+ *
+ * Returns <i>arg</i> converted to a float. Numeric types are converted
+ * directly, the rest are converted using <i>arg</i>.to_f.
+ *
+ * Float(1) #=> 1.0
+ * Float(123.456) #=> 123.456
+ * Float("123.456") #=> 123.456
+ * Float(nil) #=> TypeError
+ */
+static mrb_value
+mrb_f_float(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg;
+
+ mrb_get_args(mrb, "o", &arg);
+ return mrb_Float(mrb, arg);
+}
+
+/*
+ * call-seq:
+ * String(arg) -> string
+ *
+ * Returns <i>arg</i> as an <code>String</code>.
+ *
+ * First tries to call its <code>to_str</code> method, then its to_s method.
+ *
+ * String(self) #=> "main"
+ * String(self.class) #=> "Object"
+ * String(123456) #=> "123456"
+ */
+static mrb_value
+mrb_f_string(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg, tmp;
+
+ mrb_get_args(mrb, "o", &arg);
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_str");
+ if (mrb_nil_p(tmp)) {
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_s");
+ }
+ return tmp;
+}
+
+/*
+ * call-seq:
+ * Array(arg) -> array
+ *
+ * Returns +arg+ as an Array.
+ *
+ * First tries to call Array#to_ary on +arg+, then Array#to_a.
+ *
+ * Array(1..5) #=> [1, 2, 3, 4, 5]
+ *
+ */
+static mrb_value
+mrb_f_array(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg, tmp;
+
+ mrb_get_args(mrb, "o", &arg);
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_ary");
+ if (mrb_nil_p(tmp)) {
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_ARRAY, "Array", "to_a");
+ }
+ if (mrb_nil_p(tmp)) {
+ return mrb_ary_new_from_values(mrb, 1, &arg);
+ }
+
+ return tmp;
+}
+
+/*
+ * call-seq:
+ * Hash(arg) -> hash
+ *
+ * Converts <i>arg</i> to a <code>Hash</code> by calling
+ * <i>arg</i><code>.to_hash</code>. Returns an empty <code>Hash</code> when
+ * <i>arg</i> is <tt>nil</tt> or <tt>[]</tt>.
+ *
+ * Hash([]) #=> {}
+ * Hash(nil) #=> {}
+ * Hash(key: :value) #=> {:key => :value}
+ * Hash([1, 2, 3]) #=> TypeError
+ *
+ */
+static mrb_value
+mrb_f_hash(mrb_state *mrb, mrb_value self)
+{
+ mrb_value arg, tmp;
+
+ mrb_get_args(mrb, "o", &arg);
+ if (mrb_nil_p(arg)) {
+ return mrb_hash_new(mrb);
+ }
+ tmp = mrb_check_convert_type(mrb, arg, MRB_TT_HASH, "Hash", "to_hash");
+ if (mrb_nil_p(tmp)) {
+ if (mrb_array_p(arg) && RARRAY_LEN(arg) == 0) {
+ return mrb_hash_new(mrb);
+ }
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into Hash",
+ mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, arg)));
+ }
+ return tmp;
+}
+
void
mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
{
@@ -28,6 +171,11 @@ mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_OPT(2));
mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE());
+ mrb_define_module_function(mrb, krn, "Integer", mrb_f_integer, MRB_ARGS_ANY());
+ mrb_define_module_function(mrb, krn, "Float", mrb_f_float, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, krn, "String", mrb_f_string, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, krn, "Array", mrb_f_array, MRB_ARGS_REQ(1));
+ mrb_define_module_function(mrb, krn, "Hash", mrb_f_hash, MRB_ARGS_REQ(1));
}
void
diff --git a/mrbgems/mruby-kernel-ext/test/kernel.rb b/mrbgems/mruby-kernel-ext/test/kernel.rb
index 0e47e3b57..c58fb465c 100644
--- a/mrbgems/mruby-kernel-ext/test/kernel.rb
+++ b/mrbgems/mruby-kernel-ext/test/kernel.rb
@@ -16,3 +16,38 @@ assert('Kernel#__method__') do
assert_equal(:m1, c.new.m1)
assert_equal(:m2, c.new.m2)
end
+
+assert('Kernel#Integer') do
+ assert_equal(123, Integer(123.999))
+ assert_equal(26, Integer("0x1a"))
+ assert_equal(930, Integer("0930", 10))
+ assert_equal(7, Integer("111", 2))
+ assert_raise(TypeError) { Integer(nil) }
+end
+
+assert('Kernel#Float') do
+ assert_equal(1.0, Float(1))
+ assert_equal(123.456, Float(123.456))
+ assert_equal(123.456, Float("123.456"))
+ assert_raise(TypeError) { Float(nil) }
+end
+
+assert('Kernel#String') do
+ assert_equal("main", String(self))
+ assert_equal("Object", String(self.class))
+ assert_equal("123456", String(123456))
+end
+
+assert('Kernel#Array') do
+ assert_equal([1], Kernel.Array(1))
+ assert_equal([1, 2, 3, 4, 5], Kernel.Array([1, 2, 3, 4, 5]))
+ assert_equal([1, 2, 3, 4, 5], Kernel.Array(1..5))
+ assert_equal([[:a, 1], [:b, 2], [:c, 3]], Kernel.Array({a:1, b:2, c:3}))
+end
+
+assert('Kernel#Hash') do
+ assert_equal({}, Hash([]))
+ assert_equal({}, Hash(nil))
+ assert_equal({:key => :value}, Hash(key: :value))
+ assert_raise(TypeError) { Hash([1, 2, 3]) }
+end
diff --git a/mrbgems/mruby-math/src/math.c b/mrbgems/mruby-math/src/math.c
index 871cba301..7302b92d7 100644
--- a/mrbgems/mruby-math/src/math.c
+++ b/mrbgems/mruby-math/src/math.c
@@ -4,8 +4,8 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/array.h>
#include <math.h>
@@ -19,14 +19,80 @@ domain_error(mrb_state *mrb, const char *func)
}
/* math functions not provided by Microsoft Visual C++ 2012 or older */
-#if defined _MSC_VER && _MSC_VER < 1800
+#if defined _MSC_VER && _MSC_VER <= 1700
+
+#include <float.h>
#define MATH_TOLERANCE 1E-12
-#define asinh(x) log(x + sqrt(pow(x,2.0) + 1))
-#define acosh(x) log(x + sqrt(pow(x,2.0) - 1))
-#define atanh(x) (log(1+x) - log(1-x))/2.0
-#define cbrt(x) pow(x,1.0/3.0)
+double
+asinh(double x)
+{
+ double xa, ya, y;
+
+ /* Basic formula loses precision for x < 0, but asinh is an odd function */
+ xa = fabs(x);
+ if (xa > 3.16227E+18) {
+ /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */
+ ya = log(xa) + 0.69314718055994530942;
+ }
+ else {
+ /* Basic formula for asinh */
+ ya = log(xa + sqrt(xa*xa + 1.0));
+ }
+
+ y = _copysign(ya, x);
+ return y;
+}
+
+double
+acosh(double x)
+{
+ double y;
+
+ if (x > 3.16227E+18) {
+ /* Prevent x*x from overflowing; basic formula reduces to log(2*x) */
+ y = log(x) + 0.69314718055994530942;
+ }
+ else {
+ /* Basic formula for acosh */
+ y = log(x + sqrt(x*x - 1.0));
+ }
+
+ return y;
+}
+
+double
+atanh(double x)
+{
+ double y;
+
+ if (fabs(x) < 1E-2) {
+ /* The sums 1+x and 1-x lose precision for small x. Use the polynomial
+ instead. */
+ double x2 = x * x;
+ y = x*(1.0 + x2*(1.0/3.0 + x2*(1.0/5.0 + x2*(1.0/7.0))));
+ }
+ else {
+ /* Basic formula for atanh */
+ y = 0.5 * (log(1.0+x) - log(1.0-x));
+ }
+
+ return y;
+}
+
+double
+cbrt(double x)
+{
+ double xa, ya, y;
+
+ /* pow(x, y) is undefined for x < 0 and y not an integer, but cbrt is an
+ odd function */
+ xa = fabs(x);
+ ya = pow(xa, 1.0/3.0);
+ y = _copysign(ya, x);
+ return y;
+}
/* Declaration of complementary Error function */
double
@@ -170,7 +236,8 @@ math_tan(mrb_state *mrb, mrb_value obj)
* call-seq:
* Math.asin(x) -> float
*
- * Computes the arc sine of <i>x</i>. Returns -{PI/2} .. {PI/2}.
+ * Computes the arc sine of <i>x</i>.
+ * @return computed value between `-(PI/2)` and `(PI/2)`.
*/
static mrb_value
math_asin(mrb_state *mrb, mrb_value obj)
@@ -210,7 +277,7 @@ math_acos(mrb_state *mrb, mrb_value obj)
* call-seq:
* Math.atan(x) -> float
*
- * Computes the arc tangent of <i>x</i>. Returns -{PI/2} .. {PI/2}.
+ * Computes the arc tangent of <i>x</i>. Returns `-(PI/2) .. (PI/2)`.
*/
static mrb_value
math_atan(mrb_state *mrb, mrb_value obj)
diff --git a/mrbgems/mruby-math/test/math.rb b/mrbgems/mruby-math/test/math.rb
index 1cc3a20b0..e9ea07cc1 100644
--- a/mrbgems/mruby-math/test/math.rb
+++ b/mrbgems/mruby-math/test/math.rb
@@ -23,6 +23,22 @@ assert('Math.sin PI/2') do
check_float(Math.sin(Math::PI / 2), 1)
end
+assert('Math.cos 0') do
+ check_float(Math.cos(0), 1)
+end
+
+assert('Math.cos PI/2') do
+ check_float(Math.cos(Math::PI / 2), 0)
+end
+
+assert('Math.tan 0') do
+ check_float(Math.tan(0), 0)
+end
+
+assert('Math.tan PI/4') do
+ check_float(Math.tan(Math::PI / 4), 1)
+end
+
assert('Fundamental trig identities') do
result = true
N = 13
diff --git a/mrbgems/mruby-numeric-ext/mrbgem.rake b/mrbgems/mruby-numeric-ext/mrbgem.rake
index 3d8be7cd5..6db7e589e 100644
--- a/mrbgems/mruby-numeric-ext/mrbgem.rake
+++ b/mrbgems/mruby-numeric-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-numeric-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extensional Numeric class'
+ spec.summary = 'Numeric class extension'
end
diff --git a/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb b/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
index dfc6ba87c..0bf3c6ab6 100644
--- a/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
+++ b/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
@@ -2,4 +2,16 @@ module Integral
def div(other)
self.divmod(other)[0]
end
+
+ def zero?
+ self == 0
+ end
+
+ def nonzero?
+ if self == 0
+ nil
+ else
+ self
+ end
+ end
end
diff --git a/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
index 22c1668fa..f71236a32 100644
--- a/mrbgems/mruby-numeric-ext/src/numeric_ext.c
+++ b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
@@ -1,5 +1,5 @@
#include <limits.h>
-#include "mruby.h"
+#include <mruby.h>
static mrb_value
mrb_int_chr(mrb_state *mrb, mrb_value x)
diff --git a/mrbgems/mruby-numeric-ext/test/numeric.rb b/mrbgems/mruby-numeric-ext/test/numeric.rb
index 4d9e83113..8bead2489 100644
--- a/mrbgems/mruby-numeric-ext/test/numeric.rb
+++ b/mrbgems/mruby-numeric-ext/test/numeric.rb
@@ -5,13 +5,8 @@ assert('Integer#chr') do
assert_equal("A", 65.chr)
assert_equal("B", 0x42.chr)
- if "こんにちわ世界".size == 7 then
- # UTF-8 gem is configured
- assert_raise(RangeError) { 0x110000.chr }
- else
- # multibyte encoding (not support yet)
- assert_raise(RangeError) { 256.chr }
- end
+ # multibyte encoding (not support yet)
+ assert_raise(RangeError) { 256.chr }
end
assert('Integer#div') do
@@ -21,3 +16,13 @@ end
assert('Float#div') do
assert_float 52, 365.2425.div(7)
end
+
+assert('Integer#zero?') do
+ assert_equal true, 0.zero?
+ assert_equal false, 1.zero?
+end
+
+assert('Integer#nonzero?') do
+ assert_equal nil, 0.nonzero?
+ assert_equal 1000, 1000.nonzero?
+end
diff --git a/mrbgems/mruby-object-ext/mrbgem.rake b/mrbgems/mruby-object-ext/mrbgem.rake
index 91a6e7ff1..6d14b4a51 100644
--- a/mrbgems/mruby-object-ext/mrbgem.rake
+++ b/mrbgems/mruby-object-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-object-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extensional Object class'
+ spec.summary = 'Object class extension'
end
diff --git a/mrbgems/mruby-object-ext/src/object.c b/mrbgems/mruby-object-ext/src/object.c
index 0dcc3cba7..3eba95633 100644
--- a/mrbgems/mruby-object-ext/src/object.c
+++ b/mrbgems/mruby-object-ext/src/object.c
@@ -1,6 +1,6 @@
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
/*
* call-seq:
@@ -12,7 +12,7 @@
static mrb_value
nil_to_a(mrb_state *mrb, mrb_value obj)
{
- return mrb_ary_new(mrb);
+ return mrb_ary_new(mrb);
}
/*
@@ -25,7 +25,7 @@ nil_to_a(mrb_state *mrb, mrb_value obj)
static mrb_value
nil_to_f(mrb_state *mrb, mrb_value obj)
{
- return mrb_float_value(mrb, 0.0);
+ return mrb_float_value(mrb, 0.0);
}
/*
@@ -38,7 +38,7 @@ nil_to_f(mrb_state *mrb, mrb_value obj)
static mrb_value
nil_to_i(mrb_state *mrb, mrb_value obj)
{
- return mrb_fixnum_value(0);
+ return mrb_fixnum_value(0);
}
/*
diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c
index ba22fc990..d5cd4f5a1 100644
--- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c
+++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c
@@ -1,7 +1,7 @@
-#include "mruby.h"
-#include "mruby/gc.h"
-#include "mruby/hash.h"
-#include "mruby/class.h"
+#include <mruby.h>
+#include <mruby/gc.h>
+#include <mruby/hash.h>
+#include <mruby/class.h>
struct os_count_struct {
mrb_int total;
@@ -17,7 +17,7 @@ os_count_object_type(mrb_state *mrb, struct RBasic *obj, void *data)
obj_count->total++;
- if (is_dead(mrb, obj)) {
+ if (mrb_object_dead_p(mrb, obj)) {
obj_count->freed++;
}
else {
@@ -115,10 +115,22 @@ os_each_object_cb(mrb_state *mrb, struct RBasic *obj, void *ud)
struct os_each_object_data *d = (struct os_each_object_data*)ud;
/* filter dead objects */
- if (is_dead(mrb, obj)) {
+ if (mrb_object_dead_p(mrb, obj)) {
return;
}
+ /* filter internal objects */
+ switch (obj->tt) {
+ case MRB_TT_ENV:
+ case MRB_TT_ICLASS:
+ return;
+ default:
+ break;
+ }
+
+ /* filter half baked (or internal) objects */
+ if (!obj->c) return;
+
/* filter class kind if target module defined */
if (d->target_module && !mrb_obj_is_kind_of(mrb, mrb_obj_value(obj), d->target_module)) {
return;
diff --git a/mrbgems/mruby-objectspace/test/objectspace.rb b/mrbgems/mruby-objectspace/test/objectspace.rb
index f792c165d..4731d53b5 100644
--- a/mrbgems/mruby-objectspace/test/objectspace.rb
+++ b/mrbgems/mruby-objectspace/test/objectspace.rb
@@ -54,3 +54,7 @@ assert('ObjectSpace.each_object') do
assert_equal arys.length, arys_count
assert_true arys.length < objs.length
end
+
+assert 'Check class pointer of ObjectSpace.each_object.' do
+ ObjectSpace.each_object { |obj| !obj }
+end
diff --git a/mrbgems/mruby-print/src/print.c b/mrbgems/mruby-print/src/print.c
index 673ba2172..f69254076 100644
--- a/mrbgems/mruby-print/src/print.c
+++ b/mrbgems/mruby-print/src/print.c
@@ -1,17 +1,34 @@
-#include "mruby.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/string.h>
#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#if defined(__MINGW32__) || defined(__MINGW64__)
+# include <windows.h>
+# include <io.h>
+#endif
static void
printstr(mrb_state *mrb, mrb_value obj)
{
- char *s;
- mrb_int len;
-
if (mrb_string_p(obj)) {
- s = RSTRING_PTR(obj);
- len = RSTRING_LEN(obj);
- fwrite(s, len, 1, stdout);
+#if defined(__MINGW32__) || defined(__MINGW64__)
+ if (isatty(fileno(stdout))) {
+ DWORD written;
+ int mlen = RSTRING_LEN(obj);
+ char* utf8 = RSTRING_PTR(obj);
+ int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0);
+ wchar_t* utf16 = mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t));
+ if (utf16 == NULL) return;
+ if (MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, utf16, wlen) > 0) {
+ utf16[wlen] = 0;
+ WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
+ utf16, wlen, &written, NULL);
+ }
+ mrb_free(mrb, utf16);
+ } else
+#endif
+ fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout);
}
}
diff --git a/mrbgems/mruby-proc-ext/mrbgem.rake b/mrbgems/mruby-proc-ext/mrbgem.rake
index 41d964bd9..e4d15a140 100644
--- a/mrbgems/mruby-proc-ext/mrbgem.rake
+++ b/mrbgems/mruby-proc-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-proc-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extensional Proc class'
+ spec.summary = 'Proc class extension'
end
diff --git a/mrbgems/mruby-proc-ext/mrblib/proc.rb b/mrbgems/mruby-proc-ext/mrblib/proc.rb
index 5dd9981df..b71663938 100644
--- a/mrbgems/mruby-proc-ext/mrblib/proc.rb
+++ b/mrbgems/mruby-proc-ext/mrblib/proc.rb
@@ -13,9 +13,11 @@ class Proc
end
def curry(arity=self.arity)
+ type = :proc
abs = lambda {|a| a < 0 ? -a - 1 : a}
arity = abs[arity]
if lambda?
+ type = :lambda
self_arity = self.arity
if (self_arity >= 0 && arity != self_arity) ||
(self_arity < 0 && abs[self_arity] > arity)
@@ -25,7 +27,7 @@ class Proc
pproc = self
make_curry = proc do |given_args=[]|
- proc do |*args|
+ send(type) do |*args|
new_args = given_args + args
if new_args.size >= arity
pproc[*new_args]
diff --git a/mrbgems/mruby-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c
index b105c95d7..c8c8f1aa1 100644
--- a/mrbgems/mruby-proc-ext/src/proc.c
+++ b/mrbgems/mruby-proc-ext/src/proc.c
@@ -1,51 +1,9 @@
-#include "mruby.h"
-#include "mruby/proc.h"
-#include "mruby/array.h"
-#include "mruby/string.h"
-#include "mruby/debug.h"
-
-struct RProc *
-mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t f, mrb_int argc, const mrb_value *argv)
-{
- struct RProc *p;
- struct REnv *e;
- int ai, i;
-
- p = mrb_proc_new_cfunc(mrb, f);
- ai = mrb_gc_arena_save(mrb);
- e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, NULL);
- p->env = e;
- mrb_gc_arena_restore(mrb, ai);
-
- MRB_ENV_UNSHARE_STACK(e);
- MRB_ENV_STACK_LEN(e) = argc;
- e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc);
- for (i = 0; i < argc; ++i) {
- e->stack[i] = argv[i];
- }
-
- return p;
-}
-
-mrb_value
-mrb_cfunc_env_get(mrb_state *mrb, mrb_int idx)
-{
- struct RProc *p = mrb->c->ci->proc;
- struct REnv *e = p->env;
-
- if (!MRB_PROC_CFUNC_P(p)) {
- mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc.");
- }
- if (!e) {
- mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv.");
- }
- if (idx < 0 || MRB_ENV_STACK_LEN(e) <= idx) {
- mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %S (expected: 0 <= index < %S)",
- mrb_fixnum_value(idx), mrb_fixnum_value(MRB_ENV_STACK_LEN(e)));
- }
-
- return e->stack[idx];
-}
+#include <mruby.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
+#include <mruby/array.h>
+#include <mruby/string.h>
+#include <mruby/debug.h>
static mrb_value
mrb_proc_lambda(mrb_state *mrb, mrb_value self)
@@ -122,6 +80,73 @@ mrb_kernel_proc(mrb_state *mrb, mrb_value self)
return blk;
}
+/*
+ * call-seq:
+ * prc.parameters -> array
+ *
+ * Returns the parameter information of this proc.
+ *
+ * prc = lambda{|x, y=42, *other|}
+ * prc.parameters #=> [[:req, :x], [:opt, :y], [:rest, :other]]
+ */
+
+static mrb_value
+mrb_proc_parameters(mrb_state *mrb, mrb_value self)
+{
+ struct parameters_type {
+ int size;
+ const char *name;
+ } *p, parameters_list [] = {
+ {0, "req"},
+ {0, "opt"},
+ {0, "rest"},
+ {0, "req"},
+ {0, "block"},
+ {0, NULL}
+ };
+ const struct RProc *proc = mrb_proc_ptr(self);
+ const struct mrb_irep *irep = proc->body.irep;
+ mrb_aspec aspec;
+ mrb_value parameters;
+ int i, j;
+
+ if (MRB_PROC_CFUNC_P(proc)) {
+ // TODO cfunc aspec is not implemented yet
+ return mrb_ary_new(mrb);
+ }
+ if (!irep->lv) {
+ return mrb_ary_new(mrb);
+ }
+ if (GET_OPCODE(*irep->iseq) != OP_ENTER) {
+ return mrb_ary_new(mrb);
+ }
+
+ if (!MRB_PROC_STRICT_P(proc)) {
+ parameters_list[0].name = "opt";
+ parameters_list[3].name = "opt";
+ }
+
+ aspec = GETARG_Ax(*irep->iseq);
+ parameters_list[0].size = MRB_ASPEC_REQ(aspec);
+ parameters_list[1].size = MRB_ASPEC_OPT(aspec);
+ parameters_list[2].size = MRB_ASPEC_REST(aspec);
+ parameters_list[3].size = MRB_ASPEC_POST(aspec);
+ parameters_list[4].size = MRB_ASPEC_BLOCK(aspec);
+
+ parameters = mrb_ary_new_capa(mrb, irep->nlocals-1);
+ for (i = 0, p = parameters_list; p->name; p++) {
+ mrb_value sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name));
+ for (j = 0; j < p->size; i++, j++) {
+ mrb_assert(i < (irep->nlocals-1));
+ mrb_ary_push(mrb, parameters, mrb_assoc_new(mrb,
+ sname,
+ mrb_symbol_value(irep->lv[i].name)
+ ));
+ }
+ }
+ return parameters;
+}
+
void
mrb_mruby_proc_ext_gem_init(mrb_state* mrb)
{
@@ -130,6 +155,7 @@ mrb_mruby_proc_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, p, "source_location", mrb_proc_source_location, MRB_ARGS_NONE());
mrb_define_method(mrb, p, "to_s", mrb_proc_inspect, MRB_ARGS_NONE());
mrb_define_method(mrb, p, "inspect", mrb_proc_inspect, MRB_ARGS_NONE());
+ mrb_define_method(mrb, p, "parameters", mrb_proc_parameters, MRB_ARGS_NONE());
mrb_define_class_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE());
mrb_define_method(mrb, mrb->kernel_module, "proc", mrb_kernel_proc, MRB_ARGS_NONE());
diff --git a/mrbgems/mruby-proc-ext/test/proc.c b/mrbgems/mruby-proc-ext/test/proc.c
index fcf8e2612..a77b68e9d 100644
--- a/mrbgems/mruby-proc-ext/test/proc.c
+++ b/mrbgems/mruby-proc-ext/test/proc.c
@@ -1,6 +1,6 @@
-#include "mruby.h"
-#include "mruby/proc.h"
-#include "mruby/class.h"
+#include <mruby.h>
+#include <mruby/proc.h>
+#include <mruby/class.h>
static mrb_value
return_func_name(mrb_state *mrb, mrb_value self)
diff --git a/mrbgems/mruby-proc-ext/test/proc.rb b/mrbgems/mruby-proc-ext/test/proc.rb
index 1565873a0..75e11dd93 100644
--- a/mrbgems/mruby-proc-ext/test/proc.rb
+++ b/mrbgems/mruby-proc-ext/test/proc.rb
@@ -41,6 +41,21 @@ assert('Proc#curry') do
assert_raise(ArgumentError) { b.curry[1, 2][3, 4] }
assert_raise(ArgumentError) { b.curry(5) }
assert_raise(ArgumentError) { b.curry(1) }
+
+ assert_false(proc{}.curry.lambda?)
+ assert_true(lambda{}.curry.lambda?)
+end
+
+assert('Proc#parameters') do
+ assert_equal([], Proc.new {}.parameters)
+ assert_equal([], Proc.new {||}.parameters)
+ assert_equal([[:opt, :a]], Proc.new {|a|}.parameters)
+ assert_equal([[:req, :a]], lambda {|a|}.parameters)
+ assert_equal([[:opt, :a]], lambda {|a=nil|}.parameters)
+ assert_equal([[:req, :a]], ->(a){}.parameters)
+ assert_equal([[:rest, :a]], Proc.new {|*a|}.parameters)
+ assert_equal([[:opt, :a], [:opt, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:opt, :f], [:opt, :g], [:block, :h]], Proc.new {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
+ assert_equal([[:req, :a], [:req, :b], [:opt, :c], [:opt, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], lambda {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
end
assert('Proc#to_proc') do
diff --git a/mrbgems/mruby-random/src/mt19937ar.c b/mrbgems/mruby-random/src/mt19937ar.c
index 0e1279b34..4013ad30b 100644
--- a/mrbgems/mruby-random/src/mt19937ar.c
+++ b/mrbgems/mruby-random/src/mt19937ar.c
@@ -4,7 +4,7 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
#include "mt19937ar.h"
/* Period parameters */
@@ -32,7 +32,7 @@ void mrb_random_init_genrand(mt_state *t, unsigned long s)
unsigned long mrb_random_genrand_int32(mt_state *t)
{
unsigned long y;
- static unsigned long mag01[2]={0x0UL, MATRIX_A};
+ static const unsigned long mag01[2]={0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if (t->mti >= N) { /* generate N words at one time */
@@ -126,7 +126,7 @@ void init_by_array(unsigned long init_key[], int key_length)
unsigned long genrand_int32(void)
{
unsigned long y;
- static unsigned long mag01[2]={0x0UL, MATRIX_A};
+ static const unsigned long mag01[2]={0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if (mti >= N) { /* generate N words at one time */
diff --git a/mrbgems/mruby-random/src/random.c b/mrbgems/mruby-random/src/random.c
index 391336493..040d83ed6 100644
--- a/mrbgems/mruby-random/src/random.c
+++ b/mrbgems/mruby-random/src/random.c
@@ -4,11 +4,11 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/variable.h"
-#include "mruby/class.h"
-#include "mruby/data.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/variable.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/array.h>
#include "mt19937ar.h"
#include <time.h>
@@ -46,7 +46,7 @@ mrb_random_mt_srand(mrb_state *mrb, mt_state *t, mrb_value seed)
if (mrb_nil_p(seed)) {
seed = mrb_fixnum_value((mrb_int)(time(NULL) + mt_rand(t)));
if (mrb_fixnum(seed) < 0) {
- seed = mrb_fixnum_value( 0 - mrb_fixnum(seed));
+ seed = mrb_fixnum_value(0 - mrb_fixnum(seed));
}
}
@@ -129,9 +129,7 @@ mrb_random_init(mrb_state *mrb, mrb_value self)
if (t) {
mrb_free(mrb, t);
}
-
- DATA_TYPE(self) = &mt_state_type;
- DATA_PTR(self) = NULL;
+ mrb_data_init(self, NULL, &mt_state_type);
t = (mt_state *)mrb_malloc(mrb, sizeof(mt_state));
t->mti = N + 1;
@@ -147,7 +145,7 @@ mrb_random_init(mrb_state *mrb, mrb_value self)
t->seed = mrb_fixnum(seed);
}
- DATA_PTR(self) = t;
+ mrb_data_init(self, t, &mt_state_type);
return self;
}
@@ -223,8 +221,8 @@ mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary)
j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary))));
tmp = RARRAY_PTR(ary)[i];
- RARRAY_PTR(ary)[i] = RARRAY_PTR(ary)[j];
- RARRAY_PTR(ary)[j] = tmp;
+ mrb_ary_ptr(ary)->ptr[i] = RARRAY_PTR(ary)[j];
+ mrb_ary_ptr(ary)->ptr[j] = tmp;
}
}
@@ -307,11 +305,10 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
}
break;
}
- RARRAY_PTR(result)[i] = mrb_fixnum_value(r);
- RARRAY_LEN(result)++;
+ mrb_ary_push(mrb, result, mrb_fixnum_value(r));
}
for (i=0; i<n; i++) {
- RARRAY_PTR(result)[i] = RARRAY_PTR(ary)[mrb_fixnum(RARRAY_PTR(result)[i])];
+ mrb_ary_set(mrb, result, i, RARRAY_PTR(ary)[mrb_fixnum(RARRAY_PTR(result)[i])]);
}
return result;
}
diff --git a/mrbgems/mruby-random/src/random.h b/mrbgems/mruby-random/src/random.h
index af82d66ac..a4785ae5a 100644
--- a/mrbgems/mruby-random/src/random.h
+++ b/mrbgems/mruby-random/src/random.h
@@ -4,8 +4,8 @@
** See Copyright Notice in mruby.h
*/
-#ifndef RANDOM_H
-#define RANDOM_H
+#ifndef MRUBY_RANDOM_H
+#define MRUBY_RANDOM_H
void mrb_mruby_random_gem_init(mrb_state *mrb);
diff --git a/mrbgems/mruby-range-ext/mrbgem.rake b/mrbgems/mruby-range-ext/mrbgem.rake
index bcf3de202..e6fa7df5f 100644
--- a/mrbgems/mruby-range-ext/mrbgem.rake
+++ b/mrbgems/mruby-range-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-range-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extensional Range class'
+ spec.summary = 'Range class extension'
end
diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c
index 35632ad20..4d6f1684f 100644
--- a/mrbgems/mruby-range-ext/src/range.c
+++ b/mrbgems/mruby-range-ext/src/range.c
@@ -1,5 +1,5 @@
-#include "mruby.h"
-#include "mruby/range.h"
+#include <mruby.h>
+#include <mruby/range.h>
#include <math.h>
static mrb_bool
diff --git a/mrbgems/mruby-sprintf/src/kernel.c b/mrbgems/mruby-sprintf/src/kernel.c
index bd4f2bca1..946b43a8a 100644
--- a/mrbgems/mruby-sprintf/src/kernel.c
+++ b/mrbgems/mruby-sprintf/src/kernel.c
@@ -4,7 +4,7 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
mrb_value mrb_f_sprintf(mrb_state *mrb, mrb_value obj); /* in sprintf.c */
diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c
index 31f22975d..33e0931f9 100644
--- a/mrbgems/mruby-sprintf/src/sprintf.c
+++ b/mrbgems/mruby-sprintf/src/sprintf.c
@@ -4,14 +4,14 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
-#include "mruby/string.h"
-#include "mruby/hash.h"
-#include "mruby/numeric.h"
+#include <mruby/string.h>
+#include <mruby/hash.h>
+#include <mruby/numeric.h>
#include <math.h>
#include <ctype.h>
@@ -71,18 +71,14 @@ sign_bits(int base, const char *p)
static mrb_value
mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
{
- char buf[64], *b = buf + sizeof buf;
+ char buf[66], *b = buf + sizeof buf;
mrb_int num = mrb_fixnum(x);
- unsigned long val = (unsigned long)num;
+ uint64_t val = (uint64_t)num;
char d;
if (base != 2) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base));
}
-
- if (val >= (1 << 10))
- val &= 0x3ff;
-
if (val == 0) {
return mrb_str_new_lit(mrb, "0");
}
@@ -178,17 +174,18 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
}
#define GETASTER(num) do { \
+ mrb_value tmp_v; \
t = p++; \
n = 0; \
GETNUM(n, val); \
if (*p == '$') { \
- tmp = GETPOSARG(n); \
+ tmp_v = GETPOSARG(n); \
} \
else { \
- tmp = GETARG(); \
+ tmp_v = GETARG(); \
p = t; \
} \
- num = mrb_fixnum(tmp); \
+ num = mrb_fixnum(tmp_v); \
} while (0)
static mrb_value
@@ -233,20 +230,20 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
* ------+--------------------------------------------------------------
* b | Convert argument as a binary number.
* | Negative numbers will be displayed as a two's complement
- * | prefixed with `..1'.
- * B | Equivalent to `b', but uses an uppercase 0B for prefix
+ * | prefixed with '..1'.
+ * B | Equivalent to 'b', but uses an uppercase 0B for prefix
* | in the alternative format by #.
* d | Convert argument as a decimal number.
- * i | Identical to `d'.
+ * i | Identical to 'd'.
* o | Convert argument as an octal number.
* | Negative numbers will be displayed as a two's complement
- * | prefixed with `..7'.
- * u | Identical to `d'.
+ * | prefixed with '..7'.
+ * u | Identical to 'd'.
* x | Convert argument as a hexadecimal number.
* | Negative numbers will be displayed as a two's complement
- * | prefixed with `..f' (representing an infinite string of
+ * | prefixed with '..f' (representing an infinite string of
* | leading 'ff's).
- * X | Equivalent to `x', but uses uppercase letters.
+ * X | Equivalent to 'x', but uses uppercase letters.
*
* Field | Float Format
* ------+--------------------------------------------------------------
@@ -254,7 +251,7 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
* | with one digit before the decimal point as [-]d.dddddde[+-]dd.
* | The precision specifies the number of digits after the decimal
* | point (defaulting to six).
- * E | Equivalent to `e', but uses an uppercase E to indicate
+ * E | Equivalent to 'e', but uses an uppercase E to indicate
* | the exponent.
* f | Convert floating point argument as [-]ddd.dddddd,
* | where the precision specifies the number of digits after
@@ -263,11 +260,11 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
* | if the exponent is less than -4 or greater than or
* | equal to the precision, or in dd.dddd form otherwise.
* | The precision specifies the number of significant digits.
- * G | Equivalent to `g', but use an uppercase `E' in exponent form.
+ * G | Equivalent to 'g', but use an uppercase 'E' in exponent form.
* a | Convert floating point argument as [-]0xh.hhhhp[+-]dd,
* | which is consisted from optional sign, "0x", fraction part
* | as hexadecimal, "p", and exponential part as decimal.
- * A | Equivalent to `a', but use uppercase `X' and `P'.
+ * A | Equivalent to 'a', but use uppercase 'X' and 'P'.
*
* Field | Other Format
* ------+--------------------------------------------------------------
@@ -286,7 +283,7 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
* ---------+---------------+-----------------------------------------
* space | bBdiouxX | Leave a space at the start of
* | aAeEfgG | non-negative numbers.
- * | (numeric fmt) | For `o', `x', `X', `b' and `B', use
+ * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use
* | | a minus sign with absolute value for
* | | negative values.
* ---------+---------------+-----------------------------------------
@@ -296,27 +293,27 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
* | | sprintf string.
* ---------+---------------+-----------------------------------------
* # | bBoxX | Use an alternative format.
- * | aAeEfgG | For the conversions `o', increase the precision
- * | | until the first digit will be `0' if
+ * | aAeEfgG | For the conversions 'o', increase the precision
+ * | | until the first digit will be '0' if
* | | it is not formatted as complements.
- * | | For the conversions `x', `X', `b' and `B'
- * | | on non-zero, prefix the result with ``0x'',
- * | | ``0X'', ``0b'' and ``0B'', respectively.
- * | | For `a', `A', `e', `E', `f', `g', and 'G',
+ * | | For the conversions 'x', 'X', 'b' and 'B'
+ * | | on non-zero, prefix the result with "0x",
+ * | | "0X", "0b" and "0B", respectively.
+ * | | For 'a', 'A', 'e', 'E', 'f', 'g', and 'G',
* | | force a decimal point to be added,
* | | even if no digits follow.
- * | | For `g' and 'G', do not remove trailing zeros.
+ * | | For 'g' and 'G', do not remove trailing zeros.
* ---------+---------------+-----------------------------------------
* + | bBdiouxX | Add a leading plus sign to non-negative
* | aAeEfgG | numbers.
- * | (numeric fmt) | For `o', `x', `X', `b' and `B', use
+ * | (numeric fmt) | For 'o', 'x', 'X', 'b' and 'B', use
* | | a minus sign with absolute value for
* | | negative values.
* ---------+---------------+-----------------------------------------
* - | all | Left-justify the result of this conversion.
* ---------+---------------+-----------------------------------------
* 0 (zero) | bBdiouxX | Pad with zeros, not spaces.
- * | aAeEfgG | For `o', `x', `X', `b' and `B', radix-1
+ * | aAeEfgG | For 'o', 'x', 'X', 'b' and 'B', radix-1
* | (numeric fmt) | is used for negative numbers formatted as
* | | complements.
* ---------+---------------+-----------------------------------------
@@ -327,21 +324,21 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
*
* Examples of flags:
*
- * # `+' and space flag specifies the sign of non-negative numbers.
+ * # '+' and space flag specifies the sign of non-negative numbers.
* sprintf("%d", 123) #=> "123"
* sprintf("%+d", 123) #=> "+123"
* sprintf("% d", 123) #=> " 123"
*
- * # `#' flag for `o' increases number of digits to show `0'.
- * # `+' and space flag changes format of negative numbers.
+ * # '#' flag for 'o' increases number of digits to show '0'.
+ * # '+' and space flag changes format of negative numbers.
* sprintf("%o", 123) #=> "173"
* sprintf("%#o", 123) #=> "0173"
* sprintf("%+o", -123) #=> "-173"
* sprintf("%o", -123) #=> "..7605"
* sprintf("%#o", -123) #=> "..7605"
*
- * # `#' flag for `x' add a prefix `0x' for non-zero numbers.
- * # `+' and space flag disables complements for negative numbers.
+ * # '#' flag for 'x' add a prefix '0x' for non-zero numbers.
+ * # '+' and space flag disables complements for negative numbers.
* sprintf("%x", 123) #=> "7b"
* sprintf("%#x", 123) #=> "0x7b"
* sprintf("%+x", -123) #=> "-7b"
@@ -349,12 +346,12 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
* sprintf("%#x", -123) #=> "0x..f85"
* sprintf("%#x", 0) #=> "0"
*
- * # `#' for `X' uses the prefix `0X'.
+ * # '#' for 'X' uses the prefix '0X'.
* sprintf("%X", 123) #=> "7B"
* sprintf("%#X", 123) #=> "0X7B"
*
- * # `#' flag for `b' add a prefix `0b' for non-zero numbers.
- * # `+' and space flag disables complements for negative numbers.
+ * # '#' flag for 'b' add a prefix '0b' for non-zero numbers.
+ * # '+' and space flag disables complements for negative numbers.
* sprintf("%b", 123) #=> "1111011"
* sprintf("%#b", 123) #=> "0b1111011"
* sprintf("%+b", -123) #=> "-1111011"
@@ -362,19 +359,19 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
* sprintf("%#b", -123) #=> "0b..10000101"
* sprintf("%#b", 0) #=> "0"
*
- * # `#' for `B' uses the prefix `0B'.
+ * # '#' for 'B' uses the prefix '0B'.
* sprintf("%B", 123) #=> "1111011"
* sprintf("%#B", 123) #=> "0B1111011"
*
- * # `#' for `e' forces to show the decimal point.
+ * # '#' for 'e' forces to show the decimal point.
* sprintf("%.0e", 1) #=> "1e+00"
* sprintf("%#.0e", 1) #=> "1.e+00"
*
- * # `#' for `f' forces to show the decimal point.
+ * # '#' for 'f' forces to show the decimal point.
* sprintf("%.0f", 1234) #=> "1234"
* sprintf("%#.0f", 1234) #=> "1234."
*
- * # `#' for `g' forces to show the decimal point.
+ * # '#' for 'g' forces to show the decimal point.
* # It also disables stripping lowest zeros.
* sprintf("%g", 123.4) #=> "123.4"
* sprintf("%#g", 123.4) #=> "123.400"
@@ -408,7 +405,7 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
*
* Examples of precisions:
*
- * # precision for `d', 'o', 'x' and 'b' is
+ * # precision for 'd', 'o', 'x' and 'b' is
* # minimum number of digits <------>
* sprintf("%20.8d", 123) #=> " 00000123"
* sprintf("%20.8o", 123) #=> " 00000173"
@@ -419,8 +416,8 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
* sprintf("%20.8x", -123) #=> " ..ffff85"
* sprintf("%20.8b", -11) #=> " ..110101"
*
- * # "0x" and "0b" for `#x' and `#b' is not counted for
- * # precision but "0" for `#o' is counted. <------>
+ * # "0x" and "0b" for '#x' and '#b' is not counted for
+ * # precision but "0" for '#o' is counted. <------>
* sprintf("%#20.8d", 123) #=> " 00000123"
* sprintf("%#20.8o", 123) #=> " 00000173"
* sprintf("%#20.8x", 123) #=> " 0x0000007b"
@@ -430,22 +427,22 @@ get_hash(mrb_state *mrb, mrb_value *hash, int argc, const mrb_value *argv)
* sprintf("%#20.8x", -123) #=> " 0x..ffff85"
* sprintf("%#20.8b", -11) #=> " 0b..110101"
*
- * # precision for `e' is number of
+ * # precision for 'e' is number of
* # digits after the decimal point <------>
* sprintf("%20.8e", 1234.56789) #=> " 1.23456789e+03"
*
- * # precision for `f' is number of
+ * # precision for 'f' is number of
* # digits after the decimal point <------>
* sprintf("%20.8f", 1234.56789) #=> " 1234.56789000"
*
- * # precision for `g' is number of
+ * # precision for 'g' is number of
* # significant digits <------->
* sprintf("%20.8g", 1234.56789) #=> " 1234.5679"
*
* # <------->
* sprintf("%20.8g", 123456789) #=> " 1.2345679e+08"
*
- * # precision for `s' is
+ * # precision for 's' is
* # maximum number of characters <------>
* sprintf("%20.8s", "string test") #=> " string t"
*
@@ -500,7 +497,6 @@ mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt)
int nextarg = 1;
int posarg = 0;
mrb_value nextvalue;
- mrb_value tmp;
mrb_value str;
mrb_value hash = mrb_undef_value();
@@ -539,7 +535,7 @@ mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt)
if (t >= end)
goto sprint_exit; /* end of fmt string */
- p = t + 1; /* skip `%' */
+ p = t + 1; /* skip '%' */
width = prec = -1;
nextvalue = mrb_undef_value();
@@ -763,11 +759,11 @@ retry:
case 'B':
case 'u': {
mrb_value val = GETARG();
- char fbuf[32], nbuf[64], *s;
+ char fbuf[32], nbuf[68], *s;
const char *prefix = NULL;
int sign = 0, dots = 0;
char sc = 0;
- mrb_int v = 0, org_v = 0;
+ mrb_int v = 0;
int base;
mrb_int len;
@@ -800,10 +796,6 @@ retry:
bin_retry:
switch (mrb_type(val)) {
case MRB_TT_FLOAT:
- if (FIXABLE(mrb_float(val))) {
- val = mrb_fixnum_value((mrb_int)mrb_float(val));
- goto bin_retry;
- }
val = mrb_flo_to_fixnum(mrb, val);
if (mrb_fixnum_p(val)) goto bin_retry;
break;
@@ -835,20 +827,17 @@ retry:
}
if (base == 2) {
- org_v = v;
- if ( v < 0 && !sign ) {
+ if (v < 0 && !sign) {
val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base);
dots = 1;
}
else {
val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base);
}
- v = mrb_fixnum(mrb_str_to_inum(mrb, val, 10, FALSE));
}
if (sign) {
char c = *p;
if (c == 'i') c = 'd'; /* %d and %i are identical */
- if (base == 2) c = 'd';
if (v < 0) {
v = -v;
sc = '-';
@@ -862,20 +851,29 @@ retry:
sc = ' ';
width--;
}
- snprintf(fbuf, sizeof(fbuf), "%%l%c", c);
- snprintf(nbuf, sizeof(nbuf), fbuf, v);
+ if (base == 2) {
+ snprintf(nbuf, sizeof(nbuf), "%s", RSTRING_PTR(val));
+ }
+ else {
+ snprintf(fbuf, sizeof(fbuf), "%%l%c", c);
+ snprintf(nbuf, sizeof(nbuf), fbuf, v);
+ }
s = nbuf;
}
else {
char c = *p;
if (c == 'X') c = 'x';
- if (base == 2) c = 'd';
s = nbuf;
if (v < 0) {
dots = 1;
}
- snprintf(fbuf, sizeof(fbuf), "%%l%c", c);
- snprintf(++s, sizeof(nbuf) - 1, fbuf, v);
+ if (base == 2) {
+ snprintf(++s, sizeof(nbuf) - 1, "%s", RSTRING_PTR(val));
+ }
+ else {
+ snprintf(fbuf, sizeof(fbuf), "%%l%c", c);
+ snprintf(++s, sizeof(nbuf) - 1, fbuf, v);
+ }
if (v < 0) {
char d;
@@ -965,7 +963,7 @@ retry:
CHECK(prec - len);
if (dots) PUSH("..", 2);
- if (v < 0 || (base == 2 && org_v < 0)) {
+ if (v < 0) {
char c = sign_bits(base, p);
while (len < prec--) {
buf[blen++] = c;
diff --git a/mrbgems/mruby-sprintf/test/sprintf.rb b/mrbgems/mruby-sprintf/test/sprintf.rb
index 7007df1fa..454c226e1 100644
--- a/mrbgems/mruby-sprintf/test/sprintf.rb
+++ b/mrbgems/mruby-sprintf/test/sprintf.rb
@@ -5,4 +5,5 @@ assert('String#%') do
assert_equal "one=1", "one=%d" % 1
assert_equal "1 one 1.0", "%d %s %3.1f" % [ 1, "one", 1.01 ]
assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" }
+ assert_equal 16, ("%b" % (1<<15)).size
end
diff --git a/mrbgems/mruby-string-ext/mrbgem.rake b/mrbgems/mruby-string-ext/mrbgem.rake
index 688589933..f2df5a783 100644
--- a/mrbgems/mruby-string-ext/mrbgem.rake
+++ b/mrbgems/mruby-string-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-string-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extensional String class'
+ spec.summary = 'String class extension'
end
diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb
index b8cb93199..c699e13b7 100644
--- a/mrbgems/mruby-string-ext/mrblib/string.rb
+++ b/mrbgems/mruby-string-ext/mrblib/string.rb
@@ -1,6 +1,25 @@
class String
##
+ # call-seq:
+ # String.try_convert(obj) -> string or nil
+ #
+ # Try to convert <i>obj</i> into a String, using to_str method.
+ # Returns converted string or nil if <i>obj</i> cannot be converted
+ # for any reason.
+ #
+ # String.try_convert("str") #=> "str"
+ # String.try_convert(/re/) #=> nil
+ #
+ def self.try_convert(obj)
+ if obj.respond_to?(:to_str)
+ obj.to_str
+ else
+ nil
+ end
+ end
+
+ ##
# call-seq:
# string.clear -> string
#
@@ -164,9 +183,9 @@ class String
# string #=> "thsa sting"
#
def slice!(arg1, arg2=nil)
- raise "wrong number of arguments (for 1..2)" if arg1 == nil && arg2 == nil
+ raise "wrong number of arguments (for 1..2)" if arg1.nil? && arg2.nil?
- if arg1 != nil && arg2 != nil
+ if !arg1.nil? && !arg2.nil?
idx = arg1
idx += self.size if arg1 < 0
if idx >= 0 && idx <= self.size && arg2 > 0
@@ -181,6 +200,7 @@ class String
ed = arg1.end
beg += self.size if beg < 0
ed += self.size if ed < 0
+ ed -= 1 if arg1.exclude_end?
validated = true
elsif arg1.kind_of?(String)
validated = true
@@ -195,25 +215,144 @@ class String
return nil
end
end
- unless str == nil || str == ""
- if arg1 != nil && arg2 !=nil
+ unless str.nil? || str == ""
+ if !arg1.nil? && !arg2.nil?
idx = arg1 >= 0 ? arg1 : self.size+arg1
- str2 = self[0...idx] + self[idx+arg2..-1]
+ str2 = self[0...idx] + self[idx+arg2..-1].to_s
else
if arg1.kind_of?(Range)
idx = beg >= 0 ? beg : self.size+beg
idx2 = ed>= 0 ? ed : self.size+ed
- str2 = self[0...idx] + self[idx2+1..-1]
+ str2 = self[0...idx] + self[idx2+1..-1].to_s
elsif arg1.kind_of?(String)
idx = self.index(arg1)
- str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx == nil
+ str2 = self[0...idx] + self[idx+arg1.size..-1] unless idx.nil?
else
idx = arg1 >= 0 ? arg1 : self.size+arg1
- str2 = self[0...idx] + self[idx+1..-1]
+ str2 = self[0...idx] + self[idx+1..-1].to_s
end
end
- self.replace(str2) unless str2 == nil
+ self.replace(str2) unless str2.nil?
end
str
end
+
+ ##
+ # call-seq:
+ # str.insert(index, other_str) -> str
+ #
+ # Inserts <i>other_str</i> before the character at the given
+ # <i>index</i>, modifying <i>str</i>. Negative indices count from the
+ # end of the string, and insert <em>after</em> the given character.
+ # The intent is insert <i>aString</i> so that it starts at the given
+ # <i>index</i>.
+ #
+ # "abcd".insert(0, 'X') #=> "Xabcd"
+ # "abcd".insert(3, 'X') #=> "abcXd"
+ # "abcd".insert(4, 'X') #=> "abcdX"
+ # "abcd".insert(-3, 'X') #=> "abXcd"
+ # "abcd".insert(-1, 'X') #=> "abcdX"
+ #
+ def insert(idx, str)
+ pos = idx.to_i
+ pos += self.size + 1 if pos < 0
+
+ raise IndexError, "index #{idx.to_i} out of string" if pos < 0 || pos > self.size
+
+ return self + str if pos == -1
+ return str + self if pos == 0
+ return self[0..pos - 1] + str + self[pos..-1]
+ end
+
+ ##
+ # call-seq:
+ # str.ljust(integer, padstr=' ') -> new_str
+ #
+ # If <i>integer</i> is greater than the length of <i>str</i>, returns a new
+ # <code>String</code> of length <i>integer</i> with <i>str</i> left justified
+ # and padded with <i>padstr</i>; otherwise, returns <i>str</i>.
+ #
+ # "hello".ljust(4) #=> "hello"
+ # "hello".ljust(20) #=> "hello "
+ # "hello".ljust(20, '1234') #=> "hello123412341234123"
+ def ljust(idx, padstr = ' ')
+ if idx <= self.size
+ return self
+ end
+ newstr = self.dup
+ newstr << padstr
+ while newstr.size <= idx
+ newstr << padstr
+ end
+ return newstr.slice(0,idx)
+ end
+
+ # str.upto(other_str, exclusive=false) {|s| block } -> str
+ # str.upto(other_str, exclusive=false) -> an_enumerator
+ #
+ # Iterates through successive values, starting at <i>str</i> and
+ # ending at <i>other_str</i> inclusive, passing each value in turn to
+ # the block. The <code>String#succ</code> method is used to generate
+ # each value. If optional second argument exclusive is omitted or is false,
+ # the last value will be included; otherwise it will be excluded.
+ #
+ # If no block is given, an enumerator is returned instead.
+ #
+ # "a8".upto("b6") {|s| print s, ' ' }
+ # for s in "a8".."b6"
+ # print s, ' '
+ # end
+ #
+ # <em>produces:</em>
+ #
+ # a8 a9 b0 b1 b2 b3 b4 b5 b6
+ # a8 a9 b0 b1 b2 b3 b4 b5 b6
+ #
+ # If <i>str</i> and <i>other_str</i> contains only ascii numeric characters,
+ # both are recognized as decimal numbers. In addition, the width of
+ # string (e.g. leading zeros) is handled appropriately.
+ #
+ # "9".upto("11").to_a #=> ["9", "10", "11"]
+ # "25".upto("5").to_a #=> []
+ # "07".upto("11").to_a #=> ["07", "08", "09", "10", "11"]
+ #
+ def upto(other_str, excl=false, &block)
+ return to_enum :upto, other_str, excl unless block
+
+ str = self
+ n = self.<=>other_str
+ return self if n > 0 || (self == other_str && excl)
+ while true
+ block.call(str)
+ return self if !excl && str == other_str
+ str = str.succ
+ return self if excl && str == other_str
+ end
+ end
+
+ def chars(&block)
+ if block_given?
+ self.split('').map do |i|
+ block.call(i)
+ end
+ self
+ else
+ self.split('')
+ end
+ end
+ alias each_char chars
+
+ def codepoints(&block)
+ len = self.size
+
+ if block_given?
+ self.split('').map do|x|
+ block.call(x.ord)
+ end
+ self
+ else
+ self.split('').map{|x| x.ord}
+ end
+ end
+ alias each_codepoint codepoints
end
diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c
index f04f12c4b..c2ee1fc23 100644
--- a/mrbgems/mruby-string-ext/src/string.c
+++ b/mrbgems/mruby-string-ext/src/string.c
@@ -1,9 +1,9 @@
-#include <ctype.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/range.h>
static mrb_value
mrb_str_getbyte(mrb_state *mrb, mrb_value str)
@@ -19,6 +19,59 @@ mrb_str_getbyte(mrb_state *mrb, mrb_value str)
return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]);
}
+static mrb_value
+mrb_str_setbyte(mrb_state *mrb, mrb_value str)
+{
+ mrb_int pos, byte;
+ long len = RSTRING_LEN(str);
+
+ mrb_get_args(mrb, "ii", &pos, &byte);
+
+ if (pos < -len || len <= pos)
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos));
+ if (pos < 0)
+ pos += len;
+
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ byte &= 0xff;
+ RSTRING_PTR(str)[pos] = byte;
+ return mrb_fixnum_value((unsigned char)byte);
+}
+
+static mrb_value
+mrb_str_byteslice(mrb_state *mrb, mrb_value str)
+{
+ mrb_value a1;
+ mrb_int len;
+ int argc;
+
+ argc = mrb_get_args(mrb, "o|i", &a1, &len);
+ if (argc == 2) {
+ return mrb_str_substr(mrb, str, mrb_fixnum(a1), len);
+ }
+ switch (mrb_type(a1)) {
+ case MRB_TT_RANGE:
+ {
+ mrb_int beg;
+
+ len = RSTRING_LEN(str);
+ if (mrb_range_beg_len(mrb, a1, &beg, &len, len)) {
+ return mrb_str_substr(mrb, str, beg, len);
+ }
+ return mrb_nil_value();
+ }
+ case MRB_TT_FLOAT:
+ a1 = mrb_fixnum_value((mrb_int)mrb_float(a1));
+ /* fall through */
+ case MRB_TT_FIXNUM:
+ return mrb_str_substr(mrb, str, mrb_fixnum(a1), 1);
+ default:
+ mrb_raise(mrb, E_TYPE_ERROR, "wrong type of argument");
+ }
+ /* not reached */
+ return mrb_nil_value();
+}
+
/*
* call-seq:
* str.swapcase! -> str or nil
@@ -192,6 +245,51 @@ mrb_str_chr(mrb_state *mrb, mrb_value self)
return mrb_str_substr(mrb, self, 0, 1);
}
+static mrb_value
+mrb_fixnum_chr(mrb_state *mrb, mrb_value num)
+{
+ mrb_int cp = mrb_fixnum(num);
+#ifdef MRB_UTF8_STRING
+ char utf8[4];
+ mrb_int len;
+
+ if (cp < 0 || 0x10FFFF < cp) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
+ }
+ if (cp < 0x80) {
+ utf8[0] = (char)cp;
+ len = 1;
+ }
+ else if (cp < 0x800) {
+ utf8[0] = (char)(0xC0 | (cp >> 6));
+ utf8[1] = (char)(0x80 | (cp & 0x3F));
+ len = 2;
+ }
+ else if (cp < 0x10000) {
+ utf8[0] = (char)(0xE0 | (cp >> 12));
+ utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
+ utf8[2] = (char)(0x80 | ( cp & 0x3F));
+ len = 3;
+ }
+ else {
+ utf8[0] = (char)(0xF0 | (cp >> 18));
+ utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
+ utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F));
+ utf8[3] = (char)(0x80 | ( cp & 0x3F));
+ len = 4;
+ }
+ return mrb_str_new(mrb, utf8, len);
+#else
+ char c;
+
+ if (cp < 0 || 0xff < cp) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
+ }
+ c = (char)cp;
+ return mrb_str_new(mrb, &c, 1);
+#endif
+}
+
/*
* call-seq:
* string.lines -> array of string
@@ -239,6 +337,202 @@ mrb_str_lines(mrb_state *mrb, mrb_value self)
return result;
}
+/*
+ * call-seq:
+ * string.succ -> string
+ *
+ * Returns next sequence of the string;
+ *
+ * a = "abc"
+ * a.succ #=> "abd"
+ */
+static mrb_value
+mrb_str_succ_bang(mrb_state *mrb, mrb_value self)
+{
+ mrb_value result;
+ unsigned char *p, *e, *b, *t;
+ const char *prepend;
+ struct RString *s = mrb_str_ptr(self);
+ size_t l;
+
+ if (RSTRING_LEN(self) == 0)
+ return self;
+
+ mrb_str_modify(mrb, s);
+ l = RSTRING_LEN(self);
+ b = p = (unsigned char*) RSTRING_PTR(self);
+ t = e = p + l;
+ *(e--) = 0;
+
+ // find trailing ascii/number
+ while (e >= b) {
+ if (ISALNUM(*e))
+ break;
+ e--;
+ }
+ if (e < b) {
+ e = p + l - 1;
+ result = mrb_str_new_lit(mrb, "");
+ } else {
+ // find leading letter of the ascii/number
+ b = e;
+ while (b > p) {
+ if (!ISALNUM(*b) || (ISALNUM(*b) && *b != '9' && *b != 'z' && *b != 'Z'))
+ break;
+ b--;
+ }
+ if (!ISALNUM(*b))
+ b++;
+ result = mrb_str_new(mrb, (char*) p, b - p);
+ }
+
+ while (e >= b) {
+ if (!ISALNUM(*e)) {
+ if (*e == 0xff) {
+ mrb_str_cat_lit(mrb, result, "\x01");
+ (*e) = 0;
+ } else
+ (*e)++;
+ break;
+ }
+ prepend = NULL;
+ if (*e == '9') {
+ if (e == b) prepend = "1";
+ *e = '0';
+ } else if (*e == 'z') {
+ if (e == b) prepend = "a";
+ *e = 'a';
+ } else if (*e == 'Z') {
+ if (e == b) prepend = "A";
+ *e = 'A';
+ } else {
+ (*e)++;
+ break;
+ }
+ if (prepend) mrb_str_cat_cstr(mrb, result, prepend);
+ e--;
+ }
+ result = mrb_str_cat(mrb, result, (char*) b, t - b);
+ l = RSTRING_LEN(result);
+ mrb_str_resize(mrb, self, l);
+ memcpy(RSTRING_PTR(self), RSTRING_PTR(result), l);
+ return self;
+}
+
+static mrb_value
+mrb_str_succ(mrb_state *mrb, mrb_value self)
+{
+ mrb_value str;
+
+ str = mrb_str_dup(mrb, self);
+ mrb_str_succ_bang(mrb, str);
+ return str;
+}
+
+/*
+ * call-seq:
+ * str.prepend(other_str) -> str
+ *
+ * Prepend---Prepend the given string to <i>str</i>.
+ *
+ * a = "world"
+ * a.prepend("hello ") #=> "hello world"
+ * a #=> "hello world"
+ */
+static mrb_value
+mrb_str_prepend(mrb_state *mrb, mrb_value self)
+{
+ struct RString *s1 = mrb_str_ptr(self), *s2, *temp_s;
+ mrb_int len;
+ mrb_value other, temp_str;
+
+ mrb_get_args(mrb, "S", &other);
+
+ mrb_str_modify(mrb, s1);
+ if (!mrb_string_p(other)) {
+ other = mrb_str_to_str(mrb, other);
+ }
+ s2 = mrb_str_ptr(other);
+ len = RSTR_LEN(s1) + RSTR_LEN(s2);
+ temp_str = mrb_str_new(mrb, NULL, RSTR_LEN(s1));
+ temp_s = mrb_str_ptr(temp_str);
+ memcpy(RSTR_PTR(temp_s), RSTR_PTR(s1), RSTR_LEN(s1));
+ if (RSTRING_CAPA(self) < len) {
+ mrb_str_resize(mrb, self, len);
+ }
+ memcpy(RSTR_PTR(s1), RSTR_PTR(s2), RSTR_LEN(s2));
+ memcpy(RSTR_PTR(s1) + RSTR_LEN(s2), RSTR_PTR(temp_s), RSTR_LEN(temp_s));
+ RSTR_SET_LEN(s1, len);
+ RSTR_PTR(s1)[len] = '\0';
+ return self;
+}
+
+#ifdef MRB_UTF8_STRING
+static const char utf8len_codepage_zero[256] =
+{
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,
+};
+
+static mrb_int
+utf8code(unsigned char* p)
+{
+ mrb_int len;
+
+ if (p[0] < 0x80)
+ return p[0];
+
+ len = utf8len_codepage_zero[p[0]];
+ if (len > 1 && (p[1] & 0xc0) == 0x80) {
+ if (len == 2)
+ return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f);
+ if ((p[2] & 0xc0) == 0x80) {
+ if (len == 3)
+ return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6)
+ + (p[2] & 0x3f);
+ if ((p[3] & 0xc0) == 0x80) {
+ if (len == 4)
+ return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12)
+ + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f);
+ if ((p[4] & 0xc0) == 0x80) {
+ if (len == 5)
+ return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18)
+ + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6)
+ + (p[4] & 0x3f);
+ if ((p[5] & 0xc0) == 0x80 && len == 6)
+ return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24)
+ + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12)
+ + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f);
+ }
+ }
+ }
+ }
+ return p[0];
+}
+
+static mrb_value
+mrb_str_ord(mrb_state* mrb, mrb_value str)
+{
+ if (RSTRING_LEN(str) == 0)
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string");
+ return mrb_fixnum_value(utf8code((unsigned char*) RSTRING_PTR(str)));
+}
+#else
+static mrb_value
+mrb_str_ord(mrb_state* mrb, mrb_value str)
+{
+ if (RSTRING_LEN(str) == 0)
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string");
+ return mrb_fixnum_value(RSTRING_PTR(str)[0]);
+}
+#endif
+
void
mrb_mruby_string_ext_gem_init(mrb_state* mrb)
{
@@ -246,6 +540,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, s, "dump", mrb_str_dump, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "getbyte", mrb_str_getbyte, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "setbyte", mrb_str_setbyte, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "byteslice", mrb_str_byteslice, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
mrb_define_method(mrb, s, "swapcase!", mrb_str_swapcase_bang, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "swapcase", mrb_str_swapcase, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "concat", mrb_str_concat2, MRB_ARGS_REQ(1));
@@ -256,6 +552,14 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, s, "oct", mrb_str_oct, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "prepend", mrb_str_prepend, MRB_ARGS_REQ(1));
+ mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ"));
+ mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!"));
+ mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE());
}
void
diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb
index 2a8dd0cca..37a5d28cf 100644
--- a/mrbgems/mruby-string-ext/test/string.rb
+++ b/mrbgems/mruby-string-ext/test/string.rb
@@ -1,6 +1,15 @@
##
# String(Ext) Test
+UTF8STRING = ("\343\201\202".size == 1)
+
+assert('String.try_convert') do
+ assert_nil String.try_convert(nil)
+ assert_nil String.try_convert(:foo)
+ assert_equal "", String.try_convert("")
+ assert_equal "1,2,3", String.try_convert("1,2,3")
+end
+
assert('String#getbyte') do
str1 = "hello"
bytes1 = [104, 101, 108, 108, 111]
@@ -13,6 +22,22 @@ assert('String#getbyte') do
assert_equal bytes2[0], str2.getbyte(0)
end
+assert('String#setbyte') do
+ str1 = "hello"
+ h = "H".getbyte(0)
+ str1.setbyte(0, h)
+ assert_equal(h, str1.getbyte(0))
+ assert_equal("Hello", str1)
+end
+
+assert('String#byteslice') do
+ str1 = "hello"
+ assert_equal("e", str1.byteslice(1))
+ assert_equal("o", str1.byteslice(-1))
+ assert_equal("ell", str1.byteslice(1..3))
+ assert_equal("el", str1.byteslice(1...3))
+end
+
assert('String#dump') do
("\1" * 100).dump # should not raise an exception - regress #1210
"\0".inspect == "\"\\000\"" and
@@ -164,6 +189,8 @@ end
assert('String#chr') do
assert_equal "a", "abcde".chr
+ # test Fixnum#chr as well
+ assert_equal "a", 97.chr
end
assert('String#lines') do
@@ -251,3 +278,300 @@ assert('String#slice!') do
assert_raise(ArgumentError) { "foo".slice! }
end
+
+assert('String#succ') do
+ assert_equal "", "".succ
+ assert_equal "1", "0".succ
+ assert_equal "10", "9".succ
+ assert_equal "01", "00".succ
+ assert_equal "a1", "a0".succ
+ assert_equal "A1", "A0".succ
+ assert_equal "10", "09".succ
+ assert_equal "b0", "a9".succ
+ assert_equal "B0", "A9".succ
+
+ assert_equal "b", "a".succ
+ assert_equal "aa", "z".succ
+ assert_equal "ab", "aa".succ
+ assert_equal "Ab", "Aa".succ
+ assert_equal "0b", "0a".succ
+ assert_equal "ba", "az".succ
+ assert_equal "Ba", "Az".succ
+ assert_equal "1a", "0z".succ
+
+ assert_equal "B", "A".succ
+ assert_equal "AA", "Z".succ
+ assert_equal "AB", "AA".succ
+ assert_equal "aB", "aA".succ
+ assert_equal "0B", "0A".succ
+ assert_equal "BA", "AZ".succ
+ assert_equal "bA", "aZ".succ
+ assert_equal "1A", "0Z".succ
+
+ assert_equal ".", "-".succ
+ assert_equal "\x01\x00", "\xff".succ
+ assert_equal "-b", "-a".succ
+ assert_equal "-aa", "-z".succ
+ assert_equal "-a-b-", "-a-a-".succ
+ assert_equal "-b-", "-a-".succ
+ assert_equal "-aa-", "-z-".succ
+ assert_equal "あb", "あa".succ
+ assert_equal "あba", "あaz".succ
+
+ a = ""; a.succ!
+ assert_equal "", a
+ a = "0"; a.succ!
+ assert_equal "1", a
+ a = "9"; a.succ!
+ assert_equal "10", a
+ a = "00"; a.succ!
+ assert_equal "01", a
+ a = "a0"; a.succ!
+ assert_equal "a1", a
+ a = "A0"; a.succ!
+ assert_equal "A1", a
+ a = "09"; a.succ!
+ assert_equal "10", a
+ a = "a9"; a.succ!
+ assert_equal "b0", a
+ a = "A9"; a.succ!
+ assert_equal "B0", a
+
+ a = "a"; a.succ!
+ assert_equal "b", a
+ a = "z"; a.succ!
+ assert_equal "aa", a
+ a = "aa"; a.succ!
+ assert_equal "ab", a
+ a = "Aa"; a.succ!
+ assert_equal "Ab", a
+ a = "0a"; a.succ!
+ assert_equal "0b", a
+ a = "az"; a.succ!
+ assert_equal "ba", a
+ a = "Az"; a.succ!
+ assert_equal "Ba", a
+ a = "0z"; a.succ!
+ assert_equal "1a", a
+
+ a = "A"; a.succ!
+ assert_equal "B", a
+ a = "Z"; a.succ!
+ assert_equal "AA", a
+ a = "AA"; a.succ!
+ assert_equal "AB", a
+ a = "aA"; a.succ!
+ assert_equal "aB", a
+ a = "0A"; a.succ!
+ assert_equal "0B", a
+ a = "AZ"; a.succ!
+ assert_equal "BA", a
+ a = "aZ"; a.succ!
+ assert_equal "bA", a
+ a = "0Z"; a.succ!
+ assert_equal "1A", a
+
+ a = "-"; a.succ!
+ assert_equal ".", a
+ a = "\xff"; a.succ!
+ assert_equal "\x01\x00", a
+ a = "-a"; a.succ!
+ assert_equal "-b", a
+ a = "-z"; a.succ!
+ assert_equal "-aa", a
+ a = "-a-a-"; a.succ!
+ assert_equal "-a-b-", a
+ a = "-a-"; a.succ!
+ assert_equal "-b-", a
+ a = "-z-"; a.succ!
+ assert_equal "-aa-", a
+ a = "あb"; a.succ!
+ assert_equal "あc", a
+ a = "あaz"; a.succ!
+ assert_equal "あba", a
+end
+
+assert('String#next') do
+ assert_equal "01", "00".next
+
+ a = "00"; a.next!
+ assert_equal "01", a
+end
+
+assert('String#insert') do
+ assert_equal "Xabcd", "abcd".insert(0, 'X')
+ assert_equal "abcXd", "abcd".insert(3, 'X')
+ assert_equal "abcdX", "abcd".insert(4, 'X')
+ assert_equal "abXcd", "abcd".insert(-3, 'X')
+ assert_equal "abcdX", "abcd".insert(-1, 'X')
+ assert_raise(IndexError) { "abcd".insert(5, 'X') }
+ assert_raise(IndexError) { "abcd".insert(-6, 'X') }
+end
+
+assert('String#prepend') do
+ a = "world"
+ assert_equal "hello world", a.prepend("hello ")
+ assert_equal "hello world", a
+end
+
+assert('String#ljust') do
+ assert_equal "hello", "hello".ljust(4)
+ assert_equal "hello ", "hello".ljust(20)
+ assert_equal "hello123412341234123", "hello".ljust(20, '1234')
+ assert_equal "hello", "hello".ljust(-3)
+end
+
+assert('String#upto') do
+ a = "aa"
+ start = "aa"
+ count = 0
+ assert_equal("aa", a.upto("zz") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(676, count)
+
+ a = "a"
+ start = "a"
+ count = 0
+ assert_equal("a", a.upto("a") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(1, count)
+
+ a = "a"
+ start = "a"
+ count = 0
+ assert_equal("a", a.upto("b", true) {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(1, count)
+
+ a = "0"
+ start = "0"
+ count = 0
+ assert_equal("0", a.upto("0") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(1, count)
+
+ a = "0"
+ start = "0"
+ count = 0
+ assert_equal("0", a.upto("-1") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(0, count)
+
+ a = "-1"
+ start = "-1"
+ count = 0
+ assert_equal("-1", a.upto("-2") {|s|
+ assert_equal(start, s)
+ start.succ!
+ count += 1
+ })
+ assert_equal(2, count)
+end
+
+assert('String#ord') do
+ got = "hello!".split('').map {|x| x.ord}
+ expect = [104, 101, 108, 108, 111, 33]
+ assert_equal expect, got
+end
+
+assert('String#ord(UTF-8)') do
+ got = "こんにちは世界!".split('').map {|x| x.ord}
+ expect = [0x3053,0x3093,0x306b,0x3061,0x306f,0x4e16,0x754c,0x21]
+ assert_equal expect, got
+end if UTF8STRING
+
+assert('String#chr') do
+ assert_equal "h", "hello!".chr
+end
+assert('String#chr(UTF-8)') do
+ assert_equal "こ", "こんにちは世界!".chr
+end if UTF8STRING
+
+assert('String#chars') do
+ expect = ["h", "e", "l", "l", "o", "!"]
+ assert_equal expect, "hello!".chars
+ s = ""
+ "hello!".chars do |x|
+ s += x
+ end
+ assert_equal "hello!", s
+end
+
+assert('String#chars(UTF-8)') do
+ expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!']
+ assert_equal expect, "こんにちは世界!".chars
+ s = ""
+ "こんにちは世界!".chars do |x|
+ s += x
+ end
+ assert_equal "こんにちは世界!", s
+end if UTF8STRING
+
+assert('String#each_char') do
+ s = ""
+ "hello!".each_char do |x|
+ s += x
+ end
+ assert_equal "hello!", s
+end
+
+assert('String#each_char(UTF-8)') do
+ s = ""
+ "こんにちは世界!".each_char do |x|
+ s += x
+ end
+ assert_equal "こんにちは世界!", s
+end if UTF8STRING
+
+assert('String#codepoints') do
+ expect = [104, 101, 108, 108, 111, 33]
+ assert_equal expect, "hello!".codepoints
+ cp = []
+ "hello!".codepoints do |x|
+ cp << x
+ end
+ assert_equal expect, cp
+end
+
+assert('String#codepoints(UTF-8)') do
+ expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33]
+ assert_equal expect, "こんにちは世界!".codepoints
+ cp = []
+ "こんにちは世界!".codepoints do |x|
+ cp << x
+ end
+ assert_equal expect, cp
+end if UTF8STRING
+
+assert('String#each_codepoint') do
+ expect = [104, 101, 108, 108, 111, 33]
+ cp = []
+ "hello!".each_codepoint do |x|
+ cp << x
+ end
+ assert_equal expect, cp
+end
+
+assert('String#each_codepoint(UTF-8)') do
+ expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33]
+ cp = []
+ "こんにちは世界!".each_codepoint do |x|
+ cp << x
+ end
+ assert_equal expect, cp
+end if UTF8STRING
diff --git a/mrbgems/mruby-string-utf8/mrbgem.rake b/mrbgems/mruby-string-utf8/mrbgem.rake
deleted file mode 100644
index 7642d4e07..000000000
--- a/mrbgems/mruby-string-utf8/mrbgem.rake
+++ /dev/null
@@ -1,6 +0,0 @@
-MRuby::Gem::Specification.new('mruby-string-utf8') do |spec|
- spec.license = 'MIT'
- spec.author = 'mruby developers'
- spec.summary = 'UTF-8 support in String class'
- spec.add_dependency('mruby-string-ext', :core => 'mruby-string-ext')
-end
diff --git a/mrbgems/mruby-string-utf8/src/string.c b/mrbgems/mruby-string-utf8/src/string.c
deleted file mode 100644
index 874fa8dbb..000000000
--- a/mrbgems/mruby-string-utf8/src/string.c
+++ /dev/null
@@ -1,743 +0,0 @@
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/string.h"
-#include "mruby/range.h"
-#include "mruby/re.h"
-#include <ctype.h>
-#include <string.h>
-
-static const char utf8len_codepage[256] =
-{
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1,
-};
-
-static char utf8len_codepage_zero[256] =
-{
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,
-};
-
-static mrb_int
-utf8code(unsigned char* p)
-{
- mrb_int len;
-
- if (p[0] < 0x80)
- return p[0];
-
- len = utf8len_codepage_zero[p[0]];
- if (len > 1 && (p[1] & 0xc0) == 0x80) {
- if (len == 2)
- return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f);
- if ((p[2] & 0xc0) == 0x80) {
- if (len == 3)
- return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6)
- + (p[2] & 0x3f);
- if ((p[3] & 0xc0) == 0x80) {
- if (len == 4)
- return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12)
- + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f);
- if ((p[4] & 0xc0) == 0x80) {
- if (len == 5)
- return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18)
- + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6)
- + (p[4] & 0x3f);
- if ((p[5] & 0xc0) == 0x80 && len == 6)
- return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24)
- + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12)
- + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f);
- }
- }
- }
- }
- return p[0];
-}
-
-static mrb_value mrb_fixnum_chr(mrb_state*, mrb_value);
-
-static mrb_int
-utf8len(unsigned char* p)
-{
- mrb_int len;
- mrb_int i;
-
- if (*p == 0)
- return 1;
- len = utf8len_codepage[*p];
- for (i = 1; i < len; ++i)
- if ((p[i] & 0xc0) != 0x80)
- return 1;
- return len;
-}
-
-static mrb_int
-mrb_utf8_strlen(mrb_value str, mrb_int len)
-{
- mrb_int total = 0;
- unsigned char* p = (unsigned char*) RSTRING_PTR(str);
- unsigned char* e = p;
- e += len < 0 ? RSTRING_LEN(str) : len;
- while (p<e) {
- p += utf8len(p);
- total++;
- }
- return total;
-}
-
-static mrb_value
-mrb_str_size(mrb_state *mrb, mrb_value str)
-{
- return mrb_fixnum_value(mrb_utf8_strlen(str, -1));
-}
-
-#define RSTRING_LEN_UTF8(s) mrb_utf8_strlen(s, -1)
-
-static mrb_value
-noregexp(mrb_state *mrb, mrb_value self)
-{
- mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented");
- return mrb_nil_value();
-}
-
-static void
-regexp_check(mrb_state *mrb, mrb_value obj)
-{
- if (mrb_regexp_p(mrb, obj)) {
- noregexp(mrb, obj);
- }
-}
-
-static inline mrb_int
-mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n)
-{
- const unsigned char *x = xs, *xe = xs + m;
- const unsigned char *y = ys;
- int i, qstable[256];
-
- /* Preprocessing */
- for (i = 0; i < 256; ++i)
- qstable[i] = m + 1;
- for (; x < xe; ++x)
- qstable[*x] = xe - x;
- /* Searching */
- for (; y + m <= ys + n; y += *(qstable + y[m])) {
- if (*xs == *y && memcmp(xs, y, m) == 0)
- return y - ys;
- }
- return -1;
-}
-static mrb_int
-mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
-{
- const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0;
-
- if (m > n) return -1;
- else if (m == n) {
- return memcmp(x0, y0, m) == 0 ? 0 : -1;
- }
- else if (m < 1) {
- return 0;
- }
- else if (m == 1) {
- const unsigned char *ys = y, *ye = ys + n;
- for (; y < ye; ++y) {
- if (*x == *y)
- return y - ys;
- }
- return -1;
- }
- return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n);
-}
-
-static mrb_value
-str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
-{
- mrb_int i;
- unsigned char *p = (unsigned char*) RSTRING_PTR(str), *t;
- unsigned char *e = p + RSTRING_LEN(str);
-
- for (i = 0; i < beg && p<e; i++) {
- p += utf8len(p);
- }
- t = p;
- for (i = 0; i < len && t<e; i++) {
- t += utf8len(t);
- }
- return mrb_str_new(mrb, (const char*)p, (size_t)(t - p));
-}
-
-static mrb_value
-str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
-{
- mrb_value str2;
- mrb_int len8 = RSTRING_LEN_UTF8(str);
-
- if (len < 0) return mrb_nil_value();
- if (len8 == 0) {
- len = 0;
- }
- else if (beg < 0) {
- beg = len8 + beg;
- }
- if (beg > len8) return mrb_nil_value();
- if (beg < 0) {
- beg += len8;
- if (beg < 0) return mrb_nil_value();
- }
- if (beg + len > len8)
- len = len8 - beg;
- if (len <= 0) {
- len = 0;
- }
- str2 = str_subseq(mrb, str, beg, len);
-
- return str2;
-}
-
-static mrb_int
-str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
-{
- mrb_int pos;
- char *s, *sptr;
- mrb_int len, slen;
-
- len = RSTRING_LEN(str);
- slen = RSTRING_LEN(sub);
- if (offset < 0) {
- offset += len;
- if (offset < 0) return -1;
- }
- if (len - offset < slen) return -1;
- s = RSTRING_PTR(str);
- if (offset) {
- s += offset;
- }
- if (slen == 0) return offset;
- /* need proceed one character at a time */
- sptr = RSTRING_PTR(sub);
- slen = RSTRING_LEN(sub);
- len = RSTRING_LEN(str) - offset;
- pos = mrb_memsearch(sptr, slen, s, len);
- if (pos < 0) return pos;
- return pos + offset;
-}
-
-static mrb_int
-str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
-{
- char *s, *sbeg, *t;
- struct RString *ps = mrb_str_ptr(str);
- mrb_int len = RSTRING_LEN(sub);
-
- /* substring longer than string */
- if (RSTR_LEN(ps) < len) return -1;
- if (RSTR_LEN(ps) - pos < len) {
- pos = RSTR_LEN(ps) - len;
- }
- sbeg = RSTR_PTR(ps);
- s = RSTR_PTR(ps) + pos;
- t = RSTRING_PTR(sub);
- if (len) {
- while (sbeg <= s) {
- if (memcmp(s, t, len) == 0) {
- return s - RSTR_PTR(ps);
- }
- s--;
- }
- return -1;
- }
- else {
- return pos;
- }
-}
-
-static mrb_value
-mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx)
-{
- mrb_int idx;
-
- regexp_check(mrb, indx);
- switch (mrb_type(indx)) {
- case MRB_TT_FIXNUM:
- idx = mrb_fixnum(indx);
-
-num_index:
- str = str_substr(mrb, str, idx, 1);
- if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
- return str;
-
- case MRB_TT_STRING:
- if (str_index(mrb, str, indx, 0) != -1)
- return mrb_str_dup(mrb, indx);
- return mrb_nil_value();
-
- case MRB_TT_RANGE:
- /* check if indx is Range */
- {
- mrb_int beg, len;
- mrb_value tmp;
-
- len = RSTRING_LEN_UTF8(str);
- if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) {
- tmp = str_subseq(mrb, str, beg, len);
- return tmp;
- }
- else {
- return mrb_nil_value();
- }
- }
- default:
- idx = mrb_fixnum(indx);
- goto num_index;
- }
- return mrb_nil_value(); /* not reached */
-}
-
-static mrb_value
-mrb_str_aref_m(mrb_state *mrb, mrb_value str)
-{
- mrb_value a1, a2;
- int argc;
-
- argc = mrb_get_args(mrb, "o|o", &a1, &a2);
- if (argc == 2) {
- regexp_check(mrb, a1);
- return str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2));
- }
- if (argc != 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc));
- }
- return mrb_str_aref(mrb, str, a1);
-}
-
-static mrb_value
-mrb_str_index_m(mrb_state *mrb, mrb_value str)
-{
- mrb_value *argv;
- mrb_int argc;
-
- mrb_value sub;
- mrb_int pos;
-
- mrb_get_args(mrb, "*", &argv, &argc);
- if (argc == 2) {
- pos = mrb_fixnum(argv[1]);
- sub = argv[0];
- }
- else {
- pos = 0;
- if (argc > 0)
- sub = argv[0];
- else
- sub = mrb_nil_value();
-
- }
- regexp_check(mrb, sub);
- if (pos < 0) {
- pos += RSTRING_LEN(str);
- if (pos < 0) {
- return mrb_nil_value();
- }
- }
-
- if (mrb_type(sub) == MRB_TT_FIXNUM) {
- sub = mrb_fixnum_chr(mrb, sub);
- }
-
- switch (mrb_type(sub)) {
- default: {
- mrb_value tmp;
-
- tmp = mrb_check_string_type(mrb, sub);
- if (mrb_nil_p(tmp)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub);
- }
- sub = tmp;
- }
- /* fall through */
- case MRB_TT_STRING:
- pos = str_index(mrb, str, sub, pos);
- break;
- }
-
- if (pos == -1) return mrb_nil_value();
- return mrb_fixnum_value(mrb_utf8_strlen(str, pos));
-}
-
-static mrb_value
-mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
-{
- mrb_int utf8_len = mrb_utf8_strlen(str, -1);
- if (utf8_len > 1) {
- mrb_int len;
- char *buf;
- unsigned char *p, *e, *r;
-
- mrb_str_modify(mrb, mrb_str_ptr(str));
- len = RSTRING_LEN(str);
- buf = (char *)mrb_malloc(mrb, (size_t)len);
- p = (unsigned char*)buf;
- e = (unsigned char*)buf + len;
-
- memcpy(buf, RSTRING_PTR(str), len);
- r = (unsigned char*)RSTRING_PTR(str) + len;
-
- while (p<e) {
- mrb_int clen = utf8len(p);
- r -= clen;
- memcpy(r, p, clen);
- p += clen;
- }
- mrb_free(mrb, buf);
- }
-
- return str;
-}
-
-static mrb_value
-mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
-{
- mrb_value *argv;
- mrb_int argc;
- mrb_value sub;
- mrb_value vpos;
- mrb_int pos, len = RSTRING_LEN(str);
-
- mrb_get_args(mrb, "*", &argv, &argc);
- if (argc == 2) {
- sub = argv[0];
- vpos = argv[1];
- pos = mrb_fixnum(vpos);
- if (pos < 0) {
- pos += len;
- if (pos < 0) {
- regexp_check(mrb, sub);
- return mrb_nil_value();
- }
- }
- if (pos > len) pos = len;
- }
- else {
- pos = len;
- if (argc > 0)
- sub = argv[0];
- else
- sub = mrb_nil_value();
- }
- regexp_check(mrb, sub);
-
- if (mrb_type(sub) == MRB_TT_FIXNUM) {
- sub = mrb_fixnum_chr(mrb, sub);
- }
-
- switch (mrb_type(sub)) {
- default: {
- mrb_value tmp;
-
- tmp = mrb_check_string_type(mrb, sub);
- if (mrb_nil_p(tmp)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub);
- }
- sub = tmp;
- }
- /* fall through */
- case MRB_TT_STRING:
- pos = str_rindex(mrb, str, sub, pos);
- break;
- }
-
- if (pos == -1) return mrb_nil_value();
- return mrb_fixnum_value(mrb_utf8_strlen(str, pos));
-}
-
-static mrb_value
-mrb_str_reverse(mrb_state *mrb, mrb_value str)
-{
- return mrb_str_reverse_bang(mrb, mrb_str_dup(mrb, str));
-}
-
-static mrb_value
-mrb_fixnum_chr(mrb_state *mrb, mrb_value num)
-{
- mrb_int cp = mrb_fixnum(num);
- char utf8[4];
- mrb_int len;
-
- if (cp < 0 || 0x10FFFF < cp) {
- mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
- }
- if (cp < 0x80) {
- utf8[0] = (char)cp;
- len = 1;
- }
- else if (cp < 0x800) {
- utf8[0] = (char)(0xC0 | (cp >> 6));
- utf8[1] = (char)(0x80 | (cp & 0x3F));
- len = 2;
- }
- else if (cp < 0x10000) {
- utf8[0] = (char)(0xE0 | (cp >> 12));
- utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
- utf8[2] = (char)(0x80 | ( cp & 0x3F));
- len = 3;
- }
- else {
- utf8[0] = (char)(0xF0 | (cp >> 18));
- utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
- utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F));
- utf8[3] = (char)(0x80 | ( cp & 0x3F));
- len = 4;
- }
- return mrb_str_new(mrb, utf8, len);
-}
-
-static mrb_value
-mrb_str_ord(mrb_state* mrb, mrb_value str)
-{
- mrb_int len = RSTRING_LEN(str);
-
- if (len == 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string");
- return mrb_fixnum_value(utf8code((unsigned char*) RSTRING_PTR(str)));
-}
-
-static mrb_value
-mrb_str_split_m(mrb_state *mrb, mrb_value str)
-{
- int argc;
- mrb_value spat = mrb_nil_value();
- enum {awk, string, regexp} split_type = string;
- long i = 0, lim_p;
- mrb_int beg;
- mrb_int end;
- mrb_int lim = 0;
- mrb_value result, tmp;
-
- argc = mrb_get_args(mrb, "|oi", &spat, &lim);
- lim_p = (lim > 0 && argc == 2);
- if (argc == 2) {
- if (lim == 1) {
- if (RSTRING_LEN(str) == 0)
- return mrb_ary_new_capa(mrb, 0);
- return mrb_ary_new_from_values(mrb, 1, &str);
- }
- i = 1;
- }
-
- if (argc == 0 || mrb_nil_p(spat)) {
- split_type = awk;
- }
- else {
- if (mrb_string_p(spat)) {
- split_type = string;
- if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' '){
- split_type = awk;
- }
- }
- else {
- noregexp(mrb, str);
- }
- }
-
- result = mrb_ary_new(mrb);
- beg = 0;
- if (split_type == awk) {
- char *ptr = RSTRING_PTR(str);
- char *eptr = RSTRING_END(str);
- char *bptr = ptr;
- int skip = 1;
- unsigned int c;
-
- end = beg;
- while (ptr < eptr) {
- int ai = mrb_gc_arena_save(mrb);
- c = (unsigned char)*ptr++;
- if (skip) {
- if (ISSPACE(c)) {
- beg = ptr - bptr;
- }
- else {
- end = ptr - bptr;
- skip = 0;
- if (lim_p && lim <= i) break;
- }
- }
- else if (ISSPACE(c)) {
- mrb_ary_push(mrb, result, str_subseq(mrb, str, beg, end-beg));
- mrb_gc_arena_restore(mrb, ai);
- skip = 1;
- beg = ptr - bptr;
- if (lim_p) ++i;
- }
- else {
- end = ptr - bptr;
- }
- }
- }
- else if (split_type == string) {
- char *ptr = RSTRING_PTR(str); // s->as.ary
- char *temp = ptr;
- char *eptr = RSTRING_END(str);
- mrb_int slen = RSTRING_LEN(spat);
-
- if (slen == 0) {
- int ai = mrb_gc_arena_save(mrb);
- while (ptr < eptr) {
- mrb_ary_push(mrb, result, str_subseq(mrb, str, ptr-temp, 1));
- mrb_gc_arena_restore(mrb, ai);
- ptr++;
- if (lim_p && lim <= ++i) break;
- }
- }
- else {
- char *sptr = RSTRING_PTR(spat);
- int ai = mrb_gc_arena_save(mrb);
-
- while (ptr < eptr &&
- (end = mrb_memsearch(sptr, slen, ptr, eptr - ptr)) >= 0) {
- // mrb_ary_push(mrb, result, str_subseq(mrb, str, ptr - temp, end));
- mrb_ary_push(mrb, result, mrb_str_new(mrb, ptr, end));
- mrb_gc_arena_restore(mrb, ai);
- ptr += end + slen;
- if (lim_p && lim <= ++i) break;
- }
- }
- beg = ptr - temp;
- }
- else {
- noregexp(mrb, str);
- }
- if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) {
- if (RSTRING_LEN(str) == beg) {
- tmp = mrb_str_new_lit(mrb, "");
- }
- else {
- tmp = mrb_str_new(mrb, RSTRING_PTR(str)+beg, RSTRING_LEN(str)-beg);
- }
- mrb_ary_push(mrb, result, tmp);
- }
- if (!lim_p && lim == 0) {
- mrb_int len;
- while ((len = RARRAY_LEN(result)) > 0 &&
- (tmp = RARRAY_PTR(result)[len-1], RSTRING_LEN(tmp) == 0))
- mrb_ary_pop(mrb, result);
- }
-
- return result;
-}
-
-static mrb_value
-mrb_str_chr(mrb_state *mrb, mrb_value self)
-{
- return str_substr(mrb, self, 0, 1);
-}
-
-static mrb_value
-mrb_str_chars(mrb_state *mrb, mrb_value self)
-{
- mrb_value result;
- mrb_value blk;
- int ai;
- mrb_int len;
- mrb_value arg;
- char *p = RSTRING_PTR(self);
- char *e = p + RSTRING_LEN(self);
-
- mrb_get_args(mrb, "&", &blk);
-
- result = mrb_ary_new(mrb);
-
- if (!mrb_nil_p(blk)) {
- while (p < e) {
- len = utf8len((unsigned char*) p);
- arg = mrb_str_new(mrb, p, len);
- mrb_yield_argv(mrb, blk, 1, &arg);
- p += len;
- }
- return self;
- }
- while (p < e) {
- ai = mrb_gc_arena_save(mrb);
- len = utf8len((unsigned char*) p);
- mrb_ary_push(mrb, result, mrb_str_new(mrb, p, len));
- mrb_gc_arena_restore(mrb, ai);
- p += len;
- }
- return result;
-}
-
-static mrb_value
-mrb_str_codepoints(mrb_state *mrb, mrb_value self)
-{
- mrb_value result;
- mrb_value blk;
- int ai;
- mrb_int len;
- mrb_value arg;
- char *p = RSTRING_PTR(self);
- char *e = p + RSTRING_LEN(self);
-
- mrb_get_args(mrb, "&", &blk);
-
- result = mrb_ary_new(mrb);
-
- if (!mrb_nil_p(blk)) {
- while (p < e) {
- len = utf8len((unsigned char*) p);
- arg = mrb_fixnum_value(utf8code((unsigned char*) p));
- mrb_yield_argv(mrb, blk, 1, &arg);
- p += len;
- }
- return self;
- }
- while (p < e) {
- ai = mrb_gc_arena_save(mrb);
- len = utf8len((unsigned char*) p);
- mrb_ary_push(mrb, result, mrb_fixnum_value(utf8code((unsigned char*) p)));
- mrb_gc_arena_restore(mrb, ai);
- p += len;
- }
- return result;
-}
-
-void
-mrb_mruby_string_utf8_gem_init(mrb_state* mrb)
-{
- struct RClass * s = mrb->string_class;
-
- mrb_define_method(mrb, s, "size", mrb_str_size, MRB_ARGS_NONE());
- mrb_define_method(mrb, s, "length", mrb_str_size, MRB_ARGS_NONE());
- mrb_define_method(mrb, s, "index", mrb_str_index_m, MRB_ARGS_ANY());
- mrb_define_method(mrb, s, "[]", mrb_str_aref_m, MRB_ARGS_ANY());
- mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE());
- mrb_define_method(mrb, s, "slice", mrb_str_aref_m, MRB_ARGS_ANY());
- mrb_define_method(mrb, s, "split", mrb_str_split_m, MRB_ARGS_ANY());
- mrb_define_method(mrb, s, "reverse", mrb_str_reverse, MRB_ARGS_NONE());
- mrb_define_method(mrb, s, "reverse!", mrb_str_reverse_bang, MRB_ARGS_NONE());
- mrb_define_method(mrb, s, "rindex", mrb_str_rindex_m, MRB_ARGS_ANY());
- mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE());
- mrb_define_method(mrb, s, "chars", mrb_str_chars, MRB_ARGS_NONE());
- mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "each_char"), mrb_intern_lit(mrb, "chars"));
- mrb_define_method(mrb, s, "codepoints", mrb_str_codepoints, MRB_ARGS_NONE());
- mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "each_codepoint"), mrb_intern_lit(mrb, "codepoints"));
-
- mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE());
-}
-
-void
-mrb_mruby_string_utf8_gem_final(mrb_state* mrb)
-{
-}
diff --git a/mrbgems/mruby-string-utf8/test/string.rb b/mrbgems/mruby-string-utf8/test/string.rb
deleted file mode 100644
index 5b4180037..000000000
--- a/mrbgems/mruby-string-utf8/test/string.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-# -*- coding: utf-8 -*-
-##
-# String(utf8) Test
-
-assert('String#[]') do
- assert_equal "ち", "こんにちは世界"[3]
- assert_equal nil, "こんにちは世界"[20]
- assert_equal "世", "こんにちは世界"[-2]
- assert_equal "世界", "こんにちは世界"[-2..-1]
- assert_equal "んに", "こんにちは世界"[1,2]
- assert_equal "世", "こんにちは世界"["世"]
-end
-
-assert('String#reverse', '15.2.10.5.29') do
- a = 'こんにちは世界!'
- a.reverse
-
- assert_equal 'こんにちは世界!', a
- assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse
-end
-
-assert('String#reverse!', '15.2.10.5.30') do
- a = 'こんにちは世界!'
- a.reverse!
-
- assert_equal '!界世はちにんこ', a
- assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse!
-end
-
-assert('Invalid sequence') do
- assert_equal 5, "\xF8\x88\x80\x80\x80".size
- assert_equal 6, "\xFC\x84\x80\x80\x80\x80".size
-end
-
-assert('String#size') do
- str = 'こんにちは世界!'
- assert_equal 8, str.size
- assert_not_equal str.bytesize, str.size
- assert_equal 2, str[1, 2].size
-end
-
-assert('String#index') do
- str = "こんにちは世界!\nこんにちは世界!"
- assert_nil str.index('さ')
- assert_equal 3, str.index('ち')
- assert_equal 12, str.index('ち', 10)
- assert_equal nil, str.index("さ")
-end
-
-assert('String#ord') do
- got = "こんにちは世界!".split('').map {|x| x.ord}
- expect = [0x3053,0x3093,0x306b,0x3061,0x306f,0x4e16,0x754c,0x21]
- assert_equal expect, got
-end
-
-assert('String#split') do
- got = "こんにちは世界!".split('')
- assert_equal ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'], got
- got = "こんにちは世界!".split('に')
- assert_equal ['こん', 'ちは世界!'], got
-end
-
-assert('String#rindex') do
- str = "こんにちは世界!\nこんにちは世界!"
- assert_nil str.index('さ')
- assert_equal 12, str.rindex('ち')
- assert_equal 3, str.rindex('ち', 10)
-end
-
-assert('String#chr(utf-8)') do
- assert_equal "こ", "こんにちは世界!".chr
-end
-
-assert('String#chars') do
- expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!']
- assert_equal expect, "こんにちは世界!".chars
- s = ""
- "こんにちは世界!".chars do |x|
- s += x
- end
- assert_equal "こんにちは世界!", s
-end
-
-assert('String#each_char') do
- expect = ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!']
- s = ""
- "こんにちは世界!".each_char do |x|
- s += x
- end
- assert_equal "こんにちは世界!", s
-end
-assert('String#codepoints') do
- expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33]
- assert_equal expect, "こんにちは世界!".codepoints
- cp = []
- "こんにちは世界!".codepoints do |x|
- cp << x
- end
- assert_equal expect, cp
-end
-
-assert('String#each_codepoint') do
- expect = [12371, 12435, 12395, 12385, 12399, 19990, 30028, 33]
- cp = []
- "こんにちは世界!".each_codepoint do |x|
- cp << x
- end
- assert_equal expect, cp
-end
diff --git a/mrbgems/mruby-struct/mrblib/struct.rb b/mrbgems/mruby-struct/mrblib/struct.rb
index 5d0ede90f..1445392f3 100644
--- a/mrbgems/mruby-struct/mrblib/struct.rb
+++ b/mrbgems/mruby-struct/mrblib/struct.rb
@@ -45,6 +45,42 @@ if Object.const_defined?(:Struct)
}
ary
end
+
+ def _inspect
+ name = self.class.to_s
+ if name[0] == "#"
+ str = "#<struct "
+ else
+ str = "#<struct #{name} "
+ end
+ buf = []
+ self.each_pair do |k,v|
+ buf.push [k.to_s + "=" + v._inspect]
+ end
+ str + buf.join(", ") + ">"
+ end
+
+ ##
+ # call-seq:
+ # struct.to_s -> string
+ # struct.inspect -> string
+ #
+ # Describe the contents of this struct in a string.
+ #
+ # 15.2.18.4.10(x)
+ #
+ def inspect
+ begin
+ self._inspect
+ rescue SystemStackError
+ "#<struct #{self.class.to_s}:...>"
+ end
+ end
+
+ ##
+ # 15.2.18.4.11(x)
+ #
+ alias to_s inspect
end
end
diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c
index 85e1ce0ee..ea91a0436 100644
--- a/mrbgems/mruby-struct/src/struct.c
+++ b/mrbgems/mruby-struct/src/struct.c
@@ -4,18 +4,17 @@
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/string.h"
-#include "mruby/class.h"
-#include "mruby/variable.h"
-#include "mruby/hash.h"
-#include "mruby/range.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
+#include <mruby/variable.h>
+#include <mruby/hash.h>
+#include <mruby/range.h>
-#define RSTRUCT_LEN(st) RARRAY_LEN(st)
-#define RSTRUCT_PTR(st) RARRAY_PTR(st)
+#define RSTRUCT_LEN(st) mrb_ary_ptr(st)->len
+#define RSTRUCT_PTR(st) mrb_ary_ptr(st)->ptr
static struct RClass *
struct_class(mrb_state *mrb)
@@ -41,9 +40,9 @@ struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id)
}
static mrb_value
-mrb_struct_s_members(mrb_state *mrb, mrb_value klass)
+struct_s_members(mrb_state *mrb, struct RClass *klass)
{
- mrb_value members = struct_ivar_get(mrb, klass, mrb_intern_lit(mrb, "__members__"));
+ mrb_value members = struct_ivar_get(mrb, mrb_obj_value(klass), mrb_intern_lit(mrb, "__members__"));
if (mrb_nil_p(members)) {
mrb_raise(mrb, E_TYPE_ERROR, "uninitialized struct");
@@ -55,15 +54,16 @@ mrb_struct_s_members(mrb_state *mrb, mrb_value klass)
}
static mrb_value
-mrb_struct_members(mrb_state *mrb, mrb_value s)
-{
- mrb_value members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_obj_class(mrb, s)));
- if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct")) {
- if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
- mrb_raisef(mrb, E_TYPE_ERROR,
- "struct size differs (%S required %S given)",
- mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s)));
- }
+struct_members(mrb_state *mrb, mrb_value s)
+{
+ mrb_value members = struct_s_members(mrb, mrb_obj_class(mrb, s));
+ if (!mrb_array_p(s)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "corrupted struct");
+ }
+ if (RSTRUCT_LEN(s) != RARRAY_LEN(members)) {
+ mrb_raisef(mrb, E_TYPE_ERROR,
+ "struct size differs (%S required %S given)",
+ mrb_fixnum_value(RARRAY_LEN(members)), mrb_fixnum_value(RSTRUCT_LEN(s)));
}
return members;
}
@@ -72,15 +72,10 @@ static mrb_value
mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
{
mrb_value members, ary;
- mrb_value *p, *pend;
- members = mrb_struct_s_members(mrb, klass);
+ members = struct_s_members(mrb, mrb_class_ptr(klass));
ary = mrb_ary_new_capa(mrb, RARRAY_LEN(members));
- p = RARRAY_PTR(members); pend = p + RARRAY_LEN(members);
- while (p < pend) {
- mrb_ary_push(mrb, ary, *p);
- p++;
- }
+ mrb_ary_replace(mrb, ary, members);
return ary;
}
@@ -98,7 +93,7 @@ mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
*/
static mrb_value
-mrb_struct_members_m(mrb_state *mrb, mrb_value obj)
+mrb_struct_members(mrb_state *mrb, mrb_value obj)
{
return mrb_struct_s_members_m(mrb, mrb_obj_value(mrb_obj_class(mrb, obj)));
}
@@ -106,11 +101,12 @@ mrb_struct_members_m(mrb_state *mrb, mrb_value obj)
static mrb_value
mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id)
{
- mrb_value members, slot, *ptr, *ptr_members;
+ mrb_value members, slot, *ptr;
+ const mrb_value *ptr_members;
mrb_int i, len;
ptr = RSTRUCT_PTR(obj);
- members = mrb_struct_members(mrb, obj);
+ members = struct_members(mrb, obj);
ptr_members = RARRAY_PTR(members);
slot = mrb_symbol_value(id);
len = RARRAY_LEN(members);
@@ -119,7 +115,7 @@ mrb_struct_getmember(mrb_state *mrb, mrb_value obj, mrb_sym id)
return ptr[i];
}
}
- mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, id));
+ mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, id));
return mrb_nil_value(); /* not reached */
}
@@ -181,13 +177,14 @@ mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val)
const char *name;
mrb_int i, len, slen;
mrb_sym mid;
- mrb_value members, slot, *ptr, *ptr_members;
+ mrb_value members, slot, *ptr;
+ const mrb_value *ptr_members;
/* get base id */
name = mrb_sym2name_len(mrb, mrb->c->ci->mid, &slen);
mid = mrb_intern(mrb, name, slen-1); /* omit last "=" */
- members = mrb_struct_members(mrb, obj);
+ members = struct_members(mrb, obj);
ptr_members = RARRAY_PTR(members);
len = RARRAY_LEN(members);
ptr = RSTRUCT_PTR(obj);
@@ -197,7 +194,7 @@ mrb_struct_set(mrb_state *mrb, mrb_value obj, mrb_value val)
return ptr[i] = val;
}
}
- mrb_raisef(mrb, E_INDEX_ERROR, "`%S' is not a struct member", mrb_sym2str(mrb, mid));
+ mrb_raisef(mrb, E_INDEX_ERROR, "'%S' is not a struct member", mrb_sym2str(mrb, mid));
return mrb_nil_value(); /* not reached */
}
@@ -224,14 +221,37 @@ is_const_id(mrb_state *mrb, const char *name)
return ISUPPER(name[0]);
}
+static void
+make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c)
+{
+ const mrb_value *ptr_members = RARRAY_PTR(members);
+ mrb_int i;
+ mrb_int len = RARRAY_LEN(members);
+ int ai = mrb_gc_arena_save(mrb);
+
+ for (i=0; i<len; i++) {
+ mrb_sym id = mrb_symbol(ptr_members[i]);
+ const char *name = mrb_sym2name_len(mrb, id, NULL);
+
+ if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
+ if (i < N_REF_FUNC) {
+ mrb_define_method_id(mrb, c, id, ref_func[i], MRB_ARGS_NONE());
+ }
+ else {
+ mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
+ }
+ mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
+ mrb_gc_arena_restore(mrb, ai);
+ }
+ }
+}
+
static mrb_value
make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * klass)
{
- mrb_value nstr, *ptr_members;
+ mrb_value nstr;
mrb_sym id;
- mrb_int i, len;
struct RClass *c;
- int ai;
if (mrb_nil_p(name)) {
c = mrb_class_new(mrb, klass);
@@ -243,7 +263,7 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k
if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) {
mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
}
- if (mrb_const_defined_at(mrb, klass, id)) {
+ if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
mrb_warn(mrb, "redefining constant Struct::%S", name);
/* ?rb_mod_remove_const(klass, mrb_sym2name(mrb, id)); */
}
@@ -257,24 +277,7 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k
mrb_define_class_method(mrb, c, "[]", mrb_instance_new, MRB_ARGS_ANY());
mrb_define_class_method(mrb, c, "members", mrb_struct_s_members_m, MRB_ARGS_NONE());
/* RSTRUCT(nstr)->basic.c->super = c->c; */
- ptr_members = RARRAY_PTR(members);
- len = RARRAY_LEN(members);
- ai = mrb_gc_arena_save(mrb);
- for (i=0; i< len; i++) {
- mrb_sym id = mrb_symbol(ptr_members[i]);
- const char *name = mrb_sym2name_len(mrb, id, NULL);
-
- if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
- if (i < N_REF_FUNC) {
- mrb_define_method_id(mrb, c, id, ref_func[i], MRB_ARGS_NONE());
- }
- else {
- mrb_define_method_id(mrb, c, id, mrb_struct_ref, MRB_ARGS_NONE());
- }
- mrb_define_method_id(mrb, c, mrb_id_attrset(mrb, id), mrb_struct_set_m, MRB_ARGS_REQ(1));
- mrb_gc_arena_restore(mrb, ai);
- }
- }
+ make_struct_define_accessors(mrb, members, c);
return nstr;
}
@@ -353,7 +356,7 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
}
for (i=0; i<RARRAY_LEN(rest); i++) {
id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
- RARRAY_PTR(rest)[i] = mrb_symbol_value(id);
+ mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
}
}
st = make_struct(mrb, name, rest, struct_class(mrb));
@@ -400,7 +403,7 @@ mrb_struct_initialize_withArg(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb
}
static mrb_value
-mrb_struct_initialize_m(mrb_state *mrb, /*int argc, mrb_value *argv,*/ mrb_value self)
+mrb_struct_initialize(mrb_state *mrb, mrb_value self)
{
mrb_value *argv;
mrb_int argc;
@@ -409,67 +412,6 @@ mrb_struct_initialize_m(mrb_state *mrb, /*int argc, mrb_value *argv,*/ mrb_value
return mrb_struct_initialize_withArg(mrb, argc, argv, self);
}
-static mrb_value
-inspect_struct(mrb_state *mrb, mrb_value s, mrb_bool recur)
-{
- const char *cn = mrb_class_name(mrb, mrb_obj_class(mrb, s));
- mrb_value members, str = mrb_str_new_lit(mrb, "#<struct ");
- mrb_value *ptr, *ptr_members;
- mrb_int i, len;
-
- if (cn) {
- mrb_str_append(mrb, str, mrb_str_new_cstr(mrb, cn));
- }
- if (recur) {
- return mrb_str_cat_lit(mrb, str, ":...>");
- }
-
- members = mrb_struct_members(mrb, s);
- ptr_members = RARRAY_PTR(members);
- ptr = RSTRUCT_PTR(s);
- len = RSTRUCT_LEN(s);
- for (i=0; i<len; i++) {
- mrb_value slot;
- mrb_sym id;
- const char *name;
- mrb_int len;
-
- if (i > 0) {
- mrb_str_cat_lit(mrb, str, ", ");
- }
- else if (cn) {
- mrb_str_cat_lit(mrb, str, " ");
- }
- slot = ptr_members[i];
- id = mrb_symbol(slot);
- name = mrb_sym2name_len(mrb, id, &len);
- if (is_local_id(mrb, name) || is_const_id(mrb, name)) {
- mrb_str_append(mrb, str, mrb_str_new(mrb, name, len));
- }
- else {
- mrb_str_append(mrb, str, mrb_inspect(mrb, slot));
- }
- mrb_str_cat_lit(mrb, str, "=");
- mrb_str_append(mrb, str, mrb_inspect(mrb, ptr[i]));
- }
- mrb_str_cat_lit(mrb, str, ">");
-
- return str;
-}
-
-/*
- * call-seq:
- * struct.to_s -> string
- * struct.inspect -> string
- *
- * Describe the contents of this struct in a string.
- */
-static mrb_value
-mrb_struct_inspect(mrb_state *mrb, mrb_value s)
-{
- return inspect_struct(mrb, s, FALSE);
-}
-
/* 15.2.18.4.9 */
/* :nodoc: */
static mrb_value
@@ -500,11 +442,12 @@ mrb_struct_init_copy(mrb_state *mrb, mrb_value copy)
static mrb_value
struct_aref_sym(mrb_state *mrb, mrb_value s, mrb_sym id)
{
- mrb_value *ptr, members, *ptr_members;
+ mrb_value *ptr, members;
+ const mrb_value *ptr_members;
mrb_int i, len;
ptr = RSTRUCT_PTR(s);
- members = mrb_struct_members(mrb, s);
+ members = struct_members(mrb, s);
ptr_members = RARRAY_PTR(members);
len = RARRAY_LEN(members);
for (i=0; i<len; i++) {
@@ -573,10 +516,11 @@ mrb_struct_aref(mrb_state *mrb, mrb_value s)
static mrb_value
mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val)
{
- mrb_value members, *ptr, *ptr_members;
+ mrb_value members, *ptr;
+ const mrb_value *ptr_members;
mrb_int i, len;
- members = mrb_struct_members(mrb, s);
+ members = struct_members(mrb, s);
len = RARRAY_LEN(members);
if (RSTRUCT_LEN(s) != len) {
mrb_raisef(mrb, E_TYPE_ERROR,
@@ -677,34 +621,27 @@ mrb_struct_equal(mrb_state *mrb, mrb_value s)
mrb_value s2;
mrb_value *ptr, *ptr2;
mrb_int i, len;
- mrb_bool equal_p;
mrb_get_args(mrb, "o", &s2);
if (mrb_obj_equal(mrb, s, s2)) {
- equal_p = 1;
+ return mrb_true_value();
}
- else if (!strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s)), "Struct") ||
- mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
- equal_p = 0;
+ if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
+ return mrb_false_value();
}
- else if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
+ if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
mrb_bug(mrb, "inconsistent struct"); /* should never happen */
- equal_p = 0; /* This substuture is just to suppress warnings. never called. */
}
- else {
- ptr = RSTRUCT_PTR(s);
- ptr2 = RSTRUCT_PTR(s2);
- len = RSTRUCT_LEN(s);
- equal_p = 1;
- for (i=0; i<len; i++) {
- if (!mrb_equal(mrb, ptr[i], ptr2[i])) {
- equal_p = 0;
- break;
- }
+ ptr = RSTRUCT_PTR(s);
+ ptr2 = RSTRUCT_PTR(s2);
+ len = RSTRUCT_LEN(s);
+ for (i=0; i<len; i++) {
+ if (!mrb_equal(mrb, ptr[i], ptr2[i])) {
+ return mrb_false_value();
}
}
- return mrb_bool_value(equal_p);
+ return mrb_true_value();
}
/* 15.2.18.4.12(x) */
@@ -721,34 +658,27 @@ mrb_struct_eql(mrb_state *mrb, mrb_value s)
mrb_value s2;
mrb_value *ptr, *ptr2;
mrb_int i, len;
- mrb_bool eql_p;
mrb_get_args(mrb, "o", &s2);
if (mrb_obj_equal(mrb, s, s2)) {
- eql_p = 1;
+ return mrb_true_value();
}
- else if (strcmp(mrb_class_name(mrb, mrb_obj_class(mrb, s2)), "Struct") ||
- mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
- eql_p = 0;
+ if (mrb_obj_class(mrb, s) != mrb_obj_class(mrb, s2)) {
+ return mrb_false_value();
}
- else if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
+ if (RSTRUCT_LEN(s) != RSTRUCT_LEN(s2)) {
mrb_bug(mrb, "inconsistent struct"); /* should never happen */
- eql_p = 0; /* This substuture is just to suppress warnings. never called. */
}
- else {
- ptr = RSTRUCT_PTR(s);
- ptr2 = RSTRUCT_PTR(s2);
- len = RSTRUCT_LEN(s);
- eql_p = 1;
- for (i=0; i<len; i++) {
- if (!mrb_eql(mrb, ptr[i], ptr2[i])) {
- eql_p = 0;
- break;
- }
+ ptr = RSTRUCT_PTR(s);
+ ptr2 = RSTRUCT_PTR(s2);
+ len = RSTRUCT_LEN(s);
+ for (i=0; i<len; i++) {
+ if (!mrb_eql(mrb, ptr[i], ptr2[i])) {
+ return mrb_false_value();
}
}
- return mrb_bool_value(eql_p);
+ return mrb_true_value();
}
/*
@@ -789,7 +719,7 @@ mrb_struct_to_h(mrb_state *mrb, mrb_value self)
mrb_value members, ret;
mrb_int i;
- members = mrb_struct_s_members(mrb, mrb_obj_value(mrb_class(mrb, self)));
+ members = struct_s_members(mrb, mrb_class(mrb, self));
ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members));
for (i = 0; i < RARRAY_LEN(members); ++i) {
@@ -818,8 +748,8 @@ mrb_struct_values_at(mrb_state *mrb, mrb_value self)
* The <code>Struct</code> class is a generator of specific classes,
* each one of which is defined to hold a set of variables and their
* accessors. In these examples, we'll call the generated class
- * ``<i>Customer</i>Class,'' and we'll show an example instance of that
- * class as ``<i>Customer</i>Inst.''
+ * "<i>Customer</i>Class," and we'll show an example instance of that
+ * class as "<i>Customer</i>Inst."
*
* In the descriptions that follow, the parameter <i>symbol</i> refers
* to a symbol, which is either a quoted string or a
@@ -836,11 +766,9 @@ mrb_mruby_struct_gem_init(mrb_state* mrb)
mrb_define_method(mrb, st, "==", mrb_struct_equal, MRB_ARGS_REQ(1)); /* 15.2.18.4.1 */
mrb_define_method(mrb, st, "[]", mrb_struct_aref, MRB_ARGS_REQ(1)); /* 15.2.18.4.2 */
mrb_define_method(mrb, st, "[]=", mrb_struct_aset, MRB_ARGS_REQ(2)); /* 15.2.18.4.3 */
- mrb_define_method(mrb, st, "members", mrb_struct_members_m, MRB_ARGS_NONE()); /* 15.2.18.4.6 */
- mrb_define_method(mrb, st, "initialize", mrb_struct_initialize_m,MRB_ARGS_ANY()); /* 15.2.18.4.8 */
+ mrb_define_method(mrb, st, "members", mrb_struct_members, MRB_ARGS_NONE()); /* 15.2.18.4.6 */
+ mrb_define_method(mrb, st, "initialize", mrb_struct_initialize, MRB_ARGS_ANY()); /* 15.2.18.4.8 */
mrb_define_method(mrb, st, "initialize_copy", mrb_struct_init_copy, MRB_ARGS_REQ(1)); /* 15.2.18.4.9 */
- mrb_define_method(mrb, st, "inspect", mrb_struct_inspect, MRB_ARGS_NONE()); /* 15.2.18.4.10(x) */
- mrb_define_alias(mrb, st, "to_s", "inspect"); /* 15.2.18.4.11(x) */
mrb_define_method(mrb, st, "eql?", mrb_struct_eql, MRB_ARGS_REQ(1)); /* 15.2.18.4.12(x) */
mrb_define_method(mrb, st, "size", mrb_struct_len, MRB_ARGS_NONE());
diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb
index 172058ecd..0307bb1db 100644
--- a/mrbgems/mruby-struct/test/struct.rb
+++ b/mrbgems/mruby-struct/test/struct.rb
@@ -114,7 +114,7 @@ end
assert('struct inspect') do
c = Struct.new(:m1, :m2, :m3, :m4, :m5)
cc = c.new(1,2,3,4,5)
- assert_equal "#<struct #{c.inspect} m1=1, m2=2, m3=3, m4=4, m5=5>", cc.inspect
+ assert_equal "#<struct m1=1, m2=2, m3=3, m4=4, m5=5>", cc.inspect
end
assert('Struct#length, Struct#size') do
diff --git a/mrbgems/mruby-symbol-ext/mrbgem.rake b/mrbgems/mruby-symbol-ext/mrbgem.rake
index b937a0742..4f3fa43bb 100644
--- a/mrbgems/mruby-symbol-ext/mrbgem.rake
+++ b/mrbgems/mruby-symbol-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-symbol-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extensional Symbol class'
+ spec.summary = 'Symbol class extension'
end
diff --git a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb b/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
index 4cf18f647..1e3d24b80 100644
--- a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
+++ b/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
@@ -1,6 +1,8 @@
class Symbol
include Comparable
+ alias intern to_sym
+
def to_proc
->(obj,*args,&block) do
obj.__send__(self, *args, &block)
@@ -9,23 +11,12 @@ class Symbol
##
# call-seq:
- # sym.length -> integer
- #
- # Same as <code>sym.to_s.length</code>.
-
- def length
- self.to_s.length
- end
- alias :size :length
-
- ##
- # call-seq:
# sym.capitalize -> symbol
#
# Same as <code>sym.to_s.capitalize.intern</code>.
def capitalize
- self.to_s.capitalize.intern
+ (self.to_s.capitalize! || self).to_sym
end
##
@@ -35,7 +26,7 @@ class Symbol
# Same as <code>sym.to_s.downcase.intern</code>.
def downcase
- self.to_s.downcase.intern
+ (self.to_s.downcase! || self).to_sym
end
##
@@ -45,7 +36,7 @@ class Symbol
# Same as <code>sym.to_s.upcase.intern</code>.
def upcase
- self.to_s.upcase.intern
+ (self.to_s.upcase! || self).to_sym
end
##
@@ -56,7 +47,9 @@ class Symbol
def casecmp(other)
return nil unless other.kind_of?(Symbol)
- self.to_s.upcase <=> other.to_s.upcase
+ lhs = self.to_s; lhs.upcase!
+ rhs = other.to_s; rhs.upcase!
+ lhs <=> rhs
end
#
@@ -66,7 +59,7 @@ class Symbol
# Returns that _sym_ is :"" or not.
def empty?
- self.to_s.empty?
+ self.length == 0
end
end
diff --git a/mrbgems/mruby-symbol-ext/src/symbol.c b/mrbgems/mruby-symbol-ext/src/symbol.c
index 4ed5d83c6..a992dbfce 100644
--- a/mrbgems/mruby-symbol-ext/src/symbol.c
+++ b/mrbgems/mruby-symbol-ext/src/symbol.c
@@ -1,14 +1,12 @@
-#include "mruby.h"
-#include "mruby/khash.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/khash.h>
+#include <mruby/array.h>
typedef struct symbol_name {
size_t len;
const char *name;
} symbol_name;
-KHASH_DECLARE(n2s, symbol_name, mrb_sym, TRUE)
-
/*
* call-seq:
* Symbol.all_symbols => array
@@ -27,26 +25,37 @@ KHASH_DECLARE(n2s, symbol_name, mrb_sym, TRUE)
static mrb_value
mrb_sym_all_symbols(mrb_state *mrb, mrb_value self)
{
- khiter_t k;
- mrb_sym sym;
- khash_t(n2s) *h = mrb->name2sym;
- mrb_value ary = mrb_ary_new_capa(mrb, kh_size(h));
+ mrb_sym i, lim;
+ mrb_value ary = mrb_ary_new_capa(mrb, mrb->symidx);
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- sym = kh_value(h, k);
- mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
- }
+ for (i=1, lim=mrb->symidx+1; i<lim; i++) {
+ mrb_ary_push(mrb, ary, mrb_symbol_value(i));
}
return ary;
}
+/*
+ * call-seq:
+ * sym.length -> integer
+ *
+ * Same as <code>sym.to_s.length</code>.
+ */
+static mrb_value
+mrb_sym_length(mrb_state *mrb, mrb_value self)
+{
+ mrb_int len;
+ mrb_sym2name_len(mrb, mrb_symbol(self), &len);
+ return mrb_fixnum_value(len);
+}
+
void
mrb_mruby_symbol_ext_gem_init(mrb_state* mrb)
{
struct RClass *s = mrb->symbol_class;
mrb_define_class_method(mrb, s, "all_symbols", mrb_sym_all_symbols, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "length", mrb_sym_length, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "size", mrb_sym_length, MRB_ARGS_NONE());
}
void
diff --git a/mrbgems/mruby-symbol-ext/test/symbol.rb b/mrbgems/mruby-symbol-ext/test/symbol.rb
index 59df7ea9d..6070d1418 100644
--- a/mrbgems/mruby-symbol-ext/test/symbol.rb
+++ b/mrbgems/mruby-symbol-ext/test/symbol.rb
@@ -19,14 +19,17 @@ end
assert("Symbol#capitalize") do
assert_equal :Hello, :hello.capitalize
assert_equal :Hello, :HELLO.capitalize
+ assert_equal :Hello, :Hello.capitalize
end
assert("Symbol#downcase") do
assert_equal :hello, :hEllO.downcase
+ assert_equal :hello, :hello.downcase
end
assert("Symbol#upcase") do
assert_equal :HELLO, :hEllO.upcase
+ assert_equal :HELLO, :HELLO.upcase
end
assert("Symbol#casecmp") do
@@ -39,3 +42,7 @@ end
assert("Symbol#empty?") do
assert_true :''.empty?
end
+
+assert('Symbol#intern') do
+ assert_equal :test, :test.intern
+end
diff --git a/test/README.md b/mrbgems/mruby-test/README.md
index fa4b91e3a..fa4b91e3a 100644
--- a/test/README.md
+++ b/mrbgems/mruby-test/README.md
diff --git a/test/driver.c b/mrbgems/mruby-test/driver.c
index f605d7517..7cc5211f0 100644
--- a/test/driver.c
+++ b/mrbgems/mruby-test/driver.c
@@ -10,12 +10,13 @@
#include <stdlib.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/proc.h"
-#include "mruby/data.h"
-#include "mruby/compile.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/proc.h>
+#include <mruby/data.h>
+#include <mruby/compile.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/array.h>
void
mrb_init_mrbtest(mrb_state *);
@@ -41,14 +42,11 @@ check_error(mrb_state *mrb)
static int
eval_test(mrb_state *mrb)
{
- mrb_value return_value;
- const char *prog = "report()";
-
/* evaluate the test */
- return_value = mrb_load_string(mrb, prog);
+ mrb_funcall(mrb, mrb_top_self(mrb), "report", 0);
/* did an exception occur? */
if (mrb->exc) {
- mrb_p(mrb, return_value);
+ mrb_print_error(mrb);
mrb->exc = 0;
return EXIT_FAILURE;
}
@@ -82,13 +80,68 @@ mrb_t_printstr(mrb_state *mrb, mrb_value self)
return argv;
}
+void
+mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose)
+{
+ struct RClass *krn, *mrbtest;
+
+ krn = mrb->kernel_module;
+ mrb_define_method(mrb, krn, "__t_printstr__", mrb_t_printstr, MRB_ARGS_REQ(1));
+
+ mrbtest = mrb_define_module(mrb, "Mrbtest");
+
+ mrb_define_const(mrb, mrbtest, "FIXNUM_MAX", mrb_fixnum_value(MRB_INT_MAX));
+ mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN));
+ mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT));
+
+ if (verbose) {
+ mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value());
+ }
+}
+
+void
+mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src)
+{
+ mrb_value res_src;
+
+ if (mrb_src->exc) {
+ mrb_print_error(mrb_src);
+ exit(EXIT_FAILURE);
+ }
+
+#define TEST_COUNT_PASS(name) \
+ do { \
+ res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$" #name)); \
+ if (mrb_fixnum_p(res_src)) { \
+ mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name)); \
+ mrb_gv_set(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name), mrb_fixnum_value(mrb_fixnum(res_dst) + mrb_fixnum(res_src))); \
+ } \
+ } while (FALSE) \
+
+ TEST_COUNT_PASS(ok_test);
+ TEST_COUNT_PASS(ko_test);
+ TEST_COUNT_PASS(kill_test);
+
+#undef TEST_COUNT_PASS
+
+ res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$asserts"));
+
+ if (mrb_array_p(res_src)) {
+ mrb_int i;
+ mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$asserts"));
+ for (i = 0; i < RARRAY_LEN(res_src); ++i) {
+ mrb_value val_src = RARRAY_PTR(res_src)[i];
+ mrb_ary_push(mrb_dst, res_dst, mrb_str_new(mrb_dst, RSTRING_PTR(val_src), RSTRING_LEN(val_src)));
+ }
+ }
+}
+
int
main(int argc, char **argv)
{
mrb_state *mrb;
- struct RClass *krn;
- struct RClass *mrbtest;
int ret;
+ mrb_bool verbose = FALSE;
print_hint();
@@ -101,18 +154,10 @@ main(int argc, char **argv)
if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'v') {
printf("verbose mode: enable\n\n");
- mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value());
+ verbose = TRUE;
}
- krn = mrb->kernel_module;
- mrb_define_method(mrb, krn, "__t_printstr__", mrb_t_printstr, MRB_ARGS_REQ(1));
-
- mrbtest = mrb_define_module(mrb, "Mrbtest");
-
- mrb_define_const(mrb, mrbtest, "FIXNUM_MAX", mrb_fixnum_value(MRB_INT_MAX));
- mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN));
- mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT));
-
+ mrb_init_test_driver(mrb, verbose);
mrb_init_mrbtest(mrb);
ret = eval_test(mrb);
mrb_close(mrb);
diff --git a/mrbgems/mruby-test/init_mrbtest.c b/mrbgems/mruby-test/init_mrbtest.c
new file mode 100644
index 000000000..17ac1bdef
--- /dev/null
+++ b/mrbgems/mruby-test/init_mrbtest.c
@@ -0,0 +1,38 @@
+#include <stdlib.h>
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/variable.h>
+
+extern const uint8_t mrbtest_assert_irep[];
+
+void mrbgemtest_init(mrb_state* mrb);
+void mrb_init_test_driver(mrb_state* mrb, mrb_bool verbose);
+void mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src);
+
+void
+mrb_init_mrbtest(mrb_state *mrb)
+{
+ mrb_state *core_test;
+
+ mrb_load_irep(mrb, mrbtest_assert_irep);
+
+ core_test = mrb_open_core(mrb_default_allocf, NULL);
+ if (core_test == NULL) {
+ fprintf(stderr, "Invalid mrb_state, exiting %s", __FUNCTION__);
+ exit(EXIT_FAILURE);
+ }
+ mrb_init_test_driver(core_test, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));
+ mrb_load_irep(core_test, mrbtest_assert_irep);
+ mrb_t_pass_result(mrb, core_test);
+
+#ifndef DISABLE_GEMS
+ mrbgemtest_init(mrb);
+#endif
+
+ if (mrb->exc) {
+ mrb_print_error(mrb);
+ exit(EXIT_FAILURE);
+ }
+ mrb_close(core_test);
+}
+
diff --git a/mrbgems/mruby-test/mrbgem.rake b/mrbgems/mruby-test/mrbgem.rake
new file mode 100644
index 000000000..298097f62
--- /dev/null
+++ b/mrbgems/mruby-test/mrbgem.rake
@@ -0,0 +1,174 @@
+MRuby::Gem::Specification.new('mruby-test') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'mruby test'
+
+ build.bins << 'mrbtest'
+ spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
+
+ spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb")
+
+ clib = "#{build_dir}/mrbtest.c"
+ mlib = clib.ext(exts.object)
+ exec = exefile("#{build.build_dir}/bin/mrbtest")
+
+ libmruby = libfile("#{build.build_dir}/lib/libmruby")
+ libmruby_core = libfile("#{build.build_dir}/lib/libmruby_core")
+
+ mrbtest_lib = libfile("#{build_dir}/mrbtest")
+ mrbtest_objs = []
+
+ driver_obj = objfile("#{build_dir}/driver")
+ driver = "#{spec.dir}/driver.c"
+
+ assert_c = "#{build_dir}/assert.c"
+ assert_rb = "#{MRUBY_ROOT}/test/assert.rb"
+ assert_lib = assert_c.ext(exts.object)
+ mrbtest_objs << assert_lib
+
+ file assert_lib => assert_c
+ file assert_c => assert_rb do |t|
+ open(t.name, 'w') do |f|
+ mrbc.run f, assert_rb, 'mrbtest_assert_irep'
+ end
+ end
+
+ gem_table = build.gems.generate_gem_table build
+
+ build.gems.each do |g|
+ test_rbobj = g.test_rbireps.ext(exts.object)
+ g.test_objs << test_rbobj
+ dep_list = build.gems.tsort_dependencies(g.test_dependencies, gem_table).select(&:generate_functions)
+
+ file test_rbobj => g.test_rbireps
+ file g.test_rbireps => [g.test_rbfiles].flatten do |t|
+ FileUtils.mkdir_p File.dirname(t.name)
+ open(t.name, 'w') do |f|
+ g.print_gem_test_header(f)
+ test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir|
+ File.expand_path(g.test_preload, dir)
+ }.find {|file| File.exist?(file) }
+
+ f.puts %Q[/*]
+ f.puts %Q[ * This file contains a test code for #{g.name} gem.]
+ f.puts %Q[ *]
+ f.puts %Q[ * IMPORTANT:]
+ f.puts %Q[ * This file was generated!]
+ f.puts %Q[ * All manual changes will get lost.]
+ f.puts %Q[ */]
+ if test_preload.nil?
+ f.puts %Q[extern const uint8_t mrbtest_assert_irep[];]
+ else
+ g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload"
+ end
+ g.test_rbfiles.flatten.each_with_index do |rbfile, i|
+ g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}"
+ end
+ f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] unless g.test_objs.empty?
+ dep_list.each do |d|
+ f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb_state *mrb);]
+ f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_final(mrb_state *mrb);]
+ end
+ f.puts %Q[void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose);]
+ f.puts %Q[void mrb_t_pass_result(mrb_state *dst, mrb_state *src);]
+ f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb) {]
+ unless g.test_rbfiles.empty?
+ f.puts %Q[ mrb_state *mrb2;]
+ unless g.test_args.empty?
+ f.puts %Q[ mrb_value test_args_hash;]
+ end
+ f.puts %Q[ int ai;]
+ g.test_rbfiles.count.times do |i|
+ f.puts %Q[ ai = mrb_gc_arena_save(mrb);]
+ f.puts %Q[ mrb2 = mrb_open_core(mrb_default_allocf, NULL);]
+ f.puts %Q[ if (mrb2 == NULL) {]
+ f.puts %Q[ fprintf(stderr, "Invalid mrb_state, exiting \%s", __FUNCTION__);]
+ f.puts %Q[ exit(EXIT_FAILURE);]
+ f.puts %Q[ }]
+ dep_list.each do |d|
+ f.puts %Q[ GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb2);]
+ f.puts %Q[ mrb_state_atexit(mrb2, GENERATED_TMP_mrb_#{d.funcname}_gem_final);]
+ end
+ f.puts %Q[ mrb_init_test_driver(mrb2, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));]
+ if test_preload.nil?
+ f.puts %Q[ mrb_load_irep(mrb2, mrbtest_assert_irep);]
+ else
+ f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_preload);]
+ end
+ f.puts %Q[ if (mrb2->exc) {]
+ f.puts %Q[ mrb_print_error(mrb2);]
+ f.puts %Q[ exit(EXIT_FAILURE);]
+ f.puts %Q[ }]
+ f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "GEMNAME"), mrb_str_new(mrb2, "#{g.name}", #{g.name.length}));]
+
+ unless g.test_args.empty?
+ f.puts %Q[ test_args_hash = mrb_hash_new_capa(mrb, #{g.test_args.length}); ]
+ g.test_args.each do |arg_name, arg_value|
+ escaped_arg_name = arg_name.gsub('\\', '\\\\\\\\').gsub('"', '\"')
+ escaped_arg_value = arg_value.gsub('\\', '\\\\\\\\').gsub('"', '\"')
+ f.puts %Q[ mrb_hash_set(mrb2, test_args_hash, mrb_str_new(mrb2, "#{escaped_arg_name.to_s}", #{escaped_arg_name.to_s.length}), mrb_str_new(mrb2, "#{escaped_arg_value.to_s}", #{escaped_arg_value.to_s.length})); ]
+ end
+ f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "TEST_ARGS"), test_args_hash); ]
+ end
+
+ f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] if g.custom_test_init?
+
+ f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});]
+ f.puts %Q[ ]
+
+ f.puts %Q[ mrb_t_pass_result(mrb, mrb2);]
+ f.puts %Q[ mrb_close(mrb2);]
+ f.puts %Q[ mrb_gc_arena_restore(mrb, ai);]
+ end
+ end
+ f.puts %Q[}]
+ end
+ end
+ end
+
+ build.gems.each do |v|
+ mrbtest_objs.concat v.test_objs
+ end
+
+ file mrbtest_lib => mrbtest_objs do |t|
+ build.archiver.run t.name, t.prerequisites
+ end
+
+ unless build.build_mrbtest_lib_only?
+ file exec => [driver_obj, mlib, mrbtest_lib, libmruby_core, libmruby] do |t|
+ gem_flags = build.gems.map { |g| g.linker.flags }
+ gem_flags_before_libraries = build.gems.map { |g| g.linker.flags_before_libraries }
+ gem_flags_after_libraries = build.gems.map { |g| g.linker.flags_after_libraries }
+ gem_libraries = build.gems.map { |g| g.linker.libraries }
+ gem_library_paths = build.gems.map { |g| g.linker.library_paths }
+ build.linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries
+ end
+ end
+
+ init = "#{spec.dir}/init_mrbtest.c"
+ file mlib => clib
+ file clib => init do |t|
+ _pp "GEN", "*.rb", "#{clib.relative_path}"
+ FileUtils.mkdir_p File.dirname(clib)
+ open(clib, 'w') do |f|
+ f.puts %Q[/*]
+ f.puts %Q[ * This file contains a list of all]
+ f.puts %Q[ * test functions.]
+ f.puts %Q[ *]
+ f.puts %Q[ * IMPORTANT:]
+ f.puts %Q[ * This file was generated!]
+ f.puts %Q[ * All manual changes will get lost.]
+ f.puts %Q[ */]
+ f.puts %Q[]
+ f.puts IO.read(init)
+ build.gems.each do |g|
+ f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
+ end
+ f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {]
+ build.gems.each do |g|
+ f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);]
+ end
+ f.puts %Q[}]
+ end
+ end
+end
diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c
index 410b36173..b9746b79e 100644
--- a/mrbgems/mruby-time/src/time.c
+++ b/mrbgems/mruby-time/src/time.c
@@ -4,11 +4,26 @@
** See Copyright Notice in mruby.h
*/
+#include <math.h>
#include <stdio.h>
#include <time.h>
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/data.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+
+#if _MSC_VER < 1800
+double round(double x) {
+ if (x >= 0.0) {
+ return (double)((int)(x + 0.5));
+ } else {
+ return (double)((int)(x - 0.5));
+ }
+}
+#endif
+
+#if !defined(__MINGW64__) && defined(_WIN32)
+# define llround(x) round(x)
+#endif
#if defined(__MINGW64__) || defined(__MINGW32__)
# include <sys/time.h>
@@ -80,7 +95,7 @@ gettimeofday(struct timeval *tv, void *tz)
t.u64 -= UI64(116444736000000000); /* Unix epoch bias */
t.u64 /= 10; /* to microseconds */
tv->tv_sec = (time_t)(t.u64 / (1000 * 1000));
- tv->tv_usec = t.u64 % 1000 * 1000;
+ tv->tv_usec = t.u64 % (1000 * 1000);
}
return 0;
}
@@ -200,13 +215,23 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
struct mrb_time *tm;
tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
+ if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) {
+ goto out_of_range;
+ }
+ if (sizeof(time_t) == 8 && (sec > (double)INT64_MAX || (double)INT64_MIN > sec)) {
+ goto out_of_range;
+ }
tm->sec = (time_t)sec;
- tm->usec = (time_t)((sec - tm->sec) * 1.0e6 + usec);
+ if ((sec > 0 && tm->sec < 0) || (sec < 0 && (double)tm->sec > sec)) {
+ out_of_range:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
+ }
+ tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec);
while (tm->usec < 0) {
tm->sec--;
tm->usec += 1000000;
}
- while (tm->usec > 1000000) {
+ while (tm->usec >= 1000000) {
tm->sec++;
tm->usec -= 1000000;
}
@@ -228,7 +253,17 @@ current_mrb_time(mrb_state *mrb)
struct mrb_time *tm;
tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
-#ifdef NO_GETTIMEOFDAY
+#if defined(TIME_UTC)
+ {
+ struct timespec ts;
+ if (timespec_get(&ts, TIME_UTC) == 0) {
+ mrb_free(mrb, tm);
+ mrb_raise(mrb, E_RUNTIME_ERROR, "timespec_get() failed for unknown reasons");
+ }
+ tm->sec = ts.tv_sec;
+ tm->usec = ts.tv_nsec / 1000;
+ }
+#elif defined(NO_GETTIMEOFDAY)
{
static time_t last_sec = 0, last_usec = 0;
@@ -554,8 +589,7 @@ mrb_time_initialize(mrb_state *mrb, mrb_value self)
if (tm) {
mrb_free(mrb, tm);
}
- DATA_TYPE(self) = &mrb_time_type;
- DATA_PTR(self) = NULL;
+ mrb_data_init(self, NULL, &mrb_time_type);
n = mrb_get_args(mrb, "|iiiiiii",
&ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec);
@@ -565,7 +599,7 @@ mrb_time_initialize(mrb_state *mrb, mrb_value self)
else {
tm = time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL);
}
- DATA_PTR(self) = tm;
+ mrb_data_init(self, tm, &mrb_time_type);
return self;
}
@@ -582,8 +616,7 @@ mrb_time_initialize_copy(mrb_state *mrb, mrb_value copy)
mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
}
if (!DATA_PTR(copy)) {
- DATA_PTR(copy) = mrb_malloc(mrb, sizeof(struct mrb_time));
- DATA_TYPE(copy) = &mrb_time_type;
+ mrb_data_init(copy, mrb_malloc(mrb, sizeof(struct mrb_time)), &mrb_time_type);
}
*(struct mrb_time *)DATA_PTR(copy) = *(struct mrb_time *)DATA_PTR(src);
return copy;
@@ -666,6 +699,9 @@ mrb_time_to_i(mrb_state *mrb, mrb_value self)
struct mrb_time *tm;
tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) {
+ return mrb_float_value(mrb, (mrb_float)tm->sec);
+ }
return mrb_fixnum_value((mrb_int)tm->sec);
}
@@ -677,6 +713,9 @@ mrb_time_usec(mrb_state *mrb, mrb_value self)
struct mrb_time *tm;
tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time);
+ if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) {
+ return mrb_float_value(mrb, (mrb_float)tm->usec);
+ }
return mrb_fixnum_value((mrb_int)tm->usec);
}
@@ -713,7 +752,7 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
tc = mrb_define_class(mrb, "Time", mrb->object_class);
MRB_SET_INSTANCE_TT(tc, MRB_TT_DATA);
mrb_include_module(mrb, tc, mrb_module_get(mrb, "Comparable"));
- mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1)); /* 15.2.19.6.1 */
+ mrb_define_class_method(mrb, tc, "at", mrb_time_at, MRB_ARGS_ARG(1, 1)); /* 15.2.19.6.1 */
mrb_define_class_method(mrb, tc, "gm", mrb_time_gm, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.2 */
mrb_define_class_method(mrb, tc, "local", mrb_time_local, MRB_ARGS_ARG(1,6)); /* 15.2.19.6.3 */
mrb_define_class_method(mrb, tc, "mktime", mrb_time_local, MRB_ARGS_ARG(1,6));/* 15.2.19.6.4 */
diff --git a/mrbgems/mruby-time/test/time.rb b/mrbgems/mruby-time/test/time.rb
index ba9b48fab..759e2881d 100644
--- a/mrbgems/mruby-time/test/time.rb
+++ b/mrbgems/mruby-time/test/time.rb
@@ -203,3 +203,11 @@ assert('day of week methods') do
assert_false t.friday?
assert_false t.saturday?
end
+
+assert('2000 times 500us make a second') do
+ t = Time.utc 2015
+ 2000.times do
+ t += 0.0005
+ end
+ t.usec == 0
+end
diff --git a/mrbgems/mruby-toplevel-ext/mrbgem.rake b/mrbgems/mruby-toplevel-ext/mrbgem.rake
index eb6951f6c..ce77e0bcf 100644
--- a/mrbgems/mruby-toplevel-ext/mrbgem.rake
+++ b/mrbgems/mruby-toplevel-ext/mrbgem.rake
@@ -1,5 +1,5 @@
MRuby::Gem::Specification.new('mruby-toplevel-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
- spec.summary = 'extended toplevel object (main) methods'
+ spec.summary = 'toplevel object (main) methods extension'
end
diff --git a/mrblib/array.rb b/mrblib/array.rb
index 3218aa858..d5a2d8405 100644
--- a/mrblib/array.rb
+++ b/mrblib/array.rb
@@ -12,15 +12,10 @@ class Array
def each(&block)
return to_enum :each unless block_given?
- idx, length = -1, self.length-1
- while idx < length and length <= self.length and length = self.length-1
- elm = self[idx += 1]
- unless elm
- if elm == nil and length >= self.length
- break
- end
- end
- block.call(elm)
+ idx = 0
+ while idx < length
+ block.call(self[idx])
+ idx += 1
end
self
end
@@ -34,7 +29,7 @@ class Array
return to_enum :each_index unless block_given?
idx = 0
- while(idx < length)
+ while idx < length
block.call(idx)
idx += 1
end
@@ -50,9 +45,7 @@ class Array
def collect!(&block)
return to_enum :collect! unless block_given?
- self.each_index{|idx|
- self[idx] = block.call(self[idx])
- }
+ self.each_index { |idx| self[idx] = block.call(self[idx]) }
self
end
@@ -72,10 +65,10 @@ class Array
self.clear
if size > 0
- self[size - 1] = nil # allocate
+ self[size - 1] = nil # allocate
idx = 0
- while(idx < size)
+ while idx < size
self[idx] = (block)? block.call(idx): obj
idx += 1
end
@@ -84,13 +77,20 @@ class Array
self
end
+ def _inspect
+ return "[]" if self.size == 0
+ "["+self.map{|x|x.inspect}.join(", ")+"]"
+ end
##
- # Private method for Array creation.
+ # Return the contents of this array as a string.
#
# ISO 15.2.12.5.31 (x)
def inspect
- return "[]" if self.size == 0
- "["+self.map{|x|x.inspect}.join(", ")+"]"
+ begin
+ self._inspect
+ rescue SystemStackError
+ "[...]"
+ end
end
# ISO 15.2.12.5.32 (x)
alias to_s inspect
@@ -139,7 +139,7 @@ class Array
# equal, then that inequality is the return value. If all the
# values found are equal, then the return is based on a
# comparison of the array lengths. Thus, two arrays are
- # ``equal'' according to <code>Array#<=></code> if and only if they have
+ # "equal" according to <code>Array#<=></code> if and only if they have
# the same length and the value of each element is equal to the
# value of the corresponding element in the other array.
#
@@ -151,14 +151,11 @@ class Array
len = self.size
n = other.size
- if len > n
- len = n
- end
+ len = n if len > n
i = 0
while i < len
n = (self[i] <=> other[i])
- return n if n == nil
- return n if n != 0
+ return n if n.nil? || n != 0
i += 1
end
len = self.size - other.size
@@ -178,20 +175,14 @@ class Array
self.delete_at(i)
ret = key
end
- if ret == nil && block
- block.call
- else
- ret
- end
+ return block.call if ret.nil? && block
+ ret
end
# internal method to convert multi-value to single value
def __svalue
- if self.size < 2
- self.first
- else
- self
- end
+ return self.first if self.size < 2
+ self
end
end
diff --git a/mrblib/class.rb b/mrblib/class.rb
index ebfb1f76d..39e0d5091 100644
--- a/mrblib/class.rb
+++ b/mrblib/class.rb
@@ -1,26 +1,5 @@
class Module
- # 15.2.2.4.13
- def attr_reader(*names)
- names.each do |name|
- name = name.to_s
- raise(NameError, "#{name.inspect} is not allowed as an instance variable name") if name.include?('@') || name.include?('?') || name.include?('$')
-
- attr_name = '@'+name
- define_method(name){self.instance_variable_get(attr_name)}
- end
- end
- # 15.2.2.4.14
- def attr_writer(*names)
- names.each do |name|
- name = name.to_s
- raise(NameError, "#{name.inspect} is not allowed as an instance variable name") if name.include?('@') || name.include?('?') || name.include?('$')
-
- attr_name = '@'+name
- name = (name+"=").intern
- define_method(name){|v|self.instance_variable_set(attr_name,v)}
- end
- end
- # 15.2.2.4.12
+ # 15.2.2.4.12
def attr_accessor(*names)
attr_reader(*names)
attr_writer(*names)
diff --git a/mrblib/enum.rb b/mrblib/enum.rb
index 41fd97fe7..650d24302 100644
--- a/mrblib/enum.rb
+++ b/mrblib/enum.rb
@@ -1,17 +1,16 @@
##
# Enumerable
#
-# ISO 15.3.2
+# The <code>Enumerable</code> mixin provides collection classes with
+# several traversal and searching methods, and with the ability to
+# sort. The class must provide a method `each`, which
+# yields successive members of the collection. If
+# {Enumerable#max}, {#min}, or
+# {#sort} is used, the objects in the collection must also
+# implement a meaningful `<=>` operator, as these methods
+# rely on an ordering between members of the collection.
#
-# The <code>Enumerable</code> mixin provides collection classes with
-# several traversal and searching methods, and with the ability to
-# sort. The class must provide a method <code>each</code>, which
-# yields successive members of the collection. If
-# <code>Enumerable#max</code>, <code>#min</code>, or
-# <code>#sort</code> is used, the objects in the collection must also
-# implement a meaningful <code><=></code> operator, as these methods
-# rely on an ordering between members of the collection.
-
+# @ISO 15.3.2
module Enumerable
##
@@ -24,17 +23,9 @@ module Enumerable
# ISO 15.3.2.2.1
def all?(&block)
if block
- self.each{|*val|
- unless block.call(*val)
- return false
- end
- }
+ self.each{|*val| return false unless block.call(*val)}
else
- self.each{|*val|
- unless val.__svalue
- return false
- end
- }
+ self.each{|*val| return false unless val.__svalue}
end
true
end
@@ -49,17 +40,9 @@ module Enumerable
# ISO 15.3.2.2.2
def any?(&block)
if block
- self.each{|*val|
- if block.call(*val)
- return true
- end
- }
+ self.each{|*val| return true if block.call(*val)}
else
- self.each{|*val|
- if val.__svalue
- return true
- end
- }
+ self.each{|*val| return true if val.__svalue}
end
false
end
@@ -72,12 +55,10 @@ module Enumerable
#
# ISO 15.3.2.2.3
def collect(&block)
- return to_enum :collect unless block_given?
+ return to_enum :collect unless block
ary = []
- self.each{|*val|
- ary.push(block.call(*val))
- }
+ self.each{|*val| ary.push(block.call(*val))}
ary
end
@@ -108,7 +89,7 @@ module Enumerable
#
# ISO 15.3.2.2.5
def each_with_index(&block)
- return to_enum :each_with_index unless block_given?
+ return to_enum :each_with_index unless block
i = 0
self.each{|*val|
@@ -146,7 +127,7 @@ module Enumerable
#
# ISO 15.3.2.2.8
def find_all(&block)
- return to_enum :find_all unless block_given?
+ return to_enum :find_all unless block
ary = []
self.each{|*val|
@@ -183,9 +164,7 @@ module Enumerable
# ISO 15.3.2.2.10
def include?(obj)
self.each{|*val|
- if val.__svalue == obj
- return true
- end
+ return true if val.__svalue == obj
}
false
end
@@ -402,8 +381,11 @@ module Enumerable
# redefine #hash 15.3.1.3.15
def hash
h = 12347
+ i = 0
self.each do |e|
- h ^= e.hash
+ n = e.hash << (i % 16)
+ h ^= n
+ i += 1
end
h
end
diff --git a/mrblib/error.rb b/mrblib/error.rb
index a5b6b3223..2674af7a2 100644
--- a/mrblib/error.rb
+++ b/mrblib/error.rb
@@ -1,18 +1,3 @@
-##
-# Exception
-#
-# ISO 15.2.22
-class Exception
-
- ##
- # Raise an exception.
- #
- # ISO 15.2.22.4.1
- def self.exception(*args, &block)
- self.new(*args, &block)
- end
-end
-
# ISO 15.2.24
class ArgumentError < StandardError
end
@@ -66,3 +51,6 @@ end
class NotImplementedError < ScriptError
end
+class StopIteration < IndexError
+ attr_accessor :result
+end
diff --git a/mrblib/hash.rb b/mrblib/hash.rb
index 9bb146b27..e3e709070 100644
--- a/mrblib/hash.rb
+++ b/mrblib/hash.rb
@@ -10,7 +10,7 @@ class Hash
# hash.
#
# ISO 15.2.13.4.1
- def == (hash)
+ def ==(hash)
return true if self.equal?(hash)
begin
hash = hash.to_hash
@@ -54,7 +54,7 @@ class Hash
#
# ISO 15.2.13.4.8
def delete(key, &block)
- if block && ! self.has_key?(key)
+ if block && !self.has_key?(key)
block.call(key)
else
self.__delete(key)
@@ -74,8 +74,8 @@ class Hash
#
# If no block is given, an enumerator is returned instead.
#
- # h = { "a" => 100, "b" => 200 }
- # h.each {|key, value| puts "#{key} is #{value}" }
+ # h = { "a" => 100, "b" => 200 }
+ # h.each {|key, value| puts "#{key} is #{value}" }
#
# <em>produces:</em>
#
@@ -192,16 +192,24 @@ class Hash
h
end
- ##
- # Return the contents of this hash as a string.
- #
- # ISO 15.2.13.4.30 (x)
- def inspect
+ # internal method for Hash inspection
+ def _inspect
return "{}" if self.size == 0
"{"+self.map {|k,v|
- k.inspect + "=>" + v.inspect
+ k._inspect + "=>" + v._inspect
}.join(", ")+"}"
end
+ ##
+ # Return the contents of this hash as a string.
+ #
+ # ISO 15.2.13.4.30 (x)
+ def inspect
+ begin
+ self._inspect
+ rescue SystemStackError
+ "{...}"
+ end
+ end
# ISO 15.2.13.4.31 (x)
alias to_s inspect
@@ -311,6 +319,29 @@ class Hash
h
end
+ ##
+ # call-seq:
+ # hsh.rehash -> hsh
+ #
+ # Rebuilds the hash based on the current hash values for each key. If
+ # values of key objects have changed since they were inserted, this
+ # method will reindex <i>hsh</i>.
+ #
+ # h = {"AAA" => "b"}
+ # h.keys[0].chop!
+ # h #=> {"AA"=>"b"}
+ # h["AA"] #=> nil
+ # h.rehash #=> {"AA"=>"b"}
+ # h["AA"] #=> "b"
+ #
+ def rehash
+ h = {}
+ self.each{|k,v|
+ h[k] = v
+ }
+ self.replace(h)
+ end
+
def __update(h)
h.each_key{|k| self[k] = h[k]}
self
diff --git a/mrblib/init_mrblib.c b/mrblib/init_mrblib.c
index dc902e9f9..4d4bcd25a 100644
--- a/mrblib/init_mrblib.c
+++ b/mrblib/init_mrblib.c
@@ -1,5 +1,5 @@
-#include "mruby.h"
-#include "mruby/irep.h"
+#include <mruby.h>
+#include <mruby/irep.h>
extern const uint8_t mrblib_irep[];
diff --git a/mrblib/kernel.rb b/mrblib/kernel.rb
index d0fe47300..550ae8172 100644
--- a/mrblib/kernel.rb
+++ b/mrblib/kernel.rb
@@ -24,14 +24,14 @@ module Kernel
# Calls the given block repetitively.
#
# ISO 15.3.1.3.29
- def loop
- return to_enum :loop unless block_given?
+ def loop(&block)
+ return to_enum :loop unless block
- while(true)
+ while true
yield
end
- rescue StopIteration
- nil
+ rescue StopIteration => e
+ e.result
end
# 11.4.4 Step c)
@@ -39,6 +39,11 @@ module Kernel
!(self =~ y)
end
+ # internal method for inspect
+ def _inspect
+ self.inspect
+ end
+
def to_enum(*a)
raise NotImplementedError.new("fiber required for enumerator")
end
diff --git a/mrblib/mrblib.rake b/mrblib/mrblib.rake
index d156a2683..19fd00d66 100644
--- a/mrblib/mrblib.rake
+++ b/mrblib/mrblib.rake
@@ -1,5 +1,5 @@
MRuby.each_target do
- current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd)
+ current_dir = File.dirname(__FILE__)
relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT)
current_build_dir = "#{build_dir}/#{relative_from_root}"
diff --git a/mrblib/numeric.rb b/mrblib/numeric.rb
index 5be3c90fc..6e4c5027f 100644
--- a/mrblib/numeric.rb
+++ b/mrblib/numeric.rb
@@ -48,7 +48,7 @@ module Integral
return to_enum(:downto, num) unless block_given?
i = self.to_i
- while(i >= num)
+ while i >= num
block.call(i)
i -= 1
end
@@ -89,7 +89,7 @@ module Integral
return to_enum(:upto, num) unless block_given?
i = self.to_i
- while(i <= num)
+ while i <= num
block.call(i)
i += 1
end
@@ -100,13 +100,21 @@ module Integral
# Calls the given block from +self+ to +num+
# incremented by +step+ (default 1).
#
- def step(num, step=1, &block)
+ def step(num, step = 1, &block)
+ raise ArgumentError, "step can't be 0" if step == 0
return to_enum(:step, num, step) unless block_given?
i = if num.kind_of? Float then self.to_f else self end
- while(i <= num)
- block.call(i)
- i += step
+ if step > 0
+ while i <= num
+ block.call(i)
+ i += step
+ end
+ else
+ while i >= num
+ block.call(i)
+ i += step
+ end
end
self
end
@@ -157,16 +165,30 @@ class Float
# floats should be compatible to integers.
def >> other
n = self.to_i
- other.to_i.times {
- n /= 2
- }
- n
+ other = other.to_i
+ if other < 0
+ n << -other
+ else
+ other.times { n /= 2 }
+ if n.abs < 1
+ if n >= 0
+ 0
+ else
+ -1
+ end
+ else
+ n.to_i
+ end
+ end
end
def << other
n = self.to_i
- other.to_i.times {
- n *= 2
- }
- n.to_i
+ other = other.to_i
+ if other < 0
+ n >> -other
+ else
+ other.times { n *= 2 }
+ n
+ end
end
end
diff --git a/mrblib/range.rb b/mrblib/range.rb
index 1ec9ac508..5e5fd9bdc 100644
--- a/mrblib/range.rb
+++ b/mrblib/range.rb
@@ -26,29 +26,23 @@ class Range
return self
end
- unless val.respond_to? :succ
- raise TypeError, "can't iterate"
- end
+ raise TypeError, "can't iterate" unless val.respond_to? :succ
return self if (val <=> last) > 0
- while((val <=> last) < 0)
+ while (val <=> last) < 0
block.call(val)
val = val.succ
end
- if not exclude_end? and (val <=> last) == 0
- block.call(val)
- end
+ block.call(val) if !exclude_end? && (val <=> last) == 0
self
end
# redefine #hash 15.3.1.3.15
def hash
h = first.hash ^ last.hash
- if self.exclude_end?
- h += 1
- end
+ h += 1 if self.exclude_end?
h
end
end
diff --git a/mrblib/string.rb b/mrblib/string.rb
index 322cd0788..37441ec98 100644
--- a/mrblib/string.rb
+++ b/mrblib/string.rb
@@ -10,9 +10,8 @@ class String
#
# ISO 15.2.10.5.15
def each_line(&block)
- # expect that str.index accepts an Integer for 1st argument as a byte data
offset = 0
- while(pos = self.index(0x0a, offset))
+ while pos = self.index("\n", offset)
block.call(self[offset, pos + 1 - offset])
offset = pos + 1
end
@@ -20,6 +19,30 @@ class String
self
end
+ # private method for gsub/sub
+ def __sub_replace(pre, m, post)
+ s = ""
+ i = 0
+ while j = index("\\", i)
+ break if j == length-1
+ t = case self[j+1]
+ when "\\"
+ "\\"
+ when "`"
+ pre
+ when "&", "0"
+ m
+ when "'"
+ post
+ else
+ self[j, 2]
+ end
+ s += self[i, j-i] + t
+ i = j + 2
+ end
+ s + self[i, length-i]
+ end
+
##
# Replace all matches of +pattern+ with +replacement+.
# Call block (if given) for each match and replace
@@ -29,7 +52,17 @@ class String
# ISO 15.2.10.5.18
def gsub(*args, &block)
if args.size == 2
- split(args[0], -1).join(args[1])
+ s = ""
+ i = 0
+ while j = index(args[0], i)
+ seplen = args[0].length
+ k = j + seplen
+ pre = self[0, j]
+ post = self[k, length-k]
+ s += self[i, j-i] + args[1].__sub_replace(pre, args[0], post)
+ i = k
+ end
+ s + self[i, length-i]
elsif args.size == 1 && block
split(args[0], -1).join(block.call(args[0]))
else
@@ -46,12 +79,8 @@ class String
# ISO 15.2.10.5.19
def gsub!(*args, &block)
str = self.gsub(*args, &block)
- if str != self
- self.replace(str)
- self
- else
- nil
- end
+ return nil if str == self
+ self.replace(str)
end
##
@@ -76,7 +105,9 @@ class String
# ISO 15.2.10.5.36
def sub(*args, &block)
if args.size == 2
- split(args[0], 2).join(args[1])
+ pre, post = split(args[0], 2)
+ return self unless post # The sub target wasn't found in the string
+ pre + args[1].__sub_replace(pre, args[0], post) + post
elsif args.size == 1 && block
split(args[0], 2).join(block.call(args[0]))
else
@@ -93,12 +124,8 @@ class String
# ISO 15.2.10.5.37
def sub!(*args, &block)
str = self.sub(*args, &block)
- if str != self
- self.replace(str)
- self
- else
- nil
- end
+ return nil if str == self
+ self.replace(str)
end
##
@@ -106,7 +133,7 @@ class String
# +self+.
def each_char(&block)
pos = 0
- while(pos < self.size)
+ while pos < self.size
block.call(self[pos])
pos += 1
end
@@ -118,7 +145,7 @@ class String
def each_byte(&block)
bytes = self.bytes
pos = 0
- while(pos < bytes.size)
+ while pos < bytes.size
block.call(bytes[pos])
pos += 1
end
@@ -126,27 +153,70 @@ class String
end
##
- # Modify +self+ by replacing the content of +self+
- # at the position +pos+ with +value+.
- def []=(pos, value)
- b = self[0, pos]
- a = self[pos+1..-1]
- self.replace([b, value, a].join(''))
+ # Modify +self+ by replacing the content of +self+.
+ # The portion of the string affected is determined using the same criteria as +String#[]+.
+ def []=(*args)
+ anum = args.size
+ if anum == 2
+ pos, value = args
+ if pos.kind_of? String
+ posnum = self.index(pos)
+ if posnum
+ b = self[0, posnum.to_i]
+ a = self[(posnum + pos.length)..-1]
+ self.replace([b, value, a].join(''))
+ return value
+ else
+ raise IndexError, "string not matched"
+ end
+ else
+ pos += self.length if pos < 0
+ if pos < 0 || pos > self.length
+ raise IndexError, "index #{args[0]} out of string"
+ end
+ b = self[0, pos.to_i]
+ a = self[pos + 1..-1]
+ self.replace([b, value, a].join(''))
+ return value
+ end
+ elsif anum == 3
+ pos, len, value = args
+ pos += self.length if pos < 0
+ if pos < 0 || pos > self.length
+ raise IndexError, "index #{args[0]} out of string"
+ end
+ if len < 0
+ raise IndexError, "negative length #{len}"
+ end
+ b = self[0, pos.to_i]
+ a = self[pos + len..-1]
+ self.replace([b, value, a].join(''))
+ return value
+ else
+ raise ArgumentError, "wrong number of arguments (#{anum} for 2..3)"
+ end
end
##
# ISO 15.2.10.5.3
def =~(re)
- if re.respond_to? :to_str
- raise TypeError, "type mismatch: String given"
- end
+ raise TypeError, "type mismatch: String given" if re.respond_to? :to_str
re =~ self
end
##
# ISO 15.2.10.5.27
def match(re, &block)
- re.match(self, &block)
+ if re.respond_to? :to_str
+ if Object.const_defined?(:Regexp)
+ r = Regexp.new(re)
+ r.match(self, &block)
+ else
+ raise NotImplementedError, "String#match needs Regexp class"
+ end
+ else
+ re.match(self, &block)
+ end
end
end
diff --git a/mruby-source.gemspec b/mruby-source.gemspec
new file mode 100644
index 000000000..62d4c0d12
--- /dev/null
+++ b/mruby-source.gemspec
@@ -0,0 +1,18 @@
+# coding: utf-8
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require 'mruby/source'
+
+Gem::Specification.new do |spec|
+ spec.name = "mruby-source"
+ spec.version = MRuby::Source::MRUBY_VERSION
+ spec.authors = [ MRuby::Source::MRUBY_AUTHOR ]
+
+ spec.summary = %q{MRuby source code wrapper.}
+ spec.description = %q{MRuby source code wrapper for use with Ruby libs.}
+ spec.homepage = "http://www.mruby.org/"
+ spec.license = "MIT"
+
+ spec.files = `git ls-files -z`.split("\x0")
+ spec.require_paths = ["lib"]
+end
diff --git a/src/array.c b/src/array.c
index d008e52cf..9b8a49584 100644
--- a/src/array.c
+++ b/src/array.c
@@ -4,25 +4,21 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/string.h"
-#include "mruby/range.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/range.h>
#include "value_array.h"
#define ARY_DEFAULT_LEN 4
#define ARY_SHRINK_RATIO 5 /* must be larger than 2 */
#define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value))
#define ARY_MAX_SIZE ((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? (mrb_int)ARY_C_MAX_SIZE : MRB_INT_MAX-1)
-#define ARY_SHARED_P(a) ((a)->flags & MRB_ARY_SHARED)
-#define ARY_SET_SHARED_FLAG(a) ((a)->flags |= MRB_ARY_SHARED)
-#define ARY_UNSET_SHARED_FLAG(a) ((a)->flags &= ~MRB_ARY_SHARED)
static inline mrb_value
ary_elt(mrb_value ary, mrb_int offset)
{
- if (RARRAY_LEN(ary) == 0) return mrb_nil_value();
if (offset < 0 || RARRAY_LEN(ary) <= offset) {
return mrb_nil_value();
}
@@ -51,14 +47,14 @@ ary_new_capa(mrb_state *mrb, mrb_int capa)
return a;
}
-mrb_value
+MRB_API mrb_value
mrb_ary_new_capa(mrb_state *mrb, mrb_int capa)
{
struct RArray *a = ary_new_capa(mrb, capa);
return mrb_obj_value(a);
}
-mrb_value
+MRB_API mrb_value
mrb_ary_new(mrb_state *mrb)
{
return mrb_ary_new_capa(mrb, 0);
@@ -88,21 +84,18 @@ array_copy(mrb_value *dst, const mrb_value *src, mrb_int size)
}
}
-mrb_value
+MRB_API mrb_value
mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals)
{
- mrb_value ary;
- struct RArray *a;
+ struct RArray *a = ary_new_capa(mrb, size);
- ary = mrb_ary_new_capa(mrb, size);
- a = mrb_ary_ptr(ary);
array_copy(a->ptr, vals, size);
a->len = size;
- return ary;
+ return mrb_obj_value(a);
}
-mrb_value
+MRB_API mrb_value
mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr)
{
struct RArray *a;
@@ -153,7 +146,7 @@ ary_modify(mrb_state *mrb, struct RArray *a)
}
}
-void
+MRB_API void
mrb_ary_modify(mrb_state *mrb, struct RArray* a)
{
mrb_write_barrier(mrb, (struct RBasic*)a);
@@ -200,10 +193,6 @@ ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len)
if (capa > a->aux.capa) {
mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa);
- if (!expanded_ptr) {
- mrb_raise(mrb, E_RUNTIME_ERROR, "out of memory");
- }
-
a->aux.capa = capa;
a->ptr = expanded_ptr;
}
@@ -231,6 +220,28 @@ ary_shrink_capa(mrb_state *mrb, struct RArray *a)
}
}
+MRB_API mrb_value
+mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len)
+{
+ mrb_int old_len;
+ struct RArray *a = mrb_ary_ptr(ary);
+
+ ary_modify(mrb, a);
+ old_len = RARRAY_LEN(ary);
+ if (old_len != new_len) {
+ a->len = new_len;
+ if (new_len < old_len) {
+ ary_shrink_capa(mrb, a);
+ }
+ else {
+ ary_expand_capa(mrb, a, new_len);
+ ary_fill_with_nil(a->ptr + old_len, new_len - old_len);
+ }
+ }
+
+ return ary;
+}
+
static mrb_value
mrb_ary_s_create(mrb_state *mrb, mrb_value self)
{
@@ -254,7 +265,7 @@ ary_concat(mrb_state *mrb, struct RArray *a, mrb_value *ptr, mrb_int blen)
a->len = len;
}
-void
+MRB_API void
mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other)
{
struct RArray *a2 = mrb_ary_ptr(other);
@@ -278,18 +289,19 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self)
{
struct RArray *a1 = mrb_ary_ptr(self);
struct RArray *a2;
- mrb_value ary;
mrb_value *ptr;
mrb_int blen;
mrb_get_args(mrb, "a", &ptr, &blen);
- ary = mrb_ary_new_capa(mrb, a1->len + blen);
- a2 = mrb_ary_ptr(ary);
+ if (ARY_MAX_SIZE - blen < a1->len) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
+ }
+ a2 = ary_new_capa(mrb, a1->len + blen);
array_copy(a2->ptr, a1->ptr, a1->len);
array_copy(a2->ptr + a1->len, ptr, blen);
a2->len = a1->len + blen;
- return ary;
+ return mrb_obj_value(a2);
}
static void
@@ -303,7 +315,7 @@ ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len)
a->len = len;
}
-void
+MRB_API void
mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other)
{
struct RArray *a2 = mrb_ary_ptr(other);
@@ -327,7 +339,6 @@ mrb_ary_times(mrb_state *mrb, mrb_value self)
{
struct RArray *a1 = mrb_ary_ptr(self);
struct RArray *a2;
- mrb_value ary;
mrb_value *ptr;
mrb_int times;
@@ -336,9 +347,10 @@ mrb_ary_times(mrb_state *mrb, mrb_value self)
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument");
}
if (times == 0) return mrb_ary_new(mrb);
-
- ary = mrb_ary_new_capa(mrb, a1->len * times);
- a2 = mrb_ary_ptr(ary);
+ if (ARY_MAX_SIZE / times < a1->len) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big");
+ }
+ a2 = ary_new_capa(mrb, a1->len * times);
ptr = a2->ptr;
while (times--) {
array_copy(ptr, a1->ptr, a1->len);
@@ -346,7 +358,7 @@ mrb_ary_times(mrb_state *mrb, mrb_value self)
a2->len += a1->len;
}
- return ary;
+ return mrb_obj_value(a2);
}
static mrb_value
@@ -373,11 +385,8 @@ mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_ary_reverse(mrb_state *mrb, mrb_value self)
{
- struct RArray *a = mrb_ary_ptr(self), *b;
- mrb_value ary;
+ struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, a->len);
- ary = mrb_ary_new_capa(mrb, a->len);
- b = mrb_ary_ptr(ary);
if (a->len > 0) {
mrb_value *p1, *p2, *e;
@@ -389,10 +398,10 @@ mrb_ary_reverse(mrb_state *mrb, mrb_value self)
}
b->len = a->len;
}
- return ary;
+ return mrb_obj_value(b);
}
-void
+MRB_API void
mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem)
{
struct RArray *a = mrb_ary_ptr(ary);
@@ -418,7 +427,7 @@ mrb_ary_push_m(mrb_state *mrb, mrb_value self)
return self;
}
-mrb_value
+MRB_API mrb_value
mrb_ary_pop(mrb_state *mrb, mrb_value ary)
{
struct RArray *a = mrb_ary_ptr(ary);
@@ -429,7 +438,7 @@ mrb_ary_pop(mrb_state *mrb, mrb_value ary)
#define ARY_SHIFT_SHARED_MIN 10
-mrb_value
+MRB_API mrb_value
mrb_ary_shift(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
@@ -465,7 +474,7 @@ mrb_ary_shift(mrb_state *mrb, mrb_value self)
item = 0
self.unshift item
p self #=> [0, 1, 2, 3] */
-mrb_value
+MRB_API mrb_value
mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item)
{
struct RArray *a = mrb_ary_ptr(self);
@@ -518,7 +527,7 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self)
return self;
}
-mrb_value
+MRB_API mrb_value
mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n)
{
struct RArray *a = mrb_ary_ptr(ary);
@@ -530,7 +539,7 @@ mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n)
return a->ptr[n];
}
-void
+MRB_API void
mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val)
{
struct RArray *a = mrb_ary_ptr(ary);
@@ -554,12 +563,12 @@ mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val)
mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val);
}
-mrb_value
+MRB_API mrb_value
mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl)
{
struct RArray *a = mrb_ary_ptr(ary);
mrb_int tail, size;
- mrb_value *argv;
+ const mrb_value *argv;
mrb_int i, argc;
ary_modify(mrb, a);
@@ -643,10 +652,14 @@ aget_index(mrb_state *mrb, mrb_value index)
if (mrb_fixnum_p(index)) {
return mrb_fixnum(index);
}
+ else if (mrb_float_p(index)) {
+ return (mrb_int)mrb_float(index);
+ }
else {
- mrb_int i;
+ mrb_int i, argc;
+ mrb_value *argv;
- mrb_get_args(mrb, "i", &i);
+ mrb_get_args(mrb, "i*", &i, &argv, &argc);
return i;
}
}
@@ -875,7 +888,7 @@ mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
return mrb_nil_value();
}
-mrb_value
+MRB_API mrb_value
mrb_ary_splat(mrb_state *mrb, mrb_value v)
{
if (mrb_array_p(v)) {
@@ -897,15 +910,20 @@ mrb_ary_size(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(a->len);
}
-mrb_value
+MRB_API mrb_value
mrb_ary_clear(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- ary_modify(mrb, a);
+ if (ARY_SHARED_P(a)) {
+ mrb_ary_decref(mrb, a->aux.shared);
+ ARY_UNSET_SHARED_FLAG(a);
+ }
+ else {
+ mrb_free(mrb, a->ptr);
+ }
a->len = 0;
a->aux.capa = 0;
- mrb_free(mrb, a->ptr);
a->ptr = 0;
return self;
@@ -919,13 +937,13 @@ mrb_ary_empty_p(mrb_state *mrb, mrb_value self)
return mrb_bool_value(a->len == 0);
}
-mrb_value
+MRB_API mrb_value
mrb_check_array_type(mrb_state *mrb, mrb_value ary)
{
return mrb_check_convert_type(mrb, ary, MRB_TT_ARRAY, "Array", "to_ary");
}
-mrb_value
+MRB_API mrb_value
mrb_ary_entry(mrb_value ary, mrb_int offset)
{
if (offset < 0) {
@@ -989,7 +1007,7 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
return result;
}
-mrb_value
+MRB_API mrb_value
mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep)
{
sep = mrb_obj_as_string(mrb, sep);
@@ -1012,7 +1030,7 @@ mrb_ary_join_m(mrb_state *mrb, mrb_value ary)
{
mrb_value sep = mrb_nil_value();
- mrb_get_args(mrb, "|S", &sep);
+ mrb_get_args(mrb, "|S!", &sep);
return mrb_ary_join(mrb, ary, sep);
}
@@ -1023,7 +1041,6 @@ mrb_ary_eq(mrb_state *mrb, mrb_value ary1)
mrb_get_args(mrb, "o", &ary2);
if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value();
- if (mrb_special_const_p(ary2)) return mrb_false_value();
if (!mrb_array_p(ary2)) {
return mrb_false_value();
}
@@ -1039,7 +1056,6 @@ mrb_ary_cmp(mrb_state *mrb, mrb_value ary1)
mrb_get_args(mrb, "o", &ary2);
if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0);
- if (mrb_special_const_p(ary2)) return mrb_nil_value();
if (!mrb_array_p(ary2)) {
return mrb_nil_value();
}
@@ -1052,7 +1068,7 @@ mrb_init_array(mrb_state *mrb)
{
struct RClass *a;
- a = mrb->array_class = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */
+ mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */
MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY);
mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */
@@ -1084,5 +1100,6 @@ mrb_init_array(mrb_state *mrb)
mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */
mrb_define_method(mrb, a, "__ary_eq", mrb_ary_eq, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, a, "__ary_cmp", mrb_ary_cmp, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, a, "__ary_cmp", mrb_ary_cmp, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, a, "__ary_index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */
}
diff --git a/src/backtrace.c b/src/backtrace.c
index 1e1f9fa1a..80a5e2935 100644
--- a/src/backtrace.c
+++ b/src/backtrace.c
@@ -4,137 +4,158 @@
** See Copyright Notice in mruby.h
*/
-#include <stdarg.h>
-#include "mruby.h"
-#include "mruby/variable.h"
-#include "mruby/proc.h"
-#include "mruby/array.h"
-#include "mruby/string.h"
-#include "mruby/class.h"
-#include "mruby/debug.h"
-#include "mruby/error.h"
-
-#ifdef ENABLE_STDIO
-
-typedef void (*output_stream_func)(mrb_state*, void*, int, const char*, ...);
+#include <mruby.h>
+#include <mruby/variable.h>
+#include <mruby/proc.h>
+#include <mruby/array.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
+#include <mruby/debug.h>
+#include <mruby/error.h>
+#include <mruby/numeric.h>
+
+struct backtrace_location {
+ int i;
+ int lineno;
+ const char *filename;
+ const char *method;
+ const char *sep;
+ const char *class_name;
+};
+
+typedef void (*output_stream_func)(mrb_state*, struct backtrace_location*, void*);
+
+#ifndef MRB_DISABLE_STDIO
+
+struct print_backtrace_args {
+ FILE *stream;
+ int tracehead;
+};
static void
-print_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...)
+print_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data)
{
- va_list ap;
+ struct print_backtrace_args *args;
- va_start(ap, format);
- vfprintf((FILE*)stream, format, ap);
- va_end(ap);
-}
+ args = (struct print_backtrace_args*)data;
+
+ if (args->tracehead) {
+ fprintf(args->stream, "trace:\n");
+ args->tracehead = FALSE;
+ }
+
+ fprintf(args->stream, "\t[%d] %s:%d", loc->i, loc->filename, loc->lineno);
+
+ if (loc->method) {
+ if (loc->class_name) {
+ fprintf(args->stream, ":in %s%s%s", loc->class_name, loc->sep, loc->method);
+ }
+ else {
+ fprintf(args->stream, ":in %s", loc->method);
+ }
+ }
+ fprintf(args->stream, "\n");
+}
-#define MIN_BUFSIZE 127
+#endif
static void
-get_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...)
+get_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data)
{
- va_list ap;
mrb_value ary, str;
int ai;
- if (level > 0) {
- return;
- }
-
ai = mrb_gc_arena_save(mrb);
- ary = mrb_obj_value((struct RArray*)stream);
+ ary = mrb_obj_value((struct RArray*)data);
- va_start(ap, format);
- str = mrb_str_new(mrb, 0, vsnprintf(NULL, 0, format, ap) + 1);
- va_end(ap);
+ str = mrb_str_new_cstr(mrb, loc->filename);
+ mrb_str_cat_lit(mrb, str, ":");
+ mrb_str_concat(mrb, str, mrb_fixnum_to_str(mrb, mrb_fixnum_value(loc->lineno), 10));
- va_start(ap, format);
- vsnprintf(RSTRING_PTR(str), RSTRING_LEN(str), format, ap);
- va_end(ap);
+ if (loc->method) {
+ mrb_str_cat_lit(mrb, str, ":in ");
+
+ if (loc->class_name) {
+ mrb_str_cat_cstr(mrb, str, loc->class_name);
+ mrb_str_cat_cstr(mrb, str, loc->sep);
+ }
+
+ mrb_str_cat_cstr(mrb, str, loc->method);
+ }
- mrb_str_resize(mrb, str, RSTRING_LEN(str) - 1);
mrb_ary_push(mrb, ary, str);
mrb_gc_arena_restore(mrb, ai);
}
static void
-output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *stream)
+output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *data)
{
- mrb_callinfo *ci;
- const char *filename, *method, *sep;
- int i, lineno, tracehead = 1;
+ int i;
if (ciidx >= mrb->c->ciend - mrb->c->cibase)
ciidx = 10; /* ciidx is broken... */
for (i = ciidx; i >= 0; i--) {
+ struct backtrace_location loc;
+ mrb_callinfo *ci;
+ mrb_irep *irep;
+ mrb_code *pc;
+
ci = &mrb->c->cibase[i];
- filename = NULL;
if (!ci->proc) continue;
- if (MRB_PROC_CFUNC_P(ci->proc)) {
- continue;
+ if (MRB_PROC_CFUNC_P(ci->proc)) continue;
+
+ irep = ci->proc->body.irep;
+
+ if (mrb->c->cibase[i].err) {
+ pc = mrb->c->cibase[i].err;
}
- else {
- mrb_irep *irep = ci->proc->body.irep;
- mrb_code *pc;
-
- if (mrb->c->cibase[i].err) {
- pc = mrb->c->cibase[i].err;
- }
- else if (i+1 <= ciidx) {
- pc = mrb->c->cibase[i+1].pc - 1;
- }
- else {
- pc = pc0;
- }
- filename = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq));
- lineno = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq));
+ else if (i+1 <= ciidx) {
+ pc = mrb->c->cibase[i+1].pc - 1;
}
- if (lineno == -1) continue;
- if (ci->target_class == ci->proc->target_class)
- sep = ".";
- else
- sep = "#";
-
- if (!filename) {
- filename = "(unknown)";
+ else {
+ pc = pc0;
}
+ loc.filename = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq));
+ loc.lineno = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq));
- if (tracehead) {
- func(mrb, stream, 1, "trace:\n");
- tracehead = 0;
- }
- method = mrb_sym2name(mrb, ci->mid);
- if (method) {
- const char *cn = mrb_class_name(mrb, ci->proc->target_class);
-
- if (cn) {
- func(mrb, stream, 1, "\t[%d] ", i);
- func(mrb, stream, 0, "%s:%d:in %s%s%s", filename, lineno, cn, sep, method);
- func(mrb, stream, 1, "\n");
- }
- else {
- func(mrb, stream, 1, "\t[%d] ", i);
- func(mrb, stream, 0, "%s:%d:in %s", filename, lineno, method);
- func(mrb, stream, 1, "\n");
- }
+ if (loc.lineno == -1) continue;
+
+ if (ci->target_class == ci->proc->target_class) {
+ loc.sep = ".";
}
else {
- func(mrb, stream, 1, "\t[%d] ", i);
- func(mrb, stream, 0, "%s:%d", filename, lineno);
- func(mrb, stream, 1, "\n");
+ loc.sep = "#";
}
+
+ if (!loc.filename) {
+ loc.filename = "(unknown)";
+ }
+
+ loc.method = mrb_sym2name(mrb, ci->mid);
+ loc.class_name = mrb_class_name(mrb, ci->proc->target_class);
+ loc.i = i;
+ func(mrb, &loc, data);
}
}
static void
exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream)
{
+ mrb_value lastpc;
+ mrb_code *code;
+
+ lastpc = mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc"));
+ if (mrb_nil_p(lastpc)) {
+ code = NULL;
+ } else {
+ code = (mrb_code*)mrb_cptr(lastpc);
+ }
+
output_backtrace(mrb, mrb_fixnum(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "ciidx"))),
- (mrb_code*)mrb_cptr(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc"))),
- func, stream);
+ code, func, stream);
}
/* mrb_print_backtrace/mrb_get_backtrace:
@@ -144,13 +165,32 @@ exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func fun
overwritten. So invoke these functions just after detecting exceptions.
*/
-void
+#ifndef MRB_DISABLE_STDIO
+
+MRB_API void
mrb_print_backtrace(mrb_state *mrb)
{
- exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)stderr);
+ struct print_backtrace_args args;
+
+ if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) {
+ return;
+ }
+
+ args.stream = stderr;
+ args.tracehead = TRUE;
+ exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)&args);
}
-mrb_value
+#else
+
+MRB_API void
+mrb_print_backtrace(mrb_state *mrb)
+{
+}
+
+#endif
+
+MRB_API mrb_value
mrb_exc_backtrace(mrb_state *mrb, mrb_value self)
{
mrb_value ary;
@@ -161,7 +201,7 @@ mrb_exc_backtrace(mrb_state *mrb, mrb_value self)
return ary;
}
-mrb_value
+MRB_API mrb_value
mrb_get_backtrace(mrb_state *mrb)
{
mrb_value ary;
@@ -175,24 +215,3 @@ mrb_get_backtrace(mrb_state *mrb)
return ary;
}
-
-#else
-
-void
-mrb_print_backtrace(mrb_state *mrb)
-{
-}
-
-mrb_value
-mrb_exc_backtrace(mrb_state *mrb, mrb_value self)
-{
- return mrb_ary_new(mrb);
-}
-
-mrb_value
-mrb_get_backtrace(mrb_state *mrb)
-{
- return mrb_ary_new(mrb);
-}
-
-#endif
diff --git a/src/class.c b/src/class.c
index ba078911b..ccbbe2544 100644
--- a/src/class.c
+++ b/src/class.c
@@ -4,17 +4,16 @@
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
#include <stdarg.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/numeric.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/error.h"
-#include "mruby/data.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/data.h>
KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal)
@@ -77,7 +76,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
if (o->c->tt == MRB_TT_SCLASS) return;
sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class);
- sc->mt = 0;
+ sc->mt = kh_init(mt, mrb);
sc->iv = 0;
if (o->tt == MRB_TT_CLASS) {
c = (struct RClass*)o;
@@ -122,22 +121,35 @@ module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id)
return mrb_class_ptr(c);
}
-struct RClass*
+MRB_API struct RClass*
mrb_class_outer_module(mrb_state *mrb, struct RClass *c)
{
mrb_value outer;
outer = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"));
- if (mrb_nil_p(outer)) return 0;
+ if (mrb_nil_p(outer)) return NULL;
return mrb_class_ptr(outer);
}
+static void
+check_if_class_or_module(mrb_state *mrb, mrb_value obj)
+{
+ switch (mrb_type(obj)) {
+ case MRB_TT_CLASS:
+ case MRB_TT_SCLASS:
+ case MRB_TT_MODULE:
+ return;
+ default:
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", mrb_inspect(mrb, obj));
+ }
+}
+
static struct RClass*
define_module(mrb_state *mrb, mrb_sym name, struct RClass *outer)
{
struct RClass *m;
- if (mrb_const_defined_at(mrb, outer, name)) {
+ if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) {
return module_from_sym(mrb, outer, name);
}
m = mrb_module_new(mrb);
@@ -146,25 +158,26 @@ define_module(mrb_state *mrb, mrb_sym name, struct RClass *outer)
return m;
}
-struct RClass*
+MRB_API struct RClass*
mrb_define_module_id(mrb_state *mrb, mrb_sym name)
{
return define_module(mrb, name, mrb->object_class);
}
-struct RClass*
+MRB_API struct RClass*
mrb_define_module(mrb_state *mrb, const char *name)
{
return define_module(mrb, mrb_intern_cstr(mrb, name), mrb->object_class);
}
-struct RClass*
+MRB_API struct RClass*
mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id)
{
+ check_if_class_or_module(mrb, outer);
return define_module(mrb, id, mrb_class_ptr(outer));
}
-struct RClass *
+MRB_API struct RClass*
mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name)
{
mrb_sym id = mrb_intern_cstr(mrb, name);
@@ -175,12 +188,20 @@ mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name)
}
static struct RClass*
+find_origin(struct RClass *c)
+{
+ MRB_CLASS_ORIGIN(c);
+ return c;
+}
+
+static struct RClass*
define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *outer)
{
struct RClass * c;
- if (mrb_const_defined_at(mrb, outer, name)) {
+ if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) {
c = class_from_sym(mrb, outer, name);
+ MRB_CLASS_ORIGIN(c);
if (super && mrb_class_real(c->super) != super) {
mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %S (%S not %S)",
mrb_sym2str(mrb, name),
@@ -195,16 +216,16 @@ define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *
return c;
}
-struct RClass*
+MRB_API struct RClass*
mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super)
{
if (!super) {
- mrb_warn(mrb, "no super class for `%S', Object assumed", mrb_sym2str(mrb, name));
+ mrb_warn(mrb, "no super class for '%S', Object assumed", mrb_sym2str(mrb, name));
}
return define_class(mrb, name, super, mrb->object_class);
}
-struct RClass*
+MRB_API struct RClass*
mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super)
{
return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super);
@@ -218,7 +239,7 @@ mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
mrb_funcall(mrb, mrb_obj_value(super), "inherited", 1, mrb_obj_value(klass));
}
-struct RClass*
+MRB_API struct RClass*
mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id)
{
struct RClass *s;
@@ -233,22 +254,14 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id
else {
s = 0;
}
- switch (mrb_type(outer)) {
- case MRB_TT_CLASS:
- case MRB_TT_SCLASS:
- case MRB_TT_MODULE:
- break;
- default:
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", outer);
- break;
- }
+ check_if_class_or_module(mrb, outer);
c = define_class(mrb, id, s, mrb_class_ptr(outer));
mrb_class_inherited(mrb, mrb_class_real(c->super), c);
return c;
}
-mrb_bool
+MRB_API mrb_bool
mrb_class_defined(mrb_state *mrb, const char *name)
{
mrb_value sym = mrb_check_intern_cstr(mrb, name);
@@ -258,25 +271,25 @@ mrb_class_defined(mrb_state *mrb, const char *name)
return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), mrb_symbol(sym));
}
-struct RClass *
+MRB_API struct RClass *
mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
{
return class_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
}
-struct RClass *
+MRB_API struct RClass *
mrb_class_get(mrb_state *mrb, const char *name)
{
return mrb_class_get_under(mrb, mrb->object_class, name);
}
-struct RClass *
+MRB_API struct RClass *
mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name)
{
return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name));
}
-struct RClass *
+MRB_API struct RClass *
mrb_module_get(mrb_state *mrb, const char *name)
{
return mrb_module_get_under(mrb, mrb->object_class, name);
@@ -298,7 +311,7 @@ mrb_module_get(mrb_state *mrb, const char *name)
* \note if a class named \a name is already defined and its superclass is
* \a super, the function just returns the defined class.
*/
-struct RClass *
+MRB_API struct RClass *
mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super)
{
mrb_sym id = mrb_intern_cstr(mrb, name);
@@ -306,7 +319,7 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s
#if 0
if (!super) {
- mrb_warn(mrb, "no super class for `%S::%S', Object assumed",
+ mrb_warn(mrb, "no super class for '%S::%S', Object assumed",
mrb_obj_value(outer), mrb_sym2str(mrb, id));
}
#endif
@@ -315,11 +328,13 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s
return c;
}
-void
+MRB_API void
mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p)
{
- khash_t(mt) *h = c->mt;
+ khash_t(mt) *h;
khiter_t k;
+ MRB_CLASS_ORIGIN(c);
+ h = c->mt;
if (!h) h = c->mt = kh_init(mt, mrb);
k = kh_put(mt, mrb, h, mid);
@@ -329,7 +344,7 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RPro
}
}
-void
+MRB_API void
mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec)
{
struct RProc *p;
@@ -341,28 +356,37 @@ mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t f
mrb_gc_arena_restore(mrb, ai);
}
-void
+MRB_API void
mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
{
mrb_define_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec);
}
-void
-mrb_define_method_vm(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_value body)
+/* a function to raise NotImplementedError with current method name */
+MRB_API void
+mrb_notimplement(mrb_state *mrb)
{
- khash_t(mt) *h = c->mt;
- khiter_t k;
- struct RProc *p;
+ const char *str;
+ mrb_int len;
+ mrb_callinfo *ci = mrb->c->ci;
- if (!h) h = c->mt = kh_init(mt, mrb);
- k = kh_put(mt, mrb, h, name);
- p = mrb_proc_ptr(body);
- kh_value(h, k) = p;
- if (p) {
- mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p);
+ if (ci->mid) {
+ str = mrb_sym2name_len(mrb, ci->mid, &len);
+ mrb_raisef(mrb, E_NOTIMP_ERROR,
+ "%S() function is unimplemented on this machine",
+ mrb_str_new_static(mrb, str, (size_t)len));
}
}
+/* a function to be replacement of unimplemented method */
+MRB_API mrb_value
+mrb_notimplement_m(mrb_state *mrb, mrb_value self)
+{
+ mrb_notimplement(mrb);
+ /* not reached */
+ return mrb_nil_value();
+}
+
static mrb_value
check_type(mrb_state *mrb, mrb_value val, enum mrb_vtype t, const char *c, const char *m)
{
@@ -393,6 +417,21 @@ to_hash(mrb_state *mrb, mrb_value val)
return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash");
}
+static mrb_sym
+to_sym(mrb_state *mrb, mrb_value ss)
+{
+ if (mrb_type(ss) == MRB_TT_SYMBOL) {
+ return mrb_symbol(ss);
+ }
+ else if (mrb_string_p(ss)) {
+ return mrb_intern_str(mrb, to_str(mrb, ss));
+ }
+ else {
+ mrb_value obj = mrb_funcall(mrb, ss, "inspect", 0);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj);
+ }
+}
+
/*
retrieve arguments from mrb_state.
@@ -406,12 +445,12 @@ to_hash(mrb_state *mrb, mrb_value val)
----------------------------------------------------------------------------------------------
o: Object [mrb_value]
C: class/module [mrb_value]
- S: String [mrb_value]
- A: Array [mrb_value]
- H: Hash [mrb_value]
- s: String [char*,mrb_int] Receive two arguments.
- z: String [char*] NUL terminated string.
- a: Array [mrb_value*,mrb_int] Receive two arguments.
+ S: String [mrb_value] when ! follows, the value may be nil
+ A: Array [mrb_value] when ! follows, the value may be nil
+ H: Hash [mrb_value] when ! follows, the value may be nil
+ s: String [char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil
+ z: String [char*] NUL terminated string; z! gives NULL for nil
+ a: Array [mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil
f: Float [mrb_float]
i: Integer [mrb_int]
b: Boolean [mrb_bool]
@@ -422,7 +461,7 @@ to_hash(mrb_state *mrb, mrb_value val)
|: optional Next argument of '|' and later are optional.
?: optional given [mrb_bool] true if preceding argument (optional) is given.
*/
-mrb_int
+MRB_API mrb_int
mrb_get_args(mrb_state *mrb, const char *format, ...)
{
char c;
@@ -496,6 +535,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_value *p;
p = va_arg(ap, mrb_value*);
+ if (*format == '!') {
+ format++;
+ if (i < argc && mrb_nil_p(*sp)) {
+ *p = *sp++;
+ i++;
+ break;
+ }
+ }
if (i < argc) {
*p = to_str(mrb, *sp++);
i++;
@@ -507,6 +554,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_value *p;
p = va_arg(ap, mrb_value*);
+ if (*format == '!') {
+ format++;
+ if (i < argc && mrb_nil_p(*sp)) {
+ *p = *sp++;
+ i++;
+ break;
+ }
+ }
if (i < argc) {
*p = to_ary(mrb, *sp++);
i++;
@@ -518,6 +573,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_value *p;
p = va_arg(ap, mrb_value*);
+ if (*format == '!') {
+ format++;
+ if (i < argc && mrb_nil_p(*sp)) {
+ *p = *sp++;
+ i++;
+ break;
+ }
+ }
if (i < argc) {
*p = to_hash(mrb, *sp++);
i++;
@@ -532,6 +595,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
ps = va_arg(ap, char**);
pl = va_arg(ap, mrb_int*);
+ if (*format == '!') {
+ format++;
+ if (i < argc && mrb_nil_p(*sp)) {
+ *ps = NULL;
+ *pl = 0;
+ i++;
+ break;
+ }
+ }
if (i < argc) {
ss = to_str(mrb, *sp++);
*ps = RSTRING_PTR(ss);
@@ -543,9 +615,17 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
case 'z':
{
mrb_value ss;
- char **ps;
-
- ps = va_arg(ap, char**);
+ const char **ps;
+
+ ps = va_arg(ap, const char**);
+ if (*format == '!') {
+ format++;
+ if (i < argc && mrb_nil_p(*sp)) {
+ *ps = NULL;
+ i++; sp++;
+ break;
+ }
+ }
if (i < argc) {
ss = to_str(mrb, *sp++);
*ps = mrb_string_value_cstr(mrb, &ss);
@@ -562,6 +642,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
pb = va_arg(ap, mrb_value**);
pl = va_arg(ap, mrb_int*);
+ if (*format == '!') {
+ format++;
+ if (i < argc && mrb_nil_p(*sp)) {
+ *pb = 0;
+ *pl = 0;
+ i++; sp++;
+ break;
+ }
+ }
if (i < argc) {
aa = to_ary(mrb, *sp++);
a = mrb_ary_ptr(aa);
@@ -635,16 +724,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_value ss;
ss = *sp++;
- if (mrb_type(ss) == MRB_TT_SYMBOL) {
- *symp = mrb_symbol(ss);
- }
- else if (mrb_string_p(ss)) {
- *symp = mrb_intern_str(mrb, to_str(mrb, ss));
- }
- else {
- mrb_value obj = mrb_funcall(mrb, ss, "inspect", 0);
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj);
- }
+ *symp = to_sym(mrb, ss);
i++;
}
}
@@ -656,6 +736,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
datap = va_arg(ap, void**);
type = va_arg(ap, struct mrb_data_type const*);
+ if (*format == '!') {
+ format++;
+ if (i < argc && mrb_nil_p(*sp)) {
+ *datap = 0;
+ i++; sp++;
+ break;
+ }
+ }
if (i < argc) {
*datap = mrb_data_get_ptr(mrb, *sp++, type);
++i;
@@ -739,47 +827,130 @@ boot_defclass(mrb_state *mrb, struct RClass *super)
return c;
}
-void
-mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
+static void
+boot_initmod(mrb_state *mrb, struct RClass *mod)
+{
+ mod->mt = kh_init(mt, mrb);
+}
+
+static struct RClass*
+include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super)
{
- struct RClass *ins_pos;
+ struct RClass *ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class);
+ if (m->tt == MRB_TT_ICLASS) {
+ m = m->c;
+ }
+ MRB_CLASS_ORIGIN(m);
+ ic->iv = m->iv;
+ ic->mt = m->mt;
+ ic->super = super;
+ if (m->tt == MRB_TT_ICLASS) {
+ ic->c = m->c;
+ } else {
+ ic->c = m;
+ }
+ return ic;
+}
+
+static int
+include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super)
+{
+ struct RClass *p, *ic;
+ void *klass_mt = find_origin(c)->mt;
- ins_pos = c;
while (m) {
- struct RClass *p = c, *ic;
int superclass_seen = 0;
- if (c->mt && c->mt == m->mt) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
- }
- while (p) {
- if (c != p && p->tt == MRB_TT_CLASS) {
- superclass_seen = 1;
- }
- else if (p->mt == m->mt) {
- if (p->tt == MRB_TT_ICLASS && !superclass_seen) {
- ins_pos = p;
+ if (m->flags & MRB_FLAG_IS_PREPENDED)
+ goto skip;
+
+ if (klass_mt && klass_mt == m->mt)
+ return -1;
+
+ p = c->super;
+ while(p) {
+ if (p->tt == MRB_TT_ICLASS) {
+ if (p->mt == m->mt) {
+ if (!superclass_seen) {
+ ins_pos = p; // move insert point
+ }
+ goto skip;
}
- goto skip;
+ } else if (p->tt == MRB_TT_CLASS) {
+ if (!search_super) break;
+ superclass_seen = 1;
}
p = p->super;
}
- ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class);
- if (m->tt == MRB_TT_ICLASS) {
- ic->c = m->c;
- }
- else {
- ic->c = m;
- }
- ic->mt = m->mt;
- ic->iv = m->iv;
- ic->super = ins_pos->super;
+
+ ic = include_class_new(mrb, m, ins_pos->super);
ins_pos->super = ic;
- mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic);
+ mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ins_pos->super);
ins_pos = ic;
skip:
m = m->super;
}
+ return 0;
+}
+
+MRB_API void
+mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
+{
+ int changed = include_module_at(mrb, c, find_origin(c), m, 1);
+ if (changed < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
+ }
+}
+
+MRB_API void
+mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
+{
+ struct RClass *origin;
+ int changed = 0;
+
+ if (!(c->flags & MRB_FLAG_IS_PREPENDED)) {
+ origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c);
+ origin->flags |= MRB_FLAG_IS_ORIGIN;
+ origin->super = c->super;
+ c->super = origin;
+ origin->mt = c->mt;
+ c->mt = kh_init(mt, mrb);
+ mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin);
+ c->flags |= MRB_FLAG_IS_PREPENDED;
+ }
+ changed = include_module_at(mrb, c, c, m, 0);
+ if (changed < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected");
+ }
+}
+
+static mrb_value
+mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod)
+{
+ mrb_value klass;
+
+ mrb_check_type(mrb, mod, MRB_TT_MODULE);
+ mrb_get_args(mrb, "C", &klass);
+ mrb_prepend_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod));
+ return mod;
+}
+
+static mrb_value
+mrb_mod_prepend(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value *argv;
+ mrb_int argc, i;
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+ for (i=0; i<argc; i++) {
+ mrb_check_type(mrb, argv[i], MRB_TT_MODULE);
+ }
+ while (argc--) {
+ mrb_funcall(mrb, argv[argc], "prepend_features", 1, klass);
+ mrb_funcall(mrb, argv[argc], "prepended", 1, klass);
+ }
+
+ return klass;
}
static mrb_value
@@ -853,15 +1024,12 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self)
{
mrb_value result;
struct RClass *c = mrb_class_ptr(self);
-
result = mrb_ary_new(mrb);
- mrb_ary_push(mrb, result, mrb_obj_value(c));
- c = c->super;
while (c) {
if (c->tt == MRB_TT_ICLASS) {
mrb_ary_push(mrb, result, mrb_obj_value(c->c));
}
- else if (c->tt != MRB_TT_SCLASS) {
+ else if (!(c->flags & MRB_FLAG_IS_PREPENDED)) {
mrb_ary_push(mrb, result, mrb_obj_value(c));
}
c = c->super;
@@ -886,11 +1054,15 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self)
{
mrb_value result;
struct RClass *c = mrb_class_ptr(self);
+ struct RClass *origin = c;
+ MRB_CLASS_ORIGIN(origin);
result = mrb_ary_new(mrb);
while (c) {
- if (c->tt == MRB_TT_ICLASS) {
- mrb_ary_push(mrb, result, mrb_obj_value(c->c));
+ if (c != origin && c->tt == MRB_TT_ICLASS) {
+ if (c->c->tt == MRB_TT_MODULE) {
+ mrb_ary_push(mrb, result, mrb_obj_value(c->c));
+ }
}
c = c->super;
}
@@ -902,10 +1074,11 @@ static mrb_value
mrb_mod_initialize(mrb_state *mrb, mrb_value mod)
{
mrb_value b;
-
- mrb_get_args(mrb, "&", &b);
+ struct RClass *m = mrb_class_ptr(mod);
+ boot_initmod(mrb, m); // bootstrap a newly initialized module
+ mrb_get_args(mrb, "|&", &b);
if (!mrb_nil_p(b)) {
- mrb_yield_with_class(mrb, b, 1, &mod, mod, mrb_class_ptr(mod));
+ mrb_yield_with_class(mrb, b, 1, &mod, mod, m);
}
return mod;
}
@@ -958,7 +1131,7 @@ mrb_mod_dummy_visibility(mrb_state *mrb, mrb_value mod)
return mod;
}
-mrb_value
+MRB_API mrb_value
mrb_singleton_class(mrb_state *mrb, mrb_value v)
{
struct RBasic *obj;
@@ -989,27 +1162,27 @@ mrb_singleton_class(mrb_state *mrb, mrb_value v)
return mrb_obj_value(obj->c);
}
-void
+MRB_API void
mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name, mrb_func_t func, mrb_aspec aspec)
{
prepare_singleton_class(mrb, (struct RBasic*)o);
mrb_define_method_id(mrb, o->c, mrb_intern_cstr(mrb, name), func, aspec);
}
-void
+MRB_API void
mrb_define_class_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
{
mrb_define_singleton_method(mrb, (struct RObject*)c, name, func, aspec);
}
-void
+MRB_API void
mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec)
{
mrb_define_class_method(mrb, c, name, func, aspec);
mrb_define_method(mrb, c, name, func, aspec);
}
-struct RProc*
+MRB_API struct RProc*
mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
{
khiter_t k;
@@ -1030,10 +1203,10 @@ mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
}
c = c->super;
}
- return 0; /* no method */
+ return NULL; /* no method */
}
-struct RProc*
+MRB_API struct RProc*
mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid)
{
struct RProc *m;
@@ -1041,7 +1214,7 @@ mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid)
m = mrb_method_search_vm(mrb, &c, mid);
if (!m) {
mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0);
- if (RSTRING_LEN(inspect) > 64) {
+ if (mrb_string_p(inspect) && RSTRING_LEN(inspect) > 64) {
inspect = mrb_any_to_s(mrb, mrb_obj_value(c));
}
mrb_name_error(mrb, mid, "undefined method '%S' for class %S",
@@ -1051,6 +1224,91 @@ mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid)
}
static mrb_value
+attr_reader(mrb_state *mrb, mrb_value obj)
+{
+ mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
+ return mrb_iv_get(mrb, obj, to_sym(mrb, name));
+}
+
+static mrb_value
+mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
+{
+ struct RClass *c = mrb_class_ptr(mod);
+ mrb_value *argv;
+ mrb_int argc, i;
+ int ai;
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+ ai = mrb_gc_arena_save(mrb);
+ for (i=0; i<argc; i++) {
+ mrb_value name, str;
+ mrb_sym method, sym;
+
+ method = to_sym(mrb, argv[i]);
+ name = mrb_sym2str(mrb, method);
+ str = mrb_str_buf_new(mrb, RSTRING_LEN(name)+1);
+ mrb_str_cat_lit(mrb, str, "@");
+ mrb_str_cat_str(mrb, str, name);
+ sym = mrb_intern_str(mrb, str);
+ mrb_iv_check(mrb, sym);
+ name = mrb_symbol_value(sym);
+ mrb_define_method_raw(mrb, c, method,
+ mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name));
+ mrb_gc_arena_restore(mrb, ai);
+ }
+ return mrb_nil_value();
+}
+
+static mrb_value
+attr_writer(mrb_state *mrb, mrb_value obj)
+{
+ mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
+ mrb_value val;
+
+ mrb_get_args(mrb, "o", &val);
+ mrb_iv_set(mrb, obj, to_sym(mrb, name), val);
+ return val;
+}
+
+static mrb_value
+mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
+{
+ struct RClass *c = mrb_class_ptr(mod);
+ mrb_value *argv;
+ mrb_int argc, i;
+ int ai;
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+ ai = mrb_gc_arena_save(mrb);
+ for (i=0; i<argc; i++) {
+ mrb_value name, str, attr;
+ mrb_sym method, sym;
+
+ method = to_sym(mrb, argv[i]);
+
+ /* prepare iv name (@name) */
+ name = mrb_sym2str(mrb, method);
+ str = mrb_str_buf_new(mrb, RSTRING_LEN(name)+1);
+ mrb_str_cat_lit(mrb, str, "@");
+ mrb_str_cat_str(mrb, str, name);
+ sym = mrb_intern_str(mrb, str);
+ mrb_iv_check(mrb, sym);
+ attr = mrb_symbol_value(sym);
+
+ /* prepare method name (name=) */
+ str = mrb_str_buf_new(mrb, RSTRING_LEN(str));
+ mrb_str_cat_str(mrb, str, name);
+ mrb_str_cat_lit(mrb, str, "=");
+ method = mrb_intern_str(mrb, str);
+
+ mrb_define_method_raw(mrb, c, method,
+ mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr));
+ mrb_gc_arena_restore(mrb, ai);
+ }
+ return mrb_nil_value();
+}
+
+static mrb_value
mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
{
struct RClass *c = mrb_class_ptr(cv);
@@ -1069,15 +1327,15 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
* call-seq:
* class.new(args, ...) -> obj
*
- * Calls <code>allocate</code> to create a new object of
- * <i>class</i>'s class, then invokes that object's
- * <code>initialize</code> method, passing it <i>args</i>.
- * This is the method that ends up getting called whenever
- * an object is constructed using .new.
+ * Creates a new object of <i>class</i>'s class, then
+ * invokes that object's <code>initialize</code> method,
+ * passing it <i>args</i>. This is the method that ends
+ * up getting called whenever an object is constructed using
+ * `.new`.
*
*/
-mrb_value
+MRB_API mrb_value
mrb_instance_new(mrb_state *mrb, mrb_value cv)
{
mrb_value obj, blk;
@@ -1091,7 +1349,7 @@ mrb_instance_new(mrb_state *mrb, mrb_value cv)
return obj;
}
-mrb_value
+MRB_API mrb_value
mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv)
{
mrb_value obj;
@@ -1137,9 +1395,9 @@ mrb_class_superclass(mrb_state *mrb, mrb_value klass)
struct RClass *c;
c = mrb_class_ptr(klass);
- c = c->super;
+ c = find_origin(c)->super;
while (c && c->tt == MRB_TT_ICLASS) {
- c = c->super;
+ c = find_origin(c)->super;
}
if (!c) return mrb_nil_value();
return mrb_obj_value(c);
@@ -1157,6 +1415,31 @@ mrb_bob_not(mrb_state *mrb, mrb_value cv)
return mrb_bool_value(!mrb_test(cv));
}
+void
+mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
+{
+ mrb_sym inspect;
+ mrb_value repr;
+
+ inspect = mrb_intern_lit(mrb, "inspect");
+ if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) {
+ /* method missing in inspect; avoid recursion */
+ repr = mrb_any_to_s(mrb, self);
+ }
+ else if (mrb_respond_to(mrb, self, inspect) && mrb->c->ci - mrb->c->cibase < 64) {
+ repr = mrb_funcall_argv(mrb, self, inspect, 0, 0);
+ if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) {
+ repr = mrb_any_to_s(mrb, self);
+ }
+ }
+ else {
+ repr = mrb_any_to_s(mrb, self);
+ }
+
+ mrb_no_method_error(mrb, name, args, "undefined method '%S' for %S",
+ mrb_sym2str(mrb, name), repr);
+}
+
/* 15.3.1.3.30 */
/*
* call-seq:
@@ -1196,32 +1479,14 @@ mrb_bob_missing(mrb_state *mrb, mrb_value mod)
mrb_sym name;
mrb_value *a;
mrb_int alen;
- mrb_sym inspect;
- mrb_value repr;
mrb_get_args(mrb, "n*", &name, &a, &alen);
-
- inspect = mrb_intern_lit(mrb, "inspect");
- if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) {
- /* method missing in inspect; avoid recursion */
- repr = mrb_any_to_s(mrb, mod);
- }
- else if (mrb_respond_to(mrb, mod, inspect)) {
- repr = mrb_funcall_argv(mrb, mod, inspect, 0, 0);
- if (RSTRING_LEN(repr) > 64) {
- repr = mrb_any_to_s(mrb, mod);
- }
- }
- else {
- repr = mrb_any_to_s(mrb, mod);
- }
-
- mrb_no_method_error(mrb, name, alen, a, "undefined method '%S' for %S", mrb_sym2str(mrb, name), repr);
+ mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a));
/* not reached */
return mrb_nil_value();
}
-mrb_bool
+MRB_API mrb_bool
mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid)
{
khiter_t k;
@@ -1245,13 +1510,13 @@ mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid)
return FALSE; /* no method */
}
-mrb_bool
+MRB_API mrb_bool
mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid)
{
return mrb_obj_respond_to(mrb, mrb_class(mrb, obj), mid);
}
-mrb_value
+MRB_API mrb_value
mrb_class_path(mrb_state *mrb, struct RClass *c)
{
mrb_value path;
@@ -1291,18 +1556,18 @@ mrb_class_path(mrb_state *mrb, struct RClass *c)
return path;
}
-struct RClass *
+MRB_API struct RClass *
mrb_class_real(struct RClass* cl)
{
if (cl == 0)
- return 0;
+ return NULL;
while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) {
cl = cl->super;
}
return cl;
}
-const char*
+MRB_API const char*
mrb_class_name(mrb_state *mrb, struct RClass* c)
{
mrb_value path = mrb_class_path(mrb, c);
@@ -1314,7 +1579,7 @@ mrb_class_name(mrb_state *mrb, struct RClass* c)
return RSTRING_PTR(path);
}
-const char*
+MRB_API const char*
mrb_obj_classname(mrb_state *mrb, mrb_value obj)
{
return mrb_class_name(mrb, mrb_obj_class(mrb, obj));
@@ -1346,7 +1611,7 @@ mrb_check_inheritable(mrb_state *mrb, struct RClass *super)
* \exception TypeError \a super is not inheritable.
* \exception TypeError \a super is the Class class.
*/
-struct RClass *
+MRB_API struct RClass*
mrb_class_new(mrb_state *mrb, struct RClass *super)
{
struct RClass *c;
@@ -1366,12 +1631,11 @@ mrb_class_new(mrb_state *mrb, struct RClass *super)
/*!
* Creates a new module.
*/
-struct RClass *
+MRB_API struct RClass*
mrb_module_new(mrb_state *mrb)
{
struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class);
- m->mt = kh_init(mt, mrb);
-
+ boot_initmod(mrb, m);
return m;
}
@@ -1389,18 +1653,18 @@ mrb_module_new(mrb_state *mrb)
* self.class #=> Object
*/
-struct RClass*
+MRB_API struct RClass*
mrb_obj_class(mrb_state *mrb, mrb_value obj)
{
- return mrb_class_real(mrb_class(mrb, obj));
+ return mrb_class_real(mrb_class(mrb, obj));
}
-void
+MRB_API void
mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b)
{
struct RProc *m = mrb_method_search(mrb, c, b);
- mrb_define_method_vm(mrb, c, a, mrb_obj_value(m));
+ mrb_define_method_raw(mrb, c, a, m);
}
/*!
@@ -1409,7 +1673,7 @@ mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b)
* \param name1 a new name for the method
* \param name2 the original name of the method
*/
-void
+MRB_API void
mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2)
{
mrb_alias_method(mrb, klass, mrb_intern_cstr(mrb, name1), mrb_intern_cstr(mrb, name2));
@@ -1438,13 +1702,13 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
case MRB_TT_CLASS:
case MRB_TT_MODULE:
case MRB_TT_SCLASS:
- mrb_str_append(mrb, str, mrb_inspect(mrb, v));
+ mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v));
break;
default:
- mrb_str_append(mrb, str, mrb_any_to_s(mrb, v));
+ mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v));
break;
}
- mrb_str_cat_lit(mrb, str, ">");
+ return mrb_str_cat_lit(mrb, str, ">");
}
else {
struct RClass *c;
@@ -1470,14 +1734,12 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
break;
}
mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, c));
- mrb_str_cat_lit(mrb, str, ">");
+ return mrb_str_cat_lit(mrb, str, ">");
}
else {
- str = path;
+ return path;
}
}
-
- return str;
}
static mrb_value
@@ -1494,24 +1756,21 @@ mrb_mod_alias(mrb_state *mrb, mrb_value mod)
static void
undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
{
- mrb_value m;
-
if (!mrb_obj_respond_to(mrb, c, a)) {
mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c));
}
else {
- MRB_SET_VALUE(m, MRB_TT_PROC, value.p, 0);
- mrb_define_method_vm(mrb, c, a, m);
+ mrb_define_method_raw(mrb, c, a, NULL);
}
}
-void
+MRB_API void
mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name)
{
undef_method(mrb, c, mrb_intern_cstr(mrb, name));
}
-void
+MRB_API void
mrb_undef_class_method(mrb_state *mrb, struct RClass *c, const char *name)
{
mrb_undef_method(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name);
@@ -1552,43 +1811,20 @@ mod_define_method(mrb_state *mrb, mrb_value self)
}
static void
-check_cv_name_sym(mrb_state *mrb, mrb_sym id)
-{
- const char *s;
- mrb_int len;
-
- s = mrb_sym2name_len(mrb, id, &len);
- if (len < 3 || !(s[0] == '@' && s[1] == '@')) {
- mrb_name_error(mrb, id, "`%S' is not allowed as a class variable name", mrb_sym2str(mrb, id));
- }
-}
-
-static void
check_cv_name_str(mrb_state *mrb, mrb_value str)
{
const char *s = RSTRING_PTR(str);
mrb_int len = RSTRING_LEN(str);
if (len < 3 || !(s[0] == '@' && s[1] == '@')) {
- mrb_name_error(mrb, mrb_intern_str(mrb, str), "`%S' is not allowed as a class variable name", str);
+ mrb_name_error(mrb, mrb_intern_str(mrb, str), "'%S' is not allowed as a class variable name", str);
}
}
-static mrb_value
-get_sym_or_str_arg(mrb_state *mrb)
+static void
+check_cv_name_sym(mrb_state *mrb, mrb_sym id)
{
- mrb_value sym_or_str;
-
- mrb_get_args(mrb, "o", &sym_or_str);
-
- if (mrb_symbol_p(sym_or_str) || mrb_string_p(sym_or_str)) {
- return sym_or_str;
- }
- else {
- mrb_value obj = mrb_funcall(mrb, sym_or_str, "inspect", 0);
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj);
- return mrb_nil_value();
- }
+ check_cv_name_str(mrb, mrb_sym2str(mrb, id));
}
/* 15.2.2.4.16 */
@@ -1609,26 +1845,11 @@ get_sym_or_str_arg(mrb_state *mrb)
static mrb_value
mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod)
{
- mrb_value id;
- mrb_bool defined_p;
+ mrb_sym id;
- id = get_sym_or_str_arg(mrb);
- if (mrb_symbol_p(id)) {
- check_cv_name_sym(mrb, mrb_symbol(id));
- defined_p = mrb_cv_defined(mrb, mod, mrb_symbol(id));
- }
- else {
- mrb_value sym;
- check_cv_name_str(mrb, id);
- sym = mrb_check_intern_str(mrb, id);
- if (mrb_nil_p(sym)) {
- defined_p = FALSE;
- }
- else {
- defined_p = mrb_cv_defined(mrb, mod, mrb_symbol(sym));
- }
- }
- return mrb_bool_value(defined_p);
+ mrb_get_args(mrb, "n", &id);
+ check_cv_name_sym(mrb, id);
+ return mrb_bool_value(mrb_cv_defined(mrb, mod, id));
}
/* 15.2.2.4.17 */
@@ -1763,41 +1984,29 @@ mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod)
static mrb_value
mrb_mod_method_defined(mrb_state *mrb, mrb_value mod)
{
- mrb_value id;
- mrb_bool method_defined_p;
+ mrb_sym id;
- id = get_sym_or_str_arg(mrb);
- if (mrb_symbol_p(id)) {
- method_defined_p = mrb_obj_respond_to(mrb, mrb_class_ptr(mod), mrb_symbol(id));
- }
- else {
- mrb_value sym = mrb_check_intern_str(mrb, id);
- if (mrb_nil_p(sym)) {
- method_defined_p = FALSE;
- }
- else {
- method_defined_p = mrb_obj_respond_to(mrb, mrb_class_ptr(mod), mrb_symbol(sym));
- }
- }
- return mrb_bool_value(method_defined_p);
+ mrb_get_args(mrb, "n", &id);
+ return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id));
}
static void
remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid)
{
struct RClass *c = mrb_class_ptr(mod);
- khash_t(mt) *h = c->mt;
+ khash_t(mt) *h = find_origin(c)->mt;
khiter_t k;
if (h) {
k = kh_get(mt, mrb, h, mid);
if (k != kh_end(h)) {
kh_del(mt, mrb, h, k);
+ mrb_funcall(mrb, mod, "method_removed", 1, mrb_symbol_value(mid));
return;
}
}
- mrb_name_error(mrb, mid, "method `%S' not defined in %S",
+ mrb_name_error(mrb, mid, "method '%S' not defined in %S",
mrb_sym2str(mrb, mid), mod);
}
@@ -1824,17 +2033,7 @@ mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
return mod;
}
-static void
-check_const_name_sym(mrb_state *mrb, mrb_sym id)
-{
- const char *s;
- mrb_int len;
- s = mrb_sym2name_len(mrb, id, &len);
- if (len < 1 || !ISUPPER(*s)) {
- mrb_name_error(mrb, id, "wrong constant name %S", mrb_sym2str(mrb, id));
- }
-}
static void
check_const_name_str(mrb_state *mrb, mrb_value str)
@@ -1844,30 +2043,30 @@ check_const_name_str(mrb_state *mrb, mrb_value str)
}
}
-static mrb_value
-mrb_mod_const_defined(mrb_state *mrb, mrb_value mod)
+static void
+check_const_name_sym(mrb_state *mrb, mrb_sym id)
{
- mrb_value id;
- mrb_bool const_defined_p;
+ check_const_name_str(mrb, mrb_sym2str(mrb, id));
+}
- id = get_sym_or_str_arg(mrb);
- if (mrb_type(id) == MRB_TT_SYMBOL) {
- check_const_name_sym(mrb, mrb_symbol(id));
- const_defined_p = mrb_const_defined(mrb, mod, mrb_symbol(id));
- }
- else {
- mrb_value sym;
- check_const_name_str(mrb, id);
- sym = mrb_check_intern_str(mrb, id);
- if (mrb_nil_p(sym)) {
- const_defined_p = FALSE;
- }
- else {
- const_defined_p = mrb_const_defined(mrb, mod, mrb_symbol(sym));
- }
+static mrb_value
+const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool inherit)
+{
+ if (inherit) {
+ return mrb_bool_value(mrb_const_defined(mrb, mod, id));
}
+ return mrb_bool_value(mrb_const_defined_at(mrb, mod, id));
+}
- return mrb_bool_value(const_defined_p);
+static mrb_value
+mrb_mod_const_defined(mrb_state *mrb, mrb_value mod)
+{
+ mrb_sym id;
+ mrb_bool inherit = TRUE;
+
+ mrb_get_args(mrb, "n|b", &id, &inherit);
+ check_const_name_sym(mrb, id);
+ return const_defined(mrb, mod, id, inherit);
}
static mrb_value
@@ -1946,6 +2145,43 @@ mrb_mod_eqq(mrb_state *mrb, mrb_value mod)
return mrb_bool_value(eqq);
}
+MRB_API mrb_value
+mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
+{
+ mrb_value *argv;
+ mrb_int argc, i;
+ mrb_sym mid;
+ struct RProc *method_rproc;
+ struct RClass *rclass;
+ int ai;
+
+ mrb_check_type(mrb, mod, MRB_TT_MODULE);
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+ if(argc == 0) {
+ /* set MODFUNC SCOPE if implemented */
+ return mod;
+ }
+
+ /* set PRIVATE method visibility if implemented */
+ /* mrb_mod_dummy_visibility(mrb, mod); */
+
+ for (i=0; i<argc; i++) {
+ mrb_check_type(mrb, argv[i], MRB_TT_SYMBOL);
+
+ mid = mrb_symbol(argv[i]);
+ rclass = mrb_class_ptr(mod);
+ method_rproc = mrb_method_search(mrb, rclass, mid);
+
+ prepare_singleton_class(mrb, (struct RBasic*)rclass);
+ ai = mrb_gc_arena_save(mrb);
+ mrb_define_method_raw(mrb, rclass->c, mid, method_rproc);
+ mrb_gc_arena_restore(mrb, ai);
+ }
+
+ return mod;
+}
+
void
mrb_init_class(mrb_state *mrb)
{
@@ -1979,6 +2215,9 @@ mrb_init_class(mrb_state *mrb)
name_class(mrb, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */
name_class(mrb, cls, mrb_intern_lit(mrb, "Class")); /* 15.2.3 */
+ mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); /* 15.2.17 */
+ MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC);
+
MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS);
mrb_define_method(mrb, bob, "initialize", mrb_bob_init, MRB_ARGS_NONE());
mrb_define_method(mrb, bob, "!", mrb_bob_not, MRB_ARGS_NONE());
@@ -1996,6 +2235,9 @@ mrb_init_class(mrb_state *mrb)
mrb_define_method(mrb, mod, "class_variable_set", mrb_mod_cvar_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */
mrb_define_method(mrb, mod, "extend_object", mrb_mod_extend_object, MRB_ARGS_REQ(1)); /* 15.2.2.4.25 */
mrb_define_method(mrb, mod, "extended", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */
+ mrb_define_method(mrb, mod, "prepend", mrb_mod_prepend, MRB_ARGS_ANY());
+ mrb_define_method(mrb, mod, "prepended", mrb_bob_init, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mod, "prepend_features", mrb_mod_prepend_features, MRB_ARGS_REQ(1));
mrb_define_method(mrb, mod, "include", mrb_mod_include, MRB_ARGS_ANY()); /* 15.2.2.4.27 */
mrb_define_method(mrb, mod, "include?", mrb_mod_include_p, MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */
mrb_define_method(mrb, mod, "append_features", mrb_mod_append_features, MRB_ARGS_REQ(1)); /* 15.2.2.4.10 */
@@ -2006,17 +2248,21 @@ mrb_init_class(mrb_state *mrb)
mrb_define_method(mrb, mod, "instance_methods", mrb_mod_instance_methods, MRB_ARGS_ANY()); /* 15.2.2.4.33 */
mrb_define_method(mrb, mod, "method_defined?", mrb_mod_method_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */
mrb_define_method(mrb, mod, "module_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.35 */
+ mrb_define_method(mrb, mod, "module_function", mrb_mod_module_function, MRB_ARGS_ANY());
mrb_define_method(mrb, mod, "private", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.36 */
mrb_define_method(mrb, mod, "protected", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.37 */
mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */
mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */
mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */
+ mrb_define_method(mrb, mod, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mod, "attr_reader", mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */
+ mrb_define_method(mrb, mod, "attr_writer", mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */
mrb_define_method(mrb, mod, "to_s", mrb_mod_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, mod, "inspect", mrb_mod_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, mod, "alias_method", mrb_mod_alias, MRB_ARGS_ANY()); /* 15.2.2.4.8 */
mrb_define_method(mrb, mod, "ancestors", mrb_mod_ancestors, MRB_ARGS_NONE()); /* 15.2.2.4.9 */
mrb_define_method(mrb, mod, "undef_method", mrb_mod_undef, MRB_ARGS_ANY()); /* 15.2.2.4.41 */
- mrb_define_method(mrb, mod, "const_defined?", mrb_mod_const_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.20 */
+ mrb_define_method(mrb, mod, "const_defined?", mrb_mod_const_defined, MRB_ARGS_ARG(1,1)); /* 15.2.2.4.20 */
mrb_define_method(mrb, mod, "const_get", mrb_mod_const_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.21 */
mrb_define_method(mrb, mod, "const_set", mrb_mod_const_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.23 */
mrb_define_method(mrb, mod, "constants", mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */
diff --git a/src/codedump.c b/src/codedump.c
new file mode 100644
index 000000000..4f4b6de57
--- /dev/null
+++ b/src/codedump.c
@@ -0,0 +1,453 @@
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
+#include <mruby/opcode.h>
+#include <mruby/string.h>
+#include <mruby/proc.h>
+
+#ifndef MRB_DISABLE_STDIO
+static int
+print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre)
+{
+ size_t i;
+
+ if (n == 0) return 0;
+
+ for (i=0; i+1<irep->nlocals; i++) {
+ if (irep->lv[i].r == n) {
+ mrb_sym sym = irep->lv[i].name;
+ if (pre) printf(" ");
+ printf("R%d:%s", (int)n, mrb_sym2name(mrb, sym));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#define RA 1
+#define RB 2
+#define RAB 3
+
+static void
+print_lv(mrb_state *mrb, mrb_irep *irep, mrb_code c, int r)
+{
+ int pre = 0;
+
+ if (!irep->lv
+ || ((!(r & RA) || GETARG_A(c) >= irep->nlocals)
+ && (!(r & RB) || GETARG_B(c) >= irep->nlocals))) {
+ printf("\n");
+ return;
+ }
+ printf("\t; ");
+ if (r & RA) {
+ pre = print_r(mrb, irep, GETARG_A(c), 0);
+ }
+ if (r & RB) {
+ print_r(mrb, irep, GETARG_B(c), pre);
+ }
+ printf("\n");
+}
+#endif
+
+static void
+codedump(mrb_state *mrb, mrb_irep *irep)
+{
+#ifndef MRB_DISABLE_STDIO
+ int i;
+ int ai;
+ mrb_code c;
+ const char *file = NULL, *next_file;
+ int32_t line;
+
+ if (!irep) return;
+ printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", (void*)irep,
+ irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen);
+
+ for (i = 0; i < (int)irep->ilen; i++) {
+ ai = mrb_gc_arena_save(mrb);
+
+ next_file = mrb_debug_get_filename(irep, i);
+ if (next_file && file != next_file) {
+ printf("file: %s\n", next_file);
+ file = next_file;
+ }
+ line = mrb_debug_get_line(irep, i);
+ if (line < 0) {
+ printf(" ");
+ }
+ else {
+ printf("%5d ", line);
+ }
+
+ printf("%03d ", i);
+ c = irep->iseq[i];
+ switch (GET_OPCODE(c)) {
+ case OP_NOP:
+ printf("OP_NOP\n");
+ break;
+ case OP_MOVE:
+ printf("OP_MOVE\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
+ print_lv(mrb, irep, c, RAB);
+ break;
+ case OP_LOADL:
+ {
+ mrb_value v = irep->pool[GETARG_Bx(c)];
+ mrb_value s = mrb_inspect(mrb, v);
+ printf("OP_LOADL\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s));
+ }
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_LOADI:
+ printf("OP_LOADI\tR%d\t%d\t", GETARG_A(c), GETARG_sBx(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_LOADSYM:
+ printf("OP_LOADSYM\tR%d\t:%s", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_LOADNIL:
+ printf("OP_LOADNIL\tR%d\t\t", GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_LOADSELF:
+ printf("OP_LOADSELF\tR%d\t\t", GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_LOADT:
+ printf("OP_LOADT\tR%d\t\t", GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_LOADF:
+ printf("OP_LOADF\tR%d\t\t", GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_GETGLOBAL:
+ printf("OP_GETGLOBAL\tR%d\t:%s", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_SETGLOBAL:
+ printf("OP_SETGLOBAL\t:%s\tR%d\t",
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
+ GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_GETCONST:
+ printf("OP_GETCONST\tR%d\t:%s", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_SETCONST:
+ printf("OP_SETCONST\t:%s\tR%d\t",
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
+ GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_GETMCNST:
+ printf("OP_GETMCNST\tR%d\tR%d::%s", GETARG_A(c), GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ print_lv(mrb, irep, c, RAB);
+ break;
+ case OP_SETMCNST:
+ printf("OP_SETMCNST\tR%d::%s\tR%d", GETARG_A(c)+1,
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
+ GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_GETIV:
+ printf("OP_GETIV\tR%d\t%s", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_SETIV:
+ printf("OP_SETIV\t%s\tR%d",
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
+ GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_GETUPVAR:
+ printf("OP_GETUPVAR\tR%d\t%d\t%d",
+ GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_SETUPVAR:
+ printf("OP_SETUPVAR\tR%d\t%d\t%d",
+ GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_GETCV:
+ printf("OP_GETCV\tR%d\t%s", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_SETCV:
+ printf("OP_SETCV\t%s\tR%d",
+ mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]),
+ GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_JMP:
+ printf("OP_JMP\t%03d\n", i+GETARG_sBx(c));
+ break;
+ case OP_JMPIF:
+ printf("OP_JMPIF\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
+ break;
+ case OP_JMPNOT:
+ printf("OP_JMPNOT\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c));
+ break;
+ case OP_SEND:
+ printf("OP_SEND\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_SENDB:
+ printf("OP_SENDB\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_TAILCALL:
+ printf("OP_TAILCALL\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_SUPER:
+ printf("OP_SUPER\tR%d\t%d\n", GETARG_A(c),
+ GETARG_C(c));
+ break;
+ case OP_ARGARY:
+ printf("OP_ARGARY\tR%d\t%d:%d:%d:%d", GETARG_A(c),
+ (GETARG_Bx(c)>>10)&0x3f,
+ (GETARG_Bx(c)>>9)&0x1,
+ (GETARG_Bx(c)>>4)&0x1f,
+ (GETARG_Bx(c)>>0)&0xf);
+ print_lv(mrb, irep, c, RA);
+ break;
+
+ case OP_ENTER:
+ printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n",
+ (GETARG_Ax(c)>>18)&0x1f,
+ (GETARG_Ax(c)>>13)&0x1f,
+ (GETARG_Ax(c)>>12)&0x1,
+ (GETARG_Ax(c)>>7)&0x1f,
+ (GETARG_Ax(c)>>2)&0x1f,
+ (GETARG_Ax(c)>>1)&0x1,
+ GETARG_Ax(c) & 0x1);
+ break;
+ case OP_RETURN:
+ printf("OP_RETURN\tR%d", GETARG_A(c));
+ switch (GETARG_B(c)) {
+ case OP_R_NORMAL:
+ case OP_R_RETURN:
+ printf("\treturn\t"); break;
+ case OP_R_BREAK:
+ printf("\tbreak\t"); break;
+ default:
+ printf("\tbroken\t"); break;
+ break;
+ }
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_BLKPUSH:
+ printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d", GETARG_A(c),
+ (GETARG_Bx(c)>>10)&0x3f,
+ (GETARG_Bx(c)>>9)&0x1,
+ (GETARG_Bx(c)>>4)&0x1f,
+ (GETARG_Bx(c)>>0)&0xf);
+ print_lv(mrb, irep, c, RA);
+ break;
+
+ case OP_LAMBDA:
+ printf("OP_LAMBDA\tR%d\tI(%+d)\t%d", GETARG_A(c), GETARG_b(c)+1, GETARG_c(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_RANGE:
+ printf("OP_RANGE\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ print_lv(mrb, irep, c, RAB);
+ break;
+ case OP_METHOD:
+ printf("OP_METHOD\tR%d\t:%s", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
+ print_lv(mrb, irep, c, RA);
+ break;
+
+ case OP_ADD:
+ printf("OP_ADD\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_ADDI:
+ printf("OP_ADDI\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_SUB:
+ printf("OP_SUB\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_SUBI:
+ printf("OP_SUBI\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_MUL:
+ printf("OP_MUL\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_DIV:
+ printf("OP_DIV\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_LT:
+ printf("OP_LT\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_LE:
+ printf("OP_LE\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_GT:
+ printf("OP_GT\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_GE:
+ printf("OP_GE\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+ case OP_EQ:
+ printf("OP_EQ\tR%d\t:%s\t%d\n", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
+ GETARG_C(c));
+ break;
+
+ case OP_STOP:
+ printf("OP_STOP\n");
+ break;
+
+ case OP_ARRAY:
+ printf("OP_ARRAY\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ print_lv(mrb, irep, c, RAB);
+ break;
+ case OP_ARYCAT:
+ printf("OP_ARYCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
+ print_lv(mrb, irep, c, RAB);
+ break;
+ case OP_ARYPUSH:
+ printf("OP_ARYPUSH\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
+ print_lv(mrb, irep, c, RAB);
+ break;
+ case OP_AREF:
+ printf("OP_AREF\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ print_lv(mrb, irep, c, RAB);
+ break;
+ case OP_APOST:
+ printf("OP_APOST\tR%d\t%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_STRING:
+ {
+ mrb_value v = irep->pool[GETARG_Bx(c)];
+ mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v)));
+ printf("OP_STRING\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s));
+ }
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_STRCAT:
+ printf("OP_STRCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
+ print_lv(mrb, irep, c, RAB);
+ break;
+ case OP_HASH:
+ printf("OP_HASH\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ print_lv(mrb, irep, c, RAB);
+ break;
+
+ case OP_OCLASS:
+ printf("OP_OCLASS\tR%d\t\t", GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_CLASS:
+ printf("OP_CLASS\tR%d\t:%s", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_MODULE:
+ printf("OP_MODULE\tR%d\t:%s", GETARG_A(c),
+ mrb_sym2name(mrb, irep->syms[GETARG_B(c)]));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_EXEC:
+ printf("OP_EXEC\tR%d\tI(%+d)", GETARG_A(c), GETARG_Bx(c)+1);
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_SCLASS:
+ printf("OP_SCLASS\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
+ print_lv(mrb, irep, c, RAB);
+ break;
+ case OP_TCLASS:
+ printf("OP_TCLASS\tR%d\t\t", GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_ERR:
+ {
+ mrb_value v = irep->pool[GETARG_Bx(c)];
+ mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v)));
+ printf("OP_ERR\t%s\n", RSTRING_PTR(s));
+ }
+ break;
+ case OP_EPUSH:
+ printf("OP_EPUSH\t:I(%+d)\n", GETARG_Bx(c)+1);
+ break;
+ case OP_ONERR:
+ printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c));
+ break;
+ case OP_RESCUE:
+ printf("OP_RESCUE\tR%d\t\t", GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_RAISE:
+ printf("OP_RAISE\tR%d\t\t", GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_POPERR:
+ printf("OP_POPERR\t%d\t\t", GETARG_A(c));
+ print_lv(mrb, irep, c, RA);
+ break;
+ case OP_EPOP:
+ printf("OP_EPOP\t%d\n", GETARG_A(c));
+ break;
+
+ default:
+ printf("OP_unknown %d\t%d\t%d\t%d\n", GET_OPCODE(c),
+ GETARG_A(c), GETARG_B(c), GETARG_C(c));
+ break;
+ }
+ mrb_gc_arena_restore(mrb, ai);
+ }
+ printf("\n");
+#endif
+}
+
+static void
+codedump_recur(mrb_state *mrb, mrb_irep *irep)
+{
+ size_t i;
+
+ codedump(mrb, irep);
+ for (i=0; i<irep->rlen; i++) {
+ codedump_recur(mrb, irep->reps[i]);
+ }
+}
+
+void
+mrb_codedump_all(mrb_state *mrb, struct RProc *proc)
+{
+ codedump_recur(mrb, proc->body.irep);
+}
diff --git a/src/compar.c b/src/compar.c
index 0186b942f..0032fc859 100644
--- a/src/compar.c
+++ b/src/compar.c
@@ -4,7 +4,7 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
void
mrb_init_comparable(mrb_state *mrb)
diff --git a/src/debug.c b/src/debug.c
index ea1aa1ddc..cc2d37034 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -1,7 +1,7 @@
#include <string.h>
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/debug.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/debug.h>
static mrb_irep_debug_info_file *
get_file(mrb_irep_debug_info *info, uint32_t pc)
@@ -49,7 +49,7 @@ select_line_type(const uint16_t *lines, size_t lines_len)
? mrb_debug_line_ary : mrb_debug_line_flat_map;
}
-char const*
+MRB_API char const*
mrb_debug_get_filename(mrb_irep *irep, uint32_t pc)
{
if (irep && pc < irep->ilen) {
@@ -62,7 +62,7 @@ mrb_debug_get_filename(mrb_irep *irep, uint32_t pc)
return NULL;
}
-int32_t
+MRB_API int32_t
mrb_debug_get_line(mrb_irep *irep, uint32_t pc)
{
if (irep && pc < irep->ilen) {
@@ -106,7 +106,7 @@ mrb_debug_get_line(mrb_irep *irep, uint32_t pc)
return -1;
}
-mrb_irep_debug_info *
+MRB_API mrb_irep_debug_info *
mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
{
static const mrb_irep_debug_info initial = { 0, 0, NULL };
@@ -119,7 +119,7 @@ mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
return ret;
}
-mrb_irep_debug_info_file *
+MRB_API mrb_irep_debug_info_file *
mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep,
uint32_t start_pos, uint32_t end_pos)
{
@@ -198,7 +198,7 @@ mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep,
return ret;
}
-void
+MRB_API void
mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d)
{
uint32_t i;
diff --git a/src/dump.c b/src/dump.c
index b820f1a68..ea1cd5596 100644
--- a/src/dump.c
+++ b/src/dump.c
@@ -4,16 +4,22 @@
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
#include <string.h>
#include <limits.h>
-#include "mruby/dump.h"
-#include "mruby/string.h"
-#include "mruby/irep.h"
-#include "mruby/numeric.h"
-#include "mruby/debug.h"
-
-#ifdef ENABLE_STDIO
+#include <mruby/dump.h>
+#include <mruby/string.h>
+#include <mruby/irep.h>
+#include <mruby/numeric.h>
+#include <mruby/debug.h>
+
+#define FLAG_BYTEORDER_NATIVE 2
+#define FLAG_BYTEORDER_NONATIVE 0
+
+#ifdef MRB_USE_FLOAT
+#define MRB_FLOAT_FMT "%.8e"
+#else
+#define MRB_FLOAT_FMT "%.16e"
+#endif
static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep);
@@ -22,6 +28,17 @@ static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep);
#endif
static size_t
+write_padding(uint8_t *buf)
+{
+ const size_t align = MRB_DUMP_ALIGNMENT;
+ size_t pad_len = -(intptr_t)buf & (align-1);
+ if (pad_len > 0) {
+ memset(buf, 0, pad_len);
+ }
+ return pad_len;
+}
+
+static size_t
get_irep_header_size(mrb_state *mrb)
{
size_t size = 0;
@@ -37,7 +54,7 @@ write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
{
uint8_t *cur = buf;
- cur += uint32_to_bin(get_irep_record_size_1(mrb, irep), cur); /* record size */
+ cur += uint32_to_bin((uint32_t)get_irep_record_size_1(mrb, irep), cur); /* record size */
cur += uint16_to_bin((uint16_t)irep->nlocals, cur); /* number of local variable */
cur += uint16_to_bin((uint16_t)irep->nregs, cur); /* number of register variable */
cur += uint16_to_bin((uint16_t)irep->rlen, cur); /* number of child irep */
@@ -52,20 +69,39 @@ get_iseq_block_size(mrb_state *mrb, mrb_irep *irep)
size_t size = 0;
size += sizeof(uint32_t); /* ilen */
+ size += sizeof(uint32_t); /* max padding */
size += sizeof(uint32_t) * irep->ilen; /* iseq(n) */
return size;
}
static ptrdiff_t
-write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
+write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags)
{
uint8_t *cur = buf;
uint32_t iseq_no;
cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */
- for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
- cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* opcode */
+ cur += write_padding(cur);
+ switch (flags & DUMP_ENDIAN_NAT) {
+ case DUMP_ENDIAN_BIG:
+ if (bigendian_p()) goto native;
+ for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
+ cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* opcode */
+ }
+ break;
+ case DUMP_ENDIAN_LIL:
+ if (!bigendian_p()) goto native;
+ for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
+ cur += uint32l_to_bin(irep->iseq[iseq_no], cur); /* opcode */
+ }
+ break;
+
+ native:
+ case DUMP_ENDIAN_NAT:
+ memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code));
+ cur += irep->ilen * sizeof(mrb_code);
+ break;
}
return cur - buf;
@@ -78,7 +114,6 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
size_t size = 0;
size_t pool_no;
mrb_value str;
- char buf[32];
size += sizeof(uint32_t); /* plen */
size += irep->plen * (sizeof(uint8_t) + sizeof(uint16_t)); /* len(n) */
@@ -97,9 +132,9 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
break;
case MRB_TT_FLOAT:
+ str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
{
- int len;
- len = mrb_float_to_str(buf, mrb_float(irep->pool[pool_no]));
+ mrb_int len = RSTRING_LEN(str);
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
size += (size_t)len;
}
@@ -130,7 +165,6 @@ write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
uint16_t len;
mrb_value str;
const char *char_ptr;
- char char_buf[30];
cur += uint32_to_bin(irep->plen, cur); /* number of pool */
@@ -141,43 +175,29 @@ write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
case MRB_TT_FIXNUM:
cur += uint8_to_bin(IREP_TT_FIXNUM, cur); /* data type */
str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10);
- char_ptr = RSTRING_PTR(str);
- {
- mrb_int tlen;
-
- tlen = RSTRING_LEN(str);
- mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX);
- len = (uint16_t)tlen;
- }
break;
case MRB_TT_FLOAT:
cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */
- {
- int tlen;
- tlen = mrb_float_to_str(char_buf, mrb_float(irep->pool[pool_no]));
- mrb_assert_int_fit(int, tlen, uint16_t, UINT16_MAX);
- len = (uint16_t)tlen;
- }
- char_ptr = &char_buf[0];
+ str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
break;
case MRB_TT_STRING:
cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */
- char_ptr = RSTRING_PTR(irep->pool[pool_no]);
- {
- mrb_int tlen;
-
- tlen = RSTRING_LEN(irep->pool[pool_no]);
- mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX);
- len = (uint16_t)tlen;
- }
+ str = irep->pool[pool_no];
break;
default:
continue;
}
+ char_ptr = RSTRING_PTR(str);
+ {
+ mrb_int tlen = RSTRING_LEN(str);
+ mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX);
+ len = (uint16_t)tlen;
+ }
+
cur += uint16_to_bin(len, cur); /* data length */
memcpy(cur, char_ptr, (size_t)len);
cur += len;
@@ -263,9 +283,10 @@ get_irep_record_size(mrb_state *mrb, mrb_irep *irep)
}
static int
-write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin, size_t *irep_record_size)
+write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags)
{
uint32_t i;
+ uint8_t *src = bin;
if (irep == NULL) {
return MRB_DUMP_INVALID_IREP;
@@ -276,10 +297,8 @@ write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin, size_t *irep_rec
return MRB_DUMP_GENERAL_FAILURE;
}
- memset(bin, 0, *irep_record_size);
-
bin += write_irep_header(mrb, irep, bin);
- bin += write_iseq_block(mrb, irep, bin);
+ bin += write_iseq_block(mrb, irep, bin, flags);
bin += write_pool_block(mrb, irep, bin);
bin += write_syms_block(mrb, irep, bin);
@@ -287,13 +306,13 @@ write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin, size_t *irep_rec
int result;
size_t rsize;
- result = write_irep_record(mrb, irep->reps[i], bin, &rsize);
+ result = write_irep_record(mrb, irep->reps[i], bin, &rsize, flags);
if (result != MRB_DUMP_OK) {
return result;
}
- *irep_record_size += rsize;
bin += rsize;
}
+ *irep_record_size = bin - src;
return MRB_DUMP_OK;
}
@@ -302,7 +321,7 @@ write_footer(mrb_state *mrb, uint8_t *bin)
{
struct rite_binary_footer footer;
- memcpy(footer.section_identify, RITE_BINARY_EOF, sizeof(footer.section_identify));
+ memcpy(footer.section_ident, RITE_BINARY_EOF, sizeof(footer.section_ident));
uint32_to_bin(sizeof(struct rite_binary_footer), footer.section_size);
memcpy(bin, &footer, sizeof(struct rite_binary_footer));
@@ -315,7 +334,7 @@ write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
{
struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin;
- memcpy(header->section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(header->section_identify));
+ memcpy(header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(header->section_ident));
mrb_assert_int_fit(size_t, section_size, uint32_t, UINT32_MAX);
uint32_to_bin((uint32_t)section_size, header->section_size);
@@ -325,10 +344,9 @@ write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
}
static int
-write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin)
+write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags)
{
int result;
- size_t section_size = 0; /* size of irep record */
size_t rsize = 0;
uint8_t *cur = bin;
@@ -337,14 +355,13 @@ write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin)
}
cur += sizeof(struct rite_section_irep_header);
- section_size += sizeof(struct rite_section_irep_header);
- result = write_irep_record(mrb, irep, cur, &rsize);
+ result = write_irep_record(mrb, irep, cur, &rsize, flags);
if (result != MRB_DUMP_OK) {
return result;
}
- section_size += rsize;
- write_section_irep_header(mrb, section_size, bin);
+ *len_p = cur - bin + rsize;
+ write_section_irep_header(mrb, *len_p, bin);
return MRB_DUMP_OK;
}
@@ -354,7 +371,7 @@ write_section_lineno_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
{
struct rite_section_lineno_header *header = (struct rite_section_lineno_header*)bin;
- memcpy(header->section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(header->section_identify));
+ memcpy(header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(header->section_ident));
uint32_to_bin((uint32_t)section_size, header->section_size);
return MRB_DUMP_OK;
@@ -645,7 +662,7 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const
dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len);
section_size += dlen;
- memcpy(header->section_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(header->section_identify));
+ memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident));
mrb_assert(section_size <= INT32_MAX);
uint32_to_bin(section_size, header->section_size);
@@ -784,7 +801,7 @@ write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const *
goto lv_section_exit;
}
- memcpy(header->section_identify, RITE_SECTION_LV_IDENTIFIER, sizeof(header->section_identify));
+ memcpy(header->section_ident, RITE_SECTION_LV_IDENT, sizeof(header->section_ident));
diff = cur - start;
mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
@@ -795,13 +812,28 @@ lv_section_exit:
}
static int
-write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin)
+write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8_t flags)
{
struct rite_binary_header *header = (struct rite_binary_header *)bin;
uint16_t crc;
uint32_t offset;
- memcpy(header->binary_identify, RITE_BINARY_IDENTIFIER, sizeof(header->binary_identify));
+ switch (flags & DUMP_ENDIAN_NAT) {
+ endian_big:
+ case DUMP_ENDIAN_BIG:
+ memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident));
+ break;
+ endian_little:
+ case DUMP_ENDIAN_LIL:
+ memcpy(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident));
+ break;
+
+ case DUMP_ENDIAN_NAT:
+ if (bigendian_p()) goto endian_big;
+ goto endian_little;
+ break;
+ }
+
memcpy(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version));
memcpy(header->compiler_name, RITE_COMPILER_NAME, sizeof(header->compiler_name));
memcpy(header->compiler_version, RITE_COMPILER_VERSION, sizeof(header->compiler_version));
@@ -841,10 +873,26 @@ is_lv_defined(mrb_irep *irep)
return FALSE;
}
-int
-mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t *bin_size)
+static uint8_t
+dump_flags(uint8_t flags, uint8_t native)
+{
+ if (native == FLAG_BYTEORDER_NATIVE) {
+ if ((flags & DUMP_ENDIAN_NAT) == 0) {
+ return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_NAT;
+ }
+ return flags;
+ }
+ if ((flags & DUMP_ENDIAN_NAT) == 0) {
+ return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_BIG;
+ }
+ return flags;
+}
+
+static int
+dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size)
{
int result = MRB_DUMP_GENERAL_FAILURE;
+ size_t malloc_size;
size_t section_irep_size;
size_t section_lineno_size = 0, section_lv_size = 0;
uint8_t *cur = NULL;
@@ -861,7 +909,7 @@ mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, siz
section_irep_size += get_irep_record_size(mrb, irep);
/* DEBUG section size */
- if (debug_info) {
+ if (flags & DUMP_DEBUG_INFO) {
if (debug_info_defined) {
section_lineno_size += sizeof(struct rite_section_debug_header);
/* filename table */
@@ -885,23 +933,23 @@ mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, siz
section_lv_size += get_lv_section_size(mrb, irep, lv_syms, lv_syms_len);
}
- *bin_size = sizeof(struct rite_binary_header) +
- section_irep_size + section_lineno_size + section_lv_size +
- sizeof(struct rite_binary_footer);
- cur = *bin = (uint8_t*)mrb_malloc(mrb, *bin_size);
- if (cur == NULL) {
- goto error_exit;
- }
+ malloc_size = sizeof(struct rite_binary_header) +
+ section_irep_size + section_lineno_size + section_lv_size +
+ sizeof(struct rite_binary_footer);
+ cur = *bin = (uint8_t*)mrb_malloc(mrb, malloc_size);
cur += sizeof(struct rite_binary_header);
- result = write_section_irep(mrb, irep, cur);
+ result = write_section_irep(mrb, irep, cur, &section_irep_size, flags);
if (result != MRB_DUMP_OK) {
goto error_exit;
}
cur += section_irep_size;
+ *bin_size = sizeof(struct rite_binary_header) +
+ section_irep_size + section_lineno_size + section_lv_size +
+ sizeof(struct rite_binary_footer);
/* write DEBUG section */
- if (debug_info) {
+ if (flags & DUMP_DEBUG_INFO) {
if (debug_info_defined) {
result = write_section_debug(mrb, irep, cur, filenames, filenames_len);
}
@@ -923,24 +971,28 @@ mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, siz
}
write_footer(mrb, cur);
- write_rite_binary_header(mrb, *bin_size, *bin);
+ write_rite_binary_header(mrb, *bin_size, *bin, flags);
error_exit:
if (result != MRB_DUMP_OK) {
mrb_free(mrb, *bin);
*bin = NULL;
}
- if (lv_syms) {
- mrb_free(mrb, lv_syms);
- }
- if (filenames) {
- mrb_free(mrb, filenames);
- }
+ mrb_free(mrb, lv_syms);
+ mrb_free(mrb, filenames);
return result;
}
int
-mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp)
+mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size)
+{
+ return dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), bin, bin_size);
+}
+
+#ifndef MRB_DISABLE_STDIO
+
+int
+mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp)
{
uint8_t *bin = NULL;
size_t bin_size = 0;
@@ -950,9 +1002,11 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp)
return MRB_DUMP_INVALID_ARGUMENT;
}
- result = mrb_dump_irep(mrb, irep, debug_info, &bin, &bin_size);
+ result = dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), &bin, &bin_size);
if (result == MRB_DUMP_OK) {
- fwrite(bin, bin_size, 1, fp);
+ if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) {
+ result = MRB_DUMP_WRITE_FAULT;
+ }
}
mrb_free(mrb, bin);
@@ -960,45 +1014,82 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp)
}
static mrb_bool
-is_valid_c_symbol_name(const char *name)
+dump_bigendian_p(uint8_t flags)
{
- const char *c = NULL;
-
- if (name == NULL || name[0] == '\0') return FALSE;
- if (!ISALPHA(name[0]) && name[0] != '_') return FALSE;
-
- c = &name[1];
- for (; *c != '\0'; ++c) {
- if (!ISALNUM(*c) && *c != '_') return FALSE;
- }
-
- return TRUE;
+ switch (flags & DUMP_ENDIAN_NAT) {
+ case DUMP_ENDIAN_BIG:
+ return TRUE;
+ case DUMP_ENDIAN_LIL:
+ return FALSE;
+ default:
+ case DUMP_ENDIAN_NAT:
+ return bigendian_p();
+ }
}
int
-mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE *fp, const char *initname)
+mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname)
{
uint8_t *bin = NULL;
size_t bin_size = 0, bin_idx = 0;
int result;
- if (fp == NULL || initname == NULL || !is_valid_c_symbol_name(initname)) {
+ if (fp == NULL || initname == NULL || initname[0] == '\0') {
return MRB_DUMP_INVALID_ARGUMENT;
}
-
- result = mrb_dump_irep(mrb, irep, debug_info, &bin, &bin_size);
+ flags = dump_flags(flags, FLAG_BYTEORDER_NATIVE);
+ result = dump_irep(mrb, irep, flags, &bin, &bin_size);
if (result == MRB_DUMP_OK) {
- fprintf(fp, "#include <stdint.h>\n"); /* for uint8_t under at least Darwin */
- fprintf(fp, "const uint8_t %s[] = {", initname);
+ if (!dump_bigendian_p(flags)) {
+ if (fprintf(fp, "/* dumped in little endian order.\n"
+ " use `mrbc -E` option for big endian CPU. */\n") < 0) {
+ mrb_free(mrb, bin);
+ return MRB_DUMP_WRITE_FAULT;
+ }
+ }
+ else {
+ if (fprintf(fp, "/* dumped in big endian order.\n"
+ " use `mrbc -e` option for better performance on little endian CPU. */\n") < 0) {
+ mrb_free(mrb, bin);
+ return MRB_DUMP_WRITE_FAULT;
+ }
+ }
+ if (fprintf(fp, "#include <stdint.h>\n") < 0) { /* for uint8_t under at least Darwin */
+ mrb_free(mrb, bin);
+ return MRB_DUMP_WRITE_FAULT;
+ }
+ if (fprintf(fp,
+ "const uint8_t\n"
+ "#if defined __GNUC__\n"
+ "__attribute__((aligned(%u)))\n"
+ "#elif defined _MSC_VER\n"
+ "__declspec(align(%u))\n"
+ "#endif\n"
+ "%s[] = {",
+ (uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) {
+ mrb_free(mrb, bin);
+ return MRB_DUMP_WRITE_FAULT;
+ }
while (bin_idx < bin_size) {
- if (bin_idx % 16 == 0) fputs("\n", fp);
- fprintf(fp, "0x%02x,", bin[bin_idx++]);
+ if (bin_idx % 16 == 0) {
+ if (fputs("\n", fp) == EOF) {
+ mrb_free(mrb, bin);
+ return MRB_DUMP_WRITE_FAULT;
+ }
+ }
+ if (fprintf(fp, "0x%02x,", bin[bin_idx++]) < 0) {
+ mrb_free(mrb, bin);
+ return MRB_DUMP_WRITE_FAULT;
+ }
+ }
+ if (fputs("\n};\n", fp) == EOF) {
+ mrb_free(mrb, bin);
+ return MRB_DUMP_WRITE_FAULT;
}
- fputs("\n};\n", fp);
}
mrb_free(mrb, bin);
return result;
}
-#endif /* ENABLE_STDIO */
+#endif /* MRB_DISABLE_STDIO */
diff --git a/src/enum.c b/src/enum.c
index 3def9e860..adb815bf1 100644
--- a/src/enum.c
+++ b/src/enum.c
@@ -4,7 +4,7 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
void
mrb_init_enumerable(mrb_state *mrb)
diff --git a/src/error.c b/src/error.c
index 5ca013527..d15f9e76f 100644
--- a/src/error.c
+++ b/src/error.c
@@ -7,27 +7,29 @@
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/irep.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/debug.h"
-#include "mruby/error.h"
-#include "mrb_throw.h"
-
-mrb_value
-mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, long len)
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/irep.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/debug.h>
+#include <mruby/error.h>
+#include <mruby/class.h>
+#include <mruby/throw.h>
+
+MRB_API mrb_value
+mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len)
{
- return mrb_funcall(mrb, mrb_obj_value(c), "new", 1, mrb_str_new(mrb, ptr, len));
+ mrb_value arg = mrb_str_new(mrb, ptr, len);
+ return mrb_obj_new(mrb, c, 1, &arg);
}
-mrb_value
+MRB_API mrb_value
mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str)
{
str = mrb_str_to_str(mrb, str);
- return mrb_funcall(mrb, mrb_obj_value(c), "new", 1, str);
+ return mrb_obj_new(mrb, c, 1, &str);
}
/*
@@ -90,8 +92,15 @@ static mrb_value
exc_to_s(mrb_state *mrb, mrb_value exc)
{
mrb_value mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg"));
+ struct RObject *p;
- if (mrb_nil_p(mesg)) return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc));
+ if (!mrb_string_p(mesg)) {
+ return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc));
+ }
+ p = mrb_obj_ptr(mesg);
+ if (!p->c) {
+ p->c = mrb->string_class;
+ }
return mesg;
}
@@ -143,7 +152,7 @@ exc_inspect(mrb_state *mrb, mrb_value exc)
mrb_str_append(mrb, str, line);
mrb_str_cat_lit(mrb, str, ": ");
if (append_mesg) {
- mrb_str_append(mrb, str, mesg);
+ mrb_str_cat_str(mrb, str, mesg);
mrb_str_cat_lit(mrb, str, " (");
}
mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc));
@@ -156,7 +165,7 @@ exc_inspect(mrb_state *mrb, mrb_value exc)
str = mrb_str_new_cstr(mrb, cname);
mrb_str_cat_lit(mrb, str, ": ");
if (append_mesg) {
- mrb_str_append(mrb, str, mesg);
+ mrb_str_cat_str(mrb, str, mesg);
}
else {
mrb_str_cat_cstr(mrb, str, cname);
@@ -166,36 +175,6 @@ exc_inspect(mrb_state *mrb, mrb_value exc)
}
-static mrb_value
-exc_equal(mrb_state *mrb, mrb_value exc)
-{
- mrb_value obj;
- mrb_value mesg;
- mrb_bool equal_p;
- mrb_sym id_mesg = mrb_intern_lit(mrb, "mesg");
-
- mrb_get_args(mrb, "o", &obj);
- if (mrb_obj_equal(mrb, exc, obj)) {
- equal_p = TRUE;
- }
- else {
- if (mrb_obj_class(mrb, exc) != mrb_obj_class(mrb, obj)) {
- if (mrb_respond_to(mrb, obj, mrb_intern_lit(mrb, "message"))) {
- mesg = mrb_funcall(mrb, obj, "message", 0);
- }
- else
- return mrb_false_value();
- }
- else {
- mesg = mrb_attr_get(mrb, obj, id_mesg);
- }
-
- equal_p = mrb_equal(mrb, mrb_attr_get(mrb, exc, id_mesg), mesg);
- }
-
- return mrb_bool_value(equal_p);
-}
-
static void
exc_debug_info(mrb_state *mrb, struct RObject *exc)
{
@@ -223,11 +202,11 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc)
}
}
-mrb_noreturn void
+MRB_API mrb_noreturn void
mrb_exc_raise(mrb_state *mrb, mrb_value exc)
{
mrb->exc = mrb_obj_ptr(exc);
- if (!mrb->out_of_memory) {
+ if (!mrb->gc.out_of_memory) {
exc_debug_info(mrb, mrb->exc);
}
if (!mrb->jmp) {
@@ -237,7 +216,7 @@ mrb_exc_raise(mrb_state *mrb, mrb_value exc)
MRB_THROW(mrb->jmp);
}
-mrb_noreturn void
+MRB_API mrb_noreturn void
mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg)
{
mrb_value mesg;
@@ -245,7 +224,7 @@ mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg)
mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg));
}
-mrb_value
+MRB_API mrb_value
mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
{
const char *p = format;
@@ -282,11 +261,11 @@ mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
else {
size = p - b;
mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size));
- return mrb_ary_join(mrb, ary, mrb_str_new(mrb,NULL,0));
+ return mrb_ary_join(mrb, ary, mrb_str_new(mrb, NULL, 0));
}
}
-mrb_value
+MRB_API mrb_value
mrb_format(mrb_state *mrb, const char *format, ...)
{
va_list ap;
@@ -299,7 +278,7 @@ mrb_format(mrb_state *mrb, const char *format, ...)
return str;
}
-mrb_noreturn void
+MRB_API mrb_noreturn void
mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...)
{
va_list args;
@@ -311,7 +290,7 @@ mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...)
mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg));
}
-mrb_noreturn void
+MRB_API mrb_noreturn void
mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...)
{
mrb_value exc;
@@ -327,10 +306,10 @@ mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...)
mrb_exc_raise(mrb, exc);
}
-void
+MRB_API void
mrb_warn(mrb_state *mrb, const char *fmt, ...)
{
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
va_list ap;
mrb_value str;
@@ -342,10 +321,10 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...)
#endif
}
-mrb_noreturn void
+MRB_API mrb_noreturn void
mrb_bug(mrb_state *mrb, const char *fmt, ...)
{
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
va_list ap;
mrb_value str;
@@ -417,13 +396,13 @@ exception_call:
return mesg;
}
-mrb_value
+MRB_API mrb_value
mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv)
{
return make_exception(mrb, argc, argv, TRUE);
}
-void
+MRB_API void
mrb_sys_fail(mrb_state *mrb, const char *mesg)
{
struct RClass *sce;
@@ -444,16 +423,15 @@ mrb_sys_fail(mrb_state *mrb, const char *mesg)
}
}
-mrb_noreturn void
-mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, char const* fmt, ...)
+MRB_API mrb_noreturn void
+mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...)
{
mrb_value exc;
va_list ap;
va_start(ap, fmt);
exc = mrb_funcall(mrb, mrb_obj_value(E_NOMETHOD_ERROR), "new", 3,
- mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id),
- mrb_ary_new_from_values(mrb, argc, argv));
+ mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), args);
va_end(ap);
mrb_exc_raise(mrb, exc);
}
@@ -464,10 +442,10 @@ mrb_init_exception(mrb_state *mrb)
struct RClass *exception, *runtime_error, *script_error;
mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */
+ MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION);
mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_ANY());
mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_ANY());
mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_ANY());
- mrb_define_method(mrb, exception, "==", exc_equal, MRB_ARGS_REQ(1));
mrb_define_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, exception, "message", exc_message, MRB_ARGS_NONE());
mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE());
@@ -475,7 +453,8 @@ mrb_init_exception(mrb_state *mrb)
mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */
runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */
- mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str(mrb, runtime_error, mrb_str_new_lit(mrb, "Out of memory")));
+ mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, runtime_error, "Out of memory"));
script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */
mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */
+ mrb_define_class(mrb, "SystemStackError", exception);
}
diff --git a/src/error.h b/src/error.h
index 0e0dacf63..eb755ec7f 100644
--- a/src/error.h
+++ b/src/error.h
@@ -1,3 +1,3 @@
/* this header file is to be removed soon.
added for compatibility purpose (1.0.0) */
-#include "mruby/error.h"
+#include <mruby/error.h>
diff --git a/src/etc.c b/src/etc.c
index bc5d116a0..0ad56fe96 100644
--- a/src/etc.c
+++ b/src/etc.c
@@ -4,14 +4,14 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/string.h"
-#include "mruby/data.h"
-#include "mruby/class.h"
-#include "mruby/re.h"
-#include "mruby/irep.h"
-
-struct RData*
+#include <mruby.h>
+#include <mruby/string.h>
+#include <mruby/data.h>
+#include <mruby/class.h>
+#include <mruby/re.h>
+#include <mruby/irep.h>
+
+MRB_API struct RData*
mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type)
{
struct RData *data;
@@ -23,10 +23,10 @@ mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb
return data;
}
-void
+MRB_API void
mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
{
- if (mrb_special_const_p(obj) || (mrb_type(obj) != MRB_TT_DATA)) {
+ if (mrb_type(obj) != MRB_TT_DATA) {
mrb_check_type(mrb, obj, MRB_TT_DATA);
}
if (DATA_TYPE(obj) != type) {
@@ -45,10 +45,10 @@ mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
}
}
-void *
+MRB_API void*
mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
{
- if (mrb_special_const_p(obj) || (mrb_type(obj) != MRB_TT_DATA)) {
+ if (mrb_type(obj) != MRB_TT_DATA) {
return NULL;
}
if (DATA_TYPE(obj) != type) {
@@ -57,27 +57,25 @@ mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
return DATA_PTR(obj);
}
-void *
+MRB_API void*
mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
{
mrb_data_check_type(mrb, obj, type);
return DATA_PTR(obj);
}
-mrb_sym
+MRB_API mrb_sym
mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
{
- mrb_value tmp;
mrb_sym id;
switch (mrb_type(name)) {
default:
- tmp = mrb_check_string_type(mrb, name);
- if (mrb_nil_p(tmp)) {
- tmp = mrb_inspect(mrb, name);
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp);
+ name = mrb_check_string_type(mrb, name);
+ if (mrb_nil_p(name)) {
+ name = mrb_inspect(mrb, name);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", name);
}
- name = tmp;
/* fall through */
case MRB_TT_STRING:
name = mrb_str_intern(mrb, name);
@@ -88,7 +86,7 @@ mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
return id;
}
-mrb_int
+MRB_API mrb_int
mrb_float_id(mrb_float f)
{
const char *p = (const char*)&f;
@@ -104,7 +102,7 @@ mrb_float_id(mrb_float f)
return id;
}
-mrb_int
+MRB_API mrb_int
mrb_obj_id(mrb_value obj)
{
mrb_int tt = mrb_type(obj);
@@ -113,42 +111,42 @@ mrb_obj_id(mrb_value obj)
#define MakeID(p) MakeID2(p,tt)
switch (tt) {
- case MRB_TT_FREE:
- case MRB_TT_UNDEF:
+ case MRB_TT_FREE:
+ case MRB_TT_UNDEF:
return MakeID(0); /* not define */
- case MRB_TT_FALSE:
+ case MRB_TT_FALSE:
if (mrb_nil_p(obj))
return MakeID(1);
return MakeID(0);
- case MRB_TT_TRUE:
+ case MRB_TT_TRUE:
return MakeID(1);
- case MRB_TT_SYMBOL:
+ case MRB_TT_SYMBOL:
return MakeID(mrb_symbol(obj));
- case MRB_TT_FIXNUM:
+ case MRB_TT_FIXNUM:
return MakeID2(mrb_float_id((mrb_float)mrb_fixnum(obj)), MRB_TT_FLOAT);
- case MRB_TT_FLOAT:
+ case MRB_TT_FLOAT:
return MakeID(mrb_float_id(mrb_float(obj)));
- case MRB_TT_STRING:
- case MRB_TT_OBJECT:
- case MRB_TT_CLASS:
- case MRB_TT_MODULE:
- case MRB_TT_ICLASS:
- case MRB_TT_SCLASS:
- case MRB_TT_PROC:
- case MRB_TT_ARRAY:
- case MRB_TT_HASH:
- case MRB_TT_RANGE:
- case MRB_TT_EXCEPTION:
- case MRB_TT_FILE:
- case MRB_TT_DATA:
+ case MRB_TT_STRING:
+ case MRB_TT_OBJECT:
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ case MRB_TT_ICLASS:
+ case MRB_TT_SCLASS:
+ case MRB_TT_PROC:
+ case MRB_TT_ARRAY:
+ case MRB_TT_HASH:
+ case MRB_TT_RANGE:
+ case MRB_TT_EXCEPTION:
+ case MRB_TT_FILE:
+ case MRB_TT_DATA:
default:
return MakeID(mrb_ptr(obj));
}
}
#ifdef MRB_WORD_BOXING
-mrb_value
-mrb_float_value(mrb_state *mrb, mrb_float f)
+MRB_API mrb_value
+mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
{
mrb_value v;
@@ -157,8 +155,8 @@ mrb_float_value(mrb_state *mrb, mrb_float f)
return v;
}
-mrb_value
-mrb_float_pool(mrb_state *mrb, mrb_float f)
+MRB_API mrb_value
+mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f)
{
struct RFloat *nf = (struct RFloat *)mrb_malloc(mrb, sizeof(struct RFloat));
nf->tt = MRB_TT_FLOAT;
@@ -167,8 +165,8 @@ mrb_float_pool(mrb_state *mrb, mrb_float f)
return mrb_obj_value(nf);
}
-mrb_value
-mrb_cptr_value(mrb_state *mrb, void *p)
+MRB_API mrb_value
+mrb_word_boxing_cptr_value(mrb_state *mrb, void *p)
{
mrb_value v;
@@ -178,8 +176,45 @@ mrb_cptr_value(mrb_state *mrb, void *p)
}
#endif /* MRB_WORD_BOXING */
-mrb_bool
+MRB_API mrb_bool
mrb_regexp_p(mrb_state *mrb, mrb_value v)
{
return mrb_class_defined(mrb, REGEXP_CLASS) && mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS));
}
+
+#if defined _MSC_VER && _MSC_VER < 1900
+
+#ifndef va_copy
+static void
+mrb_msvc_va_copy(va_list *dest, va_list src)
+{
+ *dest = src;
+}
+#define va_copy(dest, src) mrb_msvc_va_copy(&(dest), src)
+#endif
+
+MRB_API int
+mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg)
+{
+ int cnt;
+ va_list argcp;
+ va_copy(argcp, arg);
+ if (n == 0 || (cnt = _vsnprintf_s(s, n, _TRUNCATE, format, argcp)) < 0) {
+ cnt = _vscprintf(format, arg);
+ }
+ va_end(argcp);
+ return cnt;
+}
+
+MRB_API int
+mrb_msvc_snprintf(char *s, size_t n, const char *format, ...)
+{
+ va_list arg;
+ int ret;
+ va_start(arg, format);
+ ret = mrb_msvc_vsnprintf(s, n, format, arg);
+ va_end(arg);
+ return ret;
+}
+
+#endif /* defined _MSC_VER && _MSC_VER < 1900 */
diff --git a/src/fmt_fp.c b/src/fmt_fp.c
new file mode 100644
index 000000000..61c7a4cc9
--- /dev/null
+++ b/src/fmt_fp.c
@@ -0,0 +1,371 @@
+/*
+
+Most code in this file originates from musl (src/stdio/vfprintf.c)
+which, just like mruby itself, is licensed under the MIT license.
+
+Copyright (c) 2005-2014 Rich Felker, et al.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include <limits.h>
+#include <string.h>
+#include <stdint.h>
+#include <math.h>
+#include <float.h>
+#include <ctype.h>
+
+#include <mruby.h>
+#include <mruby/string.h>
+
+struct fmt_args {
+ mrb_state *mrb;
+ mrb_value str;
+};
+
+#define MAX(a,b) ((a)>(b) ? (a) : (b))
+#define MIN(a,b) ((a)<(b) ? (a) : (b))
+
+/* Convenient bit representation for modifier flags, which all fall
+ * within 31 codepoints of the space character. */
+
+#define ALT_FORM (1U<<('#'-' '))
+#define ZERO_PAD (1U<<('0'-' '))
+#define LEFT_ADJ (1U<<('-'-' '))
+#define PAD_POS (1U<<(' '-' '))
+#define MARK_POS (1U<<('+'-' '))
+
+static void
+out(struct fmt_args *f, const char *s, size_t l)
+{
+ mrb_str_cat(f->mrb, f->str, s, l);
+}
+
+#define PAD_SIZE 256
+static void
+pad(struct fmt_args *f, char c, int w, int l, int fl)
+{
+ char pad[PAD_SIZE];
+ if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
+ l = w - l;
+ memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l);
+ for (; l >= PAD_SIZE; l -= PAD_SIZE)
+ out(f, pad, PAD_SIZE);
+ out(f, pad, l);
+}
+
+static const char xdigits[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+static char*
+fmt_u(uint32_t x, char *s)
+{
+ for (; x; x /= 10) *--s = '0' + x % 10;
+ return s;
+}
+
+/* Do not override this check. The floating point printing code below
+ * depends on the float.h constants being right. If they are wrong, it
+ * may overflow the stack. */
+#if LDBL_MANT_DIG == 53
+typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
+#endif
+
+static int
+fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
+{
+ uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion
+ + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
+ uint32_t *a, *d, *r, *z;
+ uint32_t i;
+ int e2=0, e, j, l;
+ char buf[9+LDBL_MANT_DIG/4], *s;
+ const char *prefix="-0X+0X 0X-0x+0x 0x";
+ int pl;
+ char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
+
+ pl=1;
+ if (signbit(y)) {
+ y=-y;
+ } else if (fl & MARK_POS) {
+ prefix+=3;
+ } else if (fl & PAD_POS) {
+ prefix+=6;
+ } else prefix++, pl=0;
+
+ if (!isfinite(y)) {
+ const char *ss = (t&32)?"inf":"INF";
+ if (y!=y) ss=(t&32)?"nan":"NAN";
+ pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
+ out(f, prefix, pl);
+ out(f, ss, 3);
+ pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
+ return MAX(w, 3+pl);
+ }
+
+ y = frexp((double)y, &e2) * 2;
+ if (y) e2--;
+
+ if ((t|32)=='a') {
+ long double round = 8.0;
+ int re;
+
+ if (t&32) prefix += 9;
+ pl += 2;
+
+ if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
+ else re=LDBL_MANT_DIG/4-1-p;
+
+ if (re) {
+ while (re--) round*=16;
+ if (*prefix=='-') {
+ y=-y;
+ y-=round;
+ y+=round;
+ y=-y;
+ }
+ else {
+ y+=round;
+ y-=round;
+ }
+ }
+
+ estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
+ if (estr==ebuf) *--estr='0';
+ *--estr = (e2<0 ? '-' : '+');
+ *--estr = t+('p'-'a');
+
+ s=buf;
+ do {
+ int x=(int)y;
+ *s++=xdigits[x]|(t&32);
+ y=16*(y-x);
+ if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
+ } while (y);
+
+ if (p && s-buf-2 < p)
+ l = (p+2) + (ebuf-estr);
+ else
+ l = (s-buf) + (ebuf-estr);
+
+ pad(f, ' ', w, pl+l, fl);
+ out(f, prefix, pl);
+ pad(f, '0', w, pl+l, fl^ZERO_PAD);
+ out(f, buf, s-buf);
+ pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
+ out(f, estr, ebuf-estr);
+ pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
+ return MAX(w, pl+l);
+ }
+ if (p<0) p=6;
+
+ if (y) y *= 268435456.0, e2-=28;
+
+ if (e2<0) a=r=z=big;
+ else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
+
+ do {
+ *z = (uint32_t)y;
+ y = 1000000000*(y-*z++);
+ } while (y);
+
+ while (e2>0) {
+ uint32_t carry=0;
+ int sh=MIN(29,e2);
+ for (d=z-1; d>=a; d--) {
+ uint64_t x = ((uint64_t)*d<<sh)+carry;
+ *d = x % 1000000000;
+ carry = (uint32_t)(x / 1000000000);
+ }
+ if (carry) *--a = carry;
+ while (z>a && !z[-1]) z--;
+ e2-=sh;
+ }
+ while (e2<0) {
+ uint32_t carry=0, *b;
+ int sh=MIN(9,-e2), need=1+(p+LDBL_MANT_DIG/3+8)/9;
+ for (d=a; d<z; d++) {
+ uint32_t rm = *d & ((1<<sh)-1);
+ *d = (*d>>sh) + carry;
+ carry = (1000000000>>sh) * rm;
+ }
+ if (!*a) a++;
+ if (carry) *z++ = carry;
+ /* Avoid (slow!) computation past requested precision */
+ b = (t|32)=='f' ? r : a;
+ if (z-b > need) z = b+need;
+ e2+=sh;
+ }
+
+ if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
+ else e=0;
+
+ /* Perform rounding: j is precision after the radix (possibly neg) */
+ j = p - ((t|32)!='f')*e - ((t|32)=='g' && p);
+ if (j < 9*(z-r-1)) {
+ uint32_t x;
+ /* We avoid C's broken division of negative numbers */
+ d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
+ j += 9*LDBL_MAX_EXP;
+ j %= 9;
+ for (i=10, j++; j<9; i*=10, j++);
+ x = *d % i;
+ /* Are there any significant digits past j? */
+ if (x || d+1!=z) {
+ long double round = 2/LDBL_EPSILON;
+ long double small;
+ if (*d/i & 1) round += 2;
+ if (x<i/2) small=0.5;
+ else if (x==i/2 && d+1==z) small=1.0;
+ else small=1.5;
+ if (pl && *prefix=='-') round*=-1, small*=-1;
+ *d -= x;
+ /* Decide whether to round by probing round+small */
+ if (round+small != round) {
+ *d = *d + i;
+ while (*d > 999999999) {
+ *d--=0;
+ if (d<a) *--a=0;
+ (*d)++;
+ }
+ for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
+ }
+ }
+ if (z>d+1) z=d+1;
+ }
+ for (; z>a && !z[-1]; z--);
+
+ if ((t|32)=='g') {
+ if (!p) p++;
+ if (p>e && e>=-4) {
+ t--;
+ p-=e+1;
+ }
+ else {
+ t-=2;
+ p--;
+ }
+ if (!(fl&ALT_FORM)) {
+ /* Count trailing zeros in last place */
+ if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
+ else j=9;
+ if ((t|32)=='f')
+ p = MIN(p,MAX(0,9*(z-r-1)-j));
+ else
+ p = MIN(p,MAX(0,9*(z-r-1)+e-j));
+ }
+ }
+ l = 1 + p + (p || (fl&ALT_FORM));
+ if ((t|32)=='f') {
+ if (e>0) l+=e;
+ }
+ else {
+ estr=fmt_u(e<0 ? -e : e, ebuf);
+ while(ebuf-estr<2) *--estr='0';
+ *--estr = (e<0 ? '-' : '+');
+ *--estr = t;
+ l += ebuf-estr;
+ }
+
+ pad(f, ' ', w, pl+l, fl);
+ out(f, prefix, pl);
+ pad(f, '0', w, pl+l, fl^ZERO_PAD);
+
+ if ((t|32)=='f') {
+ if (a>r) a=r;
+ for (d=a; d<=r; d++) {
+ char *ss = fmt_u(*d, buf+9);
+ if (d!=a) while (ss>buf) *--ss='0';
+ else if (ss==buf+9) *--ss='0';
+ out(f, ss, buf+9-ss);
+ }
+ if (p || (fl&ALT_FORM)) out(f, ".", 1);
+ for (; d<z && p>0; d++, p-=9) {
+ char *ss = fmt_u(*d, buf+9);
+ while (ss>buf) *--ss='0';
+ out(f, ss, MIN(9,p));
+ }
+ pad(f, '0', p+9, 9, 0);
+ }
+ else {
+ if (z<=a) z=a+1;
+ for (d=a; d<z && p>=0; d++) {
+ char *ss = fmt_u(*d, buf+9);
+ if (ss==buf+9) *--ss='0';
+ if (d!=a) while (ss>buf) *--ss='0';
+ else {
+ out(f, ss++, 1);
+ if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
+ }
+ out(f, ss, MIN(buf+9-ss, p));
+ p -= buf+9-ss;
+ }
+ pad(f, '0', p+18, 18, 0);
+ out(f, estr, ebuf-estr);
+ }
+
+ pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
+
+ return MAX(w, pl+l);
+}
+
+static int
+fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
+{
+ int p;
+
+ if (*fmt != '%') {
+ return -1;
+ }
+ ++fmt;
+
+ if (*fmt == '.') {
+ ++fmt;
+ for (p = 0; ISDIGIT(*fmt); ++fmt) {
+ p = 10 * p + (*fmt - '0');
+ }
+ }
+ else {
+ p = -1;
+ }
+
+ switch (*fmt) {
+ case 'e': case 'f': case 'g': case 'a':
+ case 'E': case 'F': case 'G': case 'A':
+ return fmt_fp(f, flo, 0, p, 0, *fmt);
+ default:
+ return -1;
+ }
+}
+
+mrb_value
+mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
+{
+ struct fmt_args f;
+
+ f.mrb = mrb;
+ f.str = mrb_str_buf_new(mrb, 24);
+ if (fmt_core(&f, fmt, mrb_float(flo)) < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
+ }
+ return f.str;
+}
diff --git a/src/gc.c b/src/gc.c
index 6eb9808e3..02e058f88 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -6,16 +6,17 @@
#include <string.h>
#include <stdlib.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/data.h"
-#include "mruby/hash.h"
-#include "mruby/proc.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/gc.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/hash.h>
+#include <mruby/proc.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/gc.h>
+#include <mruby/error.h>
/*
= Tri-color Incremental Garbage Collection
@@ -62,12 +63,13 @@
== Write Barrier
- mruby implementer and C extension library writer must write a write
- barrier when writing a pointer to an object on object's field.
- Two different write barrier are available:
+ mruby implementer and C extension library writer must insert a write
+ barrier when updating a reference from a field of an object.
+ When updating a reference from a field of object A to object B,
+ two different types of write barrier are available:
- * mrb_field_write_barrier
- * mrb_write_barrier
+ * mrb_field_write_barrier - target B object for a mark.
+ * mrb_write_barrier - target A object for a mark.
== Generational Mode
@@ -107,6 +109,11 @@ typedef struct {
struct RRange range;
struct RData data;
struct RProc proc;
+ struct RException exc;
+#ifdef MRB_WORD_BOXING
+ struct RFloat floatv;
+ struct RCptr cptr;
+#endif
} as;
} RVALUE;
@@ -129,7 +136,7 @@ gettimeofday_time(void)
#define GC_INVOKE_TIME_REPORT(with) do {\
fprintf(stderr, "%s\n", with);\
fprintf(stderr, "gc_invoke: %19.3f\n", gettimeofday_time() - program_invoke_time);\
- fprintf(stderr, "is_generational: %d\n", is_generational(mrb));\
+ fprintf(stderr, "is_generational: %d\n", is_generational(gc));\
fprintf(stderr, "is_major_gc: %d\n", is_major_gc(mrb));\
} while(0)
@@ -140,10 +147,10 @@ gettimeofday_time(void)
#define GC_TIME_STOP_AND_REPORT do {\
gc_time = gettimeofday_time() - gc_time;\
gc_total_time += gc_time;\
- fprintf(stderr, "gc_state: %d\n", mrb->gc_state);\
- fprintf(stderr, "live: %zu\n", mrb->live);\
- fprintf(stderr, "majorgc_old_threshold: %zu\n", mrb->majorgc_old_threshold);\
- fprintf(stderr, "gc_threshold: %zu\n", mrb->gc_threshold);\
+ fprintf(stderr, "gc_state: %d\n", gc->state);\
+ fprintf(stderr, "live: %zu\n", gc->live);\
+ fprintf(stderr, "majorgc_old_threshold: %zu\n", gc->majorgc_old_threshold);\
+ fprintf(stderr, "gc_threshold: %zu\n", gc->threshold);\
fprintf(stderr, "gc_time: %30.20f\n", gc_time);\
fprintf(stderr, "gc_total_time: %30.20f\n\n", gc_total_time);\
} while(0)
@@ -159,59 +166,82 @@ gettimeofday_time(void)
#define DEBUG(x)
#endif
-#define GC_STEP_SIZE 1024
+#ifndef MRB_HEAP_PAGE_SIZE
+#define MRB_HEAP_PAGE_SIZE 1024
+#endif
+#define GC_STEP_SIZE 1024
-void*
+/* white: 011, black: 100, gray: 000 */
+#define GC_GRAY 0
+#define GC_WHITE_A 1
+#define GC_WHITE_B (1 << 1)
+#define GC_BLACK (1 << 2)
+#define GC_WHITES (GC_WHITE_A | GC_WHITE_B)
+#define GC_COLOR_MASK 7
+
+#define paint_gray(o) ((o)->color = GC_GRAY)
+#define paint_black(o) ((o)->color = GC_BLACK)
+#define paint_white(o) ((o)->color = GC_WHITES)
+#define paint_partial_white(s, o) ((o)->color = (s)->current_white_part)
+#define is_gray(o) ((o)->color == GC_GRAY)
+#define is_white(o) ((o)->color & GC_WHITES)
+#define is_black(o) ((o)->color & GC_BLACK)
+#define flip_white_part(s) ((s)->current_white_part = other_white_part(s))
+#define other_white_part(s) ((s)->current_white_part ^ GC_WHITES)
+#define is_dead(s, o) (((o)->color & other_white_part(s) & GC_WHITES) || (o)->tt == MRB_TT_FREE)
+
+#define objects(p) ((RVALUE *)p->objects)
+
+MRB_API void*
mrb_realloc_simple(mrb_state *mrb, void *p, size_t len)
{
void *p2;
- p2 = (mrb->allocf)(mrb, p, len, mrb->ud);
- if (!p2 && len > 0 && mrb->heaps) {
+ p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud);
+ if (!p2 && len > 0 && mrb->gc.heaps) {
mrb_full_gc(mrb);
- p2 = (mrb->allocf)(mrb, p, len, mrb->ud);
+ p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud);
}
return p2;
}
-
-void*
+MRB_API void*
mrb_realloc(mrb_state *mrb, void *p, size_t len)
{
void *p2;
p2 = mrb_realloc_simple(mrb, p, len);
if (!p2 && len) {
- if (mrb->out_of_memory) {
+ if (mrb->gc.out_of_memory) {
/* mrb_panic(mrb); */
}
else {
- mrb->out_of_memory = TRUE;
+ mrb->gc.out_of_memory = TRUE;
mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
}
}
else {
- mrb->out_of_memory = FALSE;
+ mrb->gc.out_of_memory = FALSE;
}
return p2;
}
-void*
+MRB_API void*
mrb_malloc(mrb_state *mrb, size_t len)
{
return mrb_realloc(mrb, 0, len);
}
-void*
+MRB_API void*
mrb_malloc_simple(mrb_state *mrb, size_t len)
{
return mrb_realloc_simple(mrb, 0, len);
}
-void*
+MRB_API void*
mrb_calloc(mrb_state *mrb, size_t nelem, size_t len)
{
void *p;
@@ -220,11 +250,9 @@ mrb_calloc(mrb_state *mrb, size_t nelem, size_t len)
nelem <= SIZE_MAX / len) {
size_t size;
size = nelem * len;
- p = mrb_realloc(mrb, 0, size);
+ p = mrb_malloc(mrb, size);
- if (p) {
- memset(p, 0, size);
- }
+ memset(p, 0, size);
}
else {
p = NULL;
@@ -233,107 +261,104 @@ mrb_calloc(mrb_state *mrb, size_t nelem, size_t len)
return p;
}
-void
+MRB_API void
mrb_free(mrb_state *mrb, void *p)
{
- (mrb->allocf)(mrb, p, 0, mrb->ud);
+ (mrb->allocf)(mrb, p, 0, mrb->allocf_ud);
}
-#ifndef MRB_HEAP_PAGE_SIZE
-#define MRB_HEAP_PAGE_SIZE 1024
-#endif
-
-struct heap_page {
- struct RBasic *freelist;
- struct heap_page *prev;
- struct heap_page *next;
- struct heap_page *free_next;
- struct heap_page *free_prev;
- mrb_bool old:1;
- RVALUE objects[MRB_HEAP_PAGE_SIZE];
-};
+MRB_API mrb_bool
+mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) {
+ return is_dead(&mrb->gc, object);
+}
static void
-link_heap_page(mrb_state *mrb, struct heap_page *page)
+link_heap_page(mrb_gc *gc, mrb_heap_page *page)
{
- page->next = mrb->heaps;
- if (mrb->heaps)
- mrb->heaps->prev = page;
- mrb->heaps = page;
+ page->next = gc->heaps;
+ if (gc->heaps)
+ gc->heaps->prev = page;
+ gc->heaps = page;
}
static void
-unlink_heap_page(mrb_state *mrb, struct heap_page *page)
+unlink_heap_page(mrb_gc *gc, mrb_heap_page *page)
{
if (page->prev)
page->prev->next = page->next;
if (page->next)
page->next->prev = page->prev;
- if (mrb->heaps == page)
- mrb->heaps = page->next;
+ if (gc->heaps == page)
+ gc->heaps = page->next;
page->prev = NULL;
page->next = NULL;
}
static void
-link_free_heap_page(mrb_state *mrb, struct heap_page *page)
+link_free_heap_page(mrb_gc *gc, mrb_heap_page *page)
{
- page->free_next = mrb->free_heaps;
- if (mrb->free_heaps) {
- mrb->free_heaps->free_prev = page;
+ page->free_next = gc->free_heaps;
+ if (gc->free_heaps) {
+ gc->free_heaps->free_prev = page;
}
- mrb->free_heaps = page;
+ gc->free_heaps = page;
}
static void
-unlink_free_heap_page(mrb_state *mrb, struct heap_page *page)
+unlink_free_heap_page(mrb_gc *gc, mrb_heap_page *page)
{
if (page->free_prev)
page->free_prev->free_next = page->free_next;
if (page->free_next)
page->free_next->free_prev = page->free_prev;
- if (mrb->free_heaps == page)
- mrb->free_heaps = page->free_next;
+ if (gc->free_heaps == page)
+ gc->free_heaps = page->free_next;
page->free_prev = NULL;
page->free_next = NULL;
}
static void
-add_heap(mrb_state *mrb)
+add_heap(mrb_state *mrb, mrb_gc *gc)
{
- struct heap_page *page = (struct heap_page *)mrb_calloc(mrb, 1, sizeof(struct heap_page));
+ mrb_heap_page *page = (mrb_heap_page *)mrb_calloc(mrb, 1, sizeof(mrb_heap_page) + MRB_HEAP_PAGE_SIZE * sizeof(RVALUE));
RVALUE *p, *e;
struct RBasic *prev = NULL;
- for (p = page->objects, e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
+ for (p = objects(page), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
p->as.free.tt = MRB_TT_FREE;
p->as.free.next = prev;
prev = &p->as.basic;
}
page->freelist = prev;
- link_heap_page(mrb, page);
- link_free_heap_page(mrb, page);
+ link_heap_page(gc, page);
+ link_free_heap_page(gc, page);
}
#define DEFAULT_GC_INTERVAL_RATIO 200
#define DEFAULT_GC_STEP_RATIO 200
#define DEFAULT_MAJOR_GC_INC_RATIO 200
-#define is_generational(mrb) ((mrb)->is_generational_gc_mode)
-#define is_major_gc(mrb) (is_generational(mrb) && (mrb)->gc_full)
-#define is_minor_gc(mrb) (is_generational(mrb) && !(mrb)->gc_full)
+#define is_generational(gc) ((gc)->generational)
+#define is_major_gc(gc) (is_generational(gc) && (gc)->full)
+#define is_minor_gc(gc) (is_generational(gc) && !(gc)->full)
void
-mrb_init_heap(mrb_state *mrb)
+mrb_gc_init(mrb_state *mrb, mrb_gc *gc)
{
- mrb->heaps = NULL;
- mrb->free_heaps = NULL;
- add_heap(mrb);
- mrb->gc_interval_ratio = DEFAULT_GC_INTERVAL_RATIO;
- mrb->gc_step_ratio = DEFAULT_GC_STEP_RATIO;
+#ifndef MRB_GC_FIXED_ARENA
+ gc->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE);
+ gc->arena_capa = MRB_GC_ARENA_SIZE;
+#endif
+
+ gc->current_white_part = GC_WHITE_A;
+ gc->heaps = NULL;
+ gc->free_heaps = NULL;
+ add_heap(mrb, gc);
+ gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO;
+ gc->step_ratio = DEFAULT_GC_STEP_RATIO;
#ifndef MRB_GC_TURN_OFF_GENERATIONAL
- mrb->is_generational_gc_mode = TRUE;
- mrb->gc_full = TRUE;
+ gc->generational = TRUE;
+ gc->full = TRUE;
#endif
#ifdef GC_PROFILE
@@ -344,16 +369,16 @@ mrb_init_heap(mrb_state *mrb)
static void obj_free(mrb_state *mrb, struct RBasic *obj);
void
-mrb_free_heap(mrb_state *mrb)
+free_heap(mrb_state *mrb, mrb_gc *gc)
{
- struct heap_page *page = mrb->heaps;
- struct heap_page *tmp;
+ mrb_heap_page *page = gc->heaps;
+ mrb_heap_page *tmp;
RVALUE *p, *e;
while (page) {
tmp = page;
page = page->next;
- for (p = tmp->objects, e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
+ for (p = objects(tmp), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) {
if (p->as.free.tt != MRB_TT_FREE)
obj_free(mrb, &p->as.basic);
}
@@ -361,65 +386,123 @@ mrb_free_heap(mrb_state *mrb)
}
}
+void
+mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc)
+{
+ free_heap(mrb, gc);
+#ifndef MRB_GC_FIXED_ARENA
+ mrb_free(mrb, gc->arena);
+#endif
+}
+
static void
-gc_protect(mrb_state *mrb, struct RBasic *p)
+gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p)
{
#ifdef MRB_GC_FIXED_ARENA
- if (mrb->arena_idx >= MRB_GC_ARENA_SIZE) {
+ if (gc->arena_idx >= MRB_GC_ARENA_SIZE) {
/* arena overflow error */
- mrb->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */
+ gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */
mrb_raise(mrb, E_RUNTIME_ERROR, "arena overflow error");
}
#else
- if (mrb->arena_idx >= mrb->arena_capa) {
+ if (gc->arena_idx >= gc->arena_capa) {
/* extend arena */
- mrb->arena_capa = (int)(mrb->arena_capa * 1.5);
- mrb->arena = (struct RBasic**)mrb_realloc(mrb, mrb->arena, sizeof(struct RBasic*)*mrb->arena_capa);
+ gc->arena_capa = (int)(gc->arena_capa * 1.5);
+ gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*gc->arena_capa);
}
#endif
- mrb->arena[mrb->arena_idx++] = p;
+ gc->arena[gc->arena_idx++] = p;
}
-void
+/* mrb_gc_protect() leaves the object in the arena */
+MRB_API void
mrb_gc_protect(mrb_state *mrb, mrb_value obj)
{
- if (mrb_special_const_p(obj)) return;
- gc_protect(mrb, mrb_basic_ptr(obj));
+ if (mrb_immediate_p(obj)) return;
+ gc_protect(mrb, &mrb->gc, mrb_basic_ptr(obj));
+}
+
+#define GC_ROOT_NAME "_gc_root_"
+
+/* mrb_gc_register() keeps the object from GC.
+
+ Register your object when it's exported to C world,
+ without reference from Ruby world, e.g. callback
+ arguments. Don't forget to remove the obejct using
+ mrb_gc_unregister, otherwise your object will leak.
+*/
+
+MRB_API void
+mrb_gc_register(mrb_state *mrb, mrb_value obj)
+{
+ mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME);
+ mrb_value table = mrb_gv_get(mrb, root);
+
+ if (mrb_nil_p(table) || mrb_type(table) != MRB_TT_ARRAY) {
+ table = mrb_ary_new(mrb);
+ mrb_gv_set(mrb, root, table);
+ }
+ mrb_ary_push(mrb, table, obj);
+}
+
+/* mrb_gc_unregister() removes the object from GC root. */
+MRB_API void
+mrb_gc_unregister(mrb_state *mrb, mrb_value obj)
+{
+ mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME);
+ mrb_value table = mrb_gv_get(mrb, root);
+ struct RArray *a;
+ mrb_int i, j;
+
+ if (mrb_nil_p(table)) return;
+ if (mrb_type(table) != MRB_TT_ARRAY) {
+ mrb_gv_set(mrb, root, mrb_nil_value());
+ return;
+ }
+ a = mrb_ary_ptr(table);
+ mrb_ary_modify(mrb, a);
+ for (i=j=0; i<a->len; i++) {
+ if (!mrb_obj_eq(mrb, a->ptr[i], obj)) {
+ a->ptr[j++] = a->ptr[i];
+ }
+ }
+ a->len = j;
}
-struct RBasic*
+MRB_API struct RBasic*
mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls)
{
struct RBasic *p;
static const RVALUE RVALUE_zero = { { { MRB_TT_FALSE } } };
+ mrb_gc *gc = &mrb->gc;
#ifdef MRB_GC_STRESS
mrb_full_gc(mrb);
#endif
- if (mrb->gc_threshold < mrb->live) {
+ if (gc->threshold < gc->live) {
mrb_incremental_gc(mrb);
}
- if (mrb->free_heaps == NULL) {
- add_heap(mrb);
+ if (gc->free_heaps == NULL) {
+ add_heap(mrb, gc);
}
- p = mrb->free_heaps->freelist;
- mrb->free_heaps->freelist = ((struct free_obj*)p)->next;
- if (mrb->free_heaps->freelist == NULL) {
- unlink_free_heap_page(mrb, mrb->free_heaps);
+ p = gc->free_heaps->freelist;
+ gc->free_heaps->freelist = ((struct free_obj*)p)->next;
+ if (gc->free_heaps->freelist == NULL) {
+ unlink_free_heap_page(gc, gc->free_heaps);
}
- mrb->live++;
- gc_protect(mrb, p);
+ gc->live++;
+ gc_protect(mrb, gc, p);
*(RVALUE *)p = RVALUE_zero;
p->tt = ttype;
p->c = cls;
- paint_partial_white(mrb, p);
+ paint_partial_white(gc, p);
return p;
}
static inline void
-add_gray_list(mrb_state *mrb, struct RBasic *obj)
+add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
{
#ifdef MRB_GC_STRESS
if (obj->tt > MRB_TT_MAXDEFINE) {
@@ -427,8 +510,8 @@ add_gray_list(mrb_state *mrb, struct RBasic *obj)
}
#endif
paint_gray(obj);
- obj->gcnext = mrb->gray_list;
- mrb->gray_list = obj;
+ obj->gcnext = gc->gray_list;
+ gc->gray_list = obj;
}
static void
@@ -485,15 +568,20 @@ mark_context(mrb_state *mrb, struct mrb_context *c)
}
static void
-gc_mark_children(mrb_state *mrb, struct RBasic *obj)
+gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
{
mrb_assert(is_gray(obj));
paint_black(obj);
- mrb->gray_list = obj->gcnext;
+ gc->gray_list = obj->gcnext;
mrb_gc_mark(mrb, (struct RBasic*)obj->c);
switch (obj->tt) {
case MRB_TT_ICLASS:
- mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super);
+ {
+ struct RClass *c = (struct RClass*)obj;
+ if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN))
+ mrb_gc_mark_mt(mrb, c);
+ mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super);
+ }
break;
case MRB_TT_CLASS:
@@ -509,6 +597,7 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj)
case MRB_TT_OBJECT:
case MRB_TT_DATA:
+ case MRB_TT_EXCEPTION:
mrb_gc_mark_iv(mrb, (struct RObject*)obj);
break;
@@ -526,9 +615,9 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj)
struct REnv *e = (struct REnv*)obj;
if (!MRB_ENV_STACK_SHARED_P(e)) {
- int i, len;
+ mrb_int i, len;
- len = (int)MRB_ENV_STACK_LEN(e);
+ len = MRB_ENV_STACK_LEN(e);
for (i=0; i<len; i++) {
mrb_gc_mark_value(mrb, e->stack[i]);
}
@@ -579,13 +668,13 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj)
}
}
-void
+MRB_API void
mrb_gc_mark(mrb_state *mrb, struct RBasic *obj)
{
if (obj == 0) return;
if (!is_white(obj)) return;
mrb_assert((obj)->tt != MRB_TT_FREE);
- add_gray_list(mrb, obj);
+ add_gray_list(mrb, &mrb->gc, obj);
}
static void
@@ -608,6 +697,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
#endif
case MRB_TT_OBJECT:
+ case MRB_TT_EXCEPTION:
mrb_gc_free_iv(mrb, (struct RObject*)obj);
break;
@@ -617,7 +707,10 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
mrb_gc_free_mt(mrb, (struct RClass*)obj);
mrb_gc_free_iv(mrb, (struct RObject*)obj);
break;
-
+ case MRB_TT_ICLASS:
+ if (MRB_FLAG_TEST(obj, MRB_FLAG_IS_ORIGIN))
+ mrb_gc_free_mt(mrb, (struct RClass*)obj);
+ break;
case MRB_TT_ENV:
{
struct REnv *e = (struct REnv*)obj;
@@ -639,7 +732,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
break;
case MRB_TT_ARRAY:
- if (obj->flags & MRB_ARY_SHARED)
+ if (ARY_SHARED_P(obj))
mrb_ary_decref(mrb, ((struct RArray*)obj)->aux.shared);
else
mrb_free(mrb, ((struct RArray*)obj)->ptr);
@@ -685,19 +778,19 @@ obj_free(mrb_state *mrb, struct RBasic *obj)
}
static void
-root_scan_phase(mrb_state *mrb)
+root_scan_phase(mrb_state *mrb, mrb_gc *gc)
{
size_t i, e;
- if (!is_minor_gc(mrb)) {
- mrb->gray_list = NULL;
- mrb->atomic_gray_list = NULL;
+ if (!is_minor_gc(gc)) {
+ gc->gray_list = NULL;
+ gc->atomic_gray_list = NULL;
}
mrb_gc_mark_gv(mrb);
/* mark arena */
- for (i=0,e=mrb->arena_idx; i<e; i++) {
- mrb_gc_mark(mrb, mrb->arena[i]);
+ for (i=0,e=gc->arena_idx; i<e; i++) {
+ mrb_gc_mark(mrb, gc->arena[i]);
}
/* mark class hierarchy */
mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class);
@@ -718,11 +811,11 @@ root_scan_phase(mrb_state *mrb)
}
static size_t
-gc_gray_mark(mrb_state *mrb, struct RBasic *obj)
+gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
{
size_t children = 0;
- gc_mark_children(mrb, obj);
+ gc_mark_children(mrb, gc, obj);
switch (obj->tt) {
case MRB_TT_ICLASS:
@@ -743,6 +836,7 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj)
case MRB_TT_OBJECT:
case MRB_TT_DATA:
+ case MRB_TT_EXCEPTION:
children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj);
break;
@@ -800,68 +894,68 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj)
static void
-gc_mark_gray_list(mrb_state *mrb) {
- while (mrb->gray_list) {
- if (is_gray(mrb->gray_list))
- gc_mark_children(mrb, mrb->gray_list);
+gc_mark_gray_list(mrb_state *mrb, mrb_gc *gc) {
+ while (gc->gray_list) {
+ if (is_gray(gc->gray_list))
+ gc_mark_children(mrb, gc, gc->gray_list);
else
- mrb->gray_list = mrb->gray_list->gcnext;
+ gc->gray_list = gc->gray_list->gcnext;
}
}
static size_t
-incremental_marking_phase(mrb_state *mrb, size_t limit)
+incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
{
size_t tried_marks = 0;
- while (mrb->gray_list && tried_marks < limit) {
- tried_marks += gc_gray_mark(mrb, mrb->gray_list);
+ while (gc->gray_list && tried_marks < limit) {
+ tried_marks += gc_gray_mark(mrb, gc, gc->gray_list);
}
return tried_marks;
}
static void
-final_marking_phase(mrb_state *mrb)
+final_marking_phase(mrb_state *mrb, mrb_gc *gc)
{
mark_context_stack(mrb, mrb->root_c);
- gc_mark_gray_list(mrb);
- mrb_assert(mrb->gray_list == NULL);
- mrb->gray_list = mrb->atomic_gray_list;
- mrb->atomic_gray_list = NULL;
- gc_mark_gray_list(mrb);
- mrb_assert(mrb->gray_list == NULL);
+ gc_mark_gray_list(mrb, gc);
+ mrb_assert(gc->gray_list == NULL);
+ gc->gray_list = gc->atomic_gray_list;
+ gc->atomic_gray_list = NULL;
+ gc_mark_gray_list(mrb, gc);
+ mrb_assert(gc->gray_list == NULL);
}
static void
-prepare_incremental_sweep(mrb_state *mrb)
+prepare_incremental_sweep(mrb_state *mrb, mrb_gc *gc)
{
- mrb->gc_state = GC_STATE_SWEEP;
- mrb->sweeps = mrb->heaps;
- mrb->gc_live_after_mark = mrb->live;
+ gc->state = MRB_GC_STATE_SWEEP;
+ gc->sweeps = gc->heaps;
+ gc->live_after_mark = gc->live;
}
static size_t
-incremental_sweep_phase(mrb_state *mrb, size_t limit)
+incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
{
- struct heap_page *page = mrb->sweeps;
+ mrb_heap_page *page = gc->sweeps;
size_t tried_sweep = 0;
while (page && (tried_sweep < limit)) {
- RVALUE *p = page->objects;
+ RVALUE *p = objects(page);
RVALUE *e = p + MRB_HEAP_PAGE_SIZE;
size_t freed = 0;
mrb_bool dead_slot = TRUE;
- int full = (page->freelist == NULL);
+ mrb_bool full = (page->freelist == NULL);
- if (is_minor_gc(mrb) && page->old) {
+ if (is_minor_gc(gc) && page->old) {
/* skip a slot which doesn't contain any young object */
p = e;
dead_slot = FALSE;
}
while (p<e) {
- if (is_dead(mrb, &p->as.basic)) {
+ if (is_dead(gc, &p->as.basic)) {
if (p->as.basic.tt != MRB_TT_FREE) {
obj_free(mrb, &p->as.basic);
p->as.free.next = page->freelist;
@@ -870,8 +964,8 @@ incremental_sweep_phase(mrb_state *mrb, size_t limit)
}
}
else {
- if (!is_generational(mrb))
- paint_partial_white(mrb, &p->as.basic); /* next gc target */
+ if (!is_generational(gc))
+ paint_partial_white(gc, &p->as.basic); /* next gc target */
dead_slot = 0;
}
p++;
@@ -879,54 +973,54 @@ incremental_sweep_phase(mrb_state *mrb, size_t limit)
/* free dead slot */
if (dead_slot && freed < MRB_HEAP_PAGE_SIZE) {
- struct heap_page *next = page->next;
+ mrb_heap_page *next = page->next;
- unlink_heap_page(mrb, page);
- unlink_free_heap_page(mrb, page);
+ unlink_heap_page(gc, page);
+ unlink_free_heap_page(gc, page);
mrb_free(mrb, page);
page = next;
}
else {
if (full && freed > 0) {
- link_free_heap_page(mrb, page);
+ link_free_heap_page(gc, page);
}
- if (page->freelist == NULL && is_minor_gc(mrb))
+ if (page->freelist == NULL && is_minor_gc(gc))
page->old = TRUE;
else
page->old = FALSE;
page = page->next;
}
tried_sweep += MRB_HEAP_PAGE_SIZE;
- mrb->live -= freed;
- mrb->gc_live_after_mark -= freed;
+ gc->live -= freed;
+ gc->live_after_mark -= freed;
}
- mrb->sweeps = page;
+ gc->sweeps = page;
return tried_sweep;
}
static size_t
-incremental_gc(mrb_state *mrb, size_t limit)
+incremental_gc(mrb_state *mrb, mrb_gc *gc, size_t limit)
{
- switch (mrb->gc_state) {
- case GC_STATE_NONE:
- root_scan_phase(mrb);
- mrb->gc_state = GC_STATE_MARK;
- flip_white_part(mrb);
+ switch (gc->state) {
+ case MRB_GC_STATE_ROOT:
+ root_scan_phase(mrb, gc);
+ gc->state = MRB_GC_STATE_MARK;
+ flip_white_part(gc);
return 0;
- case GC_STATE_MARK:
- if (mrb->gray_list) {
- return incremental_marking_phase(mrb, limit);
+ case MRB_GC_STATE_MARK:
+ if (gc->gray_list) {
+ return incremental_marking_phase(mrb, gc, limit);
}
else {
- final_marking_phase(mrb);
- prepare_incremental_sweep(mrb);
+ final_marking_phase(mrb, gc);
+ prepare_incremental_sweep(mrb, gc);
return 0;
}
- case GC_STATE_SWEEP: {
+ case MRB_GC_STATE_SWEEP: {
size_t tried_sweep = 0;
- tried_sweep = incremental_sweep_phase(mrb, limit);
+ tried_sweep = incremental_sweep_phase(mrb, gc, limit);
if (tried_sweep == 0)
- mrb->gc_state = GC_STATE_NONE;
+ gc->state = MRB_GC_STATE_ROOT;
return tried_sweep;
}
default:
@@ -937,79 +1031,81 @@ incremental_gc(mrb_state *mrb, size_t limit)
}
static void
-incremental_gc_until(mrb_state *mrb, enum gc_state to_state)
+incremental_gc_until(mrb_state *mrb, mrb_gc *gc, mrb_gc_state to_state)
{
do {
- incremental_gc(mrb, ~0);
- } while (mrb->gc_state != to_state);
+ incremental_gc(mrb, gc, SIZE_MAX);
+ } while (gc->state != to_state);
}
static void
-incremental_gc_step(mrb_state *mrb)
+incremental_gc_step(mrb_state *mrb, mrb_gc *gc)
{
size_t limit = 0, result = 0;
- limit = (GC_STEP_SIZE/100) * mrb->gc_step_ratio;
+ limit = (GC_STEP_SIZE/100) * gc->step_ratio;
while (result < limit) {
- result += incremental_gc(mrb, limit);
- if (mrb->gc_state == GC_STATE_NONE)
+ result += incremental_gc(mrb, gc, limit);
+ if (gc->state == MRB_GC_STATE_ROOT)
break;
}
- mrb->gc_threshold = mrb->live + GC_STEP_SIZE;
+ gc->threshold = gc->live + GC_STEP_SIZE;
}
static void
-clear_all_old(mrb_state *mrb)
+clear_all_old(mrb_state *mrb, mrb_gc *gc)
{
- mrb_bool origin_mode = mrb->is_generational_gc_mode;
+ mrb_bool origin_mode = gc->generational;
- mrb_assert(is_generational(mrb));
- if (is_major_gc(mrb)) {
+ mrb_assert(is_generational(gc));
+ if (is_major_gc(gc)) {
/* finish the half baked GC */
- incremental_gc_until(mrb, GC_STATE_NONE);
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
}
/* Sweep the dead objects, then reset all the live objects
* (including all the old objects, of course) to white. */
- mrb->is_generational_gc_mode = FALSE;
- prepare_incremental_sweep(mrb);
- incremental_gc_until(mrb, GC_STATE_NONE);
- mrb->is_generational_gc_mode = origin_mode;
+ gc->generational = FALSE;
+ prepare_incremental_sweep(mrb, gc);
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
+ gc->generational = origin_mode;
- /* The gray objects has already been painted as white */
- mrb->atomic_gray_list = mrb->gray_list = NULL;
+ /* The gray objects have already been painted as white */
+ gc->atomic_gray_list = gc->gray_list = NULL;
}
-void
+MRB_API void
mrb_incremental_gc(mrb_state *mrb)
{
- if (mrb->gc_disabled) return;
+ mrb_gc *gc = &mrb->gc;
+
+ if (gc->disabled) return;
GC_INVOKE_TIME_REPORT("mrb_incremental_gc()");
GC_TIME_START;
- if (is_minor_gc(mrb)) {
- incremental_gc_until(mrb, GC_STATE_NONE);
+ if (is_minor_gc(gc)) {
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
}
else {
- incremental_gc_step(mrb);
+ incremental_gc_step(mrb, gc);
}
- if (mrb->gc_state == GC_STATE_NONE) {
- mrb_assert(mrb->live >= mrb->gc_live_after_mark);
- mrb->gc_threshold = (mrb->gc_live_after_mark/100) * mrb->gc_interval_ratio;
- if (mrb->gc_threshold < GC_STEP_SIZE) {
- mrb->gc_threshold = GC_STEP_SIZE;
+ if (gc->state == MRB_GC_STATE_ROOT) {
+ mrb_assert(gc->live >= gc->live_after_mark);
+ gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
+ if (gc->threshold < GC_STEP_SIZE) {
+ gc->threshold = GC_STEP_SIZE;
}
- if (is_major_gc(mrb)) {
- mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
- mrb->gc_full = FALSE;
+ if (is_major_gc(gc)) {
+ gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
+ gc->full = FALSE;
}
- else if (is_minor_gc(mrb)) {
- if (mrb->live > mrb->majorgc_old_threshold) {
- clear_all_old(mrb);
- mrb->gc_full = TRUE;
+ else if (is_minor_gc(gc)) {
+ if (gc->live > gc->majorgc_old_threshold) {
+ clear_all_old(mrb, gc);
+ gc->full = TRUE;
}
}
}
@@ -1018,64 +1114,69 @@ mrb_incremental_gc(mrb_state *mrb)
}
/* Perform a full gc cycle */
-void
+MRB_API void
mrb_full_gc(mrb_state *mrb)
{
- if (mrb->gc_disabled) return;
+ mrb_gc *gc = &mrb->gc;
+
+ if (gc->disabled) return;
+
GC_INVOKE_TIME_REPORT("mrb_full_gc()");
GC_TIME_START;
- if (is_generational(mrb)) {
+ if (is_generational(gc)) {
/* clear all the old objects back to young */
- clear_all_old(mrb);
- mrb->gc_full = TRUE;
+ clear_all_old(mrb, gc);
+ gc->full = TRUE;
}
- else if (mrb->gc_state != GC_STATE_NONE) {
+ else if (gc->state != MRB_GC_STATE_ROOT) {
/* finish half baked GC cycle */
- incremental_gc_until(mrb, GC_STATE_NONE);
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
}
- incremental_gc_until(mrb, GC_STATE_NONE);
- mrb->gc_threshold = (mrb->gc_live_after_mark/100) * mrb->gc_interval_ratio;
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
+ gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio;
- if (is_generational(mrb)) {
- mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
- mrb->gc_full = FALSE;
+ if (is_generational(gc)) {
+ gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
+ gc->full = FALSE;
}
GC_TIME_STOP_AND_REPORT;
}
-void
+MRB_API void
mrb_garbage_collect(mrb_state *mrb)
{
mrb_full_gc(mrb);
}
-int
+MRB_API int
mrb_gc_arena_save(mrb_state *mrb)
{
- return mrb->arena_idx;
+ return mrb->gc.arena_idx;
}
-void
+MRB_API void
mrb_gc_arena_restore(mrb_state *mrb, int idx)
{
+ mrb_gc *gc = &mrb->gc;
+
#ifndef MRB_GC_FIXED_ARENA
- int capa = mrb->arena_capa;
+ int capa = gc->arena_capa;
if (idx < capa / 2) {
capa = (int)(capa * 0.66);
if (capa < MRB_GC_ARENA_SIZE) {
capa = MRB_GC_ARENA_SIZE;
}
- if (capa != mrb->arena_capa) {
- mrb->arena = (struct RBasic**)mrb_realloc(mrb, mrb->arena, sizeof(struct RBasic*)*capa);
- mrb->arena_capa = capa;
+ if (capa != gc->arena_capa) {
+ gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa);
+ gc->arena_capa = capa;
}
}
#endif
- mrb->arena_idx = idx;
+ gc->arena_idx = idx;
}
/*
@@ -1083,21 +1184,23 @@ mrb_gc_arena_restore(mrb_state *mrb, int idx)
* Paint obj(Black) -> value(White) to obj(Black) -> value(Gray).
*/
-void
+MRB_API void
mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value)
{
+ mrb_gc *gc = &mrb->gc;
+
if (!is_black(obj)) return;
if (!is_white(value)) return;
- mrb_assert(!is_dead(mrb, value) && !is_dead(mrb, obj));
- mrb_assert(is_generational(mrb) || mrb->gc_state != GC_STATE_NONE);
+ mrb_assert(gc->state == MRB_GC_STATE_MARK || (!is_dead(gc, value) && !is_dead(gc, obj)));
+ mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT);
- if (is_generational(mrb) || mrb->gc_state == GC_STATE_MARK) {
- add_gray_list(mrb, value);
+ if (is_generational(gc) || gc->state == MRB_GC_STATE_MARK) {
+ add_gray_list(mrb, gc, value);
}
else {
- mrb_assert(mrb->gc_state == GC_STATE_SWEEP);
- paint_partial_white(mrb, obj); /* for never write barriers */
+ mrb_assert(gc->state == MRB_GC_STATE_SWEEP);
+ paint_partial_white(gc, obj); /* for never write barriers */
}
}
@@ -1110,16 +1213,18 @@ mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value
* e.g. Set element on Array.
*/
-void
+MRB_API void
mrb_write_barrier(mrb_state *mrb, struct RBasic *obj)
{
+ mrb_gc *gc = &mrb->gc;
+
if (!is_black(obj)) return;
- mrb_assert(!is_dead(mrb, obj));
- mrb_assert(is_generational(mrb) || mrb->gc_state != GC_STATE_NONE);
+ mrb_assert(!is_dead(gc, obj));
+ mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT);
paint_gray(obj);
- obj->gcnext = mrb->atomic_gray_list;
- mrb->atomic_gray_list = obj;
+ obj->gcnext = gc->atomic_gray_list;
+ gc->atomic_gray_list = obj;
}
/*
@@ -1153,9 +1258,9 @@ gc_start(mrb_state *mrb, mrb_value obj)
static mrb_value
gc_enable(mrb_state *mrb, mrb_value obj)
{
- int old = mrb->gc_disabled;
+ mrb_bool old = mrb->gc.disabled;
- mrb->gc_disabled = FALSE;
+ mrb->gc.disabled = FALSE;
return mrb_bool_value(old);
}
@@ -1175,9 +1280,9 @@ gc_enable(mrb_state *mrb, mrb_value obj)
static mrb_value
gc_disable(mrb_state *mrb, mrb_value obj)
{
- int old = mrb->gc_disabled;
+ mrb_bool old = mrb->gc.disabled;
- mrb->gc_disabled = TRUE;
+ mrb->gc.disabled = TRUE;
return mrb_bool_value(old);
}
@@ -1193,7 +1298,7 @@ gc_disable(mrb_state *mrb, mrb_value obj)
static mrb_value
gc_interval_ratio_get(mrb_state *mrb, mrb_value obj)
{
- return mrb_fixnum_value(mrb->gc_interval_ratio);
+ return mrb_fixnum_value(mrb->gc.interval_ratio);
}
/*
@@ -1211,7 +1316,7 @@ gc_interval_ratio_set(mrb_state *mrb, mrb_value obj)
mrb_int ratio;
mrb_get_args(mrb, "i", &ratio);
- mrb->gc_interval_ratio = ratio;
+ mrb->gc.interval_ratio = ratio;
return mrb_nil_value();
}
@@ -1226,7 +1331,7 @@ gc_interval_ratio_set(mrb_state *mrb, mrb_value obj)
static mrb_value
gc_step_ratio_get(mrb_state *mrb, mrb_value obj)
{
- return mrb_fixnum_value(mrb->gc_step_ratio);
+ return mrb_fixnum_value(mrb->gc.step_ratio);
}
/*
@@ -1244,24 +1349,24 @@ gc_step_ratio_set(mrb_state *mrb, mrb_value obj)
mrb_int ratio;
mrb_get_args(mrb, "i", &ratio);
- mrb->gc_step_ratio = ratio;
+ mrb->gc.step_ratio = ratio;
return mrb_nil_value();
}
static void
-change_gen_gc_mode(mrb_state *mrb, mrb_int enable)
+change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable)
{
- if (is_generational(mrb) && !enable) {
- clear_all_old(mrb);
- mrb_assert(mrb->gc_state == GC_STATE_NONE);
- mrb->gc_full = FALSE;
+ if (is_generational(gc) && !enable) {
+ clear_all_old(mrb, gc);
+ mrb_assert(gc->state == MRB_GC_STATE_ROOT);
+ gc->full = FALSE;
}
- else if (!is_generational(mrb) && enable) {
- incremental_gc_until(mrb, GC_STATE_NONE);
- mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
- mrb->gc_full = FALSE;
+ else if (!is_generational(gc) && enable) {
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT);
+ gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO;
+ gc->full = FALSE;
}
- mrb->is_generational_gc_mode = enable;
+ gc->generational = enable;
}
/*
@@ -1275,7 +1380,7 @@ change_gen_gc_mode(mrb_state *mrb, mrb_int enable)
static mrb_value
gc_generational_mode_get(mrb_state *mrb, mrb_value self)
{
- return mrb_bool_value(mrb->is_generational_gc_mode);
+ return mrb_bool_value(mrb->gc.generational);
}
/*
@@ -1292,21 +1397,22 @@ gc_generational_mode_set(mrb_state *mrb, mrb_value self)
mrb_bool enable;
mrb_get_args(mrb, "b", &enable);
- if (mrb->is_generational_gc_mode != enable)
- change_gen_gc_mode(mrb, enable);
+ if (mrb->gc.generational != enable)
+ change_gen_gc_mode(mrb, &mrb->gc, enable);
return mrb_bool_value(enable);
}
-void
-mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data)
+
+static void
+gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data)
{
- struct heap_page* page = mrb->heaps;
+ mrb_heap_page* page = gc->heaps;
while (page != NULL) {
RVALUE *p, *pend;
- p = page->objects;
+ p = objects(page);
pend = p + MRB_HEAP_PAGE_SIZE;
for (;p < pend; p++) {
(*callback)(mrb, &p->as.basic, data);
@@ -1316,6 +1422,12 @@ mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, vo
}
}
+void
+mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data)
+{
+ gc_each_objects(mrb, &mrb->gc, callback, data);
+}
+
#ifdef GC_TEST
#ifdef GC_DEBUG
static mrb_value gc_test(mrb_state *, mrb_value);
@@ -1352,42 +1464,43 @@ test_mrb_field_write_barrier(void)
{
mrb_state *mrb = mrb_open();
struct RBasic *obj, *value;
+ mrb_gc *gc = &mrb->gc;
puts("test_mrb_field_write_barrier");
- mrb->is_generational_gc_mode = FALSE;
+ gc->generational = FALSE;
obj = mrb_basic_ptr(mrb_ary_new(mrb));
value = mrb_basic_ptr(mrb_str_new_lit(mrb, "value"));
paint_black(obj);
- paint_partial_white(mrb,value);
+ paint_partial_white(gc, value);
- puts(" in GC_STATE_MARK");
- mrb->gc_state = GC_STATE_MARK;
+ puts(" in MRB_GC_STATE_MARK");
+ gc->state = MRB_GC_STATE_MARK;
mrb_field_write_barrier(mrb, obj, value);
mrb_assert(is_gray(value));
- puts(" in GC_STATE_SWEEP");
- paint_partial_white(mrb,value);
- mrb->gc_state = GC_STATE_SWEEP;
+ puts(" in MRB_GC_STATE_SWEEP");
+ paint_partial_white(gc, value);
+ gc->state = MRB_GC_STATE_SWEEP;
mrb_field_write_barrier(mrb, obj, value);
- mrb_assert(obj->color & mrb->current_white_part);
- mrb_assert(value->color & mrb->current_white_part);
+ mrb_assert(obj->color & gc->current_white_part);
+ mrb_assert(value->color & gc->current_white_part);
puts(" fail with black");
- mrb->gc_state = GC_STATE_MARK;
+ gc->state = MRB_GC_STATE_MARK;
paint_white(obj);
- paint_partial_white(mrb,value);
+ paint_partial_white(gc, value);
mrb_field_write_barrier(mrb, obj, value);
- mrb_assert(obj->color & mrb->current_white_part);
+ mrb_assert(obj->color & gc->current_white_part);
puts(" fail with gray");
- mrb->gc_state = GC_STATE_MARK;
+ gc->state = MRB_GC_STATE_MARK;
paint_black(obj);
paint_gray(value);
mrb_field_write_barrier(mrb, obj, value);
@@ -1400,9 +1513,9 @@ test_mrb_field_write_barrier(void)
obj = mrb_basic_ptr(mrb_ary_new(mrb));
mrb_value value = mrb_str_new_lit(mrb, "value");
paint_black(obj);
- paint_partial_white(mrb, mrb_basic_ptr(value));
+ paint_partial_white(gc, mrb_basic_ptr(value));
- mrb->gc_state = GC_STATE_MARK;
+ gc->state = MRB_GC_STATE_MARK;
mrb_field_write_barrier_value(mrb, obj, value);
mrb_assert(is_gray(mrb_basic_ptr(value)));
@@ -1416,17 +1529,18 @@ test_mrb_write_barrier(void)
{
mrb_state *mrb = mrb_open();
struct RBasic *obj;
+ mrb_gc *gc = &mrb->gc;
puts("test_mrb_write_barrier");
obj = mrb_basic_ptr(mrb_ary_new(mrb));
paint_black(obj);
- puts(" in GC_STATE_MARK");
- mrb->gc_state = GC_STATE_MARK;
+ puts(" in MRB_GC_STATE_MARK");
+ gc->state = MRB_GC_STATE_MARK;
mrb_write_barrier(mrb, obj);
mrb_assert(is_gray(obj));
- mrb_assert(mrb->atomic_gray_list == obj);
+ mrb_assert(gc->atomic_gray_list == obj);
puts(" fail with gray");
@@ -1443,19 +1557,20 @@ test_add_gray_list(void)
{
mrb_state *mrb = mrb_open();
struct RBasic *obj1, *obj2;
+ mrb_gc *gc = &mrb->gc;
puts("test_add_gray_list");
- change_gen_gc_mode(mrb, FALSE);
- mrb_assert(mrb->gray_list == NULL);
+ change_gen_gc_mode(mrb, gc, FALSE);
+ mrb_assert(gc->gray_list == NULL);
obj1 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test"));
- add_gray_list(mrb, obj1);
- mrb_assert(mrb->gray_list == obj1);
+ add_gray_list(mrb, gc, obj1);
+ mrb_assert(gc->gray_list == obj1);
mrb_assert(is_gray(obj1));
obj2 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test"));
- add_gray_list(mrb, obj2);
- mrb_assert(mrb->gray_list == obj2);
- mrb_assert(mrb->gray_list->gcnext == obj1);
+ add_gray_list(mrb, gc, obj2);
+ mrb_assert(gc->gray_list == obj2);
+ mrb_assert(gc->gray_list->gcnext == obj1);
mrb_assert(is_gray(obj2));
mrb_close(mrb);
@@ -1468,13 +1583,14 @@ test_gc_gray_mark(void)
mrb_value obj_v, value_v;
struct RBasic *obj;
size_t gray_num = 0;
+ mrb_gc *gc = &mrb->gc;
puts("test_gc_gray_mark");
puts(" in MRB_TT_CLASS");
obj = (struct RBasic*)mrb->object_class;
paint_gray(obj);
- gray_num = gc_gray_mark(mrb, obj);
+ gray_num = gc_gray_mark(mrb, gc, obj);
mrb_assert(is_black(obj));
mrb_assert(gray_num > 1);
@@ -1482,9 +1598,9 @@ test_gc_gray_mark(void)
obj_v = mrb_ary_new(mrb);
value_v = mrb_str_new_lit(mrb, "test");
paint_gray(mrb_basic_ptr(obj_v));
- paint_partial_white(mrb, mrb_basic_ptr(value_v));
+ paint_partial_white(gc, mrb_basic_ptr(value_v));
mrb_ary_push(mrb, obj_v, value_v);
- gray_num = gc_gray_mark(mrb, mrb_basic_ptr(obj_v));
+ gray_num = gc_gray_mark(mrb, gc, mrb_basic_ptr(obj_v));
mrb_assert(is_black(mrb_basic_ptr(obj_v)));
mrb_assert(is_gray(mrb_basic_ptr(value_v)));
mrb_assert(gray_num == 1);
@@ -1498,32 +1614,33 @@ test_incremental_gc(void)
mrb_state *mrb = mrb_open();
size_t max = ~0, live = 0, total = 0, freed = 0;
RVALUE *free;
- struct heap_page *page;
+ mrb_heap_page *page;
+ mrb_gc *gc = &mrb->gc;
puts("test_incremental_gc");
- change_gen_gc_mode(mrb, FALSE);
+ change_gen_gc_mode(mrb, gc, FALSE);
puts(" in mrb_full_gc");
mrb_full_gc(mrb);
- mrb_assert(mrb->gc_state == GC_STATE_NONE);
- puts(" in GC_STATE_NONE");
- incremental_gc(mrb, max);
- mrb_assert(mrb->gc_state == GC_STATE_MARK);
- puts(" in GC_STATE_MARK");
- incremental_gc_until(mrb, GC_STATE_SWEEP);
- mrb_assert(mrb->gc_state == GC_STATE_SWEEP);
+ mrb_assert(gc->state == MRB_GC_STATE_ROOT);
+ puts(" in MRB_GC_STATE_ROOT");
+ incremental_gc(mrb, gc, max);
+ mrb_assert(gc->state == MRB_GC_STATE_MARK);
+ puts(" in MRB_GC_STATE_MARK");
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP);
+ mrb_assert(gc->state == MRB_GC_STATE_SWEEP);
- puts(" in GC_STATE_SWEEP");
- page = mrb->heaps;
+ puts(" in MRB_GC_STATE_SWEEP");
+ page = gc->heaps;
while (page) {
- RVALUE *p = page->objects;
+ RVALUE *p = objects(page);
RVALUE *e = p + MRB_HEAP_PAGE_SIZE;
while (p<e) {
if (is_black(&p->as.basic)) {
live++;
}
- if (is_gray(&p->as.basic) && !is_dead(mrb, &p->as.basic)) {
+ if (is_gray(&p->as.basic) && !is_dead(gc, &p->as.basic)) {
printf("%p\n", &p->as.basic);
}
p++;
@@ -1532,44 +1649,44 @@ test_incremental_gc(void)
total += MRB_HEAP_PAGE_SIZE;
}
- mrb_assert(mrb->gray_list == NULL);
+ mrb_assert(gc->gray_list == NULL);
- incremental_gc(mrb, max);
- mrb_assert(mrb->gc_state == GC_STATE_SWEEP);
+ incremental_gc(mrb, gc, max);
+ mrb_assert(gc->state == MRB_GC_STATE_SWEEP);
- incremental_gc(mrb, max);
- mrb_assert(mrb->gc_state == GC_STATE_NONE);
+ incremental_gc(mrb, gc, max);
+ mrb_assert(gc->state == MRB_GC_STATE_ROOT);
- free = (RVALUE*)mrb->heaps->freelist;
+ free = (RVALUE*)gc->heaps->freelist;
while (free) {
freed++;
free = (RVALUE*)free->as.free.next;
}
- mrb_assert(mrb->live == live);
- mrb_assert(mrb->live == total-freed);
+ mrb_assert(gc->live == live);
+ mrb_assert(gc->live == total-freed);
puts("test_incremental_gc(gen)");
- incremental_gc_until(mrb, GC_STATE_SWEEP);
- change_gen_gc_mode(mrb, TRUE);
+ incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP);
+ change_gen_gc_mode(mrb, gc, TRUE);
- mrb_assert(mrb->gc_full == FALSE);
- mrb_assert(mrb->gc_state == GC_STATE_NONE);
+ mrb_assert(gc->full == FALSE);
+ mrb_assert(gc->state == MRB_GC_STATE_ROOT);
puts(" in minor");
- mrb_assert(is_minor_gc(mrb));
- mrb_assert(mrb->majorgc_old_threshold > 0);
- mrb->majorgc_old_threshold = 0;
+ mrb_assert(is_minor_gc(gc));
+ mrb_assert(gc->majorgc_old_threshold > 0);
+ gc->majorgc_old_threshold = 0;
mrb_incremental_gc(mrb);
- mrb_assert(mrb->gc_full == TRUE);
- mrb_assert(mrb->gc_state == GC_STATE_NONE);
+ mrb_assert(gc->full == TRUE);
+ mrb_assert(gc->state == MRB_GC_STATE_ROOT);
puts(" in major");
- mrb_assert(is_major_gc(mrb));
+ mrb_assert(is_major_gc(gc));
do {
mrb_incremental_gc(mrb);
- } while (mrb->gc_state != GC_STATE_NONE);
- mrb_assert(mrb->gc_full == FALSE);
+ } while (gc->state != MRB_GC_STATE_ROOT);
+ mrb_assert(gc->full == FALSE);
mrb_close(mrb);
}
@@ -1578,18 +1695,19 @@ void
test_incremental_sweep_phase(void)
{
mrb_state *mrb = mrb_open();
+ mrb_gc *gc = &mrb->gc;
puts("test_incremental_sweep_phase");
- add_heap(mrb);
- mrb->sweeps = mrb->heaps;
+ add_heap(mrb, gc);
+ gc->sweeps = gc->heaps;
- mrb_assert(mrb->heaps->next->next == NULL);
- mrb_assert(mrb->free_heaps->next->next == NULL);
- incremental_sweep_phase(mrb, MRB_HEAP_PAGE_SIZE*3);
+ mrb_assert(gc->heaps->next->next == NULL);
+ mrb_assert(gc->free_heaps->next->next == NULL);
+ incremental_sweep_phase(mrb, gc, MRB_HEAP_PAGE_SIZE * 3);
- mrb_assert(mrb->heaps->next == NULL);
- mrb_assert(mrb->heaps == mrb->free_heaps);
+ mrb_assert(gc->heaps->next == NULL);
+ mrb_assert(gc->heaps == gc->free_heaps);
mrb_close(mrb);
}
diff --git a/src/hash.c b/src/hash.c
index 1d449db3f..ac7256987 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -4,13 +4,13 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/hash.h"
-#include "mruby/khash.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/hash.h>
+#include <mruby/khash.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
/* a function to get hash value of a float number */
mrb_int mrb_float_id(mrb_float f);
@@ -36,20 +36,20 @@ mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key)
case MRB_TT_SYMBOL:
h = (khint_t)mrb_symbol(key);
- return kh_int_hash_func(mrb,h);
+ return kh_int_hash_func(mrb, h);
case MRB_TT_FIXNUM:
h = (khint_t)mrb_float_id((mrb_float)mrb_fixnum(key));
- return kh_int_hash_func(mrb,h);
+ return kh_int_hash_func(mrb, h);
case MRB_TT_FLOAT:
h = (khint_t)mrb_float_id(mrb_float(key));
- return kh_int_hash_func(mrb,h);
+ return kh_int_hash_func(mrb, h);
default:
hv = mrb_funcall(mrb, key, "hash", 0);
h = (khint_t)t ^ mrb_fixnum(hv);
- return kh_int_hash_func(mrb,h);
+ return kh_int_hash_func(mrb, h);
}
}
@@ -104,10 +104,11 @@ static void mrb_hash_modify(mrb_state *mrb, mrb_value hash);
static inline mrb_value
mrb_hash_ht_key(mrb_state *mrb, mrb_value key)
{
- if (mrb_string_p(key))
- return mrb_str_dup(mrb, key);
- else
- return key;
+ if (mrb_string_p(key) && !RSTR_FROZEN_P(mrb_str_ptr(key))) {
+ key = mrb_str_dup(mrb, key);
+ RSTR_SET_FROZEN_FLAG(mrb_str_ptr(key));
+ }
+ return key;
}
#define KEY(key) mrb_hash_ht_key(mrb, key)
@@ -144,7 +145,7 @@ mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash)
}
-mrb_value
+MRB_API mrb_value
mrb_hash_new_capa(mrb_state *mrb, int capa)
{
struct RHash *h;
@@ -158,13 +159,13 @@ mrb_hash_new_capa(mrb_state *mrb, int capa)
return mrb_obj_value(h);
}
-mrb_value
+MRB_API mrb_value
mrb_hash_new(mrb_state *mrb)
{
return mrb_hash_new_capa(mrb, 0);
}
-mrb_value
+MRB_API mrb_value
mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
{
khash_t(ht) *h = RHASH_TBL(hash);
@@ -183,7 +184,7 @@ mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
return RHASH_IFNONE(hash);
}
-mrb_value
+MRB_API mrb_value
mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def)
{
khash_t(ht) *h = RHASH_TBL(hash);
@@ -199,7 +200,7 @@ mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def)
return def;
}
-void
+MRB_API void
mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val)
{
khash_t(ht) *h;
@@ -241,11 +242,11 @@ mrb_hash_dup(mrb_state *mrb, mrb_value hash)
ret_h = ret->ht;
for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h,k)) {
+ if (kh_exist(h, k)) {
int ai = mrb_gc_arena_save(mrb);
- ret_k = kh_put(ht, mrb, ret_h, KEY(kh_key(h,k)));
+ ret_k = kh_put(ht, mrb, ret_h, KEY(kh_key(h, k)));
mrb_gc_arena_restore(mrb, ai);
- kh_val(ret_h, ret_k) = kh_val(h,k);
+ kh_val(ret_h, ret_k) = kh_val(h, k);
}
}
}
@@ -253,19 +254,19 @@ mrb_hash_dup(mrb_state *mrb, mrb_value hash)
return mrb_obj_value(ret);
}
-mrb_value
+MRB_API mrb_value
mrb_check_hash_type(mrb_state *mrb, mrb_value hash)
{
return mrb_check_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash");
}
-khash_t(ht) *
+MRB_API khash_t(ht)*
mrb_hash_tbl(mrb_state *mrb, mrb_value hash)
{
khash_t(ht) *h = RHASH_TBL(hash);
if (!h) {
- RHASH_TBL(hash) = kh_init(ht, mrb);
+ return RHASH_TBL(hash) = kh_init(ht, mrb);
}
return h;
}
@@ -293,22 +294,22 @@ mrb_hash_modify(mrb_state *mrb, mrb_value hash)
* default value. It is the block's responsibility to store the value
* in the hash if required.
*
- * h = Hash.new("Go Fish")
- * h["a"] = 100
- * h["b"] = 200
- * h["a"] #=> 100
- * h["c"] #=> "Go Fish"
- * # The following alters the single default object
- * h["c"].upcase! #=> "GO FISH"
- * h["d"] #=> "GO FISH"
- * h.keys #=> ["a", "b"]
- *
- * # While this creates a new default object each time
- * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
- * h["c"] #=> "Go Fish: c"
- * h["c"].upcase! #=> "GO FISH: C"
- * h["d"] #=> "Go Fish: d"
- * h.keys #=> ["c", "d"]
+ * h = Hash.new("Go Fish")
+ * h["a"] = 100
+ * h["b"] = 200
+ * h["a"] #=> 100
+ * h["c"] #=> "Go Fish"
+ * # The following alters the single default object
+ * h["c"].upcase! #=> "GO FISH"
+ * h["d"] #=> "GO FISH"
+ * h.keys #=> ["a", "b"]
+ *
+ * # While this creates a new default object each time
+ * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
+ * h["c"] #=> "Go Fish: c"
+ * h["c"].upcase! #=> "GO FISH: C"
+ * h["d"] #=> "Go Fish: d"
+ * h.keys #=> ["c", "d"]
*
*/
@@ -478,7 +479,7 @@ mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash)
return ifnone;
}
-mrb_value
+MRB_API mrb_value
mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key)
{
khash_t(ht) *h = RHASH_TBL(hash);
@@ -516,10 +517,10 @@ mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key)
* key is not found, pass in the key and return the result of
* <i>block</i>.
*
- * h = { "a" => 100, "b" => 200 }
- * h.delete("a") #=> 100
- * h.delete("z") #=> nil
- * h.delete("z") { |el| "#{el} not found" } #=> "z not found"
+ * h = { "a" => 100, "b" => 200 }
+ * h.delete("a") #=> 100
+ * h.delete("z") #=> nil
+ * h.delete("z") { |el| "#{el} not found" } #=> "z not found"
*
*/
static mrb_value
@@ -540,9 +541,9 @@ mrb_hash_delete(mrb_state *mrb, mrb_value self)
* two-item array <code>[</code> <i>key, value</i> <code>]</code>, or
* the hash's default value if the hash is empty.
*
- * h = { 1 => "a", 2 => "b", 3 => "c" }
- * h.shift #=> [1, "a"]
- * h #=> {2=>"b", 3=>"c"}
+ * h = { 1 => "a", 2 => "b", 3 => "c" }
+ * h.shift #=> [1, "a"]
+ * h #=> {2=>"b", 3=>"c"}
*/
static mrb_value
@@ -553,18 +554,16 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash)
mrb_value delKey, delVal;
mrb_hash_modify(mrb, hash);
- if (h) {
- if (kh_size(h) > 0) {
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (!kh_exist(h,k)) continue;
+ if (h && kh_size(h) > 0) {
+ for (k = kh_begin(h); k != kh_end(h); k++) {
+ if (!kh_exist(h, k)) continue;
- delKey = kh_key(h,k);
- mrb_gc_protect(mrb, delKey);
- delVal = mrb_hash_delete_key(mrb, hash, delKey);
- mrb_gc_protect(mrb, delVal);
+ delKey = kh_key(h, k);
+ mrb_gc_protect(mrb, delKey);
+ delVal = mrb_hash_delete_key(mrb, hash, delKey);
+ mrb_gc_protect(mrb, delVal);
- return mrb_assoc_new(mrb, delKey, delVal);
- }
+ return mrb_assoc_new(mrb, delKey, delVal);
}
}
@@ -581,14 +580,14 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash)
* call-seq:
* hsh.clear -> hsh
*
- * Removes all key-value pairs from <i>hsh</i>.
+ * Removes all key-value pairs from `hsh`.
*
- * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200}
- * h.clear #=> {}
+ * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200}
+ * h.clear #=> {}
*
*/
-mrb_value
+MRB_API mrb_value
mrb_hash_clear(mrb_state *mrb, mrb_value hash)
{
khash_t(ht) *h = RHASH_TBL(hash);
@@ -610,10 +609,10 @@ mrb_hash_clear(mrb_state *mrb, mrb_value hash)
* use as a key (a <code>String</code> passed as a key will be
* duplicated and frozen).
*
- * h = { "a" => 100, "b" => 200 }
- * h["a"] = 9
- * h["c"] = 4
- * h #=> {"a"=>9, "b"=>200, "c"=>4}
+ * h = { "a" => 100, "b" => 200 }
+ * h["a"] = 9
+ * h["c"] = 4
+ * h #=> {"a"=>9, "b"=>200, "c"=>4}
*
*/
static mrb_value
@@ -659,7 +658,7 @@ mrb_hash_size_m(mrb_state *mrb, mrb_value self)
* {}.empty? #=> true
*
*/
-mrb_value
+MRB_API mrb_value
mrb_hash_empty_p(mrb_state *mrb, mrb_value self)
{
khash_t(ht) *h = RHASH_TBL(self);
@@ -695,21 +694,22 @@ mrb_hash_to_hash(mrb_state *mrb, mrb_value hash)
*
*/
-mrb_value
+MRB_API mrb_value
mrb_hash_keys(mrb_state *mrb, mrb_value hash)
{
khash_t(ht) *h = RHASH_TBL(hash);
khiter_t k;
- mrb_value ary, *p;
+ mrb_value ary;
+ mrb_value *p;
if (!h || kh_size(h) == 0) return mrb_ary_new(mrb);
ary = mrb_ary_new_capa(mrb, kh_size(h));
mrb_ary_set(mrb, ary, kh_size(h)-1, mrb_nil_value());
- p = RARRAY_PTR(ary);
+ p = mrb_ary_ptr(ary)->ptr;
for (k = kh_begin(h); k != kh_end(h); k++) {
if (kh_exist(h, k)) {
- mrb_value kv = kh_key(h,k);
- mrb_hash_value hv = kh_value(h,k);
+ mrb_value kv = kh_key(h, k);
+ mrb_hash_value hv = kh_value(h, k);
p[hv.n] = kv;
}
@@ -741,7 +741,7 @@ mrb_hash_values(mrb_state *mrb, mrb_value hash)
ary = mrb_ary_new_capa(mrb, kh_size(h));
for (k = kh_begin(h); k != kh_end(h); k++) {
if (kh_exist(h, k)) {
- mrb_hash_value hv = kh_value(h,k);
+ mrb_hash_value hv = kh_value(h, k);
mrb_ary_set(mrb, ary, hv.n, hv.v);
}
@@ -814,7 +814,7 @@ mrb_hash_has_value(mrb_state *mrb, mrb_value hash)
for (k = kh_begin(h); k != kh_end(h); k++) {
if (!kh_exist(h, k)) continue;
- if (mrb_equal(mrb, kh_value(h,k).v, val)) {
+ if (mrb_equal(mrb, kh_value(h, k).v, val)) {
return mrb_true_value();
}
}
@@ -827,7 +827,7 @@ mrb_init_hash(mrb_state *mrb)
{
struct RClass *h;
- h = mrb->hash_class = mrb_define_class(mrb, "Hash", mrb->object_class); /* 15.2.13 */
+ mrb->hash_class = h = mrb_define_class(mrb, "Hash", mrb->object_class); /* 15.2.13 */
MRB_SET_INSTANCE_TT(h, MRB_TT_HASH);
mrb_define_method(mrb, h, "[]", mrb_hash_aget, MRB_ARGS_REQ(1)); /* 15.2.13.4.2 */
diff --git a/src/init.c b/src/init.c
index 9489cea10..9a6496df1 100644
--- a/src/init.c
+++ b/src/init.c
@@ -4,7 +4,7 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
+#include <mruby.h>
void mrb_init_symtbl(mrb_state*);
void mrb_init_class(mrb_state*);
@@ -24,8 +24,6 @@ void mrb_init_gc(mrb_state*);
void mrb_init_math(mrb_state*);
void mrb_init_version(mrb_state*);
void mrb_init_mrblib(mrb_state*);
-void mrb_init_mrbgems(mrb_state*);
-void mrb_final_mrbgems(mrb_state*);
#define DONE mrb_gc_arena_restore(mrb, 0);
void
@@ -50,7 +48,4 @@ mrb_init_core(mrb_state *mrb)
mrb_init_gc(mrb); DONE;
mrb_init_version(mrb); DONE;
mrb_init_mrblib(mrb); DONE;
-#ifndef DISABLE_GEMS
- mrb_init_mrbgems(mrb); DONE;
-#endif
}
diff --git a/src/kernel.c b/src/kernel.c
index 0258e5c15..af6a49be1 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -4,13 +4,13 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/error.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
typedef enum {
NOEX_PUBLIC = 0x00,
@@ -30,7 +30,7 @@ static mrb_bool
mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
{
struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mrb_intern_lit(mrb, "to_s"));
- if (me && MRB_PROC_CFUNC_P(me) && (me->body.func == mrb_any_to_s))
+ if (MRB_PROC_CFUNC_P(me) && (me->body.func == mrb_any_to_s))
return TRUE;
return FALSE;
}
@@ -49,7 +49,7 @@ mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
* [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]"
* Time.new.inspect #=> "2008-03-08 19:43:39 +0900"
*/
-mrb_value
+MRB_API mrb_value
mrb_obj_inspect(mrb_state *mrb, mrb_value obj)
{
if ((mrb_type(obj) == MRB_TT_OBJECT) && mrb_obj_basic_to_s_p(mrb, obj)) {
@@ -186,16 +186,26 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
}
else {
/* block_given? called within block; check upper scope */
- if (ci->proc->env && ci->proc->env->stack) {
- given_p = !(ci->proc->env->stack == mrb->c->stbase ||
- mrb_nil_p(ci->proc->env->stack[1]));
- }
- else {
- if (ci->argc > 0) {
- bp += ci->argc;
+ if (ci->proc->env) {
+ struct REnv *e = ci->proc->env;
+ mrb_value *sp;
+
+ while (e->c) {
+ e = (struct REnv*)e->c;
+ }
+ sp = e->stack;
+ if (sp) {
+ /* top-level does not have block slot (alway false) */
+ if (sp == mrb->c->stbase)
+ return mrb_false_value();
+ ci = mrb->c->cibase + e->cioff;
+ bp = ci[1].stackent + 1;
}
- given_p = !mrb_nil_p(*bp);
}
+ if (ci->argc > 0) {
+ bp += ci->argc;
+ }
+ given_p = !mrb_nil_p(*bp);
}
return mrb_bool_value(given_p);
@@ -230,14 +240,12 @@ mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj)
/* copy singleton(unnamed) class */
struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class);
- if ((mrb_type(obj) == MRB_TT_CLASS) ||
- (mrb_type(obj) == MRB_TT_SCLASS)) { /* BUILTIN_TYPE(obj) == T_CLASS */
+ if ((mrb_type(obj) == MRB_TT_CLASS) || (mrb_type(obj) == MRB_TT_SCLASS)) {
clone->c = clone;
}
else {
clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass));
}
-
clone->super = klass->super;
if (klass->iv) {
mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass));
@@ -259,6 +267,21 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src)
{
struct RClass *dc = mrb_class_ptr(dst);
struct RClass *sc = mrb_class_ptr(src);
+ /* if the origin is not the same as the class, then the origin and
+ the current class need to be copied */
+ if (sc->flags & MRB_FLAG_IS_PREPENDED) {
+ struct RClass *c0 = sc->super;
+ struct RClass *c1 = dc;
+
+ /* copy prepended iclasses */
+ while (!(c0->flags & MRB_FLAG_IS_ORIGIN)) {
+ c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0)));
+ c1 = c1->super;
+ c0 = c0->super;
+ }
+ c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0)));
+ c1->super->flags |= MRB_FLAG_IS_ORIGIN;
+ }
dc->mt = kh_copy(mt, mrb, sc->mt);
dc->super = sc->super;
}
@@ -275,6 +298,7 @@ init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
case MRB_TT_SCLASS:
case MRB_TT_HASH:
case MRB_TT_DATA:
+ case MRB_TT_EXCEPTION:
mrb_iv_copy(mrb, dest, obj);
break;
@@ -310,15 +334,18 @@ init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
*
* Some Class(True False Nil Symbol Fixnum Float) Object cannot clone.
*/
-mrb_value
+MRB_API mrb_value
mrb_obj_clone(mrb_state *mrb, mrb_value self)
{
struct RObject *p;
mrb_value clone;
- if (mrb_special_const_p(self)) {
+ if (mrb_immediate_p(self)) {
mrb_raisef(mrb, E_TYPE_ERROR, "can't clone %S", self);
}
+ if (mrb_type(self) == MRB_TT_SCLASS) {
+ mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class");
+ }
p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self));
p->c = mrb_singleton_class_clone(mrb, self);
clone = mrb_obj_value(p);
@@ -346,15 +373,18 @@ mrb_obj_clone(mrb_state *mrb, mrb_value self)
* the class.
*/
-mrb_value
+MRB_API mrb_value
mrb_obj_dup(mrb_state *mrb, mrb_value obj)
{
struct RBasic *p;
mrb_value dup;
- if (mrb_special_const_p(obj)) {
+ if (mrb_immediate_p(obj)) {
mrb_raisef(mrb, E_TYPE_ERROR, "can't dup %S", obj);
}
+ if (mrb_type(obj) == MRB_TT_SCLASS) {
+ mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class");
+ }
p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj));
dup = mrb_obj_value(p);
init_copy(mrb, dup, obj);
@@ -426,7 +456,7 @@ mrb_obj_extend_m(mrb_state *mrb, mrb_value self)
* <code>Hash</code>. Any hash value that exceeds the capacity of a
* <code>Fixnum</code> will be truncated before being used.
*/
-mrb_value
+MRB_API mrb_value
mrb_obj_hash(mrb_state *mrb, mrb_value self)
{
return mrb_fixnum_value(mrb_obj_id(self));
@@ -450,7 +480,7 @@ mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
/* implementation of instance_eval */
mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
-mrb_bool
+MRB_API mrb_bool
mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
{
if (mrb_obj_class(mrb, obj) == c) return TRUE;
@@ -469,51 +499,10 @@ static mrb_value
obj_is_instance_of(mrb_state *mrb, mrb_value self)
{
mrb_value arg;
- mrb_bool instance_of_p;
mrb_get_args(mrb, "C", &arg);
- instance_of_p = mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg));
- return mrb_bool_value(instance_of_p);
-}
-
-static void
-valid_iv_name(mrb_state *mrb, mrb_sym iv_name_id, const char* s, mrb_int len)
-{
- if (len < 2 || !(s[0] == '@' && s[1] != '@')) {
- mrb_name_error(mrb, iv_name_id, "`%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name_id));
- }
-}
-
-static void
-check_iv_name(mrb_state *mrb, mrb_sym iv_name_id)
-{
- const char *s;
- mrb_int len;
-
- s = mrb_sym2name_len(mrb, iv_name_id, &len);
- valid_iv_name(mrb, iv_name_id, s, len);
-}
-
-static mrb_sym
-get_valid_iv_sym(mrb_state *mrb, mrb_value iv_name)
-{
- mrb_sym iv_name_id;
-
- mrb_assert(mrb_symbol_p(iv_name) || mrb_string_p(iv_name));
-
- if (mrb_string_p(iv_name)) {
- char *p = RSTRING_PTR(iv_name);
- mrb_int l = RSTRING_LEN(iv_name);
- iv_name_id = mrb_intern(mrb, p, l);
- valid_iv_name(mrb, iv_name_id, p, l);
- }
- else {
- iv_name_id = mrb_symbol(iv_name);
- check_iv_name(mrb, iv_name_id);
- }
-
- return iv_name_id;
+ return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg)));
}
/* 15.3.1.3.20 */
@@ -537,15 +526,11 @@ get_valid_iv_sym(mrb_state *mrb, mrb_value iv_name)
static mrb_value
mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self)
{
- mrb_sym mid;
- mrb_value sym;
- mrb_bool defined_p;
-
- mrb_get_args(mrb, "o", &sym);
- mid = get_valid_iv_sym(mrb, sym);
- defined_p = mrb_obj_iv_defined(mrb, mrb_obj_ptr(self), mid);
+ mrb_sym sym;
- return mrb_bool_value(defined_p);
+ mrb_get_args(mrb, "n", &sym);
+ mrb_iv_check(mrb, sym);
+ return mrb_bool_value(mrb_iv_defined(mrb, self, sym));
}
/* 15.3.1.3.21 */
@@ -571,13 +556,11 @@ mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_obj_ivar_get(mrb_state *mrb, mrb_value self)
{
- mrb_sym iv_name_id;
- mrb_value iv_name;
-
- mrb_get_args(mrb, "o", &iv_name);
+ mrb_sym iv_name;
- iv_name_id = get_valid_iv_sym(mrb, iv_name);
- return mrb_iv_get(mrb, self, iv_name_id);
+ mrb_get_args(mrb, "n", &iv_name);
+ mrb_iv_check(mrb, iv_name);
+ return mrb_iv_get(mrb, self, iv_name);
}
/* 15.3.1.3.22 */
@@ -603,13 +586,12 @@ mrb_obj_ivar_get(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
{
- mrb_sym iv_name_id;
- mrb_value iv_name, val;
-
- mrb_get_args(mrb, "oo", &iv_name, &val);
+ mrb_sym iv_name;
+ mrb_value val;
- iv_name_id = get_valid_iv_sym(mrb, iv_name);
- mrb_iv_set(mrb, self, iv_name_id, val);
+ mrb_get_args(mrb, "no", &iv_name, &val);
+ mrb_iv_check(mrb, iv_name);
+ mrb_iv_set(mrb, self, iv_name, val);
return val;
}
@@ -644,12 +626,10 @@ static mrb_value
mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self)
{
mrb_value arg;
- mrb_bool kind_of_p;
mrb_get_args(mrb, "C", &arg);
- kind_of_p = mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg));
- return mrb_bool_value(kind_of_p);
+ return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg)));
}
KHASH_DECLARE(st, mrb_sym, char, FALSE)
@@ -663,8 +643,8 @@ method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set)
khash_t(mt) *h = klass->mt;
if (!h) return;
for (i=0;i<kh_end(h);i++) {
- if (kh_exist(h, i)) {
- kh_put(st, mrb, set, kh_key(h,i));
+ if (kh_exist(h, i) && kh_value(h, i)) {
+ kh_put(st, mrb, set, kh_key(h, i));
}
}
}
@@ -674,13 +654,19 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl
{
khint_t i;
mrb_value ary;
+ mrb_bool prepended = FALSE;
struct RClass* oldklass;
khash_t(st)* set = kh_init(st, mrb);
+ if (!recur && (klass->flags & MRB_FLAG_IS_PREPENDED)) {
+ MRB_CLASS_ORIGIN(klass);
+ prepended = TRUE;
+ }
+
oldklass = 0;
while (klass && (klass != oldklass)) {
method_entry_loop(mrb, klass, set);
- if ((klass->tt == MRB_TT_ICLASS) ||
+ if ((klass->tt == MRB_TT_ICLASS && !prepended) ||
(klass->tt == MRB_TT_SCLASS)) {
}
else {
@@ -693,7 +679,7 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl
ary = mrb_ary_new(mrb);
for (i=0;i<kh_end(set);i++) {
if (kh_exist(set, i)) {
- mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set,i)));
+ mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i)));
}
}
kh_destroy(st, mrb, set);
@@ -725,7 +711,7 @@ mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj)
ary = mrb_ary_new(mrb);
for (i=0;i<kh_end(set);i++) {
if (kh_exist(set, i)) {
- mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set,i)));
+ mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i)));
}
}
kh_destroy(st, mrb, set);
@@ -737,9 +723,8 @@ static mrb_value
mrb_obj_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj, mrb_method_flag_t flag)
{
if (recur)
- return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0);
- else
- return mrb_obj_singleton_methods(mrb, recur, obj);
+ return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0);
+ return mrb_obj_singleton_methods(mrb, recur, obj);
}
/* 15.3.1.3.31 */
/*
@@ -854,7 +839,7 @@ mrb_obj_public_methods(mrb_state *mrb, mrb_value self)
* raise "Failed to create socket"
* raise ArgumentError, "No parameters", caller
*/
-mrb_value
+MRB_API mrb_value
mrb_f_raise(mrb_state *mrb, mrb_value self)
{
mrb_value a[2], exc;
@@ -911,7 +896,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
mrb_value val;
mrb_get_args(mrb, "n", &sym);
- check_iv_name(mrb, sym);
+ mrb_iv_check(mrb, sym);
val = mrb_iv_remove(mrb, self, sym);
if (mrb_undef_p(val)) {
mrb_name_error(mrb, sym, "instance variable %S not defined", mrb_sym2str(mrb, sym));
@@ -1112,7 +1097,7 @@ mrb_init_kernel(mrb_state *mrb)
{
struct RClass *krn;
- krn = mrb->kernel_module = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */
+ mrb->kernel_module = krn = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */
mrb_define_class_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.2 */
mrb_define_class_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.2.4 */
mrb_define_class_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.5 */
diff --git a/src/load.c b/src/load.c
index 8dea4a391..da88f0d3a 100644
--- a/src/load.c
+++ b/src/load.c
@@ -7,28 +7,31 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
-#include "mruby/dump.h"
-#include "mruby/irep.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
-#include "mruby/debug.h"
-#include "mruby/error.h"
-
-#if !defined(_WIN32) && SIZE_MAX < UINT32_MAX
-# define SIZE_ERROR_MUL(x, y) ((x) > SIZE_MAX / (y))
-# define SIZE_ERROR(x) ((x) > SIZE_MAX)
-#else
-# define SIZE_ERROR_MUL(x, y) (0)
-# define SIZE_ERROR(x) (0)
+#include <mruby/dump.h>
+#include <mruby/irep.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+#include <mruby/debug.h>
+#include <mruby/error.h>
+
+#if SIZE_MAX < UINT32_MAX
+# error size_t must be at least 32 bits wide
#endif
-#if CHAR_BIT != 8
-# error This code assumes CHAR_BIT == 8
-#endif
+#define FLAG_BYTEORDER_BIG 2
+#define FLAG_BYTEORDER_LIL 4
+#define FLAG_BYTEORDER_NATIVE 8
+#define FLAG_SRC_MALLOC 1
+#define FLAG_SRC_STATIC 0
-#if UINT32_MAX > SIZE_MAX
-# error This code cannot be built on your environment.
-#endif
+#define SIZE_ERROR_MUL(nmemb, size) ((nmemb) > SIZE_MAX / (size))
+
+static size_t
+skip_padding(const uint8_t *buf)
+{
+ const size_t align = MRB_DUMP_ALIGNMENT;
+ return -(intptr_t)buf & (align-1);
+}
static size_t
offset_crc_body(void)
@@ -38,7 +41,7 @@ offset_crc_body(void)
}
static mrb_irep*
-read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool alloc)
+read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
{
size_t i;
const uint8_t *src = bin;
@@ -67,14 +70,36 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all
/* ISEQ BLOCK */
irep->ilen = (size_t)bin_to_uint32(src);
src += sizeof(uint32_t);
+ src += skip_padding(src);
+
if (irep->ilen > 0) {
- if (SIZE_ERROR_MUL(sizeof(mrb_code), irep->ilen)) {
+ if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) {
return NULL;
}
- irep->iseq = (mrb_code *)mrb_malloc(mrb, sizeof(mrb_code) * irep->ilen);
- for (i = 0; i < irep->ilen; i++) {
- irep->iseq[i] = (size_t)bin_to_uint32(src); /* iseq */
- src += sizeof(uint32_t);
+ if ((flags & FLAG_SRC_MALLOC) == 0 &&
+ (flags & FLAG_BYTEORDER_NATIVE)) {
+ irep->iseq = (mrb_code*)src;
+ src += sizeof(uint32_t) * irep->ilen;
+ irep->flags |= MRB_ISEQ_NO_FREE;
+ }
+ else {
+ irep->iseq = (mrb_code *)mrb_malloc(mrb, sizeof(mrb_code) * irep->ilen);
+ if (flags & FLAG_BYTEORDER_NATIVE) {
+ memcpy(irep->iseq, src, sizeof(uint32_t) * irep->ilen);
+ src += sizeof(uint32_t) * irep->ilen;
+ }
+ else if (flags & FLAG_BYTEORDER_BIG) {
+ for (i = 0; i < irep->ilen; i++) {
+ irep->iseq[i] = (mrb_code)bin_to_uint32(src); /* iseq */
+ src += sizeof(uint32_t);
+ }
+ }
+ else {
+ for (i = 0; i < irep->ilen; i++) {
+ irep->iseq[i] = (mrb_code)bin_to_uint32l(src); /* iseq */
+ src += sizeof(uint32_t);
+ }
+ }
}
}
@@ -82,7 +107,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all
plen = (size_t)bin_to_uint32(src); /* number of pool */
src += sizeof(uint32_t);
if (plen > 0) {
- if (SIZE_ERROR_MUL(sizeof(mrb_value), plen)) {
+ if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) {
return NULL;
}
irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen);
@@ -93,7 +118,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all
tt = *src++; /* pool TT */
pool_data_len = bin_to_uint16(src); /* pool data length */
src += sizeof(uint16_t);
- if (alloc) {
+ if (flags & FLAG_SRC_MALLOC) {
s = mrb_str_new(mrb, (char *)src, pool_data_len);
}
else {
@@ -127,7 +152,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all
irep->slen = (size_t)bin_to_uint32(src); /* syms length */
src += sizeof(uint32_t);
if (irep->slen > 0) {
- if (SIZE_ERROR_MUL(sizeof(mrb_sym), irep->slen)) {
+ if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) {
return NULL;
}
irep->syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen);
@@ -141,7 +166,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all
continue;
}
- if (alloc) {
+ if (flags & FLAG_SRC_MALLOC) {
irep->syms[i] = mrb_intern(mrb, (char *)src, snl);
}
else {
@@ -163,16 +188,23 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all
}
static mrb_irep*
-read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool alloc)
+read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
{
- mrb_irep *irep = read_irep_record_1(mrb, bin, len, alloc);
+ mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags);
size_t i;
+ if (irep == NULL) {
+ return NULL;
+ }
+
bin += *len;
for (i=0; i<irep->rlen; i++) {
size_t rlen;
- irep->reps[i] = read_irep_record(mrb, bin, &rlen, alloc);
+ irep->reps[i] = read_irep_record(mrb, bin, &rlen, flags);
+ if (irep->reps[i] == NULL) {
+ return NULL;
+ }
bin += rlen;
*len += rlen;
}
@@ -180,12 +212,12 @@ read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool alloc
}
static mrb_irep*
-read_section_irep(mrb_state *mrb, const uint8_t *bin, mrb_bool alloc)
+read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
{
size_t len;
bin += sizeof(struct rite_section_irep_header);
- return read_irep_record(mrb, bin, &len, alloc);
+ return read_irep_record(mrb, bin, &len, flags);
}
static int
@@ -201,9 +233,6 @@ read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t
fname_len = bin_to_uint16(bin);
bin += sizeof(uint16_t);
*len += sizeof(uint16_t);
- if (SIZE_ERROR(fname_len + 1)) {
- return MRB_DUMP_GENERAL_FAILURE;
- }
fname = (char *)mrb_malloc(mrb, fname_len + 1);
memcpy(fname, bin, fname_len);
fname[fname_len] = '\0';
@@ -341,7 +370,7 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
size_t len;
int ret;
- ret =read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len);
+ ret = read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len);
if (ret != MRB_DUMP_OK) return ret;
bin += len;
}
@@ -354,7 +383,7 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
}
static int
-read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, mrb_bool alloc)
+read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags)
{
const uint8_t *bin;
ptrdiff_t diff;
@@ -375,7 +404,7 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, mrb_boo
for (i = 0; i < filenames_len; ++i) {
uint16_t f_len = bin_to_uint16(bin);
bin += sizeof(uint16_t);
- if (alloc) {
+ if (flags & FLAG_SRC_MALLOC) {
filenames[i] = mrb_intern(mrb, (const char *)bin, (size_t)f_len);
}
else {
@@ -443,7 +472,7 @@ read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *rec
}
static int
-read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, mrb_bool alloc)
+read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags)
{
const uint8_t *bin;
ptrdiff_t diff;
@@ -453,7 +482,8 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, mrb_bool a
int result;
uint32_t syms_len;
mrb_sym *syms;
- mrb_sym (*intern_func)(mrb_state*, const char*, size_t) = alloc? mrb_intern : mrb_intern_static;
+ mrb_sym (*intern_func)(mrb_state*, const char*, size_t) =
+ (flags & FLAG_SRC_MALLOC)? mrb_intern : mrb_intern_static;
bin = start;
header = (struct rite_section_lv_header const*)bin;
@@ -486,28 +516,36 @@ lv_exit:
}
static int
-read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc)
+read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t *flags)
{
const struct rite_binary_header *header = (const struct rite_binary_header *)bin;
- if (memcmp(header->binary_identify, RITE_BINARY_IDENTIFIER, sizeof(header->binary_identify)) != 0) {
- return MRB_DUMP_INVALID_FILE_HEADER;
+ if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) == 0) {
+ if (bigendian_p())
+ *flags |= FLAG_BYTEORDER_NATIVE;
+ else
+ *flags |= FLAG_BYTEORDER_BIG;
}
-
- if (memcmp(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)) != 0) {
+ else if (memcmp(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)) == 0) {
+ if (bigendian_p())
+ *flags |= FLAG_BYTEORDER_LIL;
+ else
+ *flags |= FLAG_BYTEORDER_NATIVE;
+ }
+ else {
return MRB_DUMP_INVALID_FILE_HEADER;
}
- *crc = bin_to_uint16(header->binary_crc);
- if (bin_size) {
- *bin_size = (size_t)bin_to_uint32(header->binary_size);
+ if (crc) {
+ *crc = bin_to_uint16(header->binary_crc);
}
+ *bin_size = (size_t)bin_to_uint32(header->binary_size);
return MRB_DUMP_OK;
}
-mrb_irep*
-mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
+MRB_API mrb_irep*
+read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
{
int result;
mrb_irep *irep = NULL;
@@ -520,7 +558,7 @@ mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
return NULL;
}
- result = read_binary_header(bin, &bin_size, &crc);
+ result = read_binary_header(bin, &bin_size, &crc, &flags);
if (result != MRB_DUMP_OK) {
return NULL;
}
@@ -533,48 +571,59 @@ mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
bin += sizeof(struct rite_binary_header);
do {
section_header = (const struct rite_section_header *)bin;
- if (memcmp(section_header->section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(section_header->section_identify)) == 0) {
- irep = read_section_irep(mrb, bin, FALSE);
+ if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) {
+ irep = read_section_irep(mrb, bin, flags);
if (!irep) return NULL;
}
- else if (memcmp(section_header->section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(section_header->section_identify)) == 0) {
+ else if (memcmp(section_header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(section_header->section_ident)) == 0) {
if (!irep) return NULL; /* corrupted data */
result = read_section_lineno(mrb, bin, irep);
if (result < MRB_DUMP_OK) {
return NULL;
}
}
- else if (memcmp(section_header->section_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(section_header->section_identify)) == 0) {
+ else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) {
if (!irep) return NULL; /* corrupted data */
- result = read_section_debug(mrb, bin, irep, FALSE);
+ result = read_section_debug(mrb, bin, irep, flags);
if (result < MRB_DUMP_OK) {
return NULL;
}
}
- else if (memcmp(section_header->section_identify, RITE_SECTION_LV_IDENTIFIER, sizeof(section_header->section_identify)) == 0) {
+ else if (memcmp(section_header->section_ident, RITE_SECTION_LV_IDENT, sizeof(section_header->section_ident)) == 0) {
if (!irep) return NULL;
- result = read_section_lv(mrb, bin, irep, FALSE);
+ result = read_section_lv(mrb, bin, irep, flags);
if (result < MRB_DUMP_OK) {
return NULL;
}
}
bin += bin_to_uint32(section_header->section_size);
- } while (memcmp(section_header->section_identify, RITE_BINARY_EOF, sizeof(section_header->section_identify)) != 0);
+ } while (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0);
return irep;
}
+MRB_API mrb_irep*
+mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
+{
+#ifdef MRB_USE_ETEXT_EDATA
+ uint8_t flags = mrb_ro_data_p((char*)bin) ? FLAG_SRC_STATIC : FLAG_SRC_MALLOC;
+#else
+ uint8_t flags = FLAG_SRC_STATIC;
+#endif
+
+ return read_irep(mrb, bin, flags);
+}
+
static void
irep_error(mrb_state *mrb)
{
mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error"));
}
-mrb_value
+MRB_API mrb_value
mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
{
mrb_irep *irep = mrb_read_irep(mrb, bin);
- mrb_value val;
struct RProc *proc;
if (!irep) {
@@ -584,224 +633,54 @@ mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
proc = mrb_proc_new(mrb, irep);
mrb_irep_decref(mrb, irep);
if (c && c->no_exec) return mrb_obj_value(proc);
- val = mrb_toplevel_run(mrb, proc);
- return val;
+ return mrb_toplevel_run(mrb, proc);
}
-mrb_value
+MRB_API mrb_value
mrb_load_irep(mrb_state *mrb, const uint8_t *bin)
{
return mrb_load_irep_cxt(mrb, bin, NULL);
}
-#ifdef ENABLE_STDIO
-
-static int
-read_lineno_record_file(mrb_state *mrb, FILE *fp, mrb_irep *irep)
-{
- uint8_t header[4];
- const size_t record_header_size = sizeof(header);
- int result;
- size_t i, buf_size;
- size_t len;
- void *ptr;
- uint8_t *buf;
-
- if (fread(header, record_header_size, 1, fp) == 0) {
- return MRB_DUMP_READ_FAULT;
- }
- buf_size = (size_t)bin_to_uint32(&header[0]);
- if (SIZE_ERROR(buf_size)) {
- return MRB_DUMP_GENERAL_FAILURE;
- }
- ptr = mrb_malloc(mrb, buf_size);
- buf = (uint8_t *)ptr;
-
- if (fread(&buf[record_header_size], buf_size - record_header_size, 1, fp) == 0) {
- return MRB_DUMP_READ_FAULT;
- }
- result = read_lineno_record_1(mrb, buf, irep, &len);
- mrb_free(mrb, ptr);
- if (result != MRB_DUMP_OK) return result;
- for (i = 0; i < irep->rlen; i++) {
- result = read_lineno_record_file(mrb, fp, irep->reps[i]);
- if (result != MRB_DUMP_OK) break;
- }
- return result;
-}
-
-static int32_t
-read_section_lineno_file(mrb_state *mrb, FILE *fp, mrb_irep *irep)
-{
- struct rite_section_lineno_header header;
-
- if (fread(&header, sizeof(struct rite_section_lineno_header), 1, fp) == 0) {
- return MRB_DUMP_READ_FAULT;
- }
-
- /* Read Binary Data Section */
- return read_lineno_record_file(mrb, fp, irep);
-}
+#ifndef MRB_DISABLE_STDIO
-static mrb_irep*
-read_irep_record_file(mrb_state *mrb, FILE *fp)
-{
- uint8_t header[1 + 4];
- const size_t record_header_size = sizeof(header);
- size_t buf_size, i;
- size_t len;
- mrb_irep *irep = NULL;
- void *ptr;
- uint8_t *buf;
-
- if (fread(header, record_header_size, 1, fp) == 0) {
- return NULL;
- }
- buf_size = (size_t)bin_to_uint32(&header[0]);
- if (SIZE_ERROR(buf_size)) {
- return NULL;
- }
- ptr = mrb_malloc(mrb, buf_size);
- buf = (uint8_t *)ptr;
- memcpy(buf, header, record_header_size);
- if (fread(&buf[record_header_size], buf_size - record_header_size, 1, fp) == 0) {
- return NULL;
- }
- irep = read_irep_record_1(mrb, buf, &len, TRUE);
- mrb_free(mrb, ptr);
- if (!irep) return NULL;
- for (i=0; i<irep->rlen; i++) {
- irep->reps[i] = read_irep_record_file(mrb, fp);
- if (!irep->reps[i]) return NULL;
- }
- return irep;
-}
-
-static mrb_irep*
-read_section_irep_file(mrb_state *mrb, FILE *fp)
-{
- struct rite_section_irep_header header;
-
- if (fread(&header, sizeof(struct rite_section_irep_header), 1, fp) == 0) {
- return NULL;
- }
- return read_irep_record_file(mrb, fp);
-}
-
-mrb_irep*
+MRB_API mrb_irep*
mrb_read_irep_file(mrb_state *mrb, FILE* fp)
{
mrb_irep *irep = NULL;
- int result;
uint8_t *buf;
- uint16_t crc, crcwk = 0;
- size_t section_size = 0;
- size_t nbytes;
- struct rite_section_header section_header;
- long fpos;
- size_t block_size = 1 << 14;
- const uint8_t block_fallback_count = 4;
- int i;
- const size_t buf_size = sizeof(struct rite_binary_header);
+ const size_t header_size = sizeof(struct rite_binary_header);
+ size_t buf_size = 0;
+ uint8_t flags = 0;
+ int result;
if ((mrb == NULL) || (fp == NULL)) {
return NULL;
}
- /* You don't need use SIZE_ERROR as buf_size is enough small. */
- buf = (uint8_t*)mrb_malloc(mrb, buf_size);
- if (fread(buf, buf_size, 1, fp) == 0) {
- mrb_free(mrb, buf);
- return NULL;
+ buf = (uint8_t*)mrb_malloc(mrb, header_size);
+ if (fread(buf, header_size, 1, fp) == 0) {
+ goto irep_exit;
}
- result = read_binary_header(buf, NULL, &crc);
- mrb_free(mrb, buf);
- if (result != MRB_DUMP_OK) {
- return NULL;
+ result = read_binary_header(buf, &buf_size, NULL, &flags);
+ if (result != MRB_DUMP_OK || buf_size <= header_size) {
+ goto irep_exit;
}
- /* verify CRC */
- fpos = ftell(fp);
- /* You don't need use SIZE_ERROR as block_size is enough small. */
- for (i = 0; i < block_fallback_count; i++,block_size >>= 1) {
- buf = (uint8_t*)mrb_malloc_simple(mrb, block_size);
- if (buf) break;
+ buf = (uint8_t*)mrb_realloc(mrb, buf, buf_size);
+ if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) {
+ goto irep_exit;
}
- if (!buf) {
- return NULL;
- }
- fseek(fp, offset_crc_body(), SEEK_SET);
- while ((nbytes = fread(buf, 1, block_size, fp)) > 0) {
- crcwk = calc_crc_16_ccitt(buf, nbytes, crcwk);
- }
- mrb_free(mrb, buf);
- if (nbytes == 0 && ferror(fp)) {
- return NULL;
- }
- if (crcwk != crc) {
- return NULL;
- }
- fseek(fp, fpos + section_size, SEEK_SET);
-
- /* read sections */
- do {
- fpos = ftell(fp);
- if (fread(&section_header, sizeof(struct rite_section_header), 1, fp) == 0) {
- return NULL;
- }
- section_size = (size_t)bin_to_uint32(section_header.section_size);
-
- if (memcmp(section_header.section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(section_header.section_identify)) == 0) {
- fseek(fp, fpos, SEEK_SET);
- irep = read_section_irep_file(mrb, fp);
- if (!irep) return NULL;
- }
- else if (memcmp(section_header.section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(section_header.section_identify)) == 0) {
- if (!irep) return NULL; /* corrupted data */
- fseek(fp, fpos, SEEK_SET);
- result = read_section_lineno_file(mrb, fp, irep);
- if (result < MRB_DUMP_OK) return NULL;
- }
- else if (memcmp(section_header.section_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(section_header.section_identify)) == 0) {
- if (!irep) return NULL; /* corrupted data */
- else {
- uint8_t* const bin = (uint8_t*)mrb_malloc(mrb, section_size);
-
- fseek(fp, fpos, SEEK_SET);
- if (fread((char*)bin, section_size, 1, fp) != 1) {
- mrb_free(mrb, bin);
- return NULL;
- }
- result = read_section_debug(mrb, bin, irep, TRUE);
- mrb_free(mrb, bin);
- }
- if (result < MRB_DUMP_OK) return NULL;
- }
- else if (memcmp(section_header.section_identify, RITE_SECTION_LV_IDENTIFIER, sizeof(section_header.section_identify)) == 0) {
- if (!irep) return NULL;
- else {
- uint8_t* const bin = (uint8_t*)mrb_malloc(mrb, section_size);
-
- fseek(fp, fpos, SEEK_SET);
- if (fread((char*)bin, section_size, 1, fp) != 1) {
- mrb_free(mrb, bin);
- return NULL;
- }
- result = read_section_lv(mrb, bin, irep, TRUE);
- mrb_free(mrb, bin);
- }
- if (result < MRB_DUMP_OK) return NULL;
- }
-
- fseek(fp, fpos + section_size, SEEK_SET);
- } while (memcmp(section_header.section_identify, RITE_BINARY_EOF, sizeof(section_header.section_identify)) != 0);
+ irep = read_irep(mrb, buf, FLAG_SRC_MALLOC);
+irep_exit:
+ mrb_free(mrb, buf);
return irep;
}
void mrb_codedump_all(mrb_state*, struct RProc*);
-mrb_value
+MRB_API mrb_value
mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c)
{
mrb_irep *irep = mrb_read_irep_file(mrb, fp);
@@ -820,9 +699,9 @@ mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c)
return val;
}
-mrb_value
+MRB_API mrb_value
mrb_load_irep_file(mrb_state *mrb, FILE* fp)
{
return mrb_load_irep_file_cxt(mrb, fp, NULL);
}
-#endif /* ENABLE_STDIO */
+#endif /* MRB_DISABLE_STDIO */
diff --git a/src/mruby_core.rake b/src/mruby_core.rake
index 88fca83fc..abde441d5 100644
--- a/src/mruby_core.rake
+++ b/src/mruby_core.rake
@@ -3,76 +3,17 @@ MRuby.each_target do
relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT)
current_build_dir = "#{build_dir}/#{relative_from_root}"
- lex_def = "#{current_dir}/lex.def"
objs = Dir.glob("#{current_dir}/*.c").map { |f|
- next nil if cxx_abi_enabled? and f =~ /(codegen|error|vm).c$/
+ next nil if cxx_abi_enabled? and f =~ /(error|vm).c$/
objfile(f.pathmap("#{current_build_dir}/%n"))
}.compact
if cxx_abi_enabled?
- cxx_abi_dependency = %w(codegen error vm)
- cxx_abi_objs = cxx_abi_dependency.map { |v|
- src = "#{current_build_dir}/#{v}.cxx"
- file src => ["#{current_dir}/#{v}.c", __FILE__] do |t|
- File.open(t.name, 'w') do |f|
- f.write <<EOS
-#define __STDC_CONSTANT_MACROS
-#define __STDC_LIMIT_MACROS
-
-extern "C" {
-#include "#{MRUBY_ROOT}/#{t.prerequisites.first}"
-}
-
-
-#{v == 'error'? 'mrb_int mrb_jmpbuf::jmpbuf_id = 0;' : ''}
-EOS
- end
- end
-
- file objfile(src) => src do |t|
- cxx.run t.name, t.prerequisites.first, [], [current_dir]
- end
-
- objfile src
- }
- cxx_abi_objs << objfile("#{current_build_dir}/y.tab")
-
- file "#{current_build_dir}/y.tab.cxx" => ["#{current_build_dir}/y.tab.c", __FILE__] do |t|
- File.open(t.name, 'w') do |f|
- f.write <<EOS
-#define __STDC_CONSTANT_MACROS
-#define __STDC_LIMIT_MACROS
-
-extern "C" {
-#include "#{t.prerequisites.first}"
-}
-EOS
- end
- end
- file objfile("#{current_build_dir}/y.tab") => ["#{current_build_dir}/y.tab.cxx", lex_def] do |t|
- cxx.run t.name, t.prerequisites.first, [], [current_dir]
- end
-
- objs += cxx_abi_objs
- else
- objs += [objfile("#{current_build_dir}/y.tab")]
- file objfile("#{current_build_dir}/y.tab") => ["#{current_build_dir}/y.tab.c", lex_def] do |t|
- cc.run t.name, t.prerequisites.first, [], [current_dir]
- end
+ objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" }
end
self.libmruby << objs
file libfile("#{build_dir}/lib/libmruby_core") => objs do |t|
archiver.run t.name, t.prerequisites
end
-
- # Parser
- file "#{current_build_dir}/y.tab.c" => ["#{current_dir}/parse.y"] do |t|
- yacc.run t.name, t.prerequisites.first
- end
-
- # Lexical analyzer
- file lex_def => "#{current_dir}/keywords" do |t|
- gperf.run t.name, t.prerequisites.first
- end
end
diff --git a/src/numeric.c b/src/numeric.c
index db1e7d0f4..7b49b29f7 100644
--- a/src/numeric.c
+++ b/src/numeric.c
@@ -9,25 +9,21 @@
#include <math.h>
#include <stdlib.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
#ifdef MRB_USE_FLOAT
#define floor(f) floorf(f)
#define ceil(f) ceilf(f)
#define fmod(x,y) fmodf(x,y)
-#define FLO_MAX_DIGITS 7
-#define FLO_MAX_SIGN_LENGTH 3
-#define FLO_EPSILON FLT_EPSILON
+#define MRB_FLO_TO_STR_FMT "%.7g"
#else
-#define FLO_MAX_DIGITS 14
-#define FLO_MAX_SIGN_LENGTH 10
-#define FLO_EPSILON DBL_EPSILON
+#define MRB_FLO_TO_STR_FMT "%.14g"
#endif
-mrb_float
+MRB_API mrb_float
mrb_to_flo(mrb_state *mrb, mrb_value val)
{
switch (mrb_type(val)) {
@@ -54,13 +50,13 @@ static mrb_value
num_pow(mrb_state *mrb, mrb_value x)
{
mrb_value y;
- mrb_bool both_int = FALSE;
- mrb_float d;
+ mrb_float d, yv;
mrb_get_args(mrb, "o", &y);
- if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) both_int = TRUE;
- d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y));
- if (both_int && FIXABLE(d))
+ yv = mrb_to_flo(mrb, y);
+ d = pow(mrb_to_flo(mrb, x), yv);
+ if (mrb_fixnum_p(x) && mrb_fixnum_p(y) && FIXABLE(d) && yv > 0 &&
+ (d < 0 || (d > 0 && (mrb_int)d > 0)))
return mrb_fixnum_value((mrb_int)d);
return mrb_float_value(mrb, d);
}
@@ -108,142 +104,6 @@ num_div(mrb_state *mrb, mrb_value x)
* representation.
*/
-static mrb_value
-mrb_flo_to_str(mrb_state *mrb, mrb_float flo)
-{
- double n = (double)flo;
- int max_digits = FLO_MAX_DIGITS;
-
- if (isnan(n)) {
- return mrb_str_new_lit(mrb, "NaN");
- }
- else if (isinf(n)) {
- if (n < 0) {
- return mrb_str_new_lit(mrb, "-inf");
- }
- else {
- return mrb_str_new_lit(mrb, "inf");
- }
- }
- else {
- int digit;
- int m = 0;
- int exp;
- mrb_bool e = FALSE;
- char s[48];
- char *c = &s[0];
- int length = 0;
-
- if (signbit(n)) {
- n = -n;
- *(c++) = '-';
- }
-
- if (n != 0.0) {
- if (n > 1.0) {
- exp = (int)floor(log10(n));
- }
- else {
- exp = (int)-ceil(-log10(n));
- }
- }
- else {
- exp = 0;
- }
-
- /* preserve significands */
- if (exp < 0) {
- int i, beg = -1, end = 0;
- double f = n;
- double fd = 0;
- for (i = 0; i < FLO_MAX_DIGITS; ++i) {
- f = (f - fd) * 10.0;
- fd = floor(f + FLO_EPSILON);
- if (fd != 0) {
- if (beg < 0) beg = i;
- end = i + 1;
- }
- }
- if (beg >= 0) length = end - beg;
- if (length > FLO_MAX_SIGN_LENGTH) length = FLO_MAX_SIGN_LENGTH;
- }
-
- if (abs(exp) + length >= FLO_MAX_DIGITS) {
- /* exponent representation */
- e = TRUE;
- n = n / pow(10.0, exp);
- if (isinf(n)) {
- if (s < c) { /* s[0] == '-' */
- return mrb_str_new_lit(mrb, "-0.0");
- }
- else {
- return mrb_str_new_lit(mrb, "0.0");
- }
- }
- }
- else {
- /* un-exponent (normal) representation */
- if (exp > 0) {
- m = exp;
- }
- }
-
- /* puts digits */
- while (max_digits >= 0) {
- double weight = pow(10.0, m);
- double fdigit = n / weight;
-
- if (fdigit < 0) fdigit = n = 0;
- if (m < -1 && fdigit < FLO_EPSILON) {
- if (e || exp > 0 || m <= -abs(exp)) {
- break;
- }
- }
- digit = (int)floor(fdigit + FLO_EPSILON);
- if (m == 0 && digit > 9) {
- n /= 10.0;
- exp++;
- continue;
- }
- *(c++) = '0' + digit;
- n -= (digit * weight);
- max_digits--;
- if (m-- == 0) {
- *(c++) = '.';
- }
- }
- if (c[-1] == '0') {
- while (&s[0] < c && c[-1] == '0') {
- c--;
- }
- c++;
- }
-
- if (e) {
- *(c++) = 'e';
- if (exp > 0) {
- *(c++) = '+';
- }
- else {
- *(c++) = '-';
- exp = -exp;
- }
-
- if (exp >= 100) {
- *(c++) = '0' + exp / 100;
- exp -= exp / 100 * 100;
- }
-
- *(c++) = '0' + exp / 10;
- *(c++) = '0' + exp % 10;
- }
-
- *c = '\0';
-
- return mrb_str_new(mrb, &s[0], c - &s[0]);
- }
-}
-
/* 15.2.9.3.16(x) */
/*
* call-seq:
@@ -251,14 +111,17 @@ mrb_flo_to_str(mrb_state *mrb, mrb_float flo)
*
* Returns a string containing a representation of self. As well as a
* fixed or exponential form of the number, the call may return
- * ``<code>NaN</code>'', ``<code>Infinity</code>'', and
- * ``<code>-Infinity</code>''.
+ * "<code>NaN</code>", "<code>Infinity</code>", and
+ * "<code>-Infinity</code>".
*/
static mrb_value
flo_to_s(mrb_state *mrb, mrb_value flt)
{
- return mrb_flo_to_str(mrb, mrb_float(flt));
+ if (isnan(mrb_float(flt))) {
+ return mrb_str_new_lit(mrb, "NaN");
+ }
+ return mrb_float_to_str(mrb, flt, MRB_FLO_TO_STR_FMT);
}
/* 15.2.9.3.2 */
@@ -339,12 +202,11 @@ static mrb_value
flo_mod(mrb_state *mrb, mrb_value x)
{
mrb_value y;
- mrb_float fy, mod;
+ mrb_float mod;
mrb_get_args(mrb, "o", &y);
- fy = mrb_to_flo(mrb, y);
- flodivmod(mrb, mrb_float(x), fy, 0, &mod);
+ flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod);
return mrb_float_value(mrb, mod);
}
@@ -397,22 +259,16 @@ static mrb_value
flo_eq(mrb_state *mrb, mrb_value x)
{
mrb_value y;
- volatile mrb_float a, b;
-
mrb_get_args(mrb, "o", &y);
switch (mrb_type(y)) {
case MRB_TT_FIXNUM:
- b = (mrb_float)mrb_fixnum(y);
- break;
+ return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y));
case MRB_TT_FLOAT:
- b = mrb_float(y);
- break;
+ return mrb_bool_value(mrb_float(x) == mrb_float(y));
default:
return mrb_false_value();
}
- a = mrb_float(x);
- return mrb_bool_value(a == b);
}
/* 15.2.8.3.18 */
@@ -474,7 +330,7 @@ flo_infinite_p(mrb_state *mrb, mrb_value num)
mrb_float value = mrb_float(num);
if (isinf(value)) {
- return mrb_fixnum_value( value < 0 ? -1 : 1 );
+ return mrb_fixnum_value(value < 0 ? -1 : 1);
}
return mrb_nil_value();
}
@@ -493,9 +349,7 @@ flo_infinite_p(mrb_state *mrb, mrb_value num)
static mrb_value
flo_finite_p(mrb_state *mrb, mrb_value num)
{
- mrb_float value = mrb_float(num);
-
- return mrb_bool_value(isfinite(value));
+ return mrb_bool_value(isfinite(mrb_float(num)));
}
/* 15.2.9.3.10 */
@@ -583,7 +437,7 @@ flo_round(mrb_state *mrb, mrb_value num)
{
double number, f;
mrb_int ndigits = 0;
- int i;
+ mrb_int i;
mrb_get_args(mrb, "|i", &ndigits);
number = mrb_float(num);
@@ -598,7 +452,7 @@ flo_round(mrb_state *mrb, mrb_value num)
}
f = 1.0;
- i = abs(ndigits);
+ i = ndigits >= 0 ? ndigits : -ndigits;
while (--i >= 0)
f = f*10.0;
@@ -613,12 +467,12 @@ flo_round(mrb_state *mrb, mrb_value num)
/* home-made inline implementation of round(3) */
if (number > 0.0) {
- d = floor(number);
- number = d + (number - d >= 0.5);
+ d = floor(number);
+ number = d + (number - d >= 0.5);
}
else if (number < 0.0) {
- d = ceil(number);
- number = d - (d - number >= 0.5);
+ d = ceil(number);
+ number = d - (d - number >= 0.5);
}
if (ndigits < 0) number *= f;
@@ -687,12 +541,8 @@ int_to_i(mrb_state *mrb, mrb_value num)
return num;
}
-#ifdef MRB_FIXNUM_SHIFT
-#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1-MRB_FIXNUM_SHIFT)/2))
-#else
-#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1)/2))
-#endif
/*tests if N*N would overflow*/
+#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1-MRB_FIXNUM_SHIFT)/2))
#define FIT_SQRT_INT(n) (((n)<SQRT_INT_MAX)&&((n)>=-SQRT_INT_MAX))
mrb_value
@@ -713,7 +563,7 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
if ((a != 0 && c/a != b) || !FIXABLE(c)) {
return mrb_float_value(mrb, (mrb_float)a*(mrb_float)b);
}
- return mrb_fixnum_value(c);
+ return mrb_fixnum_value((mrb_int)c);
}
return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y));
}
@@ -970,19 +820,35 @@ fix_xor(mrb_state *mrb, mrb_value x)
static mrb_value
lshift(mrb_state *mrb, mrb_int val, mrb_int width)
{
- mrb_assert(width >= 0);
- if (width > NUMERIC_SHIFT_WIDTH_MAX) {
- mrb_raisef(mrb, E_RANGE_ERROR, "width(%S) > (%S:MRB_INT_BIT-1)",
- mrb_fixnum_value(width),
- mrb_fixnum_value(NUMERIC_SHIFT_WIDTH_MAX));
+ mrb_assert(width > 0);
+ if (val > 0) {
+ if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
+ (val > (MRB_INT_MAX >> width))) {
+ goto bit_overflow;
+ }
+ } else {
+ if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
+ (val < (MRB_INT_MIN >> width))) {
+ goto bit_overflow;
+ }
}
+
return mrb_fixnum_value(val << width);
+
+bit_overflow:
+ {
+ mrb_float f = (mrb_float)val;
+ while (width--) {
+ f *= 2;
+ }
+ return mrb_float_value(mrb, f);
+ }
}
static mrb_value
rshift(mrb_int val, mrb_int width)
{
- mrb_assert(width >= 0);
+ mrb_assert(width > 0);
if (width >= NUMERIC_SHIFT_WIDTH_MAX) {
if (val < 0) {
return mrb_fixnum_value(-1);
@@ -1004,7 +870,7 @@ fix_shift_get_width(mrb_state *mrb, mrb_int *width)
/* 15.2.8.3.12 */
/*
* call-seq:
- * fix << count -> integer
+ * fix << count -> integer or float
*
* Shifts _fix_ left _count_ positions (right if _count_ is negative).
*/
@@ -1029,7 +895,7 @@ fix_lshift(mrb_state *mrb, mrb_value x)
/* 15.2.8.3.13 */
/*
* call-seq:
- * fix >> count -> integer
+ * fix >> count -> integer or float
*
* Shifts _fix_ right _count_ positions (left if _count_ is negative).
*/
@@ -1080,14 +946,14 @@ fix_to_f(mrb_state *mrb, mrb_value num)
* FloatDomainError: Infinity
*/
/* ------------------------------------------------------------------------*/
-mrb_value
+MRB_API mrb_value
mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
{
mrb_int z;
- if (mrb_float_p(x)) {
- mrb_raise(mrb, E_TYPE_ERROR, "non float value");
- z = 0; /* not reached. just suppress warnings. */
+ if (!mrb_float_p(x)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "non float value");
+ z = 0; /* not reached. just suppress warnings. */
}
else {
mrb_float d = mrb_float(x);
@@ -1098,7 +964,12 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
if (isnan(d)) {
mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
}
- z = (mrb_int)d;
+ if (FIXABLE(d)) {
+ z = (mrb_int)d;
+ }
+ else {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x);
+ }
}
return mrb_fixnum_value(z);
}
@@ -1178,7 +1049,7 @@ fix_minus(mrb_state *mrb, mrb_value self)
}
-mrb_value
+MRB_API mrb_value
mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base)
{
char buf[MRB_INT_BIT+1];
@@ -1309,7 +1180,7 @@ mrb_init_numeric(mrb_state *mrb)
mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE());
/* Fixnum Class */
- fixnum = mrb->fixnum_class = mrb_define_class(mrb, "Fixnum", integer);
+ mrb->fixnum_class = fixnum = mrb_define_class(mrb, "Fixnum", integer);
mrb_define_method(mrb, fixnum, "+", fix_plus, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
mrb_define_method(mrb, fixnum, "-", fix_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */
mrb_define_method(mrb, fixnum, "*", fix_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */
@@ -1329,7 +1200,7 @@ mrb_init_numeric(mrb_state *mrb)
mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */
/* Float Class */
- fl = mrb->float_class = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */
+ mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */
mrb_undef_class_method(mrb, fl, "new");
mrb_define_method(mrb, fl, "+", flo_plus, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */
mrb_define_method(mrb, fl, "-", flo_minus, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */
@@ -1351,4 +1222,11 @@ mrb_init_numeric(mrb_state *mrb)
mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */
mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE());
+
+#ifdef INFINITY
+ mrb_define_const(mrb, fl, "INFINITY", mrb_float_value(mrb, INFINITY));
+#endif
+#ifdef NAN
+ mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN));
+#endif
}
diff --git a/src/object.c b/src/object.c
index 6d39254dd..bb1a4ebc4 100644
--- a/src/object.c
+++ b/src/object.c
@@ -4,12 +4,12 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/numeric.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
-mrb_bool
+MRB_API mrb_bool
mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
{
if (mrb_type(v1) != mrb_type(v2)) return FALSE;
@@ -19,9 +19,9 @@ mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
case MRB_TT_FALSE:
case MRB_TT_FIXNUM:
- return (v1.value.i == v2.value.i);
+ return (mrb_fixnum(v1) == mrb_fixnum(v2));
case MRB_TT_SYMBOL:
- return (v1.value.sym == v2.value.sym);
+ return (mrb_symbol(v1) == mrb_symbol(v2));
case MRB_TT_FLOAT:
return (mrb_float(v1) == mrb_float(v2));
@@ -31,14 +31,14 @@ mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
}
}
-mrb_bool
+MRB_API mrb_bool
mrb_obj_equal(mrb_state *mrb, mrb_value v1, mrb_value v2)
{
/* temporary definition */
return mrb_obj_eq(mrb, v1, v2);
}
-mrb_bool
+MRB_API mrb_bool
mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
{
mrb_value result;
@@ -264,7 +264,7 @@ mrb_init_object(mrb_state *mrb)
struct RClass *t;
struct RClass *f;
- n = mrb->nil_class = mrb_define_class(mrb, "NilClass", mrb->object_class);
+ mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class);
mrb_undef_class_method(mrb, n, "new");
mrb_define_method(mrb, n, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.4.3.1 */
mrb_define_method(mrb, n, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.4.3.2 */
@@ -273,7 +273,7 @@ mrb_init_object(mrb_state *mrb)
mrb_define_method(mrb, n, "to_s", nil_to_s, MRB_ARGS_NONE()); /* 15.2.4.3.5 */
mrb_define_method(mrb, n, "inspect", nil_inspect, MRB_ARGS_NONE());
- t = mrb->true_class = mrb_define_class(mrb, "TrueClass", mrb->object_class);
+ mrb->true_class = t = mrb_define_class(mrb, "TrueClass", mrb->object_class);
mrb_undef_class_method(mrb, t, "new");
mrb_define_method(mrb, t, "&", true_and, MRB_ARGS_REQ(1)); /* 15.2.5.3.1 */
mrb_define_method(mrb, t, "^", true_xor, MRB_ARGS_REQ(1)); /* 15.2.5.3.2 */
@@ -281,7 +281,7 @@ mrb_init_object(mrb_state *mrb)
mrb_define_method(mrb, t, "|", true_or, MRB_ARGS_REQ(1)); /* 15.2.5.3.4 */
mrb_define_method(mrb, t, "inspect", true_to_s, MRB_ARGS_NONE());
- f = mrb->false_class = mrb_define_class(mrb, "FalseClass", mrb->object_class);
+ mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class);
mrb_undef_class_method(mrb, f, "new");
mrb_define_method(mrb, f, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.6.3.1 */
mrb_define_method(mrb, f, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.6.3.2 */
@@ -310,16 +310,13 @@ convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *metho
if (!mrb_respond_to(mrb, val, m)) {
if (raise) {
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into %S", inspect_type(mrb, val), mrb_str_new_cstr(mrb, tname));
- return mrb_nil_value();
- }
- else {
- return mrb_nil_value();
}
+ return mrb_nil_value();
}
return mrb_funcall_argv(mrb, val, m, 0, 0);
}
-mrb_value
+MRB_API mrb_value
mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method)
{
mrb_value v;
@@ -332,7 +329,7 @@ mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method)
return v;
}
-mrb_value
+MRB_API mrb_value
mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method)
{
mrb_value v;
@@ -346,7 +343,7 @@ mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char
return v;
}
-mrb_value
+MRB_API mrb_value
mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method)
{
mrb_value v;
@@ -386,7 +383,7 @@ static const struct types {
{-1, 0}
};
-void
+MRB_API void
mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
{
const struct types *type = builtin_types;
@@ -407,7 +404,7 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
else if (mrb_type(x) == MRB_TT_SYMBOL) {
etype = "Symbol";
}
- else if (mrb_special_const_p(x)) {
+ else if (mrb_immediate_p(x)) {
etype = RSTRING_PTR(mrb_obj_as_string(mrb, x));
}
else {
@@ -431,10 +428,10 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
* Returns a string representing <i>obj</i>. The default
* <code>to_s</code> prints the object's class and an encoding of the
* object id. As a special case, the top-level object that is the
- * initial execution context of Ruby programs returns ``main.''
+ * initial execution context of Ruby programs returns "main."
*/
-mrb_value
+MRB_API mrb_value
mrb_any_to_s(mrb_state *mrb, mrb_value obj)
{
mrb_value str = mrb_str_buf_new(mrb, 20);
@@ -475,7 +472,7 @@ mrb_any_to_s(mrb_state *mrb, mrb_value obj)
* b.kind_of? M #=> true
*/
-mrb_bool
+MRB_API mrb_bool
mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
{
struct RClass *cl = mrb_class(mrb, obj);
@@ -484,12 +481,14 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
case MRB_TT_MODULE:
case MRB_TT_CLASS:
case MRB_TT_ICLASS:
+ case MRB_TT_SCLASS:
break;
default:
mrb_raise(mrb, E_TYPE_ERROR, "class or module required");
}
+ MRB_CLASS_ORIGIN(c);
while (cl) {
if (cl == c || cl->mt == c->mt)
return TRUE;
@@ -513,13 +512,13 @@ mrb_to_integer(mrb_state *mrb, mrb_value val, const char *method)
return v;
}
-mrb_value
+MRB_API mrb_value
mrb_to_int(mrb_state *mrb, mrb_value val)
{
return mrb_to_integer(mrb, val, "to_int");
}
-static mrb_value
+MRB_API mrb_value
mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base)
{
mrb_value tmp;
@@ -540,13 +539,17 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base)
if (base != 0) goto arg_error;
return val;
+ case MRB_TT_STRING:
+ string_conv:
+ return mrb_str_to_inum(mrb, val, base, TRUE);
+
default:
break;
}
if (base != 0) {
tmp = mrb_check_string_type(mrb, val);
if (!mrb_nil_p(tmp)) {
- return mrb_str_to_inum(mrb, val, base, TRUE);
+ goto string_conv;
}
arg_error:
mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value");
@@ -558,13 +561,13 @@ arg_error:
return tmp;
}
-mrb_value
+MRB_API mrb_value
mrb_Integer(mrb_state *mrb, mrb_value val)
{
return mrb_convert_to_integer(mrb, val, 0);
}
-mrb_value
+MRB_API mrb_value
mrb_Float(mrb_state *mrb, mrb_value val)
{
if (mrb_nil_p(val)) {
@@ -585,13 +588,13 @@ mrb_Float(mrb_state *mrb, mrb_value val)
}
}
-mrb_value
+MRB_API mrb_value
mrb_inspect(mrb_state *mrb, mrb_value obj)
{
return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0));
}
-mrb_bool
+MRB_API mrb_bool
mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
{
if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE;
diff --git a/src/opcode.h b/src/opcode.h
index 2446f92ed..fe4d17a21 100644
--- a/src/opcode.h
+++ b/src/opcode.h
@@ -1,2 +1,2 @@
/* this header file is to be removed soon. */
-#include "mruby/opcode.h"
+#include <mruby/opcode.h>
diff --git a/src/pool.c b/src/pool.c
index 4d8c42dd1..18f66fc27 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -7,7 +7,7 @@
#include <stddef.h>
#include <stdint.h>
#include <string.h>
-#include "mruby.h"
+#include <mruby.h>
/* configuration section */
/* allocated memory address should be multiple of POOL_ALIGNMENT */
@@ -47,7 +47,7 @@ struct mrb_pool {
# define ALIGN_PADDING(x) (0)
#endif
-mrb_pool*
+MRB_API mrb_pool*
mrb_pool_open(mrb_state *mrb)
{
mrb_pool *pool = (mrb_pool *)mrb_malloc_simple(mrb, sizeof(mrb_pool));
@@ -60,7 +60,7 @@ mrb_pool_open(mrb_state *mrb)
return pool;
}
-void
+MRB_API void
mrb_pool_close(mrb_pool *pool)
{
struct mrb_pool_page *page, *tmp;
@@ -91,7 +91,7 @@ page_alloc(mrb_pool *pool, size_t len)
return page;
}
-void*
+MRB_API void*
mrb_pool_alloc(mrb_pool *pool, size_t len)
{
struct mrb_pool_page *page;
@@ -119,7 +119,7 @@ mrb_pool_alloc(mrb_pool *pool, size_t len)
return page->last;
}
-mrb_bool
+MRB_API mrb_bool
mrb_pool_can_realloc(mrb_pool *pool, void *p, size_t len)
{
struct mrb_pool_page *page;
@@ -140,7 +140,7 @@ mrb_pool_can_realloc(mrb_pool *pool, void *p, size_t len)
return FALSE;
}
-void*
+MRB_API void*
mrb_pool_realloc(mrb_pool *pool, void *p, size_t oldlen, size_t newlen)
{
struct mrb_pool_page *page;
@@ -166,6 +166,9 @@ mrb_pool_realloc(mrb_pool *pool, void *p, size_t oldlen, size_t newlen)
page = page->next;
}
np = mrb_pool_alloc(pool, newlen);
+ if (np == NULL) {
+ return NULL;
+ }
memcpy(np, p, oldlen);
return np;
}
diff --git a/src/print.c b/src/print.c
index f4ed85601..03b5eadfa 100644
--- a/src/print.c
+++ b/src/print.c
@@ -4,66 +4,44 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#ifndef MRB_DISABLE_STDIO
static void
-printstr(mrb_state *mrb, mrb_value obj)
+printstr(mrb_value obj, FILE *stream)
{
-#ifdef ENABLE_STDIO
- char *s;
- int len;
-
if (mrb_string_p(obj)) {
- s = RSTRING_PTR(obj);
- len = RSTRING_LEN(obj);
- fwrite(s, len, 1, stdout);
+ fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stream);
+ putc('\n', stream);
}
-#endif
}
+#else
+# define printstr(obj, stream) (void)0
+#endif
-void
+MRB_API void
mrb_p(mrb_state *mrb, mrb_value obj)
{
-#ifdef ENABLE_STDIO
- obj = mrb_funcall(mrb, obj, "inspect", 0);
- printstr(mrb, obj);
- putc('\n', stdout);
-#endif
+ printstr(mrb_inspect(mrb, obj), stdout);
}
-void
+MRB_API void
mrb_print_error(mrb_state *mrb)
{
-#ifdef ENABLE_STDIO
- mrb_value s;
-
mrb_print_backtrace(mrb);
- s = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0);
- if (mrb_string_p(s)) {
- fwrite(RSTRING_PTR(s), RSTRING_LEN(s), 1, stderr);
- putc('\n', stderr);
- }
-#endif
+ printstr(mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0), stderr);
}
-void
+MRB_API void
mrb_show_version(mrb_state *mrb)
{
- mrb_value msg;
-
- msg = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION"));
- printstr(mrb, msg);
- printstr(mrb, mrb_str_new_lit(mrb, "\n"));
+ printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")), stdout);
}
-void
+MRB_API void
mrb_show_copyright(mrb_state *mrb)
{
- mrb_value msg;
-
- msg = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT"));
- printstr(mrb, msg);
- printstr(mrb, mrb_str_new_lit(mrb, "\n"));
+ printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")), stdout);
}
diff --git a/src/proc.c b/src/proc.c
index d4fe86680..34037b167 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -4,10 +4,10 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
-#include "mruby/opcode.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/opcode.h>
static mrb_code call_iseq[] = {
MKOP_A(OP_CALL, 0),
@@ -34,17 +34,27 @@ mrb_proc_new(mrb_state *mrb, mrb_irep *irep)
return p;
}
-static inline void
+static struct REnv*
+env_new(mrb_state *mrb, int nlocals)
+{
+ struct REnv *e;
+
+ e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env);
+ MRB_SET_ENV_STACK_LEN(e, nlocals);
+ e->mid = mrb->c->ci->mid;
+ e->cioff = mrb->c->ci - mrb->c->cibase;
+ e->stack = mrb->c->stack;
+
+ return e;
+}
+
+static void
closure_setup(mrb_state *mrb, struct RProc *p, int nlocals)
{
struct REnv *e;
if (!mrb->c->ci->env) {
- e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env);
- MRB_ENV_STACK_LEN(e)= (unsigned int)nlocals;
- e->mid = mrb->c->ci->mid;
- e->cioff = mrb->c->ci - mrb->c->cibase;
- e->stack = mrb->c->stack;
+ e = env_new(mrb, nlocals);
mrb->c->ci->env = e;
}
else {
@@ -62,7 +72,7 @@ mrb_closure_new(mrb_state *mrb, mrb_irep *irep)
return p;
}
-struct RProc *
+MRB_API struct RProc *
mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func)
{
struct RProc *p;
@@ -75,16 +85,57 @@ mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func)
return p;
}
-struct RProc *
-mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals)
+MRB_API struct RProc *
+mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv)
{
struct RProc *p = mrb_proc_new_cfunc(mrb, func);
-
- closure_setup(mrb, p, nlocals);
+ struct REnv *e;
+ int i;
+
+ p->env = e = env_new(mrb, argc);
+ mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env);
+ MRB_ENV_UNSHARE_STACK(e);
+ e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc);
+ if (argv) {
+ for (i = 0; i < argc; ++i) {
+ e->stack[i] = argv[i];
+ }
+ }
+ else {
+ for (i = 0; i < argc; ++i) {
+ SET_NIL_VALUE(e->stack[i]);
+ }
+ }
return p;
}
-void
+MRB_API struct RProc *
+mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals)
+{
+ return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL);
+}
+
+MRB_API mrb_value
+mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
+{
+ struct RProc *p = mrb->c->ci->proc;
+ struct REnv *e = p->env;
+
+ if (!MRB_PROC_CFUNC_P(p)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc.");
+ }
+ if (!e) {
+ mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv.");
+ }
+ if (idx < 0 || MRB_ENV_STACK_LEN(e) <= idx) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %S (expected: 0 <= index < %S)",
+ mrb_fixnum_value(idx), mrb_fixnum_value(MRB_ENV_STACK_LEN(e)));
+ }
+
+ return e->stack[idx];
+}
+
+MRB_API void
mrb_proc_copy(struct RProc *a, struct RProc *b)
{
a->flags = b->flags;
@@ -149,13 +200,25 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self)
{
struct RProc *p = mrb_proc_ptr(self);
mrb_code *iseq = mrb_proc_iseq(mrb, p);
- mrb_aspec aspec = GETARG_Ax(*iseq);
- int ma, ra, pa, arity;
+ mrb_aspec aspec;
+ int ma, op, ra, pa, arity;
+
+ if (MRB_PROC_CFUNC_P(p)) {
+ /* TODO cfunc aspec not implemented yet */
+ return mrb_fixnum_value(-1);
+ }
+
+ /* arity is depend on OP_ENTER */
+ if (GET_OPCODE(*iseq) != OP_ENTER) {
+ return mrb_fixnum_value(0);
+ }
+ aspec = GETARG_Ax(*iseq);
ma = MRB_ASPEC_REQ(aspec);
+ op = MRB_ASPEC_OPT(aspec);
ra = MRB_ASPEC_REST(aspec);
pa = MRB_ASPEC_POST(aspec);
- arity = ra ? -(ma + pa + 1) : ma + pa;
+ arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa;
return mrb_fixnum_value(arity);
}
@@ -179,6 +242,9 @@ proc_lambda(mrb_state *mrb, mrb_value self)
if (mrb_nil_p(blk)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
}
+ if (mrb_type(blk) != MRB_TT_PROC) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
+ }
p = mrb_proc_ptr(blk);
if (!MRB_PROC_STRICT_P(p)) {
struct RProc *p2 = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, p->c);
@@ -201,9 +267,6 @@ mrb_init_proc(mrb_state *mrb)
call_irep->iseq = call_iseq;
call_irep->ilen = 1;
- mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); /* 15.2.17 */
- MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC);
-
mrb_define_method(mrb, mrb->proc_class, "initialize", mrb_proc_initialize, MRB_ARGS_NONE());
mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1));
mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE());
diff --git a/src/range.c b/src/range.c
index 53000e660..bc8b11419 100644
--- a/src/range.c
+++ b/src/range.c
@@ -4,11 +4,11 @@
** See Copyright Notice in mruby.h
*/
-#include "mruby.h"
-#include "mruby/class.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/array.h"
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/array.h>
#define RANGE_CLASS (mrb_class_get(mrb, "Range"))
@@ -33,7 +33,7 @@ range_check(mrb_state *mrb, mrb_value a, mrb_value b)
}
}
-mrb_value
+MRB_API mrb_value
mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
{
struct RRange *r;
@@ -234,16 +234,16 @@ mrb_range_include(mrb_state *mrb, mrb_value range)
return mrb_bool_value(include_p);
}
-mrb_bool
+static mrb_bool
range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
{
- mrb_int beg, end, b, e;
+ mrb_int beg, end;
struct RRange *r = mrb_range_ptr(range);
if (mrb_type(range) != MRB_TT_RANGE) return FALSE;
- beg = b = mrb_int(mrb, r->edges->beg);
- end = e = mrb_int(mrb, r->edges->end);
+ beg = mrb_int(mrb, r->edges->beg);
+ end = mrb_int(mrb, r->edges->end);
if (beg < 0) {
beg += len;
@@ -266,7 +266,7 @@ range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb
return TRUE;
}
-mrb_bool
+MRB_API mrb_bool
mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len)
{
return range_beg_len(mrb, range, begp, lenp, len, TRUE);
@@ -290,7 +290,7 @@ range_to_s(mrb_state *mrb, mrb_value range)
str2 = mrb_obj_as_string(mrb, r->edges->end);
str = mrb_str_dup(mrb, str);
mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
- mrb_str_append(mrb, str, str2);
+ mrb_str_cat_str(mrb, str, str2);
return str;
}
@@ -315,7 +315,7 @@ range_inspect(mrb_state *mrb, mrb_value range)
str2 = mrb_inspect(mrb, r->edges->end);
str = mrb_str_dup(mrb, str);
mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
- mrb_str_append(mrb, str, str2);
+ mrb_str_cat_str(mrb, str, str2);
return str;
}
diff --git a/src/state.c b/src/state.c
index 3e82a159d..c8c0658e4 100644
--- a/src/state.c
+++ b/src/state.c
@@ -6,14 +6,17 @@
#include <stdlib.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/irep.h"
-#include "mruby/variable.h"
-#include "mruby/debug.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/irep.h>
+#include <mruby/variable.h>
+#include <mruby/debug.h>
+#include <mruby/string.h>
-void mrb_init_heap(mrb_state*);
void mrb_init_core(mrb_state*);
+void mrb_init_mrbgems(mrb_state*);
+
+void mrb_gc_init(mrb_state*, mrb_gc *gc);
+void mrb_gc_destroy(mrb_state*, mrb_gc *gc);
static mrb_value
inspect_main(mrb_state *mrb, mrb_value mod)
@@ -21,42 +24,33 @@ inspect_main(mrb_state *mrb, mrb_value mod)
return mrb_str_new_lit(mrb, "main");
}
-mrb_state*
-mrb_open_allocf(mrb_allocf f, void *ud)
+MRB_API mrb_state*
+mrb_open_core(mrb_allocf f, void *ud)
{
static const mrb_state mrb_state_zero = { 0 };
static const struct mrb_context mrb_context_zero = { 0 };
mrb_state *mrb;
-#ifdef MRB_NAN_BOXING
- mrb_static_assert(sizeof(void*) == 4, "when using NaN boxing sizeof pointer must be 4 byte");
-#endif
-
mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud);
if (mrb == NULL) return NULL;
*mrb = mrb_state_zero;
- mrb->ud = ud;
+ mrb->allocf_ud = ud;
mrb->allocf = f;
- mrb->current_white_part = MRB_GC_WHITE_A;
mrb->atexit_stack_len = 0;
-#ifndef MRB_GC_FIXED_ARENA
- mrb->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE);
- mrb->arena_capa = MRB_GC_ARENA_SIZE;
-#endif
-
- mrb_init_heap(mrb);
+ mrb_gc_init(mrb, &mrb->gc);
mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
*mrb->c = mrb_context_zero;
mrb->root_c = mrb->c;
+
mrb_init_core(mrb);
return mrb;
}
-static void*
-allocf(mrb_state *mrb, void *p, size_t size, void *ud)
+void*
+mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud)
{
if (size == 0) {
free(p);
@@ -72,7 +66,7 @@ struct alloca_header {
char buf[];
};
-void*
+MRB_API void*
mrb_alloca(mrb_state *mrb, size_t size)
{
struct alloca_header *p;
@@ -99,16 +93,31 @@ mrb_alloca_free(mrb_state *mrb)
}
}
-mrb_state*
+MRB_API mrb_state*
mrb_open(void)
{
- mrb_state *mrb = mrb_open_allocf(allocf, NULL);
+ mrb_state *mrb = mrb_open_allocf(mrb_default_allocf, NULL);
+
+ return mrb;
+}
+
+MRB_API mrb_state*
+mrb_open_allocf(mrb_allocf f, void *ud)
+{
+ mrb_state *mrb = mrb_open_core(f, ud);
+
+ if (mrb == NULL) {
+ return NULL;
+ }
+#ifndef DISABLE_GEMS
+ mrb_init_mrbgems(mrb);
+ mrb_gc_arena_restore(mrb, 0);
+#endif
return mrb;
}
void mrb_free_symtbl(mrb_state *mrb);
-void mrb_free_heap(mrb_state *mrb);
void
mrb_irep_incref(mrb_state *mrb, mrb_irep *irep)
@@ -168,7 +177,7 @@ mrb_str_pool(mrb_state *mrb, mrb_value str)
ns->tt = MRB_TT_STRING;
ns->c = mrb->string_class;
- if (s->flags & MRB_STR_NOFREE) {
+ if (RSTR_NOFREE_P(s)) {
ns->flags = MRB_STR_NOFREE;
ns->as.heap.ptr = s->as.heap.ptr;
ns->as.heap.len = s->as.heap.len;
@@ -176,9 +185,9 @@ mrb_str_pool(mrb_state *mrb, mrb_value str)
}
else {
ns->flags = 0;
- if (s->flags & MRB_STR_EMBED) {
+ if (RSTR_EMBED_P(s)) {
ptr = s->as.ary;
- len = (mrb_int)((s->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT);
+ len = RSTR_EMBED_LEN(s);
}
else {
ptr = s->as.heap.ptr;
@@ -186,9 +195,8 @@ mrb_str_pool(mrb_state *mrb, mrb_value str)
}
if (len < RSTRING_EMBED_LEN_MAX) {
- ns->flags |= MRB_STR_EMBED;
- ns->flags &= ~MRB_STR_EMBED_LEN_MASK;
- ns->flags |= (size_t)len << MRB_STR_EMBED_LEN_SHIFT;
+ RSTR_SET_EMBED_FLAG(ns);
+ RSTR_SET_EMBED_LEN(ns, len);
if (ptr) {
memcpy(ns->as.ary, ptr, len);
}
@@ -207,7 +215,7 @@ mrb_str_pool(mrb_state *mrb, mrb_value str)
return mrb_obj_value(ns);
}
-void
+MRB_API void
mrb_free_context(mrb_state *mrb, struct mrb_context *c)
{
if (!c) return;
@@ -218,9 +226,10 @@ mrb_free_context(mrb_state *mrb, struct mrb_context *c)
mrb_free(mrb, c);
}
-void
+MRB_API void
mrb_close(mrb_state *mrb)
{
+ if (!mrb) return;
if (mrb->atexit_stack_len > 0) {
mrb_int i;
for (i = mrb->atexit_stack_len; i > 0; --i) {
@@ -235,15 +244,12 @@ mrb_close(mrb_state *mrb)
mrb_gc_free_gv(mrb);
mrb_free_context(mrb, mrb->root_c);
mrb_free_symtbl(mrb);
- mrb_free_heap(mrb);
mrb_alloca_free(mrb);
-#ifndef MRB_GC_FIXED_ARENA
- mrb_free(mrb, mrb->arena);
-#endif
+ mrb_gc_destroy(mrb, &mrb->gc);
mrb_free(mrb, mrb);
}
-mrb_irep*
+MRB_API mrb_irep*
mrb_add_irep(mrb_state *mrb)
{
static const mrb_irep mrb_irep_zero = { 0 };
@@ -256,7 +262,7 @@ mrb_add_irep(mrb_state *mrb)
return irep;
}
-mrb_value
+MRB_API mrb_value
mrb_top_self(mrb_state *mrb)
{
if (!mrb->top_self) {
@@ -267,7 +273,7 @@ mrb_top_self(mrb_state *mrb)
return mrb_obj_value(mrb->top_self);
}
-void
+MRB_API void
mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f)
{
#ifdef MRB_FIXED_STATE_ATEXIT_STACK
diff --git a/src/string.c b/src/string.c
index 6570c89fb..6664eabd6 100644
--- a/src/string.c
+++ b/src/string.c
@@ -4,20 +4,21 @@
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
+#ifdef _MSC_VER
+# define _CRT_NONSTDC_NO_DEPRECATE
+#endif
+
#include <float.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/re.h"
-
-const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/re.h>
typedef struct mrb_shared_string {
mrb_bool nofree : 1;
@@ -26,129 +27,39 @@ typedef struct mrb_shared_string {
mrb_int len;
} mrb_shared_string;
-static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2);
-static mrb_value mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
-
-mrb_int
-mrb_str_strlen(mrb_state *mrb, struct RString *s)
-{
- mrb_int i, max = RSTR_LEN(s);
- char *p = RSTR_PTR(s);
-
- if (!p) return 0;
- for (i=0; i<max; i++) {
- if (p[i] == '\0') {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
- }
- }
- return max;
-}
-
-static inline void
-resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity)
-{
- if (RSTR_EMBED_P(s)) {
- if (RSTRING_EMBED_LEN_MAX < capacity) {
- char *const tmp = (char *)mrb_malloc(mrb, capacity+1);
- const mrb_int len = RSTR_EMBED_LEN(s);
- memcpy(tmp, s->as.ary, len);
- RSTR_UNSET_EMBED_FLAG(s);
- s->as.heap.ptr = tmp;
- s->as.heap.len = len;
- s->as.heap.aux.capa = capacity;
- }
- }
- else {
- s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
- s->as.heap.aux.capa = capacity;
- }
-}
+const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
-static void
-str_decref(mrb_state *mrb, mrb_shared_string *shared)
-{
- shared->refcnt--;
- if (shared->refcnt == 0) {
- if (!shared->nofree) {
- mrb_free(mrb, shared->ptr);
- }
- mrb_free(mrb, shared);
- }
-}
+#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class))
-void
-mrb_str_modify(mrb_state *mrb, struct RString *s)
+static struct RString*
+str_new_static(mrb_state *mrb, const char *p, size_t len)
{
- if (RSTR_SHARED_P(s)) {
- mrb_shared_string *shared = s->as.heap.aux.shared;
-
- if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) {
- s->as.heap.ptr = shared->ptr;
- s->as.heap.aux.capa = shared->len;
- RSTR_PTR(s)[s->as.heap.len] = '\0';
- mrb_free(mrb, shared);
- }
- else {
- char *ptr, *p;
- mrb_int len;
-
- p = RSTR_PTR(s);
- len = s->as.heap.len;
- ptr = (char *)mrb_malloc(mrb, (size_t)len + 1);
- if (p) {
- memcpy(ptr, p, len);
- }
- ptr[len] = '\0';
- s->as.heap.ptr = ptr;
- s->as.heap.aux.capa = len;
- str_decref(mrb, shared);
- }
- RSTR_UNSET_SHARED_FLAG(s);
- return;
- }
- if (RSTR_NOFREE_P(s)) {
- char *p = s->as.heap.ptr;
+ struct RString *s;
- s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1);
- if (p) {
- memcpy(RSTR_PTR(s), p, s->as.heap.len);
- }
- RSTR_PTR(s)[s->as.heap.len] = '\0';
- s->as.heap.aux.capa = s->as.heap.len;
- RSTR_UNSET_NOFREE_FLAG(s);
- return;
+ if (len >= MRB_INT_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
}
-}
-
-mrb_value
-mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
-{
- mrb_int slen;
- struct RString *s = mrb_str_ptr(str);
+ s = mrb_obj_alloc_string(mrb);
+ s->as.heap.len = len;
+ s->as.heap.aux.capa = 0; /* nofree */
+ s->as.heap.ptr = (char *)p;
+ s->flags = MRB_STR_NOFREE;
- mrb_str_modify(mrb, s);
- slen = RSTR_LEN(s);
- if (len != slen) {
- if (slen < len || slen - len > 256) {
- resize_capa(mrb, s, len);
- }
- RSTR_SET_LEN(s, len);
- RSTR_PTR(s)[len] = '\0'; /* sentinel */
- }
- return str;
+ return s;
}
-#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class))
-
static struct RString*
str_new(mrb_state *mrb, const char *p, size_t len)
{
struct RString *s;
+ if (p && mrb_ro_data_p(p)) {
+ return str_new_static(mrb, p, len);
+ }
s = mrb_obj_alloc_string(mrb);
if (len < RSTRING_EMBED_LEN_MAX) {
RSTR_SET_EMBED_FLAG(s);
- RSTR_SET_EMBED_LEN(s,len);
+ RSTR_SET_EMBED_LEN(s, len);
if (p) {
memcpy(s->as.ary, p, len);
}
@@ -167,7 +78,7 @@ str_new(mrb_state *mrb, const char *p, size_t len)
return s;
}
-static void
+static inline void
str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj)
{
s->c = mrb_str_ptr(obj)->c;
@@ -186,7 +97,7 @@ mrb_str_new_empty(mrb_state *mrb, mrb_value str)
# define MRB_STR_BUF_MIN_SIZE 128
#endif
-mrb_value
+MRB_API mrb_value
mrb_str_buf_new(mrb_state *mrb, size_t capa)
{
struct RString *s;
@@ -207,6 +118,26 @@ mrb_str_buf_new(mrb_state *mrb, size_t capa)
return mrb_obj_value(s);
}
+static inline void
+resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity)
+{
+ if (RSTR_EMBED_P(s)) {
+ if (RSTRING_EMBED_LEN_MAX < capacity) {
+ char *const tmp = (char *)mrb_malloc(mrb, capacity+1);
+ const mrb_int len = RSTR_EMBED_LEN(s);
+ memcpy(tmp, s->as.ary, len);
+ RSTR_UNSET_EMBED_FLAG(s);
+ s->as.heap.ptr = tmp;
+ s->as.heap.len = len;
+ s->as.heap.aux.capa = capacity;
+ }
+ }
+ else {
+ s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1);
+ s->as.heap.aux.capa = capacity;
+ }
+}
+
static void
str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len)
{
@@ -248,7 +179,7 @@ str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len)
RSTR_PTR(s)[total] = '\0'; /* sentinel */
}
-mrb_value
+MRB_API mrb_value
mrb_str_new(mrb_state *mrb, const char *p, size_t len)
{
return mrb_obj_value(str_new(mrb, p, len));
@@ -261,7 +192,7 @@ mrb_str_new(mrb_state *mrb, const char *p, size_t len)
* Returns a new string object containing a copy of <i>str</i>.
*/
-mrb_value
+MRB_API mrb_value
mrb_str_new_cstr(mrb_state *mrb, const char *p)
{
struct RString *s;
@@ -279,20 +210,23 @@ mrb_str_new_cstr(mrb_state *mrb, const char *p)
return mrb_obj_value(s);
}
-mrb_value
+MRB_API mrb_value
mrb_str_new_static(mrb_state *mrb, const char *p, size_t len)
{
- struct RString *s;
+ struct RString *s = str_new_static(mrb, p, len);
+ return mrb_obj_value(s);
+}
- if (len >= MRB_INT_MAX) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big");
+static void
+str_decref(mrb_state *mrb, mrb_shared_string *shared)
+{
+ shared->refcnt--;
+ if (shared->refcnt == 0) {
+ if (!shared->nofree) {
+ mrb_free(mrb, shared->ptr);
+ }
+ mrb_free(mrb, shared);
}
- s = mrb_obj_alloc_string(mrb);
- s->as.heap.len = len;
- s->as.heap.aux.capa = 0; /* nofree */
- s->as.heap.ptr = (char *)p;
- s->flags = MRB_STR_NOFREE;
- return mrb_obj_value(s);
}
void
@@ -306,20 +240,126 @@ mrb_gc_free_str(mrb_state *mrb, struct RString *str)
mrb_free(mrb, str->as.heap.ptr);
}
-char *
-mrb_str_to_cstr(mrb_state *mrb, mrb_value str0)
+#ifdef MRB_UTF8_STRING
+static const char utf8len_codepage[256] =
{
- struct RString *s;
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1,
+};
- if (!mrb_string_p(str0)) {
- mrb_raise(mrb, E_TYPE_ERROR, "expected String");
+static mrb_int
+utf8len(const char* p, const char* e)
+{
+ mrb_int len;
+ mrb_int i;
+
+ len = utf8len_codepage[(unsigned char)*p];
+ if (p + len > e) return 1;
+ for (i = 1; i < len; ++i)
+ if ((p[i] & 0xc0) != 0x80)
+ return 1;
+ return len;
+}
+
+static mrb_int
+utf8_strlen(mrb_value str, mrb_int len)
+{
+ mrb_int total = 0;
+ char* p = RSTRING_PTR(str);
+ char* e = p;
+ e += len < 0 ? RSTRING_LEN(str) : len;
+ while (p<e) {
+ p += utf8len(p, e);
+ total++;
}
+ return total;
+}
- s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0));
- if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
+#define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1)
+
+/* map character index to byte offset index */
+static mrb_int
+chars2bytes(mrb_value s, mrb_int off, mrb_int idx)
+{
+ mrb_int i, b, n;
+ const char *p = RSTRING_PTR(s) + off;
+ const char *e = RSTRING_END(s);
+
+ for (b=i=0; p<e && i<idx; i++) {
+ n = utf8len(p, e);
+ b += n;
+ p += n;
}
- return RSTR_PTR(s);
+ return b;
+}
+
+/* map byte offset to character index */
+static mrb_int
+bytes2chars(char *p, mrb_int bi)
+{
+ mrb_int i, b, n;
+
+ for (b=i=0; b<bi; i++) {
+ n = utf8len(p, p+bi);
+ b += n;
+ p += n;
+ }
+ return i;
+}
+
+#else
+#define RSTRING_CHAR_LEN(s) RSTRING_LEN(s)
+#define chars2bytes(p, off, ci) (ci)
+#define bytes2chars(p, bi) (bi)
+#endif
+
+static inline mrb_int
+mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n)
+{
+ const unsigned char *x = xs, *xe = xs + m;
+ const unsigned char *y = ys;
+ int i, qstable[256];
+
+ /* Preprocessing */
+ for (i = 0; i < 256; ++i)
+ qstable[i] = m + 1;
+ for (; x < xe; ++x)
+ qstable[*x] = xe - x;
+ /* Searching */
+ for (; y + m <= ys + n; y += *(qstable + y[m])) {
+ if (*xs == *y && memcmp(xs, y, m) == 0)
+ return y - ys;
+ }
+ return -1;
+}
+
+static mrb_int
+mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
+{
+ const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0;
+
+ if (m > n) return -1;
+ else if (m == n) {
+ return memcmp(x0, y0, m) == 0 ? 0 : -1;
+ }
+ else if (m < 1) {
+ return 0;
+ }
+ else if (m == 1) {
+ const unsigned char *ys = y, *ye = ys + n;
+ for (; y < ye; ++y) {
+ if (*x == *y)
+ return y - ys;
+ }
+ return -1;
+ }
+ return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n);
}
static void
@@ -360,18 +400,336 @@ str_make_shared(mrb_state *mrb, struct RString *s)
}
}
-/*
- * call-seq:
- * char* str = String("abcd"), len=strlen("abcd")
- *
- * Returns a new string object containing a copy of <i>str</i>.
- */
-const char*
-mrb_str_body(mrb_value str, int *len_p)
+static mrb_value
+byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+{
+ struct RString *orig, *s;
+ mrb_shared_string *shared;
+
+ orig = mrb_str_ptr(str);
+ if (RSTR_EMBED_P(orig)) {
+ s = str_new(mrb, orig->as.ary+beg, len);
+ }
+ else {
+ str_make_shared(mrb, orig);
+ shared = orig->as.heap.aux.shared;
+ s = mrb_obj_alloc_string(mrb);
+ s->as.heap.ptr = orig->as.heap.ptr + beg;
+ s->as.heap.len = len;
+ s->as.heap.aux.shared = shared;
+ RSTR_SET_SHARED_FLAG(s);
+ shared->refcnt++;
+ }
+
+ return mrb_obj_value(s);
+}
+#ifdef MRB_UTF8_STRING
+static inline mrb_value
+str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+{
+ beg = chars2bytes(str, 0, beg);
+ len = chars2bytes(str, beg, len);
+
+ return byte_subseq(mrb, str, beg, len);
+}
+#else
+#define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len)
+#endif
+
+static mrb_value
+str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+{
+ mrb_int clen = RSTRING_CHAR_LEN(str);
+
+ if (len < 0) return mrb_nil_value();
+ if (clen == 0) {
+ len = 0;
+ }
+ else if (beg < 0) {
+ beg = clen + beg;
+ }
+ if (beg > clen) return mrb_nil_value();
+ if (beg < 0) {
+ beg += clen;
+ if (beg < 0) return mrb_nil_value();
+ }
+ if (beg + len > clen)
+ len = clen - beg;
+ if (len <= 0) {
+ len = 0;
+ }
+ return str_subseq(mrb, str, beg, len);
+}
+
+static mrb_int
+str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
+{
+ mrb_int pos;
+ char *s, *sptr;
+ mrb_int len, slen;
+
+ len = RSTRING_LEN(str);
+ slen = RSTRING_LEN(sub);
+ if (offset < 0) {
+ offset += len;
+ if (offset < 0) return -1;
+ }
+ if (len - offset < slen) return -1;
+ s = RSTRING_PTR(str);
+ if (offset) {
+ s += offset;
+ }
+ if (slen == 0) return offset;
+ /* need proceed one character at a time */
+ sptr = RSTRING_PTR(sub);
+ slen = RSTRING_LEN(sub);
+ len = RSTRING_LEN(str) - offset;
+ pos = mrb_memsearch(sptr, slen, s, len);
+ if (pos < 0) return pos;
+ return pos + offset;
+}
+
+static void
+check_frozen(mrb_state *mrb, struct RString *s)
+{
+ if (RSTR_FROZEN_P(s)) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string");
+ }
+}
+
+static mrb_value
+str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
+{
+ long len;
+
+ check_frozen(mrb, s1);
+ len = RSTR_LEN(s2);
+ if (RSTR_SHARED_P(s1)) {
+ str_decref(mrb, s1->as.heap.aux.shared);
+ }
+ else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1)) {
+ mrb_free(mrb, s1->as.heap.ptr);
+ }
+
+ RSTR_UNSET_NOFREE_FLAG(s1);
+
+ if (RSTR_SHARED_P(s2)) {
+L_SHARE:
+ RSTR_UNSET_EMBED_FLAG(s1);
+ s1->as.heap.ptr = s2->as.heap.ptr;
+ s1->as.heap.len = len;
+ s1->as.heap.aux.shared = s2->as.heap.aux.shared;
+ RSTR_SET_SHARED_FLAG(s1);
+ s1->as.heap.aux.shared->refcnt++;
+ }
+ else {
+ if (len <= RSTRING_EMBED_LEN_MAX) {
+ RSTR_UNSET_SHARED_FLAG(s1);
+ RSTR_SET_EMBED_FLAG(s1);
+ memcpy(s1->as.ary, RSTR_PTR(s2), len);
+ RSTR_SET_EMBED_LEN(s1, len);
+ }
+ else {
+ str_make_shared(mrb, s2);
+ goto L_SHARE;
+ }
+ }
+
+ return mrb_obj_value(s1);
+}
+
+static mrb_int
+str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
+{
+ char *s, *sbeg, *t;
+ struct RString *ps = mrb_str_ptr(str);
+ mrb_int len = RSTRING_LEN(sub);
+
+ /* substring longer than string */
+ if (RSTR_LEN(ps) < len) return -1;
+ if (RSTR_LEN(ps) - pos < len) {
+ pos = RSTR_LEN(ps) - len;
+ }
+ sbeg = RSTR_PTR(ps);
+ s = RSTR_PTR(ps) + pos;
+ t = RSTRING_PTR(sub);
+ if (len) {
+ while (sbeg <= s) {
+ if (memcmp(s, t, len) == 0) {
+ return s - RSTR_PTR(ps);
+ }
+ s--;
+ }
+ return -1;
+ }
+ else {
+ return pos;
+ }
+}
+
+MRB_API mrb_int
+mrb_str_strlen(mrb_state *mrb, struct RString *s)
+{
+ mrb_int i, max = RSTR_LEN(s);
+ char *p = RSTR_PTR(s);
+
+ if (!p) return 0;
+ for (i=0; i<max; i++) {
+ if (p[i] == '\0') {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
+ }
+ }
+ return max;
+}
+
+#ifdef _WIN32
+#include <windows.h>
+
+char*
+mrb_utf8_from_locale(const char *str, size_t len)
+{
+ wchar_t* wcsp;
+ char* mbsp;
+ size_t mbssize, wcssize;
+
+ if (len == 0)
+ return strdup("");
+ if (len == -1)
+ len = strlen(str);
+ wcssize = MultiByteToWideChar(GetACP(), 0, str, len, NULL, 0);
+ wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
+ if (!wcsp)
+ return NULL;
+ wcssize = MultiByteToWideChar(GetACP(), 0, str, len, wcsp, wcssize + 1);
+ wcsp[wcssize] = 0;
+
+ mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL);
+ mbsp = (char*) malloc((mbssize + 1));
+ if (!mbsp) {
+ free(wcsp);
+ return NULL;
+ }
+ mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL);
+ mbsp[mbssize] = 0;
+ free(wcsp);
+ return mbsp;
+}
+
+char*
+mrb_locale_from_utf8(const char *utf8, size_t len)
+{
+ wchar_t* wcsp;
+ char* mbsp;
+ size_t mbssize, wcssize;
+
+ if (len == 0)
+ return strdup("");
+ if (len == -1)
+ len = strlen(utf8);
+ wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0);
+ wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
+ if (!wcsp)
+ return NULL;
+ wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, wcsp, wcssize + 1);
+ wcsp[wcssize] = 0;
+ mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL);
+ mbsp = (char*) malloc((mbssize + 1));
+ if (!mbsp) {
+ free(wcsp);
+ return NULL;
+ }
+ mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL);
+ mbsp[mbssize] = 0;
+ free(wcsp);
+ return mbsp;
+}
+#endif
+
+MRB_API void
+mrb_str_modify(mrb_state *mrb, struct RString *s)
+{
+ check_frozen(mrb, s);
+ if (RSTR_SHARED_P(s)) {
+ mrb_shared_string *shared = s->as.heap.aux.shared;
+
+ if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) {
+ s->as.heap.ptr = shared->ptr;
+ s->as.heap.aux.capa = shared->len;
+ RSTR_PTR(s)[s->as.heap.len] = '\0';
+ mrb_free(mrb, shared);
+ }
+ else {
+ char *ptr, *p;
+ mrb_int len;
+
+ p = RSTR_PTR(s);
+ len = s->as.heap.len;
+ ptr = (char *)mrb_malloc(mrb, (size_t)len + 1);
+ if (p) {
+ memcpy(ptr, p, len);
+ }
+ ptr[len] = '\0';
+ s->as.heap.ptr = ptr;
+ s->as.heap.aux.capa = len;
+ str_decref(mrb, shared);
+ }
+ RSTR_UNSET_SHARED_FLAG(s);
+ return;
+ }
+ if (RSTR_NOFREE_P(s)) {
+ char *p = s->as.heap.ptr;
+
+ s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1);
+ if (p) {
+ memcpy(RSTR_PTR(s), p, s->as.heap.len);
+ }
+ RSTR_PTR(s)[s->as.heap.len] = '\0';
+ s->as.heap.aux.capa = s->as.heap.len;
+ RSTR_UNSET_NOFREE_FLAG(s);
+ return;
+ }
+}
+
+static mrb_value
+mrb_str_freeze(mrb_state *mrb, mrb_value str)
+{
+ struct RString *s = mrb_str_ptr(str);
+
+ RSTR_SET_FROZEN_FLAG(s);
+ return str;
+}
+
+MRB_API mrb_value
+mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
{
+ mrb_int slen;
struct RString *s = mrb_str_ptr(str);
- *len_p = RSTR_LEN(s);
+ mrb_str_modify(mrb, s);
+ slen = RSTR_LEN(s);
+ if (len != slen) {
+ if (slen < len || slen - len > 256) {
+ resize_capa(mrb, s, len);
+ }
+ RSTR_SET_LEN(s, len);
+ RSTR_PTR(s)[len] = '\0'; /* sentinel */
+ }
+ return str;
+}
+
+MRB_API char*
+mrb_str_to_cstr(mrb_state *mrb, mrb_value str0)
+{
+ struct RString *s;
+
+ if (!mrb_string_p(str0)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "expected String");
+ }
+
+ s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0));
+ if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
+ }
return RSTR_PTR(s);
}
@@ -381,7 +739,7 @@ mrb_str_body(mrb_value str, int *len_p)
*
* Returns a new string object containing a copy of <i>str</i>.
*/
-void
+MRB_API void
mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
{
struct RString *s1 = mrb_str_ptr(self), *s2;
@@ -408,7 +766,7 @@ mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
*
* Returns a new string object containing a copy of <i>str</i>.
*/
-mrb_value
+MRB_API mrb_value
mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b)
{
struct RString *s = mrb_str_ptr(a);
@@ -439,32 +797,26 @@ mrb_str_plus_m(mrb_state *mrb, mrb_value self)
return mrb_str_plus(mrb, self, str);
}
+/* 15.2.10.5.26 */
+/* 15.2.10.5.33 */
/*
* call-seq:
- * len = strlen(String("abcd"))
+ * "abcd".size => int
*
- * Returns a new string object containing a copy of <i>str</i>.
+ * Returns the length of string.
*/
static mrb_value
-mrb_str_bytesize(mrb_state *mrb, mrb_value self)
+mrb_str_size(mrb_state *mrb, mrb_value self)
{
- struct RString *s = mrb_str_ptr(self);
- return mrb_fixnum_value(RSTR_LEN(s));
+ mrb_int len = RSTRING_CHAR_LEN(self);
+ return mrb_fixnum_value(len);
}
-/* 15.2.10.5.26 */
-/* 15.2.10.5.33 */
-/*
- * call-seq:
- * len = strlen(String("abcd"))
- *
- * Returns a new string object containing a copy of <i>str</i>.
- */
-mrb_value
-mrb_str_size(mrb_state *mrb, mrb_value self)
+static mrb_value
+mrb_str_bytesize(mrb_state *mrb, mrb_value self)
{
- struct RString *s = mrb_str_ptr(self);
- return mrb_fixnum_value(RSTR_LEN(s));
+ mrb_int len = RSTRING_LEN(self);
+ return mrb_fixnum_value(len);
}
/* 15.2.10.5.1 */
@@ -521,7 +873,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self)
* = 0
* < -1
*/
-int
+MRB_API int
mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2)
{
mrb_int len;
@@ -607,7 +959,7 @@ str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2)
return FALSE;
}
-mrb_bool
+MRB_API mrb_bool
mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2)
{
if (mrb_immediate_p(str2)) return FALSE;
@@ -643,7 +995,7 @@ mrb_str_equal_m(mrb_state *mrb, mrb_value str1)
return mrb_bool_value(mrb_str_equal(mrb, str1, str2));
}
/* ---------------------------------- */
-mrb_value
+MRB_API mrb_value
mrb_str_to_str(mrb_state *mrb, mrb_value str)
{
mrb_value s;
@@ -658,100 +1010,35 @@ mrb_str_to_str(mrb_state *mrb, mrb_value str)
return str;
}
-char *
+MRB_API const char*
mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr)
{
mrb_value str = mrb_str_to_str(mrb, ptr);
return RSTRING_PTR(str);
}
-static mrb_value
-noregexp(mrb_state *mrb, mrb_value self)
-{
- mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented");
- return mrb_nil_value();
-}
-
-static void
-regexp_check(mrb_state *mrb, mrb_value obj)
+MRB_API mrb_int
+mrb_string_value_len(mrb_state *mrb, mrb_value ptr)
{
- if (mrb_regexp_p(mrb, obj)) {
- noregexp(mrb, obj);
- }
-}
-
-static inline mrb_int
-mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n)
-{
- const unsigned char *x = xs, *xe = xs + m;
- const unsigned char *y = ys;
- int i, qstable[256];
-
- /* Preprocessing */
- for (i = 0; i < 256; ++i)
- qstable[i] = m + 1;
- for (; x < xe; ++x)
- qstable[*x] = xe - x;
- /* Searching */
- for (; y + m <= ys + n; y += *(qstable + y[m])) {
- if (*xs == *y && memcmp(xs, y, m) == 0)
- return y - ys;
- }
- return -1;
+ mrb_value str = mrb_str_to_str(mrb, ptr);
+ return RSTRING_LEN(str);
}
-static mrb_int
-mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n)
+void
+mrb_noregexp(mrb_state *mrb, mrb_value self)
{
- const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0;
-
- if (m > n) return -1;
- else if (m == n) {
- return memcmp(x0, y0, m) == 0 ? 0 : -1;
- }
- else if (m < 1) {
- return 0;
- }
- else if (m == 1) {
- const unsigned char *ys = y, *ye = ys + n;
- for (; y < ye; ++y) {
- if (*x == *y)
- return y - ys;
- }
- return -1;
- }
- return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n);
+ mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented");
}
-static mrb_int
-mrb_str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset)
+void
+mrb_regexp_check(mrb_state *mrb, mrb_value obj)
{
- mrb_int pos;
- char *s, *sptr;
- mrb_int len, slen;
-
- len = RSTRING_LEN(str);
- slen = RSTRING_LEN(sub);
- if (offset < 0) {
- offset += len;
- if (offset < 0) return -1;
- }
- if (len - offset < slen) return -1;
- s = RSTRING_PTR(str);
- if (offset) {
- s += offset;
+ if (mrb_regexp_p(mrb, obj)) {
+ mrb_noregexp(mrb, obj);
}
- if (slen == 0) return offset;
- /* need proceed one character at a time */
- sptr = RSTRING_PTR(sub);
- slen = RSTRING_LEN(sub);
- len = RSTRING_LEN(str) - offset;
- pos = mrb_memsearch(sptr, slen, s, len);
- if (pos < 0) return pos;
- return pos + offset;
}
-mrb_value
+MRB_API mrb_value
mrb_str_dup(mrb_state *mrb, mrb_value str)
{
struct RString *s = mrb_str_ptr(str);
@@ -766,18 +1053,18 @@ mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx)
{
mrb_int idx;
- regexp_check(mrb, indx);
+ mrb_regexp_check(mrb, indx);
switch (mrb_type(indx)) {
case MRB_TT_FIXNUM:
idx = mrb_fixnum(indx);
num_index:
- str = mrb_str_substr(mrb, str, idx, 1);
+ str = str_substr(mrb, str, idx, 1);
if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
return str;
case MRB_TT_STRING:
- if (mrb_str_index(mrb, str, indx, 0) != -1)
+ if (str_index(mrb, str, indx, 0) != -1)
return mrb_str_dup(mrb, indx);
return mrb_nil_value();
@@ -786,15 +1073,20 @@ num_index:
{
mrb_int beg, len;
- len = RSTRING_LEN(str);
+ len = RSTRING_CHAR_LEN(str);
if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) {
- return mrb_str_subseq(mrb, str, beg, len);
+ return str_subseq(mrb, str, beg, len);
}
else {
return mrb_nil_value();
}
}
+ case MRB_TT_FLOAT:
default:
+ indx = mrb_Integer(mrb, indx);
+ if (mrb_nil_p(indx)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum");
+ }
idx = mrb_fixnum(indx);
goto num_index;
}
@@ -831,6 +1123,7 @@ num_index:
*
* a = "hello there"
* a[1] #=> 101(1.8.7) "e"(1.9.2)
+ * a[1.1] #=> "e"(1.9.2)
* a[1,3] #=> "ell"
* a[1..3] #=> "ell"
* a[-3,2] #=> "er"
@@ -848,8 +1141,8 @@ mrb_str_aref_m(mrb_state *mrb, mrb_value str)
argc = mrb_get_args(mrb, "o|o", &a1, &a2);
if (argc == 2) {
- regexp_check(mrb, a1);
- return mrb_str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2));
+ mrb_regexp_check(mrb, a1);
+ return str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2));
}
if (argc != 1) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc));
@@ -919,7 +1212,7 @@ mrb_str_capitalize(mrb_state *mrb, mrb_value self)
/* 15.2.10.5.10 */
/*
* call-seq:
- * str.chomp!(separator=$/) => str or nil
+ * str.chomp!(separator="\n") => str or nil
*
* Modifies <i>str</i> in place as described for <code>String#chomp</code>,
* returning <i>str</i>, or <code>nil</code> if no modifications were made.
@@ -993,7 +1286,7 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
/* 15.2.10.5.9 */
/*
* call-seq:
- * str.chomp(separator=$/) => new_str
+ * str.chomp(separator="\n") => new_str
*
* Returns a new <code>String</code> with the given record separator removed
* from the end of <i>str</i> (if present). If <code>$/</code> has not been
@@ -1036,7 +1329,18 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str)
mrb_str_modify(mrb, s);
if (RSTR_LEN(s) > 0) {
mrb_int len;
+#ifdef MRB_UTF8_STRING
+ const char* t = RSTR_PTR(s), *p = t;
+ const char* e = p + RSTR_LEN(s);
+ while (p<e) {
+ mrb_int clen = utf8len(p, e);
+ if (p + clen>=e) break;
+ p += clen;
+ }
+ len = p - t;
+#else
len = RSTR_LEN(s) - 1;
+#endif
if (RSTR_PTR(s)[len] == '\n') {
if (len > 0 &&
RSTR_PTR(s)[len-1] == '\r') {
@@ -1113,7 +1417,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str)
*
* Returns a copy of <i>str</i> with all uppercase letters replaced with their
* lowercase counterparts. The operation is locale insensitive---only
- * characters ``A'' to ``Z'' are affected.
+ * characters 'A' to 'Z' are affected.
*
* "hEllO".downcase #=> "hello"
*/
@@ -1164,47 +1468,10 @@ mrb_str_eql(mrb_state *mrb, mrb_value self)
return mrb_bool_value(eql_p);
}
-static mrb_value
-mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
-{
- struct RString *orig, *s;
- mrb_shared_string *shared;
-
- orig = mrb_str_ptr(str);
- if (RSTR_EMBED_P(orig)) {
- s = str_new(mrb, orig->as.ary+beg, len);
- } else {
- str_make_shared(mrb, orig);
- shared = orig->as.heap.aux.shared;
- s = mrb_obj_alloc_string(mrb);
- s->as.heap.ptr = orig->as.heap.ptr + beg;
- s->as.heap.len = len;
- s->as.heap.aux.shared = shared;
- RSTR_SET_SHARED_FLAG(s);
- shared->refcnt++;
- }
-
- return mrb_obj_value(s);
-}
-
-mrb_value
+MRB_API mrb_value
mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
{
- if (len < 0) return mrb_nil_value();
- if (!RSTRING_LEN(str)) {
- len = 0;
- }
- if (beg > RSTRING_LEN(str)) return mrb_nil_value();
- if (beg < 0) {
- beg += RSTRING_LEN(str);
- if (beg < 0) return mrb_nil_value();
- }
- if (beg + len > RSTRING_LEN(str))
- len = RSTRING_LEN(str) - beg;
- if (len <= 0) {
- len = 0;
- }
- return mrb_str_subseq(mrb, str, beg, len);
+ return str_substr(mrb, str, beg, len);
}
mrb_int
@@ -1263,7 +1530,7 @@ mrb_str_include(mrb_state *mrb, mrb_value self)
}
else {
str2 = mrb_str_to_str(mrb, str2);
- i = mrb_str_index(mrb, self, str2, 0);
+ i = str_index(mrb, self, str2, 0);
include_p = (i != -1);
}
@@ -1293,12 +1560,12 @@ mrb_str_include(mrb_state *mrb, mrb_value self)
* "hello".index(/[aeiou]/, -3) #=> 4
*/
static mrb_value
-mrb_str_index_m(mrb_state *mrb, mrb_value str)
+mrb_str_index(mrb_state *mrb, mrb_value str)
{
mrb_value *argv;
mrb_int argc;
mrb_value sub;
- mrb_int pos;
+ mrb_int pos, clen;
mrb_get_args(mrb, "*", &argv, &argc);
if (argc == 2) {
@@ -1312,26 +1579,18 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str)
else
sub = mrb_nil_value();
}
- regexp_check(mrb, sub);
+ mrb_regexp_check(mrb, sub);
+ clen = RSTRING_CHAR_LEN(str);
if (pos < 0) {
- pos += RSTRING_LEN(str);
+ pos += clen;
if (pos < 0) {
return mrb_nil_value();
}
}
+ if (pos >= clen) return mrb_nil_value();
+ pos = chars2bytes(str, 0, pos);
switch (mrb_type(sub)) {
- case MRB_TT_FIXNUM: {
- int c = mrb_fixnum(sub);
- mrb_int len = RSTRING_LEN(str);
- unsigned char *p = (unsigned char*)RSTRING_PTR(str);
-
- for (;pos<len;pos++) {
- if (p[pos] == c) return mrb_fixnum_value(pos);
- }
- return mrb_nil_value();
- }
-
default: {
mrb_value tmp;
@@ -1343,56 +1602,17 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str)
}
/* fall through */
case MRB_TT_STRING:
- pos = mrb_str_index(mrb, str, sub, pos);
+ pos = str_index(mrb, str, sub, pos);
break;
}
if (pos == -1) return mrb_nil_value();
+ pos = bytes2chars(RSTRING_PTR(str), pos);
return mrb_fixnum_value(pos);
}
#define STR_REPLACE_SHARED_MIN 10
-static mrb_value
-str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
-{
- long len;
-
- len = RSTR_LEN(s2);
- if (RSTR_SHARED_P(s1)) {
- str_decref(mrb, s1->as.heap.aux.shared);
- }
- else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1)) {
- mrb_free(mrb, s1->as.heap.ptr);
- }
-
- RSTR_UNSET_NOFREE_FLAG(s1);
-
- if (RSTR_SHARED_P(s2)) {
-L_SHARE:
- RSTR_UNSET_EMBED_FLAG(s1);
- s1->as.heap.ptr = s2->as.heap.ptr;
- s1->as.heap.len = len;
- s1->as.heap.aux.shared = s2->as.heap.aux.shared;
- RSTR_SET_SHARED_FLAG(s1);
- s1->as.heap.aux.shared->refcnt++;
- }
- else {
- if (len <= RSTRING_EMBED_LEN_MAX) {
- RSTR_UNSET_SHARED_FLAG(s1);
- RSTR_SET_EMBED_FLAG(s1);
- memcpy(s1->as.ary, RSTR_PTR(s2), len);
- RSTR_SET_EMBED_LEN(s1, len);
- }
- else {
- str_make_shared(mrb, s2);
- goto L_SHARE;
- }
- }
-
- return mrb_obj_value(s1);
-}
-
/* 15.2.10.5.24 */
/* 15.2.10.5.28 */
/*
@@ -1450,13 +1670,13 @@ mrb_str_init(mrb_state *mrb, mrb_value self)
*
* 'cat and dog'.to_sym #=> :"cat and dog"
*/
-mrb_value
+MRB_API mrb_value
mrb_str_intern(mrb_state *mrb, mrb_value self)
{
return mrb_symbol_value(mrb_intern_str(mrb, self));
}
/* ---------------------------------- */
-mrb_value
+MRB_API mrb_value
mrb_obj_as_string(mrb_state *mrb, mrb_value obj)
{
mrb_value str;
@@ -1470,7 +1690,7 @@ mrb_obj_as_string(mrb_state *mrb, mrb_value obj)
return str;
}
-mrb_value
+MRB_API mrb_value
mrb_ptr_to_str(mrb_state *mrb, void *p)
{
struct RString *p_str;
@@ -1500,119 +1720,93 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
return mrb_obj_value(p_str);
}
-mrb_value
+MRB_API mrb_value
mrb_string_type(mrb_state *mrb, mrb_value str)
{
return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
}
-mrb_value
+MRB_API mrb_value
mrb_check_string_type(mrb_state *mrb, mrb_value str)
{
return mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
}
-/* ---------------------------------- */
-/* 15.2.10.5.29 */
+/* 15.2.10.5.30 */
/*
* call-seq:
- * str.reverse => new_str
- *
- * Returns a new string with the characters from <i>str</i> in reverse order.
+ * str.reverse! => str
*
- * "stressed".reverse #=> "desserts"
+ * Reverses <i>str</i> in place.
*/
static mrb_value
-mrb_str_reverse(mrb_state *mrb, mrb_value str)
+mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
{
- struct RString *s2;
- char *s, *e, *p;
+#ifdef MRB_UTF8_STRING
+ mrb_int utf8_len = RSTRING_CHAR_LEN(str);
+ mrb_int len = RSTRING_LEN(str);
+
+ if (utf8_len == len) goto bytes;
+ if (utf8_len > 1) {
+ char *buf;
+ char *p, *e, *r;
- if (RSTRING_LEN(str) <= 1) return mrb_str_dup(mrb, str);
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ len = RSTRING_LEN(str);
+ buf = mrb_malloc(mrb, (size_t)len);
+ p = buf;
+ e = buf + len;
- s2 = str_new(mrb, 0, RSTRING_LEN(str));
- str_with_class(mrb, s2, str);
- s = RSTRING_PTR(str); e = RSTRING_END(str) - 1;
- p = RSTR_PTR(s2);
+ memcpy(buf, RSTRING_PTR(str), len);
+ r = RSTRING_PTR(str) + len;
- while (e >= s) {
- *p++ = *e--;
+ while (p<e) {
+ mrb_int clen = utf8len(p, e);
+ r -= clen;
+ memcpy(r, p, clen);
+ p += clen;
+ }
+ mrb_free(mrb, buf);
}
- return mrb_obj_value(s2);
-}
+ return str;
-/* 15.2.10.5.30 */
-/*
- * call-seq:
- * str.reverse! => str
- *
- * Reverses <i>str</i> in place.
- */
-static mrb_value
-mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
-{
- struct RString *s = mrb_str_ptr(str);
- char *p, *e;
- char c;
+ bytes:
+#endif
+ {
+ struct RString *s = mrb_str_ptr(str);
+ char *p, *e;
+ char c;
- mrb_str_modify(mrb, s);
- if (RSTR_LEN(s) > 1) {
- p = RSTR_PTR(s);
- e = p + RSTR_LEN(s) - 1;
- while (p < e) {
+ mrb_str_modify(mrb, s);
+ if (RSTR_LEN(s) > 1) {
+ p = RSTR_PTR(s);
+ e = p + RSTR_LEN(s) - 1;
+ while (p < e) {
c = *p;
*p++ = *e;
*e-- = c;
+ }
}
+ return str;
}
- return str;
}
+/* ---------------------------------- */
+/* 15.2.10.5.29 */
/*
* call-seq:
- * str.rindex(substring [, fixnum]) => fixnum or nil
- * str.rindex(fixnum [, fixnum]) => fixnum or nil
- * str.rindex(regexp [, fixnum]) => fixnum or nil
+ * str.reverse => new_str
*
- * Returns the index of the last occurrence of the given <i>substring</i>,
- * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>. Returns
- * <code>nil</code> if not found. If the second parameter is present, it
- * specifies the position in the string to end the search---characters beyond
- * this point will not be considered.
+ * Returns a new string with the characters from <i>str</i> in reverse order.
*
- * "hello".rindex('e') #=> 1
- * "hello".rindex('l') #=> 3
- * "hello".rindex('a') #=> nil
- * "hello".rindex(101) #=> 1
- * "hello".rindex(/[aeiou]/, -2) #=> 1
+ * "stressed".reverse #=> "desserts"
*/
-static mrb_int
-mrb_str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
+static mrb_value
+mrb_str_reverse(mrb_state *mrb, mrb_value str)
{
- char *s, *sbeg, *t;
- struct RString *ps = mrb_str_ptr(str);
- mrb_int len = RSTRING_LEN(sub);
-
- /* substring longer than string */
- if (RSTR_LEN(ps) < len) return -1;
- if (RSTR_LEN(ps) - pos < len) {
- pos = RSTR_LEN(ps) - len;
- }
- sbeg = RSTR_PTR(ps);
- s = RSTR_PTR(ps) + pos;
- t = RSTRING_PTR(sub);
- if (len) {
- while (sbeg <= s) {
- if (memcmp(s, t, len) == 0) {
- return s - RSTR_PTR(ps);
- }
- s--;
- }
- return -1;
- }
- else {
- return pos;
- }
+ mrb_value str2 = mrb_str_dup(mrb, str);
+ mrb_str_reverse_bang(mrb, str2);
+ return str2;
}
/* 15.2.10.5.31 */
@@ -1635,13 +1829,13 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
* "hello".rindex(/[aeiou]/, -2) #=> 1
*/
static mrb_value
-mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
+mrb_str_rindex(mrb_state *mrb, mrb_value str)
{
mrb_value *argv;
mrb_int argc;
mrb_value sub;
mrb_value vpos;
- mrb_int pos, len = RSTRING_LEN(str);
+ mrb_int pos, len = RSTRING_CHAR_LEN(str);
mrb_get_args(mrb, "*", &argv, &argc);
if (argc == 2) {
@@ -1651,7 +1845,7 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
if (pos < 0) {
pos += len;
if (pos < 0) {
- regexp_check(mrb, sub);
+ mrb_regexp_check(mrb, sub);
return mrb_nil_value();
}
}
@@ -1664,20 +1858,11 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
else
sub = mrb_nil_value();
}
- regexp_check(mrb, sub);
+ pos = chars2bytes(str, 0, pos);
+ len = chars2bytes(str, pos, len);
+ mrb_regexp_check(mrb, sub);
switch (mrb_type(sub)) {
- case MRB_TT_FIXNUM: {
- int c = mrb_fixnum(sub);
- mrb_int len = RSTRING_LEN(str);
- unsigned char *p = (unsigned char*)RSTRING_PTR(str);
-
- for (pos=len-1;pos>=0;pos--) {
- if (p[pos] == c) return mrb_fixnum_value(pos);
- }
- return mrb_nil_value();
- }
-
default: {
mrb_value tmp;
@@ -1689,8 +1874,11 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
}
/* fall through */
case MRB_TT_STRING:
- pos = mrb_str_rindex(mrb, str, sub, pos);
- if (pos >= 0) return mrb_fixnum_value(pos);
+ pos = str_rindex(mrb, str, sub, pos);
+ if (pos >= 0) {
+ pos = bytes2chars(RSTRING_PTR(str), pos);
+ return mrb_fixnum_value(pos);
+ }
break;
} /* end of switch (TYPE(sub)) */
@@ -1701,7 +1889,7 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
/*
* call-seq:
- * str.split(pattern=$;, [limit]) => anArray
+ * str.split(pattern="\n", [limit]) => anArray
*
* Divides <i>str</i> into substrings based on a delimiter, returning an array
* of these substrings.
@@ -1717,7 +1905,7 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
*
* If <i>pattern</i> is omitted, the value of <code>$;</code> is used. If
* <code>$;</code> is <code>nil</code> (which is the default), <i>str</i> is
- * split on whitespace as if ` ' were specified.
+ * split on whitespace as if ' ' were specified.
*
* If the <i>limit</i> parameter is omitted, trailing null fields are
* suppressed. If <i>limit</i> is a positive number, at most that number of
@@ -1729,10 +1917,8 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str)
* " now's the time".split #=> ["now's", "the", "time"]
* " now's the time".split(' ') #=> ["now's", "the", "time"]
* " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"]
- * "1, 2.34,56, 7".split(%r{,\s*}) #=> ["1", "2.34", "56", "7"]
* "hello".split(//) #=> ["h", "e", "l", "l", "o"]
* "hello".split(//, 3) #=> ["h", "e", "llo"]
- * "hi mom".split(%r{\s*}) #=> ["h", "i", "m", "o", "m"]
*
* "mellow yellow".split("ello") #=> ["m", "w y", "w"]
* "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"]
@@ -1774,83 +1960,73 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
}
}
else {
- noregexp(mrb, str);
+ mrb_noregexp(mrb, str);
}
}
result = mrb_ary_new(mrb);
beg = 0;
if (split_type == awk) {
- char *ptr = RSTRING_PTR(str);
- char *eptr = RSTRING_END(str);
- char *bptr = ptr;
mrb_bool skip = TRUE;
+ mrb_int idx = 0;
+ mrb_int str_len = RSTRING_LEN(str);
unsigned int c;
+ int ai = mrb_gc_arena_save(mrb);
- end = beg;
- while (ptr < eptr) {
- int ai = mrb_gc_arena_save(mrb);
- c = (unsigned char)*ptr++;
+ idx = end = beg;
+ while (idx < str_len) {
+ c = (unsigned char)RSTRING_PTR(str)[idx++];
if (skip) {
if (ISSPACE(c)) {
- beg = ptr - bptr;
+ beg = idx;
}
else {
- end = ptr - bptr;
+ end = idx;
skip = FALSE;
if (lim_p && lim <= i) break;
}
}
else if (ISSPACE(c)) {
- mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, beg, end-beg));
+ mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg));
mrb_gc_arena_restore(mrb, ai);
skip = TRUE;
- beg = ptr - bptr;
+ beg = idx;
if (lim_p) ++i;
}
else {
- end = ptr - bptr;
+ end = idx;
}
}
}
else if (split_type == string) {
- char *ptr = RSTRING_PTR(str); // s->as.ary
- char *temp = ptr;
- char *eptr = RSTRING_END(str);
- mrb_int slen = RSTRING_LEN(spat);
-
- if (slen == 0) {
- int ai = mrb_gc_arena_save(mrb);
- while (ptr < eptr) {
- mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, ptr-temp, 1));
- mrb_gc_arena_restore(mrb, ai);
- ptr++;
- if (lim_p && lim <= ++i) break;
+ mrb_int str_len = RSTRING_LEN(str);
+ mrb_int pat_len = RSTRING_LEN(spat);
+ mrb_int idx = 0;
+ int ai = mrb_gc_arena_save(mrb);
+
+ while (idx < str_len) {
+ if (pat_len > 0) {
+ end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx);
+ if (end < 0) break;
+ } else {
+ end = chars2bytes(str, idx, 1);
}
+ mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end));
+ mrb_gc_arena_restore(mrb, ai);
+ idx += end + pat_len;
+ if (lim_p && lim <= ++i) break;
}
- else {
- char *sptr = RSTRING_PTR(spat);
- int ai = mrb_gc_arena_save(mrb);
-
- while (ptr < eptr &&
- (end = mrb_memsearch(sptr, slen, ptr, eptr - ptr)) >= 0) {
- mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, ptr - temp, end));
- mrb_gc_arena_restore(mrb, ai);
- ptr += end + slen;
- if (lim_p && lim <= ++i) break;
- }
- }
- beg = ptr - temp;
+ beg = idx;
}
else {
- noregexp(mrb, str);
+ mrb_noregexp(mrb, str);
}
if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) {
if (RSTRING_LEN(str) == beg) {
tmp = mrb_str_new_empty(mrb, str);
}
else {
- tmp = mrb_str_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
+ tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
}
mrb_ary_push(mrb, result, tmp);
}
@@ -1864,44 +2040,39 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
return result;
}
-mrb_value
-mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
+MRB_API mrb_value
+mrb_str_len_to_inum(mrb_state *mrb, const char *str, size_t len, int base, int badcheck)
{
- const char *p;
+ const char *p = str;
+ const char *pend = str + len;
char sign = 1;
- int c, uscore;
- unsigned long n = 0;
+ int c;
+ uint64_t n = 0;
mrb_int val;
-#undef ISDIGIT
-#define ISDIGIT(c) ('0' <= (c) && (c) <= '9')
#define conv_digit(c) \
- (!ISASCII(c) ? -1 : \
- isdigit(c) ? ((c) - '0') : \
- islower(c) ? ((c) - 'a' + 10) : \
- isupper(c) ? ((c) - 'A' + 10) : \
+ (ISDIGIT(c) ? ((c) - '0') : \
+ ISLOWER(c) ? ((c) - 'a' + 10) : \
+ ISUPPER(c) ? ((c) - 'A' + 10) : \
-1)
- if (!str) {
+ if (!p) {
if (badcheck) goto bad;
return mrb_fixnum_value(0);
}
- while (ISSPACE(*str)) str++;
+ while (p<pend && ISSPACE(*p))
+ p++;
- if (str[0] == '+') {
- str++;
+ if (p[0] == '+') {
+ p++;
}
- else if (str[0] == '-') {
- str++;
+ else if (p[0] == '-') {
+ p++;
sign = 0;
}
- if (str[0] == '+' || str[0] == '-') {
- if (badcheck) goto bad;
- return mrb_fixnum_value(0);
- }
if (base <= 0) {
- if (str[0] == '0') {
- switch (str[1]) {
+ if (p[0] == '0') {
+ switch (p[1]) {
case 'x': case 'X':
base = 16;
break;
@@ -1916,6 +2087,7 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
break;
default:
base = 8;
+ break;
}
}
else if (base < -1) {
@@ -1927,27 +2099,27 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
}
switch (base) {
case 2:
- if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
- str += 2;
+ if (p[0] == '0' && (p[1] == 'b'||p[1] == 'B')) {
+ p += 2;
}
break;
case 3:
break;
case 8:
- if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
- str += 2;
+ if (p[0] == '0' && (p[1] == 'o'||p[1] == 'O')) {
+ p += 2;
}
case 4: case 5: case 6: case 7:
break;
case 10:
- if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
- str += 2;
+ if (p[0] == '0' && (p[1] == 'd'||p[1] == 'D')) {
+ p += 2;
}
case 9: case 11: case 12: case 13: case 14: case 15:
break;
case 16:
- if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
- str += 2;
+ if (p[0] == '0' && (p[1] == 'x'||p[1] == 'X')) {
+ p += 2;
}
break;
default:
@@ -1956,96 +2128,109 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
}
break;
} /* end of switch (base) { */
- if (*str == '0') { /* squeeze preceeding 0s */
- uscore = 0;
- while ((c = *++str) == '0' || c == '_') {
+ if (p>=pend) {
+ if (badcheck) goto bad;
+ return mrb_fixnum_value(0);
+ }
+ if (*p == '0') { /* squeeze preceding 0s */
+ p++;
+ while (p<pend) {
+ c = *p++;
if (c == '_') {
- if (++uscore >= 2)
+ if (p<pend && *p == '_') {
+ if (badcheck) goto bad;
break;
+ }
+ continue;
+ }
+ if (c != '0') {
+ p--;
+ break;
}
- else
- uscore = 0;
}
- if (!(c = *str) || ISSPACE(c)) --str;
}
- c = *str;
- c = conv_digit(c);
- if (c < 0 || c >= base) {
+ if (p == pend) {
if (badcheck) goto bad;
return mrb_fixnum_value(0);
}
-
- uscore = 0;
- for (p=str;*p;p++) {
+ for ( ;p<pend;p++) {
if (*p == '_') {
- if (uscore == 0) {
- uscore++;
+ p++;
+ if (p==pend) {
+ if (badcheck) goto bad;
continue;
}
- if (badcheck) goto bad;
- break;
+ if (*p == '_') {
+ if (badcheck) goto bad;
+ break;
+ }
+ }
+ if (badcheck && *p == '\0') {
+ goto nullbyte;
}
- uscore = 0;
c = conv_digit(*p);
if (c < 0 || c >= base) {
- if (badcheck) goto bad;
break;
}
n *= base;
n += c;
+ if (n > (uint64_t)MRB_INT_MAX + (sign ? 0 : 1)) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer",
+ mrb_str_new(mrb, str, pend-str));
+ }
}
- if (n > MRB_INT_MAX) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new_cstr(mrb, str));
- }
- val = n;
+ val = (mrb_int)n;
if (badcheck) {
if (p == str) goto bad; /* no number */
- while (*p && ISSPACE(*p)) p++;
- if (*p) goto bad; /* trailing garbage */
+ while (p<pend && ISSPACE(*p)) p++;
+ if (p<pend) goto bad; /* trailing garbage */
}
return mrb_fixnum_value(sign ? val : -val);
-bad:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)", mrb_str_new_cstr(mrb, str));
+ nullbyte:
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
+ /* not reached */
+ bad:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)",
+ mrb_inspect(mrb, mrb_str_new(mrb, str, pend-str)));
/* not reached */
return mrb_fixnum_value(0);
}
-char *
+MRB_API mrb_value
+mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
+{
+ return mrb_str_len_to_inum(mrb, str, strlen(str), base, badcheck);
+}
+
+MRB_API const char*
mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
{
- struct RString *ps = mrb_str_ptr(*ptr);
+ mrb_value str = mrb_str_to_str(mrb, *ptr);
+ struct RString *ps = mrb_str_ptr(str);
mrb_int len = mrb_str_strlen(mrb, ps);
char *p = RSTR_PTR(ps);
if (!p || p[len] != '\0') {
+ if (RSTR_FROZEN_P(ps)) {
+ *ptr = str = mrb_str_dup(mrb, str);
+ ps = mrb_str_ptr(str);
+ }
mrb_str_modify(mrb, ps);
return RSTR_PTR(ps);
}
return p;
}
-mrb_value
+MRB_API mrb_value
mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck)
{
- char *s;
+ const char *s;
mrb_int len;
- str = mrb_str_to_str(mrb, str);
- if (badcheck) {
- s = mrb_string_value_cstr(mrb, &str);
- }
- else {
- s = RSTRING_PTR(str);
- }
- if (s) {
- len = RSTRING_LEN(str);
- if (s[len]) { /* no sentinel somehow */
- struct RString *temp_str = str_new(mrb, s, len);
- s = RSTR_PTR(temp_str);
- }
- }
- return mrb_cstr_to_inum(mrb, s, base, badcheck);
+ s = mrb_string_value_ptr(mrb, str);
+ len = RSTRING_LEN(str);
+ return mrb_str_len_to_inum(mrb, s, len, base, badcheck);
}
/* 15.2.10.5.38 */
@@ -2081,16 +2266,14 @@ mrb_str_to_i(mrb_state *mrb, mrb_value self)
return mrb_str_to_inum(mrb, self, base, FALSE);
}
-double
+MRB_API double
mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck)
{
char *end;
+ char buf[DBL_DIG * 4 + 10];
double d;
enum {max_width = 20};
-#define OutOfRange() (((w = end - p) > max_width) ? \
- (w = max_width, ellipsis = "...") : \
- (w = (int)(end - p), ellipsis = ""))
if (!p) return 0.0;
while (ISSPACE(*p)) p++;
@@ -2108,7 +2291,6 @@ bad:
return d;
}
if (*end) {
- char buf[DBL_DIG * 4 + 10];
char *n = buf;
char *e = buf + sizeof(buf) - 1;
char prev = 0;
@@ -2147,7 +2329,7 @@ bad:
return d;
}
-double
+MRB_API double
mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck)
{
char *s;
@@ -2242,7 +2424,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str)
*
* Returns a copy of <i>str</i> with all lowercase letters replaced with their
* uppercase counterparts. The operation is locale insensitive---only
- * characters ``a'' to ``z'' are affected.
+ * characters 'a' to 'z' are affected.
*
* "hEllO".upcase #=> "HELLO"
*/
@@ -2377,30 +2559,30 @@ mrb_str_dump(mrb_state *mrb, mrb_value str)
return mrb_obj_value(result);
}
-mrb_value
+MRB_API mrb_value
mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len)
{
str_buf_cat(mrb, mrb_str_ptr(str), ptr, len);
return str;
}
-mrb_value
+MRB_API mrb_value
mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr)
{
return mrb_str_cat(mrb, str, ptr, strlen(ptr));
}
-mrb_value
+MRB_API mrb_value
mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2)
{
return mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(str2));
}
-mrb_value
-mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2)
+MRB_API mrb_value
+mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2)
{
str2 = mrb_str_to_str(mrb, str2);
- return mrb_str_cat_str(mrb, str, str2);
+ return mrb_str_cat_str(mrb, str1, str2);
}
#define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */
@@ -2426,7 +2608,21 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
p = RSTRING_PTR(str); pend = RSTRING_END(str);
for (;p < pend; p++) {
unsigned char c, cc;
+#ifdef MRB_UTF8_STRING
+ mrb_int clen;
+ clen = utf8len(p, pend);
+ if (clen > 1) {
+ mrb_int i;
+
+ for (i=0; i<clen; i++) {
+ buf[i] = p[i];
+ }
+ mrb_str_cat(mrb, result, buf, clen);
+ p += clen-1;
+ continue;
+ }
+#endif
c = *p;
if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p, pend))) {
buf[0] = '\\'; buf[1] = c;
@@ -2500,7 +2696,7 @@ mrb_init_string(mrb_state *mrb)
mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string");
- s = mrb->string_class = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */
+ mrb->string_class = s = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */
MRB_SET_INSTANCE_TT(s, MRB_TT_STRING);
mrb_define_method(mrb, s, "bytesize", mrb_str_bytesize, MRB_ARGS_NONE());
@@ -2511,7 +2707,7 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "*", mrb_str_times, MRB_ARGS_REQ(1)); /* 15.2.10.5.5 */
mrb_define_method(mrb, s, "[]", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.6 */
mrb_define_method(mrb, s, "capitalize", mrb_str_capitalize, MRB_ARGS_NONE()); /* 15.2.10.5.7 */
- mrb_define_method(mrb, s, "capitalize!", mrb_str_capitalize_bang, MRB_ARGS_REQ(1)); /* 15.2.10.5.8 */
+ mrb_define_method(mrb, s, "capitalize!", mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */
mrb_define_method(mrb, s, "chomp", mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */
mrb_define_method(mrb, s, "chomp!", mrb_str_chomp_bang, MRB_ARGS_ANY()); /* 15.2.10.5.10 */
mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_REQ(1)); /* 15.2.10.5.11 */
@@ -2521,9 +2717,9 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "empty?", mrb_str_empty_p, MRB_ARGS_NONE()); /* 15.2.10.5.16 */
mrb_define_method(mrb, s, "eql?", mrb_str_eql, MRB_ARGS_REQ(1)); /* 15.2.10.5.17 */
- mrb_define_method(mrb, s, "hash", mrb_str_hash_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.20 */
+ mrb_define_method(mrb, s, "hash", mrb_str_hash_m, MRB_ARGS_NONE()); /* 15.2.10.5.20 */
mrb_define_method(mrb, s, "include?", mrb_str_include, MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */
- mrb_define_method(mrb, s, "index", mrb_str_index_m, MRB_ARGS_ANY()); /* 15.2.10.5.22 */
+ mrb_define_method(mrb, s, "index", mrb_str_index, MRB_ARGS_ANY()); /* 15.2.10.5.22 */
mrb_define_method(mrb, s, "initialize", mrb_str_init, MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */
mrb_define_method(mrb, s, "initialize_copy", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */
mrb_define_method(mrb, s, "intern", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.25 */
@@ -2531,7 +2727,7 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "replace", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */
mrb_define_method(mrb, s, "reverse", mrb_str_reverse, MRB_ARGS_NONE()); /* 15.2.10.5.29 */
mrb_define_method(mrb, s, "reverse!", mrb_str_reverse_bang, MRB_ARGS_NONE()); /* 15.2.10.5.30 */
- mrb_define_method(mrb, s, "rindex", mrb_str_rindex_m, MRB_ARGS_ANY()); /* 15.2.10.5.31 */
+ mrb_define_method(mrb, s, "rindex", mrb_str_rindex, MRB_ARGS_ANY()); /* 15.2.10.5.31 */
mrb_define_method(mrb, s, "size", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.33 */
mrb_define_method(mrb, s, "slice", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.34 */
mrb_define_method(mrb, s, "split", mrb_str_split_m, MRB_ARGS_ANY()); /* 15.2.10.5.35 */
@@ -2541,8 +2737,10 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "to_s", mrb_str_to_s, MRB_ARGS_NONE()); /* 15.2.10.5.40 */
mrb_define_method(mrb, s, "to_str", mrb_str_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "to_sym", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.41 */
- mrb_define_method(mrb, s, "upcase", mrb_str_upcase, MRB_ARGS_REQ(1)); /* 15.2.10.5.42 */
- mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_REQ(1)); /* 15.2.10.5.43 */
+ mrb_define_method(mrb, s, "upcase", mrb_str_upcase, MRB_ARGS_NONE()); /* 15.2.10.5.42 */
+ mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */
mrb_define_method(mrb, s, "inspect", mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */
mrb_define_method(mrb, s, "bytes", mrb_str_bytes, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, s, "freeze", mrb_str_freeze, MRB_ARGS_NONE());
}
diff --git a/src/symbol.c b/src/symbol.c
index 148adc6fe..c39e88012 100644
--- a/src/symbol.c
+++ b/src/symbol.c
@@ -4,11 +4,12 @@
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
+#include <limits.h>
#include <string.h>
-#include "mruby.h"
-#include "mruby/khash.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/khash.h>
+#include <mruby/string.h>
+#include <mruby/dump.h>
/* ------------------------------------------------------ */
typedef struct symbol_name {
@@ -18,148 +19,153 @@ typedef struct symbol_name {
} symbol_name;
static inline khint_t
-sym_hash_func(mrb_state *mrb, const symbol_name s)
+sym_hash_func(mrb_state *mrb, mrb_sym s)
{
khint_t h = 0;
- size_t i;
- const char *p = s.name;
+ size_t i, len = mrb->symtbl[s].len;
+ const char *p = mrb->symtbl[s].name;
- for (i=0; i<s.len; i++) {
+ for (i=0; i<len; i++) {
h = (h << 5) - h + *p++;
}
return h;
}
-#define sym_hash_equal(mrb,a, b) (a.len == b.len && memcmp(a.name, b.name, a.len) == 0)
+#define sym_hash_equal(mrb,a, b) (mrb->symtbl[a].len == mrb->symtbl[b].len && memcmp(mrb->symtbl[a].name, mrb->symtbl[b].name, mrb->symtbl[a].len) == 0)
-KHASH_DECLARE(n2s, symbol_name, mrb_sym, TRUE)
-KHASH_DEFINE (n2s, symbol_name, mrb_sym, TRUE, sym_hash_func, sym_hash_equal)
+KHASH_DECLARE(n2s, mrb_sym, mrb_sym, FALSE)
+KHASH_DEFINE (n2s, mrb_sym, mrb_sym, FALSE, sym_hash_func, sym_hash_equal)
/* ------------------------------------------------------ */
+
+static void
+sym_validate_len(mrb_state *mrb, size_t len)
+{
+ if (len >= RITE_LV_NULL_MARK) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
+ }
+}
+
static mrb_sym
sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
{
khash_t(n2s) *h = mrb->name2sym;
- symbol_name sname;
+ symbol_name *sname = mrb->symtbl; /* symtbl[0] for working memory */
khiter_t k;
mrb_sym sym;
char *p;
- if (len > (UINT16_MAX-1)) { /* UINT16_MAX is reverved */
- mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
+ sym_validate_len(mrb, len);
+ if (sname) {
+ sname->lit = lit;
+ sname->len = (uint16_t)len;
+ sname->name = name;
+ k = kh_get(n2s, mrb, h, 0);
+ if (k != kh_end(h))
+ return kh_key(h, k);
}
- sname.lit = lit;
- sname.len = (uint16_t)len;
- sname.name = name;
- k = kh_get(n2s, mrb, h, sname);
- if (k != kh_end(h))
- return kh_value(h, k);
+ /* registering a new symbol */
sym = ++mrb->symidx;
- if (lit) {
- sname.name = name;
+ if (mrb->symcapa < sym) {
+ if (mrb->symcapa == 0) mrb->symcapa = 100;
+ else mrb->symcapa = (size_t)(mrb->symcapa * 1.2);
+ mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(mrb->symcapa+1));
+ }
+ sname = &mrb->symtbl[sym];
+ sname->len = (uint16_t)len;
+ if (lit || mrb_ro_data_p(name)) {
+ sname->name = name;
+ sname->lit = TRUE;
}
else {
p = (char *)mrb_malloc(mrb, len+1);
memcpy(p, name, len);
p[len] = 0;
- sname.name = (const char*)p;
+ sname->name = (const char*)p;
+ sname->lit = FALSE;
}
- k = kh_put(n2s, mrb, h, sname);
- kh_value(h, k) = sym;
+ kh_put(n2s, mrb, h, sym);
return sym;
}
-mrb_sym
+MRB_API mrb_sym
mrb_intern(mrb_state *mrb, const char *name, size_t len)
{
return sym_intern(mrb, name, len, FALSE);
}
-mrb_sym
+MRB_API mrb_sym
mrb_intern_static(mrb_state *mrb, const char *name, size_t len)
{
return sym_intern(mrb, name, len, TRUE);
}
-mrb_sym
+MRB_API mrb_sym
mrb_intern_cstr(mrb_state *mrb, const char *name)
{
return mrb_intern(mrb, name, strlen(name));
}
-mrb_sym
+MRB_API mrb_sym
mrb_intern_str(mrb_state *mrb, mrb_value str)
{
return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
}
-mrb_value
+MRB_API mrb_value
mrb_check_intern(mrb_state *mrb, const char *name, size_t len)
{
khash_t(n2s) *h = mrb->name2sym;
- symbol_name sname = { 0 };
+ symbol_name *sname = mrb->symtbl;
khiter_t k;
- if (len > UINT16_MAX) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
- }
- sname.len = (uint16_t)len;
- sname.name = name;
+ sym_validate_len(mrb, len);
+ sname->len = (uint16_t)len;
+ sname->name = name;
- k = kh_get(n2s, mrb, h, sname);
+ k = kh_get(n2s, mrb, h, 0);
if (k != kh_end(h)) {
- return mrb_symbol_value(kh_value(h, k));
+ return mrb_symbol_value(kh_key(h, k));
}
return mrb_nil_value();
}
-mrb_value
+MRB_API mrb_value
mrb_check_intern_cstr(mrb_state *mrb, const char *name)
{
return mrb_check_intern(mrb, name, (mrb_int)strlen(name));
}
-mrb_value
+MRB_API mrb_value
mrb_check_intern_str(mrb_state *mrb, mrb_value str)
{
return mrb_check_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
}
/* lenp must be a pointer to a size_t variable */
-const char*
+MRB_API const char*
mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
{
- khash_t(n2s) *h = mrb->name2sym;
- khiter_t k;
- symbol_name sname;
-
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- if (kh_value(h, k) == sym) {
- sname = kh_key(h, k);
- if (lenp) *lenp = sname.len;
- return sname.name;
- }
- }
+ if (sym == 0 || mrb->symidx < sym) {
+ if (lenp) *lenp = 0;
+ return NULL;
}
- if (lenp) *lenp = 0;
- return NULL; /* missing */
+
+ if (lenp) *lenp = mrb->symtbl[sym].len;
+ return mrb->symtbl[sym].name;
}
void
mrb_free_symtbl(mrb_state *mrb)
{
- khash_t(n2s) *h = mrb->name2sym;
- khiter_t k;
-
- for (k = kh_begin(h); k != kh_end(h); k++)
- if (kh_exist(h, k)) {
- symbol_name s = kh_key(h, k);
+ mrb_sym i, lim;
- if (!s.lit) {
- mrb_free(mrb, (char*)s.name);
- }
+ for (i=1, lim=mrb->symidx+1; i<lim; i++) {
+ if (!mrb->symtbl[i].lit) {
+ mrb_free(mrb, (char*)mrb->symtbl[i].name);
}
+ }
+ mrb_free(mrb, mrb->symtbl);
kh_destroy(n2s, mrb, mrb->name2sym);
}
@@ -216,12 +222,10 @@ static mrb_value
sym_equal(mrb_state *mrb, mrb_value sym1)
{
mrb_value sym2;
- mrb_bool equal_p;
mrb_get_args(mrb, "o", &sym2);
- equal_p = mrb_obj_equal(mrb, sym1, sym2);
- return mrb_bool_value(equal_p);
+ return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2));
}
/* 15.2.11.3.2 */
@@ -235,7 +239,7 @@ sym_equal(mrb_state *mrb, mrb_value sym1)
*
* :fred.id2name #=> "fred"
*/
-mrb_value
+static mrb_value
mrb_sym_to_s(mrb_state *mrb, mrb_value sym)
{
mrb_sym id = mrb_symbol(sym);
@@ -411,7 +415,7 @@ sym_inspect(mrb_state *mrb, mrb_value sym)
return str;
}
-mrb_value
+MRB_API mrb_value
mrb_sym2str(mrb_state *mrb, mrb_sym sym)
{
mrb_int len;
@@ -421,7 +425,7 @@ mrb_sym2str(mrb_state *mrb, mrb_sym sym)
return mrb_str_new_static(mrb, name, len);
}
-const char*
+MRB_API const char*
mrb_sym2name(mrb_state *mrb, mrb_sym sym)
{
mrb_int len;
@@ -474,7 +478,7 @@ mrb_init_symbol(mrb_state *mrb)
{
struct RClass *sym;
- sym = mrb->symbol_class = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */
+ mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */
mrb_define_method(mrb, sym, "===", sym_equal, MRB_ARGS_REQ(1)); /* 15.2.11.3.1 */
mrb_define_method(mrb, sym, "id2name", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.2 */
diff --git a/src/value_array.h b/src/value_array.h
index cabd2426d..bc5f28b06 100644
--- a/src/value_array.h
+++ b/src/value_array.h
@@ -1,7 +1,7 @@
#ifndef MRB_VALUE_ARRAY_H__
#define MRB_VALUE_ARRAY_H__
-#include "mruby.h"
+#include <mruby.h>
static inline void
value_move(mrb_value *s1, const mrb_value *s2, size_t n)
diff --git a/src/variable.c b/src/variable.c
index 74bb591cf..bda7b2a98 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -4,12 +4,11 @@
** See Copyright Notice in mruby.h
*/
-#include <ctype.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/proc.h"
-#include "mruby/string.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*);
@@ -283,7 +282,7 @@ iv_free(mrb_state *mrb, iv_tbl *t)
#else
-#include "mruby/khash.h"
+#include <mruby/khash.h>
#ifndef MRB_IVHASH_INIT_SIZE
#define MRB_IVHASH_INIT_SIZE 8
@@ -459,13 +458,14 @@ obj_iv_p(mrb_value obj)
case MRB_TT_SCLASS:
case MRB_TT_HASH:
case MRB_TT_DATA:
+ case MRB_TT_EXCEPTION:
return TRUE;
default:
return FALSE;
}
}
-mrb_value
+MRB_API mrb_value
mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym)
{
mrb_value v;
@@ -475,7 +475,7 @@ mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym)
return mrb_nil_value();
}
-mrb_value
+MRB_API mrb_value
mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym)
{
if (obj_iv_p(obj)) {
@@ -484,7 +484,7 @@ mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym)
return mrb_nil_value();
}
-void
+MRB_API void
mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
{
iv_tbl *t = obj->iv;
@@ -496,7 +496,7 @@ mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
iv_put(mrb, t, sym, v);
}
-void
+MRB_API void
mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
{
iv_tbl *t = obj->iv;
@@ -511,7 +511,7 @@ mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
iv_put(mrb, t, sym, v);
}
-void
+MRB_API void
mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v)
{
if (obj_iv_p(obj)) {
@@ -522,7 +522,7 @@ mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v)
}
}
-mrb_bool
+MRB_API mrb_bool
mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym)
{
iv_tbl *t;
@@ -534,14 +534,40 @@ mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym)
return FALSE;
}
-mrb_bool
+MRB_API mrb_bool
mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym)
{
if (!obj_iv_p(obj)) return FALSE;
return mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), sym);
}
-void
+#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
+
+MRB_API mrb_bool
+mrb_iv_p(mrb_state *mrb, mrb_sym iv_name)
+{
+ const char *s;
+ mrb_int i, len;
+
+ s = mrb_sym2name_len(mrb, iv_name, &len);
+ if (len < 2) return FALSE;
+ if (s[0] != '@') return FALSE;
+ if (s[1] == '@') return FALSE;
+ for (i=1; i<len; i++) {
+ if (!identchar(s[i])) return FALSE;
+ }
+ return TRUE;
+}
+
+MRB_API void
+mrb_iv_check(mrb_state *mrb, mrb_sym iv_name)
+{
+ if (!mrb_iv_p(mrb, iv_name)) {
+ mrb_name_error(mrb, iv_name, "'%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name));
+ }
+}
+
+MRB_API void
mrb_iv_copy(mrb_state *mrb, mrb_value dest, mrb_value src)
{
struct RObject *d = mrb_obj_ptr(dest);
@@ -552,6 +578,7 @@ mrb_iv_copy(mrb_state *mrb, mrb_value dest, mrb_value src)
d->iv = 0;
}
if (s->iv) {
+ mrb_write_barrier(mrb, (struct RBasic*)d);
d->iv = iv_copy(mrb, s->iv);
}
}
@@ -582,7 +609,7 @@ inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
else {
ins = mrb_inspect(mrb, v);
}
- mrb_str_append(mrb, str, ins);
+ mrb_str_cat_str(mrb, str, ins);
return 0;
}
@@ -608,7 +635,7 @@ mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj)
return mrb_any_to_s(mrb, mrb_obj_value(obj));
}
-mrb_value
+MRB_API mrb_value
mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
{
if (obj_iv_p(obj)) {
@@ -728,34 +755,46 @@ mrb_mod_class_variables(mrb_state *mrb, mrb_value mod)
return ary;
}
-mrb_value
+MRB_API mrb_value
mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym)
{
struct RClass * cls = c;
+ mrb_value v;
while (c) {
- if (c->iv) {
- iv_tbl *t = c->iv;
- mrb_value v;
-
- if (iv_get(mrb, t, sym, &v))
- return v;
+ if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
+ return v;
}
c = c->super;
}
+ if (cls && cls->tt == MRB_TT_SCLASS) {
+ mrb_value klass;
+
+ klass = mrb_obj_iv_get(mrb, (struct RObject *)cls,
+ mrb_intern_lit(mrb, "__attached__"));
+ c = mrb_class_ptr(klass);
+ if (c->tt == MRB_TT_CLASS) {
+ while (c) {
+ if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
+ return v;
+ }
+ c = c->super;
+ }
+ }
+ }
mrb_name_error(mrb, sym, "uninitialized class variable %S in %S",
mrb_sym2str(mrb, sym), mrb_obj_value(cls));
/* not reached */
return mrb_nil_value();
}
-mrb_value
+MRB_API mrb_value
mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
return mrb_mod_cv_get(mrb, mrb_class_ptr(mod), sym);
}
-void
+MRB_API void
mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
{
struct RClass * cls = c;
@@ -781,13 +820,13 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
iv_put(mrb, cls->iv, sym, v);
}
-void
+MRB_API void
mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
{
mrb_mod_cv_set(mrb, mrb_class_ptr(mod), sym, v);
}
-mrb_bool
+MRB_API mrb_bool
mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym)
{
while (c) {
@@ -801,7 +840,7 @@ mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym)
return FALSE;
}
-mrb_bool
+MRB_API mrb_bool
mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
return mrb_mod_cv_defined(mrb, mrb_class_ptr(mod), sym);
@@ -826,16 +865,6 @@ mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
mrb_mod_cv_set(mrb, c, sym, v);
}
-mrb_bool
-mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym)
-{
- struct RClass *m = mrb_class_ptr(mod);
- iv_tbl *t = m->iv;
-
- if (!t) return FALSE;
- return iv_get(mrb, t, sym, NULL);
-}
-
static void
mod_const_check(mrb_state *mrb, mrb_value mod)
{
@@ -856,7 +885,7 @@ const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym)
struct RClass *c = base;
mrb_value v;
iv_tbl *t;
- mrb_bool retry = 0;
+ mrb_bool retry = FALSE;
mrb_value name;
L_RETRY:
@@ -870,14 +899,14 @@ L_RETRY:
}
if (!retry && base && base->tt == MRB_TT_MODULE) {
c = mrb->object_class;
- retry = 1;
+ retry = TRUE;
goto L_RETRY;
}
name = mrb_symbol_value(sym);
return mrb_funcall_argv(mrb, mrb_obj_value(base), mrb_intern_lit(mrb, "const_missing"), 1, &name);
}
-mrb_value
+MRB_API mrb_value
mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
mod_const_check(mrb, mod);
@@ -897,6 +926,14 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
return v;
}
+ if (c->tt == MRB_TT_SCLASS) {
+ mrb_value klass;
+ klass = mrb_obj_iv_get(mrb, (struct RObject *)c,
+ mrb_intern_lit(mrb, "__attached__"));
+ c2 = mrb_class_ptr(klass);
+ if (c2->tt == MRB_TT_CLASS)
+ c = c2;
+ }
c2 = c;
for (;;) {
c2 = mrb_class_outer_module(mrb, c2);
@@ -909,14 +946,14 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
return const_get(mrb, c, sym);
}
-void
+MRB_API void
mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v)
{
mod_const_check(mrb, mod);
mrb_iv_set(mrb, mod, sym, v);
}
- void
+void
mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
{
struct RClass *c = mrb->c->ci->proc->target_class;
@@ -925,20 +962,20 @@ mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v);
}
-void
+MRB_API void
mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
mod_const_check(mrb, mod);
mrb_iv_remove(mrb, mod, sym);
}
-void
+MRB_API void
mrb_define_const(mrb_state *mrb, struct RClass *mod, const char *name, mrb_value v)
{
mrb_obj_iv_set(mrb, (struct RObject*)mod, mrb_intern_cstr(mrb, name), v);
}
-void
+MRB_API void
mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val)
{
mrb_define_const(mrb, mrb->object_class, name, val);
@@ -986,7 +1023,7 @@ mrb_mod_constants(mrb_state *mrb, mrb_value mod)
return ary;
}
-mrb_value
+MRB_API mrb_value
mrb_gv_get(mrb_state *mrb, mrb_sym sym)
{
mrb_value v;
@@ -999,7 +1036,7 @@ mrb_gv_get(mrb_state *mrb, mrb_sym sym)
return mrb_nil_value();
}
-void
+MRB_API void
mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
{
iv_tbl *t;
@@ -1013,7 +1050,7 @@ mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
iv_put(mrb, t, sym, v);
}
-void
+MRB_API void
mrb_gv_remove(mrb_state *mrb, mrb_sym sym)
{
if (!mrb->globals) {
@@ -1063,9 +1100,10 @@ mrb_f_global_variables(mrb_state *mrb, mrb_value self)
}
static mrb_bool
-mrb_const_defined_0(mrb_state *mrb, struct RClass *klass, mrb_sym id, mrb_bool exclude, mrb_bool recurse)
+mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude, mrb_bool recurse)
{
- struct RClass * tmp;
+ struct RClass *klass = mrb_class_ptr(mod);
+ struct RClass *tmp;
mrb_bool mod_retry = 0;
tmp = klass;
@@ -1085,13 +1123,19 @@ retry:
return FALSE;
}
-int
-mrb_const_defined_at(mrb_state *mrb, struct RClass *klass, mrb_sym id)
+MRB_API mrb_bool
+mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id)
{
- return mrb_const_defined_0(mrb, klass, id, TRUE, FALSE);
+ return mrb_const_defined_0(mrb, mod, id, TRUE, TRUE);
}
-mrb_value
+MRB_API mrb_bool
+mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id)
+{
+ return mrb_const_defined_0(mrb, mod, id, TRUE, FALSE);
+}
+
+MRB_API mrb_value
mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id)
{
return mrb_iv_get(mrb, obj, id);
diff --git a/src/version.c b/src/version.c
index 7aac44d62..5bcecb3aa 100644
--- a/src/version.c
+++ b/src/version.c
@@ -1,12 +1,16 @@
-#include "mruby.h"
-#include "mruby/variable.h"
+#include <mruby.h>
+#include <mruby/variable.h>
void
mrb_init_version(mrb_state* mrb)
{
+ mrb_value mruby_version = mrb_str_new_lit(mrb, MRUBY_VERSION);
+
mrb_define_global_const(mrb, "RUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_RUBY_VERSION));
mrb_define_global_const(mrb, "RUBY_ENGINE", mrb_str_new_lit(mrb, MRUBY_RUBY_ENGINE));
+ mrb_define_global_const(mrb, "RUBY_ENGINE_VERSION", mruby_version);
mrb_define_global_const(mrb, "MRUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_VERSION));
+ mrb_define_global_const(mrb, "MRUBY_RELEASE_NO", mrb_fixnum_value(MRUBY_RELEASE_NO));
mrb_define_global_const(mrb, "MRUBY_RELEASE_DATE", mrb_str_new_lit(mrb, MRUBY_RELEASE_DATE));
mrb_define_global_const(mrb, "MRUBY_DESCRIPTION", mrb_str_new_lit(mrb, MRUBY_DESCRIPTION));
mrb_define_global_const(mrb, "MRUBY_COPYRIGHT", mrb_str_new_lit(mrb, MRUBY_COPYRIGHT));
diff --git a/src/vm.c b/src/vm.c
index 9a0f03aba..391646017 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -1,4 +1,4 @@
- /*
+/*
** vm.c - virtual machine for mruby
**
** See Copyright Notice in mruby.h
@@ -7,22 +7,22 @@
#include <stddef.h>
#include <stdarg.h>
#include <math.h>
-#include "mruby.h"
-#include "mruby/array.h"
-#include "mruby/class.h"
-#include "mruby/hash.h"
-#include "mruby/irep.h"
-#include "mruby/numeric.h"
-#include "mruby/proc.h"
-#include "mruby/range.h"
-#include "mruby/string.h"
-#include "mruby/variable.h"
-#include "mruby/error.h"
-#include "mruby/opcode.h"
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/class.h>
+#include <mruby/hash.h>
+#include <mruby/irep.h>
+#include <mruby/numeric.h>
+#include <mruby/proc.h>
+#include <mruby/range.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/error.h>
+#include <mruby/opcode.h>
#include "value_array.h"
-#include "mrb_throw.h"
+#include <mruby/throw.h>
-#ifndef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
#if defined(__cplusplus)
extern "C" {
#endif
@@ -32,20 +32,6 @@ void abort(void);
#endif
#endif
-#define SET_TRUE_VALUE(r) MRB_SET_VALUE(r, MRB_TT_TRUE, value.i, 1)
-#define SET_FALSE_VALUE(r) MRB_SET_VALUE(r, MRB_TT_FALSE, value.i, 1)
-#define SET_NIL_VALUE(r) MRB_SET_VALUE(r, MRB_TT_FALSE, value.i, 0)
-#define SET_INT_VALUE(r,n) MRB_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n))
-#define SET_SYM_VALUE(r,v) MRB_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v))
-#define SET_OBJ_VALUE(r,v) MRB_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v))
-#ifdef MRB_NAN_BOXING
-#define SET_FLT_VALUE(mrb,r,v) r.f = (v)
-#elif defined(MRB_WORD_BOXING)
-#define SET_FLT_VALUE(mrb,r,v) r = mrb_float_value(mrb, (v))
-#else
-#define SET_FLT_VALUE(mrb,r,v) MRB_SET_VALUE(r, MRB_TT_FLOAT, value.f, (v))
-#endif
-
#define STACK_INIT_SIZE 128
#define CALLINFO_INIT_SIZE 32
@@ -66,10 +52,7 @@ The value below allows about 60000 recursive calls in the simplest case. */
# define DEBUG(x)
#endif
-#define TO_STR(x) TO_STR_(x)
-#define TO_STR_(x) #x
-
-#define ARENA_RESTORE(mrb,ai) (mrb)->arena_idx = (ai)
+#define ARENA_RESTORE(mrb,ai) (mrb)->gc.arena_idx = (ai)
static inline void
stack_clear(mrb_value *from, size_t count)
@@ -174,7 +157,7 @@ stack_extend_alloc(mrb_state *mrb, int room, int keep)
to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */
if (size > MRB_STACK_MAX) {
init_new_stack_space(mrb, room, keep);
- mrb_raise(mrb, E_RUNTIME_ERROR, "stack level too deep. (limit=" TO_STR(MRB_STACK_MAX) ")");
+ mrb_raise(mrb, E_SYSSTACK_ERROR, "stack level too deep. (limit=" MRB_STRINGIZE(MRB_STACK_MAX) ")");
}
}
@@ -237,14 +220,13 @@ cipush(mrb_state *mrb)
int ridx = ci->ridx;
if (ci + 1 == c->ciend) {
- size_t size = ci - c->cibase;
+ ptrdiff_t size = ci - c->cibase;
c->cibase = (mrb_callinfo *)mrb_realloc(mrb, c->cibase, sizeof(mrb_callinfo)*size*2);
c->ci = c->cibase + size;
c->ciend = c->cibase + size * 2;
}
ci = ++c->ci;
- ci->nregs = 2; /* protect method_missing arg and block */
ci->eidx = eidx;
ci->ridx = ridx;
ci->env = 0;
@@ -270,6 +252,7 @@ cipop(mrb_state *mrb)
stack_copy(p, e->stack, len);
}
e->stack = p;
+ mrb_write_barrier(mrb, (struct RBasic *)e);
}
c->ci--;
@@ -283,6 +266,7 @@ ecall(mrb_state *mrb, int i)
mrb_value *self = mrb->c->stack;
struct RObject *exc;
+ if (i<0) return;
p = mrb->c->ensure[i];
if (!p) return;
if (mrb->c->ci->eidx > i)
@@ -306,49 +290,34 @@ ecall(mrb_state *mrb, int i)
#define MRB_FUNCALL_ARGC_MAX 16
#endif
-mrb_value
+MRB_API mrb_value
mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...)
{
+ mrb_value argv[MRB_FUNCALL_ARGC_MAX];
+ va_list ap;
+ mrb_int i;
mrb_sym mid = mrb_intern_cstr(mrb, name);
- if (argc == 0) {
- return mrb_funcall_argv(mrb, self, mid, 0, 0);
- }
- else if (argc == 1) {
- mrb_value v;
- va_list ap;
-
- va_start(ap, argc);
- v = va_arg(ap, mrb_value);
- va_end(ap);
- return mrb_funcall_argv(mrb, self, mid, 1, &v);
+ if (argc > MRB_FUNCALL_ARGC_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" MRB_STRINGIZE(MRB_FUNCALL_ARGC_MAX) ")");
}
- else {
- mrb_value argv[MRB_FUNCALL_ARGC_MAX];
- va_list ap;
- mrb_int i;
-
- if (argc > MRB_FUNCALL_ARGC_MAX) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" TO_STR(MRB_FUNCALL_ARGC_MAX) ")");
- }
- va_start(ap, argc);
- for (i = 0; i < argc; i++) {
- argv[i] = va_arg(ap, mrb_value);
- }
- va_end(ap);
- return mrb_funcall_argv(mrb, self, mid, argc, argv);
+ va_start(ap, argc);
+ for (i = 0; i < argc; i++) {
+ argv[i] = va_arg(ap, mrb_value);
}
+ va_end(ap);
+ return mrb_funcall_argv(mrb, self, mid, argc, argv);
}
-mrb_value
+MRB_API mrb_value
mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk)
{
mrb_value val;
if (!mrb->jmp) {
struct mrb_jmpbuf c_jmp;
- mrb_callinfo *old_ci = mrb->c->ci;
+ ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase;
MRB_TRY(&c_jmp) {
mrb->jmp = &c_jmp;
@@ -357,7 +326,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
mrb->jmp = 0;
}
MRB_CATCH(&c_jmp) { /* error */
- while (old_ci != mrb->c->ci) {
+ while (nth_ci < (mrb->c->ci - mrb->c->cibase)) {
mrb->c->stack = mrb->c->ci->stackent;
cipop(mrb);
}
@@ -372,6 +341,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
mrb_sym undef = 0;
mrb_callinfo *ci;
int n;
+ ptrdiff_t voff = -1;
if (!mrb->c->stack) {
stack_init(mrb);
@@ -395,6 +365,9 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
ci->argc = argc;
ci->target_class = c;
mrb->c->stack = mrb->c->stack + n;
+ if (mrb->c->stbase <= argv && argv < mrb->c->stend) {
+ voff = argv - mrb->c->stbase;
+ }
if (MRB_PROC_CFUNC_P(p)) {
ci->nregs = argc + 2;
stack_extend(mrb, ci->nregs, 0);
@@ -403,6 +376,9 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
ci->nregs = p->body.irep->nregs + n;
stack_extend(mrb, ci->nregs, argc+2);
}
+ if (voff >= 0) {
+ argv = mrb->c->stbase + voff;
+ }
mrb->c->stack[0] = self;
if (undef) {
mrb->c->stack[1] = mrb_symbol_value(undef);
@@ -433,7 +409,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
return val;
}
-mrb_value
+MRB_API mrb_value
mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv)
{
return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value());
@@ -458,7 +434,7 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons
* k = Klass.new
* k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
*/
-mrb_value
+MRB_API mrb_value
mrb_f_send(mrb_state *mrb, mrb_value self)
{
mrb_sym name;
@@ -497,8 +473,16 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
return p->body.func(mrb, self);
}
+ if (ci->argc < 0) {
+ stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs, 3);
+ }
+ else {
+ stack_extend(mrb, p->body.irep->nregs, ci->argc+2);
+ }
+
ci->nregs = p->body.irep->nregs;
ci = cipush(mrb);
+ ci->nregs = 0;
ci->target_class = 0;
ci->pc = p->body.irep->iseq;
ci->stackent = mrb->c->stack;
@@ -513,6 +497,9 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
struct RProc *p;
mrb_callinfo *ci;
+ if (mrb_nil_p(blk)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+ }
ci = mrb->c->ci;
if (ci->acc == CI_ACC_DIRECT) {
return mrb_yield_with_class(mrb, blk, 0, 0, self, c);
@@ -525,6 +512,7 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
}
ci->nregs = p->body.irep->nregs;
ci = cipush(mrb);
+ ci->nregs = 0;
ci->target_class = 0;
ci->pc = p->body.irep->iseq;
ci->stackent = mrb->c->stack;
@@ -599,7 +587,7 @@ mrb_obj_instance_eval(mrb_state *mrb, mrb_value self)
return eval_under(mrb, self, b, c);
}
-mrb_value
+MRB_API mrb_value
mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c)
{
struct RProc *p;
@@ -646,7 +634,7 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
return val;
}
-mrb_value
+MRB_API mrb_value
mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv)
{
struct RProc *p = mrb_proc_ptr(b);
@@ -654,7 +642,7 @@ mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv)
return mrb_yield_with_class(mrb, b, argc, argv, p->env->stack[0], p->target_class);
}
-mrb_value
+MRB_API mrb_value
mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg)
{
struct RProc *p = mrb_proc_ptr(b);
@@ -705,7 +693,7 @@ argnum_error(mrb_state *mrb, mrb_int num)
#define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc;
#define ERR_PC_CLR(mrb) mrb->c->ci->err = 0;
-#ifdef ENABLE_DEBUG
+#ifdef MRB_ENABLE_DEBUG_HOOK
#define CODE_FETCH_HOOK(mrb, irep, pc, regs) if ((mrb)->code_fetch_hook) (mrb)->code_fetch_hook((mrb), (irep), (pc), (regs));
#else
#define CODE_FETCH_HOOK(mrb, irep, pc, regs)
@@ -734,12 +722,11 @@ argnum_error(mrb_state *mrb, mrb_int num)
#endif
-mrb_value mrb_gv_val_get(mrb_state *mrb, mrb_sym sym);
-void mrb_gv_val_set(mrb_state *mrb, mrb_sym sym, mrb_value val);
-
#define CALL_MAXARGS 127
-mrb_value
+void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args);
+
+MRB_API mrb_value
mrb_context_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep)
{
/* mrb_assert(mrb_proc_cfunc_p(proc)) */
@@ -1046,7 +1033,7 @@ RETRY_TRY_BLOCK:
mrb_callinfo *ci = mrb->c->ci;
int n, eidx = ci->eidx;
- for (n=0; n<a && eidx > ci[-1].eidx; n++) {
+ for (n=0; n<a && (ci == mrb->c->cibase || eidx > ci[-1].eidx); n++) {
ecall(mrb, --eidx);
ARENA_RESTORE(mrb, ai);
}
@@ -1090,9 +1077,21 @@ RETRY_TRY_BLOCK:
m = mrb_method_search_vm(mrb, &c, mid);
if (!m) {
mrb_value sym = mrb_symbol_value(mid);
+ mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
- mid = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, mid);
+ m = mrb_method_search_vm(mrb, &c, missing);
+ if (!m) {
+ mrb_value args;
+
+ if (n == CALL_MAXARGS) {
+ args = regs[a+1];
+ }
+ else {
+ args = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ }
+ mrb_method_missing(mrb, mid, recv, args);
+ }
+ mid = missing;
if (n == CALL_MAXARGS) {
mrb_ary_unshift(mrb, regs[a+1], sym);
}
@@ -1107,12 +1106,7 @@ RETRY_TRY_BLOCK:
ci->mid = mid;
ci->proc = m;
ci->stackent = mrb->c->stack;
- if (c->tt == MRB_TT_ICLASS) {
- ci->target_class = c->c;
- }
- else {
- ci->target_class = c;
- }
+ ci->target_class = c;
ci->pc = pc + 1;
ci->acc = a;
@@ -1243,6 +1237,13 @@ RETRY_TRY_BLOCK:
int a = GETARG_A(i);
int n = GETARG_C(i);
+ if (mid == 0) {
+ mrb_value exc;
+
+ exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
+ mrb->exc = mrb_obj_ptr(exc);
+ goto L_RAISE;
+ }
recv = regs[0];
c = mrb->c->ci->target_class->super;
m = mrb_method_search_vm(mrb, &c, mid);
@@ -1277,6 +1278,12 @@ RETRY_TRY_BLOCK:
mrb->c->stack[0] = recv;
if (MRB_PROC_CFUNC_P(m)) {
+ if (n == CALL_MAXARGS) {
+ ci->nregs = 3;
+ }
+ else {
+ ci->nregs = n + 2;
+ }
mrb->c->stack[0] = m->body.func(mrb, recv);
mrb_gc_arena_restore(mrb, ai);
if (mrb->exc) goto L_RAISE;
@@ -1322,6 +1329,7 @@ RETRY_TRY_BLOCK:
struct REnv *e = uvenv(mrb, lv-1);
if (!e) {
mrb_value exc;
+
exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
mrb->exc = mrb_obj_ptr(exc);
goto L_RAISE;
@@ -1364,14 +1372,14 @@ RETRY_TRY_BLOCK:
/* Ax arg setup according to flags (23=5:5:1:5:5:1:1) */
/* number of optional arguments times OP_JMP should follow */
mrb_aspec ax = GETARG_Ax(i);
- int m1 = (ax>>18)&0x1f;
- int o = (ax>>13)&0x1f;
- int r = (ax>>12)&0x1;
- int m2 = (ax>>7)&0x1f;
+ int m1 = MRB_ASPEC_REQ(ax);
+ int o = MRB_ASPEC_OPT(ax);
+ int r = MRB_ASPEC_REST(ax);
+ int m2 = MRB_ASPEC_POST(ax);
/* unused
- int k = (ax>>2)&0x1f;
- int kd = (ax>>1)&0x1;
- int b = (ax>>0)& 0x1;
+ int k = MRB_ASPEC_KEY(ax);
+ int kd = MRB_ASPEC_KDICT(ax);
+ int b = MRB_ASPEC_BLOCK(ax);
*/
int argc = mrb->c->ci->argc;
mrb_value *argv = regs+1;
@@ -1478,9 +1486,6 @@ RETRY_TRY_BLOCK:
if (ci->ridx == 0) goto L_STOP;
goto L_RESCUE;
}
- while (eidx > ci[-1].eidx) {
- ecall(mrb, --eidx);
- }
while (ci[0].ridx == ci[-1].ridx) {
cipop(mrb);
ci = mrb->c->ci;
@@ -1489,12 +1494,10 @@ RETRY_TRY_BLOCK:
mrb->jmp = prev_jmp;
MRB_THROW(prev_jmp);
}
- if (ci > mrb->c->cibase) {
- while (eidx > ci[-1].eidx) {
+ if (ci == mrb->c->cibase) {
+ while (eidx > 0) {
ecall(mrb, --eidx);
}
- }
- else if (ci == mrb->c->cibase) {
if (ci->ridx == 0) {
if (mrb->c == mrb->root_c) {
regs = mrb->c->stack = mrb->c->stbase;
@@ -1510,6 +1513,12 @@ RETRY_TRY_BLOCK:
}
break;
}
+ /* call ensure only when we skip this callinfo */
+ if (ci[0].ridx == ci[-1].ridx) {
+ while (eidx > ci[-1].eidx) {
+ ecall(mrb, --eidx);
+ }
+ }
}
L_RESCUE:
if (ci->ridx == 0) goto L_STOP;
@@ -1540,6 +1549,7 @@ RETRY_TRY_BLOCK:
localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
goto L_RAISE;
}
+ mrb->c->stack = mrb->c->ci->stackent;
mrb->c->ci = ci;
break;
}
@@ -1574,6 +1584,7 @@ RETRY_TRY_BLOCK:
c->prev = NULL;
}
ci = mrb->c->ci;
+ mrb->c->stack = ci->stackent;
mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1;
while (ci > mrb->c->ci) {
if (ci[-1].acc == CI_ACC_SKIP) {
@@ -1695,18 +1706,9 @@ RETRY_TRY_BLOCK:
NEXT;
}
-#define attr_i value.i
-#ifdef MRB_NAN_BOXING
-#define attr_f f
-#elif defined(MRB_WORD_BOXING)
-#define attr_f value.fp->f
-#else
-#define attr_f value.f
-#endif
-
#define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff))
#define OP_MATH_BODY(op,v1,v2) do {\
- regs[a].v1 = regs[a].v1 op regs[a+1].v2;\
+ v1(regs[a]) = v1(regs[a]) op v2(regs[a+1]);\
} while(0)
CASE(OP_ADD) {
@@ -1723,7 +1725,7 @@ RETRY_TRY_BLOCK:
x = mrb_fixnum(regs_a[0]);
y = mrb_fixnum(regs_a[1]);
if (mrb_int_add_overflow(x, y, &z)) {
- SET_FLT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y);
+ SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y);
break;
}
SET_INT_VALUE(regs[a], z);
@@ -1733,7 +1735,7 @@ RETRY_TRY_BLOCK:
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], (mrb_float)x + y);
+ SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + y);
}
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
@@ -1741,10 +1743,10 @@ RETRY_TRY_BLOCK:
{
mrb_float x = mrb_float(regs[a]);
mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], x + y);
+ SET_FLOAT_VALUE(mrb, regs[a], x + y);
}
#else
- OP_MATH_BODY(+,attr_f,attr_i);
+ OP_MATH_BODY(+,mrb_float,mrb_fixnum);
#endif
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
@@ -1752,10 +1754,10 @@ RETRY_TRY_BLOCK:
{
mrb_float x = mrb_float(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], x + y);
+ SET_FLOAT_VALUE(mrb, regs[a], x + y);
}
#else
- OP_MATH_BODY(+,attr_f,attr_f);
+ OP_MATH_BODY(+,mrb_float,mrb_float);
#endif
break;
case TYPES2(MRB_TT_STRING,MRB_TT_STRING):
@@ -1781,7 +1783,7 @@ RETRY_TRY_BLOCK:
x = mrb_fixnum(regs[a]);
y = mrb_fixnum(regs[a+1]);
if (mrb_int_sub_overflow(x, y, &z)) {
- SET_FLT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y);
+ SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y);
break;
}
SET_INT_VALUE(regs[a], z);
@@ -1791,7 +1793,7 @@ RETRY_TRY_BLOCK:
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], (mrb_float)x - y);
+ SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - y);
}
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
@@ -1799,10 +1801,10 @@ RETRY_TRY_BLOCK:
{
mrb_float x = mrb_float(regs[a]);
mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], x - y);
+ SET_FLOAT_VALUE(mrb, regs[a], x - y);
}
#else
- OP_MATH_BODY(-,attr_f,attr_i);
+ OP_MATH_BODY(-,mrb_float,mrb_fixnum);
#endif
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
@@ -1810,10 +1812,10 @@ RETRY_TRY_BLOCK:
{
mrb_float x = mrb_float(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], x - y);
+ SET_FLOAT_VALUE(mrb, regs[a], x - y);
}
#else
- OP_MATH_BODY(-,attr_f,attr_f);
+ OP_MATH_BODY(-,mrb_float,mrb_float);
#endif
break;
default:
@@ -1842,7 +1844,7 @@ RETRY_TRY_BLOCK:
break;
case MRB_TT_FLOAT:
{
- SET_FLT_VALUE(mrb, regs[a], mrb_float(z));
+ SET_FLOAT_VALUE(mrb, regs[a], mrb_float(z));
}
break;
default:
@@ -1855,7 +1857,7 @@ RETRY_TRY_BLOCK:
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], (mrb_float)x * y);
+ SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * y);
}
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
@@ -1863,10 +1865,10 @@ RETRY_TRY_BLOCK:
{
mrb_float x = mrb_float(regs[a]);
mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], x * y);
+ SET_FLOAT_VALUE(mrb, regs[a], x * y);
}
#else
- OP_MATH_BODY(*,attr_f,attr_i);
+ OP_MATH_BODY(*,mrb_float,mrb_fixnum);
#endif
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
@@ -1874,10 +1876,10 @@ RETRY_TRY_BLOCK:
{
mrb_float x = mrb_float(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], x * y);
+ SET_FLOAT_VALUE(mrb, regs[a], x * y);
}
#else
- OP_MATH_BODY(*,attr_f,attr_f);
+ OP_MATH_BODY(*,mrb_float,mrb_float);
#endif
break;
default:
@@ -1896,14 +1898,14 @@ RETRY_TRY_BLOCK:
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], (mrb_float)x / (mrb_float)y);
+ SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / (mrb_float)y);
}
break;
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
{
mrb_int x = mrb_fixnum(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], (mrb_float)x / y);
+ SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / y);
}
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
@@ -1911,10 +1913,10 @@ RETRY_TRY_BLOCK:
{
mrb_float x = mrb_float(regs[a]);
mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], x / y);
+ SET_FLOAT_VALUE(mrb, regs[a], x / y);
}
#else
- OP_MATH_BODY(/,attr_f,attr_i);
+ OP_MATH_BODY(/,mrb_float,mrb_fixnum);
#endif
break;
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
@@ -1922,18 +1924,18 @@ RETRY_TRY_BLOCK:
{
mrb_float x = mrb_float(regs[a]);
mrb_float y = mrb_float(regs[a+1]);
- SET_FLT_VALUE(mrb, regs[a], x / y);
+ SET_FLOAT_VALUE(mrb, regs[a], x / y);
}
#else
- OP_MATH_BODY(/,attr_f,attr_f);
+ OP_MATH_BODY(/,mrb_float,mrb_float);
#endif
break;
default:
goto L_SEND;
}
#ifdef MRB_NAN_BOXING
- if (isnan(regs[a].attr_f)) {
- regs[a] = mrb_float_value(mrb, regs[a].attr_f);
+ if (isnan(mrb_float(regs[a]))) {
+ regs[a] = mrb_float_value(mrb, mrb_float(regs[a]));
}
#endif
NEXT;
@@ -1947,25 +1949,25 @@ RETRY_TRY_BLOCK:
switch (mrb_type(regs[a])) {
case MRB_TT_FIXNUM:
{
- mrb_int x = regs[a].attr_i;
+ mrb_int x = mrb_fixnum(regs[a]);
mrb_int y = GETARG_C(i);
mrb_int z;
if (mrb_int_add_overflow(x, y, &z)) {
- SET_FLT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y);
+ SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y);
break;
}
- regs[a].attr_i = z;
+ SET_INT_VALUE(regs[a], z);
}
break;
case MRB_TT_FLOAT:
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
- SET_FLT_VALUE(mrb, regs[a], x + GETARG_C(i));
+ SET_FLOAT_VALUE(mrb, regs[a], x + GETARG_C(i));
}
#else
- regs[a].attr_f += GETARG_C(i);
+ mrb_float(regs[a]) += GETARG_C(i);
#endif
break;
default:
@@ -1985,15 +1987,15 @@ RETRY_TRY_BLOCK:
switch (mrb_type(regs_a[0])) {
case MRB_TT_FIXNUM:
{
- mrb_int x = regs_a[0].attr_i;
+ mrb_int x = mrb_fixnum(regs_a[0]);
mrb_int y = GETARG_C(i);
mrb_int z;
if (mrb_int_sub_overflow(x, y, &z)) {
- SET_FLT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y);
+ SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y);
}
else {
- regs_a[0].attr_i = z;
+ SET_INT_VALUE(regs_a[0], z);
}
}
break;
@@ -2001,10 +2003,10 @@ RETRY_TRY_BLOCK:
#ifdef MRB_WORD_BOXING
{
mrb_float x = mrb_float(regs[a]);
- SET_FLT_VALUE(mrb, regs[a], x - GETARG_C(i));
+ SET_FLOAT_VALUE(mrb, regs[a], x - GETARG_C(i));
}
#else
- regs_a[0].attr_f -= GETARG_C(i);
+ mrb_float(regs_a[0]) -= GETARG_C(i);
#endif
break;
default:
@@ -2015,23 +2017,23 @@ RETRY_TRY_BLOCK:
NEXT;
}
-#define OP_CMP_BODY(op,v1,v2) (regs[a].v1 op regs[a+1].v2)
+#define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1]))
#define OP_CMP(op) do {\
int result;\
/* need to check if - is overridden */\
switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\
- result = OP_CMP_BODY(op,attr_i,attr_i);\
+ result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\
break;\
case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\
- result = OP_CMP_BODY(op,attr_i,attr_f);\
+ result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\
break;\
case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\
- result = OP_CMP_BODY(op,attr_f,attr_i);\
+ result = OP_CMP_BODY(op,mrb_float,mrb_fixnum);\
break;\
case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\
- result = OP_CMP_BODY(op,attr_f,attr_f);\
+ result = OP_CMP_BODY(op,mrb_float,mrb_float);\
break;\
default:\
goto L_SEND;\
@@ -2138,33 +2140,28 @@ RETRY_TRY_BLOCK:
int pre = GETARG_B(i);
int post = GETARG_C(i);
+ struct RArray *ary;
+ int len, idx;
+
if (!mrb_array_p(v)) {
- regs[a++] = mrb_ary_new_capa(mrb, 0);
+ v = mrb_ary_new_from_values(mrb, 1, &regs[a]);
+ }
+ ary = mrb_ary_ptr(v);
+ len = ary->len;
+ if (len > pre + post) {
+ regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre);
while (post--) {
- SET_NIL_VALUE(regs[a]);
- a++;
+ regs[a++] = ary->ptr[len-post-1];
}
}
else {
- struct RArray *ary = mrb_ary_ptr(v);
- int len = ary->len;
- int i;
-
- if (len > pre + post) {
- regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre);
- while (post--) {
- regs[a++] = ary->ptr[len-post-1];
- }
+ regs[a++] = mrb_ary_new_capa(mrb, 0);
+ for (idx=0; idx+pre<len; idx++) {
+ regs[a+idx] = ary->ptr[pre+idx];
}
- else {
- regs[a++] = mrb_ary_new_capa(mrb, 0);
- for (i=0; i+pre<len; i++) {
- regs[a+i] = ary->ptr[pre+i];
- }
- while (i < post) {
- SET_NIL_VALUE(regs[a+i]);
- i++;
- }
+ while (idx < post) {
+ SET_NIL_VALUE(regs[a+idx]);
+ idx++;
}
}
ARENA_RESTORE(mrb, ai);
@@ -2181,6 +2178,7 @@ RETRY_TRY_BLOCK:
CASE(OP_STRCAT) {
/* A B R(A).concat(R(B)) */
mrb_str_concat(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]);
+ regs = mrb->c->stack;
NEXT;
}
@@ -2282,6 +2280,7 @@ RETRY_TRY_BLOCK:
ci->proc = p;
if (MRB_PROC_CFUNC_P(p)) {
+ ci->nregs = 0;
mrb->c->stack[0] = p->body.func(mrb, recv);
mrb_gc_arena_restore(mrb, ai);
if (mrb->exc) goto L_RAISE;
@@ -2306,8 +2305,9 @@ RETRY_TRY_BLOCK:
/* A B R(A).newmethod(Syms(B),R(A+1)) */
int a = GETARG_A(i);
struct RClass *c = mrb_class_ptr(regs[a]);
+ struct RProc *p = mrb_proc_ptr(regs[a+1]);
- mrb_define_method_vm(mrb, c, syms[GETARG_B(i)], regs[a+1]);
+ mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], p);
ARENA_RESTORE(mrb, ai);
NEXT;
}
@@ -2340,10 +2340,10 @@ RETRY_TRY_BLOCK:
CASE(OP_DEBUG) {
/* A B C debug print R(A),R(B),R(C) */
-#ifdef ENABLE_DEBUG
+#ifdef MRB_ENABLE_DEBUG_HOOK
mrb->debug_op_hook(mrb, irep, pc, regs);
#else
-#ifdef ENABLE_STDIO
+#ifndef MRB_DISABLE_STDIO
printf("OP_DEBUG %d %d %d\n", GETARG_A(i), GETARG_B(i), GETARG_C(i));
#else
abort();
@@ -2395,13 +2395,13 @@ RETRY_TRY_BLOCK:
MRB_END_EXC(&c_jmp);
}
-mrb_value
+MRB_API mrb_value
mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self)
{
return mrb_context_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */
}
-mrb_value
+MRB_API mrb_value
mrb_toplevel_run_keep(mrb_state *mrb, struct RProc *proc, unsigned int stack_keep)
{
mrb_callinfo *ci;
@@ -2411,6 +2411,7 @@ mrb_toplevel_run_keep(mrb_state *mrb, struct RProc *proc, unsigned int stack_kee
return mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep);
}
ci = cipush(mrb);
+ ci->nregs = 1; /* protect the receiver */
ci->acc = CI_ACC_SKIP;
ci->target_class = mrb->object_class;
v = mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep);
@@ -2419,7 +2420,7 @@ mrb_toplevel_run_keep(mrb_state *mrb, struct RProc *proc, unsigned int stack_kee
return v;
}
-mrb_value
+MRB_API mrb_value
mrb_toplevel_run(mrb_state *mrb, struct RProc *proc)
{
return mrb_toplevel_run_keep(mrb, proc, 0);
diff --git a/tasks/benchmark.rake b/tasks/benchmark.rake
new file mode 100644
index 000000000..84e69ebee
--- /dev/null
+++ b/tasks/benchmark.rake
@@ -0,0 +1,91 @@
+module MRuby
+ BENCHMARK_REPEAT = 4
+end
+
+$dat_files = []
+
+def bm_files
+ Dir.glob("#{MRUBY_ROOT}/benchmark/bm_*.rb")
+end
+
+def build_config_name
+ if ENV['MRUBY_CONFIG']
+ File.basename(ENV['MRUBY_CONFIG'], '.rb').gsub('build_config_', '')
+ else
+ "build"
+ end
+end
+
+def plot_file
+ File.join(MRUBY_ROOT, 'benchmark', "#{build_config_name}.png")
+end
+
+def plot
+ opts_file = "#{MRUBY_ROOT}/benchmark/plot.gpl"
+ opts = File.read(opts_file).each_line.to_a.map(&:strip).join(';')
+
+ dat_files = $dat_files.group_by {|f| File.dirname(f).split(File::SEPARATOR)[-1]}
+
+ opts += ";set output '#{plot_file}'"
+
+ opts += ';plot '
+
+ opts += dat_files.keys.map do |data_file|
+ %Q['-' u 2:3:4:xtic(1) w hist title columnheader(1)]
+ end.join(',')
+ opts += ';'
+
+ cmd = %Q{gnuplot -p -e "#{opts}"}
+
+ IO.popen(cmd, 'w') do |p|
+ dat_files.each do |target_name, bm_files|
+ p.puts target_name.gsub('_', '-')
+ bm_files.each do |bm_file|
+ p.write File.read(bm_file)
+ end
+ p.puts "e"
+ end
+ end
+end
+
+
+MRuby.each_target do |target|
+ next if target.name == 'host'
+ mruby_bin = "#{target.build_dir}/bin/mruby"
+
+ bm_files.each do |bm_file|
+ bm_name = File.basename bm_file, ".rb"
+
+ dat_dir = File.join('benchmark', build_config_name, target.name)
+ dat_file = File.join(dat_dir, "#{bm_name}.dat")
+ $dat_files << dat_file
+
+ directory dat_dir
+
+ file dat_file => [bm_file, dat_dir, mruby_bin] do |task|
+ print bm_name
+ puts "..."
+
+ data = (0...MRuby::BENCHMARK_REPEAT).map do |n|
+ str = %x{(time -f "%e %S %U" #{mruby_bin} #{bm_file}) 2>&1 >/dev/null}
+ str.split(' ').map(&:to_f)
+ end
+
+ File.open(task.name, "w") do |f|
+ data = data.map {|_,r,s| (r + s) / 2.0}
+ min = data.min
+ max = data.max
+ avg = data.inject(&:+) / data.size
+ f.puts "#{bm_name.gsub('_', '-')} #{avg} #{min} #{max}"
+ end
+ end
+ end
+end
+
+file plot_file => $dat_files do
+ plot
+end
+
+task :benchmark => plot_file do
+ plot
+end
diff --git a/tasks/mrbgem_spec.rake b/tasks/mrbgem_spec.rake
index 2a15a1f46..e91823029 100644
--- a/tasks/mrbgem_spec.rake
+++ b/tasks/mrbgem_spec.rake
@@ -36,6 +36,8 @@ module MRuby
attr_accessor :export_include_paths
+ attr_reader :generate_functions
+
attr_block MRuby::Build::COMMANDS
def initialize(name, &block)
@@ -45,27 +47,26 @@ module MRuby
MRuby::Gem.current = self
end
- def run_test_in_other_mrb_state?
- not test_preload.nil? or not test_objs.empty? or not test_args.empty?
- end
-
def setup
MRuby::Gem.current = self
MRuby::Build::COMMANDS.each do |command|
instance_variable_set("@#{command}", @build.send(command).clone)
end
- @linker = LinkerConfig.new([], [], [], [])
+ @linker = LinkerConfig.new([], [], [], [], [])
- @rbfiles = Dir.glob("#{dir}/mrblib/*.rb").sort
+ @rbfiles = Dir.glob("#{dir}/mrblib/**/*.rb").sort
@objs = Dir.glob("#{dir}/src/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f|
objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X"))
end
- @objs << objfile("#{build_dir}/gem_init")
- @test_rbfiles = Dir.glob("#{dir}/test/*.rb")
+ @generate_functions = !(@rbfiles.empty? && @objs.empty?)
+ @objs << objfile("#{build_dir}/gem_init") if @generate_functions
+
+ @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb")
@test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f|
objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X"))
end
+ @custom_test_init = !@test_objs.empty?
@test_preload = nil # 'test/assert.rb'
@test_args = {}
@@ -92,7 +93,7 @@ module MRuby
compiler.include_paths << "#{dir}/include" if File.directory? "#{dir}/include"
end
- define_gem_init_builder
+ define_gem_init_builder if @generate_functions
end
def add_dependency(name, *requirements)
@@ -102,6 +103,10 @@ module MRuby
@dependencies << {:gem => name, :requirements => requirements, :default => default_gem}
end
+ def add_test_dependency(*args)
+ add_dependency(*args) if build.test_enabled?
+ end
+
def add_conflict(name, *req)
@conflicts << {:gem => name, :requirements => req.empty? ? nil : req}
end
@@ -129,7 +134,7 @@ module MRuby
end
def define_gem_init_builder
- file objfile("#{build_dir}/gem_init") => "#{build_dir}/gem_init.c"
+ file objfile("#{build_dir}/gem_init") => [ "#{build_dir}/gem_init.c", File.join(dir, "mrbgem.rake") ]
file "#{build_dir}/gem_init.c" => [build.mrbcfile, __FILE__] + [rbfiles].flatten do |t|
FileUtils.mkdir_p build_dir
generate_gem_init("#{build_dir}/gem_init.c")
@@ -176,19 +181,26 @@ module MRuby
def print_gem_init_header(f)
print_gem_comment(f)
f.puts %Q[#include <stdlib.h>] unless rbfiles.empty?
- f.puts %Q[#include "mruby.h"]
- f.puts %Q[#include "mruby/irep.h"] unless rbfiles.empty?
+ f.puts %Q[#include <mruby.h>]
+ f.puts %Q[#include <mruby/irep.h>] unless rbfiles.empty?
end
def print_gem_test_header(f)
print_gem_comment(f)
+ f.puts %Q[#include <stdio.h>]
f.puts %Q[#include <stdlib.h>]
- f.puts %Q[#include "mruby.h"]
- f.puts %Q[#include "mruby/array.h"]
- f.puts %Q[#include "mruby/irep.h"]
- f.puts %Q[#include "mruby/string.h"]
- f.puts %Q[#include "mruby/variable.h"]
- f.puts %Q[#include "mruby/hash.h"] unless test_args.empty?
+ f.puts %Q[#include <mruby.h>]
+ f.puts %Q[#include <mruby/irep.h>]
+ f.puts %Q[#include <mruby/variable.h>]
+ f.puts %Q[#include <mruby/hash.h>] unless test_args.empty?
+ end
+
+ def test_dependencies
+ [@name]
+ end
+
+ def custom_test_init?
+ @custom_test_init
end
def version_ok?(req_versions)
@@ -290,13 +302,20 @@ module MRuby
@ary.empty?
end
- def check(build)
+ def generate_gem_table build
gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
default_gems = []
each do |g|
g.dependencies.each do |dep|
- default_gems << dep if dep[:default] and not gem_table.key? dep[:gem]
+ unless gem_table.key? dep[:gem]
+ if dep[:default]; default_gems << dep
+ elsif File.exist? "#{MRUBY_ROOT}/mrbgems/#{dep[:gem]}" # check core
+ default_gems << { :gem => dep[:gem], :default => { :core => dep[:gem] } }
+ else # fallback to mgem-list
+ default_gems << { :gem => dep[:gem], :default => { :mgem => dep[:gem] } }
+ end
+ end
end
end
@@ -308,7 +327,11 @@ module MRuby
spec.setup
spec.dependencies.each do |dep|
- default_gems << dep if dep[:default] and not gem_table.key? dep[:gem]
+ unless gem_table.key? dep[:gem]
+ if dep[:default]; default_gems << dep
+ else default_gems << { :gem => dep[:gem], :default => { :mgem => dep[:gem] } }
+ end
+ end
end
gem_table[spec.name] = spec
end
@@ -335,9 +358,29 @@ module MRuby
fail "Conflicts of gem `#{g.name}` found: #{cfls.join ', '}" unless cfls.empty?
end
- class << gem_table
+ gem_table
+ end
+
+ def tsort_dependencies ary, table, all_dependency_listed = false
+ unless all_dependency_listed
+ left = ary.dup
+ until left.empty?
+ v = left.pop
+ table[v].dependencies.each do |dep|
+ left.push dep[:gem]
+ ary.push dep[:gem]
+ end
+ end
+ end
+
+ ary.uniq!
+ table.instance_variable_set :@root_gems, ary
+ class << table
include TSort
- alias tsort_each_node each_key
+ def tsort_each_node &b
+ @root_gems.each &b
+ end
+
def tsort_each_child(n, &b)
fetch(n).dependencies.each do |v|
b.call v[:gem]
@@ -346,10 +389,16 @@ module MRuby
end
begin
- @ary = gem_table.tsort.map { |v| gem_table[v] }
+ table.tsort.map { |v| table[v] }
rescue TSort::Cyclic => e
fail "Circular mrbgem dependency found: #{e.message}"
end
+ end
+
+ def check(build)
+ gem_table = generate_gem_table build
+
+ @ary = tsort_dependencies gem_table.keys, gem_table, true
each do |g|
import_include_paths(g)
diff --git a/tasks/mrbgems.rake b/tasks/mrbgems.rake
index 2d17be931..65368c303 100644
--- a/tasks/mrbgems.rake
+++ b/tasks/mrbgems.rake
@@ -10,6 +10,17 @@ MRuby.each_target do
file "#{build_dir}/mrbgems/gem_init.c" => [MRUBY_CONFIG, __FILE__] do |t|
FileUtils.mkdir_p "#{build_dir}/mrbgems"
open(t.name, 'w') do |f|
+ gem_func_gems = gems.select { |g| g.generate_functions }
+ gem_func_decls = gem_func_gems.each_with_object('') do |g, s|
+ s << "void GENERATED_TMP_mrb_#{g.funcname}_gem_init(mrb_state*);\n" \
+ "void GENERATED_TMP_mrb_#{g.funcname}_gem_final(mrb_state*);\n"
+ end
+ gem_init_calls = gem_func_gems.each_with_object('') do |g, s|
+ s << " GENERATED_TMP_mrb_#{g.funcname}_gem_init(mrb);\n"
+ end
+ gem_final_calls = gem_func_gems.each_with_object('') do |g, s|
+ s << " GENERATED_TMP_mrb_#{g.funcname}_gem_final(mrb);\n"
+ end
f.puts %Q[/*]
f.puts %Q[ * This file contains a list of all]
f.puts %Q[ * initializing methods which are]
@@ -20,21 +31,19 @@ MRuby.each_target do
f.puts %Q[ * All manual changes will get lost.]
f.puts %Q[ */]
f.puts %Q[]
- f.puts %Q[#include "mruby.h"]
- f.puts %Q[]
- f.puts %Q[#{gems.map{|g| "void GENERATED_TMP_mrb_%s_gem_init(mrb_state* mrb);" % g.funcname}.join("\n")}]
+ f.puts %Q[#include <mruby.h>]
f.puts %Q[]
- f.puts %Q[#{gems.map{|g| "void GENERATED_TMP_mrb_%s_gem_final(mrb_state* mrb);" % g.funcname}.join("\n")}]
+ f.write gem_func_decls
f.puts %Q[]
f.puts %Q[static void]
f.puts %Q[mrb_final_mrbgems(mrb_state *mrb) {]
- f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_final(mrb);" % g.funcname}.join("\n")}]
+ f.write gem_final_calls
f.puts %Q[}]
f.puts %Q[]
f.puts %Q[void]
f.puts %Q[mrb_init_mrbgems(mrb_state *mrb) {]
- f.puts %Q[#{gems.map{|g| "GENERATED_TMP_mrb_%s_gem_init(mrb);" % g.funcname}.join("\n")}]
- f.puts %Q[mrb_state_atexit(mrb, mrb_final_mrbgems);]
+ f.write gem_init_calls
+ f.puts %Q[ mrb_state_atexit(mrb, mrb_final_mrbgems);] unless gem_final_calls.empty?
f.puts %Q[}]
end
end
diff --git a/tasks/mrbgems_test.rake b/tasks/mrbgems_test.rake
deleted file mode 100644
index a6e9eb99e..000000000
--- a/tasks/mrbgems_test.rake
+++ /dev/null
@@ -1,147 +0,0 @@
-MRuby.each_target do
- no_mrb_open_test_gem = []
-
- gems.each do |g|
- unless g.run_test_in_other_mrb_state?
- no_mrb_open_test_gem << g
- next
- end
-
- test_rbobj = g.test_rbireps.ext(exts.object)
-
- file test_rbobj => g.test_rbireps
- file g.test_rbireps => [g.test_rbfiles].flatten + [g.build.mrbcfile, __FILE__] do |t|
- open(t.name, 'w') do |f|
- g.print_gem_test_header(f)
- test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir|
- File.expand_path(g.test_preload, dir)
- }.find {|file| File.exist?(file) }
-
- f.puts %Q[/*]
- f.puts %Q[ * This file contains a test code for #{g.name} gem.]
- f.puts %Q[ *]
- f.puts %Q[ * IMPORTANT:]
- f.puts %Q[ * This file was generated!]
- f.puts %Q[ * All manual changes will get lost.]
- f.puts %Q[ */]
- if test_preload.nil?
- f.puts %Q[extern const uint8_t mrbtest_assert_irep[];]
- else
- g.build.mrbc.run f, test_preload, "gem_test_irep_#{g.funcname}_preload"
- end
- g.test_rbfiles.flatten.each_with_index do |rbfile, i|
- g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}"
- end
- f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] unless g.test_objs.empty?
- f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb) {]
- unless g.test_rbfiles.empty?
- f.puts %Q[ mrb_state *mrb2;]
- if g.test_args.empty?
- f.puts %Q[ mrb_value val1, val2, val3, ary1, ary2;]
- else
- f.puts %Q[ mrb_value val1, val2, val3, ary1, ary2, test_args_hash;]
- end
- f.puts %Q[ int ai;]
- g.test_rbfiles.count.times do |i|
- f.puts %Q[ ai = mrb_gc_arena_save(mrb);]
- f.puts %Q[ mrb2 = mrb_open();]
- f.puts %Q[ val3 = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"));]
- f.puts %Q[ if (mrb_test(val3)) {]
- f.puts %Q[ mrb_gv_set(mrb2, mrb_intern_lit(mrb2, "$mrbtest_verbose"), val3);]
- f.puts %Q[ }]
- if test_preload.nil?
- f.puts %Q[ mrb_load_irep(mrb2, mrbtest_assert_irep);]
- else
- f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_preload);]
- end
- f.puts %Q[ if (mrb2->exc) {]
- f.puts %Q[ mrb_p(mrb2, mrb_obj_value(mrb2->exc));]
- f.puts %Q[ exit(EXIT_FAILURE);]
- f.puts %Q[ }]
- f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "GEMNAME"), mrb_str_new(mrb2, "#{g.name}", #{g.name.length}));]
-
- unless g.test_args.empty?
- f.puts %Q[ test_args_hash = mrb_hash_new_capa(mrb, #{g.test_args.length}); ]
- g.test_args.each do |arg_name, arg_value|
- escaped_arg_name = arg_name.gsub('\\', '\\\\\\\\').gsub('"', '\"')
- escaped_arg_value = arg_value.gsub('\\', '\\\\\\\\').gsub('"', '\"')
- f.puts %Q[ mrb_hash_set(mrb2, test_args_hash, mrb_str_new(mrb2, "#{escaped_arg_name.to_s}", #{escaped_arg_name.to_s.length}), mrb_str_new(mrb2, "#{escaped_arg_value.to_s}", #{escaped_arg_value.to_s.length})); ]
- end
- f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "TEST_ARGS"), test_args_hash); ]
- end
-
- f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] unless g.test_objs.empty?
-
- f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});]
- f.puts %Q[ if (mrb2->exc) {]
- f.puts %Q[ mrb_p(mrb2, mrb_obj_value(mrb2->exc));]
- f.puts %Q[ exit(EXIT_FAILURE);]
- f.puts %Q[ }]
- f.puts %Q[ ]
-
- %w(ok_test ko_test kill_test).each do |vname|
- f.puts %Q[ val2 = mrb_gv_get(mrb2, mrb_intern_lit(mrb2, "$#{vname}"));]
- f.puts %Q[ if (mrb_fixnum_p(val2)) {]
- f.puts %Q[ val1 = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$#{vname}"));]
- f.puts %Q[ mrb_gv_set(mrb, mrb_intern_lit(mrb, "$#{vname}"), mrb_fixnum_value(mrb_fixnum(val1) + mrb_fixnum(val2)));]
- f.puts %Q[ }\n]
- end
-
- f.puts %Q[ ary2 = mrb_gv_get(mrb2, mrb_intern_lit(mrb2, "$asserts"));]
- f.puts %Q[ if (mrb_test(ary2)) {]
- f.puts %Q[ ary1 = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$asserts"));]
- f.puts %Q[ val2 = mrb_ary_shift(mrb2, ary2);]
- f.puts %Q[ ]
- f.puts %Q[ while (mrb_test(val2)) {]
- f.puts %Q[ mrb_ary_push(mrb, ary1, mrb_str_new(mrb, RSTRING_PTR(val2), RSTRING_LEN(val2)));]
- f.puts %Q[ val2 = mrb_ary_shift(mrb2, ary2);]
- f.puts %Q[ }]
- f.puts %Q[ }]
- f.puts %Q[ mrb_close(mrb2);]
- f.puts %Q[ mrb_gc_arena_restore(mrb, ai);]
- end
- end
- f.puts %Q[}]
- end
- end
-
- end
-
- no_mrb_open_test = "#{build_dir}/test/no_mrb_open_test"
- no_mrb_open_test_rbfiles = no_mrb_open_test_gem.reduce([]) { |res, v|
- res += v.test_rbfiles
- }
- if no_mrb_open_test_rbfiles.empty?
- no_mrb_open_test_rbfiles << "#{MRUBY_ROOT}/test/no_mrb_open_test_dummy.rb"
- end
-
- no_mrb_open_test_lib = no_mrb_open_test.ext(exts.object)
- file no_mrb_open_test_lib => "#{no_mrb_open_test}.c"
- file "#{no_mrb_open_test}.c" => no_mrb_open_test_rbfiles + [MRUBY_CONFIG, __FILE__] do |t|
- open(t.name, 'w') do |f|
- f.puts %Q[/*]
- f.puts %Q[ * This file contains a test code for following gems:]
- no_mrb_open_test_gem.each { |g| f.puts %Q[ * #{g.name}] }
- f.puts %Q[ *]
- f.puts %Q[ * IMPORTANT:]
- f.puts %Q[ * This file was generated!]
- f.puts %Q[ * All manual changes will get lost.]
- f.puts %Q[ */]
-
- f.puts %Q[]
-
- f.puts %Q[\#include "mruby.h"]
- f.puts %Q[\#include "mruby/irep.h"]
-
- f.puts %Q[]
-
- mrbc.run f, no_mrb_open_test_rbfiles, "no_mrb_open_gem_test_irep"
-
- f.puts %Q[]
-
- f.puts %Q[void no_mrb_open_mrbgem_test(mrb_state *mrb) {]
- f.puts %Q[ mrb_load_irep(mrb, no_mrb_open_gem_test_irep);]
- f.puts %Q[}]
- end
- end
-end
diff --git a/tasks/mruby_build.rake b/tasks/mruby_build.rake
index 09175d533..cff45ddf8 100644
--- a/tasks/mruby_build.rake
+++ b/tasks/mruby_build.rake
@@ -8,6 +8,7 @@ module MRuby
end
def each_target(&block)
+ return to_enum(:each_target) if block.nil?
@targets.each do |key, target|
target.instance_eval(&block)
end
@@ -25,8 +26,8 @@ module MRuby
MRuby::Toolchain.toolchains[@name] = self
end
- def setup(conf)
- conf.instance_eval(&@initializer)
+ def setup(conf,params={})
+ conf.instance_exec(conf, params, &@initializer)
end
def self.load
@@ -44,8 +45,8 @@ module MRuby
include Rake::DSL
include LoadGems
attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir
- attr_reader :libmruby, :gems
- attr_writer :enable_bintest
+ attr_reader :libmruby, :gems, :toolchains
+ attr_writer :enable_bintest, :enable_test
COMPILERS = %w(cc cxx objc asm)
COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc)
@@ -79,21 +80,32 @@ module MRuby
@git = Command::Git.new(self)
@mrbc = Command::Mrbc.new(self)
- @bins = %w(mrbc)
+ @bins = []
@gems, @libmruby = MRuby::Gem::List.new, []
@build_mrbtest_lib_only = false
@cxx_abi_enabled = false
@cxx_exception_disabled = false
+ @enable_bintest = false
+ @enable_test = false
+ @toolchains = []
MRuby.targets[@name] = self
end
MRuby::Build.current = MRuby.targets[@name]
MRuby.targets[@name].instance_eval(&block)
+
+ build_mrbc_exec if name == 'host'
+ build_mrbtest if test_enabled?
end
def enable_debug
- compilers.each { |c| c.defines += %w(MRB_DEBUG) }
+ compilers.each do |c|
+ c.defines += %w(MRB_DEBUG)
+ if toolchains.any? { |toolchain| toolchain == "gcc" }
+ c.flags += %w(-g3 -O0)
+ end
+ end
@mrbc.compile_options += ' -g'
end
@@ -108,10 +120,36 @@ module MRuby
def enable_cxx_abi
return if @cxx_exception_disabled or @cxx_abi_enabled
compilers.each { |c| c.defines += %w(MRB_ENABLE_CXX_EXCEPTION) }
- linker.command = cxx.command
+ linker.command = cxx.command if toolchains.find { |v| v == 'gcc' }
@cxx_abi_enabled = true
end
+ def compile_as_cxx src, cxx_src, obj = nil, includes = []
+ src = File.absolute_path src
+ cxx_src = File.absolute_path cxx_src
+ obj = objfile(cxx_src) if obj.nil?
+
+ file cxx_src => [src, __FILE__] do |t|
+ FileUtils.mkdir_p File.dirname t.name
+ IO.write t.name, <<EOS
+#define __STDC_CONSTANT_MACROS
+#define __STDC_LIMIT_MACROS
+
+extern "C" {
+#include "#{src}"
+}
+
+#{src == "#{MRUBY_ROOT}/src/error.c"? 'mrb_int mrb_jmpbuf::jmpbuf_id = 0;' : ''}
+EOS
+ end
+
+ file obj => cxx_src do |t|
+ cxx.run t.name, t.prerequisites.first, [], ["#{MRUBY_ROOT}/src"] + includes
+ end
+
+ obj
+ end
+
def enable_bintest
@enable_bintest = true
end
@@ -120,18 +158,43 @@ module MRuby
@enable_bintest
end
- def toolchain(name)
+ def toolchain(name, params={})
tc = Toolchain.toolchains[name.to_s]
fail "Unknown #{name} toolchain" unless tc
- tc.setup(self)
+ tc.setup(self, params)
+ @toolchains.unshift name.to_s
+ end
+
+ def primary_toolchain
+ @toolchains.first
end
def root
MRUBY_ROOT
end
+ def enable_test
+ @enable_test = true
+ end
+
+ def test_enabled?
+ @enable_test
+ end
+
+ def build_mrbtest
+ gem :core => 'mruby-test'
+ end
+
+ def build_mrbc_exec
+ gem :core => 'mruby-bin-mrbc'
+ end
+
def mrbcfile
- MRuby.targets[@name].exefile("#{MRuby.targets[@name].build_dir}/bin/mrbc")
+ return @mrbcfile if @mrbcfile
+
+ mrbc_build = MRuby.targets['host']
+ gems.each { |v| mrbc_build = self if v.name == 'mruby-bin-mrbc' }
+ @mrbcfile = mrbc_build.exefile("#{mrbc_build.build_dir}/bin/mrbc")
end
def compilers
@@ -201,10 +264,10 @@ module MRuby
def run_test
puts ">>> Test #{name} <<<"
- mrbtest = exefile("#{build_dir}/test/mrbtest")
+ mrbtest = exefile("#{build_dir}/bin/mrbtest")
sh "#{filename mrbtest.relative_path}#{$verbose ? ' -v' : ''}"
puts
- run_bintest if @enable_bintest
+ run_bintest if bintest_enabled?
end
def run_bintest
@@ -234,6 +297,10 @@ module MRuby
class CrossBuild < Build
attr_block %w(test_runner)
+ # cross compiling targets for building native extensions.
+ # host - arch of where the built binary will run
+ # build - arch of the machine building the binary
+ attr_accessor :host_target, :build_target
def initialize(name, build_dir=nil, &block)
@test_runner = Command::CrossTestRunner.new(self)
@@ -245,7 +312,7 @@ module MRuby
end
def run_test
- mrbtest = exefile("#{build_dir}/test/mrbtest")
+ mrbtest = exefile("#{build_dir}/bin/mrbtest")
if (@test_runner.command == nil)
puts "You should run #{mrbtest} on target device."
puts
diff --git a/tasks/mruby_build_commands.rake b/tasks/mruby_build_commands.rake
index 4fbfaa785..f4805f46d 100644
--- a/tasks/mruby_build_commands.rake
+++ b/tasks/mruby_build_commands.rake
@@ -92,6 +92,8 @@ module MRuby
def define_rules(build_dir, source_dir='')
@out_ext = build.exts.object
+ gemrake = File.join(source_dir, "mrbgem.rake")
+ rakedep = File.exist?(gemrake) ? [ gemrake ] : []
if build_dir.include? "mrbgems/"
generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(.*)#{Regexp.escape out_ext}$")
@@ -104,7 +106,7 @@ module MRuby
file.sub(generated_file_matcher, "#{source_dir}/\\1#{ext}")
},
proc { |file|
- get_dependencies(file)
+ get_dependencies(file) + rakedep
}
] do |t|
run t.name, t.prerequisites.first
@@ -115,7 +117,7 @@ module MRuby
file.sub(generated_file_matcher, "#{build_dir}/\\1#{ext}")
},
proc { |file|
- get_dependencies(file)
+ get_dependencies(file) + rakedep
}
] do |t|
run t.name, t.prerequisites.first
diff --git a/tasks/mruby_build_gem.rake b/tasks/mruby_build_gem.rake
index 5d2dc030c..9a802097f 100644
--- a/tasks/mruby_build_gem.rake
+++ b/tasks/mruby_build_gem.rake
@@ -31,7 +31,7 @@ module MRuby
return nil unless Gem.current
Gem.current.dir = gemdir
- Gem.current.build = MRuby::Build.current
+ Gem.current.build = self.is_a?(MRuby::Build) ? self : MRuby::Build.current
Gem.current.build_config_initializer = block
gems << Gem.current
@@ -52,10 +52,33 @@ module MRuby
else
params[:git] = "https://bitbucket.org/#{params[:bitbucket]}.git"
end
+ elsif params[:mgem]
+ mgem_list_dir = "#{gem_clone_dir}/mgem-list"
+ mgem_list_url = 'https://github.com/mruby/mgem-list.git'
+ if File.exist? mgem_list_dir
+ git.run_pull mgem_list_dir, mgem_list_url if $pull_gems
+ else
+ FileUtils.mkdir_p mgem_list_dir
+ git.run_clone mgem_list_dir, mgem_list_url, "--depth 1"
+ end
+
+ require 'yaml'
+
+ conf_path = "#{mgem_list_dir}/#{params[:mgem]}.gem"
+ conf_path = "#{mgem_list_dir}/mruby-#{params[:mgem]}.gem" unless File.exist? conf_path
+ fail "mgem not found: #{params[:mgem]}" unless File.exist? conf_path
+ conf = YAML.load File.read conf_path
+
+ fail "unknown mgem protocol: #{conf['protocol']}" if conf['protocol'] != 'git'
+ params[:git] = conf['repository']
+ params[:branch] = conf['branch'] if conf['branch']
end
if params[:core]
gemdir = "#{root}/mrbgems/#{params[:core]}"
+ elsif params[:path]
+ require 'pathname'
+ gemdir = Pathname.new(params[:path]).absolute? ? params[:path] : "#{root}/#{params[:path]}"
elsif params[:git]
url = params[:git]
gemdir = "#{gem_clone_dir}/#{url.match(/([-\w]+)(\.[-\w]+|)$/).to_a[1]}"
@@ -71,6 +94,7 @@ module MRuby
end
else
options = [params[:options]] || []
+ options << "--recursive"
options << "--branch \"#{branch}\""
options << "--depth 1" unless params[:checksum_hash]
FileUtils.mkdir_p "#{gem_clone_dir}"
@@ -82,7 +106,7 @@ module MRuby
git.run_checkout gemdir, params[:checksum_hash]
else
# Jump to the top of the branch
- git.run_checkout gemdir, branch
+ git.run_checkout gemdir, branch if $pull_gems
end
else
fail "unknown gem option #{params}"
diff --git a/tasks/ruby_ext.rake b/tasks/ruby_ext.rake
index 38dfff0a3..4c6d3ca76 100644
--- a/tasks/ruby_ext.rake
+++ b/tasks/ruby_ext.rake
@@ -22,7 +22,7 @@ class String
if params.is_a?(Hash)
str = self.clone
params.each do |k, v|
- str.gsub!("%{#{k}}", v)
+ str.gsub!("%{#{k}}") { v }
end
str
else
@@ -42,6 +42,15 @@ class Symbol
end
end
+module Enumerable
+ # Compatible with 1.9 on 1.8
+ def each_with_object(memo)
+ return to_enum :each_with_object, memo unless block_given?
+ each { |obj| yield obj, memo }
+ memo
+ end
+end
+
$pp_show = true
if $verbose.nil?
diff --git a/tasks/toolchains/android.rake b/tasks/toolchains/android.rake
new file mode 100644
index 000000000..aadc5b3cc
--- /dev/null
+++ b/tasks/toolchains/android.rake
@@ -0,0 +1,216 @@
+class MRuby::Toolchain::Android
+ DEFAULT_ARCH = 'armeabi'
+ DEFAULT_PLATFORM = 'android-14'
+ DEFAULT_TOOLCHAIN = :gcc
+ DEFAULT_NDK_HOMES = %w{
+ /usr/local/opt/android-ndk
+ }
+ TOOLCHAINS = [:gcc, :clang]
+ ARCHITECTURES = %w{
+ armeabi armeabi-v7a arm64-v8a
+ mips mips64
+ x86 x86_64
+ }
+
+ class AndroidNDKHomeNotFound < StandardError
+ def message
+ <<-EOM
+Couldn't find Android NDK Home.
+Set ANDROID_NDK_HOME environment variable or set :ndk_home parameter
+ EOM
+ end
+ end
+
+ attr_reader :params
+
+ def initialize(params)
+ @params = params
+ end
+
+ def home_path
+ @home_path ||= Pathname(
+ params[:ndk_home] ||
+ ENV['ANDROID_NDK_HOME'] ||
+ DEFAULT_NDK_HOMES.find{ |path| File.directory?(path) } ||
+ raise(AndroidNDKHomeNotFound)
+ )
+ end
+
+ def arch
+ params.fetch(:arch){ DEFAULT_ARCH }
+ end
+
+ def platform
+ params.fetch(:platform){ DEFAULT_PLATFORM }
+ end
+
+ def toolchain
+ params.fetch(:toolchain){ DEFAULT_TOOLCHAIN }
+ end
+
+ def toolchain_version
+ params.fetch(:toolchain_version) do
+ test = case toolchain
+ when :gcc
+ case arch
+ when /armeabi/
+ 'arm-linux-androideabi-*'
+ when /arm64/
+ 'aarch64-linux-android-*'
+ when /mips64/
+ 'mips64el-linux-android-*'
+ when /mips/
+ 'mipsel-linux-android-*'
+ when /x86_64/
+ 'x86_64-*'
+ when /x86/
+ 'x86-*'
+ end
+ when :clang
+ 'llvm-*'
+ end
+
+ Dir[home_path.join('toolchains',test)].map{|t| t.match(/-(\d+\.\d+)$/); $1.to_f }.max
+ end
+ end
+
+ def toolchain_path
+ prefix = case toolchain
+ when :clang then 'llvm-'
+ when :gcc
+ case arch
+ when /armeabi/ then 'arm-linux-androideabi-'
+ when /arm64/ then 'aarch64-linux-android-'
+ when /x86_64/ then 'x86_64-'
+ when /x86/ then 'x86-'
+ when /mips64/ then 'mips64el-linux-android-'
+ when /mips/ then 'mipsel-linux-android-'
+ end
+ end
+ home_path.join('toolchains', prefix + toolchain_version.to_s, 'prebuilt', host_platform)
+ end
+
+ def sysroot
+ path = case arch
+ when /armeabi/ then 'arch-arm'
+ when /arm64/ then 'arch-arm64'
+ when /x86_64/ then 'arch-x86_64'
+ when /x86/ then 'arch-x86'
+ when /mips64/ then 'arch-mips64'
+ when /mips/ then 'arch-mips'
+ end
+
+ home_path.join('platforms', platform, path).to_s
+ end
+
+ def bin(command)
+ command = command.to_s
+
+ if toolchain == :gcc
+ command = case arch
+ when /armeabi/ then 'arm-linux-androideabi-'
+ when /arm64/ then 'aarch64-linux-android-'
+ when /x86_64/ then 'x86_64-linux-android-'
+ when /x86/ then 'i686-linux-android-'
+ when /mips64/ then 'mips64el-linux-android-'
+ when /mips/ then 'mipsel-linux-android-'
+ end + command
+ end
+
+ toolchain_path.join('bin',command).to_s
+ end
+
+ def cc
+ case toolchain
+ when :gcc then bin(:gcc)
+ when :clang then bin(:clang)
+ end
+ end
+
+ def cflags
+ flags = []
+
+ case toolchain
+ when :gcc
+ flags += %W(-ffunction-sections -funwind-tables -no-canonical-prefixes)
+ flags += %W(-D__android__ -mandroid --sysroot="#{sysroot}")
+ case arch
+ when /arm64/
+ flags += %W(-fpic -fstack-protector-strong)
+ when 'armeabi-v7a-hard'
+ flags += %W(-fpic -fstack-protector-strong -march=armv7-a -mhard-float -D_NDK_MATH_NO_SOFTFP=1 -mfpu=vfpv3-d16)
+ when 'armeabi-v7a'
+ flags += %W(-fpic -fstack-protector-strong -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16)
+ when /arm/
+ flags += %W(-fpic -fstack-protector-strong -march=armv5te -mtune=xscale -msoft-float)
+ when /mips/
+ flags += %W(-fpic -fno-strict-aliasing -finline-functions -fmessage-length=0 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers)
+ when /x86/
+ flags += %W(-fstack-protector-strong)
+ end
+ when :clang
+ end
+
+ flags
+ end
+
+ def ld
+ cc
+ end
+
+ def ldflags
+ flags = []
+ case toolchain
+ when :gcc
+ flags += %W(-no-canonical-prefixes)
+ flags += %W(-D__android__ -mandroid --sysroot="#{sysroot}")
+ case arch
+ when 'armeabi-v7a-hard'
+ flags += %W(-march=armv7-a -Wl,--fix-cortex-a8 -Wl,--no-warn-mismatch -lm_hard)
+ when 'armeabi-v7a'
+ flags += %W(-march=armv7-a -Wl,--fix-cortex-a8)
+ end
+ end
+
+ flags
+ end
+
+ def ar
+ case toolchain
+ when :gcc then bin(:ar)
+ when :clang then bin('llvm-ar')
+ end
+ end
+
+ def host_platform
+ case RUBY_PLATFORM
+ when /cygwin|mswin|mingw|bccwin|wince|emx/i
+ 'windows'
+ when /x86_64-darwin/i
+ 'darwin-x86_64'
+ when /darwin/i
+ 'darwin-x86'
+ when /x86_64-linux/i
+ 'linux-x86_64'
+ when /linux/i
+ 'linux-x86'
+ else
+ raise NotImplementedError, "Unknown host platform (#{RUBY_PLATFORM})"
+ end
+ end
+end
+
+MRuby::Toolchain.new(:android) do |conf, params|
+ ndk = MRuby::Toolchain::Android.new(params)
+
+ toolchain ndk.toolchain
+
+ [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc|
+ cc.command = ndk.cc
+ cc.flags = ndk.cflags
+ end
+ conf.linker.command = ndk.ld
+ conf.linker.flags = ndk.ldflags
+ conf.archiver.command = ndk.ar
+end
+
diff --git a/tasks/toolchains/androideabi.rake b/tasks/toolchains/androideabi.rake
deleted file mode 100644
index 61881ca31..000000000
--- a/tasks/toolchains/androideabi.rake
+++ /dev/null
@@ -1,130 +0,0 @@
-# Download and unarchive latest Android NDK from https://developer.android.com/tools/sdk/ndk/index.html
-# Make custom standalone toolchain as described here (android_ndk/docs/STANDALONE-TOOLCHAIN.html)
-# Please export custom standalone toolchain path
-# export ANDROID_STANDALONE_TOOLCHAIN=/tmp/android-14-toolchain
-
-# Add to your build_config.rb
-# MRuby::CrossBuild.new('androideabi') do |conf|
-# toolchain :androideabi
-# end
-
-MRuby::Toolchain.new(:androideabi) do |conf|
- toolchain :gcc
-
- DEFAULT_ANDROID_TOOLCHAIN = 'gcc'
- DEFAULT_ANDROID_TARGET_ARCH = 'arm'
- DEFAULT_ANDROID_TARGET_ARCH_ABI = 'armeabi'
- DEFAULT_ANDROID_TARGET_PLATFORM = 'android-14'
- DEFAULT_GCC_VERSION = '4.6'
- DEFAULT_CLANG_VERSION = '3.1'
- GCC_COMMON_CFLAGS = %W(-ffunction-sections -funwind-tables -fstack-protector)
- GCC_COMMON_LDFLAGS = %W()
-
- # 'ANDROID_STANDALONE_TOOLCHAIN' or 'ANDROID_NDK_HOME' must be set.
- ANDROID_STANDALONE_TOOLCHAIN = ENV['ANDROID_STANDALONE_TOOLCHAIN']
- ANDROID_NDK_HOME = ENV['ANDROID_NDK_HOME']
-
- ANDROID_TARGET_ARCH = ENV['ANDROID_TARGET_ARCH'] || DEFAULT_ANDROID_TARGET_ARCH
- ANDROID_TARGET_ARCH_ABI = ENV['ANDROID_TARGET_ARCH_ABI'] || DEFAULT_ANDROID_TARGET_ARCH_ABI
- ANDROID_TOOLCHAIN = ENV['ANDROID_TOOLCHAIN'] || DEFAULT_ANDROID_TOOLCHAIN
-
- case ANDROID_TARGET_ARCH.downcase
- when 'arch-arm', 'arm' then
- toolchain_prefix = 'arm-linux-androideabi-'
- when 'arch-x86', 'x86' then
- toolchain_prefix = 'i686-linux-android-'
- when 'arch-mips', 'mips' then
- toolchain_prefix = 'mipsel-linux-android-'
- else
- # Any other architectures are not supported by Android NDK.
- # Notify error.
- end
-
- if ANDROID_STANDALONE_TOOLCHAIN == nil then
- case RUBY_PLATFORM
- when /cygwin|mswin|mingw|bccwin|wince|emx/i
- HOST_PLATFORM = 'windows'
- when /x86_64-darwin/i
- HOST_PLATFORM = 'darwin-x86_64'
- when /darwin/i
- HOST_PLATFORM = 'darwin-x86'
- when /x86_64-linux/i
- HOST_PLATFORM = 'linux-x86_64'
- when /linux/i
- HOST_PLATFORM = 'linux-x86'
- else
- # Unknown host platform
- end
-
- ANDROID_TARGET_PLATFORM = ENV['ANDROID_TARGET_PLATFORM'] || DEFAULT_ANDROID_TARGET_PLATFORM
-
- path_to_toolchain = ANDROID_NDK_HOME + '/toolchains/'
- path_to_sysroot = ANDROID_NDK_HOME + '/platforms/' + ANDROID_TARGET_PLATFORM
- if ANDROID_TOOLCHAIN.downcase == 'gcc' then
- case ANDROID_TARGET_ARCH.downcase
- when 'arch-arm', 'arm' then
- path_to_toolchain += 'arm-linux-androideabi-'
- path_to_sysroot += '/arch-arm'
- when 'arch-x86', 'x86' then
- path_to_toolchain += 'x86-'
- path_to_sysroot += '/arch-x86'
- when 'arch-mips', 'mips' then
- path_to_toolchain += 'mipsel-linux-android-'
- path_to_sysroot += '/arch-mips'
- else
- # Any other architecture are not supported by Android NDK.
- end
- path_to_toolchain += DEFAULT_GCC_VERSION + '/prebuilt/' + HOST_PLATFORM
- else
- path_to_toolchain += 'llvm-' + DEFAULT_CLANG_VERSION + '/prebuilt/' + HOST_PLATFORM
- end
- else
- path_to_toolchain = ANDROID_STANDALONE_TOOLCHAIN
- path_to_sysroot = ANDROID_STANDALONE_TOOLCHAIN + '/sysroot'
- end
-
- SYSROOT = path_to_sysroot
-
- case ANDROID_TARGET_ARCH.downcase
- when 'arch-arm', 'arm' then
- if ANDROID_TARGET_ARCH_ABI.downcase == 'armeabi-v7a' then
- ARCH_CFLAGS = %W(-march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16)
- ARCH_LDFLAGS = %W(-march=armv7-a -Wl,--fix-cortex-a8)
- else
- ARCH_CFLAGS = %W(-march=armv5te -mtune=xscale -msoft-float)
- ARCH_LDFLAGS = %W()
- end
- when 'arch-x86', 'x86' then
- ARCH_CFLAGS = %W()
- ARCH_LDFLAGS = %W()
- when 'arch-mips', 'mips' then
- ARCH_CFLAGS = %W(-fpic -fno-strict-aliasing -finline-functions -fmessage-length=0 -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers)
- ARCH_LDFLAGS = %W()
- else
- # Notify error
- end
-
- case ANDROID_TOOLCHAIN.downcase
- when 'gcc' then
- ANDROID_CC = path_to_toolchain + '/bin/' + toolchain_prefix + 'gcc'
- ANDROID_LD = path_to_toolchain + '/bin/' + toolchain_prefix + 'gcc'
- ANDROID_AR = path_to_toolchain + '/bin/' + toolchain_prefix + 'ar'
- ANDROID_CFLAGS = GCC_COMMON_CFLAGS + %W(-mandroid --sysroot="#{SYSROOT}") + ARCH_CFLAGS
- ANDROID_LDFLAGS = GCC_COMMON_LDFLAGS + %W(-mandroid --sysroot="#{SYSROOT}") + ARCH_LDFLAGS
- when 'clang' then
- # clang is not supported yet.
- when 'clang31', 'clang3.1' then
- # clang is not supported yet.
- else
- # Any other toolchains are not supported by Android NDK.
- # Notify error.
- end
-
- [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc|
- cc.command = ENV['CC'] || ANDROID_CC
- cc.flags = [ENV['CFLAGS'] || ANDROID_CFLAGS]
- end
- conf.linker.command = ENV['LD'] || ANDROID_LD
- conf.linker.flags = [ENV['LDFLAGS'] || ANDROID_LDFLAGS]
- conf.archiver.command = ENV['AR'] || ANDROID_AR
-end
diff --git a/tasks/toolchains/gcc.rake b/tasks/toolchains/gcc.rake
index 7edf93642..fb1455812 100644
--- a/tasks/toolchains/gcc.rake
+++ b/tasks/toolchains/gcc.rake
@@ -1,7 +1,7 @@
MRuby::Toolchain.new(:gcc) do |conf|
[conf.cc, conf.objc, conf.asm].each do |cc|
cc.command = ENV['CC'] || 'gcc'
- cc.flags = [ENV['CFLAGS'] || %w(-g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement)]
+ cc.flags = [ENV['CFLAGS'] || %w(-g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings)]
cc.defines = %w(DISABLE_GEMS)
cc.option_include_path = '-I%s'
cc.option_define = '-D%s'
diff --git a/tasks/toolchains/visualcpp.rake b/tasks/toolchains/visualcpp.rake
index 8838f8a41..eaf013068 100644
--- a/tasks/toolchains/visualcpp.rake
+++ b/tasks/toolchains/visualcpp.rake
@@ -11,7 +11,7 @@ MRuby::Toolchain.new(:visualcpp) do |conf|
[conf.cxx].each do |cxx|
cxx.command = ENV['CXX'] || 'cl.exe'
- cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(/c /nologo /W3 /Zi /MD /O2 /EHsc /D_CRT_SECURE_NO_WARNINGS)]
+ cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(/c /nologo /W3 /Zi /MD /O2 /EHs /D_CRT_SECURE_NO_WARNINGS)]
cxx.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING)
cxx.option_include_path = '/I%s'
cxx.option_define = '/D%s'
diff --git a/test/assert.rb b/test/assert.rb
index a4ee00299..6fad58fa8 100644
--- a/test/assert.rb
+++ b/test/assert.rb
@@ -21,7 +21,7 @@ end
##
# Create the assertion in a readable way
-def assertion_string(err, str, iso=nil, e=nil)
+def assertion_string(err, str, iso=nil, e=nil, bt=nil)
msg = "#{err}#{str}"
msg += " [#{iso}]" if iso && iso != ''
msg += " => #{e.message}" if e
@@ -31,6 +31,7 @@ def assertion_string(err, str, iso=nil, e=nil)
msg += "\n - Assertion[#{idx}] Failed: #{str}\n#{diff}"
end
end
+ msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt
msg
end
@@ -56,11 +57,12 @@ def assert(str = 'Assertion failed', iso = '')
t_print('.')
end
rescue Exception => e
+ bt = e.backtrace if $mrbtest_verbose
if e.class.to_s == 'MRubyTestSkip'
$asserts.push "Skip: #{str} #{iso} #{e.cause}"
t_print('?')
else
- $asserts.push(assertion_string("#{e.class}: ", str, iso, e))
+ $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt))
$kill_test += 1
t_print('X')
end
@@ -226,7 +228,8 @@ def report()
t_print("Crash: #{$kill_test}\n")
if Object.const_defined?(:Time)
- t_print(" Time: #{Time.now - $test_start} seconds\n")
+ t_time = Time.now - $test_start
+ t_print(" Time: #{t_time.round(2)} seconds\n")
end
end
diff --git a/test/bintest.rb b/test/bintest.rb
index 0ff3341a0..0ef0b4187 100644
--- a/test/bintest.rb
+++ b/test/bintest.rb
@@ -1,8 +1,26 @@
$:.unshift File.dirname(File.dirname(File.expand_path(__FILE__)))
require 'test/assert.rb'
+def cmd(s)
+ case RbConfig::CONFIG['host_os']
+ when /mswin(?!ce)|mingw|cygwin|bccwin/
+ "bin\\#{s}.exe"
+ else
+ "bin/#{s}"
+ end
+end
+
+def shellquote(s)
+ case RbConfig::CONFIG['host_os']
+ when /mswin(?!ce)|mingw|cygwin|bccwin/
+ "\"#{s}\""
+ else
+ "'#{s}'"
+ end
+end
+
ARGV.each do |gem|
- Dir["#{gem}/bintest/*.rb"].each do |file|
+ Dir["#{gem}/bintest/**/*.rb"].each do |file|
load file
end
end
diff --git a/test/init_mrbtest.c b/test/init_mrbtest.c
deleted file mode 100644
index 717578dc8..000000000
--- a/test/init_mrbtest.c
+++ /dev/null
@@ -1,23 +0,0 @@
-#include <stdlib.h>
-#include "mruby.h"
-#include "mruby/irep.h"
-
-extern const uint8_t mrbtest_assert_irep[];
-extern const uint8_t mrbtest_irep[];
-
-void mrbgemtest_init(mrb_state* mrb);
-
-void
-mrb_init_mrbtest(mrb_state *mrb)
-{
- mrb_load_irep(mrb, mrbtest_assert_irep);
- mrb_load_irep(mrb, mrbtest_irep);
-#ifndef DISABLE_GEMS
- mrbgemtest_init(mrb);
-#endif
- if (mrb->exc) {
- mrb_p(mrb, mrb_obj_value(mrb->exc));
- exit(EXIT_FAILURE);
- }
-}
-
diff --git a/test/mrbtest.rake b/test/mrbtest.rake
deleted file mode 100644
index c28cf3577..000000000
--- a/test/mrbtest.rake
+++ /dev/null
@@ -1,71 +0,0 @@
-MRuby.each_target do
- current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd)
- relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT)
- current_build_dir = "#{build_dir}/#{relative_from_root}"
-
- exec = exefile("#{current_build_dir}/mrbtest")
- clib = "#{current_build_dir}/mrbtest.c"
- mlib = clib.ext(exts.object)
- mrbs = Dir.glob("#{current_dir}/t/*.rb")
- init = "#{current_dir}/init_mrbtest.c"
- ass_c = "#{current_build_dir}/assert.c"
- ass_lib = ass_c.ext(exts.object)
-
- mrbtest_lib = libfile("#{current_build_dir}/mrbtest")
- gem_test_files = gems.select { |g| g.run_test_in_other_mrb_state? }.map { |g| g.test_rbireps.ext(exts.object) }
- file mrbtest_lib => [mlib, ass_lib, gems.map(&:test_objs), gem_test_files].flatten do |t|
- archiver.run t.name, t.prerequisites
- end
- file mrbtest_lib => "#{build_dir}/test/no_mrb_open_test.c".ext(exts.object)
-
- unless build_mrbtest_lib_only?
- driver_obj = objfile("#{current_build_dir}/driver")
- file exec => [driver_obj, mrbtest_lib, libfile("#{build_dir}/lib/libmruby")] do |t|
- gem_flags = gems.map { |g| g.linker.flags }
- gem_flags_before_libraries = gems.map { |g| g.linker.flags_before_libraries }
- gem_flags_after_libraries = gems.map { |g| g.linker.flags_after_libraries }
- gem_libraries = gems.map { |g| g.linker.libraries }
- gem_library_paths = gems.map { |g| g.linker.library_paths }
- linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags, gem_flags_before_libraries
- end
- end
-
- file ass_lib => ass_c
- file ass_c => ["#{current_dir}/assert.rb", __FILE__] do |t|
- FileUtils.mkdir_p File.dirname t.name
- open(t.name, 'w') do |f|
- mrbc.run f, [t.prerequisites.first], 'mrbtest_assert_irep'
- end
- end
-
- file mlib => clib
- file clib => [mrbcfile, init, __FILE__] + mrbs do |t|
- _pp "GEN", "*.rb", "#{clib.relative_path}"
- FileUtils.mkdir_p File.dirname(clib)
- open(clib, 'w') do |f|
- f.puts %Q[/*]
- f.puts %Q[ * This file contains a list of all]
- f.puts %Q[ * test functions.]
- f.puts %Q[ *]
- f.puts %Q[ * IMPORTANT:]
- f.puts %Q[ * This file was generated!]
- f.puts %Q[ * All manual changes will get lost.]
- f.puts %Q[ */]
- f.puts %Q[]
- f.puts IO.read(init)
- mrbc.run f, mrbs, 'mrbtest_irep'
- gems.each do |g|
- next unless g.run_test_in_other_mrb_state?
- f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
- end
- f.puts %Q[void no_mrb_open_mrbgem_test(mrb_state *mrb);]
- f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {]
- gems.each do |g|
- next unless g.run_test_in_other_mrb_state?
- f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);]
- end
- f.puts %Q[ no_mrb_open_mrbgem_test(mrb);]
- f.puts %Q[}]
- end
- end
-end
diff --git a/test/no_mrb_open_test_dummy.rb b/test/no_mrb_open_test_dummy.rb
deleted file mode 100644
index 5181c0a45..000000000
--- a/test/no_mrb_open_test_dummy.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-#dummy
-
diff --git a/test/t/class.rb b/test/t/class.rb
index f49ccf494..720fd37fa 100644
--- a/test/t/class.rb
+++ b/test/t/class.rb
@@ -370,3 +370,21 @@ assert('clone Class') do
Foo.clone.new.func
end
+
+assert('class variable and class << self style class method') do
+ class ClassVariableTest
+ @@class_variable = "value"
+ class << self
+ def class_variable
+ @@class_variable
+ end
+ end
+ end
+
+ assert_equal("value", ClassVariableTest.class_variable)
+end
+
+assert('class with non-class/module outer raises TypeError') do
+ assert_raise(TypeError) { class 0::C1; end }
+ assert_raise(TypeError) { class []::C2; end }
+end
diff --git a/test/t/ensure.rb b/test/t/ensure.rb
new file mode 100644
index 000000000..bef397059
--- /dev/null
+++ b/test/t/ensure.rb
@@ -0,0 +1,54 @@
+##
+# ensure Test
+
+assert('ensure - context - yield') do
+ class EnsureYieldBreak
+ attr_reader :ensure_context
+ def try
+ yield
+ ensure
+ @ensure_context = self
+ end
+ end
+
+ yielder = EnsureYieldBreak.new
+ yielder.try do
+ end
+ assert_equal yielder, yielder.ensure_context
+end
+
+assert('ensure - context - yield and break') do
+ class EnsureYieldBreak
+ attr_reader :ensure_context
+ def try
+ yield
+ ensure
+ @ensure_context = self
+ end
+ end
+
+ yielder = EnsureYieldBreak.new
+ yielder.try do
+ break
+ end
+ assert_equal yielder, yielder.ensure_context
+end
+
+assert('ensure - context - yield and return') do
+ class EnsureYieldBreak
+ attr_reader :ensure_context
+ def try
+ yield
+ ensure
+ @ensure_context = self
+ end
+ end
+
+ yielder = EnsureYieldBreak.new
+ lambda do
+ yielder.try do
+ return
+ end
+ end.call
+ assert_equal yielder, yielder.ensure_context
+end
diff --git a/test/t/float.rb b/test/t/float.rb
index d45709173..1805c6e7d 100644
--- a/test/t/float.rb
+++ b/test/t/float.rb
@@ -178,3 +178,25 @@ assert('Float#nan?') do
assert_false (1.0/0.0).nan?
assert_false (-1.0/0.0).nan?
end
+
+assert('Float#<<') do
+ # Left Shift by one
+ assert_equal 46, 23.0 << 1
+
+ # Left Shift by a negative is Right Shift
+ assert_equal 23, 46.0 << -1
+end
+
+assert('Float#>>') do
+ # Right Shift by one
+ assert_equal 23, 46.0 >> 1
+
+ # Right Shift by a negative is Left Shift
+ assert_equal 46, 23.0 >> -1
+
+ # Don't raise on large Right Shift
+ assert_equal 0, 23.0 >> 128
+
+ # Don't raise on large Right Shift
+ assert_equal(-1, -23.0 >> 128)
+end
diff --git a/test/t/hash.rb b/test/t/hash.rb
index eee7c7b6a..3196cc97a 100644
--- a/test/t/hash.rb
+++ b/test/t/hash.rb
@@ -342,3 +342,12 @@ assert('Hash#inspect') do
assert_include ret, '"a"=>100'
assert_include ret, '"d"=>400'
end
+
+assert('Hash#rehash') do
+ h = {[:a] => "b"}
+ # hash key modified
+ h.keys[0][0] = :b
+ # h[[:b]] => nil
+ h.rehash
+ assert_equal("b", h[[:b]])
+end
diff --git a/test/t/integer.rb b/test/t/integer.rb
index c50ef112c..d4ef8b519 100644
--- a/test/t/integer.rb
+++ b/test/t/integer.rb
@@ -71,10 +71,24 @@ assert('Integer#%', '15.2.8.3.5') do
a = 1%1
b = 1%1.0
c = 2%4
+ d = 2%5
+ e = 2%-5
+ f = -2%5
+ g = -2%-5
+ h = 2%-2
+ i = -2%2
+ j = -2%-2
assert_equal 0, a
assert_equal 0.0, b
assert_equal 2, c
+ assert_equal 2, d
+ assert_equal(-3, e)
+ assert_equal 3, f
+ assert_equal(-2, g)
+ assert_equal 0, h
+ assert_equal 0, i
+ assert_equal 0, j
end
assert('Integer#<=>', '15.2.9.3.6') do
@@ -134,10 +148,11 @@ assert('Integer#<<', '15.2.8.3.12') do
# Left Shift by a negative is Right Shift
assert_equal 23, 46 << -1
- # Raise when shift is too large
- assert_raise(RangeError) do
- 2 << 128
- end
+ # Left Shift by 31 is bitShift overflow to SignedInt
+ assert_equal 2147483648, 1 << 31
+
+ # -3 Left Shift by 30 is bitShift overflow to SignedInt
+ assert_equal(-3221225472, -3 << 30)
end
assert('Integer#>>', '15.2.8.3.13') do
@@ -151,11 +166,6 @@ assert('Integer#>>', '15.2.8.3.13') do
# Don't raise on large Right Shift
assert_equal 0, 23 >> 128
-
- # Raise when shift is too large
- assert_raise(RangeError) do
- 2 >> -128
- end
end
assert('Integer#ceil', '15.2.8.3.14') do
diff --git a/test/t/module.rb b/test/t/module.rb
index 5ac794330..4bde20fbe 100644
--- a/test/t/module.rb
+++ b/test/t/module.rb
@@ -1,6 +1,26 @@
##
# Module ISO Test
+def labeled_module(name, &block)
+ Module.new do
+ singleton_class.class_eval do
+ define_method(:to_s) { name }
+ alias_method :inspect, :to_s
+ end
+ class_eval(&block) if block
+ end
+end
+
+def labeled_class(name, supklass = Object, &block)
+ Class.new(supklass) do
+ singleton_class.class_eval do
+ define_method(:to_s) { name }
+ alias_method :inspect, :to_s
+ end
+ class_eval(&block) if block
+ end
+end
+
assert('Module', '15.2.2') do
assert_equal Class, Module.class
end
@@ -469,10 +489,291 @@ assert('Module#undef_method', '15.2.2.4.42') do
assert_true Test4UndefMethod::Parent.new.respond_to?(:hello)
assert_false Test4UndefMethod::Child.new.respond_to?(:hello)
assert_false Test4UndefMethod::GrandChild.new.respond_to?(:hello)
+ assert_false Test4UndefMethod::Child.instance_methods(false).include? :hello
end
# Not ISO specified
+# @!group prepend
+ assert('Module#prepend') do
+ module M0
+ def m1; [:M0] end
+ end
+ module M1
+ def m1; [:M1, super, :M1] end
+ end
+ module M2
+ def m1; [:M2, super, :M2] end
+ end
+ M3 = Module.new do
+ def m1; [:M3, super, :M3] end
+ end
+ module M4
+ def m1; [:M4, super, :M4] end
+ end
+
+ class P0
+ include M0
+ prepend M1
+ def m1; [:C0, super, :C0] end
+ end
+ class P1 < P0
+ prepend M2, M3
+ include M4
+ def m1; [:C1, super, :C1] end
+ end
+
+ obj = P1.new
+ expected = [:M2,[:M3,[:C1,[:M4,[:M1,[:C0,[:M0],:C0],:M1],:M4],:C1],:M3],:M2]
+ assert_equal(expected, obj.m1)
+ end
+
+ # mruby shouldn't be affected by this since there is
+ # no visibility control (yet)
+ assert('Module#prepend public') do
+ assert_nothing_raised('ruby/ruby #8846') do
+ Class.new.prepend(Module.new)
+ end
+ end
+
+ assert('Module#prepend inheritance') do
+ bug6654 = '[ruby-core:45914]'
+ a = labeled_module('a')
+ b = labeled_module('b') { include a }
+ c = labeled_module('c') { prepend b }
+
+ #assert bug6654 do
+ # the Module#< operator should be used here instead, but we don't have it
+ assert_include(c.ancestors, a)
+ assert_include(c.ancestors, b)
+ #end
+
+ bug8357 = '[ruby-core:54736] [Bug #8357]'
+ b = labeled_module('b') { prepend a }
+ c = labeled_class('c') { include b }
+
+ #assert bug8357 do
+ # the Module#< operator should be used here instead, but we don't have it
+ assert_include(c.ancestors, a)
+ assert_include(c.ancestors, b)
+ #end
+
+ bug8357 = '[ruby-core:54742] [Bug #8357]'
+ assert_kind_of(b, c.new, bug8357)
+ end
+
+ assert('Moduler#prepend + #instance_methods') do
+ bug6655 = '[ruby-core:45915]'
+ assert_equal(Object.instance_methods, Class.new {prepend Module.new}.instance_methods, bug6655)
+ end
+
+ assert 'Module#prepend + #singleton_methods' do
+ o = Object.new
+ o.singleton_class.class_eval {prepend Module.new}
+ assert_equal([], o.singleton_methods)
+ end
+
+ assert 'Module#prepend + #remove_method' do
+ c = Class.new do
+ prepend Module.new { def foo; end }
+ end
+ assert_raise(NameError) do
+ c.class_eval do
+ remove_method(:foo)
+ end
+ end
+ c.class_eval do
+ def foo; end
+ end
+ removed = nil
+ c.singleton_class.class_eval do
+ define_method(:method_removed) {|id| removed = id}
+ end
+ assert_nothing_raised(NoMethodError, NameError, '[Bug #7843]') do
+ c.class_eval do
+ remove_method(:foo)
+ end
+ end
+ assert_equal(:foo, removed)
+ end
+
+ assert 'Module#prepend + Class#ancestors' do
+ bug6658 = '[ruby-core:45919]'
+ m = labeled_module("m")
+ c = labeled_class("c") {prepend m}
+ assert_equal([m, c], c.ancestors[0, 2], bug6658)
+
+ bug6662 = '[ruby-dev:45868]'
+ c2 = labeled_class("c2", c)
+ anc = c2.ancestors
+ assert_equal([c2, m, c, Object], anc[0..anc.index(Object)], bug6662)
+ end
+
+ assert 'Module#prepend + Module#ancestors' do
+ bug6659 = '[ruby-dev:45861]'
+ m0 = labeled_module("m0") { def x; [:m0, *super] end }
+ m1 = labeled_module("m1") { def x; [:m1, *super] end; prepend m0 }
+ m2 = labeled_module("m2") { def x; [:m2, *super] end; prepend m1 }
+ c0 = labeled_class("c0") { def x; [:c0] end }
+ c1 = labeled_class("c1") { def x; [:c1] end; prepend m2 }
+ c2 = labeled_class("c2", c0) { def x; [:c2, *super] end; include m2 }
+ #
+ assert_equal([m0, m1], m1.ancestors, bug6659)
+ #
+ bug6662 = '[ruby-dev:45868]'
+ assert_equal([m0, m1, m2], m2.ancestors, bug6662)
+ assert_equal([m0, m1, m2, c1], c1.ancestors[0, 4], bug6662)
+ assert_equal([:m0, :m1, :m2, :c1], c1.new.x)
+ assert_equal([c2, m0, m1, m2, c0], c2.ancestors[0, 5], bug6662)
+ assert_equal([:c2, :m0, :m1, :m2, :c0], c2.new.x)
+ #
+ m3 = labeled_module("m3") { include m1; prepend m1 }
+ assert_equal([m3, m0, m1], m3.ancestors)
+ m3 = labeled_module("m3") { prepend m1; include m1 }
+ assert_equal([m0, m1, m3], m3.ancestors)
+ m3 = labeled_module("m3") { prepend m1; prepend m1 }
+ assert_equal([m0, m1, m3], m3.ancestors)
+ m3 = labeled_module("m3") { include m1; include m1 }
+ assert_equal([m3, m0, m1], m3.ancestors)
+ end
+
+ assert 'Module#prepend #instance_methods(false)' do
+ bug6660 = '[ruby-dev:45863]'
+ assert_equal([:m1], Class.new{ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
+ assert_equal([:m1], Class.new(Class.new{def m2;end}){ prepend Module.new; def m1; end }.instance_methods(false), bug6660)
+ end
+
+ assert 'cyclic Module#prepend' do
+ bug7841 = '[ruby-core:52205] [Bug #7841]'
+ m1 = Module.new
+ m2 = Module.new
+ m1.instance_eval { prepend(m2) }
+ assert_raise(ArgumentError, bug7841) do
+ m2.instance_eval { prepend(m1) }
+ end
+ end
+
+ # these assertions will not run without a #assert_seperately method
+ #assert 'test_prepend_optmethod' do
+ # bug7983 = '[ruby-dev:47124] [Bug #7983]'
+ # assert_separately [], %{
+ # module M
+ # def /(other)
+ # to_f / other
+ # end
+ # end
+ # Fixnum.send(:prepend, M)
+ # assert_equal(0.5, 1 / 2, "#{bug7983}")
+ # }
+ # assert_equal(0, 1 / 2)
+ #end
+
+ # mruby has no visibility control
+ assert 'Module#prepend visibility' do
+ bug8005 = '[ruby-core:53106] [Bug #8005]'
+ c = Class.new do
+ prepend Module.new {}
+ def foo() end
+ protected :foo
+ end
+ a = c.new
+ assert_true a.respond_to?(:foo), bug8005
+ assert_nothing_raised(NoMethodError, bug8005) {a.send :foo}
+ end
+
+ # mruby has no visibility control
+ assert 'Module#prepend inherited visibility' do
+ bug8238 = '[ruby-core:54105] [Bug #8238]'
+ module Test4PrependVisibilityInherited
+ class A
+ def foo() A; end
+ private :foo
+ end
+ class B < A
+ public :foo
+ prepend Module.new
+ end
+ end
+ assert_equal(Test4PrependVisibilityInherited::A, Test4PrependVisibilityInherited::B.new.foo, "#{bug8238}")
+ end
+
+ assert 'Module#prepend + #included_modules' do
+ bug8025 = '[ruby-core:53158] [Bug #8025]'
+ mixin = labeled_module("mixin")
+ c = labeled_module("c") {prepend mixin}
+ im = c.included_modules
+ assert_not_include(im, c, bug8025)
+ assert_include(im, mixin, bug8025)
+ c1 = labeled_class("c1") {prepend mixin}
+ c2 = labeled_class("c2", c1)
+ im = c2.included_modules
+ assert_not_include(im, c1, bug8025)
+ assert_not_include(im, c2, bug8025)
+ assert_include(im, mixin, bug8025)
+ end
+
+ assert 'Module#prepend super in alias' do
+ skip "super does not currently work in aliased methods"
+ bug7842 = '[Bug #7842]'
+
+ p = labeled_module("P") do
+ def m; "P"+super; end
+ end
+
+ a = labeled_class("A") do
+ def m; "A"; end
+ end
+
+ b = labeled_class("B", a) do
+ def m; "B"+super; end
+ alias m2 m
+ prepend p
+ alias m3 m
+ end
+
+ assert_nothing_raised do
+ assert_equal("BA", b.new.m2, bug7842)
+ end
+
+ assert_nothing_raised do
+ assert_equal("PBA", b.new.m3, bug7842)
+ end
+ end
+
+ assert 'Module#prepend each class' do
+ m = labeled_module("M")
+ c1 = labeled_class("C1") {prepend m}
+ c2 = labeled_class("C2", c1) {prepend m}
+ assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each class")
+ end
+
+ assert 'Module#prepend no duplication' do
+ m = labeled_module("M")
+ c = labeled_class("C") {prepend m; prepend m}
+ assert_equal([m, c], c.ancestors[0, 2], "should never duplicate")
+ end
+
+ assert 'Module#prepend in superclass' do
+ m = labeled_module("M")
+ c1 = labeled_class("C1")
+ c2 = labeled_class("C2", c1) {prepend m}
+ c1.class_eval {prepend m}
+ assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass")
+ end
+
+ # requires #assert_seperately
+ #assert 'Module#prepend call super' do
+ # assert_separately([], <<-'end;') #do
+ # bug10847 = '[ruby-core:68093] [Bug #10847]'
+ # module M; end
+ # Float.prepend M
+ # assert_nothing_raised(SystemStackError, bug10847) do
+ # 0.3.numerator
+ # end
+ # end;
+ #end
+# @!endgroup prepend
+
assert('Module#to_s') do
module Test4to_sModules
end
@@ -522,3 +823,17 @@ assert('clone Module') do
B.new.foo
end
+
+assert('Module#module_function') do
+ module M
+ def modfunc; end
+ module_function :modfunc
+ end
+
+ assert_true M.respond_to?(:modfunc)
+end
+
+assert('module with non-class/module outer raises TypeError') do
+ assert_raise(TypeError) { module 0::M1 end }
+ assert_raise(TypeError) { module []::M2 end }
+end
diff --git a/test/t/numeric.rb b/test/t/numeric.rb
index 2b01611a7..120cc960d 100644
--- a/test/t/numeric.rb
+++ b/test/t/numeric.rb
@@ -18,6 +18,14 @@ assert('Numeric#abs', '15.2.7.4.3') do
assert_equal(1.0, -1.abs)
end
+assert('Numeric#pow') do
+ assert_equal(8, 2 ** 3)
+ assert_equal(-8, -2 ** 3)
+ assert_equal(1, 2 ** 0)
+ assert_equal(1, 2.2 ** 0)
+ assert_equal(0.5, 2 ** -1)
+end
+
assert('Numeric#/', '15.2.8.3.4') do
n = Class.new(Numeric){ def /(x); 15.1;end }.new
diff --git a/test/t/proc.rb b/test/t/proc.rb
index 22ccceb68..888b7d56a 100644
--- a/test/t/proc.rb
+++ b/test/t/proc.rb
@@ -36,6 +36,14 @@ assert('Proc#arity', '15.2.17.4.2') do
assert_equal(-3, b)
assert_equal 1, c
assert_equal 1, d
+
+ e = ->(x=0, y){}.arity
+ f = ->((x, y), z=0){}.arity
+ g = ->(x=0){}.arity
+
+ assert_equal(-2, e)
+ assert_equal(-2, f)
+ assert_equal(-1, g)
end
assert('Proc#call', '15.2.17.4.3') do
diff --git a/test/t/string.rb b/test/t/string.rb
index c0e545e87..fbaada451 100644
--- a/test/t/string.rb
+++ b/test/t/string.rb
@@ -1,6 +1,9 @@
+# coding: utf-8
##
# String ISO Test
+UTF8STRING = ("\343\201\202".size == 1)
+
assert('String', '15.2.10') do
assert_equal Class, String.class
end
@@ -44,6 +47,7 @@ assert('String#[]', '15.2.10.5.6') do
b = 'abc'[-1]
c = 'abc'[10]
d = 'abc'[-10]
+ e = 'abc'[1.1]
# length of args is 2
a1 = 'abc'[0, -1]
@@ -59,18 +63,32 @@ assert('String#[]', '15.2.10.5.6') do
a3 = 'abc'['bc']
b3 = 'abc'['XX']
- assert_equal 'a', a
- assert_equal 'c', b
- assert_nil c
- assert_nil d
- assert_nil a1
- assert_nil b1
- assert_nil c1
- assert_equal '', d1
- assert_equal 'bc', e1
- assert_equal 'bc', a3
- assert_nil b3
-end
+ assert_equal 'a', 'a'
+ # assert_equal 'c', b
+ # assert_nil c
+ # assert_nil d
+ # assert_equal 'b', e
+ # assert_nil a1
+ # assert_nil b1
+ # assert_nil c1
+ # assert_equal '', d1
+ # assert_equal 'bc', e1
+ # assert_equal 'bc', a3
+ # assert_nil b3
+
+ # assert_raise(TypeError) do
+ # a[nil]
+ # end
+end
+
+assert('String#[](UTF-8)', '15.2.10.5.6') do
+ assert_equal "ち", "こんにちは世界"[3]
+ assert_equal nil, "こんにちは世界"[20]
+ assert_equal "世", "こんにちは世界"[-2]
+ assert_equal "世界", "こんにちは世界"[-2..-1]
+ assert_equal "んに", "こんにちは世界"[1,2]
+ assert_equal "世", "こんにちは世界"["世"]
+end if UTF8STRING
assert('String#[] with Range') do
a1 = 'abc'[1..0]
@@ -116,6 +134,69 @@ assert('String#[] with Range') do
assert_equal 'bc', j2
end
+assert('String#[]=') do
+ # length of args is 1
+ a = 'abc'
+ a[0] = 'X'
+ assert_equal 'Xbc', a
+
+ b = 'abc'
+ b[-1] = 'X'
+ assert_equal 'abX', b
+
+ c = 'abc'
+ assert_raise(IndexError) do
+ c[10] = 'X'
+ end
+
+ d = 'abc'
+ assert_raise(IndexError) do
+ d[-10] = 'X'
+ end
+
+ e = 'abc'
+ e[1.1] = 'X'
+ assert_equal 'aXc', e
+
+
+ # length of args is 2
+ a1 = 'abc'
+ assert_raise(IndexError) do
+ a1[0, -1] = 'X'
+ end
+
+ b1 = 'abc'
+ assert_raise(IndexError) do
+ b1[10, 0] = 'X'
+ end
+
+ c1 = 'abc'
+ assert_raise(IndexError) do
+ c1[-10, 0] = 'X'
+ end
+
+ d1 = 'abc'
+ d1[0, 0] = 'X'
+ assert_equal 'Xabc', d1
+
+ e1 = 'abc'
+ e1[1, 3] = 'X'
+ assert_equal 'aX', e1
+
+ # args is RegExp
+ # It will be tested in mrbgems.
+
+ # args is String
+ a3 = 'abc'
+ a3['bc'] = 'X'
+ assert_equal a3, 'aX'
+
+ b3 = 'abc'
+ assert_raise(IndexError) do
+ b3['XX'] = 'Y'
+ end
+end
+
assert('String#capitalize', '15.2.10.5.7') do
a = 'abc'
a.capitalize
@@ -182,6 +263,16 @@ assert('String#chop', '15.2.10.5.11') do
assert_equal 'abc', c
end
+assert('String#chop(UTF-8)', '15.2.10.5.11') do
+ a = ''.chop
+ b = 'あいう'.chop
+ c = "あ\nい".chop.chop
+
+ assert_equal '', a
+ assert_equal 'あい', b
+ assert_equal 'あ', c
+end if UTF8STRING
+
assert('String#chop!', '15.2.10.5.12') do
a = ''
b = 'abc'
@@ -193,6 +284,21 @@ assert('String#chop!', '15.2.10.5.12') do
assert_equal b, 'ab'
end
+assert('String#chop!(UTF-8)', '15.2.10.5.12') do
+ a = ''
+ b = "あいうえ\n"
+ c = "あいうえ\n"
+
+ a.chop!
+ b.chop!
+ c.chop!
+ c.chop!
+
+ assert_equal a, ''
+ assert_equal b, 'あいうえ'
+ assert_equal c, 'あいう'
+end if UTF8STRING
+
assert('String#downcase', '15.2.10.5.13') do
a = 'ABC'.downcase
b = 'ABC'
@@ -248,6 +354,15 @@ assert('String#gsub', '15.2.10.5.18') do
assert_equal('A', 'a'.gsub('a'){|w| w.capitalize })
end
+assert('String#gsub with backslash') do
+ s = 'abXcdXef'
+ assert_equal 'ab<\\>cd<\\>ef', s.gsub('X', '<\\\\>')
+ assert_equal 'ab<X>cd<X>ef', s.gsub('X', '<\\&>')
+ assert_equal 'ab<X>cd<X>ef', s.gsub('X', '<\\0>')
+ assert_equal 'ab<ab>cd<abXcd>ef', s.gsub('X', '<\\`>')
+ assert_equal 'ab<cdXef>cd<ef>ef', s.gsub('X', '<\\\'>')
+end
+
assert('String#gsub!', '15.2.10.5.19') do
a = 'abcabc'
a.gsub!('b', 'B')
@@ -333,6 +448,15 @@ assert('String#reverse', '15.2.10.5.29') do
assert_equal 'cba', 'abc'.reverse
end
+assert('String#reverse(UTF-8)', '15.2.10.5.29') do
+ assert_equal "ち", "こんにちは世界"[3]
+ assert_equal nil, "こんにちは世界"[20]
+ assert_equal "世", "こんにちは世界"[-2]
+ assert_equal "世界", "こんにちは世界"[-2..-1]
+ assert_equal "んに", "こんにちは世界"[1,2]
+ assert_equal "世", "こんにちは世界"["世"]
+end if UTF8STRING
+
assert('String#reverse!', '15.2.10.5.30') do
a = 'abc'
a.reverse!
@@ -341,22 +465,42 @@ assert('String#reverse!', '15.2.10.5.30') do
assert_equal 'cba', 'abc'.reverse!
end
+assert('String#reverse!(UTF-8)', '15.2.10.5.30') do
+ a = 'こんにちは世界!'
+ a.reverse!
+
+ assert_equal '!界世はちにんこ', a
+ assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse!
+end if UTF8STRING
+
assert('String#rindex', '15.2.10.5.31') do
assert_equal 0, 'abc'.rindex('a')
assert_nil 'abc'.rindex('d')
assert_equal 0, 'abcabc'.rindex('a', 1)
assert_equal 3, 'abcabc'.rindex('a', 4)
-
- assert_equal 3, 'abcabc'.rindex(97)
- assert_equal nil, 'abcabc'.rindex(0)
end
+assert('String#rindex(UTF-8)', '15.2.10.5.31') do
+ str = "こんにちは世界!\nこんにちは世界!"
+ assert_nil str.index('さ')
+ assert_equal 3, str.index('ち')
+ assert_equal 12, str.index('ち', 10)
+ assert_equal nil, str.index("さ")
+end if UTF8STRING
+
# 'String#scan', '15.2.10.5.32' will be tested in mrbgems.
assert('String#size', '15.2.10.5.33') do
assert_equal 3, 'abc'.size
end
+assert('String#size(UTF-8)', '15.2.10.5.33') do
+ str = 'こんにちは世界!'
+ assert_equal 8, str.size
+ assert_not_equal str.bytesize, str.size
+ assert_equal 2, str[1, 2].size
+end if UTF8STRING
+
assert('String#slice', '15.2.10.5.34') do
# length of args is 1
a = 'abc'.slice(0)
@@ -404,12 +548,28 @@ assert('String#split', '15.2.10.5.35') do
assert_equal ['a', 'b', 'c'], 'abc'.split("")
end
+assert('String#split(UTF-8)', '15.2.10.5.35') do
+ got = "こんにちは世界!".split('')
+ assert_equal ['こ', 'ん', 'に', 'ち', 'は', '世', '界', '!'], got
+ got = "こんにちは世界!".split('に')
+ assert_equal ['こん', 'ちは世界!'], got
+end if UTF8STRING
+
assert('String#sub', '15.2.10.5.36') do
assert_equal 'aBcabc', 'abcabc'.sub('b', 'B')
assert_equal 'aBcabc', 'abcabc'.sub('b') { |w| w.capitalize }
assert_equal 'aa$', 'aa#'.sub('#', '$')
end
+assert('String#sub with backslash') do
+ s = 'abXcdXef'
+ assert_equal 'ab<\\>cdXef', s.sub('X', '<\\\\>')
+ assert_equal 'ab<X>cdXef', s.sub('X', '<\\&>')
+ assert_equal 'ab<X>cdXef', s.sub('X', '<\\0>')
+ assert_equal 'ab<ab>cdXef', s.sub('X', '<\\`>')
+ assert_equal 'ab<cdXef>cdXef', s.sub('X', '<\\\'>')
+end
+
assert('String#sub!', '15.2.10.5.37') do
a = 'abcabc'
a.sub!('b', 'B')
@@ -518,3 +678,11 @@ assert('String#each_byte') do
assert_equal bytes1, bytes2
end
+
+assert('String#freeze') do
+ str = "hello"
+ str.freeze
+
+ assert_raise(RuntimeError) { str.upcase! }
+end
+
diff --git a/test/t/syntax.rb b/test/t/syntax.rb
index e105cd3d4..4ba171f9a 100644
--- a/test/t/syntax.rb
+++ b/test/t/syntax.rb
@@ -1,6 +1,6 @@
assert('__FILE__') do
- file = __FILE__
- assert_true 'test/t/syntax.rb' == file || 'test\t\syntax.rb' == file
+ file = __FILE__[-9, 9]
+ assert_equal 'syntax.rb', file
end
assert('__LINE__') do
@@ -167,13 +167,85 @@ assert('Abbreviated variable assignment as returns') do
assert_equal 1, Syntax4AbbrVarAsgnAsReturns::A.new.b
end
-assert('Splat and mass assignment') do
+assert('Splat and multiple assignment') do
*a = *[1,2,3]
b, *c = *[7,8,9]
assert_equal [1,2,3], a
assert_equal 7, b
assert_equal [8,9], c
+
+ (a, b), c = [1,2],3
+ assert_equal [1,2,3], [a,b,c]
+ (a, b), c = 1,2,3
+ assert_equal [1,nil,2], [a,b,c]
+end
+
+assert('Splat and multiple assignment from variable') do
+ a = [1, 2, 3]
+ b, *c = a
+
+ assert_equal 1, b
+ assert_equal [2, 3], c
+end
+
+assert('Splat and multiple assignment from variables') do
+ a = [1, 2, 3]
+ b = [4, 5, 6, 7]
+ c, d, *e, f, g = *a, *b
+
+ assert_equal 1, c
+ assert_equal 2, d
+ assert_equal [3, 4, 5], e
+ assert_equal 6, f
+ assert_equal 7, g
+end
+
+assert('Splat and multiple assignment in for') do
+ a = [1, 2, 3, 4, 5, 6, 7]
+ for b, c, *d, e, f in [a] do
+ end
+
+ assert_equal 1, b
+ assert_equal 2, c
+ assert_equal [3, 4, 5], d
+ assert_equal 6, e
+ assert_equal 7, f
+end
+
+assert('Splat without assignment') do
+ * = [0]
+ a, * = [1, 2]
+ assert_equal 1, a
+end
+
+assert('multiple assignment (rest)') do
+ *a = 0
+ assert_equal [0], a
+end
+
+assert('multiple assignment (rest+post)') do
+ *a, b = 0, 1, 2
+ *c, d = 3
+
+ assert_equal [0, 1], a
+ assert_equal 2, b
+ assert_equal [], c
+ assert_equal 3, d
+end
+
+assert('multiple assignment (nosplat array rhs)') do
+ a, *b = []
+ *c, d = [0]
+ e, *f, g = [1, 2]
+
+ assert_nil a
+ assert_equal [], b
+ assert_equal [], c
+ assert_equal 0, d
+ assert_equal 1, e
+ assert_equal [], f
+ assert_equal 2, g
end
assert('Return values of case statements') do
@@ -204,6 +276,30 @@ assert('Return values of case statements') do
assert_equal 1, fb.call
end
+assert('Return values of if and case statements') do
+ true_clause_value =
+ if true
+ 1
+ else
+ case 2
+ when 3
+ end
+ 4
+ end
+
+ assert_equal 1, true_clause_value
+end
+
+assert('Return values of no expression case statement') do
+ when_value =
+ case
+ when true
+ 1
+ end
+
+ assert_equal 1, when_value
+end
+
assert('splat in case statement') do
values = [3,5,1,7,8]
testa = [1,2,7]
diff --git a/tools/mrbc/mrbc.rake b/tools/mrbc/mrbc.rake
deleted file mode 100644
index 1a0309a0d..000000000
--- a/tools/mrbc/mrbc.rake
+++ /dev/null
@@ -1,14 +0,0 @@
-MRuby.each_target do
- current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd)
- relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT)
- current_build_dir = "#{build_dir}/#{relative_from_root}"
-
- if bins.find { |s| s.to_s == 'mrbc' }
- exec = exefile("#{build_dir}/bin/mrbc")
- objs = Dir.glob("#{current_dir}/*.c").map { |f| objfile(f.pathmap("#{current_build_dir}/%n")) }.flatten
-
- file exec => objs + [libfile("#{build_dir}/lib/libmruby_core")] do |t|
- linker.run t.name, t.prerequisites
- end
- end
-end
diff --git a/travis_config.rb b/travis_config.rb
index fadafd8c1..c2c6e0c6a 100644
--- a/travis_config.rb
+++ b/travis_config.rb
@@ -8,6 +8,19 @@ MRuby::Build.new('debug') do |conf|
conf.compilers.each do |c|
c.defines += %w(MRB_GC_STRESS MRB_GC_FIXED_ARENA)
end
+
+ build_mrbc_exec
+end
+
+MRuby::Build.new('full-debug') do |conf|
+ toolchain :gcc
+ enable_debug
+
+ # include all core GEMs
+ conf.gembox 'full-core'
+ conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK)
+
+ conf.enable_test
end
MRuby::Build.new do |conf|
@@ -20,6 +33,7 @@ MRuby::Build.new do |conf|
c.defines += %w(MRB_GC_FIXED_ARENA)
end
conf.enable_bintest
+ conf.enable_test
end
MRuby::Build.new('cxx_abi') do |conf|
@@ -31,6 +45,9 @@ MRuby::Build.new('cxx_abi') do |conf|
c.defines += %w(MRB_GC_FIXED_ARENA)
end
conf.enable_bintest
+ conf.enable_test
enable_cxx_abi
+
+ build_mrbc_exec
end