diff options
| author | Felix Jones <[email protected]> | 2017-02-16 13:33:46 +0000 |
|---|---|---|
| committer | Felix Jones <[email protected]> | 2017-02-16 13:33:46 +0000 |
| commit | d83aad8d570e4bbffa3bd3ce64e210f78afa425f (patch) | |
| tree | 5389a87c135b1bdf3e23a1ba02e02400b7cf80fc /mrbgems | |
| parent | 70aa6dc38d75dd6b1e2c76f290bc576e36e36ea3 (diff) | |
| parent | b165708c8deba00685f9a27926c554aaa7f3b0fb (diff) | |
| download | mruby-d83aad8d570e4bbffa3bd3ce64e210f78afa425f.tar.gz mruby-d83aad8d570e4bbffa3bd3ce64e210f78afa425f.zip | |
Merge branch 'master' into android.rake-ndk-clang
Diffstat (limited to 'mrbgems')
45 files changed, 1253 insertions, 466 deletions
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox index 0960ba979..64f05de10 100644 --- a/mrbgems/default.gembox +++ b/mrbgems/default.gembox @@ -53,7 +53,7 @@ MRuby::GemBox.new do |conf| # Use Enumerator class (require mruby-fiber) conf.gem :core => "mruby-enumerator" - # Use Enumerable::Lazy class (require mruby-enumerator) + # Use Enumerator::Lazy class (require mruby-enumerator) conf.gem :core => "mruby-enum-lazy" # Use toplevel object (main) methods extension @@ -71,6 +71,9 @@ MRuby::GemBox.new do |conf| # Use Kernel module extension conf.gem :core => "mruby-kernel-ext" + # Use class/module extension + conf.gem :core => "mruby-class-ext" + # Use mruby-compiler to build other mrbgems conf.gem :core => "mruby-compiler" end diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c index d5c96e2cc..af947303b 100644 --- a/mrbgems/mruby-array-ext/src/array.c +++ b/mrbgems/mruby-array-ext/src/array.c @@ -131,7 +131,7 @@ mrb_ary_to_h(mrb_state *mrb, mrb_value ary) if (mrb_nil_p(v)) { mrb_raisef(mrb, E_TYPE_ERROR, "wrong element type %S at %S (expected array)", - mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, RARRAY_PTR(ary)[i])), + mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, ary_elt(ary, i))), mrb_fixnum_value(i) ); } diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb index ec1528fc3..09ec8d9e7 100644 --- a/mrbgems/mruby-array-ext/test/array.rb +++ b/mrbgems/mruby-array-ext/test/array.rb @@ -301,6 +301,17 @@ assert('Array#to_h') do assert_raise(ArgumentError) { [[1]].to_h } end +assert('Array#to_h (Modified)') do + class A + def to_ary + $a.clear + nil + end + end + $a = [A.new] + assert_raise(TypeError) { $a.to_h } +end + assert("Array#index (block)") do assert_nil (1..10).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } assert_equal 34, (1..100).to_a.index { |i| i % 5 == 0 and i % 7 == 0 } diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c index 218aeda90..27f27a05a 100644 --- a/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c +++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apibreak.c @@ -43,7 +43,7 @@ check_lineno( mrb_irep_debug_info_file *info_file, uint16_t lineno ) } static int32_t -get_break_index( mrb_debug_context *dbg, int32_t bpno ) +get_break_index( mrb_debug_context *dbg, uint32_t bpno ) { uint32_t i; int32_t index; @@ -296,7 +296,7 @@ mrb_debug_get_break_all( mrb_state *mrb, mrb_debug_context *dbg, uint32_t size, int32_t mrb_debug_get_break( mrb_state *mrb, mrb_debug_context *dbg, uint32_t bpno, mrb_debug_breakpoint *bp ) { - uint32_t index; + int32_t index; if((mrb == NULL) || (dbg == NULL) || (bp == NULL)) { return MRB_DEBUG_INVALID_ARGUMENT; diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c index 1c77d1ef8..33e9575bf 100644 --- a/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c +++ b/mrbgems/mruby-bin-debugger/tools/mrdb/apilist.c @@ -71,7 +71,7 @@ dirname(mrb_state *mrb, const char *path) } p = strrchr(path, '/'); - len = p != NULL ? p - path : strlen(path); + len = p != NULL ? (size_t)(p - path) : strlen(path); dir = mrb_malloc(mrb, len + 1); strncpy(dir, path, len); diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb index 01fc94632..72ac6586d 100644 --- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb +++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb @@ -44,3 +44,17 @@ EOS script.flush assert_equal "\"test\"\n\"fin\"\n", `#{cmd('mruby')} #{script.path}` end + +assert('garbage collecting built-in classes') do + script = Tempfile.new('test.rb') + + script.write <<RUBY +NilClass = nil +GC.start +Array.dup +print nil.class.to_s +RUBY + script.flush + assert_equal "NilClass", `#{cmd('mruby')} #{script.path}` + assert_equal 0, $?.exitstatus +end diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c index ab6c6688f..d10535140 100644 --- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c @@ -6,6 +6,7 @@ #include <mruby/compile.h> #include <mruby/dump.h> #include <mruby/variable.h> +#include <mruby/throw.h> #ifdef MRB_DISABLE_STDIO static void @@ -176,6 +177,8 @@ main(int argc, char **argv) mrbc_context *c; mrb_value v; mrb_sym zero_sym; + struct mrb_jmpbuf c_jmp; + int ai; if (mrb == NULL) { fputs("Invalid mrb_state, exiting mruby\n", stderr); @@ -189,59 +192,67 @@ main(int argc, char **argv) return n; } - ARGV = mrb_ary_new_capa(mrb, args.argc); - for (i = 0; i < args.argc; i++) { - char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); - if (utf8) { - mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); - mrb_utf8_free(utf8); + ai = mrb_gc_arena_save(mrb); + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + ARGV = mrb_ary_new_capa(mrb, args.argc); + for (i = 0; i < args.argc; i++) { + char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); + if (utf8) { + mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); + mrb_utf8_free(utf8); + } } - } - mrb_define_global_const(mrb, "ARGV", ARGV); - - c = mrbc_context_new(mrb); - if (args.verbose) - c->dump_result = TRUE; - if (args.check_syntax) - c->no_exec = TRUE; - - /* Set $0 */ - zero_sym = mrb_intern_lit(mrb, "$0"); - if (args.rfp) { - const char *cmdline; - cmdline = args.cmdline ? args.cmdline : "-"; - mrbc_filename(mrb, c, cmdline); - mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline)); - } - else { - mrbc_filename(mrb, c, "-e"); - mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e")); - } + mrb_define_global_const(mrb, "ARGV", ARGV); - /* Load program */ - if (args.mrbfile) { - v = mrb_load_irep_file_cxt(mrb, args.rfp, c); - } - else if (args.rfp) { - v = mrb_load_file_cxt(mrb, args.rfp, c); - } - else { - char* utf8 = mrb_utf8_from_locale(args.cmdline, -1); - if (!utf8) abort(); - v = mrb_load_string_cxt(mrb, utf8, c); - mrb_utf8_free(utf8); - } + c = mrbc_context_new(mrb); + if (args.verbose) + c->dump_result = TRUE; + if (args.check_syntax) + c->no_exec = TRUE; + + /* Set $0 */ + zero_sym = mrb_intern_lit(mrb, "$0"); + if (args.rfp) { + const char *cmdline; + cmdline = args.cmdline ? args.cmdline : "-"; + mrbc_filename(mrb, c, cmdline); + mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline)); + } + else { + mrbc_filename(mrb, c, "-e"); + mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e")); + } + + /* Load program */ + if (args.mrbfile) { + v = mrb_load_irep_file_cxt(mrb, args.rfp, c); + } + else if (args.rfp) { + v = mrb_load_file_cxt(mrb, args.rfp, c); + } + else { + char* utf8 = mrb_utf8_from_locale(args.cmdline, -1); + if (!utf8) abort(); + v = mrb_load_string_cxt(mrb, utf8, c); + mrb_utf8_free(utf8); + } - mrbc_context_free(mrb, c); - if (mrb->exc) { - if (!mrb_undef_p(v)) { - mrb_print_error(mrb); + mrb_gc_arena_restore(mrb, ai); + mrbc_context_free(mrb, c); + if (mrb->exc) { + if (!mrb_undef_p(v)) { + mrb_print_error(mrb); + } + n = -1; + } + else if (args.check_syntax) { + printf("Syntax OK\n"); } - n = -1; } - else if (args.check_syntax) { - printf("Syntax OK\n"); + MRB_CATCH(&c_jmp) { /* error */ } + MRB_END_EXC(&c_jmp); cleanup(mrb, &args); return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE; diff --git a/mrbgems/mruby-class-ext/mrbgem.rake b/mrbgems/mruby-class-ext/mrbgem.rake new file mode 100644 index 000000000..a384b1eef --- /dev/null +++ b/mrbgems/mruby-class-ext/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-class-ext') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'class/module extension' +end diff --git a/mrbgems/mruby-class-ext/src/class.c b/mrbgems/mruby-class-ext/src/class.c new file mode 100644 index 000000000..8ca7d66c2 --- /dev/null +++ b/mrbgems/mruby-class-ext/src/class.c @@ -0,0 +1,23 @@ +#include "mruby.h" +#include "mruby/class.h" +#include "mruby/string.h" + +static mrb_value +mrb_mod_name(mrb_state *mrb, mrb_value self) +{ + mrb_value name = mrb_class_path(mrb, mrb_class_ptr(self)); + return mrb_nil_p(name)? name : mrb_str_dup(mrb, name); +} + +void +mrb_mruby_class_ext_gem_init(mrb_state *mrb) +{ + struct RClass *mod = mrb->module_class; + + mrb_define_method(mrb, mod, "name", mrb_mod_name, MRB_ARGS_NONE()); +} + +void +mrb_mruby_class_ext_gem_final(mrb_state *mrb) +{ +} diff --git a/mrbgems/mruby-class-ext/test/module.rb b/mrbgems/mruby-class-ext/test/module.rb new file mode 100644 index 000000000..f721ad0c6 --- /dev/null +++ b/mrbgems/mruby-class-ext/test/module.rb @@ -0,0 +1,10 @@ +assert 'Module#name' do + module A + class B + end + end + + assert_nil A::B.singleton_class.name + assert_equal 'Fixnum', Fixnum.name + assert_equal 'A::B', A::B.name +end diff --git a/mrbgems/mruby-compiler/bintest/mrbc.rb b/mrbgems/mruby-compiler/bintest/mrbc.rb index e4dc6a9a8..e27365edb 100644 --- a/mrbgems/mruby-compiler/bintest/mrbc.rb +++ b/mrbgems/mruby-compiler/bintest/mrbc.rb @@ -10,3 +10,12 @@ assert('Compiling multiple files without new line in last line. #2361') do assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp assert_equal 0, $?.exitstatus end + +assert('parsing function with void argument') do + a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb') + a.write('f ()') + a.flush + result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1` + assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp + assert_equal 0, $?.exitstatus +end diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 9b064b867..23e036d49 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -93,6 +93,7 @@ codegen_error(codegen_scope *s, const char *message) if (!s) return; while (s->prev) { codegen_scope *tmp = s->prev; + mrb_free(s->mrb, s->iseq); mrb_pool_close(s->mpool); s = tmp; } @@ -387,6 +388,9 @@ dispatch(codegen_scope *s, int pc) scope_error(s); break; } + if (diff > MAXARG_sBx) { + codegen_error(s, "too distant jump address"); + } s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff); } @@ -500,7 +504,12 @@ new_lit(codegen_scope *s, mrb_value val) return i; } -static inline int +/* method symbols should be fit in 9 bits */ +#define MAXMSYMLEN 512 +/* maximum symbol numbers */ +#define MAXSYMLEN 65536 + +static int new_msym(codegen_scope *s, mrb_sym sym) { size_t i, len; @@ -508,20 +517,20 @@ new_msym(codegen_scope *s, mrb_sym sym) mrb_assert(s->irep); len = s->irep->slen; - if (len > 256) len = 256; + if (len > MAXMSYMLEN) len = MAXMSYMLEN; for (i=0; i<len; i++) { if (s->irep->syms[i] == sym) return i; if (s->irep->syms[i] == 0) break; } - if (i == 256) { - codegen_error(s, "too many symbols (max 256)"); + if (i == MAXMSYMLEN) { + codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXMSYMLEN) ")"); } s->irep->syms[i] = sym; if (i == s->irep->slen) s->irep->slen++; return i; } -static inline int +static int new_sym(codegen_scope *s, mrb_sym sym) { size_t i; @@ -529,13 +538,18 @@ new_sym(codegen_scope *s, mrb_sym sym) for (i=0; i<s->irep->slen; i++) { if (s->irep->syms[i] == sym) return i; } - if (s->irep->slen > 125 && s->irep->slen < 256) { - s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*65536); - for (i = 0; i < 256 - s->irep->slen; i++) { + if (s->irep->slen == MAXSYMLEN) { + codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXSYMLEN) ")"); + } + + if (s->irep->slen > MAXMSYMLEN/2 && s->scapa == MAXMSYMLEN) { + s->scapa = MAXSYMLEN; + s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*MAXSYMLEN); + for (i = s->irep->slen; i < MAXMSYMLEN; i++) { static const mrb_sym mrb_sym_zero = { 0 }; - s->irep->syms[i + s->irep->slen] = mrb_sym_zero; + s->irep->syms[i] = mrb_sym_zero; } - s->irep->slen = 256; + s->irep->slen = MAXMSYMLEN; } s->irep->syms[s->irep->slen] = sym; return s->irep->slen++; @@ -588,9 +602,6 @@ for_body(codegen_scope *s, node *tree) push(); /* push for a block parameter */ - lp = loop_push(s, LOOP_FOR); - lp->pc1 = new_label(s); - /* generate loop variable */ n2 = tree->car; genop(s, MKOP_Ax(OP_ENTER, 0x40000)); @@ -600,6 +611,11 @@ for_body(codegen_scope *s, node *tree) else { gen_vmassignment(s, n2, 1, VAL); } + /* construct loop */ + lp = loop_push(s, LOOP_FOR); + lp->pc2 = new_label(s); + + /* loop body */ codegen(s, tree->cdr->cdr->car, VAL); pop(); if (s->pc > 0) { @@ -650,6 +666,9 @@ lambda_body(codegen_scope *s, node *tree, int blk) ka = kd = 0; ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0; + if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) { + codegen_error(s, "too many formal arguments"); + } a = ((mrb_aspec)(ma & 0x1f) << 18) | ((mrb_aspec)(oa & 0x1f) << 13) | ((ra & 1) << 12) @@ -770,6 +789,8 @@ attrsym(codegen_scope *s, mrb_sym a) return mrb_intern(s->mrb, name2, len+1); } +#define CALL_MAXARGS 127 + static int gen_values(codegen_scope *s, node *t, int val) { @@ -778,7 +799,9 @@ gen_values(codegen_scope *s, node *t, int val) while (t) { is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */ - if (n >= 127 || is_splat) { + if ( + n >= CALL_MAXARGS - 1 /* need to subtract one because vm.c expects an array if n == CALL_MAXARGS */ + || is_splat) { if (val) { if (is_splat && n == 0 && (intptr_t)t->car->cdr->car == NODE_ARRAY) { codegen(s, t->car->cdr, VAL); @@ -812,8 +835,6 @@ gen_values(codegen_scope *s, node *t, int val) } } else { - codegen(s, t->car->cdr, NOVAL); - t = t->cdr; while (t) { codegen(s, t->car, NOVAL); t = t->cdr; @@ -829,8 +850,6 @@ gen_values(codegen_scope *s, node *t, int val) return n; } -#define CALL_MAXARGS 127 - static void gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe) { @@ -1057,7 +1076,9 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val) n++; } } - push(); + if (!val) { + push(); + } } } @@ -1360,6 +1381,10 @@ codegen(codegen_scope *s, node *tree, int val) int pos1, pos2; node *e = tree->cdr->cdr->car; + if (!tree->car) { + codegen(s, e, val); + return; + } switch ((intptr_t)tree->car->car) { case NODE_TRUE: case NODE_INT: @@ -1510,7 +1535,9 @@ codegen(codegen_scope *s, node *tree, int val) genop(s, MKOP_A(OP_LOADNIL, cursp())); if (pos3) dispatch_linked(s, pos3); if (head) pop(); - genop(s, MKOP_AB(OP_MOVE, cursp(), pos)); + if (cursp() != pos) { + genop(s, MKOP_AB(OP_MOVE, cursp(), pos)); + } push(); } else { @@ -1655,7 +1682,6 @@ codegen(codegen_scope *s, node *tree, int val) } tree = tree->car; if (tree->car) { /* pre */ - int first = TRUE; t = tree->car; n = 0; while (t) { @@ -1664,10 +1690,7 @@ codegen(codegen_scope *s, node *tree, int val) n++; } else { - if (first) { - genop(s, MKOP_A(OP_LOADNIL, rhs+n)); - first = FALSE; - } + genop(s, MKOP_A(OP_LOADNIL, rhs+n)); gen_assignment(s, t->car, rhs+n, NOVAL); } t = t->cdr; @@ -1746,6 +1769,7 @@ codegen(codegen_scope *s, node *tree, int val) genop(s, MKOP_A(OP_RESCUE, exc)); genop(s, MKOP_A(OP_LOADF, exc)); dispatch(s, noexc); + loop_pop(s, NOVAL); } else if ((intptr_t)tree->car->car == NODE_CALL) { node *n = tree->car->cdr; @@ -1795,8 +1819,10 @@ codegen(codegen_scope *s, node *tree, int val) int pos; pop(); - if (val && vsp >= 0) { - genop(s, MKOP_AB(OP_MOVE, vsp, cursp())); + if (val) { + if (vsp >= 0) { + genop(s, MKOP_AB(OP_MOVE, vsp, cursp())); + } pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0)); } else { @@ -2012,7 +2038,7 @@ codegen(codegen_scope *s, node *tree, int val) break; case NODE_REDO: - if (!s->loop) { + if (!s->loop || s->loop->type == LOOP_BEGIN || s->loop->type == LOOP_RESCUE) { raise_error(s, "unexpected redo"); } else { @@ -2021,6 +2047,7 @@ codegen(codegen_scope *s, node *tree, int val) } genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc)); } + if (val) push(); break; case NODE_RETRY: @@ -2055,6 +2082,7 @@ codegen(codegen_scope *s, node *tree, int val) genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc)); } } + if (val) push(); } break; @@ -2194,7 +2222,7 @@ codegen(codegen_scope *s, node *tree, int val) case NODE_FLOAT: if (val) { char *p = (char*)tree; - mrb_float f = str_to_mrb_float(p); + mrb_float f = mrb_float_read(p, NULL); int off = new_lit(s, mrb_float_value(s->mrb, f)); genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); @@ -2208,9 +2236,9 @@ codegen(codegen_scope *s, node *tree, int val) tree = tree->cdr; switch (nt) { case NODE_FLOAT: - { + if (val) { char *p = (char*)tree; - mrb_float f = str_to_mrb_float(p); + mrb_float f = mrb_float_read(p, NULL); int off = new_lit(s, mrb_float_value(s->mrb, -f)); genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); @@ -2219,7 +2247,7 @@ codegen(codegen_scope *s, node *tree, int val) break; case NODE_INT: - { + if (val) { char *p = (char*)tree->car; int base = (intptr_t)tree->cdr->car; mrb_int i; @@ -2248,7 +2276,7 @@ codegen(codegen_scope *s, node *tree, int val) break; default: - { + if (val) { int sym = new_msym(s, mrb_intern_lit(s->mrb, "-")); genop(s, MKOP_ABx(OP_LOADI, cursp(), 0)); @@ -2257,6 +2285,9 @@ codegen(codegen_scope *s, node *tree, int val) pop(); pop(); genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2)); } + else { + codegen(s, tree, NOVAL); + } break; } } @@ -2282,7 +2313,11 @@ codegen(codegen_scope *s, node *tree, int val) if (val) { node *n = tree; - if (!n) break; + if (!n) { + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + break; + } codegen(s, n->car, VAL); n = n->cdr; while (n) { @@ -2539,13 +2574,31 @@ codegen(codegen_scope *s, node *tree, int val) genop(s, MKOP_A(OP_TCLASS, cursp())); push(); while (t) { - int symbol = new_msym(s, sym(t->car)); + int symbol; + if (num >= CALL_MAXARGS - 1) { + pop_n(num); + genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), num)); + while (t) { + symbol = new_msym(s, sym(t->car)); + push(); + genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol)); + pop(); + genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); + t = t->cdr; + } + num = CALL_MAXARGS; + break; + } + symbol = new_msym(s, sym(t->car)); genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol)); push(); t = t->cdr; num++; } - pop_n(num + 1); + pop(); + if (num < CALL_MAXARGS) { + pop_n(num); + } genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num)); if (val) { push(); @@ -2713,13 +2766,13 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv) p->icapa = 1024; p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa); - p->irep->iseq = p->iseq; + p->irep->iseq = NULL; p->pcapa = 32; p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa); p->irep->plen = 0; - p->scapa = 256; + p->scapa = MAXMSYMLEN; p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa); p->irep->slen = 0; @@ -2797,6 +2850,7 @@ scope_finish(codegen_scope *s) memcpy(fname, s->filename, fname_len); fname[fname_len] = '\0'; irep->filename = fname; + irep->own_filename = TRUE; } irep->nlocals = s->nlocals; @@ -2904,9 +2958,6 @@ mrb_generate_code(mrb_state *mrb, parser_state *p) return proc; } MRB_CATCH(&scope->jmp) { - if (scope->filename == scope->irep->filename) { - scope->irep->filename = NULL; - } mrb_irep_decref(mrb, scope->irep); mrb_pool_close(scope->mpool); return NULL; diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index 426c262fb..f8e4eb72f 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -41,6 +41,7 @@ static void yyerror(parser_state *p, const char *s); static void yywarn(parser_state *p, const char *s); static void yywarning(parser_state *p, const char *s); static void backref_error(parser_state *p, node *n); +static void void_expr_error(parser_state *p, node *n); static void tokadd(parser_state *p, int32_t c); #define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) @@ -870,6 +871,7 @@ call_with_block(parser_state *p, node *a, node *b) break; case NODE_CALL: case NODE_FCALL: + case NODE_SCALL: n = a->cdr->cdr->cdr; if (!n->car) n->car = cons(0, b); else { @@ -1097,7 +1099,7 @@ heredoc_end(parser_state *p) %type <nd> literal numeric cpath symbol %type <nd> top_compstmt top_stmts top_stmt %type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call -%type <nd> expr_value arg_value arg_rhs primary_value +%type <nd> expr_value arg_rhs primary_value %type <nd> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure %type <nd> args call_args opt_call_args %type <nd> paren_args opt_paren_args variable @@ -1322,7 +1324,7 @@ stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym { $$ = new_asgn(p, $1, new_array(p, $3)); } - | mlhs '=' arg_value + | mlhs '=' arg { $$ = new_masgn(p, $1, $3); } @@ -1401,7 +1403,10 @@ expr : command_call expr_value : expr { if (!$1) $$ = new_nil(p); - else $$ = $1; + else { + void_expr_error(p, $1); + $$ = $1; + } } ; @@ -1907,20 +1912,13 @@ arg : lhs '=' arg_rhs } ; -arg_value : arg - { - $$ = $1; - if (!$$) $$ = new_nil(p); - } - ; - aref_args : none | args trailer { $$ = $1; NODE_LINENO($$, $1); } - | args ',' assocs trailer + | args comma assocs trailer { $$ = push($1, new_hash(p, $3)); } @@ -1931,9 +1929,14 @@ aref_args : none } ; -arg_rhs : arg %prec tOP_ASGN +arg_rhs : arg %prec tOP_ASGN + { + void_expr_error(p, $1); + $$ = $1; + } | arg modifier_rescue arg { + void_expr_error(p, $1); $$ = new_mod_rescue(p, $1, $3); } ; @@ -1955,7 +1958,7 @@ opt_call_args : none $$ = cons($1,0); NODE_LINENO($$, $1); } - | args ',' assocs ',' + | args comma assocs ',' { $$ = cons(push($1, new_hash(p, $3)), 0); NODE_LINENO($$, $1); @@ -1982,7 +1985,7 @@ call_args : command $$ = cons(list1(new_hash(p, $1)), $2); NODE_LINENO($$, $1); } - | args ',' assocs opt_block_arg + | args comma assocs opt_block_arg { $$ = cons(push($1, new_hash(p, $3)), $4); NODE_LINENO($$, $1); @@ -2005,13 +2008,13 @@ command_args : { } ; -block_arg : tAMPER arg_value +block_arg : tAMPER arg { $$ = new_block_arg(p, $2); } ; -opt_block_arg : ',' block_arg +opt_block_arg : comma block_arg { $$ = $2; } @@ -2021,44 +2024,47 @@ opt_block_arg : ',' block_arg } ; -args : arg_value +comma : ',' + | ',' heredoc_bodies + ; + +args : arg { + void_expr_error(p, $1); $$ = cons($1, 0); NODE_LINENO($$, $1); } - | tSTAR arg_value + | tSTAR arg { + void_expr_error(p, $2); $$ = cons(new_splat(p, $2), 0); NODE_LINENO($$, $2); } - | args ',' arg_value + | args comma arg { + void_expr_error(p, $3); $$ = push($1, $3); } - | args ',' tSTAR arg_value + | args comma tSTAR arg { + void_expr_error(p, $4); $$ = push($1, new_splat(p, $4)); } - | args ',' heredoc_bodies arg_value - { - $$ = push($1, $4); - } - | args ',' heredoc_bodies tSTAR arg_value - { - $$ = push($1, new_splat(p, $5)); - } ; -mrhs : args ',' arg_value +mrhs : args comma arg { + void_expr_error(p, $3); $$ = push($1, $3); } - | args ',' tSTAR arg_value + | args comma tSTAR arg { + void_expr_error(p, $4); $$ = push($1, new_splat(p, $4)); } - | tSTAR arg_value + | tSTAR arg { + void_expr_error(p, $2); $$ = list1(new_splat(p, $2)); } ; @@ -2097,7 +2103,7 @@ primary : literal } | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen { - $$ = 0; + $$ = new_nil(p); } | tLPAREN compstmt ')' { @@ -2125,17 +2131,9 @@ primary : literal { $$ = new_return(p, 0); } - | keyword_yield '(' call_args rparen + | keyword_yield opt_paren_args { - $$ = new_yield(p, $3); - } - | keyword_yield '(' rparen - { - $$ = new_yield(p, 0); - } - | keyword_yield - { - $$ = new_yield(p, 0); + $$ = new_yield(p, $2); } | keyword_not '(' expr rparen { @@ -2450,7 +2448,7 @@ block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg } | f_arg ',' { - $$ = new_args(p, $1, 0, 1, 0, 0); + $$ = new_args(p, $1, 0, 0, 0, 0); } | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg { @@ -2688,7 +2686,7 @@ opt_rescue : keyword_rescue exc_list exc_var then | none ; -exc_list : arg_value +exc_list : arg { $$ = list1($1); } @@ -2783,10 +2781,6 @@ regexp : tREGEXP_BEG tREGEXP heredoc : tHEREDOC_BEG ; -opt_heredoc_bodies : /* none */ - | heredoc_bodies - ; - heredoc_bodies : heredoc_body | heredoc_bodies heredoc_body ; @@ -3115,7 +3109,7 @@ f_opt_asgn : tIDENTIFIER '=' } ; -f_opt : f_opt_asgn arg_value +f_opt : f_opt_asgn arg { $$ = cons(nsym($1), $2); } @@ -3233,23 +3227,23 @@ assocs : assoc } ; -assoc : arg_value tASSOC arg_value +assoc : arg tASSOC arg { $$ = cons($1, $3); } - | tLABEL arg_value + | tLABEL arg { $$ = cons(new_sym(p, $1), $2); } - | tLABEL_END arg_value + | tLABEL_END arg { $$ = cons(new_sym(p, new_strsym(p, $1)), $2); } - | tSTRING_BEG tLABEL_END arg_value + | tSTRING_BEG tLABEL_END arg { $$ = cons(new_sym(p, new_strsym(p, $2)), $3); } - | tSTRING_BEG string_rep tLABEL_END arg_value + | tSTRING_BEG string_rep tLABEL_END arg { $$ = cons(new_dsym(p, push($2, $3)), $4); } @@ -3308,11 +3302,12 @@ rbracket : opt_nl ']' trailer : /* none */ | nl - | ',' + | comma ; term : ';' {yyerrok;} | nl + | heredoc_body ; nl : '\n' @@ -3320,10 +3315,10 @@ nl : '\n' p->lineno++; p->column = 0; } - opt_heredoc_bodies + ; terms : term - | terms ';' {yyerrok;} + | terms term ; none : /* none */ @@ -3430,6 +3425,26 @@ backref_error(parser_state *p, node *n) } } +static void +void_expr_error(parser_state *p, node *n) +{ + int c; + + if (n == NULL) return; + c = (int)(intptr_t)n->car; + switch (c) { + case NODE_BREAK: + case NODE_RETURN: + case NODE_NEXT: + case NODE_REDO: + case NODE_RETRY: + yyerror(p, "void value expression"); + break; + default: + break; + } +} + static void pushback(parser_state *p, int c); static mrb_bool peeks(parser_state *p, const char *s); static mrb_bool skips(parser_state *p, const char *s); @@ -3730,6 +3745,44 @@ scan_hex(const int *start, int len, int *retlen) return retval; } +static int32_t +read_escape_unicode(parser_state *p, int limit) +{ + int32_t c; + int buf[9]; + int i; + + /* Look for opening brace */ + i = 0; + buf[0] = nextc(p); + if (buf[0] < 0) goto eof; + if (ISXDIGIT(buf[0])) { + /* \uxxxx form */ + for (i=1; i<limit; i++) { + buf[i] = nextc(p); + if (buf[i] < 0) goto eof; + if (!ISXDIGIT(buf[i])) { + pushback(p, buf[i]); + break; + } + } + } + else { + pushback(p, buf[0]); + } + c = scan_hex(buf, i, &i); + if (i == 0) { + eof: + yyerror(p, "Invalid escape character syntax"); + return -1; + } + if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) { + yyerror(p, "Invalid Unicode code point"); + return -1; + } + return c; +} + /* Return negative to indicate Unicode code point */ static int32_t read_escape(parser_state *p) @@ -3802,53 +3855,17 @@ read_escape(parser_state *p) return c; case 'u': /* Unicode */ - { - int buf[9]; - int i; - - /* Look for opening brace */ - i = 0; - buf[0] = nextc(p); - if (buf[0] < 0) goto eof; - if (buf[0] == '{') { + if (peek(p, '{')) { /* \u{xxxxxxxx} form */ - for (i=0; i<9; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (buf[i] == '}') { - break; - } - else if (!ISXDIGIT(buf[i])) { - yyerror(p, "Invalid escape character syntax"); - pushback(p, buf[i]); - return 0; - } - } - } - else if (ISXDIGIT(buf[0])) { - /* \uxxxx form */ - for (i=1; i<4; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (!ISXDIGIT(buf[i])) { - pushback(p, buf[i]); - break; - } - } + nextc(p); + c = read_escape_unicode(p, 8); + if (c < 0) return 0; + if (nextc(p) != '}') goto eof; } else { - pushback(p, buf[0]); + c = read_escape_unicode(p, 4); + if (c < 0) return 0; } - c = scan_hex(buf, i, &i); - if (i == 0) { - yyerror(p, "Invalid escape character syntax"); - return 0; - } - if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) { - yyerror(p, "Invalid Unicode code point"); - return 0; - } - } return -c; case 'b':/* backspace */ @@ -3906,7 +3923,10 @@ parse_string(parser_state *p) int beg = (intptr_t)p->lex_strterm->cdr->cdr->car; int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr; parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL; + int cmd_state = p->cmd_start; + if (beg == 0) beg = -3; /* should never happen */ + if (end == 0) end = -3; newtok(p); while ((c = nextc(p)) != end || nest_level != 0) { if (hinf && (c == '\n' || c < 0)) { @@ -3928,7 +3948,12 @@ parse_string(parser_state *p) } } if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) { - return tHEREDOC_END; + if (c < 0) { + p->parsing_heredoc = NULL; + } + else { + return tHEREDOC_END; + } } } if (c < 0) { @@ -3969,6 +3994,20 @@ parse_string(parser_state *p) tokadd(p, '\\'); tokadd(p, c); } + else if (c == 'u' && peek(p, '{')) { + /* \u{xxxx xxxx xxxx} form */ + nextc(p); + while (1) { + do c = nextc(p); while (ISSPACE(c)); + if (c == '}') break; + pushback(p, c); + c = read_escape_unicode(p, 8); + if (c < 0) break; + tokadd(p, -c); + } + if (hinf) + hinf->line_head = FALSE; + } else { pushback(p, c); tokadd(p, read_escape(p)); @@ -4093,10 +4132,12 @@ parse_string(parser_state *p) return tREGEXP; } yylval.nd = new_str(p, tok(p), toklen(p)); - if (IS_LABEL_SUFFIX(0)) { - p->lstate = EXPR_BEG; - nextc(p); - return tLABEL_END; + if (IS_LABEL_POSSIBLE()) { + if (IS_LABEL_SUFFIX(0)) { + p->lstate = EXPR_BEG; + nextc(p); + return tLABEL_END; + } } return tSTRING; @@ -4871,7 +4912,7 @@ parser_yylex(parser_state *p) char *endp; errno = 0; - d = strtod(tok(p), &endp); + d = mrb_float_read(tok(p), &endp); if (d == 0 && endp == tok(p)) { yywarning_s(p, "corrupted float value %s", tok(p)); } @@ -5477,7 +5518,10 @@ mrb_parser_parse(parser_state *p, mrbc_context *c) p->lex_strterm = NULL; parser_init_cxt(p, c); - yyparse(p); + if (yyparse(p) != 0 || p->nerr > 0) { + p->tree = 0; + return; + } if (!p->tree) { p->tree = new_nil(p); } @@ -5657,8 +5701,8 @@ mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c) return mrb_parse_nstring(mrb, s, strlen(s), c); } -static mrb_value -load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c) +MRB_API mrb_value +mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c) { struct RClass *target = mrb->object_class; struct RProc *proc; @@ -5717,7 +5761,7 @@ load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c) MRB_API mrb_value mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c) { - return load_exec(mrb, mrb_parse_file(mrb, f, c), c); + return mrb_load_exec(mrb, mrb_parse_file(mrb, f, c), c); } MRB_API mrb_value @@ -5730,7 +5774,7 @@ mrb_load_file(mrb_state *mrb, FILE *f) MRB_API mrb_value mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c) { - return load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); + return mrb_load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); } MRB_API mrb_value @@ -6024,7 +6068,17 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset) case NODE_FCALL: case NODE_CALL: - printf("NODE_CALL:\n"); + case NODE_SCALL: + switch (nodetype) { + case NODE_FCALL: + printf("NODE_FCALL:\n"); break; + case NODE_CALL: + printf("NODE_CALL(.):\n"); break; + case NODE_SCALL: + printf("NODE_SCALL(&.):\n"); break; + default: + break; + } mrb_parser_dump(mrb, tree->car, offset+1); dump_prefix(tree, offset+1); printf("method='%s' (%d)\n", @@ -6504,8 +6558,8 @@ mrb_parser_dump(mrb_state *mrb, node *tree, int offset) break; case NODE_HEREDOC: - printf("NODE_HEREDOC:\n"); - mrb_parser_dump(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); + printf("NODE_HEREDOC (<<%s):\n", ((parser_heredoc_info*)tree)->term); + dump_recur(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); break; default: diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index a5f661ce6..113b470a9 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -58,13 +58,14 @@ module Enumerable def take(n) raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) - raise ArgumentError, "attempt to take negative size" if n < 0 - - n = n.to_int + i = n.to_int + raise ArgumentError, "attempt to take negative size" if i < 0 ary = [] + return ary if i == 0 self.each do |*val| - break if ary.size >= n ary << val.__svalue + i -= 1 + break if i == 0 end ary end @@ -214,21 +215,28 @@ module Enumerable # Returns the first element, or the first +n+ elements, of the enumerable. # If the enumerable is empty, the first form returns <code>nil</code>, and the # second form returns an empty array. - def first(n=NONE) - if n == NONE + def first(*args) + case args.length + when 0 self.each do |*val| return val.__svalue end return nil - else - a = [] - i = 0 + when 1 + n = args[0] + raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) + i = n.to_int + raise ArgumentError, "attempt to take negative size" if i < 0 + ary = [] + return ary if i == 0 self.each do |*val| - break if n<=i - a.push val.__svalue - i += 1 + ary << val.__svalue + i -= 1 + break if i == 0 end - a + ary + else + raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 0..1)" end end @@ -573,35 +581,38 @@ module Enumerable # a.cycle(2) { |x| puts x } # print, a, b, c, a, b, c. # - def cycle(n=nil, &block) - return to_enum(:cycle, n) if !block && n.nil? + def cycle(nv = nil, &block) + return to_enum(:cycle, nv) unless block - ary = [] - if n.nil? - self.each do|*val| - ary.push val - block.call(*val) + n = nil + + if nv.nil? + n = -1 + else + unless nv.respond_to?(:to_int) + raise TypeError, "no implicit conversion of #{nv.class} into Integer" end - loop do - ary.each do|e| - block.call(*e) - end + n = nv.to_int + unless n.kind_of?(Integer) + raise TypeError, "no implicit conversion of #{nv.class} into Integer" end - else - raise TypeError, "no implicit conversion of #{n.class} into Integer" unless n.respond_to?(:to_int) + return nil if n <= 0 + end - n = n.to_int - self.each do|*val| - ary.push val - end - count = 0 - while count < n - ary.each do|e| - block.call(*e) - end - count += 1 + ary = [] + each do |*i| + ary.push(i) + yield(*i) + end + return nil if ary.empty? + + while n < 0 || 0 < (n -= 1) + ary.each do |i| + yield(*i) end end + + nil end ## diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb index 08b553fe5..076562f45 100644 --- a/mrbgems/mruby-enum-ext/test/enum.rb +++ b/mrbgems/mruby-enum-ext/test/enum.rb @@ -128,6 +128,13 @@ assert("Enumerable#cycle") do ["a", "b", "c"].cycle(2) { |v| a << v } assert_equal ["a", "b", "c", "a", "b", "c"], a assert_raise(TypeError) { ["a", "b", "c"].cycle("a") { |v| a << v } } + + empty = Class.new do + include Enumerable + def each + end + end + assert_nil empty.new.cycle { break :nope } end assert("Enumerable#find_index") do diff --git a/mrbgems/mruby-enum-lazy/mrbgem.rake b/mrbgems/mruby-enum-lazy/mrbgem.rake index 219141e98..682134c41 100644 --- a/mrbgems/mruby-enum-lazy/mrbgem.rake +++ b/mrbgems/mruby-enum-lazy/mrbgem.rake @@ -1,7 +1,7 @@ MRuby::Gem::Specification.new('mruby-enum-lazy') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' - spec.summary = 'Enumerable::Lazy class' + spec.summary = 'Enumerator::Lazy class' spec.add_dependency('mruby-enumerator', :core => 'mruby-enumerator') spec.add_dependency('mruby-enum-ext', :core => 'mruby-enum-ext') end diff --git a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb index 8ce363c6d..b650072e2 100644 --- a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb +++ b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb @@ -2,7 +2,7 @@ module Enumerable # = Enumerable#lazy implementation # - # Enumerable#lazy returns an instance of Enumerable::Lazy. + # Enumerable#lazy returns an instance of Enumerator::Lazy. # You can use it just like as normal Enumerable object, # except these methods act as 'lazy': # @@ -16,9 +16,11 @@ module Enumerable # - flat_map collect_concat # - zip def lazy - Lazy.new(self) + Enumerator::Lazy.new(self) end +end +class Enumerator # == Acknowledgements # # Based on https://github.com/yhara/enumerable-lazy @@ -40,6 +42,15 @@ module Enumerable } end + def to_enum(meth=:each, *args, &block) + lz = Lazy.new(self, &block) + lz.obj = self + lz.meth = meth + lz.args = args + lz + end + alias enum_for to_enum + def map(&block) Lazy.new(self){|yielder, val| yielder << block.call(val) diff --git a/mrbgems/mruby-enum-lazy/test/lazy.rb b/mrbgems/mruby-enum-lazy/test/lazy.rb index ca009d34c..940d070e8 100644 --- a/mrbgems/mruby-enum-lazy/test/lazy.rb +++ b/mrbgems/mruby-enum-lazy/test/lazy.rb @@ -1,9 +1,9 @@ -assert("Enumerable::Lazy") do +assert("Enumerator::Lazy") do a = [1, 2] - assert_equal Enumerable::Lazy, a.lazy.class + assert_equal Enumerator::Lazy, a.lazy.class end -assert("Enumerable::Lazy laziness") do +assert("Enumerator::Lazy laziness") do a = Object.new def a.each return to_enum :each unless block_given? @@ -40,7 +40,13 @@ assert("Enumerable::Lazy laziness") do assert_equal [10,20], a.b end -assert("Enumerable::Lazy#zip with cycle") do +assert("Enumrator::Lazy#to_enum") do + lazy_enum = (0..Float::INFINITY).lazy.to_enum(:each_slice, 2) + assert_kind_of Enumerator::Lazy, lazy_enum + assert_equal [0*1, 2*3, 4*5, 6*7], lazy_enum.map { |a| a.first * a.last }.first(4) +end + +assert("Enumerator::Lazy#zip with cycle") do e1 = [1, 2, 3].cycle e2 = [:a, :b].cycle assert_equal [[1,:a],[2,:b],[3,:a]], e1.lazy.zip(e2).first(3) diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb index 9abaca38a..89b66cd45 100644 --- a/mrbgems/mruby-enumerator/mrblib/enumerator.rb +++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb @@ -153,12 +153,18 @@ class Enumerator # def with_index(offset=0) return to_enum :with_index, offset unless block_given? - raise TypeError, "no implicit conversion of #{offset.class} into Integer" unless offset.respond_to?(:to_int) + offset = if offset.nil? + 0 + elsif offset.respond_to?(:to_int) + offset.to_int + else + raise TypeError, "no implicit conversion of #{offset.class} into Integer" + end - n = offset.to_int - 1 - enumerator_block_call do |i| + n = offset - 1 + enumerator_block_call do |*i| n += 1 - yield [i,n] + yield i.__svalue, n end end @@ -516,6 +522,7 @@ class Enumerator # just for internal class Generator + include Enumerable def initialize(&block) raise TypeError, "wrong argument type #{self.class} (expected Proc)" unless block.kind_of? Proc diff --git a/mrbgems/mruby-enumerator/test/enumerator.rb b/mrbgems/mruby-enumerator/test/enumerator.rb index 2e45dae4b..e86e874f0 100644 --- a/mrbgems/mruby-enumerator/test/enumerator.rb +++ b/mrbgems/mruby-enumerator/test/enumerator.rb @@ -50,6 +50,9 @@ end assert 'Enumerator#with_index' do assert_equal([[1,0],[2,1],[3,2]], @obj.to_enum(:foo, 1, 2, 3).with_index.to_a) assert_equal([[1,5],[2,6],[3,7]], @obj.to_enum(:foo, 1, 2, 3).with_index(5).to_a) + a = [] + @obj.to_enum(:foo, 1, 2, 3).with_index(10).with_index(20) { |*i| a << i } + assert_equal [[[1, 10], 20], [[2, 11], 21], [[3, 12], 22]], a end assert 'Enumerator#with_index nonnum offset' do diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c index 26dd728ba..81bc80280 100644 --- a/mrbgems/mruby-eval/src/eval.c +++ b/mrbgems/mruby-eval/src/eval.c @@ -5,6 +5,9 @@ #include <mruby/proc.h> #include <mruby/opcode.h> +mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p); +mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self); + static struct mrb_irep * get_closure_irep(mrb_state *mrb, int level) { @@ -209,22 +212,15 @@ f_eval(mrb_state *mrb, mrb_value self) mrb_value binding = mrb_nil_value(); char *file = NULL; mrb_int line = 1; - mrb_value ret; struct RProc *proc; mrb_get_args(mrb, "s|ozi", &s, &len, &binding, &file, &line); proc = create_proc_from_string(mrb, s, len, binding, file, line); - ret = mrb_top_run(mrb, proc, mrb->c->stack[0], 0); - if (mrb->exc) { - mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); - } - - return ret; + mrb_assert(!MRB_PROC_CFUNC_P(proc)); + return mrb_exec_irep(mrb, self, proc); } -mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self); - #define CI_ACC_SKIP -1 static mrb_value @@ -250,7 +246,8 @@ f_instance_eval(mrb_state *mrb, mrb_value self) c->ci->target_class = mrb_class_ptr(cv); proc = create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line); mrb->c->ci->env = NULL; - return mrb_vm_run(mrb, proc, mrb->c->stack[0], 0); + mrb_assert(!MRB_PROC_CFUNC_P(proc)); + return mrb_exec_irep(mrb, self, proc); } else { mrb_get_args(mrb, "&", &b); diff --git a/mrbgems/mruby-inline-struct/mrbgem.rake b/mrbgems/mruby-inline-struct/mrbgem.rake new file mode 100644 index 000000000..91ad9f44b --- /dev/null +++ b/mrbgems/mruby-inline-struct/mrbgem.rake @@ -0,0 +1,5 @@ +MRuby::Gem::Specification.new('mruby-inline-struct') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'inline structure' +end diff --git a/mrbgems/mruby-inline-struct/test/inline.c b/mrbgems/mruby-inline-struct/test/inline.c new file mode 100644 index 000000000..0baaab617 --- /dev/null +++ b/mrbgems/mruby-inline-struct/test/inline.c @@ -0,0 +1,83 @@ +#include <mruby.h> +#include <mruby/class.h> +#include <mruby/string.h> +#include <mruby/istruct.h> + +static mrb_value +istruct_test_initialize(mrb_state *mrb, mrb_value self) +{ + char *string = (char*)mrb_istruct_ptr(self); + mrb_int size = mrb_istruct_size(); + mrb_value object; + mrb_get_args(mrb, "o", &object); + + if (mrb_float_p(object)) + { + snprintf(string, size, "float(%.3f)", mrb_float(object)); + } + else if (mrb_fixnum_p(object)) + { + snprintf(string, size, "fixnum(%" MRB_PRId ")", mrb_fixnum(object)); + } + else if (mrb_string_p(object)) + { + snprintf(string, size, "string(%s)", mrb_string_value_cstr(mrb, &object)); + } + + string[size - 1] = 0; // force NULL at the end + return self; +} + +static mrb_value +istruct_test_to_s(mrb_state *mrb, mrb_value self) +{ + return mrb_str_new_cstr(mrb, (const char*)mrb_istruct_ptr(self)); +} + +static mrb_value +istruct_test_length(mrb_state *mrb, mrb_value self) +{ + return mrb_fixnum_value(mrb_istruct_size()); +} + +static mrb_value +istruct_test_test_receive(mrb_state *mrb, mrb_value self) +{ + mrb_value object; + mrb_get_args(mrb, "o", &object); + if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest")) + { + mrb_raisef(mrb, E_TYPE_ERROR, "Expected InlineStructTest"); + } + return mrb_bool_value(((char*)mrb_istruct_ptr(object))[0] == 's'); +} + +static mrb_value +istruct_test_test_receive_direct(mrb_state *mrb, mrb_value self) +{ + char *ptr; + mrb_get_args(mrb, "I", &ptr); + return mrb_bool_value(ptr[0] == 's'); +} + +static mrb_value +istruct_test_mutate(mrb_state *mrb, mrb_value self) +{ + char *ptr = (char*)mrb_istruct_ptr(self); + memcpy(ptr, "mutate", 6); + return mrb_nil_value(); +} + +void mrb_mruby_inline_struct_gem_test(mrb_state *mrb) +{ + struct RClass *cls; + + cls = mrb_define_class(mrb, "InlineStructTest", mrb->object_class); + MRB_SET_INSTANCE_TT(cls, MRB_TT_ISTRUCT); + mrb_define_method(mrb, cls, "initialize", istruct_test_initialize, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, cls, "to_s", istruct_test_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, cls, "mutate", istruct_test_mutate, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, cls, "length", istruct_test_length, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, cls, "test_receive", istruct_test_test_receive, MRB_ARGS_REQ(1)); + mrb_define_class_method(mrb, cls, "test_receive_direct", istruct_test_test_receive_direct, MRB_ARGS_REQ(1)); +} diff --git a/mrbgems/mruby-inline-struct/test/inline.rb b/mrbgems/mruby-inline-struct/test/inline.rb new file mode 100644 index 000000000..495859232 --- /dev/null +++ b/mrbgems/mruby-inline-struct/test/inline.rb @@ -0,0 +1,151 @@ +## +# InlineStruct Test + +class InlineStructTest + def extra_method + :ok + end + + def test_ivar_set + @var = :ivar + end + + def test_ivar_get + @vat + end +end + +assert('InlineStructTest#dup') do + obj = InlineStructTest.new(1) + assert_equal obj.to_s, 'fixnum(1)' + assert_equal obj.dup.to_s, 'fixnum(1)' +end + +assert('InlineStructTest#clone') do + obj = InlineStructTest.new(1) + assert_equal obj.to_s, 'fixnum(1)' + assert_equal obj.clone.to_s, 'fixnum(1)' +end + +assert('InlineStruct#object_id') do + obj1 = InlineStructTest.new(1) + obj2 = InlineStructTest.new(1) + assert_not_equal obj1, obj2 + assert_not_equal obj1.object_id, obj2.object_id + assert_not_equal obj1.object_id, obj1.dup.object_id + assert_not_equal obj1.object_id, obj1.clone.object_id +end + +assert('InlineStructTest#mutate (dup)') do + obj1 = InlineStructTest.new("foo") + assert_equal obj1.to_s, "string(foo)" + obj2 = obj1.dup + assert_equal obj2.to_s, "string(foo)" + obj1.mutate + assert_equal obj1.to_s, "mutate(foo)" + assert_equal obj2.to_s, "string(foo)" +end + +assert('InlineStructTest#mutate (clone)') do + obj1 = InlineStructTest.new("foo") + assert_equal obj1.to_s, "string(foo)" + obj2 = obj1.clone + assert_equal obj2.to_s, "string(foo)" + obj1.mutate + assert_equal obj1.to_s, "mutate(foo)" + assert_equal obj2.to_s, "string(foo)" +end + +assert('InlineStructTest#test_receive(string)') do + assert_equal InlineStructTest.test_receive(InlineStructTest.new('a')), true +end + +assert('InlineStructTest#test_receive(float)') do + assert_equal InlineStructTest.test_receive(InlineStructTest.new(1.25)), false +end + +assert('InlineStructTest#test_receive(invalid object)') do + assert_raise(TypeError) do + InlineStructTest.test_receive([]) + end +end + +assert('InlineStructTest#test_receive(string)') do + assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new('a')), true +end + +assert('InlineStructTest#test_receive(float)') do + assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new(1.25)), false +end + +assert('InlineStructTest#test_receive(invalid object)') do + assert_raise(TypeError) do + InlineStructTest.test_receive_direct([]) + end +end + +assert('InlineStructTest#extra_method') do + assert_equal InlineStructTest.new(1).extra_method, :ok +end + +assert('InlineStructTest instance variable') do + obj = InlineStructTest.new(1) + assert_raise(ArgumentError) do + obj.test_ivar_set + end + assert_equal obj.test_ivar_get, nil +end + +# 64-bit mode +if InlineStructTest.length == 24 + assert('InlineStructTest length [64 bit]') do + assert_equal InlineStructTest.length, 3 * 8 + end + + assert('InlineStructTest w/float [64 bit]') do + obj = InlineStructTest.new(1.25) + assert_equal obj.to_s, "float(1.250)" + end + + assert('InlineStructTest w/fixnum [64 bit]') do + obj = InlineStructTest.new(42) + assert_equal obj.to_s, "fixnum(42)" + end + + assert('InlineStructTest w/string [64 bit]') do + obj = InlineStructTest.new("hello") + assert_equal obj.to_s, "string(hello)" + end + + assert('InlineStructTest w/long string [64 bit]') do + obj = InlineStructTest.new("this won't fit in 3 * 8 bytes available for the structure") + assert_equal obj.to_s, "string(this won't fit i" + end +end + +# 32-bit mode +if InlineStructTest.length == 12 + assert('InlineStructTest length [32 bit]') do + assert_equal InlineStructTest.length, 3 * 4 + end + + assert('InlineStructTest w/float [32 bit]') do + obj = InlineStructTest.new(1.25) + assert_equal obj.to_s, "float(1.250" + end + + assert('InlineStructTest w/fixnum [32 bit]') do + obj = InlineStructTest.new(42) + assert_equal obj.to_s, "fixnum(42)" + end + + assert('InlineStructTest w/string [32 bit]') do + obj = InlineStructTest.new("hello") + assert_equal obj.to_s, "string(hell" + end + + assert('InlineStructTest w/long string [32 bit]') do + obj = InlineStructTest.new("this won't fit in 3 * 4 bytes available for the structure") + assert_equal obj.to_s, "string(this" + end +end diff --git a/mrbgems/mruby-kernel-ext/src/kernel.c b/mrbgems/mruby-kernel-ext/src/kernel.c index d2153be4a..7e6fa28bd 100644 --- a/mrbgems/mruby-kernel-ext/src/kernel.c +++ b/mrbgems/mruby-kernel-ext/src/kernel.c @@ -2,6 +2,64 @@ #include <mruby/error.h> #include <mruby/array.h> #include <mruby/hash.h> +#include <mruby/range.h> + +static mrb_value +mrb_f_caller(mrb_state *mrb, mrb_value self) +{ + mrb_value bt, v, length; + mrb_int bt_len, argc, lev, n; + + bt = mrb_get_backtrace(mrb); + bt_len = RARRAY_LEN(bt); + argc = mrb_get_args(mrb, "|oo", &v, &length); + + switch (argc) { + case 0: + lev = 1; + n = bt_len - lev; + break; + case 1: + if (mrb_type(v) == MRB_TT_RANGE) { + mrb_int beg, len; + if (mrb_range_beg_len(mrb, v, &beg, &len, bt_len, TRUE) == 1) { + lev = beg; + n = len; + } + else { + return mrb_nil_value(); + } + } + else { + v = mrb_to_int(mrb, v); + lev = mrb_fixnum(v); + if (lev < 0) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v); + } + n = bt_len - lev; + } + break; + case 2: + lev = mrb_fixnum(mrb_to_int(mrb, v)); + n = mrb_fixnum(mrb_to_int(mrb, length)); + if (lev < 0) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative level (%S)", v); + } + if (n < 0) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative size (%S)", length); + } + break; + default: + lev = n = 0; + break; + } + + if (n == 0) { + return mrb_ary_new(mrb); + } + + return mrb_funcall(mrb, bt, "[]", 2, mrb_fixnum_value(lev), mrb_fixnum_value(n)); +} /* * call-seq: @@ -170,6 +228,7 @@ mrb_mruby_kernel_ext_gem_init(mrb_state *mrb) struct RClass *krn = mrb->kernel_module; mrb_define_module_function(mrb, krn, "fail", mrb_f_raise, MRB_ARGS_OPT(2)); + mrb_define_module_function(mrb, krn, "caller", mrb_f_caller, MRB_ARGS_OPT(2)); mrb_define_method(mrb, krn, "__method__", mrb_f_method, MRB_ARGS_NONE()); mrb_define_module_function(mrb, krn, "Integer", mrb_f_integer, MRB_ARGS_ANY()); mrb_define_module_function(mrb, krn, "Float", mrb_f_float, MRB_ARGS_REQ(1)); diff --git a/mrbgems/mruby-kernel-ext/test/kernel.rb b/mrbgems/mruby-kernel-ext/test/kernel.rb index cc6af13a3..dd7cea86a 100644 --- a/mrbgems/mruby-kernel-ext/test/kernel.rb +++ b/mrbgems/mruby-kernel-ext/test/kernel.rb @@ -3,6 +3,35 @@ assert('Kernel.fail, Kernel#fail') do assert_raise(RuntimeError) { Kernel.fail } end +assert('Kernel.caller, Kernel#caller') do + skip "backtrace isn't available" if caller(0).empty? + + c = Class.new do + def foo(*args) + caller(*args) + end + + def bar(*args) + foo(*args) + end + + def baz(*args) + bar(*args) + end + end + assert_equal "#bar", c.new.baz[0][-4..-1] + assert_equal "#foo", c.new.baz(0)[0][-4..-1] + assert_equal "#bar", c.new.baz(1)[0][-4..-1] + assert_equal "#baz", c.new.baz(2)[0][-4..-1] + assert_equal ["#foo", "#bar"], c.new.baz(0, 2).map { |i| i[-4..-1] } + assert_equal ["#bar", "#baz"], c.new.baz(1..2).map { |i| i[-4..-1] } + assert_nil c.new.baz(10..20) + assert_raise(ArgumentError) { c.new.baz(-1) } + assert_raise(ArgumentError) { c.new.baz(-1, 1) } + assert_raise(ArgumentError) { c.new.baz(1, -1) } + assert_raise(TypeError) { c.new.baz(nil) } +end + assert('Kernel#__method__') do assert_equal(:m, Class.new {def m; __method__; end}.new.m) assert_equal(:m, Class.new {define_method(:m) {__method__}}.new.m) diff --git a/mrbgems/mruby-object-ext/src/object.c b/mrbgems/mruby-object-ext/src/object.c index 3eba95633..e4fe328e9 100644 --- a/mrbgems/mruby-object-ext/src/object.c +++ b/mrbgems/mruby-object-ext/src/object.c @@ -96,7 +96,7 @@ mrb_mruby_object_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, n, "to_f", nil_to_f, MRB_ARGS_NONE()); mrb_define_method(mrb, n, "to_i", nil_to_i, MRB_ARGS_NONE()); - mrb_define_method(mrb, mrb->object_class, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK()); + mrb_define_method(mrb, mrb->kernel_module, "instance_exec", mrb_obj_instance_exec, MRB_ARGS_ANY() | MRB_ARGS_BLOCK()); } void diff --git a/mrbgems/mruby-object-ext/test/object.rb b/mrbgems/mruby-object-ext/test/object.rb index fe56f1ec5..f0742f8ce 100644 --- a/mrbgems/mruby-object-ext/test/object.rb +++ b/mrbgems/mruby-object-ext/test/object.rb @@ -23,3 +23,31 @@ assert('Object#tap') do ], ret assert_equal(:tap_ok, Class.new {def m; tap{return :tap_ok}; end}.new.m) end + +assert('instance_exec on primitives with class and module definition') do + begin + class A + 1.instance_exec do + class B + end + end + end + + assert_kind_of Class, A::B + ensure + Object.remove_const :A + end + + begin + class A + 1.instance_exec do + module B + end + end + end + + assert_kind_of Module, A::B + ensure + Object.remove_const :A + end +end diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index d5cd4f5a1..d0a8effd0 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -49,7 +49,7 @@ static mrb_value os_count_objects(mrb_state *mrb, mrb_value self) { struct os_count_struct obj_count = { 0 }; - enum mrb_vtype i; + mrb_int i; mrb_value hash; if (mrb_get_args(mrb, "|H", &hash) == 0) { diff --git a/mrbgems/mruby-print/src/print.c b/mrbgems/mruby-print/src/print.c index da5f36789..fd8d15241 100644 --- a/mrbgems/mruby-print/src/print.c +++ b/mrbgems/mruby-print/src/print.c @@ -22,7 +22,7 @@ printstr(mrb_state *mrb, mrb_value obj) int mlen = RSTRING_LEN(obj); char* utf8 = RSTRING_PTR(obj); int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, NULL, 0); - wchar_t* utf16 = mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t)); + wchar_t* utf16 = (wchar_t*)mrb_malloc(mrb, (wlen+1) * sizeof(wchar_t)); if (utf16 == NULL) return; if (MultiByteToWideChar(CP_UTF8, 0, utf8, mlen, utf16, wlen) > 0) { utf16[wlen] = 0; diff --git a/mrbgems/mruby-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c index c8c8f1aa1..34f6230dc 100644 --- a/mrbgems/mruby-proc-ext/src/proc.c +++ b/mrbgems/mruby-proc-ext/src/proc.c @@ -114,6 +114,9 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self) // TODO cfunc aspec is not implemented yet return mrb_ary_new(mrb); } + if (!irep) { + return mrb_ary_new(mrb); + } if (!irep->lv) { return mrb_ary_new(mrb); } diff --git a/mrbgems/mruby-random/src/random.c b/mrbgems/mruby-random/src/random.c index 3b8d0dd2f..f81d38fe6 100644 --- a/mrbgems/mruby-random/src/random.c +++ b/mrbgems/mruby-random/src/random.c @@ -124,6 +124,8 @@ mrb_random_init(mrb_state *mrb, mrb_value self) mrb_value seed; mt_state *t; + seed = get_opt(mrb); + /* avoid memory leaks */ t = (mt_state*)DATA_PTR(self); if (t) { @@ -134,7 +136,6 @@ mrb_random_init(mrb_state *mrb, mrb_value self) t = (mt_state *)mrb_malloc(mrb, sizeof(mt_state)); t->mti = N + 1; - seed = get_opt(mrb); seed = mrb_random_mt_srand(mrb, t, seed); if (mrb_nil_p(seed)) { t->has_seed = FALSE; @@ -266,7 +267,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary) mrb_int n = 0; mrb_bool given; mt_state *random = NULL; - mrb_int len = RARRAY_LEN(ary); + mrb_int len; mrb_get_args(mrb, "|i?d", &n, &given, &random, &mt_state_type); if (random == NULL) { @@ -274,6 +275,7 @@ mrb_ary_sample(mrb_state *mrb, mrb_value ary) } mrb_random_rand_seed(mrb, random); mt_rand(random); + len = RARRAY_LEN(ary); if (!given) { /* pick one element */ switch (len) { case 0: diff --git a/mrbgems/mruby-random/test/random.rb b/mrbgems/mruby-random/test/random.rb index 1653ae4a6..1c59be3a6 100644 --- a/mrbgems/mruby-random/test/random.rb +++ b/mrbgems/mruby-random/test/random.rb @@ -74,3 +74,15 @@ assert('Array#shuffle!(random)') do ary1 != [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] and 10.times { |x| ary1.include? x } and ary1 == ary2 end + +assert('Array#sample checks input length after reading arguments') do + $ary = [1, 2, 3] + class ArrayChange + def to_i + $ary << 4 + 4 + end + end + + assert_equal [1, 2, 3, 4], $ary.sample(ArrayChange.new).sort +end diff --git a/mrbgems/mruby-range-ext/mrblib/range.rb b/mrbgems/mruby-range-ext/mrblib/range.rb new file mode 100644 index 000000000..e5d1fb079 --- /dev/null +++ b/mrbgems/mruby-range-ext/mrblib/range.rb @@ -0,0 +1,31 @@ +class Range + ## + # call-seq: + # rng.first -> obj + # rng.first(n) -> an_array + # + # Returns the first object in the range, or an array of the first +n+ + # elements. + # + # (10..20).first #=> 10 + # (10..20).first(3) #=> [10, 11, 12] + # + def first(*args) + return self.begin if args.empty? + + raise ArgumentError, "wrong number of arguments (given #{args.length}, expected 1)" unless args.length == 1 + nv = args[0] + raise TypeError, "no implicit conversion from nil to integer" if nv.nil? + raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless nv.respond_to?(:to_int) + n = nv.to_int + raise TypeError, "no implicit conversion of #{nv.class} into Integer" unless n.kind_of?(Integer) + raise ArgumentError, "negative array size (or size too big)" unless 0 <= n + ary = [] + each do |i| + break if n <= 0 + ary.push(i) + n -= 1 + end + ary + end +end diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c index 4d6f1684f..3131192ff 100644 --- a/mrbgems/mruby-range-ext/src/range.c +++ b/mrbgems/mruby-range-ext/src/range.c @@ -1,6 +1,7 @@ #include <mruby.h> #include <mruby/range.h> #include <math.h> +#include <float.h> static mrb_bool r_le(mrb_state *mrb, mrb_value a, mrb_value b) @@ -43,7 +44,7 @@ static mrb_value mrb_range_cover(mrb_state *mrb, mrb_value range) { mrb_value val; - struct RRange *r = mrb_range_ptr(range); + struct RRange *r = mrb_range_ptr(mrb, range); mrb_value beg, end; mrb_get_args(mrb, "o", &val); @@ -67,32 +68,6 @@ mrb_range_cover(mrb_state *mrb, mrb_value range) /* * call-seq: - * rng.first -> obj - * rng.first(n) -> an_array - * - * Returns the first object in the range, or an array of the first +n+ - * elements. - * - * (10..20).first #=> 10 - * (10..20).first(3) #=> [10, 11, 12] - */ -static mrb_value -mrb_range_first(mrb_state *mrb, mrb_value range) -{ - mrb_int num; - mrb_value array; - struct RRange *r = mrb_range_ptr(range); - - if (mrb_get_args(mrb, "|i", &num) == 0) { - return r->edges->beg; - } - - array = mrb_funcall(mrb, range, "to_a", 0); - return mrb_funcall(mrb, array, "first", 1, mrb_fixnum_value(num)); -} - -/* - * call-seq: * rng.last -> obj * rng.last(n) -> an_array * @@ -112,7 +87,7 @@ mrb_range_last(mrb_state *mrb, mrb_value range) { mrb_value num; mrb_value array; - struct RRange *r = mrb_range_ptr(range); + struct RRange *r = mrb_range_ptr(mrb, range); if (mrb_get_args(mrb, "|o", &num) == 0) { return r->edges->end; @@ -136,15 +111,17 @@ mrb_range_last(mrb_state *mrb, mrb_value range) static mrb_value mrb_range_size(mrb_state *mrb, mrb_value range) { - struct RRange *r = mrb_range_ptr(range); + struct RRange *r = mrb_range_ptr(mrb, range); mrb_value beg, end; - double beg_f, end_f; + mrb_float beg_f, end_f; mrb_bool num_p = TRUE; + mrb_bool excl; beg = r->edges->beg; end = r->edges->end; + excl = r->excl; if (mrb_fixnum_p(beg)) { - beg_f = (double)mrb_fixnum(beg); + beg_f = (mrb_float)mrb_fixnum(beg); } else if (mrb_float_p(beg)) { beg_f = mrb_float(beg); @@ -153,7 +130,7 @@ mrb_range_size(mrb_state *mrb, mrb_value range) num_p = FALSE; } if (mrb_fixnum_p(end)) { - end_f = (double)mrb_fixnum(end); + end_f = (mrb_float)mrb_fixnum(end); } else if (mrb_float_p(end)) { end_f = mrb_float(end); @@ -162,14 +139,24 @@ mrb_range_size(mrb_state *mrb, mrb_value range) num_p = FALSE; } if (num_p) { - double f; - - if (beg_f > end_f) return mrb_fixnum_value(0); - f = end_f - beg_f; - if (!r->excl) { - return mrb_fixnum_value((mrb_int)ceil(f + 1)); + mrb_float n = end_f - beg_f; + mrb_float err = (fabs(beg_f) + fabs(end_f) + fabs(end_f-beg_f)) * MRB_FLOAT_EPSILON; + + if (err>0.5) err=0.5; + if (excl) { + if (n<=0) return mrb_fixnum_value(0); + if (n<1) + n = 0; + else + n = floor(n - err); + } + else { + if (n<0) return mrb_fixnum_value(0); + n = floor(n + err); } - return mrb_fixnum_value((mrb_int)ceil(f)); + if (isinf(n+1)) + return mrb_float_value(mrb, INFINITY); + return mrb_fixnum_value((mrb_int)n+1); } return mrb_nil_value(); } @@ -180,7 +167,6 @@ mrb_mruby_range_ext_gem_init(mrb_state* mrb) struct RClass * s = mrb_class_get(mrb, "Range"); mrb_define_method(mrb, s, "cover?", mrb_range_cover, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, s, "first", mrb_range_first, MRB_ARGS_OPT(1)); mrb_define_method(mrb, s, "last", mrb_range_last, MRB_ARGS_OPT(1)); mrb_define_method(mrb, s, "size", mrb_range_size, MRB_ARGS_NONE()); } diff --git a/mrbgems/mruby-range-ext/test/range.rb b/mrbgems/mruby-range-ext/test/range.rb index 18e7dafe4..efcbdabe4 100644 --- a/mrbgems/mruby-range-ext/test/range.rb +++ b/mrbgems/mruby-range-ext/test/range.rb @@ -10,6 +10,7 @@ end assert('Range#first') do assert_equal 10, (10..20).first assert_equal [10, 11, 12], (10..20).first(3) + assert_equal [0, 1, 2], (0..Float::INFINITY).first(3) end assert('Range#last') do @@ -25,5 +26,7 @@ assert('Range#size') do assert_equal 6, (1...6.3).size assert_equal 5, (1...6.0).size assert_equal 5, (1.1...6).size + assert_equal 15, (1.0..15.9).size + assert_equal Float::INFINITY, (0..Float::INFINITY).size assert_nil ('a'..'z').size end diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index 8d14b0fc5..d02a2aa4d 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -116,8 +116,9 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base) #define CHECK(l) do {\ /* int cr = ENC_CODERANGE(result);*/\ - while (blen + (l) >= bsiz) {\ + while ((l) >= bsiz - blen) {\ bsiz*=2;\ + if (bsiz < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \ }\ mrb_str_resize(mrb, result, bsiz);\ /* ENC_CODERANGE_SET(result, cr);*/\ @@ -136,29 +137,63 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base) blen += (l);\ } while (0) -#define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : \ - posarg == -1 ? \ - (mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg)), mrb_undef_value()) : \ - posarg == -2 ? \ - (mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg)), mrb_undef_value()) : \ +static void +check_next_arg(mrb_state *mrb, int posarg, int nextarg) +{ + switch (posarg) { + case -1: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with numbered", mrb_fixnum_value(nextarg)); + break; + case -2: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "unnumbered(%S) mixed with named", mrb_fixnum_value(nextarg)); + break; + default: + break; + } +} + +static void +check_pos_arg(mrb_state *mrb, int posarg, int n) +{ + if (posarg > 0) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)", + mrb_fixnum_value(n), mrb_fixnum_value(posarg)); + } + if (posarg == -2) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n)); + } + if (n < 1) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n)); + } +} + +static void +check_name_arg(mrb_state *mrb, int posarg, const char *name, int len) +{ + if (posarg > 0) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)", + mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg)); + } + if (posarg == -1) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len))); + } +} + +#define GETNEXTARG() (\ + check_next_arg(mrb, posarg, nextarg),\ (posarg = nextarg++, GETNTHARG(posarg))) -#define GETPOSARG(n) (posarg > 0 ? \ - (mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after unnumbered(%S)", mrb_fixnum_value(n), mrb_fixnum_value(posarg)), mrb_undef_value()) : \ - posarg == -2 ? \ - (mrb_raisef(mrb, E_ARGUMENT_ERROR, "numbered(%S) after named", mrb_fixnum_value(n)), mrb_undef_value()) : \ - ((n < 1) ? \ - (mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid index - %S$", mrb_fixnum_value(n)), mrb_undef_value()) : \ - (posarg = -1, GETNTHARG(n)))) +#define GETARG() (!mrb_undef_p(nextvalue) ? nextvalue : GETNEXTARG()) + +#define GETPOSARG(n) (\ + check_pos_arg(mrb, posarg, n),\ + (posarg = -1, GETNTHARG(n))) #define GETNTHARG(nth) \ ((nth >= argc) ? (mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"), mrb_undef_value()) : argv[nth]) -#define GETNAMEARG(id, name, len) ( \ - posarg > 0 ? \ - (mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after unnumbered(%S)", mrb_str_new(mrb, (name), (len)), mrb_fixnum_value(posarg)), mrb_undef_value()) : \ - posarg == -1 ? \ - (mrb_raisef(mrb, E_ARGUMENT_ERROR, "named%S after numbered", mrb_str_new(mrb, (name), (len))), mrb_undef_value()) : \ +#define GETNAMEARG(id, name, len) (\ + check_name_arg(mrb, posarg, name, len),\ (posarg = -2, mrb_hash_fetch(mrb, get_hash(mrb, &hash, argc, argv), id, mrb_undef_value()))) #define GETNUM(n, val) \ @@ -182,7 +217,7 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base) tmp_v = GETPOSARG(n); \ } \ else { \ - tmp_v = GETARG(); \ + tmp_v = GETNEXTARG(); \ p = t; \ } \ num = mrb_fixnum(tmp_v); \ @@ -675,6 +710,7 @@ retry: else { mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid character"); } + mrb_check_type(mrb, tmp, MRB_TT_STRING); c = RSTRING_PTR(tmp); n = RSTRING_LEN(tmp); if (!(flags & FWIDTH)) { @@ -686,10 +722,10 @@ retry: CHECK(n); memcpy(buf+blen, c, n); blen += n; - FILL(' ', width-1); + if (width>0) FILL(' ', width-1); } else { - FILL(' ', width-1); + if (width>0) FILL(' ', width-1); CHECK(n); memcpy(buf+blen, c, n); blen += n; @@ -843,20 +879,20 @@ retry: strncpy(nbuf, RSTRING_PTR(val), sizeof(nbuf)); break; case 8: - snprintf(nbuf, sizeof(nbuf), "%"MRB_PRIo, v); + snprintf(nbuf, sizeof(nbuf), "%" MRB_PRIo, v); break; case 10: - snprintf(nbuf, sizeof(nbuf), "%"MRB_PRId, v); + snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v); break; case 16: - snprintf(nbuf, sizeof(nbuf), "%"MRB_PRIx, v); + snprintf(nbuf, sizeof(nbuf), "%" MRB_PRIx, v); break; } s = nbuf; } else { s = nbuf; - if (v < 0) { + if (base != 10 && v < 0) { dots = 1; } switch (base) { @@ -864,13 +900,13 @@ retry: strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1); break; case 8: - snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRIo, v); + snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v); break; case 10: - snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRId, v); + snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRId, v); break; case 16: - snprintf(++s, sizeof(nbuf)-1, "%"MRB_PRIx, v); + snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v); break; } if (v < 0) { diff --git a/mrbgems/mruby-sprintf/test/sprintf.rb b/mrbgems/mruby-sprintf/test/sprintf.rb index 454c226e1..ccbd95d51 100644 --- a/mrbgems/mruby-sprintf/test/sprintf.rb +++ b/mrbgems/mruby-sprintf/test/sprintf.rb @@ -5,5 +5,28 @@ assert('String#%') do assert_equal "one=1", "one=%d" % 1 assert_equal "1 one 1.0", "%d %s %3.1f" % [ 1, "one", 1.01 ] assert_equal "123 < 456", "%{num} < %<str>s" % { num: 123, str: "456" } - assert_equal 16, ("%b" % (1<<15)).size + assert_equal 15, ("%b" % (1<<14)).size +end + +assert("String#% with invalid chr") do + begin + class Fixnum + alias_method :chr_, :chr if method_defined?(:chr) + + def chr + nil + end + end + + assert_raise TypeError do + "%c" % 0 + end + ensure + class Fixnum + if method_defined?(:chr_) + alias_method :chr, :chr_ + remove_method :chr_ + end + end + end end diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb index c3b765a5f..8895b7ad3 100644 --- a/mrbgems/mruby-string-ext/mrblib/string.rb +++ b/mrbgems/mruby-string-ext/mrblib/string.rb @@ -45,7 +45,7 @@ class String def lstrip a = 0 z = self.size - 1 - a += 1 while " \f\n\r\t\v".include?(self[a]) and a <= z + a += 1 while a <= z and " \f\n\r\t\v".include?(self[a]) (z >= 0) ? self[a..z] : "" end @@ -62,7 +62,7 @@ class String def rstrip a = 0 z = self.size - 1 - z -= 1 while " \f\n\r\t\v\0".include?(self[z]) and a <= z + z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z]) (z >= 0) ? self[a..z] : "" end @@ -78,8 +78,8 @@ class String def strip a = 0 z = self.size - 1 - a += 1 while " \f\n\r\t\v".include?(self[a]) and a <= z - z -= 1 while " \f\n\r\t\v\0".include?(self[z]) and a <= z + a += 1 while a <= z and " \f\n\r\t\v".include?(self[a]) + z -= 1 while a <= z and " \f\n\r\t\v\0".include?(self[z]) (z >= 0) ? self[a..z] : "" end @@ -275,15 +275,11 @@ class String # "hello".ljust(20) #=> "hello " # "hello".ljust(20, '1234') #=> "hello123412341234123" def ljust(idx, padstr = ' ') - if idx <= self.size - return self - end - newstr = self.dup - newstr << padstr - while newstr.size <= idx - newstr << padstr - end - return newstr.slice(0,idx) + raise ArgumentError, 'zero width padding' if padstr == '' + return self if idx <= self.size + pad_repetitions = (idx / padstr.length).ceil + padding = (padstr * pad_repetitions)[0...(idx - self.length)] + self + padding end ## @@ -298,15 +294,11 @@ class String # "hello".rjust(20) #=> " hello" # "hello".rjust(20, '1234') #=> "123412341234123hello" def rjust(idx, padstr = ' ') - if idx <= self.size - return self - end - padsize = idx - self.size - newstr = padstr.dup - while newstr.size <= padsize - newstr << padstr - end - return newstr.slice(0,padsize) + self + raise ArgumentError, 'zero width padding' if padstr == '' + return self if idx <= self.size + pad_repetitions = (idx / padstr.length).ceil + padding = (padstr * pad_repetitions)[0...(idx - self.length)] + padding + self end # str.upto(other_str, exclusive=false) {|s| block } -> str @@ -385,4 +377,18 @@ class String end end alias each_codepoint codepoints + + ## + # call-seq: + # str.prepend(other_str) -> str + # + # Prepend---Prepend the given string to <i>str</i>. + # + # a = "world" + # a.prepend("hello ") #=> "hello world" + # a #=> "hello world" + def prepend(arg) + self[0, 0] = arg + self + end end diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 2a52d53b3..7e87b3db4 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -23,10 +23,11 @@ static mrb_value mrb_str_setbyte(mrb_state *mrb, mrb_value str) { mrb_int pos, byte; - long len = RSTRING_LEN(str); + long len; mrb_get_args(mrb, "ii", &pos, &byte); + len = RSTRING_LEN(str); if (pos < -len || len <= pos) mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos)); if (pos < 0) @@ -55,8 +56,14 @@ mrb_str_byteslice(mrb_state *mrb, mrb_value str) mrb_int beg; len = RSTRING_LEN(str); - if (mrb_range_beg_len(mrb, a1, &beg, &len, len)) { + switch (mrb_range_beg_len(mrb, a1, &beg, &len, len, TRUE)) { + case 0: /* not range */ + break; + case 1: /* range */ return mrb_str_substr(mrb, str, beg, len); + case 2: /* out of range */ + mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", a1); + break; } return mrb_nil_value(); } @@ -307,13 +314,14 @@ mrb_str_lines(mrb_state *mrb, mrb_value self) int ai; mrb_int len; mrb_value arg; - char *p = RSTRING_PTR(self), *t; - char *e = p + RSTRING_LEN(self); + char *b = RSTRING_PTR(self); + char *p = b, *t; + char *e = b + RSTRING_LEN(self); mrb_get_args(mrb, "&", &blk); result = mrb_ary_new(mrb); - + ai = mrb_gc_arena_save(mrb); if (!mrb_nil_p(blk)) { while (p < e) { t = p; @@ -322,11 +330,17 @@ mrb_str_lines(mrb_state *mrb, mrb_value self) len = (mrb_int) (p - t); arg = mrb_str_new(mrb, t, len); mrb_yield_argv(mrb, blk, 1, &arg); + mrb_gc_arena_restore(mrb, ai); + if (b != RSTRING_PTR(self)) { + ptrdiff_t diff = p - b; + b = RSTRING_PTR(self); + p = b + diff; + } + e = b + RSTRING_LEN(self); } return self; } while (p < e) { - ai = mrb_gc_arena_save(mrb); t = p; while (p < e && *p != '\n') p++; if (*p == '\n') p++; @@ -429,44 +443,6 @@ mrb_str_succ(mrb_state *mrb, mrb_value self) return str; } -/* - * call-seq: - * str.prepend(other_str) -> str - * - * Prepend---Prepend the given string to <i>str</i>. - * - * a = "world" - * a.prepend("hello ") #=> "hello world" - * a #=> "hello world" - */ -static mrb_value -mrb_str_prepend(mrb_state *mrb, mrb_value self) -{ - struct RString *s1 = mrb_str_ptr(self), *s2, *temp_s; - mrb_int len; - mrb_value other, temp_str; - - mrb_get_args(mrb, "S", &other); - - mrb_str_modify(mrb, s1); - if (!mrb_string_p(other)) { - other = mrb_str_to_str(mrb, other); - } - s2 = mrb_str_ptr(other); - len = RSTR_LEN(s1) + RSTR_LEN(s2); - temp_str = mrb_str_new(mrb, NULL, RSTR_LEN(s1)); - temp_s = mrb_str_ptr(temp_str); - memcpy(RSTR_PTR(temp_s), RSTR_PTR(s1), RSTR_LEN(s1)); - if (RSTRING_CAPA(self) < len) { - mrb_str_resize(mrb, self, len); - } - memcpy(RSTR_PTR(s1), RSTR_PTR(s2), RSTR_LEN(s2)); - memcpy(RSTR_PTR(s1) + RSTR_LEN(s2), RSTR_PTR(temp_s), RSTR_LEN(temp_s)); - RSTR_SET_LEN(s1, len); - RSTR_PTR(s1)[len] = '\0'; - return self; -} - #ifdef MRB_UTF8_STRING static const char utf8len_codepage_zero[256] = { @@ -554,7 +530,6 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE()); - mrb_define_method(mrb, s, "prepend", mrb_str_prepend, MRB_ARGS_REQ(1)); mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ")); mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!")); mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE()); diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb index a5d55a7ee..24bc859d8 100644 --- a/mrbgems/mruby-string-ext/test/string.rb +++ b/mrbgems/mruby-string-ext/test/string.rb @@ -30,6 +30,18 @@ assert('String#setbyte') do assert_equal("Hello", str1) end +assert("String#setbyte raises IndexError if arg conversion resizes String") do + $s = "01234\n" + class Tmp + def to_i + $s.chomp! '' + 95 + end + end + tmp = Tmp.new + assert_raise(IndexError) { $s.setbyte(5, tmp) } +end + assert('String#byteslice') do str1 = "hello" assert_equal("e", str1.byteslice(1)) @@ -421,6 +433,7 @@ end assert('String#ljust') do assert_equal "hello", "hello".ljust(4) assert_equal "hello ", "hello".ljust(20) + assert_equal 20, "hello".ljust(20).length assert_equal "hello123412341234123", "hello".ljust(20, '1234') assert_equal "hello", "hello".ljust(-3) end @@ -428,10 +441,55 @@ end assert('String#rjust') do assert_equal "hello", "hello".rjust(4) assert_equal " hello", "hello".rjust(20) + assert_equal 20, "hello".rjust(20).length assert_equal "123412341234123hello", "hello".rjust(20, '1234') assert_equal "hello", "hello".rjust(-3) end +if UTF8STRING + assert('String#ljust with UTF8') do + assert_equal "helloん ", "helloん".ljust(20) + assert_equal "helloó ", "helloó".ljust(34) + assert_equal 34, "helloó".ljust(34).length + assert_equal "helloんんんんんんんんんんんんんん", "hello".ljust(19, 'ん') + assert_equal "helloんんんんんんんんんんんんんんん", "hello".ljust(20, 'ん') + end + + assert('String#rjust with UTF8') do + assert_equal " helloん", "helloん".rjust(20) + assert_equal " helloó", "helloó".rjust(34) + # assert_equal 34, "helloó".rjust(34).length + assert_equal "んんんんんんんんんんんんんんhello", "hello".rjust(19, 'ん') + assert_equal "んんんんんんんんんんんんんんんhello", "hello".rjust(20, 'ん') + end + + assert('UTF8 byte counting') do + ret = ' ' + ret[-6..-1] = "helloó" + assert_equal 34, ret.length + end +end + +assert('String#ljust should not change string') do + a = "hello" + a.ljust(20) + assert_equal "hello", a +end + +assert('String#rjust should not change string') do + a = "hello" + a.rjust(20) + assert_equal "hello", a +end + +assert('String#ljust should raise on zero width padding') do + assert_raise(ArgumentError) { "foo".ljust(10, '') } +end + +assert('String#rjust should raise on zero width padding') do + assert_raise(ArgumentError) { "foo".rjust(10, '') } +end + assert('String#upto') do a = "aa" start = "aa" diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c index 1a06e0c12..93bd1e2b2 100644 --- a/mrbgems/mruby-struct/src/struct.c +++ b/mrbgems/mruby-struct/src/struct.c @@ -203,7 +203,7 @@ make_struct(mrb_state *mrb, mrb_value name, mrb_value members, struct RClass * k } if (mrb_const_defined_at(mrb, mrb_obj_value(klass), id)) { mrb_warn(mrb, "redefining constant Struct::%S", name); - /* ?rb_mod_remove_const(klass, mrb_sym2name(mrb, id)); */ + mrb_const_remove(mrb, mrb_obj_value(klass), id); } c = mrb_define_class_under(mrb, klass, RSTRING_PTR(name), klass); } @@ -273,31 +273,21 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass) } else { if (argc > 0) name = argv[0]; - if (argc > 1) rest = argv[1]; - if (mrb_array_p(rest)) { - if (!mrb_nil_p(name) && mrb_symbol_p(name)) { - /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */ - mrb_ary_unshift(mrb, rest, name); - name = mrb_nil_value(); - } - } - else { - pargv = &argv[1]; - argcnt = argc-1; - if (!mrb_nil_p(name) && mrb_symbol_p(name)) { - /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */ - name = mrb_nil_value(); - pargv = &argv[0]; - argcnt++; - } - rest = mrb_ary_new_from_values(mrb, argcnt, pargv); + pargv = &argv[1]; + argcnt = argc-1; + if (!mrb_nil_p(name) && mrb_symbol_p(name)) { + /* 1stArgument:symbol -> name=nil rest=argv[0]-[n] */ + name = mrb_nil_value(); + pargv = &argv[0]; + argcnt++; } + rest = mrb_ary_new_from_values(mrb, argcnt, pargv); for (i=0; i<RARRAY_LEN(rest); i++) { id = mrb_obj_to_sym(mrb, RARRAY_PTR(rest)[i]); mrb_ary_set(mrb, rest, i, mrb_symbol_value(id)); } } - st = make_struct(mrb, name, rest, struct_class(mrb)); + st = make_struct(mrb, name, rest, mrb_class_ptr(klass)); if (!mrb_nil_p(b)) { mrb_yield_with_class(mrb, b, 1, &st, st, mrb_class_ptr(st)); } @@ -703,7 +693,7 @@ mrb_mruby_struct_gem_init(mrb_state* mrb) mrb_define_method(mrb, st, "to_a", mrb_struct_to_a, MRB_ARGS_NONE()); mrb_define_method(mrb, st, "values", mrb_struct_to_a, MRB_ARGS_NONE()); mrb_define_method(mrb, st, "to_h", mrb_struct_to_h, MRB_ARGS_NONE()); - mrb_define_method(mrb, st, "values_at", mrb_struct_values_at, MRB_ARGS_NONE()); + mrb_define_method(mrb, st, "values_at", mrb_struct_values_at, MRB_ARGS_ANY()); } void diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb index 02ecf69e4..1c0939e7f 100644 --- a/mrbgems/mruby-struct/test/struct.rb +++ b/mrbgems/mruby-struct/test/struct.rb @@ -158,3 +158,42 @@ assert("Struct#dig") do assert_equal 1, a.dig(:purple, :red) 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 + +assert("Struct#initialize_copy requires struct to be the same type") do + begin + Struct.new("Test", :a) + a = Struct::Test.new("a") + Struct.remove_const :Test + Struct.new("Test", :a, :b) + assert_raise(TypeError) do + a.initialize_copy(Struct::Test.new("a", "b")) + end + ensure + Struct.remove_const :Test + end +end + +assert("Struct.new does not allow array") do + assert_raise(TypeError) do + Struct.new("Test", [:a]) + end +end + +assert("Struct.new generates subclass of Struct") do + begin + original_struct = Struct + Struct = String + assert_equal original_struct, original_struct.new.superclass + ensure + Struct = original_struct + end +end diff --git a/mrbgems/mruby-test/driver.c b/mrbgems/mruby-test/driver.c index 7cc5211f0..14e93ff33 100644 --- a/mrbgems/mruby-test/driver.c +++ b/mrbgems/mruby-test/driver.c @@ -94,6 +94,12 @@ mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose) mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN)); mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT)); +#ifdef MRB_USE_FLOAT + mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-6)); +#else + mrb_define_const(mrb, mrbtest, "FLOAT_TOLERANCE", mrb_float_value(mrb, 1e-12)); +#endif + if (verbose) { mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value()); } diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c index 19ce32832..43d87e5ff 100644 --- a/mrbgems/mruby-time/src/time.c +++ b/mrbgems/mruby-time/src/time.c @@ -183,7 +183,7 @@ static const struct mrb_data_type mrb_time_type = { "Time", mrb_free }; /** Updates the datetime of a mrb_time based on it's timezone and seconds setting. Returns self on success, NULL of failure. */ static struct mrb_time* -mrb_time_update_datetime(struct mrb_time *self) +time_update_datetime(mrb_state *mrb, struct mrb_time *self) { struct tm *aid; @@ -193,7 +193,11 @@ mrb_time_update_datetime(struct mrb_time *self) else { aid = localtime_r(&self->sec, &self->datetime); } - if (!aid) return NULL; + if (!aid) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S out of Time range", mrb_float_value(mrb, (mrb_float)self->sec)); + /* not reached */ + return NULL; + } #ifdef NO_GMTIME_R self->datetime = *aid; /* copy data */ #endif @@ -213,7 +217,7 @@ static struct mrb_time* time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone) { struct mrb_time *tm; - time_t tsec; + time_t tsec = 0; if (sizeof(time_t) == 4 && (sec > (double)INT32_MAX || (double)INT32_MIN > sec)) { goto out_of_range; @@ -238,7 +242,7 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone) tm->usec -= 1000000; } tm->timezone = timezone; - mrb_time_update_datetime(tm); + time_update_datetime(mrb, tm); return tm; } @@ -290,7 +294,7 @@ current_mrb_time(mrb_state *mrb) } #endif tm->timezone = MRB_TIMEZONE_LOCAL; - mrb_time_update_datetime(tm); + time_update_datetime(mrb, tm); return tm; } @@ -328,6 +332,15 @@ time_mktime(mrb_state *mrb, mrb_int ayear, mrb_int amonth, mrb_int aday, nowtime.tm_min = (int)amin; nowtime.tm_sec = (int)asec; nowtime.tm_isdst = -1; + + if (nowtime.tm_mon < 0 || nowtime.tm_mon > 11 + || nowtime.tm_mday < 1 || nowtime.tm_mday > 31 + || nowtime.tm_hour < 0 || nowtime.tm_hour > 24 + || (nowtime.tm_hour == 24 && (nowtime.tm_min > 0 || nowtime.tm_sec > 0)) + || nowtime.tm_min < 0 || nowtime.tm_min > 59 + || nowtime.tm_sec < 0 || nowtime.tm_sec > 60) + mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range"); + if (timezone == MRB_TIMEZONE_UTC) { nowsecs = timegm(&nowtime); } @@ -368,6 +381,17 @@ mrb_time_local(mrb_state *mrb, mrb_value self) time_mktime(mrb, ayear, amonth, aday, ahour, amin, asec, ausec, MRB_TIMEZONE_LOCAL)); } +static struct mrb_time* +time_get_ptr(mrb_state *mrb, mrb_value time) +{ + struct mrb_time *tm; + + tm = DATA_GET_PTR(mrb, time, &mrb_time_type, struct mrb_time); + if (!tm) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time"); + } + return tm; +} static mrb_value mrb_time_eq(mrb_state *mrb, mrb_value self) @@ -377,7 +401,7 @@ mrb_time_eq(mrb_state *mrb, mrb_value self) mrb_bool eq_p; mrb_get_args(mrb, "o", &other); - tm1 = DATA_CHECK_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time); eq_p = tm1 && tm2 && tm1->sec == tm2->sec && tm1->usec == tm2->usec; @@ -391,7 +415,7 @@ mrb_time_cmp(mrb_state *mrb, mrb_value self) struct mrb_time *tm1, *tm2; mrb_get_args(mrb, "o", &other); - tm1 = DATA_CHECK_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm1 = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time); if (!tm1 || !tm2) return mrb_nil_value(); if (tm1->sec > tm2->sec) { @@ -417,7 +441,7 @@ mrb_time_plus(mrb_state *mrb, mrb_value self) struct mrb_time *tm; mrb_get_args(mrb, "f", &f); - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_time_make(mrb, mrb_obj_class(mrb, self), (double)tm->sec+f, (double)tm->usec, tm->timezone); } @@ -429,8 +453,7 @@ mrb_time_minus(mrb_state *mrb, mrb_value self) struct mrb_time *tm, *tm2; mrb_get_args(mrb, "o", &other); - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); - + tm = time_get_ptr(mrb, self); tm2 = DATA_CHECK_GET_PTR(mrb, other, &mrb_time_type, struct mrb_time); if (tm2) { f = (mrb_float)(tm->sec - tm2->sec) @@ -450,7 +473,7 @@ mrb_time_wday(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_wday); } @@ -461,7 +484,7 @@ mrb_time_yday(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_yday + 1); } @@ -472,7 +495,7 @@ mrb_time_year(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_year + 1900); } @@ -483,7 +506,7 @@ mrb_time_zone(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); if (tm->timezone <= MRB_TIMEZONE_NONE) return mrb_nil_value(); if (tm->timezone >= MRB_TIMEZONE_LAST) return mrb_nil_value(); return mrb_str_new_static(mrb, @@ -501,7 +524,7 @@ mrb_time_asctime(mrb_state *mrb, mrb_value self) char buf[256]; int len; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); d = &tm->datetime; len = snprintf(buf, sizeof(buf), "%s %s %02d %02d:%02d:%02d %s%d", wday_names[d->tm_wday], mon_names[d->tm_mon], d->tm_mday, @@ -518,8 +541,7 @@ mrb_time_day(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); - if (!tm) return mrb_nil_value(); + tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_mday); } @@ -531,7 +553,7 @@ mrb_time_dst_p(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_bool_value(tm->datetime.tm_isdst); } @@ -543,11 +565,11 @@ mrb_time_getutc(mrb_state *mrb, mrb_value self) { struct mrb_time *tm, *tm2; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); *tm2 = *tm; tm2->timezone = MRB_TIMEZONE_UTC; - mrb_time_update_datetime(tm2); + time_update_datetime(mrb, tm2); return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2); } @@ -558,11 +580,11 @@ mrb_time_getlocal(mrb_state *mrb, mrb_value self) { struct mrb_time *tm, *tm2; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); tm2 = (struct mrb_time *)mrb_malloc(mrb, sizeof(*tm)); *tm2 = *tm; tm2->timezone = MRB_TIMEZONE_LOCAL; - mrb_time_update_datetime(tm2); + time_update_datetime(mrb, tm2); return mrb_time_wrap(mrb, mrb_obj_class(mrb, self), tm2); } @@ -573,7 +595,7 @@ mrb_time_hour(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_hour); } @@ -587,14 +609,14 @@ mrb_time_initialize(mrb_state *mrb, mrb_value self) int n; struct mrb_time *tm; + n = mrb_get_args(mrb, "|iiiiiii", + &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); tm = (struct mrb_time*)DATA_PTR(self); if (tm) { mrb_free(mrb, tm); } mrb_data_init(self, NULL, &mrb_time_type); - n = mrb_get_args(mrb, "|iiiiiii", - &ayear, &amonth, &aday, &ahour, &amin, &asec, &ausec); if (n == 0) { tm = current_mrb_time(mrb); } @@ -611,16 +633,23 @@ static mrb_value mrb_time_initialize_copy(mrb_state *mrb, mrb_value copy) { mrb_value src; + struct mrb_time *t1, *t2; mrb_get_args(mrb, "o", &src); if (mrb_obj_equal(mrb, copy, src)) return copy; if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) { mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); } - if (!DATA_PTR(copy)) { - mrb_data_init(copy, mrb_malloc(mrb, sizeof(struct mrb_time)), &mrb_time_type); + t1 = (struct mrb_time *)DATA_PTR(copy); + t2 = (struct mrb_time *)DATA_PTR(src); + if (!t2) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized time"); + } + if (!t1) { + t1 = (struct mrb_time *)mrb_malloc(mrb, sizeof(struct mrb_time)); + mrb_data_init(copy, t1, &mrb_time_type); } - *(struct mrb_time *)DATA_PTR(copy) = *(struct mrb_time *)DATA_PTR(src); + *t1 = *t2; return copy; } @@ -631,9 +660,9 @@ mrb_time_localtime(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); tm->timezone = MRB_TIMEZONE_LOCAL; - mrb_time_update_datetime(tm); + time_update_datetime(mrb, tm); return self; } @@ -644,7 +673,7 @@ mrb_time_mday(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_mday); } @@ -655,7 +684,7 @@ mrb_time_min(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_min); } @@ -666,7 +695,7 @@ mrb_time_mon(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_mon + 1); } @@ -677,7 +706,7 @@ mrb_time_sec(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_fixnum_value(tm->datetime.tm_sec); } @@ -689,7 +718,7 @@ mrb_time_to_f(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_float_value(mrb, (mrb_float)tm->sec + (mrb_float)tm->usec/1.0e6); } @@ -700,7 +729,7 @@ mrb_time_to_i(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); if (tm->sec > MRB_INT_MAX || tm->sec < MRB_INT_MIN) { return mrb_float_value(mrb, (mrb_float)tm->sec); } @@ -714,7 +743,7 @@ mrb_time_usec(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); if (tm->usec > MRB_INT_MAX || tm->usec < MRB_INT_MIN) { return mrb_float_value(mrb, (mrb_float)tm->usec); } @@ -728,9 +757,9 @@ mrb_time_utc(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); tm->timezone = MRB_TIMEZONE_UTC; - mrb_time_update_datetime(tm); + time_update_datetime(mrb, tm); return self; } @@ -741,7 +770,7 @@ mrb_time_utc_p(mrb_state *mrb, mrb_value self) { struct mrb_time *tm; - tm = DATA_GET_PTR(mrb, self, &mrb_time_type, struct mrb_time); + tm = time_get_ptr(mrb, self); return mrb_bool_value(tm->timezone == MRB_TIMEZONE_UTC); } |
