summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYukihiro "Matz" Matsumoto <[email protected]>2019-07-17 10:35:41 +0900
committerGitHub <[email protected]>2019-07-17 10:35:41 +0900
commitd605b72c1d6fa4564a0a5e88535504b6850463b5 (patch)
tree774fc0de56002abb3bb2b1c3387ff08f91876d17
parent2af92d0ebcbeca6d3d85a27c8193273080a63090 (diff)
parent9af3b7c6258de327218dd04e69d76ae68caf17b1 (diff)
downloadmruby-d605b72c1d6fa4564a0a5e88535504b6850463b5.tar.gz
mruby-d605b72c1d6fa4564a0a5e88535504b6850463b5.zip
Merge branch 'master' into i110/inspect-recursion
-rw-r--r--.gitignore23
-rw-r--r--.travis.yml7
-rw-r--r--.yardopts2
-rw-r--r--AUTHORS8
-rw-r--r--LEGAL2
-rw-r--r--LICENSE (renamed from MITL)2
-rw-r--r--README.md14
-rw-r--r--Rakefile40
-rw-r--r--TODO2
-rw-r--r--appveyor.yml18
-rw-r--r--appveyor_config.rb2
-rw-r--r--benchmark/bm_app_lc_fizzbuzz.rb3
-rw-r--r--bin/.gitkeep0
-rw-r--r--build_config.rb5
-rw-r--r--doc/guides/debugger.md2
-rw-r--r--doc/guides/mrbconf.md56
-rw-r--r--doc/guides/mrbgems.md4
-rw-r--r--doc/limitations.md76
-rw-r--r--doc/opcode.md127
-rw-r--r--examples/targets/build_config_android_arm64-v8a.rb4
-rw-r--r--examples/targets/build_config_android_armeabi.rb4
-rw-r--r--include/mrbconf.h95
-rw-r--r--include/mruby.h98
-rw-r--r--include/mruby/array.h20
-rw-r--r--include/mruby/boxing_nan.h2
-rw-r--r--include/mruby/boxing_word.h56
-rw-r--r--include/mruby/class.h37
-rw-r--r--include/mruby/common.h17
-rw-r--r--include/mruby/compile.h19
-rw-r--r--include/mruby/data.h11
-rw-r--r--include/mruby/debug.h10
-rw-r--r--include/mruby/dump.h10
-rw-r--r--include/mruby/error.h2
-rw-r--r--include/mruby/hash.h65
-rw-r--r--include/mruby/irep.h33
-rw-r--r--include/mruby/istruct.h4
-rw-r--r--include/mruby/numeric.h10
-rw-r--r--include/mruby/object.h11
-rw-r--r--include/mruby/opcode.h186
-rw-r--r--include/mruby/ops.h117
-rw-r--r--include/mruby/proc.h17
-rw-r--r--include/mruby/range.h42
-rw-r--r--include/mruby/string.h62
-rw-r--r--include/mruby/throw.h8
-rw-r--r--include/mruby/value.h29
-rw-r--r--include/mruby/variable.h12
-rw-r--r--include/mruby/version.h26
-rw-r--r--lib/mruby-core-ext.rb40
-rw-r--r--lib/mruby/build.rb66
-rw-r--r--lib/mruby/build/command.rb45
-rw-r--r--lib/mruby/gem.rb42
-rwxr-xr-xminirake151
-rw-r--r--mrbgems/default.gembox6
-rw-r--r--mrbgems/mruby-array-ext/mrblib/array.rb107
-rw-r--r--mrbgems/mruby-array-ext/src/array.c51
-rw-r--r--mrbgems/mruby-array-ext/test/array.rb39
-rw-r--r--mrbgems/mruby-bin-config/mrbgem.rake23
-rw-r--r--mrbgems/mruby-bin-config/mruby-config (renamed from mrbgems/mruby-bin-mruby-config/mruby-config)0
-rw-r--r--mrbgems/mruby-bin-config/mruby-config.bat (renamed from mrbgems/mruby-bin-mruby-config/mruby-config.bat)0
-rw-r--r--mrbgems/mruby-bin-debugger/bintest/print.rb2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c16
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c7
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c6
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c8
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c25
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c13
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h2
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h4
-rw-r--r--mrbgems/mruby-bin-mirb/bintest/mirb.rb4
-rw-r--r--mrbgems/mruby-bin-mirb/tools/mirb/mirb.c50
-rw-r--r--mrbgems/mruby-bin-mrbc/mrbgem.rake2
-rw-r--r--mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c9
-rw-r--r--mrbgems/mruby-bin-mruby-config/mrbgem.rake30
-rw-r--r--mrbgems/mruby-bin-mruby/bintest/mruby.rb67
-rw-r--r--mrbgems/mruby-bin-mruby/mrbgem.rake1
-rw-r--r--mrbgems/mruby-bin-mruby/tools/mruby/mruby.c44
-rw-r--r--mrbgems/mruby-bin-strip/bintest/mruby-strip.rb6
-rw-r--r--mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c18
-rw-r--r--mrbgems/mruby-class-ext/mrblib/module.rb89
-rw-r--r--mrbgems/mruby-class-ext/src/class.c3
-rw-r--r--mrbgems/mruby-class-ext/test/module.rb56
-rw-r--r--mrbgems/mruby-compiler/core/codegen.c1657
-rw-r--r--mrbgems/mruby-compiler/core/node.h24
-rw-r--r--mrbgems/mruby-compiler/core/parse.y1103
-rw-r--r--mrbgems/mruby-compiler/mrbgem.rake6
-rw-r--r--mrbgems/mruby-complex/mrbgem.rake10
-rw-r--r--mrbgems/mruby-complex/mrblib/complex.rb123
-rw-r--r--mrbgems/mruby-complex/src/complex.c150
-rw-r--r--mrbgems/mruby-complex/test/complex.rb136
-rw-r--r--mrbgems/mruby-enum-chain/mrbgem.rake6
-rw-r--r--mrbgems/mruby-enum-chain/mrblib/chain.rb60
-rw-r--r--mrbgems/mruby-enum-chain/test/enum_chain.rb76
-rw-r--r--mrbgems/mruby-enum-ext/mrblib/enum.rb53
-rw-r--r--mrbgems/mruby-enum-ext/test/enum.rb6
-rw-r--r--mrbgems/mruby-enum-lazy/mrblib/lazy.rb2
-rw-r--r--mrbgems/mruby-enumerator/mrblib/enumerator.rb46
-rw-r--r--mrbgems/mruby-enumerator/test/enumerator.rb29
-rw-r--r--mrbgems/mruby-eval/src/eval.c134
-rw-r--r--mrbgems/mruby-eval/test/eval.rb37
-rw-r--r--mrbgems/mruby-fiber/src/fiber.c25
-rw-r--r--mrbgems/mruby-fiber/test/fiber.rb2
-rw-r--r--mrbgems/mruby-hash-ext/mrblib/hash.rb89
-rw-r--r--mrbgems/mruby-hash-ext/src/hash-ext.c51
-rw-r--r--mrbgems/mruby-hash-ext/test/hash.rb6
-rw-r--r--mrbgems/mruby-inline-struct/test/inline.c22
-rw-r--r--mrbgems/mruby-inline-struct/test/inline.rb67
-rw-r--r--mrbgems/mruby-io/mrbgem.rake3
-rw-r--r--mrbgems/mruby-io/src/file.c40
-rw-r--r--mrbgems/mruby-io/src/file_test.c21
-rw-r--r--mrbgems/mruby-io/src/io.c217
-rw-r--r--mrbgems/mruby-io/test/file.rb45
-rw-r--r--mrbgems/mruby-io/test/file_test.rb21
-rw-r--r--mrbgems/mruby-io/test/gc_filedes.sh4
-rw-r--r--mrbgems/mruby-io/test/io.rb130
-rw-r--r--mrbgems/mruby-io/test/mruby_io_test.c6
-rw-r--r--mrbgems/mruby-kernel-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-kernel-ext/mrblib/kernel.rb15
-rw-r--r--mrbgems/mruby-kernel-ext/src/kernel.c61
-rw-r--r--mrbgems/mruby-kernel-ext/test/kernel.rb8
-rw-r--r--mrbgems/mruby-math/src/math.c21
-rw-r--r--mrbgems/mruby-math/test/math.rb82
-rw-r--r--mrbgems/mruby-metaprog/mrbgem.rake5
-rw-r--r--mrbgems/mruby-metaprog/src/metaprog.c724
-rw-r--r--mrbgems/mruby-metaprog/test/metaprog.rb400
-rw-r--r--mrbgems/mruby-method/mrblib/kernel.rb5
-rw-r--r--mrbgems/mruby-method/mrblib/method.rb8
-rw-r--r--mrbgems/mruby-method/src/method.c31
-rw-r--r--mrbgems/mruby-method/test/method.rb38
-rw-r--r--mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb6
-rw-r--r--mrbgems/mruby-numeric-ext/src/numeric_ext.c17
-rw-r--r--mrbgems/mruby-numeric-ext/test/numeric.rb3
-rw-r--r--mrbgems/mruby-object-ext/mrbgem.rake2
-rw-r--r--mrbgems/mruby-object-ext/mrblib/object.rb16
-rw-r--r--mrbgems/mruby-object-ext/src/object.c20
-rw-r--r--mrbgems/mruby-object-ext/test/nil.rb3
-rw-r--r--mrbgems/mruby-objectspace/src/mruby_objectspace.c2
-rw-r--r--mrbgems/mruby-objectspace/test/objectspace.rb4
-rw-r--r--mrbgems/mruby-pack/src/pack.c214
-rw-r--r--mrbgems/mruby-pack/test/pack.rb92
-rw-r--r--mrbgems/mruby-print/mrblib/print.rb5
-rw-r--r--mrbgems/mruby-print/src/print.c1
-rw-r--r--mrbgems/mruby-proc-ext/mrblib/proc.rb10
-rw-r--r--mrbgems/mruby-proc-ext/src/proc.c57
-rw-r--r--mrbgems/mruby-proc-ext/test/proc.rb44
-rw-r--r--mrbgems/mruby-random/src/random.c18
-rw-r--r--mrbgems/mruby-random/test/random.rb102
-rw-r--r--mrbgems/mruby-range-ext/mrblib/range.rb44
-rw-r--r--mrbgems/mruby-range-ext/src/range.c31
-rw-r--r--mrbgems/mruby-range-ext/test/range.rb102
-rw-r--r--mrbgems/mruby-rational/mrbgem.rake5
-rw-r--r--mrbgems/mruby-rational/mrblib/rational.rb116
-rw-r--r--mrbgems/mruby-rational/src/rational.c206
-rw-r--r--mrbgems/mruby-rational/test/rational.rb308
-rw-r--r--mrbgems/mruby-sleep/.gitignore4
-rw-r--r--mrbgems/mruby-sleep/.travis.yml29
-rw-r--r--mrbgems/mruby-sleep/.travis_build_config.rb6
-rw-r--r--mrbgems/mruby-sleep/README.md27
-rw-r--r--mrbgems/mruby-sleep/Rakefile29
-rw-r--r--mrbgems/mruby-sleep/example/sleep.rb3
-rw-r--r--mrbgems/mruby-sleep/mrbgem.rake5
-rw-r--r--mrbgems/mruby-sleep/src/mrb_sleep.c135
-rw-r--r--mrbgems/mruby-sleep/test/sleep_test.rb29
-rw-r--r--mrbgems/mruby-socket/mrbgem.rake1
-rw-r--r--mrbgems/mruby-socket/src/socket.c62
-rw-r--r--mrbgems/mruby-socket/test/addrinfo.rb10
-rw-r--r--mrbgems/mruby-socket/test/socket.rb4
-rw-r--r--mrbgems/mruby-socket/test/sockettest.c1
-rw-r--r--mrbgems/mruby-sprintf/src/sprintf.c27
-rw-r--r--mrbgems/mruby-sprintf/test/sprintf.rb9
-rw-r--r--mrbgems/mruby-string-ext/mrblib/string.rb33
-rw-r--r--mrbgems/mruby-string-ext/src/string.c663
-rw-r--r--mrbgems/mruby-string-ext/test/range.rb26
-rw-r--r--mrbgems/mruby-string-ext/test/string.rb226
-rw-r--r--mrbgems/mruby-struct/mrblib/struct.rb31
-rw-r--r--mrbgems/mruby-struct/src/struct.c105
-rw-r--r--mrbgems/mruby-struct/test/struct.rb24
-rw-r--r--mrbgems/mruby-symbol-ext/mrblib/symbol.rb6
-rw-r--r--mrbgems/mruby-symbol-ext/src/symbol.c19
-rw-r--r--mrbgems/mruby-symbol-ext/test/symbol.rb34
-rw-r--r--mrbgems/mruby-test/driver.c200
-rw-r--r--mrbgems/mruby-test/init_mrbtest.c39
-rw-r--r--mrbgems/mruby-test/mrbgem.rake44
-rw-r--r--mrbgems/mruby-time/include/mruby/time.h25
-rw-r--r--mrbgems/mruby-time/src/time.c94
-rw-r--r--mrbgems/mruby-time/test/time.rb126
-rw-r--r--mrbgems/mruby-toplevel-ext/test/toplevel.rb4
-rw-r--r--mrblib/array.rb105
-rw-r--r--mrblib/enum.rb18
-rw-r--r--mrblib/float.rb9
-rw-r--r--mrblib/hash.rb60
-rw-r--r--mrblib/kernel.rb2
-rw-r--r--mrblib/mrblib.rake5
-rw-r--r--mrblib/numeric.rb12
-rw-r--r--mrblib/range.rb2
-rw-r--r--mrblib/string.rb133
-rw-r--r--mrblib/symbol.rb7
-rw-r--r--oss-fuzz/config/mruby.dict105
-rw-r--r--oss-fuzz/config/mruby_fuzzer.options5
-rw-r--r--oss-fuzz/config/mruby_proto_fuzzer.options4
-rw-r--r--oss-fuzz/mruby_fuzzer.c18
-rw-r--r--oss-fuzz/mruby_proto_fuzzer.cpp44
-rw-r--r--oss-fuzz/proto_to_ruby.cpp455
-rw-r--r--oss-fuzz/proto_to_ruby.h55
-rw-r--r--oss-fuzz/ruby.proto201
-rw-r--r--src/array.c128
-rw-r--r--src/backtrace.c31
-rw-r--r--src/class.c695
-rw-r--r--src/codedump.c800
-rw-r--r--src/debug.c87
-rw-r--r--src/dump.c179
-rw-r--r--src/enum.c16
-rw-r--r--src/error.c14
-rw-r--r--src/etc.c44
-rw-r--r--src/ext/.gitkeep0
-rw-r--r--src/fmt_fp.c16
-rw-r--r--src/gc.c114
-rw-r--r--src/hash.c982
-rw-r--r--src/kernel.c511
-rw-r--r--src/load.c169
-rw-r--r--src/mruby_core.rake5
-rw-r--r--src/numeric.c306
-rw-r--r--src/object.c127
-rw-r--r--src/proc.c97
-rw-r--r--src/range.c364
-rw-r--r--src/state.c39
-rw-r--r--src/string.c699
-rw-r--r--src/symbol.c270
-rw-r--r--src/variable.c483
-rw-r--r--src/vm.c1699
-rw-r--r--tasks/libmruby.rake7
-rw-r--r--tasks/mrbgems.rake5
-rw-r--r--tasks/toolchains/android.rake1
-rw-r--r--tasks/toolchains/clang.rake4
-rw-r--r--tasks/toolchains/gcc.rake10
-rw-r--r--tasks/toolchains/openwrt.rake2
-rw-r--r--tasks/toolchains/visualcpp.rake4
-rw-r--r--test/assert.rb373
-rw-r--r--test/bintest.rb11
-rw-r--r--test/report.rb4
-rw-r--r--test/t/array.rb66
-rw-r--r--test/t/bs_block.rb63
-rw-r--r--test/t/class.rb60
-rw-r--r--test/t/codegen.rb6
-rw-r--r--test/t/enumerable.rb6
-rw-r--r--test/t/exception.rb4
-rw-r--r--test/t/float.rb57
-rw-r--r--test/t/hash.rb11
-rw-r--r--test/t/integer.rb70
-rw-r--r--test/t/kernel.rb145
-rw-r--r--test/t/literals.rb2
-rw-r--r--test/t/module.rb425
-rw-r--r--test/t/numeric.rb93
-rw-r--r--test/t/proc.rb2
-rw-r--r--test/t/range.rb21
-rw-r--r--test/t/string.rb132
-rw-r--r--test/t/symbol.rb8
-rw-r--r--test/t/syntax.rb199
-rw-r--r--travis_config.rb4
261 files changed, 14437 insertions, 8226 deletions
diff --git a/.gitignore b/.gitignore
index bccfaf218..400c77386 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,12 @@
-# /
*.bak
+*.bc
*.d
+*.i
*.o
-/benchmark/**/*.dat
-/benchmark/*.pdf
-/benchmark/*.png
*.orig
*.pdb
*.rej
+*.s
*.sav
*.swp
*.tmp
@@ -15,13 +14,21 @@
.DS_Store
.ccmalloc
.svn
-/.git
+.vscode
+.yardoc
cscope.files
cscope.out
tags
-/src/y.tab.c
+
+/.git
/bin
/build
/mruby-source-*.gem
-doc/api
-.yardoc
+
+/benchmark/**/*.dat
+/benchmark/*.pdf
+/benchmark/*.png
+
+/doc/api
+
+/src/y.tab.c
diff --git a/.travis.yml b/.travis.yml
index 56c54914a..9ef7009a6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,7 @@ sudo: false
matrix:
include:
- os: linux
- sudo: 9000
+ sudo: false
- os: osx
osx_image: xcode7.1
@@ -15,6 +15,5 @@ addons:
- gperf
env:
- MRUBY_CONFIG=travis_config.rb
-env: MRUBY_CONFIG=travis_config.rb
-script: "./minirake all test"
+ - MRUBY_CONFIG=travis_config.rb
+script: "./minirake -j4 all test"
diff --git a/.yardopts b/.yardopts
index 27f6d59a1..4499bf513 100644
--- a/.yardopts
+++ b/.yardopts
@@ -12,6 +12,6 @@ mrbgems/*/mrblib/**/*.rb
mrbgems/*/include/**/*.h
-
AUTHORS
-MITL
+LICENSE
CONTRIBUTING.md
doc/guides/*.md
diff --git a/AUTHORS b/AUTHORS
index 4265f7f58..2353ff683 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,5 +1,8 @@
-Original Authors "mruby developers" are:
- Yukihiro Matsumoto
+This is the (likely incomplete) list of "mruby developers".
+If you submit a patch to mruby, please add your name to the end
+of this list.
+
+ Yukihiro Matsumoto (Matz)
SCSK KYUSHU CORPORATION
Kyushu Institute of Technology
Network Applied Communication Laboratory, Inc.
@@ -38,3 +41,4 @@ Original Authors "mruby developers" are:
Christopher Aue
Masahiro Wakame
YAMAMOTO Masaya
+ KOBAYASHI Shuji
diff --git a/LEGAL b/LEGAL
index 84929998c..53de0cd9f 100644
--- a/LEGAL
+++ b/LEGAL
@@ -2,5 +2,5 @@ LEGAL NOTICE INFORMATION
------------------------
All the files in this distribution are covered under the MIT license
-(see the file MITL) except some files mentioned below:
+(see the file LICENSE) except some files mentioned below:
diff --git a/MITL b/LICENSE
index 8d8c78fab..ab5432331 100644
--- a/MITL
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2018 mruby developers
+Copyright (c) 2019 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 529091bdc..863560ebc 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
## What is mruby
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.
+of) the [ISO standard][ISO-standard]. Its syntax is Ruby 2.x compatible.
mruby can be linked and embedded within your application. We provide the
interpreter program "mruby" and the interactive mruby shell "mirb" as examples.
@@ -17,7 +17,7 @@ of the Ministry of Economy, Trade and Industry of Japan.
## How to get mruby
-The stable version 1.4.1 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.4.1.zip](https://github.com/mruby/mruby/archive/1.4.1.zip)
+The stable version 2.0.1 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/2.0.1.zip](https://github.com/mruby/mruby/archive/2.0.1.zip)
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)
@@ -38,7 +38,7 @@ We don't have a mailing list, but you can use [GitHub issues](https://github.com
## How to compile and install (mruby and gems)
-See the [doc/guides/compile.md](doc/guides/compile.md) file.
+See the [compile.md](https://github.com/mruby/mruby/blob/master/doc/guides/compile.md) file.
## Running Tests
@@ -54,12 +54,12 @@ Or
mruby contains a package manager called *mrbgems*. To create extensions
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/*.
+use mrbgems consult the file [mrbgems.md](https://github.com/mruby/mruby/blob/master/doc/guides/mrbgems.md).
+For example code of how to use mrbgems look into the folder *examples/mrbgems/*.
## License
-mruby is released under the [MIT License](MITL).
+mruby is released under the [MIT License](https://github.com/mruby/mruby/blob/master/LICENSE).
## Note for License
@@ -88,5 +88,5 @@ 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
+[contribution-guidelines]: https://github.com/mruby/mruby/blob/master/CONTRIBUTING.md
[travis-ci]: https://travis-ci.org/mruby/mruby
diff --git a/Rakefile b/Rakefile
index 2f6fa056f..533153290 100644
--- a/Rakefile
+++ b/Rakefile
@@ -32,20 +32,25 @@ load "#{MRUBY_ROOT}/tasks/benchmark.rake"
load "#{MRUBY_ROOT}/tasks/gitlab.rake"
+def install_D(src, dst)
+ opts = { :verbose => $verbose }
+ FileUtils.rm_f dst, opts
+ FileUtils.mkdir_p File.dirname(dst), opts
+ FileUtils.cp src, dst, opts
+end
+
##############################
# generic build targets, rules
task :default => :all
bin_path = ENV['INSTALL_DIR'] || "#{MRUBY_ROOT}/bin"
-FileUtils.mkdir_p bin_path, { :verbose => $verbose }
depfiles = MRuby.targets['host'].bins.map do |bin|
install_path = MRuby.targets['host'].exefile("#{bin_path}/#{bin}")
source_path = MRuby.targets['host'].exefile("#{MRuby.targets['host'].build_dir}/bin/#{bin}")
file install_path => source_path do |t|
- FileUtils.rm_f t.name, { :verbose => $verbose }
- FileUtils.cp t.prerequisites.first, t.name, { :verbose => $verbose }
+ install_D t.prerequisites.first, t.name
end
install_path
@@ -65,7 +70,7 @@ MRuby.each_target do |target|
exec = exefile("#{build_dir}/bin/#{bin}")
objs = Dir.glob("#{current_dir}/tools/#{bin}/*.{c,cpp,cxx,cc}").map { |f| objfile(f.pathmap("#{current_build_dir}/tools/#{bin}/%n")) }
- file exec => objs + [libfile("#{build_dir}/lib/libmruby")] do |t|
+ file exec => objs + target.libraries 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 }
@@ -78,8 +83,7 @@ MRuby.each_target do |target|
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 }
+ install_D t.prerequisites.first, t.name
end
depfiles += [ install_path ]
elsif target == MRuby.targets['host-debug']
@@ -87,8 +91,7 @@ MRuby.each_target do |target|
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 }
+ install_D t.prerequisites.first, t.name
end
depfiles += [ install_path ]
end
@@ -100,7 +103,7 @@ MRuby.each_target do |target|
end
depfiles += MRuby.targets.map { |n, t|
- [t.libfile("#{t.build_dir}/lib/libmruby")]
+ t.libraries
}.flatten
depfiles += MRuby.targets.reject { |n, t| n == 'host' }.map { |n, t|
@@ -118,9 +121,22 @@ task :all => depfiles do
end
desc "run all mruby tests"
-task :test => ["all"] do
- MRuby.each_target do
- run_test if test_enabled?
+task :test
+MRuby.each_target do
+ if test_enabled?
+ t = :"test_#{self.name}"
+ task t => ["all"] do
+ run_test
+ end
+ task :test => t
+ end
+
+ if bintest_enabled?
+ t = :"bintest_#{self.name}"
+ task t => ["all"] do
+ run_bintest
+ end
+ task :test => t
end
end
diff --git a/TODO b/TODO
index 3e195f99b..6227b60de 100644
--- a/TODO
+++ b/TODO
@@ -2,8 +2,6 @@ Things to do (Things that are not done yet)
* special variables ($1,$2..)
* super in aliased methods
-* multi-assignment decomposing
-* keyword arguments in def statement
Things to improve (Done but things to fix)
diff --git a/appveyor.yml b/appveyor.yml
index b4514ec27..a91834cef 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,16 +1,19 @@
version: "{build}"
-os: Visual Studio 2015
+os: Visual Studio 2017
-clone_depth: 50
+shallow_clone: true
cache:
- - win_flex_bison-2.5.10.zip
+ - win_flex_bison
environment:
matrix:
+ # Visual Studio 2017 64bit
+ - visualcpp: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat
+
# Visual Studio 2015 64bit
- visualcpp: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
machine: amd64
@@ -28,11 +31,12 @@ init:
install:
- - if not exist win_flex_bison-2.5.10.zip appveyor DownloadFile "https://github.com/lexxmark/winflexbison/releases/download/v.2.5.10/win_flex_bison-2.5.10.zip"
- - 7z x -y -owin_flex_bison win_flex_bison-2.5.10.zip > nul
-
+ - if not exist win_flex_bison (
+ appveyor DownloadFile "https://github.com/lexxmark/winflexbison/releases/download/v.2.5.10/win_flex_bison-2.5.10.zip" &
+ 7z x -y -owin_flex_bison win_flex_bison-2.5.10.zip
+ )
build_script:
- set YACC=.\win_flex_bison\win_bison.exe
- set MRUBY_CONFIG=appveyor_config.rb
- - ruby .\minirake test all
+ - rake -E $stdout.sync=true test
diff --git a/appveyor_config.rb b/appveyor_config.rb
index 2555b2f62..b50a9e4ef 100644
--- a/appveyor_config.rb
+++ b/appveyor_config.rb
@@ -17,7 +17,7 @@ MRuby::Build.new('full-debug') do |conf|
# include all core GEMs
conf.gembox 'full-core'
- conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK)
+ conf.cc.defines += %w(MRB_ENABLE_DEBUG_HOOK)
conf.enable_test
end
diff --git a/benchmark/bm_app_lc_fizzbuzz.rb b/benchmark/bm_app_lc_fizzbuzz.rb
index 26283cc3f..de8268577 100644
--- a/benchmark/bm_app_lc_fizzbuzz.rb
+++ b/benchmark/bm_app_lc_fizzbuzz.rb
@@ -48,5 +48,4 @@ answer = to_array(solution).map do |p|
to_string(p)
end
-answer_str = answer.to_a
-# puts answer_str
+# puts answer
diff --git a/bin/.gitkeep b/bin/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
--- a/bin/.gitkeep
+++ /dev/null
diff --git a/build_config.rb b/build_config.rb
index 1429837be..751317c7a 100644
--- a/build_config.rb
+++ b/build_config.rb
@@ -8,7 +8,8 @@ MRuby::Build.new do |conf|
toolchain :gcc
end
- enable_debug
+ # Turn on `enable_debug` for better debugging
+ # enable_debug
# Use mrbgems
# conf.gem 'examples/mrbgems/ruby_extension_example'
@@ -28,7 +29,7 @@ MRuby::Build.new do |conf|
# cc.command = ENV['CC'] || 'gcc'
# cc.flags = [ENV['CFLAGS'] || %w()]
# cc.include_paths = ["#{root}/include"]
- # cc.defines = %w(DISABLE_GEMS)
+ # cc.defines = %w()
# cc.option_include_path = '-I%s'
# cc.option_define = '-D%s'
# cc.compile_options = "%{flags} -MMD -o %{outfile} -c %{infile}"
diff --git a/doc/guides/debugger.md b/doc/guides/debugger.md
index 1cc7a9a39..81c0e9d63 100644
--- a/doc/guides/debugger.md
+++ b/doc/guides/debugger.md
@@ -38,7 +38,7 @@ To confirm mrdb was installed properly, run mrdb with the `--version` option:
```bash
$ mrdb --version
-mruby 1.4.1 (2018-4-27)
+mruby 2.0.1 (2019-4-4)
```
## 2.2 Basic Operation
diff --git a/doc/guides/mrbconf.md b/doc/guides/mrbconf.md
index f957f8ce2..3c20b3388 100644
--- a/doc/guides/mrbconf.md
+++ b/doc/guides/mrbconf.md
@@ -50,15 +50,21 @@ You can use mrbconfs with following ways:
* 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_WITHOUT_FLOAT`
+* When defined removes floating point numbers from mruby.
+* It makes mruby easier to handle in "Microcontroller without FPU" and "Kernel Space".
+
`MRB_INT16`
* When defined `int16_t` will be defined as `mrb_int`.
-* Conflicts with `MRB_INT64`.
+* Conflicts with `MRB_INT32` and `MRB_INT64`.
+
+`MRB_INT32`
+* When defined, or both `MRB_INT16` and `MRB_INT64` are not defined on 32-bit CPU mode, `int32_t` will be defined as `mrb_int`.
+* Conflicts with `MRB_INT16` and `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`.
+* When defined, or both `MRB_INT16` and `MRB_INT32` are not defined on 64-bit CPU mode, `int64_t` will be defined as `mrb_int`.
+* Conflicts with `MRB_INT16` and `MRB_INT32`.
## Garbage collector configuration.
@@ -115,7 +121,7 @@ largest value of required alignment.
`MRB_NAN_BOXING`
* If defined represent `mrb_value` in boxed `double`.
-* Conflicts with `MRB_USE_FLOAT`.
+* Conflicts with `MRB_USE_FLOAT` and `MRB_WITHOUT_FLOAT`.
`MRB_WORD_BOXING`
* If defined represent `mrb_value` as a word.
@@ -126,6 +132,27 @@ largest value of required alignment.
* Default value is `4`.
* Specifies size of each segment in segment list.
+## Reduce heap memory configuration.
+
+`MRB_USE_ETEXT_EDATA`
+* If you specify the address of a read-only section when creating a symbol or string, that string will be used as it is.
+* Heap memory can be saved.
+* Uses `_etext` and `__init_array_start`.
+* It must be `_etext < data_addr < &__init_array_start`.
+
+`MRB_NO_INIT_ARRAY_START`
+* Ignored if `MRB_USE_ETEXT_EDATA` is not defined.
+* Please try if `__init_array_start` is not available.
+* Uses `_etext` and `_edata`.
+* It must be `_etext < data_addr < _edata`.
+
+`MRB_USE_CUSTOM_RO_DATA_P`
+* Takes precedence over `MRB_USE_ETEXT_EDATA`.
+* Please try if both `MRB_USE_ETEXT_EDATA` and `MRB_NO_INIT_ARRAY_START` are not available.
+* The `mrb_ro_data_p()` function is implemented by the user in an arbitrary file.
+* The prototype declaration is `mrb_bool mrb_ro_data_p(const char *ptr)`.
+* Return `TRUE` if `ptr` is in read-only section, otherwise return `FALSE`.
+
## Other configuration.
`MRB_UTF8_STRING`
* Adds UTF-8 encoding support to character-oriented String instance methods.
@@ -144,3 +171,20 @@ largest value of required alignment.
`MRB_STR_BUF_MIN_SIZE`
* Default value is `128`.
* Specifies initial capacity of `RString` created by `mrb_str_buf_new` function..
+
+`MRB_METHOD_CACHE`
+* Improve performance for method dispatch.
+
+`MRB_METHOD_CACHE_SIZE`
+* Default value is `128`.
+* Ignored if `MRB_METHOD_CACHE` is not defined.
+* Need to be the power of 2.
+
+`MRB_METHOD_TABLE_INLINE`
+* Reduce the size of method table.
+* Requires LSB of function pointers to be zero.
+* For example, you might need to specify `--falign-functions=n` (where `n > 1`) for GCC.
+
+`MRB_ENABLE_ALL_SYMBOLS`
+* Make it available `Symbols.all_symbols` in `mrbgems/mruby-symbol-ext`
+* Increase heap memory usage.
diff --git a/doc/guides/mrbgems.md b/doc/guides/mrbgems.md
index 8dac0dc86..0fcc936ed 100644
--- a/doc/guides/mrbgems.md
+++ b/doc/guides/mrbgems.md
@@ -179,11 +179,11 @@ Version requirement supports following operators:
When more than one version requirements is passed, the dependency must satisfy all of it.
-You can have default gem to use as depedency when it's not defined in *build_config.rb*.
+You can have default gem to use as dependency when it's not defined in *build_config.rb*.
When the last argument of `add_dependency` call is `Hash`, it will be treated as default gem information.
Its format is same as argument of method `MRuby::Build#gem`, expect that it can't be treated as path gem location.
-When a special version of depedency is required,
+When a special version of dependency is required,
use `MRuby::Build#gem` in *build_config.rb* to override default gem.
If you have conflicting GEMs use the following method:
diff --git a/doc/limitations.md b/doc/limitations.md
index 9d8924cff..9b4ed9c6f 100644
--- a/doc/limitations.md
+++ b/doc/limitations.md
@@ -38,7 +38,7 @@ puts [1,2,3]
3
```
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
```
[1, 2, 3]
@@ -61,7 +61,7 @@ end
```ZeroDivisionError``` is raised.
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
No exception is raised.
@@ -89,7 +89,7 @@ p Liste.new "foobar"
``` [] ```
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
```ArgumentError``` is raised.
@@ -119,7 +119,7 @@ false
true
```
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
```
true
@@ -142,7 +142,7 @@ defined?(Foo)
nil
```
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
```NameError``` is raised.
@@ -159,7 +159,7 @@ alias $a $__a__
``` nil ```
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
Syntax error
@@ -181,7 +181,69 @@ end
```ArgumentError``` is raised.
The re-defined ```+``` operator does not accept any arguments.
-#### mruby [1.4.1 (2018-4-27)]
+#### mruby [2.0.1 (2019-4-4)]
``` 'ab' ```
Behavior of the operator wasn't changed.
+
+## Kernel#binding is not supported
+
+`Kernel#binding` method is not supported.
+
+#### Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)]
+
+```
+$ ruby -e 'puts Proc.new {}.binding'
+#<Binding:0x00000e9deabb9950>
+```
+
+#### mruby [2.0.1 (2019-4-4)]
+
+```
+$ ./bin/mruby -e 'puts Proc.new {}.binding'
+trace (most recent call last):
+ [0] -e:1
+-e:1: undefined method 'binding' (NoMethodError)
+```
+
+## Keyword arguments
+
+mruby keyword arguments behave slightly different from CRuby 2.5
+to make the behavior simpler and less confusing. Maybe in the
+future, the simpler behavior will be adopted to CRuby as well.
+
+#### Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)]
+
+```
+$ ruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)'
+[[{"a"=>1}], {:b=>2}]
+```
+
+#### mruby [mruby 2.0.1]
+
+```
+$ ./bin/mruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)'
+trace (most recent call last):
+ [0] -e:1
+-e:1: keyword argument hash with non symbol keys (ArgumentError)
+```
+
+## Argument Destructuring
+
+```ruby
+def m(a,(b,c),d); p [a,b,c,d]; end
+m(1,[2,3],4) # => [1,2,3,4]
+```
+Destructured arguments (`b` and `c` in above example) cannot be accessed
+from the default expression of optional arguments and keyword arguments,
+since actual assignment is done after the evaluation of those default
+expressions. Thus:
+
+```ruby
+def f(a,(b,c),d=b)
+ p [a,b,c,d]
+end
+f(1,[2,3])
+```
+
+CRuby gives `[1,2,3,nil]`. mruby raises `NoMethodError` for `b`.
diff --git a/doc/opcode.md b/doc/opcode.md
new file mode 100644
index 000000000..eab82a26f
--- /dev/null
+++ b/doc/opcode.md
@@ -0,0 +1,127 @@
+# The new bytecode
+
+We will reimplement VM to use 8bit instruction code. By
+bytecode, we mean real byte code. The whole purpose is
+reducing the memory consumption of mruby VM.
+
+# Instructions
+
+Instructions are bytes. There can be 256 instructions. Currently we
+have 94 instructions. Instructions can take 0 to 3 operands.
+
+## operands
+
+The size of operands can be either 8bits, 16bits or 24bits.
+In the table.1 below, the second field describes the size (and
+sign) of operands.
+
+* B: 8bit
+* sB: signed 8bit
+* S: 16bit
+* sS: signed 16bit
+* W: 24bit
+
+First two byte operands may be extended to 16bit. When those byte
+operands are bigger than 256, the instruction will be prefixed by
+`OP_EXT1` (means 1st operand is 16bit) or `OP_EXT2` (means 2nd operand
+is 16bit) or `OP_EXT3` (means 1st and 2nd operands are 16bit).
+
+For instructions marked by `'`, `OP_EXT1` can be prefixed. For those
+with `"`, either `OP_EXT1` or `OP_EXT2` or `OP_EXT2` can be prefixed.
+
+## table.1 Instruction Table
+
+|Instruction Name |Operand type |Semantics
+|-----------------|-------------|-----------------
+|OP_NOP | - |
+|OP_MOVE" |BB |R(a) = R(b)
+|OP_LOADL" |BB |R(a) = Pool(b)
+|OP_LOADI" |BsB |R(a) = mrb_int(b)
+|OP_LOADI_0' |B |R(a) = 0
+|OP_LOADI_1' |B |R(a) = 1
+|OP_LOADI_2' |B |R(a) = 2
+|OP_LOADI_3' |B |R(a) = 3
+|OP_LOADSYM" |BB |R(a) = Syms(b)
+|OP_LOADNIL' |B |R(a) = nil
+|OP_LOADSELF' |B |R(a) = self
+|OP_LOADT' |B |R(a) = true
+|OP_LOADF' |B |R(a) = false
+|OP_GETGV" |BB |R(a) = getglobal(Syms(b))
+|OP_SETGV" |BB |setglobal(Syms(b), R(a))
+|OP_GETSV" |BB |R(a) = Special[b]
+|OP_SETSV" |BB |Special[b] = R(a)
+|OP_GETIV" |BB |R(a) = ivget(Syms(b))
+|OP_SETIV" |BB |ivset(Syms(b),R(a))
+|OP_GETCV" |BB |R(a) = cvget(Syms(b))
+|OP_SETCV" |BB |cvset(Syms(b),R(a))
+|OP_GETCONST" |BB |R(a) = constget(Syms(b))
+|OP_SETCONST" |BB |constset(Syms(b),R(a))
+|OP_GETMCNST" |BB |R(a) = R(a)::Syms(b)
+|OP_SETMCNST" |BB |R(a+1)::Syms(b) = R(a)
+|OP_GETUPVAR' |BBB |R(a) = uvget(b,c)
+|OP_SETUPVAR' |BBB |uvset(b,c,R(a))
+|OP_JMP |S |pc+=a
+|OP_JMPIF' |SB |if R(b) pc+=a
+|OP_JMPNOT' |SB |if !R(b) pc+=a
+|OP_ONERR |sS |rescue_push(pc+a)
+|OP_EXCEPT' |B |R(a) = exc
+|OP_RESCUE" |BB |R(b) = R(a).isa?(R(b))
+|OP_POPERR |B |a.times{rescue_pop()}
+|OP_RAISE' |B |raise(R(a))
+|OP_EPUSH' |B |ensure_push(SEQ[a])
+|OP_EPOP |B |A.times{ensure_pop().call}
+|OP_SENDV" |BB |R(a) = call(R(a),Syms(b),*R(a+1))
+|OP_SENDVB" |BB |R(a) = call(R(a),Syms(b),*R(a+1),&R(a+2))
+|OP_SEND" |BBB |R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c))
+|OP_SENDB" |BBB |R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1))
+|OP_CALL' |B |R(a) = self.call(frame.argc, frame.argv)
+|OP_SUPER' |BB |R(a) = super(R(a+1),... ,R(a+b+1))
+|OP_ARGARY' |BS |R(a) = argument array (16=5:1:5:1:4)
+|OP_ENTER |W |arg setup according to flags (23=5:5:1:5:5:1:1)
+|OP_KARG" |BB |R(a) = kdict[Syms(Bx)] # todo
+|OP_KARG2" |BB |R(a) = kdict[Syms(Bx)]; kdict.rm(Syms(b)) # todo
+|OP_RETURN' |B |return R(a) (normal)
+|OP_RETURN_BLK' |B |return R(a) (in-block return)
+|OP_BREAK' |B |break R(a)
+|OP_BLKPUSH' |BS |R(a) = block (16=5:1:5:1:4)
+|OP_ADD" |BB |R(a) = R(a)+R(a+1)
+|OP_ADDI" |BBB |R(a) = R(a)+mrb_int(c)
+|OP_SUB" |BB |R(a) = R(a)-R(a+1)
+|OP_SUBI" |BB |R(a) = R(a)-C
+|OP_MUL" |BB |R(a) = R(a)*R(a+1)
+|OP_DIV" |BB |R(a) = R(a)/R(a+1)
+|OP_EQ" |BB |R(a) = R(a)==R(a+1)
+|OP_LT" |BB |R(a) = R(a)<R(a+1)
+|OP_LE" |BB |R(a) = R(a)<=R(a+1)
+|OP_GT" |BB |R(a) = R(a)>R(a+1)
+|OP_GE" |BB |R(a) = R(a)>=R(a+1)
+|OP_ARRAY' |BB |R(a) = ary_new(R(a),R(a+1)..R(a+b))
+|OP_ARRAY2" |BB |R(a) = ary_new(R(b),R(b+1)..R(b+c))
+|OP_ARYCAT' |B |ary_cat(R(a),R(a+1))
+|OP_ARYPUSH' |B |ary_push(R(a),R(a+1))
+|OP_AREF' |BB |R(a) = R(a)[b]
+|OP_ASET' |BB |R(a)[b] = R(a+1)
+|OP_APOST' |BB |*R(a),R(A+1)..R(A+C) = R(a)[B..]
+|OP_STRING" |BB |R(a) = str_dup(Lit(b))
+|OP_STRCAT' |B |str_cat(R(a),R(a+1))
+|OP_HASH' |BB |R(a) = hash_new(R(a),R(a+1)..R(a+b))
+|OP_HASHADD' |BB |R(a) = hash_push(R(a),R(a+1)..R(a+b))
+|OP_LAMBDA" |BB |R(a) = lambda(SEQ[b],OP_L_LAMBDA)
+|OP_BLOCK" |BB |R(a) = lambda(SEQ[b],OP_L_BLOCK)
+|OP_METHOD" |BB |R(a) = lambda(SEQ[b],OP_L_METHOD)
+|OP_RANGE_INC' |B |R(a) = range_new(R(a),R(a+1),FALSE)
+|OP_RANGE_EXC' |B |R(a) = range_new(R(a),R(a+1),TRUE)
+|OP_OCLASS' |B |R(a) = ::Object
+|OP_CLASS" |BB |R(a) = newclass(R(a),Syms(b),R(a+1))
+|OP_MODULE" |BB |R(a) = newmodule(R(a),Syms(b))
+|OP_EXEC" |BB |R(a) = blockexec(R(a),SEQ[b])
+|OP_DEF" |BB |R(a).newmethod(Syms(b),R(a+1))
+|OP_ALIAS' |B |alias_method(R(a),R(a+1),R(a+2))
+|OP_UNDEF" |BB |undef_method(R(a),Syms(b))
+|OP_SCLASS' |B |R(a) = R(a).singleton_class
+|OP_TCLASS' |B |R(a) = target_class
+|OP_ERR' |B |raise(RuntimeError, Lit(Bx))
+|OP_EXT1 |- |make 1st operand 16bit
+|OP_EXT2 |- |make 2nd operand 16bit
+|OP_EXT3 |- |make 1st and 2nd operands 16bit
+|OP_STOP |- |stop VM
diff --git a/examples/targets/build_config_android_arm64-v8a.rb b/examples/targets/build_config_android_arm64-v8a.rb
index 6188c13ec..70b0f4b97 100644
--- a/examples/targets/build_config_android_arm64-v8a.rb
+++ b/examples/targets/build_config_android_arm64-v8a.rb
@@ -15,8 +15,8 @@ end
# Requires Android NDK r13 or later.
MRuby::CrossBuild.new('android-arm64-v8a') do |conf|
- params = {
- :arch => 'arm64-v8a',
+ params = {
+ :arch => 'arm64-v8a',
:platform => 'android-24',
:toolchain => :clang,
}
diff --git a/examples/targets/build_config_android_armeabi.rb b/examples/targets/build_config_android_armeabi.rb
index b7eb33a92..17330242a 100644
--- a/examples/targets/build_config_android_armeabi.rb
+++ b/examples/targets/build_config_android_armeabi.rb
@@ -15,8 +15,8 @@ end
# Requires Android NDK r13 or later.
MRuby::CrossBuild.new('android-armeabi') do |conf|
- params = {
- :arch => 'armeabi',
+ params = {
+ :arch => 'armeabi',
:platform => 'android-24',
:toolchain => :clang,
}
diff --git a/include/mrbconf.h b/include/mrbconf.h
index cc28acfaa..f5e8858ce 100644
--- a/include/mrbconf.h
+++ b/include/mrbconf.h
@@ -41,10 +41,15 @@
/* you might need to specify --falign-functions=n (where n>1) */
//#define MRB_METHOD_TABLE_INLINE
-/* add -DMRB_INT16 to use 16bit integer for mrb_int; conflict with MRB_INT64 */
+/* add -DMRB_INT16 to use 16bit integer for mrb_int; conflict with MRB_INT32 and MRB_INT64 */
//#define MRB_INT16
-/* add -DMRB_INT64 to use 64bit integer for mrb_int; conflict with MRB_INT16 */
+/* add -DMRB_INT32 to use 32bit integer for mrb_int; conflict with MRB_INT16 and MRB_INT64;
+ Default for 32-bit CPU mode. */
+//#define MRB_INT32
+
+/* add -DMRB_INT64 to use 64bit integer for mrb_int; conflict with MRB_INT16 and MRB_INT32;
+ Default for 64-bit CPU mode. */
//#define MRB_INT64
/* if no specific integer type is chosen */
@@ -58,12 +63,20 @@
# endif
#endif
+#define MRB_COMPLEX_NUMBERS
+#define MRB_RATIONAL_NUMBERS
+
+/* define on big endian machines; used by MRB_NAN_BOXING, etc. */
+#ifndef MRB_ENDIAN_BIG
+# if (defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN) || \
+ (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+# define MRB_ENDIAN_BIG
+# endif
+#endif
+
/* represent mrb_value in boxed double; conflict with MRB_USE_FLOAT and MRB_WITHOUT_FLOAT */
//#define MRB_NAN_BOXING
-/* define on big endian machines; used by MRB_NAN_BOXING */
-//#define MRB_ENDIAN_BIG
-
/* represent mrb_value as a word (natural unit of data for the processor) */
//#define MRB_WORD_BOXING
@@ -83,6 +96,11 @@
effective only when MRB_USE_ETEXT_EDATA is defined */
//#define MRB_NO_INIT_ARRAY_START
+/* if do not works both MRB_USE_ETEXT_EDATA and MRB_NO_INIT_ARRAY_START,
+ you can try mrb_ro_data_p() that you have implemented yourself in any file;
+ prototype is `mrb_bool mrb_ro_data_p(const char *ptr)` */
+//#define MRB_USE_CUSTOM_RO_DATA_P
+
/* turn off generational GC by default */
//#define MRB_GC_TURN_OFF_GENERATIONAL
@@ -115,6 +133,7 @@
/* -DMRB_ENABLE_XXXX to enable following features */
//#define MRB_ENABLE_DEBUG_HOOK /* hooks for debugger */
+//#define MRB_ENABLE_ALL_SYMBOLS /* Symbols.all_symbols */
/* end of configuration */
@@ -140,4 +159,70 @@
# define TRUE 1
#endif
+/*
+** mruby tuning profiles
+**/
+
+/* A profile for micro controllers */
+#if defined(MRB_CONSTRAINED_BASELINE_PROFILE)
+# ifndef KHASH_DEFAULT_SIZE
+# define KHASH_DEFAULT_SIZE 16
+# endif
+
+# ifndef MRB_STR_BUF_MIN_SIZE
+# define MRB_STR_BUF_MIN_SIZE 32
+# endif
+
+# ifndef MRB_HEAP_PAGE_SIZE
+# define MRB_HEAP_PAGE_SIZE 256
+# endif
+
+/* A profile for default mruby */
+#elif defined(MRB_BASELINE_PROFILE)
+
+/* A profile for desktop computers or workstations; rich memory! */
+#elif defined(MRB_MAIN_PROFILE)
+# ifndef MRB_METHOD_CACHE
+# define MRB_METHOD_CACHE
+# endif
+
+# ifndef MRB_METHOD_CACHE_SIZE
+# define MRB_METHOD_CACHE_SIZE (1<<10)
+# endif
+
+# ifndef MRB_METHOD_TABLE_INLINE
+# define MRB_METHOD_TABLE_INLINE
+# endif
+
+# ifndef MRB_IV_SEGMENT_SIZE
+# define MRB_IV_SEGMENT_SIZE 32
+# endif
+
+# ifndef MRB_HEAP_PAGE_SIZE
+# define MRB_HEAP_PAGE_SIZE 4096
+# endif
+
+/* A profile for server; mruby vm is long life */
+#elif defined(MRB_HIGH_PROFILE)
+# ifndef MRB_METHOD_CACHE
+# define MRB_METHOD_CACHE
+# endif
+
+# ifndef MRB_METHOD_CACHE_SIZE
+# define MRB_METHOD_CACHE_SIZE (1<<12)
+# endif
+
+# ifndef MRB_METHOD_TABLE_INLINE
+# define MRB_METHOD_TABLE_INLINE
+# endif
+
+# ifndef MRB_IV_SEGMENT_SIZE
+# define MRB_IV_SEGMENT_SIZE 64
+# endif
+
+# ifndef MRB_HEAP_PAGE_SIZE
+# define MRB_HEAP_PAGE_SIZE 4096
+# endif
+#endif
+
#endif /* MRUBYCONF_H */
diff --git a/include/mruby.h b/include/mruby.h
index 3b953a327..7deb3cbe2 100644
--- a/include/mruby.h
+++ b/include/mruby.h
@@ -1,7 +1,7 @@
/*
** mruby - An embeddable Ruby implementation
**
-** Copyright (c) mruby developers 2010-2018
+** Copyright (c) mruby developers 2010-2019
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
@@ -34,6 +34,7 @@
#define __STDC_FORMAT_MACROS
#endif
+#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
@@ -57,7 +58,7 @@
#define mrb_assert_int_fit(t1,n,t2,max) ((void)0)
#endif
-#if __STDC_VERSION__ >= 201112L
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L
#define mrb_static_assert(exp, str) _Static_assert(exp, str)
#else
#define mrb_static_assert(exp, str) mrb_assert(exp)
@@ -83,7 +84,7 @@
#endif
#endif
-#include "mruby/common.h"
+#include <mruby/common.h>
#include <mruby/value.h>
#include <mruby/gc.h>
#include <mruby/version.h>
@@ -93,7 +94,7 @@
*/
MRB_BEGIN_DECL
-typedef uint32_t mrb_code;
+typedef uint8_t mrb_code;
/**
* Required arguments signature type.
@@ -123,9 +124,8 @@ typedef struct {
mrb_sym mid;
struct RProc *proc;
mrb_value *stackent;
- int nregs;
- int ridx;
- int epos;
+ uint16_t ridx;
+ uint16_t epos;
struct REnv *env;
mrb_code *pc; /* return address */
mrb_code *err; /* error position */
@@ -152,10 +152,10 @@ struct mrb_context {
mrb_callinfo *ci;
mrb_callinfo *cibase, *ciend;
- mrb_code **rescue; /* exception handler stack */
- int rsize;
+ uint16_t *rescue; /* exception handler stack */
+ uint16_t rsize;
struct RProc **ensure; /* ensure handler stack */
- int esize, eidx;
+ uint16_t esize, eidx;
enum mrb_fiber_state status;
mrb_bool vmexec;
@@ -196,13 +196,9 @@ struct mrb_jmpbuf;
typedef void (*mrb_atexit_func)(struct mrb_state*);
-#define MRB_STATE_NO_REGEXP 1
-#define MRB_STATE_REGEXP 2
-
typedef struct mrb_state {
struct mrb_jmpbuf *jmp;
- uint32_t flags;
mrb_allocf allocf; /* memory allocation function */
void *allocf_ud; /* auxiliary data of allocf */
@@ -232,7 +228,6 @@ typedef struct mrb_state {
struct RClass *symbol_class;
struct RClass *kernel_module;
- struct alloca_header *mems;
mrb_gc gc;
#ifdef MRB_METHOD_CACHE
@@ -240,9 +235,12 @@ typedef struct mrb_state {
#endif
mrb_sym symidx;
- struct kh_n2s *name2sym; /* symbol hash */
struct symbol_name *symtbl; /* symbol table */
+ mrb_sym symhash[256];
size_t symcapa;
+#ifndef MRB_ENABLE_SYMBOLL_ALL
+ char symbuf[8]; /* buffer for small symbol names */
+#endif
#ifdef MRB_ENABLE_DEBUG_HOOK
void (*code_fetch_hook)(struct mrb_state* mrb, struct mrb_irep *irep, mrb_code *pc, mrb_value *regs);
@@ -268,7 +266,8 @@ typedef struct mrb_state {
#else
mrb_atexit_func *atexit_stack;
#endif
- mrb_int atexit_stack_len;
+ uint16_t atexit_stack_len;
+ uint16_t ecall_nest; /* prevent infinite recursive ecall() */
} mrb_state;
/**
@@ -386,7 +385,7 @@ MRB_API void mrb_define_class_method(mrb_state *, struct RClass *, const char *,
MRB_API void mrb_define_singleton_method(mrb_state*, struct RObject*, const char*, mrb_func_t, mrb_aspec);
/**
- * Defines a module fuction.
+ * Defines a module function.
*
* Example:
*
@@ -486,9 +485,10 @@ MRB_API void mrb_define_const(mrb_state*, struct RClass*, const char *name, 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.
+ * @param [const char*] const char* The name of the method to be undefined.
*/
MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*);
+MRB_API void mrb_undef_method_id(mrb_state*, struct RClass*, mrb_sym);
/**
* Undefine a class method.
@@ -525,12 +525,12 @@ MRB_API void mrb_undef_method(mrb_state*, struct RClass*, const char*);
* }
* @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.
+ * @param [const char*] const char* 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.
+ * Initialize a new object instance of c class.
*
* Example:
*
@@ -565,8 +565,6 @@ MRB_INLINE mrb_value mrb_class_new_instance(mrb_state *mrb, mrb_int argc, const
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.
*
@@ -704,6 +702,9 @@ MRB_API struct RClass * mrb_module_get(mrb_state *mrb, const char *name);
* @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);
+/* a function to raise NotImplementedError with current method name */
+MRB_API void mrb_notimplement(mrb_state*);
+/* a function to be replacement of unimplemented method */
MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
/**
@@ -716,7 +717,6 @@ MRB_API mrb_value mrb_notimplement_m(mrb_state*, mrb_value);
* @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
@@ -783,7 +783,7 @@ MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *o
#define MRB_ARGS_REQ(n) ((mrb_aspec)((n)&0x1f) << 18)
/**
- * Funtion takes n optional arguments
+ * Function takes n optional arguments
*
* @param n
* The number of optional arguments.
@@ -791,7 +791,7 @@ MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *o
#define MRB_ARGS_OPT(n) ((mrb_aspec)((n)&0x1f) << 13)
/**
- * Funtion takes n1 mandatory arguments and n2 optional arguments
+ * Function takes n1 mandatory arguments and n2 optional arguments
*
* @param n1
* The number of required arguments.
@@ -836,15 +836,17 @@ MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *o
* | `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` |
+ * | `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} | |
+ * | `f` | {Fixnum}/{Float} | {mrb_float} | |
+ * | `i` | {Fixnum}/{Float} | {mrb_int} | |
* | `b` | boolean | {mrb_bool} | |
- * | `n` | {Symbol} | {mrb_sym} | |
+ * | `n` | {String}/{Symbol} | {mrb_sym} | |
+ * | `d` | data | void *, {mrb_data_type} const | 2nd argument will be used to check data type so it won't be modified; when `!` follows, the value may be `nil` |
+ * | `I` | inline struct | void * | |
* | `&` | block | {mrb_value} | &! raises exception if no block given. |
- * | `*` | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array; *! avoid copy of the stack. |
+ * | `*` | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array; `*!` avoid copy of the stack. |
* | &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. |
*
@@ -855,10 +857,6 @@ 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.
@@ -867,7 +865,7 @@ typedef const char *mrb_args_format;
*/
MRB_API mrb_int mrb_get_args(mrb_state *mrb, mrb_args_format format, ...);
-static inline mrb_sym
+MRB_INLINE mrb_sym
mrb_get_mid(mrb_state *mrb) /* get method symbol */
{
return mrb->c->ci->mid;
@@ -994,13 +992,13 @@ 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))
#ifdef _WIN32
-char* mrb_utf8_from_locale(const char *p, int len);
-char* mrb_locale_from_utf8(const char *p, int len);
+MRB_API char* mrb_utf8_from_locale(const char *p, int len);
+MRB_API char* mrb_locale_from_utf8(const char *p, int len);
#define mrb_locale_free(p) free(p)
#define mrb_utf8_free(p) free(p)
#else
-#define mrb_utf8_from_locale(p, l) ((char*)p)
-#define mrb_locale_from_utf8(p, l) ((char*)p)
+#define mrb_utf8_from_locale(p, l) ((char*)(p))
+#define mrb_locale_from_utf8(p, l) ((char*)(p))
#define mrb_locale_free(p)
#define mrb_utf8_free(p)
#endif
@@ -1080,16 +1078,13 @@ 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);
-static inline int mrb_gc_arena_save(mrb_state*);
-static inline void mrb_gc_arena_restore(mrb_state*,int);
-
-static inline int
+MRB_INLINE int
mrb_gc_arena_save(mrb_state *mrb)
{
return mrb->gc.arena_idx;
}
-static inline void
+MRB_INLINE void
mrb_gc_arena_restore(mrb_state *mrb, int idx)
{
mrb->gc.arena_idx = idx;
@@ -1140,10 +1135,13 @@ MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc);
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 mrb_noreturn void mrb_frozen_error(mrb_state *mrb, void *frozen_obj);
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);
+/* function for `raisef` formatting */
+MRB_API mrb_value mrb_vformat(mrb_state *mrb, const char *format, va_list ap);
/* macros to get typical exception objects
note:
@@ -1188,8 +1186,14 @@ MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj);
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))
+MRB_API mrb_value mrb_to_str(mrb_state *mrb, mrb_value val);
MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t);
+MRB_INLINE void mrb_check_frozen(mrb_state *mrb, void *o)
+{
+ if (MRB_FROZEN_P((struct RBasic*)o)) mrb_frozen_error(mrb, o);
+}
+
typedef enum call_type {
CALL_PUBLIC,
CALL_FCALL,
@@ -1197,7 +1201,7 @@ typedef enum call_type {
CALL_TYPE_MAX
} call_type;
-MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2);
+MRB_API void mrb_define_alias(mrb_state *mrb, struct RClass *c, const char *a, const char *b);
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);
@@ -1235,6 +1239,7 @@ MRB_API mrb_value mrb_fiber_alive_p(mrb_state *mrb, mrb_value fib);
* @mrbgem mruby-fiber
*/
#define E_FIBER_ERROR (mrb_exc_get(mrb, "FiberError"))
+MRB_API void mrb_stack_extend(mrb_state*, mrb_int);
/* memory pool implementation */
typedef struct mrb_pool mrb_pool;
@@ -1243,6 +1248,7 @@ 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);
+/* temporary memory allocation, only effective while GC arena is kept */
MRB_API void* mrb_alloca(mrb_state *mrb, size_t);
MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func);
diff --git a/include/mruby/array.h b/include/mruby/array.h
index 6fffe4512..0c0a002f5 100644
--- a/include/mruby/array.h
+++ b/include/mruby/array.h
@@ -33,7 +33,6 @@ struct RArray {
} aux;
mrb_value *ptr;
} heap;
- mrb_value embed[MRB_ARY_EMBED_LEN_MAX];
} as;
};
@@ -46,7 +45,7 @@ struct RArray {
#define ARY_UNSET_EMBED_FLAG(a) ((a)->flags &= ~(MRB_ARY_EMBED_MASK))
#define ARY_EMBED_LEN(a) ((mrb_int)(((a)->flags & MRB_ARY_EMBED_MASK) - 1))
#define ARY_SET_EMBED_LEN(a,len) ((a)->flags = ((a)->flags&~MRB_ARY_EMBED_MASK) | ((uint32_t)(len) + 1))
-#define ARY_EMBED_PTR(a) (&((a)->as.embed[0]))
+#define ARY_EMBED_PTR(a) ((mrb_value*)&(a)->as)
#define ARY_LEN(a) (ARY_EMBED_P(a)?ARY_EMBED_LEN(a):(a)->as.heap.len)
#define ARY_PTR(a) (ARY_EMBED_P(a)?ARY_EMBED_PTR(a):(a)->as.heap.ptr)
@@ -199,6 +198,7 @@ MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val
* @param other The array to replace it with.
*/
MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other);
+MRB_API mrb_value mrb_ensure_array_type(mrb_state *mrb, mrb_value self);
MRB_API mrb_value mrb_check_array_type(mrb_state *mrb, mrb_value self);
/*
@@ -227,6 +227,22 @@ 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);
/*
+ * Replace subsequence of an array.
+ *
+ * Equivalent to:
+ *
+ * ary.shift
+ *
+ * @param mrb The mruby state reference.
+ * @param self The array from which the value will be shifted.
+ * @param head Beginning position of a replacement subsequence.
+ * @param len Length of a replacement subsequence.
+ * @param rpl The array of replacement elements.
+ * @return The receiver array.
+ */
+MRB_API mrb_value mrb_ary_splice(mrb_state *mrb, mrb_value self, mrb_int head, mrb_int len, mrb_value rpl);
+
+/*
* Shifts the first element from the array.
*
* Equivalent to:
diff --git a/include/mruby/boxing_nan.h b/include/mruby/boxing_nan.h
index 3d9bbdc69..ff6f6082d 100644
--- a/include/mruby/boxing_nan.h
+++ b/include/mruby/boxing_nan.h
@@ -81,7 +81,7 @@ typedef struct mrb_value {
} while (0)
#define SET_FLOAT_VALUE(mrb,r,v) do { \
- if (v != v) { \
+ if ((v) != (v)) { \
(r).value.ttt = 0x7ff80000; \
(r).value.i = 0; \
} \
diff --git a/include/mruby/boxing_word.h b/include/mruby/boxing_word.h
index b17bfc973..f16968a1f 100644
--- a/include/mruby/boxing_word.h
+++ b/include/mruby/boxing_word.h
@@ -45,6 +45,14 @@ enum mrb_special_consts {
#define MRB_SYMBOL_FLAG 0x0e
#define MRB_SPECIAL_SHIFT 8
+#if defined(MRB_64BIT)
+#define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT)
+#define MRB_SYMBOL_MAX UINT32_MAX
+#else
+#define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT - MRB_SPECIAL_SHIFT)
+#define MRB_SYMBOL_MAX (UINT32_MAX >> MRB_SPECIAL_SHIFT)
+#endif
+
typedef union mrb_value {
union {
void *p;
@@ -54,7 +62,7 @@ typedef union mrb_value {
};
struct {
unsigned int sym_flag : MRB_SPECIAL_SHIFT;
- mrb_sym sym : (sizeof(mrb_sym) * CHAR_BIT);
+ mrb_sym sym : MRB_SYMBOL_BITSIZE;
};
struct RBasic *bp;
#ifndef MRB_WITHOUT_FLOAT
@@ -83,7 +91,7 @@ MRB_API mrb_value mrb_word_boxing_float_pool(struct mrb_state*, mrb_float);
#define mrb_fixnum(o) ((mrb_int)(o).value.i)
#define mrb_symbol(o) (o).value.sym
-static inline enum mrb_vtype
+MRB_INLINE enum mrb_vtype
mrb_type(mrb_value o)
{
switch (o.w) {
@@ -108,29 +116,31 @@ mrb_type(mrb_value o)
#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).w = 0;(o).value.i_flag = MRB_FIXNUM_FLAG; (o).attr = (v); break;\
- case MRB_TT_SYMBOL: (o).w = 0;(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 mrb_false_p(o) ((o).w == MRB_Qfalse)
+#define mrb_true_p(o) ((o).w == MRB_Qtrue)
#ifndef MRB_WITHOUT_FLOAT
-#define SET_FLOAT_VALUE(mrb,r,v) r = mrb_word_boxing_float_value(mrb, v)
+#define SET_FLOAT_VALUE(mrb,r,v) ((r) = mrb_word_boxing_float_value(mrb, v))
#endif
-#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)
+#define SET_CPTR_VALUE(mrb,r,v) ((r) = mrb_word_boxing_cptr_value(mrb, v))
+#define SET_UNDEF_VALUE(r) ((r).w = MRB_Qundef)
+#define SET_NIL_VALUE(r) ((r).w = MRB_Qnil)
+#define SET_FALSE_VALUE(r) ((r).w = MRB_Qfalse)
+#define SET_TRUE_VALUE(r) ((r).w = MRB_Qtrue)
+#define SET_BOOL_VALUE(r,b) ((b) ? SET_TRUE_VALUE(r) : SET_FALSE_VALUE(r))
+#define SET_INT_VALUE(r,n) do { \
+ (r).w = 0; \
+ (r).value.i_flag = MRB_FIXNUM_FLAG; \
+ (r).value.i = (n); \
+} while (0)
+#define SET_SYM_VALUE(r,v) do { \
+ (r).w = 0; \
+ (r).value.sym_flag = MRB_SYMBOL_FLAG; \
+ (r).value.sym = (v); \
+} while (0)
+#define SET_OBJ_VALUE(r,v) do { \
+ (r).w = 0; \
+ (r).value.p = (v); \
+} while (0)
#endif /* MRUBY_BOXING_WORD_H */
diff --git a/include/mruby/class.h b/include/mruby/class.h
index ea35d8e17..c79a487b5 100644
--- a/include/mruby/class.h
+++ b/include/mruby/class.h
@@ -22,11 +22,8 @@ struct RClass {
};
#define mrb_class_ptr(v) ((struct RClass*)(mrb_ptr(v)))
-#define RCLASS_SUPER(v) (((struct RClass*)(mrb_ptr(v)))->super)
-#define RCLASS_IV_TBL(v) (((struct RClass*)(mrb_ptr(v)))->iv)
-#define RCLASS_M_TBL(v) (((struct RClass*)(mrb_ptr(v)))->mt)
-static inline struct RClass*
+MRB_INLINE struct RClass*
mrb_class(mrb_state *mrb, mrb_value v)
{
switch (mrb_type(v)) {
@@ -53,22 +50,28 @@ mrb_class(mrb_state *mrb, mrb_value v)
}
}
-/* TODO: figure out where to put user flags */
-/* flags bits >= 18 is reserved */
-#define MRB_FLAG_IS_PREPENDED (1 << 19)
-#define MRB_FLAG_IS_ORIGIN (1 << 20)
+/* flags:
+ 20: frozen
+ 19: is_prepended
+ 18: is_origin
+ 17: is_inherited (used by method cache)
+ 16: unused
+ 0-15: instance type
+*/
+#define MRB_FL_CLASS_IS_PREPENDED (1 << 19)
+#define MRB_FL_CLASS_IS_ORIGIN (1 << 18)
#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;\
+ if ((c)->flags & MRB_FL_CLASS_IS_PREPENDED) {\
+ (c) = (c)->super;\
+ while (!((c)->flags & MRB_FL_CLASS_IS_ORIGIN)) {\
+ (c) = (c)->super;\
}\
}\
} while (0)
-#define MRB_FLAG_IS_INHERITED (1 << 21)
+#define MRB_FL_CLASS_IS_INHERITED (1 << 17)
#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)
+#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)
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);
@@ -76,14 +79,16 @@ MRB_API struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb
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, mrb_method_t);
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);
+MRB_API void mrb_alias_method(mrb_state*, struct RClass *c, mrb_sym a, mrb_sym b);
MRB_API mrb_method_t mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym);
MRB_API mrb_method_t mrb_method_search(mrb_state*, struct RClass*, mrb_sym);
MRB_API struct RClass* mrb_class_real(struct RClass* cl);
+mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv);
void mrb_class_name_class(mrb_state*, struct RClass*, struct RClass*, mrb_sym);
+mrb_bool mrb_const_name_p(mrb_state*, const char*, mrb_int);
mrb_value mrb_class_find_path(mrb_state*, struct RClass*);
void mrb_gc_mark_mt(mrb_state*, struct RClass*);
size_t mrb_gc_mark_mt_size(mrb_state*, struct RClass*);
diff --git a/include/mruby/common.h b/include/mruby/common.h
index d6ec78b0d..dc9e3acc5 100644
--- a/include/mruby/common.h
+++ b/include/mruby/common.h
@@ -7,6 +7,11 @@
#ifndef MRUBY_COMMON_H
#define MRUBY_COMMON_H
+#ifdef __APPLE__
+ #ifndef __TARGETCONDITIONALS__
+ #include "TargetConditionals.h"
+ #endif
+#endif
#ifdef __cplusplus
#ifdef MRB_ENABLE_CXX_ABI
@@ -29,7 +34,7 @@
MRB_BEGIN_DECL
/** Declare a function that never returns. */
-#if __STDC_VERSION__ >= 201112L
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112L
# define mrb_noreturn _Noreturn
#elif defined __GNUC__ && !defined __STRICT_ANSI__
# define mrb_noreturn __attribute__((noreturn))
@@ -49,12 +54,12 @@ MRB_BEGIN_DECL
#endif
/** Declare a function as always inlined. */
-#if defined(_MSC_VER)
-# define MRB_INLINE static __inline
-#else
-# define MRB_INLINE static inline
+#if defined _MSC_VER && _MSC_VER < 1900
+# ifndef __cplusplus
+# define inline __inline
+# endif
#endif
-
+#define MRB_INLINE static inline
/** Declare a public MRuby API function. */
#if defined(MRB_BUILD_AS_DLL)
diff --git a/include/mruby/compile.h b/include/mruby/compile.h
index d7d029616..3b25ff526 100644
--- a/include/mruby/compile.h
+++ b/include/mruby/compile.h
@@ -24,7 +24,7 @@ typedef struct mrbc_context {
mrb_sym *syms;
int slen;
char *filename;
- short lineno;
+ uint16_t lineno;
int (*partial_hook)(struct mrb_parser_state*);
void *partial_data;
struct RClass *target_class;
@@ -33,6 +33,7 @@ typedef struct mrbc_context {
mrb_bool no_exec:1;
mrb_bool keep_lv:1;
mrb_bool no_optimize:1;
+ mrb_bool on_eval:1;
size_t parser_nerr;
} mrbc_context;
@@ -66,7 +67,7 @@ enum mrb_lex_state_enum {
/* saved error message */
struct mrb_parser_message {
- int lineno;
+ uint16_t lineno;
int column;
char* message;
};
@@ -104,7 +105,7 @@ struct mrb_parser_heredoc_info {
mrb_ast_node *doc;
};
-#define MRB_PARSER_TOKBUF_MAX 65536
+#define MRB_PARSER_TOKBUF_MAX (UINT16_MAX-1)
#define MRB_PARSER_TOKBUF_SIZE 256
/* parser structure */
@@ -117,8 +118,8 @@ struct mrb_parser_state {
FILE *f;
#endif
mrbc_context *cxt;
- char const *filename;
- int lineno;
+ mrb_sym filename_sym;
+ uint16_t lineno;
int column;
enum mrb_lex_state_enum lstate;
@@ -142,7 +143,6 @@ struct mrb_parser_state {
mrb_ast_node *heredocs_from_nextline;
mrb_ast_node *parsing_heredoc;
mrb_ast_node *lex_strterm_before_heredoc;
- mrb_bool heredoc_end_now:1; /* for mirb */
void *ylval;
@@ -151,13 +151,14 @@ struct mrb_parser_state {
mrb_ast_node *tree;
mrb_bool no_optimize:1;
+ mrb_bool on_eval:1;
mrb_bool capture_errors:1;
struct mrb_parser_message error_buffer[10];
struct mrb_parser_message warn_buffer[10];
mrb_sym* filename_table;
- size_t filename_table_length;
- int current_filename_index;
+ uint16_t filename_table_length;
+ uint16_t current_filename_index;
struct mrb_jmpbuf* jmp;
};
@@ -167,7 +168,7 @@ MRB_API void mrb_parser_free(struct mrb_parser_state*);
MRB_API void mrb_parser_parse(struct mrb_parser_state*,mrbc_context*);
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);
+MRB_API mrb_sym mrb_parser_get_filename(struct mrb_parser_state*, uint16_t idx);
/* utility functions */
#ifndef MRB_DISABLE_STDIO
diff --git a/include/mruby/data.h b/include/mruby/data.h
index 590470528..54466dcd6 100644
--- a/include/mruby/data.h
+++ b/include/mruby/data.h
@@ -39,10 +39,11 @@ MRB_API struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass
#define Data_Wrap_Struct(mrb,klass,type,ptr)\
mrb_data_object_alloc(mrb,klass,ptr,type)
-#define Data_Make_Struct(mrb,klass,strct,type,sval,data) do { \
- sval = mrb_malloc(mrb, sizeof(strct)); \
- { static const strct zero = { 0 }; *sval = zero; };\
- data = Data_Wrap_Struct(mrb,klass,type,sval);\
+#define Data_Make_Struct(mrb,klass,strct,type,sval,data_obj) do { \
+ (data_obj) = Data_Wrap_Struct(mrb,klass,type,NULL);\
+ (sval) = (strct *)mrb_malloc(mrb, sizeof(strct)); \
+ { static const strct zero = { 0 }; *(sval) = zero; };\
+ (data_obj)->data = (sval);\
} while (0)
#define RDATA(obj) ((struct RData *)(mrb_ptr(obj)))
@@ -62,7 +63,7 @@ MRB_API void *mrb_data_check_get_ptr(mrb_state *mrb, mrb_value, const mrb_data_t
*(void**)&sval = mrb_data_get_ptr(mrb, obj, type); \
} while (0)
-static inline void
+MRB_INLINE void
mrb_data_init(mrb_value v, void *ptr, const mrb_data_type *type)
{
mrb_assert(mrb_type(v) == MRB_TT_DATA);
diff --git a/include/mruby/debug.h b/include/mruby/debug.h
index d1de34882..e08c47cfc 100644
--- a/include/mruby/debug.h
+++ b/include/mruby/debug.h
@@ -26,7 +26,6 @@ typedef struct mrb_irep_debug_info_line {
typedef struct mrb_irep_debug_info_file {
uint32_t start_pos;
- const char *filename;
mrb_sym filename_sym;
uint32_t line_entry_count;
mrb_debug_line_type line_type;
@@ -47,18 +46,19 @@ typedef struct mrb_irep_debug_info {
* get line from irep's debug info and program counter
* @return returns NULL if not found
*/
-MRB_API const char *mrb_debug_get_filename(mrb_irep *irep, ptrdiff_t pc);
+MRB_API const char *mrb_debug_get_filename(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc);
/*
* get line from irep's debug info and program counter
* @return returns -1 if not found
*/
-MRB_API int32_t mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc);
+MRB_API int32_t mrb_debug_get_line(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc);
+MRB_API mrb_irep_debug_info *mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep);
MRB_API mrb_irep_debug_info_file *mrb_debug_info_append_file(
- mrb_state *mrb, mrb_irep *irep,
+ mrb_state *mrb, mrb_irep_debug_info *info,
+ const char *filename, uint16_t *lines,
uint32_t start_pos, uint32_t end_pos);
-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);
MRB_END_DECL
diff --git a/include/mruby/dump.h b/include/mruby/dump.h
index f56d66a32..65ed8af9d 100644
--- a/include/mruby/dump.h
+++ b/include/mruby/dump.h
@@ -31,6 +31,7 @@ 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_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*);
+MRB_API mrb_irep *mrb_read_irep_buf(mrb_state*, const void*, size_t);
/* dump/load error code
*
@@ -52,15 +53,14 @@ MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*);
/* Rite Binary File header */
#define RITE_BINARY_IDENT "RITE"
#define RITE_BINARY_IDENT_LIL "ETIR"
-#define RITE_BINARY_FORMAT_VER "0004"
+#define RITE_BINARY_FORMAT_VER "0006"
#define RITE_COMPILER_NAME "MATZ"
#define RITE_COMPILER_VERSION "0000"
-#define RITE_VM_VER "0000"
+#define RITE_VM_VER "0002"
#define RITE_BINARY_EOF "END\0"
#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"
@@ -92,10 +92,6 @@ struct rite_section_irep_header {
uint8_t rite_version[4]; /* Rite Instruction Specification Version */
};
-struct rite_section_lineno_header {
- RITE_SECTION_HEADER;
-};
-
struct rite_section_debug_header {
RITE_SECTION_HEADER;
};
diff --git a/include/mruby/error.h b/include/mruby/error.h
index 1587795fc..237c701ad 100644
--- a/include/mruby/error.h
+++ b/include/mruby/error.h
@@ -29,7 +29,7 @@ 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 */
+/* declaration for `fail` method */
MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
struct RBreak {
diff --git a/include/mruby/hash.h b/include/mruby/hash.h
index 1a870785a..7e2ed5596 100644
--- a/include/mruby/hash.h
+++ b/include/mruby/hash.h
@@ -8,7 +8,6 @@
#define MRUBY_HASH_H
#include "common.h"
-#include <mruby/khash.h>
/**
* Hash class
@@ -18,13 +17,15 @@ MRB_BEGIN_DECL
struct RHash {
MRB_OBJECT_HEADER;
struct iv_tbl *iv;
- struct kh_ht *ht;
+ struct htable *ht;
};
#define mrb_hash_ptr(v) ((struct RHash*)(mrb_ptr(v)))
#define mrb_hash_value(p) mrb_obj_value((void*)(p))
MRB_API mrb_value mrb_hash_new_capa(mrb_state*, mrb_int);
+MRB_API mrb_value mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash);
+MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
/*
* Initializes a new hash.
@@ -74,7 +75,7 @@ MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key);
*
* Equivalent to:
*
- * hash.hash_key?(key) ? hash[key] : def
+ * hash.key?(key) ? hash[key] : def
*
* @param mrb The mruby state reference.
* @param hash The target hash.
@@ -110,7 +111,19 @@ MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value
* @return An array with the keys of the hash.
*/
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);
+/*
+ * Check if the hash has the key.
+ *
+ * Equivalent to:
+ *
+ * hash.key?(key)
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @param key The key to check existence.
+ * @return True if the hash has the key
+ */
+MRB_API mrb_bool mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key);
/*
* Check if the hash is empty
@@ -123,7 +136,7 @@ MRB_API mrb_value mrb_check_hash_type(mrb_state *mrb, mrb_value hash);
* @param self The target hash.
* @return True if the hash is empty, false otherwise.
*/
-MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self);
+MRB_API mrb_bool mrb_hash_empty_p(mrb_state *mrb, mrb_value self);
/*
* Gets an array of values.
@@ -151,21 +164,51 @@ MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash);
*/
MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash);
-/* declaration of struct kh_ht */
+/*
+ * Get hash size.
+ *
+ * Equivalent to:
+ *
+ * hash.size
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return The hash size.
+ */
+MRB_API mrb_int mrb_hash_size(mrb_state *mrb, mrb_value hash);
+
+/*
+ * Copies the hash.
+ *
+ *
+ * @param mrb The mruby state reference.
+ * @param hash The target hash.
+ * @return The copy of the hash
+ */
+MRB_API mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value hash);
+
+/*
+ * Merges two hashes. The first hash will be modified by the
+ * second hash.
+ *
+ * @param mrb The mruby state reference.
+ * @param hash1 The target hash.
+ * @param hash2 Updating hash
+ */
+MRB_API void mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2);
+
+/* declaration of struct mrb_hash_value */
/* be careful when you touch the internal */
typedef struct {
mrb_value v;
mrb_int n;
} mrb_hash_value;
-KHASH_DECLARE(ht, mrb_value, mrb_hash_value, TRUE)
-
/* 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)
-MRB_API struct kh_ht * mrb_hash_tbl(mrb_state *mrb, mrb_value hash);
#define MRB_HASH_DEFAULT 1
#define MRB_HASH_PROC_DEFAULT 2
@@ -177,6 +220,10 @@ 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*);
+/* return non zero to break the loop */
+typedef int (mrb_hash_foreach_func)(mrb_state *mrb, mrb_value key, mrb_value val, void *data);
+MRB_API void mrb_hash_foreach(mrb_state *mrb, struct RHash *hash, mrb_hash_foreach_func *func, void *p);
+
MRB_END_DECL
#endif /* MRUBY_HASH_H */
diff --git a/include/mruby/irep.h b/include/mruby/irep.h
index efd226793..d42fd0fb8 100644
--- a/include/mruby/irep.h
+++ b/include/mruby/irep.h
@@ -39,23 +39,48 @@ typedef struct mrb_irep {
struct mrb_locals *lv;
/* debug info */
- mrb_bool own_filename;
- const char *filename;
- uint16_t *lines;
struct mrb_irep_debug_info* debug_info;
- int ilen, plen, slen, rlen, refcnt;
+ uint16_t ilen, plen, slen, rlen;
+ uint32_t refcnt;
} mrb_irep;
#define MRB_ISEQ_NO_FREE 1
MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb);
+
+/* @param [const uint8_t*] irep code, expected as a literal */
MRB_API mrb_value mrb_load_irep(mrb_state*, const uint8_t*);
+
+/*
+ * @param [const void*] irep code
+ * @param [size_t] size of irep buffer. If -1 is given, it is considered unrestricted.
+ */
+MRB_API mrb_value mrb_load_irep_buf(mrb_state*, const void*, size_t);
+
+/* @param [const uint8_t*] irep code, expected as a literal */
MRB_API mrb_value mrb_load_irep_cxt(mrb_state*, const uint8_t*, mrbc_context*);
+
+/*
+ * @param [const void*] irep code
+ * @param [size_t] size of irep buffer. If -1 is given, it is considered unrestricted.
+ */
+MRB_API mrb_value mrb_load_irep_buf_cxt(mrb_state*, const void*, size_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*);
void mrb_irep_cutref(mrb_state*, struct mrb_irep*);
+void mrb_irep_remove_lv(mrb_state *mrb, mrb_irep *irep);
+
+struct mrb_insn_data {
+ uint8_t insn;
+ uint16_t a;
+ uint16_t b;
+ uint8_t c;
+};
+
+struct mrb_insn_data mrb_decode_insn(mrb_code *pc);
MRB_END_DECL
diff --git a/include/mruby/istruct.h b/include/mruby/istruct.h
index 4d2393ccd..23c9bfa36 100644
--- a/include/mruby/istruct.h
+++ b/include/mruby/istruct.h
@@ -19,12 +19,12 @@ MRB_BEGIN_DECL
#define ISTRUCT_DATA_SIZE (sizeof(void*) * 3)
-struct RIstruct {
+struct RIStruct {
MRB_OBJECT_HEADER;
char inline_data[ISTRUCT_DATA_SIZE];
};
-#define RISTRUCT(obj) ((struct RIstruct*)(mrb_ptr(obj)))
+#define RISTRUCT(obj) ((struct RIStruct*)(mrb_ptr(obj)))
#define ISTRUCT_PTR(obj) (RISTRUCT(obj)->inline_data)
MRB_INLINE mrb_int mrb_istruct_size()
diff --git a/include/mruby/numeric.h b/include/mruby/numeric.h
index da9225aaa..34707e441 100644
--- a/include/mruby/numeric.h
+++ b/include/mruby/numeric.h
@@ -23,7 +23,7 @@ MRB_BEGIN_DECL
#define NEGFIXABLE(f) TYPED_NEGFIXABLE(f,mrb_int)
#define FIXABLE(f) TYPED_FIXABLE(f,mrb_int)
#ifndef MRB_WITHOUT_FLOAT
-#define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,double)
+#define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,mrb_float)
#endif
#ifndef MRB_WITHOUT_FLOAT
@@ -34,12 +34,12 @@ MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base);
#ifndef MRB_WITHOUT_FLOAT
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_API mrb_value mrb_int_value(mrb_state *mrb, mrb_float f);
#endif
-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_API mrb_value mrb_num_plus(mrb_state *mrb, mrb_value x, mrb_value y);
+MRB_API mrb_value mrb_num_minus(mrb_state *mrb, mrb_value x, mrb_value y);
+MRB_API mrb_value mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y);
#ifndef __has_builtin
#define __has_builtin(x) 0
diff --git a/include/mruby/object.h b/include/mruby/object.h
index 4f2134ae2..373e3bec7 100644
--- a/include/mruby/object.h
+++ b/include/mruby/object.h
@@ -14,7 +14,7 @@
struct RClass *c;\
struct RBasic *gcnext
-#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & flag)
+#define MRB_FLAG_TEST(obj, flag) ((obj)->flags & (flag))
struct RBasic {
@@ -22,11 +22,10 @@ struct RBasic {
};
#define mrb_basic_ptr(v) ((struct RBasic*)(mrb_ptr(v)))
-/* flags bits >= 18 is reserved */
-#define MRB_FLAG_IS_FROZEN (1 << 18)
-#define MRB_FROZEN_P(o) ((o)->flags & MRB_FLAG_IS_FROZEN)
-#define MRB_SET_FROZEN_FLAG(o) ((o)->flags |= MRB_FLAG_IS_FROZEN)
-#define MRB_UNSET_FROZEN_FLAG(o) ((o)->flags &= ~MRB_FLAG_IS_FROZEN)
+#define MRB_FL_OBJ_IS_FROZEN (1 << 20)
+#define MRB_FROZEN_P(o) ((o)->flags & MRB_FL_OBJ_IS_FROZEN)
+#define MRB_SET_FROZEN_FLAG(o) ((o)->flags |= MRB_FL_OBJ_IS_FROZEN)
+#define MRB_UNSET_FROZEN_FLAG(o) ((o)->flags &= ~MRB_FL_OBJ_IS_FROZEN)
struct RObject {
MRB_OBJECT_HEADER;
diff --git a/include/mruby/opcode.h b/include/mruby/opcode.h
index a5e3af158..d513ca472 100644
--- a/include/mruby/opcode.h
+++ b/include/mruby/opcode.h
@@ -7,145 +7,10 @@
#ifndef MRUBY_OPCODE_H
#define MRUBY_OPCODE_H
-#define MAXARG_Bx (0xffff)
-#define MAXARG_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */
-
-/* instructions: packed 32 bit */
-/* ------------------------------- */
-/* A:B:C:OP = 9: 9: 7: 7 */
-/* A:Bx:OP = 9:16: 7 */
-/* Ax:OP = 25: 7 */
-/* A:Bz:Cz:OP = 9:14: 2: 7 */
-
-#define GET_OPCODE(i) ((int)(((mrb_code)(i)) & 0x7f))
-#define GETARG_A(i) ((int)((((mrb_code)(i)) >> 23) & 0x1ff))
-#define GETARG_B(i) ((int)((((mrb_code)(i)) >> 14) & 0x1ff))
-#define GETARG_C(i) ((int)((((mrb_code)(i)) >> 7) & 0x7f))
-#define GETARG_Bx(i) ((int)((((mrb_code)(i)) >> 7) & 0xffff))
-#define GETARG_sBx(i) ((int)(GETARG_Bx(i)-MAXARG_sBx))
-#define GETARG_Ax(i) ((int32_t)((((mrb_code)(i)) >> 7) & 0x1ffffff))
-#define GETARG_UNPACK_b(i,n1,n2) ((int)((((mrb_code)(i)) >> (7+(n2))) & (((1<<(n1))-1))))
-#define GETARG_UNPACK_c(i,n1,n2) ((int)((((mrb_code)(i)) >> 7) & (((1<<(n2))-1))))
-#define GETARG_b(i) GETARG_UNPACK_b(i,14,2)
-#define GETARG_c(i) GETARG_UNPACK_c(i,14,2)
-
-#define MKOPCODE(op) ((op) & 0x7f)
-#define MKARG_A(c) ((mrb_code)((c) & 0x1ff) << 23)
-#define MKARG_B(c) ((mrb_code)((c) & 0x1ff) << 14)
-#define MKARG_C(c) (((c) & 0x7f) << 7)
-#define MKARG_Bx(v) ((mrb_code)((v) & 0xffff) << 7)
-#define MKARG_sBx(v) MKARG_Bx((v)+MAXARG_sBx)
-#define MKARG_Ax(v) ((mrb_code)((v) & 0x1ffffff) << 7)
-#define MKARG_PACK(b,n1,c,n2) ((((b) & ((1<<n1)-1)) << (7+n2))|(((c) & ((1<<n2)-1)) << 7))
-#define MKARG_bc(b,c) MKARG_PACK(b,14,c,2)
-
-#define MKOP_A(op,a) (MKOPCODE(op)|MKARG_A(a))
-#define MKOP_AB(op,a,b) (MKOP_A(op,a)|MKARG_B(b))
-#define MKOP_ABC(op,a,b,c) (MKOP_AB(op,a,b)|MKARG_C(c))
-#define MKOP_ABx(op,a,bx) (MKOP_A(op,a)|MKARG_Bx(bx))
-#define MKOP_Bx(op,bx) (MKOPCODE(op)|MKARG_Bx(bx))
-#define MKOP_sBx(op,sbx) (MKOPCODE(op)|MKARG_sBx(sbx))
-#define MKOP_AsBx(op,a,sbx) (MKOP_A(op,a)|MKARG_sBx(sbx))
-#define MKOP_Ax(op,ax) (MKOPCODE(op)|MKARG_Ax(ax))
-#define MKOP_Abc(op,a,b,c) (MKOP_A(op,a)|MKARG_bc(b,c))
-
-enum {
- /*-----------------------------------------------------------------------
- operation code operand description
- ------------------------------------------------------------------------*/
- OP_NOP=0,/* */
- OP_MOVE,/* A B R(A) := R(B) */
- OP_LOADL,/* A Bx R(A) := Pool(Bx) */
- OP_LOADI,/* A sBx R(A) := sBx */
- OP_LOADSYM,/* A Bx R(A) := Syms(Bx) */
- OP_LOADNIL,/* A R(A) := nil */
- OP_LOADSELF,/* A R(A) := self */
- OP_LOADT,/* A R(A) := true */
- OP_LOADF,/* A R(A) := false */
-
- OP_GETGLOBAL,/* A Bx R(A) := getglobal(Syms(Bx)) */
- OP_SETGLOBAL,/* A Bx setglobal(Syms(Bx), R(A)) */
- OP_GETSPECIAL,/*A Bx R(A) := Special[Bx] */
- OP_SETSPECIAL,/*A Bx Special[Bx] := R(A) */
- OP_GETIV,/* A Bx R(A) := ivget(Syms(Bx)) */
- OP_SETIV,/* A Bx ivset(Syms(Bx),R(A)) */
- OP_GETCV,/* A Bx R(A) := cvget(Syms(Bx)) */
- OP_SETCV,/* A Bx cvset(Syms(Bx),R(A)) */
- OP_GETCONST,/* A Bx R(A) := constget(Syms(Bx)) */
- OP_SETCONST,/* A Bx constset(Syms(Bx),R(A)) */
- OP_GETMCNST,/* A Bx R(A) := R(A)::Syms(Bx) */
- OP_SETMCNST,/* A Bx R(A+1)::Syms(Bx) := R(A) */
- OP_GETUPVAR,/* A B C R(A) := uvget(B,C) */
- OP_SETUPVAR,/* A B C uvset(B,C,R(A)) */
-
- OP_JMP,/* sBx pc+=sBx */
- OP_JMPIF,/* A sBx if R(A) pc+=sBx */
- OP_JMPNOT,/* A sBx if !R(A) pc+=sBx */
- OP_ONERR,/* sBx rescue_push(pc+sBx) */
- OP_RESCUE,/* A B C if A (if C exc=R(A) else R(A) := exc);
- if B R(B) := exc.isa?(R(B)); clear(exc) */
- OP_POPERR,/* A A.times{rescue_pop()} */
- OP_RAISE,/* A raise(R(A)) */
- OP_EPUSH,/* Bx ensure_push(SEQ[Bx]) */
- OP_EPOP,/* A A.times{ensure_pop().call} */
-
- OP_SEND,/* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C)) */
- OP_SENDB,/* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C),&R(A+C+1))*/
- OP_FSEND,/* A B C R(A) := fcall(R(A),Syms(B),R(A+1),...,R(A+C-1)) */
- OP_CALL,/* A R(A) := self.call(frame.argc, frame.argv) */
- OP_SUPER,/* A C R(A) := super(R(A+1),... ,R(A+C+1)) */
- OP_ARGARY,/* A Bx R(A) := argument array (16=6:1:5:4) */
- OP_ENTER,/* Ax arg setup according to flags (23=5:5:1:5:5:1:1) */
- OP_KARG,/* A B C R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B)) */
- OP_KDICT,/* A C R(A) := kdict */
-
- OP_RETURN,/* A B return R(A) (B=normal,in-block return/break) */
- OP_TAILCALL,/* A B C return call(R(A),Syms(B),*R(C)) */
- OP_BLKPUSH,/* A Bx R(A) := block (16=6:1:5:4) */
-
- OP_ADD,/* A B C R(A) := R(A)+R(A+1) (Syms[B]=:+,C=1) */
- OP_ADDI,/* A B C R(A) := R(A)+C (Syms[B]=:+) */
- OP_SUB,/* A B C R(A) := R(A)-R(A+1) (Syms[B]=:-,C=1) */
- OP_SUBI,/* A B C R(A) := R(A)-C (Syms[B]=:-) */
- OP_MUL,/* A B C R(A) := R(A)*R(A+1) (Syms[B]=:*,C=1) */
- OP_DIV,/* A B C R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1) */
- OP_EQ,/* A B C R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1) */
- OP_LT,/* A B C R(A) := R(A)<R(A+1) (Syms[B]=:<,C=1) */
- OP_LE,/* A B C R(A) := R(A)<=R(A+1) (Syms[B]=:<=,C=1) */
- OP_GT,/* A B C R(A) := R(A)>R(A+1) (Syms[B]=:>,C=1) */
- OP_GE,/* A B C R(A) := R(A)>=R(A+1) (Syms[B]=:>=,C=1) */
-
- OP_ARRAY,/* A B C R(A) := ary_new(R(B),R(B+1)..R(B+C)) */
- OP_ARYCAT,/* A B ary_cat(R(A),R(B)) */
- OP_ARYPUSH,/* A B ary_push(R(A),R(B)) */
- OP_AREF,/* A B C R(A) := R(B)[C] */
- OP_ASET,/* A B C R(B)[C] := R(A) */
- OP_APOST,/* A B C *R(A),R(A+1)..R(A+C) := R(A)[B..] */
-
- OP_STRING,/* A Bx R(A) := str_dup(Lit(Bx)) */
- OP_STRCAT,/* A B str_cat(R(A),R(B)) */
-
- OP_HASH,/* A B C R(A) := hash_new(R(B),R(B+1)..R(B+C)) */
- OP_LAMBDA,/* A Bz Cz R(A) := lambda(SEQ[Bz],Cz) */
- OP_RANGE,/* A B C R(A) := range_new(R(B),R(B+1),C) */
-
- OP_OCLASS,/* A R(A) := ::Object */
- OP_CLASS,/* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */
- OP_MODULE,/* A B R(A) := newmodule(R(A),Syms(B)) */
- OP_EXEC,/* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */
- OP_METHOD,/* A B R(A).newmethod(Syms(B),R(A+1)) */
- OP_SCLASS,/* A B R(A) := R(B).singleton_class */
- OP_TCLASS,/* A R(A) := target_class */
-
- OP_DEBUG,/* A B C print R(A),R(B),R(C) */
- OP_STOP,/* stop VM */
- OP_ERR,/* Bx raise RuntimeError with message Lit(Bx) */
-
- OP_RSVD1,/* reserved instruction #1 */
- OP_RSVD2,/* reserved instruction #2 */
- OP_RSVD3,/* reserved instruction #3 */
- OP_RSVD4,/* reserved instruction #4 */
- OP_RSVD5,/* reserved instruction #5 */
+enum mrb_insn {
+#define OPCODE(x,_) OP_ ## x,
+#include "mruby/ops.h"
+#undef OPCODE
};
#define OP_L_STRICT 1
@@ -158,4 +23,47 @@ enum {
#define OP_R_BREAK 1
#define OP_R_RETURN 2
+#define PEEK_B(pc) (*(pc))
+#define PEEK_S(pc) ((pc)[0]<<8|(pc)[1])
+#define PEEK_W(pc) ((pc)[0]<<16|(pc)[1]<<8|(pc)[2])
+
+#define READ_B() PEEK_B(pc++)
+#define READ_S() (pc+=2, PEEK_S(pc-2))
+#define READ_W() (pc+=3, PEEK_W(pc-3))
+
+#define FETCH_Z() /* nothing */
+#define FETCH_B() do {a=READ_B();} while (0)
+#define FETCH_BB() do {a=READ_B(); b=READ_B();} while (0)
+#define FETCH_BBB() do {a=READ_B(); b=READ_B(); c=READ_B();} while (0)
+#define FETCH_BS() do {a=READ_B(); b=READ_S();} while (0)
+#define FETCH_S() do {a=READ_S();} while (0)
+#define FETCH_W() do {a=READ_W();} while (0)
+
+/* with OP_EXT1 (1st 16bit) */
+#define FETCH_Z_1() FETCH_Z()
+#define FETCH_B_1() FETCH_S()
+#define FETCH_BB_1() do {a=READ_S(); b=READ_B();} while (0)
+#define FETCH_BBB_1() do {a=READ_S(); b=READ_B(); c=READ_B();} while (0)
+#define FETCH_BS_1() do {a=READ_S(); b=READ_S();} while (0)
+#define FETCH_S_1() FETCH_S()
+#define FETCH_W_1() FETCH_W()
+
+/* with OP_EXT2 (2nd 16bit) */
+#define FETCH_Z_2() FETCH_Z()
+#define FETCH_B_2() FETCH_B()
+#define FETCH_BB_2() do {a=READ_B(); b=READ_S();} while (0)
+#define FETCH_BBB_2() do {a=READ_B(); b=READ_S(); c=READ_B();} while (0)
+#define FETCH_BS_2() FETCH_BS()
+#define FETCH_S_2() FETCH_S()
+#define FETCH_W_2() FETCH_W()
+
+/* with OP_EXT3 (1st & 2nd 16bit) */
+#define FETCH_Z_3() FETCH_Z()
+#define FETCH_B_3() FETCH_B()
+#define FETCH_BB_3() do {a=READ_S(); b=READ_S();} while (0)
+#define FETCH_BBB_3() do {a=READ_S(); b=READ_S(); c=READ_B();} while (0)
+#define FETCH_BS_3() do {a=READ_S(); b=READ_S();} while (0)
+#define FETCH_S_3() FETCH_S()
+#define FETCH_W_3() FETCH_W()
+
#endif /* MRUBY_OPCODE_H */
diff --git a/include/mruby/ops.h b/include/mruby/ops.h
new file mode 100644
index 000000000..d64594625
--- /dev/null
+++ b/include/mruby/ops.h
@@ -0,0 +1,117 @@
+/* operand types:
+ + Z: no operand (Z,Z,Z,Z)
+ + B: 8bit (B,S,B,B)
+ + BB: 8+8bit (BB,SB,BS,SS)
+ + BBB: 8+8+8bit (BBB,SBB,BSB,SSB)
+ + BS: 8+16bit (BS,SS,BS,BS)
+ + S: 16bit (S,S,S,S)
+ + W: 24bit (W,W,W,W)
+*/
+
+/*-----------------------------------------------------------------------
+operation code operands semantics
+------------------------------------------------------------------------*/
+OPCODE(NOP, Z) /* no operation */
+OPCODE(MOVE, BB) /* R(a) = R(b) */
+OPCODE(LOADL, BB) /* R(a) = Pool(b) */
+OPCODE(LOADI, BB) /* R(a) = mrb_int(b) */
+OPCODE(LOADINEG, BB) /* R(a) = mrb_int(-b) */
+OPCODE(LOADI__1, B) /* R(a) = mrb_int(-1) */
+OPCODE(LOADI_0, B) /* R(a) = mrb_int(0) */
+OPCODE(LOADI_1, B) /* R(a) = mrb_int(1) */
+OPCODE(LOADI_2, B) /* R(a) = mrb_int(2) */
+OPCODE(LOADI_3, B) /* R(a) = mrb_int(3) */
+OPCODE(LOADI_4, B) /* R(a) = mrb_int(4) */
+OPCODE(LOADI_5, B) /* R(a) = mrb_int(5) */
+OPCODE(LOADI_6, B) /* R(a) = mrb_int(6) */
+OPCODE(LOADI_7, B) /* R(a) = mrb_int(7) */
+OPCODE(LOADSYM, BB) /* R(a) = Syms(b) */
+OPCODE(LOADNIL, B) /* R(a) = nil */
+OPCODE(LOADSELF, B) /* R(a) = self */
+OPCODE(LOADT, B) /* R(a) = true */
+OPCODE(LOADF, B) /* R(a) = false */
+OPCODE(GETGV, BB) /* R(a) = getglobal(Syms(b)) */
+OPCODE(SETGV, BB) /* setglobal(Syms(b), R(a)) */
+OPCODE(GETSV, BB) /* R(a) = Special[Syms(b)] */
+OPCODE(SETSV, BB) /* Special[Syms(b)] = R(a) */
+OPCODE(GETIV, BB) /* R(a) = ivget(Syms(b)) */
+OPCODE(SETIV, BB) /* ivset(Syms(b),R(a)) */
+OPCODE(GETCV, BB) /* R(a) = cvget(Syms(b)) */
+OPCODE(SETCV, BB) /* cvset(Syms(b),R(a)) */
+OPCODE(GETCONST, BB) /* R(a) = constget(Syms(b)) */
+OPCODE(SETCONST, BB) /* constset(Syms(b),R(a)) */
+OPCODE(GETMCNST, BB) /* R(a) = R(a)::Syms(b) */
+OPCODE(SETMCNST, BB) /* R(a+1)::Syms(b) = R(a) */
+OPCODE(GETUPVAR, BBB) /* R(a) = uvget(b,c) */
+OPCODE(SETUPVAR, BBB) /* uvset(b,c,R(a)) */
+OPCODE(JMP, S) /* pc=a */
+OPCODE(JMPIF, BS) /* if R(b) pc=a */
+OPCODE(JMPNOT, BS) /* if !R(b) pc=a */
+OPCODE(JMPNIL, BS) /* if R(b)==nil pc=a */
+OPCODE(ONERR, S) /* rescue_push(a) */
+OPCODE(EXCEPT, B) /* R(a) = exc */
+OPCODE(RESCUE, BB) /* R(b) = R(a).isa?(R(b)) */
+OPCODE(POPERR, B) /* a.times{rescue_pop()} */
+OPCODE(RAISE, B) /* raise(R(a)) */
+OPCODE(EPUSH, B) /* ensure_push(SEQ[a]) */
+OPCODE(EPOP, B) /* A.times{ensure_pop().call} */
+OPCODE(SENDV, BB) /* R(a) = call(R(a),Syms(b),*R(a+1)) */
+OPCODE(SENDVB, BB) /* R(a) = call(R(a),Syms(b),*R(a+1),&R(a+2)) */
+OPCODE(SEND, BBB) /* R(a) = call(R(a),Syms(b),R(a+1),...,R(a+c)) */
+OPCODE(SENDB, BBB) /* R(a) = call(R(a),Syms(Bx),R(a+1),...,R(a+c),&R(a+c+1)) */
+OPCODE(CALL, Z) /* R(0) = self.call(frame.argc, frame.argv) */
+OPCODE(SUPER, BB) /* R(a) = super(R(a+1),... ,R(a+b+1)) */
+OPCODE(ARGARY, BS) /* R(a) = argument array (16=m5:r1:m5:d1:lv4) */
+OPCODE(ENTER, W) /* arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) */
+OPCODE(KEY_P, BB) /* R(a) = kdict.key?(Syms(b)) # todo */
+OPCODE(KEYEND, Z) /* raise unless kdict.empty? # todo */
+OPCODE(KARG, BB) /* R(a) = kdict[Syms(b)]; kdict.delete(Syms(b)) # todo */
+OPCODE(RETURN, B) /* return R(a) (normal) */
+OPCODE(RETURN_BLK, B) /* return R(a) (in-block return) */
+OPCODE(BREAK, B) /* break R(a) */
+OPCODE(BLKPUSH, BS) /* R(a) = block (16=m5:r1:m5:d1:lv4) */
+OPCODE(ADD, B) /* R(a) = R(a)+R(a+1) */
+OPCODE(ADDI, BB) /* R(a) = R(a)+mrb_int(c) */
+OPCODE(SUB, B) /* R(a) = R(a)-R(a+1) */
+OPCODE(SUBI, BB) /* R(a) = R(a)-C */
+OPCODE(MUL, B) /* R(a) = R(a)*R(a+1) */
+OPCODE(DIV, B) /* R(a) = R(a)/R(a+1) */
+OPCODE(EQ, B) /* R(a) = R(a)==R(a+1) */
+OPCODE(LT, B) /* R(a) = R(a)<R(a+1) */
+OPCODE(LE, B) /* R(a) = R(a)<=R(a+1) */
+OPCODE(GT, B) /* R(a) = R(a)>R(a+1) */
+OPCODE(GE, B) /* R(a) = R(a)>=R(a+1) */
+OPCODE(ARRAY, BB) /* R(a) = ary_new(R(a),R(a+1)..R(a+b)) */
+OPCODE(ARRAY2, BBB) /* R(a) = ary_new(R(b),R(b+1)..R(b+c)) */
+OPCODE(ARYCAT, B) /* ary_cat(R(a),R(a+1)) */
+OPCODE(ARYPUSH, B) /* ary_push(R(a),R(a+1)) */
+OPCODE(ARYDUP, B) /* R(a) = ary_dup(R(a)) */
+OPCODE(AREF, BBB) /* R(a) = R(b)[c] */
+OPCODE(ASET, BBB) /* R(a)[c] = R(b) */
+OPCODE(APOST, BBB) /* *R(a),R(a+1)..R(a+c) = R(a)[b..] */
+OPCODE(INTERN, B) /* R(a) = intern(R(a)) */
+OPCODE(STRING, BB) /* R(a) = str_dup(Lit(b)) */
+OPCODE(STRCAT, B) /* str_cat(R(a),R(a+1)) */
+OPCODE(HASH, BB) /* R(a) = hash_new(R(a),R(a+1)..R(a+b)) */
+OPCODE(HASHADD, BB) /* R(a) = hash_push(R(a),R(a+1)..R(a+b)) */
+OPCODE(HASHCAT, B) /* R(a) = hash_cat(R(a),R(a+1)) */
+OPCODE(LAMBDA, BB) /* R(a) = lambda(SEQ[b],L_LAMBDA) */
+OPCODE(BLOCK, BB) /* R(a) = lambda(SEQ[b],L_BLOCK) */
+OPCODE(METHOD, BB) /* R(a) = lambda(SEQ[b],L_METHOD) */
+OPCODE(RANGE_INC, B) /* R(a) = range_new(R(a),R(a+1),FALSE) */
+OPCODE(RANGE_EXC, B) /* R(a) = range_new(R(a),R(a+1),TRUE) */
+OPCODE(OCLASS, B) /* R(a) = ::Object */
+OPCODE(CLASS, BB) /* R(a) = newclass(R(a),Syms(b),R(a+1)) */
+OPCODE(MODULE, BB) /* R(a) = newmodule(R(a),Syms(b)) */
+OPCODE(EXEC, BB) /* R(a) = blockexec(R(a),SEQ[b]) */
+OPCODE(DEF, BB) /* R(a).newmethod(Syms(b),R(a+1)) */
+OPCODE(ALIAS, BB) /* alias_method(target_class,Syms(a),Syms(b)) */
+OPCODE(UNDEF, B) /* undef_method(target_class,Syms(a)) */
+OPCODE(SCLASS, B) /* R(a) = R(a).singleton_class */
+OPCODE(TCLASS, B) /* R(a) = target_class */
+OPCODE(DEBUG, BBB) /* print a,b,c */
+OPCODE(ERR, B) /* raise(LocalJumpError, Lit(a)) */
+OPCODE(EXT1, Z) /* make 1st operand 16bit */
+OPCODE(EXT2, Z) /* make 2nd operand 16bit */
+OPCODE(EXT3, Z) /* make 1st and 2nd operands 16bit */
+OPCODE(STOP, Z) /* stop VM */
diff --git a/include/mruby/proc.h b/include/mruby/proc.h
index aa281b6dd..a8b16db1d 100644
--- a/include/mruby/proc.h
+++ b/include/mruby/proc.h
@@ -23,13 +23,13 @@ struct REnv {
};
/* flags (21bits): 1(shared flag):10(cioff/bidx):10(stack_len) */
-#define MRB_ENV_SET_STACK_LEN(e,len) (e)->flags = (((e)->flags & ~0x3ff)|((unsigned int)(len) & 0x3ff))
+#define MRB_ENV_SET_STACK_LEN(e,len) ((e)->flags = (((e)->flags & ~0x3ff)|((unsigned int)(len) & 0x3ff)))
#define MRB_ENV_STACK_LEN(e) ((mrb_int)((e)->flags & 0x3ff))
#define MRB_ENV_STACK_UNSHARED (1<<20)
-#define MRB_ENV_UNSHARE_STACK(e) (e)->flags |= MRB_ENV_STACK_UNSHARED
+#define MRB_ENV_UNSHARE_STACK(e) ((e)->flags |= MRB_ENV_STACK_UNSHARED)
#define MRB_ENV_STACK_SHARED_P(e) (((e)->flags & MRB_ENV_STACK_UNSHARED) == 0)
#define MRB_ENV_BIDX(e) (((e)->flags >> 10) & 0x3ff)
-#define MRB_ENV_SET_BIDX(e,idx) (e)->flags = (((e)->flags & ~(0x3ff<<10))|((unsigned int)(idx) & 0x3ff)<<10)
+#define MRB_ENV_SET_BIDX(e,idx) ((e)->flags = (((e)->flags & ~(0x3ff<<10))|((unsigned int)(idx) & 0x3ff)<<10))
void mrb_env_unshare(mrb_state*, struct REnv*);
@@ -69,11 +69,11 @@ struct RProc {
#define MRB_PROC_SET_TARGET_CLASS(p,tc) do {\
if (MRB_PROC_ENV_P(p)) {\
(p)->e.env->c = (tc);\
- mrb_field_write_barrier(mrb, (struct RBasic*)(p)->e.env, (struct RBasic*)tc);\
+ mrb_field_write_barrier(mrb, (struct RBasic*)(p)->e.env, (struct RBasic*)(tc));\
}\
else {\
(p)->e.target_class = (tc);\
- mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)tc);\
+ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)(tc));\
}\
} while (0)
#define MRB_PROC_SCOPE 2048
@@ -86,9 +86,10 @@ struct RProc *mrb_closure_new(mrb_state*, mrb_irep*);
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);
+mrb_int mrb_proc_arity(const struct RProc *p);
/* implementation of #send method */
-MRB_API mrb_value mrb_f_send(mrb_state *mrb, mrb_value self);
+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 */
MRB_API struct RProc *mrb_proc_new_cfunc_with_env(mrb_state*, mrb_func_t, mrb_int, const mrb_value*);
@@ -101,8 +102,8 @@ MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state*, mrb_int);
#define MRB_METHOD_FUNC_FL ((uintptr_t)1)
#define MRB_METHOD_FUNC_P(m) (((uintptr_t)(m))&MRB_METHOD_FUNC_FL)
#define MRB_METHOD_FUNC(m) ((mrb_func_t)((uintptr_t)(m)&(~MRB_METHOD_FUNC_FL)))
-#define MRB_METHOD_FROM_FUNC(m,fn) m=(mrb_method_t)((struct RProc*)((uintptr_t)(fn)|MRB_METHOD_FUNC_FL))
-#define MRB_METHOD_FROM_PROC(m,pr) m=(mrb_method_t)(struct RProc*)(pr)
+#define MRB_METHOD_FROM_FUNC(m,fn) ((m)=(mrb_method_t)((struct RProc*)((uintptr_t)(fn)|MRB_METHOD_FUNC_FL)))
+#define MRB_METHOD_FROM_PROC(m,pr) ((m)=(mrb_method_t)(struct RProc*)(pr))
#define MRB_METHOD_PROC_P(m) (!MRB_METHOD_FUNC_P(m))
#define MRB_METHOD_PROC(m) ((struct RProc*)(m))
#define MRB_METHOD_UNDEF_P(m) ((m)==0)
diff --git a/include/mruby/range.h b/include/mruby/range.h
index b166e586b..500a941d5 100644
--- a/include/mruby/range.h
+++ b/include/mruby/range.h
@@ -14,20 +14,43 @@
*/
MRB_BEGIN_DECL
+#if defined(MRB_NAN_BOXING) && defined(MRB_64BIT) || defined(MRB_WORD_BOXING)
+# define MRB_RANGE_EMBED
+#endif
+
+#ifdef MRB_RANGE_EMBED
+struct RRange {
+ MRB_OBJECT_HEADER;
+ mrb_value beg;
+ mrb_value end;
+ mrb_bool excl;
+};
+# define mrb_gc_free_range(mrb, p) ((void)0)
+# define RANGE_BEG(p) ((p)->beg)
+# define RANGE_END(p) ((p)->end)
+#else
typedef struct mrb_range_edges {
mrb_value beg;
mrb_value end;
} mrb_range_edges;
-
struct RRange {
MRB_OBJECT_HEADER;
mrb_range_edges *edges;
- mrb_bool excl : 1;
+ mrb_bool excl;
};
+# define mrb_gc_free_range(mrb, p) mrb_free(mrb, (p)->edges)
+# define RANGE_BEG(p) ((p)->edges->beg)
+# define RANGE_END(p) ((p)->edges->end)
+#endif
+
+#define mrb_range_beg(mrb, r) RANGE_BEG(mrb_range_ptr(mrb, r))
+#define mrb_range_end(mrb, r) RANGE_END(mrb_range_ptr(mrb, r))
+#define mrb_range_excl_p(mrb, r) RANGE_EXCL(mrb_range_ptr(mrb, r))
+#define mrb_range_raw_ptr(r) ((struct RRange*)mrb_ptr(r))
+#define mrb_range_value(p) mrb_obj_value((void*)(p))
+#define RANGE_EXCL(p) ((p)->excl)
-MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value v);
-#define mrb_range_raw_ptr(v) ((struct RRange*)mrb_ptr(v))
-#define mrb_range_value(p) mrb_obj_value((void*)(p))
+MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value range);
/*
* Initializes a Range.
@@ -41,8 +64,15 @@ MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value v);
*/
MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value start, mrb_value end, mrb_bool exclude);
-MRB_API mrb_int mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc);
+enum mrb_range_beg_len {
+ MRB_RANGE_TYPE_MISMATCH = 0, /* (failure) not range */
+ MRB_RANGE_OK = 1, /* (success) range */
+ MRB_RANGE_OUT = 2 /* (failure) out of range */
+};
+
+MRB_API enum mrb_range_beg_len mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc);
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));
+void mrb_gc_mark_range(mrb_state *mrb, struct RRange *r);
MRB_END_DECL
diff --git a/include/mruby/string.h b/include/mruby/string.h
index 975b1fe0d..77f6becf6 100644
--- a/include/mruby/string.h
+++ b/include/mruby/string.h
@@ -39,15 +39,15 @@ struct RString {
#define RSTR_UNSET_EMBED_FLAG(s) ((s)->flags &= ~(MRB_STR_EMBED|MRB_STR_EMBED_LEN_MASK))
#define RSTR_SET_EMBED_LEN(s, n) do {\
size_t tmp_n = (n);\
- s->flags &= ~MRB_STR_EMBED_LEN_MASK;\
- s->flags |= (tmp_n) << MRB_STR_EMBED_LEN_SHIFT;\
+ (s)->flags &= ~MRB_STR_EMBED_LEN_MASK;\
+ (s)->flags |= (tmp_n) << MRB_STR_EMBED_LEN_SHIFT;\
} while (0)
#define RSTR_SET_LEN(s, n) do {\
if (RSTR_EMBED_P(s)) {\
RSTR_SET_EMBED_LEN((s),(n));\
}\
else {\
- s->as.heap.len = (mrb_int)(n);\
+ (s)->as.heap.len = (mrb_int)(n);\
}\
} while (0)
#define RSTR_EMBED_LEN(s)\
@@ -68,6 +68,20 @@ struct RString {
#define RSTR_SET_NOFREE_FLAG(s) ((s)->flags |= MRB_STR_NOFREE)
#define RSTR_UNSET_NOFREE_FLAG(s) ((s)->flags &= ~MRB_STR_NOFREE)
+#ifdef MRB_UTF8_STRING
+# define RSTR_ASCII_P(s) ((s)->flags & MRB_STR_ASCII)
+# define RSTR_SET_ASCII_FLAG(s) ((s)->flags |= MRB_STR_ASCII)
+# define RSTR_UNSET_ASCII_FLAG(s) ((s)->flags &= ~MRB_STR_ASCII)
+# define RSTR_WRITE_ASCII_FLAG(s, v) (RSTR_UNSET_ASCII_FLAG(s), (s)->flags |= v)
+# define RSTR_COPY_ASCII_FLAG(dst, src) RSTR_WRITE_ASCII_FLAG(dst, RSTR_ASCII_P(src))
+#else
+# define RSTR_ASCII_P(s) (void)0
+# define RSTR_SET_ASCII_FLAG(s) (void)0
+# define RSTR_UNSET_ASCII_FLAG(s) (void)0
+# define RSTR_WRITE_ASCII_FLAG(s, v) (void)0
+# define RSTR_COPY_ASCII_FLAG(dst, src) (void)0
+#endif
+
#define RSTR_POOL_P(s) ((s)->flags & MRB_STR_POOL)
#define RSTR_SET_POOL_FLAG(s) ((s)->flags |= MRB_STR_POOL)
@@ -87,13 +101,14 @@ MRB_API mrb_int mrb_str_strlen(mrb_state*, struct RString*);
#define MRB_STR_FSHARED 2
#define MRB_STR_NOFREE 4
#define MRB_STR_POOL 8
-#define MRB_STR_NO_UTF 16
+#define MRB_STR_ASCII 16
#define MRB_STR_EMBED 32
#define MRB_STR_EMBED_LEN_MASK 0x7c0
#define MRB_STR_EMBED_LEN_SHIFT 6
void mrb_gc_free_str(mrb_state*, struct RString*);
-MRB_API void mrb_str_modify(mrb_state*, struct RString*);
+
+MRB_API void mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s);
/*
* Finds the index of a substring in a string
@@ -102,7 +117,7 @@ MRB_API mrb_int mrb_str_index(mrb_state*, mrb_value, const char*, mrb_int, mrb_i
#define mrb_str_index_lit(mrb, str, lit, off) mrb_str_index(mrb, str, lit, mrb_strlen_lit(lit), off);
/*
- * Appends self to other. Returns self as a concatnated string.
+ * Appends self to other. Returns self as a concatenated string.
*
*
* Example:
@@ -126,10 +141,10 @@ MRB_API mrb_int mrb_str_index(mrb_state*, mrb_value, const char*, mrb_int, mrb_i
* str1 = mrb_str_new_lit(mrb, "abc");
* str2 = mrb_str_new_lit(mrb, "def");
*
- * // Concatnates str2 to str1.
+ * // Concatenates str2 to str1.
* mrb_str_concat(mrb, str1, str2);
*
- * // Prints new Concatnated Ruby string.
+ * // Prints new Concatenated Ruby string.
* mrb_p(mrb, str1);
*
* mrb_close(mrb);
@@ -178,10 +193,10 @@ MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
* mrb_p(mrb, a);
* mrb_p(mrb, b);
*
- * // Concatnates both Ruby strings.
+ * // Concatenates both Ruby strings.
* c = mrb_str_plus(mrb, a, b);
*
- * // Prints new Concatnated Ruby string.
+ * // Prints new Concatenated Ruby string.
* mrb_p(mrb, c);
*
* mrb_close(mrb);
@@ -193,7 +208,7 @@ MRB_API void mrb_str_concat(mrb_state*, mrb_value, mrb_value);
*
* => "abc" # First string
* => "def" # Second string
- * => "abcdef" # First & Second concatnated.
+ * => "abcdef" # First & Second concatenated.
*
* @param [mrb_state] mrb The current mruby state.
* @param [mrb_value] a First string to concatenate.
@@ -311,9 +326,12 @@ MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb
* @param [mrb_value] str Ruby string.
* @return [mrb_value] A Ruby string.
*/
+MRB_API mrb_value mrb_ensure_string_type(mrb_state *mrb, mrb_value str);
+MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str);
+/* obsolete: use mrb_ensure_string_type() instead */
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_new_capa(mrb_state *mrb, size_t capa);
MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa);
@@ -349,10 +367,13 @@ MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str);
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 mrb_value mrb_cstr_to_inum(mrb_state *mrb, const char *s, mrb_int base, mrb_bool badcheck);
MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck);
+MRB_API double mrb_cstr_to_dbl(mrb_state *mrb, const char *s, mrb_bool badcheck);
/*
* Returns a converted string type.
+ * For type checking, non converting `mrb_to_str` is recommended.
*/
MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str);
@@ -427,14 +448,25 @@ mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str);
*/
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 */
#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_bool mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp);
+mrb_value mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
+
+MRB_INLINE void
+mrb_str_modify(mrb_state *mrb, struct RString *s)
+{
+ mrb_str_modify_keep_ascii(mrb, s);
+ RSTR_UNSET_ASCII_FLAG(s);
+}
+
+#ifdef MRB_UTF8_STRING
+mrb_int mrb_utf8_len(const char *str, mrb_int byte_len);
+#endif
+
MRB_END_DECL
#endif /* MRUBY_STRING_H */
diff --git a/include/mruby/throw.h b/include/mruby/throw.h
index 5d3d214e7..4a1fd8d60 100644
--- a/include/mruby/throw.h
+++ b/include/mruby/throw.h
@@ -15,9 +15,9 @@
#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus)
-#define MRB_TRY(buf) do { try {
+#define MRB_TRY(buf) try {
#define MRB_CATCH(buf) } catch(mrb_jmpbuf_impl e) { if (e != (buf)->impl) { throw e; }
-#define MRB_END_EXC(buf) } } while(0)
+#define MRB_END_EXC(buf) }
#define MRB_THROW(buf) throw((buf)->impl)
typedef mrb_int mrb_jmpbuf_impl;
@@ -34,9 +34,9 @@ typedef mrb_int mrb_jmpbuf_impl;
#define MRB_LONGJMP longjmp
#endif
-#define MRB_TRY(buf) do { if (MRB_SETJMP((buf)->impl) == 0) {
+#define MRB_TRY(buf) if (MRB_SETJMP((buf)->impl) == 0) {
#define MRB_CATCH(buf) } else {
-#define MRB_END_EXC(buf) } } while(0)
+#define MRB_END_EXC(buf) }
#define MRB_THROW(buf) MRB_LONGJMP((buf)->impl, 1);
#define mrb_jmpbuf_impl jmp_buf
diff --git a/include/mruby/value.h b/include/mruby/value.h
index f988826ca..be3dd397f 100644
--- a/include/mruby/value.h
+++ b/include/mruby/value.h
@@ -73,9 +73,6 @@ MRB_API double mrb_float_read(const char*, char**);
#endif
#if defined _MSC_VER && _MSC_VER < 1900
-# ifndef __cplusplus
-# define inline __inline
-# 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, ...);
@@ -157,9 +154,19 @@ typedef void mrb_value;
#ifndef mrb_nil_p
#define mrb_nil_p(o) (mrb_type(o) == MRB_TT_FALSE && !mrb_fixnum(o))
#endif
+#ifndef mrb_false_p
+#define mrb_false_p(o) (mrb_type(o) == MRB_TT_FALSE && !!mrb_fixnum(o))
+#endif
+#ifndef mrb_true_p
+#define mrb_true_p(o) (mrb_type(o) == MRB_TT_TRUE)
+#endif
#ifndef mrb_bool
#define mrb_bool(o) (mrb_type(o) != MRB_TT_FALSE)
#endif
+#if !defined(MRB_SYMBOL_BITSIZE)
+#define MRB_SYMBOL_BITSIZE (sizeof(mrb_sym) * CHAR_BIT)
+#define MRB_SYMBOL_MAX UINT32_MAX
+#endif
#ifndef MRB_WITHOUT_FLOAT
#define mrb_float_p(o) (mrb_type(o) == MRB_TT_FLOAT)
#endif
@@ -170,7 +177,6 @@ typedef void mrb_value;
#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_API mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value);
/*
* Returns a float in Ruby.
@@ -185,7 +191,7 @@ MRB_INLINE mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f)
}
#endif
-static inline mrb_value
+MRB_INLINE mrb_value
mrb_cptr_value(struct mrb_state *mrb, void *p)
{
mrb_value v;
@@ -204,7 +210,7 @@ MRB_INLINE mrb_value mrb_fixnum_value(mrb_int i)
return v;
}
-static inline mrb_value
+MRB_INLINE mrb_value
mrb_symbol_value(mrb_sym i)
{
mrb_value v;
@@ -212,7 +218,7 @@ mrb_symbol_value(mrb_sym i)
return v;
}
-static inline mrb_value
+MRB_INLINE mrb_value
mrb_obj_value(void *p)
{
mrb_value v;
@@ -256,7 +262,7 @@ MRB_INLINE mrb_value mrb_true_value(void)
return v;
}
-static inline mrb_value
+MRB_INLINE mrb_value
mrb_bool_value(mrb_bool boolean)
{
mrb_value v;
@@ -264,7 +270,7 @@ mrb_bool_value(mrb_bool boolean)
return v;
}
-static inline mrb_value
+MRB_INLINE mrb_value
mrb_undef_value(void)
{
mrb_value v;
@@ -272,7 +278,10 @@ mrb_undef_value(void)
return v;
}
-#ifdef MRB_USE_ETEXT_EDATA
+#if defined(MRB_USE_CUSTOM_RO_DATA_P)
+/* If you define `MRB_USE_CUSTOM_RO_DATA_P`, you must implement `mrb_ro_data_p()`. */
+mrb_bool mrb_ro_data_p(const char *p);
+#elif defined(MRB_USE_ETEXT_EDATA)
#if (defined(__APPLE__) && defined(__MACH__))
#include <mach-o/getsect.h>
static inline mrb_bool
diff --git a/include/mruby/variable.h b/include/mruby/variable.h
index 5fef83faf..ff01e5cc8 100644
--- a/include/mruby/variable.h
+++ b/include/mruby/variable.h
@@ -31,8 +31,6 @@ struct global_entry {
mrb_value mrb_vm_special_get(mrb_state*, mrb_sym);
void mrb_vm_special_set(mrb_state*, mrb_sym, mrb_value);
-mrb_value mrb_vm_iv_get(mrb_state*, mrb_sym);
-void mrb_vm_iv_set(mrb_state*, mrb_sym, mrb_value);
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);
@@ -42,8 +40,8 @@ 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_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_bool mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym sym);
+MRB_API void mrb_iv_name_sym_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);
@@ -119,12 +117,14 @@ MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass * c, mrb_sym sym, mrb_
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*);
+void mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
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_obj_instance_variables(mrb_state*, mrb_value);
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_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym);
+mrb_bool mrb_ident_p(const char *s, mrb_int len);
/* GC functions */
void mrb_gc_mark_gv(mrb_state*);
@@ -133,6 +133,10 @@ 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*);
+/* return non zero to break the loop */
+typedef int (mrb_iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*);
+MRB_API void mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p);
+
MRB_END_DECL
#endif /* MRUBY_VARIABLE_H */
diff --git a/include/mruby/version.h b/include/mruby/version.h
index 43d6461ea..206078df0 100644
--- a/include/mruby/version.h
+++ b/include/mruby/version.h
@@ -27,7 +27,7 @@ MRB_BEGIN_DECL
/*
* The version of Ruby used by mruby.
*/
-#define MRUBY_RUBY_VERSION "1.9"
+#define MRUBY_RUBY_VERSION "2.0"
/*
* Ruby engine.
@@ -37,12 +37,12 @@ MRB_BEGIN_DECL
/*
* Major release version number.
*/
-#define MRUBY_RELEASE_MAJOR 1
+#define MRUBY_RELEASE_MAJOR 2
/*
* Minor release version number.
*/
-#define MRUBY_RELEASE_MINOR 4
+#define MRUBY_RELEASE_MINOR 0
/*
* Tiny release version number.
@@ -62,7 +62,7 @@ MRB_BEGIN_DECL
/*
* Release year.
*/
-#define MRUBY_RELEASE_YEAR 2018
+#define MRUBY_RELEASE_YEAR 2019
/*
* Release month.
@@ -72,12 +72,26 @@ MRB_BEGIN_DECL
/*
* Release day.
*/
-#define MRUBY_RELEASE_DAY 27
+#define MRUBY_RELEASE_DAY 4
/*
* Release date as a string.
*/
-#define MRUBY_RELEASE_DATE MRB_STRINGIZE(MRUBY_RELEASE_YEAR) "-" MRB_STRINGIZE(MRUBY_RELEASE_MONTH) "-" MRB_STRINGIZE(MRUBY_RELEASE_DAY)
+#define MRUBY_RELEASE_DATE \
+ MRUBY_RELEASE_YEAR_STR "-" \
+ MRUBY_RELEASE_MONTH_STR "-" \
+ MRUBY_RELEASE_DAY_STR
+#define MRUBY_RELEASE_YEAR_STR MRB_STRINGIZE(MRUBY_RELEASE_YEAR)
+#if MRUBY_RELEASE_MONTH < 10
+#define MRUBY_RELEASE_MONTH_STR "0" MRB_STRINGIZE(MRUBY_RELEASE_MONTH)
+#else
+#define MRUBY_RELEASE_MONTH_STR MRB_STRINGIZE(MRUBY_RELEASE_MONTH)
+#endif
+#if MRUBY_RELEASE_DAY < 10
+#define MRUBY_RELEASE_DAY_STR "0" MRB_STRINGIZE(MRUBY_RELEASE_DAY)
+#else
+#define MRUBY_RELEASE_DAY_STR MRB_STRINGIZE(MRUBY_RELEASE_DAY)
+#endif
/*
* The year mruby was first created.
diff --git a/lib/mruby-core-ext.rb b/lib/mruby-core-ext.rb
index 4c6d3ca76..08e6f6148 100644
--- a/lib/mruby-core-ext.rb
+++ b/lib/mruby-core-ext.rb
@@ -18,18 +18,20 @@ class String
end
# Compatible with 1.9 on 1.8
- def %(params)
- if params.is_a?(Hash)
- str = self.clone
- params.each do |k, v|
- str.gsub!("%{#{k}}") { v }
- end
- str
- else
- if params.is_a?(Array)
- sprintf(self, *params)
+ unless (sprintf("%{a}", :a => 1) rescue false)
+ def %(params)
+ if params.is_a?(Hash)
+ str = self.clone
+ params.each do |k, v|
+ str.gsub!("%{#{k}}") { v }
+ end
+ str
else
- sprintf(self, params)
+ if params.is_a?(Array)
+ sprintf(self, *params)
+ else
+ sprintf(self, params)
+ end
end
end
end
@@ -37,17 +39,21 @@ end
class Symbol
# Compatible with 1.9 on 1.8
- def to_proc
- proc { |obj, *args| obj.send(self, *args) }
+ unless method_defined?(:to_proc)
+ def to_proc
+ proc { |obj, *args| obj.send(self, *args) }
+ end
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
+ unless method_defined?(:each_with_object)
+ def each_with_object(memo)
+ return to_enum :each_with_object, memo unless block_given?
+ each { |obj| yield obj, memo }
+ memo
+ end
end
end
diff --git a/lib/mruby/build.rb b/lib/mruby/build.rb
index 57bd9c51e..d9d52948b 100644
--- a/lib/mruby/build.rb
+++ b/lib/mruby/build.rb
@@ -22,7 +22,6 @@ module MRuby
def initialize(name, &block)
@name, @initializer = name.to_s, block
- MRuby::Toolchain.toolchains ||= {}
MRuby::Toolchain.toolchains[@name] = self
end
@@ -30,13 +29,8 @@ module MRuby
conf.instance_exec(conf, params, &@initializer)
end
- def self.load
- Dir.glob("#{MRUBY_ROOT}/tasks/toolchains/*.rake").each do |file|
- Kernel.load file
- end
- end
+ self.toolchains = {}
end
- Toolchain.load
class Build
class << self
@@ -45,9 +39,11 @@ module MRuby
include Rake::DSL
include LoadGems
attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir
- attr_reader :libmruby, :gems, :toolchains
+ attr_reader :libmruby_objs, :gems, :toolchains
attr_writer :enable_bintest, :enable_test
+ alias libmruby libmruby_objs
+
COMPILERS = %w(cc cxx objc asm)
COMMANDS = COMPILERS + %w(linker archiver yacc gperf git exts mrbc)
attr_block MRuby::Build::COMMANDS
@@ -81,7 +77,7 @@ module MRuby
@mrbc = Command::Mrbc.new(self)
@bins = []
- @gems, @libmruby = MRuby::Gem::List.new, []
+ @gems, @libmruby_objs = MRuby::Gem::List.new, []
@build_mrbtest_lib_only = false
@cxx_exception_enabled = false
@cxx_exception_disabled = false
@@ -100,6 +96,10 @@ module MRuby
build_mrbtest if test_enabled?
end
+ def debug_enabled?
+ @enable_debug
+ end
+
def enable_debug
compilers.each do |c|
c.defines += %w(MRB_DEBUG)
@@ -108,6 +108,8 @@ module MRuby
end
end
@mrbc.compile_options += ' -g'
+
+ @enable_debug = true
end
def disable_cxx_exception
@@ -154,8 +156,6 @@ module MRuby
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|
@@ -167,7 +167,7 @@ module MRuby
#ifndef MRB_ENABLE_CXX_ABI
extern "C" {
#endif
-#include "#{src}"
+#include "#{File.absolute_path src}"
#ifndef MRB_ENABLE_CXX_ABI
}
#endif
@@ -190,10 +190,15 @@ EOS
end
def toolchain(name, params={})
- tc = Toolchain.toolchains[name.to_s]
- fail "Unknown #{name} toolchain" unless tc
+ name = name.to_s
+ tc = Toolchain.toolchains[name] || begin
+ path = "#{MRUBY_ROOT}/tasks/toolchains/#{name}.rake"
+ fail "Unknown #{name} toolchain" unless File.exist?(path)
+ load path
+ Toolchain.toolchains[name]
+ end
tc.setup(self, params)
- @toolchains.unshift name.to_s
+ @toolchains.unshift name
end
def primary_toolchain
@@ -249,7 +254,7 @@ EOS
if name.is_a?(Array)
name.flatten.map { |n| filename(n) }
else
- '"%s"' % name.gsub('/', file_separator)
+ name.gsub('/', file_separator)
end
end
@@ -257,15 +262,18 @@ EOS
if name.is_a?(Array)
name.flatten.map { |n| cygwin_filename(n) }
else
- '"%s"' % `cygpath -w "#{filename(name)}"`.strip
+ `cygpath -w "#{filename(name)}"`.strip
end
end
def exefile(name)
if name.is_a?(Array)
name.flatten.map { |n| exefile(n) }
- else
+ elsif File.extname(name).empty?
"#{name}#{exts.executable}"
+ else
+ # `name` sometimes have (non-standard) extension (e.g. `.bat`).
+ name
end
end
@@ -293,18 +301,22 @@ EOS
@build_mrbtest_lib_only
end
+ def verbose_flag
+ $verbose ? ' -v' : ''
+ end
+
def run_test
puts ">>> Test #{name} <<<"
mrbtest = exefile("#{build_dir}/bin/mrbtest")
- sh "#{filename mrbtest.relative_path}#{$verbose ? ' -v' : ''}"
+ sh "#{filename mrbtest.relative_path}#{verbose_flag}"
puts
- run_bintest if bintest_enabled?
end
def run_bintest
+ puts ">>> Bintest #{name} <<<"
targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir }
targets << filename(".") if File.directory? "./bintest"
- sh "ruby test/bintest.rb #{targets.join ' '}"
+ sh "ruby test/bintest.rb#{verbose_flag} #{targets.join ' '}"
end
def print_build_summary
@@ -324,6 +336,18 @@ EOS
puts "================================================"
puts
end
+
+ def libmruby_static
+ libfile("#{build_dir}/lib/libmruby")
+ end
+
+ def libmruby_core_static
+ libfile("#{build_dir}/lib/libmruby_core")
+ end
+
+ def libraries
+ [libmruby_static]
+ end
end # Build
class CrossBuild < Build
diff --git a/lib/mruby/build/command.rb b/lib/mruby/build/command.rb
index 694b4a24c..d4354225e 100644
--- a/lib/mruby/build/command.rb
+++ b/lib/mruby/build/command.rb
@@ -67,8 +67,8 @@ module MRuby
path && build.filename("#{path}/#{name}").sub(/^"(.*)"$/, '\1')
end
- def all_flags(_defineds=[], _include_paths=[], _flags=[])
- define_flags = [defines, _defineds].flatten.map{ |d| option_define % d }
+ def all_flags(_defines=[], _include_paths=[], _flags=[])
+ define_flags = [defines, _defines].flatten.map{ |d| option_define % d }
include_path_flags = [include_paths, _include_paths].flatten.map do |f|
if MRUBY_BUILD_HOST_IS_CYGWIN
option_include_path % cygwin_filename(f)
@@ -79,14 +79,14 @@ module MRuby
[flags, define_flags, include_path_flags, _flags].flatten.join(' ')
end
- def run(outfile, infile, _defineds=[], _include_paths=[], _flags=[])
+ def run(outfile, infile, _defines=[], _include_paths=[], _flags=[])
FileUtils.mkdir_p File.dirname(outfile)
_pp "CC", infile.relative_path, outfile.relative_path
if MRUBY_BUILD_HOST_IS_CYGWIN
- _run compile_options, { :flags => all_flags(_defineds, _include_paths, _flags),
+ _run compile_options, { :flags => all_flags(_defines, _include_paths, _flags),
:infile => cygwin_filename(infile), :outfile => cygwin_filename(outfile) }
else
- _run compile_options, { :flags => all_flags(_defineds, _include_paths, _flags),
+ _run compile_options, { :flags => all_flags(_defines, _include_paths, _flags),
:infile => filename(infile), :outfile => filename(outfile) }
end
end
@@ -127,13 +127,40 @@ module MRuby
end
private
+
+ #
+ # === Example of +.d+ file
+ #
+ # ==== Without <tt>-MP</tt> compiler flag
+ #
+ # /build/host/src/array.o: \
+ # /src/array.c \
+ # /include/mruby/common.h \
+ # /include/mruby/value.h \
+ # /src/value_array.h
+ #
+ # ==== With <tt>-MP</tt> compiler flag
+ #
+ # /build/host/src/array.o: \
+ # /src/array.c \
+ # /include/mruby/common.h \
+ # /include/mruby/value.h \
+ # /src/value_array.h
+ #
+ # /include/mruby/common.h:
+ #
+ # /include/mruby/value.h:
+ #
+ # /src/value_array.h:
+ #
def get_dependencies(file)
file = file.ext('d') unless File.extname(file) == '.d'
+ deps = []
if File.exist?(file)
- File.read(file).gsub("\\\n ", "").scan(/^\S+:\s+(.+)$/).flatten.map {|s| s.split(' ') }.flatten
- else
- []
- end + [ MRUBY_CONFIG ]
+ File.foreach(file){|line| deps << $1 if /^ +(.*?)(?: *\\)?$/ =~ line}
+ deps.uniq!
+ end
+ deps << MRUBY_CONFIG
end
end
diff --git a/lib/mruby/gem.rb b/lib/mruby/gem.rb
index a83ca639f..95c1d4bc3 100644
--- a/lib/mruby/gem.rb
+++ b/lib/mruby/gem.rb
@@ -52,6 +52,8 @@ module MRuby
end
def setup
+ return if defined?(@linker) # return if already set up
+
MRuby::Gem.current = self
MRuby::Build::COMMANDS.each do |command|
instance_variable_set("@#{command}", @build.send(command).clone)
@@ -63,7 +65,7 @@ module MRuby
objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X"))
end
- @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb")
+ @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb").sort
@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
@@ -87,7 +89,7 @@ module MRuby
fail "#{name || dir} required to set name, license(s) and author(s)"
end
- build.libmruby << @objs
+ build.libmruby_objs << @objs
instance_eval(&@build_config_initializer) if @build_config_initializer
end
@@ -110,17 +112,13 @@ module MRuby
end
def add_test_dependency(*args)
- add_dependency(*args) if build.test_enabled?
+ add_dependency(*args) if build.test_enabled? || build.bintest_enabled?
end
def add_conflict(name, *req)
@conflicts << {:gem => name, :requirements => req.empty? ? nil : req}
end
- def self.bin=(bin)
- @bins = [bin].flatten
- end
-
def build_dir
"#{build.build_dir}/mrbgems/#{name}"
end
@@ -269,16 +267,18 @@ module MRuby
# ~> compare algorithm
#
# Example:
+ # ~> 2 means >= 2.0.0 and < 3.0.0
# ~> 2.2 means >= 2.2.0 and < 3.0.0
- # ~> 2.2.0 means >= 2.2.0 and < 2.3.0
+ # ~> 2.2.2 means >= 2.2.2 and < 2.3.0
def twiddle_wakka_ok?(other)
gr_or_eql = (self <=> other) >= 0
- still_minor = (self <=> other.skip_minor) < 0
- gr_or_eql and still_minor
+ still_major_or_minor = (self <=> other.skip_major_or_minor) < 0
+ gr_or_eql and still_major_or_minor
end
- def skip_minor
+ def skip_major_or_minor
a = @ary.dup
+ a << 0 if a.size == 1 # ~> 2 can also be represented as ~> 2.0
a.slice!(-1)
a[-1] = a[-1].succ
a
@@ -334,26 +334,26 @@ module MRuby
end
def generate_gem_table build
- gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
+ gem_table = each_with_object({}) { |spec, h| h[spec.name] = spec }
- default_gems = []
+ default_gems = {}
each do |g|
g.dependencies.each do |dep|
- default_gems << default_gem_params(dep) unless gem_table.key? dep[:gem]
+ default_gems[dep[:gem]] ||= default_gem_params(dep)
end
end
until default_gems.empty?
- def_gem = default_gems.pop
+ def_name, def_gem = default_gems.shift
+ next if gem_table[def_name]
- spec = build.gem def_gem[:default]
- fail "Invalid gem name: #{spec.name} (Expected: #{def_gem[:gem]})" if spec.name != def_gem[:gem]
+ spec = gem_table[def_name] = build.gem(def_gem[:default])
+ fail "Invalid gem name: #{spec.name} (Expected: #{def_name})" if spec.name != def_name
spec.setup
spec.dependencies.each do |dep|
- default_gems << default_gem_params(dep) unless gem_table.key? dep[:gem]
+ default_gems[dep[:gem]] ||= default_gem_params(dep)
end
- gem_table[spec.name] = spec
end
each do |g|
@@ -428,7 +428,8 @@ module MRuby
end
def import_include_paths(g)
- gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
+ gem_table = each_with_object({}) { |spec, h| h[spec.name] = spec }
+
g.dependencies.each do |dep|
dep_g = gem_table[dep[:gem]]
# We can do recursive call safely
@@ -454,5 +455,6 @@ module MRuby
def new(&block); block.call(self); end
def config=(obj); @config = obj; end
def gem(gemdir, &block); @config.gem(gemdir, &block); end
+ def gembox(gemfile); @config.gembox(gemfile); end
end # GemBox
end # MRuby
diff --git a/minirake b/minirake
index 542c37a79..cd2d85854 100755
--- a/minirake
+++ b/minirake
@@ -7,6 +7,10 @@
require 'getoptlong'
require 'fileutils'
+$rake_fiber_table = {}
+$rake_jobs = 1
+$rake_failed = []
+
class String
def ext(newext='')
return self.dup if ['.', '..'].include? self
@@ -86,14 +90,36 @@ module MiniRake
@name.to_s
end
- # Invoke the task if it is needed. Prerequites are invoked first.
+ def done?; @done end
+ def running?; @running end
+
+ # Invoke the task if it is needed. Prerequisites are invoked first.
def invoke
puts "Invoke #{name} (already=[#{@already_invoked}], needed=[#{needed?}])" if $trace
return if @already_invoked
- @already_invoked = true
prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten
- prerequisites.each { |n| Task[n].invoke }
- execute if needed?
+ prerequisites.each do |n|
+ t = Task[n]
+ unless t.done?
+ return prerequisites.select{|v| v = Task[v]; v && (!v.done? || !v.running?) }
+ end
+ end
+
+ @already_invoked = true
+
+ if needed?
+ @running = true
+ if $rake_root_fiber
+ return Fiber.new do
+ self.execute
+ $rake_root_fiber.transfer
+ end
+ else
+ self.execute
+ end
+ end
+
+ @done = true
end
# Execute the actions associated with this task.
@@ -103,6 +129,8 @@ module MiniRake
unless $dryrun
@actions.each { |act| act.call(self) }
end
+ @done = true
+ @running = false
end
# Is this task needed?
@@ -281,7 +309,19 @@ module MiniRake
# Run the system command +cmd+.
def sh(cmd)
puts cmd if $verbose
- system(cmd) or fail "Command Failed: [#{cmd}]"
+
+ if !$rake_root_fiber || Fiber.current == $rake_root_fiber
+ system(cmd) or fail "Command Failed: [#{cmd}]"
+ return
+ end
+
+ pid = Process.spawn(cmd)
+ $rake_fiber_table[pid] = {
+ fiber: Fiber.current,
+ command: cmd,
+ process_waiter: Process.detach(pid)
+ }
+ $rake_root_fiber.transfer
end
def desc(text)
@@ -329,7 +369,9 @@ class RakeApp
['--verbose', '-v', GetoptLong::NO_ARGUMENT,
"Log message to standard output."],
['--directory', '-C', GetoptLong::REQUIRED_ARGUMENT,
- "Change executing directory of rakefiles."]
+ "Change executing directory of rakefiles."],
+ ['--jobs', '-j', GetoptLong::REQUIRED_ARGUMENT,
+ 'Execute rake with parallel jobs.']
]
# Create a RakeApp object.
@@ -422,6 +464,8 @@ class RakeApp
exit
when '--directory'
Dir.chdir value
+ when '--jobs'
+ $rake_jobs = [value.to_i, 1].max
else
fail "Unknown option: #{opt}"
end
@@ -438,6 +482,12 @@ class RakeApp
# Run the +rake+ application.
def run
handle_options
+
+ unless $rake_root_fiber
+ require 'fiber'
+ $rake_root_fiber = Fiber.current
+ end
+
begin
here = Dir.pwd
while ! have_rakefile
@@ -447,12 +497,12 @@ class RakeApp
end
here = Dir.pwd
end
- tasks = []
+ root_tasks = []
ARGV.each do |task_name|
if /^(\w+)=(.*)/.match(task_name)
ENV[$1] = $2
else
- tasks << task_name
+ root_tasks << task_name
end
end
puts "(in #{Dir.pwd})"
@@ -461,22 +511,93 @@ class RakeApp
if $show_tasks
display_tasks
else
- tasks.push("default") if tasks.size == 0
- tasks.each do |task_name|
- MiniRake::Task[task_name].invoke
+ root_tasks.push("default") if root_tasks.empty?
+ # revese tasks for popping
+ root_tasks.reverse!
+
+ tasks = []
+ until root_tasks.empty?
+ root_name = root_tasks.pop
+ tasks << root_name
+ until tasks.empty?
+ task_name = tasks.pop
+ t = MiniRake::Task[task_name]
+ f = t.invoke
+
+ # append additional tasks to task queue
+ if f.kind_of?(Array)
+ tasks.push(*f)
+ tasks.uniq!
+ end
+
+ unless f.kind_of? Fiber
+ tasks.insert 0, task_name unless t.done?
+ if root_name == task_name
+ wait_process
+ end
+ next
+ end
+
+ wait_process while $rake_fiber_table.size >= $rake_jobs
+
+ f.transfer
+ end
end
+
+ wait_process until $rake_fiber_table.empty?
+ end
+ rescue Exception => e
+ begin
+ $rake_failed << e
+ wait_process until $rake_fiber_table.empty?
+ rescue Exception => next_e
+ e = next_e
+ retry
end
- rescue Exception => ex
- puts "rake aborted!"
+ end
+
+ return if $rake_failed.empty?
+
+ puts "rake aborted!"
+ $rake_failed.each do |ex|
puts ex.message
- if $trace
+ if $trace || $verbose
puts ex.backtrace.join("\n")
else
puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
end
- exit 1
end
+ exit 1
end
+
+ def wait_process(count = 0)
+ dur = [0.0001 * (10 ** count), 1].min
+ sleep dur
+
+ exited = []
+ $rake_fiber_table.each do |pid, v|
+ exited << pid unless v[:process_waiter].alive?
+ end
+
+ exited.each do |pid|
+ ent = $rake_fiber_table.delete pid
+ st = ent[:process_waiter].value
+
+ # ignore process that isn't created by `sh` method
+ return if ent.nil?
+
+ if st.exitstatus != 0
+ raise "Command Failed: [#{ent[:command]}]"
+ end
+
+ fail 'task scheduling bug!' if $rake_fiber_table.size >= $rake_jobs
+
+ ent[:fiber].transfer
+ end
+
+ wait_process(count + 1) if !$rake_fiber_table.empty? && exited.empty?
+ end
+
end
if __FILE__ == $0 then
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox
index 7ddbb16d1..9859c7d52 100644
--- a/mrbgems/default.gembox
+++ b/mrbgems/default.gembox
@@ -1,4 +1,7 @@
MRuby::GemBox.new do |conf|
+ # Meta-programming features
+ conf.gem :core => "mruby-metaprog"
+
# Use standard IO/File class
conf.gem :core => "mruby-io"
@@ -83,6 +86,9 @@ MRuby::GemBox.new do |conf|
# Use class/module extension
conf.gem :core => "mruby-class-ext"
+ # Use Method/UnboundMethod class
+ conf.gem :core => "mruby-method"
+
# Use mruby-compiler to build other mrbgems
conf.gem :core => "mruby-compiler"
end
diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb
index c0995bb99..59b6087d2 100644
--- a/mrbgems/mruby-array-ext/mrblib/array.rb
+++ b/mrbgems/mruby-array-ext/mrblib/array.rb
@@ -1,31 +1,6 @@
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
#
@@ -41,23 +16,19 @@ class Array
# c.uniq! { |s| s.first } # => [["student", "sam"], ["teacher", "matz"]]
#
def uniq!(&block)
- ary = self.dup
- result = []
+ hash = {}
if block
- hash = {}
- while ary.size > 0
- val = ary.shift
+ self.each do |val|
key = block.call(val)
- hash[key] = val unless hash.has_key?(key)
- end
- hash.each_value do |value|
- result << value
+ hash[key] = val unless hash.key?(key)
end
+ result = hash.values
else
- while ary.size > 0
- result << ary.shift
- ary.delete(result.last)
+ hash = {}
+ self.each do |val|
+ hash[val] = val
end
+ result = hash.keys
end
if result.size == self.size
nil
@@ -136,6 +107,25 @@ class Array
##
# call-seq:
+ # ary.union(other_ary,...) -> new_ary
+ #
+ # Set Union---Returns a new array by joining this array with
+ # <i>other_ary</i>, removing duplicates.
+ #
+ # ["a", "b", "c"].union(["c", "d", "a"], ["a", "c", "e"])
+ # #=> ["a", "b", "c", "d", "e"]
+ #
+ def union(*args)
+ ary = self.dup
+ args.each do |x|
+ ary.concat(x)
+ ary.uniq!
+ end
+ ary
+ end
+
+ ##
+ # call-seq:
# ary & other_ary -> new_ary
#
# Set Intersection---Returns a new array
@@ -276,7 +266,6 @@ class Array
self
end
- NONE=Object.new
##
# call-seq:
# ary.fetch(index) -> obj
@@ -301,7 +290,7 @@ class Array
# #=> "100 is out of bounds"
#
- def fetch(n=nil, ifnone=NONE, &block)
+ def fetch(n, ifnone=NONE, &block)
warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block
idx = n
@@ -783,16 +772,6 @@ class Array
end
##
- # call-seq:
- # ary.to_ary -> ary
- #
- # Returns +self+.
- #
- def to_ary
- self
- end
-
- ##
# call-seq:
# ary.dig(idx, ...) -> object
#
@@ -911,7 +890,7 @@ class Array
#
# Assumes that self is an array of arrays and transposes the rows and columns.
#
- # If the length of the subarrays don’t match, an IndexError is raised.
+ # If the length of the subarrays don't match, an IndexError is raised.
#
# Examples:
#
@@ -932,4 +911,32 @@ class Array
self.map { |row| row[column_index] }
end
end
+
+ ##
+ # call-seq:
+ # ary.to_h -> Hash
+ # ary.to_h{|item| ... } -> Hash
+ #
+ # Returns the result of interpreting <i>aray</i> as an array of
+ # <tt>[key, value]</tt> pairs. If a block is given, it should
+ # return <tt>[key, value]</tt> pairs to construct a hash.
+ #
+ # [[:foo, :bar], [1, 2]].to_h
+ # # => {:foo => :bar, 1 => 2}
+ # [1, 2].to_h{|x| [x, x*2]}
+ # # => {1 => 2, 2 => 4}
+ #
+ def to_h(&blk)
+ h = {}
+ self.each do |v|
+ v = blk.call(v) if blk
+ raise TypeError, "wrong element type #{v.class}" unless Array === v
+ raise ArgumentError, "wrong array length (expected 2, was #{v.length})" unless v.length == 2
+ h[v[0]] = v[1]
+ end
+ h
+ end
+
+ alias append push
+ alias prepend unshift
end
diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c
index 169f968f9..cb4798d49 100644
--- a/mrbgems/mruby-array-ext/src/array.c
+++ b/mrbgems/mruby-array-ext/src/array.c
@@ -106,49 +106,6 @@ mrb_ary_values_at(mrb_state *mrb, mrb_value self)
return mrb_get_values_at(mrb, self, RARRAY_LEN(self), argc, argv, mrb_ary_ref);
}
-/*
- * call-seq:
- * ary.to_h -> Hash
- *
- * 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}
- *
- */
-
-static mrb_value
-mrb_ary_to_h(mrb_state *mrb, mrb_value ary)
-{
- mrb_int i;
- mrb_value v, hash;
-
- hash = mrb_hash_new_capa(mrb, 0);
-
- for (i = 0; i < RARRAY_LEN(ary); ++i) {
- mrb_value elt = RARRAY_PTR(ary)[i];
- v = mrb_check_array_type(mrb, elt);
-
- if (mrb_nil_p(v)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)",
- mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, elt)),
- mrb_fixnum_value(i)
- );
- }
-
- if (RARRAY_LEN(v) != 2) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong array length at %S (expected 2, was %S)",
- mrb_fixnum_value(i),
- mrb_fixnum_value(RARRAY_LEN(v))
- );
- }
-
- mrb_hash_set(mrb, hash, RARRAY_PTR(v)[0], RARRAY_PTR(v)[1]);
- }
-
- return hash;
-}
/*
* call-seq:
@@ -175,7 +132,7 @@ static mrb_value
mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_int i, j, k, len, alen = ARY_LEN(a);
+ mrb_int i, j, k, len, alen;
mrb_value val;
mrb_value *ptr;
mrb_value ary;
@@ -188,7 +145,7 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
mrb_get_args(mrb, "o|i", &index, &len);
switch (mrb_type(index)) {
case MRB_TT_RANGE:
- if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
+ if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) {
goto delete_pos_len;
}
else {
@@ -205,6 +162,7 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self)
mrb_get_args(mrb, "ii", &i, &len);
delete_pos_len:
+ alen = ARY_LEN(a);
if (i < 0) i += alen;
if (i < 0 || alen < i) return mrb_nil_value();
if (len < 0) return mrb_nil_value();
@@ -236,8 +194,7 @@ mrb_mruby_array_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, a, "at", mrb_ary_at, MRB_ARGS_REQ(1));
mrb_define_method(mrb, a, "rassoc", mrb_ary_rassoc, MRB_ARGS_REQ(1));
mrb_define_method(mrb, a, "values_at", mrb_ary_values_at, MRB_ARGS_ANY());
- mrb_define_method(mrb, a, "to_h", mrb_ary_to_h, MRB_ARGS_REQ(0));
- mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ANY());
+ mrb_define_method(mrb, a, "slice!", mrb_ary_slice_bang, MRB_ARGS_ARG(1,1));
}
void
diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb
index 7d810acc2..82f09297a 100644
--- a/mrbgems/mruby-array-ext/test/array.rb
+++ b/mrbgems/mruby-array-ext/test/array.rb
@@ -1,13 +1,6 @@
##
# 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" ]
@@ -75,6 +68,14 @@ assert("Array#|") do
assert_equal [1, 2, 3, 1], a
end
+assert("Array#union") do
+ a = [1, 2, 3, 1]
+ b = [1, 4]
+ c = [1, 5]
+
+ assert_equal [1, 2, 3, 4, 5], a.union(b,c)
+end
+
assert("Array#&") do
a = [1, 2, 3, 1]
b = [1, 4]
@@ -267,9 +268,9 @@ assert("Array#bsearch") do
end
end
-assert("Array#bsearch_index") do
- # tested through Array#bsearch
-end
+# tested through Array#bsearch
+#assert("Array#bsearch_index") do
+#end
assert("Array#delete_if") do
a = [1, 2, 3, 4, 5]
@@ -330,27 +331,11 @@ assert('Array#to_h') do
assert_raise(ArgumentError) { [[1]].to_h }
end
-assert('Array#to_h (Modified)') do
- class A
- def to_ary
- $a.clear
- nil
- end
- end
- $a = [A.new]
- assert_raise(TypeError) { $a.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
-
assert("Array#dig") do
h = [[[1]], 0]
assert_equal(1, h.dig(0, 0, 0))
@@ -418,5 +403,5 @@ assert('Array#transpose') do
assert_equal([[1], [2], [3]].transpose, [[1,2,3]])
assert_equal([[1,2], [3,4], [5,6]].transpose, [[1,3,5], [2,4,6]])
assert_raise(TypeError) { [1].transpose }
- assert_raise(IndexError) { [[1], [2,3,4]].transpose }
+ assert_raise(IndexError) { [[1], [2,3,4]].transpose }
end
diff --git a/mrbgems/mruby-bin-config/mrbgem.rake b/mrbgems/mruby-bin-config/mrbgem.rake
new file mode 100644
index 000000000..3a0a1b897
--- /dev/null
+++ b/mrbgems/mruby-bin-config/mrbgem.rake
@@ -0,0 +1,23 @@
+unless MRuby::Build.current.kind_of?(MRuby::CrossBuild)
+ MRuby::Gem::Specification.new('mruby-bin-config') do |spec|
+ name = 'mruby-config'
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = "#{name} command"
+
+ mruby_config = name + (ENV['OS'] == 'Windows_NT' ? '.bat' : '')
+ mruby_config_path = "#{build.build_dir}/bin/#{mruby_config}"
+ make_cfg = "#{build.build_dir}/lib/libmruby.flags.mak"
+ tmplt_path = "#{__dir__}/#{mruby_config}"
+ build.bins << mruby_config
+
+ file mruby_config_path => [make_cfg, tmplt_path] do |t|
+ config = Hash[File.readlines(make_cfg).map!(&:chomp).map! {|l|
+ l.gsub('\\"', '"').split(' = ', 2).map! {|s| s.sub(/^(?=.)/, 'echo ')}
+ }]
+ tmplt = File.read(tmplt_path)
+ File.write(t.name, tmplt.gsub(/(#{Regexp.union(*config.keys)})\b/, config))
+ FileUtils.chmod(0755, t.name)
+ end
+ end
+end
diff --git a/mrbgems/mruby-bin-mruby-config/mruby-config b/mrbgems/mruby-bin-config/mruby-config
index 57346c03f..57346c03f 100644
--- a/mrbgems/mruby-bin-mruby-config/mruby-config
+++ b/mrbgems/mruby-bin-config/mruby-config
diff --git a/mrbgems/mruby-bin-mruby-config/mruby-config.bat b/mrbgems/mruby-bin-config/mruby-config.bat
index a1f7bfdd1..a1f7bfdd1 100644
--- a/mrbgems/mruby-bin-mruby-config/mruby-config.bat
+++ b/mrbgems/mruby-bin-config/mruby-config.bat
diff --git a/mrbgems/mruby-bin-debugger/bintest/print.rb b/mrbgems/mruby-bin-debugger/bintest/print.rb
index 0d4aad011..6675392b8 100644
--- a/mrbgems/mruby-bin-debugger/bintest/print.rb
+++ b/mrbgems/mruby-bin-debugger/bintest/print.rb
@@ -317,7 +317,7 @@ TestConstNameSubClass.new.m()
bp = nil
SRC
- # todo: wait for 'break' to be implimented
+ # todo: wait for 'break' to be implemented
tc = []
9.times { tc << {:cmd=>"s"} }
tc << {:cmd=>"p CONST", :exp=>"super class"}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
index d3ccf08ae..513db4ded 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c
@@ -84,7 +84,7 @@ free_breakpoint(mrb_state *mrb, mrb_debug_breakpoint *bp)
}
static uint16_t
-check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
+check_file_lineno(mrb_state *mrb, struct mrb_irep *irep, const char *file, uint16_t lineno)
{
mrb_irep_debug_info_file *info_file;
uint16_t result = 0;
@@ -93,8 +93,10 @@ check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
uint16_t i;
for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
+ const char *filename;
info_file = irep->debug_info->files[f_idx];
- if (!strcmp(info_file->filename, file)) {
+ filename = mrb_sym2name_len(mrb, info_file->filename_sym, NULL);
+ if (!strcmp(filename, file)) {
result = MRB_DEBUG_BP_FILE_OK;
fix_lineno = check_lineno(info_file, lineno);
@@ -103,7 +105,7 @@ check_file_lineno(struct mrb_irep *irep, const char *file, uint16_t lineno)
}
}
for (i=0; i < irep->rlen; ++i) {
- result |= check_file_lineno(irep->reps[i], file, lineno);
+ result |= check_file_lineno(mrb, irep->reps[i], file, lineno);
if (result == (MRB_DEBUG_BP_FILE_OK | MRB_DEBUG_BP_LINENO_OK)) {
return result;
}
@@ -185,7 +187,7 @@ mrb_debug_set_break_line(mrb_state *mrb, mrb_debug_context *dbg, const char *fil
}
/* file and lineno check (line type mrb_debug_line_ary only.) */
- result = check_file_lineno(dbg->root_irep, file, lineno);
+ result = check_file_lineno(mrb, dbg->root_irep, file, lineno);
if (result == 0) {
return MRB_DEBUG_BREAK_INVALID_FILE;
}
@@ -426,10 +428,10 @@ mrb_debug_disable_break_all(mrb_state *mrb, mrb_debug_context *dbg)
}
static mrb_bool
-check_start_pc_for_line(mrb_irep *irep, mrb_code *pc, uint16_t line)
+check_start_pc_for_line(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, uint16_t line)
{
if (pc > irep->iseq) {
- if (line == mrb_debug_get_line(irep, pc - irep->iseq - 1)) {
+ if (line == mrb_debug_get_line(mrb, irep, pc - irep->iseq - 1)) {
return FALSE;
}
}
@@ -447,7 +449,7 @@ mrb_debug_check_breakpoint_line(mrb_state *mrb, mrb_debug_context *dbg, const ch
return MRB_DEBUG_INVALID_ARGUMENT;
}
- if (!check_start_pc_for_line(dbg->irep, dbg->pc, line)) {
+ if (!check_start_pc_for_line(mrb, dbg->irep, dbg->pc, line)) {
return MRB_DEBUG_OK;
}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
index 21fe64127..66ddfa783 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c
@@ -181,7 +181,7 @@ mrb_debug_get_source(mrb_state *mrb, mrdb_state *mrdb, const char *srcpath, cons
else srcname = filename;
search_path[0] = srcpath;
- search_path[1] = dirname(mrb, mrb_debug_get_filename(mrdb->dbg->irep, 0));
+ search_path[1] = dirname(mrb, mrb_debug_get_filename(mrb, mrdb->dbg->irep, 0));
search_path[2] = ".";
for (i = 0; i < 3; i++) {
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
index c8700530f..f888d1430 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
@@ -31,7 +31,7 @@ mrdb_check_syntax(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size
}
mrb_value
-mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc)
+mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t len, mrb_bool *exc, int direct_eval)
{
void (*tmp)(struct mrb_state *, struct mrb_irep *, mrb_code *, mrb_value *);
mrb_value ruby_code;
@@ -48,6 +48,11 @@ mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t
v = mrb_obj_value(mrb->exc);
mrb->exc = 0;
}
+ else if (direct_eval) {
+ recv = dbg->regs[0];
+
+ v = mrb_funcall(mrb, recv, expr, 0);
+ }
else {
/*
* begin
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
index e256f6262..ab8c08869 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.h
@@ -8,6 +8,6 @@
#include <mruby.h>
#include "mrdb.h"
-mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*);
+mrb_value mrb_debug_eval(mrb_state*, mrb_debug_context*, const char*, size_t, mrb_bool*, int);
#endif /* APIPRINT_H_ */
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
index 8e5901754..bc9937e94 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdbreak.c
@@ -242,7 +242,7 @@ info_break_select(mrb_state *mrb, mrdb_state *mrdb)
}
mrb_debug_bptype
-parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
+parse_breakcommand(mrb_state *mrb, mrdb_state *mrdb, const char **file, uint32_t *line, char **cname, char **method)
{
mrb_debug_context *dbg = mrdb->dbg;
char *args;
@@ -274,7 +274,7 @@ parse_breakcommand(mrdb_state *mrdb, const char **file, uint32_t *line, char **c
STRTOUL(l, body);
if (l <= 65535) {
*line = l;
- *file = (body == args)? mrb_debug_get_filename(dbg->irep, dbg->pc - dbg->irep->iseq): args;
+ *file = (body == args)? mrb_debug_get_filename(mrb, dbg->irep, dbg->pc - dbg->irep->iseq): args;
}
else {
puts(BREAK_ERR_MSG_RANGEOVER);
@@ -332,7 +332,7 @@ dbgcmd_break(mrb_state *mrb, mrdb_state *mrdb)
char *method = NULL;
int32_t ret;
- type = parse_breakcommand(mrdb, &file, &line, &cname, &method);
+ type = parse_breakcommand(mrb, mrdb, &file, &line, &cname, &method);
switch (type) {
case MRB_DEBUG_BPTYPE_LINE:
ret = mrb_debug_set_break_line(mrb, dbg, file, line);
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
index 0a864567d..db28e4229 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdmisc.c
@@ -82,6 +82,12 @@ static help_msg help_msg_list[] = {
"Arguments are breakpoint numbers with spaces in between.\n"
},
{
+ "i[nfo]", "l[ocals]", "Print name of local variables",
+ "Usage: info locals\n"
+ "\n"
+ "Print name of local variables.\n"
+ },
+ {
"l[ist]", NULL, "List specified line",
"Usage: list\n"
" list first[,last]\n"
@@ -495,7 +501,7 @@ dbgcmd_quit(mrb_state *mrb, mrdb_state *mrdb)
if (mrdb->dbg->xm == DBG_QUIT) {
struct RClass *exc;
- exc = mrb_define_class(mrb, "DebuggerExit", mrb_class_get(mrb, "Exception"));
+ exc = mrb_define_class(mrb, "DebuggerExit", mrb->eException_class);
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
index cca636711..25f071589 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdprint.c
@@ -36,7 +36,7 @@ dbgcmd_print(mrb_state *mrb, mrdb_state *mrdb)
expr = mrb_str_cat_cstr(mrb, expr, mrdb->words[wcnt]);
}
- result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL);
+ result = mrb_debug_eval(mrb, mrdb->dbg, RSTRING_PTR(expr), RSTRING_LEN(expr), NULL, 0);
/* $print_no = result */
s = mrb_str_cat_lit(mrb, result, "\0");
@@ -56,3 +56,26 @@ dbgcmd_eval(mrb_state *mrb, mrdb_state *mrdb)
{
return dbgcmd_print(mrb, mrdb);
}
+
+dbgcmd_state
+dbgcmd_info_local(mrb_state *mrb, mrdb_state *mrdb)
+{
+ mrb_value result;
+ mrb_value s;
+ int ai;
+
+ ai = mrb_gc_arena_save(mrb);
+
+ result = mrb_debug_eval(mrb, mrdb->dbg, "local_variables", 0, NULL, 1);
+
+ 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;
+}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
index cb4c738fc..233c86cef 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/cmdrun.c
@@ -19,7 +19,7 @@ dbgcmd_run(mrb_state *mrb, mrdb_state *mrdb)
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"));
+ exc = mrb_define_class(mrb, "DebuggerRestart", mrb->eException_class);
mrb_raise(mrb, exc, "Restart mrdb.");
}
}
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
index 0588dfca5..003406172 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c
@@ -52,6 +52,7 @@ static const debug_command debug_command_list[] = {
{"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] */
+ {"info", "locals", 1, 1, 0, DBGCMD_INFO_LOCAL, dbgcmd_info_local}, /* i[nfo] l[ocals] */
{"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] */
@@ -510,6 +511,7 @@ check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value
mrb_sym sym;
int32_t bpno;
mrb_bool isCfunc;
+ struct mrb_insn_data insn;
mrb_debug_context *dbg = mrb_debug_context_get(mrb);
@@ -517,11 +519,12 @@ check_method_breakpoint(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value
bpno = dbg->method_bpno;
dbg->method_bpno = 0;
- switch(GET_OPCODE(*pc)) {
+ insn = mrb_decode_insn(pc);
+ switch(insn.insn) {
case OP_SEND:
case OP_SENDB:
- c = mrb_class(mrb, regs[GETARG_A(*pc)]);
- sym = irep->syms[GETARG_B(*pc)];
+ c = mrb_class(mrb, regs[insn.a]);
+ sym = irep->syms[insn.b];
break;
case OP_SUPER:
c = mrb->c->ci->target_class->super;
@@ -566,8 +569,8 @@ mrb_code_fetch_hook(mrb_state *mrb, mrb_irep *irep, mrb_code *pc, mrb_value *reg
dbg->xphase = DBG_PHASE_RUNNING;
}
- file = mrb_debug_get_filename(irep, pc - irep->iseq);
- line = mrb_debug_get_line(irep, pc - irep->iseq);
+ file = mrb_debug_get_filename(mrb, irep, pc - irep->iseq);
+ line = mrb_debug_get_line(mrb, irep, pc - irep->iseq);
switch (dbg->xm) {
case DBG_STEP:
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
index 5ac12c1cd..7b14a899f 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.h
@@ -23,6 +23,7 @@ typedef enum debug_command_id {
DBGCMD_STEP,
DBGCMD_BREAK,
DBGCMD_INFO_BREAK,
+ DBGCMD_INFO_LOCAL,
DBGCMD_WATCH,
DBGCMD_INFO_WATCH,
DBGCMD_ENABLE,
@@ -151,6 +152,7 @@ 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_info_local(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*);
diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
index f17f9c57d..de2f90144 100644
--- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
+++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h
@@ -6,6 +6,10 @@
#ifndef MRDBCONF_H
#define MRDBCONF_H
+#ifndef MRB_ENABLE_DEBUG_HOOK
+# error Need 'MRB_ENABLE_DEBUG_HOOK' configuration in your 'build_config.rb'
+#endif
+
/* configuration options: */
/* maximum size for command buffer */
#define MAX_COMMAND_LINE 1024
diff --git a/mrbgems/mruby-bin-mirb/bintest/mirb.rb b/mrbgems/mruby-bin-mirb/bintest/mirb.rb
index 0515cb136..0058896f1 100644
--- a/mrbgems/mruby-bin-mirb/bintest/mirb.rb
+++ b/mrbgems/mruby-bin-mirb/bintest/mirb.rb
@@ -12,9 +12,9 @@ assert('regression for #1563') do
end
assert('mirb -d option') do
- o, _ = Open3.capture2('bin/mirb', :stdin_data => "p $DEBUG\n")
+ o, _ = Open3.capture2('bin/mirb', :stdin_data => "$DEBUG\n")
assert_true o.include?('=> false')
- o, _ = Open3.capture2('bin/mirb -d', :stdin_data => "p $DEBUG\n")
+ o, _ = Open3.capture2('bin/mirb -d', :stdin_data => "$DEBUG\n")
assert_true o.include?('=> true')
end
diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
index b494b13c9..17b2ca16c 100644
--- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
+++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
@@ -6,6 +6,15 @@
** immediately. It's a REPL...
*/
+#include <mruby.h>
+#include <mruby/array.h>
+#include <mruby/proc.h>
+#include <mruby/compile.h>
+#include <mruby/dump.h>
+#include <mruby/string.h>
+#include <mruby/variable.h>
+#include <mruby/throw.h>
+
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@@ -49,14 +58,6 @@
#define SIGJMP_BUF jmp_buf
#endif
-#include <mruby.h>
-#include <mruby/array.h>
-#include <mruby/proc.h>
-#include <mruby/compile.h>
-#include <mruby/dump.h>
-#include <mruby/string.h>
-#include <mruby/variable.h>
-
#ifdef ENABLE_READLINE
static const char history_file_name[] = ".mirb_history";
@@ -111,7 +112,7 @@ p(mrb_state *mrb, mrb_value obj, int prompt)
if (!mrb_string_p(val)) {
val = mrb_obj_as_string(mrb, obj);
}
- msg = mrb_locale_from_utf8(RSTRING_PTR(val), RSTRING_LEN(val));
+ msg = mrb_locale_from_utf8(RSTRING_PTR(val), (int)RSTRING_LEN(val));
fwrite(msg, strlen(msg), 1, stdout);
mrb_locale_free(msg);
putc('\n', stdout);
@@ -126,10 +127,6 @@ is_code_block_open(struct mrb_parser_state *parser)
/* check for heredoc */
if (parser->parsing_heredoc != NULL) return TRUE;
- if (parser->heredoc_end_now) {
- parser->heredoc_end_now = FALSE;
- return FALSE;
- }
/* check for unterminated string */
if (parser->lex_strterm) return TRUE;
@@ -233,7 +230,7 @@ usage(const char *name)
{
static const char *const usage_msg[] = {
"switches:",
- "-d set $DEBUG to true (same as `mruby -d`)"
+ "-d set $DEBUG to true (same as `mruby -d`)",
"-r library same as `mruby -r`",
"-v print version number, then run in verbose mode",
"--verbose run in verbose mode",
@@ -376,7 +373,7 @@ check_keyword(const char *buf, const char *word)
size_t len = strlen(word);
/* skip preceding spaces */
- while (*p && isspace((unsigned char)*p)) {
+ while (*p && ISSPACE(*p)) {
p++;
}
/* check keyword */
@@ -386,7 +383,7 @@ check_keyword(const char *buf, const char *word)
p += len;
/* skip trailing spaces */
while (*p) {
- if (!isspace((unsigned char)*p)) return 0;
+ if (!ISSPACE(*p)) return 0;
p++;
}
return 1;
@@ -495,7 +492,10 @@ main(int argc, char **argv)
while (TRUE) {
char *utf8;
+ struct mrb_jmpbuf c_jmp;
+ MRB_TRY(&c_jmp);
+ mrb->jmp = &c_jmp;
if (args.rfp) {
if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL)
goto done;
@@ -559,8 +559,7 @@ main(int argc, char **argv)
MIRB_LINE_FREE(line);
#endif
-done:
-
+ 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);
@@ -599,13 +598,13 @@ done:
/* warning */
char* msg = mrb_locale_from_utf8(parser->warn_buffer[0].message, -1);
printf("line %d: %s\n", parser->warn_buffer[0].lineno, msg);
- mrb_utf8_free(msg);
+ mrb_locale_free(msg);
}
if (0 < parser->nerr) {
/* syntax error */
char* msg = mrb_locale_from_utf8(parser->error_buffer[0].message, -1);
printf("line %d: %s\n", parser->error_buffer[0].lineno, msg);
- mrb_utf8_free(msg);
+ mrb_locale_free(msg);
}
else {
/* generate bytecode */
@@ -652,6 +651,11 @@ done:
}
mrb_parser_free(parser);
cxt->lineno++;
+ MRB_CATCH(&c_jmp) {
+ p(mrb, mrb_obj_value(mrb->exc), 0);
+ mrb->exc = 0;
+ }
+ MRB_END_EXC(&c_jmp);
}
#ifdef ENABLE_READLINE
@@ -661,6 +665,12 @@ done:
if (args.rfp) fclose(args.rfp);
mrb_free(mrb, args.argv);
+ if (args.libv) {
+ for (i = 0; i < args.libc; ++i) {
+ mrb_free(mrb, args.libv[i]);
+ }
+ mrb_free(mrb, args.libv);
+ }
mrbc_context_free(mrb, cxt);
mrb_close(mrb);
diff --git a/mrbgems/mruby-bin-mrbc/mrbgem.rake b/mrbgems/mruby-bin-mrbc/mrbgem.rake
index e710b5a49..48b67aedb 100644
--- a/mrbgems/mruby-bin-mrbc/mrbgem.rake
+++ b/mrbgems/mruby-bin-mrbc/mrbgem.rake
@@ -8,7 +8,7 @@ MRuby::Gem::Specification.new 'mruby-bin-mrbc' do |spec|
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|
+ file exec => mrbc_objs + [build.libmruby_core_static] do |t|
build.linker.run t.name, t.prerequisites
end
diff --git a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
index 580c2e25b..2fd9da77d 100644
--- a/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
+++ b/mrbgems/mruby-bin-mrbc/tools/mrbc/mrbc.c
@@ -18,6 +18,7 @@ struct mrbc_args {
const char *initname;
mrb_bool check_syntax : 1;
mrb_bool verbose : 1;
+ mrb_bool remove_lv : 1;
unsigned int flags : 4;
};
@@ -33,6 +34,7 @@ usage(const char *name)
"-B<symbol> binary <symbol> output in C language format",
"-e generate little endian iseq data",
"-E generate big endian iseq data",
+ "--remove-lv remove local variables",
"--verbose run at verbose mode",
"--version print the version",
"--copyright print the copyright",
@@ -142,6 +144,10 @@ parse_args(mrb_state *mrb, int argc, char **argv, struct mrbc_args *args)
mrb_show_copyright(mrb);
exit(EXIT_SUCCESS);
}
+ else if (strcmp(argv[i] + 2, "remove-lv") == 0) {
+ args->remove_lv = TRUE;
+ break;
+ }
return -1;
default:
return i;
@@ -232,6 +238,9 @@ dump_file(mrb_state *mrb, FILE *wfp, const char *outfile, struct RProc *proc, st
int n = MRB_DUMP_OK;
mrb_irep *irep = proc->body.irep;
+ if (args->remove_lv) {
+ mrb_irep_remove_lv(mrb, irep);
+ }
if (args->initname) {
n = mrb_dump_irep_cfunc(mrb, irep, args->flags, wfp, args->initname);
if (n == MRB_DUMP_INVALID_ARGUMENT) {
diff --git a/mrbgems/mruby-bin-mruby-config/mrbgem.rake b/mrbgems/mruby-bin-mruby-config/mrbgem.rake
deleted file mode 100644
index 66d6ef80b..000000000
--- a/mrbgems/mruby-bin-mruby-config/mrbgem.rake
+++ /dev/null
@@ -1,30 +0,0 @@
-module MRuby
- class Build
- def exefile(name)
- if name.is_a?(Array)
- name.flatten.map { |n| exefile(n) }
- elsif name !~ /\./
- "#{name}#{exts.executable}"
- else
- name
- end
- end
- end
-end
-
-MRuby.each_target do
- next if kind_of? MRuby::CrossBuild
-
- mruby_config = 'mruby-config' + (ENV['OS'] == 'Windows_NT' ? '.bat' : '')
- mruby_config_path = "#{build_dir}/bin/#{mruby_config}"
- @bins << mruby_config
-
- file mruby_config_path => libfile("#{build_dir}/lib/libmruby") do |t|
- 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_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS|MRUBY_LIBMRUBY_PATH)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"}
- })
- FileUtils.chmod(0755, t.name)
- end
-end
diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
index a7fb63fa2..5dbbc5592 100644
--- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb
+++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
@@ -1,10 +1,18 @@
require 'tempfile'
+require 'open3'
+
+def assert_mruby(exp_out, exp_err, exp_success, args)
+ out, err, stat = Open3.capture3(cmd("mruby"), *args)
+ assert do
+ assert_operator(exp_out, :===, out, "standard output")
+ assert_operator(exp_err, :===, err, "standard error")
+ assert_equal(exp_success, stat.success?, "exit success?")
+ end
+end
assert('regression for #1564') do
- o = `#{cmd('mruby')} -e #{shellquote('<<')} 2>&1`
- assert_include o, "-e:1:2: syntax error"
- o = `#{cmd('mruby')} -e #{shellquote('<<-')} 2>&1`
- assert_include o, "-e:1:3: syntax error"
+ assert_mruby("", /\A-e:1:2: syntax error, .*\n\z/, false, %w[-e <<])
+ assert_mruby("", /\A-e:1:3: syntax error, .*\n\z/, false, %w[-e <<-])
end
assert('regression for #1572') do
@@ -12,7 +20,7 @@ assert('regression for #1572') do
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"'
+ assert_equal '"ok"', o
end
assert '$0 value' do
@@ -31,6 +39,13 @@ assert '$0 value' do
assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp
end
+assert('float literal') do
+ script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb')
+ File.write script.path, 'p [3.21, 2e308.infinite?, -2e308.infinite?]'
+ system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}"
+ assert_equal "[3.21, 1, -1]", `#{cmd('mruby')} -b #{bin.path}`.chomp!
+end
+
assert '__END__', '8.6' do
script = Tempfile.new('test.rb')
@@ -59,6 +74,11 @@ RUBY
assert_equal 0, $?.exitstatus
end
+assert('mruby -c option') do
+ assert_mruby("Syntax OK\n", "", true, ["-c", "-e", "p 1"])
+ assert_mruby("", /\A-e:1:7: syntax error, .*\n\z/, false, ["-c", "-e", "p 1; 1."])
+end
+
assert('mruby -d option') do
o = `#{cmd('mruby')} -e #{shellquote('p $DEBUG')}`
assert_equal "false\n", o
@@ -66,6 +86,14 @@ assert('mruby -d option') do
assert_equal "true\n", o
end
+assert('mruby -e option (no code specified)') do
+ assert_mruby("", /\A.*: No code specified for -e\n\z/, false, %w[-e])
+end
+
+assert('mruby -h option') do
+ assert_mruby(/\AUsage: #{Regexp.escape cmd("mruby")} .*/m, "", true, %w[-h])
+end
+
assert('mruby -r option') do
lib = Tempfile.new('lib.rb')
lib.write <<EOS
@@ -88,3 +116,32 @@ EOS
assert_equal 'hogeClass', `#{cmd('mruby')} -r #{lib.path} -r #{script.path} -e #{shellquote('print Hoge.class')}`
assert_equal 0, $?.exitstatus
end
+
+assert('mruby -r option (no library specified)') do
+ assert_mruby("", /\A.*: No library specified for -r\n\z/, false, %w[-r])
+end
+
+assert('mruby -r option (file not found)') do
+ assert_mruby("", /\A.*: Cannot open library file: .*\n\z/, false, %w[-r _no_exists_])
+end
+
+assert('mruby invalid short option') do
+ assert_mruby("", /\A.*: invalid option -1 .*\n\z/, false, %w[-1])
+end
+
+assert('mruby invalid long option') do
+ assert_mruby("", /\A.*: invalid option --longopt .*\n\z/, false, %w[--longopt])
+end
+
+assert('unhandled exception') do
+ assert_mruby("", /\bEXCEPTION\b.*\n\z/, false, %w[-e raise("EXCEPTION")])
+end
+
+assert('program file not found') do
+ assert_mruby("", /\A.*: Cannot open program file: .*\n\z/, false, %w[_no_exists_])
+end
+
+assert('codegen error') do
+ code = "def f(#{(1..100).map{|n| "a#{n}"} * ","}); end"
+ assert_mruby("", /\Acodegen error:.*\n\z/, false, ["-e", code])
+end
diff --git a/mrbgems/mruby-bin-mruby/mrbgem.rake b/mrbgems/mruby-bin-mruby/mrbgem.rake
index fbec13847..280621e31 100644
--- a/mrbgems/mruby-bin-mruby/mrbgem.rake
+++ b/mrbgems/mruby-bin-mruby/mrbgem.rake
@@ -5,6 +5,7 @@ MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec|
spec.bins = %w(mruby)
spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
spec.add_dependency('mruby-error', :core => 'mruby-error')
+ spec.add_test_dependency('mruby-print', :core => 'mruby-print')
if build.cxx_exception_enabled?
build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx")
diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
index d9f90c5e1..29ab4c17c 100644
--- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
+++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
@@ -7,19 +7,6 @@
#include <mruby/dump.h>
#include <mruby/variable.h>
-#ifdef MRB_DISABLE_STDIO
-static void
-p(mrb_state *mrb, mrb_value obj)
-{
- 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
-
struct _args {
FILE *rfp;
char* cmdline;
@@ -41,7 +28,7 @@ usage(const char *name)
"switches:",
"-b load and execute RiteBinary (mrb) file",
"-c check syntax only",
- "-d set debugging flags (set $DEBUG to true)"
+ "-d set debugging flags (set $DEBUG to true)",
"-e 'command' one line of script",
"-r library load the library before executing your script",
"-v print version number, then run in verbose mode",
@@ -119,14 +106,17 @@ append_cmdline:
}
}
else {
- printf("%s: No code specified for -e\n", *origargv);
- return EXIT_SUCCESS;
+ fprintf(stderr, "%s: No code specified for -e\n", *origargv);
+ return EXIT_FAILURE;
}
break;
+ case 'h':
+ usage(*origargv);
+ exit(EXIT_SUCCESS);
case 'r':
if (!item[0]) {
if (argc <= 1) {
- printf("%s: No library specified for -r\n", *origargv);
+ fprintf(stderr, "%s: No library specified for -r\n", *origargv);
return EXIT_FAILURE;
}
argc--; argv++;
@@ -158,6 +148,7 @@ append_cmdline:
exit(EXIT_SUCCESS);
}
default:
+ fprintf(stderr, "%s: invalid option %s (-h will show valid options)\n", *origargv, *argv);
return EXIT_FAILURE;
}
}
@@ -167,7 +158,7 @@ append_cmdline:
else {
args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r");
if (args->rfp == NULL) {
- printf("%s: Cannot open program file. (%s)\n", *origargv, *argv);
+ fprintf(stderr, "%s: Cannot open program file: %s\n", *origargv, *argv);
return EXIT_FAILURE;
}
args->fname = TRUE;
@@ -212,14 +203,13 @@ main(int argc, char **argv)
mrb_sym zero_sym;
if (mrb == NULL) {
- fputs("Invalid mrb_state, exiting mruby\n", stderr);
+ fprintf(stderr, "%s: Invalid mrb_state, exiting mruby\n", *argv);
return EXIT_FAILURE;
}
n = parse_args(mrb, argc, argv, &args);
if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) {
cleanup(mrb, &args);
- usage(argv[0]);
return n;
}
else {
@@ -258,7 +248,8 @@ main(int argc, char **argv)
for (i = 0; i < args.libc; i++) {
FILE *lfp = fopen(args.libv[i], args.mrbfile ? "rb" : "r");
if (lfp == NULL) {
- printf("Cannot open library file. (%s)\n", args.libv[i]);
+ fprintf(stderr, "%s: Cannot open library file: %s\n", *argv, args.libv[i]);
+ mrbc_context_free(mrb, c);
cleanup(mrb, &args);
return EXIT_FAILURE;
}
@@ -288,19 +279,16 @@ main(int argc, char **argv)
mrb_gc_arena_restore(mrb, ai);
mrbc_context_free(mrb, c);
if (mrb->exc) {
- if (mrb_undef_p(v)) {
- mrb_p(mrb, mrb_obj_value(mrb->exc));
- }
- else {
+ if (!mrb_undef_p(v)) {
mrb_print_error(mrb);
}
- n = -1;
+ n = EXIT_FAILURE;
}
else if (args.check_syntax) {
- printf("Syntax OK\n");
+ puts("Syntax OK");
}
}
cleanup(mrb, &args);
- return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ return n;
}
diff --git a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb b/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
index bb664a2b1..2db3c10b1 100644
--- a/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
+++ b/mrbgems/mruby-bin-strip/bintest/mruby-strip.rb
@@ -67,7 +67,7 @@ EOS
`#{cmd('mruby-strip')} -l #{without_lv.path}`
assert_true without_lv.size < with_lv.size
-
- assert_equal '[:a, :b]', `#{cmd('mruby')} -b #{with_lv.path}`.chomp
- assert_equal '[]', `#{cmd('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 deb66d54c..fb78b0c3b 100644
--- a/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
+++ b/mrbgems/mruby-bin-strip/tools/mruby-strip/mruby-strip.c
@@ -12,22 +12,6 @@ struct strip_args {
mrb_bool lvar;
};
-
-static void
-irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
-{
- int i;
-
- if (irep->lv) {
- mrb_free(mrb, irep->lv);
- irep->lv = NULL;
- }
-
- for (i = 0; i < irep->rlen; ++i) {
- irep_remove_lv(mrb, irep->reps[i]);
- }
-}
-
static void
print_usage(const char *f)
{
@@ -99,7 +83,7 @@ strip(mrb_state *mrb, struct strip_args *args)
/* clear lv if --lvar is enabled */
if (args->lvar) {
- irep_remove_lv(mrb, irep);
+ mrb_irep_remove_lv(mrb, irep);
}
wfile = fopen(filename, "wb");
diff --git a/mrbgems/mruby-class-ext/mrblib/module.rb b/mrbgems/mruby-class-ext/mrblib/module.rb
new file mode 100644
index 000000000..301585187
--- /dev/null
+++ b/mrbgems/mruby-class-ext/mrblib/module.rb
@@ -0,0 +1,89 @@
+class Module
+
+ ##
+ # call-seq:
+ # mod < other -> true, false, or nil
+ #
+ # Returns true if `mod` is a subclass of `other`. Returns
+ # <code>nil</code> if there's no relationship between the two.
+ # (Think of the relationship in terms of the class definition:
+ # "class A < B" implies "A < B".)
+ #
+ def <(other)
+ if self.equal?(other)
+ false
+ else
+ self <= other
+ end
+ end
+
+ ##
+ # call-seq:
+ # mod <= other -> true, false, or nil
+ #
+ # Returns true if `mod` is a subclass of `other` or
+ # is the same as `other`. Returns
+ # <code>nil</code> if there's no relationship between the two.
+ # (Think of the relationship in terms of the class definition:
+ # "class A < B" implies "A < B".)
+ def <=(other)
+ raise TypeError, 'compared with non class/module' unless other.is_a?(Module)
+ if self.ancestors.include?(other)
+ return true
+ elsif other.ancestors.include?(self)
+ return false
+ end
+ end
+
+ ##
+ # call-seq:
+ # mod > other -> true, false, or nil
+ #
+ # Returns true if `mod` is an ancestor of `other`. Returns
+ # <code>nil</code> if there's no relationship between the two.
+ # (Think of the relationship in terms of the class definition:
+ # "class A < B" implies "B > A".)
+ #
+ def >(other)
+ if self.equal?(other)
+ false
+ else
+ self >= other
+ end
+ end
+
+ ##
+ # call-seq:
+ # mod >= other -> true, false, or nil
+ #
+ # Returns true if `mod` is an ancestor of `other`, or the
+ # two modules are the same. Returns
+ # <code>nil</code> if there's no relationship between the two.
+ # (Think of the relationship in terms of the class definition:
+ # "class A < B" implies "B > A".)
+ #
+ def >=(other)
+ raise TypeError, 'compared with non class/module' unless other.is_a?(Module)
+ return other < self
+ end
+
+ ##
+ # call-seq:
+ # module <=> other_module -> -1, 0, +1, or nil
+ #
+ # Comparison---Returns -1, 0, +1 or nil depending on whether `module`
+ # includes `other_module`, they are the same, or if `module` is included by
+ # `other_module`.
+ #
+ # Returns `nil` if `module` has no relationship with `other_module`, if
+ # `other_module` is not a module, or if the two values are incomparable.
+ #
+ def <=>(other)
+ return 0 if self.equal?(other)
+ return nil unless other.is_a?(Module)
+ cmp = self < other
+ return -1 if cmp
+ return 1 unless cmp.nil?
+ return nil
+ end
+end
diff --git a/mrbgems/mruby-class-ext/src/class.c b/mrbgems/mruby-class-ext/src/class.c
index 705db9949..fdd56bcc3 100644
--- a/mrbgems/mruby-class-ext/src/class.c
+++ b/mrbgems/mruby-class-ext/src/class.c
@@ -5,8 +5,7 @@
static mrb_value
mrb_mod_name(mrb_state *mrb, mrb_value self)
{
- mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self));
- return mrb_nil_p(name)? name : mrb_str_dup(mrb, name);
+ return mrb_class_path(mrb, mrb_class_ptr(self));
}
static mrb_value
diff --git a/mrbgems/mruby-class-ext/test/module.rb b/mrbgems/mruby-class-ext/test/module.rb
index ed6713aac..52e04ab37 100644
--- a/mrbgems/mruby-class-ext/test/module.rb
+++ b/mrbgems/mruby-class-ext/test/module.rb
@@ -1,3 +1,57 @@
+assert 'Module#<' do
+ a = Class.new
+ b = Class.new(a)
+ c = Class.new(a)
+ d = Module.new
+ e = Class.new { include d }
+ f = Module.new { include d }
+
+ # compare class to class
+ assert_true b < a
+ assert_false b < b
+ assert_false a < b
+ assert_nil c < b
+
+ # compare class to module
+ assert_true e < d
+ assert_false d < e
+ assert_nil a < d
+
+ # compare module to module
+ assert_true f < d
+ assert_false f < f
+ assert_false d < f
+
+ assert_raise(TypeError) { a < Object.new }
+end
+
+assert 'Module#<=' do
+ a = Class.new
+ b = Class.new(a)
+ c = Class.new(a)
+ d = Module.new
+ e = Class.new { include d }
+ f = Module.new { include d }
+
+ # compare class to class
+ assert_true b <= a
+ assert_true b <= b
+ assert_false a <= b
+ assert_nil c <= b
+
+ # compare class to module
+ assert_true e <= d
+ assert_false d <= e
+ assert_nil a <= d
+
+ # compare module to module
+ assert_true f <= d
+ assert_true f <= f
+ assert_false d <= f
+
+ assert_raise(TypeError) { a <= Object.new }
+end
+
assert 'Module#name' do
module Outer
class Inner; end
@@ -26,7 +80,7 @@ end
assert 'Module#singleton_class?' do
mod = Module.new
cls = Class.new
- scl = cls.singleton_class
+ scl = (class <<cls; self; end)
assert_false mod.singleton_class?
assert_false cls.singleton_class?
diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c
index f71de9b4b..ed8fc3150 100644
--- a/mrbgems/mruby-compiler/core/codegen.c
+++ b/mrbgems/mruby-compiler/core/codegen.c
@@ -8,6 +8,7 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include <mruby.h>
#include <mruby/compile.h>
#include <mruby/proc.h>
@@ -23,6 +24,8 @@
#define MRB_CODEGEN_LEVEL_MAX 1024
#endif
+#define MAXARG_S (1<<16)
+
typedef mrb_ast_node node;
typedef struct mrb_parser_state parser_state;
@@ -36,7 +39,7 @@ enum looptype {
struct loopinfo {
enum looptype type;
- int pc1, pc2, pc3, acc;
+ int pc0, pc1, pc2, pc3, acc;
int ensure_level;
struct loopinfo *prev;
};
@@ -50,23 +53,24 @@ typedef struct scope {
node *lv;
- int sp;
- int pc;
- int lastlabel;
+ uint16_t sp;
+ uint16_t pc;
+ uint16_t lastpc;
+ uint16_t lastlabel;
int ainfo:15;
mrb_bool mscope:1;
struct loopinfo *loop;
int ensure_level;
- char const *filename;
+ mrb_sym filename_sym;
uint16_t lineno;
mrb_code *iseq;
uint16_t *lines;
- int icapa;
+ uint32_t icapa;
mrb_irep *irep;
- int pcapa, scapa, rcapa;
+ uint32_t pcapa, scapa, rcapa;
uint16_t nlocals;
uint16_t nregs;
@@ -98,12 +102,14 @@ codegen_error(codegen_scope *s, const char *message)
while (s->prev) {
codegen_scope *tmp = s->prev;
mrb_free(s->mrb, s->iseq);
+ mrb_free(s->mrb, s->lines);
mrb_pool_close(s->mpool);
s = tmp;
}
#ifndef MRB_DISABLE_STDIO
- if (s->filename && s->lineno) {
- fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message);
+ if (s->filename_sym && s->lineno) {
+ const char *filename = mrb_sym2name_len(s->mrb, s->filename_sym, NULL);
+ fprintf(stderr, "codegen error:%s:%d: %s\n", filename, s->lineno, message);
}
else {
fprintf(stderr, "codegen error: %s\n", message);
@@ -122,15 +128,6 @@ codegen_palloc(codegen_scope *s, size_t len)
}
static void*
-codegen_malloc(codegen_scope *s, size_t len)
-{
- void *p = mrb_malloc_simple(s->mrb, len);
-
- if (!p) codegen_error(s, "mrb_malloc");
- return p;
-}
-
-static void*
codegen_realloc(codegen_scope *s, void *p, size_t len)
{
p = mrb_realloc_simple(s->mrb, p, len);
@@ -142,32 +139,135 @@ codegen_realloc(codegen_scope *s, void *p, size_t len)
static int
new_label(codegen_scope *s)
{
- s->lastlabel = s->pc;
- return s->pc;
+ return s->lastlabel = s->pc;
}
-static inline int
-genop(codegen_scope *s, mrb_code i)
+static void
+emit_B(codegen_scope *s, uint32_t pc, uint8_t i)
{
- if (s->pc >= s->icapa) {
+ if (pc >= MAXARG_S || s->icapa >= MAXARG_S) {
+ codegen_error(s, "too big code block");
+ }
+ if (pc >= s->icapa) {
s->icapa *= 2;
- if (s->pc >= MAXARG_sBx) {
- codegen_error(s, "too big code block");
- }
- if (s->icapa > MAXARG_sBx) {
- s->icapa = MAXARG_sBx;
+ if (s->icapa > MAXARG_S) {
+ s->icapa = MAXARG_S;
}
s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa);
if (s->lines) {
- s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa);
- s->irep->lines = s->lines;
+ s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->icapa);
}
}
- s->iseq[s->pc] = i;
if (s->lines) {
- s->lines[s->pc] = s->lineno;
+ if (s->lineno > 0 || pc == 0)
+ s->lines[pc] = s->lineno;
+ else
+ s->lines[pc] = s->lines[pc-1];
+ }
+ s->iseq[pc] = i;
+}
+
+static void
+emit_S(codegen_scope *s, int pc, uint16_t i)
+{
+ uint8_t hi = i>>8;
+ uint8_t lo = i&0xff;
+
+ emit_B(s, pc, hi);
+ emit_B(s, pc+1, lo);
+}
+
+static void
+gen_B(codegen_scope *s, uint8_t i)
+{
+ emit_B(s, s->pc, i);
+ s->pc++;
+}
+
+static void
+gen_S(codegen_scope *s, uint16_t i)
+{
+ emit_S(s, s->pc, i);
+ s->pc += 2;
+}
+
+static void
+genop_0(codegen_scope *s, mrb_code i)
+{
+ s->lastpc = s->pc;
+ gen_B(s, i);
+}
+
+static void
+genop_1(codegen_scope *s, mrb_code i, uint16_t a)
+{
+ s->lastpc = s->pc;
+ if (a > 0xff) {
+ gen_B(s, OP_EXT1);
+ gen_B(s, i);
+ gen_S(s, a);
}
- return s->pc++;
+ else {
+ gen_B(s, i);
+ gen_B(s, (uint8_t)a);
+ }
+}
+
+static void
+genop_2(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b)
+{
+ s->lastpc = s->pc;
+ if (a > 0xff && b > 0xff) {
+ gen_B(s, OP_EXT3);
+ gen_B(s, i);
+ gen_S(s, a);
+ gen_S(s, b);
+ }
+ else if (b > 0xff) {
+ gen_B(s, OP_EXT2);
+ gen_B(s, i);
+ gen_B(s, (uint8_t)a);
+ gen_S(s, b);
+ }
+ else if (a > 0xff) {
+ gen_B(s, OP_EXT1);
+ gen_B(s, i);
+ gen_S(s, a);
+ gen_B(s, (uint8_t)b);
+ }
+ else {
+ gen_B(s, i);
+ gen_B(s, (uint8_t)a);
+ gen_B(s, (uint8_t)b);
+ }
+}
+
+static void
+genop_3(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b, uint8_t c)
+{
+ genop_2(s, i, a, b);
+ gen_B(s, c);
+}
+
+static void
+genop_2S(codegen_scope *s, mrb_code i, uint16_t a, uint16_t b)
+{
+ genop_1(s, i, a);
+ gen_S(s, b);
+}
+
+static void
+genop_W(codegen_scope *s, mrb_code i, uint32_t a)
+{
+ uint8_t a1 = (a>>16) & 0xff;
+ uint8_t a2 = (a>>8) & 0xff;
+ uint8_t a3 = a & 0xff;
+
+ s->lastpc = s->pc;
+ gen_B(s, i);
+ gen_B(s, a1);
+ gen_B(s, a2);
+ gen_B(s, a3);
}
#define NOVAL 0
@@ -181,298 +281,270 @@ no_optimize(codegen_scope *s)
return FALSE;
}
-static int
-genop_peep(codegen_scope *s, mrb_code i, int val)
+static
+mrb_bool
+on_eval(codegen_scope *s)
{
- /* peephole optimization */
- 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);
+ if (s && s->parser && s->parser->on_eval)
+ return TRUE;
+ return FALSE;
+}
- switch (c1) {
- case OP_MOVE:
- if (GETARG_A(i) == GETARG_B(i)) {
- /* skip useless OP_MOVE */
- return 0;
- }
- if (val) break;
- switch (c0) {
- case OP_MOVE:
- 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->pc--;
- return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val);
- }
- break;
- case OP_LOADI:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0));
- return 0;
- }
- break;
- case OP_ARRAY:
- case OP_HASH:
- case OP_RANGE:
- case OP_AREF:
- case OP_GETUPVAR:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0));
- return 0;
- }
- break;
- case OP_LOADSYM:
- case OP_GETGLOBAL:
- case OP_GETIV:
- case OP_GETCV:
- case OP_GETCONST:
- case OP_GETSPECIAL:
- case OP_LOADL:
- case OP_STRING:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0));
- return 0;
- }
- break;
- case OP_SCLASS:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0));
- return 0;
- }
- break;
- case OP_LOADNIL:
- case OP_LOADSELF:
- case OP_LOADT:
- case OP_LOADF:
- case OP_OCLASS:
- if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i));
- return 0;
- }
- break;
- default:
- break;
- }
- break;
- case OP_SETIV:
- case OP_SETCV:
- case OP_SETCONST:
- case OP_SETMCNST:
- case OP_SETGLOBAL:
- if (val) break;
- if (c0 == OP_MOVE) {
- if (GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i));
- return 0;
- }
- }
- break;
- case OP_SETUPVAR:
- if (val) break;
- if (c0 == OP_MOVE) {
- if (GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i));
- return 0;
- }
- }
- break;
- case OP_GETUPVAR:
- if (c0 == OP_SETUPVAR) {
- if (GETARG_B(i) == GETARG_B(i0) && GETARG_C(i) == GETARG_C(i0)) {
- if (GETARG_A(i) == GETARG_A(i0)) {
- /* just skip OP_SETUPVAR */
- return 0;
- }
- else {
- return genop(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_A(i0)));
- }
- }
- }
- break;
- case OP_EPOP:
- if (c0 == OP_EPOP) {
- s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i));
- return 0;
- }
- break;
- case OP_POPERR:
- if (c0 == OP_POPERR) {
- s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i));
- return 0;
- }
- break;
- case OP_RETURN:
- switch (c0) {
- case OP_RETURN:
- return 0;
- case OP_MOVE:
- if (GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL);
- return 0;
- }
- break;
- case OP_SETIV:
- case OP_SETCV:
- case OP_SETCONST:
- case OP_SETMCNST:
- case OP_SETUPVAR:
- case OP_SETGLOBAL:
- s->pc--;
- genop_peep(s, i0, NOVAL);
- i0 = s->iseq[s->pc-1];
- return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL));
-#if 0
- case OP_SEND:
- if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0));
- return;
- }
- break;
-#endif
- default:
- break;
- }
- break;
- case OP_ADD:
- case OP_SUB:
- if (c0 == OP_LOADI) {
- int c = GETARG_sBx(i0);
-
- if (c1 == OP_SUB) c = -c;
- if (c > 127 || c < -127) break;
- if (0 <= c)
- s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c);
- else
- s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c);
- return 0;
- }
- break;
- case OP_ARYCAT:
- case OP_ARYPUSH:
- if (c0 == OP_MOVE && GETARG_A(i0) >= s->nlocals) {
- s->iseq[s->pc-1] = MKOP_AB(c1, GETARG_A(i), GETARG_B(i0));
- return 0;
- }
- break;
- case OP_STRCAT:
- if (c0 == OP_STRING) {
- mrb_value v = s->irep->pool[GETARG_Bx(i0)];
+struct mrb_insn_data
+mrb_decode_insn(mrb_code *pc)
+{
+ struct mrb_insn_data data = { 0 };
+ mrb_code insn = READ_B();
+ uint16_t a = 0;
+ uint16_t b = 0;
+ uint8_t c = 0;
+
+ switch (insn) {
+#define FETCH_Z() /* empty */
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ switch (insn) {
+ case OP_EXT1:
+ insn = READ_B();
+ switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ break;
+ case OP_EXT2:
+ insn = READ_B();
+ switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ break;
+ case OP_EXT3:
+ insn = READ_B();
+ switch (insn) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); break;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ break;
+ default:
+ break;
+ }
+ data.insn = insn;
+ data.a = a;
+ data.b = b;
+ data.c = c;
+ return data;
+}
- if (mrb_string_p(v) && RSTRING_LEN(v) == 0) {
- s->pc--;
- return 0;
- }
- }
- if (c0 == OP_LOADNIL) {
- if (GETARG_B(i) == GETARG_A(i0)) {
- s->pc--;
- return 0;
- }
- }
+static struct mrb_insn_data
+mrb_last_insn(codegen_scope *s)
+{
+ if (s->pc == s->lastpc) {
+ struct mrb_insn_data data;
+
+ data.insn = OP_NOP;
+ return data;
+ }
+ return mrb_decode_insn(&s->iseq[s->lastpc]);
+}
+
+static mrb_bool
+no_peephole(codegen_scope *s)
+{
+ return no_optimize(s) || s->lastlabel == s->pc || s->pc == 0 || s->pc == s->lastpc;
+}
+
+static uint16_t
+genjmp(codegen_scope *s, mrb_code i, uint16_t pc)
+{
+ uint16_t pos;
+
+ s->lastpc = s->pc;
+ gen_B(s, i);
+ pos = s->pc;
+ gen_S(s, pc);
+ return pos;
+}
+
+static uint16_t
+genjmp2(codegen_scope *s, mrb_code i, uint16_t a, int pc, int val)
+{
+ uint16_t pos;
+
+ if (!no_peephole(s) && !val) {
+ struct mrb_insn_data data = mrb_last_insn(s);
+
+ if (data.insn == OP_MOVE && data.a == a) {
+ s->pc = s->lastpc;
+ a = data.b;
+ }
+ }
+
+ s->lastpc = s->pc;
+ if (a > 0xff) {
+ gen_B(s, OP_EXT1);
+ gen_B(s, i);
+ gen_S(s, a);
+ pos = s->pc;
+ gen_S(s, pc);
+ }
+ else {
+ gen_B(s, i);
+ gen_B(s, (uint8_t)a);
+ pos = s->pc;
+ gen_S(s, pc);
+ }
+ return pos;
+}
+
+static void
+gen_move(codegen_scope *s, uint16_t dst, uint16_t src, int nopeep)
+{
+ if (no_peephole(s)) {
+ normal:
+ genop_2(s, OP_MOVE, dst, src);
+ if (on_eval(s)) {
+ genop_0(s, OP_NOP);
+ }
+ return;
+ }
+ else {
+ struct mrb_insn_data data = mrb_last_insn(s);
+
+ switch (data.insn) {
+ case OP_MOVE:
+ if (dst == src) return; /* remove useless MOVE */
+ if (data.b == dst && data.a == src) /* skip swapping MOVE */
+ return;
+ goto normal;
+ case OP_LOADNIL: case OP_LOADSELF: case OP_LOADT: case OP_LOADF:
+ case OP_LOADI__1:
+ case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3:
+ case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7:
+ if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
+ s->pc = s->lastpc;
+ genop_1(s, data.insn, dst);
break;
- case OP_JMPIF:
- case OP_JMPNOT:
- if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) {
- s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i));
- return s->pc-1;
- }
+ case OP_LOADI: case OP_LOADINEG: case OP_LOADL: case OP_LOADSYM:
+ case OP_GETGV: case OP_GETSV: case OP_GETIV: case OP_GETCV:
+ case OP_GETCONST: case OP_STRING:
+ case OP_LAMBDA: case OP_BLOCK: case OP_METHOD: case OP_BLKPUSH:
+ if (nopeep || data.a != src || data.a < s->nlocals) goto normal;
+ s->pc = s->lastpc;
+ genop_2(s, data.insn, dst, data.b);
break;
default:
- break;
+ goto normal;
}
}
- return genop(s, i);
}
static void
-scope_error(codegen_scope *s)
+gen_return(codegen_scope *s, uint8_t op, uint16_t src)
{
- exit(EXIT_FAILURE);
+ if (no_peephole(s)) {
+ genop_1(s, op, src);
+ }
+ else {
+ struct mrb_insn_data data = mrb_last_insn(s);
+
+ if (data.insn == OP_MOVE && src == data.a) {
+ s->pc = s->lastpc;
+ genop_1(s, op, data.b);
+ }
+ else if (data.insn != OP_RETURN) {
+ genop_1(s, op, src);
+ }
+ }
}
static void
-distcheck(codegen_scope *s, int diff)
+gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst)
{
- if (diff > MAXARG_sBx || diff < -MAXARG_sBx) {
- codegen_error(s, "too distant jump address");
+ if (no_peephole(s)) {
+ normal:
+ genop_1(s, op, dst);
+ return;
+ }
+ else {
+ struct mrb_insn_data data = mrb_last_insn(s);
+
+ switch (data.insn) {
+ case OP_LOADI__1:
+ if (op == OP_ADD) op = OP_SUB;
+ else op = OP_ADD;
+ data.b = 1;
+ goto replace;
+ case OP_LOADI_0: case OP_LOADI_1: case OP_LOADI_2: case OP_LOADI_3:
+ case OP_LOADI_4: case OP_LOADI_5: case OP_LOADI_6: case OP_LOADI_7:
+ data.b = data.insn - OP_LOADI_0;
+ /* fall through */
+ case OP_LOADI:
+ replace:
+ if (data.b >= 128) goto normal;
+ s->pc = s->lastpc;
+ if (op == OP_ADD) {
+ genop_2(s, OP_ADDI, dst, (uint8_t)data.b);
+ }
+ else {
+ genop_2(s, OP_SUBI, dst, (uint8_t)data.b);
+ }
+ break;
+ default:
+ goto normal;
+ }
}
}
-static inline void
-dispatch(codegen_scope *s, int pc)
+static int
+dispatch(codegen_scope *s, uint16_t pos0)
{
- int diff = s->pc - pc;
- mrb_code i = s->iseq[pc];
- int c = GET_OPCODE(i);
+ uint16_t newpos;
s->lastlabel = s->pc;
- switch (c) {
- case OP_JMP:
- case OP_JMPIF:
- case OP_JMPNOT:
- case OP_ONERR:
- break;
- default:
-#ifndef MRB_DISABLE_STDIO
- fprintf(stderr, "bug: dispatch on non JMP op\n");
-#endif
- scope_error(s);
- break;
- }
- distcheck(s, diff);
- s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
+ newpos = PEEK_S(s->iseq+pos0);
+ emit_S(s, pos0, s->pc);
+ return newpos;
}
static void
-dispatch_linked(codegen_scope *s, int pc)
+dispatch_linked(codegen_scope *s, uint16_t pos)
{
- mrb_code i;
- int pos;
-
- if (!pc) return;
+ if (pos==0) return;
for (;;) {
- i = s->iseq[pc];
- pos = GETARG_sBx(i);
- dispatch(s, pc);
- if (!pos) break;
- pc = pos;
+ pos = dispatch(s, pos);
+ if (pos==0) break;
}
}
#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0)
static void
-push_(codegen_scope *s)
+push_n_(codegen_scope *s, int n)
{
- if (s->sp > 511) {
+ if (s->sp+n >= 0xffff) {
codegen_error(s, "too complex expression");
}
- s->sp++;
+ s->sp+=n;
nregs_update;
}
static void
-push_n_(codegen_scope *s, int n)
+pop_n_(codegen_scope *s, int n)
{
- if (s->sp+n > 511) {
- codegen_error(s, "too complex expression");
+ if ((int)s->sp-n < 0) {
+ codegen_error(s, "stack pointer underflow");
}
- s->sp+=n;
- nregs_update;
+ s->sp-=n;
}
-#define push() push_(s)
+#define push() push_n_(s,1)
#define push_n(n) push_n_(s,n)
-#define pop_(s) ((s)->sp--)
-#define pop() pop_(s)
-#define pop_n(n) (s->sp-=(n))
+#define pop() pop_n_(s,1)
+#define pop_n(n) pop_n_(s,n)
#define cursp() (s->sp)
static inline int
@@ -496,9 +568,12 @@ new_lit(codegen_scope *s, mrb_value val)
#ifndef MRB_WITHOUT_FLOAT
case MRB_TT_FLOAT:
for (i=0; i<s->irep->plen; i++) {
+ mrb_float f1, f2;
pv = &s->irep->pool[i];
if (mrb_type(*pv) != MRB_TT_FLOAT) continue;
- if (mrb_float(*pv) == mrb_float(val)) return i;
+ f1 = mrb_float(*pv);
+ f2 = mrb_float(val);
+ if (f1 == f2 && !signbit(f1) == !signbit(f2)) return i;
}
break;
#endif
@@ -545,52 +620,23 @@ new_lit(codegen_scope *s, mrb_value val)
return i;
}
-/* method symbols should be fit in 9 bits */
-#define MAXMSYMLEN 512
/* maximum symbol numbers */
-#define MAXSYMLEN 65536
+#define MAXSYMLEN 0x10000
static int
-new_msym(codegen_scope *s, mrb_sym sym)
+new_sym(codegen_scope *s, mrb_sym sym)
{
int i, len;
mrb_assert(s->irep);
len = s->irep->slen;
- if (len > MAXMSYMLEN) len = MAXMSYMLEN;
for (i=0; i<len; i++) {
if (s->irep->syms[i] == sym) return i;
- if (s->irep->syms[i] == 0) break;
}
- if (i == MAXMSYMLEN) {
- codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXMSYMLEN) ")");
- }
- s->irep->syms[i] = sym;
- if (i == s->irep->slen) s->irep->slen++;
- return i;
-}
-
-static int
-new_sym(codegen_scope *s, mrb_sym sym)
-{
- int i;
-
- for (i=0; i<s->irep->slen; i++) {
- if (s->irep->syms[i] == sym) return i;
- }
- if (s->irep->slen == MAXSYMLEN) {
- codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXSYMLEN) ")");
- }
-
- if (s->irep->slen > MAXMSYMLEN/2 && s->scapa == MAXMSYMLEN) {
- s->scapa = MAXSYMLEN;
- s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*MAXSYMLEN);
- for (i = s->irep->slen; i < MAXMSYMLEN; i++) {
- static const mrb_sym mrb_sym_zero = { 0 };
- s->irep->syms[i] = mrb_sym_zero;
- }
- s->irep->slen = MAXMSYMLEN;
+ if (s->irep->slen >= s->scapa) {
+ s->scapa *= 2;
+ s->irep->syms = (mrb_sym*)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*s->scapa);
}
s->irep->syms[s->irep->slen] = sym;
return s->irep->slen++;
@@ -608,8 +654,12 @@ node_len(node *tree)
return n;
}
+#define nint(x) ((int)(intptr_t)(x))
+#define nchar(x) ((char)(intptr_t)(x))
#define nsym(x) ((mrb_sym)(intptr_t)(x))
+
#define lv_name(lv) nsym((lv)->car)
+
static int
lv_idx(codegen_scope *s, mrb_sym id)
{
@@ -631,7 +681,6 @@ for_body(codegen_scope *s, node *tree)
int idx;
struct loopinfo *lp;
node *n2;
- mrb_code c;
/* generate receiver */
codegen(s, tree->cdr->car, VAL);
@@ -645,7 +694,7 @@ for_body(codegen_scope *s, node *tree)
/* generate loop variable */
n2 = tree->car;
- genop(s, MKOP_Ax(OP_ENTER, 0x40000));
+ genop_W(s, OP_ENTER, 0x40000);
if (n2->car && !n2->car->cdr && !n2->cdr) {
gen_assignment(s, n2->car->car, 1, NOVAL);
}
@@ -659,25 +708,20 @@ for_body(codegen_scope *s, node *tree)
/* loop body */
codegen(s, tree->cdr->cdr->car, VAL);
pop();
- if (s->pc > 0) {
- c = s->iseq[s->pc-1];
- if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel)
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
- }
+ gen_return(s, OP_RETURN, cursp());
loop_pop(s, NOVAL);
scope_finish(s);
s = prev;
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK));
+ genop_2(s, OP_BLOCK, cursp(), s->irep->rlen-1);
push();pop(); /* space for a block */
pop();
- idx = new_msym(s, mrb_intern_lit(s->mrb, "each"));
- genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0));
+ idx = new_sym(s, mrb_intern_lit(s->mrb, "each"));
+ genop_3(s, OP_SENDB, cursp(), idx, 0);
}
static int
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) {
@@ -688,78 +732,150 @@ lambda_body(codegen_scope *s, node *tree, int blk)
if (blk) {
struct loopinfo *lp = loop_push(s, LOOP_BLOCK);
- lp->pc1 = new_label(s);
+ lp->pc0 = new_label(s);
}
tree = tree->cdr;
- if (tree->car) {
+ if (tree->car == NULL) {
+ genop_W(s, OP_ENTER, 0);
+ }
+ else {
mrb_aspec a;
int ma, oa, ra, pa, ka, kd, ba;
int pos, i;
- node *n, *opt;
+ node *opt;
+ node *margs, *pargs;
+ node *tail;
+ /* mandatory arguments */
ma = node_len(tree->car->car);
- n = tree->car->car;
- while (n) {
- n = n->cdr;
- }
+ margs = tree->car->car;
+ tail = tree->car->cdr->cdr->cdr->cdr;
+
+ /* optional arguments */
oa = node_len(tree->car->cdr->car);
+ /* rest argument? */
ra = tree->car->cdr->cdr->car ? 1 : 0;
+ /* mandatory arugments after rest argument */
pa = node_len(tree->car->cdr->cdr->cdr->car);
- ka = kd = 0;
- ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
+ pargs = tree->car->cdr->cdr->cdr->car;
+ /* keyword arguments */
+ ka = tail? node_len(tail->cdr->car) : 0;
+ /* keyword dictionary? */
+ kd = tail && tail->cdr->cdr->car? 1 : 0;
+ /* block argument? */
+ ba = tail && tail->cdr->cdr->cdr->car ? 1 : 0;
if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) {
codegen_error(s, "too many formal arguments");
}
- a = ((mrb_aspec)(ma & 0x1f) << 18)
- | ((mrb_aspec)(oa & 0x1f) << 13)
- | ((ra & 1) << 12)
- | ((pa & 0x1f) << 7)
- | ((ka & 0x1f) << 2)
- | ((kd & 1)<< 1)
- | (ba & 1);
- s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */
- | ((ra & 1) << 5)
- | (pa & 0x1f);
- genop(s, MKOP_Ax(OP_ENTER, a));
+ a = MRB_ARGS_REQ(ma)
+ | MRB_ARGS_OPT(oa)
+ | (ra? MRB_ARGS_REST() : 0)
+ | MRB_ARGS_POST(pa)
+ | MRB_ARGS_KEY(ka, kd)
+ | (ba? MRB_ARGS_BLOCK() : 0);
+ s->ainfo = (((ma+oa) & 0x3f) << 7) /* (12bits = 5:1:5:1) */
+ | ((ra & 0x1) << 6)
+ | ((pa & 0x1f) << 1)
+ | (kd & 0x1);
+ genop_W(s, OP_ENTER, a);
+ /* generate jump table for optional arguments initializer */
pos = new_label(s);
for (i=0; i<oa; i++) {
new_label(s);
- genop(s, MKOP_sBx(OP_JMP, 0));
+ genjmp(s, OP_JMP, 0);
}
if (oa > 0) {
- genop(s, MKOP_sBx(OP_JMP, 0));
+ genjmp(s, OP_JMP, 0);
}
opt = tree->car->cdr->car;
i = 0;
while (opt) {
int idx;
- dispatch(s, pos+i);
+ dispatch(s, pos+i*3+1);
codegen(s, opt->car->cdr, VAL);
- idx = lv_idx(s, nsym(opt->car->car));
pop();
- genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL);
+ idx = lv_idx(s, nsym(opt->car->car));
+ gen_move(s, idx, cursp(), 0);
i++;
opt = opt->cdr;
}
if (oa > 0) {
- dispatch(s, pos+i);
+ dispatch(s, pos+i*3+1);
+ }
+
+ /* keyword arguments */
+ if (tail) {
+ node *kwds = tail->cdr->car;
+ int kwrest = 0;
+
+ if (tail->cdr->cdr->car) {
+ kwrest = 1;
+ }
+ mrb_assert(nint(tail->car) == NODE_ARGS_TAIL);
+ mrb_assert(node_len(tail) == 4);
+
+ while (kwds) {
+ int jmpif_key_p, jmp_def_set = -1;
+ node *kwd = kwds->car, *def_arg = kwd->cdr->cdr->car;
+ mrb_sym kwd_sym = nsym(kwd->cdr->car);
+
+ mrb_assert(nint(kwd->car) == NODE_KW_ARG);
+
+ if (def_arg) {
+ genop_2(s, OP_KEY_P, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
+ jmpif_key_p = genjmp2(s, OP_JMPIF, lv_idx(s, kwd_sym), 0, 0);
+ codegen(s, def_arg, VAL);
+ pop();
+ gen_move(s, lv_idx(s, kwd_sym), cursp(), 0);
+ jmp_def_set = genjmp(s, OP_JMP, 0);
+ dispatch(s, jmpif_key_p);
+ }
+ genop_2(s, OP_KARG, lv_idx(s, kwd_sym), new_sym(s, kwd_sym));
+ if (jmp_def_set != -1) {
+ dispatch(s, jmp_def_set);
+ }
+ i++;
+
+ kwds = kwds->cdr;
+ }
+ if (tail->cdr->car && !kwrest) {
+ genop_0(s, OP_KEYEND);
+ }
+ }
+
+ /* argument destructuring */
+ if (margs) {
+ node *n = margs;
+
+ pos = 1;
+ while (n) {
+ if (nint(n->car->car) == NODE_MASGN) {
+ gen_vmassignment(s, n->car->cdr->car, pos, NOVAL);
+ }
+ pos++;
+ n = n->cdr;
+ }
+ }
+ if (pargs) {
+ node *n = margs;
+
+ pos = ma+oa+ra+1;
+ while (n) {
+ if (nint(n->car->car) == NODE_MASGN) {
+ gen_vmassignment(s, n->car->cdr->car, pos, NOVAL);
+ }
+ pos++;
+ n = n->cdr;
+ }
}
}
+
codegen(s, tree->cdr->car, VAL);
pop();
if (s->pc > 0) {
- c = s->iseq[s->pc-1];
- if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) {
- if (s->nregs == 0) {
- genop(s, MKOP_A(OP_LOADNIL, 0));
- genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
- }
- else {
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
- }
- }
+ gen_return(s, OP_RETURN, cursp());
}
if (blk) {
loop_pop(s, NOVAL);
@@ -773,24 +889,13 @@ 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_error(s, "unexpected scope");
}
codegen(scope, tree->cdr, VAL);
+ gen_return(scope, OP_RETURN, scope->sp-1);
if (!s->iseq) {
- genop(scope, MKOP_A(OP_STOP, 0));
- }
- else if (!val) {
- genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
- }
- else {
- if (scope->nregs == 0) {
- genop(scope, MKOP_A(OP_LOADNIL, 0));
- genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
- }
- else {
- genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL);
- }
+ genop_0(scope, OP_STOP);
}
scope_finish(scope);
if (!s->irep) {
@@ -800,9 +905,6 @@ scope_body(codegen_scope *s, node *tree, int val)
return s->irep->rlen - 1;
}
-#define nint(x) ((int)(intptr_t)(x))
-#define nchar(x) ((char)(intptr_t)(x))
-
static mrb_bool
nosplat(node *t)
{
@@ -854,15 +956,15 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
}
else {
pop_n(n);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+ genop_2(s, OP_ARRAY, cursp(), n);
push();
codegen(s, t->car, VAL);
pop(); pop();
if (is_splat) {
- genop_peep(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1), NOVAL);
+ genop_1(s, OP_ARYCAT, cursp());
}
else {
- genop_peep(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1), NOVAL);
+ genop_1(s, OP_ARYPUSH, cursp());
}
}
t = t->cdr;
@@ -871,10 +973,10 @@ gen_values(codegen_scope *s, node *t, int val, int extra)
codegen(s, t->car, VAL);
pop(); pop();
if (nint(t->car->car) == NODE_SPLAT) {
- genop_peep(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1), NOVAL);
+ genop_1(s, OP_ARYCAT, cursp());
}
else {
- genop_peep(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1), NOVAL);
+ genop_1(s, OP_ARYPUSH, cursp());
}
t = t->cdr;
}
@@ -899,22 +1001,15 @@ static void
gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
{
mrb_sym sym = name ? name : nsym(tree->cdr->car);
- int idx, skip = 0;
+ int skip = 0;
int n = 0, noop = 0, sendv = 0, blk = 0;
codegen(s, tree->car, VAL); /* receiver */
if (safe) {
int recv = cursp()-1;
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push();
- genop(s, MKOP_AB(OP_MOVE, cursp(), recv));
- push_n(2); pop_n(2); /* space for one arg and a block */
- pop();
- idx = new_msym(s, mrb_intern_lit(s->mrb, "=="));
- genop(s, MKOP_ABC(OP_EQ, cursp(), idx, 1));
- skip = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+ gen_move(s, cursp(), recv, 1);
+ skip = genjmp2(s, OP_JMPNIL, cursp(), 0, val);
}
- idx = new_msym(s, sym);
tree = tree->cdr->cdr->car;
if (tree) {
n = gen_values(s, tree->car, VAL, sp?1:0);
@@ -923,14 +1018,15 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
push();
}
}
- if (sp) {
+ if (sp) { /* last argument pushed (attr=) */
if (sendv) {
+ gen_move(s, cursp(), sp, 0);
pop();
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp));
+ genop_1(s, OP_ARYPUSH, cursp());
push();
}
else {
- genop(s, MKOP_AB(OP_MOVE, cursp(), sp));
+ gen_move(s, cursp(), sp, 0);
push();
n++;
}
@@ -939,9 +1035,7 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
noop = 1;
codegen(s, tree->cdr, VAL);
pop();
- }
- else {
- blk = cursp();
+ blk = 1;
}
push();pop();
pop_n(n+1);
@@ -950,39 +1044,40 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
if (!noop && symlen == 1 && symname[0] == '+' && n == 1) {
- genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val);
+ gen_addsub(s, OP_ADD, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '-' && n == 1) {
- genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val);
+ gen_addsub(s, OP_SUB, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '*' && n == 1) {
- genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n));
+ genop_1(s, OP_MUL, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '/' && n == 1) {
- genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n));
+ genop_1(s, OP_DIV, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '<' && n == 1) {
- genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
+ genop_1(s, OP_LT, cursp());
}
else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1) {
- genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
+ genop_1(s, OP_LE, cursp());
}
else if (!noop && symlen == 1 && symname[0] == '>' && n == 1) {
- genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
+ genop_1(s, OP_GT, cursp());
}
else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1) {
- genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
+ genop_1(s, OP_GE, cursp());
}
else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1) {
- genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
+ genop_1(s, OP_EQ, cursp());
}
else {
- if (sendv) n = CALL_MAXARGS;
- if (blk > 0) { /* no block */
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n));
+ int idx = new_sym(s, sym);
+
+ if (sendv) {
+ genop_2(s, blk ? OP_SENDVB : OP_SENDV, cursp(), idx);
}
else {
- genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n));
+ genop_3(s, blk ? OP_SENDB : OP_SEND, cursp(), idx, n);
}
}
}
@@ -1004,13 +1099,15 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
switch (type) {
case NODE_GVAR:
idx = new_sym(s, nsym(tree));
- genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
+ genop_2(s, OP_SETGV, sp, idx);
break;
+ case NODE_ARG:
case NODE_LVAR:
idx = lv_idx(s, nsym(tree));
if (idx > 0) {
if (idx != sp) {
- genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
+ gen_move(s, idx, sp, val);
+ if (val && on_eval(s)) genop_0(s, OP_NOP);
}
break;
}
@@ -1021,7 +1118,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
while (up) {
idx = lv_idx(up, nsym(tree));
if (idx > 0) {
- genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
+ genop_3(s, OP_SETUPVAR, sp, idx, lv);
break;
}
lv++;
@@ -1031,23 +1128,23 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
break;
case NODE_IVAR:
idx = new_sym(s, nsym(tree));
- genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
+ genop_2(s, OP_SETIV, sp, idx);
break;
case NODE_CVAR:
idx = new_sym(s, nsym(tree));
- genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
+ genop_2(s, OP_SETCV, sp, idx);
break;
case NODE_CONST:
idx = new_sym(s, nsym(tree));
- genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
+ genop_2(s, OP_SETCONST, sp, idx);
break;
case NODE_COLON2:
- idx = new_sym(s, nsym(tree->cdr));
- genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
+ gen_move(s, cursp(), sp, 0);
push();
codegen(s, tree->car, VAL);
pop_n(2);
- genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val);
+ idx = new_sym(s, nsym(tree->cdr));
+ genop_2(s, OP_SETMCNST, sp, idx);
break;
case NODE_CALL:
@@ -1057,7 +1154,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val)
type == NODE_SCALL);
pop();
if (val) {
- genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
+ gen_move(s, cursp(), sp, 0);
}
break;
@@ -1090,7 +1187,7 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
while (t) {
int sp = cursp();
- genop(s, MKOP_ABC(OP_AREF, sp, rhs, n));
+ genop_3(s, OP_AREF, sp, rhs, n);
push();
gen_assignment(s, t->car, sp, NOVAL);
pop();
@@ -1107,12 +1204,12 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
p = p->cdr;
}
}
- genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
+ gen_move(s, cursp(), rhs, val);
push_n(post+1);
pop_n(post+1);
- genop(s, MKOP_ABC(OP_APOST, cursp(), n, post));
+ genop_3(s, OP_APOST, cursp(), n, post);
n = 1;
- if (t->car) { /* rest */
+ if (t->car && t->car != (node*)-1) { /* rest */
gen_assignment(s, t->car, cursp(), NOVAL);
}
if (t->cdr && t->cdr->car) {
@@ -1124,19 +1221,19 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
}
}
if (val) {
- genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
+ gen_move(s, cursp(), rhs, 0);
}
}
}
static void
-gen_send_intern(codegen_scope *s)
+gen_intern(codegen_scope *s)
{
- push();pop(); /* space for a block */
pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0));
+ genop_1(s, OP_INTERN, cursp());
push();
}
+
static void
gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
{
@@ -1159,25 +1256,25 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
j = 0;
++i;
if (sym)
- gen_send_intern(s);
+ gen_intern(s);
}
break;
}
- if (j >= 2) {
+ while (j >= 2) {
pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ genop_1(s, OP_STRCAT, cursp());
push();
- j = 1;
+ j--;
}
tree = tree->cdr;
}
if (j > 0) {
++i;
if (sym)
- gen_send_intern(s);
+ gen_intern(s);
}
pop_n(i);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i));
+ genop_2(s, OP_ARRAY, cursp(), i);
push();
}
else {
@@ -1196,7 +1293,7 @@ raise_error(codegen_scope *s, const char *msg)
{
int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg));
- genop(s, MKOP_ABx(OP_ERR, 1, idx));
+ genop_1(s, OP_ERR, idx);
}
#ifndef MRB_WITHOUT_FLOAT
@@ -1274,11 +1371,9 @@ static void
gen_retval(codegen_scope *s, node *tree)
{
if (nint(tree->car) == NODE_SPLAT) {
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), 0));
- push();
codegen(s, tree, VAL);
- pop(); pop();
- genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ pop();
+ genop_1(s, OP_ARYDUP, cursp());
}
else {
codegen(s, tree, VAL);
@@ -1294,7 +1389,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (!tree) {
if (val) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
return;
@@ -1305,11 +1400,14 @@ codegen(codegen_scope *s, node *tree, int val)
codegen_error(s, "too complex expression");
}
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);
+ mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index);
+ const char *filename = mrb_sym2name_len(s->mrb, fname, NULL);
+
+ mrb_debug_info_append_file(s->mrb, s->irep->debug_info,
+ filename, s->lines, s->debug_start_pos, s->pc);
s->debug_start_pos = s->pc;
s->filename_index = tree->filename_index;
- s->filename = mrb_parser_get_filename(s->parser, tree->filename_index);
+ s->filename_sym = mrb_parser_get_filename(s->parser, tree->filename_index);
}
nt = nint(tree->car);
@@ -1318,7 +1416,7 @@ codegen(codegen_scope *s, node *tree, int val)
switch (nt) {
case NODE_BEGIN:
if (val && !tree) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
while (tree) {
@@ -1329,18 +1427,18 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_RESCUE:
{
- int onerr, noexc, exend, pos1, pos2, tmp;
+ int noexc, exend, pos1, pos2, tmp;
struct loopinfo *lp;
if (tree->car == NULL) goto exit;
- onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
lp = loop_push(s, LOOP_BEGIN);
- lp->pc1 = onerr;
+ lp->pc0 = new_label(s);
+ lp->pc1 = genjmp(s, OP_ONERR, 0);
codegen(s, tree->car, VAL);
pop();
lp->type = LOOP_RESCUE;
- noexc = genop(s, MKOP_Bx(OP_JMP, 0));
- dispatch(s, onerr);
+ noexc = genjmp(s, OP_JMP, 0);
+ dispatch(s, lp->pc1);
tree = tree->cdr;
exend = 0;
pos1 = 0;
@@ -1348,7 +1446,7 @@ codegen(codegen_scope *s, node *tree, int val)
node *n2 = tree->car;
int exc = cursp();
- genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
+ genop_1(s, OP_EXCEPT, exc);
push();
while (n2) {
node *n3 = n2->car;
@@ -1359,30 +1457,29 @@ codegen(codegen_scope *s, node *tree, int val)
do {
if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) {
codegen(s, n4->car, VAL);
- genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
+ gen_move(s, cursp(), exc, 0);
push_n(2); pop_n(2); /* space for one arg and a block */
pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+ genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1);
}
else {
if (n4) {
codegen(s, n4->car, VAL);
}
else {
- genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError"))));
+ genop_2(s, OP_GETCONST, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "StandardError")));
push();
}
pop();
- genop(s, MKOP_ABC(OP_RESCUE, exc, cursp(), 1));
+ genop_2(s, OP_RESCUE, exc, cursp());
}
- distcheck(s, pos2);
- tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+ tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, val);
pos2 = tmp;
if (n4) {
n4 = n4->cdr;
}
} while (n4);
- pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ pos1 = genjmp(s, OP_JMP, 0);
dispatch_linked(s, pos2);
pop();
@@ -1393,21 +1490,20 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, n3->cdr->cdr->car, val);
if (val) pop();
}
- distcheck(s, exend);
- tmp = genop(s, MKOP_sBx(OP_JMP, exend));
+ tmp = genjmp(s, OP_JMP, exend);
exend = tmp;
n2 = n2->cdr;
push();
}
if (pos1) {
dispatch(s, pos1);
- genop(s, MKOP_A(OP_RAISE, exc));
+ genop_1(s, OP_RAISE, exc);
}
}
pop();
tree = tree->cdr;
dispatch(s, noexc);
- genop(s, MKOP_A(OP_POPERR, 1));
+ genop_1(s, OP_POPERR, 1);
if (tree->car) {
codegen(s, tree->car, val);
}
@@ -1424,15 +1520,13 @@ codegen(codegen_scope *s, node *tree, int val)
(nint(tree->cdr->cdr->car) == NODE_BEGIN &&
tree->cdr->cdr->cdr)) {
int idx;
- int epush = s->pc;
- genop(s, MKOP_Bx(OP_EPUSH, 0));
s->ensure_level++;
- codegen(s, tree->car, val);
idx = scope_body(s, tree->cdr, NOVAL);
- s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx);
+ genop_1(s, OP_EPUSH, idx);
+ codegen(s, tree->car, val);
s->ensure_level--;
- genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
+ genop_1(s, OP_EPOP, 1);
}
else { /* empty ensure ignored */
codegen(s, tree->car, val);
@@ -1443,7 +1537,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (val) {
int idx = lambda_body(s, tree, 1);
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA));
+ genop_2(s, OP_LAMBDA, cursp(), idx);
push();
}
break;
@@ -1452,7 +1546,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (val) {
int idx = lambda_body(s, tree, 1);
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
+ genop_2(s, OP_BLOCK, cursp(), idx);
push();
}
break;
@@ -1460,10 +1554,10 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_IF:
{
int pos1, pos2;
- node *e = tree->cdr->cdr->car;
+ node *elsepart = tree->cdr->cdr->car;
if (!tree->car) {
- codegen(s, e, val);
+ codegen(s, elsepart, val);
goto exit;
}
switch (nint(tree->car->car)) {
@@ -1474,27 +1568,27 @@ codegen(codegen_scope *s, node *tree, int val)
goto exit;
case NODE_FALSE:
case NODE_NIL:
- codegen(s, e, val);
+ codegen(s, elsepart, val);
goto exit;
}
codegen(s, tree->car, VAL);
pop();
- pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL);
+ pos1 = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
codegen(s, tree->cdr->car, val);
- if (e) {
+ if (elsepart) {
if (val) pop();
- pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+ pos2 = genjmp(s, OP_JMP, 0);
dispatch(s, pos1);
- codegen(s, e, val);
+ codegen(s, elsepart, val);
dispatch(s, pos2);
}
else {
if (val) {
pop();
- pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+ pos2 = genjmp(s, OP_JMP, 0);
dispatch(s, pos1);
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
dispatch(s, pos2);
push();
}
@@ -1511,7 +1605,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->car, VAL);
pop();
- pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
+ pos = genjmp2(s, OP_JMPNOT, cursp(), 0, val);
codegen(s, tree->cdr, val);
dispatch(s, pos);
}
@@ -1523,7 +1617,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->car, VAL);
pop();
- pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+ pos = genjmp2(s, OP_JMPIF, cursp(), 0, val);
codegen(s, tree->cdr, val);
dispatch(s, pos);
}
@@ -1533,14 +1627,14 @@ codegen(codegen_scope *s, node *tree, int val)
{
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
- lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ lp->pc0 = new_label(s);
+ lp->pc1 = genjmp(s, OP_JMP, 0);
lp->pc2 = new_label(s);
codegen(s, tree->cdr, NOVAL);
dispatch(s, lp->pc1);
codegen(s, tree->car, VAL);
pop();
- distcheck(s, lp->pc2 - s->pc);
- genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc));
+ genjmp2(s, OP_JMPIF, cursp(), lp->pc2, NOVAL);
loop_pop(s, val);
}
@@ -1550,14 +1644,14 @@ codegen(codegen_scope *s, node *tree, int val)
{
struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
- lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ lp->pc0 = new_label(s);
+ lp->pc1 = genjmp(s, OP_JMP, 0);
lp->pc2 = new_label(s);
codegen(s, tree->cdr, NOVAL);
dispatch(s, lp->pc1);
codegen(s, tree->car, VAL);
pop();
- distcheck(s, lp->pc2 - s->pc);
- genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc));
+ genjmp2(s, OP_JMPNOT, cursp(), lp->pc2, NOVAL);
loop_pop(s, val);
}
@@ -1586,43 +1680,40 @@ codegen(codegen_scope *s, node *tree, int val)
while (n) {
codegen(s, n->car, VAL);
if (head) {
- genop(s, MKOP_AB(OP_MOVE, cursp(), head));
- push_n(2); pop_n(2); /* space for one arg and a block */
- pop();
+ gen_move(s, cursp(), head, 0);
+ push(); push(); pop(); pop(); pop();
if (nint(n->car->car) == NODE_SPLAT) {
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+ genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1);
}
else {
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1));
+ genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "===")), 1);
}
}
else {
pop();
}
- distcheck(s, pos2);
- tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+ tmp = genjmp2(s, OP_JMPIF, cursp(), pos2, NOVAL);
pos2 = tmp;
n = n->cdr;
}
if (tree->car->car) {
- pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ pos1 = genjmp(s, OP_JMP, 0);
dispatch_linked(s, pos2);
}
codegen(s, tree->car->cdr, val);
if (val) pop();
- distcheck(s, pos3);
- tmp = genop(s, MKOP_sBx(OP_JMP, pos3));
+ tmp = genjmp(s, OP_JMP, pos3);
pos3 = tmp;
if (pos1) dispatch(s, pos1);
tree = tree->cdr;
}
if (val) {
int pos = cursp();
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
if (pos3) dispatch_linked(s, pos3);
if (head) pop();
if (cursp() != pos) {
- genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+ gen_move(s, cursp(), pos, 0);
}
push();
}
@@ -1654,7 +1745,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->cdr, val);
if (val) {
pop(); pop();
- genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE));
+ genop_1(s, OP_RANGE_INC, cursp());
push();
}
break;
@@ -1664,7 +1755,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->cdr, val);
if (val) {
pop(); pop();
- genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE));
+ genop_1(s, OP_RANGE_EXC, cursp());
push();
}
break;
@@ -1675,7 +1766,7 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->car, VAL);
pop();
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ genop_2(s, OP_GETMCNST, cursp(), sym);
if (val) push();
}
break;
@@ -1684,8 +1775,8 @@ codegen(codegen_scope *s, node *tree, int val)
{
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ genop_1(s, OP_OCLASS, cursp());
+ genop_2(s, OP_GETMCNST, cursp(), sym);
if (val) push();
}
break;
@@ -1698,7 +1789,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (n >= 0) {
if (val) {
pop_n(n);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+ genop_2(s, OP_ARRAY, cursp(), n);
push();
}
}
@@ -1709,21 +1800,47 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_HASH:
+ case NODE_KW_HASH:
{
int len = 0;
mrb_bool update = FALSE;
while (tree) {
- codegen(s, tree->car->car, val);
- codegen(s, tree->car->cdr, val);
- len++;
+ if (nint(tree->car->car->car) == NODE_KW_REST_ARGS) {
+ if (len > 0) {
+ pop_n(len*2);
+ if (!update) {
+ genop_2(s, OP_HASH, cursp(), len);
+ }
+ else {
+ pop();
+ genop_2(s, OP_HASHADD, cursp(), len);
+ }
+ push();
+ }
+ codegen(s, tree->car->cdr, VAL);
+ if (len > 0 || update) {
+ pop(); pop();
+ genop_1(s, OP_HASHCAT, cursp());
+ push();
+ }
+ update = TRUE;
+ len = 0;
+ }
+ else {
+ codegen(s, tree->car->car, val);
+ codegen(s, tree->car->cdr, val);
+ len++;
+ }
tree = tree->cdr;
- if (val && len == 126) {
+ if (val && len == 255) {
pop_n(len*2);
- genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
- if (update) {
+ if (!update) {
+ genop_2(s, OP_HASH, cursp(), len);
+ }
+ else {
pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+ genop_2(s, OP_HASHADD, cursp(), len);
}
push();
update = TRUE;
@@ -1732,10 +1849,14 @@ codegen(codegen_scope *s, node *tree, int val)
}
if (val) {
pop_n(len*2);
- genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
- if (update) {
+ if (!update) {
+ genop_2(s, OP_HASH, cursp(), len);
+ }
+ else {
pop();
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+ if (len > 0) {
+ genop_2(s, OP_HASHADD, cursp(), len);
+ }
}
push();
}
@@ -1776,7 +1897,7 @@ codegen(codegen_scope *s, node *tree, int val)
n++;
}
else {
- genop(s, MKOP_A(OP_LOADNIL, rhs+n));
+ genop_1(s, OP_LOADNIL, rhs+n);
gen_assignment(s, t->car, rhs+n, NOVAL);
}
t = t->cdr;
@@ -1800,7 +1921,7 @@ codegen(codegen_scope *s, node *tree, int val)
else {
rn = len - post - n;
}
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn));
+ genop_3(s, OP_ARRAY2, cursp(), rhs+n, rn);
gen_assignment(s, t->car, cursp(), NOVAL);
n += rn;
}
@@ -1815,7 +1936,7 @@ codegen(codegen_scope *s, node *tree, int val)
}
pop_n(len);
if (val) {
- genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len));
+ genop_2(s, OP_ARRAY, rhs, len);
push();
}
}
@@ -1843,17 +1964,17 @@ codegen(codegen_scope *s, node *tree, int val)
int onerr, noexc, exc;
struct loopinfo *lp;
- onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
+ onerr = genjmp(s, OP_ONERR, 0);
lp = loop_push(s, LOOP_BEGIN);
lp->pc1 = onerr;
exc = cursp();
codegen(s, tree->car, VAL);
lp->type = LOOP_RESCUE;
- genop(s, MKOP_A(OP_POPERR, 1));
- noexc = genop(s, MKOP_Bx(OP_JMP, 0));
+ genop_1(s, OP_POPERR, 1);
+ noexc = genjmp(s, OP_JMP, 0);
dispatch(s, onerr);
- genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
- genop(s, MKOP_A(OP_LOADF, exc));
+ genop_1(s, OP_EXCEPT, exc);
+ genop_1(s, OP_LOADF, exc);
dispatch(s, noexc);
loop_pop(s, NOVAL);
}
@@ -1867,7 +1988,7 @@ codegen(codegen_scope *s, node *tree, int val)
push();
}
codegen(s, n->car, VAL); /* receiver */
- idx = new_msym(s, nsym(n->cdr->car));
+ idx = new_sym(s, nsym(n->cdr->car));
base = cursp()-1;
if (n->cdr->cdr->car) {
nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1);
@@ -1881,12 +2002,12 @@ codegen(codegen_scope *s, node *tree, int val)
}
}
/* copy receiver and arguments */
- genop(s, MKOP_AB(OP_MOVE, cursp(), base));
+ gen_move(s, cursp(), base, 1);
for (i=0; i<nargs; i++) {
- genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1));
+ gen_move(s, cursp()+i+1, base+i+1, 1);
}
push_n(nargs+2);pop_n(nargs+2); /* space for receiver, arguments and a block */
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ genop_3(s, OP_SEND, cursp(), idx, callargs);
push();
}
else {
@@ -1900,30 +2021,30 @@ codegen(codegen_scope *s, node *tree, int val)
pop();
if (val) {
if (vsp >= 0) {
- genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ gen_move(s, vsp, cursp(), 1);
}
- pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0));
+ pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val);
}
else {
- pos = genop_peep(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0), NOVAL);
+ pos = genjmp2(s, name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0, val);
}
codegen(s, tree->cdr->cdr->car, VAL);
pop();
if (val && vsp >= 0) {
- genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ gen_move(s, vsp, cursp(), 1);
}
if (nint(tree->car->car) == NODE_CALL) {
if (callargs == CALL_MAXARGS) {
pop();
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ genop_1(s, OP_ARYPUSH, cursp());
}
else {
pop_n(callargs);
callargs++;
}
pop();
- idx = new_msym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ idx = new_sym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
+ genop_3(s, OP_SEND, cursp(), idx, callargs);
}
else {
gen_assignment(s, tree->car, cursp(), val);
@@ -1935,52 +2056,52 @@ codegen(codegen_scope *s, node *tree, int val)
push(); pop();
pop(); pop();
- idx = new_msym(s, sym);
if (len == 1 && name[0] == '+') {
- genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val);
+ gen_addsub(s, OP_ADD, cursp());
}
else if (len == 1 && name[0] == '-') {
- genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val);
+ gen_addsub(s, OP_SUB, cursp());
}
else if (len == 1 && name[0] == '*') {
- genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1));
+ genop_1(s, OP_MUL, cursp());
}
else if (len == 1 && name[0] == '/') {
- genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1));
+ genop_1(s, OP_DIV, cursp());
}
else if (len == 1 && name[0] == '<') {
- genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1));
+ genop_1(s, OP_LT, cursp());
}
else if (len == 2 && name[0] == '<' && name[1] == '=') {
- genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1));
+ genop_1(s, OP_LE, cursp());
}
else if (len == 1 && name[0] == '>') {
- genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1));
+ genop_1(s, OP_GT, cursp());
}
else if (len == 2 && name[0] == '>' && name[1] == '=') {
- genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1));
+ genop_1(s, OP_GE, cursp());
}
else {
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1));
+ idx = new_sym(s, sym);
+ genop_3(s, OP_SEND, cursp(), idx, 1);
}
if (callargs < 0) {
gen_assignment(s, tree->car, cursp(), val);
}
else {
if (val && vsp >= 0) {
- genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ gen_move(s, vsp, cursp(), 0);
}
if (callargs == CALL_MAXARGS) {
pop();
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ genop_1(s, OP_ARYPUSH, cursp());
}
else {
pop_n(callargs);
callargs++;
}
pop();
- idx = new_msym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
- genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ idx = new_sym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
+ genop_3(s, OP_SEND, cursp(), idx, callargs);
}
}
break;
@@ -1997,7 +2118,7 @@ codegen(codegen_scope *s, node *tree, int val)
s2 = s2->prev;
if (!s2) break;
}
- genop(s, MKOP_ABx(OP_ARGARY, cursp(), (lv & 0xf)));
+ genop_2S(s, OP_ARGARY, cursp(), (lv & 0xf));
push(); push(); /* ARGARY pushes two values */
pop(); pop();
if (tree) {
@@ -2015,12 +2136,12 @@ codegen(codegen_scope *s, node *tree, int val)
pop();
}
else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push(); pop();
}
pop_n(n+1);
if (sendv) n = CALL_MAXARGS;
- genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n));
+ genop_2(s, OP_SUPER, cursp(), n);
if (val) push();
}
break;
@@ -2037,14 +2158,14 @@ codegen(codegen_scope *s, node *tree, int val)
if (!s2) break;
}
if (s2) ainfo = s2->ainfo;
- genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)));
+ genop_2S(s, OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf));
push(); push(); pop(); /* ARGARY pushes two values */
if (tree && tree->cdr) {
codegen(s, tree->cdr, VAL);
pop();
}
pop(); pop();
- genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS));
+ genop_2(s, OP_SUPER, cursp(), CALL_MAXARGS);
if (val) push();
}
break;
@@ -2054,13 +2175,13 @@ codegen(codegen_scope *s, node *tree, int val)
gen_retval(s, tree);
}
else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
}
if (s->loop) {
- genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN));
+ gen_return(s, OP_RETURN_BLK, cursp());
}
else {
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ gen_return(s, OP_RETURN, cursp());
}
if (val) push();
break;
@@ -2087,9 +2208,9 @@ codegen(codegen_scope *s, node *tree, int val)
}
push();pop(); /* space for a block */
pop_n(n+1);
- genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
+ genop_2S(s, OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf));
if (sendv) n = CALL_MAXARGS;
- genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n));
+ genop_3(s, OP_SEND, cursp(), new_sym(s, mrb_intern_lit(s->mrb, "call")), n);
if (val) push();
}
break;
@@ -2105,11 +2226,10 @@ codegen(codegen_scope *s, node *tree, int val)
}
else if (s->loop->type == LOOP_NORMAL) {
if (s->ensure_level > s->loop->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
}
codegen(s, tree, NOVAL);
- distcheck(s, s->loop->pc1 - s->pc);
- genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc));
+ genjmp(s, OP_JMP, s->loop->pc0);
}
else {
if (tree) {
@@ -2117,9 +2237,9 @@ codegen(codegen_scope *s, node *tree, int val)
pop();
}
else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
}
- genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ gen_return(s, OP_RETURN, cursp());
}
if (val) push();
break;
@@ -2130,10 +2250,9 @@ codegen(codegen_scope *s, node *tree, int val)
}
else {
if (s->ensure_level > s->loop->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
}
- distcheck(s, s->loop->pc2 - s->pc);
- genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
+ genjmp(s, OP_JMP, s->loop->pc2);
}
if (val) push();
break;
@@ -2160,13 +2279,12 @@ codegen(codegen_scope *s, node *tree, int val)
}
else {
if (n > 0) {
- genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+ genop_1(s, OP_POPERR, n);
}
if (s->ensure_level > lp->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL);
+ genop_1(s, OP_EPOP, s->ensure_level - lp->ensure_level);
}
- distcheck(s, s->loop->pc1 - s->pc);
- genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
+ genjmp(s, OP_JMP, lp->pc0);
}
}
if (val) push();
@@ -2178,7 +2296,8 @@ codegen(codegen_scope *s, node *tree, int val)
int idx = lv_idx(s, nsym(tree));
if (idx > 0) {
- genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL);
+ gen_move(s, cursp(), idx, val);
+ if (val && on_eval(s)) genop_0(s, OP_NOP);
}
else {
int lv = 0;
@@ -2187,7 +2306,7 @@ codegen(codegen_scope *s, node *tree, int val)
while (up) {
idx = lv_idx(up, nsym(tree));
if (idx > 0) {
- genop_peep(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv), VAL);
+ genop_3(s, OP_GETUPVAR, cursp(), idx, lv);
break;
}
lv++;
@@ -2199,29 +2318,29 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_GVAR:
- if (val) {
+ {
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
- push();
+ genop_2(s, OP_GETGV, cursp(), sym);
+ if (val) push();
}
break;
case NODE_IVAR:
- if (val) {
+ {
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
- push();
+ genop_2(s, OP_GETIV, cursp(), sym);
+ if (val) push();
}
break;
case NODE_CVAR:
- if (val) {
+ {
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
- push();
+ genop_2(s, OP_GETCV, cursp(), sym);
+ if (val) push();
}
break;
@@ -2229,27 +2348,21 @@ codegen(codegen_scope *s, node *tree, int val)
{
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
- if (val) {
- push();
- }
+ genop_2(s, OP_GETCONST, cursp(), sym);
+ if (val) push();
}
break;
case NODE_DEFINED:
- codegen(s, tree, VAL);
+ codegen(s, tree, val);
break;
case NODE_BACK_REF:
if (val) {
- char buf[3];
- int sym;
+ char buf[] = {'$', nchar(tree)};
+ int sym = new_sym(s, mrb_intern(s->mrb, buf, sizeof(buf)));
- buf[0] = '$';
- buf[1] = nchar(tree);
- buf[2] = 0;
- sym = new_sym(s, mrb_intern_cstr(s->mrb, buf));
- genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ genop_2(s, OP_GETGV, cursp(), sym);
push();
}
break;
@@ -2262,7 +2375,7 @@ codegen(codegen_scope *s, node *tree, int val)
str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree)));
sym = new_sym(s, mrb_intern_str(mrb, str));
- genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ genop_2(s, OP_GETGV, cursp(), sym);
push();
}
break;
@@ -2272,7 +2385,7 @@ codegen(codegen_scope *s, node *tree, int val)
break;
case NODE_BLOCK_ARG:
- codegen(s, tree, VAL);
+ codegen(s, tree, val);
break;
case NODE_INT:
@@ -2280,7 +2393,6 @@ codegen(codegen_scope *s, node *tree, int val)
char *p = (char*)tree->car;
int base = nint(tree->cdr->car);
mrb_int i;
- mrb_code co;
mrb_bool overflow;
i = readint_mrb_int(s, p, base, FALSE, &overflow);
@@ -2289,19 +2401,19 @@ codegen(codegen_scope *s, node *tree, int val)
double f = readint_float(s, p, base);
int off = new_lit(s, mrb_float_value(s->mrb, f));
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ genop_2(s, OP_LOADL, cursp(), off);
}
else
#endif
{
- if (i < MAXARG_sBx && i > -MAXARG_sBx) {
- co = MKOP_AsBx(OP_LOADI, cursp(), i);
- }
+ if (i == -1) genop_1(s, OP_LOADI__1, cursp());
+ else if (i < 0) genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i);
+ else if (i < 8) genop_1(s, OP_LOADI_0 + (uint8_t)i, cursp());
+ else if (i <= 0xffff) genop_2(s, OP_LOADI, cursp(), (uint16_t)i);
else {
int off = new_lit(s, mrb_fixnum_value(i));
- co = MKOP_ABx(OP_LOADL, cursp(), off);
+ genop_2(s, OP_LOADL, cursp(), off);
}
- genop(s, co);
}
push();
}
@@ -2314,7 +2426,7 @@ codegen(codegen_scope *s, node *tree, int val)
mrb_float f = mrb_float_read(p, NULL);
int off = new_lit(s, mrb_float_value(s->mrb, f));
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ genop_2(s, OP_LOADL, cursp(), off);
push();
}
break;
@@ -2323,16 +2435,15 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_NEGATE:
{
nt = nint(tree->car);
- tree = tree->cdr;
switch (nt) {
#ifndef MRB_WITHOUT_FLOAT
case NODE_FLOAT:
if (val) {
- char *p = (char*)tree;
+ char *p = (char*)tree->cdr;
mrb_float f = mrb_float_read(p, NULL);
int off = new_lit(s, mrb_float_value(s->mrb, -f));
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ genop_2(s, OP_LOADL, cursp(), off);
push();
}
break;
@@ -2340,10 +2451,9 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_INT:
if (val) {
- char *p = (char*)tree->car;
- int base = nint(tree->cdr->car);
+ char *p = (char*)tree->cdr->car;
+ int base = nint(tree->cdr->cdr->car);
mrb_int i;
- mrb_code co;
mrb_bool overflow;
i = readint_mrb_int(s, p, base, TRUE, &overflow);
@@ -2352,18 +2462,18 @@ codegen(codegen_scope *s, node *tree, int val)
double f = readint_float(s, p, base);
int off = new_lit(s, mrb_float_value(s->mrb, -f));
- genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ genop_2(s, OP_LOADL, cursp(), off);
}
else {
#endif
- if (i < MAXARG_sBx && i > -MAXARG_sBx) {
- co = MKOP_AsBx(OP_LOADI, cursp(), i);
+ if (i == -1) genop_1(s, OP_LOADI__1, cursp());
+ else if (i >= -0xffff) {
+ genop_2(s, OP_LOADINEG, cursp(), (uint16_t)-i);
}
else {
int off = new_lit(s, mrb_fixnum_value(i));
- co = MKOP_ABx(OP_LOADL, cursp(), off);
+ genop_2(s, OP_LOADL, cursp(), off);
}
- genop(s, co);
#ifndef MRB_WITHOUT_FLOAT
}
#endif
@@ -2373,13 +2483,11 @@ codegen(codegen_scope *s, node *tree, int val)
default:
if (val) {
- int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
-
- genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
- push();
+ int sym = new_sym(s, mrb_intern_lit(s->mrb, "-@"));
codegen(s, tree, VAL);
- pop(); pop();
- genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
+ pop();
+ genop_3(s, OP_SEND, cursp(), sym, 0);
+ push();
}
else {
codegen(s, tree, NOVAL);
@@ -2397,7 +2505,7 @@ codegen(codegen_scope *s, node *tree, int val)
int off = new_lit(s, mrb_str_new(s->mrb, p, len));
mrb_gc_arena_restore(s->mrb, ai);
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push();
}
break;
@@ -2410,7 +2518,7 @@ codegen(codegen_scope *s, node *tree, int val)
node *n = tree;
if (!n) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
break;
}
@@ -2419,7 +2527,7 @@ codegen(codegen_scope *s, node *tree, int val)
while (n) {
codegen(s, n->car, VAL);
pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ genop_1(s, OP_STRCAT, cursp());
push();
n = n->cdr;
}
@@ -2450,7 +2558,7 @@ codegen(codegen_scope *s, node *tree, int val)
int ai = mrb_gc_arena_save(s->mrb);
int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
- genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ genop_1(s, OP_LOADSELF, cursp());
push();
codegen(s, tree->car, VAL);
n = tree->cdr;
@@ -2461,14 +2569,14 @@ codegen(codegen_scope *s, node *tree, int val)
}
codegen(s, n->car, VAL);
pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ genop_1(s, OP_STRCAT, cursp());
push();
n = n->cdr;
}
push(); /* for block */
pop_n(3);
sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
+ genop_3(s, OP_SEND, cursp(), sym, 1);
if (val) push();
mrb_gc_arena_restore(s->mrb, ai);
}
@@ -2482,13 +2590,13 @@ codegen(codegen_scope *s, node *tree, int val)
int off = new_lit(s, mrb_str_new(s->mrb, p, len));
int sym;
- genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ genop_1(s, OP_LOADSELF, cursp());
push();
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push(); push();
pop_n(3);
sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
+ genop_3(s, OP_SEND, cursp(), sym, 1);
if (val) push();
mrb_gc_arena_restore(s->mrb, ai);
}
@@ -2504,24 +2612,24 @@ codegen(codegen_scope *s, node *tree, int val)
int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1));
int argc = 1;
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ genop_1(s, OP_OCLASS, cursp());
+ genop_2(s, OP_GETMCNST, cursp(), sym);
push();
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push();
if (p2 || p3) {
if (p2) { /* opt */
off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
}
else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
}
push();
argc++;
if (p3) { /* enc */
off = new_lit(s, mrb_str_new(s->mrb, p3, 1));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push();
argc++;
}
@@ -2529,7 +2637,7 @@ codegen(codegen_scope *s, node *tree, int val)
push(); /* space for a block */
pop_n(argc+2);
sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
+ genop_3(s, OP_SEND, cursp(), sym, argc);
mrb_gc_arena_restore(s->mrb, ai);
push();
}
@@ -2544,15 +2652,15 @@ codegen(codegen_scope *s, node *tree, int val)
int off;
char *p;
- genop(s, MKOP_A(OP_OCLASS, cursp()));
- genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ genop_1(s, OP_OCLASS, cursp());
+ genop_2(s, OP_GETMCNST, cursp(), sym);
push();
codegen(s, n->car, VAL);
n = n->cdr;
while (n) {
codegen(s, n->car, VAL);
pop(); pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ genop_1(s, OP_STRCAT, cursp());
push();
n = n->cdr;
}
@@ -2561,29 +2669,29 @@ codegen(codegen_scope *s, node *tree, int val)
p = (char*)n->car;
off = new_lit(s, mrb_str_new_cstr(s->mrb, p));
codegen(s, tree->car, VAL);
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
pop();
- genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ genop_1(s, OP_STRCAT, cursp());
push();
}
if (n->cdr->car) { /* opt */
char *p2 = (char*)n->cdr->car;
off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push();
argc++;
}
if (n->cdr->cdr) { /* enc */
char *p2 = (char*)n->cdr->cdr;
off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
- genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ genop_2(s, OP_STRING, cursp(), off);
push();
argc++;
}
push(); /* space for a block */
pop_n(argc+2);
sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
- genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
+ genop_3(s, OP_SEND, cursp(), sym, argc);
mrb_gc_arena_restore(s->mrb, ai);
push();
}
@@ -2603,7 +2711,7 @@ codegen(codegen_scope *s, node *tree, int val)
if (val) {
int sym = new_sym(s, nsym(tree));
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ genop_2(s, OP_LOADSYM, cursp(), sym);
push();
}
break;
@@ -2611,55 +2719,46 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_DSYM:
codegen(s, tree, val);
if (val) {
- gen_send_intern(s);
+ gen_intern(s);
}
break;
case NODE_SELF:
if (val) {
- genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ genop_1(s, OP_LOADSELF, cursp());
push();
}
break;
case NODE_NIL:
if (val) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
break;
case NODE_TRUE:
if (val) {
- genop(s, MKOP_A(OP_LOADT, cursp()));
+ genop_1(s, OP_LOADT, cursp());
push();
}
break;
case NODE_FALSE:
if (val) {
- genop(s, MKOP_A(OP_LOADF, cursp()));
+ genop_1(s, OP_LOADF, cursp());
push();
}
break;
case NODE_ALIAS:
{
- int a = new_msym(s, nsym(tree->car));
- int b = new_msym(s, nsym(tree->cdr));
- int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method"));
+ int a = new_sym(s, nsym(tree->car));
+ int b = new_sym(s, nsym(tree->cdr));
- genop(s, MKOP_A(OP_TCLASS, cursp()));
- push();
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a));
- push();
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b));
- push();
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
- push(); /* space for a block */
- pop_n(4);
- genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2));
+ genop_2(s, OP_ALIAS, a, b);
if (val) {
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
}
@@ -2667,41 +2766,15 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_UNDEF:
{
- int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method"));
- int num = 0;
node *t = tree;
- genop(s, MKOP_A(OP_TCLASS, cursp()));
- push();
while (t) {
- int symbol;
- if (num >= CALL_MAXARGS - 1) {
- pop_n(num);
- genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), num));
- while (t) {
- symbol = new_msym(s, nsym(t->car));
- push();
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
- pop();
- genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
- t = t->cdr;
- }
- num = CALL_MAXARGS;
- break;
- }
- symbol = new_msym(s, nsym(t->car));
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
- push();
+ int symbol = new_sym(s, nsym(t->car));
+ genop_1(s, OP_UNDEF, symbol);
t = t->cdr;
- num++;
- }
- push();pop(); /* space for a block */
- pop();
- if (num < CALL_MAXARGS) {
- pop_n(num);
}
- genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num));
if (val) {
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
}
@@ -2710,13 +2783,14 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_CLASS:
{
int idx;
+ node *body;
if (tree->car->car == (node*)0) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
else if (tree->car->car == (node*)1) {
- genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop_1(s, OP_OCLASS, cursp());
push();
}
else {
@@ -2726,14 +2800,20 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->cdr->car, VAL);
}
else {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
pop(); pop();
- idx = new_msym(s, nsym(tree->car->cdr));
- genop(s, MKOP_AB(OP_CLASS, cursp(), idx));
- idx = scope_body(s, tree->cdr->cdr->car, val);
- genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ idx = new_sym(s, nsym(tree->car->cdr));
+ genop_2(s, OP_CLASS, cursp(), idx);
+ body = tree->cdr->cdr->car;
+ if (nint(body->cdr->car) == NODE_BEGIN && body->cdr->cdr == NULL) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
+ idx = scope_body(s, body, val);
+ genop_2(s, OP_EXEC, cursp(), idx);
+ }
if (val) {
push();
}
@@ -2745,21 +2825,27 @@ codegen(codegen_scope *s, node *tree, int val)
int idx;
if (tree->car->car == (node*)0) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
push();
}
else if (tree->car->car == (node*)1) {
- genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop_1(s, OP_OCLASS, cursp());
push();
}
else {
codegen(s, tree->car->car, VAL);
}
pop();
- idx = new_msym(s, nsym(tree->car->cdr));
- genop(s, MKOP_AB(OP_MODULE, cursp(), idx));
- idx = scope_body(s, tree->cdr->car, val);
- genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ idx = new_sym(s, nsym(tree->car->cdr));
+ genop_2(s, OP_MODULE, cursp(), idx);
+ if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN &&
+ tree->cdr->car->cdr->cdr == NULL) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
+ idx = scope_body(s, tree->cdr->car, val);
+ genop_2(s, OP_EXEC, cursp(), idx);
+ }
if (val) {
push();
}
@@ -2772,9 +2858,15 @@ codegen(codegen_scope *s, node *tree, int val)
codegen(s, tree->car, VAL);
pop();
- genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
- idx = scope_body(s, tree->cdr->car, val);
- genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ genop_1(s, OP_SCLASS, cursp());
+ if (nint(tree->cdr->car->cdr->car) == NODE_BEGIN &&
+ tree->cdr->car->cdr->cdr == NULL) {
+ genop_1(s, OP_LOADNIL, cursp());
+ }
+ else {
+ idx = scope_body(s, tree->cdr->car, val);
+ genop_2(s, OP_EXEC, cursp(), idx);
+ }
if (val) {
push();
}
@@ -2783,17 +2875,17 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_DEF:
{
- int sym = new_msym(s, nsym(tree->car));
+ int sym = new_sym(s, nsym(tree->car));
int idx = lambda_body(s, tree->cdr, 0);
- genop(s, MKOP_A(OP_TCLASS, cursp()));
+ genop_1(s, OP_TCLASS, cursp());
push();
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+ genop_2(s, OP_METHOD, cursp(), idx);
push(); pop();
pop();
- genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+ genop_2(s, OP_DEF, cursp(), sym);
if (val) {
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ genop_2(s, OP_LOADSYM, cursp(), sym);
push();
}
}
@@ -2802,18 +2894,18 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_SDEF:
{
node *recv = tree->car;
- int sym = new_msym(s, nsym(tree->cdr->car));
+ int sym = new_sym(s, nsym(tree->cdr->car));
int idx = lambda_body(s, tree->cdr->cdr, 0);
codegen(s, recv, VAL);
pop();
- genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
+ genop_1(s, OP_SCLASS, cursp());
push();
- genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+ genop_2(s, OP_METHOD, cursp(), idx);
pop();
- genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+ genop_2(s, OP_DEF, cursp(), sym);
if (val) {
- genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ genop_2(s, OP_LOADSYM, cursp(), sym);
push();
}
}
@@ -2875,7 +2967,7 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa);
p->irep->plen = 0;
- p->scapa = MAXMSYMLEN;
+ p->scapa = 256;
p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa);
p->irep->slen = 0;
@@ -2900,18 +2992,16 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
}
p->ai = mrb_gc_arena_save(mrb);
- p->filename = prev->filename;
- if (p->filename) {
+ p->filename_sym = prev->filename_sym;
+ if (p->filename_sym) {
p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa);
}
p->lineno = prev->lineno;
/* debug setting */
p->debug_start_pos = 0;
- if (p->filename) {
+ if (p->filename_sym) {
mrb_debug_info_alloc(mrb, p->irep);
- p->irep->filename = p->filename;
- p->irep->lines = p->lines;
}
else {
p->irep->debug_info = NULL;
@@ -2929,34 +3019,26 @@ scope_finish(codegen_scope *s)
{
mrb_state *mrb = s->mrb;
mrb_irep *irep = s->irep;
- size_t fname_len;
- char *fname;
+ if (s->nlocals >= 0x3ff) {
+ codegen_error(s, "too many local variables");
+ }
irep->flags = 0;
if (s->iseq) {
irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc);
irep->ilen = s->pc;
- if (s->lines) {
- irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc);
- }
- else {
- irep->lines = 0;
- }
}
irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen);
irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen);
irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen);
- if (s->filename) {
- irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
- mrb_debug_info_append_file(mrb, irep, s->debug_start_pos, s->pc);
+ if (s->filename_sym) {
+ mrb_sym fname = mrb_parser_get_filename(s->parser, s->filename_index);
+ const char *filename = mrb_sym2name_len(s->mrb, fname, NULL);
- fname_len = strlen(s->filename);
- fname = (char*)codegen_malloc(s, fname_len + 1);
- memcpy(fname, s->filename, fname_len);
- fname[fname_len] = '\0';
- irep->filename = fname;
- irep->own_filename = TRUE;
+ mrb_debug_info_append_file(s->mrb, s->irep->debug_info,
+ filename, s->lines, s->debug_start_pos, s->pc);
}
+ mrb_free(s->mrb, s->lines);
irep->nlocals = s->nlocals;
irep->nregs = s->nregs;
@@ -2971,7 +3053,7 @@ loop_push(codegen_scope *s, enum looptype t)
struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo));
p->type = t;
- p->pc1 = p->pc2 = p->pc3 = 0;
+ p->pc0 = p->pc1 = p->pc2 = p->pc3 = 0;
p->prev = s->loop;
p->ensure_level = s->ensure_level;
p->acc = cursp();
@@ -3013,27 +3095,26 @@ loop_break(codegen_scope *s, node *tree)
return;
}
if (n > 0) {
- genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+ genop_1(s, OP_POPERR, n);
}
if (loop->type == LOOP_NORMAL) {
int tmp;
if (s->ensure_level > s->loop->ensure_level) {
- genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
}
if (tree) {
- genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL);
+ gen_move(s, loop->acc, cursp(), 0);
}
- distcheck(s, s->loop->pc3);
- tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3));
+ tmp = genjmp(s, OP_JMP, loop->pc3);
loop->pc3 = tmp;
}
else {
if (!tree) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
}
- genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK));
+ gen_return(s, OP_BREAK, cursp());
}
}
}
@@ -3042,7 +3123,7 @@ static void
loop_pop(codegen_scope *s, int val)
{
if (val) {
- genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ genop_1(s, OP_LOADNIL, cursp());
}
dispatch_linked(s, s->loop->pc3);
s->loop = s->loop->prev;
@@ -3061,11 +3142,11 @@ generate_code(mrb_state *mrb, parser_state *p, int val)
}
scope->mrb = mrb;
scope->parser = p;
- scope->filename = p->filename;
+ scope->filename_sym = p->filename_sym;
scope->filename_index = p->current_filename_index;
MRB_TRY(&scope->jmp) {
- mrb->jmp = &scope->jmp;
+ mrb->jmp = &scope->jmp;
/* prepare irep */
codegen(scope, p->tree, val);
proc = mrb_proc_new(mrb, scope->irep);
@@ -3092,3 +3173,71 @@ mrb_generate_code(mrb_state *mrb, parser_state *p)
{
return generate_code(mrb, p, VAL);
}
+
+void
+mrb_irep_remove_lv(mrb_state *mrb, mrb_irep *irep)
+{
+ int i;
+
+ if (irep->lv) {
+ mrb_free(mrb, irep->lv);
+ irep->lv = NULL;
+ }
+
+ for (i = 0; i < irep->rlen; ++i) {
+ mrb_irep_remove_lv(mrb, irep->reps[i]);
+ }
+}
+
+#undef OPCODE
+#define Z 1
+#define S 3
+#define W 4
+/* instruction sizes */
+uint8_t mrb_insn_size[] = {
+#define B 2
+#define BB 3
+#define BBB 4
+#define BS 4
+#define SB 4
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+#undef OPCODE
+#undef B
+#undef BB
+#undef BS
+#undef SB
+#undef BBB
+};
+/* EXT1 instruction sizes */
+uint8_t mrb_insn_size1[] = {
+#define B 3
+#define BB 4
+#define BBB 5
+#define BS 5
+#define SB 5
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+#undef OPCODE
+#undef B
+};
+/* EXT2 instruction sizes */
+uint8_t mrb_insn_size2[] = {
+#define B 2
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+#undef OPCODE
+#undef BB
+#undef BBB
+#undef BS
+#undef SB
+};
+/* EXT3 instruction sizes */
+#define BB 5
+#define BBB 6
+#define BS 4
+#define SB 5
+uint8_t mrb_insn_size3[] = {
+#define OPCODE(_,x) x,
+#include "mruby/ops.h"
+};
diff --git a/mrbgems/mruby-compiler/core/node.h b/mrbgems/mruby-compiler/core/node.h
index 9636dd759..219bddab0 100644
--- a/mrbgems/mruby-compiler/core/node.h
+++ b/mrbgems/mruby-compiler/core/node.h
@@ -9,14 +9,11 @@
enum node_type {
NODE_METHOD,
- NODE_FBODY,
- NODE_CFUNC,
NODE_SCOPE,
NODE_BLOCK,
NODE_IF,
NODE_CASE,
NODE_WHEN,
- NODE_OPT_N,
NODE_WHILE,
NODE_UNTIL,
NODE_ITER,
@@ -40,12 +37,12 @@ enum node_type {
NODE_CALL,
NODE_SCALL,
NODE_FCALL,
- NODE_VCALL,
NODE_SUPER,
NODE_ZSUPER,
NODE_ARRAY,
NODE_ZARRAY,
NODE_HASH,
+ NODE_KW_HASH,
NODE_RETURN,
NODE_YIELD,
NODE_LVAR,
@@ -57,8 +54,6 @@ enum node_type {
NODE_NTH_REF,
NODE_BACK_REF,
NODE_MATCH,
- NODE_MATCH2,
- NODE_MATCH3,
NODE_INT,
NODE_FLOAT,
NODE_NEGATE,
@@ -71,10 +66,10 @@ enum node_type {
NODE_REGX,
NODE_DREGX,
NODE_DREGX_ONCE,
- NODE_LIST,
NODE_ARG,
- NODE_ARGSCAT,
- NODE_ARGSPUSH,
+ NODE_ARGS_TAIL,
+ NODE_KW_ARG,
+ NODE_KW_REST_ARGS,
NODE_SPLAT,
NODE_TO_ARY,
NODE_SVALUE,
@@ -88,26 +83,15 @@ enum node_type {
NODE_SCLASS,
NODE_COLON2,
NODE_COLON3,
- NODE_CREF,
NODE_DOT2,
NODE_DOT3,
- NODE_FLIP2,
- NODE_FLIP3,
- NODE_ATTRSET,
NODE_SELF,
NODE_NIL,
NODE_TRUE,
NODE_FALSE,
NODE_DEFINED,
- NODE_NEWLINE,
NODE_POSTEXE,
- NODE_ALLOCA,
- NODE_DMETHOD,
- NODE_BMETHOD,
- NODE_MEMO,
- NODE_IFUNC,
NODE_DSYM,
- NODE_ATTRASGN,
NODE_HEREDOC,
NODE_LITERAL_DELIM,
NODE_WORDS,
diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y
index 7f848b4df..ac59a8b0b 100644
--- a/mrbgems/mruby-compiler/core/parse.y
+++ b/mrbgems/mruby-compiler/core/parse.y
@@ -10,13 +10,7 @@
# define YYDEBUG 1
#endif
#define YYERROR_VERBOSE 1
-/*
- * Force yacc to use our memory management. This is a little evil because
- * the macros assume that "parser_state *p" is in scope
- */
-#define YYMALLOC(n) mrb_malloc(p->mrb, (n))
-#define YYFREE(o) mrb_free(p->mrb, (o))
-#define YYSTACK_USE_ALLOCA 0
+#define YYSTACK_USE_ALLOCA 1
#include <ctype.h>
#include <errno.h>
@@ -76,6 +70,24 @@ typedef unsigned int stack_type;
#define nint(x) ((node*)(intptr_t)(x))
#define intn(x) ((int)(intptr_t)(x))
+#if defined(MRB_COMPLEX_NUMBERS) || defined(MRB_RATIONAL_NUMBERS)
+ #define MRB_SUFFIX_SUPPORT
+
+ #ifdef MRB_RATIONAL_NUMBERS
+ #define NUM_SUFFIX_R (1<<0)
+ #else
+ #define NUM_SUFFIX_R 0
+ #endif
+
+ #ifdef MRB_COMPLEX_NUMBERS
+ #define NUM_SUFFIX_I (1<<1)
+ #else
+ #define NUM_SUFFIX_I 0
+ #endif
+
+ #define NUM_SUFFIX_ALL (NUM_SUFFIX_R | NUM_SUFFIX_I)
+#endif
+
static inline mrb_sym
intern_cstr_gen(parser_state *p, const char *s)
{
@@ -133,6 +145,10 @@ cons_gen(parser_state *p, node *car, node *cdr)
c->cdr = cdr;
c->lineno = p->lineno;
c->filename_index = p->current_filename_index;
+ /* beginning of next partial file; need to point the previous file */
+ if (p->lineno == 0 && p->current_filename_index > 0) {
+ c->filename_index-- ;
+ }
return c;
}
#define cons(a,b) cons_gen(p,(a),(b))
@@ -216,6 +232,26 @@ parser_strdup(parser_state *p, const char *s)
#undef strdup
#define strdup(s) parser_strdup(p, s)
+static void
+dump_int(uint16_t i, char *s)
+{
+ char *p = s;
+ char *t = s;
+
+ while (i > 0) {
+ *p++ = (i % 10)+'0';
+ i /= 10;
+ }
+ if (p == s) *p++ = '0';
+ *p = 0;
+ p--; /* point the last char */
+ while (t < p) {
+ char c = *t;
+ *t++ = *p;
+ *p-- = c;
+ }
+}
+
/* xxx ----------------------------- */
static node*
@@ -279,6 +315,20 @@ local_add(parser_state *p, mrb_sym sym)
}
}
+static void
+local_add_blk(parser_state *p, mrb_sym blk)
+{
+ /* allocate register for block */
+ local_add_f(p, blk ? blk : mrb_intern_lit(p->mrb, "&"));
+}
+
+static void
+local_add_kw(parser_state *p, mrb_sym kwd)
+{
+ /* allocate register for keywords hash */
+ local_add_f(p, kwd ? kwd : mrb_intern_lit(p->mrb, "**"));
+}
+
static node*
locals_node(parser_state *p)
{
@@ -568,6 +618,13 @@ new_hash(parser_state *p, node *a)
return cons((node*)NODE_HASH, a);
}
+/* (:kw_hash (k . v) (k . v)...) */
+static node*
+new_kw_hash(parser_state *p, node *a)
+{
+ return cons((node*)NODE_KW_HASH, a);
+}
+
/* (:sym . a) */
static node*
new_sym(parser_state *p, mrb_sym sym)
@@ -671,23 +728,80 @@ new_arg(parser_state *p, mrb_sym sym)
return cons((node*)NODE_ARG, nsym(sym));
}
-/* (m o r m2 b) */
+static void
+local_add_margs(parser_state *p, node *n)
+{
+ while (n) {
+ if (n->car->car == (node*)NODE_MASGN) {
+ node *t = n->car->cdr->cdr;
+
+ n->car->cdr->cdr = NULL;
+ while (t) {
+ local_add_f(p, sym(t->car));
+ t = t->cdr;
+ }
+ local_add_margs(p, n->car->cdr->car->car);
+ local_add_margs(p, n->car->cdr->car->cdr->cdr->car);
+ }
+ n = n->cdr;
+ }
+}
+
+/* (m o r m2 tail) */
/* m: (a b c) */
/* o: ((a . e1) (b . e2)) */
/* r: a */
/* m2: (a b c) */
/* b: a */
static node*
-new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, mrb_sym blk)
+new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, node *tail)
{
node *n;
- n = cons(m2, nsym(blk));
+ local_add_margs(p, m);
+ local_add_margs(p, m2);
+ n = cons(m2, tail);
n = cons(nsym(rest), n);
n = cons(opt, n);
return cons(m, n);
}
+/* (:args_tail keywords rest_keywords_sym block_sym) */
+static node*
+new_args_tail(parser_state *p, node *kws, node *kwrest, mrb_sym blk)
+{
+ node *k;
+
+ if (kws || kwrest) {
+ local_add_kw(p, (kwrest && kwrest->cdr)? sym(kwrest->cdr) : 0);
+ }
+
+ local_add_blk(p, blk);
+
+ // allocate register for keywords arguments
+ // order is for Proc#parameters
+ for (k = kws; k; k = k->cdr) {
+ if (!k->car->cdr->cdr->car) { // allocate required keywords
+ local_add_f(p, sym(k->car->cdr->car));
+ }
+ }
+ for (k = kws; k; k = k->cdr) {
+ if (k->car->cdr->cdr->car) { // allocate keywords with default
+ local_add_f(p, sym(k->car->cdr->car));
+ }
+ }
+
+ return list4((node*)NODE_ARGS_TAIL, kws, kwrest, nsym(blk));
+}
+
+/* (:kw_arg kw_sym def_arg) */
+static node*
+new_kw_arg(parser_state *p, mrb_sym kw, node *def_arg)
+{
+ mrb_assert(kw);
+ return list3((node*)NODE_KW_ARG, nsym(kw), def_arg);
+}
+
/* (:block_arg . a) */
static node*
new_block_arg(parser_state *p, node *a)
@@ -725,6 +839,13 @@ new_masgn(parser_state *p, node *a, node *b)
return cons((node*)NODE_MASGN, cons(a, b));
}
+/* (:masgn mlhs mrhs) no check */
+static node*
+new_masgn_param(parser_state *p, node *a, node *b)
+{
+ return cons((node*)NODE_MASGN, cons(a, b));
+}
+
/* (:asgn lhs rhs) */
static node*
new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
@@ -733,19 +854,57 @@ new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b)
return list4((node*)NODE_OP_ASGN, a, nsym(op), b);
}
+#ifdef MRB_COMPLEX_NUMBERS
+static node*
+new_imaginary(parser_state *p, node *imaginary)
+{
+ return new_call(p, new_const(p, intern_cstr("Kernel")), intern_cstr("Complex"), list1(list2(list3((node*)NODE_INT, (node*)strdup("0"), nint(10)), imaginary)), 1);
+}
+#endif
+
+#ifdef MRB_RATIONAL_NUMBERS
+static node*
+new_rational(parser_state *p, node *rational)
+{
+ return new_call(p, new_const(p, intern_cstr("Kernel")), intern_cstr("Rational"), list1(list1(rational)), 1);
+}
+#endif
+
/* (:int . i) */
static node*
-new_int(parser_state *p, const char *s, int base)
+new_int(parser_state *p, const char *s, int base, int suffix)
{
- return list3((node*)NODE_INT, (node*)strdup(s), nint(base));
+ node* result = list3((node*)NODE_INT, (node*)strdup(s), nint(base));
+#ifdef MRB_RATIONAL_NUMBERS
+ if (suffix & NUM_SUFFIX_R) {
+ result = new_rational(p, result);
+ }
+#endif
+#ifdef MRB_COMPLEX_NUMBERS
+ if (suffix & NUM_SUFFIX_I) {
+ result = new_imaginary(p, result);
+ }
+#endif
+ return result;
}
#ifndef MRB_WITHOUT_FLOAT
/* (:float . i) */
static node*
-new_float(parser_state *p, const char *s)
+new_float(parser_state *p, const char *s, int suffix)
{
- return cons((node*)NODE_FLOAT, (node*)strdup(s));
+ node* result = cons((node*)NODE_FLOAT, (node*)strdup(s));
+#ifdef MRB_RATIONAL_NUMBERS
+ if (suffix & NUM_SUFFIX_R) {
+ result = new_rational(p, result);
+ }
+#endif
+#ifdef MRB_COMPLEX_NUMBERS
+ if (suffix & NUM_SUFFIX_I) {
+ result = new_imaginary(p, result);
+ }
+#endif
+ return result;
}
#endif
@@ -763,6 +922,86 @@ new_dstr(parser_state *p, node *a)
return cons((node*)NODE_DSTR, a);
}
+static int
+string_node_p(node *n)
+{
+ return (int)((enum node_type)(intptr_t)n->car == NODE_STR);
+}
+
+static node*
+composite_string_node(parser_state *p, node *a, node *b)
+{
+ size_t newlen = (size_t)a->cdr + (size_t)b->cdr;
+ char *str = (char*)mrb_pool_realloc(p->pool, a->car, (size_t)a->cdr + 1, newlen + 1);
+ memcpy(str + (size_t)a->cdr, b->car, (size_t)b->cdr);
+ str[newlen] = '\0';
+ a->car = (node*)str;
+ a->cdr = (node*)newlen;
+ cons_free(b);
+ return a;
+}
+
+static node*
+concat_string(parser_state *p, node *a, node *b)
+{
+ if (string_node_p(a)) {
+ if (string_node_p(b)) {
+ /* a == NODE_STR && b == NODE_STR */
+ composite_string_node(p, a->cdr, b->cdr);
+ cons_free(b);
+ return a;
+ }
+ else {
+ /* a == NODE_STR && b == NODE_DSTR */
+
+ if (string_node_p(b->cdr->car)) {
+ /* a == NODE_STR && b->[NODE_STR, ...] */
+ composite_string_node(p, a->cdr, b->cdr->car->cdr);
+ cons_free(b->cdr->car);
+ b->cdr->car = a;
+ return b;
+ }
+ }
+ }
+ else {
+ node *c; /* last node of a */
+ for (c = a; c->cdr != NULL; c = c->cdr) ;
+
+ if (string_node_p(b)) {
+ /* a == NODE_DSTR && b == NODE_STR */
+ if (string_node_p(c->car)) {
+ /* a->[..., NODE_STR] && b == NODE_STR */
+ composite_string_node(p, c->car->cdr, b->cdr);
+ cons_free(b);
+ return a;
+ }
+
+ push(a, b);
+ return a;
+ }
+ else {
+ /* a == NODE_DSTR && b == NODE_DSTR */
+ if (string_node_p(c->car) && string_node_p(b->cdr->car)) {
+ /* a->[..., NODE_STR] && b->[NODE_STR, ...] */
+ node *d = b->cdr;
+ cons_free(b);
+ composite_string_node(p, c->car->cdr, d->car->cdr);
+ cons_free(d->car);
+ c->cdr = d->cdr;
+ cons_free(d);
+ return a;
+ }
+ else {
+ c->cdr = b->cdr;
+ cons_free(b);
+ return a;
+ }
+ }
+ }
+
+ return new_dstr(p, list2(a, b));
+}
+
/* (:str . (s . len)) */
static node*
new_xstr(parser_state *p, const char *s, int len)
@@ -1029,7 +1268,6 @@ heredoc_end(parser_state *p)
end_strterm(p);
p->lex_strterm = p->lex_strterm_before_heredoc;
p->lex_strterm_before_heredoc = NULL;
- p->heredoc_end_now = TRUE;
}
else {
/* next heredoc */
@@ -1112,7 +1350,7 @@ heredoc_end(parser_state *p)
%token <nd> tNTH_REF tBACK_REF
%token <num> tREGEXP_END
-%type <nd> singleton string string_rep string_interp xstring regexp
+%type <nd> singleton string string_fragment string_rep string_interp xstring regexp
%type <nd> literal numeric cpath symbol
%type <nd> top_compstmt top_stmts top_stmt
%type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call
@@ -1123,7 +1361,7 @@ heredoc_end(parser_state *p)
%type <nd> command_args aref_args opt_block_arg block_arg var_ref var_lhs
%type <nd> command_asgn command_rhs mrhs superclass block_call block_command
%type <nd> f_block_optarg f_block_opt
-%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs
+%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_margs
%type <nd> assoc_list assocs assoc undef_list backref for_var
%type <nd> block_param opt_block_param block_param_def f_opt
%type <nd> bv_decls opt_bv_decl bvar f_larglist lambda_body
@@ -1134,6 +1372,10 @@ heredoc_end(parser_state *p)
%type <nd> heredoc words symbols
%type <num> call_op call_op2 /* 0:'&.', 1:'.', 2:'::' */
+%type <nd> args_tail opt_args_tail f_kwarg f_kw f_kwrest
+%type <nd> f_block_kwarg f_block_kw block_args_tail opt_block_args_tail
+%type <id> f_label
+
%token tUPLUS /* unary+ */
%token tUMINUS /* unary- */
%token tPOW /* ** */
@@ -1159,6 +1401,7 @@ heredoc_end(parser_state *p)
%token tLBRACE /* { */
%token tLBRACE_ARG /* { */
%token tSTAR /* * */
+%token tDSTAR /* ** */
%token tAMPER /* & */
%token tLAMBDA /* -> */
%token tANDDOT /* &. */
@@ -1736,6 +1979,7 @@ op : '|' { $$ = intern_c('|'); }
| '/' { $$ = intern_c('/'); }
| '%' { $$ = intern_c('%'); }
| tPOW { $$ = intern("**",2); }
+ | tDSTAR { $$ = intern("**",2); }
| '!' { $$ = intern_c('!'); }
| '~' { $$ = intern_c('~'); }
| tUPLUS { $$ = intern("+@",2); }
@@ -1944,11 +2188,11 @@ aref_args : none
}
| args comma assocs trailer
{
- $$ = push($1, new_hash(p, $3));
+ $$ = push($1, new_kw_hash(p, $3));
}
| assocs trailer
{
- $$ = cons(new_hash(p, $1), 0);
+ $$ = cons(new_kw_hash(p, $1), 0);
NODE_LINENO($$, $1);
}
;
@@ -1984,12 +2228,12 @@ opt_call_args : none
}
| args comma assocs ','
{
- $$ = cons(push($1, new_hash(p, $3)), 0);
+ $$ = cons(push($1, new_kw_hash(p, $3)), 0);
NODE_LINENO($$, $1);
}
| assocs ','
{
- $$ = cons(list1(new_hash(p, $1)), 0);
+ $$ = cons(list1(new_kw_hash(p, $1)), 0);
NODE_LINENO($$, $1);
}
;
@@ -2007,12 +2251,12 @@ call_args : command
}
| assocs opt_block_arg
{
- $$ = cons(list1(new_hash(p, $1)), $2);
+ $$ = cons(list1(new_kw_hash(p, $1)), $2);
NODE_LINENO($$, $1);
}
| args comma assocs opt_block_arg
{
- $$ = cons(push($1, new_hash(p, $3)), $4);
+ $$ = cons(push($1, new_kw_hash(p, $3)), $4);
NODE_LINENO($$, $1);
}
| block_arg
@@ -2393,43 +2637,24 @@ for_var : lhs
| mlhs
;
-f_marg : f_norm_arg
- {
- $$ = new_arg(p, $1);
- }
- | tLPAREN f_margs rparen
- {
- $$ = new_masgn(p, $2, 0);
- }
- ;
-
-f_marg_list : f_marg
- {
- $$ = list1($1);
- }
- | f_marg_list ',' f_marg
- {
- $$ = push($1, $3);
- }
- ;
-
-f_margs : f_marg_list
+f_margs : f_arg
{
$$ = list3($1,0,0);
}
- | f_marg_list ',' tSTAR f_norm_arg
+ | f_arg ',' tSTAR f_norm_arg
{
$$ = list3($1, new_arg(p, $4), 0);
}
- | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list
+ | f_arg ',' tSTAR f_norm_arg ',' f_arg
{
$$ = list3($1, new_arg(p, $4), $6);
}
- | f_marg_list ',' tSTAR
+ | f_arg ',' tSTAR
{
+ local_add_f(p, 0);
$$ = list3($1, (node*)-1, 0);
}
- | f_marg_list ',' tSTAR ',' f_marg_list
+ | f_arg ',' tSTAR ',' f_arg
{
$$ = list3($1, (node*)-1, $5);
}
@@ -2437,96 +2662,134 @@ f_margs : f_marg_list
{
$$ = list3(0, new_arg(p, $2), 0);
}
- | tSTAR f_norm_arg ',' f_marg_list
+ | tSTAR f_norm_arg ',' f_arg
{
$$ = list3(0, new_arg(p, $2), $4);
}
| tSTAR
{
+ local_add_f(p, 0);
$$ = list3(0, (node*)-1, 0);
}
- | tSTAR ',' f_marg_list
+ | tSTAR ','
{
- $$ = list3(0, (node*)-1, $3);
+ local_add_f(p, 0);
+ }
+ f_arg
+ {
+ $$ = list3(0, (node*)-1, $4);
}
;
-block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg
+block_args_tail : f_block_kwarg ',' f_kwrest opt_f_block_arg
+ {
+ $$ = new_args_tail(p, $1, $3, $4);
+ }
+ | f_block_kwarg opt_f_block_arg
+ {
+ $$ = new_args_tail(p, $1, 0, $2);
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ $$ = new_args_tail(p, 0, $1, $2);
+ }
+ | f_block_arg
+ {
+ $$ = new_args_tail(p, 0, 0, $1);
+ }
+ ;
+
+opt_block_args_tail : ',' block_args_tail
+ {
+ $$ = $2;
+ }
+ | /* none */
+ {
+ $$ = new_args_tail(p, 0, 0, 0);
+ }
+ ;
+
+block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_block_args_tail
{
$$ = new_args(p, $1, $3, $5, 0, $6);
}
- | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, $1, $3, $5, $7, $8);
}
- | f_arg ',' f_block_optarg opt_f_block_arg
+ | f_arg ',' f_block_optarg opt_block_args_tail
{
$$ = new_args(p, $1, $3, 0, 0, $4);
}
- | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_block_optarg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, $1, $3, 0, $5, $6);
}
- | f_arg ',' f_rest_arg opt_f_block_arg
+ | f_arg ',' f_rest_arg opt_block_args_tail
{
$$ = new_args(p, $1, 0, $3, 0, $4);
}
- | f_arg ','
+ | f_arg ',' opt_block_args_tail
{
- $$ = new_args(p, $1, 0, 0, 0, 0);
+ $$ = new_args(p, $1, 0, 0, 0, $3);
}
- | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, $1, 0, $3, $5, $6);
}
- | f_arg opt_f_block_arg
+ | f_arg opt_block_args_tail
{
$$ = new_args(p, $1, 0, 0, 0, $2);
}
- | f_block_optarg ',' f_rest_arg opt_f_block_arg
+ | f_block_optarg ',' f_rest_arg opt_block_args_tail
{
$$ = new_args(p, 0, $1, $3, 0, $4);
}
- | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_block_optarg ',' f_rest_arg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, 0, $1, $3, $5, $6);
}
- | f_block_optarg opt_f_block_arg
+ | f_block_optarg opt_block_args_tail
{
$$ = new_args(p, 0, $1, 0, 0, $2);
}
- | f_block_optarg ',' f_arg opt_f_block_arg
+ | f_block_optarg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, 0, $1, 0, $3, $4);
}
- | f_rest_arg opt_f_block_arg
+ | f_rest_arg opt_block_args_tail
{
$$ = new_args(p, 0, 0, $1, 0, $2);
}
- | f_rest_arg ',' f_arg opt_f_block_arg
+ | f_rest_arg ',' f_arg opt_block_args_tail
{
$$ = new_args(p, 0, 0, $1, $3, $4);
}
- | f_block_arg
+ | block_args_tail
{
$$ = new_args(p, 0, 0, 0, 0, $1);
}
;
opt_block_param : none
- | block_param_def
{
+ local_add_blk(p, 0);
+ $$ = 0;
+ }
+ | block_param_def
+ {
p->cmd_start = TRUE;
$$ = $1;
}
;
-block_param_def : '|' opt_bv_decl '|'
+block_param_def : '|' {local_add_blk(p, 0);} opt_bv_decl '|'
{
$$ = 0;
}
| tOROP
{
+ local_add_blk(p, 0);
$$ = 0;
}
| '|' block_param opt_bv_decl '|'
@@ -2739,7 +3002,14 @@ literal : numeric
| symbols
;
-string : tCHAR
+string : string_fragment
+ | string string_fragment
+ {
+ $$ = concat_string(p, $1, $2);
+ }
+ ;
+
+string_fragment : tCHAR
| tSTRING
| tSTRING_BEG tSTRING
{
@@ -2961,7 +3231,7 @@ var_ref : variable
}
| keyword__FILE__
{
- const char *fn = p->filename;
+ const char *fn = mrb_sym2name_len(p->mrb, p->filename_sym, NULL);
if (!fn) {
fn = "(null)";
}
@@ -2971,8 +3241,8 @@ var_ref : variable
{
char buf[16];
- snprintf(buf, sizeof(buf), "%d", p->lineno);
- $$ = new_int(p, buf, 10);
+ dump_int(p->lineno, buf);
+ $$ = new_int(p, buf, 10, 0);
}
| keyword__ENCODING__
{
@@ -3021,65 +3291,151 @@ f_arglist : '(' f_args rparen
}
;
-f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg
+f_label : tIDENTIFIER tLABEL_TAG
+ ;
+
+f_kw : f_label arg
+ {
+ void_expr_error(p, $2);
+ $$ = new_kw_arg(p, $1, $2);
+ }
+ | f_label
+ {
+ $$ = new_kw_arg(p, $1, 0);
+ }
+ ;
+
+f_block_kw : f_label primary_value
+ {
+ $$ = new_kw_arg(p, $1, $2);
+ }
+ | f_label
+ {
+ $$ = new_kw_arg(p, $1, 0);
+ }
+ ;
+
+f_block_kwarg : f_block_kw
+ {
+ $$ = list1($1);
+ }
+ | f_block_kwarg ',' f_block_kw
+ {
+ $$ = push($1, $3);
+ }
+ ;
+
+f_kwarg : f_kw
+ {
+ $$ = list1($1);
+ }
+ | f_kwarg ',' f_kw
+ {
+ $$ = push($1, $3);
+ }
+ ;
+
+kwrest_mark : tPOW
+ | tDSTAR
+ ;
+
+f_kwrest : kwrest_mark tIDENTIFIER
+ {
+ $$ = cons((node*)NODE_KW_REST_ARGS, nsym($2));
+ }
+ | kwrest_mark
+ {
+ $$ = cons((node*)NODE_KW_REST_ARGS, 0);
+ }
+ ;
+
+args_tail : f_kwarg ',' f_kwrest opt_f_block_arg
+ {
+ $$ = new_args_tail(p, $1, $3, $4);
+ }
+ | f_kwarg opt_f_block_arg
+ {
+ $$ = new_args_tail(p, $1, 0, $2);
+ }
+ | f_kwrest opt_f_block_arg
+ {
+ $$ = new_args_tail(p, 0, $1, $2);
+ }
+ | f_block_arg
+ {
+ $$ = new_args_tail(p, 0, 0, $1);
+ }
+ ;
+
+opt_args_tail : ',' args_tail
+ {
+ $$ = $2;
+ }
+ | /* none */
+ {
+ $$ = new_args_tail(p, 0, 0, 0);
+ }
+ ;
+
+f_args : f_arg ',' f_optarg ',' f_rest_arg opt_args_tail
{
$$ = new_args(p, $1, $3, $5, 0, $6);
}
- | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_args_tail
{
$$ = new_args(p, $1, $3, $5, $7, $8);
}
- | f_arg ',' f_optarg opt_f_block_arg
+ | f_arg ',' f_optarg opt_args_tail
{
$$ = new_args(p, $1, $3, 0, 0, $4);
}
- | f_arg ',' f_optarg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_optarg ',' f_arg opt_args_tail
{
$$ = new_args(p, $1, $3, 0, $5, $6);
}
- | f_arg ',' f_rest_arg opt_f_block_arg
+ | f_arg ',' f_rest_arg opt_args_tail
{
$$ = new_args(p, $1, 0, $3, 0, $4);
}
- | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_arg ',' f_rest_arg ',' f_arg opt_args_tail
{
$$ = new_args(p, $1, 0, $3, $5, $6);
}
- | f_arg opt_f_block_arg
+ | f_arg opt_args_tail
{
$$ = new_args(p, $1, 0, 0, 0, $2);
}
- | f_optarg ',' f_rest_arg opt_f_block_arg
+ | f_optarg ',' f_rest_arg opt_args_tail
{
$$ = new_args(p, 0, $1, $3, 0, $4);
}
- | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg
+ | f_optarg ',' f_rest_arg ',' f_arg opt_args_tail
{
$$ = new_args(p, 0, $1, $3, $5, $6);
}
- | f_optarg opt_f_block_arg
+ | f_optarg opt_args_tail
{
$$ = new_args(p, 0, $1, 0, 0, $2);
}
- | f_optarg ',' f_arg opt_f_block_arg
+ | f_optarg ',' f_arg opt_args_tail
{
$$ = new_args(p, 0, $1, 0, $3, $4);
}
- | f_rest_arg opt_f_block_arg
+ | f_rest_arg opt_args_tail
{
$$ = new_args(p, 0, 0, $1, 0, $2);
}
- | f_rest_arg ',' f_arg opt_f_block_arg
+ | f_rest_arg ',' f_arg opt_args_tail
{
$$ = new_args(p, 0, 0, $1, $3, $4);
}
- | f_block_arg
+ | args_tail
{
$$ = new_args(p, 0, 0, 0, 0, $1);
}
| /* none */
{
- local_add_f(p, 0);
+ local_add_f(p, mrb_intern_lit(p->mrb, "&"));
$$ = new_args(p, 0, 0, 0, 0, 0);
}
;
@@ -3121,9 +3477,15 @@ f_arg_item : f_norm_arg
{
$$ = new_arg(p, $1);
}
- | tLPAREN f_margs rparen
+ | tLPAREN
+ {
+ $<nd>$ = local_switch(p);
+ }
+ f_margs rparen
{
- $$ = new_masgn(p, $2, 0);
+ $$ = new_masgn_param(p, $3, p->locals->car);
+ local_resume(p, $<nd>2);
+ local_add_f(p, 0);
}
;
@@ -3189,7 +3551,7 @@ f_rest_arg : restarg_mark tIDENTIFIER
}
| restarg_mark
{
- local_add_f(p, 0);
+ local_add_f(p, mrb_intern_lit(p->mrb, "*"));
$$ = -1;
}
;
@@ -3200,7 +3562,6 @@ blkarg_mark : '&'
f_block_arg : blkarg_mark tIDENTIFIER
{
- local_add_f(p, $2);
$$ = $2;
}
;
@@ -3211,7 +3572,6 @@ opt_f_block_arg : ',' f_block_arg
}
| none
{
- local_add_f(p, 0);
$$ = 0;
}
;
@@ -3275,7 +3635,7 @@ assoc : arg tASSOC arg
void_expr_error(p, $3);
$$ = cons(new_sym(p, $1), $3);
}
- | string tLABEL_TAG arg
+ | string_fragment tLABEL_TAG arg
{
void_expr_error(p, $3);
if ($1->car == (node*)NODE_DSTR) {
@@ -3285,6 +3645,11 @@ assoc : arg tASSOC arg
$$ = cons(new_sym(p, new_strsym(p, $1)), $3);
}
}
+ | tDSTAR arg
+ {
+ void_expr_error(p, $2);
+ $$ = cons(cons((node*)NODE_KW_REST_ARGS, 0), $2);
+ }
;
operation : tIDENTIFIER
@@ -3375,8 +3740,9 @@ yyerror(parser_state *p, const char *s)
if (! p->capture_errors) {
#ifndef MRB_DISABLE_STDIO
- if (p->filename) {
- fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
+ if (p->filename_sym) {
+ const char *filename = mrb_sym2name_len(p->mrb, p->filename_sym, NULL);
+ fprintf(stderr, "%s:%d:%d: %s\n", filename, p->lineno, p->column, s);
}
else {
fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
@@ -3395,11 +3761,13 @@ yyerror(parser_state *p, const char *s)
}
static void
-yyerror_i(parser_state *p, const char *fmt, int i)
+yyerror_c(parser_state *p, const char *msg, char c)
{
char buf[256];
- snprintf(buf, sizeof(buf), fmt, i);
+ strncpy(buf, msg, sizeof(buf) - 2);
+ buf[sizeof(buf) - 2] = '\0';
+ strncat(buf, &c, 1);
yyerror(p, buf);
}
@@ -3411,11 +3779,12 @@ yywarn(parser_state *p, const char *s)
if (! p->capture_errors) {
#ifndef MRB_DISABLE_STDIO
- if (p->filename) {
- fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s);
+ if (p->filename_sym) {
+ const char *filename = mrb_sym2name_len(p->mrb, p->filename_sym, NULL);
+ fprintf(stderr, "%s:%d:%d: warning: %s\n", filename, p->lineno, p->column, s);
}
else {
- fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s);
+ fprintf(stderr, "line %d:%d: warning: %s\n", p->lineno, p->column, s);
}
#endif
}
@@ -3437,11 +3806,14 @@ yywarning(parser_state *p, const char *s)
}
static void
-yywarning_s(parser_state *p, const char *fmt, const char *s)
+yywarning_s(parser_state *p, const char *msg, const char *s)
{
char buf[256];
- snprintf(buf, sizeof(buf), fmt, s);
+ strncpy(buf, msg, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ strncat(buf, ": ", sizeof(buf) - strlen(buf) - 1);
+ strncat(buf, s, sizeof(buf) - strlen(buf) - 1);
yywarning(p, buf);
}
@@ -3450,13 +3822,13 @@ backref_error(parser_state *p, node *n)
{
int c;
- c = (int)(intptr_t)n->car;
+ c = intn(n->car);
if (c == NODE_NTH_REF) {
- yyerror_i(p, "can't set variable $%" MRB_PRId, (int)(intptr_t)n->cdr);
+ yyerror_c(p, "can't set variable $", (char)intn(n->cdr)+'0');
}
else if (c == NODE_BACK_REF) {
- yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr);
+ yyerror_c(p, "can't set variable $", (char)intn(n->cdr));
}
else {
mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c));
@@ -3469,7 +3841,7 @@ void_expr_error(parser_state *p, node *n)
int c;
if (n == NULL) return;
- c = (int)(intptr_t)n->car;
+ c = intn(n->car);
switch (c) {
case NODE_BREAK:
case NODE_RETURN:
@@ -3480,8 +3852,10 @@ void_expr_error(parser_state *p, node *n)
break;
case NODE_AND:
case NODE_OR:
- void_expr_error(p, n->cdr->car);
- void_expr_error(p, n->cdr->cdr);
+ if (n->cdr) {
+ void_expr_error(p, n->cdr->car);
+ void_expr_error(p, n->cdr->cdr);
+ }
break;
case NODE_BEGIN:
if (n->cdr) {
@@ -3508,7 +3882,7 @@ nextc(parser_state *p)
if (p->pb) {
node *tmp;
- c = (int)(intptr_t)p->pb->car;
+ c = intn(p->pb->car);
tmp = p->pb;
p->pb = p->pb->cdr;
cons_free(tmp);
@@ -3532,14 +3906,6 @@ nextc(parser_state *p)
if (c >= 0) {
p->column++;
}
- if (c == '\r') {
- c = nextc(p);
- if (c != '\n') {
- pushback(p, c);
- return '\r';
- }
- return c;
- }
return c;
eof:
@@ -3557,7 +3923,7 @@ pushback(parser_state *p, int c)
if (c >= 0) {
p->column--;
}
- p->pb = cons((node*)(intptr_t)c, p->pb);
+ p->pb = cons(nint(c), p->pb);
}
static void
@@ -3582,7 +3948,7 @@ peekc_n(parser_state *p, int n)
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);
+ list = push(list, nint(c0));
} while(n--);
if (p->pb) {
p->pb = append((node*)list, p->pb);
@@ -4006,8 +4372,17 @@ parse_string(parser_state *p)
}
if (c < 0) {
char buf[256];
- snprintf(buf, sizeof(buf), "can't find heredoc delimiter \"%s\" anywhere before EOF", hinf->term);
- yyerror(p, buf);
+ const char s1[] = "can't find heredoc delimiter \"";
+ const char s2[] = "\" anywhere before EOF";
+
+ if (sizeof(s1)+sizeof(s2)+strlen(hinf->term)+1 >= sizeof(buf)) {
+ yyerror(p, "can't find heredoc delimiter anywhere before EOF");
+ } else {
+ strcpy(buf, s1);
+ strcat(buf, hinf->term);
+ strcat(buf, s2);
+ yyerror(p, buf);
+ }
return 0;
}
pylval.nd = new_str(p, tok(p), toklen(p));
@@ -4019,11 +4394,11 @@ parse_string(parser_state *p)
}
else if (c == beg) {
nest_level++;
- p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
+ p->lex_strterm->cdr->car = nint(nest_level);
}
else if (c == end) {
nest_level--;
- p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level;
+ p->lex_strterm->cdr->car = nint(nest_level);
}
else if (c == '\\') {
c = nextc(p);
@@ -4157,9 +4532,14 @@ parse_string(parser_state *p)
pushback(p, re_opt);
if (toklen(p)) {
char msg[128];
+
+ strcpy(msg, "unknown regexp option");
tokfix(p);
- snprintf(msg, sizeof(msg), "unknown regexp option%s - %s",
- toklen(p) > 1 ? "s" : "", tok(p));
+ if (toklen(p) > 1) {
+ strcat(msg, "s");
+ }
+ strcat(msg, " - ");
+ strncat(msg, tok(p), sizeof(msg) - strlen(msg) - 1);
yyerror(p, msg);
}
if (f != 0) {
@@ -4190,6 +4570,45 @@ parse_string(parser_state *p)
return tSTRING;
}
+#ifdef MRB_SUFFIX_SUPPORT
+static int
+number_literal_suffix(parser_state *p, int mask)
+{
+ int c, result = 0;
+ node *list = 0;
+ int column = p->column;
+
+ while ((c = nextc(p)) != -1) {
+ list = push(list, (node*)(intptr_t)c);
+
+ if ((mask & NUM_SUFFIX_I) && c == 'i') {
+ result |= (mask & NUM_SUFFIX_I);
+ mask &= ~NUM_SUFFIX_I;
+ /* r after i, rational of complex is disallowed */
+ mask &= ~NUM_SUFFIX_R;
+ continue;
+ }
+ if ((mask & NUM_SUFFIX_R) && c == 'r') {
+ result |= (mask & NUM_SUFFIX_R);
+ mask &= ~NUM_SUFFIX_R;
+ continue;
+ }
+ if (!ISASCII(c) || ISALPHA(c) || c == '_') {
+ p->column = column;
+ if (p->pb) {
+ p->pb = append((node*)list, p->pb);
+ }
+ else {
+ p->pb = list;
+ }
+ return 0;
+ }
+ pushback(p, c);
+ break;
+ }
+ return result;
+}
+#endif
static int
heredoc_identifier(parser_state *p)
@@ -4310,52 +4729,68 @@ parser_yylex(parser_state *p)
/* fall through */
case -2: /* end of a file */
case '\n':
- maybe_heredoc:
+ maybe_heredoc:
heredoc_treat_nextline(p);
- switch (p->lstate) {
- case EXPR_BEG:
- case EXPR_FNAME:
- case EXPR_DOT:
- case EXPR_CLASS:
- case EXPR_VALUE:
- p->lineno++;
- p->column = 0;
- if (p->parsing_heredoc != NULL) {
- if (p->lex_strterm) {
- return parse_string(p);
+ switch (p->lstate) {
+ case EXPR_BEG:
+ case EXPR_FNAME:
+ case EXPR_DOT:
+ case EXPR_CLASS:
+ case EXPR_VALUE:
+ p->lineno++;
+ p->column = 0;
+ if (p->parsing_heredoc != NULL) {
+ if (p->lex_strterm) {
+ return parse_string(p);
+ }
}
- }
- goto retry;
- default:
- break;
- }
- if (p->parsing_heredoc != NULL) {
- return '\n';
- }
- while ((c = nextc(p))) {
- switch (c) {
- case ' ': case '\t': case '\f': case '\r':
- case '\13': /* '\v' */
- space_seen = 1;
+ goto retry;
+ default:
break;
- case '.':
- if ((c = nextc(p)) != '.') {
- pushback(p, c);
- pushback(p, '.');
+ }
+ if (p->parsing_heredoc != NULL) {
+ return '\n';
+ }
+ while ((c = nextc(p))) {
+ switch (c) {
+ case ' ': case '\t': case '\f': case '\r':
+ case '\13': /* '\v' */
+ space_seen = 1;
+ break;
+ case '#': /* comment as a whitespace */
+ pushback(p, '#');
goto retry;
+ case '\n': /* consecutive newlines */
+ p->lineno++;
+ p->column = 0;
+ pushback(p, '\n');
+ goto retry;
+ case '.':
+ if (!peek(p, '.')) {
+ pushback(p, '.');
+ goto retry;
+ }
+ pushback(p, c);
+ goto normal_newline;
+ case '&':
+ if (peek(p, '.')) {
+ pushback(p, '&');
+ goto retry;
+ }
+ pushback(p, c);
+ goto normal_newline;
+ case -1: /* EOF */
+ case -2: /* end of a file */
+ goto normal_newline;
+ default:
+ pushback(p, c);
+ goto normal_newline;
}
- case -1: /* EOF */
- case -2: /* end of a file */
- goto normal_newline;
- default:
- pushback(p, c);
- goto normal_newline;
}
- }
normal_newline:
- p->cmd_start = TRUE;
- p->lstate = EXPR_BEG;
- return '\n';
+ p->cmd_start = TRUE;
+ p->lstate = EXPR_BEG;
+ return '\n';
case '*':
if ((c = nextc(p)) == '*') {
@@ -4365,7 +4800,16 @@ parser_yylex(parser_state *p)
return tOP_ASGN;
}
pushback(p, c);
- c = tPOW;
+ if (IS_SPCARG(c)) {
+ yywarning(p, "'**' interpreted as argument prefix");
+ c = tDSTAR;
+ }
+ else if (IS_BEG()) {
+ c = tDSTAR;
+ }
+ else {
+ c = tPOW; /* "**", "argument prefix" */
+ }
}
else {
if (c == '=') {
@@ -4578,7 +5022,10 @@ parser_yylex(parser_state *p)
}
if (c2) {
char buf[256];
- snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2);
+ char cc = (char)c2;
+
+ strcpy(buf, "invalid character syntax; use ?\\");
+ strncat(buf, &cc, 1);
yyerror(p, buf);
}
}
@@ -4589,10 +5036,10 @@ parser_yylex(parser_state *p)
}
newtok(p);
/* need support UTF-8 if configured */
- if ((isalnum(c) || c == '_')) {
+ if ((ISALNUM(c) || c == '_')) {
int c2 = nextc(p);
pushback(p, c2);
- if ((isalnum(c2) || c2 == '_')) {
+ if ((ISALNUM(c2) || c2 == '_')) {
goto ternary;
}
}
@@ -4752,6 +5199,7 @@ parser_yylex(parser_state *p)
case '5': case '6': case '7': case '8': case '9':
{
int is_float, seen_point, seen_e, nondigit;
+ int suffix = 0;
is_float = seen_point = seen_e = nondigit = 0;
p->lstate = EXPR_ENDARG;
@@ -4785,7 +5233,10 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 16);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 16, suffix);
return tINTEGER;
}
if (c == 'b' || c == 'B') {
@@ -4809,7 +5260,10 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 2);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 2, suffix);
return tINTEGER;
}
if (c == 'd' || c == 'D') {
@@ -4833,7 +5287,10 @@ parser_yylex(parser_state *p)
no_digits();
}
else if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 10);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 10, suffix);
return tINTEGER;
}
if (c == '_') {
@@ -4866,7 +5323,10 @@ parser_yylex(parser_state *p)
pushback(p, c);
tokfix(p);
if (nondigit) goto trailing_uc;
- pylval.nd = new_int(p, tok(p), 8);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 8, suffix);
return tINTEGER;
}
if (nondigit) {
@@ -4883,7 +5343,10 @@ parser_yylex(parser_state *p)
}
else {
pushback(p, c);
- pylval.nd = new_int(p, "0", 10);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, "0", 10, suffix);
return tINTEGER;
}
}
@@ -4951,13 +5414,13 @@ parser_yylex(parser_state *p)
pushback(p, c);
if (nondigit) {
trailing_uc:
- yyerror_i(p, "trailing '%c' in number", nondigit);
+ yyerror_c(p, "trailing non digit in number: ", (char)nondigit);
}
tokfix(p);
if (is_float) {
#ifdef MRB_WITHOUT_FLOAT
- yywarning_s(p, "floating point numbers are not supported", tok(p));
- pylval.nd = new_int(p, "0", 10);
+ yywarning(p, "floating point numbers are not supported");
+ pylval.nd = new_int(p, "0", 10, 0);
return tINTEGER;
#else
double d;
@@ -4966,17 +5429,23 @@ parser_yylex(parser_state *p)
errno = 0;
d = mrb_float_read(tok(p), &endp);
if (d == 0 && endp == tok(p)) {
- yywarning_s(p, "corrupted float value %s", tok(p));
+ yywarning_s(p, "corrupted float value", tok(p));
}
else if (errno == ERANGE) {
- yywarning_s(p, "float %s out of range", tok(p));
+ yywarning_s(p, "float out of range", tok(p));
errno = 0;
}
- pylval.nd = new_float(p, tok(p));
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_float(p, tok(p), suffix);
return tFLOAT;
#endif
}
- pylval.nd = new_int(p, tok(p), 10);
+ #ifdef MRB_SUFFIX_SUPPORT
+ suffix = number_literal_suffix(p, NUM_SUFFIX_ALL);
+ #endif
+ pylval.nd = new_int(p, tok(p), 10, suffix);
return tINTEGER;
}
@@ -5160,7 +5629,7 @@ parser_yylex(parser_state *p)
}
else {
term = nextc(p);
- if (isalnum(term)) {
+ if (ISALNUM(term)) {
yyerror(p, "unknown type of %string");
return 0;
}
@@ -5304,14 +5773,14 @@ parser_yylex(parser_state *p)
do {
tokadd(p, c);
c = nextc(p);
- } while (c >= 0 && isdigit(c));
+ } while (c >= 0 && ISDIGIT(c));
pushback(p, c);
if (last_state == EXPR_FNAME) goto gvar;
tokfix(p);
{
unsigned long n = strtoul(tok(p), NULL, 10);
if (n > INT_MAX) {
- yyerror_i(p, "capture group index must be <= %d", INT_MAX);
+ yyerror(p, "capture group index must be <= " MRB_STRINGIZE(INT_MAX));
return 0;
}
pylval.nd = new_nth_ref(p, (int)n);
@@ -5346,12 +5815,12 @@ parser_yylex(parser_state *p)
}
return 0;
}
- else if (isdigit(c)) {
+ else if (ISDIGIT(c)) {
if (p->tidx == 1) {
- yyerror_i(p, "'@%c' is not allowed as an instance variable name", c);
+ yyerror_c(p, "wrong instance variable name: @", c);
}
else {
- yyerror_i(p, "'@@%c' is not allowed as a class variable name", c);
+ yyerror_c(p, "wrong class variable name: @@", c);
}
return 0;
}
@@ -5367,7 +5836,15 @@ parser_yylex(parser_state *p)
default:
if (!identchar(c)) {
- yyerror_i(p, "Invalid char '\\x%02X' in expression", c);
+ char buf[36];
+ const char s[] = "Invalid char in expression: 0x";
+ const char hexdigits[] = "0123456789ABCDEF";
+
+ strcpy(buf, s);
+ buf[sizeof(s)-1] = hexdigits[(c & 0xf0) >> 4];
+ buf[sizeof(s)] = hexdigits[(c & 0x0f)];
+ buf[sizeof(s)+1] = 0;
+ yyerror(p, buf);
goto retry;
}
@@ -5503,7 +5980,7 @@ parser_yylex(parser_state *p)
mrb_sym ident = intern_cstr(tok(p));
pylval.id = ident;
- if (last_state != EXPR_DOT && islower(tok(p)[0]) && local_var_p(p, ident)) {
+ if (last_state != EXPR_DOT && ISLOWER(tok(p)[0]) && local_var_p(p, ident)) {
p->lstate = EXPR_END;
}
}
@@ -5534,6 +6011,7 @@ parser_init_cxt(parser_state *p, mrbc_context *cxt)
}
p->capture_errors = cxt->capture_errors;
p->no_optimize = cxt->no_optimize;
+ p->on_eval = cxt->on_eval;
if (cxt->partial_hook) {
p->cxt = cxt;
}
@@ -5546,7 +6024,7 @@ parser_update_cxt(parser_state *p, mrbc_context *cxt)
int i = 0;
if (!cxt) return;
- if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return;
+ if (intn(p->tree->car) != NODE_SCOPE) return;
n0 = n = p->tree->cdr->car;
while (n) {
i++;
@@ -5712,7 +6190,7 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
mrb_sym* new_table;
sym = mrb_intern_cstr(p->mrb, f);
- p->filename = mrb_sym2name_len(p->mrb, sym, NULL);
+ p->filename_sym = sym;
p->lineno = (p->filename_table_length > 0)? 0 : 1;
for (i = 0; i < p->filename_table_length; ++i) {
@@ -5722,7 +6200,11 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
}
}
- p->current_filename_index = (int)p->filename_table_length++;
+ if (p->filename_table_length == UINT16_MAX) {
+ yyerror(p, "too many files to compile");
+ return;
+ }
+ p->current_filename_index = p->filename_table_length++;
new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length);
if (p->filename_table) {
@@ -5732,11 +6214,11 @@ mrb_parser_set_filename(struct mrb_parser_state *p, const char *f)
p->filename_table[p->filename_table_length - 1] = sym;
}
-MRB_API char const*
+MRB_API mrb_sym
mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) {
- if (idx >= p->filename_table_length) { return NULL; }
+ if (idx >= p->filename_table_length) return 0;
else {
- return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL);
+ return p->filename_table[idx];
}
}
@@ -5791,11 +6273,12 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c)
if (c) c->parser_nerr = p->nerr;
if (p->capture_errors) {
char buf[256];
- int n;
- n = snprintf(buf, sizeof(buf), "line %d: %s\n",
- p->error_buffer[0].lineno, p->error_buffer[0].message);
- mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n));
+ strcpy(buf, "line ");
+ dump_int(p->error_buffer[0].lineno, buf+5);
+ strcat(buf, ": ");
+ strncat(buf, p->error_buffer[0].message, sizeof(buf) - strlen(buf) - 1);
+ mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, strlen(buf)));
mrb_parser_free(p);
return mrb_undef_value();
}
@@ -5896,6 +6379,48 @@ dump_recur(mrb_state *mrb, node *tree, int offset)
}
}
+static void
+dump_args(mrb_state *mrb, node *n, int offset)
+{
+ 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(n2, offset+2);
+ printf("%s=\n", mrb_sym2name(mrb, sym(n2->car->car)));
+ mrb_parser_dump(mrb, n2->car->cdr, offset+3);
+ 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) {
+ mrb_assert(intn(n->car) == NODE_ARGS_TAIL);
+ mrb_parser_dump(mrb, n, offset);
+ }
+}
+
#endif
void
@@ -5907,7 +6432,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
if (!tree) return;
again:
dump_prefix(tree, offset);
- nodetype = (int)(intptr_t)tree->car;
+ nodetype = intn(tree->car);
tree = tree->cdr;
switch (nodetype) {
case NODE_BEGIN:
@@ -5967,7 +6492,8 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_LAMBDA:
- printf("NODE_BLOCK:\n");
+ printf("NODE_LAMBDA:\n");
+ dump_prefix(tree, offset);
goto block;
case NODE_BLOCK:
@@ -5975,43 +6501,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
printf("NODE_BLOCK:\n");
tree = tree->cdr;
if (tree->car) {
- node *n = tree->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(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);
- }
- if (n->cdr) {
- dump_prefix(n, offset+1);
- printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr)));
- }
+ dump_args(mrb, tree->car, offset+1);
}
dump_prefix(tree, offset+1);
printf("body:\n");
@@ -6163,7 +6653,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
dump_prefix(tree, offset+1);
printf("method='%s' (%d)\n",
mrb_sym2name(mrb, sym(tree->cdr->car)),
- (int)(intptr_t)tree->cdr->car);
+ intn(tree->cdr->car));
tree = tree->cdr->cdr->car;
if (tree) {
dump_prefix(tree, offset+1);
@@ -6218,6 +6708,19 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
}
break;
+ case NODE_KW_HASH:
+ printf("NODE_KW_HASH:\n");
+ while (tree) {
+ dump_prefix(tree, offset+1);
+ printf("key:\n");
+ mrb_parser_dump(mrb, tree->car->car, offset+2);
+ dump_prefix(tree, offset+1);
+ printf("value:\n");
+ mrb_parser_dump(mrb, tree->car->cdr, offset+2);
+ tree = tree->cdr;
+ }
+ break;
+
case NODE_SPLAT:
printf("NODE_SPLAT:\n");
mrb_parser_dump(mrb, tree, offset+1);
@@ -6280,7 +6783,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
mrb_parser_dump(mrb, tree->car, offset+2);
tree = tree->cdr;
dump_prefix(tree, offset+1);
- printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car);
+ printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), intn(tree->car));
tree = tree->cdr;
mrb_parser_dump(mrb, tree->car, offset+1);
break;
@@ -6362,11 +6865,11 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_BACK_REF:
- printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree);
+ printf("NODE_BACK_REF: $%c\n", intn(tree));
break;
case NODE_NTH_REF:
- printf("NODE_NTH_REF: $%" MRB_PRId "\n", (mrb_int)(intptr_t)tree);
+ printf("NODE_NTH_REF: $%d\n", intn(tree));
break;
case NODE_ARG:
@@ -6379,7 +6882,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_INT:
- printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car);
+ printf("NODE_INT %s base %d\n", (char*)tree->car, intn(tree->cdr->car));
break;
case NODE_FLOAT:
@@ -6392,7 +6895,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_STR:
- printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
+ printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, intn(tree->cdr));
break;
case NODE_DSTR:
@@ -6401,7 +6904,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
break;
case NODE_XSTR:
- printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr);
+ printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, intn(tree->cdr));
break;
case NODE_DXSTR:
@@ -6430,7 +6933,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
case NODE_SYM:
printf("NODE_SYM :%s (%d)\n", mrb_sym2name(mrb, sym(tree)),
- (int)(intptr_t)tree);
+ intn(tree));
break;
case NODE_SELF:
@@ -6546,43 +7049,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
}
tree = tree->cdr;
if (tree->car) {
- node *n = tree->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(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);
- }
- if (n->cdr) {
- dump_prefix(n, offset+1);
- printf("blk=&%s\n", mrb_sym2name(mrb, sym(n->cdr)));
- }
+ dump_args(mrb, tree->car, offset);
}
mrb_parser_dump(mrb, tree->cdr->car, offset+1);
break;
@@ -6595,44 +7062,7 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
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(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(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)));
- }
+ dump_args(mrb, tree->car, offset+1);
}
tree = tree->cdr;
mrb_parser_dump(mrb, tree->car, offset+1);
@@ -6648,6 +7078,37 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset)
dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1);
break;
+ case NODE_ARGS_TAIL:
+ printf("NODE_ARGS_TAIL:\n");
+ {
+ node *kws = tree->car;
+
+ while (kws) {
+ mrb_parser_dump(mrb, kws->car, offset+1);
+ kws = kws->cdr;
+ }
+ }
+ tree = tree->cdr;
+ if (tree->car) {
+ mrb_assert(intn(tree->car->car) == NODE_KW_REST_ARGS);
+ mrb_parser_dump(mrb, tree->car, offset+1);
+ }
+ tree = tree->cdr;
+ if (tree->car) {
+ dump_prefix(tree, offset+1);
+ printf("block='%s'\n", mrb_sym2name(mrb, sym(tree->car)));
+ }
+ break;
+
+ case NODE_KW_ARG:
+ printf("NODE_KW_ARG %s\n", mrb_sym2name(mrb, sym(tree->car)));
+ mrb_parser_dump(mrb, tree->cdr->car, offset + 1);
+ break;
+
+ case NODE_KW_REST_ARGS:
+ printf("NODE_KW_REST_ARGS %s\n", mrb_sym2name(mrb, sym(tree)));
+ break;
+
default:
printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype);
break;
diff --git a/mrbgems/mruby-compiler/mrbgem.rake b/mrbgems/mruby-compiler/mrbgem.rake
index 3bf6d6ae3..fa191e69b 100644
--- a/mrbgems/mruby-compiler/mrbgem.rake
+++ b/mrbgems/mruby-compiler/mrbgem.rake
@@ -23,10 +23,10 @@ MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
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|
+ file "#{current_build_dir}/core/y.tab.c" => ["#{current_dir}/core/parse.y", lex_def] do |t|
+ FileUtils.mkdir_p File.dirname t.name
yacc.run t.name, t.prerequisites.first
end
@@ -35,6 +35,6 @@ MRuby::Gem::Specification.new 'mruby-compiler' do |spec|
gperf.run t.name, t.prerequisites.first
end
- file libfile("#{build.build_dir}/lib/libmruby_core") => core_objs
+ file build.libmruby_core_static => core_objs
build.libmruby << core_objs
end
diff --git a/mrbgems/mruby-complex/mrbgem.rake b/mrbgems/mruby-complex/mrbgem.rake
new file mode 100644
index 000000000..19612e74d
--- /dev/null
+++ b/mrbgems/mruby-complex/mrbgem.rake
@@ -0,0 +1,10 @@
+MRuby::Gem::Specification.new('mruby-complex') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Complex class'
+
+ spec.add_dependency 'mruby-metaprog', core: 'mruby-metaprog'
+ spec.add_dependency 'mruby-object-ext', core: 'mruby-object-ext'
+ spec.add_dependency 'mruby-numeric-ext', core: 'mruby-numeric-ext'
+ spec.add_dependency 'mruby-math', core: 'mruby-math'
+end
diff --git a/mrbgems/mruby-complex/mrblib/complex.rb b/mrbgems/mruby-complex/mrblib/complex.rb
new file mode 100644
index 000000000..0299e7675
--- /dev/null
+++ b/mrbgems/mruby-complex/mrblib/complex.rb
@@ -0,0 +1,123 @@
+class Complex < Numeric
+ def self.polar(abs, arg = 0)
+ Complex(abs * Math.cos(arg), abs * Math.sin(arg))
+ end
+
+ def inspect
+ "(#{to_s})"
+ end
+
+ def to_s
+ "#{real}#{'+' unless imaginary.negative?}#{imaginary}i"
+ end
+
+ def +@
+ Complex(real, imaginary)
+ end
+
+ def -@
+ Complex(-real, -imaginary)
+ end
+
+ def +(rhs)
+ if rhs.is_a? Complex
+ Complex(real + rhs.real, imaginary + rhs.imaginary)
+ elsif rhs.is_a? Numeric
+ Complex(real + rhs, imaginary)
+ end
+ end
+
+ def -(rhs)
+ if rhs.is_a? Complex
+ Complex(real - rhs.real, imaginary - rhs.imaginary)
+ elsif rhs.is_a? Numeric
+ Complex(real - rhs, imaginary)
+ end
+ end
+
+ def *(rhs)
+ if rhs.is_a? Complex
+ Complex(real * rhs.real - imaginary * rhs.imaginary, real * rhs.imaginary + rhs.real * imaginary)
+ elsif rhs.is_a? Numeric
+ Complex(real * rhs, imaginary * rhs)
+ end
+ end
+
+ def /(rhs)
+ if rhs.is_a? Complex
+ div = rhs.real * rhs.real + rhs.imaginary * rhs.imaginary
+ Complex((real * rhs.real + imaginary * rhs.imaginary) / div, (rhs.real * imaginary - real * rhs.imaginary) / div)
+ elsif rhs.is_a? Numeric
+ Complex(real / rhs, imaginary / rhs)
+ end
+ end
+ alias_method :quo, :/
+
+ def ==(rhs)
+ if rhs.is_a? Complex
+ real == rhs.real && imaginary == rhs.imaginary
+ elsif rhs.is_a? Numeric
+ imaginary.zero? && real == rhs
+ end
+ end
+
+ def abs
+ Math.sqrt(abs2)
+ end
+ alias_method :magnitude, :abs
+
+ def abs2
+ real * real + imaginary * imaginary
+ end
+
+ def arg
+ Math.atan2 imaginary, real
+ end
+ alias_method :angle, :arg
+ alias_method :phase, :arg
+
+ def conjugate
+ Complex(real, -imaginary)
+ end
+ alias_method :conj, :conjugate
+
+ def fdiv(numeric)
+ Complex(real.to_f / numeric, imaginary.to_f / numeric)
+ end
+
+ def polar
+ [abs, arg]
+ end
+
+ def real?
+ false
+ end
+
+ def rectangular
+ [real, imaginary]
+ end
+ alias_method :rect, :rectangular
+
+ def to_r
+ raise RangeError.new "can't convert #{to_s} into Rational" unless imaginary.zero?
+ Rational(real, 1)
+ end
+
+ alias_method :imag, :imaginary
+end
+
+[Fixnum, Float].each do |cls|
+ [:+, :-, :*, :/, :==].each do |op|
+ cls.instance_exec do
+ original_operator_name = "__original_operator_#{op}_complex"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Complex
+ Complex(self).send(op, rhs)
+ else
+ send(original_operator_name, rhs)
+ end
+ end
+ end
+ end
+end
diff --git a/mrbgems/mruby-complex/src/complex.c b/mrbgems/mruby-complex/src/complex.c
new file mode 100644
index 000000000..8a0569d68
--- /dev/null
+++ b/mrbgems/mruby-complex/src/complex.c
@@ -0,0 +1,150 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+
+struct mrb_complex {
+ mrb_float real;
+ mrb_float imaginary;
+};
+
+#if defined(MRB_64BIT) || defined(MRB_USE_FLOAT)
+
+#define COMPLEX_USE_ISTRUCT
+/* use TT_ISTRUCT */
+#include <mruby/istruct.h>
+
+#define complex_ptr(mrb, v) (struct mrb_complex*)mrb_istruct_ptr(v)
+
+static struct RBasic*
+complex_alloc(mrb_state *mrb, struct RClass *c, struct mrb_complex **p)
+{
+ struct RIStruct *s;
+
+ s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
+ *p = (struct mrb_complex*)s->inline_data;
+
+ return (struct RBasic*)s;
+}
+
+#else
+/* use TT_DATA */
+#include <mruby/data.h>
+
+static const struct mrb_data_type mrb_complex_type = {"Complex", mrb_free};
+
+static struct RBasic*
+complex_alloc(mrb_state *mrb, struct RClass *c, struct mrb_complex **p)
+{
+ struct RData *d;
+
+ Data_Make_Struct(mrb, c, struct mrb_complex, &mrb_complex_type, *p, d);
+
+ return (struct RBasic*)d;
+}
+
+static struct mrb_complex*
+complex_ptr(mrb_state *mrb, mrb_value v)
+{
+ struct mrb_complex *p;
+
+ p = DATA_GET_PTR(mrb, v, &mrb_complex_type, struct mrb_complex);
+ if (!p) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized complex");
+ }
+ return p;
+}
+#endif
+
+static mrb_value
+complex_new(mrb_state *mrb, mrb_float real, mrb_float imaginary)
+{
+ struct RClass *c = mrb_class_get(mrb, "Complex");
+ struct mrb_complex *p;
+ struct RBasic *comp = complex_alloc(mrb, c, &p);
+ p->real = real;
+ p->imaginary = imaginary;
+ MRB_SET_FROZEN_FLAG(comp);
+
+ return mrb_obj_value(comp);
+}
+
+static mrb_value
+complex_real(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+ return mrb_float_value(mrb, p->real);
+}
+
+static mrb_value
+complex_imaginary(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+ return mrb_float_value(mrb, p->imaginary);
+}
+
+static mrb_value
+complex_s_rect(mrb_state *mrb, mrb_value self)
+{
+ mrb_float real, imaginary = 0.0;
+
+ mrb_get_args(mrb, "f|f", &real, &imaginary);
+ return complex_new(mrb, real, imaginary);
+}
+
+#ifndef MRB_WITHOUT_FLOAT
+static mrb_value
+complex_to_f(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+
+ if (p->imaginary != 0) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %S into Float", self);
+ }
+
+ return mrb_float_value(mrb, p->real);
+}
+#endif
+
+static mrb_value
+complex_to_i(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+
+ if (p->imaginary != 0) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %S into Float", self);
+ }
+ return mrb_int_value(mrb, p->real);
+}
+
+static mrb_value
+complex_to_c(mrb_state *mrb, mrb_value self)
+{
+ return self;
+}
+
+void mrb_mruby_complex_gem_init(mrb_state *mrb)
+{
+ struct RClass *comp;
+
+#ifdef COMPLEX_USE_ISTRUCT
+ mrb_assert(sizeof(struct mrb_complex) < ISTRUCT_DATA_SIZE);
+#endif
+ comp = mrb_define_class(mrb, "Complex", mrb_class_get(mrb, "Numeric"));
+ //MRB_SET_INSTANCE_TT(comp, MRB_TT_ISTRUCT);
+ mrb_undef_class_method(mrb, comp, "new");
+ mrb_define_class_method(mrb, comp, "rectangular", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_class_method(mrb, comp, "rect", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, mrb->kernel_module, "Complex", complex_s_rect, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, comp, "real", complex_real, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "imaginary", complex_imaginary, MRB_ARGS_NONE());
+#ifndef MRB_WITHOUT_FLOAT
+ mrb_define_method(mrb, comp, "to_f", complex_to_f, MRB_ARGS_NONE());
+#endif
+ mrb_define_method(mrb, comp, "to_i", complex_to_i, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "to_c", complex_to_c, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_complex_gem_final(mrb_state* mrb)
+{
+}
diff --git a/mrbgems/mruby-complex/test/complex.rb b/mrbgems/mruby-complex/test/complex.rb
new file mode 100644
index 000000000..5b7c3bfa4
--- /dev/null
+++ b/mrbgems/mruby-complex/test/complex.rb
@@ -0,0 +1,136 @@
+def assert_complex(real, exp)
+ assert do
+ assert_float real.real, exp.real
+ assert_float real.imaginary, exp.imaginary
+ end
+end
+
+assert 'Complex' do
+ c = 123i
+ assert_equal Complex, c.class
+ assert_equal [c.real, c.imaginary], [0, 123]
+ c = 123 + -1.23i
+ assert_equal Complex, c.class
+ assert_equal [c.real, c.imaginary], [123, -1.23]
+end
+
+assert 'Complex::polar' do
+ assert_complex Complex.polar(3, 0), (3 + 0i)
+ assert_complex Complex.polar(3, Math::PI/2), (0 + 3i)
+ assert_complex Complex.polar(3, Math::PI), (-3 + 0i)
+ assert_complex Complex.polar(3, -Math::PI/2), (0 + -3i)
+end
+
+assert 'Complex::rectangular' do
+ assert_complex Complex.rectangular(1, 2), (1 + 2i)
+end
+
+assert 'Complex#*' do
+ assert_complex Complex(2, 3) * Complex(2, 3), (-5 + 12i)
+ assert_complex Complex(900) * Complex(1), (900 + 0i)
+ assert_complex Complex(-2, 9) * Complex(-9, 2), (0 - 85i)
+ assert_complex Complex(9, 8) * 4, (36 + 32i)
+ assert_complex Complex(20, 9) * 9.8, (196.0 + 88.2i)
+end
+
+assert 'Complex#+' do
+ assert_complex Complex(2, 3) + Complex(2, 3) , (4 + 6i)
+ assert_complex Complex(900) + Complex(1) , (901 + 0i)
+ assert_complex Complex(-2, 9) + Complex(-9, 2), (-11 + 11i)
+ assert_complex Complex(9, 8) + 4 , (13 + 8i)
+ assert_complex Complex(20, 9) + 9.8 , (29.8 + 9i)
+end
+
+assert 'Complex#-' do
+ assert_complex Complex(2, 3) - Complex(2, 3) , (0 + 0i)
+ assert_complex Complex(900) - Complex(1) , (899 + 0i)
+ assert_complex Complex(-2, 9) - Complex(-9, 2), (7 + 7i)
+ assert_complex Complex(9, 8) - 4 , (5 + 8i)
+ assert_complex Complex(20, 9) - 9.8 , (10.2 + 9i)
+end
+
+assert 'Complex#-@' do
+ assert_complex(-Complex(1, 2), (-1 - 2i))
+end
+
+assert 'Complex#/' do
+ assert_complex Complex(2, 3) / Complex(2, 3) , (1 + 0i)
+ assert_complex Complex(900) / Complex(1) , (900 + 0i)
+ assert_complex Complex(-2, 9) / Complex(-9, 2), ((36 / 85) - (77i / 85))
+ assert_complex Complex(9, 8) / 4 , ((9 / 4) + 2i)
+ assert_complex Complex(20, 9) / 9.8 , (2.0408163265306123 + 0.9183673469387754i)
+end
+
+assert 'Complex#==' do
+ assert_true Complex(2, 3) == Complex(2, 3)
+ assert_true Complex(5) == 5
+ assert_true Complex(0) == 0.0
+end
+
+assert 'Complex#abs' do
+ assert_float Complex(-1).abs, 1
+ assert_float Complex(3.0, -4.0).abs, 5.0
+end
+
+assert 'Complex#abs2' do
+ assert_float Complex(-1).abs2, 1
+ assert_float Complex(3.0, -4.0).abs2, 25.0
+end
+
+assert 'Complex#arg' do
+ assert_float Complex.polar(3, Math::PI/2).arg, 1.5707963267948966
+end
+
+assert 'Complex#conjugate' do
+ assert_complex Complex(1, 2).conjugate, (1 - 2i)
+end
+
+assert 'Complex#fdiv' do
+ assert_complex Complex(11, 22).fdiv(3), (3.6666666666666665 + 7.333333333333333i)
+end
+
+assert 'Complex#imaginary' do
+ assert_float Complex(7).imaginary , 0
+ assert_float Complex(9, -4).imaginary, -4
+end
+
+assert 'Complex#polar' do
+ assert_equal Complex(1, 2).polar, [2.23606797749979, 1.1071487177940904]
+end
+
+assert 'Complex#real' do
+ assert_float Complex(7).real, 7
+ assert_float Complex(9, -4).real, 9
+end
+
+assert 'Complex#real?' do
+ assert_false Complex(1).real?
+end
+
+assert 'Complex::rectangular' do
+ assert_equal Complex(1, 2).rectangular, [1, 2]
+end
+
+assert 'Complex::to_c' do
+ assert_equal Complex(1, 2).to_c, Complex(1, 2)
+end
+
+assert 'Complex::to_f' do
+ assert_float Complex(1, 0).to_f, 1.0
+ assert_raise(RangeError) do
+ Complex(1, 2).to_f
+ end
+end
+
+assert 'Complex::to_i' do
+ assert_equal Complex(1, 0).to_i, 1
+ assert_raise(RangeError) do
+ Complex(1, 2).to_i
+ end
+end
+
+assert 'Complex#frozen?' do
+ assert_predicate(1i, :frozen?)
+ assert_predicate(Complex(2,3), :frozen?)
+ assert_predicate(4+5i, :frozen?)
+end
diff --git a/mrbgems/mruby-enum-chain/mrbgem.rake b/mrbgems/mruby-enum-chain/mrbgem.rake
new file mode 100644
index 000000000..7294f2644
--- /dev/null
+++ b/mrbgems/mruby-enum-chain/mrbgem.rake
@@ -0,0 +1,6 @@
+MRuby::Gem::Specification.new('mruby-enum-chain') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Enumerator::Chain class'
+ spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator')
+end
diff --git a/mrbgems/mruby-enum-chain/mrblib/chain.rb b/mrbgems/mruby-enum-chain/mrblib/chain.rb
new file mode 100644
index 000000000..98515ea14
--- /dev/null
+++ b/mrbgems/mruby-enum-chain/mrblib/chain.rb
@@ -0,0 +1,60 @@
+##
+# chain.rb Enumerator::Chain class
+# See Copyright Notice in mruby.h
+
+module Enumerable
+ def chain(*args)
+ Enumerator::Chain.new(self, *args)
+ end
+
+ def +(other)
+ Enumerator::Chain.new(self, other)
+ end
+end
+
+class Enumerator
+ class Chain
+ include Enumerable
+
+ def initialize(*args)
+ @enums = args
+ end
+
+ def initialize_copy(orig)
+ @enums = orig.__copy_enums
+ end
+
+ def each(&block)
+ return to_enum unless block_given?
+
+ @enums.each { |e| e.each(&block) }
+
+ self
+ end
+
+ def size
+ @enums.reduce(0) do |a, e|
+ return nil unless e.respond_to?(:size)
+ a + e.size
+ end
+ end
+
+ def rewind
+ @enums.reverse_each do |e|
+ e.rewind if e.respond_to?(:rewind)
+ end
+
+ self
+ end
+
+ def inspect
+ "#<#{self.class}: #{@enums.inspect}>"
+ end
+
+ def __copy_enums
+ @enums.each_with_object([]) do |e, a|
+ a << e.clone
+ end
+ end
+ end
+end
diff --git a/mrbgems/mruby-enum-chain/test/enum_chain.rb b/mrbgems/mruby-enum-chain/test/enum_chain.rb
new file mode 100644
index 000000000..4dd59bd37
--- /dev/null
+++ b/mrbgems/mruby-enum-chain/test/enum_chain.rb
@@ -0,0 +1,76 @@
+##
+# Enumerator::Chain test
+
+assert("Enumerable#chain") do
+ a = []
+ b = {}
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, a.chain
+ assert_kind_of Enumerator::Chain, a.chain(b)
+ assert_kind_of Enumerator::Chain, a.chain(b, c)
+ assert_raise(NoMethodError) { c.chain }
+end
+
+assert("Enumerable#+") do
+ a = [].each
+ b = {}.reverse_each
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, a + b
+ assert_kind_of Enumerator::Chain, a + c
+ assert_kind_of Enumerator::Chain, b + a
+ assert_kind_of Enumerator::Chain, b + c
+ assert_raise(NoMethodError) { c + a }
+end
+
+assert("Enumerator.new") do
+ a = []
+ b = {}
+ c = Object.new # not has #each method
+
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, a)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, b)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(a, c)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, a)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, b)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(b, c)
+ assert_kind_of Enumerator::Chain, Enumerator::Chain.new(c, a)
+end
+
+assert("Enumerator::Chain#each") do
+ a = [1, 2, 3]
+
+ aa = a.chain(a)
+ assert_kind_of Enumerator, aa.each
+ assert_equal [1, 2, 3, 1, 2, 3], aa.each.to_a
+
+ aa = a.chain(a.reverse_each)
+ assert_kind_of Enumerator, aa.each
+ assert_equal [1, 2, 3, 3, 2, 1], aa.each.to_a
+
+ aa = a.chain(a.reverse_each, a)
+ assert_kind_of Enumerator, aa.each
+ assert_equal [1, 2, 3, 3, 2, 1, 1, 2, 3], aa.each.to_a
+
+ aa = a.chain(Object.new)
+ assert_kind_of Enumerator, aa.each
+ assert_raise(NoMethodError) { aa.each.to_a }
+end
+
+assert("Enumerator::Chain#size") do
+ a = [1, 2, 3]
+
+ aa = a.chain(a)
+ assert_equal 6, aa.size
+
+ aa = a.chain(a.reverse_each)
+ assert_nil aa.size
+
+ aa = a.chain(a.reverse_each, a)
+ assert_nil aa.size
+
+ aa = a.chain(Object.new)
+ assert_nil aa.size
+end
diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb
index a840ade3b..99b9cddba 100644
--- a/mrbgems/mruby-enum-ext/mrblib/enum.rb
+++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb
@@ -13,10 +13,9 @@ module Enumerable
# a.drop(3) #=> [4, 5, 0]
def drop(n)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ n = n.__to_int
raise ArgumentError, "attempt to drop negative size" if n < 0
- n = n.to_int
ary = []
self.each {|*val| n == 0 ? ary << val.__svalue : n -= 1 }
ary
@@ -57,8 +56,8 @@ module Enumerable
# a.take(3) #=> [1, 2, 3]
def take(n)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
- i = n.to_int
+ n = n.__to_int
+ i = n.to_i
raise ArgumentError, "attempt to take negative size" if i < 0
ary = []
return ary if i == 0
@@ -113,12 +112,12 @@ module Enumerable
# [8, 9, 10]
def each_cons(n, &block)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ n = n.__to_int
raise ArgumentError, "invalid size" if n <= 0
return to_enum(:each_cons,n) unless block
ary = []
- n = n.to_int
+ n = n.to_i
self.each do |*val|
ary.shift if ary.size == n
ary << val.__svalue
@@ -141,12 +140,12 @@ module Enumerable
# [10]
def each_slice(n, &block)
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
+ n = n.__to_int
raise ArgumentError, "invalid slice size" if n <= 0
return to_enum(:each_slice,n) unless block
ary = []
- n = n.to_int
+ n = n.to_i
self.each do |*val|
ary << val.__svalue
if ary.size == n
@@ -201,14 +200,11 @@ module Enumerable
ary.push([block.call(e), i])
}
if ary.size > 1
- ary.__sort_sub__(0, ary.size - 1) do |a,b|
- a <=> b
- end
+ ary.sort!
end
ary.collect{|e,i| orig[i]}
end
- NONE = Object.new
##
# call-seq:
# enum.first -> obj or nil
@@ -225,9 +221,7 @@ module Enumerable
end
return nil
when 1
- n = args[0]
- raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int)
- i = n.to_int
+ i = args[0].__to_int
raise ArgumentError, "attempt to take negative size" if i < 0
ary = []
return ary if i == 0
@@ -675,13 +669,7 @@ module Enumerable
if nv.nil?
n = -1
else
- unless nv.respond_to?(:to_int)
- raise TypeError, "no implicit conversion of #{nv.class} into Integer"
- end
- n = nv.to_int
- unless n.kind_of?(Integer)
- raise TypeError, "no implicit conversion of #{nv.class} into Integer"
- end
+ n = nv.__to_int
return nil if n <= 0
end
@@ -803,13 +791,22 @@ module Enumerable
# # => {:hello => 0, :world => 1}
#
- def to_h
+ def to_h(&blk)
h = {}
- self.each do |*v|
- v = v.__svalue
- raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array
- raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2
- h[v[0]] = v[1]
+ if blk
+ self.each do |v|
+ v = blk.call(v)
+ raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array
+ raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2
+ h[v[0]] = v[1]
+ end
+ else
+ self.each do |*v|
+ v = v.__svalue
+ raise TypeError, "wrong element type #{v.class} (expected Array)" unless v.is_a? Array
+ raise ArgumentError, "element has wrong array length (expected 2, was #{v.size})" if v.size != 2
+ h[v[0]] = v[1]
+ end
end
h
end
diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb
index 46ed5f0f9..64b1bbda9 100644
--- a/mrbgems/mruby-enum-ext/test/enum.rb
+++ b/mrbgems/mruby-enum-ext/test/enum.rb
@@ -128,14 +128,14 @@ assert("Enumerable#any? (enhancement)") do
end
assert("Enumerable#each_with_object") do
- assert_true [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], (1..10).each_with_object([]) { |i, a| a << i*2 }
+ assert_equal [2, 4, 6, 8, 10, 12, 14, 16, 18, 20], (1..10).each_with_object([]) { |i, a| a << i*2 }
assert_raise(ArgumentError) { (1..10).each_with_object() { |i, a| a << i*2 } }
end
assert("Enumerable#reverse_each") do
r = (1..3)
a = []
- assert_equal (1..3), r.reverse_each { |v| a << v }
+ assert_same r, r.reverse_each { |v| a << v }
assert_equal [3, 2, 1], a
end
@@ -188,4 +188,6 @@ assert("Enumerable#to_h") do
assert_equal h0, h
# mruby-enum-ext also provides nil.to_h
assert_equal Hash.new, nil.to_h
+
+ assert_equal({1=>4,3=>8}, c.new.to_h{|k,v|[k,v*2]})
end
diff --git a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
index 9227abe8a..e4f116a93 100644
--- a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
+++ b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb
@@ -44,7 +44,7 @@ class Enumerator
def to_enum(meth=:each, *args, &block)
unless self.respond_to?(meth)
- raise NoMethodError, "undefined method #{meth}"
+ raise ArgumentError, "undefined method #{meth}"
end
lz = Lazy.new(self, &block)
lz.obj = self
diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
index 7ca1d5eb6..89472ef01 100644
--- a/mrbgems/mruby-enumerator/mrblib/enumerator.rb
+++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb
@@ -109,27 +109,30 @@ class Enumerator
#
# p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
#
- def initialize(obj=nil, meth=:each, *args, &block)
+ # In the second, deprecated, form, a generated Enumerator iterates over the
+ # given object using the given method with the given arguments passed. This
+ # form is left only for internal use.
+ #
+ # Use of this form is discouraged. Use Kernel#enum_for or Kernel#to_enum
+ # instead.
+ def initialize(obj=NONE, meth=:each, *args, &block)
if block
obj = Generator.new(&block)
- else
- raise ArgumentError unless obj
- end
- if @obj and !self.respond_to?(meth)
- raise NoMethodError, "undefined method #{meth}"
+ elsif obj == NONE
+ raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
end
@obj = obj
@meth = meth
- @args = args.dup
+ @args = args
@fib = nil
@dst = nil
@lookahead = nil
@feedvalue = nil
@stop_exc = false
end
- attr_accessor :obj, :meth, :args, :fib
- private :obj, :meth, :args, :fib
+ attr_accessor :obj, :meth, :args
+ attr_reader :fib
def initialize_copy(obj)
raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator
@@ -157,12 +160,10 @@ class Enumerator
def with_index(offset=0, &block)
return to_enum :with_index, offset unless block
- offset = if offset.nil?
- 0
- elsif offset.respond_to?(:to_int)
- offset.to_int
+ if offset.nil?
+ offset = 0
else
- raise TypeError, "no implicit conversion of #{offset.class} into Integer"
+ offset = offset.__to_int
end
n = offset - 1
@@ -223,13 +224,11 @@ class Enumerator
end
def inspect
- return "#<#{self.class}: uninitialized>" unless @obj
-
if @args && @args.size > 0
args = @args.join(", ")
- "#<#{self.class}: #{@obj}:#{@meth}(#{args})>"
+ "#<#{self.class}: #{@obj.inspect}:#{@meth}(#{args})>"
else
- "#<#{self.class}: #{@obj}:#{@meth}>"
+ "#<#{self.class}: #{@obj.inspect}:#{@meth}>"
end
end
@@ -245,9 +244,10 @@ class Enumerator
#
# === Examples
#
- # "Hello, world!".scan(/\w+/) #=> ["Hello", "world"]
- # "Hello, world!".to_enum(:scan, /\w+/).to_a #=> ["Hello", "world"]
- # "Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"]
+ # Array.new(3) #=> [nil, nil, nil]
+ # Array.new(3) { |i| i } #=> [0, 1, 2]
+ # Array.to_enum(:new, 3).to_a #=> [0, 1, 2]
+ # Array.to_enum(:new).each(3).to_a #=> [0, 1, 2]
#
# obj = Object.new
#
@@ -623,9 +623,7 @@ module Enumerable
# use Enumerator to use infinite sequence
def zip(*args, &block)
args = args.map do |a|
- if a.respond_to?(:to_ary)
- a.to_ary.to_enum(:each)
- elsif a.respond_to?(:each)
+ if a.respond_to?(:each)
a.to_enum(:each)
else
raise TypeError, "wrong argument type #{a.class} (must respond to :each)"
diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb
index 428ea0307..d609cadb5 100644
--- a/mrbgems/mruby-enumerator/test/enumerator.rb
+++ b/mrbgems/mruby-enumerator/test/enumerator.rb
@@ -6,11 +6,11 @@ class << @obj
end
end
-assert 'Enumerator' do
+assert 'Enumerator.class' do
assert_equal Class, Enumerator.class
end
-assert 'Enumerator' do
+assert 'Enumerator.superclass' do
assert_equal Object, Enumerator.superclass
end
@@ -19,11 +19,8 @@ assert 'Enumerator.new' do
assert_equal [:x,:y,:z], [:x,:y,:z].each.map{|i| i}.sort
assert_equal [[:x,1],[:y,2]], {x:1, y:2}.each.map{|i| i}.sort
assert_equal [1,2,3], @obj.to_enum(:foo, 1,2,3).to_a
- assert_equal [1,2,3], Enumerator.new(@obj, :foo, 1,2,3).to_a
assert_equal [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }.take(3)
assert_raise(ArgumentError) { Enumerator.new }
- enum = @obj.to_enum
- assert_raise(NoMethodError) { enum.each {} }
# examples
fib = Enumerator.new do |y|
@@ -55,12 +52,6 @@ assert 'Enumerator#with_index' do
assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a
end
-assert 'Enumerator#with_index nonnum offset' do
- s = Object.new
- def s.to_int; 1 end
- assert_equal([[1,1],[2,2],[3,3]], @obj.to_enum(:foo, 1, 2, 3).with_index(s).to_a)
-end
-
assert 'Enumerator#with_index string offset' do
assert_raise(TypeError){ @obj.to_enum(:foo, 1, 2, 3).with_index('1').to_a }
end
@@ -99,11 +90,13 @@ end
assert 'Enumerator#inspect' do
e = (0..10).each
- assert_equal("#<Enumerator: 0..10:each>", e.inspect)
- e = Enumerator.new("FooObject", :foo, 1)
- assert_equal("#<Enumerator: FooObject:foo(1)>", e.inspect)
- e = Enumerator.new("FooObject", :foo, 1, 2, 3)
- assert_equal("#<Enumerator: FooObject:foo(1, 2, 3)>", e.inspect)
+ assert_equal('#<Enumerator: 0..10:each>', e.inspect)
+ e = 'FooObject'.enum_for(:foo, 1)
+ assert_equal('#<Enumerator: "FooObject":foo(1)>', e.inspect)
+ e = 'FooObject'.enum_for(:foo, 1, 2, 3)
+ assert_equal('#<Enumerator: "FooObject":foo(1, 2, 3)>', e.inspect)
+ e = nil.enum_for(:to_s)
+ assert_equal('#<Enumerator: nil:to_s>', e.inspect)
end
assert 'Enumerator#each' do
@@ -425,8 +418,10 @@ assert 'nested iteration' do
end
assert 'Kernel#to_enum' do
+ e = nil
assert_equal Enumerator, [].to_enum.class
- assert_raise(ArgumentError){ nil.to_enum }
+ assert_nothing_raised { e = [].to_enum(:_not_implemented_) }
+ assert_raise(NoMethodError) { e.first }
end
assert 'modifying existing methods' do
diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c
index 14e89ac14..30534aaec 100644
--- a/mrbgems/mruby-eval/src/eval.c
+++ b/mrbgems/mruby-eval/src/eval.c
@@ -44,7 +44,7 @@ search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom)
return NULL;
}
-static inline mrb_code
+static uint16_t
search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
{
mrb_irep *virep;
@@ -57,7 +57,7 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest)
}
for (pos = 0; pos < virep->nlocals - 1; pos++) {
if (vsym == virep->lv[pos].name) {
- return (MKARG_B(pos + 1) | MKARG_C(level + bnest));
+ return (pos+1)<<8 | (level+bnest);
}
}
}
@@ -71,8 +71,8 @@ irep_argc(mrb_irep *irep)
mrb_code c;
c = irep->iseq[0];
- if (GET_OPCODE(c) == OP_ENTER) {
- mrb_aspec ax = GETARG_Ax(c);
+ if (c == OP_ENTER) {
+ mrb_aspec ax = PEEK_W(irep->iseq+1);
/* extra 1 means a slot for block */
return MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1;
}
@@ -88,95 +88,132 @@ potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals)
return TRUE;
}
+extern uint8_t mrb_insn_size[];
+extern uint8_t mrb_insn_size1[];
+extern uint8_t mrb_insn_size2[];
+extern uint8_t mrb_insn_size3[];
+
static void
patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top)
{
int i;
- mrb_code c;
+ uint32_t a;
+ uint16_t b;
+ uint8_t c;
+ mrb_code insn;
int argc = irep_argc(irep);
- for (i = 0; i < irep->ilen; i++) {
- c = irep->iseq[i];
- switch(GET_OPCODE(c)){
+ for (i = 0; i < irep->ilen; ) {
+ insn = irep->iseq[i];
+ switch(insn){
case OP_EPUSH:
- patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1, top);
+ b = PEEK_S(irep->iseq+i+1);
+ patch_irep(mrb, irep->reps[b], bnest + 1, top);
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, top);
- }
- }
+ case OP_BLOCK:
+ a = PEEK_B(irep->iseq+i+1);
+ b = PEEK_B(irep->iseq+i+2);
+ patch_irep(mrb, irep->reps[b], bnest + 1, top);
break;
case OP_SEND:
- if (GETARG_C(c) != 0) {
+ b = PEEK_B(irep->iseq+i+2);
+ c = PEEK_B(irep->iseq+i+3);
+ if (c != 0) {
break;
}
else {
- mrb_code arg = search_variable(mrb, irep->syms[GETARG_B(c)], bnest);
+ uint16_t arg = search_variable(mrb, irep->syms[b], bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ irep->iseq[i] = OP_GETUPVAR;
+ irep->iseq[i+2] = arg >> 8;
+ irep->iseq[i+3] = arg & 0xff;
}
}
break;
case OP_MOVE:
+ a = PEEK_B(irep->iseq+i+1);
+ b = PEEK_B(irep->iseq+i+2);
/* src part */
- 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 (potential_upvar_p(irep->lv, b, argc, irep->nlocals)) {
+ uint16_t arg = search_variable(mrb, irep->lv[b - 1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ irep->iseq[i] = insn = OP_GETUPVAR;
+ irep->iseq[i+2] = arg >> 8;
+ irep->iseq[i+3] = arg & 0xff;
}
}
/* dst part */
- 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 (potential_upvar_p(irep->lv, a, argc, irep->nlocals)) {
+ uint16_t arg = search_variable(mrb, irep->lv[a - 1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_B(c)) | arg;
+ irep->iseq[i] = insn = OP_SETUPVAR;
+ irep->iseq[i+1] = (mrb_code)b;
+ irep->iseq[i+2] = arg >> 8;
+ irep->iseq[i+3] = arg & 0xff;
}
}
break;
case OP_GETUPVAR:
+ a = PEEK_B(irep->iseq+i+1);
+ b = PEEK_B(irep->iseq+i+2);
+ c = PEEK_B(irep->iseq+i+3);
{
- int lev = GETARG_C(c)+1;
+ int lev = c+1;
mrb_irep *tmp = search_irep(top, bnest, lev, irep);
- if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
- mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
+ if (potential_upvar_p(tmp->lv, b, irep_argc(tmp), tmp->nlocals)) {
+ uint16_t arg = search_variable(mrb, tmp->lv[b-1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ irep->iseq[i] = OP_GETUPVAR;
+ irep->iseq[i+2] = arg >> 8;
+ irep->iseq[i+3] = arg & 0xff;
}
}
}
break;
case OP_SETUPVAR:
+ a = PEEK_B(irep->iseq+i+1);
+ b = PEEK_B(irep->iseq+i+2);
+ c = PEEK_B(irep->iseq+i+3);
{
- int lev = GETARG_C(c)+1;
+ int lev = c+1;
mrb_irep *tmp = search_irep(top, bnest, lev, irep);
- if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) {
- mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest);
+ if (potential_upvar_p(tmp->lv, b, irep_argc(tmp), tmp->nlocals)) {
+ uint16_t arg = search_variable(mrb, tmp->lv[b-1].name, bnest);
if (arg != 0) {
/* must replace */
- irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_A(c)) | arg;
+ irep->iseq[i] = OP_SETUPVAR;
+ irep->iseq[i+1] = a;
+ irep->iseq[i+2] = arg >> 8;
+ irep->iseq[i+3] = arg & 0xff;
}
}
}
break;
- case OP_STOP:
- if (mrb->c->ci->acc >= 0) {
- irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL);
- }
- break;
+ case OP_EXT1:
+ insn = PEEK_B(irep->iseq+i+1);
+ i += mrb_insn_size1[insn]+1;
+ continue;
+ case OP_EXT2:
+ insn = PEEK_B(irep->iseq+i+1);
+ i += mrb_insn_size2[insn]+1;
+ continue;
+ case OP_EXT3:
+ insn = PEEK_B(irep->iseq+i+1);
+ i += mrb_insn_size3[insn]+1;
+ continue;
}
+ i+=mrb_insn_size[insn];
}
}
@@ -189,7 +226,7 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
struct mrb_parser_state *p;
struct RProc *proc;
struct REnv *e;
- mrb_callinfo *ci = &mrb->c->ci[-1]; /* callinfo of eval caller */
+ mrb_callinfo *ci; /* callinfo of eval caller */
struct RClass *target_class = NULL;
int bidx;
@@ -198,11 +235,12 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
}
cxt = mrbc_context_new(mrb);
- cxt->lineno = (short)line;
+ cxt->lineno = (uint16_t)line;
mrbc_filename(mrb, cxt, file ? file : "(eval)");
cxt->capture_errors = TRUE;
cxt->no_optimize = TRUE;
+ cxt->on_eval = TRUE;
p = mrb_parse_nstring(mrb, s, len, cxt);
@@ -238,8 +276,16 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
mrbc_context_free(mrb, cxt);
mrb_raise(mrb, E_SCRIPT_ERROR, "codegen error");
}
- target_class = MRB_PROC_TARGET_CLASS(ci->proc);
- if (!MRB_PROC_CFUNC_P(ci->proc)) {
+ if (mrb->c->ci > mrb->c->cibase) {
+ ci = &mrb->c->ci[-1];
+ }
+ else {
+ ci = mrb->c->cibase;
+ }
+ if (ci->proc) {
+ target_class = MRB_PROC_TARGET_CLASS(ci->proc);
+ }
+ if (ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) {
if (ci->env) {
e = ci->env;
}
@@ -254,6 +300,7 @@ create_proc_from_string(mrb_state *mrb, char *s, mrb_int len, mrb_value binding,
if (ci->argc < 0) bidx = 2;
else bidx += 1;
MRB_ENV_SET_BIDX(e, bidx);
+ ci->env = e;
}
proc->e.env = e;
proc->flags |= MRB_PROC_ENVSET;
@@ -276,10 +323,12 @@ exec_irep(mrb_state *mrb, mrb_value self, struct RProc *proc)
/* no argument passed from eval() */
mrb->c->ci->argc = 0;
if (mrb->c->ci->acc < 0) {
+ ptrdiff_t cioff = mrb->c->ci - mrb->c->cibase;
mrb_value ret = mrb_top_run(mrb, proc, self, 0);
if (mrb->exc) {
mrb_exc_raise(mrb, mrb_obj_value(mrb->exc));
}
+ mrb->c->ci = mrb->c->cibase + cioff;
return ret;
}
/* clear block */
@@ -325,6 +374,7 @@ f_instance_eval(mrb_state *mrb, mrb_value self)
proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line);
MRB_PROC_SET_TARGET_CLASS(proc, mrb_class_ptr(cv));
mrb_assert(!MRB_PROC_CFUNC_P(proc));
+ mrb->c->ci->target_class = mrb_class_ptr(cv);
return exec_irep(mrb, self, proc);
}
else {
@@ -337,7 +387,7 @@ void
mrb_mruby_eval_gem_init(mrb_state* mrb)
{
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));
+ mrb_define_method(mrb, mrb_class_get(mrb, "BasicObject"), "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 66ca1fcdb..4930259c1 100644
--- a/mrbgems/mruby-eval/test/eval.rb
+++ b/mrbgems/mruby-eval/test/eval.rb
@@ -34,7 +34,7 @@ assert('Kernel.eval', '15.3.1.2.3') do
}
assert_equal(2) {
a = 10
- Kernel.eval 'def f(a); b=a.send(:+, 1); end'
+ Kernel.eval 'def f(a); b=a+1; end'
f(1)
}
end
@@ -58,7 +58,7 @@ end
assert('String instance_eval') do
obj = Object.new
- obj.instance_variable_set :@test, 'test'
+ obj.instance_eval{ @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)}
@@ -80,7 +80,7 @@ assert('Kernel.#eval(string) context') do
assert_equal('class') { obj.const_string }
end
-assert('Object#instance_eval with begin-rescue-ensure execution order') do
+assert('BasicObject#instance_eval with begin-rescue-ensure execution order') do
class HellRaiser
def raise_hell
order = [:enter_raise_hell]
@@ -99,3 +99,34 @@ assert('Object#instance_eval with begin-rescue-ensure execution order') do
hell_raiser = HellRaiser.new
assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell)
end
+
+assert('BasicObject#instance_eval to define singleton methods Issue #3141') do
+ foo_class = Class.new do
+ def bar(x)
+ instance_eval "def baz; #{x}; end"
+ end
+ end
+
+ f1 = foo_class.new
+ f2 = foo_class.new
+ f1.bar 1
+ f2.bar 2
+ assert_equal(1){f1.baz}
+ assert_equal(2){f2.baz}
+end
+
+assert('Kernel.#eval(string) Issue #4021') do
+ assert_equal('FOO') { (eval <<'EOS').call }
+foo = "FOO"
+Proc.new { foo }
+EOS
+ assert_equal('FOO') {
+ def do_eval(code)
+ eval(code)
+ end
+ do_eval(<<'EOS').call
+foo = "FOO"
+Proc.new { foo }
+EOS
+ }
+end
diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c
index 83153a9df..17ce77c5d 100644
--- a/mrbgems/mruby-fiber/src/fiber.c
+++ b/mrbgems/mruby-fiber/src/fiber.c
@@ -127,7 +127,6 @@ fiber_init(mrb_state *mrb, mrb_value self)
ci->proc = p;
mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p);
ci->pc = p->body.irep->iseq;
- ci->nregs = p->body.irep->nregs;
ci[1] = ci[0];
c->ci++; /* push dummy callinfo */
@@ -175,6 +174,9 @@ fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c)
static void
fiber_switch_context(mrb_state *mrb, struct mrb_context *c)
{
+ if (mrb->c->fib) {
+ mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
+ }
c->status = MRB_FIBER_RUNNING;
mrb->c = c;
}
@@ -184,26 +186,30 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
{
struct mrb_context *c = fiber_check(mrb, self);
struct mrb_context *old_c = mrb->c;
+ enum mrb_fiber_state status;
mrb_value value;
fiber_check_cfunc(mrb, c);
- if (resume && c->status == MRB_FIBER_TRANSFERRED) {
+ status = c->status;
+ if (resume && status == MRB_FIBER_TRANSFERRED) {
mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
}
- if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) {
- mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)");
+ if (status == MRB_FIBER_RUNNING || status == MRB_FIBER_RESUMED) {
+ mrb_raise(mrb, E_FIBER_ERROR, "double resume");
}
- if (c->status == MRB_FIBER_TERMINATED) {
+ if (status == MRB_FIBER_TERMINATED) {
mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
}
- mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
+ old_c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
- if (c->status == MRB_FIBER_CREATED) {
+ fiber_switch_context(mrb, c);
+ if (status == MRB_FIBER_CREATED) {
mrb_value *b, *e;
- if (len >= c->stend - c->stack) {
- mrb_raise(mrb, E_FIBER_ERROR, "too many arguments to fiber");
+ if (!c->ci->proc) {
+ mrb_raise(mrb, E_FIBER_ERROR, "double resume (current)");
}
+ mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */
b = c->stack+1;
e = b + len;
while (b<e) {
@@ -215,7 +221,6 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
else {
value = fiber_result(mrb, a, len);
}
- fiber_switch_context(mrb, c);
if (vmexec) {
c->vmexec = TRUE;
diff --git a/mrbgems/mruby-fiber/test/fiber.rb b/mrbgems/mruby-fiber/test/fiber.rb
index d063a0a62..b6ecb798c 100644
--- a/mrbgems/mruby-fiber/test/fiber.rb
+++ b/mrbgems/mruby-fiber/test/fiber.rb
@@ -76,7 +76,7 @@ assert('Fiber iteration') do
end
assert('Fiber with splat in the block argument list') {
- Fiber.new{|*x|x}.resume(1) == [1]
+ assert_equal([1], Fiber.new{|*x|x}.resume(1))
}
assert('Fiber raises on resume when dead') do
diff --git a/mrbgems/mruby-hash-ext/mrblib/hash.rb b/mrbgems/mruby-hash-ext/mrblib/hash.rb
index 549bca0a8..547f3404a 100644
--- a/mrbgems/mruby-hash-ext/mrblib/hash.rb
+++ b/mrbgems/mruby-hash-ext/mrblib/hash.rb
@@ -27,9 +27,9 @@ class Hash
length = object.length
if length == 1
o = object[0]
- if o.respond_to?(:to_hash)
+ if Hash === o
h = self.new
- object[0].to_hash.each { |k, v| h[k] = v }
+ o.each { |k, v| h[k] = v }
return h
elsif o.respond_to?(:to_a)
h = self.new
@@ -62,25 +62,6 @@ class Hash
##
# 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
#
@@ -101,7 +82,7 @@ class Hash
#
def merge!(other, &block)
- raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash)
+ raise TypeError, "Hash required (#{other.class} given)" unless Hash === other
if block
other.each_key{|k|
self[k] = (self.has_key?(k))? block.call(k, self[k], other[k]): other[k]
@@ -116,6 +97,31 @@ class Hash
##
# call-seq:
+ # hsh.compact! -> hsh
+ #
+ # Removes all nil values from the hash. Returns the hash.
+ # Returns nil if the hash does not contain nil values.
+ #
+ # h = { a: 1, b: false, c: nil }
+ # h.compact! #=> { a: 1, b: false }
+ #
+
+ def compact!
+ keys = self.keys
+ nk = keys.select{|k|
+ self[k] != nil
+ }
+ return nil if (keys.size == nk.size)
+ h = {}
+ nk.each {|k|
+ h[k] = self[k]
+ }
+ h
+ self.replace(h)
+ end
+
+ ##
+ # call-seq:
# hsh.compact -> new_hsh
#
# Returns a new hash with the nil values/key pairs removed
@@ -125,9 +131,13 @@ class Hash
# h #=> { a: 1, b: false, c: nil }
#
def compact
- result = self.dup
- result.compact!
- result
+ h = {}
+ self.keys.select{|k|
+ self[k] != nil
+ }.each {|k|
+ h[k] = self[k]
+ }
+ h
end
##
@@ -300,11 +310,7 @@ class Hash
# h1 < h1 #=> false
#
def <(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
size < hash.size and all? {|key, val|
hash.key?(key) and hash[key] == val
}
@@ -324,11 +330,7 @@ class Hash
# h1 <= h1 #=> true
#
def <=(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
size <= hash.size and all? {|key, val|
hash.key?(key) and hash[key] == val
}
@@ -348,11 +350,7 @@ class Hash
# h1 > h1 #=> false
#
def >(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
size > hash.size and hash.all? {|key, val|
key?(key) and self[key] == val
}
@@ -372,11 +370,7 @@ class Hash
# h1 >= h1 #=> true
#
def >=(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
- raise TypeError, "can't convert #{hash.class} to Hash"
- end
+ raise TypeError, "can't convert #{hash.class} to Hash" unless Hash === hash
size >= hash.size and hash.all? {|key, val|
key?(key) and self[key] == val
}
@@ -432,9 +426,9 @@ class Hash
return to_enum :transform_keys! unless block
self.keys.each do |k|
value = self[k]
- new_key = block.call(k)
self.__delete(k)
- self[new_key] = value
+ k = block.call(k) if block
+ self[k] = value
end
self
end
@@ -457,6 +451,7 @@ class Hash
end
hash
end
+
##
# call-seq:
# hsh.transform_values! {|key| block } -> hsh
diff --git a/mrbgems/mruby-hash-ext/src/hash-ext.c b/mrbgems/mruby-hash-ext/src/hash-ext.c
index 6619f5268..e6112667b 100644
--- a/mrbgems/mruby-hash-ext/src/hash-ext.c
+++ b/mrbgems/mruby-hash-ext/src/hash-ext.c
@@ -37,42 +37,6 @@ hash_values_at(mrb_state *mrb, mrb_value hash)
}
/*
- * call-seq:
- * hsh.compact! -> hsh
- *
- * Removes all nil values from the hash. Returns the hash.
- *
- * h = { a: 1, b: false, c: nil }
- * h.compact! #=> { a: 1, b: false }
- */
-static mrb_value
-hash_compact_bang(mrb_state *mrb, mrb_value hash)
-{
- khiter_t k;
- khash_t(ht) *h = RHASH_TBL(hash);
- mrb_int n = -1;
-
- if (!h) return mrb_nil_value();
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- mrb_value val = kh_value(h, k).v;
- khiter_t k2;
-
- if (mrb_nil_p(val)) {
- kh_del(ht, mrb, h, k);
- n = kh_value(h, k).n;
- for (k2 = kh_begin(h); k2 != kh_end(h); k2++) {
- if (!kh_exist(h, k2)) continue;
- if (kh_value(h, k2).n > n) kh_value(h, k2).n--;
- }
- }
- }
- }
- if (n < 0) return mrb_nil_value();
- return hash;
-}
-
-/*
* call-seq:
* hsh.slice(*keys) -> a_hash
*
@@ -85,28 +49,22 @@ hash_compact_bang(mrb_state *mrb, mrb_value hash)
static mrb_value
hash_slice(mrb_state *mrb, mrb_value hash)
{
- khash_t(ht) *h = RHASH_TBL(hash);
mrb_value *argv, result;
mrb_int argc, i;
- khiter_t k;
- int ai;
mrb_get_args(mrb, "*", &argv, &argc);
- if (argc == 0 || h == NULL) {
+ if (argc == 0) {
return mrb_hash_new_capa(mrb, argc);
}
result = mrb_hash_new_capa(mrb, argc);
- ai = mrb_gc_arena_save(mrb);
for (i = 0; i < argc; i++) {
mrb_value key = argv[i];
+ mrb_value val;
- k = kh_get(ht, mrb, h, key);
- if (k != kh_end(h)) {
- mrb_value val = kh_value(h, k).v;
-
+ val = mrb_hash_fetch(mrb, hash, key, mrb_undef_value());
+ if (!mrb_undef_p(val)) {
mrb_hash_set(mrb, result, key, val);
}
- mrb_gc_arena_restore(mrb, ai);
}
return result;
}
@@ -118,7 +76,6 @@ mrb_mruby_hash_ext_gem_init(mrb_state *mrb)
h = mrb->hash_class;
mrb_define_method(mrb, h, "values_at", hash_values_at, MRB_ARGS_ANY());
- mrb_define_method(mrb, h, "compact!", hash_compact_bang, MRB_ARGS_NONE());
mrb_define_method(mrb, h, "slice", hash_slice, MRB_ARGS_ANY());
}
diff --git a/mrbgems/mruby-hash-ext/test/hash.rb b/mrbgems/mruby-hash-ext/test/hash.rb
index 269da800d..b5d0aaaf8 100644
--- a/mrbgems/mruby-hash-ext/test/hash.rb
+++ b/mrbgems/mruby-hash-ext/test/hash.rb
@@ -45,12 +45,6 @@ assert('Hash.[] for sub class') do
assert_equal(sub_hash_class, sub_hash.class)
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' }
diff --git a/mrbgems/mruby-inline-struct/test/inline.c b/mrbgems/mruby-inline-struct/test/inline.c
index 0baaab617..51804ae31 100644
--- a/mrbgems/mruby-inline-struct/test/inline.c
+++ b/mrbgems/mruby-inline-struct/test/inline.c
@@ -11,17 +11,19 @@ istruct_test_initialize(mrb_state *mrb, mrb_value self)
mrb_value object;
mrb_get_args(mrb, "o", &object);
- if (mrb_float_p(object))
- {
- snprintf(string, size, "float(%.3f)", mrb_float(object));
+ if (mrb_fixnum_p(object)) {
+ strncpy(string, "fixnum", size-1);
}
- else if (mrb_fixnum_p(object))
- {
- snprintf(string, size, "fixnum(%" MRB_PRId ")", mrb_fixnum(object));
+#ifndef MRB_WITHOUT_FLOAT
+ else if (mrb_float_p(object)) {
+ strncpy(string, "float", size-1);
}
- else if (mrb_string_p(object))
- {
- snprintf(string, size, "string(%s)", mrb_string_value_cstr(mrb, &object));
+#endif
+ else if (mrb_string_p(object)) {
+ strncpy(string, "string", size-1);
+ }
+ else {
+ strncpy(string, "anything", size-1);
}
string[size - 1] = 0; // force NULL at the end
@@ -47,7 +49,7 @@ istruct_test_test_receive(mrb_state *mrb, mrb_value self)
mrb_get_args(mrb, "o", &object);
if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest"))
{
- mrb_raisef(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
+ mrb_raise(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
}
return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's');
}
diff --git a/mrbgems/mruby-inline-struct/test/inline.rb b/mrbgems/mruby-inline-struct/test/inline.rb
index 495859232..f959a17c4 100644
--- a/mrbgems/mruby-inline-struct/test/inline.rb
+++ b/mrbgems/mruby-inline-struct/test/inline.rb
@@ -17,14 +17,14 @@ end
assert('InlineStructTest#dup') do
obj = InlineStructTest.new(1)
- assert_equal obj.to_s, 'fixnum(1)'
- assert_equal obj.dup.to_s, 'fixnum(1)'
+ assert_equal obj.to_s, 'fixnum'
+ assert_equal obj.dup.to_s, 'fixnum'
end
assert('InlineStructTest#clone') do
obj = InlineStructTest.new(1)
- assert_equal obj.to_s, 'fixnum(1)'
- assert_equal obj.clone.to_s, 'fixnum(1)'
+ assert_equal obj.to_s, 'fixnum'
+ assert_equal obj.clone.to_s, 'fixnum'
end
assert('InlineStruct#object_id') do
@@ -38,22 +38,22 @@ end
assert('InlineStructTest#mutate (dup)') do
obj1 = InlineStructTest.new("foo")
- assert_equal obj1.to_s, "string(foo)"
+ assert_equal obj1.to_s, "string"
obj2 = obj1.dup
- assert_equal obj2.to_s, "string(foo)"
+ assert_equal obj2.to_s, "string"
obj1.mutate
- assert_equal obj1.to_s, "mutate(foo)"
- assert_equal obj2.to_s, "string(foo)"
+ assert_equal obj1.to_s, "mutate"
+ assert_equal obj2.to_s, "string"
end
assert('InlineStructTest#mutate (clone)') do
obj1 = InlineStructTest.new("foo")
- assert_equal obj1.to_s, "string(foo)"
+ assert_equal obj1.to_s, "string"
obj2 = obj1.clone
- assert_equal obj2.to_s, "string(foo)"
+ assert_equal obj2.to_s, "string"
obj1.mutate
- assert_equal obj1.to_s, "mutate(foo)"
- assert_equal obj2.to_s, "string(foo)"
+ assert_equal obj1.to_s, "mutate"
+ assert_equal obj2.to_s, "string"
end
assert('InlineStructTest#test_receive(string)') do
@@ -101,26 +101,6 @@ if InlineStructTest.length == 24
assert('InlineStructTest length [64 bit]') do
assert_equal InlineStructTest.length, 3 * 8
end
-
- assert('InlineStructTest w/float [64 bit]') do
- obj = InlineStructTest.new(1.25)
- assert_equal obj.to_s, "float(1.250)"
- end
-
- assert('InlineStructTest w/fixnum [64 bit]') do
- obj = InlineStructTest.new(42)
- assert_equal obj.to_s, "fixnum(42)"
- end
-
- assert('InlineStructTest w/string [64 bit]') do
- obj = InlineStructTest.new("hello")
- assert_equal obj.to_s, "string(hello)"
- end
-
- assert('InlineStructTest w/long string [64 bit]') do
- obj = InlineStructTest.new("this won't fit in 3 * 8 bytes available for the structure")
- assert_equal obj.to_s, "string(this won't fit i"
- end
end
# 32-bit mode
@@ -128,24 +108,11 @@ if InlineStructTest.length == 12
assert('InlineStructTest length [32 bit]') do
assert_equal InlineStructTest.length, 3 * 4
end
+end
- assert('InlineStructTest w/float [32 bit]') do
- obj = InlineStructTest.new(1.25)
- assert_equal obj.to_s, "float(1.250"
- end
-
- assert('InlineStructTest w/fixnum [32 bit]') do
- obj = InlineStructTest.new(42)
- assert_equal obj.to_s, "fixnum(42)"
- end
-
- assert('InlineStructTest w/string [32 bit]') do
- obj = InlineStructTest.new("hello")
- assert_equal obj.to_s, "string(hell"
- end
-
- assert('InlineStructTest w/long string [32 bit]') do
- obj = InlineStructTest.new("this won't fit in 3 * 4 bytes available for the structure")
- assert_equal obj.to_s, "string(this"
+# 16-bit mode
+if InlineStructTest.length == 6
+ assert('InlineStructTest length [16 bit]') do
+ assert_equal InlineStructTest.length, 3 * 2
end
end
diff --git a/mrbgems/mruby-io/mrbgem.rake b/mrbgems/mruby-io/mrbgem.rake
index 50fa49678..d79964590 100644
--- a/mrbgems/mruby-io/mrbgem.rake
+++ b/mrbgems/mruby-io/mrbgem.rake
@@ -4,7 +4,7 @@ MRuby::Gem::Specification.new('mruby-io') do |spec|
spec.summary = 'IO and File class'
spec.cc.include_paths << "#{build.root}/src"
-
+
case RUBY_PLATFORM
when /mingw|mswin/
spec.linker.libraries += ['Ws2_32']
@@ -14,4 +14,5 @@ MRuby::Gem::Specification.new('mruby-io') do |spec|
if build.kind_of?(MRuby::CrossBuild) && %w(x86_64-w64-mingw32 i686-w64-mingw32).include?(build.host_target)
spec.linker.libraries += ['ws2_32']
end
+ spec.add_test_dependency 'mruby-time', core: 'mruby-time'
end
diff --git a/mrbgems/mruby-io/src/file.c b/mrbgems/mruby-io/src/file.c
index 3dcfe3a0b..243f634b4 100644
--- a/mrbgems/mruby-io/src/file.c
+++ b/mrbgems/mruby-io/src/file.c
@@ -114,11 +114,13 @@ mrb_file_s_unlink(mrb_state *mrb, mrb_value obj)
mrb_get_args(mrb, "*", &argv, &argc);
for (i = 0; i < argc; i++) {
- pathv = mrb_convert_type(mrb, argv[i], MRB_TT_STRING, "String", "to_str");
- path = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &pathv), -1);
+ const char *utf8_path;
+ pathv = mrb_ensure_string_type(mrb, argv[i]);
+ utf8_path = mrb_string_value_cstr(mrb, &pathv);
+ path = mrb_locale_from_utf8(utf8_path, -1);
if (UNLINK(path) < 0) {
mrb_locale_free(path);
- mrb_sys_fail(mrb, path);
+ mrb_sys_fail(mrb, utf8_path);
}
mrb_locale_free(path);
}
@@ -144,7 +146,7 @@ mrb_file_s_rename(mrb_state *mrb, mrb_value obj)
#endif
mrb_locale_free(src);
mrb_locale_free(dst);
- mrb_sys_fail(mrb, mrb_str_to_cstr(mrb, mrb_format(mrb, "(%S, %S)", from, to)));
+ mrb_sys_fail(mrb, RSTRING_PTR(mrb_format(mrb, "(%S, %S)", from, to)));
}
mrb_locale_free(src);
mrb_locale_free(dst);
@@ -157,12 +159,12 @@ mrb_file_dirname(mrb_state *mrb, mrb_value klass)
#if defined(_WIN32) || defined(_WIN64)
char dname[_MAX_DIR], vname[_MAX_DRIVE];
char buffer[_MAX_DRIVE + _MAX_DIR];
+ const char *utf8_path;
char *path;
size_t ridx;
- mrb_value s;
- mrb_get_args(mrb, "S", &s);
- path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, s), -1);
- _splitpath((const char*)path, vname, dname, NULL, NULL);
+ mrb_get_args(mrb, "z", &utf8_path);
+ path = mrb_locale_from_utf8(utf8_path, -1);
+ _splitpath(path, vname, dname, NULL, NULL);
snprintf(buffer, _MAX_DRIVE + _MAX_DIR, "%s%s", vname, dname);
mrb_locale_free(path);
ridx = strlen(buffer);
@@ -246,7 +248,7 @@ mrb_file_realpath(mrb_state *mrb, mrb_value klass)
s = mrb_str_append(mrb, s, pathname);
pathname = s;
}
- cpath = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, pathname), -1);
+ cpath = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &pathname), -1);
result = mrb_str_buf_new(mrb, PATH_MAX);
if (realpath(cpath, RSTRING_PTR(result)) == NULL) {
mrb_locale_free(cpath);
@@ -298,7 +300,7 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
mrb_raise(mrb, E_ARGUMENT_ERROR, "non-absolute home");
}
} else {
- const char *cuser = mrb_str_to_cstr(mrb, username);
+ const char *cuser = mrb_string_value_cstr(mrb, &username);
struct passwd *pwd = getpwnam(cuser);
if (pwd == NULL) {
return mrb_nil_value();
@@ -310,7 +312,7 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
}
home = mrb_locale_from_utf8(home, -1);
path = mrb_str_new_cstr(mrb, home);
- mrb_utf8_free(home);
+ mrb_locale_free(home);
return path;
#else
argc = mrb_get_argc(mrb);
@@ -327,7 +329,7 @@ mrb_file__gethome(mrb_state *mrb, mrb_value klass)
}
home = mrb_locale_from_utf8(home, -1);
path = mrb_str_new_cstr(mrb, home);
- mrb_utf8_free(home);
+ mrb_locale_free(home);
return path;
#endif
}
@@ -343,7 +345,7 @@ mrb_file_mtime(mrb_state *mrb, mrb_value self)
fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
if (fstat(fd, &st) == -1)
return mrb_false_value();
- return mrb_funcall(mrb, obj, "at", 1, mrb_float_value(mrb, st.st_mtime));
+ return mrb_funcall(mrb, obj, "at", 1, mrb_fixnum_value(st.st_mtime));
}
mrb_value
@@ -391,9 +393,8 @@ mrb_file_s_symlink(mrb_state *mrb, mrb_value klass)
int ai = mrb_gc_arena_save(mrb);
mrb_get_args(mrb, "SS", &from, &to);
- src = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, from), -1);
- dst = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, to), -1);
-
+ src = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &from), -1);
+ dst = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &to), -1);
if (symlink(src, dst) == -1) {
mrb_locale_free(src);
mrb_locale_free(dst);
@@ -415,15 +416,16 @@ mrb_file_s_chmod(mrb_state *mrb, mrb_value klass) {
mrb_get_args(mrb, "i*", &mode, &filenames, &argc);
for (i = 0; i < argc; i++) {
- char *path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, filenames[i]), -1);
+ const char *utf8_path = mrb_string_value_cstr(mrb, &filenames[i]);
+ char *path = mrb_locale_from_utf8(utf8_path, -1);
if (CHMOD(path, mode) == -1) {
mrb_locale_free(path);
- mrb_sys_fail(mrb, path);
+ mrb_sys_fail(mrb, utf8_path);
}
mrb_locale_free(path);
+ mrb_gc_arena_restore(mrb, ai);
}
- mrb_gc_arena_restore(mrb, ai);
return mrb_fixnum_value(argc);
}
diff --git a/mrbgems/mruby-io/src/file_test.c b/mrbgems/mruby-io/src/file_test.c
index e429b06b3..85a221ff9 100644
--- a/mrbgems/mruby-io/src/file_test.c
+++ b/mrbgems/mruby-io/src/file_test.c
@@ -1,5 +1,5 @@
/*
-** file.c - File class
+** file_test.c - FileTest class
*/
#include "mruby.h"
@@ -42,16 +42,9 @@ extern struct mrb_data_type mrb_io_type;
static int
mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
{
- mrb_value tmp;
- mrb_value io_klass, str_klass;
-
- io_klass = mrb_obj_value(mrb_class_get(mrb, "IO"));
- str_klass = mrb_obj_value(mrb_class_get(mrb, "String"));
-
- tmp = mrb_funcall(mrb, obj, "is_a?", 1, io_klass);
- if (mrb_test(tmp)) {
+ if (mrb_obj_is_kind_of(mrb, obj, mrb_class_get(mrb, "IO"))) {
struct mrb_io *fptr;
- fptr = (struct mrb_io *)mrb_get_datatype(mrb, obj, &mrb_io_type);
+ fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, obj, &mrb_io_type);
if (fptr && fptr->fd >= 0) {
return fstat(fptr->fd, st);
@@ -60,10 +53,8 @@ mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
mrb_raise(mrb, E_IO_ERROR, "closed stream");
return -1;
}
-
- tmp = mrb_funcall(mrb, obj, "is_a?", 1, str_klass);
- if (mrb_test(tmp)) {
- char *path = mrb_locale_from_utf8(mrb_str_to_cstr(mrb, obj), -1);
+ else {
+ char *path = mrb_locale_from_utf8(mrb_string_value_cstr(mrb, &obj), -1);
int ret;
if (do_lstat) {
ret = LSTAT(path, st);
@@ -73,8 +64,6 @@ mrb_stat0(mrb_state *mrb, mrb_value obj, struct stat *st, int do_lstat)
mrb_locale_free(path);
return ret;
}
-
- return -1;
}
static int
diff --git a/mrbgems/mruby-io/src/io.c b/mrbgems/mruby-io/src/io.c
index 38b0b06c2..99441f16b 100644
--- a/mrbgems/mruby-io/src/io.c
+++ b/mrbgems/mruby-io/src/io.c
@@ -79,7 +79,7 @@ io_get_open_fptr(mrb_state *mrb, mrb_value self)
{
struct mrb_io *fptr;
- fptr = (struct mrb_io *)mrb_get_datatype(mrb, self, &mrb_io_type);
+ fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, self, &mrb_io_type);
if (fptr == NULL) {
mrb_raise(mrb, E_IO_ERROR, "uninitialized stream.");
}
@@ -209,7 +209,7 @@ mrb_fd_cloexec(mrb_state *mrb, int fd)
#endif
}
-#ifndef _WIN32
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
static int
mrb_cloexec_pipe(mrb_state *mrb, int fildes[2])
{
@@ -305,7 +305,112 @@ option_to_fd(mrb_state *mrb, mrb_value obj, const char *key)
return -1; /* never reached */
}
-#ifndef _WIN32
+#ifdef _WIN32
+mrb_value
+mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
+{
+ mrb_value cmd, io;
+ mrb_value mode = mrb_str_new_cstr(mrb, "r");
+ mrb_value opt = mrb_hash_new(mrb);
+
+ struct mrb_io *fptr;
+ const char *pname;
+ int pid = 0, flags;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ SECURITY_ATTRIBUTES saAttr;
+
+ HANDLE ifd[2];
+ HANDLE ofd[2];
+
+ int doexec;
+ int opt_in, opt_out, opt_err;
+
+ ifd[0] = INVALID_HANDLE_VALUE;
+ ifd[1] = INVALID_HANDLE_VALUE;
+ ofd[0] = INVALID_HANDLE_VALUE;
+ ofd[1] = INVALID_HANDLE_VALUE;
+
+ mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
+ io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
+
+ pname = mrb_string_value_cstr(mrb, &cmd);
+ flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
+
+ doexec = (strcmp("-", pname) != 0);
+ opt_in = option_to_fd(mrb, opt, "in");
+ opt_out = option_to_fd(mrb, opt, "out");
+ opt_err = option_to_fd(mrb, opt, "err");
+
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ if (flags & FMODE_READABLE) {
+ if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
+ || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) {
+ mrb_sys_fail(mrb, "pipe");
+ }
+ }
+
+ if (flags & FMODE_WRITABLE) {
+ if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
+ || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
+ mrb_sys_fail(mrb, "pipe");
+ }
+ }
+
+ if (doexec) {
+ ZeroMemory(&pi, sizeof(pi));
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags |= STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_HIDE;
+ si.dwFlags |= STARTF_USESTDHANDLES;
+ if (flags & FMODE_READABLE) {
+ si.hStdOutput = ofd[1];
+ si.hStdError = ofd[1];
+ }
+ if (flags & FMODE_WRITABLE) {
+ si.hStdInput = ifd[0];
+ }
+ if (!CreateProcess(
+ NULL, (char*)pname, NULL, NULL,
+ TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) {
+ CloseHandle(ifd[0]);
+ CloseHandle(ifd[1]);
+ CloseHandle(ofd[0]);
+ CloseHandle(ofd[1]);
+ mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
+ }
+ CloseHandle(pi.hThread);
+ CloseHandle(ifd[0]);
+ CloseHandle(ofd[1]);
+ pid = pi.dwProcessId;
+ }
+
+ mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
+
+ fptr = mrb_io_alloc(mrb);
+ fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0);
+ fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0);
+ fptr->pid = pid;
+ fptr->readable = ((flags & FMODE_READABLE) != 0);
+ fptr->writable = ((flags & FMODE_WRITABLE) != 0);
+ fptr->sync = 0;
+
+ DATA_TYPE(io) = &mrb_io_type;
+ DATA_PTR(io) = fptr;
+ return io;
+}
+#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+mrb_value
+mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
+{
+ mrb_raise(mrb, E_NOTIMP_ERROR, "IO#popen is not supported on the platform");
+ return mrb_false_value();
+}
+#else
mrb_value
mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
{
@@ -440,104 +545,6 @@ mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
}
return result;
}
-#else
-mrb_value
-mrb_io_s_popen(mrb_state *mrb, mrb_value klass)
-{
- mrb_value cmd, io;
- mrb_value mode = mrb_str_new_cstr(mrb, "r");
- mrb_value opt = mrb_hash_new(mrb);
-
- struct mrb_io *fptr;
- const char *pname;
- int pid = 0, flags;
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- SECURITY_ATTRIBUTES saAttr;
-
- HANDLE ifd[2];
- HANDLE ofd[2];
-
- int doexec;
- int opt_in, opt_out, opt_err;
-
- ifd[0] = INVALID_HANDLE_VALUE;
- ifd[1] = INVALID_HANDLE_VALUE;
- ofd[0] = INVALID_HANDLE_VALUE;
- ofd[1] = INVALID_HANDLE_VALUE;
-
- mrb_get_args(mrb, "S|SH", &cmd, &mode, &opt);
- io = mrb_obj_value(mrb_data_object_alloc(mrb, mrb_class_ptr(klass), NULL, &mrb_io_type));
-
- pname = mrb_string_value_cstr(mrb, &cmd);
- flags = mrb_io_modestr_to_flags(mrb, mrb_string_value_cstr(mrb, &mode));
-
- doexec = (strcmp("-", pname) != 0);
- opt_in = option_to_fd(mrb, opt, "in");
- opt_out = option_to_fd(mrb, opt, "out");
- opt_err = option_to_fd(mrb, opt, "err");
-
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = TRUE;
- saAttr.lpSecurityDescriptor = NULL;
-
- if (flags & FMODE_READABLE) {
- if (!CreatePipe(&ofd[0], &ofd[1], &saAttr, 0)
- || !SetHandleInformation(ofd[0], HANDLE_FLAG_INHERIT, 0)) {
- mrb_sys_fail(mrb, "pipe");
- }
- }
-
- if (flags & FMODE_WRITABLE) {
- if (!CreatePipe(&ifd[0], &ifd[1], &saAttr, 0)
- || !SetHandleInformation(ifd[1], HANDLE_FLAG_INHERIT, 0)) {
- mrb_sys_fail(mrb, "pipe");
- }
- }
-
- if (doexec) {
- ZeroMemory(&pi, sizeof(pi));
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
- si.dwFlags |= STARTF_USESHOWWINDOW;
- si.wShowWindow = SW_HIDE;
- si.dwFlags |= STARTF_USESTDHANDLES;
- if (flags & FMODE_READABLE) {
- si.hStdOutput = ofd[1];
- si.hStdError = ofd[1];
- }
- if (flags & FMODE_WRITABLE) {
- si.hStdInput = ifd[0];
- }
- if (!CreateProcess(
- NULL, (char*)pname, NULL, NULL,
- TRUE, CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) {
- CloseHandle(ifd[0]);
- CloseHandle(ifd[1]);
- CloseHandle(ofd[0]);
- CloseHandle(ofd[1]);
- mrb_raisef(mrb, E_IO_ERROR, "command not found: %S", cmd);
- }
- CloseHandle(pi.hThread);
- CloseHandle(ifd[0]);
- CloseHandle(ofd[1]);
- pid = pi.dwProcessId;
- }
-
- mrb_iv_set(mrb, io, mrb_intern_cstr(mrb, "@buf"), mrb_str_new_cstr(mrb, ""));
-
- fptr = mrb_io_alloc(mrb);
- fptr->fd = _open_osfhandle((intptr_t)ofd[0], 0);
- fptr->fd2 = _open_osfhandle((intptr_t)ifd[1], 0);
- fptr->pid = pid;
- fptr->readable = ((flags & FMODE_READABLE) != 0);
- fptr->writable = ((flags & FMODE_WRITABLE) != 0);
- fptr->sync = 0;
-
- DATA_TYPE(io) = &mrb_io_type;
- DATA_PTR(io) = fptr;
- return io;
-}
#endif
static int
@@ -780,7 +787,7 @@ reopen:
mrb_str_modify(mrb, mrb_str_ptr(emsg));
mrb_sys_fail(mrb, RSTRING_PTR(emsg));
}
- mrb_utf8_free(fname);
+ mrb_locale_free(fname);
if (fd <= 2) {
mrb_fd_cloexec(mrb, fd);
@@ -948,7 +955,7 @@ mrb_value
mrb_io_closed(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
- fptr = (struct mrb_io *)mrb_get_datatype(mrb, io, &mrb_io_type);
+ fptr = (struct mrb_io *)mrb_data_get_ptr(mrb, io, &mrb_io_type);
if (fptr == NULL || fptr->fd >= 0) {
return mrb_false_value();
}
@@ -1004,7 +1011,7 @@ mrb_io_read_data_pending(mrb_state *mrb, mrb_value io)
return 0;
}
-#ifndef _WIN32
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
static mrb_value
mrb_io_s_pipe(mrb_state *mrb, mrb_value klass)
{
@@ -1306,7 +1313,7 @@ mrb_init_io(mrb_state *mrb)
mrb_define_class_method(mrb, io, "for_fd", mrb_io_s_for_fd, MRB_ARGS_ANY());
mrb_define_class_method(mrb, io, "select", mrb_io_s_select, MRB_ARGS_ANY());
mrb_define_class_method(mrb, io, "sysopen", mrb_io_s_sysopen, MRB_ARGS_ANY());
-#ifndef _WIN32
+#if !defined(_WIN32) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
mrb_define_class_method(mrb, io, "_pipe", mrb_io_s_pipe, MRB_ARGS_NONE());
#endif
diff --git a/mrbgems/mruby-io/test/file.rb b/mrbgems/mruby-io/test/file.rb
index dc6fe369a..1535ebb44 100644
--- a/mrbgems/mruby-io/test/file.rb
+++ b/mrbgems/mruby-io/test/file.rb
@@ -1,16 +1,14 @@
##
-# IO Test
+# File Test
-assert('File', '15.2.21') do
- File.class == Class
-end
+MRubyIOTestUtil.io_test_setup
-assert('File', '15.2.21.2') do
- File.superclass == IO
+assert('File.class', '15.2.21') do
+ assert_equal Class, File.class
end
-assert('File TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
+assert('File.superclass', '15.2.21.2') do
+ assert_equal IO, File.superclass
end
assert('File#initialize', '15.2.21.4.1') do
@@ -27,7 +25,7 @@ assert('File#path', '15.2.21.4.2') do
assert_equal $mrbtest_io_rfname, io.path
io.close
assert_equal $mrbtest_io_rfname, io.path
- io.closed?
+ assert_true io.closed?
end
assert('File.basename') do
@@ -35,6 +33,7 @@ assert('File.basename') do
assert_equal 'a', File.basename('/a/')
assert_equal 'b', File.basename('/a/b')
assert_equal 'b', File.basename('../a/b')
+ assert_raise(ArgumentError) { File.basename("/a/b\0") }
end
assert('File.dirname') do
@@ -69,18 +68,15 @@ assert('File#flock') do
end
assert('File#mtime') do
- unless Object.const_defined?(:Time)
- skip "File#mtime require Time"
- end
begin
- now = Time.now.to_i
- mt = 0
- File.open('mtime-test', 'w') do |f|
- mt = f.mtime.to_i
+ File.open("#{$mrbtest_io_wfname}.mtime", 'w') do |f|
+ assert_equal Time, f.mtime.class
+ File.open("#{$mrbtest_io_wfname}.mtime", 'r') do |f2|
+ assert_equal true, f.mtime == f2.mtime
+ end
end
- assert_equal true, mt >= now
ensure
- File.delete('mtime-test')
+ File.delete("#{$mrbtest_io_wfname}.mtime")
end
end
@@ -111,6 +107,8 @@ assert('File.realpath') do
MRubyIOTestUtil.rmdir dir
end
end
+
+ assert_raise(ArgumentError) { File.realpath("TO\0DO") }
end
assert("File.readlink") do
@@ -177,7 +175,6 @@ assert('File.path') do
assert_equal "a/../b/./c", File.path("a/../b/./c")
assert_raise(TypeError) { File.path(nil) }
assert_raise(TypeError) { File.path(123) }
-
end
assert('File.symlink') do
@@ -200,14 +197,12 @@ assert('File.symlink') do
end
assert('File.chmod') do
- File.open('chmod-test', 'w') {}
+ File.open("#{$mrbtest_io_wfname}.chmod-test", 'w') {}
begin
- assert_equal 1, File.chmod(0400, 'chmod-test')
+ assert_equal 1, File.chmod(0400, "#{$mrbtest_io_wfname}.chmod-test")
ensure
- File.delete('chmod-test')
+ File.delete("#{$mrbtest_io_wfname}.chmod-test")
end
end
-assert('File TEST CLEANUP') do
- assert_nil MRubyIOTestUtil.io_test_cleanup
-end
+MRubyIOTestUtil.io_test_cleanup
diff --git a/mrbgems/mruby-io/test/file_test.rb b/mrbgems/mruby-io/test/file_test.rb
index 2c831f0d5..2134c6a75 100644
--- a/mrbgems/mruby-io/test/file_test.rb
+++ b/mrbgems/mruby-io/test/file_test.rb
@@ -1,9 +1,7 @@
##
# FileTest
-assert('FileTest TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
-end
+MRubyIOTestUtil.io_test_setup
assert("FileTest.directory?") do
dir = MRubyIOTestUtil.mkdtemp("mruby-io-test.XXXXXX")
@@ -22,9 +20,8 @@ assert("FileTest.exist?") do
assert_equal true, FileTest.exist?(io), "io obj - exist"
io.close
assert_equal true, io.closed?
- assert_raise IOError do
- FileTest.exist?(io)
- end
+ assert_raise(IOError) { FileTest.exist?(io) }
+ assert_raise(TypeError) { File.exist?($mrbtest_io_rfname.to_sym) }
end
assert("FileTest.file?") do
@@ -67,11 +64,11 @@ assert("FileTest.size?") do
assert_raise IOError do
FileTest.size?(fp1)
end
+ assert_true fp1.closed?
assert_raise IOError do
FileTest.size?(fp2)
end
-
- fp1.closed? && fp2.closed?
+ assert_true fp2.closed?
end
assert("FileTest.socket?") do
@@ -105,13 +102,11 @@ assert("FileTest.zero?") do
assert_raise IOError do
FileTest.zero?(fp1)
end
+ assert_true fp1.closed?
assert_raise IOError do
FileTest.zero?(fp2)
end
-
- fp1.closed? && fp2.closed?
+ assert_true fp2.closed?
end
-assert('FileTest TEST CLEANUP') do
- assert_nil MRubyIOTestUtil.io_test_cleanup
-end
+MRubyIOTestUtil.io_test_cleanup
diff --git a/mrbgems/mruby-io/test/gc_filedes.sh b/mrbgems/mruby-io/test/gc_filedes.sh
deleted file mode 100644
index 6e5d1bbf1..000000000
--- a/mrbgems/mruby-io/test/gc_filedes.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-ulimit -n 20
-mruby -e '100.times { File.open "'$0'" }'
diff --git a/mrbgems/mruby-io/test/io.rb b/mrbgems/mruby-io/test/io.rb
index e06b14996..1491a4cfe 100644
--- a/mrbgems/mruby-io/test/io.rb
+++ b/mrbgems/mruby-io/test/io.rb
@@ -1,57 +1,46 @@
##
# IO Test
-unless Object.respond_to? :assert_nothing_raised
- def assert_nothing_raised(*exp)
- ret = true
- if $mrbtest_assert
- $mrbtest_assert_idx += 1
- msg = exp.last.class == String ? exp.pop : ""
- begin
- yield
- rescue Exception => e
- msg = "#{msg} exception raised."
- diff = " Class: <#{e.class}>\n" +
- " Message: #{e.message}"
- $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
- ret = false
+MRubyIOTestUtil.io_test_setup
+$cr, $crlf, $cmd = MRubyIOTestUtil.win? ? [1, "\r\n", "cmd /c "] : [0, "\n", ""]
+
+def assert_io_open(meth)
+ assert do
+ fd = IO.sysopen($mrbtest_io_rfname)
+ assert_equal Fixnum, fd.class
+ io1 = IO.__send__(meth, fd)
+ begin
+ assert_equal IO, io1.class
+ assert_equal $mrbtest_io_msg, io1.read
+ ensure
+ io1.close
+ end
+
+ io2 = IO.__send__(meth, IO.sysopen($mrbtest_io_rfname))do |io|
+ if meth == :open
+ assert_equal $mrbtest_io_msg, io.read
+ else
+ flunk "IO.#{meth} does not take block"
end
end
- ret
+ io2.close unless meth == :open
end
end
-assert('IO TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
- $cr = MRubyIOTestUtil.win? ? 1 : 0 # "\n" include CR or not
-end
-
-assert('IO', '15.2.20') do
+assert('IO.class', '15.2.20') do
assert_equal(Class, IO.class)
end
-assert('IO', '15.2.20.2') do
+assert('IO.superclass', '15.2.20.2') do
assert_equal(Object, IO.superclass)
end
-assert('IO', '15.2.20.3') do
- assert_include(IO.included_modules, Enumerable)
+assert('IO.ancestors', '15.2.20.3') do
+ assert_include(IO.ancestors, Enumerable)
end
assert('IO.open', '15.2.20.4.1') do
- fd = IO.sysopen $mrbtest_io_rfname
- assert_equal Fixnum, fd.class
- io = IO.open fd
- assert_equal IO, io.class
- assert_equal $mrbtest_io_msg, io.read
- io.close
-
- fd = IO.sysopen $mrbtest_io_rfname
- IO.open(fd) do |io|
- assert_equal $mrbtest_io_msg, io.read
- end
-
- true
+ assert_io_open(:open)
end
assert('IO#close', '15.2.20.5.1') do
@@ -92,8 +81,6 @@ assert('IO#eof?', '15.2.20.5.6') do
io.read
assert_true io.eof?
io.close
-
- true
end
assert('IO#flush', '15.2.20.5.7') do
@@ -108,12 +95,11 @@ end
assert('IO#getc', '15.2.20.5.8') do
io = IO.new(IO.sysopen($mrbtest_io_rfname))
- $mrbtest_io_msg.each_char { |ch|
+ $mrbtest_io_msg.split("").each { |ch|
assert_equal ch, io.getc
}
assert_equal nil, io.getc
io.close
- true
end
#assert('IO#gets', '15.2.20.5.9') do
@@ -152,7 +138,7 @@ end
assert('IO#readchar', '15.2.20.5.15') do
# almost same as IO#getc
IO.open(IO.sysopen($mrbtest_io_rfname)) do |io|
- $mrbtest_io_msg.each_char { |ch|
+ $mrbtest_io_msg.split("").each { |ch|
assert_equal ch, io.readchar
}
assert_raise(EOFError) do
@@ -199,8 +185,6 @@ assert('IO#write', '15.2.20.5.20') do
io.rewind
assert_equal "ab123fg", io.read
io.close
-
- true
end
assert('IO#<<') do
@@ -208,7 +192,6 @@ assert('IO#<<') do
io << "" << ""
assert_equal 0, io.pos
io.close
- true
end
assert('IO#dup for readable') do
@@ -228,7 +211,6 @@ assert('IO#dup for readable') do
dup.close
assert_false io.closed?
io.close
- true
end
assert('IO#dup for writable') do
@@ -241,25 +223,18 @@ assert('IO#dup for writable') do
assert_equal "mruby", dup.sysread(5)
dup.close
io.close
- true
end
assert('IO.for_fd') do
- fd = IO.sysopen($mrbtest_io_rfname)
- io = IO.for_fd(fd)
- assert_equal $mrbtest_io_msg, io.read
- io.close
- true
+ assert_io_open(:for_fd)
end
assert('IO.new') do
- io = IO.new(0)
- io.close
- true
+ assert_io_open(:new)
end
assert('IO gc check') do
- 100.times { IO.new(0) }
+ assert_nothing_raised { 100.times { IO.new(0) } }
end
assert('IO.sysopen("./nonexistent")') do
@@ -300,7 +275,6 @@ assert('IO.sysopen, IO#sysread') do
io = IO.new fd, "w"
assert_raise(IOError) { io.sysread(1) }
io.close
- true
end
assert('IO.sysopen, IO#syswrite') do
@@ -314,8 +288,6 @@ assert('IO.sysopen, IO#syswrite') do
io = IO.new(IO.sysopen($mrbtest_io_rfname), "r")
assert_raise(IOError) { io.syswrite("a") }
io.close
-
- true
end
assert('IO#_read_buf') do
@@ -339,20 +311,25 @@ assert('IO#_read_buf') do
assert_equal true, io.eof
assert_equal true, io.eof?
io.close
- io.closed?
end
assert('IO#isatty') do
skip "isatty is not supported on this platform" if MRubyIOTestUtil.win?
- f1 = File.open("/dev/tty")
- f2 = File.open($mrbtest_io_rfname)
-
- assert_true f1.isatty
- assert_false f2.isatty
-
- f1.close
- f2.close
- true
+ begin
+ f = File.open("/dev/tty")
+ rescue RuntimeError => e
+ skip e.message
+ else
+ assert_true f.isatty
+ ensure
+ f&.close
+ end
+ begin
+ f = File.open($mrbtest_io_rfname)
+ assert_false f.isatty
+ ensure
+ f&.close
+ end
end
assert('IO#pos=, IO#seek') do
@@ -366,7 +343,6 @@ assert('IO#pos=, IO#seek') do
assert_equal 0, io.seek(0)
assert_equal 0, io.pos
io.close
- io.closed?
end
assert('IO#rewind') do
@@ -377,7 +353,6 @@ assert('IO#rewind') do
assert_equal 0, io.rewind
assert_equal 0, io.pos
io.close
- io.closed?
end
assert('IO#gets') do
@@ -426,7 +401,6 @@ assert('IO#gets') do
assert_equal nil, io.gets, "gets third line; returns nil"
io.close
- io.closed?
end
assert('IO#gets - paragraph mode') do
@@ -437,7 +411,6 @@ assert('IO#gets - paragraph mode') do
io.write "2" * 10 + "\n"
assert_equal 34 + $cr * 4, io.pos
io.close
- assert_equal true, io.closed?
fd = IO.sysopen $mrbtest_io_wfname
io = IO.new fd
@@ -448,13 +421,12 @@ assert('IO#gets - paragraph mode') do
text2 = io.gets("")
assert_equal para2, text2
io.close
- io.closed?
end
assert('IO.popen') do
begin
$? = nil
- io = IO.popen("echo mruby-io")
+ io = IO.popen("#{$cmd}echo mruby-io")
assert_true io.close_on_exec?
assert_equal Fixnum, io.pid.class
@@ -542,7 +514,6 @@ assert('IO#fileno') do
assert_equal io.fileno, fd
assert_equal io.to_i, fd
io.close
- io.closed?
end
assert('IO#close_on_exec') do
@@ -564,7 +535,6 @@ assert('IO#close_on_exec') do
assert_equal(false, io.close_on_exec?)
io.close
- io.closed?
begin
r, w = IO.pipe
@@ -635,12 +605,10 @@ end
assert('`cmd`') do
begin
- assert_equal `echo foo`, "foo\n"
+ assert_equal `#{$cmd}echo foo`, "foo#{$crlf}"
rescue NotImplementedError => e
skip e.message
end
end
-assert('IO TEST CLEANUP') do
- assert_nil MRubyIOTestUtil.io_test_cleanup
-end
+MRubyIOTestUtil.io_test_cleanup
diff --git a/mrbgems/mruby-io/test/mruby_io_test.c b/mrbgems/mruby-io/test/mruby_io_test.c
index 71239a827..3312d6c7e 100644
--- a/mrbgems/mruby-io/test/mruby_io_test.c
+++ b/mrbgems/mruby-io/test/mruby_io_test.c
@@ -215,11 +215,9 @@ mrb_io_test_mkdtemp(mrb_state *mrb, mrb_value klass)
static mrb_value
mrb_io_test_rmdir(mrb_state *mrb, mrb_value klass)
{
- mrb_value str;
- char *cp;
+ const char *cp;
- mrb_get_args(mrb, "S", &str);
- cp = mrb_str_to_cstr(mrb, str);
+ mrb_get_args(mrb, "z", &cp);
if (rmdir(cp) == -1) {
mrb_sys_fail(mrb, "rmdir");
}
diff --git a/mrbgems/mruby-kernel-ext/mrbgem.rake b/mrbgems/mruby-kernel-ext/mrbgem.rake
index fcb3a83b0..e52c63a55 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 = 'Kernel module extension'
+ spec.summary = 'extensional function-like methods'
end
diff --git a/mrbgems/mruby-kernel-ext/mrblib/kernel.rb b/mrbgems/mruby-kernel-ext/mrblib/kernel.rb
deleted file mode 100644
index bf739ed1a..000000000
--- a/mrbgems/mruby-kernel-ext/mrblib/kernel.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Kernel
- # call-seq:
- # obj.yield_self {|_obj|...} -> an_object
- # obj.then {|_obj|...} -> an_object
- #
- # Yields <i>obj</i> and returns the result.
- #
- # 'my string'.yield_self {|s|s.upcase} #=> "MY STRING"
- #
- def yield_self(&block)
- return to_enum :yield_self unless block
- block.call(self)
- end
- alias then yield_self
-end
diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c
index 32d86376a..8e7e03c56 100644
--- a/mrbgems/mruby-kernel-ext/src/kernel.c
+++ b/mrbgems/mruby-kernel-ext/src/kernel.c
@@ -22,7 +22,7 @@ mrb_f_caller(mrb_state *mrb, mrb_value self)
case 1:
if (mrb_type(v) == MRB_TT_RANGE) {
mrb_int beg, len;
- if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == 1) {
+ if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == MRB_RANGE_OK) {
lev = beg;
n = len;
}
@@ -93,9 +93,8 @@ mrb_f_method(mrb_state *mrb, mrb_value self)
* (<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.
+ * <code>String#to_i</code>. Non string values will be treated as integers.
+ * Passing <code>nil</code> raises a TypeError.
*
* Integer(123.999) #=> 123
* Integer("0x1a") #=> 26
@@ -142,8 +141,7 @@ mrb_f_float(mrb_state *mrb, mrb_value self)
* 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.
+ * converted using <code>to_s</code> method.
*
* String(self) #=> "main"
* String(self.class) #=> "Object"
@@ -155,10 +153,7 @@ 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");
- }
+ tmp = mrb_convert_type(mrb, arg, MRB_TT_STRING, "String", "to_s");
return tmp;
}
@@ -166,9 +161,7 @@ mrb_f_string(mrb_state *mrb, mrb_value self)
* call-seq:
* Array(arg) -> array
*
- * Returns +arg+ as an Array.
- *
- * First tries to call Array#to_ary on +arg+, then Array#to_a.
+ * Returns +arg+ as an Array using to_a method.
*
* Array(1..5) #=> [1, 2, 3, 4, 5]
*
@@ -179,10 +172,7 @@ 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");
- }
+ 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);
}
@@ -194,9 +184,9 @@ mrb_f_array(mrb_state *mrb, mrb_value self)
* 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>.
+ * Returns a <code>Hash</code> if <i>arg</i> is a <code>Hash</code>.
+ * Returns an empty <code>Hash</code> when <i>arg</i> is <tt>nil</tt>
+ * or <tt>[]</tt>.
*
* Hash([]) #=> {}
* Hash(nil) #=> {}
@@ -207,37 +197,13 @@ mrb_f_array(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_f_hash(mrb_state *mrb, mrb_value self)
{
- mrb_value arg, tmp;
+ mrb_value arg;
mrb_get_args(mrb, "o", &arg);
- if (mrb_nil_p(arg)) {
+ if (mrb_nil_p(arg) || (mrb_array_p(arg) && RARRAY_LEN(arg) == 0)) {
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;
-}
-
-/*
- * call-seq:
- * obj.itself -> an_object
- *
- * Returns <i>obj</i>.
- *
- * string = 'my string' #=> "my string"
- * string.itself.object_id == string.object_id #=> true
- *
- */
-static mrb_value
-mrb_f_itself(mrb_state *mrb, mrb_value self)
-{
- return self;
+ return mrb_ensure_hash_type(mrb, arg);
}
void
@@ -255,7 +221,6 @@ mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
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));
- mrb_define_module_function(mrb, krn, "itself", mrb_f_itself, MRB_ARGS_NONE());
}
void
diff --git a/mrbgems/mruby-kernel-ext/test/kernel.rb b/mrbgems/mruby-kernel-ext/test/kernel.rb
index 206b7ac74..ad9177165 100644
--- a/mrbgems/mruby-kernel-ext/test/kernel.rb
+++ b/mrbgems/mruby-kernel-ext/test/kernel.rb
@@ -49,21 +49,25 @@ assert('Kernel#__method__') do
end
assert('Kernel#Integer') do
- assert_equal(123, Integer(123.999)) if class_defined?("Float")
assert_equal(26, Integer("0x1a"))
assert_equal(930, Integer("0930", 10))
assert_equal(7, Integer("111", 2))
assert_equal(0, Integer("0"))
assert_equal(0, Integer("00000"))
assert_raise(TypeError) { Integer(nil) }
+ skip unless Object.const_defined?(:Float)
+ assert_equal(123, Integer(123.999))
end
assert('Kernel#Float') do
+ skip unless Object.const_defined?(:Float)
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 if class_defined?("Float")
+ assert_raise(ArgumentError) { Float("1.5a") }
+ assert_raise(ArgumentError) { Float("1.5\0") }
+end
assert('Kernel#String') do
assert_equal("main", String(self))
diff --git a/mrbgems/mruby-math/src/math.c b/mrbgems/mruby-math/src/math.c
index c182debea..35fcd0fa6 100644
--- a/mrbgems/mruby-math/src/math.c
+++ b/mrbgems/mruby-math/src/math.c
@@ -4,6 +4,10 @@
** See Copyright Notice in mruby.h
*/
+#ifdef MRB_WITHOUT_FLOAT
+# error Conflict 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
#include <mruby.h>
#include <mruby/array.h>
@@ -23,8 +27,6 @@ domain_error(mrb_state *mrb, const char *func)
#include <float.h>
-#define MATH_TOLERANCE 1E-12
-
double
asinh(double x)
{
@@ -122,7 +124,8 @@ erf(double x)
term *= xsqr/j;
sum += term/(2*j+1);
++j;
- } while (fabs(term/sum) > MATH_TOLERANCE);
+ if (sum == 0) break;
+ } while (fabs(term/sum) > DBL_EPSILON);
return two_sqrtpi*sum;
}
@@ -155,12 +158,16 @@ erfc(double x)
n += 0.5;
q1 = q2;
q2 = b/d;
- } while (fabs(q1-q2)/q2 > MATH_TOLERANCE);
+ } while (fabs(q1-q2)/q2 > DBL_EPSILON);
return one_sqrtpi*exp(-x*x)*q2;
}
#endif
+#if defined __FreeBSD__ && !defined __FreeBSD_version
+#include <osreldate.h> /* for __FreeBSD_version */
+#endif
+
#if (defined _MSC_VER && _MSC_VER < 1800) || defined __ANDROID__ || (defined __FreeBSD__ && __FreeBSD_version < 803000)
double
@@ -738,12 +745,6 @@ mrb_mruby_math_gem_init(mrb_state* mrb)
mrb_define_const(mrb, mrb_math, "E", mrb_float_value(mrb, exp(1.0)));
#endif
-#ifdef MRB_USE_FLOAT
- mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-5));
-#else
- mrb_define_const(mrb, mrb_math, "TOLERANCE", mrb_float_value(mrb, 1e-12));
-#endif
-
mrb_define_module_function(mrb, mrb_math, "sin", math_sin, MRB_ARGS_REQ(1));
mrb_define_module_function(mrb, mrb_math, "cos", math_cos, MRB_ARGS_REQ(1));
mrb_define_module_function(mrb, mrb_math, "tan", math_tan, MRB_ARGS_REQ(1));
diff --git a/mrbgems/mruby-math/test/math.rb b/mrbgems/mruby-math/test/math.rb
index e9ea07cc1..86a4fc12c 100644
--- a/mrbgems/mruby-math/test/math.rb
+++ b/mrbgems/mruby-math/test/math.rb
@@ -1,46 +1,31 @@
##
# Math Test
-##
-# Performs fuzzy check for equality on methods returning floats
-# on the basis of the Math::TOLERANCE constant.
-def check_float(a, b)
- tolerance = Math::TOLERANCE
- a = a.to_f
- b = b.to_f
- if a.finite? and b.finite?
- (a-b).abs < tolerance
- else
- true
- end
-end
-
assert('Math.sin 0') do
- check_float(Math.sin(0), 0)
+ assert_float(0, Math.sin(0))
end
assert('Math.sin PI/2') do
- check_float(Math.sin(Math::PI / 2), 1)
+ assert_float(1, Math.sin(Math::PI / 2))
end
assert('Math.cos 0') do
- check_float(Math.cos(0), 1)
+ assert_float(1, Math.cos(0))
end
assert('Math.cos PI/2') do
- check_float(Math.cos(Math::PI / 2), 0)
+ assert_float(0, Math.cos(Math::PI / 2))
end
assert('Math.tan 0') do
- check_float(Math.tan(0), 0)
+ assert_float(0, Math.tan(0))
end
assert('Math.tan PI/4') do
- check_float(Math.tan(Math::PI / 4), 1)
+ assert_float(1, Math.tan(Math::PI / 4))
end
assert('Fundamental trig identities') do
- result = true
N = 13
N.times do |i|
a = Math::PI / N * i
@@ -48,105 +33,100 @@ assert('Fundamental trig identities') do
s = Math.sin(a)
c = Math.cos(a)
t = Math.tan(a)
- result &= check_float(s, Math.cos(ca))
- result &= check_float(t, 1 / Math.tan(ca))
- result &= check_float(s ** 2 + c ** 2, 1)
- result &= check_float(t ** 2 + 1, (1/c) ** 2)
- result &= check_float((1/t) ** 2 + 1, (1/s) ** 2)
+ assert_float(Math.cos(ca), s)
+ assert_float(1 / Math.tan(ca), t)
+ assert_float(1, s ** 2 + c ** 2)
+ assert_float((1/c) ** 2, t ** 2 + 1)
+ assert_float((1/s) ** 2, (1/t) ** 2 + 1)
end
- result
end
assert('Math.erf 0') do
- check_float(Math.erf(0), 0)
+ assert_float(0, Math.erf(0))
end
assert('Math.exp 0') do
- check_float(Math.exp(0), 1.0)
+ assert_float(1.0, Math.exp(0))
end
assert('Math.exp 1') do
- check_float(Math.exp(1), 2.718281828459045)
+ assert_float(2.718281828459045, Math.exp(1))
end
assert('Math.exp 1.5') do
- check_float(Math.exp(1.5), 4.4816890703380645)
+ assert_float(4.4816890703380645, Math.exp(1.5))
end
assert('Math.log 1') do
- check_float(Math.log(1), 0)
+ assert_float(0, Math.log(1))
end
assert('Math.log E') do
- check_float(Math.log(Math::E), 1.0)
+ assert_float(1.0, Math.log(Math::E))
end
assert('Math.log E**3') do
- check_float(Math.log(Math::E**3), 3.0)
+ assert_float(3.0, Math.log(Math::E**3))
end
assert('Math.log2 1') do
- check_float(Math.log2(1), 0.0)
+ assert_float(0.0, Math.log2(1))
end
assert('Math.log2 2') do
- check_float(Math.log2(2), 1.0)
+ assert_float(1.0, Math.log2(2))
end
assert('Math.log10 1') do
- check_float(Math.log10(1), 0.0)
+ assert_float(0.0, Math.log10(1))
end
assert('Math.log10 10') do
- check_float(Math.log10(10), 1.0)
+ assert_float(1.0, Math.log10(10))
end
assert('Math.log10 10**100') do
- check_float(Math.log10(10**100), 100.0)
+ assert_float(100.0, Math.log10(10**100))
end
assert('Math.sqrt') do
num = [0.0, 1.0, 2.0, 3.0, 4.0]
sqr = [0, 1, 4, 9, 16]
- result = true
sqr.each_with_index do |v,i|
- result &= check_float(Math.sqrt(v), num[i])
+ assert_float(num[i], Math.sqrt(v))
end
- result
end
assert('Math.cbrt') do
num = [-2.0, -1.0, 0.0, 1.0, 2.0]
cub = [-8, -1, 0, 1, 8]
- result = true
cub.each_with_index do |v,i|
- result &= check_float(Math.cbrt(v), num[i])
+ assert_float(num[i], Math.cbrt(v))
end
- result
end
assert('Math.hypot') do
- check_float(Math.hypot(3, 4), 5.0)
+ assert_float(5.0, Math.hypot(3, 4))
end
assert('Math.frexp 1234') do
n = 1234
fraction, exponent = Math.frexp(n)
- check_float(Math.ldexp(fraction, exponent), n)
+ assert_float(n, Math.ldexp(fraction, exponent))
end
assert('Math.erf 1') do
- check_float(Math.erf(1), 0.842700792949715)
+ assert_float(0.842700792949715, Math.erf(1))
end
assert('Math.erfc 1') do
- check_float(Math.erfc(1), 0.157299207050285)
+ assert_float(0.157299207050285, Math.erfc(1))
end
assert('Math.erf -1') do
- check_float(Math.erf(-1), -0.8427007929497148)
+ assert_float(-0.8427007929497148, Math.erf(-1))
end
assert('Math.erfc -1') do
- check_float(Math.erfc(-1), 1.8427007929497148)
+ assert_float(1.8427007929497148, Math.erfc(-1))
end
diff --git a/mrbgems/mruby-metaprog/mrbgem.rake b/mrbgems/mruby-metaprog/mrbgem.rake
new file mode 100644
index 000000000..1b81d6345
--- /dev/null
+++ b/mrbgems/mruby-metaprog/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-metaprog') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Meta-programming features for mruby'
+end
diff --git a/mrbgems/mruby-metaprog/src/metaprog.c b/mrbgems/mruby-metaprog/src/metaprog.c
new file mode 100644
index 000000000..62daa5227
--- /dev/null
+++ b/mrbgems/mruby-metaprog/src/metaprog.c
@@ -0,0 +1,724 @@
+#include "mruby.h"
+#include "mruby/array.h"
+#include "mruby/hash.h"
+#include "mruby/variable.h"
+#include "mruby/proc.h"
+#include "mruby/class.h"
+#include "mruby/string.h"
+
+typedef enum {
+ NOEX_PUBLIC = 0x00,
+ NOEX_NOSUPER = 0x01,
+ NOEX_PRIVATE = 0x02,
+ NOEX_PROTECTED = 0x04,
+ NOEX_MASK = 0x06,
+ NOEX_BASIC = 0x08,
+ NOEX_UNDEF = NOEX_NOSUPER,
+ NOEX_MODFUNC = 0x12,
+ NOEX_SUPER = 0x20,
+ NOEX_VCALL = 0x40,
+ NOEX_RESPONDS = 0x80
+} mrb_method_flag_t;
+
+static mrb_value
+mrb_f_nil(mrb_state *mrb, mrb_value cv)
+{
+ return mrb_nil_value();
+}
+
+/* 15.3.1.3.20 */
+/*
+ * call-seq:
+ * obj.instance_variable_defined?(symbol) -> true or false
+ *
+ * Returns <code>true</code> if the given instance variable is
+ * defined in <i>obj</i>.
+ *
+ * class Fred
+ * def initialize(p1, p2)
+ * @a, @b = p1, p2
+ * end
+ * end
+ * fred = Fred.new('cat', 99)
+ * fred.instance_variable_defined?(:@a) #=> true
+ * fred.instance_variable_defined?("@b") #=> true
+ * fred.instance_variable_defined?("@c") #=> false
+ */
+static mrb_value
+mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym sym;
+
+ mrb_get_args(mrb, "n", &sym);
+ mrb_iv_name_sym_check(mrb, sym);
+ return mrb_bool_value(mrb_iv_defined(mrb, self, sym));
+}
+
+/* 15.3.1.3.21 */
+/*
+ * call-seq:
+ * obj.instance_variable_get(symbol) -> obj
+ *
+ * Returns the value of the given instance variable, or nil if the
+ * instance variable is not set. The <code>@</code> part of the
+ * variable name should be included for regular instance
+ * variables. Throws a <code>NameError</code> exception if the
+ * supplied symbol is not valid as an instance variable name.
+ *
+ * class Fred
+ * def initialize(p1, p2)
+ * @a, @b = p1, p2
+ * end
+ * end
+ * fred = Fred.new('cat', 99)
+ * fred.instance_variable_get(:@a) #=> "cat"
+ * fred.instance_variable_get("@b") #=> 99
+ */
+static mrb_value
+mrb_obj_ivar_get(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym iv_name;
+
+ mrb_get_args(mrb, "n", &iv_name);
+ mrb_iv_name_sym_check(mrb, iv_name);
+ return mrb_iv_get(mrb, self, iv_name);
+}
+
+/* 15.3.1.3.22 */
+/*
+ * call-seq:
+ * obj.instance_variable_set(symbol, obj) -> obj
+ *
+ * Sets the instance variable names by <i>symbol</i> to
+ * <i>object</i>, thereby frustrating the efforts of the class's
+ * author to attempt to provide proper encapsulation. The variable
+ * did not have to exist prior to this call.
+ *
+ * class Fred
+ * def initialize(p1, p2)
+ * @a, @b = p1, p2
+ * end
+ * end
+ * fred = Fred.new('cat', 99)
+ * fred.instance_variable_set(:@a, 'dog') #=> "dog"
+ * fred.instance_variable_set(:@c, 'cat') #=> "cat"
+ * fred.inspect #=> "#<Fred:0x401b3da8 @a=\"dog\", @b=99, @c=\"cat\">"
+ */
+static mrb_value
+mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
+{
+ mrb_sym iv_name;
+ mrb_value val;
+
+ mrb_get_args(mrb, "no", &iv_name, &val);
+ mrb_iv_name_sym_check(mrb, iv_name);
+ mrb_iv_set(mrb, self, iv_name, val);
+ return val;
+}
+
+/* 15.3.1.2.7 */
+/* 15.3.1.3.28 */
+/*
+ * call-seq:
+ * local_variables -> array
+ *
+ * Returns the names of local variables in the current scope.
+ *
+ * [mruby limitation]
+ * If variable symbol information was stripped out from
+ * compiled binary files using `mruby-strip -l`, this
+ * method always returns an empty array.
+ */
+static mrb_value
+mrb_local_variables(mrb_state *mrb, mrb_value self)
+{
+ struct RProc *proc;
+ mrb_irep *irep;
+ mrb_value vars;
+ size_t i;
+
+ proc = mrb->c->ci[-1].proc;
+
+ if (MRB_PROC_CFUNC_P(proc)) {
+ return mrb_ary_new(mrb);
+ }
+ vars = mrb_hash_new(mrb);
+ while (proc) {
+ if (MRB_PROC_CFUNC_P(proc)) break;
+ irep = proc->body.irep;
+ if (!irep->lv) break;
+ for (i = 0; i + 1 < irep->nlocals; ++i) {
+ if (irep->lv[i].name) {
+ mrb_sym sym = irep->lv[i].name;
+ const char *name = mrb_sym2name(mrb, sym);
+ switch (name[0]) {
+ case '*': case '&':
+ break;
+ default:
+ mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value());
+ break;
+ }
+ }
+ }
+ if (!MRB_PROC_ENV_P(proc)) break;
+ proc = proc->upper;
+ //if (MRB_PROC_SCOPE_P(proc)) break;
+ if (!proc->c) break;
+ }
+
+ return mrb_hash_keys(mrb, vars);
+}
+
+KHASH_DECLARE(st, mrb_sym, char, FALSE)
+
+static void
+method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set)
+{
+ khint_t i;
+
+ khash_t(mt) *h = klass->mt;
+ if (!h || kh_size(h) == 0) return;
+ for (i=0;i<kh_end(h);i++) {
+ if (kh_exist(h, i)) {
+ mrb_method_t m = kh_value(h, i);
+ if (MRB_METHOD_UNDEF_P(m)) continue;
+ kh_put(st, mrb, set, kh_key(h, i));
+ }
+ }
+}
+
+static mrb_value
+mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* klass, int obj)
+{
+ 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_FL_CLASS_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 && !prepended) ||
+ (klass->tt == MRB_TT_SCLASS)) {
+ }
+ else {
+ if (!recur) break;
+ }
+ oldklass = klass;
+ klass = klass->super;
+ }
+
+ ary = mrb_ary_new_capa(mrb, kh_size(set));
+ 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)));
+ }
+ }
+ kh_destroy(st, mrb, set);
+
+ return ary;
+}
+
+static mrb_value
+mrb_obj_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj, mrb_method_flag_t flag)
+{
+ return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0);
+}
+/* 15.3.1.3.31 */
+/*
+ * call-seq:
+ * obj.methods -> array
+ *
+ * Returns a list of the names of methods publicly accessible in
+ * <i>obj</i>. This will include all the methods accessible in
+ * <i>obj</i>'s ancestors.
+ *
+ * class Klass
+ * def kMethod()
+ * end
+ * end
+ * k = Klass.new
+ * k.methods[0..9] #=> [:kMethod, :respond_to?, :nil?, :is_a?,
+ * # :class, :instance_variable_set,
+ * # :methods, :extend, :__send__, :instance_eval]
+ * k.methods.length #=> 42
+ */
+static mrb_value
+mrb_obj_methods_m(mrb_state *mrb, mrb_value self)
+{
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_obj_methods(mrb, recur, self, (mrb_method_flag_t)0); /* everything but private */
+}
+
+/* 15.3.1.3.36 */
+/*
+ * call-seq:
+ * obj.private_methods(all=true) -> array
+ *
+ * Returns the list of private methods accessible to <i>obj</i>. If
+ * the <i>all</i> parameter is set to <code>false</code>, only those methods
+ * in the receiver will be listed.
+ */
+static mrb_value
+mrb_obj_private_methods(mrb_state *mrb, mrb_value self)
+{
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_obj_methods(mrb, recur, self, NOEX_PRIVATE); /* private attribute not define */
+}
+
+/* 15.3.1.3.37 */
+/*
+ * call-seq:
+ * obj.protected_methods(all=true) -> array
+ *
+ * Returns the list of protected methods accessible to <i>obj</i>. If
+ * the <i>all</i> parameter is set to <code>false</code>, only those methods
+ * in the receiver will be listed.
+ */
+static mrb_value
+mrb_obj_protected_methods(mrb_state *mrb, mrb_value self)
+{
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_obj_methods(mrb, recur, self, NOEX_PROTECTED); /* protected attribute not define */
+}
+
+/* 15.3.1.3.38 */
+/*
+ * call-seq:
+ * obj.public_methods(all=true) -> array
+ *
+ * Returns the list of public methods accessible to <i>obj</i>. If
+ * the <i>all</i> parameter is set to <code>false</code>, only those methods
+ * in the receiver will be listed.
+ */
+static mrb_value
+mrb_obj_public_methods(mrb_state *mrb, mrb_value self)
+{
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_obj_methods(mrb, recur, self, NOEX_PUBLIC); /* public attribute not define */
+}
+
+static mrb_value
+mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj)
+{
+ khint_t i;
+ mrb_value ary;
+ struct RClass* klass;
+ khash_t(st)* set = kh_init(st, mrb);
+
+ klass = mrb_class(mrb, obj);
+
+ if (klass && (klass->tt == MRB_TT_SCLASS)) {
+ method_entry_loop(mrb, klass, set);
+ klass = klass->super;
+ }
+ if (recur) {
+ while (klass && ((klass->tt == MRB_TT_SCLASS) || (klass->tt == MRB_TT_ICLASS))) {
+ method_entry_loop(mrb, klass, set);
+ klass = klass->super;
+ }
+ }
+
+ 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)));
+ }
+ }
+ kh_destroy(st, mrb, set);
+
+ return ary;
+}
+
+/* 15.3.1.3.45 */
+/*
+ * call-seq:
+ * obj.singleton_methods(all=true) -> array
+ *
+ * Returns an array of the names of singleton methods for <i>obj</i>.
+ * If the optional <i>all</i> parameter is true, the list will include
+ * methods in modules included in <i>obj</i>.
+ * Only public and protected singleton methods are returned.
+ *
+ * module Other
+ * def three() end
+ * end
+ *
+ * class Single
+ * def Single.four() end
+ * end
+ *
+ * a = Single.new
+ *
+ * def a.one()
+ * end
+ *
+ * class << a
+ * include Other
+ * def two()
+ * end
+ * end
+ *
+ * Single.singleton_methods #=> [:four]
+ * a.singleton_methods(false) #=> [:two, :one]
+ * a.singleton_methods #=> [:two, :one, :three]
+ */
+static mrb_value
+mrb_obj_singleton_methods_m(mrb_state *mrb, mrb_value self)
+{
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_obj_singleton_methods(mrb, recur, self);
+}
+
+static mrb_value
+mod_define_singleton_method(mrb_state *mrb, mrb_value self)
+{
+ struct RProc *p;
+ mrb_method_t m;
+ mrb_sym mid;
+ mrb_value blk = mrb_nil_value();
+
+ mrb_get_args(mrb, "n&", &mid, &blk);
+ if (mrb_nil_p(blk)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+ }
+ p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
+ mrb_proc_copy(p, mrb_proc_ptr(blk));
+ p->flags |= MRB_PROC_STRICT;
+ MRB_METHOD_FROM_PROC(m, p);
+ mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, m);
+ return mrb_symbol_value(mid);
+}
+
+static mrb_bool
+cv_name_p(mrb_state *mrb, const char *name, mrb_int len)
+{
+ return len > 2 && name[0] == '@' && name[1] == '@' &&
+ !ISDIGIT(name[2]) && mrb_ident_p(name+2, len-2);
+}
+
+static void
+check_cv_name_sym(mrb_state *mrb, mrb_sym id)
+{
+ mrb_int len;
+ const char *name = mrb_sym2name_len(mrb, id, &len);
+ if (!cv_name_p(mrb, name, len)) {
+ mrb_name_error(mrb, id, "'%S' is not allowed as a class variable name", mrb_sym2str(mrb, id));
+ }
+}
+
+/* 15.2.2.4.39 */
+/*
+ * call-seq:
+ * remove_class_variable(sym) -> obj
+ *
+ * Removes the definition of the <i>sym</i>, returning that
+ * constant's value.
+ *
+ * class Dummy
+ * @@var = 99
+ * puts @@var
+ * p class_variables
+ * remove_class_variable(:@@var)
+ * p class_variables
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * 99
+ * [:@@var]
+ * []
+ */
+
+static mrb_value
+mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod)
+{
+ mrb_value val;
+ mrb_sym id;
+
+ mrb_get_args(mrb, "n", &id);
+ check_cv_name_sym(mrb, id);
+
+ val = mrb_iv_remove(mrb, mod, id);
+ if (!mrb_undef_p(val)) return val;
+
+ if (mrb_cv_defined(mrb, mod, id)) {
+ mrb_name_error(mrb, id, "cannot remove %S for %S",
+ mrb_sym2str(mrb, id), mod);
+ }
+
+ mrb_name_error(mrb, id, "class variable %S not defined for %S",
+ mrb_sym2str(mrb, id), mod);
+
+ /* not reached */
+ return mrb_nil_value();
+}
+
+/* 15.2.2.4.16 */
+/*
+ * call-seq:
+ * obj.class_variable_defined?(symbol) -> true or false
+ *
+ * Returns <code>true</code> if the given class variable is defined
+ * in <i>obj</i>.
+ *
+ * class Fred
+ * @@foo = 99
+ * end
+ * Fred.class_variable_defined?(:@@foo) #=> true
+ * Fred.class_variable_defined?(:@@bar) #=> false
+ */
+
+static mrb_value
+mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod)
+{
+ mrb_sym id;
+
+ 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 */
+/*
+ * call-seq:
+ * mod.class_variable_get(symbol) -> obj
+ *
+ * Returns the value of the given class variable (or throws a
+ * <code>NameError</code> exception). The <code>@@</code> part of the
+ * variable name should be included for regular class variables
+ *
+ * class Fred
+ * @@foo = 99
+ * end
+ * Fred.class_variable_get(:@@foo) #=> 99
+ */
+
+static mrb_value
+mrb_mod_cvar_get(mrb_state *mrb, mrb_value mod)
+{
+ mrb_sym id;
+
+ mrb_get_args(mrb, "n", &id);
+ check_cv_name_sym(mrb, id);
+ return mrb_cv_get(mrb, mod, id);
+}
+
+/* 15.2.2.4.18 */
+/*
+ * call-seq:
+ * obj.class_variable_set(symbol, obj) -> obj
+ *
+ * Sets the class variable names by <i>symbol</i> to
+ * <i>object</i>.
+ *
+ * class Fred
+ * @@foo = 99
+ * def foo
+ * @@foo
+ * end
+ * end
+ * Fred.class_variable_set(:@@foo, 101) #=> 101
+ * Fred.new.foo #=> 101
+ */
+
+static mrb_value
+mrb_mod_cvar_set(mrb_state *mrb, mrb_value mod)
+{
+ mrb_value value;
+ mrb_sym id;
+
+ mrb_get_args(mrb, "no", &id, &value);
+ check_cv_name_sym(mrb, id);
+ mrb_cv_set(mrb, mod, id, value);
+ return value;
+}
+
+static mrb_value
+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 != 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;
+ }
+
+ return result;
+}
+
+/* 15.2.2.4.33 */
+/*
+ * call-seq:
+ * mod.instance_methods(include_super=true) -> array
+ *
+ * Returns an array containing the names of the public and protected instance
+ * methods in the receiver. For a module, these are the public and protected methods;
+ * for a class, they are the instance (not singleton) methods. With no
+ * argument, or with an argument that is <code>false</code>, the
+ * instance methods in <i>mod</i> are returned, otherwise the methods
+ * in <i>mod</i> and <i>mod</i>'s superclasses are returned.
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * def method2() end
+ * end
+ * class C < B
+ * def method3() end
+ * end
+ *
+ * A.instance_methods #=> [:method1]
+ * B.instance_methods(false) #=> [:method2]
+ * C.instance_methods(false) #=> [:method3]
+ * C.instance_methods(true).length #=> 43
+ */
+
+static mrb_value
+mrb_mod_instance_methods(mrb_state *mrb, mrb_value mod)
+{
+ struct RClass *c = mrb_class_ptr(mod);
+ mrb_bool recur = TRUE;
+ mrb_get_args(mrb, "|b", &recur);
+ return mrb_class_instance_method_list(mrb, recur, c, 0);
+}
+
+static void
+remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid)
+{
+ struct RClass *c = mrb_class_ptr(mod);
+ khash_t(mt) *h;
+ khiter_t k;
+
+ MRB_CLASS_ORIGIN(c);
+ h = c->mt;
+
+ 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_sym2str(mrb, mid), mod);
+}
+
+/* 15.2.2.4.41 */
+/*
+ * call-seq:
+ * remove_method(symbol) -> self
+ *
+ * Removes the method identified by _symbol_ from the current
+ * class. For an example, see <code>Module.undef_method</code>.
+ */
+
+static mrb_value
+mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
+{
+ mrb_int argc;
+ mrb_value *argv;
+
+ mrb_get_args(mrb, "*", &argv, &argc);
+ mrb_check_frozen(mrb, mrb_obj_ptr(mod));
+ while (argc--) {
+ remove_method(mrb, mod, mrb_obj_to_sym(mrb, *argv));
+ argv++;
+ }
+ return mod;
+}
+
+static mrb_value
+mrb_mod_s_constants(mrb_state *mrb, mrb_value mod)
+{
+ mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented");
+ return mrb_nil_value(); /* not reached */
+}
+
+static mrb_value
+mrb_mod_s_nesting(mrb_state *mrb, mrb_value mod)
+{
+ struct RProc *proc;
+ mrb_value ary;
+ struct RClass *c = NULL;
+
+ mrb_get_args(mrb, "");
+ ary = mrb_ary_new(mrb);
+ proc = mrb->c->ci[-1].proc; /* callee proc */
+ mrb_assert(!MRB_PROC_CFUNC_P(proc));
+ while (proc) {
+ if (MRB_PROC_SCOPE_P(proc)) {
+ struct RClass *c2 = MRB_PROC_TARGET_CLASS(proc);
+
+ if (c2 != c) {
+ c = c2;
+ mrb_ary_push(mrb, ary, mrb_obj_value(c));
+ }
+ }
+ proc = proc->upper;
+ }
+ return ary;
+}
+
+void
+mrb_mruby_metaprog_gem_init(mrb_state* mrb)
+{
+ struct RClass *krn = mrb->kernel_module;
+ struct RClass *mod = mrb->module_class;
+
+ mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 (15.3.1.2.4) */
+ mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 (15.3.1.2.7) */
+
+ mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE());
+ mrb_define_method(mrb, krn, "instance_variable_defined?", mrb_obj_ivar_defined, MRB_ARGS_REQ(1)); /* 15.3.1.3.20 */
+ mrb_define_method(mrb, krn, "instance_variable_get", mrb_obj_ivar_get, MRB_ARGS_REQ(1)); /* 15.3.1.3.21 */
+ mrb_define_method(mrb, krn, "instance_variable_set", mrb_obj_ivar_set, MRB_ARGS_REQ(2)); /* 15.3.1.3.22 */
+ mrb_define_method(mrb, krn, "instance_variables", mrb_obj_instance_variables, MRB_ARGS_NONE()); /* 15.3.1.3.23 */
+ mrb_define_method(mrb, krn, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */
+ mrb_define_method(mrb, krn, "private_methods", mrb_obj_private_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.36 */
+ mrb_define_method(mrb, krn, "protected_methods", mrb_obj_protected_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.37 */
+ mrb_define_method(mrb, krn, "public_methods", mrb_obj_public_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.38 */
+ mrb_define_method(mrb, krn, "singleton_methods", mrb_obj_singleton_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.45 */
+ mrb_define_method(mrb, krn, "define_singleton_method", mod_define_singleton_method, MRB_ARGS_ANY());
+ mrb_define_method(mrb, krn, "send", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.44 */
+
+ mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, MRB_ARGS_NONE()); /* 15.2.2.4.19 */
+ 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, "class_variable_defined?", mrb_mod_cvar_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.16 */
+ mrb_define_method(mrb, mod, "class_variable_get", mrb_mod_cvar_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.17 */
+ 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, "included_modules", mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */
+ mrb_define_method(mrb, mod, "instance_methods", mrb_mod_instance_methods, MRB_ARGS_ANY()); /* 15.2.2.4.33 */
+ 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_f_nil, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mod, "constants", mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */
+ mrb_define_class_method(mrb, mod, "constants", mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.1 */
+ mrb_define_class_method(mrb, mod, "nesting", mrb_mod_s_nesting, MRB_ARGS_REQ(0)); /* 15.2.2.3.2 */
+}
+
+void
+mrb_mruby_metaprog_gem_final(mrb_state* mrb)
+{
+}
diff --git a/mrbgems/mruby-metaprog/test/metaprog.rb b/mrbgems/mruby-metaprog/test/metaprog.rb
new file mode 100644
index 000000000..30b75bd60
--- /dev/null
+++ b/mrbgems/mruby-metaprog/test/metaprog.rb
@@ -0,0 +1,400 @@
+assert('Kernel#send', '15.3.1.3.44') do
+ # test with block
+ l = send(:lambda) do
+ true
+ end
+
+ assert_true l.call
+ assert_equal l.class, Proc
+ # test with argument
+ assert_true send(:respond_to?, :nil?)
+ # test without argument and without block
+ assert_equal send(:to_s).class, String
+end
+
+assert('Kernel#instance_variable_defined?', '15.3.1.3.20') do
+ o = Object.new
+ o.instance_variable_set(:@a, 1)
+
+ assert_true o.instance_variable_defined?("@a")
+ assert_false o.instance_variable_defined?("@b")
+ assert_true o.instance_variable_defined?("@a"[0,2])
+ assert_true o.instance_variable_defined?("@abc"[0,2])
+ assert_raise(NameError) { o.instance_variable_defined?("@0") }
+end
+
+assert('Kernel#instance_variable_get', '15.3.1.3.21') do
+ o = Class.new { attr_accessor :foo, :bar }.new
+ o.foo = "one"
+ o.bar = 2
+ assert_equal("one", o.instance_variable_get(:@foo))
+ assert_equal(2, o.instance_variable_get("@bar"))
+ assert_equal(nil, o.instance_variable_get(:@baz))
+ %w[foo @1].each do |n|
+ assert_raise(NameError) { o.instance_variable_get(n) }
+ end
+end
+
+assert('Kernel#instance_variable_set', '15.3.1.3.22') do
+ o = Class.new { attr_reader :foo, :_bar }.new
+ assert_equal("one", o.instance_variable_set(:@foo, "one"))
+ assert_equal("one", o.foo)
+ assert_equal(2, o.instance_variable_set("@_bar", 2))
+ assert_equal(2, o._bar)
+ %w[@6 @% @@a @ a].each do |n|
+ assert_raise(NameError) { o.instance_variable_set(n, 1) }
+ end
+ assert_raise(FrozenError) { o.freeze.instance_variable_set(:@a, 2) }
+ assert_raise(FrozenError, ArgumentError) { nil.instance_variable_set(:@a, 2) }
+end
+
+assert('Kernel#instance_variables', '15.3.1.3.23') do
+ o = Object.new
+ o.instance_eval do
+ @a = 11
+ @b = 12
+ end
+ ivars = o.instance_variables
+
+ assert_equal Array, ivars.class,
+ assert_equal(2, ivars.size)
+ assert_true ivars.include?(:@a)
+ assert_true ivars.include?(:@b)
+end
+
+assert('Kernel#methods', '15.3.1.3.31') do
+ assert_equal Array, methods.class
+end
+
+assert('Kernel#private_methods', '15.3.1.3.36') do
+ assert_equal Array, private_methods.class
+end
+
+assert('Kernel#protected_methods', '15.3.1.3.37') do
+ assert_equal Array, protected_methods.class
+end
+
+assert('Kernel#public_methods', '15.3.1.3.38') do
+ assert_equal Array, public_methods.class
+ class Foo
+ def foo
+ end
+ end
+ assert_equal [:foo], Foo.new.public_methods(false)
+end
+
+assert('Kernel#singleton_methods', '15.3.1.3.45') do
+ assert_equal singleton_methods.class, Array
+end
+
+assert('Kernel.global_variables', '15.3.1.2.4') do
+ assert_equal Array, Kernel.global_variables.class
+end
+
+assert('Kernel#global_variables', '15.3.1.3.14') do
+ variables1 = global_variables
+ assert_equal Array, variables1.class
+ assert_not_include(variables1, :$kernel_global_variables_test)
+
+ $kernel_global_variables_test = nil
+ variables2 = global_variables
+ assert_include(variables2, :$kernel_global_variables_test)
+ assert_equal(1, variables2.size - variables1.size)
+end
+
+assert('Kernel.local_variables', '15.3.1.2.7') do
+ a, b = 0, 1
+ a += b
+
+ vars = Kernel.local_variables.sort
+ assert_equal [:a, :b, :vars], vars
+
+ assert_equal [:a, :b, :c, :vars], Proc.new { |a, b|
+ c = 2
+ # Kernel#local_variables: 15.3.1.3.28
+ local_variables.sort
+ }.call(-1, -2)
+end
+
+assert('Kernel#define_singleton_method') do
+ o = Object.new
+ ret = o.define_singleton_method(:test_method) do
+ :singleton_method_ok
+ end
+ assert_equal :test_method, ret
+ assert_equal :singleton_method_ok, o.test_method
+ assert_raise(TypeError) { 2.define_singleton_method(:f){} }
+ assert_raise(FrozenError) { [].freeze.define_singleton_method(:f){} }
+end
+
+assert('Kernel#singleton_class') do
+ o1 = Object.new
+ assert_same(o1.singleton_class, class << o1; self end)
+
+ o2 = Object.new
+ sc2 = class << o2; self end
+ assert_same(o2.singleton_class, sc2)
+
+ o3 = Object.new
+ sc3 = o3.singleton_class
+ o3.freeze
+ assert_predicate(sc3, :frozen?)
+
+ assert_predicate(Object.new.freeze.singleton_class, :frozen?)
+end
+
+def labeled_module(name, &block)
+ Module.new do
+ (class <<self; self end).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
+ (class <<self; self end).class_eval do
+ define_method(:to_s) { name }
+ alias_method :inspect, :to_s
+ end
+ class_eval(&block) if block
+ end
+end
+
+assert('Module#class_variable_defined?', '15.2.2.4.16') do
+ class Test4ClassVariableDefined
+ @@cv = 99
+ end
+
+ assert_true Test4ClassVariableDefined.class_variable_defined?(:@@cv)
+ assert_false Test4ClassVariableDefined.class_variable_defined?(:@@noexisting)
+ assert_raise(NameError) { Test4ClassVariableDefined.class_variable_defined?("@@2") }
+end
+
+assert('Module#class_variable_get', '15.2.2.4.17') do
+ class Test4ClassVariableGet
+ @@cv = 99
+ end
+
+ assert_equal 99, Test4ClassVariableGet.class_variable_get(:@@cv)
+ assert_raise(NameError) { Test4ClassVariableGet.class_variable_get(:@@a) }
+ %w[@@a? @@! @a a].each do |n|
+ assert_raise(NameError) { Test4ClassVariableGet.class_variable_get(n) }
+ end
+end
+
+assert('Module#class_variable_set', '15.2.2.4.18') do
+ class Test4ClassVariableSet
+ @@foo = 100
+ def foo
+ @@foo
+ end
+ end
+ assert_equal 99, Test4ClassVariableSet.class_variable_set(:@@cv, 99)
+ assert_equal 101, Test4ClassVariableSet.class_variable_set(:@@foo, 101)
+ assert_true Test4ClassVariableSet.class_variables.include? :@@cv
+ assert_equal 99, Test4ClassVariableSet.class_variable_get(:@@cv)
+ assert_equal 101, Test4ClassVariableSet.new.foo
+ %w[@@ @@1 @@x= @x @ x 1].each do |n|
+ assert_raise(NameError) { Test4ClassVariableSet.class_variable_set(n, 1) }
+ end
+
+ m = Module.new.freeze
+ assert_raise(FrozenError) { m.class_variable_set(:@@cv, 1) }
+
+ parent = Class.new{ class_variable_set(:@@a, nil) }.freeze
+ child = Class.new(parent)
+ assert_raise(FrozenError) { child.class_variable_set(:@@a, 1) }
+end
+
+assert('Module#class_variables', '15.2.2.4.19') do
+ class Test4ClassVariables1
+ @@var1 = 1
+ end
+ class Test4ClassVariables2 < Test4ClassVariables1
+ @@var2 = 2
+ end
+
+ assert_equal [:@@var1], Test4ClassVariables1.class_variables
+ assert_equal [:@@var2, :@@var1], Test4ClassVariables2.class_variables
+end
+
+assert('Module#constants', '15.2.2.4.24') do
+ $n = []
+ module TestA
+ C = 1
+ end
+ class TestB
+ include TestA
+ C2 = 1
+ $n = constants.sort
+ end
+
+ assert_equal [ :C ], TestA.constants
+ assert_equal [ :C, :C2 ], $n
+end
+
+assert('Module#included_modules', '15.2.2.4.30') do
+ module Test4includedModules
+ end
+ module Test4includedModules2
+ include Test4includedModules
+ end
+ r = Test4includedModules2.included_modules
+
+ assert_equal Array, r.class
+ assert_true r.include?(Test4includedModules)
+end
+
+assert('Module#instance_methods', '15.2.2.4.33') do
+ module Test4InstanceMethodsA
+ def method1() end
+ end
+ class Test4InstanceMethodsB
+ def method2() end
+ end
+ class Test4InstanceMethodsC < Test4InstanceMethodsB
+ def method3() end
+ end
+
+ r = Test4InstanceMethodsC.instance_methods(true)
+
+ assert_equal [:method1], Test4InstanceMethodsA.instance_methods
+ assert_equal [:method2], Test4InstanceMethodsB.instance_methods(false)
+ assert_equal [:method3], Test4InstanceMethodsC.instance_methods(false)
+ assert_equal Array, r.class
+ assert_true r.include?(:method3)
+ assert_true r.include?(:method2)
+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('Module#remove_class_variable', '15.2.2.4.39') do
+ class Test4RemoveClassVariable
+ @@cv = 99
+ end
+
+ assert_equal 99, Test4RemoveClassVariable.remove_class_variable(:@@cv)
+ assert_false Test4RemoveClassVariable.class_variables.include? :@@cv
+ assert_raise(NameError) do
+ Test4RemoveClassVariable.remove_class_variable(:@@cv)
+ end
+ assert_raise(NameError) do
+ Test4RemoveClassVariable.remove_class_variable(:@v)
+ end
+ assert_raise(FrozenError) do
+ Test4RemoveClassVariable.freeze.remove_class_variable(:@@cv)
+ end
+end
+
+assert('Module#remove_method', '15.2.2.4.41') do
+ module Test4RemoveMethod
+ class Parent
+ def hello
+ end
+ end
+
+ class Child < Parent
+ def hello
+ end
+ end
+ end
+
+ klass = Test4RemoveMethod::Child
+ assert_same klass, klass.class_eval{ remove_method :hello }
+ assert_true klass.instance_methods.include? :hello
+ assert_false klass.instance_methods(false).include? :hello
+ assert_raise(FrozenError) { klass.freeze.remove_method :m }
+end
+
+assert('Module.nesting', '15.2.2.2.2') do
+ module Test4ModuleNesting
+ module Test4ModuleNesting2
+ assert_equal [Test4ModuleNesting2, Test4ModuleNesting],
+ Module.nesting
+ end
+ end
+ module Test4ModuleNesting::Test4ModuleNesting2
+ assert_equal [Test4ModuleNesting::Test4ModuleNesting2], Module.nesting
+ end
+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('[Bug #7843]') do
+ c.class_eval do
+ remove_method(:foo)
+ end
+ end
+ assert_equal(:foo, removed)
+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("remove_method doesn't segfault if the passed in argument isn't a symbol") do
+ klass = Class.new
+ assert_raise(TypeError) { klass.remove_method nil }
+ assert_raise(TypeError) { klass.remove_method 123 }
+ assert_raise(TypeError) { klass.remove_method 1.23 }
+ assert_raise(NameError) { klass.remove_method "hello" }
+ assert_raise(TypeError) { klass.remove_method Class.new }
+end
+
+assert('alias_method and remove_method') do
+ begin
+ Fixnum.alias_method :to_s_, :to_s
+ Fixnum.remove_method :to_s
+
+ assert_nothing_raised do
+ # segfaults if mrb_cptr is used
+ 1.to_s
+ end
+ ensure
+ Fixnum.alias_method :to_s, :to_s_
+ Fixnum.remove_method :to_s_
+ end
+end
diff --git a/mrbgems/mruby-method/mrblib/kernel.rb b/mrbgems/mruby-method/mrblib/kernel.rb
index b2ebd45ea..eb17df5a6 100644
--- a/mrbgems/mruby-method/mrblib/kernel.rb
+++ b/mrbgems/mruby-method/mrblib/kernel.rb
@@ -1,8 +1,9 @@
module Kernel
def singleton_method(name)
m = method(name)
- if m.owner != singleton_class
- raise NameError, "undefined method `#{name}' for class `#{singleton_class}'"
+ sc = (class <<self; self; end)
+ if m.owner != sc
+ raise NameError, "undefined method '#{name}' for class '#{sc}'"
end
m
end
diff --git a/mrbgems/mruby-method/mrblib/method.rb b/mrbgems/mruby-method/mrblib/method.rb
index 5de0afdf7..f7cefa2e5 100644
--- a/mrbgems/mruby-method/mrblib/method.rb
+++ b/mrbgems/mruby-method/mrblib/method.rb
@@ -17,4 +17,12 @@ class Method
def name
@name
end
+
+ def <<(other)
+ ->(*args, &block) { call(other.call(*args, &block)) }
+ end
+
+ def >>(other)
+ ->(*args, &block) { other.call(call(*args, &block)) }
+ end
end
diff --git a/mrbgems/mruby-method/src/method.c b/mrbgems/mruby-method/src/method.c
index e445b34c8..d94db1cb2 100644
--- a/mrbgems/mruby-method/src/method.c
+++ b/mrbgems/mruby-method/src/method.c
@@ -212,19 +212,8 @@ static mrb_value
method_arity(mrb_state *mrb, mrb_value self)
{
mrb_value proc = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "proc"));
- struct RProc *rproc;
- struct RClass *orig;
- mrb_value ret;
-
- if (mrb_nil_p(proc))
- return mrb_fixnum_value(-1);
-
- rproc = mrb_proc_ptr(proc);
- orig = rproc->c;
- rproc->c = mrb->proc_class;
- ret = mrb_funcall(mrb, proc, "arity", 0);
- rproc->c = orig;
- return ret;
+ mrb_int arity = mrb_nil_p(proc) ? -1 : mrb_proc_arity(mrb_proc_ptr(proc));
+ return mrb_fixnum_value(arity);
}
static mrb_value
@@ -281,16 +270,16 @@ method_to_s(mrb_state *mrb, mrb_value self)
mrb_str_cat_lit(mrb, str, ": ");
rklass = mrb_class_ptr(klass);
if (mrb_class_ptr(owner) == rklass) {
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, owner));
mrb_str_cat_lit(mrb, str, "#");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, name));
}
else {
mrb_str_cat_cstr(mrb, str, mrb_class_name(mrb, rklass));
mrb_str_cat_lit(mrb, str, "(");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, owner, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, owner));
mrb_str_cat_lit(mrb, str, ")#");
- mrb_str_cat_str(mrb, str, mrb_funcall(mrb, name, "to_s", 0));
+ mrb_str_cat_str(mrb, str, mrb_str_to_str(mrb, name));
}
mrb_str_cat_lit(mrb, str, ">");
return str;
@@ -327,7 +316,7 @@ name_error:
s = mrb_class_name(mrb, c);
mrb_raisef(
mrb, E_NAME_ERROR,
- "undefined method `%S' for class `%S'",
+ "undefined method '%S' for class '%S'",
mrb_sym2str(mrb, name),
mrb_str_new_static(mrb, s, strlen(s))
);
@@ -387,7 +376,7 @@ mrb_mruby_method_gem_init(mrb_state* mrb)
mrb_define_method(mrb, unbound_method, "bind", unbound_method_bind, MRB_ARGS_REQ(1));
mrb_define_method(mrb, unbound_method, "super_method", method_super_method, MRB_ARGS_NONE());
mrb_define_method(mrb, unbound_method, "==", method_eql, MRB_ARGS_REQ(1));
- mrb_alias_method(mrb, unbound_method, mrb_intern_lit(mrb, "eql?"), mrb_intern_lit(mrb, "=="));
+ mrb_define_alias(mrb, unbound_method, "eql?", "==");
mrb_define_method(mrb, unbound_method, "to_s", method_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, unbound_method, "inspect", method_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, unbound_method, "arity", method_arity, MRB_ARGS_NONE());
@@ -396,11 +385,11 @@ mrb_mruby_method_gem_init(mrb_state* mrb)
mrb_undef_class_method(mrb, method, "new");
mrb_define_method(mrb, method, "==", method_eql, MRB_ARGS_REQ(1));
- mrb_alias_method(mrb, method, mrb_intern_lit(mrb, "eql?"), mrb_intern_lit(mrb, "=="));
+ mrb_define_alias(mrb, method, "eql?", "==");
mrb_define_method(mrb, method, "to_s", method_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, method, "inspect", method_to_s, MRB_ARGS_NONE());
mrb_define_method(mrb, method, "call", method_call, MRB_ARGS_ANY());
- mrb_alias_method(mrb, method, mrb_intern_lit(mrb, "[]"), mrb_intern_lit(mrb, "call"));
+ mrb_define_alias(mrb, method, "[]", "call");
mrb_define_method(mrb, method, "unbind", method_unbind, MRB_ARGS_NONE());
mrb_define_method(mrb, method, "super_method", method_super_method, MRB_ARGS_NONE());
mrb_define_method(mrb, method, "arity", method_arity, MRB_ARGS_NONE());
diff --git a/mrbgems/mruby-method/test/method.rb b/mrbgems/mruby-method/test/method.rb
index b229a4560..0b67d3e61 100644
--- a/mrbgems/mruby-method/test/method.rb
+++ b/mrbgems/mruby-method/test/method.rb
@@ -21,7 +21,7 @@ class Interpreter
}
def interpret(string)
@ret = ""
- string.each_char {|b| Dispatcher[b].bind(self).call }
+ string.split("").each {|b| Dispatcher[b].bind(self).call }
end
end
@@ -102,10 +102,7 @@ end
assert 'Method#call for regression' do
obj = BasicObject.new
- def obj.foo
- :ok
- end
- assert_equal :ok, Kernel.instance_method(:send).bind(obj).call(:foo), "https://github.com/ksss/mruby-method/issues/4"
+ assert_equal String, Kernel.instance_method(:inspect).bind(obj).call().class, "https://github.com/ksss/mruby-method/issues/4"
end
assert 'Method#call with undefined method' do
@@ -149,7 +146,7 @@ assert 'Method#source_location' do
assert_equal [filename, lineno], klass.new.method(:find_me_if_you_can).source_location
lineno = __LINE__ + 1
- klass.define_singleton_method(:s_find_me_if_you_can) {}
+ class <<klass; define_method(:s_find_me_if_you_can) {}; end
assert_equal [filename, lineno], klass.method(:s_find_me_if_you_can).source_location
klass = Class.new { def respond_to_missing?(m, b); m == :nothing; end }
@@ -243,7 +240,7 @@ assert 'owner' do
assert_equal(c, c.new.method(:foo).owner)
assert_equal(c, c2.new.method(:foo).owner)
- assert_equal(c.singleton_class, c2.method(:bar).owner)
+ assert_equal((class <<c; self; end), c2.method(:bar).owner)
end
assert 'owner missing' do
@@ -374,6 +371,25 @@ assert "Method#initialize_copy" do
assert_equal(m1, m2)
end
+assert "Method#<< and Method#>>" do
+ obj = Object.new
+ class << obj
+ def mul2(n); n * 2; end
+ def add3(n); n + 3; end
+ end
+
+ f = obj.method(:mul2)
+ g = obj.method(:add3)
+
+ m1 = f << g
+ assert_kind_of Proc, m1
+ assert_equal 16, m1.call(5)
+
+ m2 = f >> g
+ assert_kind_of Proc, m2
+ assert_equal 13, m2.call(5)
+end
+
assert 'UnboundMethod#arity' do
c = Class.new {
def foo(a, b)
@@ -413,12 +429,14 @@ assert 'UnboundMethod#bind' do
assert_equal(:meth, m.bind(1).call)
assert_equal(:meth, m.bind(:sym).call)
assert_equal(:meth, m.bind(Object.new).call)
- sc = Class.new {
- class << self
+ sc = nil
+ Class.new {
+ sc = class << self
def foo
end
+ self
end
- }.singleton_class
+ }
assert_raise(TypeError) { sc.instance_method(:foo).bind([]) }
assert_raise(TypeError) { Array.instance_method(:each).bind(1) }
assert_kind_of Method, Object.instance_method(:object_id).bind(Object.new)
diff --git a/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb b/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
index f250538fe..576605cb1 100644
--- a/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
+++ b/mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb
@@ -1,8 +1,4 @@
-module Integral
- def div(other)
- self.divmod(other)[0]
- end
-
+class Numeric
def zero?
self == 0
end
diff --git a/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
index 1d6a07769..cd8bbf187 100644
--- a/mrbgems/mruby-numeric-ext/src/numeric_ext.c
+++ b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
@@ -2,13 +2,10 @@
#include <mruby.h>
static inline mrb_int
-to_int(mrb_value x)
+to_int(mrb_state *mrb, mrb_value x)
{
- double f;
-
- if (mrb_fixnum_p(x)) return mrb_fixnum(x);
- f = mrb_float(x);
- return (mrb_int)f;
+ x = mrb_to_int(mrb, x);
+ return mrb_fixnum(x);
}
/*
@@ -28,7 +25,7 @@ mrb_int_chr(mrb_state *mrb, mrb_value x)
mrb_int chr;
char c;
- chr = to_int(x);
+ chr = to_int(mrb, x);
if (chr >= (1 << CHAR_BIT)) {
mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x);
}
@@ -48,8 +45,8 @@ mrb_int_allbits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
- n = to_int(self);
mrb_get_args(mrb, "i", &m);
+ n = to_int(mrb, self);
return mrb_bool_value((n & m) == m);
}
@@ -64,8 +61,8 @@ mrb_int_anybits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
- n = to_int(self);
mrb_get_args(mrb, "i", &m);
+ n = to_int(mrb, self);
return mrb_bool_value((n & m) != 0);
}
@@ -80,8 +77,8 @@ mrb_int_nobits(mrb_state *mrb, mrb_value self)
{
mrb_int n, m;
- n = to_int(self);
mrb_get_args(mrb, "i", &m);
+ n = to_int(mrb, self);
return mrb_bool_value((n & m) == 0);
}
diff --git a/mrbgems/mruby-numeric-ext/test/numeric.rb b/mrbgems/mruby-numeric-ext/test/numeric.rb
index 6ea0c14e7..c85cb61f2 100644
--- a/mrbgems/mruby-numeric-ext/test/numeric.rb
+++ b/mrbgems/mruby-numeric-ext/test/numeric.rb
@@ -14,8 +14,9 @@ assert('Integer#div') do
end
assert('Float#div') do
+ skip unless Object.const_defined?(:Float)
assert_float 52, 365.2425.div(7)
-end if class_defined?("Float")
+end
assert('Integer#zero?') do
assert_equal true, 0.zero?
diff --git a/mrbgems/mruby-object-ext/mrbgem.rake b/mrbgems/mruby-object-ext/mrbgem.rake
index 6d14b4a51..1aea2c8cd 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 = 'Object class extension'
+ spec.summary = 'extensional methods shared by all objects'
end
diff --git a/mrbgems/mruby-object-ext/mrblib/object.rb b/mrbgems/mruby-object-ext/mrblib/object.rb
index 581156cb0..f014df469 100644
--- a/mrbgems/mruby-object-ext/mrblib/object.rb
+++ b/mrbgems/mruby-object-ext/mrblib/object.rb
@@ -1,4 +1,18 @@
-class Object
+module Kernel
+ # call-seq:
+ # obj.yield_self {|_obj|...} -> an_object
+ # obj.then {|_obj|...} -> an_object
+ #
+ # Yields <i>obj</i> and returns the result.
+ #
+ # 'my string'.yield_self {|s|s.upcase} #=> "MY STRING"
+ #
+ def yield_self(&block)
+ return to_enum :yield_self unless block
+ block.call(self)
+ end
+ alias then yield_self
+
##
# call-seq:
# obj.tap{|x|...} -> obj
diff --git a/mrbgems/mruby-object-ext/src/object.c b/mrbgems/mruby-object-ext/src/object.c
index b076b3ec0..85db75b28 100644
--- a/mrbgems/mruby-object-ext/src/object.c
+++ b/mrbgems/mruby-object-ext/src/object.c
@@ -46,6 +46,22 @@ nil_to_i(mrb_state *mrb, mrb_value obj)
/*
* call-seq:
+ * obj.itself -> an_object
+ *
+ * Returns <i>obj</i>.
+ *
+ * string = 'my string' #=> "my string"
+ * string.itself.object_id == string.object_id #=> true
+ *
+ */
+static mrb_value
+mrb_f_itself(mrb_state *mrb, mrb_value self)
+{
+ return self;
+}
+
+/*
+ * call-seq:
* obj.instance_exec(arg...) {|var...| block } -> obj
*
* Executes the given block within the context of the receiver
@@ -103,7 +119,9 @@ mrb_mruby_object_ext_gem_init(mrb_state* mrb)
#endif
mrb_define_method(mrb, n, "to_i", nil_to_i, MRB_ARGS_NONE());
- mrb_define_method(mrb, mrb->kernel_module, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
+ mrb_define_method(mrb, mrb->kernel_module, "itself", mrb_f_itself, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, mrb_class_get(mrb, "BasicObject"), "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
}
void
diff --git a/mrbgems/mruby-object-ext/test/nil.rb b/mrbgems/mruby-object-ext/test/nil.rb
index 7f773637a..fbff20629 100644
--- a/mrbgems/mruby-object-ext/test/nil.rb
+++ b/mrbgems/mruby-object-ext/test/nil.rb
@@ -3,8 +3,9 @@ assert('NilClass#to_a') do
end
assert('NilClass#to_f') do
+ skip unless Object.const_defined?(:Float)
assert_equal 0.0, nil.to_f
-end if class_defined?("Float")
+end
assert('NilClass#to_i') do
assert_equal 0, nil.to_i
diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c
index 3887091d3..b31dee04c 100644
--- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c
+++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c
@@ -57,7 +57,7 @@ os_count_objects(mrb_state *mrb, mrb_value self)
hash = mrb_hash_new(mrb);
}
- if (!mrb_test(mrb_hash_empty_p(mrb, hash))) {
+ if (!mrb_hash_empty_p(mrb, hash)) {
mrb_hash_clear(mrb, hash);
}
diff --git a/mrbgems/mruby-objectspace/test/objectspace.rb b/mrbgems/mruby-objectspace/test/objectspace.rb
index 0553b97e2..9c44c2157 100644
--- a/mrbgems/mruby-objectspace/test/objectspace.rb
+++ b/mrbgems/mruby-objectspace/test/objectspace.rb
@@ -1,6 +1,6 @@
assert('ObjectSpace.count_objects') do
h = {}
- f = Fiber.new {} if Object.const_defined? :Fiber
+ f = Fiber.new {} if Object.const_defined?(:Fiber)
ObjectSpace.count_objects(h)
assert_kind_of(Hash, h)
assert_true(h.keys.all? {|x| x.is_a?(Symbol) || x.is_a?(Integer) })
@@ -56,5 +56,5 @@ assert('ObjectSpace.each_object') do
end
assert 'Check class pointer of ObjectSpace.each_object.' do
- ObjectSpace.each_object { |obj| !obj }
+ assert_nothing_raised { ObjectSpace.each_object { |obj| !obj } }
end
diff --git a/mrbgems/mruby-pack/src/pack.c b/mrbgems/mruby-pack/src/pack.c
index d96ef1002..9f091194b 100644
--- a/mrbgems/mruby-pack/src/pack.c
+++ b/mrbgems/mruby-pack/src/pack.c
@@ -3,7 +3,7 @@
*/
#include "mruby.h"
-#include "error.h"
+#include "mruby/error.h"
#include "mruby/array.h"
#include "mruby/class.h"
#include "mruby/numeric.h"
@@ -60,19 +60,39 @@ enum {
#define PACK_BASE64_IGNORE 0xff
#define PACK_BASE64_PADDING 0xfe
-static int littleendian = 0;
-
const static unsigned char base64chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static signed char base64_dec_tab[128];
+static unsigned char base64_dec_tab[128];
+#if !defined(BYTE_ORDER) && defined(__BYTE_ORDER__)
+# define BYTE_ORDER __BYTE_ORDER__
+#endif
+#if !defined(BIG_ENDIAN) && defined(__ORDER_BIG_ENDIAN__)
+# define BIG_ENDIAN __ORDER_BIG_ENDIAN__
+#endif
+#if !defined(LITTLE_ENDIAN) && defined(__ORDER_LITTLE_ENDIAN__)
+# define LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__
+#endif
-static int
+#ifdef BYTE_ORDER
+# if BYTE_ORDER == BIG_ENDIAN
+# define littleendian 0
+# define check_little_endian() (void)0
+# elif BYTE_ORDER == LITTLE_ENDIAN
+# define littleendian 1
+# define check_little_endian() (void)0
+# endif
+#endif
+#ifndef littleendian
+/* can't distinguish endian in compile time */
+static int littleendian = 0;
+static void
check_little_endian(void)
{
unsigned int n = 1;
- return (*(unsigned char *)&n == 1);
+ littleendian = (*(unsigned char *)&n == 1);
}
+#endif
static unsigned int
hex2int(unsigned char ch)
@@ -98,9 +118,9 @@ make_base64_dec_tab(void)
base64_dec_tab['a' + i] = i + 26;
for (i = 0; i < 10; i++)
base64_dec_tab['0' + i] = i + 52;
- base64_dec_tab['+'] = 62;
- base64_dec_tab['/'] = 63;
- base64_dec_tab['='] = PACK_BASE64_PADDING;
+ base64_dec_tab['+'+0] = 62;
+ base64_dec_tab['/'+0] = 63;
+ base64_dec_tab['='+0] = PACK_BASE64_PADDING;
}
static mrb_value
@@ -313,21 +333,23 @@ pack_double(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned i
d = mrb_float(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
- for (i = 0; i < 8; ++i) {
- RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+ if (littleendian) {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
+ }
+ else {
+ for (i = 0; i < 8; ++i) {
+ RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+ }
}
-#else
- memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
-#endif
} else {
-#ifdef MRB_ENDIAN_BIG
- memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
-#else
- for (i = 0; i < 8; ++i) {
- RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+ if (littleendian) {
+ for (i = 0; i < 8; ++i) {
+ RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
+ }
+ }
+ else {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
}
-#endif
}
return 8;
@@ -341,21 +363,23 @@ unpack_double(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value a
uint8_t *buffer = (uint8_t *)&d;
if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
- for (i = 0; i < 8; ++i) {
- buffer[8 - i - 1] = src[i];
+ if (littleendian) {
+ memcpy(buffer, src, 8);
+ }
+ else {
+ for (i = 0; i < 8; ++i) {
+ buffer[8 - i - 1] = src[i];
+ }
}
-#else
- memcpy(buffer, src, 8);
-#endif
} else {
-#ifdef MRB_ENDIAN_BIG
- memcpy(buffer, src, 8);
-#else
- for (i = 0; i < 8; ++i) {
- buffer[8 - i - 1] = src[i];
+ if (littleendian) {
+ for (i = 0; i < 8; ++i) {
+ buffer[8 - i - 1] = src[i];
+ }
+ }
+ else {
+ memcpy(buffer, src, 8);
}
-#endif
}
mrb_ary_push(mrb, ary, mrb_float_value(mrb, d));
@@ -372,21 +396,23 @@ pack_float(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned in
f = (float)mrb_float(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
- for (i = 0; i < 4; ++i) {
- RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+ if (littleendian) {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
+ }
+ else {
+ for (i = 0; i < 4; ++i) {
+ RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+ }
}
-#else
- memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
-#endif
} else {
-#ifdef MRB_ENDIAN_BIG
- memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
-#else
- for (i = 0; i < 4; ++i) {
- RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+ if (littleendian) {
+ for (i = 0; i < 4; ++i) {
+ RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
+ }
+ }
+ else {
+ memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
}
-#endif
}
return 4;
@@ -400,21 +426,23 @@ unpack_float(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ar
uint8_t *buffer = (uint8_t *)&f;
if (flags & PACK_FLAG_LITTLEENDIAN) {
-#ifdef MRB_ENDIAN_BIG
- for (i = 0; i < 4; ++i) {
- buffer[4 - i - 1] = src[i];
+ if (littleendian) {
+ memcpy(buffer, src, 4);
+ }
+ else {
+ for (i = 0; i < 4; ++i) {
+ buffer[4 - i - 1] = src[i];
+ }
}
-#else
- memcpy(buffer, src, 4);
-#endif
} else {
-#ifdef MRB_ENDIAN_BIG
- memcpy(buffer, src, 4);
-#else
- for (i = 0; i < 4; ++i) {
- buffer[4 - i - 1] = src[i];
+ if (littleendian) {
+ for (i = 0; i < 4; ++i) {
+ buffer[4 - i - 1] = src[i];
+ }
+ }
+ else {
+ memcpy(buffer, src, 4);
}
-#endif
}
mrb_ary_push(mrb, ary, mrb_float_value(mrb, f));
@@ -429,11 +457,6 @@ pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count,
int len = 0;
uint32_t c = 0;
-#ifndef MRB_WITHOUT_FLOAT
- if (mrb_float_p(o)) {
- goto range_error;
- }
-#endif
c = (uint32_t)mrb_fixnum(o);
/* Unicode character */
@@ -461,9 +484,6 @@ pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count,
len = 4;
}
else {
-#ifndef MRB_WITHOUT_FLOAT
-range_error:
-#endif
mrb_raise(mrb, E_RANGE_ERROR, "pack(U): value out of range");
}
@@ -510,7 +530,7 @@ utf8_to_uv(mrb_state *mrb, const char *p, long *lenp)
}
if (n > *lenp) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %S bytes, given %S bytes)",
- mrb_fixnum_value(n), mrb_fixnum_value(*lenp));
+ mrb_fixnum_value(n), mrb_fixnum_value(*lenp));
}
*lenp = n--;
if (n != 0) {
@@ -598,19 +618,21 @@ unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, u
}
copylen = slen;
- if (flags & PACK_FLAG_Z) { /* "Z" */
+ if (slen >= 0 && flags & PACK_FLAG_Z) { /* "Z" */
if ((cp = (const char *)memchr(sptr, '\0', slen)) != NULL) {
copylen = (int)(cp - sptr);
if (count == -1) {
slen = copylen + 1;
}
}
- } else if (!(flags & PACK_FLAG_a)) { /* "A" */
- while (copylen > 0 && (sptr[copylen - 1] == '\0' || isspace(sptr[copylen - 1]))) {
+ }
+ else if (!(flags & PACK_FLAG_a)) { /* "A" */
+ while (copylen > 0 && (sptr[copylen - 1] == '\0' || ISSPACE(sptr[copylen - 1]))) {
copylen--;
}
}
+ if (copylen < 0) copylen = 0;
dst = mrb_str_new(mrb, sptr, (mrb_int)copylen);
mrb_ary_push(mrb, ary, dst);
return slen;
@@ -980,7 +1002,7 @@ alias:
case 'm':
dir = PACK_DIR_BASE64;
type = PACK_TYPE_STRING;
- flags |= PACK_FLAG_WIDTH;
+ flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2;
break;
case 'N': /* = "L>" */
dir = PACK_DIR_LONG;
@@ -1050,13 +1072,14 @@ alias:
/* read suffix [0-9*_!<>] */
while (tmpl->idx < tlen) {
ch = tptr[tmpl->idx++];
- if (isdigit(ch)) {
+ if (ISDIGIT(ch)) {
count = ch - '0';
- while (tmpl->idx < tlen && isdigit(tptr[tmpl->idx])) {
- count = count * 10 + (tptr[tmpl->idx++] - '0');
- if (count < 0) {
- mrb_raisef(mrb, E_RUNTIME_ERROR, "too big template length");
+ while (tmpl->idx < tlen && ISDIGIT(tptr[tmpl->idx])) {
+ int ch = tptr[tmpl->idx++] - '0';
+ if (count+ch > INT_MAX/10) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "too big template length");
}
+ count = count * 10 + ch;
}
continue; /* special case */
} else if (ch == '*') {
@@ -1122,13 +1145,16 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
o = mrb_ary_ref(mrb, ary, aidx);
if (type == PACK_TYPE_INTEGER) {
o = mrb_to_int(mrb, o);
+ }
#ifndef MRB_WITHOUT_FLOAT
- } else if (type == PACK_TYPE_FLOAT) {
+ else if (type == PACK_TYPE_FLOAT) {
if (!mrb_float_p(o)) {
- o = mrb_funcall(mrb, o, "to_f", 0);
+ mrb_float f = mrb_to_flo(mrb, o);
+ o = mrb_float_value(mrb, f);
}
+ }
#endif
- } else if (type == PACK_TYPE_STRING) {
+ else if (type == PACK_TYPE_STRING) {
if (!mrb_string_p(o)) {
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into String", mrb_class_path(mrb, mrb_obj_class(mrb, o)));
}
@@ -1170,7 +1196,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
default:
break;
}
- if (dir == PACK_DIR_STR) { /* always consumes 1 entry */
+ if (dir == PACK_DIR_STR || dir == PACK_DIR_BASE64) { /* always consumes 1 entry */
aidx++;
break;
}
@@ -1188,7 +1214,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
}
static mrb_value
-mrb_pack_unpack(mrb_state *mrb, mrb_value str)
+pack_unpack(mrb_state *mrb, mrb_value str, int single)
{
mrb_value result;
struct tmpl tmpl;
@@ -1223,6 +1249,9 @@ mrb_pack_unpack(mrb_state *mrb, mrb_value str)
case PACK_DIR_STR:
srcidx += unpack_a(mrb, sptr, srclen - srcidx, result, count, flags);
break;
+ case PACK_DIR_BASE64:
+ srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags);
+ break;
}
continue;
}
@@ -1249,9 +1278,6 @@ mrb_pack_unpack(mrb_state *mrb, mrb_value str)
case PACK_DIR_QUAD:
srcidx += unpack_q(mrb, sptr, srclen - srcidx, result, flags);
break;
- case PACK_DIR_BASE64:
- srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags);
- break;
#ifndef MRB_WITHOUT_FLOAT
case PACK_DIR_FLOAT:
srcidx += unpack_float(mrb, sptr, srclen - srcidx, result, flags);
@@ -1270,19 +1296,39 @@ mrb_pack_unpack(mrb_state *mrb, mrb_value str)
count--;
}
}
+ if (single) break;
}
+ if (single) {
+ if (RARRAY_LEN(result) > 0) {
+ return RARRAY_PTR(result)[0];
+ }
+ return mrb_nil_value();
+ }
return result;
}
+static mrb_value
+mrb_pack_unpack(mrb_state *mrb, mrb_value str)
+{
+ return pack_unpack(mrb, str, 0);
+}
+
+static mrb_value
+mrb_pack_unpack1(mrb_state *mrb, mrb_value str)
+{
+ return pack_unpack(mrb, str, 1);
+}
+
void
mrb_mruby_pack_gem_init(mrb_state *mrb)
{
- littleendian = check_little_endian();
+ check_little_endian();
make_base64_dec_tab();
mrb_define_method(mrb, mrb->array_class, "pack", mrb_pack_pack, MRB_ARGS_REQ(1));
mrb_define_method(mrb, mrb->string_class, "unpack", mrb_pack_unpack, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mrb->string_class, "unpack1", mrb_pack_unpack1, MRB_ARGS_REQ(1));
}
void
diff --git a/mrbgems/mruby-pack/test/pack.rb b/mrbgems/mruby-pack/test/pack.rb
index 1788c2b72..68ef4165f 100644
--- a/mrbgems/mruby-pack/test/pack.rb
+++ b/mrbgems/mruby-pack/test/pack.rb
@@ -1,101 +1,95 @@
+PACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01
+
+def assert_pack tmpl, packed, unpacked
+ t = tmpl.inspect
+ assert do
+ assert_equal packed, unpacked.pack(tmpl), "#{unpacked.inspect}.pack(#{t})"
+ assert_equal unpacked, packed.unpack(tmpl), "#{packed.inspect}.unpack(#{t})"
+ end
+end
+
# pack & unpack 'm' (base64)
assert('[""].pack("m")') do
- ary = ""
- str = ""
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
+ assert_pack "m", "", [""]
end
assert('["\0"].pack("m")') do
- ary = "\0"
- str = "AA==\n"
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
+ assert_pack "m", "AA==\n", ["\0"]
end
assert('["\0\0"].pack("m")') do
- ary = "\0\0"
- str = "AAA=\n"
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
+ assert_pack "m", "AAA=\n", ["\0\0"]
end
assert('["\0\0\0"].pack("m")') do
- ary = "\0\0\0"
- str = "AAAA\n"
- [ary].pack("m") == str and
- str.unpack("m") == [ary]
+ assert_pack "m", "AAAA\n", ["\0\0\0"]
end
assert('["abc..xyzABC..XYZ"].pack("m")') do
- ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"].pack("m") == "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n"
+ assert_pack "m", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n", ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
end
assert('"YWJ...".unpack("m") should "abc..xyzABC..XYZ"') do
- str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m") == [str] and
- "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m") == [str]
+ ary = ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
+ assert_equal ary, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m")
+ assert_equal ary, "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m")
+end
+
+assert('["A", "B"].pack') do
+ assert_equal "QQ==\n", ["A", "B"].pack("m50")
+ assert_equal ["A"], "QQ==\n".unpack("m50")
+ assert_equal "QQ==Qg==", ["A", "B"].pack("m0 m0")
+ assert_equal ["A", "B"], "QQ==Qg==".unpack("m10 m10")
+end
+
+assert('["abc..xyzABC..XYZ"].pack("m0")') do
+ assert_pack "m0", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==", ["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]
end
# pack & unpack 'H'
assert('["3031"].pack("H*")') do
- ary = "3031"
- str = "01"
- [ary].pack("H*") == str and
- str.unpack("H*") == [ary]
+ assert_pack "H*", "01", ["3031"]
end
assert('["10"].pack("H*")') do
- ary = "10"
- str = "\020"
- [ary].pack("H*") == str and
- str.unpack("H*") == [ary]
+ assert_pack "H*", "\020", ["10"]
end
assert('[0,1,127,128,255].pack("C*")') do
- ary = [ 0, 1, 127, 128, 255 ]
- str = "\x00\x01\x7F\x80\xFF"
- ary.pack("C*") == str and str.unpack("C*") == ary
+ assert_pack "C*", "\x00\x01\x7F\x80\xFF", [0, 1, 127, 128, 255]
end
# pack "a"
assert('["abc"].pack("a")') do
- ["abc"].pack("a") == "a" and
- ["abc"].pack("a*") == "abc" and
- ["abc"].pack("a4") == "abc\0"
+ assert_equal "a", ["abc"].pack("a")
+ assert_equal "abc", ["abc"].pack("a*")
+ assert_equal "abc\0", ["abc"].pack("a4")
end
# upack "a"
assert('["abc"].pack("a")') do
- "abc\0".unpack("a4") == ["abc\0"] and
- "abc ".unpack("a4") == ["abc "]
+ assert_equal ["abc\0"], "abc\0".unpack("a4")
+ assert_equal ["abc "], "abc ".unpack("a4")
end
# pack "A"
assert('["abc"].pack("A")') do
- ["abc"].pack("A") == "a" and
- ["abc"].pack("A*") == "abc" and
- ["abc"].pack("A4") == "abc "
+ assert_equal "a", ["abc"].pack("A")
+ assert_equal "abc", ["abc"].pack("A*")
+ assert_equal "abc ", ["abc"].pack("A4")
end
# upack "A"
assert('["abc"].pack("A")') do
- "abc\0".unpack("A4") == ["abc"] and
- "abc ".unpack("A4") == ["abc"]
+ assert_equal ["abc"], "abc\0".unpack("A4")
+ assert_equal ["abc"], "abc ".unpack("A4")
end
# regression tests
assert('issue #1') do
- [1, 2].pack("nn") == "\000\001\000\002"
+ assert_equal "\000\001\000\002", [1, 2].pack("nn")
end
-def assert_pack tmpl, packed, unpacked
- assert_equal packed, unpacked.pack(tmpl)
- assert_equal unpacked, packed.unpack(tmpl)
-end
-
-PACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01
-
assert 'pack float' do
assert_pack 'e', "\x00\x00@@", [3.0]
assert_pack 'g', "@@\x00\x00", [3.0]
diff --git a/mrbgems/mruby-print/mrblib/print.rb b/mrbgems/mruby-print/mrblib/print.rb
index 38a10661b..27567d858 100644
--- a/mrbgems/mruby-print/mrblib/print.rb
+++ b/mrbgems/mruby-print/mrblib/print.rb
@@ -45,16 +45,13 @@ module Kernel
__printstr__ "\n"
i += 1
end
- args[0]
+ args.__svalue
end
unless Kernel.respond_to?(:sprintf)
def printf(*args)
raise NotImplementedError.new('printf not available')
end
- def sprintf(*args)
- raise NotImplementedError.new('sprintf not available')
- end
else
def printf(*args)
__printstr__(sprintf(*args))
diff --git a/mrbgems/mruby-print/src/print.c b/mrbgems/mruby-print/src/print.c
index e181b06e0..f7f99fc77 100644
--- a/mrbgems/mruby-print/src/print.c
+++ b/mrbgems/mruby-print/src/print.c
@@ -23,7 +23,6 @@ printstr(mrb_state *mrb, mrb_value obj)
char* utf8 = RSTRING_PTR(obj);
int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0);
wchar_t* utf16 = (wchar_t*)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),
diff --git a/mrbgems/mruby-proc-ext/mrblib/proc.rb b/mrbgems/mruby-proc-ext/mrblib/proc.rb
index b71663938..abe9c7944 100644
--- a/mrbgems/mruby-proc-ext/mrblib/proc.rb
+++ b/mrbgems/mruby-proc-ext/mrblib/proc.rb
@@ -27,7 +27,7 @@ class Proc
pproc = self
make_curry = proc do |given_args=[]|
- send(type) do |*args|
+ __send__(type) do |*args|
new_args = given_args + args
if new_args.size >= arity
pproc[*new_args]
@@ -39,4 +39,12 @@ class Proc
make_curry.call
end
+ def <<(other)
+ ->(*args, &block) { call(other.call(*args, &block)) }
+ end
+
+ def >>(other)
+ ->(*args, &block) { other.call(call(*args, &block)) }
+ end
+
end
diff --git a/mrbgems/mruby-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c
index b13606f5f..c9041ec75 100644
--- a/mrbgems/mruby-proc-ext/src/proc.c
+++ b/mrbgems/mruby-proc-ext/src/proc.c
@@ -25,8 +25,8 @@ mrb_proc_source_location(mrb_state *mrb, mrb_value self)
int32_t line;
const char *filename;
- filename = mrb_debug_get_filename(irep, 0);
- line = mrb_debug_get_line(irep, 0);
+ filename = mrb_debug_get_filename(mrb, irep, 0);
+ line = mrb_debug_get_line(mrb, irep, 0);
return (!filename && line == -1)? mrb_nil_value()
: mrb_assoc_new(mrb, mrb_str_new_cstr(mrb, filename), mrb_fixnum_value(line));
@@ -38,7 +38,7 @@ mrb_proc_inspect(mrb_state *mrb, mrb_value self)
{
struct RProc *p = mrb_proc_ptr(self);
mrb_value str = mrb_str_new_lit(mrb, "#<Proc:");
- mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(self)));
+ mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, mrb_cptr(self)));
if (!MRB_PROC_CFUNC_P(p)) {
mrb_irep *irep = p->body.irep;
@@ -46,13 +46,13 @@ mrb_proc_inspect(mrb_state *mrb, mrb_value self)
int32_t line;
mrb_str_cat_lit(mrb, str, "@");
- filename = mrb_debug_get_filename(irep, 0);
+ filename = mrb_debug_get_filename(mrb, irep, 0);
mrb_str_cat_cstr(mrb, str, filename ? filename : "-");
mrb_str_cat_lit(mrb, str, ":");
- line = mrb_debug_get_line(irep, 0);
+ line = mrb_debug_get_line(mrb, irep, 0);
if (line != -1) {
- str = mrb_format(mrb, "%S:%S", str, mrb_fixnum_value(line));
+ mrb_str_concat(mrb, str, mrb_fixnum_value(line));
}
else {
mrb_str_cat_lit(mrb, str, "-");
@@ -94,21 +94,23 @@ static mrb_value
mrb_proc_parameters(mrb_state *mrb, mrb_value self)
{
struct parameters_type {
- int size;
+ size_t len;
const char *name;
+ int size;
} *p, parameters_list [] = {
- {0, "req"},
- {0, "opt"},
- {0, "rest"},
- {0, "req"},
- {0, "block"},
- {0, NULL}
+ {sizeof("req") - 1, "req", 0},
+ {sizeof("opt") - 1, "opt", 0},
+ {sizeof("rest") - 1, "rest", 0},
+ {sizeof("req") - 1, "req", 0},
+ {sizeof("block") - 1, "block", 0},
+ {0, NULL, 0}
};
const struct RProc *proc = mrb_proc_ptr(self);
const struct mrb_irep *irep = proc->body.irep;
mrb_aspec aspec;
- mrb_value sname, parameters;
+ mrb_value parameters;
int i, j;
+ int max = -1;
if (MRB_PROC_CFUNC_P(proc)) {
// TODO cfunc aspec is not implemented yet
@@ -120,16 +122,18 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
if (!irep->lv) {
return mrb_ary_new(mrb);
}
- if (GET_OPCODE(*irep->iseq) != OP_ENTER) {
+ if (*irep->iseq != OP_ENTER) {
return mrb_ary_new(mrb);
}
if (!MRB_PROC_STRICT_P(proc)) {
+ parameters_list[0].len = sizeof("opt") - 1;
parameters_list[0].name = "opt";
+ parameters_list[3].len = sizeof("opt") - 1;
parameters_list[3].name = "opt";
}
- aspec = GETARG_Ax(*irep->iseq);
+ aspec = PEEK_W(irep->iseq+1);
parameters_list[0].size = MRB_ASPEC_REQ(aspec);
parameters_list[1].size = MRB_ASPEC_OPT(aspec);
parameters_list[2].size = MRB_ASPEC_REST(aspec);
@@ -138,14 +142,25 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
parameters = mrb_ary_new_capa(mrb, irep->nlocals-1);
+ max = irep->nlocals-1;
for (i = 0, p = parameters_list; p->name; p++) {
- if (p->size <= 0) continue;
- sname = mrb_symbol_value(mrb_intern_cstr(mrb, p->name));
+ mrb_value sname = mrb_symbol_value(mrb_intern_static(mrb, p->name, p->len));
+
for (j = 0; j < p->size; i++, j++) {
- mrb_value a = mrb_ary_new(mrb);
+ mrb_value a;
+
+ a = mrb_ary_new(mrb);
mrb_ary_push(mrb, a, sname);
- if (irep->lv[i].name) {
- mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i].name));
+ if (i < max && irep->lv[i].name) {
+ mrb_sym sym = irep->lv[i].name;
+ const char *name = mrb_sym2name(mrb, sym);
+ switch (name[0]) {
+ case '*': case '&':
+ break;
+ default:
+ mrb_ary_push(mrb, a, mrb_symbol_value(sym));
+ break;
+ }
}
mrb_ary_push(mrb, parameters, a);
}
diff --git a/mrbgems/mruby-proc-ext/test/proc.rb b/mrbgems/mruby-proc-ext/test/proc.rb
index 037d8d124..a6321d371 100644
--- a/mrbgems/mruby-proc-ext/test/proc.rb
+++ b/mrbgems/mruby-proc-ext/test/proc.rb
@@ -1,16 +1,37 @@
##
# Proc(Ext) Test
+def enable_debug_info?
+ return @enable_debug_info unless @enable_debug_info == nil
+ begin
+ raise
+ rescue => e
+ @enable_debug_info = !e.backtrace.empty?
+ end
+end
+
assert('Proc#source_location') do
- loc = Proc.new {}.source_location
- next true if loc.nil?
- assert_equal loc[0][-7, 7], 'proc.rb'
- assert_equal loc[1], 5
+ skip unless enable_debug_info?
+ file, line = Proc.new{}.source_location
+ assert_equal __FILE__, file
+ assert_equal __LINE__ - 2, line
end
assert('Proc#inspect') do
ins = Proc.new{}.inspect
- assert_kind_of String, ins
+ if enable_debug_info?
+ metas = %w(\\ * ? [ ] { })
+ file = __FILE__.split("").map{|c| metas.include?(c) ? "\\#{c}" : c}.join
+ line = __LINE__ - 4
+ else
+ file = line = "-"
+ end
+ assert_match "#<Proc:0x*@#{file}:#{line}>", ins
+end
+
+assert('Proc#parameters') do
+ parameters = Proc.new{|x,y=42,*other|}.parameters
+ assert_equal [[:opt, :x], [:opt, :y], [:rest, :other]], parameters
end
assert('Proc#lambda?') do
@@ -72,6 +93,19 @@ assert('Kernel#proc') do
end
end
+assert "Proc#<< and Proc#>>" do
+ add3 = ->(n) { n + 3 }
+ mul2 = ->(n) { n * 2 }
+
+ f1 = mul2 << add3
+ assert_kind_of Proc, f1
+ assert_equal 16, f1.call(5)
+
+ f2 = mul2 >> add3
+ assert_kind_of Proc, f2
+ assert_equal 13, f2.call(5)
+end
+
assert('mrb_proc_new_cfunc_with_env') do
ProcExtTest.mrb_proc_new_cfunc_with_env(:test)
ProcExtTest.mrb_proc_new_cfunc_with_env(:mruby)
diff --git a/mrbgems/mruby-random/src/random.c b/mrbgems/mruby-random/src/random.c
index b865244cc..99f2b02e4 100644
--- a/mrbgems/mruby-random/src/random.c
+++ b/mrbgems/mruby-random/src/random.c
@@ -4,6 +4,10 @@
** See Copyright Notice in mruby.h
*/
+#ifdef MRB_WITHOUT_FLOAT
+# error Conflict 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
#include <mruby.h>
#include <mruby/variable.h>
#include <mruby/class.h>
@@ -79,12 +83,12 @@ get_opt(mrb_state* mrb)
mrb_get_args(mrb, "|o", &arg);
if (!mrb_nil_p(arg)) {
- arg = mrb_check_convert_type(mrb, arg, MRB_TT_FIXNUM, "Fixnum", "to_int");
- if (mrb_nil_p(arg)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid argument type");
- }
- if (mrb_fixnum(arg) < 0) {
- arg = mrb_fixnum_value(0 - mrb_fixnum(arg));
+ mrb_int i;
+
+ arg = mrb_to_int(mrb, arg);
+ i = mrb_fixnum(arg);
+ if (i < 0) {
+ arg = mrb_fixnum_value(0 - i);
}
}
return arg;
@@ -219,7 +223,7 @@ mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary)
mrb_int j;
mrb_value *ptr = RARRAY_PTR(ary);
mrb_value tmp;
-
+
j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary))));
diff --git a/mrbgems/mruby-random/test/random.rb b/mrbgems/mruby-random/test/random.rb
index 1c59be3a6..cf4a55141 100644
--- a/mrbgems/mruby-random/test/random.rb
+++ b/mrbgems/mruby-random/test/random.rb
@@ -1,48 +1,58 @@
##
# Random Test
-assert("Random#srand") do
+assert("Random.new") do
r1 = Random.new(123)
r2 = Random.new(123)
- r1.rand == r2.rand
+ r3 = Random.new(124)
+ assert_equal(r1.rand, r2.rand)
+ assert_not_equal(r1.rand, r3.rand)
end
-assert("Kernel::srand") do
+assert("Kernel.srand") do
srand(234)
r1 = rand
srand(234)
r2 = rand
- r1 == r2
+ srand(235)
+ r3 = rand
+ assert_equal(r1, r2)
+ assert_not_equal(r1, r3)
end
-assert("Random::srand") do
+assert("Random.srand") do
Random.srand(345)
r1 = rand
srand(345)
r2 = Random.rand
- r1 == r2
+ Random.srand(346)
+ r3 = rand
+ assert_equal(r1, r2)
+ assert_not_equal(r1, r3)
end
-assert("fixnum") do
- rand(3).class == Fixnum
-end
-
-assert("float") do
- rand.class == Float
+assert("return class of Kernel.rand") do
+ assert_kind_of(Fixnum, rand(3))
+ assert_kind_of(Fixnum, rand(1.5))
+ assert_kind_of(Float, rand)
+ assert_kind_of(Float, rand(0.5))
end
assert("Array#shuffle") do
- ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ orig = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary = orig.dup
shuffled = ary.shuffle
-
- ary == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and shuffled != ary and 10.times { |x| ary.include? x }
+ assert_equal(orig, ary)
+ assert_not_equal(ary, shuffled)
+ assert_equal(orig, shuffled.sort)
end
assert('Array#shuffle!') do
- ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- ary.shuffle!
-
- ary != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary.include? x }
+ orig = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary = orig.dup
+ assert_same(ary, ary.shuffle!)
+ assert_not_equal(orig, ary)
+ assert_equal(orig, ary.sort)
end
assert("Array#shuffle(random)") do
@@ -52,12 +62,12 @@ assert("Array#shuffle(random)") do
end
# verify that the same seed causes the same results
- ary1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- shuffle1 = ary1.shuffle Random.new 345
- ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- shuffle2 = ary2.shuffle Random.new 345
-
- ary1 != shuffle1 and 10.times { |x| shuffle1.include? x } and shuffle1 == shuffle2
+ ary = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ shuffled1 = ary.shuffle Random.new 345
+ shuffled2 = ary.shuffle Random.new 345
+ shuffled3 = ary.shuffle Random.new 346
+ assert_equal(shuffled1, shuffled2)
+ assert_not_equal(shuffled1, shuffled3)
end
assert('Array#shuffle!(random)') do
@@ -71,18 +81,42 @@ assert('Array#shuffle!(random)') do
ary1.shuffle! Random.new 345
ary2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
ary2.shuffle! Random.new 345
-
- ary1 != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary1.include? x } and ary1 == ary2
+ ary3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ ary3.shuffle! Random.new 346
+ assert_equal(ary1, ary2)
+ assert_not_equal(ary1, ary3)
end
-assert('Array#sample checks input length after reading arguments') do
- $ary = [1, 2, 3]
- class ArrayChange
- def to_i
- $ary << 4
- 4
+assert('Array#sample') do
+ 100.times do
+ assert_include([0, 1, 2], [2, 1, 0].sample)
+ [2, 1, 0].sample(2).each { |sample| assert_include([0, 1, 2], sample) }
+ h = {}
+ (1..10).to_a.sample(7).each do |sample|
+ assert_not_include(h, sample)
+ h[sample] = true
end
end
- assert_equal [1, 2, 3, 4], $ary.sample(ArrayChange.new).sort
+ assert_nil([].sample)
+ assert_equal([], [].sample(1))
+ assert_equal([], [2, 1].sample(0))
+ assert_raise(TypeError) { [2, 1].sample(true) }
+ assert_raise(ArgumentError) { [2, 1].sample(-1) }
+end
+
+assert('Array#sample(random)') do
+ assert_raise(TypeError) do
+ # this will cause an exception due to the wrong argument
+ [1, 2].sample(2, "Not a Random instance")
+ end
+
+ # verify that the same seed causes the same results
+ ary = (1..10).to_a
+ srand(15)
+ samples1 = ary.sample(4)
+ samples2 = ary.sample(4, Random.new(15))
+ samples3 = ary.sample(4, Random.new(16))
+ assert_equal(samples1, samples2)
+ assert_not_equal(samples1, samples3)
end
diff --git a/mrbgems/mruby-range-ext/mrblib/range.rb b/mrbgems/mruby-range-ext/mrblib/range.rb
index e5d1fb079..a149a57dc 100644
--- a/mrbgems/mruby-range-ext/mrblib/range.rb
+++ b/mrbgems/mruby-range-ext/mrblib/range.rb
@@ -15,10 +15,7 @@ class Range
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1
nv = args[0]
- raise TypeError, "no implicit conversion from nil to integer" if nv.nil?
- raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless nv.respond_to?(:to_int)
- n = nv.to_int
- raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless n.kind_of?(Integer)
+ n = nv.__to_int
raise ArgumentError, "negative array size (or size too big)" unless 0 <= n
ary = []
each do |i|
@@ -28,4 +25,43 @@ class Range
end
ary
end
+
+ def max(&block)
+ val = self.first
+ last = self.last
+ return super if block
+
+ # fast path for numerics
+ if (val.kind_of?(Fixnum) || val.kind_of?(Float)) && (last.kind_of?(Fixnum) || last.kind_of?(Float))
+ raise TypeError if exclude_end? && !last.kind_of?(Fixnum)
+ return nil if val > last
+ return nil if val == last && exclude_end?
+
+ max = last
+ max -= 1 if exclude_end?
+ return max
+ end
+
+ # delegate to Enumerable
+ super
+ end
+
+ def min(&block)
+ val = self.first
+ last = self.last
+ return super if block
+
+ # fast path for numerics
+ if (val.kind_of?(Fixnum) || val.kind_of?(Float)) && (last.kind_of?(Fixnum) || last.kind_of?(Float))
+ raise TypeError if exclude_end? && !last.kind_of?(Fixnum)
+ return nil if val > last
+ return nil if val == last && exclude_end?
+
+ min = val
+ return min
+ end
+
+ # delegate to Enumerable
+ super
+ end
end
diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c
index aca71cc01..fb76fe0d8 100644
--- a/mrbgems/mruby-range-ext/src/range.c
+++ b/mrbgems/mruby-range-ext/src/range.c
@@ -1,3 +1,7 @@
+#ifdef MRB_WITHOUT_FLOAT
+# error Conflict 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
#include <mruby.h>
#include <mruby/range.h>
#include <math.h>
@@ -40,7 +44,7 @@ r_lt(mrb_state *mrb, mrb_value a, mrb_value b)
* ("a".."z").cover?("cc") #=> true
*/
static mrb_value
-mrb_range_cover(mrb_state *mrb, mrb_value range)
+range_cover(mrb_state *mrb, mrb_value range)
{
mrb_value val;
struct RRange *r = mrb_range_ptr(mrb, range);
@@ -48,11 +52,11 @@ mrb_range_cover(mrb_state *mrb, mrb_value range)
mrb_get_args(mrb, "o", &val);
- beg = r->edges->beg;
- end = r->edges->end;
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
if (r_le(mrb, beg, val)) {
- if (r->excl) {
+ if (RANGE_EXCL(r)) {
if (r_lt(mrb, val, end))
return mrb_true_value();
}
@@ -82,14 +86,13 @@ mrb_range_cover(mrb_state *mrb, mrb_value range)
* (10...20).last(3) #=> [17, 18, 19]
*/
static mrb_value
-mrb_range_last(mrb_state *mrb, mrb_value range)
+range_last(mrb_state *mrb, mrb_value range)
{
mrb_value num;
mrb_value array;
- struct RRange *r = mrb_range_ptr(mrb, range);
if (mrb_get_args(mrb, "|o", &num) == 0) {
- return r->edges->end;
+ return mrb_range_end(mrb, range);
}
array = mrb_funcall(mrb, range, "to_a", 0);
@@ -108,7 +111,7 @@ mrb_range_last(mrb_state *mrb, mrb_value range)
*/
static mrb_value
-mrb_range_size(mrb_state *mrb, mrb_value range)
+range_size(mrb_state *mrb, mrb_value range)
{
struct RRange *r = mrb_range_ptr(mrb, range);
mrb_value beg, end;
@@ -116,9 +119,9 @@ mrb_range_size(mrb_state *mrb, mrb_value range)
mrb_bool num_p = TRUE;
mrb_bool excl;
- beg = r->edges->beg;
- end = r->edges->end;
- excl = r->excl;
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
+ excl = RANGE_EXCL(r);
if (mrb_fixnum_p(beg)) {
beg_f = (mrb_float)mrb_fixnum(beg);
}
@@ -165,9 +168,9 @@ mrb_mruby_range_ext_gem_init(mrb_state* mrb)
{
struct RClass * s = mrb_class_get(mrb, "Range");
- mrb_define_method(mrb, s, "cover?", mrb_range_cover, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, s, "last", mrb_range_last, MRB_ARGS_OPT(1));
- mrb_define_method(mrb, s, "size", mrb_range_size, MRB_ARGS_NONE());
+ mrb_define_method(mrb, s, "cover?", range_cover, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "last", range_last, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, s, "size", range_size, MRB_ARGS_NONE());
}
void
diff --git a/mrbgems/mruby-range-ext/test/range.rb b/mrbgems/mruby-range-ext/test/range.rb
index efcbdabe4..b56d6b58e 100644
--- a/mrbgems/mruby-range-ext/test/range.rb
+++ b/mrbgems/mruby-range-ext/test/range.rb
@@ -30,3 +30,105 @@ assert('Range#size') do
assert_equal Float::INFINITY, (0..Float::INFINITY).size
assert_nil ('a'..'z').size
end
+
+assert('Range#max') do
+ # returns the maximum value in the range when called with no arguments
+ assert_equal 10, (1..10).max
+ assert_equal 9, (1...10).max
+ assert_equal 4294967295, (0...2**32).max
+
+ # returns the maximum value in the Float range when called with no arguments
+ assert_equal 908.1111, (303.20..908.1111).max
+
+ # raises TypeError when called on an exclusive range and a non Integer value
+ assert_raise(TypeError) { (303.20...908.1111).max }
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, (100..10).max
+
+ # returns nil when the endpoint equals the start point and the range is exclusive
+ assert_equal nil, (5...5).max
+
+ # returns the endpoint when the endpoint equals the start point and the range is inclusive
+ assert_equal 5, (5..5).max
+
+ # returns nil when the endpoint is less than the start point in a Float range
+ assert_equal nil, (3003.20..908.1111).max
+end
+
+assert('Range#max given a block') do
+ # passes each pair of values in the range to the block
+ acc = []
+ (1..10).max do |a, b|
+ acc << a
+ acc << b
+ a
+ end
+ (1..10).each do |value|
+ assert_true acc.include?(value)
+ end
+
+ # passes each pair of elements to the block in reversed order
+ acc = []
+ (1..5).max do |a, b|
+ acc << [a, b]
+ a
+ end
+ assert_equal [[2, 1], [3, 2], [4, 3], [5, 4]], acc
+
+ # returns the element the block determines to be the maximum
+ assert_equal 1, ((1..3).max { |_a, _b| -3 })
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, ((100..10).max { |x, y| x <=> y })
+ assert_equal nil, ((5...5).max { |x, y| x <=> y })
+end
+
+assert('Range#min') do
+ # returns the minimum value in the range when called with no arguments
+ assert_equal 1, (1..10).min
+ assert_equal 1, (1...10).min
+
+ # returns the minimum value in the Float range when called with no arguments
+ assert_equal 303.20, (303.20..908.1111).min
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, (100..10).min
+
+ # returns nil when the endpoint equals the start point and the range is exclusive
+ assert_equal nil, (5...5).max
+
+ # returns the endpoint when the endpoint equals the start point and the range is inclusive
+ assert_equal 5, (5..5).max
+
+ # returns nil when the start point is greater than the endpoint in a Float range
+ assert_equal nil, (3003.20..908.1111).max
+end
+
+assert('Range#min given a block') do
+ # passes each pair of values in the range to the block
+ acc = []
+ (1..10).min do |a, b|
+ acc << a
+ acc << b
+ a
+ end
+ (1..10).each do |value|
+ assert_true acc.include?(value)
+ end
+
+ # passes each pair of elements to the block in reversed order
+ acc = []
+ (1..5).min do |a, b|
+ acc << [a, b]
+ a
+ end
+ assert_equal [[2, 1], [3, 1], [4, 1], [5, 1]], acc
+
+ # returns the element the block determines to be the minimum
+ assert_equal 3, ((1..3).min { |_a, _b| -3 })
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, ((100..10).min { |x, y| x <=> y })
+ assert_equal nil, ((5...5).min { |x, y| x <=> y })
+end
diff --git a/mrbgems/mruby-rational/mrbgem.rake b/mrbgems/mruby-rational/mrbgem.rake
new file mode 100644
index 000000000..4b540dec4
--- /dev/null
+++ b/mrbgems/mruby-rational/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-rational') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'Rational class'
+end
diff --git a/mrbgems/mruby-rational/mrblib/rational.rb b/mrbgems/mruby-rational/mrblib/rational.rb
new file mode 100644
index 000000000..93af72b96
--- /dev/null
+++ b/mrbgems/mruby-rational/mrblib/rational.rb
@@ -0,0 +1,116 @@
+class Rational < Numeric
+ def inspect
+ "(#{to_s})"
+ end
+
+ def to_s
+ "#{numerator}/#{denominator}"
+ end
+
+ def *(rhs)
+ if rhs.is_a? Rational
+ Rational(numerator * rhs.numerator, denominator * rhs.denominator)
+ elsif rhs.is_a? Integer
+ Rational(numerator * rhs, denominator)
+ elsif rhs.is_a? Numeric
+ numerator * rhs / denominator
+ end
+ end
+
+ def +(rhs)
+ if rhs.is_a? Rational
+ Rational(numerator * rhs.denominator + rhs.numerator * denominator, denominator * rhs.denominator)
+ elsif rhs.is_a? Integer
+ Rational(numerator + rhs * denominator, denominator)
+ elsif rhs.is_a? Numeric
+ (numerator + rhs * denominator) / denominator
+ end
+ end
+
+ def -(rhs)
+ if rhs.is_a? Rational
+ Rational(numerator * rhs.denominator - rhs.numerator * denominator, denominator * rhs.denominator)
+ elsif rhs.is_a? Integer
+ Rational(numerator - rhs * denominator, denominator)
+ elsif rhs.is_a? Numeric
+ (numerator - rhs * denominator) / denominator
+ end
+ end
+
+ def /(rhs)
+ if rhs.is_a? Rational
+ Rational(numerator * rhs.denominator, denominator * rhs.numerator)
+ elsif rhs.is_a? Integer
+ Rational(numerator, denominator * rhs)
+ elsif rhs.is_a? Numeric
+ numerator / rhs / denominator
+ end
+ end
+
+ def <=>(rhs)
+ if rhs.is_a?(Integral)
+ return numerator <=> rhs if denominator == 1
+ rhs = Rational(rhs)
+ end
+
+ case rhs
+ when Rational
+ (numerator * rhs.denominator - denominator * rhs.numerator) <=> 0
+ when Numeric
+ (rhs <=> self)&.-@
+ else
+ nil
+ end
+ end
+
+ def ==(rhs)
+ return true if self.equal?(rhs)
+ if rhs.is_a?(Integral) && denominator == 1
+ return numerator == rhs
+ end
+ if rhs.is_a?(Rational)
+ numerator * rhs.denominator == denominator * rhs.numerator
+ else
+ rhs == self
+ end
+ end
+end
+
+class Numeric
+ def to_r
+ Rational(self, 1)
+ end
+end
+
+module Kernel
+ def Rational(numerator, denominator = 1)
+ a = numerator
+ b = denominator
+ a, b = b, a % b until b == 0
+ Rational._new(numerator.div(a), denominator.div(a))
+ end
+end
+
+[:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].each do |op|
+ Fixnum.instance_eval do
+ original_operator_name = "__original_operator_#{op}_rational"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Rational
+ Rational(self).__send__(op, rhs)
+ else
+ __send__(original_operator_name, rhs)
+ end
+ end
+ end
+ Float.instance_eval do
+ original_operator_name = "__original_operator_#{op}_rational"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Rational
+ rhs = rhs.to_f
+ end
+ __send__(original_operator_name, rhs)
+ end
+ end if Object.const_defined?(:Float)
+end
diff --git a/mrbgems/mruby-rational/src/rational.c b/mrbgems/mruby-rational/src/rational.c
new file mode 100644
index 000000000..31471e934
--- /dev/null
+++ b/mrbgems/mruby-rational/src/rational.c
@@ -0,0 +1,206 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/numeric.h>
+
+struct mrb_rational {
+ mrb_int numerator;
+ mrb_int denominator;
+};
+
+#if MRB_INT_MAX <= INTPTR_MAX
+
+#define RATIONAL_USE_ISTRUCT
+/* use TT_ISTRUCT */
+#include <mruby/istruct.h>
+
+#define rational_ptr(mrb, v) (struct mrb_rational*)mrb_istruct_ptr(v)
+
+static struct RBasic*
+rational_alloc(mrb_state *mrb, struct RClass *c, struct mrb_rational **p)
+{
+ struct RIStruct *s;
+
+ s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
+ *p = (struct mrb_rational*)s->inline_data;
+
+ return (struct RBasic*)s;
+}
+
+#else
+/* use TT_DATA */
+#include <mruby/data.h>
+
+static const struct mrb_data_type mrb_rational_type = {"Rational", mrb_free};
+
+static struct RBasic*
+rational_alloc(mrb_state *mrb, struct RClass *c, struct mrb_rational **p)
+{
+ struct RData *d;
+
+ Data_Make_Struct(mrb, c, struct mrb_rational, &mrb_rational_type, *p, d);
+
+ return (struct RBasic*)d;
+}
+
+static struct mrb_rational*
+rational_ptr(mrb_state *mrb, mrb_value v)
+{
+ struct mrb_rational *p;
+
+ p = DATA_GET_PTR(mrb, v, &mrb_rational_type, struct mrb_rational);
+ if (!p) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized rational");
+ }
+ return p;
+}
+#endif
+
+static mrb_value
+rational_numerator(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ return mrb_fixnum_value(p->numerator);
+}
+
+static mrb_value
+rational_denominator(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ return mrb_fixnum_value(p->denominator);
+}
+
+static mrb_value
+rational_new(mrb_state *mrb, mrb_int numerator, mrb_int denominator)
+{
+ struct RClass *c = mrb_class_get(mrb, "Rational");
+ struct mrb_rational *p;
+ struct RBasic *rat = rational_alloc(mrb, c, &p);
+ p->numerator = numerator;
+ p->denominator = denominator;
+ MRB_SET_FROZEN_FLAG(rat);
+ return mrb_obj_value(rat);
+}
+
+static mrb_value
+rational_s_new(mrb_state *mrb, mrb_value self)
+{
+ mrb_int numerator, denominator;
+
+#ifdef MRB_WITHOUT_FLOAT
+ mrb_get_args(mrb, "ii", &numerator, &denominator);
+#else
+
+#define DROP_PRECISION(cond, num, denom) \
+ do { \
+ while (cond) { \
+ num /= 2; \
+ denom /= 2; \
+ } \
+ } while (0)
+
+ mrb_value numv, denomv;
+
+ mrb_get_args(mrb, "oo", &numv, &denomv);
+ if (mrb_fixnum_p(numv)) {
+ numerator = mrb_fixnum(numv);
+
+ if (mrb_fixnum_p(denomv)) {
+ denominator = mrb_fixnum(denomv);
+ }
+ else {
+ mrb_float denomf = mrb_to_flo(mrb, denomv);
+
+ DROP_PRECISION(denomf < MRB_INT_MIN || denomf > MRB_INT_MAX, numerator, denomf);
+ denominator = denomf;
+ }
+ }
+ else {
+ mrb_float numf = mrb_to_flo(mrb, numv);
+
+ if (mrb_fixnum_p(denomv)) {
+ denominator = mrb_fixnum(denomv);
+ }
+ else {
+ mrb_float denomf = mrb_to_flo(mrb, denomv);
+
+ DROP_PRECISION(denomf < MRB_INT_MIN || denomf > MRB_INT_MAX, numf, denomf);
+ denominator = denomf;
+ }
+
+ DROP_PRECISION(numf < MRB_INT_MIN || numf > MRB_INT_MAX, numf, denominator);
+ numerator = numf;
+ }
+#endif
+
+ return rational_new(mrb, numerator, denominator);
+}
+
+#ifndef MRB_WITHOUT_FLOAT
+static mrb_value
+rational_to_f(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ mrb_float f = (mrb_float)p->numerator / (mrb_float)p->denominator;
+
+ return mrb_float_value(mrb, f);
+}
+#endif
+
+static mrb_value
+rational_to_i(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ if (p->denominator == 0) {
+ mrb_raise(mrb, mrb_exc_get(mrb, "StandardError"), "divided by 0");
+ }
+ return mrb_fixnum_value(p->numerator / p->denominator);
+}
+
+static mrb_value
+rational_to_r(mrb_state *mrb, mrb_value self)
+{
+ return self;
+}
+
+static mrb_value
+rational_negative_p(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(mrb, self);
+ if (p->numerator < 0) {
+ return mrb_true_value();
+ }
+ return mrb_false_value();
+}
+
+static mrb_value
+fix_to_r(mrb_state *mrb, mrb_value self)
+{
+ return rational_new(mrb, mrb_fixnum(self), 1);
+}
+
+void mrb_mruby_rational_gem_init(mrb_state *mrb)
+{
+ struct RClass *rat;
+
+#ifdef COMPLEX_USE_RATIONAL
+ mrb_assert(sizeof(struct mrb_rational) < ISTRUCT_DATA_SIZE);
+#endif
+ rat = mrb_define_class(mrb, "Rational", mrb_class_get(mrb, "Numeric"));
+ mrb_undef_class_method(mrb, rat, "new");
+ mrb_define_class_method(mrb, rat, "_new", rational_s_new, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, rat, "numerator", rational_numerator, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "denominator", rational_denominator, MRB_ARGS_NONE());
+#ifndef MRB_WITHOUT_FLOAT
+ mrb_define_method(mrb, rat, "to_f", rational_to_f, MRB_ARGS_NONE());
+#endif
+ mrb_define_method(mrb, rat, "to_i", rational_to_i, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "to_r", rational_to_r, MRB_ARGS_NONE());
+ mrb_define_method(mrb, rat, "negative?", rational_negative_p, MRB_ARGS_NONE());
+ mrb_define_method(mrb, mrb->fixnum_class, "to_r", fix_to_r, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_rational_gem_final(mrb_state* mrb)
+{
+}
diff --git a/mrbgems/mruby-rational/test/rational.rb b/mrbgems/mruby-rational/test/rational.rb
new file mode 100644
index 000000000..11737034b
--- /dev/null
+++ b/mrbgems/mruby-rational/test/rational.rb
@@ -0,0 +1,308 @@
+class UserDefinedNumeric < Numeric
+ def initialize(n)
+ @n = n
+ end
+
+ def <=>(rhs)
+ return nil unless rhs.respond_to?(:to_i)
+ rhs = rhs.to_i
+ rhs < 0 ? nil : @n <=> rhs
+ end
+
+ def inspect
+ "#{self.class}(#{@n})"
+ end
+end
+
+class ComplexLikeNumeric < UserDefinedNumeric
+ def ==(rhs)
+ @n == 0 && rhs == 0
+ end
+
+ undef <=>
+end
+
+def assert_rational(exp, real)
+ assert do
+ assert_float exp.numerator, real.numerator
+ assert_float exp.denominator, real.denominator
+ end
+end
+
+def assert_equal_rational(exp, o1, o2)
+ assert do
+ if exp
+ assert_operator(o1, :==, o2)
+ assert_not_operator(o1, :!=, o2)
+ else
+ assert_not_operator(o1, :==, o2)
+ assert_operator(o1, :!=, o2)
+ end
+ end
+end
+
+def assert_cmp(exp, o1, o2)
+ if exp == (o1 <=> o2)
+ pass
+ else
+ flunk "", " Expected #{o1.inspect} <=> #{o2.inspect} to be #{exp}."
+ end
+end
+
+assert 'Rational' do
+ r = 5r
+ assert_equal(Rational, r.class)
+ assert_equal([5, 1], [r.numerator, r.denominator])
+end
+
+assert 'Kernel#Rational' do
+ r = Rational(4,10)
+ assert_equal(2, r.numerator)
+ assert_equal(5, r.denominator)
+
+ r = Rational(3)
+ assert_equal(3, r.numerator)
+ assert_equal(1, r.denominator)
+
+ assert_raise(ArgumentError) { Rational() }
+ assert_raise(ArgumentError) { Rational(1,2,3) }
+end
+
+assert 'Rational#to_f' do
+ assert_float(2.0, Rational(2).to_f)
+ assert_float(2.25, Rational(9, 4).to_f)
+ assert_float(-0.75, Rational(-3, 4).to_f)
+ assert_float(6.666666666666667, Rational(20, 3).to_f)
+end
+
+assert 'Rational#to_i' do
+ assert_equal(0, Rational(2, 3).to_i)
+ assert_equal(3, Rational(3).to_i)
+ assert_equal(300, Rational(300.6).to_i)
+ assert_equal(1, Rational(98, 71).to_i)
+ assert_equal(-15, Rational(-30, 2).to_i)
+end
+
+assert 'Rational#*' do
+ assert_rational(Rational(4, 9), Rational(2, 3) * Rational(2, 3))
+ assert_rational(Rational(900, 1), Rational(900) * Rational(1))
+ assert_rational(Rational(1, 1), Rational(-2, 9) * Rational(-9, 2))
+ assert_rational(Rational(9, 2), Rational(9, 8) * 4)
+ assert_float( 21.77777777777778, Rational(20, 9) * 9.8)
+end
+
+assert 'Rational#+' do
+ assert_rational(Rational(4, 3), Rational(2, 3) + Rational(2, 3))
+ assert_rational(Rational(901, 1), Rational(900) + Rational(1))
+ assert_rational(Rational(-85, 18), Rational(-2, 9) + Rational(-9, 2))
+ assert_rational(Rational(41, 8), Rational(9, 8) + 4)
+ assert_float( 12.022222222222222, Rational(20, 9) + 9.8)
+end
+
+assert 'Rational#-' do
+ assert_rational(Rational(0, 1), Rational(2, 3) - Rational(2, 3))
+ assert_rational(Rational(899, 1), Rational(900) - Rational(1))
+ assert_rational(Rational(77, 18), Rational(-2, 9) - Rational(-9, 2))
+ assert_rational(Rational(-23, 8), Rational(9, 8) - 4)
+ assert_float( -7.577777777777778, Rational(20, 9) - 9.8)
+end
+
+assert 'Rational#/' do
+ assert_rational(Rational(1, 1), Rational(2, 3) / Rational(2, 3))
+ assert_rational(Rational(900, 1), Rational(900) / Rational(1))
+ assert_rational(Rational(4, 81), Rational(-2, 9) / Rational(-9, 2))
+ assert_rational(Rational(9, 32), Rational(9, 8) / 4)
+ assert_float( 0.22675736961451246, Rational(20, 9) / 9.8)
+end
+
+assert 'Rational#==, Rational#!=' do
+ assert_equal_rational(true, Rational(1,1), Rational(1))
+ assert_equal_rational(true, Rational(-1,1), -1r)
+ assert_equal_rational(true, Rational(13,4), 3.25)
+ assert_equal_rational(true, Rational(13,3.25), Rational(4,1))
+ assert_equal_rational(true, Rational(-3,-4), Rational(3,4))
+ assert_equal_rational(true, Rational(-4,5), Rational(4,-5))
+ assert_equal_rational(true, Rational(4,2), 2)
+ assert_equal_rational(true, Rational(-4,2), -2)
+ assert_equal_rational(true, Rational(4,-2), -2)
+ assert_equal_rational(true, Rational(4,2), 2.0)
+ assert_equal_rational(true, Rational(-4,2), -2.0)
+ assert_equal_rational(true, Rational(4,-2), -2.0)
+ assert_equal_rational(true, Rational(8,6), Rational(4,3))
+ assert_equal_rational(false, Rational(13,4), 3)
+ assert_equal_rational(false, Rational(13,4), 3.3)
+ assert_equal_rational(false, Rational(2,1), 1r)
+ assert_equal_rational(false, Rational(1), nil)
+ assert_equal_rational(false, Rational(1), '')
+ assert_equal_rational(true, 0r, UserDefinedNumeric.new(0))
+ assert_equal_rational(true, 1r, UserDefinedNumeric.new(1))
+ assert_equal_rational(false, 1r, UserDefinedNumeric.new(2))
+ assert_equal_rational(false, -1r, UserDefinedNumeric.new(-1))
+ assert_equal_rational(true, 0r, ComplexLikeNumeric.new(0))
+ assert_equal_rational(false, 1r, ComplexLikeNumeric.new(1))
+ assert_equal_rational(false, 1r, ComplexLikeNumeric.new(2))
+end
+
+assert 'Fixnum#==(Rational), Fixnum#!=(Rational)' do
+ assert_equal_rational(true, 2, Rational(4,2))
+ assert_equal_rational(true, -2, Rational(-4,2))
+ assert_equal_rational(true, -2, Rational(4,-2))
+ assert_equal_rational(false, 3, Rational(13,4))
+end
+
+assert 'Float#==(Rational), Float#!=(Rational)' do
+ assert_equal_rational(true, 2.0, Rational(4,2))
+ assert_equal_rational(true, -2.0, Rational(-4,2))
+ assert_equal_rational(true, -2.0, Rational(4,-2))
+ assert_equal_rational(false, 3.3, Rational(13,4))
+end
+
+assert 'Rational#<=>' do
+ assert_cmp(-1, Rational(-1), Rational(0))
+ assert_cmp(0, Rational(0), Rational(0))
+ assert_cmp(1, Rational(1), Rational(0))
+ assert_cmp(-1, Rational(-1), 0)
+ assert_cmp(0, Rational(0), 0)
+ assert_cmp(1, Rational(1), 0)
+ assert_cmp(-1, Rational(-1), 0.0)
+ assert_cmp(0, Rational(0), 0.0)
+ assert_cmp(1, Rational(1), 0.0)
+ assert_cmp(-1, Rational(1,2), Rational(2,3))
+ assert_cmp(0, Rational(2,3), Rational(2,3))
+ assert_cmp(1, Rational(2,3), Rational(1,2))
+ assert_cmp(1, Rational(2,3), Rational(1,2))
+ assert_cmp(1, Rational(0), Rational(-1))
+ assert_cmp(-1, Rational(0), Rational(1))
+ assert_cmp(1, Rational(2,3), Rational(1,2))
+ assert_cmp(0, Rational(2,3), Rational(2,3))
+ assert_cmp(-1, Rational(1,2), Rational(2,3))
+ assert_cmp(-1, Rational(1,2), Rational(2,3))
+ assert_cmp(nil, 3r, "3")
+ assert_cmp(1, 3r, UserDefinedNumeric.new(2))
+ assert_cmp(0, 3r, UserDefinedNumeric.new(3))
+ assert_cmp(-1, 3r, UserDefinedNumeric.new(4))
+ assert_cmp(nil, Rational(-3), UserDefinedNumeric.new(5))
+ assert_raise(NoMethodError) { 0r <=> ComplexLikeNumeric.new(0) }
+ assert_raise(NoMethodError) { 1r <=> ComplexLikeNumeric.new(2) }
+end
+
+assert 'Fixnum#<=>(Rational)' do
+ assert_cmp(-1, -2, Rational(-9,5))
+ assert_cmp(0, 5, 5r)
+ assert_cmp(1, 3, Rational(8,3))
+end
+
+assert 'Float#<=>(Rational)' do
+ assert_cmp(-1, -2.1, Rational(-9,5))
+ assert_cmp(0, 5.0, 5r)
+ assert_cmp(1, 2.7, Rational(8,3))
+end
+
+assert 'Rational#<' do
+ assert_operator(Rational(1,2), :<, Rational(2,3))
+ assert_not_operator(Rational(2,3), :<, Rational(2,3))
+ assert_operator(Rational(2,3), :<, 1)
+ assert_not_operator(2r, :<, 2)
+ assert_not_operator(Rational(2,3), :<, -3)
+ assert_operator(Rational(-4,3), :<, -0.3)
+ assert_not_operator(Rational(13,4), :<, 3.25)
+ assert_not_operator(Rational(2,3), :<, 0.6)
+ assert_raise(ArgumentError) { 1r < "2" }
+end
+
+assert 'Fixnum#<(Rational)' do
+ assert_not_operator(1, :<, Rational(2,3))
+ assert_not_operator(2, :<, 2r)
+ assert_operator(-3, :<, Rational(2,3))
+end
+
+assert 'Float#<(Rational)' do
+ assert_not_operator(-0.3, :<, Rational(-4,3))
+ assert_not_operator(3.25, :<, Rational(13,4))
+ assert_operator(0.6, :<, Rational(2,3))
+end
+
+assert 'Rational#<=' do
+ assert_operator(Rational(1,2), :<=, Rational(2,3))
+ assert_operator(Rational(2,3), :<=, Rational(2,3))
+ assert_operator(Rational(2,3), :<=, 1)
+ assert_operator(2r, :<=, 2)
+ assert_not_operator(Rational(2,3), :<=, -3)
+ assert_operator(Rational(-4,3), :<=, -0.3)
+ assert_operator(Rational(13,4), :<=, 3.25)
+ assert_not_operator(Rational(2,3), :<=, 0.6)
+ assert_raise(ArgumentError) { 1r <= "2" }
+end
+
+assert 'Fixnum#<=(Rational)' do
+ assert_not_operator(1, :<=, Rational(2,3))
+ assert_operator(2, :<=, 2r)
+ assert_operator(-3, :<=, Rational(2,3))
+end
+
+assert 'Float#<=(Rational)' do
+ assert_not_operator(-0.3, :<=, Rational(-4,3))
+ assert_operator(3.25, :<=, Rational(13,4))
+ assert_operator(0.6, :<=, Rational(2,3))
+end
+
+assert 'Rational#>' do
+ assert_not_operator(Rational(1,2), :>, Rational(2,3))
+ assert_not_operator(Rational(2,3), :>, Rational(2,3))
+ assert_not_operator(Rational(2,3), :>, 1)
+ assert_not_operator(2r, :>, 2)
+ assert_operator(Rational(2,3), :>, -3)
+ assert_not_operator(Rational(-4,3), :>, -0.3)
+ assert_not_operator(Rational(13,4), :>, 3.25)
+ assert_operator(Rational(2,3), :>, 0.6)
+ assert_raise(ArgumentError) { 1r > "2" }
+end
+
+assert 'Fixnum#>(Rational)' do
+ assert_operator(1, :>, Rational(2,3))
+ assert_not_operator(2, :>, 2r)
+ assert_not_operator(-3, :>, Rational(2,3))
+end
+
+assert 'Float#>(Rational)' do
+ assert_operator(-0.3, :>, Rational(-4,3))
+ assert_not_operator(3.25, :>, Rational(13,4))
+ assert_not_operator(0.6, :>, Rational(2,3))
+end
+
+assert 'Rational#>=' do
+ assert_not_operator(Rational(1,2), :>=, Rational(2,3))
+ assert_operator(Rational(2,3), :>=, Rational(2,3))
+ assert_not_operator(Rational(2,3), :>=, 1)
+ assert_operator(2r, :>=, 2)
+ assert_operator(Rational(2,3), :>=, -3)
+ assert_not_operator(Rational(-4,3), :>=, -0.3)
+ assert_operator(Rational(13,4), :>=, 3.25)
+ assert_operator(Rational(2,3), :>=, 0.6)
+ assert_raise(ArgumentError) { 1r >= "2" }
+end
+
+assert 'Fixnum#>=(Rational)' do
+ assert_operator(1, :>=, Rational(2,3))
+ assert_operator(2, :>=, 2r)
+ assert_not_operator(-3, :>=, Rational(2,3))
+end
+
+assert 'Float#>=(Rational)' do
+ assert_operator(-0.3, :>=, Rational(-4,3))
+ assert_operator(3.25, :>=, Rational(13,4))
+ assert_not_operator(0.6, :>=, Rational(2,3))
+end
+
+assert 'Rational#negative?' do
+ assert_predicate(Rational(-2,3), :negative?)
+ assert_predicate(Rational(2,-3), :negative?)
+ assert_not_predicate(Rational(2,3), :negative?)
+ assert_not_predicate(Rational(0), :negative?)
+end
+
+assert 'Rational#frozen?' do
+ assert_predicate(1r, :frozen?)
+ assert_predicate(Rational(2,3), :frozen?)
+ assert_predicate(4/5r, :frozen?)
+end
diff --git a/mrbgems/mruby-sleep/.gitignore b/mrbgems/mruby-sleep/.gitignore
new file mode 100644
index 000000000..b9f9e71df
--- /dev/null
+++ b/mrbgems/mruby-sleep/.gitignore
@@ -0,0 +1,4 @@
+/mruby
+
+*.so
+*.a \ No newline at end of file
diff --git a/mrbgems/mruby-sleep/.travis.yml b/mrbgems/mruby-sleep/.travis.yml
new file mode 100644
index 000000000..ad6b007b6
--- /dev/null
+++ b/mrbgems/mruby-sleep/.travis.yml
@@ -0,0 +1,29 @@
+dist: trusty
+
+language: c
+compiler:
+ - gcc
+ - clang
+env:
+ - MRUBY_VERSION=1.2.0
+ - MRUBY_VERSION=master
+matrix:
+ allow_failures:
+ - env: MRUBY_VERSION=master
+branches:
+ only:
+ - master
+addons:
+ apt:
+ packages:
+ - rake
+ - bison
+ - git
+ - gperf
+ # - aclocal
+ # - automake
+ # - autoconf
+ # - autotools-dev
+
+script:
+ - rake test \ No newline at end of file
diff --git a/mrbgems/mruby-sleep/.travis_build_config.rb b/mrbgems/mruby-sleep/.travis_build_config.rb
new file mode 100644
index 000000000..b32e38a9d
--- /dev/null
+++ b/mrbgems/mruby-sleep/.travis_build_config.rb
@@ -0,0 +1,6 @@
+MRuby::Build.new do |conf|
+ toolchain :gcc
+ conf.gembox 'default'
+ conf.gem '../mruby-sleep'
+ conf.enable_test
+end
diff --git a/mrbgems/mruby-sleep/README.md b/mrbgems/mruby-sleep/README.md
new file mode 100644
index 000000000..7707cd040
--- /dev/null
+++ b/mrbgems/mruby-sleep/README.md
@@ -0,0 +1,27 @@
+# Sleep Module for mruby
+mruby sleep module
+
+## install by mrbgems
+ - add conf.gem line to `build_config.rb`
+```ruby
+MRuby::Build.new do |conf|
+
+ # ... (snip) ...
+
+ conf.gem :core => 'mruby-sleep'
+end
+```
+
+## example
+
+```ruby
+sleep(10)
+usleep(10000)
+```
+
+# License
+under the MIT License:
+
+* http://www.opensource.org/licenses/mit-license.php
+
+
diff --git a/mrbgems/mruby-sleep/Rakefile b/mrbgems/mruby-sleep/Rakefile
new file mode 100644
index 000000000..5e3c46173
--- /dev/null
+++ b/mrbgems/mruby-sleep/Rakefile
@@ -0,0 +1,29 @@
+MRUBY_CONFIG=File.expand_path(ENV["MRUBY_CONFIG"] || ".travis_build_config.rb")
+MRUBY_VERSION=ENV["MRUBY_VERSION"] || "1.2.0"
+
+file :mruby do
+ cmd = "git clone --depth=1 git://github.com/mruby/mruby.git"
+ if MRUBY_VERSION != 'master'
+ cmd << " && cd mruby"
+ cmd << " && git fetch --tags && git checkout $(git rev-parse #{MRUBY_VERSION})"
+ end
+ sh cmd
+end
+
+desc "compile binary"
+task :compile => :mruby do
+ sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all"
+end
+
+desc "test"
+task :test => :mruby do
+ sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all test"
+end
+
+desc "cleanup"
+task :clean do
+ exit 0 unless File.directory?('mruby')
+ sh "cd mruby && rake deep_clean"
+end
+
+task :default => :compile
diff --git a/mrbgems/mruby-sleep/example/sleep.rb b/mrbgems/mruby-sleep/example/sleep.rb
new file mode 100644
index 000000000..e5acea3b2
--- /dev/null
+++ b/mrbgems/mruby-sleep/example/sleep.rb
@@ -0,0 +1,3 @@
+sleep(10)
+usleep(10000)
+
diff --git a/mrbgems/mruby-sleep/mrbgem.rake b/mrbgems/mruby-sleep/mrbgem.rake
new file mode 100644
index 000000000..8827b3580
--- /dev/null
+++ b/mrbgems/mruby-sleep/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-sleep') do |spec|
+ spec.license = 'MIT'
+ spec.authors = 'MATSUMOTO Ryosuke'
+ spec.version = '0.0.1'
+end
diff --git a/mrbgems/mruby-sleep/src/mrb_sleep.c b/mrbgems/mruby-sleep/src/mrb_sleep.c
new file mode 100644
index 000000000..3f8ef90cf
--- /dev/null
+++ b/mrbgems/mruby-sleep/src/mrb_sleep.c
@@ -0,0 +1,135 @@
+/*
+** mrb_sleep - sleep methods for mruby
+**
+** Copyright (c) mod_mruby developers 2012-
+**
+** 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.
+**
+** [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
+*/
+
+#include <time.h>
+#ifdef _WIN32
+ #include <windows.h>
+ #define sleep(x) Sleep(x * 1000)
+ #define usleep(x) Sleep((DWORD)((x)<1000) ? 1 : ((x)/1000))
+#else
+ #include <unistd.h>
+ #include <sys/time.h>
+#endif
+
+#include "mruby.h"
+
+/* not implemented forever sleep (called without an argument)*/
+static mrb_value
+mrb_f_sleep(mrb_state *mrb, mrb_value self)
+{
+ time_t beg = time(0);
+ time_t end;
+#ifndef MRB_WITHOUT_FLOAT
+ mrb_float sec;
+
+ mrb_get_args(mrb, "f", &sec);
+ if (sec >= 0) {
+ usleep(sec * 1000000);
+ }
+ else {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative");
+ }
+#else
+ mrb_int sec;
+
+ mrb_get_args(mrb, "i", &sec);
+ if (sec >= 0) {
+ sleep(sec);
+ } else {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative");
+ }
+#endif
+ end = time(0) - beg;
+
+ return mrb_fixnum_value(end);
+}
+
+/* mruby special; needed for mruby without float numbers */
+static mrb_value
+mrb_f_usleep(mrb_state *mrb, mrb_value self)
+{
+ mrb_int usec;
+#ifdef _WIN32
+ FILETIME st_ft,ed_ft;
+ unsigned __int64 st_time = 0;
+ unsigned __int64 ed_time = 0;
+#else
+ struct timeval st_tm,ed_tm;
+#endif
+ time_t slp_tm;
+
+#ifdef _WIN32
+ GetSystemTimeAsFileTime(&st_ft);
+#else
+ gettimeofday(&st_tm, NULL);
+#endif
+
+ /* not implemented forever sleep (called without an argument)*/
+ mrb_get_args(mrb, "i", &usec);
+
+ if (usec >= 0) {
+ usleep(usec);
+ } else {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "time interval must not be negative integer");
+ }
+
+#ifdef _WIN32
+ GetSystemTimeAsFileTime(&ed_ft);
+
+ st_time |= st_ft.dwHighDateTime;
+ st_time <<=32;
+ st_time |= st_ft.dwLowDateTime;
+ ed_time |= ed_ft.dwHighDateTime;
+ ed_time <<=32;
+ ed_time |= ed_ft.dwLowDateTime;
+
+ slp_tm = (ed_time - st_time) / 10;
+#else
+ gettimeofday(&ed_tm, NULL);
+
+ if (st_tm.tv_usec > ed_tm.tv_usec) {
+ slp_tm = 1000000 + ed_tm.tv_usec - st_tm.tv_usec;
+ }
+ else {
+ slp_tm = ed_tm.tv_usec - st_tm.tv_usec;
+ }
+#endif
+
+ return mrb_fixnum_value(slp_tm);
+}
+
+void
+mrb_mruby_sleep_gem_init(mrb_state *mrb)
+{
+ mrb_define_method(mrb, mrb->kernel_module, "sleep", mrb_f_sleep, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mrb->kernel_module, "usleep", mrb_f_usleep, MRB_ARGS_REQ(1));
+}
+
+void
+mrb_mruby_sleep_gem_final(mrb_state *mrb)
+{
+}
diff --git a/mrbgems/mruby-sleep/test/sleep_test.rb b/mrbgems/mruby-sleep/test/sleep_test.rb
new file mode 100644
index 000000000..f05b7a30b
--- /dev/null
+++ b/mrbgems/mruby-sleep/test/sleep_test.rb
@@ -0,0 +1,29 @@
+assert("sleep works") do
+ assert_nothing_raised { sleep(1) }
+ assert_nothing_raised { sleep(0) }
+end
+
+assert("sleep would accept non-negative float value") do
+ skip unless Object.const_defined?(:Float)
+ assert_nothing_raised { sleep(0.01) }
+ assert_nothing_raised { sleep(0.0) }
+ assert_nothing_raised { sleep(-0.0) }
+end
+
+assert("sleep would not accept negative integer value") do
+ assert_raise(ArgumentError) { sleep(-1) }
+end
+
+assert("sleep would not accept negative float value") do
+ skip unless Object.const_defined?(:Float)
+ assert_raise(ArgumentError) { sleep(-0.1) }
+end
+
+assert("usleep works") do
+ assert_nothing_raised { usleep(100) }
+ assert_nothing_raised { usleep(0) }
+end
+
+assert("usleep would not accept negative value") do
+ assert_raise(ArgumentError) { usleep(-100) }
+end
diff --git a/mrbgems/mruby-socket/mrbgem.rake b/mrbgems/mruby-socket/mrbgem.rake
index 8096815eb..b0894e095 100644
--- a/mrbgems/mruby-socket/mrbgem.rake
+++ b/mrbgems/mruby-socket/mrbgem.rake
@@ -4,6 +4,7 @@ MRuby::Gem::Specification.new('mruby-socket') do |spec|
spec.summary = 'standard socket class'
spec.cc.include_paths << "#{build.root}/src"
+ #spec.cc.defines << "HAVE_SA_LEN=0"
# If Windows, use winsock
if ( /mswin|mingw|win32/ =~ RUBY_PLATFORM ) then
diff --git a/mrbgems/mruby-socket/src/socket.c b/mrbgems/mruby-socket/src/socket.c
index 4d56d5374..9b06274dc 100644
--- a/mrbgems/mruby-socket/src/socket.c
+++ b/mrbgems/mruby-socket/src/socket.c
@@ -10,6 +10,7 @@
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
+ #include <winerror.h>
#define SHUT_RDWR SD_BOTH
#ifndef _SSIZE_T_DEFINED
@@ -19,6 +20,7 @@
#else
#include <sys/types.h>
#include <sys/socket.h>
+ #include <sys/param.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@@ -36,12 +38,21 @@
#include "mruby/array.h"
#include "mruby/class.h"
#include "mruby/data.h"
+#include "mruby/numeric.h"
#include "mruby/string.h"
#include "mruby/variable.h"
-#include "error.h"
+#include "mruby/error.h"
#include "mruby/ext/io.h"
+#if !defined(HAVE_SA_LEN)
+#if (defined(BSD) && (BSD >= 199006))
+#define HAVE_SA_LEN 1
+#else
+#define HAVE_SA_LEN 0
+#endif
+#endif
+
#define E_SOCKET_ERROR (mrb_class_get(mrb, "SocketError"))
#if !defined(mrb_cptr)
@@ -51,7 +62,7 @@
#endif
#ifdef _WIN32
-const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
+static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
{
if (af == AF_INET)
{
@@ -76,7 +87,7 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt)
return NULL;
}
-int inet_pton(int af, const char *src, void *dst)
+static int inet_pton(int af, const char *src, void *dst)
{
struct addrinfo hints, *res, *ressave;
@@ -120,7 +131,7 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "oo|oooi", &nodename, &service, &family, &socktype, &protocol, &flags);
if (mrb_string_p(nodename)) {
- hostname = mrb_str_to_cstr(mrb, nodename);
+ hostname = mrb_string_value_cstr(mrb, &nodename);
} else if (mrb_nil_p(nodename)) {
hostname = NULL;
} else {
@@ -128,9 +139,9 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)
}
if (mrb_string_p(service)) {
- servname = mrb_str_to_cstr(mrb, service);
+ servname = mrb_string_value_cstr(mrb, &service);
} else if (mrb_fixnum_p(service)) {
- servname = mrb_str_to_cstr(mrb, mrb_funcall(mrb, service, "to_s", 0));
+ servname = RSTRING_PTR(mrb_fixnum_to_str(mrb, service, 10));
} else if (mrb_nil_p(service)) {
servname = NULL;
} else {
@@ -194,8 +205,8 @@ mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self)
mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr");
}
error = getnameinfo((struct sockaddr *)RSTRING_PTR(sastr), (socklen_t)RSTRING_LEN(sastr), RSTRING_PTR(host), NI_MAXHOST, RSTRING_PTR(serv), NI_MAXSERV, (int)flags);
- if (error != 0) {
- mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %s", gai_strerror(error));
+ if (error) {
+ mrb_raisef(mrb, E_SOCKET_ERROR, "getnameinfo: %S", mrb_str_new_cstr(mrb, gai_strerror(error)));
}
ary = mrb_ary_new_capa(mrb, 2);
mrb_str_resize(mrb, host, strlen(RSTRING_PTR(host)));
@@ -214,7 +225,11 @@ mrb_addrinfo_unix_path(mrb_state *mrb, mrb_value self)
sastr = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "@sockaddr"));
if (((struct sockaddr *)RSTRING_PTR(sastr))->sa_family != AF_UNIX)
mrb_raise(mrb, E_SOCKET_ERROR, "need AF_UNIX address");
- return mrb_str_new_cstr(mrb, ((struct sockaddr_un *)RSTRING_PTR(sastr))->sun_path);
+ if (RSTRING_LEN(sastr) < (mrb_int)offsetof(struct sockaddr_un, sun_path) + 1) {
+ return mrb_str_new(mrb, "", 0);
+ } else {
+ return mrb_str_new_cstr(mrb, ((struct sockaddr_un *)RSTRING_PTR(sastr))->sun_path);
+ }
}
#endif
@@ -287,7 +302,7 @@ mrb_basicsocket_getpeereid(mrb_state *mrb, mrb_value self)
mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)egid));
return ary;
#else
- mrb_raise(mrb, E_RUNTIME_ERROR, "getpeereid is not avaialble on this system");
+ mrb_raise(mrb, E_RUNTIME_ERROR, "getpeereid is not available on this system");
return mrb_nil_value();
#endif
}
@@ -456,12 +471,12 @@ mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
}
} else if (argc == 1) {
if (strcmp(mrb_obj_classname(mrb, so), "Socket::Option") != 0)
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option");
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option");
level = mrb_fixnum(mrb_funcall(mrb, so, "level", 0));
optname = mrb_fixnum(mrb_funcall(mrb, so, "optname", 0));
optval = mrb_funcall(mrb, so, "data", 0);
} else {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%d for 3)", argc);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 3)", mrb_fixnum_value(argc));
}
s = socket_fd(mrb, self);
@@ -664,19 +679,15 @@ mrb_socket_listen(mrb_state *mrb, mrb_value klass)
static mrb_value
mrb_socket_sockaddr_family(mrb_state *mrb, mrb_value klass)
{
- mrb_value sa;
+ const struct sockaddr *sa;
+ mrb_value str;
- mrb_get_args(mrb, "S", &sa);
-#ifdef __linux__
- if ((size_t)RSTRING_LEN(sa) < offsetof(struct sockaddr, sa_family) + sizeof(sa_family_t)) {
- mrb_raisef(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
- }
-#else
- if ((size_t)RSTRING_LEN(sa) < sizeof(struct sockaddr)) {
- mrb_raisef(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
+ mrb_get_args(mrb, "S", &str);
+ if ((size_t)RSTRING_LEN(str) < offsetof(struct sockaddr, sa_family) + sizeof(sa->sa_family)) {
+ mrb_raise(mrb, E_SOCKET_ERROR, "invalid sockaddr (too short)");
}
-#endif
- return mrb_fixnum_value(((struct sockaddr *)RSTRING_PTR(sa))->sa_family);
+ sa = (const struct sockaddr *)RSTRING_PTR(str);
+ return mrb_fixnum_value(sa->sa_family);
}
static mrb_value
@@ -691,10 +702,13 @@ mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass)
mrb_get_args(mrb, "S", &path);
if ((size_t)RSTRING_LEN(path) > sizeof(sunp->sun_path) - 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %ubytes)", (unsigned int)sizeof(sunp->sun_path) - 1);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "too long unix socket path (max: %S bytes)", mrb_fixnum_value(sizeof(sunp->sun_path) - 1));
}
s = mrb_str_buf_new(mrb, sizeof(struct sockaddr_un));
sunp = (struct sockaddr_un *)RSTRING_PTR(s);
+#if HAVE_SA_LEN
+ sunp->sun_len = sizeof(struct sockaddr_un);
+#endif
sunp->sun_family = AF_UNIX;
memcpy(sunp->sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
sunp->sun_path[RSTRING_LEN(path)] = '\0';
diff --git a/mrbgems/mruby-socket/test/addrinfo.rb b/mrbgems/mruby-socket/test/addrinfo.rb
index 3ae46cd7b..491656179 100644
--- a/mrbgems/mruby-socket/test/addrinfo.rb
+++ b/mrbgems/mruby-socket/test/addrinfo.rb
@@ -7,7 +7,7 @@ assert('super class of Addrinfo') do
end
assert('Addrinfo.getaddrinfo') do
- ary = Addrinfo.getaddrinfo("localhost", "domain", Socket::AF_INET, Socket::SOCK_STREAM)
+ ary = Addrinfo.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_STREAM)
assert_true(ary.size >= 1)
ai = ary[0]
assert_equal(ai.afamily, Socket::AF_INET)
@@ -19,9 +19,9 @@ end
assert('Addrinfo.foreach') do
# assume Addrinfo.getaddrinfo works well
- a = Addrinfo.getaddrinfo("localhost", "domain")
+ a = Addrinfo.getaddrinfo("localhost", 80)
b = []
- Addrinfo.foreach("localhost", "domain") { |ai| b << ai }
+ Addrinfo.foreach("localhost", 80) { |ai| b << ai }
assert_equal(a.size, b.size)
end
@@ -35,7 +35,7 @@ assert('Addrinfo.ip') do
end
assert('Addrinfo.tcp') do
- ai = Addrinfo.tcp('127.0.0.1', 'smtp')
+ ai = Addrinfo.tcp('127.0.0.1', 25)
assert_equal('127.0.0.1', ai.ip_address)
assert_equal(Socket::AF_INET, ai.afamily)
assert_equal(25, ai.ip_port)
@@ -44,7 +44,7 @@ assert('Addrinfo.tcp') do
end
assert('Addrinfo.udp') do
- ai = Addrinfo.udp('127.0.0.1', 'domain')
+ ai = Addrinfo.udp('127.0.0.1', 53)
assert_equal('127.0.0.1', ai.ip_address)
assert_equal(Socket::AF_INET, ai.afamily)
assert_equal(53, ai.ip_port)
diff --git a/mrbgems/mruby-socket/test/socket.rb b/mrbgems/mruby-socket/test/socket.rb
index aa893588f..b64a67919 100644
--- a/mrbgems/mruby-socket/test/socket.rb
+++ b/mrbgems/mruby-socket/test/socket.rb
@@ -5,7 +5,7 @@ assert('Socket.gethostname') do
end
assert('Socket::getaddrinfo') do
- ret = Socket.getaddrinfo("localhost", "domain", Socket::AF_INET, Socket::SOCK_DGRAM)
+ ret = Socket.getaddrinfo("localhost", 53, Socket::AF_INET, Socket::SOCK_DGRAM)
assert_true ret.size >= 1
a = ret[0]
assert_equal "AF_INET", a[0]
@@ -28,7 +28,7 @@ assert('Socket#recvfrom') do
rstr, ai = s.recvfrom sstr.size
assert_equal sstr, rstr
- assert_true "127.0.0.1", ai.ip_address
+ assert_equal "127.0.0.1", ai.ip_address
ensure
s.close rescue nil
c.close rescue nil
diff --git a/mrbgems/mruby-socket/test/sockettest.c b/mrbgems/mruby-socket/test/sockettest.c
index 086bc4892..3017c7cc1 100644
--- a/mrbgems/mruby-socket/test/sockettest.c
+++ b/mrbgems/mruby-socket/test/sockettest.c
@@ -12,6 +12,7 @@
#include <fcntl.h>
#include <sys/stat.h>
+#define open _open
#define close _close
#define unlink _unlink
diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c
index 7eea1a1f3..985ffe276 100644
--- a/mrbgems/mruby-sprintf/src/sprintf.c
+++ b/mrbgems/mruby-sprintf/src/sprintf.c
@@ -119,13 +119,11 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base)
#define FPREC0 128
#define CHECK(l) do {\
-/* int cr = ENC_CODERANGE(result);*/\
while ((l) >= bsiz - blen) {\
+ if (bsiz > MRB_INT_MAX/2) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \
bsiz*=2;\
- if (bsiz < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \
}\
mrb_str_resize(mrb, result, bsiz);\
-/* ENC_CODERANGE_SET(result, cr);*/\
buf = RSTRING_PTR(result);\
} while (0)
@@ -202,11 +200,10 @@ check_name_arg(mrb_state *mrb, int posarg, const char *name, mrb_int len)
#define GETNUM(n, val) \
for (; p < end && ISDIGIT(*p); p++) {\
- mrb_int next_n = 10 * n + (*p - '0'); \
- if (next_n / 10 != n) {\
+ if (n > (MRB_INT_MAX - (*p - '0'))/10) {\
mrb_raise(mrb, E_ARGUMENT_ERROR, #val " too big"); \
} \
- n = next_n; \
+ n = 10 * n + (*p - '0'); \
} \
if (p >= end) { \
mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed format string - %*[0-9]"); \
@@ -236,7 +233,7 @@ get_hash(mrb_state *mrb, mrb_value *hash, mrb_int argc, const mrb_value *argv)
if (argc != 2) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
}
- tmp = mrb_check_convert_type(mrb, argv[1], MRB_TT_HASH, "Hash", "to_hash");
+ tmp = mrb_check_hash_type(mrb, argv[1]);
if (mrb_nil_p(tmp)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "one hash required");
}
@@ -555,7 +552,7 @@ mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fm
++argc;
--argv;
- fmt = mrb_str_to_str(mrb, fmt);
+ mrb_to_str(mrb, fmt);
p = RSTRING_PTR(fmt);
end = p + RSTRING_LEN(fmt);
blen = 0;
@@ -1059,18 +1056,22 @@ retry:
if (i > 0)
need = BIT_DIGITS(i);
}
+ if (need > MRB_INT_MAX - ((flags&FPREC) ? prec : 6)) {
+ too_big_width:
+ mrb_raise(mrb, E_ARGUMENT_ERROR,
+ (width > prec ? "width too big" : "prec too big"));
+ }
need += (flags&FPREC) ? prec : 6;
if ((flags&FWIDTH) && need < width)
need = width;
- need += 20;
- if (need <= 0) {
- mrb_raise(mrb, E_ARGUMENT_ERROR,
- (width > prec ? "width too big" : "prec too big"));
+ if (need > MRB_INT_MAX - 20) {
+ goto too_big_width;
}
+ need += 20;
CHECK(need);
n = snprintf(&buf[blen], need, fbuf, fval);
- if (n < 0) {
+ if (n < 0 || n >= need) {
mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error");
}
blen += n;
diff --git a/mrbgems/mruby-sprintf/test/sprintf.rb b/mrbgems/mruby-sprintf/test/sprintf.rb
index a5fd4e638..137812ae7 100644
--- a/mrbgems/mruby-sprintf/test/sprintf.rb
+++ b/mrbgems/mruby-sprintf/test/sprintf.rb
@@ -4,12 +4,14 @@
assert('String#%') do
assert_equal "one=1", "one=%d" % 1
assert_equal "1 one", "%d %s" % [ 1, "one" ]
- assert_equal "1.0", "%3.1f" % 1.01 if class_defined?("Float")
assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" }
assert_equal 15, ("%b" % (1<<14)).size
+ skip unless Object.const_defined?(:Float)
+ assert_equal "1.0", "%3.1f" % 1.01
end
assert('String#% with inf') do
+ skip unless Object.const_defined?(:Float)
inf = Float::INFINITY
assert_equal "Inf", "%f" % inf
@@ -35,9 +37,10 @@ assert('String#% with inf') do
assert_equal " Inf", "% 3f" % inf
assert_equal " Inf", "% 4f" % inf
assert_equal " Inf", "% 5f" % inf
-end if class_defined?("Float")
+end
assert('String#% with nan') do
+ skip unless Object.const_defined?(:Float)
nan = Float::NAN
assert_equal "NaN", "%f" % nan
@@ -63,7 +66,7 @@ assert('String#% with nan') do
assert_equal " NaN", "% 3f" % nan
assert_equal " NaN", "% 4f" % nan
assert_equal " NaN", "% 5f" % nan
-end if class_defined?("Float")
+end
assert("String#% with invalid chr") do
begin
diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb
index 27ca30610..fdaf2f960 100644
--- a/mrbgems/mruby-string-ext/mrblib/string.rb
+++ b/mrbgems/mruby-string-ext/mrblib/string.rb
@@ -1,25 +1,6 @@
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
#
@@ -112,7 +93,7 @@ class String
# "hello".rstrip! #=> nil
#
def rstrip!
- raise RuntimeError, "can't modify frozen String" if frozen?
+ raise FrozenError, "can't modify frozen String" if frozen?
s = self.rstrip
(s == self) ? nil : self.replace(s)
end
@@ -142,7 +123,7 @@ class String
# "abcdef".casecmp("ABCDEF") #=> 0
#
def casecmp(str)
- self.downcase <=> str.to_str.downcase
+ self.downcase <=> str.__to_str.downcase
rescue NoMethodError
nil
end
@@ -329,11 +310,15 @@ class String
end
end
+ ##
+ # Call the given block for each character of
+ # +self+.
def each_char(&block)
return to_enum :each_char unless block
-
- split('').each do |i|
- block.call(i)
+ pos = 0
+ while pos < self.size
+ block.call(self[pos])
+ pos += 1
end
self
end
diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c
index 4cab49094..50a4e5582 100644
--- a/mrbgems/mruby-string-ext/src/string.c
+++ b/mrbgems/mruby-string-ext/src/string.c
@@ -29,7 +29,7 @@ mrb_str_setbyte(mrb_state *mrb, mrb_value str)
len = RSTRING_LEN(str);
if (pos < -len || len <= pos)
- mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos));
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of string", mrb_fixnum_value(pos));
if (pos < 0)
pos += len;
@@ -42,44 +42,32 @@ mrb_str_setbyte(mrb_state *mrb, mrb_value str)
static mrb_value
mrb_str_byteslice(mrb_state *mrb, mrb_value str)
{
- mrb_value a1;
- mrb_int len;
+ mrb_value a1, a2;
+ mrb_int str_len = RSTRING_LEN(str), beg, len;
+ mrb_bool empty = TRUE;
- if (mrb_get_argc(mrb) == 2) {
- mrb_int pos;
- mrb_get_args(mrb, "ii", &pos, &len);
- return mrb_str_substr(mrb, str, pos, len);
+ if (mrb_get_args(mrb, "o|o", &a1, &a2) == 2) {
+ beg = mrb_fixnum(mrb_to_int(mrb, a1));
+ len = mrb_fixnum(mrb_to_int(mrb, a2));
+ goto subseq;
}
- mrb_get_args(mrb, "o|i", &a1, &len);
- switch (mrb_type(a1)) {
- case MRB_TT_RANGE:
- {
- mrb_int beg;
-
- len = RSTRING_LEN(str);
- switch (mrb_range_beg_len(mrb, a1, &beg, &len, len, TRUE)) {
- case 0: /* not range */
- break;
- case 1: /* range */
- return mrb_str_substr(mrb, str, beg, len);
- case 2: /* out of range */
- mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1);
- break;
- }
- return mrb_nil_value();
+ if (mrb_type(a1) == MRB_TT_RANGE) {
+ if (mrb_range_beg_len(mrb, a1, &beg, &len, str_len, TRUE) == MRB_RANGE_OK) {
+ goto subseq;
}
-#ifndef MRB_WITHOUT_FLOAT
- case MRB_TT_FLOAT:
- a1 = mrb_fixnum_value((mrb_int)mrb_float(a1));
- /* fall through */
-#endif
- 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");
+ return mrb_nil_value();
+ }
+
+ beg = mrb_fixnum(mrb_to_int(mrb, a1));
+ len = 1;
+ empty = FALSE;
+subseq:
+ if (mrb_str_beg_len(str_len, &beg, &len) && (empty || len != 0)) {
+ return mrb_str_byte_subseq(mrb, str, beg, len);
+ }
+ else {
+ return mrb_nil_value();
}
- /* not reached */
- return mrb_nil_value();
}
/*
@@ -163,7 +151,7 @@ mrb_str_concat_m(mrb_state *mrb, mrb_value self)
if (mrb_fixnum_p(str))
str = mrb_fixnum_chr(mrb, str);
else
- str = mrb_string_type(mrb, str);
+ str = mrb_ensure_string_type(mrb, str);
mrb_str_concat(mrb, self, str);
return self;
}
@@ -191,7 +179,7 @@ mrb_str_start_with(mrb_state *mrb, mrb_value self)
for (i = 0; i < argc; i++) {
size_t len_l, len_r;
int ai = mrb_gc_arena_save(mrb);
- sub = mrb_string_type(mrb, argv[i]);
+ sub = mrb_ensure_string_type(mrb, argv[i]);
mrb_gc_arena_restore(mrb, ai);
len_l = RSTRING_LEN(self);
len_r = RSTRING_LEN(sub);
@@ -220,7 +208,7 @@ mrb_str_end_with(mrb_state *mrb, mrb_value self)
for (i = 0; i < argc; i++) {
size_t len_l, len_r;
int ai = mrb_gc_arena_save(mrb);
- sub = mrb_string_type(mrb, argv[i]);
+ sub = mrb_ensure_string_type(mrb, argv[i]);
mrb_gc_arena_restore(mrb, ai);
len_l = RSTRING_LEN(self);
len_r = RSTRING_LEN(sub);
@@ -235,6 +223,592 @@ mrb_str_end_with(mrb_state *mrb, mrb_value self)
return mrb_false_value();
}
+enum tr_pattern_type {
+ TR_UNINITIALIZED = 0,
+ TR_IN_ORDER = 1,
+ TR_RANGE = 2,
+};
+
+/*
+ #tr Pattern syntax
+
+ <syntax> ::= (<pattern>)* | '^' (<pattern>)*
+ <pattern> ::= <in order> | <range>
+ <in order> ::= (<ch>)+
+ <range> ::= <ch> '-' <ch>
+*/
+struct tr_pattern {
+ uint8_t type; // 1:in-order, 2:range
+ mrb_bool flag_reverse : 1;
+ mrb_bool flag_on_heap : 1;
+ uint16_t n;
+ union {
+ uint16_t start_pos;
+ char ch[2];
+ } val;
+ struct tr_pattern *next;
+};
+
+#define STATIC_TR_PATTERN { 0 }
+
+static inline void
+tr_free_pattern(mrb_state *mrb, struct tr_pattern *pat)
+{
+ while (pat) {
+ struct tr_pattern *p = pat->next;
+ if (pat->flag_on_heap) {
+ mrb_free(mrb, pat);
+ }
+ pat = p;
+ }
+}
+
+static struct tr_pattern*
+tr_parse_pattern(mrb_state *mrb, struct tr_pattern *ret, const mrb_value v_pattern, mrb_bool flag_reverse_enable)
+{
+ const char *pattern = RSTRING_PTR(v_pattern);
+ mrb_int pattern_length = RSTRING_LEN(v_pattern);
+ mrb_bool flag_reverse = FALSE;
+ struct tr_pattern *pat1;
+ mrb_int i = 0;
+
+ if(flag_reverse_enable && pattern_length >= 2 && pattern[0] == '^') {
+ flag_reverse = TRUE;
+ i++;
+ }
+
+ while (i < pattern_length) {
+ /* is range pattern ? */
+ mrb_bool const ret_uninit = (ret->type == TR_UNINITIALIZED);
+ pat1 = ret_uninit
+ ? ret
+ : (struct tr_pattern*)mrb_malloc_simple(mrb, sizeof(struct tr_pattern));
+ if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-') {
+ if (pat1 == NULL && ret) {
+ nomem:
+ tr_free_pattern(mrb, ret);
+ mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
+ return NULL; /* not reached */
+ }
+ pat1->type = TR_RANGE;
+ pat1->flag_reverse = flag_reverse;
+ pat1->flag_on_heap = !ret_uninit;
+ pat1->n = pattern[i+2] - pattern[i] + 1;
+ pat1->next = NULL;
+ pat1->val.ch[0] = pattern[i];
+ pat1->val.ch[1] = pattern[i+2];
+ i += 3;
+ }
+ else {
+ /* in order pattern. */
+ mrb_int start_pos = i++;
+ mrb_int len;
+
+ while (i < pattern_length) {
+ if ((i+2) < pattern_length && pattern[i] != '\\' && pattern[i+1] == '-')
+ break;
+ i++;
+ }
+
+ len = i - start_pos;
+ if (len > UINT16_MAX) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "tr pattern too long (max 65536)");
+ }
+ if (pat1 == NULL && ret) {
+ goto nomem;
+ }
+ pat1->type = TR_IN_ORDER;
+ pat1->flag_reverse = flag_reverse;
+ pat1->flag_on_heap = !ret_uninit;
+ pat1->n = len;
+ pat1->next = NULL;
+ pat1->val.start_pos = start_pos;
+ }
+
+ if (ret == NULL || ret_uninit) {
+ ret = pat1;
+ }
+ else {
+ struct tr_pattern *p = ret;
+ while (p->next != NULL) {
+ p = p->next;
+ }
+ p->next = pat1;
+ }
+ }
+
+ return ret;
+}
+
+static inline mrb_int
+tr_find_character(const struct tr_pattern *pat, const char *pat_str, int ch)
+{
+ mrb_int ret = -1;
+ mrb_int n_sum = 0;
+ mrb_int flag_reverse = pat ? pat->flag_reverse : 0;
+
+ while (pat != NULL) {
+ if (pat->type == TR_IN_ORDER) {
+ int i;
+ for (i = 0; i < pat->n; i++) {
+ if (pat_str[pat->val.start_pos + i] == ch) ret = n_sum + i;
+ }
+ }
+ else if (pat->type == TR_RANGE) {
+ if (pat->val.ch[0] <= ch && ch <= pat->val.ch[1])
+ ret = n_sum + ch - pat->val.ch[0];
+ }
+ else {
+ mrb_assert(pat->type == TR_UNINITIALIZED);
+ }
+ n_sum += pat->n;
+ pat = pat->next;
+ }
+
+ if (flag_reverse) {
+ return (ret < 0) ? MRB_INT_MAX : -1;
+ }
+ return ret;
+}
+
+static inline mrb_int
+tr_get_character(const struct tr_pattern *pat, const char *pat_str, mrb_int n_th)
+{
+ mrb_int n_sum = 0;
+
+ while (pat != NULL) {
+ if (n_th < (n_sum + pat->n)) {
+ mrb_int i = (n_th - n_sum);
+
+ switch (pat->type) {
+ case TR_IN_ORDER:
+ return pat_str[pat->val.start_pos + i];
+ case TR_RANGE:
+ return pat->val.ch[0]+i;
+ case TR_UNINITIALIZED:
+ return -1;
+ }
+ }
+ if (pat->next == NULL) {
+ switch (pat->type) {
+ case TR_IN_ORDER:
+ return pat_str[pat->val.start_pos + pat->n - 1];
+ case TR_RANGE:
+ return pat->val.ch[1];
+ case TR_UNINITIALIZED:
+ return -1;
+ }
+ }
+ n_sum += pat->n;
+ pat = pat->next;
+ }
+
+ return -1;
+}
+
+static inline void
+tr_bitmap_set(uint8_t bitmap[32], uint8_t ch)
+{
+ uint8_t idx1 = ch / 8;
+ uint8_t idx2 = ch % 8;
+ bitmap[idx1] |= (1<<idx2);
+}
+
+static inline mrb_bool
+tr_bitmap_detect(uint8_t bitmap[32], uint8_t ch)
+{
+ uint8_t idx1 = ch / 8;
+ uint8_t idx2 = ch % 8;
+ if (bitmap[idx1] & (1<<idx2))
+ return TRUE;
+ return FALSE;
+}
+
+/* compile patter to bitmap */
+static void
+tr_compile_pattern(const struct tr_pattern *pat, mrb_value pstr, uint8_t bitmap[32])
+{
+ const char *pattern = RSTRING_PTR(pstr);
+ mrb_int flag_reverse = pat ? pat->flag_reverse : 0;
+ int i;
+
+ for (i=0; i<32; i++) {
+ bitmap[i] = 0;
+ }
+ while (pat != NULL) {
+ if (pat->type == TR_IN_ORDER) {
+ for (i = 0; i < pat->n; i++) {
+ tr_bitmap_set(bitmap, pattern[pat->val.start_pos + i]);
+ }
+ }
+ else if (pat->type == TR_RANGE) {
+ for (i = pat->val.ch[0]; i < pat->val.ch[1]; i++) {
+ tr_bitmap_set(bitmap, i);
+ }
+ }
+ else {
+ mrb_assert(pat->type == TR_UNINITIALIZED);
+ }
+ pat = pat->next;
+ }
+
+ if (flag_reverse) {
+ for (i=0; i<32; i++) {
+ bitmap[i] ^= 0xff;
+ }
+ }
+}
+
+static mrb_bool
+str_tr(mrb_state *mrb, mrb_value str, mrb_value p1, mrb_value p2, mrb_bool squeeze)
+{
+ struct tr_pattern pat = STATIC_TR_PATTERN;
+ struct tr_pattern rep_storage = STATIC_TR_PATTERN;
+ char *s;
+ mrb_int len;
+ mrb_int i;
+ mrb_int j;
+ mrb_bool flag_changed = FALSE;
+ mrb_int lastch = -1;
+ struct tr_pattern *rep;
+
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ tr_parse_pattern(mrb, &pat, p1, TRUE);
+ rep = tr_parse_pattern(mrb, &rep_storage, p2, FALSE);
+ s = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+
+ for (i=j=0; i<len; i++,j++) {
+ mrb_int n = tr_find_character(&pat, RSTRING_PTR(p1), s[i]);
+
+ if (i>j) s[j] = s[i];
+ if (n >= 0) {
+ flag_changed = TRUE;
+ if (rep == NULL) {
+ j--;
+ }
+ else {
+ mrb_int c = tr_get_character(rep, RSTRING_PTR(p2), n);
+
+ if (c < 0 || (squeeze && c == lastch)) {
+ j--;
+ continue;
+ }
+ if (c > 0x80) {
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "character (%S) out of range",
+ mrb_fixnum_value((mrb_int)c));
+ }
+ lastch = c;
+ s[i] = (char)c;
+ }
+ }
+ }
+
+ tr_free_pattern(mrb, &pat);
+ tr_free_pattern(mrb, rep);
+
+ if (flag_changed) {
+ RSTR_SET_LEN(RSTRING(str), j);
+ RSTRING_PTR(str)[j] = 0;
+ }
+ return flag_changed;
+}
+
+/*
+ * call-seq:
+ * str.tr(from_str, to_str) => new_str
+ *
+ * Returns a copy of str with the characters in from_str replaced by the
+ * corresponding characters in to_str. If to_str is shorter than from_str,
+ * it is padded with its last character in order to maintain the
+ * correspondence.
+ *
+ * "hello".tr('el', 'ip') #=> "hippo"
+ * "hello".tr('aeiou', '*') #=> "h*ll*"
+ * "hello".tr('aeiou', 'AA*') #=> "hAll*"
+ *
+ * Both strings may use the c1-c2 notation to denote ranges of characters,
+ * and from_str may start with a ^, which denotes all characters except
+ * those listed.
+ *
+ * "hello".tr('a-y', 'b-z') #=> "ifmmp"
+ * "hello".tr('^aeiou', '*') #=> "*e**o"
+ *
+ * The backslash character \ can be used to escape ^ or - and is otherwise
+ * ignored unless it appears at the end of a range or the end of the
+ * from_str or to_str:
+ *
+ *
+ * "hello^world".tr("\\^aeiou", "*") #=> "h*ll**w*rld"
+ * "hello-world".tr("a\\-eo", "*") #=> "h*ll**w*rld"
+ *
+ * "hello\r\nworld".tr("\r", "") #=> "hello\nworld"
+ * "hello\r\nworld".tr("\\r", "") #=> "hello\r\nwold"
+ * "hello\r\nworld".tr("\\\r", "") #=> "hello\nworld"
+ *
+ * "X['\\b']".tr("X\\", "") #=> "['b']"
+ * "X['\\b']".tr("X-\\]", "") #=> "'b'"
+ *
+ * Note: conversion is effective only in ASCII region.
+ */
+static mrb_value
+mrb_str_tr(mrb_state *mrb, mrb_value str)
+{
+ mrb_value dup;
+ mrb_value p1, p2;
+
+ mrb_get_args(mrb, "SS", &p1, &p2);
+ dup = mrb_str_dup(mrb, str);
+ str_tr(mrb, dup, p1, p2, FALSE);
+ return dup;
+}
+
+/*
+ * call-seq:
+ * str.tr!(from_str, to_str) -> str or nil
+ *
+ * Translates str in place, using the same rules as String#tr.
+ * Returns str, or nil if no changes were made.
+ */
+static mrb_value
+mrb_str_tr_bang(mrb_state *mrb, mrb_value str)
+{
+ mrb_value p1, p2;
+
+ mrb_get_args(mrb, "SS", &p1, &p2);
+ if (str_tr(mrb, str, p1, p2, FALSE)) {
+ return str;
+ }
+ return mrb_nil_value();
+}
+
+/*
+ * call-seq:
+ * str.tr_s(from_str, to_str) -> new_str
+ *
+ * Processes a copy of str as described under String#tr, then removes
+ * duplicate characters in regions that were affected by the translation.
+ *
+ * "hello".tr_s('l', 'r') #=> "hero"
+ * "hello".tr_s('el', '*') #=> "h*o"
+ * "hello".tr_s('el', 'hx') #=> "hhxo"
+ */
+static mrb_value
+mrb_str_tr_s(mrb_state *mrb, mrb_value str)
+{
+ mrb_value dup;
+ mrb_value p1, p2;
+
+ mrb_get_args(mrb, "SS", &p1, &p2);
+ dup = mrb_str_dup(mrb, str);
+ str_tr(mrb, dup, p1, p2, TRUE);
+ return dup;
+}
+
+/*
+ * call-seq:
+ * str.tr_s!(from_str, to_str) -> str or nil
+ *
+ * Performs String#tr_s processing on str in place, returning
+ * str, or nil if no changes were made.
+ */
+static mrb_value
+mrb_str_tr_s_bang(mrb_state *mrb, mrb_value str)
+{
+ mrb_value p1, p2;
+
+ mrb_get_args(mrb, "SS", &p1, &p2);
+ if (str_tr(mrb, str, p1, p2, TRUE)) {
+ return str;
+ }
+ return mrb_nil_value();
+}
+
+static mrb_bool
+str_squeeze(mrb_state *mrb, mrb_value str, mrb_value v_pat)
+{
+ struct tr_pattern pat_storage = STATIC_TR_PATTERN;
+ struct tr_pattern *pat = NULL;
+ mrb_int i, j;
+ char *s;
+ mrb_int len;
+ mrb_bool flag_changed = FALSE;
+ mrb_int lastch = -1;
+ uint8_t bitmap[32];
+
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ if (!mrb_nil_p(v_pat)) {
+ pat = tr_parse_pattern(mrb, &pat_storage, v_pat, TRUE);
+ tr_compile_pattern(pat, v_pat, bitmap);
+ tr_free_pattern(mrb, pat);
+ }
+ s = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+
+ if (pat) {
+ for (i=j=0; i<len; i++,j++) {
+ if (i>j) s[j] = s[i];
+ if (tr_bitmap_detect(bitmap, s[i]) && s[i] == lastch) {
+ flag_changed = TRUE;
+ j--;
+ }
+ lastch = s[i];
+ }
+ }
+ else {
+ for (i=j=0; i<len; i++,j++) {
+ if (i>j) s[j] = s[i];
+ if (s[i] >= 0 && s[i] == lastch) {
+ flag_changed = TRUE;
+ j--;
+ }
+ lastch = s[i];
+ }
+ }
+
+ if (flag_changed) {
+ RSTR_SET_LEN(RSTRING(str), j);
+ RSTRING_PTR(str)[j] = 0;
+ }
+ return flag_changed;
+}
+
+/*
+ * call-seq:
+ * str.squeeze([other_str]) -> new_str
+ *
+ * Builds a set of characters from the other_str
+ * parameter(s) using the procedure described for String#count. Returns a
+ * new string where runs of the same character that occur in this set are
+ * replaced by a single character. If no arguments are given, all runs of
+ * identical characters are replaced by a single character.
+ *
+ * "yellow moon".squeeze #=> "yelow mon"
+ * " now is the".squeeze(" ") #=> " now is the"
+ * "putters shoot balls".squeeze("m-z") #=> "puters shot balls"
+ */
+static mrb_value
+mrb_str_squeeze(mrb_state *mrb, mrb_value str)
+{
+ mrb_value pat = mrb_nil_value();
+ mrb_value dup;
+
+ mrb_get_args(mrb, "|S", &pat);
+ dup = mrb_str_dup(mrb, str);
+ str_squeeze(mrb, dup, pat);
+ return dup;
+}
+
+/*
+ * call-seq:
+ * str.squeeze!([other_str]) -> str or nil
+ *
+ * Squeezes str in place, returning either str, or nil if no
+ * changes were made.
+ */
+static mrb_value
+mrb_str_squeeze_bang(mrb_state *mrb, mrb_value str)
+{
+ mrb_value pat = mrb_nil_value();
+
+ mrb_get_args(mrb, "|S", &pat);
+ if (str_squeeze(mrb, str, pat)) {
+ return str;
+ }
+ return mrb_nil_value();
+}
+
+static mrb_bool
+str_delete(mrb_state *mrb, mrb_value str, mrb_value v_pat)
+{
+ struct tr_pattern pat = STATIC_TR_PATTERN;
+ mrb_int i, j;
+ char *s;
+ mrb_int len;
+ mrb_bool flag_changed = FALSE;
+ uint8_t bitmap[32];
+
+ mrb_str_modify(mrb, mrb_str_ptr(str));
+ tr_parse_pattern(mrb, &pat, v_pat, TRUE);
+ tr_compile_pattern(&pat, v_pat, bitmap);
+ tr_free_pattern(mrb, &pat);
+
+ s = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+
+ for (i=j=0; i<len; i++,j++) {
+ if (i>j) s[j] = s[i];
+ if (tr_bitmap_detect(bitmap, s[i])) {
+ flag_changed = TRUE;
+ j--;
+ }
+ }
+ if (flag_changed) {
+ RSTR_SET_LEN(RSTRING(str), j);
+ RSTRING_PTR(str)[j] = 0;
+ }
+ return flag_changed;
+}
+
+static mrb_value
+mrb_str_delete(mrb_state *mrb, mrb_value str)
+{
+ mrb_value pat;
+ mrb_value dup;
+
+ mrb_get_args(mrb, "S", &pat);
+ dup = mrb_str_dup(mrb, str);
+ str_delete(mrb, dup, pat);
+ return dup;
+}
+
+static mrb_value
+mrb_str_delete_bang(mrb_state *mrb, mrb_value str)
+{
+ mrb_value pat;
+
+ mrb_get_args(mrb, "S", &pat);
+ if (str_delete(mrb, str, pat)) {
+ return str;
+ }
+ return mrb_nil_value();
+}
+
+/*
+ * call_seq:
+ * str.count([other_str]) -> integer
+ *
+ * Each other_str parameter defines a set of characters to count. The
+ * intersection of these sets defines the characters to count in str. Any
+ * other_str that starts with a caret ^ is negated. The sequence c1-c2
+ * means all characters between c1 and c2. The backslash character \ can
+ * be used to escape ^ or - and is otherwise ignored unless it appears at
+ * the end of a sequence or the end of a other_str.
+ */
+static mrb_value
+mrb_str_count(mrb_state *mrb, mrb_value str)
+{
+ mrb_value v_pat = mrb_nil_value();
+ mrb_int i;
+ char *s;
+ mrb_int len;
+ mrb_int count = 0;
+ struct tr_pattern pat = STATIC_TR_PATTERN;
+ uint8_t bitmap[32];
+
+ mrb_get_args(mrb, "S", &v_pat);
+ tr_parse_pattern(mrb, &pat, v_pat, TRUE);
+ tr_compile_pattern(&pat, v_pat, bitmap);
+ tr_free_pattern(mrb, &pat);
+
+ s = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+ for (i = 0; i < len; i++) {
+ if (tr_bitmap_detect(bitmap, s[i])) count++;
+ }
+ return mrb_fixnum_value(count);
+}
+
static mrb_value
mrb_str_hex(mrb_state *mrb, mrb_value self)
{
@@ -620,6 +1194,15 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, s, "swapcase", mrb_str_swapcase, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "concat", mrb_str_concat_m, MRB_ARGS_REQ(1));
mrb_define_method(mrb, s, "<<", mrb_str_concat_m, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "count", mrb_str_count, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "tr", mrb_str_tr, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "tr!", mrb_str_tr_bang, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "tr_s", mrb_str_tr_s, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "tr_s!", mrb_str_tr_s_bang, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, s, "squeeze", mrb_str_squeeze, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, s, "squeeze!", mrb_str_squeeze_bang, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, s, "delete", mrb_str_delete, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, s, "delete!", mrb_str_delete_bang, MRB_ARGS_REQ(1));
mrb_define_method(mrb, s, "start_with?", mrb_str_start_with, MRB_ARGS_REST());
mrb_define_method(mrb, s, "end_with?", mrb_str_end_with, MRB_ARGS_REST());
mrb_define_method(mrb, s, "hex", mrb_str_hex, MRB_ARGS_NONE());
@@ -627,8 +1210,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, s, "chr", mrb_str_chr, 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_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_alias(mrb, s, "next", "succ");
+ mrb_define_alias(mrb, s, "next!", "succ!");
mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "delete_prefix!", mrb_str_del_prefix_bang, MRB_ARGS_REQ(1));
mrb_define_method(mrb, s, "delete_prefix", mrb_str_del_prefix, MRB_ARGS_REQ(1));
diff --git a/mrbgems/mruby-string-ext/test/range.rb b/mrbgems/mruby-string-ext/test/range.rb
new file mode 100644
index 000000000..80c286850
--- /dev/null
+++ b/mrbgems/mruby-string-ext/test/range.rb
@@ -0,0 +1,26 @@
+assert('Range#max') do
+ # returns the maximum value in the range when called with no arguments
+ assert_equal 'l', ('f'..'l').max
+ assert_equal 'e', ('a'...'f').max
+
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, ('z'..'l').max
+end
+
+assert('Range#max given a block') do
+ # returns nil when the endpoint is less than the start point
+ assert_equal nil, (('z'..'l').max { |x, y| x <=> y })
+end
+
+assert('Range#min') do
+ # returns the minimum value in the range when called with no arguments
+ assert_equal 'f', ('f'..'l').min
+
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, ('z'..'l').min
+end
+
+assert('Range#min given a block') do
+ # returns nil when the start point is greater than the endpoint
+ assert_equal nil, (('z'..'l').min { |x, y| x <=> y })
+end
diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb
index b6146fb90..9a324c46d 100644
--- a/mrbgems/mruby-string-ext/test/string.rb
+++ b/mrbgems/mruby-string-ext/test/string.rb
@@ -2,14 +2,7 @@
##
# 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
+UTF8STRING = __ENCODING__ == "UTF-8"
assert('String#getbyte') do
str1 = "hello"
@@ -31,83 +24,132 @@ assert('String#setbyte') do
assert_equal("Hello", str1)
end
-assert("String#setbyte raises IndexError if arg conversion resizes String") do
- $s = "01234\n"
- class Tmp
- def to_i
- $s.chomp! ''
- 95
- end
- end
- tmp = Tmp.new
- assert_raise(IndexError) { $s.setbyte(5, tmp) }
-end
-
assert('String#byteslice') do
str1 = "hello"
+ str2 = "\u3042ab" # "\xE3\x81\x82ab"
+
+ assert_equal("h", str1.byteslice(0))
assert_equal("e", str1.byteslice(1))
+ assert_equal(nil, str1.byteslice(5))
assert_equal("o", str1.byteslice(-1))
+ assert_equal(nil, str1.byteslice(-6))
+ assert_equal("\xE3", str2.byteslice(0))
+ assert_equal("\x81", str2.byteslice(1))
+ assert_equal(nil, str2.byteslice(5))
+ assert_equal("b", str2.byteslice(-1))
+ assert_equal(nil, str2.byteslice(-6))
+
+ assert_equal("", str1.byteslice(0, 0))
+ assert_equal(str1, str1.byteslice(0, 6))
+ assert_equal("el", str1.byteslice(1, 2))
+ assert_equal("", str1.byteslice(5, 1))
+ assert_equal("o", str1.byteslice(-1, 6))
+ assert_equal(nil, str1.byteslice(-6, 1))
+ assert_equal(nil, str1.byteslice(0, -1))
+ assert_equal("", str2.byteslice(0, 0))
+ assert_equal(str2, str2.byteslice(0, 6))
+ assert_equal("\x81\x82", str2.byteslice(1, 2))
+ assert_equal("", str2.byteslice(5, 1))
+ assert_equal("b", str2.byteslice(-1, 6))
+ assert_equal(nil, str2.byteslice(-6, 1))
+ assert_equal(nil, str2.byteslice(0, -1))
+
assert_equal("ell", str1.byteslice(1..3))
assert_equal("el", str1.byteslice(1...3))
+ assert_equal("h", str1.byteslice(0..0))
+ assert_equal("", str1.byteslice(5..0))
+ assert_equal("o", str1.byteslice(4..5))
+ assert_equal(nil, str1.byteslice(6..0))
+ assert_equal("", str1.byteslice(-1..0))
+ assert_equal("llo", str1.byteslice(-3..5))
+ assert_equal("\x81\x82a", str2.byteslice(1..3))
+ assert_equal("\x81\x82", str2.byteslice(1...3))
+ assert_equal("\xE3", str2.byteslice(0..0))
+ assert_equal("", str2.byteslice(5..0))
+ assert_equal("b", str2.byteslice(4..5))
+ assert_equal(nil, str2.byteslice(6..0))
+ assert_equal("", str2.byteslice(-1..0))
+ assert_equal("\x82ab", str2.byteslice(-3..5))
+
+ assert_raise(ArgumentError) { str1.byteslice }
+ assert_raise(ArgumentError) { str1.byteslice(1, 2, 3) }
+ assert_raise(TypeError) { str1.byteslice("1") }
+ assert_raise(TypeError) { str1.byteslice("1", 2) }
+ assert_raise(TypeError) { str1.byteslice(1, "2") }
+ assert_raise(TypeError) { str1.byteslice(1..2, 3) }
+
+ skip unless Object.const_defined?(:Float)
+ assert_equal("o", str1.byteslice(4.0))
+ assert_equal("\x82ab", str2.byteslice(2.0, 3.0))
end
assert('String#dump') do
- ("\1" * 100).dump # should not raise an exception - regress #1210
- "\0".inspect == "\"\\000\"" and
- "foo".dump == "\"foo\""
+ assert_equal("\"\\x00\"", "\0".dump)
+ assert_equal("\"foo\"", "foo".dump)
+ assert_nothing_raised { ("\1" * 100).dump } # regress #1210
end
assert('String#strip') do
s = " abc "
- "".strip == "" and " \t\r\n\f\v".strip == "" and
- "\0a\0".strip == "\0a" and
- "abc".strip == "abc" and
- " abc".strip == "abc" and
- "abc ".strip == "abc" and
- " abc ".strip == "abc" and
- s == " abc "
+ assert_equal("abc", s.strip)
+ assert_equal(" abc ", s)
+ assert_equal("", "".strip)
+ assert_equal("", " \t\r\n\f\v".strip)
+ assert_equal("\0a", "\0a\0".strip)
+ assert_equal("abc", "abc".strip)
+ assert_equal("abc", " abc".strip)
+ assert_equal("abc", "abc ".strip)
end
assert('String#lstrip') do
s = " abc "
- s.lstrip
- "".lstrip == "" and " \t\r\n\f\v".lstrip == "" and
- "\0a\0".lstrip == "\0a\0" and
- "abc".lstrip == "abc" and
- " abc".lstrip == "abc" and
- "abc ".lstrip == "abc " and
- " abc ".lstrip == "abc " and
- s == " abc "
+ assert_equal("abc ", s.lstrip)
+ assert_equal(" abc ", s)
+ assert_equal("", "".lstrip)
+ assert_equal("", " \t\r\n\f\v".lstrip)
+ assert_equal("\0a\0", "\0a\0".lstrip)
+ assert_equal("abc", "abc".lstrip)
+ assert_equal("abc", " abc".lstrip)
+ assert_equal("abc ", "abc ".lstrip)
end
assert('String#rstrip') do
s = " abc "
- s.rstrip
- "".rstrip == "" and " \t\r\n\f\v".rstrip == "" and
- "\0a\0".rstrip == "\0a" and
- "abc".rstrip == "abc" and
- " abc".rstrip == " abc" and
- "abc ".rstrip == "abc" and
- " abc ".rstrip == " abc" and
- s == " abc "
+ assert_equal(" abc", s.rstrip)
+ assert_equal(" abc ", s)
+ assert_equal("", "".rstrip)
+ assert_equal("", " \t\r\n\f\v".rstrip)
+ assert_equal("\0a", "\0a\0".rstrip)
+ assert_equal("abc", "abc".rstrip)
+ assert_equal(" abc", " abc".rstrip)
+ assert_equal("abc", "abc ".rstrip)
end
assert('String#strip!') do
s = " abc "
t = "abc"
- s.strip! == "abc" and s == "abc" and t.strip! == nil
+ assert_equal("abc", s.strip!)
+ assert_equal("abc", s)
+ assert_nil(t.strip!)
+ assert_equal("abc", t)
end
assert('String#lstrip!') do
s = " abc "
t = "abc "
- s.lstrip! == "abc " and s == "abc " and t.lstrip! == nil
+ assert_equal("abc ", s.lstrip!)
+ assert_equal("abc ", s)
+ assert_nil(t.lstrip!)
+ assert_equal("abc ", t)
end
assert('String#rstrip!') do
s = " abc "
t = " abc"
- s.rstrip! == " abc" and s == " abc" and t.rstrip! == nil
+ assert_equal(" abc", s.rstrip!)
+ assert_equal(" abc", s)
+ assert_nil(t.rstrip!)
+ assert_equal(" abc", t)
end
assert('String#swapcase') do
@@ -126,12 +168,6 @@ assert('String#concat') do
assert_equal "Hello World!", "Hello " << "World" << 33
assert_equal "Hello World!", "Hello ".concat("World").concat(33)
- o = Object.new
- def o.to_str
- "to_str"
- end
- assert_equal "hi to_str", "hi " << o
-
assert_raise(TypeError) { "".concat(Object.new) }
end
@@ -140,11 +176,69 @@ assert('String#casecmp') do
assert_equal 0, "aBcDeF".casecmp("abcdef")
assert_equal(-1, "abcdef".casecmp("abcdefg"))
assert_equal 0, "abcdef".casecmp("ABCDEF")
- o = Object.new
- def o.to_str
- "ABCDEF"
- end
- assert_equal 0, "abcdef".casecmp(o)
+end
+
+assert('String#count') do
+ s = "abccdeff123"
+ assert_equal 0, s.count("")
+ assert_equal 1, s.count("a")
+ assert_equal 2, s.count("ab")
+ assert_equal 9, s.count("^c")
+ assert_equal 8, s.count("a-z")
+ assert_equal 4, s.count("a0-9")
+end
+
+assert('String#tr') do
+ assert_equal "ABC", "abc".tr('a-z', 'A-Z')
+ assert_equal "hippo", "hello".tr('el', 'ip')
+ assert_equal "Ruby", "Lisp".tr("Lisp", "Ruby")
+ assert_equal "*e**o", "hello".tr('^aeiou', '*')
+ assert_equal "heo", "hello".tr('l', '')
+end
+
+assert('String#tr!') do
+ s = "abcdefghijklmnopqR"
+ assert_equal "ab12222hijklmnopqR", s.tr!("cdefg", "12")
+ assert_equal "ab12222hijklmnopqR", s
+end
+
+assert('String#tr_s') do
+ assert_equal "hero", "hello".tr_s('l', 'r')
+ assert_equal "h*o", "hello".tr_s('el', '*')
+ assert_equal "hhxo", "hello".tr_s('el', 'hx')
+end
+
+assert('String#tr_s!') do
+ s = "hello"
+ assert_equal "hero", s.tr_s!('l', 'r')
+ assert_equal "hero", s
+ assert_nil s.tr_s!('l', 'r')
+end
+
+assert('String#squeeze') do
+ assert_equal "yelow mon", "yellow moon".squeeze
+ assert_equal " now is the", " now is the".squeeze(" ")
+ assert_equal "puters shot balls", "putters shoot balls".squeeze("m-z")
+end
+
+assert('String#squeeze!') do
+ s = " now is the"
+ assert_equal " now is the", s.squeeze!(" ")
+ assert_equal " now is the", s
+end
+
+assert('String#delete') do
+ assert_equal "he", "hello".delete("lo")
+ assert_equal "hll", "hello".delete("aeiou")
+ assert_equal "ll", "hello".delete("^l")
+ assert_equal "ho", "hello".delete("ej-m")
+end
+
+assert('String#delete!') do
+ s = "hello"
+ assert_equal "he", s.delete!("lo")
+ assert_equal "he", s
+ assert_nil s.delete!("lz")
end
assert('String#start_with?') do
@@ -614,19 +708,19 @@ assert('String#chars(UTF-8)') do
end if UTF8STRING
assert('String#each_char') do
- s = ""
+ chars = []
"hello!".each_char do |x|
- s += x
+ chars << x
end
- assert_equal "hello!", s
+ assert_equal ["h", "e", "l", "l", "o", "!"], chars
end
assert('String#each_char(UTF-8)') do
- s = ""
+ chars = []
"こんにちは世界!".each_char do |x|
- s += x
+ chars << x
end
- assert_equal "こんにちは世界!", s
+ assert_equal ["こ", "ん", "に", "ち", "は", "世", "界", "!"], chars
end if UTF8STRING
assert('String#codepoints') do
diff --git a/mrbgems/mruby-struct/mrblib/struct.rb b/mrbgems/mruby-struct/mrblib/struct.rb
index 86c635df7..7682ac033 100644
--- a/mrbgems/mruby-struct/mrblib/struct.rb
+++ b/mrbgems/mruby-struct/mrblib/struct.rb
@@ -79,23 +79,22 @@ if Object.const_defined?(:Struct)
# 15.2.18.4.11(x)
#
alias to_s inspect
- end
- ##
- # call-seq:
- # hsh.dig(key,...) -> object
- #
- # Extracts the nested value specified by the sequence of <i>key</i>
- # objects by calling +dig+ at each step, returning +nil+ if any
- # intermediate step is +nil+.
- #
- def dig(idx,*args)
- n = self[idx]
- if args.size > 0
- n&.dig(*args)
- else
- n
+ ##
+ # call-seq:
+ # hsh.dig(key,...) -> object
+ #
+ # Extracts the nested value specified by the sequence of <i>key</i>
+ # objects by calling +dig+ at each step, returning +nil+ if any
+ # intermediate step is +nil+.
+ #
+ def dig(idx,*args)
+ n = self[idx]
+ if args.size > 0
+ n&.dig(*args)
+ else
+ n
+ end
end
end
end
-
diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c
index adeb09bc1..e04fe13ad 100644
--- a/mrbgems/mruby-struct/src/struct.c
+++ b/mrbgems/mruby-struct/src/struct.c
@@ -24,19 +24,18 @@ struct_class(mrb_state *mrb)
}
static inline mrb_value
-struct_ivar_get(mrb_state *mrb, mrb_value c, mrb_sym id)
+struct_ivar_get(mrb_state *mrb, mrb_value cls, mrb_sym id)
{
- struct RClass* kclass;
+ struct RClass* c = mrb_class_ptr(cls);
struct RClass* sclass = struct_class(mrb);
mrb_value ans;
for (;;) {
- ans = mrb_iv_get(mrb, c, id);
+ ans = mrb_iv_get(mrb, mrb_obj_value(c), id);
if (!mrb_nil_p(ans)) return ans;
- kclass = RCLASS_SUPER(c);
- if (kclass == 0 || kclass == sclass)
+ c = c->super;
+ if (c == sclass || c == 0)
return mrb_nil_value();
- c = mrb_obj_value(kclass);
}
}
@@ -88,10 +87,7 @@ mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass)
static void
mrb_struct_modify(mrb_state *mrb, mrb_value strct)
{
- if (MRB_FROZEN_P(mrb_basic_ptr(strct))) {
- mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen struct");
- }
-
+ mrb_check_frozen(mrb, mrb_basic_ptr(strct));
mrb_write_barrier(mrb, mrb_basic_ptr(strct));
}
@@ -127,19 +123,29 @@ mrb_struct_ref(mrb_state *mrb, mrb_value obj)
static mrb_sym
mrb_id_attrset(mrb_state *mrb, mrb_sym id)
{
+#define ONSTACK_ALLOC_MAX 32
+#define ONSTACK_STRLEN_MAX (ONSTACK_ALLOC_MAX - 1) /* '=' character */
+
const char *name;
char *buf;
mrb_int len;
mrb_sym mid;
+ char onstack[ONSTACK_ALLOC_MAX];
name = mrb_sym2name_len(mrb, id, &len);
- buf = (char *)mrb_malloc(mrb, (size_t)len+2);
+ if (len > ONSTACK_STRLEN_MAX) {
+ buf = (char *)mrb_malloc(mrb, (size_t)len+1);
+ }
+ else {
+ buf = onstack;
+ }
memcpy(buf, name, (size_t)len);
buf[len] = '=';
- buf[len+1] = '\0';
mid = mrb_intern(mrb, buf, len+1);
- mrb_free(mrb, buf);
+ if (buf != onstack) {
+ mrb_free(mrb, buf);
+ }
return mid;
}
@@ -162,20 +168,6 @@ mrb_struct_set_m(mrb_state *mrb, mrb_value obj)
return val;
}
-static mrb_bool
-is_local_id(mrb_state *mrb, const char *name)
-{
- if (!name) return FALSE;
- return !ISUPPER(name[0]);
-}
-
-static mrb_bool
-is_const_id(mrb_state *mrb, const char *name)
-{
- if (!name) return FALSE;
- return ISUPPER(name[0]);
-}
-
static void
make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c)
{
@@ -186,19 +178,15 @@ make_struct_define_accessors(mrb_state *mrb, mrb_value members, struct RClass *c
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)) {
- mrb_method_t m;
- mrb_value at = mrb_fixnum_value(i);
- struct RProc *aref = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_ref, 1, &at);
- struct RProc *aset = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_set_m, 1, &at);
- MRB_METHOD_FROM_PROC(m, aref);
- mrb_define_method_raw(mrb, c, id, m);
- MRB_METHOD_FROM_PROC(m, aset);
- mrb_define_method_raw(mrb, c, mrb_id_attrset(mrb, id), m);
- mrb_gc_arena_restore(mrb, ai);
- }
+ mrb_method_t m;
+ mrb_value at = mrb_fixnum_value(i);
+ struct RProc *aref = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_ref, 1, &at);
+ struct RProc *aset = mrb_proc_new_cfunc_with_env(mrb, mrb_struct_set_m, 1, &at);
+ MRB_METHOD_FROM_PROC(m, aref);
+ mrb_define_method_raw(mrb, c, id, m);
+ MRB_METHOD_FROM_PROC(m, aset);
+ mrb_define_method_raw(mrb, c, mrb_id_attrset(mrb, id), m);
+ mrb_gc_arena_restore(mrb, ai);
}
}
@@ -214,9 +202,9 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass *kl
}
else {
/* old style: should we warn? */
- name = mrb_str_to_str(mrb, name);
+ mrb_to_str(mrb, name);
id = mrb_obj_to_sym(mrb, name);
- if (!is_const_id(mrb, mrb_sym2name_len(mrb, id, NULL))) {
+ if (!mrb_const_name_p(mrb, RSTRING_PTR(name), RSTRING_LEN(name))) {
mrb_name_error(mrb, id, "identifier %S needs to be constant", name);
}
if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) {
@@ -303,17 +291,19 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass)
}
}
rest = mrb_ary_new_from_values(mrb, argcnt, pargv);
- for (i=0; i<RARRAY_LEN(rest); i++) {
+ for (i=0; i<argcnt; i++) {
id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]);
mrb_ary_set(mrb, rest, i, mrb_symbol_value(id));
}
- }
- st = make_struct(mrb, name, rest, mrb_class_ptr(klass));
- if (!mrb_nil_p(b)) {
- mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st));
- }
+ st = make_struct(mrb, name, rest, mrb_class_ptr(klass));
+ if (!mrb_nil_p(b)) {
+ mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st));
+ }
- return st;
+ return st;
+ }
+ /* not reached */
+ return mrb_nil_value();
}
static mrb_int
@@ -398,23 +388,24 @@ struct_aref_sym(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_name_error(mrb, id, "no member '%S' in struct", mrb_sym2str(mrb, id));
return mrb_nil_value(); /* not reached */
}
static mrb_value
struct_aref_int(mrb_state *mrb, mrb_value s, mrb_int i)
{
- if (i < 0) i = RSTRUCT_LEN(s) + i;
- if (i < 0)
- mrb_raisef(mrb, E_INDEX_ERROR,
- "offset %S too small for struct(size:%S)",
- mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
- if (RSTRUCT_LEN(s) <= i)
+ mrb_int idx = i < 0 ? RSTRUCT_LEN(s) + i : i;
+
+ if (idx < 0)
+ mrb_raisef(mrb, E_INDEX_ERROR,
+ "offset %S too small for struct(size:%S)",
+ mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
+ if (RSTRUCT_LEN(s) <= idx)
mrb_raisef(mrb, E_INDEX_ERROR,
"offset %S too large for struct(size:%S)",
mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s)));
- return RSTRUCT_PTR(s)[i];
+ return RSTRUCT_PTR(s)[idx];
}
/* 15.2.18.4.2 */
diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb
index 2b5751086..93930730b 100644
--- a/mrbgems/mruby-struct/test/struct.rb
+++ b/mrbgems/mruby-struct/test/struct.rb
@@ -153,14 +153,14 @@ assert("Struct#dig") do
assert_equal 1, a.dig(1, 0)
end
-assert("Struct.new removes existing constant") do
- skip "redefining Struct with same name cause warnings"
- begin
- assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b)
- ensure
- Struct.remove_const :Test
- end
-end
+# TODO: suppress redefining Struct warning during test
+# assert("Struct.new removes existing constant") do
+# begin
+# assert_not_equal Struct.new("Test", :a), Struct.new("Test", :a, :b)
+# ensure
+# Struct.remove_const :Test
+# end
+# end
assert("Struct#initialize_copy requires struct to be the same type") do
begin
@@ -182,6 +182,10 @@ assert("Struct.new does not allow array") do
end
end
+assert("Struct.new does not allow invalid class name") do
+ assert_raise(NameError) { Struct.new("Test-", :a) }
+end
+
assert("Struct.new generates subclass of Struct") do
begin
original_struct = Struct
@@ -200,7 +204,7 @@ assert 'Struct#freeze' do
assert_equal :test, o.m
o.freeze
- assert_raise(RuntimeError) { o.m = :modify }
- assert_raise(RuntimeError) { o[:m] = :modify }
+ assert_raise(FrozenError) { o.m = :modify }
+ assert_raise(FrozenError) { o[:m] = :modify }
assert_equal :test, o.m
end
diff --git a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb b/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
index 28cce3156..99fa275d5 100644
--- a/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
+++ b/mrbgems/mruby-symbol-ext/mrblib/symbol.rb
@@ -3,12 +3,6 @@ class Symbol
alias intern to_sym
- def to_proc
- ->(obj,*args,&block) do
- obj.__send__(self, *args, &block)
- end
- end
-
##
# call-seq:
# sym.capitalize -> symbol
diff --git a/mrbgems/mruby-symbol-ext/src/symbol.c b/mrbgems/mruby-symbol-ext/src/symbol.c
index a992dbfce..ccb2971dc 100644
--- a/mrbgems/mruby-symbol-ext/src/symbol.c
+++ b/mrbgems/mruby-symbol-ext/src/symbol.c
@@ -1,11 +1,7 @@
#include <mruby.h>
#include <mruby/khash.h>
#include <mruby/array.h>
-
-typedef struct symbol_name {
- size_t len;
- const char *name;
-} symbol_name;
+#include <mruby/string.h>
/*
* call-seq:
@@ -22,6 +18,7 @@ typedef struct symbol_name {
* :Tms, :getwd, :$=, :ThreadGroup,
* :wait2, :$>]
*/
+#ifdef MRB_ENABLE_ALL_SYMBOLS
static mrb_value
mrb_sym_all_symbols(mrb_state *mrb, mrb_value self)
{
@@ -29,11 +26,13 @@ mrb_sym_all_symbols(mrb_state *mrb, mrb_value self)
mrb_value ary = mrb_ary_new_capa(mrb, mrb->symidx);
for (i=1, lim=mrb->symidx+1; i<lim; i++) {
- mrb_ary_push(mrb, ary, mrb_symbol_value(i));
+ mrb_sym sym = i<<1;
+ mrb_ary_push(mrb, ary, mrb_symbol_value(sym));
}
return ary;
}
+#endif
/*
* call-seq:
@@ -45,7 +44,13 @@ static mrb_value
mrb_sym_length(mrb_state *mrb, mrb_value self)
{
mrb_int len;
+#ifdef MRB_UTF8_STRING
+ mrb_int byte_len;
+ const char *name = mrb_sym2name_len(mrb, mrb_symbol(self), &byte_len);
+ len = mrb_utf8_len(name, byte_len);
+#else
mrb_sym2name_len(mrb, mrb_symbol(self), &len);
+#endif
return mrb_fixnum_value(len);
}
@@ -53,7 +58,9 @@ void
mrb_mruby_symbol_ext_gem_init(mrb_state* mrb)
{
struct RClass *s = mrb->symbol_class;
+#ifdef MRB_ENABLE_ALL_SYMBOLS
mrb_define_class_method(mrb, s, "all_symbols", mrb_sym_all_symbols, MRB_ARGS_NONE());
+#endif
mrb_define_method(mrb, s, "length", mrb_sym_length, MRB_ARGS_NONE());
mrb_define_method(mrb, s, "size", mrb_sym_length, MRB_ARGS_NONE());
}
diff --git a/mrbgems/mruby-symbol-ext/test/symbol.rb b/mrbgems/mruby-symbol-ext/test/symbol.rb
index 6070d1418..db686e5f4 100644
--- a/mrbgems/mruby-symbol-ext/test/symbol.rb
+++ b/mrbgems/mruby-symbol-ext/test/symbol.rb
@@ -1,19 +1,27 @@
+# coding: utf-8
##
# Symbol(Ext) Test
-assert('Symbol#to_proc') do
- assert_equal 5, :abs.to_proc[-5]
-end
-
-assert('Symbol.all_symbols') do
- foo = [:__symbol_test_1, :__symbol_test_2, :__symbol_test_3].sort
- symbols = Symbol.all_symbols.select{|sym|sym.to_s.include? '__symbol_test'}.sort
- assert_equal foo, symbols
-end
-
-assert("Symbol#length") do
- assert_equal 5, :hello.size
- assert_equal 5, :mruby.length
+if Symbol.respond_to?(:all_symbols)
+ assert('Symbol.all_symbols') do
+ foo = [:__symbol_test_1, :__symbol_test_2, :__symbol_test_3].sort
+ symbols = Symbol.all_symbols.select{|sym|sym.to_s.include? '__symbol_test'}.sort
+ assert_equal foo, symbols
+ end
+end
+
+%w[size length].each do |n|
+ assert("Symbol##{n}") do
+ assert_equal 5, :hello.__send__(n)
+ assert_equal 4, :"aA\0b".__send__(n)
+ if __ENCODING__ == "UTF-8"
+ assert_equal 8, :"こんにちは世界!".__send__(n)
+ assert_equal 4, :"aあ\0b".__send__(n)
+ else
+ assert_equal 22, :"こんにちは世界!".__send__(n)
+ assert_equal 6, :"aあ\0b".__send__(n)
+ end
+ end
end
assert("Symbol#capitalize") do
diff --git a/mrbgems/mruby-test/driver.c b/mrbgems/mruby-test/driver.c
index 434d1fee5..602b76c79 100644
--- a/mrbgems/mruby-test/driver.c
+++ b/mrbgems/mruby-test/driver.c
@@ -18,8 +18,9 @@
#include <mruby/variable.h>
#include <mruby/array.h>
-void
-mrb_init_mrbtest(mrb_state *);
+extern const uint8_t mrbtest_assert_irep[];
+
+void mrbgemtest_init(mrb_state* mrb);
/* Print a short remark for the user */
static void
@@ -29,56 +30,182 @@ print_hint(void)
}
static int
-check_error(mrb_state *mrb)
-{
- /* Error check */
- /* $ko_test and $kill_test should be 0 */
- mrb_value ko_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$ko_test"));
- mrb_value kill_test = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$kill_test"));
-
- return mrb_fixnum_p(ko_test) && mrb_fixnum(ko_test) == 0 && mrb_fixnum_p(kill_test) && mrb_fixnum(kill_test) == 0;
-}
-
-static int
eval_test(mrb_state *mrb)
{
/* evaluate the test */
- mrb_funcall(mrb, mrb_top_self(mrb), "report", 0);
+ mrb_value result = mrb_funcall(mrb, mrb_top_self(mrb), "report", 0);
/* did an exception occur? */
if (mrb->exc) {
mrb_print_error(mrb);
mrb->exc = 0;
return EXIT_FAILURE;
}
- else if (!check_error(mrb)) {
- return EXIT_FAILURE;
+ else {
+ return mrb_bool(result) ? EXIT_SUCCESS : EXIT_FAILURE;
}
- return EXIT_SUCCESS;
}
-static void
-t_printstr(mrb_state *mrb, mrb_value obj)
+/* Implementation of print due to the reason that there might be no print */
+static mrb_value
+t_print(mrb_state *mrb, mrb_value self)
{
- char *s;
- mrb_int len;
-
- if (mrb_string_p(obj)) {
- s = RSTRING_PTR(obj);
- len = RSTRING_LEN(obj);
- fwrite(s, len, 1, stdout);
- fflush(stdout);
+ mrb_value *argv;
+ mrb_int argc;
+ mrb_int i;
+
+ mrb_get_args(mrb, "*!", &argv, &argc);
+ for (i = 0; i < argc; ++i) {
+ mrb_value s = mrb_obj_as_string(mrb, argv[i]);
+ fwrite(RSTRING_PTR(s), RSTRING_LEN(s), 1, stdout);
}
+ fflush(stdout);
+
+ return mrb_nil_value();
}
-mrb_value
-mrb_t_printstr(mrb_state *mrb, mrb_value self)
+#define UNESCAPE(p, endp) ((p) != (endp) && *(p) == '\\' ? (p)+1 : (p))
+#define CHAR_CMP(c1, c2) ((unsigned char)(c1) - (unsigned char)(c2))
+
+static const char *
+str_match_bracket(const char *p, const char *pat_end,
+ const char *s, const char *str_end)
{
- mrb_value argv;
+ mrb_bool ok = FALSE, negated = FALSE;
+
+ if (p == pat_end) return NULL;
+ if (*p == '!' || *p == '^') {
+ negated = TRUE;
+ ++p;
+ }
+
+ while (*p != ']') {
+ const char *t1 = p;
+ if ((t1 = UNESCAPE(t1, pat_end)) == pat_end) return NULL;
+ if ((p = t1 + 1) == pat_end) return NULL;
+ if (p[0] == '-' && p[1] != ']') {
+ const char *t2 = p + 1;
+ if ((t2 = UNESCAPE(t2, pat_end)) == pat_end) return NULL;
+ p = t2 + 1;
+ if (!ok && CHAR_CMP(*t1, *s) <= 0 && CHAR_CMP(*s, *t2) <= 0) ok = TRUE;
+ }
+ else {
+ if (!ok && CHAR_CMP(*t1, *s) == 0) ok = TRUE;
+ }
+ }
- mrb_get_args(mrb, "o", &argv);
- t_printstr(mrb, argv);
+ return ok == negated ? NULL : p + 1;
+}
+
+static mrb_bool
+str_match_no_brace_p(const char *pat, mrb_int pat_len,
+ const char *str, mrb_int str_len)
+{
+ const char *p = pat, *s = str;
+ const char *pat_end = pat + pat_len, *str_end = str + str_len;
+ const char *p_tmp = NULL, *s_tmp = NULL;
+
+ for (;;) {
+ if (p == pat_end) return s == str_end;
+ switch (*p) {
+ case '*':
+ do { ++p; } while (p != pat_end && *p == '*');
+ if (UNESCAPE(p, pat_end) == pat_end) return TRUE;
+ if (s == str_end) return FALSE;
+ p_tmp = p;
+ s_tmp = s;
+ continue;
+ case '?':
+ if (s == str_end) return FALSE;
+ ++p;
+ ++s;
+ continue;
+ case '[': {
+ const char *t;
+ if (s == str_end) return FALSE;
+ if ((t = str_match_bracket(p+1, pat_end, s, str_end))) {
+ p = t;
+ ++s;
+ continue;
+ }
+ goto L_failed;
+ }
+ }
+
+ /* ordinary */
+ p = UNESCAPE(p, pat_end);
+ if (s == str_end) return p == pat_end;
+ if (p == pat_end) goto L_failed;
+ if (*p++ != *s++) goto L_failed;
+ continue;
+
+ L_failed:
+ if (p_tmp && s_tmp) {
+ /* try next '*' position */
+ p = p_tmp;
+ s = ++s_tmp;
+ continue;
+ }
+
+ return FALSE;
+ }
+}
+
+#define COPY_AND_INC(dst, src, len) \
+ do { memcpy(dst, src, len); dst += len; } while (0)
+
+static mrb_bool
+str_match_p(mrb_state *mrb,
+ const char *pat, mrb_int pat_len,
+ const char *str, mrb_int str_len)
+{
+ const char *p = pat, *pat_end = pat + pat_len;
+ const char *lbrace = NULL, *rbrace = NULL;
+ int nest = 0;
+ mrb_bool ret = FALSE;
+
+ for (; p != pat_end; ++p) {
+ if (*p == '{' && nest++ == 0) lbrace = p;
+ else if (*p == '}' && lbrace && --nest == 0) { rbrace = p; break; }
+ else if (*p == '\\' && ++p == pat_end) break;
+ }
+
+ if (lbrace && rbrace) {
+ /* expand brace */
+ char *ex_pat = (char *)mrb_malloc(mrb, pat_len-2); /* expanded pattern */
+ char *ex_p = ex_pat;
+
+ COPY_AND_INC(ex_p, pat, lbrace-pat);
+ p = lbrace;
+ while (p < rbrace) {
+ char *orig_ex_p = ex_p;
+ const char *t = ++p;
+ for (nest = 0; p < rbrace && !(*p == ',' && nest == 0); ++p) {
+ if (*p == '{') ++nest;
+ else if (*p == '}') --nest;
+ else if (*p == '\\' && ++p == rbrace) break;
+ }
+ COPY_AND_INC(ex_p, t, p-t);
+ COPY_AND_INC(ex_p, rbrace+1, pat_end-rbrace-1);
+ if ((ret = str_match_p(mrb, ex_pat, ex_p-ex_pat, str, str_len))) break;
+ ex_p = orig_ex_p;
+ }
+ mrb_free(mrb, ex_pat);
+ }
+ else if (!lbrace && !rbrace) {
+ ret = str_match_no_brace_p(pat, pat_len, str, str_len);
+ }
+
+ return ret;
+}
+
+static mrb_value
+m_str_match_p(mrb_state *mrb, mrb_value self)
+{
+ const char *pat, *str;
+ mrb_int pat_len, str_len;
- return argv;
+ mrb_get_args(mrb, "ss", &pat, &pat_len, &str, &str_len);
+ return mrb_bool_value(str_match_p(mrb, pat, pat_len, str, str_len));
}
void
@@ -87,7 +214,8 @@ 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));
+ mrb_define_method(mrb, krn, "t_print", t_print, MRB_ARGS_ANY());
+ mrb_define_method(mrb, krn, "_str_match?", m_str_match_p, MRB_ARGS_REQ(2));
mrbtest = mrb_define_module(mrb, "Mrbtest");
@@ -130,6 +258,7 @@ mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src)
TEST_COUNT_PASS(ok_test);
TEST_COUNT_PASS(ko_test);
TEST_COUNT_PASS(kill_test);
+ TEST_COUNT_PASS(skip_test);
#undef TEST_COUNT_PASS
@@ -167,7 +296,8 @@ main(int argc, char **argv)
}
mrb_init_test_driver(mrb, verbose);
- mrb_init_mrbtest(mrb);
+ mrb_load_irep(mrb, mrbtest_assert_irep);
+ mrbgemtest_init(mrb);
ret = eval_test(mrb);
mrb_close(mrb);
diff --git a/mrbgems/mruby-test/init_mrbtest.c b/mrbgems/mruby-test/init_mrbtest.c
deleted file mode 100644
index 7678a5200..000000000
--- a/mrbgems/mruby-test/init_mrbtest.c
+++ /dev/null
@@ -1,39 +0,0 @@
-#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);
- mrb_close(mrb);
- exit(EXIT_FAILURE);
- }
- mrb_close(core_test);
-}
-
diff --git a/mrbgems/mruby-test/mrbgem.rake b/mrbgems/mruby-test/mrbgem.rake
index 7da0b5e50..dcb7bb719 100644
--- a/mrbgems/mruby-test/mrbgem.rake
+++ b/mrbgems/mruby-test/mrbgem.rake
@@ -7,23 +7,16 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
spec.add_dependency('mruby-compiler', :core => 'mruby-compiler')
spec.test_rbfiles = Dir.glob("#{MRUBY_ROOT}/test/t/*.rb")
- if build.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT")
- spec.test_rbfiles.delete("#{MRUBY_ROOT}/test/t/float.rb")
- end
-
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"
+ # driver = "#{spec.dir}/driver.c"
assert_c = "#{build_dir}/assert.c"
assert_rb = "#{MRUBY_ROOT}/test/assert.rb"
@@ -31,7 +24,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
mrbtest_objs << assert_lib
file assert_lib => assert_c
- file assert_c => assert_rb do |t|
+ file assert_c => [assert_rb, build.mrbcfile] do |t|
open(t.name, 'w') do |f|
mrbc.run f, assert_rb, 'mrbtest_assert_irep'
end
@@ -45,7 +38,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
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|
+ file g.test_rbireps => [g.test_rbfiles, build.mrbcfile].flatten do |t|
FileUtils.mkdir_p File.dirname(t.name)
open(t.name, 'w') do |f|
g.print_gem_test_header(f)
@@ -140,32 +133,36 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
end
unless build.build_mrbtest_lib_only?
- file exec => [driver_obj, mlib, mrbtest_lib, libmruby_core, libmruby] do |t|
+ file exec => [driver_obj, mlib, mrbtest_lib, build.libmruby_static] 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
+ build.linker.run t.name, t.prerequisites, gem_libraries, gem_library_paths, gem_flags,
+ gem_flags_before_libraries, gem_flags_after_libraries
end
end
- init = "#{spec.dir}/init_mrbtest.c"
-
# store the last gem selection and make the re-build
# of the test gem depending on a change to the gem
# selection
- active_gems = "#{build_dir}/active_gems.lst"
- FileUtils.mkdir_p File.dirname(active_gems)
- open(active_gems, 'w+') do |f|
- build.gems.each do |g|
- f.puts g.name
- end
+ active_gems_path = "#{build_dir}/active_gems_path.lst"
+ active_gem_list = if File.exist? active_gems_path
+ File.read active_gems_path
+ else
+ FileUtils.mkdir_p File.dirname(active_gems_path)
+ nil
+ end
+ current_gem_list = build.gems.map(&:name).join("\n")
+ task active_gems_path do |_t|
+ FileUtils.mkdir_p File.dirname(active_gems_path)
+ File.write active_gems_path, current_gem_list
end
- file clib => active_gems
+ file clib => active_gems_path if active_gem_list != current_gem_list
file mlib => clib
- file clib => init do |t|
+ file clib => [build.mrbcfile, __FILE__] do |_t|
_pp "GEN", "*.rb", "#{clib.relative_path}"
FileUtils.mkdir_p File.dirname(clib)
open(clib, 'w') do |f|
@@ -178,7 +175,8 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
f.puts %Q[ * All manual changes will get lost.]
f.puts %Q[ */]
f.puts %Q[]
- f.puts IO.read(init)
+ f.puts %Q[struct mrb_state;]
+ f.puts %Q[typedef struct mrb_state mrb_state;]
build.gems.each do |g|
f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);]
end
diff --git a/mrbgems/mruby-time/include/mruby/time.h b/mrbgems/mruby-time/include/mruby/time.h
new file mode 100644
index 000000000..d71f4ccd3
--- /dev/null
+++ b/mrbgems/mruby-time/include/mruby/time.h
@@ -0,0 +1,25 @@
+/*
+** mruby/time.h - Time class
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_TIME_H
+#define MRUBY_TIME_H
+
+#include "mruby/common.h"
+
+MRB_BEGIN_DECL
+
+typedef enum mrb_timezone {
+ MRB_TIMEZONE_NONE = 0,
+ MRB_TIMEZONE_UTC = 1,
+ MRB_TIMEZONE_LOCAL = 2,
+ MRB_TIMEZONE_LAST = 3
+} mrb_timezone;
+
+MRB_API mrb_value mrb_time_at(mrb_state *mrb, double sec, double usec, mrb_timezone timezone);
+
+MRB_END_DECL
+
+#endif /* MRUBY_TIME_H */
diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c
index cfd51ac63..4f0afd6c6 100644
--- a/mrbgems/mruby-time/src/time.c
+++ b/mrbgems/mruby-time/src/time.c
@@ -4,11 +4,16 @@
** See Copyright Notice in mruby.h
*/
+#ifdef MRB_WITHOUT_FLOAT
+# error Conflict 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
#include <math.h>
#include <time.h>
#include <mruby.h>
#include <mruby/class.h>
#include <mruby/data.h>
+#include <mruby/time.h>
#ifndef MRB_DISABLE_STDIO
#include <stdio.h>
@@ -17,6 +22,7 @@
#endif
#define NDIV(x,y) (-(-((x)+1)/(y))-1)
+#define TO_S_FMT "%Y-%m-%d %H:%M:%S "
#if defined(_MSC_VER) && _MSC_VER < 1800
double round(double x) {
@@ -46,7 +52,7 @@ double round(double x) {
/* #define NO_GMTIME_R */
#ifdef _WIN32
-#if _MSC_VER
+#ifdef _MSC_VER
/* Win32 platform do not provide gmtime_r/localtime_r; emulate them using gmtime_s/localtime_s */
#define gmtime_r(tp, tm) ((gmtime_s((tm), (tp)) == 0) ? (tm) : NULL)
#define localtime_r(tp, tm) ((localtime_s((tm), (tp)) == 0) ? (tm) : NULL)
@@ -166,13 +172,6 @@ timegm(struct tm *tm)
* second level. Also, there are only 2 timezones, namely UTC and LOCAL.
*/
-enum mrb_timezone {
- MRB_TIMEZONE_NONE = 0,
- MRB_TIMEZONE_UTC = 1,
- MRB_TIMEZONE_LOCAL = 2,
- MRB_TIMEZONE_LAST = 3
-};
-
typedef struct mrb_timezone_name {
const char name[8];
size_t len;
@@ -204,20 +203,25 @@ struct mrb_time {
static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
/** Updates the datetime of a mrb_time based on it's timezone and
-seconds setting. Returns self on success, NULL of failure. */
+ seconds setting. Returns self on success, NULL of failure.
+ if `dealloc` is set `true`, it frees `self` on error. */
static struct mrb_time*
-time_update_datetime(mrb_state *mrb, struct mrb_time *self)
+time_update_datetime(mrb_state *mrb, struct mrb_time *self, int dealloc)
{
struct tm *aid;
+ time_t t = self->sec;
if (self->timezone == MRB_TIMEZONE_UTC) {
- aid = gmtime_r(&self->sec, &self->datetime);
+ aid = gmtime_r(&t, &self->datetime);
}
else {
- aid = localtime_r(&self->sec, &self->datetime);
+ aid = localtime_r(&t, &self->datetime);
}
if (!aid) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec));
+ mrb_float sec = (mrb_float)t;
+
+ if (dealloc) mrb_free(mrb, self);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
/* not reached */
return NULL;
}
@@ -269,17 +273,17 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
tm->sec = tsec;
tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec);
if (tm->usec < 0) {
- long sec2 = (long)NDIV(usec,1000000); /* negative div */
+ long sec2 = (long)NDIV(tm->usec,1000000); /* negative div */
tm->usec -= sec2 * 1000000;
tm->sec += sec2;
}
else if (tm->usec >= 1000000) {
- long sec2 = (long)(usec / 1000000);
+ long sec2 = (long)(tm->usec / 1000000);
tm->usec -= sec2 * 1000000;
tm->sec += sec2;
}
tm->timezone = timezone;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, TRUE);
return tm;
}
@@ -293,24 +297,24 @@ mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mr
static struct mrb_time*
current_mrb_time(mrb_state *mrb)
{
+ struct mrb_time tmzero = {0};
struct mrb_time *tm;
+ time_t sec, usec;
- tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
-#if defined(TIME_UTC)
+#if defined(TIME_UTC) && !defined(__ANDROID__)
{
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;
+ sec = ts.tv_sec;
+ usec = ts.tv_nsec / 1000;
}
#elif defined(NO_GETTIMEOFDAY)
{
static time_t last_sec = 0, last_usec = 0;
- tm->sec = time(NULL);
+ sec = time(NULL);
if (tm->sec != last_sec) {
last_sec = tm->sec;
last_usec = 0;
@@ -319,19 +323,22 @@ current_mrb_time(mrb_state *mrb)
/* add 1 usec to differentiate two times */
last_usec += 1;
}
- tm->usec = last_usec;
+ usec = last_usec;
}
#else
{
struct timeval tv;
gettimeofday(&tv, NULL);
- tm->sec = tv.tv_sec;
- tm->usec = tv.tv_usec;
+ sec = tv.tv_sec;
+ usec = tv.tv_usec;
}
#endif
+ tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
+ *tm = tmzero;
+ tm->sec = sec; tm->usec = usec;
tm->timezone = MRB_TIMEZONE_LOCAL;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, TRUE);
return tm;
}
@@ -343,10 +350,16 @@ mrb_time_now(mrb_state *mrb, mrb_value self)
return mrb_time_wrap(mrb, mrb_class_ptr(self), current_mrb_time(mrb));
}
+MRB_API mrb_value
+mrb_time_at(mrb_state *mrb, double sec, double usec, enum mrb_timezone zone)
+{
+ return mrb_time_make(mrb, mrb_class_get(mrb, "Time"), sec, usec, zone);
+}
+
/* 15.2.19.6.1 */
/* Creates an instance of time at the given time in seconds, etc. */
static mrb_value
-mrb_time_at(mrb_state *mrb, mrb_value self)
+mrb_time_at_m(mrb_state *mrb, mrb_value self)
{
mrb_float f, f2 = 0;
@@ -572,10 +585,9 @@ mrb_time_asctime(mrb_state *mrb, mrb_value self)
#else
char buf[256];
- len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d",
+ len = snprintf(buf, sizeof(buf), "%s %s %2d %02d:%02d:%02d %.4d",
wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday,
d->tm_hour, d->tm_min, d->tm_sec,
- tm->timezone == MRB_TIMEZONE_UTC ? "UTC " : "",
d->tm_year + 1900);
#endif
return mrb_str_new(mrb, buf, len);
@@ -616,7 +628,7 @@ mrb_time_getutc(mrb_state *mrb, mrb_value self)
tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
*tm2 = *tm;
tm2->timezone = MRB_TIMEZONE_UTC;
- time_update_datetime(mrb, tm2);
+ time_update_datetime(mrb, tm2, TRUE);
return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
}
@@ -631,7 +643,7 @@ mrb_time_getlocal(mrb_state *mrb, mrb_value self)
tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm));
*tm2 = *tm;
tm2->timezone = MRB_TIMEZONE_LOCAL;
- time_update_datetime(mrb, tm2);
+ time_update_datetime(mrb, tm2, TRUE);
return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2);
}
@@ -709,7 +721,7 @@ mrb_time_localtime(mrb_state *mrb, mrb_value self)
tm = time_get_ptr(mrb, self);
tm->timezone = MRB_TIMEZONE_LOCAL;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, FALSE);
return self;
}
@@ -757,7 +769,6 @@ mrb_time_sec(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(tm->datetime.tm_sec);
}
-
/* 15.2.19.7.24 */
/* Returns a Float with the time since the epoch in seconds. */
static mrb_value
@@ -806,7 +817,7 @@ mrb_time_utc(mrb_state *mrb, mrb_value self)
tm = time_get_ptr(mrb, self);
tm->timezone = MRB_TIMEZONE_UTC;
- time_update_datetime(mrb, tm);
+ time_update_datetime(mrb, tm, FALSE);
return self;
}
@@ -821,6 +832,15 @@ mrb_time_utc_p(mrb_state *mrb, mrb_value self)
return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC);
}
+static mrb_value
+mrb_time_to_s(mrb_state *mrb, mrb_value self)
+{
+ char buf[64];
+ struct mrb_time *tm = time_get_ptr(mrb, self);
+ const char *fmt = tm->timezone == MRB_TIMEZONE_UTC ? TO_S_FMT "UTC" : TO_S_FMT "%z";
+ size_t len = strftime(buf, sizeof(buf), fmt, &tm->datetime);
+ return mrb_str_new(mrb, buf, len);
+}
void
mrb_mruby_time_gem_init(mrb_state* mrb)
@@ -830,7 +850,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_ARG(1, 1)); /* 15.2.19.6.1 */
+ mrb_define_class_method(mrb, tc, "at", mrb_time_at_m, 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 */
@@ -841,8 +861,8 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
mrb_define_method(mrb, tc, "<=>" , mrb_time_cmp , MRB_ARGS_REQ(1)); /* 15.2.19.7.1 */
mrb_define_method(mrb, tc, "+" , mrb_time_plus , MRB_ARGS_REQ(1)); /* 15.2.19.7.2 */
mrb_define_method(mrb, tc, "-" , mrb_time_minus , MRB_ARGS_REQ(1)); /* 15.2.19.7.3 */
- mrb_define_method(mrb, tc, "to_s" , mrb_time_asctime, MRB_ARGS_NONE());
- mrb_define_method(mrb, tc, "inspect", mrb_time_asctime, MRB_ARGS_NONE());
+ mrb_define_method(mrb, tc, "to_s" , mrb_time_to_s , MRB_ARGS_NONE());
+ mrb_define_method(mrb, tc, "inspect", mrb_time_to_s , MRB_ARGS_NONE());
mrb_define_method(mrb, tc, "asctime", mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.4 */
mrb_define_method(mrb, tc, "ctime" , mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.5 */
mrb_define_method(mrb, tc, "day" , mrb_time_day , MRB_ARGS_NONE()); /* 15.2.19.7.6 */
diff --git a/mrbgems/mruby-time/test/time.rb b/mrbgems/mruby-time/test/time.rb
index 54c446ca3..f59e27bc1 100644
--- a/mrbgems/mruby-time/test/time.rb
+++ b/mrbgems/mruby-time/test/time.rb
@@ -2,11 +2,11 @@
# Time ISO Test
assert('Time.new', '15.2.3.3.3') do
- Time.new.class == Time
+ assert_equal(Time, Time.new.class)
end
assert('Time', '15.2.19') do
- Time.class == Class
+ assert_equal(Class, Time.class)
end
assert('Time.at', '15.2.19.6.1') do
@@ -21,30 +21,58 @@ assert('Time.at', '15.2.19.6.1') do
end
assert('Time.gm', '15.2.19.6.2') do
- Time.gm(2012, 12, 23)
+ t = Time.gm(2012, 9, 23)
+ assert_operator(2012, :eql?, t.year)
+ assert_operator( 9, :eql?, t.month)
+ assert_operator( 23, :eql?, t.day)
+ assert_operator( 0, :eql?, t.hour)
+ assert_operator( 0, :eql?, t.min)
+ assert_operator( 0, :eql?, t.sec)
+ assert_operator( 0, :eql?, t.usec)
end
assert('Time.local', '15.2.19.6.3') do
- Time.local(2012, 12, 23)
+ t = Time.local(2014, 12, 27, 18)
+ assert_operator(2014, :eql?, t.year)
+ assert_operator( 12, :eql?, t.month)
+ assert_operator( 27, :eql?, t.day)
+ assert_operator( 18, :eql?, t.hour)
+ assert_operator( 0, :eql?, t.min)
+ assert_operator( 0, :eql?, t.sec)
+ assert_operator( 0, :eql?, t.usec)
end
assert('Time.mktime', '15.2.19.6.4') do
- Time.mktime(2012, 12, 23)
+ t = Time.mktime(2013, 10, 4, 6, 15, 58, 3485)
+ assert_operator(2013, :eql?, t.year)
+ assert_operator( 10, :eql?, t.month)
+ assert_operator( 4, :eql?, t.day)
+ assert_operator( 6, :eql?, t.hour)
+ assert_operator( 15, :eql?, t.min)
+ assert_operator( 58, :eql?, t.sec)
+ assert_operator(3485, :eql?, t.usec)
end
assert('Time.now', '15.2.19.6.5') do
- Time.now.class == Time
+ assert_equal(Time, Time.now.class)
end
assert('Time.utc', '15.2.19.6.6') do
- Time.utc(2012, 12, 23)
+ t = Time.utc(2034)
+ assert_operator(2034, :eql?, t.year)
+ assert_operator( 1, :eql?, t.month)
+ assert_operator( 1, :eql?, t.day)
+ assert_operator( 0, :eql?, t.hour)
+ assert_operator( 0, :eql?, t.min)
+ assert_operator( 0, :eql?, t.sec)
+ assert_operator( 0, :eql?, t.usec)
end
assert('Time#+', '15.2.19.7.1') do
t1 = Time.at(1300000000.0)
t2 = t1.+(60)
- assert_equal(t2.utc.asctime, "Sun Mar 13 07:07:40 UTC 2011")
+ assert_equal("Sun Mar 13 07:07:40 2011", t2.utc.asctime)
assert_raise(FloatDomainError) { Time.at(0) + Float::NAN }
assert_raise(FloatDomainError) { Time.at(0) + Float::INFINITY }
@@ -55,7 +83,7 @@ assert('Time#-', '15.2.19.7.2') do
t1 = Time.at(1300000000.0)
t2 = t1.-(60)
- assert_equal(t2.utc.asctime, "Sun Mar 13 07:05:40 UTC 2011")
+ assert_equal("Sun Mar 13 07:05:40 2011", t2.utc.asctime)
assert_raise(FloatDomainError) { Time.at(0) - Float::NAN }
assert_raise(FloatDomainError) { Time.at(0) - Float::INFINITY }
@@ -67,30 +95,30 @@ assert('Time#<=>', '15.2.19.7.3') do
t2 = Time.at(1400000000.0)
t3 = Time.at(1500000000.0)
- t2.<=>(t1) == 1 and
- t2.<=>(t2) == 0 and
- t2.<=>(t3) == -1 and
- t2.<=>(nil) == nil
+ assert_equal(1, t2 <=> t1)
+ assert_equal(0, t2 <=> t2)
+ assert_equal(-1, t2 <=> t3)
+ assert_nil(t2 <=> nil)
end
assert('Time#asctime', '15.2.19.7.4') do
- Time.at(1300000000.0).utc.asctime == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("Thu Mar 4 05:06:07 1982", Time.gm(1982,3,4,5,6,7).asctime)
end
assert('Time#ctime', '15.2.19.7.5') do
- Time.at(1300000000.0).utc.ctime == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("Thu Oct 24 15:26:47 2013", Time.gm(2013,10,24,15,26,47).ctime)
end
assert('Time#day', '15.2.19.7.6') do
- Time.gm(2012, 12, 23).day == 23
+ assert_equal(23, Time.gm(2012, 12, 23).day)
end
assert('Time#dst?', '15.2.19.7.7') do
- not Time.gm(2012, 12, 23).utc.dst?
+ assert_not_predicate(Time.gm(2012, 12, 23).utc, :dst?)
end
assert('Time#getgm', '15.2.19.7.8') do
- Time.at(1300000000.0).getgm.asctime == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("Sun Mar 13 07:06:40 2011", Time.at(1300000000.0).getgm.asctime)
end
assert('Time#getlocal', '15.2.19.7.9') do
@@ -98,114 +126,120 @@ assert('Time#getlocal', '15.2.19.7.9') do
t2 = Time.at(1300000000.0)
t3 = t1.getlocal
- t1 == t3 and t3 == t2.getlocal
+ assert_equal(t1, t3)
+ assert_equal(t3, t2.getlocal)
end
assert('Time#getutc', '15.2.19.7.10') do
- Time.at(1300000000.0).getutc.asctime == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("Sun Mar 13 07:06:40 2011", Time.at(1300000000.0).getutc.asctime)
end
assert('Time#gmt?', '15.2.19.7.11') do
- Time.at(1300000000.0).utc.gmt?
+ assert_predicate(Time.at(1300000000.0).utc, :gmt?)
end
# ATM not implemented
# assert('Time#gmt_offset', '15.2.19.7.12') do
assert('Time#gmtime', '15.2.19.7.13') do
- Time.at(1300000000.0).gmtime
+ t = Time.now
+ assert_predicate(t.gmtime, :gmt?)
+ assert_predicate(t, :gmt?)
end
# ATM not implemented
# assert('Time#gmtoff', '15.2.19.7.14') do
assert('Time#hour', '15.2.19.7.15') do
- Time.gm(2012, 12, 23, 7, 6).hour == 7
+ assert_equal(7, Time.gm(2012, 12, 23, 7, 6).hour)
end
# ATM doesn't really work
# assert('Time#initialize', '15.2.19.7.16') do
assert('Time#initialize_copy', '15.2.19.7.17') do
- time_tmp_2 = Time.at(7.0e6)
- time_tmp_2.clone == time_tmp_2
+ t = Time.at(7.0e6)
+ assert_equal(t, t.clone)
end
assert('Time#localtime', '15.2.19.7.18') do
- t1 = Time.at(1300000000.0)
- t2 = Time.at(1300000000.0)
+ t1 = Time.utc(2014, 5 ,6)
+ t2 = Time.utc(2014, 5 ,6)
+ t3 = t2.getlocal
- t1.localtime
- t1 == t2.getlocal
+ assert_equal(t3, t1.localtime)
+ assert_equal(t3, t1)
end
assert('Time#mday', '15.2.19.7.19') do
- Time.gm(2012, 12, 23).mday == 23
+ assert_equal(23, Time.gm(2012, 12, 23).mday)
end
assert('Time#min', '15.2.19.7.20') do
- Time.gm(2012, 12, 23, 7, 6).min == 6
+ assert_equal(6, Time.gm(2012, 12, 23, 7, 6).min)
end
assert('Time#mon', '15.2.19.7.21') do
- Time.gm(2012, 12, 23).mon == 12
+ assert_equal(12, Time.gm(2012, 12, 23).mon)
end
assert('Time#month', '15.2.19.7.22') do
- Time.gm(2012, 12, 23).month == 12
+ assert_equal(12, Time.gm(2012, 12, 23).month)
end
assert('Times#sec', '15.2.19.7.23') do
- Time.gm(2012, 12, 23, 7, 6, 40).sec == 40
+ assert_equal(40, Time.gm(2012, 12, 23, 7, 6, 40).sec)
end
assert('Time#to_f', '15.2.19.7.24') do
- Time.at(1300000000.0).to_f == 1300000000.0
+ assert_operator(2.0, :eql?, Time.at(2).to_f)
end
assert('Time#to_i', '15.2.19.7.25') do
- Time.at(1300000000.0).to_i == 1300000000
+ assert_operator(2, :eql?, Time.at(2.0).to_i)
end
assert('Time#usec', '15.2.19.7.26') do
- Time.at(1300000000.0).usec == 0
+ assert_equal(0, Time.at(1300000000.0).usec)
end
assert('Time#utc', '15.2.19.7.27') do
- Time.at(1300000000.0).utc
+ t = Time.now
+ assert_predicate(t.utc, :gmt?)
+ assert_predicate(t, :gmt?)
end
assert('Time#utc?', '15.2.19.7.28') do
- Time.at(1300000000.0).utc.utc?
+ assert_predicate(Time.at(1300000000.0).utc, :utc?)
end
# ATM not implemented
# assert('Time#utc_offset', '15.2.19.7.29') do
assert('Time#wday', '15.2.19.7.30') do
- Time.gm(2012, 12, 23).wday == 0
+ assert_equal(0, Time.gm(2012, 12, 23).wday)
end
assert('Time#yday', '15.2.19.7.31') do
- Time.gm(2012, 12, 23).yday == 358
+ assert_equal(358, Time.gm(2012, 12, 23).yday)
end
assert('Time#year', '15.2.19.7.32') do
- Time.gm(2012, 12, 23).year == 2012
+ assert_equal(2012, Time.gm(2012, 12, 23).year)
end
assert('Time#zone', '15.2.19.7.33') do
- Time.at(1300000000.0).utc.zone == 'UTC'
+ assert_equal('UTC', Time.at(1300000000.0).utc.zone)
end
# Not ISO specified
assert('Time#to_s') do
- Time.at(1300000000.0).utc.to_s == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_equal("2003-04-05 06:07:08 UTC", Time.gm(2003,4,5,6,7,8,9).to_s)
end
assert('Time#inspect') do
- Time.at(1300000000.0).utc.inspect == "Sun Mar 13 07:06:40 UTC 2011"
+ assert_match("2013-10-28 16:27:48 [^U]*", Time.local(2013,10,28,16,27,48).inspect)
end
assert('day of week methods') do
@@ -224,7 +258,7 @@ assert('2000 times 500us make a second') do
2000.times do
t += 0.0005
end
- t.usec == 0
+ assert_equal(0, t.usec)
end
assert('Time.gm with Dec 31 23:59:59 1969 raise ArgumentError') do
diff --git a/mrbgems/mruby-toplevel-ext/test/toplevel.rb b/mrbgems/mruby-toplevel-ext/test/toplevel.rb
index aebdd8b4b..26aae9a7d 100644
--- a/mrbgems/mruby-toplevel-ext/test/toplevel.rb
+++ b/mrbgems/mruby-toplevel-ext/test/toplevel.rb
@@ -16,8 +16,8 @@ assert('Toplevel#include') do
self.include ToplevelTestModule2, ToplevelTestModule1
- assert_true self.class.included_modules.include?( ToplevelTestModule1 )
- assert_true self.class.included_modules.include?( ToplevelTestModule2 )
+ assert_true self.class.ancestors.include?( ToplevelTestModule1 )
+ assert_true self.class.ancestors.include?( ToplevelTestModule2 )
assert_equal :foo, method_foo
assert_equal :bar2, CONST_BAR
end
diff --git a/mrblib/array.rb b/mrblib/array.rb
index bc9a2a492..2b080564c 100644
--- a/mrblib/array.rb
+++ b/mrblib/array.rb
@@ -66,7 +66,7 @@ class Array
#
# ISO 15.2.12.5.15
def initialize(size=0, obj=nil, &block)
- raise TypeError, "expected Integer for 1st argument" unless size.kind_of? Integer
+ size = size.__to_int
raise ArgumentError, "negative array size" if size < 0
self.clear
@@ -84,10 +84,17 @@ class Array
end
def _inspect(recur_list)
- return "[]" if self.size == 0
+ size = self.size
+ return "[]" if size == 0
return "[...]" if recur_list[self.object_id]
recur_list[self.object_id] = true
- "["+self.map{|x|x._inspect(recur_list)}.join(", ")+"]"
+ ary=[]
+ i=0
+ while i<size
+ ary<<self[i]._inspect(recur_list)
+ i+=1
+ end
+ "["+ary.join(", ")+"]"
end
##
# Return the contents of this array as a string.
@@ -191,43 +198,69 @@ class Array
include Enumerable
##
- # Quick sort
- # left : the beginning of sort region
- # right : the end of sort region
- def __sort_sub__(left, right, &block)
- stack = [ [left, right] ]
+ # Sort all elements and replace +self+ with these
+ # elements.
+ def sort!(&block)
+ stack = [ [ 0, self.size - 1 ] ]
until stack.empty?
- left, right = stack.pop
- if left < right
- i = left
- j = right
- pivot = self[i + (j - i) / 2]
- while true
- while ((block)? block.call(self[i], pivot): (self[i] <=> pivot)) < 0
- i += 1
+ left, mid, right = stack.pop
+ if right == nil
+ right = mid
+ # sort self[left..right]
+ if left < right
+ if left + 1 == right
+ lval = self[left]
+ rval = self[right]
+ cmp = if block then block.call(lval,rval) else lval <=> rval end
+ if cmp.nil?
+ raise ArgumentError, "comparison of #{lval.inspect} and #{rval.inspect} failed"
+ end
+ if cmp > 0
+ self[left] = rval
+ self[right] = lval
+ end
+ else
+ mid = ((left + right + 1) / 2).floor
+ stack.push [ left, mid, right ]
+ stack.push [ mid, right ]
+ stack.push [ left, (mid - 1) ] if left < mid - 1
end
- while ((block)? block.call(pivot, self[j]): (pivot <=> self[j])) < 0
- j -= 1
- end
- break if (i >= j)
- tmp = self[i]; self[i] = self[j]; self[j] = tmp;
- i += 1
- j -= 1
end
- stack.push [left, i-1]
- stack.push [j+1, right]
- end
- end
- end
- # private :__sort_sub__
+ else
+ lary = self[left, mid - left]
+ lsize = lary.size
- ##
- # Sort all elements and replace +self+ with these
- # elements.
- def sort!(&block)
- size = self.size
- if size > 1
- __sort_sub__(0, size - 1, &block)
+ # The entity sharing between lary and self may cause a large memory
+ # copy operation in the merge loop below. This harmless operation
+ # cancels the sharing and provides a huge performance gain.
+ lary[0] = lary[0]
+
+ # merge
+ lidx = 0
+ ridx = mid
+ (left..right).each { |i|
+ if lidx >= lsize
+ break
+ elsif ridx > right
+ self[i, lsize - lidx] = lary[lidx, lsize - lidx]
+ break
+ else
+ lval = lary[lidx]
+ rval = self[ridx]
+ cmp = if block then block.call(lval,rval) else lval <=> rval end
+ if cmp.nil?
+ raise ArgumentError, "comparison of #{lval.inspect} and #{rval.inspect} failed"
+ end
+ if cmp <= 0
+ self[i] = lval
+ lidx += 1
+ else
+ self[i] = rval
+ ridx += 1
+ end
+ end
+ }
+ end
end
self
end
diff --git a/mrblib/enum.rb b/mrblib/enum.rb
index a97a26f97..fe40b4d27 100644
--- a/mrblib/enum.rb
+++ b/mrblib/enum.rb
@@ -13,6 +13,8 @@
# @ISO 15.3.2
module Enumerable
+ NONE = Object.new
+
##
# Call the given block for each element
# which is yield by +each+. Return false
@@ -63,22 +65,20 @@ module Enumerable
end
##
- # Call the given block for each element
- # which is yield by +each+. Return
- # +ifnone+ if no block value was true.
- # Otherwise return the first block value
- # which had was true.
+ # Return the first element for which
+ # value from the block is true. If no
+ # object matches, calls +ifnone+ and
+ # returns its result. Otherwise returns
+ # +nil+.
#
# ISO 15.3.2.2.4
def detect(ifnone=nil, &block)
- ret = ifnone
self.each{|*val|
if block.call(*val)
- ret = val.__svalue
- break
+ return val.__svalue
end
}
- ret
+ ifnone.call unless ifnone.nil?
end
##
diff --git a/mrblib/float.rb b/mrblib/float.rb
deleted file mode 100644
index 2b86dc1e5..000000000
--- a/mrblib/float.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-##
-# Float
-#
-# ISO 15.2.9
-class Float
- # mruby special - since mruby integers may be upgraded to floats,
- # floats should be compatible to integers.
- include Integral
-end if class_defined?("Float")
diff --git a/mrblib/hash.rb b/mrblib/hash.rb
index 6fcbba173..0e2da62e1 100644
--- a/mrblib/hash.rb
+++ b/mrblib/hash.rb
@@ -12,9 +12,7 @@ class Hash
# ISO 15.2.13.4.1
def ==(hash)
return true if self.equal?(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
+ unless Hash === hash
return false
end
return false if self.size != hash.size
@@ -32,9 +30,7 @@ class Hash
# ISO 15.2.13.4.32 (x)
def eql?(hash)
return true if self.equal?(hash)
- begin
- hash = hash.to_hash
- rescue NoMethodError
+ unless Hash === hash
return false
end
return false if self.size != hash.size
@@ -55,10 +51,9 @@ class Hash
# ISO 15.2.13.4.8
def delete(key, &block)
if block && !self.has_key?(key)
- block.call(key)
- else
- self.__delete(key)
+ return block.call(key)
end
+ self.__delete(key)
end
##
@@ -154,9 +149,8 @@ class Hash
#
# ISO 15.2.13.4.23
def replace(hash)
- raise TypeError, "can't convert argument into Hash" unless hash.respond_to?(:to_hash)
+ raise TypeError, "Hash required (#{hash.class} given)" unless Hash === hash
self.clear
- hash = hash.to_hash
hash.each_key{|k|
self[k] = hash[k]
}
@@ -179,8 +173,7 @@ class Hash
#
# ISO 15.2.13.4.22
def merge(other, &block)
- raise TypeError, "can't convert argument into Hash" unless other.respond_to?(:to_hash)
- other = other.to_hash
+ raise TypeError, "Hash required (#{other.class} given)" unless Hash === other
h = self.dup
if block
other.each_key{|k|
@@ -197,9 +190,16 @@ class Hash
return "{}" if self.size == 0
return "{...}" if recur_list[self.object_id]
recur_list[self.object_id] = true
- "{"+self.map {|k,v|
- k._inspect(recur_list) + "=>" + v._inspect(recur_list)
- }.join(", ")+"}"
+ ary=[]
+ keys=self.keys
+ size=keys.size
+ i=0
+ while i<size
+ k=keys[i]
+ ary<<(k._inspect + "=>" + self[k]._inspect)
+ i+=1
+ end
+ "{"+ary.join(", ")+"}"
end
##
# Return the contents of this hash as a string.
@@ -316,34 +316,6 @@ 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
- end
end
##
diff --git a/mrblib/kernel.rb b/mrblib/kernel.rb
index 3fb324908..7c3ea9420 100644
--- a/mrblib/kernel.rb
+++ b/mrblib/kernel.rb
@@ -6,7 +6,7 @@ module Kernel
# 15.3.1.2.1 Kernel.`
# provided by Kernel#`
- # 15.3.1.3.5
+ # 15.3.1.3.3
def `(s)
raise NotImplementedError.new("backquotes not implemented")
end
diff --git a/mrblib/mrblib.rake b/mrblib/mrblib.rake
index fe4aae1f7..e96decb27 100644
--- a/mrblib/mrblib.rake
+++ b/mrblib/mrblib.rake
@@ -3,14 +3,11 @@ MRuby.each_target do
relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT)
current_build_dir = "#{build_dir}/#{relative_from_root}"
- self.libmruby << objfile("#{current_build_dir}/mrblib")
+ self.libmruby_objs << objfile("#{current_build_dir}/mrblib")
file objfile("#{current_build_dir}/mrblib") => "#{current_build_dir}/mrblib.c"
file "#{current_build_dir}/mrblib.c" => [mrbcfile, __FILE__] + Dir.glob("#{current_dir}/*.rb").sort do |t|
_, _, *rbfiles = t.prerequisites
- if self.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT")
- rbfiles.delete("#{current_dir}/float.rb")
- end
FileUtils.mkdir_p File.dirname(t.name)
open(t.name, 'w') do |f|
_pp "GEN", "*.rb", "#{t.name.relative_path}"
diff --git a/mrblib/numeric.rb b/mrblib/numeric.rb
index 1b11e92ad..5926518d5 100644
--- a/mrblib/numeric.rb
+++ b/mrblib/numeric.rb
@@ -104,15 +104,15 @@ module Integral
raise ArgumentError, "step can't be 0" if step == 0
return to_enum(:step, num, step) unless block
- i = if class_defined?("Float") && num.kind_of?(Float) then self.to_f else self end
- if num == nil
+ i = __coerce_step_counter(num, step)
+ if num == self || step.infinite?
+ block.call(i) if step > 0 && i <= (num||i) || step < 0 && i >= (num||-i)
+ elsif num == nil
while true
block.call(i)
- i+=step
+ i += step
end
- return self
- end
- if step > 0
+ elsif step > 0
while i <= num
block.call(i)
i += step
diff --git a/mrblib/range.rb b/mrblib/range.rb
index 5bd2521e8..392cc2274 100644
--- a/mrblib/range.rb
+++ b/mrblib/range.rb
@@ -26,7 +26,7 @@ class Range
return self
end
- if val.kind_of?(String) && last.kind_of?(String) # fixnums are special
+ if val.kind_of?(String) && last.kind_of?(String) # strings are special
if val.respond_to? :upto
return val.upto(last, exclude_end?, &block)
else
diff --git a/mrblib/string.rb b/mrblib/string.rb
index ee98cfa0c..c26cdb1e2 100644
--- a/mrblib/string.rb
+++ b/mrblib/string.rb
@@ -3,7 +3,9 @@
#
# ISO 15.2.10
class String
+ # ISO 15.2.10.3
include Comparable
+
##
# Calls the given block for each line
# and pass the respective line.
@@ -12,7 +14,7 @@ class String
def each_line(rs = "\n", &block)
return to_enum(:each_line, rs, &block) unless block
return block.call(self) if rs.nil?
- rs = rs.to_str
+ rs.__to_str
offset = 0
rs_len = rs.length
this = dup
@@ -67,7 +69,7 @@ class String
block = nil
end
if !replace.nil? || !block
- replace = replace.to_str
+ replace.__to_str
end
offset = 0
result = []
@@ -99,22 +101,19 @@ class String
raise FrozenError, "can't modify frozen String" if frozen?
return to_enum(:gsub!, *args) if args.length == 1 && !block
str = self.gsub(*args, &block)
- return nil if str == self
+ return nil unless self.index(args[0])
self.replace(str)
end
- ##
- # Calls the given block for each match of +pattern+
- # If no block is given return an array with all
- # matches of +pattern+.
- #
- # ISO 15.2.10.5.32
- def scan(reg, &block)
- ### *** TODO *** ###
- unless Object.const_defined?(:Regexp)
- raise NotImplementedError, "scan not available (yet)"
- end
- end
+# ##
+# # Calls the given block for each match of +pattern+
+# # If no block is given return an array with all
+# # matches of +pattern+.
+# #
+# # ISO 15.2.10.5.32
+# def scan(pattern, &block)
+# # TODO: String#scan is not implemented yet
+# end
##
# Replace only the first match of +pattern+ with
@@ -129,12 +128,12 @@ class String
end
pattern, replace = *args
- pattern = pattern.to_str
+ pattern.__to_str
if args.length == 2 && block
block = nil
end
unless block
- replace = replace.to_str
+ replace.__to_str
end
result = []
this = dup
@@ -161,25 +160,14 @@ class String
def sub!(*args, &block)
raise FrozenError, "can't modify frozen String" if frozen?
str = self.sub(*args, &block)
- return nil if str == self
+ return nil unless self.index(args[0])
self.replace(str)
end
##
- # Call the given block for each character of
- # +self+.
- def each_char(&block)
- pos = 0
- while pos < self.size
- block.call(self[pos])
- pos += 1
- end
- self
- end
-
- ##
# Call the given block for each byte of +self+.
def each_byte(&block)
+ return to_enum(:each_byte, &block) unless block
bytes = self.bytes
pos = 0
while pos < bytes.size
@@ -189,87 +177,16 @@ class String
self
end
- ##
- # 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
- case pos
- when 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(''))
- else
- raise IndexError, "string not matched"
- end
- when Range
- head = pos.begin
- tail = pos.end
- tail += self.length if tail < 0
- unless pos.exclude_end?
- tail += 1
- end
- return self[head, tail-head]=value
- 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(''))
- end
- return value
- 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
-
+ # those two methods requires Regexp that is optional in mruby
##
# ISO 15.2.10.5.3
- def =~(re)
- raise TypeError, "type mismatch: String given" if re.respond_to? :to_str
- re =~ self
- end
+ #def =~(re)
+ # re =~ self
+ #end
##
# ISO 15.2.10.5.27
- def match(re, &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
-
-##
-# String is comparable
-#
-# ISO 15.2.10.3
-module Comparable; end
-class String
- include Comparable
+ #def match(re, &block)
+ # re.match(self, &block)
+ #end
end
diff --git a/mrblib/symbol.rb b/mrblib/symbol.rb
new file mode 100644
index 000000000..9c981dd9e
--- /dev/null
+++ b/mrblib/symbol.rb
@@ -0,0 +1,7 @@
+class Symbol
+ def to_proc
+ ->(obj,*args,&block) do
+ obj.__send__(self, *args, &block)
+ end
+ end
+end
diff --git a/oss-fuzz/config/mruby.dict b/oss-fuzz/config/mruby.dict
new file mode 100644
index 000000000..a332d3505
--- /dev/null
+++ b/oss-fuzz/config/mruby.dict
@@ -0,0 +1,105 @@
+keyword___ENCODING__="__ENCODING__"
+keyword___FILE__="__FILE__"
+keyword___LINE__="__LINE__"
+keyword_BEGIN="BEGIN"
+keyword_END="END"
+keyword_alias="alias"
+keyword_and="and"
+keyword_begin="begin"
+keyword_break="break"
+keyword_case="case"
+keyword_class="class"
+keyword_def="def"
+keyword_do="do"
+keyword_else="else"
+keyword_elsif="elsif"
+keyword_end="end"
+keyword_ensure="ensure"
+keyword_false="false"
+keyword_for="for"
+keyword_if="if"
+keyword_in="in"
+keyword_module="module"
+keyword_next="next"
+keyword_nil="nil"
+keyword_not="not"
+keyword_or="or"
+keyword_redo="redo"
+keyword_rescue="rescue"
+keyword_retry="retry"
+keyword_return="return"
+keyword_self="self"
+keyword_super="super"
+keyword_then="then"
+keyword_true="true"
+keyword_undef="undef"
+keyword_unless="unless"
+keyword_until="until"
+keyword_when="when"
+keyword_while="while"
+keyword_yield="yield"
+
+operator_a=" !"
+operator_b=" ~"
+operator_c=" +"
+operator_d=" -"
+operator_e=" []"
+operator_f=" []="
+operator_g=" *"
+operator_h=" /"
+operator_i=" %"
+operator_j=" +-"
+operator_k=" >>"
+operator_l=" <<"
+operator_m=" &"
+operator_n=" ^"
+operator_o=" |"
+operator_p=" <="
+operator_q=" <>"
+operator_r=" >="
+operator_s=" <=>"
+operator_t=" =="
+operator_u=" ==="
+operator_v=" !="
+operator_w=" =~"
+operator_x=" !~"
+operator_y=" &&"
+operator_z=" ||"
+operator_aa=" .."
+operator_ab=" ..."
+operator_ac=" ?"
+operator_ad=" :"
+operator_ae=" ="
+operator_af=" %="
+operator_ag=" /="
+operator_ah=" -="
+operator_ai=" +="
+operator_aj=" |="
+operator_ak=" &="
+operator_al=" >>="
+operator_am=" <<="
+operator_an=" *="
+operator_ao=" &&="
+operator_ap=" ||="
+operator_aq=" **="
+operator_ar=" ^="
+operator_as=" not"
+operator_at=" or"
+operator_au=" and"
+operator_av=" if"
+operator_aw=" unless"
+operator_ax=" while"
+operator_ay=" until"
+operator_az=" begin"
+operator_ba=" end"
+
+snippet_1eq1=" 1=1"
+snippet_dollar=" $1"
+snippet_at=" @a"
+snippet_symbol=" :a"
+snippet_array=" [1,2]"
+snippet_block=" 1.times{|x| x}"
+snippet_multi=" 1*1"
+
+string_single_q=" 'a'"
+string_dbl_q=" \"a\""
diff --git a/oss-fuzz/config/mruby_fuzzer.options b/oss-fuzz/config/mruby_fuzzer.options
new file mode 100644
index 000000000..8658e71b2
--- /dev/null
+++ b/oss-fuzz/config/mruby_fuzzer.options
@@ -0,0 +1,5 @@
+[libfuzzer]
+close_fd_mask = 3
+dict = mruby.dict
+fork = 1
+only_ascii = 1
diff --git a/oss-fuzz/config/mruby_proto_fuzzer.options b/oss-fuzz/config/mruby_proto_fuzzer.options
new file mode 100644
index 000000000..4ced8516c
--- /dev/null
+++ b/oss-fuzz/config/mruby_proto_fuzzer.options
@@ -0,0 +1,4 @@
+[libfuzzer]
+close_fd_mask = 3
+dict = mruby.dict
+fork = 1
diff --git a/oss-fuzz/mruby_fuzzer.c b/oss-fuzz/mruby_fuzzer.c
new file mode 100644
index 000000000..9d3d44a5b
--- /dev/null
+++ b/oss-fuzz/mruby_fuzzer.c
@@ -0,0 +1,18 @@
+#include <stdlib.h>
+#include <string.h>
+#include <mruby.h>
+#include <mruby/compile.h>
+
+int LLVMFuzzerTestOneInput(uint8_t *Data, size_t size) {
+ if (size < 1) {
+ return 0;
+ }
+ char *code = malloc(size+1);
+ memcpy(code, Data, size);
+ code[size] = '\0';
+ mrb_state *mrb = mrb_open();
+ mrb_load_string(mrb, code);
+ mrb_close(mrb);
+ free(code);
+ return 0;
+}
diff --git a/oss-fuzz/mruby_proto_fuzzer.cpp b/oss-fuzz/mruby_proto_fuzzer.cpp
new file mode 100644
index 000000000..2999c5470
--- /dev/null
+++ b/oss-fuzz/mruby_proto_fuzzer.cpp
@@ -0,0 +1,44 @@
+#include <string>
+#include <iostream>
+#include <fstream>
+
+#include <mruby.h>
+#include <mruby/compile.h>
+
+#include "libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h"
+#include "ruby.pb.h"
+#include "proto_to_ruby.h"
+
+using namespace ruby_fuzzer;
+using namespace std;
+
+int FuzzRB(const uint8_t *Data, size_t size) {
+ mrb_value v;
+ mrb_state *mrb = mrb_open();
+ if (!mrb)
+ return 0;
+
+ char *code = (char *)malloc(size+1);
+ if (!code)
+ return 0;
+ memcpy(code, Data, size);
+ code[size] = '\0';
+
+ if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) {
+ // With libFuzzer binary run this to generate an RB file x.rb:
+ // PROTO_FUZZER_DUMP_PATH=x.rb ./a.out proto-input
+ std::ofstream of(dump_path);
+ of.write(code, size);
+ }
+ v = mrb_load_string(mrb, code);
+ mrb_close(mrb);
+
+ free(code);
+ return 0;
+}
+
+DEFINE_PROTO_FUZZER(const Function &function) {
+ protoConverter converter;
+ auto s = converter.FunctionToString(function);
+ (void)FuzzRB((const uint8_t*)s.data(), s.size());
+}
diff --git a/oss-fuzz/proto_to_ruby.cpp b/oss-fuzz/proto_to_ruby.cpp
new file mode 100644
index 000000000..92ad039e2
--- /dev/null
+++ b/oss-fuzz/proto_to_ruby.cpp
@@ -0,0 +1,455 @@
+#include "proto_to_ruby.h"
+
+using namespace ruby_fuzzer;
+
+std::string protoConverter::removeSpecial(const std::string &x)
+{
+ std::string tmp(x);
+ if (!tmp.empty())
+ tmp.erase(std::remove_if(tmp.begin(), tmp.end(),
+ [](char c) { return !(std::isalpha(c) || std::isdigit(c)); } ), tmp.end());
+ return tmp;
+}
+
+void protoConverter::visit(ArrType const& x)
+{
+ if (x.elements_size() > 0) {
+ int i = x.elements_size();
+ m_output << "[";
+ for (auto &e : x.elements()) {
+ i--;
+ if (i == 0) {
+ visit(e);
+ } else {
+ visit(e);
+ m_output << ", ";
+ }
+ }
+ m_output << "]";
+ } else {
+ m_output << "[1]";
+ }
+}
+
+void protoConverter::visit(Array const& x)
+{
+ switch (x.arr_func()) {
+ case Array::FLATTEN:
+ visit(x.arr_arg());
+ m_output << ".flatten";
+ break;
+ case Array::COMPACT:
+ visit(x.arr_arg());
+ m_output << ".compact";
+ break;
+ case Array::FETCH:
+ visit(x.arr_arg());
+ m_output << ".fetch";
+ break;
+ case Array::FILL:
+ visit(x.arr_arg());
+ m_output << ".fill";
+ break;
+ case Array::ROTATE:
+ visit(x.arr_arg());
+ m_output << ".rotate";
+ break;
+ case Array::ROTATE_E:
+ visit(x.arr_arg());
+ m_output << ".rotate!";
+ break;
+ case Array::DELETEIF:
+ visit(x.arr_arg());
+ m_output << ".delete_if";
+ break;
+ case Array::INSERT:
+ visit(x.arr_arg());
+ m_output << ".insert";
+ break;
+ case Array::BSEARCH:
+ visit(x.arr_arg());
+ m_output << ".bsearch";
+ break;
+ case Array::KEEPIF:
+ visit(x.arr_arg());
+ m_output << ".keep_if";
+ break;
+ case Array::SELECT:
+ visit(x.arr_arg());
+ m_output << ".select";
+ break;
+ case Array::VALUES_AT:
+ visit(x.arr_arg());
+ m_output << ".values_at";
+ break;
+ case Array::BLOCK:
+ visit(x.arr_arg());
+ m_output << ".index";
+ break;
+ case Array::DIG:
+ visit(x.arr_arg());
+ m_output << ".dig";
+ break;
+ case Array::SLICE:
+ visit(x.arr_arg());
+ m_output << ".slice";
+ break;
+ case Array::PERM:
+ visit(x.arr_arg());
+ m_output << ".permutation";
+ break;
+ case Array::COMB:
+ visit(x.arr_arg());
+ m_output << ".combination";
+ break;
+ case Array::ASSOC:
+ visit(x.arr_arg());
+ m_output << ".assoc";
+ break;
+ case Array::RASSOC:
+ visit(x.arr_arg());
+ m_output << ".rassoc";
+ break;
+ }
+ m_output << "(";
+ visit(x.val_arg());
+ m_output << ")";
+}
+
+void protoConverter::visit(AssignmentStatement const& x)
+{
+ m_output << "var_" << m_numLiveVars << " = ";
+ visit(x.rvalue());
+ m_numVarsPerScope.top()++;
+ m_numLiveVars++;
+ m_output << "\n";
+}
+
+void protoConverter::visit(BinaryOp const& x)
+{
+ m_output << "(";
+ visit(x.left());
+ switch (x.op()) {
+ case BinaryOp::ADD: m_output << " + "; break;
+ case BinaryOp::SUB: m_output << " - "; break;
+ case BinaryOp::MUL: m_output << " * "; break;
+ case BinaryOp::DIV: m_output << " / "; break;
+ case BinaryOp::MOD: m_output << " % "; break;
+ case BinaryOp::XOR: m_output << " ^ "; break;
+ case BinaryOp::AND: m_output << " and "; break;
+ case BinaryOp::OR: m_output << " or "; break;
+ case BinaryOp::EQ: m_output << " == "; break;
+ case BinaryOp::NE: m_output << " != "; break;
+ case BinaryOp::LE: m_output << " <= "; break;
+ case BinaryOp::GE: m_output << " >= "; break;
+ case BinaryOp::LT: m_output << " < "; break;
+ case BinaryOp::GT: m_output << " > "; break;
+ case BinaryOp::RS: m_output << " >> "; break;
+ }
+ visit(x.right());
+ m_output << ")";
+}
+
+void protoConverter::visit(BuiltinFuncs const& x)
+{
+ switch (x.bifunc_oneof_case()) {
+ case BuiltinFuncs::kOs:
+ visit(x.os());
+ break;
+ case BuiltinFuncs::kTime:
+ visit(x.time());
+ break;
+ case BuiltinFuncs::kArr:
+ visit(x.arr());
+ break;
+ case BuiltinFuncs::kMops:
+ visit(x.mops());
+ break;
+ case BuiltinFuncs::BIFUNC_ONEOF_NOT_SET:
+ m_output << "1";
+ break;
+ }
+ m_output << "\n";
+}
+
+void protoConverter::visit(Const const& x)
+{
+ switch (x.const_oneof_case()) {
+ case Const::kIntLit:
+ m_output << "(" << (x.int_lit() % 13) << ")";
+ break;
+ case Const::kBoolVal:
+ m_output << "(" << x.bool_val() << ")";
+ break;
+ case Const::CONST_ONEOF_NOT_SET:
+ m_output << "1";
+ break;
+ }
+}
+
+void protoConverter::visit(Function const& x)
+{
+ m_output << "def foo()\nvar_0 = 1\n";
+ visit(x.statements());
+ m_output << "end\n";
+ m_output << "foo\n";
+}
+
+void protoConverter::visit(HashType const& x)
+{
+ if (x.keyval_size() > 0) {
+ int i = x.keyval_size();
+ m_output << "{";
+ for (auto &e : x.keyval()) {
+ i--;
+ if (i == 0) {
+ visit(e);
+ }
+ else {
+ visit(e);
+ m_output << ", ";
+ }
+ }
+ m_output << "}";
+ }
+}
+
+void protoConverter::visit(IfElse const& x)
+{
+ m_output << "if ";
+ visit(x.cond());
+ m_output << "\n";
+ visit(x.if_body());
+ m_output << "\nelse\n";
+ visit(x.else_body());
+ m_output << "\nend\n";
+}
+
+void protoConverter::visit(KVPair const& x)
+{
+ m_output << "\"" << removeSpecial(x.key()) << "\"";
+ m_output << " => ";
+ m_output << "\"" << removeSpecial(x.val()) << "\"";
+}
+
+void protoConverter::visit(MathConst const& x)
+{
+ switch (x.math_const()) {
+ case MathConst::PI:
+ m_output << "Math::PI";
+ break;
+ case MathConst::E:
+ m_output << "Math::E";
+ break;
+ }
+}
+
+void protoConverter::visit(MathOps const& x)
+{
+ switch (x.math_op()) {
+ case MathOps::CBRT:
+ m_output << "Math.cbrt(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::COS:
+ m_output << "Math.cos(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::ERF:
+ m_output << "Math.erf(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::ERFC:
+ m_output << "Math.erfc(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::LOG:
+ m_output << "Math.log(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::LOG10:
+ m_output << "Math.log10(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::LOG2:
+ m_output << "Math.log2(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::SIN:
+ m_output << "Math.sin(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::SQRT:
+ m_output << "Math.sqrt(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ case MathOps::TAN:
+ m_output << "Math.tan(";
+ visit(x.math_arg());
+ m_output << ")";
+ break;
+ }
+}
+
+void protoConverter::visit(MathType const& x)
+{
+ switch (x.math_arg_oneof_case()) {
+ case MathType::kMathRval:
+ visit(x.math_rval());
+ break;
+ case MathType::kMathConst:
+ visit(x.math_const());
+ break;
+ case MathType::MATH_ARG_ONEOF_NOT_SET:
+ m_output << "1";
+ break;
+ }
+}
+
+void protoConverter::visit(ObjectSpace const& x)
+{
+ switch (x.os_func()) {
+ case ObjectSpace::COUNT:
+ m_output << "ObjectSpace.count_objects";
+ break;
+ }
+ m_output << "(";
+ visit(x.os_arg());
+ m_output << ")" << "\n";
+}
+
+void protoConverter::visit(Rvalue const& x)
+{
+ switch (x.rvalue_oneof_case()) {
+ case Rvalue::kVarref:
+ visit(x.varref());
+ break;
+ case Rvalue::kCons:
+ visit(x.cons());
+ break;
+ case Rvalue::kBinop:
+ visit(x.binop());
+ break;
+ case Rvalue::RVALUE_ONEOF_NOT_SET:
+ m_output << "1";
+ break;
+ }
+}
+
+void protoConverter::visit(Statement const& x)
+{
+ switch (x.stmt_oneof_case()) {
+ case Statement::kAssignment:
+ visit(x.assignment());
+ break;
+ case Statement::kIfelse:
+ visit(x.ifelse());
+ break;
+ case Statement::kTernaryStmt:
+ visit(x.ternary_stmt());
+ break;
+ case Statement::kBuiltins:
+ visit(x.builtins());
+ break;
+ case Statement::kBlockstmt:
+ visit(x.blockstmt());
+ break;
+ case Statement::STMT_ONEOF_NOT_SET:
+ break;
+ }
+ m_output << "\n";
+}
+
+void protoConverter::visit(StatementSeq const& x)
+{
+ if (x.statements_size() > 0) {
+ m_numVarsPerScope.push(0);
+ m_output << "@scope ||= begin\n";
+ for (auto &st : x.statements())
+ visit(st);
+ m_output << "end\n";
+ m_numLiveVars -= m_numVarsPerScope.top();
+ m_numVarsPerScope.pop();
+ }
+}
+
+void protoConverter::visit(StringExtNoArg const& x)
+{
+ m_output << "\"" << removeSpecial(x.str_arg()) << "\"";
+ switch (x.str_op()) {
+ case StringExtNoArg::DUMP:
+ m_output << ".dump";
+ break;
+ case StringExtNoArg::STRIP:
+ m_output << ".strip";
+ break;
+ case StringExtNoArg::LSTRIP:
+ m_output << ".lstrip";
+ break;
+ case StringExtNoArg::RSTRIP:
+ m_output << ".rstrip";
+ break;
+ case StringExtNoArg::STRIPE:
+ m_output << ".strip!";
+ break;
+ case StringExtNoArg::LSTRIPE:
+ m_output << ".lstrip!";
+ break;
+ case StringExtNoArg::RSTRIPE:
+ m_output << ".rstrip!";
+ break;
+ case StringExtNoArg::SWAPCASE:
+ m_output << ".swapcase";
+ break;
+ case StringExtNoArg::SWAPCASEE:
+ m_output << ".swapcase!";
+ break;
+ case StringExtNoArg::SQUEEZE:
+ m_output << ".squeeze";
+ break;
+ }
+}
+
+void protoConverter::visit(Ternary const& x)
+{
+ m_output << "(";
+ visit(x.tern_cond());
+ m_output << " ? ";
+ visit(x.t_branch());
+ m_output << " : ";
+ visit(x.f_branch());
+ m_output << ")\n";
+}
+
+void protoConverter::visit(Time const& x)
+{
+ switch (x.t_func()) {
+ case Time::AT:
+ m_output << "Time.at";
+ break;
+ case Time::GM:
+ m_output << "Time.gm";
+ break;
+ }
+ m_output << "(" << (x.t_arg()% 13) << ")" << "\n";
+}
+
+void protoConverter::visit(VarRef const& x)
+{
+ m_output << "var_" << (static_cast<uint32_t>(x.varnum()) % m_numLiveVars);
+}
+
+std::string protoConverter::FunctionToString(Function const& input)
+{
+ visit(input);
+ return m_output.str();
+}
diff --git a/oss-fuzz/proto_to_ruby.h b/oss-fuzz/proto_to_ruby.h
new file mode 100644
index 000000000..01f9d68bb
--- /dev/null
+++ b/oss-fuzz/proto_to_ruby.h
@@ -0,0 +1,55 @@
+#include <cstdint>
+#include <cstddef>
+#include <string>
+#include <ostream>
+#include <sstream>
+#include <stack>
+#include "ruby.pb.h"
+
+namespace ruby_fuzzer {
+ class protoConverter
+ {
+ public:
+ protoConverter() {
+ m_numLiveVars = 1;
+ m_numVarsPerScope.push(m_numLiveVars);
+ }
+ protoConverter(protoConverter const& x) {
+ m_numLiveVars = x.m_numLiveVars;
+ m_numVarsPerScope = x.m_numVarsPerScope;
+ }
+ ~protoConverter() {}
+ std::string FunctionToString(Function const& _input);
+
+ private:
+ void visit(ArrType const&);
+ void visit(Array const&);
+ void visit(AssignmentStatement const&);
+ void visit(BinaryOp const&);
+ void visit(BuiltinFuncs const&);
+ void visit(Const const&);
+ void visit(Function const&);
+ void visit(HashType const&);
+ void visit(IfElse const&);
+ void visit(KVPair const&);
+ void visit(MathConst const&);
+ void visit(MathOps const&);
+ void visit(MathType const&);
+ void visit(ObjectSpace const&);
+ void visit(Rvalue const&);
+ void visit(Statement const&);
+ void visit(StatementSeq const&);
+ void visit(StringExtNoArg const&);
+ void visit(Ternary const&);
+ void visit(Time const&);
+ void visit(VarRef const&);
+ template <class T>
+ void visit(google::protobuf::RepeatedPtrField<T> const& _repeated_field);
+
+ std::string removeSpecial(const std::string &x);
+
+ std::ostringstream m_output;
+ std::stack<uint8_t> m_numVarsPerScope;
+ int32_t m_numLiveVars;
+ };
+}
diff --git a/oss-fuzz/ruby.proto b/oss-fuzz/ruby.proto
new file mode 100644
index 000000000..d9b0804c8
--- /dev/null
+++ b/oss-fuzz/ruby.proto
@@ -0,0 +1,201 @@
+syntax = "proto2";
+
+message VarRef {
+ required int32 varnum = 1;
+}
+
+message ArrType {
+ repeated Const elements = 1;
+}
+
+message KVPair {
+ required string key = 1;
+ required string val = 2;
+}
+
+message HashType {
+ repeated KVPair keyval = 1;
+}
+
+message StringExtNoArg {
+ enum StrExtOp {
+ DUMP = 0;
+ STRIP = 1;
+ LSTRIP = 2;
+ RSTRIP = 3;
+ STRIPE = 4;
+ LSTRIPE = 5;
+ RSTRIPE = 6;
+ SWAPCASE = 7;
+ SWAPCASEE = 8;
+ SQUEEZE = 9;
+ }
+ required StrExtOp str_op = 1;
+ required string str_arg = 2;
+}
+
+message MathConst {
+ enum MathConstLit {
+ PI = 0;
+ E = 1;
+ }
+ required MathConstLit math_const = 1;
+}
+
+message Const {
+ oneof const_oneof {
+ uint32 int_lit = 1;
+ bool bool_val = 4;
+ }
+}
+
+message BinaryOp {
+ enum Op {
+ ADD = 0;
+ SUB = 1;
+ MUL = 2;
+ DIV = 3;
+ MOD = 4;
+ XOR = 5;
+ AND = 6;
+ OR = 7;
+ EQ = 8;
+ NE = 9;
+ LE = 10;
+ GE = 11;
+ LT = 12;
+ GT = 13;
+ RS = 14;
+ };
+ required Op op = 1;
+ required Rvalue left = 2;
+ required Rvalue right = 3;
+}
+
+message Rvalue {
+ oneof rvalue_oneof {
+ VarRef varref = 1;
+ Const cons = 2;
+ BinaryOp binop = 3;
+ }
+}
+
+message AssignmentStatement {
+ required Rvalue rvalue = 2;
+}
+
+
+message IfElse {
+ required Rvalue cond = 1;
+ required StatementSeq if_body = 2;
+ required StatementSeq else_body = 3;
+}
+
+//TODO: Add Switch statement
+//message Switch {
+// required Rvalue switch_var = 1;
+// repeated Rvalue cond = 2;
+//}
+
+message Ternary {
+ required Rvalue tern_cond = 1;
+ required Rvalue t_branch = 2;
+ required Rvalue f_branch = 3;
+}
+
+message ObjectSpace {
+ enum OS_methods {
+ COUNT = 1;
+ }
+ required OS_methods os_func = 1;
+ required HashType os_arg = 2;
+}
+
+message Time {
+ enum T_methods {
+ AT = 1;
+ GM = 2;
+ }
+ required T_methods t_func = 1;
+ required uint32 t_arg = 2;
+}
+
+message Array {
+ enum Arr_methods {
+ FLATTEN = 1;
+ COMPACT = 2;
+ FETCH = 3;
+ FILL = 4;
+ ROTATE = 5;
+ ROTATE_E = 6;
+ DELETEIF = 7;
+ INSERT = 8;
+ BSEARCH = 9;
+ KEEPIF = 10;
+ SELECT = 11;
+ VALUES_AT = 12;
+ BLOCK = 13;
+ DIG = 14;
+ SLICE = 15;
+ PERM = 16;
+ COMB = 17;
+ ASSOC = 18;
+ RASSOC = 19;
+ }
+ required Arr_methods arr_func = 1;
+ required ArrType arr_arg = 2;
+ required Rvalue val_arg = 3;
+}
+
+message MathType {
+ oneof math_arg_oneof {
+ Rvalue math_rval = 2;
+ MathConst math_const = 3;
+ }
+}
+
+message MathOps {
+ enum Mops {
+ CBRT = 1;
+ COS = 2;
+ ERF = 3;
+ ERFC = 4;
+ LOG = 5;
+ LOG10 = 6;
+ LOG2 = 7;
+ SIN = 8;
+ SQRT = 9;
+ TAN = 10;
+ }
+ required Mops math_op = 1;
+ required MathType math_arg = 2;
+}
+
+message BuiltinFuncs {
+ oneof bifunc_oneof {
+ ObjectSpace os = 1;
+ Time time = 2;
+ Array arr = 3;
+ MathOps mops = 4;
+ }
+}
+
+message Statement {
+ oneof stmt_oneof {
+ AssignmentStatement assignment = 1;
+ IfElse ifelse = 2;
+ Ternary ternary_stmt = 3;
+ BuiltinFuncs builtins = 4;
+ StatementSeq blockstmt = 5;
+ }
+}
+
+message StatementSeq {
+ repeated Statement statements = 1;
+}
+
+message Function {
+ required StatementSeq statements = 1;
+}
+
+package ruby_fuzzer;
diff --git a/src/array.c b/src/array.c
index 2152e292d..8cf813743 100644
--- a/src/array.c
+++ b/src/array.c
@@ -120,9 +120,7 @@ ary_fill_with_nil(mrb_value *ptr, mrb_int size)
static void
ary_modify_check(mrb_state *mrb, struct RArray *a)
{
- if (MRB_FROZEN_P(a)) {
- mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen array");
- }
+ mrb_check_frozen(mrb, a);
}
static void
@@ -231,7 +229,7 @@ ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len)
static void
ary_shrink_capa(mrb_state *mrb, struct RArray *a)
{
-
+
mrb_int capa;
if (ARY_EMBED_P(a)) return;
@@ -853,14 +851,14 @@ static mrb_value
mrb_ary_aget(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_int i, len, alen = ARY_LEN(a);
+ mrb_int i, len, alen;
mrb_value index;
if (mrb_get_args(mrb, "o|i", &index, &len) == 1) {
switch (mrb_type(index)) {
/* a[n..m] */
case MRB_TT_RANGE:
- if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) {
+ if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) {
return ary_subseq(mrb, a, i, len);
}
else {
@@ -874,6 +872,7 @@ mrb_ary_aget(mrb_state *mrb, mrb_value self)
}
i = aget_index(mrb, index);
+ alen = ARY_LEN(a);
if (i < 0) i += alen;
if (i < 0 || alen < i) return mrb_nil_value();
if (len < 0) return mrb_nil_value();
@@ -928,13 +927,13 @@ mrb_ary_aset(mrb_state *mrb, mrb_value self)
if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) {
/* a[n..m] = v */
switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) {
- case 0: /* not range */
+ case MRB_RANGE_TYPE_MISMATCH:
mrb_ary_set(mrb, self, aget_index(mrb, v1), v2);
break;
- case 1: /* range */
+ case MRB_RANGE_OK:
mrb_ary_splice(mrb, self, i, len, v2);
break;
- case 2: /* out of range */
+ case MRB_RANGE_OUT:
mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", v1);
break;
}
@@ -953,9 +952,10 @@ mrb_ary_delete_at(mrb_state *mrb, mrb_value self)
mrb_int index;
mrb_value val;
mrb_value *ptr;
- mrb_int len, alen = ARY_LEN(a);
+ mrb_int len, alen;
mrb_get_args(mrb, "i", &index);
+ alen = ARY_LEN(a);
if (index < 0) index += alen;
if (index < 0 || alen <= index) return mrb_nil_value();
@@ -980,16 +980,17 @@ static mrb_value
mrb_ary_first(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_int size, alen = ARY_LEN(a);
+ mrb_int size, alen;
if (mrb_get_argc(mrb) == 0) {
- return (alen > 0)? ARY_PTR(a)[0]: mrb_nil_value();
+ return (ARY_LEN(a) > 0)? ARY_PTR(a)[0]: mrb_nil_value();
}
mrb_get_args(mrb, "|i", &size);
if (size < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
}
+ alen = ARY_LEN(a);
if (size > alen) size = alen;
if (ARY_SHARED_P(a)) {
return ary_subseq(mrb, a, 0, size);
@@ -1001,10 +1002,13 @@ static mrb_value
mrb_ary_last(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
- mrb_int size, alen = ARY_LEN(a);
+ mrb_int n, size, alen;
- if (mrb_get_args(mrb, "|i", &size) == 0)
- return (alen > 0)? ARY_PTR(a)[alen - 1]: mrb_nil_value();
+ n = mrb_get_args(mrb, "|i", &size);
+ alen = ARY_LEN(a);
+ if (n == 0) {
+ return (alen > 0) ? ARY_PTR(a)[alen - 1]: mrb_nil_value();
+ }
if (size < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size");
@@ -1052,7 +1056,7 @@ mrb_ary_rindex_m(mrb_state *mrb, mrb_value self)
MRB_API mrb_value
mrb_ary_splat(mrb_state *mrb, mrb_value v)
{
- mrb_value a, recv_class;
+ mrb_value a;
if (mrb_array_p(v)) {
return v;
@@ -1063,22 +1067,11 @@ mrb_ary_splat(mrb_state *mrb, mrb_value v)
}
a = mrb_funcall(mrb, v, "to_a", 0);
- if (mrb_array_p(a)) {
- return a;
- }
- else if (mrb_nil_p(a)) {
+ if (mrb_nil_p(a)) {
return mrb_ary_new_from_values(mrb, 1, &v);
}
- else {
- recv_class = mrb_obj_value(mrb_obj_class(mrb, v));
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Array (%S#to_a gives %S)",
- recv_class,
- recv_class,
- mrb_obj_value(mrb_obj_class(mrb, a))
- );
- /* not reached */
- return mrb_undef_value();
- }
+ mrb_ensure_array_type(mrb, a);
+ return a;
}
static mrb_value
@@ -1108,6 +1101,13 @@ mrb_ary_clear(mrb_state *mrb, mrb_value self)
}
static mrb_value
+mrb_ary_clear_m(mrb_state *mrb, mrb_value self)
+{
+ mrb_get_args(mrb, "");
+ return mrb_ary_clear(mrb, self);
+}
+
+static mrb_value
mrb_ary_empty_p(mrb_state *mrb, mrb_value self)
{
struct RArray *a = mrb_ary_ptr(self);
@@ -1116,12 +1116,6 @@ mrb_ary_empty_p(mrb_state *mrb, mrb_value self)
}
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_API mrb_value
mrb_ary_entry(mrb_value ary, mrb_int offset)
{
if (offset < 0) {
@@ -1174,7 +1168,7 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list)
val = tmp;
goto str_join;
}
- tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
+ tmp = mrb_check_array_type(mrb, val);
if (!mrb_nil_p(tmp)) {
val = tmp;
goto ary_join;
@@ -1268,41 +1262,39 @@ mrb_init_array(mrb_state *mrb)
{
struct RClass *a;
- mrb->array_class = a = 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 */
-
- mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */
- mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */
- mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */
- mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.4 */
- mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ANY()); /* 15.2.12.5.5 */
- mrb_define_method(mrb, a, "clear", mrb_ary_clear, MRB_ARGS_NONE()); /* 15.2.12.5.6 */
- mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */
- mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */
- mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */
- mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */
- mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */
- mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */
- mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_ANY()); /* 15.2.12.5.17 */
- mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_ANY()); /* 15.2.12.5.18 */
- mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */
- mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */
- mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */
- mrb_define_method(mrb, a, "append", mrb_ary_push_m, MRB_ARGS_ANY());
- mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */
- mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */
- mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */
- mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */
- mrb_define_method(mrb, a, "shift", mrb_ary_shift, MRB_ARGS_NONE()); /* 15.2.12.5.27 */
- mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */
- mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.29 */
- mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */
- mrb_define_method(mrb, a, "prepend", mrb_ary_unshift_m, MRB_ARGS_ANY());
+ mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */
+
+ mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */
+ mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */
+ mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */
+ mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ARG(1,1)); /* 15.2.12.5.4 */
+ mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ARG(2,1)); /* 15.2.12.5.5 */
+ mrb_define_method(mrb, a, "clear", mrb_ary_clear_m, MRB_ARGS_NONE()); /* 15.2.12.5.6 */
+ mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */
+ mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */
+ mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */
+ mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */
+ mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */
+ mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */
+ mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_OPT(1)); /* 15.2.12.5.17 */
+ mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_OPT(1)); /* 15.2.12.5.18 */
+ mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */
+ mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */
+ mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */
+ mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */
+ mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */
+ mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */
+ mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */
+ mrb_define_method(mrb, a, "shift", mrb_ary_shift, MRB_ARGS_NONE()); /* 15.2.12.5.27 */
+ mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */
+ mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ARG(1,1)); /* 15.2.12.5.29 */
+ 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_index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */
+ mrb_define_method(mrb, a, "__ary_index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */
mrb_define_method(mrb, a, "__svalue", mrb_ary_svalue, MRB_ARGS_NONE());
}
diff --git a/src/backtrace.c b/src/backtrace.c
index 57ae7fd7f..991a67d00 100644
--- a/src/backtrace.c
+++ b/src/backtrace.c
@@ -16,24 +16,24 @@
#include <mruby/data.h>
struct backtrace_location {
- int lineno;
- const char *filename;
+ int32_t lineno;
mrb_sym method_id;
+ const char *filename;
};
-typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location*, void*);
+typedef void (*each_backtrace_func)(mrb_state*, const struct backtrace_location*, void*);
static const mrb_data_type bt_type = { "Backtrace", mrb_free };
static void
each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_func func, void *data)
{
- ptrdiff_t i, j;
+ ptrdiff_t i;
if (ciidx >= mrb->c->ciend - mrb->c->cibase)
ciidx = 10; /* ciidx is broken... */
- for (i=ciidx, j=0; i >= 0; i--,j++) {
+ for (i=ciidx; i >= 0; i--) {
struct backtrace_location loc;
mrb_callinfo *ci;
mrb_irep *irep;
@@ -57,11 +57,11 @@ each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_fu
else {
pc = pc0;
}
- loc.filename = mrb_debug_get_filename(irep, pc - irep->iseq);
- loc.lineno = mrb_debug_get_line(irep, pc - irep->iseq);
+ loc.lineno = mrb_debug_get_line(mrb, irep, pc - irep->iseq);
if (loc.lineno == -1) continue;
+ loc.filename = mrb_debug_get_filename(mrb, irep, pc - irep->iseq);
if (!loc.filename) {
loc.filename = "(unknown)";
}
@@ -80,8 +80,6 @@ print_backtrace(mrb_state *mrb, mrb_value backtrace)
mrb_int n;
FILE *stream = stderr;
- if (!mrb_array_p(backtrace)) return;
-
n = RARRAY_LEN(backtrace) - 1;
if (n == 0) return;
@@ -96,7 +94,7 @@ print_backtrace(mrb_state *mrb, mrb_value backtrace)
}
static int
-packed_bt_len(struct backtrace_location *bt, int n)
+packed_bt_len(const struct backtrace_location *bt, int n)
{
int len = 0;
int i;
@@ -113,7 +111,7 @@ static void
print_packed_backtrace(mrb_state *mrb, mrb_value packed)
{
FILE *stream = stderr;
- struct backtrace_location *bt;
+ const struct backtrace_location *bt;
int n, i;
int ai = mrb_gc_arena_save(mrb);
@@ -124,7 +122,7 @@ print_packed_backtrace(mrb_state *mrb, mrb_value packed)
if (packed_bt_len(bt, n) == 0) return;
fprintf(stream, "trace (most recent call last):\n");
for (i = 0; i<n; i++) {
- struct backtrace_location *entry = &bt[n-i-1];
+ const struct backtrace_location *entry = &bt[n-i-1];
if (entry->filename == NULL) continue;
fprintf(stream, "\t[%d] %s:%d", i, entry->filename, entry->lineno);
if (entry->method_id != 0) {
@@ -172,7 +170,7 @@ mrb_print_backtrace(mrb_state *mrb)
static void
count_backtrace_i(mrb_state *mrb,
- struct backtrace_location *loc,
+ const struct backtrace_location *loc,
void *data)
{
int *lenp = (int*)data;
@@ -183,7 +181,7 @@ count_backtrace_i(mrb_state *mrb,
static void
pack_backtrace_i(mrb_state *mrb,
- struct backtrace_location *loc,
+ const struct backtrace_location *loc,
void *data)
{
struct backtrace_location **pptr = (struct backtrace_location**)data;
@@ -206,7 +204,6 @@ packed_backtrace(mrb_state *mrb)
each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len);
size = len * sizeof(struct backtrace_location);
ptr = mrb_malloc(mrb, size);
- if (ptr) memset(ptr, 0, size);
backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type);
backtrace->flags = (unsigned int)len;
each_backtrace(mrb, ciidx, mrb->c->ci->pc, pack_backtrace_i, &ptr);
@@ -230,7 +227,7 @@ mrb_keep_backtrace(mrb_state *mrb, mrb_value exc)
mrb_value
mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
{
- struct backtrace_location *bt;
+ const struct backtrace_location *bt;
mrb_int n, i;
int ai;
@@ -245,7 +242,7 @@ mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
backtrace = mrb_ary_new_capa(mrb, n);
ai = mrb_gc_arena_save(mrb);
for (i = 0; i < n; i++) {
- struct backtrace_location *entry = &bt[i];
+ const struct backtrace_location *entry = &bt[i];
mrb_value btline;
if (entry->filename == NULL) continue;
diff --git a/src/class.c b/src/class.c
index c761f46af..edee95678 100644
--- a/src/class.c
+++ b/src/class.c
@@ -65,16 +65,22 @@ mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb
else {
name = mrb_class_path(mrb, outer);
if (mrb_nil_p(name)) { /* unnamed outer class */
- if (outer != mrb->object_class) {
- mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"),
- mrb_obj_value(outer));
+ if (outer != mrb->object_class && outer != c) {
+ mrb_obj_iv_set_force(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"),
+ mrb_obj_value(outer));
}
return;
}
mrb_str_cat_cstr(mrb, name, "::");
mrb_str_cat_cstr(mrb, name, mrb_sym2name(mrb, id));
}
- mrb_obj_iv_set(mrb, (struct RObject*)c, nsym, name);
+ mrb_obj_iv_set_force(mrb, (struct RObject*)c, nsym, name);
+}
+
+mrb_bool
+mrb_const_name_p(mrb_state *mrb, const char *name, mrb_int len)
+{
+ return len > 0 && ISUPPER(name[0]) && mrb_ident_p(name+1, len-1);
}
static void
@@ -93,7 +99,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->flags |= MRB_FLAG_IS_INHERITED;
+ sc->flags |= MRB_FL_CLASS_IS_INHERITED;
sc->mt = kh_init(mt, mrb);
sc->iv = 0;
if (o->tt == MRB_TT_CLASS) {
@@ -120,6 +126,20 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc);
mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)o);
mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern_lit(mrb, "__attached__"), mrb_obj_value(o));
+ sc->flags |= o->flags & MRB_FL_OBJ_IS_FROZEN;
+}
+
+static mrb_value
+class_name_str(mrb_state *mrb, struct RClass* c)
+{
+ mrb_value path = mrb_class_path(mrb, c);
+ if (mrb_nil_p(path)) {
+ path = c->tt == MRB_TT_MODULE ? mrb_str_new_lit(mrb, "#<Module:") :
+ mrb_str_new_lit(mrb, "#<Class:");
+ mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, c));
+ mrb_str_cat_lit(mrb, path, ">");
+ }
+ return path;
}
static struct RClass*
@@ -275,7 +295,7 @@ mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass)
if (!super)
super = mrb->object_class;
- super->flags |= MRB_FLAG_IS_INHERITED;
+ super->flags |= MRB_FL_CLASS_IS_INHERITED;
s = mrb_obj_value(super);
mc_clear_by_class(mrb, klass);
mid = mrb_intern_lit(mrb, "inherited");
@@ -428,12 +448,7 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_method_
MRB_CLASS_ORIGIN(c);
h = c->mt;
- if (MRB_FROZEN_P(c)) {
- if (c->tt == MRB_TT_MODULE)
- mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen module");
- else
- mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen class");
- }
+ mrb_check_frozen(mrb, c);
if (!h) h = c->mt = kh_init(mt, mrb);
k = kh_put(mt, mrb, h, mid);
kh_value(h, k) = m;
@@ -471,15 +486,11 @@ mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t
MRB_API void
mrb_notimplement(mrb_state *mrb)
{
- const char *str;
- mrb_int len;
mrb_callinfo *ci = mrb->c->ci;
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));
+ mrb_value str = mrb_sym2str(mrb, ci->mid);
+ mrb_raisef(mrb, E_NOTIMP_ERROR, "%S() function is unimplemented on this machine", str);
}
}
@@ -492,52 +503,34 @@ mrb_notimplement_m(mrb_state *mrb, mrb_value self)
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)
-{
- mrb_value tmp;
-
- tmp = mrb_check_convert_type(mrb, val, t, c, m);
- if (mrb_nil_p(tmp)) {
- mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_cstr(mrb, c));
- }
- return tmp;
-}
+#define CHECK_TYPE(mrb, val, t, c) do { \
+ if (mrb_type(val) != (t)) {\
+ mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_lit(mrb, c));\
+ }\
+} while (0)
static mrb_value
to_str(mrb_state *mrb, mrb_value val)
{
- return check_type(mrb, val, MRB_TT_STRING, "String", "to_str");
+ CHECK_TYPE(mrb, val, MRB_TT_STRING, "String");
+ return val;
}
static mrb_value
to_ary(mrb_state *mrb, mrb_value val)
{
- return check_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary");
+ CHECK_TYPE(mrb, val, MRB_TT_ARRAY, "Array");
+ return val;
}
static mrb_value
to_hash(mrb_state *mrb, mrb_value val)
{
- return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash");
+ CHECK_TYPE(mrb, val, MRB_TT_HASH, "Hash");
+ return val;
}
-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);
- /* not reached */
- return 0;
- }
-}
+#define to_sym(mrb, ss) mrb_obj_to_sym(mrb, ss)
MRB_API mrb_int
mrb_get_argc(mrb_state *mrb)
@@ -580,20 +573,20 @@ mrb_get_argv(mrb_state *mrb)
string mruby type C type note
----------------------------------------------------------------------------------------------
o: Object [mrb_value]
- C: class/module [mrb_value]
+ 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*] 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]
- n: Symbol [mrb_sym]
- d: Data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified
- I: Inline struct [void*]
- &: Block [mrb_value] &! raises exception if no block given
+ f: Fixnum/Float [mrb_float]
+ i: Fixnum/Float [mrb_int]
+ b: boolean [mrb_bool]
+ n: String/Symbol [mrb_sym]
+ d: data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified; when ! follows, the value may be nil
+ I: inline struct [void*]
+ &: block [mrb_value] &! raises exception if no block given
*: rest argument [mrb_value*,mrb_int] The rest of the arguments as an array; *! avoid copy of the stack
|: optional Following arguments are optional
?: optional given [mrb_bool] true if preceding argument (optional) is given
@@ -851,29 +844,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
p = va_arg(ap, mrb_int*);
if (i < argc) {
- switch (mrb_type(ARGV[arg_i])) {
- case MRB_TT_FIXNUM:
- *p = mrb_fixnum(ARGV[arg_i]);
- break;
-#ifndef MRB_WITHOUT_FLOAT
- case MRB_TT_FLOAT:
- {
- mrb_float f = mrb_float(ARGV[arg_i]);
-
- if (!FIXABLE_FLOAT(f)) {
- mrb_raise(mrb, E_RANGE_ERROR, "float too big for int");
- }
- *p = (mrb_int)f;
- }
- break;
-#endif
- case MRB_TT_STRING:
- mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer");
- break;
- default:
- *p = mrb_fixnum(mrb_Integer(mrb, ARGV[arg_i]));
- break;
- }
+ *p = mrb_fixnum(mrb_to_int(mrb, ARGV[arg_i]));
arg_i++;
i++;
}
@@ -1061,7 +1032,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
while (m) {
int superclass_seen = 0;
- if (m->flags & MRB_FLAG_IS_PREPENDED)
+ if (m->flags & MRB_FL_CLASS_IS_PREPENDED)
goto skip;
if (klass_mt && klass_mt == m->mt)
@@ -1084,7 +1055,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
}
ic = include_class_new(mrb, m, ins_pos->super);
- m->flags |= MRB_FLAG_IS_INHERITED;
+ m->flags |= MRB_FL_CLASS_IS_INHERITED;
ins_pos->super = ic;
mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic);
mc_clear_by_class(mrb, ins_pos);
@@ -1099,8 +1070,8 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru
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_check_frozen(mrb, c);
+ if (include_module_at(mrb, c, find_origin(c), m, 1) < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected");
}
}
@@ -1111,15 +1082,16 @@ 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)) {
+ mrb_check_frozen(mrb, c);
+ if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) {
origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c);
- origin->flags |= MRB_FLAG_IS_ORIGIN | MRB_FLAG_IS_INHERITED;
+ origin->flags |= MRB_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED;
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;
+ c->flags |= MRB_FL_CLASS_IS_PREPENDED;
}
changed = include_module_at(mrb, c, c, m, 0);
if (changed < 0) {
@@ -1196,7 +1168,7 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self)
if (c->tt == MRB_TT_ICLASS) {
mrb_ary_push(mrb, result, mrb_obj_value(c->c));
}
- else if (!(c->flags & MRB_FLAG_IS_PREPENDED)) {
+ else if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) {
mrb_ary_push(mrb, result, mrb_obj_value(c));
}
c = c->super;
@@ -1217,27 +1189,6 @@ mrb_mod_extend_object(mrb_state *mrb, mrb_value mod)
}
static mrb_value
-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 != 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;
- }
-
- return result;
-}
-
-static mrb_value
mrb_mod_initialize(mrb_state *mrb, mrb_value mod)
{
mrb_value b;
@@ -1250,45 +1201,6 @@ mrb_mod_initialize(mrb_state *mrb, mrb_value mod)
return mod;
}
-mrb_value mrb_class_instance_method_list(mrb_state*, mrb_bool, struct RClass*, int);
-
-/* 15.2.2.4.33 */
-/*
- * call-seq:
- * mod.instance_methods(include_super=true) -> array
- *
- * Returns an array containing the names of the public and protected instance
- * methods in the receiver. For a module, these are the public and protected methods;
- * for a class, they are the instance (not singleton) methods. With no
- * argument, or with an argument that is <code>false</code>, the
- * instance methods in <i>mod</i> are returned, otherwise the methods
- * in <i>mod</i> and <i>mod</i>'s superclasses are returned.
- *
- * module A
- * def method1() end
- * end
- * class B
- * def method2() end
- * end
- * class C < B
- * def method3() end
- * end
- *
- * A.instance_methods #=> [:method1]
- * B.instance_methods(false) #=> [:method2]
- * C.instance_methods(false) #=> [:method3]
- * C.instance_methods(true).length #=> 43
- */
-
-static mrb_value
-mrb_mod_instance_methods(mrb_state *mrb, mrb_value mod)
-{
- struct RClass *c = mrb_class_ptr(mod);
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_class_instance_method_list(mrb, recur, c, 0);
-}
-
/* implementation of module_eval/class_eval */
mrb_value mrb_mod_module_eval(mrb_state*, mrb_value);
@@ -1365,9 +1277,9 @@ mc_clear_by_class(mrb_state *mrb, struct RClass *c)
struct mrb_cache_entry *mc = mrb->cache;
int i;
- if (c->flags & MRB_FLAG_IS_INHERITED) {
+ if (c->flags & MRB_FL_CLASS_IS_INHERITED) {
mc_clear_all(mrb);
- c->flags &= ~MRB_FLAG_IS_INHERITED;
+ c->flags &= ~MRB_FL_CLASS_IS_INHERITED;
return;
}
for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
@@ -1381,9 +1293,9 @@ mc_clear_by_id(mrb_state *mrb, struct RClass *c, mrb_sym mid)
struct mrb_cache_entry *mc = mrb->cache;
int i;
- if (c->flags & MRB_FLAG_IS_INHERITED) {
+ if (c->flags & MRB_FL_CLASS_IS_INHERITED) {
mc_clear_all(mrb);
- c->flags &= ~MRB_FLAG_IS_INHERITED;
+ c->flags &= ~MRB_FL_CLASS_IS_INHERITED;
return;
}
for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) {
@@ -1480,7 +1392,7 @@ mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
mrb_str_cat_lit(mrb, str, "@");
mrb_str_cat_str(mrb, str, name);
sym = mrb_intern_str(mrb, str);
- mrb_iv_check(mrb, sym);
+ mrb_iv_name_sym_check(mrb, sym);
name = mrb_symbol_value(sym);
p = mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name);
MRB_METHOD_FROM_PROC(m, p);
@@ -1525,7 +1437,7 @@ mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
mrb_str_cat_lit(mrb, str, "@");
mrb_str_cat_str(mrb, str, name);
sym = mrb_intern_str(mrb, str);
- mrb_iv_check(mrb, sym);
+ mrb_iv_name_sym_check(mrb, sym);
attr = mrb_symbol_value(sym);
/* prepare method name (name=) */
@@ -1572,29 +1484,20 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv)
*
*/
-MRB_API mrb_value
+mrb_value
mrb_instance_new(mrb_state *mrb, mrb_value cv)
{
mrb_value obj, blk;
mrb_value *argv;
mrb_int argc;
mrb_sym init;
- mrb_method_t m;
mrb_get_args(mrb, "*&", &argv, &argc, &blk);
obj = mrb_instance_alloc(mrb, cv);
init = mrb_intern_lit(mrb, "initialize");
- m = mrb_method_search(mrb, mrb_class(mrb, obj), init);
- if (MRB_METHOD_CFUNC_P(m)) {
- mrb_func_t f = MRB_METHOD_CFUNC(m);
- if (f != mrb_bob_init) {
- f(mrb, obj);
- }
- }
- else {
+ if (!mrb_func_basic_p(mrb, obj, init, mrb_bob_init)) {
mrb_funcall_with_block(mrb, obj, init, argc, argv, blk);
}
-
return obj;
}
@@ -1751,11 +1654,7 @@ mrb_class_path(mrb_state *mrb, struct RClass *c)
}
else if (mrb_symbol_p(path)) {
/* toplevel class/module */
- const char *str;
- mrb_int len;
-
- str = mrb_sym2name_len(mrb, mrb_symbol(path), &len);
- return mrb_str_new(mrb, str, len);
+ return mrb_sym2str(mrb, mrb_symbol(path));
}
return mrb_str_dup(mrb, path);
}
@@ -1763,10 +1662,10 @@ mrb_class_path(mrb_state *mrb, struct RClass *c)
MRB_API struct RClass*
mrb_class_real(struct RClass* cl)
{
- if (cl == 0)
- return NULL;
+ if (cl == 0) return NULL;
while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) {
cl = cl->super;
+ if (cl == 0) return NULL;
}
return cl;
}
@@ -1774,13 +1673,8 @@ mrb_class_real(struct RClass* cl)
MRB_API const char*
mrb_class_name(mrb_state *mrb, struct RClass* c)
{
- mrb_value path = mrb_class_path(mrb, c);
- if (mrb_nil_p(path)) {
- path = mrb_str_new_lit(mrb, "#<Class:");
- mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, c));
- mrb_str_cat_lit(mrb, path, ">");
- }
- return RSTRING_PTR(path);
+ mrb_value name = class_name_str(mrb, c);
+ return RSTRING_PTR(name);
}
MRB_API const char*
@@ -1892,15 +1786,13 @@ mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const
* show information on the thing we're attached to as well.
*/
-static mrb_value
+mrb_value
mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
{
- mrb_value str;
if (mrb_type(klass) == MRB_TT_SCLASS) {
mrb_value v = mrb_iv_get(mrb, klass, mrb_intern_lit(mrb, "__attached__"));
-
- str = mrb_str_new_lit(mrb, "#<Class:");
+ mrb_value str = mrb_str_new_lit(mrb, "#<Class:");
if (class_ptr_p(v)) {
mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v));
@@ -1911,34 +1803,7 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass)
return mrb_str_cat_lit(mrb, str, ">");
}
else {
- struct RClass *c;
- mrb_value path;
-
- str = mrb_str_new_capa(mrb, 32);
- c = mrb_class_ptr(klass);
- path = mrb_class_path(mrb, c);
-
- if (mrb_nil_p(path)) {
- switch (mrb_type(klass)) {
- case MRB_TT_CLASS:
- mrb_str_cat_lit(mrb, str, "#<Class:");
- break;
-
- case MRB_TT_MODULE:
- mrb_str_cat_lit(mrb, str, "#<Module:");
- break;
-
- default:
- /* Shouldn't be happened? */
- mrb_str_cat_lit(mrb, str, "#<??????:");
- break;
- }
- mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, c));
- return mrb_str_cat_lit(mrb, str, ">");
- }
- else {
- return path;
- }
+ return class_name_str(mrb, mrb_class_ptr(klass));
}
}
@@ -1950,11 +1815,11 @@ mrb_mod_alias(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "nn", &new_name, &old_name);
mrb_alias_method(mrb, c, new_name, old_name);
- return mrb_nil_value();
+ return mod;
}
-static void
-undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
+void
+mrb_undef_method_id(mrb_state *mrb, struct RClass *c, mrb_sym a)
{
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));
@@ -1970,7 +1835,7 @@ undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a)
MRB_API void
mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name)
{
- undef_method(mrb, c, mrb_intern_cstr(mrb, name));
+ mrb_undef_method_id(mrb, c, mrb_intern_cstr(mrb, name));
}
MRB_API void
@@ -1988,297 +1853,20 @@ mrb_mod_undef(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "*", &argv, &argc);
while (argc--) {
- undef_method(mrb, c, to_sym(mrb, *argv));
+ mrb_undef_method_id(mrb, c, to_sym(mrb, *argv));
argv++;
}
return mrb_nil_value();
}
-static mrb_value
-mod_define_method(mrb_state *mrb, mrb_value self)
-{
- struct RClass *c = mrb_class_ptr(self);
- struct RProc *p;
- mrb_method_t m;
- mrb_sym mid;
- mrb_value proc = mrb_undef_value();
- mrb_value blk;
-
- mrb_get_args(mrb, "n|o&", &mid, &proc, &blk);
- switch (mrb_type(proc)) {
- case MRB_TT_PROC:
- blk = proc;
- break;
- case MRB_TT_UNDEF:
- /* ignored */
- break;
- default:
- mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc)));
- break;
- }
- if (mrb_nil_p(blk)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
- }
- p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
- mrb_proc_copy(p, mrb_proc_ptr(blk));
- p->flags |= MRB_PROC_STRICT;
- MRB_METHOD_FROM_PROC(m, p);
- mrb_define_method_raw(mrb, c, mid, m);
- return mrb_symbol_value(mid);
-}
-
-static mrb_value
-top_define_method(mrb_state *mrb, mrb_value self)
-{
- return mod_define_method(mrb, mrb_obj_value(mrb->object_class));
-}
-
-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);
- }
-}
-
-static void
-check_cv_name_sym(mrb_state *mrb, mrb_sym id)
-{
- check_cv_name_str(mrb, mrb_sym2str(mrb, id));
-}
-
-/* 15.2.2.4.16 */
-/*
- * call-seq:
- * obj.class_variable_defined?(symbol) -> true or false
- *
- * Returns <code>true</code> if the given class variable is defined
- * in <i>obj</i>.
- *
- * class Fred
- * @@foo = 99
- * end
- * Fred.class_variable_defined?(:@@foo) #=> true
- * Fred.class_variable_defined?(:@@bar) #=> false
- */
-
-static mrb_value
-mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod)
-{
- mrb_sym id;
-
- 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 */
-/*
- * call-seq:
- * mod.class_variable_get(symbol) -> obj
- *
- * Returns the value of the given class variable (or throws a
- * <code>NameError</code> exception). The <code>@@</code> part of the
- * variable name should be included for regular class variables
- *
- * class Fred
- * @@foo = 99
- * end
- * Fred.class_variable_get(:@@foo) #=> 99
- */
-
-static mrb_value
-mrb_mod_cvar_get(mrb_state *mrb, mrb_value mod)
-{
- mrb_sym id;
-
- mrb_get_args(mrb, "n", &id);
- check_cv_name_sym(mrb, id);
- return mrb_cv_get(mrb, mod, id);
-}
-
-/* 15.2.2.4.18 */
-/*
- * call-seq:
- * obj.class_variable_set(symbol, obj) -> obj
- *
- * Sets the class variable names by <i>symbol</i> to
- * <i>object</i>.
- *
- * class Fred
- * @@foo = 99
- * def foo
- * @@foo
- * end
- * end
- * Fred.class_variable_set(:@@foo, 101) #=> 101
- * Fred.new.foo #=> 101
- */
-
-static mrb_value
-mrb_mod_cvar_set(mrb_state *mrb, mrb_value mod)
-{
- mrb_value value;
- mrb_sym id;
-
- mrb_get_args(mrb, "no", &id, &value);
- check_cv_name_sym(mrb, id);
- mrb_cv_set(mrb, mod, id, value);
- return value;
-}
-
-/* 15.2.2.4.39 */
-/*
- * call-seq:
- * remove_class_variable(sym) -> obj
- *
- * Removes the definition of the <i>sym</i>, returning that
- * constant's value.
- *
- * class Dummy
- * @@var = 99
- * puts @@var
- * p class_variables
- * remove_class_variable(:@@var)
- * p class_variables
- * end
- *
- * <em>produces:</em>
- *
- * 99
- * [:@@var]
- * []
- */
-
-static mrb_value
-mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod)
-{
- mrb_value val;
- mrb_sym id;
-
- mrb_get_args(mrb, "n", &id);
- check_cv_name_sym(mrb, id);
-
- val = mrb_iv_remove(mrb, mod, id);
- if (!mrb_undef_p(val)) return val;
-
- if (mrb_cv_defined(mrb, mod, id)) {
- mrb_name_error(mrb, id, "cannot remove %S for %S",
- mrb_sym2str(mrb, id), mod);
- }
-
- mrb_name_error(mrb, id, "class variable %S not defined for %S",
- mrb_sym2str(mrb, id), mod);
-
- /* not reached */
- return mrb_nil_value();
-}
-
-/* 15.2.2.4.34 */
-/*
- * call-seq:
- * mod.method_defined?(symbol) -> true or false
- *
- * Returns +true+ if the named method is defined by
- * _mod_ (or its included modules and, if _mod_ is a class,
- * its ancestors). Public and protected methods are matched.
- *
- * module A
- * def method1() end
- * end
- * class B
- * def method2() end
- * end
- * class C < B
- * include A
- * def method3() end
- * end
- *
- * A.method_defined? :method1 #=> true
- * C.method_defined? "method1" #=> true
- * C.method_defined? "method2" #=> true
- * C.method_defined? "method3" #=> true
- * C.method_defined? "method4" #=> false
- */
-
-static mrb_value
-mrb_mod_method_defined(mrb_state *mrb, mrb_value mod)
-{
- mrb_sym id;
-
- 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 = 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_sym2str(mrb, mid), mod);
-}
-
-/* 15.2.2.4.41 */
-/*
- * call-seq:
- * remove_method(symbol) -> self
- *
- * Removes the method identified by _symbol_ from the current
- * class. For an example, see <code>Module.undef_method</code>.
- */
-
-static mrb_value
-mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
-{
- mrb_int argc;
- mrb_value *argv;
-
- mrb_get_args(mrb, "*", &argv, &argc);
- while (argc--) {
- remove_method(mrb, mod, to_sym(mrb, *argv));
- argv++;
- }
- return mod;
-}
-
-
-
-static void
-check_const_name_str(mrb_state *mrb, mrb_value str)
-{
- if (RSTRING_LEN(str) < 1 || !ISUPPER(*RSTRING_PTR(str))) {
- mrb_name_error(mrb, mrb_intern_str(mrb, str), "wrong constant name %S", str);
- }
-}
-
static void
check_const_name_sym(mrb_state *mrb, mrb_sym id)
{
- check_const_name_str(mrb, mrb_sym2str(mrb, id));
-}
-
-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));
+ mrb_int len;
+ const char *name = mrb_sym2name_len(mrb, id, &len);
+ if (!mrb_const_name_p(mrb, name, len)) {
+ mrb_name_error(mrb, id, "wrong constant name %S", mrb_sym2str(mrb, id));
}
- return mrb_bool_value(mrb_const_defined_at(mrb, mod, id));
}
static mrb_value
@@ -2289,7 +1877,10 @@ mrb_mod_const_defined(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "n|b", &id, &inherit);
check_const_name_sym(mrb, id);
- return const_defined(mrb, mod, id, inherit);
+ if (inherit) {
+ return mrb_bool_value(mrb_const_defined(mrb, mod, id));
+ }
+ return mrb_bool_value(mrb_const_defined_at(mrb, mod, id));
}
static mrb_value
@@ -2316,7 +1907,7 @@ mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
}
/* const get with class path string */
- path = mrb_string_type(mrb, path);
+ path = mrb_ensure_string_type(mrb, path);
ptr = RSTRING_PTR(path);
len = RSTRING_LEN(path);
off = 0;
@@ -2326,7 +1917,14 @@ mrb_mod_const_get(mrb_state *mrb, mrb_value mod)
end = (end == -1) ? len : end;
id = mrb_intern(mrb, ptr+off, end-off);
mod = mrb_const_get_sym(mrb, mod, id);
- off = (end == len) ? end : end+2;
+ if (end == len)
+ off = end;
+ else {
+ off = end + 2;
+ if (off == len) { /* trailing "::" */
+ mrb_name_error(mrb, id, "wrong constant name '%S'", path);
+ }
+ }
}
return mod;
@@ -2379,11 +1977,79 @@ mrb_mod_const_missing(mrb_state *mrb, mrb_value mod)
return mrb_nil_value();
}
+/* 15.2.2.4.34 */
+/*
+ * call-seq:
+ * mod.method_defined?(symbol) -> true or false
+ *
+ * Returns +true+ if the named method is defined by
+ * _mod_ (or its included modules and, if _mod_ is a class,
+ * its ancestors). Public and protected methods are matched.
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * def method2() end
+ * end
+ * class C < B
+ * include A
+ * def method3() end
+ * end
+ *
+ * A.method_defined? :method1 #=> true
+ * C.method_defined? "method1" #=> true
+ * C.method_defined? "method2" #=> true
+ * C.method_defined? "method3" #=> true
+ * C.method_defined? "method4" #=> false
+ */
+
+static mrb_value
+mrb_mod_method_defined(mrb_state *mrb, mrb_value mod)
+{
+ mrb_sym id;
+
+ mrb_get_args(mrb, "n", &id);
+ return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id));
+}
+
+static mrb_value
+mod_define_method(mrb_state *mrb, mrb_value self)
+{
+ struct RClass *c = mrb_class_ptr(self);
+ struct RProc *p;
+ mrb_method_t m;
+ mrb_sym mid;
+ mrb_value proc = mrb_undef_value();
+ mrb_value blk;
+
+ mrb_get_args(mrb, "n|o&", &mid, &proc, &blk);
+ switch (mrb_type(proc)) {
+ case MRB_TT_PROC:
+ blk = proc;
+ break;
+ case MRB_TT_UNDEF:
+ /* ignored */
+ break;
+ default:
+ mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc)));
+ break;
+ }
+ if (mrb_nil_p(blk)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
+ }
+ p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
+ mrb_proc_copy(p, mrb_proc_ptr(blk));
+ p->flags |= MRB_PROC_STRICT;
+ MRB_METHOD_FROM_PROC(m, p);
+ mrb_define_method_raw(mrb, c, mid, m);
+ return mrb_symbol_value(mid);
+}
+
static mrb_value
-mrb_mod_s_constants(mrb_state *mrb, mrb_value mod)
+top_define_method(mrb_state *mrb, mrb_value self)
{
- mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented");
- return mrb_nil_value(); /* not reached */
+ return mod_define_method(mrb, mrb_obj_value(mrb->object_class));
}
static mrb_value
@@ -2398,7 +2064,7 @@ mrb_mod_eqq(mrb_state *mrb, mrb_value mod)
return mrb_bool_value(eqq);
}
-MRB_API mrb_value
+static mrb_value
mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
{
mrb_value *argv;
@@ -2439,8 +2105,6 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self);
/* implementation of instance_eval */
mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
-/* implementation of Module.nesting */
-mrb_value mrb_mod_s_nesting(mrb_state*, mrb_value);
static mrb_value
inspect_main(mrb_state *mrb, mrb_value mod)
@@ -2489,8 +2153,9 @@ mrb_init_class(mrb_state *mrb)
mrb_define_method(mrb, bob, "!", mrb_bob_not, MRB_ARGS_NONE());
mrb_define_method(mrb, bob, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */
mrb_define_method(mrb, bob, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, bob, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.3 */
- mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.4 */
+ mrb_define_method(mrb, bob, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.4 */
+ mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.5 */
+ mrb_define_method(mrb, bob, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */
mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */
mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1));
@@ -2500,9 +2165,6 @@ mrb_init_class(mrb_state *mrb)
mrb_define_method(mrb, cls, "inherited", mrb_bob_init, MRB_ARGS_REQ(1));
MRB_SET_INSTANCE_TT(mod, MRB_TT_MODULE);
- mrb_define_method(mrb, mod, "class_variable_defined?", mrb_mod_cvar_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.16 */
- mrb_define_method(mrb, mod, "class_variable_get", mrb_mod_cvar_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.17 */
- 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, "prepended", mrb_bob_init, MRB_ARGS_REQ(1));
@@ -2511,18 +2173,12 @@ mrb_init_class(mrb_state *mrb)
mrb_define_method(mrb, mod, "append_features", mrb_mod_append_features, MRB_ARGS_REQ(1)); /* 15.2.2.4.10 */
mrb_define_method(mrb, mod, "class_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.15 */
mrb_define_method(mrb, mod, "included", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.29 */
- mrb_define_method(mrb, mod, "included_modules", mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */
mrb_define_method(mrb, mod, "initialize", mrb_mod_initialize, MRB_ARGS_NONE()); /* 15.2.2.4.31 */
- 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());
@@ -2533,14 +2189,11 @@ mrb_init_class(mrb_state *mrb)
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 */
mrb_define_method(mrb, mod, "remove_const", mrb_mod_remove_const, MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */
mrb_define_method(mrb, mod, "const_missing", mrb_mod_const_missing, MRB_ARGS_REQ(1));
+ 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, "define_method", mod_define_method, MRB_ARGS_ARG(1,1));
- mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, MRB_ARGS_NONE()); /* 15.2.2.4.19 */
- mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1));
- mrb_define_class_method(mrb, mod, "constants", mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.1 */
- mrb_define_class_method(mrb, mod, "nesting", mrb_mod_s_nesting, MRB_ARGS_REQ(0)); /* 15.2.2.3.2 */
+ mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); /* 15.2.2.4.7 */
mrb_undef_method(mrb, cls, "append_features");
mrb_undef_method(mrb, cls, "extend_object");
diff --git a/src/codedump.c b/src/codedump.c
index d79a65a70..12d609075 100644
--- a/src/codedump.c
+++ b/src/codedump.c
@@ -6,457 +6,526 @@
#include <mruby/proc.h>
#ifndef MRB_DISABLE_STDIO
-static int
-print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre)
+static void
+print_r(mrb_state *mrb, mrb_irep *irep, size_t n)
{
size_t i;
- if (n == 0) return 0;
+ if (n == 0) return;
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;
+ printf(" R%d:%s", (int)n, mrb_sym2name(mrb, sym));
+ break;
}
}
- 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)
+print_lv_a(mrb_state *mrb, mrb_irep *irep, uint16_t a)
{
- int pre = 0;
+ if (!irep->lv || a >= irep->nlocals || a == 0) {
+ printf("\n");
+ return;
+ }
+ printf("\t;");
+ print_r(mrb, irep, a);
+ printf("\n");
+}
- if (!irep->lv
- || ((!(r & RA) || GETARG_A(c) >= irep->nlocals)
- && (!(r & RB) || GETARG_B(c) >= irep->nlocals))) {
+static void
+print_lv_ab(mrb_state *mrb, mrb_irep *irep, uint16_t a, uint16_t b)
+{
+ if (!irep->lv || (a >= irep->nlocals && b >= irep->nlocals) || a+b == 0) {
printf("\n");
return;
}
- printf("\t; ");
- if (r & RA) {
- pre = print_r(mrb, irep, GETARG_A(c), 0);
+ printf("\t;");
+ if (a > 0) print_r(mrb, irep, a);
+ if (b > 0) print_r(mrb, irep, b);
+ printf("\n");
+}
+
+static void
+print_header(mrb_state *mrb, mrb_irep *irep, ptrdiff_t i)
+{
+ int32_t line;
+
+ line = mrb_debug_get_line(mrb, irep, i);
+ if (line < 0) {
+ printf(" ");
}
- if (r & RB) {
- print_r(mrb, irep, GETARG_B(c), pre);
+ else {
+ printf("%5d ", line);
}
- printf("\n");
+
+ printf("%03d ", (int)i);
}
-#endif
+
+#define CASE(insn,ops) case insn: FETCH_ ## ops (); L_ ## insn
static void
codedump(mrb_state *mrb, mrb_irep *irep)
{
-#ifndef MRB_DISABLE_STDIO
- int i;
int ai;
- mrb_code c;
+ mrb_code *pc, *pcend;
+ mrb_code ins;
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);
+ printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d iseq=%d\n", (void*)irep,
+ irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen, (int)irep->ilen);
+
+ if (irep->lv) {
+ int i;
+
+ printf("local variable names:\n");
+ for (i = 1; i < irep->nlocals; ++i) {
+ char const *s = mrb_sym2name(mrb, irep->lv[i - 1].name);
+ int n = irep->lv[i - 1].r ? irep->lv[i - 1].r : i;
+ printf(" R%d:%s\n", n, s ? s : "");
+ }
+ }
+
+ pc = irep->iseq;
+ pcend = pc + irep->ilen;
+ while (pc < pcend) {
+ ptrdiff_t i;
+ uint32_t a;
+ uint16_t b;
+ uint8_t c;
- for (i = 0; i < (int)irep->ilen; i++) {
ai = mrb_gc_arena_save(mrb);
- next_file = mrb_debug_get_filename(irep, i);
+ i = pc - irep->iseq;
+ next_file = mrb_debug_get_filename(mrb, 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:
+ print_header(mrb, irep, i);
+ ins = READ_B();
+ switch (ins) {
+ CASE(OP_NOP, Z):
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);
+ CASE(OP_MOVE, BB):
+ printf("OP_MOVE\tR%d\tR%d\t", a, b);
+ print_lv_ab(mrb, irep, a, b);
break;
- case OP_LOADL:
+ CASE(OP_LOADL, BB):
{
- mrb_value v = irep->pool[GETARG_Bx(c)];
+ mrb_value v = irep->pool[b];
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));
+ printf("OP_LOADL\tR%d\tL(%d)\t; %s", a, b, 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 (%d)\n", i+GETARG_sBx(c), GETARG_sBx(c));
- break;
- case OP_JMPIF:
- printf("OP_JMPIF\tR%d\t%03d (%d)\n", GETARG_A(c), i+GETARG_sBx(c), GETARG_sBx(c));
- break;
- case OP_JMPNOT:
- printf("OP_JMPNOT\tR%d\t%03d (%d)\n", GETARG_A(c), i+GETARG_sBx(c), 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_CALL:
- printf("OP_CALL\tR%d\n", GETARG_A(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);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADI, BB):
+ printf("OP_LOADI\tR%d\t%d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADINEG, BB):
+ printf("OP_LOADI\tR%d\t-%d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADI__1, B):
+ printf("OP_LOADI__1\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADI_0, B): goto L_LOADI;
+ CASE(OP_LOADI_1, B): goto L_LOADI;
+ CASE(OP_LOADI_2, B): goto L_LOADI;
+ CASE(OP_LOADI_3, B): goto L_LOADI;
+ CASE(OP_LOADI_4, B): goto L_LOADI;
+ CASE(OP_LOADI_5, B): goto L_LOADI;
+ CASE(OP_LOADI_6, B): goto L_LOADI;
+ CASE(OP_LOADI_7, B):
+ L_LOADI:
+ printf("OP_LOADI_%d\tR%d\t\t", ins-(int)OP_LOADI_0, a);
+ print_lv_a(mrb, irep, a);
break;
-
- case OP_ENTER:
+ CASE(OP_LOADSYM, BB):
+ printf("OP_LOADSYM\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADNIL, B):
+ printf("OP_LOADNIL\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADSELF, B):
+ printf("OP_LOADSELF\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADT, B):
+ printf("OP_LOADT\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LOADF, B):
+ printf("OP_LOADF\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETGV, BB):
+ printf("OP_GETGV\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETGV, BB):
+ printf("OP_SETGV\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETSV, BB):
+ printf("OP_GETSV\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETSV, BB):
+ printf("OP_SETSV\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETCONST, BB):
+ printf("OP_GETCONST\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETCONST, BB):
+ printf("OP_SETCONST\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETMCNST, BB):
+ printf("OP_GETMCNST\tR%d\tR%d::%s", a, a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETMCNST, BB):
+ printf("OP_SETMCNST\tR%d::%s\tR%d", a+1, mrb_sym2name(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETIV, BB):
+ printf("OP_GETIV\tR%d\t%s", a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETIV, BB):
+ printf("OP_SETIV\t%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETUPVAR, BBB):
+ printf("OP_GETUPVAR\tR%d\t%d\t%d", a, b, c);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETUPVAR, BBB):
+ printf("OP_SETUPVAR\tR%d\t%d\t%d", a, b, c);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_GETCV, BB):
+ printf("OP_GETCV\tR%d\t%s", a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SETCV, BB):
+ printf("OP_SETCV\t%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_JMP, S):
+ printf("OP_JMP\t\t%03d\n", a);
+ break;
+ CASE(OP_JMPIF, BS):
+ printf("OP_JMPIF\tR%d\t%03d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_JMPNOT, BS):
+ printf("OP_JMPNOT\tR%d\t%03d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_JMPNIL, BS):
+ printf("OP_JMPNIL\tR%d\t%03d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_SENDV, BB):
+ printf("OP_SENDV\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ break;
+ CASE(OP_SENDVB, BB):
+ printf("OP_SENDVB\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ break;
+ CASE(OP_SEND, BBB):
+ printf("OP_SEND\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c);
+ break;
+ CASE(OP_SENDB, BBB):
+ printf("OP_SENDB\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c);
+ break;
+ CASE(OP_CALL, Z):
+ printf("OP_CALL\n");
+ break;
+ CASE(OP_SUPER, BB):
+ printf("OP_SUPER\tR%d\t%d\n", a, b);
+ break;
+ CASE(OP_ARGARY, BS):
+ printf("OP_ARGARY\tR%d\t%d:%d:%d:%d (%d)", a,
+ (b>>11)&0x3f,
+ (b>>10)&0x1,
+ (b>>5)&0x1f,
+ (b>>4)&0x1,
+ (b>>0)&0xf);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_ENTER, W):
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:
- printf("\tnormal\t"); break;
- case OP_R_RETURN:
- printf("\treturn\t"); break;
- case OP_R_BREAK:
- printf("\tbreak\t"); break;
- default:
- printf("\tbroken\t"); break;
- }
- print_lv(mrb, irep, c, RA);
+ MRB_ASPEC_REQ(a),
+ MRB_ASPEC_OPT(a),
+ MRB_ASPEC_REST(a),
+ MRB_ASPEC_POST(a),
+ MRB_ASPEC_KEY(a),
+ MRB_ASPEC_KDICT(a),
+ MRB_ASPEC_BLOCK(a));
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);
+ CASE(OP_KEY_P, BB):
+ printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
break;
-
- case OP_LAMBDA:
- printf("OP_LAMBDA\tR%d\tI(%+d)\t", GETARG_A(c), GETARG_b(c)+1);
- switch (GETARG_c(c)) {
- case OP_L_METHOD:
- printf("method"); break;
- case OP_L_BLOCK:
- printf("block"); break;
- case OP_L_LAMBDA:
- printf("lambda"); break;
- }
- print_lv(mrb, irep, c, RA);
+ CASE(OP_KEYEND, Z):
+ printf("OP_KEYEND\n");
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);
+ CASE(OP_KARG, BB):
+ printf("OP_KARG\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
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);
+ CASE(OP_RETURN, B):
+ printf("OP_RETURN\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
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\t\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\t\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\t\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\t\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\t\tR%d\t:%s\t%d\n", GETARG_A(c),
- mrb_sym2name(mrb, irep->syms[GETARG_B(c)]),
- GETARG_C(c));
+ CASE(OP_RETURN_BLK, B):
+ printf("OP_RETURN_BLK\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
break;
-
- case OP_STOP:
- printf("OP_STOP\n");
+ CASE(OP_BREAK, B):
+ printf("OP_BREAK\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
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);
+ CASE(OP_BLKPUSH, BS):
+ printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d (%d)", a,
+ (b>>11)&0x3f,
+ (b>>10)&0x1,
+ (b>>5)&0x1f,
+ (b>>4)&0x1,
+ (b>>0)&0xf);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_LAMBDA, BB):
+ printf("OP_LAMBDA\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
+ break;
+ CASE(OP_BLOCK, BB):
+ printf("OP_BLOCK\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
+ break;
+ CASE(OP_METHOD, BB):
+ printf("OP_METHOD\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
+ break;
+ CASE(OP_RANGE_INC, B):
+ printf("OP_RANGE_INC\tR%d\n", a);
+ break;
+ CASE(OP_RANGE_EXC, B):
+ printf("OP_RANGE_EXC\tR%d\n", a);
+ break;
+ CASE(OP_DEF, BB):
+ printf("OP_DEF\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b]));
+ break;
+ CASE(OP_UNDEF, B):
+ printf("OP_UNDEF\t:%s\n", mrb_sym2name(mrb, irep->syms[a]));
+ break;
+ CASE(OP_ALIAS, BB):
+ printf("OP_ALIAS\t:%s\t%s\n", mrb_sym2name(mrb, irep->syms[a]), mrb_sym2name(mrb, irep->syms[b]));
+ break;
+ CASE(OP_ADD, B):
+ printf("OP_ADD\tR%d\t\n", a);
+ break;
+ CASE(OP_ADDI, BB):
+ printf("OP_ADDI\tR%d\t%d\n", a, b);
+ break;
+ CASE(OP_SUB, B):
+ printf("OP_SUB\tR%d\t\n", a);
+ break;
+ CASE(OP_SUBI, BB):
+ printf("OP_SUBI\tR%d\t%d\n", a, b);
+ break;
+ CASE(OP_MUL, B):
+ printf("OP_MUL\tR%d\t\n", a);
+ break;
+ CASE(OP_DIV, B):
+ printf("OP_DIV\tR%d\t\n", a);
+ break;
+ CASE(OP_LT, B):
+ printf("OP_LT\t\tR%d\t\n", a);
+ break;
+ CASE(OP_LE, B):
+ printf("OP_LE\t\tR%d\t\n", a);
+ break;
+ CASE(OP_GT, B):
+ printf("OP_GT\t\tR%d\t\n", a);
+ break;
+ CASE(OP_GE, B):
+ printf("OP_GE\t\tR%d\t\n", a);
+ break;
+ CASE(OP_EQ, B):
+ printf("OP_EQ\t\tR%d\t\n", a);
+ break;
+ CASE(OP_ARRAY, BB):
+ printf("OP_ARRAY\tR%d\t%d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_ARRAY2, BBB):
+ printf("OP_ARRAY\tR%d\tR%d\t%d\t", a, b, c);
+ print_lv_ab(mrb, irep, a, b);
break;
- case OP_ARYCAT:
- printf("OP_ARYCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
+ CASE(OP_ARYCAT, B):
+ printf("OP_ARYCAT\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
break;
- case OP_ARYPUSH:
- printf("OP_ARYPUSH\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
+ CASE(OP_ARYPUSH, B):
+ printf("OP_ARYPUSH\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
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);
+ CASE(OP_ARYDUP, B):
+ printf("OP_ARYDUP\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
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);
+ CASE(OP_AREF, BBB):
+ printf("OP_AREF\tR%d\tR%d\t%d", a, b, c);
+ print_lv_ab(mrb, irep, a, b);
break;
- case OP_STRING:
+ CASE(OP_ASET, BBB):
+ printf("OP_ASET\tR%d\tR%d\t%d", a, b, c);
+ print_lv_ab(mrb, irep, a, b);
+ break;
+ CASE(OP_APOST, BBB):
+ printf("OP_APOST\tR%d\t%d\t%d", a, b, c);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_INTERN, B):
+ printf("OP_INTERN\tR%d", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_STRING, BB):
{
- mrb_value v = irep->pool[GETARG_Bx(c)];
+ mrb_value v = irep->pool[b];
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));
+ printf("OP_STRING\tR%d\tL(%d)\t; %s", a, b, RSTRING_PTR(s));
}
- print_lv(mrb, irep, c, RA);
+ print_lv_a(mrb, irep, a);
break;
- case OP_STRCAT:
- printf("OP_STRCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
+ CASE(OP_STRCAT, B):
+ printf("OP_STRCAT\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
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);
+ CASE(OP_HASH, BB):
+ printf("OP_HASH\tR%d\t%d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_HASHADD, BB):
+ printf("OP_HASHADD\tR%d\t%d\t", a, b);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_HASHCAT, B):
+ printf("OP_HASHCAT\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
break;
- case OP_OCLASS:
- printf("OP_OCLASS\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
+ CASE(OP_OCLASS, B):
+ printf("OP_OCLASS\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
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);
+ CASE(OP_CLASS, BB):
+ printf("OP_CLASS\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
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);
+ CASE(OP_MODULE, BB):
+ printf("OP_MODULE\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b]));
+ print_lv_a(mrb, irep, a);
break;
- case OP_EXEC:
- printf("OP_EXEC\tR%d\tI(%+d)", GETARG_A(c), GETARG_Bx(c)+1);
- print_lv(mrb, irep, c, RA);
+ CASE(OP_EXEC, BB):
+ printf("OP_EXEC\tR%d\tI(%d:%p)", a, b, irep->reps[b]);
+ print_lv_a(mrb, irep, a);
break;
- case OP_SCLASS:
- printf("OP_SCLASS\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c));
- print_lv(mrb, irep, c, RAB);
+ CASE(OP_SCLASS, B):
+ printf("OP_SCLASS\tR%d\t", a);
+ print_lv_a(mrb, irep, a);
break;
- case OP_TCLASS:
- printf("OP_TCLASS\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
+ CASE(OP_TCLASS, B):
+ printf("OP_TCLASS\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
break;
- case OP_ERR:
+ CASE(OP_ERR, B):
{
- mrb_value v = irep->pool[GETARG_Bx(c)];
+ mrb_value v = irep->pool[a];
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);
+ CASE(OP_EPUSH, B):
+ printf("OP_EPUSH\t\t:I(%d:%p)\n", a, irep->reps[a]);
break;
- case OP_ONERR:
- printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c));
+ CASE(OP_ONERR, S):
+ printf("OP_ONERR\t%03d\n", a);
+ break;
+ CASE(OP_EXCEPT, B):
+ printf("OP_EXCEPT\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_RESCUE, BB):
+ printf("OP_RESCUE\tR%d\tR%d", a, b);
+ print_lv_ab(mrb, irep, a, b);
+ break;
+ CASE(OP_RAISE, B):
+ printf("OP_RAISE\tR%d\t\t", a);
+ print_lv_a(mrb, irep, a);
+ break;
+ CASE(OP_POPERR, B):
+ printf("OP_POPERR\t%d\t\t\n", a);
+ break;
+ CASE(OP_EPOP, B):
+ printf("OP_EPOP\t%d\n", a);
break;
- case OP_RESCUE:
- {
- int a = GETARG_A(c);
- int b = GETARG_B(c);
- int cnt = GETARG_C(c);
- if (b == 0) {
- printf("OP_RESCUE\tR%d\t\t%s", a, cnt ? "cont" : "");
- print_lv(mrb, irep, c, RA);
- break;
- }
- else {
- printf("OP_RESCUE\tR%d\tR%d\t%s", a, b, cnt ? "cont" : "");
- print_lv(mrb, irep, c, RAB);
- break;
- }
- }
+ CASE(OP_DEBUG, BBB):
+ printf("OP_DEBUG\t%d\t%d\t%d\n", a, b, c);
break;
- case OP_RAISE:
- printf("OP_RAISE\tR%d\t\t", GETARG_A(c));
- print_lv(mrb, irep, c, RA);
+
+ CASE(OP_STOP, Z):
+ printf("OP_STOP\n");
break;
- case OP_POPERR:
- printf("OP_POPERR\t%d\t\t\n", GETARG_A(c));
+
+ CASE(OP_EXT1, Z):
+ ins = READ_B();
+ printf("OP_EXT1\n");
+ print_header(mrb, irep, pc-irep->iseq-2);
+ switch (ins) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); goto L_OP_ ## i;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
break;
- case OP_EPOP:
- printf("OP_EPOP\t%d\n", GETARG_A(c));
+ CASE(OP_EXT2, Z):
+ ins = READ_B();
+ printf("OP_EXT2\n");
+ print_header(mrb, irep, pc-irep->iseq-2);
+ switch (ins) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); goto L_OP_ ## i;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ break;
+ CASE(OP_EXT3, Z):
+ ins = READ_B();
+ printf("OP_EXT3\n");
+ print_header(mrb, irep, pc-irep->iseq-2);
+ switch (ins) {
+#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); goto L_OP_ ## i;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
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));
+ printf("OP_unknown (0x%x)\n", ins);
break;
}
mrb_gc_arena_restore(mrb, ai);
}
printf("\n");
-#endif
}
static void
@@ -469,9 +538,12 @@ codedump_recur(mrb_state *mrb, mrb_irep *irep)
codedump_recur(mrb, irep->reps[i]);
}
}
+#endif
void
mrb_codedump_all(mrb_state *mrb, struct RProc *proc)
{
+#ifndef MRB_DISABLE_STDIO
codedump_recur(mrb, proc->body.irep);
+#endif
}
diff --git a/src/debug.c b/src/debug.c
index e55f11d4f..0dc02a1e3 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -51,25 +51,25 @@ select_line_type(const uint16_t *lines, size_t lines_len)
}
MRB_API char const*
-mrb_debug_get_filename(mrb_irep *irep, ptrdiff_t pc)
+mrb_debug_get_filename(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc)
{
if (irep && pc >= 0 && pc < irep->ilen) {
mrb_irep_debug_info_file* f = NULL;
- if (!irep->debug_info) { return irep->filename; }
+ if (!irep->debug_info) return NULL;
else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
- return f->filename;
+ return mrb_sym2name_len(mrb, f->filename_sym, NULL);
}
}
return NULL;
}
MRB_API int32_t
-mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc)
+mrb_debug_get_line(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc)
{
if (irep && pc >= 0 && pc < irep->ilen) {
mrb_irep_debug_info_file* f = NULL;
if (!irep->debug_info) {
- return irep->lines? irep->lines[pc] : -1;
+ return -1;
}
else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
switch (f->line_type) {
@@ -122,82 +122,79 @@ mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
}
MRB_API mrb_irep_debug_info_file*
-mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep,
+mrb_debug_info_append_file(mrb_state *mrb, mrb_irep_debug_info *d,
+ const char *filename, uint16_t *lines,
uint32_t start_pos, uint32_t end_pos)
{
- mrb_irep_debug_info *info;
- mrb_irep_debug_info_file *ret;
+ mrb_irep_debug_info_file *f;
uint32_t file_pc_count;
size_t fn_len;
- mrb_int len;
uint32_t i;
- if (!irep->debug_info) { return NULL; }
+ if (!d) return NULL;
+ if (start_pos == end_pos) return NULL;
- mrb_assert(irep->filename);
- mrb_assert(irep->lines);
+ mrb_assert(filename);
+ mrb_assert(lines);
- info = irep->debug_info;
-
- if (info->flen > 0 && strcmp(irep->filename, info->files[info->flen - 1]->filename) == 0) {
- return NULL;
+ if (d->flen > 0) {
+ const char *fn = mrb_sym2name_len(mrb, d->files[d->flen - 1]->filename_sym, NULL);
+ if (strcmp(filename, fn) == 0)
+ return NULL;
}
- ret = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*ret));
- info->files =
- (mrb_irep_debug_info_file**)(
- info->files
- ? mrb_realloc(mrb, info->files, sizeof(mrb_irep_debug_info_file*) * (info->flen + 1))
+ f = (mrb_irep_debug_info_file*)mrb_malloc(mrb, sizeof(*f));
+ d->files = (mrb_irep_debug_info_file**)(
+ d->files
+ ? mrb_realloc(mrb, d->files, sizeof(mrb_irep_debug_info_file*) * (d->flen + 1))
: mrb_malloc(mrb, sizeof(mrb_irep_debug_info_file*)));
- info->files[info->flen++] = ret;
+ d->files[d->flen++] = f;
file_pc_count = end_pos - start_pos;
- ret->start_pos = start_pos;
- info->pc_count = end_pos;
+ f->start_pos = start_pos;
+ d->pc_count = end_pos;
- fn_len = strlen(irep->filename);
- ret->filename_sym = mrb_intern(mrb, irep->filename, fn_len);
- len = 0;
- ret->filename = mrb_sym2name_len(mrb, ret->filename_sym, &len);
+ fn_len = strlen(filename);
+ f->filename_sym = mrb_intern(mrb, filename, fn_len);
- ret->line_type = select_line_type(irep->lines + start_pos, end_pos - start_pos);
- ret->lines.ptr = NULL;
+ f->line_type = select_line_type(lines + start_pos, end_pos - start_pos);
+ f->lines.ptr = NULL;
- switch (ret->line_type) {
+ switch (f->line_type) {
case mrb_debug_line_ary:
- ret->line_entry_count = file_pc_count;
- ret->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count);
+ f->line_entry_count = file_pc_count;
+ f->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count);
for (i = 0; i < file_pc_count; ++i) {
- ret->lines.ary[i] = irep->lines[start_pos + i];
+ f->lines.ary[i] = lines[start_pos + i];
}
break;
case mrb_debug_line_flat_map: {
uint16_t prev_line = 0;
mrb_irep_debug_info_line m;
- ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1);
- ret->line_entry_count = 0;
+ f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1);
+ f->line_entry_count = 0;
for (i = 0; i < file_pc_count; ++i) {
- if (irep->lines[start_pos + i] == prev_line) { continue; }
+ if (lines[start_pos + i] == prev_line) { continue; }
- ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc(
- mrb, ret->lines.flat_map,
- sizeof(mrb_irep_debug_info_line) * (ret->line_entry_count + 1));
+ f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc(
+ mrb, f->lines.flat_map,
+ sizeof(mrb_irep_debug_info_line) * (f->line_entry_count + 1));
m.start_pos = start_pos + i;
- m.line = irep->lines[start_pos + i];
- ret->lines.flat_map[ret->line_entry_count] = m;
+ m.line = lines[start_pos + i];
+ f->lines.flat_map[f->line_entry_count] = m;
/* update */
- ++ret->line_entry_count;
- prev_line = irep->lines[start_pos + i];
+ ++f->line_entry_count;
+ prev_line = lines[start_pos + i];
}
} break;
default: mrb_assert(0); break;
}
- return ret;
+ return f;
}
MRB_API void
diff --git a/src/dump.c b/src/dump.c
index df1e171e4..f1e167e35 100644
--- a/src/dump.c
+++ b/src/dump.c
@@ -6,6 +6,7 @@
#include <string.h>
#include <limits.h>
+#include <math.h>
#include <mruby/dump.h>
#include <mruby/string.h>
#include <mruby/irep.h>
@@ -17,9 +18,9 @@
#ifndef MRB_WITHOUT_FLOAT
#ifdef MRB_USE_FLOAT
-#define MRB_FLOAT_FMT "%.8e"
+#define MRB_FLOAT_FMT "%.9g"
#else
-#define MRB_FLOAT_FMT "%.16e"
+#define MRB_FLOAT_FMT "%.17g"
#endif
#endif
@@ -81,34 +82,27 @@ static ptrdiff_t
write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags)
{
uint8_t *cur = buf;
- int iseq_no;
cur += uint32_to_bin(irep->ilen, cur); /* number of 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;
- }
+ memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code));
+ cur += irep->ilen * sizeof(mrb_code);
return cur - buf;
}
+#ifndef MRB_WITHOUT_FLOAT
+static mrb_value
+float_to_str(mrb_state *mrb, mrb_value flt)
+{
+ mrb_float f = mrb_float(flt);
+
+ if (isinf(f)) {
+ return f < 0 ? mrb_str_new_lit(mrb, "I") : mrb_str_new_lit(mrb, "i");
+ }
+ return mrb_float_to_str(mrb, flt, MRB_FLOAT_FMT);
+}
+#endif
static size_t
get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
@@ -135,7 +129,7 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
#ifndef MRB_WITHOUT_FLOAT
case MRB_TT_FLOAT:
- str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
+ str = float_to_str(mrb, irep->pool[pool_no]);
{
mrb_int len = RSTRING_LEN(str);
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
@@ -184,7 +178,7 @@ write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
#ifndef MRB_WITHOUT_FLOAT
case MRB_TT_FLOAT:
cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */
- str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT);
+ str = float_to_str(mrb, irep->pool[pool_no]);
break;
#endif
@@ -372,118 +366,6 @@ write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p,
return MRB_DUMP_OK;
}
-static int
-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_ident, RITE_SECTION_LINENO_IDENT, sizeof(header->section_ident));
- uint32_to_bin((uint32_t)section_size, header->section_size);
-
- return MRB_DUMP_OK;
-}
-
-static size_t
-get_lineno_record_size(mrb_state *mrb, mrb_irep *irep)
-{
- size_t size = 0;
-
- size += sizeof(uint32_t); /* record size */
- size += sizeof(uint16_t); /* filename size */
- if (irep->filename) {
- size += strlen(irep->filename); /* filename */
- }
- size += sizeof(uint32_t); /* niseq */
- if (irep->lines) {
- size += sizeof(uint16_t) * irep->ilen; /* lineno */
- }
-
- return size;
-}
-
-static size_t
-write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
-{
- uint8_t *cur = bin;
- int iseq_no;
- size_t filename_len;
- ptrdiff_t diff;
-
- cur += sizeof(uint32_t); /* record size */
-
- if (irep->filename) {
- filename_len = strlen(irep->filename);
- }
- else {
- filename_len = 0;
- }
- mrb_assert_int_fit(size_t, filename_len, uint16_t, UINT16_MAX);
- cur += uint16_to_bin((uint16_t)filename_len, cur); /* filename size */
-
- if (filename_len) {
- memcpy(cur, irep->filename, filename_len);
- cur += filename_len; /* filename */
- }
-
- if (irep->lines) {
- mrb_assert_int_fit(size_t, irep->ilen, uint32_t, UINT32_MAX);
- cur += uint32_to_bin((uint32_t)(irep->ilen), cur); /* niseq */
- for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) {
- cur += uint16_to_bin(irep->lines[iseq_no], cur); /* opcode */
- }
- }
- else {
- cur += uint32_to_bin(0, cur); /* niseq */
- }
-
- diff = cur - bin;
- mrb_assert_int_fit(ptrdiff_t, diff, uint32_t, UINT32_MAX);
-
- uint32_to_bin((uint32_t)diff, bin); /* record size */
-
- mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
- return (size_t)diff;
-}
-
-static size_t
-write_lineno_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin)
-{
- size_t rlen, size = 0;
- int i;
-
- rlen = write_lineno_record_1(mrb, irep, bin);
- bin += rlen;
- size += rlen;
- for (i=0; i<irep->rlen; i++) {
- rlen = write_lineno_record(mrb, irep, bin);
- bin += rlen;
- size += rlen;
- }
- return size;
-}
-
-static int
-write_section_lineno(mrb_state *mrb, mrb_irep *irep, uint8_t *bin)
-{
- size_t section_size = 0;
- size_t rlen = 0; /* size of irep record */
- uint8_t *cur = bin;
-
- if (mrb == NULL || bin == NULL) {
- return MRB_DUMP_INVALID_ARGUMENT;
- }
-
- cur += sizeof(struct rite_section_lineno_header);
- section_size += sizeof(struct rite_section_lineno_header);
-
- rlen = write_lineno_record(mrb, irep, cur);
- section_size += rlen;
-
- write_section_lineno_header(mrb, section_size, bin);
-
- return MRB_DUMP_OK;
-}
-
static size_t
get_debug_record_size(mrb_state *mrb, mrb_irep *irep)
{
@@ -857,26 +739,26 @@ write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8
}
static mrb_bool
-is_debug_info_defined(mrb_irep *irep)
+debug_info_defined_p(mrb_irep *irep)
{
int i;
if (!irep->debug_info) return FALSE;
for (i=0; i<irep->rlen; i++) {
- if (!is_debug_info_defined(irep->reps[i])) return FALSE;
+ if (!debug_info_defined_p(irep->reps[i])) return FALSE;
}
return TRUE;
}
static mrb_bool
-is_lv_defined(mrb_irep *irep)
+lv_defined_p(mrb_irep *irep)
{
int i;
if (irep->lv) { return TRUE; }
for (i = 0; i < irep->rlen; ++i) {
- if (is_lv_defined(irep->reps[i])) { return TRUE; }
+ if (lv_defined_p(irep->reps[i])) { return TRUE; }
}
return FALSE;
@@ -905,7 +787,7 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *
size_t section_irep_size;
size_t section_lineno_size = 0, section_lv_size = 0;
uint8_t *cur = NULL;
- mrb_bool const debug_info_defined = is_debug_info_defined(irep), lv_defined = is_lv_defined(irep);
+ mrb_bool const debug_info_defined = debug_info_defined_p(irep), lv_defined = lv_defined_p(irep);
mrb_sym *lv_syms = NULL; uint32_t lv_syms_len = 0;
mrb_sym *filenames = NULL; uint16_t filenames_len = 0;
@@ -930,10 +812,6 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *
section_lineno_size += get_debug_record_size(mrb, irep);
}
- else {
- section_lineno_size += sizeof(struct rite_section_lineno_header);
- section_lineno_size += get_lineno_record_size(mrb, irep);
- }
}
if (lv_defined) {
@@ -961,12 +839,9 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *
if (flags & DUMP_DEBUG_INFO) {
if (debug_info_defined) {
result = write_section_debug(mrb, irep, cur, filenames, filenames_len);
- }
- else {
- result = write_section_lineno(mrb, irep, cur);
- }
- if (result != MRB_DUMP_OK) {
- goto error_exit;
+ if (result != MRB_DUMP_OK) {
+ goto error_exit;
+ }
}
cur += section_lineno_size;
}
diff --git a/src/enum.c b/src/enum.c
index ea05777b3..1e9445176 100644
--- a/src/enum.c
+++ b/src/enum.c
@@ -14,21 +14,9 @@ enum_update_hash(mrb_state *mrb, mrb_value self)
mrb_int hash;
mrb_int index;
mrb_int hv;
- mrb_value item_hash;
- mrb_get_args(mrb, "iio", &hash, &index, &item_hash);
- if (mrb_fixnum_p(item_hash)) {
- hv = mrb_fixnum(item_hash);
- }
-#ifndef MRB_WITHOUT_FLOAT
- else if (mrb_float_p(item_hash)) {
- hv = (mrb_int)mrb_float(item_hash);
- }
-#endif
- else {
- mrb_raise(mrb, E_TYPE_ERROR, "can't calculate hash");
- }
- hash ^= (hv << (index % 16));
+ mrb_get_args(mrb, "iii", &hash, &index, &hv);
+ hash ^= ((uint32_t)hv << (index % 16));
return mrb_fixnum_value(hash);
}
diff --git a/src/error.c b/src/error.c
index 599612b97..4c1b67c99 100644
--- a/src/error.c
+++ b/src/error.c
@@ -28,7 +28,7 @@ mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len)
MRB_API mrb_value
mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str)
{
- str = mrb_str_to_str(mrb, str);
+ mrb_to_str(mrb, str);
return mrb_obj_new(mrb, c, 1, &str);
}
@@ -208,8 +208,8 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc)
if (err && ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) {
mrb_irep *irep = ci->proc->body.irep;
- int32_t const line = mrb_debug_get_line(irep, err - irep->iseq);
- char const* file = mrb_debug_get_filename(irep, err - irep->iseq);
+ int32_t const line = mrb_debug_get_line(mrb, irep, err - irep->iseq);
+ char const* file = mrb_debug_get_filename(mrb, irep, err - irep->iseq);
if (line != -1 && file) {
mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "file"), mrb_str_new_cstr(mrb, file));
mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "line"), mrb_fixnum_value(line));
@@ -376,6 +376,7 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...)
str = mrb_vformat(mrb, fmt, ap);
fputs("warning: ", stderr);
fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr);
+ putc('\n', stderr);
va_end(ap);
#endif
}
@@ -483,6 +484,13 @@ mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt,
mrb_exc_raise(mrb, exc);
}
+MRB_API mrb_noreturn void
+mrb_frozen_error(mrb_state *mrb, void *frozen_obj)
+{
+ mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %S",
+ mrb_obj_value(mrb_class(mrb, mrb_obj_value(frozen_obj))));
+}
+
void
mrb_init_exception(mrb_state *mrb)
{
diff --git a/src/etc.c b/src/etc.c
index 1b8d44a53..18d2839d9 100644
--- a/src/etc.c
+++ b/src/etc.c
@@ -1,5 +1,5 @@
/*
-** etc.c -
+** etc.c
**
** See Copyright Notice in mruby.h
*/
@@ -8,8 +8,6 @@
#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)
@@ -67,23 +65,10 @@ mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
MRB_API mrb_sym
mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
{
- mrb_sym id;
-
- switch (mrb_type(name)) {
- default:
- 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);
- }
- /* fall through */
- case MRB_TT_STRING:
- name = mrb_str_intern(mrb, name);
- /* fall through */
- case MRB_TT_SYMBOL:
- id = mrb_symbol(name);
- }
- return id;
+ if (mrb_symbol_p(name)) return mrb_symbol(name);
+ if (mrb_string_p(name)) return mrb_intern_str(mrb, name);
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol nor a string", mrb_inspect(mrb, name));
+ return 0; /* not reached */
}
MRB_API mrb_int
@@ -166,6 +151,7 @@ mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
v.value.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);
v.value.fp->f = f;
+ MRB_SET_FROZEN_FLAG(v.value.bp);
return v;
}
@@ -176,6 +162,7 @@ mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f)
nf->tt = MRB_TT_FLOAT;
nf->c = mrb->float_class;
nf->f = f;
+ MRB_SET_FROZEN_FLAG(nf);
return mrb_obj_value(nf);
}
#endif /* MRB_WITHOUT_FLOAT */
@@ -191,23 +178,6 @@ mrb_word_boxing_cptr_value(mrb_state *mrb, void *p)
}
#endif /* MRB_WORD_BOXING */
-MRB_API mrb_bool
-mrb_regexp_p(mrb_state *mrb, mrb_value v)
-{
- if (mrb->flags & MRB_STATE_NO_REGEXP) {
- return FALSE;
- }
- if ((mrb->flags & MRB_STATE_REGEXP) || mrb_class_defined(mrb, REGEXP_CLASS)) {
- mrb->flags |= MRB_STATE_REGEXP;
- return mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS));
- }
- else {
- mrb->flags |= MRB_STATE_REGEXP;
- mrb->flags |= MRB_STATE_NO_REGEXP;
- }
- return FALSE;
-}
-
#if defined _MSC_VER && _MSC_VER < 1900
#ifndef va_copy
diff --git a/src/ext/.gitkeep b/src/ext/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
--- a/src/ext/.gitkeep
+++ /dev/null
diff --git a/src/fmt_fp.c b/src/fmt_fp.c
index f8a8f7904..1f1af6764 100644
--- a/src/fmt_fp.c
+++ b/src/fmt_fp.c
@@ -1,3 +1,5 @@
+#ifndef MRB_WITHOUT_FLOAT
+#if defined(MRB_DISABLE_STDIO) || defined(_WIN32) || defined(_WIN64)
/*
Most code in this file originates from musl (src/stdio/vfprintf.c)
@@ -36,7 +38,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <mruby.h>
#include <mruby/string.h>
-#ifndef MRB_WITHOUT_FLOAT
struct fmt_args {
mrb_state *mrb;
mrb_value str;
@@ -371,4 +372,17 @@ mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
}
return f.str;
}
+#else /* MRB_DISABLE_STDIO || _WIN32 || _WIN64 */
+#include <mruby.h>
+#include <stdio.h>
+
+mrb_value
+mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
+{
+ char buf[25];
+
+ snprintf(buf, sizeof(buf), fmt, mrb_float(flo));
+ return mrb_str_new_cstr(mrb, buf);
+}
+#endif /* MRB_DISABLE_STDIO || _WIN32 || _WIN64 */
#endif
diff --git a/src/gc.c b/src/gc.c
index 69121d630..b05d929a1 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -10,6 +10,7 @@
#include <mruby/array.h>
#include <mruby/class.h>
#include <mruby/data.h>
+#include <mruby/istruct.h>
#include <mruby/hash.h>
#include <mruby/proc.h>
#include <mruby/range.h>
@@ -109,8 +110,10 @@ typedef struct {
struct RHash hash;
struct RRange range;
struct RData data;
+ struct RIStruct istruct;
struct RProc proc;
struct REnv env;
+ struct RFiber fiber;
struct RException exc;
struct RBreak brk;
#ifdef MRB_WORD_BOXING
@@ -274,9 +277,36 @@ mrb_free(mrb_state *mrb, void *p)
(mrb->allocf)(mrb, p, 0, mrb->allocf_ud);
}
+MRB_API void*
+mrb_alloca(mrb_state *mrb, size_t size)
+{
+ mrb_value str = mrb_str_new(mrb, NULL, size);
+ return RSTRING_PTR(str);
+}
+
+static mrb_bool
+heap_p(mrb_gc *gc, struct RBasic *object)
+{
+ mrb_heap_page* page;
+
+ page = gc->heaps;
+ while (page) {
+ RVALUE *p;
+
+ p = objects(page);
+ if (&p[0].as.basic <= object && object <= &p[MRB_HEAP_PAGE_SIZE].as.basic) {
+ return TRUE;
+ }
+ page = page->next;
+ }
+ return FALSE;
+}
+
MRB_API mrb_bool
mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) {
- return is_dead(&mrb->gc, object);
+ mrb_gc *gc = &mrb->gc;
+ if (!heap_p(gc, object)) return TRUE;
+ return is_dead(gc, object);
}
static void
@@ -376,7 +406,7 @@ mrb_gc_init(mrb_state *mrb, mrb_gc *gc)
static void obj_free(mrb_state *mrb, struct RBasic *obj, int end);
-void
+static void
free_heap(mrb_state *mrb, mrb_gc *gc)
{
mrb_heap_page *page = gc->heaps;
@@ -443,9 +473,12 @@ mrb_gc_protect(mrb_state *mrb, mrb_value obj)
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);
+ mrb_sym root;
+ mrb_value table;
+ if (mrb_immediate_p(obj)) return;
+ root = mrb_intern_lit(mrb, GC_ROOT_NAME);
+ 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);
@@ -457,11 +490,14 @@ mrb_gc_register(mrb_state *mrb, mrb_value obj)
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);
+ mrb_sym root;
+ mrb_value table;
struct RArray *a;
mrb_int i;
+ if (mrb_immediate_p(obj)) return;
+ root = mrb_intern_lit(mrb, GC_ROOT_NAME);
+ table = mrb_gv_get(mrb, root);
if (mrb_nil_p(table)) return;
if (mrb_type(table) != MRB_TT_ARRAY) {
mrb_gv_set(mrb, root, mrb_nil_value());
@@ -470,7 +506,7 @@ mrb_gc_unregister(mrb_state *mrb, mrb_value obj)
a = mrb_ary_ptr(table);
mrb_ary_modify(mrb, a);
for (i = 0; i < ARY_LEN(a); i++) {
- if (mrb_obj_eq(mrb, ARY_PTR(a)[i], obj)) {
+ if (mrb_ptr(ARY_PTR(a)[i]) == mrb_ptr(obj)) {
mrb_int len = ARY_LEN(a)-1;
mrb_value *ptr = ARY_PTR(a);
@@ -548,21 +584,39 @@ add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
gc->gray_list = obj;
}
+static int
+ci_nregs(mrb_callinfo *ci)
+{
+ struct RProc *p = ci->proc;
+ int n = 0;
+
+ if (!p) {
+ if (ci->argc < 0) return 3;
+ return ci->argc+2;
+ }
+ if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
+ n = p->body.irep->nregs;
+ }
+ if (ci->argc < 0) {
+ if (n < 3) n = 3; /* self + args + blk */
+ }
+ if (ci->argc > n) {
+ n = ci->argc + 2; /* self + blk */
+ }
+ return n;
+}
+
static void
mark_context_stack(mrb_state *mrb, struct mrb_context *c)
{
size_t i;
size_t e;
mrb_value nil;
- mrb_int nregs;
if (c->stack == NULL) return;
e = c->stack - c->stbase;
if (c->ci) {
- nregs = c->ci->argc + 2;
- if (c->ci->nregs > nregs)
- nregs = c->ci->nregs;
- e += nregs;
+ e += ci_nregs(c->ci);
}
if (c->stbase + e > c->stend) e = c->stend - c->stbase;
for (i=0; i<e; i++) {
@@ -622,7 +676,7 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
case MRB_TT_ICLASS:
{
struct RClass *c = (struct RClass*)obj;
- if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN))
+ if (MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_ORIGIN))
mrb_gc_mark_mt(mrb, c);
mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super);
}
@@ -701,14 +755,7 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
break;
case MRB_TT_RANGE:
- {
- struct RRange *r = (struct RRange*)obj;
-
- if (r->edges) {
- mrb_gc_mark_value(mrb, r->edges->beg);
- mrb_gc_mark_value(mrb, r->edges->end);
- }
- }
+ mrb_gc_mark_range(mrb, (struct RRange*)obj);
break;
default:
@@ -761,7 +808,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
mrb_gc_free_iv(mrb, (struct RObject*)obj);
break;
case MRB_TT_ICLASS:
- if (MRB_FLAG_TEST(obj, MRB_FLAG_IS_ORIGIN))
+ if (MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN))
mrb_gc_free_mt(mrb, (struct RClass*)obj);
break;
case MRB_TT_ENV:
@@ -770,7 +817,8 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
if (MRB_ENV_STACK_SHARED_P(e)) {
/* cannot be freed */
- return;
+ e->stack = NULL;
+ break;
}
mrb_free(mrb, e->stack);
e->stack = NULL;
@@ -782,13 +830,13 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
struct mrb_context *c = ((struct RFiber*)obj)->cxt;
if (c && c != mrb->root_c) {
- mrb_callinfo *ci = c->ci;
- mrb_callinfo *ce = c->cibase;
+ if (!end && c->status != MRB_FIBER_TERMINATED) {
+ mrb_callinfo *ci = c->ci;
+ mrb_callinfo *ce = c->cibase;
- if (!end) {
while (ce <= ci) {
struct REnv *e = ci->env;
- if (e && !is_dead(&mrb->gc, e) &&
+ if (e && !mrb_object_dead_p(mrb, (struct RBasic*)e) &&
e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) {
mrb_env_unshare(mrb, e);
}
@@ -831,7 +879,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end)
break;
case MRB_TT_RANGE:
- mrb_free(mrb, ((struct RRange*)obj)->edges);
+ mrb_gc_free_range(mrb, ((struct RRange*)obj));
break;
case MRB_TT_DATA:
@@ -947,10 +995,14 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj)
size_t i;
mrb_callinfo *ci;
- if (!c) break;
+ if (!c || c->status == MRB_FIBER_TERMINATED) break;
+
/* mark stack */
i = c->stack - c->stbase;
- if (c->ci) i += c->ci->nregs;
+
+ if (c->ci) {
+ i += ci_nregs(c->ci);
+ }
if (c->stbase + i > c->stend) i = c->stend - c->stbase;
children += i;
@@ -1537,7 +1589,7 @@ mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, vo
mrb->jmp = &c_jmp;
gc_each_objects(mrb, &mrb->gc, callback, data);
mrb->jmp = prev_jmp;
- mrb->gc.iterating = iterating;
+ mrb->gc.iterating = iterating;
} MRB_CATCH(&c_jmp) {
mrb->gc.iterating = iterating;
mrb->jmp = prev_jmp;
diff --git a/src/hash.c b/src/hash.c
index db9d1d9c8..2a0a19363 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -8,7 +8,6 @@
#include <mruby/array.h>
#include <mruby/class.h>
#include <mruby/hash.h>
-#include <mruby/khash.h>
#include <mruby/string.h>
#include <mruby/variable.h>
@@ -17,14 +16,47 @@
mrb_int mrb_float_id(mrb_float f);
#endif
-static inline khint_t
-mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key)
+#ifndef MRB_HT_INIT_SIZE
+#define MRB_HT_INIT_SIZE 4
+#endif
+#define HT_SEG_INCREASE_RATIO 6 / 5
+
+struct segkv {
+ mrb_value key;
+ mrb_value val;
+};
+
+typedef struct segment {
+ uint16_t size;
+ struct segment *next;
+ struct segkv e[];
+} segment;
+
+typedef struct segindex {
+ size_t size;
+ size_t capa;
+ struct segkv *table[];
+} segindex;
+
+/* hash table structure */
+typedef struct htable {
+ segment *rootseg;
+ segment *lastseg;
+ mrb_int size;
+ uint16_t last_len;
+ segindex *index;
+} htable;
+
+static /* inline */ size_t
+ht_hash_func(mrb_state *mrb, htable *t, mrb_value key)
{
- enum mrb_vtype t = mrb_type(key);
+ enum mrb_vtype tt = mrb_type(key);
mrb_value hv;
- khint_t h;
+ size_t h;
+ segindex *index = t->index;
+ size_t capa = index ? index->capa : 0;
- switch (t) {
+ switch (tt) {
case MRB_TT_STRING:
h = mrb_str_hash(mrb, key);
break;
@@ -36,23 +68,26 @@ mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key)
#ifndef MRB_WITHOUT_FLOAT
case MRB_TT_FLOAT:
#endif
- h = (khint_t)mrb_obj_id(key);
+ h = (size_t)mrb_obj_id(key);
break;
default:
hv = mrb_funcall(mrb, key, "hash", 0);
- h = (khint_t)t ^ (khint_t)mrb_fixnum(hv);
+ h = (size_t)t ^ (size_t)mrb_fixnum(hv);
break;
}
- return kh_int_hash_func(mrb, h);
+ if (index && (index != t->index || capa != index->capa)) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified");
+ }
+ return ((h)^((h)<<2)^((h)>>2));
}
-static inline khint_t
-mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b)
+static inline mrb_bool
+ht_hash_equal(mrb_state *mrb, htable *t, mrb_value a, mrb_value b)
{
- enum mrb_vtype t = mrb_type(a);
+ enum mrb_vtype tt = mrb_type(a);
- switch (t) {
+ switch (tt) {
case MRB_TT_STRING:
return mrb_str_equal(mrb, a, b);
@@ -85,16 +120,461 @@ mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b)
#endif
default:
- return mrb_eql(mrb, a, b);
+ {
+ segindex *index = t->index;
+ size_t capa = index ? index->capa : 0;
+ mrb_bool eql = mrb_eql(mrb, a, b);
+ if (index && (index != t->index || capa != index->capa)) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified");
+ }
+ return eql;
+ }
+ }
+}
+
+/* Creates the hash table. */
+static htable*
+ht_new(mrb_state *mrb)
+{
+ htable *t;
+
+ t = (htable*)mrb_malloc(mrb, sizeof(htable));
+ t->size = 0;
+ t->rootseg = NULL;
+ t->lastseg = NULL;
+ t->last_len = 0;
+ t->index = NULL;
+
+ return t;
+}
+
+#define power2(v) do { \
+ v--;\
+ v |= v >> 1;\
+ v |= v >> 2;\
+ v |= v >> 4;\
+ v |= v >> 8;\
+ v |= v >> 16;\
+ v++;\
+} while (0)
+
+#ifndef UPPER_BOUND
+#define UPPER_BOUND(x) ((x)>>2|(x)>>1)
+#endif
+
+#define HT_MASK(index) ((index->capa)-1)
+
+/* Build index for the hash table */
+static void
+ht_index(mrb_state *mrb, htable *t)
+{
+ size_t size = (size_t)t->size;
+ size_t mask;
+ segindex *index = t->index;
+ segment *seg;
+ size_t i;
+
+ /* allocate index table */
+ if (index && index->size >= UPPER_BOUND(index->capa)) {
+ size = index->capa+1;
+ }
+ power2(size);
+ if (!index || index->capa < size) {
+ index = (segindex*)mrb_realloc_simple(mrb, index, sizeof(segindex)+sizeof(struct segkv*)*size);
+ if (index == NULL) {
+ mrb_free(mrb, t->index);
+ t->index = NULL;
+ return;
+ }
+ t->index = index;
+ }
+ index->size = t->size;
+ index->capa = size;
+ for (i=0; i<size; i++) {
+ index->table[i] = NULL;
+ }
+
+ /* rebuld index */
+ mask = HT_MASK(index);
+ seg = t->rootseg;
+ while (seg) {
+ for (i=0; i<seg->size; i++) {
+ mrb_value key;
+ size_t k, step = 0;
+
+ if (!seg->next && i >= (size_t)t->last_len) {
+ return;
+ }
+ key = seg->e[i].key;
+ if (mrb_undef_p(key)) continue;
+ k = ht_hash_func(mrb, t, key) & mask;
+ while (index->table[k]) {
+ k = (k+(++step)) & mask;
+ }
+ index->table[k] = &seg->e[i];
+ }
+ seg = seg->next;
+ }
+}
+
+/* Compacts the hash table removing deleted entries. */
+static void
+ht_compact(mrb_state *mrb, htable *t)
+{
+ segment *seg;
+ mrb_int i;
+ segment *seg2 = NULL;
+ mrb_int i2;
+ mrb_int size = 0;
+
+ if (t == NULL) return;
+ seg = t->rootseg;
+ if (t->index && (size_t)t->size == t->index->size) {
+ ht_index(mrb, t);
+ return;
+ }
+ while (seg) {
+ for (i=0; i<seg->size; i++) {
+ mrb_value k = seg->e[i].key;
+
+ if (!seg->next && i >= t->last_len) {
+ goto exit;
+ }
+ if (mrb_undef_p(k)) { /* found deleted key */
+ if (seg2 == NULL) {
+ seg2 = seg;
+ i2 = i;
+ }
+ }
+ else {
+ size++;
+ if (seg2 != NULL) {
+ seg2->e[i2++] = seg->e[i];
+ if (i2 >= seg2->size) {
+ seg2 = seg2->next;
+ i2 = 0;
+ }
+ }
+ }
+ }
+ seg = seg->next;
+ }
+ exit:
+ /* reached at end */
+ t->size = size;
+ if (seg2) {
+ seg = seg2->next;
+ seg2->next = NULL;
+ t->last_len = i2;
+ t->lastseg = seg2;
+ while (seg) {
+ seg2 = seg->next;
+ mrb_free(mrb, seg);
+ seg = seg2;
+ }
+ }
+ if (t->index) {
+ ht_index(mrb, t);
+ }
+}
+
+static segment*
+segment_alloc(mrb_state *mrb, segment *seg)
+{
+ uint32_t size;
+
+ if (!seg) size = MRB_HT_INIT_SIZE;
+ else {
+ size = seg->size*HT_SEG_INCREASE_RATIO + 1;
+ if (size > UINT16_MAX) size = UINT16_MAX;
+ }
+
+ seg = (segment*)mrb_malloc(mrb, sizeof(segment)+sizeof(struct segkv)*size);
+ seg->size = size;
+ seg->next = NULL;
+
+ return seg;
+}
+
+/* Set the value for the key in the indexed table. */
+static void
+ht_index_put(mrb_state *mrb, htable *t, mrb_value key, mrb_value val)
+{
+ segindex *index = t->index;
+ size_t k, sp, step = 0, mask;
+ segment *seg;
+
+ if (index->size >= UPPER_BOUND(index->capa)) {
+ /* need to expand table */
+ ht_compact(mrb, t);
+ index = t->index;
+ }
+ mask = HT_MASK(index);
+ sp = index->capa;
+ k = ht_hash_func(mrb, t, key) & mask;
+ while (index->table[k]) {
+ mrb_value key2 = index->table[k]->key;
+ if (mrb_undef_p(key2)) {
+ if (sp == index->capa) sp = k;
+ }
+ else if (ht_hash_equal(mrb, t, key, key2)) {
+ index->table[k]->val = val;
+ return;
+ }
+ k = (k+(++step)) & mask;
+ }
+ if (sp < index->capa) {
+ k = sp;
+ }
+
+ /* put the value at the last */
+ seg = t->lastseg;
+ if (t->last_len < seg->size) {
+ index->table[k] = &seg->e[t->last_len++];
+ }
+ else { /* append a new segment */
+ seg->next = segment_alloc(mrb, seg);
+ seg = seg->next;
+ seg->next = NULL;
+ t->lastseg = seg;
+ t->last_len = 1;
+ index->table[k] = &seg->e[0];
+ }
+ index->table[k]->key = key;
+ index->table[k]->val = val;
+ index->size++;
+ t->size++;
+}
+
+/* Set the value for the key in the hash table. */
+static void
+ht_put(mrb_state *mrb, htable *t, mrb_value key, mrb_value val)
+{
+ segment *seg;
+ mrb_int i, deleted = 0;
+
+ if (t == NULL) return;
+ if (t->index) {
+ ht_index_put(mrb, t, key, val);
+ return;
+ }
+ seg = t->rootseg;
+ while (seg) {
+ for (i=0; i<seg->size; i++) {
+ mrb_value k = seg->e[i].key;
+ /* Found room in last segment after last_len */
+ if (!seg->next && i >= t->last_len) {
+ seg->e[i].key = key;
+ seg->e[i].val = val;
+ t->last_len = i+1;
+ t->size++;
+ return;
+ }
+ if (mrb_undef_p(k)) {
+ deleted++;
+ continue;
+ }
+ if (ht_hash_equal(mrb, t, k, key)) {
+ seg->e[i].val = val;
+ return;
+ }
+ }
+ seg = seg->next;
+ }
+ /* Not found */
+
+ /* Compact if last segment has room */
+ if (deleted > 0 && deleted > MRB_HT_INIT_SIZE) {
+ ht_compact(mrb, t);
+ }
+ t->size++;
+
+ /* check if thre's room after compaction */
+ if (t->lastseg && t->last_len < t->lastseg->size) {
+ seg = t->lastseg;
+ i = t->last_len;
+ }
+ else {
+ /* append new segment */
+ seg = segment_alloc(mrb, t->lastseg);
+ i = 0;
+ if (t->rootseg == NULL) {
+ t->rootseg = seg;
+ }
+ else {
+ t->lastseg->next = seg;
+ }
+ t->lastseg = seg;
+ }
+ seg->e[i].key = key;
+ seg->e[i].val = val;
+ t->last_len = i+1;
+ if (t->index == NULL && t->size > MRB_HT_INIT_SIZE*4) {
+ ht_index(mrb, t);
}
}
-KHASH_DEFINE (ht, mrb_value, mrb_hash_value, TRUE, mrb_hash_ht_hash_func, mrb_hash_ht_hash_equal)
+/* Get a value for a key from the indexed table. */
+static mrb_bool
+ht_index_get(mrb_state *mrb, htable *t, mrb_value key, mrb_value *vp)
+{
+ segindex *index = t->index;
+ size_t mask = HT_MASK(index);
+ size_t k = ht_hash_func(mrb, t, key) & mask;
+ size_t step = 0;
+
+ while (index->table[k]) {
+ mrb_value key2 = index->table[k]->key;
+ if (!mrb_undef_p(key2) && ht_hash_equal(mrb, t, key, key2)) {
+ if (vp) *vp = index->table[k]->val;
+ return TRUE;
+ }
+ k = (k+(++step)) & mask;
+ }
+ return FALSE;
+}
+
+/* Get a value for a key from the hash table. */
+static mrb_bool
+ht_get(mrb_state *mrb, htable *t, mrb_value key, mrb_value *vp)
+{
+ segment *seg;
+ mrb_int i;
+
+ if (t == NULL) return FALSE;
+ if (t->index) {
+ return ht_index_get(mrb, t, key, vp);
+ }
+
+ seg = t->rootseg;
+ while (seg) {
+ for (i=0; i<seg->size; i++) {
+ mrb_value k = seg->e[i].key;
+
+ if (!seg->next && i >= t->last_len) {
+ return FALSE;
+ }
+ if (mrb_undef_p(k)) continue;
+ if (ht_hash_equal(mrb, t, k, key)) {
+ if (vp) *vp = seg->e[i].val;
+ return TRUE;
+ }
+ }
+ seg = seg->next;
+ }
+ return FALSE;
+}
+
+/* Deletes the value for the symbol from the hash table. */
+/* Deletion is done by overwriting keys by `undef`. */
+static mrb_bool
+ht_del(mrb_state *mrb, htable *t, mrb_value key, mrb_value *vp)
+{
+ segment *seg;
+ mrb_int i;
+
+ if (t == NULL) return FALSE;
+ seg = t->rootseg;
+ while (seg) {
+ for (i=0; i<seg->size; i++) {
+ mrb_value key2;
+
+ if (!seg->next && i >= t->last_len) {
+ /* not found */
+ return FALSE;
+ }
+ key2 = seg->e[i].key;
+ if (!mrb_undef_p(key2) && ht_hash_equal(mrb, t, key, key2)) {
+ if (vp) *vp = seg->e[i].val;
+ seg->e[i].key = mrb_undef_value();
+ t->size--;
+ return TRUE;
+ }
+ }
+ seg = seg->next;
+ }
+ return FALSE;
+}
+
+/* Iterates over the hash table. */
+static void
+ht_foreach(mrb_state *mrb, htable *t, mrb_hash_foreach_func *func, void *p)
+{
+ segment *seg;
+ mrb_int i;
+
+ if (t == NULL) return;
+ seg = t->rootseg;
+ while (seg) {
+ for (i=0; i<seg->size; i++) {
+ /* no value in last segment after last_len */
+ if (!seg->next && i >= t->last_len) {
+ return;
+ }
+ if (mrb_undef_p(seg->e[i].key)) continue;
+ if ((*func)(mrb, seg->e[i].key, seg->e[i].val, p) != 0)
+ return;
+ }
+ seg = seg->next;
+ }
+}
+
+/* Iterates over the hash table. */
+MRB_API void
+mrb_hash_foreach(mrb_state *mrb, struct RHash *hash, mrb_hash_foreach_func *func, void *p)
+{
+ ht_foreach(mrb, hash->ht, func, p);
+}
+
+/* Copy the hash table. */
+static htable*
+ht_copy(mrb_state *mrb, htable *t)
+{
+ segment *seg;
+ htable *t2;
+ mrb_int i;
+
+ seg = t->rootseg;
+ t2 = ht_new(mrb);
+ if (t->size == 0) return t2;
+
+ while (seg) {
+ for (i=0; i<seg->size; i++) {
+ mrb_value key = seg->e[i].key;
+ mrb_value val = seg->e[i].val;
+
+ if ((seg->next == NULL) && (i >= t->last_len)) {
+ return t2;
+ }
+ if (mrb_undef_p(key)) continue; /* skip deleted key */
+ ht_put(mrb, t2, key, val);
+ }
+ seg = seg->next;
+ }
+ return t2;
+}
+
+/* Free memory of the hash table. */
+static void
+ht_free(mrb_state *mrb, htable *t)
+{
+ segment *seg;
+
+ if (!t) return;
+ seg = t->rootseg;
+ while (seg) {
+ segment *p = seg;
+ seg = seg->next;
+ mrb_free(mrb, p);
+ }
+ if (t->index) mrb_free(mrb, t->index);
+ mrb_free(mrb, t);
+}
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)
+ht_key(mrb_state *mrb, mrb_value key)
{
if (mrb_string_p(key) && !MRB_FROZEN_P(mrb_str_ptr(key))) {
key = mrb_str_dup(mrb, key);
@@ -103,59 +583,57 @@ mrb_hash_ht_key(mrb_state *mrb, mrb_value key)
return key;
}
-#define KEY(key) mrb_hash_ht_key(mrb, key)
+#define KEY(key) ht_key(mrb, key)
+
+static int
+hash_mark_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
+{
+ mrb_gc_mark_value(mrb, key);
+ mrb_gc_mark_value(mrb, val);
+ return 0;
+}
void
mrb_gc_mark_hash(mrb_state *mrb, struct RHash *hash)
{
- khiter_t k;
- khash_t(ht) *h = hash->ht;
-
- if (!h) return;
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- mrb_value key = kh_key(h, k);
- mrb_value val = kh_value(h, k).v;
-
- mrb_gc_mark_value(mrb, key);
- mrb_gc_mark_value(mrb, val);
- }
- }
+ ht_foreach(mrb, hash->ht, hash_mark_i, NULL);
}
size_t
mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *hash)
{
if (!hash->ht) return 0;
- return kh_size(hash->ht)*2;
+ return hash->ht->size*2;
}
void
mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash)
{
- if (hash->ht) kh_destroy(ht, mrb, hash->ht);
+ ht_free(mrb, hash->ht);
}
-
MRB_API mrb_value
-mrb_hash_new_capa(mrb_state *mrb, mrb_int capa)
+mrb_hash_new(mrb_state *mrb)
{
struct RHash *h;
h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
- /* khash needs 1/4 empty space so it is not resized immediately */
- if (capa == 0)
- h->ht = 0;
- else
- h->ht = kh_init_size(ht, mrb, (khint_t)(capa*4/3));
+ h->ht = 0;
h->iv = 0;
return mrb_obj_value(h);
}
MRB_API mrb_value
-mrb_hash_new(mrb_state *mrb)
+mrb_hash_new_capa(mrb_state *mrb, mrb_int capa)
{
- return mrb_hash_new_capa(mrb, 0);
+ struct RHash *h;
+
+ h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
+ /* preallocate hash table */
+ h->ht = ht_new(mrb);
+ /* capacity ignored */
+ h->iv = 0;
+ return mrb_obj_value(h);
}
static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash);
@@ -166,7 +644,7 @@ mrb_hash_init_copy(mrb_state *mrb, mrb_value self)
{
mrb_value orig;
struct RHash* copy;
- khash_t(ht) *orig_h;
+ htable *orig_h;
mrb_value ifnone, vret;
mrb_get_args(mrb, "o", &orig);
@@ -177,22 +655,7 @@ mrb_hash_init_copy(mrb_state *mrb, mrb_value self)
orig_h = RHASH_TBL(self);
copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
- copy->ht = kh_init(ht, mrb);
-
- if (orig_h && kh_size(orig_h) > 0) {
- khash_t(ht) *copy_h = copy->ht;
- khiter_t k, copy_k;
-
- for (k = kh_begin(orig_h); k != kh_end(orig_h); k++) {
- if (kh_exist(orig_h, k)) {
- int ai = mrb_gc_arena_save(mrb);
- copy_k = kh_put(ht, mrb, copy_h, KEY(kh_key(orig_h, k)));
- mrb_gc_arena_restore(mrb, ai);
- kh_val(copy_h, copy_k).v = kh_val(orig_h, k).v;
- kh_val(copy_h, copy_k).n = kh_size(copy_h)-1;
- }
- }
- }
+ copy->ht = ht_copy(mrb, orig_h);
if (MRB_RHASH_DEFAULT_P(self)) {
copy->flags |= MRB_HASH_DEFAULT;
@@ -208,17 +671,45 @@ mrb_hash_init_copy(mrb_state *mrb, mrb_value self)
return vret;
}
+static int
+check_kdict_i(mrb_state *mrb, mrb_value key, mrb_value val, void *data)
+{
+ if (!mrb_symbol_p(key)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword argument hash with non symbol keys");
+ }
+ return 0;
+}
+
+void
+mrb_hash_check_kdict(mrb_state *mrb, mrb_value self)
+{
+ htable *t;
+
+ t = RHASH_TBL(self);
+ if (!t || t->size == 0) return;
+ ht_foreach(mrb, t, check_kdict_i, NULL);
+}
+
+MRB_API mrb_value
+mrb_hash_dup(mrb_state *mrb, mrb_value self)
+{
+ struct RHash* copy;
+ htable *orig_h;
+
+ orig_h = RHASH_TBL(self);
+ copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class);
+ copy->ht = orig_h ? ht_copy(mrb, orig_h) : NULL;
+ return mrb_obj_value(copy);
+}
+
MRB_API mrb_value
mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
+ mrb_value val;
mrb_sym mid;
- if (h) {
- k = kh_get(ht, mrb, h, key);
- if (k != kh_end(h))
- return kh_value(h, k).v;
+ if (ht_get(mrb, RHASH_TBL(hash), key, &val)) {
+ return val;
}
mid = mrb_intern_lit(mrb, "default");
@@ -232,15 +723,11 @@ mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key)
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);
- khiter_t k;
+ mrb_value val;
- if (h) {
- k = kh_get(ht, mrb, h, key);
- if (k != kh_end(h))
- return kh_value(h, k).v;
+ if (ht_get(mrb, RHASH_TBL(hash), key, &val)) {
+ return val;
}
-
/* not found */
return def;
}
@@ -248,54 +735,22 @@ mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def)
MRB_API void
mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val)
{
- khash_t(ht) *h;
- khiter_t k;
- int r;
-
mrb_hash_modify(mrb, hash);
- h = RHASH_TBL(hash);
-
- if (!h) h = RHASH_TBL(hash) = kh_init(ht, mrb);
- k = kh_put2(ht, mrb, h, key, &r);
- kh_value(h, k).v = val;
-
- if (r != 0) {
- /* expand */
- int ai = mrb_gc_arena_save(mrb);
- key = kh_key(h, k) = KEY(key);
- mrb_gc_arena_restore(mrb, ai);
- kh_value(h, k).n = kh_size(h)-1;
- }
+ key = KEY(key);
+ ht_put(mrb, RHASH_TBL(hash), key, val);
mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), key);
mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), val);
return;
}
-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");
-}
-
-MRB_API khash_t(ht)*
-mrb_hash_tbl(mrb_state *mrb, mrb_value hash)
-{
- khash_t(ht) *h = RHASH_TBL(hash);
-
- if (!h) {
- return RHASH_TBL(hash) = kh_init(ht, mrb);
- }
- return h;
-}
-
static void
mrb_hash_modify(mrb_state *mrb, mrb_value hash)
{
- if (MRB_FROZEN_P(mrb_hash_ptr(hash))) {
- mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen hash");
+ mrb_check_frozen(mrb, mrb_hash_ptr(hash));
+ if (!RHASH_TBL(hash)) {
+ RHASH_TBL(hash) = ht_new(mrb);
}
- mrb_hash_tbl(mrb, hash);
}
/* 15.2.13.4.16 */
@@ -535,47 +990,17 @@ mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash)
MRB_API mrb_value
mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
- mrb_value delVal;
- mrb_int n;
-
- if (h) {
- k = kh_get(ht, mrb, h, key);
- if (k != kh_end(h)) {
- delVal = kh_value(h, k).v;
- n = kh_value(h, k).n;
- kh_del(ht, mrb, h, k);
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (!kh_exist(h, k)) continue;
- if (kh_value(h, k).n > n) kh_value(h, k).n--;
- }
- return delVal;
- }
+ htable *t = RHASH_TBL(hash);
+ mrb_value del_val;
+
+ if (ht_del(mrb, t, key, &del_val)) {
+ return del_val;
}
/* not found */
return mrb_nil_value();
}
-/* 15.2.13.4.8 */
-/*
- * call-seq:
- * hsh.delete(key) -> value
- * hsh.delete(key) {| key | block } -> value
- *
- * Deletes and returns a key-value pair from <i>hsh</i> whose key is
- * equal to <i>key</i>. If the key is not found, returns the
- * <em>default value</em>. If the optional code block is given and the
- * 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"
- *
- */
static mrb_value
mrb_hash_delete(mrb_state *mrb, mrb_value self)
{
@@ -586,6 +1011,33 @@ mrb_hash_delete(mrb_state *mrb, mrb_value self)
return mrb_hash_delete_key(mrb, self, key);
}
+/* find first element in the hash table, and remove it. */
+static void
+ht_shift(mrb_state *mrb, htable *t, mrb_value *kp, mrb_value *vp)
+{
+ segment *seg = t->rootseg;
+ mrb_int i;
+
+ while (seg) {
+ for (i=0; i<seg->size; i++) {
+ mrb_value key;
+
+ if (!seg->next && i >= t->last_len) {
+ return;
+ }
+ key = seg->e[i].key;
+ if (mrb_undef_p(key)) continue;
+ *kp = key;
+ *vp = seg->e[i].val;
+ /* delete element */
+ seg->e[i].key = mrb_undef_value();
+ t->size--;
+ return;
+ }
+ seg = seg->next;
+ }
+}
+
/* 15.2.13.4.24 */
/*
* call-seq:
@@ -603,22 +1055,16 @@ mrb_hash_delete(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_hash_shift(mrb_state *mrb, mrb_value hash)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
- mrb_value delKey, delVal;
+ htable *t = RHASH_TBL(hash);
mrb_hash_modify(mrb, hash);
- 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);
+ if (t && t->size > 0) {
+ mrb_value del_key, del_val;
- return mrb_assoc_new(mrb, delKey, delVal);
- }
+ ht_shift(mrb, t, &del_key, &del_val);
+ mrb_gc_protect(mrb, del_key);
+ mrb_gc_protect(mrb, del_val);
+ return mrb_assoc_new(mrb, del_key, del_val);
}
if (MRB_RHASH_DEFAULT_P(hash)) {
@@ -647,10 +1093,13 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash)
MRB_API mrb_value
mrb_hash_clear(mrb_state *mrb, mrb_value hash)
{
- khash_t(ht) *h = RHASH_TBL(hash);
+ htable *t = RHASH_TBL(hash);
mrb_hash_modify(mrb, hash);
- if (h) kh_clear(ht, mrb, h);
+ if (t) {
+ ht_free(mrb, t);
+ RHASH_TBL(hash) = NULL;
+ }
return hash;
}
@@ -683,6 +1132,15 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self)
return val;
}
+MRB_API mrb_int
+mrb_hash_size(mrb_state *mrb, mrb_value hash)
+{
+ htable *t = RHASH_TBL(hash);
+
+ if (!t) return 0;
+ return t->size;
+}
+
/* 15.2.13.4.20 */
/* 15.2.13.4.25 */
/*
@@ -700,10 +1158,17 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_hash_size_m(mrb_state *mrb, mrb_value self)
{
- khash_t(ht) *h = RHASH_TBL(self);
+ mrb_int size = mrb_hash_size(mrb, self);
+ return mrb_fixnum_value(size);
+}
+
+MRB_API mrb_bool
+mrb_hash_empty_p(mrb_state *mrb, mrb_value self)
+{
+ htable *t = RHASH_TBL(self);
- if (!h) return mrb_fixnum_value(0);
- return mrb_fixnum_value(kh_size(h));
+ if (!t) return TRUE;
+ return t->size == 0;
}
/* 15.2.13.4.12 */
@@ -716,27 +1181,17 @@ mrb_hash_size_m(mrb_state *mrb, mrb_value self)
* {}.empty? #=> true
*
*/
-MRB_API mrb_value
-mrb_hash_empty_p(mrb_state *mrb, mrb_value self)
+static mrb_value
+mrb_hash_empty_m(mrb_state *mrb, mrb_value self)
{
- khash_t(ht) *h = RHASH_TBL(self);
-
- if (h) return mrb_bool_value(kh_size(h) == 0);
- return mrb_true_value();
+ return mrb_bool_value(mrb_hash_empty_p(mrb, self));
}
-/* 15.2.13.4.29 (x)*/
-/*
- * call-seq:
- * hsh.to_hash => hsh
- *
- * Returns +self+.
- */
-
-static mrb_value
-mrb_hash_to_hash(mrb_state *mrb, mrb_value hash)
+static int
+hash_keys_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
{
- return hash;
+ mrb_ary_push(mrb, *(mrb_value*)p, key);
+ return 0;
}
/* 15.2.13.4.19 */
@@ -755,33 +1210,24 @@ mrb_hash_to_hash(mrb_state *mrb, mrb_value hash)
MRB_API mrb_value
mrb_hash_keys(mrb_state *mrb, mrb_value hash)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
- mrb_int end;
+ htable *t = RHASH_TBL(hash);
+ mrb_int size;
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));
- end = kh_size(h)-1;
- mrb_ary_set(mrb, ary, end, mrb_nil_value());
- p = RARRAY_PTR(ary);
- 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);
-
- if (hv.n <= end) {
- p[hv.n] = kv;
- }
- else {
- p[end] = kv;
- }
- }
- }
+
+ if (!t || (size = t->size) == 0)
+ return mrb_ary_new(mrb);
+ ary = mrb_ary_new_capa(mrb, size);
+ ht_foreach(mrb, t, hash_keys_i, (void*)&ary);
return ary;
}
+static int
+hash_vals_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
+{
+ mrb_ary_push(mrb, *(mrb_value*)p, val);
+ return 0;
+}
+
/* 15.2.13.4.28 */
/*
* call-seq:
@@ -798,19 +1244,14 @@ mrb_hash_keys(mrb_state *mrb, mrb_value hash)
MRB_API mrb_value
mrb_hash_values(mrb_state *mrb, mrb_value hash)
{
- khash_t(ht) *h = RHASH_TBL(hash);
- khiter_t k;
+ htable *t = RHASH_TBL(hash);
+ mrb_int size;
mrb_value ary;
- if (!h) return mrb_ary_new(mrb);
- 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_ary_set(mrb, ary, hv.n, hv.v);
- }
- }
+ if (!t || (size = t->size) == 0)
+ return mrb_ary_new(mrb);
+ ary = mrb_ary_new_capa(mrb, size);
+ ht_foreach(mrb, t, hash_vals_i, (void*)&ary);
return ary;
}
@@ -833,21 +1274,44 @@ mrb_hash_values(mrb_state *mrb, mrb_value hash)
*
*/
+MRB_API mrb_bool
+mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key)
+{
+ htable *t;
+
+ t = RHASH_TBL(hash);
+ if (ht_get(mrb, t, key, NULL)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
static mrb_value
mrb_hash_has_key(mrb_state *mrb, mrb_value hash)
{
mrb_value key;
- khash_t(ht) *h;
- khiter_t k;
+ mrb_bool key_p;
mrb_get_args(mrb, "o", &key);
+ key_p = mrb_hash_key_p(mrb, hash, key);
+ return mrb_bool_value(key_p);
+}
- h = RHASH_TBL(hash);
- if (h) {
- k = kh_get(ht, mrb, h, key);
- return mrb_bool_value(k != kh_end(h));
+struct has_v_arg {
+ mrb_bool found;
+ mrb_value val;
+};
+
+static int
+hash_has_value_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p)
+{
+ struct has_v_arg *arg = (struct has_v_arg*)p;
+
+ if (mrb_equal(mrb, arg->val, val)) {
+ arg->found = TRUE;
+ return 1;
}
- return mrb_false_value();
+ return 0;
}
/* 15.2.13.4.14 */
@@ -869,22 +1333,73 @@ static mrb_value
mrb_hash_has_value(mrb_state *mrb, mrb_value hash)
{
mrb_value val;
- khash_t(ht) *h;
- khiter_t k;
-
+ struct has_v_arg arg;
+
mrb_get_args(mrb, "o", &val);
- h = RHASH_TBL(hash);
+ arg.found = FALSE;
+ arg.val = val;
+ ht_foreach(mrb, RHASH_TBL(hash), hash_has_value_i, &arg);
+ return mrb_bool_value(arg.found);
+}
- if (h) {
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (!kh_exist(h, k)) continue;
+static int
+merge_i(mrb_state *mrb, mrb_value key, mrb_value val, void *data)
+{
+ htable *h1 = (htable*)data;
- if (mrb_equal(mrb, kh_value(h, k).v, val)) {
- return mrb_true_value();
- }
- }
+ ht_put(mrb, h1, key, val);
+ return 0;
+}
+
+MRB_API void
+mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2)
+{
+ htable *h1, *h2;
+
+ mrb_hash_modify(mrb, hash1);
+ hash2 = mrb_ensure_hash_type(mrb, hash2);
+ h1 = RHASH_TBL(hash1);
+ h2 = RHASH_TBL(hash2);
+
+ if (!h2) return;
+ if (!h1) {
+ RHASH_TBL(hash1) = ht_copy(mrb, h2);
+ return;
}
- return mrb_false_value();
+ ht_foreach(mrb, h2, merge_i, h1);
+ mrb_write_barrier(mrb, (struct RBasic*)RHASH(hash1));
+ return;
+}
+
+/*
+ * 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>.
+ *
+ * keys = (1..17).map{|n| [n]}
+ * k = keys[0]
+ * h = {}
+ * keys.each{|key| h[key] = key[0]}
+ * h #=> { [1]=> 1, [2]=> 2, [3]=> 3, [4]=> 4, [5]=> 5, [6]=> 6, [7]=> 7,
+ * [8]=> 8, [9]=> 9,[10]=>10,[11]=>11,[12]=>12,[13]=>13,[14]=>14,
+ * [15]=>15,[16]=>16,[17]=>17}
+ * h[k] #=> 1
+ * k[0] = keys.size + 1
+ * h #=> {[18]=> 1, [2]=> 2, [3]=> 3, [4]=> 4, [5]=> 5, [6]=> 6, [7]=> 7,
+ * [8]=> 8, [9]=> 9,[10]=>10,[11]=>11,[12]=>12,[13]=>13,[14]=>14,
+ * [15]=>15,[16]=>16,[17]=>17}
+ * h[k] #=> nil
+ * h.rehash
+ * h[k] #=> 1
+ */
+static mrb_value
+mrb_hash_rehash(mrb_state *mrb, mrb_value self)
+{
+ ht_compact(mrb, RHASH_TBL(self));
+ return self;
}
void
@@ -904,7 +1419,7 @@ mrb_init_hash(mrb_state *mrb)
mrb_define_method(mrb, h, "default_proc", mrb_hash_default_proc,MRB_ARGS_NONE()); /* 15.2.13.4.7 */
mrb_define_method(mrb, h, "default_proc=", mrb_hash_set_default_proc,MRB_ARGS_REQ(1)); /* 15.2.13.4.7 */
mrb_define_method(mrb, h, "__delete", mrb_hash_delete, MRB_ARGS_REQ(1)); /* core of 15.2.13.4.8 */
- mrb_define_method(mrb, h, "empty?", mrb_hash_empty_p, MRB_ARGS_NONE()); /* 15.2.13.4.12 */
+ mrb_define_method(mrb, h, "empty?", mrb_hash_empty_m, MRB_ARGS_NONE()); /* 15.2.13.4.12 */
mrb_define_method(mrb, h, "has_key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.13 */
mrb_define_method(mrb, h, "has_value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.14 */
mrb_define_method(mrb, h, "include?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.15 */
@@ -918,6 +1433,5 @@ mrb_init_hash(mrb_state *mrb)
mrb_define_method(mrb, h, "store", mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.26 */
mrb_define_method(mrb, h, "value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.27 */
mrb_define_method(mrb, h, "values", mrb_hash_values, MRB_ARGS_NONE()); /* 15.2.13.4.28 */
-
- mrb_define_method(mrb, h, "to_hash", mrb_hash_to_hash, MRB_ARGS_NONE()); /* 15.2.13.4.29 (x)*/
+ mrb_define_method(mrb, h, "rehash", mrb_hash_rehash, MRB_ARGS_NONE());
}
diff --git a/src/kernel.c b/src/kernel.c
index f378004cb..a3c2d2ec6 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -14,24 +14,11 @@
#include <mruby/error.h>
#include <mruby/istruct.h>
-typedef enum {
- NOEX_PUBLIC = 0x00,
- NOEX_NOSUPER = 0x01,
- NOEX_PRIVATE = 0x02,
- NOEX_PROTECTED = 0x04,
- NOEX_MASK = 0x06,
- NOEX_BASIC = 0x08,
- NOEX_UNDEF = NOEX_NOSUPER,
- NOEX_MODFUNC = 0x12,
- NOEX_SUPER = 0x20,
- NOEX_VCALL = 0x40,
- NOEX_RESPONDS = 0x80
-} mrb_method_flag_t;
-
MRB_API mrb_bool
mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func)
{
- mrb_method_t m = mrb_method_search(mrb, mrb_class(mrb, obj), mid);
+ struct RClass *c = mrb_class(mrb, obj);
+ mrb_method_t m = mrb_method_search_vm(mrb, &c, mid);
struct RProc *p;
if (MRB_METHOD_UNDEF_P(m)) return FALSE;
@@ -252,18 +239,18 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src)
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) {
+ if (sc->flags & MRB_FL_CLASS_IS_PREPENDED) {
struct RClass *c0 = sc->super;
struct RClass *c1 = dc;
/* copy prepended iclasses */
- while (!(c0->flags & MRB_FLAG_IS_ORIGIN)) {
+ while (!(c0->flags & MRB_FL_CLASS_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;
+ c1->super->flags |= MRB_FL_CLASS_IS_ORIGIN;
}
if (sc->mt) {
dc->mt = kh_copy(mt, mrb, sc->mt);
@@ -348,7 +335,7 @@ mrb_obj_clone(mrb_state *mrb, mrb_value self)
mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c);
clone = mrb_obj_value(p);
init_copy(mrb, clone, self);
- p->flags = mrb_obj_ptr(self)->flags;
+ p->flags |= mrb_obj_ptr(self)->flags & MRB_FL_OBJ_IS_FROZEN;
return clone;
}
@@ -447,24 +434,12 @@ mrb_obj_extend_m(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_obj_freeze(mrb_state *mrb, mrb_value self)
{
- struct RBasic *b;
-
- switch (mrb_type(self)) {
- case MRB_TT_FALSE:
- case MRB_TT_TRUE:
- case MRB_TT_FIXNUM:
- case MRB_TT_SYMBOL:
-#ifndef MRB_WITHOUT_FLOAT
- case MRB_TT_FLOAT:
-#endif
- return self;
- default:
- break;
- }
-
- b = mrb_basic_ptr(self);
- if (!MRB_FROZEN_P(b)) {
- MRB_SET_FROZEN_FLAG(b);
+ if (!mrb_immediate_p(self)) {
+ struct RBasic *b = mrb_basic_ptr(self);
+ if (!MRB_FROZEN_P(b)) {
+ MRB_SET_FROZEN_FLAG(b);
+ if (b->c->tt == MRB_TT_SCLASS) MRB_SET_FROZEN_FLAG(b->c);
+ }
}
return self;
}
@@ -472,26 +447,7 @@ mrb_obj_freeze(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_obj_frozen(mrb_state *mrb, mrb_value self)
{
- struct RBasic *b;
-
- switch (mrb_type(self)) {
- case MRB_TT_FALSE:
- case MRB_TT_TRUE:
- case MRB_TT_FIXNUM:
- case MRB_TT_SYMBOL:
-#ifndef MRB_WITHOUT_FLOAT
- case MRB_TT_FLOAT:
-#endif
- return mrb_true_value();
- default:
- break;
- }
-
- b = mrb_basic_ptr(self);
- if (!MRB_FROZEN_P(b)) {
- return mrb_false_value();
- }
- return mrb_true_value();
+ return mrb_bool_value(mrb_immediate_p(self) || MRB_FROZEN_P(mrb_basic_ptr(self)));
}
/* 15.3.1.3.15 */
@@ -505,7 +461,7 @@ mrb_obj_frozen(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_API mrb_value
+static mrb_value
mrb_obj_hash(mrb_state *mrb, mrb_value self)
{
return mrb_fixnum_value(mrb_obj_id(self));
@@ -551,96 +507,6 @@ obj_is_instance_of(mrb_state *mrb, mrb_value self)
return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg)));
}
-/* 15.3.1.3.20 */
-/*
- * call-seq:
- * obj.instance_variable_defined?(symbol) -> true or false
- *
- * Returns <code>true</code> if the given instance variable is
- * defined in <i>obj</i>.
- *
- * class Fred
- * def initialize(p1, p2)
- * @a, @b = p1, p2
- * end
- * end
- * fred = Fred.new('cat', 99)
- * fred.instance_variable_defined?(:@a) #=> true
- * fred.instance_variable_defined?("@b") #=> true
- * fred.instance_variable_defined?("@c") #=> false
- */
-static mrb_value
-mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self)
-{
- mrb_sym sym;
-
- 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 */
-/*
- * call-seq:
- * obj.instance_variable_get(symbol) -> obj
- *
- * Returns the value of the given instance variable, or nil if the
- * instance variable is not set. The <code>@</code> part of the
- * variable name should be included for regular instance
- * variables. Throws a <code>NameError</code> exception if the
- * supplied symbol is not valid as an instance variable name.
- *
- * class Fred
- * def initialize(p1, p2)
- * @a, @b = p1, p2
- * end
- * end
- * fred = Fred.new('cat', 99)
- * fred.instance_variable_get(:@a) #=> "cat"
- * fred.instance_variable_get("@b") #=> 99
- */
-static mrb_value
-mrb_obj_ivar_get(mrb_state *mrb, mrb_value self)
-{
- mrb_sym iv_name;
-
- 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 */
-/*
- * call-seq:
- * obj.instance_variable_set(symbol, obj) -> obj
- *
- * Sets the instance variable names by <i>symbol</i> to
- * <i>object</i>, thereby frustrating the efforts of the class's
- * author to attempt to provide proper encapsulation. The variable
- * did not have to exist prior to this call.
- *
- * class Fred
- * def initialize(p1, p2)
- * @a, @b = p1, p2
- * end
- * end
- * fred = Fred.new('cat', 99)
- * fred.instance_variable_set(:@a, 'dog') #=> "dog"
- * fred.instance_variable_set(:@c, 'cat') #=> "cat"
- * fred.inspect #=> "#<Fred:0x401b3da8 @a=\"dog\", @b=99, @c=\"cat\">"
- */
-static mrb_value
-mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
-{
- mrb_sym iv_name;
- mrb_value 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;
-}
-
/* 15.3.1.3.24 */
/* 15.3.1.3.26 */
/*
@@ -681,124 +547,6 @@ mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self)
KHASH_DECLARE(st, mrb_sym, char, FALSE)
KHASH_DEFINE(st, mrb_sym, char, FALSE, kh_int_hash_func, kh_int_hash_equal)
-static void
-method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set)
-{
- khint_t i;
-
- khash_t(mt) *h = klass->mt;
- if (!h || kh_size(h) == 0) return;
- for (i=0;i<kh_end(h);i++) {
- if (kh_exist(h, i)) {
- mrb_method_t m = kh_value(h, i);
- if (MRB_METHOD_UNDEF_P(m)) continue;
- kh_put(st, mrb, set, kh_key(h, i));
- }
- }
-}
-
-mrb_value
-mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* klass, int obj)
-{
- 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 && !prepended) ||
- (klass->tt == MRB_TT_SCLASS)) {
- }
- else {
- if (!recur) break;
- }
- oldklass = klass;
- klass = klass->super;
- }
-
- ary = mrb_ary_new_capa(mrb, kh_size(set));
- 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)));
- }
- }
- kh_destroy(st, mrb, set);
-
- return ary;
-}
-
-static mrb_value
-mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj)
-{
- khint_t i;
- mrb_value ary;
- struct RClass* klass;
- khash_t(st)* set = kh_init(st, mrb);
-
- klass = mrb_class(mrb, obj);
-
- if (klass && (klass->tt == MRB_TT_SCLASS)) {
- method_entry_loop(mrb, klass, set);
- klass = klass->super;
- }
- if (recur) {
- while (klass && ((klass->tt == MRB_TT_SCLASS) || (klass->tt == MRB_TT_ICLASS))) {
- method_entry_loop(mrb, klass, set);
- klass = klass->super;
- }
- }
-
- 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)));
- }
- }
- kh_destroy(st, mrb, set);
-
- return ary;
-}
-
-static mrb_value
-mrb_obj_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj, mrb_method_flag_t flag)
-{
- return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0);
-}
-/* 15.3.1.3.31 */
-/*
- * call-seq:
- * obj.methods -> array
- *
- * Returns a list of the names of methods publicly accessible in
- * <i>obj</i>. This will include all the methods accessible in
- * <i>obj</i>'s ancestors.
- *
- * class Klass
- * def kMethod()
- * end
- * end
- * k = Klass.new
- * k.methods[0..9] #=> [:kMethod, :respond_to?, :nil?, :is_a?,
- * # :class, :instance_variable_set,
- * # :methods, :extend, :__send__, :instance_eval]
- * k.methods.length #=> 42
- */
-static mrb_value
-mrb_obj_methods_m(mrb_state *mrb, mrb_value self)
-{
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_obj_methods(mrb, recur, self, (mrb_method_flag_t)0); /* everything but private */
-}
-
/* 15.3.1.3.32 */
/*
* call_seq:
@@ -813,57 +561,6 @@ mrb_false(mrb_state *mrb, mrb_value self)
return mrb_false_value();
}
-/* 15.3.1.3.36 */
-/*
- * call-seq:
- * obj.private_methods(all=true) -> array
- *
- * Returns the list of private methods accessible to <i>obj</i>. If
- * the <i>all</i> parameter is set to <code>false</code>, only those methods
- * in the receiver will be listed.
- */
-static mrb_value
-mrb_obj_private_methods(mrb_state *mrb, mrb_value self)
-{
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_obj_methods(mrb, recur, self, NOEX_PRIVATE); /* private attribute not define */
-}
-
-/* 15.3.1.3.37 */
-/*
- * call-seq:
- * obj.protected_methods(all=true) -> array
- *
- * Returns the list of protected methods accessible to <i>obj</i>. If
- * the <i>all</i> parameter is set to <code>false</code>, only those methods
- * in the receiver will be listed.
- */
-static mrb_value
-mrb_obj_protected_methods(mrb_state *mrb, mrb_value self)
-{
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_obj_methods(mrb, recur, self, NOEX_PROTECTED); /* protected attribute not define */
-}
-
-/* 15.3.1.3.38 */
-/*
- * call-seq:
- * obj.public_methods(all=true) -> array
- *
- * Returns the list of public methods accessible to <i>obj</i>. If
- * the <i>all</i> parameter is set to <code>false</code>, only those methods
- * in the receiver will be listed.
- */
-static mrb_value
-mrb_obj_public_methods(mrb_state *mrb, mrb_value self)
-{
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_obj_methods(mrb, recur, self, NOEX_PUBLIC); /* public attribute not define */
-}
-
/* 15.3.1.2.12 */
/* 15.3.1.3.40 */
/*
@@ -912,16 +609,6 @@ mrb_f_raise(mrb_state *mrb, mrb_value self)
return mrb_nil_value(); /* not reached */
}
-static mrb_value
-mrb_krn_class_defined(mrb_state *mrb, mrb_value self)
-{
- mrb_value str;
-
- mrb_get_args(mrb, "S", &str);
- return mrb_bool_value(mrb_class_defined(mrb, RSTRING_PTR(str)));
-}
-
-
/* 15.3.1.3.41 */
/*
* call-seq:
@@ -951,7 +638,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
mrb_value val;
mrb_get_args(mrb, "n", &sym);
- mrb_iv_check(mrb, sym);
+ mrb_iv_name_sym_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));
@@ -1018,6 +705,7 @@ basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub)
{
return mrb_respond_to(mrb, obj, id);
}
+
/* 15.3.1.3.43 */
/*
* call-seq:
@@ -1037,45 +725,16 @@ basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub)
static mrb_value
obj_respond_to(mrb_state *mrb, mrb_value self)
{
- mrb_value mid;
mrb_sym id, rtm_id;
- mrb_bool priv = FALSE, respond_to_p = TRUE;
-
- mrb_get_args(mrb, "o|b", &mid, &priv);
-
- if (mrb_symbol_p(mid)) {
- id = mrb_symbol(mid);
- }
- else {
- mrb_value tmp;
- if (mrb_string_p(mid)) {
- tmp = mrb_check_intern_str(mrb, mid);
- }
- else {
- tmp = mrb_check_string_type(mrb, mid);
- if (mrb_nil_p(tmp)) {
- tmp = mrb_inspect(mrb, mid);
- mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp);
- }
- tmp = mrb_check_intern_str(mrb, tmp);
- }
- if (mrb_nil_p(tmp)) {
- respond_to_p = FALSE;
- }
- else {
- id = mrb_symbol(tmp);
- }
- }
-
- if (respond_to_p) {
- respond_to_p = basic_obj_respond_to(mrb, self, id, !priv);
- }
+ mrb_bool priv = FALSE, respond_to_p;
+ mrb_get_args(mrb, "n|b", &id, &priv);
+ respond_to_p = basic_obj_respond_to(mrb, self, id, !priv);
if (!respond_to_p) {
rtm_id = mrb_intern_lit(mrb, "respond_to_missing?");
if (basic_obj_respond_to(mrb, self, rtm_id, !priv)) {
mrb_value args[2], v;
- args[0] = mid;
+ args[0] = mrb_symbol_value(id);
args[1] = mrb_bool_value(priv);
v = mrb_funcall_argv(mrb, self, rtm_id, 2, args);
return mrb_bool_value(mrb_bool(v));
@@ -1084,67 +743,6 @@ obj_respond_to(mrb_state *mrb, mrb_value self)
return mrb_bool_value(respond_to_p);
}
-/* 15.3.1.3.45 */
-/*
- * call-seq:
- * obj.singleton_methods(all=true) -> array
- *
- * Returns an array of the names of singleton methods for <i>obj</i>.
- * If the optional <i>all</i> parameter is true, the list will include
- * methods in modules included in <i>obj</i>.
- * Only public and protected singleton methods are returned.
- *
- * module Other
- * def three() end
- * end
- *
- * class Single
- * def Single.four() end
- * end
- *
- * a = Single.new
- *
- * def a.one()
- * end
- *
- * class << a
- * include Other
- * def two()
- * end
- * end
- *
- * Single.singleton_methods #=> [:four]
- * a.singleton_methods(false) #=> [:two, :one]
- * a.singleton_methods #=> [:two, :one, :three]
- */
-static mrb_value
-mrb_obj_singleton_methods_m(mrb_state *mrb, mrb_value self)
-{
- mrb_bool recur = TRUE;
- mrb_get_args(mrb, "|b", &recur);
- return mrb_obj_singleton_methods(mrb, recur, self);
-}
-
-static mrb_value
-mod_define_singleton_method(mrb_state *mrb, mrb_value self)
-{
- struct RProc *p;
- mrb_method_t m;
- mrb_sym mid;
- mrb_value blk = mrb_nil_value();
-
- mrb_get_args(mrb, "n&", &mid, &blk);
- if (mrb_nil_p(blk)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
- }
- p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
- mrb_proc_copy(p, mrb_proc_ptr(blk));
- p->flags |= MRB_PROC_STRICT;
- MRB_METHOD_FROM_PROC(m, p);
- mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, m);
- return mrb_symbol_value(mid);
-}
-
static mrb_value
mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
{
@@ -1162,51 +760,8 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
return mrb_false_value();
}
-/* 15.3.1.2.7 */
-/*
- * call-seq:
- * local_variables -> array
- *
- * Returns the names of local variables in the current scope.
- *
- * [mruby limitation]
- * If variable symbol information was stripped out from
- * compiled binary files using `mruby-strip -l`, this
- * method always returns an empty array.
- */
-static mrb_value
-mrb_local_variables(mrb_state *mrb, mrb_value self)
-{
- struct RProc *proc;
- mrb_irep *irep;
- mrb_value vars;
- size_t i;
-
- proc = mrb->c->ci[-1].proc;
-
- if (MRB_PROC_CFUNC_P(proc)) {
- return mrb_ary_new(mrb);
- }
- vars = mrb_hash_new(mrb);
- while (proc) {
- if (MRB_PROC_CFUNC_P(proc)) break;
- irep = proc->body.irep;
- if (!irep->lv) break;
- for (i = 0; i + 1 < irep->nlocals; ++i) {
- if (irep->lv[i].name) {
- mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value());
- }
- }
- if (!MRB_PROC_ENV_P(proc)) break;
- proc = proc->upper;
- //if (MRB_PROC_SCOPE_P(proc)) break;
- if (!proc->c) break;
- }
-
- return mrb_hash_keys(mrb, vars);
-}
-
mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value);
+
void
mrb_init_kernel(mrb_state *mrb)
{
@@ -1214,13 +769,10 @@ mrb_init_kernel(mrb_state *mrb)
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 */
- mrb_define_class_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.2.7 */
; /* 15.3.1.2.11 */
mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_OPT(2)); /* 15.3.1.2.12 */
- mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE());
mrb_define_method(mrb, krn, "===", mrb_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.2 */
mrb_define_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.6 */
@@ -1228,43 +780,30 @@ mrb_init_kernel(mrb_state *mrb)
mrb_define_method(mrb, krn, "clone", mrb_obj_clone, MRB_ARGS_NONE()); /* 15.3.1.3.8 */
mrb_define_method(mrb, krn, "dup", mrb_obj_dup, MRB_ARGS_NONE()); /* 15.3.1.3.9 */
mrb_define_method(mrb, krn, "eql?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.10 */
- mrb_define_method(mrb, krn, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */
mrb_define_method(mrb, krn, "extend", mrb_obj_extend_m, MRB_ARGS_ANY()); /* 15.3.1.3.13 */
mrb_define_method(mrb, krn, "freeze", mrb_obj_freeze, MRB_ARGS_NONE());
mrb_define_method(mrb, krn, "frozen?", mrb_obj_frozen, MRB_ARGS_NONE());
- mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 */
mrb_define_method(mrb, krn, "hash", mrb_obj_hash, MRB_ARGS_NONE()); /* 15.3.1.3.15 */
mrb_define_method(mrb, krn, "initialize_copy", mrb_obj_init_copy, MRB_ARGS_REQ(1)); /* 15.3.1.3.16 */
mrb_define_method(mrb, krn, "inspect", mrb_obj_inspect, MRB_ARGS_NONE()); /* 15.3.1.3.17 */
mrb_define_method(mrb, krn, "instance_of?", obj_is_instance_of, MRB_ARGS_REQ(1)); /* 15.3.1.3.19 */
- mrb_define_method(mrb, krn, "instance_variable_defined?", mrb_obj_ivar_defined, MRB_ARGS_REQ(1)); /* 15.3.1.3.20 */
- mrb_define_method(mrb, krn, "instance_variable_get", mrb_obj_ivar_get, MRB_ARGS_REQ(1)); /* 15.3.1.3.21 */
- mrb_define_method(mrb, krn, "instance_variable_set", mrb_obj_ivar_set, MRB_ARGS_REQ(2)); /* 15.3.1.3.22 */
- mrb_define_method(mrb, krn, "instance_variables", mrb_obj_instance_variables, MRB_ARGS_NONE()); /* 15.3.1.3.23 */
+
mrb_define_method(mrb, krn, "is_a?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.24 */
mrb_define_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.25 */
mrb_define_method(mrb, krn, "kind_of?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.26 */
- mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 */
#ifdef MRB_DEFAULT_METHOD_MISSING
mrb_define_method(mrb, krn, "method_missing", mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */
#endif
- mrb_define_method(mrb, krn, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */
mrb_define_method(mrb, krn, "nil?", mrb_false, MRB_ARGS_NONE()); /* 15.3.1.3.32 */
mrb_define_method(mrb, krn, "object_id", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.33 */
- mrb_define_method(mrb, krn, "private_methods", mrb_obj_private_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.36 */
- mrb_define_method(mrb, krn, "protected_methods", mrb_obj_protected_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.37 */
- mrb_define_method(mrb, krn, "public_methods", mrb_obj_public_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.38 */
mrb_define_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_ANY()); /* 15.3.1.3.40 */
mrb_define_method(mrb, krn, "remove_instance_variable", mrb_obj_remove_instance_variable,MRB_ARGS_REQ(1)); /* 15.3.1.3.41 */
mrb_define_method(mrb, krn, "respond_to?", obj_respond_to, MRB_ARGS_ANY()); /* 15.3.1.3.43 */
- mrb_define_method(mrb, krn, "send", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.44 */
- mrb_define_method(mrb, krn, "singleton_methods", mrb_obj_singleton_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.45 */
- mrb_define_method(mrb, krn, "define_singleton_method", mod_define_singleton_method, MRB_ARGS_ANY());
mrb_define_method(mrb, krn, "to_s", mrb_any_to_s, MRB_ARGS_NONE()); /* 15.3.1.3.46 */
mrb_define_method(mrb, krn, "__case_eqq", mrb_obj_ceqq, MRB_ARGS_REQ(1)); /* internal */
-
- mrb_define_method(mrb, krn, "class_defined?", mrb_krn_class_defined, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, krn, "__to_int", mrb_to_int, MRB_ARGS_NONE()); /* internal */
+ mrb_define_method(mrb, krn, "__to_str", mrb_to_str, MRB_ARGS_NONE()); /* internal */
mrb_include_module(mrb, mrb->object_class, mrb->kernel_module);
- mrb_alias_method(mrb, mrb->module_class, mrb_intern_lit(mrb, "dup"), mrb_intern_lit(mrb, "clone"));
+ mrb_define_alias(mrb, mrb->module_class, "dup", "clone"); /* XXX */
}
diff --git a/src/load.c b/src/load.c
index ddf3cdfbf..97eafdbb5 100644
--- a/src/load.c
+++ b/src/load.c
@@ -7,6 +7,7 @@
#include <limits.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include <mruby/dump.h>
#include <mruby/irep.h>
#include <mruby/proc.h>
@@ -40,6 +41,23 @@ offset_crc_body(void)
return ((uint8_t *)header.binary_crc - (uint8_t *)&header) + sizeof(header.binary_crc);
}
+#ifndef MRB_WITHOUT_FLOAT
+static double
+str_to_double(mrb_state *mrb, mrb_value str)
+{
+ const char *p = RSTRING_PTR(str);
+ mrb_int len = RSTRING_LEN(str);
+
+ /* `i`, `inf`, `infinity` */
+ if (len > 0 && p[0] == 'i') return INFINITY;
+
+ /* `I`, `-inf`, `-infinity` */
+ if (p[0] == 'I' || (len > 1 && p[0] == '-' && p[1] == 'i')) return -INFINITY;
+
+ return mrb_str_to_dbl(mrb, str, TRUE);
+}
+#endif
+
static mrb_irep*
read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
{
@@ -68,7 +86,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
/* Binary Data Section */
/* ISEQ BLOCK */
- irep->ilen = (size_t)bin_to_uint32(src);
+ irep->ilen = (uint16_t)bin_to_uint32(src);
src += sizeof(uint32_t);
src += skip_padding(src);
@@ -79,27 +97,14 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
if ((flags & FLAG_SRC_MALLOC) == 0 &&
(flags & FLAG_BYTEORDER_NATIVE)) {
irep->iseq = (mrb_code*)src;
- src += sizeof(uint32_t) * irep->ilen;
+ src += sizeof(mrb_code) * 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);
- }
- }
+ size_t data_len = sizeof(mrb_code) * irep->ilen;
+ irep->iseq = (mrb_code *)mrb_malloc(mrb, data_len);
+ memcpy(irep->iseq, src, data_len);
+ src += data_len;
}
}
@@ -138,7 +143,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
#ifndef MRB_WITHOUT_FLOAT
case IREP_TT_FLOAT:
- irep->pool[i] = mrb_float_pool(mrb, mrb_str_to_dbl(mrb, s, FALSE));
+ irep->pool[i] = mrb_float_pool(mrb, str_to_double(mrb, s));
break;
#endif
@@ -157,7 +162,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag
}
/* SYMS BLOCK */
- irep->slen = (size_t)bin_to_uint32(src); /* syms length */
+ irep->slen = (uint16_t)bin_to_uint32(src); /* syms length */
src += sizeof(uint32_t);
if (irep->slen > 0) {
if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) {
@@ -229,74 +234,6 @@ read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
}
static int
-read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *len)
-{
- size_t i, fname_len, niseq;
- char *fname;
- uint16_t *lines;
-
- *len = 0;
- bin += sizeof(uint32_t); /* record size */
- *len += sizeof(uint32_t);
- fname_len = bin_to_uint16(bin);
- bin += sizeof(uint16_t);
- *len += sizeof(uint16_t);
- fname = (char *)mrb_malloc(mrb, fname_len + 1);
- memcpy(fname, bin, fname_len);
- fname[fname_len] = '\0';
- bin += fname_len;
- *len += fname_len;
-
- niseq = (size_t)bin_to_uint32(bin);
- bin += sizeof(uint32_t); /* niseq */
- *len += sizeof(uint32_t);
-
- if (SIZE_ERROR_MUL(niseq, sizeof(uint16_t))) {
- return MRB_DUMP_GENERAL_FAILURE;
- }
- lines = (uint16_t *)mrb_malloc(mrb, niseq * sizeof(uint16_t));
- for (i = 0; i < niseq; i++) {
- lines[i] = bin_to_uint16(bin);
- bin += sizeof(uint16_t); /* niseq */
- *len += sizeof(uint16_t);
- }
-
- irep->filename = fname;
- irep->lines = lines;
- return MRB_DUMP_OK;
-}
-
-static int
-read_lineno_record(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *lenp)
-{
- int result = read_lineno_record_1(mrb, bin, irep, lenp);
- int i;
-
- if (result != MRB_DUMP_OK) return result;
- for (i = 0; i < irep->rlen; i++) {
- size_t len;
-
- result = read_lineno_record(mrb, bin, irep->reps[i], &len);
- if (result != MRB_DUMP_OK) break;
- bin += len;
- *lenp += len;
- }
- return result;
-}
-
-static int
-read_section_lineno(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep)
-{
- size_t len;
-
- len = 0;
- bin += sizeof(struct rite_section_lineno_header);
-
- /* Read Binary Data Section */
- return read_lineno_record(mrb, bin, irep, &len);
-}
-
-static int
read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *record_len, const mrb_sym *filenames, size_t filenames_len)
{
const uint8_t *bin = start;
@@ -320,7 +257,6 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
mrb_irep_debug_info_file *file;
uint16_t filename_idx;
- mrb_int len;
file = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*file));
irep->debug_info->files[f_idx] = file;
@@ -333,8 +269,6 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *
bin += sizeof(uint16_t);
mrb_assert(filename_idx < filenames_len);
file->filename_sym = filenames[filename_idx];
- len = 0;
- file->filename = mrb_sym2name_len(mrb, file->filename_sym, &len);
file->line_entry_count = bin_to_uint32(bin);
bin += sizeof(uint32_t);
@@ -525,10 +459,14 @@ lv_exit:
}
static int
-read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t *flags)
+read_binary_header(const uint8_t *bin, size_t bufsize, size_t *bin_size, uint16_t *crc, uint8_t *flags)
{
const struct rite_binary_header *header = (const struct rite_binary_header *)bin;
+ if (bufsize < sizeof(struct rite_binary_header)) {
+ return MRB_DUMP_READ_FAULT;
+ }
+
if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) == 0) {
if (bigendian_p())
*flags |= FLAG_BYTEORDER_NATIVE;
@@ -545,16 +483,24 @@ read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t
return MRB_DUMP_INVALID_FILE_HEADER;
}
+ if (memcmp(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)) != 0) {
+ return MRB_DUMP_INVALID_FILE_HEADER;
+ }
+
if (crc) {
*crc = bin_to_uint16(header->binary_crc);
}
*bin_size = (size_t)bin_to_uint32(header->binary_size);
+ if (bufsize < *bin_size) {
+ return MRB_DUMP_READ_FAULT;
+ }
+
return MRB_DUMP_OK;
}
static mrb_irep*
-read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
+read_irep(mrb_state *mrb, const uint8_t *bin, size_t bufsize, uint8_t flags)
{
int result;
mrb_irep *irep = NULL;
@@ -567,7 +513,7 @@ read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
return NULL;
}
- result = read_binary_header(bin, &bin_size, &crc, &flags);
+ result = read_binary_header(bin, bufsize, &bin_size, &crc, &flags);
if (result != MRB_DUMP_OK) {
return NULL;
}
@@ -584,13 +530,6 @@ read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
irep = read_section_irep(mrb, bin, flags);
if (!irep) return NULL;
}
- 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_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) {
if (!irep) return NULL; /* corrupted data */
result = read_section_debug(mrb, bin, irep, flags);
@@ -614,13 +553,19 @@ read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
mrb_irep*
mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
{
-#ifdef MRB_USE_ETEXT_EDATA
+#if defined(MRB_USE_ETEXT_EDATA) || defined(MRB_USE_CUSTOM_RO_DATA_P)
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);
+ return read_irep(mrb, bin, (size_t)-1, flags);
+}
+
+MRB_API mrb_irep*
+mrb_read_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize)
+{
+ return read_irep(mrb, (const uint8_t *)buf, bufsize, FLAG_SRC_MALLOC);
}
void mrb_exc_set(mrb_state *mrb, mrb_value exc);
@@ -657,11 +602,23 @@ mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
}
MRB_API mrb_value
+mrb_load_irep_buf_cxt(mrb_state *mrb, const void *buf, size_t bufsize, mrbc_context *c)
+{
+ return load_irep(mrb, mrb_read_irep_buf(mrb, buf, bufsize), c);
+}
+
+MRB_API mrb_value
mrb_load_irep(mrb_state *mrb, const uint8_t *bin)
{
return mrb_load_irep_cxt(mrb, bin, NULL);
}
+MRB_API mrb_value
+mrb_load_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize)
+{
+ return mrb_load_irep_buf_cxt(mrb, buf, bufsize, NULL);
+}
+
#ifndef MRB_DISABLE_STDIO
mrb_irep*
@@ -682,7 +639,7 @@ mrb_read_irep_file(mrb_state *mrb, FILE* fp)
if (fread(buf, header_size, 1, fp) == 0) {
goto irep_exit;
}
- result = read_binary_header(buf, &buf_size, NULL, &flags);
+ result = read_binary_header(buf, (size_t)-1, &buf_size, NULL, &flags);
if (result != MRB_DUMP_OK || buf_size <= header_size) {
goto irep_exit;
}
@@ -691,7 +648,7 @@ mrb_read_irep_file(mrb_state *mrb, FILE* fp)
if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) {
goto irep_exit;
}
- irep = read_irep(mrb, buf, FLAG_SRC_MALLOC);
+ irep = read_irep(mrb, buf, (size_t)-1, FLAG_SRC_MALLOC);
irep_exit:
mrb_free(mrb, buf);
diff --git a/src/mruby_core.rake b/src/mruby_core.rake
index bb3d7b633..73fddb220 100644
--- a/src/mruby_core.rake
+++ b/src/mruby_core.rake
@@ -5,16 +5,15 @@ MRuby.each_target do
objs = Dir.glob("#{current_dir}/*.c").map { |f|
next nil if cxx_exception_enabled? and f =~ /(error|vm).c$/
- next nil if self.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT") and f =~ /fmt_fp.c$/
objfile(f.pathmap("#{current_build_dir}/%n"))
}.compact
if cxx_exception_enabled?
objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" }
end
- self.libmruby << objs
+ self.libmruby_objs << objs
- file libfile("#{build_dir}/lib/libmruby_core") => objs do |t|
+ file libmruby_core_static => objs do |t|
archiver.run t.name, t.prerequisites
end
end
diff --git a/src/numeric.c b/src/numeric.c
index 4e5fc394e..4288df44a 100644
--- a/src/numeric.c
+++ b/src/numeric.c
@@ -10,6 +10,7 @@
#endif
#include <limits.h>
#include <stdlib.h>
+#include <string.h>
#include <mruby.h>
#include <mruby/array.h>
@@ -23,9 +24,9 @@
#define floor(f) floorf(f)
#define ceil(f) ceilf(f)
#define fmod(x,y) fmodf(x,y)
-#define MRB_FLO_TO_STR_FMT "%.8g"
+#define FLO_TO_STR_PREC 8
#else
-#define MRB_FLO_TO_STR_FMT "%.16g"
+#define FLO_TO_STR_PREC 16
#endif
#endif
@@ -43,6 +44,15 @@ mrb_to_flo(mrb_state *mrb, mrb_value val)
}
return mrb_float(val);
}
+
+MRB_API mrb_value
+mrb_int_value(mrb_state *mrb, mrb_float f)
+{
+ if (FIXABLE_FLOAT(f)) {
+ return mrb_fixnum_value((mrb_int)f);
+ }
+ return mrb_float_value(mrb, f);
+}
#endif
/*
@@ -55,7 +65,7 @@ mrb_to_flo(mrb_state *mrb, mrb_value val)
* 2.0**3 #=> 8.0
*/
static mrb_value
-num_pow(mrb_state *mrb, mrb_value x)
+integral_pow(mrb_state *mrb, mrb_value x)
{
mrb_value y;
#ifndef MRB_WITHOUT_FLOAT
@@ -102,6 +112,25 @@ num_pow(mrb_state *mrb, mrb_value x)
#endif
}
+static mrb_value
+integral_idiv(mrb_state *mrb, mrb_value x)
+{
+#ifdef MRB_WITHOUT_FLOAT
+ mrb_value y;
+
+ mrb_get_args(mrb, "o", &y);
+ if (!mrb_fixnum_p(y)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
+ }
+ return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y));
+#else
+ mrb_float y;
+
+ mrb_get_args(mrb, "f", &y);
+ return mrb_int_value(mrb, mrb_to_flo(mrb, x) / y);
+#endif
+}
+
/* 15.2.8.3.4 */
/* 15.2.9.3.4 */
/*
@@ -113,19 +142,6 @@ num_pow(mrb_state *mrb, mrb_value x)
* result.
*/
-mrb_value
-mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
-{
-#ifdef MRB_WITHOUT_FLOAT
- if (!mrb_fixnum_p(y)) {
- mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value");
- }
- return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y));
-#else
- return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y));
-#endif
-}
-
/* 15.2.9.3.19(x) */
/*
* call-seq:
@@ -135,7 +151,7 @@ mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y)
*/
static mrb_value
-num_div(mrb_state *mrb, mrb_value x)
+integral_div(mrb_state *mrb, mrb_value x)
{
#ifdef MRB_WITHOUT_FLOAT
mrb_value y;
@@ -153,6 +169,22 @@ num_div(mrb_state *mrb, mrb_value x)
#endif
}
+static mrb_value
+integral_coerce_step_counter(mrb_state *mrb, mrb_value self)
+{
+ mrb_value num, step;
+
+ mrb_get_args(mrb, "oo", &num, &step);
+
+#ifndef MRB_WITHOUT_FLOAT
+ if (mrb_float_p(self) || mrb_float_p(num) || mrb_float_p(step)) {
+ return mrb_Float(mrb, self);
+ }
+#endif
+
+ return self;
+}
+
#ifndef MRB_WITHOUT_FLOAT
/********************************************************************
*
@@ -177,10 +209,49 @@ num_div(mrb_state *mrb, mrb_value x)
static mrb_value
flo_to_s(mrb_state *mrb, mrb_value flt)
{
- if (isnan(mrb_float(flt))) {
+ mrb_float f = mrb_float(flt);
+
+ if (isinf(f)) {
+ return f < 0 ? mrb_str_new_lit(mrb, "-Infinity")
+ : mrb_str_new_lit(mrb, "Infinity");
+ }
+ else if (isnan(f)) {
return mrb_str_new_lit(mrb, "NaN");
}
- return mrb_float_to_str(mrb, flt, MRB_FLO_TO_STR_FMT);
+ else {
+ char fmt[] = "%." MRB_STRINGIZE(FLO_TO_STR_PREC) "g";
+ mrb_value str = mrb_float_to_str(mrb, flt, fmt);
+ mrb_int len;
+ char *begp, *p, *endp;
+
+ insert_dot_zero:
+ begp = RSTRING_PTR(str);
+ len = RSTRING_LEN(str);
+ for (p = begp, endp = p + len; p < endp; ++p) {
+ if (*p == '.') {
+ return str;
+ }
+ else if (*p == 'e') {
+ ptrdiff_t e_pos = p - begp;
+ mrb_str_cat(mrb, str, ".0", 2);
+ p = RSTRING_PTR(str) + e_pos;
+ memmove(p + 2, p, len - e_pos);
+ memcpy(p, ".0", 2);
+ return str;
+ }
+ }
+
+ if (FLO_TO_STR_PREC + (begp[0] == '-') <= len) {
+ --fmt[sizeof(fmt) - 3]; /* %.16g(%.8g) -> %.15g(%.7g) */
+ str = mrb_float_to_str(mrb, flt, fmt);
+ goto insert_dot_zero;
+ }
+ else {
+ mrb_str_cat(mrb, str, ".0", 2);
+ }
+
+ return str;
+ }
}
/* 15.2.9.3.2 */
@@ -220,29 +291,40 @@ flo_mul(mrb_state *mrb, mrb_value x)
}
static void
-flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *modp)
+flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp)
{
- mrb_float div;
- mrb_float mod;
+ double div, mod;
+ if (isnan(y)) {
+ /* y is NaN so all results are NaN */
+ div = mod = y;
+ goto exit;
+ }
if (y == 0.0) {
- if (x > 0.0) div = INFINITY;
- else if (x < 0.0) div = -INFINITY;
- else div = NAN; /* x == 0.0 */
+ if (x == 0) div = NAN;
+ else if (x > 0.0) div = INFINITY;
+ else div = -INFINITY; /* x < 0.0 */
mod = NAN;
+ goto exit;
+ }
+ if ((x == 0.0) || (isinf(y) && !isinf(x))) {
+ mod = x;
}
else {
mod = fmod(x, y);
- if (isinf(x) && isfinite(y))
- div = x;
- else
- div = (x - mod) / y;
- if (y*mod < 0) {
- mod += y;
- div -= 1.0;
- }
}
-
+ if (isinf(x) && !isinf(y)) {
+ div = x;
+ }
+ else {
+ div = (x - mod) / y;
+ if (modp && divp) div = round(div);
+ }
+ if (y*mod < 0) {
+ mod += y;
+ div -= 1.0;
+ }
+ exit:
if (modp) *modp = mod;
if (divp) *divp = div;
}
@@ -302,7 +384,7 @@ flo_eql(mrb_state *mrb, mrb_value x)
mrb_get_args(mrb, "o", &y);
if (!mrb_float_p(y)) return mrb_false_value();
- return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y));
+ return mrb_bool_value(mrb_float(x) == mrb_float(y));
}
/* 15.2.9.3.7 */
@@ -354,7 +436,7 @@ value_int64(mrb_state *mrb, mrb_value x)
static mrb_value
int64_value(mrb_state *mrb, int64_t v)
{
- if (FIXABLE(v)) {
+ if (TYPED_FIXABLE(v,int64_t)) {
return mrb_fixnum_value((mrb_int)v);
}
return mrb_float_value(mrb, (mrb_float)v);
@@ -417,12 +499,16 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
if (width < 0) {
while (width++) {
val /= 2;
+ if (val < 1.0) {
+ val = 0;
+ break;
+ }
}
#if defined(_ISOC99_SOURCE)
val = trunc(val);
#else
if (val > 0){
- val = floor(val);
+ val = floor(val);
} else {
val = ceil(val);
}
@@ -436,14 +522,11 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width)
val *= 2;
}
}
- if (FIXABLE_FLOAT(val)) {
- return mrb_fixnum_value((mrb_int)val);
- }
- return mrb_float_value(mrb, val);
+ return mrb_int_value(mrb, val);
}
static mrb_value
-flo_lshift(mrb_state *mrb, mrb_value x)
+flo_rshift(mrb_state *mrb, mrb_value x)
{
mrb_int width;
@@ -452,7 +535,7 @@ flo_lshift(mrb_state *mrb, mrb_value x)
}
static mrb_value
-flo_rshift(mrb_state *mrb, mrb_value x)
+flo_lshift(mrb_state *mrb, mrb_value x)
{
mrb_int width;
@@ -545,10 +628,7 @@ flo_floor(mrb_state *mrb, mrb_value num)
mrb_float f = floor(mrb_float(num));
mrb_check_num_exact(mrb, f);
- if (!FIXABLE_FLOAT(f)) {
- return mrb_float_value(mrb, f);
- }
- return mrb_fixnum_value((mrb_int)f);
+ return mrb_int_value(mrb, f);
}
/* 15.2.9.3.8 */
@@ -571,10 +651,7 @@ flo_ceil(mrb_state *mrb, mrb_value num)
mrb_float f = ceil(mrb_float(num));
mrb_check_num_exact(mrb, f);
- if (!FIXABLE_FLOAT(f)) {
- return mrb_float_value(mrb, f);
- }
- return mrb_fixnum_value((mrb_int)f);
+ return mrb_int_value(mrb, f);
}
/* 15.2.9.3.12 */
@@ -655,7 +732,7 @@ flo_round(mrb_state *mrb, mrb_value num)
if (!isfinite(number)) return num;
return mrb_float_value(mrb, number);
}
- return mrb_fixnum_value((mrb_int)number);
+ return mrb_int_value(mrb, number);
}
/* 15.2.9.3.14 */
@@ -663,7 +740,6 @@ flo_round(mrb_state *mrb, mrb_value num)
/*
* call-seq:
* flt.to_i -> integer
- * flt.to_int -> integer
* flt.truncate -> integer
*
* Returns <i>flt</i> truncated to an <code>Integer</code>.
@@ -678,10 +754,7 @@ flo_truncate(mrb_state *mrb, mrb_value num)
if (f < 0.0) f = ceil(f);
mrb_check_num_exact(mrb, f);
- if (!FIXABLE_FLOAT(f)) {
- return mrb_float_value(mrb, f);
- }
- return mrb_fixnum_value((mrb_int)f);
+ return mrb_int_value(mrb, f);
}
static mrb_value
@@ -703,7 +776,6 @@ flo_nan_p(mrb_state *mrb, mrb_value num)
/*
* call-seq:
* int.to_i -> integer
- * int.to_int -> integer
*
* As <i>int</i> is already an <code>Integer</code>, all these
* methods simply return the receiver.
@@ -715,8 +787,8 @@ int_to_i(mrb_state *mrb, mrb_value num)
return num;
}
-mrb_value
-mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
@@ -740,6 +812,21 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
#endif
}
+MRB_API mrb_value
+mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_fixnum_p(x)) {
+ return fixnum_mul(mrb, x, y);
+ }
+#ifndef MRB_WITHOUT_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y));
+ }
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number multiply");
+ return mrb_nil_value(); /* not reached */
+}
+
/* 15.2.8.3.3 */
/*
* call-seq:
@@ -756,7 +843,7 @@ fix_mul(mrb_state *mrb, mrb_value x)
mrb_value y;
mrb_get_args(mrb, "o", &y);
- return mrb_fixnum_mul(mrb, x, y);
+ return fixnum_mul(mrb, x, y);
}
static void
@@ -868,7 +955,7 @@ fix_divmod(mrb_state *mrb, mrb_value x)
mrb_value a, b;
flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod);
- a = mrb_float_value(mrb, div);
+ a = mrb_int_value(mrb, div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
@@ -886,7 +973,7 @@ flo_divmod(mrb_state *mrb, mrb_value x)
mrb_get_args(mrb, "o", &y);
flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod);
- a = mrb_float_value(mrb, div);
+ a = mrb_int_value(mrb, div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
@@ -1141,7 +1228,7 @@ fix_to_f(mrb_state *mrb, mrb_value num)
* (in particular infinite or NaN)
* to numerical classes which don't support them.
*
- * Float::INFINITY.to_r
+ * Float::INFINITY.to_i
*
* <em>raises the exception:</em>
*
@@ -1160,25 +1247,20 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
else {
mrb_float d = mrb_float(x);
- if (isinf(d)) {
- mrb_raise(mrb, E_FLOATDOMAIN_ERROR, d < 0 ? "-Infinity" : "Infinity");
- }
- if (isnan(d)) {
- mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
- }
+ mrb_check_num_exact(mrb, d);
if (FIXABLE_FLOAT(d)) {
z = (mrb_int)d;
}
else {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x);
+ mrb_raisef(mrb, E_RANGE_ERROR, "number (%S) too big for integer", x);
}
}
return mrb_fixnum_value(z);
}
#endif
-mrb_value
-mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
@@ -1202,6 +1284,21 @@ mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
#endif
}
+MRB_API mrb_value
+mrb_num_plus(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_fixnum_p(x)) {
+ return fixnum_plus(mrb, x, y);
+ }
+#ifndef MRB_WITHOUT_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y));
+ }
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number addition");
+ return mrb_nil_value(); /* not reached */
+}
+
/* 15.2.8.3.1 */
/*
* call-seq:
@@ -1217,11 +1314,11 @@ fix_plus(mrb_state *mrb, mrb_value self)
mrb_value other;
mrb_get_args(mrb, "o", &other);
- return mrb_fixnum_plus(mrb, self, other);
+ return fixnum_plus(mrb, self, other);
}
-mrb_value
-mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
@@ -1244,6 +1341,21 @@ mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
#endif
}
+MRB_API mrb_value
+mrb_num_minus(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_fixnum_p(x)) {
+ return fixnum_minus(mrb, x, y);
+ }
+#ifndef MRB_WITHOUT_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y));
+ }
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number subtraction");
+ return mrb_nil_value(); /* not reached */
+}
+
/* 15.2.8.3.2 */
/* 15.2.8.3.16 */
/*
@@ -1260,7 +1372,7 @@ fix_minus(mrb_state *mrb, mrb_value self)
mrb_value other;
mrb_get_args(mrb, "o", &other);
- return mrb_fixnum_minus(mrb, self, other);
+ return fixnum_minus(mrb, self, other);
}
@@ -1370,7 +1482,7 @@ cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2)
* basis for the tests in <code>Comparable</code>.
*/
static mrb_value
-num_cmp(mrb_state *mrb, mrb_value self)
+integral_cmp(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_int n;
@@ -1390,7 +1502,7 @@ cmperr(mrb_state *mrb, mrb_value v1, mrb_value v2)
}
static mrb_value
-num_lt(mrb_state *mrb, mrb_value self)
+integral_lt(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_int n;
@@ -1403,7 +1515,7 @@ num_lt(mrb_state *mrb, mrb_value self)
}
static mrb_value
-num_le(mrb_state *mrb, mrb_value self)
+integral_le(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_int n;
@@ -1416,7 +1528,7 @@ num_le(mrb_state *mrb, mrb_value self)
}
static mrb_value
-num_gt(mrb_state *mrb, mrb_value self)
+integral_gt(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_int n;
@@ -1429,7 +1541,7 @@ num_gt(mrb_state *mrb, mrb_value self)
}
static mrb_value
-num_ge(mrb_state *mrb, mrb_value self)
+integral_ge(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_int n;
@@ -1478,22 +1590,25 @@ flo_plus(mrb_state *mrb, mrb_value x)
void
mrb_init_numeric(mrb_state *mrb)
{
- struct RClass *numeric, *integer, *fixnum;
+ struct RClass *numeric, *integer, *fixnum, *integral;
#ifndef MRB_WITHOUT_FLOAT
struct RClass *fl;
#endif
+ integral = mrb_define_module(mrb, "Integral");
+ mrb_define_method(mrb, integral,"**", integral_pow, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integral,"/", integral_div, MRB_ARGS_REQ(1)); /* 15.2.{8,9}.3.6 */
+ mrb_define_method(mrb, integral,"quo", integral_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
+ mrb_define_method(mrb, integral,"div", integral_idiv, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integral,"<=>", integral_cmp, MRB_ARGS_REQ(1)); /* 15.2.{8,9}.3.1 */
+ mrb_define_method(mrb, integral,"<", integral_lt, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integral,"<=", integral_le, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integral,">", integral_gt, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integral,">=", integral_ge, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, integral,"__coerce_step_counter", integral_coerce_step_counter, MRB_ARGS_REQ(2));
+
/* Numeric Class */
numeric = mrb_define_class(mrb, "Numeric", mrb->object_class); /* 15.2.7 */
-
- mrb_define_method(mrb, numeric, "**", num_pow, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, numeric, "/", num_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.4 */
- mrb_define_method(mrb, numeric, "quo", num_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */
- mrb_define_method(mrb, numeric, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */
- mrb_define_method(mrb, numeric, "<", num_lt, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, numeric, "<=", num_le, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, numeric, ">", num_gt, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, numeric, ">=", num_ge, MRB_ARGS_REQ(1));
mrb_define_method(mrb, numeric, "finite?", num_finite_p, MRB_ARGS_NONE());
mrb_define_method(mrb, numeric, "infinite?",num_infinite_p, MRB_ARGS_NONE());
@@ -1545,8 +1660,8 @@ mrb_init_numeric(mrb_state *mrb)
mrb_define_method(mrb, fl, "&", flo_and, MRB_ARGS_REQ(1));
mrb_define_method(mrb, fl, "|", flo_or, MRB_ARGS_REQ(1));
mrb_define_method(mrb, fl, "^", flo_xor, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, fl, ">>", flo_lshift, MRB_ARGS_REQ(1));
- mrb_define_method(mrb, fl, "<<", flo_rshift, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, ">>", flo_rshift, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, fl, "<<", flo_lshift, MRB_ARGS_REQ(1));
mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */
mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */
mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */
@@ -1569,6 +1684,7 @@ mrb_init_numeric(mrb_state *mrb)
#ifdef NAN
mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN));
#endif
+
+ mrb_include_module(mrb, fl, integral);
#endif
- mrb_define_module(mrb, "Integral");
}
diff --git a/src/object.c b/src/object.c
index 8724c5416..7c1879019 100644
--- a/src/object.c
+++ b/src/object.c
@@ -323,19 +323,6 @@ convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *metho
}
MRB_API mrb_value
-mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method)
-{
- mrb_value v;
-
- if (mrb_fixnum_p(val)) return val;
- v = convert_type(mrb, val, "Integer", method, FALSE);
- if (mrb_nil_p(v) || !mrb_fixnum_p(v)) {
- return mrb_nil_value();
- }
- return v;
-}
-
-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;
@@ -447,8 +434,10 @@ mrb_any_to_s(mrb_state *mrb, mrb_value obj)
mrb_str_cat_lit(mrb, str, "#<");
mrb_str_cat_cstr(mrb, str, cname);
- mrb_str_cat_lit(mrb, str, ":");
- mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj)));
+ if (!mrb_immediate_p(obj)) {
+ mrb_str_cat_lit(mrb, str, ":");
+ mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj)));
+ }
mrb_str_cat_lit(mrb, str, ">");
return str;
@@ -505,25 +494,22 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
return FALSE;
}
-static mrb_value
-mrb_to_integer(mrb_state *mrb, mrb_value val, const char *method)
-{
- mrb_value v;
-
- if (mrb_fixnum_p(val)) return val;
- v = convert_type(mrb, val, "Integer", method, TRUE);
- if (!mrb_obj_is_kind_of(mrb, v, mrb->fixnum_class)) {
- mrb_value type = inspect_type(mrb, val);
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer (%S#%S gives %S)",
- type, type, mrb_str_new_cstr(mrb, method), inspect_type(mrb, v));
- }
- return v;
-}
-
MRB_API mrb_value
mrb_to_int(mrb_state *mrb, mrb_value val)
{
- return mrb_to_integer(mrb, val, "to_int");
+
+ if (!mrb_fixnum_p(val)) {
+ mrb_value type;
+
+#ifndef MRB_WITHOUT_FLOAT
+ if (mrb_float_p(val)) {
+ return mrb_flo_to_fixnum(mrb, val);
+ }
+#endif
+ type = inspect_type(mrb, val);
+ mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer", type);
+ }
+ return val;
}
MRB_API mrb_value
@@ -533,18 +519,12 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
if (mrb_nil_p(val)) {
if (base != 0) goto arg_error;
- mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
+ mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
}
switch (mrb_type(val)) {
#ifndef MRB_WITHOUT_FLOAT
case MRB_TT_FLOAT:
if (base != 0) goto arg_error;
- else {
- mrb_float f = mrb_float(val);
- if (FIXABLE_FLOAT(f)) {
- break;
- }
- }
return mrb_flo_to_fixnum(mrb, val);
#endif
@@ -568,11 +548,8 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
arg_error:
mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value");
}
- tmp = convert_type(mrb, val, "Integer", "to_int", FALSE);
- if (mrb_nil_p(tmp) || !mrb_fixnum_p(tmp)) {
- tmp = mrb_to_integer(mrb, val, "to_i");
- }
- return tmp;
+ /* to raise TypeError */
+ return mrb_to_int(mrb, val);
}
MRB_API mrb_value
@@ -605,6 +582,70 @@ mrb_Float(mrb_state *mrb, mrb_value val)
#endif
MRB_API mrb_value
+mrb_to_str(mrb_state *mrb, mrb_value val)
+{
+ return mrb_ensure_string_type(mrb, val);
+}
+
+/* obsolete: use mrb_ensure_string_type() instead */
+MRB_API mrb_value
+mrb_string_type(mrb_state *mrb, mrb_value str)
+{
+ return mrb_ensure_string_type(mrb, str);
+}
+
+MRB_API mrb_value
+mrb_ensure_string_type(mrb_state *mrb, mrb_value str)
+{
+ if (!mrb_string_p(str)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to String",
+ inspect_type(mrb, str));
+ }
+ return str;
+}
+
+MRB_API mrb_value
+mrb_check_string_type(mrb_state *mrb, mrb_value str)
+{
+ if (!mrb_string_p(str)) return mrb_nil_value();
+ return str;
+}
+
+MRB_API mrb_value
+mrb_ensure_array_type(mrb_state *mrb, mrb_value ary)
+{
+ if (!mrb_array_p(ary)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to Array",
+ inspect_type(mrb, ary));
+ }
+ return ary;
+}
+
+MRB_API mrb_value
+mrb_check_array_type(mrb_state *mrb, mrb_value ary)
+{
+ if (!mrb_array_p(ary)) return mrb_nil_value();
+ return ary;
+}
+
+MRB_API mrb_value
+mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash)
+{
+ if (!mrb_hash_p(hash)) {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to Hash",
+ inspect_type(mrb, hash));
+ }
+ return hash;
+}
+
+MRB_API mrb_value
+mrb_check_hash_type(mrb_state *mrb, mrb_value hash)
+{
+ if (!mrb_hash_p(hash)) return mrb_nil_value();
+ return hash;
+}
+
+MRB_API mrb_value
mrb_inspect(mrb_state *mrb, mrb_value obj)
{
return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0));
diff --git a/src/proc.c b/src/proc.c
index c6e9be433..094fff816 100644
--- a/src/proc.c
+++ b/src/proc.c
@@ -10,7 +10,7 @@
#include <mruby/opcode.h>
static mrb_code call_iseq[] = {
- MKOP_A(OP_CALL, 0),
+ OP_CALL,
};
struct RProc*
@@ -63,12 +63,12 @@ closure_setup(mrb_state *mrb, struct RProc *p)
{
mrb_callinfo *ci = mrb->c->ci;
struct RProc *up = p->upper;
- struct REnv *e;
+ struct REnv *e = NULL;
- if (ci->env) {
+ if (ci && ci->env) {
e = ci->env;
}
- else {
+ else if (up) {
struct RClass *tc = MRB_PROC_TARGET_CLASS(p);
e = env_new(mrb, up->body.irep->nlocals);
@@ -78,9 +78,11 @@ closure_setup(mrb_state *mrb, struct RProc *p)
mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc);
}
}
- p->e.env = e;
- p->flags |= MRB_PROC_ENVSET;
- mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
+ if (e) {
+ p->e.env = e;
+ p->flags |= MRB_PROC_ENVSET;
+ mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
+ }
}
struct RProc*
@@ -211,46 +213,11 @@ mrb_proc_init_copy(mrb_state *mrb, mrb_value self)
return self;
}
-int
-mrb_proc_cfunc_p(struct RProc *p)
-{
- return MRB_PROC_CFUNC_P(p);
-}
-
/* 15.2.17.4.2 */
static mrb_value
-mrb_proc_arity(mrb_state *mrb, mrb_value self)
+proc_arity(mrb_state *mrb, mrb_value self)
{
- struct RProc *p = mrb_proc_ptr(self);
- struct mrb_irep *irep;
- mrb_code *iseq;
- 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);
- }
-
- irep = p->body.irep;
- if (!irep) {
- return mrb_fixnum_value(0);
- }
-
- iseq = irep->iseq;
- /* 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 || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa;
-
- return mrb_fixnum_value(arity);
+ return mrb_fixnum_value(mrb_proc_arity(mrb_proc_ptr(self)));
}
/* 15.3.1.2.6 */
@@ -285,6 +252,40 @@ proc_lambda(mrb_state *mrb, mrb_value self)
return blk;
}
+mrb_int
+mrb_proc_arity(const struct RProc *p)
+{
+ struct mrb_irep *irep;
+ mrb_code *pc;
+ mrb_aspec aspec;
+ int ma, op, ra, pa, arity;
+
+ if (MRB_PROC_CFUNC_P(p)) {
+ /* TODO cfunc aspec not implemented yet */
+ return -1;
+ }
+
+ irep = p->body.irep;
+ if (!irep) {
+ return 0;
+ }
+
+ pc = irep->iseq;
+ /* arity is depend on OP_ENTER */
+ if (*pc != OP_ENTER) {
+ return 0;
+ }
+
+ aspec = PEEK_W(pc+1);
+ ma = MRB_ASPEC_REQ(aspec);
+ op = MRB_ASPEC_OPT(aspec);
+ ra = MRB_ASPEC_REST(aspec);
+ pa = MRB_ASPEC_POST(aspec);
+ arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa;
+
+ return arity;
+}
+
void
mrb_init_proc(mrb_state *mrb)
{
@@ -299,15 +300,15 @@ mrb_init_proc(mrb_state *mrb)
call_irep->ilen = 1;
call_irep->nregs = 2; /* receiver and block */
- mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_ANY());
+ mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_NONE()|MRB_ARGS_BLOCK());
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());
+ mrb_define_method(mrb, mrb->proc_class, "arity", proc_arity, MRB_ARGS_NONE());
p = mrb_proc_new(mrb, call_irep);
MRB_METHOD_FROM_PROC(m, p);
mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "call"), m);
mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "[]"), m);
- mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.2.6 */
- mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.3.27 */
+ mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.2.6 */
+ mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.3.27 */
}
diff --git a/src/range.c b/src/range.c
index 0360b48ae..c9dfb2b3c 100644
--- a/src/range.c
+++ b/src/range.c
@@ -10,19 +10,12 @@
#include <mruby/string.h>
#include <mruby/array.h>
-MRB_API struct RRange*
-mrb_range_ptr(mrb_state *mrb, mrb_value v)
-{
- struct RRange *r = (struct RRange*)mrb_ptr(v);
-
- if (r->edges == NULL) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
- }
- return r;
-}
+#define RANGE_INITIALIZED_MASK 1
+#define RANGE_INITIALIZED(p) ((p)->flags |= RANGE_INITIALIZED_MASK)
+#define RANGE_INITIALIZED_P(p) ((p)->flags & RANGE_INITIALIZED_MASK)
static void
-range_check(mrb_state *mrb, mrb_value a, mrb_value b)
+r_check(mrb_state *mrb, mrb_value a, mrb_value b)
{
mrb_value ans;
enum mrb_vtype ta;
@@ -46,18 +39,83 @@ range_check(mrb_state *mrb, mrb_value a, mrb_value b)
}
}
-MRB_API mrb_value
-mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
+static mrb_bool
+r_le(mrb_state *mrb, mrb_value a, mrb_value b)
{
- struct RRange *r;
+ mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
+ /* output :a < b => -1, a = b => 0, a > b => +1 */
- range_check(mrb, beg, end);
- r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
+ if (mrb_fixnum_p(r)) {
+ mrb_int c = mrb_fixnum(r);
+ if (c == 0 || c == -1) return TRUE;
+ }
+
+ return FALSE;
+}
+
+static mrb_bool
+r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
+ /* output :a < b => -1, a = b => 0, a > b => +1 */
+
+ return mrb_fixnum_p(r) && mrb_fixnum(r) == 1;
+}
+
+static mrb_bool
+r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
+{
+ mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
+ /* output :a < b => -1, a = b => 0, a > b => +1 */
+
+ if (mrb_fixnum_p(r)) {
+ mrb_int c = mrb_fixnum(r);
+ if (c == 0 || c == 1) return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r)
+{
+#ifndef MRB_RANGE_EMBED
r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
- r->edges->beg = beg;
- r->edges->end = end;
- r->excl = excl;
- return mrb_range_value(r);
+#endif
+}
+
+static struct RRange *
+range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+ r_check(mrb, beg, end);
+
+ if (r) {
+ if (RANGE_INITIALIZED_P(r)) {
+ /* Ranges are immutable, so that they should be initialized only once. */
+ mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "'initialize' called twice");
+ }
+ else {
+ range_ptr_alloc_edges(mrb, r);
+ }
+ }
+ else {
+ r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
+ range_ptr_alloc_edges(mrb, r);
+ }
+
+ RANGE_BEG(r) = beg;
+ RANGE_END(r) = end;
+ RANGE_EXCL(r) = excl;
+ RANGE_INITIALIZED(r);
+
+ return r;
+}
+
+static void
+range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+ range_ptr_init(mrb, r, beg, end, excl);
+ mrb_write_barrier(mrb, (struct RBasic*)r);
}
/*
@@ -67,12 +125,10 @@ mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
*
* Returns the first object in <i>rng</i>.
*/
-mrb_value
-mrb_range_beg(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_beg(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(mrb, range);
-
- return r->edges->beg;
+ return mrb_range_beg(mrb, range);
}
/*
@@ -85,13 +141,10 @@ mrb_range_beg(mrb_state *mrb, mrb_value range)
* (1..10).end #=> 10
* (1...10).end #=> 10
*/
-
-mrb_value
-mrb_range_end(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_end(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(mrb, range);
-
- return r->edges->end;
+ return mrb_range_end(mrb, range);
}
/*
@@ -100,27 +153,12 @@ mrb_range_end(mrb_state *mrb, mrb_value range)
*
* Returns <code>true</code> if <i>range</i> excludes its end value.
*/
-mrb_value
-mrb_range_excl(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_excl(mrb_state *mrb, mrb_value range)
{
- struct RRange *r = mrb_range_ptr(mrb, range);
-
- return mrb_bool_value(r->excl);
+ return mrb_bool_value(mrb_range_excl_p(mrb, range));
}
-static void
-range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bool exclude_end)
-{
- struct RRange *r = mrb_range_raw_ptr(range);
-
- range_check(mrb, beg, end);
- r->excl = exclude_end;
- if (!r->edges) {
- r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
- }
- r->edges->beg = beg;
- r->edges->end = end;
-}
/*
* call-seq:
* Range.new(start, end, exclusive=false) => range
@@ -129,25 +167,17 @@ range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bo
* parameter is omitted or is <code>false</code>, the <i>range</i> will include
* the end object; otherwise, it will be excluded.
*/
-
-mrb_value
-mrb_range_initialize(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_initialize(mrb_state *mrb, mrb_value range)
{
mrb_value beg, end;
- mrb_bool exclusive;
- mrb_int n;
+ mrb_bool exclusive = FALSE;
- n = mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
- if (n != 3) {
- exclusive = FALSE;
- }
- /* Ranges are immutable, so that they should be initialized only once. */
- if (mrb_range_raw_ptr(range)->edges) {
- mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice");
- }
- range_init(mrb, range, beg, end, exclusive);
+ mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
+ range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive);
return range;
}
+
/*
* call-seq:
* range == obj => true or false
@@ -160,11 +190,9 @@ mrb_range_initialize(mrb_state *mrb, mrb_value range)
* (0..2) == (0..2) #=> true
* (0..2) == Range.new(0,2) #=> true
* (0..2) == (0...2) #=> false
- *
*/
-
-mrb_value
-mrb_range_eq(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_eq(mrb_state *mrb, mrb_value range)
{
struct RRange *rr;
struct RRange *ro;
@@ -179,60 +207,22 @@ mrb_range_eq(mrb_state *mrb, mrb_value range)
rr = mrb_range_ptr(mrb, range);
ro = mrb_range_ptr(mrb, obj);
- v1 = mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg);
- v2 = mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end);
- if (!mrb_bool(v1) || !mrb_bool(v2) || rr->excl != ro->excl) {
+ v1 = mrb_funcall(mrb, RANGE_BEG(rr), "==", 1, RANGE_BEG(ro));
+ v2 = mrb_funcall(mrb, RANGE_END(rr), "==", 1, RANGE_END(ro));
+ if (!mrb_bool(v1) || !mrb_bool(v2) || RANGE_EXCL(rr) != RANGE_EXCL(ro)) {
return mrb_false_value();
}
return mrb_true_value();
}
-static mrb_bool
-r_le(mrb_state *mrb, mrb_value a, mrb_value b)
-{
- mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
- /* output :a < b => -1, a = b => 0, a > b => +1 */
-
- if (mrb_fixnum_p(r)) {
- mrb_int c = mrb_fixnum(r);
- if (c == 0 || c == -1) return TRUE;
- }
-
- return FALSE;
-}
-
-static mrb_bool
-r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
-{
- mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
- /* output :a < b => -1, a = b => 0, a > b => +1 */
-
- return mrb_fixnum_p(r) && mrb_fixnum(r) == 1;
-}
-
-static mrb_bool
-r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
-{
- mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
- /* output :a < b => -1, a = b => 0, a > b => +1 */
-
- if (mrb_fixnum_p(r)) {
- mrb_int c = mrb_fixnum(r);
- if (c == 0 || c == 1) return TRUE;
- }
-
- return FALSE;
-}
-
/*
* call-seq:
* range === obj => true or false
* range.member?(val) => true or false
* range.include?(val) => true or false
- *
*/
-mrb_value
-mrb_range_include(mrb_state *mrb, mrb_value range)
+static mrb_value
+range_include(mrb_state *mrb, mrb_value range)
{
mrb_value val;
struct RRange *r = mrb_range_ptr(mrb, range);
@@ -241,48 +231,15 @@ mrb_range_include(mrb_state *mrb, mrb_value range)
mrb_get_args(mrb, "o", &val);
- beg = r->edges->beg;
- end = r->edges->end;
- include_p = r_le(mrb, beg, val) && /* beg <= val */
- (r->excl ? r_gt(mrb, end, val) /* end > val */
- : r_ge(mrb, end, val)); /* end >= val */
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
+ include_p = r_le(mrb, beg, val) && /* beg <= val */
+ (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */
+ : r_ge(mrb, end, val)); /* end >= val */
return mrb_bool_value(include_p);
}
-MRB_API mrb_int
-mrb_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;
- struct RRange *r;
-
- if (mrb_type(range) != MRB_TT_RANGE) return 0;
- r = mrb_range_ptr(mrb, range);
-
- beg = mrb_int(mrb, r->edges->beg);
- end = mrb_int(mrb, r->edges->end);
-
- if (beg < 0) {
- beg += len;
- if (beg < 0) return 2;
- }
-
- if (trunc) {
- if (beg > len) return 2;
- if (end > len) end = len;
- }
-
- if (end < 0) end += len;
- if (!r->excl && (!trunc || end < len))
- end++; /* include end point */
- len = end - beg;
- if (len < 0) len = 0;
-
- *begp = beg;
- *lenp = len;
- return 1;
-}
-
/* 15.2.14.4.12(x) */
/*
* call-seq:
@@ -290,17 +247,16 @@ mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp,
*
* Convert this range object to a printable form.
*/
-
static mrb_value
range_to_s(mrb_state *mrb, mrb_value range)
{
mrb_value str, str2;
struct RRange *r = mrb_range_ptr(mrb, range);
- str = mrb_obj_as_string(mrb, r->edges->beg);
- str2 = mrb_obj_as_string(mrb, r->edges->end);
+ str = mrb_obj_as_string(mrb, RANGE_BEG(r));
+ str2 = mrb_obj_as_string(mrb, RANGE_END(r));
str = mrb_str_dup(mrb, str);
- mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
+ mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
mrb_str_cat_str(mrb, str, str2);
return str;
@@ -315,17 +271,16 @@ range_to_s(mrb_state *mrb, mrb_value range)
* <code>inspect</code> to convert the start and end
* objects).
*/
-
static mrb_value
range_inspect(mrb_state *mrb, mrb_value range)
{
mrb_value str, str2;
struct RRange *r = mrb_range_ptr(mrb, range);
- str = mrb_inspect(mrb, r->edges->beg);
- str2 = mrb_inspect(mrb, r->edges->end);
+ str = mrb_inspect(mrb, RANGE_BEG(r));
+ str2 = mrb_inspect(mrb, RANGE_END(r));
str = mrb_str_dup(mrb, str);
- mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2);
+ mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
mrb_str_cat_str(mrb, str, str2);
return str;
@@ -343,9 +298,7 @@ range_inspect(mrb_state *mrb, mrb_value range)
* (0..2).eql?(0..2) #=> true
* (0..2).eql?(Range.new(0,2)) #=> true
* (0..2).eql?(0...2) #=> false
- *
*/
-
static mrb_value
range_eql(mrb_state *mrb, mrb_value range)
{
@@ -355,16 +308,14 @@ range_eql(mrb_state *mrb, mrb_value range)
mrb_get_args(mrb, "o", &obj);
if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
- if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) {
- return mrb_false_value();
- }
+ if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) return mrb_false_value();
if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
r = mrb_range_ptr(mrb, range);
o = mrb_range_ptr(mrb, obj);
- if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) ||
- !mrb_eql(mrb, r->edges->end, o->edges->end) ||
- (r->excl != o->excl)) {
+ if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) ||
+ !mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) ||
+ (RANGE_EXCL(r) != RANGE_EXCL(o))) {
return mrb_false_value();
}
return mrb_true_value();
@@ -385,7 +336,7 @@ range_initialize_copy(mrb_state *mrb, mrb_value copy)
}
r = mrb_range_ptr(mrb, src);
- range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl);
+ range_ptr_replace(mrb, mrb_range_raw_ptr(copy), RANGE_BEG(r), RANGE_END(r), RANGE_EXCL(r));
return copy;
}
@@ -401,7 +352,7 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
if (mrb_fixnum_p(argv[i])) {
mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i])));
}
- else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) {
+ else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == MRB_RANGE_OK) {
mrb_int const end = olen < beg + len ? olen : beg + len;
for (j = beg; j < end; ++j) {
mrb_ary_push(mrb, result, func(mrb, obj, j));
@@ -420,6 +371,66 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
}
void
+mrb_gc_mark_range(mrb_state *mrb, struct RRange *r)
+{
+ if (RANGE_INITIALIZED_P(r)) {
+ mrb_gc_mark_value(mrb, RANGE_BEG(r));
+ mrb_gc_mark_value(mrb, RANGE_END(r));
+ }
+}
+
+MRB_API struct RRange*
+mrb_range_ptr(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_raw_ptr(range);
+
+ /* check for if #initialize_copy was removed [#3320] */
+ if (!RANGE_INITIALIZED_P(r)) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
+ }
+ return r;
+}
+
+MRB_API mrb_value
+mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
+{
+ struct RRange *r = range_ptr_init(mrb, NULL, beg, end, excl);
+ return mrb_range_value(r);
+}
+
+MRB_API enum mrb_range_beg_len
+mrb_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;
+ struct RRange *r;
+
+ if (mrb_type(range) != MRB_TT_RANGE) return MRB_RANGE_TYPE_MISMATCH;
+ r = mrb_range_ptr(mrb, range);
+
+ beg = mrb_int(mrb, RANGE_BEG(r));
+ end = mrb_int(mrb, RANGE_END(r));
+
+ if (beg < 0) {
+ beg += len;
+ if (beg < 0) return MRB_RANGE_OUT;
+ }
+
+ if (trunc) {
+ if (beg > len) return MRB_RANGE_OUT;
+ if (end > len) end = len;
+ }
+
+ if (end < 0) end += len;
+ if (!RANGE_EXCL(r) && (!trunc || end < len)) end++; /* include end point */
+ len = end - beg;
+ if (len < 0) len = 0;
+
+ *begp = beg;
+ *lenp = len;
+ return MRB_RANGE_OK;
+}
+
+void
mrb_init_range(mrb_state *mrb)
{
struct RClass *r;
@@ -428,17 +439,16 @@ mrb_init_range(mrb_state *mrb)
mrb->range_class = r;
MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE);
- mrb_define_method(mrb, r, "begin", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */
- mrb_define_method(mrb, r, "end", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */
- mrb_define_method(mrb, r, "==", mrb_range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */
- mrb_define_method(mrb, r, "===", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */
- mrb_define_method(mrb, r, "exclude_end?", mrb_range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */
- mrb_define_method(mrb, r, "first", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */
- mrb_define_method(mrb, r, "include?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */
- mrb_define_method(mrb, r, "initialize", mrb_range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */
- mrb_define_method(mrb, r, "last", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */
- mrb_define_method(mrb, r, "member?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
-
+ mrb_define_method(mrb, r, "begin", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */
+ mrb_define_method(mrb, r, "end", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */
+ mrb_define_method(mrb, r, "==", range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */
+ mrb_define_method(mrb, r, "===", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */
+ mrb_define_method(mrb, r, "exclude_end?", range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */
+ mrb_define_method(mrb, r, "first", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */
+ mrb_define_method(mrb, r, "include?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */
+ mrb_define_method(mrb, r, "initialize", range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */
+ mrb_define_method(mrb, r, "last", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */
+ mrb_define_method(mrb, r, "member?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
mrb_define_method(mrb, r, "to_s", range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */
mrb_define_method(mrb, r, "inspect", range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */
mrb_define_method(mrb, r, "eql?", range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */
diff --git a/src/state.c b/src/state.c
index 18d104555..c42df71ba 100644
--- a/src/state.c
+++ b/src/state.c
@@ -26,6 +26,7 @@ mrb_open_core(mrb_allocf f, void *ud)
static const struct mrb_context mrb_context_zero = { 0 };
mrb_state *mrb;
+ if (f == NULL) f = mrb_default_allocf;
mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud);
if (mrb == NULL) return NULL;
@@ -56,38 +57,6 @@ mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud)
}
}
-struct alloca_header {
- struct alloca_header *next;
- char buf[1];
-};
-
-MRB_API void*
-mrb_alloca(mrb_state *mrb, size_t size)
-{
- struct alloca_header *p;
-
- p = (struct alloca_header*) mrb_malloc(mrb, sizeof(struct alloca_header)+size);
- p->next = mrb->mems;
- mrb->mems = p;
- return (void*)p->buf;
-}
-
-static void
-mrb_alloca_free(mrb_state *mrb)
-{
- struct alloca_header *p;
- struct alloca_header *tmp;
-
- if (mrb == NULL) return;
- p = mrb->mems;
-
- while (p) {
- tmp = p;
- p = p->next;
- mrb_free(mrb, tmp);
- }
-}
-
MRB_API mrb_state*
mrb_open(void)
{
@@ -168,10 +137,6 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
}
mrb_free(mrb, irep->reps);
mrb_free(mrb, irep->lv);
- if (irep->own_filename) {
- mrb_free(mrb, (void *)irep->filename);
- }
- mrb_free(mrb, irep->lines);
mrb_debug_info_free(mrb, irep->debug_info);
mrb_free(mrb, irep);
}
@@ -259,7 +224,6 @@ mrb_close(mrb_state *mrb)
mrb_gc_free_gv(mrb);
mrb_free_context(mrb, mrb->root_c);
mrb_free_symtbl(mrb);
- mrb_alloca_free(mrb);
mrb_gc_destroy(mrb, &mrb->gc);
mrb_free(mrb, mrb);
}
@@ -273,7 +237,6 @@ mrb_add_irep(mrb_state *mrb)
irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
*irep = mrb_irep_zero;
irep->refcnt = 1;
- irep->own_filename = FALSE;
return irep;
}
diff --git a/src/string.c b/src/string.c
index 5cca6d460..6b7cced13 100644
--- a/src/string.c
+++ b/src/string.c
@@ -20,6 +20,7 @@
#include <mruby/class.h>
#include <mruby/range.h>
#include <mruby/string.h>
+#include <mruby/numeric.h>
#include <mruby/re.h>
typedef struct mrb_shared_string {
@@ -82,7 +83,7 @@ str_new(mrb_state *mrb, const char *p, size_t len)
}
static inline void
-str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj)
+str_with_class(struct RString *s, mrb_value obj)
{
s->c = mrb_str_ptr(obj)->c;
}
@@ -92,7 +93,7 @@ mrb_str_new_empty(mrb_state *mrb, mrb_value str)
{
struct RString *s = str_new(mrb, 0, 0);
- str_with_class(mrb, s, str);
+ str_with_class(s, str);
return mrb_obj_value(s);
}
@@ -156,13 +157,6 @@ mrb_str_new(mrb_state *mrb, const char *p, size_t len)
return mrb_obj_value(str_new(mrb, p, len));
}
-/*
- * call-seq: (Caution! NULL string)
- * String.new(str="") => new_str
- *
- * Returns a new string object containing a copy of <i>str</i>.
- */
-
MRB_API mrb_value
mrb_str_new_cstr(mrb_state *mrb, const char *p)
{
@@ -200,6 +194,15 @@ str_decref(mrb_state *mrb, mrb_shared_string *shared)
}
}
+static void
+check_null_byte(mrb_state *mrb, mrb_value str)
+{
+ mrb_to_str(mrb, str);
+ if (memchr(RSTRING_PTR(str), '\0', RSTRING_LEN(str))) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte");
+ }
+}
+
void
mrb_gc_free_str(mrb_state *mrb, struct RString *str)
{
@@ -230,35 +233,47 @@ utf8len(const char* p, const char* e)
mrb_int len;
mrb_int i;
+ if ((unsigned char)*p < 0x80) return 1;
len = utf8len_codepage[(unsigned char)*p];
- if (p + len > e) return 1;
+ if (len == 1) return 1;
+ if (len > e - p) 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
+mrb_utf8_len(const char *str, mrb_int byte_len)
{
mrb_int total = 0;
- char* p = RSTRING_PTR(str);
- char* e = p;
- if (RSTRING(str)->flags & MRB_STR_NO_UTF) {
- return RSTRING_LEN(str);
- }
- e += len < 0 ? RSTRING_LEN(str) : len;
- while (p<e) {
+ const char *p = str;
+ const char *e = p + byte_len;
+
+ while (p < e) {
p += utf8len(p, e);
total++;
}
- if (RSTRING_LEN(str) == total) {
- RSTRING(str)->flags |= MRB_STR_NO_UTF;
- }
return total;
}
-#define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1)
+static mrb_int
+utf8_strlen(mrb_value str)
+{
+ struct RString *s = mrb_str_ptr(str);
+ mrb_int byte_len = RSTR_LEN(s);
+
+ if (RSTR_ASCII_P(s)) {
+ return byte_len;
+ }
+ else {
+ mrb_int utf8_len = mrb_utf8_len(RSTR_PTR(s), byte_len);
+ if (byte_len == utf8_len) RSTR_SET_ASCII_FLAG(s);
+ return utf8_len;
+ }
+}
+
+#define RSTRING_CHAR_LEN(s) utf8_strlen(s)
/* map character index to byte offset index */
static mrb_int
@@ -291,12 +306,71 @@ bytes2chars(char *p, mrb_int bi)
return i;
}
+static mrb_int
+str_index_str_by_char_search(mrb_state *mrb, const char *p, const char *pend, const char *s, const mrb_int slen, mrb_int off)
+{
+ /* Based on Quick Search algorithm (Boyer-Moore-Horspool algorithm) */
+
+ ptrdiff_t qstable[1 << CHAR_BIT];
+
+ /* Preprocessing */
+ {
+ mrb_int i;
+
+ for (i = 0; i < 1 << CHAR_BIT; i ++) {
+ qstable[i] = slen;
+ }
+ for (i = 0; i < slen; i ++) {
+ qstable[(unsigned char)s[i]] = slen - (i + 1);
+ }
+ }
+
+ /* Searching */
+ while (p < pend && pend - p >= slen) {
+ const char *pivot;
+
+ if (memcmp(p, s, slen) == 0) {
+ return off;
+ }
+
+ pivot = p + qstable[(unsigned char)p[slen - 1]];
+ if (pivot > pend || pivot < p /* overflowed */) { return -1; }
+
+ do {
+ p += utf8len(p, pend);
+ off ++;
+ } while (p < pivot);
+ }
+
+ return -1;
+}
+
+static mrb_int
+str_index_str_by_char(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos)
+{
+ const char *p = RSTRING_PTR(str);
+ const char *pend = p + RSTRING_LEN(str);
+ const char *s = RSTRING_PTR(sub);
+ const mrb_int slen = RSTRING_LEN(sub);
+ mrb_int off = pos;
+
+ for (; pos > 0; pos --) {
+ if (pend - p < 1) { return -1; }
+ p += utf8len(p, pend);
+ }
+
+ if (slen < 1) { return off; }
+
+ return str_index_str_by_char_search(mrb, p, pend, s, slen, off);
+}
+
#define BYTES_ALIGN_CHECK(pos) if (pos < 0) return mrb_nil_value();
#else
#define RSTRING_CHAR_LEN(s) RSTRING_LEN(s)
#define chars2bytes(p, off, ci) (ci)
#define bytes2chars(p, bi) (bi)
#define BYTES_ALIGN_CHECK(pos)
+#define str_index_str_by_char(mrb, str, sub, pos) str_index_str(mrb, str, sub, pos)
#endif
static inline mrb_int
@@ -398,8 +472,8 @@ str_make_shared(mrb_state *mrb, struct RString *orig, struct RString *s)
}
}
-static mrb_value
-byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+mrb_value
+mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
{
struct RString *orig, *s;
@@ -413,44 +487,48 @@ byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
s->as.heap.ptr += beg;
s->as.heap.len = len;
}
+ RSTR_COPY_ASCII_FLAG(s, orig);
return mrb_obj_value(s);
}
+
+static void
+str_range_to_bytes(mrb_value str, mrb_int *pos, mrb_int *len)
+{
+ *pos = chars2bytes(str, 0, *pos);
+ *len = chars2bytes(str, *pos, *len);
+}
#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);
+ str_range_to_bytes(str, &beg, &len);
+ return mrb_str_byte_subseq(mrb, str, beg, len);
}
#else
-#define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len)
+#define str_subseq(mrb, str, beg, len) mrb_str_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_bool
+mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp)
{
- 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 (str_len < *begp || *lenp < 0) return FALSE;
+ if (*begp < 0) {
+ *begp += str_len;
+ if (*begp < 0) return FALSE;
}
- if (len > clen - beg)
- len = clen - beg;
- if (len <= 0) {
- len = 0;
+ if (*lenp > str_len - *begp)
+ *lenp = str_len - *begp;
+ if (*lenp <= 0) {
+ *lenp = 0;
}
- return str_subseq(mrb, str, beg, len);
+ return TRUE;
+}
+
+static mrb_value
+str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len)
+{
+ return mrb_str_beg_len(RSTRING_CHAR_LEN(str), &beg, &len) ?
+ str_subseq(mrb, str, beg, len) : mrb_nil_value();
}
MRB_API mrb_int
@@ -490,23 +568,14 @@ str_index_str(mrb_state *mrb, mrb_value str, mrb_value str2, mrb_int offset)
return mrb_str_index(mrb, str, ptr, len, offset);
}
-static void
-check_frozen(mrb_state *mrb, struct RString *s)
-{
- if (MRB_FROZEN_P(s)) {
- mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen string");
- }
-}
-
static mrb_value
str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
{
mrb_int len;
- check_frozen(mrb, s1);
+ mrb_check_frozen(mrb, s1);
if (s1 == s2) return mrb_obj_value(s1);
- s1->flags &= ~MRB_STR_NO_UTF;
- s1->flags |= s2->flags&MRB_STR_NO_UTF;
+ RSTR_COPY_ASCII_FLAG(s1, s2);
len = RSTR_LEN(s2);
if (RSTR_SHARED_P(s1)) {
str_decref(mrb, s1->as.heap.aux.shared);
@@ -641,10 +710,9 @@ mrb_locale_from_utf8(const char *utf8, int len)
#endif
MRB_API void
-mrb_str_modify(mrb_state *mrb, struct RString *s)
+mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s)
{
- check_frozen(mrb, s);
- s->flags &= ~MRB_STR_NO_UTF;
+ mrb_check_frozen(mrb, s);
if (RSTR_SHARED_P(s)) {
mrb_shared_string *shared = s->as.heap.aux.shared;
@@ -708,6 +776,9 @@ mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
mrb_int slen;
struct RString *s = mrb_str_ptr(str);
+ if (len < 0) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "negative (or overflowed) string size");
+ }
mrb_str_modify(mrb, s);
slen = RSTR_LEN(s);
if (len != slen) {
@@ -725,38 +796,18 @@ 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");
- }
-
+ check_null_byte(mrb, str0);
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);
}
-/*
- * call-seq: (Caution! String("abcd") change)
- * String("abcdefg") = String("abcd") + String("efg")
- *
- * Returns a new string object containing a copy of <i>str</i>.
- */
MRB_API void
mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other)
{
- if (!mrb_string_p(other)) {
- other = mrb_str_to_str(mrb, other);
- }
+ other = mrb_str_to_str(mrb, other);
mrb_str_cat_str(mrb, self, other);
}
-/*
- * call-seq: (Caution! String("abcd") remain)
- * String("abcdefg") = String("abcd") + String("efg")
- *
- * Returns a new string object containing a copy of <i>str</i>.
- */
MRB_API mrb_value
mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b)
{
@@ -774,10 +825,13 @@ mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b)
/* 15.2.10.5.2 */
/*
- * call-seq: (Caution! String("abcd") remain) for stack_argument
- * String("abcdefg") = String("abcd") + String("efg")
+ * call-seq:
+ * str + other_str -> new_str
*
- * Returns a new string object containing a copy of <i>str</i>.
+ * Concatenation---Returns a new <code>String</code> containing
+ * <i>other_str</i> concatenated to <i>str</i>.
+ *
+ * "Hello from " + self.to_s #=> "Hello from main"
*/
static mrb_value
mrb_str_plus_m(mrb_state *mrb, mrb_value self)
@@ -837,7 +891,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self)
len = RSTRING_LEN(self)*times;
str2 = str_new(mrb, 0, len);
- str_with_class(mrb, str2, self);
+ str_with_class(str2, self);
p = RSTR_PTR(str2);
if (len > 0) {
n = RSTRING_LEN(self);
@@ -849,6 +903,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self)
memcpy(p + n, p, len-n);
}
p[RSTR_LEN(str2)] = '\0';
+ RSTR_COPY_ASCII_FLAG(str2, mrb_str_ptr(self));
return mrb_obj_value(str2);
}
@@ -953,15 +1008,7 @@ str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2)
MRB_API mrb_bool
mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2)
{
- if (mrb_immediate_p(str2)) return FALSE;
- if (!mrb_string_p(str2)) {
- if (mrb_nil_p(str2)) return FALSE;
- if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_str"))) {
- return FALSE;
- }
- str2 = mrb_funcall(mrb, str2, "to_str", 0);
- return mrb_equal(mrb, str2, str1);
- }
+ if (!mrb_string_p(str2)) return FALSE;
return str_eql(mrb, str1, str2);
}
@@ -986,47 +1033,38 @@ mrb_str_equal_m(mrb_state *mrb, mrb_value str1)
return mrb_bool_value(mrb_str_equal(mrb, str1, str2));
}
/* ---------------------------------- */
+mrb_value mrb_mod_to_s(mrb_state *mrb, mrb_value klass);
+
MRB_API mrb_value
mrb_str_to_str(mrb_state *mrb, mrb_value str)
{
- mrb_value s;
-
- if (!mrb_string_p(str)) {
- s = mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
- if (mrb_nil_p(s)) {
- s = mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s");
- }
- return s;
+ switch (mrb_type(str)) {
+ case MRB_TT_STRING:
+ return str;
+ case MRB_TT_SYMBOL:
+ return mrb_sym2str(mrb, mrb_symbol(str));
+ case MRB_TT_FIXNUM:
+ return mrb_fixnum_to_str(mrb, str, 10);
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ return mrb_mod_to_s(mrb, str);
+ default:
+ return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s");
}
- return str;
}
MRB_API const char*
-mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr)
+mrb_string_value_ptr(mrb_state *mrb, mrb_value str)
{
- mrb_value str = mrb_str_to_str(mrb, ptr);
+ str = mrb_str_to_str(mrb, str);
return RSTRING_PTR(str);
}
MRB_API mrb_int
mrb_string_value_len(mrb_state *mrb, mrb_value ptr)
{
- mrb_value str = mrb_str_to_str(mrb, ptr);
- return RSTRING_LEN(str);
-}
-
-void
-mrb_noregexp(mrb_state *mrb, mrb_value self)
-{
- mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented");
-}
-
-void
-mrb_regexp_check(mrb_state *mrb, mrb_value obj)
-{
- if (mrb_regexp_p(mrb, obj)) {
- mrb_noregexp(mrb, obj);
- }
+ mrb_to_str(mrb, ptr);
+ return RSTRING_LEN(ptr);
}
MRB_API mrb_value
@@ -1035,56 +1073,95 @@ mrb_str_dup(mrb_state *mrb, mrb_value str)
struct RString *s = mrb_str_ptr(str);
struct RString *dup = str_new(mrb, 0, 0);
- str_with_class(mrb, dup, str);
+ str_with_class(dup, str);
return str_replace(mrb, dup, s);
}
-static mrb_value
-mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx)
-{
- mrb_int idx;
+enum str_convert_range {
+ /* `beg` and `len` are byte unit in `0 ... str.bytesize` */
+ STR_BYTE_RANGE_CORRECTED = 1,
- mrb_regexp_check(mrb, indx);
- switch (mrb_type(indx)) {
- case MRB_TT_FIXNUM:
- idx = mrb_fixnum(indx);
+ /* `beg` and `len` are char unit in any range */
+ STR_CHAR_RANGE = 2,
-num_index:
- str = str_substr(mrb, str, idx, 1);
- if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
- return str;
+ /* `beg` and `len` are char unit in `0 ... str.size` */
+ STR_CHAR_RANGE_CORRECTED = 3,
- case MRB_TT_STRING:
- if (str_index_str(mrb, str, indx, 0) != -1)
- return mrb_str_dup(mrb, indx);
- return mrb_nil_value();
+ /* `beg` is out of range */
+ STR_OUT_OF_RANGE = -1
+};
- case MRB_TT_RANGE:
- goto range_arg;
+static enum str_convert_range
+str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_int *beg, mrb_int *len)
+{
+ if (!mrb_undef_p(alen)) {
+ *beg = mrb_int(mrb, indx);
+ *len = mrb_int(mrb, alen);
+ return STR_CHAR_RANGE;
+ }
+ else {
+ switch (mrb_type(indx)) {
+ case MRB_TT_FIXNUM:
+ *beg = mrb_fixnum(indx);
+ *len = 1;
+ return STR_CHAR_RANGE;
- default:
- indx = mrb_Integer(mrb, indx);
- if (mrb_nil_p(indx)) {
- range_arg:
- {
- mrb_int beg, len;
-
- len = RSTRING_CHAR_LEN(str);
- switch (mrb_range_beg_len(mrb, indx, &beg, &len, len, TRUE)) {
- case 1:
- return str_subseq(mrb, str, beg, len);
- case 2:
- return mrb_nil_value();
+ case MRB_TT_STRING:
+ *beg = str_index_str(mrb, str, indx, 0);
+ if (*beg < 0) { break; }
+ *len = RSTRING_LEN(indx);
+ return STR_BYTE_RANGE_CORRECTED;
+
+ case MRB_TT_RANGE:
+ goto range_arg;
+
+ default:
+ indx = mrb_to_int(mrb, indx);
+ if (mrb_fixnum_p(indx)) {
+ *beg = mrb_fixnum(indx);
+ *len = 1;
+ return STR_CHAR_RANGE;
+ }
+range_arg:
+ *len = RSTRING_CHAR_LEN(str);
+ switch (mrb_range_beg_len(mrb, indx, beg, len, *len, TRUE)) {
+ case MRB_RANGE_OK:
+ return STR_CHAR_RANGE_CORRECTED;
+ case MRB_RANGE_OUT:
+ return STR_OUT_OF_RANGE;
default:
break;
- }
}
+
mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum");
+ }
+ }
+ return STR_OUT_OF_RANGE;
+}
+
+static mrb_value
+mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen)
+{
+ mrb_int beg, len;
+
+ switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) {
+ case STR_CHAR_RANGE_CORRECTED:
+ return str_subseq(mrb, str, beg, len);
+ case STR_CHAR_RANGE:
+ str = str_substr(mrb, str, beg, len);
+ if (mrb_undef_p(alen) && !mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
+ return str;
+ case STR_BYTE_RANGE_CORRECTED:
+ if (mrb_string_p(indx)) {
+ return mrb_str_dup(mrb, indx);
+ }
+ else {
+ return mrb_str_byte_subseq(mrb, str, beg, len);
}
- idx = mrb_fixnum(indx);
- goto num_index;
+ case STR_OUT_OF_RANGE:
+ default:
+ return mrb_nil_value();
}
- return mrb_nil_value(); /* not reached */
}
/* 15.2.10.5.6 */
@@ -1131,20 +1208,118 @@ static mrb_value
mrb_str_aref_m(mrb_state *mrb, mrb_value str)
{
mrb_value a1, a2;
- mrb_int argc;
- argc = mrb_get_args(mrb, "o|o", &a1, &a2);
- if (argc == 2) {
- mrb_int n1, n2;
+ if (mrb_get_args(mrb, "o|o", &a1, &a2) == 1) {
+ a2 = mrb_undef_value();
+ }
+
+ return mrb_str_aref(mrb, str, a1, a2);
+}
+
+static mrb_noreturn void
+str_out_of_index(mrb_state *mrb, mrb_value index)
+{
+ mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of string", index);
+}
+
+static mrb_value
+str_replace_partial(mrb_state *mrb, mrb_value src, mrb_int pos, mrb_int end, mrb_value rep)
+{
+ const mrb_int shrink_threshold = 256;
+ struct RString *str = mrb_str_ptr(src);
+ mrb_int len = RSTR_LEN(str);
+ mrb_int replen, newlen;
+ char *strp;
+
+ if (end > len) { end = len; }
+
+ if (pos < 0 || pos > len) {
+ str_out_of_index(mrb, mrb_fixnum_value(pos));
+ }
+
+ replen = (mrb_nil_p(rep) ? 0 : RSTRING_LEN(rep));
+ newlen = replen + len - (end - pos);
- mrb_regexp_check(mrb, a1);
- mrb_get_args(mrb, "ii", &n1, &n2);
- return str_substr(mrb, str, n1, n2);
+ if (newlen >= MRB_INT_MAX || newlen < replen /* overflowed */) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "string size too big");
}
- if (argc != 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc));
+
+ mrb_str_modify(mrb, str);
+
+ if (len < newlen) {
+ resize_capa(mrb, str, newlen);
+ }
+
+ strp = RSTR_PTR(str);
+
+ memmove(strp + newlen - (len - end), strp + end, len - end);
+ if (!mrb_nil_p(rep)) {
+ memcpy(strp + pos, RSTRING_PTR(rep), replen);
+ }
+ RSTR_SET_LEN(str, newlen);
+ strp[newlen] = '\0';
+
+ if (len - newlen >= shrink_threshold) {
+ resize_capa(mrb, str, newlen);
+ }
+
+ return src;
+}
+
+static void
+mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_value replace)
+{
+ mrb_int beg, len, charlen;
+
+ replace = mrb_to_str(mrb, replace);
+
+ switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) {
+ case STR_OUT_OF_RANGE:
+ default:
+ mrb_raise(mrb, E_INDEX_ERROR, "string not matched");
+ case STR_CHAR_RANGE:
+ if (len < 0) {
+ mrb_raisef(mrb, E_INDEX_ERROR, "negative length %S", alen);
+ }
+ charlen = RSTRING_CHAR_LEN(str);
+ if (beg < 0) { beg += charlen; }
+ if (beg < 0 || beg > charlen) { str_out_of_index(mrb, indx); }
+ /* fall through */
+ case STR_CHAR_RANGE_CORRECTED:
+ str_range_to_bytes(str, &beg, &len);
+ /* fall through */
+ case STR_BYTE_RANGE_CORRECTED:
+ str_replace_partial(mrb, str, beg, beg + len, replace);
+ }
+}
+
+/*
+ * call-seq:
+ * str[fixnum] = replace
+ * str[fixnum, fixnum] = replace
+ * str[range] = replace
+ * str[regexp] = replace
+ * str[regexp, fixnum] = replace
+ * str[other_str] = replace
+ *
+ * Modify +self+ by replacing the content of +self+.
+ * The portion of the string affected is determined using the same criteria as +String#[]+.
+ */
+static mrb_value
+mrb_str_aset_m(mrb_state *mrb, mrb_value str)
+{
+ mrb_value indx, alen, replace;
+
+ switch (mrb_get_args(mrb, "oo|S!", &indx, &alen, &replace)) {
+ case 2:
+ replace = alen;
+ alen = mrb_undef_value();
+ break;
+ case 3:
+ break;
}
- return mrb_str_aref(mrb, str, a1);
+ mrb_str_aset(mrb, str, indx, alen, replace);
+ return str;
}
/* 15.2.10.5.8 */
@@ -1167,7 +1342,7 @@ mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str)
mrb_bool modify = FALSE;
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
if (RSTR_LEN(s) == 0 || !RSTR_PTR(s)) return mrb_nil_value();
p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s);
if (ISLOWER(*p)) {
@@ -1226,7 +1401,7 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str)
struct RString *s = mrb_str_ptr(str);
argc = mrb_get_args(mrb, "|S", &rs);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
len = RSTR_LEN(s);
if (argc == 0) {
if (len == 0) return mrb_nil_value();
@@ -1325,7 +1500,7 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str)
{
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
if (RSTR_LEN(s) > 0) {
mrb_int len;
#ifdef MRB_UTF8_STRING
@@ -1394,7 +1569,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str)
mrb_bool modify = FALSE;
struct RString *s = mrb_str_ptr(str);
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
p = RSTR_PTR(s);
pend = RSTR_PTR(s) + RSTR_LEN(s);
while (p < pend) {
@@ -1567,16 +1742,13 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str)
else
sub = mrb_nil_value();
}
- mrb_regexp_check(mrb, sub);
- clen = RSTRING_CHAR_LEN(str);
if (pos < 0) {
+ clen = RSTRING_CHAR_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)) {
default: {
@@ -1590,18 +1762,15 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str)
}
/* fall through */
case MRB_TT_STRING:
- pos = str_index_str(mrb, str, sub, pos);
+ pos = str_index_str_by_char(mrb, str, sub, pos);
break;
}
if (pos == -1) return mrb_nil_value();
- pos = bytes2chars(RSTRING_PTR(str), pos);
BYTES_ALIGN_CHECK(pos);
return mrb_fixnum_value(pos);
}
-#define STR_REPLACE_SHARED_MIN 10
-
/* 15.2.10.5.24 */
/* 15.2.10.5.28 */
/*
@@ -1711,16 +1880,16 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
return mrb_obj_value(p_str);
}
-MRB_API mrb_value
-mrb_string_type(mrb_state *mrb, mrb_value str)
+static inline void
+str_reverse(char *p, char *e)
{
- return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str");
-}
+ char c;
-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");
+ while (p < e) {
+ c = *p;
+ *p++ = *e;
+ *e-- = c;
+ }
}
/* 15.2.10.5.30 */
@@ -1733,53 +1902,38 @@ mrb_check_string_type(mrb_state *mrb, mrb_value str)
static mrb_value
mrb_str_reverse_bang(mrb_state *mrb, mrb_value str)
{
+ struct RString *s = mrb_str_ptr(str);
+ char *p, *e;
+
#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;
-
- mrb_str_modify(mrb, mrb_str_ptr(str));
- len = RSTRING_LEN(str);
- buf = (char*)mrb_malloc(mrb, (size_t)len);
- p = buf;
- e = buf + len;
-
- memcpy(buf, RSTRING_PTR(str), len);
- r = RSTRING_PTR(str) + len;
+ mrb_int len = RSTR_LEN(s);
+ if (utf8_len < 2) return str;
+ if (utf8_len < len) {
+ mrb_str_modify(mrb, s);
+ p = RSTR_PTR(s);
+ e = p + RSTR_LEN(s);
while (p<e) {
mrb_int clen = utf8len(p, e);
- r -= clen;
- memcpy(r, p, clen);
+ str_reverse(p, p + clen - 1);
p += clen;
}
- mrb_free(mrb, buf);
+ goto bytes;
}
- return str;
-
- bytes:
#endif
- {
- struct RString *s = mrb_str_ptr(str);
- char *p, *e;
- char c;
+ if (RSTR_LEN(s) > 1) {
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;
+ goto bytes;
}
+ return str;
+
+ bytes:
+ p = RSTR_PTR(s);
+ e = p + RSTR_LEN(s) - 1;
+ str_reverse(p, e);
+ return str;
}
/* ---------------------------------- */
@@ -1833,7 +1987,6 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str)
if (pos < 0) {
pos += len;
if (pos < 0) {
- mrb_regexp_check(mrb, sub);
return mrb_nil_value();
}
}
@@ -1847,7 +2000,6 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str)
sub = mrb_nil_value();
}
pos = chars2bytes(str, 0, pos);
- mrb_regexp_check(mrb, sub);
switch (mrb_type(sub)) {
default: {
@@ -1941,16 +2093,11 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
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 {
- mrb_noregexp(mrb, str);
- }
+ else if (!mrb_string_p(spat)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "expected String");
+ }
+ else if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') {
+ split_type = awk;
}
result = mrb_ary_new(mrb);
@@ -1976,7 +2123,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
}
}
else if (ISSPACE(c)) {
- mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg));
+ mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, beg, end-beg));
mrb_gc_arena_restore(mrb, ai);
skip = TRUE;
beg = idx;
@@ -1987,7 +2134,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
}
}
}
- else if (split_type == string) {
+ else { /* split_type == string */
mrb_int str_len = RSTRING_LEN(str);
mrb_int pat_len = RSTRING_LEN(spat);
mrb_int idx = 0;
@@ -2001,22 +2148,19 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
else {
end = chars2bytes(str, idx, 1);
}
- mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end));
+ mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, idx, end));
mrb_gc_arena_restore(mrb, ai);
idx += end + pat_len;
if (lim_p && lim <= ++i) break;
}
beg = idx;
}
- else {
- 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 = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
+ tmp = mrb_str_byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg);
}
mrb_ary_push(mrb, result, tmp);
}
@@ -2030,7 +2174,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
return result;
}
-MRB_API mrb_value
+static mrb_value
mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base, int badcheck)
{
const char *p = str;
@@ -2174,7 +2318,7 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base,
else
#endif
{
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer",
+ mrb_raisef(mrb, E_RANGE_ERROR, "string (%S) too big for integer",
mrb_str_new(mrb, str, pend-str));
}
}
@@ -2198,7 +2342,7 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base,
}
MRB_API mrb_value
-mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
+mrb_cstr_to_inum(mrb_state *mrb, const char *str, mrb_int base, mrb_bool badcheck)
{
return mrb_str_len_to_inum(mrb, str, strlen(str), base, badcheck);
}
@@ -2206,20 +2350,27 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck)
MRB_API const char*
mrb_string_value_cstr(mrb_state *mrb, mrb_value *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);
+ struct RString *ps;
+ const char *p;
+ mrb_int len;
- if (!p || p[len] != '\0') {
+ check_null_byte(mrb, *ptr);
+ ps = mrb_str_ptr(*ptr);
+ p = RSTR_PTR(ps);
+ len = RSTR_LEN(ps);
+ if (p[len] == '\0') {
+ return p;
+ }
+ else {
if (MRB_FROZEN_P(ps)) {
- *ptr = str = mrb_str_dup(mrb, str);
- ps = mrb_str_ptr(str);
+ ps = str_new(mrb, p, len);
+ *ptr = mrb_obj_value(ps);
+ }
+ else {
+ mrb_str_modify(mrb, ps);
}
- mrb_str_modify(mrb, ps);
return RSTR_PTR(ps);
}
- return p;
}
MRB_API mrb_value
@@ -2333,22 +2484,7 @@ bad:
MRB_API double
mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck)
{
- char *s;
- mrb_int len;
-
- str = mrb_str_to_str(mrb, str);
- s = RSTRING_PTR(str);
- len = RSTRING_LEN(str);
- if (s) {
- if (badcheck && memchr(s, '\0', len)) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte");
- }
- if (s[len]) { /* no sentinel somehow */
- struct RString *temp_str = str_new(mrb, s, len);
- s = RSTR_PTR(temp_str);
- }
- }
- return mrb_cstr_to_dbl(mrb, s, badcheck);
+ return mrb_cstr_to_dbl(mrb, mrb_string_value_cstr(mrb, &str), badcheck);
}
/* 15.2.10.5.39 */
@@ -2376,7 +2512,6 @@ mrb_str_to_f(mrb_state *mrb, mrb_value self)
/*
* call-seq:
* str.to_s => str
- * str.to_str => str
*
* Returns the receiver.
*/
@@ -2404,7 +2539,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str)
char *p, *pend;
mrb_bool modify = FALSE;
- mrb_str_modify(mrb, s);
+ mrb_str_modify_keep_ascii(mrb, s);
p = RSTRING_PTR(str);
pend = RSTRING_END(str);
while (p < pend) {
@@ -2485,7 +2620,7 @@ mrb_str_dump(mrb_state *mrb, mrb_value str)
}
result = str_new(mrb, 0, len);
- str_with_class(mrb, result, str);
+ str_with_class(result, str);
p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str);
q = RSTR_PTR(result);
*q++ = '"';
@@ -2624,7 +2759,7 @@ mrb_str_cat_str(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);
+ mrb_to_str(mrb, str2);
return mrb_str_cat_str(mrb, str1, str2);
}
@@ -2704,6 +2839,7 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
}
}
mrb_str_cat_lit(mrb, result, "\"");
+ RSTR_COPY_ASCII_FLAG(mrb_str_ptr(result), mrb_str_ptr(str));
return result;
}
@@ -2749,6 +2885,7 @@ mrb_init_string(mrb_state *mrb)
mrb_define_method(mrb, s, "+", mrb_str_plus_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.4 */
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, "[]=", mrb_str_aset_m, MRB_ARGS_ANY());
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_NONE()); /* 15.2.10.5.8 */
mrb_define_method(mrb, s, "chomp", mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */
@@ -2869,7 +3006,7 @@ mrb_float_read(const char *string, char **endPtr)
*/
p = string;
- while (isspace(*p)) {
+ while (ISSPACE(*p)) {
p += 1;
}
if (*p == '-') {
@@ -2892,7 +3029,7 @@ mrb_float_read(const char *string, char **endPtr)
for (mantSize = 0; ; mantSize += 1)
{
c = *p;
- if (!isdigit(c)) {
+ if (!ISDIGIT(c)) {
if ((c != '.') || (decPt >= 0)) {
break;
}
@@ -2977,7 +3114,7 @@ mrb_float_read(const char *string, char **endPtr)
}
expSign = FALSE;
}
- while (isdigit(*p)) {
+ while (ISDIGIT(*p)) {
exp = exp * 10 + (*p - '0');
if (exp > 19999) {
exp = 19999;
diff --git a/src/symbol.c b/src/symbol.c
index 5e1f9f561..53f4f6e1d 100644
--- a/src/symbol.c
+++ b/src/symbol.c
@@ -15,54 +15,158 @@
/* ------------------------------------------------------ */
typedef struct symbol_name {
mrb_bool lit : 1;
+ uint8_t prev;
uint16_t len;
const char *name;
} symbol_name;
-static inline khint_t
-sym_hash_func(mrb_state *mrb, mrb_sym s)
+#define SYMBOL_INLINE_BIT 1
+#define SYMBOL_INLINE_LOWER_BIT 2
+#define SYMBOL_INLINE (1 << (SYMBOL_INLINE_BIT - 1))
+#define SYMBOL_INLINE_LOWER (1 << (SYMBOL_INLINE_LOWER_BIT - 1))
+#define SYMBOL_NORMAL_SHIFT SYMBOL_INLINE_BIT
+#define SYMBOL_INLINE_SHIFT SYMBOL_INLINE_LOWER_BIT
+#ifdef MRB_ENABLE_ALL_SYMBOLS
+# define SYMBOL_INLINE_P(sym) FALSE
+# define SYMBOL_INLINE_LOWER_P(sym) FALSE
+# define sym_inline_pack(name, len) 0
+# define sym_inline_unpack(sym, buf, lenp) NULL
+#else
+# define SYMBOL_INLINE_P(sym) ((sym) & SYMBOL_INLINE)
+# define SYMBOL_INLINE_LOWER_P(sym) ((sym) & SYMBOL_INLINE_LOWER)
+#endif
+
+static void
+sym_validate_len(mrb_state *mrb, size_t len)
{
- khint_t h = 0;
- size_t i, len = mrb->symtbl[s].len;
- const char *p = mrb->symtbl[s].name;
+ if (len >= RITE_LV_NULL_MARK) {
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
+ }
+}
+#ifndef MRB_ENABLE_ALL_SYMBOLS
+static const char pack_table[] = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+static mrb_sym
+sym_inline_pack(const char *name, uint16_t len)
+{
+ const int lower_length_max = (MRB_SYMBOL_BITSIZE - 2) / 5;
+ const int mix_length_max = (MRB_SYMBOL_BITSIZE - 2) / 6;
+
+ char c;
+ const char *p;
+ int i;
+ mrb_sym sym = 0;
+ mrb_bool lower = TRUE;
+
+ if (len > lower_length_max) return 0; /* too long */
for (i=0; i<len; i++) {
- h = (h << 5) - h + *p++;
+ uint32_t bits;
+
+ c = name[i];
+ if (c == 0) return 0; /* NUL in name */
+ p = strchr(pack_table, (int)c);
+ if (p == 0) return 0; /* non alnum char */
+ bits = (uint32_t)(p - pack_table)+1;
+ if (bits > 27) lower = FALSE;
+ if (i >= mix_length_max) break;
+ sym |= bits<<(i*6+SYMBOL_INLINE_SHIFT);
}
- return h;
+ if (lower) {
+ sym = 0;
+ for (i=0; i<len; i++) {
+ uint32_t bits;
+
+ c = name[i];
+ p = strchr(pack_table, (int)c);
+ bits = (uint32_t)(p - pack_table)+1;
+ sym |= bits<<(i*5+SYMBOL_INLINE_SHIFT);
+ }
+ return sym | SYMBOL_INLINE | SYMBOL_INLINE_LOWER;
+ }
+ if (len > mix_length_max) return 0;
+ return sym | SYMBOL_INLINE;
}
-#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, 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)
+static const char*
+sym_inline_unpack(mrb_sym sym, char *buf, mrb_int *lenp)
{
- if (len >= RITE_LV_NULL_MARK) {
- mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
+ int bit_per_char = SYMBOL_INLINE_LOWER_P(sym) ? 5 : 6;
+ int i;
+
+ mrb_assert(SYMBOL_INLINE_P(sym));
+
+ for (i=0; i<30/bit_per_char; i++) {
+ uint32_t bits = sym>>(i*bit_per_char+SYMBOL_INLINE_SHIFT) & ((1<<bit_per_char)-1);
+ if (bits == 0) break;
+ buf[i] = pack_table[bits-1];;
}
+ buf[i] = '\0';
+ if (lenp) *lenp = i;
+ return buf;
+}
+#endif
+
+static uint8_t
+symhash(const char *key, size_t len)
+{
+ uint32_t hash, i;
+
+ for(hash = i = 0; i < len; ++i) {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash & 0xff;
+}
+
+static mrb_sym
+find_symbol(mrb_state *mrb, const char *name, uint16_t len, uint8_t hash)
+{
+ mrb_sym i;
+ symbol_name *sname;
+
+ /* inline symbol */
+ i = sym_inline_pack(name, len);
+ if (i > 0) return i;
+
+ i = mrb->symhash[hash];
+ if (i == 0) return 0;
+ do {
+ sname = &mrb->symtbl[i];
+ if (sname->len == len && memcmp(sname->name, name, len) == 0) {
+ return i<<SYMBOL_NORMAL_SHIFT;
+ }
+ if (sname->prev == 0xff) {
+ i -= 0xff;
+ sname = &mrb->symtbl[i];
+ while (mrb->symtbl < sname) {
+ if (sname->len == len && memcmp(sname->name, name, len) == 0) {
+ return (mrb_sym)(sname - mrb->symtbl)<<SYMBOL_NORMAL_SHIFT;
+ }
+ sname--;
+ }
+ return 0;
+ }
+ i -= sname->prev;
+ } while (sname->prev > 0);
+ return 0;
}
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 = mrb->symtbl; /* symtbl[0] for working memory */
- khiter_t k;
mrb_sym sym;
- char *p;
+ symbol_name *sname;
+ uint8_t hash;
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);
- }
+ hash = symhash(name, len);
+ sym = find_symbol(mrb, name, len, hash);
+ if (sym > 0) return sym;
/* registering a new symbol */
sym = ++mrb->symidx;
@@ -78,15 +182,25 @@ sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
sname->lit = TRUE;
}
else {
- p = (char *)mrb_malloc(mrb, len+1);
+ char *p = (char *)mrb_malloc(mrb, len+1);
memcpy(p, name, len);
p[len] = 0;
sname->name = (const char*)p;
sname->lit = FALSE;
}
- kh_put(n2s, mrb, h, sym);
+ if (mrb->symhash[hash]) {
+ mrb_sym i = sym - mrb->symhash[hash];
+ if (i > 0xff)
+ sname->prev = 0xff;
+ else
+ sname->prev = i;
+ }
+ else {
+ sname->prev = 0;
+ }
+ mrb->symhash[hash] = sym;
- return sym;
+ return sym<<SYMBOL_NORMAL_SHIFT;
}
MRB_API mrb_sym
@@ -116,25 +230,18 @@ mrb_intern_str(mrb_state *mrb, mrb_value str)
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 = mrb->symtbl;
- khiter_t k;
+ mrb_sym sym;
sym_validate_len(mrb, len);
- sname->len = (uint16_t)len;
- sname->name = name;
-
- k = kh_get(n2s, mrb, h, 0);
- if (k != kh_end(h)) {
- return mrb_symbol_value(kh_key(h, k));
- }
+ sym = find_symbol(mrb, name, len, symhash(name, len));
+ if (sym > 0) return mrb_symbol_value(sym);
return mrb_nil_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));
+ return mrb_check_intern(mrb, name, strlen(name));
}
MRB_API mrb_value
@@ -143,10 +250,12 @@ 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 */
-MRB_API const char*
-mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
+static const char*
+sym2name_len(mrb_state *mrb, mrb_sym sym, char *buf, mrb_int *lenp)
{
+ if (SYMBOL_INLINE_P(sym)) return sym_inline_unpack(sym, buf, lenp);
+
+ sym >>= SYMBOL_NORMAL_SHIFT;
if (sym == 0 || mrb->symidx < sym) {
if (lenp) *lenp = 0;
return NULL;
@@ -156,6 +265,12 @@ mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
return mrb->symtbl[sym].name;
}
+MRB_API const char*
+mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
+{
+ return sym2name_len(mrb, sym, mrb->symbuf, lenp);
+}
+
void
mrb_free_symtbl(mrb_state *mrb)
{
@@ -167,13 +282,11 @@ mrb_free_symtbl(mrb_state *mrb)
}
}
mrb_free(mrb, mrb->symtbl);
- kh_destroy(n2s, mrb, mrb->name2sym);
}
void
mrb_init_symtbl(mrb_state *mrb)
{
- mrb->name2sym = kh_init(n2s, mrb);
}
/**********************************************************************
@@ -209,26 +322,6 @@ mrb_init_symtbl(mrb_state *mrb)
*
*/
-
-/* 15.2.11.3.1 */
-/*
- * call-seq:
- * sym == obj -> true or false
- *
- * Equality---If <i>sym</i> and <i>obj</i> are exactly the same
- * symbol, returns <code>true</code>.
- */
-
-static mrb_value
-sym_equal(mrb_state *mrb, mrb_value sym1)
-{
- mrb_value sym2;
-
- mrb_get_args(mrb, "o", &sym2);
-
- return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2));
-}
-
/* 15.2.11.3.2 */
/* 15.2.11.3.3 */
/*
@@ -241,14 +334,9 @@ sym_equal(mrb_state *mrb, mrb_value sym1)
* :fred.id2name #=> "fred"
*/
static mrb_value
-mrb_sym_to_s(mrb_state *mrb, mrb_value sym)
+sym_to_s(mrb_state *mrb, mrb_value sym)
{
- mrb_sym id = mrb_symbol(sym);
- const char *p;
- mrb_int len;
-
- p = mrb_sym2name_len(mrb, id, &len);
- return mrb_str_new_static(mrb, p, len);
+ return mrb_sym2str(mrb, mrb_symbol(sym));
}
/* 15.2.11.3.4 */
@@ -387,8 +475,8 @@ id:
switch (*m) {
case '!': case '?': case '=': ++m;
default: break;
- }
}
+ }
break;
}
return *m ? FALSE : TRUE;
@@ -406,7 +494,7 @@ sym_inspect(mrb_state *mrb, mrb_value sym)
name = mrb_sym2name_len(mrb, id, &len);
str = mrb_str_new(mrb, 0, len+1);
sp = RSTRING_PTR(str);
- RSTRING_PTR(str)[0] = ':';
+ sp[0] = ':';
memcpy(sp+1, name, len);
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
if (!symname_p(name) || strlen(name) != (size_t)len) {
@@ -415,6 +503,9 @@ sym_inspect(mrb_state *mrb, mrb_value sym)
sp[0] = ':';
sp[1] = '"';
}
+#ifdef MRB_UTF8_STRING
+ if (SYMBOL_INLINE_P(id)) RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+#endif
return str;
}
@@ -425,6 +516,11 @@ mrb_sym2str(mrb_state *mrb, mrb_sym sym)
const char *name = mrb_sym2name_len(mrb, sym, &len);
if (!name) return mrb_undef_value(); /* can't happen */
+ if (SYMBOL_INLINE_P(sym)) {
+ mrb_value str = mrb_str_new(mrb, name, len);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
+ }
return mrb_str_new_static(mrb, name, len);
}
@@ -439,7 +535,9 @@ mrb_sym2name(mrb_state *mrb, mrb_sym sym)
return name;
}
else {
- mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len));
+ mrb_value str = SYMBOL_INLINE_P(sym) ?
+ mrb_str_new(mrb, name, len) : mrb_str_new_static(mrb, name, len);
+ str = mrb_str_dump(mrb, str);
return RSTRING_PTR(str);
}
}
@@ -461,9 +559,10 @@ sym_cmp(mrb_state *mrb, mrb_value s1)
const char *p1, *p2;
int retval;
mrb_int len, len1, len2;
+ char buf1[8], buf2[8];
- p1 = mrb_sym2name_len(mrb, sym1, &len1);
- p2 = mrb_sym2name_len(mrb, sym2, &len2);
+ p1 = sym2name_len(mrb, sym1, buf1, &len1);
+ p2 = sym2name_len(mrb, sym2, buf2, &len2);
len = lesser(len1, len2);
retval = memcmp(p1, p2, len);
if (retval == 0) {
@@ -481,14 +580,13 @@ mrb_init_symbol(mrb_state *mrb)
{
struct RClass *sym;
- mrb->symbol_class = sym = 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_SET_INSTANCE_TT(sym, MRB_TT_SYMBOL);
mrb_undef_class_method(mrb, sym, "new");
- 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 */
- mrb_define_method(mrb, sym, "to_s", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */
- mrb_define_method(mrb, sym, "to_sym", sym_to_sym, MRB_ARGS_NONE()); /* 15.2.11.3.4 */
- mrb_define_method(mrb, sym, "inspect", sym_inspect, MRB_ARGS_NONE()); /* 15.2.11.3.5(x) */
- mrb_define_method(mrb, sym, "<=>", sym_cmp, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, sym, "id2name", sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.2 */
+ mrb_define_method(mrb, sym, "to_s", sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */
+ mrb_define_method(mrb, sym, "to_sym", sym_to_sym, MRB_ARGS_NONE()); /* 15.2.11.3.4 */
+ mrb_define_method(mrb, sym, "inspect", sym_inspect, MRB_ARGS_NONE()); /* 15.2.11.3.5(x) */
+ mrb_define_method(mrb, sym, "<=>", sym_cmp, MRB_ARGS_REQ(1));
}
diff --git a/src/variable.c b/src/variable.c
index de36efac6..e6f2f397e 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -9,155 +9,241 @@
#include <mruby/class.h>
#include <mruby/proc.h>
#include <mruby/string.h>
+#include <mruby/variable.h>
-typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*);
-
-#include <mruby/khash.h>
-
-#ifndef MRB_IVHASH_INIT_SIZE
-#define MRB_IVHASH_INIT_SIZE KHASH_MIN_SIZE
+#ifndef MRB_IV_SEGMENT_SIZE
+#define MRB_IV_SEGMENT_SIZE 4
#endif
-KHASH_DECLARE(iv, mrb_sym, mrb_value, TRUE)
-KHASH_DEFINE(iv, mrb_sym, mrb_value, TRUE, kh_int_hash_func, kh_int_hash_equal)
+typedef struct segment {
+ mrb_sym key[MRB_IV_SEGMENT_SIZE];
+ mrb_value val[MRB_IV_SEGMENT_SIZE];
+ struct segment *next;
+} segment;
/* Instance variable table structure */
typedef struct iv_tbl {
- khash_t(iv) h;
+ segment *rootseg;
+ size_t size;
+ size_t last_len;
} iv_tbl;
-/*
- * Creates the instance variable table.
- *
- * Parameters
- * mrb
- * Returns
- * the instance variable table.
- */
+/* Creates the instance variable table. */
static iv_tbl*
iv_new(mrb_state *mrb)
{
- return (iv_tbl*)kh_init_size(iv, mrb, MRB_IVHASH_INIT_SIZE);
+ iv_tbl *t;
+
+ t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl));
+ t->size = 0;
+ t->rootseg = NULL;
+ t->last_len = 0;
+
+ return t;
}
-/*
- * Set the value for the symbol in the instance variable table.
- *
- * Parameters
- * mrb
- * t the instance variable table to be set in.
- * sym the symbol to be used as the key.
- * val the value to be set.
- */
+/* Set the value for the symbol in the instance variable table. */
static void
iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
{
- khash_t(iv) *h = &t->h;
- khiter_t k;
+ segment *seg;
+ segment *prev = NULL;
+ segment *matched_seg = NULL;
+ size_t matched_idx = 0;
+ size_t i;
+
+ if (t == NULL) return;
+ seg = t->rootseg;
+ while (seg) {
+ for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
+ mrb_sym key = seg->key[i];
+ /* Found room in last segment after last_len */
+ if (!seg->next && i >= t->last_len) {
+ seg->key[i] = sym;
+ seg->val[i] = val;
+ t->last_len = i+1;
+ t->size++;
+ return;
+ }
+ if (!matched_seg && key == 0) {
+ matched_seg = seg;
+ matched_idx = i;
+ }
+ else if (key == sym) {
+ seg->val[i] = val;
+ return;
+ }
+ }
+ prev = seg;
+ seg = seg->next;
+ }
- k = kh_put(iv, mrb, h, sym);
- kh_value(h, k) = val;
+ /* Not found */
+ if (matched_seg) {
+ matched_seg->key[matched_idx] = sym;
+ matched_seg->val[matched_idx] = val;
+ t->size++;
+ return;
+ }
+
+ seg = (segment*)mrb_malloc(mrb, sizeof(segment));
+ seg->next = NULL;
+ seg->key[0] = sym;
+ seg->val[0] = val;
+ t->last_len = 1;
+ t->size++;
+ if (prev) {
+ prev->next = seg;
+ }
+ else {
+ t->rootseg = seg;
+ }
}
-/*
- * Get a value for a symbol from the instance variable table.
- *
- * Parameters
- * mrb
- * t the variable table to be searched.
- * sym the symbol to be used as the key.
- * vp the value pointer. Receives the value if the specified symbol is
- * contained in the instance variable table.
- * Returns
- * true if the specified symbol is contained in the instance variable table.
- */
+/* Get a value for a symbol from the instance variable table. */
static mrb_bool
iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
- khash_t(iv) *h = &t->h;
- khiter_t k;
+ segment *seg;
+ size_t i;
- k = kh_get(iv, mrb, h, sym);
- if (k != kh_end(h)) {
- if (vp) *vp = kh_value(h, k);
- return TRUE;
+ if (t == NULL) return FALSE;
+ seg = t->rootseg;
+ while (seg) {
+ for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
+ mrb_sym key = seg->key[i];
+
+ if (!seg->next && i >= t->last_len) {
+ return FALSE;
+ }
+ if (key == sym) {
+ if (vp) *vp = seg->val[i];
+ return TRUE;
+ }
+ }
+ seg = seg->next;
}
return FALSE;
}
-/*
- * Deletes the value for the symbol from the instance variable table.
- *
- * Parameters
- * t the variable table to be searched.
- * sym the symbol to be used as the key.
- * vp the value pointer. Receive the deleted value if the symbol is
- * contained in the instance variable table.
- * Returns
- * true if the specified symbol is contained in the instance variable table.
- */
+/* Deletes the value for the symbol from the instance variable table. */
static mrb_bool
iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
+ segment *seg;
+ size_t i;
+
if (t == NULL) return FALSE;
- else {
- khash_t(iv) *h = &t->h;
- khiter_t k;
-
- k = kh_get(iv, mrb, h, sym);
- if (k != kh_end(h)) {
- mrb_value val = kh_value(h, k);
- kh_del(iv, mrb, h, k);
- if (vp) *vp = val;
- return TRUE;
+ seg = t->rootseg;
+ while (seg) {
+ for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
+ mrb_sym key = seg->key[i];
+
+ if (!seg->next && i >= t->last_len) {
+ return FALSE;
+ }
+ if (key == sym) {
+ t->size--;
+ seg->key[i] = 0;
+ if (vp) *vp = seg->val[i];
+ return TRUE;
+ }
}
+ seg = seg->next;
}
return FALSE;
}
-static mrb_bool
-iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p)
+/* Iterates over the instance variable table. */
+static void
+iv_foreach(mrb_state *mrb, iv_tbl *t, mrb_iv_foreach_func *func, void *p)
{
- if (t == NULL) {
- return TRUE;
- }
- else {
- khash_t(iv) *h = &t->h;
- khiter_t k;
- int n;
-
- for (k = kh_begin(h); k != kh_end(h); k++) {
- if (kh_exist(h, k)) {
- n = (*func)(mrb, kh_key(h, k), kh_value(h, k), p);
- if (n > 0) return FALSE;
- if (n < 0) {
- kh_del(iv, mrb, h, k);
+ segment *seg;
+ size_t i;
+
+ if (t == NULL) return;
+ seg = t->rootseg;
+ while (seg) {
+ for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
+ mrb_sym key = seg->key[i];
+
+ /* no value in last segment after last_len */
+ if (!seg->next && i >= t->last_len) {
+ return;
+ }
+ if (key != 0) {
+ if ((*func)(mrb, key, seg->val[i], p) != 0) {
+ return;
}
}
}
+ seg = seg->next;
}
- return TRUE;
+ return;
}
+/* Get the size of the instance variable table. */
static size_t
iv_size(mrb_state *mrb, iv_tbl *t)
{
- if (t) {
- return kh_size(&t->h);
+ segment *seg;
+ size_t size = 0;
+
+ if (t == NULL) return 0;
+ if (t->size > 0) return t->size;
+ seg = t->rootseg;
+ while (seg) {
+ if (seg->next == NULL) {
+ size += t->last_len;
+ return size;
+ }
+ seg = seg->next;
+ size += MRB_IV_SEGMENT_SIZE;
}
+ /* empty iv_tbl */
return 0;
}
+/* Copy the instance variable table. */
static iv_tbl*
iv_copy(mrb_state *mrb, iv_tbl *t)
{
- return (iv_tbl*)kh_copy(iv, mrb, &t->h);
+ segment *seg;
+ iv_tbl *t2;
+
+ size_t i;
+
+ seg = t->rootseg;
+ t2 = iv_new(mrb);
+
+ while (seg != NULL) {
+ for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) {
+ mrb_sym key = seg->key[i];
+ mrb_value val = seg->val[i];
+
+ if ((seg->next == NULL) && (i >= t->last_len)) {
+ return t2;
+ }
+ iv_put(mrb, t2, key, val);
+ }
+ seg = seg->next;
+ }
+ return t2;
}
+/* Free memory of the instance variable table. */
static void
iv_free(mrb_state *mrb, iv_tbl *t)
{
- kh_destroy(iv, mrb, &t->h);
+ segment *seg;
+
+ seg = t->rootseg;
+ while (seg) {
+ segment *p = seg;
+ seg = seg->next;
+ mrb_free(mrb, p);
+ }
+ mrb_free(mrb, t);
}
static int
@@ -170,9 +256,7 @@ iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
static void
mark_tbl(mrb_state *mrb, iv_tbl *t)
{
- if (t) {
- iv_foreach(mrb, t, iv_mark_i, 0);
- }
+ iv_foreach(mrb, t, iv_mark_i, 0);
}
void
@@ -255,19 +339,64 @@ mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym)
return mrb_nil_value();
}
+static inline void assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
+
+void
+mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
+{
+ assign_class_name(mrb, obj, sym, v);
+ if (!obj->iv) {
+ obj->iv = iv_new(mrb);
+ }
+ iv_put(mrb, obj->iv, sym, v);
+ mrb_write_barrier(mrb, (struct RBasic*)obj);
+}
+
MRB_API void
mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
{
- iv_tbl *t = obj->iv;
+ mrb_check_frozen(mrb, obj);
+ mrb_obj_iv_set_force(mrb, obj, sym, v);
+}
- if (MRB_FROZEN_P(obj)) {
- mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %S", mrb_obj_value(obj));
- }
- if (!t) {
- t = obj->iv = iv_new(mrb);
+/* Iterates over the instance variable table. */
+MRB_API void
+mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p)
+{
+ if (!obj_iv_p(obj)) return;
+ iv_foreach(mrb, mrb_obj_ptr(obj)->iv, func, p);
+}
+
+static inline mrb_bool
+namespace_p(enum mrb_vtype tt)
+{
+ return tt == MRB_TT_CLASS || tt == MRB_TT_MODULE ? TRUE : FALSE;
+}
+
+static inline void
+assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
+{
+ if (namespace_p(obj->tt) && namespace_p(mrb_type(v))) {
+ struct RObject *c = mrb_obj_ptr(v);
+ if (obj != c && ISUPPER(mrb_sym2name(mrb, sym)[0])) {
+ mrb_sym id_classname = mrb_intern_lit(mrb, "__classname__");
+ mrb_value o = mrb_obj_iv_get(mrb, c, id_classname);
+
+ if (mrb_nil_p(o)) {
+ mrb_sym id_outer = mrb_intern_lit(mrb, "__outer__");
+ o = mrb_obj_iv_get(mrb, c, id_outer);
+
+ if (mrb_nil_p(o)) {
+ if ((struct RClass *)obj == mrb->object_class) {
+ mrb_obj_iv_set_force(mrb, c, id_classname, mrb_symbol_value(sym));
+ }
+ else {
+ mrb_obj_iv_set_force(mrb, c, id_outer, mrb_obj_value(obj));
+ }
+ }
+ }
+ }
}
- mrb_write_barrier(mrb, (struct RBasic*)obj);
- iv_put(mrb, t, sym, v);
}
MRB_API void
@@ -300,28 +429,23 @@ mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym)
return mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), sym);
}
-#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
-
MRB_API mrb_bool
-mrb_iv_p(mrb_state *mrb, mrb_sym iv_name)
+mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym iv_name)
{
const char *s;
- mrb_int i, len;
+ mrb_int 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;
+ if (ISDIGIT(s[1])) return FALSE;
+ return mrb_ident_p(s+1, len-1);
}
MRB_API void
-mrb_iv_check(mrb_state *mrb, mrb_sym iv_name)
+mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name)
{
- if (!mrb_iv_p(mrb, iv_name)) {
+ if (!mrb_iv_name_sym_p(mrb, iv_name)) {
mrb_name_error(mrb, iv_name, "'%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name));
}
}
@@ -401,27 +525,14 @@ mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
iv_tbl *t = mrb_obj_ptr(obj)->iv;
mrb_value val;
- if (t && iv_del(mrb, t, sym, &val)) {
+ mrb_check_frozen(mrb, mrb_obj_ptr(obj));
+ if (iv_del(mrb, t, sym, &val)) {
return val;
}
}
return mrb_undef_value();
}
-mrb_value
-mrb_vm_iv_get(mrb_state *mrb, mrb_sym sym)
-{
- /* get self */
- return mrb_iv_get(mrb, mrb->c->stack[0], sym);
-}
-
-void
-mrb_vm_iv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
-{
- /* get self */
- mrb_iv_set(mrb, mrb->c->stack[0], sym, v);
-}
-
static int
iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
@@ -460,7 +571,7 @@ mrb_obj_instance_variables(mrb_state *mrb, mrb_value self)
mrb_value ary;
ary = mrb_ary_new(mrb);
- if (obj_iv_p(self) && mrb_obj_ptr(self)->iv) {
+ if (obj_iv_p(self)) {
iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary);
}
return ary;
@@ -506,15 +617,13 @@ mrb_mod_class_variables(mrb_state *mrb, mrb_value mod)
ary = mrb_ary_new(mrb);
c = mrb_class_ptr(mod);
while (c) {
- if (c->iv) {
- iv_foreach(mrb, c->iv, cv_i, &ary);
- }
+ iv_foreach(mrb, c->iv, cv_i, &ary);
c = c->super;
}
return ary;
}
-MRB_API mrb_value
+mrb_value
mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
{
struct RClass * cls = c;
@@ -563,14 +672,13 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
struct RClass * cls = c;
while (c) {
- if (c->iv) {
- iv_tbl *t = c->iv;
+ iv_tbl *t = c->iv;
- if (iv_get(mrb, t, sym, NULL)) {
- mrb_write_barrier(mrb, (struct RBasic*)c);
- iv_put(mrb, t, sym, v);
- return;
- }
+ if (iv_get(mrb, t, sym, NULL)) {
+ mrb_check_frozen(mrb, c);
+ iv_put(mrb, t, sym, v);
+ mrb_write_barrier(mrb, (struct RBasic*)c);
+ return;
}
c = c->super;
}
@@ -595,12 +703,13 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
c = cls;
}
+ mrb_check_frozen(mrb, c);
if (!c->iv) {
c->iv = iv_new(mrb);
}
- mrb_write_barrier(mrb, (struct RBasic*)c);
iv_put(mrb, c->iv, sym, v);
+ mrb_write_barrier(mrb, (struct RBasic*)c);
}
MRB_API void
@@ -609,14 +718,12 @@ 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_API mrb_bool
+mrb_bool
mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym)
{
while (c) {
- if (c->iv) {
- iv_tbl *t = c->iv;
- if (iv_get(mrb, t, sym, NULL)) return TRUE;
- }
+ iv_tbl *t = c->iv;
+ if (iv_get(mrb, t, sym, NULL)) return TRUE;
c = c->super;
}
@@ -662,24 +769,23 @@ mod_const_check(mrb_state *mrb, mrb_value mod)
}
static mrb_value
-const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym, mrb_bool top)
+const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym)
{
struct RClass *c = base;
mrb_value v;
mrb_bool retry = FALSE;
mrb_value name;
- struct RClass *oclass = mrb->object_class;
L_RETRY:
while (c) {
- if (c->iv && (top || c != oclass || base == oclass)) {
+ if (c->iv) {
if (iv_get(mrb, c->iv, sym, &v))
return v;
}
c = c->super;
}
if (!retry && base->tt == MRB_TT_MODULE) {
- c = oclass;
+ c = mrb->object_class;
retry = TRUE;
goto L_RETRY;
}
@@ -691,7 +797,7 @@ MRB_API mrb_value
mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
{
mod_const_check(mrb, mod);
- return const_get(mrb, mrb_class_ptr(mod), sym, FALSE);
+ return const_get(mrb, mrb_class_ptr(mod), sym);
}
mrb_value
@@ -703,27 +809,30 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
struct RProc *proc;
c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
- if (c->iv && iv_get(mrb, c->iv, sym, &v)) {
+ if (iv_get(mrb, c->iv, sym, &v)) {
return v;
}
c2 = c;
while (c2 && c2->tt == MRB_TT_SCLASS) {
mrb_value klass;
- klass = mrb_obj_iv_get(mrb, (struct RObject *)c2,
- mrb_intern_lit(mrb, "__attached__"));
+
+ if (!iv_get(mrb, c2->iv, mrb_intern_lit(mrb, "__attached__"), &klass)) {
+ c2 = NULL;
+ break;
+ }
c2 = mrb_class_ptr(klass);
}
- if (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE) c = c2;
+ if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2;
mrb_assert(!MRB_PROC_CFUNC_P(mrb->c->ci->proc));
proc = mrb->c->ci->proc;
while (proc) {
c2 = MRB_PROC_TARGET_CLASS(proc);
- if (c2 && c2->iv && iv_get(mrb, c2->iv, sym, &v)) {
+ if (c2 && iv_get(mrb, c2->iv, sym, &v)) {
return v;
}
proc = proc->upper;
}
- return const_get(mrb, c, sym, TRUE);
+ return const_get(mrb, c, sym);
}
MRB_API void
@@ -796,9 +905,7 @@ mrb_mod_constants(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "|b", &inherit);
ary = mrb_ary_new(mrb);
while (c) {
- if (c->iv) {
- iv_foreach(mrb, c->iv, const_i, &ary);
- }
+ iv_foreach(mrb, c->iv, const_i, &ary);
if (!inherit) break;
c = c->super;
if (c == mrb->object_class) break;
@@ -811,9 +918,6 @@ mrb_gv_get(mrb_state *mrb, mrb_sym sym)
{
mrb_value v;
- if (!mrb->globals) {
- return mrb_nil_value();
- }
if (iv_get(mrb, mrb->globals, sym, &v))
return v;
return mrb_nil_value();
@@ -825,20 +929,15 @@ mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v)
iv_tbl *t;
if (!mrb->globals) {
- t = mrb->globals = iv_new(mrb);
- }
- else {
- t = mrb->globals;
+ mrb->globals = iv_new(mrb);
}
+ t = mrb->globals;
iv_put(mrb, t, sym, v);
}
MRB_API void
mrb_gv_remove(mrb_state *mrb, mrb_sym sym)
{
- if (!mrb->globals) {
- return;
- }
iv_del(mrb, mrb->globals, sym, NULL);
}
@@ -867,18 +966,8 @@ mrb_f_global_variables(mrb_state *mrb, mrb_value self)
{
iv_tbl *t = mrb->globals;
mrb_value ary = mrb_ary_new(mrb);
- size_t i;
- char buf[3];
- if (t) {
- iv_foreach(mrb, t, gv_i, &ary);
- }
- buf[0] = '$';
- buf[2] = 0;
- for (i = 1; i <= 9; ++i) {
- buf[1] = (char)(i + '0');
- mrb_ary_push(mrb, ary, mrb_symbol_value(mrb_intern(mrb, buf, 2)));
- }
+ iv_foreach(mrb, t, gv_i, &ary);
return ary;
}
@@ -892,7 +981,7 @@ mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude,
tmp = klass;
retry:
while (tmp) {
- if (tmp->iv && iv_get(mrb, tmp->iv, id, NULL)) {
+ if (iv_get(mrb, tmp->iv, id, NULL)) {
return TRUE;
}
if (!recurse && (klass != mrb->object_class)) break;
@@ -928,25 +1017,25 @@ struct csym_arg {
struct RClass *c;
mrb_sym sym;
};
-
+
static int
csym_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
{
struct csym_arg *a = (struct csym_arg*)p;
struct RClass *c = a->c;
-
+
if (mrb_type(v) == c->tt && mrb_class_ptr(v) == c) {
a->sym = sym;
return 1; /* stop iteration */
}
return 0;
}
-
+
static mrb_sym
find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c)
{
struct csym_arg arg;
-
+
if (!outer) return 0;
if (outer == c) return 0;
arg.c = c;
@@ -1009,8 +1098,24 @@ mrb_class_find_path(mrb_state *mrb, struct RClass *c)
str = mrb_sym2name_len(mrb, name, &len);
mrb_str_cat(mrb, path, str, len);
- iv_del(mrb, c->iv, mrb_intern_lit(mrb, "__outer__"), NULL);
- iv_put(mrb, c->iv, mrb_intern_lit(mrb, "__classname__"), path);
- mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path);
+ if (RSTRING_PTR(path)[0] != '#') {
+ iv_del(mrb, c->iv, mrb_intern_lit(mrb, "__outer__"), NULL);
+ iv_put(mrb, c->iv, mrb_intern_lit(mrb, "__classname__"), path);
+ mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path);
+ path = mrb_str_dup(mrb, path);
+ }
return path;
}
+
+#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c))
+
+mrb_bool
+mrb_ident_p(const char *s, mrb_int len)
+{
+ mrb_int i;
+
+ for (i = 0; i < len; i++) {
+ if (!identchar(s[i])) return FALSE;
+ }
+ return TRUE;
+}
diff --git a/src/vm.c b/src/vm.c
index c9e923ee0..0a6d4af8d 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -55,7 +55,7 @@ void abort(void);
/* Maximum depth of ecall() recursion. */
#ifndef MRB_ECALL_DEPTH_MAX
-#define MRB_ECALL_DEPTH_MAX 32
+#define MRB_ECALL_DEPTH_MAX 512
#endif
/* Maximum stack depth. Should be set lower on memory constrained systems.
@@ -101,7 +101,7 @@ static inline void
stack_clear(mrb_value *from, size_t count)
{
#ifndef MRB_NAN_BOXING
- const mrb_value mrb_value_zero = { 0 };
+ const mrb_value mrb_value_zero = { { 0 } };
while (count-- > 0) {
*from++ = mrb_value_zero;
@@ -141,7 +141,7 @@ stack_init(mrb_state *mrb)
}
static inline void
-envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
+envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t oldsize)
{
mrb_callinfo *ci = mrb->c->cibase;
@@ -151,7 +151,7 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
mrb_value *st;
if (e && MRB_ENV_STACK_SHARED_P(e) &&
- (st = e->stack) && oldbase <= st && st < oldbase+size) {
+ (st = e->stack) && oldbase <= st && st < oldbase+oldsize) {
ptrdiff_t off = e->stack - oldbase;
e->stack = newbase + off;
@@ -161,7 +161,7 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
e = MRB_PROC_ENV(ci->proc);
if (e && MRB_ENV_STACK_SHARED_P(e) &&
- (st = e->stack) && oldbase <= st && st < oldbase+size) {
+ (st = e->stack) && oldbase <= st && st < oldbase+oldsize) {
ptrdiff_t off = e->stack - oldbase;
e->stack = newbase + off;
@@ -176,7 +176,7 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size)
/** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end */
static void
-stack_extend_alloc(mrb_state *mrb, int room)
+stack_extend_alloc(mrb_state *mrb, mrb_int room)
{
mrb_value *oldbase = mrb->c->stbase;
mrb_value *newstack;
@@ -186,7 +186,7 @@ stack_extend_alloc(mrb_state *mrb, int room)
if (off > size) size = off;
#ifdef MRB_STACK_EXTEND_DOUBLING
- if (room <= size)
+ if ((size_t)room <= size)
size *= 2;
else
size += room;
@@ -205,7 +205,7 @@ stack_extend_alloc(mrb_state *mrb, int room)
mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
}
stack_clear(&(newstack[oldsize]), size - oldsize);
- envadjust(mrb, oldbase, newstack, size);
+ envadjust(mrb, oldbase, newstack, oldsize);
mrb->c->stbase = newstack;
mrb->c->stack = mrb->c->stbase + off;
mrb->c->stend = mrb->c->stbase + size;
@@ -217,8 +217,8 @@ stack_extend_alloc(mrb_state *mrb, int room)
}
}
-static inline void
-stack_extend(mrb_state *mrb, int room)
+MRB_API void
+mrb_stack_extend(mrb_state *mrb, mrb_int room)
{
if (mrb->c->stack + room >= mrb->c->stend) {
stack_extend_alloc(mrb, room);
@@ -333,11 +333,12 @@ ecall(mrb_state *mrb)
struct REnv *env;
ptrdiff_t cioff;
int ai = mrb_gc_arena_save(mrb);
- int i = --c->eidx;
+ uint16_t i = --c->eidx;
int nregs;
if (i<0) return;
- if (ci - c->cibase > MRB_ECALL_DEPTH_MAX) {
+ /* restrict total call depth of ecall() */
+ if (++mrb->ecall_nest > MRB_ECALL_DEPTH_MAX) {
mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
}
p = c->ensure[i];
@@ -356,7 +357,6 @@ ecall(mrb_state *mrb)
ci->acc = CI_ACC_SKIP;
ci->argc = 0;
ci->proc = p;
- ci->nregs = p->body.irep->nregs;
ci->target_class = MRB_PROC_TARGET_CLASS(p);
env = MRB_PROC_ENV(p);
mrb_assert(env);
@@ -365,11 +365,15 @@ ecall(mrb_state *mrb)
if (exc) {
mrb_gc_protect(mrb, mrb_obj_value(exc));
}
+ if (mrb->c->fib) {
+ mrb_gc_protect(mrb, mrb_obj_value(mrb->c->fib));
+ }
mrb_run(mrb, p, env->stack[0]);
mrb->c = c;
c->ci = c->cibase + cioff;
if (!mrb->exc) mrb->exc = exc;
mrb_gc_arena_restore(mrb, ai);
+ mrb->ecall_nest--;
}
#ifndef MRB_FUNCALL_ARGC_MAX
@@ -396,6 +400,30 @@ mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...)
return mrb_funcall_argv(mrb, self, mid, argc, argv);
}
+static int
+ci_nregs(mrb_callinfo *ci)
+{
+ struct RProc *p;
+ int n = 0;
+
+ if (!ci) return 3;
+ p = ci->proc;
+ if (!p) {
+ if (ci->argc < 0) return 3;
+ return ci->argc+2;
+ }
+ if (!MRB_PROC_CFUNC_P(p) && p->body.irep) {
+ n = p->body.irep->nregs;
+ }
+ if (ci->argc < 0) {
+ if (n < 3) n = 3; /* self + args + blk */
+ }
+ if (ci->argc > n) {
+ n = ci->argc + 2; /* self + blk */
+ }
+ return n;
+}
+
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)
{
@@ -426,13 +454,12 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
mrb_method_t m;
struct RClass *c;
mrb_callinfo *ci;
- int n;
+ int n = ci_nregs(mrb->c->ci);
ptrdiff_t voff = -1;
if (!mrb->c->stack) {
stack_init(mrb);
}
- n = mrb->c->ci->nregs;
if (argc < 0) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%S)", mrb_fixnum_value(argc));
}
@@ -446,7 +473,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
mrb_method_missing(mrb, mid, self, args);
}
mrb_ary_unshift(mrb, args, mrb_symbol_value(mid));
- stack_extend(mrb, n+2);
+ mrb_stack_extend(mrb, n+2);
mrb->c->stack[n+1] = args;
argc = -1;
}
@@ -459,26 +486,25 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
ci->argc = (int)argc;
ci->target_class = c;
mrb->c->stack = mrb->c->stack + n;
+ if (argc < 0) argc = 1;
if (mrb->c->stbase <= argv && argv < mrb->c->stend) {
voff = argv - mrb->c->stbase;
}
- if (MRB_METHOD_CFUNC_P(m)) {
- ci->nregs = (int)(argc + 2);
- stack_extend(mrb, ci->nregs);
- }
- else if (argc >= CALL_MAXARGS) {
+ if (argc >= CALL_MAXARGS) {
mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);
- stack_extend(mrb, ci->nregs+2);
+
mrb->c->stack[1] = args;
ci->argc = -1;
argc = 1;
}
- else {
+ mrb_stack_extend(mrb, argc + 2);
+ if (MRB_METHOD_PROC_P(m)) {
struct RProc *p = MRB_METHOD_PROC(m);
+
ci->proc = p;
- if (argc < 0) argc = 1;
- ci->nregs = (int)(p->body.irep->nregs + argc);
- stack_extend(mrb, ci->nregs);
+ if (!MRB_PROC_CFUNC_P(p)) {
+ mrb_stack_extend(mrb, p->body.irep->nregs + argc);
+ }
}
if (voff >= 0) {
argv = mrb->c->stbase + voff;
@@ -493,9 +519,6 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
int ai = mrb_gc_arena_save(mrb);
ci->acc = CI_ACC_DIRECT;
- if (MRB_METHOD_PROC_P(m)) {
- ci->proc = MRB_METHOD_PROC(m);
- }
val = MRB_METHOD_CFUNC(m)(mrb, self);
mrb->c->stack = mrb->c->ci->stackent;
cipop(mrb);
@@ -520,28 +543,25 @@ mrb_value
mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
{
mrb_callinfo *ci = mrb->c->ci;
- int keep;
+ int keep, nregs;
mrb->c->stack[0] = self;
ci->proc = p;
if (MRB_PROC_CFUNC_P(p)) {
return MRB_PROC_CFUNC(p)(mrb, self);
}
- ci->nregs = p->body.irep->nregs;
- ci->env = MRB_PROC_ENV(p);
- if (ci->env) ci->env->stack[0] = self;
+ nregs = p->body.irep->nregs;
if (ci->argc < 0) keep = 3;
else keep = ci->argc + 2;
- if (ci->nregs < keep) {
- stack_extend(mrb, keep);
+ if (nregs < keep) {
+ mrb_stack_extend(mrb, keep);
}
else {
- stack_extend(mrb, ci->nregs);
- stack_clear(mrb->c->stack+keep, ci->nregs-keep);
+ mrb_stack_extend(mrb, nregs);
+ stack_clear(mrb->c->stack+keep, nregs-keep);
}
ci = cipush(mrb);
- ci->nregs = 0;
ci->target_class = 0;
ci->pc = p->body.irep->iseq;
ci->stackent = mrb->c->stack;
@@ -569,7 +589,7 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
* k = Klass.new
* k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
*/
-MRB_API mrb_value
+mrb_value
mrb_f_send(mrb_state *mrb, mrb_value self)
{
mrb_sym name;
@@ -620,6 +640,7 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
{
struct RProc *p;
mrb_callinfo *ci;
+ int nregs;
if (mrb_nil_p(blk)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
@@ -635,19 +656,19 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
ci->argc = 1;
ci->mid = ci[-1].mid;
if (MRB_PROC_CFUNC_P(p)) {
- stack_extend(mrb, 3);
+ mrb_stack_extend(mrb, 3);
mrb->c->stack[0] = self;
mrb->c->stack[1] = self;
mrb->c->stack[2] = mrb_nil_value();
return MRB_PROC_CFUNC(p)(mrb, self);
}
- ci->nregs = p->body.irep->nregs;
- stack_extend(mrb, (ci->nregs < 3) ? 3 : ci->nregs);
+ nregs = p->body.irep->nregs;
+ if (nregs < 3) nregs = 3;
+ mrb_stack_extend(mrb, nregs);
mrb->c->stack[0] = self;
mrb->c->stack[1] = self;
- mrb->c->stack[2] = mrb_nil_value();
+ stack_clear(mrb->c->stack+2, nregs-2);
ci = cipush(mrb);
- ci->nregs = 0;
ci->target_class = 0;
ci->pc = p->body.irep->iseq;
ci->stackent = mrb->c->stack;
@@ -730,13 +751,15 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
struct RProc *p;
mrb_sym mid = mrb->c->ci->mid;
mrb_callinfo *ci;
- int n = mrb->c->ci->nregs;
mrb_value val;
+ int n;
if (mrb_nil_p(b)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
}
- if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
+ ci = mrb->c->ci;
+ n = ci_nregs(ci);
+ if (ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) {
mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err));
}
p = mrb_proc_ptr(b);
@@ -747,9 +770,9 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value
ci->argc = (int)argc;
ci->target_class = c;
ci->acc = CI_ACC_SKIP;
+ n = MRB_PROC_CFUNC_P(p) ? (int)(argc+2) : p->body.irep->nregs;
mrb->c->stack = mrb->c->stack + n;
- ci->nregs = MRB_PROC_CFUNC_P(p) ? (int)(argc+2) : p->body.irep->nregs;
- stack_extend(mrb, ci->nregs);
+ mrb_stack_extend(mrb, n);
mrb->c->stack[0] = self;
if (argc > 0) {
@@ -800,38 +823,13 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const
p = mrb_proc_ptr(b);
ci = mrb->c->ci;
- stack_extend(mrb, 3);
+ mrb_stack_extend(mrb, 3);
mrb->c->stack[1] = mrb_ary_new_from_values(mrb, argc, argv);
mrb->c->stack[2] = mrb_nil_value();
ci->argc = -1;
return mrb_exec_irep(mrb, self, p);
}
-mrb_value
-mrb_mod_s_nesting(mrb_state *mrb, mrb_value mod)
-{
- struct RProc *proc;
- mrb_value ary;
- struct RClass *c = NULL;
-
- mrb_get_args(mrb, "");
- ary = mrb_ary_new(mrb);
- proc = mrb->c->ci[-1].proc; /* callee proc */
- mrb_assert(!MRB_PROC_CFUNC_P(proc));
- while (proc) {
- if (MRB_PROC_SCOPE_P(proc)) {
- struct RClass *c2 = MRB_PROC_TARGET_CLASS(proc);
-
- if (c2 != c) {
- c = c2;
- mrb_ary_push(mrb, ary, mrb_obj_value(c));
- }
- }
- proc = proc->upper;
- }
- return ary;
-}
-
static struct RBreak*
break_new(mrb_state *mrb, struct RProc *p, mrb_value val)
{
@@ -892,8 +890,8 @@ argnum_error(mrb_state *mrb, mrb_int num)
mrb_exc_set(mrb, exc);
}
-#define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc;
-#define ERR_PC_CLR(mrb) mrb->c->ci->err = 0;
+#define ERR_PC_SET(mrb) mrb->c->ci->err = pc0;
+#define ERR_PC_CLR(mrb) mrb->c->ci->err = 0;
#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
@@ -906,25 +904,26 @@ argnum_error(mrb_state *mrb, mrb_int num)
#define BYTECODE_DECODER(x) (x)
#endif
-
+#ifndef MRB_DISABLE_DIRECT_THREADING
#if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER
#define DIRECT_THREADED
#endif
+#endif /* ifndef MRB_DISABLE_DIRECT_THREADING */
#ifndef DIRECT_THREADED
-#define INIT_DISPATCH for (;;) { i = BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (GET_OPCODE(i)) {
-#define CASE(op) case op:
-#define NEXT pc++; break
-#define JUMP break
+#define INIT_DISPATCH for (;;) { insn = BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (insn) {
+#define CASE(insn,ops) case insn: pc0=pc++; FETCH_ ## ops ();; L_ ## insn ## _BODY:
+#define NEXT break
+#define JUMP NEXT
#define END_DISPATCH }}
#else
#define INIT_DISPATCH JUMP; return mrb_nil_value();
-#define CASE(op) L_ ## op:
-#define NEXT i=BYTECODE_DECODER(*++pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
-#define JUMP i=BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)]
+#define CASE(insn,ops) L_ ## insn: pc0=pc++; FETCH_ ## ops (); L_ ## insn ## _BODY:
+#define NEXT insn=BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[insn]
+#define JUMP NEXT
#define END_DISPATCH
@@ -944,51 +943,57 @@ mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stac
}
if (stack_keep > nregs)
nregs = stack_keep;
- stack_extend(mrb, nregs);
+ mrb_stack_extend(mrb, nregs);
stack_clear(c->stack + stack_keep, nregs - stack_keep);
c->stack[0] = self;
result = mrb_vm_exec(mrb, proc, irep->iseq);
- if (c->ci - c->cibase > cioff) {
+ if (mrb->c != c) {
+ if (mrb->c->fib) {
+ mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib);
+ }
+ mrb->c = c;
+ }
+ else if (c->ci - c->cibase > cioff) {
c->ci = c->cibase + cioff;
}
- mrb->c = c;
return result;
}
+static mrb_bool
+check_target_class(mrb_state *mrb)
+{
+ if (!mrb->c->ci->target_class) {
+ mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "no target class or module");
+ mrb_exc_set(mrb, exc);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self);
+
MRB_API mrb_value
mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
{
- /* mrb_assert(mrb_proc_cfunc_p(proc)) */
+ /* mrb_assert(MRB_PROC_CFUNC_P(proc)) */
+ mrb_code *pc0 = pc;
mrb_irep *irep = proc->body.irep;
mrb_value *pool = irep->pool;
mrb_sym *syms = irep->syms;
- mrb_code i;
+ mrb_code insn;
int ai = mrb_gc_arena_save(mrb);
struct mrb_jmpbuf *prev_jmp = mrb->jmp;
struct mrb_jmpbuf c_jmp;
+ uint32_t a;
+ uint16_t b;
+ uint8_t c;
+ mrb_sym mid;
#ifdef DIRECT_THREADED
static void *optable[] = {
- &&L_OP_NOP, &&L_OP_MOVE,
- &&L_OP_LOADL, &&L_OP_LOADI, &&L_OP_LOADSYM, &&L_OP_LOADNIL,
- &&L_OP_LOADSELF, &&L_OP_LOADT, &&L_OP_LOADF,
- &&L_OP_GETGLOBAL, &&L_OP_SETGLOBAL, &&L_OP_GETSPECIAL, &&L_OP_SETSPECIAL,
- &&L_OP_GETIV, &&L_OP_SETIV, &&L_OP_GETCV, &&L_OP_SETCV,
- &&L_OP_GETCONST, &&L_OP_SETCONST, &&L_OP_GETMCNST, &&L_OP_SETMCNST,
- &&L_OP_GETUPVAR, &&L_OP_SETUPVAR,
- &&L_OP_JMP, &&L_OP_JMPIF, &&L_OP_JMPNOT,
- &&L_OP_ONERR, &&L_OP_RESCUE, &&L_OP_POPERR, &&L_OP_RAISE, &&L_OP_EPUSH, &&L_OP_EPOP,
- &&L_OP_SEND, &&L_OP_SENDB, &&L_OP_FSEND,
- &&L_OP_CALL, &&L_OP_SUPER, &&L_OP_ARGARY, &&L_OP_ENTER,
- &&L_OP_KARG, &&L_OP_KDICT, &&L_OP_RETURN, &&L_OP_TAILCALL, &&L_OP_BLKPUSH,
- &&L_OP_ADD, &&L_OP_ADDI, &&L_OP_SUB, &&L_OP_SUBI, &&L_OP_MUL, &&L_OP_DIV,
- &&L_OP_EQ, &&L_OP_LT, &&L_OP_LE, &&L_OP_GT, &&L_OP_GE,
- &&L_OP_ARRAY, &&L_OP_ARYCAT, &&L_OP_ARYPUSH, &&L_OP_AREF, &&L_OP_ASET, &&L_OP_APOST,
- &&L_OP_STRING, &&L_OP_STRCAT, &&L_OP_HASH,
- &&L_OP_LAMBDA, &&L_OP_RANGE, &&L_OP_OCLASS,
- &&L_OP_CLASS, &&L_OP_MODULE, &&L_OP_EXEC,
- &&L_OP_METHOD, &&L_OP_SCLASS, &&L_OP_TCLASS,
- &&L_OP_DEBUG, &&L_OP_STOP, &&L_OP_ERR,
+#define OPCODE(x,_) &&L_OP_ ## x,
+#include "mruby/ops.h"
+#undef OPCODE
};
#endif
@@ -999,35 +1004,29 @@ RETRY_TRY_BLOCK:
if (exc_catched) {
exc_catched = FALSE;
+ mrb_gc_arena_restore(mrb, ai);
if (mrb->exc && mrb->exc->tt == MRB_TT_BREAK)
goto L_BREAK;
goto L_RAISE;
}
mrb->jmp = &c_jmp;
mrb->c->ci->proc = proc;
- mrb->c->ci->nregs = irep->nregs;
#define regs (mrb->c->stack)
INIT_DISPATCH {
- CASE(OP_NOP) {
+ CASE(OP_NOP, Z) {
/* do nothing */
NEXT;
}
- CASE(OP_MOVE) {
- /* A B R(A) := R(B) */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
+ CASE(OP_MOVE, BB) {
regs[a] = regs[b];
NEXT;
}
- CASE(OP_LOADL) {
- /* A Bx R(A) := Pool(Bx) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
+ CASE(OP_LOADL, BB) {
#ifdef MRB_WORD_BOXING
- mrb_value val = pool[bx];
+ mrb_value val = pool[b];
#ifndef MRB_WITHOUT_FLOAT
if (mrb_float_p(val)) {
val = mrb_float_value(mrb, mrb_float(val));
@@ -1035,167 +1034,138 @@ RETRY_TRY_BLOCK:
#endif
regs[a] = val;
#else
- regs[a] = pool[bx];
+ regs[a] = pool[b];
#endif
NEXT;
}
- CASE(OP_LOADI) {
- /* A sBx R(A) := sBx */
- int a = GETARG_A(i);
- mrb_int bx = GETARG_sBx(i);
- SET_INT_VALUE(regs[a], bx);
+ CASE(OP_LOADI, BB) {
+ SET_INT_VALUE(regs[a], b);
+ NEXT;
+ }
+
+ CASE(OP_LOADINEG, BB) {
+ SET_INT_VALUE(regs[a], -b);
+ NEXT;
+ }
+
+ CASE(OP_LOADI__1,B) goto L_LOADI;
+ CASE(OP_LOADI_0,B) goto L_LOADI;
+ CASE(OP_LOADI_1,B) goto L_LOADI;
+ CASE(OP_LOADI_2,B) goto L_LOADI;
+ CASE(OP_LOADI_3,B) goto L_LOADI;
+ CASE(OP_LOADI_4,B) goto L_LOADI;
+ CASE(OP_LOADI_5,B) goto L_LOADI;
+ CASE(OP_LOADI_6,B) goto L_LOADI;
+ CASE(OP_LOADI_7, B) {
+ L_LOADI:
+ SET_INT_VALUE(regs[a], (mrb_int)insn - (mrb_int)OP_LOADI_0);
NEXT;
}
- CASE(OP_LOADSYM) {
- /* A Bx R(A) := Syms(Bx) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- SET_SYM_VALUE(regs[a], syms[bx]);
+ CASE(OP_LOADSYM, BB) {
+ SET_SYM_VALUE(regs[a], syms[b]);
NEXT;
}
- CASE(OP_LOADSELF) {
- /* A R(A) := self */
- int a = GETARG_A(i);
+ CASE(OP_LOADNIL, B) {
+ SET_NIL_VALUE(regs[a]);
+ NEXT;
+ }
+
+ CASE(OP_LOADSELF, B) {
regs[a] = regs[0];
NEXT;
}
- CASE(OP_LOADT) {
- /* A R(A) := true */
- int a = GETARG_A(i);
+ CASE(OP_LOADT, B) {
SET_TRUE_VALUE(regs[a]);
NEXT;
}
- CASE(OP_LOADF) {
- /* A R(A) := false */
- int a = GETARG_A(i);
+ CASE(OP_LOADF, B) {
SET_FALSE_VALUE(regs[a]);
NEXT;
}
- CASE(OP_GETGLOBAL) {
- /* A Bx R(A) := getglobal(Syms(Bx)) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- mrb_value val = mrb_gv_get(mrb, syms[bx]);
+ CASE(OP_GETGV, BB) {
+ mrb_value val = mrb_gv_get(mrb, syms[b]);
regs[a] = val;
NEXT;
}
- CASE(OP_SETGLOBAL) {
- /* A Bx setglobal(Syms(Bx), R(A)) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- mrb_gv_set(mrb, syms[bx], regs[a]);
+ CASE(OP_SETGV, BB) {
+ mrb_gv_set(mrb, syms[b], regs[a]);
NEXT;
}
- CASE(OP_GETSPECIAL) {
- /* A Bx R(A) := Special[Bx] */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- mrb_value val = mrb_vm_special_get(mrb, bx);
+ CASE(OP_GETSV, BB) {
+ mrb_value val = mrb_vm_special_get(mrb, b);
regs[a] = val;
NEXT;
}
- CASE(OP_SETSPECIAL) {
- /* A Bx Special[Bx] := R(A) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- mrb_vm_special_set(mrb, bx, regs[a]);
+ CASE(OP_SETSV, BB) {
+ mrb_vm_special_set(mrb, b, regs[a]);
NEXT;
}
- CASE(OP_GETIV) {
- /* A Bx R(A) := ivget(Bx) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- mrb_value val = mrb_vm_iv_get(mrb, syms[bx]);
- regs[a] = val;
+ CASE(OP_GETIV, BB) {
+ regs[a] = mrb_iv_get(mrb, regs[0], syms[b]);
NEXT;
}
- CASE(OP_SETIV) {
- /* A Bx ivset(Syms(Bx),R(A)) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- mrb_vm_iv_set(mrb, syms[bx], regs[a]);
+ CASE(OP_SETIV, BB) {
+ mrb_iv_set(mrb, regs[0], syms[b], regs[a]);
NEXT;
}
- CASE(OP_GETCV) {
- /* A Bx R(A) := cvget(Syms(Bx)) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
+ CASE(OP_GETCV, BB) {
mrb_value val;
- ERR_PC_SET(mrb, pc);
- val = mrb_vm_cv_get(mrb, syms[bx]);
+ ERR_PC_SET(mrb);
+ val = mrb_vm_cv_get(mrb, syms[b]);
ERR_PC_CLR(mrb);
regs[a] = val;
NEXT;
}
- CASE(OP_SETCV) {
- /* A Bx cvset(Syms(Bx),R(A)) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- mrb_vm_cv_set(mrb, syms[bx], regs[a]);
+ CASE(OP_SETCV, BB) {
+ mrb_vm_cv_set(mrb, syms[b], regs[a]);
NEXT;
}
- CASE(OP_GETCONST) {
- /* A Bx R(A) := constget(Syms(Bx)) */
+ CASE(OP_GETCONST, BB) {
mrb_value val;
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- mrb_sym sym = syms[bx];
+ mrb_sym sym = syms[b];
- ERR_PC_SET(mrb, pc);
+ ERR_PC_SET(mrb);
val = mrb_vm_const_get(mrb, sym);
ERR_PC_CLR(mrb);
regs[a] = val;
NEXT;
}
- CASE(OP_SETCONST) {
- /* A Bx constset(Syms(Bx),R(A)) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- mrb_vm_const_set(mrb, syms[bx], regs[a]);
+ CASE(OP_SETCONST, BB) {
+ mrb_vm_const_set(mrb, syms[b], regs[a]);
NEXT;
}
- CASE(OP_GETMCNST) {
- /* A Bx R(A) := R(A)::Syms(Bx) */
+ CASE(OP_GETMCNST, BB) {
mrb_value val;
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- ERR_PC_SET(mrb, pc);
- val = mrb_const_get(mrb, regs[a], syms[bx]);
+ ERR_PC_SET(mrb);
+ val = mrb_const_get(mrb, regs[a], syms[b]);
ERR_PC_CLR(mrb);
regs[a] = val;
NEXT;
}
- CASE(OP_SETMCNST) {
- /* A Bx R(A+1)::Syms(Bx) := R(A) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- mrb_const_set(mrb, regs[a+1], syms[bx], regs[a]);
+ CASE(OP_SETMCNST, BB) {
+ mrb_const_set(mrb, regs[a+1], syms[b], regs[a]);
NEXT;
}
- CASE(OP_GETUPVAR) {
- /* A B C R(A) := uvget(B,C) */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
- int c = GETARG_C(i);
+ CASE(OP_GETUPVAR, BBB) {
mrb_value *regs_a = regs + a;
struct REnv *e = uvenv(mrb, c);
@@ -1208,12 +1178,7 @@ RETRY_TRY_BLOCK:
NEXT;
}
- CASE(OP_SETUPVAR) {
- /* A B C uvset(B,C,R(A)) */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
- int c = GETARG_C(i);
-
+ CASE(OP_SETUPVAR, BBB) {
struct REnv *e = uvenv(mrb, c);
if (e) {
@@ -1227,127 +1192,126 @@ RETRY_TRY_BLOCK:
NEXT;
}
- CASE(OP_JMP) {
- /* sBx pc+=sBx */
- int sbx = GETARG_sBx(i);
- pc += sbx;
+ CASE(OP_JMP, S) {
+ pc = irep->iseq+a;
JUMP;
}
-
- CASE(OP_JMPIF) {
- /* A sBx if R(A) pc+=sBx */
- int a = GETARG_A(i);
- int sbx = GETARG_sBx(i);
+ CASE(OP_JMPIF, BS) {
if (mrb_test(regs[a])) {
- pc += sbx;
+ pc = irep->iseq+b;
JUMP;
}
NEXT;
}
-
- CASE(OP_JMPNOT) {
- /* A sBx if !R(A) pc+=sBx */
- int a = GETARG_A(i);
- int sbx = GETARG_sBx(i);
+ CASE(OP_JMPNOT, BS) {
if (!mrb_test(regs[a])) {
- pc += sbx;
+ pc = irep->iseq+b;
+ JUMP;
+ }
+ NEXT;
+ }
+ CASE(OP_JMPNIL, BS) {
+ if (mrb_nil_p(regs[a])) {
+ pc = irep->iseq+b;
JUMP;
}
NEXT;
}
- CASE(OP_ONERR) {
- /* sBx pc+=sBx on exception */
- int sbx = GETARG_sBx(i);
+ CASE(OP_ONERR, S) {
+ /* check rescue stack */
+ if (mrb->c->ci->ridx == UINT16_MAX-1) {
+ mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "too many nested rescues");
+ mrb_exc_set(mrb, exc);
+ goto L_RAISE;
+ }
+ /* expand rescue stack */
if (mrb->c->rsize <= mrb->c->ci->ridx) {
if (mrb->c->rsize == 0) mrb->c->rsize = RESCUE_STACK_INIT_SIZE;
- else mrb->c->rsize *= 2;
- mrb->c->rescue = (mrb_code **)mrb_realloc(mrb, mrb->c->rescue, sizeof(mrb_code*) * mrb->c->rsize);
+ else {
+ mrb->c->rsize *= 2;
+ if (mrb->c->rsize <= mrb->c->ci->ridx) {
+ mrb->c->rsize = UINT16_MAX;
+ }
+ }
+ mrb->c->rescue = (uint16_t*)mrb_realloc(mrb, mrb->c->rescue, sizeof(uint16_t)*mrb->c->rsize);
}
- mrb->c->rescue[mrb->c->ci->ridx++] = pc + sbx;
+ /* push rescue stack */
+ mrb->c->rescue[mrb->c->ci->ridx++] = a;
NEXT;
}
- CASE(OP_RESCUE) {
- /* A B R(A) := exc; clear(exc); R(B) := matched (bool) */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
- int c = GETARG_C(i);
- mrb_value exc;
-
- if (c == 0) {
- exc = mrb_obj_value(mrb->exc);
- mrb->exc = 0;
- }
- else { /* continued; exc taken from R(A) */
- exc = regs[a];
- }
- if (b != 0) {
- mrb_value e = regs[b];
- struct RClass *ec;
+ CASE(OP_EXCEPT, B) {
+ mrb_value exc = mrb_obj_value(mrb->exc);
+ mrb->exc = 0;
+ regs[a] = exc;
+ NEXT;
+ }
+ CASE(OP_RESCUE, BB) {
+ mrb_value exc = regs[a]; /* exc on stack */
+ mrb_value e = regs[b];
+ struct RClass *ec;
- switch (mrb_type(e)) {
- case MRB_TT_CLASS:
- case MRB_TT_MODULE:
- break;
- default:
- {
- mrb_value exc;
+ switch (mrb_type(e)) {
+ case MRB_TT_CLASS:
+ case MRB_TT_MODULE:
+ break;
+ default:
+ {
+ mrb_value exc;
- exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR,
- "class or module required for rescue clause");
- mrb_exc_set(mrb, exc);
- goto L_RAISE;
- }
+ exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR,
+ "class or module required for rescue clause");
+ mrb_exc_set(mrb, exc);
+ goto L_RAISE;
}
- ec = mrb_class_ptr(e);
- regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec));
- }
- if (a != 0 && c == 0) {
- regs[a] = exc;
}
+ ec = mrb_class_ptr(e);
+ regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec));
NEXT;
}
- CASE(OP_POPERR) {
- /* A A.times{rescue_pop()} */
- int a = GETARG_A(i);
-
+ CASE(OP_POPERR, B) {
mrb->c->ci->ridx -= a;
NEXT;
}
- CASE(OP_RAISE) {
- /* A raise(R(A)) */
- int a = GETARG_A(i);
-
+ CASE(OP_RAISE, B) {
mrb_exc_set(mrb, regs[a]);
goto L_RAISE;
}
- CASE(OP_EPUSH) {
- /* Bx ensure_push(SEQ[Bx]) */
- int bx = GETARG_Bx(i);
+ CASE(OP_EPUSH, B) {
struct RProc *p;
- p = mrb_closure_new(mrb, irep->reps[bx]);
- /* push ensure_stack */
+ p = mrb_closure_new(mrb, irep->reps[a]);
+ /* check ensure stack */
+ if (mrb->c->eidx == UINT16_MAX-1) {
+ mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "too many nested ensures");
+ mrb_exc_set(mrb, exc);
+ goto L_RAISE;
+ }
+ /* expand ensure stack */
if (mrb->c->esize <= mrb->c->eidx+1) {
if (mrb->c->esize == 0) mrb->c->esize = ENSURE_STACK_INIT_SIZE;
- else mrb->c->esize *= 2;
- mrb->c->ensure = (struct RProc **)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*) * mrb->c->esize);
+ else {
+ mrb->c->esize *= 2;
+ if (mrb->c->esize <= mrb->c->eidx) {
+ mrb->c->esize = UINT16_MAX;
+ }
+ }
+ mrb->c->ensure = (struct RProc**)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*)*mrb->c->esize);
}
+ /* push ensure stack */
mrb->c->ensure[mrb->c->eidx++] = p;
mrb->c->ensure[mrb->c->eidx] = NULL;
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_EPOP) {
- /* A A.times{ensure_pop().call} */
- int a = GETARG_A(i);
+ CASE(OP_EPOP, B) {
mrb_callinfo *ci = mrb->c->ci;
- int n, epos = ci->epos;
+ unsigned int n, epos = ci->epos;
mrb_value self = regs[0];
struct RClass *target_class = ci->target_class;
@@ -1355,10 +1319,11 @@ RETRY_TRY_BLOCK:
NEXT;
}
- if (a > mrb->c->eidx - epos)
+ if (a > (int)mrb->c->eidx - epos)
a = mrb->c->eidx - epos;
- pc = pc + 1;
for (n=0; n<a; n++) {
+ int nregs = irep->nregs;
+
proc = mrb->c->ensure[epos+n];
mrb->c->ensure[epos+n] = NULL;
if (proc == NULL) continue;
@@ -1368,12 +1333,11 @@ RETRY_TRY_BLOCK:
ci->argc = 0;
ci->proc = proc;
ci->stackent = mrb->c->stack;
- ci->nregs = irep->nregs;
ci->target_class = target_class;
ci->pc = pc;
- ci->acc = ci[-1].nregs;
+ ci->acc = nregs;
mrb->c->stack += ci->acc;
- stack_extend(mrb, ci->nregs);
+ mrb_stack_extend(mrb, irep->nregs);
regs[0] = self;
pc = irep->iseq;
}
@@ -1383,63 +1347,69 @@ RETRY_TRY_BLOCK:
JUMP;
}
- CASE(OP_LOADNIL) {
- /* A R(A) := nil */
- int a = GETARG_A(i);
+ CASE(OP_SENDV, BB) {
+ c = CALL_MAXARGS;
+ goto L_SEND;
+ };
- SET_NIL_VALUE(regs[a]);
- NEXT;
- }
+ CASE(OP_SENDVB, BB) {
+ c = CALL_MAXARGS;
+ goto L_SENDB;
+ };
- CASE(OP_SENDB) {
- /* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C),&R(A+C+1))*/
- /* fall through */
+ CASE(OP_SEND, BBB)
+ L_SEND:
+ {
+ /* push nil after arguments */
+ int bidx = (c == CALL_MAXARGS) ? a+2 : a+c+1;
+ SET_NIL_VALUE(regs[bidx]);
+ goto L_SENDB;
+ };
+ L_SEND_SYM:
+ {
+ /* push nil after arguments */
+ int bidx = (c == CALL_MAXARGS) ? a+2 : a+c+1;
+ SET_NIL_VALUE(regs[bidx]);
+ goto L_SENDB_SYM;
};
- L_SEND:
- CASE(OP_SEND) {
- /* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C)) */
- int a = GETARG_A(i);
- int n = GETARG_C(i);
- int argc = (n == CALL_MAXARGS) ? -1 : n;
- int bidx = (argc < 0) ? a+2 : a+n+1;
+ CASE(OP_SENDB, BBB)
+ L_SENDB:
+ mid = syms[b];
+ L_SENDB_SYM:
+ {
+ int argc = (c == CALL_MAXARGS) ? -1 : c;
+ int bidx = (argc < 0) ? a+2 : a+c+1;
mrb_method_t m;
- struct RClass *c;
+ struct RClass *cls;
mrb_callinfo *ci = mrb->c->ci;
mrb_value recv, blk;
- mrb_sym mid = syms[GETARG_B(i)];
- mrb_assert(bidx < ci->nregs);
+ mrb_assert(bidx < irep->nregs);
recv = regs[a];
- if (GET_OPCODE(i) != OP_SENDB) {
- SET_NIL_VALUE(regs[bidx]);
- blk = regs[bidx];
- }
- else {
- blk = regs[bidx];
- if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
- blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
- /* The stack might have been reallocated during mrb_convert_type(),
- see #3622 */
- regs[bidx] = blk;
- }
+ blk = regs[bidx];
+ if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) {
+ blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc");
+ /* The stack might have been reallocated during mrb_convert_type(),
+ see #3622 */
+ regs[bidx] = blk;
}
- c = mrb_class(mrb, recv);
- m = mrb_method_search_vm(mrb, &c, mid);
+ cls = mrb_class(mrb, recv);
+ m = mrb_method_search_vm(mrb, &cls, mid);
if (MRB_METHOD_UNDEF_P(m)) {
mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, missing);
+ m = mrb_method_search_vm(mrb, &cls, missing);
if (MRB_METHOD_UNDEF_P(m) || (missing == mrb->c->ci->mid && mrb_obj_eq(mrb, regs[0], recv))) {
- mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, n, regs+a+1);
- ERR_PC_SET(mrb, pc);
+ mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, c, regs+a+1);
+ ERR_PC_SET(mrb);
mrb_method_missing(mrb, mid, recv, args);
}
if (argc >= 0) {
if (a+2 >= irep->nregs) {
- stack_extend(mrb, a+3);
+ mrb_stack_extend(mrb, a+3);
}
- regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ regs[a+1] = mrb_ary_new_from_values(mrb, c, regs+a+1);
regs[a+2] = blk;
argc = -1;
}
@@ -1451,17 +1421,16 @@ RETRY_TRY_BLOCK:
ci = cipush(mrb);
ci->mid = mid;
ci->stackent = mrb->c->stack;
- ci->target_class = c;
+ ci->target_class = cls;
ci->argc = argc;
- ci->pc = pc + 1;
+ ci->pc = pc;
ci->acc = a;
/* prepare stack */
mrb->c->stack += a;
if (MRB_METHOD_CFUNC_P(m)) {
- ci->nregs = (argc < 0) ? 3 : n+2;
if (MRB_METHOD_PROC_P(m)) {
struct RProc *p = MRB_METHOD_PROC(m);
@@ -1475,12 +1444,10 @@ RETRY_TRY_BLOCK:
mrb_gc_arena_shrink(mrb, ai);
if (mrb->exc) goto L_RAISE;
ci = mrb->c->ci;
- if (GET_OPCODE(i) == OP_SENDB) {
- if (mrb_type(blk) == MRB_TT_PROC) {
- struct RProc *p = mrb_proc_ptr(blk);
- if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == ci[-1].env) {
- p->flags |= MRB_PROC_ORPHAN;
- }
+ if (mrb_type(blk) == MRB_TT_PROC) {
+ struct RProc *p = mrb_proc_ptr(blk);
+ if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == ci[-1].env) {
+ p->flags |= MRB_PROC_ORPHAN;
}
}
if (!ci->target_class) { /* return from context modifying method (resume/yield) */
@@ -1509,21 +1476,13 @@ RETRY_TRY_BLOCK:
irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
- ci->nregs = irep->nregs;
- stack_extend(mrb, (argc < 0 && ci->nregs < 3) ? 3 : ci->nregs);
+ mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : irep->nregs);
pc = irep->iseq;
JUMP;
}
}
- CASE(OP_FSEND) {
- /* A B C R(A) := fcall(R(A),Syms(B),R(A+1),... ,R(A+C-1)) */
- /* not implemented yet */
- NEXT;
- }
-
- CASE(OP_CALL) {
- /* A R(A) := self.call(frame.argc, frame.argv) */
+ CASE(OP_CALL, Z) {
mrb_callinfo *ci;
mrb_value recv = mrb->c->stack[0];
struct RProc *m = mrb_proc_ptr(recv);
@@ -1566,12 +1525,13 @@ RETRY_TRY_BLOCK:
irep = m->body.irep;
if (!irep) {
mrb->c->stack[0] = mrb_nil_value();
- goto L_RETURN;
+ a = 0;
+ c = OP_R_NORMAL;
+ goto L_OP_RETURN_BODY;
}
pool = irep->pool;
syms = irep->syms;
- ci->nregs = irep->nregs;
- stack_extend(mrb, ci->nregs);
+ mrb_stack_extend(mrb, irep->nregs);
if (ci->argc < 0) {
if (irep->nregs > 3) {
stack_clear(regs+3, irep->nregs-3);
@@ -1588,20 +1548,17 @@ RETRY_TRY_BLOCK:
}
}
- CASE(OP_SUPER) {
- /* A C R(A) := super(R(A+1),... ,R(A+C+1)) */
- int a = GETARG_A(i);
- int n = GETARG_C(i);
- int argc = (n == CALL_MAXARGS) ? -1 : n;
- int bidx = (argc < 0) ? a+2 : a+n+1;
+ CASE(OP_SUPER, BB) {
+ int argc = (b == CALL_MAXARGS) ? -1 : b;
+ int bidx = (argc < 0) ? a+2 : a+b+1;
mrb_method_t m;
- struct RClass *c;
+ struct RClass *cls;
mrb_callinfo *ci = mrb->c->ci;
mrb_value recv, blk;
mrb_sym mid = ci->mid;
struct RClass* target_class = MRB_PROC_TARGET_CLASS(ci->proc);
- mrb_assert(bidx < ci->nregs);
+ mrb_assert(bidx < irep->nregs);
if (mid == 0 || !target_class) {
mrb_value exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method");
@@ -1631,26 +1588,26 @@ RETRY_TRY_BLOCK:
regs[bidx] = blk;
ci = mrb->c->ci;
}
- c = target_class->super;
- m = mrb_method_search_vm(mrb, &c, mid);
+ cls = target_class->super;
+ m = mrb_method_search_vm(mrb, &cls, mid);
if (MRB_METHOD_UNDEF_P(m)) {
mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
if (mid != missing) {
- c = mrb_class(mrb, recv);
+ cls = mrb_class(mrb, recv);
}
- m = mrb_method_search_vm(mrb, &c, missing);
+ m = mrb_method_search_vm(mrb, &cls, missing);
if (MRB_METHOD_UNDEF_P(m)) {
- mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, n, regs+a+1);
- ERR_PC_SET(mrb, pc);
+ mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, b, regs+a+1);
+ ERR_PC_SET(mrb);
mrb_method_missing(mrb, mid, recv, args);
}
mid = missing;
if (argc >= 0) {
- if (a+2 >= ci->nregs) {
- stack_extend(mrb, a+3);
+ if (a+2 >= irep->nregs) {
+ mrb_stack_extend(mrb, a+3);
}
- regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1);
+ regs[a+1] = mrb_ary_new_from_values(mrb, b, regs+a+1);
regs[a+2] = blk;
argc = -1;
}
@@ -1661,8 +1618,8 @@ RETRY_TRY_BLOCK:
ci = cipush(mrb);
ci->mid = mid;
ci->stackent = mrb->c->stack;
- ci->target_class = c;
- ci->pc = pc + 1;
+ ci->target_class = cls;
+ ci->pc = pc;
ci->argc = argc;
/* prepare stack */
@@ -1671,7 +1628,7 @@ RETRY_TRY_BLOCK:
if (MRB_METHOD_CFUNC_P(m)) {
mrb_value v;
- ci->nregs = (argc < 0) ? 3 : n+2;
+
if (MRB_METHOD_PROC_P(m)) {
ci->proc = MRB_METHOD_PROC(m);
}
@@ -1708,21 +1665,18 @@ RETRY_TRY_BLOCK:
irep = proc->body.irep;
pool = irep->pool;
syms = irep->syms;
- ci->nregs = irep->nregs;
- stack_extend(mrb, (argc < 0 && ci->nregs < 3) ? 3 : ci->nregs);
+ mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : irep->nregs);
pc = irep->iseq;
JUMP;
}
}
- CASE(OP_ARGARY) {
- /* A Bx R(A) := argument array (16=6:1:5:4) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- int m1 = (bx>>10)&0x3f;
- int r = (bx>>9)&0x1;
- int m2 = (bx>>4)&0x1f;
- int lv = (bx>>0)&0xf;
+ CASE(OP_ARGARY, BS) {
+ int m1 = (b>>11)&0x3f;
+ int r = (b>>10)&0x1;
+ int m2 = (b>>5)&0x1f;
+ int kd = (b>>4)&0x1;
+ int lv = (b>>0)&0xf;
mrb_value *stack;
if (mrb->c->ci->mid == 0 || mrb->c->ci->target_class == NULL) {
@@ -1737,12 +1691,12 @@ RETRY_TRY_BLOCK:
else {
struct REnv *e = uvenv(mrb, lv-1);
if (!e) goto L_NOSUPER;
- if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+1)
+ if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+kd+1)
goto L_NOSUPER;
stack = e->stack + 1;
}
if (r == 0) {
- regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack);
+ regs[a] = mrb_ary_new_from_values(mrb, m1+m2+kd, stack);
}
else {
mrb_value *pp = NULL;
@@ -1755,7 +1709,7 @@ RETRY_TRY_BLOCK:
pp = ARY_PTR(ary);
len = (int)ARY_LEN(ary);
}
- regs[a] = mrb_ary_new_capa(mrb, m1+len+m2);
+ regs[a] = mrb_ary_new_capa(mrb, m1+len+m2+kd);
rest = mrb_ary_ptr(regs[a]);
if (m1 > 0) {
stack_copy(ARY_PTR(rest), stack, m1);
@@ -1766,89 +1720,126 @@ RETRY_TRY_BLOCK:
if (m2 > 0) {
stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2);
}
- ARY_SET_LEN(rest, m1+len+m2);
+ if (kd) {
+ stack_copy(ARY_PTR(rest)+m1+len+m2, stack+m1+m2+1, kd);
+ }
+ ARY_SET_LEN(rest, m1+len+m2+kd);
}
regs[a+1] = stack[m1+r+m2];
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_ENTER) {
- /* 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 = MRB_ASPEC_REQ(ax);
- int o = MRB_ASPEC_OPT(ax);
- int r = MRB_ASPEC_REST(ax);
- int m2 = MRB_ASPEC_POST(ax);
+ CASE(OP_ENTER, W) {
+ int m1 = MRB_ASPEC_REQ(a);
+ int o = MRB_ASPEC_OPT(a);
+ int r = MRB_ASPEC_REST(a);
+ int m2 = MRB_ASPEC_POST(a);
+ int kd = (MRB_ASPEC_KEY(a) > 0 || MRB_ASPEC_KDICT(a))? 1 : 0;
/* unused
- int k = MRB_ASPEC_KEY(ax);
- int kd = MRB_ASPEC_KDICT(ax);
- int b = MRB_ASPEC_BLOCK(ax);
+ int b = MRB_ASPEC_BLOCK(a);
*/
int argc = mrb->c->ci->argc;
mrb_value *argv = regs+1;
- mrb_value *argv0 = argv;
- int len = m1 + o + r + m2;
+ mrb_value * const argv0 = argv;
+ int const len = m1 + o + r + m2;
+ int const blk_pos = len + kd + 1;
mrb_value *blk = &argv[argc < 0 ? 1 : argc];
+ mrb_value kdict;
+ int kargs = kd;
+ /* arguments is passed with Array */
if (argc < 0) {
struct RArray *ary = mrb_ary_ptr(regs[1]);
argv = ARY_PTR(ary);
argc = (int)ARY_LEN(ary);
mrb_gc_protect(mrb, regs[1]);
}
+
+ /* strict argument check */
if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) {
- if (argc >= 0) {
- if (argc < m1 + m2 || (r == 0 && argc > len)) {
- argnum_error(mrb, m1+m2);
- goto L_RAISE;
- }
+ if (argc < m1 + m2 || (r == 0 && argc > len + kd)) {
+ argnum_error(mrb, m1+m2);
+ goto L_RAISE;
}
}
+ /* extract first argument array to arguments */
else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) {
mrb_gc_protect(mrb, argv[0]);
argc = (int)RARRAY_LEN(argv[0]);
argv = RARRAY_PTR(argv[0]);
}
- if (argc < len) {
+
+ if (kd) {
+ /* check last arguments is hash if method takes keyword arguments */
+ if (argc == m1+m2) {
+ kdict = mrb_hash_new(mrb);
+ kargs = 0;
+ }
+ else {
+ if (argv && argc > 0 && mrb_hash_p(argv[argc-1])) {
+ kdict = argv[argc-1];
+ mrb_hash_check_kdict(mrb, kdict);
+ }
+ else if (r || argc <= m1+m2+o
+ || !(mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc))) {
+ kdict = mrb_hash_new(mrb);
+ kargs = 0;
+ }
+ else {
+ argnum_error(mrb, m1+m2);
+ goto L_RAISE;
+ }
+ if (MRB_ASPEC_KEY(a) > 0) {
+ kdict = mrb_hash_dup(mrb, kdict);
+ }
+ }
+ }
+
+ /* no rest arguments */
+ if (argc-kargs < len) {
int mlen = m2;
if (argc < m1+m2) {
- if (m1 < argc)
- mlen = argc - m1;
- else
- mlen = 0;
+ mlen = m1 < argc ? argc - m1 : 0;
}
- regs[len+1] = *blk; /* move block */
- SET_NIL_VALUE(regs[argc+1]);
+ regs[blk_pos] = *blk; /* move block */
+ if (kd) regs[len + 1] = kdict;
+
+ /* copy mandatory and optional arguments */
if (argv0 != argv) {
value_move(&regs[1], argv, argc-mlen); /* m1 + o */
}
if (argc < m1) {
stack_clear(&regs[argc+1], m1-argc);
}
+ /* copy post mandatory arguments */
if (mlen) {
value_move(&regs[len-m2+1], &argv[argc-mlen], mlen);
}
if (mlen < m2) {
stack_clear(&regs[len-m2+mlen+1], m2-mlen);
}
+ /* initalize rest arguments with empty Array */
if (r) {
regs[m1+o+1] = mrb_ary_new_capa(mrb, 0);
}
- if (o == 0 || argc < m1+m2) pc++;
- else
- pc += argc - m1 - m2 + 1;
+ /* skip initailizer of passed arguments */
+ if (o > 0 && argc-kargs > m1+m2)
+ pc += (argc - kargs - m1 - m2)*3;
}
else {
int rnum = 0;
if (argv0 != argv) {
- regs[len+1] = *blk; /* move block */
+ regs[blk_pos] = *blk; /* move block */
+ if (kd) regs[len + 1] = kdict;
value_move(&regs[1], argv, m1+o);
}
if (r) {
- rnum = argc-m1-o-m2;
- regs[m1+o+1] = mrb_ary_new_from_values(mrb, rnum, argv+m1+o);
+ mrb_value ary;
+
+ rnum = argc-m1-o-m2-kargs;
+ ary = mrb_ary_new_from_values(mrb, rnum, argv+m1+o);
+ regs[m1+o+1] = ary;
}
if (m2) {
if (argc-m2 > m1) {
@@ -1856,36 +1847,74 @@ RETRY_TRY_BLOCK:
}
}
if (argv0 == argv) {
- regs[len+1] = *blk; /* move block */
+ regs[blk_pos] = *blk; /* move block */
+ if (kd) regs[len + 1] = kdict;
}
- pc += o + 1;
+ pc += o*3;
}
- mrb->c->ci->argc = len;
+
+ /* format arguments for generated code */
+ mrb->c->ci->argc = len + kd;
+
/* clear local (but non-argument) variables */
- if (irep->nlocals-len-2 > 0) {
- stack_clear(&regs[len+2], irep->nlocals-len-2);
+ if (irep->nlocals-blk_pos-1 > 0) {
+ stack_clear(&regs[blk_pos+1], irep->nlocals-blk_pos-1);
}
JUMP;
}
- CASE(OP_KARG) {
- /* A B C R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B)) */
- /* if C == 2; raise unless kdict.empty? */
- /* OP_JMP should follow to skip init code */
+ CASE(OP_KARG, BB) {
+ mrb_value k = mrb_symbol_value(syms[b]);
+ mrb_value kdict = regs[mrb->c->ci->argc];
+
+ if (!mrb_hash_p(kdict) || !mrb_hash_key_p(mrb, kdict, k)) {
+ mrb_value str = mrb_format(mrb, "missing keyword: %S", k);
+ mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
+ goto L_RAISE;
+ }
+ regs[a] = mrb_hash_get(mrb, kdict, k);
+ mrb_hash_delete_key(mrb, kdict, k);
NEXT;
}
- CASE(OP_KDICT) {
- /* A C R(A) := kdict */
+ CASE(OP_KEY_P, BB) {
+ mrb_value k = mrb_symbol_value(syms[b]);
+ mrb_value kdict = regs[mrb->c->ci->argc];
+ mrb_bool key_p = FALSE;
+
+ if (mrb_hash_p(kdict)) {
+ key_p = mrb_hash_key_p(mrb, kdict, k);
+ }
+ regs[a] = mrb_bool_value(key_p);
NEXT;
}
+ CASE(OP_KEYEND, Z) {
+ mrb_value kdict = regs[mrb->c->ci->argc];
+
+ if (mrb_hash_p(kdict) && !mrb_hash_empty_p(mrb, kdict)) {
+ mrb_value keys = mrb_hash_keys(mrb, kdict);
+ mrb_value key1 = RARRAY_PTR(keys)[0];
+ mrb_value str = mrb_format(mrb, "unknown keyword: %S", key1);
+ mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str));
+ goto L_RAISE;
+ }
+ NEXT;
+ }
+
+ CASE(OP_BREAK, B) {
+ c = OP_R_BREAK;
+ goto L_RETURN;
+ }
+ CASE(OP_RETURN_BLK, B) {
+ c = OP_R_RETURN;
+ goto L_RETURN;
+ }
+ CASE(OP_RETURN, B)
+ c = OP_R_NORMAL;
L_RETURN:
- i = MKOP_AB(OP_RETURN, GETARG_A(i), OP_R_NORMAL);
- /* fall through */
- CASE(OP_RETURN) {
- /* A B return R(A) (B=normal,in-block return/break) */
- mrb_callinfo *ci;
+ {
+ mrb_callinfo *ci;
#define ecall_adjust() do {\
ptrdiff_t cioff = ci - mrb->c->cibase;\
@@ -1943,7 +1972,7 @@ RETRY_TRY_BLOCK:
while (c->eidx > ci->epos) {
ecall_adjust();
}
- mrb->c->status = MRB_FIBER_TERMINATED;
+ c->status = MRB_FIBER_TERMINATED;
mrb->c = c->prev;
c->prev = NULL;
goto L_RAISE;
@@ -1967,8 +1996,8 @@ RETRY_TRY_BLOCK:
if (ci < ci0) {
mrb->c->stack = ci[1].stackent;
}
- stack_extend(mrb, irep->nregs);
- pc = mrb->c->rescue[--ci->ridx];
+ mrb_stack_extend(mrb, irep->nregs);
+ pc = irep->iseq+mrb->c->rescue[--ci->ridx];
}
else {
int acc;
@@ -1976,9 +2005,9 @@ RETRY_TRY_BLOCK:
struct RProc *dst;
ci = mrb->c->ci;
- v = regs[GETARG_A(i)];
+ v = regs[a];
mrb_gc_protect(mrb, v);
- switch (GETARG_B(i)) {
+ switch (c) {
case OP_R_RETURN:
/* Fall through to OP_R_NORMAL otherwise */
if (ci->acc >=0 && MRB_PROC_ENV_P(proc) && !MRB_PROC_STRICT_P(proc)) {
@@ -2010,22 +2039,21 @@ RETRY_TRY_BLOCK:
case OP_R_NORMAL:
NORMAL_RETURN:
if (ci == mrb->c->cibase) {
- struct mrb_context *c;
+ struct mrb_context *c = mrb->c;
- if (!mrb->c->prev) { /* toplevel return */
- localjump_error(mrb, LOCALJUMP_ERROR_RETURN);
- goto L_RAISE;
+ if (!c->prev) { /* toplevel return */
+ regs[irep->nlocals] = v;
+ goto L_STOP;
}
- if (mrb->c->prev->ci == mrb->c->prev->cibase) {
+ if (c->prev->ci == c->prev->cibase) {
mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume");
mrb_exc_set(mrb, exc);
goto L_RAISE;
}
- while (mrb->c->eidx > 0) {
+ while (c->eidx > 0) {
ecall(mrb);
}
/* automatic yield at the end */
- c = mrb->c;
c->status = MRB_FIBER_TERMINATED;
mrb->c = c->prev;
c->prev = NULL;
@@ -2035,7 +2063,7 @@ RETRY_TRY_BLOCK:
break;
case OP_R_BREAK:
if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN;
- if (MRB_PROC_ORPHAN_P(proc)) {
+ if (MRB_PROC_ORPHAN_P(proc)) {
mrb_value exc;
L_BREAK_ERROR:
@@ -2133,91 +2161,12 @@ RETRY_TRY_BLOCK:
JUMP;
}
- CASE(OP_TAILCALL) {
- /* A B C return call(R(A),Syms(B),R(A+1),... ,R(A+C+1)) */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
- int n = GETARG_C(i);
- mrb_method_t m;
- struct RClass *c;
- mrb_callinfo *ci;
- mrb_value recv;
- mrb_sym mid = syms[b];
-
- recv = regs[a];
- c = mrb_class(mrb, recv);
- m = mrb_method_search_vm(mrb, &c, mid);
- if (MRB_METHOD_UNDEF_P(m)) {
- mrb_value sym = mrb_symbol_value(mid);
- mrb_sym missing = mrb_intern_lit(mrb, "method_missing");
- m = mrb_method_search_vm(mrb, &c, missing);
- if (MRB_METHOD_UNDEF_P(m)) {
- mrb_value args;
-
- if (n == CALL_MAXARGS) {
- args = regs[a+1];
- }
- else {
- args = mrb_ary_new_from_values(mrb, n, regs+a+1);
- }
- ERR_PC_SET(mrb, pc);
- mrb_method_missing(mrb, mid, recv, args);
- }
- mid = missing;
- if (n == CALL_MAXARGS) {
- mrb_ary_unshift(mrb, regs[a+1], sym);
- }
- else {
- value_move(regs+a+2, regs+a+1, ++n);
- regs[a+1] = sym;
- }
- }
-
- /* replace callinfo */
- ci = mrb->c->ci;
- ci->mid = mid;
- ci->target_class = c;
- if (n == CALL_MAXARGS) {
- ci->argc = -1;
- }
- else {
- ci->argc = n;
- }
-
- /* move stack */
- value_move(mrb->c->stack, &regs[a], ci->argc+1);
-
- if (MRB_METHOD_CFUNC_P(m)) {
- mrb_value v = MRB_METHOD_CFUNC(m)(mrb, recv);
- mrb->c->stack[0] = v;
- mrb_gc_arena_restore(mrb, ai);
- goto L_RETURN;
- }
- else {
- /* setup environment for calling method */
- struct RProc *p = MRB_METHOD_PROC(m);
- irep = p->body.irep;
- pool = irep->pool;
- syms = irep->syms;
- if (ci->argc < 0) {
- stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs);
- }
- else {
- stack_extend(mrb, irep->nregs);
- }
- pc = irep->iseq;
- }
- JUMP;
- }
-
- CASE(OP_BLKPUSH) {
- /* A Bx R(A) := block (16=6:1:5:4) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
- int m1 = (bx>>10)&0x3f;
- int r = (bx>>9)&0x1;
- int m2 = (bx>>4)&0x1f;
- int lv = (bx>>0)&0xf;
+ CASE(OP_BLKPUSH, BS) {
+ int m1 = (b>>11)&0x3f;
+ int r = (b>>10)&0x1;
+ int m2 = (b>>5)&0x1f;
+ int kd = (b>>4)&0x1;
+ int lv = (b>>0)&0xf;
mrb_value *stack;
if (lv == 0) stack = regs + 1;
@@ -2234,197 +2183,75 @@ RETRY_TRY_BLOCK:
localjump_error(mrb, LOCALJUMP_ERROR_YIELD);
goto L_RAISE;
}
- regs[a] = stack[m1+r+m2];
+ regs[a] = stack[m1+r+m2+kd];
NEXT;
}
#define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff))
-#define OP_MATH_BODY(op,v1,v2) do {\
- v1(regs[a]) = v1(regs[a]) op v2(regs[a+1]);\
-} while(0)
-
- CASE(OP_ADD) {
- /* A B C R(A) := R(A)+R(A+1) (Syms[B]=:+,C=1)*/
- int a = GETARG_A(i);
-
- /* need to check if op is overridden */
- switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
- {
- mrb_int x, y, z;
- mrb_value *regs_a = regs + a;
-
- x = mrb_fixnum(regs_a[0]);
- y = mrb_fixnum(regs_a[1]);
- if (mrb_int_add_overflow(x, y, &z)) {
-#ifndef MRB_WITHOUT_FLOAT
- SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y);
- break;
-#endif
- }
- SET_INT_VALUE(regs[a], z);
- }
- break;
-#ifndef MRB_WITHOUT_FLOAT
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
- {
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + y);
- }
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x + y);
- }
-#else
- OP_MATH_BODY(+,mrb_float,mrb_fixnum);
-#endif
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x + y);
- }
+#define OP_MATH(op_name) \
+ /* need to check if op is overridden */ \
+ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { \
+ OP_MATH_CASE_FIXNUM(op_name); \
+ OP_MATH_CASE_FLOAT(op_name, fixnum, float); \
+ OP_MATH_CASE_FLOAT(op_name, float, fixnum); \
+ OP_MATH_CASE_FLOAT(op_name, float, float); \
+ OP_MATH_CASE_STRING_##op_name(); \
+ default: \
+ c = 1; \
+ mid = mrb_intern_lit(mrb, MRB_STRINGIZE(OP_MATH_OP_##op_name)); \
+ goto L_SEND_SYM; \
+ } \
+ NEXT;
+#define OP_MATH_CASE_FIXNUM(op_name) \
+ case TYPES2(MRB_TT_FIXNUM, MRB_TT_FIXNUM): \
+ { \
+ mrb_int x = mrb_fixnum(regs[a]), y = mrb_fixnum(regs[a+1]), z; \
+ if (mrb_int_##op_name##_overflow(x, y, &z)) \
+ OP_MATH_OVERFLOW_INT(op_name, x, y, z); \
+ else \
+ SET_INT_VALUE(regs[a], z); \
+ } \
+ break
+#ifdef MRB_WITHOUT_FLOAT
+#define OP_MATH_CASE_FLOAT(op_name, t1, t2) (void)0
+#define OP_MATH_OVERFLOW_INT(op_name, x, y, z) SET_INT_VALUE(regs[a], z)
#else
- OP_MATH_BODY(+,mrb_float,mrb_float);
+#define OP_MATH_CASE_FLOAT(op_name, t1, t2) \
+ case TYPES2(OP_MATH_TT_##t1, OP_MATH_TT_##t2): \
+ { \
+ mrb_float z = mrb_##t1(regs[a]) OP_MATH_OP_##op_name mrb_##t2(regs[a+1]); \
+ SET_FLOAT_VALUE(mrb, regs[a], z); \
+ } \
+ break
+#define OP_MATH_OVERFLOW_INT(op_name, x, y, z) \
+ SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x OP_MATH_OP_##op_name (mrb_float)y)
#endif
- break;
-#endif
- case TYPES2(MRB_TT_STRING,MRB_TT_STRING):
- regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]);
- break;
- default:
- goto L_SEND;
- }
- mrb_gc_arena_restore(mrb, ai);
- NEXT;
- }
-
- CASE(OP_SUB) {
- /* A B C R(A) := R(A)-R(A+1) (Syms[B]=:-,C=1)*/
- int a = GETARG_A(i);
-
- /* need to check if op is overridden */
- switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
- {
- mrb_int x, y, z;
+#define OP_MATH_CASE_STRING_add() \
+ case TYPES2(MRB_TT_STRING, MRB_TT_STRING): \
+ regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]); \
+ mrb_gc_arena_restore(mrb, ai); \
+ break
+#define OP_MATH_CASE_STRING_sub() (void)0
+#define OP_MATH_CASE_STRING_mul() (void)0
+#define OP_MATH_OP_add +
+#define OP_MATH_OP_sub -
+#define OP_MATH_OP_mul *
+#define OP_MATH_TT_fixnum MRB_TT_FIXNUM
+#define OP_MATH_TT_float MRB_TT_FLOAT
- x = mrb_fixnum(regs[a]);
- y = mrb_fixnum(regs[a+1]);
- if (mrb_int_sub_overflow(x, y, &z)) {
-#ifndef MRB_WITHOUT_FLOAT
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y);
- break;
-#endif
- }
- SET_INT_VALUE(regs[a], z);
- }
- break;
-#ifndef MRB_WITHOUT_FLOAT
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
- {
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - y);
- }
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x - y);
- }
-#else
- OP_MATH_BODY(-,mrb_float,mrb_fixnum);
-#endif
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x - y);
- }
-#else
- OP_MATH_BODY(-,mrb_float,mrb_float);
-#endif
- break;
-#endif
- default:
- goto L_SEND;
- }
- NEXT;
+ CASE(OP_ADD, B) {
+ OP_MATH(add);
}
- CASE(OP_MUL) {
- /* A B C R(A) := R(A)*R(A+1) (Syms[B]=:*,C=1)*/
- int a = GETARG_A(i);
-
- /* need to check if op is overridden */
- switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):
- {
- mrb_int x, y, z;
+ CASE(OP_SUB, B) {
+ OP_MATH(sub);
+ }
- x = mrb_fixnum(regs[a]);
- y = mrb_fixnum(regs[a+1]);
- if (mrb_int_mul_overflow(x, y, &z)) {
-#ifndef MRB_WITHOUT_FLOAT
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * (mrb_float)y);
- break;
-#endif
- }
- SET_INT_VALUE(regs[a], z);
- }
- break;
-#ifndef MRB_WITHOUT_FLOAT
- case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):
- {
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * y);
- }
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_int y = mrb_fixnum(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x * y);
- }
-#else
- OP_MATH_BODY(*,mrb_float,mrb_fixnum);
-#endif
- break;
- case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- mrb_float y = mrb_float(regs[a+1]);
- SET_FLOAT_VALUE(mrb, regs[a], x * y);
- }
-#else
- OP_MATH_BODY(*,mrb_float,mrb_float);
-#endif
- break;
-#endif
- default:
- goto L_SEND;
- }
- NEXT;
+ CASE(OP_MUL, B) {
+ OP_MATH(mul);
}
- CASE(OP_DIV) {
- /* A B C R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1)*/
- int a = GETARG_A(i);
+ CASE(OP_DIV, B) {
#ifndef MRB_WITHOUT_FLOAT
double x, y, f;
#endif
@@ -2457,7 +2284,9 @@ RETRY_TRY_BLOCK:
break;
#endif
default:
- goto L_SEND;
+ c = 1;
+ mid = mrb_intern_lit(mrb, "/");
+ goto L_SEND_SYM;
}
#ifndef MRB_WITHOUT_FLOAT
@@ -2474,87 +2303,46 @@ RETRY_TRY_BLOCK:
NEXT;
}
- CASE(OP_ADDI) {
- /* A B C R(A) := R(A)+C (Syms[B]=:+)*/
- int a = GETARG_A(i);
-
- /* need to check if + is overridden */
- switch (mrb_type(regs[a])) {
- case MRB_TT_FIXNUM:
- {
- mrb_int x = mrb_fixnum(regs[a]);
- mrb_int y = GETARG_C(i);
- mrb_int z;
-
- if (mrb_int_add_overflow(x, y, &z)) {
-#ifndef MRB_WITHOUT_FLOAT
- SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y);
- break;
-#endif
- }
- SET_INT_VALUE(regs[a], z);
- }
- break;
-#ifndef MRB_WITHOUT_FLOAT
- case MRB_TT_FLOAT:
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- SET_FLOAT_VALUE(mrb, regs[a], x + GETARG_C(i));
- }
+#define OP_MATHI(op_name) \
+ /* need to check if op is overridden */ \
+ switch (mrb_type(regs[a])) { \
+ OP_MATHI_CASE_FIXNUM(op_name); \
+ OP_MATHI_CASE_FLOAT(op_name); \
+ default: \
+ SET_INT_VALUE(regs[a+1], b); \
+ c = 1; \
+ mid = mrb_intern_lit(mrb, MRB_STRINGIZE(OP_MATH_OP_##op_name)); \
+ goto L_SEND_SYM; \
+ } \
+ NEXT;
+#define OP_MATHI_CASE_FIXNUM(op_name) \
+ case MRB_TT_FIXNUM: \
+ { \
+ mrb_int x = mrb_fixnum(regs[a]), y = (mrb_int)b, z; \
+ if (mrb_int_##op_name##_overflow(x, y, &z)) \
+ OP_MATH_OVERFLOW_INT(op_name, x, y, z); \
+ else \
+ SET_INT_VALUE(regs[a], z); \
+ } \
+ break
+#ifdef MRB_WITHOUT_FLOAT
+#define OP_MATHI_CASE_FLOAT(op_name) (void)0
#else
- mrb_float(regs[a]) += GETARG_C(i);
-#endif
- break;
+#define OP_MATHI_CASE_FLOAT(op_name) \
+ case MRB_TT_FLOAT: \
+ { \
+ mrb_float z = mrb_float(regs[a]) OP_MATH_OP_##op_name b; \
+ SET_FLOAT_VALUE(mrb, regs[a], z); \
+ } \
+ break
#endif
- default:
- SET_INT_VALUE(regs[a+1], GETARG_C(i));
- i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
- goto L_SEND;
- }
- NEXT;
- }
-
- CASE(OP_SUBI) {
- /* A B C R(A) := R(A)-C (Syms[B]=:-)*/
- int a = GETARG_A(i);
- mrb_value *regs_a = regs + a;
- /* need to check if + is overridden */
- switch (mrb_type(regs_a[0])) {
- case MRB_TT_FIXNUM:
- {
- mrb_int x = mrb_fixnum(regs_a[0]);
- mrb_int y = GETARG_C(i);
- mrb_int z;
+ CASE(OP_ADDI, BB) {
+ OP_MATHI(add);
+ }
- if (mrb_int_sub_overflow(x, y, &z)) {
-#ifndef MRB_WITHOUT_FLOAT
- SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y);
- break;
-#endif
- }
- SET_INT_VALUE(regs_a[0], z);
- }
- break;
-#ifndef MRB_WITHOUT_FLOAT
- case MRB_TT_FLOAT:
-#ifdef MRB_WORD_BOXING
- {
- mrb_float x = mrb_float(regs[a]);
- SET_FLOAT_VALUE(mrb, regs[a], x - GETARG_C(i));
- }
-#else
- mrb_float(regs_a[0]) -= GETARG_C(i);
-#endif
- break;
-#endif
- default:
- SET_INT_VALUE(regs_a[1], GETARG_C(i));
- i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1);
- goto L_SEND;
- }
- NEXT;
+ CASE(OP_SUBI, BB) {
+ OP_MATHI(sub);
}
#define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1]))
@@ -2568,7 +2356,9 @@ RETRY_TRY_BLOCK:
result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\
break;\
default:\
- goto L_SEND;\
+ c = 1;\
+ mid = mrb_intern_lit(mrb, # op);\
+ goto L_SEND_SYM;\
}\
if (result) {\
SET_TRUE_VALUE(regs[a]);\
@@ -2595,7 +2385,9 @@ RETRY_TRY_BLOCK:
result = OP_CMP_BODY(op,mrb_float,mrb_float);\
break;\
default:\
- goto L_SEND;\
+ c = 1;\
+ mid = mrb_intern_lit(mrb, # op);\
+ goto L_SEND_SYM;\
}\
if (result) {\
SET_TRUE_VALUE(regs[a]);\
@@ -2606,9 +2398,7 @@ RETRY_TRY_BLOCK:
} while(0)
#endif
- CASE(OP_EQ) {
- /* A B C R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1)*/
- int a = GETARG_A(i);
+ CASE(OP_EQ, B) {
if (mrb_obj_eq(mrb, regs[a], regs[a+1])) {
SET_TRUE_VALUE(regs[a]);
}
@@ -2618,68 +2408,64 @@ RETRY_TRY_BLOCK:
NEXT;
}
- CASE(OP_LT) {
- /* A B C R(A) := R(A)<R(A+1) (Syms[B]=:<,C=1)*/
- int a = GETARG_A(i);
+ CASE(OP_LT, B) {
OP_CMP(<);
NEXT;
}
- CASE(OP_LE) {
- /* A B C R(A) := R(A)<=R(A+1) (Syms[B]=:<=,C=1)*/
- int a = GETARG_A(i);
+ CASE(OP_LE, B) {
OP_CMP(<=);
NEXT;
}
- CASE(OP_GT) {
- /* A B C R(A) := R(A)>R(A+1) (Syms[B]=:>,C=1)*/
- int a = GETARG_A(i);
+ CASE(OP_GT, B) {
OP_CMP(>);
NEXT;
}
- CASE(OP_GE) {
- /* A B C R(A) := R(A)>=R(A+1) (Syms[B]=:>=,C=1)*/
- int a = GETARG_A(i);
+ CASE(OP_GE, B) {
OP_CMP(>=);
NEXT;
}
- CASE(OP_ARRAY) {
- /* A B C R(A) := ary_new(R(B),R(B+1)..R(B+C)) */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
- int c = GETARG_C(i);
+ CASE(OP_ARRAY, BB) {
+ mrb_value v = mrb_ary_new_from_values(mrb, b, &regs[a]);
+ regs[a] = v;
+ mrb_gc_arena_restore(mrb, ai);
+ NEXT;
+ }
+ CASE(OP_ARRAY2, BBB) {
mrb_value v = mrb_ary_new_from_values(mrb, c, &regs[b]);
regs[a] = v;
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_ARYCAT) {
- /* A B mrb_ary_concat(R(A),R(B)) */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
- mrb_value splat = mrb_ary_splat(mrb, regs[b]);
+ CASE(OP_ARYCAT, B) {
+ mrb_value splat = mrb_ary_splat(mrb, regs[a+1]);
mrb_ary_concat(mrb, regs[a], splat);
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_ARYPUSH) {
- /* A B R(A).push(R(B)) */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
- mrb_ary_push(mrb, regs[a], regs[b]);
+ CASE(OP_ARYPUSH, B) {
+ mrb_ary_push(mrb, regs[a], regs[a+1]);
NEXT;
}
- CASE(OP_AREF) {
- /* A B C R(A) := R(B)[C] */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
- int c = GETARG_C(i);
+ CASE(OP_ARYDUP, B) {
+ mrb_value ary = regs[a];
+ if (mrb_array_p(ary)) {
+ ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary));
+ }
+ else {
+ ary = mrb_ary_new_from_values(mrb, 1, &ary);
+ }
+ regs[a] = ary;
+ NEXT;
+ }
+
+ CASE(OP_AREF, BBB) {
mrb_value v = regs[b];
if (!mrb_array_p(v)) {
@@ -2697,21 +2483,15 @@ RETRY_TRY_BLOCK:
NEXT;
}
- CASE(OP_ASET) {
- /* A B C R(B)[C] := R(A) */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
- int c = GETARG_C(i);
+ CASE(OP_ASET, BBB) {
mrb_ary_set(mrb, regs[b], c, regs[a]);
NEXT;
}
- CASE(OP_APOST) {
- /* A B C *R(A),R(A+1)..R(A+C) := R(A)[B..] */
- int a = GETARG_A(i);
+ CASE(OP_APOST, BBB) {
mrb_value v = regs[a];
- int pre = GETARG_B(i);
- int post = GETARG_C(i);
+ int pre = b;
+ int post = c;
struct RArray *ary;
int len, idx;
@@ -2742,48 +2522,65 @@ RETRY_TRY_BLOCK:
NEXT;
}
- CASE(OP_STRING) {
- /* A Bx R(A) := str_new(Lit(Bx)) */
- mrb_int a = GETARG_A(i);
- mrb_int bx = GETARG_Bx(i);
- mrb_value str = mrb_str_dup(mrb, pool[bx]);
+ CASE(OP_INTERN, B) {
+ mrb_sym sym = mrb_intern_str(mrb, regs[a]);
+
+ regs[a] = mrb_symbol_value(sym);
+ mrb_gc_arena_restore(mrb, ai);
+ NEXT;
+ }
+
+ CASE(OP_STRING, BB) {
+ mrb_value str = mrb_str_dup(mrb, pool[b]);
regs[a] = str;
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_STRCAT) {
- /* A B R(A).concat(R(B)) */
- mrb_int a = GETARG_A(i);
- mrb_int b = GETARG_B(i);
+ CASE(OP_STRCAT, B) {
+ mrb_str_concat(mrb, regs[a], regs[a+1]);
+ NEXT;
+ }
+
+ CASE(OP_HASH, BB) {
+ mrb_value hash = mrb_hash_new_capa(mrb, b);
+ int i;
+ int lim = a+b*2;
- mrb_str_concat(mrb, regs[a], regs[b]);
+ for (i=a; i<lim; i+=2) {
+ mrb_hash_set(mrb, hash, regs[i], regs[i+1]);
+ }
+ regs[a] = hash;
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_HASH) {
- /* A B C R(A) := hash_new(R(B),R(B+1)..R(B+C)) */
- int b = GETARG_B(i);
- int c = GETARG_C(i);
- int lim = b+c*2;
- mrb_value hash = mrb_hash_new_capa(mrb, c);
+ CASE(OP_HASHADD, BB) {
+ mrb_value hash;
+ int i;
+ int lim = a+b*2+1;
- while (b < lim) {
- mrb_hash_set(mrb, hash, regs[b], regs[b+1]);
- b+=2;
+ hash = mrb_ensure_hash_type(mrb, regs[a]);
+ for (i=a+1; i<lim; i+=2) {
+ mrb_hash_set(mrb, hash, regs[i], regs[i+1]);
}
- regs[GETARG_A(i)] = hash;
+ mrb_gc_arena_restore(mrb, ai);
+ NEXT;
+ }
+ CASE(OP_HASHCAT, B) {
+ mrb_value hash = mrb_ensure_hash_type(mrb, regs[a]);
+
+ mrb_hash_merge(mrb, hash, regs[a+1]);
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_LAMBDA) {
- /* A b c R(A) := lambda(SEQ[b],c) (b:c = 14:2) */
+ CASE(OP_LAMBDA, BB)
+ c = OP_L_LAMBDA;
+ L_MAKE_LAMBDA:
+ {
struct RProc *p;
- int a = GETARG_A(i);
- int b = GETARG_b(i);
- int c = GETARG_c(i);
mrb_irep *nirep = irep->reps[b];
if (c & OP_L_CAPTURE) {
@@ -2798,19 +2595,38 @@ RETRY_TRY_BLOCK:
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
+ CASE(OP_BLOCK, BB) {
+ c = OP_L_BLOCK;
+ goto L_MAKE_LAMBDA;
+ }
+ CASE(OP_METHOD, BB) {
+ c = OP_L_METHOD;
+ goto L_MAKE_LAMBDA;
+ }
+
+ CASE(OP_RANGE_INC, B) {
+ mrb_value val = mrb_range_new(mrb, regs[a], regs[a+1], FALSE);
+ regs[a] = val;
+ mrb_gc_arena_restore(mrb, ai);
+ NEXT;
+ }
- CASE(OP_OCLASS) {
- /* A R(A) := ::Object */
- regs[GETARG_A(i)] = mrb_obj_value(mrb->object_class);
+ CASE(OP_RANGE_EXC, B) {
+ mrb_value val = mrb_range_new(mrb, regs[a], regs[a+1], TRUE);
+ regs[a] = val;
+ mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_CLASS) {
- /* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */
+ CASE(OP_OCLASS, B) {
+ regs[a] = mrb_obj_value(mrb->object_class);
+ NEXT;
+ }
+
+ CASE(OP_CLASS, BB) {
struct RClass *c = 0, *baseclass;
- int a = GETARG_A(i);
mrb_value base, super;
- mrb_sym id = syms[GETARG_B(i)];
+ mrb_sym id = syms[b];
base = regs[a];
super = regs[a+1];
@@ -2824,32 +2640,27 @@ RETRY_TRY_BLOCK:
NEXT;
}
- CASE(OP_MODULE) {
- /* A B R(A) := newmodule(R(A),Syms(B)) */
- struct RClass *c = 0, *baseclass;
- int a = GETARG_A(i);
+ CASE(OP_MODULE, BB) {
+ struct RClass *cls = 0, *baseclass;
mrb_value base;
- mrb_sym id = syms[GETARG_B(i)];
+ mrb_sym id = syms[b];
base = regs[a];
if (mrb_nil_p(base)) {
baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc);
base = mrb_obj_value(baseclass);
}
- c = mrb_vm_define_module(mrb, base, id);
- regs[a] = mrb_obj_value(c);
+ cls = mrb_vm_define_module(mrb, base, id);
+ regs[a] = mrb_obj_value(cls);
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_EXEC) {
- /* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */
- int a = GETARG_A(i);
- int bx = GETARG_Bx(i);
+ CASE(OP_EXEC, BB) {
mrb_callinfo *ci;
mrb_value recv = regs[a];
struct RProc *p;
- mrb_irep *nirep = irep->reps[bx];
+ mrb_irep *nirep = irep->reps[b];
/* prepare closure */
p = mrb_proc_new(mrb, nirep);
@@ -2860,7 +2671,7 @@ RETRY_TRY_BLOCK:
/* prepare call stack */
ci = cipush(mrb);
- ci->pc = pc + 1;
+ ci->pc = pc;
ci->acc = a;
ci->mid = 0;
ci->stackent = mrb->c->stack;
@@ -2876,63 +2687,59 @@ RETRY_TRY_BLOCK:
irep = p->body.irep;
pool = irep->pool;
syms = irep->syms;
- ci->nregs = irep->nregs;
- stack_extend(mrb, ci->nregs);
- stack_clear(regs+1, ci->nregs-1);
+ mrb_stack_extend(mrb, irep->nregs);
+ stack_clear(regs+1, irep->nregs-1);
pc = irep->iseq;
JUMP;
}
- CASE(OP_METHOD) {
- /* A B R(A).newmethod(Syms(B),R(A+1)) */
- int a = GETARG_A(i);
- struct RClass *c = mrb_class_ptr(regs[a]);
+ CASE(OP_DEF, BB) {
+ struct RClass *target = mrb_class_ptr(regs[a]);
struct RProc *p = mrb_proc_ptr(regs[a+1]);
mrb_method_t m;
MRB_METHOD_FROM_PROC(m, p);
- mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], m);
+ mrb_define_method_raw(mrb, target, syms[b], m);
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_SCLASS) {
- /* A B R(A) := R(B).singleton_class */
- int a = GETARG_A(i);
- int b = GETARG_B(i);
-
- regs[a] = mrb_singleton_class(mrb, regs[b]);
+ CASE(OP_SCLASS, B) {
+ regs[a] = mrb_singleton_class(mrb, regs[a]);
mrb_gc_arena_restore(mrb, ai);
NEXT;
}
- CASE(OP_TCLASS) {
- /* A R(A) := target_class */
- if (!mrb->c->ci->target_class) {
- mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "no target class or module");
- mrb_exc_set(mrb, exc);
- goto L_RAISE;
- }
- regs[GETARG_A(i)] = mrb_obj_value(mrb->c->ci->target_class);
+ CASE(OP_TCLASS, B) {
+ if (!check_target_class(mrb)) goto L_RAISE;
+ regs[a] = mrb_obj_value(mrb->c->ci->target_class);
NEXT;
}
- CASE(OP_RANGE) {
- /* A B C R(A) := range_new(R(B),R(B+1),C) */
- int b = GETARG_B(i);
- mrb_value val = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i));
- regs[GETARG_A(i)] = val;
- mrb_gc_arena_restore(mrb, ai);
+ CASE(OP_ALIAS, BB) {
+ struct RClass *target;
+
+ if (!check_target_class(mrb)) goto L_RAISE;
+ target = mrb->c->ci->target_class;
+ mrb_alias_method(mrb, target, syms[a], syms[b]);
+ NEXT;
+ }
+ CASE(OP_UNDEF, B) {
+ struct RClass *target;
+
+ if (!check_target_class(mrb)) goto L_RAISE;
+ target = mrb->c->ci->target_class;
+ mrb_undef_method_id(mrb, target, syms[a]);
NEXT;
}
- CASE(OP_DEBUG) {
- /* A B C debug print R(A),R(B),R(C) */
+ CASE(OP_DEBUG, Z) {
+ FETCH_BBB();
#ifdef MRB_ENABLE_DEBUG_HOOK
mrb->debug_op_hook(mrb, irep, pc, regs);
#else
#ifndef MRB_DISABLE_STDIO
- printf("OP_DEBUG %d %d %d\n", GETARG_A(i), GETARG_B(i), GETARG_C(i));
+ printf("OP_DEBUG %d %d %d\n", a, b, c);
#else
abort();
#endif
@@ -2940,12 +2747,54 @@ RETRY_TRY_BLOCK:
NEXT;
}
- CASE(OP_STOP) {
+ CASE(OP_ERR, B) {
+ mrb_value msg = mrb_str_dup(mrb, pool[a]);
+ mrb_value exc;
+
+ exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
+ ERR_PC_SET(mrb);
+ mrb_exc_set(mrb, exc);
+ goto L_RAISE;
+ }
+
+ CASE(OP_EXT1, Z) {
+ insn = READ_B();
+ switch (insn) {
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _1(); goto L_OP_ ## insn ## _BODY;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ pc--;
+ NEXT;
+ }
+ CASE(OP_EXT2, Z) {
+ insn = READ_B();
+ switch (insn) {
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _2(); goto L_OP_ ## insn ## _BODY;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ pc--;
+ NEXT;
+ }
+ CASE(OP_EXT3, Z) {
+ uint8_t insn = READ_B();
+ switch (insn) {
+#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _3(); goto L_OP_ ## insn ## _BODY;
+#include "mruby/ops.h"
+#undef OPCODE
+ }
+ pc--;
+ NEXT;
+ }
+
+ CASE(OP_STOP, Z) {
/* stop VM */
L_STOP:
while (mrb->c->eidx > 0) {
ecall(mrb);
}
+ mrb->c->cibase->ridx = 0;
ERR_PC_CLR(mrb);
mrb->jmp = prev_jmp;
if (mrb->exc) {
@@ -2953,26 +2802,9 @@ RETRY_TRY_BLOCK:
}
return regs[irep->nlocals];
}
-
- CASE(OP_ERR) {
- /* Bx raise RuntimeError with message Lit(Bx) */
- mrb_value msg = mrb_str_dup(mrb, pool[GETARG_Bx(i)]);
- mrb_value exc;
-
- if (GETARG_A(i) == 0) {
- exc = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, msg);
- }
- else {
- exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg);
- }
- ERR_PC_SET(mrb, pc);
- mrb_exc_set(mrb, exc);
- goto L_RAISE;
- }
}
END_DISPATCH;
#undef regs
-
}
MRB_CATCH(&c_jmp) {
exc_catched = TRUE;
@@ -3005,12 +2837,11 @@ mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int sta
return mrb_vm_run(mrb, proc, self, stack_keep);
}
ci = cipush(mrb);
+ ci->stackent = mrb->c->stack;
ci->mid = 0;
- ci->nregs = 1; /* protect the receiver */
ci->acc = CI_ACC_SKIP;
ci->target_class = mrb->object_class;
v = mrb_vm_run(mrb, proc, self, stack_keep);
- cipop(mrb);
return v;
}
diff --git a/tasks/libmruby.rake b/tasks/libmruby.rake
index 540aa3eb5..ab5a15b4a 100644
--- a/tasks/libmruby.rake
+++ b/tasks/libmruby.rake
@@ -1,9 +1,10 @@
MRuby.each_target do
- file libfile("#{build_dir}/lib/libmruby") => libmruby.flatten do |t|
+ file libmruby_static => libmruby_objs.flatten do |t|
archiver.run t.name, t.prerequisites
end
- file "#{build_dir}/lib/libmruby.flags.mak" => [__FILE__, libfile("#{build_dir}/lib/libmruby")] do |t|
+ file "#{build_dir}/lib/libmruby.flags.mak" => [__FILE__, libmruby_static] do |t|
+ FileUtils.mkdir_p File.dirname t.name
open(t.name, 'w') do |f|
f.puts "MRUBY_CFLAGS = #{cc.all_flags}"
@@ -17,7 +18,7 @@ MRuby.each_target do
gem_libraries = gems.map { |g| g.linker.libraries }
f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries)}"
- f.puts "MRUBY_LIBMRUBY_PATH = #{libfile("#{build_dir}/lib/libmruby")}"
+ f.puts "MRUBY_LIBMRUBY_PATH = #{libmruby_static}"
end
end
task :all => "#{build_dir}/lib/libmruby.flags.mak"
diff --git a/tasks/mrbgems.rake b/tasks/mrbgems.rake
index 1b964524c..fb76856e5 100644
--- a/tasks/mrbgems.rake
+++ b/tasks/mrbgems.rake
@@ -5,7 +5,7 @@ MRuby.each_target do
gems.check self
# loader all gems
- self.libmruby << objfile("#{build_dir}/mrbgems/gem_init")
+ self.libmruby_objs << objfile("#{build_dir}/mrbgems/gem_init")
file objfile("#{build_dir}/mrbgems/gem_init") => ["#{build_dir}/mrbgems/gem_init.c", "#{build_dir}/LEGAL"]
file "#{build_dir}/mrbgems/gem_init.c" => [MRUBY_CONFIG, __FILE__] do |t|
FileUtils.mkdir_p "#{build_dir}/mrbgems"
@@ -18,7 +18,7 @@ MRuby.each_target do
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|
+ gem_final_calls = gem_func_gems.reverse_each.with_object('') do |g, s|
s << " GENERATED_TMP_mrb_#{g.funcname}_gem_final(mrb);\n"
end
f.puts %Q[/*]
@@ -53,6 +53,7 @@ MRuby.each_target do
# legal documents
file "#{build_dir}/LEGAL" => [MRUBY_CONFIG, __FILE__] do |t|
+ FileUtils.mkdir_p File.dirname t.name
open(t.name, 'w+') do |f|
f.puts <<LEGAL
Copyright (c) #{Time.now.year} mruby developers
diff --git a/tasks/toolchains/android.rake b/tasks/toolchains/android.rake
index c59da7fcb..c7df9ef80 100644
--- a/tasks/toolchains/android.rake
+++ b/tasks/toolchains/android.rake
@@ -7,6 +7,7 @@ class MRuby::Toolchain::Android
DEFAULT_NDK_HOMES = %w{
/usr/local/opt/android-sdk/ndk-bundle
/usr/local/opt/android-ndk
+ ~/Android/Sdk/ndk-bundle
%LOCALAPPDATA%/Android/android-sdk/ndk-bundle
%LOCALAPPDATA%/Android/android-ndk
~/Library/Android/sdk/ndk-bundle
diff --git a/tasks/toolchains/clang.rake b/tasks/toolchains/clang.rake
index c75fa030c..7d0fe6a45 100644
--- a/tasks/toolchains/clang.rake
+++ b/tasks/toolchains/clang.rake
@@ -3,7 +3,9 @@ MRuby::Toolchain.new(:clang) do |conf, _params|
[conf.cc, conf.objc, conf.asm].each do |cc|
cc.command = ENV['CC'] || 'clang'
+ cc.flags << '-Wzero-length-array' unless ENV['CFLAGS']
end
conf.cxx.command = ENV['CXX'] || 'clang++'
- conf.linker.command = ENV['LD'] || 'clang'
+ conf.cxx.flags << '-Wzero-length-array' unless ENV['CXXFLAGS'] || ENV['CFLAGS']
+ conf.linker.command = ENV['LD'] || ENV['CXX'] || ENV['CC'] || 'clang'
end
diff --git a/tasks/toolchains/gcc.rake b/tasks/toolchains/gcc.rake
index fc2e0bff3..663fef9e6 100644
--- a/tasks/toolchains/gcc.rake
+++ b/tasks/toolchains/gcc.rake
@@ -1,8 +1,7 @@
MRuby::Toolchain.new(:gcc) do |conf, _params|
[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 -Wwrite-strings)]
- cc.defines = %w(DISABLE_GEMS)
+ cc.flags = [ENV['CFLAGS'] || %w(-g -std=gnu99 -O3 -Wall -Werror-implicit-function-declaration -Wdeclaration-after-statement -Wwrite-strings -Wundef)]
cc.option_include_path = '-I%s'
cc.option_define = '-D%s'
cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
@@ -12,8 +11,7 @@ MRuby::Toolchain.new(:gcc) do |conf, _params|
[conf.cxx].each do |cxx|
cxx.command = ENV['CXX'] || 'g++'
- cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(-g -O3 -Wall -Werror-implicit-function-declaration)]
- cxx.defines = %w(DISABLE_GEMS)
+ cxx.flags = [ENV['CXXFLAGS'] || ENV['CFLAGS'] || %w(-g -O3 -Wall -Werror-implicit-function-declaration -Wundef)]
cxx.option_include_path = '-I%s'
cxx.option_define = '-D%s'
cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
@@ -22,7 +20,7 @@ MRuby::Toolchain.new(:gcc) do |conf, _params|
end
conf.linker do |linker|
- linker.command = ENV['LD'] || 'gcc'
+ linker.command = ENV['LD'] || ENV['CXX'] || ENV['CC'] || 'gcc'
linker.flags = [ENV['LDFLAGS'] || %w()]
linker.libraries = %w(m)
linker.library_paths = []
@@ -55,7 +53,7 @@ MRuby::Toolchain.new(:gcc) do |conf, _params|
@header_search_paths
end
end
-
+
def conf.enable_sanitizer(*opts)
fail 'sanitizer already set' if @sanitizer_list
diff --git a/tasks/toolchains/openwrt.rake b/tasks/toolchains/openwrt.rake
index 1637f6d91..aeb6dbcbc 100644
--- a/tasks/toolchains/openwrt.rake
+++ b/tasks/toolchains/openwrt.rake
@@ -5,7 +5,6 @@ MRuby::Toolchain.new(:openwrt) do |conf|
cc.command = ENV['TARGET_CC']
cc.flags = ENV['TARGET_CFLAGS']
cc.include_paths = ["#{MRUBY_ROOT}/include"]
- cc.defines = %w(DISABLE_GEMS)
cc.option_include_path = '-I%s'
cc.option_define = '-D%s'
cc.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
@@ -15,7 +14,6 @@ MRuby::Toolchain.new(:openwrt) do |conf|
cxx.command = ENV['TARGET_CXX']
cxx.flags = ENV['TARGET_CXXFLAGS']
cxx.include_paths = ["#{MRUBY_ROOT}/include"]
- cxx.defines = %w(DISABLE_GEMS)
cxx.option_include_path = '-I%s'
cxx.option_define = '-D%s'
cxx.compile_options = '%{flags} -MMD -o %{outfile} -c %{infile}'
diff --git a/tasks/toolchains/visualcpp.rake b/tasks/toolchains/visualcpp.rake
index b008273a2..6275059bb 100644
--- a/tasks/toolchains/visualcpp.rake
+++ b/tasks/toolchains/visualcpp.rake
@@ -3,7 +3,7 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params|
cc.command = ENV['CC'] || 'cl.exe'
# C4013: implicit function declaration
cc.flags = [ENV['CFLAGS'] || %w(/c /nologo /W3 /we4013 /Zi /MD /O2 /D_CRT_SECURE_NO_WARNINGS)]
- cc.defines = %w(DISABLE_GEMS MRB_STACK_EXTEND_DOUBLING)
+ cc.defines = %w(MRB_STACK_EXTEND_DOUBLING)
cc.option_include_path = '/I%s'
cc.option_define = '/D%s'
cc.compile_options = "%{flags} /Fo%{outfile} %{infile}"
@@ -14,7 +14,7 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params|
conf.cxx do |cxx|
cxx.command = ENV['CXX'] || 'cl.exe'
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.defines = %w(MRB_STACK_EXTEND_DOUBLING)
cxx.option_include_path = '/I%s'
cxx.option_define = '/D%s'
cxx.compile_options = "%{flags} /Fo%{outfile} %{infile}"
diff --git a/test/assert.rb b/test/assert.rb
index a9baae5e1..c493cbbc0 100644
--- a/test/assert.rb
+++ b/test/assert.rb
@@ -1,17 +1,47 @@
+$undefined = Object.new
$ok_test = 0
$ko_test = 0
$kill_test = 0
+$skip_test = 0
$asserts = []
$test_start = Time.now if Object.const_defined?(:Time)
-# Implementation of print due to the reason that there might be no print
-def t_print(*args)
- i = 0
- len = args.size
- while i < len
- str = args[i].to_s
- __t_printstr__ str rescue print str
- i += 1
+unless RUBY_ENGINE == "mruby"
+ # For bintest on Ruby
+ def t_print(*args)
+ print(*args)
+ $stdout.flush
+ nil
+ end
+end
+
+class Array
+ def _assertion_join
+ join("-")
+ end
+end
+
+class String
+ def _assertion_indent(indent)
+ indent = indent.to_s
+ off = 0
+ str = self
+ while nl = index("\n", off)
+ nl += 1
+ nl += 1 while slice(nl) == "\n"
+ break if nl >= size
+ str = indent.dup if off == 0
+ str += slice(off, nl - off) + indent
+ off = nl
+ end
+
+ if off == 0
+ str = indent + self
+ else
+ str += slice(off..-1)
+ end
+
+ str
end
end
@@ -19,16 +49,17 @@ end
# Create the assertion in a readable way
def assertion_string(err, str, iso=nil, e=nil, bt=nil)
msg = "#{err}#{str}"
- msg += " [#{iso}]" if iso && iso != ''
- msg += " => #{e.cause}" if e && e.respond_to?(:cause)
- msg += " => #{e.message}" if e && !e.respond_to?(:cause)
- msg += " (mrbgems: #{GEMNAME})" if Object.const_defined?(:GEMNAME)
- if $mrbtest_assert && $mrbtest_assert.size > 0
+ msg += " [#{iso}]" if iso && !iso.empty?
+ msg += " => #{e}" if e && !e.to_s.empty?
+ msg += " (#{GEMNAME == 'mruby-test' ? 'core' : "mrbgems: #{GEMNAME}"})"
+ if $mrbtest_assert
$mrbtest_assert.each do |idx, assert_msg, diff|
- msg += "\n - Assertion[#{idx}] Failed: #{assert_msg}\n#{diff}"
+ msg += "\n - Assertion[#{idx}]"
+ msg += " #{assert_msg}." if assert_msg && !assert_msg.empty?
+ msg += "\n#{diff}" if diff && !diff.empty?
end
end
- msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt
+ msg += "\nbacktrace:\n #{bt.join("\n ")}" if bt
msg
end
@@ -43,206 +74,292 @@ end
def assert(str = 'Assertion failed', iso = '')
t_print(str, (iso != '' ? " [#{iso}]" : ''), ' : ') if $mrbtest_verbose
begin
+ $mrbtest_child_noassert ||= [0]
+ $mrbtest_child_noassert << 0
+ parent_asserts = $asserts
+ $asserts = []
+ parent_mrbtest_assert = $mrbtest_assert
$mrbtest_assert = []
- $mrbtest_assert_idx = 0
+
+ if $mrbtest_assert_idx && !$mrbtest_assert_idx.empty?
+ $mrbtest_assert_idx[-1] += 1
+ $mrbtest_assert_idx << 0
+ else
+ $mrbtest_assert_idx = [0]
+ class << $mrbtest_assert_idx
+ alias to_s _assertion_join
+ end
+ end
+
yield
- if($mrbtest_assert.size > 0)
- $asserts.push(assertion_string('Fail: ', str, iso, nil))
- $ko_test += 1
- t_print('F')
+ if $mrbtest_assert.size > 0
+ if $mrbtest_assert.size == $mrbtest_child_noassert[-1]
+ $asserts.push(assertion_string('Info: ', str, iso))
+ $mrbtest_child_noassert[-2] += 1
+ $ok_test += 1
+ t_print('.')
+ else
+ $asserts.push(assertion_string('Fail: ', str, iso))
+ $ko_test += 1
+ t_print('F')
+ end
else
$ok_test += 1
t_print('.')
end
+ rescue MRubyTestSkip => e
+ $asserts.push(assertion_string('Skip: ', str, iso, e))
+ $skip_test += 1
+ $mrbtest_child_noassert[-2] += 1
+ t_print('?')
rescue Exception => e
bt = e.backtrace if $mrbtest_verbose
- if e.class.to_s == 'MRubyTestSkip'
- $asserts.push(assertion_string('Skip: ', str, iso, e, nil))
- t_print('?')
+ $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt))
+ $kill_test += 1
+ t_print('X')
+ ensure
+ if $mrbtest_assert_idx.size > 1
+ $asserts.each do |mesg|
+ idx = $mrbtest_assert_idx[0..-2]._assertion_join
+ mesg = mesg._assertion_indent(" ")
+
+ # Give `mesg` as a `diff` argument to avoid adding extra periods.
+ parent_mrbtest_assert << [idx, nil, mesg]
+ end
else
- $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt))
- $kill_test += 1
- t_print('X')
+ parent_asserts.concat $asserts
end
- ensure
- $mrbtest_assert = nil
+ $asserts = parent_asserts
+
+ $mrbtest_assert = parent_mrbtest_assert
+ $mrbtest_assert_idx.pop
+ $mrbtest_assert_idx = nil if $mrbtest_assert_idx.empty?
+ $mrbtest_child_noassert.pop
+
+ nil
end
t_print("\n") if $mrbtest_verbose
end
def assertion_diff(exp, act)
- " Expected: #{exp.inspect}\n" +
+ " Expected: #{exp.inspect}\n" \
" Actual: #{act.inspect}"
end
-def assert_true(ret, msg = nil, diff = nil)
- if $mrbtest_assert
- $mrbtest_assert_idx += 1
- unless ret
- msg = "Expected #{ret.inspect} to be true" unless msg
- diff = assertion_diff(true, ret) unless diff
- $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
+def assert_true(obj, msg = nil, diff = nil)
+ if $mrbtest_assert_idx && $mrbtest_assert_idx.size > 0
+ $mrbtest_assert_idx[-1] += 1
+ unless obj == true
+ diff ||= " Expected #{obj.inspect} to be true."
+ $mrbtest_assert.push([$mrbtest_assert_idx.to_s, msg, diff])
end
end
- ret
+ obj
end
-def assert_false(ret, msg = nil, diff = nil)
- if $mrbtest_assert
- $mrbtest_assert_idx += 1
- if ret
- msg = "Expected #{ret.inspect} to be false" unless msg
- diff = assertion_diff(false, ret) unless diff
+def assert_false(obj, msg = nil, diff = nil)
+ unless obj == false
+ diff ||= " Expected #{obj.inspect} to be false."
+ end
+ assert_true(!obj, msg, diff)
+end
+
+def assert_equal(exp, act_or_msg = nil, msg = nil, &block)
+ ret, exp, act, msg = _eval_assertion(:==, exp, act_or_msg, msg, block)
+ unless ret
+ diff = assertion_diff(exp, act)
+ end
+ assert_true(ret, msg, diff)
+end
- $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff])
+def assert_not_equal(exp, act_or_msg = nil, msg = nil, &block)
+ ret, exp, act, msg = _eval_assertion(:==, exp, act_or_msg, msg, block)
+ if ret
+ diff = " Expected #{act.inspect} to not be equal to #{exp.inspect}."
+ end
+ assert_true(!ret, msg, diff)
+end
+
+def assert_same(*args); _assert_same(true, *args) end
+def assert_not_same(*args); _assert_same(false, *args) end
+def _assert_same(affirmed, exp, act, msg = nil)
+ unless ret = exp.equal?(act) == affirmed
+ exp_str, act_str = [exp, act].map do |o|
+ "#{o.inspect} (class=#{o.class}, oid=#{o.__id__})"
end
+ diff = " Expected #{act_str} to #{'not ' unless affirmed}be the same as #{exp_str}."
end
- !ret
+ assert_true(ret, msg, diff)
end
-def assert_equal(arg1, arg2 = nil, arg3 = nil)
- if block_given?
- exp, act, msg = arg1, yield, arg2
- else
- exp, act, msg = arg1, arg2, arg3
+def assert_nil(obj, msg = nil)
+ unless ret = obj.nil?
+ diff = " Expected #{obj.inspect} to be nil."
end
+ assert_true(ret, msg, diff)
+end
- msg = "Expected to be equal" unless msg
- diff = assertion_diff(exp, act)
- assert_true(exp == act, msg, diff)
+def assert_include(*args); _assert_include(true, *args) end
+def assert_not_include(*args); _assert_include(false, *args) end
+def _assert_include(affirmed, collection, obj, msg = nil)
+ unless ret = collection.include?(obj) == affirmed
+ diff = " Expected #{collection.inspect} to #{'not ' unless affirmed}include #{obj.inspect}."
+ end
+ assert_true(ret, msg, diff)
end
-def assert_not_equal(arg1, arg2 = nil, arg3 = nil)
- if block_given?
- exp, act, msg = arg1, yield, arg2
- else
- exp, act, msg = arg1, arg2, arg3
+def assert_predicate(*args); _assert_predicate(true, *args) end
+def assert_not_predicate(*args); _assert_predicate(false, *args) end
+def _assert_predicate(affirmed, obj, op, msg = nil)
+ unless ret = obj.__send__(op) == affirmed
+ diff = " Expected #{obj.inspect} to #{'not ' unless affirmed}be #{op}."
end
+ assert_true(ret, msg, diff)
+end
- msg = "Expected to be not equal" unless msg
- diff = assertion_diff(exp, act)
- assert_false(exp == act, msg, diff)
+def assert_operator(*args); _assert_operator(true, *args) end
+def assert_not_operator(*args); _assert_operator(false, *args) end
+def _assert_operator(affirmed, obj1, op, obj2 = $undefined, msg = nil)
+ return _assert_predicate(affirmed, obj1, op, msg) if $undefined.equal?(obj2)
+ unless ret = obj1.__send__(op, obj2) == affirmed
+ diff = " Expected #{obj1.inspect} to #{'not ' unless affirmed}be #{op} #{obj2.inspect}."
+ end
+ assert_true(ret, msg, diff)
end
-def assert_nil(obj, msg = nil)
- msg = "Expected #{obj.inspect} to be nil" unless msg
- diff = assertion_diff(nil, obj)
- assert_true(obj.nil?, msg, diff)
+##
+# Fail unless +str+ matches against +pattern+.
+#
+# +pattern+ is interpreted as pattern for File.fnmatch?. It may contain the
+# following metacharacters:
+#
+# <code>*</code> ::
+# Matches any string.
+#
+# <code>?</code> ::
+# Matches any one character.
+#
+# <code>[_SET_]</code>, <code>[^_SET_]</code> (<code>[!_SET_]</code>) ::
+# Matches any one character in _SET_. Behaves like character sets in
+# Regexp, including set negation (<code>[^a-z]</code>).
+#
+# <code>{_A_,_B_}</code> ::
+# Matches pattern _A_ or pattern _B_.
+#
+# <code> \ </code> ::
+# Escapes the next character.
+def assert_match(*args); _assert_match(true, *args) end
+def assert_not_match(*args); _assert_match(false, *args) end
+def _assert_match(affirmed, pattern, str, msg = nil)
+ receiver, *args = RUBY_ENGINE == "mruby" ?
+ [self, :_str_match?, pattern, str] :
+ [File, :fnmatch?, pattern, str, File::FNM_EXTGLOB|File::FNM_DOTMATCH]
+ unless ret = !receiver.__send__(*args) == !affirmed
+ diff = " Expected #{pattern.inspect} to #{'not ' unless affirmed}match #{str.inspect}."
+ end
+ assert_true(ret, msg, diff)
end
-def assert_include(collection, obj, msg = nil)
- msg = "Expected #{collection.inspect} to include #{obj.inspect}" unless msg
- diff = " Collection: #{collection.inspect}\n" +
- " Object: #{obj.inspect}"
- assert_true(collection.include?(obj), msg, diff)
+##
+# Fails unless +obj+ is a kind of +cls+.
+def assert_kind_of(cls, obj, msg = nil)
+ unless ret = obj.kind_of?(cls)
+ diff = " Expected #{obj.inspect} to be a kind of #{cls}, not #{obj.class}."
+ end
+ assert_true(ret, msg, diff)
end
-def assert_not_include(collection, obj, msg = nil)
- msg = "Expected #{collection.inspect} to not include #{obj.inspect}" unless msg
- diff = " Collection: #{collection.inspect}\n" +
- " Object: #{obj.inspect}"
- assert_false(collection.include?(obj), msg, diff)
+##
+# Fails unless +exp+ is equal to +act+ in terms of a Float
+def assert_float(exp, act, msg = nil)
+ e, a = exp.to_f, act.to_f
+ if e.finite? && a.finite? && (n = (e - a).abs) > Mrbtest::FLOAT_TOLERANCE
+ flunk(msg, " Expected |#{exp} - #{act}| (#{n}) to be <= #{Mrbtest::FLOAT_TOLERANCE}.")
+ elsif (e.infinite? || a.infinite?) && e != a ||
+ e.nan? && !a.nan? || !e.nan? && a.nan?
+ flunk(msg, " Expected #{act} to be #{exp}.")
+ else
+ pass
+ end
end
def assert_raise(*exc)
- return true unless $mrbtest_assert
- $mrbtest_assert_idx += 1
-
msg = (exc.last.is_a? String) ? exc.pop : nil
-
+ exc = exc.empty? ? StandardError : exc.size == 1 ? exc[0] : exc
begin
yield
- msg ||= "Expected to raise #{exc} but nothing was raised."
- diff = nil
- $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff]
- false
rescue *exc
- true
+ pass
rescue Exception => e
- msg ||= "Expected to raise #{exc}, not"
- diff = " Class: <#{e.class}>\n" +
- " Message: #{e.message}"
- $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff]
- false
+ diff = " #{exc} exception expected, not\n" \
+ " Class: <#{e.class}>\n" \
+ " Message: <#{e}>"
+ flunk(msg, diff)
+ else
+ diff = " #{exc} expected but nothing was raised."
+ flunk(msg, diff)
end
end
def assert_nothing_raised(msg = nil)
- return true unless $mrbtest_assert
- $mrbtest_assert_idx += 1
-
begin
yield
- true
rescue Exception => e
- msg ||= "Expected not to raise #{exc.join(', ')} but it raised"
- diff = " Class: <#{e.class}>\n" +
- " Message: #{e.message}"
- $mrbtest_assert.push [$mrbtest_assert_idx, msg, diff]
- false
+ diff = " Exception raised:\n" \
+ " Class: <#{e.class}>\n" \
+ " Message: <#{e}>"
+ flunk(msg, diff)
+ else
+ pass
end
end
-##
-# Fails unless +obj+ is a kind of +cls+.
-def assert_kind_of(cls, obj, msg = nil)
- msg = "Expected #{obj.inspect} to be a kind of #{cls}, not #{obj.class}" unless msg
- diff = assertion_diff(cls, obj.class)
- assert_true(obj.kind_of?(cls), msg, diff)
+def pass
+ assert_true(true)
end
-##
-# Fails unless +exp+ is equal to +act+ in terms of a Float
-def assert_float(exp, act, msg = nil)
- msg = "Float #{exp} expected to be equal to float #{act}" unless msg
- diff = assertion_diff(exp, act)
- assert_true check_float(exp, act), msg, diff
+def flunk(msg = "Epic Fail!", diff = "")
+ assert_true(false, msg, diff)
end
##
# Report the test result and print all assertions
# which were reported broken.
-def report()
+def report
t_print("\n")
$asserts.each do |msg|
- t_print "#{msg}\n"
+ t_print("#{msg}\n")
end
- $total_test = $ok_test+$ko_test+$kill_test
+ $total_test = $ok_test + $ko_test + $kill_test + $skip_test
t_print("Total: #{$total_test}\n")
t_print(" OK: #{$ok_test}\n")
t_print(" KO: #{$ko_test}\n")
t_print("Crash: #{$kill_test}\n")
+ t_print(" Skip: #{$skip_test}\n")
if Object.const_defined?(:Time)
t_time = Time.now - $test_start
t_print(" Time: #{t_time.round(2)} seconds\n")
end
+
+ $ko_test == 0 && $kill_test == 0
end
-##
-# Performs fuzzy check for equality on methods returning floats
-def check_float(a, b)
- tolerance = Mrbtest::FLOAT_TOLERANCE
- a = a.to_f
- b = b.to_f
- if a.finite? and b.finite?
- (a-b).abs < tolerance
+def _eval_assertion(meth, exp, act_or_msg, msg, block)
+ if block
+ exp, act, msg = exp, block.call, act_or_msg
else
- true
+ exp, act, msg = exp, act_or_msg, msg
end
+ return exp.__send__(meth, act), exp, act, msg
end
##
# Skip the test
-class MRubyTestSkip < NotImplementedError
- attr_accessor :cause
- def initialize(cause)
- @cause = cause
- end
-end
+class MRubyTestSkip < NotImplementedError; end
def skip(cause = "")
raise MRubyTestSkip.new(cause)
diff --git a/test/bintest.rb b/test/bintest.rb
index 12971a9d9..ed71e57fd 100644
--- a/test/bintest.rb
+++ b/test/bintest.rb
@@ -1,6 +1,8 @@
$:.unshift File.dirname(File.dirname(File.expand_path(__FILE__)))
require 'test/assert.rb'
+GEMNAME = ""
+
def cmd(s)
case RbConfig::CONFIG['host_os']
when /mswin(?!ce)|mingw|bccwin/
@@ -19,15 +21,22 @@ def shellquote(s)
end
end
+print "bintest - Command Binary Test\n\n"
+
ARGV.each do |gem|
+ case gem
+ when '-v'; $mrbtest_verbose = true
+ end
+
case RbConfig::CONFIG['host_os']
when /mswin(?!ce)|mingw|bccwin/
gem = gem.gsub('\\', '/')
end
Dir["#{gem}/bintest/**/*.rb"].each do |file|
+ GEMNAME.replace(File.basename(gem))
load file
end
end
-load 'test/report.rb'
+exit report
diff --git a/test/report.rb b/test/report.rb
deleted file mode 100644
index fb77fd0aa..000000000
--- a/test/report.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-report
-if $ko_test > 0 or $kill_test > 0
- raise "mrbtest failed (KO:#{$ko_test}, Crash:#{$kill_test})"
-end
diff --git a/test/t/array.rb b/test/t/array.rb
index 3ed3d54ec..eec31d751 100644
--- a/test/t/array.rb
+++ b/test/t/array.rb
@@ -55,9 +55,10 @@ assert('Array#[]', '15.2.12.5.4') do
assert_equal(nil, [1,2,3].[](-4))
a = [ "a", "b", "c", "d", "e" ]
- assert_equal("b", a[1.1]) if class_defined?("Float")
assert_equal(["b", "c"], a[1,2])
assert_equal(["b", "c", "d"], a[1..-2])
+ skip unless Object.const_defined?(:Float)
+ assert_equal("b", a[1.1])
end
assert('Array#[]=', '15.2.12.5.5') do
@@ -238,7 +239,7 @@ assert('Array#pop', '15.2.12.5.21') do
assert_equal([1,2], a)
assert_equal(3, b)
- assert_raise(RuntimeError) { [].freeze.pop }
+ assert_raise(FrozenError) { [].freeze.pop }
end
assert('Array#push', '15.2.12.5.22') do
@@ -287,7 +288,7 @@ assert('Array#shift', '15.2.12.5.27') do
assert_equal([2,3], a)
assert_equal(1, b)
- assert_raise(RuntimeError) { [].freeze.shift }
+ assert_raise(FrozenError) { [].freeze.shift }
end
assert('Array#size', '15.2.12.5.28') do
@@ -297,11 +298,38 @@ assert('Array#size', '15.2.12.5.28') do
end
assert('Array#slice', '15.2.12.5.29') do
- a = "12345".slice(1, 3)
- b = a.slice(0)
-
- assert_equal("2:", "#{b}:")
- assert_equal(2, [1,2,3].[](1))
+ a = [*(1..100)]
+ b = a.dup
+
+ assert_equal(1, a.slice(0))
+ assert_equal(100, a.slice(99))
+ assert_nil(a.slice(100))
+ assert_equal(100, a.slice(-1))
+ assert_equal(99, a.slice(-2))
+ assert_equal(1, a.slice(-100))
+ assert_nil(a.slice(-101))
+ assert_equal([1], a.slice(0,1))
+ assert_equal([100], a.slice(99,1))
+ assert_equal([], a.slice(100,1))
+ assert_equal([100], a.slice(99,100))
+ assert_equal([100], a.slice(-1,1))
+ assert_equal([99], a.slice(-2,1))
+ assert_equal([10, 11, 12], a.slice(9, 3))
+ assert_equal([10, 11, 12], a.slice(-91, 3))
+ assert_nil(a.slice(-101, 2))
+ assert_equal([1], a.slice(0..0))
+ assert_equal([100], a.slice(99..99))
+ assert_equal([], a.slice(100..100))
+ assert_equal([100], a.slice(99..200))
+ assert_equal([100], a.slice(-1..-1))
+ assert_equal([99], a.slice(-2..-2))
+ assert_equal([10, 11, 12], a.slice(9..11))
+ assert_equal([10, 11, 12], a.slice(-91..-89))
+ assert_equal([10, 11, 12], a.slice(-91..-89))
+ assert_nil(a.slice(-101..-1))
+ assert_nil(a.slice(10, -3))
+ assert_equal([], a.slice(10..7))
+ assert_equal(b, a)
end
assert('Array#unshift', '15.2.12.5.30') do
@@ -361,13 +389,6 @@ end
# Not ISO specified
-assert("Array (Shared Array Corruption)") do
- a = [ "a", "b", "c", "d", "e", "f" ]
- b = a.slice(1, 3)
- a.clear
- b.clear
-end
-
assert("Array (Longish inline array)") do
ary = [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10], [11, 11], [12, 12], [13, 13], [14, 14], [15, 15], [16, 16], [17, 17], [18, 18], [19, 19], [20, 20], [21, 21], [22, 22], [23, 23], [24, 24], [25, 25], [26, 26], [27, 27], [28, 28], [29, 29], [30, 30], [31, 31], [32, 32], [33, 33], [34, 34], [35, 35], [36, 36], [37, 37], [38, 38], [39, 39], [40, 40], [41, 41], [42, 42], [43, 43], [44, 44], [45, 45], [46, 46], [47, 47], [48, 48], [49, 49], [50, 50], [51, 51], [52, 52], [53, 53], [54, 54], [55, 55], [56, 56], [57, 57], [58, 58], [59, 59], [60, 60], [61, 61], [62, 62], [63, 63], [64, 64], [65, 65], [66, 66], [67, 67], [68, 68], [69, 69], [70, 70], [71, 71], [72, 72], [73, 73], [74, 74], [75, 75], [76, 76], [77, 77], [78, 78], [79, 79], [80, 80], [81, 81], [82, 82], [83, 83], [84, 84], [85, 85], [86, 86], [87, 87], [88, 88], [89, 89], [90, 90], [91, 91], [92, 92], [93, 93], [94, 94], [95, 95], [96, 96], [97, 97], [98, 98], [99, 99], [100, 100], [101, 101], [102, 102], [103, 103], [104, 104], [105, 105], [106, 106], [107, 107], [108, 108], [109, 109], [110, 110], [111, 111], [112, 112], [113, 113], [114, 114], [115, 115], [116, 116], [117, 117], [118, 118], [119, 119], [120, 120], [121, 121], [122, 122], [123, 123], [124, 124], [125, 125], [126, 126], [127, 127], [128, 128], [129, 129], [130, 130], [131, 131], [132, 132], [133, 133], [134, 134], [135, 135], [136, 136], [137, 137], [138, 138], [139, 139], [140, 140], [141, 141], [142, 142], [143, 143], [144, 144], [145, 145], [146, 146], [147, 147], [148, 148], [149, 149], [150, 150], [151, 151], [152, 152], [153, 153], [154, 154], [155, 155], [156, 156], [157, 157], [158, 158], [159, 159], [160, 160], [161, 161], [162, 162], [163, 163], [164, 164], [165, 165], [166, 166], [167, 167], [168, 168], [169, 169], [170, 170], [171, 171], [172, 172], [173, 173], [174, 174], [175, 175], [176, 176], [177, 177], [178, 178], [179, 179], [180, 180], [181, 181], [182, 182], [183, 183], [184, 184], [185, 185], [186, 186], [187, 187], [188, 188], [189, 189], [190, 190], [191, 191], [192, 192], [193, 193], [194, 194], [195, 195], [196, 196], [197, 197], [198, 198], [199, 199]]
h = Hash.new(0)
@@ -387,16 +408,15 @@ assert("Array#rindex") do
assert_equal 0, $a.rindex(1)
end
+assert('Array#sort!') do
+ a = [3, 2, 1]
+ assert_equal a, a.sort! # sort! returns self.
+ assert_equal [1, 2, 3], a # it is sorted.
+end
+
assert('Array#freeze') do
a = [].freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
a[0] = 1
end
end
-
-assert('shared array replace') do
- a = [0] * 40
- b = [0, 1, 2]
- b.replace a[1, 20].dup
- assert_equal 20, b.size
-end
diff --git a/test/t/bs_block.rb b/test/t/bs_block.rb
index 62eb7e32e..995e52559 100644
--- a/test/t/bs_block.rb
+++ b/test/t/bs_block.rb
@@ -408,42 +408,43 @@ assert('BS Block 32') do
end
assert('BS Block [ruby-core:14395]') do
- class Controller
- def respond_to(&block)
- responder = Responder.new
- block.call(responder)
- responder.respond
- end
- def test_for_bug
- respond_to{|format|
- format.js{
- "in test"
- render{|obj|
- obj
+ assert_nothing_raised do
+ class Controller
+ def respond_to(&block)
+ responder = Responder.new
+ block.call(responder)
+ responder.respond
+ end
+ def test_for_bug
+ respond_to{|format|
+ format.js{
+ "in test"
+ render{|obj|
+ obj
+ }
}
}
- }
- end
- def render(&block)
- "in render"
- end
- end
-
- class Responder
- def method_missing(symbol, &block)
- "enter method_missing"
- @response = Proc.new{
- 'in method missing'
- block.call
- }
- "leave method_missing"
+ end
+ def render(&block)
+ "in render"
+ end
end
- def respond
- @response.call
+ class Responder
+ def method_missing(symbol, &block)
+ "enter method_missing"
+ @response = Proc.new{
+ 'in method missing'
+ block.call
+ }
+ "leave method_missing"
+ end
+ def respond
+ @response.call
+ end
end
+ t = Controller.new
+ t.test_for_bug
end
- t = Controller.new
- assert_true t.test_for_bug
end
assert("BS Block 33") do
diff --git a/test/t/class.rb b/test/t/class.rb
index a5118fa93..e2839111c 100644
--- a/test/t/class.rb
+++ b/test/t/class.rb
@@ -36,7 +36,7 @@ end
assert('Class#new', '15.2.3.3.3') do
assert_raise(TypeError, 'Singleton should raise TypeError') do
- "a".singleton_class.new
+ (class <<"a"; self; end).new
end
class TestClass
@@ -236,6 +236,11 @@ assert('class to return the last value') do
assert_equal(m, :m)
end
+assert('class to return nil if body is empty') do
+ assert_nil(class C end)
+ assert_nil(class << self; end)
+end
+
assert('raise when superclass is not a class') do
module FirstModule; end
assert_raise(TypeError, 'should raise TypeError') do
@@ -293,15 +298,7 @@ assert('singleton tests') do
end
end
- assert_false baz.singleton_methods.include? :run_foo_mod
- assert_false baz.singleton_methods.include? :run_baz
-
- assert_raise(NoMethodError, 'should raise NoMethodError') do
- baz.run_foo_mod
- end
- assert_raise(NoMethodError, 'should raise NoMethodError') do
- baz.run_baz
- end
+ assert_equal :run_baz, baz
assert_raise(NoMethodError, 'should raise NoMethodError') do
bar.run_foo_mod
@@ -318,8 +315,8 @@ assert('singleton tests') do
self
end
- assert_true baz.singleton_methods.include? :run_baz
- assert_true baz.singleton_methods.include? :run_foo_mod
+ assert_true baz.respond_to? :run_baz
+ assert_true baz.respond_to? :run_foo_mod
assert_equal 100, baz.run_foo_mod
assert_equal 300, baz.run_baz
@@ -358,7 +355,14 @@ assert('singleton tests') do
7
end
end
- end if class_defined?("Float")
+ end if Object.const_defined?(:Float)
+
+ o = Object.new
+ sc = class << o; self end
+ o.freeze
+ assert_predicate(sc, :frozen?)
+
+ assert_predicate(class << Object.new.freeze; self end, :frozen?)
end
assert('clone Class') do
@@ -368,7 +372,7 @@ assert('clone Class') do
end
end
- Foo.clone.new.func
+ assert_true(Foo.clone.new.func)
end
assert('class variable and class << self style class method') do
@@ -436,16 +440,26 @@ assert('overriding class variable with a module (#3235)') do
end
end
+assert('class variable for frozen class/module') do
+ module CVarForFrozenModule
+ freeze
+ assert_raise(FrozenError) { @@cv = 1 }
+ end
+
+ class CVarForFrozenClassA
+ @@a = nil
+ freeze
+ end
+ class CVarForFrozenClassB < CVarForFrozenClassA
+ def a=(v)
+ @@a = v
+ end
+ end
+ b = CVarForFrozenClassB.new
+ assert_raise(FrozenError) { b.a = 1 }
+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
-
-assert("remove_method doesn't segfault if the passed in argument isn't a symbol") do
- klass = Class.new
- assert_raise(TypeError) { klass.remove_method nil }
- assert_raise(TypeError) { klass.remove_method 123 }
- assert_raise(TypeError) { klass.remove_method 1.23 }
- assert_raise(NameError) { klass.remove_method "hello" }
- assert_raise(TypeError) { klass.remove_method Class.new }
-end
diff --git a/test/t/codegen.rb b/test/t/codegen.rb
index 4c9e2c594..acb9e1bf5 100644
--- a/test/t/codegen.rb
+++ b/test/t/codegen.rb
@@ -184,14 +184,14 @@ assert('register window of calls (#3783)') do
# NODE_UNDEF
assert_nothing_raised do
class << Object.new
- undef send
+ undef inspect
end
end
# NODE_ALIAS
assert_nothing_raised do
class << Object.new
- alias send2 send
+ alias inspect2 inspect
end
end
-end \ No newline at end of file
+end
diff --git a/test/t/enumerable.rb b/test/t/enumerable.rb
index 359c3451b..9e7602db7 100644
--- a/test/t/enumerable.rb
+++ b/test/t/enumerable.rb
@@ -45,7 +45,7 @@ end
assert('Enumerable#detect', '15.3.2.2.4') do
assert_equal 1, [1,2,3].detect() { true }
- assert_equal 'a', [1,2,3].detect("a") { false }
+ assert_equal 'a', [1,2,3].detect(->{"a"}) { false }
end
assert('Array#each_with_index', '15.3.2.2.5') do
@@ -64,11 +64,11 @@ end
assert('Enumerable#find', '15.3.2.2.7') do
assert_equal 1, [1,2,3].find() { true }
- assert_equal 'a', [1,2,3].find("a") { false }
+ assert_equal 'a', [1,2,3].find(->{"a"}) { false }
end
assert('Enumerable#find_all', '15.3.2.2.8') do
- assert_true [1,2,3,4,5,6,7,8,9].find_all() {|i| i%2 == 0}, [2,4,6,8]
+ assert_equal [2,4,6,8], [1,2,3,4,5,6,7,8,9].find_all() {|i| i%2 == 0}
end
assert('Enumerable#grep', '15.3.2.2.9') do
diff --git a/test/t/exception.rb b/test/t/exception.rb
index ce7b5841e..bdf277c1e 100644
--- a/test/t/exception.rb
+++ b/test/t/exception.rb
@@ -263,10 +263,10 @@ assert('Exception 13') do
end
assert('Exception 14') do
- def exception_test14; UnknownConstant; end
+ def (o = Object.new).exception_test14; UnknownConstant end
a = :ng
begin
- send(:exception_test14)
+ o.__send__(:exception_test14)
rescue
a = :ok
end
diff --git a/test/t/float.rb b/test/t/float.rb
index 92f7a15f1..63bf83f40 100644
--- a/test/t/float.rb
+++ b/test/t/float.rb
@@ -1,7 +1,7 @@
##
# Float ISO Test
-if class_defined?("Float")
+if Object.const_defined?(:Float)
assert('Float', '15.2.9') do
assert_equal Class, Float.class
@@ -82,8 +82,8 @@ assert('Float#ceil', '15.2.9.3.8') do
end
assert('Float#finite?', '15.2.9.3.9') do
- assert_true 3.123456789.finite?
- assert_false (1.0 / 0.0).finite?
+ assert_predicate 3.123456789, :finite?
+ assert_not_predicate 1.0 / 0.0, :finite?
end
assert('Float#floor', '15.2.9.3.10') do
@@ -139,7 +139,7 @@ assert('Float#round', '15.2.9.3.12') do
nan = 0.0/0.0
assert_raise(FloatDomainError){ nan.round }
assert_raise(FloatDomainError){ nan.round(-1) }
- assert_true(nan.round(1).nan?)
+ assert_predicate(nan.round(1), :nan?)
end
assert('Float#to_f', '15.2.9.3.13') do
@@ -178,10 +178,10 @@ assert('Float#divmod') do
end
assert('Float#nan?') do
- assert_true (0.0/0.0).nan?
- assert_false 0.0.nan?
- assert_false (1.0/0.0).nan?
- assert_false (-1.0/0.0).nan?
+ assert_predicate(0.0/0.0, :nan?)
+ assert_not_predicate(0.0, :nan?)
+ assert_not_predicate(1.0/0.0, :nan?)
+ assert_not_predicate(-1.0/0.0, :nan?)
end
assert('Float#<<') do
@@ -206,4 +206,43 @@ assert('Float#>>') do
assert_equal(-1, -23.0 >> 128)
end
-end # class_defined?("Float")
+assert('Float#to_s') do
+ uses_float = 4e38.infinite? # enable MRB_USE_FLOAT?
+
+ assert_equal("Infinity", Float::INFINITY.to_s)
+ assert_equal("-Infinity", (-Float::INFINITY).to_s)
+ assert_equal("NaN", Float::NAN.to_s)
+ assert_equal("0.0", 0.0.to_s)
+ assert_equal("-0.0", -0.0.to_s)
+ assert_equal("-3.25", -3.25.to_s)
+ assert_equal("50.0", 50.0.to_s)
+ assert_equal("0.0125", 0.0125.to_s)
+ assert_equal("-0.0125", -0.0125.to_s)
+ assert_equal("1.0e-10", 0.0000000001.to_s)
+ assert_equal("-1.0e-10", -0.0000000001.to_s)
+ assert_equal("1.0e+20", 1e20.to_s)
+ assert_equal("-1.0e+20", -1e20.to_s)
+ assert_equal("1.0e+16", 10000000000000000.0.to_s)
+ assert_equal("-1.0e+16", -10000000000000000.0.to_s)
+ assert_equal("100000.0", 100000.0.to_s)
+ assert_equal("-100000.0", -100000.0.to_s)
+ if uses_float
+ assert_equal("1.0e+08", 100000000.0.to_s)
+ assert_equal("-1.0e+08", -100000000.0.to_s)
+ assert_equal("1.0e+07", 10000000.0.to_s)
+ assert_equal("-1.0e+07", -10000000.0.to_s)
+ else
+ assert_equal("1.0e+15", 1000000000000000.0.to_s)
+ assert_equal("-1.0e+15", -1000000000000000.0.to_s)
+ assert_equal("100000000000000.0", 100000000000000.0.to_s)
+ assert_equal("-100000000000000.0", -100000000000000.0.to_s)
+ end
+end
+
+assert('Float#eql?') do
+ assert_operator(5.0, :eql?, 5.0)
+ assert_not_operator(5.0, :eql?, 5)
+ assert_not_operator(5.0, :eql?, "5.0")
+end
+
+end # const_defined?(:Float)
diff --git a/test/t/hash.rb b/test/t/hash.rb
index 63029be42..cd47d251d 100644
--- a/test/t/hash.rb
+++ b/test/t/hash.rb
@@ -8,8 +8,9 @@ end
assert('Hash#==', '15.2.13.4.1') do
assert_true({ 'abc' => 'abc' } == { 'abc' => 'abc' })
assert_false({ 'abc' => 'abc' } == { 'cba' => 'cba' })
- assert_true({ :equal => 1 } == { :equal => 1.0 }) if class_defined?("Float")
assert_false({ :a => 1 } == true)
+ skip unless Object.const_defined?(:Float)
+ assert_true({ :equal => 1 } == { :equal => 1.0 })
end
assert('Hash#[]', '15.2.13.4.2') do
@@ -82,12 +83,12 @@ assert('Hash#default_proc', '15.2.13.4.7') do
end
assert('Hash#delete', '15.2.13.4.8') do
- a = { 'abc' => 'abc' }
- b = { 'abc' => 'abc' }
+ a = { 'abc' => 'ABC' }
+ b = { 'abc' => 'ABC' }
b_tmp_1 = false
b_tmp_2 = false
- a.delete('abc')
+ assert_equal 'ABC', a.delete('abc')
b.delete('abc') do |k|
b_tmp_1 = true
end
@@ -371,7 +372,7 @@ end
assert('Hash#freeze') do
h = {}.freeze
- assert_raise(RuntimeError) do
+ assert_raise(FrozenError) do
h[:a] = 'b'
end
end
diff --git a/test/t/integer.rb b/test/t/integer.rb
index cea97a1e6..4ab49eb0a 100644
--- a/test/t/integer.rb
+++ b/test/t/integer.rb
@@ -7,10 +7,10 @@ end
assert('Integer#+', '15.2.8.3.1') do
a = 1+1
- b = 1+1.0 if class_defined?("Float")
+ b = 1+1.0 if Object.const_defined?(:Float)
assert_equal 2, a
- assert_equal 2.0, b if class_defined?("Float")
+ assert_equal 2.0, b if Object.const_defined?(:Float)
assert_raise(TypeError){ 0+nil }
assert_raise(TypeError){ 1+nil }
@@ -18,40 +18,38 @@ assert('Integer#+', '15.2.8.3.1') do
c = Mrbtest::FIXNUM_MAX + 1
d = Mrbtest::FIXNUM_MAX.__send__(:+, 1)
- if class_defined?("Float")
- e = Mrbtest::FIXNUM_MAX + 1.0
- assert_equal Float, c.class
- assert_equal Float, d.class
- assert_float e, c
- assert_float e, d
- end
+ skip unless Object.const_defined?(:Float)
+ e = Mrbtest::FIXNUM_MAX + 1.0
+ assert_equal Float, c.class
+ assert_equal Float, d.class
+ assert_float e, c
+ assert_float e, d
end
assert('Integer#-', '15.2.8.3.2') do
a = 2-1
- b = 2-1.0 if class_defined?("Float")
+ b = 2-1.0 if Object.const_defined?(:Float)
assert_equal 1, a
- assert_equal 1.0, b if class_defined?("Float")
+ assert_equal 1.0, b if Object.const_defined?(:Float)
c = Mrbtest::FIXNUM_MIN - 1
d = Mrbtest::FIXNUM_MIN.__send__(:-, 1)
- if class_defined?("Float")
- e = Mrbtest::FIXNUM_MIN - 1.0
- assert_equal Float, c.class
- assert_equal Float, d.class
- assert_float e, c
- assert_float e, d
- end
+ skip unless Object.const_defined?(:Float)
+ e = Mrbtest::FIXNUM_MIN - 1.0
+ assert_equal Float, c.class
+ assert_equal Float, d.class
+ assert_float e, c
+ assert_float e, d
end
assert('Integer#*', '15.2.8.3.3') do
a = 1*1
- b = 1*1.0 if class_defined?("Float")
+ b = 1*1.0 if Object.const_defined?(:Float)
assert_equal 1, a
- assert_equal 1.0, b if class_defined?("Float")
+ assert_equal 1.0, b if Object.const_defined?(:Float)
assert_raise(TypeError){ 0*nil }
assert_raise(TypeError){ 1*nil }
@@ -59,13 +57,12 @@ assert('Integer#*', '15.2.8.3.3') do
c = Mrbtest::FIXNUM_MAX * 2
d = Mrbtest::FIXNUM_MAX.__send__(:*, 2)
- if class_defined?("Float")
- e = Mrbtest::FIXNUM_MAX * 2.0
- assert_equal Float, c.class
- assert_equal Float, d.class
- assert_float e, c
- assert_float e, d
- end
+ skip unless Object.const_defined?(:Float)
+ e = Mrbtest::FIXNUM_MAX * 2.0
+ assert_equal Float, c.class
+ assert_equal Float, d.class
+ assert_float e, c
+ assert_float e, d
end
assert('Integer#/', '15.2.8.3.4') do
@@ -226,8 +223,9 @@ assert('Integer#times', '15.2.8.3.22') do
end
assert('Integer#to_f', '15.2.8.3.23') do
+ skip unless Object.const_defined?(:Float)
assert_equal 1.0, 1.to_f
-end if class_defined?("Float")
+end
assert('Integer#to_i', '15.2.8.3.24') do
assert_equal 1, 1.to_i
@@ -259,19 +257,3 @@ assert('Integer#divmod', '15.2.8.3.30') do
assert_equal [-2, -1], 25.divmod(-13)
assert_equal [ 1, -6], -13.divmod(-7)
end
-
-# Not ISO specified
-
-assert('Integer#step') do
- a = []
- b = []
- 1.step(3) do |i|
- a << i
- end
- 1.step(6, 2) do |i|
- b << i
- end
-
- assert_equal [1, 2, 3], a
- assert_equal [1, 3, 5], b
-end
diff --git a/test/t/kernel.rb b/test/t/kernel.rb
index 561118302..ecfb863a8 100644
--- a/test/t/kernel.rb
+++ b/test/t/kernel.rb
@@ -31,10 +31,6 @@ end
# Kernel.eval is provided by the mruby-gem mrbgem. '15.3.1.2.3'
-assert('Kernel.global_variables', '15.3.1.2.4') do
- assert_equal Array, Kernel.global_variables.class
-end
-
assert('Kernel.iterator?', '15.3.1.2.5') do
assert_false Kernel.iterator?
end
@@ -103,7 +99,7 @@ assert('Kernel#__send__', '15.3.1.3.4') do
# test with argument
assert_true __send__(:respond_to?, :nil?)
# test without argument and without block
- assert_equal Array, __send__(:public_methods).class
+ assert_equal String, __send__(:to_s).class
end
assert('Kernel#block_given?', '15.3.1.3.6') do
@@ -171,7 +167,7 @@ assert('Kernel#clone', '15.3.1.3.8') do
assert_true a.respond_to?(:test)
assert_false b.respond_to?(:test)
assert_true c.respond_to?(:test)
-
+
a.freeze
d = a.clone
assert_true d.frozen?
@@ -244,6 +240,9 @@ assert('Kernel#extend', '15.3.1.3.13') do
assert_true a.respond_to?(:test_method)
assert_false b.respond_to?(:test_method)
+
+ assert_raise(FrozenError) { Object.new.freeze.extend(Test4ExtendModule) }
+ assert_raise(FrozenError, TypeError) { :sym.extend(Test4ExtendModule) }
end
assert('Kernel#extend works on toplevel', '15.3.1.3.13') do
@@ -261,10 +260,23 @@ assert('Kernel#freeze') do
assert_equal obj, obj.freeze
assert_equal 0, 0.freeze
assert_equal :a, :a.freeze
+ assert_equal true, true.freeze
+ assert_equal false, false.freeze
+ assert_equal nil, nil.freeze
+ skip unless Object.const_defined?(:Float)
+ assert_equal 0.0, 0.0.freeze
end
-assert('Kernel#global_variables', '15.3.1.3.14') do
- assert_equal Array, global_variables.class
+assert('Kernel#frozen?') do
+ assert_false "".frozen?
+ assert_true "".freeze.frozen?
+ assert_true 0.frozen?
+ assert_true :a.frozen?
+ assert_true true.frozen?
+ assert_true false.frozen?
+ assert_true nil.frozen?
+ skip unless Object.const_defined?(:Float)
+ assert_true 0.0.frozen?
end
assert('Kernel#hash', '15.3.1.3.15') do
@@ -278,30 +290,6 @@ assert('Kernel#inspect', '15.3.1.3.17') do
assert_equal "main", s
end
-assert('Kernel#instance_variable_defined?', '15.3.1.3.20') do
- o = Object.new
- o.instance_variable_set(:@a, 1)
-
- assert_true o.instance_variable_defined?("@a")
- assert_false o.instance_variable_defined?("@b")
- assert_true o.instance_variable_defined?("@a"[0,2])
- assert_true o.instance_variable_defined?("@abc"[0,2])
-end
-
-assert('Kernel#instance_variables', '15.3.1.3.23') do
- o = Object.new
- o.instance_eval do
- @a = 11
- @b = 12
- end
- ivars = o.instance_variables
-
- assert_equal Array, ivars.class,
- assert_equal(2, ivars.size)
- assert_true ivars.include?(:@a)
- assert_true ivars.include?(:@b)
-end
-
assert('Kernel#is_a?', '15.3.1.3.24') do
assert_true is_a?(Kernel)
assert_false is_a?(Array)
@@ -381,10 +369,6 @@ assert('Kernel#method_missing', '15.3.1.3.30') do
end
end
-assert('Kernel#methods', '15.3.1.3.31') do
- assert_equal Array, methods.class
-end
-
assert('Kernel#nil?', '15.3.1.3.32') do
assert_false nil?
end
@@ -408,23 +392,6 @@ end
# Kernel#print is defined in mruby-print mrbgem. '15.3.1.3.35'
-assert('Kernel#private_methods', '15.3.1.3.36') do
- assert_equal Array, private_methods.class
-end
-
-assert('Kernel#protected_methods', '15.3.1.3.37') do
- assert_equal Array, protected_methods.class
-end
-
-assert('Kernel#public_methods', '15.3.1.3.38') do
- assert_equal Array, public_methods.class
- class Foo
- def foo
- end
- end
- assert_equal [:foo], Foo.new.public_methods(false)
-end
-
# Kernel#puts is defined in mruby-print mrbgem. '15.3.1.3.39'
assert('Kernel#raise', '15.3.1.3.40') do
@@ -450,11 +417,12 @@ assert('Kernel#remove_instance_variable', '15.3.1.3.41') do
tri = Test4RemoveInstanceVar.new
assert_equal 99, tri.var
- tri.remove
+ assert_equal 99, tri.remove
assert_equal nil, tri.var
- assert_raise NameError do
- tri.remove
- end
+ assert_raise(NameError) { tri.remove }
+ assert_raise(NameError) { tri.remove_instance_variable(:var) }
+ assert_raise(FrozenError) { tri.freeze.remove }
+ assert_raise(FrozenError, NameError) { :a.remove_instance_variable(:@v) }
end
# Kernel#require is defined in mruby-require. '15.3.1.3.42'
@@ -485,57 +453,10 @@ assert('Kernel#respond_to?', '15.3.1.3.43') do
assert_false Test4RespondTo.new.respond_to?(:test_method)
end
-assert('Kernel#send', '15.3.1.3.44') do
- # test with block
- l = send(:lambda) do
- true
- end
-
- assert_true l.call
- assert_equal l.class, Proc
- # test with argument
- assert_true send(:respond_to?, :nil?)
- # test without argument and without block
- assert_equal send(:public_methods).class, Array
-end
-
-assert('Kernel#singleton_methods', '15.3.1.3.45') do
- assert_equal singleton_methods.class, Array
-end
-
assert('Kernel#to_s', '15.3.1.3.46') do
assert_equal to_s.class, String
end
-assert('Kernel#to_s on primitives') do
- begin
- Fixnum.alias_method :to_s_, :to_s
- Fixnum.remove_method :to_s
-
- assert_nothing_raised do
- # segfaults if mrb_cptr is used
- 1.to_s
- end
- ensure
- Fixnum.alias_method :to_s, :to_s_
- Fixnum.remove_method :to_s_
- end
-end
-
-assert('Kernel.local_variables', '15.3.1.2.7') do
- a, b = 0, 1
- a += b
-
- vars = Kernel.local_variables.sort
- assert_equal [:a, :b, :vars], vars
-
- assert_equal [:a, :b, :c, :vars], Proc.new { |a, b|
- c = 2
- # Kernel#local_variables: 15.3.1.3.28
- local_variables.sort
- }.call(-1, -2)
-end
-
assert('Kernel#!=') do
str1 = "hello"
str2 = str1
@@ -578,22 +499,6 @@ assert('Kernel#respond_to_missing?') do
assert_false Test4RespondToMissing.new.respond_to?(:no_method)
end
-assert('Kernel#global_variables') do
- variables = global_variables
- 1.upto(9) do |i|
- assert_equal variables.include?(:"$#{i}"), true
- end
-end
-
-assert('Kernel#define_singleton_method') do
- o = Object.new
- ret = o.define_singleton_method(:test_method) do
- :singleton_method_ok
- end
- assert_equal :test_method, ret
- assert_equal :singleton_method_ok, o.test_method
-end
-
assert('stack extend') do
def recurse(count, stop)
return count if count > stop
diff --git a/test/t/literals.rb b/test/t/literals.rb
index 51a37c32d..6344219aa 100644
--- a/test/t/literals.rb
+++ b/test/t/literals.rb
@@ -22,7 +22,7 @@ assert('Literals Numerical', '8.7.6.2') do
# decimal
assert_equal 999, 0d999
assert_equal 999, 0D999
- # decimal seperator
+ # decimal separator
assert_equal 10000000, 10_000_000
assert_equal 10, 1_0
# integer with exponent
diff --git a/test/t/module.rb b/test/t/module.rb
index 5a46c24ff..7f869bf1f 100644
--- a/test/t/module.rb
+++ b/test/t/module.rb
@@ -3,7 +3,7 @@
def labeled_module(name, &block)
Module.new do
- singleton_class.class_eval do
+ (class <<self; self end).class_eval do
define_method(:to_s) { name }
alias_method :inspect, :to_s
end
@@ -13,7 +13,7 @@ end
def labeled_class(name, supklass = Object, &block)
Class.new(supklass) do
- singleton_class.class_eval do
+ (class <<self; self end).class_eval do
define_method(:to_s) { name }
alias_method :inspect, :to_s
end
@@ -25,26 +25,22 @@ assert('Module', '15.2.2') do
assert_equal Class, Module.class
end
-# TODO not implemented ATM assert('Module.constants', '15.2.2.3.1') do
-
-# TODO not implemented ATM assert('Module.nesting', '15.2.2.3.2') do
-
-assert('Module.nesting', '15.2.2.2.2') do
- module Test4ModuleNesting
- module Test4ModuleNesting2
- assert_equal [Test4ModuleNesting2, Test4ModuleNesting],
- Module.nesting
+assert('Module#alias_method', '15.2.2.4.8') do
+ cls = Class.new do
+ def foo
+ "FOO"
end
end
- module Test4ModuleNesting::Test4ModuleNesting2
- assert_equal [Test4ModuleNesting::Test4ModuleNesting2], Module.nesting
- end
+
+ assert_same(cls, cls.alias_method(:bar, :foo))
+ assert_equal("FOO", cls.new.bar)
end
+# TODO not implemented ATM assert('Module.constants', '15.2.2.3.1') do
+
assert('Module#ancestors', '15.2.2.4.9') do
class Test4ModuleAncestors
end
- sc = Test4ModuleAncestors.singleton_class
r = String.ancestors
assert_equal Array, r.class
@@ -63,6 +59,7 @@ assert('Module#append_features', '15.2.2.4.10') do
end
assert_equal Test4AppendFeatures2, Test4AppendFeatures2.const_get(:Const4AppendFeatures2)
+ assert_raise(FrozenError) { Module.new.append_features Class.new.freeze }
end
assert('Module#attr NameError') do
@@ -213,56 +210,9 @@ assert('Module#class_eval', '15.2.2.4.15') do
def method1
end
end
- r = Test4ClassEval.instance_methods
-
assert_equal 11, Test4ClassEval.class_eval{ @a }
assert_equal 12, Test4ClassEval.class_eval{ @b }
- assert_equal Array, r.class
- assert_true r.include?(:method1)
-end
-
-assert('Module#class_variable_defined?', '15.2.2.4.16') do
- class Test4ClassVariableDefined
- @@cv = 99
- end
-
- assert_true Test4ClassVariableDefined.class_variable_defined?(:@@cv)
- assert_false Test4ClassVariableDefined.class_variable_defined?(:@@noexisting)
-end
-
-assert('Module#class_variable_get', '15.2.2.4.17') do
- class Test4ClassVariableGet
- @@cv = 99
- end
-
- assert_equal 99, Test4ClassVariableGet.class_variable_get(:@@cv)
-end
-
-assert('Module#class_variable_set', '15.2.2.4.18') do
- class Test4ClassVariableSet
- @@foo = 100
- def foo
- @@foo
- end
- end
-
- assert_true Test4ClassVariableSet.class_variable_set(:@@cv, 99)
- assert_true Test4ClassVariableSet.class_variable_set(:@@foo, 101)
- assert_true Test4ClassVariableSet.class_variables.include? :@@cv
- assert_equal 99, Test4ClassVariableSet.class_variable_get(:@@cv)
- assert_equal 101, Test4ClassVariableSet.new.foo
-end
-
-assert('Module#class_variables', '15.2.2.4.19') do
- class Test4ClassVariables1
- @@var1 = 1
- end
- class Test4ClassVariables2 < Test4ClassVariables1
- @@var2 = 2
- end
-
- assert_equal [:@@var1], Test4ClassVariables1.class_variables
- assert_equal [:@@var2, :@@var1], Test4ClassVariables2.class_variables
+ assert_equal true, Test4ClassEval.new.respond_to?(:method1)
end
assert('Module#const_defined?', '15.2.2.4.20') do
@@ -272,6 +222,7 @@ assert('Module#const_defined?', '15.2.2.4.20') do
assert_true Test4ConstDefined.const_defined?(:Const4Test4ConstDefined)
assert_false Test4ConstDefined.const_defined?(:NotExisting)
+ assert_raise(NameError){ Test4ConstDefined.const_defined?(:wrong_name) }
end
assert('Module#const_get', '15.2.2.4.21') do
@@ -286,16 +237,7 @@ assert('Module#const_get', '15.2.2.4.21') do
assert_raise(TypeError){ Test4ConstGet.const_get(123) }
assert_raise(NameError){ Test4ConstGet.const_get(:I_DO_NOT_EXIST) }
assert_raise(NameError){ Test4ConstGet.const_get("I_DO_NOT_EXIST::ME_NEITHER") }
-end
-
-assert('Module#const_missing', '15.2.2.4.22') do
- module Test4ConstMissing
- def self.const_missing(sym)
- 42 # the answer to everything
- end
- end
-
- assert_equal 42, Test4ConstMissing.const_get(:ConstDoesntExist)
+ assert_raise(NameError){ Test4ConstGet.const_get(:wrong_name) }
end
assert('Module#const_set', '15.2.2.4.23') do
@@ -303,23 +245,47 @@ assert('Module#const_set', '15.2.2.4.23') do
Const4Test4ConstSet = 42
end
- assert_true Test4ConstSet.const_set(:Const4Test4ConstSet, 23)
+ assert_equal 23, Test4ConstSet.const_set(:Const4Test4ConstSet, 23)
assert_equal 23, Test4ConstSet.const_get(:Const4Test4ConstSet)
+ ["", "wrongNAME", "Wrong-Name"].each do |n|
+ assert_raise(NameError) { Test4ConstSet.const_set(n, 1) }
+ end
end
-assert('Module#constants', '15.2.2.4.24') do
- $n = []
- module TestA
- C = 1
+assert('Module#remove_const', '15.2.2.4.40') do
+ module Test4RemoveConst
+ ExistingConst = 23
+ end
+
+ assert_equal 23, Test4RemoveConst.remove_const(:ExistingConst)
+ assert_false Test4RemoveConst.const_defined?(:ExistingConst)
+ assert_raise(NameError) { Test4RemoveConst.remove_const(:NonExistingConst) }
+ %i[x X!].each do |n|
+ assert_raise(NameError) { Test4RemoveConst.remove_const(n) }
end
- class TestB
- include TestA
- C2 = 1
- $n = constants.sort
+ assert_raise(FrozenError) { Test4RemoveConst.freeze.remove_const(:A) }
+end
+
+assert('Module#const_missing', '15.2.2.4.22') do
+ module Test4ConstMissing
+ def self.const_missing(sym)
+ 42 # the answer to everything
+ end
end
- assert_equal [ :C ], TestA.constants
- assert_equal [ :C, :C2 ], $n
+ assert_equal 42, Test4ConstMissing.const_get(:ConstDoesntExist)
+end
+
+assert('Module#extend_object', '15.2.2.4.25') do
+ cls = Class.new
+ mod = Module.new { def foo; end }
+ a = cls.new
+ b = cls.new
+ mod.extend_object(b)
+ assert_false a.respond_to?(:foo)
+ assert_true b.respond_to?(:foo)
+ assert_raise(FrozenError) { mod.extend_object(cls.new.freeze) }
+ assert_raise(FrozenError, TypeError) { mod.extend_object(1) }
end
assert('Module#include', '15.2.2.4.27') do
@@ -335,6 +301,7 @@ assert('Module#include', '15.2.2.4.27') do
assert_equal 42, Test4Include2.const_get(:Const4Include)
assert_equal Test4Include2, Test4Include2.include_result
+ assert_raise(FrozenError) { Module.new.freeze.include Test4Include }
end
assert('Module#include?', '15.2.2.4.28') do
@@ -366,48 +333,16 @@ assert('Module#included', '15.2.2.4.29') do
assert_equal Test4Included2, Test4Included2.const_get(:Const4Included2)
end
-assert('Module#included_modules', '15.2.2.4.30') do
- module Test4includedModules
- end
- module Test4includedModules2
- include Test4includedModules
- end
- r = Test4includedModules2.included_modules
-
- assert_equal Array, r.class
- assert_true r.include?(Test4includedModules)
-end
-
assert('Module#initialize', '15.2.2.4.31') do
assert_kind_of Module, Module.new
mod = Module.new { def hello; "hello"; end }
- assert_equal [:hello], mod.instance_methods
+ cls = Class.new{include mod}
+ assert_true cls.new.respond_to?(:hello)
a = nil
mod = Module.new { |m| a = m }
assert_equal mod, a
end
-assert('Module#instance_methods', '15.2.2.4.33') do
- module Test4InstanceMethodsA
- def method1() end
- end
- class Test4InstanceMethodsB
- def method2() end
- end
- class Test4InstanceMethodsC < Test4InstanceMethodsB
- def method3() end
- end
-
- r = Test4InstanceMethodsC.instance_methods(true)
-
- assert_equal [:method1], Test4InstanceMethodsA.instance_methods
- assert_equal [:method2], Test4InstanceMethodsB.instance_methods(false)
- assert_equal [:method3], Test4InstanceMethodsC.instance_methods(false)
- assert_equal Array, r.class
- assert_true r.include?(:method3)
- assert_true r.include?(:method2)
-end
-
assert('Module#method_defined?', '15.2.2.4.34') do
module Test4MethodDefined
module A
@@ -431,7 +366,6 @@ assert('Module#method_defined?', '15.2.2.4.34') do
assert_false Test4MethodDefined::C.method_defined? "method4"
end
-
assert('Module#module_eval', '15.2.2.4.35') do
module Test4ModuleEval
@a = 11
@@ -442,55 +376,6 @@ assert('Module#module_eval', '15.2.2.4.35') do
assert_equal 12, Test4ModuleEval.module_eval{ @b }
end
-assert('Module#remove_class_variable', '15.2.2.4.39') do
- class Test4RemoveClassVariable
- @@cv = 99
- end
-
- assert_equal 99, Test4RemoveClassVariable.remove_class_variable(:@@cv)
- assert_false Test4RemoveClassVariable.class_variables.include? :@@cv
-end
-
-assert('Module#remove_const', '15.2.2.4.40') do
- module Test4RemoveConst
- ExistingConst = 23
- end
-
- result = Test4RemoveConst.module_eval { remove_const :ExistingConst }
-
- name_error = false
- begin
- Test4RemoveConst.module_eval { remove_const :NonExistingConst }
- rescue NameError
- name_error = true
- end
-
- # Constant removed from Module
- assert_false Test4RemoveConst.const_defined? :ExistingConst
- # Return value of binding
- assert_equal 23, result
- # Name Error raised when Constant doesn't exist
- assert_true name_error
-end
-
-assert('Module#remove_method', '15.2.2.4.41') do
- module Test4RemoveMethod
- class Parent
- def hello
- end
- end
-
- class Child < Parent
- def hello
- end
- end
- end
-
- assert_true Test4RemoveMethod::Child.class_eval{ remove_method :hello }
- assert_true Test4RemoveMethod::Child.instance_methods.include? :hello
- assert_false Test4RemoveMethod::Child.instance_methods(false).include? :hello
-end
-
assert('Module#undef_method', '15.2.2.4.42') do
module Test4UndefMethod
class Parent
@@ -511,7 +396,6 @@ 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
@@ -528,6 +412,15 @@ assert('Module#define_method') do
end
end
+assert 'Module#prepend_features' do
+ mod = Module.new { def m; :mod end }
+ cls = Class.new { def m; :cls end }
+ assert_equal :cls, cls.new.m
+ mod.prepend_features(cls)
+ assert_equal :mod, cls.new.m
+ assert_raise(FrozenError) { Module.new.prepend_features(Class.new.freeze) }
+end
+
# @!group prepend
assert('Module#prepend') do
module M0
@@ -608,41 +501,6 @@ end
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('[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")
@@ -683,12 +541,6 @@ end
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
@@ -699,7 +551,7 @@ end
end
end
- # these assertions will not run without a #assert_seperately method
+ # these assertions will not run without a #assert_separately method
#assert 'test_prepend_optmethod' do
# bug7983 = '[ruby-dev:47124] [Bug #7983]'
# assert_separately [], %{
@@ -715,76 +567,61 @@ end
#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(bug8005) {a.send :foo}
- end
+ # 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(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 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 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")
@@ -807,7 +644,7 @@ end
assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass")
end
- # requires #assert_seperately
+ # requires #assert_separately
#assert 'Module#prepend call super' do
# assert_separately([], <<-'end;') #do
# bug10847 = '[ruby-core:68093] [Bug #10847]'
@@ -818,6 +655,10 @@ end
# end
# end;
#end
+
+ assert 'Module#prepend to frozen class' do
+ assert_raise(FrozenError) { Class.new.freeze.prepend Module.new }
+ end
# @!endgroup prepend
assert('Module#to_s') do
@@ -838,11 +679,12 @@ assert('Module#to_s') do
assert_equal 'SetOuter', SetOuter.to_s
assert_equal 'SetOuter::SetInner', SetOuter::SetInner.to_s
- mod = Module.new
- cls = Class.new
+ assert_match "#<Module:0x*>", Module.new.to_s
+ assert_match "#<Class:0x*>", Class.new.to_s
- assert_equal "#<Module:0x", mod.to_s[0,11]
- assert_equal "#<Class:0x", cls.to_s[0,10]
+ assert_equal "FrozenClassToS", (FrozenClassToS = Class.new.freeze).to_s
+ assert_equal "Outer::A", (Outer::A = Module.new.freeze).to_s
+ assert_match "#<Module:0x*>::A", (Module.new::A = Class.new.freeze).to_s
end
assert('Module#inspect') do
@@ -870,8 +712,8 @@ assert('Issue 1467') do
include M1
end
- C1.new
- C2.new
+ assert_kind_of(M1, C1.new)
+ assert_kind_of(M1, C2.new)
end
assert('clone Module') do
@@ -885,7 +727,7 @@ assert('clone Module') do
include M1.clone
end
- B.new.foo
+ assert_true(B.new.foo)
end
assert('Module#module_function') do
@@ -902,6 +744,15 @@ assert('module with non-class/module outer raises TypeError') do
assert_raise(TypeError) { module []::M2 end }
end
+assert('module to return the last value') do
+ m = module M; :m end
+ assert_equal(m, :m)
+end
+
+assert('module to return nil if body is empty') do
+ assert_nil(module M end)
+end
+
assert('get constant of parent module in singleton class; issue #3568') do
actual = module GetConstantInSingletonTest
EXPECTED = "value"
diff --git a/test/t/numeric.rb b/test/t/numeric.rb
index 38c62a669..bb95f8ef3 100644
--- a/test/t/numeric.rb
+++ b/test/t/numeric.rb
@@ -1,8 +1,21 @@
##
# Numeric ISO Test
+def assert_step(exp, receiver, args, inf: false)
+ act = []
+ ret = receiver.step(*args) do |i|
+ act << i
+ break if inf && exp.size == act.size
+ end
+ expr = "#{receiver.inspect}.step(#{args.map(&:inspect).join(', ')})"
+ assert do
+ assert_true(exp.eql?(act), "#{expr}: counters", assertion_diff(exp, act))
+ assert_same(receiver, ret, "#{expr}: return value") unless inf
+ end
+end
+
assert('Numeric', '15.2.7') do
- assert_equal Class, Numeric.class
+ assert_equal(Class, Numeric.class)
end
assert('Numeric#+@', '15.2.7.4.1') do
@@ -15,15 +28,8 @@ end
assert('Numeric#abs', '15.2.7.4.3') do
assert_equal(1, 1.abs)
- assert_equal(1.0, -1.abs) if class_defined?("Float")
-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)
+ skip unless Object.const_defined?(:Float)
+ assert_equal(1.0, -1.abs)
end
assert('Numeric#/', '15.2.8.3.4') do
@@ -39,5 +45,70 @@ end
# Not ISO specified
assert('Numeric#**') do
- assert_equal 8.0, 2.0**3
+ assert_equal(8, 2 ** 3)
+ assert_equal(-8, -2 ** 3)
+ assert_equal(1, 2 ** 0)
+ skip unless Object.const_defined?(:Float)
+ assert_equal(1.0, 2.2 ** 0)
+ assert_equal(0.5, 2 ** -1)
+ assert_equal(8.0, 2.0**3)
+end
+
+assert('Numeric#step') do
+ assert_raise(ArgumentError) { 1.step(2, 0) { break } }
+ assert_step([2, 3, 4], 2, [4])
+ assert_step([10, 8, 6, 4, 2], 10, [1, -2])
+ assert_step([], 2, [1, 3])
+ assert_step([], -2, [-1, -3])
+ assert_step([10, 11, 12, 13], 10, [], inf: true)
+ assert_step([10, 7, 4], 10, [nil, -3], inf: true)
+
+ skip unless Object.const_defined?(:Float)
+ inf = Float::INFINITY
+ assert_raise(ArgumentError) { 1.step(2, 0.0) { break } }
+ assert_step([2.0, 3.0, 4.0], 2, [4.0])
+ assert_step([7.0, 4.0, 1.0, -2.0], 7, [-4, -3.0])
+ assert_step([2.0, 3.0, 4.0], 2.0, [4])
+ assert_step([10.0, 11.0, 12.0, 13.0], 10.0, [], inf: true)
+ assert_step([10.0, 7.0, 4.0], 10, [nil, -3.0], inf: true)
+ assert_step([1.0], 1, [nil, inf])
+ assert_step([1.0], 1, [nil, -inf])
+ assert_step([1.0], 1, [3, inf])
+ assert_step([], 1, [-3, inf])
+ assert_step([], 1, [3, -inf])
+ assert_step([1.0], 1, [-3, -inf])
+ assert_step([1.0], 1, [inf, inf])
+ assert_step([], 1, [inf, -inf])
+ assert_step([], 1, [-inf, inf])
+ assert_step([1.0], 1, [-inf, -inf])
+ assert_step([], inf, [2])
+ assert_step([], inf, [-2])
+ assert_step([], inf, [2, 3])
+ assert_step([inf, inf, inf], inf, [2, -3], inf: true)
+ assert_step([], inf, [2, inf])
+ assert_step([inf], inf, [2, -inf])
+ assert_step([], inf, [-2, inf])
+ assert_step([inf], inf, [-2, -inf])
+ assert_step([], inf, [-2, 3])
+ assert_step([inf, inf, inf], inf, [-2, -3], inf: true)
+ assert_step([inf], inf, [inf])
+ assert_step([], inf, [-inf])
+ assert_step([inf], inf, [inf, inf])
+ assert_step([inf], inf, [inf, -inf])
+ assert_step([inf], inf, [-inf, -inf])
+ assert_step([-inf, -inf, -inf], -inf, [2], inf: true)
+ assert_step([-inf, -inf, -inf], -inf, [-2], inf: true)
+ assert_step([-inf, -inf, -inf], -inf, [2, 3], inf: true)
+ assert_step([], -inf, [2, -3])
+ assert_step([-inf], -inf, [2, inf])
+ assert_step([], -inf, [2, -inf])
+ assert_step([-inf], -inf, [-2, inf])
+ assert_step([], -inf, [-2, -inf])
+ assert_step([-inf, -inf, -inf], -inf, [-2, 3], inf: true)
+ assert_step([], -inf, [-2, -3])
+ assert_step([-inf, -inf, -inf], -inf, [inf], inf: true)
+ assert_step([-inf], -inf, [-inf])
+ assert_step([-inf], -inf, [inf, inf])
+ assert_step([], -inf, [inf, -inf])
+ assert_step([-inf], -inf, [-inf, -inf])
end
diff --git a/test/t/proc.rb b/test/t/proc.rb
index 42ac3b941..b17b21e8c 100644
--- a/test/t/proc.rb
+++ b/test/t/proc.rb
@@ -157,7 +157,7 @@ assert('&obj call to_proc if defined') do
def mock(&b)
b
end
- assert_equal pr.object_id, mock(&pr).object_id
+ assert_same pr, mock(&pr)
assert_equal pr, mock(&pr)
obj = Object.new
diff --git a/test/t/range.rb b/test/t/range.rb
index 3e67fcc1c..106c2866e 100644
--- a/test/t/range.rb
+++ b/test/t/range.rb
@@ -8,7 +8,8 @@ end
assert('Range#==', '15.2.14.4.1') do
assert_true (1..10) == (1..10)
assert_false (1..10) == (1..100)
- assert_true (1..10) == Range.new(1.0, 10.0) if class_defined?("Float")
+ skip unless Object.const_defined?(:Float)
+ assert_true (1..10) == Range.new(1.0, 10.0)
end
assert('Range#===', '15.2.14.4.2') do
@@ -59,7 +60,7 @@ assert('Range#initialize', '15.2.14.4.9') do
assert_equal (1..10), b
assert_false b.exclude_end?
- assert_raise(NameError) { (0..1).send(:initialize, 1, 3) }
+ assert_raise(NameError) { (0..1).__send__(:initialize, 1, 3) }
end
assert('Range#last', '15.2.14.4.10') do
@@ -93,3 +94,19 @@ assert('Range#eql?', '15.2.14.4.14') do
assert_false (1..10).eql? (Range.new(1.0, 10.0))
assert_false (1..10).eql? "1..10"
end
+
+assert('Range#initialize_copy', '15.2.14.4.15') do
+ assert_raise(NameError) { (0..1).__send__(:initialize_copy, 1..3) }
+end
+
+assert('Range#dup') do
+ r = (1..3).dup
+ assert_equal 1, r.begin
+ assert_equal 3, r.end
+ assert_false r.exclude_end?
+
+ r = ("a"..."z").dup
+ assert_equal "a", r.begin
+ assert_equal "z", r.end
+ assert_true r.exclude_end?
+end
diff --git a/test/t/string.rb b/test/t/string.rb
index e91b915fe..cf145f97e 100644
--- a/test/t/string.rb
+++ b/test/t/string.rb
@@ -2,7 +2,7 @@
##
# String ISO Test
-UTF8STRING = ("\343\201\202".size == 1)
+UTF8STRING = __ENCODING__ == "UTF-8"
assert('String', '15.2.10') do
assert_equal Class, String.class
@@ -37,11 +37,14 @@ end
assert('String#*', '15.2.10.5.5') do
assert_equal 'aaaaa', 'a' * 5
assert_equal '', 'a' * 0
- assert_raise(ArgumentError) do
- 'a' * -1
- end
+ assert_equal 'aa', 'a' * 2.1
+ assert_raise(ArgumentError) { 'a' * -1 }
+ assert_raise(RangeError) { '' * 1e30 }
+ assert_raise(RangeError) { '' * Float::INFINITY }
+ assert_raise(RangeError) { '' * Float::NAN }
+ assert_raise(TypeError) { 'a' * '1' }
+ assert_raise(TypeError) { 'a' * nil }
end
-
assert('String#[]', '15.2.10.5.6') do
# length of args is 1
a = 'abc'[0]
@@ -155,12 +158,15 @@ assert('String#[]=') do
d[-10] = 'X'
end
- if class_defined?("Float")
+ if Object.const_defined?(:Float)
e = 'abc'
e[1.1] = 'X'
assert_equal 'aXc', e
end
+ assert_raise(TypeError) { 'a'[0] = 1 }
+ assert_raise(TypeError) { 'a'[:a] = '1' }
+
# length of args is 2
a1 = 'abc'
assert_raise(IndexError) do
@@ -197,8 +203,62 @@ assert('String#[]=') do
assert_raise(IndexError) do
b3['XX'] = 'Y'
end
+
+ assert_raise(TypeError) { 'a'[:a, 0] = '1' }
+ assert_raise(TypeError) { 'a'[0, :a] = '1' }
+ assert_raise(TypeError) { 'a'[0, 1] = 1 }
end
+assert('String[]=(UTF-8)') do
+ a = "➀➁➂➃➄"
+ a[3] = "⚃"
+ assert_equal "➀➁➂⚃➄", a
+
+ b = "➀➁➂➃➄"
+ b[3, 0] = "⛄"
+ assert_equal "➀➁➂⛄➃➄", b
+
+ c = "➀➁➂➃➄"
+ c[3, 2] = "⚃⚄"
+ assert_equal "➀➁➂⚃⚄", c
+
+ d = "➀➁➂➃➄"
+ d[5] = "⛄"
+ assert_equal "➀➁➂➃➄⛄", d
+
+ e = "➀➁➂➃➄"
+ e[5, 0] = "⛄"
+ assert_equal "➀➁➂➃➄⛄", e
+
+ f = "➀➁➂➃➄"
+ f[5, 2] = "⛄"
+ assert_equal "➀➁➂➃➄⛄", f
+
+ g = "➀➁➂➃➄"
+ assert_raise(IndexError) { g[6] = "⛄" }
+
+ h = "➀➁➂➃➄"
+ assert_raise(IndexError) { h[6, 0] = "⛄" }
+
+ i = "➀➁➂➃➄"
+ assert_raise(IndexError) { i[6, 2] = "⛄" }
+
+ j = "➀➁➂➃➄"
+ j["➃"] = "⚃"
+ assert_equal "➀➁➂⚃➄", j
+
+ k = "➀➁➂➃➄"
+ assert_raise(IndexError) { k["⛄"] = "⛇" }
+
+ l = "➀➁➂➃➄"
+ assert_nothing_raised { l["➂"] = "" }
+ assert_equal "➀➁➃➄", l
+
+ m = "➀➁➂➃➄"
+ assert_raise(TypeError) { m["➂"] = nil }
+ assert_equal "➀➁➂➃➄", m
+end if UTF8STRING
+
assert('String#capitalize', '15.2.10.5.7') do
a = 'abc'
a.capitalize
@@ -253,19 +313,6 @@ assert('String#chomp!', '15.2.10.5.10') do
assert_equal 'abc', e
end
-assert('String#chomp! uses the correct length') do
- class A
- def to_str
- $s.replace("AA")
- "A"
- end
- end
-
- $s = "AAA"
- $s.chomp!(A.new)
- assert_equal $s, "A"
-end
-
assert('String#chop', '15.2.10.5.11') do
a = ''.chop
b = 'abc'.chop
@@ -421,6 +468,17 @@ assert('String#index', '15.2.10.5.22') do
assert_equal nil, "hello".index("", 6)
end
+assert('String#index(UTF-8)', '15.2.10.5.22') do
+ assert_equal 0, '⓿➊➋➌➍➎'.index('⓿')
+ assert_nil '⓿➊➋➌➍➎'.index('➓')
+ assert_equal 6, '⓿➊➋➌➍➎⓿➊➋➌➍➎'.index('⓿', 1)
+ assert_equal 6, "⓿➊➋➌➍➎".index("", 6)
+ assert_equal nil, "⓿➊➋➌➍➎".index("", 7)
+ assert_equal 0, '⓿➊➋➌➍➎'.index("\xe2")
+ assert_equal nil, '⓿➊➋➌➍➎'.index("\xe3")
+ assert_equal 6, "\xd1\xd1\xd1\xd1\xd1\xd1⓿➊➋➌➍➎".index('⓿')
+end if UTF8STRING
+
assert('String#initialize', '15.2.10.5.23') do
a = ''
a.initialize('abc')
@@ -477,12 +535,12 @@ assert('String#reverse', '15.2.10.5.29') do
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 "世", "こんにちは世界"["世"]
+ a = 'こんにちは世界!'
+ a.reverse
+
+ assert_equal 'こんにちは世界!', a
+ assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse
+ assert_equal 'あ', 'あ'.reverse
end if UTF8STRING
assert('String#reverse!', '15.2.10.5.30') do
@@ -499,6 +557,10 @@ assert('String#reverse!(UTF-8)', '15.2.10.5.30') do
assert_equal '!界世はちにんこ', a
assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse!
+
+ b = 'あ'
+ b.reverse!
+ assert_equal 'あ', b
end if UTF8STRING
assert('String#rindex', '15.2.10.5.31') do
@@ -516,7 +578,9 @@ assert('String#rindex(UTF-8)', '15.2.10.5.31') do
assert_equal nil, str.index("さ")
end if UTF8STRING
-# 'String#scan', '15.2.10.5.32' will be tested in mrbgems.
+# assert('String#scan', '15.2.10.5.32') do
+# # Not implemented yet
+# end
assert('String#size', '15.2.10.5.33') do
assert_equal 3, 'abc'.size
@@ -592,7 +656,7 @@ assert('String#sub', '15.2.10.5.36') do
str = "abc"
miss = str.sub("X", "Z")
assert_equal str, miss
- assert_not_equal str.object_id, miss.object_id
+ assert_not_same str, miss
a = []
assert_equal '.abc', "abc".sub("") { |i| a << i; "." }
@@ -631,7 +695,7 @@ assert('String#to_f', '15.2.10.5.38') do
assert_float(12345.6789, c)
assert_float(0, d)
assert_float(Float::INFINITY, e)
-end if class_defined?("Float")
+end if Object.const_defined?(:Float)
assert('String#to_i', '15.2.10.5.39') do
a = ''.to_i
@@ -696,10 +760,6 @@ assert('String interpolation (mrb_str_concat for shared strings)') do
assert_equal "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:", "#{a}:"
end
-assert('Check the usage of a NUL character') do
- "qqq\0ppp"
-end
-
assert('String#bytes') do
str1 = "hello"
bytes1 = [104, 101, 108, 108, 111]
@@ -725,5 +785,11 @@ assert('String#freeze') do
str = "hello"
str.freeze
- assert_raise(RuntimeError) { str.upcase! }
+ assert_raise(FrozenError) { str.upcase! }
+end
+
+assert('String literal concatenation') do
+ assert_equal 2, ("A" "B").size
+ assert_equal 3, ('A' "B" 'C').size
+ assert_equal 4, (%(A) "B#{?C}" "D").size
end
diff --git a/test/t/symbol.rb b/test/t/symbol.rb
index 9059f45c2..5c674a9cb 100644
--- a/test/t/symbol.rb
+++ b/test/t/symbol.rb
@@ -13,8 +13,8 @@ assert('Symbol', '15.2.11') do
end
assert('Symbol#===', '15.2.11.3.1') do
- assert_true :abc == :abc
- assert_false :abc == :cba
+ assert_true :abc === :abc
+ assert_false :abc === :cba
end
assert('Symbol#id2name', '15.2.11.3.2') do
@@ -28,3 +28,7 @@ end
assert('Symbol#to_sym', '15.2.11.3.4') do
assert_equal :abc, :abc.to_sym
end
+
+assert('Symbol#to_proc') do
+ assert_equal 5, :abs.to_proc[-5]
+end
diff --git a/test/t/syntax.rb b/test/t/syntax.rb
index 299394557..afb735e7f 100644
--- a/test/t/syntax.rb
+++ b/test/t/syntax.rb
@@ -403,6 +403,9 @@ assert('External command execution.') do
assert_equal 'test dynamic `', t
assert_equal ['test', 'test dynamic `', 'test', 'test dynamic `'], results
+ results = []
+ assert_equal 'test sym test sym test', `test #{:sym} test #{:sym} test`
+
alias_method sym, :old_cmd
end
true
@@ -420,10 +423,11 @@ assert('parenthesed do-block in cmdarg') do
end
assert('method definition in cmdarg') do
- if false
+ result = class MethodDefinitionInCmdarg
+ def self.bar(arg); arg end
bar def foo; self.each do end end
end
- true
+ assert_equal(:foo, result)
end
assert('optional argument in the rhs default expressions') do
@@ -451,18 +455,203 @@ assert('multiline comments work correctly') do
=begin
this is a comment with nothing after begin and end
=end
-=begin this is a comment
+=begin this is a comment
this is a comment with extra after =begin
=end
=begin
this is a comment that has =end with spaces after it
-=end
+=end
=begin this is a comment
this is a comment that has extra after =begin and =end with spaces after it
-=end
+=end
line = __LINE__
=begin this is a comment
this is a comment that has extra after =begin and =end with tabs after it
=end xxxxxxxxxxxxxxxxxxxxxxxxxx
assert_equal(line + 4, __LINE__)
end
+
+assert 'keyword arguments' do
+ def m(a, b:1) [a, b] end
+ assert_equal [1, 1], m(1)
+ assert_equal [1, 2], m(1, b: 2)
+
+ def m(a, b:) [a, b] end
+ assert_equal [1, 2], m(1, b: 2)
+ assert_raise(ArgumentError) { m b: 1 }
+ assert_raise(ArgumentError) { m 1 }
+
+ def m(a:) a end
+ assert_equal 1, m(a: 1)
+ assert_raise(ArgumentError) { m }
+ assert_raise(ArgumentError) { m 'a' => 1, a: 1 }
+ h = { a: 1 }
+ assert_equal 1, m(h)
+ assert_equal({ a: 1 }, h)
+
+ def m(a: 1) a end
+ assert_equal 1, m
+ assert_equal 2, m(a: 2)
+ assert_raise(ArgumentError) { m 1 }
+
+ def m(**) end
+ assert_nil m
+ assert_nil m a: 1, b: 2
+ assert_raise(ArgumentError) { m 2 }
+
+ def m(a, **) a end
+ assert_equal 1, m(1)
+ assert_equal 1, m(1, a: 2, b: 3)
+ assert_equal({ 'a' => 1, b: 2 }, m('a' => 1, b: 2))
+
+ def m(a, **k) [a, k] end
+ assert_equal [1, {}], m(1)
+ assert_equal [1, {a: 2, b: 3}], m(1, a: 2, b: 3)
+ assert_equal [{'a' => 1, b: 2}, {}], m('a' => 1, b: 2)
+
+ def m(a=1, **) a end
+ assert_equal 1, m
+ assert_equal 2, m(2, a: 1, b: 0)
+ assert_raise(ArgumentError) { m('a' => 1, a: 2) }
+
+ def m(a=1, **k) [a, k] end
+ assert_equal [1, {}], m
+ assert_equal [1, {a: 1}], m(a: 1)
+ assert_equal [2, {a: 1, b: 2}], m(2, a: 1, b: 2)
+ assert_equal [{a: 1}, {b: 2}], m({a: 1}, {b: 2})
+
+ def m(*, a:) a end
+ assert_equal 1, m(a: 1)
+ assert_equal 3, m(1, 2, a: 3)
+ assert_raise(ArgumentError) { m('a' => 1, a: 2) }
+
+ def m(*a, b:) [a, b] end
+ assert_equal [[], 1], m(b: 1)
+ assert_equal [[1, 2], 3], m(1, 2, b: 3)
+ assert_raise(ArgumentError) { m('a' => 1, b: 2) }
+
+ def m(*a, b: 1) [a, b] end
+ assert_equal [[], 1], m
+ assert_equal [[1, 2, 3], 4], m(1, 2, 3, b: 4)
+ assert_raise(ArgumentError) { m('a' => 1, b: 2) }
+
+ def m(*, **) end
+ assert_nil m()
+ assert_nil m(a: 1, b: 2)
+ assert_nil m(1, 2, 3, a: 4, b: 5)
+
+ def m(*a, **) a end
+ assert_equal [], m()
+ assert_equal [1, 2, 3], m(1, 2, 3, a: 4, b: 5)
+ assert_raise(ArgumentError) { m("a" => 1, a: 1) }
+ assert_equal [1], m(1, **{a: 2})
+
+ def m(*, **k) k end
+ assert_equal({}, m())
+ assert_equal({a: 4, b: 5}, m(1, 2, 3, a: 4, b: 5))
+ assert_raise(ArgumentError) { m("a" => 1, a: 1) }
+
+ def m(a = nil, b = nil, **k) [a, k] end
+ assert_equal [nil, {}], m()
+ assert_equal([nil, {a: 1}], m(a: 1))
+ assert_raise(ArgumentError) { m("a" => 1, a: 1) }
+ assert_equal([{"a" => 1}, {a: 1}], m({ "a" => 1 }, a: 1))
+ assert_equal([{a: 1}, {}], m({a: 1}, {}))
+ assert_equal([nil, {}], m({}))
+
+ def m(*a, **k) [a, k] end
+ assert_equal([[], {}], m())
+ assert_equal([[1], {}], m(1))
+ assert_equal([[], {a: 1, b: 2}], m(a: 1, b: 2))
+ assert_equal([[1, 2, 3], {a: 2}], m(1, 2, 3, a: 2))
+ assert_raise(ArgumentError) { m("a" => 1, a: 1) }
+ assert_raise(ArgumentError) { m("a" => 1) }
+ assert_equal([[], {a: 1}], m(a: 1))
+ assert_raise(ArgumentError) { m("a" => 1, a: 1) }
+ assert_equal([[{"a" => 1}], {a: 1}], m({ "a" => 1 }, a: 1))
+ assert_equal([[{a: 1}], {}], m({a: 1}, {}))
+ assert_raise(ArgumentError) { m({a: 1}, {"a" => 1}) }
+
+ def m(a:, b:) [a, b] end
+ assert_equal([1, 2], m(a: 1, b: 2))
+ assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
+
+ def m(a:, b: 1) [a, b] end
+ assert_equal([1, 1], m(a: 1))
+ assert_equal([1, 2], m(a: 1, b: 2))
+ assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
+
+ def m(a:, **) a end
+ assert_equal(1, m(a: 1))
+ assert_equal(1, m(a: 1, b: 2))
+ assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
+
+ def m(a:, **k) [a, k] end
+ assert_equal([1, {}], m(a: 1))
+ assert_equal([1, {b: 2, c: 3}], m(a: 1, b: 2, c: 3))
+ assert_raise(ArgumentError) { m("a" => 1, a: 1, b: 2) }
+
+=begin
+ def m(a:, &b) [a, b] end
+ assert_equal([1, nil], m(a: 1))
+ assert_equal([1, l], m(a: 1, &(l = ->{})))
+=end
+
+ def m(a: 1, b:) [a, b] end
+ assert_equal([1, 0], m(b: 0))
+ assert_equal([3, 2], m(b: 2, a: 3))
+ assert_raise(ArgumentError) { m a: 1 }
+
+ def m(a: def m(a: 1) a end, b:)
+ [a, b]
+ end
+ assert_equal([2, 3], m(a: 2, b: 3))
+ assert_equal([:m, 1], m(b: 1))
+ # Note the default value of a: in the original method.
+ assert_equal(1, m())
+
+ def m(a: 1, b: 2) [a, b] end
+ assert_equal([1, 2], m())
+ assert_equal([4, 3], m(b: 3, a: 4))
+
+ def m(a: 1, **) a end
+ assert_equal(1, m())
+ assert_equal(2, m(a: 2, b: 1))
+
+ def m(a: 1, **k) [a, k] end
+ assert_equal([1, {b: 2, c: 3}], m(b: 2, c: 3))
+
+ def m(a:, **) yield end
+ assert_raise(ArgumentError) { m { :blk } }
+ assert_equal :blk, m(a: 1){ :blk }
+
+ def m(a:, **k, &b) [b.call, k] end
+ assert_raise(ArgumentError) { m { :blk } }
+ assert_equal [:blk, {b: 2}], m(a: 1, b: 2){ :blk }
+
+ def m(**k, &b) [k, b] end
+ assert_equal([{ a: 1, b: 2}, nil], m(a: 1, b: 2))
+ assert_equal :blk, m{ :blk }[1].call
+
+ def m(hsh = {}) hsh end
+ assert_equal({ a: 1, b: 2 }, m(a: 1, b: 2))
+ assert_equal({ a: 1, 'b' => 2 }, m(a: 1, 'b' => 2))
+
+ def m(hsh) hsh end
+ assert_equal({ a: 1, b: 2 }, m(a: 1, b: 2))
+ assert_equal({ a: 1, 'b' => 2 }, m(a: 1, 'b' => 2))
+
+=begin
+ def m(a, b=1, *c, (*d, (e)), f: 2, g:, h:, **k, &l)
+ [a, b, c, d, e, f, g, h, k, l]
+ end
+ result = m(9, 8, 7, 6, f: 5, g: 4, h: 3, &(l = ->{}))
+ assert_equal([9, 8, [7], [], 6, 5, 4, 3, {}, l], result)
+
+ def m a, b=1, *c, d, e:, f: 2, g:, **k, &l
+ [a, b, c, d, e, f, g, k, l]
+ end
+ result = m(1, 2, e: 3, g: 4, h: 5, i: 6, &(l = ->{}))
+ assert_equal([1, 1, [], 2, 3, 2, 4, { h: 5, i: 6 }, l], result)
+=end
+end
diff --git a/travis_config.rb b/travis_config.rb
index 5904d6880..7a13ced72 100644
--- a/travis_config.rb
+++ b/travis_config.rb
@@ -18,7 +18,7 @@ MRuby::Build.new('full-debug') do |conf|
# include all core GEMs
conf.gembox 'full-core'
- conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK)
+ conf.cc.defines += %w(MRB_ENABLE_DEBUG_HOOK)
conf.enable_test
end
@@ -40,7 +40,7 @@ MRuby::Build.new('cxx_abi') do |conf|
toolchain :gcc
conf.gembox 'full-core'
- conf.cc.flags += %w(-Werror=declaration-after-statement)
+ conf.cc.flags += %w(-Werror=declaration-after-statement -fpermissive)
conf.compilers.each do |c|
c.defines += %w(MRB_GC_FIXED_ARENA)
end