summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--appveyor_config.rb2
-rw-r--r--doc/guides/mrbconf.md56
-rw-r--r--include/mrbconf.h83
-rw-r--r--include/mruby.h2
-rw-r--r--include/mruby/compile.h2
-rw-r--r--include/mruby/dump.h5
-rw-r--r--include/mruby/istruct.h4
-rw-r--r--include/mruby/numeric.h10
-rw-r--r--include/mruby/value.h5
-rw-r--r--include/mruby/variable.h1
-rw-r--r--lib/mruby/build.rb4
-rw-r--r--mrbgems/mruby-bin-mruby/bintest/mruby.rb56
-rw-r--r--mrbgems/mruby-bin-mruby/tools/mruby/mruby.c41
-rw-r--r--mrbgems/mruby-compiler/core/parse.y206
-rw-r--r--mrbgems/mruby-complex/mrbgem.rake10
-rw-r--r--mrbgems/mruby-complex/mrblib/complex.rb140
-rw-r--r--mrbgems/mruby-complex/src/complex.c140
-rw-r--r--mrbgems/mruby-complex/test/complex.rb128
-rw-r--r--mrbgems/mruby-eval/src/eval.c2
-rw-r--r--mrbgems/mruby-eval/test/eval.rb4
-rw-r--r--mrbgems/mruby-io/src/file.c23
-rw-r--r--mrbgems/mruby-io/src/file_test.c19
-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.rb61
-rw-r--r--mrbgems/mruby-io/test/mruby_io_test.c6
-rw-r--r--mrbgems/mruby-kernel-ext/mrblib/kernel.rb15
-rw-r--r--mrbgems/mruby-kernel-ext/src/kernel.c17
-rw-r--r--mrbgems/mruby-kernel-ext/test/kernel.rb2
-rw-r--r--mrbgems/mruby-metaprog/test/metaprog.rb24
-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-object-ext/mrblib/object.rb16
-rw-r--r--mrbgems/mruby-object-ext/src/object.c19
-rw-r--r--mrbgems/mruby-pack/src/pack.c11
-rw-r--r--mrbgems/mruby-rational/mrbgem.rake5
-rw-r--r--mrbgems/mruby-rational/mrblib/rational.rb104
-rw-r--r--mrbgems/mruby-rational/src/rational.c115
-rw-r--r--mrbgems/mruby-rational/test/rational.rb267
-rw-r--r--mrbgems/mruby-socket/src/socket.c7
-rw-r--r--mrbgems/mruby-string-ext/mrblib/string.rb10
-rw-r--r--mrbgems/mruby-string-ext/test/string.rb12
-rw-r--r--mrbgems/mruby-struct/test/struct.rb16
-rw-r--r--mrbgems/mruby-time/src/time.c46
-rw-r--r--mrbgems/mruby-time/test/time.rb126
-rw-r--r--mrblib/string.rb37
-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/class.c34
-rw-r--r--src/gc.c16
-rw-r--r--src/kernel.c2
-rw-r--r--src/load.c69
-rw-r--r--src/numeric.c174
-rw-r--r--src/object.c6
-rw-r--r--src/string.c64
-rw-r--r--src/variable.c23
-rw-r--r--test/assert.rb2
-rw-r--r--test/t/class.rb26
-rw-r--r--test/t/module.rb4
-rw-r--r--test/t/string.rb11
-rw-r--r--travis_config.rb2
68 files changed, 2655 insertions, 562 deletions
diff --git a/README.md b/README.md
index 3e71ef7ca..2acc8de1a 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.
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..20cdc1b83 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>
diff --git a/include/mruby/compile.h b/include/mruby/compile.h
index f19d9b0b3..8f8f2ebd7 100644
--- a/include/mruby/compile.h
+++ b/include/mruby/compile.h
@@ -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 */
diff --git a/include/mruby/dump.h b/include/mruby/dump.h
index 201d7ef61..65ed8af9d 100644
--- a/include/mruby/dump.h
+++ b/include/mruby/dump.h
@@ -61,7 +61,6 @@ MRB_API mrb_irep *mrb_read_irep_buf(mrb_state*, const void*, size_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"
@@ -93,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/istruct.h b/include/mruby/istruct.h
index 4d2393ccd..23c9bfa36 100644
--- a/include/mruby/istruct.h
+++ b/include/mruby/istruct.h
@@ -19,12 +19,12 @@ MRB_BEGIN_DECL
#define ISTRUCT_DATA_SIZE (sizeof(void*) * 3)
-struct RIstruct {
+struct RIStruct {
MRB_OBJECT_HEADER;
char inline_data[ISTRUCT_DATA_SIZE];
};
-#define RISTRUCT(obj) ((struct RIstruct*)(mrb_ptr(obj)))
+#define RISTRUCT(obj) ((struct RIStruct*)(mrb_ptr(obj)))
#define ISTRUCT_PTR(obj) (RISTRUCT(obj)->inline_data)
MRB_INLINE mrb_int mrb_istruct_size()
diff --git a/include/mruby/numeric.h b/include/mruby/numeric.h
index da9225aaa..34707e441 100644
--- a/include/mruby/numeric.h
+++ b/include/mruby/numeric.h
@@ -23,7 +23,7 @@ MRB_BEGIN_DECL
#define NEGFIXABLE(f) TYPED_NEGFIXABLE(f,mrb_int)
#define FIXABLE(f) TYPED_FIXABLE(f,mrb_int)
#ifndef MRB_WITHOUT_FLOAT
-#define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,double)
+#define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,mrb_float)
#endif
#ifndef MRB_WITHOUT_FLOAT
@@ -34,12 +34,12 @@ MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base);
#ifndef MRB_WITHOUT_FLOAT
MRB_API mrb_value mrb_float_to_str(mrb_state *mrb, mrb_value x, const char *fmt);
MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value x);
+MRB_API mrb_value mrb_int_value(mrb_state *mrb, mrb_float f);
#endif
-mrb_value mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y);
-mrb_value mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y);
-mrb_value mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y);
-mrb_value mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y);
+MRB_API mrb_value mrb_num_plus(mrb_state *mrb, mrb_value x, mrb_value y);
+MRB_API mrb_value mrb_num_minus(mrb_state *mrb, mrb_value x, mrb_value y);
+MRB_API mrb_value mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y);
#ifndef __has_builtin
#define __has_builtin(x) 0
diff --git a/include/mruby/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..887a5518e 100644
--- a/lib/mruby/build.rb
+++ b/lib/mruby/build.rb
@@ -255,7 +255,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 +263,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/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
index e8d1510f7..09350ff49 100644
--- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb
+++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
@@ -1,10 +1,16 @@
require 'tempfile'
+require 'open3'
+
+def assert_mruby(exp_out, exp_err, exp_success, args)
+ out, err, stat = Open3.capture3(cmd("mruby"), *args)
+ assert_operator(exp_out, :===, out, "standard output")
+ assert_operator(exp_err, :===, err, "standard error")
+ assert_equal(exp_success, stat.success?, "exit success?")
+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 +72,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 +84,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 +114,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-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y
index 7838b6dfb..37d4d1bf1 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)
{
@@ -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)
@@ -5095,6 +5183,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 +5217,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 +5244,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 +5271,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 +5307,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 +5327,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;
}
}
@@ -5300,7 +5404,7 @@ parser_yylex(parser_state *p)
if (is_float) {
#ifdef MRB_WITHOUT_FLOAT
yywarning(p, "floating point numbers are not supported");
- pylval.nd = new_int(p, "0", 10);
+ pylval.nd = new_int(p, "0", 10, 0);
return tINTEGER;
#else
double d;
@@ -5315,11 +5419,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..4c0c19c70
--- /dev/null
+++ b/mrbgems/mruby-complex/mrblib/complex.rb
@@ -0,0 +1,140 @@
+class Complex < Numeric
+ def initialize(real, imaginary)
+ real = real.to_f unless real.is_a? Numeric
+ imaginary = imaginary.to_f unless imaginary.is_a? Numeric
+ @real = real
+ @imaginary = imaginary
+ end
+
+ def self.polar(abs, arg = 0)
+ Complex(abs * Math.cos(arg), abs * Math.sin(arg))
+ end
+
+ def self.rectangular(real, imaginary = 0)
+ _new(real, imaginary)
+ end
+
+ def inspect
+ "(#{to_s})"
+ end
+
+ def to_s
+ "#{real}#{'+' unless imaginary.negative?}#{imaginary}i"
+ end
+
+ def +@
+ Complex._new(real, imaginary)
+ end
+
+ def -@
+ Complex._new(-real, -imaginary)
+ end
+
+ def +(rhs)
+ if rhs.is_a? Complex
+ Complex._new(real + rhs.real, imaginary + rhs.imaginary)
+ elsif rhs.is_a? Numeric
+ Complex._new(real + rhs, imaginary)
+ end
+ end
+
+ def -(rhs)
+ if rhs.is_a? Complex
+ Complex._new(real - rhs.real, imaginary - rhs.imaginary)
+ elsif rhs.is_a? Numeric
+ Complex._new(real - rhs, imaginary)
+ end
+ end
+
+ def *(rhs)
+ if rhs.is_a? Complex
+ Complex._new(real * rhs.real - imaginary * rhs.imaginary, real * rhs.imaginary + rhs.real * imaginary)
+ elsif rhs.is_a? Numeric
+ Complex._new(real * rhs, imaginary * rhs)
+ end
+ end
+
+ def /(rhs)
+ if rhs.is_a? Complex
+ div = rhs.real * rhs.real + rhs.imaginary * rhs.imaginary
+ Complex._new((real * rhs.real + imaginary * rhs.imaginary) / div, (rhs.real * imaginary - real * rhs.imaginary) / div)
+ elsif rhs.is_a? Numeric
+ Complex._new(real / rhs, imaginary / rhs)
+ end
+ end
+ alias_method :quo, :/
+
+ def ==(rhs)
+ if rhs.is_a? Complex
+ real == rhs.real && imaginary == rhs.imaginary
+ elsif rhs.is_a? Numeric
+ imaginary.zero? && real == rhs
+ end
+ end
+
+ def abs
+ Math.sqrt(abs2)
+ end
+ alias_method :magnitude, :abs
+
+ def abs2
+ real * real + imaginary * imaginary
+ end
+
+ def arg
+ Math.atan2 imaginary, real
+ end
+ alias_method :angle, :arg
+ alias_method :phase, :arg
+
+ def conjugate
+ Complex(real, -imaginary)
+ end
+ alias_method :conj, :conjugate
+
+ def fdiv(numeric)
+ Complex(real.to_f / numeric, imaginary.to_f / numeric)
+ end
+
+ def polar
+ [abs, arg]
+ end
+
+ def real?
+ false
+ end
+
+ def rectangular
+ [real, imaginary]
+ end
+ alias_method :rect, :rectangular
+
+ def to_r
+ raise RangeError.new "can't convert #{to_s} into Rational" unless imaginary.zero?
+ Rational(real, 1)
+ end
+
+ alias_method :imag, :imaginary
+end
+
+module Kernel
+ def Complex(real, imaginary = 0)
+ Complex.rectangular(real, imaginary)
+ end
+end
+
+[Fixnum, Float].each do |cls|
+ [:+, :-, :*, :/, :==].each do |op|
+ cls.instance_exec do
+ original_operator_name = "__original_operator_#{op}_complex"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Complex
+ Complex(self).send(op, rhs)
+ else
+ send(original_operator_name, rhs)
+ end
+ end
+ end
+ end
+end
diff --git a/mrbgems/mruby-complex/src/complex.c b/mrbgems/mruby-complex/src/complex.c
new file mode 100644
index 000000000..c6fb7a829
--- /dev/null
+++ b/mrbgems/mruby-complex/src/complex.c
@@ -0,0 +1,140 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/numeric.h>
+
+struct mrb_complex {
+ mrb_float real;
+ mrb_float imaginary;
+};
+
+#if defined(MRB_64BIT) || defined(MRB_USE_FLOAT)
+
+#define COMPLEX_USE_ISTRUCT
+/* use TT_ISTRUCT */
+#include <mruby/istruct.h>
+
+#define complex_ptr(mrb, v) (struct mrb_complex*)mrb_istruct_ptr(v)
+
+static mrb_value
+complex_new(mrb_state *mrb, mrb_float real, mrb_float imaginary)
+{
+ struct RClass *c = mrb_class_get(mrb, "Complex");
+ struct RIStruct *s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
+ mrb_value comp = mrb_obj_value(s);
+ struct mrb_complex *p = complex_ptr(mrb, comp);
+ p->real = real;
+ p->imaginary = imaginary;
+
+ return comp;
+}
+
+#else
+/* use TT_DATA */
+#include <mruby/data.h>
+
+static const struct mrb_data_type mrb_complex_type = {"Complex", mrb_free};
+
+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;
+
+ p = (struct mrb_complex*)mrb_malloc(mrb, sizeof(struct mrb_complex));
+ p->real = real;
+ p->imaginary = imaginary;
+
+ return mrb_obj_value(Data_Wrap_Struct(mrb, c, &mrb_complex_type, p));
+}
+
+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_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_new(mrb_state *mrb, mrb_value self)
+{
+ mrb_float real, imaginary;
+
+ mrb_get_args(mrb, "ff", &real, &imaginary);
+ return complex_new(mrb, real, imaginary);
+}
+
+#ifndef MRB_WITHOUT_FLOAT
+static mrb_value
+complex_to_f(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+
+ if (p->imaginary != 0) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %S into Float", self);
+ }
+
+ return mrb_float_value(mrb, p->real);
+}
+#endif
+
+static mrb_value
+complex_to_i(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_complex *p = complex_ptr(mrb, self);
+
+ if (p->imaginary != 0) {
+ mrb_raisef(mrb, E_RANGE_ERROR, "can't convert %S into Float", self);
+ }
+ return mrb_int_value(mrb, p->real);
+}
+
+static mrb_value
+complex_to_c(mrb_state *mrb, mrb_value self)
+{
+ return self;
+}
+
+void mrb_mruby_complex_gem_init(mrb_state *mrb)
+{
+ struct RClass *comp;
+
+#ifdef COMPLEX_USE_ISTRUCT
+ mrb_assert(sizeof(struct mrb_complex) < ISTRUCT_DATA_SIZE);
+#endif
+ comp = mrb_define_class(mrb, "Complex", mrb_class_get(mrb, "Numeric"));
+ //MRB_SET_INSTANCE_TT(comp, MRB_TT_ISTRUCT);
+ mrb_undef_class_method(mrb, comp, "new");
+ mrb_define_class_method(mrb, comp, "_new", complex_s_new, MRB_ARGS_REQ(2));
+ mrb_define_method(mrb, comp, "real", complex_real, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "imaginary", complex_imaginary, MRB_ARGS_NONE());
+#ifndef MRB_WITHOUT_FLOAT
+ mrb_define_method(mrb, comp, "to_f", complex_to_f, MRB_ARGS_NONE());
+#endif
+ mrb_define_method(mrb, comp, "to_i", complex_to_i, MRB_ARGS_NONE());
+ mrb_define_method(mrb, comp, "to_c", complex_to_c, MRB_ARGS_NONE());
+}
+
+void
+mrb_mruby_complex_gem_final(mrb_state* mrb)
+{
+}
diff --git a/mrbgems/mruby-complex/test/complex.rb b/mrbgems/mruby-complex/test/complex.rb
new file mode 100644
index 000000000..e7fcc7322
--- /dev/null
+++ b/mrbgems/mruby-complex/test/complex.rb
@@ -0,0 +1,128 @@
+def assert_complex(real, exp)
+ assert_float real.real, exp.real
+ assert_float real.imaginary, exp.imaginary
+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
diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c
index fa687d624..a3b211ba2 100644
--- a/mrbgems/mruby-eval/src/eval.c
+++ b/mrbgems/mruby-eval/src/eval.c
@@ -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-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..5d5ecb93f 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,14 +42,7 @@ 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);
@@ -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/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..5004d0042 100644
--- a/mrbgems/mruby-io/test/io.rb
+++ b/mrbgems/mruby-io/test/io.rb
@@ -1,35 +1,44 @@
##
# 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", ""]
+
+assert_io_open = ->(meth) 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
-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 +93,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 +136,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 +224,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 +609,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/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..9288b0e6f 100644
--- a/mrbgems/mruby-kernel-ext/src/kernel.c
+++ b/mrbgems/mruby-kernel-ext/src/kernel.c
@@ -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-metaprog/test/metaprog.rb b/mrbgems/mruby-metaprog/test/metaprog.rb
index 1262c9945..3aa1d8732 100644
--- a/mrbgems/mruby-metaprog/test/metaprog.rb
+++ b/mrbgems/mruby-metaprog/test/metaprog.rb
@@ -122,6 +122,22 @@ assert('Kernel#define_singleton_method') do
assert_equal :singleton_method_ok, o.test_method
end
+assert('Kernel#singleton_class') do
+ o1 = Object.new
+ assert_same(o1.singleton_class, class << o1; self end)
+
+ o2 = Object.new
+ sc2 = class << o2; self end
+ assert_same(o2.singleton_class, sc2)
+
+ o3 = Object.new
+ sc3 = o3.singleton_class
+ o3.freeze
+ assert_predicate(sc3, :frozen?)
+
+ assert_predicate(Object.new.freeze.singleton_class, :frozen?)
+end
+
def labeled_module(name, &block)
Module.new do
(class <<self; self end).class_eval do
@@ -171,7 +187,6 @@ assert('Module#class_variable_set', '15.2.2.4.18') do
@@foo
end
end
-
assert_equal 99, Test4ClassVariableSet.class_variable_set(:@@cv, 99)
assert_equal 101, Test4ClassVariableSet.class_variable_set(:@@foo, 101)
assert_true Test4ClassVariableSet.class_variables.include? :@@cv
@@ -180,6 +195,13 @@ assert('Module#class_variable_set', '15.2.2.4.18') do
%w[@@ @@1 @@x= @x @ x 1].each do |n|
assert_raise(NameError) { Test4ClassVariableSet.class_variable_set(n, 1) }
end
+
+ m = Module.new.freeze
+ assert_raise(FrozenError) { m.class_variable_set(:@@cv, 1) }
+
+ parent = Class.new{ class_variable_set(:@@a, nil) }.freeze
+ child = Class.new(parent)
+ assert_raise(FrozenError) { child.class_variable_set(:@@a, 1) }
end
assert('Module#class_variables', '15.2.2.4.19') do
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-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..d3e7a9c50 100644
--- a/mrbgems/mruby-object-ext/src/object.c
+++ b/mrbgems/mruby-object-ext/src/object.c
@@ -46,6 +46,22 @@ nil_to_i(mrb_state *mrb, mrb_value obj)
/*
* call-seq:
+ * obj.itself -> an_object
+ *
+ * Returns <i>obj</i>.
+ *
+ * string = 'my string' #=> "my string"
+ * string.itself.object_id == string.object_id #=> true
+ *
+ */
+static mrb_value
+mrb_f_itself(mrb_state *mrb, mrb_value self)
+{
+ return self;
+}
+
+/*
+ * call-seq:
* obj.instance_exec(arg...) {|var...| block } -> obj
*
* Executes the given block within the context of the receiver
@@ -102,8 +118,9 @@ mrb_mruby_object_ext_gem_init(mrb_state* mrb)
mrb_define_method(mrb, n, "to_f", nil_to_f, MRB_ARGS_NONE());
#endif
mrb_define_method(mrb, n, "to_i", nil_to_i, MRB_ARGS_NONE());
+ mrb_define_module_function(mrb, mrb->kernel_module, "itself", mrb_f_itself, 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_class_get(mrb, "BasicObject"), "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK());
}
void
diff --git a/mrbgems/mruby-pack/src/pack.c b/mrbgems/mruby-pack/src/pack.c
index ac29fdbf3..75a447d6c 100644
--- a/mrbgems/mruby-pack/src/pack.c
+++ b/mrbgems/mruby-pack/src/pack.c
@@ -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
@@ -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 == '*') {
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..c8614ecea
--- /dev/null
+++ b/mrbgems/mruby-rational/mrblib/rational.rb
@@ -0,0 +1,104 @@
+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
+end
+
+class Numeric
+ def to_r
+ Rational(self, 1)
+ end
+end
+
+module Kernel
+ def Rational(numerator = 0, denominator = 1)
+ a = numerator
+ b = denominator
+ a, b = b, a % b until b == 0
+ Rational._new(numerator.div(a), denominator.div(a))
+ end
+end
+
+[:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].each do |op|
+ Fixnum.instance_eval do
+ original_operator_name = "__original_operator_#{op}_rational"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Rational
+ Rational(self).__send__(op, rhs)
+ else
+ __send__(original_operator_name, rhs)
+ end
+ end
+ end
+ Float.instance_eval do
+ original_operator_name = "__original_operator_#{op}_rational"
+ alias_method original_operator_name, op
+ define_method op do |rhs|
+ if rhs.is_a? Rational
+ rhs = rhs.to_f
+ end
+ __send__(original_operator_name, rhs)
+ end
+ end if Object.const_defined?(:Float)
+end
diff --git a/mrbgems/mruby-rational/src/rational.c b/mrbgems/mruby-rational/src/rational.c
new file mode 100644
index 000000000..2a3f6df09
--- /dev/null
+++ b/mrbgems/mruby-rational/src/rational.c
@@ -0,0 +1,115 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/istruct.h>
+
+struct mrb_rational {
+ mrb_int numerator;
+ mrb_int denominator;
+};
+
+static struct mrb_rational*
+rational_ptr(mrb_value v)
+{
+ return (struct mrb_rational*)mrb_istruct_ptr(v);
+}
+
+static mrb_value
+rational_numerator(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(self);
+ return mrb_fixnum_value(p->numerator);
+}
+
+static mrb_value
+rational_denominator(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_rational *p = rational_ptr(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 RIStruct *s = (struct RIStruct*)mrb_obj_alloc(mrb, MRB_TT_ISTRUCT, c);
+ mrb_value rat = mrb_obj_value(s);
+ struct mrb_rational *p = rational_ptr(rat);
+ p->numerator = numerator;
+ p->denominator = denominator;
+ return rat;
+}
+
+static mrb_value
+rational_s_new(mrb_state *mrb, mrb_value self)
+{
+ mrb_int numerator, denominator;
+
+ mrb_get_args(mrb, "ii", &numerator, &denominator);
+ 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(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(self);
+ 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(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;
+
+ mrb_assert(sizeof(struct mrb_rational) < ISTRUCT_DATA_SIZE);
+ rat = mrb_define_class(mrb, "Rational", mrb_class_get(mrb, "Numeric"));
+ MRB_SET_INSTANCE_TT(rat, MRB_TT_ISTRUCT);
+ 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..ea55880fe
--- /dev/null
+++ b/mrbgems/mruby-rational/test/rational.rb
@@ -0,0 +1,267 @@
+def assert_rational(exp, real)
+ assert_float exp.numerator, real.numerator
+ assert_float exp.denominator, real.denominator
+end
+
+def assert_equal_rational(exp, o1, o2)
+ if exp
+ assert_operator(o1, :==, o2)
+ assert_not_operator(o1, :!=, o2)
+ else
+ assert_not_operator(o1, :==, o2)
+ assert_operator(o1, :!=, o2)
+ 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 '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), '')
+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
+ num = Class.new(Numeric) do
+ def initialize(n)
+ @n = n
+ end
+
+ def <=>(rhs)
+ rhs = rhs.to_i
+ rhs < 0 ? nil : @n <=> rhs
+ end
+
+ def inspect
+ "num(#{@n})"
+ end
+ end
+
+ 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, num.new(2))
+ assert_cmp(0, 3r, num.new(3))
+ assert_cmp(-1, 3r, num.new(4))
+ assert_cmp(nil, Rational(-3), num.new(5))
+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
diff --git a/mrbgems/mruby-socket/src/socket.c b/mrbgems/mruby-socket/src/socket.c
index dff176778..8515a6057 100644
--- a/mrbgems/mruby-socket/src/socket.c
+++ b/mrbgems/mruby-socket/src/socket.c
@@ -38,6 +38,7 @@
#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"
@@ -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..fdaf2f960 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
diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb
index 44ca1fde2..02777e594 100644
--- a/mrbgems/mruby-string-ext/test/string.rb
+++ b/mrbgems/mruby-string-ext/test/string.rb
@@ -657,19 +657,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/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb
index c298fef9f..91e8cecc6 100644
--- a/mrbgems/mruby-struct/test/struct.rb
+++ b/mrbgems/mruby-struct/test/struct.rb
@@ -152,14 +152,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
diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c
index 7f6c3004d..34376c286 100644
--- a/mrbgems/mruby-time/src/time.c
+++ b/mrbgems/mruby-time/src/time.c
@@ -18,6 +18,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) {
@@ -204,17 +205,18 @@ 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_float sec = (mrb_float)t;
- mrb_free(mrb, self);
+ if (dealloc) mrb_free(mrb, self);
mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, sec));
/* not reached */
return NULL;
@@ -291,24 +293,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 +319,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);
@@ -576,10 +581,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 +765,6 @@ mrb_time_sec(mrb_state *mrb, mrb_value self)
return mrb_fixnum_value(tm->datetime.tm_sec);
}
-
/* 15.2.19.7.24 */
/* Returns a Float with the time since the epoch in seconds. */
static mrb_value
@@ -825,6 +828,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 +857,8 @@ mrb_mruby_time_gem_init(mrb_state* mrb)
mrb_define_method(mrb, tc, "<=>" , mrb_time_cmp , MRB_ARGS_REQ(1)); /* 15.2.19.7.1 */
mrb_define_method(mrb, tc, "+" , mrb_time_plus , MRB_ARGS_REQ(1)); /* 15.2.19.7.2 */
mrb_define_method(mrb, tc, "-" , mrb_time_minus , MRB_ARGS_REQ(1)); /* 15.2.19.7.3 */
- mrb_define_method(mrb, tc, "to_s" , mrb_time_asctime, MRB_ARGS_NONE());
- mrb_define_method(mrb, tc, "inspect", mrb_time_asctime, MRB_ARGS_NONE());
+ mrb_define_method(mrb, tc, "to_s" , mrb_time_to_s , MRB_ARGS_NONE());
+ mrb_define_method(mrb, tc, "inspect", mrb_time_to_s , MRB_ARGS_NONE());
mrb_define_method(mrb, tc, "asctime", mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.4 */
mrb_define_method(mrb, tc, "ctime" , mrb_time_asctime, MRB_ARGS_NONE()); /* 15.2.19.7.5 */
mrb_define_method(mrb, tc, "day" , mrb_time_day , MRB_ARGS_NONE()); /* 15.2.19.7.6 */
diff --git a/mrbgems/mruby-time/test/time.rb b/mrbgems/mruby-time/test/time.rb
index 54c446ca3..ce7b39928 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(t2.utc.asctime, "Sun Mar 13 07:07:40 2011")
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(t2.utc.asctime, "Sun Mar 13 07:05:40 2011")
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/string.rb b/mrblib/string.rb
index c92a9e7be..b0fe4033e 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
@@ -242,26 +231,16 @@ class String
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/class.c b/src/class.c
index d6efdbdc4..6ceaa0cfa 100644
--- a/src/class.c
+++ b/src/class.c
@@ -66,15 +66,15 @@ 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);
}
static void
@@ -120,6 +120,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o)
mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc);
mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)o);
mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern_lit(mrb, "__attached__"), mrb_obj_value(o));
+ sc->flags |= o->flags & MRB_FL_OBJ_IS_FROZEN;
}
static mrb_value
@@ -837,29 +838,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++;
}
@@ -2175,6 +2154,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));
@@ -2212,7 +2192,7 @@ 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_undef_method(mrb, cls, "append_features");
mrb_undef_method(mrb, cls, "extend_object");
diff --git a/src/gc.c b/src/gc.c
index 27f3716ec..6dc8e373b 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;
@@ -466,9 +466,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 +483,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());
diff --git a/src/kernel.c b/src/kernel.c
index d9a1d36ce..349e71cb8 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -438,6 +438,7 @@ mrb_obj_freeze(mrb_state *mrb, mrb_value self)
struct RBasic *b = mrb_basic_ptr(self);
if (!MRB_FROZEN_P(b)) {
MRB_SET_FROZEN_FLAG(b);
+ if (b->c->tt == MRB_TT_SCLASS) MRB_SET_FROZEN_FLAG(b->c);
}
}
return self;
@@ -779,7 +780,6 @@ 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());
diff --git a/src/load.c b/src/load.c
index 0274f30d4..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)
{
@@ -590,13 +530,6 @@ read_irep(mrb_state *mrb, const uint8_t *bin, size_t bufsize, 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);
@@ -620,7 +553,7 @@ read_irep(mrb_state *mrb, const uint8_t *bin, size_t bufsize, 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;
diff --git a/src/numeric.c b/src/numeric.c
index 8205e41f6..608b4f289 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,7 +170,7 @@ 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;
@@ -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 */
@@ -722,7 +732,7 @@ flo_round(mrb_state *mrb, mrb_value num)
if (!isfinite(number)) return num;
return mrb_float_value(mrb, number);
}
- return mrb_fixnum_value((mrb_int)number);
+ return mrb_int_value(mrb, number);
}
/* 15.2.9.3.14 */
@@ -744,10 +754,7 @@ flo_truncate(mrb_state *mrb, mrb_value num)
if (f < 0.0) f = ceil(f);
mrb_check_num_exact(mrb, f);
- if (!FIXABLE_FLOAT(f)) {
- return mrb_float_value(mrb, f);
- }
- return mrb_fixnum_value((mrb_int)f);
+ return mrb_int_value(mrb, f);
}
static mrb_value
@@ -780,8 +787,8 @@ int_to_i(mrb_state *mrb, mrb_value num)
return num;
}
-mrb_value
-mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
@@ -805,6 +812,21 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y)
#endif
}
+MRB_API mrb_value
+mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_fixnum_p(x)) {
+ return fixnum_mul(mrb, x, y);
+ }
+#ifndef MRB_WITHOUT_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y));
+ }
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number multiply");
+ return mrb_nil_value(); /* not reached */
+}
+
/* 15.2.8.3.3 */
/*
* call-seq:
@@ -821,7 +843,7 @@ fix_mul(mrb_state *mrb, mrb_value x)
mrb_value y;
mrb_get_args(mrb, "o", &y);
- return mrb_fixnum_mul(mrb, x, y);
+ return fixnum_mul(mrb, x, y);
}
static void
@@ -933,7 +955,7 @@ fix_divmod(mrb_state *mrb, mrb_value x)
mrb_value a, b;
flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod);
- a = mrb_float_value(mrb, div);
+ a = mrb_int_value(mrb, div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
@@ -951,7 +973,7 @@ flo_divmod(mrb_state *mrb, mrb_value x)
mrb_get_args(mrb, "o", &y);
flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod);
- a = mrb_float_value(mrb, div);
+ a = mrb_int_value(mrb, div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
@@ -1230,15 +1252,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 +1284,21 @@ mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y)
#endif
}
+MRB_API mrb_value
+mrb_num_plus(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_fixnum_p(x)) {
+ return fixnum_plus(mrb, x, y);
+ }
+#ifndef MRB_WITHOUT_FLOAT
+ if (mrb_float_p(x)) {
+ return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y));
+ }
+#endif
+ mrb_raise(mrb, E_TYPE_ERROR, "no number addition");
+ return mrb_nil_value(); /* not reached */
+}
+
/* 15.2.8.3.1 */
/*
* call-seq:
@@ -1277,11 +1314,11 @@ fix_plus(mrb_state *mrb, mrb_value self)
mrb_value other;
mrb_get_args(mrb, "o", &other);
- return mrb_fixnum_plus(mrb, self, other);
+ return fixnum_plus(mrb, self, other);
}
-mrb_value
-mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
+static mrb_value
+fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
@@ -1304,6 +1341,21 @@ mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y)
#endif
}
+MRB_API mrb_value
+mrb_num_minus(mrb_state *mrb, mrb_value x, mrb_value y)
+{
+ if (mrb_fixnum_p(x)) {
+ return fixnum_minus(mrb, x, y);
+ }
+#ifdef 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 +1372,7 @@ fix_minus(mrb_state *mrb, mrb_value self)
mrb_value other;
mrb_get_args(mrb, "o", &other);
- return mrb_fixnum_minus(mrb, self, other);
+ return fixnum_minus(mrb, self, other);
}
@@ -1430,7 +1482,7 @@ cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2)
* basis for the tests in <code>Comparable</code>.
*/
static mrb_value
-num_cmp(mrb_state *mrb, mrb_value self)
+integral_cmp(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_int n;
@@ -1450,7 +1502,7 @@ cmperr(mrb_state *mrb, mrb_value v1, mrb_value v2)
}
static mrb_value
-num_lt(mrb_state *mrb, mrb_value self)
+integral_lt(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_int n;
@@ -1463,7 +1515,7 @@ num_lt(mrb_state *mrb, mrb_value self)
}
static mrb_value
-num_le(mrb_state *mrb, mrb_value self)
+integral_le(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_int n;
@@ -1476,7 +1528,7 @@ num_le(mrb_state *mrb, mrb_value self)
}
static mrb_value
-num_gt(mrb_state *mrb, mrb_value self)
+integral_gt(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_int n;
@@ -1489,7 +1541,7 @@ num_gt(mrb_state *mrb, mrb_value self)
}
static mrb_value
-num_ge(mrb_state *mrb, mrb_value self)
+integral_ge(mrb_state *mrb, mrb_value self)
{
mrb_value other;
mrb_int n;
@@ -1544,21 +1596,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/string.c b/src/string.c
index 89ab59d4b..578e3bdcc 100644
--- a/src/string.c
+++ b/src/string.c
@@ -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)
{
@@ -723,14 +732,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);
}
@@ -1107,9 +1110,6 @@ mrb_str_aref_m(mrb_state *mrb, mrb_value str)
mrb_get_args(mrb, "ii", &n1, &n2);
return str_substr(mrb, str, n1, n2);
}
- if (argc != 1) {
- mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc));
- }
return mrb_str_aref(mrb, str, a1);
}
@@ -2115,7 +2115,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 +2147,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 +2281,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 */
diff --git a/src/variable.c b/src/variable.c
index 983fe52f7..ee21a3b96 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));
}
}
}
@@ -671,6 +674,7 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
iv_tbl *t = c->iv;
if (iv_get(mrb, t, sym, NULL)) {
+ mrb_check_frozen(mrb, c);
iv_put(mrb, t, sym, v);
mrb_write_barrier(mrb, (struct RBasic*)c);
return;
@@ -698,6 +702,7 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
c = cls;
}
+ mrb_check_frozen(mrb, c);
if (!c->iv) {
c->iv = iv_new(mrb);
}
diff --git a/test/assert.rb b/test/assert.rb
index 121bd0a8e..a9bbc9a05 100644
--- a/test/assert.rb
+++ b/test/assert.rb
@@ -247,7 +247,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
diff --git a/test/t/class.rb b/test/t/class.rb
index 290ecf74a..e2839111c 100644
--- a/test/t/class.rb
+++ b/test/t/class.rb
@@ -356,6 +356,13 @@ assert('singleton tests') do
end
end
end if Object.const_defined?(:Float)
+
+ o = Object.new
+ sc = class << o; self end
+ o.freeze
+ assert_predicate(sc, :frozen?)
+
+ assert_predicate(class << Object.new.freeze; self end, :frozen?)
end
assert('clone Class') do
@@ -433,6 +440,25 @@ assert('overriding class variable with a module (#3235)') do
end
end
+assert('class variable for frozen class/module') do
+ module CVarForFrozenModule
+ freeze
+ assert_raise(FrozenError) { @@cv = 1 }
+ end
+
+ class CVarForFrozenClassA
+ @@a = nil
+ freeze
+ end
+ class CVarForFrozenClassB < CVarForFrozenClassA
+ def a=(v)
+ @@a = v
+ end
+ end
+ b = CVarForFrozenClassB.new
+ assert_raise(FrozenError) { b.a = 1 }
+end
+
assert('class with non-class/module outer raises TypeError') do
assert_raise(TypeError) { class 0::C1; end }
assert_raise(TypeError) { class []::C2; end }
diff --git a/test/t/module.rb b/test/t/module.rb
index 09613e1bc..f4999019a 100644
--- a/test/t/module.rb
+++ b/test/t/module.rb
@@ -653,6 +653,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
diff --git a/test/t/string.rb b/test/t/string.rb
index e563db55a..e5b001366 100644
--- a/test/t/string.rb
+++ b/test/t/string.rb
@@ -37,11 +37,14 @@ end
assert('String#*', '15.2.10.5.5') do
assert_equal 'aaaaa', 'a' * 5
assert_equal '', 'a' * 0
- assert_raise(ArgumentError) do
- 'a' * -1
- end
+ assert_equal 'aa', 'a' * 2.1
+ assert_raise(ArgumentError) { 'a' * -1 }
+ assert_raise(RangeError) { '' * 1e30 }
+ assert_raise(RangeError) { '' * Float::INFINITY }
+ assert_raise(RangeError) { '' * Float::NAN }
+ assert_raise(TypeError) { 'a' * '1' }
+ assert_raise(TypeError) { 'a' * nil }
end
-
assert('String#[]', '15.2.10.5.6') do
# length of args is 1
a = 'abc'[0]
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