diff options
43 files changed, 601 insertions, 399 deletions
@@ -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/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..6b0b9e37e 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 */ @@ -88,6 +93,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 diff --git a/include/mruby.h b/include/mruby.h index de6a803ef..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> @@ -196,13 +196,9 @@ struct mrb_jmpbuf; typedef void (*mrb_atexit_func)(struct mrb_state*); -#define MRB_STATE_NO_REGEXP 1 -#define MRB_STATE_REGEXP 2 - typedef struct mrb_state { struct mrb_jmpbuf *jmp; - uint32_t flags; mrb_allocf allocf; /* memory allocation function */ void *allocf_ud; /* auxiliary data of allocf */ diff --git a/include/mruby/common.h b/include/mruby/common.h index 4eaac9af7..dc9e3acc5 100644 --- a/include/mruby/common.h +++ b/include/mruby/common.h @@ -54,12 +54,12 @@ MRB_BEGIN_DECL #endif /** Declare a function as always inlined. */ -#if defined(_MSC_VER) -# define MRB_INLINE static __inline -#else -# define MRB_INLINE static inline +#if defined _MSC_VER && _MSC_VER < 1900 +# ifndef __cplusplus +# define inline __inline +# endif #endif - +#define MRB_INLINE static inline /** Declare a public MRuby API function. */ #if defined(MRB_BUILD_AS_DLL) diff --git a/include/mruby/string.h b/include/mruby/string.h index 0b90debec..22445f654 100644 --- a/include/mruby/string.h +++ b/include/mruby/string.h @@ -433,9 +433,6 @@ mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str); */ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str); -void mrb_noregexp(mrb_state *mrb, mrb_value self); -void mrb_regexp_check(mrb_state *mrb, mrb_value obj); - /* For backward compatibility */ #define mrb_str_cat2(mrb, str, ptr) mrb_str_cat_cstr(mrb, str, ptr) #define mrb_str_buf_cat(mrb, str, ptr, len) mrb_str_cat(mrb, str, ptr, len) diff --git a/include/mruby/value.h b/include/mruby/value.h index 0446e772c..be3dd397f 100644 --- a/include/mruby/value.h +++ b/include/mruby/value.h @@ -73,9 +73,6 @@ MRB_API double mrb_float_read(const char*, char**); #endif #if defined _MSC_VER && _MSC_VER < 1900 -# ifndef __cplusplus -# define inline __inline -# endif # include <stdarg.h> MRB_API int mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg); MRB_API int mrb_msvc_snprintf(char *s, size_t n, const char *format, ...); @@ -180,7 +177,6 @@ typedef void mrb_value; #define mrb_cptr_p(o) (mrb_type(o) == MRB_TT_CPTR) #define mrb_exception_p(o) (mrb_type(o) == MRB_TT_EXCEPTION) #define mrb_test(o) mrb_bool(o) -MRB_API mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value); /* * Returns a float in Ruby. @@ -282,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/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..b7d47118f 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -913,40 +913,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; + } } } 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..ec731b094 100644 --- a/mrbgems/mruby-io/src/file_test.c +++ b/mrbgems/mruby-io/src/file_test.c @@ -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/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-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..b5a540d09 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) { @@ -576,10 +577,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 +761,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 +824,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 +853,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..132affcae --- /dev/null +++ b/oss-fuzz/config/mruby_fuzzer.options @@ -0,0 +1,5 @@ +[libfuzzer] +close_fd_mask = 3 +dict = mruby.dict +fork = 8 +only_ascii = 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/src/class.c b/src/class.c index 77ff04437..9d26851b9 100644 --- a/src/class.c +++ b/src/class.c @@ -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++; } @@ -194,22 +194,6 @@ mrb_word_boxing_cptr_value(mrb_state *mrb, void *p) } #endif /* MRB_WORD_BOXING */ -MRB_API mrb_bool -mrb_regexp_p(mrb_state *mrb, mrb_value v) -{ - if (mrb->flags & MRB_STATE_NO_REGEXP) { - return FALSE; - } - if ((mrb->flags & MRB_STATE_REGEXP) || mrb_class_defined(mrb, REGEXP_CLASS)) { - mrb->flags |= MRB_STATE_REGEXP; - return mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS)); - } - else { - mrb->flags |= MRB_STATE_NO_REGEXP; - } - return FALSE; -} - #if defined _MSC_VER && _MSC_VER < 1900 #ifndef va_copy @@ -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..45bca7558 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; diff --git a/src/load.c b/src/load.c index ab0346750..70f18da31 100644 --- a/src/load.c +++ b/src/load.c @@ -608,7 +608,7 @@ read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) mrb_irep* mrb_read_irep(mrb_state *mrb, const uint8_t *bin) { -#ifdef MRB_USE_ETEXT_EDATA +#if defined(MRB_USE_ETEXT_EDATA) || defined(MRB_USE_CUSTOM_RO_DATA_P) uint8_t flags = mrb_ro_data_p((char*)bin) ? FLAG_SRC_STATIC : FLAG_SRC_MALLOC; #else uint8_t flags = FLAG_SRC_STATIC; diff --git a/src/numeric.c b/src/numeric.c index 8205e41f6..101b338de 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -1230,7 +1230,7 @@ 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); 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 f7a805a94..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); } @@ -997,20 +1000,6 @@ mrb_string_value_len(mrb_state *mrb, mrb_value ptr) return RSTRING_LEN(ptr); } -void -mrb_noregexp(mrb_state *mrb, mrb_value self) -{ - mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented"); -} - -void -mrb_regexp_check(mrb_state *mrb, mrb_value obj) -{ - if (mrb_regexp_p(mrb, obj)) { - mrb_noregexp(mrb, obj); - } -} - MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str) { @@ -1026,7 +1015,6 @@ mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx) { mrb_int idx; - mrb_regexp_check(mrb, indx); switch (mrb_type(indx)) { case MRB_TT_FIXNUM: idx = mrb_fixnum(indx); @@ -1119,13 +1107,9 @@ mrb_str_aref_m(mrb_state *mrb, mrb_value str) if (argc == 2) { mrb_int n1, n2; - mrb_regexp_check(mrb, a1); 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); } @@ -1549,7 +1533,6 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str) else sub = mrb_nil_value(); } - mrb_regexp_check(mrb, sub); clen = RSTRING_CHAR_LEN(str); if (pos < 0) { pos += clen; @@ -1801,7 +1784,6 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str) if (pos < 0) { pos += len; if (pos < 0) { - mrb_regexp_check(mrb, sub); return mrb_nil_value(); } } @@ -1815,7 +1797,6 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str) sub = mrb_nil_value(); } pos = chars2bytes(str, 0, pos); - mrb_regexp_check(mrb, sub); switch (mrb_type(sub)) { default: { @@ -1909,16 +1890,11 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) if (argc == 0 || mrb_nil_p(spat)) { split_type = awk; } - else { - if (mrb_string_p(spat)) { - split_type = string; - if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') { - split_type = awk; - } - } - else { - mrb_noregexp(mrb, str); - } + else if (!mrb_string_p(spat)) { + mrb_raise(mrb, E_TYPE_ERROR, "expected String"); + } + else if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') { + split_type = awk; } result = mrb_ary_new(mrb); @@ -1955,7 +1931,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) } } } - else if (split_type == string) { + else { /* split_type == string */ mrb_int str_len = RSTRING_LEN(str); mrb_int pat_len = RSTRING_LEN(spat); mrb_int idx = 0; @@ -1976,9 +1952,6 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) } beg = idx; } - else { - mrb_noregexp(mrb, str); - } if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) { if (RSTRING_LEN(str) == beg) { tmp = mrb_str_new_empty(mrb, str); @@ -2142,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)); } } @@ -2174,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 @@ -2301,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 e859f02bf..ee21a3b96 100644 --- a/src/variable.c +++ b/src/variable.c @@ -674,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; @@ -701,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); } @@ -490,22 +490,21 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc if (mrb->c->stbase <= argv && argv < mrb->c->stend) { voff = argv - mrb->c->stbase; } - if (MRB_METHOD_CFUNC_P(m)) { - mrb_stack_extend(mrb, argc + 2); - } - else if (argc >= CALL_MAXARGS) { + if (argc >= CALL_MAXARGS) { mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); - mrb_stack_extend(mrb, 3); mrb->c->stack[1] = args; ci->argc = -1; argc = 1; } - else if (MRB_METHOD_PROC_P(m)) { + mrb_stack_extend(mrb, argc + 2); + if (MRB_METHOD_PROC_P(m)) { struct RProc *p = MRB_METHOD_PROC(m); ci->proc = p; - mrb_stack_extend(mrb, p->body.irep->nregs + argc); + if (!MRB_PROC_CFUNC_P(p)) { + mrb_stack_extend(mrb, p->body.irep->nregs + argc); + } } if (voff >= 0) { argv = mrb->c->stbase + voff; 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/float.rb b/test/t/float.rb index b4eb0bfd6..63bf83f40 100644 --- a/test/t/float.rb +++ b/test/t/float.rb @@ -178,10 +178,10 @@ assert('Float#divmod') do end assert('Float#nan?') do - assert_predicate 0.0/0.0, :nan? - assert_not_predicate 0.0, :nan? - assert_not_predicate 1.0/0.0, :nan? - assert_not_predicate -1.0/0.0, :nan? + assert_predicate(0.0/0.0, :nan?) + assert_not_predicate(0.0, :nan?) + assert_not_predicate(1.0/0.0, :nan?) + assert_not_predicate(-1.0/0.0, :nan?) end assert('Float#<<') do 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] |
