diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/array.c | 302 | ||||
| -rw-r--r-- | src/backtrace.c | 205 | ||||
| -rw-r--r-- | src/cdump.c | 468 | ||||
| -rw-r--r-- | src/class.c | 1792 | ||||
| -rw-r--r-- | src/codedump.c | 284 | ||||
| -rw-r--r-- | src/crc.c | 39 | ||||
| -rw-r--r-- | src/debug.c | 214 | ||||
| -rw-r--r-- | src/dump.c | 542 | ||||
| -rw-r--r-- | src/enum.c | 22 | ||||
| -rw-r--r-- | src/error.c | 401 | ||||
| -rw-r--r-- | src/etc.c | 152 | ||||
| -rw-r--r-- | src/ext/.gitkeep | 0 | ||||
| -rw-r--r-- | src/fmt_fp.c | 644 | ||||
| -rw-r--r-- | src/gc.c | 533 | ||||
| -rw-r--r-- | src/hash.c | 1857 | ||||
| -rw-r--r-- | src/kernel.c | 431 | ||||
| -rw-r--r-- | src/load.c | 504 | ||||
| -rw-r--r-- | src/mruby_core.rake | 20 | ||||
| -rw-r--r-- | src/numeric.c | 1290 | ||||
| -rw-r--r-- | src/object.c | 315 | ||||
| -rw-r--r-- | src/pool.c | 2 | ||||
| -rw-r--r-- | src/print.c | 68 | ||||
| -rw-r--r-- | src/proc.c | 343 | ||||
| -rw-r--r-- | src/range.c | 477 | ||||
| -rw-r--r-- | src/readflt.c | 120 | ||||
| -rw-r--r-- | src/readint.c | 30 | ||||
| -rw-r--r-- | src/state.c | 204 | ||||
| -rw-r--r-- | src/string.c | 2195 | ||||
| -rw-r--r-- | src/symbol.c | 396 | ||||
| -rw-r--r-- | src/value_array.h | 1 | ||||
| -rw-r--r-- | src/variable.c | 465 | ||||
| -rw-r--r-- | src/vm.c | 1816 |
32 files changed, 9037 insertions, 7095 deletions
diff --git a/src/array.c b/src/array.c index 0b039a6ec..bb7190bfd 100644 --- a/src/array.c +++ b/src/array.c @@ -9,6 +9,8 @@ #include <mruby/class.h> #include <mruby/string.h> #include <mruby/range.h> +#include <mruby/proc.h> +#include <mruby/presym.h> #include "value_array.h" #define ARY_DEFAULT_LEN 4 @@ -27,7 +29,7 @@ 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 = MRB_OBJ_ALLOC(mrb, MRB_TT_ARRAY, mrb->array_class); if (capa <= MRB_ARY_EMBED_LEN_MAX) { ARY_SET_EMBED_LEN(a, 0); } @@ -54,18 +56,17 @@ mrb_ary_new(mrb_state *mrb) } /* - * to copy array, use this instead of memcpy because of portability + * To copy array, use this instead of memcpy because of portability * * gcc on ARM may fail optimization of memcpy - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3934.html + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56620 * * gcc on MIPS also fail - * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755 + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39755 * * memcpy doesn't exist on freestanding environment * * If you optimize for binary size, use memcpy instead of this at your own risk * of above portability issue. * - * see also http://togetter.com/li/462898 - * + * See also https://togetter.com/li/462898 (Japanese) */ static inline void array_copy(mrb_value *dst, const mrb_value *src, mrb_int size) @@ -120,9 +121,7 @@ ary_fill_with_nil(mrb_value *ptr, mrb_int size) static void ary_modify_check(mrb_state *mrb, struct RArray *a) { - if (MRB_FROZEN_P(a)) { - mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen array"); - } + mrb_check_frozen(mrb, a); } static void @@ -280,7 +279,7 @@ static mrb_value mrb_ary_s_create(mrb_state *mrb, mrb_value klass) { mrb_value ary; - mrb_value *vals; + const mrb_value *vals; mrb_int len; struct RArray *a; @@ -340,7 +339,7 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; - mrb_value *ptr; + const mrb_value *ptr; mrb_int blen, len1; mrb_get_args(mrb, "a", &ptr, &blen); @@ -388,7 +387,7 @@ ary_replace(mrb_state *mrb, struct RArray *a, struct RArray *b) mrb_write_barrier(mrb, (struct RBasic*)a); return; } - if (!MRB_FROZEN_P(b) && len > ARY_REPLACE_SHARED_MIN) { + if (!mrb_frozen_p(b) && len > ARY_REPLACE_SHARED_MIN) { ary_make_shared(mrb, b); goto shared_b; } @@ -508,22 +507,26 @@ mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem) static mrb_value mrb_ary_push_m(mrb_state *mrb, mrb_value self) { - mrb_value *argv; - mrb_int len, len2, alen; + mrb_int argc; + const mrb_value *argv; + mrb_int len, len2; struct RArray *a; - mrb_get_args(mrb, "*!", &argv, &alen); + argc = mrb_get_argc(mrb); + argv = mrb_get_argv(mrb); a = mrb_ary_ptr(self); ary_modify(mrb, a); len = ARY_LEN(a); - len2 = len + alen; + len2 = len + argc; if (ARY_CAPA(a) < len2) { ary_expand_capa(mrb, a, len2); } - array_copy(ARY_PTR(a)+len, argv, alen); + array_copy(ARY_PTR(a)+len, argv, argc); ARY_SET_LEN(a, len2); - mrb_write_barrier(mrb, (struct RBasic*)a); - + while (argc--) { + mrb_field_write_barrier_value(mrb, (struct RBasic*)a, *argv); + argv++; + } return self; } @@ -575,6 +578,48 @@ mrb_ary_shift(mrb_state *mrb, mrb_value self) return val; } +static mrb_value +mrb_ary_shift_m(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + mrb_int len = ARY_LEN(a); + mrb_int n; + mrb_value val; + + if (mrb_get_args(mrb, "|i", &n) == 0) { + return mrb_ary_shift(mrb, self); + }; + ary_modify_check(mrb, a); + if (len == 0 || n == 0) return mrb_ary_new(mrb); + if (n < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array shift"); + if (n > len) n = len; + val = mrb_ary_new_from_values(mrb, n, ARY_PTR(a)); + if (ARY_SHARED_P(a)) { + L_SHIFT: + a->as.heap.ptr+=n; + a->as.heap.len-=n; + return val; + } + if (len > ARY_SHIFT_SHARED_MIN) { + ary_make_shared(mrb, a); + goto L_SHIFT; + } + else if (len == n) { + ARY_SET_LEN(a, 0); + } + else { + mrb_value *ptr = ARY_PTR(a); + mrb_int size = len-n; + + while (size--) { + *ptr = *(ptr+n); + ++ptr; + } + ARY_SET_LEN(a, len-n); + } + return val; +} + /* self = [1,2,3] item = 0 self.unshift item @@ -611,7 +656,8 @@ static mrb_value mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_value *vals, *ptr; + const mrb_value *vals; + mrb_value *ptr; mrb_int alen, len; mrb_get_args(mrb, "*!", &vals, &alen); @@ -631,11 +677,13 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) ptr = a->as.heap.ptr; } else { + mrb_bool same = vals == ARY_PTR(a); ary_modify(mrb, a); if (ARY_CAPA(a) < len + alen) ary_expand_capa(mrb, a, len + alen); ptr = ARY_PTR(a); value_move(ptr + alen, ptr, len); + if (same) vals = ptr; } array_copy(ptr, vals, alen); ARY_SET_LEN(a, len+alen); @@ -646,19 +694,6 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) return self; } -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 += len; - if (n < 0 || len <= n) return mrb_nil_value(); - - return ARY_PTR(a)[n]; -} - MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val) { @@ -670,7 +705,7 @@ mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val) if (n < 0) { n += len; if (n < 0) { - mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - len)); + mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of array", n - len); } } if (len <= n) { @@ -702,7 +737,7 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val ary_modify(mrb, a); /* len check */ - if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%S)", mrb_fixnum_value(len)); + if (len < 0) mrb_raisef(mrb, E_INDEX_ERROR, "negative length (%i)", len); /* range check */ if (head < 0) { @@ -730,13 +765,17 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val argv = ARY_PTR(r); } } + else if (mrb_undef_p(rpl)) { + argc = 0; + argv = NULL; + } else { argc = 1; argv = &rpl; } if (head >= alen) { if (head > ARY_MAX_SIZE - argc) { - mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(head)); + mrb_raisef(mrb, E_INDEX_ERROR, "index %i too big", head); } len = head + argc; if (len > ARY_CAPA(a)) { @@ -752,7 +791,7 @@ mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_val mrb_int newlen; if (alen - len > ARY_MAX_SIZE - argc) { - mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(alen + argc - len)); + mrb_raisef(mrb, E_INDEX_ERROR, "index %i too big", alen + argc - len); } newlen = alen + argc - len; if (newlen > ARY_CAPA(a)) { @@ -792,7 +831,7 @@ ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len) 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 = MRB_OBJ_ALLOC(mrb, MRB_TT_ARRAY, mrb->array_class); b->as.heap.ptr = a->as.heap.ptr + beg; b->as.heap.len = len; b->as.heap.aux.shared = a->as.heap.aux.shared; @@ -802,20 +841,27 @@ ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len) return mrb_obj_value(b); } +mrb_value +mrb_ary_subseq(mrb_state *mrb, mrb_value ary, mrb_int beg, mrb_int len) +{ + struct RArray *a = mrb_ary_ptr(ary); + return ary_subseq(mrb, a, beg, len); +} + static mrb_int aget_index(mrb_state *mrb, mrb_value index) { - if (mrb_fixnum_p(index)) { - return mrb_fixnum(index); + if (mrb_integer_p(index)) { + return mrb_integer(index); } -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT else if (mrb_float_p(index)) { return (mrb_int)mrb_float(index); } #endif else { mrb_int i, argc; - mrb_value *argv; + const mrb_value *argv; mrb_get_args(mrb, "i*!", &i, &argv, &argc); return i; @@ -853,26 +899,29 @@ static mrb_value mrb_ary_aget(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_int i, len, alen; + mrb_int i; + mrb_int len, alen; mrb_value index; - if (mrb_get_args(mrb, "o|i", &index, &len) == 1) { + if (mrb_get_argc(mrb) == 1) { + index = mrb_get_arg1(mrb); switch (mrb_type(index)) { /* a[n..m] */ case MRB_TT_RANGE: - if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == 1) { + if (mrb_range_beg_len(mrb, index, &i, &len, ARY_LEN(a), TRUE) == MRB_RANGE_OK) { return ary_subseq(mrb, a, i, len); } else { return mrb_nil_value(); } - case MRB_TT_FIXNUM: - return mrb_ary_ref(mrb, self, mrb_fixnum(index)); + case MRB_TT_INTEGER: + return mrb_ary_ref(mrb, self, mrb_integer(index)); default: return mrb_ary_ref(mrb, self, aget_index(mrb, index)); } } + mrb_get_args(mrb, "oi", &index, &len); i = aget_index(mrb, index); alen = ARY_LEN(a); if (i < 0) i += alen; @@ -925,23 +974,27 @@ mrb_ary_aset(mrb_state *mrb, mrb_value self) mrb_value v1, v2, v3; mrb_int i, len; - mrb_ary_modify(mrb, mrb_ary_ptr(self)); - if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) { + ary_modify(mrb, mrb_ary_ptr(self)); + if (mrb_get_argc(mrb) == 2) { + const mrb_value *vs = mrb_get_argv(mrb); + v1 = vs[0]; v2 = vs[1]; + /* a[n..m] = v */ switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) { - case 0: /* not range */ + case MRB_RANGE_TYPE_MISMATCH: mrb_ary_set(mrb, self, aget_index(mrb, v1), v2); break; - case 1: /* range */ + case MRB_RANGE_OK: mrb_ary_splice(mrb, self, i, len, v2); break; - case 2: /* out of range */ - mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", v1); + case MRB_RANGE_OUT: + mrb_raisef(mrb, E_RANGE_ERROR, "%v out of range", v1); break; } return v2; } + mrb_get_args(mrb, "ooo", &v1, &v2, &v3); /* a[n,m] = v */ mrb_ary_splice(mrb, self, aget_index(mrb, v1), aget_index(mrb, v2), v3); return v3; @@ -1025,10 +1078,9 @@ mrb_ary_last(mrb_state *mrb, mrb_value self) static mrb_value mrb_ary_index_m(mrb_state *mrb, mrb_value self) { - mrb_value obj; + mrb_value obj = mrb_get_arg1(mrb); mrb_int i; - mrb_get_args(mrb, "o", &obj); for (i = 0; i < RARRAY_LEN(self); i++) { if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) { return mrb_fixnum_value(i); @@ -1040,10 +1092,9 @@ mrb_ary_index_m(mrb_state *mrb, mrb_value self) static mrb_value mrb_ary_rindex_m(mrb_state *mrb, mrb_value self) { - mrb_value obj; + mrb_value obj = mrb_get_arg1(mrb); mrb_int i, len; - mrb_get_args(mrb, "o", &obj); for (i = RARRAY_LEN(self) - 1; i >= 0; i--) { if (mrb_equal(mrb, RARRAY_PTR(self)[i], obj)) { return mrb_fixnum_value(i); @@ -1058,33 +1109,26 @@ mrb_ary_rindex_m(mrb_state *mrb, mrb_value self) MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value v) { - mrb_value a, recv_class; + mrb_value ary; + struct RArray *a; if (mrb_array_p(v)) { - return v; + a = ary_dup(mrb, mrb_ary_ptr(v)); + return mrb_obj_value(a); } - if (!mrb_respond_to(mrb, v, mrb_intern_lit(mrb, "to_a"))) { + if (!mrb_respond_to(mrb, v, MRB_SYM(to_a))) { return mrb_ary_new_from_values(mrb, 1, &v); } - a = mrb_funcall(mrb, v, "to_a", 0); - if (mrb_array_p(a)) { - return a; - } - else if (mrb_nil_p(a)) { + ary = mrb_funcall_id(mrb, v, MRB_SYM(to_a), 0); + if (mrb_nil_p(ary)) { return mrb_ary_new_from_values(mrb, 1, &v); } - else { - recv_class = mrb_obj_value(mrb_obj_class(mrb, v)); - mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Array (%S#to_a gives %S)", - recv_class, - recv_class, - mrb_obj_value(mrb_obj_class(mrb, a)) - ); - /* not reached */ - return mrb_undef_value(); - } + mrb_ensure_array_type(mrb, ary); + a = mrb_ary_ptr(ary); + a = ary_dup(mrb, a); + return mrb_obj_value(a); } static mrb_value @@ -1108,12 +1152,24 @@ mrb_ary_clear(mrb_state *mrb, mrb_value self) else if (!ARY_EMBED_P(a)){ mrb_free(mrb, a->as.heap.ptr); } - ARY_SET_EMBED_LEN(a, 0); - + if (MRB_ARY_EMBED_LEN_MAX > 0) { + ARY_SET_EMBED_LEN(a, 0); + } + else { + a->as.heap.ptr = NULL; + a->as.heap.aux.capa = 0; + ARY_SET_LEN(a, 0); + } return self; } static mrb_value +mrb_ary_clear_m(mrb_state *mrb, mrb_value self) +{ + return mrb_ary_clear(mrb, self); +} + +static mrb_value mrb_ary_empty_p(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); @@ -1122,21 +1178,16 @@ mrb_ary_empty_p(mrb_state *mrb, mrb_value self) } MRB_API mrb_value -mrb_check_array_type(mrb_state *mrb, mrb_value ary) +mrb_ary_entry(mrb_value ary, mrb_int n) { - return mrb_check_convert_type(mrb, ary, MRB_TT_ARRAY, "Array", "to_ary"); -} + struct RArray *a = mrb_ary_ptr(ary); + mrb_int len = ARY_LEN(a); -MRB_API mrb_value -mrb_ary_entry(mrb_value ary, mrb_int offset) -{ - if (offset < 0) { - offset += RARRAY_LEN(ary); - } - if (offset < 0 || RARRAY_LEN(ary) <= offset) { - return mrb_nil_value(); - } - return RARRAY_PTR(ary)[offset]; + /* range check */ + if (n < 0) n += len; + if (n < 0 || len <= n) return mrb_nil_value(); + + return ARY_PTR(a)[n]; } static mrb_value @@ -1180,7 +1231,7 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list) val = tmp; goto str_join; } - tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary"); + tmp = mrb_check_array_type(mrb, val); if (!mrb_nil_p(tmp)) { val = tmp; goto ary_join; @@ -1228,9 +1279,9 @@ mrb_ary_join_m(mrb_state *mrb, mrb_value ary) static mrb_value mrb_ary_eq(mrb_state *mrb, mrb_value ary1) { - mrb_value ary2; + mrb_value ary2 = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &ary2); + mrb->c->ci->mid = 0; if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value(); if (!mrb_array_p(ary2)) { return mrb_false_value(); @@ -1243,9 +1294,9 @@ mrb_ary_eq(mrb_state *mrb, mrb_value ary1) static mrb_value mrb_ary_cmp(mrb_state *mrb, mrb_value ary1) { - mrb_value ary2; + mrb_value ary2 = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &ary2); + mrb->c->ci->mid = 0; if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0); if (!mrb_array_p(ary2)) { return mrb_nil_value(); @@ -1258,7 +1309,6 @@ mrb_ary_cmp(mrb_state *mrb, mrb_value ary1) static mrb_value mrb_ary_svalue(mrb_state *mrb, mrb_value ary) { - mrb_get_args(mrb, ""); switch (RARRAY_LEN(ary)) { case 0: return mrb_nil_value(); @@ -1274,41 +1324,39 @@ mrb_init_array(mrb_state *mrb) { struct RClass *a; - mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */ + mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */ MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY); - mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */ - - mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */ - mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */ - mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */ - mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.4 */ - mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ANY()); /* 15.2.12.5.5 */ - mrb_define_method(mrb, a, "clear", mrb_ary_clear, MRB_ARGS_NONE()); /* 15.2.12.5.6 */ - mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */ - mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */ - mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */ - mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */ - mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */ - mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */ - mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_ANY()); /* 15.2.12.5.17 */ - mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_ANY()); /* 15.2.12.5.18 */ - mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */ - mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */ - mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */ - mrb_define_method(mrb, a, "append", mrb_ary_push_m, MRB_ARGS_ANY()); - mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */ - mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */ - mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */ - mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */ - mrb_define_method(mrb, a, "shift", mrb_ary_shift, MRB_ARGS_NONE()); /* 15.2.12.5.27 */ - mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */ - mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.29 */ - mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */ - mrb_define_method(mrb, a, "prepend", mrb_ary_unshift_m, MRB_ARGS_ANY()); + mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */ + + mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */ + mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */ + mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */ + mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ARG(1,1)); /* 15.2.12.5.4 */ + mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ARG(2,1)); /* 15.2.12.5.5 */ + mrb_define_method(mrb, a, "clear", mrb_ary_clear_m, MRB_ARGS_NONE()); /* 15.2.12.5.6 */ + mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */ + mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */ + mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */ + mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */ + mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */ + mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */ + mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_OPT(1)); /* 15.2.12.5.17 */ + mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_OPT(1)); /* 15.2.12.5.18 */ + mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */ + mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */ + mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */ + mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */ + mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */ + mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */ + mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */ + mrb_define_method(mrb, a, "shift", mrb_ary_shift_m, MRB_ARGS_OPT(1)); /* 15.2.12.5.27 */ + mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */ + mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ARG(1,1)); /* 15.2.12.5.29 */ + mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */ mrb_define_method(mrb, a, "__ary_eq", mrb_ary_eq, MRB_ARGS_REQ(1)); mrb_define_method(mrb, a, "__ary_cmp", mrb_ary_cmp, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, a, "__ary_index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */ + mrb_define_method(mrb, a, "__ary_index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* kept for mruby-array-ext */ mrb_define_method(mrb, a, "__svalue", mrb_ary_svalue, MRB_ARGS_NONE()); } diff --git a/src/backtrace.c b/src/backtrace.c index 57ae7fd7f..65cb0da1c 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -14,128 +14,112 @@ #include <mruby/error.h> #include <mruby/numeric.h> #include <mruby/data.h> +#include <mruby/presym.h> struct backtrace_location { - int lineno; - const char *filename; + int32_t lineno; mrb_sym method_id; + const char *filename; }; -typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location*, void*); +typedef void (*each_backtrace_func)(mrb_state*, const struct backtrace_location*, void*); static const mrb_data_type bt_type = { "Backtrace", mrb_free }; +mrb_value mrb_exc_inspect(mrb_state *mrb, mrb_value exc); +mrb_value mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace); + static void -each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_func func, void *data) +each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, each_backtrace_func func, void *data) { - ptrdiff_t i, j; - if (ciidx >= mrb->c->ciend - mrb->c->cibase) ciidx = 10; /* ciidx is broken... */ - for (i=ciidx, j=0; i >= 0; i--,j++) { + for (ptrdiff_t i=ciidx; i >= 0; i--) { struct backtrace_location loc; mrb_callinfo *ci; - mrb_irep *irep; - mrb_code *pc; + const mrb_irep *irep = 0; + const mrb_code *pc; + uint32_t idx; ci = &mrb->c->cibase[i]; - if (!ci->proc) continue; - if (MRB_PROC_CFUNC_P(ci->proc)) continue; - - irep = ci->proc->body.irep; - if (!irep) continue; - - if (mrb->c->cibase[i].err) { - pc = mrb->c->cibase[i].err; - } - else if (i+1 <= ciidx) { - if (!mrb->c->cibase[i + 1].pc) continue; - pc = &mrb->c->cibase[i+1].pc[-1]; + if (!ci->proc || MRB_PROC_CFUNC_P(ci->proc)) { + if (!ci->mid) continue; + loc.lineno = -1; + idx = 0; } else { - pc = pc0; + irep = ci->proc->body.irep; + if (!irep) continue; + if (mrb->c->cibase[i].pc) { + pc = &mrb->c->cibase[i].pc[-1]; + } + else { + continue; + } + idx = (uint32_t)(pc - irep->iseq); + loc.lineno = mrb_debug_get_line(mrb, irep, idx); + } + loc.method_id = ci->mid; + if (loc.lineno == -1) { + for (ptrdiff_t j=i-1; j >= 0; j--) { + ci = &mrb->c->cibase[j]; + + if (!ci->proc) continue; + if (MRB_PROC_CFUNC_P(ci->proc)) continue; + + irep = ci->proc->body.irep; + if (!irep) continue; + + if (mrb->c->cibase[j].pc) { + pc = &mrb->c->cibase[j].pc[-1]; + } + else { + continue; + } + + idx = (uint32_t)(pc - irep->iseq); + loc.lineno = mrb_debug_get_line(mrb, irep, idx); + if (loc.lineno > 0) break; + } } - loc.filename = mrb_debug_get_filename(irep, pc - irep->iseq); - loc.lineno = mrb_debug_get_line(irep, pc - irep->iseq); - - if (loc.lineno == -1) continue; + loc.filename = mrb_debug_get_filename(mrb, irep, idx); if (!loc.filename) { loc.filename = "(unknown)"; } - loc.method_id = ci->mid; func(mrb, &loc, data); } } -#ifndef MRB_DISABLE_STDIO +#ifndef MRB_NO_STDIO static void -print_backtrace(mrb_state *mrb, mrb_value backtrace) +print_backtrace(mrb_state *mrb, struct RObject *exc, mrb_value backtrace) { - int i; - mrb_int n; + mrb_int i; + mrb_int n = RARRAY_LEN(backtrace); + mrb_value *loc, mesg; FILE *stream = stderr; - if (!mrb_array_p(backtrace)) return; - - n = RARRAY_LEN(backtrace) - 1; - if (n == 0) return; - - fprintf(stream, "trace (most recent call last):\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)); + if (n != 0) { + if (n > 1) { + fprintf(stream, "trace (most recent call last):\n"); } - } -} - -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_packed_backtrace(mrb_state *mrb, mrb_value packed) -{ - FILE *stream = stderr; - struct backtrace_location *bt; - int n, i; - int ai = mrb_gc_arena_save(mrb); - - bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, packed, &bt_type); - if (bt == NULL) return; - n = (mrb_int)RDATA(packed)->flags; - - if (packed_bt_len(bt, n) == 0) return; - fprintf(stream, "trace (most recent call last):\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); - fprintf(stream, ":in %s", method_name); - mrb_gc_arena_restore(mrb, ai); + for (i=n-1,loc=&RARRAY_PTR(backtrace)[i]; i>0; i--,loc--) { + if (mrb_string_p(*loc)) { + fprintf(stream, "\t[%d] %.*s\n", + (int)i, (int)RSTRING_LEN(*loc), RSTRING_PTR(*loc)); + } + } + if (mrb_string_p(*loc)) { + fprintf(stream, "%.*s: ", (int)RSTRING_LEN(*loc), RSTRING_PTR(*loc)); } - fprintf(stream, "\n"); } + mesg = mrb_exc_inspect(mrb, mrb_obj_value(exc)); + fprintf(stream, "%.*s\n", (int)RSTRING_LEN(mesg), RSTRING_PTR(mesg)); } /* mrb_print_backtrace @@ -152,14 +136,10 @@ mrb_print_backtrace(mrb_state *mrb) return; } - backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace")); + backtrace = mrb_obj_iv_get(mrb, mrb->exc, MRB_SYM(backtrace)); if (mrb_nil_p(backtrace)) return; - if (mrb_array_p(backtrace)) { - print_backtrace(mrb, backtrace); - } - else { - print_packed_backtrace(mrb, backtrace); - } + if (!mrb_array_p(backtrace)) backtrace = mrb_unpack_backtrace(mrb, backtrace); + print_backtrace(mrb, mrb->exc, backtrace); } #else @@ -172,24 +152,22 @@ mrb_print_backtrace(mrb_state *mrb) static void count_backtrace_i(mrb_state *mrb, - struct backtrace_location *loc, + const struct backtrace_location *loc, void *data) { int *lenp = (int*)data; - if (loc->filename == NULL) return; (*lenp)++; } static void pack_backtrace_i(mrb_state *mrb, - struct backtrace_location *loc, + const struct backtrace_location *loc, void *data) { struct backtrace_location **pptr = (struct backtrace_location**)data; struct backtrace_location *ptr = *pptr; - if (loc->filename == NULL) return; *ptr = *loc; *pptr = ptr+1; } @@ -203,20 +181,19 @@ packed_backtrace(mrb_state *mrb) int size; void *ptr; - each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len); + each_backtrace(mrb, ciidx, count_backtrace_i, &len); size = len * sizeof(struct backtrace_location); ptr = mrb_malloc(mrb, size); - if (ptr) 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); + backtrace->flags = (uint32_t)len; + each_backtrace(mrb, ciidx, pack_backtrace_i, &ptr); return mrb_obj_value(backtrace); } void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc) { - mrb_sym sym = mrb_intern_lit(mrb, "backtrace"); + mrb_sym sym = MRB_SYM(backtrace); mrb_value backtrace; int ai; @@ -230,7 +207,7 @@ mrb_keep_backtrace(mrb_state *mrb, mrb_value exc) mrb_value mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace) { - struct backtrace_location *bt; + const struct backtrace_location *bt; mrb_int n, i; int ai; @@ -242,19 +219,22 @@ mrb_unpack_backtrace(mrb_state *mrb, mrb_value 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; + if (n == 0) goto empty_backtrace; 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]; + const struct backtrace_location *entry = &bt[i]; mrb_value btline; - 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->lineno != -1) {//debug info was available + btline = mrb_format(mrb, "%s:%d", entry->filename, (int)entry->lineno); + } + else { //all that was left was the stack frame + btline = mrb_format(mrb, "%s:0", entry->filename); + } 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_str_cat_cstr(mrb, btline, mrb_sym_name(mrb, entry->method_id)); } mrb_ary_push(mrb, backtrace, btline); mrb_gc_arena_restore(mrb, ai); @@ -263,23 +243,22 @@ mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace) return backtrace; } -MRB_API mrb_value +mrb_value mrb_exc_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); + backtrace = mrb_iv_get(mrb, exc, MRB_SYM(backtrace)); if (mrb_nil_p(backtrace) || mrb_array_p(backtrace)) { return backtrace; } + /* unpack packed-backtrace */ backtrace = mrb_unpack_backtrace(mrb, backtrace); - mrb_iv_set(mrb, exc, attr_name, backtrace); + mrb_iv_set(mrb, exc, MRB_SYM(backtrace), backtrace); return backtrace; } -MRB_API mrb_value +mrb_value mrb_get_backtrace(mrb_state *mrb) { return mrb_unpack_backtrace(mrb, packed_backtrace(mrb)); diff --git a/src/cdump.c b/src/cdump.c new file mode 100644 index 000000000..ecc27d9a1 --- /dev/null +++ b/src/cdump.c @@ -0,0 +1,468 @@ +/* +** dump.c - mruby binary dumper (mrbc binary format) +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> +#include <mruby/string.h> +#include <mruby/dump.h> +#include <mruby/irep.h> +#include <mruby/debug.h> + +#include <string.h> + +#ifndef MRB_NO_STDIO + +#ifndef MRB_NO_FLOAT +#include <mruby/endian.h> +#define MRB_FLOAT_FMT "%.17g" +#endif + +static int +cdump_pool(mrb_state *mrb, const mrb_pool_value *p, FILE *fp) +{ + if (p->tt & IREP_TT_NFLAG) { /* number */ + switch (p->tt) { +#ifdef MRB_64BIT + case IREP_TT_INT64: + if (p->u.i64 < INT32_MIN || INT32_MAX < p->u.i64) { + fprintf(fp, "{IREP_TT_INT64, {.i64=%" PRId64 "}},\n", p->u.i64); + } + else { + fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", (int32_t)p->u.i64); + } + break; +#endif + case IREP_TT_INT32: + fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", p->u.i32); + break; + case IREP_TT_FLOAT: +#ifndef MRB_NO_FLOAT + if (p->u.f == 0) { + fprintf(fp, "{IREP_TT_FLOAT, {.f=%#.1f}},\n", p->u.f); + } + else { + fprintf(fp, "{IREP_TT_FLOAT, {.f=" MRB_FLOAT_FMT "}},\n", p->u.f); + } +#endif + break; + case IREP_TT_BIGINT: + { + const char *s = p->u.str; + int len = s[0]+2; + fputs("{IREP_TT_BIGINT, {\"", fp); + for (int i=0; i<len; i++) { + fprintf(fp, "\\x%02x", (int)s[i]&0xff); + } + fputs("\"}},\n", fp); + } + break; + } + } + else { /* string */ + int i, len = p->tt>>2; + const char *s = p->u.str; + fprintf(fp, "{IREP_TT_STR|(%d<<2), {\"", len); + for (i=0; i<len; i++) { + fprintf(fp, "\\x%02x", (int)s[i]&0xff); + } + fputs("\"}},\n", fp); + } + return MRB_DUMP_OK; +} + +static mrb_bool +sym_name_word_p(const char *name, mrb_int len) +{ + if (len == 0) return FALSE; + if (name[0] != '_' && !ISALPHA(name[0])) return FALSE; + for (int i = 1; i < len; i++) { + if (name[i] != '_' && !ISALNUM(name[i])) return FALSE; + } + return TRUE; +} + +static mrb_bool +sym_name_with_equal_p(const char *name, mrb_int len) +{ + return len >= 2 && name[len-1] == '=' && sym_name_word_p(name, len-1); +} + +static mrb_bool +sym_name_with_question_mark_p(const char *name, mrb_int len) +{ + return len >= 2 && name[len-1] == '?' && sym_name_word_p(name, len-1); +} + +static mrb_bool +sym_name_with_bang_p(const char *name, mrb_int len) +{ + return len >= 2 && name[len-1] == '!' && sym_name_word_p(name, len-1); +} + +static mrb_bool +sym_name_ivar_p(const char *name, mrb_int len) +{ + return len >= 2 && name[0] == '@' && sym_name_word_p(name+1, len-1); +} + +static mrb_bool +sym_name_cvar_p(const char *name, mrb_int len) +{ + return len >= 3 && name[0] == '@' && sym_name_ivar_p(name+1, len-1); +} + +#define OPERATOR_SYMBOL(sym_name, name) {name, sym_name, sizeof(sym_name)-1} +struct operator_symbol { + const char *name; + const char *sym_name; + uint16_t sym_name_len; +}; +static const struct operator_symbol operator_table[] = { + OPERATOR_SYMBOL("!", "not"), + OPERATOR_SYMBOL("%", "mod"), + OPERATOR_SYMBOL("&", "and"), + OPERATOR_SYMBOL("*", "mul"), + OPERATOR_SYMBOL("+", "add"), + OPERATOR_SYMBOL("-", "sub"), + OPERATOR_SYMBOL("/", "div"), + OPERATOR_SYMBOL("<", "lt"), + OPERATOR_SYMBOL(">", "gt"), + OPERATOR_SYMBOL("^", "xor"), + OPERATOR_SYMBOL("`", "tick"), + OPERATOR_SYMBOL("|", "or"), + OPERATOR_SYMBOL("~", "neg"), + OPERATOR_SYMBOL("!=", "neq"), + OPERATOR_SYMBOL("!~", "nmatch"), + OPERATOR_SYMBOL("&&", "andand"), + OPERATOR_SYMBOL("**", "pow"), + OPERATOR_SYMBOL("+@", "plus"), + OPERATOR_SYMBOL("-@", "minus"), + OPERATOR_SYMBOL("<<", "lshift"), + OPERATOR_SYMBOL("<=", "le"), + OPERATOR_SYMBOL("==", "eq"), + OPERATOR_SYMBOL("=~", "match"), + OPERATOR_SYMBOL(">=", "ge"), + OPERATOR_SYMBOL(">>", "rshift"), + OPERATOR_SYMBOL("[]", "aref"), + OPERATOR_SYMBOL("||", "oror"), + OPERATOR_SYMBOL("<=>", "cmp"), + OPERATOR_SYMBOL("===", "eqq"), + OPERATOR_SYMBOL("[]=", "aset"), +}; + +static const char* +sym_operator_name(const char *sym_name, mrb_int len) +{ + mrb_sym table_size = sizeof(operator_table)/sizeof(struct operator_symbol); + if (operator_table[table_size-1].sym_name_len < len) return NULL; + + mrb_sym start, idx; + int cmp; + const struct operator_symbol *op_sym; + for (start = 0; table_size != 0; table_size/=2) { + idx = start+table_size/2; + op_sym = &operator_table[idx]; + cmp = (int)len-(int)op_sym->sym_name_len; + if (cmp == 0) { + cmp = memcmp(sym_name, op_sym->sym_name, len); + if (cmp == 0) return op_sym->name; + } + if (0 < cmp) { + start = ++idx; + --table_size; + } + } + return NULL; +} + +static const char* +sym_var_name(mrb_state *mrb, const char *initname, const char *key, int n) +{ + char buf[32]; + mrb_value s = mrb_str_new_cstr(mrb, initname); + mrb_str_cat_lit(mrb, s, "_"); + mrb_str_cat_cstr(mrb, s, key); + mrb_str_cat_lit(mrb, s, "_"); + snprintf(buf, sizeof(buf), "%d", n); + mrb_str_cat_cstr(mrb, s, buf); + return RSTRING_PTR(s); +} + +static int +cdump_sym(mrb_state *mrb, mrb_sym sym, const char *var_name, int idx, mrb_value init_syms_code, FILE *fp) +{ + if (sym == 0) return MRB_DUMP_INVALID_ARGUMENT; + + mrb_int len; + const char *name = mrb_sym_name_len(mrb, sym, &len), *op_name; + if (!name) return MRB_DUMP_INVALID_ARGUMENT; + if (sym_name_word_p(name, len)) { + fprintf(fp, "MRB_SYM(%s)", name); + } + else if (sym_name_with_equal_p(name, len)) { + fprintf(fp, "MRB_SYM_E(%.*s)", (int)(len-1), name); + } + else if (sym_name_with_question_mark_p(name, len)) { + fprintf(fp, "MRB_SYM_Q(%.*s)", (int)(len-1), name); + } + else if (sym_name_with_bang_p(name, len)) { + fprintf(fp, "MRB_SYM_B(%.*s)", (int)(len-1), name); + } + else if (sym_name_ivar_p(name, len)) { + fprintf(fp, "MRB_IVSYM(%s)", name+1); + } + else if (sym_name_cvar_p(name, len)) { + fprintf(fp, "MRB_CVSYM(%s)", name+2); + } + else if ((op_name = sym_operator_name(name, len))) { + fprintf(fp, "MRB_OPSYM(%s)", op_name); + } + else { + char buf[32]; + mrb_value name_obj = mrb_str_new(mrb, name, len); + mrb_str_cat_lit(mrb, init_syms_code, " "); + mrb_str_cat_cstr(mrb, init_syms_code, var_name); + snprintf(buf, sizeof(buf), "[%d] = ", idx); + mrb_str_cat_cstr(mrb, init_syms_code, buf); + mrb_str_cat_lit(mrb, init_syms_code, "mrb_intern_lit(mrb, "); + mrb_str_cat_str(mrb, init_syms_code, mrb_str_dump(mrb, name_obj)); + mrb_str_cat_lit(mrb, init_syms_code, ");\n"); + fputs("0", fp); + } + fputs(", ", fp); + return MRB_DUMP_OK; +} + +static int +cdump_syms(mrb_state *mrb, const char *name, const char *key, int n, int syms_len, const mrb_sym *syms, mrb_value init_syms_code, FILE *fp) +{ + int ai = mrb_gc_arena_save(mrb); + mrb_int code_len = RSTRING_LEN(init_syms_code); + const char *var_name = sym_var_name(mrb, name, key, n); + fprintf(fp, "mrb_DEFINE_SYMS_VAR(%s, %d, (", var_name, syms_len); + for (int i=0; i<syms_len; i++) { + cdump_sym(mrb, syms[i], var_name, i, init_syms_code, fp); + } + fputs("), ", fp); + if (code_len == RSTRING_LEN(init_syms_code)) fputs("const", fp); + fputs(");\n", fp); + mrb_gc_arena_restore(mrb, ai); + return MRB_DUMP_OK; +} + +//Handle the simple/common case of debug_info: +// - 1 file associated with a single irep +// - mrb_debug_line_ary format only +static int +simple_debug_info(mrb_irep_debug_info *info) +{ + if (!info || info->flen != 1) { + return 0; + } + return 1; +} + +//Adds debug information to c-structs and +//adds filenames in init_syms_code block +static int +cdump_debug(mrb_state *mrb, const char *name, int n, mrb_irep_debug_info *info, + mrb_value init_syms_code, FILE *fp) +{ + char buffer[256]; + const char *filename; + mrb_int file_len; + int len, i; + const char *line_type = "mrb_debug_line_ary"; + + if (!simple_debug_info(info)) + return MRB_DUMP_INVALID_IREP; + + len = info->files[0]->line_entry_count; + + filename = mrb_sym_name_len(mrb, info->files[0]->filename_sym, &file_len); + snprintf(buffer, sizeof(buffer), " %s_debug_file_%d.filename_sym = mrb_intern_lit(mrb,\"", + name, n); + mrb_str_cat_cstr(mrb, init_syms_code, buffer); + mrb_str_cat_cstr(mrb, init_syms_code, filename); + mrb_str_cat_cstr(mrb, init_syms_code, "\");\n"); + + switch (info->files[0]->line_type) { + case mrb_debug_line_ary: + fprintf(fp, "static uint16_t %s_debug_lines_%d[%d] = {", name, n, len); + for (i=0; i<len; i++) { + if (i%10 == 0) fputs("\n", fp); + fprintf(fp, "0x%04x,", info->files[0]->lines.ary[i]); + } + fputs("};\n", fp); + break; + + case mrb_debug_line_flat_map: + line_type = "mrb_debug_line_flat_map"; + fprintf(fp, "static struct mrb_irep_debug_info_line %s_debug_lines_%d[%d] = {", name, n, len); + for (i=0; i<len; i++) { + mrb_irep_debug_info_line *fmap = &info->files[0]->lines.flat_map[i]; + fprintf(fp, "\t{.start_pos=0x%04x,.line=%d},\n", fmap->start_pos, fmap->line); + } + fputs("};\n", fp); + break; + + case mrb_debug_line_packed_map: + line_type = "mrb_debug_line_packed_map"; + fprintf(fp, "static char %s_debug_lines_%d[] = \"", name, n); + uint8_t *pmap = info->files[0]->lines.packed_map; + for (i=0; i<len; i++) { + fprintf(fp, "\\x%02x", pmap[i]&0xff); + } + fputs("\";\n", fp); + break; + } + fprintf(fp, "static mrb_irep_debug_info_file %s_debug_file_%d = {\n", name, n); + fprintf(fp, "%d, %d, %d, %s, {%s_debug_lines_%d}};\n", + info->files[0]->start_pos, + info->files[0]->filename_sym, + info->files[0]->line_entry_count, + line_type, + name,n); + fprintf(fp, "static mrb_irep_debug_info_file *%s_debug_file_%d_ = &%s_debug_file_%d;\n", name, n, name, n); + + fprintf(fp, "static mrb_irep_debug_info %s_debug_%d = {\n", name, n); + fprintf(fp, "%d, %d, &%s_debug_file_%d_};\n", info->pc_count, info->flen, name, n); + + return MRB_DUMP_OK; +} + +static int +cdump_irep_struct(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *name, int n, mrb_value init_syms_code, int *mp) +{ + int i, len; + int max = *mp; + int debug_available = 0; + + /* dump reps */ + if (irep->reps) { + for (i=0,len=irep->rlen; i<len; i++) { + *mp += len; + if (cdump_irep_struct(mrb, irep->reps[i], flags, fp, name, max+i, init_syms_code, mp) != MRB_DUMP_OK) + return MRB_DUMP_INVALID_ARGUMENT; + } + fprintf(fp, "static const mrb_irep *%s_reps_%d[%d] = {\n", name, n, len); + for (i=0,len=irep->rlen; i<len; i++) { + fprintf(fp, " &%s_irep_%d,\n", name, max+i); + } + fputs("};\n", fp); + } + /* dump pool */ + if (irep->pool) { + len=irep->plen; + fprintf(fp, "static const mrb_pool_value %s_pool_%d[%d] = {\n", name, n, len); + for (i=0; i<len; i++) { + if (cdump_pool(mrb, &irep->pool[i], fp) != MRB_DUMP_OK) + return MRB_DUMP_INVALID_ARGUMENT; + } + fputs("};\n", fp); + } + /* dump syms */ + if (irep->syms) { + cdump_syms(mrb, name, "syms", n, irep->slen, irep->syms, init_syms_code, fp); + } + /* dump iseq */ + len=irep->ilen+sizeof(struct mrb_irep_catch_handler)*irep->clen; + fprintf(fp, "static const mrb_code %s_iseq_%d[%d] = {", name, n, len); + for (i=0; i<len; i++) { + if (i%20 == 0) fputs("\n", fp); + fprintf(fp, "0x%02x,", irep->iseq[i]); + } + fputs("};\n", fp); + /* dump lv */ + if (irep->lv) { + cdump_syms(mrb, name, "lv", n, irep->nlocals-1, irep->lv, init_syms_code, fp); + } + /* dump debug */ + if (flags & MRB_DUMP_DEBUG_INFO) { + if(cdump_debug(mrb, name, n, irep->debug_info, + init_syms_code, fp) == MRB_DUMP_OK) { + debug_available = 1; + } + } + + + /* dump irep */ + fprintf(fp, "static const mrb_irep %s_irep_%d = {\n", name, n); + fprintf(fp, " %d,%d,%d,\n", irep->nlocals, irep->nregs, irep->clen); + fprintf(fp, " MRB_IREP_STATIC,%s_iseq_%d,\n", name, n); + if (irep->pool) { + fprintf(fp, " %s_pool_%d,", name, n); + } + else { + fputs( " NULL,", fp); + } + if (irep->syms) { + fprintf(fp, "%s_syms_%d,", name, n); + } + else { + fputs( "NULL,", fp); + } + if (irep->reps) { + fprintf(fp, "%s_reps_%d,\n", name, n); + } + else { + fputs( "NULL,\n", fp); + } + if (irep->lv) { + fprintf(fp, " %s_lv_%d,\n", name, n); + } + else { + fputs( " NULL,\t\t\t\t\t/* lv */\n", fp); + } + if(debug_available) { + fprintf(fp, " &%s_debug_%d,\n", name, n); + } + else { + fputs(" NULL,\t\t\t\t\t/* debug_info */\n", fp); + } + fprintf(fp, " %d,%d,%d,%d,0\n};\n", irep->ilen, irep->plen, irep->slen, irep->rlen); + + return MRB_DUMP_OK; +} + +int +mrb_dump_irep_cstruct(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname) +{ + if (fp == NULL || initname == NULL || initname[0] == '\0') { + return MRB_DUMP_INVALID_ARGUMENT; + } + if (fprintf(fp, "#include <mruby.h>\n" + "#include <mruby/irep.h>\n" + "#include <mruby/debug.h>\n" + "#include <mruby/proc.h>\n" + "#include <mruby/presym.h>\n" + "\n") < 0) { + return MRB_DUMP_WRITE_FAULT; + } + fputs("#define mrb_BRACED(...) {__VA_ARGS__}\n", fp); + fputs("#define mrb_DEFINE_SYMS_VAR(name, len, syms, qualifier) \\\n", fp); + fputs(" static qualifier mrb_sym name[len] = mrb_BRACED syms\n", fp); + fputs("\n", fp); + mrb_value init_syms_code = mrb_str_new_capa(mrb, 0); + int max = 1; + int n = cdump_irep_struct(mrb, irep, flags, fp, initname, 0, init_syms_code, &max); + if (n != MRB_DUMP_OK) return n; + fprintf(fp, + "%s\n" + "const struct RProc %s[] = {{\n", + (flags & MRB_DUMP_STATIC) ? "static" + : "#ifdef __cplusplus\n" + "extern\n" + "#endif", + initname); + fprintf(fp, "NULL,NULL,MRB_TT_PROC,MRB_GC_RED,0,{&%s_irep_0},NULL,{NULL},\n}};\n", initname); + fputs("static void\n", fp); + fprintf(fp, "%s_init_syms(mrb_state *mrb)\n", initname); + fputs("{\n", fp); + fputs(RSTRING_PTR(init_syms_code), fp); + fputs("}\n", fp); + return MRB_DUMP_OK; +} +#endif diff --git a/src/class.c b/src/class.c index 50ab0ea59..33a610c26 100644 --- a/src/class.c +++ b/src/class.c @@ -7,6 +7,7 @@ #include <stdarg.h> #include <mruby.h> #include <mruby/array.h> +#include <mruby/hash.h> #include <mruby/class.h> #include <mruby/numeric.h> #include <mruby/proc.h> @@ -15,48 +16,285 @@ #include <mruby/error.h> #include <mruby/data.h> #include <mruby/istruct.h> +#include <mruby/opcode.h> +#include <mruby/presym.h> -KHASH_DEFINE(mt, mrb_sym, mrb_method_t, TRUE, kh_int_hash_func, kh_int_hash_equal) +union mt_ptr { + struct RProc *proc; + mrb_func_t func; +}; + +struct mt_elem { + union mt_ptr ptr; + size_t func_p:1; + size_t noarg_p:1; + mrb_sym key:sizeof(mrb_sym)*8-2; +}; + +/* method table structure */ +typedef struct mt_tbl { + size_t size; + size_t alloc; + struct mt_elem *table; +} mt_tbl; + +/* Creates the method table. */ +static mt_tbl* +mt_new(mrb_state *mrb) +{ + mt_tbl *t; + + t = (mt_tbl*)mrb_malloc(mrb, sizeof(mt_tbl)); + t->size = 0; + t->alloc = 0; + t->table = NULL; + + return t; +} + +static struct mt_elem *mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, size_t func_p, size_t noarg_p, union mt_ptr ptr); + +static void +mt_rehash(mrb_state *mrb, mt_tbl *t) +{ + size_t old_alloc = t->alloc; + size_t new_alloc = old_alloc+1; + struct mt_elem *old_table = t->table; + + khash_power2(new_alloc); + if (old_alloc == new_alloc) return; + + t->alloc = new_alloc; + t->size = 0; + t->table = (struct mt_elem*)mrb_calloc(mrb, sizeof(struct mt_elem), new_alloc); + + for (size_t i = 0; i < old_alloc; i++) { + struct mt_elem *slot = &old_table[i]; + + /* key = 0 means empty or deleted */ + if (slot->key != 0) { + mt_put(mrb, t, slot->key, slot->func_p, slot->noarg_p, slot->ptr); + } + } + mrb_free(mrb, old_table); +} + +#define slot_empty_p(slot) ((slot)->key == 0 && (slot)->func_p == 0) + +/* Set the value for the symbol in the method table. */ +static struct mt_elem* +mt_put(mrb_state *mrb, mt_tbl *t, mrb_sym sym, size_t func_p, size_t noarg_p, union mt_ptr ptr) +{ + size_t hash, pos, start; + struct mt_elem *dslot = NULL; + + if (t->alloc == 0) { + mt_rehash(mrb, t); + } + hash = kh_int_hash_func(mrb, sym); + start = pos = hash & (t->alloc-1); + for (;;) { + struct mt_elem *slot = &t->table[pos]; + + if (slot->key == sym) { + slot->func_p = func_p; + slot->noarg_p = noarg_p; + slot->ptr = ptr; + return slot; + } + else if (slot->key == 0) { /* empty or deleted */ + if (slot->func_p == 0) { /* empty */ + t->size++; + slot->key = sym; + slot->func_p = func_p; + slot->noarg_p = noarg_p; + slot->ptr = ptr; + return slot; + } + else if (!dslot) { /* deleted */ + dslot = slot; + } + } + pos = (pos+1) & (t->alloc-1); + if (pos == start) { /* not found */ + if (dslot) { + t->size++; + dslot->key = sym; + dslot->func_p = func_p; + dslot->noarg_p = noarg_p; + dslot->ptr = ptr; + return dslot; + } + /* no room */ + mt_rehash(mrb, t); + start = pos = hash & (t->alloc-1); + } + } +} + +/* Get a value for a symbol from the method table. */ +static struct mt_elem* +mt_get(mrb_state *mrb, mt_tbl *t, mrb_sym sym) +{ + size_t hash, pos, start; + + if (t == NULL) return NULL; + if (t->alloc == 0) return NULL; + if (t->size == 0) return NULL; + + hash = kh_int_hash_func(mrb, sym); + start = pos = hash & (t->alloc-1); + for (;;) { + struct mt_elem *slot = &t->table[pos]; + + if (slot->key == sym) { + return slot; + } + else if (slot_empty_p(slot)) { + return NULL; + } + pos = (pos+1) & (t->alloc-1); + if (pos == start) { /* not found */ + return NULL; + } + } +} + +/* Deletes the value for the symbol from the method table. */ +static mrb_bool +mt_del(mrb_state *mrb, mt_tbl *t, mrb_sym sym) +{ + size_t hash, pos, start; + + if (t == NULL) return FALSE; + if (t->alloc == 0) return FALSE; + if (t->size == 0) return FALSE; + + hash = kh_int_hash_func(mrb, sym); + start = pos = hash & (t->alloc-1); + for (;;) { + struct mt_elem *slot = &t->table[pos]; + + if (slot->key == sym) { + t->size--; + slot->key = 0; + slot->func_p = 1; + return TRUE; + } + else if (slot_empty_p(slot)) { + return FALSE; + } + pos = (pos+1) & (t->alloc-1); + if (pos == start) { /* not found */ + return FALSE; + } + } +} + +/* Copy the method table. */ +static struct mt_tbl* +mt_copy(mrb_state *mrb, mt_tbl *t) +{ + mt_tbl *t2; + size_t i; + + if (t == NULL) return NULL; + if (t->alloc == 0) return NULL; + if (t->size == 0) return NULL; + + t2 = mt_new(mrb); + for (i=0; i<t->alloc; i++) { + struct mt_elem *slot = &t->table[i]; + + if (slot->key) { + mt_put(mrb, t2, slot->key, slot->func_p, slot->noarg_p, slot->ptr); + } + } + return t2; +} + +/* Free memory of the method table. */ +static void +mt_free(mrb_state *mrb, mt_tbl *t) +{ + mrb_free(mrb, t->table); + mrb_free(mrb, t); +} + +MRB_API void +mrb_mt_foreach(mrb_state *mrb, struct RClass *c, mrb_mt_foreach_func *fn, void *p) +{ + mt_tbl *t = c->mt; + size_t i; + + if (t == NULL) return; + if (t->alloc == 0) return; + if (t->size == 0) return; + + for (i=0; i<t->alloc; i++) { + struct mt_elem *slot = &t->table[i]; + + if (slot->key) { + mrb_method_t m; + + if (slot->func_p) { + MRB_METHOD_FROM_FUNC(m, slot->ptr.func); + } + else { + MRB_METHOD_FROM_PROC(m, slot->ptr.proc); + } + if (slot->noarg_p) { + MRB_METHOD_NOARG_SET(m); + } + + if (fn(mrb, slot->key, m, p) != 0) + return; + } + } + return; +} void mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c) { - khiter_t k; - khash_t(mt) *h = c->mt; + mt_tbl *t = c->mt; + size_t i; - if (!h) return; - for (k = kh_begin(h); k != kh_end(h); k++) { - if (kh_exist(h, k)) { - mrb_method_t m = kh_value(h, k); + if (t == NULL) return; + if (t->alloc == 0) return; + if (t->size == 0) return; - if (MRB_METHOD_PROC_P(m)) { - struct RProc *p = MRB_METHOD_PROC(m); - mrb_gc_mark(mrb, (struct RBasic*)p); - } + for (i=0; i<t->alloc; i++) { + struct mt_elem *slot = &t->table[i]; + + if (slot->key && !slot->func_p) { /* Proc pointer */ + struct RProc *p = slot->ptr.proc; + mrb_gc_mark(mrb, (struct RBasic*)p); } } + return; } size_t mrb_gc_mark_mt_size(mrb_state *mrb, struct RClass *c) { - khash_t(mt) *h = c->mt; + struct mt_tbl *h = c->mt; if (!h) return 0; - return kh_size(h); + return h->size; } void mrb_gc_free_mt(mrb_state *mrb, struct RClass *c) { - kh_destroy(mt, mrb, c->mt); + if (c->mt) mt_free(mrb, c->mt); } void mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) { mrb_value name; - mrb_sym nsym = mrb_intern_lit(mrb, "__classname__"); + mrb_sym nsym = MRB_SYM(__classname__); if (mrb_obj_iv_defined(mrb, (struct RObject*)c, nsym)) return; if (outer == NULL || outer == mrb->object_class) { @@ -66,15 +304,26 @@ mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb name = mrb_class_path(mrb, outer); if (mrb_nil_p(name)) { /* unnamed outer class */ if (outer != mrb->object_class && outer != c) { - mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"), - mrb_obj_value(outer)); + mrb_obj_iv_set_force(mrb, (struct RObject*)c, MRB_SYM(__outer__), + mrb_obj_value(outer)); } return; } - mrb_str_cat_cstr(mrb, name, "::"); - mrb_str_cat_cstr(mrb, name, mrb_sym2name(mrb, id)); + else { + mrb_int len; + const char *n = mrb_sym_name_len(mrb, id, &len); + + mrb_str_cat_lit(mrb, name, "::"); + mrb_str_cat(mrb, name, n, len); + } } - mrb_obj_iv_set(mrb, (struct RObject*)c, nsym, name); + mrb_obj_iv_set_force(mrb, (struct RObject*)c, nsym, name); +} + +mrb_bool +mrb_const_name_p(mrb_state *mrb, const char *name, mrb_int len) +{ + return len > 0 && ISUPPER(name[0]) && mrb_ident_p(name+1, len-1); } static void @@ -92,9 +341,9 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o) struct RClass *sc, *c; if (o->c->tt == MRB_TT_SCLASS) return; - sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); + sc = MRB_OBJ_ALLOC(mrb, MRB_TT_SCLASS, mrb->class_class); sc->flags |= MRB_FL_CLASS_IS_INHERITED; - sc->mt = kh_init(mt, mrb); + sc->mt = mt_new(mrb); sc->iv = 0; if (o->tt == MRB_TT_CLASS) { c = (struct RClass*)o; @@ -119,7 +368,21 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o) o->c = sc; mrb_field_write_barrier(mrb, (struct RBasic*)o, (struct RBasic*)sc); mrb_field_write_barrier(mrb, (struct RBasic*)sc, (struct RBasic*)o); - mrb_obj_iv_set(mrb, (struct RObject*)sc, mrb_intern_lit(mrb, "__attached__"), mrb_obj_value(o)); + mrb_obj_iv_set(mrb, (struct RObject*)sc, MRB_SYM(__attached__), mrb_obj_value(o)); + sc->flags |= o->flags & MRB_FL_OBJ_IS_FROZEN; +} + +static mrb_value +class_name_str(mrb_state *mrb, struct RClass* c) +{ + mrb_value path = mrb_class_path(mrb, c); + if (mrb_nil_p(path)) { + path = c->tt == MRB_TT_MODULE ? mrb_str_new_lit(mrb, "#<Module:") : + mrb_str_new_lit(mrb, "#<Class:"); + mrb_str_cat_str(mrb, path, mrb_ptr_to_str(mrb, c)); + mrb_str_cat_lit(mrb, path, ">"); + } + return path; } static struct RClass* @@ -157,7 +420,7 @@ static void check_if_class_or_module(mrb_state *mrb, mrb_value obj) { if (!class_ptr_p(obj)) { - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", mrb_inspect(mrb, obj)); + mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class/module", obj); } } @@ -187,15 +450,15 @@ mrb_define_module(mrb_state *mrb, const char *name) return define_module(mrb, mrb_intern_cstr(mrb, name), mrb->object_class); } -MRB_API struct RClass* +struct RClass* mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id) { check_if_class_or_module(mrb, outer); if (mrb_const_defined_at(mrb, outer, id)) { mrb_value old = mrb_const_get(mrb, outer, id); - if (mrb_type(old) != MRB_TT_MODULE) { - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a module", mrb_inspect(mrb, old)); + if (!mrb_module_p(old)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a module", old); } return mrb_class_ptr(old); } @@ -203,6 +466,15 @@ mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id) } MRB_API struct RClass* +mrb_define_module_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name) +{ + struct RClass * c = define_module(mrb, name, outer); + + setup_class(mrb, outer, c, name); + return c; +} + +MRB_API struct RClass* mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name) { mrb_sym id = mrb_intern_cstr(mrb, name); @@ -228,9 +500,8 @@ define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass * c = class_from_sym(mrb, outer, name); MRB_CLASS_ORIGIN(c); if (super && mrb_class_real(c->super) != super) { - mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %S (%S not %S)", - mrb_sym2str(mrb, name), - mrb_obj_value(c->super), mrb_obj_value(super)); + mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for Class %n (%C not %C)", + name, c->super, super); } return c; } @@ -245,7 +516,7 @@ MRB_API struct RClass* mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super) { if (!super) { - mrb_warn(mrb, "no super class for '%S', Object assumed", mrb_sym2str(mrb, name)); + mrb_warn(mrb, "no super class for '%n', Object assumed", name); } return define_class(mrb, name, super, mrb->object_class); } @@ -257,14 +528,10 @@ mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super) } static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value); -#ifdef MRB_METHOD_CACHE -static void mc_clear_all(mrb_state *mrb); -static void mc_clear_by_class(mrb_state *mrb, struct RClass*); -static void mc_clear_by_id(mrb_state *mrb, struct RClass*, mrb_sym); +#ifndef MRB_NO_METHOD_CACHE +static void mc_clear(mrb_state *mrb); #else -#define mc_clear_all(mrb) -#define mc_clear_by_class(mrb,c) -#define mc_clear_by_id(mrb,c,s) +#define mc_clear(mrb) #endif static void @@ -277,24 +544,23 @@ mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass) super = mrb->object_class; super->flags |= MRB_FL_CLASS_IS_INHERITED; s = mrb_obj_value(super); - mc_clear_by_class(mrb, klass); - mid = mrb_intern_lit(mrb, "inherited"); + mrb_mc_clear_by_class(mrb, klass); + mid = MRB_SYM(inherited); if (!mrb_func_basic_p(mrb, s, mid, mrb_bob_init)) { mrb_value c = mrb_obj_value(klass); mrb_funcall_argv(mrb, s, mid, 1, &c); } } -MRB_API struct RClass* +struct RClass* mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id) { struct RClass *s; struct RClass *c; if (!mrb_nil_p(super)) { - if (mrb_type(super) != MRB_TT_CLASS) { - mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", - mrb_inspect(mrb, super)); + if (!mrb_class_p(super)) { + mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%!v given)", super); } s = mrb_class_ptr(super); } @@ -305,14 +571,14 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id if (mrb_const_defined_at(mrb, outer, id)) { mrb_value old = mrb_const_get(mrb, outer, id); - if (mrb_type(old) != MRB_TT_CLASS) { - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class", mrb_inspect(mrb, old)); + if (!mrb_class_p(old)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class", old); } c = mrb_class_ptr(old); if (s) { /* check super class */ if (mrb_class_real(c->super) != s) { - mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %S", old); + mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %v", old); } } return c; @@ -326,21 +592,29 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name) { - mrb_value sym = mrb_check_intern_cstr(mrb, name); - if (mrb_nil_p(sym)) { - return FALSE; - } - return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), mrb_symbol(sym)); + mrb_sym sym = mrb_intern_check_cstr(mrb, name); + if (!sym) return FALSE; + return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), sym); +} + +MRB_API mrb_bool +mrb_class_defined_id(mrb_state *mrb, mrb_sym name) +{ + return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), name); } MRB_API mrb_bool mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name) { - mrb_value sym = mrb_check_intern_cstr(mrb, name); - if (mrb_nil_p(sym)) { - return FALSE; - } - return mrb_const_defined_at(mrb, mrb_obj_value(outer), mrb_symbol(sym)); + mrb_sym sym = mrb_intern_check_cstr(mrb, name); + if (!sym) return FALSE; + return mrb_const_defined_at(mrb, mrb_obj_value(outer), sym); +} + +MRB_API mrb_bool +mrb_class_defined_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name) +{ + return mrb_const_defined_at(mrb, mrb_obj_value(outer), name); } MRB_API struct RClass* @@ -350,19 +624,30 @@ mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name) } MRB_API struct RClass* +mrb_class_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name) +{ + return class_from_sym(mrb, outer, name); +} + +MRB_API struct RClass* mrb_class_get(mrb_state *mrb, const char *name) { return mrb_class_get_under(mrb, mrb->object_class, name); } MRB_API struct RClass* -mrb_exc_get(mrb_state *mrb, const char *name) +mrb_class_get_id(mrb_state *mrb, mrb_sym name) +{ + return mrb_class_get_under_id(mrb, mrb->object_class, name); +} + +MRB_API struct RClass* +mrb_exc_get_id(mrb_state *mrb, mrb_sym name) { struct RClass *exc, *e; - mrb_value c = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), - mrb_intern_cstr(mrb, name)); + mrb_value c = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), name); - if (mrb_type(c) != MRB_TT_CLASS) { + if (!mrb_class_p(c)) { mrb_raise(mrb, mrb->eException_class, "exception corrupted"); } exc = e = mrb_class_ptr(c); @@ -382,15 +667,27 @@ mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name) } MRB_API struct RClass* +mrb_module_get_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name) +{ + return module_from_sym(mrb, outer, name); +} + +MRB_API struct RClass* mrb_module_get(mrb_state *mrb, const char *name) { return mrb_module_get_under(mrb, mrb->object_class, name); } +MRB_API struct RClass* +mrb_module_get_id(mrb_state *mrb, mrb_sym name) +{ + return mrb_module_get_under_id(mrb, mrb->object_class, name); +} + /*! * Defines a class under the namespace of \a outer. * \param outer a class which contains the new class. - * \param id name of the new class + * \param name name of the new class * \param super a class from which the new class will derive. * NULL means \c Object class. * \return the created class @@ -404,50 +701,60 @@ mrb_module_get(mrb_state *mrb, const char *name) * \a super, the function just returns the defined class. */ MRB_API struct RClass* -mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super) +mrb_define_class_under_id(mrb_state *mrb, struct RClass *outer, mrb_sym name, struct RClass *super) { - mrb_sym id = mrb_intern_cstr(mrb, name); struct RClass * c; #if 0 if (!super) { - mrb_warn(mrb, "no super class for '%S::%S', Object assumed", - mrb_obj_value(outer), mrb_sym2str(mrb, id)); + mrb_warn(mrb, "no super class for '%C::%n', Object assumed", outer, id); } #endif - c = define_class(mrb, id, super, outer); - setup_class(mrb, outer, c, id); + c = define_class(mrb, name, super, outer); + setup_class(mrb, outer, c, name); return c; } +MRB_API struct RClass* +mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super) +{ + return mrb_define_class_under_id(mrb, outer, mrb_intern_cstr(mrb, name), super); +} + MRB_API void mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_method_t m) { - khash_t(mt) *h; - khiter_t k; + mt_tbl *h; + union mt_ptr ptr; + MRB_CLASS_ORIGIN(c); h = c->mt; - - if (MRB_FROZEN_P(c)) { - if (c->tt == MRB_TT_MODULE) - mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen module"); - else - mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen class"); - } - if (!h) h = c->mt = kh_init(mt, mrb); - k = kh_put(mt, mrb, h, mid); - kh_value(h, k) = m; - if (MRB_METHOD_PROC_P(m) && !MRB_METHOD_UNDEF_P(m)) { + mrb_check_frozen(mrb, c); + if (!h) h = c->mt = mt_new(mrb); + if (MRB_METHOD_PROC_P(m)) { struct RProc *p = MRB_METHOD_PROC(m); - p->flags |= MRB_PROC_SCOPE; - p->c = NULL; - mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)p); - if (!MRB_PROC_ENV_P(p)) { - MRB_PROC_SET_TARGET_CLASS(p, c); + ptr.proc = p; + if (p) { + if (p->color != MRB_GC_RED) { + p->flags |= MRB_PROC_SCOPE; + p->c = NULL; + mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)p); + if (!MRB_PROC_ENV_P(p)) { + MRB_PROC_SET_TARGET_CLASS(p, c); + } + } + else { + mrb_assert(MRB_FROZEN_P(p) && MRB_PROC_SCOPE_P(p)); + mrb_assert(p->c == NULL && p->upper == NULL && p->e.target_class == NULL); + } } } - mc_clear_by_id(mrb, c, mid); + else { + ptr.func = MRB_METHOD_FUNC(m); + } + mt_put(mrb, h, mid, MRB_METHOD_FUNC_P(m), MRB_METHOD_NOARG_P(m), ptr); + mc_clear(mrb); } MRB_API void @@ -457,6 +764,12 @@ mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t f int ai = mrb_gc_arena_save(mrb); MRB_METHOD_FROM_FUNC(m, func); +#ifndef MRB_USE_METHOD_T_STRUCT + mrb_assert(MRB_METHOD_FUNC(m) == func); +#endif + if (aspec == MRB_ARGS_NONE()) { + MRB_METHOD_NOARG_SET(m); + } mrb_define_method_raw(mrb, c, mid, m); mrb_gc_arena_restore(mrb, ai); } @@ -471,15 +784,10 @@ mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t MRB_API void mrb_notimplement(mrb_state *mrb) { - const char *str; - mrb_int len; mrb_callinfo *ci = mrb->c->ci; if (ci->mid) { - str = mrb_sym2name_len(mrb, ci->mid, &len); - mrb_raisef(mrb, E_NOTIMP_ERROR, - "%S() function is unimplemented on this machine", - mrb_str_new_static(mrb, str, (size_t)len)); + mrb_raisef(mrb, E_NOTIMP_ERROR, "%n() function is unimplemented on this machine", ci->mid); } } @@ -493,33 +801,17 @@ mrb_notimplement_m(mrb_state *mrb, mrb_value self) } static mrb_value -check_type(mrb_state *mrb, mrb_value val, enum mrb_vtype t, const char *c, const char *m) -{ - mrb_value tmp; - - tmp = mrb_check_convert_type(mrb, val, t, c, m); - if (mrb_nil_p(tmp)) { - mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_cstr(mrb, c)); - } - return tmp; -} - -static mrb_value -to_str(mrb_state *mrb, mrb_value val) -{ - return check_type(mrb, val, MRB_TT_STRING, "String", "to_str"); -} - -static mrb_value to_ary(mrb_state *mrb, mrb_value val) { - return check_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary"); + mrb_check_type(mrb, val, MRB_TT_ARRAY); + return val; } static mrb_value to_hash(mrb_state *mrb, mrb_value val) { - return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash"); + mrb_check_type(mrb, val, MRB_TT_HASH); + return val; } #define to_sym(mrb, ss) mrb_obj_to_sym(mrb, ss) @@ -530,29 +822,56 @@ mrb_get_argc(mrb_state *mrb) mrb_int argc = mrb->c->ci->argc; if (argc < 0) { - struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]); + struct RArray *a = mrb_ary_ptr(mrb->c->ci->stack[1]); argc = ARY_LEN(a); } return argc; } -MRB_API mrb_value* +MRB_API const mrb_value* mrb_get_argv(mrb_state *mrb) { mrb_int argc = mrb->c->ci->argc; - mrb_value *array_argv; + mrb_value *array_argv = mrb->c->ci->stack + 1; if (argc < 0) { - struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]); + struct RArray *a = mrb_ary_ptr(*array_argv); array_argv = ARY_PTR(a); } - else { - array_argv = NULL; - } return array_argv; } +MRB_API mrb_value +mrb_get_arg1(mrb_state *mrb) +{ + mrb_int argc = mrb->c->ci->argc; + mrb_value *array_argv = mrb->c->ci->stack + 1; + if (argc < 0) { + struct RArray *a = mrb_ary_ptr(*array_argv); + + argc = ARY_LEN(a); + array_argv = ARY_PTR(a); + } + if (argc != 1) { + mrb_argnum_error(mrb, argc, 1, 1); + } + return array_argv[0]; +} + +MRB_API mrb_bool +mrb_block_given_p(mrb_state *mrb) +{ + const mrb_callinfo *ci = mrb->c->ci; + int argc = ci->argc; + int idx = (argc < 0) ? 2 : argc + 1; + mrb_value b = ci->stack[idx]; + + return !mrb_nil_p(b); +} + +void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self); + /* retrieve arguments from mrb_state. @@ -565,23 +884,25 @@ mrb_get_argv(mrb_state *mrb) string mruby type C type note ---------------------------------------------------------------------------------------------- o: Object [mrb_value] - C: class/module [mrb_value] + C: Class/Module [mrb_value] S: String [mrb_value] when ! follows, the value may be nil A: Array [mrb_value] when ! follows, the value may be nil H: Hash [mrb_value] when ! follows, the value may be nil - s: String [char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil - z: String [char*] NUL terminated string; z! gives NULL for nil - a: Array [mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil - f: Float [mrb_float] - i: Integer [mrb_int] - b: Boolean [mrb_bool] - n: Symbol [mrb_sym] - d: Data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified - I: Inline struct [void*] - &: Block [mrb_value] &! raises exception if no block given - *: rest argument [mrb_value*,mrb_int] The rest of the arguments as an array; *! avoid copy of the stack + s: String [const char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil + z: String [const char*] NUL terminated string; z! gives NULL for nil + a: Array [const mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil + c: Class/Module [strcut RClass*] + f: Integer/Float [mrb_float] + i: Integer/Float [mrb_int] + b: boolean [mrb_bool] + n: String/Symbol [mrb_sym] + d: data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified; when ! follows, the value may be nil + I: inline struct [void*] + &: block [mrb_value] &! raises exception if no block given + *: rest argument [const 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 + ':': keyword args [mrb_kwargs const] Get keyword arguments */ MRB_API mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...) @@ -590,17 +911,24 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) char c; mrb_int i = 0; va_list ap; - mrb_int argc = mrb_get_argc(mrb); - mrb_int arg_i = 0; - mrb_value *array_argv = mrb_get_argv(mrb); + mrb_int argc = mrb->c->ci->argc; + mrb_value *array_argv = mrb->c->ci->stack+1; + mrb_bool argv_on_stack = argc >= 0; mrb_bool opt = FALSE; mrb_bool opt_skip = TRUE; mrb_bool given = TRUE; + mrb_value kdict; + mrb_bool reqkarg = FALSE; + int argc_min = 0, argc_max = 0; + if (!argv_on_stack) { + struct RArray *a = mrb_ary_ptr(*array_argv); + array_argv = ARY_PTR(a); + argc = ARY_LEN(a); + } va_start(ap, format); -#define ARGV \ - (array_argv ? array_argv : (mrb->c->stack + 1)) +#define ARGV array_argv while ((c = *fmt++)) { switch (c) { @@ -609,23 +937,41 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) break; case '*': opt_skip = FALSE; + argc_max = -1; + if (!reqkarg) reqkarg = strchr(fmt, ':') ? TRUE : FALSE; goto check_exit; case '!': break; + case ':': + reqkarg = TRUE; + /* fall through */ case '&': case '?': if (opt) opt_skip = FALSE; break; default: + if (!opt) argc_min++; + argc_max++; break; } } check_exit: + if (reqkarg && argc > argc_min && mrb_hash_p(kdict = ARGV[argc - 1])) { + mrb_hash_check_kdict(mrb, kdict); + argc --; + } + else { + kdict = mrb_nil_value(); + } + opt = FALSE; i = 0; while ((c = *format++)) { + mrb_value *argv = ARGV; + mrb_bool altmode; + switch (c) { - case '|': case '*': case '&': case '?': + case '|': case '*': case '&': case '?': case ':': break; default: if (argc <= i) { @@ -633,12 +979,20 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) given = FALSE; } else { - mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); + mrb_argnum_error(mrb, argc, argc_min, argc_max); } } break; } + if (*format == '!') { + format ++; + altmode = TRUE; + } + else { + altmode = FALSE; + } + switch (c) { case 'o': { @@ -646,8 +1000,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) p = va_arg(ap, mrb_value*); if (i < argc) { - *p = ARGV[arg_i++]; - i++; + *p = argv[i++]; } } break; @@ -659,12 +1012,27 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) if (i < argc) { mrb_value ss; - ss = ARGV[arg_i++]; + ss = argv[i++]; if (!class_ptr_p(ss)) { - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss); + mrb_raisef(mrb, E_TYPE_ERROR, "%v is not class/module", ss); } *p = ss; - i++; + } + } + break; + case 'c': + { + struct RClass **p; + + p = va_arg(ap, struct RClass**); + if (i < argc) { + mrb_value ss; + + ss = argv[i++]; + if (!class_ptr_p(ss)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%v is not class/module", ss); + } + *p = mrb_class_ptr(ss); } } break; @@ -673,17 +1041,11 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value *p; p = va_arg(ap, mrb_value*); - if (*format == '!') { - format++; - if (i < argc && mrb_nil_p(ARGV[arg_i])) { - *p = ARGV[arg_i++]; - i++; - break; - } - } if (i < argc) { - *p = to_str(mrb, ARGV[arg_i++]); - i++; + *p = argv[i++]; + if (!(altmode && mrb_nil_p(*p))) { + mrb_to_str(mrb, *p); + } } } break; @@ -692,17 +1054,11 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value *p; p = va_arg(ap, mrb_value*); - if (*format == '!') { - format++; - if (i < argc && mrb_nil_p(ARGV[arg_i])) { - *p = ARGV[arg_i++]; - i++; - break; - } - } if (i < argc) { - *p = to_ary(mrb, ARGV[arg_i++]); - i++; + *p = argv[i++]; + if (!(altmode && mrb_nil_p(*p))) { + *p = to_ary(mrb, *p); + } } } break; @@ -711,42 +1067,33 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value *p; p = va_arg(ap, mrb_value*); - if (*format == '!') { - format++; - if (i < argc && mrb_nil_p(ARGV[arg_i])) { - *p = ARGV[arg_i++]; - i++; - break; - } - } if (i < argc) { - *p = to_hash(mrb, ARGV[arg_i++]); - i++; + *p = argv[i++]; + if (!(altmode && mrb_nil_p(*p))) { + *p = to_hash(mrb, *p); + } } } break; case 's': { mrb_value ss; - char **ps = 0; + const char **ps = 0; mrb_int *pl = 0; - ps = va_arg(ap, char**); + ps = va_arg(ap, const char**); pl = va_arg(ap, mrb_int*); - if (*format == '!') { - format++; - if (i < argc && mrb_nil_p(ARGV[arg_i])) { + if (i < argc) { + ss = argv[i++]; + if (altmode && mrb_nil_p(ss)) { *ps = NULL; *pl = 0; - i++; arg_i++; - break; } - } - if (i < argc) { - ss = to_str(mrb, ARGV[arg_i++]); - *ps = RSTRING_PTR(ss); - *pl = RSTRING_LEN(ss); - i++; + else { + mrb_to_str(mrb, ss); + *ps = RSTRING_PTR(ss); + *pl = RSTRING_LEN(ss); + } } } break; @@ -756,18 +1103,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) const char **ps; ps = va_arg(ap, const char**); - if (*format == '!') { - format++; - if (i < argc && mrb_nil_p(ARGV[arg_i])) { + if (i < argc) { + ss = argv[i++]; + if (altmode && mrb_nil_p(ss)) { *ps = NULL; - i++; arg_i++; - break; } - } - if (i < argc) { - ss = to_str(mrb, ARGV[arg_i++]); - *ps = mrb_string_value_cstr(mrb, &ss); - i++; + else { + mrb_to_str(mrb, ss); + *ps = RSTRING_CSTR(mrb, ss); + } } } break; @@ -775,26 +1119,23 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) { mrb_value aa; struct RArray *a; - mrb_value **pb; + const mrb_value **pb; mrb_int *pl; - pb = va_arg(ap, mrb_value**); + pb = va_arg(ap, const mrb_value**); pl = va_arg(ap, mrb_int*); - if (*format == '!') { - format++; - if (i < argc && mrb_nil_p(ARGV[arg_i])) { + if (i < argc) { + aa = argv[i++]; + if (altmode && mrb_nil_p(aa)) { *pb = 0; *pl = 0; - i++; arg_i++; - break; } - } - if (i < argc) { - aa = to_ary(mrb, ARGV[arg_i++]); - a = mrb_ary_ptr(aa); - *pb = ARY_PTR(a); - *pl = ARY_LEN(a); - i++; + else { + aa = to_ary(mrb, aa); + a = mrb_ary_ptr(aa); + *pb = ARY_PTR(a); + *pl = ARY_LEN(a); + } } } break; @@ -805,27 +1146,23 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) p = va_arg(ap, void**); if (i < argc) { - ss = ARGV[arg_i]; - if (mrb_type(ss) != MRB_TT_ISTRUCT) + ss = argv[i++]; + if (!mrb_istruct_p(ss)) { - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not inline struct", ss); + mrb_raisef(mrb, E_TYPE_ERROR, "%v is not inline struct", ss); } *p = mrb_istruct_ptr(ss); - arg_i++; - i++; } } break; -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT case 'f': { mrb_float *p; p = va_arg(ap, mrb_float*); if (i < argc) { - *p = mrb_to_flo(mrb, ARGV[arg_i]); - arg_i++; - i++; + *p = mrb_as_float(mrb, argv[i]); i++; } } break; @@ -836,31 +1173,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) p = va_arg(ap, mrb_int*); if (i < argc) { - switch (mrb_type(ARGV[arg_i])) { - case MRB_TT_FIXNUM: - *p = mrb_fixnum(ARGV[arg_i]); - break; -#ifndef MRB_WITHOUT_FLOAT - case MRB_TT_FLOAT: - { - mrb_float f = mrb_float(ARGV[arg_i]); - - if (!FIXABLE_FLOAT(f)) { - mrb_raise(mrb, E_RANGE_ERROR, "float too big for int"); - } - *p = (mrb_int)f; - } - break; -#endif - case MRB_TT_STRING: - mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer"); - break; - default: - *p = mrb_fixnum(mrb_Integer(mrb, ARGV[arg_i])); - break; - } - arg_i++; - i++; + *p = mrb_as_int(mrb, argv[i]); i++; } } break; @@ -869,9 +1182,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_bool *boolp = va_arg(ap, mrb_bool*); if (i < argc) { - mrb_value b = ARGV[arg_i++]; + mrb_value b = argv[i++]; *boolp = mrb_test(b); - i++; } } break; @@ -883,9 +1195,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) if (i < argc) { mrb_value ss; - ss = ARGV[arg_i++]; + ss = argv[i++]; *symp = to_sym(mrb, ss); - i++; } } break; @@ -896,17 +1207,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) datap = va_arg(ap, void**); type = va_arg(ap, struct mrb_data_type const*); - if (*format == '!') { - format++; - if (i < argc && mrb_nil_p(ARGV[arg_i])) { + if (i < argc) { + mrb_value dd = argv[i++]; + if (altmode && mrb_nil_p(dd)) { *datap = 0; - i++; arg_i++; - break; } - } - if (i < argc) { - *datap = mrb_data_get_ptr(mrb, ARGV[arg_i++], type); - ++i; + else { + *datap = mrb_data_get_ptr(mrb, dd, type); + } } } break; @@ -917,22 +1225,19 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) p = va_arg(ap, mrb_value*); if (mrb->c->ci->argc < 0) { - bp = mrb->c->stack + 2; + bp = mrb->c->ci->stack + 2; } else { - bp = mrb->c->stack + mrb->c->ci->argc + 1; + bp = mrb->c->ci->stack + mrb->c->ci->argc + 1; } - if (*format == '!') { - format ++; - if (mrb_nil_p(*bp)) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); - } + if (altmode && mrb_nil_p(*bp)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } *p = *bp; } break; case '|': - if (opt_skip && i == argc) return argc; + if (opt_skip && i == argc) goto finish; opt = TRUE; break; case '?': @@ -946,30 +1251,25 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) case '*': { - mrb_value **var; + const mrb_value **var; mrb_int *pl; - mrb_bool nocopy = array_argv ? TRUE : FALSE; + mrb_bool nocopy = (altmode || !argv_on_stack) ? TRUE : FALSE; - if (*format == '!') { - format++; - nocopy = TRUE; - } - var = va_arg(ap, mrb_value**); + var = va_arg(ap, const mrb_value**); pl = va_arg(ap, mrb_int*); if (argc > i) { *pl = argc-i; if (*pl > 0) { if (nocopy) { - *var = ARGV+arg_i; + *var = argv+i; } else { - mrb_value args = mrb_ary_new_from_values(mrb, *pl, ARGV+arg_i); + mrb_value args = mrb_ary_new_from_values(mrb, *pl, argv+i); RARRAY(args)->c = NULL; *var = RARRAY_PTR(args); } } i = argc; - arg_i += *pl; } else { *pl = 0; @@ -977,8 +1277,64 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) } } break; + + case ':': + { + mrb_value ksrc = mrb_hash_p(kdict) ? mrb_hash_dup(mrb, kdict) : mrb_hash_new(mrb); + const mrb_kwargs *kwargs = va_arg(ap, const mrb_kwargs*); + mrb_value *rest; + + if (kwargs == NULL) { + rest = NULL; + } + else { + uint32_t kwnum = kwargs->num; + uint32_t required = kwargs->required; + const mrb_sym *kname = kwargs->table; + mrb_value *values = kwargs->values; + uint32_t j; + const uint32_t keyword_max = 40; + + if (kwnum > keyword_max || required > kwnum) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword number is too large"); + } + + for (j = required; j > 0; j --, kname ++, values ++) { + mrb_value k = mrb_symbol_value(*kname); + if (!mrb_hash_key_p(mrb, ksrc, k)) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "missing keyword: %n", *kname); + } + *values = mrb_hash_delete_key(mrb, ksrc, k); + mrb_gc_protect(mrb, *values); + } + + for (j = kwnum - required; j > 0; j --, kname ++, values ++) { + mrb_value k = mrb_symbol_value(*kname); + if (mrb_hash_key_p(mrb, ksrc, k)) { + *values = mrb_hash_delete_key(mrb, ksrc, k); + mrb_gc_protect(mrb, *values); + } + else { + *values = mrb_undef_value(); + } + } + + rest = kwargs->rest; + } + + if (rest) { + *rest = ksrc; + } + else if (!mrb_hash_empty_p(mrb, ksrc)) { + ksrc = mrb_hash_keys(mrb, ksrc); + ksrc = RARRAY_PTR(ksrc)[0]; + mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown keyword: %v", ksrc); + } + } + break; + default: - mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %S", mrb_str_new(mrb, &c, 1)); + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %c", c); break; } } @@ -986,8 +1342,10 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) #undef ARGV if (!c && argc > i) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); + mrb_argnum_error(mrb, argc, argc_min, argc_max); } + +finish: va_end(ap); return i; } @@ -997,7 +1355,7 @@ boot_defclass(mrb_state *mrb, struct RClass *super) { struct RClass *c; - c = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_CLASS, mrb->class_class); + c = MRB_OBJ_ALLOC(mrb, MRB_TT_CLASS, mrb->class_class); if (super) { c->super = super; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)super); @@ -1005,7 +1363,7 @@ boot_defclass(mrb_state *mrb, struct RClass *super) else { c->super = mrb->object_class; } - c->mt = kh_init(mt, mrb); + c->mt = mt_new(mrb); return c; } @@ -1013,14 +1371,14 @@ static void boot_initmod(mrb_state *mrb, struct RClass *mod) { if (!mod->mt) { - mod->mt = kh_init(mt, mrb); + mod->mt = mt_new(mrb); } } static struct RClass* include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) { - struct RClass *ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); + struct RClass *ic = MRB_OBJ_ALLOC(mrb, MRB_TT_ICLASS, mrb->class_class); if (m->tt == MRB_TT_ICLASS) { m = m->c; } @@ -1044,8 +1402,10 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru void *klass_mt = find_origin(c)->mt; while (m) { - int superclass_seen = 0; + int original_seen = FALSE; + int superclass_seen = FALSE; + if (c == ins_pos) original_seen = TRUE; if (m->flags & MRB_FL_CLASS_IS_PREPENDED) goto skip; @@ -1054,16 +1414,17 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru p = c->super; while (p) { + if (c == p) original_seen = TRUE; if (p->tt == MRB_TT_ICLASS) { if (p->mt == m->mt) { - if (!superclass_seen) { + if (!superclass_seen && original_seen) { ins_pos = p; /* move insert point */ } goto skip; } } else if (p->tt == MRB_TT_CLASS) { if (!search_super) break; - superclass_seen = 1; + superclass_seen = TRUE; } p = p->super; } @@ -1072,65 +1433,123 @@ include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, stru m->flags |= MRB_FL_CLASS_IS_INHERITED; ins_pos->super = ic; mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); - mc_clear_by_class(mrb, ins_pos); ins_pos = ic; skip: m = m->super; } - mc_clear_all(mrb); + mc_clear(mrb); return 0; } +static int +fix_include_module(mrb_state *mrb, struct RBasic *obj, void *data) +{ + struct RClass **m = (struct RClass**)data; + + if (obj->tt == MRB_TT_ICLASS && obj->c == m[0] && !MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN)) { + struct RClass *ic = (struct RClass*)obj; + include_module_at(mrb, ic, ic, m[1], 1); + } + return MRB_EACH_OBJ_OK; +} + MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { - int changed = include_module_at(mrb, c, find_origin(c), m, 1); - if (changed < 0) { + mrb_check_frozen(mrb, c); + if (include_module_at(mrb, c, find_origin(c), m, 1) < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); } + if (c->tt == MRB_TT_MODULE && (c->flags & MRB_FL_CLASS_IS_INHERITED)) { + struct RClass *data[2]; + data[0] = c; + data[1] = m; + mrb_objspace_each_objects(mrb, fix_include_module, data); + } +} + +static int +fix_prepend_module(mrb_state *mrb, struct RBasic *obj, void *data) +{ + struct RClass **m = (struct RClass**)data; + struct RClass *c = (struct RClass*)obj; + + if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) { + struct RClass *p = c->super; + struct RClass *ins_pos = c; + while (p) { + if (c == m[0]) break; + if (p == m[0]->super->c) { + ins_pos = c; + } + if (p->tt == MRB_TT_CLASS) break; + if (p->c == m[0]) { + include_module_at(mrb, ins_pos, ins_pos, m[1], 0); + break; + } + c = p; + p = p->super; + } + } + return MRB_EACH_OBJ_OK; } MRB_API void mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) { struct RClass *origin; - int changed = 0; + mrb_check_frozen(mrb, c); if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) { - origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + struct RClass *c0; + + if (c->tt == MRB_TT_ICLASS) { + c0 = c->c; + } + else { + c0 = c; + } + origin = MRB_OBJ_ALLOC(mrb, MRB_TT_ICLASS, c0); origin->flags |= MRB_FL_CLASS_IS_ORIGIN | MRB_FL_CLASS_IS_INHERITED; origin->super = c->super; c->super = origin; origin->mt = c->mt; - c->mt = kh_init(mt, mrb); + c->mt = NULL; + origin->iv = c->iv; mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin); c->flags |= MRB_FL_CLASS_IS_PREPENDED; } - changed = include_module_at(mrb, c, c, m, 0); - if (changed < 0) { + if (include_module_at(mrb, c, c, m, 0) < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected"); } + if (c->tt == MRB_TT_MODULE && + (c->flags & (MRB_FL_CLASS_IS_INHERITED|MRB_FL_CLASS_IS_PREPENDED))) { + struct RClass *data[2]; + data[0] = c; + data[1] = m; + mrb_objspace_each_objects(mrb, fix_prepend_module, data); + } } static mrb_value mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod) { - mrb_value klass; + struct RClass *c; mrb_check_type(mrb, mod, MRB_TT_MODULE); - mrb_get_args(mrb, "C", &klass); - mrb_prepend_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); + mrb_get_args(mrb, "c", &c); + mrb_prepend_module(mrb, c, mrb_class_ptr(mod)); return mod; } static mrb_value mrb_mod_append_features(mrb_state *mrb, mrb_value mod) { - mrb_value klass; + struct RClass *c; mrb_check_type(mrb, mod, MRB_TT_MODULE); - mrb_get_args(mrb, "C", &klass); - mrb_include_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); + mrb_get_args(mrb, "c", &c); + mrb_include_module(mrb, c, mrb_class_ptr(mod)); return mod; } @@ -1193,10 +1612,9 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self) static mrb_value mrb_mod_extend_object(mrb_state *mrb, mrb_value mod) { - mrb_value obj; + mrb_value obj = mrb_get_arg1(mrb); mrb_check_type(mrb, mod, MRB_TT_MODULE); - mrb_get_args(mrb, "o", &obj); mrb_include_module(mrb, mrb_class_ptr(mrb_singleton_class(mrb, obj)), mrb_class_ptr(mod)); return mod; } @@ -1223,33 +1641,44 @@ mrb_mod_dummy_visibility(mrb_state *mrb, mrb_value mod) return mod; } -MRB_API mrb_value -mrb_singleton_class(mrb_state *mrb, mrb_value v) +/* returns mrb_class_ptr(mrb_singleton_class()) */ +/* except that it return NULL for immediate values */ +MRB_API struct RClass* +mrb_singleton_class_ptr(mrb_state *mrb, mrb_value v) { struct RBasic *obj; switch (mrb_type(v)) { case MRB_TT_FALSE: if (mrb_nil_p(v)) - return mrb_obj_value(mrb->nil_class); - return mrb_obj_value(mrb->false_class); + return mrb->nil_class; + return mrb->false_class; case MRB_TT_TRUE: - return mrb_obj_value(mrb->true_class); + return mrb->true_class; case MRB_TT_CPTR: - return mrb_obj_value(mrb->object_class); case MRB_TT_SYMBOL: - case MRB_TT_FIXNUM: -#ifndef MRB_WITHOUT_FLOAT + case MRB_TT_INTEGER: +#ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: #endif - mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton"); - return mrb_nil_value(); /* not reached */ + return NULL; default: break; } obj = mrb_basic_ptr(v); prepare_singleton_class(mrb, obj); - return mrb_obj_value(obj->c); + return obj->c; +} + +MRB_API mrb_value +mrb_singleton_class(mrb_state *mrb, mrb_value v) +{ + struct RClass *c = mrb_singleton_class_ptr(mrb, v); + + if (c == NULL) { + mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton"); + } + return mrb_obj_value(c); } MRB_API void @@ -1260,60 +1689,56 @@ mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name, } MRB_API void +mrb_define_singleton_method_id(mrb_state *mrb, struct RObject *o, mrb_sym name, mrb_func_t func, mrb_aspec aspec) +{ + prepare_singleton_class(mrb, (struct RBasic*)o); + mrb_define_method_id(mrb, o->c, name, func, aspec); +} + +MRB_API void mrb_define_class_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) { mrb_define_singleton_method(mrb, (struct RObject*)c, name, func, aspec); } MRB_API void -mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) +mrb_define_class_method_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec) { - mrb_define_class_method(mrb, c, name, func, aspec); - mrb_define_method(mrb, c, name, func, aspec); + mrb_define_singleton_method_id(mrb, (struct RObject*)c, name, func, aspec); } -#ifdef MRB_METHOD_CACHE -static void -mc_clear_all(mrb_state *mrb) +MRB_API void +mrb_define_module_function_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec) { - struct mrb_cache_entry *mc = mrb->cache; - int i; + mrb_define_class_method_id(mrb, c, name, func, aspec); + mrb_define_method_id(mrb, c, name, func, aspec); +} - for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) { - mc[i].c = 0; - } +MRB_API void +mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) +{ + mrb_define_module_function_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec); } +#ifndef MRB_NO_METHOD_CACHE static void -mc_clear_by_class(mrb_state *mrb, struct RClass *c) +mc_clear(mrb_state *mrb) { - struct mrb_cache_entry *mc = mrb->cache; - int i; - - if (c->flags & MRB_FL_CLASS_IS_INHERITED) { - mc_clear_all(mrb); - c->flags &= ~MRB_FL_CLASS_IS_INHERITED; - return; - } - for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) { - if (mc[i].c == c) mc[i].c = 0; - } + memset(mrb->cache, 0, MRB_METHOD_CACHE_SIZE*sizeof(mrb->cache[0])); } -static void -mc_clear_by_id(mrb_state *mrb, struct RClass *c, mrb_sym mid) +void +mrb_mc_clear_by_class(mrb_state *mrb, struct RClass *c) { struct mrb_cache_entry *mc = mrb->cache; int i; if (c->flags & MRB_FL_CLASS_IS_INHERITED) { - mc_clear_all(mrb); - c->flags &= ~MRB_FL_CLASS_IS_INHERITED; + mc_clear(mrb); return; } for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) { - if (mc[i].c == c || mc[i].mid == mid) - mc[i].c = 0; + if (mc[i].c == c) mc[i].c = 0; } } #endif @@ -1321,10 +1746,9 @@ mc_clear_by_id(mrb_state *mrb, struct RClass *c, mrb_sym mid) MRB_API mrb_method_t mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) { - khiter_t k; mrb_method_t m; struct RClass *c = *cp; -#ifdef MRB_METHOD_CACHE +#ifndef MRB_NO_METHOD_CACHE struct RClass *oc = c; int h = kh_int_hash_func(mrb, ((intptr_t)oc) ^ mid) & (MRB_METHOD_CACHE_SIZE-1); struct mrb_cache_entry *mc = &mrb->cache[h]; @@ -1336,15 +1760,23 @@ mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) #endif while (c) { - khash_t(mt) *h = c->mt; + mt_tbl *h = c->mt; if (h) { - k = kh_get(mt, mrb, h, mid); - if (k != kh_end(h)) { - m = kh_value(h, k); - if (MRB_METHOD_UNDEF_P(m)) break; + struct mt_elem *e = mt_get(mrb, h, mid); + if (e) { + if (e->ptr.proc == 0) break; *cp = c; -#ifdef MRB_METHOD_CACHE + if (e->func_p) { + MRB_METHOD_FROM_FUNC(m, e->ptr.func); + } + else { + MRB_METHOD_FROM_PROC(m, e->ptr.proc); + } + if (e->noarg_p) { + MRB_METHOD_NOARG_SET(m); + } +#ifndef MRB_NO_METHOD_CACHE mc->c = oc; mc->c0 = c; mc->mid = mid; @@ -1366,48 +1798,78 @@ mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid) m = mrb_method_search_vm(mrb, &c, mid); if (MRB_METHOD_UNDEF_P(m)) { - mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0); - if (mrb_string_p(inspect) && RSTRING_LEN(inspect) > 64) { - inspect = mrb_any_to_s(mrb, mrb_obj_value(c)); - } - mrb_name_error(mrb, mid, "undefined method '%S' for class %S", - mrb_sym2str(mrb, mid), inspect); + mrb_name_error(mrb, mid, "undefined method '%n' for class %C", mid, c); } return m; } +#define ONSTACK_ALLOC_MAX 32 + +static mrb_sym +prepare_name_common(mrb_state *mrb, mrb_sym sym, const char *prefix, const char *suffix) +{ + char onstack[ONSTACK_ALLOC_MAX]; + mrb_int sym_len; + const char *sym_str = mrb_sym_name_len(mrb, sym, &sym_len); + size_t prefix_len = prefix ? strlen(prefix) : 0; + size_t suffix_len = suffix ? strlen(suffix) : 0; + size_t name_len = sym_len + prefix_len + suffix_len; + char *buf = name_len > sizeof(onstack) ? (char *)mrb_alloca(mrb, name_len) : onstack; + char *p = buf; + + if (prefix_len > 0) { + memcpy(p, prefix, prefix_len); + p += prefix_len; + } + + memcpy(p, sym_str, sym_len); + p += sym_len; + + if (suffix_len > 0) { + memcpy(p, suffix, suffix_len); + p += suffix_len; + } + + return mrb_intern(mrb, buf, name_len); +} + static mrb_value -attr_reader(mrb_state *mrb, mrb_value obj) +prepare_ivar_name(mrb_state *mrb, mrb_sym sym) { - mrb_value name = mrb_proc_cfunc_env_get(mrb, 0); - return mrb_iv_get(mrb, obj, to_sym(mrb, name)); + sym = prepare_name_common(mrb, sym, "@", NULL); + mrb_iv_name_sym_check(mrb, sym); + return mrb_symbol_value(sym); +} + +static mrb_sym +prepare_writer_name(mrb_state *mrb, mrb_sym sym) +{ + return prepare_name_common(mrb, sym, NULL, "="); } static mrb_value -mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod) +mod_attr_define(mrb_state *mrb, mrb_value mod, mrb_value (*accessor)(mrb_state *, mrb_value), mrb_sym (*access_name)(mrb_state *, mrb_sym)) { struct RClass *c = mrb_class_ptr(mod); - mrb_value *argv; + const mrb_value *argv; mrb_int argc, i; int ai; mrb_get_args(mrb, "*", &argv, &argc); ai = mrb_gc_arena_save(mrb); for (i=0; i<argc; i++) { - mrb_value name, str; - mrb_sym method, sym; + mrb_value name; + mrb_sym method; struct RProc *p; mrb_method_t m; method = to_sym(mrb, argv[i]); - name = mrb_sym2str(mrb, method); - str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1); - mrb_str_cat_lit(mrb, str, "@"); - mrb_str_cat_str(mrb, str, name); - sym = mrb_intern_str(mrb, str); - mrb_iv_name_sym_check(mrb, sym); - name = mrb_symbol_value(sym); - p = mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name); + name = prepare_ivar_name(mrb, method); + if (access_name) { + method = access_name(mrb, method); + } + + p = mrb_proc_new_cfunc_with_env(mrb, accessor, 1, &name); MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, c, method, m); mrb_gc_arena_restore(mrb, ai); @@ -1416,12 +1878,24 @@ mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod) } static mrb_value +attr_reader(mrb_state *mrb, mrb_value obj) +{ + mrb_value name = mrb_proc_cfunc_env_get(mrb, 0); + return mrb_iv_get(mrb, obj, to_sym(mrb, name)); +} + +static mrb_value +mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod) +{ + return mod_attr_define(mrb, mod, attr_reader, NULL); +} + +static mrb_value attr_writer(mrb_state *mrb, mrb_value obj) { mrb_value name = mrb_proc_cfunc_env_get(mrb, 0); - mrb_value val; + mrb_value val = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &val); mrb_iv_set(mrb, obj, to_sym(mrb, name), val); return val; } @@ -1429,42 +1903,7 @@ attr_writer(mrb_state *mrb, mrb_value obj) static mrb_value mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod) { - struct RClass *c = mrb_class_ptr(mod); - mrb_value *argv; - mrb_int argc, i; - int ai; - - mrb_get_args(mrb, "*", &argv, &argc); - ai = mrb_gc_arena_save(mrb); - for (i=0; i<argc; i++) { - mrb_value name, str, attr; - mrb_sym method, sym; - struct RProc *p; - mrb_method_t m; - - method = to_sym(mrb, argv[i]); - - /* prepare iv name (@name) */ - name = mrb_sym2str(mrb, method); - str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1); - mrb_str_cat_lit(mrb, str, "@"); - mrb_str_cat_str(mrb, str, name); - sym = mrb_intern_str(mrb, str); - mrb_iv_name_sym_check(mrb, sym); - attr = mrb_symbol_value(sym); - - /* prepare method name (name=) */ - str = mrb_str_new_capa(mrb, RSTRING_LEN(str)); - mrb_str_cat_str(mrb, str, name); - mrb_str_cat_lit(mrb, str, "="); - method = mrb_intern_str(mrb, str); - - p = mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr); - MRB_METHOD_FROM_PROC(m, p); - mrb_define_method_raw(mrb, c, method, m); - mrb_gc_arena_restore(mrb, ai); - } - return mrb_nil_value(); + return mod_attr_define(mrb, mod, attr_writer, prepare_writer_name); } static mrb_value @@ -1479,7 +1918,7 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv) if (ttype == 0) ttype = MRB_TT_OBJECT; if (ttype <= MRB_TT_CPTR) { - mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %S", cv); + mrb_raisef(mrb, E_TYPE_ERROR, "can't create instance of %v", cv); } o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c); return mrb_obj_value(o); @@ -1497,29 +1936,20 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv) * */ -MRB_API mrb_value +mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv) { mrb_value obj, blk; - mrb_value *argv; + const mrb_value *argv; mrb_int argc; mrb_sym init; - mrb_method_t m; - mrb_get_args(mrb, "*&", &argv, &argc, &blk); + mrb_get_args(mrb, "*!&", &argv, &argc, &blk); obj = mrb_instance_alloc(mrb, cv); - init = mrb_intern_lit(mrb, "initialize"); - m = mrb_method_search(mrb, mrb_class(mrb, obj), init); - if (MRB_METHOD_CFUNC_P(m)) { - mrb_func_t f = MRB_METHOD_CFUNC(m); - if (f != mrb_bob_init) { - f(mrb, obj); - } - } - else { + init = MRB_SYM(initialize); + if (!mrb_func_basic_p(mrb, obj, init, mrb_bob_init)) { mrb_funcall_with_block(mrb, obj, init, argc, argv, blk); } - return obj; } @@ -1530,7 +1960,7 @@ mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *arg mrb_sym mid; obj = mrb_instance_alloc(mrb, mrb_obj_value(c)); - mid = mrb_intern_lit(mrb, "initialize"); + mid = MRB_SYM(initialize); if (!mrb_func_basic_p(mrb, obj, mid, mrb_bob_init)) { mrb_funcall_argv(mrb, obj, mid, argc, argv); } @@ -1562,8 +1992,11 @@ mrb_class_new_class(mrb_state *mrb, mrb_value cv) super = mrb_obj_value(mrb->object_class); } new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super))); - mid = mrb_intern_lit(mrb, "initialize"); - if (!mrb_func_basic_p(mrb, new_class, mid, mrb_bob_init)) { + mid = MRB_SYM(initialize); + if (mrb_func_basic_p(mrb, new_class, mid, mrb_class_initialize)) { + mrb_class_initialize(mrb, new_class); + } + else { mrb_funcall_with_block(mrb, new_class, mid, n, &super, blk); } mrb_class_inherited(mrb, mrb_class_ptr(super), mrb_class_ptr(new_class)); @@ -1630,21 +2063,11 @@ mrb_bob_not(mrb_state *mrb, mrb_value cv) mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value self) { - mrb_value arg; + mrb_value arg = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &arg); return mrb_bool_value(mrb_obj_equal(mrb, self, arg)); } -static mrb_value -mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self) -{ - mrb_value arg; - - mrb_get_args(mrb, "o", &arg); - return mrb_bool_value(!mrb_equal(mrb, self, arg)); -} - MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid) { @@ -1667,7 +2090,7 @@ MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c) { mrb_value path; - mrb_sym nsym = mrb_intern_lit(mrb, "__classname__"); + mrb_sym nsym = MRB_SYM(__classname__); path = mrb_obj_iv_get(mrb, (struct RObject*)c, nsym); if (mrb_nil_p(path)) { @@ -1676,11 +2099,7 @@ mrb_class_path(mrb_state *mrb, struct RClass *c) } else if (mrb_symbol_p(path)) { /* toplevel class/module */ - const char *str; - mrb_int len; - - str = mrb_sym2name_len(mrb, mrb_symbol(path), &len); - return mrb_str_new(mrb, str, len); + return mrb_sym_str(mrb, mrb_symbol(path)); } return mrb_str_dup(mrb, path); } @@ -1699,14 +2118,11 @@ mrb_class_real(struct RClass* cl) MRB_API const char* mrb_class_name(mrb_state *mrb, struct RClass* c) { - mrb_value path = mrb_class_path(mrb, c); - if (mrb_nil_p(path)) { - path = c->tt == MRB_TT_MODULE ? mrb_str_new_lit(mrb, "#<Module:") : - mrb_str_new_lit(mrb, "#<Class:"); - mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, c)); - mrb_str_cat_lit(mrb, path, ">"); - } - return RSTRING_PTR(path); + mrb_value name; + + if (c == NULL) return NULL; + name = class_name_str(mrb, c); + return RSTRING_PTR(name); } MRB_API const char* @@ -1725,7 +2141,7 @@ static void mrb_check_inheritable(mrb_state *mrb, struct RClass *super) { if (super->tt != MRB_TT_CLASS) { - mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", mrb_obj_value(super)); + mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%C given)", super); } if (super->tt == MRB_TT_SCLASS) { mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of singleton class"); @@ -1764,7 +2180,7 @@ mrb_class_new(mrb_state *mrb, struct RClass *super) MRB_API struct RClass* mrb_module_new(mrb_state *mrb) { - struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class); + struct RClass *m = MRB_OBJ_ALLOC(mrb, MRB_TT_MODULE, mrb->module_class); boot_initmod(mrb, m); return m; } @@ -1779,7 +2195,7 @@ mrb_module_new(mrb_state *mrb) * called with an explicit receiver, as <code>class</code> is also a * reserved word in Ruby. * - * 1.class #=> Fixnum + * 1.class #=> Integer * self.class #=> Object */ @@ -1792,13 +2208,35 @@ mrb_obj_class(mrb_state *mrb, mrb_value obj) MRB_API void mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b) { + if (a == b) return; mrb_method_t m = mrb_method_search(mrb, c, b); + if (!MRB_METHOD_CFUNC_P(m)) { + struct RProc *p = MRB_METHOD_PROC(m); + + if (MRB_PROC_ENV_P(p)) { + MRB_PROC_ENV(p)->mid = b; + } + else if (p->color != MRB_GC_RED) { + struct RClass *tc = MRB_PROC_TARGET_CLASS(p); + struct REnv *e = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, NULL); + + e->mid = b; + if (tc) { + e->c = tc; + mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc); + } + p->e.env = e; + p->flags |= MRB_PROC_ENVSET; + mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e); + } + } mrb_define_method_raw(mrb, c, a, m); } /*! * Defines an alias of a method. + * \param mrb the mruby state * \param klass the class which the original method belongs to * \param name1 a new name for the method * \param name2 the original name of the method @@ -1809,6 +2247,12 @@ mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const mrb_alias_method(mrb, klass, mrb_intern_cstr(mrb, name1), mrb_intern_cstr(mrb, name2)); } +MRB_API void +mrb_define_alias_id(mrb_state *mrb, struct RClass *klass, mrb_sym a, mrb_sym b) +{ + mrb_alias_method(mrb, klass, a, b); +} + /* * call-seq: * mod.to_s -> string @@ -1818,15 +2262,12 @@ mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const * show information on the thing we're attached to as well. */ -static mrb_value +mrb_value mrb_mod_to_s(mrb_state *mrb, mrb_value klass) { - mrb_value str; - - if (mrb_type(klass) == MRB_TT_SCLASS) { - mrb_value v = mrb_iv_get(mrb, klass, mrb_intern_lit(mrb, "__attached__")); - - str = mrb_str_new_lit(mrb, "#<Class:"); + if (mrb_sclass_p(klass)) { + mrb_value v = mrb_iv_get(mrb, klass, MRB_SYM(__attached__)); + mrb_value str = mrb_str_new_lit(mrb, "#<Class:"); if (class_ptr_p(v)) { mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v)); @@ -1837,37 +2278,12 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass) return mrb_str_cat_lit(mrb, str, ">"); } else { - struct RClass *c; - mrb_value path; - - str = mrb_str_new_capa(mrb, 32); - c = mrb_class_ptr(klass); - path = mrb_class_path(mrb, c); - - if (mrb_nil_p(path)) { - switch (mrb_type(klass)) { - case MRB_TT_CLASS: - mrb_str_cat_lit(mrb, str, "#<Class:"); - break; - - case MRB_TT_MODULE: - mrb_str_cat_lit(mrb, str, "#<Module:"); - break; - - default: - /* Shouldn't be happened? */ - mrb_str_cat_lit(mrb, str, "#<??????:"); - break; - } - mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, c)); - return mrb_str_cat_lit(mrb, str, ">"); - } - else { - return path; - } + return class_name_str(mrb, mrb_class_ptr(klass)); } } +void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid); + static mrb_value mrb_mod_alias(mrb_state *mrb, mrb_value mod) { @@ -1876,27 +2292,38 @@ mrb_mod_alias(mrb_state *mrb, mrb_value mod) mrb_get_args(mrb, "nn", &new_name, &old_name); mrb_alias_method(mrb, c, new_name, old_name); - return mrb_nil_value(); + mrb_method_added(mrb, c, new_name); + return mod; } -void +static void +undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a) +{ + mrb_method_t m; + + MRB_METHOD_FROM_PROC(m, NULL); + mrb_define_method_raw(mrb, c, a, m); +} + +MRB_API void mrb_undef_method_id(mrb_state *mrb, struct RClass *c, mrb_sym a) { if (!mrb_obj_respond_to(mrb, c, a)) { - mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c)); - } - else { - mrb_method_t m; - - MRB_METHOD_FROM_PROC(m, NULL); - mrb_define_method_raw(mrb, c, a, m); + mrb_name_error(mrb, a, "undefined method '%n' for class '%C'", a, c); } + undef_method(mrb, c, a); } MRB_API void mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name) { - mrb_undef_method_id(mrb, c, mrb_intern_cstr(mrb, name)); + undef_method(mrb, c, mrb_intern_cstr(mrb, name)); +} + +MRB_API void +mrb_undef_class_method_id(mrb_state *mrb, struct RClass *c, mrb_sym name) +{ + mrb_undef_method_id(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name); } MRB_API void @@ -1905,12 +2332,24 @@ mrb_undef_class_method(mrb_state *mrb, struct RClass *c, const char *name) mrb_undef_method(mrb, mrb_class_ptr(mrb_singleton_class(mrb, mrb_obj_value(c))), name); } +MRB_API void +mrb_remove_method(mrb_state *mrb, struct RClass *c, mrb_sym mid) +{ + mt_tbl *h; + + MRB_CLASS_ORIGIN(c); + h = c->mt; + + if (h && mt_del(mrb, h, mid)) return; + mrb_name_error(mrb, mid, "method '%n' not defined in %C", mid, c); +} + static mrb_value mrb_mod_undef(mrb_state *mrb, mrb_value mod) { struct RClass *c = mrb_class_ptr(mod); mrb_int argc; - mrb_value *argv; + const mrb_value *argv; mrb_get_args(mrb, "*", &argv, &argc); while (argc--) { @@ -1921,17 +2360,13 @@ mrb_mod_undef(mrb_state *mrb, mrb_value mod) } static void -check_const_name_str(mrb_state *mrb, mrb_value str) -{ - if (RSTRING_LEN(str) < 1 || !ISUPPER(*RSTRING_PTR(str))) { - mrb_name_error(mrb, mrb_intern_str(mrb, str), "wrong constant name %S", str); - } -} - -static void check_const_name_sym(mrb_state *mrb, mrb_sym id) { - check_const_name_str(mrb, mrb_sym2str(mrb, id)); + mrb_int len; + const char *name = mrb_sym_name_len(mrb, id, &len); + if (!mrb_const_name_p(mrb, name, len)) { + mrb_name_error(mrb, id, "wrong constant name %n", id); + } } static mrb_value @@ -1958,13 +2393,11 @@ mrb_const_get_sym(mrb_state *mrb, mrb_value mod, mrb_sym id) static mrb_value mrb_mod_const_get(mrb_state *mrb, mrb_value mod) { - mrb_value path; + mrb_value path = mrb_get_arg1(mrb); mrb_sym id; char *ptr; mrb_int off, end, len; - mrb_get_args(mrb, "o", &path); - if (mrb_symbol_p(path)) { /* const get with symbol */ id = mrb_symbol(path); @@ -1972,7 +2405,7 @@ mrb_mod_const_get(mrb_state *mrb, mrb_value mod) } /* const get with class path string */ - path = mrb_string_type(mrb, path); + path = mrb_ensure_string_type(mrb, path); ptr = RSTRING_PTR(path); len = RSTRING_LEN(path); off = 0; @@ -1987,7 +2420,7 @@ mrb_mod_const_get(mrb_state *mrb, mrb_value mod) else { off = end + 2; if (off == len) { /* trailing "::" */ - mrb_name_error(mrb, id, "wrong constant name '%S'", path); + mrb_name_error(mrb, id, "wrong constant name '%v'", path); } } } @@ -2017,7 +2450,7 @@ mrb_mod_remove_const(mrb_state *mrb, mrb_value mod) check_const_name_sym(mrb, id); val = mrb_iv_remove(mrb, mod, id); if (mrb_undef_p(val)) { - mrb_name_error(mrb, id, "constant %S not defined", mrb_sym2str(mrb, id)); + mrb_name_error(mrb, id, "constant %n not defined", id); } return val; } @@ -2030,13 +2463,10 @@ mrb_mod_const_missing(mrb_state *mrb, mrb_value mod) mrb_get_args(mrb, "n", &sym); if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) { - mrb_name_error(mrb, sym, "uninitialized constant %S::%S", - mod, - mrb_sym2str(mrb, sym)); + mrb_name_error(mrb, sym, "uninitialized constant %v::%n", mod, sym); } else { - mrb_name_error(mrb, sym, "uninitialized constant %S", - mrb_sym2str(mrb, sym)); + mrb_name_error(mrb, sym, "uninitialized constant %n", sym); } /* not reached */ return mrb_nil_value(); @@ -2078,10 +2508,25 @@ mrb_mod_method_defined(mrb_state *mrb, mrb_value mod) return mrb_bool_value(mrb_obj_respond_to(mrb, mrb_class_ptr(mod), id)); } -static mrb_value -mod_define_method(mrb_state *mrb, mrb_value self) +void +mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid) +{ + mrb_sym added; + mrb_value recv = mrb_obj_value(c); + + if (c->tt == MRB_TT_SCLASS) { + added = MRB_SYM(singleton_method_added); + recv = mrb_iv_get(mrb, recv, MRB_SYM(__attached__)); + } + else { + added = MRB_SYM(method_added); + } + mrb_funcall_id(mrb, recv, added, 1, mrb_symbol_value(mid)); +} + +mrb_value +mrb_mod_define_method_m(mrb_state *mrb, struct RClass *c) { - struct RClass *c = mrb_class_ptr(self); struct RProc *p; mrb_method_t m; mrb_sym mid; @@ -2097,42 +2542,56 @@ mod_define_method(mrb_state *mrb, mrb_value self) /* ignored */ break; default: - mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc))); + mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %T (expected Proc)", proc); break; } if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } - p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); + p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class); mrb_proc_copy(p, mrb_proc_ptr(blk)); p->flags |= MRB_PROC_STRICT; MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb, c, mid, m); + mrb_method_added(mrb, c, mid); return mrb_symbol_value(mid); } static mrb_value +mod_define_method(mrb_state *mrb, mrb_value self) +{ + return mrb_mod_define_method_m(mrb, mrb_class_ptr(self)); +} + +static mrb_value top_define_method(mrb_state *mrb, mrb_value self) { - return mod_define_method(mrb, mrb_obj_value(mrb->object_class)); + return mrb_mod_define_method_m(mrb, mrb->object_class); } static mrb_value mrb_mod_eqq(mrb_state *mrb, mrb_value mod) { - mrb_value obj; + mrb_value obj = mrb_get_arg1(mrb); mrb_bool eqq; - mrb_get_args(mrb, "o", &obj); eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod)); return mrb_bool_value(eqq); } -MRB_API mrb_value +static mrb_value +mrb_mod_dup(mrb_state *mrb, mrb_value self) +{ + mrb_value mod = mrb_obj_clone(mrb, self); + MRB_UNSET_FROZEN_FLAG(mrb_obj_ptr(mod)); + return mod; +} + +static mrb_value mrb_mod_module_function(mrb_state *mrb, mrb_value mod) { - mrb_value *argv; + const mrb_value *argv; mrb_int argc, i; mrb_sym mid; mrb_method_t m; @@ -2166,6 +2625,202 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod) return mod; } +static struct RClass* +mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) +{ + struct RClass *klass = mrb_basic_ptr(obj)->c; + + if (klass->tt != MRB_TT_SCLASS) + return klass; + else { + /* copy singleton(unnamed) class */ + struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class); + + 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) { + mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); + mrb_obj_iv_set(mrb, (struct RObject*)clone, MRB_SYM(__attached__), obj); + } + if (klass->mt) { + clone->mt = mt_copy(mrb, klass->mt); + } + else { + clone->mt = mt_new(mrb); + } + clone->tt = MRB_TT_SCLASS; + return clone; + } +} + +static void +copy_class(mrb_state *mrb, mrb_value dst, mrb_value src) +{ + struct RClass *dc = mrb_class_ptr(dst); + struct RClass *sc = mrb_class_ptr(src); + /* if the origin is not the same as the class, then the origin and + the current class need to be copied */ + if (sc->flags & MRB_FL_CLASS_IS_PREPENDED) { + struct RClass *c0 = sc->super; + struct RClass *c1 = dc; + + /* copy prepended iclasses */ + while (!(c0->flags & MRB_FL_CLASS_IS_ORIGIN)) { + c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); + c1 = c1->super; + c0 = c0->super; + } + c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); + c1->super->flags |= MRB_FL_CLASS_IS_ORIGIN; + } + if (sc->mt) { + dc->mt = mt_copy(mrb, sc->mt); + } + else { + dc->mt = mt_new(mrb); + } + dc->super = sc->super; + MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc)); +} + +/* 15.3.1.3.16 */ +static mrb_value +mrb_obj_init_copy(mrb_state *mrb, mrb_value self) +{ + mrb_value orig = mrb_get_arg1(mrb); + + if (mrb_obj_equal(mrb, self, orig)) return self; + if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) { + mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object"); + } + return self; +} + +static void +init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj) +{ + switch (mrb_type(obj)) { + case MRB_TT_ICLASS: + copy_class(mrb, dest, obj); + return; + case MRB_TT_CLASS: + case MRB_TT_MODULE: + copy_class(mrb, dest, obj); + mrb_iv_copy(mrb, dest, obj); + mrb_iv_remove(mrb, dest, MRB_SYM(__classname__)); + break; + case MRB_TT_OBJECT: + case MRB_TT_SCLASS: + case MRB_TT_HASH: + case MRB_TT_DATA: + case MRB_TT_EXCEPTION: + mrb_iv_copy(mrb, dest, obj); + break; + case MRB_TT_ISTRUCT: + mrb_istruct_copy(dest, obj); + break; + + default: + break; + } + if (!mrb_func_basic_p(mrb, dest, MRB_SYM(initialize_copy), mrb_obj_init_copy)) { + mrb_funcall_id(mrb, dest, MRB_SYM(initialize_copy), 1, obj); + } +} + +/* 15.3.1.3.8 */ +/* + * call-seq: + * obj.clone -> an_object + * + * Produces a shallow copy of <i>obj</i>---the instance variables of + * <i>obj</i> are copied, but not the objects they reference. Copies + * the frozen state of <i>obj</i>. See also the discussion + * under <code>Object#dup</code>. + * + * class Klass + * attr_accessor :str + * end + * s1 = Klass.new #=> #<Klass:0x401b3a38> + * s1.str = "Hello" #=> "Hello" + * s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello"> + * s2.str[1,4] = "i" #=> "i" + * s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">" + * s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">" + * + * This method may have class-specific behavior. If so, that + * behavior will be documented under the #+initialize_copy+ method of + * the class. + * + * Some Class(True False Nil Symbol Integer Float) Object cannot clone. + */ +MRB_API mrb_value +mrb_obj_clone(mrb_state *mrb, mrb_value self) +{ + struct RObject *p; + mrb_value clone; + + if (mrb_immediate_p(self)) { + return self; + } + if (mrb_sclass_p(self)) { + mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class"); + } + 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); + p->flags |= mrb_obj_ptr(self)->flags & MRB_FL_OBJ_IS_FROZEN; + + return clone; +} + +/* 15.3.1.3.9 */ +/* + * call-seq: + * obj.dup -> an_object + * + * Produces a shallow copy of <i>obj</i>---the instance variables of + * <i>obj</i> are copied, but not the objects they reference. + * <code>dup</code> copies the frozen state of <i>obj</i>. See also + * the discussion under <code>Object#clone</code>. In general, + * <code>clone</code> and <code>dup</code> may have different semantics + * in descendant classes. While <code>clone</code> is used to duplicate + * an object, including its internal state, <code>dup</code> typically + * uses the class of the descendant object to create the new instance. + * + * This method may have class-specific behavior. If so, that + * behavior will be documented under the #+initialize_copy+ method of + * the class. + */ + +MRB_API mrb_value +mrb_obj_dup(mrb_state *mrb, mrb_value obj) +{ + struct RBasic *p; + mrb_value dup; + + if (mrb_immediate_p(obj)) { + return obj; + } + if (mrb_sclass_p(obj)) { + mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class"); + } + p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj)); + dup = mrb_obj_value(p); + init_copy(mrb, dup, obj); + + return dup; +} + /* implementation of __id__ */ mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self); /* implementation of instance_eval */ @@ -2177,6 +2832,43 @@ inspect_main(mrb_state *mrb, mrb_value mod) return mrb_str_new_lit(mrb, "main"); } +static const mrb_code new_iseq[] = { + OP_ENTER, 0x0, 0x10, 0x1, /* OP_ENTER 0:0:1:0:0:0:1 */ + OP_LOADSELF, 0x3, /* OP_LOADSELF R3 */ + OP_SEND, 0x3, 0x0, 0x0, /* OP_SEND R3 :allocate 0 */ + OP_MOVE, 0x0, 0x3, /* OP_MOVE R0 R3 */ + OP_MOVE, 0x4, 0x1, /* OP_MOVE R4 R1 */ + OP_MOVE, 0x5, 0x2, /* OP_MOVE R5 R2 */ + OP_SENDVB, 0x3, 0x1, /* OP_SENDVB R3 :initialize */ + OP_RETURN, 0x0 /* OP_RETURN R0 */ +}; + +MRB_PRESYM_DEFINE_VAR_AND_INITER(new_syms, 2, MRB_SYM(allocate), MRB_SYM(initialize)) + +static const mrb_irep new_irep = { + 3, 6, 0, MRB_IREP_STATIC, + new_iseq, NULL, new_syms, NULL, NULL, NULL, + sizeof(new_iseq), 0, 2, 0, 0, +}; + +static const struct RProc new_proc = { + NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_FL_OBJ_IS_FROZEN | MRB_PROC_SCOPE | MRB_PROC_STRICT, + { &new_irep }, NULL, { NULL } +}; + +static void +init_class_new(mrb_state *mrb, struct RClass *cls) +{ + mrb_method_t m; + + MRB_PRESYM_INIT_SYMBOLS(mrb, new_syms); + MRB_METHOD_FROM_PROC(m, &new_proc); + mrb_define_method_raw(mrb, cls, MRB_SYM(new), m); +} + +/* implementation of #send method */ +mrb_value mrb_f_send(mrb_state *mrb, mrb_value self); + void mrb_init_class(mrb_state *mrb) { @@ -2198,17 +2890,16 @@ mrb_init_class(mrb_state *mrb) make_metaclass(mrb, cls); /* name basic classes */ - mrb_define_const(mrb, bob, "BasicObject", mrb_obj_value(bob)); - mrb_define_const(mrb, obj, "BasicObject", mrb_obj_value(bob)); - mrb_define_const(mrb, obj, "Object", mrb_obj_value(obj)); - mrb_define_const(mrb, obj, "Module", mrb_obj_value(mod)); - mrb_define_const(mrb, obj, "Class", mrb_obj_value(cls)); + mrb_define_const_id(mrb, bob, MRB_SYM(BasicObject), mrb_obj_value(bob)); + mrb_define_const_id(mrb, obj, MRB_SYM(Object), mrb_obj_value(obj)); + mrb_define_const_id(mrb, obj, MRB_SYM(Module), mrb_obj_value(mod)); + mrb_define_const_id(mrb, obj, MRB_SYM(Class), mrb_obj_value(cls)); /* name each classes */ - mrb_class_name_class(mrb, NULL, bob, mrb_intern_lit(mrb, "BasicObject")); - mrb_class_name_class(mrb, NULL, obj, mrb_intern_lit(mrb, "Object")); /* 15.2.1 */ - mrb_class_name_class(mrb, NULL, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */ - mrb_class_name_class(mrb, NULL, cls, mrb_intern_lit(mrb, "Class")); /* 15.2.3 */ + mrb_class_name_class(mrb, NULL, bob, MRB_SYM(BasicObject)); + mrb_class_name_class(mrb, NULL, obj, MRB_SYM(Object)); /* 15.2.1 */ + mrb_class_name_class(mrb, NULL, mod, MRB_SYM(Module)); /* 15.2.2 */ + mrb_class_name_class(mrb, NULL, cls, MRB_SYM(Class)); /* 15.2.3 */ mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); /* 15.2.17 */ MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC); @@ -2217,17 +2908,20 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, bob, "initialize", mrb_bob_init, MRB_ARGS_NONE()); mrb_define_method(mrb, bob, "!", mrb_bob_not, MRB_ARGS_NONE()); mrb_define_method(mrb, bob, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */ - mrb_define_method(mrb, bob, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1)); mrb_define_method(mrb, bob, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.4 */ - mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.5 */ - mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */ + mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_REQ(1)|MRB_ARGS_REST()|MRB_ARGS_BLOCK()); /* 15.3.1.3.5 */ + mrb_define_method(mrb, bob, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */ + mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.3.1.3.18 */ + mrb_define_method(mrb, bob, "singleton_method_added", mrb_bob_init, MRB_ARGS_REQ(1)); - mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)); + mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); + mrb_define_method(mrb, cls, "allocate", mrb_instance_alloc, MRB_ARGS_NONE()); mrb_define_method(mrb, cls, "superclass", mrb_class_superclass, MRB_ARGS_NONE()); /* 15.2.3.3.4 */ - mrb_define_method(mrb, cls, "new", mrb_instance_new, MRB_ARGS_ANY()); /* 15.2.3.3.3 */ mrb_define_method(mrb, cls, "initialize", mrb_class_initialize, MRB_ARGS_OPT(1)); /* 15.2.3.3.1 */ mrb_define_method(mrb, cls, "inherited", mrb_bob_init, MRB_ARGS_REQ(1)); + init_class_new(mrb, cls); + MRB_SET_INSTANCE_TT(mod, MRB_TT_MODULE); mrb_define_method(mrb, mod, "extend_object", mrb_mod_extend_object, MRB_ARGS_REQ(1)); /* 15.2.2.4.25 */ mrb_define_method(mrb, mod, "extended", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.26 */ @@ -2257,12 +2951,16 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, mod, "const_missing", mrb_mod_const_missing, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "method_defined?", mrb_mod_method_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.34 */ mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_ARG(1,1)); - mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); /* 15.2.2.4.7 */ + mrb_define_method(mrb, mod, "dup", mrb_mod_dup, MRB_ARGS_NONE()); + mrb_define_method(mrb, bob, "method_added", mrb_bob_init, MRB_ARGS_REQ(1)); mrb_undef_method(mrb, cls, "append_features"); + mrb_undef_method(mrb, cls, "prepend_features"); mrb_undef_method(mrb, cls, "extend_object"); + mrb_undef_method(mrb, cls, "module_function"); - mrb->top_self = (struct RObject*)mrb_obj_alloc(mrb, MRB_TT_OBJECT, mrb->object_class); + mrb->top_self = MRB_OBJ_ALLOC(mrb, MRB_TT_OBJECT, mrb->object_class); mrb_define_singleton_method(mrb, mrb->top_self, "inspect", inspect_main, MRB_ARGS_NONE()); mrb_define_singleton_method(mrb, mrb->top_self, "to_s", inspect_main, MRB_ARGS_NONE()); mrb_define_singleton_method(mrb, mrb->top_self, "define_method", top_define_method, MRB_ARGS_ARG(1,1)); diff --git a/src/codedump.c b/src/codedump.c index 30a14f937..4d1d96171 100644 --- a/src/codedump.c +++ b/src/codedump.c @@ -4,25 +4,20 @@ #include <mruby/opcode.h> #include <mruby/string.h> #include <mruby/proc.h> +#include <mruby/dump.h> -#ifndef MRB_DISABLE_STDIO +#ifndef MRB_NO_STDIO static void -print_r(mrb_state *mrb, mrb_irep *irep, size_t n) +print_r(mrb_state *mrb, const mrb_irep *irep, size_t n) { - size_t i; - if (n == 0) return; - - for (i=0; i+1<irep->nlocals; i++) { - if (irep->lv[i].r == n) { - mrb_sym sym = irep->lv[i].name; - printf(" R%d:%s", (int)n, mrb_sym2name(mrb, sym)); - } - } + if (n >= irep->nlocals) return; + if (!irep->lv[n-1]) return; + printf(" R%d:%s", (int)n, mrb_sym_dump(mrb, irep->lv[n-1])); } static void -print_lv_a(mrb_state *mrb, mrb_irep *irep, uint16_t a) +print_lv_a(mrb_state *mrb, const mrb_irep *irep, uint16_t a) { if (!irep->lv || a >= irep->nlocals || a == 0) { printf("\n"); @@ -34,7 +29,7 @@ print_lv_a(mrb_state *mrb, mrb_irep *irep, uint16_t a) } static void -print_lv_ab(mrb_state *mrb, mrb_irep *irep, uint16_t a, uint16_t b) +print_lv_ab(mrb_state *mrb, const mrb_irep *irep, uint16_t a, uint16_t b) { if (!irep->lv || (a >= irep->nlocals && b >= irep->nlocals) || a+b == 0) { printf("\n"); @@ -47,11 +42,11 @@ print_lv_ab(mrb_state *mrb, mrb_irep *irep, uint16_t a, uint16_t b) } static void -print_header(mrb_irep *irep, ptrdiff_t i) +print_header(mrb_state *mrb, const mrb_irep *irep, uint32_t i) { int32_t line; - line = mrb_debug_get_line(irep, i); + line = mrb_debug_get_line(mrb, irep, i); if (line < 0) { printf(" "); } @@ -65,24 +60,52 @@ print_header(mrb_irep *irep, ptrdiff_t i) #define CASE(insn,ops) case insn: FETCH_ ## ops (); L_ ## insn static void -codedump(mrb_state *mrb, mrb_irep *irep) +codedump(mrb_state *mrb, const mrb_irep *irep) { int ai; - mrb_code *pc, *pcend; + const mrb_code *pc, *pcend; mrb_code ins; const char *file = NULL, *next_file; if (!irep) return; - printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", (void*)irep, - irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen); + printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d iseq=%d\n", (void*)irep, + irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen, (int)irep->ilen); if (irep->lv) { int i; printf("local variable names:\n"); for (i = 1; i < irep->nlocals; ++i) { - char const *n = mrb_sym2name(mrb, irep->lv[i - 1].name); - printf(" R%d:%s\n", irep->lv[i - 1].r, n? n : ""); + char const *s = mrb_sym_dump(mrb, irep->lv[i - 1]); + printf(" R%d:%s\n", i, s ? s : ""); + } + } + + if (irep->clen > 0) { + int i = irep->clen; + const struct mrb_irep_catch_handler *e = mrb_irep_catch_handler_table(irep); + + for (; i > 0; i --, e ++) { + uint32_t begin = mrb_irep_catch_handler_unpack(e->begin); + uint32_t end = mrb_irep_catch_handler_unpack(e->end); + uint32_t target = mrb_irep_catch_handler_unpack(e->target); + char buf[20]; + const char *type; + + switch (e->type) { + case MRB_CATCH_RESCUE: + type = "rescue"; + break; + case MRB_CATCH_ENSURE: + type = "ensure"; + break; + default: + buf[0] = '\0'; + snprintf(buf, sizeof(buf), "0x%02x <unknown>", (int)e->type); + type = buf; + break; + } + printf("catch type: %-8s begin: %04" PRIu32 " end: %04" PRIu32 " target: %04" PRIu32 "\n", type, begin, end, target); } } @@ -92,17 +115,17 @@ codedump(mrb_state *mrb, mrb_irep *irep) ptrdiff_t i; uint32_t a; uint16_t b; - uint8_t c; + uint16_t c; ai = mrb_gc_arena_save(mrb); i = pc - irep->iseq; - next_file = mrb_debug_get_filename(irep, i); + next_file = mrb_debug_get_filename(mrb, irep, (uint32_t)i); if (next_file && file != next_file) { printf("file: %s\n", next_file); file = next_file; } - print_header(irep, i); + print_header(mrb, irep, (uint32_t)i); ins = READ_B(); switch (ins) { CASE(OP_NOP, Z): @@ -112,11 +135,25 @@ codedump(mrb_state *mrb, mrb_irep *irep) printf("OP_MOVE\tR%d\tR%d\t", a, b); print_lv_ab(mrb, irep, a, b); break; + CASE(OP_LOADL, BB): - { - mrb_value v = irep->pool[b]; - mrb_value s = mrb_inspect(mrb, v); - printf("OP_LOADL\tR%d\tL(%d)\t; %s", a, b, RSTRING_PTR(s)); + switch (irep->pool[b].tt) { +#ifndef MRB_NO_FLOAT + case IREP_TT_FLOAT: + printf("OP_LOADL\tR%d\tL(%d)\t; %f", a, b, (double)irep->pool[b].u.f); + break; +#endif + case IREP_TT_INT32: + printf("OP_LOADL\tR%d\tL(%d)\t; %" PRId32, a, b, irep->pool[b].u.i32); + break; +#ifdef MRB_64BIT + case IREP_TT_INT64: + printf("OP_LOADL\tR%d\tL(%d)\t; %" PRId64, a, b, irep->pool[b].u.i64); + break; +#endif + default: + printf("OP_LOADL\tR%d\tL(%d)\t", a, b); + break; } print_lv_a(mrb, irep, a); break; @@ -128,6 +165,14 @@ codedump(mrb_state *mrb, mrb_irep *irep) printf("OP_LOADI\tR%d\t-%d\t", a, b); print_lv_a(mrb, irep, a); break; + CASE(OP_LOADI16, BS): + printf("OP_LOADI16\tR%d\t%d\t", a, (int)(int16_t)b); + print_lv_a(mrb, irep, a); + break; + CASE(OP_LOADI32, BSS): + printf("OP_LOADI32\tR%d\t%d\t", a, (int32_t)(((uint32_t)b<<16)+c)); + print_lv_a(mrb, irep, a); + break; CASE(OP_LOADI__1, B): printf("OP_LOADI__1\tR%d\t\t", a); print_lv_a(mrb, irep, a); @@ -145,7 +190,7 @@ codedump(mrb_state *mrb, mrb_irep *irep) print_lv_a(mrb, irep, a); break; CASE(OP_LOADSYM, BB): - printf("OP_LOADSYM\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_LOADSYM\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_LOADNIL, B): @@ -165,43 +210,43 @@ codedump(mrb_state *mrb, mrb_irep *irep) print_lv_a(mrb, irep, a); break; CASE(OP_GETGV, BB): - printf("OP_GETGV\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_GETGV\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETGV, BB): - printf("OP_SETGV\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a); + printf("OP_SETGV\t:%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_GETSV, BB): - printf("OP_GETSV\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_GETSV\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETSV, BB): - printf("OP_SETSV\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a); + printf("OP_SETSV\t:%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_GETCONST, BB): - printf("OP_GETCONST\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_GETCONST\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETCONST, BB): - printf("OP_SETCONST\t:%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a); + printf("OP_SETCONST\t:%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_GETMCNST, BB): - printf("OP_GETMCNST\tR%d\tR%d::%s", a, a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_GETMCNST\tR%d\tR%d::%s", a, a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETMCNST, BB): - printf("OP_SETMCNST\tR%d::%s\tR%d", a+1, mrb_sym2name(mrb, irep->syms[b]), a); + printf("OP_SETMCNST\tR%d::%s\tR%d", a+1, mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_GETIV, BB): - printf("OP_GETIV\tR%d\t%s", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_GETIV\tR%d\t%s", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETIV, BB): - printf("OP_SETIV\t%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a); + printf("OP_SETIV\t%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_GETUPVAR, BBB): @@ -213,39 +258,50 @@ codedump(mrb_state *mrb, mrb_irep *irep) print_lv_a(mrb, irep, a); break; CASE(OP_GETCV, BB): - printf("OP_GETCV\tR%d\t%s", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_GETCV\tR%d\t%s", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_SETCV, BB): - printf("OP_SETCV\t%s\tR%d", mrb_sym2name(mrb, irep->syms[b]), a); + printf("OP_SETCV\t%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a); print_lv_a(mrb, irep, a); break; CASE(OP_JMP, S): - printf("OP_JMP\t\t%03d\n", a); + i = pc - irep->iseq; + printf("OP_JMP\t\t%03d\n", (int)i+(int16_t)a); + break; + CASE(OP_JMPUW, S): + i = pc - irep->iseq; + printf("OP_JMPUW\t\t%03d\n", (int)i+(int16_t)a); break; CASE(OP_JMPIF, BS): - printf("OP_JMPIF\tR%d\t%03d\t", a, b); + i = pc - irep->iseq; + printf("OP_JMPIF\tR%d\t%03d\t", a, (int)i+(int16_t)b); print_lv_a(mrb, irep, a); break; CASE(OP_JMPNOT, BS): - printf("OP_JMPNOT\tR%d\t%03d\t", a, b); + i = pc - irep->iseq; + printf("OP_JMPNOT\tR%d\t%03d\t", a, (int)i+(int16_t)b); print_lv_a(mrb, irep, a); break; CASE(OP_JMPNIL, BS): - printf("OP_JMPNIL\tR%d\t%03d\t", a, b); + i = pc - irep->iseq; + printf("OP_JMPNIL\tR%d\t%03d\t", a, (int)i+(int16_t)b); print_lv_a(mrb, irep, a); break; CASE(OP_SENDV, BB): - printf("OP_SENDV\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_SENDV\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b])); break; CASE(OP_SENDVB, BB): - printf("OP_SENDVB\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_SENDVB\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b])); break; CASE(OP_SEND, BBB): - printf("OP_SEND\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c); + printf("OP_SEND\tR%d\t:%s\t%d\n", a, mrb_sym_dump(mrb, irep->syms[b]), c); break; CASE(OP_SENDB, BBB): - printf("OP_SENDB\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c); + printf("OP_SENDB\tR%d\t:%s\t%d\n", a, mrb_sym_dump(mrb, irep->syms[b]), c); + break; + CASE(OP_SENDVK, BB): + printf("OP_SENDVK\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b])); break; CASE(OP_CALL, Z): printf("OP_CALL\n"); @@ -264,23 +320,23 @@ codedump(mrb_state *mrb, mrb_irep *irep) break; CASE(OP_ENTER, W): printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n", - (a>>18)&0x1f, - (a>>13)&0x1f, - (a>>12)&0x1, - (a>>7)&0x1f, - (a>>2)&0x1f, - (a>>1)&0x1, - a & 0x1); + MRB_ASPEC_REQ(a), + MRB_ASPEC_OPT(a), + MRB_ASPEC_REST(a), + MRB_ASPEC_POST(a), + MRB_ASPEC_KEY(a), + MRB_ASPEC_KDICT(a), + MRB_ASPEC_BLOCK(a)); break; CASE(OP_KEY_P, BB): - printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_KEYEND, Z): printf("OP_KEYEND\n"); break; CASE(OP_KARG, BB): - printf("OP_KARG\tR%d\t:%s\t", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_KARG\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_RETURN, B): @@ -305,13 +361,13 @@ codedump(mrb_state *mrb, mrb_irep *irep) print_lv_a(mrb, irep, a); break; CASE(OP_LAMBDA, BB): - printf("OP_LAMBDA\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]); + printf("OP_LAMBDA\tR%d\tI(%d:%p)\n", a, b, (void*)irep->reps[b]); break; CASE(OP_BLOCK, BB): - printf("OP_BLOCK\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]); + printf("OP_BLOCK\tR%d\tI(%d:%p)\n", a, b, (void*)irep->reps[b]); break; CASE(OP_METHOD, BB): - printf("OP_METHOD\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]); + printf("OP_METHOD\tR%d\tI(%d:%p)\n", a, b, (void*)irep->reps[b]); break; CASE(OP_RANGE_INC, B): printf("OP_RANGE_INC\tR%d\n", a); @@ -320,46 +376,46 @@ codedump(mrb_state *mrb, mrb_irep *irep) printf("OP_RANGE_EXC\tR%d\n", a); break; CASE(OP_DEF, BB): - printf("OP_DEF\tR%d\t:%s\n", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_DEF\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b])); break; CASE(OP_UNDEF, B): - printf("OP_UNDEF\t:%s\n", mrb_sym2name(mrb, irep->syms[a])); + printf("OP_UNDEF\t:%s\n", mrb_sym_dump(mrb, irep->syms[a])); break; CASE(OP_ALIAS, BB): - printf("OP_ALIAS\t:%s\t%s\n", mrb_sym2name(mrb, irep->syms[a]), mrb_sym2name(mrb, irep->syms[b])); + printf("OP_ALIAS\t:%s\t%s\n", mrb_sym_dump(mrb, irep->syms[a]), mrb_sym_dump(mrb, irep->syms[b])); break; - CASE(OP_ADD, BB): - printf("OP_ADD\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b])); + CASE(OP_ADD, B): + printf("OP_ADD\tR%d\tR%d\n", a, a+1); break; - CASE(OP_ADDI, BBB): - printf("OP_ADDI\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c); + CASE(OP_ADDI, BB): + printf("OP_ADDI\tR%d\t%d\n", a, b); break; - CASE(OP_SUB, BB): - printf("OP_SUB\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b])); + CASE(OP_SUB, B): + printf("OP_SUB\tR%d\tR%d\n", a, a+1); break; - CASE(OP_SUBI, BBB): - printf("OP_SUBI\tR%d\t:%s\t%d\n", a, mrb_sym2name(mrb, irep->syms[b]), c); + CASE(OP_SUBI, BB): + printf("OP_SUBI\tR%d\t%d\n", a, b); break; - CASE(OP_MUL, BB): - printf("OP_MUL\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b])); + CASE(OP_MUL, B): + printf("OP_MUL\tR%d\tR%d\n", a, a+1); break; - CASE(OP_DIV, BB): - printf("OP_DIV\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b])); + CASE(OP_DIV, B): + printf("OP_DIV\tR%d\tR%d\n", a, a+1); break; - CASE(OP_LT, BB): - printf("OP_LT\t\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b])); + CASE(OP_LT, B): + printf("OP_LT\t\tR%d\tR%d\n", a, a+1); break; - CASE(OP_LE, BB): - printf("OP_LE\t\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b])); + CASE(OP_LE, B): + printf("OP_LE\t\tR%d\tR%d\n", a, a+1); break; - CASE(OP_GT, BB): - printf("OP_GT\t\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b])); + CASE(OP_GT, B): + printf("OP_GT\t\tR%d\tR%d\n", a, a+1); break; - CASE(OP_GE, BB): - printf("OP_GE\t\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b])); + CASE(OP_GE, B): + printf("OP_GE\t\tR%d\tR%d\n", a, a+1); break; - CASE(OP_EQ, BB): - printf("OP_EQ\t\tR%d\t:%s\t\n", a, mrb_sym2name(mrb, irep->syms[b])); + CASE(OP_EQ, B): + printf("OP_EQ\t\tR%d\tR%d\n", a, a+1); break; CASE(OP_ARRAY, BB): printf("OP_ARRAY\tR%d\t%d\t", a, b); @@ -398,10 +454,11 @@ codedump(mrb_state *mrb, mrb_irep *irep) print_lv_a(mrb, irep, a); break; CASE(OP_STRING, BB): - { - mrb_value v = irep->pool[b]; - mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); - printf("OP_STRING\tR%d\tL(%d)\t; %s", a, b, RSTRING_PTR(s)); + if ((irep->pool[b].tt & IREP_TT_NFLAG) == 0) { + printf("OP_STRING\tR%d\tL(%d)\t; %s", a, b, irep->pool[b].u.str); + } + else { + printf("OP_STRING\tR%d\tL(%d)\t", a, b); } print_lv_a(mrb, irep, a); break; @@ -427,15 +484,15 @@ codedump(mrb_state *mrb, mrb_irep *irep) print_lv_a(mrb, irep, a); break; CASE(OP_CLASS, BB): - printf("OP_CLASS\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_CLASS\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_MODULE, BB): - printf("OP_MODULE\tR%d\t:%s", a, mrb_sym2name(mrb, irep->syms[b])); + printf("OP_MODULE\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b])); print_lv_a(mrb, irep, a); break; CASE(OP_EXEC, BB): - printf("OP_EXEC\tR%d\tI(%d:%p)", a, b, irep->reps[b]); + printf("OP_EXEC\tR%d\tI(%d:%p)", a, b, (void*)irep->reps[b]); print_lv_a(mrb, irep, a); break; CASE(OP_SCLASS, B): @@ -447,17 +504,12 @@ codedump(mrb_state *mrb, mrb_irep *irep) print_lv_a(mrb, irep, a); break; CASE(OP_ERR, B): - { - mrb_value v = irep->pool[a]; - mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); - printf("OP_ERR\t%s\n", RSTRING_PTR(s)); + if ((irep->pool[a].tt & IREP_TT_NFLAG) == 0) { + printf("OP_ERR\t%s\n", irep->pool[a].u.str); + } + else { + printf("OP_ERR\tL(%d)\n", a); } - break; - CASE(OP_EPUSH, B): - printf("OP_EPUSH\t\t:I(%d:%p)\n", a, irep->reps[a]); - break; - CASE(OP_ONERR, S): - printf("OP_ONERR\t%03d\n", a); break; CASE(OP_EXCEPT, B): printf("OP_EXCEPT\tR%d\t\t", a); @@ -467,16 +519,10 @@ codedump(mrb_state *mrb, mrb_irep *irep) printf("OP_RESCUE\tR%d\tR%d", a, b); print_lv_ab(mrb, irep, a, b); break; - CASE(OP_RAISE, B): - printf("OP_RAISE\tR%d\t\t", a); + CASE(OP_RAISEIF, B): + printf("OP_RAISEIF\tR%d\t\t", a); print_lv_a(mrb, irep, a); break; - CASE(OP_POPERR, B): - printf("OP_POPERR\t%d\t\t\n", a); - break; - CASE(OP_EPOP, B): - printf("OP_EPOP\t%d\n", a); - break; CASE(OP_DEBUG, BBB): printf("OP_DEBUG\t%d\t%d\t%d\n", a, b, c); @@ -489,7 +535,7 @@ codedump(mrb_state *mrb, mrb_irep *irep) CASE(OP_EXT1, Z): ins = READ_B(); printf("OP_EXT1\n"); - print_header(irep, pc-irep->iseq-2); + print_header(mrb, irep, pc-irep->iseq-2); switch (ins) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); goto L_OP_ ## i; #include "mruby/ops.h" @@ -499,7 +545,7 @@ codedump(mrb_state *mrb, mrb_irep *irep) CASE(OP_EXT2, Z): ins = READ_B(); printf("OP_EXT2\n"); - print_header(irep, pc-irep->iseq-2); + print_header(mrb, irep, pc-irep->iseq-2); switch (ins) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); goto L_OP_ ## i; #include "mruby/ops.h" @@ -509,7 +555,7 @@ codedump(mrb_state *mrb, mrb_irep *irep) CASE(OP_EXT3, Z): ins = READ_B(); printf("OP_EXT3\n"); - print_header(irep, pc-irep->iseq-2); + print_header(mrb, irep, pc-irep->iseq-2); switch (ins) { #define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); goto L_OP_ ## i; #include "mruby/ops.h" @@ -527,13 +573,15 @@ codedump(mrb_state *mrb, mrb_irep *irep) } static void -codedump_recur(mrb_state *mrb, mrb_irep *irep) +codedump_recur(mrb_state *mrb, const mrb_irep *irep) { int i; codedump(mrb, irep); - for (i=0; i<irep->rlen; i++) { - codedump_recur(mrb, irep->reps[i]); + if (irep->reps) { + for (i=0; i<irep->rlen; i++) { + codedump_recur(mrb, irep->reps[i]); + } } } #endif @@ -541,7 +589,7 @@ codedump_recur(mrb_state *mrb, mrb_irep *irep) void mrb_codedump_all(mrb_state *mrb, struct RProc *proc) { -#ifndef MRB_DISABLE_STDIO +#ifndef MRB_NO_STDIO codedump_recur(mrb, proc->body.irep); #endif } diff --git a/src/crc.c b/src/crc.c deleted file mode 100644 index 290b2ca0e..000000000 --- a/src/crc.c +++ /dev/null @@ -1,39 +0,0 @@ -/* -** crc.c - calculate CRC -** -** See Copyright Notice in mruby.h -*/ - -#include <limits.h> -#include <stdint.h> -#include <stddef.h> - -/* Calculate CRC (CRC-16-CCITT) -** -** 0000_0000_0000_0000_0000_0000_0000_0000 -** ^|------- CRC -------|- work --| -** carry -*/ -#define CRC_16_CCITT 0x11021ul /* x^16+x^12+x^5+1 */ -#define CRC_XOR_PATTERN (CRC_16_CCITT << 8) -#define CRC_CARRY_BIT (0x01000000) - -uint16_t -calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc) -{ - size_t ibyte; - uint32_t ibit; - uint32_t crcwk = crc << 8; - - for (ibyte = 0; ibyte < nbytes; ibyte++) { - crcwk |= *src++; - for (ibit = 0; ibit < CHAR_BIT; ibit++) { - crcwk <<= 1; - if (crcwk & CRC_CARRY_BIT) { - crcwk ^= CRC_XOR_PATTERN; - } - } - } - return (uint16_t)(crcwk >> 8); -} - diff --git a/src/debug.c b/src/debug.c index e55f11d4f..203dcf981 100644 --- a/src/debug.c +++ b/src/debug.c @@ -35,43 +35,96 @@ get_file(mrb_irep_debug_info *info, uint32_t pc) return *ret; } +static size_t +packed_int_len(uint32_t num) +{ + size_t llen = 0; + + do { + llen++; + } while (num >>= 7); + return llen; +} + +static size_t +packed_int_encode(uint32_t num, uint8_t *p, uint8_t *pend) +{ + size_t llen = 0; + + do { + uint8_t byte = num & 0x7f; + num >>= 7; + if (num != 0) byte |= 0x80; + if (p < pend) *p++ = byte; + llen++; + } while (num != 0); + + return llen; +} + +static uint32_t +packed_int_decode(uint8_t *p, uint8_t **newpos) +{ + size_t i = 0, shift = 0; + uint32_t n = 0; + + do { + n |= ((uint32_t)(p[i] & 0x7f)) << shift; + i++; + shift += 7; + } while (shift < sizeof(uint32_t) * 8 && (p[i - 1] & 0x80)); + if (newpos) *newpos = p + i; + return n; +} + static mrb_debug_line_type select_line_type(const uint16_t *lines, size_t lines_len) { size_t line_count = 0; - int prev_line = -1; + size_t packed_map_len = 0; + uint32_t prev_line = 0; + uint32_t prev_pc = 0; size_t i; for (i = 0; i < lines_len; ++i) { if (lines[i] != prev_line) { + packed_map_len += packed_int_len(lines[i]-prev_line); + prev_line = lines[i]; + packed_map_len += packed_int_len(i-prev_pc); + prev_pc = i; ++line_count; } } - return (sizeof(uint16_t) * lines_len) <= (sizeof(mrb_irep_debug_info_line) * line_count) - ? mrb_debug_line_ary : mrb_debug_line_flat_map; + size_t line_ary_len = sizeof(uint16_t) * lines_len; + size_t flat_map_len = sizeof(mrb_irep_debug_info_line) * line_count; + if (line_ary_len < flat_map_len && line_ary_len < packed_map_len) { + return mrb_debug_line_ary; + } + if (flat_map_len < packed_map_len) return mrb_debug_line_flat_map; + return mrb_debug_line_packed_map; } MRB_API char const* -mrb_debug_get_filename(mrb_irep *irep, ptrdiff_t pc) +mrb_debug_get_filename(mrb_state *mrb, const mrb_irep *irep, uint32_t pc) { - if (irep && pc >= 0 && pc < irep->ilen) { + if (irep && pc < irep->ilen) { mrb_irep_debug_info_file* f = NULL; - if (!irep->debug_info) { return irep->filename; } - else if ((f = get_file(irep->debug_info, (uint32_t)pc))) { - return f->filename; + if (!irep->debug_info) return NULL; + else if ((f = get_file(irep->debug_info, pc))) { + return mrb_sym_name_len(mrb, f->filename_sym, NULL); } } return NULL; } MRB_API int32_t -mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc) +mrb_debug_get_line(mrb_state *mrb, const mrb_irep *irep, uint32_t pc) { - if (irep && pc >= 0 && pc < irep->ilen) { + if (irep && pc < irep->ilen) { mrb_irep_debug_info_file* f = NULL; if (!irep->debug_info) { - return irep->lines? irep->lines[pc] : -1; + return -1; } - else if ((f = get_file(irep->debug_info, (uint32_t)pc))) { + else if ((f = get_file(irep->debug_info, pc))) { switch (f->line_type) { case mrb_debug_line_ary: mrb_assert(f->start_pos <= pc && pc < (f->start_pos + f->line_entry_count)); @@ -102,6 +155,19 @@ mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc) return ret->line; } + + case mrb_debug_line_packed_map: { + uint8_t *p = f->lines.packed_map; + uint8_t *pend = p + f->line_entry_count; + uint32_t pos = 0, line = 0, line_diff; + while (p < pend) { + pos += packed_int_decode(p, &p); + line_diff = packed_int_decode(p, &p); + if (pc < pos) break; + line += line_diff; + } + return line; + } } } } @@ -122,82 +188,111 @@ mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep) } MRB_API mrb_irep_debug_info_file* -mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep, +mrb_debug_info_append_file(mrb_state *mrb, mrb_irep_debug_info *d, + const char *filename, uint16_t *lines, uint32_t start_pos, uint32_t end_pos) { - mrb_irep_debug_info *info; - mrb_irep_debug_info_file *ret; + mrb_irep_debug_info_file *f; uint32_t file_pc_count; size_t fn_len; - mrb_int len; uint32_t i; - if (!irep->debug_info) { return NULL; } - - mrb_assert(irep->filename); - mrb_assert(irep->lines); + if (!d) return NULL; + if (start_pos == end_pos) return NULL; - info = irep->debug_info; + mrb_assert(filename); + mrb_assert(lines); - if (info->flen > 0 && strcmp(irep->filename, info->files[info->flen - 1]->filename) == 0) { - return NULL; + if (d->flen > 0) { + const char *fn = mrb_sym_name_len(mrb, d->files[d->flen - 1]->filename_sym, NULL); + if (strcmp(filename, fn) == 0) + return NULL; } - ret = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*ret)); - info->files = - (mrb_irep_debug_info_file**)( - info->files - ? mrb_realloc(mrb, info->files, sizeof(mrb_irep_debug_info_file*) * (info->flen + 1)) + f = (mrb_irep_debug_info_file*)mrb_malloc(mrb, sizeof(*f)); + d->files = (mrb_irep_debug_info_file**)( + d->files + ? mrb_realloc(mrb, d->files, sizeof(mrb_irep_debug_info_file*) * (d->flen + 1)) : mrb_malloc(mrb, sizeof(mrb_irep_debug_info_file*))); - info->files[info->flen++] = ret; + d->files[d->flen++] = f; file_pc_count = end_pos - start_pos; - ret->start_pos = start_pos; - info->pc_count = end_pos; + f->start_pos = start_pos; + d->pc_count = end_pos; - fn_len = strlen(irep->filename); - ret->filename_sym = mrb_intern(mrb, irep->filename, fn_len); - len = 0; - ret->filename = mrb_sym2name_len(mrb, ret->filename_sym, &len); + fn_len = strlen(filename); + f->filename_sym = mrb_intern(mrb, filename, fn_len); - ret->line_type = select_line_type(irep->lines + start_pos, end_pos - start_pos); - ret->lines.ptr = NULL; + f->line_type = select_line_type(lines + start_pos, end_pos - start_pos); + f->lines.ptr = NULL; - switch (ret->line_type) { + switch (f->line_type) { case mrb_debug_line_ary: - ret->line_entry_count = file_pc_count; - ret->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count); + f->line_entry_count = file_pc_count; + f->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count); for (i = 0; i < file_pc_count; ++i) { - ret->lines.ary[i] = irep->lines[start_pos + i]; + f->lines.ary[i] = lines[start_pos + i]; } break; case mrb_debug_line_flat_map: { uint16_t prev_line = 0; mrb_irep_debug_info_line m; - ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1); - ret->line_entry_count = 0; + f->line_entry_count = 0; for (i = 0; i < file_pc_count; ++i) { - if (irep->lines[start_pos + i] == prev_line) { continue; } + if (lines[start_pos + i] == prev_line) { continue; } + ++f->line_entry_count; + prev_line = lines[start_pos + i]; + } + f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * f->line_entry_count); + prev_line = 0; + for (i = 0; i < file_pc_count; ++i) { + if (lines[start_pos + i] == prev_line) { continue; } - ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc( - mrb, ret->lines.flat_map, - sizeof(mrb_irep_debug_info_line) * (ret->line_entry_count + 1)); m.start_pos = start_pos + i; - m.line = irep->lines[start_pos + i]; - ret->lines.flat_map[ret->line_entry_count] = m; + m.line = lines[start_pos + i]; + f->lines.flat_map[f->line_entry_count] = m; /* update */ - ++ret->line_entry_count; - prev_line = irep->lines[start_pos + i]; + prev_line = lines[start_pos + i]; } - } break; + break; + } + + case mrb_debug_line_packed_map: { + uint32_t prev_line = 0; + uint32_t prev_pc = 0; + size_t packed_size = 0; + uint8_t *p, *pend; + + for (i = 0; i < file_pc_count; ++i) { + if (lines[start_pos + i] == prev_line) { continue; } + + packed_size += packed_int_len(start_pos+i-prev_pc); + prev_pc = start_pos+i; + packed_size += packed_int_len(lines[start_pos+i]-prev_line); + prev_line = lines[start_pos + i]; + } + p = f->lines.packed_map = (uint8_t*)mrb_malloc(mrb, packed_size); + pend = p + packed_size; + prev_line = 0; prev_pc = 0; + for (i = 0; i < file_pc_count; ++i) { + if (lines[start_pos + i] == prev_line) { continue; } + p += packed_int_encode(start_pos+i-prev_pc, p, pend); + prev_pc = start_pos + i; + p += packed_int_encode(lines[start_pos + i]-prev_line, p, pend); + prev_line = lines[start_pos + i]; + /* update */ + } + f->line_entry_count = (uint32_t)packed_size; + break; + } default: mrb_assert(0); break; } - return ret; + return f; } MRB_API void @@ -207,11 +302,14 @@ mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d) if (!d) { return; } - for (i = 0; i < d->flen; ++i) { - mrb_assert(d->files[i]); - mrb_free(mrb, d->files[i]->lines.ptr); - mrb_free(mrb, d->files[i]); + if (d->files) { + for (i = 0; i < d->flen; ++i) { + if (d->files[i]) { + mrb_free(mrb, d->files[i]->lines.ptr); + mrb_free(mrb, d->files[i]); + } + } + mrb_free(mrb, d->files); } - mrb_free(mrb, d->files); mrb_free(mrb, d); } diff --git a/src/dump.c b/src/dump.c index df1e171e4..628c3dbc4 100644 --- a/src/dump.c +++ b/src/dump.c @@ -1,46 +1,28 @@ /* -** dump.c - mruby binary dumper (mrbc binary format) +** cdump.c - mruby binary dumper (in C) ** ** See Copyright Notice in mruby.h */ #include <string.h> #include <limits.h> +#include <math.h> #include <mruby/dump.h> #include <mruby/string.h> #include <mruby/irep.h> -#include <mruby/numeric.h> #include <mruby/debug.h> -#define FLAG_BYTEORDER_NATIVE 2 -#define FLAG_BYTEORDER_NONATIVE 0 - -#ifndef MRB_WITHOUT_FLOAT -#ifdef MRB_USE_FLOAT -#define MRB_FLOAT_FMT "%.8e" -#else -#define MRB_FLOAT_FMT "%.16e" -#endif +#ifndef MRB_NO_FLOAT +#include <mruby/endian.h> #endif -static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep); +static size_t get_irep_record_size_1(mrb_state *mrb, const mrb_irep *irep); #if UINT32_MAX > SIZE_MAX # error This code cannot be built on your environment. #endif static size_t -write_padding(uint8_t *buf) -{ - const size_t align = MRB_DUMP_ALIGNMENT; - size_t pad_len = -(intptr_t)buf & (align-1); - if (pad_len > 0) { - memset(buf, 0, pad_len); - } - return pad_len; -} - -static size_t get_irep_header_size(mrb_state *mrb) { size_t size = 0; @@ -52,7 +34,7 @@ get_irep_header_size(mrb_state *mrb) } static ptrdiff_t -write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) +write_irep_header(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf) { uint8_t *cur = buf; @@ -64,95 +46,112 @@ write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) return cur - buf; } - static size_t -get_iseq_block_size(mrb_state *mrb, mrb_irep *irep) +get_iseq_block_size(mrb_state *mrb, const mrb_irep *irep) { size_t size = 0; - size += sizeof(uint32_t); /* ilen */ - size += sizeof(uint32_t); /* max padding */ - size += sizeof(uint32_t) * irep->ilen; /* iseq(n) */ + size += sizeof(uint16_t); /* clen */ + size += sizeof(uint16_t); /* ilen */ + size += irep->ilen * sizeof(mrb_code); /* iseq(n) */ + size += irep->clen * sizeof(struct mrb_irep_catch_handler); return size; } static ptrdiff_t -write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags) +write_iseq_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf, uint8_t flags) { uint8_t *cur = buf; - int iseq_no; - - cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */ - cur += write_padding(cur); - switch (flags & DUMP_ENDIAN_NAT) { - case DUMP_ENDIAN_BIG: - if (bigendian_p()) goto native; - for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { - cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* opcode */ - } - break; - case DUMP_ENDIAN_LIL: - if (!bigendian_p()) goto native; - for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { - cur += uint32l_to_bin(irep->iseq[iseq_no], cur); /* opcode */ - } - break; + size_t seqlen = irep->ilen * sizeof(mrb_code) + + irep->clen * sizeof(struct mrb_irep_catch_handler); - native: - case DUMP_ENDIAN_NAT: - memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code)); - cur += irep->ilen * sizeof(mrb_code); - break; - } + cur += uint16_to_bin(irep->clen, cur); /* number of catch handlers */ + cur += uint16_to_bin(irep->ilen, cur); /* number of opcode */ + memcpy(cur, irep->iseq, seqlen); + cur += seqlen; return cur - buf; } +#ifndef MRB_NO_FLOAT +static void +dump_float(mrb_state *mrb, uint8_t *buf, mrb_float f) +{ + /* dump IEEE754 binary in little endian */ + union { + double f; + char s[sizeof(double)]; + } u = {.f = (double)f}; + + if (littleendian) { + memcpy(buf, u.s, sizeof(double)); + } + else { + size_t i; + + for (i=0; i<sizeof(double); i++) { + buf[i] = u.s[sizeof(double)-i-1]; + } + } +} +#endif static size_t -get_pool_block_size(mrb_state *mrb, mrb_irep *irep) +get_pool_block_size(mrb_state *mrb, const mrb_irep *irep) { int pool_no; size_t size = 0; - mrb_value str; - size += sizeof(uint32_t); /* plen */ - size += irep->plen * (sizeof(uint8_t) + sizeof(uint16_t)); /* len(n) */ + size += sizeof(uint16_t); /* plen */ + size += irep->plen * sizeof(uint8_t); /* len(n) */ for (pool_no = 0; pool_no < irep->plen; pool_no++) { int ai = mrb_gc_arena_save(mrb); - switch (mrb_type(irep->pool[pool_no])) { - case MRB_TT_FIXNUM: - str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10); + switch (irep->pool[pool_no].tt) { + case IREP_TT_INT64: +#ifdef MRB_64BIT { - mrb_int len = RSTRING_LEN(str); - mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); - size += (size_t)len; + int64_t i = irep->pool[pool_no].u.i64; + + if (i < INT32_MIN || INT32_MAX < i) + size += 8; + else + size += 4; } break; +#else + /* fall through */ +#endif + case IREP_TT_INT32: + size += 4; /* 32bits = 4bytes */ + break; -#ifndef MRB_WITHOUT_FLOAT - case MRB_TT_FLOAT: - str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT); + case IREP_TT_BIGINT: { - mrb_int len = RSTRING_LEN(str); + mrb_int len = irep->pool[pool_no].u.str[0]; mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); - size += (size_t)len; + size += sizeof(uint8_t); + size += (size_t)len+2; } break; -#endif - case MRB_TT_STRING: + case IREP_TT_FLOAT: +#ifndef MRB_NO_FLOAT { - mrb_int len = RSTRING_LEN(irep->pool[pool_no]); - mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); - size += (size_t)len; + size += sizeof(double); } +#endif break; - default: + default: /* packed IREP_TT_STRING */ + { + mrb_int len = irep->pool[pool_no].tt >> 2; /* unpack length */ + mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); + size += sizeof(uint16_t); + size += (size_t)len+1; + } break; } mrb_gc_arena_restore(mrb, ai); @@ -162,71 +161,89 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep) } static ptrdiff_t -write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) +write_pool_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf) { int pool_no; uint8_t *cur = buf; - uint16_t len; - mrb_value str; - const char *char_ptr; + mrb_int len; + const char *ptr; - cur += uint32_to_bin(irep->plen, cur); /* number of pool */ + cur += uint16_to_bin(irep->plen, cur); /* number of pool */ for (pool_no = 0; pool_no < irep->plen; pool_no++) { int ai = mrb_gc_arena_save(mrb); - switch (mrb_type(irep->pool[pool_no])) { - case MRB_TT_FIXNUM: - cur += uint8_to_bin(IREP_TT_FIXNUM, cur); /* data type */ - str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10); - break; - -#ifndef MRB_WITHOUT_FLOAT - case MRB_TT_FLOAT: - cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ - str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT); + switch (irep->pool[pool_no].tt) { +#ifdef MRB_64BIT + case IREP_TT_INT64: + { + int64_t i = irep->pool[pool_no].u.i64; + if (i < INT32_MIN || INT32_MAX < i) { + cur += uint8_to_bin(IREP_TT_INT64, cur); /* data type */ + cur += uint32_to_bin((uint32_t)((i>>32) & 0xffffffff), cur); /* i64 hi */ + cur += uint32_to_bin((uint32_t)((i ) & 0xffffffff), cur); /* i64 lo */ + } + else { + cur += uint8_to_bin(IREP_TT_INT32, cur); /* data type */ + cur += uint32_to_bin(irep->pool[pool_no].u.i32, cur); /* i32 */ + } + } break; #endif + case IREP_TT_INT32: + cur += uint8_to_bin(IREP_TT_INT32, cur); /* data type */ + cur += uint32_to_bin(irep->pool[pool_no].u.i32, cur); /* i32 */ + break; - case MRB_TT_STRING: - cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */ - str = irep->pool[pool_no]; + case IREP_TT_BIGINT: + cur += uint8_to_bin(IREP_TT_BIGINT, cur); /* data type */ + len = irep->pool[pool_no].u.str[0]; + memcpy(cur, irep->pool[pool_no].u.str, (size_t)len+2); + cur += len+2; + *cur++ = '\0'; break; - default: - continue; - } + case IREP_TT_FLOAT: + cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ +#ifndef MRB_NO_FLOAT + { + dump_float(mrb, cur,irep->pool[pool_no].u.f); + cur += sizeof(double); + } +#else + cur += uint16_to_bin(0, cur); /* zero length */ +#endif + break; - char_ptr = RSTRING_PTR(str); - { - mrb_int tlen = RSTRING_LEN(str); - mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX); - len = (uint16_t)tlen; + default: /* string */ + cur += uint8_to_bin(IREP_TT_STR, cur); /* data type */ + ptr = irep->pool[pool_no].u.str; + len = irep->pool[pool_no].tt>>2; + mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX); + cur += uint16_to_bin((uint16_t)len, cur); /* data length */ + memcpy(cur, ptr, (size_t)len); + cur += len; + *cur++ = '\0'; + break; } - - cur += uint16_to_bin(len, cur); /* data length */ - memcpy(cur, char_ptr, (size_t)len); - cur += len; - mrb_gc_arena_restore(mrb, ai); } return cur - buf; } - static size_t -get_syms_block_size(mrb_state *mrb, mrb_irep *irep) +get_syms_block_size(mrb_state *mrb, const mrb_irep *irep) { size_t size = 0; int sym_no; mrb_int len; - size += sizeof(uint32_t); /* slen */ + size += sizeof(uint16_t); /* slen */ for (sym_no = 0; sym_no < irep->slen; sym_no++) { size += sizeof(uint16_t); /* snl(n) */ if (irep->syms[sym_no] != 0) { - mrb_sym2name_len(mrb, irep->syms[sym_no], &len); + mrb_sym_name_len(mrb, irep->syms[sym_no], &len); size += len + 1; /* sn(n) + null char */ } } @@ -235,19 +252,19 @@ get_syms_block_size(mrb_state *mrb, mrb_irep *irep) } static ptrdiff_t -write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) +write_syms_block(mrb_state *mrb, const mrb_irep *irep, uint8_t *buf) { int sym_no; uint8_t *cur = buf; const char *name; - cur += uint32_to_bin(irep->slen, cur); /* number of symbol */ + cur += uint16_to_bin(irep->slen, cur); /* number of symbol */ for (sym_no = 0; sym_no < irep->slen; sym_no++) { if (irep->syms[sym_no] != 0) { mrb_int len; - name = mrb_sym2name_len(mrb, irep->syms[sym_no], &len); + name = mrb_sym_name_len(mrb, irep->syms[sym_no], &len); mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX); cur += uint16_to_bin((uint16_t)len, cur); /* length of symbol name */ @@ -264,7 +281,7 @@ write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) } static size_t -get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep) +get_irep_record_size_1(mrb_state *mrb, const mrb_irep *irep) { size_t size = 0; @@ -276,7 +293,7 @@ get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep) } static size_t -get_irep_record_size(mrb_state *mrb, mrb_irep *irep) +get_irep_record_size(mrb_state *mrb, const mrb_irep *irep) { size_t size = 0; int irep_no; @@ -289,7 +306,7 @@ get_irep_record_size(mrb_state *mrb, mrb_irep *irep) } static int -write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags) +write_irep_record(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags) { int i; uint8_t *src = bin; @@ -298,11 +315,6 @@ write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_rec return MRB_DUMP_INVALID_IREP; } - *irep_record_size = get_irep_record_size_1(mrb, irep); - if (*irep_record_size == 0) { - return MRB_DUMP_GENERAL_FAILURE; - } - bin += write_irep_header(mrb, irep, bin); bin += write_iseq_block(mrb, irep, bin, flags); bin += write_pool_block(mrb, irep, bin); @@ -350,7 +362,7 @@ write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin) } static int -write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags) +write_section_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags) { int result; size_t rsize = 0; @@ -366,126 +378,15 @@ write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, if (result != MRB_DUMP_OK) { return result; } + mrb_assert(rsize == get_irep_record_size(mrb, irep)); *len_p = cur - bin + rsize; write_section_irep_header(mrb, *len_p, bin); return MRB_DUMP_OK; } -static int -write_section_lineno_header(mrb_state *mrb, size_t section_size, uint8_t *bin) -{ - struct rite_section_lineno_header *header = (struct rite_section_lineno_header*)bin; - - memcpy(header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(header->section_ident)); - uint32_to_bin((uint32_t)section_size, header->section_size); - - return MRB_DUMP_OK; -} - -static size_t -get_lineno_record_size(mrb_state *mrb, mrb_irep *irep) -{ - size_t size = 0; - - size += sizeof(uint32_t); /* record size */ - size += sizeof(uint16_t); /* filename size */ - if (irep->filename) { - size += strlen(irep->filename); /* filename */ - } - size += sizeof(uint32_t); /* niseq */ - if (irep->lines) { - size += sizeof(uint16_t) * irep->ilen; /* lineno */ - } - - return size; -} - -static size_t -write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin) -{ - uint8_t *cur = bin; - int iseq_no; - size_t filename_len; - ptrdiff_t diff; - - cur += sizeof(uint32_t); /* record size */ - - if (irep->filename) { - filename_len = strlen(irep->filename); - } - else { - filename_len = 0; - } - mrb_assert_int_fit(size_t, filename_len, uint16_t, UINT16_MAX); - cur += uint16_to_bin((uint16_t)filename_len, cur); /* filename size */ - - if (filename_len) { - memcpy(cur, irep->filename, filename_len); - cur += filename_len; /* filename */ - } - - if (irep->lines) { - mrb_assert_int_fit(size_t, irep->ilen, uint32_t, UINT32_MAX); - cur += uint32_to_bin((uint32_t)(irep->ilen), cur); /* niseq */ - for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { - cur += uint16_to_bin(irep->lines[iseq_no], cur); /* opcode */ - } - } - else { - cur += uint32_to_bin(0, cur); /* niseq */ - } - - diff = cur - bin; - mrb_assert_int_fit(ptrdiff_t, diff, uint32_t, UINT32_MAX); - - uint32_to_bin((uint32_t)diff, bin); /* record size */ - - mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); - return (size_t)diff; -} - -static size_t -write_lineno_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin) -{ - size_t rlen, size = 0; - int i; - - rlen = write_lineno_record_1(mrb, irep, bin); - bin += rlen; - size += rlen; - for (i=0; i<irep->rlen; i++) { - rlen = write_lineno_record(mrb, irep, bin); - bin += rlen; - size += rlen; - } - return size; -} - -static int -write_section_lineno(mrb_state *mrb, mrb_irep *irep, uint8_t *bin) -{ - size_t section_size = 0; - size_t rlen = 0; /* size of irep record */ - uint8_t *cur = bin; - - if (mrb == NULL || bin == NULL) { - return MRB_DUMP_INVALID_ARGUMENT; - } - - cur += sizeof(struct rite_section_lineno_header); - section_size += sizeof(struct rite_section_lineno_header); - - rlen = write_lineno_record(mrb, irep, cur); - section_size += rlen; - - write_section_lineno_header(mrb, section_size, bin); - - return MRB_DUMP_OK; -} - static size_t -get_debug_record_size(mrb_state *mrb, mrb_irep *irep) +get_debug_record_size(mrb_state *mrb, const mrb_irep *irep) { size_t ret = 0; uint16_t f_idx; @@ -512,6 +413,10 @@ get_debug_record_size(mrb_state *mrb, mrb_irep *irep) ret += (sizeof(uint32_t) + sizeof(uint16_t)) * (size_t)(file->line_entry_count); break; + case mrb_debug_line_packed_map: + ret += (size_t)(file->line_entry_count); + break; + default: mrb_assert(0); break; } } @@ -534,11 +439,11 @@ find_filename_index(const mrb_sym *ary, int ary_len, mrb_sym s) } static size_t -get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *lp) +get_filename_table_size(mrb_state *mrb, const mrb_irep *irep, mrb_sym **fp, uint16_t *lp) { mrb_sym *filenames = *fp; size_t size = 0; - mrb_irep_debug_info *di = irep->debug_info; + const mrb_irep_debug_info *di = irep->debug_info; int i; mrb_assert(lp); @@ -554,7 +459,7 @@ get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t * filenames[*lp - 1] = file->filename_sym; /* filename */ - mrb_sym2name_len(mrb, file->filename_sym, &filename_len); + mrb_sym_name_len(mrb, file->filename_sym, &filename_len); size += sizeof(uint16_t) + (size_t)filename_len; } } @@ -565,7 +470,7 @@ get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t * } static size_t -write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) +write_debug_record_1(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) { uint8_t *cur; uint16_t f_idx; @@ -606,6 +511,11 @@ write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const } } break; + case mrb_debug_line_packed_map: { + memcpy(cur, file->lines.packed_map, file->line_entry_count); + cur += file->line_entry_count; + } break; + default: mrb_assert(0); break; } } @@ -619,7 +529,7 @@ write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const } static size_t -write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) +write_debug_record(mrb_state *mrb, const mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) { size_t size, len; int irep_no; @@ -637,7 +547,7 @@ write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* } static int -write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len) +write_section_debug(mrb_state *mrb, const mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len) { size_t section_size = 0; const uint8_t *bin = cur; @@ -658,7 +568,7 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const cur += uint16_to_bin(filenames_len, cur); section_size += sizeof(uint16_t); for (i = 0; i < filenames_len; ++i) { - sym = mrb_sym2name_len(mrb, filenames[i], &sym_len); + sym = mrb_sym_name_len(mrb, filenames[i], &sym_len); mrb_assert(sym); cur += uint16_to_bin((uint16_t)sym_len, cur); memcpy(cur, sym, sym_len); @@ -687,7 +597,7 @@ create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32 } for (i = 0; i + 1 < irep->nlocals; ++i) { - mrb_sym const name = irep->lv[i].name; + mrb_sym const name = irep->lv[i]; if (name == 0) continue; if (find_filename_index(*syms, *syms_len, name) != -1) continue; @@ -712,7 +622,7 @@ write_lv_sym_table(mrb_state *mrb, uint8_t **start, mrb_sym const *syms, uint32_ cur += uint32_to_bin(syms_len, cur); for (i = 0; i < syms_len; ++i) { - str = mrb_sym2name_len(mrb, syms[i], &str_len); + str = mrb_sym_name_len(mrb, syms[i], &str_len); cur += uint16_to_bin((uint16_t)str_len, cur); memcpy(cur, str, str_len); cur += str_len; @@ -730,16 +640,14 @@ write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym c int i; for (i = 0; i + 1 < irep->nlocals; ++i) { - if (irep->lv[i].name == 0) { + if (irep->lv[i] == 0) { cur += uint16_to_bin(RITE_LV_NULL_MARK, cur); - cur += uint16_to_bin(0, cur); } else { - int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i].name); + int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i]); mrb_assert(sym_idx != -1); /* local variable name must be in syms */ cur += uint16_to_bin(sym_idx, cur); - cur += uint16_to_bin(irep->lv[i].r, cur); } } @@ -753,12 +661,12 @@ write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym c } static size_t -get_lv_record_size(mrb_state *mrb, mrb_irep *irep) +get_lv_record_size(mrb_state *mrb, const mrb_irep *irep) { size_t ret = 0; int i; - ret += (sizeof(uint16_t) + sizeof(uint16_t)) * (irep->nlocals - 1); + ret += sizeof(uint16_t) * (irep->nlocals - 1); for (i = 0; i < irep->rlen; ++i) { ret += get_lv_record_size(mrb, irep->reps[i]); @@ -768,7 +676,7 @@ get_lv_record_size(mrb_state *mrb, mrb_irep *irep) } static size_t -get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len) +get_lv_section_size(mrb_state *mrb, const mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len) { size_t ret = 0, i; @@ -776,7 +684,7 @@ get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_ ret += sizeof(uint16_t) * syms_len; /* symbol name lengths */ for (i = 0; i < syms_len; ++i) { mrb_int str_len; - mrb_sym2name_len(mrb, syms[i], &str_len); + mrb_sym_name_len(mrb, syms[i], &str_len); ret += str_len; } @@ -786,7 +694,7 @@ get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_ } static int -write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const *syms, uint32_t const syms_len) +write_section_lv(mrb_state *mrb, const mrb_irep *irep, uint8_t *start, mrb_sym const *syms, uint32_t const syms_len) { uint8_t *cur = start; struct rite_section_lv_header *header; @@ -824,88 +732,53 @@ static int write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8_t flags) { struct rite_binary_header *header = (struct rite_binary_header *)bin; - uint16_t crc; - uint32_t offset; - - switch (flags & DUMP_ENDIAN_NAT) { - endian_big: - case DUMP_ENDIAN_BIG: - memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)); - break; - endian_little: - case DUMP_ENDIAN_LIL: - memcpy(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)); - break; - - case DUMP_ENDIAN_NAT: - if (bigendian_p()) goto endian_big; - goto endian_little; - break; - } - memcpy(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)); + memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)); + memcpy(header->major_version, RITE_BINARY_MAJOR_VER, sizeof(header->major_version)); + memcpy(header->minor_version, RITE_BINARY_MINOR_VER, sizeof(header->minor_version)); memcpy(header->compiler_name, RITE_COMPILER_NAME, sizeof(header->compiler_name)); memcpy(header->compiler_version, RITE_COMPILER_VERSION, sizeof(header->compiler_version)); mrb_assert(binary_size <= UINT32_MAX); uint32_to_bin((uint32_t)binary_size, header->binary_size); - offset = (uint32_t)((&(header->binary_crc[0]) - bin) + sizeof(uint16_t)); - crc = calc_crc_16_ccitt(bin + offset, binary_size - offset, 0); - uint16_to_bin(crc, header->binary_crc); - return MRB_DUMP_OK; } static mrb_bool -is_debug_info_defined(mrb_irep *irep) +debug_info_defined_p(const mrb_irep *irep) { int i; if (!irep->debug_info) return FALSE; for (i=0; i<irep->rlen; i++) { - if (!is_debug_info_defined(irep->reps[i])) return FALSE; + if (!debug_info_defined_p(irep->reps[i])) return FALSE; } return TRUE; } static mrb_bool -is_lv_defined(mrb_irep *irep) +lv_defined_p(const mrb_irep *irep) { int i; if (irep->lv) { return TRUE; } for (i = 0; i < irep->rlen; ++i) { - if (is_lv_defined(irep->reps[i])) { return TRUE; } + if (lv_defined_p(irep->reps[i])) { return TRUE; } } return FALSE; } -static uint8_t -dump_flags(uint8_t flags, uint8_t native) -{ - if (native == FLAG_BYTEORDER_NATIVE) { - if ((flags & DUMP_ENDIAN_NAT) == 0) { - return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_NAT; - } - return flags; - } - if ((flags & DUMP_ENDIAN_NAT) == 0) { - return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_BIG; - } - return flags; -} - static int -dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) +dump_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) { int result = MRB_DUMP_GENERAL_FAILURE; size_t malloc_size; size_t section_irep_size; size_t section_lineno_size = 0, section_lv_size = 0; uint8_t *cur = NULL; - mrb_bool const debug_info_defined = is_debug_info_defined(irep), lv_defined = is_lv_defined(irep); + mrb_bool const debug_info_defined = debug_info_defined_p(irep), lv_defined = lv_defined_p(irep); mrb_sym *lv_syms = NULL; uint32_t lv_syms_len = 0; mrb_sym *filenames = NULL; uint16_t filenames_len = 0; @@ -918,7 +791,7 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t * section_irep_size += get_irep_record_size(mrb, irep); /* DEBUG section size */ - if (flags & DUMP_DEBUG_INFO) { + if (flags & MRB_DUMP_DEBUG_INFO) { if (debug_info_defined) { section_lineno_size += sizeof(struct rite_section_debug_header); /* filename table */ @@ -930,10 +803,6 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t * section_lineno_size += get_debug_record_size(mrb, irep); } - else { - section_lineno_size += sizeof(struct rite_section_lineno_header); - section_lineno_size += get_lineno_record_size(mrb, irep); - } } if (lv_defined) { @@ -958,15 +827,12 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t * sizeof(struct rite_binary_footer); /* write DEBUG section */ - if (flags & DUMP_DEBUG_INFO) { + if (flags & MRB_DUMP_DEBUG_INFO) { if (debug_info_defined) { result = write_section_debug(mrb, irep, cur, filenames, filenames_len); - } - else { - result = write_section_lineno(mrb, irep, cur); - } - if (result != MRB_DUMP_OK) { - goto error_exit; + if (result != MRB_DUMP_OK) { + goto error_exit; + } } cur += section_lineno_size; } @@ -993,15 +859,15 @@ error_exit: } int -mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) +mrb_dump_irep(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) { - return dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), bin, bin_size); + return dump_irep(mrb, irep, flags, bin, bin_size); } -#ifndef MRB_DISABLE_STDIO +#ifndef MRB_NO_STDIO int -mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp) +mrb_dump_irep_binary(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE* fp) { uint8_t *bin = NULL; size_t bin_size = 0; @@ -1011,7 +877,7 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp) return MRB_DUMP_INVALID_ARGUMENT; } - result = dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), &bin, &bin_size); + result = dump_irep(mrb, irep, flags, &bin, &bin_size); if (result == MRB_DUMP_OK) { if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) { result = MRB_DUMP_WRITE_FAULT; @@ -1022,22 +888,8 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp) return result; } -static mrb_bool -dump_bigendian_p(uint8_t flags) -{ - switch (flags & DUMP_ENDIAN_NAT) { - case DUMP_ENDIAN_BIG: - return TRUE; - case DUMP_ENDIAN_LIL: - return FALSE; - default: - case DUMP_ENDIAN_NAT: - return bigendian_p(); - } -} - int -mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname) +mrb_dump_irep_cfunc(mrb_state *mrb, const mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname) { uint8_t *bin = NULL; size_t bin_size = 0, bin_idx = 0; @@ -1046,38 +898,20 @@ mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, con if (fp == NULL || initname == NULL || initname[0] == '\0') { return MRB_DUMP_INVALID_ARGUMENT; } - flags = dump_flags(flags, FLAG_BYTEORDER_NATIVE); result = dump_irep(mrb, irep, flags, &bin, &bin_size); if (result == MRB_DUMP_OK) { - if (!dump_bigendian_p(flags)) { - if (fprintf(fp, "/* dumped in little endian order.\n" - " use `mrbc -E` option for big endian CPU. */\n") < 0) { - mrb_free(mrb, bin); - return MRB_DUMP_WRITE_FAULT; - } - } - else { - if (fprintf(fp, "/* dumped in big endian order.\n" - " use `mrbc -e` option for better performance on little endian CPU. */\n") < 0) { - mrb_free(mrb, bin); - return MRB_DUMP_WRITE_FAULT; - } - } if (fprintf(fp, "#include <stdint.h>\n") < 0) { /* for uint8_t under at least Darwin */ mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } if (fprintf(fp, - "extern const uint8_t %s[];\n" - "const uint8_t\n" - "#if defined __GNUC__\n" - "__attribute__((aligned(%u)))\n" - "#elif defined _MSC_VER\n" - "__declspec(align(%u))\n" - "#endif\n" - "%s[] = {", - initname, - (uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) { + "%s\n" + "const uint8_t %s[] = {", + (flags & MRB_DUMP_STATIC) ? "static" + : "#ifdef __cplusplus\n" + "extern\n" + "#endif", + initname) < 0) { mrb_free(mrb, bin); return MRB_DUMP_WRITE_FAULT; } @@ -1103,4 +937,4 @@ mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, con return result; } -#endif /* MRB_DISABLE_STDIO */ +#endif /* MRB_NO_STDIO */ diff --git a/src/enum.c b/src/enum.c index 03508953e..b95956715 100644 --- a/src/enum.c +++ b/src/enum.c @@ -14,25 +14,11 @@ enum_update_hash(mrb_state *mrb, mrb_value self) mrb_int hash; mrb_int index; mrb_int hv; - mrb_value item_hash; - mrb_get_args(mrb, "iio", &hash, &index, &item_hash); - if (mrb_fixnum_p(item_hash)) { - hv = mrb_fixnum(item_hash); - } -#ifndef MRB_WITHOUT_FLOAT - else if (mrb_float_p(item_hash)) { - hv = (mrb_int)mrb_float(item_hash); - } -#endif - else { - mrb_raise(mrb, E_TYPE_ERROR, "can't calculate hash"); - /* not reached */ - hv = 0; - } - hash ^= (hv << (index % 16)); + mrb_get_args(mrb, "iii", &hash, &index, &hv); + hash ^= ((uint32_t)hv << (index % 16)); - return mrb_fixnum_value(hash); + return mrb_int_value(mrb, hash); } void @@ -40,5 +26,5 @@ mrb_init_enumerable(mrb_state *mrb) { struct RClass *enumerable; enumerable = mrb_define_module(mrb, "Enumerable"); /* 15.3.2 */ - mrb_define_module_function(mrb, enumerable, "__update_hash", enum_update_hash, MRB_ARGS_REQ(1)); + mrb_define_module_function(mrb, enumerable, "__update_hash", enum_update_hash, MRB_ARGS_REQ(3)); } diff --git a/src/error.c b/src/error.c index 599612b97..7953deea7 100644 --- a/src/error.c +++ b/src/error.c @@ -13,10 +13,10 @@ #include <mruby/proc.h> #include <mruby/string.h> #include <mruby/variable.h> -#include <mruby/debug.h> #include <mruby/error.h> #include <mruby/class.h> #include <mruby/throw.h> +#include <mruby/presym.h> MRB_API mrb_value mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len) @@ -28,7 +28,7 @@ mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len) MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str) { - str = mrb_str_to_str(mrb, str); + mrb_to_str(mrb, str); return mrb_obj_new(mrb, c, 1, &str); } @@ -44,11 +44,9 @@ static mrb_value exc_initialize(mrb_state *mrb, mrb_value exc) { mrb_value mesg; - mrb_int argc; - mrb_value *argv; - if (mrb_get_args(mrb, "|o*!", &mesg, &argv, &argc) >= 1) { - mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg); + if (mrb_get_args(mrb, "|o", &mesg) == 1) { + mrb_iv_set(mrb, exc, MRB_SYM(mesg), mesg); } return exc; } @@ -77,7 +75,7 @@ exc_exception(mrb_state *mrb, mrb_value self) if (argc == 0) return self; if (mrb_obj_equal(mrb, self, a)) return self; exc = mrb_obj_clone(mrb, self); - mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), a); + mrb_iv_set(mrb, exc, MRB_SYM(mesg), a); return exc; } @@ -93,7 +91,7 @@ exc_exception(mrb_state *mrb, mrb_value self) static mrb_value exc_to_s(mrb_state *mrb, mrb_value exc) { - mrb_value mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg")); + mrb_value mesg = mrb_attr_get(mrb, exc, MRB_SYM(mesg)); struct RObject *p; if (!mrb_string_p(mesg)) { @@ -117,7 +115,7 @@ exc_to_s(mrb_state *mrb, mrb_value exc) static mrb_value exc_message(mrb_state *mrb, mrb_value exc) { - return mrb_funcall(mrb, exc, "to_s", 0); + return mrb_funcall_id(mrb, exc, MRB_SYM(to_s), 0); } /* @@ -130,37 +128,13 @@ exc_message(mrb_state *mrb, mrb_value exc) * returns message and class name. */ -static mrb_value -exc_inspect(mrb_state *mrb, mrb_value exc) +mrb_value +mrb_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")); - line = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "line")); - - append_mesg = !mrb_nil_p(mesg); - if (append_mesg) { - mesg = mrb_obj_as_string(mrb, mesg); - 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)) { - if (append_mesg) { - str = mrb_format(mrb, "%S:%S: %S (%S)", file, line, mesg, str); - } - else { - str = mrb_format(mrb, "%S:%S: %S", file, line, str); - } - } - else if (append_mesg) { - str = mrb_format(mrb, "%S: %S", str, mesg); - } - return str; + mrb_value mesg = mrb_attr_get(mrb, exc, MRB_SYM(mesg)); + mrb_value cname = mrb_mod_to_s(mrb, mrb_obj_value(mrb_obj_class(mrb, exc))); + mesg = mrb_obj_as_string(mrb, mesg); + return RSTRING_LEN(mesg) == 0 ? cname : mrb_format(mrb, "%v (%v)", mesg, cname); } void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc); @@ -181,46 +155,18 @@ set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace) p++; } } - mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace); + mrb_iv_set(mrb, exc, MRB_SYM(backtrace), backtrace); } static mrb_value exc_set_backtrace(mrb_state *mrb, mrb_value exc) { - mrb_value backtrace; + mrb_value backtrace = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &backtrace); set_backtrace(mrb, exc, backtrace); return backtrace; } -static void -exc_debug_info(mrb_state *mrb, struct RObject *exc) -{ - mrb_callinfo *ci = mrb->c->ci; - mrb_code *pc = ci->pc; - - if (mrb_obj_iv_defined(mrb, exc, mrb_intern_lit(mrb, "file"))) return; - while (ci >= mrb->c->cibase) { - mrb_code *err = ci->err; - - if (!err && pc) err = pc - 1; - if (err && ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) { - mrb_irep *irep = ci->proc->body.irep; - - int32_t const line = mrb_debug_get_line(irep, err - irep->iseq); - char const* file = mrb_debug_get_filename(irep, err - irep->iseq); - if (line != -1 && file) { - mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "file"), mrb_str_new_cstr(mrb, file)); - mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "line"), mrb_fixnum_value(line)); - return; - } - } - pc = ci->pc; - ci--; - } -} - void mrb_exc_set(mrb_state *mrb, mrb_value exc) { @@ -233,20 +179,15 @@ mrb_exc_set(mrb_state *mrb, mrb_value exc) (struct RBasic*)mrb->exc == mrb->gc.arena[mrb->gc.arena_idx-1]) { mrb->gc.arena_idx--; } - if (!mrb->gc.out_of_memory && !MRB_FROZEN_P(mrb->exc)) { - exc_debug_info(mrb, mrb->exc); + if (!mrb->gc.out_of_memory && !mrb_frozen_p(mrb->exc)) { mrb_keep_backtrace(mrb, exc); } } } -MRB_API mrb_noreturn void -mrb_exc_raise(mrb_state *mrb, mrb_value exc) +static mrb_noreturn void +exc_throw(mrb_state *mrb, mrb_value exc) { - if (!mrb_obj_is_kind_of(mrb, exc, mrb->eException_class)) { - mrb_raise(mrb, E_TYPE_ERROR, "exception object expected"); - } - mrb_exc_set(mrb, exc); if (!mrb->jmp) { mrb_p(mrb, exc); abort(); @@ -255,64 +196,182 @@ mrb_exc_raise(mrb_state *mrb, mrb_value exc) } MRB_API mrb_noreturn void +mrb_exc_raise(mrb_state *mrb, mrb_value exc) +{ + if (mrb_break_p(exc)) { + mrb->exc = mrb_obj_ptr(exc); + } + else { + if (!mrb_obj_is_kind_of(mrb, exc, mrb->eException_class)) { + mrb_raise(mrb, E_TYPE_ERROR, "exception object expected"); + } + mrb_exc_set(mrb, exc); + } + exc_throw(mrb, exc); +} + +MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg) { mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mrb_str_new_cstr(mrb, msg))); } +/* + * <code>vsprintf</code> like formatting. + * + * The syntax of a format sequence is as follows. + * + * %[modifier]specifier + * + * The modifiers are: + * + * ----------+------------------------------------------------------------ + * Modifier | Meaning + * ----------+------------------------------------------------------------ + * ! | Convert to string by corresponding `inspect` instead of + * | corresponding `to_s`. + * ----------+------------------------------------------------------------ + * + * The specifiers are: + * + * ----------+----------------+-------------------------------------------- + * Specifier | Argument Type | Note + * ----------+----------------+-------------------------------------------- + * c | char | + * d | int | + * f | mrb_float | + * i | mrb_int | + * l | char*, size_t | Arguments are string and length. + * n | mrb_sym | + * s | char* | Argument is NUL terminated string. + * t | mrb_value | Convert to type (class) of object. + * v,S | mrb_value | + * C | struct RClass* | + * T | mrb_value | Convert to real type (class) of object. + * Y | mrb_value | Same as `!v` if argument is `true`, `false` + * | | or `nil`, otherwise same as `T`. + * % | - | Convert to percent sign itself (no argument + * | | taken). + * ----------+----------------+-------------------------------------------- + */ MRB_API mrb_value mrb_vformat(mrb_state *mrb, const char *format, va_list ap) { - const char *p = format; - const char *b = p; - ptrdiff_t size; - int ai0 = mrb_gc_arena_save(mrb); - mrb_value ary = mrb_ary_new_capa(mrb, 4); + const char *chars, *p = format, *b = format, *e; + char ch; + size_t len; + mrb_int i; + struct RClass *cls; + mrb_bool inspect = FALSE; + mrb_value result = mrb_str_new_capa(mrb, 128), obj, str; int ai = mrb_gc_arena_save(mrb); while (*p) { const char c = *p++; - + e = p; if (c == '%') { - if (*p == 'S') { - mrb_value val; - - size = p - b - 1; - mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); - val = va_arg(ap, mrb_value); - mrb_ary_push(mrb, ary, mrb_obj_as_string(mrb, val)); - b = p + 1; - } - } - else if (c == '\\') { - if (*p) { - size = p - b - 1; - mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); - mrb_ary_push(mrb, ary, mrb_str_new(mrb, p, 1)); - b = ++p; + if (*p == '!') { + inspect = TRUE; + ++p; } - else { - break; + if (!*p) break; + switch (*p) { + case 'c': + ch = (char)va_arg(ap, int); + chars = &ch; + len = 1; + goto L_cat; + case 'd': case 'i': +#if MRB_INT_MAX < INT_MAX + i = (mrb_int)va_arg(ap, int); +#else + i = *p == 'd' ? (mrb_int)va_arg(ap, int) : va_arg(ap, mrb_int); +#endif + obj = mrb_fixnum_value(i); + goto L_cat_obj; +#ifndef MRB_NO_FLOAT + case 'f': + obj = mrb_float_value(mrb, (mrb_float)va_arg(ap, double)); + goto L_cat_obj; +#endif + case 'l': + chars = va_arg(ap, char*); + len = va_arg(ap, size_t); + L_cat: + if (inspect) { + obj = mrb_str_new(mrb, chars, len); + goto L_cat_obj; + } + L_cat_plain: + mrb_str_cat(mrb, result, b, e - b - 1); + mrb_str_cat(mrb, result, chars, len); + b = ++p; + mrb_gc_arena_restore(mrb, ai); + break; + case 'n': +#if UINT32_MAX < INT_MAX + obj = mrb_symbol_value((mrb_sym)va_arg(ap, int)); +#else + obj = mrb_symbol_value(va_arg(ap, mrb_sym)); +#endif + goto L_cat_obj; + case 's': + chars = va_arg(ap, char*); + len = strlen(chars); + goto L_cat; + case 't': + cls = mrb_class(mrb, va_arg(ap, mrb_value)); + goto L_cat_class; + case 'v': case 'S': + obj = va_arg(ap, mrb_value); + L_cat_obj: + str = (inspect ? mrb_inspect : mrb_obj_as_string)(mrb, obj); + if (mrb_type(str) != MRB_TT_STRING) { + chars = "void (no string conversion)"; + len = strlen(chars); + } + else { + chars = RSTRING_PTR(str); + len = RSTRING_LEN(str); + } + goto L_cat_plain; + case 'C': + cls = va_arg(ap, struct RClass*); + L_cat_class: + obj = mrb_obj_value(cls); + goto L_cat_obj; + case 'T': + obj = va_arg(ap, mrb_value); + L_cat_real_class_of: + cls = mrb_obj_class(mrb, obj); + goto L_cat_class; + case 'Y': + obj = va_arg(ap, mrb_value); + if (!mrb_test(obj) || mrb_true_p(obj)) { + inspect = TRUE; + goto L_cat_obj; + } + else { + goto L_cat_real_class_of; + } + case '%': + L_cat_current: + chars = p; + len = 1; + goto L_cat_plain; + default: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - %%%c", *p); } } - mrb_gc_arena_restore(mrb, ai); - } - if (b == format) { - mrb_gc_arena_restore(mrb, ai0); - return mrb_str_new_cstr(mrb, format); - } - else { - mrb_value val; + else if (c == '\\') { + if (!*p) break; + goto L_cat_current; - size = p - b; - if (size > 0) { - mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); } - val = mrb_ary_join(mrb, ary, mrb_nil_value()); - mrb_gc_arena_restore(mrb, ai0); - mrb_gc_protect(mrb, val); - return val; } + + mrb_str_cat(mrb, result, b, p - b); + return result; } MRB_API mrb_value @@ -368,7 +427,7 @@ mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...) MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...) { -#ifndef MRB_DISABLE_STDIO +#ifndef MRB_NO_STDIO va_list ap; mrb_value str; @@ -376,6 +435,7 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...) str = mrb_vformat(mrb, fmt, ap); fputs("warning: ", stderr); fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr); + putc('\n', stderr); va_end(ap); #endif } @@ -383,7 +443,7 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...) MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *fmt, ...) { -#ifndef MRB_DISABLE_STDIO +#ifndef MRB_NO_STDIO va_list ap; mrb_value str; @@ -421,7 +481,7 @@ mrb_make_exception(mrb_state *mrb, mrb_int argc, const mrb_value *argv) n = 1; exception_call: { - mrb_sym exc = mrb_intern_lit(mrb, "exception"); + mrb_sym exc = MRB_SYM(exception); if (mrb_respond_to(mrb, argv[0], exc)) { mesg = mrb_funcall_argv(mrb, argv[0], exc, n, argv+1); } @@ -433,7 +493,7 @@ exception_call: break; default: - mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 0..3)", mrb_fixnum_value(argc)); + mrb_argnum_error(mrb, argc, 0, 3); break; } if (argc > 0) { @@ -483,6 +543,93 @@ mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, mrb_exc_raise(mrb, exc); } +MRB_API mrb_noreturn void +mrb_frozen_error(mrb_state *mrb, void *frozen_obj) +{ + mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %t", mrb_obj_value(frozen_obj)); +} + +MRB_API mrb_noreturn void +mrb_argnum_error(mrb_state *mrb, mrb_int argc, int min, int max) +{ +#define FMT(exp) "wrong number of arguments (given %i, expected " exp ")" + if (min == max) + mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d"), argc, min); + else if (max < 0) + mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d+"), argc, min); + else + mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d..%d"), argc, min, max); +#undef FMT +} + +void mrb_core_init_printabort(void); + +int +mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state *, void *), void *opaque) +{ + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + int err = 1; + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + body(mrb, opaque); + err = 0; + } MRB_CATCH(&c_jmp) { + if (mrb->exc) { + mrb_p(mrb, mrb_obj_value(mrb->exc)); + mrb->exc = NULL; + } + else { + mrb_core_init_printabort(); + } + } MRB_END_EXC(&c_jmp); + + mrb->jmp = prev_jmp; + + return err; +} + +mrb_noreturn void +mrb_core_init_abort(mrb_state *mrb) +{ + mrb->exc = NULL; + exc_throw(mrb, mrb_nil_value()); +} + +void +mrb_protect_atexit(mrb_state *mrb) +{ + if (mrb->atexit_stack_len > 0) { + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + for (int i = mrb->atexit_stack_len; i > 0; --i) { + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + mrb->atexit_stack[i - 1](mrb); + mrb->jmp = prev_jmp; + } MRB_CATCH(&c_jmp) { + /* ignore atexit errors */ + } MRB_END_EXC(&c_jmp); + } +#ifndef MRB_FIXED_STATE_ATEXIT_STACK + mrb_free(mrb, mrb->atexit_stack); +#endif + mrb->jmp = prev_jmp; + } +} + +mrb_noreturn void +mrb_raise_nomemory(mrb_state *mrb) +{ + if (mrb->nomem_err) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); + } + else { + mrb_core_init_abort(mrb); + } +} + void mrb_init_exception(mrb_state *mrb) { @@ -490,12 +637,12 @@ mrb_init_exception(mrb_state *mrb) mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION); - mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_ANY()); - mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_ANY()); - mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_ANY()); + mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_OPT(1)); 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, "inspect", mrb_exc_inspect, 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)); @@ -504,11 +651,11 @@ mrb_init_exception(mrb_state *mrb) 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")); + mrb->stack_err = mrb_obj_ptr(mrb_exc_new_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")); + mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_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")); + mrb->arena_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, nomem_error, "arena overflow error")); #endif } @@ -1,5 +1,5 @@ /* -** etc.c - +** etc.c ** ** See Copyright Notice in mruby.h */ @@ -8,15 +8,14 @@ #include <mruby/string.h> #include <mruby/data.h> #include <mruby/class.h> -#include <mruby/re.h> -#include <mruby/irep.h> +#include <mruby/numeric.h> MRB_API struct RData* mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type) { struct RData *data; - data = (struct RData*)mrb_obj_alloc(mrb, MRB_TT_DATA, klass); + data = MRB_OBJ_ALLOC(mrb, MRB_TT_DATA, klass); data->data = ptr; data->type = type; @@ -26,21 +25,19 @@ mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { - if (mrb_type(obj) != MRB_TT_DATA) { + if (!mrb_data_p(obj)) { mrb_check_type(mrb, obj, MRB_TT_DATA); } if (DATA_TYPE(obj) != type) { const mrb_data_type *t2 = DATA_TYPE(obj); if (t2) { - mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)", - mrb_str_new_cstr(mrb, t2->struct_name), mrb_str_new_cstr(mrb, type->struct_name)); + mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)", + t2->struct_name, type->struct_name); } else { - struct RClass *c = mrb_class(mrb, obj); - - mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %S (expected %S)", - mrb_obj_value(c), mrb_str_new_cstr(mrb, type->struct_name)); + mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %t (expected %s)", + obj, type->struct_name); } } } @@ -48,7 +45,7 @@ mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) MRB_API void* mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { - if (mrb_type(obj) != MRB_TT_DATA) { + if (!mrb_data_p(obj)) { return NULL; } if (DATA_TYPE(obj) != type) { @@ -67,41 +64,17 @@ mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name) { - mrb_sym id; - - switch (mrb_type(name)) { - default: - name = mrb_check_string_type(mrb, name); - if (mrb_nil_p(name)) { - name = mrb_inspect(mrb, name); - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", name); - /* not reached */ - } - /* fall through */ - case MRB_TT_STRING: - name = mrb_str_intern(mrb, name); - /* fall through */ - case MRB_TT_SYMBOL: - id = mrb_symbol(name); - } - return id; + if (mrb_symbol_p(name)) return mrb_symbol(name); + if (mrb_string_p(name)) return mrb_intern_str(mrb, name); + mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a symbol nor a string", name); + return 0; /* not reached */ } -MRB_API mrb_int -#ifdef MRB_WITHOUT_FLOAT -mrb_fixnum_id(mrb_int f) -#else -mrb_float_id(mrb_float f) -#endif +static mrb_int +make_num_id(const char *p, size_t len) { - const char *p = (const char*)&f; - int len = sizeof(f); uint32_t id = 0; -#ifndef MRB_WITHOUT_FLOAT - /* normalize -0.0 to 0.0 */ - if (f == 0) f = 0.0; -#endif while (len--) { id = id*65599 + *p; p++; @@ -112,6 +85,22 @@ mrb_float_id(mrb_float f) } MRB_API mrb_int +mrb_int_id(mrb_int n) +{ + return make_num_id((const char*)&n, sizeof(n)); +} + +#ifndef MRB_NO_FLOAT +MRB_API mrb_int +mrb_float_id(mrb_float f) +{ + /* normalize -0.0 to 0.0 */ + if (f == 0) f = 0.0; + return make_num_id((const char*)&f, sizeof(f)); +} +#endif + +MRB_API mrb_int mrb_obj_id(mrb_value obj) { mrb_int tt = mrb_type(obj); @@ -125,17 +114,16 @@ mrb_obj_id(mrb_value obj) return MakeID(0); /* not define */ case MRB_TT_FALSE: if (mrb_nil_p(obj)) - return MakeID(1); - return MakeID(0); + return MakeID(4); + else + return MakeID(0); case MRB_TT_TRUE: - return MakeID(1); + return MakeID(2); case MRB_TT_SYMBOL: return MakeID(mrb_symbol(obj)); - case MRB_TT_FIXNUM: -#ifdef MRB_WITHOUT_FLOAT - return MakeID(mrb_fixnum_id(mrb_fixnum(obj))); -#else - return MakeID2(mrb_float_id((mrb_float)mrb_fixnum(obj)), MRB_TT_FLOAT); + case MRB_TT_INTEGER: + return MakeID(mrb_int_id(mrb_integer(obj))); +#ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: return MakeID(mrb_float_id(mrb_float(obj))); #endif @@ -150,7 +138,6 @@ mrb_obj_id(mrb_value obj) case MRB_TT_HASH: case MRB_TT_RANGE: case MRB_TT_EXCEPTION: - case MRB_TT_FILE: case MRB_TT_DATA: case MRB_TT_ISTRUCT: default: @@ -158,56 +145,53 @@ mrb_obj_id(mrb_value obj) } } +#if defined(MRB_NAN_BOXING) && defined(MRB_64BIT) +#define mrb_xxx_boxing_cptr_value mrb_nan_boxing_cptr_value +#endif + #ifdef MRB_WORD_BOXING -#ifndef MRB_WITHOUT_FLOAT +#define mrb_xxx_boxing_cptr_value mrb_word_boxing_cptr_value + +#ifndef MRB_NO_FLOAT MRB_API mrb_value mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f) { - mrb_value v; + union mrb_value_ v; - v.value.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class); - v.value.fp->f = f; - return v; + v.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class); + v.fp->f = f; + MRB_SET_FROZEN_FLAG(v.bp); + return v.value; } +#endif /* MRB_NO_FLOAT */ MRB_API mrb_value -mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f) +mrb_word_boxing_int_value(mrb_state *mrb, mrb_int n) { - struct RFloat *nf = (struct RFloat *)mrb_malloc(mrb, sizeof(struct RFloat)); - nf->tt = MRB_TT_FLOAT; - nf->c = mrb->float_class; - nf->f = f; - return mrb_obj_value(nf); + if (FIXABLE(n)) return mrb_fixnum_value(n); + else { + union mrb_value_ v; + + v.p = mrb_obj_alloc(mrb, MRB_TT_INTEGER, mrb->integer_class); + v.ip->i = n; + MRB_SET_FROZEN_FLAG(v.ip); + return v.value; + } } -#endif /* MRB_WITHOUT_FLOAT */ +#endif /* MRB_WORD_BOXING */ +#if defined(MRB_WORD_BOXING) || (defined(MRB_NAN_BOXING) && defined(MRB_64BIT)) MRB_API mrb_value -mrb_word_boxing_cptr_value(mrb_state *mrb, void *p) +mrb_xxx_boxing_cptr_value(mrb_state *mrb, void *p) { mrb_value v; + struct RCptr *cptr = MRB_OBJ_ALLOC(mrb, MRB_TT_CPTR, mrb->object_class); - v.value.p = mrb_obj_alloc(mrb, MRB_TT_CPTR, mrb->object_class); - v.value.vp->p = p; + SET_OBJ_VALUE(v, cptr); + cptr->p = p; return v; } -#endif /* MRB_WORD_BOXING */ - -MRB_API mrb_bool -mrb_regexp_p(mrb_state *mrb, mrb_value v) -{ - if (mrb->flags & MRB_STATE_NO_REGEXP) { - return FALSE; - } - if ((mrb->flags & MRB_STATE_REGEXP) || mrb_class_defined(mrb, REGEXP_CLASS)) { - mrb->flags |= MRB_STATE_REGEXP; - return mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS)); - } - else { - mrb->flags |= MRB_STATE_REGEXP; - mrb->flags |= MRB_STATE_NO_REGEXP; - } - return FALSE; -} +#endif #if defined _MSC_VER && _MSC_VER < 1900 diff --git a/src/ext/.gitkeep b/src/ext/.gitkeep deleted file mode 100644 index e69de29bb..000000000 --- a/src/ext/.gitkeep +++ /dev/null diff --git a/src/fmt_fp.c b/src/fmt_fp.c index 783cc6c02..43daf2307 100644 --- a/src/fmt_fp.c +++ b/src/fmt_fp.c @@ -1,388 +1,364 @@ -#ifndef MRB_WITHOUT_FLOAT -#ifdef MRB_DISABLE_STDIO -/* - -Most code in this file originates from musl (src/stdio/vfprintf.c) -which, just like mruby itself, is licensed under the MIT license. +#include <mruby.h> +#include <string.h> +#include <stdlib.h> -Copyright (c) 2005-2014 Rich Felker, et al. +#ifndef MRB_NO_FLOAT +/*********************************************************************** -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: + Routine for converting a single-precision + floating point number into a string. -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. + The code in this function was inspired from Fred Bayer's pdouble.c. + Since pdouble.c was released as Public Domain, I'm releasing this + code as public domain as well. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + Dave Hylands -*/ + The original code can be found in https://github.com/dhylands/format-float +***********************************************************************/ -#include <limits.h> -#include <string.h> -#include <stdint.h> -#include <math.h> -#include <float.h> -#include <ctype.h> +/*********************************************************************** -#include <mruby.h> -#include <mruby/string.h> + I modified the routine for mruby: -struct fmt_args { - mrb_state *mrb; - mrb_value str; -}; + * support `double` + * support `#` (alt_form) modifier -#define MAX(a,b) ((a)>(b) ? (a) : (b)) -#define MIN(a,b) ((a)<(b) ? (a) : (b)) + My modifications in this file are also placed in the public domain. -/* Convenient bit representation for modifier flags, which all fall - * within 31 codepoints of the space character. */ + Matz (Yukihiro Matsumoto) -#define ALT_FORM (1U<<('#'-' ')) -#define ZERO_PAD (1U<<('0'-' ')) -#define LEFT_ADJ (1U<<('-'-' ')) -#define PAD_POS (1U<<(' '-' ')) -#define MARK_POS (1U<<('+'-' ')) +***********************************************************************/ -static void -out(struct fmt_args *f, const char *s, size_t l) -{ - mrb_str_cat(f->mrb, f->str, s, l); -} +#include <math.h> -#define PAD_SIZE 256 -static void -pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint8_t fl) -{ - char pad[PAD_SIZE]; - if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return; - l = w - l; - memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l); - for (; l >= PAD_SIZE; l -= PAD_SIZE) - out(f, pad, PAD_SIZE); - out(f, pad, l); -} +#ifdef MRB_USE_FLOAT32 -static const char xdigits[16] = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' -}; +// 1 sign bit, 8 exponent bits, and 23 mantissa bits. +// exponent values 0 and 255 are reserved, exponent can be 1 to 254. +// exponent is stored with a bias of 127. +// The min and max floats are on the order of 1x10^37 and 1x10^-37 -static char* -fmt_u(uint32_t x, char *s) -{ - for (; x; x /= 10) *--s = '0' + x % 10; - return s; -} +#define FLT_DECEXP 32 +#define FLT_ROUND_TO_ONE 0.9999995F +#define FLT_MIN_BUF_SIZE 6 // -9e+99 -/* Do not override this check. The floating point printing code below - * depends on the float.h constants being right. If they are wrong, it - * may overflow the stack. */ -#if LDBL_MANT_DIG == 53 -typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)]; -#endif +#else -static int -fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) -{ - uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion - + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion - uint32_t *a, *d, *r, *z; - uint32_t i; - int e2=0, e, j; - ptrdiff_t l; - char buf[9+LDBL_MANT_DIG/4], *s; - const char *prefix="-0X+0X 0X-0x+0x 0x"; - ptrdiff_t pl; - char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr; - - pl=1; - if (signbit(y)) { - y=-y; - } else if (fl & MARK_POS) { - prefix+=3; - } else if (fl & PAD_POS) { - prefix+=6; - } else prefix++, pl=0; - - if (!isfinite(y)) { - const char *ss = (t&32)?"inf":"INF"; - if (y!=y) ss=(t&32)?"nan":"NAN"; - pad(f, ' ', 0, 3+pl, fl&~ZERO_PAD); - out(f, prefix, pl); - out(f, ss, 3); - pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ); - return 3+(int)pl; - } +// 1 sign bit, 11 exponent bits, and 52 mantissa bits. - y = frexp((double)y, &e2) * 2; - if (y) e2--; +#define FLT_DECEXP 256 +#define FLT_ROUND_TO_ONE 0.999999999995 +#define FLT_MIN_BUF_SIZE 7 // -9e+199 - if ((t|32)=='a') { - long double round = 8.0; - ptrdiff_t re; +#endif /* MRB_USE_FLOAT32 */ - if (t&32) prefix += 9; - pl += 2; +static const mrb_float g_pos_pow[] = { +#ifndef MRB_USE_FLOAT32 + 1e256, 1e128, 1e64, +#endif + 1e32, 1e16, 1e8, 1e4, 1e2, 1e1 +}; +static const mrb_float g_neg_pow[] = { +#ifndef MRB_USE_FLOAT32 + 1e-256, 1e-128, 1e-64, +#endif + 1e-32, 1e-16, 1e-8, 1e-4, 1e-2, 1e-1 +}; - if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0; - else re=LDBL_MANT_DIG/4-1-p; +/* + * mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) + * + * fmt: should be one of 'e', 'E', 'f', 'F', 'g', or 'G'. (|0x80 for '#') + * prec: is the precision (as specified in printf) + * sign: should be '\0', '+', or ' ' ('\0' is the normal one - only print + * a sign if ```f``` is negative. Anything else is printed as the + * sign character for positive numbers. + */ + +int +mrb_format_float(mrb_float f, char *buf, size_t buf_size, char fmt, int prec, char sign) { + char *s = buf; + int buf_remaining = buf_size - 1; + int alt_form = 0; + + if ((uint8_t)fmt & 0x80) { + fmt &= 0x7f; /* turn off alt_form flag */ + alt_form = 1; + } + if (buf_size <= FLT_MIN_BUF_SIZE) { + // Smallest exp notion is -9e+99 (-9e+199) which is 6 (7) chars plus terminating + // null. - if (re) { - while (re--) round*=16; - if (*prefix=='-') { - y=-y; - y-=round; - y+=round; - y=-y; - } - else { - y+=round; - y-=round; - } + if (buf_size >= 2) { + *s++ = '?'; + } + if (buf_size >= 1) { + *s++ = '\0'; + } + return buf_size >= 2; + } + if (signbit(f)) { + *s++ = '-'; + f = -f; + } else if (sign) { + *s++ = sign; + } + buf_remaining -= (s - buf); // Adjust for sign + + { + char uc = fmt & 0x20; + if (isinf(f)) { + *s++ = 'I' ^ uc; + *s++ = 'N' ^ uc; + *s++ = 'F' ^ uc; + goto ret; + } else if (isnan(f)) { + *s++ = 'N' ^ uc; + *s++ = 'A' ^ uc; + *s++ = 'N' ^ uc; + ret: + *s = '\0'; + return s - buf; } + } - estr=fmt_u(e2<0 ? -e2 : e2, ebuf); - if (estr==ebuf) *--estr='0'; - *--estr = (e2<0 ? '-' : '+'); - *--estr = t+('p'-'a'); - - s=buf; - do { - int x=(int)y; - *s++=xdigits[x]|(t&32); - y=16*(y-x); - if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.'; - } while (y); - - if (p && s-buf-2 < p) - l = (p+2) + (ebuf-estr); - else - l = (s-buf) + (ebuf-estr); - - pad(f, ' ', 0, pl+l, fl); - out(f, prefix, pl); - pad(f, '0', 0, pl+l, fl^ZERO_PAD); - out(f, buf, s-buf); - pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0); - out(f, estr, ebuf-estr); - pad(f, ' ', 0, pl+l, fl^LEFT_ADJ); - return (int)pl+(int)l; + if (prec < 0) { + prec = 6; } - if (p<0) p=6; + char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt + fmt |= 0x20; // Force fmt to be lowercase + char org_fmt = fmt; + if (fmt == 'g' && prec == 0) { + prec = 1; + } + int e, e1; + int dec = 0; + char e_sign = '\0'; + int num_digits = 0; + const mrb_float *pos_pow = g_pos_pow; + const mrb_float *neg_pow = g_neg_pow; + + if (f == 0.0) { + e = 0; + if (fmt == 'e') { + e_sign = '+'; + } else if (fmt == 'f') { + num_digits = prec + 1; + } + } else if (f < 1.0) { // f < 1.0 + char first_dig = '0'; + if (f >= FLT_ROUND_TO_ONE) { + first_dig = '1'; + } - if (y) y *= 268435456.0, e2-=28; + // Build negative exponent + for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) { + if (*neg_pow > f) { + e += e1; + f *= *pos_pow; + } + } + char e_sign_char = '-'; + if (f < 1.0) { + if (f >= FLT_ROUND_TO_ONE) { + f = 1.0; + if (e == 0) { + e_sign_char = '+'; + } + } else { + e++; + f *= 10.0; + } + } - if (e2<0) a=r=z=big; - else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1; + // If the user specified 'g' format, and e is <= 4, then we'll switch + // to the fixed format ('f') - do { - *z = (uint32_t)y; - y = 1000000000*(y-*z++); - } while (y); + if (fmt == 'f' || (fmt == 'g' && e <= 4)) { + fmt = 'f'; + dec = -1; + *s++ = first_dig; - while (e2>0) { - uint32_t carry=0; - int sh=MIN(29,e2); - for (d=z-1; d>=a; d--) { - uint64_t x = ((uint64_t)*d<<sh)+carry; - *d = x % 1000000000; - carry = (uint32_t)(x / 1000000000); + if (org_fmt == 'g') { + prec += (e - 1); + } + // truncate precision to prevent buffer overflow + if (prec + 2 > buf_remaining) { + prec = buf_remaining - 2; + } + num_digits = prec; + if (num_digits || alt_form) { + *s++ = '.'; + while (--e && num_digits) { + *s++ = '0'; + num_digits--; + } + } + } else { + // For e & g formats, we'll be printing the exponent, so set the + // sign. + e_sign = e_sign_char; + dec = 0; + + if (prec > (buf_remaining - FLT_MIN_BUF_SIZE)) { + prec = buf_remaining - FLT_MIN_BUF_SIZE; + if (fmt == 'g') { + prec++; + } + } } - if (carry) *--a = carry; - while (z>a && !z[-1]) z--; - e2-=sh; - } - while (e2<0) { - uint32_t carry=0, *b; - int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9; - for (d=a; d<z; d++) { - uint32_t rm = *d & ((1<<sh)-1); - *d = (*d>>sh) + carry; - carry = (1000000000>>sh) * rm; + } else { + // Build positive exponent + for (e = 0, e1 = FLT_DECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) { + if (*pos_pow <= f) { + e += e1; + f *= *neg_pow; + } } - if (!*a) a++; - if (carry) *z++ = carry; - /* Avoid (slow!) computation past requested precision */ - b = (t|32)=='f' ? r : a; - if (z-b > need) z = b+need; - e2+=sh; - } - if (a<z) for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++); - else e=0; - - /* Perform rounding: j is precision after the radix (possibly neg) */ - j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p); - if (j < 9*(z-r-1)) { - uint32_t x; - /* We avoid C's broken division of negative numbers */ - d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP); - j += 9*LDBL_MAX_EXP; - j %= 9; - for (i=10, j++; j<9; i*=10, j++); - x = *d % i; - /* Are there any significant digits past j? */ - if (x || d+1!=z) { - long double round = 2/LDBL_EPSILON; - long double small; - if (*d/i & 1) round += 2; - if (x<i/2) small=0.5; - else if (x==i/2 && d+1==z) small=1.0; - else small=1.5; - if (pl && *prefix=='-') round*=-1, small*=-1; - *d -= x; - /* Decide whether to round by probing round+small */ - if (round+small != round) { - *d = *d + i; - while (*d > 999999999) { - *d--=0; - if (d<a) *--a=0; - (*d)++; + // If the user specified fixed format (fmt == 'f') and e makes the + // number too big to fit into the available buffer, then we'll + // switch to the 'e' format. + + if (fmt == 'f') { + if (e >= buf_remaining) { + fmt = 'e'; + } else if ((e + prec + 2) > buf_remaining) { + prec = buf_remaining - e - 2; + if (prec < 0) { + // This means no decimal point, so we can add one back + // for the decimal. + prec++; } - for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++); } } - if (z>d+1) z=d+1; - } - for (; z>a && !z[-1]; z--); - - if ((t|32)=='g') { - if (!p) p++; - if (p>e && e>=-4) { - t--; - p-=e+1; + if (fmt == 'e' && prec > (buf_remaining - 6)) { + prec = buf_remaining - 6; } - else { - t-=2; - p--; + // If the user specified 'g' format, and e is < prec, then we'll switch + // to the fixed format. + + if (fmt == 'g' && e < prec) { + fmt = 'f'; + prec -= (e + 1); } - if (!(fl&ALT_FORM)) { - /* Count trailing zeros in last place */ - if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++); - else j=9; - if ((t|32)=='f') - p = MIN(p,MAX(0,9*(z-r-1)-j)); - else - p = MIN(p,MAX(0,9*(z-r-1)+e-j)); + if (fmt == 'f') { + dec = e; + num_digits = prec + e + 1; + } else { + e_sign = '+'; } } - l = 1 + p + (p || (fl&ALT_FORM)); - if ((t|32)=='f') { - if (e>0) l+=e; - } - else { - estr=fmt_u(e<0 ? -e : e, ebuf); - while(ebuf-estr<2) *--estr='0'; - *--estr = (e<0 ? '-' : '+'); - *--estr = t; - l += ebuf-estr; + if (prec < 0) { + // This can happen when the prec is trimmed to prevent buffer overflow + prec = 0; } - pad(f, ' ', 0, pl+l, fl); - out(f, prefix, pl); - pad(f, '0', 0, pl+l, fl^ZERO_PAD); - - if ((t|32)=='f') { - if (a>r) a=r; - for (d=a; d<=r; d++) { - char *ss = fmt_u(*d, buf+9); - if (d!=a) while (ss>buf) *--ss='0'; - else if (ss==buf+9) *--ss='0'; - out(f, ss, buf+9-ss); + // We now have f as a floating point number between >= 1 and < 10 + // (or equal to zero), and e contains the absolute value of the power of + // 10 exponent. and (dec + 1) == the number of dgits before the decimal. + + // For e, prec is # digits after the decimal + // For f, prec is # digits after the decimal + // For g, prec is the max number of significant digits + // + // For e & g there will be a single digit before the decimal + // for f there will be e digits before the decimal + + if (fmt == 'e') { + num_digits = prec + 1; + } else if (fmt == 'g') { + if (prec == 0) { + prec = 1; } - if (p || (fl&ALT_FORM)) out(f, ".", 1); - for (; d<z && p>0; d++, p-=9) { - char *ss = fmt_u(*d, buf+9); - while (ss>buf) *--ss='0'; - out(f, ss, MIN(9,p)); - } - pad(f, '0', p+9, 9, 0); + num_digits = prec; } - else { - if (z<=a) z=a+1; - for (d=a; d<z && p>=0; d++) { - char *ss = fmt_u(*d, buf+9); - if (ss==buf+9) *--ss='0'; - if (d!=a) while (ss>buf) *--ss='0'; - else { - out(f, ss++, 1); - if (p>0||(fl&ALT_FORM)) out(f, ".", 1); - } - out(f, ss, MIN(buf+9-ss, p)); - p -= (int)(buf+9-ss); - } - pad(f, '0', p+18, 18, 0); - out(f, estr, ebuf-estr); - } - - pad(f, ' ', 0, pl+l, fl^LEFT_ADJ); - return (int)pl+(int)l; -} - -static int -fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) -{ - ptrdiff_t p; - - if (*fmt != '%') { - return -1; + // Print the digits of the mantissa + for (int i = 0; i < num_digits; ++i, --dec) { + int8_t d = f; + *s++ = '0' + d; + if (dec == 0 && (prec > 0 || alt_form)) { + *s++ = '.'; + } + f -= (mrb_float)d; + f *= 10.0; } - ++fmt; - if (*fmt == '.') { - ++fmt; - for (p = 0; ISDIGIT(*fmt); ++fmt) { - p = 10 * p + (*fmt - '0'); + // Round + if (f >= 5.0) { + char *rs = s; + rs--; + while (1) { + if (*rs == '.') { + rs--; + continue; + } + if (*rs < '0' || *rs > '9') { + // + or - + rs++; // So we sit on the digit to the right of the sign + break; + } + if (*rs < '9') { + (*rs)++; + break; + } + *rs = '0'; + if (rs == buf) { + break; + } + rs--; + } + if (*rs == '0') { + // We need to insert a 1 + if (rs[1] == '.' && fmt != 'f') { + // We're going to round 9.99 to 10.00 + // Move the decimal point + rs[0] = '.'; + rs[1] = '0'; + if (e_sign == '-') { + e--; + } else { + e++; + } + } + s++; + char *ss = s; + while (ss > rs) { + *ss = ss[-1]; + ss--; + } + *rs = '1'; + if (f < 1.0 && fmt == 'f') { + // We rounded up to 1.0 + prec--; + } } - } - else { - p = -1; } - switch (*fmt) { - case 'e': case 'f': case 'g': case 'a': - case 'E': case 'F': case 'G': case 'A': - return fmt_fp(f, flo, p, 0, *fmt); - default: - return -1; + if (org_fmt == 'g' && prec > 0 && !alt_form) { + // Remove trailing zeros and a trailing decimal point + while (s[-1] == '0') { + s--; + } + if (s[-1] == '.') { + s--; + } } -} - -mrb_value -mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) -{ - struct fmt_args f; - - f.mrb = mrb; - f.str = mrb_str_new_capa(mrb, 24); - if (fmt_core(&f, fmt, mrb_float(flo)) < 0) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string"); + // Append the exponent + if (e_sign) { + *s++ = e_char; + *s++ = e_sign; + if (e >= 100) { + *s++ = '0' + (e / 100); + e %= 100; + } + *s++ = '0' + (e / 10); + *s++ = '0' + (e % 10); } - return f.str; -} -#else /* MRB_DISABLE_STDIO */ -#include <mruby.h> -#include <stdio.h> - -mrb_value -mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) -{ - char buf[24]; + *s = '\0'; - snprintf(buf, sizeof(buf), fmt, mrb_float(flo)); - return mrb_str_new_cstr(mrb, buf); + return s - buf; } -#endif /* MRB_DISABLE_STDIO */ #endif @@ -6,10 +6,14 @@ #include <string.h> #include <stdlib.h> +#ifdef MRB_USE_MALLOC_TRIM +#include <malloc.h> +#endif #include <mruby.h> #include <mruby/array.h> #include <mruby/class.h> #include <mruby/data.h> +#include <mruby/istruct.h> #include <mruby/hash.h> #include <mruby/proc.h> #include <mruby/range.h> @@ -18,6 +22,7 @@ #include <mruby/gc.h> #include <mruby/error.h> #include <mruby/throw.h> +#include <mruby/presym.h> /* = Tri-color Incremental Garbage Collection @@ -34,9 +39,14 @@ * Gray - Marked, But the child objects are unmarked. * Black - Marked, the child objects are also marked. + Extra color + + * Red - Static (ROM object) no need to be collected. + - All child objects should be Red as well. + == Two White Types - There're two white color types in a flip-flop fashion: White-A and White-B, + There are two white color types in a flip-flop fashion: White-A and White-B, which respectively represent the Current White color (the newly allocated objects in the current GC cycle) and the Sweep Target White color (the dead objects to be swept). @@ -98,8 +108,14 @@ struct free_obj { struct RBasic *next; }; +struct RVALUE_initializer { + MRB_OBJECT_HEADER; + char padding[sizeof(void*) * 4 - sizeof(uint32_t)]; +}; + typedef struct { union { + struct RVALUE_initializer init; /* must be first member to ensure initialization */ struct free_obj free; struct RBasic basic; struct RObject object; @@ -109,12 +125,14 @@ typedef struct { struct RHash hash; struct RRange range; struct RData data; + struct RIStruct istruct; struct RProc proc; struct REnv env; + struct RFiber fiber; struct RException exc; struct RBreak brk; #ifdef MRB_WORD_BOXING -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT struct RFloat floatv; #endif struct RCptr cptr; @@ -182,8 +200,10 @@ gettimeofday_time(void) #define GC_WHITE_A 1 #define GC_WHITE_B (1 << 1) #define GC_BLACK (1 << 2) +#define GC_RED MRB_GC_RED #define GC_WHITES (GC_WHITE_A | GC_WHITE_B) #define GC_COLOR_MASK 7 +mrb_static_assert1(MRB_GC_RED <= GC_COLOR_MASK); #define paint_gray(o) ((o)->color = GC_GRAY) #define paint_black(o) ((o)->color = GC_BLACK) @@ -191,13 +211,16 @@ gettimeofday_time(void) #define paint_partial_white(s, o) ((o)->color = (s)->current_white_part) #define is_gray(o) ((o)->color == GC_GRAY) #define is_white(o) ((o)->color & GC_WHITES) -#define is_black(o) ((o)->color & GC_BLACK) +#define is_black(o) ((o)->color == GC_BLACK) +#define is_red(o) ((o)->color == GC_RED) #define flip_white_part(s) ((s)->current_white_part = other_white_part(s)) #define other_white_part(s) ((s)->current_white_part ^ GC_WHITES) #define is_dead(s, o) (((o)->color & other_white_part(s) & GC_WHITES) || (o)->tt == MRB_TT_FREE) #define objects(p) ((RVALUE *)p->objects) +mrb_noreturn void mrb_raise_nomemory(mrb_state *mrb); + MRB_API void* mrb_realloc_simple(mrb_state *mrb, void *p, size_t len) { @@ -220,14 +243,8 @@ mrb_realloc(mrb_state *mrb, void *p, size_t len) p2 = mrb_realloc_simple(mrb, p, 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); */ - } - else { - mrb->gc.out_of_memory = TRUE; - mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); - } + mrb->gc.out_of_memory = TRUE; + mrb_raise_nomemory(mrb); } else { mrb->gc.out_of_memory = FALSE; @@ -274,9 +291,37 @@ mrb_free(mrb_state *mrb, void *p) (mrb->allocf)(mrb, p, 0, mrb->allocf_ud); } +MRB_API void* +mrb_alloca(mrb_state *mrb, size_t size) +{ + struct RString *s; + s = MRB_OBJ_ALLOC(mrb, MRB_TT_STRING, mrb->string_class); + return s->as.heap.ptr = (char*)mrb_malloc(mrb, size); +} + +static mrb_bool +heap_p(mrb_gc *gc, struct RBasic *object) +{ + mrb_heap_page* page; + + page = gc->heaps; + while (page) { + RVALUE *p; + + p = objects(page); + if (&p[0].as.basic <= object && object <= &p[MRB_HEAP_PAGE_SIZE].as.basic) { + return TRUE; + } + page = page->next; + } + return FALSE; +} + MRB_API mrb_bool mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) { - return is_dead(&mrb->gc, object); + mrb_gc *gc = &mrb->gc; + if (!heap_p(gc, object)) return TRUE; + return is_dead(gc, object); } static void @@ -376,7 +421,7 @@ mrb_gc_init(mrb_state *mrb, mrb_gc *gc) static void obj_free(mrb_state *mrb, struct RBasic *obj, int end); -void +static void free_heap(mrb_state *mrb, mrb_gc *gc) { mrb_heap_page *page = gc->heaps; @@ -430,7 +475,7 @@ mrb_gc_protect(mrb_state *mrb, mrb_value obj) gc_protect(mrb, &mrb->gc, mrb_basic_ptr(obj)); } -#define GC_ROOT_NAME "_gc_root_" +#define GC_ROOT_SYM MRB_SYM(_gc_root_) /* mrb_gc_register() keeps the object from GC. @@ -443,10 +488,13 @@ mrb_gc_protect(mrb_state *mrb, mrb_value obj) MRB_API void mrb_gc_register(mrb_state *mrb, mrb_value obj) { - mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME); - mrb_value table = mrb_gv_get(mrb, root); + mrb_sym root; + mrb_value table; - if (mrb_nil_p(table) || mrb_type(table) != MRB_TT_ARRAY) { + if (mrb_immediate_p(obj)) return; + root = GC_ROOT_SYM; + table = mrb_gv_get(mrb, root); + if (mrb_nil_p(table) || !mrb_array_p(table)) { table = mrb_ary_new(mrb); mrb_gv_set(mrb, root, table); } @@ -457,20 +505,23 @@ mrb_gc_register(mrb_state *mrb, mrb_value obj) MRB_API void mrb_gc_unregister(mrb_state *mrb, mrb_value obj) { - mrb_sym root = mrb_intern_lit(mrb, GC_ROOT_NAME); - mrb_value table = mrb_gv_get(mrb, root); + mrb_sym root; + mrb_value table; struct RArray *a; mrb_int i; + if (mrb_immediate_p(obj)) return; + root = GC_ROOT_SYM; + table = mrb_gv_get(mrb, root); if (mrb_nil_p(table)) return; - if (mrb_type(table) != MRB_TT_ARRAY) { + if (!mrb_array_p(table)) { mrb_gv_set(mrb, root, mrb_nil_value()); return; } a = mrb_ary_ptr(table); mrb_ary_modify(mrb, a); for (i = 0; i < ARY_LEN(a); i++) { - if (mrb_obj_eq(mrb, ARY_PTR(a)[i], obj)) { + if (mrb_ptr(ARY_PTR(a)[i]) == mrb_ptr(obj)) { mrb_int len = ARY_LEN(a)-1; mrb_value *ptr = ARY_PTR(a); @@ -485,7 +536,7 @@ MRB_API struct RBasic* mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls) { struct RBasic *p; - static const RVALUE RVALUE_zero = { { { MRB_TT_FALSE } } }; + static const RVALUE RVALUE_zero = { { { NULL, NULL, MRB_TT_FALSE } } }; mrb_gc *gc = &mrb->gc; if (cls) { @@ -506,9 +557,12 @@ mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls) ttype != MRB_TT_ICLASS && ttype != MRB_TT_ENV && ttype != tt) { - mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %S", mrb_obj_value(cls)); + mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C", cls); } } + if (ttype <= MRB_TT_FREE) { + mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C (type %d)", cls, (int)ttype); + } #ifdef MRB_GC_STRESS mrb_full_gc(mrb); @@ -548,11 +602,11 @@ add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) gc->gray_list = obj; } -static int +static mrb_int ci_nregs(mrb_callinfo *ci) { - struct RProc *p = ci->proc; - int n = 0; + const struct RProc *p = ci->proc; + mrb_int n = 0; if (!p) { if (ci->argc < 0) return 3; @@ -577,11 +631,14 @@ mark_context_stack(mrb_state *mrb, struct mrb_context *c) size_t e; mrb_value nil; - if (c->stack == NULL) return; - e = c->stack - c->stbase; + if (c->stbase == NULL) return; if (c->ci) { + e = (c->ci->stack ? c->ci->stack - c->stbase : 0); e += ci_nregs(c->ci); } + else { + e = 0; + } if (c->stbase + e > c->stend) e = c->stend - c->stbase; for (i=0; i<e; i++) { mrb_value v = c->stbase[i]; @@ -600,7 +657,6 @@ mark_context_stack(mrb_state *mrb, struct mrb_context *c) static void mark_context(mrb_state *mrb, struct mrb_context *c) { - int i; mrb_callinfo *ci; start: @@ -612,15 +668,10 @@ mark_context(mrb_state *mrb, struct mrb_context *c) /* mark call stack */ if (c->cibase) { for (ci = c->cibase; ci <= c->ci; ci++) { - mrb_gc_mark(mrb, (struct RBasic*)ci->env); mrb_gc_mark(mrb, (struct RBasic*)ci->proc); - mrb_gc_mark(mrb, (struct RBasic*)ci->target_class); + mrb_gc_mark(mrb, (struct RBasic*)ci->u.target_class); } } - /* mark ensure stack */ - for (i=0; i<c->eidx; i++) { - mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]); - } /* mark fibers */ mrb_gc_mark(mrb, (struct RBasic*)c->fib); if (c->prev) { @@ -634,7 +685,6 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { mrb_assert(is_gray(obj)); paint_black(obj); - gc->gray_list = obj->gcnext; mrb_gc_mark(mrb, (struct RBasic*)obj->c); switch (obj->tt) { case MRB_TT_ICLASS: @@ -677,10 +727,10 @@ 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) && e->cxt && e->cxt->fib) { + if (MRB_ENV_ONSTACK_P(e) && e->cxt && e->cxt->fib) { mrb_gc_mark(mrb, (struct RBasic*)e->cxt->fib); } - len = MRB_ENV_STACK_LEN(e); + len = MRB_ENV_LEN(e); for (i=0; i<len; i++) { mrb_gc_mark_value(mrb, e->stack[i]); } @@ -698,10 +748,11 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) case MRB_TT_ARRAY: { struct RArray *a = (struct RArray*)obj; - size_t i, e; + size_t i, e=ARY_LEN(a); + mrb_value *p = ARY_PTR(a); - for (i=0,e=ARY_LEN(a); i<e; i++) { - mrb_gc_mark_value(mrb, ARY_PTR(a)[i]); + for (i=0; i<e; i++) { + mrb_gc_mark_value(mrb, p[i]); } } break; @@ -712,20 +763,21 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) break; case MRB_TT_STRING: - if (RSTR_FSHARED_P(obj) && !RSTR_NOFREE_P(obj)) { + if (RSTR_FSHARED_P(obj)) { struct RString *s = (struct RString*)obj; mrb_gc_mark(mrb, (struct RBasic*)s->as.heap.aux.fshared); } break; case MRB_TT_RANGE: - { - struct RRange *r = (struct RRange*)obj; + mrb_gc_mark_range(mrb, (struct RRange*)obj); + break; - if (r->edges) { - mrb_gc_mark_value(mrb, r->edges->beg); - mrb_gc_mark_value(mrb, r->edges->end); - } + case MRB_TT_BREAK: + { + struct RBreak *brk = (struct RBreak*)obj; + mrb_gc_mark(mrb, (struct RBasic*)mrb_break_proc_get(brk)); + mrb_gc_mark_value(mrb, mrb_break_value_get(brk)); } break; @@ -739,6 +791,7 @@ mrb_gc_mark(mrb_state *mrb, struct RBasic *obj) { if (obj == 0) return; if (!is_white(obj)) return; + if (is_red(obj)) return; mrb_assert((obj)->tt != MRB_TT_FREE); add_gray_list(mrb, &mrb->gc, obj); } @@ -748,22 +801,6 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) { DEBUG(fprintf(stderr, "obj_free(%p,tt=%d)\n",obj,obj->tt)); switch (obj->tt) { - /* immediate - no mark */ - case MRB_TT_TRUE: - case MRB_TT_FIXNUM: - case MRB_TT_SYMBOL: - /* cannot happen */ - return; - -#ifndef MRB_WITHOUT_FLOAT - case MRB_TT_FLOAT: -#ifdef MRB_WORD_BOXING - break; -#else - return; -#endif -#endif - case MRB_TT_OBJECT: mrb_gc_free_iv(mrb, (struct RObject*)obj); break; @@ -777,18 +814,23 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) case MRB_TT_SCLASS: mrb_gc_free_mt(mrb, (struct RClass*)obj); mrb_gc_free_iv(mrb, (struct RObject*)obj); + if (!end) + mrb_mc_clear_by_class(mrb, (struct RClass*)obj); break; case MRB_TT_ICLASS: if (MRB_FLAG_TEST(obj, MRB_FL_CLASS_IS_ORIGIN)) mrb_gc_free_mt(mrb, (struct RClass*)obj); + if (!end) + mrb_mc_clear_by_class(mrb, (struct RClass*)obj); break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; - if (MRB_ENV_STACK_SHARED_P(e)) { + if (MRB_ENV_ONSTACK_P(e)) { /* cannot be freed */ - return; + e->stack = NULL; + break; } mrb_free(mrb, e->stack); e->stack = NULL; @@ -800,14 +842,14 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) struct mrb_context *c = ((struct RFiber*)obj)->cxt; if (c && c != mrb->root_c) { - mrb_callinfo *ci = c->ci; - mrb_callinfo *ce = c->cibase; + if (!end && c->status != MRB_FIBER_TERMINATED) { + mrb_callinfo *ci = c->ci; + mrb_callinfo *ce = c->cibase; - if (!end) { while (ce <= ci) { - struct REnv *e = ci->env; - if (e && !is_dead(&mrb->gc, e) && - e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) { + struct REnv *e = ci->u.env; + if (e && !mrb_object_dead_p(mrb, (struct RBasic*)e) && + e->tt == MRB_TT_ENV && MRB_ENV_ONSTACK_P(e)) { mrb_env_unshare(mrb, e); } ci--; @@ -839,7 +881,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) struct RProc *p = (struct RProc*)obj; if (!MRB_PROC_CFUNC_P(p) && p->body.irep) { - mrb_irep *irep = p->body.irep; + mrb_irep *irep = (mrb_irep*)p->body.irep; if (end) { mrb_irep_cutref(mrb, irep); } @@ -849,7 +891,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) break; case MRB_TT_RANGE: - mrb_free(mrb, ((struct RRange*)obj)->edges); + mrb_gc_free_range(mrb, ((struct RRange*)obj)); break; case MRB_TT_DATA: @@ -862,6 +904,24 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) } break; +#if defined(MRB_USE_RATIONAL) && defined(MRB_INT64) && defined(MRB_32BIT) + case MRB_TT_RATIONAL: + { + struct RData *o = (struct RData*)obj; + mrb_free(mrb, o->iv); + } + break; +#endif + +#if defined(MRB_USE_COMPLEX) && defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32) + case MRB_TT_COMPLEX: + { + struct RData *o = (struct RData*)obj; + mrb_free(mrb, o->iv); + } + break; +#endif + default: break; } @@ -895,10 +955,10 @@ root_scan_phase(mrb_state *mrb, mrb_gc *gc) mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->range_class); -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class); #endif - mrb_gc_mark(mrb, (struct RBasic*)mrb->fixnum_class); + mrb_gc_mark(mrb, (struct RBasic*)mrb->integer_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->true_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->false_class); mrb_gc_mark(mrb, (struct RBasic*)mrb->nil_class); @@ -925,13 +985,12 @@ root_scan_phase(mrb_state *mrb, mrb_gc *gc) } } +/* rough estimation of number of GC marks (non recursive) */ static size_t -gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) +gc_gray_counts(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { size_t children = 0; - gc_mark_children(mrb, gc, obj); - switch (obj->tt) { case MRB_TT_ICLASS: children++; @@ -956,7 +1015,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) break; case MRB_TT_ENV: - children += MRB_ENV_STACK_LEN(obj); + children += MRB_ENV_LEN(obj); break; case MRB_TT_FIBER: @@ -968,7 +1027,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) if (!c || c->status == MRB_FIBER_TERMINATED) break; /* mark stack */ - i = c->stack - c->stbase; + i = c->ci->stack - c->stbase; if (c->ci) { i += ci_nregs(c->ci); @@ -976,9 +1035,6 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) if (c->stbase + i > c->stend) i = c->stend - c->stbase; children += i; - /* mark ensure stack */ - children += c->eidx; - /* mark closure */ if (c->cibase) { for (i=0, ci = c->cibase; ci <= c->ci; i++, ci++) @@ -1002,6 +1058,7 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) case MRB_TT_PROC: case MRB_TT_RANGE: + case MRB_TT_BREAK: children+=2; break; @@ -1015,10 +1072,9 @@ gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) static void gc_mark_gray_list(mrb_state *mrb, mrb_gc *gc) { while (gc->gray_list) { - if (is_gray(gc->gray_list)) - gc_mark_children(mrb, gc, gc->gray_list); - else - gc->gray_list = gc->gray_list->gcnext; + struct RBasic *obj = gc->gray_list; + gc->gray_list = obj->gcnext; + gc_mark_children(mrb, gc, obj); } } @@ -1029,7 +1085,10 @@ incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) size_t tried_marks = 0; while (gc->gray_list && tried_marks < limit) { - tried_marks += gc_gray_mark(mrb, gc, gc->gray_list); + struct RBasic *obj = gc->gray_list; + gc->gray_list = obj->gcnext; + gc_mark_children(mrb, gc, obj); + tried_marks += gc_gray_counts(mrb, gc, obj); } return tried_marks; @@ -1046,7 +1105,9 @@ final_marking_phase(mrb_state *mrb, mrb_gc *gc) } mrb_gc_mark_gv(mrb); mark_context(mrb, mrb->c); - mark_context(mrb, mrb->root_c); + if (mrb->c != mrb->root_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); @@ -1261,6 +1322,7 @@ mrb_full_gc(mrb_state *mrb) { mrb_gc *gc = &mrb->gc; + if (!mrb->c) return; if (gc->disabled || gc->iterating) return; GC_INVOKE_TIME_REPORT("mrb_full_gc()"); @@ -1284,6 +1346,9 @@ mrb_full_gc(mrb_state *mrb) gc->full = FALSE; } +#ifdef MRB_USE_MALLOC_TRIM + malloc_trim(0); +#endif GC_TIME_STOP_AND_REPORT; } @@ -1568,17 +1633,21 @@ mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, vo } } -#ifdef GC_TEST -#ifdef GC_DEBUG -static mrb_value gc_test(mrb_state *, mrb_value); -#endif -#endif +size_t +mrb_objspace_page_slot_size(void) +{ + return sizeof(RVALUE); +} + void mrb_init_gc(mrb_state *mrb) { struct RClass *gc; + mrb_static_assert(sizeof(RVALUE) <= sizeof(void*) * 6, + "RVALUE size must be within 6 words"); + gc = mrb_define_module(mrb, "GC"); mrb_define_class_method(mrb, gc, "start", gc_start, MRB_ARGS_NONE()); @@ -1590,278 +1659,4 @@ mrb_init_gc(mrb_state *mrb) mrb_define_class_method(mrb, gc, "step_ratio=", gc_step_ratio_set, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, gc, "generational_mode=", gc_generational_mode_set, MRB_ARGS_REQ(1)); mrb_define_class_method(mrb, gc, "generational_mode", gc_generational_mode_get, MRB_ARGS_NONE()); -#ifdef GC_TEST -#ifdef GC_DEBUG - mrb_define_class_method(mrb, gc, "test", gc_test, MRB_ARGS_NONE()); -#endif -#endif -} - -#ifdef GC_TEST -#ifdef GC_DEBUG -void -test_mrb_field_write_barrier(void) -{ - mrb_state *mrb = mrb_open(); - struct RBasic *obj, *value; - mrb_gc *gc = &mrb->gc; - - puts("test_mrb_field_write_barrier"); - gc->generational = FALSE; - obj = mrb_basic_ptr(mrb_ary_new(mrb)); - value = mrb_basic_ptr(mrb_str_new_lit(mrb, "value")); - paint_black(obj); - paint_partial_white(gc, value); - - - puts(" in MRB_GC_STATE_MARK"); - gc->state = MRB_GC_STATE_MARK; - mrb_field_write_barrier(mrb, obj, value); - - mrb_assert(is_gray(value)); - - - puts(" in MRB_GC_STATE_SWEEP"); - paint_partial_white(gc, value); - gc->state = MRB_GC_STATE_SWEEP; - mrb_field_write_barrier(mrb, obj, value); - - mrb_assert(obj->color & gc->current_white_part); - mrb_assert(value->color & gc->current_white_part); - - - puts(" fail with black"); - gc->state = MRB_GC_STATE_MARK; - paint_white(obj); - paint_partial_white(gc, value); - mrb_field_write_barrier(mrb, obj, value); - - mrb_assert(obj->color & gc->current_white_part); - - - puts(" fail with gray"); - gc->state = MRB_GC_STATE_MARK; - paint_black(obj); - paint_gray(value); - mrb_field_write_barrier(mrb, obj, value); - - mrb_assert(is_gray(value)); - - - { - puts("test_mrb_field_write_barrier_value"); - obj = mrb_basic_ptr(mrb_ary_new(mrb)); - mrb_value value = mrb_str_new_lit(mrb, "value"); - paint_black(obj); - paint_partial_white(gc, mrb_basic_ptr(value)); - - gc->state = MRB_GC_STATE_MARK; - mrb_field_write_barrier_value(mrb, obj, value); - - mrb_assert(is_gray(mrb_basic_ptr(value))); - } - - mrb_close(mrb); -} - -void -test_mrb_write_barrier(void) -{ - mrb_state *mrb = mrb_open(); - struct RBasic *obj; - mrb_gc *gc = &mrb->gc; - - puts("test_mrb_write_barrier"); - obj = mrb_basic_ptr(mrb_ary_new(mrb)); - paint_black(obj); - - puts(" in MRB_GC_STATE_MARK"); - gc->state = MRB_GC_STATE_MARK; - mrb_write_barrier(mrb, obj); - - mrb_assert(is_gray(obj)); - mrb_assert(gc->atomic_gray_list == obj); - - - puts(" fail with gray"); - paint_gray(obj); - mrb_write_barrier(mrb, obj); - - mrb_assert(is_gray(obj)); - - mrb_close(mrb); -} - -void -test_add_gray_list(void) -{ - mrb_state *mrb = mrb_open(); - struct RBasic *obj1, *obj2; - mrb_gc *gc = &mrb->gc; - - puts("test_add_gray_list"); - change_gen_gc_mode(mrb, gc, FALSE); - mrb_assert(gc->gray_list == NULL); - obj1 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test")); - add_gray_list(mrb, gc, obj1); - mrb_assert(gc->gray_list == obj1); - mrb_assert(is_gray(obj1)); - - obj2 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test")); - add_gray_list(mrb, gc, obj2); - mrb_assert(gc->gray_list == obj2); - mrb_assert(gc->gray_list->gcnext == obj1); - mrb_assert(is_gray(obj2)); - - mrb_close(mrb); -} - -void -test_gc_gray_mark(void) -{ - mrb_state *mrb = mrb_open(); - mrb_value obj_v, value_v; - struct RBasic *obj; - size_t gray_num = 0; - mrb_gc *gc = &mrb->gc; - - puts("test_gc_gray_mark"); - - puts(" in MRB_TT_CLASS"); - obj = (struct RBasic*)mrb->object_class; - paint_gray(obj); - gray_num = gc_gray_mark(mrb, gc, obj); - mrb_assert(is_black(obj)); - mrb_assert(gray_num > 1); - - puts(" in MRB_TT_ARRAY"); - obj_v = mrb_ary_new(mrb); - value_v = mrb_str_new_lit(mrb, "test"); - paint_gray(mrb_basic_ptr(obj_v)); - paint_partial_white(gc, mrb_basic_ptr(value_v)); - mrb_ary_push(mrb, obj_v, value_v); - gray_num = gc_gray_mark(mrb, gc, mrb_basic_ptr(obj_v)); - mrb_assert(is_black(mrb_basic_ptr(obj_v))); - mrb_assert(is_gray(mrb_basic_ptr(value_v))); - mrb_assert(gray_num == 1); - - mrb_close(mrb); -} - -void -test_incremental_gc(void) -{ - mrb_state *mrb = mrb_open(); - size_t max = ~0, live = 0, total = 0, freed = 0; - RVALUE *free; - mrb_heap_page *page; - mrb_gc *gc = &mrb->gc; - - puts("test_incremental_gc"); - change_gen_gc_mode(mrb, gc, FALSE); - - puts(" in mrb_full_gc"); - mrb_full_gc(mrb); - - mrb_assert(gc->state == MRB_GC_STATE_ROOT); - puts(" in MRB_GC_STATE_ROOT"); - incremental_gc(mrb, gc, max); - mrb_assert(gc->state == MRB_GC_STATE_MARK); - puts(" in MRB_GC_STATE_MARK"); - incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP); - mrb_assert(gc->state == MRB_GC_STATE_SWEEP); - - puts(" in MRB_GC_STATE_SWEEP"); - page = gc->heaps; - while (page) { - RVALUE *p = objects(page); - RVALUE *e = p + MRB_HEAP_PAGE_SIZE; - while (p<e) { - if (is_black(&p->as.basic)) { - live++; - } - if (is_gray(&p->as.basic) && !is_dead(gc, &p->as.basic)) { - printf("%p\n", &p->as.basic); - } - p++; - } - page = page->next; - total += MRB_HEAP_PAGE_SIZE; - } - - mrb_assert(gc->gray_list == NULL); - - incremental_gc(mrb, gc, max); - mrb_assert(gc->state == MRB_GC_STATE_SWEEP); - - incremental_gc(mrb, gc, max); - mrb_assert(gc->state == MRB_GC_STATE_ROOT); - - free = (RVALUE*)gc->heaps->freelist; - while (free) { - freed++; - free = (RVALUE*)free->as.free.next; - } - - mrb_assert(gc->live == live); - mrb_assert(gc->live == total-freed); - - puts("test_incremental_gc(gen)"); - incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP); - change_gen_gc_mode(mrb, gc, TRUE); - - mrb_assert(gc->full == FALSE); - mrb_assert(gc->state == MRB_GC_STATE_ROOT); - - puts(" in minor"); - mrb_assert(is_minor_gc(gc)); - mrb_assert(gc->majorgc_old_threshold > 0); - gc->majorgc_old_threshold = 0; - mrb_incremental_gc(mrb); - mrb_assert(gc->full == TRUE); - mrb_assert(gc->state == MRB_GC_STATE_ROOT); - - puts(" in major"); - mrb_assert(is_major_gc(gc)); - do { - mrb_incremental_gc(mrb); - } while (gc->state != MRB_GC_STATE_ROOT); - mrb_assert(gc->full == FALSE); - - mrb_close(mrb); -} - -void -test_incremental_sweep_phase(void) -{ - mrb_state *mrb = mrb_open(); - mrb_gc *gc = &mrb->gc; - - puts("test_incremental_sweep_phase"); - - add_heap(mrb, gc); - gc->sweeps = gc->heaps; - - mrb_assert(gc->heaps->next->next == NULL); - mrb_assert(gc->free_heaps->next->next == NULL); - incremental_sweep_phase(mrb, gc, MRB_HEAP_PAGE_SIZE * 3); - - mrb_assert(gc->heaps->next == NULL); - mrb_assert(gc->heaps == gc->free_heaps); - - mrb_close(mrb); -} - -static mrb_value -gc_test(mrb_state *mrb, mrb_value self) -{ - test_mrb_field_write_barrier(); - test_mrb_write_barrier(); - test_add_gray_list(); - test_gc_gray_mark(); - test_incremental_gc(); - test_incremental_sweep_phase(); - return mrb_nil_value(); } -#endif /* GC_DEBUG */ -#endif /* GC_TEST */ diff --git a/src/hash.c b/src/hash.c index e1ceec489..918722a2b 100644 --- a/src/hash.c +++ b/src/hash.c @@ -4,682 +4,1208 @@ ** See Copyright Notice in mruby.h */ +#include <string.h> #include <mruby.h> #include <mruby/array.h> #include <mruby/class.h> #include <mruby/hash.h> #include <mruby/string.h> #include <mruby/variable.h> +#include <mruby/presym.h> -#ifndef MRB_WITHOUT_FLOAT -/* a function to get hash value of a float number */ -mrb_int mrb_float_id(mrb_float f); +/* + * === Glossary + * + * [EA] + * Entry Array. Store `Hash' entries in insertion order. + * + * [AR] + * Array Table Implementation. The structure of `Hash` that doesn't have a + * hash table and linearly searches EA. It is used when `Hash` size <= 16. + * + * [IB] + * Index Buckets. The buckets of hash table, where the bucket value is EA + * index. The index is represented by variable length bits according to + * the capacity. + * + * [HT] + * Hash Table Implementation. The structure of `Hash` that has IB and is + * searched by hash table algorithm. It is used when `Hash` size > 16. + * Collision resolution strategy is open addressing method. + * + * [size] + * The number of `Hash` entries (value of `Hash#size`). + * + * [slot] + * The generic term for EA or IB elements. + * + * [active] + * The state in which a slot is recognized as a `Hash` entry. + * + * [deleted] + * The state in which a slot is marked as deleted. + * + * [used] + * The state in which a slot is active or deleted. + * + * [empty] + * The state in which a slot is not used. Capacity is equal to the sum of + * the number of used slots and the number of empty slots. + */ + +#define EA_N_RESERVED_INDICES 2 /* empty and deleted */ +#define EA_INCREASE_RATIO 6 / 5 + 6 +#define EA_MAX_INCREASE UINT16_MAX +#define EA_MAX_CAPA U32(lesser(IB_MAX_CAPA - EA_N_RESERVED_INDICES, MRB_INT_MAX)) +#define IB_MAX_CAPA (U32(1) << IB_MAX_BIT) +#define IB_TYPE_BIT 32 +#define IB_INIT_BIT ( \ + ib_upper_bound_for(32) <= AR_MAX_SIZE ? 6 : \ + ib_upper_bound_for(16) <= AR_MAX_SIZE ? 5 : \ + 4 \ +) +#define IB_MAX_BIT (IB_TYPE_BIT - 1) +#define AR_DEFAULT_CAPA 4 +#define AR_MAX_SIZE 16 +#define H_MAX_SIZE EA_MAX_CAPA + +mrb_static_assert1(offsetof(struct RHash, iv) == offsetof(struct RObject, iv)); +mrb_static_assert1(AR_MAX_SIZE < (1 << MRB_HASH_AR_EA_CAPA_BIT)); + +typedef struct hash_entry { + mrb_value key; + mrb_value val; +} hash_entry; + +typedef struct hash_table { + hash_entry *ea; +#ifdef MRB_32BIT + uint32_t ea_capa; + uint32_t ea_n_used; #endif + uint32_t ib[]; +} hash_table; + +typedef struct index_buckets_iter { + struct RHash *h; + uint32_t bit; + uint32_t mask; + uint32_t pos; + uint32_t ary_index; + uint32_t ea_index; + uint32_t shift1; + uint32_t shift2; + uint32_t step; +} index_buckets_iter; -/* return non zero to break the loop */ -typedef int (sg_foreach_func)(mrb_state *mrb,mrb_value key, mrb_value val, void *data); +/* + * `c_` :: receiver class (category) + * `n_` :: attribute name + * `t_` :: attribute type + * `p_` :: struct member path + * `k_` :: macro key + */ +#define DEFINE_GETTER(c_, n_, t_, p_) \ + MRB_INLINE t_ c_##_##n_(const struct RHash *h) {return h->p_;} +#define DEFINE_SETTER(c_, n_, t_, p_) \ + MRB_INLINE void c_##_set_##n_(struct RHash *h, t_ v) {h->p_ = v;} +#define DEFINE_ACCESSOR(c_, n_, t_, p_) \ + DEFINE_GETTER(c_, n_, t_, p_) \ + DEFINE_SETTER(c_, n_, t_, p_) +#define DEFINE_FLAG_GETTER(c_, n_, t_, k_) \ + MRB_INLINE t_ c_##_##n_(const struct RHash *h) { \ + return (t_)((h->flags & MRB_HASH_##k_##_MASK) >> MRB_HASH_##k_##_SHIFT); \ + } +#define DEFINE_FLAG_SETTER(c_, n_, t_, k_) \ + MRB_INLINE void c_##_set_##n_(struct RHash *h, t_ v) { \ + h->flags &= ~MRB_HASH_##k_##_MASK; \ + h->flags |= v << MRB_HASH_##k_##_SHIFT; \ + } +#define DEFINE_FLAG_ACCESSOR(c_, n_, t_, k_) \ + DEFINE_FLAG_GETTER(c_, n_, t_, k_) \ + DEFINE_FLAG_SETTER(c_, n_, t_, k_) +#define DEFINE_INCREMENTER(c_, n_) \ + MRB_INLINE void c_##_inc_##n_(struct RHash *h) { \ + c_##_set_##n_(h, c_##_##n_(h) + 1); \ + } +#define DEFINE_DECREMENTER(c_, n_) \ + MRB_INLINE void c_##_dec_##n_(struct RHash *h) { \ + c_##_set_##n_(h, c_##_##n_(h) - 1); \ + } +#define DEFINE_SWITCHER(n_, k_) \ + MRB_INLINE void h_##n_##_on(struct RHash *h) { \ + h->flags |= MRB_HASH_##k_; \ + } \ + MRB_INLINE void h_##n_##_off(struct RHash *h) { \ + h->flags &= ~MRB_HASH_##k_; \ + } \ + MRB_INLINE mrb_bool h_##n_##_p(const struct RHash *h) { \ + return (h->flags & MRB_HASH_##k_) == MRB_HASH_##k_; \ + } -#ifndef MRB_SG_SEGMENT_SIZE -#define MRB_SG_SEGMENT_SIZE 5 +#ifdef MRB_64BIT +DEFINE_ACCESSOR(ar, ea_capa, uint32_t, ea_capa) +DEFINE_ACCESSOR(ar, ea_n_used, uint32_t, ea_n_used) +DEFINE_ACCESSOR(ht, ea_capa, uint32_t, ea_capa) +DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, ea_n_used) +#else +DEFINE_FLAG_ACCESSOR(ar, ea_capa, uint32_t, AR_EA_CAPA) +DEFINE_FLAG_ACCESSOR(ar, ea_n_used, uint32_t, AR_EA_N_USED) +DEFINE_ACCESSOR(ht, ea_capa, uint32_t, hsh.ht->ea_capa) +DEFINE_ACCESSOR(ht, ea_n_used, uint32_t, hsh.ht->ea_n_used) #endif +DEFINE_FLAG_ACCESSOR(ib, bit, uint32_t, IB_BIT) +DEFINE_ACCESSOR(ar, size, uint32_t, size) +DEFINE_ACCESSOR(ar, ea, hash_entry*, hsh.ea) +DEFINE_DECREMENTER(ar, size) +DEFINE_ACCESSOR(ht, size, uint32_t, size) +DEFINE_ACCESSOR(ht, ea, hash_entry*, hsh.ht->ea) +DEFINE_GETTER(ht, ib, uint32_t*, hsh.ht->ib) +DEFINE_INCREMENTER(ht, size) +DEFINE_DECREMENTER(ht, size) +DEFINE_GETTER(h, size, uint32_t, size) +DEFINE_ACCESSOR(h, ht, hash_table*, hsh.ht) +DEFINE_SWITCHER(ht, HT) + +#define ea_each_used(ea, n_used, entry_var, code) do { \ + hash_entry *entry_var = ea, *ea_end__ = entry_var + (n_used); \ + for (; entry_var < ea_end__; ++entry_var) { \ + code; \ + } \ +} while (0) -struct segkv { - mrb_value key; - mrb_value val; -}; - -typedef struct segment { - struct segment *next; - struct segkv e[MRB_SG_SEGMENT_SIZE]; -} segment; - -typedef struct segindex { - size_t size; - size_t capa; - struct segkv *table[]; -} segindex; - -/* Instance variable table structure */ -typedef struct seglist { - segment *rootseg; - segment *lastseg; - mrb_int size; - mrb_int last_len; - segindex *index; -} seglist; - -static /* inline */ size_t -sg_hash_func(mrb_state *mrb, seglist *t, mrb_value key) +#define ea_each(ea, size, entry_var, code) do { \ + hash_entry *entry_var = ea; \ + uint32_t size__ = size; \ + for (; 0 < size__; ++entry_var) { \ + if (entry_deleted_p(entry_var)) continue; \ + --size__; \ + code; \ + } \ +} while (0) + +#define ib_cycle_by_key(mrb, h, key, it_var, code) do { \ + index_buckets_iter it_var[1]; \ + ib_it_init(mrb, it_var, h, key); \ + for (;;) { \ + ib_it_next(it_var); \ + code; \ + } \ +} while (0) + +#define ib_find_by_key(mrb, h_, key_, it_var, code) do { \ + mrb_value ib_fbk_key__ = key_; \ + ib_cycle_by_key(mrb, h_, ib_fbk_key__, it_var, { \ + if (ib_it_empty_p(it_var)) break; \ + if (ib_it_deleted_p(it_var)) continue; \ + if (obj_eql(mrb, ib_fbk_key__, ib_it_entry(it_var)->key, it_var->h)) { \ + code; \ + break; \ + } \ + }); \ +} while (0) + +#define h_each(h, entry_var, code) do { \ + struct RHash *h__ = h; \ + hash_entry *h_e_ea__; \ + uint32_t h_e_size__; \ + h_ar_p(h) ? (h_e_ea__ = ar_ea(h__), h_e_size__ = ar_size(h__)) : \ + (h_e_ea__ = ht_ea(h__), h_e_size__ = ht_size(h__)); \ + ea_each(h_e_ea__, h_e_size__, entry_var, code); \ +} while (0) + +/* + * In `h_check_modified()`, in the case of `MRB_NO_BOXING`, `ht_ea()` or + * `ht_ea_capa()` for AR may read uninitialized area (#5332). Therefore, do + * not use those macros for AR in `MRB_NO_BOXING` (but in the case of + * `MRB_64BIT`, `ht_ea_capa()` is the same as `ar_ea_capa()`, so use it). + */ +#ifdef MRB_NO_BOXING +# define H_CHECK_MODIFIED_USE_HT_EA_FOR_AR FALSE +# ifdef MRB_64BIT +# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR TRUE +# else +# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR FALSE +# endif /* MRB_64BIT */ +#else +# define H_CHECK_MODIFIED_USE_HT_EA_FOR_AR TRUE +# define H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR TRUE + /* + * `h_check_modified` raises an exception when a dangerous modification is + * made to `h` by executing `code`. + * + * `h_check_modified` macro is not called if `h->hsh.ht` (`h->hsh.ea`) is `NULL` + * (`Hash` size is zero). And because the `hash_entry` is rather large, + * `h->hsh.ht->ea` and `h->hsh.ht->ea_capa` are able to be safely accessed even for + * AR. This nature is used to eliminate branch of AR or HT. + * + * `HT_ASSERT_SAFE_READ` checks if members can be accessed according to its + * assumptions. + */ +# define HT_ASSERT_SAFE_READ(attr_name) \ + mrb_static_assert1( \ + offsetof(hash_table, attr_name) + sizeof(((hash_table*)0)->attr_name) <= \ + sizeof(hash_entry)) +HT_ASSERT_SAFE_READ(ea); +# ifdef MRB_32BIT +HT_ASSERT_SAFE_READ(ea_capa); +# endif +# undef HT_ASSERT_SAFE_READ +#endif /* MRB_NO_BOXING */ + +/* + * `h_check_modified` raises an exception when a dangerous modification is + * made to `h` by executing `code`. + */ +#define h_check_modified(mrb, h, code) do { \ + struct RHash *h__ = h; \ + uint32_t mask__ = MRB_HASH_HT|MRB_HASH_IB_BIT_MASK|MRB_HASH_AR_EA_CAPA_MASK; \ + uint32_t flags__ = h__->flags & mask__; \ + void* tbl__ = (mrb_assert(h__->hsh.ht), h__->hsh.ht); \ + uint32_t ht_ea_capa__ = 0; \ + hash_entry *ht_ea__ = NULL; \ + if (H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR || h_ht_p(h__)) { \ + ht_ea_capa__ = ht_ea_capa(h__); \ + } \ + if (H_CHECK_MODIFIED_USE_HT_EA_FOR_AR || h_ht_p(h__)) { \ + ht_ea__ = ht_ea(h__); \ + } \ + code; \ + if (flags__ != (h__->flags & mask__) || \ + tbl__ != h__->hsh.ht || \ + ((H_CHECK_MODIFIED_USE_HT_EA_CAPA_FOR_AR || h_ht_p(h__)) && \ + ht_ea_capa__ != ht_ea_capa(h__)) || \ + ((H_CHECK_MODIFIED_USE_HT_EA_FOR_AR || h_ht_p(h__)) && \ + ht_ea__ != ht_ea(h__))) { \ + mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified"); \ + } \ +} while (0) + +#define U32(v) ((uint32_t)(v)) +#define h_ar_p(h) (!h_ht_p(h)) +#define h_ar_on(h) h_ht_off(h) +#define lesser(a, b) ((a) < (b) ? (a) : (b)) +#define RHASH_IFNONE(hash) mrb_iv_get(mrb, (hash), MRB_SYM(ifnone)) +#define RHASH_PROCDEFAULT(hash) RHASH_IFNONE(hash) + +static uint32_t ib_upper_bound_for(uint32_t capa); +static uint32_t ib_bit_to_capa(uint32_t bit); +static void ht_init( + mrb_state *mrb, struct RHash *h, uint32_t size, + hash_entry *ea, uint32_t ea_capa, hash_table *ht, uint32_t ib_bit); +static void ht_set_without_ib_adjustment( + mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val); + +static uint32_t +next_power2(uint32_t v) { - enum mrb_vtype tt = mrb_type(key); - mrb_value hv; - size_t h; - segindex *index = t->index; - size_t capa = index ? index->capa : 0; + mrb_assert(v != 0); +#ifdef __GNUC__ + return U32(1) << ((sizeof(unsigned) * CHAR_BIT) - __builtin_clz(v)); +#else + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + ++v; + return v; +#endif +} +static uint32_t +obj_hash_code(mrb_state *mrb, mrb_value key, struct RHash *h) +{ + enum mrb_vtype tt = mrb_type(key); + uint32_t hash_code; + mrb_value hash_code_obj; switch (tt) { case MRB_TT_STRING: - h = mrb_str_hash(mrb, key); + hash_code = mrb_str_hash(mrb, key); break; - case MRB_TT_TRUE: case MRB_TT_FALSE: case MRB_TT_SYMBOL: - case MRB_TT_FIXNUM: -#ifndef MRB_WITHOUT_FLOAT + case MRB_TT_INTEGER: +#ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: #endif - h = (size_t)mrb_obj_id(key); + hash_code = U32(mrb_obj_id(key)); break; - default: - hv = mrb_funcall(mrb, key, "hash", 0); - h = (size_t)t ^ (size_t)mrb_fixnum(hv); + h_check_modified(mrb, h, { + hash_code_obj = mrb_funcall_argv(mrb, key, MRB_SYM(hash), 0, NULL); + }); + + hash_code = U32(tt) ^ U32(mrb_integer(hash_code_obj)); break; } - if (index && (index != t->index || capa != index->capa)) { - mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified"); - } - return ((h)^((h)<<2)^((h)>>2)); + return hash_code ^ (hash_code << 2) ^ (hash_code >> 2); } -static inline mrb_bool -sg_hash_equal(mrb_state *mrb, seglist *t, mrb_value a, mrb_value b) +static mrb_bool +obj_eql(mrb_state *mrb, mrb_value a, mrb_value b, struct RHash *h) { enum mrb_vtype tt = mrb_type(a); + mrb_bool eql; switch (tt) { case MRB_TT_STRING: return mrb_str_equal(mrb, a, b); case MRB_TT_SYMBOL: - if (mrb_type(b) != MRB_TT_SYMBOL) return FALSE; + if (!mrb_symbol_p(b)) return FALSE; return mrb_symbol(a) == mrb_symbol(b); - case MRB_TT_FIXNUM: - switch (mrb_type(b)) { - case MRB_TT_FIXNUM: - return mrb_fixnum(a) == mrb_fixnum(b); -#ifndef MRB_WITHOUT_FLOAT - case MRB_TT_FLOAT: - return (mrb_float)mrb_fixnum(a) == mrb_float(b); -#endif - default: - return FALSE; - } + case MRB_TT_INTEGER: + if (!mrb_integer_p(b)) return FALSE; + return mrb_integer(a) == mrb_integer(b); -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: - switch (mrb_type(b)) { - case MRB_TT_FIXNUM: - return mrb_float(a) == (mrb_float)mrb_fixnum(b); - case MRB_TT_FLOAT: - return mrb_float(a) == mrb_float(b); - default: - return FALSE; - } + if (!mrb_float_p(b)) return FALSE; + return mrb_float(a) == mrb_float(b); #endif default: - { - segindex *index = t->index; - size_t capa = index ? index->capa : 0; - mrb_bool eql = mrb_eql(mrb, a, b); - if (index && (index != t->index || capa != index->capa)) { - mrb_raise(mrb, E_RUNTIME_ERROR, "hash modified"); - } - return eql; - } - } + h_check_modified(mrb, h, {eql = mrb_eql(mrb, a, b);}); + return eql; + } } -/* Creates the instance variable table. */ -static seglist* -sg_new(mrb_state *mrb) +static mrb_bool +entry_deleted_p(const hash_entry* entry) { - seglist *t; + return mrb_undef_p(entry->key); +} - t = (seglist*)mrb_malloc(mrb, sizeof(seglist)); - t->size = 0; - t->rootseg = NULL; - t->lastseg = NULL; - t->last_len = 0; - t->index = NULL; +static void +entry_delete(hash_entry* entry) +{ + entry->key = mrb_undef_value(); +} - return t; +static uint32_t +ea_next_capa_for(uint32_t size, uint32_t max_capa) +{ + if (size < AR_DEFAULT_CAPA) { + return AR_DEFAULT_CAPA; + } + else { + /* + * For 32-bit CPU, the theoretical value of maximum EA capacity is + * `UINT32_MAX / sizeof (hash_entry)`. At this time, if + * `EA_INCREASE_RATIO` is the current value, 32-bit range will not be + * exceeded during the calculation of `capa`, so `size_t` is used. + */ + size_t capa = (size_t)size * EA_INCREASE_RATIO, inc = capa - size; + if (EA_MAX_INCREASE < inc) capa = size + EA_MAX_INCREASE; + return capa <= max_capa ? U32(capa) : max_capa; + } } -#define power2(v) do { \ - v--;\ - v |= v >> 1;\ - v |= v >> 2;\ - v |= v >> 4;\ - v |= v >> 8;\ - v |= v >> 16;\ - v++;\ -} while (0) +static hash_entry* +ea_resize(mrb_state *mrb, hash_entry *ea, uint32_t capa) +{ + return (hash_entry*)mrb_realloc(mrb, ea, sizeof(hash_entry) * capa); +} -#ifndef UPPER_BOUND -#define UPPER_BOUND(x) ((x)>>2|(x)>>1) -#endif +static void +ea_compress(hash_entry *ea, uint32_t n_used) +{ + hash_entry *w_entry = ea; + ea_each_used(ea, n_used, r_entry, { + if (entry_deleted_p(r_entry)) continue; + if (r_entry != w_entry) *w_entry = *r_entry; + ++w_entry; + }); +} + +/* + * Increase or decrease capacity of `ea` to a standard size that can + * accommodate `*capap + 1` entries (but, not exceed `max_capa`). Set the + * changed capacity to `*capap` and return a pointer to `mrb_realloc`ed EA. + */ +static hash_entry* +ea_adjust(mrb_state *mrb, hash_entry *ea, uint32_t *capap, uint32_t max_capa) +{ + *capap = ea_next_capa_for(*capap, max_capa); + return ea_resize(mrb, ea, *capap); +} -#define SG_MASK(index) ((index->capa)-1) +static hash_entry* +ea_dup(mrb_state *mrb, const hash_entry *ea, uint32_t capa) +{ + size_t byte_size = sizeof(hash_entry) * capa; + hash_entry *new_ea = (hash_entry*)mrb_malloc(mrb, byte_size); + return (hash_entry*)memcpy(new_ea, ea, byte_size); +} + +static hash_entry* +ea_get_by_key(mrb_state *mrb, hash_entry *ea, uint32_t size, mrb_value key, + struct RHash *h) +{ + ea_each(ea, size, entry, { + if (obj_eql(mrb, key, entry->key, h)) return entry; + }); + return NULL; +} + +static hash_entry* +ea_get(hash_entry *ea, uint32_t index) +{ + return &ea[index]; +} -/* Build index for the segment list */ static void -sg_index(mrb_state *mrb, seglist *t) -{ - size_t size = (size_t)t->size; - size_t mask; - segindex *index = t->index; - segment *seg; - size_t i; - - if (size < MRB_SG_SEGMENT_SIZE) { - if (index) { - failed: - mrb_free(mrb, index); - t->index = NULL; - } - return; - } - /* allocate index table */ - if (index && index->size >= UPPER_BOUND(index->capa)) { - size = index->capa+1; - } - power2(size); - if (!index || index->capa < size) { - index = (segindex*)mrb_realloc_simple(mrb, index, sizeof(segindex)+sizeof(struct segkv*)*size); - if (index == NULL) goto failed; - t->index = index; - } - index->size = t->size; - index->capa = size; - for (i=0; i<size; i++) { - index->table[i] = NULL; - } +ea_set(hash_entry *ea, uint32_t index, mrb_value key, mrb_value val) +{ + ea[index].key = key; + ea[index].val = val; +} - /* rebuld index */ - mask = SG_MASK(index); - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) { - mrb_value key; - size_t k, step = 0; +static void +ar_init(struct RHash *h, uint32_t size, + hash_entry *ea, uint32_t ea_capa, uint32_t ea_n_used) +{ + h_ar_on(h); + ar_set_size(h, size); + ar_set_ea(h, ea); + ar_set_ea_capa(h, ea_capa); + ar_set_ea_n_used(h, ea_n_used); +} - if (!seg->next && i >= (size_t)t->last_len) { - return; - } - key = seg->e[i].key; - if (mrb_undef_p(key)) continue; - k = sg_hash_func(mrb, t, key) & mask; - while (index->table[k]) { - k = (k+(++step)) & mask; - } - index->table[k] = &seg->e[i]; - } - seg = seg->next; - } +static void +ar_free(mrb_state *mrb, struct RHash *h) +{ + mrb_free(mrb, ar_ea(h)); } -/* Compacts the segment list removing deleted entries. */ static void -sg_compact(mrb_state *mrb, seglist *t) -{ - segment *seg; - mrb_int i; - segment *seg2 = NULL; - mrb_int i2; - mrb_int size = 0; - - if (t == NULL) return; - seg = t->rootseg; - if (t->index && (size_t)t->size == t->index->size) { - sg_index(mrb, t); - return; - } - while (seg) { - for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) { - mrb_value k = seg->e[i].key; +ar_adjust_ea(mrb_state *mrb, struct RHash *h, uint32_t size, uint32_t max_ea_capa) +{ + uint32_t ea_capa = size; + hash_entry *ea = ea_adjust(mrb, ar_ea(h), &ea_capa, max_ea_capa); + ar_set_ea(h, ea); + ar_set_ea_capa(h, ea_capa); +} - if (!seg->next && i >= t->last_len) { - goto exit; - } - if (mrb_undef_p(k)) { /* found delete key */ - if (seg2 == NULL) { - seg2 = seg; - i2 = i; +static void +ar_compress(mrb_state *mrb, struct RHash *h) +{ + uint32_t size = ar_size(h); + ea_compress(ar_ea(h), ar_ea_n_used(h)); + ar_set_ea_n_used(h, size); + ar_adjust_ea(mrb, h, size, lesser(ar_ea_capa(h), AR_MAX_SIZE)); +} + +static mrb_bool +ar_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) +{ + ea_each(ar_ea(h), ar_size(h), entry, { + if (!obj_eql(mrb, key, entry->key, h)) continue; + *valp = entry->val; + return TRUE; + }); + return FALSE; +} + +static void +ar_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val) +{ + uint32_t size = ar_size(h); + hash_entry *entry; + if ((entry = ea_get_by_key(mrb, ar_ea(h), size, key, h))) { + entry->val = val; + } + else { + uint32_t ea_capa = ar_ea_capa(h), ea_n_used = ar_ea_n_used(h); + if (ea_capa == ea_n_used) { + if (size == ea_n_used) { + if (size == AR_MAX_SIZE) { + hash_entry *ea = ea_adjust(mrb, ar_ea(h), &ea_capa, EA_MAX_CAPA); + ea_set(ea, ea_n_used, key, val); + ht_init(mrb, h, ++size, ea, ea_capa, NULL, IB_INIT_BIT); + return; + } + else { + ar_adjust_ea(mrb, h, size, AR_MAX_SIZE); } } else { - size++; - if (seg2 != NULL) { - seg2->e[i2++] = seg->e[i]; - if (i2 >= MRB_SG_SEGMENT_SIZE) { - seg2 = seg2->next; - i2 = 0; - } - } + ar_compress(mrb, h); + ea_n_used = size; } } - seg = seg->next; - } - exit: - /* reached at end */ - t->size = size; - if (seg2) { - seg = seg2->next; - seg2->next = NULL; - t->last_len = i2; - t->lastseg = seg2; - while (seg) { - seg2 = seg->next; - mrb_free(mrb, seg); - seg = seg2; - } - } - if (t->index) { - sg_index(mrb, t); + ea_set(ar_ea(h), ea_n_used, key, val); + ar_set_size(h, ++size); + ar_set_ea_n_used(h, ++ea_n_used); } } -/* Set the value for the key in the indexed segment list. */ +static mrb_bool +ar_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) +{ + hash_entry *entry = ea_get_by_key(mrb, ar_ea(h), ar_size(h), key, h); + if (!entry) return FALSE; + *valp = entry->val; + entry_delete(entry); + ar_dec_size(h); + return TRUE; +} + static void -sg_index_put(mrb_state *mrb, seglist *t, mrb_value key, mrb_value val) +ar_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp) { - segindex *index = t->index; - size_t k, sp, step = 0, mask; - segment *seg; + uint32_t size = ar_size(h); + ea_each(ar_ea(h), size, entry, { + *keyp = entry->key; + *valp = entry->val; + entry_delete(entry); + ar_set_size(h, --size); + return; + }); +} - if (index->size >= UPPER_BOUND(index->capa)) { - /* need to expand table */ - sg_compact(mrb, t); - index = t->index; - } - mask = SG_MASK(index); - sp = index->capa; - k = sg_hash_func(mrb, t, key) & mask; - while (index->table[k]) { - mrb_value key2 = index->table[k]->key; - if (mrb_undef_p(key2)) { - if (sp == index->capa) sp = k; +static void +ar_rehash(mrb_state *mrb, struct RHash *h) +{ + /* see comments in `h_rehash` */ + uint32_t size = ar_size(h), w_size = 0, ea_capa = ar_ea_capa(h); + hash_entry *ea = ar_ea(h), *w_entry; + ea_each(ea, size, r_entry, { + if ((w_entry = ea_get_by_key(mrb, ea, w_size, r_entry->key, h))) { + w_entry->val = r_entry->val; + ar_set_size(h, --size); + entry_delete(r_entry); } - else if (sg_hash_equal(mrb, t, key, key2)) { - index->table[k]->val = val; - return; + else { + if (w_size != U32(r_entry - ea)) { + ea_set(ea, w_size, r_entry->key, r_entry->val); + entry_delete(r_entry); + } + ++w_size; } - k = (k+(++step)) & mask; - } - if (sp < index->capa) { - k = sp; - } + }); + mrb_assert(size == w_size); + ar_set_ea_n_used(h, size); + ar_adjust_ea(mrb, h, size, ea_capa); +} - /* put the value at the last */ - seg = t->lastseg; - if (t->last_len < MRB_SG_SEGMENT_SIZE) { - index->table[k] = &seg->e[t->last_len++]; - } - else { /* append a new segment */ - seg->next = (segment*)mrb_malloc(mrb, sizeof(segment)); - seg = seg->next; - seg->next = NULL; - t->lastseg = seg; - t->last_len = 1; - index->table[k] = &seg->e[0]; - } - index->table[k]->key = key; - index->table[k]->val = val; - index->size++; - t->size++; +static uint32_t +ib_it_pos_for(index_buckets_iter *it, uint32_t v) +{ + return v & it->mask; } -/* Set the value for the key in the segment list. */ -static void -sg_put(mrb_state *mrb, seglist *t, mrb_value key, mrb_value val) +static uint32_t +ib_it_empty_value(const index_buckets_iter *it) { - segment *seg; - mrb_int i, deleted = 0; + return it->mask; +} - if (t == NULL) return; - if (t->index) { - sg_index_put(mrb, t, key, val); - return; - } - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) { - mrb_value k = seg->e[i].key; - /* Found room in last segment after last_len */ - if (!seg->next && i >= t->last_len) { - seg->e[i].key = key; - seg->e[i].val = val; - t->last_len = i+1; - t->size++; - return; - } - if (mrb_undef_p(k)) { - deleted++; - continue; - } - if (sg_hash_equal(mrb, t, k, key)) { - seg->e[i].val = val; - return; - } - } - seg = seg->next; - } +static uint32_t +ib_it_deleted_value(const index_buckets_iter *it) +{ + return it->mask - 1; +} - /* Not found */ - if (deleted > MRB_SG_SEGMENT_SIZE) { - sg_compact(mrb, t); - } - t->size++; - - seg = (segment*)mrb_malloc(mrb, sizeof(segment)); - seg->next = NULL; - seg->e[0].key = key; - seg->e[0].val = val; - t->last_len = 1; - if (t->rootseg == NULL) { - t->rootseg = seg; - } - else { - t->lastseg->next = seg; - } - t->lastseg = seg; - if (t->index == NULL && t->size > MRB_SG_SEGMENT_SIZE*4) { - sg_index(mrb, t); - } +static mrb_bool +ib_it_empty_p(const index_buckets_iter *it) +{ + return it->ea_index == ib_it_empty_value(it); } -/* Get a value for a key from the indexed segment list. */ static mrb_bool -sg_index_get(mrb_state *mrb, seglist *t, mrb_value key, mrb_value *vp) -{ - segindex *index = t->index; - size_t mask = SG_MASK(index); - size_t k = sg_hash_func(mrb, t, key) & mask; - size_t step = 0; - - while (index->table[k]) { - mrb_value key2 = index->table[k]->key; - if (!mrb_undef_p(key2) && sg_hash_equal(mrb, t, key, key2)) { - if (vp) *vp = index->table[k]->val; - return TRUE; - } - k = (k+(++step)) & mask; - } - return FALSE; +ib_it_deleted_p(const index_buckets_iter *it) +{ + return it->ea_index == ib_it_deleted_value(it); } -/* Get a value for a key from the segment list. */ static mrb_bool -sg_get(mrb_state *mrb, seglist *t, mrb_value key, mrb_value *vp) +ib_it_active_p(const index_buckets_iter *it) +{ + return it->ea_index < ib_it_deleted_value(it); +} + +static void +ib_it_init(mrb_state *mrb, index_buckets_iter *it, struct RHash *h, mrb_value key) { - segment *seg; - mrb_int i; + it->h = h; + it->bit = ib_bit(h); + it->mask = ib_bit_to_capa(it->bit) - 1; + it->pos = ib_it_pos_for(it, obj_hash_code(mrb, key, h)); + it->step = 0; +} - if (t == NULL) return FALSE; - if (t->index) { - return sg_index_get(mrb, t, key, vp); +static void +ib_it_next(index_buckets_iter *it) +{ + /* + * [IB image] + * + * ary_index(1) --. + * \ .-- shift1(3) .-- shift2(29) + * pos(6) --. \ / / + * View | \ \ <-o-> <----------o----------> + * -------- +---------------------\----\--+-----------------------------+----- + * array | 0 `--. `-|--- o 1 | ... + * +---------+---------+-----+\--+-----+---------+---------+---+----- + * buckets | 0 | 1 | ... | o 6 | 7 | 8 | ... + * +---------+---------+-----+=========+---------+---------+--------- + * bit set |1 1 1 0 0|0 0 0 1 1| ... |0 1 0 1 1|0 1 1 1 0|0 1 0 1 0| ... + * +---------+---------+-----+========*+---------+---------+--------- + * <---o---> \ + * \ `-- bit_pos(34) + * `-- bit(5) + */ + + /* Slide to handle as `capa == 32` to avoid 64-bit operations */ + uint32_t slid_pos = it->pos & (IB_TYPE_BIT - 1); + uint32_t slid_bit_pos = it->bit * (slid_pos + 1) - 1; + uint32_t slid_ary_index = slid_bit_pos / IB_TYPE_BIT; + it->ary_index = slid_ary_index + it->pos / IB_TYPE_BIT * it->bit; + it->shift2 = (slid_ary_index + 1) * IB_TYPE_BIT - slid_bit_pos - 1; + it->ea_index = (ht_ib(it->h)[it->ary_index] >> it->shift2) & it->mask; + if (IB_TYPE_BIT - it->bit < it->shift2) { + it->shift1 = IB_TYPE_BIT - it->shift2; + it->ea_index |= (ht_ib(it->h)[it->ary_index - 1] << it->shift1) & it->mask; + } + else { + it->shift1 = 0; } + it->pos = ib_it_pos_for(it, it->pos + (++it->step)); +} - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) { - mrb_value k = seg->e[i].key; +static uint32_t +ib_it_get(const index_buckets_iter *it) +{ + return it->ea_index; +} - if (!seg->next && i >= t->last_len) { - return FALSE; - } - if (mrb_undef_p(k)) continue; - if (sg_hash_equal(mrb, t, k, key)) { - if (vp) *vp = seg->e[i].val; - return TRUE; - } - } - seg = seg->next; +static void +ib_it_set(index_buckets_iter *it, uint32_t ea_index) +{ + uint32_t mask, i; + it->ea_index = ea_index; + if (it->shift1) { + i = it->ary_index - 1; + mask = it->mask >> it->shift1; + ht_ib(it->h)[i] = (ht_ib(it->h)[i] & ~mask) | (ea_index >> it->shift1); } - return FALSE; + i = it->ary_index; + mask = it->mask << it->shift2; + ht_ib(it->h)[i] = (ht_ib(it->h)[i] & ~mask) | (ea_index << it->shift2); +} + +static void +ib_it_delete(index_buckets_iter *it) +{ + ib_it_set(it, ib_it_deleted_value(it)); +} + +static hash_entry* +ib_it_entry(index_buckets_iter *it) +{ + return ea_get(ht_ea(it->h), it->ea_index); +} + +static uint32_t +ib_capa_to_bit(uint32_t capa) +{ +#ifdef __GNUC__ + return U32(__builtin_ctz(capa)); +#else + /* http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn */ + static const uint32_t MultiplyDeBruijnBitPosition2[] = { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; + return MultiplyDeBruijnBitPosition2[U32(capa * 0x077CB531U) >> 27]; +#endif +} + +static uint32_t +ib_bit_to_capa(uint32_t bit) +{ + return U32(1) << bit; +} + +static uint32_t +ib_upper_bound_for(uint32_t capa) +{ + return (capa >> 2) | (capa >> 1); /* 3/4 */ +} + +static uint32_t +ib_bit_for(uint32_t size) +{ + uint32_t capa = next_power2(size); + if (capa != IB_MAX_CAPA && ib_upper_bound_for(capa) < size) capa *= 2; + return ib_capa_to_bit(capa); +} + +static uint32_t +ib_byte_size_for(uint32_t ib_bit) +{ + mrb_assert(IB_INIT_BIT <= ib_bit); + uint32_t ary_size = IB_INIT_BIT == 4 ? + ib_bit_to_capa(ib_bit) * 2 / IB_TYPE_BIT * ib_bit / 2 : + ib_bit_to_capa(ib_bit) / IB_TYPE_BIT * ib_bit; + return U32(sizeof(uint32_t) * ary_size); +} + +static void +ib_init(mrb_state *mrb, struct RHash *h, uint32_t ib_bit, size_t ib_byte_size) +{ + hash_entry *ea = ht_ea(h); + memset(ht_ib(h), 0xff, ib_byte_size); + ib_set_bit(h, ib_bit); + ea_each_used(ea, ht_ea_n_used(h), entry, { + ib_cycle_by_key(mrb, h, entry->key, it, { + if (!ib_it_empty_p(it)) continue; + ib_it_set(it, U32(entry - ea)); + break; + }); + }); +} + +static void +ht_init(mrb_state *mrb, struct RHash *h, uint32_t size, + hash_entry *ea, uint32_t ea_capa, hash_table *ht, uint32_t ib_bit) +{ + size_t ib_byte_size = ib_byte_size_for(ib_bit); + size_t ht_byte_size = sizeof(hash_table) + ib_byte_size; + h_ht_on(h); + h_set_ht(h, (hash_table*)mrb_realloc(mrb, ht, ht_byte_size)); + ht_set_size(h, size); + ht_set_ea(h, ea); + ht_set_ea_capa(h, ea_capa); + ht_set_ea_n_used(h, size); + ib_init(mrb, h, ib_bit, ib_byte_size); +} + +static void +ht_free(mrb_state *mrb, struct RHash *h) +{ + mrb_free(mrb, ht_ea(h)); + mrb_free(mrb, h_ht(h)); +} + +static hash_table* +ht_dup(mrb_state *mrb, const struct RHash *h) +{ + size_t ib_byte_size = ib_byte_size_for(ib_bit(h)); + size_t ht_byte_size = sizeof(hash_table) + ib_byte_size; + hash_table *new_ht = (hash_table*)mrb_malloc(mrb, ht_byte_size); + return (hash_table*)memcpy(new_ht, h_ht(h), ht_byte_size); +} + +static void +ht_adjust_ea(mrb_state *mrb, struct RHash *h, uint32_t size, uint32_t max_ea_capa) +{ + uint32_t ea_capa = size; + hash_entry *ea = ea_adjust(mrb, ht_ea(h), &ea_capa, max_ea_capa); + ht_set_ea(h, ea); + ht_set_ea_capa(h, ea_capa); +} + +static void +ht_to_ar(mrb_state *mrb, struct RHash *h) +{ + uint32_t size = ht_size(h), ea_capa = size; + hash_entry *ea = ht_ea(h); + ea_compress(ea, ht_ea_n_used(h)); + ea = ea_adjust(mrb, ea, &ea_capa, AR_MAX_SIZE); + mrb_free(mrb, h_ht(h)); + ar_init(h, size, ea, ea_capa, size); } -/* Deletes the value for the symbol from the instance variable table. */ -/* Deletion is done by overwriting keys by `undef`. */ static mrb_bool -sg_del(mrb_state *mrb, seglist *t, mrb_value key, mrb_value *vp) +ht_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) { - segment *seg; - mrb_int i; + ib_find_by_key(mrb, h, key, it, { + *valp = ib_it_entry(it)->val; + return TRUE; + }); + return FALSE; +} - if (t == NULL) return FALSE; - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) { - mrb_value key2; +static void +ht_set_as_ar(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val) +{ + ht_to_ar(mrb, h); + ar_set(mrb, h, key, val); +} - if (!seg->next && i >= t->last_len) { - /* not found */ - return FALSE; +static void +ht_set_without_ib_adjustment(mrb_state *mrb, struct RHash *h, + mrb_value key, mrb_value val) +{ + mrb_assert(ht_size(h) < ib_bit_to_capa(ib_bit(h))); + ib_cycle_by_key(mrb, h, key, it, { + if (ib_it_active_p(it)) { + if (!obj_eql(mrb, key, ib_it_entry(it)->key, h)) continue; + ib_it_entry(it)->val = val; + } + else { + uint32_t ea_n_used = ht_ea_n_used(h); + if (ea_n_used == H_MAX_SIZE) { + mrb_assert(ht_size(h) == ea_n_used); + mrb_raise(mrb, E_ARGUMENT_ERROR, "hash too big"); } - key2 = seg->e[i].key; - if (!mrb_undef_p(key2) && sg_hash_equal(mrb, t, key, key2)) { - if (vp) *vp = seg->e[i].val; - seg->e[i].key = mrb_undef_value(); - t->size--; - return TRUE; + if (ea_n_used == ht_ea_capa(h)) ht_adjust_ea(mrb, h, ea_n_used, EA_MAX_CAPA); + ib_it_set(it, ea_n_used); + ea_set(ht_ea(h), ea_n_used, key, val); + ht_inc_size(h); + ht_set_ea_n_used(h, ++ea_n_used); + } + return; + }); +} + +static void +ht_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val) +{ + uint32_t size = ht_size(h); + uint32_t ib_bit_width = ib_bit(h), ib_capa = ib_bit_to_capa(ib_bit_width); + if (ib_upper_bound_for(ib_capa) <= size) { + if (size != ht_ea_n_used(h)) ea_compress(ht_ea(h), ht_ea_n_used(h)); + ht_init(mrb, h, size, ht_ea(h), ht_ea_capa(h), h_ht(h), ++ib_bit_width); + } + else if (size != ht_ea_n_used(h)) { + if (ib_capa - EA_N_RESERVED_INDICES <= ht_ea_n_used(h)) goto compress; + if (ht_ea_capa(h) == ht_ea_n_used(h)) { + if (size <= AR_MAX_SIZE) {ht_set_as_ar(mrb, h, key, val); return;} + if (ea_next_capa_for(size, EA_MAX_CAPA) <= ht_ea_capa(h)) { + compress: + ea_compress(ht_ea(h), ht_ea_n_used(h)); + ht_adjust_ea(mrb, h, size, ht_ea_capa(h)); + ht_init(mrb, h, size, ht_ea(h), ht_ea_capa(h), h_ht(h), ib_bit_width); } } - seg = seg->next; } + ht_set_without_ib_adjustment(mrb, h, key, val); +} + +static mrb_bool +ht_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) +{ + ib_find_by_key(mrb, h, key, it, { + hash_entry *entry = ib_it_entry(it); + *valp = entry->val; + ib_it_delete(it); + entry_delete(entry); + ht_dec_size(h); + return TRUE; + }); return FALSE; } -/* Iterates over the instance variable table. */ static void -sg_foreach(mrb_state *mrb, seglist *t, sg_foreach_func *func, void *p) +ht_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp) { - segment *seg; - mrb_int i; + hash_entry *ea = ht_ea(h); + ea_each(ea, ht_size(h), entry, { + ib_cycle_by_key(mrb, h, entry->key, it, { + if (ib_it_get(it) != U32(entry - ea)) continue; + *keyp = entry->key; + *valp = entry->val; + ib_it_delete(it); + entry_delete(entry); + ht_dec_size(h); + return; + }); + }); +} - if (t == NULL) return; - if (t->index && t->index->size-(size_t)t->size > MRB_SG_SEGMENT_SIZE) { - sg_compact(mrb, t); +static void +ht_rehash(mrb_state *mrb, struct RHash *h) +{ + /* see comments in `h_rehash` */ + uint32_t size = ht_size(h); + if (size <= AR_MAX_SIZE) { + ht_to_ar(mrb, h); + ar_rehash(mrb, h); + return; } - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) { - /* no value in last segment after last_len */ - if (!seg->next && i >= t->last_len) { - return; + uint32_t w_size = 0, ea_capa = ht_ea_capa(h); + hash_entry *ea = ht_ea(h); + ht_init(mrb, h, 0, ea, ea_capa, h_ht(h), ib_bit_for(size)); + ht_set_size(h, size); + ht_set_ea_n_used(h, ht_ea_n_used(h)); + ea_each(ea, size, r_entry, { + ib_cycle_by_key(mrb, h, r_entry->key, it, { + if (ib_it_active_p(it)) { + if (!obj_eql(mrb, r_entry->key, ib_it_entry(it)->key, h)) continue; + ib_it_entry(it)->val = r_entry->val; + ht_set_size(h, --size); + entry_delete(r_entry); } - if (mrb_undef_p(seg->e[i].key)) continue; - if ((*func)(mrb, seg->e[i].key, seg->e[i].val, p) != 0) - return; - } - seg = seg->next; + else { + if (w_size != U32(r_entry - ea)) { + ea_set(ea, w_size, r_entry->key, r_entry->val); + entry_delete(r_entry); + } + ib_it_set(it, w_size++); + } + break; + }); + }); + mrb_assert(size == w_size); + ht_set_ea_n_used(h, size); + size <= AR_MAX_SIZE ? ht_to_ar(mrb, h) : ht_adjust_ea(mrb, h, size, ea_capa); +} + +static mrb_value +h_key_for(mrb_state *mrb, mrb_value key) +{ + if (mrb_string_p(key) && !MRB_FROZEN_P(mrb_str_ptr(key))) { + key = mrb_str_dup(mrb, key); + MRB_SET_FROZEN_FLAG(mrb_str_ptr(key)); } + return key; } -/* Get the size of the instance variable table. */ -static mrb_int -sg_size(mrb_state *mrb, seglist *t) +static struct RHash* +h_alloc(mrb_state *mrb) { - if (t == NULL) return 0; - return t->size; + return MRB_OBJ_ALLOC(mrb, MRB_TT_HASH, mrb->hash_class); } -/* Copy the instance variable table. */ -static seglist* -sg_copy(mrb_state *mrb, seglist *t) +static void +h_init(struct RHash *h) { - segment *seg; - seglist *t2; - mrb_int i; + ar_init(h, 0, NULL, 0, 0); +} - seg = t->rootseg; - t2 = sg_new(mrb); +static void +h_free_table(mrb_state *mrb, struct RHash *h) +{ + (h_ar_p(h) ? ar_free : ht_free)(mrb, h); +} - while (seg != NULL) { - for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) { - mrb_value key = seg->e[i].key; - mrb_value val = seg->e[i].val; +static void +h_clear(mrb_state *mrb, struct RHash *h) +{ + h_free_table(mrb, h); + h_init(h); +} - if ((seg->next == NULL) && (i >= t->last_len)) { - return t2; - } - sg_put(mrb, t2, key, val); - } - seg = seg->next; - } - return t2; +static mrb_bool +h_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) +{ + return (h_ar_p(h) ? ar_get : ht_get)(mrb, h, key, valp); } -/* Free memory of the instance variable table. */ static void -sg_free(mrb_state *mrb, seglist *t) +h_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val) { - segment *seg; + (h_ar_p(h) ? ar_set : ht_set)(mrb, h, key, val); +} - if (!t) return; - seg = t->rootseg; - while (seg) { - segment *p = seg; - seg = seg->next; - mrb_free(mrb, p); - } - if (t->index) mrb_free(mrb, t->index); - mrb_free(mrb, t); +static mrb_bool +h_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) +{ + return (h_ar_p(h) ? ar_delete : ht_delete)(mrb, h, key, valp); } -static void mrb_hash_modify(mrb_state *mrb, mrb_value hash); +/* find first element in the table, and remove it. */ +static void +h_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp) +{ + (h_ar_p(h) ? ar_shift : ht_shift)(mrb, h, keyp, valp); +} -static inline mrb_value -ht_key(mrb_state *mrb, mrb_value key) +static void +h_rehash(mrb_state *mrb, struct RHash *h) { - if (mrb_string_p(key) && !MRB_FROZEN_P(mrb_str_ptr(key))) { - key = mrb_str_dup(mrb, key); - MRB_SET_FROZEN_FLAG(mrb_str_ptr(key)); + /* + * ==== Comments common to `ar_rehash` and `ht_rehash` + * + * - Because reindex (such as elimination of duplicate keys) must be + * guaranteed, it is necessary to set one by one. + * + * - To prevent EA from breaking if an exception occurs in the middle, + * delete the slot before moving when moving the entry, and update size + * at any time when overwriting. + */ + (h_size(h) == 0 ? h_clear : h_ar_p(h) ? ar_rehash : ht_rehash)(mrb, h); +} + +static void +h_replace(mrb_state *mrb, struct RHash *h, struct RHash *orig_h) +{ + uint32_t size = h_size(orig_h); + if (size == 0) { + h_clear(mrb, h); + } + else if (h_ar_p(orig_h)) { + uint32_t ea_capa = ar_ea_capa(orig_h); + hash_entry *ea = ea_dup(mrb, ar_ea(orig_h), ea_capa); + h_free_table(mrb, h); + ar_init(h, size, ea, ea_capa, ar_ea_n_used(orig_h)); + } + else { /* HT */ + uint32_t ea_capa = ht_ea_capa(orig_h); + hash_entry *ea = ea_dup(mrb, ht_ea(orig_h), ea_capa); + hash_table *ht = ht_dup(mrb, orig_h); + h_free_table(mrb, h); + h_ht_on(h); + h_set_ht(h, ht); + ht_set_size(h, size); + ht_set_ea(h, ea); +#ifdef MRB_64BIT + ht_set_ea_capa(h, ea_capa); + ht_set_ea_n_used(h, ht_ea_n_used(orig_h)); +#endif + ib_set_bit(h, ib_bit(orig_h)); } - return key; } -#define KEY(key) ht_key(mrb, key) +void +mrb_gc_mark_hash(mrb_state *mrb, struct RHash *h) +{ + h_each(h, entry, { + mrb_gc_mark_value(mrb, entry->key); + mrb_gc_mark_value(mrb, entry->val); + }); +} -static int -hash_mark_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) +size_t +mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *h) { - mrb_gc_mark_value(mrb, key); - mrb_gc_mark_value(mrb, val); - return 0; + return h_size(h) * 2; } void -mrb_gc_mark_hash(mrb_state *mrb, struct RHash *hash) +mrb_gc_free_hash(mrb_state *mrb, struct RHash *h) { - sg_foreach(mrb, hash->ht, hash_mark_i, NULL); + h_free_table(mrb, h); } size_t -mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *hash) +mrb_hash_memsize(mrb_value self) { - if (!hash->ht) return 0; - return sg_size(mrb, hash->ht)*2; + struct RHash *h = mrb_hash_ptr(self); + return mrb_obj_iv_tbl_memsize(self) + + (h_ar_p(h) ? (ar_ea_capa(h) * sizeof(hash_entry)) : + (ht_ea_capa(h) * sizeof(hash_entry) + + sizeof(hash_table) + + ib_byte_size_for(ib_bit(h)))); } -void -mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash) +/* Iterates over the key/value pairs. */ +MRB_API void +mrb_hash_foreach(mrb_state *mrb, struct RHash *h, mrb_hash_foreach_func *func, void *data) { - sg_free(mrb, hash->ht); + h_each(h, entry, { + if (func(mrb, entry->key, entry->val, data) != 0) return; + }); } MRB_API mrb_value mrb_hash_new(mrb_state *mrb) { - struct RHash *h; - - h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); - h->ht = 0; - h->iv = 0; + struct RHash *h = h_alloc(mrb); return mrb_obj_value(h); } +/* + * Set the capacity of EA and IB to minimum capacity (and appropriate load + * factor) that does not cause expansion when inserting `capa` elements. + */ MRB_API mrb_value mrb_hash_new_capa(mrb_state *mrb, mrb_int capa) { - struct RHash *h; - - h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); - /* preallocate segment list */ - h->ht = sg_new(mrb); - /* capacity ignored */ - h->iv = 0; - return mrb_obj_value(h); + if (capa < 0 || EA_MAX_CAPA < capa) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "hash too big"); + return mrb_nil_value(); /* not reached */ + } + else if (capa == 0) { + return mrb_hash_new(mrb); + } + else { + uint32_t size = U32(capa); + struct RHash *h = h_alloc(mrb); + hash_entry *ea = ea_resize(mrb, NULL, size); + if (size <= AR_MAX_SIZE) { + ar_init(h, 0, ea, size, 0); + } + else { + ht_init(mrb, h, 0, ea, size, NULL, ib_bit_for(size)); + } + return mrb_obj_value(h); + } } static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash); -static mrb_value hash_default(mrb_state *mrb, mrb_value hash, mrb_value key); + +static void +hash_modify(mrb_state *mrb, mrb_value hash) +{ + mrb_check_frozen(mrb, mrb_hash_ptr(hash)); +} static mrb_value -mrb_hash_init_copy(mrb_state *mrb, mrb_value self) +hash_default(mrb_state *mrb, mrb_value hash, mrb_value key) { - mrb_value orig; - struct RHash* copy; - seglist *orig_h; - mrb_value ifnone, vret; - - mrb_get_args(mrb, "o", &orig); - if (mrb_obj_equal(mrb, self, orig)) return self; - if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) { - mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object"); + if (MRB_RHASH_DEFAULT_P(hash)) { + if (MRB_RHASH_PROCDEFAULT_P(hash)) { + return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key); + } + else { + return RHASH_IFNONE(hash); + } } + return mrb_nil_value(); +} - orig_h = RHASH_TBL(self); - copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); - copy->ht = sg_copy(mrb, orig_h); - - if (MRB_RHASH_DEFAULT_P(self)) { - copy->flags |= MRB_HASH_DEFAULT; - } - if (MRB_RHASH_PROCDEFAULT_P(self)) { - copy->flags |= MRB_HASH_PROC_DEFAULT; +static void +hash_replace(mrb_state *mrb, mrb_value self, mrb_value orig) +{ + struct RHash *h = mrb_hash_ptr(self), *orig_h = mrb_hash_ptr(orig); + uint32_t mask = MRB_HASH_DEFAULT | MRB_HASH_PROC_DEFAULT; + mrb_sym name; + h_replace(mrb, h, orig_h); + name = MRB_SYM(ifnone); + if (orig_h->flags & MRB_HASH_DEFAULT) { + mrb_iv_set(mrb, self, name, mrb_iv_get(mrb, orig, name)); } - vret = mrb_obj_value(copy); - ifnone = RHASH_IFNONE(self); - if (!mrb_nil_p(ifnone)) { - mrb_iv_set(mrb, vret, mrb_intern_lit(mrb, "ifnone"), ifnone); + else { + mrb_iv_remove(mrb, self, name); } - return vret; + h->flags &= ~mask; + h->flags |= orig_h->flags & mask; } -static int -check_kdict_i(mrb_state *mrb, mrb_value key, mrb_value val, void *data) +static mrb_value +mrb_hash_init_copy(mrb_state *mrb, mrb_value self) { - if (!mrb_symbol_p(key)) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword argument hash with non symbol keys"); - } - return 0; + mrb_value orig; + mrb_get_args(mrb, "H", &orig); + hash_modify(mrb, self); + if (mrb_hash_ptr(self) != mrb_hash_ptr(orig)) hash_replace(mrb, self, orig); + return self; } void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self) { - seglist *sg; - - sg = RHASH_TBL(self); - if (!sg || sg_size(mrb, sg) == 0) return; - sg_foreach(mrb, sg, check_kdict_i, NULL); + h_each(mrb_hash_ptr(self), entry, { + if (mrb_symbol_p(entry->key)) continue; + mrb_raise(mrb, E_ARGUMENT_ERROR, "keyword argument hash with non symbol keys"); + }); } MRB_API mrb_value mrb_hash_dup(mrb_state *mrb, mrb_value self) { - struct RHash* copy; - seglist *orig_h; - - orig_h = RHASH_TBL(self); - copy = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); - copy->ht = orig_h ? sg_copy(mrb, orig_h) : NULL; - return mrb_obj_value(copy); + struct RHash* copy_h = h_alloc(mrb); + mrb_value copy = mrb_obj_value(copy_h); + copy_h->c = mrb_hash_ptr(self)->c; + hash_replace(mrb, copy, self); + return copy; } MRB_API mrb_value @@ -688,11 +1214,11 @@ mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key) mrb_value val; mrb_sym mid; - if (sg_get(mrb, RHASH_TBL(hash), key, &val)) { + if (h_get(mrb, mrb_hash_ptr(hash), key, &val)) { return val; } - mid = mrb_intern_lit(mrb, "default"); + mid = MRB_SYM(default); if (mrb_func_basic_p(mrb, hash, mid, mrb_hash_default)) { return hash_default(mrb, hash, key); } @@ -705,7 +1231,7 @@ mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def) { mrb_value val; - if (sg_get(mrb, RHASH_TBL(hash), key, &val)) { + if (h_get(mrb, mrb_hash_ptr(hash), key, &val)) { return val; } /* not found */ @@ -715,36 +1241,11 @@ mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def) MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) { - mrb_hash_modify(mrb, hash); - - sg_put(mrb, RHASH_TBL(hash), key, val); - mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), key); - mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), val); - return; -} - -MRB_API mrb_value -mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash) -{ - return mrb_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash"); -} - -MRB_API mrb_value -mrb_check_hash_type(mrb_state *mrb, mrb_value hash) -{ - return mrb_check_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash"); -} - -static void -mrb_hash_modify(mrb_state *mrb, mrb_value hash) -{ - if (MRB_FROZEN_P(mrb_hash_ptr(hash))) { - mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen hash"); - } - - if (!RHASH_TBL(hash)) { - RHASH_TBL(hash) = sg_new(mrb); - } + hash_modify(mrb, hash); + key = h_key_for(mrb, key); + h_set(mrb, mrb_hash_ptr(hash), key, val); + mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), key); + mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), val); } /* 15.2.13.4.16 */ @@ -791,17 +1292,17 @@ mrb_hash_init(mrb_state *mrb, mrb_value hash) ifnone = mrb_nil_value(); mrb_get_args(mrb, "&|o?", &block, &ifnone, &ifnone_p); - mrb_hash_modify(mrb, hash); + hash_modify(mrb, hash); if (!mrb_nil_p(block)) { if (ifnone_p) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); + mrb_argnum_error(mrb, 1, 0, 0); } RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; ifnone = block; } if (!mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_DEFAULT; - mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); + mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone); } return hash; } @@ -823,26 +1324,11 @@ mrb_hash_init(mrb_state *mrb, mrb_value hash) static mrb_value mrb_hash_aget(mrb_state *mrb, mrb_value self) { - mrb_value key; + mrb_value key = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &key); return mrb_hash_get(mrb, self, key); } -static mrb_value -hash_default(mrb_state *mrb, mrb_value hash, mrb_value key) -{ - if (MRB_RHASH_DEFAULT_P(hash)) { - if (MRB_RHASH_PROCDEFAULT_P(hash)) { - return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); - } - else { - return RHASH_IFNONE(hash); - } - } - return mrb_nil_value(); -} - /* 15.2.13.4.5 */ /* * call-seq: @@ -875,7 +1361,7 @@ mrb_hash_default(mrb_state *mrb, mrb_value hash) if (MRB_RHASH_DEFAULT_P(hash)) { if (MRB_RHASH_PROCDEFAULT_P(hash)) { if (!given) return mrb_nil_value(); - return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); + return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key); } else { return RHASH_IFNONE(hash); @@ -908,11 +1394,10 @@ mrb_hash_default(mrb_state *mrb, mrb_value hash) static mrb_value mrb_hash_set_default(mrb_state *mrb, mrb_value hash) { - mrb_value ifnone; + mrb_value ifnone = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &ifnone); - mrb_hash_modify(mrb, hash); - mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); + hash_modify(mrb, hash); + mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone); RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT; if (!mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_DEFAULT; @@ -938,7 +1423,6 @@ mrb_hash_set_default(mrb_state *mrb, mrb_value hash) * a #=> [nil, nil, 4] */ - static mrb_value mrb_hash_default_proc(mrb_state *mrb, mrb_value hash) { @@ -964,11 +1448,10 @@ mrb_hash_default_proc(mrb_state *mrb, mrb_value hash) static mrb_value mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash) { - mrb_value ifnone; + mrb_value ifnone = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &ifnone); - mrb_hash_modify(mrb, hash); - mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); + hash_modify(mrb, hash); + mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone); if (!mrb_nil_p(ifnone)) { RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; RHASH(hash)->flags |= MRB_HASH_DEFAULT; @@ -984,10 +1467,10 @@ mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash) MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key) { - seglist *sg = RHASH_TBL(hash); mrb_value del_val; - if (sg_del(mrb, sg, key, &del_val)) { + hash_modify(mrb, hash); + if (h_delete(mrb, mrb_hash_ptr(hash), key, &del_val)) { return del_val; } @@ -998,40 +1481,11 @@ mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key) static mrb_value mrb_hash_delete(mrb_state *mrb, mrb_value self) { - mrb_value key; - - mrb_get_args(mrb, "o", &key); - mrb_hash_modify(mrb, self); + mrb_value key = mrb_get_arg1(mrb); + mrb->c->ci->mid = 0; return mrb_hash_delete_key(mrb, self, key); } -/* find first element in segment list, and remove it. */ -static void -sg_shift(mrb_state *mrb, seglist *t, mrb_value *kp, mrb_value *vp) -{ - segment *seg = t->rootseg; - mrb_int i; - - while (seg) { - for (i=0; i<MRB_SG_SEGMENT_SIZE; i++) { - mrb_value key; - - if (!seg->next && i >= t->last_len) { - return; - } - key = seg->e[i].key; - if (mrb_undef_p(key)) continue; - *kp = key; - *vp = seg->e[i].val; - /* delete element */ - seg->e[i].key = mrb_undef_value(); - t->size--; - return; - } - seg = seg->next; - } -} - /* 15.2.13.4.24 */ /* * call-seq: @@ -1049,25 +1503,19 @@ sg_shift(mrb_state *mrb, seglist *t, mrb_value *kp, mrb_value *vp) static mrb_value mrb_hash_shift(mrb_state *mrb, mrb_value hash) { - seglist *sg = RHASH_TBL(hash); + struct RHash *h = mrb_hash_ptr(hash); - mrb_hash_modify(mrb, hash); - if (sg && sg_size(mrb, sg) > 0) { + hash_modify(mrb, hash); + if (h_size(h) == 0) { + return mrb_nil_value(); + } + else { mrb_value del_key, del_val; - - sg_shift(mrb, sg, &del_key, &del_val); + h_shift(mrb, h, &del_key, &del_val); + mrb_gc_protect(mrb, del_key); + mrb_gc_protect(mrb, del_val); return mrb_assoc_new(mrb, del_key, del_val); } - - if (MRB_RHASH_DEFAULT_P(hash)) { - if (MRB_RHASH_PROCDEFAULT_P(hash)) { - return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, mrb_nil_value()); - } - else { - return RHASH_IFNONE(hash); - } - } - return mrb_nil_value(); } /* 15.2.13.4.4 */ @@ -1085,13 +1533,8 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash) MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash) { - seglist *sg = RHASH_TBL(hash); - - mrb_hash_modify(mrb, hash); - if (sg) { - sg_free(mrb, sg); - RHASH_TBL(hash) = NULL; - } + hash_modify(mrb, hash); + h_clear(mrb, mrb_hash_ptr(hash)); return hash; } @@ -1124,12 +1567,18 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self) return val; } +MRB_API mrb_int +mrb_hash_size(mrb_state *mrb, mrb_value hash) +{ + return (mrb_int)h_size(mrb_hash_ptr(hash)); +} + /* 15.2.13.4.20 */ /* 15.2.13.4.25 */ /* * call-seq: - * hsh.length -> fixnum - * hsh.size -> fixnum + * hsh.length -> integer + * hsh.size -> integer * * Returns the number of key-value pairs in the hash. * @@ -1141,19 +1590,14 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self) static mrb_value mrb_hash_size_m(mrb_state *mrb, mrb_value self) { - seglist *sg = RHASH_TBL(self); - - if (!sg) return mrb_fixnum_value(0); - return mrb_fixnum_value(sg_size(mrb, sg)); + mrb_int size = mrb_hash_size(mrb, self); + return mrb_int_value(mrb, size); } MRB_API mrb_bool mrb_hash_empty_p(mrb_state *mrb, mrb_value self) { - seglist *sg = RHASH_TBL(self); - - if (!sg) return TRUE; - return sg_size(mrb, sg) == 0; + return h_size(mrb_hash_ptr(self)) == 0; } /* 15.2.13.4.12 */ @@ -1172,27 +1616,6 @@ mrb_hash_empty_m(mrb_state *mrb, mrb_value self) return mrb_bool_value(mrb_hash_empty_p(mrb, self)); } -/* 15.2.13.4.29 (x)*/ -/* - * call-seq: - * hsh.to_hash => hsh - * - * Returns +self+. - */ - -static mrb_value -mrb_hash_to_hash(mrb_state *mrb, mrb_value hash) -{ - return hash; -} - -static int -hash_keys_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) -{ - mrb_ary_push(mrb, *(mrb_value*)p, key); - return 0; -} - /* 15.2.13.4.19 */ /* * call-seq: @@ -1209,24 +1632,14 @@ hash_keys_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash) { - seglist *sg = RHASH_TBL(hash); - size_t size; - mrb_value ary; - - if (!sg || (size = sg_size(mrb, sg)) == 0) - return mrb_ary_new(mrb); - ary = mrb_ary_new_capa(mrb, size); - sg_foreach(mrb, sg, hash_keys_i, (void*)&ary); + struct RHash *h = mrb_hash_ptr(hash); + mrb_value ary = mrb_ary_new_capa(mrb, (mrb_int)h_size(h)); + h_each(h, entry, { + mrb_ary_push(mrb, ary, entry->key); + }); return ary; } -static int -hash_vals_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) -{ - mrb_ary_push(mrb, *(mrb_value*)p, val); - return 0; -} - /* 15.2.13.4.28 */ /* * call-seq: @@ -1243,14 +1656,11 @@ hash_vals_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash) { - seglist *sg = RHASH_TBL(hash); - size_t size; - mrb_value ary; - - if (!sg || (size = sg_size(mrb, sg)) == 0) - return mrb_ary_new(mrb); - ary = mrb_ary_new_capa(mrb, size); - sg_foreach(mrb, sg, hash_vals_i, (void*)&ary); + struct RHash *h = mrb_hash_ptr(hash); + mrb_value ary = mrb_ary_new_capa(mrb, (mrb_int)h_size(h)); + h_each(h, entry, { + mrb_ary_push(mrb, ary, entry->val); + }); return ary; } @@ -1276,43 +1686,20 @@ mrb_hash_values(mrb_state *mrb, mrb_value hash) MRB_API mrb_bool mrb_hash_key_p(mrb_state *mrb, mrb_value hash, mrb_value key) { - seglist *sg; - - sg = RHASH_TBL(hash); - if (sg_get(mrb, sg, key, NULL)) { - return TRUE; - } - return FALSE; + mrb_value val; + return h_get(mrb, mrb_hash_ptr(hash), key, &val); } static mrb_value mrb_hash_has_key(mrb_state *mrb, mrb_value hash) { - mrb_value key; + mrb_value key = mrb_get_arg1(mrb); mrb_bool key_p; - mrb_get_args(mrb, "o", &key); key_p = mrb_hash_key_p(mrb, hash, key); return mrb_bool_value(key_p); } -struct has_v_arg { - mrb_bool found; - mrb_value val; -}; - -static int -hash_has_value_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) -{ - struct has_v_arg *arg = (struct has_v_arg*)p; - - if (mrb_equal(mrb, arg->val, val)) { - arg->found = TRUE; - return 1; - } - return 0; -} - /* 15.2.13.4.14 */ /* 15.2.13.4.27 */ /* @@ -1331,43 +1718,33 @@ hash_has_value_i(mrb_state *mrb, mrb_value key, mrb_value val, void *p) static mrb_value mrb_hash_has_value(mrb_state *mrb, mrb_value hash) { - mrb_value val; - struct has_v_arg arg; - - mrb_get_args(mrb, "o", &val); - arg.found = FALSE; - arg.val = val; - sg_foreach(mrb, RHASH_TBL(hash), hash_has_value_i, &arg); - return mrb_bool_value(arg.found); -} - -static int -merge_i(mrb_state *mrb, mrb_value key, mrb_value val, void *data) -{ - seglist *h1 = (seglist*)data; - - sg_put(mrb, h1, key, val); - return 0; + mrb_value val = mrb_get_arg1(mrb); + struct RHash *h = mrb_hash_ptr(hash); + h_each(h, entry, { + h_check_modified(mrb, h, { + if (mrb_equal(mrb, val, entry->val)) return mrb_true_value(); + }); + }); + return mrb_false_value(); } MRB_API void mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2) { - seglist *h1, *h2; - - mrb_hash_modify(mrb, hash1); - hash2 = mrb_ensure_hash_type(mrb, hash2); - h1 = RHASH_TBL(hash1); - h2 = RHASH_TBL(hash2); - - if (!h2) return; - if (!h1) { - RHASH_TBL(hash1) = sg_copy(mrb, h2); - return; - } - sg_foreach(mrb, h2, merge_i, h1); - mrb_write_barrier(mrb, (struct RBasic*)RHASH(hash1)); - return; + struct RHash *h1, *h2; + + hash_modify(mrb, hash1); + mrb_ensure_hash_type(mrb, hash2); + h1 = mrb_hash_ptr(hash1); + h2 = mrb_hash_ptr(hash2); + + if (h1 == h2) return; + if (h_size(h2) == 0) return; + h_each(h2, entry, { + h_check_modified(mrb, h2, {h_set(mrb, h1, entry->key, entry->val);}); + mrb_field_write_barrier_value(mrb, (struct RBasic *)h1, entry->key); + mrb_field_write_barrier_value(mrb, (struct RBasic *)h1, entry->val); + }); } /* @@ -1378,15 +1755,22 @@ mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2) * values of key objects have changed since they were inserted, this * method will reindex <i>hsh</i>. * - * h = {"AAA" => "b"} - * h.keys[0].chop! - * h.rehash #=> {"AA"=>"b"} - * h["AA"] #=> "b" + * keys = (1..17).map{|n| [n]} + * k = keys[0] + * h = {} + * keys.each{|key| h[key] = key[0]} + * h #=> { [1]=>1, [2]=>2, ... [16]=>16, [17]=>17} + * h[k] #=> 1 + * k[0] = keys.size + 1 + * h #=> {[18]=>1, [2]=>2, ... [16]=>16, [17]=>17} + * h[k] #=> nil + * h.rehash + * h[k] #=> 1 */ static mrb_value mrb_hash_rehash(mrb_state *mrb, mrb_value self) { - sg_compact(mrb, RHASH_TBL(self)); + h_rehash(mrb, mrb_hash_ptr(self)); return self; } @@ -1398,11 +1782,10 @@ mrb_init_hash(mrb_state *mrb) mrb->hash_class = h = mrb_define_class(mrb, "Hash", mrb->object_class); /* 15.2.13 */ MRB_SET_INSTANCE_TT(h, MRB_TT_HASH); - mrb_define_method(mrb, h, "initialize_copy", mrb_hash_init_copy, MRB_ARGS_REQ(1)); mrb_define_method(mrb, h, "[]", mrb_hash_aget, MRB_ARGS_REQ(1)); /* 15.2.13.4.2 */ mrb_define_method(mrb, h, "[]=", mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.3 */ mrb_define_method(mrb, h, "clear", mrb_hash_clear, MRB_ARGS_NONE()); /* 15.2.13.4.4 */ - mrb_define_method(mrb, h, "default", mrb_hash_default, MRB_ARGS_ANY()); /* 15.2.13.4.5 */ + mrb_define_method(mrb, h, "default", mrb_hash_default, MRB_ARGS_OPT(1)); /* 15.2.13.4.5 */ mrb_define_method(mrb, h, "default=", mrb_hash_set_default, MRB_ARGS_REQ(1)); /* 15.2.13.4.6 */ mrb_define_method(mrb, h, "default_proc", mrb_hash_default_proc,MRB_ARGS_NONE()); /* 15.2.13.4.7 */ mrb_define_method(mrb, h, "default_proc=", mrb_hash_set_default_proc,MRB_ARGS_REQ(1)); /* 15.2.13.4.7 */ @@ -1411,17 +1794,17 @@ mrb_init_hash(mrb_state *mrb) mrb_define_method(mrb, h, "has_key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.13 */ mrb_define_method(mrb, h, "has_value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.14 */ mrb_define_method(mrb, h, "include?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.15 */ - mrb_define_method(mrb, h, "initialize", mrb_hash_init, MRB_ARGS_OPT(1)); /* 15.2.13.4.16 */ + mrb_define_method(mrb, h, "initialize", mrb_hash_init, MRB_ARGS_OPT(1)|MRB_ARGS_BLOCK()); /* 15.2.13.4.16 */ + mrb_define_method(mrb, h, "initialize_copy", mrb_hash_init_copy, MRB_ARGS_REQ(1)); /* 15.2.13.4.17 */ mrb_define_method(mrb, h, "key?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.18 */ mrb_define_method(mrb, h, "keys", mrb_hash_keys, MRB_ARGS_NONE()); /* 15.2.13.4.19 */ mrb_define_method(mrb, h, "length", mrb_hash_size_m, MRB_ARGS_NONE()); /* 15.2.13.4.20 */ mrb_define_method(mrb, h, "member?", mrb_hash_has_key, MRB_ARGS_REQ(1)); /* 15.2.13.4.21 */ + mrb_define_method(mrb, h, "replace", mrb_hash_init_copy, MRB_ARGS_REQ(1)); /* 15.2.13.4.23 */ mrb_define_method(mrb, h, "shift", mrb_hash_shift, MRB_ARGS_NONE()); /* 15.2.13.4.24 */ mrb_define_method(mrb, h, "size", mrb_hash_size_m, MRB_ARGS_NONE()); /* 15.2.13.4.25 */ mrb_define_method(mrb, h, "store", mrb_hash_aset, MRB_ARGS_REQ(2)); /* 15.2.13.4.26 */ mrb_define_method(mrb, h, "value?", mrb_hash_has_value, MRB_ARGS_REQ(1)); /* 15.2.13.4.27 */ mrb_define_method(mrb, h, "values", mrb_hash_values, MRB_ARGS_NONE()); /* 15.2.13.4.28 */ mrb_define_method(mrb, h, "rehash", mrb_hash_rehash, MRB_ARGS_NONE()); - - mrb_define_method(mrb, h, "to_hash", mrb_hash_to_hash, MRB_ARGS_NONE()); /* 15.2.13.4.29 (x)*/ } diff --git a/src/kernel.c b/src/kernel.c index db681d510..4dcf6261b 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -13,13 +13,14 @@ #include <mruby/variable.h> #include <mruby/error.h> #include <mruby/istruct.h> +#include <mruby/presym.h> MRB_API mrb_bool mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func) { struct RClass *c = mrb_class(mrb, obj); mrb_method_t m = mrb_method_search_vm(mrb, &c, mid); - struct RProc *p; + const struct RProc *p; if (MRB_METHOD_UNDEF_P(m)) return FALSE; if (MRB_METHOD_FUNC_P(m)) @@ -33,7 +34,7 @@ mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func) static mrb_bool mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj) { - return mrb_func_basic_p(mrb, obj, mrb_intern_lit(mrb, "to_s"), mrb_any_to_s); + return mrb_func_basic_p(mrb, obj, MRB_SYM(to_s), mrb_any_to_s); } /* 15.3.1.3.17 */ @@ -53,7 +54,7 @@ mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj) MRB_API mrb_value mrb_obj_inspect(mrb_state *mrb, mrb_value obj) { - if ((mrb_type(obj) == MRB_TT_OBJECT) && mrb_obj_basic_to_s_p(mrb, obj)) { + if (mrb_object_p(obj) && mrb_obj_basic_to_s_p(mrb, obj)) { return mrb_obj_iv_inspect(mrb, mrb_obj_ptr(obj)); } return mrb_any_to_s(mrb, obj); @@ -71,9 +72,8 @@ mrb_obj_inspect(mrb_state *mrb, mrb_value obj) static mrb_value mrb_equal_m(mrb_state *mrb, mrb_value self) { - mrb_value arg; + mrb_value arg = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &arg); return mrb_bool_value(mrb_equal(mrb, self, arg)); } @@ -97,7 +97,19 @@ mrb_equal_m(mrb_state *mrb, mrb_value self) mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self) { - return mrb_fixnum_value(mrb_obj_id(self)); + return mrb_int_value(mrb, mrb_obj_id(self)); +} + +static int +env_bidx(struct REnv *e) +{ + int bidx; + + /* use saved block arg position */ + bidx = MRB_ENV_BIDX(e); + /* bidx may be useless (e.g. define_method) */ + if (bidx >= MRB_ENV_LEN(e)) return -1; + return bidx; } /* 15.3.1.2.2 */ @@ -130,7 +142,9 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) mrb_callinfo *ci = &mrb->c->ci[-1]; mrb_callinfo *cibase = mrb->c->cibase; mrb_value *bp; - struct RProc *p; + int bidx; + struct REnv *e = NULL; + const struct RProc *p; if (ci <= cibase) { /* toplevel does not have block */ @@ -140,33 +154,39 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) /* search method/class/module proc */ while (p) { if (MRB_PROC_SCOPE_P(p)) break; + e = MRB_PROC_ENV(p); p = p->upper; } if (p == NULL) return mrb_false_value(); + if (e) { + bidx = env_bidx(e); + if (bidx < 0) return mrb_false_value(); + bp = &e->stack[bidx]; + goto block_given; + } /* search ci corresponding to proc */ while (cibase < ci) { if (ci->proc == p) break; ci--; } if (ci == cibase) { - return mrb_false_value(); + /* proc is closure */ + if (!MRB_PROC_ENV_P(p)) return mrb_false_value(); + e = MRB_PROC_ENV(p); + bidx = env_bidx(e); + if (bidx < 0) return mrb_false_value(); + bp = &e->stack[bidx]; } - else if (ci->env) { - struct REnv *e = ci->env; - int bidx; - + else if ((e = mrb_vm_ci_env(ci)) != NULL) { /* top-level does not have block slot (always false) */ - if (e->stack == mrb->c->stbase) - return mrb_false_value(); - /* use saved block arg position */ - bidx = MRB_ENV_BIDX(e); + if (e->stack == mrb->c->stbase) return mrb_false_value(); + bidx = env_bidx(e); /* bidx may be useless (e.g. define_method) */ - if (bidx >= MRB_ENV_STACK_LEN(e)) - return mrb_false_value(); + if (bidx < 0) return mrb_false_value(); bp = &e->stack[bidx]; } else { - bp = ci[1].stackent+1; + bp = ci->stack+1; if (ci->argc >= 0) { bp += ci->argc; } @@ -174,6 +194,7 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) bp++; } } + block_given: if (mrb_nil_p(*bp)) return mrb_false_value(); return mrb_true_value(); @@ -188,7 +209,7 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) * called with an explicit receiver, as <code>class</code> is also a * reserved word in Ruby. * - * 1.class #=> Fixnum + * 1.class #=> Integer * self.class #=> Object */ static mrb_value @@ -197,201 +218,20 @@ mrb_obj_class_m(mrb_state *mrb, mrb_value self) return mrb_obj_value(mrb_obj_class(mrb, self)); } -static struct RClass* -mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) -{ - struct RClass *klass = mrb_basic_ptr(obj)->c; - - if (klass->tt != MRB_TT_SCLASS) - return klass; - else { - /* copy singleton(unnamed) class */ - struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class); - - 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) { - mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); - mrb_obj_iv_set(mrb, (struct RObject*)clone, mrb_intern_lit(mrb, "__attached__"), obj); - } - if (klass->mt) { - clone->mt = kh_copy(mt, mrb, klass->mt); - } - else { - clone->mt = kh_init(mt, mrb); - } - clone->tt = MRB_TT_SCLASS; - return clone; - } -} - -static void -copy_class(mrb_state *mrb, mrb_value dst, mrb_value src) -{ - struct RClass *dc = mrb_class_ptr(dst); - struct RClass *sc = mrb_class_ptr(src); - /* if the origin is not the same as the class, then the origin and - the current class need to be copied */ - if (sc->flags & MRB_FL_CLASS_IS_PREPENDED) { - struct RClass *c0 = sc->super; - struct RClass *c1 = dc; - - /* copy prepended iclasses */ - while (!(c0->flags & MRB_FL_CLASS_IS_ORIGIN)) { - c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); - c1 = c1->super; - c0 = c0->super; - } - c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0))); - c1->super->flags |= MRB_FL_CLASS_IS_ORIGIN; - } - 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)); -} - -static void -init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj) -{ - switch (mrb_type(obj)) { - case MRB_TT_ICLASS: - copy_class(mrb, dest, obj); - return; - case MRB_TT_CLASS: - case MRB_TT_MODULE: - copy_class(mrb, dest, obj); - mrb_iv_copy(mrb, dest, obj); - mrb_iv_remove(mrb, dest, mrb_intern_lit(mrb, "__classname__")); - break; - case MRB_TT_OBJECT: - case MRB_TT_SCLASS: - case MRB_TT_HASH: - case MRB_TT_DATA: - case MRB_TT_EXCEPTION: - mrb_iv_copy(mrb, dest, obj); - break; - case MRB_TT_ISTRUCT: - mrb_istruct_copy(dest, obj); - break; - - default: - break; - } - mrb_funcall(mrb, dest, "initialize_copy", 1, obj); -} - -/* 15.3.1.3.8 */ -/* - * call-seq: - * obj.clone -> an_object - * - * Produces a shallow copy of <i>obj</i>---the instance variables of - * <i>obj</i> are copied, but not the objects they reference. Copies - * the frozen state of <i>obj</i>. See also the discussion - * under <code>Object#dup</code>. - * - * class Klass - * attr_accessor :str - * end - * s1 = Klass.new #=> #<Klass:0x401b3a38> - * s1.str = "Hello" #=> "Hello" - * s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello"> - * s2.str[1,4] = "i" #=> "i" - * s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">" - * s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">" - * - * This method may have class-specific behavior. If so, that - * behavior will be documented under the #+initialize_copy+ method of - * the class. - * - * Some Class(True False Nil Symbol Fixnum Float) Object cannot clone. - */ -MRB_API mrb_value -mrb_obj_clone(mrb_state *mrb, mrb_value self) -{ - struct RObject *p; - mrb_value clone; - - if (mrb_immediate_p(self)) { - mrb_raisef(mrb, E_TYPE_ERROR, "can't clone %S", self); - } - if (mrb_type(self) == MRB_TT_SCLASS) { - mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class"); - } - 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); - p->flags |= mrb_obj_ptr(self)->flags & MRB_FL_OBJ_IS_FROZEN; - - return clone; -} - -/* 15.3.1.3.9 */ -/* - * call-seq: - * obj.dup -> an_object - * - * Produces a shallow copy of <i>obj</i>---the instance variables of - * <i>obj</i> are copied, but not the objects they reference. - * <code>dup</code> copies the frozen state of <i>obj</i>. See also - * the discussion under <code>Object#clone</code>. In general, - * <code>clone</code> and <code>dup</code> may have different semantics - * in descendant classes. While <code>clone</code> is used to duplicate - * an object, including its internal state, <code>dup</code> typically - * uses the class of the descendant object to create the new instance. - * - * This method may have class-specific behavior. If so, that - * behavior will be documented under the #+initialize_copy+ method of - * the class. - */ - -MRB_API mrb_value -mrb_obj_dup(mrb_state *mrb, mrb_value obj) -{ - struct RBasic *p; - mrb_value dup; - - if (mrb_immediate_p(obj)) { - mrb_raisef(mrb, E_TYPE_ERROR, "can't dup %S", obj); - } - if (mrb_type(obj) == MRB_TT_SCLASS) { - mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class"); - } - p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj)); - dup = mrb_obj_value(p); - init_copy(mrb, dup, obj); - - return dup; -} - static mrb_value -mrb_obj_extend(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value obj) +mrb_obj_extend(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value obj) { mrb_int i; if (argc == 0) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (at least 1)"); + mrb_argnum_error(mrb, argc, 1, -1); } for (i = 0; i < argc; i++) { mrb_check_type(mrb, argv[i], MRB_TT_MODULE); } while (argc--) { - mrb_funcall(mrb, argv[argc], "extend_object", 1, obj); - mrb_funcall(mrb, argv[argc], "extended", 1, obj); + mrb_funcall_id(mrb, argv[argc], MRB_SYM(extend_object), 1, obj); + mrb_funcall_id(mrb, argv[argc], MRB_SYM(extended), 1, obj); } return obj; } @@ -424,34 +264,22 @@ mrb_obj_extend(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value obj) static mrb_value mrb_obj_extend_m(mrb_state *mrb, mrb_value self) { - mrb_value *argv; + const mrb_value *argv; mrb_int argc; mrb_get_args(mrb, "*", &argv, &argc); return mrb_obj_extend(mrb, argc, argv, self); } -static mrb_value +MRB_API mrb_value mrb_obj_freeze(mrb_state *mrb, mrb_value self) { - struct RBasic *b; - - switch (mrb_type(self)) { - case MRB_TT_FALSE: - case MRB_TT_TRUE: - case MRB_TT_FIXNUM: - case MRB_TT_SYMBOL: -#ifndef MRB_WITHOUT_FLOAT - case MRB_TT_FLOAT: -#endif - return self; - default: - break; - } - - b = mrb_basic_ptr(self); - if (!MRB_FROZEN_P(b)) { - MRB_SET_FROZEN_FLAG(b); + if (!mrb_immediate_p(self)) { + struct RBasic *b = mrb_basic_ptr(self); + if (!mrb_frozen_p(b)) { + MRB_SET_FROZEN_FLAG(b); + if (b->c->tt == MRB_TT_SCLASS) MRB_SET_FROZEN_FLAG(b->c); + } } return self; } @@ -459,26 +287,7 @@ mrb_obj_freeze(mrb_state *mrb, mrb_value self) static mrb_value mrb_obj_frozen(mrb_state *mrb, mrb_value self) { - struct RBasic *b; - - switch (mrb_type(self)) { - case MRB_TT_FALSE: - case MRB_TT_TRUE: - case MRB_TT_FIXNUM: - case MRB_TT_SYMBOL: -#ifndef MRB_WITHOUT_FLOAT - case MRB_TT_FLOAT: -#endif - return mrb_true_value(); - default: - break; - } - - b = mrb_basic_ptr(self); - if (!MRB_FROZEN_P(b)) { - return mrb_false_value(); - } - return mrb_true_value(); + return mrb_bool_value(mrb_immediate_p(self) || mrb_frozen_p(mrb_basic_ptr(self))); } /* 15.3.1.3.15 */ @@ -486,25 +295,24 @@ mrb_obj_frozen(mrb_state *mrb, mrb_value self) * call-seq: * obj.hash -> fixnum * - * Generates a <code>Fixnum</code> hash value for this object. This + * Generates a <code>Integer</code> hash value for this object. This * function must have the property that <code>a.eql?(b)</code> implies * <code>a.hash == b.hash</code>. The hash value is used by class * <code>Hash</code>. Any hash value that exceeds the capacity of a - * <code>Fixnum</code> will be truncated before being used. + * <code>Integer</code> will be truncated before being used. */ -MRB_API mrb_value +static mrb_value mrb_obj_hash(mrb_state *mrb, mrb_value self) { - return mrb_fixnum_value(mrb_obj_id(self)); + return mrb_int_value(mrb, mrb_obj_id(self)); } /* 15.3.1.3.16 */ -static mrb_value +mrb_value mrb_obj_init_copy(mrb_state *mrb, mrb_value self) { - mrb_value orig; + mrb_value orig = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &orig); if (mrb_obj_equal(mrb, self, orig)) return self; if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) { mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object"); @@ -512,7 +320,6 @@ mrb_obj_init_copy(mrb_state *mrb, mrb_value self) return self; } - MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c) { @@ -531,11 +338,11 @@ mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c) static mrb_value obj_is_instance_of(mrb_state *mrb, mrb_value self) { - mrb_value arg; + struct RClass *c; - mrb_get_args(mrb, "C", &arg); + mrb_get_args(mrb, "c", &c); - return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg))); + return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, c)); } /* 15.3.1.3.24 */ @@ -568,16 +375,13 @@ obj_is_instance_of(mrb_state *mrb, mrb_value self) static mrb_value mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self) { - mrb_value arg; + struct RClass *c; - mrb_get_args(mrb, "C", &arg); + mrb_get_args(mrb, "c", &c); - return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg))); + return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, c)); } -KHASH_DECLARE(st, mrb_sym, char, FALSE) -KHASH_DEFINE(st, mrb_sym, char, FALSE, kh_int_hash_func, kh_int_hash_equal) - /* 15.3.1.3.32 */ /* * call_seq: @@ -619,8 +423,8 @@ mrb_f_raise(mrb_state *mrb, mrb_value self) mrb_value a[2], exc; mrb_int argc; - argc = mrb_get_args(mrb, "|oo", &a[0], &a[1]); + mrb->c->ci->mid = 0; switch (argc) { case 0: mrb_raise(mrb, E_RUNTIME_ERROR, ""); @@ -640,16 +444,6 @@ mrb_f_raise(mrb_state *mrb, mrb_value self) return mrb_nil_value(); /* not reached */ } -static mrb_value -mrb_krn_class_defined(mrb_state *mrb, mrb_value self) -{ - mrb_value str; - - mrb_get_args(mrb, "S", &str); - return mrb_bool_value(mrb_class_defined(mrb, RSTRING_PTR(str))); -} - - /* 15.3.1.3.41 */ /* * call-seq: @@ -682,7 +476,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self) mrb_iv_name_sym_check(mrb, sym); val = mrb_iv_remove(mrb, self, sym); if (mrb_undef_p(val)) { - mrb_name_error(mrb, sym, "instance variable %S not defined", mrb_sym2str(mrb, sym)); + mrb_name_error(mrb, sym, "instance variable %n not defined", sym); } return val; } @@ -690,7 +484,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self) void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args) { - mrb_no_method_error(mrb, name, args, "undefined method '%S'", mrb_sym2str(mrb, name)); + mrb_no_method_error(mrb, name, args, "undefined method '%n'", name); } /* 15.3.1.3.30 */ @@ -716,7 +510,7 @@ mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args) * # ... * end * def method_missing(methId) - * str = methId.id2name + * str = methId.to_s * romanToInt(str) * end * end @@ -726,26 +520,26 @@ mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args) * r.xxiii #=> 23 * r.mm #=> 2000 */ -#ifdef MRB_DEFAULT_METHOD_MISSING -static mrb_value +mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod) { mrb_sym name; - mrb_value *a; + const mrb_value *a; mrb_int alen; + mrb->c->ci->mid = 0; 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(); } -#endif static inline mrb_bool basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub) { return mrb_respond_to(mrb, obj, id); } + /* 15.3.1.3.43 */ /* * call-seq: @@ -765,45 +559,16 @@ basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub) static mrb_value obj_respond_to(mrb_state *mrb, mrb_value self) { - mrb_value mid; mrb_sym id, rtm_id; - mrb_bool priv = FALSE, respond_to_p = TRUE; - - mrb_get_args(mrb, "o|b", &mid, &priv); - - if (mrb_symbol_p(mid)) { - id = mrb_symbol(mid); - } - else { - mrb_value tmp; - if (mrb_string_p(mid)) { - tmp = mrb_check_intern_str(mrb, mid); - } - else { - tmp = mrb_check_string_type(mrb, mid); - if (mrb_nil_p(tmp)) { - tmp = mrb_inspect(mrb, mid); - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp); - } - tmp = mrb_check_intern_str(mrb, tmp); - } - if (mrb_nil_p(tmp)) { - respond_to_p = FALSE; - } - else { - id = mrb_symbol(tmp); - } - } - - if (respond_to_p) { - respond_to_p = basic_obj_respond_to(mrb, self, id, !priv); - } + mrb_bool priv = FALSE, respond_to_p; + mrb_get_args(mrb, "n|b", &id, &priv); + respond_to_p = basic_obj_respond_to(mrb, self, id, !priv); if (!respond_to_p) { - rtm_id = mrb_intern_lit(mrb, "respond_to_missing?"); + rtm_id = MRB_SYM_Q(respond_to_missing); if (basic_obj_respond_to(mrb, self, rtm_id, !priv)) { mrb_value args[2], v; - args[0] = mid; + args[0] = mrb_symbol_value(id); args[1] = mrb_bool_value(priv); v = mrb_funcall_argv(mrb, self, rtm_id, 2, args); return mrb_bool_value(mrb_bool(v)); @@ -815,12 +580,30 @@ obj_respond_to(mrb_state *mrb, mrb_value self) static mrb_value mrb_obj_ceqq(mrb_state *mrb, mrb_value self) { - mrb_value v; + mrb_value v = mrb_get_arg1(mrb); mrb_int i, len; - mrb_sym eqq = mrb_intern_lit(mrb, "==="); - mrb_value ary = mrb_ary_splat(mrb, self); + mrb_sym eqq = MRB_OPSYM(eqq); + mrb_value ary; - mrb_get_args(mrb, "o", &v); + mrb->c->ci->mid = 0; + if (mrb_array_p(self)) { + ary = self; + } + else if (mrb_nil_p(self)) { + return mrb_false_value(); + } + else if (!mrb_respond_to(mrb, self, mrb_intern_lit(mrb, "to_a"))) { + mrb_value c = mrb_funcall_argv(mrb, self, eqq, 1, &v); + if (mrb_test(c)) return mrb_true_value(); + return mrb_false_value(); + } + else { + ary = mrb_funcall(mrb, self, "to_a", 0); + if (mrb_nil_p(ary)) { + return mrb_funcall_argv(mrb, self, eqq, 1, &v); + } + mrb_ensure_array_type(mrb, ary); + } len = RARRAY_LEN(ary); for (i=0; i<len; i++) { mrb_value c = mrb_funcall_argv(mrb, mrb_ary_entry(ary, i), eqq, 1, &v); @@ -830,6 +613,7 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self) } mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value); + void mrb_init_kernel(mrb_state *mrb) { @@ -848,11 +632,9 @@ mrb_init_kernel(mrb_state *mrb) mrb_define_method(mrb, krn, "clone", mrb_obj_clone, MRB_ARGS_NONE()); /* 15.3.1.3.8 */ mrb_define_method(mrb, krn, "dup", mrb_obj_dup, MRB_ARGS_NONE()); /* 15.3.1.3.9 */ mrb_define_method(mrb, krn, "eql?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.10 */ - mrb_define_method(mrb, krn, "equal?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.11 */ mrb_define_method(mrb, krn, "extend", mrb_obj_extend_m, MRB_ARGS_ANY()); /* 15.3.1.3.13 */ mrb_define_method(mrb, krn, "freeze", mrb_obj_freeze, MRB_ARGS_NONE()); mrb_define_method(mrb, krn, "frozen?", mrb_obj_frozen, MRB_ARGS_NONE()); - mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 */ mrb_define_method(mrb, krn, "hash", mrb_obj_hash, MRB_ARGS_NONE()); /* 15.3.1.3.15 */ mrb_define_method(mrb, krn, "initialize_copy", mrb_obj_init_copy, MRB_ARGS_REQ(1)); /* 15.3.1.3.16 */ mrb_define_method(mrb, krn, "inspect", mrb_obj_inspect, MRB_ARGS_NONE()); /* 15.3.1.3.17 */ @@ -861,19 +643,16 @@ mrb_init_kernel(mrb_state *mrb) mrb_define_method(mrb, krn, "is_a?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.24 */ mrb_define_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.25 */ mrb_define_method(mrb, krn, "kind_of?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.26 */ -#ifdef MRB_DEFAULT_METHOD_MISSING mrb_define_method(mrb, krn, "method_missing", mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */ -#endif mrb_define_method(mrb, krn, "nil?", mrb_false, MRB_ARGS_NONE()); /* 15.3.1.3.32 */ mrb_define_method(mrb, krn, "object_id", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.33 */ mrb_define_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_ANY()); /* 15.3.1.3.40 */ mrb_define_method(mrb, krn, "remove_instance_variable", mrb_obj_remove_instance_variable,MRB_ARGS_REQ(1)); /* 15.3.1.3.41 */ - mrb_define_method(mrb, krn, "respond_to?", obj_respond_to, MRB_ARGS_ANY()); /* 15.3.1.3.43 */ + mrb_define_method(mrb, krn, "respond_to?", obj_respond_to, MRB_ARGS_ARG(1,1)); /* 15.3.1.3.43 */ mrb_define_method(mrb, krn, "to_s", mrb_any_to_s, MRB_ARGS_NONE()); /* 15.3.1.3.46 */ mrb_define_method(mrb, krn, "__case_eqq", mrb_obj_ceqq, MRB_ARGS_REQ(1)); /* internal */ - - mrb_define_method(mrb, krn, "class_defined?", mrb_krn_class_defined, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, krn, "__to_int", mrb_to_int, MRB_ARGS_NONE()); /* internal */ + mrb_define_method(mrb, krn, "__to_str", mrb_to_str, MRB_ARGS_NONE()); /* internal */ mrb_include_module(mrb, mrb->object_class, mrb->kernel_module); - mrb_define_alias(mrb, mrb->module_class, "dup", "clone"); /* XXX */ } diff --git a/src/load.c b/src/load.c index 54b50b14d..bd0811743 100644 --- a/src/load.c +++ b/src/load.c @@ -7,50 +7,74 @@ #include <limits.h> #include <stdlib.h> #include <string.h> +#include <math.h> #include <mruby/dump.h> #include <mruby/irep.h> #include <mruby/proc.h> #include <mruby/string.h> #include <mruby/debug.h> #include <mruby/error.h> +#include <mruby/data.h> +#include <mruby/endian.h> #if SIZE_MAX < UINT32_MAX # error size_t must be at least 32 bits wide #endif -#define FLAG_BYTEORDER_BIG 2 -#define FLAG_BYTEORDER_LIL 4 -#define FLAG_BYTEORDER_NATIVE 8 #define FLAG_SRC_MALLOC 1 #define FLAG_SRC_STATIC 0 #define SIZE_ERROR_MUL(nmemb, size) ((size_t)(nmemb) > SIZE_MAX / (size)) -static size_t -skip_padding(const uint8_t *buf) -{ - const size_t align = MRB_DUMP_ALIGNMENT; - return -(intptr_t)buf & (align-1); -} +#define DEFINE_READ_IREP_FUNC(funcdecl, basecall) \ + funcdecl \ + { \ + int ai = mrb_gc_arena_save(mrb); \ + struct RProc *proc = basecall; \ + struct mrb_irep *irep = (mrb_irep*)(proc ? proc->body.irep : NULL); \ + if (irep) proc->body.irep = NULL; \ + mrb_gc_arena_restore(mrb, ai); \ + return irep; \ + } -static size_t -offset_crc_body(void) +#ifndef MRB_NO_FLOAT +static double +str_to_double(mrb_state *mrb, const char *p) { - struct rite_binary_header header; - return ((uint8_t *)header.binary_crc - (uint8_t *)&header) + sizeof(header.binary_crc); + /* dump IEEE754 little endian binary */ + union { + char s[sizeof(double)]; + double f; + } u; + + if (littleendian) { + memcpy(u.s, p, sizeof(double)); + } + else { + size_t i; + for (i=0; i<sizeof(double); i++) { + u.s[i] = p[sizeof(double)-i-1]; + } + } + return u.f; } +#endif -static mrb_irep* -read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags) +static mrb_bool +read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags, mrb_irep **irepp) { int i; const uint8_t *src = bin; ptrdiff_t diff; uint16_t tt, pool_data_len, snl; int plen; + mrb_pool_value *pool; + mrb_sym *syms; int ai = mrb_gc_arena_save(mrb); mrb_irep *irep = mrb_add_irep(mrb); + *irepp = irep; + /* skip record size */ src += sizeof(uint32_t); @@ -63,237 +87,213 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flag src += sizeof(uint16_t); /* number of child irep */ - irep->rlen = (size_t)bin_to_uint16(src); + irep->rlen = (uint8_t)bin_to_uint16(src); src += sizeof(uint16_t); /* Binary Data Section */ - /* ISEQ BLOCK */ - irep->ilen = (uint16_t)bin_to_uint32(src); - src += sizeof(uint32_t); - src += skip_padding(src); + /* ISEQ BLOCK (and CATCH HANDLER TABLE BLOCK) */ + irep->clen = bin_to_uint16(src); /* number of catch handler */ + src += sizeof(uint16_t); + irep->ilen = bin_to_uint16(src); + src += sizeof(uint16_t); if (irep->ilen > 0) { + size_t data_len = sizeof(mrb_code) * irep->ilen + + sizeof(struct mrb_irep_catch_handler) * irep->clen; + mrb_static_assert1(sizeof(struct mrb_irep_catch_handler) == 13); if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) { - return NULL; + return FALSE; } - if ((flags & FLAG_SRC_MALLOC) == 0 && - (flags & FLAG_BYTEORDER_NATIVE)) { + if ((flags & FLAG_SRC_MALLOC) == 0) { irep->iseq = (mrb_code*)src; - src += sizeof(mrb_code) * irep->ilen; irep->flags |= MRB_ISEQ_NO_FREE; } else { - irep->iseq = (mrb_code *)mrb_malloc(mrb, sizeof(mrb_code) * irep->ilen); - if (flags & FLAG_BYTEORDER_NATIVE) { - memcpy(irep->iseq, src, sizeof(uint32_t) * irep->ilen); - src += sizeof(uint32_t) * irep->ilen; - } - else if (flags & FLAG_BYTEORDER_BIG) { - for (i = 0; i < irep->ilen; i++) { - irep->iseq[i] = (mrb_code)bin_to_uint32(src); /* iseq */ - src += sizeof(uint32_t); - } - } - else { - for (i = 0; i < irep->ilen; i++) { - irep->iseq[i] = (mrb_code)bin_to_uint32l(src); /* iseq */ - src += sizeof(uint32_t); - } - } + void *buf = mrb_malloc(mrb, data_len); + irep->iseq = (mrb_code *)buf; + memcpy(buf, src, data_len); } + src += data_len; } /* POOL BLOCK */ - plen = bin_to_uint32(src); /* number of pool */ - src += sizeof(uint32_t); + plen = bin_to_uint16(src); /* number of pool */ + src += sizeof(uint16_t); if (plen > 0) { if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) { - return NULL; + return FALSE; } - irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen); + irep->pool = pool = (mrb_pool_value*)mrb_calloc(mrb, sizeof(mrb_pool_value), plen); for (i = 0; i < plen; i++) { - mrb_value s; + mrb_bool st = (flags & FLAG_SRC_MALLOC)==0; tt = *src++; /* pool TT */ - pool_data_len = bin_to_uint16(src); /* pool data length */ - src += sizeof(uint16_t); - if (flags & FLAG_SRC_MALLOC) { - s = mrb_str_new(mrb, (char *)src, pool_data_len); - } - else { - s = mrb_str_new_static(mrb, (char *)src, pool_data_len); - } - src += pool_data_len; switch (tt) { /* pool data */ - case IREP_TT_FIXNUM: { - mrb_value num = mrb_str_to_inum(mrb, s, 10, FALSE); -#ifdef MRB_WITHOUT_FLOAT - irep->pool[i] = num; + case IREP_TT_INT32: + { + mrb_int v = (int32_t)bin_to_uint32(src); + src += sizeof(uint32_t); +#ifdef MRB_64BIT + pool[i].tt = IREP_TT_INT64; + pool[i].u.i64 = (int64_t)v; #else - irep->pool[i] = mrb_float_p(num)? mrb_float_pool(mrb, mrb_float(num)) : num; + pool[i].tt = IREP_TT_INT32; + pool[i].u.i32 = v; #endif } break; + case IREP_TT_INT64: +#ifdef MRB_INT64 + { + uint64_t i64 = bin_to_uint32(src); + src += sizeof(uint32_t); + i64 <<= 32; + i64 |= bin_to_uint32(src); + src += sizeof(uint32_t); + pool[i].tt = tt; + pool[i].u.i64 = (int64_t)i64; + } + break; +#else + return FALSE; +#endif + + case IREP_TT_BIGINT: + pool_data_len = bin_to_uint8(src); /* pool data length */ + src += sizeof(uint8_t); + { + char *p; + pool[i].tt = IREP_TT_BIGINT; + p = (char*)mrb_malloc(mrb, pool_data_len+2); + memcpy(p, src, pool_data_len+2); + pool[i].u.str = (const char*)p; + } + src += pool_data_len + 2; + break; -#ifndef MRB_WITHOUT_FLOAT case IREP_TT_FLOAT: - irep->pool[i] = mrb_float_pool(mrb, mrb_str_to_dbl(mrb, s, FALSE)); +#ifndef MRB_NO_FLOAT + pool[i].tt = tt; + pool[i].u.f = str_to_double(mrb, (const char*)src); + src += sizeof(double); break; +#else + return FALSE; /* MRB_NO_FLOAT */ #endif - case IREP_TT_STRING: - irep->pool[i] = mrb_str_pool(mrb, s); + case IREP_TT_STR: + pool_data_len = bin_to_uint16(src); /* pool data length */ + src += sizeof(uint16_t); + if (st) { + pool[i].tt = (pool_data_len<<2) | IREP_TT_SSTR; + pool[i].u.str = (const char*)src; + } + else { + char *p; + pool[i].tt = (pool_data_len<<2) | IREP_TT_STR; + p = (char*)mrb_malloc(mrb, pool_data_len+1); + memcpy(p, src, pool_data_len+1); + pool[i].u.str = (const char*)p; + } + src += pool_data_len + 1; break; default: /* should not happen */ - irep->pool[i] = mrb_nil_value(); - break; + return FALSE; } - irep->plen++; - mrb_gc_arena_restore(mrb, ai); + irep->plen = i+1; } } /* SYMS BLOCK */ - irep->slen = (uint16_t)bin_to_uint32(src); /* syms length */ - src += sizeof(uint32_t); + irep->slen = bin_to_uint16(src); /* syms length */ + src += sizeof(uint16_t); if (irep->slen > 0) { if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) { - return NULL; + return FALSE; } - irep->syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen); + irep->syms = syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen); for (i = 0; i < irep->slen; i++) { snl = bin_to_uint16(src); /* symbol name length */ src += sizeof(uint16_t); if (snl == MRB_DUMP_NULL_SYM_LEN) { - irep->syms[i] = 0; + syms[i] = 0; continue; } if (flags & FLAG_SRC_MALLOC) { - irep->syms[i] = mrb_intern(mrb, (char *)src, snl); + syms[i] = mrb_intern(mrb, (char *)src, snl); } else { - irep->syms[i] = mrb_intern_static(mrb, (char *)src, snl); + syms[i] = mrb_intern_static(mrb, (char *)src, snl); } src += snl + 1; - mrb_gc_arena_restore(mrb, ai); } } - irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*irep->rlen); - diff = src - bin; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); *len = (size_t)diff; - return irep; + return TRUE; } -static mrb_irep* -read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags) +static mrb_bool +read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags, mrb_irep **irepp) { - mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags); + int ai = mrb_gc_arena_save(mrb); + mrb_bool readsuccess = read_irep_record_1(mrb, bin, len, flags, irepp); + mrb_irep **reps; int i; - if (irep == NULL) { - return NULL; + mrb_gc_arena_restore(mrb, ai); + if (!readsuccess) { + return FALSE; } + reps = (mrb_irep**)mrb_calloc(mrb, (*irepp)->rlen, sizeof(mrb_irep*)); + (*irepp)->reps = (const mrb_irep**)reps; + bin += *len; - for (i=0; i<irep->rlen; i++) { + for (i=0; i<(*irepp)->rlen; i++) { size_t rlen; - irep->reps[i] = read_irep_record(mrb, bin, &rlen, flags); - if (irep->reps[i] == NULL) { - return NULL; + readsuccess = read_irep_record(mrb, bin, &rlen, flags, &reps[i]); + mrb_gc_arena_restore(mrb, ai); + if (!readsuccess) { + return FALSE; } bin += rlen; *len += rlen; } - return irep; + + return TRUE; } static mrb_irep* -read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) +read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags, struct RProc **proc) { size_t len; - bin += sizeof(struct rite_section_irep_header); - return read_irep_record(mrb, bin, &len, flags); -} + /* + * This proc object keeps all the data in progress to avoid memory leaks + * if something goes wrong while reading irep. + */ + *proc = mrb_proc_new(mrb, NULL); -static int -read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *len) -{ - size_t i, fname_len, niseq; - char *fname; - uint16_t *lines; - - *len = 0; - bin += sizeof(uint32_t); /* record size */ - *len += sizeof(uint32_t); - fname_len = bin_to_uint16(bin); - bin += sizeof(uint16_t); - *len += sizeof(uint16_t); - fname = (char *)mrb_malloc(mrb, fname_len + 1); - memcpy(fname, bin, fname_len); - fname[fname_len] = '\0'; - bin += fname_len; - *len += fname_len; - - niseq = (size_t)bin_to_uint32(bin); - bin += sizeof(uint32_t); /* niseq */ - *len += sizeof(uint32_t); - - if (SIZE_ERROR_MUL(niseq, sizeof(uint16_t))) { - return MRB_DUMP_GENERAL_FAILURE; - } - lines = (uint16_t *)mrb_malloc(mrb, niseq * sizeof(uint16_t)); - for (i = 0; i < niseq; i++) { - lines[i] = bin_to_uint16(bin); - bin += sizeof(uint16_t); /* niseq */ - *len += sizeof(uint16_t); + mrb_irep **irepp = (mrb_irep**)&(*proc)->body.irep; + bin += sizeof(struct rite_section_irep_header); + if (read_irep_record(mrb, bin, &len, flags, irepp)) { + return *irepp; } - - irep->filename = fname; - irep->lines = lines; - return MRB_DUMP_OK; -} - -static int -read_lineno_record(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *lenp) -{ - int result = read_lineno_record_1(mrb, bin, irep, lenp); - int i; - - if (result != MRB_DUMP_OK) return result; - for (i = 0; i < irep->rlen; i++) { - size_t len; - - result = read_lineno_record(mrb, bin, irep->reps[i], &len); - if (result != MRB_DUMP_OK) break; - bin += len; - *lenp += len; + else { + return NULL; } - return result; -} - -static int -read_section_lineno(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep) -{ - size_t len; - - len = 0; - bin += sizeof(struct rite_section_lineno_header); - - /* Read Binary Data Section */ - return read_lineno_record(mrb, bin, irep, &len); } static int @@ -304,26 +304,26 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * size_t record_size; uint16_t f_idx; int i; + mrb_irep_debug_info *debug; if (irep->debug_info) { return MRB_DUMP_INVALID_IREP; } - irep->debug_info = (mrb_irep_debug_info*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info)); - irep->debug_info->pc_count = (uint32_t)irep->ilen; + irep->debug_info = debug = (mrb_irep_debug_info*)mrb_calloc(mrb, 1, sizeof(mrb_irep_debug_info)); + debug->pc_count = (uint32_t)irep->ilen; record_size = (size_t)bin_to_uint32(bin); bin += sizeof(uint32_t); - irep->debug_info->flen = bin_to_uint16(bin); - irep->debug_info->files = (mrb_irep_debug_info_file**)mrb_malloc(mrb, sizeof(mrb_irep_debug_info*) * irep->debug_info->flen); + debug->flen = bin_to_uint16(bin); + debug->files = (mrb_irep_debug_info_file**)mrb_calloc(mrb, irep->debug_info->flen, sizeof(mrb_irep_debug_info*)); bin += sizeof(uint16_t); - for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { + for (f_idx = 0; f_idx < debug->flen; ++f_idx) { mrb_irep_debug_info_file *file; uint16_t filename_idx; - mrb_int len; - file = (mrb_irep_debug_info_file *)mrb_malloc(mrb, sizeof(*file)); - irep->debug_info->files[f_idx] = file; + file = (mrb_irep_debug_info_file *)mrb_calloc(mrb, 1, sizeof(*file)); + debug->files[f_idx] = file; file->start_pos = bin_to_uint32(bin); bin += sizeof(uint32_t); @@ -333,8 +333,6 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * bin += sizeof(uint16_t); mrb_assert(filename_idx < filenames_len); file->filename_sym = filenames[filename_idx]; - len = 0; - file->filename = mrb_sym2name_len(mrb, file->filename_sym, &len); file->line_entry_count = bin_to_uint32(bin); bin += sizeof(uint32_t); @@ -354,8 +352,8 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * case mrb_debug_line_flat_map: { uint32_t l; - file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc( - mrb, sizeof(mrb_irep_debug_info_line) * (size_t)(file->line_entry_count)); + file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_calloc( + mrb, (size_t)(file->line_entry_count), sizeof(mrb_irep_debug_info_line)); for (l = 0; l < file->line_entry_count; ++l) { file->lines.flat_map[l].start_pos = bin_to_uint32(bin); bin += sizeof(uint32_t); @@ -364,6 +362,12 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * } } break; + case mrb_debug_line_packed_map: { + file->lines.packed_map = (uint8_t*)mrb_calloc(mrb, 1, (size_t)file->line_entry_count); + memcpy(file->lines.packed_map, bin, file->line_entry_count); + bin += file->line_entry_count; + } break; + default: return MRB_DUMP_GENERAL_FAILURE; } } @@ -379,7 +383,7 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * size_t len; int ret; - ret = read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len); + ret = read_debug_record(mrb, bin, (mrb_irep*)irep->reps[i], &len, filenames, filenames_len); if (ret != MRB_DUMP_OK) return ret; bin += len; } @@ -402,6 +406,7 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t int result; uint16_t filenames_len; mrb_sym *filenames; + mrb_value filenames_obj; bin = start; header = (struct rite_section_debug_header *)bin; @@ -409,7 +414,8 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t filenames_len = bin_to_uint16(bin); bin += sizeof(uint16_t); - filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)filenames_len); + filenames_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)filenames_len); + filenames = (mrb_sym*)RSTRING_PTR(filenames_obj); for (i = 0; i < filenames_len; ++i) { uint16_t f_len = bin_to_uint16(bin); bin += sizeof(uint16_t); @@ -433,7 +439,7 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t } debug_exit: - mrb_free(mrb, filenames); + mrb_str_resize(mrb, filenames_obj, 0); return result; } @@ -441,34 +447,31 @@ static int read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *record_len, mrb_sym const *syms, uint32_t syms_len) { const uint8_t *bin = start; + mrb_sym *lv; ptrdiff_t diff; int i; - irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (irep->nlocals - 1)); + irep->lv = lv = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (irep->nlocals - 1)); - for (i = 0; i + 1< irep->nlocals; ++i) { + for (i = 0; i + 1 < irep->nlocals; ++i) { uint16_t const sym_idx = bin_to_uint16(bin); bin += sizeof(uint16_t); if (sym_idx == RITE_LV_NULL_MARK) { - irep->lv[i].name = 0; - irep->lv[i].r = 0; + lv[i] = 0; } else { if (sym_idx >= syms_len) { return MRB_DUMP_GENERAL_FAILURE; } - irep->lv[i].name = syms[sym_idx]; - - irep->lv[i].r = bin_to_uint16(bin); + lv[i] = syms[sym_idx]; } - bin += sizeof(uint16_t); } for (i = 0; i < irep->rlen; ++i) { size_t len; int ret; - ret = read_lv_record(mrb, bin, irep->reps[i], &len, syms, syms_len); + ret = read_lv_record(mrb, bin, (mrb_irep*)irep->reps[i], &len, syms, syms_len); if (ret != MRB_DUMP_OK) return ret; bin += len; } @@ -491,6 +494,7 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t fl int result; uint32_t syms_len; mrb_sym *syms; + mrb_value syms_obj; mrb_sym (*intern_func)(mrb_state*, const char*, size_t) = (flags & FLAG_SRC_MALLOC)? mrb_intern : mrb_intern_static; @@ -500,7 +504,8 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t fl syms_len = bin_to_uint32(bin); bin += sizeof(uint32_t); - syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)syms_len); + syms_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)syms_len); + syms = (mrb_sym*)RSTRING_PTR(syms_obj); for (i = 0; i < syms_len; ++i) { uint16_t const str_len = bin_to_uint16(bin); bin += sizeof(uint16_t); @@ -520,77 +525,66 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t fl } lv_exit: - mrb_free(mrb, syms); + mrb_str_resize(mrb, syms_obj, 0); return result; } static int -read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t *flags) +read_binary_header(const uint8_t *bin, size_t bufsize, size_t *bin_size, uint8_t *flags) { const struct rite_binary_header *header = (const struct rite_binary_header *)bin; - if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) == 0) { - if (bigendian_p()) - *flags |= FLAG_BYTEORDER_NATIVE; - else - *flags |= FLAG_BYTEORDER_BIG; + if (bufsize < sizeof(struct rite_binary_header)) { + return MRB_DUMP_READ_FAULT; } - else if (memcmp(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)) == 0) { - if (bigendian_p()) - *flags |= FLAG_BYTEORDER_LIL; - else - *flags |= FLAG_BYTEORDER_NATIVE; - } - else { + + if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) != 0) { return MRB_DUMP_INVALID_FILE_HEADER; } - if (crc) { - *crc = bin_to_uint16(header->binary_crc); + /* if major version is different, they are incompatible */ + if (memcmp(header->major_version, RITE_BINARY_MAJOR_VER, sizeof(header->major_version)) != 0) { + return MRB_DUMP_INVALID_FILE_HEADER; } + /* if minor version is different, we can accept the older version */ + if (memcmp(header->minor_version, RITE_BINARY_MINOR_VER, sizeof(header->minor_version)) > 0) { + return MRB_DUMP_INVALID_FILE_HEADER; + } + *bin_size = (size_t)bin_to_uint32(header->binary_size); + if (bufsize < *bin_size) { + return MRB_DUMP_READ_FAULT; + } + return MRB_DUMP_OK; } -static mrb_irep* -read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) +static struct RProc* +read_irep(mrb_state *mrb, const uint8_t *bin, size_t bufsize, uint8_t flags) { int result; + struct RProc *proc = NULL; mrb_irep *irep = NULL; const struct rite_section_header *section_header; - uint16_t crc; size_t bin_size = 0; - size_t n; if ((mrb == NULL) || (bin == NULL)) { return NULL; } - result = read_binary_header(bin, &bin_size, &crc, &flags); + result = read_binary_header(bin, bufsize, &bin_size, &flags); if (result != MRB_DUMP_OK) { return NULL; } - n = offset_crc_body(); - if (crc != calc_crc_16_ccitt(bin + n, bin_size - n, 0)) { - return NULL; - } - bin += sizeof(struct rite_binary_header); do { section_header = (const struct rite_section_header *)bin; if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) { - irep = read_section_irep(mrb, bin, flags); + irep = read_section_irep(mrb, bin, flags, &proc); if (!irep) return NULL; } - else if (memcmp(section_header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(section_header->section_ident)) == 0) { - if (!irep) return NULL; /* corrupted data */ - result = read_section_lineno(mrb, bin, irep); - if (result < MRB_DUMP_OK) { - return NULL; - } - } else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; /* corrupted data */ result = read_section_debug(mrb, bin, irep, flags); @@ -608,43 +602,53 @@ read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) bin += bin_to_uint32(section_header->section_size); } while (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0); - return irep; + return proc; } -mrb_irep* -mrb_read_irep(mrb_state *mrb, const uint8_t *bin) +static struct RProc* +mrb_proc_read_irep(mrb_state *mrb, const uint8_t *bin) { -#ifdef MRB_USE_ETEXT_EDATA +#if defined(MRB_USE_LINK_TIME_RO_DATA_P) || defined(MRB_USE_CUSTOM_RO_DATA_P) uint8_t flags = mrb_ro_data_p((char*)bin) ? FLAG_SRC_STATIC : FLAG_SRC_MALLOC; #else uint8_t flags = FLAG_SRC_STATIC; #endif - return read_irep(mrb, bin, flags); + return read_irep(mrb, bin, (size_t)-1, flags); +} + +DEFINE_READ_IREP_FUNC( + mrb_irep *mrb_read_irep(mrb_state *mrb, const uint8_t *bin), + mrb_proc_read_irep(mrb, bin)) + +static struct RProc* +mrb_proc_read_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize) +{ + return read_irep(mrb, (const uint8_t *)buf, bufsize, FLAG_SRC_MALLOC); } +DEFINE_READ_IREP_FUNC( + MRB_API mrb_irep *mrb_read_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize), + mrb_proc_read_irep_buf(mrb, buf, bufsize)) + void mrb_exc_set(mrb_state *mrb, mrb_value exc); static void irep_error(mrb_state *mrb) { - mrb_exc_set(mrb, mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error")); + mrb_exc_set(mrb, mrb_exc_new_lit(mrb, E_SCRIPT_ERROR, "irep load error")); } void mrb_codedump_all(mrb_state*, struct RProc*); static mrb_value -load_irep(mrb_state *mrb, mrb_irep *irep, mrbc_context *c) +load_irep(mrb_state *mrb, struct RProc *proc, mrbc_context *c) { - struct RProc *proc; - - if (!irep) { + if (!proc || !proc->body.irep) { irep_error(mrb); return mrb_nil_value(); } - proc = mrb_proc_new(mrb, irep); proc->c = NULL; - mrb_irep_decref(mrb, irep); if (c && c->dump_result) mrb_codedump_all(mrb, proc); if (c && c->no_exec) return mrb_obj_value(proc); return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); @@ -653,7 +657,15 @@ load_irep(mrb_state *mrb, mrb_irep *irep, mrbc_context *c) MRB_API mrb_value mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c) { - return load_irep(mrb, mrb_read_irep(mrb, bin), c); + struct RProc *proc = mrb_proc_read_irep(mrb, bin); + if (!proc) return mrb_undef_value(); + return load_irep(mrb, proc, c); +} + +MRB_API mrb_value +mrb_load_irep_buf_cxt(mrb_state *mrb, const void *buf, size_t bufsize, mrbc_context *c) +{ + return load_irep(mrb, mrb_proc_read_irep_buf(mrb, buf, bufsize), c); } MRB_API mrb_value @@ -662,12 +674,24 @@ mrb_load_irep(mrb_state *mrb, const uint8_t *bin) return mrb_load_irep_cxt(mrb, bin, NULL); } -#ifndef MRB_DISABLE_STDIO +MRB_API mrb_value +mrb_load_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize) +{ + return mrb_load_irep_buf_cxt(mrb, buf, bufsize, NULL); +} -mrb_irep* -mrb_read_irep_file(mrb_state *mrb, FILE* fp) +MRB_API mrb_value +mrb_load_proc(mrb_state *mrb, const struct RProc *proc) { - mrb_irep *irep = NULL; + return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); +} + +#ifndef MRB_NO_STDIO + +static struct RProc* +mrb_proc_read_irep_file(mrb_state *mrb, FILE *fp) +{ + struct RProc *proc = NULL; uint8_t *buf; const size_t header_size = sizeof(struct rite_binary_header); size_t buf_size = 0; @@ -682,7 +706,7 @@ mrb_read_irep_file(mrb_state *mrb, FILE* fp) if (fread(buf, header_size, 1, fp) == 0) { goto irep_exit; } - result = read_binary_header(buf, &buf_size, NULL, &flags); + result = read_binary_header(buf, (size_t)-1, &buf_size, &flags); if (result != MRB_DUMP_OK || buf_size <= header_size) { goto irep_exit; } @@ -691,17 +715,21 @@ mrb_read_irep_file(mrb_state *mrb, FILE* fp) if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) { goto irep_exit; } - irep = read_irep(mrb, buf, FLAG_SRC_MALLOC); + proc = read_irep(mrb, buf, (size_t)-1, FLAG_SRC_MALLOC); irep_exit: mrb_free(mrb, buf); - return irep; + return proc; } +DEFINE_READ_IREP_FUNC( + mrb_irep *mrb_read_irep_file(mrb_state *mrb, FILE *fp), + mrb_proc_read_irep_file(mrb, fp)) + MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c) { - return load_irep(mrb, mrb_read_irep_file(mrb, fp), c); + return load_irep(mrb, mrb_proc_read_irep_file(mrb, fp), c); } MRB_API mrb_value @@ -709,4 +737,4 @@ mrb_load_irep_file(mrb_state *mrb, FILE* fp) { return mrb_load_irep_file_cxt(mrb, fp, NULL); } -#endif /* MRB_DISABLE_STDIO */ +#endif /* MRB_NO_STDIO */ diff --git a/src/mruby_core.rake b/src/mruby_core.rake deleted file mode 100644 index bb3d7b633..000000000 --- a/src/mruby_core.rake +++ /dev/null @@ -1,20 +0,0 @@ -MRuby.each_target do - current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd) - relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT) - current_build_dir = "#{build_dir}/#{relative_from_root}" - - objs = Dir.glob("#{current_dir}/*.c").map { |f| - next nil if cxx_exception_enabled? and f =~ /(error|vm).c$/ - next nil if self.cc.defines.flatten.include?("MRB_WITHOUT_FLOAT") and f =~ /fmt_fp.c$/ - objfile(f.pathmap("#{current_build_dir}/%n")) - }.compact - - if cxx_exception_enabled? - objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" } - end - self.libmruby << objs - - file libfile("#{build_dir}/lib/libmruby_core") => objs do |t| - archiver.run t.name, t.prerequisites - end -end diff --git a/src/numeric.c b/src/numeric.c index f7f0318e8..b22026ebb 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -1,50 +1,65 @@ /* -** numeric.c - Numeric, Integer, Float, Fixnum class +** numeric.c - Numeric, Integer, Float class ** ** See Copyright Notice in mruby.h */ -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT #include <float.h> #include <math.h> #endif #include <limits.h> #include <stdlib.h> +#include <string.h> #include <mruby.h> #include <mruby/array.h> #include <mruby/numeric.h> #include <mruby/string.h> #include <mruby/class.h> +#include <mruby/presym.h> -#ifndef MRB_WITHOUT_FLOAT -#ifdef MRB_USE_FLOAT +#ifndef MRB_NO_FLOAT +#ifdef MRB_USE_FLOAT32 #define trunc(f) truncf(f) -#define floor(f) floorf(f) -#define ceil(f) ceilf(f) #define fmod(x,y) fmodf(x,y) -#define MRB_FLO_TO_STR_FMT "%.8g" #else -#define MRB_FLO_TO_STR_FMT "%.16g" #endif #endif -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT MRB_API mrb_float -mrb_to_flo(mrb_state *mrb, mrb_value val) +mrb_as_float(mrb_state *mrb, mrb_value val) { switch (mrb_type(val)) { - case MRB_TT_FIXNUM: - return (mrb_float)mrb_fixnum(val); + case MRB_TT_INTEGER: + return (mrb_float)mrb_integer(val); case MRB_TT_FLOAT: break; - default: + case MRB_TT_STRING: + case MRB_TT_FALSE: + case MRB_TT_TRUE: mrb_raise(mrb, E_TYPE_ERROR, "non float value"); + default: + val = mrb_type_convert(mrb, val, MRB_TT_FLOAT, MRB_SYM(to_f)); + break; } return mrb_float(val); } #endif +static void +int_overflow(mrb_state *mrb, const char *reason) +{ + mrb_raisef(mrb, E_RANGE_ERROR, "integer overflow in %s", reason); +} + +static void +int_zerodiv(mrb_state *mrb) +{ + mrb_raise(mrb, E_ZERODIV_ERROR, "divided by 0"); +} + /* * call-seq: * @@ -55,74 +70,93 @@ mrb_to_flo(mrb_state *mrb, mrb_value val) * 2.0**3 #=> 8.0 */ static mrb_value -num_pow(mrb_state *mrb, mrb_value x) +int_pow(mrb_state *mrb, mrb_value x) { - mrb_value y; -#ifndef MRB_WITHOUT_FLOAT - mrb_float d; -#endif + mrb_int base = mrb_integer(x); + mrb_int result = 1; + mrb_int exp; - mrb_get_args(mrb, "o", &y); - 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; +#ifndef MRB_NO_FLOAT + mrb_value y = mrb_get_arg1(mrb); - if (exp < 0) -#ifdef MRB_WITHOUT_FLOAT - return mrb_fixnum_value(0); -#else - goto float_pow; -#endif - for (;;) { - if (exp & 1) { - if (mrb_int_mul_overflow(result, base, &result)) { -#ifndef MRB_WITHOUT_FLOAT - goto float_pow; + if (mrb_float_p(y)) { + return mrb_float_value(mrb, pow((double)base, mrb_float(y))); + } + else if (mrb_integer_p(y)) { + exp = mrb_integer(y); + } + else #endif - } - } - exp >>= 1; - if (exp == 0) break; - if (mrb_int_mul_overflow(base, base, &base)) { -#ifndef MRB_WITHOUT_FLOAT - goto float_pow; + { + mrb_get_args(mrb, "i", &exp); + } + if (exp < 0) { +#ifndef MRB_NO_FLOAT + return mrb_float_value(mrb, pow((double)base, (double)exp)); +#else + int_overflow(mrb, "negative power"); #endif + } + for (;;) { + if (exp & 1) { + if (mrb_int_mul_overflow(result, base, &result)) { + int_overflow(mrb, "power"); } } - return mrb_fixnum_value(result); + exp >>= 1; + if (exp == 0) break; + if (mrb_int_mul_overflow(base, base, &base)) { + int_overflow(mrb, "power"); + } } -#ifdef MRB_WITHOUT_FLOAT - mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); -#else - float_pow: - d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y)); - return mrb_float_value(mrb, d); -#endif + return mrb_int_value(mrb, result); +} + +mrb_int +mrb_div_int(mrb_state *mrb, mrb_int x, mrb_int y) +{ + if (y == 0) { + int_zerodiv(mrb); + } + else if(x == MRB_INT_MIN && y == -1) { + int_overflow(mrb, "division"); + } + else { + mrb_int div = x / y; + + if ((x ^ y) < 0 && x != div * y) { + div -= 1; + } + return div; + } + /* not reached */ + return 0; } /* 15.2.8.3.4 */ /* 15.2.9.3.4 */ /* * call-seq: - * num / other -> num + * int / other -> num * * Performs division: the class of the resulting object depends on * the class of <code>num</code> and on the magnitude of the * result. */ - -mrb_value -mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y) +static mrb_value +int_div(mrb_state *mrb, mrb_value x) { -#ifdef MRB_WITHOUT_FLOAT - if (!mrb_fixnum_p(y)) { - mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); + mrb_value y = mrb_get_arg1(mrb); + mrb_int a = mrb_integer(x); + + if (mrb_integer_p(y)) { + mrb_int div = mrb_div_int(mrb, a, mrb_integer(y)); + return mrb_int_value(mrb, div); } - return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y)); +#ifdef MRB_NO_FLOAT + mrb_raise(mrb, E_TYPE_ERROR, "non integer division"); #else - return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y)); + return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y))); #endif } @@ -134,53 +168,199 @@ mrb_num_div(mrb_state *mrb, mrb_value x, mrb_value y) * Returns most exact division. */ +/* + * call-seq: + * int.div(other) -> int + * + * Performs division: resulting integer. + */ static mrb_value -num_div(mrb_state *mrb, mrb_value x) +int_idiv(mrb_state *mrb, mrb_value x) { -#ifdef MRB_WITHOUT_FLOAT - mrb_value y; + mrb_int y; - mrb_get_args(mrb, "o", &y); - if (!mrb_fixnum_p(y)) { - mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); + mrb_get_args(mrb, "i", &y); + if (y == 0) { + int_zerodiv(mrb); } - return mrb_fixnum_value(mrb_fixnum(x) / mrb_fixnum(y)); + return mrb_fixnum_value(mrb_integer(x) / y); +} + +static mrb_value +int_quo(mrb_state *mrb, mrb_value xv) +{ +#ifdef MRB_NO_FLOAT + return int_idiv(mrb, xv); #else mrb_float y; mrb_get_args(mrb, "f", &y); - return mrb_float_value(mrb, mrb_to_flo(mrb, x) / y); + if (y == 0) { + int_zerodiv(mrb); + } + return mrb_float_value(mrb, mrb_integer(xv) / y); #endif } -#ifndef MRB_WITHOUT_FLOAT +static mrb_value +coerce_step_counter(mrb_state *mrb, mrb_value self) +{ + mrb_value num, step; + + mrb_get_args(mrb, "oo", &num, &step); + +#ifndef MRB_NO_FLOAT + mrb->c->ci->mid = 0; + if (mrb_float_p(num) || mrb_float_p(step)) { + return mrb_to_float(mrb, self); + } +#endif + + return self; +} + +#ifndef MRB_NO_FLOAT /******************************************************************** * * Document-class: Float * * <code>Float</code> objects represent inexact real numbers using - * the native architecture's double-precision floating point + * the native architecture's double-precision floating-point * representation. */ +static mrb_value +flo_pow(mrb_state *mrb, mrb_value x) +{ + mrb_value y = mrb_get_arg1(mrb); + mrb_float d = pow(mrb_as_float(mrb, x), mrb_as_float(mrb, y)); + return mrb_float_value(mrb, d); +} + +static mrb_value +flo_idiv(mrb_state *mrb, mrb_value xv) +{ + mrb_int y, div; + + mrb_get_args(mrb, "i", &y); + div = mrb_div_int(mrb, (mrb_int)mrb_float(xv), y); + return mrb_int_value(mrb, (mrb_int)div); +} + +mrb_float +mrb_div_float(mrb_float x, mrb_float y) +{ + if (y != 0.0) { + return x / y; + } + else if (x == 0.0) { + return NAN; + } + else { + return x * (signbit(y) ? -1.0 : 1.0) * INFINITY; + } +} + +static mrb_value +flo_div(mrb_state *mrb, mrb_value x) +{ + mrb_value y = mrb_get_arg1(mrb); + mrb_float a = mrb_float(x); + + if (mrb_float_p(y)) { + a = mrb_div_float(a, mrb_float(y)); + } + else { + a = mrb_div_float(a, mrb_as_float(mrb, y)); + } + return mrb_float_value(mrb, a); +} + +/* the argument `fmt` is no longer used; you can pass `NULL` */ +mrb_value +mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) +{ + char buf[25]; +#ifdef MRB_USE_FLOAT32 + const int prec = 7; +#else + const int prec = 15; +#endif + + mrb_format_float(mrb_float(flo), buf, sizeof(buf), 'g', prec, '\0'); + for (char *p = buf; *p; p++) { + if (*p == '.') goto exit; + if (*p == 'e') { + memmove(p+2, p, strlen(p)+1); + memcpy(p, ".0", 2); + goto exit; + } + } + strcat(buf, ".0"); + exit: + return mrb_str_new_cstr(mrb, buf); +} + /* 15.2.9.3.16(x) */ /* * call-seq: * flt.to_s -> string + * flt.inspect -> string * * Returns a string containing a representation of self. As well as a * fixed or exponential form of the number, the call may return * "<code>NaN</code>", "<code>Infinity</code>", and * "<code>-Infinity</code>". + * + * 3.0.to_s #=> 3.0 + * 3.25.to_s #=> 3.25 */ static mrb_value flo_to_s(mrb_state *mrb, mrb_value flt) { - if (isnan(mrb_float(flt))) { - return mrb_str_new_lit(mrb, "NaN"); + mrb_float f = mrb_float(flt); + mrb_value str; + + if (isinf(f)) { + str = f < 0 ? mrb_str_new_lit(mrb, "-Infinity") + : mrb_str_new_lit(mrb, "Infinity"); + } + else if (isnan(f)) { + str = mrb_str_new_lit(mrb, "NaN"); + } + else { + str = mrb_float_to_str(mrb, flt, NULL); + } + + RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); + return str; +} + +/* 15.2.9.3.1 */ +/* + * call-seq: + * float + other -> float + * + * Returns a new float which is the sum of <code>float</code> + * and <code>other</code>. + */ +static mrb_value +flo_add(mrb_state *mrb, mrb_value x) +{ + mrb_value y = mrb_get_arg1(mrb); + mrb_float a = mrb_float(x); + + switch (mrb_type(y)) { + case MRB_TT_FLOAT: + return mrb_float_value(mrb, a + mrb_float(y)); +#if defined(MRB_USE_COMPLEX) + case MRB_TT_COMPLEX: + return mrb_funcall_id(mrb, y, MRB_OPSYM(add), 1, x); +#endif + default: + return mrb_float_value(mrb, a + mrb_as_float(mrb, y)); } - return mrb_float_to_str(mrb, flt, MRB_FLO_TO_STR_FMT); } /* 15.2.9.3.2 */ @@ -193,12 +373,22 @@ flo_to_s(mrb_state *mrb, mrb_value flt) */ static mrb_value -flo_minus(mrb_state *mrb, mrb_value x) +flo_sub(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); + mrb_float a = mrb_float(x); - mrb_get_args(mrb, "o", &y); - return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y)); + switch (mrb_type(y)) { + case MRB_TT_FLOAT: + return mrb_float_value(mrb, a - mrb_float(y)); +#if defined(MRB_USE_COMPLEX) + case MRB_TT_COMPLEX: + x = mrb_funcall_id(mrb, y, MRB_OPSYM(sub), 1, x); + return mrb_funcall_id(mrb, x, MRB_OPSYM(minus), 0); +#endif + default: + return mrb_float_value(mrb, a - mrb_as_float(mrb, y)); + } } /* 15.2.9.3.3 */ @@ -213,10 +403,19 @@ flo_minus(mrb_state *mrb, mrb_value x) static mrb_value flo_mul(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); + mrb_float a = mrb_float(x); - mrb_get_args(mrb, "o", &y); - return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y)); + switch (mrb_type(y)) { + case MRB_TT_FLOAT: + return mrb_float_value(mrb, a * mrb_float(y)); +#if defined(MRB_USE_COMPLEX) + case MRB_TT_COMPLEX: + return mrb_funcall_id(mrb, y, MRB_OPSYM(mul), 1, x); +#endif + default: + return mrb_float_value(mrb, a * mrb_as_float(mrb, y)); + } } static void @@ -230,13 +429,9 @@ flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp) goto exit; } if (y == 0.0) { - if (x == 0) div = NAN; - else if (x > 0.0) div = INFINITY; - else div = -INFINITY; /* x < 0.0 */ - mod = NAN; - goto exit; + int_zerodiv(mrb); } - if ((x == 0.0) || (isinf(y) && !isinf(x))) { + if (isinf(y) && !isinf(x)) { mod = x; } else { @@ -249,6 +444,8 @@ flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp) div = (x - mod) / y; if (modp && divp) div = round(div); } + if (div == 0) div = 0.0; + if (mod == 0) mod = 0.0; if (y*mod < 0) { mod += y; div -= 1.0; @@ -273,12 +470,10 @@ flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp) static mrb_value flo_mod(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); mrb_float mod; - mrb_get_args(mrb, "o", &y); - - flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod); + flodivmod(mrb, mrb_float(x), mrb_as_float(mrb, y), 0, &mod); return mrb_float_value(mrb, mod); } #endif @@ -296,24 +491,22 @@ flo_mod(mrb_state *mrb, mrb_value x) * (1.0).eql?(1.0) #=> true */ static mrb_value -fix_eql(mrb_state *mrb, mrb_value x) +int_eql(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &y); - if (!mrb_fixnum_p(y)) return mrb_false_value(); - return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y)); + if (!mrb_integer_p(y)) return mrb_false_value(); + return mrb_bool_value(mrb_integer(x) == mrb_integer(y)); } -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT static mrb_value flo_eql(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &y); if (!mrb_float_p(y)) return mrb_false_value(); - return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y)); + return mrb_bool_value(mrb_float(x) == mrb_float(y)); } /* 15.2.9.3.7 */ @@ -332,14 +525,21 @@ flo_eql(mrb_state *mrb, mrb_value x) static mrb_value flo_eq(mrb_state *mrb, mrb_value x) { - mrb_value y; - mrb_get_args(mrb, "o", &y); + mrb_value y = mrb_get_arg1(mrb); switch (mrb_type(y)) { - case MRB_TT_FIXNUM: - return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y)); + case MRB_TT_INTEGER: + return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_integer(y)); case MRB_TT_FLOAT: return mrb_bool_value(mrb_float(x) == mrb_float(y)); +#ifdef MRB_USE_RATIONAL + case MRB_TT_RATIONAL: + return mrb_bool_value(mrb_float(x) == mrb_as_float(mrb, y)); +#endif +#ifdef MRB_USE_COMPLEX + case MRB_TT_COMPLEX: + return mrb_bool_value(mrb_equal(mrb, y, x)); +#endif default: return mrb_false_value(); } @@ -349,11 +549,15 @@ static int64_t value_int64(mrb_state *mrb, mrb_value x) { switch (mrb_type(x)) { - case MRB_TT_FIXNUM: - return (int64_t)mrb_fixnum(x); - break; + case MRB_TT_INTEGER: + return (int64_t)mrb_integer(x); case MRB_TT_FLOAT: - return (int64_t)mrb_float(x); + { + double f = mrb_float(x); + + if ((mrb_float)INT64_MAX >= f && f >= (mrb_float)INT64_MIN) + return (int64_t)f; + } default: mrb_raise(mrb, E_TYPE_ERROR, "cannot convert to Integer"); break; @@ -365,29 +569,26 @@ value_int64(mrb_state *mrb, mrb_value x) static mrb_value int64_value(mrb_state *mrb, int64_t v) { - if (FIXABLE(v)) { - return mrb_fixnum_value((mrb_int)v); + if (!TYPED_FIXABLE(v,int64_t)) { + int_overflow(mrb, "bit operation"); } - return mrb_float_value(mrb, (mrb_float)v); + return mrb_fixnum_value((mrb_int)v); } static mrb_value flo_rev(mrb_state *mrb, mrb_value x) { - int64_t v1; - mrb_get_args(mrb, ""); - v1 = (int64_t)mrb_float(x); + int64_t v1 = value_int64(mrb, x); return int64_value(mrb, ~v1); } static mrb_value flo_and(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); int64_t v1, v2; - mrb_get_args(mrb, "o", &y); - v1 = (int64_t)mrb_float(x); + v1 = value_int64(mrb, x); v2 = value_int64(mrb, y); return int64_value(mrb, v1 & v2); } @@ -395,11 +596,10 @@ flo_and(mrb_state *mrb, mrb_value x) static mrb_value flo_or(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); int64_t v1, v2; - mrb_get_args(mrb, "o", &y); - v1 = (int64_t)mrb_float(x); + v1 = value_int64(mrb, x); v2 = value_int64(mrb, y); return int64_value(mrb, v1 | v2); } @@ -407,11 +607,10 @@ flo_or(mrb_state *mrb, mrb_value x) static mrb_value flo_xor(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); int64_t v1, v2; - mrb_get_args(mrb, "o", &y); - v1 = (int64_t)mrb_float(x); + v1 = value_int64(mrb, x); v2 = value_int64(mrb, y); return int64_value(mrb, v1 ^ v2); } @@ -425,9 +624,17 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width) return x; } val = mrb_float(x); + if (width < -MRB_INT_BIT/2) { + if (val < 0) return mrb_fixnum_value(-1); + return mrb_fixnum_value(0); + } if (width < 0) { while (width++) { val /= 2; + if (val < 1.0) { + val = 0; + break; + } } #if defined(_ISOC99_SOURCE) val = trunc(val); @@ -447,23 +654,23 @@ flo_shift(mrb_state *mrb, mrb_value x, mrb_int width) val *= 2; } } - if (FIXABLE_FLOAT(val)) { - return mrb_fixnum_value((mrb_int)val); - } + if (FIXABLE_FLOAT(val)) + return mrb_int_value(mrb, (mrb_int)val); return mrb_float_value(mrb, val); } static mrb_value -flo_lshift(mrb_state *mrb, mrb_value x) +flo_rshift(mrb_state *mrb, mrb_value x) { mrb_int width; mrb_get_args(mrb, "i", &width); + if (width == MRB_INT_MIN) return flo_shift(mrb, x, -MRB_INT_BIT); return flo_shift(mrb, x, -width); } static mrb_value -flo_rshift(mrb_state *mrb, mrb_value x) +flo_lshift(mrb_state *mrb, mrb_value x) { mrb_int width; @@ -537,55 +744,127 @@ mrb_check_num_exact(mrb_state *mrb, mrb_float num) } } +static mrb_value +flo_ceil_floor(mrb_state *mrb, mrb_value num, double (*func)(double)) +{ + mrb_float f = mrb_float(num); + mrb_int ndigits = 0; +#ifdef MRB_USE_FLOAT32 + const int fprec = 7; +#else + const int fprec = 15; +#endif + + mrb_get_args(mrb, "|i", &ndigits); + if (f == 0.0) { + return ndigits > 0 ? mrb_float_value(mrb, f) : mrb_fixnum_value(0); + } + if (ndigits > 0) { + if (ndigits > fprec) return num; + mrb_float d = pow(10, ndigits); + f = func(f * d) / d; + return mrb_float_value(mrb, f); + } + if (ndigits < 0) { + mrb_float d = pow(10, -ndigits); + f = func(f / d) * d; + } + else { /* ndigits == 0 */ + f = func(f); + } + mrb_check_num_exact(mrb, f); + return mrb_int_value(mrb, (mrb_int)f); +} + /* 15.2.9.3.10 */ /* * call-seq: - * flt.floor -> integer + * float.floor([ndigits]) -> integer or float * - * Returns the largest integer less than or equal to <i>flt</i>. + * Returns the largest number less than or equal to +float+ with + * a precision of +ndigits+ decimal digits (default: 0). + * + * When the precision is negative, the returned value is an integer + * with at least <code>ndigits.abs</code> trailing zeros. + * + * Returns a floating point number when +ndigits+ is positive, + * otherwise returns an integer. * * 1.2.floor #=> 1 * 2.0.floor #=> 2 * (-1.2).floor #=> -2 * (-2.0).floor #=> -2 + * + * 1.234567.floor(2) #=> 1.23 + * 1.234567.floor(3) #=> 1.234 + * 1.234567.floor(4) #=> 1.2345 + * 1.234567.floor(5) #=> 1.23456 + * + * 34567.89.floor(-5) #=> 0 + * 34567.89.floor(-4) #=> 30000 + * 34567.89.floor(-3) #=> 34000 + * 34567.89.floor(-2) #=> 34500 + * 34567.89.floor(-1) #=> 34560 + * 34567.89.floor(0) #=> 34567 + * 34567.89.floor(1) #=> 34567.8 + * 34567.89.floor(2) #=> 34567.89 + * 34567.89.floor(3) #=> 34567.89 + * + * Note that the limited precision of floating point arithmetic + * might lead to surprising results: + * + * (0.3 / 0.1).floor #=> 2 (!) */ - static mrb_value flo_floor(mrb_state *mrb, mrb_value num) { - mrb_float f = floor(mrb_float(num)); - - mrb_check_num_exact(mrb, f); - if (!FIXABLE_FLOAT(f)) { - return mrb_float_value(mrb, f); - } - return mrb_fixnum_value((mrb_int)f); + return flo_ceil_floor(mrb, num, floor); } /* 15.2.9.3.8 */ /* * call-seq: - * flt.ceil -> integer + * float.ceil([ndigits]) -> integer or float + * + * Returns the smallest number greater than or equal to +float+ with + * a precision of +ndigits+ decimal digits (default: 0). + * + * When the precision is negative, the returned value is an integer + * with at least <code>ndigits.abs</code> trailing zeros. * - * Returns the smallest <code>Integer</code> greater than or equal to - * <i>flt</i>. + * Returns a floating point number when +ndigits+ is positive, + * otherwise returns an integer. * * 1.2.ceil #=> 2 * 2.0.ceil #=> 2 * (-1.2).ceil #=> -1 * (-2.0).ceil #=> -2 + * + * 1.234567.ceil(2) #=> 1.24 + * 1.234567.ceil(3) #=> 1.235 + * 1.234567.ceil(4) #=> 1.2346 + * 1.234567.ceil(5) #=> 1.23457 + * + * 34567.89.ceil(-5) #=> 100000 + * 34567.89.ceil(-4) #=> 40000 + * 34567.89.ceil(-3) #=> 35000 + * 34567.89.ceil(-2) #=> 34600 + * 34567.89.ceil(-1) #=> 34570 + * 34567.89.ceil(0) #=> 34568 + * 34567.89.ceil(1) #=> 34567.9 + * 34567.89.ceil(2) #=> 34567.89 + * 34567.89.ceil(3) #=> 34567.89 + * + * Note that the limited precision of floating point arithmetic + * might lead to surprising results: + * + * (2.1 / 0.7).ceil #=> 4 (!) */ static mrb_value flo_ceil(mrb_state *mrb, mrb_value num) { - mrb_float f = ceil(mrb_float(num)); - - mrb_check_num_exact(mrb, f); - if (!FIXABLE_FLOAT(f)) { - return mrb_float_value(mrb, f); - } - return mrb_fixnum_value((mrb_int)f); + return flo_ceil_floor(mrb, num, ceil); } /* 15.2.9.3.12 */ @@ -594,7 +873,7 @@ flo_ceil(mrb_state *mrb, mrb_value num) * flt.round([ndigits]) -> integer or float * * Rounds <i>flt</i> to a given precision in decimal digits (default 0 digits). - * Precision may be negative. Returns a floating point number when ndigits + * Precision may be negative. Returns a floating-point number when ndigits * is more than zero. * * 1.4.round #=> 1 @@ -636,6 +915,7 @@ flo_round(mrb_state *mrb, mrb_value num) f = 1.0; i = ndigits >= 0 ? ndigits : -ndigits; + if (ndigits > DBL_DIG+2) return num; while (--i >= 0) f = f*10.0; @@ -666,15 +946,28 @@ flo_round(mrb_state *mrb, mrb_value num) if (!isfinite(number)) return num; return mrb_float_value(mrb, number); } - return mrb_fixnum_value((mrb_int)number); + if (!FIXABLE_FLOAT(number)) + return mrb_float_value(mrb, number); + return mrb_int_value(mrb, (mrb_int)number); } /* 15.2.9.3.14 */ +static mrb_value +flo_to_i(mrb_state *mrb, mrb_value num) +{ + mrb_float f = mrb_float(num); + + if (f > 0.0) f = floor(f); + if (f < 0.0) f = ceil(f); + + mrb_check_num_exact(mrb, f); + return mrb_int_value(mrb, (mrb_int)f); +} + /* 15.2.9.3.15 */ /* * call-seq: * flt.to_i -> integer - * flt.to_int -> integer * flt.truncate -> integer * * Returns <i>flt</i> truncated to an <code>Integer</code>. @@ -683,16 +976,8 @@ flo_round(mrb_state *mrb, mrb_value num) static mrb_value flo_truncate(mrb_state *mrb, mrb_value num) { - mrb_float f = mrb_float(num); - - if (f > 0.0) f = floor(f); - if (f < 0.0) f = ceil(f); - - mrb_check_num_exact(mrb, f); - if (!FIXABLE_FLOAT(f)) { - return mrb_float_value(mrb, f); - } - return mrb_fixnum_value((mrb_int)f); + if (signbit(mrb_float(num))) return flo_ceil(mrb, num); + return flo_floor(mrb, num); } static mrb_value @@ -705,8 +990,7 @@ flo_nan_p(mrb_state *mrb, mrb_value num) /* * Document-class: Integer * - * <code>Integer</code> is the basis for the two concrete classes that - * hold whole numbers, <code>Bignum</code> and <code>Fixnum</code>. + * <code>Integer</code> is hold whole numbers. * */ @@ -714,7 +998,6 @@ flo_nan_p(mrb_state *mrb, mrb_value num) /* * call-seq: * int.to_i -> integer - * int.to_int -> integer * * As <i>int</i> is already an <code>Integer</code>, all these * methods simply return the receiver. @@ -726,29 +1009,59 @@ int_to_i(mrb_state *mrb, mrb_value num) return num; } -mrb_value -mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y) +static mrb_value +fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; - a = mrb_fixnum(x); - if (mrb_fixnum_p(y)) { + a = mrb_integer(x); + if (mrb_integer_p(y)) { mrb_int b, c; if (a == 0) return x; - b = mrb_fixnum(y); + b = mrb_integer(y); if (mrb_int_mul_overflow(a, b, &c)) { -#ifndef MRB_WITHOUT_FLOAT - return mrb_float_value(mrb, (mrb_float)a * (mrb_float)b); -#endif + int_overflow(mrb, "multiplication"); } - return mrb_fixnum_value(c); + return mrb_int_value(mrb, c); } -#ifdef MRB_WITHOUT_FLOAT - mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); + switch (mrb_type(y)) { +#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX) + case MRB_TT_RATIONAL: + case MRB_TT_COMPLEX: + return mrb_funcall_id(mrb, y, MRB_OPSYM(mul), 1, x); +#endif + default: +#ifdef MRB_NO_FLOAT + mrb_raise(mrb, E_TYPE_ERROR, "non integer multiplication"); #else - return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y)); + return mrb_float_value(mrb, (mrb_float)a * mrb_as_float(mrb, y)); #endif + } +} + +MRB_API mrb_value +mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y) +{ + if (mrb_integer_p(x)) { + return fixnum_mul(mrb, x, y); + } +#ifndef MRB_NO_FLOAT + if (mrb_float_p(x)) { + return mrb_float_value(mrb, mrb_float(x) * mrb_as_float(mrb, y)); + } +#endif +#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX) + switch (mrb_type(x)) { + case MRB_TT_RATIONAL: + case MRB_TT_COMPLEX: + return mrb_funcall_id(mrb, x, MRB_OPSYM(mul), 1, y); + default: + break; + } +#endif + mrb_raise(mrb, E_TYPE_ERROR, "no number multiply"); + return mrb_nil_value(); /* not reached */ } /* 15.2.8.3.3 */ @@ -762,40 +1075,33 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y) */ static mrb_value -fix_mul(mrb_state *mrb, mrb_value x) +int_mul(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &y); - return mrb_fixnum_mul(mrb, x, y); + return fixnum_mul(mrb, x, y); } static void fixdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp) { - mrb_int div, mod; - - /* TODO: add mrb_assert(y != 0) to make sure */ - - if (y < 0) { - if (x < 0) - div = -x / -y; - else - div = - (x / -y); + if (y == 0) { + int_zerodiv(mrb); } - else { - if (x < 0) - div = - (-x / y); - else - div = x / y; + else if(x == MRB_INT_MIN && y == -1) { + int_overflow(mrb, "division"); } - mod = x - div*y; - if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) { - mod += y; - div -= 1; + else { + mrb_int div = x / y; + mrb_int mod = x - div * y; + + if ((x ^ y) < 0 && x != div * y) { + mod += y; + div -= 1; + } + if (divp) *divp = div; + if (modp) *modp = mod; } - if (divp) *divp = div; - if (modp) *modp = mod; } /* 15.2.8.3.5 */ @@ -809,34 +1115,25 @@ fixdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp) */ static mrb_value -fix_mod(mrb_state *mrb, mrb_value x) +int_mod(mrb_state *mrb, mrb_value x) { - mrb_value y; - mrb_int a; + mrb_value y = mrb_get_arg1(mrb); + mrb_int a, b; - mrb_get_args(mrb, "o", &y); - a = mrb_fixnum(x); - if (mrb_fixnum_p(y)) { - mrb_int b, mod; + a = mrb_integer(x); + if (mrb_integer_p(y) && a != MRB_INT_MIN && (b=mrb_integer(y)) != MRB_INT_MIN) { + mrb_int mod; - if ((b=mrb_fixnum(y)) == 0) { -#ifdef MRB_WITHOUT_FLOAT - /* ZeroDivisionError */ - return mrb_fixnum_value(0); -#else - return mrb_float_value(mrb, NAN); -#endif - } - fixdivmod(mrb, a, b, 0, &mod); + fixdivmod(mrb, a, b, NULL, &mod); return mrb_fixnum_value(mod); } -#ifdef MRB_WITHOUT_FLOAT - mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); +#ifdef MRB_NO_FLOAT + mrb_raise(mrb, E_TYPE_ERROR, "non integer modulo"); #else else { mrb_float mod; - flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), 0, &mod); + flodivmod(mrb, (mrb_float)a, mrb_as_float(mrb, y), NULL, &mod); return mrb_float_value(mrb, mod); } #endif @@ -849,55 +1146,44 @@ fix_mod(mrb_state *mrb, mrb_value x) * See <code>Numeric#divmod</code>. */ static mrb_value -fix_divmod(mrb_state *mrb, mrb_value x) +int_divmod(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &y); - - if (mrb_fixnum_p(y)) { + if (mrb_integer_p(y)) { mrb_int div, mod; - if (mrb_fixnum(y) == 0) { -#ifdef MRB_WITHOUT_FLOAT - return mrb_assoc_new(mrb, mrb_fixnum_value(0), mrb_fixnum_value(0)); -#else - return mrb_assoc_new(mrb, ((mrb_fixnum(x) == 0) ? - mrb_float_value(mrb, NAN): - mrb_float_value(mrb, INFINITY)), - mrb_float_value(mrb, NAN)); -#endif - } - fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod); - return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod)); + fixdivmod(mrb, mrb_integer(x), mrb_integer(y), &div, &mod); + return mrb_assoc_new(mrb, mrb_int_value(mrb, div), mrb_int_value(mrb, mod)); } -#ifdef MRB_WITHOUT_FLOAT - mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); +#ifdef MRB_NO_FLOAT + mrb_raise(mrb, E_TYPE_ERROR, "non integer divmod"); #else else { mrb_float div, mod; mrb_value a, b; - flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod); - a = mrb_float_value(mrb, div); + flodivmod(mrb, (mrb_float)mrb_integer(x), mrb_as_float(mrb, y), &div, &mod); + a = mrb_int_value(mrb, (mrb_int)div); b = mrb_float_value(mrb, mod); return mrb_assoc_new(mrb, a, b); } #endif } -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT static mrb_value flo_divmod(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); mrb_float div, mod; mrb_value a, b; - mrb_get_args(mrb, "o", &y); - - flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod); - a = mrb_float_value(mrb, div); + flodivmod(mrb, mrb_float(x), mrb_as_float(mrb, y), &div, &mod); + if (!FIXABLE_FLOAT(div)) + a = mrb_float_value(mrb, div); + else + a = mrb_int_value(mrb, (mrb_int)div); b = mrb_float_value(mrb, mod); return mrb_assoc_new(mrb, a, b); } @@ -916,17 +1202,24 @@ flo_divmod(mrb_state *mrb, mrb_value x) */ static mrb_value -fix_equal(mrb_state *mrb, mrb_value x) +int_equal(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &y); switch (mrb_type(y)) { - case MRB_TT_FIXNUM: - return mrb_bool_value(mrb_fixnum(x) == mrb_fixnum(y)); -#ifndef MRB_WITHOUT_FLOAT + case MRB_TT_INTEGER: + return mrb_bool_value(mrb_integer(x) == mrb_integer(y)); +#ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: - return mrb_bool_value((mrb_float)mrb_fixnum(x) == mrb_float(y)); + return mrb_bool_value((mrb_float)mrb_integer(x) == mrb_float(y)); +#endif +#ifdef MRB_USE_RATIONAL + case MRB_TT_RATIONAL: + return mrb_bool_value(mrb_equal(mrb, y, x)); +#endif +#ifdef MRB_USE_COMPLEX + case MRB_TT_COMPLEX: + return mrb_bool_value(mrb_equal(mrb, y, x)); #endif default: return mrb_false_value(); @@ -945,24 +1238,24 @@ fix_equal(mrb_state *mrb, mrb_value x) */ static mrb_value -fix_rev(mrb_state *mrb, mrb_value num) +int_rev(mrb_state *mrb, mrb_value num) { - mrb_int val = mrb_fixnum(num); + mrb_int val = mrb_integer(num); - return mrb_fixnum_value(~val); + return mrb_int_value(mrb, ~val); } -#ifdef MRB_WITHOUT_FLOAT +#ifdef MRB_NO_FLOAT #define bit_op(x,y,op1,op2) do {\ - return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\ + return mrb_int_value(mrb, (mrb_integer(x) op2 mrb_integer(y)));\ } while(0) #else static mrb_value flo_and(mrb_state *mrb, mrb_value x); static mrb_value flo_or(mrb_state *mrb, mrb_value x); static mrb_value flo_xor(mrb_state *mrb, mrb_value x); #define bit_op(x,y,op1,op2) do {\ - if (mrb_fixnum_p(y)) return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\ - return flo_ ## op1(mrb, mrb_float_value(mrb, (mrb_float)mrb_fixnum(x)));\ + if (mrb_integer_p(y)) return mrb_int_value(mrb, (mrb_integer(x) op2 mrb_integer(y))); \ + return flo_ ## op1(mrb, mrb_float_value(mrb, (mrb_float)mrb_integer(x)));\ } while(0) #endif @@ -975,11 +1268,10 @@ static mrb_value flo_xor(mrb_state *mrb, mrb_value x); */ static mrb_value -fix_and(mrb_state *mrb, mrb_value x) +int_and(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &y); bit_op(x, y, and, &); } @@ -992,11 +1284,10 @@ fix_and(mrb_state *mrb, mrb_value x) */ static mrb_value -fix_or(mrb_state *mrb, mrb_value x) +int_or(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &y); bit_op(x, y, or, |); } @@ -1009,11 +1300,10 @@ fix_or(mrb_state *mrb, mrb_value x) */ static mrb_value -fix_xor(mrb_state *mrb, mrb_value x) +int_xor(mrb_state *mrb, mrb_value x) { - mrb_value y; + mrb_value y = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &y); bit_op(x, y, or, ^); } @@ -1022,61 +1312,34 @@ fix_xor(mrb_state *mrb, mrb_value x) static mrb_value lshift(mrb_state *mrb, mrb_int val, mrb_int width) { - if (width < 0) { /* mrb_int overflow */ -#ifdef MRB_WITHOUT_FLOAT - return mrb_fixnum_value(0); -#else - return mrb_float_value(mrb, INFINITY); -#endif - } + mrb_assert(width >= 0); if (val > 0) { if ((width > NUMERIC_SHIFT_WIDTH_MAX) || (val > (MRB_INT_MAX >> width))) { -#ifdef MRB_WITHOUT_FLOAT - return mrb_fixnum_value(-1); -#else - goto bit_overflow; -#endif + int_overflow(mrb, "bit shift"); } - return mrb_fixnum_value(val << width); + return mrb_int_value(mrb, val << width); } else { if ((width > NUMERIC_SHIFT_WIDTH_MAX) || - (val < (MRB_INT_MIN >> width))) { -#ifdef MRB_WITHOUT_FLOAT - return mrb_fixnum_value(0); -#else - goto bit_overflow; -#endif - } - return mrb_fixnum_value(val * ((mrb_int)1 << width)); - } - -#ifndef MRB_WITHOUT_FLOAT -bit_overflow: - { - mrb_float f = (mrb_float)val; - while (width--) { - f *= 2; + (val <= (MRB_INT_MIN >> width))) { + int_overflow(mrb, "bit shift"); } - return mrb_float_value(mrb, f); + return mrb_int_value(mrb, (val * ((mrb_int)1 << width))); } -#endif } static mrb_value -rshift(mrb_int val, mrb_int width) +rshift(mrb_state *mrb, mrb_int val, mrb_int width) { - if (width < 0) { /* mrb_int overflow */ - return mrb_fixnum_value(0); - } + mrb_assert(width >= 0); if (width >= NUMERIC_SHIFT_WIDTH_MAX) { if (val < 0) { return mrb_fixnum_value(-1); } return mrb_fixnum_value(0); } - return mrb_fixnum_value(val >> width); + return mrb_int_value(mrb, val >> width); } /* 15.2.8.3.12 */ @@ -1088,7 +1351,7 @@ rshift(mrb_int val, mrb_int width) */ static mrb_value -fix_lshift(mrb_state *mrb, mrb_value x) +int_lshift(mrb_state *mrb, mrb_value x) { mrb_int width, val; @@ -1096,10 +1359,11 @@ fix_lshift(mrb_state *mrb, mrb_value x) if (width == 0) { return x; } - val = mrb_fixnum(x); + val = mrb_integer(x); if (val == 0) return x; if (width < 0) { - return rshift(val, -width); + if (width == MRB_INT_MIN) return rshift(mrb, val, MRB_INT_BIT); + return rshift(mrb, val, -width); } return lshift(mrb, val, width); } @@ -1113,7 +1377,7 @@ fix_lshift(mrb_state *mrb, mrb_value x) */ static mrb_value -fix_rshift(mrb_state *mrb, mrb_value x) +int_rshift(mrb_state *mrb, mrb_value x) { mrb_int width, val; @@ -1121,12 +1385,13 @@ fix_rshift(mrb_state *mrb, mrb_value x) if (width == 0) { return x; } - val = mrb_fixnum(x); + val = mrb_integer(x); if (val == 0) return x; if (width < 0) { + if (width == MRB_INT_MIN) int_overflow(mrb, "bit shift"); return lshift(mrb, val, -width); } - return rshift(val, width); + return rshift(mrb, val, width); } /* 15.2.8.3.23 */ @@ -1138,11 +1403,11 @@ fix_rshift(mrb_state *mrb, mrb_value x) * */ -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT static mrb_value -fix_to_f(mrb_state *mrb, mrb_value num) +int_to_f(mrb_state *mrb, mrb_value num) { - return mrb_float_value(mrb, (mrb_float)mrb_fixnum(num)); + return mrb_float_value(mrb, (mrb_float)mrb_integer(num)); } /* @@ -1152,7 +1417,7 @@ fix_to_f(mrb_state *mrb, mrb_value num) * (in particular infinite or NaN) * to numerical classes which don't support them. * - * Float::INFINITY.to_r + * Float::INFINITY.to_i * * <em>raises the exception:</em> * @@ -1160,57 +1425,81 @@ fix_to_f(mrb_state *mrb, mrb_value num) */ /* ------------------------------------------------------------------------*/ MRB_API mrb_value -mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x) +mrb_float_to_integer(mrb_state *mrb, mrb_value x) { mrb_int z = 0; if (!mrb_float_p(x)) { mrb_raise(mrb, E_TYPE_ERROR, "non float value"); - z = 0; /* not reached. just suppress warnings. */ } else { mrb_float d = mrb_float(x); - if (isinf(d)) { - mrb_raise(mrb, E_FLOATDOMAIN_ERROR, d < 0 ? "-Infinity" : "Infinity"); - } - if (isnan(d)) { - mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); - } + mrb_check_num_exact(mrb, d); if (FIXABLE_FLOAT(d)) { z = (mrb_int)d; } else { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x); + mrb_raisef(mrb, E_RANGE_ERROR, "number (%v) too big for integer", x); } } - return mrb_fixnum_value(z); + return mrb_int_value(mrb, z); } #endif -mrb_value -mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y) +static mrb_value +int_plus(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; - a = mrb_fixnum(x); - if (mrb_fixnum_p(y)) { + a = mrb_integer(x); + if (mrb_integer_p(y)) { mrb_int b, c; if (a == 0) return y; - b = mrb_fixnum(y); + b = mrb_integer(y); if (mrb_int_add_overflow(a, b, &c)) { -#ifndef MRB_WITHOUT_FLOAT - return mrb_float_value(mrb, (mrb_float)a + (mrb_float)b); -#endif + int_overflow(mrb, "addition"); } - return mrb_fixnum_value(c); + return mrb_int_value(mrb, c); } -#ifdef MRB_WITHOUT_FLOAT - mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); + switch (mrb_type(y)) { +#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX) + case MRB_TT_RATIONAL: + case MRB_TT_COMPLEX: + return mrb_funcall_id(mrb, y, MRB_OPSYM(add), 1, x); +#endif + default: +#ifdef MRB_NO_FLOAT + mrb_raise(mrb, E_TYPE_ERROR, "non integer addition"); #else - return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y)); + return mrb_float_value(mrb, (mrb_float)a + mrb_as_float(mrb, y)); #endif + } +} + +MRB_API mrb_value +mrb_num_plus(mrb_state *mrb, mrb_value x, mrb_value y) +{ + if (mrb_integer_p(x)) { + return int_plus(mrb, x, y); + } +#ifndef MRB_NO_FLOAT + if (mrb_float_p(x)) { + return mrb_float_value(mrb, mrb_float(x) + mrb_as_float(mrb, y)); + } +#endif +#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX) + switch (mrb_type(x)) { + case MRB_TT_RATIONAL: + case MRB_TT_COMPLEX: + return mrb_funcall_id(mrb, x, MRB_OPSYM(add), 1, y); + default: + break; + } +#endif + mrb_raise(mrb, E_TYPE_ERROR, "no number addition"); + return mrb_nil_value(); /* not reached */ } /* 15.2.8.3.1 */ @@ -1223,36 +1512,66 @@ mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y) * result. */ static mrb_value -fix_plus(mrb_state *mrb, mrb_value self) +int_add(mrb_state *mrb, mrb_value self) { - mrb_value other; + mrb_value other = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &other); - return mrb_fixnum_plus(mrb, self, other); + return int_plus(mrb, self, other); } -mrb_value -mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y) +static mrb_value +int_minus(mrb_state *mrb, mrb_value x, mrb_value y) { mrb_int a; - a = mrb_fixnum(x); - if (mrb_fixnum_p(y)) { + a = mrb_integer(x); + if (mrb_integer_p(y)) { mrb_int b, c; - b = mrb_fixnum(y); + b = mrb_integer(y); if (mrb_int_sub_overflow(a, b, &c)) { -#ifndef MRB_WITHOUT_FLOAT - return mrb_float_value(mrb, (mrb_float)a - (mrb_float)b); -#endif + int_overflow(mrb, "subtraction"); } - return mrb_fixnum_value(c); + return mrb_int_value(mrb, c); } -#ifdef MRB_WITHOUT_FLOAT - mrb_raise(mrb, E_TYPE_ERROR, "non fixnum value"); + switch (mrb_type(y)) { +#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX) + case MRB_TT_RATIONAL: + case MRB_TT_COMPLEX: + x = mrb_funcall_id(mrb, y, MRB_OPSYM(sub), 1, x); + return mrb_funcall_id(mrb, x, MRB_OPSYM(minus), 0); +#endif + default: +#ifdef MRB_NO_FLOAT + mrb_raise(mrb, E_TYPE_ERROR, "non integer subtraction"); #else - return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y)); + return mrb_float_value(mrb, (mrb_float)a - mrb_as_float(mrb, y)); #endif + } +} + +MRB_API mrb_value +mrb_num_minus(mrb_state *mrb, mrb_value x, mrb_value y) +{ + if (mrb_integer_p(x)) { + return int_minus(mrb, x, y); + } +#ifndef MRB_NO_FLOAT + if (mrb_float_p(x)) { + return mrb_float_value(mrb, mrb_float(x) - mrb_as_float(mrb, y)); + } +#endif +#if defined(MRB_USE_RATIONAL) || defined(MRB_USE_COMPLEX) + switch (mrb_type(x)) { + case MRB_TT_RATIONAL: + case MRB_TT_COMPLEX: + return mrb_funcall_id(mrb, x, MRB_OPSYM(sub), 1, y); + default: + break; + } +#endif + mrb_raise(mrb, E_TYPE_ERROR, "no number subtraction"); + return mrb_nil_value(); /* not reached */ } /* 15.2.8.3.2 */ @@ -1266,42 +1585,60 @@ mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y) * result. */ static mrb_value -fix_minus(mrb_state *mrb, mrb_value self) +int_sub(mrb_state *mrb, mrb_value self) { - mrb_value other; + mrb_value other = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &other); - return mrb_fixnum_minus(mrb, self, other); + return int_minus(mrb, self, other); } - -MRB_API mrb_value -mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base) +MRB_API char* +mrb_int_to_cstr(char *buf, size_t len, mrb_int n, mrb_int base) { - char buf[MRB_INT_BIT+1]; - char *b = buf + sizeof buf; - mrb_int val = mrb_fixnum(x); + char *bufend = buf + len; + char *b = bufend-1; - if (base < 2 || 36 < base) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base)); - } + if (base < 2 || 36 < base) return NULL; + if (len < 2) return NULL; - if (val == 0) { - *--b = '0'; + if (n == 0) { + buf[0] = '0'; + buf[1] = '\0'; + return buf; } - else if (val < 0) { + + *b = '\0'; + if (n < 0) { do { - *--b = mrb_digitmap[-(val % base)]; - } while (val /= base); - *--b = '-'; + if (b-- == buf) return NULL; + *b = mrb_digitmap[-(n % base)]; + } while (n /= base); + if (b-- == buf) return NULL; + *b = '-'; } else { do { - *--b = mrb_digitmap[(int)(val % base)]; - } while (val /= base); + if (b-- == buf) return NULL; + *b = mrb_digitmap[(int)(n % base)]; + } while (n /= base); } + return b; +} - return mrb_str_new(mrb, b, buf + sizeof(buf) - b); +MRB_API mrb_value +mrb_integer_to_str(mrb_state *mrb, mrb_value x, mrb_int base) +{ + char buf[MRB_INT_BIT+1]; + mrb_int val = mrb_integer(x); + + if (base < 2 || 36 < base) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %i", base); + } + const char *p = mrb_int_to_cstr(buf, sizeof(buf), val, base); + mrb_assert(p != NULL); + mrb_value str = mrb_str_new_cstr(mrb, p); + RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); + return str; } /* 15.2.8.3.25 */ @@ -1321,41 +1658,46 @@ mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, mrb_int base) * */ static mrb_value -fix_to_s(mrb_state *mrb, mrb_value self) +int_to_s(mrb_state *mrb, mrb_value self) { mrb_int base = 10; mrb_get_args(mrb, "|i", &base); - return mrb_fixnum_to_str(mrb, self, base); + return mrb_integer_to_str(mrb, self, base); } /* compare two numbers: (1:0:-1; -2 for error) */ static mrb_int cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2) { -#ifdef MRB_WITHOUT_FLOAT +#ifdef MRB_NO_FLOAT mrb_int x, y; #else mrb_float x, y; #endif -#ifdef MRB_WITHOUT_FLOAT - x = mrb_fixnum(v1); +#ifdef MRB_NO_FLOAT + x = mrb_integer(v1); #else - x = mrb_to_flo(mrb, v1); + x = mrb_as_float(mrb, v1); #endif switch (mrb_type(v2)) { - case MRB_TT_FIXNUM: -#ifdef MRB_WITHOUT_FLOAT - y = mrb_fixnum(v2); + case MRB_TT_INTEGER: +#ifdef MRB_NO_FLOAT + y = mrb_integer(v2); #else - y = (mrb_float)mrb_fixnum(v2); + y = (mrb_float)mrb_integer(v2); #endif break; -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: y = mrb_float(v2); break; +#ifdef MRB_USE_RATIONAL + case MRB_TT_RATIONAL: + y = mrb_as_float(mrb, v2); + break; +#endif #endif default: return -2; @@ -1372,41 +1714,38 @@ cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2) /* 15.2.9.3.6 */ /* * call-seq: - * self.f <=> other.f => -1, 0, +1 + * self.f <=> other.f => -1, 0, +1, or nil * < => -1 * = => 0 * > => +1 * Comparison---Returns -1, 0, or +1 depending on whether <i>fix</i> is * less than, equal to, or greater than <i>numeric</i>. This is the - * basis for the tests in <code>Comparable</code>. + * basis for the tests in <code>Comparable</code>. When the operands are + * not comparable, it returns nil instead of raising an exception. */ static mrb_value num_cmp(mrb_state *mrb, mrb_value self) { - mrb_value other; + mrb_value other = mrb_get_arg1(mrb); mrb_int n; - mrb_get_args(mrb, "o", &other); n = cmpnum(mrb, self, other); if (n == -2) return mrb_nil_value(); return mrb_fixnum_value(n); } -static void +static mrb_noreturn void cmperr(mrb_state *mrb, mrb_value v1, mrb_value v2) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %S with %S failed", - mrb_obj_value(mrb_class(mrb, v1)), - mrb_obj_value(mrb_class(mrb, v2))); + mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %t with %t failed", v1, v2); } static mrb_value num_lt(mrb_state *mrb, mrb_value self) { - mrb_value other; + mrb_value other = mrb_get_arg1(mrb); mrb_int n; - mrb_get_args(mrb, "o", &other); n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n < 0) return mrb_true_value(); @@ -1416,10 +1755,9 @@ num_lt(mrb_state *mrb, mrb_value self) static mrb_value num_le(mrb_state *mrb, mrb_value self) { - mrb_value other; + mrb_value other = mrb_get_arg1(mrb); mrb_int n; - mrb_get_args(mrb, "o", &other); n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n <= 0) return mrb_true_value(); @@ -1429,10 +1767,9 @@ num_le(mrb_state *mrb, mrb_value self) static mrb_value num_gt(mrb_state *mrb, mrb_value self) { - mrb_value other; + mrb_value other = mrb_get_arg1(mrb); mrb_int n; - mrb_get_args(mrb, "o", &other); n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n > 0) return mrb_true_value(); @@ -1442,131 +1779,135 @@ num_gt(mrb_state *mrb, mrb_value self) static mrb_value num_ge(mrb_state *mrb, mrb_value self) { - mrb_value other; + mrb_value other = mrb_get_arg1(mrb); mrb_int n; - mrb_get_args(mrb, "o", &other); n = cmpnum(mrb, self, other); if (n == -2) cmperr(mrb, self, other); if (n >= 0) return mrb_true_value(); return mrb_false_value(); } +MRB_API mrb_int +mrb_cmp(mrb_state *mrb, mrb_value obj1, mrb_value obj2) +{ + mrb_value v; + + switch (mrb_type(obj1)) { + case MRB_TT_INTEGER: + case MRB_TT_FLOAT: + return cmpnum(mrb, obj1, obj2); + case MRB_TT_STRING: + if (!mrb_string_p(obj2)) + return -2; + return mrb_str_cmp(mrb, obj1, obj2); + default: + v = mrb_funcall_id(mrb, obj1, MRB_OPSYM(cmp), 1, obj2); + if (mrb_nil_p(v) || !mrb_integer_p(v)) + return -2; + return mrb_integer(v); + } +} + static mrb_value num_finite_p(mrb_state *mrb, mrb_value self) { - mrb_get_args(mrb, ""); return mrb_true_value(); } static mrb_value num_infinite_p(mrb_state *mrb, mrb_value self) { - mrb_get_args(mrb, ""); return mrb_false_value(); } -/* 15.2.9.3.1 */ -/* - * call-seq: - * float + other -> float - * - * Returns a new float which is the sum of <code>float</code> - * and <code>other</code>. - */ -#ifndef MRB_WITHOUT_FLOAT -static mrb_value -flo_plus(mrb_state *mrb, mrb_value x) -{ - mrb_value y; - - mrb_get_args(mrb, "o", &y); - return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y)); -} -#endif - /* ------------------------------------------------------------------------*/ void mrb_init_numeric(mrb_state *mrb) { - struct RClass *numeric, *integer, *fixnum; -#ifndef MRB_WITHOUT_FLOAT + struct RClass *numeric, *integer; +#ifndef MRB_NO_FLOAT struct RClass *fl; #endif /* Numeric Class */ numeric = mrb_define_class(mrb, "Numeric", mrb->object_class); /* 15.2.7 */ - - mrb_define_method(mrb, numeric, "**", num_pow, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, numeric, "/", num_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.4 */ - mrb_define_method(mrb, numeric, "quo", num_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */ - mrb_define_method(mrb, numeric, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */ - mrb_define_method(mrb, numeric, "<", num_lt, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, numeric, "<=", num_le, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, numeric, ">", num_gt, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, numeric, ">=", num_ge, MRB_ARGS_REQ(1)); mrb_define_method(mrb, numeric, "finite?", num_finite_p, MRB_ARGS_NONE()); mrb_define_method(mrb, numeric, "infinite?",num_infinite_p, MRB_ARGS_NONE()); /* Integer Class */ - integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */ - MRB_SET_INSTANCE_TT(integer, MRB_TT_FIXNUM); + mrb->integer_class = integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */ + MRB_SET_INSTANCE_TT(integer, MRB_TT_INTEGER); mrb_undef_class_method(mrb, integer, "new"); + mrb_define_method(mrb, integer, "**", int_pow, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, integer, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */ + mrb_define_method(mrb, integer, "<", num_lt, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, integer, "<=", num_le, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, integer, ">", num_gt, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, integer, ">=", num_ge, MRB_ARGS_REQ(1)); + 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()); -#ifndef MRB_WITHOUT_FLOAT - 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) */ -#endif - /* 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 */ -#ifndef MRB_WITHOUT_FLOAT - mrb_define_method(mrb, fixnum, "to_f", fix_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */ + mrb_define_method(mrb, integer, "+", int_add, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */ + mrb_define_method(mrb, integer, "-", int_sub, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */ + mrb_define_method(mrb, integer, "*", int_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */ + mrb_define_method(mrb, integer, "%", int_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */ + mrb_define_method(mrb, integer, "/", int_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.6 */ + mrb_define_method(mrb, integer, "quo", int_quo, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */ + mrb_define_method(mrb, integer, "div", int_idiv, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, integer, "==", int_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */ + mrb_define_method(mrb, integer, "~", int_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */ + mrb_define_method(mrb, integer, "&", int_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */ + mrb_define_method(mrb, integer, "|", int_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */ + mrb_define_method(mrb, integer, "^", int_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */ + mrb_define_method(mrb, integer, "<<", int_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */ + mrb_define_method(mrb, integer, ">>", int_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */ + mrb_define_method(mrb, integer, "eql?", int_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ +#ifndef MRB_NO_FLOAT + mrb_define_method(mrb, integer, "to_f", int_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */ #endif - 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, integer, "to_s", int_to_s, MRB_ARGS_OPT(1)); /* 15.2.8.3.25 */ + mrb_define_method(mrb, integer, "inspect", int_to_s, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, integer, "divmod", int_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */ + mrb_define_method(mrb, integer, "__coerce_step_counter", coerce_step_counter, MRB_ARGS_REQ(2)); + + /* Fixnum Class for compatibility */ + mrb_define_const(mrb, mrb->object_class, "Fixnum", mrb_obj_value(integer)); -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT /* 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_pow, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "/", flo_div, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */ + mrb_define_method(mrb, fl, "quo", flo_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5 (x) */ + mrb_define_method(mrb, fl, "div", flo_idiv, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "+", flo_add, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */ + mrb_define_method(mrb, fl, "-", flo_sub, MRB_ARGS_REQ(1)); /* 15.2.9.3.4 */ + mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */ + mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */ + mrb_define_method(mrb, fl, "<=>", num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */ + mrb_define_method(mrb, fl, "<", num_lt, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "<=", num_le, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, ">", num_gt, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, ">=", num_ge, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */ 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, ">>", flo_rshift, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "<<", flo_lshift, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_OPT(1)); /* 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, "floor", flo_floor, MRB_ARGS_OPT(1)); /* 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, "to_i", flo_to_i, MRB_ARGS_NONE()); /* 15.2.9.3.14 */ + mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_OPT(1)); /* 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 */ @@ -1575,11 +1916,10 @@ mrb_init_numeric(mrb_state *mrb) 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)); + mrb_define_const_id(mrb, fl, MRB_SYM(INFINITY), mrb_float_value(mrb, INFINITY)); #endif #ifdef NAN - mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN)); + mrb_define_const_id(mrb, fl, MRB_SYM(NAN), mrb_float_value(mrb, NAN)); #endif #endif - mrb_define_module(mrb, "Integral"); } diff --git a/src/object.c b/src/object.c index 8724c5416..8fe4688ac 100644 --- a/src/object.c +++ b/src/object.c @@ -9,6 +9,7 @@ #include <mruby/numeric.h> #include <mruby/string.h> #include <mruby/class.h> +#include <mruby/presym.h> MRB_API mrb_bool mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2) @@ -19,12 +20,12 @@ mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2) return TRUE; case MRB_TT_FALSE: - case MRB_TT_FIXNUM: - return (mrb_fixnum(v1) == mrb_fixnum(v2)); + case MRB_TT_INTEGER: + return (mrb_integer(v1) == mrb_integer(v2)); case MRB_TT_SYMBOL: return (mrb_symbol(v1) == mrb_symbol(v2)); -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: return (mrb_float(v1) == mrb_float(v2)); #endif @@ -47,7 +48,18 @@ mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2) mrb_value result; if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE; - result = mrb_funcall(mrb, obj1, "==", 1, obj2); +#ifndef MRB_NO_FLOAT + /* value mixing with integer and float */ + if (mrb_integer_p(obj1)) { + if (mrb_float_p(obj2) && (mrb_float)mrb_integer(obj1) == mrb_float(obj2)) + return TRUE; + } + else if (mrb_float_p(obj1)) { + if (mrb_integer_p(obj2) && mrb_float(obj1) == (mrb_float)mrb_integer(obj2)) + return TRUE; + } +#endif + result = mrb_funcall_id(mrb, obj1, MRB_OPSYM(eq), 1, obj2); if (mrb_test(result)) return TRUE; return FALSE; } @@ -83,13 +95,17 @@ mrb_true(mrb_state *mrb, mrb_value obj) static mrb_value nil_to_s(mrb_state *mrb, mrb_value obj) { - return mrb_str_new(mrb, 0, 0); + mrb_value str = mrb_str_new_frozen(mrb, NULL, 0); + RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); + return str; } static mrb_value nil_inspect(mrb_state *mrb, mrb_value obj) { - return mrb_str_new_lit(mrb, "nil"); + mrb_value str = mrb_str_new_lit_frozen(mrb, "nil"); + RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); + return str; } /*********************************************************************** @@ -150,7 +166,9 @@ true_xor(mrb_state *mrb, mrb_value obj) static mrb_value true_to_s(mrb_state *mrb, mrb_value obj) { - return mrb_str_new_lit(mrb, "true"); + mrb_value str = mrb_str_new_lit_frozen(mrb, "true"); + RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); + return str; } /* 15.2.5.3.4 */ @@ -257,7 +275,9 @@ false_or(mrb_state *mrb, mrb_value obj) static mrb_value false_to_s(mrb_state *mrb, mrb_value obj) { - return mrb_str_new_lit(mrb, "false"); + mrb_value str = mrb_str_new_lit_frozen(mrb, "false"); + RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); + return str; } void @@ -268,7 +288,7 @@ mrb_init_object(mrb_state *mrb) struct RClass *f; mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class); - MRB_SET_INSTANCE_TT(n, MRB_TT_TRUE); + MRB_SET_INSTANCE_TT(n, MRB_TT_FALSE); mrb_undef_class_method(mrb, n, "new"); mrb_define_method(mrb, n, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.4.3.1 */ mrb_define_method(mrb, n, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.4.3.2 */ @@ -287,7 +307,7 @@ mrb_init_object(mrb_state *mrb) mrb_define_method(mrb, t, "inspect", true_to_s, MRB_ARGS_NONE()); mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class); - MRB_SET_INSTANCE_TT(f, MRB_TT_TRUE); + MRB_SET_INSTANCE_TT(f, MRB_TT_FALSE); mrb_undef_class_method(mrb, f, "new"); mrb_define_method(mrb, f, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.6.3.1 */ mrb_define_method(mrb, f, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.6.3.2 */ @@ -296,136 +316,86 @@ mrb_init_object(mrb_state *mrb) mrb_define_method(mrb, f, "inspect", false_to_s, MRB_ARGS_NONE()); } -static mrb_value -inspect_type(mrb_state *mrb, mrb_value val) +static const char* +type_name(enum mrb_vtype t) { - if (mrb_type(val) == MRB_TT_FALSE || mrb_type(val) == MRB_TT_TRUE) { - return mrb_inspect(mrb, val); - } - else { - return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, val)); + switch (t) { +#define MRB_VTYPE_NAME(tt, type, name) case tt: return name; + MRB_VTYPE_FOREACH(MRB_VTYPE_NAME) +#undef MRB_VTYPE_NAME + default: return NULL; } } static mrb_value -convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *method, mrb_bool raise) +convert_type(mrb_state *mrb, mrb_value val, const char *tname, mrb_sym method, mrb_bool raise) { - mrb_sym m = 0; - - m = mrb_intern_cstr(mrb, method); - if (!mrb_respond_to(mrb, val, m)) { + if (!mrb_respond_to(mrb, val, method)) { if (raise) { - mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into %S", inspect_type(mrb, val), mrb_str_new_cstr(mrb, tname)); + if (tname) mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y into %s", val, tname); + mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y", val); } return mrb_nil_value(); } - return mrb_funcall_argv(mrb, val, m, 0, 0); + return mrb_funcall_argv(mrb, val, method, 0, 0); } MRB_API mrb_value -mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method) -{ - mrb_value v; - - if (mrb_fixnum_p(val)) return val; - v = convert_type(mrb, val, "Integer", method, FALSE); - if (mrb_nil_p(v) || !mrb_fixnum_p(v)) { - return mrb_nil_value(); - } - return v; -} - -MRB_API mrb_value -mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method) +mrb_type_convert(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method) { mrb_value v; + const char *tname; if (mrb_type(val) == type) return val; + tname = type_name(type); v = convert_type(mrb, val, tname, method, TRUE); if (mrb_type(v) != type) { - mrb_raisef(mrb, E_TYPE_ERROR, "%S cannot be converted to %S by #%S", val, - mrb_str_new_cstr(mrb, tname), mrb_str_new_cstr(mrb, method)); + if (type == MRB_TT_STRING) return mrb_any_to_s(mrb, val); + mrb_raisef(mrb, E_TYPE_ERROR, "%v cannot be converted to %s by #%n", val, tname, method); } return v; } MRB_API mrb_value -mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method) +mrb_type_convert_check(mrb_state *mrb, mrb_value val, enum mrb_vtype type, mrb_sym method) { mrb_value v; if (mrb_type(val) == type && type != MRB_TT_DATA && type != MRB_TT_ISTRUCT) return val; - v = convert_type(mrb, val, tname, method, FALSE); + v = convert_type(mrb, val, type_name(type), method, FALSE); if (mrb_nil_p(v) || mrb_type(v) != type) return mrb_nil_value(); return v; } -static const struct types { - unsigned char type; - const char *name; -} builtin_types[] = { -/* {MRB_TT_NIL, "nil"}, */ - {MRB_TT_FALSE, "false"}, - {MRB_TT_TRUE, "true"}, - {MRB_TT_FIXNUM, "Fixnum"}, - {MRB_TT_SYMBOL, "Symbol"}, /* :symbol */ - {MRB_TT_MODULE, "Module"}, - {MRB_TT_OBJECT, "Object"}, - {MRB_TT_CLASS, "Class"}, - {MRB_TT_ICLASS, "iClass"}, /* internal use: mixed-in module holder */ - {MRB_TT_SCLASS, "SClass"}, - {MRB_TT_PROC, "Proc"}, -#ifndef MRB_WITHOUT_FLOAT - {MRB_TT_FLOAT, "Float"}, -#endif - {MRB_TT_ARRAY, "Array"}, - {MRB_TT_HASH, "Hash"}, - {MRB_TT_STRING, "String"}, - {MRB_TT_RANGE, "Range"}, -/* {MRB_TT_BIGNUM, "Bignum"}, */ - {MRB_TT_FILE, "File"}, - {MRB_TT_DATA, "Data"}, /* internal use: wrapped C pointers */ -/* {MRB_TT_VARMAP, "Varmap"}, */ /* internal use: dynamic variables */ -/* {MRB_TT_NODE, "Node"}, */ /* internal use: syntax tree node */ -/* {MRB_TT_UNDEF, "undef"}, */ /* internal use: #undef; should not happen */ - {MRB_TT_MAXDEFINE, 0} -}; - MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) { - const struct types *type = builtin_types; - enum mrb_vtype xt; - - xt = mrb_type(x); - if ((xt != t) || (xt == MRB_TT_DATA) || (xt == MRB_TT_ISTRUCT)) { - while (type->type < MRB_TT_MAXDEFINE) { - if (type->type == t) { - const char *etype; - - if (mrb_nil_p(x)) { - etype = "nil"; - } - else if (mrb_fixnum_p(x)) { - etype = "Fixnum"; - } - else if (mrb_type(x) == MRB_TT_SYMBOL) { - etype = "Symbol"; - } - else if (mrb_immediate_p(x)) { - etype = RSTRING_PTR(mrb_obj_as_string(mrb, x)); - } - else { - etype = mrb_obj_classname(mrb, x); - } - mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)", - mrb_str_new_cstr(mrb, etype), mrb_str_new_cstr(mrb, type->name)); - } - type++; - } - mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %S (%S given)", - mrb_fixnum_value(t), mrb_fixnum_value(mrb_type(x))); + enum mrb_vtype xt = mrb_type(x); + const char *tname, *ename; + + if (t == xt) return; + + tname = type_name(t); + if (mrb_nil_p(x)) { + ename = "nil"; + } + else if (mrb_integer_p(x)) { + ename = "Integer"; + } + else if (mrb_symbol_p(x)) { + ename = "Symbol"; } + else if (mrb_immediate_p(x)) { + ename = RSTRING_PTR(mrb_obj_as_string(mrb, x)); + } + else { + ename = mrb_obj_classname(mrb, x); + } + if (tname) { + mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)", + ename, tname); + } + mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %d (%s given)", t, ename); } /* 15.3.1.3.46 */ @@ -447,8 +417,10 @@ mrb_any_to_s(mrb_state *mrb, mrb_value obj) mrb_str_cat_lit(mrb, str, "#<"); mrb_str_cat_cstr(mrb, str, cname); - mrb_str_cat_lit(mrb, str, ":"); - mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj))); + if (!mrb_immediate_p(obj)) { + mrb_str_cat_lit(mrb, str, ":"); + mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj))); + } mrb_str_cat_lit(mrb, str, ">"); return str; @@ -505,25 +477,22 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) return FALSE; } -static mrb_value -mrb_to_integer(mrb_state *mrb, mrb_value val, const char *method) -{ - mrb_value v; - - if (mrb_fixnum_p(val)) return val; - v = convert_type(mrb, val, "Integer", method, TRUE); - if (!mrb_obj_is_kind_of(mrb, v, mrb->fixnum_class)) { - mrb_value type = inspect_type(mrb, val); - mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Integer (%S#%S gives %S)", - type, type, mrb_str_new_cstr(mrb, method), inspect_type(mrb, v)); - } - return v; -} - MRB_API mrb_value mrb_to_int(mrb_state *mrb, mrb_value val) { - return mrb_to_integer(mrb, val, "to_int"); + + if (!mrb_integer_p(val)) { +#ifndef MRB_NO_FLOAT + if (mrb_float_p(val)) { + return mrb_float_to_integer(mrb, val); + } +#endif + if (mrb_string_p(val)) { + mrb_raise(mrb, E_TYPE_ERROR, "can't convert String to Integer"); + } + return mrb_type_convert(mrb, val, MRB_TT_INTEGER, MRB_SYM(to_i)); + } + return val; } MRB_API mrb_value @@ -533,22 +502,16 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base) if (mrb_nil_p(val)) { if (base != 0) goto arg_error; - mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer"); + mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer"); } switch (mrb_type(val)) { -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: if (base != 0) goto arg_error; - else { - mrb_float f = mrb_float(val); - if (FIXABLE_FLOAT(f)) { - break; - } - } - return mrb_flo_to_fixnum(mrb, val); + return mrb_float_to_integer(mrb, val); #endif - case MRB_TT_FIXNUM: + case MRB_TT_INTEGER: if (base != 0) goto arg_error; return val; @@ -568,29 +531,20 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base) arg_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value"); } - tmp = convert_type(mrb, val, "Integer", "to_int", FALSE); - if (mrb_nil_p(tmp) || !mrb_fixnum_p(tmp)) { - tmp = mrb_to_integer(mrb, val, "to_i"); - } - return tmp; + /* to raise TypeError */ + return mrb_to_int(mrb, val); } +#ifndef MRB_NO_FLOAT MRB_API mrb_value -mrb_Integer(mrb_state *mrb, mrb_value val) -{ - return mrb_convert_to_integer(mrb, val, 0); -} - -#ifndef MRB_WITHOUT_FLOAT -MRB_API mrb_value -mrb_Float(mrb_state *mrb, mrb_value val) +mrb_to_float(mrb_state *mrb, mrb_value val) { if (mrb_nil_p(val)) { mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Float"); } switch (mrb_type(val)) { - case MRB_TT_FIXNUM: - return mrb_float_value(mrb, (mrb_float)mrb_fixnum(val)); + case MRB_TT_INTEGER: + return mrb_float_value(mrb, (mrb_float)mrb_integer(val)); case MRB_TT_FLOAT: return val; @@ -599,20 +553,81 @@ mrb_Float(mrb_state *mrb, mrb_value val) return mrb_float_value(mrb, mrb_str_to_dbl(mrb, val, TRUE)); default: - return mrb_convert_type(mrb, val, MRB_TT_FLOAT, "Float", "to_f"); + return mrb_type_convert(mrb, val, MRB_TT_FLOAT, MRB_SYM(to_f)); } } #endif MRB_API mrb_value +mrb_to_str(mrb_state *mrb, mrb_value val) +{ + return mrb_ensure_string_type(mrb, val); +} + +/* obsolete: use mrb_ensure_string_type() instead */ +MRB_API mrb_value +mrb_string_type(mrb_state *mrb, mrb_value str) +{ + return mrb_ensure_string_type(mrb, str); +} + +MRB_API mrb_value +mrb_ensure_string_type(mrb_state *mrb, mrb_value str) +{ + if (!mrb_string_p(str)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to String", str); + } + return str; +} + +MRB_API mrb_value +mrb_check_string_type(mrb_state *mrb, mrb_value str) +{ + if (!mrb_string_p(str)) return mrb_nil_value(); + return str; +} + +MRB_API mrb_value +mrb_ensure_array_type(mrb_state *mrb, mrb_value ary) +{ + if (!mrb_array_p(ary)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Array", ary); + } + return ary; +} + +MRB_API mrb_value +mrb_check_array_type(mrb_state *mrb, mrb_value ary) +{ + if (!mrb_array_p(ary)) return mrb_nil_value(); + return ary; +} + +MRB_API mrb_value +mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash) +{ + if (!mrb_hash_p(hash)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Hash", hash); + } + return hash; +} + +MRB_API mrb_value +mrb_check_hash_type(mrb_state *mrb, mrb_value hash) +{ + if (!mrb_hash_p(hash)) return mrb_nil_value(); + return hash; +} + +MRB_API mrb_value mrb_inspect(mrb_state *mrb, mrb_value obj) { - return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0)); + return mrb_obj_as_string(mrb, mrb_funcall_id(mrb, obj, MRB_SYM(inspect), 0)); } MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2) { if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE; - return mrb_test(mrb_funcall(mrb, obj1, "eql?", 1, obj2)); + return mrb_test(mrb_funcall_id(mrb, obj1, MRB_SYM_Q(eql), 1, obj2)); } diff --git a/src/pool.c b/src/pool.c index b87d2cfaa..ab30be1d8 100644 --- a/src/pool.c +++ b/src/pool.c @@ -4,8 +4,6 @@ ** See Copyright Notice in mruby.h */ -#include <stddef.h> -#include <stdint.h> #include <string.h> #include <mruby.h> diff --git a/src/print.c b/src/print.c index 03b5eadfa..4af871b43 100644 --- a/src/print.c +++ b/src/print.c @@ -7,41 +7,89 @@ #include <mruby.h> #include <mruby/string.h> #include <mruby/variable.h> +#include <mruby/error.h> +#include <mruby/presym.h> +#include <string.h> + +#ifndef MRB_NO_STDIO +static void +printcstr(const char *str, size_t len, FILE *stream) +{ + if (str) { + fwrite(str, len, 1, stream); + putc('\n', stream); + } +} -#ifndef MRB_DISABLE_STDIO static void printstr(mrb_value obj, FILE *stream) { if (mrb_string_p(obj)) { - fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stream); - putc('\n', stream); + printcstr(RSTRING_PTR(obj), RSTRING_LEN(obj), stream); } } -#else -# define printstr(obj, stream) (void)0 -#endif + +void +mrb_core_init_printabort(void) +{ + static const char *str = "Failed mruby core initialization"; + printcstr(str, strlen(str), stdout); +} MRB_API void mrb_p(mrb_state *mrb, mrb_value obj) { - printstr(mrb_inspect(mrb, obj), stdout); + if (mrb_type(obj) == MRB_TT_EXCEPTION && mrb_obj_ptr(obj) == mrb->nomem_err) { + static const char *str = "Out of memory"; + printcstr(str, strlen(str), stdout); + } + else { + printstr(mrb_inspect(mrb, obj), stdout); + } } + MRB_API void mrb_print_error(mrb_state *mrb) { mrb_print_backtrace(mrb); - printstr(mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0), stderr); } MRB_API void mrb_show_version(mrb_state *mrb) { - printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")), stdout); + printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(MRUBY_DESCRIPTION)), stdout); +} + +MRB_API void +mrb_show_copyright(mrb_state *mrb) +{ + printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(MRUBY_COPYRIGHT)), stdout); +} + +#else +void +mrb_core_init_printabort(void) +{ +} + +MRB_API void +mrb_p(mrb_state *mrb, mrb_value obj) +{ +} + +MRB_API void +mrb_print_error(mrb_state *mrb) +{ +} + +MRB_API void +mrb_show_version(mrb_state *mrb) +{ } MRB_API void mrb_show_copyright(mrb_state *mrb) { - printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")), stdout); } +#endif diff --git a/src/proc.c b/src/proc.c index 05b897480..1d5a4aa76 100644 --- a/src/proc.c +++ b/src/proc.c @@ -8,52 +8,88 @@ #include <mruby/class.h> #include <mruby/proc.h> #include <mruby/opcode.h> +#include <mruby/data.h> +#include <mruby/presym.h> +#include <mruby/array.h> +#include <mruby/hash.h> -static mrb_code call_iseq[] = { +static const mrb_code call_iseq[] = { OP_CALL, }; +static const mrb_irep call_irep = { + 0, /* nlocals */ + 2, /* nregs */ + 0, /* clen */ + MRB_ISEQ_NO_FREE | MRB_IREP_NO_FREE, /* flags */ + call_iseq, /* iseq */ + NULL, /* pool */ + NULL, /* syms */ + NULL, /* reps */ + NULL, /* lv */ + NULL, /* debug_info */ + 1, /* ilen */ + 0, /* plen */ + 0, /* slen */ + 1, /* rlen */ + 0, /* refcnt */ +}; + +static const struct RProc call_proc = { + NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_FL_OBJ_IS_FROZEN | MRB_PROC_SCOPE | MRB_PROC_STRICT, + { &call_irep }, NULL, { NULL } +}; + struct RProc* -mrb_proc_new(mrb_state *mrb, mrb_irep *irep) +mrb_proc_new(mrb_state *mrb, const mrb_irep *irep) { struct RProc *p; mrb_callinfo *ci = mrb->c->ci; - p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); + p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class); if (ci) { struct RClass *tc = NULL; if (ci->proc) { - tc = MRB_PROC_TARGET_CLASS(ci->proc); + if (ci->proc->color != MRB_GC_RED) { + tc = MRB_PROC_TARGET_CLASS(ci->proc); + } + else { + tc = mrb_vm_ci_target_class(ci); + if (tc && tc->tt == MRB_TT_ICLASS) { + tc = tc->c; + } + } } if (tc == NULL) { - tc = ci->target_class; + tc = mrb_vm_ci_target_class(ci); } p->upper = ci->proc; p->e.target_class = tc; } p->body.irep = irep; - mrb_irep_incref(mrb, irep); + if (irep) { + mrb_irep_incref(mrb, (mrb_irep*)irep); + } return p; } -static struct REnv* -env_new(mrb_state *mrb, mrb_int nlocals) +struct REnv* +mrb_env_new(mrb_state *mrb, struct mrb_context *c, mrb_callinfo *ci, int nstacks, mrb_value *stack, struct RClass *tc) { struct REnv *e; - mrb_callinfo *ci = mrb->c->ci; - int bidx; + mrb_int bidx; - e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, NULL); - MRB_ENV_SET_STACK_LEN(e, nlocals); + e = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, tc); + MRB_ENV_SET_LEN(e, nstacks); bidx = ci->argc; - if (ci->argc < 0) bidx = 2; + if (bidx < 0) bidx = 2; else bidx += 1; MRB_ENV_SET_BIDX(e, bidx); e->mid = ci->mid; - e->stack = mrb->c->stack; - e->cxt = mrb->c; + e->stack = stack; + e->cxt = c; return e; } @@ -62,29 +98,30 @@ static void closure_setup(mrb_state *mrb, struct RProc *p) { mrb_callinfo *ci = mrb->c->ci; - struct RProc *up = p->upper; - struct REnv *e; + const struct RProc *up = p->upper; + struct REnv *e = NULL; - if (ci->env) { - e = ci->env; + if (ci && (e = mrb_vm_ci_env(ci)) != NULL) { + /* do nothing, because e is assigned already */ } - else { + else if (up) { struct RClass *tc = MRB_PROC_TARGET_CLASS(p); - e = env_new(mrb, up->body.irep->nlocals); - ci->env = e; - if (tc) { - e->c = tc; - mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc); + e = mrb_env_new(mrb, mrb->c, ci, up->body.irep->nlocals, ci->stack, tc); + ci->u.env = e; + if (MRB_PROC_ENV_P(up) && MRB_PROC_ENV(up)->cxt == NULL) { + e->mid = MRB_PROC_ENV(up)->mid; } } - p->e.env = e; - p->flags |= MRB_PROC_ENVSET; - mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e); + if (e) { + p->e.env = e; + p->flags |= MRB_PROC_ENVSET; + mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e); + } } struct RProc* -mrb_closure_new(mrb_state *mrb, mrb_irep *irep) +mrb_closure_new(mrb_state *mrb, const mrb_irep *irep) { struct RProc *p = mrb_proc_new(mrb, irep); @@ -97,7 +134,7 @@ mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func) { struct RProc *p; - p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); + p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class); p->body.func = func; p->flags |= MRB_PROC_CFUNC_FL; p->upper = 0; @@ -113,11 +150,14 @@ mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const struct REnv *e; int i; - p->e.env = e = env_new(mrb, argc); + p->e.env = e = mrb_env_new(mrb, mrb->c, mrb->c->ci, 0, NULL, NULL); p->flags |= MRB_PROC_ENVSET; mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e); - MRB_ENV_UNSHARE_STACK(e); + MRB_ENV_CLOSE(e); + e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc); + MRB_ENV_SET_LEN(e, argc); + if (argv) { for (i = 0; i < argc; ++i) { e->stack[i] = argv[i]; @@ -140,19 +180,19 @@ mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals) MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx) { - struct RProc *p = mrb->c->ci->proc; + const struct RProc *p = mrb->c->ci->proc; struct REnv *e; if (!p || !MRB_PROC_CFUNC_P(p)) { - mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc."); + mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc"); } e = MRB_PROC_ENV(p); if (!e) { - mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv."); + mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv"); } - if (idx < 0 || MRB_ENV_STACK_LEN(e) <= idx) { - mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %S (expected: 0 <= index < %S)", - mrb_fixnum_value(idx), mrb_fixnum_value(MRB_ENV_STACK_LEN(e))); + if (idx < 0 || MRB_ENV_LEN(e) <= idx) { + mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %i (expected: 0 <= index < %i)", + idx, MRB_ENV_LEN(e)); } return e->stack[idx]; @@ -168,7 +208,7 @@ mrb_proc_copy(struct RProc *a, struct RProc *b) a->flags = b->flags; a->body = b->body; if (!MRB_PROC_CFUNC_P(a) && a->body.irep) { - a->body.irep->refcnt++; + mrb_irep_incref(NULL, (mrb_irep*)a->body.irep); } a->upper = b->upper; a->e.env = b->e.env; @@ -182,17 +222,14 @@ mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class) mrb_value proc; struct RProc *p; - mrb_get_args(mrb, "&", &blk); - if (mrb_nil_p(blk)) { - /* Calling Proc.new without a block is not implemented yet */ - mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); - } - p = (struct RProc *)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class)); + /* Calling Proc.new without a block is not implemented yet */ + mrb_get_args(mrb, "&!", &blk); + p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class)); mrb_proc_copy(p, mrb_proc_ptr(blk)); proc = mrb_obj_value(p); - mrb_funcall_with_block(mrb, proc, mrb_intern_lit(mrb, "initialize"), 0, NULL, proc); + mrb_funcall_with_block(mrb, proc, MRB_SYM(initialize), 0, NULL, proc); if (!MRB_PROC_STRICT_P(p) && - mrb->c->ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb->c->ci[-1].env) { + mrb->c->ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb->c->ci[-1].u.env) { p->flags |= MRB_PROC_ORPHAN; } return proc; @@ -201,46 +238,76 @@ mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class) static mrb_value mrb_proc_init_copy(mrb_state *mrb, mrb_value self) { - mrb_value proc; + mrb_value proc = mrb_get_arg1(mrb); - mrb_get_args(mrb, "o", &proc); - if (mrb_type(proc) != MRB_TT_PROC) { + if (!mrb_proc_p(proc)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc"); } mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(proc)); return self; } -int -mrb_proc_cfunc_p(struct RProc *p) +/* 15.2.17.4.2 */ +static mrb_value +proc_arity(mrb_state *mrb, mrb_value self) { - return MRB_PROC_CFUNC_P(p); + return mrb_int_value(mrb, mrb_proc_arity(mrb_proc_ptr(self))); } -/* 15.2.17.4.2 */ +/* 15.3.1.2.6 */ +/* 15.3.1.3.27 */ +/* + * call-seq: + * lambda { |...| block } -> a_proc + * + * Equivalent to <code>Proc.new</code>, except the resulting Proc objects + * check the number of parameters passed when called. + */ static mrb_value -mrb_proc_arity(mrb_state *mrb, mrb_value self) +proc_lambda(mrb_state *mrb, mrb_value self) { - struct RProc *p = mrb_proc_ptr(self); - struct mrb_irep *irep; - mrb_code *pc; + mrb_value blk; + struct RProc *p; + + mrb_get_args(mrb, "&", &blk); + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); + } + if (!mrb_proc_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc"); + } + p = mrb_proc_ptr(blk); + if (!MRB_PROC_STRICT_P(p)) { + struct RProc *p2 = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, p->c); + mrb_proc_copy(p2, p); + p2->flags |= MRB_PROC_STRICT; + return mrb_obj_value(p2); + } + return blk; +} + +mrb_int +mrb_proc_arity(const struct RProc *p) +{ + const mrb_irep *irep; + const mrb_code *pc; mrb_aspec aspec; int ma, op, ra, pa, arity; if (MRB_PROC_CFUNC_P(p)) { /* TODO cfunc aspec not implemented yet */ - return mrb_fixnum_value(-1); + return -1; } irep = p->body.irep; if (!irep) { - return mrb_fixnum_value(0); + return 0; } pc = irep->iseq; /* arity is depend on OP_ENTER */ if (*pc != OP_ENTER) { - return mrb_fixnum_value(0); + return 0; } aspec = PEEK_W(pc+1); @@ -250,64 +317,132 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self) pa = MRB_ASPEC_POST(aspec); arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa; - return mrb_fixnum_value(arity); + return arity; } -/* 15.3.1.2.6 */ -/* 15.3.1.3.27 */ -/* - * call-seq: - * lambda { |...| block } -> a_proc - * - * Equivalent to <code>Proc.new</code>, except the resulting Proc objects - * check the number of parameters passed when called. - */ -static mrb_value -proc_lambda(mrb_state *mrb, mrb_value self) +mrb_value +mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc) { - mrb_value blk; - struct RProc *p; + const mrb_irep *irep; + mrb_value vars; + size_t i; - mrb_get_args(mrb, "&", &blk); - if (mrb_nil_p(blk)) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); + if (proc == NULL || MRB_PROC_CFUNC_P(proc)) { + return mrb_ary_new(mrb); } - if (mrb_type(blk) != MRB_TT_PROC) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc"); + vars = mrb_hash_new(mrb); + while (proc) { + if (MRB_PROC_CFUNC_P(proc)) break; + irep = proc->body.irep; + if (irep->lv) { + for (i = 0; i + 1 < irep->nlocals; ++i) { + if (irep->lv[i]) { + mrb_sym sym = irep->lv[i]; + const char *name = mrb_sym_name(mrb, sym); + switch (name[0]) { + case '*': case '&': + break; + default: + mrb_hash_set(mrb, vars, mrb_symbol_value(sym), mrb_true_value()); + break; + } + } + } + } + if (MRB_PROC_SCOPE_P(proc)) break; + proc = proc->upper; } - p = mrb_proc_ptr(blk); - if (!MRB_PROC_STRICT_P(p)) { - struct RProc *p2 = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, p->c); - mrb_proc_copy(p2, p); - p2->flags |= MRB_PROC_STRICT; - return mrb_obj_value(p2); + + return mrb_hash_keys(mrb, vars); +} + +const struct RProc * +mrb_proc_get_caller(mrb_state *mrb, struct REnv **envp) +{ + struct mrb_context *c = mrb->c; + mrb_callinfo *ci = (c->ci > c->cibase) ? c->ci - 1 : c->cibase; + const struct RProc *proc = ci->proc; + + if (!proc || MRB_PROC_CFUNC_P(proc)) { + if (envp) *envp = NULL; } - return blk; + else { + struct RClass *tc = MRB_PROC_TARGET_CLASS(proc); + struct REnv *e = mrb_vm_ci_env(ci); + + if (e == NULL) { + int nstacks = proc->body.irep->nlocals; + e = mrb_env_new(mrb, c, ci, nstacks, ci->stack, tc); + ci->u.env = e; + } + else if (tc) { + e->c = tc; + mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc); + } + if (envp) *envp = e; + } + + return proc; +} + +#define IREP_LVAR_MERGE_DEFAULT 50 +#define IREP_LVAR_MERGE_MINIMUM 8 +#define IREP_LVAR_MERGE_MAXIMUM 240 + +#ifdef MRB_IREP_LVAR_MERGE_LIMIT +# define IREP_LVAR_MERGE_LIMIT \ + ((MRB_IREP_LVAR_MERGE_LIMIT) < IREP_LVAR_MERGE_MINIMUM ? IREP_LVAR_MERGE_MINIMUM : \ + (MRB_IREP_LVAR_MERGE_LIMIT) > IREP_LVAR_MERGE_MAXIMUM ? IREP_LVAR_MERGE_MAXIMUM : \ + (MRB_IREP_LVAR_MERGE_LIMIT)) +#else +# define IREP_LVAR_MERGE_LIMIT IREP_LVAR_MERGE_DEFAULT +#endif + +void +mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack) +{ + mrb_assert(!(irep->flags & MRB_IREP_NO_FREE)); + + if ((irep->nlocals + num) > IREP_LVAR_MERGE_LIMIT) { + mrb_raise(mrb, E_RUNTIME_ERROR, "too many local variables for binding (mruby limitation)"); + } + + if (!lv) { + mrb_raise(mrb, E_RUNTIME_ERROR, "unavailable local variable names"); + } + + irep->lv = (mrb_sym*)mrb_realloc(mrb, (mrb_sym*)irep->lv, sizeof(mrb_sym) * (irep->nlocals + num)); + env->stack = (mrb_value*)mrb_realloc(mrb, env->stack, sizeof(mrb_value) * (irep->nlocals + 1 /* self */ + num)); + + mrb_sym *destlv = (mrb_sym*)irep->lv + irep->nlocals - 1 /* self */; + mrb_value *destst = env->stack + irep->nlocals; + memmove(destlv, lv, sizeof(mrb_sym) * num); + if (stack) { + memmove(destst, stack, sizeof(mrb_value) * num); + } + else { + for (int i = num; i > 0; i--, destst++) { + *destst = mrb_nil_value(); + } + } + irep->nlocals += num; + irep->nregs = irep->nlocals; + MRB_ENV_SET_LEN(env, irep->nlocals); } void mrb_init_proc(mrb_state *mrb) { - struct RProc *p; mrb_method_t m; - mrb_irep *call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep)); - static const mrb_irep mrb_irep_zero = { 0 }; - - *call_irep = mrb_irep_zero; - 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_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, mrb->proc_class, "arity", mrb_proc_arity, MRB_ARGS_NONE()); + mrb_define_method(mrb, mrb->proc_class, "arity", proc_arity, MRB_ARGS_NONE()); - p = mrb_proc_new(mrb, call_irep); - MRB_METHOD_FROM_PROC(m, p); - mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "call"), m); - mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "[]"), m); + MRB_METHOD_FROM_PROC(m, &call_proc); + mrb_define_method_raw(mrb, mrb->proc_class, MRB_SYM(call), m); + mrb_define_method_raw(mrb, mrb->proc_class, MRB_OPSYM(aref), m); - mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.2.6 */ - mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()); /* 15.3.1.3.27 */ + mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.2.6 */ + mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.3.27 */ } diff --git a/src/range.c b/src/range.c index e45308d35..477baa5e8 100644 --- a/src/range.c +++ b/src/range.c @@ -9,55 +9,102 @@ #include <mruby/range.h> #include <mruby/string.h> #include <mruby/array.h> +#include <mruby/presym.h> -MRB_API struct RRange* -mrb_range_ptr(mrb_state *mrb, mrb_value v) -{ - struct RRange *r = (struct RRange*)mrb_ptr(v); - - if (r->edges == NULL) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range"); - } - return r; -} +#define RANGE_INITIALIZED_FLAG 1 +#define RANGE_INITIALIZED(p) ((p)->flags |= RANGE_INITIALIZED_FLAG) +#define RANGE_INITIALIZED_P(p) ((p)->flags & RANGE_INITIALIZED_FLAG) static void -range_check(mrb_state *mrb, mrb_value a, mrb_value b) +r_check(mrb_state *mrb, mrb_value a, mrb_value b) { - mrb_value ans; enum mrb_vtype ta; enum mrb_vtype tb; + mrb_int n; ta = mrb_type(a); tb = mrb_type(b); -#ifdef MRB_WITHOUT_FLOAT - if (ta == MRB_TT_FIXNUM && tb == MRB_TT_FIXNUM ) { +#ifdef MRB_NO_FLOAT + if (ta == MRB_TT_INTEGER && tb == MRB_TT_INTEGER ) return; #else - if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) && - (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) { -#endif + if ((ta == MRB_TT_INTEGER || ta == MRB_TT_FLOAT) && + (tb == MRB_TT_INTEGER || tb == MRB_TT_FLOAT)) { return; } +#endif + + if (mrb_nil_p(a) || mrb_nil_p(b)) return; - ans = mrb_funcall(mrb, a, "<=>", 1, b); - if (mrb_nil_p(ans)) { - /* can not be compared */ + n = mrb_cmp(mrb, a, b); + if (n == -2) { /* can not be compared */ mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range"); } } -MRB_API mrb_value -mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl) +static mrb_bool +r_le(mrb_state *mrb, mrb_value a, mrb_value b) { - struct RRange *r; + mrb_int n = mrb_cmp(mrb, a, b); - range_check(mrb, beg, end); - r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class); + if (n == 0 || n == -1) return TRUE; + return FALSE; +} + +static mrb_bool +r_gt(mrb_state *mrb, mrb_value a, mrb_value b) +{ + return mrb_cmp(mrb, a, b) == 1; +} + +static mrb_bool +r_ge(mrb_state *mrb, mrb_value a, mrb_value b) +{ + mrb_int n = mrb_cmp(mrb, a, b); + + if (n == 0 || n == 1) return TRUE; + return FALSE; +} + +static void +range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r) +{ +#ifndef MRB_RANGE_EMBED r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges)); - r->edges->beg = beg; - r->edges->end = end; - r->excl = excl; - return mrb_range_value(r); +#endif +} + +static struct RRange * +range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl) +{ + r_check(mrb, beg, end); + + if (r) { + if (RANGE_INITIALIZED_P(r)) { + /* Ranges are immutable, so that they should be initialized only once. */ + mrb_name_error(mrb, MRB_SYM(initialize), "'initialize' called twice"); + } + else { + range_ptr_alloc_edges(mrb, r); + } + } + else { + r = MRB_OBJ_ALLOC(mrb, MRB_TT_RANGE, mrb->range_class); + range_ptr_alloc_edges(mrb, r); + } + + RANGE_BEG(r) = beg; + RANGE_END(r) = end; + RANGE_EXCL(r) = excl; + RANGE_INITIALIZED(r); + + return r; +} + +static void +range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl) +{ + range_ptr_init(mrb, r, beg, end, excl); + mrb_write_barrier(mrb, (struct RBasic*)r); } /* @@ -67,12 +114,10 @@ mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl) * * Returns the first object in <i>rng</i>. */ -mrb_value -mrb_range_beg(mrb_state *mrb, mrb_value range) +static mrb_value +range_beg(mrb_state *mrb, mrb_value range) { - struct RRange *r = mrb_range_ptr(mrb, range); - - return r->edges->beg; + return mrb_range_beg(mrb, range); } /* @@ -85,13 +130,10 @@ mrb_range_beg(mrb_state *mrb, mrb_value range) * (1..10).end #=> 10 * (1...10).end #=> 10 */ - -mrb_value -mrb_range_end(mrb_state *mrb, mrb_value range) +static mrb_value +range_end(mrb_state *mrb, mrb_value range) { - struct RRange *r = mrb_range_ptr(mrb, range); - - return r->edges->end; + return mrb_range_end(mrb, range); } /* @@ -100,28 +142,12 @@ mrb_range_end(mrb_state *mrb, mrb_value range) * * Returns <code>true</code> if <i>range</i> excludes its end value. */ -mrb_value -mrb_range_excl(mrb_state *mrb, mrb_value range) +static mrb_value +range_excl(mrb_state *mrb, mrb_value range) { - struct RRange *r = mrb_range_ptr(mrb, range); - - return mrb_bool_value(r->excl); + return mrb_bool_value(mrb_range_excl_p(mrb, range)); } -static void -range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bool exclude_end) -{ - struct RRange *r = mrb_range_raw_ptr(range); - - range_check(mrb, beg, end); - r->excl = exclude_end; - if (!r->edges) { - r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges)); - } - r->edges->beg = beg; - r->edges->end = end; - mrb_write_barrier(mrb, (struct RBasic*)r); -} /* * call-seq: * Range.new(start, end, exclusive=false) => range @@ -130,25 +156,18 @@ range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bo * parameter is omitted or is <code>false</code>, the <i>range</i> will include * the end object; otherwise, it will be excluded. */ - -mrb_value -mrb_range_initialize(mrb_state *mrb, mrb_value range) +static mrb_value +range_initialize(mrb_state *mrb, mrb_value range) { mrb_value beg, end; - mrb_bool exclusive; - mrb_int n; + mrb_bool exclusive = FALSE; - n = mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive); - if (n != 3) { - exclusive = FALSE; - } - /* Ranges are immutable, so that they should be initialized only once. */ - if (mrb_range_raw_ptr(range)->edges) { - mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice"); - } - range_init(mrb, range, beg, end, exclusive); + mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive); + range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive); + mrb_obj_freeze(mrb, range); return range; } + /* * call-seq: * range == obj => true or false @@ -161,17 +180,14 @@ mrb_range_initialize(mrb_state *mrb, mrb_value range) * (0..2) == (0..2) #=> true * (0..2) == Range.new(0,2) #=> true * (0..2) == (0...2) #=> false - * */ - -mrb_value -mrb_range_eq(mrb_state *mrb, mrb_value range) +static mrb_value +range_eq(mrb_state *mrb, mrb_value range) { struct RRange *rr; struct RRange *ro; - mrb_value obj, v1, v2; - - mrb_get_args(mrb, "o", &obj); + mrb_value obj = mrb_get_arg1(mrb); + mrb_bool v1, v2; if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value(); if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */ @@ -180,108 +196,45 @@ mrb_range_eq(mrb_state *mrb, mrb_value range) rr = mrb_range_ptr(mrb, range); ro = mrb_range_ptr(mrb, obj); - v1 = mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg); - v2 = mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end); - if (!mrb_bool(v1) || !mrb_bool(v2) || rr->excl != ro->excl) { + v1 = mrb_equal(mrb, RANGE_BEG(rr), RANGE_BEG(ro)); + v2 = mrb_equal(mrb, RANGE_END(rr), RANGE_END(ro)); + if (!v1 || !v2 || RANGE_EXCL(rr) != RANGE_EXCL(ro)) { return mrb_false_value(); } return mrb_true_value(); } -static mrb_bool -r_le(mrb_state *mrb, mrb_value a, mrb_value b) -{ - mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */ - /* output :a < b => -1, a = b => 0, a > b => +1 */ - - if (mrb_fixnum_p(r)) { - mrb_int c = mrb_fixnum(r); - if (c == 0 || c == -1) return TRUE; - } - - return FALSE; -} - -static mrb_bool -r_gt(mrb_state *mrb, mrb_value a, mrb_value b) -{ - mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); - /* output :a < b => -1, a = b => 0, a > b => +1 */ - - return mrb_fixnum_p(r) && mrb_fixnum(r) == 1; -} - -static mrb_bool -r_ge(mrb_state *mrb, mrb_value a, mrb_value b) -{ - mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */ - /* output :a < b => -1, a = b => 0, a > b => +1 */ - - if (mrb_fixnum_p(r)) { - mrb_int c = mrb_fixnum(r); - if (c == 0 || c == 1) return TRUE; - } - - return FALSE; -} - /* * call-seq: * range === obj => true or false * range.member?(val) => true or false * range.include?(val) => true or false - * */ -mrb_value -mrb_range_include(mrb_state *mrb, mrb_value range) +static mrb_value +range_include(mrb_state *mrb, mrb_value range) { - mrb_value val; + mrb_value val = mrb_get_arg1(mrb); struct RRange *r = mrb_range_ptr(mrb, range); mrb_value beg, end; - mrb_bool include_p; - - mrb_get_args(mrb, "o", &val); - - beg = r->edges->beg; - end = r->edges->end; - include_p = r_le(mrb, beg, val) && /* beg <= val */ - (r->excl ? r_gt(mrb, end, val) /* end > val */ - : r_ge(mrb, end, val)); /* end >= val */ - - return mrb_bool_value(include_p); -} - -MRB_API mrb_int -mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc) -{ - mrb_int beg, end; - struct RRange *r; - if (mrb_type(range) != MRB_TT_RANGE) return 0; - r = mrb_range_ptr(mrb, range); - - beg = mrb_int(mrb, r->edges->beg); - end = mrb_int(mrb, r->edges->end); - - if (beg < 0) { - beg += len; - if (beg < 0) return 2; + beg = RANGE_BEG(r); + end = RANGE_END(r); + if (mrb_nil_p(beg)) { + if (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */ + : r_ge(mrb, end, val)) { /* end >= val */ + return mrb_true_value(); + } } - - if (trunc) { - if (beg > len) return 2; - if (end > len) end = len; + else if (r_le(mrb, beg, val)) { /* beg <= val */ + if (mrb_nil_p(end)) { + return mrb_true_value(); + } + if (RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */ + : r_ge(mrb, end, val)) { /* end >= val */ + return mrb_true_value(); + } } - - if (end < 0) end += len; - if (!r->excl && (!trunc || end < len)) - end++; /* include end point */ - len = end - beg; - if (len < 0) len = 0; - - *begp = beg; - *lenp = len; - return 1; + return mrb_false_value(); } /* 15.2.14.4.12(x) */ @@ -291,17 +244,16 @@ mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, * * Convert this range object to a printable form. */ - static mrb_value range_to_s(mrb_state *mrb, mrb_value range) { mrb_value str, str2; struct RRange *r = mrb_range_ptr(mrb, range); - str = mrb_obj_as_string(mrb, r->edges->beg); - str2 = mrb_obj_as_string(mrb, r->edges->end); + str = mrb_obj_as_string(mrb, RANGE_BEG(r)); + str2 = mrb_obj_as_string(mrb, RANGE_END(r)); str = mrb_str_dup(mrb, str); - mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); + mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2); mrb_str_cat_str(mrb, str, str2); return str; @@ -316,18 +268,24 @@ range_to_s(mrb_state *mrb, mrb_value range) * <code>inspect</code> to convert the start and end * objects). */ - static mrb_value range_inspect(mrb_state *mrb, mrb_value range) { - mrb_value str, str2; + mrb_value str; struct RRange *r = mrb_range_ptr(mrb, range); - str = mrb_inspect(mrb, r->edges->beg); - str2 = mrb_inspect(mrb, r->edges->end); - str = mrb_str_dup(mrb, str); - mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); - mrb_str_cat_str(mrb, str, str2); + if (!mrb_nil_p(RANGE_BEG(r))) { + str = mrb_inspect(mrb, RANGE_BEG(r)); + str = mrb_str_dup(mrb, str); + mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2); + } + else { + str = mrb_str_new(mrb, "...", RANGE_EXCL(r) ? 3 : 2); + } + if (!mrb_nil_p(RANGE_END(r))) { + mrb_value str2 = mrb_inspect(mrb, RANGE_END(r)); + mrb_str_cat_str(mrb, str, str2); + } return str; } @@ -344,28 +302,22 @@ range_inspect(mrb_state *mrb, mrb_value range) * (0..2).eql?(0..2) #=> true * (0..2).eql?(Range.new(0,2)) #=> true * (0..2).eql?(0...2) #=> false - * */ - static mrb_value range_eql(mrb_state *mrb, mrb_value range) { - mrb_value obj; + mrb_value obj = mrb_get_arg1(mrb); struct RRange *r, *o; - mrb_get_args(mrb, "o", &obj); - if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value(); - if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) { - return mrb_false_value(); - } - if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value(); + if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) return mrb_false_value(); + if (!mrb_range_p(obj)) return mrb_false_value(); r = mrb_range_ptr(mrb, range); o = mrb_range_ptr(mrb, obj); - if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) || - !mrb_eql(mrb, r->edges->end, o->edges->end) || - (r->excl != o->excl)) { + if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) || + !mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) || + (RANGE_EXCL(r) != RANGE_EXCL(o))) { return mrb_false_value(); } return mrb_true_value(); @@ -375,22 +327,71 @@ range_eql(mrb_state *mrb, mrb_value range) static mrb_value range_initialize_copy(mrb_state *mrb, mrb_value copy) { - mrb_value src; + mrb_value src = mrb_get_arg1(mrb); struct RRange *r; - mrb_get_args(mrb, "o", &src); - if (mrb_obj_equal(mrb, copy, src)) return copy; if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) { mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); } r = mrb_range_ptr(mrb, src); - range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl); + range_ptr_replace(mrb, mrb_range_raw_ptr(copy), RANGE_BEG(r), RANGE_END(r), RANGE_EXCL(r)); + mrb_obj_freeze(mrb, copy); return copy; } +static mrb_value +range_num_to_a(mrb_state *mrb, mrb_value range) +{ + struct RRange *r = mrb_range_ptr(mrb, range); + mrb_value beg = RANGE_BEG(r); + mrb_value end = RANGE_END(r); + mrb_value ary; + + if (mrb_nil_p(end)) { + mrb->c->ci->mid = 0; + mrb_raise(mrb, E_RANGE_ERROR, "cannot convert endless range to an array"); + } + if (mrb_integer_p(beg)) { + if (mrb_integer_p(end)) { + mrb_int a = mrb_integer(beg); + mrb_int b = mrb_integer(end); + mrb_int len = b - a; + + if (!RANGE_EXCL(r)) len++; + ary = mrb_ary_new_capa(mrb, len); + for (mrb_int i=0; i<len; i++) { + mrb_ary_push(mrb, ary, mrb_int_value(mrb, a+i)); + } + return ary; + } +#ifndef MRB_NO_FLOAT + if (mrb_float_p(end)) { + mrb_float a = (mrb_float)mrb_integer(beg); + mrb_float b = mrb_float(end); + + ary = mrb_ary_new_capa(mrb, (mrb_int)(b - a) + 1); + if (RANGE_EXCL(r)) { + while (a < b) { + mrb_ary_push(mrb, ary, mrb_int_value(mrb, (mrb_int)a)); + a += 1.0; + } + } + else { + while (a <= b) { + mrb_ary_push(mrb, ary, mrb_int_value(mrb, (mrb_int)a)); + a += 1.0; + } + } + return ary; + } +#endif + } + return mrb_nil_value(); +} + mrb_value mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int)) { @@ -399,10 +400,10 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con result = mrb_ary_new(mrb); for (i = 0; i < argc; ++i) { - if (mrb_fixnum_p(argv[i])) { - mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i]))); + if (mrb_integer_p(argv[i])) { + mrb_ary_push(mrb, result, func(mrb, obj, mrb_integer(argv[i]))); } - else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) { + else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == MRB_RANGE_OK) { mrb_int const end = olen < beg + len ? olen : beg + len; for (j = beg; j < end; ++j) { mrb_ary_push(mrb, result, func(mrb, obj, j)); @@ -413,7 +414,7 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con } } else { - mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]); + mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %v", argv[i]); } } @@ -421,6 +422,68 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con } void +mrb_gc_mark_range(mrb_state *mrb, struct RRange *r) +{ + if (RANGE_INITIALIZED_P(r)) { + mrb_gc_mark_value(mrb, RANGE_BEG(r)); + mrb_gc_mark_value(mrb, RANGE_END(r)); + } +} + +MRB_API struct RRange* +mrb_range_ptr(mrb_state *mrb, mrb_value range) +{ + struct RRange *r = mrb_range_raw_ptr(range); + + /* check for if #initialize_copy was removed [#3320] */ + if (!RANGE_INITIALIZED_P(r)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range"); + } + return r; +} + +MRB_API mrb_value +mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl) +{ + struct RRange *r = range_ptr_init(mrb, NULL, beg, end, excl); + return mrb_range_value(r); +} + +MRB_API enum mrb_range_beg_len +mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc) +{ + mrb_int beg, end; + mrb_bool excl; + struct RRange *r; + + if (!mrb_range_p(range)) return MRB_RANGE_TYPE_MISMATCH; + r = mrb_range_ptr(mrb, range); + + beg = mrb_nil_p(RANGE_BEG(r)) ? 0 : mrb_as_int(mrb, RANGE_BEG(r)); + end = mrb_nil_p(RANGE_END(r)) ? -1 : mrb_as_int(mrb, RANGE_END(r)); + excl = mrb_nil_p(RANGE_END(r)) ? 0 : RANGE_EXCL(r); + + if (beg < 0) { + beg += len; + if (beg < 0) return MRB_RANGE_OUT; + } + + if (trunc) { + if (beg > len) return MRB_RANGE_OUT; + if (end > len) end = len; + } + + if (end < 0) end += len; + if (!excl && (!trunc || end < len)) end++; /* include end point */ + len = end - beg; + if (len < 0) len = 0; + + *begp = beg; + *lenp = len; + return MRB_RANGE_OK; +} + +void mrb_init_range(mrb_state *mrb) { struct RClass *r; @@ -429,19 +492,19 @@ mrb_init_range(mrb_state *mrb) mrb->range_class = r; MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE); - mrb_define_method(mrb, r, "begin", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */ - mrb_define_method(mrb, r, "end", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */ - mrb_define_method(mrb, r, "==", mrb_range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */ - mrb_define_method(mrb, r, "===", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */ - mrb_define_method(mrb, r, "exclude_end?", mrb_range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */ - mrb_define_method(mrb, r, "first", mrb_range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */ - mrb_define_method(mrb, r, "include?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */ - mrb_define_method(mrb, r, "initialize", mrb_range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */ - mrb_define_method(mrb, r, "last", mrb_range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */ - mrb_define_method(mrb, r, "member?", mrb_range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */ - + mrb_define_method(mrb, r, "begin", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */ + mrb_define_method(mrb, r, "end", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */ + mrb_define_method(mrb, r, "==", range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */ + mrb_define_method(mrb, r, "===", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */ + mrb_define_method(mrb, r, "exclude_end?", range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */ + mrb_define_method(mrb, r, "first", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */ + mrb_define_method(mrb, r, "include?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */ + mrb_define_method(mrb, r, "initialize", range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */ + mrb_define_method(mrb, r, "last", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */ + mrb_define_method(mrb, r, "member?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */ mrb_define_method(mrb, r, "to_s", range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */ mrb_define_method(mrb, r, "inspect", range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */ mrb_define_method(mrb, r, "eql?", range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */ mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */ + mrb_define_method(mrb, r, "__num_to_a", range_num_to_a, MRB_ARGS_NONE()); } diff --git a/src/readflt.c b/src/readflt.c new file mode 100644 index 000000000..19a8e8dc6 --- /dev/null +++ b/src/readflt.c @@ -0,0 +1,120 @@ +#include <mruby.h> + +#ifndef MRB_NO_FLOAT +/* + * strtod implementation. + * author: Yasuhiro Matsumoto (@mattn) + * license: public domain + */ + +/* +The original code can be found in https://github.com/mattn/strtod + +I modified the routine for mruby: + + * renamed the function `vim_strtod` -> `mrb_float_read` + * simplified the code + +My modifications in this file are also placed in the public domain. + +Matz (Yukihiro Matsumoto) +*/ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <errno.h> + +MRB_API double +mrb_float_read(const char *str, char **end) +{ + double d = 0.0; + int sign; + int n = 0; + const char *p, *a; + + a = p = str; + while (ISSPACE(*p)) + ++p; + + /* decimal part */ + sign = 1; + if (*p == '-') { + sign = -1; + ++p; + } else if (*p == '+') + ++p; + if (ISDIGIT(*p)) { + d = (double)(*p++ - '0'); + while (*p && ISDIGIT(*p)) { + d = d * 10.0 + (double)(*p - '0'); + ++p; + ++n; + } + a = p; + } else if (*p != '.') + goto done; + d *= sign; + + /* fraction part */ + if (*p == '.') { + double f = 0.0; + double base = 0.1; + ++p; + + if (ISDIGIT(*p)) + { + while (*p && ISDIGIT(*p)) { + f += base * (*p - '0') ; + base /= 10.0; + ++p; + ++n; + } + } + d += f * sign; + a = p; + } + + /* exponential part */ + if ((*p == 'E') || (*p == 'e')) { + int e = 0; + ++p; + + sign = 1; + if (*p == '-') { + sign = -1; + ++p; + } else if (*p == '+') + ++p; + + if (ISDIGIT(*p)) { + while (*p == '0') + ++p; + if (*p == '\0') --p; + e = (int)(*p++ - '0'); + for (; *p && ISDIGIT(*p); p++) { + if (e < 10000) + e = e * 10 + (*p - '0'); + } + e *= sign; + } + else if (!ISDIGIT(*(a-1))) { + a = str; + goto done; + } + else if (*p == 0) + goto done; + d *= pow(10.0, (double) e); + a = p; + } + else if (p > str && !ISDIGIT(*(p-1))) { + a = str; + goto done; + } + +done: + if (end) + *end = (char*)a; + return d; +} +#endif diff --git a/src/readint.c b/src/readint.c new file mode 100644 index 000000000..5fae222c2 --- /dev/null +++ b/src/readint.c @@ -0,0 +1,30 @@ +#include <mruby.h> +#include <mruby/numeric.h> +#include <errno.h> + +/* mrb_int_read(): read mrb_int from a string (base 10 only) */ +/* const char *p - string to read */ +/* const char *e - end of string */ +/* char **endp - end of parsed integer */ + +/* if integer overflows, errno will be set to ERANGE */ +/* also endp will be set to NULL on overflow */ +MRB_API mrb_int +mrb_int_read(const char *p, const char *e, char **endp) +{ + mrb_int n = 0; + int ch; + + while ((e == NULL || p < e) && ISDIGIT(*p)) { + ch = *p - '0'; + if (mrb_int_mul_overflow(n, 10, &n) || + mrb_int_add_overflow(n, ch, &n)) { + if (endp) *endp = NULL; + errno = ERANGE; + return MRB_INT_MAX; + } + p++; + } + if (endp) *endp = (char*)p; + return n; +} diff --git a/src/state.c b/src/state.c index 18d104555..4dafc964a 100644 --- a/src/state.c +++ b/src/state.c @@ -19,13 +19,28 @@ void mrb_init_mrbgems(mrb_state*); void mrb_gc_init(mrb_state*, mrb_gc *gc); void mrb_gc_destroy(mrb_state*, mrb_gc *gc); +int mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state *, void *), void *opaque); + +static void +init_gc_and_core(mrb_state *mrb, void *opaque) +{ + static const struct mrb_context mrb_context_zero = { 0 }; + + mrb_gc_init(mrb, &mrb->gc); + mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); + *mrb->c = mrb_context_zero; + mrb->root_c = mrb->c; + + mrb_init_core(mrb); +} + MRB_API mrb_state* mrb_open_core(mrb_allocf f, void *ud) { static const mrb_state mrb_state_zero = { 0 }; - static const struct mrb_context mrb_context_zero = { 0 }; mrb_state *mrb; + if (f == NULL) f = mrb_default_allocf; mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud); if (mrb == NULL) return NULL; @@ -34,12 +49,10 @@ mrb_open_core(mrb_allocf f, void *ud) mrb->allocf = f; mrb->atexit_stack_len = 0; - mrb_gc_init(mrb, &mrb->gc); - mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); - *mrb->c = mrb_context_zero; - mrb->root_c = mrb->c; - - mrb_init_core(mrb); + if (mrb_core_init_protect(mrb, init_gc_and_core, NULL)) { + mrb_close(mrb); + return NULL; + } return mrb; } @@ -56,38 +69,6 @@ mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud) } } -struct alloca_header { - struct alloca_header *next; - char buf[1]; -}; - -MRB_API void* -mrb_alloca(mrb_state *mrb, size_t size) -{ - struct alloca_header *p; - - p = (struct alloca_header*) mrb_malloc(mrb, sizeof(struct alloca_header)+size); - p->next = mrb->mems; - mrb->mems = p; - return (void*)p->buf; -} - -static void -mrb_alloca_free(mrb_state *mrb) -{ - struct alloca_header *p; - struct alloca_header *tmp; - - if (mrb == NULL) return; - p = mrb->mems; - - while (p) { - tmp = p; - p = p->next; - mrb_free(mrb, tmp); - } -} - MRB_API mrb_state* mrb_open(void) { @@ -96,6 +77,14 @@ mrb_open(void) return mrb; } +#ifndef MRB_NO_GEMS +static void +init_mrbgems(mrb_state *mrb, void *opaque) +{ + mrb_init_mrbgems(mrb); +} +#endif + MRB_API mrb_state* mrb_open_allocf(mrb_allocf f, void *ud) { @@ -105,8 +94,11 @@ mrb_open_allocf(mrb_allocf f, void *ud) return NULL; } -#ifndef DISABLE_GEMS - mrb_init_mrbgems(mrb); +#ifndef MRB_NO_GEMS + if (mrb_core_init_protect(mrb, init_mrbgems, NULL)) { + mrb_close(mrb); + return NULL; + } mrb_gc_arena_restore(mrb, 0); #endif return mrb; @@ -117,12 +109,20 @@ void mrb_free_symtbl(mrb_state *mrb); void mrb_irep_incref(mrb_state *mrb, mrb_irep *irep) { + if (irep->flags & MRB_IREP_NO_FREE) return; + if (irep->refcnt == UINT16_MAX) { + mrb_garbage_collect(mrb); + if (irep->refcnt == UINT16_MAX) { + mrb_raise(mrb, E_RUNTIME_ERROR, "too many irep references"); + } + } irep->refcnt++; } void mrb_irep_decref(mrb_state *mrb, mrb_irep *irep) { + if (irep->flags & MRB_IREP_NO_FREE) return; irep->refcnt--; if (irep->refcnt == 0) { mrb_irep_free(mrb, irep); @@ -132,12 +132,14 @@ mrb_irep_decref(mrb_state *mrb, mrb_irep *irep) void mrb_irep_cutref(mrb_state *mrb, mrb_irep *irep) { - mrb_irep *tmp; + mrb_irep **reps; int i; + if (irep->flags & MRB_IREP_NO_FREE) return; + reps = (mrb_irep**)irep->reps; for (i=0; i<irep->rlen; i++) { - tmp = irep->reps[i]; - irep->reps[i] = NULL; + mrb_irep *tmp = reps[i]; + reps[i] = NULL; if (tmp) mrb_irep_decref(mrb, tmp); } } @@ -147,120 +149,53 @@ mrb_irep_free(mrb_state *mrb, mrb_irep *irep) { int i; + if (irep->flags & MRB_IREP_NO_FREE) return; if (!(irep->flags & MRB_ISEQ_NO_FREE)) - mrb_free(mrb, irep->iseq); - if (irep->pool) for (i=0; i<irep->plen; i++) { - if (mrb_type(irep->pool[i]) == MRB_TT_STRING) { - mrb_gc_free_str(mrb, RSTRING(irep->pool[i])); - mrb_free(mrb, mrb_obj_ptr(irep->pool[i])); - } -#if defined(MRB_WORD_BOXING) && !defined(MRB_WITHOUT_FLOAT) - else if (mrb_type(irep->pool[i]) == MRB_TT_FLOAT) { - mrb_free(mrb, mrb_obj_ptr(irep->pool[i])); + mrb_free(mrb, (void*)irep->iseq); + if (irep->pool) { + for (i=0; i<irep->plen; i++) { + if ((irep->pool[i].tt & 3) == IREP_TT_STR || + irep->pool[i].tt == IREP_TT_BIGINT) { + mrb_free(mrb, (void*)irep->pool[i].u.str); + } } -#endif - } - mrb_free(mrb, irep->pool); - mrb_free(mrb, irep->syms); - for (i=0; i<irep->rlen; i++) { - if (irep->reps[i]) - mrb_irep_decref(mrb, irep->reps[i]); + mrb_free(mrb, (void*)irep->pool); } - mrb_free(mrb, irep->reps); - mrb_free(mrb, irep->lv); - if (irep->own_filename) { - mrb_free(mrb, (void *)irep->filename); + mrb_free(mrb, (void*)irep->syms); + if (irep->reps) { + for (i=0; i<irep->rlen; i++) { + if (irep->reps[i]) + mrb_irep_decref(mrb, (mrb_irep*)irep->reps[i]); + } + mrb_free(mrb, (void*)irep->reps); } - mrb_free(mrb, irep->lines); + mrb_free(mrb, (void*)irep->lv); mrb_debug_info_free(mrb, irep->debug_info); mrb_free(mrb, irep); } -mrb_value -mrb_str_pool(mrb_state *mrb, mrb_value str) -{ - struct RString *s = mrb_str_ptr(str); - struct RString *ns; - char *ptr; - mrb_int len; - - ns = (struct RString *)mrb_malloc(mrb, sizeof(struct RString)); - ns->tt = MRB_TT_STRING; - ns->c = mrb->string_class; - - if (RSTR_NOFREE_P(s)) { - ns->flags = MRB_STR_NOFREE; - ns->as.heap.ptr = s->as.heap.ptr; - ns->as.heap.len = s->as.heap.len; - ns->as.heap.aux.capa = 0; - } - else { - ns->flags = 0; - if (RSTR_EMBED_P(s)) { - ptr = s->as.ary; - len = RSTR_EMBED_LEN(s); - } - else { - ptr = s->as.heap.ptr; - len = s->as.heap.len; - } - - if (len < RSTRING_EMBED_LEN_MAX) { - RSTR_SET_EMBED_FLAG(ns); - RSTR_SET_EMBED_LEN(ns, len); - if (ptr) { - memcpy(ns->as.ary, ptr, len); - } - ns->as.ary[len] = '\0'; - } - else { - ns->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1); - ns->as.heap.len = len; - ns->as.heap.aux.capa = len; - if (ptr) { - memcpy(ns->as.heap.ptr, ptr, len); - } - ns->as.heap.ptr[len] = '\0'; - } - } - RSTR_SET_POOL_FLAG(ns); - MRB_SET_FROZEN_FLAG(ns); - return mrb_obj_value(ns); -} - -void mrb_free_backtrace(mrb_state *mrb); - MRB_API void mrb_free_context(mrb_state *mrb, struct mrb_context *c) { if (!c) return; mrb_free(mrb, c->stbase); mrb_free(mrb, c->cibase); - mrb_free(mrb, c->rescue); - mrb_free(mrb, c->ensure); mrb_free(mrb, c); } -MRB_API void +void mrb_protect_atexit(mrb_state *mrb); + + MRB_API void mrb_close(mrb_state *mrb) { if (!mrb) return; - if (mrb->atexit_stack_len > 0) { - mrb_int i; - for (i = mrb->atexit_stack_len; i > 0; --i) { - mrb->atexit_stack[i - 1](mrb); - } -#ifndef MRB_FIXED_STATE_ATEXIT_STACK - mrb_free(mrb, mrb->atexit_stack); -#endif - } + mrb_protect_atexit(mrb); /* free */ - mrb_gc_free_gv(mrb); + mrb_gc_destroy(mrb, &mrb->gc); mrb_free_context(mrb, mrb->root_c); + mrb_gc_free_gv(mrb); mrb_free_symtbl(mrb); - mrb_alloca_free(mrb); - mrb_gc_destroy(mrb, &mrb->gc); mrb_free(mrb, mrb); } @@ -273,7 +208,6 @@ mrb_add_irep(mrb_state *mrb) irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep)); *irep = mrb_irep_zero; irep->refcnt = 1; - irep->own_filename = FALSE; return irep; } diff --git a/src/string.c b/src/string.c index b7abfb762..03d9c8a9c 100644 --- a/src/string.c +++ b/src/string.c @@ -8,8 +8,9 @@ # define _CRT_NONSTDC_NO_DEPRECATE #endif -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT #include <float.h> +#include <math.h> #endif #include <limits.h> #include <stddef.h> @@ -20,80 +21,124 @@ #include <mruby/class.h> #include <mruby/range.h> #include <mruby/string.h> -#include <mruby/re.h> +#include <mruby/numeric.h> +#include <mruby/presym.h> typedef struct mrb_shared_string { - mrb_bool nofree : 1; int refcnt; + mrb_ssize capa; char *ptr; - mrb_int len; } mrb_shared_string; const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; -#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class)) +#define mrb_obj_alloc_string(mrb) MRB_OBJ_ALLOC((mrb), MRB_TT_STRING, (mrb)->string_class) static struct RString* -str_new_static(mrb_state *mrb, const char *p, size_t len) +str_init_normal_capa(mrb_state *mrb, struct RString *s, + const char *p, size_t len, size_t capa) +{ + char *dst = (char *)mrb_malloc(mrb, capa + 1); + if (p) memcpy(dst, p, len); + dst[len] = '\0'; + s->as.heap.ptr = dst; + s->as.heap.len = (mrb_ssize)len; + s->as.heap.aux.capa = (mrb_ssize)capa; + RSTR_UNSET_TYPE_FLAG(s); + return s; +} + +static struct RString* +str_init_normal(mrb_state *mrb, struct RString *s, const char *p, size_t len) { - struct RString *s; + return str_init_normal_capa(mrb, s, p, len, len); +} - if (len >= MRB_INT_MAX) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); - } - s = mrb_obj_alloc_string(mrb); - s->as.heap.len = (mrb_int)len; - s->as.heap.aux.capa = 0; /* nofree */ +static struct RString* +str_init_embed(struct RString *s, const char *p, size_t len) +{ + if (p) memcpy(RSTR_EMBED_PTR(s), p, len); + RSTR_EMBED_PTR(s)[len] = '\0'; + RSTR_SET_TYPE_FLAG(s, EMBED); + RSTR_SET_EMBED_LEN(s, len); + return s; +} + +static struct RString* +str_init_nofree(struct RString *s, const char *p, size_t len) +{ s->as.heap.ptr = (char *)p; - s->flags = MRB_STR_NOFREE; + s->as.heap.len = (mrb_ssize)len; + s->as.heap.aux.capa = 0; /* nofree */ + RSTR_SET_TYPE_FLAG(s, NOFREE); + return s; +} +static struct RString* +str_init_shared(mrb_state *mrb, const struct RString *orig, struct RString *s, mrb_shared_string *shared) +{ + if (shared) { + shared->refcnt++; + } + else { + shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string)); + shared->refcnt = 1; + shared->ptr = orig->as.heap.ptr; + shared->capa = orig->as.heap.aux.capa; + } + s->as.heap.ptr = orig->as.heap.ptr; + s->as.heap.len = orig->as.heap.len; + s->as.heap.aux.shared = shared; + RSTR_SET_TYPE_FLAG(s, SHARED); return s; } static struct RString* -str_new(mrb_state *mrb, const char *p, size_t len) +str_init_fshared(const struct RString *orig, struct RString *s, struct RString *fshared) { - struct RString *s; + s->as.heap.ptr = orig->as.heap.ptr; + s->as.heap.len = orig->as.heap.len; + s->as.heap.aux.fshared = fshared; + RSTR_SET_TYPE_FLAG(s, FSHARED); + return s; +} - if (p && mrb_ro_data_p(p)) { - return str_new_static(mrb, p, len); - } - s = mrb_obj_alloc_string(mrb); - if (len <= RSTRING_EMBED_LEN_MAX) { - RSTR_SET_EMBED_FLAG(s); - RSTR_SET_EMBED_LEN(s, len); - if (p) { - memcpy(s->as.ary, p, len); - } +static struct RString* +str_init_modifiable(mrb_state *mrb, struct RString *s, const char *p, size_t len) +{ + if (RSTR_EMBEDDABLE_P(len)) { + return str_init_embed(s, p, len); } else { - if (len >= MRB_INT_MAX) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); - } - s->as.heap.ptr = (char *)mrb_malloc(mrb, len+1); - s->as.heap.len = (mrb_int)len; - s->as.heap.aux.capa = (mrb_int)len; - if (p) { - memcpy(s->as.heap.ptr, p, len); - } + return str_init_normal(mrb, s, p, len); } - RSTR_PTR(s)[len] = '\0'; - return s; } -static inline void -str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj) +static struct RString* +str_new_static(mrb_state *mrb, const char *p, size_t len) { - s->c = mrb_str_ptr(obj)->c; + if (RSTR_EMBEDDABLE_P(len)) { + return str_init_embed(mrb_obj_alloc_string(mrb), p, len); + } + if (len >= MRB_SSIZE_MAX) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); + } + return str_init_nofree(mrb_obj_alloc_string(mrb), p, len); } -static mrb_value -mrb_str_new_empty(mrb_state *mrb, mrb_value str) +static struct RString* +str_new(mrb_state *mrb, const char *p, size_t len) { - struct RString *s = str_new(mrb, 0, 0); - - str_with_class(mrb, s, str); - return mrb_obj_value(s); + if (RSTR_EMBEDDABLE_P(len)) { + return str_init_embed(mrb_obj_alloc_string(mrb), p, len); + } + if (len >= MRB_SSIZE_MAX) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); + } + if (p && mrb_ro_data_p(p)) { + return str_init_nofree(mrb_obj_alloc_string(mrb), p, len); + } + return str_init_normal(mrb, mrb_obj_alloc_string(mrb), p, len); } MRB_API mrb_value @@ -101,52 +146,35 @@ mrb_str_new_capa(mrb_state *mrb, size_t capa) { struct RString *s; - s = mrb_obj_alloc_string(mrb); - - if (capa >= MRB_INT_MAX) { + if (RSTR_EMBEDDABLE_P(capa)) { + s = str_init_embed(mrb_obj_alloc_string(mrb), NULL, 0); + } + else if (capa >= MRB_SSIZE_MAX) { mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big"); + /* not reached */ + s = NULL; + } + else { + s = str_init_normal_capa(mrb, mrb_obj_alloc_string(mrb), NULL, 0, capa); } - s->as.heap.len = 0; - s->as.heap.aux.capa = (mrb_int)capa; - s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1); - RSTR_PTR(s)[0] = '\0'; return mrb_obj_value(s); } -#ifndef MRB_STR_BUF_MIN_SIZE -# define MRB_STR_BUF_MIN_SIZE 128 -#endif - -MRB_API mrb_value -mrb_str_buf_new(mrb_state *mrb, size_t capa) -{ - if (capa < MRB_STR_BUF_MIN_SIZE) { - capa = MRB_STR_BUF_MIN_SIZE; - } - return mrb_str_new_capa(mrb, capa); -} - static void resize_capa(mrb_state *mrb, struct RString *s, size_t capacity) { -#if SIZE_MAX > MRB_INT_MAX - mrb_assert(capacity < MRB_INT_MAX); +#if SIZE_MAX > MRB_SSIZE_MAX + mrb_assert(capacity < MRB_SSIZE_MAX); #endif if (RSTR_EMBED_P(s)) { - if (RSTRING_EMBED_LEN_MAX < capacity) { - char *const tmp = (char *)mrb_malloc(mrb, capacity+1); - const mrb_int len = RSTR_EMBED_LEN(s); - memcpy(tmp, s->as.ary, len); - RSTR_UNSET_EMBED_FLAG(s); - s->as.heap.ptr = tmp; - s->as.heap.len = len; - s->as.heap.aux.capa = (mrb_int)capacity; + if (!RSTR_EMBEDDABLE_P(capacity)) { + str_init_normal_capa(mrb, s, RSTR_EMBED_PTR(s), RSTR_EMBED_LEN(s), capacity); } } else { s->as.heap.ptr = (char*)mrb_realloc(mrb, RSTR_PTR(s), capacity+1); - s->as.heap.aux.capa = (mrb_int)capacity; + s->as.heap.aux.capa = (mrb_ssize)capacity; } } @@ -156,13 +184,6 @@ mrb_str_new(mrb_state *mrb, const char *p, size_t len) return mrb_obj_value(str_new(mrb, p, len)); } -/* - * call-seq: (Caution! NULL string) - * String.new(str="") => new_str - * - * Returns a new string object containing a copy of <i>str</i>. - */ - MRB_API mrb_value mrb_str_new_cstr(mrb_state *mrb, const char *p) { @@ -193,13 +214,42 @@ str_decref(mrb_state *mrb, mrb_shared_string *shared) { shared->refcnt--; if (shared->refcnt == 0) { - if (!shared->nofree) { - mrb_free(mrb, shared->ptr); - } + mrb_free(mrb, shared->ptr); mrb_free(mrb, shared); } } +static void +str_modify_keep_ascii(mrb_state *mrb, struct RString *s) +{ + if (RSTR_SHARED_P(s)) { + mrb_shared_string *shared = s->as.heap.aux.shared; + + if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { + s->as.heap.aux.capa = shared->capa; + s->as.heap.ptr[s->as.heap.len] = '\0'; + RSTR_UNSET_SHARED_FLAG(s); + mrb_free(mrb, shared); + } + else { + str_init_modifiable(mrb, s, s->as.heap.ptr, (size_t)s->as.heap.len); + str_decref(mrb, shared); + } + } + else if (RSTR_NOFREE_P(s) || RSTR_FSHARED_P(s)) { + str_init_modifiable(mrb, s, s->as.heap.ptr, (size_t)s->as.heap.len); + } +} + +static void +check_null_byte(mrb_state *mrb, mrb_value str) +{ + mrb_to_str(mrb, str); + if (memchr(RSTRING_PTR(str), '\0', RSTRING_LEN(str))) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); + } +} + void mrb_gc_free_str(mrb_state *mrb, struct RString *str) { @@ -220,104 +270,254 @@ static const char utf8len_codepage[256] = 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1, }; -static mrb_int -utf8len(const char* p, const char* e) +#define utf8_islead(c) ((unsigned char)((c)&0xc0) != 0x80) + +mrb_int +mrb_utf8len(const char* p, const char* e) { mrb_int len; mrb_int i; + if ((unsigned char)*p < 0x80) return 1; len = utf8len_codepage[(unsigned char)*p]; - if (p + len > e) return 1; + if (len == 1) return 1; + if (len > e - p) return 1; for (i = 1; i < len; ++i) - if ((p[i] & 0xc0) != 0x80) + if (utf8_islead(p[i])) return 1; return len; } -static mrb_int -utf8_strlen(mrb_value str, mrb_int len) +mrb_int +mrb_utf8_strlen(const char *str, mrb_int byte_len) { - mrb_int total = 0; - char* p = RSTRING_PTR(str); - char* e = p; - if (RSTRING(str)->flags & MRB_STR_NO_UTF) { - return RSTRING_LEN(str); + mrb_int len = 0; + const char *p = str; + const char *e = p + byte_len; + + while (p < e) { + p += mrb_utf8len(p, e); + len++; } - e += len < 0 ? RSTRING_LEN(str) : len; - while (p<e) { - p += utf8len(p, e); - total++; + return len; +} + +static mrb_int +utf8_strlen(mrb_value str) +{ + struct RString *s = mrb_str_ptr(str); + mrb_int byte_len = RSTR_LEN(s); + + if (RSTR_ASCII_P(s)) { + return byte_len; } - if (RSTRING_LEN(str) == total) { - RSTRING(str)->flags |= MRB_STR_NO_UTF; + else { + mrb_int utf8_len = mrb_utf8_strlen(RSTR_PTR(s), byte_len); + if (byte_len == utf8_len) RSTR_SET_ASCII_FLAG(s); + return utf8_len; } - return total; } -#define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1) +#define RSTRING_CHAR_LEN(s) utf8_strlen(s) /* map character index to byte offset index */ static mrb_int chars2bytes(mrb_value s, mrb_int off, mrb_int idx) { - mrb_int i, b, n; - const char *p = RSTRING_PTR(s) + off; - const char *e = RSTRING_END(s); + if (RSTR_ASCII_P(mrb_str_ptr(s))) { + return idx; + } + else { + mrb_int i, b, n; + const char *p = RSTRING_PTR(s) + off; + const char *e = RSTRING_END(s); - for (b=i=0; p<e && i<idx; i++) { - n = utf8len(p, e); - b += n; - p += n; + for (b=i=0; p<e && i<idx; i++) { + n = mrb_utf8len(p, e); + b += n; + p += n; + } + return b; } - return b; } /* map byte offset to character index */ static mrb_int -bytes2chars(char *p, mrb_int bi) +bytes2chars(char *p, mrb_int len, mrb_int bi) { - mrb_int i, b, n; + const char *e = p + (size_t)len; + const char *pivot = p + bi; + mrb_int i; - for (b=i=0; b<bi; i++) { - n = utf8len_codepage[(unsigned char)*p]; - b += n; - p += n; + for (i = 0; p < pivot; i ++) { + p += mrb_utf8len(p, e); } - if (b != bi) return -1; + if (p != pivot) return -1; return i; } +static const char * +char_adjust(const char *beg, const char *end, const char *ptr) +{ + if ((ptr > beg || ptr < end) && (*ptr & 0xc0) == 0x80) { + const int utf8_adjust_max = 3; + const char *p; + + if (ptr - beg > utf8_adjust_max) { + beg = ptr - utf8_adjust_max; + } + + p = ptr; + while (p > beg) { + p --; + if ((*p & 0xc0) != 0x80) { + int clen = mrb_utf8len(p, end); + if (clen > ptr - p) return p; + break; + } + } + } + + return ptr; +} + +static const char * +char_backtrack(const char *ptr, const char *end) +{ + if (ptr < end) { + const int utf8_bytelen_max = 4; + const char *p; + + if (end - ptr > utf8_bytelen_max) { + ptr = end - utf8_bytelen_max; + } + + p = end; + while (p > ptr) { + p --; + if ((*p & 0xc0) != 0x80) { + int clen = utf8len_codepage[(unsigned char)*p]; + if (clen == end - p) { return p; } + break; + } + } + } + + return end - 1; +} + +static mrb_int +str_index_str_by_char_search(mrb_state *mrb, const char *p, const char *pend, const char *s, const mrb_int slen, mrb_int off) +{ + /* Based on Quick Search algorithm (Boyer-Moore-Horspool algorithm) */ + + ptrdiff_t qstable[1 << CHAR_BIT]; + + /* Preprocessing */ + { + mrb_int i; + + for (i = 0; i < 1 << CHAR_BIT; i ++) { + qstable[i] = slen; + } + for (i = 0; i < slen; i ++) { + qstable[(unsigned char)s[i]] = slen - (i + 1); + } + } + + /* Searching */ + while (p < pend && pend - p >= slen) { + const char *pivot; + + if (memcmp(p, s, slen) == 0) { + return off; + } + + pivot = p + qstable[(unsigned char)p[slen - 1]]; + if (pivot >= pend || pivot < p /* overflowed */) { return -1; } + + do { + p += mrb_utf8len(p, pend); + off ++; + } while (p < pivot); + } + + return -1; +} + +static mrb_int +str_index_str_by_char(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) +{ + const char *p = RSTRING_PTR(str); + const char *pend = p + RSTRING_LEN(str); + const char *s = RSTRING_PTR(sub); + const mrb_int slen = RSTRING_LEN(sub); + mrb_int off = pos; + + for (; pos > 0; pos --) { + if (pend - p < 1) { return -1; } + p += mrb_utf8len(p, pend); + } + + if (slen < 1) { return off; } + + return str_index_str_by_char_search(mrb, p, pend, s, slen, off); +} + #define BYTES_ALIGN_CHECK(pos) if (pos < 0) return mrb_nil_value(); #else #define RSTRING_CHAR_LEN(s) RSTRING_LEN(s) #define chars2bytes(p, off, ci) (ci) -#define bytes2chars(p, bi) (bi) +#define bytes2chars(p, end, bi) (bi) +#define char_adjust(beg, end, ptr) (ptr) +#define char_backtrack(ptr, end) ((end) - 1) #define BYTES_ALIGN_CHECK(pos) +#define str_index_str_by_char(mrb, str, sub, pos) str_index_str(mrb, str, sub, pos) +#endif + +#ifndef MRB_QS_SHORT_STRING_LENGTH +#define MRB_QS_SHORT_STRING_LENGTH 2048 #endif static inline mrb_int mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n) { - const unsigned char *x = xs, *xe = xs + m; - const unsigned char *y = ys; - int i; - ptrdiff_t qstable[256]; + if (n + m < MRB_QS_SHORT_STRING_LENGTH) { + const unsigned char *y = ys; + const unsigned char *ye = ys+n-m+1; - /* Preprocessing */ - for (i = 0; i < 256; ++i) - qstable[i] = m + 1; - for (; x < xe; ++x) - qstable[*x] = xe - x; - /* Searching */ - for (; y + m <= ys + n; y += *(qstable + y[m])) { - if (*xs == *y && memcmp(xs, y, m) == 0) - return (mrb_int)(y - ys); + for (;;) { + y = (const unsigned char*)memchr(y, xs[0], (size_t)(ye-y)); + if (y == NULL) return -1; + if (memcmp(xs, y, m) == 0) { + return (mrb_int)(y - ys); + } + y++; + } + return -1; + } + else { + const unsigned char *x = xs, *xe = xs + m; + const unsigned char *y = ys; + int i; + ptrdiff_t qstable[256]; + + /* Preprocessing */ + for (i = 0; i < 256; ++i) + qstable[i] = m + 1; + for (; x < xe; ++x) + qstable[*x] = xe - x; + /* Searching */ + for (; y + m <= ys + n; y += *(qstable + y[m])) { + if (*xs == *y && memcmp(xs, y, m) == 0) + return (mrb_int)(y - ys); + } + return -1; } - return -1; } static mrb_int @@ -344,113 +544,87 @@ mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n) } static void -str_make_shared(mrb_state *mrb, struct RString *orig, struct RString *s) +str_share(mrb_state *mrb, struct RString *orig, struct RString *s) { - mrb_shared_string *shared; - mrb_int len = RSTR_LEN(orig); + size_t len = (size_t)orig->as.heap.len; mrb_assert(!RSTR_EMBED_P(orig)); - if (RSTR_SHARED_P(orig)) { - shared = orig->as.heap.aux.shared; - shared->refcnt++; - s->as.heap.ptr = orig->as.heap.ptr; - s->as.heap.len = len; - s->as.heap.aux.shared = shared; - RSTR_SET_SHARED_FLAG(s); - RSTR_UNSET_EMBED_FLAG(s); + if (RSTR_NOFREE_P(orig)) { + str_init_nofree(s, orig->as.heap.ptr, len); + } + else if (RSTR_SHARED_P(orig)) { + str_init_shared(mrb, orig, s, orig->as.heap.aux.shared); } else if (RSTR_FSHARED_P(orig)) { - struct RString *fs; - - fs = orig->as.heap.aux.fshared; - s->as.heap.ptr = orig->as.heap.ptr; - s->as.heap.len = len; - s->as.heap.aux.fshared = fs; - RSTR_SET_FSHARED_FLAG(s); - RSTR_UNSET_EMBED_FLAG(s); - } - else if (MRB_FROZEN_P(orig) && !RSTR_POOL_P(orig)) { - s->as.heap.ptr = orig->as.heap.ptr; - s->as.heap.len = len; - s->as.heap.aux.fshared = orig; - RSTR_SET_FSHARED_FLAG(s); - RSTR_UNSET_EMBED_FLAG(s); + str_init_fshared(orig, s, orig->as.heap.aux.fshared); } else { - shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string)); - shared->refcnt = 2; - shared->nofree = !!RSTR_NOFREE_P(orig); - if (!shared->nofree && orig->as.heap.aux.capa > orig->as.heap.len) { - shared->ptr = (char *)mrb_realloc(mrb, orig->as.heap.ptr, len+1); - orig->as.heap.ptr = shared->ptr; - } - else { - shared->ptr = orig->as.heap.ptr; + if (orig->as.heap.aux.capa > orig->as.heap.len) { + orig->as.heap.ptr = (char *)mrb_realloc(mrb, orig->as.heap.ptr, len+1); + orig->as.heap.aux.capa = (mrb_ssize)len; } - orig->as.heap.aux.shared = shared; - RSTR_SET_SHARED_FLAG(orig); - shared->len = len; - s->as.heap.aux.shared = shared; - s->as.heap.ptr = shared->ptr; - s->as.heap.len = len; - RSTR_SET_SHARED_FLAG(s); - RSTR_UNSET_EMBED_FLAG(s); + str_init_shared(mrb, orig, s, NULL); + str_init_shared(mrb, orig, orig, s->as.heap.aux.shared); } } -static mrb_value -byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) +mrb_value +mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { struct RString *orig, *s; orig = mrb_str_ptr(str); - if (RSTR_EMBED_P(orig) || RSTR_LEN(orig) == 0 || len <= RSTRING_EMBED_LEN_MAX) { - s = str_new(mrb, RSTR_PTR(orig)+beg, len); + s = mrb_obj_alloc_string(mrb); + if (RSTR_EMBEDDABLE_P(len)) { + str_init_embed(s, RSTR_PTR(orig)+beg, len); } else { - s = mrb_obj_alloc_string(mrb); - str_make_shared(mrb, orig, s); - s->as.heap.ptr += beg; - s->as.heap.len = len; + str_share(mrb, orig, s); + s->as.heap.ptr += (mrb_ssize)beg; + s->as.heap.len = (mrb_ssize)len; } + RSTR_COPY_ASCII_FLAG(s, orig); return mrb_obj_value(s); } + +static void +str_range_to_bytes(mrb_value str, mrb_int *pos, mrb_int *len) +{ + *pos = chars2bytes(str, 0, *pos); + *len = chars2bytes(str, *pos, *len); +} #ifdef MRB_UTF8_STRING static inline mrb_value str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { - beg = chars2bytes(str, 0, beg); - len = chars2bytes(str, beg, len); - - return byte_subseq(mrb, str, beg, len); + str_range_to_bytes(str, &beg, &len); + return mrb_str_byte_subseq(mrb, str, beg, len); } #else -#define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len) +#define str_subseq(mrb, str, beg, len) mrb_str_byte_subseq(mrb, str, beg, len) #endif -static mrb_value -str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) +mrb_bool +mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp) { - mrb_int clen = RSTRING_CHAR_LEN(str); - - if (len < 0) return mrb_nil_value(); - if (clen == 0) { - len = 0; - } - else if (beg < 0) { - beg = clen + beg; + if (str_len < *begp || *lenp < 0) return FALSE; + if (*begp < 0) { + *begp += str_len; + if (*begp < 0) return FALSE; } - if (beg > clen) return mrb_nil_value(); - if (beg < 0) { - beg += clen; - if (beg < 0) return mrb_nil_value(); - } - if (len > clen - beg) - len = clen - beg; - if (len <= 0) { - len = 0; + if (*lenp > str_len - *begp) + *lenp = str_len - *begp; + if (*lenp <= 0) { + *lenp = 0; } - return str_subseq(mrb, str, beg, len); + return TRUE; +} + +static mrb_value +str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) +{ + return mrb_str_beg_len(RSTRING_CHAR_LEN(str), &beg, &len) ? + str_subseq(mrb, str, beg, len) : mrb_nil_value(); } MRB_API mrb_int @@ -490,44 +664,28 @@ str_index_str(mrb_state *mrb, mrb_value str, mrb_value str2, mrb_int offset) return mrb_str_index(mrb, str, ptr, len, offset); } -static void -check_frozen(mrb_state *mrb, struct RString *s) -{ - if (MRB_FROZEN_P(s)) { - mrb_raise(mrb, E_FROZEN_ERROR, "can't modify frozen string"); - } -} - static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) { - mrb_int len; + size_t len; - check_frozen(mrb, s1); + mrb_check_frozen(mrb, s1); if (s1 == s2) return mrb_obj_value(s1); - s1->flags &= ~MRB_STR_NO_UTF; - s1->flags |= s2->flags&MRB_STR_NO_UTF; - len = RSTR_LEN(s2); + RSTR_COPY_ASCII_FLAG(s1, s2); if (RSTR_SHARED_P(s1)) { str_decref(mrb, s1->as.heap.aux.shared); - RSTR_UNSET_SHARED_FLAG(s1); } else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1) && !RSTR_FSHARED_P(s1) && s1->as.heap.ptr) { mrb_free(mrb, s1->as.heap.ptr); } - RSTR_UNSET_FSHARED_FLAG(s1); - RSTR_UNSET_NOFREE_FLAG(s1); - if (len <= RSTRING_EMBED_LEN_MAX) { - RSTR_UNSET_SHARED_FLAG(s1); - RSTR_UNSET_FSHARED_FLAG(s1); - RSTR_SET_EMBED_FLAG(s1); - memcpy(s1->as.ary, RSTR_PTR(s2), len); - RSTR_SET_EMBED_LEN(s1, len); + len = (size_t)RSTR_LEN(s2); + if (RSTR_EMBEDDABLE_P(len)) { + str_init_embed(s1, RSTR_PTR(s2), len); } else { - str_make_shared(mrb, s2, s1); + str_share(mrb, s2, s1); } return mrb_obj_value(s1); @@ -536,7 +694,7 @@ str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) static mrb_int str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) { - char *s, *sbeg, *t; + const char *s, *sbeg, *t; struct RString *ps = mrb_str_ptr(str); mrb_int len = RSTRING_LEN(sub); @@ -549,11 +707,12 @@ str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) s = RSTR_PTR(ps) + pos; t = RSTRING_PTR(sub); if (len) { + s = char_adjust(sbeg, sbeg + RSTR_LEN(ps), s); while (sbeg <= s) { if (memcmp(s, t, len) == 0) { return (mrb_int)(s - RSTR_PTR(ps)); } - s--; + s = char_backtrack(sbeg, s); } return -1; } @@ -641,65 +800,17 @@ mrb_locale_from_utf8(const char *utf8, int len) #endif MRB_API void -mrb_str_modify(mrb_state *mrb, struct RString *s) +mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s) { - check_frozen(mrb, s); - s->flags &= ~MRB_STR_NO_UTF; - if (RSTR_SHARED_P(s)) { - mrb_shared_string *shared = s->as.heap.aux.shared; + mrb_check_frozen(mrb, s); + str_modify_keep_ascii(mrb, s); +} - if (shared->nofree == 0 && shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { - s->as.heap.ptr = shared->ptr; - s->as.heap.aux.capa = shared->len; - RSTR_PTR(s)[s->as.heap.len] = '\0'; - mrb_free(mrb, shared); - } - else { - char *ptr, *p; - mrb_int len; - - p = RSTR_PTR(s); - len = s->as.heap.len; - 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'; - str_decref(mrb, shared); - } - RSTR_UNSET_SHARED_FLAG(s); - return; - } - if (RSTR_NOFREE_P(s) || RSTR_FSHARED_P(s)) { - char *p = s->as.heap.ptr; - mrb_int len = s->as.heap.len; - - RSTR_UNSET_FSHARED_FLAG(s); - RSTR_UNSET_NOFREE_FLAG(s); - RSTR_UNSET_FSHARED_FLAG(s); - if (len < RSTRING_EMBED_LEN_MAX) { - RSTR_SET_EMBED_FLAG(s); - RSTR_SET_EMBED_LEN(s, len); - } - else { - s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1); - s->as.heap.aux.capa = len; - } - if (p) { - memcpy(RSTR_PTR(s), p, len); - } - RSTR_PTR(s)[len] = '\0'; - return; - } +MRB_API void +mrb_str_modify(mrb_state *mrb, struct RString *s) +{ + mrb_str_modify_keep_ascii(mrb, s); + RSTR_UNSET_ASCII_FLAG(s); } MRB_API mrb_value @@ -728,38 +839,18 @@ mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) { struct RString *s; - if (!mrb_string_p(str0)) { - mrb_raise(mrb, E_TYPE_ERROR, "expected String"); - } - + check_null_byte(mrb, str0); s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0)); - if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); - } return RSTR_PTR(s); } -/* - * call-seq: (Caution! String("abcd") change) - * String("abcdefg") = String("abcd") + String("efg") - * - * Returns a new string object containing a copy of <i>str</i>. - */ MRB_API void mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) { - if (!mrb_string_p(other)) { - other = mrb_str_to_str(mrb, other); - } + other = mrb_obj_as_string(mrb, other); mrb_str_cat_str(mrb, self, other); } -/* - * call-seq: (Caution! String("abcd") remain) - * String("abcdefg") = String("abcd") + String("efg") - * - * Returns a new string object containing a copy of <i>str</i>. - */ MRB_API mrb_value mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) { @@ -777,10 +868,13 @@ mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) /* 15.2.10.5.2 */ /* - * call-seq: (Caution! String("abcd") remain) for stack_argument - * String("abcdefg") = String("abcd") + String("efg") + * call-seq: + * str + other_str -> new_str * - * Returns a new string object containing a copy of <i>str</i>. + * Concatenation---Returns a new <code>String</code> containing + * <i>other_str</i> concatenated to <i>str</i>. + * + * "Hello from " + self.to_s #=> "Hello from main" */ static mrb_value mrb_str_plus_m(mrb_state *mrb, mrb_value self) @@ -834,13 +928,12 @@ mrb_str_times(mrb_state *mrb, mrb_value self) if (times < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } - if (times && MRB_INT_MAX / times < RSTRING_LEN(self)) { + if (times && MRB_SSIZE_MAX / times < RSTRING_LEN(self)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "argument too big"); } len = RSTRING_LEN(self)*times; str2 = str_new(mrb, 0, len); - str_with_class(mrb, str2, self); p = RSTR_PTR(str2); if (len > 0) { n = RSTRING_LEN(self); @@ -852,6 +945,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self) memcpy(p + n, p, len-n); } p[RSTR_LEN(str2)] = '\0'; + RSTR_COPY_ASCII_FLAG(str2, mrb_str_ptr(self)); return mrb_obj_value(str2); } @@ -915,26 +1009,11 @@ mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) static mrb_value mrb_str_cmp_m(mrb_state *mrb, mrb_value str1) { - mrb_value str2; + mrb_value str2 = mrb_get_arg1(mrb); mrb_int result; - mrb_get_args(mrb, "o", &str2); if (!mrb_string_p(str2)) { - if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_s"))) { - return mrb_nil_value(); - } - else if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "<=>"))) { - return mrb_nil_value(); - } - else { - mrb_value tmp = mrb_funcall(mrb, str2, "<=>", 1, str1); - - if (mrb_nil_p(tmp)) return mrb_nil_value(); - if (!mrb_fixnum_p(tmp)) { - return mrb_funcall(mrb, mrb_fixnum_value(0), "-", 1, tmp); - } - result = -mrb_fixnum(tmp); - } + return mrb_nil_value(); } else { result = mrb_str_cmp(mrb, str1, str2); @@ -956,15 +1035,7 @@ str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2) MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2) { - if (mrb_immediate_p(str2)) return FALSE; - if (!mrb_string_p(str2)) { - if (mrb_nil_p(str2)) return FALSE; - if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_str"))) { - return FALSE; - } - str2 = mrb_funcall(mrb, str2, "to_str", 0); - return mrb_equal(mrb, str2, str1); - } + if (!mrb_string_p(str2)) return FALSE; return str_eql(mrb, str1, str2); } @@ -982,54 +1053,26 @@ mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2) static mrb_value mrb_str_equal_m(mrb_state *mrb, mrb_value str1) { - mrb_value str2; - - mrb_get_args(mrb, "o", &str2); + mrb_value str2 = mrb_get_arg1(mrb); return mrb_bool_value(mrb_str_equal(mrb, str1, str2)); } /* ---------------------------------- */ -MRB_API mrb_value -mrb_str_to_str(mrb_state *mrb, mrb_value str) -{ - mrb_value s; - - if (!mrb_string_p(str)) { - s = mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str"); - if (mrb_nil_p(s)) { - s = mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_s"); - } - return s; - } - return str; -} +/* obslete: use RSTRING_PTR() */ MRB_API const char* -mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr) +mrb_string_value_ptr(mrb_state *mrb, mrb_value str) { - mrb_value str = mrb_str_to_str(mrb, ptr); + str = mrb_obj_as_string(mrb, str); return RSTRING_PTR(str); } +/* obslete: use RSTRING_LEN() */ MRB_API mrb_int mrb_string_value_len(mrb_state *mrb, mrb_value ptr) { - mrb_value str = mrb_str_to_str(mrb, ptr); - return RSTRING_LEN(str); -} - -void -mrb_noregexp(mrb_state *mrb, mrb_value self) -{ - mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented"); -} - -void -mrb_regexp_check(mrb_state *mrb, mrb_value obj) -{ - if (mrb_regexp_p(mrb, obj)) { - mrb_noregexp(mrb, obj); - } + mrb_to_str(mrb, ptr); + return RSTRING_LEN(ptr); } MRB_API mrb_value @@ -1038,56 +1081,94 @@ mrb_str_dup(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); struct RString *dup = str_new(mrb, 0, 0); - str_with_class(mrb, dup, str); return str_replace(mrb, dup, s); } -static mrb_value -mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx) -{ - mrb_int idx; +enum str_convert_range { + /* `beg` and `len` are byte unit in `0 ... str.bytesize` */ + STR_BYTE_RANGE_CORRECTED = 1, - mrb_regexp_check(mrb, indx); - switch (mrb_type(indx)) { - case MRB_TT_FIXNUM: - idx = mrb_fixnum(indx); + /* `beg` and `len` are char unit in any range */ + STR_CHAR_RANGE = 2, -num_index: - str = str_substr(mrb, str, idx, 1); - if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); - return str; + /* `beg` and `len` are char unit in `0 ... str.size` */ + STR_CHAR_RANGE_CORRECTED = 3, - case MRB_TT_STRING: - if (str_index_str(mrb, str, indx, 0) != -1) - return mrb_str_dup(mrb, indx); - return mrb_nil_value(); + /* `beg` is out of range */ + STR_OUT_OF_RANGE = -1 +}; + +static enum str_convert_range +str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_int *beg, mrb_int *len) +{ + if (!mrb_undef_p(alen)) { + *beg = mrb_as_int(mrb, indx); + *len = mrb_as_int(mrb, alen); + return STR_CHAR_RANGE; + } + else { + switch (mrb_type(indx)) { + case MRB_TT_INTEGER: + *beg = mrb_integer(indx); + *len = 1; + return STR_CHAR_RANGE; - case MRB_TT_RANGE: - goto range_arg; + case MRB_TT_STRING: + *beg = str_index_str(mrb, str, indx, 0); + if (*beg < 0) { break; } + *len = RSTRING_LEN(indx); + return STR_BYTE_RANGE_CORRECTED; - default: - indx = mrb_Integer(mrb, indx); - if (mrb_nil_p(indx)) { - range_arg: - { - mrb_int beg, len; - - len = RSTRING_CHAR_LEN(str); - switch (mrb_range_beg_len(mrb, indx, &beg, &len, len, TRUE)) { - case 1: - return str_subseq(mrb, str, beg, len); - case 2: - return mrb_nil_value(); + case MRB_TT_RANGE: + goto range_arg; + + default: + indx = mrb_to_int(mrb, indx); + if (mrb_integer_p(indx)) { + *beg = mrb_integer(indx); + *len = 1; + return STR_CHAR_RANGE; + } +range_arg: + *len = RSTRING_CHAR_LEN(str); + switch (mrb_range_beg_len(mrb, indx, beg, len, *len, TRUE)) { + case MRB_RANGE_OK: + return STR_CHAR_RANGE_CORRECTED; + case MRB_RANGE_OUT: + return STR_OUT_OF_RANGE; default: break; - } } - mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum"); + + mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Integer"); + } + } + return STR_OUT_OF_RANGE; +} + +static mrb_value +mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen) +{ + mrb_int beg, len; + + switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) { + case STR_CHAR_RANGE_CORRECTED: + return str_subseq(mrb, str, beg, len); + case STR_CHAR_RANGE: + str = str_substr(mrb, str, beg, len); + if (mrb_undef_p(alen) && !mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); + return str; + case STR_BYTE_RANGE_CORRECTED: + if (mrb_string_p(indx)) { + return mrb_str_dup(mrb, indx); } - idx = mrb_fixnum(indx); - goto num_index; + else { + return mrb_str_byte_subseq(mrb, str, beg, len); + } + case STR_OUT_OF_RANGE: + default: + return mrb_nil_value(); } - return mrb_nil_value(); /* not reached */ } /* 15.2.10.5.6 */ @@ -1097,16 +1178,14 @@ num_index: * str[fixnum] => fixnum or nil * str[fixnum, fixnum] => new_str or nil * str[range] => new_str or nil - * str[regexp] => new_str or nil - * str[regexp, fixnum] => new_str or nil * str[other_str] => new_str or nil * str.slice(fixnum) => fixnum or nil * str.slice(fixnum, fixnum) => new_str or nil * str.slice(range) => new_str or nil * str.slice(other_str) => new_str or nil * - * Element Reference---If passed a single <code>Fixnum</code>, returns the code - * of the character at that position. If passed two <code>Fixnum</code> + * Element Reference---If passed a single <code>Integer</code>, returns the code + * of the character at that position. If passed two <code>Integer</code> * objects, returns a substring starting at the offset given by the first, and * a length given by the second. If given a range, a substring containing * characters at offsets given by the range is returned. In all three cases, if @@ -1134,20 +1213,198 @@ static mrb_value mrb_str_aref_m(mrb_state *mrb, mrb_value str) { mrb_value a1, a2; - mrb_int argc; - argc = mrb_get_args(mrb, "o|o", &a1, &a2); - if (argc == 2) { - mrb_int n1, n2; + if (mrb_get_args(mrb, "o|o", &a1, &a2) == 1) { + a2 = mrb_undef_value(); + } + + return mrb_str_aref(mrb, str, a1, a2); +} + +static mrb_noreturn void +str_out_of_index(mrb_state *mrb, mrb_value index) +{ + mrb_raisef(mrb, E_INDEX_ERROR, "index %v out of string", index); +} - mrb_regexp_check(mrb, a1); - mrb_get_args(mrb, "ii", &n1, &n2); - return str_substr(mrb, str, n1, n2); +static mrb_value +str_replace_partial(mrb_state *mrb, mrb_value src, mrb_int pos, mrb_int end, mrb_value rep) +{ + const mrb_int shrink_threshold = 256; + struct RString *str = mrb_str_ptr(src); + mrb_int len = RSTR_LEN(str); + mrb_int replen, newlen; + char *strp; + + if (end > len) { end = len; } + + if (pos < 0 || pos > len) { + str_out_of_index(mrb, mrb_fixnum_value(pos)); + } + + replen = (mrb_nil_p(rep) ? 0 : RSTRING_LEN(rep)); + newlen = replen + (len - (end - pos)); + + if (newlen >= MRB_SSIZE_MAX || newlen < replen /* overflowed */) { + mrb_raise(mrb, E_RUNTIME_ERROR, "string size too big"); + } + + mrb_str_modify(mrb, str); + + if (len < newlen) { + resize_capa(mrb, str, newlen); + } + + strp = RSTR_PTR(str); + + memmove(strp + newlen - (len - end), strp + end, len - end); + if (!mrb_nil_p(rep)) { + memmove(strp + pos, RSTRING_PTR(rep), replen); } - if (argc != 1) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc)); + RSTR_SET_LEN(str, newlen); + strp[newlen] = '\0'; + + if (len - newlen >= shrink_threshold) { + resize_capa(mrb, str, newlen); } - return mrb_str_aref(mrb, str, a1); + + return src; +} + +#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) + +static mrb_value +str_escape(mrb_state *mrb, mrb_value str, mrb_bool inspect) +{ + const char *p, *pend; + char buf[4]; /* `\x??` or UTF-8 character */ + mrb_value result = mrb_str_new_lit(mrb, "\""); +#ifdef MRB_UTF8_STRING + uint32_t ascii_flag = MRB_STR_ASCII; +#endif + + p = RSTRING_PTR(str); pend = RSTRING_END(str); + for (;p < pend; p++) { + unsigned char c, cc; +#ifdef MRB_UTF8_STRING + if (inspect) { + mrb_int clen = mrb_utf8len(p, pend); + if (clen > 1) { + mrb_int i; + + for (i=0; i<clen; i++) { + buf[i] = p[i]; + } + mrb_str_cat(mrb, result, buf, clen); + p += clen-1; + ascii_flag = 0; + continue; + } + } +#endif + c = *p; + if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) { + buf[0] = '\\'; buf[1] = c; + mrb_str_cat(mrb, result, buf, 2); + continue; + } + if (ISPRINT(c)) { + buf[0] = c; + mrb_str_cat(mrb, result, buf, 1); + continue; + } + switch (c) { + case '\n': cc = 'n'; break; + case '\r': cc = 'r'; break; + case '\t': cc = 't'; break; + case '\f': cc = 'f'; break; + case '\013': cc = 'v'; break; + case '\010': cc = 'b'; break; + case '\007': cc = 'a'; break; + case 033: cc = 'e'; break; + default: cc = 0; break; + } + if (cc) { + buf[0] = '\\'; + buf[1] = (char)cc; + mrb_str_cat(mrb, result, buf, 2); + continue; + } + else { + buf[0] = '\\'; + buf[1] = 'x'; + buf[3] = mrb_digitmap[c % 16]; c /= 16; + buf[2] = mrb_digitmap[c % 16]; + mrb_str_cat(mrb, result, buf, 4); + continue; + } + } + mrb_str_cat_lit(mrb, result, "\""); +#ifdef MRB_UTF8_STRING + if (inspect) { + mrb_str_ptr(str)->flags |= ascii_flag; + mrb_str_ptr(result)->flags |= ascii_flag; + } + else { + RSTR_SET_ASCII_FLAG(mrb_str_ptr(result)); + } +#endif + + return result; +} + +static void +mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_value replace) +{ + mrb_int beg, len, charlen; + + mrb_to_str(mrb, replace); + + switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) { + case STR_OUT_OF_RANGE: + default: + mrb_raise(mrb, E_INDEX_ERROR, "string not matched"); + case STR_CHAR_RANGE: + if (len < 0) { + mrb_raisef(mrb, E_INDEX_ERROR, "negative length %v", alen); + } + charlen = RSTRING_CHAR_LEN(str); + if (beg < 0) { beg += charlen; } + if (beg < 0 || beg > charlen) { str_out_of_index(mrb, indx); } + /* fall through */ + case STR_CHAR_RANGE_CORRECTED: + str_range_to_bytes(str, &beg, &len); + /* fall through */ + case STR_BYTE_RANGE_CORRECTED: + str_replace_partial(mrb, str, beg, beg + len, replace); + } +} + +/* + * call-seq: + * str[fixnum] = replace + * str[fixnum, fixnum] = replace + * str[range] = replace + * str[other_str] = replace + * + * Modify +self+ by replacing the content of +self+. + * The portion of the string affected is determined using the same criteria as +String#[]+. + */ +static mrb_value +mrb_str_aset_m(mrb_state *mrb, mrb_value str) +{ + mrb_value indx, alen, replace; + + switch (mrb_get_args(mrb, "oo|S!", &indx, &alen, &replace)) { + case 2: + replace = alen; + alen = mrb_undef_value(); + break; + case 3: + break; + } + mrb_str_aset(mrb, str, indx, alen, replace); + return str; } /* 15.2.10.5.8 */ @@ -1170,7 +1427,7 @@ mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str) mrb_bool modify = FALSE; struct RString *s = mrb_str_ptr(str); - mrb_str_modify(mrb, s); + mrb_str_modify_keep_ascii(mrb, s); if (RSTR_LEN(s) == 0 || !RSTR_PTR(s)) return mrb_nil_value(); p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s); if (ISLOWER(*p)) { @@ -1229,7 +1486,7 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) struct RString *s = mrb_str_ptr(str); argc = mrb_get_args(mrb, "|S", &rs); - mrb_str_modify(mrb, s); + mrb_str_modify_keep_ascii(mrb, s); len = RSTR_LEN(s); if (argc == 0) { if (len == 0) return mrb_nil_value(); @@ -1291,9 +1548,8 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) * str.chomp(separator="\n") => new_str * * Returns a new <code>String</code> with the given record separator removed - * from the end of <i>str</i> (if present). If <code>$/</code> has not been - * changed from the default Ruby record separator, then <code>chomp</code> also - * removes carriage return characters (that is it will remove <code>\n</code>, + * from the end of <i>str</i> (if present). <code>chomp</code> also removes + * carriage return characters (that is it will remove <code>\n</code>, * <code>\r</code>, and <code>\r\n</code>). * * "hello".chomp #=> "hello" @@ -1328,14 +1584,14 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); - mrb_str_modify(mrb, s); + mrb_str_modify_keep_ascii(mrb, s); if (RSTR_LEN(s) > 0) { mrb_int len; #ifdef MRB_UTF8_STRING const char* t = RSTR_PTR(s), *p = t; const char* e = p + RSTR_LEN(s); while (p<e) { - mrb_int clen = utf8len(p, e); + mrb_int clen = mrb_utf8len(p, e); if (p + clen>=e) break; p += clen; } @@ -1397,7 +1653,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) mrb_bool modify = FALSE; struct RString *s = mrb_str_ptr(str); - mrb_str_modify(mrb, s); + mrb_str_modify_keep_ascii(mrb, s); p = RSTR_PTR(s); pend = RSTR_PTR(s) + RSTR_LEN(s); while (p < pend) { @@ -1461,11 +1717,10 @@ mrb_str_empty_p(mrb_state *mrb, mrb_value self) static mrb_value mrb_str_eql(mrb_state *mrb, mrb_value self) { - mrb_value str2; + mrb_value str2 = mrb_get_arg1(mrb); mrb_bool eql_p; - mrb_get_args(mrb, "o", &str2); - eql_p = (mrb_type(str2) == MRB_TT_STRING) && str_eql(mrb, self, str2); + eql_p = (mrb_string_p(str2)) && str_eql(mrb, self, str2); return mrb_bool_value(eql_p); } @@ -1483,13 +1738,17 @@ 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); - uint64_t key = 0; + uint32_t hash = 0; - while (len--) { - key = key*65599 + *p; - p++; + for(int i = 0; i < len; ++i) { + hash += p[i]; + hash += (hash << 10); + hash ^= (hash >> 6); } - return (uint32_t)(key + (key>>5)); + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; } /* 15.2.10.5.20 */ @@ -1534,77 +1793,40 @@ mrb_str_include(mrb_state *mrb, mrb_value self) /* * call-seq: * str.index(substring [, offset]) => fixnum or nil - * str.index(fixnum [, offset]) => fixnum or nil - * str.index(regexp [, offset]) => fixnum or nil * * Returns the index of the first occurrence of the given - * <i>substring</i>, - * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>. - * Returns - * <code>nil</code> if not found. + * <i>substring</i>. Returns <code>nil</code> if not found. * If the second parameter is present, it * specifies the position in the string to begin the search. * - * "hello".index('e') #=> 1 + * "hello".index('l') #=> 2 * "hello".index('lo') #=> 3 * "hello".index('a') #=> nil - * "hello".index(101) #=> 1(101=0x65='e') - * "hello".index(/[aeiou]/, -3) #=> 4 + * "hello".index('l', -2) #=> 3 */ static mrb_value mrb_str_index_m(mrb_state *mrb, mrb_value str) { - mrb_value *argv; - mrb_int argc; mrb_value sub; - mrb_int pos, clen; + mrb_int pos; - mrb_get_args(mrb, "*!", &argv, &argc); - if (argc == 2) { - mrb_get_args(mrb, "oi", &sub, &pos); - } - else { + if (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) { pos = 0; - if (argc > 0) - sub = argv[0]; - else - sub = mrb_nil_value(); } - mrb_regexp_check(mrb, sub); - clen = RSTRING_CHAR_LEN(str); - if (pos < 0) { + else if (pos < 0) { + mrb_int clen = RSTRING_CHAR_LEN(str); pos += clen; if (pos < 0) { return mrb_nil_value(); } } - if (pos > clen) return mrb_nil_value(); - pos = chars2bytes(str, 0, pos); - - switch (mrb_type(sub)) { - default: { - mrb_value tmp; - - tmp = mrb_check_string_type(mrb, sub); - if (mrb_nil_p(tmp)) { - mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub); - } - sub = tmp; - } - /* fall through */ - case MRB_TT_STRING: - pos = str_index_str(mrb, str, sub, pos); - break; - } + pos = str_index_str_by_char(mrb, str, sub, pos); if (pos == -1) return mrb_nil_value(); - pos = bytes2chars(RSTRING_PTR(str), pos); BYTES_ALIGN_CHECK(pos); return mrb_fixnum_value(pos); } -#define STR_REPLACE_SHARED_MIN 10 - /* 15.2.10.5.24 */ /* 15.2.10.5.28 */ /* @@ -1651,7 +1873,7 @@ mrb_str_init(mrb_state *mrb, mrb_value self) * str.to_sym => symbol * * Returns the <code>Symbol</code> corresponding to <i>str</i>, creating the - * symbol if it did not previously exist. See <code>Symbol#id2name</code>. + * symbol if it did not previously exist. * * "Koala".intern #=> :Koala * s = 'cat'.to_sym #=> :cat @@ -1673,15 +1895,20 @@ mrb_str_intern(mrb_state *mrb, mrb_value self) MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj) { - mrb_value str; - - if (mrb_string_p(obj)) { + switch (mrb_type(obj)) { + case MRB_TT_STRING: return obj; + case MRB_TT_SYMBOL: + return mrb_sym_str(mrb, mrb_symbol(obj)); + case MRB_TT_INTEGER: + return mrb_integer_to_str(mrb, obj, 10); + case MRB_TT_SCLASS: + case MRB_TT_CLASS: + case MRB_TT_MODULE: + return mrb_mod_to_s(mrb, obj); + default: + return mrb_type_convert(mrb, obj, MRB_TT_STRING, MRB_SYM(to_s)); } - str = mrb_funcall(mrb, obj, "to_s", 0); - if (!mrb_string_p(str)) - return mrb_any_to_s(mrb, obj); - return str; } MRB_API mrb_value @@ -1714,16 +1941,16 @@ mrb_ptr_to_str(mrb_state *mrb, void *p) return mrb_obj_value(p_str); } -MRB_API mrb_value -mrb_string_type(mrb_state *mrb, mrb_value str) +static inline void +str_reverse(char *p, char *e) { - return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str"); -} + char c; -MRB_API mrb_value -mrb_check_string_type(mrb_state *mrb, mrb_value str) -{ - return mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str"); + while (p < e) { + c = *p; + *p++ = *e; + *e-- = c; + } } /* 15.2.10.5.30 */ @@ -1736,53 +1963,38 @@ mrb_check_string_type(mrb_state *mrb, mrb_value str) static mrb_value mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) { + struct RString *s = mrb_str_ptr(str); + char *p, *e; + #ifdef MRB_UTF8_STRING mrb_int utf8_len = RSTRING_CHAR_LEN(str); - mrb_int len = RSTRING_LEN(str); - - if (utf8_len == len) goto bytes; - if (utf8_len > 1) { - char *buf; - char *p, *e, *r; - - mrb_str_modify(mrb, mrb_str_ptr(str)); - len = RSTRING_LEN(str); - buf = (char*)mrb_malloc(mrb, (size_t)len); - p = buf; - e = buf + len; - - memcpy(buf, RSTRING_PTR(str), len); - r = RSTRING_PTR(str) + len; + mrb_int len = RSTR_LEN(s); + if (utf8_len < 2) return str; + if (utf8_len < len) { + mrb_str_modify(mrb, s); + p = RSTR_PTR(s); + e = p + RSTR_LEN(s); while (p<e) { - mrb_int clen = utf8len(p, e); - r -= clen; - memcpy(r, p, clen); + mrb_int clen = mrb_utf8len(p, e); + str_reverse(p, p + clen - 1); p += clen; } - mrb_free(mrb, buf); + goto bytes; } - return str; - - bytes: #endif - { - struct RString *s = mrb_str_ptr(str); - char *p, *e; - char c; + if (RSTR_LEN(s) > 1) { mrb_str_modify(mrb, s); - if (RSTR_LEN(s) > 1) { - p = RSTR_PTR(s); - e = p + RSTR_LEN(s) - 1; - while (p < e) { - c = *p; - *p++ = *e; - *e-- = c; - } - } - return str; + goto bytes; } + return str; + + bytes: + p = RSTR_PTR(s); + e = p + RSTR_LEN(s) - 1; + str_reverse(p, e); + return str; } /* ---------------------------------- */ @@ -1806,73 +2018,43 @@ mrb_str_reverse(mrb_state *mrb, mrb_value str) /* 15.2.10.5.31 */ /* * call-seq: - * str.rindex(substring [, fixnum]) => fixnum or nil - * str.rindex(fixnum [, fixnum]) => fixnum or nil - * str.rindex(regexp [, fixnum]) => fixnum or nil + * str.rindex(substring [, offset]) => fixnum or nil * - * Returns the index of the last occurrence of the given <i>substring</i>, - * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>. Returns - * <code>nil</code> if not found. If the second parameter is present, it - * specifies the position in the string to end the search---characters beyond - * this point will not be considered. + * Returns the index of the last occurrence of the given <i>substring</i>. + * Returns <code>nil</code> if not found. If the second parameter is + * present, it specifies the position in the string to end the + * search---characters beyond this point will not be considered. * * "hello".rindex('e') #=> 1 * "hello".rindex('l') #=> 3 * "hello".rindex('a') #=> nil - * "hello".rindex(101) #=> 1 - * "hello".rindex(/[aeiou]/, -2) #=> 1 + * "hello".rindex('l', 2) #=> 2 */ static mrb_value mrb_str_rindex(mrb_state *mrb, mrb_value str) { - mrb_value *argv; - mrb_int argc; mrb_value sub; mrb_int pos, len = RSTRING_CHAR_LEN(str); - mrb_get_args(mrb, "*!", &argv, &argc); - if (argc == 2) { - mrb_get_args(mrb, "oi", &sub, &pos); + if (mrb_get_args(mrb, "S|i", &sub, &pos) == 1) { + pos = len; + } + else { if (pos < 0) { pos += len; if (pos < 0) { - mrb_regexp_check(mrb, sub); return mrb_nil_value(); } } if (pos > len) pos = len; } - else { - pos = len; - if (argc > 0) - sub = argv[0]; - else - sub = mrb_nil_value(); - } pos = chars2bytes(str, 0, pos); - mrb_regexp_check(mrb, sub); - - switch (mrb_type(sub)) { - default: { - mrb_value tmp; - - tmp = mrb_check_string_type(mrb, sub); - if (mrb_nil_p(tmp)) { - mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub); - } - sub = tmp; - } - /* fall through */ - case MRB_TT_STRING: - pos = str_rindex(mrb, str, sub, pos); - if (pos >= 0) { - pos = bytes2chars(RSTRING_PTR(str), pos); - BYTES_ALIGN_CHECK(pos); - return mrb_fixnum_value(pos); - } - break; - - } /* end of switch (TYPE(sub)) */ + pos = str_rindex(mrb, str, sub, pos); + if (pos >= 0) { + pos = bytes2chars(RSTRING_PTR(str), RSTRING_LEN(str), pos); + BYTES_ALIGN_CHECK(pos); + return mrb_fixnum_value(pos); + } return mrb_nil_value(); } @@ -1880,23 +2062,18 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str) /* * call-seq: - * str.split(pattern="\n", [limit]) => anArray + * str.split(separator=nil, [limit]) => anArray * * Divides <i>str</i> into substrings based on a delimiter, returning an array * of these substrings. * - * If <i>pattern</i> is a <code>String</code>, then its contents are used as - * the delimiter when splitting <i>str</i>. If <i>pattern</i> is a single + * If <i>separator</i> is a <code>String</code>, then its contents are used as + * the delimiter when splitting <i>str</i>. If <i>separator</i> is a single * space, <i>str</i> is split on whitespace, with leading whitespace and runs * of contiguous whitespace characters ignored. * - * If <i>pattern</i> is a <code>Regexp</code>, <i>str</i> is divided where the - * pattern matches. Whenever the pattern matches a zero-length string, - * <i>str</i> is split into individual characters. - * - * If <i>pattern</i> is omitted, the value of <code>$;</code> is used. If - * <code>$;</code> is <code>nil</code> (which is the default), <i>str</i> is - * split on whitespace as if ' ' were specified. + * If <i>separator</i> is omitted or <code>nil</code> (which is the default), + * <i>str</i> is split on whitespace as if ' ' were specified. * * If the <i>limit</i> parameter is omitted, trailing null fields are * suppressed. If <i>limit</i> is a positive number, at most that number of @@ -1907,9 +2084,6 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str) * * " now's the time".split #=> ["now's", "the", "time"] * " now's the time".split(' ') #=> ["now's", "the", "time"] - * " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"] - * "hello".split(//) #=> ["h", "e", "l", "l", "o"] - * "hello".split(//, 3) #=> ["h", "e", "llo"] * * "mellow yellow".split("ello") #=> ["m", "w y", "w"] * "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"] @@ -1922,7 +2096,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) { mrb_int argc; mrb_value spat = mrb_nil_value(); - enum {awk, string, regexp} split_type = string; + enum {awk, string} split_type = string; mrb_int i = 0; mrb_int beg; mrb_int end; @@ -1944,16 +2118,11 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) if (argc == 0 || mrb_nil_p(spat)) { split_type = awk; } - else { - if (mrb_string_p(spat)) { - split_type = string; - if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') { - split_type = awk; - } - } - else { - mrb_noregexp(mrb, str); - } + else if (!mrb_string_p(spat)) { + mrb_raise(mrb, E_TYPE_ERROR, "expected String"); + } + else if (RSTRING_LEN(spat) == 1 && RSTRING_PTR(spat)[0] == ' ') { + split_type = awk; } result = mrb_ary_new(mrb); @@ -1979,7 +2148,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) } } else if (ISSPACE(c)) { - mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg)); + mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = TRUE; beg = idx; @@ -1990,7 +2159,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) } } } - else if (split_type == string) { + else { /* split_type == string */ mrb_int str_len = RSTRING_LEN(str); mrb_int pat_len = RSTRING_LEN(spat); mrb_int idx = 0; @@ -2004,22 +2173,19 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) else { end = chars2bytes(str, idx, 1); } - mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end)); + mrb_ary_push(mrb, result, mrb_str_byte_subseq(mrb, str, idx, end)); mrb_gc_arena_restore(mrb, ai); idx += end + pat_len; if (lim_p && lim <= ++i) break; } beg = idx; } - else { - mrb_noregexp(mrb, str); - } if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) { if (RSTRING_LEN(str) == beg) { - tmp = mrb_str_new_empty(mrb, str); + tmp = mrb_str_new(mrb, 0, 0); } else { - tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg); + tmp = mrb_str_byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg); } mrb_ary_push(mrb, result, tmp); } @@ -2033,14 +2199,14 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) return result; } -MRB_API mrb_value -mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base, int badcheck) +static mrb_value +mrb_str_len_to_inum(mrb_state *mrb, const char *str, size_t len, mrb_int base, int badcheck) { const char *p = str; const char *pend = str + len; char sign = 1; int c; - uint64_t n = 0; + mrb_int n = 0; mrb_int val; #define conv_digit(c) \ @@ -2117,7 +2283,7 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base, break; default: if (base < 2 || 36 < base) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base)); + mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %i", base); } break; } /* end of switch (base) { */ @@ -2144,7 +2310,7 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base, if (*(p - 1) == '0') p--; } - if (p == pend) { + if (p == pend || *p == '_') { if (badcheck) goto bad; return mrb_fixnum_value(0); } @@ -2167,62 +2333,65 @@ mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base, if (c < 0 || c >= base) { break; } - n *= base; - n += c; - if (n > (uint64_t)MRB_INT_MAX + (sign ? 0 : 1)) { -#ifndef MRB_WITHOUT_FLOAT - if (base == 10) { - return mrb_float_value(mrb, mrb_str_to_dbl(mrb, mrb_str_new(mrb, str, len), badcheck)); - } - else -#endif - { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", - mrb_str_new(mrb, str, pend-str)); + if (mrb_int_mul_overflow(n, base, &n)) goto overflow; + if (MRB_INT_MAX - c < n) { + if (sign == 0 && MRB_INT_MAX - n == c - 1) { + n = MRB_INT_MIN; + sign = 1; + break; } + overflow: + mrb_raisef(mrb, E_RANGE_ERROR, "string (%l) too big for integer", str, pend-str); } + n += c; } val = (mrb_int)n; if (badcheck) { - if (p == str) goto bad; /* no number */ + if (p == str) goto bad; /* no number */ + if (*(p - 1) == '_') goto bad; /* trailing '_' */ while (p<pend && ISSPACE(*p)) p++; - if (p<pend) goto bad; /* trailing garbage */ + if (p<pend) goto bad; /* trailing garbage */ } - return mrb_fixnum_value(sign ? val : -val); + return mrb_int_value(mrb, sign ? val : -val); nullbyte: mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); /* not reached */ bad: - mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)", - mrb_inspect(mrb, mrb_str_new(mrb, str, pend-str))); + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%!l)", str, pend-str); /* not reached */ return mrb_fixnum_value(0); } -MRB_API mrb_value -mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) -{ - return mrb_str_len_to_inum(mrb, str, strlen(str), base, badcheck); -} - +/* obslete: use RSTRING_CSTR() or mrb_string_cstr() */ MRB_API const char* mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr) { - mrb_value str = mrb_str_to_str(mrb, *ptr); - struct RString *ps = mrb_str_ptr(str); - mrb_int len = mrb_str_strlen(mrb, ps); - char *p = RSTR_PTR(ps); + struct RString *ps; + const char *p; + mrb_int len; - if (!p || p[len] != '\0') { - if (MRB_FROZEN_P(ps)) { - *ptr = str = mrb_str_dup(mrb, str); - ps = mrb_str_ptr(str); - } - mrb_str_modify(mrb, ps); - return RSTR_PTR(ps); + check_null_byte(mrb, *ptr); + ps = mrb_str_ptr(*ptr); + p = RSTR_PTR(ps); + len = RSTR_LEN(ps); + if (p[len] == '\0') { + return p; } - return p; + + /* + * Even after str_modify_keep_ascii(), NULL termination is not ensured if + * RSTR_SET_LEN() is used explicitly (e.g. String#delete_suffix!). + */ + str_modify_keep_ascii(mrb, ps); + RSTR_PTR(ps)[len] = '\0'; + return RSTR_PTR(ps); +} + +MRB_API const char* +mrb_string_cstr(mrb_state *mrb, mrb_value str) +{ + return mrb_string_value_cstr(mrb, &str); } MRB_API mrb_value @@ -2231,7 +2400,8 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) const char *s; mrb_int len; - s = mrb_string_value_ptr(mrb, str); + mrb_to_str(mrb, str); + s = RSTRING_PTR(str); len = RSTRING_LEN(str); return mrb_str_len_to_inum(mrb, s, len, base, badcheck); } @@ -2263,72 +2433,98 @@ mrb_str_to_i(mrb_state *mrb, mrb_value self) mrb_int base = 10; mrb_get_args(mrb, "|i", &base); - if (base < 0) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base)); + if (base < 0 || 36 < base) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %i", base); } return mrb_str_to_inum(mrb, self, base, FALSE); } -#ifndef MRB_WITHOUT_FLOAT -MRB_API double -mrb_cstr_to_dbl(mrb_state *mrb, const char * p, mrb_bool badcheck) +#ifndef MRB_NO_FLOAT +static double +mrb_str_len_to_dbl(mrb_state *mrb, const char *s, size_t len, mrb_bool badcheck) { + char buf[DBL_DIG * 4 + 20]; + const char *p = s, *p2; + const char *pend = p + len; char *end; - char buf[DBL_DIG * 4 + 10]; + char *n; + char prev = 0; double d; - - enum {max_width = 20}; + mrb_bool dot = FALSE; if (!p) return 0.0; - while (ISSPACE(*p)) p++; - - if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { - return 0.0; + while (p<pend && ISSPACE(*p)) p++; + p2 = p; + + if (pend - p > 2 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { + mrb_value x; + + if (!badcheck) return 0.0; + x = mrb_str_len_to_inum(mrb, p, pend-p, 0, badcheck); + if (mrb_integer_p(x)) + d = (double)mrb_integer(x); + else /* if (mrb_float_p(x)) */ + d = mrb_float(x); + return d; + } + while (p < pend) { + if (!*p) { + if (badcheck) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte"); + /* not reached */ + } + pend = p; + p = p2; + goto nocopy; + } + if (!badcheck && *p == ' ') { + pend = p; + p = p2; + goto nocopy; + } + if (*p == '_') break; + p++; } + p = p2; + n = buf; + while (p < pend) { + char c = *p++; + if (c == '.') dot = TRUE; + if (c == '_') { + /* remove an underscore between digits */ + if (n == buf || !ISDIGIT(prev) || p == pend) { + if (badcheck) goto bad; + break; + } + } + else if (badcheck && prev == '_' && !ISDIGIT(c)) goto bad; + else { + const char *bend = buf+sizeof(buf)-1; + if (n==bend) { /* buffer overflow */ + if (dot) break; /* cut off remaining fractions */ + return INFINITY; + } + *n++ = c; + } + prev = c; + } + *n = '\0'; + p = buf; + pend = n; +nocopy: d = mrb_float_read(p, &end); if (p == end) { if (badcheck) { bad: - mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%S)", mrb_str_new_cstr(mrb, p)); + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%!s)", s); /* not reached */ } return d; } - if (*end) { - char *n = buf; - char *e = buf + sizeof(buf) - 1; - char prev = 0; - - while (p < end && n < e) prev = *n++ = *p++; - while (*p) { - if (*p == '_') { - /* remove underscores between digits */ - if (badcheck) { - if (n == buf || !ISDIGIT(prev)) goto bad; - ++p; - if (!ISDIGIT(*p)) goto bad; - } - else { - while (*++p == '_'); - continue; - } - } - prev = *p++; - if (n < e) *n++ = prev; - } - *n = '\0'; - p = buf; - - if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { - return 0.0; - } - - d = mrb_float_read(p, &end); - if (badcheck) { - if (!end || p == end) goto bad; - while (*end && ISSPACE(*end)) end++; - if (*end) goto bad; - } + if (badcheck) { + if (!end || p == end) goto bad; + while (end<pend && ISSPACE(*end)) end++; + if (end<pend) goto bad; } return d; } @@ -2336,22 +2532,7 @@ bad: MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck) { - char *s; - mrb_int len; - - str = mrb_str_to_str(mrb, str); - s = RSTRING_PTR(str); - len = RSTRING_LEN(str); - if (s) { - if (badcheck && memchr(s, '\0', len)) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string for Float contains null byte"); - } - if (s[len]) { /* no sentinel somehow */ - struct RString *temp_str = str_new(mrb, s, len); - s = RSTR_PTR(temp_str); - } - } - return mrb_cstr_to_dbl(mrb, s, badcheck); + return mrb_str_len_to_dbl(mrb, RSTRING_PTR(str), RSTRING_LEN(str), badcheck); } /* 15.2.10.5.39 */ @@ -2360,7 +2541,7 @@ mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck) * str.to_f => float * * Returns the result of interpreting leading characters in <i>str</i> as a - * floating point number. Extraneous characters past the end of a valid number + * floating-point number. Extraneous characters past the end of a valid number * are ignored. If there is not a valid number at the start of <i>str</i>, * <code>0.0</code> is returned. This method never raises an exception. * @@ -2379,7 +2560,6 @@ mrb_str_to_f(mrb_state *mrb, mrb_value self) /* * call-seq: * str.to_s => str - * str.to_str => str * * Returns the receiver. */ @@ -2407,7 +2587,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str) char *p, *pend; mrb_bool modify = FALSE; - mrb_str_modify(mrb, s); + mrb_str_modify_keep_ascii(mrb, s); p = RSTRING_PTR(str); pend = RSTRING_END(str); while (p < pend) { @@ -2443,8 +2623,6 @@ mrb_str_upcase(mrb_state *mrb, mrb_value self) return str; } -#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) - /* * call-seq: * str.dump -> new_str @@ -2455,113 +2633,7 @@ mrb_str_upcase(mrb_state *mrb, mrb_value self) mrb_value mrb_str_dump(mrb_state *mrb, mrb_value str) { - mrb_int len; - const char *p, *pend; - char *q; - struct RString *result; - - len = 2; /* "" */ - p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); - while (p < pend) { - unsigned char c = *p++; - switch (c) { - case '"': case '\\': - case '\n': case '\r': - case '\t': case '\f': - case '\013': case '\010': case '\007': case '\033': - len += 2; - break; - - case '#': - len += IS_EVSTR(p, pend) ? 2 : 1; - break; - - default: - if (ISPRINT(c)) { - len++; - } - else { - len += 4; /* \NNN */ - } - break; - } - } - - result = str_new(mrb, 0, len); - str_with_class(mrb, result, str); - p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); - q = RSTR_PTR(result); - *q++ = '"'; - while (p < pend) { - unsigned char c = *p++; - - switch (c) { - case '"': - case '\\': - *q++ = '\\'; - *q++ = c; - break; - - case '\n': - *q++ = '\\'; - *q++ = 'n'; - break; - - case '\r': - *q++ = '\\'; - *q++ = 'r'; - break; - - case '\t': - *q++ = '\\'; - *q++ = 't'; - break; - - case '\f': - *q++ = '\\'; - *q++ = 'f'; - break; - - case '\013': - *q++ = '\\'; - *q++ = 'v'; - break; - - case '\010': - *q++ = '\\'; - *q++ = 'b'; - break; - - case '\007': - *q++ = '\\'; - *q++ = 'a'; - break; - - case '\033': - *q++ = '\\'; - *q++ = 'e'; - break; - - case '#': - if (IS_EVSTR(p, pend)) *q++ = '\\'; - *q++ = '#'; - break; - - default: - if (ISPRINT(c)) { - *q++ = c; - } - else { - *q++ = '\\'; - *q++ = 'x'; - q[1] = mrb_digitmap[c % 16]; c /= 16; - q[0] = mrb_digitmap[c % 16]; - q += 2; - } - } - } - *q = '"'; - return mrb_obj_value(result); + return str_escape(mrb, str, FALSE); } MRB_API mrb_value @@ -2580,21 +2652,21 @@ mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) capa = RSTR_CAPA(s); total = RSTR_LEN(s)+len; - if (total >= MRB_INT_MAX) { + if (total >= MRB_SSIZE_MAX) { size_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } if (capa <= total) { if (capa == 0) capa = 1; while (capa <= total) { - if (capa <= MRB_INT_MAX / 2) { + if (capa <= MRB_SSIZE_MAX / 2) { capa *= 2; } else { capa = total+1; } } - if (capa <= total || capa > MRB_INT_MAX) { + if (capa <= total || capa > MRB_SSIZE_MAX) { goto size_error; } resize_capa(mrb, s, capa); @@ -2603,7 +2675,7 @@ mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) ptr = RSTR_PTR(s) + off; } memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len); - mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX); + mrb_assert_int_fit(size_t, total, mrb_ssize, MRB_SSIZE_MAX); RSTR_SET_LEN(s, total); RSTR_PTR(s)[total] = '\0'; /* sentinel */ return str; @@ -2612,7 +2684,7 @@ mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) MRB_API mrb_value mrb_str_cat_cstr(mrb_state *mrb, mrb_value str, const char *ptr) { - return mrb_str_cat(mrb, str, ptr, strlen(ptr)); + return mrb_str_cat(mrb, str, ptr, ptr ? strlen(ptr) : 0); } MRB_API mrb_value @@ -2627,12 +2699,10 @@ mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2) MRB_API mrb_value mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) { - str2 = mrb_str_to_str(mrb, str2); + mrb_to_str(mrb, str2); return mrb_str_cat_str(mrb, str1, str2); } -#define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */ - /* * call-seq: * str.inspect -> string @@ -2647,68 +2717,7 @@ mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str) { - const char *p, *pend; - char buf[CHAR_ESC_LEN + 1]; - mrb_value result = mrb_str_new_lit(mrb, "\""); - - p = RSTRING_PTR(str); pend = RSTRING_END(str); - for (;p < pend; p++) { - unsigned char c, cc; -#ifdef MRB_UTF8_STRING - mrb_int clen; - - clen = utf8len(p, pend); - if (clen > 1) { - mrb_int i; - - for (i=0; i<clen; i++) { - buf[i] = p[i]; - } - mrb_str_cat(mrb, result, buf, clen); - p += clen-1; - continue; - } -#endif - c = *p; - if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) { - buf[0] = '\\'; buf[1] = c; - mrb_str_cat(mrb, result, buf, 2); - continue; - } - if (ISPRINT(c)) { - buf[0] = c; - mrb_str_cat(mrb, result, buf, 1); - continue; - } - switch (c) { - case '\n': cc = 'n'; break; - case '\r': cc = 'r'; break; - case '\t': cc = 't'; break; - case '\f': cc = 'f'; break; - case '\013': cc = 'v'; break; - case '\010': cc = 'b'; break; - case '\007': cc = 'a'; break; - case 033: cc = 'e'; break; - default: cc = 0; break; - } - if (cc) { - buf[0] = '\\'; - buf[1] = (char)cc; - mrb_str_cat(mrb, result, buf, 2); - continue; - } - else { - buf[0] = '\\'; - buf[1] = 'x'; - buf[3] = mrb_digitmap[c % 16]; c /= 16; - buf[2] = mrb_digitmap[c % 16]; - mrb_str_cat(mrb, result, buf, 4); - continue; - } - } - mrb_str_cat_lit(mrb, result, "\""); - - return result; + return str_escape(mrb, str, TRUE); } /* @@ -2734,13 +2743,119 @@ mrb_str_bytes(mrb_state *mrb, mrb_value str) return a; } +/* + * call-seq: + * str.getbyte(index) -> 0 .. 255 + * + * returns the <i>index</i>th byte as an integer. + */ +static mrb_value +mrb_str_getbyte(mrb_state *mrb, mrb_value str) +{ + mrb_int pos; + mrb_get_args(mrb, "i", &pos); + + if (pos < 0) + pos += RSTRING_LEN(str); + if (pos < 0 || RSTRING_LEN(str) <= pos) + return mrb_nil_value(); + + return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]); +} + +/* + * call-seq: + * str.setbyte(index, integer) -> integer + * + * modifies the <i>index</i>th byte as <i>integer</i>. + */ +static mrb_value +mrb_str_setbyte(mrb_state *mrb, mrb_value str) +{ + mrb_int pos, byte; + mrb_int len; + + mrb_get_args(mrb, "ii", &pos, &byte); + + len = RSTRING_LEN(str); + if (pos < -len || len <= pos) + mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of string", pos); + if (pos < 0) + pos += len; + + mrb_str_modify(mrb, mrb_str_ptr(str)); + byte &= 0xff; + RSTRING_PTR(str)[pos] = (unsigned char)byte; + return mrb_fixnum_value((unsigned char)byte); +} + +/* + * call-seq: + * str.byteslice(integer) -> new_str or nil + * str.byteslice(integer, integer) -> new_str or nil + * str.byteslice(range) -> new_str or nil + * + * Byte Reference---If passed a single Integer, returns a + * substring of one byte at that position. If passed two Integer + * objects, returns a substring starting at the offset given by the first, and + * a length given by the second. If given a Range, a substring containing + * bytes at offsets given by the range is returned. In all three cases, if + * an offset is negative, it is counted from the end of <i>str</i>. Returns + * <code>nil</code> if the initial offset falls outside the string, the length + * is negative, or the beginning of the range is greater than the end. + * The encoding of the resulted string keeps original encoding. + * + * "hello".byteslice(1) #=> "e" + * "hello".byteslice(-1) #=> "o" + * "hello".byteslice(1, 2) #=> "el" + * "\x80\u3042".byteslice(1, 3) #=> "\u3042" + * "\x03\u3042\xff".byteslice(1..3) #=> "\u3042" + */ +static mrb_value +mrb_str_byteslice(mrb_state *mrb, mrb_value str) +{ + mrb_value a1; + mrb_int str_len = RSTRING_LEN(str), beg, len; + mrb_bool empty = TRUE; + + len = mrb_get_argc(mrb); + switch (len) { + case 2: + mrb_get_args(mrb, "ii", &beg, &len); + break; + case 1: + a1 = mrb_get_arg1(mrb); + if (mrb_range_p(a1)) { + if (mrb_range_beg_len(mrb, a1, &beg, &len, str_len, TRUE) != MRB_RANGE_OK) { + return mrb_nil_value(); + } + } + else { + beg = mrb_integer(mrb_to_int(mrb, a1)); + len = 1; + empty = FALSE; + } + break; + default: + mrb_argnum_error(mrb, len, 1, 2); + break; + } + if (mrb_str_beg_len(str_len, &beg, &len) && (empty || len != 0)) { + return mrb_str_byte_subseq(mrb, str, beg, len); + } + else { + return mrb_nil_value(); + } +} + /* ---------------------------*/ void mrb_init_string(mrb_state *mrb) { struct RClass *s; - mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string"); + mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << MRB_STR_EMBED_LEN_BIT), + "pointer size too big for embedded string"); mrb->string_class = s = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */ MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); @@ -2752,6 +2867,7 @@ mrb_init_string(mrb_state *mrb) mrb_define_method(mrb, s, "+", mrb_str_plus_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.4 */ mrb_define_method(mrb, s, "*", mrb_str_times, MRB_ARGS_REQ(1)); /* 15.2.10.5.5 */ mrb_define_method(mrb, s, "[]", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.6 */ + mrb_define_method(mrb, s, "[]=", mrb_str_aset_m, MRB_ARGS_ANY()); mrb_define_method(mrb, s, "capitalize", mrb_str_capitalize, MRB_ARGS_NONE()); /* 15.2.10.5.7 */ mrb_define_method(mrb, s, "capitalize!", mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */ mrb_define_method(mrb, s, "chomp", mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */ @@ -2765,7 +2881,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_m, MRB_ARGS_ANY()); /* 15.2.10.5.22 */ + mrb_define_method(mrb, s, "index", mrb_str_index_m, MRB_ARGS_ARG(1,1)); /* 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 */ @@ -2778,7 +2894,7 @@ mrb_init_string(mrb_state *mrb) mrb_define_method(mrb, s, "slice", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.34 */ mrb_define_method(mrb, s, "split", mrb_str_split_m, MRB_ARGS_ANY()); /* 15.2.10.5.35 */ -#ifndef MRB_WITHOUT_FLOAT +#ifndef MRB_NO_FLOAT mrb_define_method(mrb, s, "to_f", mrb_str_to_f, MRB_ARGS_NONE()); /* 15.2.10.5.38 */ #endif mrb_define_method(mrb, s, "to_i", mrb_str_to_i, MRB_ARGS_ANY()); /* 15.2.10.5.39 */ @@ -2789,251 +2905,8 @@ mrb_init_string(mrb_state *mrb) mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.43 */ mrb_define_method(mrb, s, "inspect", mrb_str_inspect, MRB_ARGS_NONE()); /* 15.2.10.5.46(x) */ mrb_define_method(mrb, s, "bytes", mrb_str_bytes, MRB_ARGS_NONE()); -} - -#ifndef MRB_WITHOUT_FLOAT -/* - * Source code for the "strtod" library procedure. - * - * Copyright (c) 1988-1993 The Regents of the University of California. - * Copyright (c) 1994 Sun Microsystems, Inc. - * - * Permission to use, copy, modify, and distribute this - * software and its documentation for any purpose and without - * fee is hereby granted, provided that the above copyright - * notice appear in all copies. The University of California - * makes no representations about the suitability of this - * software for any purpose. It is provided "as is" without - * express or implied warranty. - * - * RCS: @(#) $Id: strtod.c 11708 2007-02-12 23:01:19Z shyouhei $ - */ - -#include <ctype.h> -#include <errno.h> - -static const int maxExponent = 511; /* Largest possible base 10 exponent. Any - * exponent larger than this will already - * produce underflow or overflow, so there's - * no need to worry about additional digits. - */ -static const double powersOf10[] = {/* Table giving binary powers of 10. Entry */ - 10., /* is 10^2^i. Used to convert decimal */ - 100., /* exponents into floating-point numbers. */ - 1.0e4, - 1.0e8, - 1.0e16, - 1.0e32, - 1.0e64, - 1.0e128, - 1.0e256 -}; - -MRB_API double -mrb_float_read(const char *string, char **endPtr) -/* const char *string; A decimal ASCII floating-point number, - * optionally preceded by white space. - * Must have form "-I.FE-X", where I is the - * integer part of the mantissa, F is the - * fractional part of the mantissa, and X - * is the exponent. Either of the signs - * may be "+", "-", or omitted. Either I - * or F may be omitted, or both. The decimal - * point isn't necessary unless F is present. - * The "E" may actually be an "e". E and X - * may both be omitted (but not just one). - */ -/* char **endPtr; If non-NULL, store terminating character's - * address here. */ -{ - int sign, expSign = FALSE; - double fraction, dblExp; - const double *d; - const char *p; - int c; - int exp = 0; /* Exponent read from "EX" field. */ - int fracExp = 0; /* Exponent that derives from the fractional - * part. Under normal circumstatnces, it is - * the negative of the number of digits in F. - * However, if I is very long, the last digits - * of I get dropped (otherwise a long I with a - * large negative exponent could cause an - * unnecessary overflow on I alone). In this - * case, fracExp is incremented one for each - * dropped digit. */ - int mantSize; /* Number of digits in mantissa. */ - int decPt; /* Number of mantissa digits BEFORE decimal - * point. */ - const char *pExp; /* Temporarily holds location of exponent - * in string. */ - - /* - * Strip off leading blanks and check for a sign. - */ - - p = string; - while (isspace(*p)) { - p += 1; - } - if (*p == '-') { - sign = TRUE; - p += 1; - } - else { - if (*p == '+') { - p += 1; - } - sign = FALSE; - } - - /* - * Count the number of digits in the mantissa (including the decimal - * point), and also locate the decimal point. - */ - - decPt = -1; - for (mantSize = 0; ; mantSize += 1) - { - c = *p; - if (!isdigit(c)) { - if ((c != '.') || (decPt >= 0)) { - break; - } - decPt = mantSize; - } - p += 1; - } - - /* - * Now suck up the digits in the mantissa. Use two integers to - * collect 9 digits each (this is faster than using floating-point). - * If the mantissa has more than 18 digits, ignore the extras, since - * they can't affect the value anyway. - */ - - pExp = p; - p -= mantSize; - if (decPt < 0) { - decPt = mantSize; - } - else { - mantSize -= 1; /* One of the digits was the point. */ - } - if (mantSize > 18) { - if (decPt - 18 > 29999) { - fracExp = 29999; - } - else { - fracExp = decPt - 18; - } - mantSize = 18; - } - else { - fracExp = decPt - mantSize; - } - if (mantSize == 0) { - fraction = 0.0; - p = string; - goto done; - } - else { - int frac1, frac2; - frac1 = 0; - for ( ; mantSize > 9; mantSize -= 1) - { - c = *p; - p += 1; - if (c == '.') { - c = *p; - p += 1; - } - frac1 = 10*frac1 + (c - '0'); - } - frac2 = 0; - for (; mantSize > 0; mantSize -= 1) - { - c = *p; - p += 1; - if (c == '.') { - c = *p; - p += 1; - } - frac2 = 10*frac2 + (c - '0'); - } - fraction = (1.0e9 * frac1) + frac2; - } - - /* - * Skim off the exponent. - */ - - p = pExp; - if ((*p == 'E') || (*p == 'e')) { - p += 1; - if (*p == '-') { - expSign = TRUE; - p += 1; - } - else { - if (*p == '+') { - p += 1; - } - expSign = FALSE; - } - while (isdigit(*p)) { - exp = exp * 10 + (*p - '0'); - if (exp > 19999) { - exp = 19999; - } - p += 1; - } - } - if (expSign) { - exp = fracExp - exp; - } - else { - exp = fracExp + exp; - } - - /* - * Generate a floating-point number that represents the exponent. - * Do this by processing the exponent one bit at a time to combine - * many powers of 2 of 10. Then combine the exponent with the - * fraction. - */ - - if (exp < 0) { - expSign = TRUE; - exp = -exp; - } - else { - expSign = FALSE; - } - if (exp > maxExponent) { - exp = maxExponent; - errno = ERANGE; - } - dblExp = 1.0; - for (d = powersOf10; exp != 0; exp >>= 1, d += 1) { - if (exp & 01) { - dblExp *= *d; - } - } - if (expSign) { - fraction /= dblExp; - } - else { - fraction *= dblExp; - } - -done: - if (endPtr != NULL) { - *endPtr = (char *) p; - } - if (sign) { - return -fraction; - } - return fraction; + mrb_define_method(mrb, s, "getbyte", mrb_str_getbyte, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, s, "setbyte", mrb_str_setbyte, MRB_ARGS_REQ(2)); + mrb_define_method(mrb, s, "byteslice", mrb_str_byteslice, MRB_ARGS_ARG(1,1)); } -#endif diff --git a/src/symbol.c b/src/symbol.c index 6b4c7200c..007b8c885 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -11,65 +11,195 @@ #include <mruby/string.h> #include <mruby/dump.h> #include <mruby/class.h> +#include <mruby/presym.h> + +#ifndef MRB_NO_PRESYM + +#ifndef MRB_PRESYM_SCANNING +/* const uint16_t presym_length_table[] */ +/* const char * const presym_name_table[] */ +# include <mruby/presym/table.h> +#endif + +static mrb_sym +presym_find(const char *name, size_t len) +{ + if (presym_length_table[MRB_PRESYM_MAX-1] < len) return 0; + + mrb_sym start, idx, presym_size = MRB_PRESYM_MAX; + int cmp; + for (start = 0; presym_size != 0; presym_size/=2) { + idx = start+presym_size/2; + cmp = (int)len-(int)presym_length_table[idx]; + if (cmp == 0) { + cmp = memcmp(name, presym_name_table[idx], len); + if (cmp == 0) return idx+1; + } + if (0 < cmp) { + start = ++idx; + --presym_size; + } + } + return 0; +} + +static const char* +presym_sym2name(mrb_sym sym, mrb_int *lenp) +{ + if (sym > MRB_PRESYM_MAX) return NULL; + if (lenp) *lenp = presym_length_table[sym-1]; + return presym_name_table[sym-1]; +} + +#endif /* MRB_NO_PRESYM */ /* ------------------------------------------------------ */ typedef struct symbol_name { mrb_bool lit : 1; + uint8_t prev; uint16_t len; const char *name; } symbol_name; -static inline khint_t -sym_hash_func(mrb_state *mrb, mrb_sym s) +static void +sym_validate_len(mrb_state *mrb, size_t len) +{ + if (len >= UINT16_MAX) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long"); + } +} + +#ifdef MRB_USE_ALL_SYMBOLS +# define SYMBOL_INLINE_P(sym) FALSE +# define sym_inline_pack(name, len) 0 +# define sym_inline_unpack(sym, buf, lenp) NULL +#else +# define SYMBOL_INLINE_P(sym) ((sym) >= (1<<24)) + +static const char pack_table[] = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +static mrb_sym +sym_inline_pack(const char *name, size_t len) { - khint_t h = 0; - size_t i, len = mrb->symtbl[s].len; - const char *p = mrb->symtbl[s].name; + const size_t pack_length_max = 5; + + char c; + const char *p; + size_t i; + mrb_sym sym = 0; + if (len > pack_length_max) return 0; /* too long */ + if (len == 0) return 0; /* empty string */ for (i=0; i<len; i++) { - h = (h << 5) - h + *p++; + uint32_t bits; + + c = name[i]; + if (c == 0) return 0; /* NUL in name */ + p = strchr(pack_table, (int)c); + if (p == 0) return 0; /* non alnum char */ + bits = (uint32_t)(p - pack_table)+1; + sym |= bits<<(24-i*6); } - return h; + mrb_assert(SYMBOL_INLINE_P(sym)); + return sym; } -#define sym_hash_equal(mrb,a, b) (mrb->symtbl[a].len == mrb->symtbl[b].len && memcmp(mrb->symtbl[a].name, mrb->symtbl[b].name, mrb->symtbl[a].len) == 0) - -KHASH_DECLARE(n2s, mrb_sym, mrb_sym, FALSE) -KHASH_DEFINE (n2s, mrb_sym, mrb_sym, FALSE, sym_hash_func, sym_hash_equal) -/* ------------------------------------------------------ */ -static void -sym_validate_len(mrb_state *mrb, size_t len) +static const char* +sym_inline_unpack(mrb_sym sym, char *buf, mrb_int *lenp) { - if (len >= RITE_LV_NULL_MARK) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long"); + int i; + + mrb_assert(SYMBOL_INLINE_P(sym)); + + for (i=0; i<5; i++) { + uint32_t bits = sym>>(24-i*6) & 0x3f; + if (bits == 0) break; + buf[i] = pack_table[bits-1];; } + buf[i] = '\0'; + if (lenp) *lenp = i; + return buf; +} +#endif + +static uint8_t +symhash(const char *key, size_t len) +{ + uint32_t hash, i; + + for(hash = i = 0; i < len; ++i) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash & 0xff; +} + +static mrb_sym +find_symbol(mrb_state *mrb, const char *name, size_t len, uint8_t *hashp) +{ + mrb_sym i; + symbol_name *sname; + uint8_t hash; + +#ifndef MRB_NO_PRESYM + /* presym */ + i = presym_find(name, len); + if (i > 0) return i; +#endif + + /* inline symbol */ + i = sym_inline_pack(name, len); + if (i > 0) return i; + + hash = symhash(name, len); + if (hashp) *hashp = hash; + + i = mrb->symhash[hash]; + if (i == 0) return 0; + do { + sname = &mrb->symtbl[i]; + if (sname->len == len && memcmp(sname->name, name, len) == 0) { + return (i+MRB_PRESYM_MAX); + } + if (sname->prev == 0xff) { + i -= 0xff; + sname = &mrb->symtbl[i]; + while (mrb->symtbl < sname) { + if (sname->len == len && memcmp(sname->name, name, len) == 0) { + return (mrb_sym)((sname - mrb->symtbl)+MRB_PRESYM_MAX); + } + sname--; + } + return 0; + } + i -= sname->prev; + } while (sname->prev > 0); + return 0; } static mrb_sym sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit) { - khash_t(n2s) *h = mrb->name2sym; - symbol_name *sname = mrb->symtbl; /* symtbl[0] for working memory */ - khiter_t k; mrb_sym sym; - char *p; + symbol_name *sname; + uint8_t hash; sym_validate_len(mrb, len); - if (sname) { - sname->lit = lit; - sname->len = (uint16_t)len; - sname->name = name; - k = kh_get(n2s, mrb, h, 0); - if (k != kh_end(h)) - return kh_key(h, k); - } + sym = find_symbol(mrb, name, len, &hash); + if (sym > 0) return sym; /* registering a new symbol */ - sym = ++mrb->symidx; + sym = mrb->symidx + 1; if (mrb->symcapa < sym) { - if (mrb->symcapa == 0) mrb->symcapa = 100; - else mrb->symcapa = (size_t)(mrb->symcapa * 6 / 5); - mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(mrb->symcapa+1)); + size_t symcapa = mrb->symcapa; + if (symcapa == 0) symcapa = 100; + else symcapa = (size_t)(symcapa * 6 / 5); + mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(symcapa+1)); + mrb->symcapa = symcapa; } sname = &mrb->symtbl[sym]; sname->len = (uint16_t)len; @@ -78,15 +208,25 @@ sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit) sname->lit = TRUE; } else { - p = (char *)mrb_malloc(mrb, len+1); + char *p = (char *)mrb_malloc(mrb, len+1); memcpy(p, name, len); p[len] = 0; sname->name = (const char*)p; sname->lit = FALSE; } - kh_put(n2s, mrb, h, sym); + if (mrb->symhash[hash]) { + mrb_sym i = sym - mrb->symhash[hash]; + if (i > 0xff) + sname->prev = 0xff; + else + sname->prev = i; + } + else { + sname->prev = 0; + } + mrb->symhash[hash] = mrb->symidx = sym; - return sym; + return (sym+MRB_PRESYM_MAX); } MRB_API mrb_sym @@ -113,41 +253,69 @@ mrb_intern_str(mrb_state *mrb, mrb_value str) return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } -MRB_API mrb_value -mrb_check_intern(mrb_state *mrb, const char *name, size_t len) +MRB_API mrb_sym +mrb_intern_check(mrb_state *mrb, const char *name, size_t len) { - khash_t(n2s) *h = mrb->name2sym; - symbol_name *sname = mrb->symtbl; - khiter_t k; + mrb_sym sym; sym_validate_len(mrb, len); - sname->len = (uint16_t)len; - sname->name = name; + sym = find_symbol(mrb, name, len, NULL); + if (sym > 0) return sym; + return 0; +} - k = kh_get(n2s, mrb, h, 0); - if (k != kh_end(h)) { - return mrb_symbol_value(kh_key(h, k)); - } - return mrb_nil_value(); +MRB_API mrb_value +mrb_check_intern(mrb_state *mrb, const char *name, size_t len) +{ + mrb_sym sym = mrb_intern_check(mrb, name, len); + if (sym == 0) return mrb_nil_value(); + return mrb_symbol_value(sym); +} + +MRB_API mrb_sym +mrb_intern_check_cstr(mrb_state *mrb, const char *name) +{ + return mrb_intern_check(mrb, name, strlen(name)); } MRB_API mrb_value mrb_check_intern_cstr(mrb_state *mrb, const char *name) { - return mrb_check_intern(mrb, name, (mrb_int)strlen(name)); + mrb_sym sym = mrb_intern_check_cstr(mrb, name); + if (sym == 0) return mrb_nil_value(); + return mrb_symbol_value(sym); +} + +MRB_API mrb_sym +mrb_intern_check_str(mrb_state *mrb, mrb_value str) +{ + return mrb_intern_check(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } MRB_API mrb_value mrb_check_intern_str(mrb_state *mrb, mrb_value str) { - return mrb_check_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); + mrb_sym sym = mrb_intern_check_str(mrb, str); + if (sym == 0) return mrb_nil_value(); + return mrb_symbol_value(sym); } -/* lenp must be a pointer to a size_t variable */ -MRB_API const char* -mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp) +static const char* +sym2name_len(mrb_state *mrb, mrb_sym sym, char *buf, mrb_int *lenp) { - if (sym == 0 || mrb->symidx < sym) { + if (sym == 0) goto outofsym; + if (SYMBOL_INLINE_P(sym)) return sym_inline_unpack(sym, buf, lenp); + +#ifndef MRB_NO_PRESYM + { + const char *name = presym_sym2name(sym, lenp); + if (name) return name; + } +#endif + sym -= MRB_PRESYM_MAX; + + if (mrb->symidx < sym) { + outofsym: if (lenp) *lenp = 0; return NULL; } @@ -156,6 +324,16 @@ mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp) return mrb->symtbl[sym].name; } +MRB_API const char* +mrb_sym_name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp) +{ +#ifdef MRB_USE_ALL_SYMBOLS + return sym2name_len(mrb, sym, NULL, lenp); +#else + return sym2name_len(mrb, sym, mrb->symbuf, lenp); +#endif +} + void mrb_free_symtbl(mrb_state *mrb) { @@ -167,13 +345,11 @@ mrb_free_symtbl(mrb_state *mrb) } } mrb_free(mrb, mrb->symtbl); - kh_destroy(n2s, mrb, mrb->name2sym); } void mrb_init_symtbl(mrb_state *mrb) { - mrb->name2sym = kh_init(n2s, mrb); } /********************************************************************** @@ -209,46 +385,44 @@ mrb_init_symtbl(mrb_state *mrb) * */ - -/* 15.2.11.3.1 */ +/* 15.2.11.3.2 */ +/* 15.2.11.3.3 */ /* * call-seq: - * sym == obj -> true or false + * sym.to_s -> string * - * Equality---If <i>sym</i> and <i>obj</i> are exactly the same - * symbol, returns <code>true</code>. + * Returns the name or string corresponding to <i>sym</i>. + * + * :fred.to_s #=> "fred" */ - static mrb_value -sym_equal(mrb_state *mrb, mrb_value sym1) +sym_to_s(mrb_state *mrb, mrb_value sym) { - mrb_value sym2; - - mrb_get_args(mrb, "o", &sym2); - - return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2)); + return mrb_sym_str(mrb, mrb_symbol(sym)); } -/* 15.2.11.3.2 */ -/* 15.2.11.3.3 */ /* * call-seq: - * sym.id2name -> string - * sym.to_s -> string + * sym.name -> string * - * Returns the name or string corresponding to <i>sym</i>. + * Returns the name or string corresponding to <i>sym</i>. Unlike #to_s, the + * returned string is frozen. * - * :fred.id2name #=> "fred" + * :fred.name #=> "fred" + * :fred.name.frozen? #=> true */ static mrb_value -mrb_sym_to_s(mrb_state *mrb, mrb_value sym) +sym_name(mrb_state *mrb, mrb_value vsym) { - mrb_sym id = mrb_symbol(sym); - const char *p; + mrb_sym sym = mrb_symbol(vsym); mrb_int len; + const char *name = mrb_sym_name_len(mrb, sym, &len); - p = mrb_sym2name_len(mrb, id, &len); - return mrb_str_new_static(mrb, p, len); + mrb_assert(name != NULL); + if (SYMBOL_INLINE_P(sym)) { + return mrb_str_new_frozen(mrb, name, len); + } + return mrb_str_new_static_frozen(mrb, name, len); } /* 15.2.11.3.4 */ @@ -403,57 +577,77 @@ sym_inspect(mrb_state *mrb, mrb_value sym) mrb_sym id = mrb_symbol(sym); char *sp; - name = mrb_sym2name_len(mrb, id, &len); - str = mrb_str_new(mrb, 0, len+1); + name = mrb_sym_name_len(mrb, id, &len); + str = mrb_str_new(mrb, NULL, len+1); sp = RSTRING_PTR(str); - RSTRING_PTR(str)[0] = ':'; + sp[0] = ':'; memcpy(sp+1, name, len); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); if (!symname_p(name) || strlen(name) != (size_t)len) { - str = mrb_str_dump(mrb, str); + str = mrb_str_inspect(mrb, str); sp = RSTRING_PTR(str); sp[0] = ':'; sp[1] = '"'; } +#ifdef MRB_UTF8_STRING + if (SYMBOL_INLINE_P(id)) RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); +#endif return str; } MRB_API mrb_value -mrb_sym2str(mrb_state *mrb, mrb_sym sym) +mrb_sym_str(mrb_state *mrb, mrb_sym sym) { mrb_int len; - const char *name = mrb_sym2name_len(mrb, sym, &len); + const char *name = mrb_sym_name_len(mrb, sym, &len); if (!name) return mrb_undef_value(); /* can't happen */ + if (SYMBOL_INLINE_P(sym)) { + mrb_value str = mrb_str_new(mrb, name, len); + RSTR_SET_ASCII_FLAG(mrb_str_ptr(str)); + return str; + } return mrb_str_new_static(mrb, name, len); } -MRB_API const char* -mrb_sym2name(mrb_state *mrb, mrb_sym sym) +static const char* +sym_cstr(mrb_state *mrb, mrb_sym sym, mrb_bool dump) { mrb_int len; - const char *name = mrb_sym2name_len(mrb, sym, &len); + const char *name = mrb_sym_name_len(mrb, sym, &len); if (!name) return NULL; - if (symname_p(name) && strlen(name) == (size_t)len) { + if (strlen(name) == (size_t)len && (!dump || symname_p(name))) { return name; } else { - mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len)); + mrb_value str = mrb_str_new_static(mrb, name, len); + str = mrb_str_dump(mrb, str); return RSTRING_PTR(str); } } +MRB_API const char* +mrb_sym_name(mrb_state *mrb, mrb_sym sym) +{ + return sym_cstr(mrb, sym, FALSE); +} + +MRB_API const char* +mrb_sym_dump(mrb_state *mrb, mrb_sym sym) +{ + return sym_cstr(mrb, sym, TRUE); +} + #define lesser(a,b) (((a)>(b))?(b):(a)) static mrb_value sym_cmp(mrb_state *mrb, mrb_value s1) { - mrb_value s2; + mrb_value s2 = mrb_get_arg1(mrb); mrb_sym sym1, sym2; - mrb_get_args(mrb, "o", &s2); - if (mrb_type(s2) != MRB_TT_SYMBOL) return mrb_nil_value(); + if (!mrb_symbol_p(s2)) return mrb_nil_value(); sym1 = mrb_symbol(s1); sym2 = mrb_symbol(s2); if (sym1 == sym2) return mrb_fixnum_value(0); @@ -461,9 +655,10 @@ sym_cmp(mrb_state *mrb, mrb_value s1) const char *p1, *p2; int retval; mrb_int len, len1, len2; + char buf1[8], buf2[8]; - p1 = mrb_sym2name_len(mrb, sym1, &len1); - p2 = mrb_sym2name_len(mrb, sym2, &len2); + p1 = sym2name_len(mrb, sym1, buf1, &len1); + p2 = sym2name_len(mrb, sym2, buf2, &len2); len = lesser(len1, len2); retval = memcmp(p1, p2, len); if (retval == 0) { @@ -481,14 +676,13 @@ mrb_init_symbol(mrb_state *mrb) { struct RClass *sym; - mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */ + mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */ MRB_SET_INSTANCE_TT(sym, MRB_TT_SYMBOL); mrb_undef_class_method(mrb, sym, "new"); - mrb_define_method(mrb, sym, "===", sym_equal, MRB_ARGS_REQ(1)); /* 15.2.11.3.1 */ - mrb_define_method(mrb, sym, "id2name", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.2 */ - mrb_define_method(mrb, sym, "to_s", mrb_sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */ - mrb_define_method(mrb, sym, "to_sym", sym_to_sym, MRB_ARGS_NONE()); /* 15.2.11.3.4 */ - mrb_define_method(mrb, sym, "inspect", sym_inspect, MRB_ARGS_NONE()); /* 15.2.11.3.5(x) */ - mrb_define_method(mrb, sym, "<=>", sym_cmp, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, sym, "to_s", sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */ + mrb_define_method(mrb, sym, "name", sym_name, MRB_ARGS_NONE()); + mrb_define_method(mrb, sym, "to_sym", sym_to_sym, MRB_ARGS_NONE()); /* 15.2.11.3.4 */ + mrb_define_method(mrb, sym, "inspect", sym_inspect, MRB_ARGS_NONE()); /* 15.2.11.3.5(x) */ + mrb_define_method(mrb, sym, "<=>", sym_cmp, MRB_ARGS_REQ(1)); } diff --git a/src/value_array.h b/src/value_array.h index bc5f28b06..6089b8aa0 100644 --- a/src/value_array.h +++ b/src/value_array.h @@ -6,6 +6,7 @@ static inline void value_move(mrb_value *s1, const mrb_value *s2, size_t n) { + if (n == 0) return; if (s1 > s2 && s1 < s2 + n) { s1 += n; diff --git a/src/variable.c b/src/variable.c index 72c13aa1f..d89295229 100644 --- a/src/variable.c +++ b/src/variable.c @@ -9,24 +9,19 @@ #include <mruby/class.h> #include <mruby/proc.h> #include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/presym.h> -typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*); - -#ifndef MRB_IV_SEGMENT_SIZE -#define MRB_IV_SEGMENT_SIZE 4 -#endif - -typedef struct segment { - mrb_sym key[MRB_IV_SEGMENT_SIZE]; - mrb_value val[MRB_IV_SEGMENT_SIZE]; - struct segment *next; -} segment; +struct iv_elem { + mrb_sym key; + mrb_value val; +}; /* Instance variable table structure */ typedef struct iv_tbl { - segment *rootseg; size_t size; - size_t last_len; + size_t alloc; + struct iv_elem *table; } iv_tbl; /* Creates the instance variable table. */ @@ -37,67 +32,82 @@ iv_new(mrb_state *mrb) t = (iv_tbl*)mrb_malloc(mrb, sizeof(iv_tbl)); t->size = 0; - t->rootseg = NULL; - t->last_len = 0; + t->alloc = 0; + t->table = NULL; return t; } +static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val); + +static void +iv_rehash(mrb_state *mrb, iv_tbl *t) +{ + size_t old_alloc = t->alloc; + size_t new_alloc = old_alloc+1; + struct iv_elem *old_table = t->table; + + khash_power2(new_alloc); + if (old_alloc == new_alloc) return; + + t->alloc = new_alloc; + t->size = 0; + t->table = (struct iv_elem*)mrb_calloc(mrb, sizeof(struct iv_elem), new_alloc); + + for (size_t i = 0; i < old_alloc; i++) { + struct iv_elem *slot = &old_table[i]; + + /* key = 0 means empty; val = undef means deleted */ + if (slot->key != 0 && !mrb_undef_p(slot->val)) { + iv_put(mrb, t, slot->key, slot->val); + } + } + mrb_free(mrb, old_table); +} + +#define slot_empty_p(slot) ((slot)->key == 0 && !mrb_undef_p((slot)->val)) + /* Set the value for the symbol in the instance variable table. */ static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) { - segment *seg; - segment *prev = NULL; - segment *matched_seg = NULL; - size_t matched_idx = 0; - size_t i; + size_t hash, pos, start; + struct iv_elem *dslot = NULL; if (t == NULL) return; - seg = t->rootseg; - while (seg) { - 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) { - seg->key[i] = sym; - seg->val[i] = val; - t->last_len = i+1; + if (t->alloc == 0) { + iv_rehash(mrb, t); + } + hash = kh_int_hash_func(mrb, sym); + start = pos = hash & (t->alloc-1); + for (;;) { + struct iv_elem *slot = &t->table[pos]; + + if (slot->key == sym) { + slot->val = val; + return; + } + else if (slot_empty_p(slot)) { + t->size++; + slot->key = sym; + slot->val = val; + return; + } + else if (!dslot && mrb_undef_p(slot->val)) { /* deleted */ + dslot = slot; + } + pos = (pos+1) & (t->alloc-1); + if (pos == start) { /* not found */ + if (dslot) { t->size++; + dslot->key = sym; + dslot->val = val; return; } - if (!matched_seg && key == 0) { - matched_seg = seg; - matched_idx = i; - } - else if (key == sym) { - seg->val[i] = val; - return; - } + /* no room */ + iv_rehash(mrb, t); + start = pos = hash & (t->alloc-1); } - prev = seg; - seg = seg->next; - } - - /* Not found */ - t->size++; - if (matched_seg) { - matched_seg->key[matched_idx] = sym; - matched_seg->val[matched_idx] = val; - return; - } - - seg = (segment*)mrb_malloc(mrb, sizeof(segment)); - if (!seg) return; - seg->next = NULL; - seg->key[0] = sym; - seg->val[0] = val; - t->last_len = 1; - if (prev) { - prev->next = seg; - } - else { - t->rootseg = seg; } } @@ -105,133 +115,111 @@ iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) static mrb_bool iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) { - segment *seg; - size_t i; + size_t hash, pos, start; if (t == NULL) return FALSE; - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) { - mrb_sym key = seg->key[i]; + if (t->alloc == 0) return FALSE; + if (t->size == 0) return FALSE; - if (!seg->next && i >= t->last_len) { - return FALSE; - } - if (key == sym) { - if (vp) *vp = seg->val[i]; - return TRUE; - } + hash = kh_int_hash_func(mrb, sym); + start = pos = hash & (t->alloc-1); + for (;;) { + struct iv_elem *slot = &t->table[pos]; + + if (slot->key == sym) { + if (vp) *vp = slot->val; + return TRUE; + } + else if (slot_empty_p(slot)) { + return FALSE; + } + pos = (pos+1) & (t->alloc-1); + if (pos == start) { /* not found */ + return FALSE; } - seg = seg->next; } - return FALSE; } /* Deletes the value for the symbol from the instance variable table. */ static mrb_bool iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) { - segment *seg; - size_t i; + size_t hash, pos, start; if (t == NULL) return FALSE; - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) { - mrb_sym key = seg->key[i]; + if (t->alloc == 0) return FALSE; + if (t->size == 0) return FALSE; - if (!seg->next && i >= t->last_len) { - return FALSE; - } - if (key == sym) { - t->size--; - seg->key[i] = 0; - if (vp) *vp = seg->val[i]; - return TRUE; - } + hash = kh_int_hash_func(mrb, sym); + start = pos = hash & (t->alloc-1); + for (;;) { + struct iv_elem *slot = &t->table[pos]; + + if (slot->key == sym) { + if (vp) *vp = slot->val; + t->size--; + slot->key = 0; + slot->val = mrb_undef_value(); + return TRUE; + } + else if (slot_empty_p(slot)) { + return FALSE; + } + pos = (pos+1) & (t->alloc-1); + if (pos == start) { /* not found */ + return FALSE; } - seg = seg->next; } - return FALSE; } /* Iterates over the instance variable table. */ -static mrb_bool -iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p) +static void +iv_foreach(mrb_state *mrb, iv_tbl *t, mrb_iv_foreach_func *func, void *p) { - segment *seg; size_t i; - int n; - if (t == NULL) return TRUE; - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) { - mrb_sym key = seg->key[i]; + if (t == NULL) return; + if (t->alloc == 0) return; + if (t->size == 0) return; - /* no value in last segment after last_len */ - if (!seg->next && i >= t->last_len) { - return FALSE; - } - if (key != 0) { - n =(*func)(mrb, key, seg->val[i], p); - if (n > 0) return FALSE; - if (n < 0) { - t->size--; - seg->key[i] = 0; - } + for (i=0; i<t->alloc; i++) { + struct iv_elem *slot = &t->table[i]; + + if (slot->key && !mrb_undef_p(slot->val)) { + if ((*func)(mrb, slot->key, slot->val, p) != 0) { + return; } } - seg = seg->next; } - return TRUE; + return; } /* Get the size of the instance variable table. */ static size_t iv_size(mrb_state *mrb, iv_tbl *t) { - segment *seg; - size_t size = 0; - if (t == NULL) return 0; - if (t->size > 0) return t->size; - seg = t->rootseg; - while (seg) { - if (seg->next == NULL) { - size += t->last_len; - return size; - } - seg = seg->next; - size += MRB_IV_SEGMENT_SIZE; - } - /* empty iv_tbl */ - return 0; + return t->size; } /* Copy the instance variable table. */ static iv_tbl* iv_copy(mrb_state *mrb, iv_tbl *t) { - segment *seg; iv_tbl *t2; - size_t i; - seg = t->rootseg; - t2 = iv_new(mrb); + if (t == NULL) return NULL; + if (t->alloc == 0) return NULL; + if (t->size == 0) return NULL; - while (seg != NULL) { - for (i=0; i<MRB_IV_SEGMENT_SIZE; i++) { - mrb_sym key = seg->key[i]; - mrb_value val = seg->val[i]; + t2 = iv_new(mrb); + for (i=0; i<t->alloc; i++) { + struct iv_elem *slot = &t->table[i]; - if ((seg->next == NULL) && (i >= t->last_len)) { - return t2; - } - iv_put(mrb, t2, key, val); + if (slot->key && !mrb_undef_p(slot->val)) { + iv_put(mrb, t2, slot->key, slot->val); } - seg = seg->next; } return t2; } @@ -240,14 +228,7 @@ iv_copy(mrb_state *mrb, iv_tbl *t) static void iv_free(mrb_state *mrb, iv_tbl *t) { - segment *seg; - - seg = t->rootseg; - while (seg) { - segment *p = seg; - seg = seg->next; - mrb_free(mrb, p); - } + mrb_free(mrb, t->table); mrb_free(mrb, t); } @@ -346,21 +327,30 @@ mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym) static inline void assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v); -MRB_API void -mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) +void +mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { - iv_tbl *t; - - if (MRB_FROZEN_P(obj)) { - mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %S", mrb_obj_value(obj)); - } assign_class_name(mrb, obj, sym, v); if (!obj->iv) { obj->iv = iv_new(mrb); } - t = obj->iv; - iv_put(mrb, t, sym, v); - mrb_write_barrier(mrb, (struct RBasic*)obj); + iv_put(mrb, obj->iv, sym, v); + mrb_field_write_barrier_value(mrb, (struct RBasic*)obj, v); +} + +MRB_API void +mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) +{ + mrb_check_frozen(mrb, obj); + mrb_obj_iv_set_force(mrb, obj, sym, v); +} + +/* Iterates over the instance variable table. */ +MRB_API void +mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p) +{ + if (!obj_iv_p(obj)) return; + iv_foreach(mrb, mrb_obj_ptr(obj)->iv, func, p); } static inline mrb_bool @@ -374,20 +364,20 @@ assign_class_name(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { if (namespace_p(obj->tt) && namespace_p(mrb_type(v))) { struct RObject *c = mrb_obj_ptr(v); - if (obj != c && ISUPPER(mrb_sym2name(mrb, sym)[0])) { - mrb_sym id_classname = mrb_intern_lit(mrb, "__classname__"); + if (obj != c && ISUPPER(mrb_sym_name_len(mrb, sym, NULL)[0])) { + mrb_sym id_classname = MRB_SYM(__classname__); mrb_value o = mrb_obj_iv_get(mrb, c, id_classname); if (mrb_nil_p(o)) { - mrb_sym id_outer = mrb_intern_lit(mrb, "__outer__"); + mrb_sym id_outer = MRB_SYM(__outer__); o = mrb_obj_iv_get(mrb, c, id_outer); if (mrb_nil_p(o)) { if ((struct RClass *)obj == mrb->object_class) { - mrb_obj_iv_set(mrb, c, id_classname, mrb_symbol_value(sym)); + mrb_obj_iv_set_force(mrb, c, id_classname, mrb_symbol_value(sym)); } else { - mrb_obj_iv_set(mrb, c, id_outer, mrb_obj_value(obj)); + mrb_obj_iv_set_force(mrb, c, id_outer, mrb_obj_value(obj)); } } } @@ -425,29 +415,24 @@ mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym) return mrb_obj_iv_defined(mrb, mrb_obj_ptr(obj), sym); } -#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) - MRB_API mrb_bool mrb_iv_name_sym_p(mrb_state *mrb, mrb_sym iv_name) { const char *s; - mrb_int i, len; + mrb_int len; - s = mrb_sym2name_len(mrb, iv_name, &len); + s = mrb_sym_name_len(mrb, iv_name, &len); if (len < 2) return FALSE; if (s[0] != '@') return FALSE; - if (s[1] == '@') return FALSE; - for (i=1; i<len; i++) { - if (!identchar(s[i])) return FALSE; - } - return TRUE; + if (ISDIGIT(s[1])) return FALSE; + return mrb_ident_p(s+1, len-1); } MRB_API void mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name) { if (!mrb_iv_name_sym_p(mrb, iv_name)) { - mrb_name_error(mrb, iv_name, "'%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name)); + mrb_name_error(mrb, iv_name, "'%n' is not allowed as an instance variable name", iv_name); } } @@ -484,10 +469,10 @@ inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) else { mrb_str_cat_lit(mrb, str, ", "); } - s = mrb_sym2name_len(mrb, sym, &len); + s = mrb_sym_name_len(mrb, sym, &len); mrb_str_cat(mrb, str, s, len); mrb_str_cat_lit(mrb, str, "="); - if (mrb_type(v) == MRB_TT_OBJECT) { + if (mrb_object_p(v)) { ins = mrb_any_to_s(mrb, v); } else { @@ -510,7 +495,7 @@ mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj) mrb_str_cat_lit(mrb, str, "-<"); mrb_str_cat_cstr(mrb, str, cn); mrb_str_cat_lit(mrb, str, ":"); - mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, obj)); + mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, obj)); iv_foreach(mrb, t, inspect_i, &str); mrb_str_cat_lit(mrb, str, ">"); @@ -526,6 +511,7 @@ mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym) iv_tbl *t = mrb_obj_ptr(obj)->iv; mrb_value val; + mrb_check_frozen(mrb, mrb_obj_ptr(obj)); if (iv_del(mrb, t, sym, &val)) { return val; } @@ -541,7 +527,7 @@ iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) mrb_int len; ary = *(mrb_value*)p; - s = mrb_sym2name_len(mrb, sym, &len); + s = mrb_sym_name_len(mrb, sym, &len); if (len > 1 && s[0] == '@' && s[1] != '@') { mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } @@ -585,7 +571,7 @@ cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) mrb_int len; ary = *(mrb_value*)p; - s = mrb_sym2name_len(mrb, sym, &len); + s = mrb_sym_name_len(mrb, sym, &len); if (len > 2 && s[0] == '@' && s[1] == '@') { mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); } @@ -595,7 +581,7 @@ cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) /* 15.2.2.4.19 */ /* * call-seq: - * mod.class_variables -> array + * mod.class_variables(inherit=true) -> array * * Returns an array of the names of class variables in <i>mod</i>. * @@ -613,17 +599,20 @@ mrb_mod_class_variables(mrb_state *mrb, mrb_value mod) { mrb_value ary; struct RClass *c; + mrb_bool inherit = TRUE; + mrb_get_args(mrb, "|b", &inherit); ary = mrb_ary_new(mrb); c = mrb_class_ptr(mod); while (c) { iv_foreach(mrb, c->iv, cv_i, &ary); + if (!inherit) break; c = c->super; } return ary; } -MRB_API mrb_value +mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym) { struct RClass * cls = c; @@ -640,8 +629,7 @@ mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym) if (cls && cls->tt == MRB_TT_SCLASS) { mrb_value klass; - klass = mrb_obj_iv_get(mrb, (struct RObject *)cls, - mrb_intern_lit(mrb, "__attached__")); + klass = mrb_obj_iv_get(mrb, (struct RObject *)cls, MRB_SYM(__attached__)); c = mrb_class_ptr(klass); if (c->tt == MRB_TT_CLASS || c->tt == MRB_TT_MODULE) { given = FALSE; @@ -654,8 +642,7 @@ mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym) if (given) return v; } } - mrb_name_error(mrb, sym, "uninitialized class variable %S in %S", - mrb_sym2str(mrb, sym), mrb_obj_value(cls)); + mrb_name_error(mrb, sym, "uninitialized class variable %n in %C", sym, cls); /* not reached */ return mrb_nil_value(); } @@ -675,8 +662,9 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) iv_tbl *t = c->iv; if (iv_get(mrb, t, sym, NULL)) { + mrb_check_frozen(mrb, c); iv_put(mrb, t, sym, v); - mrb_write_barrier(mrb, (struct RBasic*)c); + mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v); return; } c = c->super; @@ -685,8 +673,7 @@ 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__")); + klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__)); switch (mrb_type(klass)) { case MRB_TT_CLASS: case MRB_TT_MODULE: @@ -702,12 +689,13 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) c = cls; } + mrb_check_frozen(mrb, c); if (!c->iv) { c->iv = iv_new(mrb); } iv_put(mrb, c->iv, sym, v); - mrb_write_barrier(mrb, (struct RBasic*)c); + mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v); } MRB_API void @@ -716,7 +704,7 @@ mrb_cv_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v) mrb_mod_cv_set(mrb, mrb_class_ptr(mod), sym, v); } -MRB_API mrb_bool +mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym) { while (c) { @@ -739,7 +727,13 @@ mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym) { struct RClass *c; - c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); + const struct RProc *p = mrb->c->ci->proc; + + for (;;) { + c = MRB_PROC_TARGET_CLASS(p); + if (c && c->tt != MRB_TT_SCLASS) break; + p = p->upper; + } return mrb_mod_cv_get(mrb, c, sym); } @@ -747,8 +741,13 @@ void mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { struct RClass *c; + const struct RProc *p = mrb->c->ci->proc; - c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); + for (;;) { + c = MRB_PROC_TARGET_CLASS(p); + if (c && c->tt != MRB_TT_SCLASS) break; + p = p->upper; + } mrb_mod_cv_set(mrb, c, sym, v); } @@ -767,16 +766,17 @@ mod_const_check(mrb_state *mrb, mrb_value mod) } static mrb_value -const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym) +const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym, mrb_bool skip) { struct RClass *c = base; mrb_value v; mrb_bool retry = FALSE; mrb_value name; + if (skip) c = c->super; L_RETRY: while (c) { - if (c->iv) { + if (!MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_PREPENDED) && c->iv) { if (iv_get(mrb, c->iv, sym, &v)) return v; } @@ -788,14 +788,14 @@ L_RETRY: goto L_RETRY; } name = mrb_symbol_value(sym); - return mrb_funcall_argv(mrb, mrb_obj_value(base), mrb_intern_lit(mrb, "const_missing"), 1, &name); + return mrb_funcall_argv(mrb, mrb_obj_value(base), MRB_SYM(const_missing), 1, &name); } MRB_API mrb_value mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) { mod_const_check(mrb, mod); - return const_get(mrb, mrb_class_ptr(mod), sym); + return const_get(mrb, mrb_class_ptr(mod), sym, FALSE); } mrb_value @@ -804,9 +804,10 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) struct RClass *c; struct RClass *c2; mrb_value v; - struct RProc *proc; + const struct RProc *proc = mrb->c->ci->proc; - c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); + c = MRB_PROC_TARGET_CLASS(proc); + if (!c) c = mrb->object_class; if (iv_get(mrb, c->iv, sym, &v)) { return v; } @@ -814,15 +815,14 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) while (c2 && c2->tt == MRB_TT_SCLASS) { mrb_value klass; - if (!iv_get(mrb, c2->iv, mrb_intern_lit(mrb, "__attached__"), &klass)) { + if (!iv_get(mrb, c2->iv, MRB_SYM(__attached__), &klass)) { c2 = NULL; break; } c2 = mrb_class_ptr(klass); } if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2; - mrb_assert(!MRB_PROC_CFUNC_P(mrb->c->ci->proc)); - proc = mrb->c->ci->proc; + proc = proc->upper; while (proc) { c2 = MRB_PROC_TARGET_CLASS(proc); if (c2 && iv_get(mrb, c2->iv, sym, &v)) { @@ -830,7 +830,7 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) } proc = proc->upper; } - return const_get(mrb, c, sym); + return const_get(mrb, c, sym, TRUE); } MRB_API void @@ -849,6 +849,7 @@ mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v) struct RClass *c; c = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); + if (!c) c = mrb->object_class; mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v); } @@ -860,6 +861,12 @@ mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym) } MRB_API void +mrb_define_const_id(mrb_state *mrb, struct RClass *mod, mrb_sym name, mrb_value v) +{ + mrb_obj_iv_set(mrb, (struct RObject*)mod, name, v); +} + +MRB_API void mrb_define_const(mrb_state *mrb, struct RClass *mod, const char *name, mrb_value v) { mrb_obj_iv_set(mrb, (struct RObject*)mod, mrb_intern_cstr(mrb, name), v); @@ -879,9 +886,17 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) mrb_int len; ary = *(mrb_value*)p; - s = mrb_sym2name_len(mrb, sym, &len); + s = mrb_sym_name_len(mrb, sym, &len); if (len >= 1 && ISUPPER(s[0])) { - mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); + mrb_int i, alen = RARRAY_LEN(ary); + + for (i=0; i<alen; i++) { + if (mrb_symbol(RARRAY_PTR(ary)[i]) == sym) + break; + } + if (i==alen) { + mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); + } } return 0; } @@ -891,7 +906,7 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) * call-seq: * mod.constants -> array * - * Returns an array of all names of contants defined in the receiver. + * Returns an array of all names of constants defined in the receiver. */ mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod) @@ -964,16 +979,8 @@ mrb_f_global_variables(mrb_state *mrb, mrb_value self) { iv_tbl *t = mrb->globals; mrb_value ary = mrb_ary_new(mrb); - size_t i; - char buf[3]; iv_foreach(mrb, t, gv_i, &ary); - buf[0] = '$'; - buf[2] = 0; - for (i = 1; i <= 9; ++i) { - buf[1] = (char)(i + '0'); - mrb_ary_push(mrb, ary, mrb_symbol_value(mrb_intern(mrb, buf, 2))); - } return ary; } @@ -1055,7 +1062,7 @@ outer_class(mrb_state *mrb, struct RClass *c) { mrb_value ov; - ov = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__")); + ov = mrb_obj_iv_get(mrb, (struct RObject*)c, MRB_SYM(__outer__)); if (mrb_nil_p(ov)) return NULL; switch (mrb_type(ov)) { case MRB_TT_CLASS: @@ -1102,12 +1109,34 @@ mrb_class_find_path(mrb_state *mrb, struct RClass *c) mrb_str_cat_cstr(mrb, path, str); mrb_str_cat_cstr(mrb, path, "::"); - str = mrb_sym2name_len(mrb, name, &len); + str = mrb_sym_name_len(mrb, name, &len); mrb_str_cat(mrb, path, str, len); if (RSTRING_PTR(path)[0] != '#') { - iv_del(mrb, c->iv, mrb_intern_lit(mrb, "__outer__"), NULL); - iv_put(mrb, c->iv, mrb_intern_lit(mrb, "__classname__"), path); + iv_del(mrb, c->iv, MRB_SYM(__outer__), NULL); + iv_put(mrb, c->iv, MRB_SYM(__classname__), path); mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path); + path = mrb_str_dup(mrb, path); } return path; } + +size_t +mrb_obj_iv_tbl_memsize(mrb_value obj) +{ + iv_tbl *t = mrb_obj_ptr(obj)->iv; + if (t == NULL) return 0; + return sizeof(iv_tbl) + t->alloc*sizeof(struct iv_elem); +} + +#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) + +mrb_bool +mrb_ident_p(const char *s, mrb_int len) +{ + mrb_int i; + + for (i = 0; i < len; i++) { + if (!identchar(s[i])) return FALSE; + } + return TRUE; +} @@ -6,7 +6,9 @@ #include <stddef.h> #include <stdarg.h> +#ifndef MRB_NO_FLOAT #include <math.h> +#endif #include <mruby.h> #include <mruby/array.h> #include <mruby/class.h> @@ -21,8 +23,10 @@ #include <mruby/opcode.h> #include "value_array.h" #include <mruby/throw.h> +#include <mruby/dump.h> +#include <mruby/presym.h> -#ifdef MRB_DISABLE_STDIO +#ifdef MRB_NO_STDIO #if defined(__cplusplus) extern "C" { #endif @@ -35,14 +39,6 @@ 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 @@ -53,11 +49,6 @@ void abort(void); #define MRB_FUNCALL_DEPTH_MAX 512 #endif -/* Maximum depth of ecall() recursion. */ -#ifndef MRB_ECALL_DEPTH_MAX -#define MRB_ECALL_DEPTH_MAX 32 -#endif - /* Maximum stack depth. Should be set lower on memory constrained systems. The value below allows about 60000 recursive calls in the simplest case. */ #ifndef MRB_STACK_MAX @@ -100,26 +91,20 @@ void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value static inline void stack_clear(mrb_value *from, size_t count) { -#ifndef MRB_NAN_BOXING - const mrb_value mrb_value_zero = { 0 }; - - while (count-- > 0) { - *from++ = mrb_value_zero; - } -#else +#ifdef MRB_NAN_BOXING while (count-- > 0) { SET_NIL_VALUE(*from); from++; } +#else + memset(from, 0, sizeof(mrb_value)*count); #endif } static inline void stack_copy(mrb_value *dst, const mrb_value *src, size_t size) { - while (size-- > 0) { - *dst++ = *src++; - } + memcpy(dst, src, sizeof(mrb_value)*size); } static void @@ -130,14 +115,13 @@ stack_init(mrb_state *mrb) /* mrb_assert(mrb->stack == NULL); */ c->stbase = (mrb_value *)mrb_calloc(mrb, STACK_INIT_SIZE, sizeof(mrb_value)); c->stend = c->stbase + STACK_INIT_SIZE; - c->stack = c->stbase; /* mrb_assert(ci == NULL); */ c->cibase = (mrb_callinfo *)mrb_calloc(mrb, CALLINFO_INIT_SIZE, sizeof(mrb_callinfo)); c->ciend = c->cibase + CALLINFO_INIT_SIZE; c->ci = c->cibase; - c->ci->target_class = mrb->object_class; - c->ci->stackent = c->stack; + c->ci->u.target_class = mrb->object_class; + c->ci->stack = c->stbase; } static inline void @@ -147,20 +131,20 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t oldsize if (newbase == oldbase) return; while (ci <= mrb->c->ci) { - struct REnv *e = ci->env; + struct REnv *e = mrb_vm_ci_env(ci); mrb_value *st; - if (e && MRB_ENV_STACK_SHARED_P(e) && + if (e && MRB_ENV_ONSTACK_P(e) && (st = e->stack) && oldbase <= st && st < oldbase+oldsize) { ptrdiff_t off = e->stack - oldbase; e->stack = newbase + off; } - if (ci->proc && MRB_PROC_ENV_P(ci->proc) && ci->env != MRB_PROC_ENV(ci->proc)) { + if (ci->proc && MRB_PROC_ENV_P(ci->proc) && e != MRB_PROC_ENV(ci->proc)) { e = MRB_PROC_ENV(ci->proc); - if (e && MRB_ENV_STACK_SHARED_P(e) && + if (e && MRB_ENV_ONSTACK_P(e) && (st = e->stack) && oldbase <= st && st < oldbase+oldsize) { ptrdiff_t off = e->stack - oldbase; @@ -168,7 +152,7 @@ envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t oldsize } } - ci->stackent = newbase + (ci->stackent - oldbase); + ci->stack = newbase + (ci->stack - oldbase); ci++; } } @@ -182,11 +166,11 @@ stack_extend_alloc(mrb_state *mrb, mrb_int room) 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; + size_t off = mrb->c->ci->stack ? mrb->c->stend - mrb->c->ci->stack : 0; if (off > size) size = off; #ifdef MRB_STACK_EXTEND_DOUBLING - if (room <= (size_t)size) + if ((size_t)room <= size) size *= 2; else size += room; @@ -200,14 +184,13 @@ stack_extend_alloc(mrb_state *mrb, mrb_int room) size += room; #endif - newstack = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size); + newstack = (mrb_value *)mrb_realloc_simple(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, oldsize); mrb->c->stbase = newstack; - mrb->c->stack = mrb->c->stbase + off; mrb->c->stend = mrb->c->stbase + size; /* Raise an exception if the new stack size will be too large, @@ -220,15 +203,15 @@ stack_extend_alloc(mrb_state *mrb, mrb_int room) MRB_API void mrb_stack_extend(mrb_state *mrb, mrb_int room) { - if (mrb->c->stack + room >= mrb->c->stend) { + if (!mrb->c->ci->stack || mrb->c->ci->stack + room >= mrb->c->stend) { stack_extend_alloc(mrb, room); } } static inline struct REnv* -uvenv(mrb_state *mrb, int up) +uvenv(mrb_state *mrb, mrb_int up) { - struct RProc *proc = mrb->c->ci->proc; + const struct RProc *proc = mrb->c->ci->proc; struct REnv *e; while (up--) { @@ -243,7 +226,7 @@ uvenv(mrb_state *mrb, int up) while (cb <= ci) { if (ci->proc == proc) { - return ci->env; + return mrb_vm_ci_env(ci); } ci--; } @@ -251,8 +234,8 @@ uvenv(mrb_state *mrb, int up) return NULL; } -static inline struct RProc* -top_proc(mrb_state *mrb, struct RProc *proc) +static inline const struct RProc* +top_proc(mrb_state *mrb, const struct RProc *proc) { while (proc->upper) { if (MRB_PROC_SCOPE_P(proc) || MRB_PROC_STRICT_P(proc)) @@ -267,14 +250,12 @@ top_proc(mrb_state *mrb, struct RProc *proc) #define CI_ACC_RESUMED -3 static inline mrb_callinfo* -cipush(mrb_state *mrb) +cipush(mrb_state *mrb, mrb_int push_stacks, mrb_int acc, + struct RClass *target_class, const struct RProc *proc, mrb_sym mid, mrb_int argc) { struct mrb_context *c = mrb->c; - static const mrb_callinfo ci_zero = { 0 }; mrb_callinfo *ci = c->ci; - int ridx = ci->ridx; - if (ci + 1 == c->ciend) { ptrdiff_t size = ci - c->cibase; @@ -283,9 +264,12 @@ cipush(mrb_state *mrb) c->ciend = c->cibase + size * 2; } ci = ++c->ci; - *ci = ci_zero; - ci->epos = mrb->c->eidx; - ci->ridx = ridx; + ci->mid = mid; + mrb_vm_ci_proc_set(ci, proc); + ci->stack = ci[-1].stack + push_stacks; + ci->argc = (int16_t)argc; + ci->acc = (int16_t)acc; + ci->u.target_class = target_class; return ci; } @@ -295,82 +279,79 @@ mrb_env_unshare(mrb_state *mrb, struct REnv *e) { if (e == NULL) return; else { - size_t len = (size_t)MRB_ENV_STACK_LEN(e); + size_t len = (size_t)MRB_ENV_LEN(e); mrb_value *p; - if (!MRB_ENV_STACK_SHARED_P(e)) return; + if (!MRB_ENV_ONSTACK_P(e)) return; if (e->cxt != mrb->c) return; - if (e == mrb->c->cibase->env) return; /* for mirb */ + if (e == mrb_vm_ci_env(mrb->c->cibase)) return; /* for mirb */ p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len); if (len > 0) { stack_copy(p, e->stack, len); } e->stack = p; - MRB_ENV_UNSHARE_STACK(e); + MRB_ENV_CLOSE(e); mrb_write_barrier(mrb, (struct RBasic *)e); } } -static inline void +static inline mrb_callinfo* cipop(mrb_state *mrb) { struct mrb_context *c = mrb->c; - struct REnv *env = c->ci->env; + struct REnv *env = mrb_vm_ci_env(c->ci); c->ci--; if (env) mrb_env_unshare(mrb, env); + return c->ci; } -void mrb_exc_set(mrb_state *mrb, mrb_value exc); - -static void -ecall(mrb_state *mrb) +MRB_API mrb_value +mrb_protect_error(mrb_state *mrb, mrb_protect_error_func *body, void *userdata, mrb_bool *error) { - struct RProc *p; - struct mrb_context *c = mrb->c; - mrb_callinfo *ci = c->ci; - struct RObject *exc; - struct REnv *env; - ptrdiff_t cioff; + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + mrb_value result = mrb_nil_value(); int ai = mrb_gc_arena_save(mrb); - uint16_t i = --c->eidx; - int nregs; + const struct mrb_context *c = mrb->c; + int ci_index = c->ci - c->cibase; - if (i<0) return; - if (ci - c->cibase > MRB_ECALL_DEPTH_MAX) { - mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); + if (error) { *error = FALSE; } + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + result = body(mrb, userdata); + mrb->jmp = prev_jmp; } - p = c->ensure[i]; - if (!p) return; - mrb_assert(!MRB_PROC_CFUNC_P(p)); - c->ensure[i] = NULL; - nregs = p->upper->body.irep->nregs; - if (ci->proc && !MRB_PROC_CFUNC_P(ci->proc) && - ci->proc->body.irep->nregs > nregs) { - nregs = ci->proc->body.irep->nregs; - } - cioff = ci - c->cibase; - ci = cipush(mrb); - ci->stackent = mrb->c->stack; - ci->mid = ci[-1].mid; - ci->acc = CI_ACC_SKIP; - ci->argc = 0; - ci->proc = p; - ci->target_class = MRB_PROC_TARGET_CLASS(p); - env = MRB_PROC_ENV(p); - mrb_assert(env); - c->stack += nregs; - exc = mrb->exc; mrb->exc = 0; - if (exc) { - mrb_gc_protect(mrb, mrb_obj_value(exc)); - } - mrb_run(mrb, p, env->stack[0]); - mrb->c = c; - c->ci = c->cibase + cioff; - if (!mrb->exc) mrb->exc = exc; + MRB_CATCH(&c_jmp) { + mrb->jmp = prev_jmp; + result = mrb_obj_value(mrb->exc); + mrb->exc = NULL; + if (error) { *error = TRUE; } + if (mrb->c == c) { + while (c->ci - c->cibase > ci_index) { + cipop(mrb); + } + } + else { + // It was probably switched by mrb_fiber_resume(). + // Simply destroy all successive CI_ACC_DIRECTs once the fiber has been switched. + c = mrb->c; + while (c->ci > c->cibase && c->ci->acc == CI_ACC_DIRECT) { + cipop(mrb); + } + } + } + MRB_END_EXC(&c_jmp); + mrb_gc_arena_restore(mrb, ai); + mrb_gc_protect(mrb, result); + return result; } +void mrb_exc_set(mrb_state *mrb, mrb_value exc); +static mrb_value mrb_run(mrb_state *mrb, const struct RProc* proc, mrb_value self); + #ifndef MRB_FUNCALL_ARGC_MAX #define MRB_FUNCALL_ARGC_MAX 16 #endif @@ -395,11 +376,30 @@ mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...) return mrb_funcall_argv(mrb, self, mid, argc, argv); } -static int +MRB_API mrb_value +mrb_funcall_id(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, ...) +{ + mrb_value argv[MRB_FUNCALL_ARGC_MAX]; + va_list ap; + mrb_int i; + + if (argc > MRB_FUNCALL_ARGC_MAX) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" MRB_STRINGIZE(MRB_FUNCALL_ARGC_MAX) ")"); + } + + va_start(ap, argc); + for (i = 0; i < argc; i++) { + argv[i] = va_arg(ap, mrb_value); + } + va_end(ap); + return mrb_funcall_argv(mrb, self, mid, argc, argv); +} + +static mrb_int ci_nregs(mrb_callinfo *ci) { - struct RProc *p; - int n = 0; + const struct RProc *p; + mrb_int n = 0; if (!ci) return 3; p = ci->proc; @@ -423,6 +423,7 @@ MRB_API mrb_value mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk) { mrb_value val; + int ai = mrb_gc_arena_save(mrb); if (!mrb->jmp) { struct mrb_jmpbuf c_jmp; @@ -436,7 +437,6 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc } MRB_CATCH(&c_jmp) { /* error */ while (nth_ci < (mrb->c->ci - mrb->c->cibase)) { - mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); } mrb->jmp = 0; @@ -449,19 +449,19 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc mrb_method_t m; struct RClass *c; mrb_callinfo *ci; - int n = ci_nregs(mrb->c->ci); + mrb_int n = ci_nregs(mrb->c->ci); ptrdiff_t voff = -1; - if (!mrb->c->stack) { + if (!mrb->c->stbase) { stack_init(mrb); } if (argc < 0) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%S)", mrb_fixnum_value(argc)); + mrb_raisef(mrb, E_ARGUMENT_ERROR, "negative argc for funcall (%i)", argc); } c = mrb_class(mrb, self); m = mrb_method_search_vm(mrb, &c, mid); if (MRB_METHOD_UNDEF_P(m)) { - mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); + mrb_sym missing = MRB_SYM(method_missing); mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); m = mrb_method_search_vm(mrb, &c, missing); if (MRB_METHOD_UNDEF_P(m)) { @@ -469,65 +469,53 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc } mrb_ary_unshift(mrb, args, mrb_symbol_value(mid)); mrb_stack_extend(mrb, n+2); - mrb->c->stack[n+1] = args; + mrb->c->ci->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)); } - ci = cipush(mrb); - ci->mid = mid; - ci->stackent = mrb->c->stack; - ci->argc = (int)argc; - ci->target_class = c; - mrb->c->stack = mrb->c->stack + n; + ci = cipush(mrb, n, 0, c, NULL, mid, argc); + if (argc < 0) argc = 1; if (mrb->c->stbase <= argv && argv < mrb->c->stend) { voff = argv - mrb->c->stbase; } - if (MRB_METHOD_CFUNC_P(m)) { - mrb_stack_extend(mrb, argc + 2); - } - else if (argc >= CALL_MAXARGS) { + if (argc >= CALL_MAXARGS) { mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); - mrb_stack_extend(mrb, 3); - mrb->c->stack[1] = args; + mrb->c->ci->stack[1] = args; ci->argc = -1; argc = 1; } - else { + mrb_stack_extend(mrb, argc + 2); + if (MRB_METHOD_PROC_P(m)) { struct RProc *p = MRB_METHOD_PROC(m); - ci->proc = p; - if (argc < 0) argc = 1; - mrb_stack_extend(mrb, p->body.irep->nregs + argc); + mrb_vm_ci_proc_set(ci, p); + if (!MRB_PROC_CFUNC_P(p)) { + mrb_stack_extend(mrb, p->body.irep->nregs + argc); + } } if (voff >= 0) { argv = mrb->c->stbase + voff; } - mrb->c->stack[0] = self; + mrb->c->ci->stack[0] = self; if (ci->argc > 0) { - stack_copy(mrb->c->stack+1, argv, argc); + stack_copy(mrb->c->ci->stack+1, argv, argc); } - mrb->c->stack[argc+1] = blk; + mrb->c->ci->stack[argc+1] = blk; if (MRB_METHOD_CFUNC_P(m)) { - int ai = mrb_gc_arena_save(mrb); - ci->acc = CI_ACC_DIRECT; - if (MRB_METHOD_PROC_P(m)) { - ci->proc = MRB_METHOD_PROC(m); - } val = MRB_METHOD_CFUNC(m)(mrb, self); - mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); - mrb_gc_arena_restore(mrb, ai); } else { ci->acc = CI_ACC_SKIP; val = mrb_run(mrb, MRB_METHOD_PROC(m), self); } } + mrb_gc_arena_restore(mrb, ai); mrb_gc_protect(mrb, val); return val; } @@ -538,14 +526,14 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value()); } -mrb_value -mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) +static mrb_value +exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) { mrb_callinfo *ci = mrb->c->ci; int keep, nregs; - mrb->c->stack[0] = self; - ci->proc = p; + ci->stack[0] = self; + mrb_vm_ci_proc_set(ci, p); if (MRB_PROC_CFUNC_P(p)) { return MRB_PROC_CFUNC(p)(mrb, self); } @@ -557,18 +545,39 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) } else { mrb_stack_extend(mrb, nregs); - stack_clear(mrb->c->stack+keep, nregs-keep); + stack_clear(ci->stack+keep, nregs-keep); } - ci = cipush(mrb); - ci->target_class = 0; - ci->pc = p->body.irep->iseq; - ci->stackent = mrb->c->stack; - ci->acc = 0; + cipush(mrb, 0, 0, NULL, NULL, 0, 0); return self; } +mrb_value +mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) +{ + mrb_callinfo *ci = mrb->c->ci; + if (ci->acc >= 0) { + return exec_irep(mrb, self, p); + } + else { + mrb_value ret; + if (MRB_PROC_CFUNC_P(p)) { + cipush(mrb, 0, CI_ACC_DIRECT, mrb_vm_ci_target_class(ci), p, ci->mid, ci->argc); + ret = MRB_PROC_CFUNC(p)(mrb, self); + cipop(mrb); + } + else { + int keep = (ci->argc < 0 ? 1 : ci->argc) + 2 /* receiver + block */; + ret = mrb_top_run(mrb, p, self, keep); + } + if (mrb->exc && mrb->jmp) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->exc)); + } + return ret; + } +} + /* 15.3.1.3.4 */ /* 15.3.1.3.44 */ /* @@ -588,11 +597,12 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) * k = Klass.new * k.send :hello, "gentle", "readers" #=> "Hello gentle readers" */ -MRB_API mrb_value +mrb_value mrb_f_send(mrb_state *mrb, mrb_value self) { mrb_sym name; - mrb_value block, *argv, *regs; + mrb_value block, *regs; + const mrb_value *argv; mrb_int argc, i, len; mrb_method_t m; struct RClass *c; @@ -612,8 +622,8 @@ mrb_f_send(mrb_state *mrb, mrb_value self) } ci->mid = name; - ci->target_class = c; - regs = mrb->c->stack+1; + ci->u.target_class = c; + regs = mrb->c->ci->stack+1; /* remove first symbol from arguments */ if (ci->argc >= 0) { for (i=0,len=ci->argc; i<len; i++) { @@ -622,16 +632,16 @@ mrb_f_send(mrb_state *mrb, mrb_value self) ci->argc--; } else { /* variable length arguments */ - mrb_ary_shift(mrb, regs[0]); + regs[0] = mrb_ary_subseq(mrb, regs[0], 1, RARRAY_LEN(regs[0]) - 1); } if (MRB_METHOD_CFUNC_P(m)) { if (MRB_METHOD_PROC_P(m)) { - ci->proc = MRB_METHOD_PROC(m); + mrb_vm_ci_proc_set(ci, MRB_METHOD_PROC(m)); } return MRB_METHOD_CFUNC(m)(mrb, self); } - return mrb_exec_irep(mrb, self, MRB_METHOD_PROC(m)); + return exec_irep(mrb, self, MRB_METHOD_PROC(m)); } static mrb_value @@ -646,31 +656,27 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) } ci = mrb->c->ci; if (ci->acc == CI_ACC_DIRECT) { - ci->target_class = c; - return mrb_yield_cont(mrb, blk, self, 1, &self); + return mrb_yield_with_class(mrb, blk, 1, &self, self, c); } - ci->target_class = c; + ci->u.target_class = c; p = mrb_proc_ptr(blk); - ci->proc = p; + mrb_vm_ci_proc_set(ci, p); ci->argc = 1; ci->mid = ci[-1].mid; if (MRB_PROC_CFUNC_P(p)) { mrb_stack_extend(mrb, 3); - mrb->c->stack[0] = self; - mrb->c->stack[1] = self; - mrb->c->stack[2] = mrb_nil_value(); + mrb->c->ci->stack[0] = self; + mrb->c->ci->stack[1] = self; + mrb->c->ci->stack[2] = mrb_nil_value(); return MRB_PROC_CFUNC(p)(mrb, self); } nregs = p->body.irep->nregs; - mrb_stack_extend(mrb, (nregs < 3) ? 3 : nregs); - mrb->c->stack[0] = self; - mrb->c->stack[1] = self; - mrb->c->stack[2] = mrb_nil_value(); - ci = cipush(mrb); - ci->target_class = 0; - ci->pc = p->body.irep->iseq; - ci->stackent = mrb->c->stack; - ci->acc = 0; + if (nregs < 3) nregs = 3; + mrb_stack_extend(mrb, nregs); + mrb->c->ci->stack[0] = self; + mrb->c->ci->stack[1] = self; + stack_clear(mrb->c->ci->stack+2, nregs-2); + ci = cipush(mrb, 0, 0, NULL, NULL, 0, 0); return self; } @@ -721,26 +727,11 @@ mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self) { mrb_value a, b; - mrb_value cv; - struct RClass *c; if (mrb_get_args(mrb, "|S&", &a, &b) == 1) { mrb_raise(mrb, E_NOTIMP_ERROR, "instance_eval with string not implemented"); } - switch (mrb_type(self)) { - case MRB_TT_SYMBOL: - case MRB_TT_FIXNUM: -#ifndef MRB_WITHOUT_FLOAT - case MRB_TT_FLOAT: -#endif - c = 0; - break; - default: - cv = mrb_singleton_class(mrb, self); - c = mrb_class_ptr(cv); - break; - } - return eval_under(mrb, self, b, c); + return eval_under(mrb, self, b, mrb_singleton_class_ptr(mrb, self)); } MRB_API mrb_value @@ -750,7 +741,7 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value mrb_sym mid = mrb->c->ci->mid; mrb_callinfo *ci; mrb_value val; - int n; + mrb_int n; if (mrb_nil_p(b)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); @@ -761,26 +752,29 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } p = mrb_proc_ptr(b); - ci = cipush(mrb); - ci->mid = mid; - ci->proc = p; - ci->stackent = mrb->c->stack; - ci->argc = (int)argc; - ci->target_class = c; - ci->acc = CI_ACC_SKIP; - n = MRB_PROC_CFUNC_P(p) ? (int)(argc+2) : p->body.irep->nregs; - mrb->c->stack = mrb->c->stack + n; + ci = cipush(mrb, n, CI_ACC_SKIP, c, p, mid, 0 /* dummy */); + if (argc >= CALL_MAXARGS) { + ci->argc = -1; + n = 3; + } + else { + ci->argc = (int)argc; + n = argc + 2; + } mrb_stack_extend(mrb, n); - - mrb->c->stack[0] = self; - if (argc > 0) { - stack_copy(mrb->c->stack+1, argv, argc); + mrb->c->ci->stack[0] = self; + if (ci->argc < 0) { + mrb->c->ci->stack[1] = mrb_ary_new_from_values(mrb, argc, argv); + argc = 1; + } + else if (argc > 0) { + stack_copy(mrb->c->ci->stack+1, argv, argc); } - mrb->c->stack[argc+1] = mrb_nil_value(); + mrb->c->ci->stack[argc+1] = mrb_nil_value(); if (MRB_PROC_CFUNC_P(p)) { + ci->acc = CI_ACC_DIRECT; val = MRB_PROC_CFUNC(p)(mrb, self); - mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); } else { @@ -814,7 +808,7 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const if (mrb_nil_p(b)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } - if (mrb_type(b) != MRB_TT_PROC) { + if (!mrb_proc_p(b)) { mrb_raise(mrb, E_TYPE_ERROR, "not a block"); } @@ -822,47 +816,61 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const ci = mrb->c->ci; mrb_stack_extend(mrb, 3); - mrb->c->stack[1] = mrb_ary_new_from_values(mrb, argc, argv); - mrb->c->stack[2] = mrb_nil_value(); + mrb->c->ci->stack[1] = mrb_ary_new_from_values(mrb, argc, argv); + mrb->c->ci->stack[2] = mrb_nil_value(); ci->argc = -1; - return mrb_exec_irep(mrb, self, p); + return exec_irep(mrb, self, p); } -mrb_value -mrb_mod_s_nesting(mrb_state *mrb, mrb_value mod) +static struct RBreak* +break_new(mrb_state *mrb, uint32_t tag, const struct RProc *p, mrb_value val) { - struct RProc *proc; - mrb_value ary; - struct RClass *c = NULL; + struct RBreak *brk; - mrb_get_args(mrb, ""); - ary = mrb_ary_new(mrb); - proc = mrb->c->ci[-1].proc; /* callee proc */ - mrb_assert(!MRB_PROC_CFUNC_P(proc)); - while (proc) { - if (MRB_PROC_SCOPE_P(proc)) { - struct RClass *c2 = MRB_PROC_TARGET_CLASS(proc); + brk = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL); + mrb_break_proc_set(brk, p); + mrb_break_value_set(brk, val); + mrb_break_tag_set(brk, tag); - if (c2 != c) { - c = c2; - mrb_ary_push(mrb, ary, mrb_obj_value(c)); - } - } - proc = proc->upper; - } - return ary; + return brk; } -static struct RBreak* -break_new(mrb_state *mrb, struct RProc *p, mrb_value val) +#define MRB_CATCH_FILTER_RESCUE (UINT32_C(1) << MRB_CATCH_RESCUE) +#define MRB_CATCH_FILTER_ENSURE (UINT32_C(1) << MRB_CATCH_ENSURE) +#define MRB_CATCH_FILTER_ALL (MRB_CATCH_FILTER_RESCUE | MRB_CATCH_FILTER_ENSURE) + +static const struct mrb_irep_catch_handler * +catch_handler_find(mrb_state *mrb, mrb_callinfo *ci, const mrb_code *pc, uint32_t filter) { - struct RBreak *brk; + const mrb_irep *irep; + ptrdiff_t xpc; + size_t cnt; + const struct mrb_irep_catch_handler *e; + +/* The comparison operators use `>` and `<=` because pc already points to the next instruction */ +#define catch_cover_p(pc, beg, end) ((pc) > (ptrdiff_t)(beg) && (pc) <= (ptrdiff_t)(end)) + + if (ci->proc == NULL || MRB_PROC_CFUNC_P(ci->proc)) return NULL; + irep = ci->proc->body.irep; + if (irep->clen < 1) return NULL; + xpc = pc - irep->iseq; + /* If it retry at the top level, pc will be 0, so check with -1 as the start position */ + mrb_assert(catch_cover_p(xpc, -1, irep->ilen)); + if (!catch_cover_p(xpc, -1, irep->ilen)) return NULL; + + /* Currently uses a simple linear search to avoid processing complexity. */ + cnt = irep->clen; + e = mrb_irep_catch_handler_table(irep) + cnt - 1; + for (; cnt > 0; cnt --, e --) { + if (((UINT32_C(1) << e->type) & filter) && + catch_cover_p(xpc, mrb_irep_catch_handler_unpack(e->begin), mrb_irep_catch_handler_unpack(e->end))) { + return e; + } + } - brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL); - brk->proc = p; - brk->val = val; +#undef catch_cover_p - return brk; + return NULL; } typedef enum { @@ -895,27 +903,87 @@ argnum_error(mrb_state *mrb, mrb_int num) mrb_int argc = mrb->c->ci->argc; if (argc < 0) { - mrb_value args = mrb->c->stack[1]; + mrb_value args = mrb->c->ci->stack[1]; if (mrb_array_p(args)) { argc = RARRAY_LEN(args); } } if (mrb->c->ci->mid) { - str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)", - mrb_sym2str(mrb, mrb->c->ci->mid), - mrb_fixnum_value(argc), mrb_fixnum_value(num)); + str = mrb_format(mrb, "'%n': wrong number of arguments (%i for %i)", + mrb->c->ci->mid, argc, num); } else { - str = mrb_format(mrb, "wrong number of arguments (%S for %S)", - mrb_fixnum_value(argc), mrb_fixnum_value(num)); + str = mrb_format(mrb, "wrong number of arguments (%i for %i)", argc, num); } exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str); mrb_exc_set(mrb, exc); } -#define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc; -#define ERR_PC_CLR(mrb) mrb->c->ci->err = 0; -#ifdef MRB_ENABLE_DEBUG_HOOK +static mrb_bool +break_tag_p(struct RBreak *brk, uint32_t tag) +{ + return (brk != NULL && brk->tt == MRB_TT_BREAK) ? TRUE : FALSE; +} + +static void +prepare_tagged_break(mrb_state *mrb, uint32_t tag, const struct RProc *proc, mrb_value val) +{ + if (break_tag_p((struct RBreak*)mrb->exc, tag)) { + mrb_break_tag_set((struct RBreak*)mrb->exc, tag); + } + else { + mrb->exc = (struct RObject*)break_new(mrb, tag, proc, val); + } +} + +#define THROW_TAGGED_BREAK(mrb, tag, proc, val) \ + do { \ + prepare_tagged_break(mrb, tag, proc, val); \ + goto L_CATCH_TAGGED_BREAK; \ + } while (0) + +#define UNWIND_ENSURE(mrb, ci, pc, tag, proc, val) \ + do { \ + ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ENSURE); \ + if (ch) { \ + THROW_TAGGED_BREAK(mrb, tag, proc, val); \ + } \ + } while (0) + +/* + * CHECKPOINT_RESTORE(tag) { + * This part is executed when jumping by the same "tag" of RBreak (it is not executed the first time). + * Write the code required (initialization of variables, etc.) for the subsequent processing. + * } + * CHECKPOINT_MAIN(tag) { + * This part is always executed. + * } + * CHECKPOINT_END(tag); + * + * ... + * + * // Jump to CHECKPOINT_RESTORE with the same "tag". + * goto CHECKPOINT_LABEL_MAKE(tag); + */ + +#define CHECKPOINT_LABEL_MAKE(tag) L_CHECKPOINT_ ## tag + +#define CHECKPOINT_RESTORE(tag) \ + do { \ + if (FALSE) { \ + CHECKPOINT_LABEL_MAKE(tag): \ + do { + +#define CHECKPOINT_MAIN(tag) \ + } while (0); \ + } \ + do { + +#define CHECKPOINT_END(tag) \ + } while (0); \ + } while (0) + +#ifdef MRB_USE_DEBUG_HOOK #define CODE_FETCH_HOOK(mrb, irep, pc, regs) if ((mrb)->code_fetch_hook) (mrb)->code_fetch_hook((mrb), (irep), (pc), (regs)); #else #define CODE_FETCH_HOOK(mrb, irep, pc, regs) @@ -927,24 +995,24 @@ argnum_error(mrb_state *mrb, mrb_int num) #define BYTECODE_DECODER(x) (x) #endif -#ifndef MRB_DISABLE_DIRECT_THREADING +#ifndef MRB_NO_DIRECT_THREADING #if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER #define DIRECT_THREADED #endif -#endif /* ifndef MRB_DISABLE_DIRECT_THREADING */ +#endif /* ifndef MRB_NO_DIRECT_THREADING */ #ifndef DIRECT_THREADED -#define INIT_DISPATCH for (;;) { insn = BYTECODE_DECODER(*pc); pc++; CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (insn) { -#define CASE(insn,ops) case insn: pc++; FETCH_ ## ops ();; L_ ## insn ## _BODY: -#define NEXT break +#define INIT_DISPATCH for (;;) { insn = BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (insn) { +#define CASE(insn,ops) case insn: pc++; FETCH_ ## ops (); mrb->c->ci->pc = pc; L_ ## insn ## _BODY: +#define NEXT goto L_END_DISPATCH #define JUMP NEXT -#define END_DISPATCH }} +#define END_DISPATCH L_END_DISPATCH:;}} #else #define INIT_DISPATCH JUMP; return mrb_nil_value(); -#define CASE(insn,ops) L_ ## insn: pc++; FETCH_ ## ops (); L_ ## insn ## _BODY: +#define CASE(insn,ops) L_ ## insn: pc++; FETCH_ ## ops (); mrb->c->ci->pc = pc; L_ ## insn ## _BODY: #define NEXT insn=BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[insn] #define JUMP NEXT @@ -953,65 +1021,89 @@ argnum_error(mrb_state *mrb, mrb_int num) #endif MRB_API mrb_value -mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) +mrb_vm_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep) { - mrb_irep *irep = proc->body.irep; + const mrb_irep *irep = proc->body.irep; mrb_value result; struct mrb_context *c = mrb->c; ptrdiff_t cioff = c->ci - c->cibase; - unsigned int nregs = irep->nregs; + mrb_int nregs = irep->nregs; - if (!c->stack) { + if (!c->stbase) { stack_init(mrb); } if (stack_keep > nregs) nregs = stack_keep; mrb_stack_extend(mrb, nregs); - stack_clear(c->stack + stack_keep, nregs - stack_keep); - c->stack[0] = self; + stack_clear(c->ci->stack + stack_keep, nregs - stack_keep); + c->ci->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; } + else if (c->ci - c->cibase > cioff) { + c->ci = c->cibase + cioff; + } return result; } static mrb_bool check_target_class(mrb_state *mrb) { - if (!mrb->c->ci->target_class) { - mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "no target class or module"); + if (!mrb_vm_ci_target_class(mrb->c->ci)) { + mrb_value exc = mrb_exc_new_lit(mrb, E_TYPE_ERROR, "no target class or module"); mrb_exc_set(mrb, exc); return FALSE; } return TRUE; } +mrb_value +get_send_args(mrb_state *mrb, mrb_int argc, mrb_value *regs) +{ + if (argc < 0) return regs[0]; + return mrb_ary_new_from_values(mrb, argc, regs); +} + +static void +proc_adjust_upper(struct RProc *p) +{ + /* skip upper procs while unnamed blocks and method closures */ + while (p->upper) { + if (MRB_FLAG_TEST(p->upper, MRB_PROC_SCOPE) && + !MRB_FLAG_TEST(p->upper, MRB_PROC_STRICT)) { + break; + } + p->upper = p->upper->upper; + } +} + +mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod); void mrb_hash_check_kdict(mrb_state *mrb, mrb_value self); +void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid); MRB_API mrb_value -mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc) +mrb_vm_exec(mrb_state *mrb, const struct RProc *proc, const mrb_code *pc) { - /* mrb_assert(mrb_proc_cfunc_p(proc)) */ - mrb_irep *irep = proc->body.irep; - mrb_value *pool = irep->pool; - mrb_sym *syms = irep->syms; + /* mrb_assert(MRB_PROC_CFUNC_P(proc)) */ + const mrb_irep *irep = proc->body.irep; + const mrb_pool_value *pool = irep->pool; + const mrb_sym *syms = irep->syms; mrb_code insn; int ai = mrb_gc_arena_save(mrb); struct mrb_jmpbuf *prev_jmp = mrb->jmp; struct mrb_jmpbuf c_jmp; uint32_t a; uint16_t b; - uint8_t c; + uint16_t c; + mrb_sym mid; + const struct mrb_irep_catch_handler *ch; #ifdef DIRECT_THREADED - static void *optable[] = { + static const void * const optable[] = { #define OPCODE(x,_) &&L_OP_ ## x, #include "mruby/ops.h" #undef OPCODE @@ -1031,9 +1123,9 @@ RETRY_TRY_BLOCK: goto L_RAISE; } mrb->jmp = &c_jmp; - mrb->c->ci->proc = proc; + mrb_vm_ci_proc_set(mrb->c->ci, proc); -#define regs (mrb->c->stack) +#define regs (mrb->c->ci->stack) INIT_DISPATCH { CASE(OP_NOP, Z) { /* do nothing */ @@ -1046,27 +1138,45 @@ RETRY_TRY_BLOCK: } CASE(OP_LOADL, BB) { -#ifdef MRB_WORD_BOXING - mrb_value val = pool[b]; -#ifndef MRB_WITHOUT_FLOAT - if (mrb_float_p(val)) { - val = mrb_float_value(mrb, mrb_float(val)); - } -#endif - regs[a] = val; + switch (pool[b].tt) { /* number */ + case IREP_TT_INT32: + regs[a] = mrb_int_value(mrb, (mrb_int)pool[b].u.i32); + break; + case IREP_TT_INT64: +#if defined(MRB_INT64) + regs[a] = mrb_int_value(mrb, (mrb_int)pool[b].u.i64); + break; #else - regs[a] = pool[b]; +#if defined(MRB_64BIT) + if (INT32_MIN <= pool[b].u.i64 && pool[b].u.i64 <= INT32_MAX) { + regs[a] = mrb_int_value(mrb, (mrb_int)pool[b].u.i64); + break; + } #endif + goto L_INT_OVERFLOW; +#endif + case IREP_TT_BIGINT: + goto L_INT_OVERFLOW; +#ifndef MRB_NO_FLOAT + case IREP_TT_FLOAT: + regs[a] = mrb_float_value(mrb, pool[b].u.f); + break; +#endif + default: + /* should not happen (tt:string) */ + regs[a] = mrb_nil_value(); + break; + } NEXT; } CASE(OP_LOADI, BB) { - SET_INT_VALUE(regs[a], b); + SET_FIXNUM_VALUE(regs[a], b); NEXT; } CASE(OP_LOADINEG, BB) { - SET_INT_VALUE(regs[a], -b); + SET_FIXNUM_VALUE(regs[a], -b); NEXT; } @@ -1080,7 +1190,17 @@ RETRY_TRY_BLOCK: CASE(OP_LOADI_6,B) goto L_LOADI; CASE(OP_LOADI_7, B) { L_LOADI: - SET_INT_VALUE(regs[a], (mrb_int)insn - (mrb_int)OP_LOADI_0); + SET_FIXNUM_VALUE(regs[a], (mrb_int)insn - (mrb_int)OP_LOADI_0); + NEXT; + } + + CASE(OP_LOADI16, BS) { + SET_FIXNUM_VALUE(regs[a], (mrb_int)(int16_t)b); + NEXT; + } + + CASE(OP_LOADI32, BSS) { + SET_INT_VALUE(mrb, regs[a], (int32_t)(((uint32_t)b<<16)+c)); NEXT; } @@ -1121,13 +1241,13 @@ RETRY_TRY_BLOCK: } CASE(OP_GETSV, BB) { - mrb_value val = mrb_vm_special_get(mrb, b); + mrb_value val = mrb_vm_special_get(mrb, syms[b]); regs[a] = val; NEXT; } CASE(OP_SETSV, BB) { - mrb_vm_special_set(mrb, b, regs[a]); + mrb_vm_special_set(mrb, syms[b], regs[a]); NEXT; } @@ -1143,9 +1263,7 @@ RETRY_TRY_BLOCK: CASE(OP_GETCV, BB) { mrb_value val; - ERR_PC_SET(mrb, pc); val = mrb_vm_cv_get(mrb, syms[b]); - ERR_PC_CLR(mrb); regs[a] = val; NEXT; } @@ -1159,9 +1277,7 @@ RETRY_TRY_BLOCK: mrb_value val; mrb_sym sym = syms[b]; - ERR_PC_SET(mrb, pc); val = mrb_vm_const_get(mrb, sym); - ERR_PC_CLR(mrb); regs[a] = val; NEXT; } @@ -1174,9 +1290,7 @@ RETRY_TRY_BLOCK: CASE(OP_GETMCNST, BB) { mrb_value val; - ERR_PC_SET(mrb, pc); val = mrb_const_get(mrb, regs[a], syms[b]); - ERR_PC_CLR(mrb); regs[a] = val; NEXT; } @@ -1190,7 +1304,7 @@ RETRY_TRY_BLOCK: mrb_value *regs_a = regs + a; struct REnv *e = uvenv(mrb, c); - if (e && b < MRB_ENV_STACK_LEN(e)) { + if (e && b < MRB_ENV_LEN(e)) { *regs_a = e->stack[b]; } else { @@ -1205,7 +1319,7 @@ RETRY_TRY_BLOCK: if (e) { mrb_value *regs_a = regs + a; - if (b < MRB_ENV_STACK_LEN(e)) { + if (b < MRB_ENV_LEN(e)) { e->stack[b] = *regs_a; mrb_write_barrier(mrb, (struct RBasic*)e); } @@ -1214,57 +1328,75 @@ RETRY_TRY_BLOCK: } CASE(OP_JMP, S) { - pc = irep->iseq+a; + pc += (int16_t)a; JUMP; } CASE(OP_JMPIF, BS) { if (mrb_test(regs[a])) { - pc = irep->iseq+b; + pc += (int16_t)b; JUMP; } NEXT; } CASE(OP_JMPNOT, BS) { if (!mrb_test(regs[a])) { - pc = irep->iseq+b; + pc += (int16_t)b; JUMP; } NEXT; } CASE(OP_JMPNIL, BS) { if (mrb_nil_p(regs[a])) { - pc = irep->iseq+b; + pc += (int16_t)b; JUMP; } NEXT; } - CASE(OP_ONERR, S) { - /* check rescue stack */ - if (mrb->c->ci->ridx == UINT16_MAX-1) { - mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "too many nested rescues"); - mrb_exc_set(mrb, exc); - goto L_RAISE; + CASE(OP_JMPUW, S) { + a = (uint32_t)((pc - irep->iseq) + (int16_t)a); + CHECKPOINT_RESTORE(RBREAK_TAG_JUMP) { + struct RBreak *brk = (struct RBreak*)mrb->exc; + mrb_value target = mrb_break_value_get(brk); + mrb_assert(mrb_integer_p(target)); + a = (uint32_t)mrb_integer(target); + mrb_assert(a >= 0 && a < irep->ilen); } - /* expand rescue stack */ - if (mrb->c->rsize <= mrb->c->ci->ridx) { - if (mrb->c->rsize == 0) mrb->c->rsize = RESCUE_STACK_INIT_SIZE; - else { - mrb->c->rsize *= 2; - if (mrb->c->rsize <= mrb->c->ci->ridx) { - mrb->c->rsize = UINT16_MAX; + CHECKPOINT_MAIN(RBREAK_TAG_JUMP) { + ch = catch_handler_find(mrb, mrb->c->ci, pc, MRB_CATCH_FILTER_ENSURE); + if (ch) { + /* avoiding a jump from a catch handler into the same handler */ + if (a < mrb_irep_catch_handler_unpack(ch->begin) || a >= mrb_irep_catch_handler_unpack(ch->end)) { + THROW_TAGGED_BREAK(mrb, RBREAK_TAG_JUMP, proc, mrb_fixnum_value(a)); } } - mrb->c->rescue = (uint16_t*)mrb_realloc(mrb, mrb->c->rescue, sizeof(uint16_t)*mrb->c->rsize); } - /* push rescue stack */ - mrb->c->rescue[mrb->c->ci->ridx++] = a; - NEXT; + CHECKPOINT_END(RBREAK_TAG_JUMP); + + mrb->exc = NULL; /* clear break object */ + pc = irep->iseq + a; + JUMP; } CASE(OP_EXCEPT, B) { - mrb_value exc = mrb_obj_value(mrb->exc); - mrb->exc = 0; + mrb_value exc; + + if (mrb->exc == NULL) { + exc = mrb_nil_value(); + } + else { + switch (mrb->exc->tt) { + case MRB_TT_BREAK: + case MRB_TT_EXCEPTION: + exc = mrb_obj_value(mrb->exc); + break; + default: + mrb_assert(!"bad mrb_type"); + exc = mrb_nil_value(); + break; + } + mrb->exc = NULL; + } regs[a] = exc; NEXT; } @@ -1281,7 +1413,7 @@ RETRY_TRY_BLOCK: { mrb_value exc; - exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, + exc = mrb_exc_new_lit(mrb, E_TYPE_ERROR, "class or module required for rescue clause"); mrb_exc_set(mrb, exc); goto L_RAISE; @@ -1292,82 +1424,19 @@ RETRY_TRY_BLOCK: NEXT; } - CASE(OP_POPERR, B) { - mrb->c->ci->ridx -= a; - NEXT; - } - - CASE(OP_RAISE, B) { - mrb_exc_set(mrb, regs[a]); - goto L_RAISE; - } - - CASE(OP_EPUSH, B) { - struct RProc *p; - - p = mrb_closure_new(mrb, irep->reps[a]); - /* check ensure stack */ - if (mrb->c->eidx == UINT16_MAX-1) { - mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "too many nested ensures"); - mrb_exc_set(mrb, exc); - goto L_RAISE; + CASE(OP_RAISEIF, B) { + mrb_value exc = regs[a]; + if (mrb_break_p(exc)) { + mrb->exc = mrb_obj_ptr(exc); + goto L_BREAK; } - /* expand ensure stack */ - 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; - if (mrb->c->esize <= mrb->c->eidx) { - mrb->c->esize = UINT16_MAX; - } - } - mrb->c->ensure = (struct RProc**)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*)*mrb->c->esize); + mrb_exc_set(mrb, exc); + if (mrb->exc) { + goto L_RAISE; } - /* push ensure stack */ - mrb->c->ensure[mrb->c->eidx++] = p; - mrb->c->ensure[mrb->c->eidx] = NULL; - mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_EPOP, B) { - mrb_callinfo *ci = mrb->c->ci; - unsigned int n, epos = ci->epos; - mrb_value self = regs[0]; - struct RClass *target_class = ci->target_class; - - if (mrb->c->eidx <= epos) { - NEXT; - } - - if (a > (int)mrb->c->eidx - epos) - a = mrb->c->eidx - epos; - for (n=0; n<a; n++) { - int nregs = irep->nregs; - - proc = mrb->c->ensure[epos+n]; - mrb->c->ensure[epos+n] = NULL; - if (proc == NULL) continue; - irep = proc->body.irep; - ci = cipush(mrb); - ci->mid = ci[-1].mid; - ci->argc = 0; - ci->proc = proc; - ci->stackent = mrb->c->stack; - ci->target_class = target_class; - ci->pc = pc; - ci->acc = nregs; - mrb->c->stack += ci->acc; - mrb_stack_extend(mrb, irep->nregs); - regs[0] = self; - pc = irep->iseq; - } - pool = irep->pool; - syms = irep->syms; - mrb->c->eidx = epos; - JUMP; - } - CASE(OP_SENDV, BB) { c = CALL_MAXARGS; goto L_SEND; @@ -1386,38 +1455,52 @@ RETRY_TRY_BLOCK: SET_NIL_VALUE(regs[bidx]); goto L_SENDB; }; + L_SEND_SYM: + { + /* push nil after arguments */ + int bidx = (c == CALL_MAXARGS) ? a+2 : a+c+1; + SET_NIL_VALUE(regs[bidx]); + goto L_SENDB_SYM; + }; CASE(OP_SENDB, BBB) L_SENDB: + mid = syms[b]; + L_SENDB_SYM: { - int argc = (c == CALL_MAXARGS) ? -1 : c; - int bidx = (argc < 0) ? a+2 : a+c+1; + mrb_int argc = (c == CALL_MAXARGS) ? -1 : c; + mrb_int bidx = (argc < 0) ? a+2 : a+c+1; mrb_method_t m; struct RClass *cls; mrb_callinfo *ci = mrb->c->ci; mrb_value recv, blk; - mrb_sym mid = syms[b]; mrb_assert(bidx < irep->nregs); recv = regs[a]; blk = regs[bidx]; - if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { - blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); - /* The stack might have been reallocated during mrb_convert_type(), + if (!mrb_nil_p(blk) && !mrb_proc_p(blk)) { + blk = mrb_type_convert(mrb, blk, MRB_TT_PROC, MRB_SYM(to_proc)); + /* The stack might have been reallocated during mrb_type_convert(), see #3622 */ regs[bidx] = blk; } cls = mrb_class(mrb, recv); m = mrb_method_search_vm(mrb, &cls, mid); if (MRB_METHOD_UNDEF_P(m)) { - mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); - m = mrb_method_search_vm(mrb, &cls, missing); - if (MRB_METHOD_UNDEF_P(m) || (missing == mrb->c->ci->mid && mrb_obj_eq(mrb, regs[0], recv))) { - mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, c, regs+a+1); - ERR_PC_SET(mrb, pc); + mrb_sym missing = MRB_SYM(method_missing); + mrb_value args; + + if (mrb_func_basic_p(mrb, recv, missing, mrb_obj_missing)) { + method_missing: + args = get_send_args(mrb, argc, regs+a+1); mrb_method_missing(mrb, mid, recv, args); } + if (mid != missing) { + cls = mrb_class(mrb, recv); + } + m = mrb_method_search_vm(mrb, &cls, missing); + if (MRB_METHOD_UNDEF_P(m)) goto method_missing; /* just in case */ if (argc >= 0) { if (a+2 >= irep->nregs) { mrb_stack_extend(mrb, a+3); @@ -1431,25 +1514,20 @@ RETRY_TRY_BLOCK: } /* push callinfo */ - ci = cipush(mrb); - ci->mid = mid; - ci->stackent = mrb->c->stack; - ci->target_class = cls; - ci->argc = argc; - - ci->pc = pc; - ci->acc = a; - - /* prepare stack */ - mrb->c->stack += a; + ci = cipush(mrb, a, a, cls, NULL, mid, argc); if (MRB_METHOD_CFUNC_P(m)) { if (MRB_METHOD_PROC_P(m)) { struct RProc *p = MRB_METHOD_PROC(m); - ci->proc = p; + mrb_vm_ci_proc_set(ci, p); recv = p->body.func(mrb, recv); } + else if (MRB_METHOD_NOARG_P(m) && + (argc > 0 || (argc == -1 && RARRAY_LEN(regs[1]) != 0))) { + argnum_error(mrb, 0); + goto L_RAISE; + } else { recv = MRB_METHOD_FUNC(m)(mrb, recv); } @@ -1457,13 +1535,13 @@ RETRY_TRY_BLOCK: mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; ci = mrb->c->ci; - if (mrb_type(blk) == MRB_TT_PROC) { + if (mrb_proc_p(blk)) { struct RProc *p = mrb_proc_ptr(blk); - if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == ci[-1].env) { + if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == mrb_vm_ci_env(&ci[-1])) { p->flags |= MRB_PROC_ORPHAN; } } - if (!ci->target_class) { /* return from context modifying method (resume/yield) */ + if (!ci->u.target_class) { /* return from context modifying method (resume/yield) */ if (ci->acc == CI_ACC_RESUMED) { mrb->jmp = prev_jmp; return recv; @@ -1476,43 +1554,34 @@ RETRY_TRY_BLOCK: syms = irep->syms; } } - mrb->c->stack[0] = recv; + mrb->c->ci->stack[0] = recv; /* pop stackpos */ - mrb->c->stack = ci->stackent; + ci = cipop(mrb); pc = ci->pc; - cipop(mrb); - JUMP; } else { /* setup environment for calling method */ - proc = ci->proc = MRB_METHOD_PROC(m); + mrb_vm_ci_proc_set(ci, (proc = MRB_METHOD_PROC(m))); irep = proc->body.irep; pool = irep->pool; syms = irep->syms; mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : irep->nregs); pc = irep->iseq; - JUMP; } } + JUMP; CASE(OP_CALL, Z) { mrb_callinfo *ci; - mrb_value recv = mrb->c->stack[0]; + mrb_value recv = mrb->c->ci->stack[0]; struct RProc *m = mrb_proc_ptr(recv); /* replace callinfo */ ci = mrb->c->ci; - ci->target_class = MRB_PROC_TARGET_CLASS(m); - ci->proc = m; + ci->u.target_class = MRB_PROC_TARGET_CLASS(m); + mrb_vm_ci_proc_set(ci, m); if (MRB_PROC_ENV_P(m)) { - mrb_sym mid; - struct REnv *e = MRB_PROC_ENV(m); - - mid = e->mid; - if (mid) ci->mid = mid; - if (!e->stack) { - e->stack = mrb->c->stack; - } + ci->mid = MRB_PROC_ENV(m)->mid; } /* prepare stack */ @@ -1522,11 +1591,9 @@ RETRY_TRY_BLOCK: mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; /* pop stackpos */ - ci = mrb->c->ci; - mrb->c->stack = ci->stackent; - regs[ci->acc] = recv; + ci = cipop(mrb); pc = ci->pc; - cipop(mrb); + regs[ci[1].acc] = recv; irep = mrb->c->ci->proc->body.irep; pool = irep->pool; syms = irep->syms; @@ -1537,7 +1604,7 @@ RETRY_TRY_BLOCK: proc = m; irep = m->body.irep; if (!irep) { - mrb->c->stack[0] = mrb_nil_value(); + mrb->c->ci->stack[0] = mrb_nil_value(); a = 0; c = OP_R_NORMAL; goto L_OP_RETURN_BODY; @@ -1562,60 +1629,67 @@ RETRY_TRY_BLOCK: } CASE(OP_SUPER, BB) { - int argc = (b == CALL_MAXARGS) ? -1 : b; + mrb_int argc = (b == CALL_MAXARGS) ? -1 : b; int bidx = (argc < 0) ? a+2 : a+b+1; mrb_method_t m; struct RClass *cls; mrb_callinfo *ci = mrb->c->ci; mrb_value recv, blk; + const struct RProc *p = ci->proc; mrb_sym mid = ci->mid; - struct RClass* target_class = MRB_PROC_TARGET_CLASS(ci->proc); + struct RClass* target_class = MRB_PROC_TARGET_CLASS(p); + if (MRB_PROC_ENV_P(p) && p->e.env->mid && p->e.env->mid != mid) { /* alias support */ + mid = p->e.env->mid; /* restore old mid */ + } mrb_assert(bidx < irep->nregs); if (mid == 0 || !target_class) { - mrb_value exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); + mrb_value exc = mrb_exc_new_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); mrb_exc_set(mrb, exc); goto L_RAISE; } - if (target_class->tt == MRB_TT_MODULE) { - target_class = ci->target_class; + if (target_class->flags & MRB_FL_CLASS_IS_PREPENDED) { + target_class = mrb_vm_ci_target_class(ci); + } + else if (target_class->tt == MRB_TT_MODULE) { + target_class = mrb_vm_ci_target_class(ci); if (target_class->tt != MRB_TT_ICLASS) { - mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "superclass info lost [mruby limitations]"); - mrb_exc_set(mrb, exc); - goto L_RAISE; + goto super_typeerror; } } recv = regs[0]; if (!mrb_obj_is_kind_of(mrb, recv, target_class)) { - mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, + super_typeerror: ; + mrb_value exc = mrb_exc_new_lit(mrb, E_TYPE_ERROR, "self has wrong type to call super in this context"); mrb_exc_set(mrb, exc); goto L_RAISE; } blk = regs[bidx]; - if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { - blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); + if (!mrb_nil_p(blk) && !mrb_proc_p(blk)) { + blk = mrb_type_convert(mrb, blk, MRB_TT_PROC, MRB_SYM(to_proc)); /* The stack or ci stack might have been reallocated during - mrb_convert_type(), see #3622 and #3784 */ + mrb_type_convert(), see #3622 and #3784 */ regs[bidx] = blk; ci = mrb->c->ci; } cls = target_class->super; m = mrb_method_search_vm(mrb, &cls, mid); if (MRB_METHOD_UNDEF_P(m)) { - mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); + mrb_sym missing = MRB_SYM(method_missing); + mrb_value args; + if (mrb_func_basic_p(mrb, recv, missing, mrb_obj_missing)) { + super_missing: + args = get_send_args(mrb, argc, regs+a+1); + mrb_no_method_error(mrb, mid, args, "no superclass method '%n'", mid); + } if (mid != missing) { cls = mrb_class(mrb, recv); } m = mrb_method_search_vm(mrb, &cls, missing); - if (MRB_METHOD_UNDEF_P(m)) { - mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, b, regs+a+1); - ERR_PC_SET(mrb, pc); - mrb_method_missing(mrb, mid, recv, args); - } - mid = missing; + if (MRB_METHOD_UNDEF_P(m)) goto super_missing; /* just in case */ if (argc >= 0) { if (a+2 >= irep->nregs) { mrb_stack_extend(mrb, a+3); @@ -1624,32 +1698,28 @@ RETRY_TRY_BLOCK: regs[a+2] = blk; argc = -1; } - mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid)); + mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(mid)); + mid = missing; } /* push callinfo */ - ci = cipush(mrb); - ci->mid = mid; - ci->stackent = mrb->c->stack; - ci->target_class = cls; - ci->pc = pc; - ci->argc = argc; + ci = cipush(mrb, a, 0, cls, NULL, mid, argc); /* prepare stack */ - mrb->c->stack += a; - mrb->c->stack[0] = recv; + mrb->c->ci->stack[0] = recv; if (MRB_METHOD_CFUNC_P(m)) { mrb_value v; if (MRB_METHOD_PROC_P(m)) { - ci->proc = MRB_METHOD_PROC(m); + mrb_vm_ci_proc_set(ci, MRB_METHOD_PROC(m)); } v = MRB_METHOD_CFUNC(m)(mrb, recv); 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) */ + mrb_assert(!mrb_break_p(v)); + if (!mrb_vm_ci_target_class(ci)) { /* return from context modifying method (resume/yield) */ if (ci->acc == CI_ACC_RESUMED) { mrb->jmp = prev_jmp; return v; @@ -1662,11 +1732,9 @@ RETRY_TRY_BLOCK: syms = irep->syms; } } - mrb->c->stack[0] = v; - /* pop stackpos */ - mrb->c->stack = ci->stackent; + mrb->c->ci->stack[0] = v; + ci = cipop(mrb); pc = ci->pc; - cipop(mrb); JUMP; } else { @@ -1674,7 +1742,7 @@ RETRY_TRY_BLOCK: ci->acc = a; /* setup environment for calling method */ - proc = ci->proc = MRB_METHOD_PROC(m); + mrb_vm_ci_proc_set(ci, (proc = MRB_METHOD_PROC(m))); irep = proc->body.irep; pool = irep->pool; syms = irep->syms; @@ -1685,18 +1753,18 @@ RETRY_TRY_BLOCK: } CASE(OP_ARGARY, BS) { - int m1 = (b>>11)&0x3f; - int r = (b>>10)&0x1; - int m2 = (b>>5)&0x1f; - int kd = (b>>4)&0x1; - int lv = (b>>0)&0xf; + mrb_int m1 = (b>>11)&0x3f; + mrb_int r = (b>>10)&0x1; + mrb_int m2 = (b>>5)&0x1f; + mrb_int kd = (b>>4)&0x1; + mrb_int lv = (b>>0)&0xf; mrb_value *stack; - if (mrb->c->ci->mid == 0 || mrb->c->ci->target_class == NULL) { + if (mrb->c->ci->mid == 0 || mrb_vm_ci_target_class(mrb->c->ci) == NULL) { mrb_value exc; L_NOSUPER: - exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); + exc = mrb_exc_new_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); mrb_exc_set(mrb, exc); goto L_RAISE; } @@ -1704,7 +1772,7 @@ RETRY_TRY_BLOCK: else { struct REnv *e = uvenv(mrb, lv-1); if (!e) goto L_NOSUPER; - if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+kd+1) + if (MRB_ENV_LEN(e) <= m1+r+m2+kd+1) goto L_NOSUPER; stack = e->stack + 1; } @@ -1714,13 +1782,13 @@ RETRY_TRY_BLOCK: else { mrb_value *pp = NULL; struct RArray *rest; - int len = 0; + mrb_int len = 0; if (mrb_array_p(stack[m1])) { struct RArray *ary = mrb_ary_ptr(stack[m1]); pp = ARY_PTR(ary); - len = (int)ARY_LEN(ary); + len = ARY_LEN(ary); } regs[a] = mrb_ary_new_capa(mrb, m1+len+m2+kd); rest = mrb_ary_ptr(regs[a]); @@ -1744,22 +1812,22 @@ RETRY_TRY_BLOCK: } CASE(OP_ENTER, W) { - int m1 = MRB_ASPEC_REQ(a); - int o = MRB_ASPEC_OPT(a); - int r = MRB_ASPEC_REST(a); - int m2 = MRB_ASPEC_POST(a); - int kd = (MRB_ASPEC_KEY(a) > 0 || MRB_ASPEC_KDICT(a))? 1 : 0; + mrb_int m1 = MRB_ASPEC_REQ(a); + mrb_int o = MRB_ASPEC_OPT(a); + mrb_int r = MRB_ASPEC_REST(a); + mrb_int m2 = MRB_ASPEC_POST(a); + mrb_int kd = (MRB_ASPEC_KEY(a) > 0 || MRB_ASPEC_KDICT(a))? 1 : 0; /* unused int b = MRB_ASPEC_BLOCK(a); */ - int argc = mrb->c->ci->argc; + mrb_int argc = mrb->c->ci->argc; mrb_value *argv = regs+1; mrb_value * const argv0 = argv; - int const len = m1 + o + r + m2; - int const blk_pos = len + kd + 1; + mrb_int const len = m1 + o + r + m2; + mrb_int const blk_pos = len + kd + 1; mrb_value *blk = &argv[argc < 0 ? 1 : argc]; - mrb_value kdict; - int kargs = kd; + mrb_value kdict = mrb_nil_value(); + mrb_int kargs = kd; /* arguments is passed with Array */ if (argc < 0) { @@ -1771,11 +1839,9 @@ RETRY_TRY_BLOCK: /* strict argument check */ if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) { - if (argc >= 0 && !(argc <= 1 && kd)) { - if (argc < m1 + m2 + kd || (r == 0 && argc > len + kd)) { - argnum_error(mrb, m1+m2); - goto L_RAISE; - } + if (argc < m1 + m2 || (r == 0 && argc > len + kd)) { + argnum_error(mrb, m1+m2); + goto L_RAISE; } } /* extract first argument array to arguments */ @@ -1796,13 +1862,13 @@ RETRY_TRY_BLOCK: kdict = argv[argc-1]; mrb_hash_check_kdict(mrb, kdict); } - else if (r) { + else if (r || argc <= m1+m2+o + || !(mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc))) { kdict = mrb_hash_new(mrb); kargs = 0; } else { - mrb_value str = mrb_str_new_lit(mrb, "Excepcted `Hash` as last argument for keyword arguments"); - mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str)); + argnum_error(mrb, m1+m2); goto L_RAISE; } if (MRB_ASPEC_KEY(a) > 0) { @@ -1813,7 +1879,7 @@ RETRY_TRY_BLOCK: /* no rest arguments */ if (argc-kargs < len) { - int mlen = m2; + mrb_int mlen = m2; if (argc < m1+m2) { mlen = m1 < argc ? argc - m1 : 0; } @@ -1821,7 +1887,7 @@ RETRY_TRY_BLOCK: if (kd) regs[len + 1] = kdict; /* copy mandatory and optional arguments */ - if (argv0 != argv) { + if (argv0 != argv && argv) { value_move(®s[1], argv, argc-mlen); /* m1 + o */ } if (argc < m1) { @@ -1834,16 +1900,16 @@ RETRY_TRY_BLOCK: if (mlen < m2) { stack_clear(®s[len-m2+mlen+1], m2-mlen); } - /* initalize rest arguments with empty Array */ + /* initialize rest arguments with empty Array */ if (r) { regs[m1+o+1] = mrb_ary_new_capa(mrb, 0); } - /* skip initailizer of passed arguments */ + /* skip initializer of passed arguments */ if (o > 0 && argc-kargs > m1+m2) pc += (argc - kargs - m1 - m2)*3; } else { - int rnum = 0; + mrb_int rnum = 0; if (argv0 != argv) { regs[blk_pos] = *blk; /* move block */ if (kd) regs[len + 1] = kdict; @@ -1869,7 +1935,7 @@ RETRY_TRY_BLOCK: } /* format arguments for generated code */ - mrb->c->ci->argc = len + kd; + mrb->c->ci->argc = (int16_t)(len + kd); /* clear local (but non-argument) variables */ if (irep->nlocals-blk_pos-1 > 0) { @@ -1882,8 +1948,8 @@ RETRY_TRY_BLOCK: mrb_value k = mrb_symbol_value(syms[b]); mrb_value kdict = regs[mrb->c->ci->argc]; - if (!mrb_hash_key_p(mrb, kdict, k)) { - mrb_value str = mrb_format(mrb, "missing keyword: %S", k); + if (!mrb_hash_p(kdict) || !mrb_hash_key_p(mrb, kdict, k)) { + mrb_value str = mrb_format(mrb, "missing keyword: %v", k); mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str)); goto L_RAISE; } @@ -1895,8 +1961,11 @@ RETRY_TRY_BLOCK: CASE(OP_KEY_P, BB) { mrb_value k = mrb_symbol_value(syms[b]); mrb_value kdict = regs[mrb->c->ci->argc]; - mrb_bool key_p = mrb_hash_key_p(mrb, kdict, k); + mrb_bool key_p = FALSE; + if (mrb_hash_p(kdict)) { + key_p = mrb_hash_key_p(mrb, kdict, k); + } regs[a] = mrb_bool_value(key_p); NEXT; } @@ -1907,7 +1976,7 @@ RETRY_TRY_BLOCK: if (mrb_hash_p(kdict) && !mrb_hash_empty_p(mrb, kdict)) { mrb_value keys = mrb_hash_keys(mrb, kdict); mrb_value key1 = RARRAY_PTR(keys)[0]; - mrb_value str = mrb_format(mrb, "unknown keyword: %S", key1); + mrb_value str = mrb_format(mrb, "unknown keyword: %v", key1); mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str)); goto L_RAISE; } @@ -1926,13 +1995,7 @@ RETRY_TRY_BLOCK: c = OP_R_NORMAL; L_RETURN: { - mrb_callinfo *ci; - -#define ecall_adjust() do {\ - ptrdiff_t cioff = ci - mrb->c->cibase;\ - ecall(mrb);\ - ci = mrb->c->cibase + cioff;\ -} while (0) + mrb_callinfo *ci; ci = mrb->c->ci; if (ci->mid) { @@ -1944,50 +2007,43 @@ RETRY_TRY_BLOCK: else { blk = regs[ci->argc+1]; } - if (mrb_type(blk) == MRB_TT_PROC) { + if (mrb_proc_p(blk)) { struct RProc *p = mrb_proc_ptr(blk); if (!MRB_PROC_STRICT_P(p) && - ci > mrb->c->cibase && MRB_PROC_ENV(p) == ci[-1].env) { + ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb_vm_ci_env(&ci[-1])) { p->flags |= MRB_PROC_ORPHAN; } } } if (mrb->exc) { - mrb_callinfo *ci0; - L_RAISE: - ci0 = ci = mrb->c->ci; + ci = mrb->c->ci; if (ci == mrb->c->cibase) { - if (ci->ridx == 0) goto L_FTOP; - goto L_RESCUE; + ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL); + if (ch == NULL) goto L_FTOP; + goto L_CATCH; } - while (ci[0].ridx == ci[-1].ridx) { - cipop(mrb); - mrb->c->stack = ci->stackent; - if (ci->acc == CI_ACC_SKIP && prev_jmp) { + while ((ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL)) == NULL) { + ci = cipop(mrb); + if (ci[1].acc == CI_ACC_SKIP && prev_jmp) { mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } - ci = mrb->c->ci; + pc = ci[0].pc; if (ci == mrb->c->cibase) { - if (ci->ridx == 0) { + ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL); + if (ch == NULL) { L_FTOP: /* fiber top */ if (mrb->c == mrb->root_c) { - mrb->c->stack = mrb->c->stbase; + mrb->c->ci->stack = mrb->c->stbase; goto L_STOP; } else { struct mrb_context *c = mrb->c; - while (c->eidx > ci->epos) { - ecall_adjust(); - } - if (c->fib) { - mrb_write_barrier(mrb, (struct RBasic*)c->fib); - } - mrb->c->status = MRB_FIBER_TERMINATED; + c->status = MRB_FIBER_TERMINATED; mrb->c = c->prev; c->prev = NULL; goto L_RAISE; @@ -1995,29 +2051,23 @@ RETRY_TRY_BLOCK: } break; } - /* call ensure only when we skip this callinfo */ - if (ci[0].ridx == ci[-1].ridx) { - while (mrb->c->eidx > ci->epos) { - ecall_adjust(); - } - } } - L_RESCUE: - if (ci->ridx == 0) goto L_STOP; + L_CATCH: + if (ch == NULL) goto L_STOP; + if (FALSE) { + L_CATCH_TAGGED_BREAK: /* from THROW_TAGGED_BREAK() or UNWIND_ENSURE() */ + ci = mrb->c->ci; + } proc = ci->proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; - if (ci < ci0) { - mrb->c->stack = ci[1].stackent; - } mrb_stack_extend(mrb, irep->nregs); - pc = irep->iseq+mrb->c->rescue[--ci->ridx]; + pc = irep->iseq + mrb_irep_catch_handler_unpack(ch->target); } else { - int acc; + mrb_int acc; mrb_value v; - struct RProc *dst; ci = mrb->c->ci; v = regs[a]; @@ -2026,55 +2076,92 @@ RETRY_TRY_BLOCK: case OP_R_RETURN: /* Fall through to OP_R_NORMAL otherwise */ if (ci->acc >=0 && MRB_PROC_ENV_P(proc) && !MRB_PROC_STRICT_P(proc)) { - mrb_callinfo *cibase = mrb->c->cibase; + const struct RProc *dst; + mrb_callinfo *cibase; + cibase = mrb->c->cibase; dst = top_proc(mrb, proc); if (MRB_PROC_ENV_P(dst)) { struct REnv *e = MRB_PROC_ENV(dst); - if (!MRB_ENV_STACK_SHARED_P(e) || e->cxt != mrb->c) { + if (!MRB_ENV_ONSTACK_P(e) || (e->cxt && e->cxt != mrb->c)) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } } + /* check jump destination */ while (cibase <= ci && ci->proc != dst) { - if (ci->acc < 0) { + if (ci->acc < 0) { /* jump cross C boundary */ localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } ci--; } - if (ci <= cibase) { + if (ci <= cibase) { /* no jump destination */ localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } + ci = mrb->c->ci; + while (cibase <= ci && ci->proc != dst) { + CHECKPOINT_RESTORE(RBREAK_TAG_RETURN_BLOCK) { + cibase = mrb->c->cibase; + dst = top_proc(mrb, proc); + } + CHECKPOINT_MAIN(RBREAK_TAG_RETURN_BLOCK) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN_BLOCK, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_RETURN_BLOCK); + ci = cipop(mrb); + pc = ci->pc; + } + proc = ci->proc; + mrb->exc = NULL; /* clear break object */ break; } /* fallthrough */ case OP_R_NORMAL: NORMAL_RETURN: if (ci == mrb->c->cibase) { - struct mrb_context *c = mrb->c; + struct mrb_context *c; + c = mrb->c; if (!c->prev) { /* toplevel return */ regs[irep->nlocals] = v; - goto L_STOP; + goto CHECKPOINT_LABEL_MAKE(RBREAK_TAG_STOP); } - if (c->prev->ci == c->prev->cibase) { - mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume"); + if (!c->vmexec && c->prev->ci == c->prev->cibase) { + mrb_value exc = mrb_exc_new_lit(mrb, E_FIBER_ERROR, "double resume"); mrb_exc_set(mrb, exc); goto L_RAISE; } - while (c->eidx > 0) { - ecall(mrb); + CHECKPOINT_RESTORE(RBREAK_TAG_RETURN_TOPLEVEL) { + c = mrb->c; } + CHECKPOINT_MAIN(RBREAK_TAG_RETURN_TOPLEVEL) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN_TOPLEVEL, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_RETURN_TOPLEVEL); /* automatic yield at the end */ c->status = MRB_FIBER_TERMINATED; mrb->c = c->prev; - c->prev = NULL; mrb->c->status = MRB_FIBER_RUNNING; + c->prev = NULL; + if (c->vmexec) { + mrb_gc_arena_restore(mrb, ai); + c->vmexec = FALSE; + mrb->jmp = prev_jmp; + return v; + } ci = mrb->c->ci; } + CHECKPOINT_RESTORE(RBREAK_TAG_RETURN) { + /* do nothing */ + } + CHECKPOINT_MAIN(RBREAK_TAG_RETURN) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_RETURN); + mrb->exc = NULL; /* clear break object */ break; case OP_R_BREAK: if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN; @@ -2082,12 +2169,12 @@ RETRY_TRY_BLOCK: mrb_value exc; L_BREAK_ERROR: - exc = mrb_exc_new_str_lit(mrb, E_LOCALJUMP_ERROR, + exc = mrb_exc_new_lit(mrb, E_LOCALJUMP_ERROR, "break from proc-closure"); mrb_exc_set(mrb, exc); goto L_RAISE; } - if (!MRB_PROC_ENV_P(proc) || !MRB_ENV_STACK_SHARED_P(MRB_PROC_ENV(proc))) { + if (!MRB_PROC_ENV_P(proc) || !MRB_ENV_ONSTACK_P(MRB_PROC_ENV(proc))) { goto L_BREAK_ERROR; } else { @@ -2097,9 +2184,13 @@ RETRY_TRY_BLOCK: goto L_BREAK_ERROR; } } - while (mrb->c->eidx > mrb->c->ci->epos) { - ecall_adjust(); + CHECKPOINT_RESTORE(RBREAK_TAG_BREAK) { + /* do nothing */ + } + CHECKPOINT_MAIN(RBREAK_TAG_BREAK) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK, proc, v); } + CHECKPOINT_END(RBREAK_TAG_BREAK); /* break from fiber block */ if (ci == mrb->c->cibase && ci->pc) { struct mrb_context *c = mrb->c; @@ -2109,62 +2200,78 @@ RETRY_TRY_BLOCK: ci = mrb->c->ci; } if (ci->acc < 0) { + ci = cipop(mrb); mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; - mrb->exc = (struct RObject*)break_new(mrb, proc, v); + mrb->exc = (struct RObject*)break_new(mrb, RBREAK_TAG_BREAK, proc, v); mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } if (FALSE) { + struct RBreak *brk; + L_BREAK: - v = ((struct RBreak*)mrb->exc)->val; - proc = ((struct RBreak*)mrb->exc)->proc; - mrb->exc = NULL; + brk = (struct RBreak*)mrb->exc; + proc = mrb_break_proc_get(brk); + v = mrb_break_value_get(brk); ci = mrb->c->ci; + + switch (mrb_break_tag_get(brk)) { +#define DISPATCH_CHECKPOINTS(n, i) case n: goto CHECKPOINT_LABEL_MAKE(n); + RBREAK_TAG_FOREACH(DISPATCH_CHECKPOINTS) +#undef DISPATCH_CHECKPOINTS + default: + mrb_assert(!"wrong break tag"); + } } - mrb->c->stack = ci->stackent; - proc = proc->upper; - while (mrb->c->cibase < ci && ci[-1].proc != proc) { + while (mrb->c->cibase < ci && ci[-1].proc != proc->upper) { if (ci[-1].acc == CI_ACC_SKIP) { - while (ci < mrb->c->ci) { - cipop(mrb); - } goto L_BREAK_ERROR; } - ci--; + CHECKPOINT_RESTORE(RBREAK_TAG_BREAK_UPPER) { + /* do nothing */ + } + CHECKPOINT_MAIN(RBREAK_TAG_BREAK_UPPER) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK_UPPER, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_BREAK_UPPER); + ci = cipop(mrb); + pc = ci->pc; } + CHECKPOINT_RESTORE(RBREAK_TAG_BREAK_INTARGET) { + /* do nothing */ + } + CHECKPOINT_MAIN(RBREAK_TAG_BREAK_INTARGET) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK_INTARGET, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_BREAK_INTARGET); if (ci == mrb->c->cibase) { goto L_BREAK_ERROR; } + mrb->exc = NULL; /* clear break object */ break; default: /* cannot happen */ break; } - while (ci < mrb->c->ci) { - cipop(mrb); - } - ci[0].ridx = ci[-1].ridx; - while (mrb->c->eidx > ci->epos) { - ecall_adjust(); - } - if (mrb->c->vmexec && !ci->target_class) { + mrb_assert(ci == mrb->c->ci); + mrb_assert(mrb->exc == NULL); + + if (mrb->c->vmexec && !mrb_vm_ci_target_class(ci)) { mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; mrb->jmp = prev_jmp; return v; } acc = ci->acc; - mrb->c->stack = ci->stackent; - cipop(mrb); + ci = 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; - ci = mrb->c->ci; - DEBUG(fprintf(stderr, "from :%s\n", mrb_sym2name(mrb, ci->mid))); + pc = ci[0].pc; + DEBUG(fprintf(stderr, "from :%s\n", mrb_sym_name(mrb, ci->mid))); proc = mrb->c->ci->proc; irep = proc->body.irep; pool = irep->pool; @@ -2187,8 +2294,8 @@ RETRY_TRY_BLOCK: if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); - if (!e || (!MRB_ENV_STACK_SHARED_P(e) && e->mid == 0) || - MRB_ENV_STACK_LEN(e) <= m1+r+m2+1) { + if (!e || (!MRB_ENV_ONSTACK_P(e) && e->mid == 0) || + MRB_ENV_LEN(e) <= m1+r+m2+1) { localjump_error(mrb, LOCALJUMP_ERROR_YIELD); goto L_RAISE; } @@ -2202,210 +2309,98 @@ RETRY_TRY_BLOCK: NEXT; } -#define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff)) -#define OP_MATH_BODY(op,v1,v2) do {\ - v1(regs[a]) = v1(regs[a]) op v2(regs[a+1]);\ -} while(0) - - CASE(OP_ADD, BB) { - /* need to check if op is overridden */ - switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): - { - mrb_int x, y, z; - mrb_value *regs_a = regs + a; - - x = mrb_fixnum(regs_a[0]); - y = mrb_fixnum(regs_a[1]); - if (mrb_int_add_overflow(x, y, &z)) { -#ifndef MRB_WITHOUT_FLOAT - SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y); - break; -#endif - } - SET_INT_VALUE(regs[a], z); - } - break; -#ifndef MRB_WITHOUT_FLOAT - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): - { - mrb_int x = mrb_fixnum(regs[a]); - mrb_float y = mrb_float(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + y); - } - break; - case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): -#ifdef MRB_WORD_BOXING - { - mrb_float x = mrb_float(regs[a]); - mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], x + y); - } -#else - OP_MATH_BODY(+,mrb_float,mrb_fixnum); -#endif - break; - case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): -#ifdef MRB_WORD_BOXING - { - mrb_float x = mrb_float(regs[a]); - mrb_float y = mrb_float(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], x + y); - } -#else - OP_MATH_BODY(+,mrb_float,mrb_float); -#endif - break; -#endif - case TYPES2(MRB_TT_STRING,MRB_TT_STRING): - regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]); - break; - default: - c = 1; - goto L_SEND; - } - mrb_gc_arena_restore(mrb, ai); - NEXT; + L_INT_OVERFLOW: + { + mrb_value exc = mrb_exc_new_lit(mrb, E_RANGE_ERROR, "integer overflow"); + mrb_exc_set(mrb, exc); } + goto L_RAISE; - CASE(OP_SUB, BB) { - /* need to check if op is overridden */ - switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): - { - mrb_int x, y, z; - - x = mrb_fixnum(regs[a]); - y = mrb_fixnum(regs[a+1]); - if (mrb_int_sub_overflow(x, y, &z)) { -#ifndef MRB_WITHOUT_FLOAT - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y); - break; -#endif - } - SET_INT_VALUE(regs[a], z); - } - break; -#ifndef MRB_WITHOUT_FLOAT - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): - { - mrb_int x = mrb_fixnum(regs[a]); - mrb_float y = mrb_float(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - y); - } - break; - case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): -#ifdef MRB_WORD_BOXING - { - mrb_float x = mrb_float(regs[a]); - mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], x - y); - } -#else - OP_MATH_BODY(-,mrb_float,mrb_fixnum); -#endif - break; - case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): -#ifdef MRB_WORD_BOXING - { - mrb_float x = mrb_float(regs[a]); - mrb_float y = mrb_float(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], x - y); - } +#define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff)) +#define OP_MATH(op_name) \ + /* need to check if op is overridden */ \ + switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { \ + OP_MATH_CASE_INTEGER(op_name); \ + OP_MATH_CASE_FLOAT(op_name, integer, float); \ + OP_MATH_CASE_FLOAT(op_name, float, integer); \ + OP_MATH_CASE_FLOAT(op_name, float, float); \ + OP_MATH_CASE_STRING_##op_name(); \ + default: \ + c = 1; \ + mid = MRB_OPSYM(op_name); \ + goto L_SEND_SYM; \ + } \ + NEXT; +#define OP_MATH_CASE_INTEGER(op_name) \ + case TYPES2(MRB_TT_INTEGER, MRB_TT_INTEGER): \ + { \ + mrb_int x = mrb_integer(regs[a]), y = mrb_integer(regs[a+1]), z; \ + if (mrb_int_##op_name##_overflow(x, y, &z)) \ + OP_MATH_OVERFLOW_INT(); \ + else \ + SET_INT_VALUE(mrb,regs[a], z); \ + } \ + break +#ifdef MRB_NO_FLOAT +#define OP_MATH_CASE_FLOAT(op_name, t1, t2) (void)0 #else - OP_MATH_BODY(-,mrb_float,mrb_float); +#define OP_MATH_CASE_FLOAT(op_name, t1, t2) \ + case TYPES2(OP_MATH_TT_##t1, OP_MATH_TT_##t2): \ + { \ + mrb_float z = mrb_##t1(regs[a]) OP_MATH_OP_##op_name mrb_##t2(regs[a+1]); \ + SET_FLOAT_VALUE(mrb, regs[a], z); \ + } \ + break #endif - break; -#endif - default: - c = 1; - goto L_SEND; - } - NEXT; +#define OP_MATH_OVERFLOW_INT() goto L_INT_OVERFLOW +#define OP_MATH_CASE_STRING_add() \ + case TYPES2(MRB_TT_STRING, MRB_TT_STRING): \ + regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]); \ + mrb_gc_arena_restore(mrb, ai); \ + break +#define OP_MATH_CASE_STRING_sub() (void)0 +#define OP_MATH_CASE_STRING_mul() (void)0 +#define OP_MATH_OP_add + +#define OP_MATH_OP_sub - +#define OP_MATH_OP_mul * +#define OP_MATH_TT_integer MRB_TT_INTEGER +#define OP_MATH_TT_float MRB_TT_FLOAT + + CASE(OP_ADD, B) { + OP_MATH(add); } - CASE(OP_MUL, BB) { - /* need to check if op is overridden */ - switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): - { - mrb_int x, y, z; + CASE(OP_SUB, B) { + OP_MATH(sub); + } - x = mrb_fixnum(regs[a]); - y = mrb_fixnum(regs[a+1]); - if (mrb_int_mul_overflow(x, y, &z)) { -#ifndef MRB_WITHOUT_FLOAT - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * (mrb_float)y); - break; -#endif - } - SET_INT_VALUE(regs[a], z); - } - break; -#ifndef MRB_WITHOUT_FLOAT - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): - { - mrb_int x = mrb_fixnum(regs[a]); - mrb_float y = mrb_float(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * y); - } - break; - case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): -#ifdef MRB_WORD_BOXING - { - mrb_float x = mrb_float(regs[a]); - mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], x * y); - } -#else - OP_MATH_BODY(*,mrb_float,mrb_fixnum); -#endif - break; - case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): -#ifdef MRB_WORD_BOXING - { - mrb_float x = mrb_float(regs[a]); - mrb_float y = mrb_float(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], x * y); - } -#else - OP_MATH_BODY(*,mrb_float,mrb_float); -#endif - break; -#endif - default: - c = 1; - goto L_SEND; - } - NEXT; + CASE(OP_MUL, B) { + OP_MATH(mul); } - CASE(OP_DIV, BB) { -#ifndef MRB_WITHOUT_FLOAT - double x, y, f; + CASE(OP_DIV, B) { +#ifndef MRB_NO_FLOAT + mrb_float x, y, f; #endif /* need to check if op is overridden */ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) { - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM): -#ifdef MRB_WITHOUT_FLOAT + case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER): { - mrb_int x = mrb_fixnum(regs[a]); - mrb_int y = mrb_fixnum(regs[a+1]); - SET_INT_VALUE(regs[a], y ? x / y : 0); + mrb_int x = mrb_integer(regs[a]); + mrb_int y = mrb_integer(regs[a+1]); + mrb_int div = mrb_div_int(mrb, x, y); + SET_INT_VALUE(mrb, regs[a], div); } - break; -#else - x = (mrb_float)mrb_fixnum(regs[a]); - y = (mrb_float)mrb_fixnum(regs[a+1]); - break; - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT): - x = (mrb_float)mrb_fixnum(regs[a]); + NEXT; +#ifndef MRB_NO_FLOAT + case TYPES2(MRB_TT_INTEGER,MRB_TT_FLOAT): + x = (mrb_float)mrb_integer(regs[a]); y = mrb_float(regs[a+1]); break; - case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): + case TYPES2(MRB_TT_FLOAT,MRB_TT_INTEGER): x = mrb_float(regs[a]); - y = (mrb_float)mrb_fixnum(regs[a+1]); + y = (mrb_float)mrb_integer(regs[a+1]); break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): x = mrb_float(regs[a]); @@ -2414,114 +2409,73 @@ RETRY_TRY_BLOCK: #endif default: c = 1; - goto L_SEND; + mid = MRB_OPSYM(div); + goto L_SEND_SYM; } -#ifndef MRB_WITHOUT_FLOAT - if (y == 0) { - if (x > 0) f = INFINITY; - else if (x < 0) f = -INFINITY; - else /* if (x == 0) */ f = NAN; - } - else { - f = x / y; - } +#ifndef MRB_NO_FLOAT + f = mrb_div_float(x, y); SET_FLOAT_VALUE(mrb, regs[a], f); #endif NEXT; } - CASE(OP_ADDI, BBB) { - /* need to check if + is overridden */ - switch (mrb_type(regs[a])) { - case MRB_TT_FIXNUM: - { - mrb_int x = mrb_fixnum(regs[a]); - mrb_int y = (mrb_int)c; - mrb_int z; - - if (mrb_int_add_overflow(x, y, &z)) { -#ifndef MRB_WITHOUT_FLOAT - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y); - break; -#endif - } - SET_INT_VALUE(regs[a], z); - } - break; -#ifndef MRB_WITHOUT_FLOAT - case MRB_TT_FLOAT: -#ifdef MRB_WORD_BOXING - { - mrb_float x = mrb_float(regs[a]); - SET_FLOAT_VALUE(mrb, regs[a], x + c); - } +#define OP_MATHI(op_name) \ + /* need to check if op is overridden */ \ + switch (mrb_type(regs[a])) { \ + OP_MATHI_CASE_INTEGER(op_name); \ + OP_MATHI_CASE_FLOAT(op_name); \ + default: \ + SET_INT_VALUE(mrb,regs[a+1], b); \ + c = 1; \ + mid = MRB_OPSYM(op_name); \ + goto L_SEND_SYM; \ + } \ + NEXT; +#define OP_MATHI_CASE_INTEGER(op_name) \ + case MRB_TT_INTEGER: \ + { \ + mrb_int x = mrb_integer(regs[a]), y = (mrb_int)b, z; \ + if (mrb_int_##op_name##_overflow(x, y, &z)) \ + OP_MATH_OVERFLOW_INT(); \ + else \ + SET_INT_VALUE(mrb,regs[a], z); \ + } \ + break +#ifdef MRB_NO_FLOAT +#define OP_MATHI_CASE_FLOAT(op_name) (void)0 #else - mrb_float(regs[a]) += c; +#define OP_MATHI_CASE_FLOAT(op_name) \ + case MRB_TT_FLOAT: \ + { \ + mrb_float z = mrb_float(regs[a]) OP_MATH_OP_##op_name b; \ + SET_FLOAT_VALUE(mrb, regs[a], z); \ + } \ + break #endif - break; -#endif - default: - SET_INT_VALUE(regs[a+1], c); - c = 1; - goto L_SEND; - } - NEXT; - } - CASE(OP_SUBI, BBB) { - mrb_value *regs_a = regs + a; - - /* need to check if + is overridden */ - switch (mrb_type(regs_a[0])) { - case MRB_TT_FIXNUM: - { - mrb_int x = mrb_fixnum(regs_a[0]); - mrb_int y = (mrb_int)c; - mrb_int z; + CASE(OP_ADDI, BB) { + OP_MATHI(add); + } - if (mrb_int_sub_overflow(x, y, &z)) { -#ifndef MRB_WITHOUT_FLOAT - SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y); - break; -#endif - } - SET_INT_VALUE(regs_a[0], z); - } - break; -#ifndef MRB_WITHOUT_FLOAT - case MRB_TT_FLOAT: -#ifdef MRB_WORD_BOXING - { - mrb_float x = mrb_float(regs[a]); - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)c); - } -#else - mrb_float(regs_a[0]) -= c; -#endif - break; -#endif - default: - SET_INT_VALUE(regs_a[1], c); - c = 1; - goto L_SEND; - } - NEXT; + CASE(OP_SUBI, BB) { + OP_MATHI(sub); } #define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1])) -#ifdef MRB_WITHOUT_FLOAT -#define OP_CMP(op) do {\ +#ifdef MRB_NO_FLOAT +#define OP_CMP(op,sym) do {\ int result;\ /* need to check if - is overridden */\ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\ - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\ + case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER):\ result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\ break;\ default:\ c = 1;\ - goto L_SEND;\ + mid = MRB_OPSYM(sym);\ + goto L_SEND_SYM;\ }\ if (result) {\ SET_TRUE_VALUE(regs[a]);\ @@ -2531,17 +2485,17 @@ RETRY_TRY_BLOCK: }\ } while(0) #else -#define OP_CMP(op) do {\ +#define OP_CMP(op, sym) do {\ int result;\ /* need to check if - is overridden */\ switch (TYPES2(mrb_type(regs[a]),mrb_type(regs[a+1]))) {\ - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FIXNUM):\ + case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER):\ result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\ break;\ - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\ + case TYPES2(MRB_TT_INTEGER,MRB_TT_FLOAT):\ result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\ break;\ - case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\ + case TYPES2(MRB_TT_FLOAT,MRB_TT_INTEGER):\ result = OP_CMP_BODY(op,mrb_float,mrb_fixnum);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\ @@ -2549,7 +2503,8 @@ RETRY_TRY_BLOCK: break;\ default:\ c = 1;\ - goto L_SEND;\ + mid = MRB_OPSYM(sym);\ + goto L_SEND_SYM;\ }\ if (result) {\ SET_TRUE_VALUE(regs[a]);\ @@ -2560,33 +2515,33 @@ RETRY_TRY_BLOCK: } while(0) #endif - CASE(OP_EQ, BB) { + CASE(OP_EQ, B) { if (mrb_obj_eq(mrb, regs[a], regs[a+1])) { SET_TRUE_VALUE(regs[a]); } else { - OP_CMP(==); + OP_CMP(==,eq); } NEXT; } - CASE(OP_LT, BB) { - OP_CMP(<); + CASE(OP_LT, B) { + OP_CMP(<,lt); NEXT; } - CASE(OP_LE, BB) { - OP_CMP(<=); + CASE(OP_LE, B) { + OP_CMP(<=,le); NEXT; } - CASE(OP_GT, BB) { - OP_CMP(>); + CASE(OP_GT, B) { + OP_CMP(>,gt); NEXT; } - CASE(OP_GE, BB) { - OP_CMP(>=); + CASE(OP_GE, B) { + OP_CMP(>=,ge); NEXT; } @@ -2605,7 +2560,12 @@ RETRY_TRY_BLOCK: CASE(OP_ARYCAT, B) { mrb_value splat = mrb_ary_splat(mrb, regs[a+1]); - mrb_ary_concat(mrb, regs[a], splat); + if (mrb_nil_p(regs[a])) { + regs[a] = splat; + } + else { + mrb_ary_concat(mrb, regs[a], splat); + } mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -2693,9 +2653,15 @@ RETRY_TRY_BLOCK: } CASE(OP_STRING, BB) { - mrb_value str = mrb_str_dup(mrb, pool[b]); + size_t len; - regs[a] = str; + len = pool[b].tt >> 2; + if (pool[b].tt & IREP_TT_SFLAG) { + regs[a] = mrb_str_new_static(mrb, pool[b].u.str, len); + } + else { + regs[a] = mrb_str_new(mrb, pool[b].u.str, len); + } mrb_gc_arena_restore(mrb, ai); NEXT; } @@ -2743,7 +2709,7 @@ RETRY_TRY_BLOCK: L_MAKE_LAMBDA: { struct RProc *p; - mrb_irep *nirep = irep->reps[b]; + const mrb_irep *nirep = irep->reps[b]; if (c & OP_L_CAPTURE) { p = mrb_closure_new(mrb, nirep); @@ -2753,6 +2719,7 @@ RETRY_TRY_BLOCK: p->flags |= MRB_PROC_SCOPE; } if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT; + if (c == OP_L_METHOD) proc_adjust_upper(p); regs[a] = mrb_obj_value(p); mrb_gc_arena_restore(mrb, ai); NEXT; @@ -2794,6 +2761,7 @@ RETRY_TRY_BLOCK: super = regs[a+1]; if (mrb_nil_p(base)) { baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); + if (!baseclass) baseclass = mrb->object_class; base = mrb_obj_value(baseclass); } c = mrb_vm_define_class(mrb, base, super, id); @@ -2810,6 +2778,7 @@ RETRY_TRY_BLOCK: base = regs[a]; if (mrb_nil_p(base)) { baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); + if (!baseclass) baseclass = mrb->object_class; base = mrb_obj_value(baseclass); } cls = mrb_vm_define_module(mrb, base, id); @@ -2818,11 +2787,11 @@ RETRY_TRY_BLOCK: NEXT; } - CASE(OP_EXEC, BB) { - mrb_callinfo *ci; + CASE(OP_EXEC, BB) + { mrb_value recv = regs[a]; struct RProc *p; - mrb_irep *nirep = irep->reps[b]; + const mrb_irep *nirep = irep->reps[b]; /* prepare closure */ p = mrb_proc_new(mrb, nirep); @@ -2830,21 +2799,10 @@ RETRY_TRY_BLOCK: mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)proc); MRB_PROC_SET_TARGET_CLASS(p, mrb_class_ptr(recv)); p->flags |= MRB_PROC_SCOPE; + proc_adjust_upper(p); /* prepare call stack */ - ci = cipush(mrb); - ci->pc = pc; - ci->acc = a; - ci->mid = 0; - ci->stackent = mrb->c->stack; - ci->argc = 0; - ci->target_class = mrb_class_ptr(recv); - - /* prepare stack */ - mrb->c->stack += a; - - /* setup block to call */ - ci->proc = p; + cipush(mrb, a, a, mrb_class_ptr(recv), p, 0, 0); irep = p->body.irep; pool = irep->pool; @@ -2859,10 +2817,13 @@ RETRY_TRY_BLOCK: struct RClass *target = mrb_class_ptr(regs[a]); struct RProc *p = mrb_proc_ptr(regs[a+1]); mrb_method_t m; + mrb_sym mid = syms[b]; MRB_METHOD_FROM_PROC(m, p); - mrb_define_method_raw(mrb, target, syms[b], m); + mrb_define_method_raw(mrb, target, mid, m); + mrb_method_added(mrb, target, mid); mrb_gc_arena_restore(mrb, ai); + regs[a] = mrb_symbol_value(mid); NEXT; } @@ -2874,7 +2835,7 @@ RETRY_TRY_BLOCK: CASE(OP_TCLASS, B) { if (!check_target_class(mrb)) goto L_RAISE; - regs[a] = mrb_obj_value(mrb->c->ci->target_class); + regs[a] = mrb_obj_value(mrb_vm_ci_target_class(mrb->c->ci)); NEXT; } @@ -2882,25 +2843,26 @@ RETRY_TRY_BLOCK: struct RClass *target; if (!check_target_class(mrb)) goto L_RAISE; - target = mrb->c->ci->target_class; + target = mrb_vm_ci_target_class(mrb->c->ci); mrb_alias_method(mrb, target, syms[a], syms[b]); - NEXT; + mrb_method_added(mrb, target, syms[a]); + NEXT; } CASE(OP_UNDEF, B) { struct RClass *target; if (!check_target_class(mrb)) goto L_RAISE; - target = mrb->c->ci->target_class; + target = mrb_vm_ci_target_class(mrb->c->ci); mrb_undef_method_id(mrb, target, syms[a]); NEXT; } CASE(OP_DEBUG, Z) { FETCH_BBB(); -#ifdef MRB_ENABLE_DEBUG_HOOK +#ifdef MRB_USE_DEBUG_HOOK mrb->debug_op_hook(mrb, irep, pc, regs); #else -#ifndef MRB_DISABLE_STDIO +#ifndef MRB_NO_STDIO printf("OP_DEBUG %d %d %d\n", a, b, c); #else abort(); @@ -2910,19 +2872,23 @@ RETRY_TRY_BLOCK: } CASE(OP_ERR, B) { - mrb_value msg = mrb_str_dup(mrb, pool[a]); + size_t len = pool[a].tt >> 2; mrb_value exc; - exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg); - ERR_PC_SET(mrb, pc); + mrb_assert((pool[a].tt&IREP_TT_NFLAG)==0); + exc = mrb_exc_new(mrb, E_LOCALJUMP_ERROR, pool[a].u.str, len); mrb_exc_set(mrb, exc); goto L_RAISE; } + CASE(OP_SENDVK, BB) { /* not yet implemented */ + NEXT; + } + CASE(OP_EXT1, Z) { insn = READ_B(); switch (insn) { -#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _1(); goto L_OP_ ## insn ## _BODY; +#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _1(); mrb->c->ci->pc = pc; goto L_OP_ ## insn ## _BODY; #include "mruby/ops.h" #undef OPCODE } @@ -2932,7 +2898,7 @@ RETRY_TRY_BLOCK: CASE(OP_EXT2, Z) { insn = READ_B(); switch (insn) { -#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _2(); goto L_OP_ ## insn ## _BODY; +#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _2(); mrb->c->ci->pc = pc; goto L_OP_ ## insn ## _BODY; #include "mruby/ops.h" #undef OPCODE } @@ -2942,7 +2908,7 @@ RETRY_TRY_BLOCK: CASE(OP_EXT3, Z) { uint8_t insn = READ_B(); switch (insn) { -#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _3(); goto L_OP_ ## insn ## _BODY; +#define OPCODE(insn,ops) case OP_ ## insn: FETCH_ ## ops ## _3(); mrb->c->ci->pc = pc; goto L_OP_ ## insn ## _BODY; #include "mruby/ops.h" #undef OPCODE } @@ -2952,14 +2918,17 @@ RETRY_TRY_BLOCK: CASE(OP_STOP, Z) { /* stop VM */ - L_STOP: - while (mrb->c->eidx > 0) { - ecall(mrb); + CHECKPOINT_RESTORE(RBREAK_TAG_STOP) { + /* do nothing */ } - mrb->c->cibase->ridx = 0; - ERR_PC_CLR(mrb); + CHECKPOINT_MAIN(RBREAK_TAG_STOP) { + UNWIND_ENSURE(mrb, mrb->c->ci, pc, RBREAK_TAG_STOP, proc, mrb_nil_value()); + } + CHECKPOINT_END(RBREAK_TAG_STOP); + L_STOP: mrb->jmp = prev_jmp; if (mrb->exc) { + mrb_assert(mrb->exc->tt == MRB_TT_EXCEPTION); return mrb_obj_value(mrb->exc); } return regs[irep->nlocals]; @@ -2969,14 +2938,19 @@ RETRY_TRY_BLOCK: #undef regs } MRB_CATCH(&c_jmp) { + mrb_callinfo *ci = mrb->c->ci; + while (ci > mrb->c->cibase && ci->acc == CI_ACC_DIRECT) { + ci = cipop(mrb); + } exc_catched = TRUE; + pc = ci->pc; goto RETRY_TRY_BLOCK; } MRB_END_EXC(&c_jmp); } -MRB_API mrb_value -mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self) +static mrb_value +mrb_run(mrb_state *mrb, const struct RProc *proc, mrb_value self) { if (mrb->c->ci->argc < 0) { return mrb_vm_run(mrb, proc, self, 3); /* receiver, args and block) */ @@ -2987,33 +2961,29 @@ mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self) } MRB_API mrb_value -mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) +mrb_top_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep) { - mrb_callinfo *ci; mrb_value v; if (!mrb->c->cibase) { return mrb_vm_run(mrb, proc, self, stack_keep); } if (mrb->c->ci == mrb->c->cibase) { + mrb_vm_ci_env_set(mrb->c->ci, NULL); return mrb_vm_run(mrb, proc, self, stack_keep); } - ci = cipush(mrb); - ci->mid = 0; - ci->acc = CI_ACC_SKIP; - ci->target_class = mrb->object_class; + cipush(mrb, 0, CI_ACC_SKIP, mrb->object_class, NULL, 0, 0); v = mrb_vm_run(mrb, proc, self, stack_keep); - cipop(mrb); return v; } -#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus) -# if !defined(MRB_ENABLE_CXX_ABI) +#if defined(MRB_USE_CXX_EXCEPTION) && defined(__cplusplus) +# if !defined(MRB_USE_CXX_ABI) } /* end of extern "C" */ # endif mrb_int mrb_jmpbuf::jmpbuf_id = 0; -# if !defined(MRB_ENABLE_CXX_ABI) +# if !defined(MRB_USE_CXX_ABI) extern "C" { # endif #endif |
