summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md12
-rw-r--r--appveyor_config.rb2
-rw-r--r--doc/guides/mrbconf.md56
-rw-r--r--include/mrbconf.h83
-rw-r--r--include/mruby.h16
-rw-r--r--include/mruby/boxing_word.h1
-rw-r--r--include/mruby/class.h1
-rw-r--r--include/mruby/compile.h8
-rw-r--r--include/mruby/data.h2
-rw-r--r--include/mruby/dump.h6
-rw-r--r--include/mruby/irep.h12
-rw-r--r--include/mruby/istruct.h4
-rw-r--r--include/mruby/numeric.h14
-rw-r--r--include/mruby/range.h10
-rw-r--r--include/mruby/string.h24
-rw-r--r--include/mruby/value.h5
-rw-r--r--include/mruby/variable.h1
-rw-r--r--lib/mruby/build.rb23
-rw-r--r--lib/mruby/build/command.rb35
-rw-r--r--mrbgems/default.gembox3
-rw-r--r--mrbgems/mruby-array-ext/mrbgem.rake1
-rw-r--r--mrbgems/mruby-array-ext/mrblib/array.rb4
-rw-r--r--mrbgems/mruby-array-ext/src/array.c4
-rw-r--r--mrbgems/mruby-array-ext/test/array.rb52
-rw-r--r--mrbgems/mruby-bin-debugger/tools/mrdb/mrdbconf.h4
-rw-r--r--mrbgems/mruby-bin-mruby/bintest/mruby.rb58
-rw-r--r--mrbgems/mruby-bin-mruby/tools/mruby/mruby.c41
-rw-r--r--mrbgems/mruby-class-ext/src/class.c3
-rw-r--r--mrbgems/mruby-compiler/core/parse.y300
-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.c154
-rw-r--r--mrbgems/mruby-complex/test/complex.rb136
-rw-r--r--mrbgems/mruby-enum-chain/mrblib/chain.rb25
-rw-r--r--mrbgems/mruby-enum-chain/test/enum_chain.rb39
-rw-r--r--mrbgems/mruby-enum-ext/mrblib/enum.rb4
-rw-r--r--mrbgems/mruby-enum-ext/test/enum.rb3
-rw-r--r--mrbgems/mruby-enumerator/mrbgem.rake1
-rw-r--r--mrbgems/mruby-enumerator/test/enumerator.rb15
-rw-r--r--mrbgems/mruby-eval/src/eval.c4
-rw-r--r--mrbgems/mruby-eval/test/eval.rb4
-rw-r--r--mrbgems/mruby-fiber/test/fiber.rb2
-rw-r--r--mrbgems/mruby-io/src/file.c23
-rw-r--r--mrbgems/mruby-io/src/file_test.c21
-rw-r--r--mrbgems/mruby-io/src/io.c4
-rw-r--r--mrbgems/mruby-io/test/file.rb15
-rw-r--r--mrbgems/mruby-io/test/file_test.rb13
-rw-r--r--mrbgems/mruby-io/test/io.rb63
-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.c19
-rw-r--r--mrbgems/mruby-kernel-ext/test/kernel.rb2
-rw-r--r--mrbgems/mruby-math/src/math.c4
-rw-r--r--mrbgems/mruby-metaprog/src/metaprog.c36
-rw-r--r--mrbgems/mruby-metaprog/test/metaprog.rb41
-rw-r--r--mrbgems/mruby-method/src/method.c8
-rw-r--r--mrbgems/mruby-method/test/method.rb2
-rw-r--r--mrbgems/mruby-numeric-ext/mrblib/numeric_ext.rb6
-rw-r--r--mrbgems/mruby-numeric-ext/src/numeric_ext.c27
-rw-r--r--mrbgems/mruby-numeric-ext/test/numeric.rb8
-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.c35
-rw-r--r--mrbgems/mruby-object-ext/test/nil.rb4
-rw-r--r--mrbgems/mruby-objectspace/test/objectspace.rb2
-rw-r--r--mrbgems/mruby-pack/src/pack.c30
-rw-r--r--mrbgems/mruby-pack/test/pack.rb92
-rw-r--r--mrbgems/mruby-proc-ext/src/proc.c4
-rw-r--r--mrbgems/mruby-proc-ext/test/proc.rb26
-rw-r--r--mrbgems/mruby-random/src/mt19937ar.c224
-rw-r--r--mrbgems/mruby-random/src/mt19937ar.h80
-rw-r--r--mrbgems/mruby-random/src/random.c281
-rw-r--r--mrbgems/mruby-range-ext/mrblib/range.rb38
-rw-r--r--mrbgems/mruby-range-ext/src/range.c23
-rw-r--r--mrbgems/mruby-range-ext/test/range.rb112
-rw-r--r--mrbgems/mruby-rational/mrbgem.rake5
-rw-r--r--mrbgems/mruby-rational/mrblib/rational.rb117
-rw-r--r--mrbgems/mruby-rational/src/rational.c209
-rw-r--r--mrbgems/mruby-rational/test/rational.rb308
-rw-r--r--mrbgems/mruby-socket/src/socket.c9
-rw-r--r--mrbgems/mruby-string-ext/mrblib/string.rb12
-rw-r--r--mrbgems/mruby-string-ext/src/string.c230
-rw-r--r--mrbgems/mruby-string-ext/test/numeric.rb29
-rw-r--r--mrbgems/mruby-string-ext/test/range.rb26
-rw-r--r--mrbgems/mruby-string-ext/test/string.rb83
-rw-r--r--mrbgems/mruby-struct/mrblib/struct.rb12
-rw-r--r--mrbgems/mruby-struct/src/struct.c69
-rw-r--r--mrbgems/mruby-struct/test/struct.rb27
-rw-r--r--mrbgems/mruby-symbol-ext/test/symbol.rb2
-rw-r--r--mrbgems/mruby-test/driver.c1
-rw-r--r--mrbgems/mruby-time/include/mruby/time.h3
-rw-r--r--mrbgems/mruby-time/src/time.c242
-rw-r--r--mrbgems/mruby-time/test/time.rb126
-rw-r--r--mrblib/array.rb12
-rw-r--r--mrblib/enum.rb16
-rw-r--r--mrblib/hash.rb12
-rw-r--r--mrblib/kernel.rb2
-rw-r--r--mrblib/numeric.rb8
-rw-r--r--mrblib/range.rb2
-rw-r--r--mrblib/string.rb91
-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.c66
-rw-r--r--src/backtrace.c2
-rw-r--r--src/class.c190
-rw-r--r--src/etc.c26
-rw-r--r--src/gc.c25
-rw-r--r--src/hash.c22
-rw-r--r--src/kernel.c3
-rw-r--r--src/load.c111
-rw-r--r--src/numeric.c181
-rw-r--r--src/object.c6
-rw-r--r--src/range.c14
-rw-r--r--src/state.c33
-rw-r--r--src/string.c541
-rw-r--r--src/symbol.c70
-rw-r--r--src/variable.c30
-rw-r--r--src/vm.c26
-rw-r--r--test/assert.rb117
-rw-r--r--test/t/array.rb3
-rw-r--r--test/t/enumerable.rb4
-rw-r--r--test/t/hash.rb2
-rw-r--r--test/t/kernel.rb34
-rw-r--r--test/t/module.rb61
-rw-r--r--test/t/numeric.rb88
-rw-r--r--test/t/range.rb8
-rw-r--r--test/t/string.rb130
-rw-r--r--test/t/syntax.rb5
-rw-r--r--travis_config.rb2
136 files changed, 4920 insertions, 2009 deletions
diff --git a/README.md b/README.md
index 3e71ef7ca..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.
@@ -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](LICENSE).
+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/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/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/include/mrbconf.h b/include/mrbconf.h
index 08e69d3aa..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,6 +63,9 @@
# 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) || \
@@ -88,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
@@ -146,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 dcd64b2d8..7deb3cbe2 100644
--- a/include/mruby.h
+++ b/include/mruby.h
@@ -84,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>
@@ -228,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
@@ -837,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. |
*
@@ -1247,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/boxing_word.h b/include/mruby/boxing_word.h
index 2fb84052a..f16968a1f 100644
--- a/include/mruby/boxing_word.h
+++ b/include/mruby/boxing_word.h
@@ -141,7 +141,6 @@ mrb_type(mrb_value o)
#define SET_OBJ_VALUE(r,v) do { \
(r).w = 0; \
(r).value.p = (v); \
- if ((r).value.bp) (r).value.bp->tt = ((struct RObject*)(v))->tt; \
} while (0)
#endif /* MRUBY_BOXING_WORD_H */
diff --git a/include/mruby/class.h b/include/mruby/class.h
index a68724538..c79a487b5 100644
--- a/include/mruby/class.h
+++ b/include/mruby/class.h
@@ -88,6 +88,7 @@ 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/compile.h b/include/mruby/compile.h
index f19d9b0b3..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;
@@ -67,7 +67,7 @@ enum mrb_lex_state_enum {
/* saved error message */
struct mrb_parser_message {
- int lineno;
+ uint16_t lineno;
int column;
char* message;
};
@@ -105,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 */
@@ -119,7 +119,7 @@ struct mrb_parser_state {
#endif
mrbc_context *cxt;
mrb_sym filename_sym;
- int lineno;
+ uint16_t lineno;
int column;
enum mrb_lex_state_enum lstate;
diff --git a/include/mruby/data.h b/include/mruby/data.h
index d85edeb3a..54466dcd6 100644
--- a/include/mruby/data.h
+++ b/include/mruby/data.h
@@ -41,7 +41,7 @@ MRB_API struct RData *mrb_data_object_alloc(mrb_state *mrb, struct RClass* klass
#define Data_Make_Struct(mrb,klass,strct,type,sval,data_obj) do { \
(data_obj) = Data_Wrap_Struct(mrb,klass,type,NULL);\
- (sval) = mrb_malloc(mrb, sizeof(strct)); \
+ (sval) = (strct *)mrb_malloc(mrb, sizeof(strct)); \
{ static const strct zero = { 0 }; *(sval) = zero; };\
(data_obj)->data = (sval);\
} while (0)
diff --git a/include/mruby/dump.h b/include/mruby/dump.h
index 0234a362b..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
*
@@ -60,7 +61,6 @@ MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*);
#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/irep.h b/include/mruby/irep.h
index 027a294d5..d42fd0fb8 100644
--- a/include/mruby/irep.h
+++ b/include/mruby/irep.h
@@ -52,9 +52,21 @@ 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*);
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..07266aa10 100644
--- a/include/mruby/numeric.h
+++ b/include/mruby/numeric.h
@@ -23,7 +23,11 @@ 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)
+#ifdef MRB_INT64
+#define FIXABLE_FLOAT(f) ((f)>=-9223372036854775808.0 && (f)<9223372036854775808.0)
+#else
+#define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,mrb_float)
+#endif
#endif
#ifndef MRB_WITHOUT_FLOAT
@@ -34,12 +38,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/range.h b/include/mruby/range.h
index b5626993a..500a941d5 100644
--- a/include/mruby/range.h
+++ b/include/mruby/range.h
@@ -14,7 +14,7 @@
*/
MRB_BEGIN_DECL
-#if defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING)
+#if defined(MRB_NAN_BOXING) && defined(MRB_64BIT) || defined(MRB_WORD_BOXING)
# define MRB_RANGE_EMBED
#endif
@@ -64,7 +64,13 @@ MRB_API struct RRange* mrb_range_ptr(mrb_state *mrb, mrb_value range);
*/
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);
diff --git a/include/mruby/string.h b/include/mruby/string.h
index 22445f654..d17ac1c1d 100644
--- a/include/mruby/string.h
+++ b/include/mruby/string.h
@@ -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,16 @@ 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(mrb_state *mrb, struct RString *s);
+/* mrb_str_modify() with keeping ASCII flag if set */
+MRB_API void mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s);
/*
* Finds the index of a substring in a string
@@ -438,6 +455,9 @@ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str);
#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);
+
#ifdef MRB_UTF8_STRING
mrb_int mrb_utf8_len(const char *str, mrb_int byte_len);
#endif
diff --git a/include/mruby/value.h b/include/mruby/value.h
index 6838daaa5..be3dd397f 100644
--- a/include/mruby/value.h
+++ b/include/mruby/value.h
@@ -278,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 ba6037959..ff01e5cc8 100644
--- a/include/mruby/variable.h
+++ b/include/mruby/variable.h
@@ -117,6 +117,7 @@ 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);
diff --git a/lib/mruby/build.rb b/lib/mruby/build.rb
index 016b32b3e..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
@@ -196,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
@@ -255,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
@@ -263,7 +262,7 @@ 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
diff --git a/lib/mruby/build/command.rb b/lib/mruby/build/command.rb
index 0f18e0e62..d4354225e 100644
--- a/lib/mruby/build/command.rb
+++ b/lib/mruby/build/command.rb
@@ -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/mrbgems/default.gembox b/mrbgems/default.gembox
index 23e65fcee..9859c7d52 100644
--- a/mrbgems/default.gembox
+++ b/mrbgems/default.gembox
@@ -86,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/mrbgem.rake b/mrbgems/mruby-array-ext/mrbgem.rake
index 58d4428d4..882caf1ab 100644
--- a/mrbgems/mruby-array-ext/mrbgem.rake
+++ b/mrbgems/mruby-array-ext/mrbgem.rake
@@ -2,5 +2,4 @@ MRuby::Gem::Specification.new('mruby-array-ext') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'Array class extension'
- spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator'
end
diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb
index 59b6087d2..1cd1eb643 100644
--- a/mrbgems/mruby-array-ext/mrblib/array.rb
+++ b/mrbgems/mruby-array-ext/mrblib/array.rb
@@ -903,8 +903,8 @@ class Array
column_count = nil
self.each do |row|
raise TypeError unless row.is_a?(Array)
- column_count ||= row.count
- raise IndexError, 'element size differs' unless column_count == row.count
+ column_count ||= row.size
+ raise IndexError, 'element size differs' unless column_count == row.size
end
Array.new(column_count) do |column_index|
diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c
index b6d9c9c80..cb4798d49 100644
--- a/mrbgems/mruby-array-ext/src/array.c
+++ b/mrbgems/mruby-array-ext/src/array.c
@@ -145,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, ARY_LEN(a), TRUE) == 1) {
+ if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) {
goto delete_pos_len;
}
else {
@@ -194,7 +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, "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 853554bcc..4fad42518 100644
--- a/mrbgems/mruby-array-ext/test/array.rb
+++ b/mrbgems/mruby-array-ext/test/array.rb
@@ -1,6 +1,20 @@
##
# Array(Ext) Test
+def assert_permutation_combination(exp, receiver, meth, *args)
+ act = []
+ receiver.__send__(meth, *args) { |v| act << v }
+ assert_equal(exp, act.sort)
+end
+
+def assert_permutation(exp, receiver, *args)
+ assert_permutation_combination(exp, receiver, :permutation, *args)
+end
+
+def assert_combination(exp, receiver, *args)
+ assert_permutation_combination(exp, receiver, :combination, *args)
+end
+
assert("Array#assoc") do
s1 = [ "colors", "red", "blue", "green" ]
s2 = [ "letters", "a", "b", "c" ]
@@ -268,9 +282,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]
@@ -369,30 +383,22 @@ end
assert("Array#permutation") do
a = [1, 2, 3]
- assert_equal([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]],
- a.permutation.to_a)
- assert_equal([[1],[2],[3]],
- a.permutation(1).to_a)
- assert_equal([[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]],
- a.permutation(2).to_a)
- assert_equal([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]],
- a.permutation(3).to_a)
- assert_equal([[]], a.permutation(0).to_a)
- assert_equal([], a.permutation(4).to_a)
+ assert_permutation([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a)
+ assert_permutation([[1],[2],[3]], a, 1)
+ assert_permutation([[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]], a, 2)
+ assert_permutation([[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]], a, 3)
+ assert_permutation([[]], a, 0)
+ assert_permutation([], a, 4)
end
assert("Array#combination") do
a = [1, 2, 3, 4]
- assert_equal([[1],[2],[3],[4]],
- a.combination(1).to_a)
- assert_equal([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]],
- a.combination(2).to_a)
- assert_equal([[1,2,3],[1,2,4],[1,3,4],[2,3,4]],
- a.combination(3).to_a)
- assert_equal([[1,2,3,4]],
- a.combination(4).to_a)
- assert_equal([[]], a.combination(0).to_a)
- assert_equal([], a.combination(5).to_a)
+ assert_combination([[1],[2],[3],[4]], a, 1)
+ assert_combination([[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]], a, 2)
+ assert_combination([[1,2,3],[1,2,4],[1,3,4],[2,3,4]], a, 3)
+ assert_combination([[1,2,3,4]], a, 4)
+ assert_combination([[]], a, 0)
+ assert_combination([], a, 5)
end
assert('Array#transpose') do
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-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
index e8d1510f7..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
@@ -66,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
@@ -73,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
@@ -95,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/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
index 498bedef2..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;
@@ -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,7 @@ 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;
@@ -289,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-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-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y
index 7838b6dfb..f6d543c5d 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)
{
@@ -221,7 +233,7 @@ parser_strdup(parser_state *p, const char *s)
#define strdup(s) parser_strdup(p, s)
static void
-dump_int(short i, char *s)
+dump_int(uint16_t i, char *s)
{
char *p = s;
char *t = s;
@@ -842,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
@@ -913,40 +963,39 @@ concat_string(parser_state *p, node *a, node *b)
}
}
}
- else if (string_node_p(b)) {
- /* a == NODE_DSTR && b == NODE_STR */
-
- node *c;
+ else {
+ node *c; /* last node of a */
for (c = a; c->cdr != NULL; c = c->cdr) ;
- 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(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;
+ }
- node *c, *d;
- for (c = a; c->cdr != NULL; c = c->cdr) ;
- if (string_node_p(c->car) && string_node_p(b->cdr->car)) {
- /* a->[..., NODE_STR] && b->[NODE_STR, ...] */
- 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);
+ push(a, b);
return a;
}
else {
- c->cdr = b->cdr;
- cons_free(b);
- return a;
+ /* 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;
+ }
}
}
@@ -3193,7 +3242,7 @@ var_ref : variable
char buf[16];
dump_int(p->lineno, buf);
- $$ = new_int(p, buf, 10);
+ $$ = new_int(p, buf, 10, 0);
}
| keyword__ENCODING__
{
@@ -4521,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)
@@ -4641,52 +4729,66 @@ 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++;
+ 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, '#');
+ p->lineno++;
goto retry;
+ case '.':
+ if (!peek(p, '.')) {
+ pushback(p, '.');
+ p->lineno++;
+ goto retry;
+ }
+ pushback(p, c);
+ goto normal_newline;
+ case '&':
+ if (peek(p, '.')) {
+ pushback(p, '&');
+ p->lineno++;
+ 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)) == '*') {
@@ -5095,6 +5197,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;
@@ -5128,7 +5231,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') {
@@ -5152,7 +5258,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') {
@@ -5176,7 +5285,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 == '_') {
@@ -5209,7 +5321,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) {
@@ -5226,7 +5341,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;
}
}
@@ -5299,8 +5417,8 @@ parser_yylex(parser_state *p)
tokfix(p);
if (is_float) {
#ifdef MRB_WITHOUT_FLOAT
- yywarning(p, "floating point numbers are not supported");
- pylval.nd = new_int(p, "0", 10);
+ yywarning_s(p, "floating point numbers are not supported", tok(p));
+ pylval.nd = new_int(p, "0", 10, 0);
return tINTEGER;
#else
double d;
@@ -5315,11 +5433,17 @@ parser_yylex(parser_state *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;
}
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..1a6f7e92e
--- /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
+
+ [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
+end
diff --git a/mrbgems/mruby-complex/src/complex.c b/mrbgems/mruby-complex/src/complex.c
new file mode 100644
index 000000000..aa2afc2ce
--- /dev/null
+++ b/mrbgems/mruby-complex/src/complex.c
@@ -0,0 +1,154 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+
+#ifdef MRB_WITHOUT_FLOAT
+# error Complex conflicts 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
+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);
+}
+
+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);
+}
+
+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"));
+#ifdef COMPLEX_USE_ISTRUCT
+ MRB_SET_INSTANCE_TT(comp, MRB_TT_ISTRUCT);
+#else
+ MRB_SET_INSTANCE_TT(comp, MRB_TT_DATA);
+#endif
+ 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());
+ mrb_define_method(mrb, comp, "to_f", complex_to_f, MRB_ARGS_NONE());
+ 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/mrblib/chain.rb b/mrbgems/mruby-enum-chain/mrblib/chain.rb
index 98515ea14..55474eb92 100644
--- a/mrbgems/mruby-enum-chain/mrblib/chain.rb
+++ b/mrbgems/mruby-enum-chain/mrblib/chain.rb
@@ -6,13 +6,13 @@ module Enumerable
def chain(*args)
Enumerator::Chain.new(self, *args)
end
+end
+class Enumerator
def +(other)
- Enumerator::Chain.new(self, other)
+ Chain.new(self, other)
end
-end
-class Enumerator
class Chain
include Enumerable
@@ -20,10 +20,6 @@ class Enumerator
@enums = args
end
- def initialize_copy(orig)
- @enums = orig.__copy_enums
- end
-
def each(&block)
return to_enum unless block_given?
@@ -40,21 +36,22 @@ class Enumerator
end
def rewind
- @enums.reverse_each do |e|
+ i = @enums.size - 1
+ while 0 <= i
+ e = @enums[i]
e.rewind if e.respond_to?(:rewind)
+ i -= 1
end
self
end
- def inspect
- "#<#{self.class}: #{@enums.inspect}>"
+ def +(other)
+ self.class.new(self, other)
end
- def __copy_enums
- @enums.each_with_object([]) do |e, a|
- a << e.clone
- end
+ def inspect
+ "#<#{self.class}: #{@enums.inspect}>"
end
end
end
diff --git a/mrbgems/mruby-enum-chain/test/enum_chain.rb b/mrbgems/mruby-enum-chain/test/enum_chain.rb
index 4dd59bd37..1d3d691ca 100644
--- a/mrbgems/mruby-enum-chain/test/enum_chain.rb
+++ b/mrbgems/mruby-enum-chain/test/enum_chain.rb
@@ -12,9 +12,9 @@ assert("Enumerable#chain") do
assert_raise(NoMethodError) { c.chain }
end
-assert("Enumerable#+") do
+assert("Enumerator#+") do
a = [].each
- b = {}.reverse_each
+ b = {}.each
c = Object.new # not has #each method
assert_kind_of Enumerator::Chain, a + b
@@ -24,7 +24,7 @@ assert("Enumerable#+") do
assert_raise(NoMethodError) { c + a }
end
-assert("Enumerator.new") do
+assert("Enumerator::Chain.new") do
a = []
b = {}
c = Object.new # not has #each method
@@ -46,13 +46,13 @@ assert("Enumerator::Chain#each") do
assert_kind_of Enumerator, aa.each
assert_equal [1, 2, 3, 1, 2, 3], aa.each.to_a
- aa = a.chain(a.reverse_each)
+ aa = a.chain(6..9)
assert_kind_of Enumerator, aa.each
- assert_equal [1, 2, 3, 3, 2, 1], aa.each.to_a
+ assert_equal [1, 2, 3, 6, 7, 8, 9], aa.each.to_a
- aa = a.chain(a.reverse_each, a)
+ aa = a.chain((-3..-2).each_with_index, a)
assert_kind_of Enumerator, aa.each
- assert_equal [1, 2, 3, 3, 2, 1, 1, 2, 3], aa.each.to_a
+ assert_equal [1, 2, 3, [-3, 0], [-2, 1], 1, 2, 3], aa.each.to_a
aa = a.chain(Object.new)
assert_kind_of Enumerator, aa.each
@@ -65,12 +65,33 @@ assert("Enumerator::Chain#size") do
aa = a.chain(a)
assert_equal 6, aa.size
- aa = a.chain(a.reverse_each)
+ aa = a.chain(3..4)
assert_nil aa.size
- aa = a.chain(a.reverse_each, a)
+ aa = a.chain(3..4, a)
assert_nil aa.size
aa = a.chain(Object.new)
assert_nil aa.size
end
+
+assert("Enumerator::Chain#rewind") do
+ rewound = []
+ e1 = [1, 2]
+ e2 = (4..6)
+ (class << e1; self end).define_method(:rewind) { rewound << __id__ }
+ (class << e2; self end).define_method(:rewind) { rewound << __id__ }
+ c = e1.chain(e2).each{}.rewind
+ assert_equal [e2.__id__, e1.__id__], rewound
+end
+
+assert("Enumerator::Chain#+") do
+ a = [].chain
+ b = {}.chain
+ 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
+end
diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb
index 99b9cddba..b427bd67e 100644
--- a/mrbgems/mruby-enum-ext/mrblib/enum.rb
+++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb
@@ -811,10 +811,6 @@ module Enumerable
h
end
- def nil.to_h
- {}
- end
-
def uniq(&block)
hash = {}
if block
diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb
index 64b1bbda9..6929d8ddc 100644
--- a/mrbgems/mruby-enum-ext/test/enum.rb
+++ b/mrbgems/mruby-enum-ext/test/enum.rb
@@ -186,8 +186,5 @@ assert("Enumerable#to_h") do
h = c.new.to_h
assert_equal Hash, h.class
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-enumerator/mrbgem.rake b/mrbgems/mruby-enumerator/mrbgem.rake
index 8757a15ea..abcc54e7a 100644
--- a/mrbgems/mruby-enumerator/mrbgem.rake
+++ b/mrbgems/mruby-enumerator/mrbgem.rake
@@ -2,6 +2,5 @@ MRuby::Gem::Specification.new('mruby-enumerator') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.add_dependency('mruby-fiber', :core => 'mruby-fiber')
- spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext'
spec.summary = 'Enumerator class'
end
diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb
index d609cadb5..dce0c2cf2 100644
--- a/mrbgems/mruby-enumerator/test/enumerator.rb
+++ b/mrbgems/mruby-enumerator/test/enumerator.rb
@@ -6,6 +6,17 @@ class << @obj
end
end
+def assert_take(exp, enumerator)
+ result = []
+ n = exp.size
+ enumerator.each do |v|
+ break if n == 0
+ result << v
+ n -= 1
+ end
+ assert_equal exp, result
+end
+
assert 'Enumerator.class' do
assert_equal Class, Enumerator.class
end
@@ -19,7 +30,7 @@ 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 { |y| i = 0; loop { y << (i += 1) } }.take(3)
+ assert_take [1,2,3], Enumerator.new { |y| i = 0; loop { y << (i += 1) } }
assert_raise(ArgumentError) { Enumerator.new }
# examples
@@ -30,7 +41,7 @@ assert 'Enumerator.new' do
a, b = b, a + b
end
end
- assert_equal [1,1,2,3,5,8,13,21,34,55], fib.take(10)
+ assert_take [1,1,2,3,5,8,13,21,34,55], fib
end
assert 'Enumerator#initialize_copy' do
diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c
index fa687d624..30534aaec 100644
--- a/mrbgems/mruby-eval/src/eval.c
+++ b/mrbgems/mruby-eval/src/eval.c
@@ -235,7 +235,7 @@ 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;
@@ -387,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 4d7dd4606..4930259c1 100644
--- a/mrbgems/mruby-eval/test/eval.rb
+++ b/mrbgems/mruby-eval/test/eval.rb
@@ -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]
@@ -100,7 +100,7 @@ assert('Object#instance_eval with begin-rescue-ensure execution order') do
assert_equal([:enter_raise_hell, :begin, :rescue, :ensure], hell_raiser.raise_hell)
end
-assert('Kernel#instance_eval() to define singleton methods Issue #3141') do
+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"
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-io/src/file.c b/mrbgems/mruby-io/src/file.c
index c00663481..243f634b4 100644
--- a/mrbgems/mruby-io/src/file.c
+++ b/mrbgems/mruby-io/src/file.c
@@ -146,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);
@@ -159,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);
@@ -248,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);
@@ -300,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();
@@ -393,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);
@@ -417,16 +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++) {
- const char *utf8_path = mrb_str_to_cstr(mrb, filenames[i]);
+ 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, 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 e5b83e923..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.");
}
@@ -955,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();
}
diff --git a/mrbgems/mruby-io/test/file.rb b/mrbgems/mruby-io/test/file.rb
index ba4100492..1535ebb44 100644
--- a/mrbgems/mruby-io/test/file.rb
+++ b/mrbgems/mruby-io/test/file.rb
@@ -1,15 +1,13 @@
##
# File Test
-assert('File TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
-end
+MRubyIOTestUtil.io_test_setup
-assert('File', '15.2.21') do
+assert('File.class', '15.2.21') do
assert_equal Class, File.class
end
-assert('File', '15.2.21.2') do
+assert('File.superclass', '15.2.21.2') do
assert_equal IO, File.superclass
end
@@ -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
@@ -108,6 +107,8 @@ assert('File.realpath') do
MRubyIOTestUtil.rmdir dir
end
end
+
+ assert_raise(ArgumentError) { File.realpath("TO\0DO") }
end
assert("File.readlink") do
@@ -204,6 +205,4 @@ assert('File.chmod') do
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 04e10e0c8..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
@@ -112,6 +109,4 @@ assert("FileTest.zero?") do
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/io.rb b/mrbgems/mruby-io/test/io.rb
index 44eaca6be..1491a4cfe 100644
--- a/mrbgems/mruby-io/test/io.rb
+++ b/mrbgems/mruby-io/test/io.rb
@@ -1,35 +1,46 @@
##
# IO Test
-assert('IO TEST SETUP') do
- MRubyIOTestUtil.io_test_setup
- $cr, $crlf, $cmd = MRubyIOTestUtil.win? ? [1, "\r\n", "cmd /c "] : [0, "\n", ""]
+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
+ io2.close unless meth == :open
+ end
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('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
+ assert_io_open(:open)
end
assert('IO#close', '15.2.20.5.1') do
@@ -84,7 +95,7 @@ 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
@@ -127,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
@@ -215,19 +226,15 @@ assert('IO#dup for writable') do
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
+ assert_io_open(:for_fd)
end
assert('IO.new') do
- io = IO.new(0)
- io.close
+ 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
@@ -604,6 +611,4 @@ assert('`cmd`') do
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 99affbfa4..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;
}
@@ -206,22 +206,6 @@ mrb_f_hash(mrb_state *mrb, mrb_value self)
return mrb_ensure_hash_type(mrb, arg);
}
-/*
- * 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;
-}
-
void
mrb_mruby_kernel_ext_gem_init(mrb_state *mrb)
{
@@ -237,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 28f089007..ad9177165 100644
--- a/mrbgems/mruby-kernel-ext/test/kernel.rb
+++ b/mrbgems/mruby-kernel-ext/test/kernel.rb
@@ -65,6 +65,8 @@ assert('Kernel#Float') do
assert_equal(123.456, Float(123.456))
assert_equal(123.456, Float("123.456"))
assert_raise(TypeError) { Float(nil) }
+ assert_raise(ArgumentError) { Float("1.5a") }
+ assert_raise(ArgumentError) { Float("1.5\0") }
end
assert('Kernel#String') do
diff --git a/mrbgems/mruby-math/src/math.c b/mrbgems/mruby-math/src/math.c
index caa16b789..c6d4ca414 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 Math conflicts 'MRB_WITHOUT_FLOAT' configuration in your 'build_config.rb'
+#endif
+
#include <mruby.h>
#include <mruby/array.h>
diff --git a/mrbgems/mruby-metaprog/src/metaprog.c b/mrbgems/mruby-metaprog/src/metaprog.c
index 0aafb4c34..62daa5227 100644
--- a/mrbgems/mruby-metaprog/src/metaprog.c
+++ b/mrbgems/mruby-metaprog/src/metaprog.c
@@ -117,6 +117,7 @@ mrb_obj_ivar_set(mrb_state *mrb, mrb_value self)
}
/* 15.3.1.2.7 */
+/* 15.3.1.3.28 */
/*
* call-seq:
* local_variables -> array
@@ -186,7 +187,7 @@ method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set)
}
}
-mrb_value
+static mrb_value
mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* klass, int obj)
{
khint_t i;
@@ -565,8 +566,6 @@ mrb_mod_included_modules(mrb_state *mrb, mrb_value self)
return result;
}
-mrb_value mrb_class_instance_method_list(mrb_state*, mrb_bool, struct RClass*, int);
-
/* 15.2.2.4.33 */
/*
* call-seq:
@@ -643,6 +642,7 @@ mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
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++;
@@ -657,8 +657,30 @@ mrb_mod_s_constants(mrb_state *mrb, mrb_value mod)
return mrb_nil_value(); /* not reached */
}
-/* implementation of Module.nesting */
-mrb_value mrb_mod_s_nesting(mrb_state*, mrb_value);
+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)
@@ -666,8 +688,8 @@ 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.2.4 */
- mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 */
+ 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 */
diff --git a/mrbgems/mruby-metaprog/test/metaprog.rb b/mrbgems/mruby-metaprog/test/metaprog.rb
index 3aa1d8732..30b75bd60 100644
--- a/mrbgems/mruby-metaprog/test/metaprog.rb
+++ b/mrbgems/mruby-metaprog/test/metaprog.rb
@@ -1,17 +1,3 @@
-assert('Kernel#__send__', '15.3.1.3.4') do
- # test with block
- l = __send__(:lambda) do
- true
- end
-
- assert_true l.call
- assert_equal Proc, l.class
- # test with argument
- assert_true __send__(:respond_to?, :nil?)
- # test without argument and without block
- assert_equal String, __send__(:to_s).class
-end
-
assert('Kernel#send', '15.3.1.3.44') do
# test with block
l = send(:lambda) do
@@ -58,6 +44,8 @@ assert('Kernel#instance_variable_set', '15.3.1.3.22') do
%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
@@ -99,6 +87,21 @@ 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
@@ -120,6 +123,8 @@ assert('Kernel#define_singleton_method') do
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
@@ -283,6 +288,9 @@ assert('Module#remove_class_variable', '15.2.2.4.39') do
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
@@ -290,9 +298,9 @@ assert('Module#remove_method', '15.2.2.4.41') do
class Parent
def hello
end
- end
+ end
- class Child < Parent
+ class Child < Parent
def hello
end
end
@@ -302,6 +310,7 @@ assert('Module#remove_method', '15.2.2.4.41') do
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
diff --git a/mrbgems/mruby-method/src/method.c b/mrbgems/mruby-method/src/method.c
index 9f1134227..d94db1cb2 100644
--- a/mrbgems/mruby-method/src/method.c
+++ b/mrbgems/mruby-method/src/method.c
@@ -270,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;
diff --git a/mrbgems/mruby-method/test/method.rb b/mrbgems/mruby-method/test/method.rb
index dfddde9cc..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
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 cd8bbf187..86c384a87 100644
--- a/mrbgems/mruby-numeric-ext/src/numeric_ext.c
+++ b/mrbgems/mruby-numeric-ext/src/numeric_ext.c
@@ -9,32 +9,6 @@ to_int(mrb_state *mrb, mrb_value x)
}
/*
- * Document-method: Integer#chr
- * call-seq:
- * int.chr -> string
- *
- * Returns a string containing the character represented by the +int+'s value
- * according to +encoding+.
- *
- * 65.chr #=> "A"
- * 230.chr #=> "\xE6"
- */
-static mrb_value
-mrb_int_chr(mrb_state *mrb, mrb_value x)
-{
- mrb_int chr;
- char c;
-
- chr = to_int(mrb, x);
- if (chr >= (1 << CHAR_BIT)) {
- mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", x);
- }
- c = (char)chr;
-
- return mrb_str_new(mrb, &c, 1);
-}
-
-/*
* call-seq:
* int.allbits?(mask) -> true or false
*
@@ -87,7 +61,6 @@ mrb_mruby_numeric_ext_gem_init(mrb_state* mrb)
{
struct RClass *i = mrb_module_get(mrb, "Integral");
- mrb_define_method(mrb, i, "chr", mrb_int_chr, MRB_ARGS_NONE());
mrb_define_method(mrb, i, "allbits?", mrb_int_allbits, MRB_ARGS_REQ(1));
mrb_define_method(mrb, i, "anybits?", mrb_int_anybits, MRB_ARGS_REQ(1));
mrb_define_method(mrb, i, "nobits?", mrb_int_nobits, MRB_ARGS_REQ(1));
diff --git a/mrbgems/mruby-numeric-ext/test/numeric.rb b/mrbgems/mruby-numeric-ext/test/numeric.rb
index c85cb61f2..efdf25a34 100644
--- a/mrbgems/mruby-numeric-ext/test/numeric.rb
+++ b/mrbgems/mruby-numeric-ext/test/numeric.rb
@@ -1,14 +1,6 @@
##
# Numeric(Ext) Test
-assert('Integer#chr') do
- assert_equal("A", 65.chr)
- assert_equal("B", 0x42.chr)
-
- # multibyte encoding (not support yet)
- assert_raise(RangeError) { 256.chr }
-end
-
assert('Integer#div') do
assert_equal 52, 365.div(7)
end
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..0aedc9a73 100644
--- a/mrbgems/mruby-object-ext/src/object.c
+++ b/mrbgems/mruby-object-ext/src/object.c
@@ -1,6 +1,7 @@
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/class.h>
+#include <mruby/hash.h>
#include <mruby/proc.h>
/*
@@ -33,6 +34,19 @@ nil_to_f(mrb_state *mrb, mrb_value obj)
/*
* call-seq:
+ * nil.to_h -> {}
+ *
+ * Always returns an empty hash.
+ */
+
+static mrb_value
+nil_to_h(mrb_state *mrb, mrb_value obj)
+{
+ return mrb_hash_new(mrb);
+}
+
+/*
+ * call-seq:
* nil.to_i -> 0
*
* Always returns zero.
@@ -46,6 +60,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
@@ -101,9 +131,12 @@ mrb_mruby_object_ext_gem_init(mrb_state* mrb)
#ifndef MRB_WITHOUT_FLOAT
mrb_define_method(mrb, n, "to_f", nil_to_f, MRB_ARGS_NONE());
#endif
+ mrb_define_method(mrb, n, "to_h", nil_to_h, MRB_ARGS_NONE());
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 fbff20629..9975e785a 100644
--- a/mrbgems/mruby-object-ext/test/nil.rb
+++ b/mrbgems/mruby-object-ext/test/nil.rb
@@ -7,6 +7,10 @@ assert('NilClass#to_f') do
assert_equal 0.0, nil.to_f
end
+assert('NilClass#to_h') do
+ assert_equal Hash.new, nil.to_h
+end
+
assert('NilClass#to_i') do
assert_equal 0, nil.to_i
end
diff --git a/mrbgems/mruby-objectspace/test/objectspace.rb b/mrbgems/mruby-objectspace/test/objectspace.rb
index 8db89eeaf..9c44c2157 100644
--- a/mrbgems/mruby-objectspace/test/objectspace.rb
+++ b/mrbgems/mruby-objectspace/test/objectspace.rb
@@ -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 ac29fdbf3..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"
@@ -118,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
@@ -1002,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;
@@ -1075,10 +1075,11 @@ alias:
if (ISDIGIT(ch)) {
count = ch - '0';
while (tmpl->idx < tlen && ISDIGIT(tptr[tmpl->idx])) {
- count = count * 10 + (tptr[tmpl->idx++] - '0');
- if (count < 0) {
+ 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 == '*') {
@@ -1195,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;
}
@@ -1248,6 +1249,9 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
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;
}
@@ -1274,9 +1278,6 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
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);
@@ -1298,7 +1299,12 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
if (single) break;
}
- if (single) return RARRAY_PTR(result)[0];
+ if (single) {
+ if (RARRAY_LEN(result) > 0) {
+ return RARRAY_PTR(result)[0];
+ }
+ return mrb_nil_value();
+ }
return result;
}
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-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c
index 17884e3c6..c9041ec75 100644
--- a/mrbgems/mruby-proc-ext/src/proc.c
+++ b/mrbgems/mruby-proc-ext/src/proc.c
@@ -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;
@@ -52,7 +52,7 @@ mrb_proc_inspect(mrb_state *mrb, mrb_value self)
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, "-");
diff --git a/mrbgems/mruby-proc-ext/test/proc.rb b/mrbgems/mruby-proc-ext/test/proc.rb
index 1220841c8..a6321d371 100644
--- a/mrbgems/mruby-proc-ext/test/proc.rb
+++ b/mrbgems/mruby-proc-ext/test/proc.rb
@@ -1,16 +1,32 @@
##
# 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
diff --git a/mrbgems/mruby-random/src/mt19937ar.c b/mrbgems/mruby-random/src/mt19937ar.c
deleted file mode 100644
index 405bd5c20..000000000
--- a/mrbgems/mruby-random/src/mt19937ar.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
-** mt19937ar.c - MT Random functions
-**
-** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura,
-** All rights reserved.
-**
-** 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 ]
-**
-** Any feedback is very welcome.
-** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
-** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
-**
-** This version is modified by mruby developers. If you see any problem,
-** contact us first at https://github.com/mruby/mruby/issues
-*/
-
-#include <mruby.h>
-#include "mt19937ar.h"
-
-/* Period parameters */
-/* #define N 624 */
-#define M 397
-#define MATRIX_A 0x9908b0dfUL /* constant vector a */
-#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
-#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
-
-#if 0 /* dead_code */
-static unsigned long mt[N]; /* the array for the state vector */
-static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
-#endif /* dead_code */
-
-void mrb_random_init_genrand(mt_state *t, unsigned long s)
-{
- t->mt[0]= s & 0xffffffffUL;
- for (t->mti=1; t->mti<N; t->mti++) {
- t->mt[t->mti] = (1812433253UL * (t->mt[t->mti-1] ^ (t->mt[t->mti-1] >> 30)) + t->mti);
- t->mt[t->mti] &= 0xffffffffUL;
- }
-}
-
-unsigned long mrb_random_genrand_int32(mt_state *t)
-{
- unsigned long y;
- static const unsigned long mag01[2]={0x0UL, MATRIX_A};
- /* mag01[x] = x * MATRIX_A for x=0,1 */
-
- if (t->mti >= N) { /* generate N words at one time */
- int kk;
-
- if (t->mti == N+1) /* if init_genrand() has not been called, */
- mrb_random_init_genrand(t, 5489UL); /* a default initial seed is used */
-
- for (kk=0;kk<N-M;kk++) {
- y = (t->mt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK);
- t->mt[kk] = t->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
- }
- for (;kk<N-1;kk++) {
- y = (t->mt[kk]&UPPER_MASK)|(t->mt[kk+1]&LOWER_MASK);
- t->mt[kk] = t->mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
- }
- y = (t->mt[N-1]&UPPER_MASK)|(t->mt[0]&LOWER_MASK);
- t->mt[N-1] = t->mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
-
- t->mti = 0;
- }
-
- y = t->mt[t->mti++];
-
- /* Tempering */
- y ^= (y >> 11);
- y ^= (y << 7) & 0x9d2c5680UL;
- y ^= (y << 15) & 0xefc60000UL;
- y ^= (y >> 18);
-
- t->gen.int_ = y;
-
- return y;
-}
-
-double mrb_random_genrand_real1(mt_state *t)
-{
- mrb_random_genrand_int32(t);
- t->gen.double_ = t->gen.int_*(1.0/4294967295.0);
- return t->gen.double_;
- /* divided by 2^32-1 */
-}
-
-#if 0 /* dead_code */
-/* initializes mt[N] with a seed */
-void init_genrand(unsigned long s)
-{
- mt[0]= s & 0xffffffffUL;
- for (mti=1; mti<N; mti++) {
- mt[mti] = (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
- /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
- /* In the previous versions, MSBs of the seed affect */
- /* only MSBs of the array mt[]. */
- /* 2002/01/09 modified by Makoto Matsumoto */
- mt[mti] &= 0xffffffffUL;
- /* for >32 bit machines */
- }
-}
-
-/* initialize by an array with array-length */
-/* init_key is the array for initializing keys */
-/* key_length is its length */
-/* slight change for C++, 2004/2/26 */
-void init_by_array(unsigned long init_key[], int key_length)
-{
- int i, j, k;
- init_genrand(19650218UL);
- i=1; j=0;
- k = (N>key_length ? N : key_length);
- for (; k; k--) {
- mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
- + init_key[j] + j; /* non linear */
- mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
- i++; j++;
- if (i>=N) { mt[0] = mt[N-1]; i=1; }
- if (j>=key_length) j=0;
- }
- for (k=N-1; k; k--) {
- mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
- - i; /* non linear */
- mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
- i++;
- if (i>=N) { mt[0] = mt[N-1]; i=1; }
- }
-
- mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
-}
-
-/* generates a random number on [0,0xffffffff]-interval */
-unsigned long genrand_int32(void)
-{
- unsigned long y;
- static const unsigned long mag01[2]={0x0UL, MATRIX_A};
- /* mag01[x] = x * MATRIX_A for x=0,1 */
-
- if (mti >= N) { /* generate N words at one time */
- int kk;
-
- if (mti == N+1) /* if init_genrand() has not been called, */
- init_genrand(5489UL); /* a default initial seed is used */
-
- for (kk=0;kk<N-M;kk++) {
- y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
- mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
- }
- for (;kk<N-1;kk++) {
- y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
- mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
- }
- y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
- mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
-
- mti = 0;
- }
-
- y = mt[mti++];
-
- /* Tempering */
- y ^= (y >> 11);
- y ^= (y << 7) & 0x9d2c5680UL;
- y ^= (y << 15) & 0xefc60000UL;
- y ^= (y >> 18);
-
- return y;
-}
-
-/* generates a random number on [0,0x7fffffff]-interval */
-long genrand_int31(void)
-{
- return (long)(genrand_int32()>>1);
-}
-
-/* generates a random number on [0,1]-real-interval */
-double genrand_real1(void)
-{
- return genrand_int32()*(1.0/4294967295.0);
- /* divided by 2^32-1 */
-}
-
-/* generates a random number on [0,1)-real-interval */
-double genrand_real2(void)
-{
- return genrand_int32()*(1.0/4294967296.0);
- /* divided by 2^32 */
-}
-
-/* generates a random number on (0,1)-real-interval */
-double genrand_real3(void)
-{
- return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0);
- /* divided by 2^32 */
-}
-
-/* generates a random number on [0,1) with 53-bit resolution*/
-double genrand_res53(void)
-{
- unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6;
- return(a*67108864.0+b)*(1.0/9007199254740992.0);
-}
-/* These real versions are due to Isaku Wada, 2002/01/09 added */
-#endif /* dead_code */
diff --git a/mrbgems/mruby-random/src/mt19937ar.h b/mrbgems/mruby-random/src/mt19937ar.h
deleted file mode 100644
index 7d382320d..000000000
--- a/mrbgems/mruby-random/src/mt19937ar.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
-** mt19937ar.h - MT Random functions
-**
-** Copyright (C) 1997 - 2016, Makoto Matsumoto and Takuji Nishimura,
-** All rights reserved.
-**
-** 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 ]
-**
-** Any feedback is very welcome.
-** http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
-** email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
-**
-** This version is modified by mruby developers. If you see any problem,
-** contact us first at https://github.com/mruby/mruby/issues
-*/
-
-#define N 624
-
-typedef struct {
- unsigned long mt[N];
- int mti;
- union {
- unsigned long int_;
- double double_;
- } gen;
-
- mrb_int seed;
- mrb_bool has_seed : 1;
-} mt_state;
-
-void mrb_random_init_genrand(mt_state *, unsigned long);
-unsigned long mrb_random_genrand_int32(mt_state *);
-double mrb_random_genrand_real1(mt_state *t);
-
-/* initializes mt[N] with a seed */
-void init_genrand(unsigned long s);
-
-/* initialize by an array with array-length */
-/* init_key is the array for initializing keys */
-/* key_length is its length */
-/* slight change for C++, 2004/2/26 */
-void init_by_array(unsigned long init_key[], int key_length);
-
-/* generates a random number on [0,0xffffffff]-interval */
-unsigned long genrand_int32(void);
-
-/* generates a random number on [0,0x7fffffff]-interval */
-long genrand_int31(void);
-
-/* These real versions are due to Isaku Wada, 2002/01/09 added */
-/* generates a random number on [0,1]-real-interval */
-double genrand_real1(void);
-
-/* generates a random number on [0,1)-real-interval */
-double genrand_real2(void);
-
-/* generates a random number on (0,1)-real-interval */
-double genrand_real3(void);
-
-/* generates a random number on [0,1) with 53-bit resolution*/
-double genrand_res53(void);
diff --git a/mrbgems/mruby-random/src/random.c b/mrbgems/mruby-random/src/random.c
index 68209840a..a970e65a3 100644
--- a/mrbgems/mruby-random/src/random.c
+++ b/mrbgems/mruby-random/src/random.c
@@ -9,62 +9,105 @@
#include <mruby/class.h>
#include <mruby/data.h>
#include <mruby/array.h>
-#include "mt19937ar.h"
+#include <mruby/istruct.h>
+#if INT32_MAX <= INTPTR_MAX
+# define XORSHIFT96
+# define NSEEDS 3
+#else
+# define NSEEDS 4
+#endif
+#define LASTSEED (NSEEDS-1)
#include <time.h>
-static char const MT_STATE_KEY[] = "$mrb_i_mt_state";
-
-static const struct mrb_data_type mt_state_type = {
- MT_STATE_KEY, mrb_free,
-};
-
-static mrb_value mrb_random_rand(mrb_state *mrb, mrb_value self);
-static mrb_value mrb_random_srand(mrb_state *mrb, mrb_value self);
+typedef struct rand_state {
+ uint32_t seed[NSEEDS];
+} rand_state;
static void
-mt_srand(mt_state *t, unsigned long seed)
+rand_init(rand_state *t)
{
- mrb_random_init_genrand(t, seed);
+ t->seed[0] = 123456789;
+ t->seed[1] = 362436069;
+ t->seed[2] = 521288629;
+#ifndef XORSHIFT96
+ t->seed[3] = 88675123;
+#endif
}
-static unsigned long
-mt_rand(mt_state *t)
+static uint32_t
+rand_seed(rand_state *t, uint32_t seed)
{
- return mrb_random_genrand_int32(t);
+ uint32_t old_seed = t->seed[LASTSEED];
+ rand_init(t);
+ t->seed[LASTSEED] = seed;
+ return old_seed;
}
-static double
-mt_rand_real(mt_state *t)
+#ifdef XORSHIFT96
+static uint32_t
+rand_uint32(rand_state *state)
{
- return mrb_random_genrand_real1(t);
+ uint32_t *seed = state->seed;
+ uint32_t x = seed[0];
+ uint32_t y = seed[1];
+ uint32_t z = seed[2];
+ uint32_t t;
+
+ t = (x ^ (x << 3)) ^ (y ^ (y >> 19)) ^ (z ^ (z << 6));
+ x = y; y = z; z = t;
+ seed[0] = x;
+ seed[1] = y;
+ seed[2] = z;
+
+ return z;
}
-
-static mrb_value
-mrb_random_mt_srand(mrb_state *mrb, mt_state *t, mrb_value seed)
+#else /* XORSHIFT96 */
+static uint32_t
+rand_uint32(rand_state *state)
{
- if (mrb_nil_p(seed)) {
- seed = mrb_fixnum_value((mrb_int)(time(NULL) + mt_rand(t)));
- if (mrb_fixnum(seed) < 0) {
- seed = mrb_fixnum_value(0 - mrb_fixnum(seed));
- }
- }
-
- mt_srand(t, (unsigned) mrb_fixnum(seed));
+ uint32_t *seed = state->seed;
+ uint32_t x = seed[0];
+ uint32_t y = seed[1];
+ uint32_t z = seed[2];
+ uint32_t w = seed[3];
+ uint32_t t;
+
+ t = x ^ (x << 11);
+ x = y; y = z; z = w;
+ w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
+ seed[0] = x;
+ seed[1] = y;
+ seed[2] = z;
+ seed[3] = w;
+
+ return w;
+}
+#endif /* XORSHIFT96 */
- return seed;
+#ifndef MRB_WITHOUT_FLOAT
+static double
+rand_real(rand_state *t)
+{
+ uint32_t x = rand_uint32(t);
+ return x*(1.0/4294967295.0);
}
+#endif
static mrb_value
-mrb_random_mt_rand(mrb_state *mrb, mt_state *t, mrb_value max)
+random_rand(mrb_state *mrb, rand_state *t, mrb_value max)
{
mrb_value value;
if (mrb_fixnum(max) == 0) {
- value = mrb_float_value(mrb, mt_rand_real(t));
+#ifndef MRB_WITHOUT_FLOAT
+ value = mrb_float_value(mrb, rand_real(t));
+#else
+ mrb_raise(mrb, E_ARGUMENT_ERROR, "Float not supported");
+#endif
}
else {
- value = mrb_fixnum_value(mt_rand(t) % mrb_fixnum(max));
+ value = mrb_fixnum_value(rand_uint32(t) % mrb_fixnum(max));
}
return value;
@@ -90,106 +133,74 @@ get_opt(mrb_state* mrb)
return arg;
}
-static mrb_value
-get_random(mrb_state *mrb) {
- return mrb_const_get(mrb,
- mrb_obj_value(mrb_class_get(mrb, "Random")),
- mrb_intern_lit(mrb, "DEFAULT"));
-}
-
-static mt_state *
-get_random_state(mrb_state *mrb)
-{
- mrb_value random_val = get_random(mrb);
- return DATA_GET_PTR(mrb, random_val, &mt_state_type, mt_state);
+static void
+random_check(mrb_state *mrb, mrb_value random) {
+ struct RClass *c = mrb_class_get(mrb, "Random");
+ if (!mrb_obj_is_kind_of(mrb, random, c) || mrb_type(random) != MRB_TT_ISTRUCT) {
+ mrb_raise(mrb, E_TYPE_ERROR, "Random instance required");
+ }
}
static mrb_value
-mrb_random_g_rand(mrb_state *mrb, mrb_value self)
-{
- mrb_value random = get_random(mrb);
- return mrb_random_rand(mrb, random);
+random_default(mrb_state *mrb) {
+ struct RClass *c = mrb_class_get(mrb, "Random");
+ mrb_value d = mrb_const_get(mrb, mrb_obj_value(c), mrb_intern_lit(mrb, "DEFAULT"));
+ if (!mrb_obj_is_kind_of(mrb, d, c)) {
+ mrb_raise(mrb, E_TYPE_ERROR, "Random::DEFAULT replaced");
+ }
+ return d;
}
-static mrb_value
-mrb_random_g_srand(mrb_state *mrb, mrb_value self)
-{
- mrb_value random = get_random(mrb);
- return mrb_random_srand(mrb, random);
-}
+#define random_ptr(v) (rand_state*)mrb_istruct_ptr(v)
+#define random_default_state(mrb) random_ptr(random_default(mrb))
static mrb_value
-mrb_random_init(mrb_state *mrb, mrb_value self)
+random_m_init(mrb_state *mrb, mrb_value self)
{
mrb_value seed;
- mt_state *t;
+ rand_state *t;
seed = get_opt(mrb);
-
/* avoid memory leaks */
- t = (mt_state*)DATA_PTR(self);
- if (t) {
- mrb_free(mrb, t);
- }
- mrb_data_init(self, NULL, &mt_state_type);
-
- t = (mt_state *)mrb_malloc(mrb, sizeof(mt_state));
- t->mti = N + 1;
-
- seed = mrb_random_mt_srand(mrb, t, seed);
+ t = random_ptr(self);
if (mrb_nil_p(seed)) {
- t->has_seed = FALSE;
+ rand_init(t);
}
else {
- mrb_assert(mrb_fixnum_p(seed));
- t->has_seed = TRUE;
- t->seed = mrb_fixnum(seed);
+ rand_seed(t, (uint32_t)mrb_fixnum(seed));
}
- mrb_data_init(self, t, &mt_state_type);
-
return self;
}
-static void
-mrb_random_rand_seed(mrb_state *mrb, mt_state *t)
-{
- if (!t->has_seed) {
- mrb_random_mt_srand(mrb, t, mrb_nil_value());
- }
-}
-
static mrb_value
-mrb_random_rand(mrb_state *mrb, mrb_value self)
+random_m_rand(mrb_state *mrb, mrb_value self)
{
mrb_value max;
- mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state);
+ rand_state *t = random_ptr(self);
max = get_opt(mrb);
- mrb_random_rand_seed(mrb, t);
- return mrb_random_mt_rand(mrb, t, max);
+ return random_rand(mrb, t, max);
}
static mrb_value
-mrb_random_srand(mrb_state *mrb, mrb_value self)
+random_m_srand(mrb_state *mrb, mrb_value self)
{
- mrb_value seed;
- mrb_value old_seed;
- mt_state *t = DATA_GET_PTR(mrb, self, &mt_state_type, mt_state);
-
- seed = get_opt(mrb);
- seed = mrb_random_mt_srand(mrb, t, seed);
- old_seed = t->has_seed? mrb_fixnum_value(t->seed) : mrb_nil_value();
- if (mrb_nil_p(seed)) {
- t->has_seed = FALSE;
+ uint32_t seed;
+ uint32_t old_seed;
+ mrb_value sv;
+ rand_state *t = random_ptr(self);
+
+ sv = get_opt(mrb);
+ if (mrb_nil_p(sv)) {
+ seed = (uint32_t)time(NULL) + rand_uint32(t);
}
else {
- mrb_assert(mrb_fixnum_p(seed));
- t->has_seed = TRUE;
- t->seed = mrb_fixnum(seed);
+ seed = (uint32_t)mrb_fixnum(sv);
}
+ old_seed = rand_seed(t, seed);
- return old_seed;
+ return mrb_fixnum_value((mrb_int)old_seed);
}
/*
@@ -203,25 +214,28 @@ static mrb_value
mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary)
{
mrb_int i;
- mt_state *random = NULL;
+ mrb_value max;
+ mrb_value r = mrb_nil_value();
+ rand_state *random;
if (RARRAY_LEN(ary) > 1) {
- mrb_get_args(mrb, "|d", &random, &mt_state_type);
+ mrb_get_args(mrb, "|o", &r);
- if (random == NULL) {
- random = get_random_state(mrb);
+ if (mrb_nil_p(r)) {
+ random = random_default_state(mrb);
+ }
+ else {
+ random_check(mrb, r);
+ random = random_ptr(r);
}
- mrb_random_rand_seed(mrb, random);
-
mrb_ary_modify(mrb, mrb_ary_ptr(ary));
-
+ max = mrb_fixnum_value(RARRAY_LEN(ary));
for (i = RARRAY_LEN(ary) - 1; i > 0; i--) {
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))));
+ j = mrb_fixnum(random_rand(mrb, random, max));
tmp = ptr[i];
ptr[i] = ptr[j];
@@ -268,15 +282,18 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
{
mrb_int n = 0;
mrb_bool given;
- mt_state *random = NULL;
+ mrb_value r = mrb_nil_value();
+ rand_state *random;
mrb_int len;
- mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type);
- if (random == NULL) {
- random = get_random_state(mrb);
+ mrb_get_args(mrb, "|i?o", &n, &given, &r);
+ if (mrb_nil_p(r)) {
+ random = random_default_state(mrb);
+ }
+ else {
+ random_check(mrb, r);
+ random = random_ptr(r);
}
- mrb_random_rand_seed(mrb, random);
- mt_rand(random);
len = RARRAY_LEN(ary);
if (!given) { /* pick one element */
switch (len) {
@@ -285,7 +302,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
case 1:
return RARRAY_PTR(ary)[0];
default:
- return RARRAY_PTR(ary)[mt_rand(random) % len];
+ return RARRAY_PTR(ary)[rand_uint32(random) % len];
}
}
else {
@@ -300,7 +317,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
for (;;) {
retry:
- r = mt_rand(random) % len;
+ r = (mrb_int)rand_uint32(random) % len;
for (j=0; j<i; j++) {
if (mrb_fixnum(RARRAY_PTR(result)[j]) == r) {
@@ -318,23 +335,39 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary)
}
}
+static mrb_value
+random_f_rand(mrb_state *mrb, mrb_value self)
+{
+ rand_state *t = random_default_state(mrb);
+ return random_rand(mrb, t, get_opt(mrb));
+}
+
+static mrb_value
+random_f_srand(mrb_state *mrb, mrb_value self)
+{
+ mrb_value random = random_default(mrb);
+ return random_m_srand(mrb, random);
+}
+
void mrb_mruby_random_gem_init(mrb_state *mrb)
{
struct RClass *random;
struct RClass *array = mrb->array_class;
- mrb_define_method(mrb, mrb->kernel_module, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1));
- mrb_define_method(mrb, mrb->kernel_module, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1));
+ mrb_assert(sizeof(rand_state) < ISTRUCT_DATA_SIZE);
+
+ mrb_define_method(mrb, mrb->kernel_module, "rand", random_f_rand, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, mrb->kernel_module, "srand", random_f_srand, MRB_ARGS_OPT(1));
random = mrb_define_class(mrb, "Random", mrb->object_class);
- MRB_SET_INSTANCE_TT(random, MRB_TT_DATA);
- mrb_define_class_method(mrb, random, "rand", mrb_random_g_rand, MRB_ARGS_OPT(1));
- mrb_define_class_method(mrb, random, "srand", mrb_random_g_srand, MRB_ARGS_OPT(1));
+ MRB_SET_INSTANCE_TT(random, MRB_TT_ISTRUCT);
+ mrb_define_class_method(mrb, random, "rand", random_f_rand, MRB_ARGS_OPT(1));
+ mrb_define_class_method(mrb, random, "srand", random_f_srand, MRB_ARGS_OPT(1));
- mrb_define_method(mrb, random, "initialize", mrb_random_init, MRB_ARGS_OPT(1));
- mrb_define_method(mrb, random, "rand", mrb_random_rand, MRB_ARGS_OPT(1));
- mrb_define_method(mrb, random, "srand", mrb_random_srand, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, random, "initialize", random_m_init, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, random, "rand", random_m_rand, MRB_ARGS_OPT(1));
+ mrb_define_method(mrb, random, "srand", random_m_srand, MRB_ARGS_OPT(1));
mrb_define_method(mrb, array, "shuffle", mrb_ary_shuffle, MRB_ARGS_OPT(1));
mrb_define_method(mrb, array, "shuffle!", mrb_ary_shuffle_bang, MRB_ARGS_OPT(1));
diff --git a/mrbgems/mruby-range-ext/mrblib/range.rb b/mrbgems/mruby-range-ext/mrblib/range.rb
index de7925ba7..a213beb57 100644
--- a/mrbgems/mruby-range-ext/mrblib/range.rb
+++ b/mrbgems/mruby-range-ext/mrblib/range.rb
@@ -25,4 +25,42 @@ 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?(Numeric) && last.kind_of?(Numeric)
+ 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?(Numeric) && last.kind_of?(Numeric)
+ 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 1f6690904..b8c76b796 100644
--- a/mrbgems/mruby-range-ext/src/range.c
+++ b/mrbgems/mruby-range-ext/src/range.c
@@ -106,6 +106,7 @@ range_last(mrb_state *mrb, mrb_value range)
* ('a'..'z').size #=> nil
*/
+#ifndef MRB_WITHOUT_FLOAT
static mrb_value
range_size(mrb_state *mrb, mrb_value range)
{
@@ -158,6 +159,28 @@ range_size(mrb_state *mrb, mrb_value range)
}
return mrb_nil_value();
}
+#else
+static mrb_value
+range_size(mrb_state *mrb, mrb_value range)
+{
+ struct RRange *r = mrb_range_ptr(mrb, range);
+ mrb_value beg, end;
+ mrb_int excl;
+
+ beg = RANGE_BEG(r);
+ end = RANGE_END(r);
+ excl = RANGE_EXCL(r) ? 0 : 1;
+
+ if (mrb_fixnum_p(beg) && mrb_fixnum_p(end)) {
+ mrb_int a = mrb_fixnum(beg);
+ mrb_int b = mrb_fixnum(end);
+ mrb_int c = b - a + excl;
+
+ return mrb_fixnum_value(c);
+ }
+ return mrb_nil_value();
+}
+#endif /* MRB_WITHOUT_FLOAT */
void
mrb_mruby_range_ext_gem_init(mrb_state* mrb)
diff --git a/mrbgems/mruby-range-ext/test/range.rb b/mrbgems/mruby-range-ext/test/range.rb
index efcbdabe4..e2c549d04 100644
--- a/mrbgems/mruby-range-ext/test/range.rb
+++ b/mrbgems/mruby-range-ext/test/range.rb
@@ -10,6 +10,8 @@ end
assert('Range#first') do
assert_equal 10, (10..20).first
assert_equal [10, 11, 12], (10..20).first(3)
+
+ skip unless Object.const_defined?(:Float)
assert_equal [0, 1, 2], (0..Float::INFINITY).first(3)
end
@@ -23,10 +25,118 @@ end
assert('Range#size') do
assert_equal 42, (1..42).size
assert_equal 41, (1...42).size
+ assert_nil ('a'..'z').size
+
+ skip unless Object.const_defined?(:Float)
assert_equal 6, (1...6.3).size
assert_equal 5, (1...6.0).size
assert_equal 5, (1.1...6).size
assert_equal 15, (1.0..15.9).size
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 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
+
+ skip unless Object.const_defined?(:Float)
+
+ # 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 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 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
+
+ skip unless Object.const_defined?(:Float)
+
+ # 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 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..c0b16a6ae
--- /dev/null
+++ b/mrbgems/mruby-rational/mrblib/rational.rb
@@ -0,0 +1,117 @@
+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
+
+ [:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].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
+end
+
diff --git a/mrbgems/mruby-rational/src/rational.c b/mrbgems/mruby-rational/src/rational.c
new file mode 100644
index 000000000..09bd68003
--- /dev/null
+++ b/mrbgems/mruby-rational/src/rational.c
@@ -0,0 +1,209 @@
+#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;
+
+ rat = mrb_define_class(mrb, "Rational", mrb_class_get(mrb, "Numeric"));
+#ifdef RATIONAL_USE_ISTRUCT
+ MRB_SET_INSTANCE_TT(rat, MRB_TT_ISTRUCT);
+ mrb_assert(sizeof(struct mrb_rational) < ISTRUCT_DATA_SIZE);
+#else
+ MRB_SET_INSTANCE_TT(rat, MRB_TT_DATA);
+#endif
+ 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-socket/src/socket.c b/mrbgems/mruby-socket/src/socket.c
index dff176778..9b06274dc 100644
--- a/mrbgems/mruby-socket/src/socket.c
+++ b/mrbgems/mruby-socket/src/socket.c
@@ -38,9 +38,10 @@
#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"
@@ -130,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 {
@@ -138,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 {
diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb
index 311803ea2..e57d75355 100644
--- a/mrbgems/mruby-string-ext/mrblib/string.rb
+++ b/mrbgems/mruby-string-ext/mrblib/string.rb
@@ -310,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
@@ -410,7 +414,7 @@ class String
e = max.ord
while c <= e
break if exclusive and c == e
- yield c.chr
+ yield c.chr(__ENCODING__)
c += 1
end
return self
diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c
index ba7e3c610..80b277444 100644
--- a/mrbgems/mruby-string-ext/src/string.c
+++ b/mrbgems/mruby-string-ext/src/string.c
@@ -5,6 +5,91 @@
#include <mruby/string.h>
#include <mruby/range.h>
+#define ENC_ASCII_8BIT "ASCII-8BIT"
+#define ENC_BINARY "BINARY"
+#define ENC_UTF8 "UTF-8"
+
+#define ENC_COMP_P(enc, enc_lit) \
+ str_casecmp_p(RSTRING_PTR(enc), RSTRING_LEN(enc), enc_lit, sizeof(enc_lit"")-1)
+
+#ifdef MRB_WITHOUT_FLOAT
+# define mrb_float_p(o) FALSE
+#endif
+
+static mrb_bool
+str_casecmp_p(const char *s1, mrb_int len1, const char *s2, mrb_int len2)
+{
+ const char *e1, *e2;
+
+ if (len1 != len2) return FALSE;
+ e1 = s1 + len1;
+ e2 = s2 + len2;
+ while (s1 < e1 && s2 < e2) {
+ if (*s1 != *s2 && TOUPPER(*s1) != TOUPPER(*s2)) return FALSE;
+ ++s1;
+ ++s2;
+ }
+ return TRUE;
+}
+
+static mrb_value
+int_chr_binary(mrb_state *mrb, mrb_value num)
+{
+ mrb_int cp = mrb_int(mrb, num);
+ char c;
+ mrb_value str;
+
+ if (cp < 0 || 0xff < cp) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
+ }
+ c = (char)cp;
+ str = mrb_str_new(mrb, &c, 1);
+ RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
+ return str;
+}
+
+#ifdef MRB_UTF8_STRING
+static mrb_value
+int_chr_utf8(mrb_state *mrb, mrb_value num)
+{
+ mrb_int cp = mrb_int(mrb, num);
+ char utf8[4];
+ mrb_int len;
+ mrb_value str;
+ uint32_t ascii_flag = 0;
+
+ if (cp < 0 || 0x10FFFF < cp) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
+ }
+ if (cp < 0x80) {
+ utf8[0] = (char)cp;
+ len = 1;
+ ascii_flag = MRB_STR_ASCII;
+ }
+ else if (cp < 0x800) {
+ utf8[0] = (char)(0xC0 | (cp >> 6));
+ utf8[1] = (char)(0x80 | (cp & 0x3F));
+ len = 2;
+ }
+ else if (cp < 0x10000) {
+ utf8[0] = (char)(0xE0 | (cp >> 12));
+ utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
+ utf8[2] = (char)(0x80 | ( cp & 0x3F));
+ len = 3;
+ }
+ else {
+ utf8[0] = (char)(0xF0 | (cp >> 18));
+ utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
+ utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F));
+ utf8[3] = (char)(0x80 | ( cp & 0x3F));
+ len = 4;
+ }
+ str = mrb_str_new(mrb, utf8, len);
+ mrb_str_ptr(str)->flags |= ascii_flag;
+ return str;
+}
+#endif
+
static mrb_value
mrb_str_getbyte(mrb_state *mrb, mrb_value str)
{
@@ -29,7 +114,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 +127,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;
-
- if (mrb_get_argc(mrb) == 2) {
- mrb_int pos;
- mrb_get_args(mrb, "ii", &pos, &len);
- return mrb_str_substr(mrb, str, pos, len);
+ mrb_value a1, a2;
+ mrb_int str_len = RSTRING_LEN(str), beg, len;
+ mrb_bool empty = TRUE;
+
+ 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();
}
/*
@@ -137,8 +210,6 @@ mrb_str_swapcase(mrb_state *mrb, mrb_value self)
return str;
}
-static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
-
/*
* call-seq:
* str << integer -> str
@@ -148,7 +219,8 @@ static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num);
*
* Append---Concatenates the given object to <i>str</i>. If the object is a
* <code>Integer</code>, it is considered as a codepoint, and is converted
- * to a character before concatenation.
+ * to a character before concatenation
+ * (equivalent to <code>str.concat(integer.chr(__ENCODING__))</code>).
*
* a = "hello "
* a << "world" #=> "hello world"
@@ -160,8 +232,12 @@ mrb_str_concat_m(mrb_state *mrb, mrb_value self)
mrb_value str;
mrb_get_args(mrb, "o", &str);
- if (mrb_fixnum_p(str))
- str = mrb_fixnum_chr(mrb, str);
+ if (mrb_fixnum_p(str) || mrb_float_p(str))
+#ifdef MRB_UTF8_STRING
+ str = int_chr_utf8(mrb, str);
+#else
+ str = int_chr_binary(mrb, str);
+#endif
else
str = mrb_ensure_string_type(mrb, str);
mrb_str_concat(mrb, self, str);
@@ -812,7 +888,7 @@ mrb_str_count(mrb_state *mrb, mrb_value 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 = 0; i < len; i++) {
@@ -848,49 +924,42 @@ mrb_str_chr(mrb_state *mrb, mrb_value self)
return mrb_str_substr(mrb, self, 0, 1);
}
+/*
+ * call-seq:
+ * int.chr([encoding]) -> string
+ *
+ * Returns a string containing the character represented by the +int+'s value
+ * according to +encoding+. +"ASCII-8BIT"+ (+"BINARY"+) and +"UTF-8"+ (only
+ * with +MRB_UTF8_STRING+) can be specified as +encoding+ (default is
+ * +"ASCII-8BIT"+).
+ *
+ * 65.chr #=> "A"
+ * 230.chr #=> "\xE6"
+ * 230.chr("ASCII-8BIT") #=> "\xE6"
+ * 230.chr("UTF-8") #=> "\u00E6"
+ */
static mrb_value
-mrb_fixnum_chr(mrb_state *mrb, mrb_value num)
+mrb_int_chr(mrb_state *mrb, mrb_value num)
{
- mrb_int cp = mrb_fixnum(num);
-#ifdef MRB_UTF8_STRING
- char utf8[4];
- mrb_int len;
-
- if (cp < 0 || 0x10FFFF < cp) {
- mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
+ mrb_value enc;
+ mrb_bool enc_given;
+
+ mrb_get_args(mrb, "|S?", &enc, &enc_given);
+ if (!enc_given ||
+ ENC_COMP_P(enc, ENC_ASCII_8BIT) ||
+ ENC_COMP_P(enc, ENC_BINARY)) {
+ return int_chr_binary(mrb, num);
}
- if (cp < 0x80) {
- utf8[0] = (char)cp;
- len = 1;
- }
- else if (cp < 0x800) {
- utf8[0] = (char)(0xC0 | (cp >> 6));
- utf8[1] = (char)(0x80 | (cp & 0x3F));
- len = 2;
- }
- else if (cp < 0x10000) {
- utf8[0] = (char)(0xE0 | (cp >> 12));
- utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
- utf8[2] = (char)(0x80 | ( cp & 0x3F));
- len = 3;
+#ifdef MRB_UTF8_STRING
+ else if (ENC_COMP_P(enc, ENC_UTF8)) {
+ return int_chr_utf8(mrb, num);
}
+#endif
else {
- utf8[0] = (char)(0xF0 | (cp >> 18));
- utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
- utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F));
- utf8[3] = (char)(0x80 | ( cp & 0x3F));
- len = 4;
- }
- return mrb_str_new(mrb, utf8, len);
-#else
- char c;
-
- if (cp < 0 || 0xff < cp) {
- mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown encoding name - %S", enc);
}
- c = (char)cp;
- return mrb_str_new(mrb, &c, 1);
-#endif
+ /* not reached */
+ return mrb_nil_value();
}
/*
@@ -1231,7 +1300,8 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, s, "delete_suffix", mrb_str_del_suffix, MRB_ARGS_REQ(1));
mrb_define_method(mrb, s, "__lines", mrb_str_lines, MRB_ARGS_NONE());
- mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, mrb_module_get(mrb, "Integral"), "chr", mrb_int_chr, MRB_ARGS_OPT(1));
}
void
diff --git a/mrbgems/mruby-string-ext/test/numeric.rb b/mrbgems/mruby-string-ext/test/numeric.rb
new file mode 100644
index 000000000..dfcb9ebf4
--- /dev/null
+++ b/mrbgems/mruby-string-ext/test/numeric.rb
@@ -0,0 +1,29 @@
+# coding: utf-8
+
+assert('Integer#chr') do
+ assert_equal("A", 65.chr)
+ assert_equal("B", 0x42.chr)
+ assert_equal("\xab", 171.chr)
+ assert_raise(RangeError) { -1.chr }
+ assert_raise(RangeError) { 256.chr }
+
+ assert_equal("A", 65.chr("ASCII-8BIT"))
+ assert_equal("B", 0x42.chr("BINARY"))
+ assert_equal("\xab", 171.chr("ascii-8bit"))
+ assert_raise(RangeError) { -1.chr("binary") }
+ assert_raise(RangeError) { 256.chr("Ascii-8bit") }
+ assert_raise(ArgumentError) { 65.chr("ASCII") }
+ assert_raise(ArgumentError) { 65.chr("ASCII-8BIT", 2) }
+ assert_raise(TypeError) { 65.chr(:BINARY) }
+
+ if __ENCODING__ == "ASCII-8BIT"
+ assert_raise(ArgumentError) { 65.chr("UTF-8") }
+ else
+ assert_equal("A", 65.chr("UTF-8"))
+ assert_equal("B", 0x42.chr("UTF-8"))
+ assert_equal("«", 171.chr("utf-8"))
+ assert_equal("あ", 12354.chr("Utf-8"))
+ assert_raise(RangeError) { -1.chr("utf-8") }
+ assert_raise(RangeError) { 0x110000.chr.chr("UTF-8") }
+ end
+end
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 44ca1fde2..8f1d25f29 100644
--- a/mrbgems/mruby-string-ext/test/string.rb
+++ b/mrbgems/mruby-string-ext/test/string.rb
@@ -2,7 +2,7 @@
##
# String(Ext) Test
-UTF8STRING = ("\343\201\202".size == 1)
+UTF8STRING = __ENCODING__ == "UTF-8"
assert('String#getbyte') do
str1 = "hello"
@@ -26,10 +26,61 @@ 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
@@ -116,8 +167,15 @@ end
assert('String#concat') do
assert_equal "Hello World!", "Hello " << "World" << 33
assert_equal "Hello World!", "Hello ".concat("World").concat(33)
-
assert_raise(TypeError) { "".concat(Object.new) }
+
+ if UTF8STRING
+ assert_equal "H«", "H" << 0xab
+ assert_equal "Hは", "H" << 12399
+ else
+ assert_equal "H\xab", "H" << 0xab
+ assert_raise(RangeError) { "H" << 12399 }
+ end
end
assert('String#casecmp') do
@@ -247,12 +305,6 @@ assert('String#oct') do
assert_equal (-8), "-10".oct
end
-assert('String#chr') do
- assert_equal "a", "abcde".chr
- # test Fixnum#chr as well
- assert_equal "a", 97.chr
-end
-
assert('String#lines') do
assert_equal ["Hel\n", "lo\n", "World!"], "Hel\nlo\nWorld!".lines
assert_equal ["Hel\n", "lo\n", "World!\n"], "Hel\nlo\nWorld!\n".lines
@@ -630,8 +682,11 @@ assert('String#ord(UTF-8)') do
end if UTF8STRING
assert('String#chr') do
+ assert_equal "a", "abcde".chr
assert_equal "h", "hello!".chr
+ assert_equal "", "".chr
end
+
assert('String#chr(UTF-8)') do
assert_equal "こ", "こんにちは世界!".chr
end if UTF8STRING
@@ -657,19 +712,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 c5b5354be..7682ac033 100644
--- a/mrbgems/mruby-struct/mrblib/struct.rb
+++ b/mrbgems/mruby-struct/mrblib/struct.rb
@@ -46,7 +46,9 @@ if Object.const_defined?(:Struct)
ary
end
- def _inspect
+ def _inspect(recur_list)
+ return "#<struct #{self.class}:...>" if recur_list[self.object_id]
+ recur_list[self.object_id] = true
name = self.class.to_s
if name[0] == "#"
str = "#<struct "
@@ -55,7 +57,7 @@ if Object.const_defined?(:Struct)
end
buf = []
self.each_pair do |k,v|
- buf.push [k.to_s + "=" + v._inspect]
+ buf.push [k.to_s + "=" + v._inspect(recur_list)]
end
str + buf.join(", ") + ">"
end
@@ -70,11 +72,7 @@ if Object.const_defined?(:Struct)
# 15.2.18.4.10(x)
#
def inspect
- begin
- self._inspect
- rescue SystemStackError
- "#<struct #{self.class.to_s}:...>"
- end
+ self._inspect({})
end
##
diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c
index 1df135a9f..e04fe13ad 100644
--- a/mrbgems/mruby-struct/src/struct.c
+++ b/mrbgems/mruby-struct/src/struct.c
@@ -123,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;
}
@@ -158,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)
{
@@ -182,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);
}
}
@@ -212,7 +204,7 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass *kl
/* old style: should we warn? */
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)) {
@@ -403,16 +395,17 @@ struct_aref_sym(mrb_state *mrb, mrb_value obj, mrb_sym id)
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 c298fef9f..93930730b 100644
--- a/mrbgems/mruby-struct/test/struct.rb
+++ b/mrbgems/mruby-struct/test/struct.rb
@@ -116,9 +116,10 @@ assert('struct dup') do
end
assert('struct inspect') do
- c = Struct.new(:m1, :m2, :m3, :m4, :m5)
- cc = c.new(1,2,3,4,5)
- assert_equal "#<struct m1=1, m2=2, m3=3, m4=4, m5=5>", cc.inspect
+ c = Struct.new(:m1, :m2, :m3, :m4, :m5, :recur)
+ cc = c.new(1,2,3,4,5,nil)
+ cc.recur = cc
+ assert_equal "#<struct m1=1, m2=2, m3=3, m4=4, m5=5, recur=#<struct #{cc.class}:...>>", cc.inspect
end
assert('Struct#length, Struct#size') do
@@ -152,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
@@ -181,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
diff --git a/mrbgems/mruby-symbol-ext/test/symbol.rb b/mrbgems/mruby-symbol-ext/test/symbol.rb
index 61ecad247..db686e5f4 100644
--- a/mrbgems/mruby-symbol-ext/test/symbol.rb
+++ b/mrbgems/mruby-symbol-ext/test/symbol.rb
@@ -14,7 +14,7 @@ end
assert("Symbol##{n}") do
assert_equal 5, :hello.__send__(n)
assert_equal 4, :"aA\0b".__send__(n)
- if "あ".size == 1 # enable MRB_UTF8_STRING?
+ if __ENCODING__ == "UTF-8"
assert_equal 8, :"こんにちは世界!".__send__(n)
assert_equal 4, :"aあ\0b".__send__(n)
else
diff --git a/mrbgems/mruby-test/driver.c b/mrbgems/mruby-test/driver.c
index 602b76c79..b6c9fab35 100644
--- a/mrbgems/mruby-test/driver.c
+++ b/mrbgems/mruby-test/driver.c
@@ -258,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(warning_test);
TEST_COUNT_PASS(skip_test);
#undef TEST_COUNT_PASS
diff --git a/mrbgems/mruby-time/include/mruby/time.h b/mrbgems/mruby-time/include/mruby/time.h
index d71f4ccd3..1adcfd49c 100644
--- a/mrbgems/mruby-time/include/mruby/time.h
+++ b/mrbgems/mruby-time/include/mruby/time.h
@@ -8,6 +8,7 @@
#define MRUBY_TIME_H
#include "mruby/common.h"
+#include <time.h>
MRB_BEGIN_DECL
@@ -18,7 +19,7 @@ typedef enum mrb_timezone {
MRB_TIMEZONE_LAST = 3
} mrb_timezone;
-MRB_API mrb_value mrb_time_at(mrb_state *mrb, double sec, double usec, mrb_timezone timezone);
+MRB_API mrb_value mrb_time_at(mrb_state *mrb, time_t sec, time_t usec, mrb_timezone timezone);
MRB_END_DECL
diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c
index 7f6c3004d..18010476b 100644
--- a/mrbgems/mruby-time/src/time.c
+++ b/mrbgems/mruby-time/src/time.c
@@ -4,8 +4,10 @@
** See Copyright Notice in mruby.h
*/
+#ifndef MRB_WITHOUT_FLOAT
#include <math.h>
-#include <time.h>
+#endif
+
#include <mruby.h>
#include <mruby/class.h>
#include <mruby/data.h>
@@ -18,6 +20,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) {
@@ -25,8 +28,10 @@ double round(double x) {
}
#endif
-#if !defined(__MINGW64__) && defined(_WIN32)
-# define llround(x) round(x)
+#ifdef MRB_WITHOUT_FLOAT
+# if !defined(__MINGW64__) && defined(_WIN32)
+# define llround(x) round(x)
+# endif
#endif
#if defined(__MINGW64__) || defined(__MINGW32__)
@@ -197,6 +202,88 @@ struct mrb_time {
static const struct mrb_data_type mrb_time_type = { "Time", mrb_free };
+#ifndef MRB_WITHOUT_FLOAT
+void mrb_check_num_exact(mrb_state *mrb, mrb_float num);
+typedef mrb_float mrb_sec;
+#define mrb_sec_value(mrb, sec) mrb_float_value(mrb, sec)
+#else
+typedef mrb_int mrb_sec;
+#define mrb_sec_value(mrb, sec) mrb_fixnum_value(sec)
+#endif
+
+static time_t
+mrb_to_time_t(mrb_state *mrb, mrb_value obj, time_t *usec)
+{
+ time_t t;
+
+ switch (mrb_type(obj)) {
+#ifndef MRB_WITHOUT_FLOAT
+ case MRB_TT_FLOAT:
+ {
+ mrb_float f = mrb_float(obj);
+
+ mrb_check_num_exact(mrb, f);
+# ifndef MRB_TIME_T_UINT
+ if (sizeof(time_t) == 4 && (f > (mrb_float)INT32_MAX || (mrb_float)INT32_MIN > f)) {
+ goto out_of_range;
+ }
+ if (sizeof(time_t) == 8 && (f > (mrb_float)INT64_MAX || (mrb_float)INT64_MIN > f)) {
+ goto out_of_range;
+ }
+# else
+ if (sizeof(time_t) == 4 && (f > (mrb_float)UINT32_MAX || (mrb_float)0 > f)) {
+ goto out_of_range;
+ }
+ if (sizeof(time_t) == 8 && (f > (mrb_float)UINT64_MAX || (mrb_float)0 > f)) {
+ goto out_of_range;
+ }
+# endif
+
+ if (usec) {
+ t = (time_t)f;
+ *usec = (time_t)llround((f - t) * 1.0e+6);
+ }
+ else {
+ t = (time_t)llround(f);
+ }
+ }
+ break;
+#endif /* MRB_WITHOUT_FLOAT */
+ default:
+ case MRB_TT_FIXNUM:
+ {
+ mrb_int i = mrb_int(mrb, obj);
+
+#ifndef MRB_TIME_T_UINT
+ if (sizeof(time_t) == 4 && (i > INT32_MAX || INT32_MIN > i)) {
+ goto out_of_range;
+ }
+ if (sizeof(time_t) == 8 && (i > INT64_MAX || INT64_MIN > i)) {
+ goto out_of_range;
+ }
+#else
+ if (sizeof(time_t) == 4 && (i > UINT32_MAX || 0 > i)) {
+ goto out_of_range;
+ }
+ if (sizeof(time_t) == 8 && (i > UINT64_MAX || 0 > i)) {
+ goto out_of_range;
+ }
+#endif
+
+ t = (time_t)i;
+ if (usec) { *usec = 0; }
+ }
+ break;
+ }
+
+ return t;
+
+out_of_range:
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", obj);
+
+ return 0; /* suppress compiler warnings */
+}
+
/** Updates the datetime of a mrb_time based on it's timezone and
seconds setting. Returns self on success, NULL of failure.
if `dealloc` is set `true`, it frees `self` on error. */
@@ -204,18 +291,19 @@ static struct mrb_time*
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_float sec = (mrb_float)self->sec;
+ mrb_sec sec = (mrb_sec)t;
- mrb_free(mrb, self);
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
+ if (dealloc) mrb_free(mrb, self);
+ mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_sec_value(mrb, sec));
/* not reached */
return NULL;
}
@@ -232,40 +320,15 @@ mrb_time_wrap(mrb_state *mrb, struct RClass *tc, struct mrb_time *tm)
return mrb_obj_value(Data_Wrap_Struct(mrb, tc, &mrb_time_type, tm));
}
-void mrb_check_num_exact(mrb_state *mrb, mrb_float num);
-
/* Allocates a mrb_time object and initializes it. */
static struct mrb_time*
-time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
+time_alloc_time(mrb_state *mrb, time_t sec, time_t usec, enum mrb_timezone timezone)
{
struct mrb_time *tm;
- time_t tsec = 0;
- mrb_check_num_exact(mrb, (mrb_float)sec);
- mrb_check_num_exact(mrb, (mrb_float)usec);
-#ifndef MRB_TIME_T_UINT
- if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) {
- goto out_of_range;
- }
- if (sizeof(time_t) == 8 && (sec > (double)INT64_MAX || (double)INT64_MIN > sec)) {
- goto out_of_range;
- }
-#else
- if (sizeof(time_t) == 4 && (sec > (double)UINT32_MAX || (double)0 > sec)) {
- goto out_of_range;
- }
- if (sizeof(time_t) == 8 && (sec > (double)UINT64_MAX || (double)0 > sec)) {
- goto out_of_range;
- }
-#endif
- tsec = (time_t)sec;
- if ((sec > 0 && tsec < 0) || (sec < 0 && (double)tsec > sec)) {
- out_of_range:
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
- }
tm = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time));
- tm->sec = tsec;
- tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec);
+ tm->sec = sec;
+ tm->usec = usec;
if (tm->usec < 0) {
long sec2 = (long)NDIV(tm->usec,1000000); /* negative div */
tm->usec -= sec2 * 1000000;
@@ -282,8 +345,25 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone)
return tm;
}
+static struct mrb_time*
+time_alloc(mrb_state *mrb, mrb_value sec, mrb_value usec, enum mrb_timezone timezone)
+{
+ time_t tsec, tusec;
+
+ tsec = mrb_to_time_t(mrb, sec, &tusec);
+ tusec += mrb_to_time_t(mrb, usec, NULL);
+
+ return time_alloc_time(mrb, tsec, tusec, timezone);
+}
+
static mrb_value
-mrb_time_make(mrb_state *mrb, struct RClass *c, double sec, double usec, enum mrb_timezone timezone)
+mrb_time_make_time(mrb_state *mrb, struct RClass *c, time_t sec, time_t usec, enum mrb_timezone timezone)
+{
+ return mrb_time_wrap(mrb, c, time_alloc_time(mrb, sec, usec, timezone));
+}
+
+static mrb_value
+mrb_time_make(mrb_state *mrb, struct RClass *c, mrb_value sec, mrb_value usec, enum mrb_timezone timezone)
{
return mrb_time_wrap(mrb, c, time_alloc(mrb, sec, usec, timezone));
}
@@ -291,24 +371,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) && !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;
@@ -317,17 +397,20 @@ 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, TRUE);
@@ -342,9 +425,9 @@ mrb_time_now(mrb_state *mrb, mrb_value self)
}
MRB_API mrb_value
-mrb_time_at(mrb_state *mrb, double sec, double usec, enum mrb_timezone zone)
+mrb_time_at(mrb_state *mrb, time_t sec, time_t usec, enum mrb_timezone zone)
{
- return mrb_time_make(mrb, mrb_class_get(mrb, "Time"), sec, usec, zone);
+ return mrb_time_make_time(mrb, mrb_class_get(mrb, "Time"), sec, usec, zone);
}
/* 15.2.19.6.1 */
@@ -352,10 +435,12 @@ mrb_time_at(mrb_state *mrb, double sec, double usec, enum mrb_timezone zone)
static mrb_value
mrb_time_at_m(mrb_state *mrb, mrb_value self)
{
- mrb_float f, f2 = 0;
+ mrb_value sec;
+ mrb_value usec = mrb_fixnum_value(0);
+
+ mrb_get_args(mrb, "o|o", &sec, &usec);
- mrb_get_args(mrb, "f|f", &f, &f2);
- return mrb_time_make(mrb, mrb_class_ptr(self), f, f2, MRB_TIMEZONE_LOCAL);
+ return mrb_time_make(mrb, mrb_class_ptr(self), sec, usec, MRB_TIMEZONE_LOCAL);
}
static struct mrb_time*
@@ -392,7 +477,7 @@ time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday,
mrb_raise(mrb, E_ARGUMENT_ERROR, "Not a valid time.");
}
- return time_alloc(mrb, (double)nowsecs, (double)ausec, timezone);
+ return time_alloc_time(mrb, nowsecs, ausec, timezone);
}
/* 15.2.19.6.2 */
@@ -478,18 +563,19 @@ mrb_time_cmp(mrb_state *mrb, mrb_value self)
static mrb_value
mrb_time_plus(mrb_state *mrb, mrb_value self)
{
- mrb_float f;
+ mrb_value o;
struct mrb_time *tm;
+ time_t sec, usec;
- mrb_get_args(mrb, "f", &f);
+ mrb_get_args(mrb, "o", &o);
tm = time_get_ptr(mrb, self);
- return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec+f, (double)tm->usec, tm->timezone);
+ sec = mrb_to_time_t(mrb, o, &usec);
+ return mrb_time_make_time(mrb, mrb_obj_class(mrb, self), tm->sec+sec, tm->usec+usec, tm->timezone);
}
static mrb_value
mrb_time_minus(mrb_state *mrb, mrb_value self)
{
- mrb_float f;
mrb_value other;
struct mrb_time *tm, *tm2;
@@ -497,13 +583,22 @@ mrb_time_minus(mrb_state *mrb, mrb_value self)
tm = time_get_ptr(mrb, self);
tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time);
if (tm2) {
- f = (mrb_float)(tm->sec - tm2->sec)
- + (mrb_float)(tm->usec - tm2->usec) / 1.0e6;
+#ifndef MRB_WITHOUT_FLOAT
+ mrb_float f;
+ f = (mrb_sec)(tm->sec - tm2->sec)
+ + (mrb_sec)(tm->usec - tm2->usec) / 1.0e6;
return mrb_float_value(mrb, f);
+#else
+ mrb_int f;
+ f = tm->sec - tm2->sec;
+ if (tm->usec < tm2->usec) f--;
+ return mrb_fixnum_value(f);
+#endif
}
else {
- mrb_get_args(mrb, "f", &f);
- return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec-f, (double)tm->usec, tm->timezone);
+ time_t sec, usec;
+ sec = mrb_to_time_t(mrb, other, &usec);
+ return mrb_time_make_time(mrb, mrb_obj_class(mrb, self), tm->sec-sec, tm->usec-usec, tm->timezone);
}
}
@@ -576,10 +671,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);
@@ -761,7 +855,7 @@ mrb_time_sec(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(tm->datetime.tm_sec);
}
-
+#ifndef MRB_WITHOUT_FLOAT
/* 15.2.19.7.24 */
/* Returns a Float with the time since the epoch in seconds. */
static mrb_value
@@ -772,32 +866,37 @@ mrb_time_to_f(mrb_state *mrb, mrb_value self)
tm = time_get_ptr(mrb, self);
return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6);
}
+#endif
/* 15.2.19.7.25 */
-/* Returns a Fixnum with the time since the epoch in seconds. */
+/* Returns an Integer with the time since the epoch in seconds. */
static mrb_value
mrb_time_to_i(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
tm = time_get_ptr(mrb, self);
+#ifndef MRB_WITHOUT_FLOAT
if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) {
return mrb_float_value(mrb, (mrb_float)tm->sec);
}
+#endif
return mrb_fixnum_value((mrb_int)tm->sec);
}
/* 15.2.19.7.26 */
-/* Returns a Float with the time since the epoch in microseconds. */
+/* Returns an Integer with the time since the epoch in microseconds. */
static mrb_value
mrb_time_usec(mrb_state *mrb, mrb_value self)
{
struct mrb_time *tm;
tm = time_get_ptr(mrb, self);
+#ifndef MRB_WITHOUT_FLOAT
if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) {
return mrb_float_value(mrb, (mrb_float)tm->usec);
}
+#endif
return mrb_fixnum_value((mrb_int)tm->usec);
}
@@ -825,6 +924,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)
@@ -845,8 +953,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 */
@@ -866,7 +974,9 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
mrb_define_method(mrb, tc, "sec" , mrb_time_sec, MRB_ARGS_NONE()); /* 15.2.19.7.23 */
mrb_define_method(mrb, tc, "to_i", mrb_time_to_i, MRB_ARGS_NONE()); /* 15.2.19.7.25 */
+#ifndef MRB_WITHOUT_FLOAT
mrb_define_method(mrb, tc, "to_f", mrb_time_to_f, MRB_ARGS_NONE()); /* 15.2.19.7.24 */
+#endif
mrb_define_method(mrb, tc, "usec", mrb_time_usec, MRB_ARGS_NONE()); /* 15.2.19.7.26 */
mrb_define_method(mrb, tc, "utc" , mrb_time_utc, MRB_ARGS_NONE()); /* 15.2.19.7.27 */
mrb_define_method(mrb, tc, "utc?", mrb_time_utc_p,MRB_ARGS_NONE()); /* 15.2.19.7.28 */
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/mrblib/array.rb b/mrblib/array.rb
index d598efc77..2b080564c 100644
--- a/mrblib/array.rb
+++ b/mrblib/array.rb
@@ -83,13 +83,15 @@ class Array
self
end
- def _inspect
+ def _inspect(recur_list)
size = self.size
return "[]" if size == 0
+ return "[...]" if recur_list[self.object_id]
+ recur_list[self.object_id] = true
ary=[]
i=0
while i<size
- ary<<self[i].inspect
+ ary<<self[i]._inspect(recur_list)
i+=1
end
"["+ary.join(", ")+"]"
@@ -99,11 +101,7 @@ class Array
#
# ISO 15.2.12.5.31 (x)
def inspect
- begin
- self._inspect
- rescue SystemStackError
- "[...]"
- end
+ self._inspect({})
end
# ISO 15.2.12.5.32 (x)
alias to_s inspect
diff --git a/mrblib/enum.rb b/mrblib/enum.rb
index 9bd74e1c4..fe40b4d27 100644
--- a/mrblib/enum.rb
+++ b/mrblib/enum.rb
@@ -65,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/hash.rb b/mrblib/hash.rb
index 609883ecb..b49e987c7 100644
--- a/mrblib/hash.rb
+++ b/mrblib/hash.rb
@@ -186,15 +186,17 @@ class Hash
end
# internal method for Hash inspection
- def _inspect
+ def _inspect(recur_list)
return "{}" if self.size == 0
+ return "{...}" if recur_list[self.object_id]
+ recur_list[self.object_id] = true
ary=[]
keys=self.keys
size=keys.size
i=0
while i<size
k=keys[i]
- ary<<(k._inspect + "=>" + self[k]._inspect)
+ ary<<(k._inspect(recur_list) + "=>" + self[k]._inspect(recur_list))
i+=1
end
"{"+ary.join(", ")+"}"
@@ -204,11 +206,7 @@ class Hash
#
# ISO 15.2.13.4.30 (x)
def inspect
- begin
- self._inspect
- rescue SystemStackError
- "{...}"
- end
+ self._inspect({})
end
# ISO 15.2.13.4.31 (x)
alias to_s inspect
diff --git a/mrblib/kernel.rb b/mrblib/kernel.rb
index 4700684b6..7c3ea9420 100644
--- a/mrblib/kernel.rb
+++ b/mrblib/kernel.rb
@@ -40,7 +40,7 @@ module Kernel
end
# internal method for inspect
- def _inspect
+ def _inspect(_recur_list)
self.inspect
end
diff --git a/mrblib/numeric.rb b/mrblib/numeric.rb
index 5a3c5eb58..5926518d5 100644
--- a/mrblib/numeric.rb
+++ b/mrblib/numeric.rb
@@ -105,14 +105,14 @@ module Integral
return to_enum(:step, num, step) unless block
i = __coerce_step_counter(num, step)
- if num == nil
+ 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
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 c92a9e7be..c26cdb1e2 100644
--- a/mrblib/string.rb
+++ b/mrblib/string.rb
@@ -165,20 +165,9 @@ class String
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
@@ -188,80 +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[0], args[1].__to_str
- case pos
- when String
- posnum = self.index(pos)
- if posnum
- b = self[0, posnum]
- 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 = pos.__to_int
- 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]
- a = self[pos + 1..-1]
- self.replace([b, value, a].join(''))
- end
- return value
- elsif anum == 3
- pos, len, value = args[0].__to_int, args[1].__to_int, args[2].__to_str
- 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]
- 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
-
- def _regexp(re, mid)
- if String === re
- if Object.const_defined?(:Regexp)
- return Regexp.new(re)
- else
- raise NotImplementedError, "String##{mid} needs Regexp class"
- end
- end
- re
- end
-
+ # those two methods requires Regexp that is optional in mruby
##
# ISO 15.2.10.5.3
- def =~(re)
- _regexp(re, :=~) =~ self
- end
+ #def =~(re)
+ # re =~ self
+ #end
##
# ISO 15.2.10.5.27
- def match(re, &block)
- _regexp(re, :match).match(self, &block)
- end
+ #def match(re, &block)
+ # re.match(self, &block)
+ #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 d4302cb22..8cf813743 100644
--- a/src/array.c
+++ b/src/array.c
@@ -858,7 +858,7 @@ mrb_ary_aget(mrb_state *mrb, mrb_value self)
switch (mrb_type(index)) {
/* a[n..m] */
case MRB_TT_RANGE:
- if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), 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 {
@@ -927,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;
}
@@ -1262,39 +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_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_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, "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_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 e4f5a3064..991a67d00 100644
--- a/src/backtrace.c
+++ b/src/backtrace.c
@@ -16,7 +16,7 @@
#include <mruby/data.h>
struct backtrace_location {
- int lineno;
+ int32_t lineno;
mrb_sym method_id;
const char *filename;
};
diff --git a/src/class.c b/src/class.c
index 254f5b005..65d21ad4f 100644
--- a/src/class.c
+++ b/src/class.c
@@ -66,15 +66,21 @@ mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb
name = mrb_class_path(mrb, outer);
if (mrb_nil_p(name)) { /* unnamed outer class */
if (outer != mrb->object_class && outer != c) {
- mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"),
- mrb_obj_value(outer));
+ 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
@@ -567,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
@@ -838,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++;
}
@@ -1086,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");
}
}
@@ -1098,6 +1082,7 @@ mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m)
struct RClass *origin;
int changed = 0;
+ 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_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED;
@@ -1378,15 +1363,52 @@ mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid)
return m;
}
+#define ONSTACK_ALLOC_MAX 32
+
+static mrb_sym
+prepare_name_common(mrb_state *mrb, mrb_sym sym, const char *prefix, const char *suffix)
+{
+ char onstack[ONSTACK_ALLOC_MAX];
+ mrb_int sym_len;
+ const char *sym_str = mrb_sym2name_len(mrb, sym, &sym_len);
+ size_t prefix_len = prefix ? strlen(prefix) : 0;
+ size_t suffix_len = suffix ? strlen(suffix) : 0;
+ size_t name_len = sym_len + prefix_len + suffix_len;
+ char *buf = name_len > sizeof(onstack) ? (char *)mrb_alloca(mrb, name_len) : onstack;
+ char *p = buf;
+
+ if (prefix_len > 0) {
+ memcpy(p, prefix, prefix_len);
+ p += prefix_len;
+ }
+
+ memcpy(p, sym_str, sym_len);
+ p += sym_len;
+
+ if (suffix_len > 0) {
+ memcpy(p, suffix, suffix_len);
+ p += suffix_len;
+ }
+
+ return mrb_intern(mrb, buf, name_len);
+}
+
static mrb_value
-attr_reader(mrb_state *mrb, mrb_value obj)
+prepare_ivar_name(mrb_state *mrb, mrb_sym sym)
{
- mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
- return mrb_iv_get(mrb, obj, to_sym(mrb, name));
+ sym = prepare_name_common(mrb, sym, "@", NULL);
+ mrb_iv_name_sym_check(mrb, sym);
+ return mrb_symbol_value(sym);
+}
+
+static mrb_sym
+prepare_writer_name(mrb_state *mrb, mrb_sym sym)
+{
+ return prepare_name_common(mrb, sym, NULL, "=");
}
static mrb_value
-mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
+mod_attr_define(mrb_state *mrb, mrb_value mod, mrb_value (*accessor)(mrb_state *, mrb_value), mrb_sym (*access_name)(mrb_state *, mrb_sym))
{
struct RClass *c = mrb_class_ptr(mod);
mrb_value *argv;
@@ -1396,20 +1418,18 @@ mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
mrb_get_args(mrb, "*", &argv, &argc);
ai = mrb_gc_arena_save(mrb);
for (i=0; i<argc; i++) {
- mrb_value name, str;
- mrb_sym method, sym;
+ mrb_value name;
+ mrb_sym method;
struct RProc *p;
mrb_method_t m;
method = to_sym(mrb, argv[i]);
- name = mrb_sym2str(mrb, method);
- str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1);
- mrb_str_cat_lit(mrb, str, "@");
- mrb_str_cat_str(mrb, str, name);
- sym = mrb_intern_str(mrb, str);
- mrb_iv_name_sym_check(mrb, sym);
- name = mrb_symbol_value(sym);
- p = mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name);
+ name = prepare_ivar_name(mrb, method);
+ if (access_name) {
+ method = access_name(mrb, method);
+ }
+
+ p = mrb_proc_new_cfunc_with_env(mrb, accessor, 1, &name);
MRB_METHOD_FROM_PROC(m, p);
mrb_define_method_raw(mrb, c, method, m);
mrb_gc_arena_restore(mrb, ai);
@@ -1418,6 +1438,19 @@ mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
}
static mrb_value
+attr_reader(mrb_state *mrb, mrb_value obj)
+{
+ mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
+ return mrb_iv_get(mrb, obj, to_sym(mrb, name));
+}
+
+static mrb_value
+mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
+{
+ return mod_attr_define(mrb, mod, attr_reader, NULL);
+}
+
+static mrb_value
attr_writer(mrb_state *mrb, mrb_value obj)
{
mrb_value name = mrb_proc_cfunc_env_get(mrb, 0);
@@ -1431,42 +1464,7 @@ attr_writer(mrb_state *mrb, mrb_value obj)
static mrb_value
mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
{
- struct RClass *c = mrb_class_ptr(mod);
- mrb_value *argv;
- mrb_int argc, i;
- int ai;
-
- mrb_get_args(mrb, "*", &argv, &argc);
- ai = mrb_gc_arena_save(mrb);
- for (i=0; i<argc; i++) {
- mrb_value name, str, attr;
- mrb_sym method, sym;
- struct RProc *p;
- mrb_method_t m;
-
- method = to_sym(mrb, argv[i]);
-
- /* prepare iv name (@name) */
- name = mrb_sym2str(mrb, method);
- str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1);
- mrb_str_cat_lit(mrb, str, "@");
- mrb_str_cat_str(mrb, str, name);
- sym = mrb_intern_str(mrb, str);
- mrb_iv_name_sym_check(mrb, sym);
- attr = mrb_symbol_value(sym);
-
- /* prepare method name (name=) */
- str = mrb_str_new_capa(mrb, RSTRING_LEN(str));
- mrb_str_cat_str(mrb, str, name);
- mrb_str_cat_lit(mrb, str, "=");
- method = mrb_intern_str(mrb, str);
-
- p = mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr);
- MRB_METHOD_FROM_PROC(m, p);
- mrb_define_method_raw(mrb, c, method, m);
- mrb_gc_arena_restore(mrb, ai);
- }
- return mrb_nil_value();
+ return mod_attr_define(mrb, mod, attr_writer, prepare_writer_name);
}
static mrb_value
@@ -1874,18 +1872,12 @@ mrb_mod_undef(mrb_state *mrb, mrb_value mod)
return mrb_nil_value();
}
-static mrb_bool
-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
check_const_name_sym(mrb_state *mrb, mrb_sym id)
{
mrb_int len;
const char *name = mrb_sym2name_len(mrb, id, &len);
- if (!const_name_p(mrb, name, len)) {
+ if (!mrb_const_name_p(mrb, name, len)) {
mrb_name_error(mrb, id, "wrong constant name %S", mrb_sym2str(mrb, id));
}
}
@@ -2086,6 +2078,14 @@ mrb_mod_eqq(mrb_state *mrb, mrb_value mod)
}
static mrb_value
+mrb_mod_dup(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mod = mrb_obj_clone(mrb, self);
+ MRB_UNSET_FROZEN_FLAG(mrb_obj_ptr(mod));
+ return mod;
+}
+
+static mrb_value
mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
{
mrb_value *argv;
@@ -2176,6 +2176,7 @@ mrb_init_class(mrb_state *mrb)
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.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));
@@ -2213,7 +2214,8 @@ mrb_init_class(mrb_state *mrb)
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, "===", mrb_mod_eqq, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); /* 15.2.2.4.7 */
+ mrb_define_method(mrb, mod, "dup", mrb_mod_dup, MRB_ARGS_NONE());
mrb_undef_method(mrb, cls, "append_features");
mrb_undef_method(mrb, cls, "extend_object");
diff --git a/src/etc.c b/src/etc.c
index ac1540f92..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,24 +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);
- /* not reached */
- }
- /* 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
diff --git a/src/gc.c b/src/gc.c
index 27f3716ec..b05d929a1 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -110,7 +110,7 @@ typedef struct {
struct RHash hash;
struct RRange range;
struct RData data;
- struct RIstruct istruct;
+ struct RIStruct istruct;
struct RProc proc;
struct REnv env;
struct RFiber fiber;
@@ -277,6 +277,13 @@ 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)
{
@@ -466,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);
@@ -480,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());
@@ -493,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);
diff --git a/src/hash.c b/src/hash.c
index a9367a426..2a0a19363 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -240,7 +240,7 @@ ht_compact(mrb_state *mrb, htable *t)
if (!seg->next && i >= t->last_len) {
goto exit;
}
- if (mrb_undef_p(k)) { /* found delete key */
+ if (mrb_undef_p(k)) { /* found deleted key */
if (seg2 == NULL) {
seg2 = seg;
i2 = i;
@@ -546,6 +546,7 @@ ht_copy(mrb_state *mrb, htable *t)
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;
@@ -1378,10 +1379,21 @@ mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2)
* 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.rehash #=> {"AA"=>"b"}
- * h["AA"] #=> "b"
+ * 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)
diff --git a/src/kernel.c b/src/kernel.c
index 45bca7558..f223be9fc 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -780,11 +780,9 @@ 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 */
@@ -807,5 +805,4 @@ mrb_init_kernel(mrb_state *mrb)
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_define_alias(mrb, mrb->module_class, "dup", "clone"); /* XXX */
}
diff --git a/src/load.c b/src/load.c
index ab0346750..97eafdbb5 100644
--- a/src/load.c
+++ b/src/load.c
@@ -233,66 +233,6 @@ read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
return read_irep_record(mrb, bin, &len, flags);
}
-/* ignore lineno record */
-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;
-
- *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);
- 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;
- }
- for (i = 0; i < niseq; i++) {
- bin += sizeof(uint16_t); /* niseq */
- *len += sizeof(uint16_t);
- }
-
- 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)
{
@@ -519,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;
@@ -539,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;
@@ -561,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;
}
@@ -578,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);
@@ -608,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);
@@ -651,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*
@@ -676,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;
}
@@ -685,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/numeric.c b/src/numeric.c
index 8205e41f6..a1299a74f 100644
--- a/src/numeric.c
+++ b/src/numeric.c
@@ -44,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
/*
@@ -56,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
@@ -103,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 */
/*
@@ -114,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:
@@ -136,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;
@@ -155,19 +170,19 @@ num_div(mrb_state *mrb, mrb_value x)
}
static mrb_value
-num_coerce_step_counter(mrb_state *mrb, mrb_value self)
+integral_coerce_step_counter(mrb_state *mrb, mrb_value self)
{
- mrb_value counter = self, num, step;
+ 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)) {
- counter = mrb_funcall(mrb, counter, "to_f", 0);
+ return mrb_Float(mrb, self);
}
#endif
- return counter;
+ return self;
}
#ifndef MRB_WITHOUT_FLOAT
@@ -421,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);
@@ -484,6 +499,10 @@ 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);
@@ -503,10 +522,7 @@ 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
@@ -612,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 */
@@ -638,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 */
@@ -692,6 +702,7 @@ flo_round(mrb_state *mrb, mrb_value num)
f = 1.0;
i = ndigits >= 0 ? ndigits : -ndigits;
+ if (ndigits > DBL_DIG+2) return num;
while (--i >= 0)
f = f*10.0;
@@ -722,7 +733,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 */
@@ -744,10 +755,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
@@ -780,8 +788,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;
@@ -805,6 +813,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:
@@ -821,7 +844,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
@@ -933,7 +956,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);
}
@@ -951,7 +974,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);
}
@@ -1230,15 +1253,15 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x)
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;
@@ -1262,6 +1285,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:
@@ -1277,11 +1315,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;
@@ -1304,6 +1342,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 */
/*
@@ -1320,7 +1373,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);
}
@@ -1430,7 +1483,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;
@@ -1450,7 +1503,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;
@@ -1463,7 +1516,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;
@@ -1476,7 +1529,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;
@@ -1489,7 +1542,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;
@@ -1544,21 +1597,21 @@ mrb_init_numeric(mrb_state *mrb)
#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());
- mrb_define_method(mrb, numeric, "__coerce_step_counter", num_coerce_step_counter, MRB_ARGS_REQ(2));
/* Integer Class */
integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */
diff --git a/src/object.c b/src/object.c
index d45ab27c7..7c1879019 100644
--- a/src/object.c
+++ b/src/object.c
@@ -584,11 +584,7 @@ mrb_Float(mrb_state *mrb, mrb_value val)
MRB_API mrb_value
mrb_to_str(mrb_state *mrb, mrb_value val)
{
- if (!mrb_string_p(val)) {
- mrb_value type = inspect_type(mrb, val);
- mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to String", type);
- }
- return val;
+ return mrb_ensure_string_type(mrb, val);
}
/* obsolete: use mrb_ensure_string_type() instead */
diff --git a/src/range.c b/src/range.c
index 21771c8ec..c9dfb2b3c 100644
--- a/src/range.c
+++ b/src/range.c
@@ -92,7 +92,7 @@ range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, m
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");
+ mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "'initialize' called twice");
}
else {
range_ptr_alloc_edges(mrb, r);
@@ -352,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));
@@ -398,13 +398,13 @@ mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
return mrb_range_value(r);
}
-MRB_API mrb_int
+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 0;
+ 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));
@@ -412,11 +412,11 @@ mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp,
if (beg < 0) {
beg += len;
- if (beg < 0) return 2;
+ if (beg < 0) return MRB_RANGE_OUT;
}
if (trunc) {
- if (beg > len) return 2;
+ if (beg > len) return MRB_RANGE_OUT;
if (end > len) end = len;
}
@@ -427,7 +427,7 @@ mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp,
*begp = beg;
*lenp = len;
- return 1;
+ return MRB_RANGE_OK;
}
void
diff --git a/src/state.c b/src/state.c
index 08d7ba906..c42df71ba 100644
--- a/src/state.c
+++ b/src/state.c
@@ -57,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)
{
@@ -256,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);
}
diff --git a/src/string.c b/src/string.c
index 89ab59d4b..ecbe21a22 100644
--- a/src/string.c
+++ b/src/string.c
@@ -83,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;
}
@@ -93,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);
}
@@ -194,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)
{
@@ -224,8 +233,10 @@ 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;
@@ -249,14 +260,15 @@ mrb_utf8_len(const char *str, mrb_int byte_len)
static mrb_int
utf8_strlen(mrb_value str)
{
- mrb_int byte_len = RSTRING_LEN(str);
+ struct RString *s = mrb_str_ptr(str);
+ mrb_int byte_len = RSTR_LEN(s);
- if (RSTRING(str)->flags & MRB_STR_NO_UTF) {
+ if (RSTR_ASCII_P(s)) {
return byte_len;
}
else {
- mrb_int utf8_len = mrb_utf8_len(RSTRING_PTR(str), byte_len);
- if (byte_len == utf8_len) RSTRING(str)->flags |= MRB_STR_NO_UTF;
+ 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;
}
}
@@ -294,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
@@ -401,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;
@@ -416,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 (str_len < *begp || *lenp < 0) return FALSE;
+ if (*begp < 0) {
+ *begp += str_len;
+ if (*begp < 0) return FALSE;
}
- if (beg > clen) return mrb_nil_value();
- if (beg < 0) {
- beg += clen;
- if (beg < 0) return mrb_nil_value();
+ if (*lenp > str_len - *begp)
+ *lenp = str_len - *begp;
+ if (*lenp <= 0) {
+ *lenp = 0;
}
- if (len > clen - beg)
- len = clen - beg;
- if (len <= 0) {
- len = 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
@@ -500,8 +575,7 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2)
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);
@@ -636,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)
{
mrb_check_frozen(mrb, s);
- s->flags &= ~MRB_STR_NO_UTF;
if (RSTR_SHARED_P(s)) {
mrb_shared_string *shared = s->as.heap.aux.shared;
@@ -697,6 +770,13 @@ mrb_str_modify(mrb_state *mrb, struct RString *s)
}
}
+MRB_API void
+mrb_str_modify(mrb_state *mrb, struct RString *s)
+{
+ mrb_str_modify_keep_ascii(mrb, s);
+ RSTR_UNSET_ASCII_FLAG(s);
+}
+
MRB_API mrb_value
mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len)
{
@@ -723,14 +803,8 @@ 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);
}
@@ -824,7 +898,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);
@@ -836,6 +910,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);
}
@@ -973,6 +1048,8 @@ mrb_str_to_str(mrb_state *mrb, mrb_value str)
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:
@@ -1003,55 +1080,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,
- 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 */
@@ -1098,19 +1215,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();
+ }
- mrb_get_args(mrb, "ii", &n1, &n2);
- return str_substr(mrb, str, n1, n2);
+ 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);
+
+ if (newlen >= MRB_INT_MAX || newlen < replen /* overflowed */) {
+ mrb_raise(mrb, E_RUNTIME_ERROR, "string size too big");
+ }
+
+ 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);
}
- if (argc != 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc));
+}
+
+/*
+ * 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 */
@@ -1133,7 +1349,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)) {
@@ -1192,7 +1408,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();
@@ -1291,7 +1507,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
@@ -1360,7 +1576,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) {
@@ -1533,15 +1749,13 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str)
else
sub = mrb_nil_value();
}
- 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: {
@@ -1555,12 +1769,11 @@ 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);
}
@@ -1674,6 +1887,18 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
return mrb_obj_value(p_str);
}
+static inline void
+str_reverse(char *p, char *e)
+{
+ char c;
+
+ while (p < e) {
+ c = *p;
+ *p++ = *e;
+ *e-- = c;
+ }
+}
+
/* 15.2.10.5.30 */
/*
* call-seq:
@@ -1684,53 +1909,38 @@ mrb_ptr_to_str(mrb_state *mrb, void *p)
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;
}
/* ---------------------------------- */
@@ -1920,7 +2130,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;
@@ -1945,7 +2155,7 @@ 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;
@@ -1957,7 +2167,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str)
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);
}
@@ -2115,7 +2325,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));
}
}
@@ -2147,20 +2357,27 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, mrb_int base, mrb_bool badchec
MRB_API const char*
mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr)
{
- mrb_value str = mrb_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
@@ -2274,22 +2491,7 @@ bad:
MRB_API double
mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck)
{
- char *s;
- mrb_int len;
-
- mrb_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 */
@@ -2344,7 +2546,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) {
@@ -2425,7 +2627,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++ = '"';
@@ -2587,6 +2789,9 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
const char *p, *pend;
char buf[CHAR_ESC_LEN + 1];
mrb_value result = mrb_str_new_lit(mrb, "\"");
+#ifdef MRB_UTF8_STRING
+ uint32_t ascii_flag = MRB_STR_ASCII;
+#endif
p = RSTRING_PTR(str); pend = RSTRING_END(str);
for (;p < pend; p++) {
@@ -2603,6 +2808,7 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
}
mrb_str_cat(mrb, result, buf, clen);
p += clen-1;
+ ascii_flag = 0;
continue;
}
#endif
@@ -2644,6 +2850,10 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str)
}
}
mrb_str_cat_lit(mrb, result, "\"");
+#ifdef MRB_UTF8_STRING
+ mrb_str_ptr(str)->flags |= ascii_flag;
+ mrb_str_ptr(result)->flags |= ascii_flag;
+#endif
return result;
}
@@ -2689,6 +2899,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 */
diff --git a/src/symbol.c b/src/symbol.c
index b26f2b1fd..53f4f6e1d 100644
--- a/src/symbol.c
+++ b/src/symbol.c
@@ -20,6 +20,22 @@ typedef struct symbol_name {
const char *name;
} symbol_name;
+#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)
{
@@ -41,7 +57,7 @@ sym_inline_pack(const char *name, uint16_t len)
const char *p;
int i;
mrb_sym sym = 0;
- int lower = 1;
+ mrb_bool lower = TRUE;
if (len > lower_length_max) return 0; /* too long */
for (i=0; i<len; i++) {
@@ -52,9 +68,9 @@ sym_inline_pack(const char *name, uint16_t len)
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 = 0;
+ if (bits > 27) lower = FALSE;
if (i >= mix_length_max) break;
- sym |= bits<<(i*6+2);
+ sym |= bits<<(i*6+SYMBOL_INLINE_SHIFT);
}
if (lower) {
sym = 0;
@@ -64,24 +80,24 @@ sym_inline_pack(const char *name, uint16_t len)
c = name[i];
p = strchr(pack_table, (int)c);
bits = (uint32_t)(p - pack_table)+1;
- sym |= bits<<(i*5+2);
+ sym |= bits<<(i*5+SYMBOL_INLINE_SHIFT);
}
- return sym | 3;
+ return sym | SYMBOL_INLINE | SYMBOL_INLINE_LOWER;
}
if (len > mix_length_max) return 0;
- return sym | 1;
+ return sym | SYMBOL_INLINE;
}
static const char*
sym_inline_unpack(mrb_sym sym, char *buf, mrb_int *lenp)
{
- int bit_per_char = sym&2 ? 5 : 6; /* all lower case if `sym&2` is true */
+ int bit_per_char = SYMBOL_INLINE_LOWER_P(sym) ? 5 : 6;
int i;
- mrb_assert(sym&1);
+ mrb_assert(SYMBOL_INLINE_P(sym));
for (i=0; i<30/bit_per_char; i++) {
- uint32_t bits = sym>>(i*bit_per_char+2) & ((1<<bit_per_char)-1);
+ 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];;
}
@@ -113,25 +129,23 @@ find_symbol(mrb_state *mrb, const char *name, uint16_t len, uint8_t hash)
mrb_sym i;
symbol_name *sname;
-#ifndef MRB_ENABLE_ALL_SYMBOLS
/* inline symbol */
i = sym_inline_pack(name, len);
if (i > 0) return i;
-#endif
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<<1;
+ 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)<<1;
+ return (mrb_sym)(sname - mrb->symtbl)<<SYMBOL_NORMAL_SHIFT;
}
sname--;
}
@@ -186,7 +200,7 @@ sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
}
mrb->symhash[hash] = sym;
- return sym<<1;
+ return sym<<SYMBOL_NORMAL_SHIFT;
}
MRB_API mrb_sym
@@ -239,13 +253,9 @@ mrb_check_intern_str(mrb_state *mrb, mrb_value str)
static const char*
sym2name_len(mrb_state *mrb, mrb_sym sym, char *buf, mrb_int *lenp)
{
-#ifndef MRB_ENABLE_ALL_SYMBOLS
- if (sym & 1) { /* inline packed symbol */
- return sym_inline_unpack(sym, buf, lenp);
- }
-#endif
+ if (SYMBOL_INLINE_P(sym)) return sym_inline_unpack(sym, buf, lenp);
- sym >>= 1;
+ sym >>= SYMBOL_NORMAL_SHIFT;
if (sym == 0 || mrb->symidx < sym) {
if (lenp) *lenp = 0;
return NULL;
@@ -484,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) {
@@ -493,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;
}
@@ -503,8 +516,10 @@ 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 (sym&1) { /* inline symbol */
- return mrb_str_new(mrb, name, len);
+ 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);
}
@@ -520,13 +535,8 @@ mrb_sym2name(mrb_state *mrb, mrb_sym sym)
return name;
}
else {
- mrb_value str;
- if (sym&1) { /* inline symbol */
- str = mrb_str_new(mrb, name, len);
- }
- else {
- str = 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);
}
diff --git a/src/variable.c b/src/variable.c
index c4c9d369f..e6f2f397e 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -341,21 +341,24 @@ mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym)
static inline void assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
-MRB_API void
-mrb_obj_iv_set(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)
{
- iv_tbl *t;
-
- mrb_check_frozen(mrb, obj);
assign_class_name(mrb, obj, sym, v);
if (!obj->iv) {
obj->iv = iv_new(mrb);
}
- t = obj->iv;
- iv_put(mrb, t, sym, v);
+ 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)
+{
+ mrb_check_frozen(mrb, obj);
+ mrb_obj_iv_set_force(mrb, obj, sym, v);
+}
+
/* 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)
@@ -385,10 +388,10 @@ assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v)
if (mrb_nil_p(o)) {
if ((struct RClass *)obj == mrb->object_class) {
- mrb_obj_iv_set(mrb, c, id_classname, mrb_symbol_value(sym));
+ mrb_obj_iv_set_force(mrb, c, id_classname, mrb_symbol_value(sym));
}
else {
- mrb_obj_iv_set(mrb, c, id_outer, mrb_obj_value(obj));
+ mrb_obj_iv_set_force(mrb, c, id_outer, mrb_obj_value(obj));
}
}
}
@@ -522,6 +525,7 @@ mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym)
iv_tbl *t = mrb_obj_ptr(obj)->iv;
mrb_value val;
+ mrb_check_frozen(mrb, mrb_obj_ptr(obj));
if (iv_del(mrb, t, sym, &val)) {
return val;
}
@@ -962,16 +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];
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)));
- }
return ary;
}
diff --git a/src/vm.c b/src/vm.c
index 8dc6623d1..86262650e 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -830,31 +830,6 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const
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)
{
@@ -2859,6 +2834,7 @@ mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int sta
return mrb_vm_run(mrb, proc, self, stack_keep);
}
if (mrb->c->ci == mrb->c->cibase) {
+ mrb->c->ci->env = NULL;
return mrb_vm_run(mrb, proc, self, stack_keep);
}
ci = cipush(mrb);
diff --git a/test/assert.rb b/test/assert.rb
index da313bf6a..b2d5011ad 100644
--- a/test/assert.rb
+++ b/test/assert.rb
@@ -2,6 +2,7 @@ $undefined = Object.new
$ok_test = 0
$ko_test = 0
$kill_test = 0
+$warning_test = 0
$skip_test = 0
$asserts = []
$test_start = Time.now if Object.const_defined?(:Time)
@@ -19,6 +20,36 @@ unless RUBY_ENGINE == "mruby"
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
+
##
# Create the assertion in a readable way
def assertion_string(err, str, iso=nil, e=nil, bt=nil)
@@ -26,14 +57,14 @@ def assertion_string(err, str, iso=nil, e=nil, bt=nil)
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.size > 0
+ if $mrbtest_assert
$mrbtest_assert.each do |idx, assert_msg, 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
@@ -48,13 +79,39 @@ 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))
- $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
+ elsif $mrbtest_assert_idx == 0
+ $asserts.push(assertion_string('Warn: ', str, iso, 'no assertion'))
+ $warning_test += 1
+ t_print('W')
else
$ok_test += 1
t_print('.')
@@ -62,6 +119,7 @@ def assert(str = 'Assertion failed', iso = '')
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
@@ -69,7 +127,25 @@ def assert(str = 'Assertion failed', iso = '')
$kill_test += 1
t_print('X')
ensure
- $mrbtest_assert = nil
+ 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
+ parent_asserts.concat $asserts
+ end
+ $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
@@ -80,11 +156,11 @@ def assertion_diff(exp, act)
end
def assert_true(obj, msg = nil, diff = nil)
- if $mrbtest_assert
- $mrbtest_assert_idx += 1
+ 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, msg, diff])
+ $mrbtest_assert.push([$mrbtest_assert_idx.to_s, msg, diff])
end
end
obj
@@ -153,7 +229,7 @@ end
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 obj2 == $undefined
+ 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
@@ -271,7 +347,7 @@ def pass
assert_true(true)
end
-def flunk(msg = nil, diff = "Epic Fail!")
+def flunk(msg = "Epic Fail!", diff = "")
assert_true(false, msg, diff)
end
@@ -285,17 +361,18 @@ def report
t_print("#{msg}\n")
end
- $total_test = $ok_test + $ko_test + $kill_test + $skip_test
- t_print("Total: #{$total_test}\n")
+ $total_test = $ok_test + $ko_test + $kill_test + $warning_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")
+ t_print(" OK: #{$ok_test}\n")
+ t_print(" KO: #{$ko_test}\n")
+ t_print(" Crash: #{$kill_test}\n")
+ t_print("Warning: #{$warning_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")
+ t_print(" Time: #{t_time.round(2)} seconds\n")
end
$ko_test == 0 && $kill_test == 0
diff --git a/test/t/array.rb b/test/t/array.rb
index 3df99056f..eec31d751 100644
--- a/test/t/array.rb
+++ b/test/t/array.rb
@@ -346,11 +346,12 @@ end
assert('Array#to_s', '15.2.12.5.31 / 15.2.12.5.32') do
a = [2, 3, 4, 5]
+ a[4] = a
r1 = a.to_s
r2 = a.inspect
assert_equal(r2, r1)
- assert_equal("[2, 3, 4, 5]", r1)
+ assert_equal("[2, 3, 4, 5, [...]]", r1)
end
assert('Array#==', '15.2.12.5.33') do
diff --git a/test/t/enumerable.rb b/test/t/enumerable.rb
index 652c304da..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,7 +64,7 @@ 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
diff --git a/test/t/hash.rb b/test/t/hash.rb
index 156991f4a..cd47d251d 100644
--- a/test/t/hash.rb
+++ b/test/t/hash.rb
@@ -352,11 +352,13 @@ end
assert('Hash#inspect') do
h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
+ h["recur"] = h
ret = h.to_s
assert_include ret, '"c"=>300'
assert_include ret, '"a"=>100'
assert_include ret, '"d"=>400'
+ assert_include ret, '"recur"=>{...}'
end
assert('Hash#rehash') do
diff --git a/test/t/kernel.rb b/test/t/kernel.rb
index 7fc8cd2e7..c2eeee1a5 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
@@ -92,6 +88,20 @@ assert('Kernel#__id__', '15.3.1.3.3') do
assert_equal Fixnum, __id__.class
end
+assert('Kernel#__send__', '15.3.1.3.4') do
+ # test with block
+ l = __send__(:lambda) do
+ true
+ end
+
+ assert_true l.call
+ assert_equal Proc, l.class
+ # test with argument
+ assert_true __send__(:respond_to?, :nil?)
+ # test without argument and without block
+ assert_equal String, __send__(:to_s).class
+end
+
assert('Kernel#block_given?', '15.3.1.3.6') do
def bg_try(&b)
if block_given?
@@ -230,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
@@ -266,10 +279,6 @@ assert('Kernel#frozen?') do
assert_true 0.0.frozen?
end
-assert('Kernel#global_variables', '15.3.1.3.14') do
- assert_equal Array, global_variables.class
-end
-
assert('Kernel#hash', '15.3.1.3.15') do
assert_equal hash, hash
end
@@ -410,6 +419,8 @@ assert('Kernel#remove_instance_variable', '15.3.1.3.41') do
assert_equal nil, tri.var
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'
@@ -486,13 +497,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('stack extend') do
def recurse(count, stop)
return count if count > stop
diff --git a/test/t/module.rb b/test/t/module.rb
index 5c659f149..12b7f1344 100644
--- a/test/t/module.rb
+++ b/test/t/module.rb
@@ -67,6 +67,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
@@ -272,6 +273,7 @@ assert('Module#remove_const', '15.2.2.4.40') do
%i[x X!].each do |n|
assert_wrong_const_name { Test4RemoveConst.remove_const(n) }
end
+ assert_raise(FrozenError) { Test4RemoveConst.freeze.remove_const(:A) }
end
assert('Module#const_missing', '15.2.2.4.22') do
@@ -284,6 +286,18 @@ assert('Module#const_missing', '15.2.2.4.22') do
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
module Test4Include
Const4Include = 42
@@ -297,6 +311,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
@@ -395,6 +410,29 @@ end
# Not ISO specified
+assert('Module#dup') do
+ module TestModuleDup
+ @@cvar = :cvar
+ class << self
+ attr_accessor :cattr
+ def cmeth; :cmeth end
+ end
+ def cvar; @@cvar end
+ def imeth; :imeth end
+ self.cattr = :cattr
+ end
+
+ m = TestModuleDup.dup
+ o = Object.include(m).new
+ assert_equal(:cattr, m.cattr)
+ assert_equal(:cmeth, m.cmeth)
+ assert_equal(:cvar, o.cvar)
+ assert_equal(:imeth, o.imeth)
+ assert_match("#<Module:0x*>", m.to_s)
+ assert_not_predicate(m, :frozen?)
+ assert_not_predicate(TestModuleDup.freeze.dup, :frozen?)
+end
+
assert('Module#define_method') do
c = Class.new {
define_method(:m1) { :ok }
@@ -407,6 +445,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
@@ -641,6 +688,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
@@ -663,6 +714,10 @@ assert('Module#to_s') do
assert_match "#<Module:0x*>", Module.new.to_s
assert_match "#<Class:0x*>", Class.new.to_s
+
+ 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
@@ -690,8 +745,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
@@ -705,7 +760,7 @@ assert('clone Module') do
include M1.clone
end
- B.new.foo
+ assert_true(B.new.foo)
end
assert('Module#module_function') do
diff --git a/test/t/numeric.rb b/test/t/numeric.rb
index d73dfdb61..bb95f8ef3 100644
--- a/test/t/numeric.rb
+++ b/test/t/numeric.rb
@@ -1,6 +1,19 @@
##
# 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)
end
@@ -42,31 +55,60 @@ assert('Numeric#**') do
end
assert('Numeric#step') do
- assert_step = ->(exp, receiver, args) do
- inf = !args[0]
- 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_true(exp.eql?(act), "#{expr}: counters", assertion_diff(exp, act))
- assert_same(receiver, ret, "#{expr}: return value") unless inf
- end
-
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, [])
- assert_step.([10, 7, 4], 10, [nil, -3])
+ 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, [])
- assert_step.([10.0, 7.0, 4.0], 10, [nil, -3.0])
+ 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/range.rb b/test/t/range.rb
index d71fe8946..106c2866e 100644
--- a/test/t/range.rb
+++ b/test/t/range.rb
@@ -101,12 +101,12 @@ end
assert('Range#dup') do
r = (1..3).dup
- assert_equal r.begin, 1
- assert_equal r.end, 3
+ assert_equal 1, r.begin
+ assert_equal 3, r.end
assert_false r.exclude_end?
r = ("a"..."z").dup
- assert_equal r.begin, "a"
- assert_equal r.end, "z"
+ 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 e563db55a..46cbe6e2a 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,49 +37,37 @@ 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_raise(ArgumentError) { 'a' * -1 }
+ assert_raise(TypeError) { 'a' * '1' }
+ assert_raise(TypeError) { 'a' * nil }
+
+ skip unless Object.const_defined?(:Float)
+ assert_equal 'aa', 'a' * 2.1
+ assert_raise(RangeError) { '' * 1e30 }
+ assert_raise(RangeError) { '' * Float::INFINITY }
+ assert_raise(RangeError) { '' * Float::NAN }
end
assert('String#[]', '15.2.10.5.6') do
# length of args is 1
- a = 'abc'[0]
- b = 'abc'[-1]
- c = 'abc'[10]
- d = 'abc'[-10]
- e = 'abc'[1.1]
+ assert_equal 'a', 'abc'[0]
+ assert_equal 'c', 'abc'[-1]
+ assert_nil 'abc'[10]
+ assert_nil 'abc'[-10]
+ assert_equal 'b', 'abc'[1.1] if Object.const_defined?(:Float)
# length of args is 2
- a1 = 'abc'[0, -1]
- b1 = 'abc'[10, 0]
- c1 = 'abc'[-10, 0]
- d1 = 'abc'[0, 0]
- e1 = 'abc'[1, 2]
-
- # args is RegExp
- # It will be tested in mrbgems.
+ assert_nil 'abc'[0, -1]
+ assert_nil 'abc'[10, 0]
+ assert_nil 'abc'[-10, 0]
+ assert_equal '', 'abc'[0, 0]
+ assert_equal 'bc', 'abc'[1, 2]
# args is String
- a3 = 'abc'['bc']
- b3 = 'abc'['XX']
-
- assert_equal 'a', 'a'
- # assert_equal 'c', b
- # assert_nil c
- # assert_nil d
- # assert_equal 'b', e
- # assert_nil a1
- # assert_nil b1
- # assert_nil c1
- # assert_equal '', d1
- # assert_equal 'bc', e1
- # assert_equal 'bc', a3
- # assert_nil b3
-
- # assert_raise(TypeError) do
- # a[nil]
- # end
+ assert_equal 'bc', 'abc'['bc']
+ assert_nil 'abc'['XX']
+
+ assert_raise(TypeError) { 'abc'[nil] }
end
assert('String#[](UTF-8)', '15.2.10.5.6') do
@@ -206,6 +194,56 @@ assert('String#[]=') do
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
@@ -415,6 +453,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')
@@ -476,6 +525,7 @@ assert('String#reverse(UTF-8)', '15.2.10.5.29') do
assert_equal 'こんにちは世界!', a
assert_equal '!界世はちにんこ', 'こんにちは世界!'.reverse
+ assert_equal 'あ', 'あ'.reverse
end if UTF8STRING
assert('String#reverse!', '15.2.10.5.30') do
@@ -492,6 +542,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
@@ -691,10 +745,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]
diff --git a/test/t/syntax.rb b/test/t/syntax.rb
index 603547c7c..afb735e7f 100644
--- a/test/t/syntax.rb
+++ b/test/t/syntax.rb
@@ -423,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
diff --git a/travis_config.rb b/travis_config.rb
index e12bae648..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