diff options
96 files changed, 2671 insertions, 1737 deletions
@@ -17,7 +17,7 @@ of the Ministry of Economy, Trade and Industry of Japan. ## How to get mruby -The stable version 1.2.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.2.0.zip](https://github.com/mruby/mruby/archive/1.2.0.zip) +The stable version 1.3.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/1.3.0.zip](https://github.com/mruby/mruby/archive/1.3.0.zip) The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master) diff --git a/doc/guides/debugger.md b/doc/guides/debugger.md index 366701c88..72f2c2b32 100644 --- a/doc/guides/debugger.md +++ b/doc/guides/debugger.md @@ -38,7 +38,7 @@ To confirm mrdb was installed properly, run mrdb with the `--version` option: ```bash $ mrdb --version -mruby 1.2.0 (2014-11-17) +mruby 1.3.0 (2017-7-4) ``` ## 2.2 Basic Operation diff --git a/doc/guides/mrbconf.md b/doc/guides/mrbconf.md index fabd7208b..f957f8ce2 100644 --- a/doc/guides/mrbconf.md +++ b/doc/guides/mrbconf.md @@ -122,20 +122,9 @@ largest value of required alignment. * If defined `Float` will be a mruby object with `RBasic`. ## Instance variable configuration. -`MRB_USE_IV_SEGLIST` -* If defined enable segmented list in instance variable table instead of khash. -* Segmented list is a linked list of key and value segments. -* It will linear search instead of hash search. - -`MRB_SEGMENT_SIZE` +`MRB_IV_SEGMENT_SIZE` * Default value is `4`. * Specifies size of each segment in segment list. -* Ignored when `MRB_USE_IV_SEGLIST` isn't defined. - -`MRB_IVHASH_INIT_SIZE` -* Default value is `8`. -* Specifies initial size for instance variable table. -* Ignored when `MRB_USE_IV_SEGLIST` is defined. ## Other configuration. `MRB_UTF8_STRING` diff --git a/doc/limitations.md b/doc/limitations.md index c15b3ba81..db8db9a5c 100644 --- a/doc/limitations.md +++ b/doc/limitations.md @@ -38,7 +38,7 @@ puts [1,2,3] 3 ``` -#### mruby [1.2.0 (2015-11-17)] +#### mruby [1.3.0 (2017-7-4)] ``` [1, 2, 3] @@ -61,7 +61,7 @@ end ```ZeroDivisionError``` is raised. -#### mruby [1.2.0 (2015-11-17)] +#### mruby [1.3.0 (2017-7-4)] No exception is raised. @@ -77,7 +77,7 @@ def test; eval 'test'; end; test ```SystemStackError``` is raised. -#### mruby [1.2.0 (2015-11-17)] +#### mruby [1.3.0 (2017-7-4)] Segmentation fault. @@ -105,7 +105,7 @@ p Liste.new "foobar" ``` [] ``` -#### mruby [1.2.0 (2015-11-17)] +#### mruby [1.3.0 (2017-7-4)] ```ArgumentError``` is raised. @@ -135,7 +135,7 @@ false true ``` -#### mruby [1.2.0 (2015-11-17)] +#### mruby [1.3.0 (2017-7-4)] ``` true @@ -158,7 +158,7 @@ defined?(Foo) nil ``` -#### mruby [1.2.0 (2015-11-17)] +#### mruby [1.3.0 (2017-7-4)] ```NameError``` is raised. @@ -175,7 +175,7 @@ alias $a $__a__ ``` nil ``` -#### mruby [1.2.0 (2015-11-17)] +#### mruby [1.3.0 (2017-7-4)] Syntax error @@ -197,7 +197,7 @@ end ```ArgumentError``` is raised. The re-defined ```+``` operator does not accept any arguments. -#### mruby [1.2.0 (2015-11-17)] +#### mruby [1.3.0 (2017-7-4)] ``` 'ab' ``` Behavior of the operator wasn't changed. diff --git a/examples/targets/build_config_ArduinoDue.rb b/examples/targets/build_config_ArduinoDue.rb index 740dcfa02..527aaa4f6 100644 --- a/examples/targets/build_config_ArduinoDue.rb +++ b/examples/targets/build_config_ArduinoDue.rb @@ -46,7 +46,6 @@ MRuby::CrossBuild.new("ArduinoDue") do |conf| #configuration for low memory environment cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) - cc.defines << %w(MRB_USE_IV_SEGLIST) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20) cc.defines << %w(MRB_GC_STRESS) diff --git a/examples/targets/build_config_RX630.rb b/examples/targets/build_config_RX630.rb index 8e387c29d..fd17eae99 100644 --- a/examples/targets/build_config_RX630.rb +++ b/examples/targets/build_config_RX630.rb @@ -32,7 +32,6 @@ MRuby::CrossBuild.new("RX630") do |conf| #configuration for low memory environment cc.defines << %w(MRB_USE_FLOAT) cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) - cc.defines << %w(MRB_USE_IV_SEGLIST) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20) cc.defines << %w(MRB_GC_STRESS) diff --git a/examples/targets/build_config_chipKITMax32.rb b/examples/targets/build_config_chipKITMax32.rb index c3a146e4e..951f71483 100644 --- a/examples/targets/build_config_chipKITMax32.rb +++ b/examples/targets/build_config_chipKITMax32.rb @@ -43,7 +43,6 @@ MRuby::CrossBuild.new("chipKITMax32") do |conf| #configuration for low memory environment cc.defines << %w(MRB_HEAP_PAGE_SIZE=64) - cc.defines << %w(MRB_USE_IV_SEGLIST) cc.defines << %w(KHASH_DEFAULT_SIZE=8) cc.defines << %w(MRB_STR_BUF_MIN_SIZE=20) cc.defines << %w(MRB_GC_STRESS) diff --git a/include/mrbconf.h b/include/mrbconf.h index 4796919c2..b246b970c 100644 --- a/include/mrbconf.h +++ b/include/mrbconf.h @@ -52,12 +52,6 @@ /* number of object per heap page */ //#define MRB_HEAP_PAGE_SIZE 1024 -/* use segmented list for IV table */ -//#define MRB_USE_IV_SEGLIST - -/* initial size for IV khash; ignored when MRB_USE_IV_SEGLIST is set */ -//#define MRB_IVHASH_INIT_SIZE 8 - /* if _etext and _edata available, mruby can reduce memory used by symbols */ //#define MRB_USE_ETEXT_EDATA diff --git a/include/mruby.h b/include/mruby.h index 8adce289b..18f54fd2f 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -65,6 +65,13 @@ #include "mrbconf.h" +#ifndef DBL_EPSILON +#define DBL_EPSILON ((double)2.22044604925031308085e-16L) +#endif +#ifndef LDBL_EPSILON +#define LDBL_EPSILON (1.08420217248550443401e-19L) +#endif + #ifdef MRB_USE_FLOAT #define MRB_FLOAT_EPSILON FLT_EPSILON #else @@ -113,7 +120,7 @@ typedef struct { mrb_value *stackent; int nregs; int ridx; - int eidx; + int epos; struct REnv *env; mrb_code *pc; /* return address */ mrb_code *err; /* error position */ @@ -143,7 +150,7 @@ struct mrb_context { mrb_code **rescue; /* exception handler stack */ int rsize; struct RProc **ensure; /* ensure handler stack */ - int esize; + int esize, eidx; enum mrb_fiber_state status; mrb_bool vmexec; @@ -152,14 +159,6 @@ struct mrb_context { struct mrb_jmpbuf; -typedef struct { - const char *filename; - int lineno; - struct RClass *klass; - char sep; - mrb_sym method_id; -} mrb_backtrace_entry; - typedef void (*mrb_atexit_func)(struct mrb_state*); #define MRB_STATE_NO_REGEXP 1 @@ -174,15 +173,9 @@ typedef struct mrb_state { struct mrb_context *c; struct mrb_context *root_c; + struct iv_tbl *globals; /* global variable table */ struct RObject *exc; /* exception */ - struct { - struct RObject *exc; - int n; - int n_allocated; - mrb_backtrace_entry *entries; - } backtrace; - struct iv_tbl *globals; /* global variable table */ struct RObject *top_self; struct RClass *object_class; /* Object class */ @@ -1043,11 +1036,27 @@ MRB_API mrb_value mrb_Float(mrb_state *mrb, mrb_value val); MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj); MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2); +static inline int mrb_gc_arena_save(mrb_state*); +static inline void mrb_gc_arena_restore(mrb_state*,int); + +static inline int +mrb_gc_arena_save(mrb_state *mrb) +{ + return mrb->gc.arena_idx; +} + +static inline void +mrb_gc_arena_restore(mrb_state *mrb, int idx) +{ + mrb->gc.arena_idx = idx; +} + +MRB_API int mrb_gc_arena_save(mrb_state*); +MRB_API void mrb_gc_arena_restore(mrb_state*,int); + MRB_API void mrb_garbage_collect(mrb_state*); MRB_API void mrb_full_gc(mrb_state*); MRB_API void mrb_incremental_gc(mrb_state *); -MRB_API int mrb_gc_arena_save(mrb_state*); -MRB_API void mrb_gc_arena_restore(mrb_state*,int); MRB_API void mrb_gc_mark(mrb_state*,struct RBasic*); #define mrb_gc_mark_value(mrb,val) do {\ if (!mrb_immediate_p(val)) mrb_gc_mark((mrb), mrb_basic_ptr(val)); \ @@ -1121,6 +1130,11 @@ MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg); MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv); MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c); +/* continue execution to the proc */ +/* this function should always be called as the last function of a method */ +/* e.g. return mrb_yield_cont(mrb, proc, self, argc, argv); */ +mrb_value mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv); + /* mrb_gc_protect() leaves the object in the arena */ MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj); /* mrb_gc_register() keeps the object from GC. */ @@ -1187,6 +1201,33 @@ MRB_API void mrb_show_copyright(mrb_state *mrb); MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...); +#if 0 +/* memcpy and memset does not work with gdb reverse-next on my box */ +/* use naive memcpy and memset instead */ +#undef memcpy +#undef memset +static inline void* +mrbmemcpy(void *dst, const void *src, size_t n) +{ + char *d = (char*)dst; + const char *s = (const char*)src; + while (n--) + *d++ = *s++; + return d; +} +#define memcpy(a,b,c) mrbmemcpy(a,b,c) + +static inline void* +mrbmemset(void *s, int c, size_t n) +{ + char *t = (char*)s; + while (n--) + *t++ = c; + return s; +} +#define memset(a,b,c) mrbmemset(a,b,c) +#endif + MRB_END_DECL #endif /* MRUBY_H */ diff --git a/include/mruby/array.h b/include/mruby/array.h index e553faf92..bd3124d8c 100644 --- a/include/mruby/array.h +++ b/include/mruby/array.h @@ -21,22 +21,48 @@ typedef struct mrb_shared_array { mrb_value *ptr; } mrb_shared_array; +#define MRB_ARY_EMBED_LEN_MAX ((mrb_int)(sizeof(void*)*3/sizeof(mrb_value))) struct RArray { MRB_OBJECT_HEADER; - mrb_int len; union { - mrb_int capa; - mrb_shared_array *shared; - } aux; - mrb_value *ptr; + struct { + mrb_int len; + union { + mrb_int capa; + mrb_shared_array *shared; + } aux; + mrb_value *ptr; + } heap; + mrb_value embed[MRB_ARY_EMBED_LEN_MAX]; + } as; }; #define mrb_ary_ptr(v) ((struct RArray*)(mrb_ptr(v))) #define mrb_ary_value(p) mrb_obj_value((void*)(p)) #define RARRAY(v) ((struct RArray*)(mrb_ptr(v))) -#define RARRAY_LEN(a) (RARRAY(a)->len) -#define RARRAY_PTR(a) ((const mrb_value*)RARRAY(a)->ptr) +#define MRB_ARY_EMBED 4 +#define MRB_ARY_EMBED_MASK 3 +#define ARY_EMBED_P(a) ((a)->flags & MRB_ARY_EMBED) +#define ARY_SET_EMBED_FLAG(a) ((a)->flags |= MRB_ARY_EMBED) +#define ARY_UNSET_EMBED_FLAG(a) ((a)->flags &= ~(MRB_ARY_EMBED|MRB_ARY_EMBED_MASK)) +#define ARY_EMBED_LEN(a) ((a)->flags & MRB_ARY_EMBED_MASK) +#define ARY_SET_EMBED_LEN(a,len) (a)->flags = ((a)->flags&~MRB_ARY_EMBED_MASK) | ((len)&MRB_ARY_EMBED_MASK); +#define ARY_EMBED_PTR(a) (&((a)->as.embed[0])) + +#define ARY_LEN(a) (ARY_EMBED_P(a)?ARY_EMBED_LEN(a):(a)->as.heap.len) +#define ARY_PTR(a) (ARY_EMBED_P(a)?ARY_EMBED_PTR(a):(a)->as.heap.ptr) +#define RARRAY_LEN(a) ARY_LEN(RARRAY(a)) +#define RARRAY_PTR(a) ARY_PTR(RARRAY(a)) +#define ARY_SET_LEN(a,n) do {\ + if (ARY_EMBED_P(a)) {\ + mrb_assert((n) <= MRB_ARY_EMBED_LEN_MAX); \ + ARY_SET_EMBED_LEN(a,n);\ + }\ + else\ + (a)->as.heap.len = (n);\ +} while (0) +#define ARY_CAPA(a) (ARY_EMBED_P(a)?MRB_ARY_EMBED_LEN_MAX:(a)->as.heap.aux.capa) #define MRB_ARY_SHARED 256 #define ARY_SHARED_P(a) ((a)->flags & MRB_ARY_SHARED) #define ARY_SET_SHARED_FLAG(a) ((a)->flags |= MRB_ARY_SHARED) diff --git a/include/mruby/boxing_word.h b/include/mruby/boxing_word.h index 6ca491585..d1c457fae 100644 --- a/include/mruby/boxing_word.h +++ b/include/mruby/boxing_word.h @@ -66,7 +66,7 @@ MRB_API mrb_value mrb_word_boxing_float_pool(struct mrb_state*, mrb_float); #define mrb_ptr(o) (o).value.p #define mrb_cptr(o) (o).value.vp->p #define mrb_float(o) (o).value.fp->f -#define mrb_fixnum(o) (o).value.i +#define mrb_fixnum(o) ((mrb_int)(o).value.i) #define mrb_symbol(o) (o).value.sym static inline enum mrb_vtype diff --git a/include/mruby/class.h b/include/mruby/class.h index ce953af3b..0526386d6 100644 --- a/include/mruby/class.h +++ b/include/mruby/class.h @@ -51,7 +51,7 @@ mrb_class(mrb_state *mrb, mrb_value v) } } -// TODO: figure out where to put user flags +/* TODO: figure out where to put user flags */ #define MRB_FLAG_IS_FROZEN (1 << 18) #define MRB_FLAG_IS_PREPENDED (1 << 19) #define MRB_FLAG_IS_ORIGIN (1 << 20) diff --git a/include/mruby/compile.h b/include/mruby/compile.h index ad3f19db1..13e04d0fc 100644 --- a/include/mruby/compile.h +++ b/include/mruby/compile.h @@ -33,6 +33,8 @@ typedef struct mrbc_context { mrb_bool no_exec:1; mrb_bool keep_lv:1; mrb_bool no_optimize:1; + + size_t parser_nerr; } mrbc_context; MRB_API mrbc_context* mrbc_context_new(mrb_state *mrb); diff --git a/include/mruby/dump.h b/include/mruby/dump.h index 165a20b3f..f56d66a32 100644 --- a/include/mruby/dump.h +++ b/include/mruby/dump.h @@ -52,7 +52,7 @@ MRB_API mrb_irep *mrb_read_irep(mrb_state*, const uint8_t*); /* Rite Binary File header */ #define RITE_BINARY_IDENT "RITE" #define RITE_BINARY_IDENT_LIL "ETIR" -#define RITE_BINARY_FORMAT_VER "0003" +#define RITE_BINARY_FORMAT_VER "0004" #define RITE_COMPILER_NAME "MATZ" #define RITE_COMPILER_VERSION "0000" diff --git a/include/mruby/error.h b/include/mruby/error.h index bb67e7bd8..0a262550e 100644 --- a/include/mruby/error.h +++ b/include/mruby/error.h @@ -32,6 +32,13 @@ MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_va /* declaration for fail method */ MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value); +struct RBreak { + MRB_OBJECT_HEADER; + struct iv_tbl *iv; + struct RProc *proc; + mrb_value val; +}; + /** * Protect * diff --git a/include/mruby/gc.h b/include/mruby/gc.h index 81950f169..ce214aa56 100644 --- a/include/mruby/gc.h +++ b/include/mruby/gc.h @@ -17,7 +17,9 @@ MRB_BEGIN_DECL struct mrb_state; -typedef void (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data); +#define MRB_EACH_OBJ_OK 0 +#define MRB_EACH_OBJ_BREAK 1 +typedef int (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data); void mrb_objspace_each_objects(struct mrb_state *mrb, mrb_each_object_callback *callback, void *data); MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c); @@ -62,6 +64,7 @@ typedef struct mrb_gc { size_t threshold; int interval_ratio; int step_ratio; + mrb_bool iterating :1; mrb_bool disabled :1; mrb_bool full :1; mrb_bool generational :1; diff --git a/include/mruby/hash.h b/include/mruby/hash.h index 55ad6a921..1a870785a 100644 --- a/include/mruby/hash.h +++ b/include/mruby/hash.h @@ -24,7 +24,7 @@ struct RHash { #define mrb_hash_ptr(v) ((struct RHash*)(mrb_ptr(v))) #define mrb_hash_value(p) mrb_obj_value((void*)(p)) -MRB_API mrb_value mrb_hash_new_capa(mrb_state*, int); +MRB_API mrb_value mrb_hash_new_capa(mrb_state*, mrb_int); /* * Initializes a new hash. diff --git a/include/mruby/numeric.h b/include/mruby/numeric.h index 36489b1ce..40c8c4a20 100644 --- a/include/mruby/numeric.h +++ b/include/mruby/numeric.h @@ -16,9 +16,13 @@ */ MRB_BEGIN_DECL -#define POSFIXABLE(f) ((f) <= MRB_INT_MAX) -#define NEGFIXABLE(f) ((f) >= MRB_INT_MIN) -#define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f)) +#define TYPED_POSFIXABLE(f,t) ((f) <= (t)MRB_INT_MAX) +#define TYPED_NEGFIXABLE(f,t) ((f) >= (t)MRB_INT_MIN) +#define TYPED_FIXABLE(f,t) (TYPED_POSFIXABLE(f,t) && TYPED_NEGFIXABLE(f,t)) +#define POSFIXABLE(f) TYPED_POSFIXABLE(f,mrb_int) +#define NEGFIXABLE(f) TYPED_NEGFIXABLE(f,mrb_int) +#define FIXABLE(f) TYPED_FIXABLE(f,mrb_int) +#define FIXABLE_FLOAT(f) TYPED_FIXABLE(f,double) MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value val); MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base); @@ -42,9 +46,11 @@ mrb_value mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y); # define MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS #endif +/* // Clang 3.8 and 3.9 have problem compiling mruby in 32-bit mode, when MRB_INT64 is set // because of missing __mulodi4 and similar functions in its runtime. We need to use custom // implementation for them. +*/ #ifdef MRB_HAVE_TYPE_GENERIC_CHECKED_ARITHMETIC_BUILTINS #if defined(__clang__) && (__clang_major__ == 3) && (__clang_minor__ >= 8) && \ defined(MRB_32BIT) && defined(MRB_INT64) diff --git a/include/mruby/proc.h b/include/mruby/proc.h index 5965a2b39..9c2666289 100644 --- a/include/mruby/proc.h +++ b/include/mruby/proc.h @@ -18,8 +18,11 @@ MRB_BEGIN_DECL struct REnv { MRB_OBJECT_HEADER; mrb_value *stack; - mrb_sym mid; ptrdiff_t cioff; + union { + mrb_sym mid; + struct mrb_context *c; + } cxt; }; #define MRB_SET_ENV_STACK_LEN(e,len) (e)->flags = (unsigned int)(len) @@ -52,6 +55,8 @@ struct RProc { #define MRB_PROC_CFUNC_P(p) (((p)->flags & MRB_PROC_CFUNC) != 0) #define MRB_PROC_STRICT 256 #define MRB_PROC_STRICT_P(p) (((p)->flags & MRB_PROC_STRICT) != 0) +#define MRB_PROC_ORPHAN 512 +#define MRB_PROC_ORPHAN_P(p) (((p)->flags & MRB_PROC_ORPHAN) != 0) #define mrb_proc_ptr(v) ((struct RProc*)(mrb_ptr(v))) diff --git a/include/mruby/string.h b/include/mruby/string.h index b18093218..c3fbb063d 100644 --- a/include/mruby/string.h +++ b/include/mruby/string.h @@ -84,6 +84,13 @@ MRB_API mrb_int mrb_str_strlen(mrb_state*, struct RString*); void mrb_gc_free_str(mrb_state*, struct RString*); MRB_API void mrb_str_modify(mrb_state*, struct RString*); + +/* + * Finds the index of a substring in a string + */ +MRB_API mrb_int mrb_str_index(mrb_state*, mrb_value, const char*, mrb_int, mrb_int); +#define mrb_str_index_lit(mrb, str, lit, off) mrb_str_index(mrb, str, lit, mrb_strlen_lit(lit), off); + /* * Appends self to other. Returns self as a concatnated string. * diff --git a/include/mruby/value.h b/include/mruby/value.h index 54d197f8f..98c68d657 100644 --- a/include/mruby/value.h +++ b/include/mruby/value.h @@ -116,7 +116,8 @@ enum mrb_vtype { MRB_TT_DATA, /* 21 */ MRB_TT_FIBER, /* 22 */ MRB_TT_ISTRUCT, /* 23 */ - MRB_TT_MAXDEFINE /* 24 */ + MRB_TT_BREAK, /* 24 */ + MRB_TT_MAXDEFINE /* 25 */ }; #include <mruby/object.h> @@ -266,6 +267,14 @@ mrb_undef_value(void) } #ifdef MRB_USE_ETEXT_EDATA +#if (defined(__APPLE__) && defined(__MACH__)) +#include <mach-o/getsect.h> +static inline mrb_bool +mrb_ro_data_p(const char *p) +{ + return (const char*)get_etext() < p && p < (const char*)get_edata(); +} +#else extern char _etext[]; #ifdef MRB_NO_INIT_ARRAY_START extern char _edata[]; @@ -284,6 +293,7 @@ mrb_ro_data_p(const char *p) return _etext < p && p < (char*)&__init_array_start; } #endif +#endif #else # define mrb_ro_data_p(p) FALSE #endif diff --git a/include/mruby/version.h b/include/mruby/version.h index 20c39ec79..8414bf204 100644 --- a/include/mruby/version.h +++ b/include/mruby/version.h @@ -42,7 +42,7 @@ MRB_BEGIN_DECL /* * Minor release version number. */ -#define MRUBY_RELEASE_MINOR 2 +#define MRUBY_RELEASE_MINOR 3 /* * Tiny release version number. @@ -62,17 +62,17 @@ MRB_BEGIN_DECL /* * Release year. */ -#define MRUBY_RELEASE_YEAR 2015 +#define MRUBY_RELEASE_YEAR 2017 /* * Release month. */ -#define MRUBY_RELEASE_MONTH 11 +#define MRUBY_RELEASE_MONTH 7 /* * Release day. */ -#define MRUBY_RELEASE_DAY 17 +#define MRUBY_RELEASE_DAY 4 /* * Release date as a string. diff --git a/lib/mruby/gem.rb b/lib/mruby/gem.rb index 1f7cde529..27a1d358c 100644 --- a/lib/mruby/gem.rb +++ b/lib/mruby/gem.rb @@ -63,9 +63,6 @@ module MRuby objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X")) end - @generate_functions = !(@rbfiles.empty? && @objs.empty?) - @objs << objfile("#{build_dir}/gem_init") if @generate_functions - @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb") @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X")) @@ -83,6 +80,9 @@ module MRuby instance_eval(&@initializer) + @generate_functions = !(@rbfiles.empty? && @objs.empty?) + @objs << objfile("#{build_dir}/gem_init") if @generate_functions + if !name || !licenses || !authors fail "#{name || dir} required to set name, license(s) and author(s)" end diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb index 1e6d4d581..716eabe06 100644 --- a/mrbgems/mruby-array-ext/mrblib/array.rb +++ b/mrbgems/mruby-array-ext/mrblib/array.rb @@ -595,31 +595,58 @@ class Array def bsearch(&block) return to_enum :bsearch unless block_given? + if idx = bsearch_index(&block) + self[idx] + else + nil + end + end + + ## + # call-seq: + # ary.bsearch_index {|x| block } -> int or nil + # + # By using binary search, finds an index of a value from this array which + # meets the given condition in O(log n) where n is the size of the array. + # + # It supports two modes, depending on the nature of the block and they are + # exactly the same as in the case of #bsearch method with the only difference + # being that this method returns the index of the element instead of the + # element itself. For more details consult the documentation for #bsearch. + + def bsearch_index(&block) + return to_enum :bsearch_index unless block_given? + low = 0 - high = self.size + high = size satisfied = false + while low < high - mid = low + ((high - low) / 2).truncate - val = self[mid] - v = block.call(val) - if v.is_a?(Integer) - return val if v == 0 - smaller = v < 0 - elsif v == true + mid = ((low+high)/2).truncate + res = block.call self[mid] + + case res + when 0 # find-any mode: Found! + return mid + when Numeric # find-any mode: Continue... + in_lower_half = res < 0 + when true # find-min mode + in_lower_half = true satisfied = true - smaller = true - elsif v == false || v.nil? - smaller = false + when false, nil # find-min mode + in_lower_half = false + else + raise TypeError, 'invalid block result (must be numeric, true, false or nil)' end - if smaller + + if in_lower_half high = mid else low = mid + 1 end end - return nil if low == self.size - return nil unless satisfied - self[low] + + satisfied ? low : nil end ## diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c index 6919186bd..72e5080f1 100644 --- a/mrbgems/mruby-array-ext/src/array.c +++ b/mrbgems/mruby-array-ext/src/array.c @@ -174,7 +174,7 @@ static mrb_value mrb_ary_slice_bang(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_int i, j, k, len; + mrb_int i, j, k, len, alen = ARY_LEN(a); mrb_value index; mrb_value val; mrb_value *ptr; @@ -185,7 +185,7 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self) if (mrb_get_args(mrb, "o|i", &index, &len) == 1) { switch (mrb_type(index)) { case MRB_TT_RANGE: - if (mrb_range_beg_len(mrb, index, &i, &len, a->len, TRUE) == 1) { + if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) { goto delete_pos_len; } else { @@ -202,25 +202,25 @@ mrb_ary_slice_bang(mrb_state *mrb, mrb_value self) i = mrb_fixnum(index); delete_pos_len: - if (i < 0) i += a->len; - if (i < 0 || a->len < i) return mrb_nil_value(); + if (i < 0) i += alen; + if (i < 0 || alen < i) return mrb_nil_value(); if (len < 0) return mrb_nil_value(); - if (a->len == i) return mrb_ary_new(mrb); - if (len > a->len - i) len = a->len - i; + if (alen == i) return mrb_ary_new(mrb); + if (len > alen - i) len = alen - i; ary = mrb_ary_new_capa(mrb, len); - + ptr = ARY_PTR(a); for (j = i, k = 0; k < len; ++j, ++k) { - mrb_ary_push(mrb, ary, a->ptr[j]); + mrb_ary_push(mrb, ary, ptr[j]); } - ptr = a->ptr + i; - for (j = i; j < a->len - len; ++j) { + ptr += i; + for (j = i; j < alen - len; ++j) { *ptr = *(ptr+len); ++ptr; } - mrb_ary_resize(mrb, self, a->len - len); + mrb_ary_resize(mrb, self, alen - len); return ary; } diff --git a/mrbgems/mruby-array-ext/test/array.rb b/mrbgems/mruby-array-ext/test/array.rb index 95a796cf9..c0db1b1cc 100644 --- a/mrbgems/mruby-array-ext/test/array.rb +++ b/mrbgems/mruby-array-ext/test/array.rb @@ -228,18 +228,47 @@ end assert("Array#bsearch") do # Find minimum mode - a = [0, 4, 7, 10, 12] - assert_include [4, 7], a.bsearch {|x| x >= 4 } - assert_equal 7, a.bsearch {|x| x >= 6 } - assert_equal 0, a.bsearch {|x| x >= -1 } - assert_nil a.bsearch {|x| x >= 100 } + a = [0, 2, 4] + assert_equal 0, a.bsearch{ |x| x >= -1 } + assert_equal 0, a.bsearch{ |x| x >= 0 } + assert_equal 2, a.bsearch{ |x| x >= 1 } + assert_equal 2, a.bsearch{ |x| x >= 2 } + assert_equal 4, a.bsearch{ |x| x >= 3 } + assert_equal 4, a.bsearch{ |x| x >= 4 } + assert_nil a.bsearch{ |x| x >= 5 } # Find any mode - a = [0, 4, 7, 10, 12] - assert_include [4, 7], a.bsearch {|x| 1 - (x / 4).truncate } - assert_nil a.bsearch {|x| 4 - (x / 2).truncate } - assert_equal(nil, a.bsearch {|x| 1 }) - assert_equal(nil, a.bsearch {|x| -1 }) + a = [0, 4, 8] + def between(lo, x, hi) + if x < lo + 1 + elsif x > hi + -1 + else + 0 + end + end + assert_nil a.bsearch{ |x| between(-3, x, -1) } + assert_equal 0, a.bsearch{ |x| between(-1, x, 1) } + assert_nil a.bsearch{ |x| between( 1, x, 3) } + assert_equal 4, a.bsearch{ |x| between( 3, x, 5) } + assert_nil a.bsearch{ |x| between( 5, x, 7) } + assert_equal 8, a.bsearch{ |x| between( 7, x, 9) } + assert_nil a.bsearch{ |x| between( 9, x, 11) } + + assert_equal 0, a.bsearch{ |x| between( 0, x, 3) } + assert_equal 4, a.bsearch{ |x| between( 0, x, 4) } + assert_equal 4, a.bsearch{ |x| between( 4, x, 8) } + assert_equal 8, a.bsearch{ |x| between( 5, x, 8) } + + # Invalid block result + assert_raise TypeError, 'invalid block result (must be numeric, true, false or nil)' do + a.bsearch{ 'I like to watch the world burn' } + end +end + +assert("Array#bsearch_index") do + # tested through Array#bsearch end assert("Array#delete_if") do diff --git a/mrbgems/mruby-bin-debugger/bintest/print.rb b/mrbgems/mruby-bin-debugger/bintest/print.rb index 403ada8e1..1bc96c478 100644 --- a/mrbgems/mruby-bin-debugger/bintest/print.rb +++ b/mrbgems/mruby-bin-debugger/bintest/print.rb @@ -341,8 +341,8 @@ assert('mruby-bin-debugger(print) Literal:Numeric') do tc << {:cmd=>"p +0100", :exp=>'$3 = 64'} tc << {:cmd=>"p 0x100", :exp=>'$4 = 256'} tc << {:cmd=>"p 1_234", :exp=>'$5 = 1234'} - tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000.to_s}"} - tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000.to_s}"} + tc << {:cmd=>"p 0b1000_0000", :exp=>"$6 = #{0b1000_0000}"} + tc << {:cmd=>"p 0x1000_0000", :exp=>"$7 = #{0x1000_0000}"} tc << {:cmd=>"p 3.14", :exp=>'$8 = 3.14'} tc << {:cmd=>"p -12.3", :exp=>'$9 = -12.3'} diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index d633a09b2..fe311d830 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -442,7 +442,7 @@ main(int argc, char **argv) char_index = 0; while ((last_char = getchar()) != '\n') { if (last_char == EOF) break; - if (char_index > sizeof(last_code_line)-2) { + if (char_index >= sizeof(last_code_line)-2) { fputs("input string too long\n", stderr); continue; } diff --git a/mrbgems/mruby-bin-mruby-config/mrbgem.rake b/mrbgems/mruby-bin-mruby-config/mrbgem.rake index 73f971a91..66d6ef80b 100644 --- a/mrbgems/mruby-bin-mruby-config/mrbgem.rake +++ b/mrbgems/mruby-bin-mruby-config/mrbgem.rake @@ -23,7 +23,7 @@ MRuby.each_target do FileUtils.copy "#{File.dirname(__FILE__)}/#{mruby_config}", t.name config = Hash[open("#{build_dir}/lib/libmruby.flags.mak").read.split("\n").map {|x| a = x.split(/\s*=\s*/, 2); [a[0], a[1].gsub('\\"', '"') ]}] IO.write(t.name, File.open(t.name) {|f| - f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"} + f.read.gsub (/echo (MRUBY_CFLAGS|MRUBY_LIBS|MRUBY_LDFLAGS_BEFORE_LIBS|MRUBY_LDFLAGS|MRUBY_LIBMRUBY_PATH)/) {|x| config[$1].empty? ? '' : "echo #{config[$1]}"} }) FileUtils.chmod(0755, t.name) end diff --git a/mrbgems/mruby-bin-mruby-config/mruby-config b/mrbgems/mruby-bin-mruby-config/mruby-config index fcc3fabe3..57346c03f 100644 --- a/mrbgems/mruby-bin-mruby-config/mruby-config +++ b/mrbgems/mruby-bin-mruby-config/mruby-config @@ -6,12 +6,14 @@ while [ $# -gt 0 ]; do --ldflags) echo MRUBY_LDFLAGS;; --ldflags-before-libs) echo MRUBY_LDFLAGS_BEFORE_LIBS;; --libs) echo MRUBY_LIBS;; + --libmruby-path) echo MRUBY_LIBMRUBY_PATH;; --help) echo "Usage: mruby-config [switches]" echo " switches:" echo " --cflags print flags passed to compiler" echo " --ldflags print flags passed to linker" - echo " --ldflags-before-libs print flags passwd to linker before linked libraries" + echo " --ldflags-before-libs print flags passed to linker before linked libraries" echo " --libs print linked libraries" + echo " --libmruby-path print libmruby path" exit 0;; esac shift diff --git a/mrbgems/mruby-bin-mruby-config/mruby-config.bat b/mrbgems/mruby-bin-mruby-config/mruby-config.bat index 212dbdd77..a1f7bfdd1 100644 --- a/mrbgems/mruby-bin-mruby-config/mruby-config.bat +++ b/mrbgems/mruby-bin-mruby-config/mruby-config.bat @@ -7,6 +7,7 @@ if "%0" equ "--cflags" goto cflags if "%0" equ "--ldflags" goto ldflags if "%0" equ "--ldflags-before-libs" goto ldflagsbeforelibs if "%0" equ "--libs" goto libs +if "%0" equ "--libmruby-path" goto libmrubypath if "%0" equ "--help" goto showhelp echo Invalid Option goto :eof @@ -27,10 +28,15 @@ goto top echo MRUBY_LDFLAGS_BEFORE_LIBS goto top +:libmrubypath +echo MRUBY_LIBMRUBY_PATH +goto top + :showhelp echo Usage: mruby-config [switches] echo switches: echo --cflags print flags passed to compiler echo --ldflags print flags passed to linker -echo --ldflags-before-libs print flags passwd to linker before linked libraries +echo --ldflags-before-libs print flags passed to linker before linked libraries echo --libs print linked libraries +echo --libmruby-path print libmruby path diff --git a/mrbgems/mruby-bin-mruby/mrbgem.rake b/mrbgems/mruby-bin-mruby/mrbgem.rake index 243b413eb..fbec13847 100644 --- a/mrbgems/mruby-bin-mruby/mrbgem.rake +++ b/mrbgems/mruby-bin-mruby/mrbgem.rake @@ -7,7 +7,6 @@ MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec| spec.add_dependency('mruby-error', :core => 'mruby-error') if build.cxx_exception_enabled? - @objs << build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx") - @objs.delete_if { |v| v == objfile("#{spec.build_dir}/tools/mruby/mruby") } + build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx") end end diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c index d0e5dc8dc..61d4cde94 100644 --- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c @@ -236,7 +236,10 @@ main(int argc, char **argv) mrb_gc_arena_restore(mrb, ai); mrbc_context_free(mrb, c); if (mrb->exc) { - if (!mrb_undef_p(v)) { + if (mrb_undef_p(v)) { + mrb_p(mrb, mrb_obj_value(mrb->exc)); + } + else { mrb_print_error(mrb); } n = -1; diff --git a/mrbgems/mruby-class-ext/src/class.c b/mrbgems/mruby-class-ext/src/class.c index 8ca7d66c2..5506c4829 100644 --- a/mrbgems/mruby-class-ext/src/class.c +++ b/mrbgems/mruby-class-ext/src/class.c @@ -9,12 +9,19 @@ mrb_mod_name(mrb_state *mrb, mrb_value self) return mrb_nil_p(name)? name : mrb_str_dup(mrb, name); } +static mrb_value +mrb_mod_singleton_class_p(mrb_state *mrb, mrb_value self) +{ + return mrb_bool_value(mrb_type(self) == MRB_TT_SCLASS); +} + 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()); + mrb_define_method(mrb, mod, "singleton_class?", mrb_mod_singleton_class_p, MRB_ARGS_NONE()); } void diff --git a/mrbgems/mruby-class-ext/test/module.rb b/mrbgems/mruby-class-ext/test/module.rb index f721ad0c6..65abde108 100644 --- a/mrbgems/mruby-class-ext/test/module.rb +++ b/mrbgems/mruby-class-ext/test/module.rb @@ -1,10 +1,34 @@ assert 'Module#name' do - module A - class B - end + module Outer + class Inner; end + const_set :SetInner, Class.new end - assert_nil A::B.singleton_class.name - assert_equal 'Fixnum', Fixnum.name - assert_equal 'A::B', A::B.name + assert_equal 'Outer', Outer.name + assert_equal 'Outer::Inner', Outer::Inner.name + assert_equal 'Outer::SetInner', Outer::SetInner.name + + outer = Module.new do + const_set :SetInner, Class.new + end + Object.const_set :SetOuter, outer + + assert_equal 'SetOuter', SetOuter.name + assert_equal 'SetOuter::SetInner', SetOuter::SetInner.name + + mod = Module.new + cls = Class.new + + assert_nil mod.name + assert_nil cls.name +end + +assert 'Module#singleton_class?' do + mod = Module.new + cls = Class.new + scl = cls.singleton_class + + assert_false mod.singleton_class? + assert_false cls.singleton_class? + assert_true scl.singleton_class? end diff --git a/mrbgems/mruby-compiler/bintest/mrbc.rb b/mrbgems/mruby-compiler/bintest/mrbc.rb index e27365edb..f4d9208b3 100644 --- a/mrbgems/mruby-compiler/bintest/mrbc.rb +++ b/mrbgems/mruby-compiler/bintest/mrbc.rb @@ -19,3 +19,12 @@ assert('parsing function with void argument') do assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp assert_equal 0, $?.exitstatus end + +assert('embedded document with invalid terminator') do + a, out = Tempfile.new('a.rb'), Tempfile.new('out.mrb') + a.write("=begin\n=endx\n") + a.flush + result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1` + assert_equal "#{a.path}:3:0: embedded document meets end of file", result.chomp + assert_equal 1, $?.exitstatus +end diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 64f4702a7..a1045c38c 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -19,6 +19,10 @@ #include <mruby/re.h> #include <mruby/throw.h> +#ifndef MRB_CODEGEN_LEVEL_MAX +#define MRB_CODEGEN_LEVEL_MAX 1024 +#endif + typedef mrb_ast_node node; typedef struct mrb_parser_state parser_state; @@ -73,6 +77,8 @@ typedef struct scope { int debug_start_pos; uint16_t filename_index; parser_state* parser; + + int rlev; /* recursion levels */ } codegen_scope; static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv); @@ -956,7 +962,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val) int type = (intptr_t)tree->car; tree = tree->cdr; - switch ((intptr_t)type) { + switch (type) { case NODE_GVAR: idx = new_sym(s, sym(tree)); genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val); @@ -1006,8 +1012,10 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val) break; case NODE_CALL: + case NODE_SCALL: push(); - gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL, 0); + gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL, + type == NODE_SCALL); pop(); if (val) { genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val); @@ -1024,7 +1032,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val) default: #ifndef MRB_DISABLE_STDIO - printf("unknown lhs %d\n", type); + fprintf(stderr, "unknown lhs %d\n", type); #endif break; } @@ -1241,6 +1249,7 @@ static void codegen(codegen_scope *s, node *tree, int val) { int nt; + int rlev = s->rlev; if (!tree) { if (val) { @@ -1250,6 +1259,10 @@ codegen(codegen_scope *s, node *tree, int val) return; } + s->rlev++; + if (s->rlev > MRB_CODEGEN_LEVEL_MAX) { + codegen_error(s, "too complex expression"); + } if (s->irep && s->filename_index != tree->filename_index) { s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc); @@ -1278,7 +1291,7 @@ codegen(codegen_scope *s, node *tree, int val) int onerr, noexc, exend, pos1, pos2, tmp; struct loopinfo *lp; - if (tree->car == NULL) return; + if (tree->car == NULL) goto exit; onerr = genop(s, MKOP_Bx(OP_ONERR, 0)); lp = loop_push(s, LOOP_BEGIN); lp->pc1 = onerr; @@ -1383,7 +1396,7 @@ codegen(codegen_scope *s, node *tree, int val) break; case NODE_LAMBDA: - { + if (val) { int idx = lambda_body(s, tree, 1); genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA)); @@ -1392,7 +1405,7 @@ codegen(codegen_scope *s, node *tree, int val) break; case NODE_BLOCK: - { + if (val) { int idx = lambda_body(s, tree, 1); genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK)); @@ -1407,18 +1420,18 @@ codegen(codegen_scope *s, node *tree, int val) if (!tree->car) { codegen(s, e, val); - return; + goto exit; } switch ((intptr_t)tree->car->car) { case NODE_TRUE: case NODE_INT: case NODE_STR: codegen(s, tree->cdr->car, val); - return; + goto exit; case NODE_FALSE: case NODE_NIL: codegen(s, e, val); - return; + goto exit; } codegen(s, tree->car, VAL); pop(); @@ -1879,7 +1892,7 @@ codegen(codegen_scope *s, node *tree, int val) gen_assignment(s, tree->car, cursp(), val); } dispatch(s, pos); - return; + goto exit; } codegen(s, tree->cdr->cdr->car, VAL); push(); pop(); @@ -1937,9 +1950,19 @@ codegen(codegen_scope *s, node *tree, int val) case NODE_SUPER: { + codegen_scope *s2 = s; + int lv = 0; int n = 0, noop = 0, sendv = 0; push(); /* room for receiver */ + while (!s2->mscope) { + lv++; + s2 = s2->prev; + if (!s2) break; + } + genop(s, MKOP_ABx(OP_ARGARY, cursp(), (lv & 0xf))); + push(); push(); /* ARGARY pushes two values */ + pop(); pop(); if (tree) { node *args = tree->car; if (args) { @@ -2017,7 +2040,6 @@ codegen(codegen_scope *s, node *tree, int val) if (!s2) break; } if (s2) ainfo = s2->ainfo; - genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf))); push(); if (tree) { n = gen_values(s, tree, VAL, 0); @@ -2027,6 +2049,7 @@ codegen(codegen_scope *s, node *tree, int val) } } pop_n(n+1); + genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf))); if (sendv) n = CALL_MAXARGS; genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n)); if (val) push(); @@ -2195,11 +2218,11 @@ codegen(codegen_scope *s, node *tree, int val) case NODE_NTH_REF: if (val) { mrb_state *mrb = s->mrb; - char buf[32]; + mrb_value str; int sym; - snprintf(buf, sizeof(buf), "$%" MRB_PRId, (mrb_int)(intptr_t)tree); - sym = new_sym(s, mrb_intern_cstr(mrb, buf)); + str = mrb_format(mrb, "$%S", mrb_fixnum_value((mrb_int)(intptr_t)tree)); + sym = new_sym(s, mrb_intern_str(mrb, str)); genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); push(); } @@ -2377,9 +2400,7 @@ codegen(codegen_scope *s, node *tree, int val) int ai = mrb_gc_arena_save(s->mrb); int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); - if (val == NOVAL) { push(); } - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + genop(s, MKOP_A(OP_LOADSELF, cursp())); push(); codegen(s, tree->car, VAL); n = tree->cdr; @@ -2394,12 +2415,11 @@ codegen(codegen_scope *s, node *tree, int val) push(); n = n->cdr; } - pop(); - pop(); + push(); /* for block */ + pop_n(3); sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); - if (val == NOVAL) { pop(); } - else { push(); } + if (val) push(); mrb_gc_arena_restore(s->mrb, ai); } break; @@ -2409,19 +2429,17 @@ codegen(codegen_scope *s, node *tree, int val) char *p = (char*)tree->car; size_t len = (intptr_t)tree->cdr; int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); int off = new_lit(s, mrb_str_new(s->mrb, p, len)); + int sym; - if (val == NOVAL) { push(); } - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); + genop(s, MKOP_A(OP_LOADSELF, cursp())); push(); genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - pop(); + push(); push(); + pop_n(3); sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); - if (val == NOVAL) { pop(); } - else { push(); } + if (val) push(); mrb_gc_arena_restore(s->mrb, ai); } break; @@ -2588,7 +2606,8 @@ codegen(codegen_scope *s, node *tree, int val) genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b)); push(); genop(s, MKOP_A(OP_LOADNIL, cursp())); - pop_n(3); + push(); + pop_n(4); genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2)); if (val) { push(); @@ -2756,6 +2775,8 @@ codegen(codegen_scope *s, node *tree, int val) default: break; } + exit: + s->rlev = rlev; } static void @@ -2847,6 +2868,8 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *lv) p->parser = prev->parser; p->filename_index = prev->filename_index; + p->rlev = prev->rlev+1; + return p; } @@ -2925,7 +2948,7 @@ loop_break(codegen_scope *s, node *tree) genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); loop = loop->prev; } - while (loop && loop->type == LOOP_RESCUE) { + while (loop && (loop->type == LOOP_RESCUE || loop->type == LOOP_BEGIN)) { loop = loop->prev; } if (!loop) { @@ -2957,10 +2980,10 @@ loop_break(codegen_scope *s, node *tree) static void loop_pop(codegen_scope *s, int val) { - dispatch_linked(s, s->loop->pc3); if (val) { genop(s, MKOP_A(OP_LOADNIL, cursp())); } + dispatch_linked(s, s->loop->pc3); s->loop = s->loop->prev; if (val) push(); } @@ -2970,6 +2993,7 @@ mrb_generate_code(mrb_state *mrb, parser_state *p) { codegen_scope *scope = scope_new(mrb, 0, 0); struct RProc *proc; + struct mrb_jmpbuf *prev_jmp = mrb->jmp; if (!scope) { return NULL; @@ -2980,16 +3004,20 @@ mrb_generate_code(mrb_state *mrb, parser_state *p) scope->filename_index = p->current_filename_index; MRB_TRY(&scope->jmp) { + mrb->jmp = &scope->jmp; /* prepare irep */ codegen(scope, p->tree, NOVAL); proc = mrb_proc_new(mrb, scope->irep); mrb_irep_decref(mrb, scope->irep); mrb_pool_close(scope->mpool); + proc->c = NULL; + mrb->jmp = prev_jmp; return proc; } MRB_CATCH(&scope->jmp) { mrb_irep_decref(mrb, scope->irep); mrb_pool_close(scope->mpool); + mrb->jmp = prev_jmp; return NULL; } MRB_END_EXC(&scope->jmp); diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index 226701b52..5d5ef297c 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -356,6 +356,7 @@ new_alias(parser_state *p, mrb_sym a, mrb_sym b) static node* new_if(parser_state *p, node *a, node *b, node *c) { + void_expr_error(p, a); return list4((node*)NODE_IF, a, b, c); } @@ -363,6 +364,7 @@ new_if(parser_state *p, node *a, node *b, node *c) static node* new_unless(parser_state *p, node *a, node *b, node *c) { + void_expr_error(p, a); return list4((node*)NODE_IF, a, c, b); } @@ -370,6 +372,7 @@ new_unless(parser_state *p, node *a, node *b, node *c) static node* new_while(parser_state *p, node *a, node *b) { + void_expr_error(p, a); return cons((node*)NODE_WHILE, cons(a, b)); } @@ -377,6 +380,7 @@ new_while(parser_state *p, node *a, node *b) static node* new_until(parser_state *p, node *a, node *b) { + void_expr_error(p, a); return cons((node*)NODE_UNTIL, cons(a, b)); } @@ -384,6 +388,7 @@ new_until(parser_state *p, node *a, node *b) static node* new_for(parser_state *p, node *v, node *o, node *b) { + void_expr_error(p, o); return list4((node*)NODE_FOR, v, o, b); } @@ -394,6 +399,7 @@ new_case(parser_state *p, node *a, node *b) node *n = list2((node*)NODE_CASE, a); node *n2 = n; + void_expr_error(p, a); while (n2->cdr) { n2 = n2->cdr; } @@ -420,6 +426,7 @@ static node* new_call(parser_state *p, node *a, mrb_sym b, node *c, int pass) { node *n = list4(nint(pass?NODE_CALL:NODE_SCALL), a, nsym(b), c); + void_expr_error(p, a); NODE_LINENO(n, a); return n; } @@ -515,6 +522,7 @@ new_dot3(parser_state *p, node *a, node *b) static node* new_colon2(parser_state *p, node *b, mrb_sym c) { + void_expr_error(p, b); return cons((node*)NODE_COLON2, cons(b, nsym(c))); } @@ -622,6 +630,7 @@ new_undef(parser_state *p, mrb_sym sym) static node* new_class(parser_state *p, node *c, node *s, node *b) { + void_expr_error(p, s); return list4((node*)NODE_CLASS, c, s, cons(locals_node(p), b)); } @@ -629,6 +638,7 @@ new_class(parser_state *p, node *c, node *s, node *b) static node* new_sclass(parser_state *p, node *o, node *b) { + void_expr_error(p, o); return list3((node*)NODE_SCLASS, o, cons(locals_node(p), b)); } @@ -650,6 +660,7 @@ new_def(parser_state *p, mrb_sym m, node *a, node *b) static node* new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b) { + void_expr_error(p, o); return list6((node*)NODE_SDEF, o, nsym(m), locals_node(p), a, b); } @@ -702,6 +713,7 @@ new_lambda(parser_state *p, node *a, node *b) static node* new_asgn(parser_state *p, node *a, node *b) { + void_expr_error(p, b); return cons((node*)NODE_ASGN, cons(a, b)); } @@ -709,6 +721,7 @@ new_asgn(parser_state *p, node *a, node *b) static node* new_masgn(parser_state *p, node *a, node *b) { + void_expr_error(p, b); return cons((node*)NODE_MASGN, cons(a, b)); } @@ -716,6 +729,7 @@ new_masgn(parser_state *p, node *a, node *b) static node* new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b) { + void_expr_error(p, b); return list4((node*)NODE_OP_ASGN, a, nsym(op), b); } @@ -835,6 +849,7 @@ new_symbols(parser_state *p, node *a) static node* call_uni_op(parser_state *p, node *recv, const char *m) { + void_expr_error(p, recv); return new_call(p, recv, intern_cstr(m), 0, 1); } @@ -1404,7 +1419,6 @@ expr_value : expr { if (!$1) $$ = new_nil(p); else { - void_expr_error(p, $1); $$ = $1; } } @@ -1416,6 +1430,9 @@ command_call : command block_command : block_call | block_call call_op2 operation2 command_args + { + $$ = new_call(p, $1, $3, $4, $2); + } ; cmd_brace_block : tLBRACE_ARG @@ -1661,6 +1678,7 @@ cpath : tCOLON3 cname } | primary_value tCOLON2 cname { + void_expr_error(p, $1); $$ = cons($1, nsym($3)); } ; @@ -1931,12 +1949,12 @@ aref_args : none arg_rhs : arg %prec tOP_ASGN { - void_expr_error(p, $1); $$ = $1; } | arg modifier_rescue arg { void_expr_error(p, $1); + void_expr_error(p, $3); $$ = new_mod_rescue(p, $1, $3); } ; @@ -1972,6 +1990,7 @@ opt_call_args : none call_args : command { + void_expr_error(p, $1); $$ = cons(list1($1), 0); NODE_LINENO($$, $1); } @@ -3112,12 +3131,14 @@ f_opt_asgn : tIDENTIFIER '=' f_opt : f_opt_asgn arg { + void_expr_error(p, $2); $$ = cons(nsym($1), $2); } ; f_block_opt : f_opt_asgn primary_value { + void_expr_error(p, $2); $$ = cons(nsym($1), $2); } ; @@ -3230,22 +3251,28 @@ assocs : assoc assoc : arg tASSOC arg { + void_expr_error(p, $1); + void_expr_error(p, $3); $$ = cons($1, $3); } | tLABEL arg { + void_expr_error(p, $2); $$ = cons(new_sym(p, $1), $2); } | tLABEL_END arg { + void_expr_error(p, $2); $$ = cons(new_sym(p, new_strsym(p, $1)), $2); } | tSTRING_BEG tLABEL_END arg { + void_expr_error(p, $3); $$ = cons(new_sym(p, new_strsym(p, $2)), $3); } | tSTRING_BEG string_rep tLABEL_END arg { + void_expr_error(p, $4); $$ = cons(new_dsym(p, push($2, $3)), $4); } ; @@ -3441,6 +3468,19 @@ void_expr_error(parser_state *p, node *n) case NODE_RETRY: yyerror(p, "void value expression"); break; + case NODE_AND: + case NODE_OR: + void_expr_error(p, n->cdr->car); + void_expr_error(p, n->cdr->cdr); + break; + case NODE_BEGIN: + if (n->cdr) { + while (n->cdr) { + n = n->cdr; + } + void_expr_error(p, n->car); + } + break; default: break; } @@ -3580,7 +3620,7 @@ skips(parser_state *p, const char *s) /* skip until first char */ for (;;) { c = nextc(p); - if (c < 0) return c; + if (c < 0) return FALSE; if (c == '\n') { p->lineno++; p->column = 0; @@ -5514,10 +5554,11 @@ void mrb_parser_dump(mrb_state *mrb, node *tree, int offset); MRB_API void mrb_parser_parse(parser_state *p, mrbc_context *c) { - struct mrb_jmpbuf buf; - p->jmp = &buf; + struct mrb_jmpbuf buf1; + p->jmp = &buf1; MRB_TRY(p->jmp) { + int n = 1; p->cmd_start = TRUE; p->in_def = p->in_single = 0; @@ -5525,7 +5566,24 @@ mrb_parser_parse(parser_state *p, mrbc_context *c) p->lex_strterm = NULL; parser_init_cxt(p, c); - if (yyparse(p) != 0 || p->nerr > 0) { + + if (p->mrb->jmp) { + n = yyparse(p); + } + else { + struct mrb_jmpbuf buf2; + + p->mrb->jmp = &buf2; + MRB_TRY(p->mrb->jmp) { + n = yyparse(p); + } + MRB_CATCH(p->mrb->jmp) { + p->nerr++; + } + MRB_END_EXC(p->mrb->jmp); + p->mrb->jmp = 0; + } + if (n != 0 || p->nerr > 0) { p->tree = 0; return; } @@ -5536,7 +5594,6 @@ mrb_parser_parse(parser_state *p, mrbc_context *c) if (c && c->dump_result) { mrb_parser_dump(p->mrb, p->tree, 0); } - } MRB_CATCH(p->jmp) { yyerror(p, "memory allocation error"); @@ -5720,6 +5777,7 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c) return mrb_undef_value(); } if (!p->tree || p->nerr) { + c->parser_nerr = p->nerr; if (p->capture_errors) { char buf[256]; int n; @@ -5731,7 +5789,9 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c) return mrb_undef_value(); } else { - mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error")); + if (mrb->exc == NULL) { + mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error")); + } mrb_parser_free(p); return mrb_undef_value(); } @@ -5739,7 +5799,9 @@ mrb_load_exec(mrb_state *mrb, struct mrb_parser_state *p, mrbc_context *c) proc = mrb_generate_code(mrb, p); mrb_parser_free(p); if (proc == NULL) { - mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error")); + if (mrb->exc == NULL) { + mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error")); + } return mrb_undef_value(); } if (c) { diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index 7e101cf65..7741e515d 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -201,7 +201,7 @@ module Enumerable ary.push([block.call(e), i]) } if ary.size > 1 - __sort_sub__(ary, ::Array.new(ary.size), 0, 0, ary.size - 1) do |a,b| + __sort_sub__(ary, 0, ary.size - 1) do |a,b| a <=> b end end diff --git a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb index b650072e2..8a7ac7fdc 100644 --- a/mrbgems/mruby-enum-lazy/mrblib/lazy.rb +++ b/mrbgems/mruby-enum-lazy/mrblib/lazy.rb @@ -69,7 +69,7 @@ class Enumerator def reject(&block) Lazy.new(self){|yielder, val| - if not block.call(val) + unless block.call(val) yielder << val end } diff --git a/mrbgems/mruby-enumerator/mrblib/enumerator.rb b/mrbgems/mruby-enumerator/mrblib/enumerator.rb index c7b78be5c..a60f19aaf 100644 --- a/mrbgems/mruby-enumerator/mrblib/enumerator.rb +++ b/mrbgems/mruby-enumerator/mrblib/enumerator.rb @@ -612,7 +612,7 @@ module Kernel def to_enum(meth=:each, *args) Enumerator.new self, meth, *args end - alias :enum_for :to_enum + alias enum_for to_enum end module Enumerable diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c index 5e8952f0d..146c6df08 100644 --- a/mrbgems/mruby-eval/src/eval.c +++ b/mrbgems/mruby-eval/src/eval.c @@ -4,6 +4,7 @@ #include <mruby/irep.h> #include <mruby/proc.h> #include <mruby/opcode.h> +#include <mruby/error.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); @@ -30,7 +31,7 @@ get_closure_irep(mrb_state *mrb, int level) if (!e) return NULL; if (!MRB_ENV_STACK_SHARED_P(e)) return NULL; - + c = e->cxt.c; proc = c->cibase[e->cioff].proc; if (!proc || MRB_PROC_CFUNC_P(proc)) { @@ -39,6 +40,25 @@ get_closure_irep(mrb_state *mrb, int level) return proc->body.irep; } +/* search for irep lev above the bottom */ +static mrb_irep* +search_irep(mrb_irep *top, int bnest, int lev, mrb_irep *bottom) +{ + size_t i; + + for (i=0; i<top->rlen; i++) { + mrb_irep* tmp = top->reps[i]; + + if (tmp == bottom) return top; + tmp = search_irep(tmp, bnest-1, lev, bottom); + if (tmp) { + if (bnest == lev) return top; + return tmp; + } + } + return NULL; +} + static inline mrb_code search_variable(mrb_state *mrb, mrb_sym vsym, int bnest) { @@ -60,6 +80,20 @@ search_variable(mrb_state *mrb, mrb_sym vsym, int bnest) return 0; } +static int +irep_argc(mrb_irep *irep) +{ + mrb_code c; + + c = irep->iseq[0]; + if (GET_OPCODE(c) == OP_ENTER) { + mrb_aspec ax = GETARG_Ax(c); + /* extra 1 means a slot for block */ + return MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1; + } + return 0; +} + static mrb_bool potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals) { @@ -70,32 +104,24 @@ potential_upvar_p(struct mrb_locals *lv, uint16_t v, int argc, uint16_t nlocals) } static void -patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest) +patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest, mrb_irep *top) { size_t i; mrb_code c; - int argc = 0; + int argc = irep_argc(irep); for (i = 0; i < irep->ilen; i++) { c = irep->iseq[i]; switch(GET_OPCODE(c)){ - case OP_ENTER: - { - mrb_aspec ax = GETARG_Ax(c); - /* extra 1 means a slot for block */ - argc = MRB_ASPEC_REQ(ax)+MRB_ASPEC_OPT(ax)+MRB_ASPEC_REST(ax)+MRB_ASPEC_POST(ax)+1; - } - break; - case OP_EPUSH: - patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1); + patch_irep(mrb, irep->reps[GETARG_Bx(c)], bnest + 1, top); break; case OP_LAMBDA: { int arg_c = GETARG_c(c); if (arg_c & OP_L_CAPTURE) { - patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1); + patch_irep(mrb, irep->reps[GETARG_b(c)], bnest + 1, top); } } break; @@ -132,6 +158,34 @@ patch_irep(mrb_state *mrb, mrb_irep *irep, int bnest) } break; + case OP_GETUPVAR: + { + int lev = GETARG_C(c)+1; + mrb_irep *tmp = search_irep(top, bnest, lev, irep); + if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) { + mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest); + if (arg != 0) { + /* must replace */ + irep->iseq[i] = MKOPCODE(OP_GETUPVAR) | MKARG_A(GETARG_A(c)) | arg; + } + } + } + break; + + case OP_SETUPVAR: + { + int lev = GETARG_C(c)+1; + mrb_irep *tmp = search_irep(top, bnest, lev, irep); + if (potential_upvar_p(tmp->lv, GETARG_B(c), irep_argc(tmp), tmp->nlocals)) { + mrb_code arg = search_variable(mrb, tmp->lv[GETARG_B(c)-1].name, bnest); + if (arg != 0) { + /* must replace */ + irep->iseq[i] = MKOPCODE(OP_SETUPVAR) | MKARG_A(GETARG_A(c)) | arg; + } + } + } + break; + case OP_STOP: if (mrb->c->ci->acc >= 0) { irep->iseq[i] = MKOP_AB(OP_RETURN, irep->nlocals, OP_R_NORMAL); @@ -159,10 +213,7 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, con cxt = mrbc_context_new(mrb); cxt->lineno = line; - if (!file) { - file = "(eval)"; - } - mrbc_filename(mrb, cxt, file); + mrbc_filename(mrb, cxt, file ? file : "(eval)"); cxt->capture_errors = TRUE; cxt->no_optimize = TRUE; @@ -175,12 +226,22 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, con if (0 < p->nerr) { /* parse error */ - char buf[256]; - int n; - n = snprintf(buf, sizeof(buf), "line %d: %s\n", p->error_buffer[0].lineno, p->error_buffer[0].message); + mrb_value str; + + if (file) { + str = mrb_format(mrb, " file %S line %S: %S", + mrb_str_new_cstr(mrb, file), + mrb_fixnum_value(p->error_buffer[0].lineno), + mrb_str_new_cstr(mrb, p->error_buffer[0].message)); + } + else { + str = mrb_format(mrb, " line %S: %S", + mrb_fixnum_value(p->error_buffer[0].lineno), + mrb_str_new_cstr(mrb, p->error_buffer[0].message)); + } mrb_parser_free(p); mrbc_context_free(mrb, cxt); - mrb_exc_raise(mrb, mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n)); + mrb_exc_raise(mrb, mrb_exc_new_str(mrb, E_SYNTAX_ERROR, str)); } proc = mrb_generate_code(mrb, p); @@ -196,14 +257,15 @@ create_proc_from_string(mrb_state *mrb, char *s, int len, mrb_value binding, con e = c->ci[-1].proc->env; if (!e) e = c->ci[-1].env; e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)e); - e->mid = c->ci[-1].mid; - e->cioff = c->ci - c->cibase - 1; + e->cxt.c = c; + e->cioff = c->ci - c->cibase; e->stack = c->ci->stackent; - MRB_SET_ENV_STACK_LEN(e, c->ci[-1].proc->body.irep->nlocals); + MRB_SET_ENV_STACK_LEN(e, c->ci->proc->body.irep->nlocals); c->ci->target_class = proc->target_class; c->ci->env = 0; proc->env = e; - patch_irep(mrb, proc->body.irep, 0); + patch_irep(mrb, proc->body.irep, 0, proc->body.irep); + /* mrb_codedump_all(mrb, proc); */ mrb_parser_free(p); mrbc_context_free(mrb, cxt); @@ -247,7 +309,7 @@ f_instance_eval(mrb_state *mrb, mrb_value self) mrb_value b; mrb_int argc; mrb_value *argv; - mrb_get_args(mrb, "*&", &argv, &argc, &b); + mrb_get_args(mrb, "*!&", &argv, &argc, &b); if (mrb_nil_p(b)) { char *s; diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c index 232c925d3..9de175f34 100644 --- a/mrbgems/mruby-fiber/src/fiber.c +++ b/mrbgems/mruby-fiber/src/fiber.c @@ -74,6 +74,9 @@ fiber_init(mrb_state *mrb, mrb_value self) mrb_get_args(mrb, "&", &blk); + if (f->cxt) { + mrb_raise(mrb, E_RUNTIME_ERROR, "cannot initialize twice"); + } if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Fiber object without a block"); } @@ -82,9 +85,9 @@ fiber_init(mrb_state *mrb, mrb_value self) mrb_raise(mrb, E_FIBER_ERROR, "tried to create Fiber from C defined method"); } - f->cxt = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); - *f->cxt = mrb_context_zero; - c = f->cxt; + c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); + *c = mrb_context_zero; + f->cxt = c; /* initialize VM stack */ slen = FIBER_STACK_INIT_SIZE; @@ -122,6 +125,7 @@ fiber_init(mrb_state *mrb, mrb_value self) ci = c->ci; ci->target_class = p->target_class; ci->proc = p; + mrb_field_write_barrier(mrb, (struct RBasic*)mrb_obj_ptr(self), (struct RBasic*)p); ci->pc = p->body.irep->iseq; ci->nregs = p->body.irep->nregs; ci[1] = ci[0]; @@ -168,6 +172,16 @@ fiber_check_cfunc(mrb_state *mrb, struct mrb_context *c) } } +static void +fiber_switch_context(mrb_state *mrb, struct mrb_context *c) +{ + if (mrb->c->fib) { + mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib); + } + c->status = MRB_FIBER_RUNNING; + mrb->c = c; +} + static mrb_value fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mrb_bool resume, mrb_bool vmexec) { @@ -188,9 +202,13 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED; c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c); if (c->status == MRB_FIBER_CREATED) { - mrb_value *b = c->stack+1; - mrb_value *e = b + len; + mrb_value *b, *e; + if (len >= c->stend - c->stack) { + mrb_raise(mrb, E_FIBER_ERROR, "too many arguments to fiber"); + } + b = c->stack+1; + e = b + len; while (b<e) { *b++ = *a++; } @@ -200,9 +218,7 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr else { value = fiber_result(mrb, a, len); } - mrb_write_barrier(mrb, (struct RBasic*)c->fib); - c->status = MRB_FIBER_RUNNING; - mrb->c = c; + fiber_switch_context(mrb, c); if (vmexec) { c->vmexec = TRUE; @@ -237,7 +253,7 @@ fiber_resume(mrb_state *mrb, mrb_value self) mrb_int len; mrb_bool vmexec = FALSE; - mrb_get_args(mrb, "*", &a, &len); + mrb_get_args(mrb, "*!", &a, &len); if (mrb->c->ci->acc < 0) { vmexec = TRUE; } @@ -297,14 +313,12 @@ fiber_transfer(mrb_state *mrb, mrb_value self) mrb_int len; fiber_check_cfunc(mrb, mrb->c); - mrb_get_args(mrb, "*", &a, &len); + mrb_get_args(mrb, "*!", &a, &len); if (c == mrb->root_c) { mrb->c->status = MRB_FIBER_TRANSFERRED; - mrb->c = c; - c->status = MRB_FIBER_RUNNING; + fiber_switch_context(mrb, c); MARK_CONTEXT_MODIFY(c); - mrb_write_barrier(mrb, (struct RBasic*)c->fib); return fiber_result(mrb, a, len); } @@ -326,15 +340,15 @@ mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a) mrb_raise(mrb, E_FIBER_ERROR, "can't yield from root fiber"); } + fiber_check_cfunc(mrb, c); c->prev->status = MRB_FIBER_RUNNING; c->status = MRB_FIBER_SUSPENDED; - mrb->c = c->prev; + fiber_switch_context(mrb, c->prev); c->prev = NULL; if (c->vmexec) { c->vmexec = FALSE; mrb->c->ci->acc = CI_ACC_RESUMED; } - mrb_write_barrier(mrb, (struct RBasic*)c->fib); MARK_CONTEXT_MODIFY(mrb->c); return fiber_result(mrb, a, len); } @@ -357,7 +371,7 @@ fiber_yield(mrb_state *mrb, mrb_value self) mrb_value *a; mrb_int len; - mrb_get_args(mrb, "*", &a, &len); + mrb_get_args(mrb, "*!", &a, &len); return mrb_fiber_yield(mrb, len, a); } diff --git a/mrbgems/mruby-hash-ext/mrbgem.rake b/mrbgems/mruby-hash-ext/mrbgem.rake index f01033a6c..103410ab9 100644 --- a/mrbgems/mruby-hash-ext/mrbgem.rake +++ b/mrbgems/mruby-hash-ext/mrbgem.rake @@ -2,6 +2,7 @@ MRuby::Gem::Specification.new('mruby-hash-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Hash class extension' - spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext' - spec.add_dependency 'mruby-array-ext', :core => 'mruby-array-ext' + spec.add_dependency 'mruby-enum-ext', core: 'mruby-enum-ext' + spec.add_dependency 'mruby-array-ext', core: 'mruby-array-ext' + spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator' end diff --git a/mrbgems/mruby-hash-ext/mrblib/hash.rb b/mrbgems/mruby-hash-ext/mrblib/hash.rb index 31ff6d685..87817f833 100644 --- a/mrbgems/mruby-hash-ext/mrblib/hash.rb +++ b/mrbgems/mruby-hash-ext/mrblib/hash.rb @@ -382,4 +382,80 @@ class Hash n end end + + ## + # call-seq: + # hsh.transform_keys {|key| block } -> new_hash + # hsh.transform_keys -> an_enumerator + # + # Returns a new hash, with the keys computed from running the block + # once for each key in the hash, and the values unchanged. + # + # If no block is given, an enumerator is returned instead. + # + def transform_keys(&b) + return to_enum :transform_keys unless block_given? + hash = {} + self.keys.each do |k| + new_key = yield(k) + hash[new_key] = self[k] + end + hash + end + ## + # call-seq: + # hsh.transform_keys! {|key| block } -> hsh + # hsh.transform_keys! -> an_enumerator + # + # Invokes the given block once for each key in <i>hsh</i>, replacing it + # with the new key returned by the block, and then returns <i>hsh</i>. + # + # If no block is given, an enumerator is returned instead. + # + def transform_keys!(&b) + return to_enum :transform_keys! unless block_given? + self.keys.each do |k| + value = self[k] + new_key = yield(k) + self.__delete(k) + self[new_key] = value + end + self + end + ## + # call-seq: + # hsh.transform_values {|value| block } -> new_hash + # hsh.transform_values -> an_enumerator + # + # Returns a new hash with the results of running the block once for + # every value. + # This method does not change the keys. + # + # If no block is given, an enumerator is returned instead. + # + def transform_values(&b) + return to_enum :transform_values unless block_given? + hash = {} + self.keys.each do |k| + hash[k] = yield(self[k]) + end + hash + end + ## + # call-seq: + # hsh.transform_values! {|key| block } -> hsh + # hsh.transform_values! -> an_enumerator + # + # Invokes the given block once for each value in the hash, replacing + # with the new value returned by the block, and then returns <i>hsh</i>. + # + # If no block is given, an enumerator is returned instead. + # + def transform_values!(&b) + return to_enum :transform_values! unless block_given? + self.keys.each do |k| + self[k] = yield(self[k]) + end + self + end end diff --git a/mrbgems/mruby-hash-ext/src/hash-ext.c b/mrbgems/mruby-hash-ext/src/hash-ext.c index 9560829b6..61abc080d 100644 --- a/mrbgems/mruby-hash-ext/src/hash-ext.c +++ b/mrbgems/mruby-hash-ext/src/hash-ext.c @@ -26,7 +26,7 @@ hash_values_at(mrb_state *mrb, mrb_value hash) mrb_int argc, i; int ai; - mrb_get_args(mrb, "*", &argv, &argc); + mrb_get_args(mrb, "*!", &argv, &argc); result = mrb_ary_new_capa(mrb, argc); ai = mrb_gc_arena_save(mrb); for (i = 0; i < argc; i++) { diff --git a/mrbgems/mruby-hash-ext/test/hash.rb b/mrbgems/mruby-hash-ext/test/hash.rb index 6e70953f5..2ae88c307 100644 --- a/mrbgems/mruby-hash-ext/test/hash.rb +++ b/mrbgems/mruby-hash-ext/test/hash.rb @@ -254,3 +254,27 @@ assert("Hash#dig") do assert_equal(1, h.dig(:a, :b, :c)) assert_nil(h.dig(:d)) end + +assert("Hash#transform_keys") do + h = {"1" => 100, "2" => 200} + assert_equal(h.transform_keys{|k| k+"!"}, + {"1!" => 100, "2!" => 200}) + assert_equal(h.transform_keys{|k|k.to_i}, + {1 => 100, 2 => 200}) + assert_equal(h.transform_keys.with_index{|k, i| "#{k}.#{i}"}, + {"1.0" => 100, "2.1" => 200}) + assert_equal(h.transform_keys!{|k|k.to_i}, h) + assert_equal(h, {1 => 100, 2 => 200}) +end + +assert("Hash#transform_values") do + h = {a: 1, b: 2, c: 3} + assert_equal(h.transform_values{|v| v * v + 1}, + {a: 2, b: 5, c: 10}) + assert_equal(h.transform_values{|v|v.to_s}, + {a: "1", b: "2", c: "3"}) + assert_equal(h.transform_values.with_index{|v, i| "#{v}.#{i}"}, + {a: "1.0", b: "2.1", c: "3.2"}) + assert_equal(h.transform_values!{|v|v.to_s}, h) + assert_equal(h, {a: "1", b: "2", c: "3"}) +end diff --git a/mrbgems/mruby-kernel-ext/test/kernel.rb b/mrbgems/mruby-kernel-ext/test/kernel.rb index 9828538f0..89cedaf27 100644 --- a/mrbgems/mruby-kernel-ext/test/kernel.rb +++ b/mrbgems/mruby-kernel-ext/test/kernel.rb @@ -20,13 +20,13 @@ assert('Kernel.caller, Kernel#caller') do bar(*args) end end - assert_equal "kernel.rb:#{caller_lineno}:in Object#foo", c.new.baz(0)[0][-26..-1] - 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_equal "kernel.rb:#{caller_lineno}:in foo", c.new.baz(0)[0][-19..-1] + assert_equal "bar", c.new.baz[0][-3..-1] + assert_equal "foo", c.new.baz(0)[0][-3..-1] + assert_equal "bar", c.new.baz(1)[0][-3..-1] + assert_equal "baz", c.new.baz(2)[0][-3..-1] + assert_equal ["foo", "bar"], c.new.baz(0, 2).map { |i| i[-3..-1] } + assert_equal ["bar", "baz"], c.new.baz(1..2).map { |i| i[-3..-1] } assert_nil c.new.baz(10..20) assert_raise(ArgumentError) { c.new.baz(-1) } assert_raise(ArgumentError) { c.new.baz(-1, 1) } diff --git a/mrbgems/mruby-object-ext/src/object.c b/mrbgems/mruby-object-ext/src/object.c index 8a2325ef9..35a07b584 100644 --- a/mrbgems/mruby-object-ext/src/object.c +++ b/mrbgems/mruby-object-ext/src/object.c @@ -1,6 +1,7 @@ #include <mruby.h> #include <mruby/array.h> #include <mruby/class.h> +#include <mruby/proc.h> /* * call-seq: @@ -66,7 +67,6 @@ mrb_obj_instance_exec(mrb_state *mrb, mrb_value self) mrb_int argc; mrb_value blk; struct RClass *c; - mrb_value args; mrb_get_args(mrb, "*&", &argv, &argc, &blk); @@ -84,9 +84,8 @@ mrb_obj_instance_exec(mrb_state *mrb, mrb_value self) c = mrb_class_ptr(mrb_singleton_class(mrb, self)); break; } - args = mrb_ary_new_from_values(mrb, argc, argv); - argv = RARRAY_PTR(args); - return mrb_yield_with_class(mrb, blk, argc, argv, self, c); + mrb->c->ci->target_class = c; + return mrb_yield_cont(mrb, blk, self, argc, argv); } void diff --git a/mrbgems/mruby-objectspace/src/mruby_objectspace.c b/mrbgems/mruby-objectspace/src/mruby_objectspace.c index d0a8effd0..3887091d3 100644 --- a/mrbgems/mruby-objectspace/src/mruby_objectspace.c +++ b/mrbgems/mruby-objectspace/src/mruby_objectspace.c @@ -9,7 +9,7 @@ struct os_count_struct { mrb_int counts[MRB_TT_MAXDEFINE+1]; }; -static void +static int os_count_object_type(mrb_state *mrb, struct RBasic *obj, void *data) { struct os_count_struct *obj_count; @@ -23,6 +23,7 @@ os_count_object_type(mrb_state *mrb, struct RBasic *obj, void *data) else { obj_count->counts[obj->tt]++; } + return MRB_EACH_OBJ_OK; } /* @@ -109,35 +110,36 @@ struct os_each_object_data { mrb_int count; }; -static void +static int os_each_object_cb(mrb_state *mrb, struct RBasic *obj, void *ud) { struct os_each_object_data *d = (struct os_each_object_data*)ud; /* filter dead objects */ if (mrb_object_dead_p(mrb, obj)) { - return; + return MRB_EACH_OBJ_OK; } /* filter internal objects */ switch (obj->tt) { case MRB_TT_ENV: case MRB_TT_ICLASS: - return; + return MRB_EACH_OBJ_OK; default: break; } /* filter half baked (or internal) objects */ - if (!obj->c) return; + if (!obj->c) return MRB_EACH_OBJ_OK; /* filter class kind if target module defined */ if (d->target_module && !mrb_obj_is_kind_of(mrb, mrb_obj_value(obj), d->target_module)) { - return; + return MRB_EACH_OBJ_OK; } mrb_yield(mrb, d->block, mrb_obj_value(obj)); ++d->count; + return MRB_EACH_OBJ_OK; } /* diff --git a/mrbgems/mruby-proc-ext/src/proc.c b/mrbgems/mruby-proc-ext/src/proc.c index f7f031e7b..b13606f5f 100644 --- a/mrbgems/mruby-proc-ext/src/proc.c +++ b/mrbgems/mruby-proc-ext/src/proc.c @@ -52,10 +52,7 @@ mrb_proc_inspect(mrb_state *mrb, mrb_value self) line = mrb_debug_get_line(irep, 0); if (line != -1) { - char buf[32]; - - snprintf(buf, sizeof(buf), "%" PRId32, line); - mrb_str_cat_cstr(mrb, str, buf); + str = mrb_format(mrb, "%S:%S", str, mrb_fixnum_value(line)); } else { mrb_str_cat_lit(mrb, str, "-"); diff --git a/mrbgems/mruby-random/src/random.c b/mrbgems/mruby-random/src/random.c index f81d38fe6..b865244cc 100644 --- a/mrbgems/mruby-random/src/random.c +++ b/mrbgems/mruby-random/src/random.c @@ -217,13 +217,15 @@ mrb_ary_shuffle_bang(mrb_state *mrb, mrb_value ary) for (i = RARRAY_LEN(ary) - 1; i > 0; i--) { mrb_int j; + mrb_value *ptr = RARRAY_PTR(ary); mrb_value tmp; + j = mrb_fixnum(mrb_random_mt_rand(mrb, random, mrb_fixnum_value(RARRAY_LEN(ary)))); - tmp = RARRAY_PTR(ary)[i]; - mrb_ary_ptr(ary)->ptr[i] = RARRAY_PTR(ary)[j]; - mrb_ary_ptr(ary)->ptr[j] = tmp; + tmp = ptr[i]; + ptr[i] = ptr[j]; + ptr[j] = tmp; } } diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index 3d96573a7..7b8201736 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -116,7 +116,6 @@ mrb_fix2binstr(mrb_state *mrb, mrb_value x, int base) #define CHECK(l) do {\ /* int cr = ENC_CODERANGE(result);*/\ - if ((l) < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "illegal specifier"); \ while ((l) >= bsiz - blen) {\ bsiz*=2;\ if (bsiz < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "too big specifier"); \ @@ -529,7 +528,6 @@ mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt) mrb_int n; mrb_int width; mrb_int prec; - int flags = FNONE; int nextarg = 1; int posarg = 0; mrb_value nextvalue; @@ -565,6 +563,7 @@ mrb_str_format(mrb_state *mrb, int argc, const mrb_value *argv, mrb_value fmt) for (; p < end; p++) { const char *t; mrb_sym id = 0; + int flags = FNONE; for (t = p; t < end && *t != '%'; t++) ; if (t + 1 == end) ++t; @@ -702,35 +701,39 @@ retry: tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { - if (mrb_fixnum(mrb_funcall(mrb, tmp, "size", 0)) != 1 ) { + if (RSTRING_LEN(tmp) != 1) { mrb_raise(mrb, E_ARGUMENT_ERROR, "%c requires a character"); } } else if (mrb_fixnum_p(val)) { - tmp = mrb_funcall(mrb, val, "chr", 0); + mrb_int n = mrb_fixnum(val); + + if (n < 0x80) { + char buf[1]; + + buf[0] = (char)n; + tmp = mrb_str_new(mrb, buf, 1); + } + else { + tmp = mrb_funcall(mrb, val, "chr", 0); + mrb_check_type(mrb, tmp, MRB_TT_STRING); + } } 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)) { - CHECK(n); - memcpy(buf+blen, c, n); - blen += n; + PUSH(c, n); } else if ((flags & FMINUS)) { - CHECK(n); - memcpy(buf+blen, c, n); - blen += n; + PUSH(c, n); if (width>0) FILL(' ', width-1); } else { if (width>0) FILL(' ', width-1); - CHECK(n); - memcpy(buf+blen, c, n); - blen += n; + PUSH(c, n); } } break; @@ -768,19 +771,11 @@ retry: if ((flags&FWIDTH) && (width > slen)) { width -= (int)slen; if (!(flags&FMINUS)) { - CHECK(width); - while (width-- > 0) { - buf[blen++] = ' '; - } + FILL(' ', width); } - CHECK(len); - memcpy(&buf[blen], RSTRING_PTR(str), len); - blen += len; + PUSH(RSTRING_PTR(str), len); if (flags&FMINUS) { - CHECK(width); - while (width-- > 0) { - buf[blen++] = ' '; - } + FILL(' ', width); } break; } @@ -806,13 +801,6 @@ retry: int base; mrb_int len; - switch (*p) { - case 'd': - case 'i': - sign = 1; break; - default: - break; - } if (flags & FSHARP) { switch (*p) { case 'o': prefix = "0"; break; @@ -853,21 +841,13 @@ retry: case 'u': case 'd': case 'i': + sign = 1; default: base = 10; break; } - if (base == 2) { - if (v < 0 && !sign) { - val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base); - dots = 1; - } - else { - val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base); - } - } if (sign) { - if (v > 0) { + if (v >= 0) { if (flags & FPLUS) { sc = '+'; width--; @@ -877,37 +857,33 @@ retry: width--; } } - switch (base) { - case 2: - strncpy(nbuf, RSTRING_PTR(val), sizeof(nbuf)); - break; - case 8: - snprintf(nbuf, sizeof(nbuf), "%" MRB_PRIo, v); - break; - case 10: - snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v); - break; - case 16: - snprintf(nbuf, sizeof(nbuf), "%" MRB_PRIx, v); - break; + else { + sc = '-'; + width--; } + mrb_assert(base == 10); + snprintf(nbuf, sizeof(nbuf), "%" MRB_PRId, v); s = nbuf; + if (v < 0) s++; /* skip minus sign */ } else { s = nbuf; - if (base != 10 && v < 0) { + if (v < 0) { dots = 1; } switch (base) { case 2: + if (v < 0) { + val = mrb_fix2binstr(mrb, mrb_fixnum_value(v), base); + } + else { + val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base); + } strncpy(++s, RSTRING_PTR(val), sizeof(nbuf)-1); break; case 8: snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIo, v); break; - case 10: - snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRId, v); - break; case 16: snprintf(++s, sizeof(nbuf)-1, "%" MRB_PRIx, v); break; @@ -935,11 +911,6 @@ retry: len = (mrb_int)size; } - if (dots) { - prec -= 2; - width -= 2; - } - if (*p == 'X') { char *pp = s; int c; @@ -986,10 +957,8 @@ retry: } if (!(flags&FMINUS) && width > 0) { - CHECK(width); - while (width-- > 0) { - buf[blen++] = ' '; - } + FILL(' ', width); + width = 0; } if (sc) PUSH(&sc, 1); @@ -998,28 +967,25 @@ retry: int plen = (int)strlen(prefix); PUSH(prefix, plen); } - CHECK(prec - len); - if (dots) PUSH("..", 2); - - if (v < 0) { - char c = sign_bits(base, p); - while (len < prec--) { - buf[blen++] = c; - } + if (dots) { + prec -= 2; + width -= 2; + PUSH("..", 2); } - else if ((flags & (FMINUS|FPREC)) != FMINUS) { - char c = '0'; - while (len < prec--) { - buf[blen++] = c; + + if (prec > len) { + CHECK(prec - len); + if ((flags & (FMINUS|FPREC)) != FMINUS) { + char c = '0'; + FILL(c, prec - len); + } else if (v < 0) { + char c = sign_bits(base, p); + FILL(c, prec - len); } } - PUSH(s, len); if (width > 0) { - CHECK(width); - while (width-- > 0) { - buf[blen++] = ' '; - } + FILL(' ', width); } } break; @@ -1040,6 +1006,7 @@ retry: if (!isfinite(fval)) { const char *expr; const int elen = 3; + char sign = '\0'; if (isnan(fval)) { expr = "NaN"; @@ -1048,35 +1015,29 @@ retry: expr = "Inf"; } need = elen; - if ((!isnan(fval) && fval < 0.0) || (flags & FPLUS)) - need++; + if (!isnan(fval) && fval < 0.0) + sign = '-'; + else if (flags & (FPLUS|FSPACE)) + sign = (flags & FPLUS) ? '+' : ' '; + if (sign) + ++need; if ((flags & FWIDTH) && need < width) need = width; - CHECK(need + 1); - n = snprintf(&buf[blen], need + 1, "%*s", need, ""); - if (n < 0) { - mrb_raise(mrb, E_RUNTIME_ERROR, "formatting error"); + if (need < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "width too big"); } + FILL(' ', need); if (flags & FMINUS) { - if (!isnan(fval) && fval < 0.0) - buf[blen++] = '-'; - else if (flags & FPLUS) - buf[blen++] = '+'; - else if (flags & FSPACE) - blen++; - memcpy(&buf[blen], expr, elen); + if (sign) + buf[blen - need--] = sign; + memcpy(&buf[blen - need], expr, elen); } else { - if (!isnan(fval) && fval < 0.0) - buf[blen + need - elen - 1] = '-'; - else if (flags & FPLUS) - buf[blen + need - elen - 1] = '+'; - else if ((flags & FSPACE) && need > width) - blen++; - memcpy(&buf[blen + need - elen], expr, elen); + if (sign) + buf[blen - elen - 1] = sign; + memcpy(&buf[blen - elen], expr, elen); } - blen += strlen(&buf[blen]); break; } @@ -1092,6 +1053,10 @@ retry: if ((flags&FWIDTH) && need < width) need = width; need += 20; + if (need <= 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, + (width > prec ? "width too big" : "prec too big")); + } CHECK(need); n = snprintf(&buf[blen], need, fbuf, fval); diff --git a/mrbgems/mruby-sprintf/test/sprintf.rb b/mrbgems/mruby-sprintf/test/sprintf.rb index 178904d60..84dba9d19 100644 --- a/mrbgems/mruby-sprintf/test/sprintf.rb +++ b/mrbgems/mruby-sprintf/test/sprintf.rb @@ -8,6 +8,62 @@ assert('String#%') do assert_equal 15, ("%b" % (1<<14)).size end +assert('String#% with inf') do + inf = Float::INFINITY + + assert_equal "Inf", "%f" % inf + assert_equal "Inf", "%2f" % inf + assert_equal "Inf", "%3f" % inf + assert_equal " Inf", "%4f" % inf + assert_equal " Inf", "%5f" % inf + + assert_equal "+Inf", "%+f" % inf + assert_equal "+Inf", "%+2f" % inf + assert_equal "+Inf", "%+3f" % inf + assert_equal "+Inf", "%+4f" % inf + assert_equal " +Inf", "%+5f" % inf + + assert_equal "Inf", "%-f" % inf + assert_equal "Inf", "%-2f" % inf + assert_equal "Inf", "%-3f" % inf + assert_equal "Inf ", "%-4f" % inf + assert_equal "Inf ", "%-5f" % inf + + assert_equal " Inf", "% f" % inf + assert_equal " Inf", "% 2f" % inf + assert_equal " Inf", "% 3f" % inf + assert_equal " Inf", "% 4f" % inf + assert_equal " Inf", "% 5f" % inf +end + +assert('String#% with nan') do + nan = Float::NAN + + assert_equal "NaN", "%f" % nan + assert_equal "NaN", "%2f" % nan + assert_equal "NaN", "%3f" % nan + assert_equal " NaN", "%4f" % nan + assert_equal " NaN", "%5f" % nan + + assert_equal "+NaN", "%+f" % nan + assert_equal "+NaN", "%+2f" % nan + assert_equal "+NaN", "%+3f" % nan + assert_equal "+NaN", "%+4f" % nan + assert_equal " +NaN", "%+5f" % nan + + assert_equal "NaN", "%-f" % nan + assert_equal "NaN", "%-2f" % nan + assert_equal "NaN", "%-3f" % nan + assert_equal "NaN ", "%-4f" % nan + assert_equal "NaN ", "%-5f" % nan + + assert_equal " NaN", "% f" % nan + assert_equal " NaN", "% 2f" % nan + assert_equal " NaN", "% 3f" % nan + assert_equal " NaN", "% 4f" % nan + assert_equal " NaN", "% 5f" % nan +end + assert("String#% with invalid chr") do begin class Fixnum @@ -19,7 +75,7 @@ assert("String#% with invalid chr") do end assert_raise TypeError do - "%c" % 0 + "%c" % 0x80 end ensure class Fixnum @@ -31,6 +87,16 @@ assert("String#% with invalid chr") do end end +assert("String#% %b") do + assert_equal("..10115", "%0b5" % -5) +end + +assert("String#% %d") do + assert_equal(" 10", "%4d" % 10) + assert_equal("1000", "%4d" % 1000) + assert_equal("100000", "%4d" % 100000) +end + assert("String#% invalid format") do assert_raise ArgumentError do "%?" % "" diff --git a/mrbgems/mruby-string-ext/mrbgem.rake b/mrbgems/mruby-string-ext/mrbgem.rake index f2df5a783..9812f2cc9 100644 --- a/mrbgems/mruby-string-ext/mrbgem.rake +++ b/mrbgems/mruby-string-ext/mrbgem.rake @@ -2,4 +2,5 @@ MRuby::Gem::Specification.new('mruby-string-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'String class extension' + spec.add_test_dependency 'mruby-enumerator', core: 'mruby-enumerator' end diff --git a/mrbgems/mruby-string-ext/mrblib/string.rb b/mrbgems/mruby-string-ext/mrblib/string.rb index 15f0549f3..c3a7eb380 100644 --- a/mrbgems/mruby-string-ext/mrblib/string.rb +++ b/mrbgems/mruby-string-ext/mrblib/string.rb @@ -305,50 +305,6 @@ class String padding + self end - # str.upto(other_str, exclusive=false) {|s| block } -> str - # str.upto(other_str, exclusive=false) -> an_enumerator - # - # Iterates through successive values, starting at <i>str</i> and - # ending at <i>other_str</i> inclusive, passing each value in turn to - # the block. The <code>String#succ</code> method is used to generate - # each value. If optional second argument exclusive is omitted or is false, - # the last value will be included; otherwise it will be excluded. - # - # If no block is given, an enumerator is returned instead. - # - # "a8".upto("b6") {|s| print s, ' ' } - # for s in "a8".."b6" - # print s, ' ' - # end - # - # <em>produces:</em> - # - # a8 a9 b0 b1 b2 b3 b4 b5 b6 - # a8 a9 b0 b1 b2 b3 b4 b5 b6 - # - # If <i>str</i> and <i>other_str</i> contains only ascii numeric characters, - # both are recognized as decimal numbers. In addition, the width of - # string (e.g. leading zeros) is handled appropriately. - # - # "9".upto("11").to_a #=> ["9", "10", "11"] - # "25".upto("5").to_a #=> [] - # "07".upto("11").to_a #=> ["07", "08", "09", "10", "11"] - # - def upto(other_str, excl=false, &block) - return to_enum :upto, other_str, excl unless block - - str = self - n = self.<=>other_str - return self if n > 0 || (self == other_str && excl) - while true - block.call(str) - return self if !excl && str == other_str - str = str.succ - return self if excl && str == other_str - return self if str.size > other_str.size - end - end - def chars(&block) if block_given? self.split('').each do |i| diff --git a/mrbgems/mruby-string-ext/src/string.c b/mrbgems/mruby-string-ext/src/string.c index 402dbf987..0d834b6ef 100644 --- a/mrbgems/mruby-string-ext/src/string.c +++ b/mrbgems/mruby-string-ext/src/string.c @@ -134,6 +134,8 @@ mrb_str_swapcase(mrb_state *mrb, mrb_value self) return str; } +static mrb_value mrb_fixnum_chr(mrb_state *mrb, mrb_value num); + /* * call-seq: * str << integer -> str @@ -153,7 +155,12 @@ static mrb_value mrb_str_concat2(mrb_state *mrb, mrb_value self) { mrb_value str; - mrb_get_args(mrb, "S", &str); + + mrb_get_args(mrb, "o", &str); + if (mrb_fixnum_p(str)) + str = mrb_fixnum_chr(mrb, str); + else + str = mrb_string_type(mrb, str); mrb_str_concat(mrb, self, str); return self; } @@ -367,7 +374,7 @@ mrb_str_succ_bang(mrb_state *mrb, mrb_value self) unsigned char *p, *e, *b, *t; const char *prepend; struct RString *s = mrb_str_ptr(self); - size_t l; + mrb_int l; if (RSTRING_LEN(self) == 0) return self; @@ -514,6 +521,135 @@ mrb_str_ord(mrb_state* mrb, mrb_value str) } #endif +static mrb_bool +all_digits_p(const char *s, mrb_int len) +{ + while (len-- > 0) { + if (!ISDIGIT(*s)) return FALSE; + s++; + } + return TRUE; +} + +/* + * call-seq: + * str.upto(other_str, exclusive=false) {|s| block } -> str + * str.upto(other_str, exclusive=false) -> an_enumerator + * + * Iterates through successive values, starting at <i>str</i> and + * ending at <i>other_str</i> inclusive, passing each value in turn to + * the block. The <code>String#succ</code> method is used to generate + * each value. If optional second argument exclusive is omitted or is false, + * the last value will be included; otherwise it will be excluded. + * + * If no block is given, an enumerator is returned instead. + * + * "a8".upto("b6") {|s| print s, ' ' } + * for s in "a8".."b6" + * print s, ' ' + * end + * + * <em>produces:</em> + * + * a8 a9 b0 b1 b2 b3 b4 b5 b6 + * a8 a9 b0 b1 b2 b3 b4 b5 b6 + * + * If <i>str</i> and <i>other_str</i> contains only ascii numeric characters, + * both are recognized as decimal numbers. In addition, the width of + * string (e.g. leading zeros) is handled appropriately. + * + * "9".upto("11").to_a #=> ["9", "10", "11"] + * "25".upto("5").to_a #=> [] + * "07".upto("11").to_a #=> ["07", "08", "09", "10", "11"] + */ +static mrb_value +mrb_str_upto(mrb_state *mrb, mrb_value beg) +{ + mrb_value end; + mrb_value exclusive = mrb_false_value(); + mrb_value block = mrb_nil_value(); + mrb_value current, after_end; + mrb_int n; + mrb_bool excl; + + mrb_get_args(mrb, "o|o&", &end, &exclusive, &block); + + if (mrb_nil_p(block)) { + return mrb_funcall(mrb, beg, "to_enum", 3, mrb_symbol_value(mrb_intern_lit(mrb, "upto")), end, exclusive); + } + end = mrb_string_type(mrb, end); + excl = mrb_test(exclusive); + + /* single character */ + if (RSTRING_LEN(beg) == 1 && RSTRING_LEN(end) == 1 && + ISASCII(RSTRING_PTR(beg)[0]) && ISASCII(RSTRING_PTR(end)[0])) { + char c = RSTRING_PTR(beg)[0]; + char e = RSTRING_PTR(end)[0]; + int ai = mrb_gc_arena_save(mrb); + + if (c > e || (excl && c == e)) return beg; + for (;;) { + mrb_yield(mrb, block, mrb_str_new(mrb, &c, 1)); + mrb_gc_arena_restore(mrb, ai); + if (!excl && c == e) break; + c++; + if (excl && c == e) break; + } + return beg; + } + /* both edges are all digits */ + if (ISDIGIT(RSTRING_PTR(beg)[0]) && ISDIGIT(RSTRING_PTR(end)[0]) && + all_digits_p(RSTRING_PTR(beg), RSTRING_LEN(beg)) && + all_digits_p(RSTRING_PTR(end), RSTRING_LEN(end))) { + mrb_int min_width = RSTRING_LEN(beg); + mrb_int bi = mrb_int(mrb, mrb_str_to_inum(mrb, beg, 10, FALSE)); + mrb_int ei = mrb_int(mrb, mrb_str_to_inum(mrb, end, 10, FALSE)); + int ai = mrb_gc_arena_save(mrb); + + while (bi <= ei) { + mrb_value ns, str; + + if (excl && bi == ei) break; + ns = mrb_format(mrb, "%S", mrb_fixnum_value(bi)); + if (min_width > RSTRING_LEN(ns)) { + str = mrb_str_new(mrb, NULL, min_width); + memset(RSTRING_PTR(str), '0', min_width-RSTRING_LEN(ns)); + memcpy(RSTRING_PTR(str)+min_width-RSTRING_LEN(ns), + RSTRING_PTR(ns), RSTRING_LEN(ns)); + } + else { + str = ns; + } + mrb_yield(mrb, block, str); + mrb_gc_arena_restore(mrb, ai); + bi++; + } + + return beg; + } + /* normal case */ + n = mrb_int(mrb, mrb_funcall(mrb, beg, "<=>", 1, end)); + if (n > 0 || (excl && n == 0)) return beg; + + after_end = mrb_funcall(mrb, end, "succ", 0); + current = mrb_str_dup(mrb, beg); + while (!mrb_str_equal(mrb, current, after_end)) { + int ai = mrb_gc_arena_save(mrb); + mrb_value next = mrb_nil_value(); + if (excl || !mrb_str_equal(mrb, current, end)) + next = mrb_funcall(mrb, current, "succ", 0); + mrb_yield(mrb, block, current); + if (mrb_nil_p(next)) break; + current = mrb_str_to_str(mrb, next); + if (excl && mrb_str_equal(mrb, current, end)) break; + if (RSTRING_LEN(current) > RSTRING_LEN(end) || RSTRING_LEN(current) == 0) + break; + mrb_gc_arena_restore(mrb, ai); + } + + return beg; +} + void mrb_mruby_string_ext_gem_init(mrb_state* mrb) { @@ -538,6 +674,7 @@ mrb_mruby_string_ext_gem_init(mrb_state* mrb) 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()); + mrb_define_method(mrb, s, "upto", mrb_str_upto, MRB_ARGS_ANY()); mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE()); } diff --git a/mrbgems/mruby-string-ext/test/string.rb b/mrbgems/mruby-string-ext/test/string.rb index 24bc859d8..2a568c7d6 100644 --- a/mrbgems/mruby-string-ext/test/string.rb +++ b/mrbgems/mruby-string-ext/test/string.rb @@ -122,12 +122,16 @@ assert('String#swapcase!') do end assert('String#concat') do - s = "Hello " - s.concat "World!" - t = "Hello " - t << "World!" - assert_equal "Hello World!", t - assert_equal "Hello World!", s + assert_equal "Hello World!", "Hello " << "World" << 33 + assert_equal "Hello World!", "Hello ".concat("World").concat(33) + + o = Object.new + def o.to_str + "to_str" + end + assert_equal "hi to_str", "hi " << o + + assert_raise(TypeError) { "".concat(Object.new) } end assert('String#casecmp') do @@ -491,6 +495,17 @@ assert('String#rjust should raise on zero width padding') do end assert('String#upto') do + assert_equal %w(a8 a9 b0 b1 b2 b3 b4 b5 b6), "a8".upto("b6").to_a + assert_equal ["9", "10", "11"], "9".upto("11").to_a + assert_equal [], "25".upto("5").to_a + assert_equal ["07", "08", "09", "10", "11"], "07".upto("11").to_a + +if UTF8STRING + assert_equal ["あ", "ぃ", "い", "ぅ", "う", "ぇ", "え", "ぉ", "お"], "あ".upto("お").to_a +end + + assert_equal ["9", ":", ";", "<", "=", ">", "?", "@", "A"], "9".upto("A").to_a + a = "aa" start = "aa" count = 0 @@ -550,6 +565,8 @@ assert('String#upto') do count += 1 }) assert_equal(2, count) + + assert_raise(TypeError) { "a".upto(:c) {} } end assert('String#ord') do diff --git a/mrbgems/mruby-struct/src/struct.c b/mrbgems/mruby-struct/src/struct.c index 93bd1e2b2..67762a948 100644 --- a/mrbgems/mruby-struct/src/struct.c +++ b/mrbgems/mruby-struct/src/struct.c @@ -13,8 +13,8 @@ #include <mruby/hash.h> #include <mruby/range.h> -#define RSTRUCT_LEN(st) mrb_ary_ptr(st)->len -#define RSTRUCT_PTR(st) mrb_ary_ptr(st)->ptr +#define RSTRUCT_LEN(st) RARRAY_LEN(st) +#define RSTRUCT_PTR(st) RARRAY_PTR(st) static struct RClass * struct_class(mrb_state *mrb) @@ -84,6 +84,16 @@ mrb_struct_s_members_m(mrb_state *mrb, mrb_value klass) return ary; } +static void +mrb_struct_modify(mrb_state *mrb, mrb_value strct) +{ + if (MRB_FROZEN_P(mrb_basic_ptr(strct))) { + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen struct"); + } + + mrb_write_barrier(mrb, mrb_basic_ptr(strct)); +} + /* 15.2.18.4.6 */ /* * call-seq: @@ -266,7 +276,6 @@ mrb_struct_s_def(mrb_state *mrb, mrb_value klass) mrb_int argc; name = mrb_nil_value(); - rest = mrb_nil_value(); mrb_get_args(mrb, "*&", &argv, &argc, &b); if (argc == 0) { /* special case to avoid crash */ rest = mrb_ary_new(mrb); @@ -336,7 +345,7 @@ mrb_struct_initialize(mrb_state *mrb, mrb_value self) mrb_value *argv; mrb_int argc; - mrb_get_args(mrb, "*", &argv, &argc); + mrb_get_args(mrb, "*!", &argv, &argc); return mrb_struct_initialize_withArg(mrb, argc, argv, self); } @@ -448,6 +457,7 @@ mrb_struct_aset_sym(mrb_state *mrb, mrb_value s, mrb_sym id, mrb_value val) ptr_members = RARRAY_PTR(members); for (i=0; i<len; i++) { if (mrb_symbol(ptr_members[i]) == id) { + mrb_struct_modify(mrb, s); ptr[i] = val; return val; } @@ -511,6 +521,7 @@ mrb_struct_aset(mrb_state *mrb, mrb_value s) "offset %S too large for struct(size:%S)", mrb_fixnum_value(i), mrb_fixnum_value(RSTRUCT_LEN(s))); } + mrb_struct_modify(mrb, s); return RSTRUCT_PTR(s)[i] = val; } @@ -636,7 +647,7 @@ mrb_struct_to_h(mrb_state *mrb, mrb_value self) mrb_value members, ret; mrb_int i; - members = struct_s_members(mrb, mrb_class(mrb, self)); + members = struct_members(mrb, self); ret = mrb_hash_new_capa(mrb, RARRAY_LEN(members)); for (i = 0; i < RARRAY_LEN(members); ++i) { @@ -677,6 +688,7 @@ mrb_mruby_struct_gem_init(mrb_state* mrb) { struct RClass *st; st = mrb_define_class(mrb, "Struct", mrb->object_class); + MRB_SET_INSTANCE_TT(st, MRB_TT_ARRAY); mrb_define_class_method(mrb, st, "new", mrb_struct_s_def, MRB_ARGS_ANY()); /* 15.2.18.3.1 */ diff --git a/mrbgems/mruby-struct/test/struct.rb b/mrbgems/mruby-struct/test/struct.rb index 1c0939e7f..421fe4b5d 100644 --- a/mrbgems/mruby-struct/test/struct.rb +++ b/mrbgems/mruby-struct/test/struct.rb @@ -197,3 +197,16 @@ assert("Struct.new generates subclass of Struct") do Struct = original_struct end end + +assert 'Struct#freeze' do + c = Struct.new :m + + o = c.new + o.m = :test + assert_equal :test, o.m + + o.freeze + assert_raise(RuntimeError) { o.m = :modify } + assert_raise(RuntimeError) { o[:m] = :modify } + assert_equal :test, o.m +end diff --git a/mrbgems/mruby-time/src/time.c b/mrbgems/mruby-time/src/time.c index ce99a5a65..5e8624830 100644 --- a/mrbgems/mruby-time/src/time.c +++ b/mrbgems/mruby-time/src/time.c @@ -5,22 +5,22 @@ */ #include <math.h> -#include <stdio.h> #include <time.h> #include <mruby.h> #include <mruby/class.h> #include <mruby/data.h> +#ifndef DISABLE_STDIO +#include <stdio.h> +#else +#include <string.h> +#endif + #define NDIV(x,y) (-(-((x)+1)/(y))-1) -#if _MSC_VER < 1800 +#if defined(_MSC_VER) && _MSC_VER < 1800 double round(double x) { - if (x >= 0.0) { - return (double)((int)(x + 0.5)); - } - else { - return (double)((int)(x - 0.5)); - } + return floor(x + 0.5); } #endif @@ -55,6 +55,13 @@ double round(double x) { #endif #endif +/* asctime(3) */ +/* mruby usually use its own implementation of struct tm to string conversion */ +/* except when DISABLE_STDIO is set. In that case, it uses asctime() or asctime_r(). */ +/* By default mruby tries to use asctime_r() which is reentrant. */ +/* Undef following macro on platforms that does not have asctime_r(). */ +/* #define NO_ASCTIME_R */ + /* timegm(3) */ /* mktime() creates tm structure for localtime; timegm() is for UTC time */ /* define following macro to use probably faster timegm() on the platform */ @@ -166,6 +173,7 @@ static const mrb_timezone_name timezone_names[] = { { "LOCAL", sizeof("LOCAL") - 1 }, }; +#ifndef DISABLE_STDIO static const char mon_names[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; @@ -173,6 +181,7 @@ static const char mon_names[12][4] = { static const char wday_names[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; +#endif struct mrb_time { time_t sec; @@ -241,12 +250,12 @@ time_alloc(mrb_state *mrb, double sec, double usec, enum mrb_timezone timezone) tm->sec = tsec; tm->usec = (time_t)llround((sec - tm->sec) * 1.0e6 + usec); if (tm->usec < 0) { - long sec2 = NDIV(usec,1000000); /* negative div */ + long sec2 = (long)NDIV(usec,1000000); /* negative div */ tm->usec -= sec2 * 1000000; tm->sec += sec2; } else if (tm->usec >= 1000000) { - long sec2 = usec / 1000000; + long sec2 = (long)(usec / 1000000); tm->usec -= sec2 * 1000000; tm->sec += sec2; } @@ -528,18 +537,28 @@ mrb_time_zone(mrb_state *mrb, mrb_value self) static mrb_value mrb_time_asctime(mrb_state *mrb, mrb_value self) { - struct mrb_time *tm; - struct tm *d; - char buf[256]; + struct mrb_time *tm = time_get_ptr(mrb, self); + struct tm *d = &tm->datetime; int len; - tm = time_get_ptr(mrb, self); - d = &tm->datetime; +#if defined(DISABLE_STDIO) + char *s; +# ifdef NO_ASCTIME_R + s = asctime(d); +# else + char buf[32]; + s = asctime_r(d, buf); +# endif + len = strlen(s)-1; /* truncate the last newline */ +#else + char buf[256]; + 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, 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); } diff --git a/mrblib/00class.rb b/mrblib/00class.rb index f3762e8d0..1a2d833c8 100644 --- a/mrblib/00class.rb +++ b/mrblib/00class.rb @@ -15,6 +15,7 @@ class Module m.append_features(self) m.included(self) end + self end def prepend(*args) @@ -22,5 +23,6 @@ class Module m.prepend_features(self) m.prepended(self) end + self end end diff --git a/mrblib/10error.rb b/mrblib/10error.rb index 2674af7a2..22a8d1ad7 100644 --- a/mrblib/10error.rb +++ b/mrblib/10error.rb @@ -2,8 +2,8 @@ class ArgumentError < StandardError end -# ISO 15.2.25 -class LocalJumpError < StandardError +# ISO 15.2.25 says "LocalJumpError < StandardError" +class LocalJumpError < ScriptError end # ISO 15.2.26 diff --git a/mrblib/array.rb b/mrblib/array.rb index d5a2d8405..be98f217e 100644 --- a/mrblib/array.rb +++ b/mrblib/array.rb @@ -1,3 +1,4 @@ +# coding: utf-8 ## # Array # @@ -193,9 +194,45 @@ class Array include Enumerable ## + # Quick sort + # a : the array to sort + # left : the beginning of sort region + # right : the end of sort region + def __sort_sub__(a, left, right, &block) + if left < right + i = left + j = right + pivot = a[i + (j - i) / 2] + while true + while ((block)? block.call(a[i], pivot): (a[i] <=> pivot)) < 0 + i += 1 + end + while ((block)? block.call(pivot, a[j]): (pivot <=> a[j])) < 0 + j -= 1 + end + break if (i >= j) + tmp = a[i]; a[i] = a[j]; a[j] = tmp; + i += 1 + j -= 1 + end + __sort_sub__(a, left, i-1, &block) + __sort_sub__(a, j+1, right, &block) + end + end + # private :__sort_sub__ + + ## # Sort all elements and replace +self+ with these # elements. def sort!(&block) - self.replace(self.sort(&block)) + size = self.size + if size > 1 + __sort_sub__(self, 0, size - 1, &block) + end + self + end + + def sort(&block) + self.dup.sort!(&block) end end diff --git a/mrblib/enum.rb b/mrblib/enum.rb index fb55efcf8..12bd1d37b 100644 --- a/mrblib/enum.rb +++ b/mrblib/enum.rb @@ -316,45 +316,6 @@ module Enumerable alias select find_all ## - # TODO - # Does this OK? Please test it. - def __sort_sub__(sorted, work, src_ary, head, tail, &block) - if head == tail - sorted[head] = work[head] if src_ary == 1 - return - end - - # on current step, which is a src ary? - if src_ary == 0 - src, dst = sorted, work - else - src, dst = work, sorted - end - - key = src[head] # key value for dividing values - i, j = head, tail # position to store on the dst ary - - (head + 1).upto(tail){|idx| - if ((block)? block.call(src[idx], key): (src[idx] <=> key)) > 0 - # larger than key - dst[j] = src[idx] - j -= 1 - else - dst[i] = src[idx] - i += 1 - end - } - - sorted[i] = key - - # sort each sub-array - src_ary = (src_ary + 1) % 2 # exchange a src ary - __sort_sub__(sorted, work, src_ary, head, i - 1, &block) if i > head - __sort_sub__(sorted, work, src_ary, i + 1, tail, &block) if i < tail - end -# private :__sort_sub__ - - ## # Return a sorted array of all elements # which are yield by +each+. If no block # is given <=> will be invoked on each @@ -364,12 +325,7 @@ module Enumerable # # ISO 15.3.2.2.19 def sort(&block) - ary = [] - self.each{|*val| ary.push(val.__svalue)} - if ary.size > 1 - __sort_sub__(ary, ::Array.new(ary.size), 0, 0, ary.size - 1, &block) - end - ary + self.map{|*val| val.__svalue}.sort end ## diff --git a/mrblib/string.rb b/mrblib/string.rb index f91b5a23c..4c6114ecb 100644 --- a/mrblib/string.rb +++ b/mrblib/string.rb @@ -133,7 +133,7 @@ class String if args.length == 2 && block block = nil end - if !block + unless block replace = replace.to_str end result = [] diff --git a/src/array.c b/src/array.c index 67cae92eb..9124ff950 100644 --- a/src/array.c +++ b/src/array.c @@ -28,9 +28,15 @@ ary_new_capa(mrb_state *mrb, mrb_int capa) blen = capa * sizeof(mrb_value); a = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class); - a->ptr = (mrb_value *)mrb_malloc(mrb, blen); - a->aux.capa = capa; - a->len = 0; + if (capa <= MRB_ARY_EMBED_LEN_MAX) { + ARY_SET_EMBED_FLAG(a); + /* ARY_SET_EMBED_LEN(a, 0); */ + } + else { + a->as.heap.ptr = (mrb_value *)mrb_malloc(mrb, blen); + a->as.heap.aux.capa = capa; + a->as.heap.len = 0; + } return a; } @@ -77,8 +83,8 @@ mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) { struct RArray *a = ary_new_capa(mrb, size); - array_copy(a->ptr, vals, size); - a->len = size; + array_copy(ARY_PTR(a), vals, size); + ARY_SET_LEN(a, size); return mrb_obj_value(a); } @@ -89,9 +95,9 @@ mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr) struct RArray *a; a = ary_new_capa(mrb, 2); - a->ptr[0] = car; - a->ptr[1] = cdr; - a->len = 2; + ARY_PTR(a)[0] = car; + ARY_PTR(a)[1] = cdr; + ARY_SET_LEN(a, 2); return mrb_obj_value(a); } @@ -106,32 +112,38 @@ ary_fill_with_nil(mrb_value *ptr, mrb_int size) } static void -ary_modify(mrb_state *mrb, struct RArray *a) +ary_modify_check(mrb_state *mrb, struct RArray *a) { if (MRB_FROZEN_P(a)) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen array"); } +} + +static void +ary_modify(mrb_state *mrb, struct RArray *a) +{ + ary_modify_check(mrb, a); if (ARY_SHARED_P(a)) { - mrb_shared_array *shared = a->aux.shared; + mrb_shared_array *shared = a->as.heap.aux.shared; - if (shared->refcnt == 1 && a->ptr == shared->ptr) { - a->ptr = shared->ptr; - a->aux.capa = a->len; + if (shared->refcnt == 1 && a->as.heap.ptr == shared->ptr) { + a->as.heap.ptr = shared->ptr; + a->as.heap.aux.capa = a->as.heap.len; mrb_free(mrb, shared); } else { mrb_value *ptr, *p; - size_t len; + mrb_int len; - p = a->ptr; - len = a->len * sizeof(mrb_value); + p = a->as.heap.ptr; + len = a->as.heap.len * sizeof(mrb_value); ptr = (mrb_value *)mrb_malloc(mrb, len); if (p) { - array_copy(ptr, p, a->len); + array_copy(ptr, p, a->as.heap.len); } - a->ptr = ptr; - a->aux.capa = a->len; + a->as.heap.ptr = ptr; + a->as.heap.aux.capa = a->as.heap.len; mrb_ary_decref(mrb, shared); } ARY_UNSET_SHARED_FLAG(a); @@ -148,33 +160,35 @@ mrb_ary_modify(mrb_state *mrb, struct RArray* a) static void ary_make_shared(mrb_state *mrb, struct RArray *a) { - if (!ARY_SHARED_P(a)) { + if (!ARY_SHARED_P(a) && !ARY_EMBED_P(a)) { mrb_shared_array *shared = (mrb_shared_array *)mrb_malloc(mrb, sizeof(mrb_shared_array)); + mrb_value *ptr = a->as.heap.ptr; + mrb_int len = a->as.heap.len; shared->refcnt = 1; - if (a->aux.capa > a->len) { - a->ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*a->len+1); + if (a->as.heap.aux.capa > len) { + a->as.heap.ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, ptr, sizeof(mrb_value)*len+1); } else { - shared->ptr = a->ptr; + shared->ptr = ptr; } - shared->len = a->len; - a->aux.shared = shared; + shared->len = len; + a->as.heap.aux.shared = shared; ARY_SET_SHARED_FLAG(a); } } static void -ary_expand_capa(mrb_state *mrb, struct RArray *a, size_t len) +ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len) { - size_t capa = a->aux.capa; + mrb_int capa = ARY_CAPA(a); - if (len > ARY_MAX_SIZE) { + if (len > ARY_MAX_SIZE || len < 0) { size_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } - if (capa == 0) { + if (capa < ARY_DEFAULT_LEN) { capa = ARY_DEFAULT_LEN; } while (capa < len) { @@ -189,21 +203,36 @@ ary_expand_capa(mrb_state *mrb, struct RArray *a, size_t len) goto size_error; } - if (capa > (size_t)a->aux.capa) { - mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa); + if (ARY_EMBED_P(a)) { + mrb_value *ptr = ARY_EMBED_PTR(a); + mrb_int len = ARY_EMBED_LEN(a); + mrb_value *expanded_ptr = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*capa); - a->aux.capa = capa; - a->ptr = expanded_ptr; + ARY_UNSET_EMBED_FLAG(a); + array_copy(expanded_ptr, ptr, len); + a->as.heap.len = len; + a->as.heap.aux.capa = capa; + a->as.heap.ptr = expanded_ptr; + } + else if (capa > a->as.heap.aux.capa) { + mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa); + + a->as.heap.aux.capa = capa; + a->as.heap.ptr = expanded_ptr; } } static void ary_shrink_capa(mrb_state *mrb, struct RArray *a) { - mrb_int capa = a->aux.capa; + + mrb_int capa; + + if (ARY_EMBED_P(a)) return; + capa = a->as.heap.aux.capa; if (capa < ARY_DEFAULT_LEN * 2) return; - if (capa <= a->len * ARY_SHRINK_RATIO) return; + if (capa <= a->as.heap.len * ARY_SHRINK_RATIO) return; do { capa /= 2; @@ -211,11 +240,11 @@ ary_shrink_capa(mrb_state *mrb, struct RArray *a) capa = ARY_DEFAULT_LEN; break; } - } while (capa > a->len * ARY_SHRINK_RATIO); + } while (capa > a->as.heap.len * ARY_SHRINK_RATIO); - if (capa > a->len && capa < a->aux.capa) { - a->aux.capa = capa; - a->ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa); + if (capa > a->as.heap.len && capa < a->as.heap.aux.capa) { + a->as.heap.aux.capa = capa; + a->as.heap.ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa); } } @@ -228,13 +257,13 @@ mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len) ary_modify(mrb, a); old_len = RARRAY_LEN(ary); if (old_len != new_len) { - a->len = new_len; + ARY_SET_LEN(a, new_len); if (new_len < old_len) { ary_shrink_capa(mrb, a); } else { ary_expand_capa(mrb, a, new_len); - ary_fill_with_nil(a->ptr + old_len, new_len - old_len); + ary_fill_with_nil(ARY_PTR(a) + old_len, new_len - old_len); } } @@ -249,7 +278,7 @@ mrb_ary_s_create(mrb_state *mrb, mrb_value klass) mrb_int len; struct RArray *a; - mrb_get_args(mrb, "*", &vals, &len); + mrb_get_args(mrb, "*!", &vals, &len); ary = mrb_ary_new_from_values(mrb, len, vals); a = mrb_ary_ptr(ary); a->c = mrb_class_ptr(klass); @@ -262,18 +291,18 @@ ary_concat(mrb_state *mrb, struct RArray *a, struct RArray *a2) { mrb_int len; - if (a2->len > ARY_MAX_SIZE - a->len) { + if (ARY_LEN(a2) > ARY_MAX_SIZE - ARY_LEN(a)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } - len = a->len + a2->len; + len = ARY_LEN(a) + ARY_LEN(a2); ary_modify(mrb, a); - if (a->aux.capa < len) { + if (ARY_CAPA(a) < len) { ary_expand_capa(mrb, a, len); } - array_copy(a->ptr+a->len, a2->ptr, a2->len); + array_copy(ARY_PTR(a)+ARY_LEN(a), ARY_PTR(a2), ARY_LEN(a2)); mrb_write_barrier(mrb, (struct RBasic*)a); - a->len = len; + ARY_SET_LEN(a, len); } MRB_API void @@ -300,16 +329,17 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self) struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; mrb_value *ptr; - mrb_int blen; + mrb_int blen, len1; mrb_get_args(mrb, "a", &ptr, &blen); - if (ARY_MAX_SIZE - blen < a1->len) { + if (ARY_MAX_SIZE - blen < ARY_LEN(a1)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } - a2 = ary_new_capa(mrb, a1->len + blen); - array_copy(a2->ptr, a1->ptr, a1->len); - array_copy(a2->ptr + a1->len, ptr, blen); - a2->len = a1->len + blen; + len1 = ARY_LEN(a1); + a2 = ary_new_capa(mrb, len1 + blen); + array_copy(ARY_PTR(a2), ARY_PTR(a1), len1); + array_copy(ARY_PTR(a2) + len1, ptr, blen); + ARY_SET_LEN(a2, len1+blen); return mrb_obj_value(a2); } @@ -318,11 +348,11 @@ static void ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len) { ary_modify(mrb, a); - if (a->aux.capa < len) + if (ARY_CAPA(a) < len) ary_expand_capa(mrb, a, len); - array_copy(a->ptr, argv, len); + array_copy(ARY_PTR(a), argv, len); mrb_write_barrier(mrb, (struct RBasic*)a); - a->len = len; + ARY_SET_LEN(a, len); } MRB_API void @@ -332,7 +362,7 @@ mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other) struct RArray *a2 = mrb_ary_ptr(other); if (a1 != a2) { - ary_replace(mrb, a1, a2->ptr, a2->len); + ary_replace(mrb, a1, ARY_PTR(a2), ARY_LEN(a2)); } } @@ -353,22 +383,23 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; mrb_value *ptr; - mrb_int times; + mrb_int times, len1; mrb_get_args(mrb, "i", ×); if (times < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } if (times == 0) return mrb_ary_new(mrb); - if (ARY_MAX_SIZE / times < a1->len) { + if (ARY_MAX_SIZE / times < ARY_LEN(a1)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } - a2 = ary_new_capa(mrb, a1->len * times); - ptr = a2->ptr; + len1 = ARY_LEN(a1); + a2 = ary_new_capa(mrb, len1 * times); + ARY_SET_LEN(a2, len1 * times); + ptr = ARY_PTR(a2); while (times--) { - array_copy(ptr, a1->ptr, a1->len); - ptr += a1->len; - a2->len += a1->len; + array_copy(ptr, ARY_PTR(a1), len1); + ptr += len1; } return mrb_obj_value(a2); @@ -378,13 +409,14 @@ static mrb_value mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); + mrb_int len = ARY_LEN(a); - if (a->len > 1) { + if (len > 1) { mrb_value *p1, *p2; ary_modify(mrb, a); - p1 = a->ptr; - p2 = a->ptr + a->len - 1; + p1 = ARY_PTR(a); + p2 = p1 + len - 1; while (p1 < p2) { mrb_value tmp = *p1; @@ -398,18 +430,19 @@ mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self) static mrb_value mrb_ary_reverse(mrb_state *mrb, mrb_value self) { - struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, a->len); + struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, ARY_LEN(a)); + mrb_int len = ARY_LEN(a); - if (a->len > 0) { + if (len > 0) { mrb_value *p1, *p2, *e; - p1 = a->ptr; - e = p1 + a->len; - p2 = b->ptr + a->len - 1; + p1 = ARY_PTR(a); + e = p1 + len; + p2 = ARY_PTR(b) + len - 1; while (p1 < e) { *p2-- = *p1++; } - b->len = a->len; + ARY_SET_LEN(b, len); } return mrb_obj_value(b); } @@ -418,11 +451,13 @@ MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem) { struct RArray *a = mrb_ary_ptr(ary); + mrb_int len = ARY_LEN(a); ary_modify(mrb, a); - if (a->len == a->aux.capa) - ary_expand_capa(mrb, a, a->len + 1); - a->ptr[a->len++] = elem; + if (len == ARY_CAPA(a)) + ary_expand_capa(mrb, a, len + 1); + ARY_PTR(a)[len] = elem; + ARY_SET_LEN(a, len+1); mrb_field_write_barrier_value(mrb, (struct RBasic*)a, elem); } @@ -432,7 +467,7 @@ mrb_ary_push_m(mrb_state *mrb, mrb_value self) mrb_value *argv; mrb_int len; - mrb_get_args(mrb, "*", &argv, &len); + mrb_get_args(mrb, "*!", &argv, &len); while (len--) { mrb_ary_push(mrb, self, *argv++); } @@ -444,10 +479,12 @@ MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary) { struct RArray *a = mrb_ary_ptr(ary); + mrb_int len = ARY_LEN(a); - ary_modify(mrb, a); - if (a->len == 0) return mrb_nil_value(); - return a->ptr[--a->len]; + ary_modify_check(mrb, a); + if (len == 0) return mrb_nil_value(); + ARY_SET_LEN(a, len-1); + return ARY_PTR(a)[len-1]; } #define ARY_SHIFT_SHARED_MIN 10 @@ -456,31 +493,32 @@ MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); + mrb_int len = ARY_LEN(a); mrb_value val; - ary_modify(mrb, a); - if (a->len == 0) return mrb_nil_value(); + ary_modify_check(mrb, a); + if (len == 0) return mrb_nil_value(); if (ARY_SHARED_P(a)) { L_SHIFT: - val = a->ptr[0]; - a->ptr++; - a->len--; + val = a->as.heap.ptr[0]; + a->as.heap.ptr++; + a->as.heap.len--; return val; } - if (a->len > ARY_SHIFT_SHARED_MIN) { + if (len > ARY_SHIFT_SHARED_MIN) { ary_make_shared(mrb, a); goto L_SHIFT; } else { - mrb_value *ptr = a->ptr; - mrb_int size = a->len; + mrb_value *ptr = ARY_PTR(a); + mrb_int size = len; val = *ptr; while (--size) { *ptr = *(ptr+1); ++ptr; } - --a->len; + ARY_SET_LEN(a, len-1); } return val; } @@ -493,21 +531,25 @@ MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item) { struct RArray *a = mrb_ary_ptr(self); + mrb_int len = ARY_LEN(a); if (ARY_SHARED_P(a) - && a->aux.shared->refcnt == 1 /* shared only referenced from this array */ - && a->ptr - a->aux.shared->ptr >= 1) /* there's room for unshifted item */ { - a->ptr--; - a->ptr[0] = item; + && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */ + && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= 1) /* there's room for unshifted item */ { + a->as.heap.ptr--; + a->as.heap.ptr[0] = item; } else { + mrb_value *ptr; + ary_modify(mrb, a); - if (a->aux.capa < a->len + 1) - ary_expand_capa(mrb, a, a->len + 1); - value_move(a->ptr + 1, a->ptr, a->len); - a->ptr[0] = item; + if (ARY_CAPA(a) < len + 1) + ary_expand_capa(mrb, a, len + 1); + ptr = ARY_PTR(a); + value_move(ptr + 1, ptr, len); + ptr[0] = item; } - a->len++; + ARY_SET_LEN(a, len+1); mrb_field_write_barrier_value(mrb, (struct RBasic*)a, item); return self; @@ -517,27 +559,31 @@ static mrb_value mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_value *vals; - mrb_int len; + mrb_value *vals, *ptr; + mrb_int alen, len; - mrb_get_args(mrb, "*", &vals, &len); - if (len > ARY_MAX_SIZE - a->len) { + mrb_get_args(mrb, "*!", &vals, &alen); + len = ARY_LEN(a); + if (alen > ARY_MAX_SIZE - len) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } if (ARY_SHARED_P(a) - && a->aux.shared->refcnt == 1 /* shared only referenced from this array */ - && a->ptr - a->aux.shared->ptr >= len) /* there's room for unshifted item */ { - a->ptr -= len; + && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */ + && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= alen) /* there's room for unshifted item */ { + ary_modify_check(mrb, a); + a->as.heap.ptr -= len; + ptr = a->as.heap.ptr; } else { ary_modify(mrb, a); - if (len == 0) return self; - if (a->aux.capa < a->len + len) - ary_expand_capa(mrb, a, a->len + len); - value_move(a->ptr + len, a->ptr, a->len); - } - array_copy(a->ptr, vals, len); - a->len += len; + if (alen == 0) return self; + if (ARY_CAPA(a) < len + alen) + ary_expand_capa(mrb, a, len + alen); + ptr = ARY_PTR(a); + value_move(ptr + alen, ptr, len); + } + array_copy(ptr, vals, alen); + ARY_SET_LEN(a, len+alen); while (len--) { mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[len]); } @@ -549,44 +595,47 @@ MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n) { struct RArray *a = mrb_ary_ptr(ary); + mrb_int len = ARY_LEN(a); /* range check */ - if (n < 0) n += a->len; - if (n < 0 || a->len <= n) return mrb_nil_value(); + if (n < 0) n += len; + if (n < 0 || len <= n) return mrb_nil_value(); - return a->ptr[n]; + return ARY_PTR(a)[n]; } MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val) { struct RArray *a = mrb_ary_ptr(ary); + mrb_int len = ARY_LEN(a); ary_modify(mrb, a); /* range check */ if (n < 0) { - n += a->len; + n += len; if (n < 0) { - mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - a->len)); + mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - len)); } } - if (a->len <= n) { - if (a->aux.capa <= n) + if (len <= n) { + if (ARY_CAPA(a) <= n) ary_expand_capa(mrb, a, n + 1); - ary_fill_with_nil(a->ptr + a->len, n + 1 - a->len); - a->len = n + 1; + ary_fill_with_nil(ARY_PTR(a) + len, n + 1 - len); + ARY_SET_LEN(a, n+1); } - a->ptr[n] = val; + ARY_PTR(a)[n] = val; mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val); } static struct RArray* ary_dup(mrb_state *mrb, struct RArray *a) { - struct RArray *d = ary_new_capa(mrb, a->len); + mrb_int len = ARY_LEN(a); + struct RArray *d = ary_new_capa(mrb, len); - ary_replace(mrb, d, a->ptr, a->len); + ary_replace(mrb, d, ARY_PTR(a), len); return d; } @@ -594,9 +643,10 @@ MRB_API mrb_value mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl) { struct RArray *a = mrb_ary_ptr(ary); + mrb_int alen = ARY_LEN(a); const mrb_value *argv; mrb_int argc; - size_t tail; + mrb_int tail; ary_modify(mrb, a); @@ -605,61 +655,67 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val /* range check */ if (head < 0) { - head += a->len; + head += alen; if (head < 0) { mrb_raise(mrb, E_INDEX_ERROR, "index is out of array"); } } tail = head + len; - if (a->len < len || (size_t)a->len < tail) { - len = a->len - head; + if (alen < len || alen < tail) { + len = alen - head; } /* size check */ if (mrb_array_p(rpl)) { argc = RARRAY_LEN(rpl); argv = RARRAY_PTR(rpl); - if (argv == a->ptr) { - struct RArray *r = ary_dup(mrb, a); - argv = r->ptr; + if (argv == ARY_PTR(a)) { + struct RArray *r; + + if (argc > 32767) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "too big recursive splice"); + } + r = ary_dup(mrb, a); + argv = ARY_PTR(r); } } else { argc = 1; argv = &rpl; } - if (head >= a->len) { + if (head >= alen) { if (head > ARY_MAX_SIZE - argc) { mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(head)); } len = head + argc; - if (len > a->aux.capa) { + if (len > ARY_CAPA(a)) { ary_expand_capa(mrb, a, head + argc); } - ary_fill_with_nil(a->ptr + a->len, head - a->len); + ary_fill_with_nil(ARY_PTR(a) + alen, head - alen); if (argc > 0) { - array_copy(a->ptr + head, argv, argc); + array_copy(ARY_PTR(a) + head, argv, argc); } - a->len = len; + ARY_SET_LEN(a, len); } else { - mrb_int alen; + mrb_int newlen; - if (a->len - len > ARY_MAX_SIZE - argc) { - mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(a->len + argc - len)); + if (alen - len > ARY_MAX_SIZE - argc) { + mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(alen + argc - len)); } - alen = a->len + argc - len; - if (alen > a->aux.capa) { - ary_expand_capa(mrb, a, alen); + newlen = alen + argc - len; + if (newlen > ARY_CAPA(a)) { + ary_expand_capa(mrb, a, newlen); } if (len != argc) { + mrb_value *ptr = ARY_PTR(a); tail = head + len; - value_move(a->ptr + head + argc, a->ptr + tail, a->len - tail); - a->len = alen; + value_move(ptr + head + argc, ptr + tail, alen - tail); + ARY_SET_LEN(a, newlen); } if (argc > 0) { - value_move(a->ptr + head, argv, argc); + value_move(ARY_PTR(a) + head, argv, argc); } } mrb_write_barrier(mrb, (struct RBasic*)a); @@ -681,12 +737,15 @@ ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len) { struct RArray *b; + if (!ARY_SHARED_P(a) && len <= ARY_SHIFT_SHARED_MIN) { + return mrb_ary_new_from_values(mrb, len, ARY_PTR(a)+beg); + } ary_make_shared(mrb, a); b = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class); - b->ptr = a->ptr + beg; - b->len = len; - b->aux.shared = a->aux.shared; - b->aux.shared->refcnt++; + b->as.heap.ptr = a->as.heap.ptr + beg; + b->as.heap.len = len; + b->as.heap.aux.shared = a->as.heap.aux.shared; + b->as.heap.aux.shared->refcnt++; ARY_SET_SHARED_FLAG(b); return mrb_obj_value(b); @@ -705,7 +764,7 @@ aget_index(mrb_state *mrb, mrb_value index) mrb_int i, argc; mrb_value *argv; - mrb_get_args(mrb, "i*", &i, &argv, &argc); + mrb_get_args(mrb, "i*!", &i, &argv, &argc); return i; } } @@ -741,14 +800,14 @@ static mrb_value mrb_ary_aget(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_int i, len; + mrb_int i, len, alen = ARY_LEN(a); mrb_value index; if (mrb_get_args(mrb, "o|i", &index, &len) == 1) { switch (mrb_type(index)) { /* a[n..m] */ case MRB_TT_RANGE: - if (mrb_range_beg_len(mrb, index, &i, &len, a->len, TRUE) == 1) { + if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) { return ary_subseq(mrb, a, i, len); } else { @@ -762,11 +821,11 @@ mrb_ary_aget(mrb_state *mrb, mrb_value self) } i = aget_index(mrb, index); - if (i < 0) i += a->len; - if (i < 0 || a->len < i) return mrb_nil_value(); + if (i < 0) i += alen; + if (i < 0 || alen < i) return mrb_nil_value(); if (len < 0) return mrb_nil_value(); - if (a->len == i) return mrb_ary_new(mrb); - if (len > a->len - i) len = a->len - i; + if (alen == i) return mrb_ary_new(mrb); + if (len > alen - i) len = alen - i; return ary_subseq(mrb, a, i, len); } @@ -841,22 +900,23 @@ mrb_ary_delete_at(mrb_state *mrb, mrb_value self) mrb_int index; mrb_value val; mrb_value *ptr; - mrb_int len; + mrb_int len, alen = ARY_LEN(a); mrb_get_args(mrb, "i", &index); - if (index < 0) index += a->len; - if (index < 0 || a->len <= index) return mrb_nil_value(); + if (index < 0) index += alen; + if (index < 0 || alen <= index) return mrb_nil_value(); ary_modify(mrb, a); - val = a->ptr[index]; + ptr = ARY_PTR(a); + val = ptr[index]; - ptr = a->ptr + index; - len = a->len - index; + ptr += index; + len = alen - index; while (--len) { *ptr = *(ptr+1); ++ptr; } - --a->len; + ARY_SET_LEN(a, alen-1); ary_shrink_capa(mrb, a); @@ -867,39 +927,39 @@ static mrb_value mrb_ary_first(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_int size; + mrb_int size, alen = ARY_LEN(a); if (mrb_get_args(mrb, "|i", &size) == 0) { - return (a->len > 0)? a->ptr[0]: mrb_nil_value(); + return (alen > 0)? ARY_PTR(a)[0]: mrb_nil_value(); } if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } - if (size > a->len) size = a->len; + if (size > alen) size = alen; if (ARY_SHARED_P(a)) { return ary_subseq(mrb, a, 0, size); } - return mrb_ary_new_from_values(mrb, size, a->ptr); + return mrb_ary_new_from_values(mrb, size, ARY_PTR(a)); } static mrb_value mrb_ary_last(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_int size; + mrb_int size, alen = ARY_LEN(a); if (mrb_get_args(mrb, "|i", &size) == 0) - return (a->len > 0)? a->ptr[a->len - 1]: mrb_nil_value(); + return (alen > 0)? ARY_PTR(a)[alen - 1]: mrb_nil_value(); if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } - if (size > a->len) size = a->len; + if (size > alen) size = alen; if (ARY_SHARED_P(a) || size > ARY_DEFAULT_LEN) { - return ary_subseq(mrb, a, a->len - size, size); + return ary_subseq(mrb, a, alen - size, size); } - return mrb_ary_new_from_values(mrb, size, a->ptr + a->len - size); + return mrb_ary_new_from_values(mrb, size, ARY_PTR(a) + alen - size); } static mrb_value @@ -972,7 +1032,7 @@ mrb_ary_size(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - return mrb_fixnum_value(a->len); + return mrb_fixnum_value(ARY_LEN(a)); } MRB_API mrb_value @@ -982,15 +1042,14 @@ mrb_ary_clear(mrb_state *mrb, mrb_value self) ary_modify(mrb, a); if (ARY_SHARED_P(a)) { - mrb_ary_decref(mrb, a->aux.shared); + mrb_ary_decref(mrb, a->as.heap.aux.shared); ARY_UNSET_SHARED_FLAG(a); } - else { - mrb_free(mrb, a->ptr); + else if (!ARY_EMBED_P(a)){ + mrb_free(mrb, a->as.heap.ptr); } - a->len = 0; - a->aux.capa = 0; - a->ptr = 0; + ARY_SET_EMBED_FLAG(a); + ARY_SET_EMBED_LEN(a, 0); return self; } @@ -1000,7 +1059,7 @@ mrb_ary_empty_p(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - return mrb_bool_value(a->len == 0); + return mrb_bool_value(ARY_LEN(a) == 0); } MRB_API mrb_value diff --git a/src/backtrace.c b/src/backtrace.c index d634123b4..f04395597 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -13,101 +13,28 @@ #include <mruby/debug.h> #include <mruby/error.h> #include <mruby/numeric.h> - -struct backtrace_location_raw { - int i; - int lineno; - const char *filename; - mrb_sym method_id; - char sep; - struct RClass *klass; -}; +#include <mruby/data.h> struct backtrace_location { - int i; int lineno; const char *filename; - const char *method; - char sep; - const char *class_name; + mrb_sym method_id; }; -typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location_raw*, void*); -typedef void (*output_stream_func)(mrb_state*, struct backtrace_location*, void*); +typedef void (*each_backtrace_func)(mrb_state*, int i, struct backtrace_location*, void*); -#ifndef MRB_DISABLE_STDIO - -struct print_backtrace_args { - FILE *stream; - int tracehead; -}; - -static void -print_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data) -{ - struct print_backtrace_args *args; - - args = (struct print_backtrace_args*)data; - - if (args->tracehead) { - fprintf(args->stream, "trace:\n"); - args->tracehead = FALSE; - } - - fprintf(args->stream, "\t[%d] %s:%d", loc->i, loc->filename, loc->lineno); - - if (loc->method) { - if (loc->class_name) { - fprintf(args->stream, ":in %s%c%s", loc->class_name, loc->sep, loc->method); - } - else { - fprintf(args->stream, ":in %s", loc->method); - } - } - - fprintf(args->stream, "\n"); -} - -#endif - -static void -get_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data) -{ - mrb_value ary, str; - char buf[32]; - int ai = mrb_gc_arena_save(mrb); - - ary = mrb_obj_value((struct RArray*)data); - - str = mrb_str_new_cstr(mrb, loc->filename); - snprintf(buf, sizeof(buf), ":%d", loc->lineno); - mrb_str_cat_cstr(mrb, str, buf); - - if (loc->method) { - mrb_str_cat_lit(mrb, str, ":in "); - - if (loc->class_name) { - mrb_str_cat_cstr(mrb, str, loc->class_name); - mrb_str_cat(mrb, str, &loc->sep, 1); - } - - mrb_str_cat_cstr(mrb, str, loc->method); - } - - mrb_ary_push(mrb, ary, str); - mrb_gc_arena_restore(mrb, ai); -} +static const mrb_data_type bt_type = { "Backtrace", mrb_free }; static void each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func func, void *data) { - int i; + int i, j; if (ciidx >= mrb->c->ciend - mrb->c->cibase) ciidx = 10; /* ciidx is broken... */ - for (i = ciidx; i >= 0; i--) { - struct backtrace_location_raw loc; + for (i=ciidx, j=0; i >= 0; i--,j++) { + struct backtrace_location loc; mrb_callinfo *ci; mrb_irep *irep; mrb_code *pc; @@ -134,79 +61,15 @@ each_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, each_backtrace_func if (loc.lineno == -1) continue; - if (ci->target_class == ci->proc->target_class) { - loc.sep = '.'; - } - else { - loc.sep = '#'; - } - if (!loc.filename) { loc.filename = "(unknown)"; } loc.method_id = ci->mid; - loc.klass = ci->proc->target_class; - loc.i = i; - func(mrb, &loc, data); - } -} - -struct output_backtrace_args { - output_stream_func func; - void *data; -}; - -static void -output_backtrace_i(mrb_state *mrb, struct backtrace_location_raw *loc_raw, void *data) -{ - struct backtrace_location loc; - struct output_backtrace_args *args = (struct output_backtrace_args *)data; - - loc.i = loc_raw->i; - loc.lineno = loc_raw->lineno; - loc.filename = loc_raw->filename; - loc.method = mrb_sym2name(mrb, loc_raw->method_id); - loc.sep = loc_raw->sep; - loc.class_name = mrb_class_name(mrb, loc_raw->klass); - - args->func(mrb, &loc, args->data); -} - -static void -output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *data) -{ - struct output_backtrace_args args; - args.func = func; - args.data = data; - each_backtrace(mrb, ciidx, pc0, output_backtrace_i, &args); -} - -static void -exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream) -{ - mrb_value lastpc; - mrb_code *code; - - lastpc = mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc")); - if (mrb_nil_p(lastpc)) { - code = NULL; + func(mrb, j, &loc, data); } - else { - code = (mrb_code*)mrb_cptr(lastpc); - } - - output_backtrace(mrb, mrb_fixnum(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "ciidx"))), - code, func, stream); } -/* mrb_print_backtrace/mrb_get_backtrace: - - function to retrieve backtrace information from the exception. - note that if you call method after the exception, call stack will be - overwritten. So invoke these functions just after detecting exceptions. -*/ - #ifndef MRB_DISABLE_STDIO static void @@ -215,11 +78,14 @@ print_backtrace(mrb_state *mrb, mrb_value backtrace) int i, n; FILE *stream = stderr; - fprintf(stream, "trace:\n"); + if (!mrb_array_p(backtrace)) return; - n = RARRAY_LEN(backtrace); - for (i = 0; i < n; i++) { - mrb_value entry = RARRAY_PTR(backtrace)[i]; + n = RARRAY_LEN(backtrace) - 1; + if (n == 0) return; + + fprintf(stream, "trace:\n"); + for (i=0; i<n; i++) { + mrb_value entry = RARRAY_PTR(backtrace)[n-i-1]; if (mrb_string_p(entry)) { fprintf(stream, "\t[%d] %.*s\n", i, (int)RSTRING_LEN(entry), RSTRING_PTR(entry)); @@ -227,40 +93,54 @@ print_backtrace(mrb_state *mrb, mrb_value backtrace) } } +static int +packed_bt_len(struct backtrace_location *bt, int n) +{ + int len = 0; + int i; + + for (i=0; i<n; i++) { + if (!bt[i].filename && !bt[i].lineno && !bt[i].method_id) + continue; + len++; + } + return len; +} + static void -print_backtrace_saved(mrb_state *mrb) +print_packed_backtrace(mrb_state *mrb, mrb_value packed) { - int i, ai; FILE *stream = stderr; + struct backtrace_location *bt; + int n, i; + int ai = mrb_gc_arena_save(mrb); - fprintf(stream, "trace:\n"); - ai = mrb_gc_arena_save(mrb); - for (i = 0; i < mrb->backtrace.n; i++) { - mrb_backtrace_entry *entry; + bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, packed, &bt_type); + if (bt == NULL) return; + n = (mrb_int)RDATA(packed)->flags; - entry = &(mrb->backtrace.entries[i]); + if (packed_bt_len(bt, n) == 0) return; + fprintf(stream, "trace:\n"); + for (i = 0; i<n; i++) { + struct backtrace_location *entry = &bt[n-i-1]; + if (entry->filename == NULL) continue; fprintf(stream, "\t[%d] %s:%d", i, entry->filename, entry->lineno); - if (entry->method_id != 0) { const char *method_name; method_name = mrb_sym2name(mrb, entry->method_id); - if (entry->klass) { - fprintf(stream, ":in %s%c%s", - mrb_class_name(mrb, entry->klass), - entry->sep, - method_name); - } - else { - fprintf(stream, ":in %s", method_name); - } + fprintf(stream, ":in %s", method_name); mrb_gc_arena_restore(mrb, ai); } - fprintf(stream, "\n"); } } +/* mrb_print_backtrace + + function to retrieve backtrace information from the last exception. +*/ + MRB_API void mrb_print_backtrace(mrb_state *mrb) { @@ -271,20 +151,14 @@ mrb_print_backtrace(mrb_state *mrb) } backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace")); - if (!mrb_nil_p(backtrace)) { + if (mrb_nil_p(backtrace)) return; + if (mrb_array_p(backtrace)) { print_backtrace(mrb, backtrace); } - else if (mrb->backtrace.n > 0) { - print_backtrace_saved(mrb); - } else { - struct print_backtrace_args args; - args.stream = stderr; - args.tracehead = TRUE; - exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)&args); + print_packed_backtrace(mrb, backtrace); } } - #else MRB_API void @@ -294,135 +168,116 @@ mrb_print_backtrace(mrb_state *mrb) #endif -MRB_API mrb_value -mrb_exc_backtrace(mrb_state *mrb, mrb_value self) +static void +count_backtrace_i(mrb_state *mrb, + int i, + struct backtrace_location *loc, + void *data) { - mrb_value ary; - - ary = mrb_ary_new(mrb); - exc_output_backtrace(mrb, mrb_obj_ptr(self), get_backtrace_i, (void*)mrb_ary_ptr(ary)); + int *lenp = (int*)data; - return ary; + if (loc->filename == NULL) return; + (*lenp)++; } -MRB_API mrb_value -mrb_get_backtrace(mrb_state *mrb) +static void +pack_backtrace_i(mrb_state *mrb, + int i, + struct backtrace_location *loc, + void *data) { - mrb_value ary; - mrb_callinfo *ci = mrb->c->ci; - mrb_code *pc = ci->pc; - mrb_int ciidx = (mrb_int)(ci - mrb->c->cibase - 1); + struct backtrace_location **pptr = (struct backtrace_location**)data; + struct backtrace_location *ptr = *pptr; - if (ciidx < 0) ciidx = 0; - ary = mrb_ary_new(mrb); - output_backtrace(mrb, ciidx, pc, get_backtrace_i, (void*)mrb_ary_ptr(ary)); - - return ary; + if (loc->filename == NULL) return; + *ptr = *loc; + *pptr = ptr+1; } -void -mrb_free_backtrace(mrb_state *mrb) +static mrb_value +packed_backtrace(mrb_state *mrb) { - mrb->backtrace.exc = 0; - mrb->backtrace.n = 0; - mrb->backtrace.n_allocated = 0; - mrb_free(mrb, mrb->backtrace.entries); + struct RData *backtrace; + ptrdiff_t ciidx = mrb->c->ci - mrb->c->cibase; + int len = 0; + int size; + void *ptr; + + each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len); + size = len * sizeof(struct backtrace_location); + ptr = mrb_malloc(mrb, size); + memset(ptr, 0, size); + backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type); + backtrace->flags = (unsigned int)len; + each_backtrace(mrb, ciidx, mrb->c->ci->pc, pack_backtrace_i, &ptr); + return mrb_obj_value(backtrace); } -static void -save_backtrace_i(mrb_state *mrb, - struct backtrace_location_raw *loc_raw, - void *data) +void +mrb_keep_backtrace(mrb_state *mrb, mrb_value exc) { - mrb_backtrace_entry *entry; - - if (mrb->backtrace.n >= mrb->backtrace.n_allocated) { - int new_n_allocated; - if (mrb->backtrace.n_allocated == 0) { - new_n_allocated = 8; - } - else { - new_n_allocated = mrb->backtrace.n_allocated * 2; - } - mrb->backtrace.entries = (mrb_backtrace_entry *) - mrb_realloc(mrb, - mrb->backtrace.entries, - sizeof(mrb_backtrace_entry) * new_n_allocated); - mrb->backtrace.n_allocated = new_n_allocated; - } - - entry = &mrb->backtrace.entries[mrb->backtrace.n]; - entry->filename = loc_raw->filename; - entry->lineno = loc_raw->lineno; - entry->klass = loc_raw->klass; - entry->sep = loc_raw->sep; - entry->method_id = loc_raw->method_id; + mrb_value backtrace; + int ai = mrb_gc_arena_save(mrb); - mrb->backtrace.n++; + backtrace = packed_backtrace(mrb); + mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace); + mrb_gc_arena_restore(mrb, ai); } -void -mrb_save_backtrace(mrb_state *mrb) +mrb_value +mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace) { - mrb_value lastpc; - mrb_code *code; - mrb_int ciidx; - - mrb->backtrace.n = 0; - mrb->backtrace.exc = 0; - - if (!mrb->exc) - return; - - mrb->backtrace.exc = mrb->exc; + struct backtrace_location *bt; + mrb_int n, i; + int ai; - lastpc = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "lastpc")); - if (mrb_nil_p(lastpc)) { - code = NULL; - } - else { - code = (mrb_code*)mrb_cptr(lastpc); + if (mrb_nil_p(backtrace)) { + empty_backtrace: + return mrb_ary_new_capa(mrb, 0); } + if (mrb_array_p(backtrace)) return backtrace; + bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, backtrace, &bt_type); + if (bt == NULL) goto empty_backtrace; + n = (mrb_int)RDATA(backtrace)->flags; + backtrace = mrb_ary_new_capa(mrb, n); + ai = mrb_gc_arena_save(mrb); + for (i = 0; i < n; i++) { + struct backtrace_location *entry = &bt[i]; + mrb_value btline; - ciidx = mrb_fixnum(mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "ciidx"))); + if (entry->filename == NULL) continue; + btline = mrb_format(mrb, "%S:%S", + mrb_str_new_cstr(mrb, entry->filename), + mrb_fixnum_value(entry->lineno)); + if (entry->method_id != 0) { + mrb_str_cat_lit(mrb, btline, ":in "); + mrb_str_cat_cstr(mrb, btline, mrb_sym2name(mrb, entry->method_id)); + } + mrb_ary_push(mrb, backtrace, btline); + mrb_gc_arena_restore(mrb, ai); + } - each_backtrace(mrb, ciidx, code, save_backtrace_i, NULL); + return backtrace; } -mrb_value -mrb_restore_backtrace(mrb_state *mrb) +MRB_API mrb_value +mrb_exc_backtrace(mrb_state *mrb, mrb_value exc) { - int i; + mrb_sym attr_name; mrb_value backtrace; - backtrace = mrb_ary_new(mrb); - for (i = 0; i < mrb->backtrace.n; i++) { - int ai; - mrb_backtrace_entry *entry; - mrb_value mrb_entry; - char buf[32]; - - ai = mrb_gc_arena_save(mrb); - entry = &(mrb->backtrace.entries[i]); - - mrb_entry = mrb_str_new_cstr(mrb, entry->filename); - snprintf(buf, sizeof(buf), ":%d", entry->lineno); - mrb_str_cat_cstr(mrb, mrb_entry, buf); - if (entry->method_id != 0) { - mrb_str_cat_lit(mrb, mrb_entry, ":in "); - - if (entry->klass) { - mrb_str_cat_cstr(mrb, mrb_entry, mrb_class_name(mrb, entry->klass)); - mrb_str_cat(mrb, mrb_entry, &entry->sep, 1); - } - - mrb_str_cat_cstr(mrb, mrb_entry, mrb_sym2name(mrb, entry->method_id)); - } - - mrb_ary_push(mrb, backtrace, mrb_entry); - - mrb_gc_arena_restore(mrb, ai); + attr_name = mrb_intern_lit(mrb, "backtrace"); + backtrace = mrb_iv_get(mrb, exc, attr_name); + if (mrb_nil_p(backtrace) || mrb_array_p(backtrace)) { + return backtrace; } - + backtrace = mrb_unpack_backtrace(mrb, backtrace); + mrb_iv_set(mrb, exc, attr_name, backtrace); return backtrace; } + +MRB_API mrb_value +mrb_get_backtrace(mrb_state *mrb) +{ + return mrb_unpack_backtrace(mrb, packed_backtrace(mrb)); +} diff --git a/src/class.c b/src/class.c index 7cb544b43..57f64dcc5 100644 --- a/src/class.c +++ b/src/class.c @@ -123,25 +123,44 @@ module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) return mrb_class_ptr(c); } +static mrb_bool +class_ptr_p(mrb_value obj) +{ + switch (mrb_type(obj)) { + case MRB_TT_CLASS: + case MRB_TT_SCLASS: + case MRB_TT_MODULE: + return TRUE; + default: + return FALSE; + } +} + MRB_API struct RClass* mrb_class_outer_module(mrb_state *mrb, struct RClass *c) { mrb_value outer; + struct RClass *cls; outer = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__")); if (mrb_nil_p(outer)) return NULL; - return mrb_class_ptr(outer); + cls = mrb_class_ptr(outer); + if (cls->tt == MRB_TT_SCLASS) + { + mrb_value klass; + klass = mrb_obj_iv_get(mrb, (struct RObject *)cls, + mrb_intern_lit(mrb, "__attached__")); + if (class_ptr_p(klass)) { + cls = mrb_class_ptr(klass); + } + } + return cls; } static void check_if_class_or_module(mrb_state *mrb, mrb_value obj) { - switch (mrb_type(obj)) { - case MRB_TT_CLASS: - case MRB_TT_SCLASS: - case MRB_TT_MODULE: - return; - default: + if (!class_ptr_p(obj)) { mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", mrb_inspect(mrb, obj)); } } @@ -412,6 +431,7 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RPro k = kh_put(mt, mrb, h, mid); kh_value(h, k) = p; if (p) { + p->c = NULL; mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p); } } @@ -532,9 +552,9 @@ to_sym(mrb_state *mrb, mrb_value ss) d: Data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified I: Inline struct [void*] &: Block [mrb_value] - *: rest argument [mrb_value*,mrb_int] Receive the rest of the arguments as an array. - |: optional Next argument of '|' and later are optional. - ?: optional given [mrb_bool] true if preceding argument (optional) is given. + *: rest argument [mrb_value*,mrb_int] The rest of the arguments as an array; *! avoid copy of the stack + |: optional Following arguments are optional + ?: optional given [mrb_bool] true if preceding argument (optional) is given */ MRB_API mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...) @@ -544,7 +564,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) va_list ap; int argc = mrb->c->ci->argc; int arg_i = 0; - mrb_bool array_argv; + mrb_value *array_argv; mrb_bool opt = FALSE; mrb_bool given = TRUE; @@ -552,15 +572,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) if (argc < 0) { struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]); - argc = a->len; - array_argv = TRUE; + argc = ARY_LEN(a); + array_argv = ARY_PTR(a); } else { - array_argv = FALSE; + array_argv = NULL; } #define ARGV \ - (array_argv ? mrb_ary_ptr(mrb->c->stack[1])->ptr : (mrb->c->stack + 1)) + (array_argv ? array_argv : (mrb->c->stack + 1)) while ((c = *format++)) { switch (c) { @@ -599,14 +619,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value ss; ss = ARGV[arg_i++]; - switch (mrb_type(ss)) { - case MRB_TT_CLASS: - case MRB_TT_MODULE: - case MRB_TT_SCLASS: - break; - default: + if (!class_ptr_p(ss)) { mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss); - break; } *p = ss; i++; @@ -683,7 +697,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) if (i < argc && mrb_nil_p(ARGV[arg_i])) { *ps = NULL; *pl = 0; - i++; + i++; arg_i++; break; } } @@ -737,8 +751,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) if (i < argc) { aa = to_ary(mrb, ARGV[arg_i++]); a = mrb_ary_ptr(aa); - *pb = a->ptr; - *pl = a->len; + *pb = ARY_PTR(a); + *pl = ARY_LEN(a); i++; } } @@ -787,7 +801,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) { mrb_float f = mrb_float(ARGV[arg_i]); - if (!FIXABLE(f)) { + if (!FIXABLE_FLOAT(f)) { mrb_raise(mrb, E_RANGE_ERROR, "float too big for int"); } *p = (mrb_int)f; @@ -882,13 +896,25 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) { mrb_value **var; mrb_int *pl; + mrb_bool nocopy = array_argv ? TRUE : FALSE; + if (*format == '!') { + format++; + nocopy = TRUE; + } var = va_arg(ap, mrb_value**); pl = va_arg(ap, mrb_int*); if (argc > i) { *pl = argc-i; if (*pl > 0) { - *var = ARGV + arg_i; + if (nocopy) { + *var = ARGV+arg_i; + } + else { + mrb_value args = mrb_ary_new_from_values(mrb, *pl, ARGV+arg_i); + RARRAY(args)->c = NULL; + *var = RARRAY_PTR(args); + } } i = argc; arg_i += *pl; @@ -979,7 +1005,7 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru if (p->tt == MRB_TT_ICLASS) { if (p->mt == m->mt) { if (!superclass_seen) { - ins_pos = p; // move insert point + ins_pos = p; /* move insert point */ } goto skip; } @@ -1146,7 +1172,7 @@ mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; struct RClass *m = mrb_class_ptr(mod); - boot_initmod(mrb, m); // bootstrap a newly initialized module + boot_initmod(mrb, m); /* bootstrap a newly initialized module */ mrb_get_args(mrb, "|&", &b); if (!mrb_nil_p(b)) { mrb_yield_with_class(mrb, b, 1, &mod, mod, m); @@ -1755,15 +1781,11 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass) str = mrb_str_new_lit(mrb, "#<Class:"); - switch (mrb_type(v)) { - case MRB_TT_CLASS: - case MRB_TT_MODULE: - case MRB_TT_SCLASS: - mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v)); - break; - default: - mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v)); - break; + if (class_ptr_p(v)) { + mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v)); + } + else { + mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v)); } return mrb_str_cat_lit(mrb, str, ">"); } @@ -2139,13 +2161,43 @@ mrb_mod_const_defined(mrb_state *mrb, mrb_value mod) } static mrb_value +mrb_const_get_sym(mrb_state *mrb, mrb_value mod, mrb_sym id) +{ + check_const_name_sym(mrb, id); + return mrb_const_get(mrb, mod, id); +} + +static mrb_value mrb_mod_const_get(mrb_state *mrb, mrb_value mod) { + mrb_value path; mrb_sym id; + char *ptr; + mrb_int off, end, len; - mrb_get_args(mrb, "n", &id); - check_const_name_sym(mrb, id); - return mrb_const_get(mrb, mod, id); + mrb_get_args(mrb, "o", &path); + + if (mrb_symbol_p(path)) { + /* const get with symbol */ + id = mrb_symbol(path); + return mrb_const_get_sym(mrb, mod, id); + } + + /* const get with class path string */ + path = mrb_string_type(mrb, path); + ptr = RSTRING_PTR(path); + len = RSTRING_LEN(path); + off = 0; + + while (off < len) { + end = mrb_str_index_lit(mrb, path, "::", off); + end = (end == -1) ? len : end; + id = mrb_intern(mrb, ptr+off, end-off); + mod = mrb_const_get_sym(mrb, mod, id); + off = (end == len) ? end : end+2; + } + + return mod; } static mrb_value @@ -2156,7 +2208,14 @@ mrb_mod_const_set(mrb_state *mrb, mrb_value mod) mrb_get_args(mrb, "no", &id, &value); check_const_name_sym(mrb, id); - mrb_const_set(mrb, mod, id, value); + if ((mrb_type(value) == MRB_TT_CLASS || mrb_type(value) == MRB_TT_MODULE) + && !mrb_obj_iv_defined(mrb, mrb_obj_ptr(value), mrb_intern_lit(mrb, "__classid__"))) { + /* name unnamed classes/modules */ + setup_class(mrb, mrb_class_ptr(mod), mrb_class_ptr(value), id); + } + else { + mrb_const_set(mrb, mod, id, value); + } return value; } diff --git a/src/codedump.c b/src/codedump.c index c20afdd00..6b2c43b48 100644 --- a/src/codedump.c +++ b/src/codedump.c @@ -239,6 +239,7 @@ codedump(mrb_state *mrb, mrb_irep *irep) printf("OP_RETURN\tR%d", GETARG_A(c)); switch (GETARG_B(c)) { case OP_R_NORMAL: + printf("\tnormal\t"); break; case OP_R_RETURN: printf("\treturn\t"); break; case OP_R_BREAK: @@ -258,7 +259,15 @@ codedump(mrb_state *mrb, mrb_irep *irep) break; case OP_LAMBDA: - printf("OP_LAMBDA\tR%d\tI(%+d)\t%d", GETARG_A(c), GETARG_b(c)+1, GETARG_c(c)); + printf("OP_LAMBDA\tR%d\tI(%+d)\t", GETARG_A(c), GETARG_b(c)+1); + switch (GETARG_c(c)) { + case OP_L_METHOD: + printf("method"); break; + case OP_L_BLOCK: + printf("block"); break; + case OP_L_LAMBDA: + printf("lambda"); break; + } print_lv(mrb, irep, c, RA); break; case OP_RANGE: diff --git a/src/error.c b/src/error.c index 2201fc840..062282719 100644 --- a/src/error.c +++ b/src/error.c @@ -47,7 +47,7 @@ exc_initialize(mrb_state *mrb, mrb_value exc) mrb_int argc; mrb_value *argv; - if (mrb_get_args(mrb, "|o*", &mesg, &argv, &argc) >= 1) { + if (mrb_get_args(mrb, "|o*!", &mesg, &argv, &argc) >= 1) { mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg); } return exc; @@ -62,7 +62,7 @@ exc_initialize(mrb_state *mrb, mrb_value exc) * With no argument, or if the argument is the same as the receiver, * return the receiver. Otherwise, create a new * exception object of the same class as the receiver, but with a - * message equal to <code>string.to_str</code>. + * message equal to <code>string</code>. * */ @@ -111,9 +111,7 @@ exc_to_s(mrb_state *mrb, mrb_value exc) * exception.message -> string * * Returns the result of invoking <code>exception.to_s</code>. - * Normally this returns the exception's message or name. By - * supplying a to_str method, exceptions are agreeing to - * be used where Strings are expected. + * Normally this returns the exception's message or name. */ static mrb_value @@ -137,6 +135,7 @@ exc_inspect(mrb_state *mrb, mrb_value exc) { mrb_value str, mesg, file, line; mrb_bool append_mesg; + const char *cname; mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg")); file = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "file")); @@ -148,57 +147,23 @@ exc_inspect(mrb_state *mrb, mrb_value exc) append_mesg = RSTRING_LEN(mesg) > 0; } + cname = mrb_obj_classname(mrb, exc); + str = mrb_str_new_cstr(mrb, cname); if (mrb_string_p(file) && mrb_fixnum_p(line)) { - char buf[32]; - - str = mrb_str_dup(mrb, file); - snprintf(buf, sizeof(buf), ":%" MRB_PRId ": ", mrb_fixnum(line)); - mrb_str_cat_cstr(mrb, str, buf); if (append_mesg) { - mrb_str_cat_str(mrb, str, mesg); - mrb_str_cat_lit(mrb, str, " ("); + str = mrb_format(mrb, "%S:%S: %S (%S)", file, line, mesg, str); } - mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc)); - if (append_mesg) { - mrb_str_cat_lit(mrb, str, ")"); + else { + str = mrb_format(mrb, "%S:%S: %S", file, line, str); } } - else { - const char *cname = mrb_obj_classname(mrb, exc); - str = mrb_str_new_cstr(mrb, cname); - if (append_mesg) { - mrb_str_cat_lit(mrb, str, ": "); - mrb_str_cat_str(mrb, str, mesg); - } + else if (append_mesg) { + str = mrb_format(mrb, "%S: %S", str, mesg); } return str; } -void mrb_save_backtrace(mrb_state *mrb); -mrb_value mrb_restore_backtrace(mrb_state *mrb); - -static mrb_value -exc_get_backtrace(mrb_state *mrb, mrb_value exc) -{ - mrb_sym attr_name; - mrb_value backtrace; - - attr_name = mrb_intern_lit(mrb, "backtrace"); - backtrace = mrb_iv_get(mrb, exc, attr_name); - if (mrb_nil_p(backtrace)) { - if (mrb_obj_ptr(exc) == mrb->backtrace.exc && mrb->backtrace.n > 0) { - backtrace = mrb_restore_backtrace(mrb); - mrb->backtrace.n = 0; - mrb->backtrace.exc = 0; - } - else { - backtrace = mrb_exc_backtrace(mrb, exc); - } - mrb_iv_set(mrb, exc, attr_name, backtrace); - } - - return backtrace; -} +void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc); static void set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace) @@ -235,7 +200,6 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc) mrb_callinfo *ci = mrb->c->ci; mrb_code *pc = ci->pc; - mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value((mrb_int)(ci - mrb->c->cibase))); while (ci >= mrb->c->cibase) { mrb_code *err = ci->err; @@ -256,41 +220,18 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc) } } -static mrb_bool -have_backtrace(mrb_state *mrb, struct RObject *exc) -{ - return !mrb_nil_p(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "backtrace"))); -} - void mrb_exc_set(mrb_state *mrb, mrb_value exc) { - if (!mrb->gc.out_of_memory && mrb->backtrace.n > 0) { - mrb_value target_exc = mrb_nil_value(); - int ai; - - ai = mrb_gc_arena_save(mrb); - if ((mrb->exc && !have_backtrace(mrb, mrb->exc))) { - target_exc = mrb_obj_value(mrb->exc); - } - else if (!mrb_nil_p(exc) && mrb->backtrace.exc) { - target_exc = mrb_obj_value(mrb->backtrace.exc); - mrb_gc_protect(mrb, target_exc); - } - if (!mrb_nil_p(target_exc)) { - mrb_value backtrace; - backtrace = mrb_restore_backtrace(mrb); - set_backtrace(mrb, target_exc, backtrace); - } - mrb_gc_arena_restore(mrb, ai); - } - - mrb->backtrace.n = 0; if (mrb_nil_p(exc)) { mrb->exc = 0; } else { mrb->exc = mrb_obj_ptr(exc); + if (!mrb->gc.out_of_memory) { + exc_debug_info(mrb, mrb->exc); + mrb_keep_backtrace(mrb, exc); + } } } @@ -301,10 +242,6 @@ mrb_exc_raise(mrb_state *mrb, mrb_value exc) mrb_raise(mrb, E_TYPE_ERROR, "exception object expected"); } mrb_exc_set(mrb, exc); - if (!mrb->gc.out_of_memory) { - exc_debug_info(mrb, mrb->exc); - mrb_save_backtrace(mrb); - } if (!mrb->jmp) { mrb_p(mrb, exc); abort(); @@ -535,7 +472,7 @@ mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, void mrb_init_exception(mrb_state *mrb) { - struct RClass *exception, *runtime_error, *script_error, *stack_error; + struct RClass *exception, *script_error, *stack_error, *nomem_error; mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION); @@ -545,17 +482,19 @@ mrb_init_exception(mrb_state *mrb) mrb_define_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "message", exc_message, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE()); - mrb_define_method(mrb, exception, "backtrace", exc_get_backtrace, MRB_ARGS_NONE()); + mrb_define_method(mrb, exception, "backtrace", mrb_exc_backtrace, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "set_backtrace", exc_set_backtrace, MRB_ARGS_REQ(1)); mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ - runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ - mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, runtime_error, "Out of memory")); -#ifdef MRB_GC_FIXED_ARENA - mrb->arena_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, runtime_error, "arena overflow error")); -#endif + mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ stack_error = mrb_define_class(mrb, "SystemStackError", exception); mrb->stack_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, stack_error, "stack level too deep")); + + nomem_error = mrb_define_class(mrb, "NoMemoryError", exception); + mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "Out of memory")); +#ifdef MRB_GC_FIXED_ARENA + mrb->arena_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "arena overflow error")); +#endif } @@ -17,6 +17,7 @@ #include <mruby/variable.h> #include <mruby/gc.h> #include <mruby/error.h> +#include <mruby/throw.h> /* = Tri-color Incremental Garbage Collection @@ -111,6 +112,7 @@ typedef struct { struct RProc proc; struct REnv env; struct RException exc; + struct RBreak brk; #ifdef MRB_WORD_BOXING struct RFloat floatv; struct RCptr cptr; @@ -214,7 +216,8 @@ mrb_realloc(mrb_state *mrb, void *p, size_t len) void *p2; p2 = mrb_realloc_simple(mrb, p, len); - if (!p2 && len) { + if (len == 0) return p2; + if (p2 == NULL) { if (mrb->gc.out_of_memory) { mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); /* mrb_panic(mrb); */ @@ -430,7 +433,7 @@ mrb_gc_protect(mrb_state *mrb, mrb_value obj) Register your object when it's exported to C world, without reference from Ruby world, e.g. callback - arguments. Don't forget to remove the obejct using + arguments. Don't forget to remove the object using mrb_gc_unregister, otherwise your object will leak. */ @@ -463,10 +466,13 @@ mrb_gc_unregister(mrb_state *mrb, mrb_value obj) } a = mrb_ary_ptr(table); mrb_ary_modify(mrb, a); - for (i = 0; i < a->len; i++) { - if (mrb_obj_eq(mrb, a->ptr[i], obj)) { - a->len--; - memmove(&a->ptr[i], &a->ptr[i + 1], (a->len - i) * sizeof(a->ptr[i])); + for (i = 0; i < ARY_LEN(a); i++) { + if (mrb_obj_eq(mrb, ARY_PTR(a)[i], obj)) { + mrb_int len = ARY_LEN(a)-1; + mrb_value *ptr = ARY_PTR(a); + + ARY_SET_LEN(a, len); + memmove(&ptr[i], &ptr[i + 1], (len - i) * sizeof(mrb_value)); break; } } @@ -544,22 +550,30 @@ mark_context_stack(mrb_state *mrb, struct mrb_context *c) { size_t i; size_t e; + mrb_value nil; + int nregs; + if (c->stack == NULL) return; e = c->stack - c->stbase; - if (c->ci) e += c->ci->nregs; + if (c->ci) { + nregs = c->ci->argc + 2; + if (c->ci->nregs > nregs) + nregs = c->ci->nregs; + e += nregs; + } if (c->stbase + e > c->stend) e = c->stend - c->stbase; for (i=0; i<e; i++) { mrb_value v = c->stbase[i]; if (!mrb_immediate_p(v)) { - if (mrb_basic_ptr(v)->tt == MRB_TT_FREE) { - c->stbase[i] = mrb_nil_value(); - } - else { - mrb_gc_mark(mrb, mrb_basic_ptr(v)); - } + mrb_gc_mark(mrb, mrb_basic_ptr(v)); } } + e = c->stend - c->stbase; + nil = mrb_nil_value(); + for (; i<e; i++) { + c->stbase[i] = nil; + } } static void @@ -568,10 +582,12 @@ mark_context(mrb_state *mrb, struct mrb_context *c) int i; mrb_callinfo *ci; - /* mark stack */ - mark_context_stack(mrb, c); + if (c->status == MRB_FIBER_TERMINATED) return; /* mark VM stack */ + mark_context_stack(mrb, c); + + /* mark call stack */ if (c->cibase) { for (ci = c->cibase; ci <= c->ci; ci++) { mrb_gc_mark(mrb, (struct RBasic*)ci->env); @@ -585,8 +601,9 @@ mark_context(mrb_state *mrb, struct mrb_context *c) mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]); } /* mark fibers */ - if (c->prev && c->prev->fib) { - mrb_gc_mark(mrb, (struct RBasic*)c->prev->fib); + mrb_gc_mark(mrb, (struct RBasic*)c->fib); + if (c->prev) { + mark_context(mrb, c->prev); } } @@ -638,7 +655,12 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) struct REnv *e = (struct REnv*)obj; mrb_int i, len; - if MRB_ENV_STACK_SHARED_P(e) break; + if (MRB_ENV_STACK_SHARED_P(e)) { + if (e->cxt.c->fib) { + mrb_gc_mark(mrb, (struct RBasic*)e->cxt.c->fib); + } + break; + } len = MRB_ENV_STACK_LEN(e); for (i=0; i<len; i++) { mrb_gc_mark_value(mrb, e->stack[i]); @@ -659,8 +681,8 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) struct RArray *a = (struct RArray*)obj; size_t i, e; - for (i=0,e=a->len; i<e; i++) { - mrb_gc_mark_value(mrb, a->ptr[i]); + for (i=0,e=ARY_LEN(a); i<e; i++) { + mrb_gc_mark_value(mrb, ARY_PTR(a)[i]); } } break; @@ -701,7 +723,7 @@ mrb_gc_mark(mrb_state *mrb, struct RBasic *obj) static void obj_free(mrb_state *mrb, struct RBasic *obj, int end) { - DEBUG(printf("obj_free(%p,tt=%d)\n",obj,obj->tt)); + DEBUG(fprintf(stderr, "obj_free(%p,tt=%d)\n",obj,obj->tt)); switch (obj->tt) { /* immediate - no mark */ case MRB_TT_TRUE: @@ -723,8 +745,6 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) case MRB_TT_EXCEPTION: mrb_gc_free_iv(mrb, (struct RObject*)obj); - if ((struct RObject*)obj == mrb->backtrace.exc) - mrb->backtrace.exc = 0; break; case MRB_TT_CLASS: @@ -753,6 +773,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) case MRB_TT_FIBER: { struct mrb_context *c = ((struct RFiber*)obj)->cxt; + if (!end && c && c != mrb->root_c) { mrb_callinfo *ci = c->ci; mrb_callinfo *ce = c->cibase; @@ -772,9 +793,9 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) case MRB_TT_ARRAY: if (ARY_SHARED_P(obj)) - mrb_ary_decref(mrb, ((struct RArray*)obj)->aux.shared); - else - mrb_free(mrb, ((struct RArray*)obj)->ptr); + mrb_ary_decref(mrb, ((struct RArray*)obj)->as.heap.aux.shared); + else if (!ARY_EMBED_P(obj)) + mrb_free(mrb, ((struct RArray*)obj)->as.heap.ptr); break; case MRB_TT_HASH: @@ -819,7 +840,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) static void root_scan_phase(mrb_state *mrb, mrb_gc *gc) { - size_t i, e; + int i, e; if (!is_minor_gc(gc)) { gc->gray_list = NULL; @@ -857,12 +878,6 @@ root_scan_phase(mrb_state *mrb, mrb_gc *gc) mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self); /* mark exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); - /* mark backtrace */ - mrb_gc_mark(mrb, (struct RBasic*)mrb->backtrace.exc); - e = (size_t)mrb->backtrace.n; - for (i=0; i<e; i++) { - mrb_gc_mark(mrb, (struct RBasic*)mrb->backtrace.entries[i].klass); - } /* mark pre-allocated exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err); mrb_gc_mark(mrb, (struct RBasic*)mrb->stack_err); @@ -870,12 +885,9 @@ root_scan_phase(mrb_state *mrb, mrb_gc *gc) mrb_gc_mark(mrb, (struct RBasic*)mrb->arena_err); #endif - mark_context(mrb, mrb->root_c); - if (mrb->root_c->fib) { - mrb_gc_mark(mrb, (struct RBasic*)mrb->root_c->fib); - } + mark_context(mrb, mrb->c); if (mrb->root_c != mrb->c) { - mark_context(mrb, mrb->c); + mark_context(mrb, mrb->root_c); } } @@ -927,7 +939,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) children += i; /* mark ensure stack */ - children += (c->ci) ? c->ci->eidx : 0; + children += c->eidx; /* mark closure */ if (c->cibase) { @@ -941,7 +953,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) case MRB_TT_ARRAY: { struct RArray *a = (struct RArray*)obj; - children += a->len; + children += ARY_LEN(a); } break; @@ -988,7 +1000,16 @@ incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) static void final_marking_phase(mrb_state *mrb, mrb_gc *gc) { - mark_context_stack(mrb, mrb->root_c); + int i, e; + + /* mark arena */ + for (i=0,e=gc->arena_idx; i<e; i++) { + mrb_gc_mark(mrb, gc->arena[i]); + } + mrb_gc_mark_gv(mrb); + mark_context(mrb, mrb->c); + mark_context(mrb, mrb->root_c); + mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); gc_mark_gray_list(mrb, gc); mrb_assert(gc->gray_list == NULL); gc->gray_list = gc->atomic_gray_list; @@ -1153,7 +1174,7 @@ mrb_incremental_gc(mrb_state *mrb) { mrb_gc *gc = &mrb->gc; - if (gc->disabled) return; + if (gc->disabled || gc->iterating) return; GC_INVOKE_TIME_REPORT("mrb_incremental_gc()"); GC_TIME_START; @@ -1193,7 +1214,7 @@ mrb_full_gc(mrb_state *mrb) { mrb_gc *gc = &mrb->gc; - if (gc->disabled) return; + if (gc->disabled || gc->iterating) return; GC_INVOKE_TIME_REPORT("mrb_full_gc()"); GC_TIME_START; @@ -1225,34 +1246,6 @@ mrb_garbage_collect(mrb_state *mrb) mrb_full_gc(mrb); } -MRB_API int -mrb_gc_arena_save(mrb_state *mrb) -{ - return mrb->gc.arena_idx; -} - -MRB_API void -mrb_gc_arena_restore(mrb_state *mrb, int idx) -{ - mrb_gc *gc = &mrb->gc; - -#ifndef MRB_GC_FIXED_ARENA - int capa = gc->arena_capa; - - if (idx < capa / 2) { - capa = (int)(capa * 0.66); - if (capa < MRB_GC_ARENA_SIZE) { - capa = MRB_GC_ARENA_SIZE; - } - if (capa != gc->arena_capa) { - gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa); - gc->arena_capa = capa; - } - } -#endif - gc->arena_idx = idx; -} - /* * Field write barrier * Paint obj(Black) -> value(White) to obj(Black) -> value(Gray). @@ -1430,6 +1423,10 @@ gc_step_ratio_set(mrb_state *mrb, mrb_value obj) static void change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable) { + if (gc->disabled || gc->iterating) { + mrb_raise(mrb, E_RUNTIME_ERROR, "generational mode changed when GC disabled"); + return; + } if (is_generational(gc) && !enable) { clear_all_old(mrb, gc); mrb_assert(gc->state == MRB_GC_STATE_ROOT); @@ -1481,17 +1478,18 @@ gc_generational_mode_set(mrb_state *mrb, mrb_value self) static void gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data) { - mrb_heap_page* page = gc->heaps; + mrb_heap_page* page; + page = gc->heaps; while (page != NULL) { - RVALUE *p, *pend; + RVALUE *p; + int i; p = objects(page); - pend = p + MRB_HEAP_PAGE_SIZE; - for (;p < pend; p++) { - (*callback)(mrb, &p->as.basic, data); + for (i=0; i < MRB_HEAP_PAGE_SIZE; i++) { + if ((*callback)(mrb, &p[i].as.basic, data) == MRB_EACH_OBJ_BREAK) + return; } - page = page->next; } } @@ -1499,7 +1497,27 @@ gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data) { - gc_each_objects(mrb, &mrb->gc, callback, data); + mrb_bool iterating = mrb->gc.iterating; + + mrb->gc.iterating = TRUE; + if (iterating) { + gc_each_objects(mrb, &mrb->gc, callback, data); + } + else { + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + gc_each_objects(mrb, &mrb->gc, callback, data); + mrb->jmp = prev_jmp; + mrb->gc.iterating = iterating; + } MRB_CATCH(&c_jmp) { + mrb->gc.iterating = iterating; + mrb->jmp = prev_jmp; + MRB_THROW(prev_jmp); + } MRB_END_EXC(&c_jmp); + } } #ifdef GC_TEST diff --git a/src/hash.c b/src/hash.c index 86dfae996..27d25b384 100644 --- a/src/hash.c +++ b/src/hash.c @@ -140,7 +140,7 @@ mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash) MRB_API mrb_value -mrb_hash_new_capa(mrb_state *mrb, int capa) +mrb_hash_new_capa(mrb_state *mrb, mrb_int capa) { struct RHash *h; @@ -238,7 +238,7 @@ mrb_hash_dup(mrb_state *mrb, mrb_value hash) ret = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); ret->ht = kh_init(ht, mrb); - if (kh_size(h) > 0) { + if (h && kh_size(h) > 0) { ret_h = ret->ht; for (k = kh_begin(h); k != kh_end(h); k++) { @@ -759,7 +759,7 @@ mrb_hash_keys(mrb_state *mrb, mrb_value hash) ary = mrb_ary_new_capa(mrb, kh_size(h)); end = kh_size(h)-1; mrb_ary_set(mrb, ary, end, mrb_nil_value()); - p = mrb_ary_ptr(ary)->ptr; + p = RARRAY_PTR(ary); for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { mrb_value kv = kh_key(h, k); diff --git a/src/init.c b/src/init.c index 9a6496df1..afd69975a 100644 --- a/src/init.c +++ b/src/init.c @@ -13,9 +13,9 @@ void mrb_init_kernel(mrb_state*); void mrb_init_comparable(mrb_state*); void mrb_init_enumerable(mrb_state*); void mrb_init_symbol(mrb_state*); +void mrb_init_string(mrb_state*); void mrb_init_exception(mrb_state*); void mrb_init_proc(mrb_state*); -void mrb_init_string(mrb_state*); void mrb_init_array(mrb_state*); void mrb_init_hash(mrb_state*); void mrb_init_numeric(mrb_state*); @@ -38,9 +38,9 @@ mrb_init_core(mrb_state *mrb) mrb_init_enumerable(mrb); DONE; mrb_init_symbol(mrb); DONE; + mrb_init_string(mrb); DONE; mrb_init_exception(mrb); DONE; mrb_init_proc(mrb); DONE; - mrb_init_string(mrb); DONE; mrb_init_array(mrb); DONE; mrb_init_hash(mrb); DONE; mrb_init_numeric(mrb); DONE; diff --git a/src/kernel.c b/src/kernel.c index 8670e0651..bd21ddf53 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -136,38 +136,38 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) { mrb_callinfo *ci = mrb->c->ci; mrb_value *bp; - mrb_bool given_p; bp = ci->stackent + 1; ci--; if (ci <= mrb->c->cibase) { - given_p = FALSE; + return mrb_false_value(); } - else { - /* block_given? called within block; check upper scope */ - if (ci->proc->env) { - struct REnv *e = ci->proc->env; - mrb_value *sp; + /* block_given? called within block; check upper scope */ + if (ci->proc->env) { + struct REnv *e = ci->proc->env; - while (e->c) { - e = (struct REnv*)e->c; - } - sp = e->stack; - if (sp) { - /* top-level does not have block slot (alway false) */ - if (sp == mrb->c->stbase) - return mrb_false_value(); - ci = mrb->c->cibase + e->cioff; - bp = ci[1].stackent + 1; - } + while (e->c) { + e = (struct REnv*)e->c; } - if (ci->argc > 0) { - bp += ci->argc; + /* top-level does not have block slot (always false) */ + if (e->stack == mrb->c->stbase) + return mrb_false_value(); + if (e->stack && e->cioff < 0) { + /* use saved block arg position */ + bp = &e->stack[-e->cioff]; + ci = 0; /* no callinfo available */ + } + else { + ci = e->cxt.c->cibase + e->cioff; + bp = ci[1].stackent + 1; } - given_p = !mrb_nil_p(*bp); } - - return mrb_bool_value(given_p); + if (ci && ci->argc > 0) { + bp += ci->argc; + } + if (mrb_nil_p(*bp)) + return mrb_false_value(); + return mrb_true_value(); } /* 15.3.1.3.7 */ @@ -199,11 +199,13 @@ mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) /* copy singleton(unnamed) class */ struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class); - if ((mrb_type(obj) == MRB_TT_CLASS) || (mrb_type(obj) == MRB_TT_SCLASS)) { - clone->c = clone; - } - else { + switch (mrb_type(obj)) { + case MRB_TT_CLASS: + case MRB_TT_SCLASS: + break; + default: clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); + break; } clone->super = klass->super; if (klass->iv) { @@ -241,7 +243,12 @@ copy_class(mrb_state *mrb, mrb_value dst, mrb_value src) c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); c1->super->flags |= MRB_FLAG_IS_ORIGIN; } - dc->mt = kh_copy(mt, mrb, sc->mt); + if (sc->mt) { + dc->mt = kh_copy(mt, mrb, sc->mt); + } + else { + dc->mt = kh_init(mt, mrb); + } dc->super = sc->super; MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc)); } @@ -311,6 +318,7 @@ mrb_obj_clone(mrb_state *mrb, mrb_value self) } p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self)); p->c = mrb_singleton_class_clone(mrb, self); + mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c); clone = mrb_obj_value(p); init_copy(mrb, clone, self); @@ -403,11 +411,8 @@ mrb_obj_extend_m(mrb_state *mrb, mrb_value self) { mrb_value *argv; mrb_int argc; - mrb_value args; mrb_get_args(mrb, "*", &argv, &argc); - args = mrb_ary_new_from_values(mrb, argc, argv); - argv = (mrb_value*)RARRAY_PTR(args); return mrb_obj_extend(mrb, argc, argv, self); } @@ -867,7 +872,6 @@ mrb_f_raise(mrb_state *mrb, mrb_value self) /* fall through */ default: exc = mrb_make_exception(mrb, argc, a); - mrb_obj_iv_set(mrb, mrb_obj_ptr(exc), mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, mrb->c->ci->pc)); mrb_exc_raise(mrb, exc); break; } @@ -977,7 +981,7 @@ mrb_obj_missing(mrb_state *mrb, mrb_value mod) mrb_value *a; mrb_int alen; - mrb_get_args(mrb, "n*", &name, &a, &alen); + mrb_get_args(mrb, "n*!", &name, &a, &alen); mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a)); /* not reached */ return mrb_nil_value(); @@ -1160,8 +1164,8 @@ mrb_local_variables(mrb_state *mrb, mrb_value self) while (e) { if (MRB_ENV_STACK_SHARED_P(e) && - !MRB_PROC_CFUNC_P(mrb->c->cibase[e->cioff].proc)) { - irep = mrb->c->cibase[e->cioff].proc->body.irep; + !MRB_PROC_CFUNC_P(e->cxt.c->cibase[e->cioff].proc)) { + irep = e->cxt.c->cibase[e->cioff].proc->body.irep; if (irep->lv) { for (i = 0; i + 1 < irep->nlocals; ++i) { if (irep->lv[i].name) { diff --git a/src/numeric.c b/src/numeric.c index 2117fcc3c..be71f1185 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -52,14 +52,32 @@ static mrb_value num_pow(mrb_state *mrb, mrb_value x) { mrb_value y; - mrb_float d, yv; + mrb_float d; mrb_get_args(mrb, "o", &y); - yv = mrb_to_flo(mrb, y); - d = pow(mrb_to_flo(mrb, x), yv); - if (mrb_fixnum_p(x) && mrb_fixnum_p(y) && FIXABLE(d) && yv > 0 && - (d < 0 || (d > 0 && (mrb_int)d > 0))) - return mrb_fixnum_value((mrb_int)d); + if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) { + /* try ipow() */ + mrb_int base = mrb_fixnum(x); + mrb_int exp = mrb_fixnum(y); + mrb_int result = 1; + + if (exp < 0) goto float_pow; + for (;;) { + if (exp & 1) { + if (mrb_int_mul_overflow(result, base, &result)) { + goto float_pow; + } + } + exp >>= 1; + if (exp == 0) break; + if (mrb_int_mul_overflow(base, base, &base)) { + goto float_pow; + } + } + return mrb_fixnum_value(result); + } + float_pow: + d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y)); return mrb_float_value(mrb, d); } @@ -169,7 +187,12 @@ flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float * mrb_float mod; if (y == 0.0) { - div = INFINITY; + if (x == 0.0) { + div = NAN; + } + else { + div = INFINITY; + } mod = NAN; } else { @@ -371,7 +394,7 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width) val *= 2; } } - if (FIXABLE(val)) { + if (FIXABLE_FLOAT(val)) { return mrb_fixnum_value((mrb_int)val); } return mrb_float_value(mrb, val); @@ -408,13 +431,13 @@ flo_hash(mrb_state *mrb, mrb_value num) mrb_float d; char *c; size_t i; - int hash; + mrb_int hash; d = (mrb_float)mrb_fixnum(num); /* normalize -0.0 to 0.0 */ if (d == 0) d = 0.0; c = (char*)&d; - for (hash=0, i=0; i<sizeof(mrb_float);i++) { + for (hash=0,i=0; i<sizeof(mrb_float); i++) { hash = (hash * 971) ^ (unsigned char)c[i]; } if (hash < 0) hash = -hash; @@ -476,6 +499,17 @@ flo_finite_p(mrb_state *mrb, mrb_value num) return mrb_bool_value(isfinite(mrb_float(num))); } +void +mrb_check_num_exact(mrb_state *mrb, mrb_float num) +{ + if (isinf(num)) { + mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity"); + } + if (isnan(num)) { + mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); + } +} + /* 15.2.9.3.10 */ /* * call-seq: @@ -494,7 +528,8 @@ flo_floor(mrb_state *mrb, mrb_value num) { mrb_float f = floor(mrb_float(num)); - if (!FIXABLE(f)) { + mrb_check_num_exact(mrb, f); + if (!FIXABLE_FLOAT(f)) { return mrb_float_value(mrb, f); } return mrb_fixnum_value((mrb_int)f); @@ -519,7 +554,8 @@ flo_ceil(mrb_state *mrb, mrb_value num) { mrb_float f = ceil(mrb_float(num)); - if (!FIXABLE(f)) { + mrb_check_num_exact(mrb, f); + if (!FIXABLE_FLOAT(f)) { return mrb_float_value(mrb, f); } return mrb_fixnum_value((mrb_int)f); @@ -566,14 +602,10 @@ flo_round(mrb_state *mrb, mrb_value num) mrb_get_args(mrb, "|i", &ndigits); number = mrb_float(num); - if (isinf(number)) { - if (0 < ndigits) return num; - else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, number < 0 ? "-Infinity" : "Infinity"); - } - if (isnan(number)) { - if (0 < ndigits) return num; - else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); + if (0 < ndigits && (isinf(number) || isnan(number))) { + return num; } + mrb_check_num_exact(mrb, number); f = 1.0; i = ndigits >= 0 ? ndigits : -ndigits; @@ -610,17 +642,6 @@ flo_round(mrb_state *mrb, mrb_value num) return mrb_fixnum_value((mrb_int)number); } -void -mrb_check_num_exact(mrb_state *mrb, mrb_float num) -{ - if (isinf(num)) { - mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity"); - } - if (isnan(num)) { - mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); - } -} - /* 15.2.9.3.14 */ /* 15.2.9.3.15 */ /* @@ -640,8 +661,8 @@ flo_truncate(mrb_state *mrb, mrb_value num) if (f > 0.0) f = floor(f); if (f < 0.0) f = ceil(f); - if (!FIXABLE(f)) { - mrb_check_num_exact(mrb, f); + mrb_check_num_exact(mrb, f); + if (!FIXABLE_FLOAT(f)) { return mrb_float_value(mrb, f); } return mrb_fixnum_value((mrb_int)f); @@ -795,8 +816,10 @@ fix_divmod(mrb_state *mrb, mrb_value x) mrb_int div, mod; if (mrb_fixnum(y) == 0) { - return mrb_assoc_new(mrb, mrb_float_value(mrb, INFINITY), - mrb_float_value(mrb, NAN)); + return mrb_assoc_new(mrb, ((mrb_fixnum(x) == 0) ? + mrb_float_value(mrb, NAN): + mrb_float_value(mrb, INFINITY)), + mrb_float_value(mrb, NAN)); } fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod); return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod)); @@ -806,7 +829,7 @@ fix_divmod(mrb_state *mrb, mrb_value x) mrb_value a, b; flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod); - a = mrb_float_value(mrb, (mrb_int)div); + a = mrb_float_value(mrb, div); b = mrb_float_value(mrb, mod); return mrb_assoc_new(mrb, a, b); } @@ -822,7 +845,7 @@ flo_divmod(mrb_state *mrb, mrb_value x) mrb_get_args(mrb, "o", &y); flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod); - a = mrb_float_value(mrb, (mrb_int)div); + a = mrb_float_value(mrb, div); b = mrb_float_value(mrb, mod); return mrb_assoc_new(mrb, a, b); } @@ -938,22 +961,24 @@ fix_xor(mrb_state *mrb, mrb_value x) static mrb_value lshift(mrb_state *mrb, mrb_int val, mrb_int width) { - mrb_assert(width > 0); + if (width < 0) { /* mrb_int overflow */ + return mrb_float_value(mrb, INFINITY); + } if (val > 0) { if ((width > NUMERIC_SHIFT_WIDTH_MAX) || (val > (MRB_INT_MAX >> width))) { goto bit_overflow; } + return mrb_fixnum_value(val << width); } else { if ((width > NUMERIC_SHIFT_WIDTH_MAX) || (val < (MRB_INT_MIN >> width))) { goto bit_overflow; } + return mrb_fixnum_value(val * (1u << width)); } - return mrb_fixnum_value(val << width); - bit_overflow: { mrb_float f = (mrb_float)val; @@ -967,7 +992,9 @@ bit_overflow: static mrb_value rshift(mrb_int val, mrb_int width) { - mrb_assert(width > 0); + if (width < 0) { /* mrb_int overflow */ + return mrb_fixnum_value(0); + } if (width >= NUMERIC_SHIFT_WIDTH_MAX) { if (val < 0) { return mrb_fixnum_value(-1); @@ -995,6 +1022,7 @@ fix_lshift(mrb_state *mrb, mrb_value x) return x; } val = mrb_fixnum(x); + if (val == 0) return x; if (width < 0) { return rshift(val, -width); } @@ -1019,6 +1047,7 @@ fix_rshift(mrb_state *mrb, mrb_value x) return x; } val = mrb_fixnum(x); + if (val == 0) return x; if (width < 0) { return lshift(mrb, val, -width); } @@ -1072,7 +1101,7 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x) if (isnan(d)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); } - if (FIXABLE(d)) { + if (FIXABLE_FLOAT(d)) { z = (mrb_int)d; } else { @@ -1276,68 +1305,72 @@ mrb_init_numeric(mrb_state *mrb) /* Numeric Class */ numeric = mrb_define_class(mrb, "Numeric", mrb->object_class); /* 15.2.7 */ - mrb_define_method(mrb, numeric, "**", num_pow, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, numeric, "/", num_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.4 */ - mrb_define_method(mrb, numeric, "quo", num_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */ - mrb_define_method(mrb, numeric, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */ + mrb_define_method(mrb, numeric, "**", num_pow, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, numeric, "/", num_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.4 */ + mrb_define_method(mrb, numeric, "quo", num_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */ + mrb_define_method(mrb, numeric, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */ /* Integer Class */ integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */ MRB_SET_INSTANCE_TT(integer, MRB_TT_FIXNUM); mrb_undef_class_method(mrb, integer, "new"); - mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */ - mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE()); + mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */ + mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE()); + mrb_define_method(mrb, integer, "ceil", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.8 (x) */ + mrb_define_method(mrb, integer, "floor", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 (x) */ + mrb_define_method(mrb, integer, "round", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 (x) */ + mrb_define_method(mrb, integer, "truncate", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.15 (x) */ /* Fixnum Class */ mrb->fixnum_class = fixnum = mrb_define_class(mrb, "Fixnum", integer); - mrb_define_method(mrb, fixnum, "+", fix_plus, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */ - mrb_define_method(mrb, fixnum, "-", fix_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */ - mrb_define_method(mrb, fixnum, "*", fix_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */ - mrb_define_method(mrb, fixnum, "%", fix_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */ - mrb_define_method(mrb, fixnum, "==", fix_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */ - mrb_define_method(mrb, fixnum, "~", fix_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */ - mrb_define_method(mrb, fixnum, "&", fix_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */ - mrb_define_method(mrb, fixnum, "|", fix_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */ - mrb_define_method(mrb, fixnum, "^", fix_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */ - mrb_define_method(mrb, fixnum, "<<", fix_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */ - mrb_define_method(mrb, fixnum, ">>", fix_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */ - mrb_define_method(mrb, fixnum, "eql?", fix_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ - mrb_define_method(mrb, fixnum, "hash", flo_hash, MRB_ARGS_NONE()); /* 15.2.8.3.18 */ - mrb_define_method(mrb, fixnum, "to_f", fix_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */ - mrb_define_method(mrb, fixnum, "to_s", fix_to_s, MRB_ARGS_NONE()); /* 15.2.8.3.25 */ - mrb_define_method(mrb, fixnum, "inspect", fix_to_s, MRB_ARGS_NONE()); - mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */ + mrb_define_method(mrb, fixnum, "+", fix_plus, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */ + mrb_define_method(mrb, fixnum, "-", fix_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */ + mrb_define_method(mrb, fixnum, "*", fix_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */ + mrb_define_method(mrb, fixnum, "%", fix_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */ + mrb_define_method(mrb, fixnum, "==", fix_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */ + mrb_define_method(mrb, fixnum, "~", fix_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */ + mrb_define_method(mrb, fixnum, "&", fix_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */ + mrb_define_method(mrb, fixnum, "|", fix_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */ + mrb_define_method(mrb, fixnum, "^", fix_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */ + mrb_define_method(mrb, fixnum, "<<", fix_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */ + mrb_define_method(mrb, fixnum, ">>", fix_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */ + mrb_define_method(mrb, fixnum, "eql?", fix_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ + mrb_define_method(mrb, fixnum, "hash", flo_hash, MRB_ARGS_NONE()); /* 15.2.8.3.18 */ + mrb_define_method(mrb, fixnum, "to_f", fix_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */ + mrb_define_method(mrb, fixnum, "to_s", fix_to_s, MRB_ARGS_NONE()); /* 15.2.8.3.25 */ + mrb_define_method(mrb, fixnum, "inspect", fix_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */ /* Float Class */ mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */ MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT); mrb_undef_class_method(mrb, fl, "new"); - mrb_define_method(mrb, fl, "+", flo_plus, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */ - mrb_define_method(mrb, fl, "-", flo_minus, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */ - mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */ - mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */ - mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */ - mrb_define_method(mrb, fl, "~", flo_rev, MRB_ARGS_NONE()); - mrb_define_method(mrb, fl, "&", flo_and, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, fl, "|", flo_or, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, fl, "^", flo_xor, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, fl, ">>", flo_lshift, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, fl, "<<", flo_rshift, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */ - mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */ - mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */ - mrb_define_method(mrb, fl, "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */ - mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */ - mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */ - mrb_define_method(mrb, fl, "to_i", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.14 */ - mrb_define_method(mrb, fl, "to_int", flo_truncate, MRB_ARGS_NONE()); - mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */ - mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ - - mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */ - mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE()); - mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE()); + mrb_define_method(mrb, fl, "+", flo_plus, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */ + mrb_define_method(mrb, fl, "-", flo_minus, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */ + mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */ + mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */ + mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */ + mrb_define_method(mrb, fl, "~", flo_rev, MRB_ARGS_NONE()); + mrb_define_method(mrb, fl, "&", flo_and, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "|", flo_or, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "^", flo_xor, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, ">>", flo_lshift, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "<<", flo_rshift, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */ + mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */ + mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */ + mrb_define_method(mrb, fl, "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */ + mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */ + mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */ + mrb_define_method(mrb, fl, "to_i", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.14 */ + mrb_define_method(mrb, fl, "to_int", flo_truncate, MRB_ARGS_NONE()); + mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */ + mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ + + mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */ + mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE()); #ifdef INFINITY mrb_define_const(mrb, fl, "INFINITY", mrb_float_value(mrb, INFINITY)); diff --git a/src/object.c b/src/object.c index eb2c23e63..a78e9a443 100644 --- a/src/object.c +++ b/src/object.c @@ -534,8 +534,11 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base) switch (mrb_type(val)) { case MRB_TT_FLOAT: if (base != 0) goto arg_error; - if (FIXABLE(mrb_float(val))) { - break; + else { + mrb_float f = mrb_float(val); + if (FIXABLE_FLOAT(f)) { + break; + } } return mrb_flo_to_fixnum(mrb, val); @@ -553,6 +556,7 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base) if (base != 0) { tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { + val = tmp; goto string_conv; } arg_error: diff --git a/src/pool.c b/src/pool.c index 18f66fc27..db4546abf 100644 --- a/src/pool.c +++ b/src/pool.c @@ -13,8 +13,12 @@ /* allocated memory address should be multiple of POOL_ALIGNMENT */ /* or undef it if alignment does not matter */ #ifndef POOL_ALIGNMENT +#if INTPTR_MAX == INT64_MAX +#define POOL_ALIGNMENT 8 +#else #define POOL_ALIGNMENT 4 #endif +#endif /* page size of memory pool */ #ifndef POOL_PAGE_SIZE #define POOL_PAGE_SIZE 16000 diff --git a/src/proc.c b/src/proc.c index ab22bc1fa..ee4a493cd 100644 --- a/src/proc.c +++ b/src/proc.c @@ -41,7 +41,7 @@ env_new(mrb_state *mrb, int nlocals) e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env); MRB_SET_ENV_STACK_LEN(e, nlocals); - e->mid = mrb->c->ci->mid; + e->cxt.c = mrb->c; e->cioff = mrb->c->ci - mrb->c->cibase; e->stack = mrb->c->stack; @@ -271,6 +271,7 @@ mrb_init_proc(mrb_state *mrb) call_irep->flags = MRB_ISEQ_NO_FREE; call_irep->iseq = call_iseq; call_irep->ilen = 1; + call_irep->nregs = 2; /* receiver and block */ mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_ANY()); mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1)); diff --git a/src/state.c b/src/state.c index 2a02debef..0be5a184a 100644 --- a/src/state.c +++ b/src/state.c @@ -246,7 +246,6 @@ mrb_close(mrb_state *mrb) /* free */ mrb_gc_free_gv(mrb); - mrb_free_backtrace(mrb); mrb_free_context(mrb, mrb->root_c); mrb_free_symtbl(mrb); mrb_alloca_free(mrb); diff --git a/src/string.c b/src/string.c index 742046361..6fdb7fe72 100644 --- a/src/string.c +++ b/src/string.c @@ -478,15 +478,14 @@ str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) return str_subseq(mrb, str, beg, len); } -static mrb_int -str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset) +MRB_API mrb_int +mrb_str_index(mrb_state *mrb, mrb_value str, const char *sptr, mrb_int slen, mrb_int offset) { mrb_int pos; - char *s, *sptr; - mrb_int len, slen; + char *s; + mrb_int len; len = RSTRING_LEN(str); - slen = RSTRING_LEN(sub); if (offset < 0) { offset += len; if (offset < 0) return -1; @@ -498,14 +497,24 @@ str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset) } if (slen == 0) return offset; /* need proceed one character at a time */ - sptr = RSTRING_PTR(sub); - slen = RSTRING_LEN(sub); len = RSTRING_LEN(str) - offset; pos = mrb_memsearch(sptr, slen, s, len); if (pos < 0) return pos; return pos + offset; } +static mrb_int +str_index_str(mrb_state *mrb, mrb_value str, mrb_value str2, mrb_int offset) +{ + const char *ptr; + mrb_int len; + + ptr = RSTRING_PTR(str2); + len = RSTRING_LEN(str2); + + return mrb_str_index(mrb, str, ptr, len, offset); +} + static void check_frozen(mrb_state *mrb, struct RString *s) { @@ -520,9 +529,9 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) long len; check_frozen(mrb, s1); + if (s1 == s2) return mrb_obj_value(s1); s1->flags &= ~MRB_STR_NO_UTF; s1->flags |= s2->flags&MRB_STR_NO_UTF; - if (s1 == s2) return mrb_obj_value(s1); len = RSTR_LEN(s2); if (RSTR_SHARED_P(s1)) { str_decref(mrb, s1->as.heap.aux.shared); @@ -685,13 +694,20 @@ mrb_str_modify(mrb_state *mrb, struct RString *s) p = RSTR_PTR(s); len = s->as.heap.len; - ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); + if (len < RSTRING_EMBED_LEN_MAX) { + RSTR_SET_EMBED_FLAG(s); + RSTR_SET_EMBED_LEN(s, len); + ptr = RSTR_PTR(s); + } + else { + ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); + s->as.heap.ptr = ptr; + s->as.heap.aux.capa = len; + } if (p) { memcpy(ptr, p, len); } ptr[len] = '\0'; - s->as.heap.ptr = ptr; - s->as.heap.aux.capa = len; str_decref(mrb, shared); } RSTR_UNSET_SHARED_FLAG(s); @@ -1089,7 +1105,7 @@ num_index: return str; case MRB_TT_STRING: - if (str_index(mrb, str, indx, 0) != -1) + if (str_index_str(mrb, str, indx, 0) != -1) return mrb_str_dup(mrb, indx); return mrb_nil_value(); @@ -1514,13 +1530,13 @@ mrb_str_hash(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); mrb_int len = RSTR_LEN(s); char *p = RSTR_PTR(s); - mrb_int key = 0; + uint64_t key = 0; while (len--) { key = key*65599 + *p; p++; } - return key + (key>>5); + return (mrb_int)(key + (key>>5)); } /* 15.2.10.5.20 */ @@ -1556,7 +1572,7 @@ mrb_str_include(mrb_state *mrb, mrb_value self) mrb_value str2; mrb_get_args(mrb, "S", &str2); - if (str_index(mrb, self, str2, 0) < 0) + if (str_index_str(mrb, self, str2, 0) < 0) return mrb_bool_value(FALSE); return mrb_bool_value(TRUE); } @@ -1583,14 +1599,14 @@ mrb_str_include(mrb_state *mrb, mrb_value self) * "hello".index(/[aeiou]/, -3) #=> 4 */ static mrb_value -mrb_str_index(mrb_state *mrb, mrb_value str) +mrb_str_index_m(mrb_state *mrb, mrb_value str) { mrb_value *argv; mrb_int argc; mrb_value sub; mrb_int pos, clen; - mrb_get_args(mrb, "*", &argv, &argc); + mrb_get_args(mrb, "*!", &argv, &argc); if (argc == 2) { mrb_get_args(mrb, "oi", &sub, &pos); } @@ -1624,7 +1640,7 @@ mrb_str_index(mrb_state *mrb, mrb_value str) } /* fall through */ case MRB_TT_STRING: - pos = str_index(mrb, str, sub, pos); + pos = str_index_str(mrb, str, sub, pos); break; } @@ -1861,7 +1877,7 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str) mrb_value sub; mrb_int pos, len = RSTRING_CHAR_LEN(str); - mrb_get_args(mrb, "*", &argv, &argc); + mrb_get_args(mrb, "*!", &argv, &argc); if (argc == 2) { mrb_get_args(mrb, "oi", &sub, &pos); if (pos < 0) { @@ -1954,10 +1970,11 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) int argc; mrb_value spat = mrb_nil_value(); enum {awk, string, regexp} split_type = string; - long i = 0, lim_p; + mrb_int i = 0; mrb_int beg; mrb_int end; mrb_int lim = 0; + mrb_bool lim_p; mrb_value result, tmp; argc = mrb_get_args(mrb, "|oi", &spat, &lim); @@ -2744,7 +2761,7 @@ mrb_init_string(mrb_state *mrb) mrb_define_method(mrb, s, "hash", mrb_str_hash_m, MRB_ARGS_NONE()); /* 15.2.10.5.20 */ mrb_define_method(mrb, s, "include?", mrb_str_include, MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */ - mrb_define_method(mrb, s, "index", mrb_str_index, MRB_ARGS_ANY()); /* 15.2.10.5.22 */ + mrb_define_method(mrb, s, "index", mrb_str_index_m, MRB_ARGS_ANY()); /* 15.2.10.5.22 */ mrb_define_method(mrb, s, "initialize", mrb_str_init, MRB_ARGS_REQ(1)); /* 15.2.10.5.23 */ mrb_define_method(mrb, s, "initialize_copy", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.24 */ mrb_define_method(mrb, s, "intern", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.25 */ diff --git a/src/variable.c b/src/variable.c index f130a968f..86ac32e61 100644 --- a/src/variable.c +++ b/src/variable.c @@ -12,15 +12,13 @@ typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*); -#ifdef MRB_USE_IV_SEGLIST - -#ifndef MRB_SEGMENT_SIZE -#define MRB_SEGMENT_SIZE 4 +#ifndef MRB_IV_SEGMENT_SIZE +#define MRB_IV_SEGMENT_SIZE 4 #endif typedef struct segment { - mrb_sym key[MRB_SEGMENT_SIZE]; - mrb_value val[MRB_SEGMENT_SIZE]; + mrb_sym key[MRB_IV_SEGMENT_SIZE]; + mrb_value val[MRB_IV_SEGMENT_SIZE]; struct segment *next; } segment; @@ -71,7 +69,7 @@ iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) size_t i; while (seg) { - for (i=0; i<MRB_SEGMENT_SIZE; i++) { + for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) { mrb_sym key = seg->key[i]; /* Found room in last segment after last_len */ if (!seg->next && i >= t->last_len) { @@ -136,7 +134,7 @@ iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) seg = t->rootseg; while (seg) { - for (i=0; i<MRB_SEGMENT_SIZE; i++) { + for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) { mrb_sym key = seg->key[i]; if (!seg->next && i >= t->last_len) { @@ -171,7 +169,7 @@ iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) seg = t->rootseg; while (seg) { - for (i=0; i<MRB_SEGMENT_SIZE; i++) { + for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) { mrb_sym key = seg->key[i]; if (!seg->next && i >= t->last_len) { @@ -198,7 +196,7 @@ iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p) seg = t->rootseg; while (seg) { - for (i=0; i<MRB_SEGMENT_SIZE; i++) { + for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) { mrb_sym key = seg->key[i]; /* no value in last segment after last_len */ @@ -234,7 +232,7 @@ iv_size(mrb_state *mrb, iv_tbl *t) return size; } seg = seg->next; - size += MRB_SEGMENT_SIZE; + size += MRB_IV_SEGMENT_SIZE; } /* empty iv_tbl */ return 0; @@ -252,7 +250,7 @@ iv_copy(mrb_state *mrb, iv_tbl *t) t2 = iv_new(mrb); while (seg != NULL) { - for (i=0; i<MRB_SEGMENT_SIZE; i++) { + for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) { mrb_sym key = seg->key[i]; mrb_value val = seg->val[i]; @@ -280,115 +278,6 @@ iv_free(mrb_state *mrb, iv_tbl *t) mrb_free(mrb, t); } -#else - -#include <mruby/khash.h> - -#ifndef MRB_IVHASH_INIT_SIZE -#define MRB_IVHASH_INIT_SIZE 8 -#endif - -KHASH_DECLARE(iv, mrb_sym, mrb_value, TRUE) -KHASH_DEFINE(iv, mrb_sym, mrb_value, TRUE, kh_int_hash_func, kh_int_hash_equal) - -typedef struct iv_tbl { - khash_t(iv) h; -} iv_tbl; - -static iv_tbl* -iv_new(mrb_state *mrb) -{ - return (iv_tbl*)kh_init_size(iv, mrb, MRB_IVHASH_INIT_SIZE); -} - -static void -iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) -{ - khash_t(iv) *h = &t->h; - khiter_t k; - - k = kh_put(iv, mrb, h, sym); - kh_value(h, k) = val; -} - -static mrb_bool -iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) -{ - khash_t(iv) *h = &t->h; - khiter_t k; - - k = kh_get(iv, mrb, h, sym); - if (k != kh_end(h)) { - if (vp) *vp = kh_value(h, k); - return TRUE; - } - return FALSE; -} - -static mrb_bool -iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) -{ - khash_t(iv) *h = &t->h; - khiter_t k; - - if (h) { - k = kh_get(iv, mrb, h, sym); - if (k != kh_end(h)) { - mrb_value val = kh_value(h, k); - kh_del(iv, mrb, h, k); - if (vp) *vp = val; - return TRUE; - } - } - return FALSE; -} - -static mrb_bool -iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p) -{ - khash_t(iv) *h = &t->h; - khiter_t k; - int n; - - if (h) { - for (k = kh_begin(h); k != kh_end(h); k++) { - if (kh_exist(h, k)) { - n = (*func)(mrb, kh_key(h, k), kh_value(h, k), p); - if (n > 0) return FALSE; - if (n < 0) { - kh_del(iv, mrb, h, k); - } - } - } - } - return TRUE; -} - -static size_t -iv_size(mrb_state *mrb, iv_tbl *t) -{ - khash_t(iv) *h; - - if (t && (h = &t->h)) { - return kh_size(h); - } - return 0; -} - -static iv_tbl* -iv_copy(mrb_state *mrb, iv_tbl *t) -{ - return (iv_tbl*)kh_copy(iv, mrb, &t->h); -} - -static void -iv_free(mrb_state *mrb, iv_tbl *t) -{ - kh_destroy(iv, mrb, &t->h); -} - -#endif - static int iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { @@ -821,10 +710,19 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) if (cls && cls->tt == MRB_TT_SCLASS) { mrb_value klass; - klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, - mrb_intern_lit(mrb, "__attached__")); - c = mrb_class_ptr(klass); + klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, + mrb_intern_lit(mrb, "__attached__")); + switch (mrb_type(klass)) { + case MRB_TT_CLASS: + case MRB_TT_MODULE: + case MRB_TT_SCLASS: + c = mrb_class_ptr(klass); + break; + default: + c = cls; + break; + } } else{ c = cls; @@ -944,14 +842,14 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) if (c->iv && iv_get(mrb, c->iv, sym, &v)) { return v; } - if (c->tt == MRB_TT_SCLASS) { + c2 = c; + while (c2 && c2->tt == MRB_TT_SCLASS) { mrb_value klass; - klass = mrb_obj_iv_get(mrb, (struct RObject *)c, + klass = mrb_obj_iv_get(mrb, (struct RObject *)c2, mrb_intern_lit(mrb, "__attached__")); c2 = mrb_class_ptr(klass); - if (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE) - c = c2; } + if (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE) c = c2; c2 = c; for (;;) { c2 = mrb_class_outer_module(mrb, c2); @@ -35,6 +35,14 @@ void abort(void); #define STACK_INIT_SIZE 128 #define CALLINFO_INIT_SIZE 32 +#ifndef ENSURE_STACK_INIT_SIZE +#define ENSURE_STACK_INIT_SIZE 16 +#endif + +#ifndef RESCUE_STACK_INIT_SIZE +#define RESCUE_STACK_INIT_SIZE 16 +#endif + /* Define amount of linear stack growth. */ #ifndef MRB_STACK_GROWTH #define MRB_STACK_GROWTH 128 @@ -57,7 +65,28 @@ The value below allows about 60000 recursive calls in the simplest case. */ # define DEBUG(x) #endif -#define ARENA_RESTORE(mrb,ai) (mrb)->gc.arena_idx = (ai) + +#ifndef MRB_GC_FIXED_ARENA +static void +mrb_gc_arena_shrink(mrb_state *mrb, int idx) +{ + mrb_gc *gc = &mrb->gc; + int capa = gc->arena_capa; + + if (idx < capa / 4) { + capa >>= 2; + if (capa < MRB_GC_ARENA_SIZE) { + capa = MRB_GC_ARENA_SIZE; + } + if (capa != gc->arena_capa) { + gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa); + gc->arena_capa = capa; + } + } +} +#else +#define mrb_gc_arena_shrink(mrb,idx) +#endif #define CALL_MAXARGS 127 @@ -107,14 +136,17 @@ stack_init(mrb_state *mrb) } static inline void -envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase) +envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size) { mrb_callinfo *ci = mrb->c->cibase; if (newbase == oldbase) return; while (ci <= mrb->c->ci) { struct REnv *e = ci->env; - if (e && MRB_ENV_STACK_SHARED_P(e)) { + mrb_value *st; + + if (e && MRB_ENV_STACK_SHARED_P(e) && + (st = e->stack) && oldbase <= st && st < oldbase+size) { ptrdiff_t off = e->stack - oldbase; e->stack = newbase + off; @@ -124,22 +156,15 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase) } } -static inline void -init_new_stack_space(mrb_state *mrb, int room, int keep) -{ - if (room > keep) { - /* do not leave uninitialized malloc region */ - stack_clear(&(mrb->c->stack[keep]), room - keep); - } -} - /** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end */ static void -stack_extend_alloc(mrb_state *mrb, int room, int keep) +stack_extend_alloc(mrb_state *mrb, int room) { mrb_value *oldbase = mrb->c->stbase; - size_t size = mrb->c->stend - mrb->c->stbase; + mrb_value *newstack; + size_t oldsize = mrb->c->stend - mrb->c->stbase; + size_t size = oldsize; size_t off = mrb->c->stack - mrb->c->stbase; if (off > size) size = off; @@ -158,29 +183,29 @@ stack_extend_alloc(mrb_state *mrb, int room, int keep) size += room; #endif - mrb->c->stbase = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size); + newstack = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size); + if (newstack == NULL) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); + } + stack_clear(&(newstack[oldsize]), size - oldsize); + envadjust(mrb, oldbase, newstack, size); + mrb->c->stbase = newstack; mrb->c->stack = mrb->c->stbase + off; mrb->c->stend = mrb->c->stbase + size; - envadjust(mrb, oldbase, mrb->c->stbase); /* Raise an exception if the new stack size will be too large, to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */ if (size > MRB_STACK_MAX) { - init_new_stack_space(mrb, room, keep); mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } } static inline void -stack_extend(mrb_state *mrb, int room, int keep) +stack_extend(mrb_state *mrb, int room) { - if (room < keep) { - room = keep; - } if (mrb->c->stack + room >= mrb->c->stend) { - stack_extend_alloc(mrb, room, keep); + stack_extend_alloc(mrb, room); } - init_new_stack_space(mrb, room, keep); } static inline struct REnv* @@ -200,8 +225,8 @@ is_strict(mrb_state *mrb, struct REnv *e) { int cioff = e->cioff; - if (MRB_ENV_STACK_SHARED_P(e) && mrb->c->cibase[cioff].proc && - MRB_PROC_STRICT_P(mrb->c->cibase[cioff].proc)) { + if (MRB_ENV_STACK_SHARED_P(e) && e->cxt.c->cibase[cioff].proc && + MRB_PROC_STRICT_P(e->cxt.c->cibase[cioff].proc)) { return TRUE; } return FALSE; @@ -224,13 +249,13 @@ top_env(mrb_state *mrb, struct RProc *proc) #define CI_ACC_DIRECT -2 #define CI_ACC_RESUMED -3 -static mrb_callinfo* +static inline mrb_callinfo* cipush(mrb_state *mrb) { struct mrb_context *c = mrb->c; + static const mrb_callinfo ci_zero = { 0 }; mrb_callinfo *ci = c->ci; - int eidx = ci->eidx; int ridx = ci->ridx; if (ci + 1 == c->ciend) { @@ -241,13 +266,9 @@ cipush(mrb_state *mrb) c->ciend = c->cibase + size * 2; } ci = ++c->ci; - ci->eidx = eidx; + *ci = ci_zero; + ci->epos = mrb->c->eidx; ci->ridx = ridx; - ci->env = 0; - ci->pc = 0; - ci->err = 0; - ci->proc = 0; - ci->acc = 0; return ci; } @@ -255,28 +276,38 @@ cipush(mrb_state *mrb) MRB_API void mrb_env_unshare(mrb_state *mrb, struct REnv *e) { - size_t len = (size_t)MRB_ENV_STACK_LEN(e); - mrb_value *p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len); - - MRB_ENV_UNSHARE_STACK(e); - if (len > 0) { - stack_copy(p, e->stack, len); + if (e == NULL) return; + else { + size_t len = (size_t)MRB_ENV_STACK_LEN(e); + ptrdiff_t cioff = e->cioff; + mrb_value *p; + + if (!MRB_ENV_STACK_SHARED_P(e)) return; + if (e->cxt.c != mrb->c) return; + if (e->cioff == 0 && e->cxt.c == mrb->root_c) return; + MRB_ENV_UNSHARE_STACK(e); + if (!e->c) { + /* save block argument position (negated) */ + e->cioff = -e->cxt.c->cibase[cioff].argc-1; + } + e->cxt.mid = e->cxt.c->cibase[cioff].mid; + p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len); + if (len > 0) { + stack_copy(p, e->stack, len); + } + e->stack = p; + mrb_write_barrier(mrb, (struct RBasic *)e); } - e->stack = p; - mrb_write_barrier(mrb, (struct RBasic *)e); } -static void +static inline void cipop(mrb_state *mrb) { struct mrb_context *c = mrb->c; struct REnv *env = c->ci->env; c->ci--; - - if (env) { - mrb_env_unshare(mrb, env); - } + mrb_env_unshare(mrb, env); } void mrb_exc_set(mrb_state *mrb, mrb_value exc); @@ -285,21 +316,20 @@ static void ecall(mrb_state *mrb, int i) { struct RProc *p; - mrb_callinfo *ci; + mrb_callinfo *ci = mrb->c->ci; mrb_value *self = mrb->c->stack; struct RObject *exc; - int cioff; + ptrdiff_t cioff; + int ai = mrb_gc_arena_save(mrb); if (i<0) return; - if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { + if (ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } p = mrb->c->ensure[i]; if (!p) return; mrb->c->ensure[i] = NULL; - if (mrb->c->ci->eidx > i) - mrb->c->ci->eidx = i; - cioff = mrb->c->ci - mrb->c->cibase; + cioff = ci - mrb->c->cibase; ci = cipush(mrb); ci->stackent = mrb->c->stack; ci->mid = ci[-1].mid; @@ -310,9 +340,13 @@ ecall(mrb_state *mrb, int i) ci->target_class = p->target_class; mrb->c->stack = mrb->c->stack + ci[-1].nregs; exc = mrb->exc; mrb->exc = 0; + if (exc) { + mrb_gc_protect(mrb, mrb_obj_value(exc)); + } mrb_run(mrb, p, *self); mrb->c->ci = mrb->c->cibase + cioff; if (!mrb->exc) mrb->exc = exc; + mrb_gc_arena_restore(mrb, ai); } #ifndef MRB_FUNCALL_ARGC_MAX @@ -363,11 +397,11 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc val = mrb_obj_value(mrb->exc); } MRB_END_EXC(&c_jmp); + mrb->jmp = 0; } else { struct RProc *p; struct RClass *c; - mrb_sym undef = 0; mrb_callinfo *ci; int n; ptrdiff_t voff = -1; @@ -383,13 +417,15 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc p = mrb_method_search_vm(mrb, &c, mid); if (!p) { mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); + mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); p = mrb_method_search_vm(mrb, &c, missing); if (!p) { - mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); mrb_method_missing(mrb, mid, self, args); } - undef = mid; - argc++; + mrb_ary_unshift(mrb, args, mrb_symbol_value(mid)); + stack_extend(mrb, n+2); + mrb->c->stack[n+1] = args; + argc = -1; } if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); @@ -406,33 +442,25 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc } if (MRB_PROC_CFUNC_P(p)) { ci->nregs = argc + 2; - stack_extend(mrb, ci->nregs, 0); + stack_extend(mrb, ci->nregs); } else if (argc >= CALL_MAXARGS) { mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); - stack_extend(mrb, ci->nregs, 0); + stack_extend(mrb, ci->nregs); mrb->c->stack[1] = args; - if (undef) { - mrb_ary_unshift(mrb, mrb->c->stack[1], mrb_symbol_value(undef)); - } ci->argc = -1; argc = 1; } else { + if (argc < 0) argc = 1; ci->nregs = p->body.irep->nregs + argc; - stack_extend(mrb, ci->nregs, argc+2); + stack_extend(mrb, ci->nregs); } if (voff >= 0) { argv = mrb->c->stbase + voff; } mrb->c->stack[0] = self; - if (undef) { - mrb->c->stack[1] = mrb_symbol_value(undef); - if (argc > 1) { - stack_copy(mrb->c->stack+2, argv, argc-1); - } - } - else if (ci->argc > 0) { + if (ci->argc > 0) { stack_copy(mrb->c->stack+1, argv, argc); } mrb->c->stack[argc+1] = blk; @@ -466,16 +494,17 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) { mrb_callinfo *ci = mrb->c->ci; + mrb->c->stack[0] = self; ci->proc = p; ci->target_class = p->target_class; if (MRB_PROC_CFUNC_P(p)) { return p->body.func(mrb, self); } if (ci->argc < 0) { - stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs, 3); + stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs); } else { - stack_extend(mrb, p->body.irep->nregs, ci->argc+2); + stack_extend(mrb, p->body.irep->nregs); } ci->nregs = p->body.irep->nregs; @@ -561,7 +590,8 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) } ci = mrb->c->ci; if (ci->acc == CI_ACC_DIRECT) { - return mrb_yield_with_class(mrb, blk, 1, &self, self, c); + ci->target_class = c; + return mrb_yield_cont(mrb, blk, self, 1, &self); } ci->target_class = c; p = mrb_proc_ptr(blk); @@ -569,7 +599,7 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) ci->argc = 1; ci->mid = ci[-1].mid; if (MRB_PROC_CFUNC_P(p)) { - stack_extend(mrb, 3, 0); + stack_extend(mrb, 3); mrb->c->stack[0] = self; mrb->c->stack[1] = self; mrb->c->stack[2] = mrb_nil_value(); @@ -577,7 +607,7 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) } ci->nregs = p->body.irep->nregs; if (max < ci->nregs) max = ci->nregs; - stack_extend(mrb, max, 0); + stack_extend(mrb, max); mrb->c->stack[0] = self; mrb->c->stack[1] = self; mrb->c->stack[2] = mrb_nil_value(); @@ -683,11 +713,11 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value mrb->c->stack = mrb->c->stack + n; if (MRB_PROC_CFUNC_P(p)) { ci->nregs = argc + 2; - stack_extend(mrb, ci->nregs, 0); + stack_extend(mrb, ci->nregs); } else { ci->nregs = p->body.irep->nregs; - stack_extend(mrb, ci->nregs, argc+2); + stack_extend(mrb, ci->nregs); } mrb->c->stack[0] = self; @@ -725,6 +755,42 @@ mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg) return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class); } +mrb_value +mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv) +{ + struct RProc *p; + mrb_callinfo *ci; + + if (mrb_nil_p(b)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); + } + if (mrb_type(b) != MRB_TT_PROC) { + mrb_raise(mrb, E_TYPE_ERROR, "not a block"); + } + + p = mrb_proc_ptr(b); + ci = mrb->c->ci; + + stack_extend(mrb, 3); + mrb->c->stack[1] = mrb_ary_new_from_values(mrb, argc, argv); + mrb->c->stack[2] = mrb_nil_value(); + ci->argc = -1; + return mrb_exec_irep(mrb, self, p); +} + +static struct RBreak* +break_new(mrb_state *mrb, struct RProc *p, mrb_value val) +{ + struct RBreak *brk; + + brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL); + brk->iv = NULL; + brk->proc = p; + brk->val = val; + + return brk; +} + typedef enum { LOCALJUMP_ERROR_RETURN = 0, LOCALJUMP_ERROR_BREAK = 1, @@ -815,13 +881,30 @@ MRB_API mrb_value mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) { mrb_irep *irep = proc->body.irep; + mrb_value result; + struct mrb_context *c = mrb->c; + int cioff = c->ci - c->cibase; + unsigned int nregs = irep->nregs; - if (!mrb->c->stack) { + if (!c->stack) { stack_init(mrb); } - stack_extend(mrb, irep->nregs, stack_keep); - mrb->c->stack[0] = self; - return mrb_vm_exec(mrb, proc, irep->iseq); + if (stack_keep > nregs) + nregs = stack_keep; + stack_extend(mrb, nregs); + stack_clear(c->stack + stack_keep, nregs - stack_keep); + c->stack[0] = self; + result = mrb_vm_exec(mrb, proc, irep->iseq); + if (c->ci - c->cibase > cioff) { + c->ci = c->cibase + cioff; + } + if (mrb->c != c) { + if (mrb->c->fib) { + mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib); + } + mrb->c = c; + } + return result; } MRB_API mrb_value @@ -868,6 +951,8 @@ RETRY_TRY_BLOCK: if (exc_catched) { exc_catched = FALSE; + if (mrb->exc && mrb->exc->tt == MRB_TT_BREAK) + goto L_BREAK; goto L_RAISE; } mrb->jmp = &c_jmp; @@ -883,20 +968,24 @@ RETRY_TRY_BLOCK: CASE(OP_MOVE) { /* A B R(A) := R(B) */ - regs[GETARG_A(i)] = regs[GETARG_B(i)]; + int a = GETARG_A(i); + int b = GETARG_B(i); + regs[a] = regs[b]; NEXT; } CASE(OP_LOADL) { /* A Bx R(A) := Pool(Bx) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); #ifdef MRB_WORD_BOXING - mrb_value val = pool[GETARG_Bx(i)]; + mrb_value val = pool[bx]; if (mrb_float_p(val)) { val = mrb_float_value(mrb, mrb_float(val)); } - regs[GETARG_A(i)] = val; + regs[a] = val; #else - regs[GETARG_A(i)] = pool[GETARG_Bx(i)]; + regs[a] = pool[bx]; #endif NEXT; } @@ -909,92 +998,123 @@ RETRY_TRY_BLOCK: CASE(OP_LOADSYM) { /* A Bx R(A) := Syms(Bx) */ - SET_SYM_VALUE(regs[GETARG_A(i)], syms[GETARG_Bx(i)]); + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + SET_SYM_VALUE(regs[a], syms[bx]); NEXT; } CASE(OP_LOADSELF) { /* A R(A) := self */ - regs[GETARG_A(i)] = regs[0]; + int a = GETARG_A(i); + regs[a] = regs[0]; NEXT; } CASE(OP_LOADT) { /* A R(A) := true */ - SET_TRUE_VALUE(regs[GETARG_A(i)]); + int a = GETARG_A(i); + SET_TRUE_VALUE(regs[a]); NEXT; } CASE(OP_LOADF) { /* A R(A) := false */ - SET_FALSE_VALUE(regs[GETARG_A(i)]); + int a = GETARG_A(i); + SET_FALSE_VALUE(regs[a]); NEXT; } CASE(OP_GETGLOBAL) { /* A Bx R(A) := getglobal(Syms(Bx)) */ - regs[GETARG_A(i)] = mrb_gv_get(mrb, syms[GETARG_Bx(i)]); + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_value val = mrb_gv_get(mrb, syms[bx]); + regs[a] = val; NEXT; } CASE(OP_SETGLOBAL) { - /* setglobal(Syms(Bx), R(A)) */ - mrb_gv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); + /* A Bx setglobal(Syms(Bx), R(A)) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_gv_set(mrb, syms[bx], regs[a]); NEXT; } CASE(OP_GETSPECIAL) { /* A Bx R(A) := Special[Bx] */ - regs[GETARG_A(i)] = mrb_vm_special_get(mrb, GETARG_Bx(i)); + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_value val = mrb_vm_special_get(mrb, bx); + regs[a] = val; NEXT; } CASE(OP_SETSPECIAL) { /* A Bx Special[Bx] := R(A) */ - mrb_vm_special_set(mrb, GETARG_Bx(i), regs[GETARG_A(i)]); + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_vm_special_set(mrb, bx, regs[a]); NEXT; } CASE(OP_GETIV) { /* A Bx R(A) := ivget(Bx) */ - regs[GETARG_A(i)] = mrb_vm_iv_get(mrb, syms[GETARG_Bx(i)]); + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_value val = mrb_vm_iv_get(mrb, syms[bx]); + regs[a] = val; NEXT; } CASE(OP_SETIV) { - /* ivset(Syms(Bx),R(A)) */ - mrb_vm_iv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); + /* A Bx ivset(Syms(Bx),R(A)) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_vm_iv_set(mrb, syms[bx], regs[a]); NEXT; } CASE(OP_GETCV) { /* A Bx R(A) := cvget(Syms(Bx)) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_value val; ERR_PC_SET(mrb, pc); - regs[GETARG_A(i)] = mrb_vm_cv_get(mrb, syms[GETARG_Bx(i)]); + val = mrb_vm_cv_get(mrb, syms[bx]); ERR_PC_CLR(mrb); + regs[a] = val; NEXT; } CASE(OP_SETCV) { - /* cvset(Syms(Bx),R(A)) */ - mrb_vm_cv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); + /* A Bx cvset(Syms(Bx),R(A)) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_vm_cv_set(mrb, syms[bx], regs[a]); NEXT; } CASE(OP_GETCONST) { /* A Bx R(A) := constget(Syms(Bx)) */ mrb_value val; + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_sym sym = syms[bx]; ERR_PC_SET(mrb, pc); - val = mrb_vm_const_get(mrb, syms[GETARG_Bx(i)]); + val = mrb_vm_const_get(mrb, sym); ERR_PC_CLR(mrb); - regs[GETARG_A(i)] = val; + regs[a] = val; NEXT; } CASE(OP_SETCONST) { /* A Bx constset(Syms(Bx),R(A)) */ - mrb_vm_const_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_vm_const_set(mrb, syms[bx], regs[a]); NEXT; } @@ -1002,9 +1122,10 @@ RETRY_TRY_BLOCK: /* A Bx R(A) := R(A)::Syms(Bx) */ mrb_value val; int a = GETARG_A(i); + int bx = GETARG_Bx(i); ERR_PC_SET(mrb, pc); - val = mrb_const_get(mrb, regs[a], syms[GETARG_Bx(i)]); + val = mrb_const_get(mrb, regs[a], syms[bx]); ERR_PC_CLR(mrb); regs[a] = val; NEXT; @@ -1013,53 +1134,60 @@ RETRY_TRY_BLOCK: CASE(OP_SETMCNST) { /* A Bx R(A+1)::Syms(Bx) := R(A) */ int a = GETARG_A(i); - - mrb_const_set(mrb, regs[a+1], syms[GETARG_Bx(i)], regs[a]); + int bx = GETARG_Bx(i); + mrb_const_set(mrb, regs[a+1], syms[bx], regs[a]); NEXT; } CASE(OP_GETUPVAR) { /* A B C R(A) := uvget(B,C) */ - mrb_value *regs_a = regs + GETARG_A(i); - int up = GETARG_C(i); - - struct REnv *e = uvenv(mrb, up); + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + mrb_value *regs_a = regs + a; + struct REnv *e = uvenv(mrb, c); if (!e) { *regs_a = mrb_nil_value(); } else { - int idx = GETARG_B(i); - *regs_a = e->stack[idx]; + *regs_a = e->stack[b]; } NEXT; } CASE(OP_SETUPVAR) { /* A B C uvset(B,C,R(A)) */ - int up = GETARG_C(i); + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); - struct REnv *e = uvenv(mrb, up); + struct REnv *e = uvenv(mrb, c); if (e) { - mrb_value *regs_a = regs + GETARG_A(i); - int idx = GETARG_B(i); - e->stack[idx] = *regs_a; - mrb_write_barrier(mrb, (struct RBasic*)e); + mrb_value *regs_a = regs + a; + + if (b < MRB_ENV_STACK_LEN(e)) { + e->stack[b] = *regs_a; + mrb_write_barrier(mrb, (struct RBasic*)e); + } } NEXT; } CASE(OP_JMP) { /* sBx pc+=sBx */ - pc += GETARG_sBx(i); + int sbx = GETARG_sBx(i); + pc += sbx; JUMP; } CASE(OP_JMPIF) { /* A sBx if R(A) pc+=sBx */ - if (mrb_test(regs[GETARG_A(i)])) { - pc += GETARG_sBx(i); + int a = GETARG_A(i); + int sbx = GETARG_sBx(i); + if (mrb_test(regs[a])) { + pc += sbx; JUMP; } NEXT; @@ -1067,8 +1195,10 @@ RETRY_TRY_BLOCK: CASE(OP_JMPNOT) { /* A sBx if !R(A) pc+=sBx */ - if (!mrb_test(regs[GETARG_A(i)])) { - pc += GETARG_sBx(i); + int a = GETARG_A(i); + int sbx = GETARG_sBx(i); + if (!mrb_test(regs[a])) { + pc += sbx; JUMP; } NEXT; @@ -1076,12 +1206,13 @@ RETRY_TRY_BLOCK: CASE(OP_ONERR) { /* sBx pc+=sBx on exception */ + int sbx = GETARG_sBx(i); if (mrb->c->rsize <= mrb->c->ci->ridx) { - if (mrb->c->rsize == 0) mrb->c->rsize = 16; + if (mrb->c->rsize == 0) mrb->c->rsize = RESCUE_STACK_INIT_SIZE; else mrb->c->rsize *= 2; mrb->c->rescue = (mrb_code **)mrb_realloc(mrb, mrb->c->rescue, sizeof(mrb_code*) * mrb->c->rsize); } - mrb->c->rescue[mrb->c->ci->ridx++] = pc + GETARG_sBx(i); + mrb->c->rescue[mrb->c->ci->ridx++] = pc + sbx; NEXT; } @@ -1108,8 +1239,14 @@ RETRY_TRY_BLOCK: case MRB_TT_MODULE: break; default: - mrb_raise(mrb, E_TYPE_ERROR, "class or module required for rescue clause"); - break; + { + mrb_value exc; + + exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, + "class or module required for rescue clause"); + mrb_exc_set(mrb, exc); + goto L_RAISE; + } } ec = mrb_class_ptr(e); regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec)); @@ -1124,32 +1261,33 @@ RETRY_TRY_BLOCK: /* A A.times{rescue_pop()} */ int a = GETARG_A(i); - while (a--) { - mrb->c->ci->ridx--; - } + mrb->c->ci->ridx -= a; NEXT; } CASE(OP_RAISE) { /* A raise(R(A)) */ - mrb_exc_set(mrb, regs[GETARG_A(i)]); + int a = GETARG_A(i); + + mrb_exc_set(mrb, regs[a]); goto L_RAISE; } CASE(OP_EPUSH) { /* Bx ensure_push(SEQ[Bx]) */ + int bx = GETARG_Bx(i); struct RProc *p; - p = mrb_closure_new(mrb, irep->reps[GETARG_Bx(i)]); + p = mrb_closure_new(mrb, irep->reps[bx]); /* push ensure_stack */ - if (mrb->c->esize <= mrb->c->ci->eidx+1) { - if (mrb->c->esize == 0) mrb->c->esize = 16; + if (mrb->c->esize <= mrb->c->eidx+1) { + if (mrb->c->esize == 0) mrb->c->esize = ENSURE_STACK_INIT_SIZE; else mrb->c->esize *= 2; mrb->c->ensure = (struct RProc **)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*) * mrb->c->esize); } - mrb->c->ensure[mrb->c->ci->eidx++] = p; - mrb->c->ensure[mrb->c->ci->eidx] = NULL; - ARENA_RESTORE(mrb, ai); + mrb->c->ensure[mrb->c->eidx++] = p; + mrb->c->ensure[mrb->c->eidx] = NULL; + mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -1157,11 +1295,11 @@ RETRY_TRY_BLOCK: /* A A.times{ensure_pop().call} */ int a = GETARG_A(i); mrb_callinfo *ci = mrb->c->ci; - int n, eidx = ci->eidx; + int n, epos = ci->epos; - for (n=0; n<a && (ci == mrb->c->cibase || eidx > ci[-1].eidx); n++) { - ecall(mrb, --eidx); - ARENA_RESTORE(mrb, ai); + for (n=0; n<a && mrb->c->eidx > epos; n++) { + ecall(mrb, --mrb->c->eidx); + mrb_gc_arena_restore(mrb, ai); } NEXT; } @@ -1186,10 +1324,11 @@ RETRY_TRY_BLOCK: int n = GETARG_C(i); struct RProc *m; struct RClass *c; - mrb_callinfo *ci; + mrb_callinfo *ci = mrb->c->ci; mrb_value recv, result; mrb_sym mid = syms[GETARG_B(i)]; int bidx; + mrb_value blk; recv = regs[a]; if (n == CALL_MAXARGS) { @@ -1199,16 +1338,22 @@ RETRY_TRY_BLOCK: bidx = a+n+1; } if (GET_OPCODE(i) != OP_SENDB) { + if (bidx >= ci->nregs) { + stack_extend(mrb, bidx+1); + ci->nregs = bidx+1; + } SET_NIL_VALUE(regs[bidx]); + blk = mrb_nil_value(); } else { - mrb_value blk = regs[bidx]; + blk = regs[bidx]; if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { - if (bidx >= mrb->c->ci->nregs) { - stack_extend(mrb, bidx+1, mrb->c->ci->nregs); - mrb->c->ci->nregs = bidx+1; + if (bidx >= ci->nregs) { + stack_extend(mrb, bidx+1); + ci->nregs = bidx+1; } - regs[bidx] = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); + result = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); + blk = regs[bidx] = result; } } c = mrb_class(mrb, recv); @@ -1227,14 +1372,13 @@ RETRY_TRY_BLOCK: else { args = mrb_ary_new_from_values(mrb, n, regs+a+1); } + ERR_PC_SET(mrb, pc); mrb_method_missing(mrb, mid, recv, args); } mid = missing; if (n != CALL_MAXARGS) { - mrb_value blk = regs[bidx]; - - if (a+2 > irep->nregs) { - stack_extend(mrb, a+2, a+n+1); + if (a+2 >= irep->nregs) { + stack_extend(mrb, a+3); } regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1); regs[a+2] = blk; @@ -1267,9 +1411,18 @@ RETRY_TRY_BLOCK: } result = m->body.func(mrb, recv); mrb_gc_arena_restore(mrb, ai); + mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; - /* pop stackpos */ ci = mrb->c->ci; + if (GET_OPCODE(i) == OP_SENDB) { + if (mrb_type(blk) == MRB_TT_PROC) { + struct RProc *p = mrb_proc_ptr(blk); + + if (p && !MRB_PROC_STRICT_P(p) && p->env == ci[-1].env) { + p->flags |= MRB_PROC_ORPHAN; + } + } + } if (!ci->target_class) { /* return from context modifying method (resume/yield) */ if (ci->acc == CI_ACC_RESUMED) { mrb->jmp = prev_jmp; @@ -1284,6 +1437,7 @@ RETRY_TRY_BLOCK: } } mrb->c->stack[0] = result; + /* pop stackpos */ mrb->c->stack = ci->stackent; pc = ci->pc; cipop(mrb); @@ -1298,11 +1452,11 @@ RETRY_TRY_BLOCK: ci->nregs = irep->nregs; if (n == CALL_MAXARGS) { ci->argc = -1; - stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); + stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs); } else { ci->argc = n; - stack_extend(mrb, irep->nregs, n+2); + stack_extend(mrb, irep->nregs); } pc = irep->iseq; JUMP; @@ -1311,6 +1465,7 @@ RETRY_TRY_BLOCK: CASE(OP_FSEND) { /* A B C R(A) := fcall(R(A),Syms(B),R(A+1),... ,R(A+C-1)) */ + /* not implemented yet */ NEXT; } @@ -1325,9 +1480,15 @@ RETRY_TRY_BLOCK: ci->target_class = m->target_class; ci->proc = m; if (m->env) { - if (m->env->mid) { - ci->mid = m->env->mid; + mrb_sym mid; + + if (MRB_ENV_STACK_SHARED_P(m->env)) { + mid = m->env->cxt.c->cibase[m->env->cioff].mid; + } + else { + mid = m->env->cxt.mid; } + if (mid) ci->mid = mid; if (!m->env->stack) { m->env->stack = mrb->c->stack; } @@ -1337,6 +1498,7 @@ RETRY_TRY_BLOCK: if (MRB_PROC_CFUNC_P(m)) { recv = m->body.func(mrb, recv); mrb_gc_arena_restore(mrb, ai); + mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; /* pop stackpos */ ci = mrb->c->ci; @@ -1360,11 +1522,14 @@ RETRY_TRY_BLOCK: pool = irep->pool; syms = irep->syms; ci->nregs = irep->nregs; + stack_extend(mrb, irep->nregs); if (ci->argc < 0) { - stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); + if (irep->nregs > 3) { + stack_clear(regs+3, irep->nregs-3); + } } - else { - stack_extend(mrb, irep->nregs, ci->argc+2); + else if (ci->argc+2 < irep->nregs) { + stack_clear(regs+ci->argc+2, irep->nregs-ci->argc-2); } if (m->env) { regs[0] = m->env->stack[0]; @@ -1383,6 +1548,8 @@ RETRY_TRY_BLOCK: mrb_sym mid = ci->mid; int a = GETARG_A(i); int n = GETARG_C(i); + mrb_value blk; + int bidx; if (mid == 0 || !ci->target_class) { mrb_value exc; @@ -1406,6 +1573,7 @@ RETRY_TRY_BLOCK: else { args = mrb_ary_new_from_values(mrb, n, regs+a+1); } + ERR_PC_SET(mrb, pc); mrb_method_missing(mrb, mid, recv, args); } mid = missing; @@ -1422,6 +1590,24 @@ RETRY_TRY_BLOCK: } } + if (n == CALL_MAXARGS) { + bidx = a+2; + } + else { + bidx = a+n+1; + } + blk = regs[bidx]; + if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { + mrb_value result; + + if (bidx >= ci->nregs) { + stack_extend(mrb, bidx+1); + ci->nregs = bidx+1; + } + result = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); + regs[bidx] = result; + } + /* push callinfo */ ci = cipush(mrb); ci->mid = mid; @@ -1429,27 +1615,11 @@ RETRY_TRY_BLOCK: ci->stackent = mrb->c->stack; ci->target_class = c; ci->pc = pc + 1; - { - int bidx; - mrb_value blk; - - if (n == CALL_MAXARGS) { - ci->argc = -1; - bidx = a+2; - } - else { - ci->argc = n; - bidx = a+n+1; - } - blk = regs[bidx]; - if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { - if (bidx >= ci->nregs) { - stack_extend(mrb, bidx+1, ci->nregs); - ci->nregs = bidx+1; - } - regs[bidx] = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); - ci = mrb->c->ci; - } + if (n == CALL_MAXARGS) { + ci->argc = -1; + } + else { + ci->argc = n; } /* prepare stack */ @@ -1466,13 +1636,28 @@ RETRY_TRY_BLOCK: ci->nregs = n + 2; } v = m->body.func(mrb, recv); - mrb->c->stack[0] = v; mrb_gc_arena_restore(mrb, ai); if (mrb->exc) goto L_RAISE; + ci = mrb->c->ci; + if (!ci->target_class) { /* return from context modifying method (resume/yield) */ + if (ci->acc == CI_ACC_RESUMED) { + mrb->jmp = prev_jmp; + return v; + } + else { + mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc)); + proc = ci[-1].proc; + irep = proc->body.irep; + pool = irep->pool; + syms = irep->syms; + } + } + mrb->c->stack[0] = v; /* pop stackpos */ - mrb->c->stack = mrb->c->ci->stackent; + mrb->c->stack = ci->stackent; + pc = ci->pc; cipop(mrb); - NEXT; + JUMP; } else { /* fill callinfo */ @@ -1485,10 +1670,10 @@ RETRY_TRY_BLOCK: syms = irep->syms; ci->nregs = irep->nregs; if (n == CALL_MAXARGS) { - stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); + stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs); } else { - stack_extend(mrb, irep->nregs, ci->argc+2); + stack_extend(mrb, irep->nregs); } pc = irep->iseq; JUMP; @@ -1505,16 +1690,20 @@ RETRY_TRY_BLOCK: int lv = (bx>>0)&0xf; mrb_value *stack; + if (mrb->c->ci->mid == 0 || mrb->c->ci->target_class == NULL) { + mrb_value exc; + + L_NOSUPER: + exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); + mrb_exc_set(mrb, exc); + goto L_RAISE; + } if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); - if (!e) { - mrb_value exc; - - exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); - mrb_exc_set(mrb, exc); - goto L_RAISE; - } + if (!e) goto L_NOSUPER; + if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+1) + goto L_NOSUPER; stack = e->stack + 1; } if (r == 0) { @@ -1528,24 +1717,24 @@ RETRY_TRY_BLOCK: if (mrb_array_p(stack[m1])) { struct RArray *ary = mrb_ary_ptr(stack[m1]); - pp = ary->ptr; - len = ary->len; + pp = ARY_PTR(ary); + len = ARY_LEN(ary); } regs[a] = mrb_ary_new_capa(mrb, m1+len+m2); rest = mrb_ary_ptr(regs[a]); if (m1 > 0) { - stack_copy(rest->ptr, stack, m1); + stack_copy(ARY_PTR(rest), stack, m1); } if (len > 0) { - stack_copy(rest->ptr+m1, pp, len); + stack_copy(ARY_PTR(rest)+m1, pp, len); } if (m2 > 0) { - stack_copy(rest->ptr+m1+len, stack+m1+1, m2); + stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2); } - rest->len = m1+len+m2; + ARY_SET_LEN(rest, m1+len+m2); } regs[a+1] = stack[m1+r+m2]; - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -1570,8 +1759,8 @@ RETRY_TRY_BLOCK: if (argc < 0) { struct RArray *ary = mrb_ary_ptr(regs[1]); - argv = ary->ptr; - argc = ary->len; + argv = ARY_PTR(ary); + argc = ARY_LEN(ary); mrb_gc_protect(mrb, regs[1]); } if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) { @@ -1584,10 +1773,9 @@ RETRY_TRY_BLOCK: } else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) { mrb_gc_protect(mrb, argv[0]); - argc = mrb_ary_ptr(argv[0])->len; - argv = mrb_ary_ptr(argv[0])->ptr; + argc = RARRAY_LEN(argv[0]); + argv = RARRAY_PTR(argv[0]); } - mrb->c->ci->argc = len; if (argc < len) { int mlen = m2; if (argc < m1+m2) { @@ -1601,9 +1789,15 @@ RETRY_TRY_BLOCK: if (argv0 != argv) { value_move(®s[1], argv, argc-mlen); /* m1 + o */ } + if (argc < m1) { + stack_clear(®s[argc+1], m1-argc); + } if (mlen) { value_move(®s[len-m2+1], &argv[argc-mlen], mlen); } + if (mlen < m2) { + stack_clear(®s[len-m2+mlen+1], m2-mlen); + } if (r) { regs[m1+o+1] = mrb_ary_new_capa(mrb, 0); } @@ -1631,6 +1825,11 @@ RETRY_TRY_BLOCK: } pc += o + 1; } + mrb->c->ci->argc = len; + /* clear local (but non-argument) variables */ + if (irep->nlocals-len-2 > 0) { + stack_clear(®s[len+2], irep->nlocals-len-2); + } JUMP; } @@ -1651,18 +1850,36 @@ RETRY_TRY_BLOCK: /* fall through */ CASE(OP_RETURN) { /* A B return R(A) (B=normal,in-block return/break) */ + mrb_callinfo *ci; + + ci = mrb->c->ci; + if (ci->mid) { + mrb_value blk; + + if (ci->argc < 0) { + blk = regs[2]; + } + else { + blk = regs[ci->argc+1]; + } + if (mrb_type(blk) == MRB_TT_PROC) { + struct RProc *p = mrb_proc_ptr(blk); + + if (!MRB_PROC_STRICT_P(proc) && + ci > mrb->c->cibase && p->env == ci[-1].env) { + p->flags |= MRB_PROC_ORPHAN; + } + } + } + if (mrb->exc) { - mrb_callinfo *ci, *ci0; + mrb_callinfo *ci0; mrb_value *stk; - int eidx; L_RAISE: ci0 = ci = mrb->c->ci; - mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, pc)); - mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value(ci - mrb->c->cibase)); - eidx = ci->eidx; if (ci == mrb->c->cibase) { - if (ci->ridx == 0) goto L_STOP; + if (ci->ridx == 0) goto L_FTOP; goto L_RESCUE; } stk = mrb->c->stack; @@ -1677,6 +1894,7 @@ RETRY_TRY_BLOCK: if (ci == mrb->c->cibase) { mrb->c->stack = stk; if (ci->ridx == 0) { + L_FTOP: /* fiber top */ if (mrb->c == mrb->root_c) { mrb->c->stack = mrb->c->stbase; goto L_STOP; @@ -1684,6 +1902,9 @@ RETRY_TRY_BLOCK: else { struct mrb_context *c = mrb->c; + if (c->fib) { + mrb_write_barrier(mrb, (struct RBasic*)c->fib); + } mrb->c = c->prev; c->prev = NULL; goto L_RAISE; @@ -1693,8 +1914,13 @@ RETRY_TRY_BLOCK: } /* call ensure only when we skip this callinfo */ if (ci[0].ridx == ci[-1].ridx) { - while (eidx > ci[-1].eidx) { - ecall(mrb, --eidx); + mrb_value *org_stbase = mrb->c->stbase; + while (mrb->c->eidx > ci->epos) { + ecall(mrb, --mrb->c->eidx); + ci = mrb->c->ci; + if (org_stbase != mrb->c->stbase) { + stk = mrb->c->stack; + } } } } @@ -1707,13 +1933,15 @@ RETRY_TRY_BLOCK: if (ci != ci0) { mrb->c->stack = ci[1].stackent; } + stack_extend(mrb, irep->nregs); pc = mrb->c->rescue[--ci->ridx]; } else { - mrb_callinfo *ci = mrb->c->ci; - int acc, eidx = mrb->c->ci->eidx; - mrb_value v = regs[GETARG_A(i)]; + int acc; + mrb_value v; + v = regs[GETARG_A(i)]; + mrb_gc_protect(mrb, v); switch (GETARG_B(i)) { case OP_R_RETURN: /* Fall through to OP_R_NORMAL otherwise */ @@ -1721,21 +1949,21 @@ RETRY_TRY_BLOCK: struct REnv *e = top_env(mrb, proc); mrb_callinfo *ce; - if (!MRB_ENV_STACK_SHARED_P(e)) { + if (!MRB_ENV_STACK_SHARED_P(e) || e->cxt.c != mrb->c) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } ce = mrb->c->cibase + e->cioff; - while (--ci > ce) { - if (ci->env) { - mrb_env_unshare(mrb, ci->env); - } + while (ci > ce) { + mrb_env_unshare(mrb, ci->env); if (ci->acc < 0) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } + ci--; } + mrb_env_unshare(mrb, ci->env); if (ce == mrb->c->cibase) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; @@ -1745,6 +1973,7 @@ RETRY_TRY_BLOCK: break; } case OP_R_NORMAL: + NORMAL_RETURN: if (ci == mrb->c->cibase) { if (!mrb->c->prev) { /* toplevel return */ localjump_error(mrb, LOCALJUMP_ERROR_RETURN); @@ -1755,6 +1984,9 @@ RETRY_TRY_BLOCK: mrb_exc_set(mrb, exc); goto L_RAISE; } + while (mrb->c->eidx > 0) { + ecall(mrb, --mrb->c->eidx); + } /* automatic yield at the end */ mrb->c->status = MRB_FIBER_TERMINATED; mrb->c = mrb->c->prev; @@ -1763,39 +1995,68 @@ RETRY_TRY_BLOCK: ci = mrb->c->ci; break; case OP_R_BREAK: - if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) { - localjump_error(mrb, LOCALJUMP_ERROR_BREAK); + if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN; + if (MRB_PROC_ORPHAN_P(proc)) { + mrb_value exc; + + L_BREAK_ERROR: + exc = mrb_exc_new_str_lit(mrb, E_LOCALJUMP_ERROR, + "break from proc-closure"); + mrb_exc_set(mrb, exc); goto L_RAISE; } + if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) { + goto L_BREAK_ERROR; + } + if (proc->env->cxt.c != mrb->c) { + goto L_BREAK_ERROR; + } + while (mrb->c->eidx > mrb->c->ci->epos) { + ecall(mrb, --mrb->c->eidx); + } /* break from fiber block */ if (mrb->c->ci == mrb->c->cibase && mrb->c->ci->pc) { struct mrb_context *c = mrb->c; mrb->c = c->prev; c->prev = NULL; + ci = mrb->c->ci; + } + if (ci->acc < 0) { + mrb_gc_arena_restore(mrb, ai); + mrb->c->vmexec = FALSE; + mrb->exc = (struct RObject*)break_new(mrb, proc, v); + mrb->jmp = prev_jmp; + MRB_THROW(prev_jmp); + } + if (FALSE) { + L_BREAK: + v = ((struct RBreak*)mrb->exc)->val; + proc = ((struct RBreak*)mrb->exc)->proc; + mrb->exc = NULL; + ci = mrb->c->ci; } - ci = mrb->c->ci; mrb->c->stack = ci->stackent; mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1; while (ci > mrb->c->ci) { + mrb_env_unshare(mrb, ci->env); if (ci[-1].acc == CI_ACC_SKIP) { mrb->c->ci = ci; - break; - } - if (ci->env) { - mrb_env_unshare(mrb, ci->env); + goto L_BREAK_ERROR; } ci--; } + mrb_env_unshare(mrb, ci->env); break; default: /* cannot happen */ break; } - while (eidx > mrb->c->ci[-1].eidx) { - ecall(mrb, --eidx); + while (mrb->c->eidx > mrb->c->ci->epos) { + ecall(mrb, --mrb->c->eidx); } if (mrb->c->vmexec && !mrb->c->ci->target_class) { + mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; mrb->jmp = prev_jmp; return v; @@ -1805,17 +2066,19 @@ RETRY_TRY_BLOCK: mrb->c->stack = ci->stackent; cipop(mrb); if (acc == CI_ACC_SKIP || acc == CI_ACC_DIRECT) { + mrb_gc_arena_restore(mrb, ai); mrb->jmp = prev_jmp; return v; } pc = ci->pc; - DEBUG(printf("from :%s\n", mrb_sym2name(mrb, ci->mid))); + DEBUG(fprintf(stderr, "from :%s\n", mrb_sym2name(mrb, ci->mid))); proc = mrb->c->ci->proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; regs[acc] = v; + mrb_gc_arena_restore(mrb, ai); } JUMP; } @@ -1846,6 +2109,7 @@ RETRY_TRY_BLOCK: else { args = mrb_ary_new_from_values(mrb, n, regs+a+1); } + ERR_PC_SET(mrb, pc); mrb_method_missing(mrb, mid, recv, args); } mid = missing; @@ -1884,10 +2148,10 @@ RETRY_TRY_BLOCK: pool = irep->pool; syms = irep->syms; if (ci->argc < 0) { - stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); + stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs); } else { - stack_extend(mrb, irep->nregs, ci->argc+2); + stack_extend(mrb, irep->nregs); } pc = irep->iseq; } @@ -1907,7 +2171,9 @@ RETRY_TRY_BLOCK: if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); - if (!e || e->mid == 0) { + if (!e || e->cioff == 0 || + (!MRB_ENV_STACK_SHARED_P(e) && e->cxt.mid == 0) || + MRB_ENV_STACK_LEN(e) <= m1+r+m2+1) { localjump_error(mrb, LOCALJUMP_ERROR_YIELD); goto L_RAISE; } @@ -1981,7 +2247,7 @@ RETRY_TRY_BLOCK: default: goto L_SEND; } - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -2103,7 +2369,14 @@ RETRY_TRY_BLOCK: { mrb_int x = mrb_fixnum(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / (mrb_float)y); + double f; + if (y == 0 && x != 0) { + f = INFINITY; + } + else { + f = (mrb_float)x / (mrb_float)y; + } + SET_FLOAT_VALUE(mrb, regs[a], f); } break; case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): @@ -2118,7 +2391,14 @@ RETRY_TRY_BLOCK: { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], x / y); + double f; + if (y == 0) { + f = INFINITY; + } + else { + f = x / y; + } + SET_FLOAT_VALUE(mrb, regs[a], f); } #else OP_MATH_BODY(/,mrb_float,mrb_fixnum); @@ -2140,7 +2420,8 @@ RETRY_TRY_BLOCK: } #ifdef MRB_NAN_BOXING if (isnan(mrb_float(regs[a]))) { - regs[a] = mrb_float_value(mrb, mrb_float(regs[a])); + mrb_value v = mrb_float_value(mrb, mrb_float(regs[a])); + regs[a] = v; } #endif NEXT; @@ -2293,48 +2574,61 @@ RETRY_TRY_BLOCK: CASE(OP_ARRAY) { /* A B C R(A) := ary_new(R(B),R(B+1)..R(B+C)) */ - regs[GETARG_A(i)] = mrb_ary_new_from_values(mrb, GETARG_C(i), ®s[GETARG_B(i)]); - ARENA_RESTORE(mrb, ai); + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + mrb_value v = mrb_ary_new_from_values(mrb, c, ®s[b]); + regs[a] = v; + mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_ARYCAT) { /* A B mrb_ary_concat(R(A),R(B)) */ - mrb_value splat = mrb_ary_splat(mrb, regs[GETARG_B(i)]); - mrb_ary_concat(mrb, regs[GETARG_A(i)], splat); - ARENA_RESTORE(mrb, ai); + int a = GETARG_A(i); + int b = GETARG_B(i); + mrb_value splat = mrb_ary_splat(mrb, regs[b]); + mrb_ary_concat(mrb, regs[a], splat); + mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_ARYPUSH) { /* A B R(A).push(R(B)) */ - mrb_ary_push(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]); + int a = GETARG_A(i); + int b = GETARG_B(i); + mrb_ary_push(mrb, regs[a], regs[b]); NEXT; } CASE(OP_AREF) { /* A B C R(A) := R(B)[C] */ int a = GETARG_A(i); + int b = GETARG_B(i); int c = GETARG_C(i); - mrb_value v = regs[GETARG_B(i)]; + mrb_value v = regs[b]; if (!mrb_array_p(v)) { if (c == 0) { - regs[GETARG_A(i)] = v; + regs[a] = v; } else { SET_NIL_VALUE(regs[a]); } } else { - regs[GETARG_A(i)] = mrb_ary_ref(mrb, v, c); + v = mrb_ary_ref(mrb, v, c); + regs[a] = v; } NEXT; } CASE(OP_ASET) { /* A B C R(B)[C] := R(A) */ - mrb_ary_set(mrb, regs[GETARG_B(i)], GETARG_C(i), regs[GETARG_A(i)]); + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + mrb_ary_set(mrb, regs[b], c, regs[a]); NEXT; } @@ -2344,7 +2638,6 @@ RETRY_TRY_BLOCK: mrb_value v = regs[a]; int pre = GETARG_B(i); int post = GETARG_C(i); - struct RArray *ary; int len, idx; @@ -2352,31 +2645,34 @@ RETRY_TRY_BLOCK: v = mrb_ary_new_from_values(mrb, 1, ®s[a]); } ary = mrb_ary_ptr(v); - len = ary->len; + len = ARY_LEN(ary); if (len > pre + post) { - regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre); + v = mrb_ary_new_from_values(mrb, len - pre - post, ARY_PTR(ary)+pre); + regs[a++] = v; while (post--) { - regs[a++] = ary->ptr[len-post-1]; + regs[a++] = ARY_PTR(ary)[len-post-1]; } } else { - regs[a++] = mrb_ary_new_capa(mrb, 0); + v = mrb_ary_new_capa(mrb, 0); + regs[a++] = v; for (idx=0; idx+pre<len; idx++) { - regs[a+idx] = ary->ptr[pre+idx]; + regs[a+idx] = ARY_PTR(ary)[pre+idx]; } while (idx < post) { SET_NIL_VALUE(regs[a+idx]); idx++; } } - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_STRING) { /* A Bx R(A) := str_new(Lit(Bx)) */ - regs[GETARG_A(i)] = mrb_str_dup(mrb, pool[GETARG_Bx(i)]); - ARENA_RESTORE(mrb, ai); + mrb_value str = mrb_str_dup(mrb, pool[GETARG_Bx(i)]); + regs[GETARG_A(i)] = str; + mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -2398,7 +2694,7 @@ RETRY_TRY_BLOCK: b+=2; } regs[GETARG_A(i)] = hash; - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -2415,7 +2711,7 @@ RETRY_TRY_BLOCK: } if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT; regs[GETARG_A(i)] = mrb_obj_value(p); - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -2442,7 +2738,7 @@ RETRY_TRY_BLOCK: } c = mrb_vm_define_class(mrb, base, super, id); regs[a] = mrb_obj_value(c); - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -2462,7 +2758,7 @@ RETRY_TRY_BLOCK: } c = mrb_vm_define_module(mrb, base, id); regs[a] = mrb_obj_value(c); - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -2475,6 +2771,7 @@ RETRY_TRY_BLOCK: /* prepare closure */ p = mrb_closure_new(mrb, irep->reps[GETARG_Bx(i)]); + p->c = NULL; /* prepare stack */ ci = cipush(mrb); @@ -2492,25 +2789,14 @@ RETRY_TRY_BLOCK: p->target_class = ci->target_class; ci->proc = p; - if (MRB_PROC_CFUNC_P(p)) { - ci->nregs = 0; - mrb->c->stack[0] = p->body.func(mrb, recv); - mrb_gc_arena_restore(mrb, ai); - if (mrb->exc) goto L_RAISE; - /* pop stackpos */ - mrb->c->stack = mrb->c->ci->stackent; - cipop(mrb); - NEXT; - } - else { - irep = p->body.irep; - pool = irep->pool; - syms = irep->syms; - stack_extend(mrb, irep->nregs, 1); - ci->nregs = irep->nregs; - pc = irep->iseq; - JUMP; - } + irep = p->body.irep; + pool = irep->pool; + syms = irep->syms; + stack_extend(mrb, irep->nregs); + stack_clear(regs+1, irep->nregs-1); + ci->nregs = irep->nregs; + pc = irep->iseq; + JUMP; } CASE(OP_METHOD) { @@ -2520,14 +2806,14 @@ RETRY_TRY_BLOCK: struct RProc *p = mrb_proc_ptr(regs[a+1]); mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], p); - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } CASE(OP_SCLASS) { /* A B R(A) := R(B).singleton_class */ regs[GETARG_A(i)] = mrb_singleton_class(mrb, regs[GETARG_B(i)]); - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -2547,7 +2833,7 @@ RETRY_TRY_BLOCK: int b = GETARG_B(i); mrb_value val = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i)); regs[GETARG_A(i)] = val; - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -2569,10 +2855,10 @@ RETRY_TRY_BLOCK: /* stop VM */ L_STOP: { - int eidx_stop = mrb->c->ci == mrb->c->cibase ? 0 : mrb->c->ci[-1].eidx; - int eidx = mrb->c->ci->eidx; - while (eidx > eidx_stop) { - ecall(mrb, --eidx); + int epos = mrb->c->ci->epos; + + while (mrb->c->eidx > epos) { + ecall(mrb, --mrb->c->eidx); } } ERR_PC_CLR(mrb); @@ -2626,7 +2912,11 @@ mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int sta mrb_callinfo *ci; mrb_value v; - if (!mrb->c->cibase || mrb->c->ci == mrb->c->cibase) { + if (!mrb->c->cibase) { + return mrb_vm_run(mrb, proc, self, stack_keep); + } + if (mrb->c->ci == mrb->c->cibase) { + mrb->c->ci->env = NULL; return mrb_vm_run(mrb, proc, self, stack_keep); } ci = cipush(mrb); diff --git a/tasks/libmruby.rake b/tasks/libmruby.rake index 23663d0a5..540aa3eb5 100644 --- a/tasks/libmruby.rake +++ b/tasks/libmruby.rake @@ -16,6 +16,8 @@ MRuby.each_target do gem_libraries = gems.map { |g| g.linker.libraries } f.puts "MRUBY_LIBS = #{linker.option_library % 'mruby'} #{linker.library_flags(gem_libraries)}" + + f.puts "MRUBY_LIBMRUBY_PATH = #{libfile("#{build_dir}/lib/libmruby")}" end end task :all => "#{build_dir}/lib/libmruby.flags.mak" diff --git a/tasks/toolchains/android.rake b/tasks/toolchains/android.rake index e69dbf5a6..c59da7fcb 100644 --- a/tasks/toolchains/android.rake +++ b/tasks/toolchains/android.rake @@ -263,7 +263,7 @@ Set ANDROID_PLATFORM environment variable or set :platform parameter case toolchain when :gcc when :clang - flags += %W(-gcc-toolchain "#{gcc_toolchain_path.to_s}" -Wno-invalid-command-line-argument -Wno-unused-command-line-argument) + flags += %W(-gcc-toolchain "#{gcc_toolchain_path}" -Wno-invalid-command-line-argument -Wno-unused-command-line-argument) end flags += %W(-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes) diff --git a/test/assert.rb b/test/assert.rb index 7efc24e19..8ab9db29e 100644 --- a/test/assert.rb +++ b/test/assert.rb @@ -23,8 +23,8 @@ def assertion_string(err, str, iso=nil, e=nil, bt=nil) msg += " => #{e.message}" if e msg += " (mrbgems: #{GEMNAME})" if Object.const_defined?(:GEMNAME) if $mrbtest_assert && $mrbtest_assert.size > 0 - $mrbtest_assert.each do |idx, str, diff| - msg += "\n - Assertion[#{idx}] Failed: #{str}\n#{diff}" + $mrbtest_assert.each do |idx, assert_msg, diff| + msg += "\n - Assertion[#{idx}] Failed: #{assert_msg}\n#{diff}" end end msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt @@ -62,7 +62,7 @@ def assert(str = 'Assertion failed', iso = '') $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt)) $kill_test += 1 t_print('X') - end + end ensure $mrbtest_assert = nil end @@ -157,7 +157,7 @@ def assert_raise(*exp) msg = "#{msg}#{exp.inspect} exception expected, not" diff = " Class: <#{e.class}>\n" + " Message: #{e.message}" - if not exp.any?{|ex| ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class } + unless exp.any?{|ex| ex.instance_of?(Module) ? e.kind_of?(ex) : ex == e.class } $mrbtest_assert.push([$mrbtest_assert_idx, msg, diff]) ret = false end diff --git a/test/bintest.rb b/test/bintest.rb index 8f56cc0ad..12971a9d9 100644 --- a/test/bintest.rb +++ b/test/bintest.rb @@ -3,7 +3,7 @@ require 'test/assert.rb' def cmd(s) case RbConfig::CONFIG['host_os'] - when /mswin(?!ce)|mingw|cygwin|bccwin/ + when /mswin(?!ce)|mingw|bccwin/ "bin\\#{s}.exe" else "bin/#{s}" @@ -12,7 +12,7 @@ end def shellquote(s) case RbConfig::CONFIG['host_os'] - when /mswin(?!ce)|mingw|cygwin|bccwin/ + when /mswin(?!ce)|mingw|bccwin/ "\"#{s}\"" else "'#{s}'" @@ -21,7 +21,7 @@ end ARGV.each do |gem| case RbConfig::CONFIG['host_os'] - when /mswin(?!ce)|mingw|cygwin|bccwin/ + when /mswin(?!ce)|mingw|bccwin/ gem = gem.gsub('\\', '/') end diff --git a/test/t/array.rb b/test/t/array.rb index 4efc5166a..7c11265a4 100644 --- a/test/t/array.rb +++ b/test/t/array.rb @@ -237,6 +237,8 @@ assert('Array#pop', '15.2.12.5.21') do assert_nil([].pop) assert_equal([1,2], a) assert_equal(3, b) + + assert_raise(RuntimeError) { [].freeze.pop } end assert('Array#push', '15.2.12.5.22') do @@ -284,6 +286,8 @@ assert('Array#shift', '15.2.12.5.27') do assert_nil([].shift) assert_equal([2,3], a) assert_equal(1, b) + + assert_raise(RuntimeError) { [].freeze.shift } end assert('Array#size', '15.2.12.5.28') do diff --git a/test/t/class.rb b/test/t/class.rb index 54fadc0ca..eb077fce6 100644 --- a/test/t/class.rb +++ b/test/t/class.rb @@ -410,6 +410,20 @@ assert('class variable in module and class << self style class method') do assert_equal("value", ClassVariableInModuleTest.class_variable) end +assert('child class/module defined in singleton class get parent constant') do + actual = module GetParentConstantTest + EXPECT = "value" + class << self + class CHILD + class << self + EXPECT + end + end + end + end + assert_equal("value", actual) +end + assert('overriding class variable with a module (#3235)') do module ModuleWithCVar @@class_variable = 1 diff --git a/test/t/codegen.rb b/test/t/codegen.rb index 709902741..29f5ad525 100644 --- a/test/t/codegen.rb +++ b/test/t/codegen.rb @@ -14,24 +14,6 @@ assert('empty condition in ternary expression parses correctly') do assert_equal(() ? 1 : 2, 2) end -assert('codegen absorbs arguments to redo and retry if they are the argument of a call') do - assert_nothing_raised do - a=*"1", case nil - when 1 - redo | - 1 - end - end - - assert_nothing_raised do - a=*"1", case nil - when 1 - retry | - 1 - end - end -end - assert('method call with exactly 127 arguments') do def args_to_ary(*args) args diff --git a/test/t/exception.rb b/test/t/exception.rb index 9c8b36e44..ce7b5841e 100644 --- a/test/t/exception.rb +++ b/test/t/exception.rb @@ -398,7 +398,7 @@ assert('GC in rescue') do end rescue => exception GC.start - assert_equal("#{__FILE__}:#{line}:in Object.call", + assert_equal("#{__FILE__}:#{line}:in call", exception.backtrace.first) end end @@ -416,7 +416,7 @@ assert('Method call in rescue') do rescue => exception [3].each do end - assert_equal("#{__FILE__}:#{line}:in Object.call", + assert_equal("#{__FILE__}:#{line}:in call", exception.backtrace.first) end end diff --git a/test/t/iterations.rb b/test/t/iterations.rb new file mode 100644 index 000000000..f227a6037 --- /dev/null +++ b/test/t/iterations.rb @@ -0,0 +1,61 @@ +assert('while expression', '11.5.2.3.2') do + idx = 10 + all = [] + res = while idx > 0 + all << idx + idx -= 1 + end + + assert_equal nil, res + assert_equal [10,9,8,7,6,5,4,3,2,1], all +end + +assert('until expression', '11.5.2.3.3') do + idx = 10 + all = [] + res = until idx == 0 + all << idx + idx -= 1 + end + + assert_equal nil, res + assert_equal [10,9,8,7,6,5,4,3,2,1], all +end + +assert('break expression', '11.5.2.4.3') do + assert_equal :result do + while true + break :result + end + end + + assert_equal :result do + until false + break :result + end + end +end + +assert('next expression', '11.5.2.4.4') do + assert_equal [8,6,4,2,0] do + all = [] + idx = 10 + while idx > 0 + idx -= 1 + next if (idx % 2) == 1 + all << idx + end + all + end + + assert_equal [8,6,4,2,0] do + all = [] + idx = 10 + until idx == 0 + idx -= 1 + next if (idx % 2) == 1 + all << idx + end + all + end +end
\ No newline at end of file diff --git a/test/t/kernel.rb b/test/t/kernel.rb index aff2dd461..40a3482f8 100644 --- a/test/t/kernel.rb +++ b/test/t/kernel.rb @@ -387,7 +387,7 @@ assert('Kernel#method_missing', '15.3.1.3.30') do begin c.no_method_named_this rescue NoMethodError => e - assert_equal "undefined method 'no_method_named_this' for #{c.to_s}", e.message + assert_equal "undefined method 'no_method_named_this' for #{c}", e.message end class NoInspectClass @@ -397,7 +397,7 @@ assert('Kernel#method_missing', '15.3.1.3.30') do begin d.no_method_named_this rescue NoMethodError => e - assert_equal "undefined method 'no_method_named_this' for #{d.to_s}", e.message + assert_equal "undefined method 'no_method_named_this' for #{d}", e.message end end diff --git a/test/t/module.rb b/test/t/module.rb index cfdca8503..e32cac5f1 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -268,6 +268,12 @@ assert('Module#const_get', '15.2.2.4.21') do end assert_equal 42, Test4ConstGet.const_get(:Const4Test4ConstGet) + assert_equal 42, Test4ConstGet.const_get("Const4Test4ConstGet") + assert_equal 42, Object.const_get("Test4ConstGet::Const4Test4ConstGet") + + assert_raise(TypeError){ Test4ConstGet.const_get(123) } + assert_raise(NameError){ Test4ConstGet.const_get(:I_DO_NOT_EXIST) } + assert_raise(NameError){ Test4ConstGet.const_get("I_DO_NOT_EXIST::ME_NEITHER") } end assert('Module#const_missing', '15.2.2.4.22') do @@ -280,7 +286,7 @@ assert('Module#const_missing', '15.2.2.4.22') do assert_equal 42, Test4ConstMissing.const_get(:ConstDoesntExist) end -assert('Module#const_get', '15.2.2.4.23') do +assert('Module#const_set', '15.2.2.4.23') do module Test4ConstSet Const4Test4ConstSet = 42 end @@ -309,10 +315,14 @@ assert('Module#include', '15.2.2.4.27') do Const4Include = 42 end module Test4Include2 - include Test4Include + @include_result = include Test4Include + class << self + attr_reader :include_result + end end assert_equal 42, Test4Include2.const_get(:Const4Include) + assert_equal Test4Include2, Test4Include2.include_result end assert('Module#include?', '15.2.2.4.28') do @@ -540,6 +550,18 @@ end assert_equal(expected, obj.m1) end + assert('Module#prepend result') do + module TestPrepended; end + module TestPrependResult + @prepend_result = prepend TestPrepended + class << self + attr_reader :prepend_result + end + end + + assert_equal TestPrependResult, TestPrependResult.prepend_result + end + # mruby shouldn't be affected by this since there is # no visibility control (yet) assert('Module#prepend public') do @@ -787,10 +809,28 @@ end # @!endgroup prepend assert('Module#to_s') do - module Test4to_sModules + module Outer + class Inner; end + const_set :SetInner, Class.new end - assert_equal 'Test4to_sModules', Test4to_sModules.to_s + assert_equal 'Outer', Outer.to_s + assert_equal 'Outer::Inner', Outer::Inner.to_s + assert_equal 'Outer::SetInner', Outer::SetInner.to_s + + outer = Module.new do + const_set :SetInner, Class.new + end + Object.const_set :SetOuter, outer + + assert_equal 'SetOuter', SetOuter.to_s + assert_equal 'SetOuter::SetInner', SetOuter::SetInner.to_s + + mod = Module.new + cls = Class.new + + assert_equal "#<Module:0x", mod.to_s[0,11] + assert_equal "#<Class:0x", cls.to_s[0,10] end assert('Module#inspect') do diff --git a/test/t/superclass.rb b/test/t/superclass.rb index 9fd8830b3..10b6438d3 100644 --- a/test/t/superclass.rb +++ b/test/t/superclass.rb @@ -23,7 +23,8 @@ [:Exception, :Object, '15.2.22.2'], [:StandardError, :Exception, '15.2.23.2'], [:ArgumentError, :StandardError, '15.2.24.2'], - [:LocalJumpError, :StandardError, '15.2.25.2'], + # [:LocalJumpError, :StandardError, '15.2.25.2'], + [:LocalJumpError, :ScriptError, '15.2.25.2'], # mruby specific [:RangeError, :StandardError, '12.2.26.2'], [:RegexpError, :StandardError, '12.2.27.2'], [:RuntimeError, :StandardError, '12.2.28.2'], diff --git a/test/t/syntax.rb b/test/t/syntax.rb index 461f5430b..299394557 100644 --- a/test/t/syntax.rb +++ b/test/t/syntax.rb @@ -381,7 +381,7 @@ assert('splat in case statement') do end assert('External command execution.') do - class << Kernel + module Kernel sym = '`'.to_sym alias_method :old_cmd, sym |
