diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/array.c | 908 | ||||
| -rw-r--r-- | src/backtrace.c | 345 | ||||
| -rw-r--r-- | src/cdump.c | 445 | ||||
| -rw-r--r-- | src/class.c | 2637 | ||||
| -rw-r--r-- | src/codedump.c | 595 | ||||
| -rw-r--r-- | src/codegen.c | 3156 | ||||
| -rw-r--r-- | src/compar.c | 2 | ||||
| -rw-r--r-- | src/crc.c | 39 | ||||
| -rw-r--r-- | src/debug.c | 118 | ||||
| -rw-r--r-- | src/dump.c | 560 | ||||
| -rw-r--r-- | src/enum.c | 22 | ||||
| -rw-r--r-- | src/error.c | 520 | ||||
| -rw-r--r-- | src/error.h | 2 | ||||
| -rw-r--r-- | src/etc.c | 143 | ||||
| -rw-r--r-- | src/ext/.gitkeep | 0 | ||||
| -rw-r--r-- | src/fmt_fp.c | 364 | ||||
| -rw-r--r-- | src/gc.c | 1315 | ||||
| -rw-r--r-- | src/hash.c | 1706 | ||||
| -rw-r--r-- | src/init.c | 6 | ||||
| -rw-r--r-- | src/kernel.c | 917 | ||||
| -rw-r--r-- | src/keywords | 50 | ||||
| -rw-r--r-- | src/lex.def | 212 | ||||
| -rw-r--r-- | src/load.c | 590 | ||||
| -rw-r--r-- | src/mrb_throw.h | 41 | ||||
| -rw-r--r-- | src/mruby_core.rake | 78 | ||||
| -rw-r--r-- | src/node.h | 117 | ||||
| -rw-r--r-- | src/numeric.c | 1657 | ||||
| -rw-r--r-- | src/object.c | 335 | ||||
| -rw-r--r-- | src/opcode.h | 2 | ||||
| -rw-r--r-- | src/parse.y | 6407 | ||||
| -rw-r--r-- | src/pool.c | 19 | ||||
| -rw-r--r-- | src/print.c | 94 | ||||
| -rw-r--r-- | src/proc.c | 423 | ||||
| -rw-r--r-- | src/range.c | 497 | ||||
| -rw-r--r-- | src/readflt.c | 120 | ||||
| -rw-r--r-- | src/readint.c | 30 | ||||
| -rw-r--r-- | src/state.c | 233 | ||||
| -rw-r--r-- | src/string.c | 2672 | ||||
| -rw-r--r-- | src/symbol.c | 414 | ||||
| -rw-r--r-- | src/value_array.h | 3 | ||||
| -rw-r--r-- | src/variable.c | 912 | ||||
| -rw-r--r-- | src/version.c | 10 | ||||
| -rw-r--r-- | src/vm.c | 3266 |
43 files changed, 13608 insertions, 18374 deletions
diff --git a/src/array.c b/src/array.c index 48dc1ff10..dd669a0b1 100644 --- a/src/array.c +++ b/src/array.c @@ -4,46 +4,40 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/string.h" -#include "mruby/range.h" +#include <mruby.h> +#include <mruby/array.h> +#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 #define ARY_SHRINK_RATIO 5 /* must be larger than 2 */ #define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value)) -#define ARY_MAX_SIZE ((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? (mrb_int)ARY_C_MAX_SIZE : MRB_INT_MAX-1) - -static inline mrb_value -ary_elt(mrb_value ary, mrb_int offset) -{ - if (RARRAY_LEN(ary) == 0) return mrb_nil_value(); - if (offset < 0 || RARRAY_LEN(ary) <= offset) { - return mrb_nil_value(); - } - return RARRAY_PTR(ary)[offset]; -} +#define ARY_MAX_SIZE ((mrb_int)((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? ARY_C_MAX_SIZE : MRB_INT_MAX-1)) static struct RArray* ary_new_capa(mrb_state *mrb, mrb_int capa) { struct RArray *a; - mrb_int blen; + size_t blen; if (capa > ARY_MAX_SIZE) { mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } blen = capa * sizeof(mrb_value); - if (blen < capa) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); - } - a = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class); - a->ptr = (mrb_value *)mrb_malloc(mrb, blen); - a->aux.capa = capa; - a->len = 0; + a = MRB_OBJ_ALLOC(mrb, MRB_TT_ARRAY, mrb->array_class); + if (capa <= MRB_ARY_EMBED_LEN_MAX) { + ARY_SET_EMBED_LEN(a, 0); + } + else { + a->as.heap.ptr = (mrb_value *)mrb_malloc(mrb, blen); + a->as.heap.aux.capa = capa; + a->as.heap.len = 0; + } return a; } @@ -62,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) @@ -85,18 +78,22 @@ array_copy(mrb_value *dst, const mrb_value *src, mrb_int size) } } -MRB_API mrb_value -mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) +static struct RArray* +ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) { - mrb_value ary; - struct RArray *a; + struct RArray *a = ary_new_capa(mrb, size); - ary = mrb_ary_new_capa(mrb, size); - a = mrb_ary_ptr(ary); - array_copy(a->ptr, vals, size); - a->len = size; + array_copy(ARY_PTR(a), vals, size); + ARY_SET_LEN(a, size); - return ary; + return a; +} + +MRB_API mrb_value +mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) +{ + struct RArray *a = ary_new_from_values(mrb, size, vals); + return mrb_obj_value(a); } MRB_API mrb_value @@ -105,9 +102,9 @@ mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr) struct RArray *a; a = ary_new_capa(mrb, 2); - a->ptr[0] = car; - a->ptr[1] = cdr; - a->len = 2; + ARY_PTR(a)[0] = car; + ARY_PTR(a)[1] = cdr; + ARY_SET_LEN(a, 2); return mrb_obj_value(a); } @@ -122,28 +119,36 @@ ary_fill_with_nil(mrb_value *ptr, mrb_int size) } static void +ary_modify_check(mrb_state *mrb, struct RArray *a) +{ + mrb_check_frozen(mrb, a); +} + +static void ary_modify(mrb_state *mrb, struct RArray *a) { + ary_modify_check(mrb, a); + if (ARY_SHARED_P(a)) { - mrb_shared_array *shared = a->aux.shared; + mrb_shared_array *shared = a->as.heap.aux.shared; - if (shared->refcnt == 1 && a->ptr == shared->ptr) { - a->ptr = shared->ptr; - a->aux.capa = a->len; + if (shared->refcnt == 1 && a->as.heap.ptr == shared->ptr) { + a->as.heap.ptr = shared->ptr; + a->as.heap.aux.capa = a->as.heap.len; mrb_free(mrb, shared); } else { mrb_value *ptr, *p; mrb_int len; - p = a->ptr; - len = a->len * sizeof(mrb_value); + p = a->as.heap.ptr; + len = a->as.heap.len * sizeof(mrb_value); ptr = (mrb_value *)mrb_malloc(mrb, len); if (p) { - array_copy(ptr, p, a->len); + array_copy(ptr, p, a->as.heap.len); } - a->ptr = ptr; - a->aux.capa = a->len; + a->as.heap.ptr = ptr; + a->as.heap.aux.capa = a->as.heap.len; mrb_ary_decref(mrb, shared); } ARY_UNSET_SHARED_FLAG(a); @@ -160,18 +165,20 @@ mrb_ary_modify(mrb_state *mrb, struct RArray* a) static void ary_make_shared(mrb_state *mrb, struct RArray *a) { - if (!ARY_SHARED_P(a)) { + if (!ARY_SHARED_P(a) && !ARY_EMBED_P(a)) { mrb_shared_array *shared = (mrb_shared_array *)mrb_malloc(mrb, sizeof(mrb_shared_array)); + mrb_value *ptr = a->as.heap.ptr; + mrb_int len = a->as.heap.len; shared->refcnt = 1; - if (a->aux.capa > a->len) { - a->ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*a->len+1); + if (a->as.heap.aux.capa > len) { + a->as.heap.ptr = shared->ptr = (mrb_value *)mrb_realloc(mrb, ptr, sizeof(mrb_value)*len+1); } else { - shared->ptr = a->ptr; + shared->ptr = ptr; } - shared->len = a->len; - a->aux.shared = shared; + shared->len = len; + a->as.heap.aux.shared = shared; ARY_SET_SHARED_FLAG(a); } } @@ -179,36 +186,58 @@ ary_make_shared(mrb_state *mrb, struct RArray *a) static void ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len) { - mrb_int capa = a->aux.capa; + mrb_int capa = ARY_CAPA(a); - if (len > ARY_MAX_SIZE) { + if (len > ARY_MAX_SIZE || len < 0) { + size_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); } - if (capa == 0) { + if (capa < ARY_DEFAULT_LEN) { capa = ARY_DEFAULT_LEN; } while (capa < len) { - capa *= 2; + if (capa <= ARY_MAX_SIZE / 2) { + capa *= 2; + } + else { + capa = len; + } + } + if (capa < len || capa > ARY_MAX_SIZE) { + goto size_error; } - if (capa > ARY_MAX_SIZE) capa = ARY_MAX_SIZE; /* len <= capa <= ARY_MAX_SIZE */ + if (ARY_EMBED_P(a)) { + mrb_value *ptr = ARY_EMBED_PTR(a); + mrb_int len = ARY_EMBED_LEN(a); + mrb_value *expanded_ptr = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*capa); - if (capa > a->aux.capa) { - mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa); + ARY_UNSET_EMBED_FLAG(a); + array_copy(expanded_ptr, ptr, len); + a->as.heap.len = len; + a->as.heap.aux.capa = capa; + a->as.heap.ptr = expanded_ptr; + } + else if (capa > a->as.heap.aux.capa) { + mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa); - a->aux.capa = capa; - a->ptr = expanded_ptr; + a->as.heap.aux.capa = capa; + a->as.heap.ptr = expanded_ptr; } } static void ary_shrink_capa(mrb_state *mrb, struct RArray *a) { - mrb_int capa = a->aux.capa; + mrb_int capa; + + if (ARY_EMBED_P(a)) return; + + capa = a->as.heap.aux.capa; if (capa < ARY_DEFAULT_LEN * 2) return; - if (capa <= a->len * ARY_SHRINK_RATIO) return; + if (capa <= a->as.heap.len * ARY_SHRINK_RATIO) return; do { capa /= 2; @@ -216,11 +245,11 @@ ary_shrink_capa(mrb_state *mrb, struct RArray *a) capa = ARY_DEFAULT_LEN; break; } - } while (capa > a->len * ARY_SHRINK_RATIO); + } while (capa > a->as.heap.len * ARY_SHRINK_RATIO); - if (capa > a->len && capa < a->aux.capa) { - a->aux.capa = capa; - a->ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa); + if (capa > a->as.heap.len && capa < a->as.heap.aux.capa) { + a->as.heap.aux.capa = capa; + a->as.heap.ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa); } } @@ -233,40 +262,58 @@ mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len) ary_modify(mrb, a); old_len = RARRAY_LEN(ary); if (old_len != new_len) { - a->len = new_len; if (new_len < old_len) { ary_shrink_capa(mrb, a); } else { ary_expand_capa(mrb, a, new_len); - ary_fill_with_nil(a->ptr + old_len, new_len - old_len); + ary_fill_with_nil(ARY_PTR(a) + old_len, new_len - old_len); } + ARY_SET_LEN(a, new_len); } return ary; } static mrb_value -mrb_ary_s_create(mrb_state *mrb, mrb_value self) +mrb_ary_s_create(mrb_state *mrb, mrb_value klass) { - mrb_value *vals; + mrb_value ary; + const mrb_value *vals; mrb_int len; + struct RArray *a; - mrb_get_args(mrb, "*", &vals, &len); + mrb_get_args(mrb, "*!", &vals, &len); + ary = mrb_ary_new_from_values(mrb, len, vals); + a = mrb_ary_ptr(ary); + a->c = mrb_class_ptr(klass); - return mrb_ary_new_from_values(mrb, len, vals); + return ary; } +static void ary_replace(mrb_state*, struct RArray*, struct RArray*); + static void -ary_concat(mrb_state *mrb, struct RArray *a, mrb_value *ptr, mrb_int blen) +ary_concat(mrb_state *mrb, struct RArray *a, struct RArray *a2) { - mrb_int len = a->len + blen; + mrb_int len; + + if (ARY_LEN(a) == 0) { + ary_replace(mrb, a, a2); + return; + } + if (ARY_LEN(a2) > ARY_MAX_SIZE - ARY_LEN(a)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + } + len = ARY_LEN(a) + ARY_LEN(a2); ary_modify(mrb, a); - if (a->aux.capa < len) ary_expand_capa(mrb, a, len); - array_copy(a->ptr+a->len, ptr, blen); + if (ARY_CAPA(a) < len) { + ary_expand_capa(mrb, a, len); + } + array_copy(ARY_PTR(a)+ARY_LEN(a), ARY_PTR(a2), ARY_LEN(a2)); mrb_write_barrier(mrb, (struct RBasic*)a); - a->len = len; + ARY_SET_LEN(a, len); } MRB_API void @@ -274,17 +321,16 @@ mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other) { struct RArray *a2 = mrb_ary_ptr(other); - ary_concat(mrb, mrb_ary_ptr(self), a2->ptr, a2->len); + ary_concat(mrb, mrb_ary_ptr(self), a2); } static mrb_value mrb_ary_concat_m(mrb_state *mrb, mrb_value self) { - mrb_value *ptr; - mrb_int blen; + mrb_value ary; - mrb_get_args(mrb, "a", &ptr, &blen); - ary_concat(mrb, mrb_ary_ptr(self), ptr, blen); + mrb_get_args(mrb, "A", &ary); + mrb_ary_concat(mrb, self, ary); return self; } @@ -293,37 +339,74 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; - mrb_value ary; - mrb_value *ptr; - mrb_int blen; + const mrb_value *ptr; + mrb_int blen, len1; mrb_get_args(mrb, "a", &ptr, &blen); - ary = mrb_ary_new_capa(mrb, a1->len + blen); - a2 = mrb_ary_ptr(ary); - array_copy(a2->ptr, a1->ptr, a1->len); - array_copy(a2->ptr + a1->len, ptr, blen); - a2->len = a1->len + blen; + if (ARY_MAX_SIZE - blen < ARY_LEN(a1)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + } + len1 = ARY_LEN(a1); + a2 = ary_new_capa(mrb, len1 + blen); + array_copy(ARY_PTR(a2), ARY_PTR(a1), len1); + array_copy(ARY_PTR(a2) + len1, ptr, blen); + ARY_SET_LEN(a2, len1+blen); - return ary; + return mrb_obj_value(a2); } +#define ARY_REPLACE_SHARED_MIN 20 + static void -ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len) +ary_replace(mrb_state *mrb, struct RArray *a, struct RArray *b) { - ary_modify(mrb, a); - if (a->aux.capa < len) + mrb_int len = ARY_LEN(b); + + ary_modify_check(mrb, a); + if (a == b) return; + if (ARY_SHARED_P(a)) { + mrb_ary_decref(mrb, a->as.heap.aux.shared); + a->as.heap.aux.capa = 0; + a->as.heap.len = 0; + a->as.heap.ptr = NULL; + ARY_UNSET_SHARED_FLAG(a); + } + if (ARY_SHARED_P(b)) { + shared_b: + if (ARY_EMBED_P(a)) { + ARY_UNSET_EMBED_FLAG(a); + } + else { + mrb_free(mrb, a->as.heap.ptr); + } + a->as.heap.ptr = b->as.heap.ptr; + a->as.heap.len = len; + a->as.heap.aux.shared = b->as.heap.aux.shared; + a->as.heap.aux.shared->refcnt++; + ARY_SET_SHARED_FLAG(a); + mrb_write_barrier(mrb, (struct RBasic*)a); + return; + } + if (!mrb_frozen_p(b) && len > ARY_REPLACE_SHARED_MIN) { + ary_make_shared(mrb, b); + goto shared_b; + } + if (ARY_CAPA(a) < len) ary_expand_capa(mrb, a, len); - array_copy(a->ptr, argv, len); + array_copy(ARY_PTR(a), ARY_PTR(b), len); mrb_write_barrier(mrb, (struct RBasic*)a); - a->len = len; + ARY_SET_LEN(a, len); } MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other) { + struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2 = mrb_ary_ptr(other); - ary_replace(mrb, mrb_ary_ptr(self), a2->ptr, a2->len); + if (a1 != a2) { + ary_replace(mrb, a1, a2); + } } static mrb_value @@ -342,39 +425,41 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; - mrb_value ary; mrb_value *ptr; - mrb_int times; + mrb_int times, len1; mrb_get_args(mrb, "i", ×); if (times < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } if (times == 0) return mrb_ary_new(mrb); - - ary = mrb_ary_new_capa(mrb, a1->len * times); - a2 = mrb_ary_ptr(ary); - ptr = a2->ptr; + if (ARY_MAX_SIZE / times < ARY_LEN(a1)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + } + len1 = ARY_LEN(a1); + a2 = ary_new_capa(mrb, len1 * times); + ARY_SET_LEN(a2, len1 * times); + ptr = ARY_PTR(a2); while (times--) { - array_copy(ptr, a1->ptr, a1->len); - ptr += a1->len; - a2->len += a1->len; + array_copy(ptr, ARY_PTR(a1), len1); + ptr += len1; } - return ary; + return mrb_obj_value(a2); } static mrb_value mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); + mrb_int len = ARY_LEN(a); - if (a->len > 1) { + if (len > 1) { mrb_value *p1, *p2; ary_modify(mrb, a); - p1 = a->ptr; - p2 = a->ptr + a->len - 1; + p1 = ARY_PTR(a); + p2 = p1 + len - 1; while (p1 < p2) { mrb_value tmp = *p1; @@ -388,48 +473,60 @@ mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self) static mrb_value mrb_ary_reverse(mrb_state *mrb, mrb_value self) { - struct RArray *a = mrb_ary_ptr(self), *b; - mrb_value ary; + struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, ARY_LEN(a)); + mrb_int len = ARY_LEN(a); - ary = mrb_ary_new_capa(mrb, a->len); - b = mrb_ary_ptr(ary); - if (a->len > 0) { + if (len > 0) { mrb_value *p1, *p2, *e; - p1 = a->ptr; - e = p1 + a->len; - p2 = b->ptr + a->len - 1; + p1 = ARY_PTR(a); + e = p1 + len; + p2 = ARY_PTR(b) + len - 1; while (p1 < e) { *p2-- = *p1++; } - b->len = a->len; + ARY_SET_LEN(b, len); } - return ary; + return mrb_obj_value(b); } MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem) { struct RArray *a = mrb_ary_ptr(ary); + mrb_int len = ARY_LEN(a); ary_modify(mrb, a); - if (a->len == a->aux.capa) - ary_expand_capa(mrb, a, a->len + 1); - a->ptr[a->len++] = elem; + if (len == ARY_CAPA(a)) + ary_expand_capa(mrb, a, len + 1); + ARY_PTR(a)[len] = elem; + ARY_SET_LEN(a, len+1); mrb_field_write_barrier_value(mrb, (struct RBasic*)a, elem); } static mrb_value mrb_ary_push_m(mrb_state *mrb, mrb_value self) { - mrb_value *argv; - mrb_int len; + mrb_int argc; + const mrb_value *argv; + mrb_int len, len2; + struct RArray *a; - mrb_get_args(mrb, "*", &argv, &len); - while (len--) { - mrb_ary_push(mrb, self, *argv++); + 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 + argc; + if (ARY_CAPA(a) < len2) { + ary_expand_capa(mrb, a, len2); + } + array_copy(ARY_PTR(a)+len, argv, argc); + ARY_SET_LEN(a, len2); + while (argc--) { + mrb_field_write_barrier_value(mrb, (struct RBasic*)a, *argv); + argv++; } - return self; } @@ -437,9 +534,12 @@ MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary) { struct RArray *a = mrb_ary_ptr(ary); + mrb_int len = ARY_LEN(a); - if (a->len == 0) return mrb_nil_value(); - return a->ptr[--a->len]; + ary_modify_check(mrb, a); + if (len == 0) return mrb_nil_value(); + ARY_SET_LEN(a, len-1); + return ARY_PTR(a)[len-1]; } #define ARY_SHIFT_SHARED_MIN 10 @@ -448,30 +548,74 @@ MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); + mrb_int len = ARY_LEN(a); mrb_value val; - if (a->len == 0) return mrb_nil_value(); + ary_modify_check(mrb, a); + if (len == 0) return mrb_nil_value(); if (ARY_SHARED_P(a)) { L_SHIFT: - val = a->ptr[0]; - a->ptr++; - a->len--; + val = a->as.heap.ptr[0]; + a->as.heap.ptr++; + a->as.heap.len--; return val; } - if (a->len > ARY_SHIFT_SHARED_MIN) { + if (len > ARY_SHIFT_SHARED_MIN) { ary_make_shared(mrb, a); goto L_SHIFT; } else { - mrb_value *ptr = a->ptr; - mrb_int size = a->len; + mrb_value *ptr = ARY_PTR(a); + mrb_int size = len; val = *ptr; while (--size) { *ptr = *(ptr+1); ++ptr; } - --a->len; + ARY_SET_LEN(a, len-1); + } + return val; +} + +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; } @@ -484,21 +628,25 @@ MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item) { struct RArray *a = mrb_ary_ptr(self); + mrb_int len = ARY_LEN(a); if (ARY_SHARED_P(a) - && a->aux.shared->refcnt == 1 /* shared only referenced from this array */ - && a->ptr - a->aux.shared->ptr >= 1) /* there's room for unshifted item */ { - a->ptr--; - a->ptr[0] = item; + && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */ + && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= 1) /* there's room for unshifted item */ { + a->as.heap.ptr--; + a->as.heap.ptr[0] = item; } else { + mrb_value *ptr; + ary_modify(mrb, a); - if (a->aux.capa < a->len + 1) - ary_expand_capa(mrb, a, a->len + 1); - value_move(a->ptr + 1, a->ptr, a->len); - a->ptr[0] = item; + if (ARY_CAPA(a) < len + 1) + ary_expand_capa(mrb, a, len + 1); + ptr = ARY_PTR(a); + value_move(ptr + 1, ptr, len); + ptr[0] = item; } - a->len++; + ARY_SET_LEN(a, len+1); mrb_field_write_barrier_value(mrb, (struct RBasic*)a, item); return self; @@ -508,121 +656,159 @@ static mrb_value mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_value *vals; - mrb_int len; + const mrb_value *vals; + mrb_value *ptr; + mrb_int alen, len; - mrb_get_args(mrb, "*", &vals, &len); + mrb_get_args(mrb, "*!", &vals, &alen); + if (alen == 0) { + ary_modify_check(mrb, a); + return self; + } + len = ARY_LEN(a); + if (alen > ARY_MAX_SIZE - len) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + } if (ARY_SHARED_P(a) - && a->aux.shared->refcnt == 1 /* shared only referenced from this array */ - && a->ptr - a->aux.shared->ptr >= len) /* there's room for unshifted item */ { - a->ptr -= len; + && a->as.heap.aux.shared->refcnt == 1 /* shared only referenced from this array */ + && a->as.heap.ptr - a->as.heap.aux.shared->ptr >= alen) /* there's room for unshifted item */ { + ary_modify_check(mrb, a); + a->as.heap.ptr -= alen; + ptr = a->as.heap.ptr; } else { + mrb_bool same = vals == ARY_PTR(a); ary_modify(mrb, a); - if (len == 0) return self; - if (a->aux.capa < a->len + len) - ary_expand_capa(mrb, a, a->len + len); - value_move(a->ptr + len, a->ptr, a->len); + 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(a->ptr, vals, len); - a->len += len; - while (len--) { - mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[len]); + array_copy(ptr, vals, alen); + ARY_SET_LEN(a, len+alen); + while (alen--) { + mrb_field_write_barrier_value(mrb, (struct RBasic*)a, vals[alen]); } 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); - - /* range check */ - if (n < 0) n += a->len; - if (n < 0 || a->len <= n) return mrb_nil_value(); - - return a->ptr[n]; -} - MRB_API void mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val) { struct RArray *a = mrb_ary_ptr(ary); + mrb_int len = ARY_LEN(a); ary_modify(mrb, a); /* range check */ if (n < 0) { - n += a->len; + n += len; if (n < 0) { - mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - a->len)); + mrb_raisef(mrb, E_INDEX_ERROR, "index %i out of array", n - len); } } - if (a->len <= n) { - if (a->aux.capa <= n) + if (len <= n) { + if (ARY_CAPA(a) <= n) ary_expand_capa(mrb, a, n + 1); - ary_fill_with_nil(a->ptr + a->len, n + 1 - a->len); - a->len = n + 1; + ary_fill_with_nil(ARY_PTR(a) + len, n + 1 - len); + ARY_SET_LEN(a, n+1); } - a->ptr[n] = val; + ARY_PTR(a)[n] = val; mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val); } +static struct RArray* +ary_dup(mrb_state *mrb, struct RArray *a) +{ + return ary_new_from_values(mrb, ARY_LEN(a), ARY_PTR(a)); +} + MRB_API mrb_value mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl) { struct RArray *a = mrb_ary_ptr(ary); - mrb_int tail, size; + mrb_int alen = ARY_LEN(a); const mrb_value *argv; - mrb_int i, argc; + mrb_int argc; + mrb_int tail; 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) { - head += a->len; + head += alen; if (head < 0) { mrb_raise(mrb, E_INDEX_ERROR, "index is out of array"); } } - if (a->len < len || a->len < head + len) { - len = a->len - head; - } tail = head + len; + if (alen < len || alen < tail) { + len = alen - head; + } /* size check */ if (mrb_array_p(rpl)) { argc = RARRAY_LEN(rpl); argv = RARRAY_PTR(rpl); + if (argv == ARY_PTR(a)) { + struct RArray *r; + + if (argc > 32767) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "too big recursive splice"); + } + r = ary_dup(mrb, a); + argv = ARY_PTR(r); + } + } + else if (mrb_undef_p(rpl)) { + argc = 0; + argv = NULL; } else { argc = 1; argv = &rpl; } - size = head + argc; - - if (tail < a->len) size += a->len - tail; - if (size > a->aux.capa) - ary_expand_capa(mrb, a, size); - - if (head > a->len) { - ary_fill_with_nil(a->ptr + a->len, head - a->len); - } - else if (head < a->len) { - value_move(a->ptr + head + argc, a->ptr + tail, a->len - tail); - } - - for (i = 0; i < argc; i++) { - *(a->ptr + head + i) = *(argv + i); - mrb_field_write_barrier_value(mrb, (struct RBasic*)a, argv[i]); + if (head >= alen) { + if (head > ARY_MAX_SIZE - argc) { + mrb_raisef(mrb, E_INDEX_ERROR, "index %i too big", head); + } + len = head + argc; + if (len > ARY_CAPA(a)) { + ary_expand_capa(mrb, a, head + argc); + } + ary_fill_with_nil(ARY_PTR(a) + alen, head - alen); + if (argc > 0) { + array_copy(ARY_PTR(a) + head, argv, argc); + } + ARY_SET_LEN(a, len); } + else { + mrb_int newlen; - a->len = size; + if (alen - len > ARY_MAX_SIZE - argc) { + mrb_raisef(mrb, E_INDEX_ERROR, "index %i too big", alen + argc - len); + } + newlen = alen + argc - len; + if (newlen > ARY_CAPA(a)) { + ary_expand_capa(mrb, a, newlen); + } + if (len != argc) { + mrb_value *ptr = ARY_PTR(a); + tail = head + len; + value_move(ptr + head + argc, ptr + tail, alen - tail); + ARY_SET_LEN(a, newlen); + } + if (argc > 0) { + value_move(ARY_PTR(a) + head, argv, argc); + } + } + mrb_write_barrier(mrb, (struct RBasic*)a); return ary; } @@ -641,31 +827,43 @@ ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len) { struct RArray *b; + if (!ARY_SHARED_P(a) && len <= ARY_SHIFT_SHARED_MIN) { + return mrb_ary_new_from_values(mrb, len, ARY_PTR(a)+beg); + } ary_make_shared(mrb, a); - b = (struct RArray*)mrb_obj_alloc(mrb, MRB_TT_ARRAY, mrb->array_class); - b->ptr = a->ptr + beg; - b->len = len; - b->aux.shared = a->aux.shared; - b->aux.shared->refcnt++; + b = 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; + b->as.heap.aux.shared->refcnt++; ARY_SET_SHARED_FLAG(b); 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_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); + mrb_get_args(mrb, "i*!", &i, &argv, &argc); return i; } } @@ -701,32 +899,36 @@ static mrb_value mrb_ary_aget(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_int i, len; + mrb_int i; + 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, a->len)) { + 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); - if (i < 0) i += a->len; - if (i < 0 || a->len < i) return mrb_nil_value(); + alen = ARY_LEN(a); + if (i < 0) i += alen; + if (i < 0 || alen < i) return mrb_nil_value(); if (len < 0) return mrb_nil_value(); - if (a->len == i) return mrb_ary_new(mrb); - if (len > a->len - i) len = a->len - i; + if (alen == i) return mrb_ary_new(mrb); + if (len > alen - i) len = alen - i; return ary_subseq(mrb, a, i, len); } @@ -772,25 +974,27 @@ mrb_ary_aset(mrb_state *mrb, mrb_value self) mrb_value v1, v2, v3; mrb_int i, len; - if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) { - switch (mrb_type(v1)) { + 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 */ - case MRB_TT_RANGE: - if (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self))) { - mrb_ary_splice(mrb, self, i, len, v2); - } + switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) { + case MRB_RANGE_TYPE_MISMATCH: + mrb_ary_set(mrb, self, aget_index(mrb, v1), v2); break; - /* a[n] = v */ - case MRB_TT_FIXNUM: - mrb_ary_set(mrb, self, mrb_fixnum(v1), v2); + case MRB_RANGE_OK: + mrb_ary_splice(mrb, self, i, len, v2); break; - default: - mrb_ary_set(mrb, self, aget_index(mrb, v1), v2); + 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; @@ -803,22 +1007,24 @@ mrb_ary_delete_at(mrb_state *mrb, mrb_value self) mrb_int index; mrb_value val; mrb_value *ptr; - mrb_int len; + mrb_int len, alen; mrb_get_args(mrb, "i", &index); - if (index < 0) index += a->len; - if (index < 0 || a->len <= index) return mrb_nil_value(); + alen = ARY_LEN(a); + if (index < 0) index += alen; + if (index < 0 || alen <= index) return mrb_nil_value(); ary_modify(mrb, a); - val = a->ptr[index]; + ptr = ARY_PTR(a); + val = ptr[index]; - ptr = a->ptr + index; - len = a->len - index; + ptr += index; + len = alen - index; while (--len) { *ptr = *(ptr+1); ++ptr; } - --a->len; + ARY_SET_LEN(a, alen-1); ary_shrink_capa(mrb, a); @@ -829,48 +1035,52 @@ static mrb_value mrb_ary_first(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_int size; + mrb_int size, alen; - if (mrb_get_args(mrb, "|i", &size) == 0) { - return (a->len > 0)? a->ptr[0]: mrb_nil_value(); + if (mrb_get_argc(mrb) == 0) { + return (ARY_LEN(a) > 0)? ARY_PTR(a)[0]: mrb_nil_value(); } + mrb_get_args(mrb, "|i", &size); if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } - if (size > a->len) size = a->len; + alen = ARY_LEN(a); + if (size > alen) size = alen; if (ARY_SHARED_P(a)) { return ary_subseq(mrb, a, 0, size); } - return mrb_ary_new_from_values(mrb, size, a->ptr); + return mrb_ary_new_from_values(mrb, size, ARY_PTR(a)); } static mrb_value mrb_ary_last(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - mrb_int size; + mrb_int n, size, alen; - if (mrb_get_args(mrb, "|i", &size) == 0) - return (a->len > 0)? a->ptr[a->len - 1]: mrb_nil_value(); + n = mrb_get_args(mrb, "|i", &size); + alen = ARY_LEN(a); + if (n == 0) { + return (alen > 0) ? ARY_PTR(a)[alen - 1]: mrb_nil_value(); + } if (size < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative array size"); } - if (size > a->len) size = a->len; + if (size > alen) size = alen; if (ARY_SHARED_P(a) || size > ARY_DEFAULT_LEN) { - return ary_subseq(mrb, a, a->len - size, size); + return ary_subseq(mrb, a, alen - size, size); } - return mrb_ary_new_from_values(mrb, size, a->ptr + a->len - size); + return mrb_ary_new_from_values(mrb, size, ARY_PTR(a) + alen - size); } static mrb_value 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); @@ -882,14 +1092,16 @@ 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_int i; + 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); } + if (i > (len = RARRAY_LEN(self))) { + i = len; + } } return mrb_nil_value(); } @@ -897,15 +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 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"))) { - return mrb_funcall(mrb, v, "to_a", 0); + + if (!mrb_respond_to(mrb, v, MRB_SYM(to_a))) { + return mrb_ary_new_from_values(mrb, 1, &v); } - else { + + 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); } + mrb_ensure_array_type(mrb, ary); + a = mrb_ary_ptr(ary); + a = ary_dup(mrb, a); + return mrb_obj_value(a); } static mrb_value @@ -913,7 +1136,7 @@ mrb_ary_size(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); - return mrb_fixnum_value(a->len); + return mrb_fixnum_value(ARY_LEN(a)); } MRB_API mrb_value @@ -921,41 +1144,50 @@ mrb_ary_clear(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); + ary_modify(mrb, a); if (ARY_SHARED_P(a)) { - mrb_ary_decref(mrb, a->aux.shared); + mrb_ary_decref(mrb, a->as.heap.aux.shared); ARY_UNSET_SHARED_FLAG(a); } + else if (!ARY_EMBED_P(a)){ + mrb_free(mrb, a->as.heap.ptr); + } + if (MRB_ARY_EMBED_LEN_MAX > 0) { + ARY_SET_EMBED_LEN(a, 0); + } else { - mrb_free(mrb, a->ptr); + a->as.heap.ptr = NULL; + a->as.heap.aux.capa = 0; + ARY_SET_LEN(a, 0); } - a->len = 0; - a->aux.capa = 0; - a->ptr = 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); - return mrb_bool_value(a->len == 0); + return mrb_bool_value(ARY_LEN(a) == 0); } 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); - } - return ary_elt(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 @@ -973,7 +1205,7 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list) mrb_ary_push(mrb, list, ary); - result = mrb_str_buf_new(mrb, 64); + result = mrb_str_new_capa(mrb, 64); for (i=0; i<RARRAY_LEN(ary); i++) { if (i > 0 && !mrb_nil_p(sep)) { @@ -993,15 +1225,17 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list) break; default: - tmp = mrb_check_string_type(mrb, val); - if (!mrb_nil_p(tmp)) { - val = tmp; - goto str_join; - } - tmp = mrb_check_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary"); - if (!mrb_nil_p(tmp)) { - val = tmp; - goto ary_join; + if (!mrb_immediate_p(val)) { + tmp = mrb_check_string_type(mrb, val); + if (!mrb_nil_p(tmp)) { + val = tmp; + goto str_join; + } + tmp = mrb_check_array_type(mrb, val); + if (!mrb_nil_p(tmp)) { + val = tmp; + goto ary_join; + } } val = mrb_obj_as_string(mrb, val); goto str_join; @@ -1016,7 +1250,9 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list) MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep) { - sep = mrb_obj_as_string(mrb, sep); + if (!mrb_nil_p(sep)) { + sep = mrb_obj_as_string(mrb, sep); + } return join_ary(mrb, ary, sep, mrb_ary_new(mrb)); } @@ -1036,18 +1272,16 @@ mrb_ary_join_m(mrb_state *mrb, mrb_value ary) { mrb_value sep = mrb_nil_value(); - mrb_get_args(mrb, "|S", &sep); + mrb_get_args(mrb, "|S!", &sep); return mrb_ary_join(mrb, ary, sep); } 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); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value(); - if (mrb_immediate_p(ary2)) return mrb_false_value(); if (!mrb_array_p(ary2)) { return mrb_false_value(); } @@ -1059,11 +1293,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); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0); - if (mrb_immediate_p(ary2)) return mrb_nil_value(); if (!mrb_array_p(ary2)) { return mrb_nil_value(); } @@ -1071,42 +1303,58 @@ mrb_ary_cmp(mrb_state *mrb, mrb_value ary1) return ary2; } +/* internal method to convert multi-value to single value */ +static mrb_value +mrb_ary_svalue(mrb_state *mrb, mrb_value ary) +{ + switch (RARRAY_LEN(ary)) { + case 0: + return mrb_nil_value(); + case 1: + return RARRAY_PTR(ary)[0]; + default: + return ary; + } +} + void mrb_init_array(mrb_state *mrb) { struct RClass *a; - a = mrb->array_class = 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, "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_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, "__svalue", mrb_ary_svalue, MRB_ARGS_NONE()); } diff --git a/src/backtrace.c b/src/backtrace.c index a82d8f343..7ff1cea6c 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -4,198 +4,261 @@ ** See Copyright Notice in mruby.h */ -#include <stdarg.h> -#include "mruby.h" -#include "mruby/variable.h" -#include "mruby/proc.h" -#include "mruby/array.h" -#include "mruby/string.h" -#include "mruby/class.h" -#include "mruby/debug.h" -#include "mruby/error.h" - -#ifdef ENABLE_STDIO - -typedef void (*output_stream_func)(mrb_state*, void*, int, const char*, ...); - -static void -print_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - vfprintf((FILE*)stream, format, ap); - va_end(ap); -} - - -#define MIN_BUFSIZE 127 +#include <mruby.h> +#include <mruby/variable.h> +#include <mruby/proc.h> +#include <mruby/array.h> +#include <mruby/string.h> +#include <mruby/class.h> +#include <mruby/debug.h> +#include <mruby/error.h> +#include <mruby/numeric.h> +#include <mruby/data.h> +#include <mruby/presym.h> + +struct backtrace_location { + int32_t lineno; + mrb_sym method_id; + const char *filename; +}; + +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 -get_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...) +each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, each_backtrace_func func, void *data) { - va_list ap; - mrb_value ary, str; - int ai; - - if (level > 0) { - return; - } - - ai = mrb_gc_arena_save(mrb); - ary = mrb_obj_value((struct RArray*)stream); - - va_start(ap, format); - str = mrb_str_new(mrb, 0, vsnprintf(NULL, 0, format, ap) + 1); - va_end(ap); - - va_start(ap, format); - vsnprintf(RSTRING_PTR(str), RSTRING_LEN(str), format, ap); - va_end(ap); - - mrb_str_resize(mrb, str, RSTRING_LEN(str) - 1); - mrb_ary_push(mrb, ary, str); - mrb_gc_arena_restore(mrb, ai); -} - -static void -output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *stream) -{ - mrb_callinfo *ci; - const char *filename, *method, *sep; - int i, lineno, tracehead = 1; - if (ciidx >= mrb->c->ciend - mrb->c->cibase) ciidx = 10; /* ciidx is broken... */ - for (i = ciidx; i >= 0; i--) { + for (ptrdiff_t i=ciidx; i >= 0; i--) { + struct backtrace_location loc; + mrb_callinfo *ci; + const mrb_irep *irep = 0; + const mrb_code *pc; + uint32_t idx; + ci = &mrb->c->cibase[i]; - filename = NULL; - if (!ci->proc) continue; - if (MRB_PROC_CFUNC_P(ci->proc)) { - continue; + if (!ci->proc || MRB_PROC_CFUNC_P(ci->proc)) { + loc.lineno = -1; + idx = 0; } else { - mrb_irep *irep = ci->proc->body.irep; - mrb_code *pc; - - if (mrb->c->cibase[i].err) { - pc = mrb->c->cibase[i].err; - } - else if (i+1 <= ciidx) { - pc = mrb->c->cibase[i+1].pc - 1; + irep = ci->proc->body.irep; + if (!irep) continue; + if (mrb->c->cibase[i].pc) { + pc = &mrb->c->cibase[i].pc[-1]; } else { - pc = pc0; + continue; } - filename = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq)); - lineno = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq)); - } - if (lineno == -1) continue; - if (ci->target_class == ci->proc->target_class) - sep = "."; - else - sep = "#"; - - if (!filename) { - filename = "(unknown)"; - } - - if (tracehead) { - func(mrb, stream, 1, "trace:\n"); - tracehead = 0; + idx = (uint32_t)(pc - irep->iseq); + loc.lineno = mrb_debug_get_line(mrb, irep, idx); } - method = mrb_sym2name(mrb, ci->mid); - if (method) { - const char *cn = mrb_class_name(mrb, ci->proc->target_class); - - if (cn) { - func(mrb, stream, 1, "\t[%d] ", i); - func(mrb, stream, 0, "%s:%d:in %s%s%s", filename, lineno, cn, sep, method); - func(mrb, stream, 1, "\n"); - } - else { - func(mrb, stream, 1, "\t[%d] ", i); - func(mrb, stream, 0, "%s:%d:in %s", filename, lineno, method); - func(mrb, stream, 1, "\n"); + 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; } } - else { - func(mrb, stream, 1, "\t[%d] ", i); - func(mrb, stream, 0, "%s:%d", filename, lineno); - func(mrb, stream, 1, "\n"); + + loc.filename = mrb_debug_get_filename(mrb, irep, idx); + if (!loc.filename) { + loc.filename = "(unknown)"; } + + func(mrb, &loc, data); } } +#ifndef MRB_NO_STDIO + static void -exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream) +print_backtrace(mrb_state *mrb, struct RObject *exc, mrb_value backtrace) { - output_backtrace(mrb, mrb_fixnum(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "ciidx"))), - (mrb_code*)mrb_cptr(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc"))), - func, stream); + mrb_int i; + mrb_int n = RARRAY_LEN(backtrace); + mrb_value *loc, mesg; + FILE *stream = stderr; + + if (n != 0) { + if (n > 1) { + fprintf(stream, "trace (most recent call last):\n"); + } + 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)); + } + } + mesg = mrb_exc_inspect(mrb, mrb_obj_value(exc)); + fprintf(stream, "%.*s\n", (int)RSTRING_LEN(mesg), RSTRING_PTR(mesg)); } -/* mrb_print_backtrace/mrb_get_backtrace: +/* mrb_print_backtrace - function to retrieve backtrace information from the exception. - note that if you call method after the exception, call stack will be - overwritten. So invoke these functions just after detecting exceptions. + function to retrieve backtrace information from the last exception. */ MRB_API void mrb_print_backtrace(mrb_state *mrb) { - if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) { + mrb_value backtrace; + + if (!mrb->exc) { return; } - exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)stderr); + + backtrace = mrb_obj_iv_get(mrb, mrb->exc, MRB_SYM(backtrace)); + if (mrb_nil_p(backtrace)) return; + if (!mrb_array_p(backtrace)) backtrace = mrb_unpack_backtrace(mrb, backtrace); + print_backtrace(mrb, mrb->exc, backtrace); } +#else -MRB_API mrb_value -mrb_exc_backtrace(mrb_state *mrb, mrb_value self) +MRB_API void +mrb_print_backtrace(mrb_state *mrb) { - mrb_value ary; +} + +#endif - ary = mrb_ary_new(mrb); - exc_output_backtrace(mrb, mrb_obj_ptr(self), get_backtrace_i, (void*)mrb_ary_ptr(ary)); +static void +count_backtrace_i(mrb_state *mrb, + const struct backtrace_location *loc, + void *data) +{ + int *lenp = (int*)data; - return ary; + (*lenp)++; } -MRB_API mrb_value -mrb_get_backtrace(mrb_state *mrb) +static void +pack_backtrace_i(mrb_state *mrb, + const struct backtrace_location *loc, + void *data) { - mrb_value ary; - mrb_callinfo *ci = mrb->c->ci; - mrb_code *pc = ci->pc; - mrb_int ciidx = (mrb_int)(ci - mrb->c->cibase - 1); + struct backtrace_location **pptr = (struct backtrace_location**)data; + struct backtrace_location *ptr = *pptr; - if (ciidx < 0) ciidx = 0; - ary = mrb_ary_new(mrb); - output_backtrace(mrb, ciidx, pc, get_backtrace_i, (void*)mrb_ary_ptr(ary)); + *ptr = *loc; + *pptr = ptr+1; +} - return ary; +static mrb_value +packed_backtrace(mrb_state *mrb) +{ + struct RData *backtrace; + ptrdiff_t ciidx = mrb->c->ci - mrb->c->cibase; + int len = 0; + int size; + void *ptr; + + each_backtrace(mrb, ciidx, count_backtrace_i, &len); + size = len * sizeof(struct backtrace_location); + ptr = mrb_malloc(mrb, size); + backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type); + backtrace->flags = (uint32_t)len; + each_backtrace(mrb, ciidx, pack_backtrace_i, &ptr); + return mrb_obj_value(backtrace); } -#else +void +mrb_keep_backtrace(mrb_state *mrb, mrb_value exc) +{ + mrb_sym sym = MRB_SYM(backtrace); + mrb_value backtrace; + int ai; -MRB_API void -mrb_print_backtrace(mrb_state *mrb) + if (mrb_iv_defined(mrb, exc, sym)) return; + ai = mrb_gc_arena_save(mrb); + backtrace = packed_backtrace(mrb); + mrb_iv_set(mrb, exc, sym, backtrace); + mrb_gc_arena_restore(mrb, ai); +} + +mrb_value +mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace) { + const struct backtrace_location *bt; + mrb_int n, i; + int ai; + + if (mrb_nil_p(backtrace)) { + empty_backtrace: + return mrb_ary_new_capa(mrb, 0); + } + if (mrb_array_p(backtrace)) return backtrace; + bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, backtrace, &bt_type); + if (bt == NULL) goto empty_backtrace; + n = (mrb_int)RDATA(backtrace)->flags; + 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++) { + const struct backtrace_location *entry = &bt[i]; + mrb_value btline; + + 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_sym_name(mrb, entry->method_id)); + } + mrb_ary_push(mrb, backtrace, btline); + mrb_gc_arena_restore(mrb, ai); + } + + return backtrace; } -MRB_API mrb_value -mrb_exc_backtrace(mrb_state *mrb, mrb_value self) +mrb_value +mrb_exc_backtrace(mrb_state *mrb, mrb_value exc) { - return mrb_ary_new(mrb); + mrb_value backtrace; + + 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, MRB_SYM(backtrace), backtrace); + return backtrace; } -MRB_API mrb_value +mrb_value mrb_get_backtrace(mrb_state *mrb) { - return mrb_ary_new(mrb); + return mrb_unpack_backtrace(mrb, packed_backtrace(mrb)); } - -#endif diff --git a/src/cdump.c b/src/cdump.c new file mode 100644 index 000000000..9b7040e58 --- /dev/null +++ b/src/cdump.c @@ -0,0 +1,445 @@ +/* +** 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 || + info->files[0]->line_type != mrb_debug_line_ary) { + 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; + + 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"); + + 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); + + fprintf(fp, "static mrb_irep_debug_info_file %s_debug_file_%d = {\n", name, n); + fprintf(fp, "%d, %d, %d, mrb_debug_line_ary, {%s_debug_lines_%d}};\n", + info->files[0]->start_pos, + info->files[0]->filename_sym, + info->files[0]->line_entry_count, + 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 afcc4dc1d..33a610c26 100644 --- a/src/class.c +++ b/src/class.c @@ -4,68 +4,333 @@ ** See Copyright Notice in mruby.h */ -#include <ctype.h> #include <stdarg.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/numeric.h" -#include "mruby/proc.h" -#include "mruby/string.h" -#include "mruby/variable.h" -#include "mruby/error.h" -#include "mruby/data.h" - -KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal) +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/hash.h> +#include <mruby/class.h> +#include <mruby/numeric.h> +#include <mruby/proc.h> +#include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/error.h> +#include <mruby/data.h> +#include <mruby/istruct.h> +#include <mruby/opcode.h> +#include <mruby/presym.h> + +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)) { - struct RProc *m = kh_value(h, k); - if (m) { - mrb_gc_mark(mrb, (struct RBasic*)m); - } + 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 && !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); } -static void -name_class(mrb_state *mrb, struct RClass *c, mrb_sym name) +void +mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) +{ + mrb_value name; + mrb_sym nsym = MRB_SYM(__classname__); + + if (mrb_obj_iv_defined(mrb, (struct RObject*)c, nsym)) return; + if (outer == NULL || outer == mrb->object_class) { + name = mrb_symbol_value(id); + } + else { + 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_force(mrb, (struct RObject*)c, MRB_SYM(__outer__), + mrb_obj_value(outer)); + } + return; + } + 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_force(mrb, (struct RObject*)c, nsym, name); +} + +mrb_bool +mrb_const_name_p(mrb_state *mrb, const char *name, mrb_int len) { - mrb_obj_iv_set(mrb, (struct RObject*)c, - mrb_intern_lit(mrb, "__classid__"), mrb_symbol_value(name)); + return len > 0 && ISUPPER(name[0]) && mrb_ident_p(name+1, len-1); } static void setup_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) { - name_class(mrb, c, id); + mrb_class_name_class(mrb, outer, c, id); mrb_obj_iv_set(mrb, (struct RObject*)outer, id, mrb_obj_value(c)); - if (outer != mrb->object_class) { - mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"), - mrb_obj_value(outer)); - } } #define make_metaclass(mrb, c) prepare_singleton_class((mrb), (struct RBasic*)(c)) @@ -76,8 +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->mt = 0; + sc = MRB_OBJ_ALLOC(mrb, MRB_TT_SCLASS, mrb->class_class); + sc->flags |= MRB_FL_CLASS_IS_INHERITED; + sc->mt = mt_new(mrb); sc->iv = 0; if (o->tt == MRB_TT_CLASS) { c = (struct RClass*)o; @@ -97,14 +363,29 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o) } else { sc->super = o->c; + prepare_singleton_class(mrb, (struct RBasic*)sc); } 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 struct RClass * +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* class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) { mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id); @@ -113,7 +394,7 @@ class_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) return mrb_class_ptr(c); } -static struct RClass * +static struct RClass* module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) { mrb_value c = mrb_const_get(mrb, mrb_obj_value(klass), id); @@ -122,14 +403,25 @@ module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) return mrb_class_ptr(c); } -MRB_API struct RClass* -mrb_class_outer_module(mrb_state *mrb, struct RClass *c) +static mrb_bool +class_ptr_p(mrb_value obj) { - mrb_value outer; + switch (mrb_type(obj)) { + case MRB_TT_CLASS: + case MRB_TT_SCLASS: + case MRB_TT_MODULE: + return TRUE; + default: + return FALSE; + } +} - outer = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__")); - if (mrb_nil_p(outer)) return NULL; - return mrb_class_ptr(outer); +static void +check_if_class_or_module(mrb_state *mrb, mrb_value obj) +{ + if (!class_ptr_p(obj)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a class/module", obj); + } } static struct RClass* @@ -158,13 +450,31 @@ 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_module_p(old)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a module", old); + } + return mrb_class_ptr(old); + } return define_module(mrb, id, mrb_class_ptr(outer)); } 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); @@ -175,16 +485,23 @@ mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name) } static struct RClass* +find_origin(struct RClass *c) +{ + MRB_CLASS_ORIGIN(c); + return c; +} + +static struct RClass* define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *outer) { struct RClass * c; if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { 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; } @@ -199,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); } @@ -210,37 +527,61 @@ mrb_define_class(mrb_state *mrb, const char *name, struct RClass *super) return mrb_define_class_id(mrb, mrb_intern_cstr(mrb, name), super); } +static mrb_value mrb_bob_init(mrb_state *mrb, mrb_value); +#ifndef MRB_NO_METHOD_CACHE +static void mc_clear(mrb_state *mrb); +#else +#define mc_clear(mrb) +#endif + static void mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass) { + mrb_value s; + mrb_sym mid; + if (!super) super = mrb->object_class; - mrb_funcall(mrb, mrb_obj_value(super), "inherited", 1, mrb_obj_value(klass)); + super->flags |= MRB_FL_CLASS_IS_INHERITED; + s = mrb_obj_value(super); + 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)", 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); } else { s = 0; } - switch (mrb_type(outer)) { - case MRB_TT_CLASS: - case MRB_TT_SCLASS: - case MRB_TT_MODULE: - break; - default: - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", outer); - break; + 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_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 %v", old); + } + } + return c; } c = define_class(mrb, id, s, mrb_class_ptr(outer)); mrb_class_inherited(mrb, mrb_class_real(c->super), c); @@ -251,41 +592,102 @@ 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 struct RClass * +MRB_API mrb_bool +mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name) +{ + 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* mrb_class_get_under(mrb_state *mrb, struct RClass *outer, const char *name) { return class_from_sym(mrb, outer, mrb_intern_cstr(mrb, name)); } -MRB_API struct RClass * +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_API struct RClass* +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), name); + + if (!mrb_class_p(c)) { + mrb_raise(mrb, mrb->eException_class, "exception corrupted"); + } + exc = e = mrb_class_ptr(c); + + while (e) { + if (e == mrb->eException_class) + return exc; + e = e->super; + } + return mrb->eException_class; +} + +MRB_API struct RClass* mrb_module_get_under(mrb_state *mrb, struct RClass *outer, const char *name) { return module_from_sym(mrb, outer, mrb_intern_cstr(mrb, name)); } -MRB_API struct RClass * +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 @@ -298,46 +700,77 @@ mrb_module_get(mrb_state *mrb, const char *name) * \note if a class named \a name is already defined and its superclass is * \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_API struct RClass* +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 void -mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p) +MRB_API struct RClass* +mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, struct RClass *super) { - khash_t(mt) *h = c->mt; - khiter_t k; + return mrb_define_class_under_id(mrb, outer, mrb_intern_cstr(mrb, name), super); +} - if (!h) h = c->mt = kh_init(mt, mrb); - k = kh_put(mt, mrb, h, mid); - kh_value(h, k) = p; - if (p) { - mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p); +MRB_API void +mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_method_t m) +{ + mt_tbl *h; + union mt_ptr ptr; + + MRB_CLASS_ORIGIN(c); + h = c->mt; + 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); + + 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); + } + } } + 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 mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t func, mrb_aspec aspec) { - struct RProc *p; + mrb_method_t m; int ai = mrb_gc_arena_save(mrb); - p = mrb_proc_new_cfunc(mrb, func); - p->target_class = c; - mrb_define_method_raw(mrb, c, mid, p); + 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); } @@ -347,35 +780,14 @@ mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t mrb_define_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec); } -MRB_API void -mrb_define_method_vm(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_value body) -{ - khash_t(mt) *h = c->mt; - khiter_t k; - struct RProc *p; - - if (!h) h = c->mt = kh_init(mt, mrb); - k = kh_put(mt, mrb, h, name); - p = mrb_proc_ptr(body); - kh_value(h, k) = p; - if (p) { - mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p); - } -} - /* a function to raise NotImplementedError with current method name */ 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); } } @@ -389,50 +801,77 @@ 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) +to_ary(mrb_state *mrb, mrb_value val) { - 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; + mrb_check_type(mrb, val, MRB_TT_ARRAY); + return val; } static mrb_value -to_str(mrb_state *mrb, mrb_value val) +to_hash(mrb_state *mrb, mrb_value val) { - return check_type(mrb, val, MRB_TT_STRING, "String", "to_str"); + mrb_check_type(mrb, val, MRB_TT_HASH); + return val; } -static mrb_value -to_ary(mrb_state *mrb, mrb_value val) +#define to_sym(mrb, ss) mrb_obj_to_sym(mrb, ss) + +MRB_API mrb_int +mrb_get_argc(mrb_state *mrb) { - return check_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary"); + mrb_int argc = mrb->c->ci->argc; + + if (argc < 0) { + struct RArray *a = mrb_ary_ptr(mrb->c->ci->stack[1]); + + argc = ARY_LEN(a); + } + return argc; } -static mrb_value -to_hash(mrb_state *mrb, mrb_value val) +MRB_API const mrb_value* +mrb_get_argv(mrb_state *mrb) { - return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash"); + 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); + + array_argv = ARY_PTR(a); + } + return array_argv; } -static mrb_sym -to_sym(mrb_state *mrb, mrb_value ss) +MRB_API mrb_value +mrb_get_arg1(mrb_state *mrb) { - if (mrb_type(ss) == MRB_TT_SYMBOL) { - return mrb_symbol(ss); + 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); } - else if (mrb_string_p(ss)) { - return mrb_intern_str(mrb, to_str(mrb, ss)); - } - else { - mrb_value obj = mrb_funcall(mrb, ss, "inspect", 0); - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj); + 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. @@ -445,44 +884,94 @@ to_sym(mrb_state *mrb, mrb_value ss) string mruby type C type note ---------------------------------------------------------------------------------------------- o: Object [mrb_value] - C: class/module [mrb_value] - S: String [mrb_value] - A: Array [mrb_value] - H: Hash [mrb_value] - s: String [char*,mrb_int] Receive two arguments. - z: String [char*] NUL terminated string. - a: Array [mrb_value*,mrb_int] Receive two arguments. - 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 - &: Block [mrb_value] - *: rest argument [mrb_value*,mrb_int] Receive the rest of the arguments as an array. - |: optional Next argument of '|' and later are optional. - ?: optional given [mrb_bool] true if preceding argument (optional) is given. + 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 [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, ...) { + const char *fmt = format; char c; - int i = 0; - mrb_value *sp = mrb->c->stack + 1; + mrb_int i = 0; va_list ap; - int argc = mrb->c->ci->argc; + 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); - if (argc < 0) { - struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]); - argc = a->len; - sp = a->ptr; +#define ARGV array_argv + + while ((c = *fmt++)) { + switch (c) { + case '|': + opt = TRUE; + 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) { @@ -490,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': { @@ -503,8 +1000,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) p = va_arg(ap, mrb_value*); if (i < argc) { - *p = *sp++; - i++; + *p = argv[i++]; } } break; @@ -516,18 +1012,27 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) if (i < argc) { mrb_value ss; - ss = *sp++; - switch (mrb_type(ss)) { - case MRB_TT_CLASS: - case MRB_TT_MODULE: - case MRB_TT_SCLASS: - break; - default: - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss); - break; + ss = argv[i++]; + if (!class_ptr_p(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; @@ -537,8 +1042,10 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) p = va_arg(ap, mrb_value*); if (i < argc) { - *p = to_str(mrb, *sp++); - i++; + *p = argv[i++]; + if (!(altmode && mrb_nil_p(*p))) { + mrb_to_str(mrb, *p); + } } } break; @@ -548,8 +1055,10 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) p = va_arg(ap, mrb_value*); if (i < argc) { - *p = to_ary(mrb, *sp++); - i++; + *p = argv[i++]; + if (!(altmode && mrb_nil_p(*p))) { + *p = to_ary(mrb, *p); + } } } break; @@ -559,24 +1068,32 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) p = va_arg(ap, mrb_value*); if (i < argc) { - *p = to_hash(mrb, *sp++); - 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 (i < argc) { - ss = to_str(mrb, *sp++); - *ps = RSTRING_PTR(ss); - *pl = RSTRING_LEN(ss); - i++; + ss = argv[i++]; + if (altmode && mrb_nil_p(ss)) { + *ps = NULL; + *pl = 0; + } + else { + mrb_to_str(mrb, ss); + *ps = RSTRING_PTR(ss); + *pl = RSTRING_LEN(ss); + } } } break; @@ -587,9 +1104,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) ps = va_arg(ap, const char**); if (i < argc) { - ss = to_str(mrb, *sp++); - *ps = mrb_string_value_cstr(mrb, &ss); - i++; + ss = argv[i++]; + if (altmode && mrb_nil_p(ss)) { + *ps = NULL; + } + else { + mrb_to_str(mrb, ss); + *ps = RSTRING_CSTR(mrb, ss); + } } } break; @@ -597,61 +1119,61 @@ 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 (i < argc) { - aa = to_ary(mrb, *sp++); - a = mrb_ary_ptr(aa); - *pb = a->ptr; - *pl = a->len; - i++; + aa = argv[i++]; + if (altmode && mrb_nil_p(aa)) { + *pb = 0; + *pl = 0; + } + else { + aa = to_ary(mrb, aa); + a = mrb_ary_ptr(aa); + *pb = ARY_PTR(a); + *pl = ARY_LEN(a); + } + } + } + break; + case 'I': + { + void* *p; + mrb_value ss; + + p = va_arg(ap, void**); + if (i < argc) { + ss = argv[i++]; + if (!mrb_istruct_p(ss)) + { + mrb_raisef(mrb, E_TYPE_ERROR, "%v is not inline struct", ss); + } + *p = mrb_istruct_ptr(ss); } } break; +#ifndef MRB_NO_FLOAT case 'f': { mrb_float *p; p = va_arg(ap, mrb_float*); if (i < argc) { - *p = mrb_to_flo(mrb, *sp); - sp++; - i++; + *p = mrb_as_float(mrb, argv[i]); i++; } } break; +#endif case 'i': { mrb_int *p; p = va_arg(ap, mrb_int*); if (i < argc) { - switch (mrb_type(*sp)) { - case MRB_TT_FIXNUM: - *p = mrb_fixnum(*sp); - break; - case MRB_TT_FLOAT: - { - mrb_float f = mrb_float(*sp); - - if (!FIXABLE(f)) { - mrb_raise(mrb, E_RANGE_ERROR, "float too big for int"); - } - *p = (mrb_int)f; - } - break; - 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, *sp)); - break; - } - sp++; - i++; + *p = mrb_as_int(mrb, argv[i]); i++; } } break; @@ -660,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 = *sp++; + mrb_value b = argv[i++]; *boolp = mrb_test(b); - i++; } } break; @@ -674,9 +1195,8 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) if (i < argc) { mrb_value ss; - ss = *sp++; + ss = argv[i++]; *symp = to_sym(mrb, ss); - i++; } } break; @@ -688,8 +1208,13 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) datap = va_arg(ap, void**); type = va_arg(ap, struct mrb_data_type const*); if (i < argc) { - *datap = mrb_data_get_ptr(mrb, *sp++, type); - ++i; + mrb_value dd = argv[i++]; + if (altmode && mrb_nil_p(dd)) { + *datap = 0; + } + else { + *datap = mrb_data_get_ptr(mrb, dd, type); + } } } break; @@ -700,15 +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 (altmode && mrb_nil_p(*bp)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } *p = *bp; } break; case '|': + if (opt_skip && i == argc) goto finish; opt = TRUE; break; case '?': @@ -722,18 +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 = (altmode || !argv_on_stack) ? TRUE : FALSE; - 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) { - *var = sp; + if (nocopy) { + *var = argv+i; + } + else { + mrb_value args = mrb_ary_new_from_values(mrb, *pl, argv+i); + RARRAY(args)->c = NULL; + *var = RARRAY_PTR(args); + } } i = argc; - sp += *pl; } else { *pl = 0; @@ -741,14 +1277,75 @@ 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; } } + +#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; } @@ -758,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); @@ -766,80 +1363,194 @@ 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; } -MRB_API void -mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +static void +boot_initmod(mrb_state *mrb, struct RClass *mod) { - struct RClass *ins_pos; + if (!mod->mt) { + mod->mt = mt_new(mrb); + } +} + +static struct RClass* +include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) +{ + struct RClass *ic = MRB_OBJ_ALLOC(mrb, MRB_TT_ICLASS, mrb->class_class); + if (m->tt == MRB_TT_ICLASS) { + m = m->c; + } + MRB_CLASS_ORIGIN(m); + ic->iv = m->iv; + ic->mt = m->mt; + ic->super = super; + if (m->tt == MRB_TT_ICLASS) { + ic->c = m->c; + } + else { + ic->c = m; + } + return ic; +} + +static int +include_module_at(mrb_state *mrb, struct RClass *c, struct RClass *ins_pos, struct RClass *m, int search_super) +{ + struct RClass *p, *ic; + void *klass_mt = find_origin(c)->mt; - ins_pos = c; while (m) { - struct RClass *p = c, *ic; - int superclass_seen = 0; + int original_seen = FALSE; + int superclass_seen = FALSE; - if (c->mt && c->mt == m->mt) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); - } + if (c == ins_pos) original_seen = TRUE; + if (m->flags & MRB_FL_CLASS_IS_PREPENDED) + goto skip; + + if (klass_mt && klass_mt == m->mt) + return -1; + + p = c->super; while (p) { - if (c != p && p->tt == MRB_TT_CLASS) { - superclass_seen = 1; - } - else if (p->mt == m->mt) { - if (p->tt == MRB_TT_ICLASS && !superclass_seen) { - ins_pos = p; + if (c == p) original_seen = TRUE; + if (p->tt == MRB_TT_ICLASS) { + if (p->mt == m->mt) { + if (!superclass_seen && original_seen) { + ins_pos = p; /* move insert point */ + } + goto skip; } - goto skip; + } else if (p->tt == MRB_TT_CLASS) { + if (!search_super) break; + superclass_seen = TRUE; } p = p->super; } - ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); - if (m->tt == MRB_TT_ICLASS) { - ic->c = m->c; - } - else { - ic->c = m; - } - ic->mt = m->mt; - ic->iv = m->iv; - ic->super = ins_pos->super; + + ic = include_class_new(mrb, m, ins_pos->super); + m->flags |= MRB_FL_CLASS_IS_INHERITED; ins_pos->super = ic; mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); ins_pos = ic; skip: m = m->super; } + 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) +{ + 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; + + mrb_check_frozen(mrb, c); + if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) { + 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 = NULL; + origin->iv = c->iv; + mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin); + c->flags |= MRB_FL_CLASS_IS_PREPENDED; + } + 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_append_features(mrb_state *mrb, mrb_value mod) +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_include_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_include(mrb_state *mrb, mrb_value klass) +mrb_mod_append_features(mrb_state *mrb, mrb_value mod) { - mrb_value *argv; - mrb_int argc, i; - - mrb_get_args(mrb, "*", &argv, &argc); - for (i=0; i<argc; i++) { - mrb_check_type(mrb, argv[i], MRB_TT_MODULE); - } - while (argc--) { - mrb_funcall(mrb, argv[argc], "append_features", 1, klass); - mrb_funcall(mrb, argv[argc], "included", 1, klass); - } + struct RClass *c; - return klass; + mrb_check_type(mrb, mod, MRB_TT_MODULE); + mrb_get_args(mrb, "c", &c); + mrb_include_module(mrb, c, mrb_class_ptr(mod)); + return mod; } /* 15.2.2.4.28 */ @@ -884,15 +1595,12 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); - result = mrb_ary_new(mrb); - mrb_ary_push(mrb, result, mrb_obj_value(c)); - c = c->super; while (c) { if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } - else if (c->tt != MRB_TT_SCLASS) { + else if (!(c->flags & MRB_FL_CLASS_IS_PREPENDED)) { mrb_ary_push(mrb, result, mrb_obj_value(c)); } c = c->super; @@ -904,82 +1612,26 @@ 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; } static mrb_value -mrb_mod_included_modules(mrb_state *mrb, mrb_value self) -{ - mrb_value result; - struct RClass *c = mrb_class_ptr(self); - - result = mrb_ary_new(mrb); - while (c) { - if (c->tt == MRB_TT_ICLASS) { - mrb_ary_push(mrb, result, mrb_obj_value(c->c)); - } - c = c->super; - } - - return result; -} - -static mrb_value mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; - - mrb_get_args(mrb, "&", &b); + struct RClass *m = mrb_class_ptr(mod); + boot_initmod(mrb, m); /* bootstrap a newly initialized module */ + mrb_get_args(mrb, "|&", &b); if (!mrb_nil_p(b)) { - mrb_yield_with_class(mrb, b, 1, &mod, mod, mrb_class_ptr(mod)); + mrb_yield_with_class(mrb, b, 1, &mod, mod, m); } return mod; } -mrb_value mrb_class_instance_method_list(mrb_state*, mrb_bool, struct RClass*, int); - -/* 15.2.2.4.33 */ -/* - * call-seq: - * mod.instance_methods(include_super=true) -> array - * - * Returns an array containing the names of the public and protected instance - * methods in the receiver. For a module, these are the public and protected methods; - * for a class, they are the instance (not singleton) methods. With no - * argument, or with an argument that is <code>false</code>, the - * instance methods in <i>mod</i> are returned, otherwise the methods - * in <i>mod</i> and <i>mod</i>'s superclasses are returned. - * - * module A - * def method1() end - * end - * class B - * def method2() end - * end - * class C < B - * def method3() end - * end - * - * A.instance_methods #=> [:method1] - * B.instance_methods(false) #=> [:method2] - * C.instance_methods(false) #=> [:method3] - * C.instance_methods(true).length #=> 43 - */ - -static mrb_value -mrb_mod_instance_methods(mrb_state *mrb, mrb_value mod) -{ - struct RClass *c = mrb_class_ptr(mod); - mrb_bool recur = TRUE; - mrb_get_args(mrb, "|b", &recur); - return mrb_class_instance_method_list(mrb, recur, c, 0); -} - /* implementation of module_eval/class_eval */ mrb_value mrb_mod_module_eval(mrb_state*, mrb_value); @@ -989,35 +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: + case MRB_TT_INTEGER: +#ifndef MRB_NO_FLOAT case MRB_TT_FLOAT: - mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton"); - return mrb_nil_value(); /* not reached */ +#endif + return NULL; default: break; } obj = mrb_basic_ptr(v); prepare_singleton_class(mrb, obj); - if (mrb->c && mrb->c->ci && mrb->c->ci->target_class) { - mrb_obj_iv_set(mrb, (struct RObject*)obj->c, mrb_intern_lit(mrb, "__outer__"), - mrb_obj_value(mrb->c->ci->target_class)); + 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(obj->c); + return mrb_obj_value(c); } MRB_API void @@ -1028,102 +1689,213 @@ 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_class_method_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec) +{ + mrb_define_singleton_method_id(mrb, (struct RObject*)c, name, func, aspec); +} + +MRB_API void +mrb_define_module_function_id(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_func_t func, mrb_aspec aspec) +{ + mrb_define_class_method_id(mrb, c, name, func, aspec); + mrb_define_method_id(mrb, 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(mrb, c, name, func, aspec); - mrb_define_method(mrb, c, name, func, aspec); + mrb_define_module_function_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec); +} + +#ifndef MRB_NO_METHOD_CACHE +static void +mc_clear(mrb_state *mrb) +{ + memset(mrb->cache, 0, MRB_METHOD_CACHE_SIZE*sizeof(mrb->cache[0])); } -MRB_API struct RProc* +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(mrb); + return; + } + for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) { + if (mc[i].c == c) mc[i].c = 0; + } +} +#endif + +MRB_API mrb_method_t mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) { - khiter_t k; - struct RProc *m; + mrb_method_t m; struct RClass *c = *cp; +#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]; + + if (mc->c == c && mc->mid == mid) { + *cp = mc->c0; + return mc->m; + } +#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 (!m) break; + struct mt_elem *e = mt_get(mrb, h, mid); + if (e) { + if (e->ptr.proc == 0) break; *cp = c; + 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; + mc->m = m; +#endif return m; } } c = c->super; } - return NULL; /* no method */ + MRB_METHOD_FROM_PROC(m, NULL); + return m; /* no method */ } -MRB_API struct RProc* +MRB_API mrb_method_t mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid) { - struct RProc *m; + mrb_method_t m; m = mrb_method_search_vm(mrb, &c, mid); - if (!m) { - mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0); - if (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); + if (MRB_METHOD_UNDEF_P(m)) { + 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_buf_new(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_check(mrb, sym); - name = mrb_symbol_value(sym); - mrb_define_method_raw(mrb, c, method, - 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); } return mrb_nil_value(); } 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; } @@ -1131,39 +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; - - method = to_sym(mrb, argv[i]); - - /* prepare iv name (@name) */ - name = mrb_sym2str(mrb, method); - str = mrb_str_buf_new(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_check(mrb, sym); - attr = mrb_symbol_value(sym); - - /* prepare method name (name=) */ - str = mrb_str_buf_new(mrb, RSTRING_LEN(str)); - mrb_str_cat_str(mrb, str, name); - mrb_str_cat_lit(mrb, str, "="); - method = mrb_intern_str(mrb, str); - - mrb_define_method_raw(mrb, c, method, - mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr)); - mrb_gc_arena_restore(mrb, ai); - } - return mrb_nil_value(); + return mod_attr_define(mrb, mod, attr_writer, prepare_writer_name); } static mrb_value @@ -1177,6 +1917,9 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv) mrb_raise(mrb, E_TYPE_ERROR, "can't create instance of singleton class"); if (ttype == 0) ttype = MRB_TT_OBJECT; + if (ttype <= MRB_TT_CPTR) { + 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); } @@ -1185,25 +1928,28 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv) * call-seq: * class.new(args, ...) -> obj * - * Calls <code>allocate</code> to create a new object of - * <i>class</i>'s class, then invokes that object's - * <code>initialize</code> method, passing it <i>args</i>. - * This is the method that ends up getting called whenever - * an object is constructed using .new. + * Creates a new object of <i>class</i>'s class, then + * invokes that object's <code>initialize</code> method, + * passing it <i>args</i>. This is the method that ends + * up getting called whenever an object is constructed using + * `.new`. * */ -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_get_args(mrb, "*&", &argv, &argc, &blk); + mrb_get_args(mrb, "*!&", &argv, &argc, &blk); obj = mrb_instance_alloc(mrb, cv); - mrb_funcall_with_block(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv, blk); - + 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; } @@ -1211,10 +1957,13 @@ MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv) { mrb_value obj; + mrb_sym mid; obj = mrb_instance_alloc(mrb, mrb_obj_value(c)); - mrb_funcall_argv(mrb, obj, mrb_intern_lit(mrb, "initialize"), argc, argv); - + mid = MRB_SYM(initialize); + if (!mrb_func_basic_p(mrb, obj, mid, mrb_bob_init)) { + mrb_funcall_argv(mrb, obj, mid, argc, argv); + } return obj; } @@ -1236,13 +1985,20 @@ mrb_class_new_class(mrb_state *mrb, mrb_value cv) mrb_int n; mrb_value super, blk; mrb_value new_class; + mrb_sym mid; n = mrb_get_args(mrb, "|C&", &super, &blk); if (n == 0) { super = mrb_obj_value(mrb->object_class); } new_class = mrb_obj_value(mrb_class_new(mrb, mrb_class_ptr(super))); - mrb_funcall_with_block(mrb, new_class, mrb_intern_lit(mrb, "initialize"), n, &super, blk); + 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)); return new_class; } @@ -1253,9 +2009,9 @@ mrb_class_superclass(mrb_state *mrb, mrb_value klass) struct RClass *c; c = mrb_class_ptr(klass); - c = c->super; + c = find_origin(c)->super; while (c && c->tt == MRB_TT_ICLASS) { - c = c->super; + c = find_origin(c)->super; } if (!c) return mrb_nil_value(); return mrb_obj_value(c); @@ -1273,92 +2029,55 @@ mrb_bob_not(mrb_state *mrb, mrb_value cv) return mrb_bool_value(!mrb_test(cv)); } -/* 15.3.1.3.30 */ +/* 15.3.1.3.1 */ +/* 15.3.1.3.10 */ +/* 15.3.1.3.11 */ /* * call-seq: - * obj.method_missing(symbol [, *args] ) -> result + * obj == other -> true or false + * obj.equal?(other) -> true or false + * obj.eql?(other) -> true or false * - * Invoked by Ruby when <i>obj</i> is sent a message it cannot handle. - * <i>symbol</i> is the symbol for the method called, and <i>args</i> - * are any arguments that were passed to it. By default, the interpreter - * raises an error when this method is called. However, it is possible - * to override the method to provide more dynamic behavior. - * If it is decided that a particular method should not be handled, then - * <i>super</i> should be called, so that ancestors can pick up the - * missing method. - * The example below creates - * a class <code>Roman</code>, which responds to methods with names - * consisting of roman numerals, returning the corresponding integer - * values. + * Equality---At the <code>Object</code> level, <code>==</code> returns + * <code>true</code> only if <i>obj</i> and <i>other</i> are the + * same object. Typically, this method is overridden in descendant + * classes to provide class-specific meaning. * - * class Roman - * def romanToInt(str) - * # ... - * end - * def method_missing(methId) - * str = methId.id2name - * romanToInt(str) - * end - * end + * Unlike <code>==</code>, the <code>equal?</code> method should never be + * overridden by subclasses: it is used to determine object identity + * (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same + * object as <code>b</code>). * - * r = Roman.new - * r.iv #=> 4 - * r.xxiii #=> 23 - * r.mm #=> 2000 + * The <code>eql?</code> method returns <code>true</code> if + * <i>obj</i> and <i>anObject</i> have the same value. Used by + * <code>Hash</code> to test members for equality. For objects of + * class <code>Object</code>, <code>eql?</code> is synonymous with + * <code>==</code>. Subclasses normally continue this tradition, but + * there are exceptions. <code>Numeric</code> types, for example, + * perform type conversion across <code>==</code>, but not across + * <code>eql?</code>, so: + * + * 1 == 1.0 #=> true + * 1.eql? 1.0 #=> false */ -static mrb_value -mrb_bob_missing(mrb_state *mrb, mrb_value mod) +mrb_value +mrb_obj_equal_m(mrb_state *mrb, mrb_value self) { - mrb_sym name; - mrb_value *a; - mrb_int alen; - mrb_sym inspect; - mrb_value repr; - - mrb_get_args(mrb, "n*", &name, &a, &alen); - - inspect = mrb_intern_lit(mrb, "inspect"); - if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) { - /* method missing in inspect; avoid recursion */ - repr = mrb_any_to_s(mrb, mod); - } - else if (mrb_respond_to(mrb, mod, inspect) && mrb->c->ci - mrb->c->cibase < 64) { - repr = mrb_funcall_argv(mrb, mod, inspect, 0, 0); - if (RSTRING_LEN(repr) > 64) { - repr = mrb_any_to_s(mrb, mod); - } - } - else { - repr = mrb_any_to_s(mrb, mod); - } + mrb_value arg = mrb_get_arg1(mrb); - mrb_no_method_error(mrb, name, alen, a, "undefined method '%S' for %S", mrb_sym2str(mrb, name), repr); - /* not reached */ - return mrb_nil_value(); + return mrb_bool_value(mrb_obj_equal(mrb, self, arg)); } MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid) { - khiter_t k; - - while (c) { - khash_t(mt) *h = c->mt; + mrb_method_t m; - if (h) { - k = kh_get(mt, mrb, h, mid); - if (k != kh_end(h)) { - if (kh_value(h, k)) { - return TRUE; /* method exists */ - } - else { - return FALSE; /* undefined method */ - } - } - } - c = c->super; + m = mrb_method_search_vm(mrb, &c, mid); + if (MRB_METHOD_UNDEF_P(m)) { + return FALSE; } - return FALSE; /* no method */ + return TRUE; } MRB_API mrb_bool @@ -1371,49 +2090,27 @@ MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c) { mrb_value path; - const char *name; - mrb_sym classpath = mrb_intern_lit(mrb, "__classpath__"); + mrb_sym nsym = MRB_SYM(__classname__); - path = mrb_obj_iv_get(mrb, (struct RObject*)c, classpath); + path = mrb_obj_iv_get(mrb, (struct RObject*)c, nsym); if (mrb_nil_p(path)) { - struct RClass *outer = mrb_class_outer_module(mrb, c); - mrb_sym sym = mrb_class_sym(mrb, c, outer); - mrb_int len; - - if (sym == 0) { - return mrb_nil_value(); - } - else if (outer && outer != mrb->object_class) { - mrb_value base = mrb_class_path(mrb, outer); - path = mrb_str_buf_new(mrb, 0); - if (mrb_nil_p(base)) { - mrb_str_cat_lit(mrb, path, "#<Class:"); - mrb_str_concat(mrb, path, mrb_ptr_to_str(mrb, outer)); - mrb_str_cat_lit(mrb, path, ">"); - } - else { - mrb_str_concat(mrb, path, base); - } - mrb_str_cat_lit(mrb, path, "::"); - name = mrb_sym2name_len(mrb, sym, &len); - mrb_str_cat(mrb, path, name, len); - } - else { - name = mrb_sym2name_len(mrb, sym, &len); - path = mrb_str_new(mrb, name, len); - } - mrb_obj_iv_set(mrb, (struct RObject*)c, classpath, path); + /* no name (yet) */ + return mrb_class_find_path(mrb, c); } - return path; + else if (mrb_symbol_p(path)) { + /* toplevel class/module */ + return mrb_sym_str(mrb, mrb_symbol(path)); + } + return mrb_str_dup(mrb, path); } -MRB_API struct RClass * +MRB_API struct RClass* mrb_class_real(struct RClass* cl) { - if (cl == 0) - return NULL; + if (cl == 0) return NULL; while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) { cl = cl->super; + if (cl == 0) return NULL; } return cl; } @@ -1421,13 +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 = 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* @@ -1446,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"); @@ -1485,9 +2180,8 @@ 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); - m->mt = kh_init(mt, mrb); - + struct RClass *m = MRB_OBJ_ALLOC(mrb, MRB_TT_MODULE, mrb->module_class); + boot_initmod(mrb, m); return m; } @@ -1501,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 */ @@ -1514,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) { - struct RProc *m = mrb_method_search(mrb, c, b); + if (a == b) return; + mrb_method_t m = mrb_method_search(mrb, c, b); - mrb_define_method_vm(mrb, c, a, mrb_obj_value(m)); + 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 @@ -1531,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 @@ -1540,60 +2262,28 @@ 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__")); + 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:"); - str = mrb_str_new_lit(mrb, "#<Class:"); - - switch (mrb_type(v)) { - case MRB_TT_CLASS: - case MRB_TT_MODULE: - case MRB_TT_SCLASS: - mrb_str_append(mrb, str, mrb_inspect(mrb, v)); - break; - default: - mrb_str_append(mrb, str, mrb_any_to_s(mrb, v)); - break; + if (class_ptr_p(v)) { + mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v)); + } + else { + mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v)); } return mrb_str_cat_lit(mrb, str, ">"); } else { - struct RClass *c; - mrb_value path; - - str = mrb_str_buf_new(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) { @@ -1602,21 +2292,26 @@ 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; } static void undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a) { - mrb_value m; + 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 { - SET_PROC_VALUE(m, 0); - mrb_define_method_vm(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 @@ -1626,187 +2321,155 @@ mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *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 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--) { - undef_method(mrb, c, mrb_symbol(*argv)); + mrb_undef_method_id(mrb, c, to_sym(mrb, *argv)); argv++; } return mrb_nil_value(); } -static mrb_value -mod_define_method(mrb_state *mrb, mrb_value self) +static void +check_const_name_sym(mrb_state *mrb, mrb_sym id) { - struct RClass *c = mrb_class_ptr(self); - struct RProc *p; - mrb_sym mid; - mrb_value blk; - - mrb_get_args(mrb, "n&", &mid, &blk); - if (mrb_nil_p(blk)) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); + 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); } - p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); - mrb_proc_copy(p, mrb_proc_ptr(blk)); - p->flags |= MRB_PROC_STRICT; - mrb_define_method_raw(mrb, c, mid, p); - return mrb_symbol_value(mid); } -static void -check_cv_name_str(mrb_state *mrb, mrb_value str) +static mrb_value +mrb_mod_const_defined(mrb_state *mrb, mrb_value mod) { - const char *s = RSTRING_PTR(str); - mrb_int len = RSTRING_LEN(str); + mrb_sym id; + mrb_bool inherit = TRUE; - if (len < 3 || !(s[0] == '@' && s[1] == '@')) { - mrb_name_error(mrb, mrb_intern_str(mrb, str), "`%S' is not allowed as a class variable name", str); + mrb_get_args(mrb, "n|b", &id, &inherit); + check_const_name_sym(mrb, id); + if (inherit) { + return mrb_bool_value(mrb_const_defined(mrb, mod, id)); } + return mrb_bool_value(mrb_const_defined_at(mrb, mod, id)); } -static void -check_cv_name_sym(mrb_state *mrb, mrb_sym id) -{ - check_cv_name_str(mrb, mrb_sym2str(mrb, id)); -} - -/* 15.2.2.4.16 */ -/* - * call-seq: - * obj.class_variable_defined?(symbol) -> true or false - * - * Returns <code>true</code> if the given class variable is defined - * in <i>obj</i>. - * - * class Fred - * @@foo = 99 - * end - * Fred.class_variable_defined?(:@@foo) #=> true - * Fred.class_variable_defined?(:@@bar) #=> false - */ - static mrb_value -mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod) +mrb_const_get_sym(mrb_state *mrb, mrb_value mod, mrb_sym id) { - mrb_sym id; - - mrb_get_args(mrb, "n", &id); - check_cv_name_sym(mrb, id); - return mrb_bool_value(mrb_cv_defined(mrb, mod, id)); + check_const_name_sym(mrb, id); + return mrb_const_get(mrb, mod, id); } -/* 15.2.2.4.17 */ -/* - * call-seq: - * mod.class_variable_get(symbol) -> obj - * - * Returns the value of the given class variable (or throws a - * <code>NameError</code> exception). The <code>@@</code> part of the - * variable name should be included for regular class variables - * - * class Fred - * @@foo = 99 - * end - * Fred.class_variable_get(:@@foo) #=> 99 - */ - static mrb_value -mrb_mod_cvar_get(mrb_state *mrb, mrb_value mod) +mrb_mod_const_get(mrb_state *mrb, mrb_value mod) { + mrb_value path = mrb_get_arg1(mrb); mrb_sym id; + char *ptr; + mrb_int off, end, len; + + if (mrb_symbol_p(path)) { + /* const get with symbol */ + id = mrb_symbol(path); + return mrb_const_get_sym(mrb, mod, id); + } + + /* const get with class path string */ + path = mrb_ensure_string_type(mrb, path); + ptr = RSTRING_PTR(path); + len = RSTRING_LEN(path); + off = 0; + + while (off < len) { + end = mrb_str_index_lit(mrb, path, "::", off); + end = (end == -1) ? len : end; + id = mrb_intern(mrb, ptr+off, end-off); + mod = mrb_const_get_sym(mrb, mod, id); + if (end == len) + off = end; + else { + off = end + 2; + if (off == len) { /* trailing "::" */ + mrb_name_error(mrb, id, "wrong constant name '%v'", path); + } + } + } - mrb_get_args(mrb, "n", &id); - check_cv_name_sym(mrb, id); - return mrb_cv_get(mrb, mod, id); + return mod; } -/* 15.2.2.4.18 */ -/* - * call-seq: - * obj.class_variable_set(symbol, obj) -> obj - * - * Sets the class variable names by <i>symbol</i> to - * <i>object</i>. - * - * class Fred - * @@foo = 99 - * def foo - * @@foo - * end - * end - * Fred.class_variable_set(:@@foo, 101) #=> 101 - * Fred.new.foo #=> 101 - */ - static mrb_value -mrb_mod_cvar_set(mrb_state *mrb, mrb_value mod) +mrb_mod_const_set(mrb_state *mrb, mrb_value mod) { - mrb_value value; mrb_sym id; + mrb_value value; mrb_get_args(mrb, "no", &id, &value); - check_cv_name_sym(mrb, id); - mrb_cv_set(mrb, mod, id, value); + check_const_name_sym(mrb, id); + mrb_const_set(mrb, mod, id, value); return value; } -/* 15.2.2.4.39 */ -/* - * call-seq: - * remove_class_variable(sym) -> obj - * - * Removes the definition of the <i>sym</i>, returning that - * constant's value. - * - * class Dummy - * @@var = 99 - * puts @@var - * p class_variables - * remove_class_variable(:@@var) - * p class_variables - * end - * - * <em>produces:</em> - * - * 99 - * [:@@var] - * [] - */ - static mrb_value -mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod) +mrb_mod_remove_const(mrb_state *mrb, mrb_value mod) { - mrb_value val; mrb_sym id; + mrb_value val; mrb_get_args(mrb, "n", &id); - check_cv_name_sym(mrb, id); - + check_const_name_sym(mrb, id); val = mrb_iv_remove(mrb, mod, id); - if (!mrb_undef_p(val)) return val; - - if (mrb_cv_defined(mrb, mod, id)) { - mrb_name_error(mrb, id, "cannot remove %S for %S", - mrb_sym2str(mrb, id), mod); + if (mrb_undef_p(val)) { + mrb_name_error(mrb, id, "constant %n not defined", id); } + return val; +} - mrb_name_error(mrb, id, "class variable %S not defined for %S", - mrb_sym2str(mrb, id), mod); +static mrb_value +mrb_mod_const_missing(mrb_state *mrb, mrb_value mod) +{ + mrb_sym sym; - /* not reached */ - return mrb_nil_value(); + mrb_get_args(mrb, "n", &sym); + + if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) { + mrb_name_error(mrb, sym, "uninitialized constant %v::%n", mod, sym); + } + else { + mrb_name_error(mrb, sym, "uninitialized constant %n", sym); + } + /* not reached */ + return mrb_nil_value(); } /* 15.2.2.4.34 */ @@ -1845,197 +2508,367 @@ 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 void -remove_method(mrb_state *mrb, mrb_value mod, mrb_sym mid) +void +mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid) { - struct RClass *c = mrb_class_ptr(mod); - khash_t(mt) *h = c->mt; - khiter_t k; + mrb_sym added; + mrb_value recv = mrb_obj_value(c); - if (h) { - k = kh_get(mt, mrb, h, mid); - if (k != kh_end(h)) { - kh_del(mt, mrb, h, k); - return; - } + if (c->tt == MRB_TT_SCLASS) { + added = MRB_SYM(singleton_method_added); + recv = mrb_iv_get(mrb, recv, MRB_SYM(__attached__)); } - - mrb_name_error(mrb, mid, "method `%S' not defined in %S", - mrb_sym2str(mrb, mid), mod); + else { + added = MRB_SYM(method_added); + } + mrb_funcall_id(mrb, recv, added, 1, mrb_symbol_value(mid)); } -/* 15.2.2.4.41 */ -/* - * call-seq: - * remove_method(symbol) -> self - * - * Removes the method identified by _symbol_ from the current - * class. For an example, see <code>Module.undef_method</code>. - */ - -static mrb_value -mrb_mod_remove_method(mrb_state *mrb, mrb_value mod) +mrb_value +mrb_mod_define_method_m(mrb_state *mrb, struct RClass *c) { - mrb_int argc; - mrb_value *argv; + struct RProc *p; + mrb_method_t m; + mrb_sym mid; + mrb_value proc = mrb_undef_value(); + mrb_value blk; - mrb_get_args(mrb, "*", &argv, &argc); - while (argc--) { - remove_method(mrb, mod, mrb_symbol(*argv)); - argv++; + mrb_get_args(mrb, "n|o&", &mid, &proc, &blk); + switch (mrb_type(proc)) { + case MRB_TT_PROC: + blk = proc; + break; + case MRB_TT_UNDEF: + /* ignored */ + break; + default: + mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %T (expected Proc)", proc); + break; } - return 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); + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } + 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 void -check_const_name_sym(mrb_state *mrb, mrb_sym id) +static mrb_value +mod_define_method(mrb_state *mrb, mrb_value self) { - check_const_name_str(mrb, mrb_sym2str(mrb, id)); + return mrb_mod_define_method_m(mrb, mrb_class_ptr(self)); } static mrb_value -const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool inherit) +top_define_method(mrb_state *mrb, mrb_value self) { - if (inherit) { - return mrb_bool_value(mrb_const_defined(mrb, mod, id)); - } - return mrb_bool_value(mrb_const_defined_at(mrb, mod, id)); + return mrb_mod_define_method_m(mrb, mrb->object_class); } static mrb_value -mrb_mod_const_defined(mrb_state *mrb, mrb_value mod) +mrb_mod_eqq(mrb_state *mrb, mrb_value mod) { - mrb_sym id; - mrb_bool inherit = TRUE; + mrb_value obj = mrb_get_arg1(mrb); + mrb_bool eqq; - mrb_get_args(mrb, "n|b", &id, &inherit); - check_const_name_sym(mrb, id); - return const_defined(mrb, mod, id, inherit); + eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod)); + + return mrb_bool_value(eqq); } static mrb_value -mrb_mod_const_get(mrb_state *mrb, mrb_value mod) +mrb_mod_dup(mrb_state *mrb, mrb_value self) { - mrb_sym id; - - mrb_get_args(mrb, "n", &id); - check_const_name_sym(mrb, id); - return mrb_const_get(mrb, mod, id); + mrb_value mod = mrb_obj_clone(mrb, self); + MRB_UNSET_FROZEN_FLAG(mrb_obj_ptr(mod)); + return mod; } static mrb_value -mrb_mod_const_set(mrb_state *mrb, mrb_value mod) +mrb_mod_module_function(mrb_state *mrb, mrb_value mod) { - mrb_sym id; - mrb_value value; + const mrb_value *argv; + mrb_int argc, i; + mrb_sym mid; + mrb_method_t m; + struct RClass *rclass; + int ai; - mrb_get_args(mrb, "no", &id, &value); - check_const_name_sym(mrb, id); - mrb_const_set(mrb, mod, id, value); - return value; -} + mrb_check_type(mrb, mod, MRB_TT_MODULE); -static mrb_value -mrb_mod_remove_const(mrb_state *mrb, mrb_value mod) -{ - mrb_sym id; - mrb_value val; + mrb_get_args(mrb, "*", &argv, &argc); + if (argc == 0) { + /* set MODFUNC SCOPE if implemented */ + return mod; + } - mrb_get_args(mrb, "n", &id); - 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)); + /* set PRIVATE method visibility if implemented */ + /* mrb_mod_dummy_visibility(mrb, mod); */ + + for (i=0; i<argc; i++) { + mrb_check_type(mrb, argv[i], MRB_TT_SYMBOL); + + mid = mrb_symbol(argv[i]); + rclass = mrb_class_ptr(mod); + m = mrb_method_search(mrb, rclass, mid); + + prepare_singleton_class(mrb, (struct RBasic*)rclass); + ai = mrb_gc_arena_save(mrb); + mrb_define_method_raw(mrb, rclass->c, mid, m); + mrb_gc_arena_restore(mrb, ai); } - return val; + + return mod; } -static mrb_value -mrb_mod_const_missing(mrb_state *mrb, mrb_value mod) +static struct RClass* +mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) { - mrb_sym sym; + struct RClass *klass = mrb_basic_ptr(obj)->c; - mrb_get_args(mrb, "n", &sym); + 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); - 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)); + 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 { - mrb_name_error(mrb, sym, "uninitialized constant %S", - mrb_sym2str(mrb, sym)); + dc->mt = mt_new(mrb); } - /* not reached */ - return mrb_nil_value(); + dc->super = sc->super; + MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc)); } +/* 15.3.1.3.16 */ static mrb_value -mrb_mod_s_constants(mrb_state *mrb, mrb_value mod) +mrb_obj_init_copy(mrb_state *mrb, mrb_value self) { - mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented"); - return mrb_nil_value(); /* not reached */ + 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 mrb_value -mrb_mod_eqq(mrb_state *mrb, mrb_value mod) +static void +init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj) { - mrb_value obj; - mrb_bool eqq; - - mrb_get_args(mrb, "o", &obj); - eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod)); + 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; - return mrb_bool_value(eqq); + 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_mod_module_function(mrb_state *mrb, mrb_value mod) +mrb_obj_clone(mrb_state *mrb, mrb_value self) { - mrb_value *argv; - mrb_int argc, i; - mrb_sym mid; - struct RProc *method_rproc; - struct RClass *rclass; - int ai; + struct RObject *p; + mrb_value clone; - mrb_check_type(mrb, mod, MRB_TT_MODULE); - - mrb_get_args(mrb, "*", &argv, &argc); - if(argc == 0) { - /* set MODFUNC SCOPE if implemented */ - return mod; + 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; - /* set PRIVATE method visibility if implemented */ - /* mrb_mod_dummy_visibility(mrb, mod); */ + return clone; +} - for (i=0; i<argc; i++) { - mrb_check_type(mrb, argv[i], MRB_TT_SYMBOL); +/* 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. + */ - mid = mrb_symbol(argv[i]); - rclass = mrb_class_ptr(mod); - method_rproc = mrb_method_search(mrb, rclass, mid); +MRB_API mrb_value +mrb_obj_dup(mrb_state *mrb, mrb_value obj) +{ + struct RBasic *p; + mrb_value dup; - prepare_singleton_class(mrb, (struct RBasic*)rclass); - ai = mrb_gc_arena_save(mrb); - mrb_define_method_raw(mrb, rclass->c, mid, method_rproc); - mrb_gc_arena_restore(mrb, ai); + 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 mod; + return dup; +} + +/* implementation of __id__ */ +mrb_value mrb_obj_id_m(mrb_state *mrb, mrb_value self); +/* implementation of instance_eval */ +mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value); + +static mrb_value +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) { @@ -2057,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 */ - name_class(mrb, bob, mrb_intern_lit(mrb, "BasicObject")); - name_class(mrb, obj, mrb_intern_lit(mrb, "Object")); /* 15.2.1 */ - name_class(mrb, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */ - name_class(mrb, 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); @@ -2075,36 +2907,36 @@ mrb_init_class(mrb_state *mrb) MRB_SET_INSTANCE_TT(cls, MRB_TT_CLASS); 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, "method_missing", mrb_bob_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */ - - mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)); + mrb_define_method(mrb, bob, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.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_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_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, "class_variable_defined?", mrb_mod_cvar_defined, MRB_ARGS_REQ(1)); /* 15.2.2.4.16 */ - mrb_define_method(mrb, mod, "class_variable_get", mrb_mod_cvar_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.17 */ - mrb_define_method(mrb, mod, "class_variable_set", mrb_mod_cvar_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.18 */ 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 */ - mrb_define_method(mrb, mod, "include", mrb_mod_include, MRB_ARGS_ANY()); /* 15.2.2.4.27 */ + mrb_define_method(mrb, mod, "prepended", mrb_bob_init, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, mod, "prepend_features", mrb_mod_prepend_features, MRB_ARGS_REQ(1)); mrb_define_method(mrb, mod, "include?", mrb_mod_include_p, MRB_ARGS_REQ(1)); /* 15.2.2.4.28 */ mrb_define_method(mrb, mod, "append_features", mrb_mod_append_features, MRB_ARGS_REQ(1)); /* 15.2.2.4.10 */ mrb_define_method(mrb, mod, "class_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.15 */ mrb_define_method(mrb, mod, "included", mrb_bob_init, MRB_ARGS_REQ(1)); /* 15.2.2.4.29 */ - mrb_define_method(mrb, mod, "included_modules", mrb_mod_included_modules, MRB_ARGS_NONE()); /* 15.2.2.4.30 */ mrb_define_method(mrb, mod, "initialize", mrb_mod_initialize, MRB_ARGS_NONE()); /* 15.2.2.4.31 */ - mrb_define_method(mrb, mod, "instance_methods", mrb_mod_instance_methods, MRB_ARGS_ANY()); /* 15.2.2.4.33 */ - 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, "module_eval", mrb_mod_module_eval, MRB_ARGS_ANY()); /* 15.2.2.4.35 */ mrb_define_method(mrb, mod, "module_function", mrb_mod_module_function, MRB_ARGS_ANY()); mrb_define_method(mrb, mod, "private", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.36 */ mrb_define_method(mrb, mod, "protected", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.37 */ mrb_define_method(mrb, mod, "public", mrb_mod_dummy_visibility, MRB_ARGS_ANY()); /* 15.2.2.4.38 */ - mrb_define_method(mrb, mod, "remove_class_variable", mrb_mod_remove_cvar, MRB_ARGS_REQ(1)); /* 15.2.2.4.39 */ - mrb_define_method(mrb, mod, "remove_method", mrb_mod_remove_method, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ mrb_define_method(mrb, mod, "attr_reader", mrb_mod_attr_reader, MRB_ARGS_ANY()); /* 15.2.2.4.13 */ mrb_define_method(mrb, mod, "attr_writer", mrb_mod_attr_writer, MRB_ARGS_ANY()); /* 15.2.2.4.14 */ mrb_define_method(mrb, mod, "to_s", mrb_mod_to_s, MRB_ARGS_NONE()); @@ -2115,14 +2947,21 @@ mrb_init_class(mrb_state *mrb) mrb_define_method(mrb, mod, "const_defined?", mrb_mod_const_defined, MRB_ARGS_ARG(1,1)); /* 15.2.2.4.20 */ mrb_define_method(mrb, mod, "const_get", mrb_mod_const_get, MRB_ARGS_REQ(1)); /* 15.2.2.4.21 */ mrb_define_method(mrb, mod, "const_set", mrb_mod_const_set, MRB_ARGS_REQ(2)); /* 15.2.2.4.23 */ - mrb_define_method(mrb, mod, "constants", mrb_mod_constants, MRB_ARGS_OPT(1)); /* 15.2.2.4.24 */ mrb_define_method(mrb, mod, "remove_const", mrb_mod_remove_const, MRB_ARGS_REQ(1)); /* 15.2.2.4.40 */ mrb_define_method(mrb, mod, "const_missing", mrb_mod_const_missing, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, mod, "define_method", mod_define_method, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, mod, "class_variables", mrb_mod_class_variables, MRB_ARGS_NONE()); /* 15.2.2.4.19 */ - mrb_define_method(mrb, mod, "===", mrb_mod_eqq, MRB_ARGS_REQ(1)); - mrb_define_class_method(mrb, mod, "constants", mrb_mod_s_constants, MRB_ARGS_ANY()); /* 15.2.2.3.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)); /* 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 = 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 new file mode 100644 index 000000000..4d1d96171 --- /dev/null +++ b/src/codedump.c @@ -0,0 +1,595 @@ +#include <mruby.h> +#include <mruby/irep.h> +#include <mruby/debug.h> +#include <mruby/opcode.h> +#include <mruby/string.h> +#include <mruby/proc.h> +#include <mruby/dump.h> + +#ifndef MRB_NO_STDIO +static void +print_r(mrb_state *mrb, const mrb_irep *irep, size_t n) +{ + if (n == 0) return; + 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, const mrb_irep *irep, uint16_t a) +{ + if (!irep->lv || a >= irep->nlocals || a == 0) { + printf("\n"); + return; + } + printf("\t;"); + print_r(mrb, irep, a); + printf("\n"); +} + +static void +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"); + return; + } + printf("\t;"); + if (a > 0) print_r(mrb, irep, a); + if (b > 0) print_r(mrb, irep, b); + printf("\n"); +} + +static void +print_header(mrb_state *mrb, const mrb_irep *irep, uint32_t i) +{ + int32_t line; + + line = mrb_debug_get_line(mrb, irep, i); + if (line < 0) { + printf(" "); + } + else { + printf("%5d ", line); + } + + printf("%03d ", (int)i); +} + +#define CASE(insn,ops) case insn: FETCH_ ## ops (); L_ ## insn + +static void +codedump(mrb_state *mrb, const mrb_irep *irep) +{ + int ai; + 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 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 *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); + } + } + + pc = irep->iseq; + pcend = pc + irep->ilen; + while (pc < pcend) { + ptrdiff_t i; + uint32_t a; + uint16_t b; + uint16_t c; + + ai = mrb_gc_arena_save(mrb); + + i = pc - irep->iseq; + 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(mrb, irep, (uint32_t)i); + ins = READ_B(); + switch (ins) { + CASE(OP_NOP, Z): + printf("OP_NOP\n"); + break; + CASE(OP_MOVE, BB): + printf("OP_MOVE\tR%d\tR%d\t", a, b); + print_lv_ab(mrb, irep, a, b); + break; + + CASE(OP_LOADL, BB): + 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; + CASE(OP_LOADI, BB): + printf("OP_LOADI\tR%d\t%d\t", a, b); + print_lv_a(mrb, irep, a); + break; + CASE(OP_LOADINEG, BB): + 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); + break; + CASE(OP_LOADI_0, B): goto L_LOADI; + CASE(OP_LOADI_1, B): goto L_LOADI; + CASE(OP_LOADI_2, B): goto L_LOADI; + CASE(OP_LOADI_3, B): goto L_LOADI; + CASE(OP_LOADI_4, B): goto L_LOADI; + CASE(OP_LOADI_5, B): goto L_LOADI; + CASE(OP_LOADI_6, B): goto L_LOADI; + CASE(OP_LOADI_7, B): + L_LOADI: + printf("OP_LOADI_%d\tR%d\t\t", ins-(int)OP_LOADI_0, a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_LOADSYM, BB): + 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): + printf("OP_LOADNIL\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_LOADSELF, B): + printf("OP_LOADSELF\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_LOADT, B): + printf("OP_LOADT\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_LOADF, B): + printf("OP_LOADF\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_GETGV, BB): + 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_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_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_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_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_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_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_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_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_sym_dump(mrb, irep->syms[b]), a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_GETUPVAR, BBB): + printf("OP_GETUPVAR\tR%d\t%d\t%d", a, b, c); + print_lv_a(mrb, irep, a); + break; + CASE(OP_SETUPVAR, BBB): + printf("OP_SETUPVAR\tR%d\t%d\t%d", a, b, c); + print_lv_a(mrb, irep, a); + break; + CASE(OP_GETCV, BB): + 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_sym_dump(mrb, irep->syms[b]), a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_JMP, S): + 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): + 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): + 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): + 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_sym_dump(mrb, irep->syms[b])); + break; + CASE(OP_SENDVB, BB): + 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_sym_dump(mrb, irep->syms[b]), c); + break; + CASE(OP_SENDB, BBB): + 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"); + break; + CASE(OP_SUPER, BB): + printf("OP_SUPER\tR%d\t%d\n", a, b); + break; + CASE(OP_ARGARY, BS): + printf("OP_ARGARY\tR%d\t%d:%d:%d:%d (%d)", a, + (b>>11)&0x3f, + (b>>10)&0x1, + (b>>5)&0x1f, + (b>>4)&0x1, + (b>>0)&0xf); + print_lv_a(mrb, irep, a); + break; + CASE(OP_ENTER, W): + printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n", + 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_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_sym_dump(mrb, irep->syms[b])); + print_lv_a(mrb, irep, a); + break; + CASE(OP_RETURN, B): + printf("OP_RETURN\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_RETURN_BLK, B): + printf("OP_RETURN_BLK\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_BREAK, B): + printf("OP_BREAK\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_BLKPUSH, BS): + printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d (%d)", a, + (b>>11)&0x3f, + (b>>10)&0x1, + (b>>5)&0x1f, + (b>>4)&0x1, + (b>>0)&0xf); + print_lv_a(mrb, irep, a); + break; + CASE(OP_LAMBDA, BB): + 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, (void*)irep->reps[b]); + break; + CASE(OP_METHOD, BB): + 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); + break; + CASE(OP_RANGE_EXC, B): + printf("OP_RANGE_EXC\tR%d\n", a); + break; + CASE(OP_DEF, BB): + 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_sym_dump(mrb, irep->syms[a])); + break; + CASE(OP_ALIAS, BB): + 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, B): + printf("OP_ADD\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_ADDI, BB): + printf("OP_ADDI\tR%d\t%d\n", a, b); + break; + CASE(OP_SUB, B): + printf("OP_SUB\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_SUBI, BB): + printf("OP_SUBI\tR%d\t%d\n", a, b); + break; + CASE(OP_MUL, B): + printf("OP_MUL\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_DIV, B): + printf("OP_DIV\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_LT, B): + printf("OP_LT\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_LE, B): + printf("OP_LE\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_GT, B): + printf("OP_GT\t\tR%d\tR%d\n", a, a+1); + break; + CASE(OP_GE, B): + printf("OP_GE\t\tR%d\tR%d\n", a, a+1); + break; + 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); + print_lv_a(mrb, irep, a); + break; + CASE(OP_ARRAY2, BBB): + printf("OP_ARRAY\tR%d\tR%d\t%d\t", a, b, c); + print_lv_ab(mrb, irep, a, b); + break; + CASE(OP_ARYCAT, B): + printf("OP_ARYCAT\tR%d\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_ARYPUSH, B): + printf("OP_ARYPUSH\tR%d\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_ARYDUP, B): + printf("OP_ARYDUP\tR%d\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_AREF, BBB): + printf("OP_AREF\tR%d\tR%d\t%d", a, b, c); + print_lv_ab(mrb, irep, a, b); + break; + CASE(OP_ASET, BBB): + printf("OP_ASET\tR%d\tR%d\t%d", a, b, c); + print_lv_ab(mrb, irep, a, b); + break; + CASE(OP_APOST, BBB): + printf("OP_APOST\tR%d\t%d\t%d", a, b, c); + print_lv_a(mrb, irep, a); + break; + CASE(OP_INTERN, B): + printf("OP_INTERN\tR%d", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_STRING, BB): + 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; + CASE(OP_STRCAT, B): + printf("OP_STRCAT\tR%d\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_HASH, BB): + printf("OP_HASH\tR%d\t%d\t", a, b); + print_lv_a(mrb, irep, a); + break; + CASE(OP_HASHADD, BB): + printf("OP_HASHADD\tR%d\t%d\t", a, b); + print_lv_a(mrb, irep, a); + break; + CASE(OP_HASHCAT, B): + printf("OP_HASHCAT\tR%d\t", a); + print_lv_a(mrb, irep, a); + break; + + CASE(OP_OCLASS, B): + printf("OP_OCLASS\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_CLASS, BB): + 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_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, (void*)irep->reps[b]); + print_lv_a(mrb, irep, a); + break; + CASE(OP_SCLASS, B): + printf("OP_SCLASS\tR%d\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_TCLASS, B): + printf("OP_TCLASS\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_ERR, B): + 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_EXCEPT, B): + printf("OP_EXCEPT\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + CASE(OP_RESCUE, BB): + printf("OP_RESCUE\tR%d\tR%d", a, b); + print_lv_ab(mrb, irep, a, b); + break; + CASE(OP_RAISEIF, B): + printf("OP_RAISEIF\tR%d\t\t", a); + print_lv_a(mrb, irep, a); + break; + + CASE(OP_DEBUG, BBB): + printf("OP_DEBUG\t%d\t%d\t%d\n", a, b, c); + break; + + CASE(OP_STOP, Z): + printf("OP_STOP\n"); + break; + + CASE(OP_EXT1, Z): + ins = READ_B(); + printf("OP_EXT1\n"); + 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" +#undef OPCODE + } + break; + CASE(OP_EXT2, Z): + ins = READ_B(); + printf("OP_EXT2\n"); + 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" +#undef OPCODE + } + break; + CASE(OP_EXT3, Z): + ins = READ_B(); + printf("OP_EXT3\n"); + 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" +#undef OPCODE + } + break; + + default: + printf("OP_unknown (0x%x)\n", ins); + break; + } + mrb_gc_arena_restore(mrb, ai); + } + printf("\n"); +} + +static void +codedump_recur(mrb_state *mrb, const mrb_irep *irep) +{ + int i; + + codedump(mrb, irep); + if (irep->reps) { + for (i=0; i<irep->rlen; i++) { + codedump_recur(mrb, irep->reps[i]); + } + } +} +#endif + +void +mrb_codedump_all(mrb_state *mrb, struct RProc *proc) +{ +#ifndef MRB_NO_STDIO + codedump_recur(mrb, proc->body.irep); +#endif +} diff --git a/src/codegen.c b/src/codegen.c deleted file mode 100644 index a880244cc..000000000 --- a/src/codegen.c +++ /dev/null @@ -1,3156 +0,0 @@ -/* -** codegen.c - mruby code generator -** -** See Copyright Notice in mruby.h -*/ - -#include <ctype.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include "mruby.h" -#include "mruby/compile.h" -#include "mruby/proc.h" -#include "mruby/numeric.h" -#include "mruby/string.h" -#include "mruby/debug.h" -#include "node.h" -#include "mruby/opcode.h" -#include "mruby/re.h" -#include "mrb_throw.h" - -typedef mrb_ast_node node; -typedef struct mrb_parser_state parser_state; - -enum looptype { - LOOP_NORMAL, - LOOP_BLOCK, - LOOP_FOR, - LOOP_BEGIN, - LOOP_RESCUE, -}; - -struct loopinfo { - enum looptype type; - int pc1, pc2, pc3, acc; - int ensure_level; - struct loopinfo *prev; -}; - -typedef struct scope { - mrb_state *mrb; - mrb_pool *mpool; - struct mrb_jmpbuf jmp; - - struct scope *prev; - - node *lv; - - int sp; - int pc; - int lastlabel; - int ainfo:15; - mrb_bool mscope:1; - - struct loopinfo *loop; - int ensure_level; - char const *filename; - uint16_t lineno; - - mrb_code *iseq; - uint16_t *lines; - int icapa; - - mrb_irep *irep; - size_t pcapa; - size_t scapa; - size_t rcapa; - - uint16_t nlocals; - uint16_t nregs; - int ai; - - int debug_start_pos; - uint16_t filename_index; - parser_state* parser; -} codegen_scope; - -static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv); -static void scope_finish(codegen_scope *s); -static struct loopinfo *loop_push(codegen_scope *s, enum looptype t); -static void loop_break(codegen_scope *s, node *tree); -static void loop_pop(codegen_scope *s, int val); - -static void gen_assignment(codegen_scope *s, node *tree, int sp, int val); -static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val); - -static void codegen(codegen_scope *s, node *tree, int val); -static void raise_error(codegen_scope *s, const char *msg); - -static void -codegen_error(codegen_scope *s, const char *message) -{ - if (!s) return; - while (s->prev) { - codegen_scope *tmp = s->prev; - mrb_pool_close(s->mpool); - s = tmp; - } -#ifdef ENABLE_STDIO - if (s->filename && s->lineno) { - fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message); - } - else { - fprintf(stderr, "codegen error: %s\n", message); - } -#endif - MRB_THROW(&s->jmp); -} - -static void* -codegen_palloc(codegen_scope *s, size_t len) -{ - void *p = mrb_pool_alloc(s->mpool, len); - - if (!p) codegen_error(s, "pool memory allocation"); - return p; -} - -static void* -codegen_malloc(codegen_scope *s, size_t len) -{ - void *p = mrb_malloc_simple(s->mrb, len); - - if (!p) codegen_error(s, "mrb_malloc"); - return p; -} - -static void* -codegen_realloc(codegen_scope *s, void *p, size_t len) -{ - p = mrb_realloc_simple(s->mrb, p, len); - - if (!p && len > 0) codegen_error(s, "mrb_realloc"); - return p; -} - -static int -new_label(codegen_scope *s) -{ - s->lastlabel = s->pc; - return s->pc; -} - -static inline int -genop(codegen_scope *s, mrb_code i) -{ - if (s->pc == s->icapa) { - s->icapa *= 2; - s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa); - if (s->lines) { - s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa); - s->irep->lines = s->lines; - } - } - s->iseq[s->pc] = i; - if (s->lines) { - s->lines[s->pc] = s->lineno; - } - return s->pc++; -} - -#define NOVAL 0 -#define VAL 1 - -static mrb_bool -no_optimize(codegen_scope *s) -{ - if (s && s->parser && s->parser->no_optimize) - return TRUE; - return FALSE; -} - -static int -genop_peep(codegen_scope *s, mrb_code i, int val) -{ - /* peephole optimization */ - if (!no_optimize(s) && s->lastlabel != s->pc && s->pc > 0) { - mrb_code i0 = s->iseq[s->pc-1]; - int c1 = GET_OPCODE(i); - int c0 = GET_OPCODE(i0); - - switch (c1) { - case OP_MOVE: - if (GETARG_A(i) == GETARG_B(i)) { - /* skip useless OP_MOVE */ - return 0; - } - if (val) break; - switch (c0) { - case OP_MOVE: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) { - /* skip swapping OP_MOVE */ - return 0; - } - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->pc--; - return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val); - } - break; - case OP_LOADI: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0)); - return 0; - } - break; - case OP_ARRAY: - case OP_HASH: - case OP_RANGE: - case OP_AREF: - case OP_GETUPVAR: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0)); - return 0; - } - break; - case OP_LOADSYM: - case OP_GETGLOBAL: - case OP_GETIV: - case OP_GETCV: - case OP_GETCONST: - case OP_GETSPECIAL: - case OP_LOADL: - case OP_STRING: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0)); - return 0; - } - break; - case OP_SCLASS: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0)); - return 0; - } - break; - case OP_LOADNIL: - case OP_LOADSELF: - case OP_LOADT: - case OP_LOADF: - case OP_OCLASS: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i)); - return 0; - } - break; - default: - break; - } - break; - case OP_SETIV: - case OP_SETCV: - case OP_SETCONST: - case OP_SETMCNST: - case OP_SETGLOBAL: - if (val) break; - if (c0 == OP_MOVE) { - if (GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i)); - return 0; - } - } - break; - case OP_SETUPVAR: - if (val) break; - if (c0 == OP_MOVE) { - if (GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i)); - return 0; - } - } - break; - case OP_EPOP: - if (c0 == OP_EPOP) { - s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i)); - return 0; - } - break; - case OP_POPERR: - if (c0 == OP_POPERR) { - s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i)); - return 0; - } - break; - case OP_RETURN: - switch (c0) { - case OP_RETURN: - return 0; - case OP_MOVE: - if (GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL); - return 0; - } - break; - case OP_SETIV: - case OP_SETCV: - case OP_SETCONST: - case OP_SETMCNST: - case OP_SETUPVAR: - case OP_SETGLOBAL: - s->pc--; - genop_peep(s, i0, NOVAL); - i0 = s->iseq[s->pc-1]; - return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL)); -#if 0 - case OP_SEND: - if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0)); - return; - } - break; -#endif - default: - break; - } - break; - case OP_ADD: - case OP_SUB: - if (c0 == OP_LOADI) { - int c = GETARG_sBx(i0); - - if (c1 == OP_SUB) c = -c; - if (c > 127 || c < -127) break; - if (0 <= c) - s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c); - else - s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c); - return 0; - } - case OP_STRCAT: - if (c0 == OP_STRING) { - mrb_value v = s->irep->pool[GETARG_Bx(i0)]; - - if (mrb_string_p(v) && RSTRING_LEN(v) == 0) { - s->pc--; - return 0; - } - } - break; - case OP_JMPIF: - case OP_JMPNOT: - if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i)); - return s->pc-1; - } - break; - default: - break; - } - } - return genop(s, i); -} - -static void -scope_error(codegen_scope *s) -{ - exit(EXIT_FAILURE); -} - -static inline void -dispatch(codegen_scope *s, int pc) -{ - int diff = s->pc - pc; - mrb_code i = s->iseq[pc]; - int c = GET_OPCODE(i); - - s->lastlabel = s->pc; - switch (c) { - case OP_JMP: - case OP_JMPIF: - case OP_JMPNOT: - case OP_ONERR: - break; - default: -#ifdef ENABLE_STDIO - fprintf(stderr, "bug: dispatch on non JMP op\n"); -#endif - scope_error(s); - break; - } - s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff); -} - -static void -dispatch_linked(codegen_scope *s, int pc) -{ - mrb_code i; - int pos; - - if (!pc) return; - for (;;) { - i = s->iseq[pc]; - pos = GETARG_sBx(i); - dispatch(s, pc); - if (!pos) break; - pc = pos; - } -} - -#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0) -static void -push_(codegen_scope *s) -{ - if (s->sp > 511) { - codegen_error(s, "too complex expression"); - } - s->sp++; - nregs_update; -} - -#define push() push_(s) -#define pop_(s) ((s)->sp--) -#define pop() pop_(s) -#define pop_n(n) (s->sp-=(n)) -#define cursp() (s->sp) - -static inline int -new_lit(codegen_scope *s, mrb_value val) -{ - size_t i; - mrb_value *pv; - - switch (mrb_type(val)) { - case MRB_TT_STRING: - for (i=0; i<s->irep->plen; i++) { - mrb_int len; - pv = &s->irep->pool[i]; - - if (mrb_type(*pv) != MRB_TT_STRING) continue; - if ((len = RSTRING_LEN(*pv)) != RSTRING_LEN(val)) continue; - if (memcmp(RSTRING_PTR(*pv), RSTRING_PTR(val), len) == 0) - return i; - } - break; - case MRB_TT_FLOAT: - for (i=0; i<s->irep->plen; i++) { - pv = &s->irep->pool[i]; - if (mrb_type(*pv) != MRB_TT_FLOAT) continue; - if (mrb_float(*pv) == mrb_float(val)) return i; - } - break; - case MRB_TT_FIXNUM: - for (i=0; i<s->irep->plen; i++) { - pv = &s->irep->pool[i]; - if (!mrb_fixnum_p(*pv)) continue; - if (mrb_fixnum(*pv) == mrb_fixnum(val)) return i; - } - break; - default: - /* should not happen */ - return 0; - } - - if (s->irep->plen == s->pcapa) { - s->pcapa *= 2; - s->irep->pool = (mrb_value *)codegen_realloc(s, s->irep->pool, sizeof(mrb_value)*s->pcapa); - } - - pv = &s->irep->pool[s->irep->plen]; - i = s->irep->plen++; - - switch (mrb_type(val)) { - case MRB_TT_STRING: - *pv = mrb_str_pool(s->mrb, val); - break; - - case MRB_TT_FLOAT: -#ifdef MRB_WORD_BOXING - *pv = mrb_float_pool(s->mrb, mrb_float(val)); - break; -#endif - case MRB_TT_FIXNUM: - *pv = val; - break; - - default: - /* should not happen */ - break; - } - return i; -} - -static inline int -new_msym(codegen_scope *s, mrb_sym sym) -{ - size_t i, len; - - mrb_assert(s->irep); - - len = s->irep->slen; - if (len > 256) len = 256; - for (i=0; i<len; i++) { - if (s->irep->syms[i] == sym) return i; - if (s->irep->syms[i] == 0) break; - } - if (i == 256) { - codegen_error(s, "too many symbols (max 256)"); - } - s->irep->syms[i] = sym; - if (i == s->irep->slen) s->irep->slen++; - return i; -} - -static inline int -new_sym(codegen_scope *s, mrb_sym sym) -{ - size_t i; - - for (i=0; i<s->irep->slen; i++) { - if (s->irep->syms[i] == sym) return i; - } - if (s->irep->slen > 125 && s->irep->slen < 256) { - s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*65536); - for (i = 0; i < 256 - s->irep->slen; i++) { - static const mrb_sym mrb_sym_zero = { 0 }; - s->irep->syms[i + s->irep->slen] = mrb_sym_zero; - } - s->irep->slen = 256; - } - s->irep->syms[s->irep->slen] = sym; - return s->irep->slen++; -} - -static int -node_len(node *tree) -{ - int n = 0; - - while (tree) { - n++; - tree = tree->cdr; - } - return n; -} - -#define sym(x) ((mrb_sym)(intptr_t)(x)) -#define lv_name(lv) sym((lv)->car) -static int -lv_idx(codegen_scope *s, mrb_sym id) -{ - node *lv = s->lv; - int n = 1; - - while (lv) { - if (lv_name(lv) == id) return n; - n++; - lv = lv->cdr; - } - return 0; -} - -static void -for_body(codegen_scope *s, node *tree) -{ - codegen_scope *prev = s; - int idx; - struct loopinfo *lp; - node *n2; - mrb_code c; - - /* generate receiver */ - codegen(s, tree->cdr->car, VAL); - /* generate loop-block */ - s = scope_new(s->mrb, s, NULL); - if (s == NULL) { - raise_error(prev, "unexpected scope"); - } - - push(); /* push for a block parameter */ - - lp = loop_push(s, LOOP_FOR); - lp->pc1 = new_label(s); - - /* generate loop variable */ - n2 = tree->car; - genop(s, MKOP_Ax(OP_ENTER, 0x40000)); - if (n2->car && !n2->car->cdr && !n2->cdr) { - gen_assignment(s, n2->car->car, 1, NOVAL); - } - else { - gen_vmassignment(s, n2, 1, VAL); - } - codegen(s, tree->cdr->cdr->car, VAL); - pop(); - if (s->pc > 0) { - c = s->iseq[s->pc-1]; - if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - loop_pop(s, NOVAL); - scope_finish(s); - s = prev; - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK)); - pop(); - idx = new_msym(s, mrb_intern_lit(s->mrb, "each")); - genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0)); -} - -static int -lambda_body(codegen_scope *s, node *tree, int blk) -{ - mrb_code c; - codegen_scope *parent = s; - s = scope_new(s->mrb, s, tree->car); - if (s == NULL) { - raise_error(parent, "unexpected scope"); - } - - s->mscope = !blk; - - if (blk) { - struct loopinfo *lp = loop_push(s, LOOP_BLOCK); - lp->pc1 = new_label(s); - } - tree = tree->cdr; - if (tree->car) { - mrb_aspec a; - int ma, oa, ra, pa, ka, kd, ba; - int pos, i; - node *n, *opt; - - ma = node_len(tree->car->car); - n = tree->car->car; - while (n) { - n = n->cdr; - } - oa = node_len(tree->car->cdr->car); - ra = tree->car->cdr->cdr->car ? 1 : 0; - pa = node_len(tree->car->cdr->cdr->cdr->car); - ka = kd = 0; - ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0; - - a = ((mrb_aspec)(ma & 0x1f) << 18) - | ((mrb_aspec)(oa & 0x1f) << 13) - | ((ra & 1) << 12) - | ((pa & 0x1f) << 7) - | ((ka & 0x1f) << 2) - | ((kd & 1)<< 1) - | (ba & 1); - s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */ - | ((ra & 1) << 5) - | (pa & 0x1f); - genop(s, MKOP_Ax(OP_ENTER, a)); - pos = new_label(s); - for (i=0; i<oa; i++) { - new_label(s); - genop(s, MKOP_sBx(OP_JMP, 0)); - } - if (oa > 0) { - genop(s, MKOP_sBx(OP_JMP, 0)); - } - opt = tree->car->cdr->car; - i = 0; - while (opt) { - int idx; - - dispatch(s, pos+i); - codegen(s, opt->car->cdr, VAL); - idx = lv_idx(s, (mrb_sym)(intptr_t)opt->car->car); - pop(); - genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL); - i++; - opt = opt->cdr; - } - if (oa > 0) { - dispatch(s, pos+i); - } - } - codegen(s, tree->cdr->car, VAL); - pop(); - if (s->pc > 0) { - c = s->iseq[s->pc-1]; - if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) { - if (s->nregs == 0) { - genop(s, MKOP_A(OP_LOADNIL, 0)); - genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); - } - else { - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - } - } - if (blk) { - loop_pop(s, NOVAL); - } - scope_finish(s); - return parent->irep->rlen - 1; -} - -static int -scope_body(codegen_scope *s, node *tree, int val) -{ - codegen_scope *scope = scope_new(s->mrb, s, tree->car); - if (scope == NULL) { - raise_error(s, "unexpected scope"); - } - - codegen(scope, tree->cdr, VAL); - if (!s->iseq) { - genop(scope, MKOP_A(OP_STOP, 0)); - } - else if (!val) { - genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); - } - else { - if (scope->nregs == 0) { - genop(scope, MKOP_A(OP_LOADNIL, 0)); - genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); - } - else { - genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL); - } - } - scope_finish(scope); - if (!s->irep) { - /* should not happen */ - return 0; - } - return s->irep->rlen - 1; -} - -static mrb_bool -nosplat(node *t) -{ - while (t) { - if ((intptr_t)t->car->car == NODE_SPLAT) return FALSE; - t = t->cdr; - } - return TRUE; -} - -static mrb_sym -attrsym(codegen_scope *s, mrb_sym a) -{ - const char *name; - mrb_int len; - char *name2; - - name = mrb_sym2name_len(s->mrb, a, &len); - name2 = (char *)codegen_palloc(s, - (size_t)len - + 1 /* '=' */ - + 1 /* '\0' */ - ); - mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); - memcpy(name2, name, (size_t)len); - name2[len] = '='; - name2[len+1] = '\0'; - - return mrb_intern(s->mrb, name2, len+1); -} - -static int -gen_values(codegen_scope *s, node *t, int val) -{ - int n = 0; - int is_splat; - - while (t) { - is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */ - if (n >= 127 || is_splat) { - if (val) { - pop_n(n); - genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n)); - push(); - codegen(s, t->car, VAL); - pop(); pop(); - if (is_splat) { - genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); - } - else { - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); - } - t = t->cdr; - while (t) { - push(); - codegen(s, t->car, VAL); - pop(); pop(); - if ((intptr_t)t->car->car == NODE_SPLAT) { - genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); - } - else { - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); - } - t = t->cdr; - } - } - else { - codegen(s, t->car->cdr, NOVAL); - t = t->cdr; - while (t) { - codegen(s, t->car, NOVAL); - t = t->cdr; - } - } - return -1; - } - /* normal (no splat) mode */ - codegen(s, t->car, val); - n++; - t = t->cdr; - } - return n; -} - -#define CALL_MAXARGS 127 - -static void -gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val) -{ - mrb_sym sym = name ? name : sym(tree->cdr->car); - int idx; - int n = 0, noop = 0, sendv = 0, blk = 0; - - codegen(s, tree->car, VAL); /* receiver */ - idx = new_msym(s, sym); - tree = tree->cdr->cdr->car; - if (tree) { - n = gen_values(s, tree->car, VAL); - if (n < 0) { - n = noop = sendv = 1; - push(); - } - } - if (sp) { - if (sendv) { - pop(); - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp)); - push(); - } - else { - genop(s, MKOP_AB(OP_MOVE, cursp(), sp)); - push(); - n++; - } - } - if (tree && tree->cdr) { - noop = 1; - codegen(s, tree->cdr, VAL); - pop(); - } - else { - blk = cursp(); - } - push();pop(); - pop_n(n+1); - { - mrb_int symlen; - const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen); - - if (!noop && symlen == 1 && symname[0] == '+') { - genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val); - } - else if (!noop && symlen == 1 && symname[0] == '-') { - genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val); - } - else if (!noop && symlen == 1 && symname[0] == '*') { - genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n)); - } - else if (!noop && symlen == 1 && symname[0] == '/') { - genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n)); - } - else if (!noop && symlen == 1 && symname[0] == '<') { - genop(s, MKOP_ABC(OP_LT, cursp(), idx, n)); - } - else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=') { - genop(s, MKOP_ABC(OP_LE, cursp(), idx, n)); - } - else if (!noop && symlen == 1 && symname[0] == '>') { - genop(s, MKOP_ABC(OP_GT, cursp(), idx, n)); - } - else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=') { - genop(s, MKOP_ABC(OP_GE, cursp(), idx, n)); - } - else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=') { - genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n)); - } - else { - if (sendv) n = CALL_MAXARGS; - if (blk > 0) { /* no block */ - genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n)); - } - else { - genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n)); - } - } - } - if (val) { - push(); - } -} - -static void -gen_assignment(codegen_scope *s, node *tree, int sp, int val) -{ - int idx; - int type = (intptr_t)tree->car; - - tree = tree->cdr; - switch ((intptr_t)type) { - case NODE_GVAR: - idx = new_sym(s, sym(tree)); - genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val); - break; - case NODE_LVAR: - idx = lv_idx(s, sym(tree)); - if (idx > 0) { - if (idx != sp) { - genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val); - } - break; - } - else { /* upvar */ - int lv = 0; - codegen_scope *up = s->prev; - - while (up) { - idx = lv_idx(up, sym(tree)); - if (idx > 0) { - genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val); - break; - } - lv++; - up = up->prev; - } - } - break; - case NODE_IVAR: - idx = new_sym(s, sym(tree)); - genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val); - break; - case NODE_CVAR: - idx = new_sym(s, sym(tree)); - genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val); - break; - case NODE_CONST: - idx = new_sym(s, sym(tree)); - genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val); - break; - case NODE_COLON2: - idx = new_sym(s, sym(tree->cdr)); - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL); - push(); - codegen(s, tree->car, VAL); - pop_n(2); - genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val); - break; - - case NODE_CALL: - push(); - gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL); - pop(); - if (val) { - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val); - } - break; - - case NODE_MASGN: - gen_vmassignment(s, tree->car, sp, val); - break; - - push(); - gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL); - pop(); - if (val) { - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val); - } - break; - - default: -#ifdef ENABLE_STDIO - printf("unknown lhs %d\n", type); -#endif - break; - } - if (val) push(); -} - -static void -gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val) -{ - int n = 0, post = 0; - node *t, *p; - - if (tree->car) { /* pre */ - t = tree->car; - n = 0; - while (t) { - genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n)); - gen_assignment(s, t->car, cursp(), NOVAL); - n++; - t = t->cdr; - } - } - t = tree->cdr; - if (t) { - if (t->cdr) { /* post count */ - p = t->cdr->car; - while (p) { - post++; - p = p->cdr; - } - } - if (val) { - genop(s, MKOP_AB(OP_MOVE, cursp(), rhs)); - push(); - } - genop(s, MKOP_ABC(OP_APOST, cursp(), n, post)); - n = 1; - if (t->car) { /* rest */ - gen_assignment(s, t->car, cursp(), NOVAL); - } - if (t->cdr && t->cdr->car) { - t = t->cdr->car; - while (t) { - gen_assignment(s, t->car, cursp()+n, NOVAL); - t = t->cdr; - n++; - } - } - } - if (!val) { - pop(); - } -} - -static void -gen_send_intern(codegen_scope *s) -{ - pop(); - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0)); - push(); -} -static void -gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val) -{ - if (val) { - int i = 0, j = 0; - - while (tree) { - switch ((intptr_t)tree->car->car) { - case NODE_STR: - if ((tree->cdr == NULL) && ((intptr_t)tree->car->cdr->cdr == 0)) - break; - /* fall through */ - case NODE_BEGIN: - codegen(s, tree->car, VAL); - ++j; - break; - - case NODE_LITERAL_DELIM: - if (j > 0) { - j = 0; - ++i; - if (sym) - gen_send_intern(s); - } - break; - } - if (j >= 2) { - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - j = 1; - } - tree = tree->cdr; - } - if (j > 0) { - ++i; - if (sym) - gen_send_intern(s); - } - pop_n(i); - genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i)); - push(); - } - else { - while (tree) { - switch ((intptr_t)tree->car->car) { - case NODE_BEGIN: case NODE_BLOCK: - codegen(s, tree->car, NOVAL); - } - tree = tree->cdr; - } - } -} - -static void -raise_error(codegen_scope *s, const char *msg) -{ - int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg)); - - genop(s, MKOP_ABx(OP_ERR, 1, idx)); -} - -static double -readint_float(codegen_scope *s, const char *p, int base) -{ - const char *e = p + strlen(p); - double f = 0; - int n; - - if (*p == '+') p++; - while (p < e) { - char c = *p; - c = tolower((unsigned char)c); - for (n=0; n<base; n++) { - if (mrb_digitmap[n] == c) { - f *= base; - f += n; - break; - } - } - if (n == base) { - codegen_error(s, "malformed readint input"); - } - p++; - } - return f; -} - -static mrb_int -readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow) -{ - const char *e = p + strlen(p); - mrb_int result = 0; - int n; - - mrb_assert(base >= 2 && base <= 36); - if (*p == '+') p++; - while (p < e) { - char c = *p; - c = tolower((unsigned char)c); - for (n=0; n<base; n++) { - if (mrb_digitmap[n] == c) { - break; - } - } - if (n == base) { - codegen_error(s, "malformed readint input"); - } - - if (neg) { - if ((MRB_INT_MIN + n)/base > result) { - *overflow = TRUE; - return 0; - } - result *= base; - result -= n; - } - else { - if ((MRB_INT_MAX - n)/base < result) { - *overflow = TRUE; - return 0; - } - result *= base; - result += n; - } - p++; - } - *overflow = FALSE; - return result; -} - -static void -codegen(codegen_scope *s, node *tree, int val) -{ - int nt; - - if (!tree) return; - - if (s->irep && s->filename_index != tree->filename_index) { - s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); - mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc); - s->debug_start_pos = s->pc; - s->filename_index = tree->filename_index; - s->filename = mrb_parser_get_filename(s->parser, tree->filename_index); - } - - nt = (intptr_t)tree->car; - s->lineno = tree->lineno; - tree = tree->cdr; - switch (nt) { - case NODE_BEGIN: - if (val && !tree) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - while (tree) { - codegen(s, tree->car, tree->cdr ? NOVAL : val); - tree = tree->cdr; - } - break; - - case NODE_RESCUE: - { - int onerr, noexc, exend, pos1, pos2, tmp; - struct loopinfo *lp; - - onerr = genop(s, MKOP_Bx(OP_ONERR, 0)); - lp = loop_push(s, LOOP_BEGIN); - lp->pc1 = onerr; - if (tree->car) { - codegen(s, tree->car, val); - if (val) pop(); - } - lp->type = LOOP_RESCUE; - noexc = genop(s, MKOP_Bx(OP_JMP, 0)); - dispatch(s, onerr); - tree = tree->cdr; - exend = 0; - pos1 = 0; - if (tree->car) { - node *n2 = tree->car; - int exc = cursp(); - - genop(s, MKOP_A(OP_RESCUE, exc)); - push(); - while (n2) { - node *n3 = n2->car; - node *n4 = n3->car; - - if (pos1) dispatch(s, pos1); - pos2 = 0; - do { - if (n4) { - codegen(s, n4->car, VAL); - } - else { - genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError")))); - push(); - } - genop(s, MKOP_AB(OP_MOVE, cursp(), exc)); - pop(); - if (n4 && n4->car && (intptr_t)n4->car->car == NODE_SPLAT) { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1)); - } - else { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); - } - tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); - pos2 = tmp; - if (n4) { - n4 = n4->cdr; - } - } while (n4); - pos1 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch_linked(s, pos2); - - pop(); - if (n3->cdr->car) { - gen_assignment(s, n3->cdr->car, exc, NOVAL); - } - if (n3->cdr->cdr->car) { - codegen(s, n3->cdr->cdr->car, val); - if (val) pop(); - } - tmp = genop(s, MKOP_sBx(OP_JMP, exend)); - exend = tmp; - n2 = n2->cdr; - push(); - } - if (pos1) { - dispatch(s, pos1); - genop(s, MKOP_A(OP_RAISE, exc)); - } - } - pop(); - tree = tree->cdr; - dispatch(s, noexc); - genop(s, MKOP_A(OP_POPERR, 1)); - if (tree->car) { - codegen(s, tree->car, val); - } - else if (val) { - push(); - } - dispatch_linked(s, exend); - loop_pop(s, NOVAL); - } - break; - - case NODE_ENSURE: - { - int idx; - int epush = s->pc; - - genop(s, MKOP_Bx(OP_EPUSH, 0)); - s->ensure_level++; - codegen(s, tree->car, val); - idx = scope_body(s, tree->cdr, NOVAL); - s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx); - s->ensure_level--; - genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL); - } - break; - - case NODE_LAMBDA: - { - int idx = lambda_body(s, tree, 1); - - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA)); - push(); - } - break; - - case NODE_BLOCK: - { - int idx = lambda_body(s, tree, 1); - - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK)); - push(); - } - break; - - case NODE_IF: - { - int pos1, pos2; - node *e = tree->cdr->cdr->car; - - codegen(s, tree->car, VAL); - pop(); - pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL); - - codegen(s, tree->cdr->car, val); - if (val && !(tree->cdr->car)) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - if (e) { - if (val) pop(); - pos2 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch(s, pos1); - codegen(s, e, val); - dispatch(s, pos2); - } - else { - if (val) { - pop(); - pos2 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch(s, pos1); - genop(s, MKOP_A(OP_LOADNIL, cursp())); - dispatch(s, pos2); - push(); - } - else { - dispatch(s, pos1); - } - } - } - break; - - case NODE_AND: - { - int pos; - - codegen(s, tree->car, VAL); - pop(); - pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0)); - codegen(s, tree->cdr, val); - dispatch(s, pos); - } - break; - - case NODE_OR: - { - int pos; - - codegen(s, tree->car, VAL); - pop(); - pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0)); - codegen(s, tree->cdr, val); - dispatch(s, pos); - } - break; - - case NODE_WHILE: - { - struct loopinfo *lp = loop_push(s, LOOP_NORMAL); - - lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0)); - lp->pc2 = new_label(s); - codegen(s, tree->cdr, NOVAL); - dispatch(s, lp->pc1); - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc)); - - loop_pop(s, val); - } - break; - - case NODE_UNTIL: - { - struct loopinfo *lp = loop_push(s, LOOP_NORMAL); - - lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0)); - lp->pc2 = new_label(s); - codegen(s, tree->cdr, NOVAL); - dispatch(s, lp->pc1); - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc)); - - loop_pop(s, val); - } - break; - - case NODE_FOR: - for_body(s, tree); - if (val) push(); - break; - - case NODE_CASE: - { - int head = 0; - int pos1, pos2, pos3, tmp; - node *n; - - pos3 = 0; - if (tree->car) { - head = cursp(); - codegen(s, tree->car, VAL); - } - tree = tree->cdr; - while (tree) { - n = tree->car->car; - pos1 = pos2 = 0; - while (n) { - codegen(s, n->car, VAL); - if (head) { - genop(s, MKOP_AB(OP_MOVE, cursp(), head)); - pop(); - if ((intptr_t)n->car->car == NODE_SPLAT) { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1)); - } - else { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); - } - } - else { - pop(); - } - tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); - pos2 = tmp; - n = n->cdr; - } - if (tree->car->car) { - pos1 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch_linked(s, pos2); - } - codegen(s, tree->car->cdr, val); - if (val) pop(); - tmp = genop(s, MKOP_sBx(OP_JMP, pos3)); - pos3 = tmp; - if (pos1) dispatch(s, pos1); - tree = tree->cdr; - } - if (val) { - int pos = cursp(); - genop(s, MKOP_A(OP_LOADNIL, cursp())); - if (pos3) dispatch_linked(s, pos3); - pop(); - genop(s, MKOP_AB(OP_MOVE, cursp(), pos)); - push(); - } - else if (pos3) { - dispatch_linked(s, pos3); - } - } - break; - - case NODE_SCOPE: - scope_body(s, tree, NOVAL); - break; - - case NODE_FCALL: - case NODE_CALL: - gen_call(s, tree, 0, 0, val); - break; - - case NODE_DOT2: - codegen(s, tree->car, val); - codegen(s, tree->cdr, val); - if (val) { - pop(); pop(); - genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE)); - push(); - } - break; - - case NODE_DOT3: - codegen(s, tree->car, val); - codegen(s, tree->cdr, val); - if (val) { - pop(); pop(); - genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE)); - push(); - } - break; - - case NODE_COLON2: - { - int sym = new_sym(s, sym(tree->cdr)); - - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - if (val) push(); - } - break; - - case NODE_COLON3: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - if (val) push(); - } - break; - - case NODE_ARRAY: - { - int n; - - n = gen_values(s, tree, val); - if (n >= 0) { - if (val) { - pop_n(n); - genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n)); - push(); - } - } - else if (val) { - push(); - } - } - break; - - case NODE_HASH: - { - int len = 0; - mrb_bool update = FALSE; - - while (tree) { - codegen(s, tree->car->car, val); - codegen(s, tree->car->cdr, val); - len++; - tree = tree->cdr; - if (val && len == 126) { - pop_n(len*2); - genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); - if (update) { - pop(); - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1)); - } - push(); - update = TRUE; - len = 0; - } - } - if (val) { - pop_n(len*2); - genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); - if (update) { - pop(); - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1)); - } - push(); - } - } - break; - - case NODE_SPLAT: - codegen(s, tree, VAL); - break; - - case NODE_ASGN: - codegen(s, tree->cdr, VAL); - pop(); - gen_assignment(s, tree->car, cursp(), val); - break; - - case NODE_MASGN: - { - int len = 0, n = 0, post = 0; - node *t = tree->cdr, *p; - int rhs = cursp(); - - if ((intptr_t)t->car == NODE_ARRAY && nosplat(t->cdr)) { - /* fixed rhs */ - t = t->cdr; - while (t) { - codegen(s, t->car, VAL); - len++; - t = t->cdr; - } - tree = tree->car; - if (tree->car) { /* pre */ - t = tree->car; - n = 0; - while (t) { - gen_assignment(s, t->car, rhs+n, NOVAL); - n++; - t = t->cdr; - } - } - t = tree->cdr; - if (t) { - if (t->cdr) { /* post count */ - p = t->cdr->car; - while (p) { - post++; - p = p->cdr; - } - } - if (t->car) { /* rest (len - pre - post) */ - int rn = len - post - n; - - genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn)); - gen_assignment(s, t->car, cursp(), NOVAL); - n += rn; - } - if (t->cdr && t->cdr->car) { - t = t->cdr->car; - while (n<len) { - gen_assignment(s, t->car, rhs+n, NOVAL); - t = t->cdr; - n++; - } - } - } - pop_n(len); - if (val) { - genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len)); - push(); - } - } - else { - /* variable rhs */ - codegen(s, t, VAL); - gen_vmassignment(s, tree->car, rhs, val); - } - } - break; - - case NODE_OP_ASGN: - { - mrb_sym sym = sym(tree->cdr->car); - mrb_int len; - const char *name = mrb_sym2name_len(s->mrb, sym, &len); - int idx; - - codegen(s, tree->car, VAL); - if (len == 2 && - ((name[0] == '|' && name[1] == '|') || - (name[0] == '&' && name[1] == '&'))) { - int pos; - - pop(); - pos = genop_peep(s, MKOP_AsBx(name[0] == '|' ? OP_JMPIF : OP_JMPNOT, cursp(), 0), NOVAL); - codegen(s, tree->cdr->cdr->car, VAL); - pop(); - gen_assignment(s, tree->car, cursp(), val); - dispatch(s, pos); - break; - } - codegen(s, tree->cdr->cdr->car, VAL); - push(); pop(); - pop(); pop(); - - idx = new_msym(s, sym); - if (len == 1 && name[0] == '+') { - genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val); - } - else if (len == 1 && name[0] == '-') { - genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val); - } - else if (len == 1 && name[0] == '*') { - genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1)); - } - else if (len == 1 && name[0] == '/') { - genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1)); - } - else if (len == 1 && name[0] == '<') { - genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1)); - } - else if (len == 2 && name[0] == '<' && name[1] == '=') { - genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1)); - } - else if (len == 1 && name[0] == '>') { - genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1)); - } - else if (len == 2 && name[0] == '>' && name[1] == '=') { - genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1)); - } - else { - genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1)); - } - } - gen_assignment(s, tree->car, cursp(), val); - break; - - case NODE_SUPER: - { - int n = 0, noop = 0, sendv = 0; - - push(); /* room for receiver */ - if (tree) { - node *args = tree->car; - if (args) { - n = gen_values(s, args, VAL); - if (n < 0) { - n = noop = sendv = 1; - push(); - } - } - } - if (tree && tree->cdr) { - codegen(s, tree->cdr, VAL); - pop(); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); pop(); - } - pop_n(n+1); - if (sendv) n = CALL_MAXARGS; - genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n)); - if (val) push(); - } - break; - - case NODE_ZSUPER: - { - codegen_scope *s2 = s; - int lv = 0, ainfo = 0; - - push(); /* room for receiver */ - while (!s2->mscope) { - lv++; - s2 = s2->prev; - if (!s2) break; - } - if (s2) ainfo = s2->ainfo; - genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf))); - push(); push(); pop(); /* ARGARY pushes two values */ - if (tree && tree->cdr) { - codegen(s, tree->cdr, VAL); - pop(); - } - pop(); pop(); - genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS)); - if (val) push(); - } - break; - - case NODE_RETURN: - if (tree) { - codegen(s, tree, VAL); - pop(); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - } - if (s->loop) { - genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN)); - } - else { - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - if (val) push(); - break; - - case NODE_YIELD: - { - codegen_scope *s2 = s; - int lv = 0, ainfo = 0; - int n = 0, sendv = 0; - - while (!s2->mscope) { - lv++; - s2 = s2->prev; - if (!s2) break; - } - if (s2) ainfo = s2->ainfo; - genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf))); - push(); - if (tree) { - n = gen_values(s, tree, VAL); - if (n < 0) { - n = sendv = 1; - push(); - } - } - pop_n(n+1); - if (sendv) n = CALL_MAXARGS; - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n)); - if (val) push(); - } - break; - - case NODE_BREAK: - loop_break(s, tree); - if (val) push(); - break; - - case NODE_NEXT: - if (!s->loop) { - raise_error(s, "unexpected next"); - } - else if (s->loop->type == LOOP_NORMAL) { - if (s->ensure_level > s->loop->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); - } - codegen(s, tree, NOVAL); - genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc)); - } - else { - if (tree) { - codegen(s, tree, VAL); - pop(); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - } - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - if (val) push(); - break; - - case NODE_REDO: - if (!s->loop) { - raise_error(s, "unexpected redo"); - } - else { - if (s->ensure_level > s->loop->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); - } - genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc)); - } - break; - - case NODE_RETRY: - { - const char *msg = "unexpected retry"; - - if (!s->loop) { - raise_error(s, msg); - } - else { - struct loopinfo *lp = s->loop; - int n = 0; - - while (lp && lp->type != LOOP_RESCUE) { - if (lp->type == LOOP_BEGIN) { - n++; - } - lp = lp->prev; - } - if (!lp) { - raise_error(s, msg); - } - else { - if (n > 0) { - while (n--) { - genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); - } - } - if (s->ensure_level > lp->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL); - } - genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc)); - } - } - } - break; - - case NODE_LVAR: - if (val) { - int idx = lv_idx(s, sym(tree)); - - if (idx > 0) { - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL); - } - else { - int lv = 0; - codegen_scope *up = s->prev; - - while (up) { - idx = lv_idx(up, sym(tree)); - if (idx > 0) { - genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv)); - break; - } - lv++; - up = up->prev; - } - } - push(); - } - break; - - case NODE_GVAR: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); - push(); - } - break; - - case NODE_IVAR: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETIV, cursp(), sym)); - push(); - } - break; - - case NODE_CVAR: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETCV, cursp(), sym)); - push(); - } - break; - - case NODE_CONST: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym)); - push(); - } - break; - - case NODE_DEFINED: - codegen(s, tree, VAL); - break; - - case NODE_BACK_REF: - { - char buf[2] = { '$' }; - mrb_value str; - int sym; - - buf[1] = (char)(intptr_t)tree; - str = mrb_str_new(s->mrb, buf, 2); - sym = new_sym(s, mrb_intern_str(s->mrb, str)); - genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); - push(); - } - break; - - case NODE_NTH_REF: - { - int sym; - mrb_state *mrb = s->mrb; - mrb_value fix = mrb_fixnum_value((intptr_t)tree); - mrb_value str = mrb_str_buf_new(mrb, 4); - - mrb_str_cat_lit(mrb, str, "$"); - mrb_str_cat_str(mrb, str, mrb_fixnum_to_str(mrb, fix, 10)); - sym = new_sym(s, mrb_intern_str(mrb, str)); - genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); - push(); - } - break; - - case NODE_ARG: - /* should not happen */ - break; - - case NODE_BLOCK_ARG: - codegen(s, tree, VAL); - break; - - case NODE_INT: - if (val) { - char *p = (char*)tree->car; - int base = (intptr_t)tree->cdr->car; - mrb_int i; - mrb_code co; - mrb_bool overflow; - - i = readint_mrb_int(s, p, base, FALSE, &overflow); - if (overflow) { - double f = readint_float(s, p, base); - int off = new_lit(s, mrb_float_value(s->mrb, f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - } - else { - if (i < MAXARG_sBx && i > -MAXARG_sBx) { - co = MKOP_AsBx(OP_LOADI, cursp(), i); - } - else { - int off = new_lit(s, mrb_fixnum_value(i)); - co = MKOP_ABx(OP_LOADL, cursp(), off); - } - genop(s, co); - } - push(); - } - break; - - case NODE_FLOAT: - if (val) { - char *p = (char*)tree; - mrb_float f = str_to_mrb_float(p); - int off = new_lit(s, mrb_float_value(s->mrb, f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - push(); - } - break; - - case NODE_NEGATE: - { - nt = (intptr_t)tree->car; - tree = tree->cdr; - switch (nt) { - case NODE_FLOAT: - { - char *p = (char*)tree; - mrb_float f = str_to_mrb_float(p); - int off = new_lit(s, mrb_float_value(s->mrb, -f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - push(); - } - break; - - case NODE_INT: - { - char *p = (char*)tree->car; - int base = (intptr_t)tree->cdr->car; - mrb_int i; - mrb_code co; - mrb_bool overflow; - - i = readint_mrb_int(s, p, base, TRUE, &overflow); - if (overflow) { - double f = readint_float(s, p, base); - int off = new_lit(s, mrb_float_value(s->mrb, -f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - } - else { - if (i < MAXARG_sBx && i > -MAXARG_sBx) { - co = MKOP_AsBx(OP_LOADI, cursp(), i); - } - else { - int off = new_lit(s, mrb_fixnum_value(i)); - co = MKOP_ABx(OP_LOADL, cursp(), off); - } - genop(s, co); - } - push(); - } - break; - - default: - { - int sym = new_msym(s, mrb_intern_lit(s->mrb, "-")); - - genop(s, MKOP_ABx(OP_LOADI, cursp(), 0)); - push(); - codegen(s, tree, VAL); - pop(); pop(); - genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2)); - } - break; - } - } - break; - - case NODE_STR: - if (val) { - char *p = (char*)tree->car; - size_t len = (intptr_t)tree->cdr; - int ai = mrb_gc_arena_save(s->mrb); - int off = new_lit(s, mrb_str_new(s->mrb, p, len)); - - mrb_gc_arena_restore(s->mrb, ai); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - push(); - } - break; - - case NODE_HEREDOC: - tree = ((struct mrb_parser_heredoc_info *)tree)->doc; - /* fall through */ - case NODE_DSTR: - if (val) { - node *n = tree; - - codegen(s, n->car, VAL); - n = n->cdr; - while (n) { - codegen(s, n->car, VAL); - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - n = n->cdr; - } - } - else { - node *n = tree; - - while (n) { - if ((intptr_t)n->car->car != NODE_STR) { - codegen(s, n->car, NOVAL); - } - n = n->cdr; - } - } - break; - - case NODE_WORDS: - gen_literal_array(s, tree, FALSE, val); - break; - - case NODE_SYMBOLS: - gen_literal_array(s, tree, TRUE, val); - break; - - case NODE_DXSTR: - { - node *n; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); - - if (val == NOVAL) { push(); } - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - codegen(s, tree->car, VAL); - n = tree->cdr; - while (n) { - if ((intptr_t)n->car->car == NODE_XSTR) { - n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR; - mrb_assert(!n->cdr); /* must be the end */ - } - codegen(s, n->car, VAL); - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - n = n->cdr; - } - pop(); - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); - if (val == NOVAL) { pop(); } - else { push(); } - mrb_gc_arena_restore(s->mrb, ai); - } - break; - - case NODE_XSTR: - { - char *p = (char*)tree->car; - size_t len = (intptr_t)tree->cdr; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); - int off = new_lit(s, mrb_str_new(s->mrb, p, len)); - - if (val == NOVAL) { push(); } - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); - if (val == NOVAL) { pop(); } - else { push(); } - mrb_gc_arena_restore(s->mrb, ai); - } - break; - - case NODE_REGX: - if (val) { - char *p1 = (char*)tree->car; - char *p2 = (char*)tree->cdr; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); - int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1)); - int argc = 1; - - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - if (p2) { - push(); - off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - argc++; - pop(); - } - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc)); - mrb_gc_arena_restore(s->mrb, ai); - push(); - } - break; - - case NODE_DREGX: - if (val) { - node *n = tree->car; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); - int argc = 1; - int off; - char *p; - - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - codegen(s, n->car, VAL); - n = n->cdr; - while (n) { - codegen(s, n->car, VAL); - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - n = n->cdr; - } - n = tree->cdr->cdr; - if (n->car) { - p = (char*)n->car; - off = new_lit(s, mrb_str_new_cstr(s->mrb, p)); - codegen(s, tree->car, VAL); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - } - if (n->cdr) { - char *p2 = (char*)n->cdr; - - push(); - off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - argc++; - pop(); - } - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc)); - mrb_gc_arena_restore(s->mrb, ai); - push(); - } - else { - node *n = tree->car; - - while (n) { - if ((intptr_t)n->car->car != NODE_STR) { - codegen(s, n->car, NOVAL); - } - n = n->cdr; - } - } - break; - - case NODE_SYM: - if (val) { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); - push(); - } - break; - - case NODE_DSYM: - codegen(s, tree, val); - if (val) { - gen_send_intern(s); - } - break; - - case NODE_SELF: - if (val) { - genop(s, MKOP_A(OP_LOADSELF, cursp())); - push(); - } - break; - - case NODE_NIL: - if (val) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - break; - - case NODE_TRUE: - if (val) { - genop(s, MKOP_A(OP_LOADT, cursp())); - push(); - } - break; - - case NODE_FALSE: - if (val) { - genop(s, MKOP_A(OP_LOADF, cursp())); - push(); - } - break; - - case NODE_ALIAS: - { - int a = new_msym(s, sym(tree->car)); - int b = new_msym(s, sym(tree->cdr)); - int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method")); - - genop(s, MKOP_A(OP_TCLASS, cursp())); - push(); - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a)); - push(); - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b)); - push(); - genop(s, MKOP_A(OP_LOADNIL, cursp())); - pop_n(3); - genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2)); - if (val) { - push(); - } - } - break; - - case NODE_UNDEF: - { - int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method")); - int num = 0; - node *t = tree; - - genop(s, MKOP_A(OP_TCLASS, cursp())); - push(); - while (t) { - int symbol = new_msym(s, sym(t->car)); - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol)); - push(); - t = t->cdr; - num++; - } - pop_n(num + 1); - genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num)); - if (val) { - push(); - } - } - break; - - case NODE_CLASS: - { - int idx; - - if (tree->car->car == (node*)0) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - else if (tree->car->car == (node*)1) { - genop(s, MKOP_A(OP_OCLASS, cursp())); - push(); - } - else { - codegen(s, tree->car->car, VAL); - } - if (tree->cdr->car) { - codegen(s, tree->cdr->car, VAL); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - pop(); pop(); - idx = new_msym(s, sym(tree->car->cdr)); - genop(s, MKOP_AB(OP_CLASS, cursp(), idx)); - idx = scope_body(s, tree->cdr->cdr->car, val); - genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); - if (val) { - push(); - } - } - break; - - case NODE_MODULE: - { - int idx; - - if (tree->car->car == (node*)0) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - else if (tree->car->car == (node*)1) { - genop(s, MKOP_A(OP_OCLASS, cursp())); - push(); - } - else { - codegen(s, tree->car->car, VAL); - } - pop(); - idx = new_msym(s, sym(tree->car->cdr)); - genop(s, MKOP_AB(OP_MODULE, cursp(), idx)); - idx = scope_body(s, tree->cdr->car, val); - genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); - if (val) { - push(); - } - } - break; - - case NODE_SCLASS: - { - int idx; - - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp())); - idx = scope_body(s, tree->cdr->car, val); - genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); - if (val) { - push(); - } - } - break; - - case NODE_DEF: - { - int sym = new_msym(s, sym(tree->car)); - int idx = lambda_body(s, tree->cdr, 0); - - genop(s, MKOP_A(OP_TCLASS, cursp())); - push(); - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD)); - push(); pop(); - pop(); - genop(s, MKOP_AB(OP_METHOD, cursp(), sym)); - if (val) { - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); - push(); - } - } - break; - - case NODE_SDEF: - { - node *recv = tree->car; - int sym = new_msym(s, sym(tree->cdr->car)); - int idx = lambda_body(s, tree->cdr->cdr, 0); - - codegen(s, recv, VAL); - pop(); - genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp())); - push(); - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD)); - pop(); - genop(s, MKOP_AB(OP_METHOD, cursp(), sym)); - if (val) { - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); - push(); - } - } - break; - - case NODE_POSTEXE: - codegen(s, tree, NOVAL); - break; - - default: - break; - } -} - -static void -scope_add_irep(codegen_scope *s, mrb_irep *irep) -{ - if (s->irep == NULL) { - s->irep = irep; - return; - } - if (s->irep->rlen == s->rcapa) { - s->rcapa *= 2; - s->irep->reps = (mrb_irep**)codegen_realloc(s, s->irep->reps, sizeof(mrb_irep*)*s->rcapa); - } - s->irep->reps[s->irep->rlen] = irep; - s->irep->rlen++; -} - -static codegen_scope* -scope_new(mrb_state *mrb, codegen_scope *prev, node *lv) -{ - static const codegen_scope codegen_scope_zero = { 0 }; - mrb_pool *pool = mrb_pool_open(mrb); - codegen_scope *p = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope)); - - if (!p) return NULL; - *p = codegen_scope_zero; - p->mrb = mrb; - p->mpool = pool; - if (!prev) return p; - p->prev = prev; - p->ainfo = -1; - p->mscope = 0; - - p->irep = mrb_add_irep(mrb); - scope_add_irep(prev, p->irep); - - p->rcapa = 8; - p->irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*p->rcapa); - - p->icapa = 1024; - p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa); - p->irep->iseq = p->iseq; - - p->pcapa = 32; - p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa); - p->irep->plen = 0; - - p->scapa = 256; - p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa); - p->irep->slen = 0; - - p->lv = lv; - p->sp += node_len(lv)+1; /* add self */ - p->nlocals = p->sp; - if (lv) { - node *n = lv; - size_t i = 0; - - p->irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (p->nlocals - 1)); - for (i=0, n=lv; n; i++,n=n->cdr) { - p->irep->lv[i].name = lv_name(n); - if (lv_name(n)) { - p->irep->lv[i].r = lv_idx(p, lv_name(n)); - } - else { - p->irep->lv[i].r = 0; - } - } - mrb_assert(i + 1 == p->nlocals); - } - p->ai = mrb_gc_arena_save(mrb); - - p->filename = prev->filename; - if (p->filename) { - p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa); - } - p->lineno = prev->lineno; - - /* debug setting */ - p->debug_start_pos = 0; - if (p->filename) { - mrb_debug_info_alloc(mrb, p->irep); - p->irep->filename = p->filename; - p->irep->lines = p->lines; - } - else { - p->irep->debug_info = NULL; - } - p->parser = prev->parser; - p->filename_index = prev->filename_index; - - return p; -} - -static void -scope_finish(codegen_scope *s) -{ - mrb_state *mrb = s->mrb; - mrb_irep *irep = s->irep; - size_t fname_len; - char *fname; - - irep->flags = 0; - if (s->iseq) { - irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc); - irep->ilen = s->pc; - if (s->lines) { - irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc); - } - else { - irep->lines = 0; - } - } - irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen); - irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen); - irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen); - if (s->filename) { - s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); - mrb_debug_info_append_file(mrb, s->irep, s->debug_start_pos, s->pc); - - fname_len = strlen(s->filename); - fname = (char*)codegen_malloc(s, fname_len + 1); - memcpy(fname, s->filename, fname_len); - fname[fname_len] = '\0'; - irep->filename = fname; - } - - irep->nlocals = s->nlocals; - irep->nregs = s->nregs; - - mrb_gc_arena_restore(mrb, s->ai); - mrb_pool_close(s->mpool); -} - -static struct loopinfo* -loop_push(codegen_scope *s, enum looptype t) -{ - struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo)); - - p->type = t; - p->pc1 = p->pc2 = p->pc3 = 0; - p->prev = s->loop; - p->ensure_level = s->ensure_level; - p->acc = cursp(); - s->loop = p; - - return p; -} - -static void -loop_break(codegen_scope *s, node *tree) -{ - if (!s->loop) { - codegen(s, tree, NOVAL); - raise_error(s, "unexpected break"); - } - else { - struct loopinfo *loop; - - if (tree) { - codegen(s, tree, VAL); - pop(); - } - - loop = s->loop; - while (loop->type == LOOP_BEGIN) { - genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); - loop = loop->prev; - } - while (loop->type == LOOP_RESCUE) { - loop = loop->prev; - } - if (loop->type == LOOP_NORMAL) { - int tmp; - - if (s->ensure_level > s->loop->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); - } - if (tree) { - genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL); - } - tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3)); - loop->pc3 = tmp; - } - else { - genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK)); - } - } -} - -static void -loop_pop(codegen_scope *s, int val) -{ - if (val) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - } - dispatch_linked(s, s->loop->pc3); - s->loop = s->loop->prev; - if (val) push(); -} - -#ifdef ENABLE_STDIO -static int -print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre) -{ - size_t i; - - if (n == 0) return 0; - - for (i=0; i+1<irep->nlocals; i++) { - if (irep->lv[i].r == n) { - mrb_sym sym = irep->lv[i].name; - if (pre) printf(" "); - printf("R%d:%s", (int)n, mrb_sym2name(mrb, sym)); - return 1; - } - } - return 0; -} - -#define RA 1 -#define RB 2 -#define RAB 3 - -static void -print_lv(mrb_state *mrb, mrb_irep *irep, mrb_code c, int r) -{ - int pre = 0; - - if (!irep->lv - || ((!(r & RA) || GETARG_A(c) >= irep->nlocals) - && (!(r & RB) || GETARG_B(c) >= irep->nlocals))) { - printf("\n"); - return; - } - printf("\t; "); - if (r & RA) { - pre = print_r(mrb, irep, GETARG_A(c), 0); - } - if (r & RB) { - print_r(mrb, irep, GETARG_B(c), pre); - } - printf("\n"); -} -#endif - -static void -codedump(mrb_state *mrb, mrb_irep *irep) -{ -#ifdef ENABLE_STDIO - int i; - int ai; - mrb_code c; - const char *file = NULL, *next_file; - int32_t line; - - 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); - - for (i = 0; i < (int)irep->ilen; i++) { - ai = mrb_gc_arena_save(mrb); - - next_file = mrb_debug_get_filename(irep, i); - if (next_file && file != next_file) { - printf("file: %s\n", next_file); - file = next_file; - } - line = mrb_debug_get_line(irep, i); - if (line < 0) { - printf(" "); - } - else { - printf("%5d ", line); - } - - printf("%03d ", i); - c = irep->iseq[i]; - switch (GET_OPCODE(c)) { - case OP_NOP: - printf("OP_NOP\n"); - break; - case OP_MOVE: - printf("OP_MOVE\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_LOADL: - { - mrb_value v = irep->pool[GETARG_Bx(c)]; - mrb_value s = mrb_inspect(mrb, v); - printf("OP_LOADL\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s)); - } - print_lv(mrb, irep, c, RA); - break; - case OP_LOADI: - printf("OP_LOADI\tR%d\t%d\t", GETARG_A(c), GETARG_sBx(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_LOADSYM: - printf("OP_LOADSYM\tR%d\t:%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); - print_lv(mrb, irep, c, RA); - break; - case OP_LOADNIL: - printf("OP_LOADNIL\tR%d\t\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_LOADSELF: - printf("OP_LOADSELF\tR%d\t\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_LOADT: - printf("OP_LOADT\tR%d\t\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_LOADF: - printf("OP_LOADF\tR%d\t\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_GETGLOBAL: - printf("OP_GETGLOBAL\tR%d\t:%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); - print_lv(mrb, irep, c, RA); - break; - case OP_SETGLOBAL: - printf("OP_SETGLOBAL\t:%s\tR%d\t", - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), - GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_GETCONST: - printf("OP_GETCONST\tR%d\t:%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); - print_lv(mrb, irep, c, RA); - break; - case OP_SETCONST: - printf("OP_SETCONST\t:%s\tR%d\t", - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), - GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_GETMCNST: - printf("OP_GETMCNST\tR%d\tR%d::%s", GETARG_A(c), GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); - print_lv(mrb, irep, c, RAB); - break; - case OP_SETMCNST: - printf("OP_SETMCNST\tR%d::%s\tR%d", GETARG_A(c)+1, - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), - GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_GETIV: - printf("OP_GETIV\tR%d\t%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); - print_lv(mrb, irep, c, RA); - break; - case OP_SETIV: - printf("OP_SETIV\t%s\tR%d", - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), - GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_GETUPVAR: - printf("OP_GETUPVAR\tR%d\t%d\t%d", - GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_SETUPVAR: - printf("OP_SETUPVAR\tR%d\t%d\t%d", - GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_GETCV: - printf("OP_GETCV\tR%d\t%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); - print_lv(mrb, irep, c, RA); - break; - case OP_SETCV: - printf("OP_SETCV\t%s\tR%d", - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), - GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_JMP: - printf("OP_JMP\t\t%03d\n", i+GETARG_sBx(c)); - break; - case OP_JMPIF: - printf("OP_JMPIF\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c)); - break; - case OP_JMPNOT: - printf("OP_JMPNOT\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c)); - break; - case OP_SEND: - printf("OP_SEND\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_SENDB: - printf("OP_SENDB\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_TAILCALL: - printf("OP_TAILCALL\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_SUPER: - printf("OP_SUPER\tR%d\t%d\n", GETARG_A(c), - GETARG_C(c)); - break; - case OP_ARGARY: - printf("OP_ARGARY\tR%d\t%d:%d:%d:%d", GETARG_A(c), - (GETARG_Bx(c)>>10)&0x3f, - (GETARG_Bx(c)>>9)&0x1, - (GETARG_Bx(c)>>4)&0x1f, - (GETARG_Bx(c)>>0)&0xf); - print_lv(mrb, irep, c, RA); - break; - - case OP_ENTER: - printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n", - (GETARG_Ax(c)>>18)&0x1f, - (GETARG_Ax(c)>>13)&0x1f, - (GETARG_Ax(c)>>12)&0x1, - (GETARG_Ax(c)>>7)&0x1f, - (GETARG_Ax(c)>>2)&0x1f, - (GETARG_Ax(c)>>1)&0x1, - GETARG_Ax(c) & 0x1); - break; - case OP_RETURN: - printf("OP_RETURN\tR%d", GETARG_A(c)); - switch (GETARG_B(c)) { - case OP_R_NORMAL: - case OP_R_RETURN: - printf("\treturn"); break; - case OP_R_BREAK: - printf("\tbreak"); break; - default: - printf("\tbroken"); break; - break; - } - print_lv(mrb, irep, c, RA); - break; - case OP_BLKPUSH: - printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d", GETARG_A(c), - (GETARG_Bx(c)>>10)&0x3f, - (GETARG_Bx(c)>>9)&0x1, - (GETARG_Bx(c)>>4)&0x1f, - (GETARG_Bx(c)>>0)&0xf); - print_lv(mrb, irep, c, RA); - break; - - case OP_LAMBDA: - printf("OP_LAMBDA\tR%d\tI(%+d)\t%d", GETARG_A(c), GETARG_b(c)+1, GETARG_c(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_RANGE: - printf("OP_RANGE\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_METHOD: - printf("OP_METHOD\tR%d\t:%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); - print_lv(mrb, irep, c, RA); - break; - - case OP_ADD: - printf("OP_ADD\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_ADDI: - printf("OP_ADDI\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_SUB: - printf("OP_SUB\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_SUBI: - printf("OP_SUBI\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_MUL: - printf("OP_MUL\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_DIV: - printf("OP_DIV\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_LT: - printf("OP_LT\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_LE: - printf("OP_LE\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_GT: - printf("OP_GT\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_GE: - printf("OP_GE\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_EQ: - printf("OP_EQ\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - - case OP_STOP: - printf("OP_STOP\n"); - break; - - case OP_ARRAY: - printf("OP_ARRAY\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_ARYCAT: - printf("OP_ARYCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_ARYPUSH: - printf("OP_ARYPUSH\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_AREF: - printf("OP_AREF\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_APOST: - printf("OP_APOST\tR%d\t%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_STRING: - { - mrb_value v = irep->pool[GETARG_Bx(c)]; - 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", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s)); - } - print_lv(mrb, irep, c, RA); - break; - case OP_STRCAT: - printf("OP_STRCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_HASH: - printf("OP_HASH\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RAB); - break; - - case OP_OCLASS: - printf("OP_OCLASS\tR%d\t\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_CLASS: - printf("OP_CLASS\tR%d\t:%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); - print_lv(mrb, irep, c, RA); - break; - case OP_MODULE: - printf("OP_MODULE\tR%d\t:%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); - print_lv(mrb, irep, c, RA); - break; - case OP_EXEC: - printf("OP_EXEC\tR%d\tI(%+d)", GETARG_A(c), GETARG_Bx(c)+1); - print_lv(mrb, irep, c, RA); - break; - case OP_SCLASS: - printf("OP_SCLASS\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_TCLASS: - printf("OP_TCLASS\tR%d\t\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_ERR: - { - mrb_value v = irep->pool[GETARG_Bx(c)]; - 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)); - } - break; - case OP_EPUSH: - printf("OP_EPUSH\t:I(%+d)\n", GETARG_Bx(c)+1); - break; - case OP_ONERR: - printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c)); - break; - case OP_RESCUE: - printf("OP_RESCUE\tR%d\t\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_RAISE: - printf("OP_RAISE\tR%d\t\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_POPERR: - printf("OP_POPERR\t%d\t\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_EPOP: - printf("OP_EPOP\t%d\n", GETARG_A(c)); - break; - - default: - printf("OP_unknown %d\t%d\t%d\t%d\n", GET_OPCODE(c), - GETARG_A(c), GETARG_B(c), GETARG_C(c)); - break; - } - mrb_gc_arena_restore(mrb, ai); - } - printf("\n"); -#endif -} - -static void -codedump_recur(mrb_state *mrb, mrb_irep *irep) -{ - size_t i; - - codedump(mrb, irep); - for (i=0; i<irep->rlen; i++) { - codedump_recur(mrb, irep->reps[i]); - } -} - -void -mrb_codedump_all(mrb_state *mrb, struct RProc *proc) -{ - codedump_recur(mrb, proc->body.irep); -} - -MRB_API struct RProc* -mrb_generate_code(mrb_state *mrb, parser_state *p) -{ - codegen_scope *scope = scope_new(mrb, 0, 0); - struct RProc *proc; - - if (!scope) { - return NULL; - } - scope->mrb = mrb; - scope->parser = p; - scope->filename = p->filename; - scope->filename_index = p->current_filename_index; - - MRB_TRY(&scope->jmp) { - /* prepare irep */ - codegen(scope, p->tree, NOVAL); - proc = mrb_proc_new(mrb, scope->irep); - mrb_irep_decref(mrb, scope->irep); - mrb_pool_close(scope->mpool); - return proc; - } - MRB_CATCH(&scope->jmp) { - if (scope->filename == scope->irep->filename) { - scope->irep->filename = NULL; - } - mrb_irep_decref(mrb, scope->irep); - mrb_pool_close(scope->mpool); - return NULL; - } - MRB_END_EXC(&scope->jmp); -} diff --git a/src/compar.c b/src/compar.c index 0186b942f..0032fc859 100644 --- a/src/compar.c +++ b/src/compar.c @@ -4,7 +4,7 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" +#include <mruby.h> void mrb_init_comparable(mrb_state *mrb) 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 4ac692086..c03c91cf5 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,9 +1,9 @@ #include <string.h> -#include "mruby.h" -#include "mruby/irep.h" -#include "mruby/debug.h" +#include <mruby.h> +#include <mruby/irep.h> +#include <mruby/debug.h> -static mrb_irep_debug_info_file * +static mrb_irep_debug_info_file* get_file(mrb_irep_debug_info *info, uint32_t pc) { mrb_irep_debug_info_file **ret; @@ -19,7 +19,8 @@ get_file(mrb_irep_debug_info *info, uint32_t pc) if (!(pc < (*it)->start_pos)) { ret = it + 1; count -= step + 1; - } else { count = step; } + } + else { count = step; } } --ret; @@ -50,25 +51,25 @@ select_line_type(const uint16_t *lines, size_t lines_len) } MRB_API char const* -mrb_debug_get_filename(mrb_irep *irep, uint32_t pc) +mrb_debug_get_filename(mrb_state *mrb, const mrb_irep *irep, uint32_t pc) { if (irep && pc < irep->ilen) { mrb_irep_debug_info_file* f = NULL; - if (!irep->debug_info) { return irep->filename; } + if (!irep->debug_info) return NULL; else if ((f = get_file(irep->debug_info, pc))) { - return f->filename; + return mrb_sym_name_len(mrb, f->filename_sym, NULL); } } return NULL; } MRB_API int32_t -mrb_debug_get_line(mrb_irep *irep, uint32_t pc) +mrb_debug_get_line(mrb_state *mrb, const mrb_irep *irep, uint32_t pc) { 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, pc))) { switch (f->line_type) { @@ -86,7 +87,8 @@ mrb_debug_get_line(mrb_irep *irep, uint32_t pc) if (!(pc < it->start_pos)) { ret = it + 1; count -= step + 1; - } else { count = step; } + } + else { count = step; } } --ret; @@ -106,7 +108,7 @@ mrb_debug_get_line(mrb_irep *irep, uint32_t pc) return -1; } -MRB_API mrb_irep_debug_info * +MRB_API mrb_irep_debug_info* mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep) { static const mrb_irep_debug_info initial = { 0, 0, NULL }; @@ -119,83 +121,80 @@ mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep) return ret; } -MRB_API mrb_irep_debug_info_file * -mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep, +MRB_API mrb_irep_debug_info_file* +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->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1); + 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; } - 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)); + f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc( + mrb, f->lines.flat_map, + sizeof(mrb_irep_debug_info_line) * (f->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]; + ++f->line_entry_count; + prev_line = lines[start_pos + i]; } } break; default: mrb_assert(0); break; } - return ret; + return f; } MRB_API void @@ -205,11 +204,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 af12ed605..0b4200795 100644 --- a/src/dump.c +++ b/src/dump.c @@ -1,41 +1,28 @@ /* -** dump.c - mruby binary dumper (mrbc binary format) +** cdump.c - mruby binary dumper (in C) ** ** See Copyright Notice in mruby.h */ -#include <ctype.h> #include <string.h> #include <limits.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 - -#ifdef ENABLE_STDIO +#include <math.h> +#include <mruby/dump.h> +#include <mruby/string.h> +#include <mruby/irep.h> +#include <mruby/debug.h> + +#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; @@ -47,11 +34,11 @@ 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; - cur += uint32_to_bin(get_irep_record_size_1(mrb, irep), cur); /* record size */ + cur += uint32_to_bin((uint32_t)get_irep_record_size_1(mrb, irep), cur); /* record size */ cur += uint16_to_bin((uint16_t)irep->nlocals, cur); /* number of local variable */ cur += uint16_to_bin((uint16_t)irep->nregs, cur); /* number of register variable */ cur += uint16_to_bin((uint16_t)irep->rlen, cur); /* number of child irep */ @@ -59,83 +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; - uint32_t iseq_no; + size_t seqlen = irep->ilen * sizeof(mrb_code) + + irep->clen * sizeof(struct mrb_irep_catch_handler); + + 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; - cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */ - cur += write_padding(cur); - if (flags & FLAG_BYTEORDER_NATIVE) { - memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code)); - cur += irep->ilen * sizeof(mrb_code); + 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 { - for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { - cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* opcode */ + size_t i; + + for (i=0; i<sizeof(double); i++) { + buf[i] = u.s[sizeof(double)-i-1]; } } - - return cur - buf; } - +#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; - size_t pool_no; - mrb_value str; - char buf[32]; - 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; - case MRB_TT_FLOAT: + case IREP_TT_BIGINT: { - int len; - len = mrb_float_to_str(buf, mrb_float(irep->pool[pool_no])); + 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; - 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); @@ -145,84 +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) { - size_t pool_no; + int pool_no; uint8_t *cur = buf; - uint16_t len; - mrb_value str; - const char *char_ptr; - char char_buf[30]; + 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); - char_ptr = RSTRING_PTR(str); + switch (irep->pool[pool_no].tt) { +#ifdef MRB_64BIT + case IREP_TT_INT64: { - mrb_int tlen; - - tlen = RSTRING_LEN(str); - mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX); - len = (uint16_t)tlen; + 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_FLOAT: - cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ - { - int tlen; - tlen = mrb_float_to_str(char_buf, mrb_float(irep->pool[pool_no])); - mrb_assert_int_fit(int, tlen, uint16_t, UINT16_MAX); - len = (uint16_t)tlen; - } - char_ptr = &char_buf[0]; + 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; - case MRB_TT_STRING: - cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */ - char_ptr = RSTRING_PTR(irep->pool[pool_no]); + case IREP_TT_FLOAT: + cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ +#ifndef MRB_NO_FLOAT { - mrb_int tlen; - - tlen = RSTRING_LEN(irep->pool[pool_no]); - mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX); - len = (uint16_t)tlen; + dump_float(mrb, cur,irep->pool[pool_no].u.f); + cur += sizeof(double); } +#else + cur += uint16_to_bin(0, cur); /* zero length */ +#endif break; - default: - continue; + 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; - uint32_t sym_no; + 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 */ } } @@ -231,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) { - uint32_t sym_no; + 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 */ @@ -260,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; @@ -272,10 +293,10 @@ 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; - size_t irep_no; + int irep_no; size = get_irep_record_size_1(mrb, irep); for (irep_no = 0; irep_no < irep->rlen; irep_no++) { @@ -285,20 +306,15 @@ 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) { - uint32_t i; + int i; uint8_t *src = bin; if (irep == NULL) { 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); @@ -323,7 +339,7 @@ write_footer(mrb_state *mrb, uint8_t *bin) { struct rite_binary_footer footer; - memcpy(footer.section_identify, RITE_BINARY_EOF, sizeof(footer.section_identify)); + memcpy(footer.section_ident, RITE_BINARY_EOF, sizeof(footer.section_ident)); uint32_to_bin(sizeof(struct rite_binary_footer), footer.section_size); memcpy(bin, &footer, sizeof(struct rite_binary_footer)); @@ -336,7 +352,7 @@ write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin) { struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin; - memcpy(header->section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(header->section_identify)); + memcpy(header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(header->section_ident)); mrb_assert_int_fit(size_t, section_size, uint32_t, UINT32_MAX); uint32_to_bin((uint32_t)section_size, header->section_size); @@ -346,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; @@ -362,129 +378,19 @@ 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_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(header->section_identify)); - 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; - size_t 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 i; - size_t rlen, size = 0; - - 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; - size_t i; + int i; ret += sizeof(uint32_t); /* record size */ ret += sizeof(uint16_t); /* file count */ @@ -529,11 +435,12 @@ 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 i, size = 0; - mrb_irep_debug_info *di = irep->debug_info; + size_t size = 0; + const mrb_irep_debug_info *di = irep->debug_info; + int i; mrb_assert(lp); for (i = 0; i < di->flen; ++i) { @@ -548,7 +455,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; } } @@ -559,7 +466,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,17 +513,17 @@ write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const ret = cur - bin; mrb_assert_int_fit(ptrdiff_t, ret, uint32_t, UINT32_MAX); - uint32_to_bin(ret, bin); + uint32_to_bin((uint32_t)ret, bin); mrb_assert_int_fit(ptrdiff_t, ret, size_t, SIZE_MAX); return (size_t)ret; } 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; - size_t irep_no; + int irep_no; size = len = write_debug_record_1(mrb, irep, bin, filenames, filenames_len); bin += len; @@ -631,7 +538,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; @@ -652,9 +559,9 @@ 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(sym_len, cur); + cur += uint16_to_bin((uint16_t)sym_len, cur); memcpy(cur, sym, sym_len); cur += sym_len; section_size += sizeof(uint16_t) + sym_len; @@ -664,9 +571,9 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len); section_size += dlen; - memcpy(header->section_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(header->section_identify)); + memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident)); mrb_assert(section_size <= INT32_MAX); - uint32_to_bin(section_size, header->section_size); + uint32_to_bin((uint32_t)section_size, header->section_size); return MRB_DUMP_OK; } @@ -674,14 +581,14 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const static void create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32_t *syms_len) { - size_t i; + int i; if (*syms == NULL) { *syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * 1); } 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; @@ -706,8 +613,8 @@ 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); - cur += uint16_to_bin(str_len, cur); + 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; } @@ -721,19 +628,17 @@ static int write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym const *syms, uint32_t syms_len) { uint8_t *cur = *start; - size_t i; + 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); } } @@ -747,11 +652,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, i; + 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]); @@ -761,7 +667,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; @@ -769,7 +675,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; } @@ -779,7 +685,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; @@ -803,11 +709,11 @@ write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const * goto lv_section_exit; } - memcpy(header->section_identify, RITE_SECTION_LV_IDENTIFIER, sizeof(header->section_identify)); + memcpy(header->section_ident, RITE_SECTION_LV_IDENT, sizeof(header->section_ident)); diff = cur - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); - uint32_to_bin(diff, header->section_size); + uint32_to_bin((uint32_t)diff, header->section_size); lv_section_exit: return result; @@ -817,70 +723,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; - if (flags & FLAG_BYTEORDER_NATIVE) { - uint32_t ident = 0; - size_t i; - - for(i=0; i<sizeof(ident); i++) { - ident<<=8; - ident|=RITE_BINARY_IDENTIFIER[i]; - } - memcpy(header->binary_identify, (char*)&ident, sizeof(uint32_t)); - } - else { - memcpy(header->binary_identify, RITE_BINARY_IDENTIFIER, sizeof(header->binary_identify)); - } - 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 = (&(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) { - size_t i; + 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) { - size_t i; + 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 int -dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t *bin_size, uint8_t flags) +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; @@ -893,7 +782,7 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t section_irep_size += get_irep_record_size(mrb, irep); /* DEBUG section size */ - if (debug_info) { + if (flags & MRB_DUMP_DEBUG_INFO) { if (debug_info_defined) { section_lineno_size += sizeof(struct rite_section_debug_header); /* filename table */ @@ -905,10 +794,6 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, 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) { @@ -933,15 +818,12 @@ dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t sizeof(struct rite_binary_footer); /* write DEBUG section */ - if (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; } @@ -962,23 +844,21 @@ error_exit: mrb_free(mrb, *bin); *bin = NULL; } - if (lv_syms) { - mrb_free(mrb, lv_syms); - } - if (filenames) { - mrb_free(mrb, filenames); - } + mrb_free(mrb, lv_syms); + mrb_free(mrb, filenames); return result; } int -mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, 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, debug_info, bin, bin_size, FLAG_BYTEORDER_NONATIVE); + return dump_irep(mrb, irep, flags, bin, bin_size); } +#ifndef MRB_NO_STDIO + int -mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, 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; @@ -988,7 +868,7 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp) return MRB_DUMP_INVALID_ARGUMENT; } - result = dump_irep(mrb, irep, debug_info, &bin, &bin_size, FLAG_BYTEORDER_NONATIVE); + 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; @@ -999,48 +879,30 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp) return result; } -static mrb_bool -is_valid_c_symbol_name(const char *name) -{ - const char *c = NULL; - - if (name == NULL || name[0] == '\0') return FALSE; - if (!ISALPHA(name[0]) && name[0] != '_') return FALSE; - - c = &name[1]; - for (; *c != '\0'; ++c) { - if (!ISALNUM(*c) && *c != '_') return FALSE; - } - - return TRUE; -} - int -mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, int debug_info, 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; int result; - if (fp == NULL || initname == NULL || !is_valid_c_symbol_name(initname)) { + if (fp == NULL || initname == NULL || initname[0] == '\0') { return MRB_DUMP_INVALID_ARGUMENT; } - - result = dump_irep(mrb, irep, debug_info, &bin, &bin_size, FLAG_BYTEORDER_NATIVE); + result = dump_irep(mrb, irep, flags, &bin, &bin_size); if (result == MRB_DUMP_OK) { 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, - "const uint8_t\n" - "#if defined __GNUC__\n" - "__attribute__((aligned(%u)))\n" - "#elif defined _MSC_VER\n" - "__declspec(align(%u))\n" - "#endif\n" - "%s[] = {", - (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; } @@ -1066,4 +928,4 @@ mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE *fp, co return result; } -#endif /* ENABLE_STDIO */ +#endif /* MRB_NO_STDIO */ diff --git a/src/enum.c b/src/enum.c index 3def9e860..b95956715 100644 --- a/src/enum.c +++ b/src/enum.c @@ -4,11 +4,27 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" +#include <mruby.h> +#include <mruby/proc.h> + +/* internal method `__update_hash(oldhash, index, itemhash)` */ +static mrb_value +enum_update_hash(mrb_state *mrb, mrb_value self) +{ + mrb_int hash; + mrb_int index; + mrb_int hv; + + mrb_get_args(mrb, "iii", &hash, &index, &hv); + hash ^= ((uint32_t)hv << (index % 16)); + + return mrb_int_value(mrb, hash); +} void mrb_init_enumerable(mrb_state *mrb) { - mrb_define_module(mrb, "Enumerable"); /* 15.3.2 */ + 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(3)); } - diff --git a/src/error.c b/src/error.c index 0a1a97a0b..7953deea7 100644 --- a/src/error.c +++ b/src/error.c @@ -7,19 +7,19 @@ #include <errno.h> #include <stdarg.h> #include <stdlib.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/irep.h" -#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 "mrb_throw.h" +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/irep.h> +#include <mruby/proc.h> +#include <mruby/string.h> +#include <mruby/variable.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, long len) +mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len) { mrb_value arg = mrb_str_new(mrb, ptr, len); return mrb_obj_new(mrb, c, 1, &arg); @@ -28,7 +28,7 @@ mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, long 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); } @@ -46,7 +46,7 @@ exc_initialize(mrb_state *mrb, mrb_value exc) mrb_value mesg; if (mrb_get_args(mrb, "|o", &mesg) == 1) { - mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg); + mrb_iv_set(mrb, exc, MRB_SYM(mesg), mesg); } return exc; } @@ -60,7 +60,7 @@ exc_initialize(mrb_state *mrb, mrb_value exc) * With no argument, or if the argument is the same as the receiver, * return the receiver. Otherwise, create a new * exception object of the same class as the receiver, but with a - * message equal to <code>string.to_str</code>. + * message equal to <code>string</code>. * */ @@ -69,13 +69,13 @@ exc_exception(mrb_state *mrb, mrb_value self) { mrb_value exc; mrb_value a; - int argc; + mrb_int argc; argc = mrb_get_args(mrb, "|o", &a); 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; } @@ -91,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)) { @@ -109,15 +109,13 @@ exc_to_s(mrb_state *mrb, mrb_value exc) * exception.message -> string * * Returns the result of invoking <code>exception.to_s</code>. - * Normally this returns the exception's message or name. By - * supplying a to_str method, exceptions are agreeing to - * be used where Strings are expected. + * Normally this returns the exception's message or name. */ static mrb_value 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,139 +128,250 @@ 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; + 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); +} - 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")); +void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc); - append_mesg = !mrb_nil_p(mesg); - if (append_mesg) { - mesg = mrb_obj_as_string(mrb, mesg); - append_mesg = RSTRING_LEN(mesg) > 0; +static void +set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace) +{ + if (!mrb_array_p(backtrace)) { + type_err: + mrb_raise(mrb, E_TYPE_ERROR, "backtrace must be Array of String"); } + else { + const mrb_value *p = RARRAY_PTR(backtrace); + const mrb_value *pend = p + RARRAY_LEN(backtrace); - if (!mrb_nil_p(file) && !mrb_nil_p(line)) { - str = mrb_str_dup(mrb, file); - mrb_str_cat_lit(mrb, str, ":"); - mrb_str_append(mrb, str, line); - mrb_str_cat_lit(mrb, str, ": "); - if (append_mesg) { - mrb_str_append(mrb, str, mesg); - mrb_str_cat_lit(mrb, str, " ("); - } - mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc)); - if (append_mesg) { - mrb_str_cat_lit(mrb, str, ")"); + while (p < pend) { + if (!mrb_string_p(*p)) goto type_err; + p++; } } + 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_get_arg1(mrb); + + set_backtrace(mrb, exc, backtrace); + return backtrace; +} + +void +mrb_exc_set(mrb_state *mrb, mrb_value exc) +{ + if (mrb_nil_p(exc)) { + mrb->exc = 0; + } else { - const char *cname = mrb_obj_classname(mrb, exc); - str = mrb_str_new_cstr(mrb, cname); - mrb_str_cat_lit(mrb, str, ": "); - if (append_mesg) { - mrb_str_append(mrb, str, mesg); + mrb->exc = mrb_obj_ptr(exc); + if (mrb->gc.arena_idx > 0 && + (struct RBasic*)mrb->exc == mrb->gc.arena[mrb->gc.arena_idx-1]) { + mrb->gc.arena_idx--; } - else { - mrb_str_cat_cstr(mrb, str, cname); + if (!mrb->gc.out_of_memory && !mrb_frozen_p(mrb->exc)) { + mrb_keep_backtrace(mrb, exc); } } - return str; } - -static void -exc_debug_info(mrb_state *mrb, struct RObject *exc) +static mrb_noreturn void +exc_throw(mrb_state *mrb, mrb_value exc) { - mrb_callinfo *ci = mrb->c->ci; - mrb_code *pc = ci->pc; - - mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value((mrb_int)(ci - mrb->c->cibase))); - while (ci >= mrb->c->cibase) { - mrb_code *err = ci->err; - - 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, (uint32_t)(err - irep->iseq)); - char const* file = mrb_debug_get_filename(irep, (uint32_t)(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--; + if (!mrb->jmp) { + mrb_p(mrb, exc); + abort(); } + MRB_THROW(mrb->jmp); } MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc) { - mrb->exc = mrb_obj_ptr(exc); - if (!mrb->out_of_memory) { - exc_debug_info(mrb, mrb->exc); + if (mrb_break_p(exc)) { + mrb->exc = mrb_obj_ptr(exc); } - if (!mrb->jmp) { - mrb_p(mrb, exc); - abort(); + 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); } - MRB_THROW(mrb->jmp); + exc_throw(mrb, exc); } MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg) { - mrb_value mesg; - mesg = mrb_str_new_cstr(mrb, msg); - mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg)); + 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; - 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') { - size = p - b - 1; - mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); - mrb_ary_push(mrb, ary, va_arg(ap, mrb_value)); - b = p + 1; + if (*p == '!') { + inspect = TRUE; + ++p; + } + 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); } } 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; - } - else { - break; - } + if (!*p) break; + goto L_cat_current; + } } - if (b == format) { - return mrb_str_new_cstr(mrb, format); - } - else { - size = p - b; - mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); - return mrb_ary_join(mrb, ary, mrb_str_new(mrb, NULL, 0)); - } + + mrb_str_cat(mrb, result, b, p - b); + return result; } MRB_API mrb_value @@ -278,38 +387,47 @@ mrb_format(mrb_state *mrb, const char *format, ...) return str; } +static mrb_noreturn void +raise_va(mrb_state *mrb, struct RClass *c, const char *fmt, va_list ap, int argc, mrb_value *argv) +{ + mrb_value mesg; + + mesg = mrb_vformat(mrb, fmt, ap); + if (argv == NULL) { + argv = &mesg; + } + else { + argv[0] = mesg; + } + mrb_exc_raise(mrb, mrb_obj_new(mrb, c, argc+1, argv)); +} + MRB_API mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...) { va_list args; - mrb_value mesg; va_start(args, fmt); - mesg = mrb_vformat(mrb, fmt, args); + raise_va(mrb, c, fmt, args, 0, NULL); va_end(args); - mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg)); } MRB_API mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...) { - mrb_value exc; mrb_value argv[2]; va_list args; va_start(args, fmt); - argv[0] = mrb_vformat(mrb, fmt, args); - va_end(args); - argv[1] = mrb_symbol_value(id); - exc = mrb_obj_new(mrb, E_NAME_ERROR, 2, argv); - mrb_exc_raise(mrb, exc); + raise_va(mrb, E_NAME_ERROR, fmt, args, 1, argv); + va_end(args); } MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...) { -#ifdef ENABLE_STDIO +#ifndef MRB_NO_STDIO va_list ap; mrb_value str; @@ -317,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 } @@ -324,7 +443,7 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...) MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *fmt, ...) { -#ifdef ENABLE_STDIO +#ifndef MRB_NO_STDIO va_list ap; mrb_value str; @@ -337,14 +456,8 @@ mrb_bug(mrb_state *mrb, const char *fmt, ...) exit(EXIT_FAILURE); } -static void -set_backtrace(mrb_state *mrb, mrb_value info, mrb_value bt) -{ - mrb_funcall(mrb, info, "set_backtrace", 1, bt); -} - -static mrb_value -make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr) +MRB_API mrb_value +mrb_make_exception(mrb_state *mrb, mrb_int argc, const mrb_value *argv) { mrb_value mesg; int n; @@ -356,12 +469,9 @@ make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr) case 1: if (mrb_nil_p(argv[0])) break; - if (isstr) { - mesg = mrb_check_string_type(mrb, argv[0]); - if (!mrb_nil_p(mesg)) { - mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, mesg); - break; - } + if (mrb_string_p(argv[0])) { + mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, argv[0]); + break; } n = 0; goto exception_call; @@ -371,7 +481,7 @@ make_exception(mrb_state *mrb, int argc, const mrb_value *argv, mrb_bool isstr) 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); } @@ -383,25 +493,19 @@ 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) { if (!mrb_obj_is_kind_of(mrb, mesg, mrb->eException_class)) - mrb_raise(mrb, E_TYPE_ERROR, "exception object expected"); + mrb_raise(mrb, mrb->eException_class, "exception object expected"); if (argc > 2) - set_backtrace(mrb, mesg, argv[2]); + set_backtrace(mrb, mesg, argv[2]); } return mesg; } -MRB_API mrb_value -mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv) -{ - return make_exception(mrb, argc, argv, TRUE); -} - MRB_API void mrb_sys_fail(mrb_state *mrb, const char *mesg) { @@ -424,38 +528,134 @@ mrb_sys_fail(mrb_state *mrb, const char *mesg) } MRB_API mrb_noreturn void -mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, char const* fmt, ...) +mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...) { mrb_value exc; + mrb_value argv[3]; va_list ap; va_start(ap, fmt); - exc = mrb_funcall(mrb, mrb_obj_value(E_NOMETHOD_ERROR), "new", 3, - mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), - mrb_ary_new_from_values(mrb, argc, argv)); + argv[0] = mrb_vformat(mrb, fmt, ap); + argv[1] = mrb_symbol_value(id); + argv[2] = args; va_end(ap); + exc = mrb_obj_new(mrb, E_NOMETHOD_ERROR, 3, argv); 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) { - struct RClass *exception, *runtime_error, *script_error; + struct RClass *exception, *script_error, *stack_error, *nomem_error; mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION); - 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)); mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ - runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ - mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str(mrb, runtime_error, mrb_str_new_lit(mrb, "Out of memory"))); + mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ - mrb_define_class(mrb, "SystemStackError", exception); + stack_error = mrb_define_class(mrb, "SystemStackError", exception); + 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_lit(mrb, nomem_error, "Out of memory")); +#ifdef MRB_GC_FIXED_ARENA + mrb->arena_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, nomem_error, "arena overflow error")); +#endif } diff --git a/src/error.h b/src/error.h index 0e0dacf63..eb755ec7f 100644 --- a/src/error.h +++ b/src/error.h @@ -1,3 +1,3 @@ /* this header file is to be removed soon. added for compatibility purpose (1.0.0) */ -#include "mruby/error.h" +#include <mruby/error.h> @@ -1,22 +1,21 @@ /* -** etc.c - +** etc.c ** ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/string.h" -#include "mruby/data.h" -#include "mruby/class.h" -#include "mruby/re.h" -#include "mruby/irep.h" +#include <mruby.h> +#include <mruby/string.h> +#include <mruby/data.h> +#include <mruby/class.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_immediate_p(obj) || (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_immediate_p(obj) || (mrb_type(obj) != MRB_TT_DATA)) { + if (!mrb_data_p(obj)) { return NULL; } if (DATA_TYPE(obj) != type) { @@ -67,33 +64,16 @@ 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_value tmp; - mrb_sym id; - - switch (mrb_type(name)) { - default: - tmp = mrb_check_string_type(mrb, name); - if (mrb_nil_p(tmp)) { - tmp = mrb_inspect(mrb, name); - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp); - } - name = tmp; - /* 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 -mrb_float_id(mrb_float f) +static mrb_int +make_num_id(const char *p, size_t len) { - const char *p = (const char*)&f; - int len = sizeof(f); - mrb_int id = 0; + uint32_t id = 0; while (len--) { id = id*65599 + *p; @@ -101,8 +81,24 @@ mrb_float_id(mrb_float f) } id = id + (id>>5); - return id; + return (mrb_int)id; +} + +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) @@ -118,16 +114,19 @@ 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: - 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 case MRB_TT_STRING: case MRB_TT_OBJECT: case MRB_TT_CLASS: @@ -139,50 +138,60 @@ 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: return MakeID(mrb_ptr(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 +#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_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) -{ - return mrb_class_defined(mrb, REGEXP_CLASS) && mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS)); -} +#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 new file mode 100644 index 000000000..43daf2307 --- /dev/null +++ b/src/fmt_fp.c @@ -0,0 +1,364 @@ +#include <mruby.h> +#include <string.h> +#include <stdlib.h> + +#ifndef MRB_NO_FLOAT +/*********************************************************************** + + Routine for converting a single-precision + floating point number into a string. + + 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. + + Dave Hylands + + The original code can be found in https://github.com/dhylands/format-float +***********************************************************************/ + +/*********************************************************************** + + I modified the routine for mruby: + + * support `double` + * support `#` (alt_form) modifier + + My modifications in this file are also placed in the public domain. + + Matz (Yukihiro Matsumoto) + +***********************************************************************/ + +#include <math.h> + +#ifdef MRB_USE_FLOAT32 + +// 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 + +#define FLT_DECEXP 32 +#define FLT_ROUND_TO_ONE 0.9999995F +#define FLT_MIN_BUF_SIZE 6 // -9e+99 + +#else + +// 1 sign bit, 11 exponent bits, and 52 mantissa bits. + +#define FLT_DECEXP 256 +#define FLT_ROUND_TO_ONE 0.999999999995 +#define FLT_MIN_BUF_SIZE 7 // -9e+199 + +#endif /* MRB_USE_FLOAT32 */ + +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 +}; + +/* + * 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 (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; + } + } + + if (prec < 0) { + prec = 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'; + } + + // 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 the user specified 'g' format, and e is <= 4, then we'll switch + // to the fixed format ('f') + + if (fmt == 'f' || (fmt == 'g' && e <= 4)) { + fmt = 'f'; + dec = -1; + *s++ = first_dig; + + 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++; + } + } + } + } 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 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++; + } + } + } + if (fmt == 'e' && prec > (buf_remaining - 6)) { + prec = buf_remaining - 6; + } + // 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 (fmt == 'f') { + dec = e; + num_digits = prec + e + 1; + } else { + e_sign = '+'; + } + } + if (prec < 0) { + // This can happen when the prec is trimmed to prevent buffer overflow + prec = 0; + } + + // 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; + } + num_digits = prec; + } + + // 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; + } + + // 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--; + } + } + } + + 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--; + } + } + // 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); + } + *s = '\0'; + + return s - buf; +} +#endif @@ -6,17 +6,23 @@ #include <string.h> #include <stdlib.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/data.h" -#include "mruby/hash.h" -#include "mruby/proc.h" -#include "mruby/range.h" -#include "mruby/string.h" -#include "mruby/variable.h" -#include "mruby/gc.h" -#include "mruby/error.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> +#include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/gc.h> +#include <mruby/error.h> +#include <mruby/throw.h> +#include <mruby/presym.h> /* = Tri-color Incremental Garbage Collection @@ -33,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). @@ -65,7 +76,7 @@ mruby implementer and C extension library writer must insert a write barrier when updating a reference from a field of an object. - When updating a reference from a field of object A to object B, + When updating a reference from a field of object A to object B, two different types of write barrier are available: * mrb_field_write_barrier - target B object for a mark. @@ -97,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; @@ -108,10 +125,16 @@ 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_NO_FLOAT struct RFloat floatv; +#endif struct RCptr cptr; #endif } as; @@ -136,8 +159,8 @@ gettimeofday_time(void) #define GC_INVOKE_TIME_REPORT(with) do {\ fprintf(stderr, "%s\n", with);\ fprintf(stderr, "gc_invoke: %19.3f\n", gettimeofday_time() - program_invoke_time);\ - fprintf(stderr, "is_generational: %d\n", is_generational(mrb));\ - fprintf(stderr, "is_major_gc: %d\n", is_major_gc(mrb));\ + fprintf(stderr, "is_generational: %d\n", is_generational(gc));\ + fprintf(stderr, "is_major_gc: %d\n", is_major_gc(gc));\ } while(0) #define GC_TIME_START do {\ @@ -147,10 +170,10 @@ gettimeofday_time(void) #define GC_TIME_STOP_AND_REPORT do {\ gc_time = gettimeofday_time() - gc_time;\ gc_total_time += gc_time;\ - fprintf(stderr, "gc_state: %d\n", mrb->gc_state);\ - fprintf(stderr, "live: %zu\n", mrb->live);\ - fprintf(stderr, "majorgc_old_threshold: %zu\n", mrb->majorgc_old_threshold);\ - fprintf(stderr, "gc_threshold: %zu\n", mrb->gc_threshold);\ + fprintf(stderr, "gc_state: %d\n", gc->state);\ + fprintf(stderr, "live: %zu\n", gc->live);\ + fprintf(stderr, "majorgc_old_threshold: %zu\n", gc->majorgc_old_threshold);\ + fprintf(stderr, "gc_threshold: %zu\n", gc->threshold);\ fprintf(stderr, "gc_time: %30.20f\n", gc_time);\ fprintf(stderr, "gc_total_time: %30.20f\n\n", gc_total_time);\ } while(0) @@ -166,8 +189,37 @@ gettimeofday_time(void) #define DEBUG(x) #endif +#ifndef MRB_HEAP_PAGE_SIZE +#define MRB_HEAP_PAGE_SIZE 1024 +#endif + #define GC_STEP_SIZE 1024 +/* white: 001 or 010, black: 100, gray: 000 */ +#define GC_GRAY 0 +#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) +#define paint_white(o) ((o)->color = GC_WHITES) +#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_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) @@ -175,7 +227,7 @@ mrb_realloc_simple(mrb_state *mrb, void *p, size_t len) void *p2; p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud); - if (!p2 && len > 0 && mrb->heaps) { + if (!p2 && len > 0 && mrb->gc.heaps) { mrb_full_gc(mrb); p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud); } @@ -183,24 +235,19 @@ mrb_realloc_simple(mrb_state *mrb, void *p, size_t len) return p2; } - MRB_API void* mrb_realloc(mrb_state *mrb, void *p, size_t len) { void *p2; p2 = mrb_realloc_simple(mrb, p, len); - if (!p2 && len) { - if (mrb->out_of_memory) { - /* mrb_panic(mrb); */ - } - else { - mrb->out_of_memory = TRUE; - mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); - } + if (len == 0) return p2; + if (p2 == NULL) { + mrb->gc.out_of_memory = TRUE; + mrb_raise_nomemory(mrb); } else { - mrb->out_of_memory = FALSE; + mrb->gc.out_of_memory = FALSE; } return p2; @@ -244,101 +291,127 @@ mrb_free(mrb_state *mrb, void *p) (mrb->allocf)(mrb, p, 0, mrb->allocf_ud); } -#ifndef MRB_HEAP_PAGE_SIZE -#define MRB_HEAP_PAGE_SIZE 1024 -#endif +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); +} -struct heap_page { - struct RBasic *freelist; - struct heap_page *prev; - struct heap_page *next; - struct heap_page *free_next; - struct heap_page *free_prev; - mrb_bool old:1; - RVALUE objects[MRB_HEAP_PAGE_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) { + mrb_gc *gc = &mrb->gc; + if (!heap_p(gc, object)) return TRUE; + return is_dead(gc, object); +} static void -link_heap_page(mrb_state *mrb, struct heap_page *page) +link_heap_page(mrb_gc *gc, mrb_heap_page *page) { - page->next = mrb->heaps; - if (mrb->heaps) - mrb->heaps->prev = page; - mrb->heaps = page; + page->next = gc->heaps; + if (gc->heaps) + gc->heaps->prev = page; + gc->heaps = page; } static void -unlink_heap_page(mrb_state *mrb, struct heap_page *page) +unlink_heap_page(mrb_gc *gc, mrb_heap_page *page) { if (page->prev) page->prev->next = page->next; if (page->next) page->next->prev = page->prev; - if (mrb->heaps == page) - mrb->heaps = page->next; + if (gc->heaps == page) + gc->heaps = page->next; page->prev = NULL; page->next = NULL; } static void -link_free_heap_page(mrb_state *mrb, struct heap_page *page) +link_free_heap_page(mrb_gc *gc, mrb_heap_page *page) { - page->free_next = mrb->free_heaps; - if (mrb->free_heaps) { - mrb->free_heaps->free_prev = page; + page->free_next = gc->free_heaps; + if (gc->free_heaps) { + gc->free_heaps->free_prev = page; } - mrb->free_heaps = page; + gc->free_heaps = page; } static void -unlink_free_heap_page(mrb_state *mrb, struct heap_page *page) +unlink_free_heap_page(mrb_gc *gc, mrb_heap_page *page) { if (page->free_prev) page->free_prev->free_next = page->free_next; if (page->free_next) page->free_next->free_prev = page->free_prev; - if (mrb->free_heaps == page) - mrb->free_heaps = page->free_next; + if (gc->free_heaps == page) + gc->free_heaps = page->free_next; page->free_prev = NULL; page->free_next = NULL; } static void -add_heap(mrb_state *mrb) +add_heap(mrb_state *mrb, mrb_gc *gc) { - struct heap_page *page = (struct heap_page *)mrb_calloc(mrb, 1, sizeof(struct heap_page)); + mrb_heap_page *page = (mrb_heap_page *)mrb_calloc(mrb, 1, sizeof(mrb_heap_page) + MRB_HEAP_PAGE_SIZE * sizeof(RVALUE)); RVALUE *p, *e; struct RBasic *prev = NULL; - for (p = page->objects, e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) { + for (p = objects(page), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) { p->as.free.tt = MRB_TT_FREE; p->as.free.next = prev; prev = &p->as.basic; } page->freelist = prev; - link_heap_page(mrb, page); - link_free_heap_page(mrb, page); + link_heap_page(gc, page); + link_free_heap_page(gc, page); } #define DEFAULT_GC_INTERVAL_RATIO 200 #define DEFAULT_GC_STEP_RATIO 200 -#define DEFAULT_MAJOR_GC_INC_RATIO 200 -#define is_generational(mrb) ((mrb)->is_generational_gc_mode) -#define is_major_gc(mrb) (is_generational(mrb) && (mrb)->gc_full) -#define is_minor_gc(mrb) (is_generational(mrb) && !(mrb)->gc_full) +#define MAJOR_GC_INC_RATIO 120 +#define MAJOR_GC_TOOMANY 10000 +#define is_generational(gc) ((gc)->generational) +#define is_major_gc(gc) (is_generational(gc) && (gc)->full) +#define is_minor_gc(gc) (is_generational(gc) && !(gc)->full) void -mrb_init_heap(mrb_state *mrb) +mrb_gc_init(mrb_state *mrb, mrb_gc *gc) { - mrb->heaps = NULL; - mrb->free_heaps = NULL; - add_heap(mrb); - mrb->gc_interval_ratio = DEFAULT_GC_INTERVAL_RATIO; - mrb->gc_step_ratio = DEFAULT_GC_STEP_RATIO; +#ifndef MRB_GC_FIXED_ARENA + gc->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE); + gc->arena_capa = MRB_GC_ARENA_SIZE; +#endif + + gc->current_white_part = GC_WHITE_A; + gc->heaps = NULL; + gc->free_heaps = NULL; + add_heap(mrb, gc); + gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO; + gc->step_ratio = DEFAULT_GC_STEP_RATIO; #ifndef MRB_GC_TURN_OFF_GENERATIONAL - mrb->is_generational_gc_mode = TRUE; - mrb->gc_full = TRUE; + gc->generational = TRUE; + gc->full = TRUE; #endif #ifdef GC_PROFILE @@ -346,85 +419,178 @@ mrb_init_heap(mrb_state *mrb) #endif } -static void obj_free(mrb_state *mrb, struct RBasic *obj); +static void obj_free(mrb_state *mrb, struct RBasic *obj, int end); -void -mrb_free_heap(mrb_state *mrb) +static void +free_heap(mrb_state *mrb, mrb_gc *gc) { - struct heap_page *page = mrb->heaps; - struct heap_page *tmp; + mrb_heap_page *page = gc->heaps; + mrb_heap_page *tmp; RVALUE *p, *e; while (page) { tmp = page; page = page->next; - for (p = tmp->objects, e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) { + for (p = objects(tmp), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) { if (p->as.free.tt != MRB_TT_FREE) - obj_free(mrb, &p->as.basic); + obj_free(mrb, &p->as.basic, TRUE); } mrb_free(mrb, tmp); } } +void +mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc) +{ + free_heap(mrb, gc); +#ifndef MRB_GC_FIXED_ARENA + mrb_free(mrb, gc->arena); +#endif +} + static void -gc_protect(mrb_state *mrb, struct RBasic *p) +gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p) { #ifdef MRB_GC_FIXED_ARENA - if (mrb->arena_idx >= MRB_GC_ARENA_SIZE) { + if (gc->arena_idx >= MRB_GC_ARENA_SIZE) { /* arena overflow error */ - mrb->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */ - mrb_raise(mrb, E_RUNTIME_ERROR, "arena overflow error"); + gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */ + mrb_exc_raise(mrb, mrb_obj_value(mrb->arena_err)); } #else - if (mrb->arena_idx >= mrb->arena_capa) { + if (gc->arena_idx >= gc->arena_capa) { /* extend arena */ - mrb->arena_capa = (int)(mrb->arena_capa * 1.5); - mrb->arena = (struct RBasic**)mrb_realloc(mrb, mrb->arena, sizeof(struct RBasic*)*mrb->arena_capa); + gc->arena_capa = (int)(gc->arena_capa * 3 / 2); + gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*gc->arena_capa); } #endif - mrb->arena[mrb->arena_idx++] = p; + gc->arena[gc->arena_idx++] = p; } +/* mrb_gc_protect() leaves the object in the arena */ MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj) { if (mrb_immediate_p(obj)) return; - gc_protect(mrb, mrb_basic_ptr(obj)); + gc_protect(mrb, &mrb->gc, mrb_basic_ptr(obj)); +} + +#define GC_ROOT_SYM MRB_SYM(_gc_root_) + +/* mrb_gc_register() keeps the object from GC. + + Register your object when it's exported to C world, + without reference from Ruby world, e.g. callback + arguments. Don't forget to remove the object using + mrb_gc_unregister, otherwise your object will leak. +*/ + +MRB_API void +mrb_gc_register(mrb_state *mrb, mrb_value obj) +{ + mrb_sym root; + mrb_value table; + + 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); + } + mrb_ary_push(mrb, table, obj); +} + +/* mrb_gc_unregister() removes the object from GC root. */ +MRB_API void +mrb_gc_unregister(mrb_state *mrb, mrb_value obj) +{ + 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_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_ptr(ARY_PTR(a)[i]) == mrb_ptr(obj)) { + mrb_int len = ARY_LEN(a)-1; + mrb_value *ptr = ARY_PTR(a); + + ARY_SET_LEN(a, len); + memmove(&ptr[i], &ptr[i + 1], (len - i) * sizeof(mrb_value)); + break; + } + } } 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) { + enum mrb_vtype tt; + + switch (cls->tt) { + case MRB_TT_CLASS: + case MRB_TT_SCLASS: + case MRB_TT_MODULE: + case MRB_TT_ENV: + break; + default: + mrb_raise(mrb, E_TYPE_ERROR, "allocation failure"); + } + tt = MRB_INSTANCE_TT(cls); + if (tt != MRB_TT_FALSE && + ttype != MRB_TT_SCLASS && + ttype != MRB_TT_ICLASS && + ttype != MRB_TT_ENV && + ttype != tt) { + 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); #endif - if (mrb->gc_threshold < mrb->live) { + if (gc->threshold < gc->live) { mrb_incremental_gc(mrb); } - if (mrb->free_heaps == NULL) { - add_heap(mrb); + if (gc->free_heaps == NULL) { + add_heap(mrb, gc); } - p = mrb->free_heaps->freelist; - mrb->free_heaps->freelist = ((struct free_obj*)p)->next; - if (mrb->free_heaps->freelist == NULL) { - unlink_free_heap_page(mrb, mrb->free_heaps); + p = gc->free_heaps->freelist; + gc->free_heaps->freelist = ((struct free_obj*)p)->next; + if (gc->free_heaps->freelist == NULL) { + unlink_free_heap_page(gc, gc->free_heaps); } - mrb->live++; - gc_protect(mrb, p); + gc->live++; + gc_protect(mrb, gc, p); *(RVALUE *)p = RVALUE_zero; p->tt = ttype; p->c = cls; - paint_partial_white(mrb, p); + paint_partial_white(gc, p); return p; } static inline void -add_gray_list(mrb_state *mrb, struct RBasic *obj) +add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { #ifdef MRB_GC_STRESS if (obj->tt > MRB_TT_MAXDEFINE) { @@ -432,8 +598,30 @@ add_gray_list(mrb_state *mrb, struct RBasic *obj) } #endif paint_gray(obj); - obj->gcnext = mrb->gray_list; - mrb->gray_list = obj; + obj->gcnext = gc->gray_list; + gc->gray_list = obj; +} + +static mrb_int +ci_nregs(mrb_callinfo *ci) +{ + const struct RProc *p = ci->proc; + mrb_int n = 0; + + if (!p) { + if (ci->argc < 0) return 3; + return ci->argc+2; + } + if (!MRB_PROC_CFUNC_P(p) && p->body.irep) { + n = p->body.irep->nregs; + } + if (ci->argc < 0) { + if (n < 3) n = 3; /* self + args + blk */ + } + if (ci->argc > n) { + n = ci->argc + 2; /* self + blk */ + } + return n; } static void @@ -441,64 +629,71 @@ mark_context_stack(mrb_state *mrb, struct mrb_context *c) { size_t i; size_t e; + mrb_value nil; - e = c->stack - c->stbase; - if (c->ci) e += c->ci->nregs; + 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]; if (!mrb_immediate_p(v)) { - if (mrb_basic_ptr(v)->tt == MRB_TT_FREE) { - c->stbase[i] = mrb_nil_value(); - } - else { - mrb_gc_mark(mrb, mrb_basic_ptr(v)); - } + mrb_gc_mark(mrb, mrb_basic_ptr(v)); } } + e = c->stend - c->stbase; + nil = mrb_nil_value(); + for (; i<e; i++) { + c->stbase[i] = nil; + } } static void mark_context(mrb_state *mrb, struct mrb_context *c) { - int i, e = 0; mrb_callinfo *ci; - /* mark stack */ - mark_context_stack(mrb, c); + start: + if (c->status == MRB_FIBER_TERMINATED) return; /* mark VM stack */ + mark_context_stack(mrb, c); + + /* mark call stack */ if (c->cibase) { for (ci = c->cibase; ci <= c->ci; ci++) { - if (ci->eidx > e) { - e = ci->eidx; - } - 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<e; i++) { - mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]); - } /* mark fibers */ - if (c->prev && c->prev->fib) { - mrb_gc_mark(mrb, (struct RBasic*)c->prev->fib); + mrb_gc_mark(mrb, (struct RBasic*)c->fib); + if (c->prev) { + c = c->prev; + goto start; } } static void -gc_mark_children(mrb_state *mrb, struct RBasic *obj) +gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { mrb_assert(is_gray(obj)); paint_black(obj); - mrb->gray_list = obj->gcnext; mrb_gc_mark(mrb, (struct RBasic*)obj->c); switch (obj->tt) { case MRB_TT_ICLASS: - mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + { + struct RClass *c = (struct RClass*)obj; + if (MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_ORIGIN)) + mrb_gc_mark_mt(mrb, c); + mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + } break; case MRB_TT_CLASS: @@ -522,22 +717,22 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj) { struct RProc *p = (struct RProc*)obj; - mrb_gc_mark(mrb, (struct RBasic*)p->env); - mrb_gc_mark(mrb, (struct RBasic*)p->target_class); + mrb_gc_mark(mrb, (struct RBasic*)p->upper); + mrb_gc_mark(mrb, (struct RBasic*)p->e.env); } break; case MRB_TT_ENV: { struct REnv *e = (struct REnv*)obj; + mrb_int i, len; - if (!MRB_ENV_STACK_SHARED_P(e)) { - mrb_int i, len; - - len = MRB_ENV_STACK_LEN(e); - for (i=0; i<len; i++) { - mrb_gc_mark_value(mrb, e->stack[i]); - } + if (MRB_ENV_ONSTACK_P(e) && e->cxt && e->cxt->fib) { + mrb_gc_mark(mrb, (struct RBasic*)e->cxt->fib); + } + len = MRB_ENV_LEN(e); + for (i=0; i<len; i++) { + mrb_gc_mark_value(mrb, e->stack[i]); } } break; @@ -553,10 +748,11 @@ gc_mark_children(mrb_state *mrb, 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=a->len; i<e; i++) { - mrb_gc_mark_value(mrb, a->ptr[i]); + for (i=0; i<e; i++) { + mrb_gc_mark_value(mrb, p[i]); } } break; @@ -567,16 +763,21 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj) break; case MRB_TT_STRING: + 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; @@ -590,30 +791,20 @@ 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, obj); + add_gray_list(mrb, &mrb->gc, obj); } static void -obj_free(mrb_state *mrb, struct RBasic *obj) +obj_free(mrb_state *mrb, struct RBasic *obj, int end) { - DEBUG(printf("obj_free(%p,tt=%d)\n",obj,obj->tt)); + DEBUG(fprintf(stderr, "obj_free(%p,tt=%d)\n",obj,obj->tt)); switch (obj->tt) { - /* immediate - no mark */ - case MRB_TT_TRUE: - case MRB_TT_FIXNUM: - case MRB_TT_SYMBOL: - /* cannot happen */ - return; - - case MRB_TT_FLOAT: -#ifdef MRB_WORD_BOXING + case MRB_TT_OBJECT: + mrb_gc_free_iv(mrb, (struct RObject*)obj); break; -#else - return; -#endif - case MRB_TT_OBJECT: case MRB_TT_EXCEPTION: mrb_gc_free_iv(mrb, (struct RObject*)obj); break; @@ -623,16 +814,26 @@ obj_free(mrb_state *mrb, struct RBasic *obj) 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)) { - mrb_free(mrb, e->stack); + if (MRB_ENV_ONSTACK_P(e)) { + /* cannot be freed */ e->stack = NULL; + break; } + mrb_free(mrb, e->stack); + e->stack = NULL; } break; @@ -640,16 +841,30 @@ obj_free(mrb_state *mrb, struct RBasic *obj) { struct mrb_context *c = ((struct RFiber*)obj)->cxt; - if (c != mrb->root_c) + if (c && c != mrb->root_c) { + if (!end && c->status != MRB_FIBER_TERMINATED) { + mrb_callinfo *ci = c->ci; + mrb_callinfo *ce = c->cibase; + + while (ce <= ci) { + 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--; + } + } mrb_free_context(mrb, c); + } } break; case MRB_TT_ARRAY: if (ARY_SHARED_P(obj)) - mrb_ary_decref(mrb, ((struct RArray*)obj)->aux.shared); - else - mrb_free(mrb, ((struct RArray*)obj)->ptr); + mrb_ary_decref(mrb, ((struct RArray*)obj)->as.heap.aux.shared); + else if (!ARY_EMBED_P(obj)) + mrb_free(mrb, ((struct RArray*)obj)->as.heap.ptr); break; case MRB_TT_HASH: @@ -666,13 +881,17 @@ obj_free(mrb_state *mrb, struct RBasic *obj) struct RProc *p = (struct RProc*)obj; if (!MRB_PROC_CFUNC_P(p) && p->body.irep) { - mrb_irep_decref(mrb, p->body.irep); + mrb_irep *irep = (mrb_irep*)p->body.irep; + if (end) { + mrb_irep_cutref(mrb, irep); + } + mrb_irep_decref(mrb, irep); } } 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: @@ -685,6 +904,24 @@ obj_free(mrb_state *mrb, struct RBasic *obj) } 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; } @@ -692,45 +929,68 @@ obj_free(mrb_state *mrb, struct RBasic *obj) } static void -root_scan_phase(mrb_state *mrb) +root_scan_phase(mrb_state *mrb, mrb_gc *gc) { - size_t i, e; + int i, e; - if (!is_minor_gc(mrb)) { - mrb->gray_list = NULL; - mrb->atomic_gray_list = NULL; + if (!is_minor_gc(gc)) { + gc->gray_list = NULL; + gc->atomic_gray_list = NULL; } mrb_gc_mark_gv(mrb); /* mark arena */ - for (i=0,e=mrb->arena_idx; i<e; i++) { - mrb_gc_mark(mrb, mrb->arena[i]); + for (i=0,e=gc->arena_idx; i<e; i++) { + mrb_gc_mark(mrb, gc->arena[i]); } /* mark class hierarchy */ mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class); + + /* mark built-in classes */ + mrb_gc_mark(mrb, (struct RBasic*)mrb->class_class); + mrb_gc_mark(mrb, (struct RBasic*)mrb->module_class); + mrb_gc_mark(mrb, (struct RBasic*)mrb->proc_class); + mrb_gc_mark(mrb, (struct RBasic*)mrb->string_class); + mrb_gc_mark(mrb, (struct RBasic*)mrb->array_class); + mrb_gc_mark(mrb, (struct RBasic*)mrb->hash_class); + mrb_gc_mark(mrb, (struct RBasic*)mrb->range_class); + +#ifndef MRB_NO_FLOAT + mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class); +#endif + 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); + mrb_gc_mark(mrb, (struct RBasic*)mrb->symbol_class); + mrb_gc_mark(mrb, (struct RBasic*)mrb->kernel_module); + + mrb_gc_mark(mrb, (struct RBasic*)mrb->eException_class); + mrb_gc_mark(mrb, (struct RBasic*)mrb->eStandardError_class); + /* mark top_self */ mrb_gc_mark(mrb, (struct RBasic*)mrb->top_self); /* mark exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); /* mark pre-allocated exception */ mrb_gc_mark(mrb, (struct RBasic*)mrb->nomem_err); + mrb_gc_mark(mrb, (struct RBasic*)mrb->stack_err); +#ifdef MRB_GC_FIXED_ARENA + mrb_gc_mark(mrb, (struct RBasic*)mrb->arena_err); +#endif - mark_context(mrb, mrb->root_c); - if (mrb->root_c->fib) { - mrb_gc_mark(mrb, (struct RBasic*)mrb->root_c->fib); - } + mark_context(mrb, mrb->c); if (mrb->root_c != mrb->c) { - mark_context(mrb, mrb->c); + mark_context(mrb, mrb->root_c); } } +/* rough estimation of number of GC marks (non recursive) */ static size_t -gc_gray_mark(mrb_state *mrb, struct RBasic *obj) +gc_gray_counts(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { size_t children = 0; - gc_mark_children(mrb, obj); - switch (obj->tt) { case MRB_TT_ICLASS: children++; @@ -755,7 +1015,7 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj) break; case MRB_TT_ENV: - children += (int)obj->flags; + children += MRB_ENV_LEN(obj); break; case MRB_TT_FIBER: @@ -764,16 +1024,17 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj) size_t i; mrb_callinfo *ci; - if (!c) break; + if (!c || c->status == MRB_FIBER_TERMINATED) break; + /* mark stack */ - i = c->stack - c->stbase; - if (c->ci) i += c->ci->nregs; + i = c->ci->stack - c->stbase; + + if (c->ci) { + i += ci_nregs(c->ci); + } if (c->stbase + i > c->stend) i = c->stend - c->stbase; children += i; - /* mark ensure stack */ - children += (c->ci) ? c->ci->eidx : 0; - /* mark closure */ if (c->cibase) { for (i=0, ci = c->cibase; ci <= c->ci; i++, ci++) @@ -786,7 +1047,7 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj) case MRB_TT_ARRAY: { struct RArray *a = (struct RArray*)obj; - children += a->len; + children += ARY_LEN(a); } break; @@ -797,6 +1058,7 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj) case MRB_TT_PROC: case MRB_TT_RANGE: + case MRB_TT_BREAK: children+=2; break; @@ -808,133 +1070,151 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj) static void -gc_mark_gray_list(mrb_state *mrb) { - while (mrb->gray_list) { - if (is_gray(mrb->gray_list)) - gc_mark_children(mrb, mrb->gray_list); - else - mrb->gray_list = mrb->gray_list->gcnext; +gc_mark_gray_list(mrb_state *mrb, mrb_gc *gc) { + while (gc->gray_list) { + struct RBasic *obj = gc->gray_list; + gc->gray_list = obj->gcnext; + gc_mark_children(mrb, gc, obj); } } static size_t -incremental_marking_phase(mrb_state *mrb, size_t limit) +incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) { size_t tried_marks = 0; - while (mrb->gray_list && tried_marks < limit) { - tried_marks += gc_gray_mark(mrb, mrb->gray_list); + while (gc->gray_list && tried_marks < limit) { + 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; } static void -final_marking_phase(mrb_state *mrb) +final_marking_phase(mrb_state *mrb, mrb_gc *gc) { - mark_context_stack(mrb, mrb->root_c); - gc_mark_gray_list(mrb); - mrb_assert(mrb->gray_list == NULL); - mrb->gray_list = mrb->atomic_gray_list; - mrb->atomic_gray_list = NULL; - gc_mark_gray_list(mrb); - mrb_assert(mrb->gray_list == NULL); + int i, e; + + /* mark arena */ + for (i=0,e=gc->arena_idx; i<e; i++) { + mrb_gc_mark(mrb, gc->arena[i]); + } + mrb_gc_mark_gv(mrb); + mark_context(mrb, mrb->c); + 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); + gc->gray_list = gc->atomic_gray_list; + gc->atomic_gray_list = NULL; + gc_mark_gray_list(mrb, gc); + mrb_assert(gc->gray_list == NULL); } static void -prepare_incremental_sweep(mrb_state *mrb) +prepare_incremental_sweep(mrb_state *mrb, mrb_gc *gc) { - mrb->gc_state = GC_STATE_SWEEP; - mrb->sweeps = mrb->heaps; - mrb->gc_live_after_mark = mrb->live; + gc->state = MRB_GC_STATE_SWEEP; + gc->sweeps = gc->heaps; + gc->live_after_mark = gc->live; } static size_t -incremental_sweep_phase(mrb_state *mrb, size_t limit) +incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) { - struct heap_page *page = mrb->sweeps; + mrb_heap_page *page = gc->sweeps; size_t tried_sweep = 0; while (page && (tried_sweep < limit)) { - RVALUE *p = page->objects; + RVALUE *p = objects(page); RVALUE *e = p + MRB_HEAP_PAGE_SIZE; size_t freed = 0; mrb_bool dead_slot = TRUE; mrb_bool full = (page->freelist == NULL); - if (is_minor_gc(mrb) && page->old) { + if (is_minor_gc(gc) && page->old) { /* skip a slot which doesn't contain any young object */ p = e; dead_slot = FALSE; } while (p<e) { - if (is_dead(mrb, &p->as.basic)) { + if (is_dead(gc, &p->as.basic)) { if (p->as.basic.tt != MRB_TT_FREE) { - obj_free(mrb, &p->as.basic); - p->as.free.next = page->freelist; - page->freelist = (struct RBasic*)p; - freed++; + obj_free(mrb, &p->as.basic, FALSE); + if (p->as.basic.tt == MRB_TT_FREE) { + p->as.free.next = page->freelist; + page->freelist = (struct RBasic*)p; + freed++; + } + else { + dead_slot = FALSE; + } } } else { - if (!is_generational(mrb)) - paint_partial_white(mrb, &p->as.basic); /* next gc target */ - dead_slot = 0; + if (!is_generational(gc)) + paint_partial_white(gc, &p->as.basic); /* next gc target */ + dead_slot = FALSE; } p++; } /* free dead slot */ if (dead_slot && freed < MRB_HEAP_PAGE_SIZE) { - struct heap_page *next = page->next; + mrb_heap_page *next = page->next; - unlink_heap_page(mrb, page); - unlink_free_heap_page(mrb, page); + unlink_heap_page(gc, page); + unlink_free_heap_page(gc, page); mrb_free(mrb, page); page = next; } else { if (full && freed > 0) { - link_free_heap_page(mrb, page); + link_free_heap_page(gc, page); } - if (page->freelist == NULL && is_minor_gc(mrb)) + if (page->freelist == NULL && is_minor_gc(gc)) page->old = TRUE; else page->old = FALSE; page = page->next; } tried_sweep += MRB_HEAP_PAGE_SIZE; - mrb->live -= freed; - mrb->gc_live_after_mark -= freed; + gc->live -= freed; + gc->live_after_mark -= freed; } - mrb->sweeps = page; + gc->sweeps = page; return tried_sweep; } static size_t -incremental_gc(mrb_state *mrb, size_t limit) +incremental_gc(mrb_state *mrb, mrb_gc *gc, size_t limit) { - switch (mrb->gc_state) { - case GC_STATE_ROOT: - root_scan_phase(mrb); - mrb->gc_state = GC_STATE_MARK; - flip_white_part(mrb); + switch (gc->state) { + case MRB_GC_STATE_ROOT: + root_scan_phase(mrb, gc); + gc->state = MRB_GC_STATE_MARK; + flip_white_part(gc); return 0; - case GC_STATE_MARK: - if (mrb->gray_list) { - return incremental_marking_phase(mrb, limit); + case MRB_GC_STATE_MARK: + if (gc->gray_list) { + return incremental_marking_phase(mrb, gc, limit); } else { - final_marking_phase(mrb); - prepare_incremental_sweep(mrb); + final_marking_phase(mrb, gc); + prepare_incremental_sweep(mrb, gc); return 0; } - case GC_STATE_SWEEP: { + case MRB_GC_STATE_SWEEP: { size_t tried_sweep = 0; - tried_sweep = incremental_sweep_phase(mrb, limit); + tried_sweep = incremental_sweep_phase(mrb, gc, limit); if (tried_sweep == 0) - mrb->gc_state = GC_STATE_ROOT; + gc->state = MRB_GC_STATE_ROOT; return tried_sweep; } default: @@ -945,79 +1225,90 @@ incremental_gc(mrb_state *mrb, size_t limit) } static void -incremental_gc_until(mrb_state *mrb, enum gc_state to_state) +incremental_gc_until(mrb_state *mrb, mrb_gc *gc, mrb_gc_state to_state) { do { - incremental_gc(mrb, SIZE_MAX); - } while (mrb->gc_state != to_state); + incremental_gc(mrb, gc, SIZE_MAX); + } while (gc->state != to_state); } static void -incremental_gc_step(mrb_state *mrb) +incremental_gc_step(mrb_state *mrb, mrb_gc *gc) { size_t limit = 0, result = 0; - limit = (GC_STEP_SIZE/100) * mrb->gc_step_ratio; + limit = (GC_STEP_SIZE/100) * gc->step_ratio; while (result < limit) { - result += incremental_gc(mrb, limit); - if (mrb->gc_state == GC_STATE_ROOT) + result += incremental_gc(mrb, gc, limit); + if (gc->state == MRB_GC_STATE_ROOT) break; } - mrb->gc_threshold = mrb->live + GC_STEP_SIZE; + gc->threshold = gc->live + GC_STEP_SIZE; } static void -clear_all_old(mrb_state *mrb) +clear_all_old(mrb_state *mrb, mrb_gc *gc) { - mrb_bool origin_mode = mrb->is_generational_gc_mode; + mrb_bool origin_mode = gc->generational; - mrb_assert(is_generational(mrb)); - if (is_major_gc(mrb)) { + mrb_assert(is_generational(gc)); + if (is_major_gc(gc)) { /* finish the half baked GC */ - incremental_gc_until(mrb, GC_STATE_ROOT); + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } /* Sweep the dead objects, then reset all the live objects * (including all the old objects, of course) to white. */ - mrb->is_generational_gc_mode = FALSE; - prepare_incremental_sweep(mrb); - incremental_gc_until(mrb, GC_STATE_ROOT); - mrb->is_generational_gc_mode = origin_mode; + gc->generational = FALSE; + prepare_incremental_sweep(mrb, gc); + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); + gc->generational = origin_mode; /* The gray objects have already been painted as white */ - mrb->atomic_gray_list = mrb->gray_list = NULL; + gc->atomic_gray_list = gc->gray_list = NULL; } MRB_API void mrb_incremental_gc(mrb_state *mrb) { - if (mrb->gc_disabled) return; + mrb_gc *gc = &mrb->gc; + + if (gc->disabled || gc->iterating) return; GC_INVOKE_TIME_REPORT("mrb_incremental_gc()"); GC_TIME_START; - if (is_minor_gc(mrb)) { - incremental_gc_until(mrb, GC_STATE_ROOT); + if (is_minor_gc(gc)) { + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } else { - incremental_gc_step(mrb); + incremental_gc_step(mrb, gc); } - if (mrb->gc_state == GC_STATE_ROOT) { - mrb_assert(mrb->live >= mrb->gc_live_after_mark); - mrb->gc_threshold = (mrb->gc_live_after_mark/100) * mrb->gc_interval_ratio; - if (mrb->gc_threshold < GC_STEP_SIZE) { - mrb->gc_threshold = GC_STEP_SIZE; + if (gc->state == MRB_GC_STATE_ROOT) { + mrb_assert(gc->live >= gc->live_after_mark); + gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; + if (gc->threshold < GC_STEP_SIZE) { + gc->threshold = GC_STEP_SIZE; } - if (is_major_gc(mrb)) { - mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; - mrb->gc_full = FALSE; + if (is_major_gc(gc)) { + size_t threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO; + + gc->full = FALSE; + if (threshold < MAJOR_GC_TOOMANY) { + gc->majorgc_old_threshold = threshold; + } + else { + /* too many objects allocated during incremental GC, */ + /* instead of increasing threshold, invoke full GC. */ + mrb_full_gc(mrb); + } } - else if (is_minor_gc(mrb)) { - if (mrb->live > mrb->majorgc_old_threshold) { - clear_all_old(mrb); - mrb->gc_full = TRUE; + else if (is_minor_gc(gc)) { + if (gc->live > gc->majorgc_old_threshold) { + clear_all_old(mrb, gc); + gc->full = TRUE; } } } @@ -1029,28 +1320,35 @@ mrb_incremental_gc(mrb_state *mrb) MRB_API void mrb_full_gc(mrb_state *mrb) { - if (mrb->gc_disabled) return; + mrb_gc *gc = &mrb->gc; + + if (!mrb->c) return; + if (gc->disabled || gc->iterating) return; + GC_INVOKE_TIME_REPORT("mrb_full_gc()"); GC_TIME_START; - if (is_generational(mrb)) { + if (is_generational(gc)) { /* clear all the old objects back to young */ - clear_all_old(mrb); - mrb->gc_full = TRUE; + clear_all_old(mrb, gc); + gc->full = TRUE; } - else if (mrb->gc_state != GC_STATE_ROOT) { + else if (gc->state != MRB_GC_STATE_ROOT) { /* finish half baked GC cycle */ - incremental_gc_until(mrb, GC_STATE_ROOT); + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } - incremental_gc_until(mrb, GC_STATE_ROOT); - mrb->gc_threshold = (mrb->gc_live_after_mark/100) * mrb->gc_interval_ratio; + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); + gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; - if (is_generational(mrb)) { - mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; - mrb->gc_full = FALSE; + if (is_generational(gc)) { + gc->majorgc_old_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO; + gc->full = FALSE; } +#ifdef MRB_USE_MALLOC_TRIM + malloc_trim(0); +#endif GC_TIME_STOP_AND_REPORT; } @@ -1060,32 +1358,6 @@ mrb_garbage_collect(mrb_state *mrb) mrb_full_gc(mrb); } -MRB_API int -mrb_gc_arena_save(mrb_state *mrb) -{ - return mrb->arena_idx; -} - -MRB_API void -mrb_gc_arena_restore(mrb_state *mrb, int idx) -{ -#ifndef MRB_GC_FIXED_ARENA - int capa = mrb->arena_capa; - - if (idx < capa / 2) { - capa = (int)(capa * 0.66); - if (capa < MRB_GC_ARENA_SIZE) { - capa = MRB_GC_ARENA_SIZE; - } - if (capa != mrb->arena_capa) { - mrb->arena = (struct RBasic**)mrb_realloc(mrb, mrb->arena, sizeof(struct RBasic*)*capa); - mrb->arena_capa = capa; - } - } -#endif - mrb->arena_idx = idx; -} - /* * Field write barrier * Paint obj(Black) -> value(White) to obj(Black) -> value(Gray). @@ -1094,18 +1366,20 @@ mrb_gc_arena_restore(mrb_state *mrb, int idx) MRB_API void mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value) { + mrb_gc *gc = &mrb->gc; + if (!is_black(obj)) return; if (!is_white(value)) return; - mrb_assert(!is_dead(mrb, value) && !is_dead(mrb, obj)); - mrb_assert(is_generational(mrb) || mrb->gc_state != GC_STATE_ROOT); + mrb_assert(gc->state == MRB_GC_STATE_MARK || (!is_dead(gc, value) && !is_dead(gc, obj))); + mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); - if (is_generational(mrb) || mrb->gc_state == GC_STATE_MARK) { - add_gray_list(mrb, value); + if (is_generational(gc) || gc->state == MRB_GC_STATE_MARK) { + add_gray_list(mrb, gc, value); } else { - mrb_assert(mrb->gc_state == GC_STATE_SWEEP); - paint_partial_white(mrb, obj); /* for never write barriers */ + mrb_assert(gc->state == MRB_GC_STATE_SWEEP); + paint_partial_white(gc, obj); /* for never write barriers */ } } @@ -1121,13 +1395,15 @@ mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value MRB_API void mrb_write_barrier(mrb_state *mrb, struct RBasic *obj) { + mrb_gc *gc = &mrb->gc; + if (!is_black(obj)) return; - mrb_assert(!is_dead(mrb, obj)); - mrb_assert(is_generational(mrb) || mrb->gc_state != GC_STATE_ROOT); + mrb_assert(!is_dead(gc, obj)); + mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); paint_gray(obj); - obj->gcnext = mrb->atomic_gray_list; - mrb->atomic_gray_list = obj; + obj->gcnext = gc->atomic_gray_list; + gc->atomic_gray_list = obj; } /* @@ -1161,9 +1437,9 @@ gc_start(mrb_state *mrb, mrb_value obj) static mrb_value gc_enable(mrb_state *mrb, mrb_value obj) { - mrb_bool old = mrb->gc_disabled; + mrb_bool old = mrb->gc.disabled; - mrb->gc_disabled = FALSE; + mrb->gc.disabled = FALSE; return mrb_bool_value(old); } @@ -1183,9 +1459,9 @@ gc_enable(mrb_state *mrb, mrb_value obj) static mrb_value gc_disable(mrb_state *mrb, mrb_value obj) { - mrb_bool old = mrb->gc_disabled; + mrb_bool old = mrb->gc.disabled; - mrb->gc_disabled = TRUE; + mrb->gc.disabled = TRUE; return mrb_bool_value(old); } @@ -1201,7 +1477,7 @@ gc_disable(mrb_state *mrb, mrb_value obj) static mrb_value gc_interval_ratio_get(mrb_state *mrb, mrb_value obj) { - return mrb_fixnum_value(mrb->gc_interval_ratio); + return mrb_fixnum_value(mrb->gc.interval_ratio); } /* @@ -1219,7 +1495,7 @@ gc_interval_ratio_set(mrb_state *mrb, mrb_value obj) mrb_int ratio; mrb_get_args(mrb, "i", &ratio); - mrb->gc_interval_ratio = ratio; + mrb->gc.interval_ratio = (int)ratio; return mrb_nil_value(); } @@ -1234,7 +1510,7 @@ gc_interval_ratio_set(mrb_state *mrb, mrb_value obj) static mrb_value gc_step_ratio_get(mrb_state *mrb, mrb_value obj) { - return mrb_fixnum_value(mrb->gc_step_ratio); + return mrb_fixnum_value(mrb->gc.step_ratio); } /* @@ -1252,24 +1528,28 @@ gc_step_ratio_set(mrb_state *mrb, mrb_value obj) mrb_int ratio; mrb_get_args(mrb, "i", &ratio); - mrb->gc_step_ratio = ratio; + mrb->gc.step_ratio = (int)ratio; return mrb_nil_value(); } static void -change_gen_gc_mode(mrb_state *mrb, mrb_bool enable) +change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable) { - if (is_generational(mrb) && !enable) { - clear_all_old(mrb); - mrb_assert(mrb->gc_state == GC_STATE_ROOT); - mrb->gc_full = FALSE; + if (gc->disabled || gc->iterating) { + mrb_raise(mrb, E_RUNTIME_ERROR, "generational mode changed when GC disabled"); + return; } - else if (!is_generational(mrb) && enable) { - incremental_gc_until(mrb, GC_STATE_ROOT); - mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; - mrb->gc_full = FALSE; + if (is_generational(gc) && !enable) { + clear_all_old(mrb, gc); + mrb_assert(gc->state == MRB_GC_STATE_ROOT); + gc->full = FALSE; } - mrb->is_generational_gc_mode = enable; + else if (!is_generational(gc) && enable) { + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); + gc->majorgc_old_threshold = gc->live_after_mark/100 * MAJOR_GC_INC_RATIO; + gc->full = FALSE; + } + gc->generational = enable; } /* @@ -1283,7 +1563,7 @@ change_gen_gc_mode(mrb_state *mrb, mrb_bool enable) static mrb_value gc_generational_mode_get(mrb_state *mrb, mrb_value self) { - return mrb_bool_value(mrb->is_generational_gc_mode); + return mrb_bool_value(mrb->gc.generational); } /* @@ -1300,41 +1580,74 @@ gc_generational_mode_set(mrb_state *mrb, mrb_value self) mrb_bool enable; mrb_get_args(mrb, "b", &enable); - if (mrb->is_generational_gc_mode != enable) - change_gen_gc_mode(mrb, enable); + if (mrb->gc.generational != enable) + change_gen_gc_mode(mrb, &mrb->gc, enable); return mrb_bool_value(enable); } -void -mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data) + +static void +gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data) { - struct heap_page* page = mrb->heaps; + mrb_heap_page* page; + page = gc->heaps; while (page != NULL) { - RVALUE *p, *pend; + RVALUE *p; + int i; - p = page->objects; - pend = p + MRB_HEAP_PAGE_SIZE; - for (;p < pend; p++) { - (*callback)(mrb, &p->as.basic, data); + p = objects(page); + for (i=0; i < MRB_HEAP_PAGE_SIZE; i++) { + if ((*callback)(mrb, &p[i].as.basic, data) == MRB_EACH_OBJ_BREAK) + return; } - page = page->next; } } -#ifdef GC_TEST -#ifdef GC_DEBUG -static mrb_value gc_test(mrb_state *, mrb_value); -#endif -#endif +void +mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data) +{ + mrb_bool iterating = mrb->gc.iterating; + + mrb_full_gc(mrb); + mrb->gc.iterating = TRUE; + if (iterating) { + gc_each_objects(mrb, &mrb->gc, callback, data); + } + else { + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + gc_each_objects(mrb, &mrb->gc, callback, data); + mrb->jmp = prev_jmp; + mrb->gc.iterating = iterating; + } MRB_CATCH(&c_jmp) { + mrb->gc.iterating = iterating; + mrb->jmp = prev_jmp; + MRB_THROW(prev_jmp); + } MRB_END_EXC(&c_jmp); + } +} + +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()); @@ -1346,272 +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; - - puts("test_mrb_field_write_barrier"); - mrb->is_generational_gc_mode = 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(mrb, value); - - - puts(" in GC_STATE_MARK"); - mrb->gc_state = GC_STATE_MARK; - mrb_field_write_barrier(mrb, obj, value); - - mrb_assert(is_gray(value)); - - - puts(" in GC_STATE_SWEEP"); - paint_partial_white(mrb, value); - mrb->gc_state = GC_STATE_SWEEP; - mrb_field_write_barrier(mrb, obj, value); - - mrb_assert(obj->color & mrb->current_white_part); - mrb_assert(value->color & mrb->current_white_part); - - - puts(" fail with black"); - mrb->gc_state = GC_STATE_MARK; - paint_white(obj); - paint_partial_white(mrb, value); - mrb_field_write_barrier(mrb, obj, value); - - mrb_assert(obj->color & mrb->current_white_part); - - - puts(" fail with gray"); - mrb->gc_state = 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(mrb, mrb_basic_ptr(value)); - - mrb->gc_state = 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; - - puts("test_mrb_write_barrier"); - obj = mrb_basic_ptr(mrb_ary_new(mrb)); - paint_black(obj); - - puts(" in GC_STATE_MARK"); - mrb->gc_state = GC_STATE_MARK; - mrb_write_barrier(mrb, obj); - - mrb_assert(is_gray(obj)); - mrb_assert(mrb->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; - - puts("test_add_gray_list"); - change_gen_gc_mode(mrb, FALSE); - mrb_assert(mrb->gray_list == NULL); - obj1 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test")); - add_gray_list(mrb, obj1); - mrb_assert(mrb->gray_list == obj1); - mrb_assert(is_gray(obj1)); - - obj2 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test")); - add_gray_list(mrb, obj2); - mrb_assert(mrb->gray_list == obj2); - mrb_assert(mrb->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; - - 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, 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(mrb, mrb_basic_ptr(value_v)); - mrb_ary_push(mrb, obj_v, value_v); - gray_num = gc_gray_mark(mrb, 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; - struct heap_page *page; - - puts("test_incremental_gc"); - change_gen_gc_mode(mrb, FALSE); - - puts(" in mrb_full_gc"); - mrb_full_gc(mrb); - - mrb_assert(mrb->gc_state == GC_STATE_ROOT); - puts(" in GC_STATE_ROOT"); - incremental_gc(mrb, max); - mrb_assert(mrb->gc_state == GC_STATE_MARK); - puts(" in GC_STATE_MARK"); - incremental_gc_until(mrb, GC_STATE_SWEEP); - mrb_assert(mrb->gc_state == GC_STATE_SWEEP); - - puts(" in GC_STATE_SWEEP"); - page = mrb->heaps; - while (page) { - RVALUE *p = page->objects; - 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(mrb, &p->as.basic)) { - printf("%p\n", &p->as.basic); - } - p++; - } - page = page->next; - total += MRB_HEAP_PAGE_SIZE; - } - - mrb_assert(mrb->gray_list == NULL); - - incremental_gc(mrb, max); - mrb_assert(mrb->gc_state == GC_STATE_SWEEP); - - incremental_gc(mrb, max); - mrb_assert(mrb->gc_state == GC_STATE_ROOT); - - free = (RVALUE*)mrb->heaps->freelist; - while (free) { - freed++; - free = (RVALUE*)free->as.free.next; - } - - mrb_assert(mrb->live == live); - mrb_assert(mrb->live == total-freed); - - puts("test_incremental_gc(gen)"); - incremental_gc_until(mrb, GC_STATE_SWEEP); - change_gen_gc_mode(mrb, TRUE); - - mrb_assert(mrb->gc_full == FALSE); - mrb_assert(mrb->gc_state == GC_STATE_ROOT); - - puts(" in minor"); - mrb_assert(is_minor_gc(mrb)); - mrb_assert(mrb->majorgc_old_threshold > 0); - mrb->majorgc_old_threshold = 0; - mrb_incremental_gc(mrb); - mrb_assert(mrb->gc_full == TRUE); - mrb_assert(mrb->gc_state == GC_STATE_ROOT); - - puts(" in major"); - mrb_assert(is_major_gc(mrb)); - do { - mrb_incremental_gc(mrb); - } while (mrb->gc_state != GC_STATE_ROOT); - mrb_assert(mrb->gc_full == FALSE); - - mrb_close(mrb); -} - -void -test_incremental_sweep_phase(void) -{ - mrb_state *mrb = mrb_open(); - - puts("test_incremental_sweep_phase"); - - add_heap(mrb); - mrb->sweeps = mrb->heaps; - - mrb_assert(mrb->heaps->next->next == NULL); - mrb_assert(mrb->free_heaps->next->next == NULL); - incremental_sweep_phase(mrb, MRB_HEAP_PAGE_SIZE*3); - - mrb_assert(mrb->heaps->next == NULL); - mrb_assert(mrb->heaps == mrb->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 0bda2b48b..2704be0c4 100644 --- a/src/hash.c +++ b/src/hash.c @@ -4,276 +4,1248 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/hash.h" -#include "mruby/khash.h" -#include "mruby/string.h" -#include "mruby/variable.h" - -/* a function to get hash value of a float number */ -mrb_int mrb_float_id(mrb_float f); - -static inline khint_t -mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key) -{ - enum mrb_vtype t = mrb_type(key); - mrb_value hv; - const char *p; - mrb_int i, len; - khint_t h; - - switch (t) { - case MRB_TT_STRING: - p = RSTRING_PTR(key); - len = RSTRING_LEN(key); - h = 0; - for (i=0; i<len; i++) { - h = (h << 5) - h + *p++; - } - return 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> - case MRB_TT_SYMBOL: - h = (khint_t)mrb_symbol(key); - return kh_int_hash_func(mrb, h); +/* + * === 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. + */ - case MRB_TT_FIXNUM: - h = (khint_t)mrb_float_id((mrb_float)mrb_fixnum(key)); - return kh_int_hash_func(mrb, h); +#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; - case MRB_TT_FLOAT: - h = (khint_t)mrb_float_id(mrb_float(key)); - return kh_int_hash_func(mrb, h); +/* + * `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_; \ + } + +#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) + +#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) +{ + 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: + hash_code = mrb_str_hash(mrb, key); + break; + case MRB_TT_TRUE: + case MRB_TT_FALSE: + case MRB_TT_SYMBOL: + case MRB_TT_INTEGER: +#ifndef MRB_NO_FLOAT + case MRB_TT_FLOAT: +#endif + hash_code = U32(mrb_obj_id(key)); + break; default: - hv = mrb_funcall(mrb, key, "hash", 0); - h = (khint_t)t ^ mrb_fixnum(hv); - return kh_int_hash_func(mrb, h); + 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; } + return hash_code ^ (hash_code << 2) ^ (hash_code >> 2); } -static inline khint_t -mrb_hash_ht_hash_equal(mrb_state *mrb, 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 t = mrb_type(a); + enum mrb_vtype tt = mrb_type(a); + mrb_bool eql; - switch (t) { + 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); - case MRB_TT_FLOAT: - return (mrb_float)mrb_fixnum(a) == mrb_float(b); - default: - return FALSE; - } + case MRB_TT_INTEGER: + if (!mrb_integer_p(b)) return FALSE; + return mrb_integer(a) == mrb_integer(b); +#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: - return mrb_eql(mrb, a, b); + h_check_modified(mrb, h, {eql = mrb_eql(mrb, a, b);}); + return eql; + } +} + +static mrb_bool +entry_deleted_p(const hash_entry* entry) +{ + return mrb_undef_p(entry->key); +} + +static void +entry_delete(hash_entry* entry) +{ + entry->key = mrb_undef_value(); +} + +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; } } -typedef struct { - mrb_value v; - mrb_int n; -} mrb_hash_value; +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); +} -KHASH_DECLARE(ht, mrb_value, mrb_hash_value, TRUE) -KHASH_DEFINE (ht, mrb_value, mrb_hash_value, TRUE, mrb_hash_ht_hash_func, mrb_hash_ht_hash_equal) +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; + }); +} -static void mrb_hash_modify(mrb_state *mrb, mrb_value hash); +/* + * 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); +} -static inline mrb_value -mrb_hash_ht_key(mrb_state *mrb, mrb_value key) +static hash_entry* +ea_dup(mrb_state *mrb, const hash_entry *ea, uint32_t capa) { - if (mrb_string_p(key)) - return mrb_str_dup(mrb, key); - else - return key; + 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); } -#define KEY(key) mrb_hash_ht_key(mrb, key) +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; +} -void -mrb_gc_mark_hash(mrb_state *mrb, struct RHash *hash) +static hash_entry* +ea_get(hash_entry *ea, uint32_t index) { - khiter_t k; - khash_t(ht) *h = hash->ht; + return &ea[index]; +} - if (!h) return; - for (k = kh_begin(h); k != kh_end(h); k++) { - if (kh_exist(h, k)) { - mrb_value key = kh_key(h, k); - mrb_value val = kh_value(h, k).v; +static void +ea_set(hash_entry *ea, uint32_t index, mrb_value key, mrb_value val) +{ + ea[index].key = key; + ea[index].val = val; +} - mrb_gc_mark_value(mrb, key); - mrb_gc_mark_value(mrb, val); +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); +} + +static void +ar_free(mrb_state *mrb, struct RHash *h) +{ + mrb_free(mrb, ar_ea(h)); +} + +static void +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); +} + +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 { + ar_compress(mrb, h); + ea_n_used = size; + } } + ea_set(ar_ea(h), ea_n_used, key, val); + ar_set_size(h, ++size); + ar_set_ea_n_used(h, ++ea_n_used); } } -size_t -mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *hash) +static mrb_bool +ar_delete(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) { - if (!hash->ht) return 0; - return kh_size(hash->ht)*2; + 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; } -void -mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash) +static void +ar_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp) { - if (hash->ht) kh_destroy(ht, mrb, hash->ht); + 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; + }); } +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 (w_size != U32(r_entry - ea)) { + ea_set(ea, w_size, r_entry->key, r_entry->val); + entry_delete(r_entry); + } + ++w_size; + } + }); + mrb_assert(size == w_size); + ar_set_ea_n_used(h, size); + ar_adjust_ea(mrb, h, size, ea_capa); +} -MRB_API mrb_value -mrb_hash_new_capa(mrb_state *mrb, int capa) +static uint32_t +ib_it_pos_for(index_buckets_iter *it, uint32_t v) { - struct RHash *h; + return v & it->mask; +} - h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); - h->ht = kh_init(ht, mrb); - if (capa > 0) { - kh_resize(ht, mrb, h->ht, capa); - } - h->iv = 0; - return mrb_obj_value(h); +static uint32_t +ib_it_empty_value(const index_buckets_iter *it) +{ + return it->mask; } -MRB_API mrb_value -mrb_hash_new(mrb_state *mrb) +static uint32_t +ib_it_deleted_value(const index_buckets_iter *it) { - return mrb_hash_new_capa(mrb, 0); + return it->mask - 1; } -MRB_API mrb_value -mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key) +static mrb_bool +ib_it_empty_p(const index_buckets_iter *it) { - khash_t(ht) *h = RHASH_TBL(hash); - khiter_t k; + return it->ea_index == ib_it_empty_value(it); +} - if (h) { - k = kh_get(ht, mrb, h, key); - if (k != kh_end(h)) - return kh_value(h, k).v; - } +static mrb_bool +ib_it_deleted_p(const index_buckets_iter *it) +{ + return it->ea_index == ib_it_deleted_value(it); +} - /* not found */ - if (MRB_RHASH_PROCDEFAULT_P(hash)) { - return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); +static mrb_bool +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) +{ + 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; +} + +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; } - return RHASH_IFNONE(hash); + else { + it->shift1 = 0; + } + it->pos = ib_it_pos_for(it, it->pos + (++it->step)); } -MRB_API mrb_value -mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def) +static uint32_t +ib_it_get(const index_buckets_iter *it) { - khash_t(ht) *h = RHASH_TBL(hash); - khiter_t k; + return it->ea_index; +} - if (h) { - k = kh_get(ht, mrb, h, key); - if (k != kh_end(h)) - return kh_value(h, k).v; +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); } + i = it->ary_index; + mask = it->mask << it->shift2; + ht_ib(it->h)[i] = (ht_ib(it->h)[i] & ~mask) | (ea_index << it->shift2); +} - /* not found */ - return def; +static void +ib_it_delete(index_buckets_iter *it) +{ + ib_it_set(it, ib_it_deleted_value(it)); } -MRB_API void -mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) +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) { - khash_t(ht) *h; - khiter_t k; - int r; +#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 +} - mrb_hash_modify(mrb, hash); - h = RHASH_TBL(hash); +static uint32_t +ib_bit_to_capa(uint32_t bit) +{ + return U32(1) << bit; +} - if (!h) h = RHASH_TBL(hash) = kh_init(ht, mrb); - k = kh_put2(ht, mrb, h, key, &r); - kh_value(h, k).v = val; +static uint32_t +ib_upper_bound_for(uint32_t capa) +{ + return (capa >> 2) | (capa >> 1); /* 3/4 */ +} - if (r != 0) { - /* expand */ - int ai = mrb_gc_arena_save(mrb); - key = kh_key(h, k) = KEY(key); - mrb_gc_arena_restore(mrb, ai); - kh_value(h, k).n = kh_size(h)-1; - } +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); +} - mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), key); - mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), val); - return; +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 mrb_value -mrb_hash_dup(mrb_state *mrb, mrb_value hash) +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) { - struct RHash* ret; - khash_t(ht) *h, *ret_h; - khiter_t k, ret_k; + mrb_free(mrb, ht_ea(h)); + mrb_free(mrb, h_ht(h)); +} - h = RHASH_TBL(hash); - ret = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); - ret->ht = kh_init(ht, mrb); +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); +} - if (kh_size(h) > 0) { - ret_h = ret->ht; +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); +} - for (k = kh_begin(h); k != kh_end(h); k++) { - if (kh_exist(h, k)) { - int ai = mrb_gc_arena_save(mrb); - ret_k = kh_put(ht, mrb, ret_h, KEY(kh_key(h, k))); - mrb_gc_arena_restore(mrb, ai); - kh_val(ret_h, ret_k) = kh_val(h, k); +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); +} + +static mrb_bool +ht_get(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value *valp) +{ + ib_find_by_key(mrb, h, key, it, { + *valp = ib_it_entry(it)->val; + return TRUE; + }); + return FALSE; +} + +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); +} + +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"); } + 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); + } + } + } + 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; +} + +static void +ht_shift(mrb_state *mrb, struct RHash *h, mrb_value *keyp, mrb_value *valp) +{ + 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; + }); + }); +} + +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; } + 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); + } + 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); +} - return mrb_obj_value(ret); +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; +} + +static struct RHash* +h_alloc(mrb_state *mrb) +{ + return MRB_OBJ_ALLOC(mrb, MRB_TT_HASH, mrb->hash_class); +} + +static void +h_init(struct RHash *h) +{ + ar_init(h, 0, NULL, 0, 0); +} + +static void +h_free_table(mrb_state *mrb, struct RHash *h) +{ + (h_ar_p(h) ? ar_free : ht_free)(mrb, h); +} + +static void +h_clear(mrb_state *mrb, struct RHash *h) +{ + h_free_table(mrb, h); + h_init(h); +} + +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); +} + +static void +h_set(mrb_state *mrb, struct RHash *h, mrb_value key, mrb_value val) +{ + (h_ar_p(h) ? ar_set : ht_set)(mrb, h, key, val); +} + +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); +} + +/* 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 void +h_rehash(mrb_state *mrb, struct RHash *h) +{ + /* + * ==== 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)); + } +} + +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); + }); +} + +size_t +mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *h) +{ + return h_size(h) * 2; +} + +void +mrb_gc_free_hash(mrb_state *mrb, struct RHash *h) +{ + h_free_table(mrb, h); +} + +size_t +mrb_hash_memsize(mrb_value self) +{ + 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)))); +} + +/* 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) +{ + h_each(h, entry, { + if (func(mrb, entry->key, entry->val, data) != 0) return; + }); } MRB_API mrb_value -mrb_check_hash_type(mrb_state *mrb, mrb_value hash) +mrb_hash_new(mrb_state *mrb) { - return mrb_check_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash"); + struct RHash *h = h_alloc(mrb); + return mrb_obj_value(h); } -MRB_API khash_t(ht)* -mrb_hash_tbl(mrb_state *mrb, mrb_value hash) +/* + * 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) { - khash_t(ht) *h = RHASH_TBL(hash); + 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); + } +} - if (!h) { - return RHASH_TBL(hash) = kh_init(ht, mrb); +static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash); + +static void +hash_modify(mrb_state *mrb, mrb_value hash) +{ + mrb_check_frozen(mrb, mrb_hash_ptr(hash)); +} + +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_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key); + } + else { + return RHASH_IFNONE(hash); + } } - return h; + return mrb_nil_value(); } static void -mrb_hash_modify(mrb_state *mrb, mrb_value hash) +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)); + } + else { + mrb_iv_remove(mrb, self, name); + } + h->flags &= ~mask; + h->flags |= orig_h->flags & mask; +} + +static mrb_value +mrb_hash_init_copy(mrb_state *mrb, mrb_value self) +{ + 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) +{ + 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) { - mrb_hash_tbl(mrb, hash); + 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 +mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key) +{ + mrb_value val; + mrb_sym mid; + + if (h_get(mrb, mrb_hash_ptr(hash), key, &val)) { + return val; + } + + mid = MRB_SYM(default); + if (mrb_func_basic_p(mrb, hash, mid, mrb_hash_default)) { + return hash_default(mrb, hash, key); + } + /* xxx mrb_funcall_tailcall(mrb, hash, "default", 1, key); */ + return mrb_funcall_argv(mrb, hash, mid, 1, &key); +} + +MRB_API mrb_value +mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def) +{ + mrb_value val; + + if (h_get(mrb, mrb_hash_ptr(hash), key, &val)) { + return val; + } + /* not found */ + return def; +} + +MRB_API void +mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) +{ + 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 */ @@ -293,22 +1265,22 @@ mrb_hash_modify(mrb_state *mrb, mrb_value hash) * default value. It is the block's responsibility to store the value * in the hash if required. * - * h = Hash.new("Go Fish") - * h["a"] = 100 - * h["b"] = 200 - * h["a"] #=> 100 - * h["c"] #=> "Go Fish" - * # The following alters the single default object - * h["c"].upcase! #=> "GO FISH" - * h["d"] #=> "GO FISH" - * h.keys #=> ["a", "b"] - * - * # While this creates a new default object each time - * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" } - * h["c"] #=> "Go Fish: c" - * h["c"].upcase! #=> "GO FISH: C" - * h["d"] #=> "Go Fish: d" - * h.keys #=> ["c", "d"] + * h = Hash.new("Go Fish") + * h["a"] = 100 + * h["b"] = 200 + * h["a"] #=> 100 + * h["c"] #=> "Go Fish" + * # The following alters the single default object + * h["c"].upcase! #=> "GO FISH" + * h["d"] #=> "GO FISH" + * h.keys #=> ["a", "b"] + * + * # While this creates a new default object each time + * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" } + * h["c"] #=> "Go Fish: c" + * h["c"].upcase! #=> "GO FISH: C" + * h["d"] #=> "Go Fish: d" + * h.keys #=> ["c", "d"] * */ @@ -320,15 +1292,18 @@ 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; } - mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); + if (!mrb_nil_p(ifnone)) { + RHASH(hash)->flags |= MRB_HASH_DEFAULT; + mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone); + } return hash; } @@ -349,9 +1324,8 @@ 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); } @@ -384,13 +1358,16 @@ mrb_hash_default(mrb_state *mrb, mrb_value hash) mrb_bool given; mrb_get_args(mrb, "|o?", &key, &given); - if (MRB_RHASH_PROCDEFAULT_P(hash)) { - if (!given) return mrb_nil_value(); - return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); - } - else { - return RHASH_IFNONE(hash); + if (MRB_RHASH_DEFAULT_P(hash)) { + if (MRB_RHASH_PROCDEFAULT_P(hash)) { + if (!given) return mrb_nil_value(); + return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key); + } + else { + return RHASH_IFNONE(hash); + } } + return mrb_nil_value(); } /* 15.2.13.4.6 */ @@ -417,13 +1394,17 @@ 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_get_args(mrb, "o", &ifnone); - mrb_hash_modify(mrb, hash); - mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); - RHASH(hash)->flags &= ~(MRB_HASH_PROC_DEFAULT); + mrb_value ifnone = mrb_get_arg1(mrb); + 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; + } + else { + RHASH(hash)->flags &= ~MRB_HASH_DEFAULT; + } return ifnone; } @@ -442,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) { @@ -468,12 +1448,18 @@ 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); - RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; + 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; + } + else { + RHASH(hash)->flags &= ~MRB_HASH_DEFAULT; + RHASH(hash)->flags &= ~MRB_HASH_PROC_DEFAULT; + } return ifnone; } @@ -481,53 +1467,21 @@ 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) { - khash_t(ht) *h = RHASH_TBL(hash); - khiter_t k; - mrb_value delVal; - mrb_int n; - - if (h) { - k = kh_get(ht, mrb, h, key); - if (k != kh_end(h)) { - delVal = kh_value(h, k).v; - n = kh_value(h, k).n; - kh_del(ht, mrb, h, k); - for (k = kh_begin(h); k != kh_end(h); k++) { - if (!kh_exist(h, k)) continue; - if (kh_value(h, k).n > n) kh_value(h, k).n--; - } - return delVal; - } + mrb_value del_val; + + hash_modify(mrb, hash); + if (h_delete(mrb, mrb_hash_ptr(hash), key, &del_val)) { + return del_val; } /* not found */ return mrb_nil_value(); } -/* 15.2.13.4.8 */ -/* - * call-seq: - * hsh.delete(key) -> value - * hsh.delete(key) {| key | block } -> value - * - * Deletes and returns a key-value pair from <i>hsh</i> whose key is - * equal to <i>key</i>. If the key is not found, returns the - * <em>default value</em>. If the optional code block is given and the - * key is not found, pass in the key and return the result of - * <i>block</i>. - * - * h = { "a" => 100, "b" => 200 } - * h.delete("a") #=> 100 - * h.delete("z") #=> nil - * h.delete("z") { |el| "#{el} not found" } #=> "z not found" - * - */ static mrb_value mrb_hash_delete(mrb_state *mrb, mrb_value self) { - mrb_value key; - - mrb_get_args(mrb, "o", &key); + mrb_value key = mrb_get_arg1(mrb); return mrb_hash_delete_key(mrb, self, key); } @@ -540,37 +1494,26 @@ mrb_hash_delete(mrb_state *mrb, mrb_value self) * two-item array <code>[</code> <i>key, value</i> <code>]</code>, or * the hash's default value if the hash is empty. * - * h = { 1 => "a", 2 => "b", 3 => "c" } - * h.shift #=> [1, "a"] - * h #=> {2=>"b", 3=>"c"} + * h = { 1 => "a", 2 => "b", 3 => "c" } + * h.shift #=> [1, "a"] + * h #=> {2=>"b", 3=>"c"} */ static mrb_value mrb_hash_shift(mrb_state *mrb, mrb_value hash) { - khash_t(ht) *h = RHASH_TBL(hash); - khiter_t k; - mrb_value delKey, delVal; - - mrb_hash_modify(mrb, hash); - if (h && kh_size(h) > 0) { - for (k = kh_begin(h); k != kh_end(h); k++) { - if (!kh_exist(h, k)) continue; - - delKey = kh_key(h, k); - mrb_gc_protect(mrb, delKey); - delVal = mrb_hash_delete_key(mrb, hash, delKey); - mrb_gc_protect(mrb, delVal); + struct RHash *h = mrb_hash_ptr(hash); - return mrb_assoc_new(mrb, delKey, delVal); - } - } - - if (MRB_RHASH_PROCDEFAULT_P(hash)) { - return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, mrb_nil_value()); + hash_modify(mrb, hash); + if (h_size(h) == 0) { + return mrb_nil_value(); } else { - return RHASH_IFNONE(hash); + mrb_value 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); } } @@ -579,19 +1522,18 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash) * call-seq: * hsh.clear -> hsh * - * Removes all key-value pairs from <i>hsh</i>. + * Removes all key-value pairs from `hsh`. * - * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200} - * h.clear #=> {} + * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200} + * h.clear #=> {} * */ MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash) { - khash_t(ht) *h = RHASH_TBL(hash); - - if (h) kh_clear(ht, mrb, h); + hash_modify(mrb, hash); + h_clear(mrb, mrb_hash_ptr(hash)); return hash; } @@ -608,10 +1550,10 @@ mrb_hash_clear(mrb_state *mrb, mrb_value hash) * use as a key (a <code>String</code> passed as a key will be * duplicated and frozen). * - * h = { "a" => 100, "b" => 200 } - * h["a"] = 9 - * h["c"] = 4 - * h #=> {"a"=>9, "b"=>200, "c"=>4} + * h = { "a" => 100, "b" => 200 } + * h["a"] = 9 + * h["c"] = 4 + * h #=> {"a"=>9, "b"=>200, "c"=>4} * */ static mrb_value @@ -624,12 +1566,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. * @@ -641,10 +1589,14 @@ mrb_hash_aset(mrb_state *mrb, mrb_value self) static mrb_value mrb_hash_size_m(mrb_state *mrb, mrb_value self) { - khash_t(ht) *h = RHASH_TBL(self); + mrb_int size = mrb_hash_size(mrb, self); + return mrb_int_value(mrb, size); +} - if (!h) return mrb_fixnum_value(0); - return mrb_fixnum_value(kh_size(h)); +MRB_API mrb_bool +mrb_hash_empty_p(mrb_state *mrb, mrb_value self) +{ + return h_size(mrb_hash_ptr(self)) == 0; } /* 15.2.13.4.12 */ @@ -657,27 +1609,10 @@ mrb_hash_size_m(mrb_state *mrb, mrb_value self) * {}.empty? #=> true * */ -MRB_API mrb_value -mrb_hash_empty_p(mrb_state *mrb, mrb_value self) -{ - khash_t(ht) *h = RHASH_TBL(self); - - if (h) return mrb_bool_value(kh_size(h) == 0); - return mrb_true_value(); -} - -/* 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) +mrb_hash_empty_m(mrb_state *mrb, mrb_value self) { - return hash; + return mrb_bool_value(mrb_hash_empty_p(mrb, self)); } /* 15.2.13.4.19 */ @@ -696,23 +1631,11 @@ mrb_hash_to_hash(mrb_state *mrb, mrb_value hash) MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash) { - khash_t(ht) *h = RHASH_TBL(hash); - khiter_t k; - mrb_value ary; - mrb_value *p; - - if (!h || kh_size(h) == 0) return mrb_ary_new(mrb); - ary = mrb_ary_new_capa(mrb, kh_size(h)); - mrb_ary_set(mrb, ary, kh_size(h)-1, mrb_nil_value()); - p = mrb_ary_ptr(ary)->ptr; - for (k = kh_begin(h); k != kh_end(h); k++) { - if (kh_exist(h, k)) { - mrb_value kv = kh_key(h, k); - mrb_hash_value hv = kh_value(h, k); - - p[hv.n] = kv; - } - } + 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; } @@ -729,22 +1652,14 @@ mrb_hash_keys(mrb_state *mrb, mrb_value hash) * */ -static mrb_value +MRB_API mrb_value mrb_hash_values(mrb_state *mrb, mrb_value hash) { - khash_t(ht) *h = RHASH_TBL(hash); - khiter_t k; - mrb_value ary; - - if (!h) return mrb_ary_new(mrb); - ary = mrb_ary_new_capa(mrb, kh_size(h)); - for (k = kh_begin(h); k != kh_end(h); k++) { - if (kh_exist(h, k)) { - mrb_hash_value hv = kh_value(h, k); - - mrb_ary_set(mrb, ary, hv.n, hv.v); - } - } + 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; } @@ -767,21 +1682,21 @@ 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) +{ + 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; - khash_t(ht) *h; - khiter_t k; + mrb_value key = mrb_get_arg1(mrb); + mrb_bool key_p; - mrb_get_args(mrb, "o", &key); - - h = RHASH_TBL(hash); - if (h) { - k = kh_get(ht, mrb, h, key); - return mrb_bool_value(k != kh_end(h)); - } - return mrb_false_value(); + key_p = mrb_hash_key_p(mrb, hash, key); + return mrb_bool_value(key_p); } /* 15.2.13.4.14 */ @@ -802,23 +1717,60 @@ mrb_hash_has_key(mrb_state *mrb, mrb_value hash) static mrb_value mrb_hash_has_value(mrb_state *mrb, mrb_value hash) { - mrb_value val; - khash_t(ht) *h; - khiter_t k; - - mrb_get_args(mrb, "o", &val); - h = RHASH_TBL(hash); + 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(); +} - if (h) { - for (k = kh_begin(h); k != kh_end(h); k++) { - if (!kh_exist(h, k)) continue; +MRB_API void +mrb_hash_merge(mrb_state *mrb, mrb_value hash1, mrb_value hash2) +{ + 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); + }); +} - if (mrb_equal(mrb, kh_value(h, k).v, val)) { - return mrb_true_value(); - } - } - } - return mrb_false_value(); +/* + * call-seq: + * hsh.rehash -> hsh + * + * Rebuilds the hash based on the current hash values for each key. If + * values of key objects have changed since they were inserted, this + * method will reindex <i>hsh</i>. + * + * 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) +{ + h_rehash(mrb, mrb_hash_ptr(self)); + return self; } void @@ -826,32 +1778,32 @@ mrb_init_hash(mrb_state *mrb) { struct RClass *h; - h = mrb->hash_class = mrb_define_class(mrb, "Hash", mrb->object_class); /* 15.2.13 */ + 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, "[]", 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 */ mrb_define_method(mrb, h, "__delete", mrb_hash_delete, MRB_ARGS_REQ(1)); /* core of 15.2.13.4.8 */ - mrb_define_method(mrb, h, "empty?", mrb_hash_empty_p, MRB_ARGS_NONE()); /* 15.2.13.4.12 */ + mrb_define_method(mrb, h, "empty?", mrb_hash_empty_m, MRB_ARGS_NONE()); /* 15.2.13.4.12 */ 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, "dup", mrb_hash_dup, MRB_ARGS_NONE()); 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, "to_hash", mrb_hash_to_hash, MRB_ARGS_NONE()); /* 15.2.13.4.29 (x)*/ + mrb_define_method(mrb, h, "rehash", mrb_hash_rehash, MRB_ARGS_NONE()); } diff --git a/src/init.c b/src/init.c index 955d6e3a1..afd69975a 100644 --- a/src/init.c +++ b/src/init.c @@ -4,7 +4,7 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" +#include <mruby.h> void mrb_init_symtbl(mrb_state*); void mrb_init_class(mrb_state*); @@ -13,9 +13,9 @@ void mrb_init_kernel(mrb_state*); void mrb_init_comparable(mrb_state*); void mrb_init_enumerable(mrb_state*); void mrb_init_symbol(mrb_state*); +void mrb_init_string(mrb_state*); void mrb_init_exception(mrb_state*); void mrb_init_proc(mrb_state*); -void mrb_init_string(mrb_state*); void mrb_init_array(mrb_state*); void mrb_init_hash(mrb_state*); void mrb_init_numeric(mrb_state*); @@ -38,9 +38,9 @@ mrb_init_core(mrb_state *mrb) mrb_init_enumerable(mrb); DONE; mrb_init_symbol(mrb); DONE; + mrb_init_string(mrb); DONE; mrb_init_exception(mrb); DONE; mrb_init_proc(mrb); DONE; - mrb_init_string(mrb); DONE; mrb_init_array(mrb); DONE; mrb_init_hash(mrb); DONE; mrb_init_numeric(mrb); DONE; diff --git a/src/kernel.c b/src/kernel.c index 22fe40218..615b68ac3 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -4,35 +4,37 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/proc.h" -#include "mruby/string.h" -#include "mruby/variable.h" -#include "mruby/error.h" +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/hash.h> +#include <mruby/class.h> +#include <mruby/proc.h> +#include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/error.h> +#include <mruby/istruct.h> +#include <mruby/presym.h> -typedef enum { - NOEX_PUBLIC = 0x00, - NOEX_NOSUPER = 0x01, - NOEX_PRIVATE = 0x02, - NOEX_PROTECTED = 0x04, - NOEX_MASK = 0x06, - NOEX_BASIC = 0x08, - NOEX_UNDEF = NOEX_NOSUPER, - NOEX_MODFUNC = 0x12, - NOEX_SUPER = 0x20, - NOEX_VCALL = 0x40, - NOEX_RESPONDS = 0x80 -} mrb_method_flag_t; +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); + const struct RProc *p; + + if (MRB_METHOD_UNDEF_P(m)) return FALSE; + if (MRB_METHOD_FUNC_P(m)) + return MRB_METHOD_FUNC(m) == func; + p = MRB_METHOD_PROC(m); + if (MRB_PROC_CFUNC_P(p) && (MRB_PROC_CFUNC(p) == func)) + return TRUE; + return FALSE; +} static mrb_bool mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj) { - struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mrb_intern_lit(mrb, "to_s")); - if (MRB_PROC_CFUNC_P(me) && (me->body.func == mrb_any_to_s)) - return TRUE; - return FALSE; + return mrb_func_basic_p(mrb, obj, MRB_SYM(to_s), mrb_any_to_s); } /* 15.3.1.3.17 */ @@ -52,61 +54,12 @@ 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); } -/* 15.3.1.3.1 */ -/* 15.3.1.3.10 */ -/* 15.3.1.3.11 */ -/* - * call-seq: - * obj == other -> true or false - * obj.equal?(other) -> true or false - * obj.eql?(other) -> true or false - * - * Equality---At the <code>Object</code> level, <code>==</code> returns - * <code>true</code> only if <i>obj</i> and <i>other</i> are the - * same object. Typically, this method is overridden in descendant - * classes to provide class-specific meaning. - * - * Unlike <code>==</code>, the <code>equal?</code> method should never be - * overridden by subclasses: it is used to determine object identity - * (that is, <code>a.equal?(b)</code> iff <code>a</code> is the same - * object as <code>b</code>). - * - * The <code>eql?</code> method returns <code>true</code> if - * <i>obj</i> and <i>anObject</i> have the same value. Used by - * <code>Hash</code> to test members for equality. For objects of - * class <code>Object</code>, <code>eql?</code> is synonymous with - * <code>==</code>. Subclasses normally continue this tradition, but - * there are exceptions. <code>Numeric</code> types, for example, - * perform type conversion across <code>==</code>, but not across - * <code>eql?</code>, so: - * - * 1 == 1.0 #=> true - * 1.eql? 1.0 #=> false - */ -static mrb_value -mrb_obj_equal_m(mrb_state *mrb, mrb_value self) -{ - mrb_value arg; - - 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)); -} - /* 15.3.1.3.2 */ /* * call-seq: @@ -119,9 +72,8 @@ mrb_obj_not_equal_m(mrb_state *mrb, mrb_value self) 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)); } @@ -142,10 +94,22 @@ mrb_equal_m(mrb_state *mrb, mrb_value self) * <code>:name</code> notation, which returns the symbol id of * <code>name</code>. Replaces the deprecated <code>Object#id</code>. */ -static mrb_value +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 */ @@ -175,30 +139,65 @@ mrb_obj_id_m(mrb_state *mrb, mrb_value self) static mrb_value mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) { - mrb_callinfo *ci = mrb->c->ci; + mrb_callinfo *ci = &mrb->c->ci[-1]; + mrb_callinfo *cibase = mrb->c->cibase; mrb_value *bp; - mrb_bool given_p; + int bidx; + struct REnv *e = NULL; + const struct RProc *p; - bp = ci->stackent + 1; - ci--; - if (ci <= mrb->c->cibase) { - given_p = FALSE; + if (ci <= cibase) { + /* toplevel does not have block */ + return mrb_false_value(); + } + p = ci->proc; + /* 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) { + /* 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 ((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(); + bidx = env_bidx(e); + /* bidx may be useless (e.g. define_method) */ + if (bidx < 0) return mrb_false_value(); + bp = &e->stack[bidx]; } else { - /* block_given? called within block; check upper scope */ - if (ci->proc->env && ci->proc->env->stack) { - given_p = !(ci->proc->env->stack == mrb->c->stbase || - mrb_nil_p(ci->proc->env->stack[1])); + bp = ci->stack+1; + if (ci->argc >= 0) { + bp += ci->argc; } else { - if (ci->argc > 0) { - bp += ci->argc; - } - given_p = !mrb_nil_p(*bp); + bp++; } } - - return mrb_bool_value(given_p); + block_given: + if (mrb_nil_p(*bp)) + return mrb_false_value(); + return mrb_true_value(); } /* 15.3.1.3.7 */ @@ -210,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 @@ -219,164 +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); - - if ((mrb_type(obj) == MRB_TT_CLASS) || - (mrb_type(obj) == MRB_TT_SCLASS)) { /* BUILTIN_TYPE(obj) == T_CLASS */ - clone->c = clone; - } - else { - clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); - } - - 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); - dc->mt = kh_copy(mt, mrb, sc->mt); - dc->super = sc->super; -} - -static void -init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj) -{ - switch (mrb_type(obj)) { - case MRB_TT_CLASS: - case MRB_TT_MODULE: - copy_class(mrb, dest, obj); - /* fall through */ - 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; - - 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); - } - p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self)); - p->c = mrb_singleton_class_clone(mrb, self); - clone = mrb_obj_value(p); - init_copy(mrb, clone, self); - - 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); - } - 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; } @@ -409,37 +264,55 @@ 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); } +MRB_API mrb_value +mrb_obj_freeze(mrb_state *mrb, mrb_value self) +{ + 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; +} + +static mrb_value +mrb_obj_frozen(mrb_state *mrb, mrb_value self) +{ + return mrb_bool_value(mrb_immediate_p(self) || mrb_frozen_p(mrb_basic_ptr(self))); +} + /* 15.3.1.3.15 */ /* * 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"); @@ -447,10 +320,6 @@ mrb_obj_init_copy(mrb_state *mrb, mrb_value self) return self; } - -/* implementation of instance_eval */ -mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value); - MRB_API mrb_bool mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c) { @@ -469,101 +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; - - mrb_get_args(mrb, "C", &arg); - - return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg))); -} - -/* 15.3.1.3.20 */ -/* - * call-seq: - * obj.instance_variable_defined?(symbol) -> true or false - * - * Returns <code>true</code> if the given instance variable is - * defined in <i>obj</i>. - * - * class Fred - * def initialize(p1, p2) - * @a, @b = p1, p2 - * end - * end - * fred = Fred.new('cat', 99) - * fred.instance_variable_defined?(:@a) #=> true - * fred.instance_variable_defined?("@b") #=> true - * fred.instance_variable_defined?("@c") #=> false - */ -static mrb_value -mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self) -{ - mrb_sym sym; + struct RClass *c; - mrb_get_args(mrb, "n", &sym); - mrb_iv_check(mrb, sym); - return mrb_bool_value(mrb_iv_defined(mrb, self, sym)); -} + mrb_get_args(mrb, "c", &c); -/* 15.3.1.3.21 */ -/* - * call-seq: - * obj.instance_variable_get(symbol) -> obj - * - * Returns the value of the given instance variable, or nil if the - * instance variable is not set. The <code>@</code> part of the - * variable name should be included for regular instance - * variables. Throws a <code>NameError</code> exception if the - * supplied symbol is not valid as an instance variable name. - * - * class Fred - * def initialize(p1, p2) - * @a, @b = p1, p2 - * end - * end - * fred = Fred.new('cat', 99) - * fred.instance_variable_get(:@a) #=> "cat" - * fred.instance_variable_get("@b") #=> 99 - */ -static mrb_value -mrb_obj_ivar_get(mrb_state *mrb, mrb_value self) -{ - mrb_sym iv_name; - - mrb_get_args(mrb, "n", &iv_name); - mrb_iv_check(mrb, iv_name); - return mrb_iv_get(mrb, self, iv_name); -} - -/* 15.3.1.3.22 */ -/* - * call-seq: - * obj.instance_variable_set(symbol, obj) -> obj - * - * Sets the instance variable names by <i>symbol</i> to - * <i>object</i>, thereby frustrating the efforts of the class's - * author to attempt to provide proper encapsulation. The variable - * did not have to exist prior to this call. - * - * class Fred - * def initialize(p1, p2) - * @a, @b = p1, p2 - * end - * end - * fred = Fred.new('cat', 99) - * fred.instance_variable_set(:@a, 'dog') #=> "dog" - * fred.instance_variable_set(:@c, 'cat') #=> "cat" - * fred.inspect #=> "#<Fred:0x401b3da8 @a=\"dog\", @b=99, @c=\"cat\">" - */ -static mrb_value -mrb_obj_ivar_set(mrb_state *mrb, mrb_value self) -{ - mrb_sym iv_name; - mrb_value val; - - mrb_get_args(mrb, "no", &iv_name, &val); - mrb_iv_check(mrb, iv_name); - mrb_iv_set(mrb, self, iv_name, val); - return val; + return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, c)); } /* 15.3.1.3.24 */ @@ -596,126 +375,11 @@ mrb_obj_ivar_set(mrb_state *mrb, mrb_value self) static mrb_value mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self) { - mrb_value arg; - - mrb_get_args(mrb, "C", &arg); - - return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg))); -} - -KHASH_DECLARE(st, mrb_sym, char, FALSE) -KHASH_DEFINE(st, mrb_sym, char, FALSE, kh_int_hash_func, kh_int_hash_equal) - -static void -method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set) -{ - khint_t i; - - khash_t(mt) *h = klass->mt; - if (!h) return; - for (i=0;i<kh_end(h);i++) { - if (kh_exist(h, i) && kh_value(h, i)) { - kh_put(st, mrb, set, kh_key(h, i)); - } - } -} - -mrb_value -mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* klass, int obj) -{ - khint_t i; - mrb_value ary; - struct RClass* oldklass; - khash_t(st)* set = kh_init(st, mrb); - - oldklass = 0; - while (klass && (klass != oldklass)) { - method_entry_loop(mrb, klass, set); - if ((klass->tt == MRB_TT_ICLASS) || - (klass->tt == MRB_TT_SCLASS)) { - } - else { - if (!recur) break; - } - oldklass = klass; - klass = klass->super; - } - - ary = mrb_ary_new(mrb); - for (i=0;i<kh_end(set);i++) { - if (kh_exist(set, i)) { - mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i))); - } - } - kh_destroy(st, mrb, set); - - return ary; -} - -static mrb_value -mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj) -{ - khint_t i; - mrb_value ary; - struct RClass* klass; - khash_t(st)* set = kh_init(st, mrb); + struct RClass *c; - klass = mrb_class(mrb, obj); + mrb_get_args(mrb, "c", &c); - if (klass && (klass->tt == MRB_TT_SCLASS)) { - method_entry_loop(mrb, klass, set); - klass = klass->super; - } - if (recur) { - while (klass && ((klass->tt == MRB_TT_SCLASS) || (klass->tt == MRB_TT_ICLASS))) { - method_entry_loop(mrb, klass, set); - klass = klass->super; - } - } - - ary = mrb_ary_new(mrb); - for (i=0;i<kh_end(set);i++) { - if (kh_exist(set, i)) { - mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i))); - } - } - kh_destroy(st, mrb, set); - - return ary; -} - -static mrb_value -mrb_obj_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj, mrb_method_flag_t flag) -{ - if (recur) - return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0); - return mrb_obj_singleton_methods(mrb, recur, obj); -} -/* 15.3.1.3.31 */ -/* - * call-seq: - * obj.methods -> array - * - * Returns a list of the names of methods publicly accessible in - * <i>obj</i>. This will include all the methods accessible in - * <i>obj</i>'s ancestors. - * - * class Klass - * def kMethod() - * end - * end - * k = Klass.new - * k.methods[0..9] #=> [:kMethod, :respond_to?, :nil?, :is_a?, - * # :class, :instance_variable_set, - * # :methods, :extend, :__send__, :instance_eval] - * k.methods.length #=> 42 - */ -static mrb_value -mrb_obj_methods_m(mrb_state *mrb, mrb_value self) -{ - mrb_bool recur = TRUE; - mrb_get_args(mrb, "|b", &recur); - return mrb_obj_methods(mrb, recur, self, (mrb_method_flag_t)0); /* everything but private */ + return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, c)); } /* 15.3.1.3.32 */ @@ -732,57 +396,6 @@ mrb_false(mrb_state *mrb, mrb_value self) return mrb_false_value(); } -/* 15.3.1.3.36 */ -/* - * call-seq: - * obj.private_methods(all=true) -> array - * - * Returns the list of private methods accessible to <i>obj</i>. If - * the <i>all</i> parameter is set to <code>false</code>, only those methods - * in the receiver will be listed. - */ -static mrb_value -mrb_obj_private_methods(mrb_state *mrb, mrb_value self) -{ - mrb_bool recur = TRUE; - mrb_get_args(mrb, "|b", &recur); - return mrb_obj_methods(mrb, recur, self, NOEX_PRIVATE); /* private attribute not define */ -} - -/* 15.3.1.3.37 */ -/* - * call-seq: - * obj.protected_methods(all=true) -> array - * - * Returns the list of protected methods accessible to <i>obj</i>. If - * the <i>all</i> parameter is set to <code>false</code>, only those methods - * in the receiver will be listed. - */ -static mrb_value -mrb_obj_protected_methods(mrb_state *mrb, mrb_value self) -{ - mrb_bool recur = TRUE; - mrb_get_args(mrb, "|b", &recur); - return mrb_obj_methods(mrb, recur, self, NOEX_PROTECTED); /* protected attribute not define */ -} - -/* 15.3.1.3.38 */ -/* - * call-seq: - * obj.public_methods(all=true) -> array - * - * Returns the list of public methods accessible to <i>obj</i>. If - * the <i>all</i> parameter is set to <code>false</code>, only those methods - * in the receiver will be listed. - */ -static mrb_value -mrb_obj_public_methods(mrb_state *mrb, mrb_value self) -{ - mrb_bool recur = TRUE; - mrb_get_args(mrb, "|b", &recur); - return mrb_obj_methods(mrb, recur, self, NOEX_PUBLIC); /* public attribute not define */ -} - /* 15.3.1.2.12 */ /* 15.3.1.3.40 */ /* @@ -808,24 +421,23 @@ MRB_API mrb_value mrb_f_raise(mrb_state *mrb, mrb_value self) { mrb_value a[2], exc; - int argc; - + 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, ""); break; case 1: - a[1] = mrb_check_string_type(mrb, a[0]); - if (!mrb_nil_p(a[1])) { + if (mrb_string_p(a[0])) { + a[1] = a[0]; argc = 2; a[0] = mrb_obj_value(E_RUNTIME_ERROR); } /* fall through */ default: exc = mrb_make_exception(mrb, argc, a); - mrb_obj_iv_set(mrb, mrb_obj_ptr(exc), mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, mrb->c->ci->pc)); mrb_exc_raise(mrb, exc); break; } @@ -861,19 +473,73 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self) mrb_value val; mrb_get_args(mrb, "n", &sym); - mrb_iv_check(mrb, sym); + 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; } +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 '%n'", name); +} + +/* 15.3.1.3.30 */ +/* + * call-seq: + * obj.method_missing(symbol [, *args] ) -> result + * + * Invoked by Ruby when <i>obj</i> is sent a message it cannot handle. + * <i>symbol</i> is the symbol for the method called, and <i>args</i> + * are any arguments that were passed to it. By default, the interpreter + * raises an error when this method is called. However, it is possible + * to override the method to provide more dynamic behavior. + * If it is decided that a particular method should not be handled, then + * <i>super</i> should be called, so that ancestors can pick up the + * missing method. + * The example below creates + * a class <code>Roman</code>, which responds to methods with names + * consisting of roman numerals, returning the corresponding integer + * values. + * + * class Roman + * def romanToInt(str) + * # ... + * end + * def method_missing(methId) + * str = methId.to_s + * romanToInt(str) + * end + * end + * + * r = Roman.new + * r.iv #=> 4 + * r.xxiii #=> 23 + * r.mm #=> 2000 + */ +mrb_value +mrb_obj_missing(mrb_state *mrb, mrb_value mod) +{ + mrb_sym name; + 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(); +} + 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: @@ -893,117 +559,50 @@ 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_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, mid); - 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]; - args[0] = mid; + mrb_value args[2], v; + args[0] = mrb_symbol_value(id); args[1] = mrb_bool_value(priv); - return mrb_funcall_argv(mrb, self, rtm_id, 2, args); + v = mrb_funcall_argv(mrb, self, rtm_id, 2, args); + return mrb_bool_value(mrb_bool(v)); } } return mrb_bool_value(respond_to_p); } -/* 15.3.1.3.45 */ -/* - * call-seq: - * obj.singleton_methods(all=true) -> array - * - * Returns an array of the names of singleton methods for <i>obj</i>. - * If the optional <i>all</i> parameter is true, the list will include - * methods in modules included in <i>obj</i>. - * Only public and protected singleton methods are returned. - * - * module Other - * def three() end - * end - * - * class Single - * def Single.four() end - * end - * - * a = Single.new - * - * def a.one() - * end - * - * class << a - * include Other - * def two() - * end - * end - * - * Single.singleton_methods #=> [:four] - * a.singleton_methods(false) #=> [:two, :one] - * a.singleton_methods #=> [:two, :one, :three] - */ -static mrb_value -mrb_obj_singleton_methods_m(mrb_state *mrb, mrb_value self) -{ - mrb_bool recur = TRUE; - mrb_get_args(mrb, "|b", &recur); - return mrb_obj_singleton_methods(mrb, recur, self); -} - -static mrb_value -mod_define_singleton_method(mrb_state *mrb, mrb_value self) -{ - struct RProc *p; - mrb_sym mid; - mrb_value blk = mrb_nil_value(); - - mrb_get_args(mrb, "n&", &mid, &blk); - 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); - mrb_proc_copy(p, mrb_proc_ptr(blk)); - p->flags |= MRB_PROC_STRICT; - mrb_define_method_raw(mrb, mrb_class_ptr(mrb_singleton_class(mrb, self)), mid, p); - return mrb_symbol_value(mid); -} - 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); + 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); @@ -1012,107 +611,47 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self) return mrb_false_value(); } -static mrb_value -mrb_local_variables(mrb_state *mrb, mrb_value self) -{ - mrb_value ret; - struct RProc *proc; - struct mrb_irep *irep; - size_t i; - - proc = mrb->c->ci[-1].proc; - - if (MRB_PROC_CFUNC_P(proc)) { - return mrb_ary_new(mrb); - } - - irep = proc->body.irep; - if (!irep->lv) { - return mrb_ary_new(mrb); - } - ret = mrb_ary_new_capa(mrb, irep->nlocals - 1); - for (i = 0; i + 1 < irep->nlocals; ++i) { - if (irep->lv[i].name) { - mrb_ary_push(mrb, ret, mrb_symbol_value(irep->lv[i].name)); - } - } - if (proc->env) { - struct REnv *e = proc->env; - - while (e) { - if (!MRB_PROC_CFUNC_P(mrb->c->cibase[e->cioff].proc)) { - irep = mrb->c->cibase[e->cioff].proc->body.irep; - if (irep->lv) { - for (i = 0; i + 1 < irep->nlocals; ++i) { - if (irep->lv[i].name) { - mrb_ary_push(mrb, ret, mrb_symbol_value(irep->lv[i].name)); - } - } - } - } - e = (struct REnv*)e->c; - } - } - - return ret; -} +mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value); void mrb_init_kernel(mrb_state *mrb) { struct RClass *krn; - krn = mrb->kernel_module = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */ + mrb->kernel_module = krn = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */ mrb_define_class_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.2 */ - mrb_define_class_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.2.4 */ mrb_define_class_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.5 */ - mrb_define_class_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.2.7 */ ; /* 15.3.1.2.11 */ mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_OPT(2)); /* 15.3.1.2.12 */ - mrb_define_method(mrb, krn, "singleton_class", mrb_singleton_class, MRB_ARGS_NONE()); - mrb_define_method(mrb, krn, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */ - mrb_define_method(mrb, krn, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1)); mrb_define_method(mrb, krn, "===", mrb_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.2 */ - mrb_define_method(mrb, krn, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.3 */ - mrb_define_method(mrb, krn, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.4 */ mrb_define_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.6 */ mrb_define_method(mrb, krn, "class", mrb_obj_class_m, MRB_ARGS_NONE()); /* 15.3.1.3.7 */ 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, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 */ + 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, "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 */ - mrb_define_method(mrb, krn, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */ mrb_define_method(mrb, krn, "instance_of?", obj_is_instance_of, MRB_ARGS_REQ(1)); /* 15.3.1.3.19 */ - mrb_define_method(mrb, krn, "instance_variable_defined?", mrb_obj_ivar_defined, MRB_ARGS_REQ(1)); /* 15.3.1.3.20 */ - mrb_define_method(mrb, krn, "instance_variable_get", mrb_obj_ivar_get, MRB_ARGS_REQ(1)); /* 15.3.1.3.21 */ - mrb_define_method(mrb, krn, "instance_variable_set", mrb_obj_ivar_set, MRB_ARGS_REQ(2)); /* 15.3.1.3.22 */ - mrb_define_method(mrb, krn, "instance_variables", mrb_obj_instance_variables, MRB_ARGS_NONE()); /* 15.3.1.3.23 */ + 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 */ - mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 */ - mrb_define_method(mrb, krn, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */ + mrb_define_method(mrb, krn, "method_missing", mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */ 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, "private_methods", mrb_obj_private_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.36 */ - mrb_define_method(mrb, krn, "protected_methods", mrb_obj_protected_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.37 */ - mrb_define_method(mrb, krn, "public_methods", mrb_obj_public_methods, MRB_ARGS_OPT(1)); /* 15.3.1.3.38 */ 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, "send", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.44 */ - mrb_define_method(mrb, krn, "singleton_methods", mrb_obj_singleton_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.45 */ - mrb_define_method(mrb, krn, "define_singleton_method", mod_define_singleton_method, MRB_ARGS_ANY()); + 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, "__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_alias_method(mrb, mrb->module_class, mrb_intern_lit(mrb, "dup"), mrb_intern_lit(mrb, "clone")); } diff --git a/src/keywords b/src/keywords deleted file mode 100644 index 9cb86608c..000000000 --- a/src/keywords +++ /dev/null @@ -1,50 +0,0 @@ -%{ -struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; -const struct kwtable *mrb_reserved_word(const char *, unsigned int); -static const struct kwtable *reserved_word(const char *, unsigned int); -#define mrb_reserved_word(str, len) reserved_word(str, len) -%} - -struct kwtable; -%% -__ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END -__FILE__, {keyword__FILE__, keyword__FILE__}, EXPR_END -__LINE__, {keyword__LINE__, keyword__LINE__}, EXPR_END -BEGIN, {keyword_BEGIN, keyword_BEGIN}, EXPR_END -END, {keyword_END, keyword_END}, EXPR_END -alias, {keyword_alias, keyword_alias}, EXPR_FNAME -and, {keyword_and, keyword_and}, EXPR_VALUE -begin, {keyword_begin, keyword_begin}, EXPR_BEG -break, {keyword_break, keyword_break}, EXPR_MID -case, {keyword_case, keyword_case}, EXPR_VALUE -class, {keyword_class, keyword_class}, EXPR_CLASS -def, {keyword_def, keyword_def}, EXPR_FNAME -do, {keyword_do, keyword_do}, EXPR_BEG -else, {keyword_else, keyword_else}, EXPR_BEG -elsif, {keyword_elsif, keyword_elsif}, EXPR_VALUE -end, {keyword_end, keyword_end}, EXPR_END -ensure, {keyword_ensure, keyword_ensure}, EXPR_BEG -false, {keyword_false, keyword_false}, EXPR_END -for, {keyword_for, keyword_for}, EXPR_VALUE -if, {keyword_if, modifier_if}, EXPR_VALUE -in, {keyword_in, keyword_in}, EXPR_VALUE -module, {keyword_module, keyword_module}, EXPR_VALUE -next, {keyword_next, keyword_next}, EXPR_MID -nil, {keyword_nil, keyword_nil}, EXPR_END -not, {keyword_not, keyword_not}, EXPR_ARG -or, {keyword_or, keyword_or}, EXPR_VALUE -redo, {keyword_redo, keyword_redo}, EXPR_END -rescue, {keyword_rescue, modifier_rescue}, EXPR_MID -retry, {keyword_retry, keyword_retry}, EXPR_END -return, {keyword_return, keyword_return}, EXPR_MID -self, {keyword_self, keyword_self}, EXPR_END -super, {keyword_super, keyword_super}, EXPR_ARG -then, {keyword_then, keyword_then}, EXPR_BEG -true, {keyword_true, keyword_true}, EXPR_END -undef, {keyword_undef, keyword_undef}, EXPR_FNAME -unless, {keyword_unless, modifier_unless}, EXPR_VALUE -until, {keyword_until, modifier_until}, EXPR_VALUE -when, {keyword_when, keyword_when}, EXPR_VALUE -while, {keyword_while, modifier_while}, EXPR_VALUE -yield, {keyword_yield, keyword_yield}, EXPR_ARG -%% diff --git a/src/lex.def b/src/lex.def deleted file mode 100644 index ea456a843..000000000 --- a/src/lex.def +++ /dev/null @@ -1,212 +0,0 @@ -/* ANSI-C code produced by gperf version 3.0.3 */ -/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' src/keywords */ - -#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ - && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ - && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ - && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ - && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ - && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ - && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ - && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ - && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ - && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ - && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ - && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ - && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ - && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ - && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ - && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ - && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ - && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ - && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ - && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ - && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ - && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ - && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) -/* The character set is not based on ISO-646. */ -#error "gperf generated tables don't work with this execution character set. Please report a bug to <[email protected]>." -#endif - -#line 1 "src/keywords" - -struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; -const struct kwtable *mrb_reserved_word(const char *, unsigned int); -static const struct kwtable *reserved_word(const char *, unsigned int); -#define mrb_reserved_word(str, len) reserved_word(str, len) -#line 8 "src/keywords" -struct kwtable; - -#define TOTAL_KEYWORDS 40 -#define MIN_WORD_LENGTH 2 -#define MAX_WORD_LENGTH 12 -#define MIN_HASH_VALUE 8 -#define MAX_HASH_VALUE 50 -/* maximum key range = 43, duplicates = 0 */ - -#ifdef __GNUC__ -__inline -#else -#ifdef __cplusplus -inline -#endif -#endif -static unsigned int -hash (register const char *str, register unsigned int len) -{ - static const unsigned char asso_values[] = - { - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 14, 51, 16, 8, - 11, 13, 51, 51, 51, 51, 10, 51, 13, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 11, 51, 13, 1, 26, - 4, 1, 8, 28, 51, 23, 51, 1, 1, 27, - 5, 19, 21, 51, 8, 3, 3, 11, 51, 21, - 24, 16, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51 - }; - register int hval = len; - - switch (hval) - { - default: - hval += asso_values[(unsigned char)str[2]]; - /*FALLTHROUGH*/ - case 2: - case 1: - hval += asso_values[(unsigned char)str[0]]; - break; - } - return hval + asso_values[(unsigned char)str[len - 1]]; -} - -#ifdef __GNUC__ -__inline -#ifdef __GNUC_STDC_INLINE__ -__attribute__ ((__gnu_inline__)) -#endif -#endif -const struct kwtable * -mrb_reserved_word (register const char *str, register unsigned int len) -{ - static const struct kwtable wordlist[] = - { - {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, -#line 18 "src/keywords" - {"break", {keyword_break, keyword_break}, EXPR_MID}, -#line 23 "src/keywords" - {"else", {keyword_else, keyword_else}, EXPR_BEG}, -#line 33 "src/keywords" - {"nil", {keyword_nil, keyword_nil}, EXPR_END}, -#line 26 "src/keywords" - {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG}, -#line 25 "src/keywords" - {"end", {keyword_end, keyword_end}, EXPR_END}, -#line 42 "src/keywords" - {"then", {keyword_then, keyword_then}, EXPR_BEG}, -#line 34 "src/keywords" - {"not", {keyword_not, keyword_not}, EXPR_ARG}, -#line 27 "src/keywords" - {"false", {keyword_false, keyword_false}, EXPR_END}, -#line 40 "src/keywords" - {"self", {keyword_self, keyword_self}, EXPR_END}, -#line 24 "src/keywords" - {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE}, -#line 37 "src/keywords" - {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID}, -#line 43 "src/keywords" - {"true", {keyword_true, keyword_true}, EXPR_END}, -#line 46 "src/keywords" - {"until", {keyword_until, modifier_until}, EXPR_VALUE}, -#line 45 "src/keywords" - {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE}, -#line 39 "src/keywords" - {"return", {keyword_return, keyword_return}, EXPR_MID}, -#line 21 "src/keywords" - {"def", {keyword_def, keyword_def}, EXPR_FNAME}, -#line 16 "src/keywords" - {"and", {keyword_and, keyword_and}, EXPR_VALUE}, -#line 22 "src/keywords" - {"do", {keyword_do, keyword_do}, EXPR_BEG}, -#line 49 "src/keywords" - {"yield", {keyword_yield, keyword_yield}, EXPR_ARG}, -#line 28 "src/keywords" - {"for", {keyword_for, keyword_for}, EXPR_VALUE}, -#line 44 "src/keywords" - {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME}, -#line 35 "src/keywords" - {"or", {keyword_or, keyword_or}, EXPR_VALUE}, -#line 30 "src/keywords" - {"in", {keyword_in, keyword_in}, EXPR_VALUE}, -#line 47 "src/keywords" - {"when", {keyword_when, keyword_when}, EXPR_VALUE}, -#line 38 "src/keywords" - {"retry", {keyword_retry, keyword_retry}, EXPR_END}, -#line 29 "src/keywords" - {"if", {keyword_if, modifier_if}, EXPR_VALUE}, -#line 19 "src/keywords" - {"case", {keyword_case, keyword_case}, EXPR_VALUE}, -#line 36 "src/keywords" - {"redo", {keyword_redo, keyword_redo}, EXPR_END}, -#line 32 "src/keywords" - {"next", {keyword_next, keyword_next}, EXPR_MID}, -#line 41 "src/keywords" - {"super", {keyword_super, keyword_super}, EXPR_ARG}, -#line 31 "src/keywords" - {"module", {keyword_module, keyword_module}, EXPR_VALUE}, -#line 17 "src/keywords" - {"begin", {keyword_begin, keyword_begin}, EXPR_BEG}, -#line 12 "src/keywords" - {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END}, -#line 11 "src/keywords" - {"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END}, -#line 10 "src/keywords" - {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END}, -#line 14 "src/keywords" - {"END", {keyword_END, keyword_END}, EXPR_END}, -#line 15 "src/keywords" - {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME}, -#line 13 "src/keywords" - {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END}, - {""}, -#line 20 "src/keywords" - {"class", {keyword_class, keyword_class}, EXPR_CLASS}, - {""}, {""}, -#line 48 "src/keywords" - {"while", {keyword_while, modifier_while}, EXPR_VALUE} - }; - - if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) - { - register int key = hash (str, len); - - if (key <= MAX_HASH_VALUE && key >= 0) - { - register const char *s = wordlist[key].name; - - if (*str == *s && !strcmp (str + 1, s + 1)) - return &wordlist[key]; - } - } - return 0; -} -#line 50 "src/keywords" - diff --git a/src/load.c b/src/load.c index 7640d8fda..2e637aa19 100644 --- a/src/load.c +++ b/src/load.c @@ -7,55 +7,74 @@ #include <limits.h> #include <stdlib.h> #include <string.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" - -#define FLAG_BYTEORDER_NATIVE 2 -#define FLAG_BYTEORDER_NONATIVE 0 -#define FLAG_SRC_MALLOC 1 -#define FLAG_SRC_STATIC 0 +#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 -# define SIZE_ERROR_MUL(x, y) ((x) > SIZE_MAX / (y)) -# define SIZE_ERROR(x) ((x) > SIZE_MAX) -#else -# define SIZE_ERROR_MUL(x, y) (0) -# define SIZE_ERROR(x) (0) +# error size_t must be at least 32 bits wide #endif -#if UINT32_MAX > SIZE_MAX -# error This code cannot be built on your environment. -#endif +#define FLAG_SRC_MALLOC 1 +#define FLAG_SRC_STATIC 0 -static size_t -skip_padding(const uint8_t *buf) -{ - const size_t align = MRB_DUMP_ALIGNMENT; - return -(intptr_t)buf & (align-1); -} +#define SIZE_ERROR_MUL(nmemb, size) ((size_t)(nmemb) > SIZE_MAX / (size)) + +#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) { - size_t i; + int i; const uint8_t *src = bin; ptrdiff_t diff; uint16_t tt, pool_data_len, snl; - size_t plen; + 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); @@ -68,226 +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 = (size_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) { - if (SIZE_ERROR_MUL(sizeof(mrb_code), irep->ilen)) { - return NULL; + 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 FALSE; } - if ((flags & FLAG_SRC_MALLOC) == 0 && - (flags & FLAG_BYTEORDER_NATIVE)) { + if ((flags & FLAG_SRC_MALLOC) == 0) { irep->iseq = (mrb_code*)src; - src += sizeof(uint32_t) * 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 { - for (i = 0; i < irep->ilen; i++) { - irep->iseq[i] = (mrb_code)bin_to_uint32(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 = (size_t)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(sizeof(mrb_value), plen)) { - return NULL; + if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) { + 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: - irep->pool[i] = mrb_str_to_inum(mrb, s, 10, FALSE); + 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 + 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; 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 = (size_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(sizeof(mrb_sym), irep->slen)) { - return NULL; + if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) { + 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); - size_t i; + 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); -} - -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); - if (SIZE_ERROR(fname_len + 1)) { - return MRB_DUMP_GENERAL_FAILURE; - } - fname = (char *)mrb_malloc(mrb, fname_len + 1); - memcpy(fname, bin, fname_len); - fname[fname_len] = '\0'; - bin += fname_len; - *len += fname_len; + /* + * 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); - 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); - size_t 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 @@ -295,28 +301,29 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * { const uint8_t *bin = start; ptrdiff_t diff; - size_t record_size, i; + 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 = 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); @@ -326,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); @@ -347,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); @@ -372,7 +377,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; } @@ -395,6 +400,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; @@ -402,7 +408,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); @@ -426,7 +433,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; } @@ -434,34 +441,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; - size_t i; + 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; } @@ -484,6 +488,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; @@ -493,7 +498,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); @@ -513,87 +519,74 @@ 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; - uint32_t ident = 0; - size_t i; - /* create native byteorder version of RITE_BINARY_IDENTIFIER */ - for(i=0; i<sizeof(ident); i++) { - ident<<=8; - ident|=RITE_BINARY_IDENTIFIER[i]; + if (bufsize < sizeof(struct rite_binary_header)) { + return MRB_DUMP_READ_FAULT; } - if (memcmp(header->binary_identify, &ident, sizeof(header->binary_identify)) == 0) { - *flags |= FLAG_BYTEORDER_NATIVE; - } - else if (memcmp(header->binary_identify, RITE_BINARY_IDENTIFIER, sizeof(header->binary_identify)) != 0) { + + if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) != 0) { return MRB_DUMP_INVALID_FILE_HEADER; } - if (memcmp(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)) != 0) { + /* 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 (crc) { - *crc = bin_to_uint16(header->binary_crc); + /* 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; } -MRB_API 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_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(section_header->section_identify)) == 0) { - irep = read_section_irep(mrb, bin, flags); + if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) { + irep = read_section_irep(mrb, bin, flags, &proc); if (!irep) return NULL; } - else if (memcmp(section_header->section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(section_header->section_identify)) == 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_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(section_header->section_identify)) == 0) { + 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); if (result < MRB_DUMP_OK) { return NULL; } } - else if (memcmp(section_header->section_identify, RITE_SECTION_LV_IDENTIFIER, sizeof(section_header->section_identify)) == 0) { + else if (memcmp(section_header->section_ident, RITE_SECTION_LV_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; result = read_section_lv(mrb, bin, irep, flags); if (result < MRB_DUMP_OK) { @@ -601,43 +594,72 @@ 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_identify, RITE_BINARY_EOF, sizeof(section_header->section_identify)) != 0); + } while (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0); - return irep; + return proc; } -MRB_API 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 = mrb_obj_ptr(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")); } -MRB_API mrb_value -mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c) -{ - mrb_irep *irep = mrb_read_irep(mrb, bin); - struct RProc *proc; +void mrb_codedump_all(mrb_state*, struct RProc*); - if (!irep) { +static mrb_value +load_irep(mrb_state *mrb, struct RProc *proc, mrbc_context *c) +{ + if (!proc || !proc->body.irep) { irep_error(mrb); return mrb_nil_value(); } - proc = mrb_proc_new(mrb, irep); - mrb_irep_decref(mrb, irep); + proc->c = NULL; + if (c && c->dump_result) mrb_codedump_all(mrb, proc); if (c && c->no_exec) return mrb_obj_value(proc); - return mrb_toplevel_run(mrb, proc); + return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); +} + +MRB_API mrb_value +mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *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 @@ -646,64 +668,62 @@ mrb_load_irep(mrb_state *mrb, const uint8_t *bin) return mrb_load_irep_cxt(mrb, bin, NULL); } -#ifdef ENABLE_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_API 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_vm_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; - uint8_t flags; + uint8_t flags = 0; int result; if ((mrb == NULL) || (fp == NULL)) { return NULL; } - /* You don't need use SIZE_ERROR as buf_size is enough small. */ buf = (uint8_t*)mrb_malloc(mrb, header_size); if (fread(buf, header_size, 1, fp) == 0) { - mrb_free(mrb, buf); - return NULL; + goto irep_exit; } - result = read_binary_header(buf, &buf_size, NULL, &flags); - if (result != MRB_DUMP_OK) { - mrb_free(mrb, buf); - return NULL; + result = read_binary_header(buf, (size_t)-1, &buf_size, &flags); + if (result != MRB_DUMP_OK || buf_size <= header_size) { + goto irep_exit; } buf = (uint8_t*)mrb_realloc(mrb, buf, buf_size); if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) { - mrb_free(mrb, buf); - return NULL; + goto irep_exit; } - irep = read_irep(mrb, buf, FLAG_SRC_MALLOC); - mrb_free(mrb, buf); + proc = read_irep(mrb, buf, (size_t)-1, FLAG_SRC_MALLOC); - return irep; +irep_exit: + mrb_free(mrb, buf); + return proc; } -void mrb_codedump_all(mrb_state*, struct RProc*); +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) { - mrb_irep *irep = mrb_read_irep_file(mrb, fp); - mrb_value val; - struct RProc *proc; - - if (!irep) { - irep_error(mrb); - return mrb_nil_value(); - } - proc = mrb_proc_new(mrb, irep); - 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); - val = mrb_toplevel_run(mrb, proc); - return val; + return load_irep(mrb, mrb_proc_read_irep_file(mrb, fp), c); } MRB_API mrb_value @@ -711,4 +731,4 @@ mrb_load_irep_file(mrb_state *mrb, FILE* fp) { return mrb_load_irep_file_cxt(mrb, fp, NULL); } -#endif /* ENABLE_STDIO */ +#endif /* MRB_NO_STDIO */ diff --git a/src/mrb_throw.h b/src/mrb_throw.h deleted file mode 100644 index 3c7407a8d..000000000 --- a/src/mrb_throw.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -** mrb_throw.h - mruby exception throwing handler -** -** See Copyright Notice in mruby.h -*/ - -#ifndef MRB_THROW_H -#define MRB_THROW_H - -#ifdef MRB_ENABLE_CXX_EXCEPTION - -#define MRB_TRY(buf) do { try { -#define MRB_CATCH(buf) } catch(mrb_jmpbuf_impl e) { if (e != (buf)->impl) { throw e; } -#define MRB_END_EXC(buf) } } while(0) - -#define MRB_THROW(buf) throw((buf)->impl) -typedef mrb_int mrb_jmpbuf_impl; - -#else - -#include <setjmp.h> - -#define MRB_TRY(buf) do { if (setjmp((buf)->impl) == 0) { -#define MRB_CATCH(buf) } else { -#define MRB_END_EXC(buf) } } while(0) - -#define MRB_THROW(buf) longjmp((buf)->impl, 1); -#define mrb_jmpbuf_impl jmp_buf - -#endif - -struct mrb_jmpbuf { - mrb_jmpbuf_impl impl; - -#ifdef MRB_ENABLE_CXX_EXCEPTION - static mrb_int jmpbuf_id; - mrb_jmpbuf() : impl(jmpbuf_id++) {} -#endif -}; - -#endif /* MRB_THROW_H */ diff --git a/src/mruby_core.rake b/src/mruby_core.rake deleted file mode 100644 index 88fca83fc..000000000 --- a/src/mruby_core.rake +++ /dev/null @@ -1,78 +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}" - - lex_def = "#{current_dir}/lex.def" - objs = Dir.glob("#{current_dir}/*.c").map { |f| - next nil if cxx_abi_enabled? and f =~ /(codegen|error|vm).c$/ - objfile(f.pathmap("#{current_build_dir}/%n")) - }.compact - - if cxx_abi_enabled? - cxx_abi_dependency = %w(codegen error vm) - cxx_abi_objs = cxx_abi_dependency.map { |v| - src = "#{current_build_dir}/#{v}.cxx" - file src => ["#{current_dir}/#{v}.c", __FILE__] do |t| - File.open(t.name, 'w') do |f| - f.write <<EOS -#define __STDC_CONSTANT_MACROS -#define __STDC_LIMIT_MACROS - -extern "C" { -#include "#{MRUBY_ROOT}/#{t.prerequisites.first}" -} - - -#{v == 'error'? 'mrb_int mrb_jmpbuf::jmpbuf_id = 0;' : ''} -EOS - end - end - - file objfile(src) => src do |t| - cxx.run t.name, t.prerequisites.first, [], [current_dir] - end - - objfile src - } - cxx_abi_objs << objfile("#{current_build_dir}/y.tab") - - file "#{current_build_dir}/y.tab.cxx" => ["#{current_build_dir}/y.tab.c", __FILE__] do |t| - File.open(t.name, 'w') do |f| - f.write <<EOS -#define __STDC_CONSTANT_MACROS -#define __STDC_LIMIT_MACROS - -extern "C" { -#include "#{t.prerequisites.first}" -} -EOS - end - end - file objfile("#{current_build_dir}/y.tab") => ["#{current_build_dir}/y.tab.cxx", lex_def] do |t| - cxx.run t.name, t.prerequisites.first, [], [current_dir] - end - - objs += cxx_abi_objs - else - objs += [objfile("#{current_build_dir}/y.tab")] - file objfile("#{current_build_dir}/y.tab") => ["#{current_build_dir}/y.tab.c", lex_def] do |t| - cc.run t.name, t.prerequisites.first, [], [current_dir] - end - end - self.libmruby << objs - - file libfile("#{build_dir}/lib/libmruby_core") => objs do |t| - archiver.run t.name, t.prerequisites - end - - # Parser - file "#{current_build_dir}/y.tab.c" => ["#{current_dir}/parse.y"] do |t| - yacc.run t.name, t.prerequisites.first - end - - # Lexical analyzer - file lex_def => "#{current_dir}/keywords" do |t| - gperf.run t.name, t.prerequisites.first - end -end diff --git a/src/node.h b/src/node.h deleted file mode 100644 index 532a8323a..000000000 --- a/src/node.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -** node.h - nodes of abstract syntax tree -** -** See Copyright Notice in mruby.h -*/ - -#ifndef NODE_H -#define NODE_H - -enum node_type { - NODE_METHOD, - NODE_FBODY, - NODE_CFUNC, - NODE_SCOPE, - NODE_BLOCK, - NODE_IF, - NODE_CASE, - NODE_WHEN, - NODE_OPT_N, - NODE_WHILE, - NODE_UNTIL, - NODE_ITER, - NODE_FOR, - NODE_BREAK, - NODE_NEXT, - NODE_REDO, - NODE_RETRY, - NODE_BEGIN, - NODE_RESCUE, - NODE_ENSURE, - NODE_AND, - NODE_OR, - NODE_NOT, - NODE_MASGN, - NODE_ASGN, - NODE_CDECL, - NODE_CVASGN, - NODE_CVDECL, - NODE_OP_ASGN, - NODE_CALL, - NODE_FCALL, - NODE_VCALL, - NODE_SUPER, - NODE_ZSUPER, - NODE_ARRAY, - NODE_ZARRAY, - NODE_HASH, - NODE_RETURN, - NODE_YIELD, - NODE_LVAR, - NODE_DVAR, - NODE_GVAR, - NODE_IVAR, - NODE_CONST, - NODE_CVAR, - NODE_NTH_REF, - NODE_BACK_REF, - NODE_MATCH, - NODE_MATCH2, - NODE_MATCH3, - NODE_INT, - NODE_FLOAT, - NODE_NEGATE, - NODE_LAMBDA, - NODE_SYM, - NODE_STR, - NODE_DSTR, - NODE_XSTR, - NODE_DXSTR, - NODE_REGX, - NODE_DREGX, - NODE_DREGX_ONCE, - NODE_LIST, - NODE_ARG, - NODE_ARGSCAT, - NODE_ARGSPUSH, - NODE_SPLAT, - NODE_TO_ARY, - NODE_SVALUE, - NODE_BLOCK_ARG, - NODE_DEF, - NODE_SDEF, - NODE_ALIAS, - NODE_UNDEF, - NODE_CLASS, - NODE_MODULE, - NODE_SCLASS, - NODE_COLON2, - NODE_COLON3, - NODE_CREF, - NODE_DOT2, - NODE_DOT3, - NODE_FLIP2, - NODE_FLIP3, - NODE_ATTRSET, - NODE_SELF, - NODE_NIL, - NODE_TRUE, - NODE_FALSE, - NODE_DEFINED, - NODE_NEWLINE, - NODE_POSTEXE, - NODE_ALLOCA, - NODE_DMETHOD, - NODE_BMETHOD, - NODE_MEMO, - NODE_IFUNC, - NODE_DSYM, - NODE_ATTRASGN, - NODE_HEREDOC, - NODE_LITERAL_DELIM, - NODE_WORDS, - NODE_SYMBOLS, - NODE_LAST -}; - -#endif /* NODE_H */ diff --git a/src/numeric.c b/src/numeric.c index e01e661a5..306aab803 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -1,45 +1,64 @@ /* -** numeric.c - Numeric, Integer, Float, Fixnum class +** numeric.c - Numeric, Integer, Float class ** ** See Copyright Notice in mruby.h */ +#ifndef MRB_NO_FLOAT #include <float.h> -#include <limits.h> #include <math.h> +#endif +#include <limits.h> #include <stdlib.h> - -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/numeric.h" -#include "mruby/string.h" - -#ifdef MRB_USE_FLOAT -#define floor(f) floorf(f) -#define ceil(f) ceilf(f) +#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_NO_FLOAT +#ifdef MRB_USE_FLOAT32 +#define trunc(f) truncf(f) #define fmod(x,y) fmodf(x,y) -#define FLO_MAX_DIGITS 7 -#define FLO_MAX_SIGN_LENGTH 3 -#define FLO_EPSILON FLT_EPSILON #else -#define FLO_MAX_DIGITS 14 -#define FLO_MAX_SIGN_LENGTH 10 -#define FLO_EPSILON DBL_EPSILON +#endif #endif +#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: @@ -51,34 +70,94 @@ 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; - mrb_float d, yv; + mrb_int base = mrb_integer(x); + mrb_int result = 1; + mrb_int exp; - mrb_get_args(mrb, "o", &y); - yv = mrb_to_flo(mrb, y); - d = pow(mrb_to_flo(mrb, x), yv); - if (mrb_fixnum_p(x) && mrb_fixnum_p(y) && FIXABLE(d) && yv > 0) - return mrb_fixnum_value((mrb_int)d); - return mrb_float_value(mrb, d); +#ifndef MRB_NO_FLOAT + mrb_value y = mrb_get_arg1(mrb); + + 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 + { + 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"); + } + } + exp >>= 1; + if (exp == 0) break; + if (mrb_int_mul_overflow(base, base, &base)) { + int_overflow(mrb, "power"); + } + } + 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) { - return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y)); + 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); + } +#ifdef MRB_NO_FLOAT + mrb_raise(mrb, E_TYPE_ERROR, "non integer division"); +#else + return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y))); +#endif } /* 15.2.9.3.19(x) */ @@ -89,175 +168,198 @@ 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) { + mrb_int y; + + mrb_get_args(mrb, "i", &y); + if (y == 0) { + int_zerodiv(mrb); + } + 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 +} + +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 + if (mrb_float_p(self) || 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 -mrb_flo_to_str(mrb_state *mrb, mrb_float flo) +flo_pow(mrb_state *mrb, mrb_value x) { - double n = (double)flo; - int max_digits = FLO_MAX_DIGITS; + 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); +} - if (isnan(n)) { - return mrb_str_new_lit(mrb, "NaN"); +mrb_float +mrb_div_float(mrb_float x, mrb_float y) +{ + if (y != 0.0) { + return x / y; } - else if (isinf(n)) { - if (n < 0) { - return mrb_str_new_lit(mrb, "-inf"); - } - else { - return mrb_str_new_lit(mrb, "inf"); - } + else if (x == 0.0) { + return NAN; } else { - int digit; - int m = 0; - int exp; - mrb_bool e = FALSE; - char s[48]; - char *c = &s[0]; - int length = 0; - - if (signbit(n)) { - n = -n; - *(c++) = '-'; - } - - if (n != 0.0) { - if (n > 1.0) { - exp = (int)floor(log10(n)); - } - else { - exp = (int)-ceil(-log10(n)); - } - } - else { - exp = 0; - } - - /* preserve significands */ - if (exp < 0) { - int i, beg = -1, end = 0; - double f = n; - double fd = 0; - for (i = 0; i < FLO_MAX_DIGITS; ++i) { - f = (f - fd) * 10.0; - fd = floor(f + FLO_EPSILON); - if (fd != 0) { - if (beg < 0) beg = i; - end = i + 1; - } - } - if (beg >= 0) length = end - beg; - if (length > FLO_MAX_SIGN_LENGTH) length = FLO_MAX_SIGN_LENGTH; - } - - if (abs(exp) + length >= FLO_MAX_DIGITS) { - /* exponent representation */ - e = TRUE; - n = n / pow(10.0, exp); - if (isinf(n)) { - if (s < c) { /* s[0] == '-' */ - return mrb_str_new_lit(mrb, "-0.0"); - } - else { - return mrb_str_new_lit(mrb, "0.0"); - } - } - } - else { - /* un-exponent (normal) representation */ - if (exp > 0) { - m = exp; - } - } - - /* puts digits */ - while (max_digits >= 0) { - double weight = (m < 0) ? 0.0 : pow(10.0, m); - double fdigit = (m < 0) ? n * 10.0 : n / weight; + return x * (signbit(y) ? -1.0 : 1.0) * INFINITY; + } +} - if (fdigit < 0) fdigit = n = 0; - if (m < -1 && fdigit < FLO_EPSILON) { - if (e || exp > 0 || m <= -abs(exp)) { - break; - } - } - digit = (int)floor(fdigit + FLO_EPSILON); - if (m == 0 && digit > 9) { - n /= 10.0; - exp++; - continue; - } - *(c++) = '0' + digit; - n = (m < 0) ? n * 10.0 - digit : n - (digit * weight); - max_digits--; - if (m-- == 0) { - *(c++) = '.'; - } - } - if (c[-1] == '0') { - while (&s[0] < c && c[-1] == '0') { - c--; - } - c++; - } +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 (e) { - *(c++) = 'e'; - if (exp > 0) { - *(c++) = '+'; - } - else { - *(c++) = '-'; - exp = -exp; - } + 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); +} - if (exp >= 100) { - *(c++) = '0' + exp / 100; - exp -= exp / 100 * 100; - } +/* 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 - *(c++) = '0' + exp / 10; - *(c++) = '0' + exp % 10; + 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; } - - *c = '\0'; - - return mrb_str_new(mrb, &s[0], c - &s[0]); } + 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>''. + * "<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) { - return mrb_flo_to_str(mrb, mrb_float(flt)); + 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)); + } } /* 15.2.9.3.2 */ @@ -270,12 +372,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 */ @@ -290,34 +402,54 @@ 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 -flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *modp) +flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp) { - mrb_float div; - mrb_float mod; + double div, mod; + if (isnan(y)) { + /* y is NaN so all results are NaN */ + div = mod = y; + goto exit; + } if (y == 0.0) { - div = INFINITY; - mod = NAN; + int_zerodiv(mrb); + } + if (isinf(y) && !isinf(x)) { + mod = x; } else { mod = fmod(x, y); - if (isinf(x) && isfinite(y)) - div = x; - else - div = (x - mod) / y; - if (y*mod < 0) { - mod += y; - div -= 1.0; - } } - + if (isinf(x) && !isinf(y)) { + div = x; + } + else { + 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; + } + exit: if (modp) *modp = mod; if (divp) *divp = div; } @@ -337,14 +469,13 @@ flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float * 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 /* 15.2.8.3.16 */ /* @@ -359,23 +490,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_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 */ @@ -394,43 +524,157 @@ 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(); } } -/* 15.2.8.3.18 */ -/* - * call-seq: - * flt.hash -> integer - * - * Returns a hash code for this float. - */ +static int64_t +value_int64(mrb_state *mrb, mrb_value x) +{ + switch (mrb_type(x)) { + case MRB_TT_INTEGER: + return (int64_t)mrb_integer(x); + case MRB_TT_FLOAT: + { + 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; + } + /* not reached */ + return 0; +} + +static mrb_value +int64_value(mrb_state *mrb, int64_t v) +{ + if (!TYPED_FIXABLE(v,int64_t)) { + int_overflow(mrb, "bit operation"); + } + return mrb_fixnum_value((mrb_int)v); +} + +static mrb_value +flo_rev(mrb_state *mrb, mrb_value 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_get_arg1(mrb); + int64_t v1, v2; + + v1 = value_int64(mrb, x); + v2 = value_int64(mrb, y); + return int64_value(mrb, v1 & v2); +} + static mrb_value -flo_hash(mrb_state *mrb, mrb_value num) +flo_or(mrb_state *mrb, mrb_value x) { - mrb_float d; - char *c; - size_t i; - int hash; + mrb_value y = mrb_get_arg1(mrb); + int64_t v1, v2; - d = (mrb_float)mrb_fixnum(num); - /* normalize -0.0 to 0.0 */ - if (d == 0) d = 0.0; - c = (char*)&d; - for (hash=0, i=0; i<sizeof(mrb_float);i++) { - hash = (hash * 971) ^ (unsigned char)c[i]; + v1 = value_int64(mrb, x); + v2 = value_int64(mrb, y); + return int64_value(mrb, v1 | v2); +} + +static mrb_value +flo_xor(mrb_state *mrb, mrb_value x) +{ + mrb_value y = mrb_get_arg1(mrb); + int64_t v1, v2; + + v1 = value_int64(mrb, x); + v2 = value_int64(mrb, y); + return int64_value(mrb, v1 ^ v2); +} + +static mrb_value +flo_shift(mrb_state *mrb, mrb_value x, mrb_int width) +{ + mrb_float val; + + if (width == 0) { + 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); +#else + if (val > 0){ + val = floor(val); + } else { + val = ceil(val); + } +#endif + if (val == 0 && mrb_float(x) < 0) { + return mrb_fixnum_value(-1); + } } - if (hash < 0) hash = -hash; - return mrb_fixnum_value(hash); + else { + while (width--) { + val *= 2; + } + } + if (FIXABLE_FLOAT(val)) + return mrb_int_value(mrb, (mrb_int)val); + return mrb_float_value(mrb, val); +} + +static mrb_value +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_lshift(mrb_state *mrb, mrb_value x) +{ + mrb_int width; + + mrb_get_args(mrb, "i", &width); + return flo_shift(mrb, x, width); } /* 15.2.9.3.13 */ @@ -488,53 +732,138 @@ flo_finite_p(mrb_state *mrb, mrb_value num) return mrb_bool_value(isfinite(mrb_float(num))); } +void +mrb_check_num_exact(mrb_state *mrb, mrb_float num) +{ + if (isinf(num)) { + mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity"); + } + if (isnan(num)) { + mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); + } +} + +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 number less than or equal to +float+ with + * a precision of +ndigits+ decimal digits (default: 0). * - * Returns the largest integer less than or equal to <i>flt</i>. + * 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)); - - if (!FIXABLE(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). * - * Returns the smallest <code>Integer</code> greater than or equal to - * <i>flt</i>. + * 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.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)); - - if (!FIXABLE(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 */ @@ -543,7 +872,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 @@ -573,22 +902,19 @@ flo_round(mrb_state *mrb, mrb_value num) { double number, f; mrb_int ndigits = 0; - int i; + mrb_int i; mrb_get_args(mrb, "|i", &ndigits); number = mrb_float(num); - if (isinf(number)) { - if (0 < ndigits) return num; - else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, number < 0 ? "-Infinity" : "Infinity"); - } - if (isnan(number)) { - if (0 < ndigits) return num; - else mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); + if (0 < ndigits && (isinf(number) || isnan(number))) { + return num; } + mrb_check_num_exact(mrb, number); f = 1.0; - i = abs(ndigits); + i = ndigits >= 0 ? ndigits : -ndigits; + if (ndigits > DBL_DIG+2) return num; while (--i >= 0) f = f*10.0; @@ -619,15 +945,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>. @@ -636,15 +975,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); - - if (!FIXABLE(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 @@ -652,12 +984,12 @@ flo_nan_p(mrb_state *mrb, mrb_value num) { return mrb_bool_value(isnan(mrb_float(num))); } +#endif /* * 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. * */ @@ -665,7 +997,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. @@ -677,31 +1008,59 @@ int_to_i(mrb_state *mrb, mrb_value num) return num; } -/*tests if N*N would overflow*/ -#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1-MRB_FIXNUM_SHIFT)/2)) -#define FIT_SQRT_INT(n) (((n)<SQRT_INT_MAX)&&((n)>=-SQRT_INT_MAX)) - -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)) { - mrb_float c; - mrb_int b; + a = mrb_integer(x); + if (mrb_integer_p(y)) { + mrb_int b, c; if (a == 0) return x; - b = mrb_fixnum(y); - if (FIT_SQRT_INT(a) && FIT_SQRT_INT(b)) - return mrb_fixnum_value(a*b); - c = a * b; - if ((a != 0 && c/a != b) || !FIXABLE(c)) { - return mrb_float_value(mrb, (mrb_float)a*(mrb_float)b); + b = mrb_integer(y); + if (mrb_int_mul_overflow(a, b, &c)) { + int_overflow(mrb, "multiplication"); } - return mrb_fixnum_value((mrb_int)c); + return mrb_int_value(mrb, c); + } + 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_as_float(mrb, y)); +#endif } - return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y)); +} + +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 */ @@ -715,40 +1074,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 */ @@ -762,28 +1114,28 @@ 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) { - return mrb_float_value(mrb, NAN); - } - fixdivmod(mrb, a, b, 0, &mod); + fixdivmod(mrb, a, b, NULL, &mod); return mrb_fixnum_value(mod); } +#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 } /* @@ -793,47 +1145,48 @@ 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) { - return mrb_assoc_new(mrb, mrb_float_value(mrb, INFINITY), - mrb_float_value(mrb, NAN)); - } - fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod); - return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod)); + 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_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, (mrb_int)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_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, (mrb_int)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); } +#endif /* 15.2.8.3.7 */ /* @@ -848,16 +1201,25 @@ 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)); + 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(); } @@ -875,24 +1237,26 @@ 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); } -static mrb_value -bit_coerce(mrb_state *mrb, mrb_value x) -{ - while (!mrb_fixnum_p(x)) { - if (mrb_float_p(x)) { - mrb_raise(mrb, E_TYPE_ERROR, "can't convert Float into Integer"); - } - x = mrb_to_int(mrb, x); - } - return x; -} +#ifdef MRB_NO_FLOAT +#define bit_op(x,y,op1,op2) do {\ + 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_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 /* 15.2.8.3.9 */ /* @@ -903,14 +1267,11 @@ bit_coerce(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_get_args(mrb, "o", &y); + mrb_value y = mrb_get_arg1(mrb); - y = bit_coerce(mrb, y); - return mrb_fixnum_value(mrb_fixnum(x) & mrb_fixnum(y)); + bit_op(x, y, and, &); } /* 15.2.8.3.10 */ @@ -922,14 +1283,11 @@ 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); - - y = bit_coerce(mrb, y); - return mrb_fixnum_value(mrb_fixnum(x) | mrb_fixnum(y)); + bit_op(x, y, or, |); } /* 15.2.8.3.11 */ @@ -941,14 +1299,11 @@ 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_get_args(mrb, "o", &y); + mrb_value y = mrb_get_arg1(mrb); - y = bit_coerce(mrb, y); - return mrb_fixnum_value(mrb_fixnum(x) ^ mrb_fixnum(y)); + bit_op(x, y, or, ^); } #define NUMERIC_SHIFT_WIDTH_MAX (MRB_INT_BIT-1) @@ -957,16 +1312,24 @@ static mrb_value lshift(mrb_state *mrb, mrb_int val, mrb_int width) { mrb_assert(width >= 0); - if (width > NUMERIC_SHIFT_WIDTH_MAX) { - mrb_raisef(mrb, E_RANGE_ERROR, "width(%S) > (%S:MRB_INT_BIT-1)", - mrb_fixnum_value(width), - mrb_fixnum_value(NUMERIC_SHIFT_WIDTH_MAX)); + if (val > 0) { + if ((width > NUMERIC_SHIFT_WIDTH_MAX) || + (val > (MRB_INT_MAX >> width))) { + int_overflow(mrb, "bit shift"); + } + return mrb_int_value(mrb, val << width); + } + else { + if ((width > NUMERIC_SHIFT_WIDTH_MAX) || + (val <= (MRB_INT_MIN >> width))) { + int_overflow(mrb, "bit shift"); + } + return mrb_int_value(mrb, (val * ((mrb_int)1 << width))); } - return mrb_fixnum_value(val << width); } static mrb_value -rshift(mrb_int val, mrb_int width) +rshift(mrb_state *mrb, mrb_int val, mrb_int width) { mrb_assert(width >= 0); if (width >= NUMERIC_SHIFT_WIDTH_MAX) { @@ -975,39 +1338,31 @@ rshift(mrb_int val, mrb_int width) } return mrb_fixnum_value(0); } - return mrb_fixnum_value(val >> width); -} - -static inline void -fix_shift_get_width(mrb_state *mrb, mrb_int *width) -{ - mrb_value y; - - mrb_get_args(mrb, "o", &y); - *width = mrb_fixnum(bit_coerce(mrb, y)); + return mrb_int_value(mrb, val >> width); } /* 15.2.8.3.12 */ /* * call-seq: - * fix << count -> integer + * fix << count -> integer or float * * Shifts _fix_ left _count_ positions (right if _count_ is negative). */ static mrb_value -fix_lshift(mrb_state *mrb, mrb_value x) +int_lshift(mrb_state *mrb, mrb_value x) { mrb_int width, val; - fix_shift_get_width(mrb, &width); - + mrb_get_args(mrb, "i", &width); 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); } @@ -1015,26 +1370,27 @@ fix_lshift(mrb_state *mrb, mrb_value x) /* 15.2.8.3.13 */ /* * call-seq: - * fix >> count -> integer + * fix >> count -> integer or float * * Shifts _fix_ right _count_ positions (left if _count_ is negative). */ static mrb_value -fix_rshift(mrb_state *mrb, mrb_value x) +int_rshift(mrb_state *mrb, mrb_value x) { mrb_int width, val; - fix_shift_get_width(mrb, &width); - + mrb_get_args(mrb, "i", &width); 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 */ @@ -1046,10 +1402,11 @@ fix_rshift(mrb_state *mrb, mrb_value x) * */ +#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)); } /* @@ -1059,7 +1416,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> * @@ -1067,45 +1424,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; + 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"); + mrb_check_num_exact(mrb, d); + if (FIXABLE_FLOAT(d)) { + z = (mrb_int)d; } - if (isnan(d)) { - mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); + else { + mrb_raisef(mrb, E_RANGE_ERROR, "number (%v) too big for integer", x); } - z = (mrb_int)d; } - 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)) { - return mrb_float_value(mrb, (mrb_float)a + (mrb_float)b); + int_overflow(mrb, "addition"); } - return mrb_fixnum_value(c); + return mrb_int_value(mrb, c); + } + 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_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; } - return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y)); +#endif + mrb_raise(mrb, E_TYPE_ERROR, "no number addition"); + return mrb_nil_value(); /* not reached */ } /* 15.2.8.3.1 */ @@ -1118,30 +1511,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)) { - return mrb_float_value(mrb, (mrb_float)a - (mrb_float)b); + int_overflow(mrb, "subtraction"); } - return mrb_fixnum_value(c); + return mrb_int_value(mrb, c); + } + 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_as_float(mrb, y)); +#endif } - return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y)); +} + +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 */ @@ -1155,42 +1584,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, 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 */ @@ -1210,138 +1657,268 @@ mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, 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_NO_FLOAT + mrb_int x, y; +#else + mrb_float x, y; +#endif + +#ifdef MRB_NO_FLOAT + x = mrb_integer(v1); +#else + x = mrb_as_float(mrb, v1); +#endif + switch (mrb_type(v2)) { + case MRB_TT_INTEGER: +#ifdef MRB_NO_FLOAT + y = mrb_integer(v2); +#else + y = (mrb_float)mrb_integer(v2); +#endif + break; +#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; + } + if (x > y) + return 1; + else { + if (x < y) + return -1; + return 0; + } } /* 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_float x, y; + 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); +} - x = mrb_to_flo(mrb, self); - switch (mrb_type(other)) { - case MRB_TT_FIXNUM: - y = (mrb_float)mrb_fixnum(other); - break; +static mrb_noreturn void +cmperr(mrb_state *mrb, mrb_value v1, mrb_value 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_get_arg1(mrb); + mrb_int n; + + n = cmpnum(mrb, self, other); + if (n == -2) cmperr(mrb, self, other); + if (n < 0) return mrb_true_value(); + return mrb_false_value(); +} + +static mrb_value +num_le(mrb_state *mrb, mrb_value self) +{ + mrb_value other = mrb_get_arg1(mrb); + mrb_int n; + + n = cmpnum(mrb, self, other); + if (n == -2) cmperr(mrb, self, other); + if (n <= 0) return mrb_true_value(); + return mrb_false_value(); +} + +static mrb_value +num_gt(mrb_state *mrb, mrb_value self) +{ + mrb_value other = mrb_get_arg1(mrb); + mrb_int n; + + n = cmpnum(mrb, self, other); + if (n == -2) cmperr(mrb, self, other); + if (n > 0) return mrb_true_value(); + return mrb_false_value(); +} + +static mrb_value +num_ge(mrb_state *mrb, mrb_value self) +{ + mrb_value other = mrb_get_arg1(mrb); + mrb_int n; + + 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: - y = mrb_float(other); - break; + return cmpnum(mrb, obj1, obj2); + case MRB_TT_STRING: + if (!mrb_string_p(obj2)) + return -2; + return mrb_str_cmp(mrb, obj1, obj2); default: - return mrb_nil_value(); - } - if (x > y) - return mrb_fixnum_value(1); - else { - if (x < y) - return mrb_fixnum_value(-1); - return mrb_fixnum_value(0); + 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); } } -/* 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_plus(mrb_state *mrb, mrb_value x) +num_finite_p(mrb_state *mrb, mrb_value self) { - mrb_value y; + return mrb_true_value(); +} - mrb_get_args(mrb, "o", &y); - return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y)); +static mrb_value +num_infinite_p(mrb_state *mrb, mrb_value self) +{ + return mrb_false_value(); } /* ------------------------------------------------------------------------*/ void mrb_init_numeric(mrb_state *mrb) { - struct RClass *numeric, *integer, *fixnum, *fl; + 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, "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->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, "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()); - - /* Fixnum Class */ - fixnum = mrb->fixnum_class = mrb_define_class(mrb, "Fixnum", integer); - mrb_define_method(mrb, fixnum, "+", fix_plus, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */ - mrb_define_method(mrb, fixnum, "-", fix_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */ - mrb_define_method(mrb, fixnum, "*", fix_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */ - mrb_define_method(mrb, fixnum, "%", fix_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */ - mrb_define_method(mrb, fixnum, "==", fix_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */ - mrb_define_method(mrb, fixnum, "~", fix_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */ - mrb_define_method(mrb, fixnum, "&", fix_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */ - mrb_define_method(mrb, fixnum, "|", fix_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */ - mrb_define_method(mrb, fixnum, "^", fix_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */ - mrb_define_method(mrb, fixnum, "<<", fix_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */ - mrb_define_method(mrb, fixnum, ">>", fix_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */ - mrb_define_method(mrb, fixnum, "eql?", fix_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ - mrb_define_method(mrb, fixnum, "hash", flo_hash, MRB_ARGS_NONE()); /* 15.2.8.3.18 */ - mrb_define_method(mrb, fixnum, "to_f", fix_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */ - mrb_define_method(mrb, fixnum, "to_s", fix_to_s, MRB_ARGS_NONE()); /* 15.2.8.3.25 */ - mrb_define_method(mrb, fixnum, "inspect", fix_to_s, MRB_ARGS_NONE()); - mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */ + mrb_define_method(mrb, 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()); + + 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, 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_NO_FLOAT /* Float Class */ - fl = mrb->float_class = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */ + 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, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */ - mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */ - mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */ - mrb_define_method(mrb, fl, "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */ - mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */ - mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */ - mrb_define_method(mrb, fl, "to_i", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.14 */ - mrb_define_method(mrb, fl, "to_int", flo_truncate, MRB_ARGS_NONE()); - mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */ - mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ - - mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */ - mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE()); - mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE()); + mrb_define_method(mrb, fl, "**", flo_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_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_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_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 */ + + mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */ + mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE()); #ifdef INFINITY - mrb_define_const(mrb, fl, "INFINITY", mrb_float_value(mrb, INFINITY)); + 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 } diff --git a/src/object.c b/src/object.c index c5fb74575..8fe4688ac 100644 --- a/src/object.c +++ b/src/object.c @@ -4,10 +4,12 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/class.h" -#include "mruby/numeric.h" -#include "mruby/string.h" +#include <mruby.h> +#include <mruby/class.h> +#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) @@ -18,13 +20,15 @@ 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_NO_FLOAT case MRB_TT_FLOAT: return (mrb_float(v1) == mrb_float(v2)); +#endif default: return (mrb_ptr(v1) == mrb_ptr(v2)); @@ -44,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; } @@ -80,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; } /*********************************************************************** @@ -147,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 */ @@ -254,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 @@ -264,7 +287,8 @@ mrb_init_object(mrb_state *mrb) struct RClass *t; struct RClass *f; - n = mrb->nil_class = mrb_define_class(mrb, "NilClass", mrb->object_class); + mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class); + 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 */ @@ -273,7 +297,8 @@ mrb_init_object(mrb_state *mrb) mrb_define_method(mrb, n, "to_s", nil_to_s, MRB_ARGS_NONE()); /* 15.2.4.3.5 */ mrb_define_method(mrb, n, "inspect", nil_inspect, MRB_ARGS_NONE()); - t = mrb->true_class = mrb_define_class(mrb, "TrueClass", mrb->object_class); + mrb->true_class = t = mrb_define_class(mrb, "TrueClass", mrb->object_class); + MRB_SET_INSTANCE_TT(t, MRB_TT_TRUE); mrb_undef_class_method(mrb, t, "new"); mrb_define_method(mrb, t, "&", true_and, MRB_ARGS_REQ(1)); /* 15.2.5.3.1 */ mrb_define_method(mrb, t, "^", true_xor, MRB_ARGS_REQ(1)); /* 15.2.5.3.2 */ @@ -281,7 +306,8 @@ mrb_init_object(mrb_state *mrb) mrb_define_method(mrb, t, "|", true_or, MRB_ARGS_REQ(1)); /* 15.2.5.3.4 */ mrb_define_method(mrb, t, "inspect", true_to_s, MRB_ARGS_NONE()); - f = mrb->false_class = mrb_define_class(mrb, "FalseClass", mrb->object_class); + mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class); + 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 */ @@ -290,134 +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) return val; - v = convert_type(mrb, val, tname, method, FALSE); + if (mrb_type(val) == type && type != MRB_TT_DATA && type != MRB_TT_ISTRUCT) return val; + 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"}, - {MRB_TT_FLOAT, "Float"}, - {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 */ - {-1, 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)) { - 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 */ @@ -428,19 +406,21 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) * Returns a string representing <i>obj</i>. The default * <code>to_s</code> prints the object's class and an encoding of the * object id. As a special case, the top-level object that is the - * initial execution context of Ruby programs returns ``main.'' + * initial execution context of Ruby programs returns "main." */ MRB_API mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj) { - mrb_value str = mrb_str_buf_new(mrb, 20); + mrb_value str = mrb_str_new_capa(mrb, 20); const char *cname = mrb_obj_classname(mrb, 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_cptr(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; @@ -481,12 +461,14 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) case MRB_TT_MODULE: case MRB_TT_CLASS: case MRB_TT_ICLASS: + case MRB_TT_SCLASS: break; default: mrb_raise(mrb, E_TYPE_ERROR, "class or module required"); } + MRB_CLASS_ORIGIN(c); while (cl) { if (cl == c || cl->mt == c->mt) return TRUE; @@ -495,45 +477,41 @@ 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 -mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base) +mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base) { mrb_value tmp; 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_NO_FLOAT case MRB_TT_FLOAT: if (base != 0) goto arg_error; - if (FIXABLE(mrb_float(val))) { - 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; @@ -547,33 +525,26 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base) if (base != 0) { tmp = mrb_check_string_type(mrb, val); if (!mrb_nil_p(tmp)) { + val = tmp; goto string_conv; } arg_error: 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)) { - return mrb_to_integer(mrb, val, "to_i"); - } - return tmp; -} - -MRB_API mrb_value -mrb_Integer(mrb_state *mrb, mrb_value val) -{ - return mrb_convert_to_integer(mrb, val, 0); + /* to raise TypeError */ + return mrb_to_int(mrb, val); } +#ifndef MRB_NO_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; @@ -582,19 +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/opcode.h b/src/opcode.h index 2446f92ed..fe4d17a21 100644 --- a/src/opcode.h +++ b/src/opcode.h @@ -1,2 +1,2 @@ /* this header file is to be removed soon. */ -#include "mruby/opcode.h" +#include <mruby/opcode.h> diff --git a/src/parse.y b/src/parse.y deleted file mode 100644 index f8e1ff9aa..000000000 --- a/src/parse.y +++ /dev/null @@ -1,6407 +0,0 @@ -/* -** parse.y - mruby parser -** -** See Copyright Notice in mruby.h -*/ - -%{ -#undef PARSER_DEBUG - -#define YYDEBUG 1 -#define YYERROR_VERBOSE 1 -/* - * Force yacc to use our memory management. This is a little evil because - * the macros assume that "parser_state *p" is in scope - */ -#define YYMALLOC(n) mrb_malloc(p->mrb, (n)) -#define YYFREE(o) mrb_free(p->mrb, (o)) -#define YYSTACK_USE_ALLOCA 0 - -#include <ctype.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include "mruby.h" -#include "mruby/compile.h" -#include "mruby/proc.h" -#include "mruby/error.h" -#include "node.h" -#include "mrb_throw.h" - -#define YYLEX_PARAM p - -typedef mrb_ast_node node; -typedef struct mrb_parser_state parser_state; -typedef struct mrb_parser_heredoc_info parser_heredoc_info; - -static int yyparse(parser_state *p); -static int yylex(void *lval, parser_state *p); -static void yyerror(parser_state *p, const char *s); -static void yywarn(parser_state *p, const char *s); -static void yywarning(parser_state *p, const char *s); -static void backref_error(parser_state *p, node *n); -static void tokadd(parser_state *p, int32_t c); - -#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) - -typedef unsigned int stack_type; - -#define BITSTACK_PUSH(stack, n) ((stack) = ((stack)<<1)|((n)&1)) -#define BITSTACK_POP(stack) ((stack) = (stack) >> 1) -#define BITSTACK_LEXPOP(stack) ((stack) = ((stack) >> 1) | ((stack) & 1)) -#define BITSTACK_SET_P(stack) ((stack)&1) - -#define COND_PUSH(n) BITSTACK_PUSH(p->cond_stack, (n)) -#define COND_POP() BITSTACK_POP(p->cond_stack) -#define COND_LEXPOP() BITSTACK_LEXPOP(p->cond_stack) -#define COND_P() BITSTACK_SET_P(p->cond_stack) - -#define CMDARG_PUSH(n) BITSTACK_PUSH(p->cmdarg_stack, (n)) -#define CMDARG_POP() BITSTACK_POP(p->cmdarg_stack) -#define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack) -#define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack) - -#define SET_LINENO(c,n) ((c)->lineno = (n)) -#define NODE_LINENO(c,n) do {\ - if (n) {\ - (c)->filename_index = (n)->filename_index;\ - (c)->lineno = (n)->lineno;\ - }\ -} while (0) - -#define sym(x) ((mrb_sym)(intptr_t)(x)) -#define nsym(x) ((node*)(intptr_t)(x)) - -static inline mrb_sym -intern_cstr_gen(parser_state *p, const char *s) -{ - return mrb_intern_cstr(p->mrb, s); -} -#define intern_cstr(s) intern_cstr_gen(p,(s)) - -static inline mrb_sym -intern_gen(parser_state *p, const char *s, size_t len) -{ - return mrb_intern(p->mrb, s, len); -} -#define intern(s,len) intern_gen(p,(s),(len)) - -static inline mrb_sym -intern_gen_c(parser_state *p, const char c) -{ - return mrb_intern(p->mrb, &c, 1); -} -#define intern_c(c) intern_gen_c(p,(c)) - -static void -cons_free_gen(parser_state *p, node *cons) -{ - cons->cdr = p->cells; - p->cells = cons; -} -#define cons_free(c) cons_free_gen(p, (c)) - -static void* -parser_palloc(parser_state *p, size_t size) -{ - void *m = mrb_pool_alloc(p->pool, size); - - if (!m) { - MRB_THROW(p->jmp); - } - return m; -} - -static node* -cons_gen(parser_state *p, node *car, node *cdr) -{ - node *c; - - if (p->cells) { - c = p->cells; - p->cells = p->cells->cdr; - } - else { - c = (node *)parser_palloc(p, sizeof(mrb_ast_node)); - } - - c->car = car; - c->cdr = cdr; - c->lineno = p->lineno; - c->filename_index = p->current_filename_index; - return c; -} -#define cons(a,b) cons_gen(p,(a),(b)) - -static node* -list1_gen(parser_state *p, node *a) -{ - return cons(a, 0); -} -#define list1(a) list1_gen(p, (a)) - -static node* -list2_gen(parser_state *p, node *a, node *b) -{ - return cons(a, cons(b,0)); -} -#define list2(a,b) list2_gen(p, (a),(b)) - -static node* -list3_gen(parser_state *p, node *a, node *b, node *c) -{ - return cons(a, cons(b, cons(c,0))); -} -#define list3(a,b,c) list3_gen(p, (a),(b),(c)) - -static node* -list4_gen(parser_state *p, node *a, node *b, node *c, node *d) -{ - return cons(a, cons(b, cons(c, cons(d, 0)))); -} -#define list4(a,b,c,d) list4_gen(p, (a),(b),(c),(d)) - -static node* -list5_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e) -{ - return cons(a, cons(b, cons(c, cons(d, cons(e, 0))))); -} -#define list5(a,b,c,d,e) list5_gen(p, (a),(b),(c),(d),(e)) - -static node* -list6_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e, node *f) -{ - return cons(a, cons(b, cons(c, cons(d, cons(e, cons(f, 0)))))); -} -#define list6(a,b,c,d,e,f) list6_gen(p, (a),(b),(c),(d),(e),(f)) - -static node* -append_gen(parser_state *p, node *a, node *b) -{ - node *c = a; - - if (!a) return b; - while (c->cdr) { - c = c->cdr; - } - if (b) { - c->cdr = b; - } - return a; -} -#define append(a,b) append_gen(p,(a),(b)) -#define push(a,b) append_gen(p,(a),list1(b)) - -static char* -parser_strndup(parser_state *p, const char *s, size_t len) -{ - char *b = (char *)parser_palloc(p, len+1); - - memcpy(b, s, len); - b[len] = '\0'; - return b; -} -#define strndup(s,len) parser_strndup(p, s, len) - -static char* -parser_strdup(parser_state *p, const char *s) -{ - return parser_strndup(p, s, strlen(s)); -} -#undef strdup -#define strdup(s) parser_strdup(p, s) - -/* xxx ----------------------------- */ - -static node* -local_switch(parser_state *p) -{ - node *prev = p->locals; - - p->locals = cons(0, 0); - return prev; -} - -static void -local_resume(parser_state *p, node *prev) -{ - p->locals = prev; -} - -static void -local_nest(parser_state *p) -{ - p->locals = cons(0, p->locals); -} - -static void -local_unnest(parser_state *p) -{ - p->locals = p->locals->cdr; -} - -static mrb_bool -local_var_p(parser_state *p, mrb_sym sym) -{ - node *l = p->locals; - - while (l) { - node *n = l->car; - while (n) { - if (sym(n->car) == sym) return TRUE; - n = n->cdr; - } - l = l->cdr; - } - return FALSE; -} - -static void -local_add_f(parser_state *p, mrb_sym sym) -{ - p->locals->car = push(p->locals->car, nsym(sym)); -} - -static void -local_add(parser_state *p, mrb_sym sym) -{ - if (!local_var_p(p, sym)) { - local_add_f(p, sym); - } -} - -/* (:scope (vars..) (prog...)) */ -static node* -new_scope(parser_state *p, node *body) -{ - return cons((node*)NODE_SCOPE, cons(p->locals->car, body)); -} - -/* (:begin prog...) */ -static node* -new_begin(parser_state *p, node *body) -{ - if (body) { - return list2((node*)NODE_BEGIN, body); - } - return cons((node*)NODE_BEGIN, 0); -} - -#define newline_node(n) (n) - -/* (:rescue body rescue else) */ -static node* -new_rescue(parser_state *p, node *body, node *resq, node *els) -{ - return list4((node*)NODE_RESCUE, body, resq, els); -} - -/* (:ensure body ensure) */ -static node* -new_ensure(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_ENSURE, cons(a, cons(0, b))); -} - -/* (:nil) */ -static node* -new_nil(parser_state *p) -{ - return list1((node*)NODE_NIL); -} - -/* (:true) */ -static node* -new_true(parser_state *p) -{ - return list1((node*)NODE_TRUE); -} - -/* (:false) */ -static node* -new_false(parser_state *p) -{ - return list1((node*)NODE_FALSE); -} - -/* (:alias new old) */ -static node* -new_alias(parser_state *p, mrb_sym a, mrb_sym b) -{ - return cons((node*)NODE_ALIAS, cons(nsym(a), nsym(b))); -} - -/* (:if cond then else) */ -static node* -new_if(parser_state *p, node *a, node *b, node *c) -{ - return list4((node*)NODE_IF, a, b, c); -} - -/* (:unless cond then else) */ -static node* -new_unless(parser_state *p, node *a, node *b, node *c) -{ - return list4((node*)NODE_IF, a, c, b); -} - -/* (:while cond body) */ -static node* -new_while(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_WHILE, cons(a, b)); -} - -/* (:until cond body) */ -static node* -new_until(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_UNTIL, cons(a, b)); -} - -/* (:for var obj body) */ -static node* -new_for(parser_state *p, node *v, node *o, node *b) -{ - return list4((node*)NODE_FOR, v, o, b); -} - -/* (:case a ((when ...) body) ((when...) body)) */ -static node* -new_case(parser_state *p, node *a, node *b) -{ - node *n = list2((node*)NODE_CASE, a); - node *n2 = n; - - while (n2->cdr) { - n2 = n2->cdr; - } - n2->cdr = b; - return n; -} - -/* (:postexe a) */ -static node* -new_postexe(parser_state *p, node *a) -{ - return cons((node*)NODE_POSTEXE, a); -} - -/* (:self) */ -static node* -new_self(parser_state *p) -{ - return list1((node*)NODE_SELF); -} - -/* (:call a b c) */ -static node* -new_call(parser_state *p, node *a, mrb_sym b, node *c) -{ - node *n = list4((node*)NODE_CALL, a, nsym(b), c); - NODE_LINENO(n, a); - return n; -} - -/* (:fcall self mid args) */ -static node* -new_fcall(parser_state *p, mrb_sym b, node *c) -{ - node *n = new_self(p); - NODE_LINENO(n, c); - n = list4((node*)NODE_FCALL, n, nsym(b), c); - NODE_LINENO(n, c); - return n; -} - -/* (:super . c) */ -static node* -new_super(parser_state *p, node *c) -{ - return cons((node*)NODE_SUPER, c); -} - -/* (:zsuper) */ -static node* -new_zsuper(parser_state *p) -{ - return list1((node*)NODE_ZSUPER); -} - -/* (:yield . c) */ -static node* -new_yield(parser_state *p, node *c) -{ - if (c) { - if (c->cdr) { - yyerror(p, "both block arg and actual block given"); - } - return cons((node*)NODE_YIELD, c->car); - } - return cons((node*)NODE_YIELD, 0); -} - -/* (:return . c) */ -static node* -new_return(parser_state *p, node *c) -{ - return cons((node*)NODE_RETURN, c); -} - -/* (:break . c) */ -static node* -new_break(parser_state *p, node *c) -{ - return cons((node*)NODE_BREAK, c); -} - -/* (:next . c) */ -static node* -new_next(parser_state *p, node *c) -{ - return cons((node*)NODE_NEXT, c); -} - -/* (:redo) */ -static node* -new_redo(parser_state *p) -{ - return list1((node*)NODE_REDO); -} - -/* (:retry) */ -static node* -new_retry(parser_state *p) -{ - return list1((node*)NODE_RETRY); -} - -/* (:dot2 a b) */ -static node* -new_dot2(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_DOT2, cons(a, b)); -} - -/* (:dot3 a b) */ -static node* -new_dot3(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_DOT3, cons(a, b)); -} - -/* (:colon2 b c) */ -static node* -new_colon2(parser_state *p, node *b, mrb_sym c) -{ - return cons((node*)NODE_COLON2, cons(b, nsym(c))); -} - -/* (:colon3 . c) */ -static node* -new_colon3(parser_state *p, mrb_sym c) -{ - return cons((node*)NODE_COLON3, nsym(c)); -} - -/* (:and a b) */ -static node* -new_and(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_AND, cons(a, b)); -} - -/* (:or a b) */ -static node* -new_or(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_OR, cons(a, b)); -} - -/* (:array a...) */ -static node* -new_array(parser_state *p, node *a) -{ - return cons((node*)NODE_ARRAY, a); -} - -/* (:splat . a) */ -static node* -new_splat(parser_state *p, node *a) -{ - return cons((node*)NODE_SPLAT, a); -} - -/* (:hash (k . v) (k . v)...) */ -static node* -new_hash(parser_state *p, node *a) -{ - return cons((node*)NODE_HASH, a); -} - -/* (:sym . a) */ -static node* -new_sym(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_SYM, nsym(sym)); -} - -static mrb_sym -new_strsym(parser_state *p, node* str) -{ - const char *s = (const char*)str->cdr->car; - size_t len = (size_t)str->cdr->cdr; - - return mrb_intern(p->mrb, s, len); -} - -/* (:lvar . a) */ -static node* -new_lvar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_LVAR, nsym(sym)); -} - -/* (:gvar . a) */ -static node* -new_gvar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_GVAR, nsym(sym)); -} - -/* (:ivar . a) */ -static node* -new_ivar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_IVAR, nsym(sym)); -} - -/* (:cvar . a) */ -static node* -new_cvar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_CVAR, nsym(sym)); -} - -/* (:const . a) */ -static node* -new_const(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_CONST, nsym(sym)); -} - -/* (:undef a...) */ -static node* -new_undef(parser_state *p, mrb_sym sym) -{ - return list2((node*)NODE_UNDEF, nsym(sym)); -} - -/* (:class class super body) */ -static node* -new_class(parser_state *p, node *c, node *s, node *b) -{ - return list4((node*)NODE_CLASS, c, s, cons(p->locals->car, b)); -} - -/* (:sclass obj body) */ -static node* -new_sclass(parser_state *p, node *o, node *b) -{ - return list3((node*)NODE_SCLASS, o, cons(p->locals->car, b)); -} - -/* (:module module body) */ -static node* -new_module(parser_state *p, node *m, node *b) -{ - return list3((node*)NODE_MODULE, m, cons(p->locals->car, b)); -} - -/* (:def m lv (arg . body)) */ -static node* -new_def(parser_state *p, mrb_sym m, node *a, node *b) -{ - return list5((node*)NODE_DEF, nsym(m), p->locals->car, a, b); -} - -/* (:sdef obj m lv (arg . body)) */ -static node* -new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b) -{ - return list6((node*)NODE_SDEF, o, nsym(m), p->locals->car, a, b); -} - -/* (:arg . sym) */ -static node* -new_arg(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_ARG, nsym(sym)); -} - -/* (m o r m2 b) */ -/* m: (a b c) */ -/* o: ((a . e1) (b . e2)) */ -/* r: a */ -/* m2: (a b c) */ -/* b: a */ -static node* -new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, mrb_sym blk) -{ - node *n; - - n = cons(m2, nsym(blk)); - n = cons(nsym(rest), n); - n = cons(opt, n); - return cons(m, n); -} - -/* (:block_arg . a) */ -static node* -new_block_arg(parser_state *p, node *a) -{ - return cons((node*)NODE_BLOCK_ARG, a); -} - -/* (:block arg body) */ -static node* -new_block(parser_state *p, node *a, node *b) -{ - return list4((node*)NODE_BLOCK, p->locals->car, a, b); -} - -/* (:lambda arg body) */ -static node* -new_lambda(parser_state *p, node *a, node *b) -{ - return list4((node*)NODE_LAMBDA, p->locals->car, a, b); -} - -/* (:asgn lhs rhs) */ -static node* -new_asgn(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_ASGN, cons(a, b)); -} - -/* (:masgn mlhs=(pre rest post) mrhs) */ -static node* -new_masgn(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_MASGN, cons(a, b)); -} - -/* (:asgn lhs rhs) */ -static node* -new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b) -{ - return list4((node*)NODE_OP_ASGN, a, nsym(op), b); -} - -/* (:int . i) */ -static node* -new_int(parser_state *p, const char *s, int base) -{ - return list3((node*)NODE_INT, (node*)strdup(s), (node*)(intptr_t)base); -} - -/* (:float . i) */ -static node* -new_float(parser_state *p, const char *s) -{ - return cons((node*)NODE_FLOAT, (node*)strdup(s)); -} - -/* (:str . (s . len)) */ -static node* -new_str(parser_state *p, const char *s, int len) -{ - return cons((node*)NODE_STR, cons((node*)strndup(s, len), (node*)(intptr_t)len)); -} - -/* (:dstr . a) */ -static node* -new_dstr(parser_state *p, node *a) -{ - return cons((node*)NODE_DSTR, a); -} - -/* (:str . (s . len)) */ -static node* -new_xstr(parser_state *p, const char *s, int len) -{ - return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), (node*)(intptr_t)len)); -} - -/* (:xstr . a) */ -static node* -new_dxstr(parser_state *p, node *a) -{ - return cons((node*)NODE_DXSTR, a); -} - -/* (:dsym . a) */ -static node* -new_dsym(parser_state *p, node *a) -{ - return cons((node*)NODE_DSYM, new_dstr(p, a)); -} - -/* (:str . (a . a)) */ -static node* -new_regx(parser_state *p, const char *p1, const char* p2) -{ - return cons((node*)NODE_REGX, cons((node*)p1, (node*)p2)); -} - -/* (:dregx . a) */ -static node* -new_dregx(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_DREGX, cons(a, b)); -} - -/* (:backref . n) */ -static node* -new_back_ref(parser_state *p, int n) -{ - return cons((node*)NODE_BACK_REF, (node*)(intptr_t)n); -} - -/* (:nthref . n) */ -static node* -new_nth_ref(parser_state *p, int n) -{ - return cons((node*)NODE_NTH_REF, (node*)(intptr_t)n); -} - -/* (:heredoc . a) */ -static node* -new_heredoc(parser_state *p) -{ - parser_heredoc_info *inf = (parser_heredoc_info *)parser_palloc(p, sizeof(parser_heredoc_info)); - return cons((node*)NODE_HEREDOC, (node*)inf); -} - -static void -new_bv(parser_state *p, mrb_sym id) -{ -} - -static node* -new_literal_delim(parser_state *p) -{ - return cons((node*)NODE_LITERAL_DELIM, 0); -} - -/* (:words . a) */ -static node* -new_words(parser_state *p, node *a) -{ - return cons((node*)NODE_WORDS, a); -} - -/* (:symbols . a) */ -static node* -new_symbols(parser_state *p, node *a) -{ - return cons((node*)NODE_SYMBOLS, a); -} - -/* xxx ----------------------------- */ - -/* (:call a op) */ -static node* -call_uni_op(parser_state *p, node *recv, const char *m) -{ - return new_call(p, recv, intern_cstr(m), 0); -} - -/* (:call a op b) */ -static node* -call_bin_op(parser_state *p, node *recv, const char *m, node *arg1) -{ - return new_call(p, recv, intern_cstr(m), list1(list1(arg1))); -} - -static void -args_with_block(parser_state *p, node *a, node *b) -{ - if (b) { - if (a->cdr) { - yyerror(p, "both block arg and actual block given"); - } - a->cdr = b; - } -} - -static void -call_with_block(parser_state *p, node *a, node *b) -{ - node *n; - - if (a->car == (node*)NODE_SUPER || - a->car == (node*)NODE_ZSUPER) { - if (!a->cdr) a->cdr = cons(0, b); - else { - args_with_block(p, a->cdr, b); - } - } - else { - n = a->cdr->cdr->cdr; - if (!n->car) n->car = cons(0, b); - else { - args_with_block(p, n->car, b); - } - } -} - -static node* -negate_lit(parser_state *p, node *n) -{ - return cons((node*)NODE_NEGATE, n); -} - -static node* -cond(node *n) -{ - return n; -} - -static node* -ret_args(parser_state *p, node *n) -{ - if (n->cdr) { - yyerror(p, "block argument should not be given"); - return NULL; - } - if (!n->car->cdr) return n->car->car; - return new_array(p, n->car); -} - -static void -assignable(parser_state *p, node *lhs) -{ - if ((int)(intptr_t)lhs->car == NODE_LVAR) { - local_add(p, sym(lhs->cdr)); - } -} - -static node* -var_reference(parser_state *p, node *lhs) -{ - node *n; - - if ((int)(intptr_t)lhs->car == NODE_LVAR) { - if (!local_var_p(p, sym(lhs->cdr))) { - n = new_fcall(p, sym(lhs->cdr), 0); - cons_free(lhs); - return n; - } - } - - return lhs; -} - -typedef enum mrb_string_type string_type; - -static node* -new_strterm(parser_state *p, string_type type, int term, int paren) -{ - return cons((node*)(intptr_t)type, cons((node*)0, cons((node*)(intptr_t)paren, (node*)(intptr_t)term))); -} - -static void -end_strterm(parser_state *p) -{ - cons_free(p->lex_strterm->cdr->cdr); - cons_free(p->lex_strterm->cdr); - cons_free(p->lex_strterm); - p->lex_strterm = NULL; -} - -static parser_heredoc_info * -parsing_heredoc_inf(parser_state *p) -{ - node *nd = p->parsing_heredoc; - if (nd == NULL) - return NULL; - /* mrb_assert(nd->car->car == NODE_HEREDOC); */ - return (parser_heredoc_info*)nd->car->cdr; -} - -static void -heredoc_treat_nextline(parser_state *p) -{ - if (p->heredocs_from_nextline == NULL) - return; - if (p->parsing_heredoc == NULL) { - node *n; - p->parsing_heredoc = p->heredocs_from_nextline; - p->lex_strterm_before_heredoc = p->lex_strterm; - p->lex_strterm = new_strterm(p, parsing_heredoc_inf(p)->type, 0, 0); - n = p->all_heredocs; - if (n) { - while (n->cdr) - n = n->cdr; - n->cdr = p->parsing_heredoc; - } - else { - p->all_heredocs = p->parsing_heredoc; - } - } - else { - node *n, *m; - m = p->heredocs_from_nextline; - while (m->cdr) - m = m->cdr; - n = p->all_heredocs; - mrb_assert(n != NULL); - if (n == p->parsing_heredoc) { - m->cdr = n; - p->all_heredocs = p->heredocs_from_nextline; - p->parsing_heredoc = p->heredocs_from_nextline; - } - else { - while (n->cdr != p->parsing_heredoc) { - n = n->cdr; - mrb_assert(n != NULL); - } - m->cdr = n->cdr; - n->cdr = p->heredocs_from_nextline; - p->parsing_heredoc = p->heredocs_from_nextline; - } - } - p->heredocs_from_nextline = NULL; -} - -static void -heredoc_end(parser_state *p) -{ - p->parsing_heredoc = p->parsing_heredoc->cdr; - if (p->parsing_heredoc == NULL) { - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - end_strterm(p); - p->lex_strterm = p->lex_strterm_before_heredoc; - p->lex_strterm_before_heredoc = NULL; - p->heredoc_end_now = TRUE; - } - else { - /* next heredoc */ - p->lex_strterm->car = (node*)(intptr_t)parsing_heredoc_inf(p)->type; - } -} -#define is_strterm_type(p,str_func) ((int)(intptr_t)((p)->lex_strterm->car) & (str_func)) - -/* xxx ----------------------------- */ - -%} - -%pure-parser -%parse-param {parser_state *p} -%lex-param {parser_state *p} - -%union { - node *nd; - mrb_sym id; - int num; - stack_type stack; - const struct vtable *vars; -} - -%token <num> - keyword_class - keyword_module - keyword_def - keyword_begin - keyword_if - keyword_unless - keyword_while - keyword_until - keyword_for - -%token - keyword_undef - keyword_rescue - keyword_ensure - keyword_end - keyword_then - keyword_elsif - keyword_else - keyword_case - keyword_when - keyword_break - keyword_next - keyword_redo - keyword_retry - keyword_in - keyword_do - keyword_do_cond - keyword_do_block - keyword_do_LAMBDA - keyword_return - keyword_yield - keyword_super - keyword_self - keyword_nil - keyword_true - keyword_false - keyword_and - keyword_or - keyword_not - modifier_if - modifier_unless - modifier_while - modifier_until - modifier_rescue - keyword_alias - keyword_BEGIN - keyword_END - keyword__LINE__ - keyword__FILE__ - keyword__ENCODING__ - -%token <id> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL -%token <nd> tINTEGER tFLOAT tCHAR tXSTRING tREGEXP -%token <nd> tSTRING tSTRING_PART tSTRING_MID -%token <nd> tNTH_REF tBACK_REF -%token <num> tREGEXP_END - -%type <nd> singleton string string_rep string_interp xstring regexp -%type <nd> literal numeric cpath symbol -%type <nd> top_compstmt top_stmts top_stmt -%type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call -%type <nd> expr_value arg_value primary_value -%type <nd> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure -%type <nd> args call_args opt_call_args -%type <nd> paren_args opt_paren_args variable -%type <nd> command_args aref_args opt_block_arg block_arg var_ref var_lhs -%type <nd> command_asgn mrhs superclass block_call block_command -%type <nd> f_block_optarg f_block_opt -%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs -%type <nd> assoc_list assocs assoc undef_list backref for_var -%type <nd> block_param opt_block_param block_param_def f_opt -%type <nd> bv_decls opt_bv_decl bvar f_larglist lambda_body -%type <nd> brace_block cmd_brace_block do_block lhs none f_bad_arg -%type <nd> mlhs mlhs_list mlhs_post mlhs_basic mlhs_item mlhs_node mlhs_inner -%type <id> fsym sym basic_symbol operation operation2 operation3 -%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn -%type <nd> heredoc words symbols - -%token tUPLUS /* unary+ */ -%token tUMINUS /* unary- */ -%token tPOW /* ** */ -%token tCMP /* <=> */ -%token tEQ /* == */ -%token tEQQ /* === */ -%token tNEQ /* != */ -%token tGEQ /* >= */ -%token tLEQ /* <= */ -%token tANDOP tOROP /* && and || */ -%token tMATCH tNMATCH /* =~ and !~ */ -%token tDOT2 tDOT3 /* .. and ... */ -%token tAREF tASET /* [] and []= */ -%token tLSHFT tRSHFT /* << and >> */ -%token tCOLON2 /* :: */ -%token tCOLON3 /* :: at EXPR_BEG */ -%token <id> tOP_ASGN /* +=, -= etc. */ -%token tASSOC /* => */ -%token tLPAREN /* ( */ -%token tLPAREN_ARG /* ( */ -%token tRPAREN /* ) */ -%token tLBRACK /* [ */ -%token tLBRACE /* { */ -%token tLBRACE_ARG /* { */ -%token tSTAR /* * */ -%token tAMPER /* & */ -%token tLAMBDA /* -> */ -%token tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG -%token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG -%token <nd> tHEREDOC_BEG /* <<, <<- */ -%token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM -%token <nd> tHD_STRING_PART tHD_STRING_MID - -/* - * precedence table - */ - -%nonassoc tLOWEST -%nonassoc tLBRACE_ARG - -%nonassoc modifier_if modifier_unless modifier_while modifier_until -%left keyword_or keyword_and -%right keyword_not -%right '=' tOP_ASGN -%left modifier_rescue -%right '?' ':' -%nonassoc tDOT2 tDOT3 -%left tOROP -%left tANDOP -%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH -%left '>' tGEQ '<' tLEQ -%left '|' '^' -%left '&' -%left tLSHFT tRSHFT -%left '+' '-' -%left '*' '/' '%' -%right tUMINUS_NUM tUMINUS -%right tPOW -%right '!' '~' tUPLUS - -%nonassoc idNULL -%nonassoc idRespond_to -%nonassoc idIFUNC -%nonassoc idCFUNC -%nonassoc id_core_set_method_alias -%nonassoc id_core_set_variable_alias -%nonassoc id_core_undef_method -%nonassoc id_core_define_method -%nonassoc id_core_define_singleton_method -%nonassoc id_core_set_postexe - -%token tLAST_TOKEN - -%% -program : { - p->lstate = EXPR_BEG; - if (!p->locals) p->locals = cons(0,0); - } - top_compstmt - { - p->tree = new_scope(p, $2); - NODE_LINENO(p->tree, $2); - } - ; - -top_compstmt : top_stmts opt_terms - { - $$ = $1; - } - ; - -top_stmts : none - { - $$ = new_begin(p, 0); - } - | top_stmt - { - $$ = new_begin(p, $1); - NODE_LINENO($$, $1); - } - | top_stmts terms top_stmt - { - $$ = push($1, newline_node($3)); - } - | error top_stmt - { - $$ = new_begin(p, 0); - } - ; - -top_stmt : stmt - | keyword_BEGIN - { - $<nd>$ = local_switch(p); - } - '{' top_compstmt '}' - { - yyerror(p, "BEGIN not supported"); - local_resume(p, $<nd>2); - $$ = 0; - } - ; - -bodystmt : compstmt - opt_rescue - opt_else - opt_ensure - { - if ($2) { - $$ = new_rescue(p, $1, $2, $3); - NODE_LINENO($$, $1); - } - else if ($3) { - yywarn(p, "else without rescue is useless"); - $$ = push($1, $3); - } - else { - $$ = $1; - } - if ($4) { - if ($$) { - $$ = new_ensure(p, $$, $4); - } - else { - $$ = push($4, new_nil(p)); - } - } - } - ; - -compstmt : stmts opt_terms - { - $$ = $1; - } - ; - -stmts : none - { - $$ = new_begin(p, 0); - } - | stmt - { - $$ = new_begin(p, $1); - NODE_LINENO($$, $1); - } - | stmts terms stmt - { - $$ = push($1, newline_node($3)); - } - | error stmt - { - $$ = new_begin(p, $2); - } - ; - -stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym - { - $$ = new_alias(p, $2, $4); - } - | keyword_undef undef_list - { - $$ = $2; - } - | stmt modifier_if expr_value - { - $$ = new_if(p, cond($3), $1, 0); - } - | stmt modifier_unless expr_value - { - $$ = new_unless(p, cond($3), $1, 0); - } - | stmt modifier_while expr_value - { - $$ = new_while(p, cond($3), $1); - } - | stmt modifier_until expr_value - { - $$ = new_until(p, cond($3), $1); - } - | stmt modifier_rescue stmt - { - $$ = new_rescue(p, $1, list1(list3(0, 0, $3)), 0); - } - | keyword_END '{' compstmt '}' - { - yyerror(p, "END not suported"); - $$ = new_postexe(p, $3); - } - | command_asgn - | mlhs '=' command_call - { - $$ = new_masgn(p, $1, $3); - } - | var_lhs tOP_ASGN command_call - { - $$ = new_op_asgn(p, $1, $2, $3); - } - | primary_value '[' opt_call_args rbracket tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); - } - | primary_value '.' tIDENTIFIER tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value '.' tCONSTANT tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call - { - yyerror(p, "constant re-assignment"); - $$ = 0; - } - | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | backref tOP_ASGN command_call - { - backref_error(p, $1); - $$ = new_begin(p, 0); - } - | lhs '=' mrhs - { - $$ = new_asgn(p, $1, new_array(p, $3)); - } - | mlhs '=' arg_value - { - $$ = new_masgn(p, $1, $3); - } - | mlhs '=' mrhs - { - $$ = new_masgn(p, $1, new_array(p, $3)); - } - | expr - ; - -command_asgn : lhs '=' command_call - { - $$ = new_asgn(p, $1, $3); - } - | lhs '=' command_asgn - { - $$ = new_asgn(p, $1, $3); - } - ; - - -expr : command_call - | expr keyword_and expr - { - $$ = new_and(p, $1, $3); - } - | expr keyword_or expr - { - $$ = new_or(p, $1, $3); - } - | keyword_not opt_nl expr - { - $$ = call_uni_op(p, cond($3), "!"); - } - | '!' command_call - { - $$ = call_uni_op(p, cond($2), "!"); - } - | arg - ; - -expr_value : expr - { - if (!$1) $$ = new_nil(p); - else $$ = $1; - } - ; - -command_call : command - | block_command - ; - -block_command : block_call - | block_call dot_or_colon operation2 command_args - ; - -cmd_brace_block : tLBRACE_ARG - { - local_nest(p); - } - opt_block_param - compstmt - '}' - { - $$ = new_block(p, $3, $4); - local_unnest(p); - } - ; - -command : operation command_args %prec tLOWEST - { - $$ = new_fcall(p, $1, $2); - } - | operation command_args cmd_brace_block - { - args_with_block(p, $2, $3); - $$ = new_fcall(p, $1, $2); - } - | primary_value '.' operation2 command_args %prec tLOWEST - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value '.' operation2 command_args cmd_brace_block - { - args_with_block(p, $4, $5); - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation2 command_args %prec tLOWEST - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation2 command_args cmd_brace_block - { - args_with_block(p, $4, $5); - $$ = new_call(p, $1, $3, $4); - } - | keyword_super command_args - { - $$ = new_super(p, $2); - } - | keyword_yield command_args - { - $$ = new_yield(p, $2); - } - | keyword_return call_args - { - $$ = new_return(p, ret_args(p, $2)); - } - | keyword_break call_args - { - $$ = new_break(p, ret_args(p, $2)); - } - | keyword_next call_args - { - $$ = new_next(p, ret_args(p, $2)); - } - ; - -mlhs : mlhs_basic - { - $$ = $1; - } - | tLPAREN mlhs_inner rparen - { - $$ = $2; - } - ; - -mlhs_inner : mlhs_basic - | tLPAREN mlhs_inner rparen - { - $$ = $2; - } - ; - -mlhs_basic : mlhs_list - { - $$ = list1($1); - } - | mlhs_list mlhs_item - { - $$ = list1(push($1,$2)); - } - | mlhs_list tSTAR mlhs_node - { - $$ = list2($1, $3); - } - | mlhs_list tSTAR mlhs_node ',' mlhs_post - { - $$ = list3($1, $3, $5); - } - | mlhs_list tSTAR - { - $$ = list2($1, new_nil(p)); - } - | mlhs_list tSTAR ',' mlhs_post - { - $$ = list3($1, new_nil(p), $4); - } - | tSTAR mlhs_node - { - $$ = list2(0, $2); - } - | tSTAR mlhs_node ',' mlhs_post - { - $$ = list3(0, $2, $4); - } - | tSTAR - { - $$ = list2(0, new_nil(p)); - } - | tSTAR ',' mlhs_post - { - $$ = list3(0, new_nil(p), $3); - } - ; - -mlhs_item : mlhs_node - | tLPAREN mlhs_inner rparen - { - $$ = new_masgn(p, $2, NULL); - } - ; - -mlhs_list : mlhs_item ',' - { - $$ = list1($1); - } - | mlhs_list mlhs_item ',' - { - $$ = push($1, $2); - } - ; - -mlhs_post : mlhs_item - { - $$ = list1($1); - } - | mlhs_list mlhs_item - { - $$ = push($1, $2); - } - ; - -mlhs_node : variable - { - assignable(p, $1); - } - | primary_value '[' opt_call_args rbracket - { - $$ = new_call(p, $1, intern("[]",2), $3); - } - | primary_value '.' tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value '.' tCONSTANT - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon2(p, $1, $3); - } - | tCOLON3 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon3(p, $2); - } - | backref - { - backref_error(p, $1); - $$ = 0; - } - ; - -lhs : variable - { - assignable(p, $1); - } - | primary_value '[' opt_call_args rbracket - { - $$ = new_call(p, $1, intern("[]",2), $3); - } - | primary_value '.' tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value '.' tCONSTANT - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon2(p, $1, $3); - } - | tCOLON3 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon3(p, $2); - } - | backref - { - backref_error(p, $1); - $$ = 0; - } - ; - -cname : tIDENTIFIER - { - yyerror(p, "class/module name must be CONSTANT"); - } - | tCONSTANT - ; - -cpath : tCOLON3 cname - { - $$ = cons((node*)1, nsym($2)); - } - | cname - { - $$ = cons((node*)0, nsym($1)); - } - | primary_value tCOLON2 cname - { - $$ = cons($1, nsym($3)); - } - ; - -fname : tIDENTIFIER - | tCONSTANT - | tFID - | op - { - p->lstate = EXPR_ENDFN; - $$ = $1; - } - | reswords - { - p->lstate = EXPR_ENDFN; - $$ = $<id>1; - } - ; - -fsym : fname - | basic_symbol - ; - -undef_list : fsym - { - $$ = new_undef(p, $1); - } - | undef_list ',' {p->lstate = EXPR_FNAME;} fsym - { - $$ = push($1, nsym($4)); - } - ; - -op : '|' { $$ = intern_c('|'); } - | '^' { $$ = intern_c('^'); } - | '&' { $$ = intern_c('&'); } - | tCMP { $$ = intern("<=>",3); } - | tEQ { $$ = intern("==",2); } - | tEQQ { $$ = intern("===",3); } - | tMATCH { $$ = intern("=~",2); } - | tNMATCH { $$ = intern("!~",2); } - | '>' { $$ = intern_c('>'); } - | tGEQ { $$ = intern(">=",2); } - | '<' { $$ = intern_c('<'); } - | tLEQ { $$ = intern("<=",2); } - | tNEQ { $$ = intern("!=",2); } - | tLSHFT { $$ = intern("<<",2); } - | tRSHFT { $$ = intern(">>",2); } - | '+' { $$ = intern_c('+'); } - | '-' { $$ = intern_c('-'); } - | '*' { $$ = intern_c('*'); } - | tSTAR { $$ = intern_c('*'); } - | '/' { $$ = intern_c('/'); } - | '%' { $$ = intern_c('%'); } - | tPOW { $$ = intern("**",2); } - | '!' { $$ = intern_c('!'); } - | '~' { $$ = intern_c('~'); } - | tUPLUS { $$ = intern("+@",2); } - | tUMINUS { $$ = intern("-@",2); } - | tAREF { $$ = intern("[]",2); } - | tASET { $$ = intern("[]=",3); } - | '`' { $$ = intern_c('`'); } - ; - -reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__ - | keyword_BEGIN | keyword_END - | keyword_alias | keyword_and | keyword_begin - | keyword_break | keyword_case | keyword_class | keyword_def - | keyword_do | keyword_else | keyword_elsif - | keyword_end | keyword_ensure | keyword_false - | keyword_for | keyword_in | keyword_module | keyword_next - | keyword_nil | keyword_not | keyword_or | keyword_redo - | keyword_rescue | keyword_retry | keyword_return | keyword_self - | keyword_super | keyword_then | keyword_true | keyword_undef - | keyword_when | keyword_yield | keyword_if | keyword_unless - | keyword_while | keyword_until - ; - -arg : lhs '=' arg - { - $$ = new_asgn(p, $1, $3); - } - | lhs '=' arg modifier_rescue arg - { - $$ = new_asgn(p, $1, new_rescue(p, $3, list1(list3(0, 0, $5)), 0)); - } - | var_lhs tOP_ASGN arg - { - $$ = new_op_asgn(p, $1, $2, $3); - } - | var_lhs tOP_ASGN arg modifier_rescue arg - { - $$ = new_op_asgn(p, $1, $2, new_rescue(p, $3, list1(list3(0, 0, $5)), 0)); - } - | primary_value '[' opt_call_args rbracket tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); - } - | primary_value '.' tIDENTIFIER tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value '.' tCONSTANT tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value tCOLON2 tCONSTANT tOP_ASGN arg - { - yyerror(p, "constant re-assignment"); - $$ = new_begin(p, 0); - } - | tCOLON3 tCONSTANT tOP_ASGN arg - { - yyerror(p, "constant re-assignment"); - $$ = new_begin(p, 0); - } - | backref tOP_ASGN arg - { - backref_error(p, $1); - $$ = new_begin(p, 0); - } - | arg tDOT2 arg - { - $$ = new_dot2(p, $1, $3); - } - | arg tDOT3 arg - { - $$ = new_dot3(p, $1, $3); - } - | arg '+' arg - { - $$ = call_bin_op(p, $1, "+", $3); - } - | arg '-' arg - { - $$ = call_bin_op(p, $1, "-", $3); - } - | arg '*' arg - { - $$ = call_bin_op(p, $1, "*", $3); - } - | arg '/' arg - { - $$ = call_bin_op(p, $1, "/", $3); - } - | arg '%' arg - { - $$ = call_bin_op(p, $1, "%", $3); - } - | arg tPOW arg - { - $$ = call_bin_op(p, $1, "**", $3); - } - | tUMINUS_NUM tINTEGER tPOW arg - { - $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@"); - } - | tUMINUS_NUM tFLOAT tPOW arg - { - $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@"); - } - | tUPLUS arg - { - $$ = call_uni_op(p, $2, "+@"); - } - | tUMINUS arg - { - $$ = call_uni_op(p, $2, "-@"); - } - | arg '|' arg - { - $$ = call_bin_op(p, $1, "|", $3); - } - | arg '^' arg - { - $$ = call_bin_op(p, $1, "^", $3); - } - | arg '&' arg - { - $$ = call_bin_op(p, $1, "&", $3); - } - | arg tCMP arg - { - $$ = call_bin_op(p, $1, "<=>", $3); - } - | arg '>' arg - { - $$ = call_bin_op(p, $1, ">", $3); - } - | arg tGEQ arg - { - $$ = call_bin_op(p, $1, ">=", $3); - } - | arg '<' arg - { - $$ = call_bin_op(p, $1, "<", $3); - } - | arg tLEQ arg - { - $$ = call_bin_op(p, $1, "<=", $3); - } - | arg tEQ arg - { - $$ = call_bin_op(p, $1, "==", $3); - } - | arg tEQQ arg - { - $$ = call_bin_op(p, $1, "===", $3); - } - | arg tNEQ arg - { - $$ = call_bin_op(p, $1, "!=", $3); - } - | arg tMATCH arg - { - $$ = call_bin_op(p, $1, "=~", $3); - } - | arg tNMATCH arg - { - $$ = call_bin_op(p, $1, "!~", $3); - } - | '!' arg - { - $$ = call_uni_op(p, cond($2), "!"); - } - | '~' arg - { - $$ = call_uni_op(p, cond($2), "~"); - } - | arg tLSHFT arg - { - $$ = call_bin_op(p, $1, "<<", $3); - } - | arg tRSHFT arg - { - $$ = call_bin_op(p, $1, ">>", $3); - } - | arg tANDOP arg - { - $$ = new_and(p, $1, $3); - } - | arg tOROP arg - { - $$ = new_or(p, $1, $3); - } - | arg '?' arg opt_nl ':' arg - { - $$ = new_if(p, cond($1), $3, $6); - } - | primary - { - $$ = $1; - } - ; - -arg_value : arg - { - $$ = $1; - if (!$$) $$ = new_nil(p); - } - ; - -aref_args : none - | args trailer - { - $$ = $1; - NODE_LINENO($$, $1); - } - | args ',' assocs trailer - { - $$ = push($1, new_hash(p, $3)); - } - | assocs trailer - { - $$ = cons(new_hash(p, $1), 0); - NODE_LINENO($$, $1); - } - ; - -paren_args : '(' opt_call_args rparen - { - $$ = $2; - } - ; - -opt_paren_args : none - | paren_args - ; - -opt_call_args : none - | call_args - | args ',' - { - $$ = cons($1,0); - NODE_LINENO($$, $1); - } - | args ',' assocs ',' - { - $$ = cons(push($1, new_hash(p, $3)), 0); - NODE_LINENO($$, $1); - } - | assocs ',' - { - $$ = cons(list1(new_hash(p, $1)), 0); - NODE_LINENO($$, $1); - } - ; - -call_args : command - { - $$ = cons(list1($1), 0); - NODE_LINENO($$, $1); - } - | args opt_block_arg - { - $$ = cons($1, $2); - NODE_LINENO($$, $1); - } - | assocs opt_block_arg - { - $$ = cons(list1(new_hash(p, $1)), $2); - NODE_LINENO($$, $1); - } - | args ',' assocs opt_block_arg - { - $$ = cons(push($1, new_hash(p, $3)), $4); - NODE_LINENO($$, $1); - } - | block_arg - { - $$ = cons(0, $1); - NODE_LINENO($$, $1); - } - ; - -command_args : { - $<stack>$ = p->cmdarg_stack; - CMDARG_PUSH(1); - } - call_args - { - p->cmdarg_stack = $<stack>1; - $$ = $2; - } - ; - -block_arg : tAMPER arg_value - { - $$ = new_block_arg(p, $2); - } - ; - -opt_block_arg : ',' block_arg - { - $$ = $2; - } - | none - { - $$ = 0; - } - ; - -args : arg_value - { - $$ = cons($1, 0); - NODE_LINENO($$, $1); - } - | tSTAR arg_value - { - $$ = cons(new_splat(p, $2), 0); - NODE_LINENO($$, $2); - } - | args ',' arg_value - { - $$ = push($1, $3); - } - | args ',' tSTAR arg_value - { - $$ = push($1, new_splat(p, $4)); - } - | args ',' heredoc_bodies arg_value - { - $$ = push($1, $4); - } - | args ',' heredoc_bodies tSTAR arg_value - { - $$ = push($1, new_splat(p, $5)); - } - ; - -mrhs : args ',' arg_value - { - $$ = push($1, $3); - } - | args ',' tSTAR arg_value - { - $$ = push($1, new_splat(p, $4)); - } - | tSTAR arg_value - { - $$ = list1(new_splat(p, $2)); - } - ; - -primary : literal - | string - | xstring - | regexp - | heredoc - | var_ref - | backref - | tFID - { - $$ = new_fcall(p, $1, 0); - } - | keyword_begin - { - $<stack>$ = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - bodystmt - keyword_end - { - p->cmdarg_stack = $<stack>2; - $$ = $3; - } - | tLPAREN_ARG - { - $<stack>$ = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - expr {p->lstate = EXPR_ENDARG;} rparen - { - p->cmdarg_stack = $<stack>2; - $$ = $3; - } - | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen - { - $$ = 0; - } - | tLPAREN compstmt ')' - { - $$ = $2; - } - | primary_value tCOLON2 tCONSTANT - { - $$ = new_colon2(p, $1, $3); - } - | tCOLON3 tCONSTANT - { - $$ = new_colon3(p, $2); - } - | tLBRACK aref_args ']' - { - $$ = new_array(p, $2); - NODE_LINENO($$, $2); - } - | tLBRACE assoc_list '}' - { - $$ = new_hash(p, $2); - NODE_LINENO($$, $2); - } - | keyword_return - { - $$ = new_return(p, 0); - } - | keyword_yield '(' call_args rparen - { - $$ = new_yield(p, $3); - } - | keyword_yield '(' rparen - { - $$ = new_yield(p, 0); - } - | keyword_yield - { - $$ = new_yield(p, 0); - } - | keyword_not '(' expr rparen - { - $$ = call_uni_op(p, cond($3), "!"); - } - | keyword_not '(' rparen - { - $$ = call_uni_op(p, new_nil(p), "!"); - } - | operation brace_block - { - $$ = new_fcall(p, $1, cons(0, $2)); - } - | method_call - | method_call brace_block - { - call_with_block(p, $1, $2); - $$ = $1; - } - | tLAMBDA - { - local_nest(p); - $<num>$ = p->lpar_beg; - p->lpar_beg = ++p->paren_nest; - } - f_larglist - lambda_body - { - p->lpar_beg = $<num>2; - $$ = new_lambda(p, $3, $4); - local_unnest(p); - } - | keyword_if expr_value then - compstmt - if_tail - keyword_end - { - $$ = new_if(p, cond($2), $4, $5); - SET_LINENO($$, $1); - } - | keyword_unless expr_value then - compstmt - opt_else - keyword_end - { - $$ = new_unless(p, cond($2), $4, $5); - SET_LINENO($$, $1); - } - | keyword_while {COND_PUSH(1);} expr_value do {COND_POP();} - compstmt - keyword_end - { - $$ = new_while(p, cond($3), $6); - SET_LINENO($$, $1); - } - | keyword_until {COND_PUSH(1);} expr_value do {COND_POP();} - compstmt - keyword_end - { - $$ = new_until(p, cond($3), $6); - SET_LINENO($$, $1); - } - | keyword_case expr_value opt_terms - case_body - keyword_end - { - $$ = new_case(p, $2, $4); - } - | keyword_case opt_terms case_body keyword_end - { - $$ = new_case(p, 0, $3); - } - | keyword_for for_var keyword_in - {COND_PUSH(1);} - expr_value do - {COND_POP();} - compstmt - keyword_end - { - $$ = new_for(p, $2, $5, $8); - SET_LINENO($$, $1); - } - | keyword_class - cpath superclass - { - if (p->in_def || p->in_single) - yyerror(p, "class definition in method body"); - $<nd>$ = local_switch(p); - } - bodystmt - keyword_end - { - $$ = new_class(p, $2, $3, $5); - SET_LINENO($$, $1); - local_resume(p, $<nd>4); - } - | keyword_class - tLSHFT expr - { - $<num>$ = p->in_def; - p->in_def = 0; - } - term - { - $<nd>$ = cons(local_switch(p), (node*)(intptr_t)p->in_single); - p->in_single = 0; - } - bodystmt - keyword_end - { - $$ = new_sclass(p, $3, $7); - SET_LINENO($$, $1); - local_resume(p, $<nd>6->car); - p->in_def = $<num>4; - p->in_single = (int)(intptr_t)$<nd>6->cdr; - } - | keyword_module - cpath - { - if (p->in_def || p->in_single) - yyerror(p, "module definition in method body"); - $<nd>$ = local_switch(p); - } - bodystmt - keyword_end - { - $$ = new_module(p, $2, $4); - SET_LINENO($$, $1); - local_resume(p, $<nd>3); - } - | keyword_def fname - { - $<stack>$ = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - { - p->in_def++; - $<nd>$ = local_switch(p); - } - f_arglist - bodystmt - keyword_end - { - $$ = new_def(p, $2, $5, $6); - SET_LINENO($$, $1); - local_resume(p, $<nd>4); - p->in_def--; - p->cmdarg_stack = $<stack>3; - } - | keyword_def singleton dot_or_colon - { - p->lstate = EXPR_FNAME; - $<stack>$ = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - fname - { - p->in_single++; - p->lstate = EXPR_ENDFN; /* force for args */ - $<nd>$ = local_switch(p); - } - f_arglist - bodystmt - keyword_end - { - $$ = new_sdef(p, $2, $5, $7, $8); - SET_LINENO($$, $1); - local_resume(p, $<nd>6); - p->in_single--; - p->cmdarg_stack = $<stack>4; - } - | keyword_break - { - $$ = new_break(p, 0); - } - | keyword_next - { - $$ = new_next(p, 0); - } - | keyword_redo - { - $$ = new_redo(p); - } - | keyword_retry - { - $$ = new_retry(p); - } - ; - -primary_value : primary - { - $$ = $1; - if (!$$) $$ = new_nil(p); - } - ; - -then : term - | keyword_then - | term keyword_then - ; - -do : term - | keyword_do_cond - ; - -if_tail : opt_else - | keyword_elsif expr_value then - compstmt - if_tail - { - $$ = new_if(p, cond($2), $4, $5); - } - ; - -opt_else : none - | keyword_else compstmt - { - $$ = $2; - } - ; - -for_var : lhs - { - $$ = list1(list1($1)); - } - | mlhs - ; - -f_marg : f_norm_arg - { - $$ = new_arg(p, $1); - } - | tLPAREN f_margs rparen - { - $$ = new_masgn(p, $2, 0); - } - ; - -f_marg_list : f_marg - { - $$ = list1($1); - } - | f_marg_list ',' f_marg - { - $$ = push($1, $3); - } - ; - -f_margs : f_marg_list - { - $$ = list3($1,0,0); - } - | f_marg_list ',' tSTAR f_norm_arg - { - $$ = list3($1, new_arg(p, $4), 0); - } - | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list - { - $$ = list3($1, new_arg(p, $4), $6); - } - | f_marg_list ',' tSTAR - { - $$ = list3($1, (node*)-1, 0); - } - | f_marg_list ',' tSTAR ',' f_marg_list - { - $$ = list3($1, (node*)-1, $5); - } - | tSTAR f_norm_arg - { - $$ = list3(0, new_arg(p, $2), 0); - } - | tSTAR f_norm_arg ',' f_marg_list - { - $$ = list3(0, new_arg(p, $2), $4); - } - | tSTAR - { - $$ = list3(0, (node*)-1, 0); - } - | tSTAR ',' f_marg_list - { - $$ = list3(0, (node*)-1, $3); - } - ; - -block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, 0, $6); - } - | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, $7, $8); - } - | f_arg ',' f_block_optarg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, 0, $4); - } - | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, $5, $6); - } - | f_arg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, 0, $4); - } - | f_arg ',' - { - $$ = new_args(p, $1, 0, 1, 0, 0); - } - | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, $5, $6); - } - | f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, 0, 0, $2); - } - | f_block_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, 0, $4); - } - | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, $5, $6); - } - | f_block_optarg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, 0, $2); - } - | f_block_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, $3, $4); - } - | f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, 0, $2); - } - | f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, $3, $4); - } - | f_block_arg - { - $$ = new_args(p, 0, 0, 0, 0, $1); - } - ; - -opt_block_param : none - | block_param_def - { - p->cmd_start = TRUE; - $$ = $1; - } - ; - -block_param_def : '|' opt_bv_decl '|' - { - $$ = 0; - } - | tOROP - { - $$ = 0; - } - | '|' block_param opt_bv_decl '|' - { - $$ = $2; - } - ; - - -opt_bv_decl : opt_nl - { - $$ = 0; - } - | opt_nl ';' bv_decls opt_nl - { - $$ = 0; - } - ; - -bv_decls : bvar - | bv_decls ',' bvar - ; - -bvar : tIDENTIFIER - { - local_add_f(p, $1); - new_bv(p, $1); - } - | f_bad_arg - ; - -f_larglist : '(' f_args opt_bv_decl ')' - { - $$ = $2; - } - | f_args - { - $$ = $1; - } - ; - -lambda_body : tLAMBEG compstmt '}' - { - $$ = $2; - } - | keyword_do_LAMBDA compstmt keyword_end - { - $$ = $2; - } - ; - -do_block : keyword_do_block - { - local_nest(p); - } - opt_block_param - compstmt - keyword_end - { - $$ = new_block(p,$3,$4); - local_unnest(p); - } - ; - -block_call : command do_block - { - if ($1->car == (node*)NODE_YIELD) { - yyerror(p, "block given to yield"); - } - else { - call_with_block(p, $1, $2); - } - $$ = $1; - } - | block_call dot_or_colon operation2 opt_paren_args - { - $$ = new_call(p, $1, $3, $4); - } - | block_call dot_or_colon operation2 opt_paren_args brace_block - { - $$ = new_call(p, $1, $3, $4); - call_with_block(p, $$, $5); - } - | block_call dot_or_colon operation2 command_args do_block - { - $$ = new_call(p, $1, $3, $4); - call_with_block(p, $$, $5); - } - ; - -method_call : operation paren_args - { - $$ = new_fcall(p, $1, $2); - } - | primary_value '.' operation2 opt_paren_args - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation2 paren_args - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation3 - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value '.' paren_args - { - $$ = new_call(p, $1, intern("call",4), $3); - } - | primary_value tCOLON2 paren_args - { - $$ = new_call(p, $1, intern("call",4), $3); - } - | keyword_super paren_args - { - $$ = new_super(p, $2); - } - | keyword_super - { - $$ = new_zsuper(p); - } - | primary_value '[' opt_call_args rbracket - { - $$ = new_call(p, $1, intern("[]",2), $3); - } - ; - -brace_block : '{' - { - local_nest(p); - $<num>$ = p->lineno; - } - opt_block_param - compstmt '}' - { - $$ = new_block(p,$3,$4); - SET_LINENO($$, $<num>2); - local_unnest(p); - } - | keyword_do - { - local_nest(p); - $<num>$ = p->lineno; - } - opt_block_param - compstmt keyword_end - { - $$ = new_block(p,$3,$4); - SET_LINENO($$, $<num>2); - local_unnest(p); - } - ; - -case_body : keyword_when args then - compstmt - cases - { - $$ = cons(cons($2, $4), $5); - } - ; - -cases : opt_else - { - if ($1) { - $$ = cons(cons(0, $1), 0); - } - else { - $$ = 0; - } - } - | case_body - ; - -opt_rescue : keyword_rescue exc_list exc_var then - compstmt - opt_rescue - { - $$ = list1(list3($2, $3, $5)); - if ($6) $$ = append($$, $6); - } - | none - ; - -exc_list : arg_value - { - $$ = list1($1); - } - | mrhs - | none - ; - -exc_var : tASSOC lhs - { - $$ = $2; - } - | none - ; - -opt_ensure : keyword_ensure compstmt - { - $$ = $2; - } - | none - ; - -literal : numeric - | symbol - | words - | symbols - ; - -string : tCHAR - | tSTRING - | tSTRING_BEG tSTRING - { - $$ = $2; - } - | tSTRING_BEG string_rep tSTRING - { - $$ = new_dstr(p, push($2, $3)); - } - ; - -string_rep : string_interp - | string_rep string_interp - { - $$ = append($1, $2); - } - ; - -string_interp : tSTRING_MID - { - $$ = list1($1); - } - | tSTRING_PART - { - $<nd>$ = p->lex_strterm; - p->lex_strterm = NULL; - } - compstmt - '}' - { - p->lex_strterm = $<nd>2; - $$ = list2($1, $3); - } - | tLITERAL_DELIM - { - $$ = list1(new_literal_delim(p)); - } - | tHD_LITERAL_DELIM heredoc_bodies - { - $$ = list1(new_literal_delim(p)); - } - ; - -xstring : tXSTRING_BEG tXSTRING - { - $$ = $2; - } - | tXSTRING_BEG string_rep tXSTRING - { - $$ = new_dxstr(p, push($2, $3)); - } - ; - -regexp : tREGEXP_BEG tREGEXP - { - $$ = $2; - } - | tREGEXP_BEG string_rep tREGEXP - { - $$ = new_dregx(p, $2, $3); - } - ; - -heredoc : tHEREDOC_BEG - ; - -opt_heredoc_bodies : /* none */ - | heredoc_bodies - ; - -heredoc_bodies : heredoc_body - | heredoc_bodies heredoc_body - ; - -heredoc_body : tHEREDOC_END - { - parser_heredoc_info * inf = parsing_heredoc_inf(p); - inf->doc = push(inf->doc, new_str(p, "", 0)); - heredoc_end(p); - } - | heredoc_string_rep tHEREDOC_END - { - heredoc_end(p); - } - ; - -heredoc_string_rep : heredoc_string_interp - | heredoc_string_rep heredoc_string_interp - ; - -heredoc_string_interp : tHD_STRING_MID - { - parser_heredoc_info * inf = parsing_heredoc_inf(p); - inf->doc = push(inf->doc, $1); - heredoc_treat_nextline(p); - } - | tHD_STRING_PART - { - $<nd>$ = p->lex_strterm; - p->lex_strterm = NULL; - } - compstmt - '}' - { - parser_heredoc_info * inf = parsing_heredoc_inf(p); - p->lex_strterm = $<nd>2; - inf->doc = push(push(inf->doc, $1), $3); - } - ; - -words : tWORDS_BEG tSTRING - { - $$ = new_words(p, list1($2)); - } - | tWORDS_BEG string_rep tSTRING - { - $$ = new_words(p, push($2, $3)); - } - ; - - -symbol : basic_symbol - { - $$ = new_sym(p, $1); - } - | tSYMBEG tSTRING_BEG string_interp tSTRING - { - p->lstate = EXPR_END; - $$ = new_dsym(p, push($3, $4)); - } - ; - -basic_symbol : tSYMBEG sym - { - p->lstate = EXPR_END; - $$ = $2; - } - ; - -sym : fname - | tIVAR - | tGVAR - | tCVAR - | tSTRING - { - $$ = new_strsym(p, $1); - } - | tSTRING_BEG tSTRING - { - $$ = new_strsym(p, $2); - } - ; - -symbols : tSYMBOLS_BEG tSTRING - { - $$ = new_symbols(p, list1($2)); - } - | tSYMBOLS_BEG string_rep tSTRING - { - $$ = new_symbols(p, push($2, $3)); - } - ; - -numeric : tINTEGER - | tFLOAT - | tUMINUS_NUM tINTEGER %prec tLOWEST - { - $$ = negate_lit(p, $2); - } - | tUMINUS_NUM tFLOAT %prec tLOWEST - { - $$ = negate_lit(p, $2); - } - ; - -variable : tIDENTIFIER - { - $$ = new_lvar(p, $1); - } - | tIVAR - { - $$ = new_ivar(p, $1); - } - | tGVAR - { - $$ = new_gvar(p, $1); - } - | tCVAR - { - $$ = new_cvar(p, $1); - } - | tCONSTANT - { - $$ = new_const(p, $1); - } - ; - -var_lhs : variable - { - assignable(p, $1); - } - ; - -var_ref : variable - { - $$ = var_reference(p, $1); - } - | keyword_nil - { - $$ = new_nil(p); - } - | keyword_self - { - $$ = new_self(p); - } - | keyword_true - { - $$ = new_true(p); - } - | keyword_false - { - $$ = new_false(p); - } - | keyword__FILE__ - { - if (!p->filename) { - p->filename = "(null)"; - } - $$ = new_str(p, p->filename, strlen(p->filename)); - } - | keyword__LINE__ - { - char buf[16]; - - snprintf(buf, sizeof(buf), "%d", p->lineno); - $$ = new_int(p, buf, 10); - } - ; - -backref : tNTH_REF - | tBACK_REF - ; - -superclass : term - { - $$ = 0; - } - | '<' - { - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - } - expr_value term - { - $$ = $3; - } - | error term - { - yyerrok; - $$ = 0; - } - ; - -f_arglist : '(' f_args rparen - { - $$ = $2; - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - } - | f_args term - { - $$ = $1; - } - ; - -f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, 0, $6); - } - | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, $7, $8); - } - | f_arg ',' f_optarg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, 0, $4); - } - | f_arg ',' f_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, $5, $6); - } - | f_arg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, 0, $4); - } - | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, $5, $6); - } - | f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, 0, 0, $2); - } - | f_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, 0, $4); - } - | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, $5, $6); - } - | f_optarg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, 0, $2); - } - | f_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, $3, $4); - } - | f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, 0, $2); - } - | f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, $3, $4); - } - | f_block_arg - { - $$ = new_args(p, 0, 0, 0, 0, $1); - } - | /* none */ - { - local_add_f(p, 0); - $$ = new_args(p, 0, 0, 0, 0, 0); - } - ; - -f_bad_arg : tCONSTANT - { - yyerror(p, "formal argument cannot be a constant"); - $$ = 0; - } - | tIVAR - { - yyerror(p, "formal argument cannot be an instance variable"); - $$ = 0; - } - | tGVAR - { - yyerror(p, "formal argument cannot be a global variable"); - $$ = 0; - } - | tCVAR - { - yyerror(p, "formal argument cannot be a class variable"); - $$ = 0; - } - ; - -f_norm_arg : f_bad_arg - { - $$ = 0; - } - | tIDENTIFIER - { - local_add_f(p, $1); - $$ = $1; - } - ; - -f_arg_item : f_norm_arg - { - $$ = new_arg(p, $1); - } - | tLPAREN f_margs rparen - { - $$ = new_masgn(p, $2, 0); - } - ; - -f_arg : f_arg_item - { - $$ = list1($1); - } - | f_arg ',' f_arg_item - { - $$ = push($1, $3); - } - ; - -f_opt_asgn : tIDENTIFIER '=' - { - local_add_f(p, $1); - $$ = $1; - } - ; - -f_opt : f_opt_asgn arg_value - { - $$ = cons(nsym($1), $2); - } - ; - -f_block_opt : f_opt_asgn primary_value - { - $$ = cons(nsym($1), $2); - } - ; - -f_block_optarg : f_block_opt - { - $$ = list1($1); - } - | f_block_optarg ',' f_block_opt - { - $$ = push($1, $3); - } - ; - -f_optarg : f_opt - { - $$ = list1($1); - } - | f_optarg ',' f_opt - { - $$ = push($1, $3); - } - ; - -restarg_mark : '*' - | tSTAR - ; - -f_rest_arg : restarg_mark tIDENTIFIER - { - local_add_f(p, $2); - $$ = $2; - } - | restarg_mark - { - local_add_f(p, 0); - $$ = -1; - } - ; - -blkarg_mark : '&' - | tAMPER - ; - -f_block_arg : blkarg_mark tIDENTIFIER - { - local_add_f(p, $2); - $$ = $2; - } - ; - -opt_f_block_arg : ',' f_block_arg - { - $$ = $2; - } - | none - { - local_add_f(p, 0); - $$ = 0; - } - ; - -singleton : var_ref - { - $$ = $1; - if (!$$) $$ = new_nil(p); - } - | '(' {p->lstate = EXPR_BEG;} expr rparen - { - if ($3 == 0) { - yyerror(p, "can't define singleton method for ()."); - } - else { - switch ((enum node_type)(int)(intptr_t)$3->car) { - case NODE_STR: - case NODE_DSTR: - case NODE_XSTR: - case NODE_DXSTR: - case NODE_DREGX: - case NODE_MATCH: - case NODE_FLOAT: - case NODE_ARRAY: - case NODE_HEREDOC: - yyerror(p, "can't define singleton method for literals"); - default: - break; - } - } - $$ = $3; - } - ; - -assoc_list : none - | assocs trailer - { - $$ = $1; - } - ; - -assocs : assoc - { - $$ = list1($1); - NODE_LINENO($$, $1); - } - | assocs ',' assoc - { - $$ = push($1, $3); - } - ; - -assoc : arg_value tASSOC arg_value - { - $$ = cons($1, $3); - } - | tLABEL arg_value - { - $$ = cons(new_sym(p, $1), $2); - } - ; - -operation : tIDENTIFIER - | tCONSTANT - | tFID - ; - -operation2 : tIDENTIFIER - | tCONSTANT - | tFID - | op - ; - -operation3 : tIDENTIFIER - | tFID - | op - ; - -dot_or_colon : '.' - | tCOLON2 - ; - -opt_terms : /* none */ - | terms - ; - -opt_nl : /* none */ - | nl - ; - -rparen : opt_nl ')' - ; - -rbracket : opt_nl ']' - ; - -trailer : /* none */ - | nl - | ',' - ; - -term : ';' {yyerrok;} - | nl - ; - -nl : '\n' - { - p->lineno++; - p->column = 0; - } - opt_heredoc_bodies - -terms : term - | terms ';' {yyerrok;} - ; - -none : /* none */ - { - $$ = 0; - } - ; -%% -#define yylval (*((YYSTYPE*)(p->ylval))) - -static void -yyerror(parser_state *p, const char *s) -{ - char* c; - int n; - - if (! p->capture_errors) { -#ifdef ENABLE_STDIO - if (p->filename) { - fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s); - } - else { - fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); - } -#endif - } - else if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) { - n = strlen(s); - c = (char *)parser_palloc(p, n + 1); - memcpy(c, s, n + 1); - p->error_buffer[p->nerr].message = c; - p->error_buffer[p->nerr].lineno = p->lineno; - p->error_buffer[p->nerr].column = p->column; - } - p->nerr++; -} - -static void -yyerror_i(parser_state *p, const char *fmt, int i) -{ - char buf[256]; - - snprintf(buf, sizeof(buf), fmt, i); - yyerror(p, buf); -} - -static void -yywarn(parser_state *p, const char *s) -{ - char* c; - int n; - - if (! p->capture_errors) { -#ifdef ENABLE_STDIO - if (p->filename) { - fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s); - } - else { - fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); - } -#endif - } - else if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) { - n = strlen(s); - c = (char *)parser_palloc(p, n + 1); - memcpy(c, s, n + 1); - p->warn_buffer[p->nwarn].message = c; - p->warn_buffer[p->nwarn].lineno = p->lineno; - p->warn_buffer[p->nwarn].column = p->column; - } - p->nwarn++; -} - -static void -yywarning(parser_state *p, const char *s) -{ - yywarn(p, s); -} - -static void -yywarning_s(parser_state *p, const char *fmt, const char *s) -{ - char buf[256]; - - snprintf(buf, sizeof(buf), fmt, s); - yywarning(p, buf); -} - -static void -backref_error(parser_state *p, node *n) -{ - int c; - - c = (int)(intptr_t)n->car; - - if (c == NODE_NTH_REF) { - yyerror_i(p, "can't set variable $%d", (int)(intptr_t)n->cdr); - } - else if (c == NODE_BACK_REF) { - yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr); - } - else { - mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c)); - } -} - -static void pushback(parser_state *p, int c); -static mrb_bool peeks(parser_state *p, const char *s); -static mrb_bool skips(parser_state *p, const char *s); - -static inline int -nextc(parser_state *p) -{ - int c; - - if (p->pb) { - node *tmp; - - c = (int)(intptr_t)p->pb->car; - tmp = p->pb; - p->pb = p->pb->cdr; - cons_free(tmp); - } - else { -#ifdef ENABLE_STDIO - if (p->f) { - if (feof(p->f)) goto eof; - c = fgetc(p->f); - if (c == EOF) goto eof; - } - else -#endif - if (!p->s || p->s >= p->send) { - goto eof; - } - else { - c = (unsigned char)*p->s++; - } - } - if (c >= 0) { - p->column++; - } - if (c == '\r') { - c = nextc(p); - if (c != '\n') { - pushback(p, c); - return '\r'; - } - return c; - } - return c; - - eof: - if (!p->cxt) return -1; - else { - if (p->cxt->partial_hook(p) < 0) - return -1; /* end of program(s) */ - return -2; /* end of a file in the program files */ - } -} - -static void -pushback(parser_state *p, int c) -{ - if (c >= 0) { - p->column--; - } - p->pb = cons((node*)(intptr_t)c, p->pb); -} - -static void -skip(parser_state *p, char term) -{ - int c; - - for (;;) { - c = nextc(p); - if (c < 0) break; - if (c == term) break; - } -} - -static int -peekc_n(parser_state *p, int n) -{ - node *list = 0; - int c0; - - do { - c0 = nextc(p); - if (c0 == -1) return c0; /* do not skip partial EOF */ - list = push(list, (node*)(intptr_t)c0); - } while(n--); - if (p->pb) { - p->pb = append((node*)list, p->pb); - } - else { - p->pb = list; - } - return c0; -} - -static mrb_bool -peek_n(parser_state *p, int c, int n) -{ - return peekc_n(p, n) == c && c >= 0; -} -#define peek(p,c) peek_n((p), (c), 0) - -static mrb_bool -peeks(parser_state *p, const char *s) -{ - int len = strlen(s); - -#ifdef ENABLE_STDIO - if (p->f) { - int n = 0; - while (*s) { - if (!peek_n(p, *s++, n++)) return FALSE; - } - return TRUE; - } - else -#endif - if (p->s && p->s + len <= p->send) { - if (memcmp(p->s, s, len) == 0) return TRUE; - } - return FALSE; -} - -static mrb_bool -skips(parser_state *p, const char *s) -{ - int c; - - for (;;) { - /* skip until first char */ - for (;;) { - c = nextc(p); - if (c < 0) return c; - if (c == '\n') { - p->lineno++; - p->column = 0; - } - if (c == *s) break; - } - s++; - if (peeks(p, s)) { - int len = strlen(s); - - while (len--) { - if (nextc(p) == '\n') { - p->lineno++; - p->column = 0; - } - } - return TRUE; - } - else{ - s--; - } - } - return FALSE; -} - - -static int -newtok(parser_state *p) -{ - p->bidx = 0; - return p->column - 1; -} - -static void -tokadd(parser_state *p, int32_t c) -{ - char utf8[4]; - unsigned len; - - /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */ - if (c >= 0) { - /* Single byte from source or non-Unicode escape */ - utf8[0] = (char)c; - len = 1; - } - else { - /* Unicode character */ - c = -c; - if (c < 0x80) { - utf8[0] = (char)c; - len = 1; - } - else if (c < 0x800) { - utf8[0] = (char)(0xC0 | (c >> 6)); - utf8[1] = (char)(0x80 | (c & 0x3F)); - len = 2; - } - else if (c < 0x10000) { - utf8[0] = (char)(0xE0 | (c >> 12) ); - utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F)); - utf8[2] = (char)(0x80 | ( c & 0x3F)); - len = 3; - } - else { - utf8[0] = (char)(0xF0 | (c >> 18) ); - utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F)); - utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F)); - utf8[3] = (char)(0x80 | ( c & 0x3F)); - len = 4; - } - } - if (p->bidx+len <= MRB_PARSER_BUF_SIZE) { - unsigned i; - for (i = 0; i < len; i++) { - p->buf[p->bidx++] = utf8[i]; - } - } -} - -static int -toklast(parser_state *p) -{ - return p->buf[p->bidx-1]; -} - -static void -tokfix(parser_state *p) -{ - if (p->bidx >= MRB_PARSER_BUF_SIZE) { - yyerror(p, "string too long (truncated)"); - } - p->buf[p->bidx] = '\0'; -} - -static const char* -tok(parser_state *p) -{ - return p->buf; -} - -static int -toklen(parser_state *p) -{ - return p->bidx; -} - -#define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG) -#define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN) -#define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS) -#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c)) -#define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG()) -#define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1)) - -static int -scan_oct(const int *start, int len, int *retlen) -{ - const int *s = start; - int retval = 0; - - /* mrb_assert(len <= 3) */ - while (len-- && *s >= '0' && *s <= '7') { - retval <<= 3; - retval |= *s++ - '0'; - } - *retlen = s - start; - - return retval; -} - -static int32_t -scan_hex(const int *start, int len, int *retlen) -{ - static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; - const int *s = start; - int32_t retval = 0; - char *tmp; - - /* mrb_assert(len <= 8) */ - while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) { - retval <<= 4; - retval |= (tmp - hexdigit) & 15; - s++; - } - *retlen = s - start; - - return retval; -} - -/* Return negative to indicate Unicode code point */ -static int32_t -read_escape(parser_state *p) -{ - int32_t c; - - switch (c = nextc(p)) { - case '\\':/* Backslash */ - return c; - - case 'n':/* newline */ - return '\n'; - - case 't':/* horizontal tab */ - return '\t'; - - case 'r':/* carriage-return */ - return '\r'; - - case 'f':/* form-feed */ - return '\f'; - - case 'v':/* vertical tab */ - return '\13'; - - case 'a':/* alarm(bell) */ - return '\007'; - - case 'e':/* escape */ - return 033; - - case '0': case '1': case '2': case '3': /* octal constant */ - case '4': case '5': case '6': case '7': - { - int buf[3]; - int i; - - buf[0] = c; - for (i=1; i<3; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (buf[i] < '0' || '7' < buf[i]) { - pushback(p, buf[i]); - break; - } - } - c = scan_oct(buf, i, &i); - } - return c; - - case 'x': /* hex constant */ - { - int buf[2]; - int i; - - for (i=0; i<2; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (!ISXDIGIT(buf[i])) { - pushback(p, buf[i]); - break; - } - } - c = scan_hex(buf, i, &i); - if (i == 0) { - yyerror(p, "Invalid escape character syntax"); - return 0; - } - } - return c; - - case 'u': /* Unicode */ - { - int buf[9]; - int i; - - /* Look for opening brace */ - i = 0; - buf[0] = nextc(p); - if (buf[0] < 0) goto eof; - if (buf[0] == '{') { - /* \u{xxxxxxxx} form */ - for (i=0; i<9; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (buf[i] == '}') { - break; - } - else if (!ISXDIGIT(buf[i])) { - yyerror(p, "Invalid escape character syntax"); - pushback(p, buf[i]); - return 0; - } - } - } - else if (ISXDIGIT(buf[0])) { - /* \uxxxx form */ - for (i=1; i<4; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (!ISXDIGIT(buf[i])) { - pushback(p, buf[i]); - break; - } - } - } - else { - pushback(p, buf[0]); - } - c = scan_hex(buf, i, &i); - if (i == 0) { - yyerror(p, "Invalid escape character syntax"); - return 0; - } - if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) { - yyerror(p, "Invalid Unicode code point"); - return 0; - } - } - return -c; - - case 'b':/* backspace */ - return '\010'; - - case 's':/* space */ - return ' '; - - case 'M': - if ((c = nextc(p)) != '-') { - yyerror(p, "Invalid escape character syntax"); - pushback(p, c); - return '\0'; - } - if ((c = nextc(p)) == '\\') { - return read_escape(p) | 0x80; - } - else if (c < 0) goto eof; - else { - return ((c & 0xff) | 0x80); - } - - case 'C': - if ((c = nextc(p)) != '-') { - yyerror(p, "Invalid escape character syntax"); - pushback(p, c); - return '\0'; - } - case 'c': - if ((c = nextc(p))== '\\') { - c = read_escape(p); - } - else if (c == '?') - return 0177; - else if (c < 0) goto eof; - return c & 0x9f; - - eof: - case -1: - case -2: /* end of a file */ - yyerror(p, "Invalid escape character syntax"); - return '\0'; - - default: - return c; - } -} - -static int -parse_string(parser_state *p) -{ - int c; - string_type type = (string_type)(intptr_t)p->lex_strterm->car; - int nest_level = (intptr_t)p->lex_strterm->cdr->car; - int beg = (intptr_t)p->lex_strterm->cdr->cdr->car; - int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr; - parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL; - - newtok(p); - while ((c = nextc(p)) != end || nest_level != 0) { - if (hinf && (c == '\n' || c < 0)) { - mrb_bool line_head; - tokadd(p, '\n'); - tokfix(p); - p->lineno++; - p->column = 0; - line_head = hinf->line_head; - hinf->line_head = TRUE; - if (line_head) { - /* check whether end of heredoc */ - const char *s = tok(p); - int len = toklen(p); - if (hinf->allow_indent) { - while (ISSPACE(*s) && len > 0) { - ++s; - --len; - } - } - if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) { - return tHEREDOC_END; - } - } - if (c < 0) { - char buf[256]; - snprintf(buf, sizeof(buf), "can't find heredoc delimiter \"%s\" anywhere before EOF", hinf->term); - yyerror(p, buf); - return 0; - } - yylval.nd = new_str(p, tok(p), toklen(p)); - return tHD_STRING_MID; - } - if (c < 0) { - yyerror(p, "unterminated string meets end of file"); - return 0; - } - else if (c == beg) { - nest_level++; - p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level; - } - else if (c == end) { - nest_level--; - p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level; - } - else if (c == '\\') { - c = nextc(p); - if (type & STR_FUNC_EXPAND) { - if (c == end || c == beg) { - tokadd(p, c); - } - else if (c == '\n') { - p->lineno++; - p->column = 0; - if (type & STR_FUNC_ARRAY) { - tokadd(p, '\n'); - } - } - else if (type & STR_FUNC_REGEXP) { - tokadd(p, '\\'); - tokadd(p, c); - } - else { - pushback(p, c); - tokadd(p, read_escape(p)); - if (hinf) - hinf->line_head = FALSE; - } - } - else { - if (c != beg && c != end) { - if (c == '\n') { - p->lineno++; - p->column = 0; - } - if (!(c == '\\' || ((type & STR_FUNC_ARRAY) && ISSPACE(c)))) { - tokadd(p, '\\'); - } - } - tokadd(p, c); - } - continue; - } - else if ((c == '#') && (type & STR_FUNC_EXPAND)) { - c = nextc(p); - if (c == '{') { - tokfix(p); - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - yylval.nd = new_str(p, tok(p), toklen(p)); - if (hinf) { - hinf->line_head = FALSE; - return tHD_STRING_PART; - } - return tSTRING_PART; - } - tokadd(p, '#'); - pushback(p, c); - continue; - } - if ((type & STR_FUNC_ARRAY) && ISSPACE(c)) { - if (toklen(p) == 0) { - do { - if (c == '\n') { - p->lineno++; - p->column = 0; - heredoc_treat_nextline(p); - if (p->parsing_heredoc != NULL) { - return tHD_LITERAL_DELIM; - } - } - c = nextc(p); - } while (ISSPACE(c)); - pushback(p, c); - return tLITERAL_DELIM; - } - else { - pushback(p, c); - tokfix(p); - yylval.nd = new_str(p, tok(p), toklen(p)); - return tSTRING_MID; - } - } - tokadd(p, c); - } - - tokfix(p); - p->lstate = EXPR_END; - end_strterm(p); - - if (type & STR_FUNC_XQUOTE) { - yylval.nd = new_xstr(p, tok(p), toklen(p)); - return tXSTRING; - } - - if (type & STR_FUNC_REGEXP) { - int f = 0; - int re_opt; - char *s = strndup(tok(p), toklen(p)); - char flags[3]; - char *flag = flags; - char *dup; - - newtok(p); - while (re_opt = nextc(p), re_opt >= 0 && ISALPHA(re_opt)) { - switch (re_opt) { - case 'i': f |= 1; break; - case 'x': f |= 2; break; - case 'm': f |= 4; break; - default: tokadd(p, re_opt); break; - } - } - pushback(p, re_opt); - if (toklen(p)) { - char msg[128]; - tokfix(p); - snprintf(msg, sizeof(msg), "unknown regexp option%s - %s", - toklen(p) > 1 ? "s" : "", tok(p)); - yyerror(p, msg); - } - if (f != 0) { - if (f & 1) *flag++ = 'i'; - if (f & 2) *flag++ = 'x'; - if (f & 4) *flag++ = 'm'; - dup = strndup(flags, (size_t)(flag - flags)); - } - else { - dup = NULL; - } - yylval.nd = new_regx(p, s, dup); - - return tREGEXP; - } - - yylval.nd = new_str(p, tok(p), toklen(p)); - return tSTRING; -} - - -static int -heredoc_identifier(parser_state *p) -{ - int c; - int type = str_heredoc; - mrb_bool indent = FALSE; - mrb_bool quote = FALSE; - node *newnode; - parser_heredoc_info *info; - - c = nextc(p); - if (ISSPACE(c) || c == '=') { - pushback(p, c); - return 0; - } - if (c == '-') { - indent = TRUE; - c = nextc(p); - } - if (c == '\'' || c == '"') { - int term = c; - if (c == '\'') - quote = TRUE; - newtok(p); - while ((c = nextc(p)) >= 0 && c != term) { - if (c == '\n') { - c = -1; - break; - } - tokadd(p, c); - } - if (c < 0) { - yyerror(p, "unterminated here document identifier"); - return 0; - } - } - else { - if (c < 0) { - return 0; /* missing here document identifier */ - } - if (! identchar(c)) { - pushback(p, c); - if (indent) pushback(p, '-'); - return 0; - } - newtok(p); - do { - tokadd(p, c); - } while ((c = nextc(p)) >= 0 && identchar(c)); - pushback(p, c); - } - tokfix(p); - newnode = new_heredoc(p); - info = (parser_heredoc_info*)newnode->cdr; - info->term = strndup(tok(p), toklen(p)); - info->term_len = toklen(p); - if (! quote) - type |= STR_FUNC_EXPAND; - info->type = (string_type)type; - info->allow_indent = indent; - info->line_head = TRUE; - info->doc = NULL; - p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode); - p->lstate = EXPR_END; - - yylval.nd = newnode; - return tHEREDOC_BEG; -} - -static int -arg_ambiguous(parser_state *p) -{ - yywarning(p, "ambiguous first argument; put parentheses or even spaces"); - return 1; -} - -#include "lex.def" - -static int -parser_yylex(parser_state *p) -{ - int32_t c; - int space_seen = 0; - int cmd_state; - enum mrb_lex_state_enum last_state; - int token_column; - - if (p->lex_strterm) { - if (is_strterm_type(p, STR_FUNC_HEREDOC)) { - if (p->parsing_heredoc != NULL) - return parse_string(p); - } - else - return parse_string(p); - } - cmd_state = p->cmd_start; - p->cmd_start = FALSE; - retry: - last_state = p->lstate; - switch (c = nextc(p)) { - case '\0': /* NUL */ - case '\004': /* ^D */ - case '\032': /* ^Z */ - return 0; - case -1: /* end of script. */ - if (p->heredocs_from_nextline) - goto maybe_heredoc; - return 0; - - /* white spaces */ - case ' ': case '\t': case '\f': case '\r': - case '\13': /* '\v' */ - space_seen = 1; - goto retry; - - case '#': /* it's a comment */ - skip(p, '\n'); - /* fall through */ - case -2: /* end of a file */ - case '\n': - maybe_heredoc: - heredoc_treat_nextline(p); - switch (p->lstate) { - case EXPR_BEG: - case EXPR_FNAME: - case EXPR_DOT: - case EXPR_CLASS: - case EXPR_VALUE: - p->lineno++; - p->column = 0; - if (p->parsing_heredoc != NULL) { - return parse_string(p); - } - goto retry; - default: - break; - } - if (p->parsing_heredoc != NULL) { - return '\n'; - } - while ((c = nextc(p))) { - switch (c) { - case ' ': case '\t': case '\f': case '\r': - case '\13': /* '\v' */ - space_seen = 1; - break; - case '.': - if ((c = nextc(p)) != '.') { - pushback(p, c); - pushback(p, '.'); - goto retry; - } - case -1: /* EOF */ - case -2: /* end of a file */ - goto normal_newline; - default: - pushback(p, c); - goto normal_newline; - } - } - normal_newline: - p->cmd_start = TRUE; - p->lstate = EXPR_BEG; - return '\n'; - - case '*': - if ((c = nextc(p)) == '*') { - if ((c = nextc(p)) == '=') { - yylval.id = intern("**",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - c = tPOW; - } - else { - if (c == '=') { - yylval.id = intern_c('*'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - if (IS_SPCARG(c)) { - yywarning(p, "`*' interpreted as argument prefix"); - c = tSTAR; - } - else if (IS_BEG()) { - c = tSTAR; - } - else { - c = '*'; - } - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return c; - - case '!': - c = nextc(p); - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if (c == '@') { - return '!'; - } - } - else { - p->lstate = EXPR_BEG; - } - if (c == '=') { - return tNEQ; - } - if (c == '~') { - return tNMATCH; - } - pushback(p, c); - return '!'; - - case '=': - if (p->column == 1) { - static const char begin[] = "begin"; - static const char end[] = "\n=end"; - if (peeks(p, begin)) { - c = peekc_n(p, sizeof(begin)-1); - if (c < 0 || ISSPACE(c)) { - do { - if (!skips(p, end)) { - yyerror(p, "embedded document meets end of file"); - return 0; - } - c = nextc(p); - } while (!(c < 0 || ISSPACE(c))); - if (c != '\n') skip(p, '\n'); - p->lineno++; - p->column = 0; - goto retry; - } - } - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - if ((c = nextc(p)) == '=') { - if ((c = nextc(p)) == '=') { - return tEQQ; - } - pushback(p, c); - return tEQ; - } - if (c == '~') { - return tMATCH; - } - else if (c == '>') { - return tASSOC; - } - pushback(p, c); - return '='; - - case '<': - c = nextc(p); - if (c == '<' && - p->lstate != EXPR_DOT && - p->lstate != EXPR_CLASS && - !IS_END() && - (!IS_ARG() || space_seen)) { - int token = heredoc_identifier(p); - if (token) - return token; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - if (p->lstate == EXPR_CLASS) { - p->cmd_start = TRUE; - } - } - if (c == '=') { - if ((c = nextc(p)) == '>') { - return tCMP; - } - pushback(p, c); - return tLEQ; - } - if (c == '<') { - if ((c = nextc(p)) == '=') { - yylval.id = intern("<<",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tLSHFT; - } - pushback(p, c); - return '<'; - - case '>': - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - if ((c = nextc(p)) == '=') { - return tGEQ; - } - if (c == '>') { - if ((c = nextc(p)) == '=') { - yylval.id = intern(">>",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tRSHFT; - } - pushback(p, c); - return '>'; - - case '"': - p->lex_strterm = new_strterm(p, str_dquote, '"', 0); - return tSTRING_BEG; - - case '\'': - p->lex_strterm = new_strterm(p, str_squote, '\'', 0); - return parse_string(p); - - case '`': - if (p->lstate == EXPR_FNAME) { - p->lstate = EXPR_ENDFN; - return '`'; - } - if (p->lstate == EXPR_DOT) { - if (cmd_state) - p->lstate = EXPR_CMDARG; - else - p->lstate = EXPR_ARG; - return '`'; - } - p->lex_strterm = new_strterm(p, str_xquote, '`', 0); - return tXSTRING_BEG; - - case '?': - if (IS_END()) { - p->lstate = EXPR_VALUE; - return '?'; - } - c = nextc(p); - if (c < 0) { - yyerror(p, "incomplete character syntax"); - return 0; - } - if (ISSPACE(c)) { - if (!IS_ARG()) { - int c2; - switch (c) { - case ' ': - c2 = 's'; - break; - case '\n': - c2 = 'n'; - break; - case '\t': - c2 = 't'; - break; - case '\v': - c2 = 'v'; - break; - case '\r': - c2 = 'r'; - break; - case '\f': - c2 = 'f'; - break; - default: - c2 = 0; - break; - } - if (c2) { - char buf[256]; - snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2); - yyerror(p, buf); - } - } - ternary: - pushback(p, c); - p->lstate = EXPR_VALUE; - return '?'; - } - newtok(p); - /* need support UTF-8 if configured */ - if ((isalnum(c) || c == '_')) { - int c2 = nextc(p); - pushback(p, c2); - if ((isalnum(c2) || c2 == '_')) { - goto ternary; - } - } - if (c == '\\') { - c = read_escape(p); - tokadd(p, c); - } - else { - tokadd(p, c); - } - tokfix(p); - yylval.nd = new_str(p, tok(p), toklen(p)); - p->lstate = EXPR_END; - return tCHAR; - - case '&': - if ((c = nextc(p)) == '&') { - p->lstate = EXPR_BEG; - if ((c = nextc(p)) == '=') { - yylval.id = intern("&&",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tANDOP; - } - else if (c == '=') { - yylval.id = intern_c('&'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - if (IS_SPCARG(c)) { - yywarning(p, "`&' interpreted as argument prefix"); - c = tAMPER; - } - else if (IS_BEG()) { - c = tAMPER; - } - else { - c = '&'; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return c; - - case '|': - if ((c = nextc(p)) == '|') { - p->lstate = EXPR_BEG; - if ((c = nextc(p)) == '=') { - yylval.id = intern("||",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tOROP; - } - if (c == '=') { - yylval.id = intern_c('|'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - pushback(p, c); - return '|'; - - case '+': - c = nextc(p); - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if (c == '@') { - return tUPLUS; - } - pushback(p, c); - return '+'; - } - if (c == '=') { - yylval.id = intern_c('+'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { - p->lstate = EXPR_BEG; - pushback(p, c); - if (c >= 0 && ISDIGIT(c)) { - c = '+'; - goto start_num; - } - return tUPLUS; - } - p->lstate = EXPR_BEG; - pushback(p, c); - return '+'; - - case '-': - c = nextc(p); - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if (c == '@') { - return tUMINUS; - } - pushback(p, c); - return '-'; - } - if (c == '=') { - yylval.id = intern_c('-'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (c == '>') { - p->lstate = EXPR_ENDFN; - return tLAMBDA; - } - if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { - p->lstate = EXPR_BEG; - pushback(p, c); - if (c >= 0 && ISDIGIT(c)) { - return tUMINUS_NUM; - } - return tUMINUS; - } - p->lstate = EXPR_BEG; - pushback(p, c); - return '-'; - - case '.': - p->lstate = EXPR_BEG; - if ((c = nextc(p)) == '.') { - if ((c = nextc(p)) == '.') { - return tDOT3; - } - pushback(p, c); - return tDOT2; - } - pushback(p, c); - if (c >= 0 && ISDIGIT(c)) { - yyerror(p, "no .<digit> floating literal anymore; put 0 before dot"); - } - p->lstate = EXPR_DOT; - return '.'; - - start_num: - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - { - int is_float, seen_point, seen_e, nondigit; - - is_float = seen_point = seen_e = nondigit = 0; - p->lstate = EXPR_END; - newtok(p); - if (c == '-' || c == '+') { - tokadd(p, c); - c = nextc(p); - } - if (c == '0') { -#define no_digits() do {yyerror(p,"numeric literal without digits"); return 0;} while (0) - int start = toklen(p); - c = nextc(p); - if (c == 'x' || c == 'X') { - /* hexadecimal */ - c = nextc(p); - if (c >= 0 && ISXDIGIT(c)) { - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (!ISXDIGIT(c)) break; - nondigit = 0; - tokadd(p, tolower(c)); - } while ((c = nextc(p)) >= 0); - } - pushback(p, c); - tokfix(p); - if (toklen(p) == start) { - no_digits(); - } - else if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 16); - return tINTEGER; - } - if (c == 'b' || c == 'B') { - /* binary */ - c = nextc(p); - if (c == '0' || c == '1') { - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (c != '0' && c != '1') break; - nondigit = 0; - tokadd(p, c); - } while ((c = nextc(p)) >= 0); - } - pushback(p, c); - tokfix(p); - if (toklen(p) == start) { - no_digits(); - } - else if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 2); - return tINTEGER; - } - if (c == 'd' || c == 'D') { - /* decimal */ - c = nextc(p); - if (c >= 0 && ISDIGIT(c)) { - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (!ISDIGIT(c)) break; - nondigit = 0; - tokadd(p, c); - } while ((c = nextc(p)) >= 0); - } - pushback(p, c); - tokfix(p); - if (toklen(p) == start) { - no_digits(); - } - else if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 10); - return tINTEGER; - } - if (c == '_') { - /* 0_0 */ - goto octal_number; - } - if (c == 'o' || c == 'O') { - /* prefixed octal */ - c = nextc(p); - if (c < 0 || c == '_' || !ISDIGIT(c)) { - no_digits(); - } - } - if (c >= '0' && c <= '7') { - /* octal */ - octal_number: - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (c < '0' || c > '9') break; - if (c > '7') goto invalid_octal; - nondigit = 0; - tokadd(p, c); - } while ((c = nextc(p)) >= 0); - - if (toklen(p) > start) { - pushback(p, c); - tokfix(p); - if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 8); - return tINTEGER; - } - if (nondigit) { - pushback(p, c); - goto trailing_uc; - } - } - if (c > '7' && c <= '9') { - invalid_octal: - yyerror(p, "Invalid octal digit"); - } - else if (c == '.' || c == 'e' || c == 'E') { - tokadd(p, '0'); - } - else { - pushback(p, c); - yylval.nd = new_int(p, "0", 10); - return tINTEGER; - } - } - - for (;;) { - switch (c) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - nondigit = 0; - tokadd(p, c); - break; - - case '.': - if (nondigit) goto trailing_uc; - if (seen_point || seen_e) { - goto decode_num; - } - else { - int c0 = nextc(p); - if (c0 < 0 || !ISDIGIT(c0)) { - pushback(p, c0); - goto decode_num; - } - c = c0; - } - tokadd(p, '.'); - tokadd(p, c); - is_float++; - seen_point++; - nondigit = 0; - break; - - case 'e': - case 'E': - if (nondigit) { - pushback(p, c); - c = nondigit; - goto decode_num; - } - if (seen_e) { - goto decode_num; - } - tokadd(p, c); - seen_e++; - is_float++; - nondigit = c; - c = nextc(p); - if (c != '-' && c != '+') continue; - tokadd(p, c); - nondigit = c; - break; - - case '_': /* `_' in number just ignored */ - if (nondigit) goto decode_num; - nondigit = c; - break; - - default: - goto decode_num; - } - c = nextc(p); - } - - decode_num: - pushback(p, c); - if (nondigit) { - trailing_uc: - yyerror_i(p, "trailing `%c' in number", nondigit); - } - tokfix(p); - if (is_float) { - double d; - char *endp; - - errno = 0; - d = strtod(tok(p), &endp); - if (d == 0 && endp == tok(p)) { - yywarning_s(p, "corrupted float value %s", tok(p)); - } - else if (errno == ERANGE) { - yywarning_s(p, "float %s out of range", tok(p)); - errno = 0; - } - yylval.nd = new_float(p, tok(p)); - return tFLOAT; - } - yylval.nd = new_int(p, tok(p), 10); - return tINTEGER; - } - - case ')': - case ']': - p->paren_nest--; - case '}': - COND_LEXPOP(); - CMDARG_LEXPOP(); - if (c == ')') - p->lstate = EXPR_ENDFN; - else - p->lstate = EXPR_ENDARG; - return c; - - case ':': - c = nextc(p); - if (c == ':') { - if (IS_BEG() || p->lstate == EXPR_CLASS || IS_SPCARG(-1)) { - p->lstate = EXPR_BEG; - return tCOLON3; - } - p->lstate = EXPR_DOT; - return tCOLON2; - } - if (IS_END() || ISSPACE(c)) { - pushback(p, c); - p->lstate = EXPR_BEG; - return ':'; - } - pushback(p, c); - p->lstate = EXPR_FNAME; - return tSYMBEG; - - case '/': - if (IS_BEG()) { - p->lex_strterm = new_strterm(p, str_regexp, '/', 0); - return tREGEXP_BEG; - } - if ((c = nextc(p)) == '=') { - yylval.id = intern_c('/'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - if (IS_SPCARG(c)) { - p->lex_strterm = new_strterm(p, str_regexp, '/', 0); - return tREGEXP_BEG; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return '/'; - - case '^': - if ((c = nextc(p)) == '=') { - yylval.id = intern_c('^'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - pushback(p, c); - return '^'; - - case ';': - p->lstate = EXPR_BEG; - return ';'; - - case ',': - p->lstate = EXPR_BEG; - return ','; - - case '~': - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - if ((c = nextc(p)) != '@') { - pushback(p, c); - } - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return '~'; - - case '(': - if (IS_BEG()) { - c = tLPAREN; - } - else if (IS_SPCARG(-1)) { - c = tLPAREN_ARG; - } - p->paren_nest++; - COND_PUSH(0); - CMDARG_PUSH(0); - p->lstate = EXPR_BEG; - return c; - - case '[': - p->paren_nest++; - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if ((c = nextc(p)) == ']') { - if ((c = nextc(p)) == '=') { - return tASET; - } - pushback(p, c); - return tAREF; - } - pushback(p, c); - return '['; - } - else if (IS_BEG()) { - c = tLBRACK; - } - else if (IS_ARG() && space_seen) { - c = tLBRACK; - } - p->lstate = EXPR_BEG; - COND_PUSH(0); - CMDARG_PUSH(0); - return c; - - case '{': - if (p->lpar_beg && p->lpar_beg == p->paren_nest) { - p->lstate = EXPR_BEG; - p->lpar_beg = 0; - p->paren_nest--; - COND_PUSH(0); - CMDARG_PUSH(0); - return tLAMBEG; - } - if (IS_ARG() || p->lstate == EXPR_END || p->lstate == EXPR_ENDFN) - c = '{'; /* block (primary) */ - else if (p->lstate == EXPR_ENDARG) - c = tLBRACE_ARG; /* block (expr) */ - else - c = tLBRACE; /* hash */ - COND_PUSH(0); - CMDARG_PUSH(0); - p->lstate = EXPR_BEG; - return c; - - case '\\': - c = nextc(p); - if (c == '\n') { - p->lineno++; - p->column = 0; - space_seen = 1; - goto retry; /* skip \\n */ - } - pushback(p, c); - return '\\'; - - case '%': - if (IS_BEG()) { - int term; - int paren; - - c = nextc(p); - quotation: - if (c < 0 || !ISALNUM(c)) { - term = c; - c = 'Q'; - } - else { - term = nextc(p); - if (isalnum(term)) { - yyerror(p, "unknown type of %string"); - return 0; - } - } - if (c < 0 || term < 0) { - yyerror(p, "unterminated quoted string meets end of file"); - return 0; - } - paren = term; - if (term == '(') term = ')'; - else if (term == '[') term = ']'; - else if (term == '{') term = '}'; - else if (term == '<') term = '>'; - else paren = 0; - - switch (c) { - case 'Q': - p->lex_strterm = new_strterm(p, str_dquote, term, paren); - return tSTRING_BEG; - - case 'q': - p->lex_strterm = new_strterm(p, str_squote, term, paren); - return parse_string(p); - - case 'W': - p->lex_strterm = new_strterm(p, str_dword, term, paren); - return tWORDS_BEG; - - case 'w': - p->lex_strterm = new_strterm(p, str_sword, term, paren); - return tWORDS_BEG; - - case 'x': - p->lex_strterm = new_strterm(p, str_xquote, term, paren); - return tXSTRING_BEG; - - case 'r': - p->lex_strterm = new_strterm(p, str_regexp, term, paren); - return tREGEXP_BEG; - - case 's': - p->lex_strterm = new_strterm(p, str_ssym, term, paren); - return tSYMBEG; - - case 'I': - p->lex_strterm = new_strterm(p, str_dsymbols, term, paren); - return tSYMBOLS_BEG; - - case 'i': - p->lex_strterm = new_strterm(p, str_ssymbols, term, paren); - return tSYMBOLS_BEG; - - default: - yyerror(p, "unknown type of %string"); - return 0; - } - } - if ((c = nextc(p)) == '=') { - yylval.id = intern_c('%'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (IS_SPCARG(c)) { - goto quotation; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - pushback(p, c); - return '%'; - - case '$': - p->lstate = EXPR_END; - token_column = newtok(p); - c = nextc(p); - if (c < 0) { - yyerror(p, "incomplete global variable syntax"); - return 0; - } - switch (c) { - case '_': /* $_: last read line string */ - c = nextc(p); - if (c >= 0 && identchar(c)) { /* if there is more after _ it is a variable */ - tokadd(p, '$'); - tokadd(p, c); - break; - } - pushback(p, c); - c = '_'; - /* fall through */ - case '~': /* $~: match-data */ - case '*': /* $*: argv */ - case '$': /* $$: pid */ - case '?': /* $?: last status */ - case '!': /* $!: error string */ - case '@': /* $@: error position */ - case '/': /* $/: input record separator */ - case '\\': /* $\: output record separator */ - case ';': /* $;: field separator */ - case ',': /* $,: output field separator */ - case '.': /* $.: last read line number */ - case '=': /* $=: ignorecase */ - case ':': /* $:: load path */ - case '<': /* $<: reading filename */ - case '>': /* $>: default output handle */ - case '\"': /* $": already loaded files */ - tokadd(p, '$'); - tokadd(p, c); - tokfix(p); - yylval.id = intern_cstr(tok(p)); - return tGVAR; - - case '-': - tokadd(p, '$'); - tokadd(p, c); - c = nextc(p); - pushback(p, c); - gvar: - tokfix(p); - yylval.id = intern_cstr(tok(p)); - return tGVAR; - - case '&': /* $&: last match */ - case '`': /* $`: string before last match */ - case '\'': /* $': string after last match */ - case '+': /* $+: string matches last pattern */ - if (last_state == EXPR_FNAME) { - tokadd(p, '$'); - tokadd(p, c); - goto gvar; - } - yylval.nd = new_back_ref(p, c); - return tBACK_REF; - - case '1': case '2': case '3': - case '4': case '5': case '6': - case '7': case '8': case '9': - do { - tokadd(p, c); - c = nextc(p); - } while (c >= 0 && isdigit(c)); - pushback(p, c); - if (last_state == EXPR_FNAME) goto gvar; - tokfix(p); - yylval.nd = new_nth_ref(p, atoi(tok(p))); - return tNTH_REF; - - default: - if (!identchar(c)) { - pushback(p, c); - return '$'; - } - case '0': - tokadd(p, '$'); - } - break; - - case '@': - c = nextc(p); - token_column = newtok(p); - tokadd(p, '@'); - if (c == '@') { - tokadd(p, '@'); - c = nextc(p); - } - if (c < 0) { - if (p->bidx == 1) { - yyerror(p, "incomplete instance variable syntax"); - } - else { - yyerror(p, "incomplete class variable syntax"); - } - return 0; - } - else if (isdigit(c)) { - if (p->bidx == 1) { - yyerror_i(p, "`@%c' is not allowed as an instance variable name", c); - } - else { - yyerror_i(p, "`@@%c' is not allowed as a class variable name", c); - } - return 0; - } - if (!identchar(c)) { - pushback(p, c); - return '@'; - } - break; - - case '_': - token_column = newtok(p); - break; - - default: - if (!identchar(c)) { - yyerror_i(p, "Invalid char `\\x%02X' in expression", c); - goto retry; - } - - token_column = newtok(p); - break; - } - - do { - tokadd(p, c); - c = nextc(p); - if (c < 0) break; - } while (identchar(c)); - if (token_column == 0 && toklen(p) == 7 && (c < 0 || c == '\n') && - strncmp(tok(p), "__END__", toklen(p)) == 0) - return -1; - - switch (tok(p)[0]) { - case '@': case '$': - pushback(p, c); - break; - default: - if ((c == '!' || c == '?') && !peek(p, '=')) { - tokadd(p, c); - } - else { - pushback(p, c); - } - } - tokfix(p); - { - int result = 0; - - switch (tok(p)[0]) { - case '$': - p->lstate = EXPR_END; - result = tGVAR; - break; - case '@': - p->lstate = EXPR_END; - if (tok(p)[1] == '@') - result = tCVAR; - else - result = tIVAR; - break; - - default: - if (toklast(p) == '!' || toklast(p) == '?') { - result = tFID; - } - else { - if (p->lstate == EXPR_FNAME) { - if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') && - (!peek(p, '=') || (peek_n(p, '>', 1)))) { - result = tIDENTIFIER; - tokadd(p, c); - tokfix(p); - } - else { - pushback(p, c); - } - } - if (result == 0 && ISUPPER(tok(p)[0])) { - result = tCONSTANT; - } - else { - result = tIDENTIFIER; - } - } - - if (IS_LABEL_POSSIBLE()) { - if (IS_LABEL_SUFFIX(0)) { - p->lstate = EXPR_BEG; - nextc(p); - tokfix(p); - yylval.id = intern_cstr(tok(p)); - return tLABEL; - } - } - if (p->lstate != EXPR_DOT) { - const struct kwtable *kw; - - /* See if it is a reserved word. */ - kw = mrb_reserved_word(tok(p), toklen(p)); - if (kw) { - enum mrb_lex_state_enum state = p->lstate; - yylval.num = p->lineno; - p->lstate = kw->state; - if (state == EXPR_FNAME) { - yylval.id = intern_cstr(kw->name); - return kw->id[0]; - } - if (p->lstate == EXPR_BEG) { - p->cmd_start = TRUE; - } - if (kw->id[0] == keyword_do) { - if (p->lpar_beg && p->lpar_beg == p->paren_nest) { - p->lpar_beg = 0; - p->paren_nest--; - return keyword_do_LAMBDA; - } - if (COND_P()) return keyword_do_cond; - if (CMDARG_P() && state != EXPR_CMDARG) - return keyword_do_block; - if (state == EXPR_ENDARG || state == EXPR_BEG) - return keyword_do_block; - return keyword_do; - } - if (state == EXPR_BEG || state == EXPR_VALUE) - return kw->id[0]; - else { - if (kw->id[0] != kw->id[1]) - p->lstate = EXPR_BEG; - return kw->id[1]; - } - } - } - - if (IS_BEG() || p->lstate == EXPR_DOT || IS_ARG()) { - if (cmd_state) { - p->lstate = EXPR_CMDARG; - } - else { - p->lstate = EXPR_ARG; - } - } - else if (p->lstate == EXPR_FNAME) { - p->lstate = EXPR_ENDFN; - } - else { - p->lstate = EXPR_END; - } - } - { - mrb_sym ident = intern_cstr(tok(p)); - - yylval.id = ident; -#if 0 - if (last_state != EXPR_DOT && islower(tok(p)[0]) && lvar_defined(ident)) { - p->lstate = EXPR_END; - } -#endif - } - return result; - } -} - -static int -yylex(void *lval, parser_state *p) -{ - p->ylval = lval; - return parser_yylex(p); -} - -static void -parser_init_cxt(parser_state *p, mrbc_context *cxt) -{ - if (!cxt) return; - if (cxt->filename) mrb_parser_set_filename(p, cxt->filename); - if (cxt->lineno) p->lineno = cxt->lineno; - if (cxt->syms) { - int i; - - p->locals = cons(0,0); - for (i=0; i<cxt->slen; i++) { - local_add_f(p, cxt->syms[i]); - } - } - p->capture_errors = cxt->capture_errors; - p->no_optimize = cxt->no_optimize; - if (cxt->partial_hook) { - p->cxt = cxt; - } -} - -static void -parser_update_cxt(parser_state *p, mrbc_context *cxt) -{ - node *n, *n0; - int i = 0; - - if (!cxt) return; - if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return; - n0 = n = p->tree->cdr->car; - while (n) { - i++; - n = n->cdr; - } - cxt->syms = (mrb_sym *)mrb_realloc(p->mrb, cxt->syms, i*sizeof(mrb_sym)); - cxt->slen = i; - for (i=0, n=n0; n; i++,n=n->cdr) { - cxt->syms[i] = sym(n->car); - } -} - -void mrb_codedump_all(mrb_state*, struct RProc*); -void mrb_parser_dump(mrb_state *mrb, node *tree, int offset); - -MRB_API void -mrb_parser_parse(parser_state *p, mrbc_context *c) -{ - struct mrb_jmpbuf buf; - p->jmp = &buf; - - MRB_TRY(p->jmp) { - - p->cmd_start = TRUE; - p->in_def = p->in_single = 0; - p->nerr = p->nwarn = 0; - p->lex_strterm = NULL; - - parser_init_cxt(p, c); - yyparse(p); - if (!p->tree) { - p->tree = new_nil(p); - } - parser_update_cxt(p, c); - if (c && c->dump_result) { - mrb_parser_dump(p->mrb, p->tree, 0); - } - - } - MRB_CATCH(p->jmp) { - yyerror(p, "memory allocation error"); - p->nerr++; - p->tree = 0; - return; - } - MRB_END_EXC(p->jmp); -} - -MRB_API parser_state* -mrb_parser_new(mrb_state *mrb) -{ - mrb_pool *pool; - parser_state *p; - static const parser_state parser_state_zero = { 0 }; - - pool = mrb_pool_open(mrb); - if (!pool) return NULL; - p = (parser_state *)mrb_pool_alloc(pool, sizeof(parser_state)); - if (!p) return NULL; - - *p = parser_state_zero; - p->mrb = mrb; - p->pool = pool; - - p->s = p->send = NULL; -#ifdef ENABLE_STDIO - p->f = NULL; -#endif - - p->cmd_start = TRUE; - p->in_def = p->in_single = 0; - - p->capture_errors = FALSE; - p->lineno = 1; - p->column = 0; -#if defined(PARSER_TEST) || defined(PARSER_DEBUG) - yydebug = 1; -#endif - - p->lex_strterm = NULL; - p->all_heredocs = p->parsing_heredoc = NULL; - p->lex_strterm_before_heredoc = NULL; - - p->current_filename_index = -1; - p->filename_table = NULL; - p->filename_table_length = 0; - - return p; -} - -MRB_API void -mrb_parser_free(parser_state *p) { - mrb_pool_close(p->pool); -} - -MRB_API mrbc_context* -mrbc_context_new(mrb_state *mrb) -{ - return (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context)); -} - -MRB_API void -mrbc_context_free(mrb_state *mrb, mrbc_context *cxt) -{ - mrb_free(mrb, cxt->syms); - mrb_free(mrb, cxt); -} - -MRB_API const char* -mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s) -{ - if (s) { - int len = strlen(s); - char *p = (char *)mrb_alloca(mrb, len + 1); - - memcpy(p, s, len + 1); - c->filename = p; - } - return c->filename; -} - -MRB_API void -mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*func)(struct mrb_parser_state*), void *data) -{ - c->partial_hook = func; - c->partial_data = data; -} - -MRB_API void -mrb_parser_set_filename(struct mrb_parser_state *p, const char *f) -{ - mrb_sym sym; - size_t i; - mrb_sym* new_table; - - sym = mrb_intern_cstr(p->mrb, f); - p->filename = mrb_sym2name_len(p->mrb, sym, NULL); - p->lineno = (p->filename_table_length > 0)? 0 : 1; - - for (i = 0; i < p->filename_table_length; ++i) { - if (p->filename_table[i] == sym) { - p->current_filename_index = i; - return; - } - } - - p->current_filename_index = p->filename_table_length++; - - new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length); - if (p->filename_table) { - memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->filename_table_length); - } - p->filename_table = new_table; - p->filename_table[p->filename_table_length - 1] = sym; -} - -MRB_API char const* -mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) { - if (idx >= p->filename_table_length) { return NULL; } - else { - return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL); - } -} - -#ifdef ENABLE_STDIO -MRB_API parser_state* -mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c) -{ - parser_state *p; - - p = mrb_parser_new(mrb); - if (!p) return NULL; - p->s = p->send = NULL; - p->f = f; - - mrb_parser_parse(p, c); - return p; -} -#endif - -MRB_API parser_state* -mrb_parse_nstring(mrb_state *mrb, const char *s, int len, mrbc_context *c) -{ - parser_state *p; - - p = mrb_parser_new(mrb); - if (!p) return NULL; - p->s = s; - p->send = s + len; - - mrb_parser_parse(p, c); - return p; -} - -MRB_API parser_state* -mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c) -{ - return mrb_parse_nstring(mrb, s, strlen(s), c); -} - -static mrb_value -load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c) -{ - struct RClass *target = mrb->object_class; - struct RProc *proc; - mrb_value v; - unsigned int keep = 0; - - if (!p) { - return mrb_undef_value(); - } - if (!p->tree || p->nerr) { - if (p->capture_errors) { - char buf[256]; - int n; - - n = snprintf(buf, sizeof(buf), "line %d: %s\n", - p->error_buffer[0].lineno, p->error_buffer[0].message); - mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n)); - mrb_parser_free(p); - return mrb_undef_value(); - } - else { - mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error")); - mrb_parser_free(p); - return mrb_undef_value(); - } - } - proc = mrb_generate_code(mrb, p); - mrb_parser_free(p); - if (proc == NULL) { - mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error")); - return mrb_undef_value(); - } - if (c) { - if (c->dump_result) mrb_codedump_all(mrb, proc); - if (c->no_exec) return mrb_obj_value(proc); - if (c->target_class) { - target = c->target_class; - } - if (c->keep_lv) { - keep = c->slen + 1; - } - else { - c->keep_lv = TRUE; - } - } - proc->target_class = target; - if (mrb->c->ci) { - mrb->c->ci->target_class = target; - } - v = mrb_toplevel_run_keep(mrb, proc, keep); - if (mrb->exc) return mrb_nil_value(); - return v; -} - -#ifdef ENABLE_STDIO -MRB_API mrb_value -mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c) -{ - return load_exec(mrb, mrb_parse_file(mrb, f, c), c); -} - -MRB_API mrb_value -mrb_load_file(mrb_state *mrb, FILE *f) -{ - return mrb_load_file_cxt(mrb, f, NULL); -} -#endif - -MRB_API mrb_value -mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c) -{ - return load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); -} - -MRB_API mrb_value -mrb_load_nstring(mrb_state *mrb, const char *s, int len) -{ - return mrb_load_nstring_cxt(mrb, s, len, NULL); -} - -MRB_API mrb_value -mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *c) -{ - return mrb_load_nstring_cxt(mrb, s, strlen(s), c); -} - -MRB_API mrb_value -mrb_load_string(mrb_state *mrb, const char *s) -{ - return mrb_load_string_cxt(mrb, s, NULL); -} - -#ifdef ENABLE_STDIO - -static void -dump_prefix(node *tree, int offset) -{ - printf("%05d ", tree->lineno); - while (offset--) { - putc(' ', stdout); - putc(' ', stdout); - } -} - -static void -dump_recur(mrb_state *mrb, node *tree, int offset) -{ - while (tree) { - mrb_parser_dump(mrb, tree->car, offset); - tree = tree->cdr; - } -} - -#endif - -void -mrb_parser_dump(mrb_state *mrb, node *tree, int offset) -{ -#ifdef ENABLE_STDIO - int nodetype; - - if (!tree) return; - again: - dump_prefix(tree, offset); - nodetype = (int)(intptr_t)tree->car; - tree = tree->cdr; - switch (nodetype) { - case NODE_BEGIN: - printf("NODE_BEGIN:\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_RESCUE: - printf("NODE_RESCUE:\n"); - if (tree->car) { - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - } - tree = tree->cdr; - if (tree->car) { - node *n2 = tree->car; - - dump_prefix(n2, offset+1); - printf("rescue:\n"); - while (n2) { - node *n3 = n2->car; - if (n3->car) { - dump_prefix(n2, offset+2); - printf("handle classes:\n"); - dump_recur(mrb, n3->car, offset+3); - } - if (n3->cdr->car) { - dump_prefix(n3, offset+2); - printf("exc_var:\n"); - mrb_parser_dump(mrb, n3->cdr->car, offset+3); - } - if (n3->cdr->cdr->car) { - dump_prefix(n3, offset+2); - printf("rescue body:\n"); - mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3); - } - n2 = n2->cdr; - } - } - tree = tree->cdr; - if (tree->car) { - dump_prefix(tree, offset+1); - printf("else:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - } - break; - - case NODE_ENSURE: - printf("NODE_ENSURE:\n"); - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(tree, offset+1); - printf("ensure:\n"); - mrb_parser_dump(mrb, tree->cdr->cdr, offset+2); - break; - - case NODE_LAMBDA: - printf("NODE_BLOCK:\n"); - goto block; - - case NODE_BLOCK: - block: - printf("NODE_BLOCK:\n"); - tree = tree->cdr; - if (tree->car) { - node *n = tree->car; - - if (n->car) { - dump_prefix(n, offset+1); - printf("mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("optional args:\n"); - { - node *n2 = n->car; - - while (n2) { - dump_prefix(n2, offset+2); - printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); - mrb_parser_dump(mrb, n2->car->cdr, 0); - n2 = n2->cdr; - } - } - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("post mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n) { - dump_prefix(n, offset+1); - printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); - } - } - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->car, offset+2); - break; - - case NODE_IF: - printf("NODE_IF:\n"); - dump_prefix(tree, offset+1); - printf("cond:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(tree, offset+1); - printf("then:\n"); - mrb_parser_dump(mrb, tree->cdr->car, offset+2); - if (tree->cdr->cdr->car) { - dump_prefix(tree, offset+1); - printf("else:\n"); - mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2); - } - break; - - case NODE_AND: - printf("NODE_AND:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_OR: - printf("NODE_OR:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_CASE: - printf("NODE_CASE:\n"); - if (tree->car) { - mrb_parser_dump(mrb, tree->car, offset+1); - } - tree = tree->cdr; - while (tree) { - dump_prefix(tree, offset+1); - printf("case:\n"); - dump_recur(mrb, tree->car->car, offset+2); - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->car->cdr, offset+2); - tree = tree->cdr; - } - break; - - case NODE_WHILE: - printf("NODE_WHILE:\n"); - dump_prefix(tree, offset+1); - printf("cond:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_UNTIL: - printf("NODE_UNTIL:\n"); - dump_prefix(tree, offset+1); - printf("cond:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_FOR: - printf("NODE_FOR:\n"); - dump_prefix(tree, offset+1); - printf("var:\n"); - { - node *n2 = tree->car; - - if (n2->car) { - dump_prefix(n2, offset+2); - printf("pre:\n"); - dump_recur(mrb, n2->car, offset+3); - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(n2, offset+2); - printf("rest:\n"); - mrb_parser_dump(mrb, n2->car, offset+3); - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(n2, offset+2); - printf("post:\n"); - dump_recur(mrb, n2->car, offset+3); - } - } - } - } - tree = tree->cdr; - dump_prefix(tree, offset+1); - printf("in:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - tree = tree->cdr; - dump_prefix(tree, offset+1); - printf("do:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - break; - - case NODE_SCOPE: - printf("NODE_SCOPE:\n"); - { - node *n2 = tree->car; - mrb_bool first_lval = TRUE; - - if (n2 && (n2->car || n2->cdr)) { - dump_prefix(n2, offset+1); - printf("local variables:\n"); - dump_prefix(n2, offset+2); - while (n2) { - if (n2->car) { - if (!first_lval) printf(", "); - printf("%s", mrb_sym2name(mrb, sym(n2->car))); - first_lval = FALSE; - } - n2 = n2->cdr; - } - printf("\n"); - } - } - tree = tree->cdr; - offset++; - goto again; - - case NODE_FCALL: - case NODE_CALL: - printf("NODE_CALL:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - dump_prefix(tree, offset+1); - printf("method='%s' (%d)\n", - mrb_sym2name(mrb, sym(tree->cdr->car)), - (int)(intptr_t)tree->cdr->car); - tree = tree->cdr->cdr->car; - if (tree) { - dump_prefix(tree, offset+1); - printf("args:\n"); - dump_recur(mrb, tree->car, offset+2); - if (tree->cdr) { - dump_prefix(tree, offset+1); - printf("block:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - } - } - break; - - case NODE_DOT2: - printf("NODE_DOT2:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_DOT3: - printf("NODE_DOT3:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_COLON2: - printf("NODE_COLON2:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->cdr))); - break; - - case NODE_COLON3: - printf("NODE_COLON3:\n"); - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_ARRAY: - printf("NODE_ARRAY:\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_HASH: - printf("NODE_HASH:\n"); - while (tree) { - dump_prefix(tree, offset+1); - printf("key:\n"); - mrb_parser_dump(mrb, tree->car->car, offset+2); - dump_prefix(tree, offset+1); - printf("value:\n"); - mrb_parser_dump(mrb, tree->car->cdr, offset+2); - tree = tree->cdr; - } - break; - - case NODE_SPLAT: - printf("NODE_SPLAT:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_ASGN: - printf("NODE_ASGN:\n"); - dump_prefix(tree, offset+1); - printf("lhs:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(tree, offset+1); - printf("rhs:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_MASGN: - printf("NODE_MASGN:\n"); - dump_prefix(tree, offset+1); - printf("mlhs:\n"); - { - node *n2 = tree->car; - - if (n2->car) { - dump_prefix(tree, offset+2); - printf("pre:\n"); - dump_recur(mrb, n2->car, offset+3); - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(n2, offset+2); - printf("rest:\n"); - if (n2->car == (node*)-1) { - dump_prefix(n2, offset+2); - printf("(empty)\n"); - } - else { - mrb_parser_dump(mrb, n2->car, offset+3); - } - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(n2, offset+2); - printf("post:\n"); - dump_recur(mrb, n2->car, offset+3); - } - } - } - } - dump_prefix(tree, offset+1); - printf("rhs:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_OP_ASGN: - printf("NODE_OP_ASGN:\n"); - dump_prefix(tree, offset+1); - printf("lhs:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - tree = tree->cdr; - dump_prefix(tree, offset+1); - printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car); - tree = tree->cdr; - mrb_parser_dump(mrb, tree->car, offset+1); - break; - - case NODE_SUPER: - printf("NODE_SUPER:\n"); - if (tree) { - dump_prefix(tree, offset+1); - printf("args:\n"); - dump_recur(mrb, tree->car, offset+2); - if (tree->cdr) { - dump_prefix(tree, offset+1); - printf("block:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - } - } - break; - - case NODE_ZSUPER: - printf("NODE_ZSUPER\n"); - break; - - case NODE_RETURN: - printf("NODE_RETURN:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_YIELD: - printf("NODE_YIELD:\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_BREAK: - printf("NODE_BREAK:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_NEXT: - printf("NODE_NEXT:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_REDO: - printf("NODE_REDO\n"); - break; - - case NODE_RETRY: - printf("NODE_RETRY\n"); - break; - - case NODE_LVAR: - printf("NODE_LVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_GVAR: - printf("NODE_GVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_IVAR: - printf("NODE_IVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_CVAR: - printf("NODE_CVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_CONST: - printf("NODE_CONST %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_MATCH: - printf("NODE_MATCH:\n"); - dump_prefix(tree, offset + 1); - printf("lhs:\n"); - mrb_parser_dump(mrb, tree->car, offset + 2); - dump_prefix(tree, offset + 1); - printf("rhs:\n"); - mrb_parser_dump(mrb, tree->cdr, offset + 2); - break; - - case NODE_BACK_REF: - printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree); - break; - - case NODE_NTH_REF: - printf("NODE_NTH_REF: $%d\n", (int)(intptr_t)tree); - break; - - case NODE_ARG: - printf("NODE_ARG %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_BLOCK_ARG: - printf("NODE_BLOCK_ARG:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_INT: - printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car); - break; - - case NODE_FLOAT: - printf("NODE_FLOAT %s\n", (char*)tree); - break; - - case NODE_NEGATE: - printf("NODE_NEGATE\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_STR: - printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr); - break; - - case NODE_DSTR: - printf("NODE_DSTR\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_XSTR: - printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr); - break; - - case NODE_DXSTR: - printf("NODE_DXSTR\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_REGX: - printf("NODE_REGX /%s/%s\n", (char*)tree->car, (char*)tree->cdr); - break; - - case NODE_DREGX: - printf("NODE_DREGX\n"); - dump_recur(mrb, tree->car, offset+1); - dump_prefix(tree, offset); - printf("tail: %s\n", (char*)tree->cdr->cdr->car); - dump_prefix(tree, offset); - printf("opt: %s\n", (char*)tree->cdr->cdr->cdr); - break; - - case NODE_SYM: - printf("NODE_SYM :%s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_SELF: - printf("NODE_SELF\n"); - break; - - case NODE_NIL: - printf("NODE_NIL\n"); - break; - - case NODE_TRUE: - printf("NODE_TRUE\n"); - break; - - case NODE_FALSE: - printf("NODE_FALSE\n"); - break; - - case NODE_ALIAS: - printf("NODE_ALIAS %s %s:\n", - mrb_sym2name(mrb, sym(tree->car)), - mrb_sym2name(mrb, sym(tree->cdr))); - break; - - case NODE_UNDEF: - printf("NODE_UNDEF"); - { - node *t = tree; - while (t) { - printf(" %s", mrb_sym2name(mrb, sym(t->car))); - t = t->cdr; - } - } - printf(":\n"); - break; - - case NODE_CLASS: - printf("NODE_CLASS:\n"); - if (tree->car->car == (node*)0) { - dump_prefix(tree, offset+1); - printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else if (tree->car->car == (node*)1) { - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else { - mrb_parser_dump(mrb, tree->car->car, offset+1); - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - if (tree->cdr->car) { - dump_prefix(tree, offset+1); - printf("super:\n"); - mrb_parser_dump(mrb, tree->cdr->car, offset+2); - } - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2); - break; - - case NODE_MODULE: - printf("NODE_MODULE:\n"); - if (tree->car->car == (node*)0) { - dump_prefix(tree, offset+1); - printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else if (tree->car->car == (node*)1) { - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else { - mrb_parser_dump(mrb, tree->car->car, offset+1); - dump_prefix(tree, offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); - break; - - case NODE_SCLASS: - printf("NODE_SCLASS:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - dump_prefix(tree, offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); - break; - - case NODE_DEF: - printf("NODE_DEF:\n"); - dump_prefix(tree, offset+1); - printf("%s\n", mrb_sym2name(mrb, sym(tree->car))); - tree = tree->cdr; - { - node *n2 = tree->car; - mrb_bool first_lval = TRUE; - - if (n2 && (n2->car || n2->cdr)) { - dump_prefix(n2, offset+1); - printf("local variables:\n"); - dump_prefix(n2, offset+2); - while (n2) { - if (n2->car) { - if (!first_lval) printf(", "); - printf("%s", mrb_sym2name(mrb, sym(n2->car))); - first_lval = FALSE; - } - n2 = n2->cdr; - } - printf("\n"); - } - } - tree = tree->cdr; - if (tree->car) { - node *n = tree->car; - - if (n->car) { - dump_prefix(n, offset+1); - printf("mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("optional args:\n"); - { - node *n2 = n->car; - - while (n2) { - dump_prefix(n2, offset+2); - printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); - mrb_parser_dump(mrb, n2->car->cdr, 0); - n2 = n2->cdr; - } - } - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("post mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n) { - dump_prefix(n, offset+1); - printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); - } - } - mrb_parser_dump(mrb, tree->cdr->car, offset+1); - break; - - case NODE_SDEF: - printf("NODE_SDEF:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - tree = tree->cdr; - dump_prefix(tree, offset+1); - printf(":%s\n", mrb_sym2name(mrb, sym(tree->car))); - tree = tree->cdr->cdr; - if (tree->car) { - node *n = tree->car; - - if (n->car) { - dump_prefix(n, offset+1); - printf("mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("optional args:\n"); - { - node *n2 = n->car; - - while (n2) { - dump_prefix(n2, offset+2); - printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); - mrb_parser_dump(mrb, n2->car->cdr, 0); - n2 = n2->cdr; - } - } - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); - } - n = n->cdr; - if (n->car) { - dump_prefix(n, offset+1); - printf("post mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n) { - dump_prefix(n, offset+1); - printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); - } - } - tree = tree->cdr; - mrb_parser_dump(mrb, tree->car, offset+1); - break; - - case NODE_POSTEXE: - printf("NODE_POSTEXE:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_HEREDOC: - printf("NODE_HEREDOC:\n"); - mrb_parser_dump(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); - break; - - default: - printf("node type: %d (0x%x)\n", nodetype, (unsigned)nodetype); - break; - } -#endif -} diff --git a/src/pool.c b/src/pool.c index 285cca6c3..ab30be1d8 100644 --- a/src/pool.c +++ b/src/pool.c @@ -4,23 +4,32 @@ ** See Copyright Notice in mruby.h */ -#include <stddef.h> -#include <stdint.h> #include <string.h> -#include "mruby.h" +#include <mruby.h> /* configuration section */ /* allocated memory address should be multiple of POOL_ALIGNMENT */ /* or undef it if alignment does not matter */ #ifndef POOL_ALIGNMENT +#if INTPTR_MAX == INT64_MAX +#define POOL_ALIGNMENT 8 +#else #define POOL_ALIGNMENT 4 #endif +#endif /* page size of memory pool */ #ifndef POOL_PAGE_SIZE #define POOL_PAGE_SIZE 16000 #endif /* end of configuration section */ +/* Disable MSVC warning "C4200: nonstandard extension used: zero-sized array + * in struct/union" when in C++ mode */ +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4200) +#endif + struct mrb_pool_page { struct mrb_pool_page *next; size_t offset; @@ -29,6 +38,10 @@ struct mrb_pool_page { char page[]; }; +#ifdef _MSC_VER +#pragma warning(pop) +#endif + struct mrb_pool { mrb_state *mrb; struct mrb_pool_page *pages; diff --git a/src/print.c b/src/print.c index 6c561100f..4af871b43 100644 --- a/src/print.c +++ b/src/print.c @@ -4,66 +4,92 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/string.h" -#include "mruby/variable.h" +#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 -printstr(mrb_state *mrb, mrb_value obj) +printcstr(const char *str, size_t len, FILE *stream) { -#ifdef ENABLE_STDIO - char *s; - int len; + if (str) { + fwrite(str, len, 1, stream); + putc('\n', stream); + } +} +static void +printstr(mrb_value obj, FILE *stream) +{ if (mrb_string_p(obj)) { - s = RSTRING_PTR(obj); - len = RSTRING_LEN(obj); - fwrite(s, len, 1, stdout); + printcstr(RSTRING_PTR(obj), RSTRING_LEN(obj), stream); } -#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) { -#ifdef ENABLE_STDIO - obj = mrb_funcall(mrb, obj, "inspect", 0); - printstr(mrb, obj); - putc('\n', stdout); -#endif + 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) { -#ifdef ENABLE_STDIO - mrb_value s; - mrb_print_backtrace(mrb); - s = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0); - if (mrb_string_p(s)) { - fwrite(RSTRING_PTR(s), RSTRING_LEN(s), 1, stderr); - putc('\n', stderr); - } -#endif } MRB_API void mrb_show_version(mrb_state *mrb) { - mrb_value msg; - - msg = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")); - printstr(mrb, msg); - printstr(mrb, mrb_str_new_lit(mrb, "\n")); + 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) { - mrb_value msg; + printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), MRB_SYM(MRUBY_COPYRIGHT)), stdout); +} - msg = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")); - printstr(mrb, msg); - printstr(mrb, mrb_str_new_lit(mrb, "\n")); +#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) +{ +} +#endif diff --git a/src/proc.c b/src/proc.c index e1d1d138d..1d5a4aa76 100644 --- a/src/proc.c +++ b/src/proc.c @@ -4,97 +4,160 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/class.h" -#include "mruby/proc.h" -#include "mruby/opcode.h" +#include <mruby.h> +#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 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 mrb_code call_iseq[] = { - MKOP_A(OP_CALL, 0), +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) +struct RProc* +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->target_class = 0; + p = MRB_OBJ_ALLOC(mrb, MRB_TT_PROC, mrb->proc_class); if (ci) { - if (ci->proc) - p->target_class = ci->proc->target_class; - if (!p->target_class) - p->target_class = ci->target_class; + struct RClass *tc = NULL; + + if (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 = mrb_vm_ci_target_class(ci); + } + p->upper = ci->proc; + p->e.target_class = tc; } p->body.irep = irep; - p->env = 0; - mrb_irep_incref(mrb, irep); + if (irep) { + mrb_irep_incref(mrb, (mrb_irep*)irep); + } return p; } -static struct REnv* -env_new(mrb_state *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; - - e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env); - MRB_SET_ENV_STACK_LEN(e, nlocals); - e->mid = mrb->c->ci->mid; - e->cioff = mrb->c->ci - mrb->c->cibase; - e->stack = mrb->c->stack; + mrb_int bidx; + + e = MRB_OBJ_ALLOC(mrb, MRB_TT_ENV, tc); + MRB_ENV_SET_LEN(e, nstacks); + bidx = ci->argc; + if (bidx < 0) bidx = 2; + else bidx += 1; + MRB_ENV_SET_BIDX(e, bidx); + e->mid = ci->mid; + e->stack = stack; + e->cxt = c; return e; } static void -closure_setup(mrb_state *mrb, struct RProc *p, int nlocals) +closure_setup(mrb_state *mrb, struct RProc *p) { - struct REnv *e; + mrb_callinfo *ci = mrb->c->ci; + const struct RProc *up = p->upper; + struct REnv *e = NULL; - if (!mrb->c->ci->env) { - e = env_new(mrb, nlocals); - mrb->c->ci->env = e; + if (ci && (e = mrb_vm_ci_env(ci)) != NULL) { + /* do nothing, because e is assigned already */ } - else { - e = mrb->c->ci->env; + else if (up) { + struct RClass *tc = MRB_PROC_TARGET_CLASS(p); + + 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; + } + } + if (e) { + p->e.env = e; + p->flags |= MRB_PROC_ENVSET; + mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e); } - p->env = e; } -struct RProc * -mrb_closure_new(mrb_state *mrb, mrb_irep *irep) +struct RProc* +mrb_closure_new(mrb_state *mrb, const mrb_irep *irep) { struct RProc *p = mrb_proc_new(mrb, irep); - closure_setup(mrb, p, mrb->c->ci->proc->body.irep->nlocals); + closure_setup(mrb, p); return p; } -MRB_API struct RProc * +MRB_API struct RProc* 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; - p->env = 0; + p->flags |= MRB_PROC_CFUNC_FL; + p->upper = 0; + p->e.target_class = 0; return p; } -MRB_API struct RProc * +MRB_API struct RProc* mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv) { struct RProc *p = mrb_proc_new_cfunc(mrb, func); struct REnv *e; int i; - p->env = e = env_new(mrb, argc); - MRB_ENV_UNSHARE_STACK(e); + 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_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]; @@ -108,7 +171,7 @@ mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const return p; } -MRB_API struct RProc * +MRB_API struct RProc* mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals) { return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL); @@ -117,108 +180,78 @@ 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; - struct REnv *e = p->env; + const struct RProc *p = mrb->c->ci->proc; + struct REnv *e; - if (!MRB_PROC_CFUNC_P(p)) { - mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc."); + if (!p || !MRB_PROC_CFUNC_P(p)) { + 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]; } -MRB_API void +void mrb_proc_copy(struct RProc *a, struct RProc *b) { + if (a->body.irep) { + /* already initialized proc */ + return; + } a->flags = b->flags; a->body = b->body; - if (!MRB_PROC_CFUNC_P(a)) { - a->body.irep->refcnt++; + if (!MRB_PROC_CFUNC_P(a) && a->body.irep) { + mrb_irep_incref(NULL, (mrb_irep*)a->body.irep); } - a->target_class = b->target_class; - a->env = b->env; + a->upper = b->upper; + a->e.env = b->e.env; + /* a->e.target_class = a->e.target_class; */ } static mrb_value -mrb_proc_initialize(mrb_state *mrb, mrb_value self) +mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class) { mrb_value blk; + 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"); - } - else { - mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(blk)); + /* 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_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].u.env) { + p->flags |= MRB_PROC_ORPHAN; } - return self; + return proc; } 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) -{ - return MRB_PROC_CFUNC_P(p); -} - -mrb_value -mrb_proc_call_cfunc(mrb_state *mrb, struct RProc *p, mrb_value self) -{ - return (p->body.func)(mrb, self); -} - -mrb_code* -mrb_proc_iseq(mrb_state *mrb, struct RProc *p) -{ - return p->body.irep->iseq; -} - /* 15.2.17.4.2 */ static mrb_value -mrb_proc_arity(mrb_state *mrb, mrb_value self) +proc_arity(mrb_state *mrb, mrb_value self) { - struct RProc *p = mrb_proc_ptr(self); - mrb_code *iseq = mrb_proc_iseq(mrb, p); - mrb_aspec aspec; - int ma, ra, pa, arity; - - if (MRB_PROC_CFUNC_P(p)) { - /* TODO cfunc aspec not implemented yet */ - return mrb_fixnum_value(-1); - } - - /* arity is depend on OP_ENTER */ - if (GET_OPCODE(*iseq) != OP_ENTER) { - return mrb_fixnum_value(0); - } - - aspec = GETARG_Ax(*iseq); - ma = MRB_ASPEC_REQ(aspec); - ra = MRB_ASPEC_REST(aspec); - pa = MRB_ASPEC_POST(aspec); - arity = ra ? -(ma + pa + 1) : ma + pa; - - return mrb_fixnum_value(arity); + return mrb_int_value(mrb, mrb_proc_arity(mrb_proc_ptr(self))); } /* 15.3.1.2.6 */ @@ -240,9 +273,12 @@ proc_lambda(mrb_state *mrb, mrb_value self) 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 = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, p->c); + 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); @@ -250,26 +286,163 @@ proc_lambda(mrb_state *mrb, mrb_value self) 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 -1; + } + + irep = p->body.irep; + if (!irep) { + return 0; + } + + pc = irep->iseq; + /* arity is depend on OP_ENTER */ + if (*pc != OP_ENTER) { + return 0; + } + + aspec = PEEK_W(pc+1); + ma = MRB_ASPEC_REQ(aspec); + op = MRB_ASPEC_OPT(aspec); + ra = MRB_ASPEC_REST(aspec); + pa = MRB_ASPEC_POST(aspec); + arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa; + + return arity; +} + +mrb_value +mrb_proc_local_variables(mrb_state *mrb, const struct RProc *proc) +{ + const mrb_irep *irep; + mrb_value vars; + size_t i; + + if (proc == NULL || MRB_PROC_CFUNC_P(proc)) { + return mrb_ary_new(mrb); + } + 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; + } + + 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; + } + 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_init_proc(mrb_state *mrb) +mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, const mrb_sym *lv, const mrb_value *stack) { - struct RProc *m; - mrb_irep *call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep)); - static const mrb_irep mrb_irep_zero = { 0 }; + 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)); - *call_irep = mrb_irep_zero; - call_irep->flags = MRB_ISEQ_NO_FREE; - call_irep->iseq = call_iseq; - call_irep->ilen = 1; + 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) +{ + mrb_method_t m; - mrb_define_method(mrb, mrb->proc_class, "initialize", mrb_proc_initialize, MRB_ARGS_NONE()); + 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()); - m = mrb_proc_new(mrb, call_irep); - 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 b427dc1b7..ec764e39b 100644 --- a/src/range.c +++ b/src/range.c @@ -4,47 +4,107 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/class.h" -#include "mruby/range.h" -#include "mruby/string.h" -#include "mruby/array.h" +#include <mruby.h> +#include <mruby/class.h> +#include <mruby/range.h> +#include <mruby/string.h> +#include <mruby/array.h> +#include <mruby/presym.h> -#define RANGE_CLASS (mrb_class_get(mrb, "Range")) +#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); - if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) && - (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) { +#ifdef MRB_NO_FLOAT + if (ta == MRB_TT_INTEGER && tb == MRB_TT_INTEGER ) return; +#else + if ((ta == MRB_TT_INTEGER || ta == MRB_TT_FLOAT) && + (tb == MRB_TT_INTEGER || tb == MRB_TT_FLOAT)) { return; } +#endif - ans = mrb_funcall(mrb, a, "<=>", 1, b); - if (mrb_nil_p(ans)) { - /* can not be compared */ + if (mrb_nil_p(a) || mrb_nil_p(b)) return; + + 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, 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); } /* @@ -54,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(range); - - return r->edges->beg; + return mrb_range_beg(mrb, range); } /* @@ -72,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(range); - - return r->edges->end; + return mrb_range_end(mrb, range); } /* @@ -87,27 +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(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_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; -} /* * call-seq: * Range.new(start, end, exclusive=false) => range @@ -116,22 +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; - 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. */ - 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 @@ -144,132 +180,61 @@ 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; - - 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? */ return mrb_false_value(); } - rr = mrb_range_ptr(range); - ro = mrb_range_ptr(obj); - if (!mrb_bool(mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg)) || - !mrb_bool(mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end)) || - rr->excl != ro->excl) { + rr = mrb_range_ptr(mrb, range); + ro = mrb_range_ptr(mrb, obj); + 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; - struct RRange *r = mrb_range_ptr(range); + 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); -} - -static mrb_bool -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 = mrb_range_ptr(range); - - if (mrb_type(range) != MRB_TT_RANGE) return FALSE; - - beg = mrb_int(mrb, r->edges->beg); - end = mrb_int(mrb, r->edges->end); - - if (beg < 0) { - beg += len; - if (beg < 0) return FALSE; + 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 FALSE; - 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 TRUE; -} - -MRB_API mrb_bool -mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len) -{ - return range_beg_len(mrb, range, begp, lenp, len, TRUE); + return mrb_false_value(); } /* 15.2.14.4.12(x) */ @@ -279,18 +244,17 @@ 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(range); + 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_append(mrb, str, str2); + mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2); + mrb_str_cat_str(mrb, str, str2); return str; } @@ -304,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; - struct RRange *r = mrb_range_ptr(range); + 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_append(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; } @@ -332,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, RANGE_CLASS)) { - return mrb_false_value(); - } - if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value(); - - r = mrb_range_ptr(range); - o = mrb_range_ptr(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_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, 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(); @@ -363,22 +327,70 @@ 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(src); - range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl); + r = mrb_range_ptr(mrb, src); + 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_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, a)); + a += 1.0; + } + } + else { + while (a <= b) { + mrb_ary_push(mrb, ary, mrb_int_value(mrb, 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)) { @@ -387,10 +399,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 (range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE)) { + 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)); @@ -401,7 +413,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]); } } @@ -409,26 +421,89 @@ 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; r = mrb_define_class(mrb, "Range", mrb->object_class); /* 15.2.14 */ + 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 2efd34334..4dafc964a 100644 --- a/src/state.c +++ b/src/state.c @@ -6,49 +6,53 @@ #include <stdlib.h> #include <string.h> -#include "mruby.h" -#include "mruby/irep.h" -#include "mruby/variable.h" -#include "mruby/debug.h" -#include "mruby/string.h" +#include <mruby.h> +#include <mruby/irep.h> +#include <mruby/variable.h> +#include <mruby/debug.h> +#include <mruby/string.h> +#include <mruby/class.h> -void mrb_init_heap(mrb_state*); void mrb_init_core(mrb_state*); void mrb_init_mrbgems(mrb_state*); -static mrb_value -inspect_main(mrb_state *mrb, mrb_value mod) +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) { - return mrb_str_new_lit(mrb, "main"); + 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; *mrb = mrb_state_zero; mrb->allocf_ud = ud; mrb->allocf = f; - mrb->current_white_part = MRB_GC_WHITE_A; mrb->atexit_stack_len = 0; -#ifndef MRB_GC_FIXED_ARENA - mrb->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE); - mrb->arena_capa = MRB_GC_ARENA_SIZE; -#endif - - mrb_init_heap(mrb); - 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; } @@ -65,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[]; -}; - -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) { @@ -105,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) { @@ -114,25 +94,35 @@ 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; } void mrb_free_symtbl(mrb_state *mrb); -void mrb_free_heap(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); @@ -140,84 +130,48 @@ mrb_irep_decref(mrb_state *mrb, mrb_irep *irep) } void -mrb_irep_free(mrb_state *mrb, mrb_irep *irep) +mrb_irep_cutref(mrb_state *mrb, mrb_irep *irep) { - size_t i; + mrb_irep **reps; + int i; - if (!(irep->flags & MRB_ISEQ_NO_FREE)) - mrb_free(mrb, irep->iseq); - 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])); - } -#ifdef MRB_WORD_BOXING - else if (mrb_type(irep->pool[i]) == MRB_TT_FLOAT) { - mrb_free(mrb, mrb_obj_ptr(irep->pool[i])); - } -#endif - } - mrb_free(mrb, irep->pool); - mrb_free(mrb, irep->syms); + if (irep->flags & MRB_IREP_NO_FREE) return; + reps = (mrb_irep**)irep->reps; for (i=0; i<irep->rlen; i++) { - mrb_irep_decref(mrb, irep->reps[i]); + mrb_irep *tmp = reps[i]; + reps[i] = NULL; + if (tmp) mrb_irep_decref(mrb, tmp); } - mrb_free(mrb, irep->reps); - mrb_free(mrb, irep->lv); - mrb_free(mrb, (void *)irep->filename); - mrb_free(mrb, irep->lines); - mrb_debug_info_free(mrb, irep->debug_info); - mrb_free(mrb, irep); } -mrb_value -mrb_str_pool(mrb_state *mrb, mrb_value str) +void +mrb_irep_free(mrb_state *mrb, mrb_irep *irep) { - 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; - } + int i; - 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); + if (irep->flags & MRB_IREP_NO_FREE) return; + if (!(irep->flags & MRB_ISEQ_NO_FREE)) + 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); } - 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'; + mrb_free(mrb, (void*)irep->pool); + } + 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); } - return mrb_obj_value(ns); + mrb_free(mrb, (void*)irep->lv); + mrb_debug_info_free(mrb, irep->debug_info); + mrb_free(mrb, irep); } MRB_API void @@ -226,33 +180,22 @@ 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->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 - } + if (!mrb) return; + 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_free_heap(mrb); - mrb_alloca_free(mrb); -#ifndef MRB_GC_FIXED_ARENA - mrb_free(mrb, mrb->arena); -#endif mrb_free(mrb, mrb); } @@ -272,11 +215,6 @@ mrb_add_irep(mrb_state *mrb) MRB_API mrb_value mrb_top_self(mrb_state *mrb) { - if (!mrb->top_self) { - mrb->top_self = (struct RObject*)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()); - } return mrb_obj_value(mrb->top_self); } @@ -293,7 +231,8 @@ mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f) stack_size = sizeof(mrb_atexit_func) * (mrb->atexit_stack_len + 1); if (mrb->atexit_stack_len == 0) { mrb->atexit_stack = (mrb_atexit_func*)mrb_malloc(mrb, stack_size); - } else { + } + else { mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size); } #endif diff --git a/src/string.c b/src/string.c index 23cd76747..03d9c8a9c 100644 --- a/src/string.c +++ b/src/string.c @@ -4,65 +4,209 @@ ** See Copyright Notice in mruby.h */ -#include <ctype.h> +#ifdef _MSC_VER +# define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#ifndef MRB_NO_FLOAT #include <float.h> +#include <math.h> +#endif #include <limits.h> #include <stddef.h> #include <stdlib.h> #include <string.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/range.h" -#include "mruby/string.h" -#include "mruby/numeric.h" -#include "mruby/re.h" - -const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/range.h> +#include <mruby/string.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; -static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2); -static mrb_value mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); +const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; -MRB_API mrb_int -mrb_str_strlen(mrb_state *mrb, struct RString *s) +#define mrb_obj_alloc_string(mrb) MRB_OBJ_ALLOC((mrb), MRB_TT_STRING, (mrb)->string_class) + +static struct RString* +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) { - mrb_int i, max = RSTR_LEN(s); - char *p = RSTR_PTR(s); + return str_init_normal_capa(mrb, s, p, len, len); +} - if (!p) return 0; - for (i=0; i<max; i++) { - if (p[i] == '\0') { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); - } +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->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++; } - return max; + 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 inline void -resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) +static struct RString* +str_init_fshared(const struct RString *orig, struct RString *s, struct RString *fshared) +{ + 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; +} + +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 { + return str_init_normal(mrb, s, p, len); + } +} + +static struct RString* +str_new_static(mrb_state *mrb, const char *p, size_t len) +{ + 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 struct RString* +str_new(mrb_state *mrb, const char *p, size_t len) +{ + 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 +mrb_str_new_capa(mrb_state *mrb, size_t capa) +{ + struct RString *s; + + 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); + } + + return mrb_obj_value(s); +} + +static void +resize_capa(mrb_state *mrb, struct RString *s, size_t capacity) { +#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 = 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 = capacity; + s->as.heap.ptr = (char*)mrb_realloc(mrb, RSTR_PTR(s), capacity+1); + s->as.heap.aux.capa = (mrb_ssize)capacity; + } +} + +MRB_API mrb_value +mrb_str_new(mrb_state *mrb, const char *p, size_t len) +{ + return mrb_obj_value(str_new(mrb, p, len)); +} + +MRB_API mrb_value +mrb_str_new_cstr(mrb_state *mrb, const char *p) +{ + struct RString *s; + size_t len; + + if (p) { + len = strlen(p); + } + else { + len = 0; } + + s = str_new(mrb, p, len); + + return mrb_obj_value(s); +} + +MRB_API mrb_value +mrb_str_new_static(mrb_state *mrb, const char *p, size_t len) +{ + struct RString *s = str_new_static(mrb, p, len); + return mrb_obj_value(s); } static void @@ -70,341 +214,643 @@ 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); } } -MRB_API void -mrb_str_modify(mrb_state *mrb, struct RString *s) +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.ptr = shared->ptr; - s->as.heap.aux.capa = shared->len; - RSTR_PTR(s)[s->as.heap.len] = '\0'; + 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 { - char *ptr, *p; - mrb_int len; - - p = RSTR_PTR(s); - len = s->as.heap.len; - ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); - if (p) { - memcpy(ptr, p, len); - } - ptr[len] = '\0'; - s->as.heap.ptr = ptr; - s->as.heap.aux.capa = len; + str_init_modifiable(mrb, s, s->as.heap.ptr, (size_t)s->as.heap.len); str_decref(mrb, shared); } - RSTR_UNSET_SHARED_FLAG(s); - return; } - if (RSTR_NOFREE_P(s)) { - char *p = s->as.heap.ptr; + 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); + } +} - s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1); - if (p) { - memcpy(RSTR_PTR(s), p, s->as.heap.len); - } - RSTR_PTR(s)[s->as.heap.len] = '\0'; - s->as.heap.aux.capa = s->as.heap.len; - RSTR_UNSET_NOFREE_FLAG(s); - return; +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"); } } -MRB_API mrb_value -mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) +void +mrb_gc_free_str(mrb_state *mrb, struct RString *str) +{ + if (RSTR_EMBED_P(str)) + /* no code */; + else if (RSTR_SHARED_P(str)) + str_decref(mrb, str->as.heap.aux.shared); + else if (!RSTR_NOFREE_P(str) && !RSTR_FSHARED_P(str)) + mrb_free(mrb, str->as.heap.ptr); +} + +#ifdef MRB_UTF8_STRING +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, + 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, + 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, +}; + +#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 (len == 1) return 1; + if (len > e - p) return 1; + for (i = 1; i < len; ++i) + if (utf8_islead(p[i])) + return 1; + return len; +} + +mrb_int +mrb_utf8_strlen(const char *str, mrb_int byte_len) +{ + mrb_int len = 0; + const char *p = str; + const char *e = p + byte_len; + + while (p < e) { + p += mrb_utf8len(p, e); + len++; + } + return len; +} + +static mrb_int +utf8_strlen(mrb_value str) { - mrb_int slen; struct RString *s = mrb_str_ptr(str); + mrb_int byte_len = RSTR_LEN(s); - mrb_str_modify(mrb, s); - slen = RSTR_LEN(s); - if (len != slen) { - if (slen < len || slen - len > 256) { - resize_capa(mrb, s, len); - } - RSTR_SET_LEN(s, len); - RSTR_PTR(s)[len] = '\0'; /* sentinel */ + if (RSTR_ASCII_P(s)) { + return byte_len; + } + 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 str; } -#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class)) +#define RSTRING_CHAR_LEN(s) utf8_strlen(s) -static struct RString* -str_new_static(mrb_state *mrb, const char *p, size_t len) +/* map character index to byte offset index */ +static mrb_int +chars2bytes(mrb_value s, mrb_int off, mrb_int idx) { - struct RString *s; - - if (len >= MRB_INT_MAX) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); + if (RSTR_ASCII_P(mrb_str_ptr(s))) { + return idx; } - s = mrb_obj_alloc_string(mrb); - s->as.heap.len = len; - s->as.heap.aux.capa = 0; /* nofree */ - s->as.heap.ptr = (char *)p; - s->flags = MRB_STR_NOFREE; + else { + mrb_int i, b, n; + const char *p = RSTRING_PTR(s) + off; + const char *e = RSTRING_END(s); - return s; + for (b=i=0; p<e && i<idx; i++) { + n = mrb_utf8len(p, e); + b += n; + p += n; + } + return b; + } } -static struct RString* -str_new(mrb_state *mrb, const char *p, size_t len) +/* map byte offset to character index */ +static mrb_int +bytes2chars(char *p, mrb_int len, mrb_int bi) { - struct RString *s; + const char *e = p + (size_t)len; + const char *pivot = p + bi; + mrb_int i; - if (mrb_ro_data_p(p)) { - return str_new_static(mrb, p, len); + for (i = 0; p < pivot; i ++) { + p += mrb_utf8len(p, e); } - 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); - } - } else { - if (len >= MRB_INT_MAX) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); + 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; } - s->as.heap.len = len; - s->as.heap.aux.capa = len; - s->as.heap.ptr = (char *)mrb_malloc(mrb, len+1); - if (p) { - memcpy(s->as.heap.ptr, p, len); + + p = ptr; + while (p > beg) { + p --; + if ((*p & 0xc0) != 0x80) { + int clen = mrb_utf8len(p, end); + if (clen > ptr - p) return p; + break; + } } } - RSTR_PTR(s)[len] = '\0'; - return s; -} -static inline void -str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj) -{ - s->c = mrb_str_ptr(obj)->c; + return ptr; } -static mrb_value -mrb_str_new_empty(mrb_state *mrb, mrb_value str) +static const char * +char_backtrack(const char *ptr, const char *end) { - struct RString *s = str_new(mrb, 0, 0); + if (ptr < end) { + const int utf8_bytelen_max = 4; + const char *p; - str_with_class(mrb, s, str); - return mrb_obj_value(s); -} + if (end - ptr > utf8_bytelen_max) { + ptr = end - utf8_bytelen_max; + } -#ifndef MRB_STR_BUF_MIN_SIZE -# define MRB_STR_BUF_MIN_SIZE 128 -#endif + p = end; + while (p > ptr) { + p --; + if ((*p & 0xc0) != 0x80) { + int clen = utf8len_codepage[(unsigned char)*p]; + if (clen == end - p) { return p; } + break; + } + } + } -MRB_API mrb_value -mrb_str_buf_new(mrb_state *mrb, size_t capa) + 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) { - struct RString *s; + /* Based on Quick Search algorithm (Boyer-Moore-Horspool algorithm) */ - s = mrb_obj_alloc_string(mrb); + ptrdiff_t qstable[1 << CHAR_BIT]; - if (capa >= MRB_INT_MAX) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big"); + /* 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); + } } - if (capa < MRB_STR_BUF_MIN_SIZE) { - capa = MRB_STR_BUF_MIN_SIZE; + + /* 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); } - s->as.heap.len = 0; - s->as.heap.aux.capa = capa; - s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1); - RSTR_PTR(s)[0] = '\0'; - return mrb_obj_value(s); + return -1; } -static void -str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len) +static mrb_int +str_index_str_by_char(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) { - size_t capa; - size_t total; - ptrdiff_t off = -1; + 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; - if (len == 0) return; - mrb_str_modify(mrb, s); - if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) { - off = ptr - RSTR_PTR(s); + for (; pos > 0; pos --) { + if (pend - p < 1) { return -1; } + p += mrb_utf8len(p, pend); } - if (RSTR_EMBED_P(s)) - capa = RSTRING_EMBED_LEN_MAX; - else - capa = s->as.heap.aux.capa; + if (slen < 1) { return off; } - if (RSTR_LEN(s) >= MRB_INT_MAX - (mrb_int)len) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); - } - total = RSTR_LEN(s)+len; - if (capa <= total) { - while (total > capa) { - if (capa + 1 >= MRB_INT_MAX / 2) { - capa = (total + 4095) / 4096; - break; + 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, 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) +{ + if (n + m < MRB_QS_SHORT_STRING_LENGTH) { + const unsigned char *y = ys; + const unsigned char *ye = ys+n-m+1; + + 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); } - capa = (capa + 1) * 2; + y++; } - resize_capa(mrb, s, capa); + return -1; } - if (off != -1) { - ptr = RSTR_PTR(s) + off; + 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; } - memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len); - mrb_assert_int_fit(size_t, total, mrb_int, MRB_INT_MAX); - RSTR_SET_LEN(s, total); - RSTR_PTR(s)[total] = '\0'; /* sentinel */ } -MRB_API mrb_value -mrb_str_new(mrb_state *mrb, const char *p, size_t len) +static mrb_int +mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n) { - return mrb_obj_value(str_new(mrb, p, len)); -} + const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0; -/* - * call-seq: (Caution! NULL string) - * String.new(str="") => new_str - * - * Returns a new string object containing a copy of <i>str</i>. - */ + if (m > n) return -1; + else if (m == n) { + return memcmp(x0, y0, m) == 0 ? 0 : -1; + } + else if (m < 1) { + return 0; + } + else if (m == 1) { + const unsigned char *ys = (const unsigned char *)memchr(y, *x, n); -MRB_API mrb_value -mrb_str_new_cstr(mrb_state *mrb, const char *p) + if (ys) + return (mrb_int)(ys - y); + else + return -1; + } + return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); +} + +static void +str_share(mrb_state *mrb, struct RString *orig, struct RString *s) { - struct RString *s; - size_t len; + size_t len = (size_t)orig->as.heap.len; - if (p) { - len = strlen(p); + mrb_assert(!RSTR_EMBED_P(orig)); + 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)) { + str_init_fshared(orig, s, orig->as.heap.aux.fshared); } else { - len = 0; + 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; + } + str_init_shared(mrb, orig, s, NULL); + str_init_shared(mrb, orig, orig, s->as.heap.aux.shared); } +} - s = str_new(mrb, p, 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); + s = mrb_obj_alloc_string(mrb); + if (RSTR_EMBEDDABLE_P(len)) { + str_init_embed(s, RSTR_PTR(orig)+beg, len); + } + else { + 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); } -MRB_API mrb_value -mrb_str_new_static(mrb_state *mrb, const char *p, size_t len) +static void +str_range_to_bytes(mrb_value str, mrb_int *pos, mrb_int *len) { - struct RString *s = str_new_static(mrb, p, len); - return mrb_obj_value(s); + *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) +{ + str_range_to_bytes(str, &beg, &len); + return mrb_str_byte_subseq(mrb, str, beg, len); } +#else +#define str_subseq(mrb, str, beg, len) mrb_str_byte_subseq(mrb, str, beg, len) +#endif -void -mrb_gc_free_str(mrb_state *mrb, struct RString *str) +mrb_bool +mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp) { - if (RSTR_EMBED_P(str)) - /* no code */; - else if (RSTR_SHARED_P(str)) - str_decref(mrb, str->as.heap.aux.shared); - else if (!RSTR_NOFREE_P(str)) - mrb_free(mrb, str->as.heap.ptr); + if (str_len < *begp || *lenp < 0) return FALSE; + if (*begp < 0) { + *begp += str_len; + if (*begp < 0) return FALSE; + } + if (*lenp > str_len - *begp) + *lenp = str_len - *begp; + if (*lenp <= 0) { + *lenp = 0; + } + return TRUE; } -MRB_API char* -mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) +static mrb_value +str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { - struct RString *s; + return mrb_str_beg_len(RSTRING_CHAR_LEN(str), &beg, &len) ? + str_subseq(mrb, str, beg, len) : mrb_nil_value(); +} - if (!mrb_string_p(str0)) { - mrb_raise(mrb, E_TYPE_ERROR, "expected String"); +MRB_API mrb_int +mrb_str_index(mrb_state *mrb, mrb_value str, const char *sptr, mrb_int slen, mrb_int offset) +{ + mrb_int pos; + char *s; + mrb_int len; + + len = RSTRING_LEN(str); + if (offset < 0) { + offset += len; + if (offset < 0) return -1; + } + if (len - offset < slen) return -1; + s = RSTRING_PTR(str); + if (offset) { + s += offset; + } + if (slen == 0) return offset; + /* need proceed one character at a time */ + len = RSTRING_LEN(str) - offset; + pos = mrb_memsearch(sptr, slen, s, len); + if (pos < 0) return pos; + return pos + offset; +} + +static mrb_int +str_index_str(mrb_state *mrb, mrb_value str, mrb_value str2, mrb_int offset) +{ + const char *ptr; + mrb_int len; + + ptr = RSTRING_PTR(str2); + len = RSTRING_LEN(str2); + + return mrb_str_index(mrb, str, ptr, len, offset); +} + +static mrb_value +str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) +{ + size_t len; + + mrb_check_frozen(mrb, s1); + if (s1 == s2) return mrb_obj_value(s1); + RSTR_COPY_ASCII_FLAG(s1, s2); + if (RSTR_SHARED_P(s1)) { + str_decref(mrb, s1->as.heap.aux.shared); + } + 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); } - 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"); + len = (size_t)RSTR_LEN(s2); + if (RSTR_EMBEDDABLE_P(len)) { + str_init_embed(s1, RSTR_PTR(s2), len); } - return RSTR_PTR(s); + else { + str_share(mrb, s2, s1); + } + + return mrb_obj_value(s1); } -static void -str_make_shared(mrb_state *mrb, struct RString *s) +static mrb_int +str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) { - if (!RSTR_SHARED_P(s)) { - mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string)); + const char *s, *sbeg, *t; + struct RString *ps = mrb_str_ptr(str); + mrb_int len = RSTRING_LEN(sub); - shared->refcnt = 1; - if (RSTR_EMBED_P(s)) { - const mrb_int len = RSTR_EMBED_LEN(s); - char *const tmp = (char *)mrb_malloc(mrb, len+1); - memcpy(tmp, s->as.ary, len); - tmp[len] = '\0'; - RSTR_UNSET_EMBED_FLAG(s); - s->as.heap.ptr = tmp; - s->as.heap.len = len; - shared->nofree = FALSE; - shared->ptr = s->as.heap.ptr; - } - else if (RSTR_NOFREE_P(s)) { - shared->nofree = TRUE; - shared->ptr = s->as.heap.ptr; - RSTR_UNSET_NOFREE_FLAG(s); - } - else { - shared->nofree = FALSE; - if (s->as.heap.aux.capa > s->as.heap.len) { - s->as.heap.ptr = shared->ptr = (char *)mrb_realloc(mrb, s->as.heap.ptr, s->as.heap.len+1); - } - else { - shared->ptr = s->as.heap.ptr; + /* substring longer than string */ + if (RSTR_LEN(ps) < len) return -1; + if (RSTR_LEN(ps) - pos < len) { + pos = RSTR_LEN(ps) - len; + } + sbeg = RSTR_PTR(ps); + 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 = char_backtrack(sbeg, s); } - shared->len = s->as.heap.len; - s->as.heap.aux.shared = shared; - RSTR_SET_SHARED_FLAG(s); + return -1; + } + else { + return pos; } } -/* - * 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) +MRB_API mrb_int +mrb_str_strlen(mrb_state *mrb, struct RString *s) { - struct RString *s1 = mrb_str_ptr(self), *s2; - mrb_int len; + mrb_int i, max = RSTR_LEN(s); + char *p = RSTR_PTR(s); - mrb_str_modify(mrb, s1); - if (!mrb_string_p(other)) { - other = mrb_str_to_str(mrb, other); + if (!p) return 0; + for (i=0; i<max; i++) { + if (p[i] == '\0') { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); + } } - s2 = mrb_str_ptr(other); - len = RSTR_LEN(s1) + RSTR_LEN(s2); + return max; +} + +#ifdef _WIN32 +#include <windows.h> + +char* +mrb_utf8_from_locale(const char *str, int len) +{ + wchar_t* wcsp; + char* mbsp; + int mbssize, wcssize; + + if (len == 0) + return strdup(""); + if (len == -1) + len = (int)strlen(str); + wcssize = MultiByteToWideChar(GetACP(), 0, str, len, NULL, 0); + wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); + if (!wcsp) + return NULL; + wcssize = MultiByteToWideChar(GetACP(), 0, str, len, wcsp, wcssize + 1); + wcsp[wcssize] = 0; + + mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); + mbsp = (char*) malloc((mbssize + 1)); + if (!mbsp) { + free(wcsp); + return NULL; + } + mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); + mbsp[mbssize] = 0; + free(wcsp); + return mbsp; +} + +char* +mrb_locale_from_utf8(const char *utf8, int len) +{ + wchar_t* wcsp; + char* mbsp; + int mbssize, wcssize; + + if (len == 0) + return strdup(""); + if (len == -1) + len = (int)strlen(utf8); + wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, NULL, 0); + wcsp = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t)); + if (!wcsp) + return NULL; + wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, len, wcsp, wcssize + 1); + wcsp[wcssize] = 0; + mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, NULL, 0, NULL, NULL); + mbsp = (char*) malloc((mbssize + 1)); + if (!mbsp) { + free(wcsp); + return NULL; + } + mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsp, -1, mbsp, mbssize, NULL, NULL); + mbsp[mbssize] = 0; + free(wcsp); + return mbsp; +} +#endif + +MRB_API void +mrb_str_modify_keep_ascii(mrb_state *mrb, struct RString *s) +{ + mrb_check_frozen(mrb, s); + str_modify_keep_ascii(mrb, s); +} - if (RSTRING_CAPA(self) < len) { - resize_capa(mrb, s1, len); +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 +mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) +{ + mrb_int slen; + struct RString *s = mrb_str_ptr(str); + + if (len < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "negative (or overflowed) string size"); } - memcpy(RSTR_PTR(s1)+RSTR_LEN(s1), RSTR_PTR(s2), RSTR_LEN(s2)); - RSTR_SET_LEN(s1, len); - RSTR_PTR(s1)[len] = '\0'; + mrb_str_modify(mrb, s); + slen = RSTR_LEN(s); + if (len != slen) { + if (slen < len || slen - len > 256) { + resize_capa(mrb, s, len); + } + RSTR_SET_LEN(s, len); + RSTR_PTR(s)[len] = '\0'; /* sentinel */ + } + return str; +} + +MRB_API char* +mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) +{ + struct RString *s; + + check_null_byte(mrb, str0); + s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0)); + return RSTR_PTR(s); +} + +MRB_API void +mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value 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) { @@ -422,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) @@ -440,15 +889,22 @@ mrb_str_plus_m(mrb_state *mrb, mrb_value self) /* 15.2.10.5.33 */ /* * call-seq: - * len = strlen(String("abcd")) + * "abcd".size => int * * Returns the length of string. */ static mrb_value mrb_str_size(mrb_state *mrb, mrb_value self) { - struct RString *s = mrb_str_ptr(self); - return mrb_fixnum_value(RSTR_LEN(s)); + mrb_int len = RSTRING_CHAR_LEN(self); + return mrb_fixnum_value(len); +} + +static mrb_value +mrb_str_bytesize(mrb_state *mrb, mrb_value self) +{ + mrb_int len = RSTRING_LEN(self); + return mrb_fixnum_value(len); } /* 15.2.10.5.1 */ @@ -472,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); @@ -490,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); } @@ -553,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(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); @@ -594,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); } @@ -620,171 +1053,122 @@ 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); } -void -mrb_noregexp(mrb_state *mrb, mrb_value self) +/* obslete: use RSTRING_LEN() */ +MRB_API mrb_int +mrb_string_value_len(mrb_state *mrb, mrb_value ptr) { - mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented"); + mrb_to_str(mrb, ptr); + return RSTRING_LEN(ptr); } -void -mrb_regexp_check(mrb_state *mrb, mrb_value obj) +MRB_API mrb_value +mrb_str_dup(mrb_state *mrb, mrb_value str) { - if (mrb_regexp_p(mrb, obj)) { - mrb_noregexp(mrb, obj); - } + struct RString *s = mrb_str_ptr(str); + struct RString *dup = str_new(mrb, 0, 0); + + return str_replace(mrb, dup, s); } -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, qstable[256]; +enum str_convert_range { + /* `beg` and `len` are byte unit in `0 ... str.bytesize` */ + STR_BYTE_RANGE_CORRECTED = 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 y - ys; - } - return -1; -} + /* `beg` and `len` are char unit in any range */ + STR_CHAR_RANGE = 2, -static mrb_int -mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n) -{ - const unsigned char *x = (const unsigned char *)x0, *y = (const unsigned char *)y0; + /* `beg` and `len` are char unit in `0 ... str.size` */ + STR_CHAR_RANGE_CORRECTED = 3, - if (m > n) return -1; - else if (m == n) { - return memcmp(x0, y0, m) == 0 ? 0 : -1; - } - else if (m < 1) { - return 0; - } - else if (m == 1) { - const unsigned char *ys = y, *ye = ys + n; - for (; y < ye; ++y) { - if (*x == *y) - return y - ys; - } - return -1; - } - return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); -} + /* `beg` is out of range */ + STR_OUT_OF_RANGE = -1 +}; -static mrb_int -mrb_str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset) +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) { - mrb_int pos; - char *s, *sptr; - mrb_int len, slen; - - len = RSTRING_LEN(str); - slen = RSTRING_LEN(sub); - if (offset < 0) { - offset += len; - if (offset < 0) return -1; + if (!mrb_undef_p(alen)) { + *beg = mrb_as_int(mrb, indx); + *len = mrb_as_int(mrb, alen); + return STR_CHAR_RANGE; } - if (len - offset < slen) return -1; - s = RSTRING_PTR(str); - if (offset) { - s += offset; - } - if (slen == 0) return offset; - /* need proceed one character at a time */ - sptr = RSTRING_PTR(sub); - slen = RSTRING_LEN(sub); - len = RSTRING_LEN(str) - offset; - pos = mrb_memsearch(sptr, slen, s, len); - if (pos < 0) return pos; - return pos + offset; -} + else { + switch (mrb_type(indx)) { + case MRB_TT_INTEGER: + *beg = mrb_integer(indx); + *len = 1; + return STR_CHAR_RANGE; -MRB_API mrb_value -mrb_str_dup(mrb_state *mrb, mrb_value str) -{ - struct RString *s = mrb_str_ptr(str); - struct RString *dup = str_new(mrb, 0, 0); + 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; - str_with_class(mrb, dup, str); - return str_replace(mrb, dup, s); + 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 Integer"); + } + } + return STR_OUT_OF_RANGE; } static mrb_value -mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx) +mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen) { - mrb_int idx; - - mrb_regexp_check(mrb, indx); - switch (mrb_type(indx)) { - case MRB_TT_FLOAT: - indx = mrb_flo_to_fixnum(mrb, indx); - /* fall through */ - case MRB_TT_FIXNUM: - idx = mrb_fixnum(indx); + mrb_int beg, len; -num_index: - str = mrb_str_substr(mrb, str, idx, 1); - if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); + 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 MRB_TT_STRING: - if (mrb_str_index(mrb, str, indx, 0) != -1) + case STR_BYTE_RANGE_CORRECTED: + if (mrb_string_p(indx)) { return mrb_str_dup(mrb, indx); - return mrb_nil_value(); - - case MRB_TT_RANGE: - /* check if indx is Range */ - { - mrb_int beg, len; - - len = RSTRING_LEN(str); - if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) { - return mrb_str_subseq(mrb, str, beg, len); - } - else { - return mrb_nil_value(); - } } + else { + return mrb_str_byte_subseq(mrb, str, beg, len); + } + case STR_OUT_OF_RANGE: default: - idx = mrb_fixnum(indx); - goto num_index; + return mrb_nil_value(); } - return mrb_nil_value(); /* not reached */ } /* 15.2.10.5.6 */ @@ -794,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 @@ -831,17 +1213,198 @@ static mrb_value mrb_str_aref_m(mrb_state *mrb, mrb_value str) { mrb_value a1, a2; - int argc; - argc = mrb_get_args(mrb, "o|o", &a1, &a2); - if (argc == 2) { - mrb_regexp_check(mrb, a1); - return mrb_str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2)); + 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); +} + +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); + } + RSTR_SET_LEN(str, newlen); + strp[newlen] = '\0'; + + if (len - newlen >= shrink_threshold) { + resize_capa(mrb, str, newlen); + } + + 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); } - if (argc != 1) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc)); +} + +/* + * 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; } - return mrb_str_aref(mrb, str, a1); + mrb_str_aset(mrb, str, indx, alen, replace); + return str; } /* 15.2.10.5.8 */ @@ -864,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)) { @@ -906,7 +1469,7 @@ mrb_str_capitalize(mrb_state *mrb, mrb_value self) /* 15.2.10.5.10 */ /* * call-seq: - * str.chomp!(separator=$/) => str or nil + * str.chomp!(separator="\n") => str or nil * * Modifies <i>str</i> in place as described for <code>String#chomp</code>, * returning <i>str</i>, or <code>nil</code> if no modifications were made. @@ -919,11 +1482,13 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) char *p, *pp; mrb_int rslen; mrb_int len; + mrb_int argc; struct RString *s = mrb_str_ptr(str); - mrb_str_modify(mrb, s); + argc = mrb_get_args(mrb, "|S", &rs); + mrb_str_modify_keep_ascii(mrb, s); len = RSTR_LEN(s); - if (mrb_get_args(mrb, "|S", &rs) == 0) { + if (argc == 0) { if (len == 0) return mrb_nil_value(); smart_chomp: if (RSTR_PTR(s)[len-1] == '\n') { @@ -980,12 +1545,11 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) /* 15.2.10.5.9 */ /* * call-seq: - * str.chomp(separator=$/) => new_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" @@ -1020,10 +1584,21 @@ 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 = mrb_utf8len(p, e); + if (p + clen>=e) break; + p += clen; + } + len = p - t; +#else len = RSTR_LEN(s) - 1; +#endif if (RSTR_PTR(s)[len] == '\n') { if (len > 0 && RSTR_PTR(s)[len-1] == '\r') { @@ -1078,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) { @@ -1100,7 +1675,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) * * Returns a copy of <i>str</i> with all uppercase letters replaced with their * lowercase counterparts. The operation is locale insensitive---only - * characters ``A'' to ``Z'' are affected. + * characters 'A' to 'Z' are affected. * * "hEllO".downcase #=> "hello" */ @@ -1142,72 +1717,38 @@ 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); } -static mrb_value -mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) -{ - struct RString *orig, *s; - mrb_shared_string *shared; - - orig = mrb_str_ptr(str); - if (RSTR_EMBED_P(orig)) { - s = str_new(mrb, orig->as.ary+beg, len); - } else { - str_make_shared(mrb, orig); - shared = orig->as.heap.aux.shared; - s = mrb_obj_alloc_string(mrb); - s->as.heap.ptr = orig->as.heap.ptr + beg; - s->as.heap.len = len; - s->as.heap.aux.shared = shared; - RSTR_SET_SHARED_FLAG(s); - shared->refcnt++; - } - - return mrb_obj_value(s); -} - MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { - if (len < 0) return mrb_nil_value(); - if (!RSTRING_LEN(str)) { - len = 0; - } - if (beg > RSTRING_LEN(str)) return mrb_nil_value(); - if (beg < 0) { - beg += RSTRING_LEN(str); - if (beg < 0) return mrb_nil_value(); - } - if (beg + len > RSTRING_LEN(str)) - len = RSTRING_LEN(str) - beg; - if (len <= 0) { - len = 0; - } - return mrb_str_subseq(mrb, str, beg, len); + return str_substr(mrb, str, beg, len); } -mrb_int +uint32_t mrb_str_hash(mrb_state *mrb, mrb_value str) { /* 1-8-7 */ struct RString *s = mrb_str_ptr(str); mrb_int len = RSTR_LEN(s); char *p = RSTR_PTR(s); - mrb_int 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 key + (key>>5); + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; } /* 15.2.10.5.20 */ @@ -1240,146 +1781,52 @@ mrb_str_hash_m(mrb_state *mrb, mrb_value self) static mrb_value mrb_str_include(mrb_state *mrb, mrb_value self) { - mrb_int i; mrb_value str2; - mrb_bool include_p; - - mrb_get_args(mrb, "o", &str2); - if (mrb_fixnum_p(str2)) { - include_p = (memchr(RSTRING_PTR(self), mrb_fixnum(str2), RSTRING_LEN(self)) != NULL); - } - else { - str2 = mrb_str_to_str(mrb, str2); - i = mrb_str_index(mrb, self, str2, 0); - include_p = (i != -1); - } - - return mrb_bool_value(include_p); + mrb_get_args(mrb, "S", &str2); + if (str_index_str(mrb, self, str2, 0) < 0) + return mrb_bool_value(FALSE); + return mrb_bool_value(TRUE); } /* 15.2.10.5.22 */ /* * 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; - mrb_get_args(mrb, "*", &argv, &argc); - if (argc == 2) { - pos = mrb_fixnum(argv[1]); - sub = argv[0]; - } - 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); - if (pos < 0) { - pos += RSTRING_LEN(str); + else if (pos < 0) { + mrb_int clen = RSTRING_CHAR_LEN(str); + pos += clen; if (pos < 0) { return mrb_nil_value(); } } - - switch (mrb_type(sub)) { - case MRB_TT_FIXNUM: { - int c = mrb_fixnum(sub); - mrb_int len = RSTRING_LEN(str); - unsigned char *p = (unsigned char*)RSTRING_PTR(str); - - for (;pos<len;pos++) { - if (p[pos] == c) return mrb_fixnum_value(pos); - } - return mrb_nil_value(); - } - - 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 = mrb_str_index(mrb, str, sub, pos); - break; - } + pos = str_index_str_by_char(mrb, str, sub, pos); if (pos == -1) return mrb_nil_value(); + BYTES_ALIGN_CHECK(pos); return mrb_fixnum_value(pos); } -#define STR_REPLACE_SHARED_MIN 10 - -static mrb_value -str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) -{ - long len; - - len = RSTR_LEN(s2); - if (RSTR_SHARED_P(s1)) { - str_decref(mrb, s1->as.heap.aux.shared); - } - else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1)) { - mrb_free(mrb, s1->as.heap.ptr); - } - - RSTR_UNSET_NOFREE_FLAG(s1); - - if (RSTR_SHARED_P(s2)) { -L_SHARE: - RSTR_UNSET_EMBED_FLAG(s1); - s1->as.heap.ptr = s2->as.heap.ptr; - s1->as.heap.len = len; - s1->as.heap.aux.shared = s2->as.heap.aux.shared; - RSTR_SET_SHARED_FLAG(s1); - s1->as.heap.aux.shared->refcnt++; - } - else { - if (len <= RSTRING_EMBED_LEN_MAX) { - RSTR_UNSET_SHARED_FLAG(s1); - RSTR_SET_EMBED_FLAG(s1); - memcpy(s1->as.ary, RSTR_PTR(s2), len); - RSTR_SET_EMBED_LEN(s1, len); - } - else { - str_make_shared(mrb, s2); - goto L_SHARE; - } - } - - return mrb_obj_value(s1); -} - /* 15.2.10.5.24 */ /* 15.2.10.5.28 */ /* @@ -1410,9 +1857,11 @@ mrb_str_init(mrb_state *mrb, mrb_value self) { mrb_value str2; - if (mrb_get_args(mrb, "|S", &str2) == 1) { - str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2)); + if (mrb_get_args(mrb, "|S", &str2) == 0) { + struct RString *s = str_new(mrb, 0, 0); + str2 = mrb_obj_value(s); } + str_replace(mrb, mrb_str_ptr(self), mrb_str_ptr(str2)); return self; } @@ -1424,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 @@ -1446,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 @@ -1487,45 +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) -{ - return mrb_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str"); -} - -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"); -} - -/* ---------------------------------- */ -/* 15.2.10.5.29 */ -/* - * call-seq: - * str.reverse => new_str - * - * Returns a new string with the characters from <i>str</i> in reverse order. - * - * "stressed".reverse #=> "desserts" - */ -static mrb_value -mrb_str_reverse(mrb_state *mrb, mrb_value str) +static inline void +str_reverse(char *p, char *e) { - struct RString *s2; - char *s, *e, *p; - - if (RSTRING_LEN(str) <= 1) return mrb_str_dup(mrb, str); - - s2 = str_new(mrb, 0, RSTRING_LEN(str)); - str_with_class(mrb, s2, str); - s = RSTRING_PTR(str); e = RSTRING_END(str) - 1; - p = RSTR_PTR(s2); + char c; - while (e >= s) { - *p++ = *e--; + while (p < e) { + c = *p; + *p++ = *e; + *e-- = c; } - return mrb_obj_value(s2); } /* 15.2.10.5.30 */ @@ -1540,146 +1965,96 @@ mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); char *p, *e; - char c; - mrb_str_modify(mrb, s); - if (RSTR_LEN(s) > 1) { +#ifdef MRB_UTF8_STRING + mrb_int utf8_len = RSTRING_CHAR_LEN(str); + 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) - 1; - while (p < e) { - c = *p; - *p++ = *e; - *e-- = c; + e = p + RSTR_LEN(s); + while (p<e) { + mrb_int clen = mrb_utf8len(p, e); + str_reverse(p, p + clen - 1); + p += clen; } + goto bytes; + } +#endif + + if (RSTR_LEN(s) > 1) { + mrb_str_modify(mrb, s); + goto bytes; } return str; + + bytes: + p = RSTR_PTR(s); + e = p + RSTR_LEN(s) - 1; + str_reverse(p, e); + return str; } +/* ---------------------------------- */ +/* 15.2.10.5.29 */ /* * call-seq: - * str.rindex(substring [, fixnum]) => fixnum or nil - * str.rindex(fixnum [, fixnum]) => fixnum or nil - * str.rindex(regexp [, fixnum]) => fixnum or nil + * str.reverse => new_str * - * 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 a new string with the characters from <i>str</i> in reverse order. * - * "hello".rindex('e') #=> 1 - * "hello".rindex('l') #=> 3 - * "hello".rindex('a') #=> nil - * "hello".rindex(101) #=> 1 - * "hello".rindex(/[aeiou]/, -2) #=> 1 + * "stressed".reverse #=> "desserts" */ -static mrb_int -mrb_str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) +static mrb_value +mrb_str_reverse(mrb_state *mrb, mrb_value str) { - char *s, *sbeg, *t; - struct RString *ps = mrb_str_ptr(str); - mrb_int len = RSTRING_LEN(sub); - - /* substring longer than string */ - if (RSTR_LEN(ps) < len) return -1; - if (RSTR_LEN(ps) - pos < len) { - pos = RSTR_LEN(ps) - len; - } - sbeg = RSTR_PTR(ps); - s = RSTR_PTR(ps) + pos; - t = RSTRING_PTR(sub); - if (len) { - while (sbeg <= s) { - if (memcmp(s, t, len) == 0) { - return s - RSTR_PTR(ps); - } - s--; - } - return -1; - } - else { - return pos; - } + mrb_value str2 = mrb_str_dup(mrb, str); + mrb_str_reverse_bang(mrb, str2); + return str2; } /* 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_m(mrb_state *mrb, mrb_value str) +mrb_str_rindex(mrb_state *mrb, mrb_value str) { - mrb_value *argv; - mrb_int argc; mrb_value sub; - mrb_value vpos; - mrb_int pos, len = RSTRING_LEN(str); + mrb_int pos, len = RSTRING_CHAR_LEN(str); - mrb_get_args(mrb, "*", &argv, &argc); - if (argc == 2) { - sub = argv[0]; - vpos = argv[1]; - pos = mrb_fixnum(vpos); + 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); + 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); } - mrb_regexp_check(mrb, sub); - - switch (mrb_type(sub)) { - case MRB_TT_FIXNUM: { - int c = mrb_fixnum(sub); - unsigned char *p = (unsigned char*)RSTRING_PTR(str); - - for (pos=len-1;pos>=0;pos--) { - if (p[pos] == c) return mrb_fixnum_value(pos); - } - return mrb_nil_value(); - } - - 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 = mrb_str_rindex(mrb, str, sub, pos); - if (pos >= 0) return mrb_fixnum_value(pos); - break; - - } /* end of switch (TYPE(sub)) */ return mrb_nil_value(); } @@ -1687,23 +2062,18 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) /* * call-seq: - * str.split(pattern=$;, [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 @@ -1714,11 +2084,6 @@ mrb_str_rindex_m(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"] - * "1, 2.34,56, 7".split(%r{,\s*}) #=> ["1", "2.34", "56", "7"] - * "hello".split(//) #=> ["h", "e", "l", "l", "o"] - * "hello".split(//, 3) #=> ["h", "e", "llo"] - * "hi mom".split(%r{\s*}) #=> ["h", "i", "m", "o", "m"] * * "mellow yellow".split("ello") #=> ["m", "w y", "w"] * "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"] @@ -1729,13 +2094,14 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) static mrb_value mrb_str_split_m(mrb_state *mrb, mrb_value str) { - int argc; + mrb_int argc; mrb_value spat = mrb_nil_value(); - enum {awk, string, regexp} split_type = string; - long i = 0, lim_p; + enum {awk, string} split_type = string; + mrb_int i = 0; mrb_int beg; mrb_int end; mrb_int lim = 0; + mrb_bool lim_p; mrb_value result, tmp; argc = mrb_get_args(mrb, "|oi", &spat, &lim); @@ -1752,91 +2118,74 @@ 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); beg = 0; if (split_type == awk) { - char *ptr = RSTRING_PTR(str); - char *eptr = RSTRING_END(str); - char *bptr = ptr; mrb_bool skip = TRUE; + mrb_int idx = 0; + mrb_int str_len = RSTRING_LEN(str); unsigned int c; + int ai = mrb_gc_arena_save(mrb); - end = beg; - while (ptr < eptr) { - int ai = mrb_gc_arena_save(mrb); - c = (unsigned char)*ptr++; + idx = end = beg; + while (idx < str_len) { + c = (unsigned char)RSTRING_PTR(str)[idx++]; if (skip) { if (ISSPACE(c)) { - beg = ptr - bptr; + beg = idx; } else { - end = ptr - bptr; + end = idx; skip = FALSE; if (lim_p && lim <= i) break; } } else if (ISSPACE(c)) { - mrb_ary_push(mrb, result, mrb_str_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 = ptr - bptr; + beg = idx; if (lim_p) ++i; } else { - end = ptr - bptr; + end = idx; } } } - else if (split_type == string) { - char *ptr = RSTRING_PTR(str); /* s->as.ary */ - char *temp = ptr; - char *eptr = RSTRING_END(str); - mrb_int slen = RSTRING_LEN(spat); + else { /* split_type == string */ + mrb_int str_len = RSTRING_LEN(str); + mrb_int pat_len = RSTRING_LEN(spat); + mrb_int idx = 0; + int ai = mrb_gc_arena_save(mrb); - if (slen == 0) { - int ai = mrb_gc_arena_save(mrb); - while (ptr < eptr) { - mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, ptr-temp, 1)); - mrb_gc_arena_restore(mrb, ai); - ptr++; - if (lim_p && lim <= ++i) break; + while (idx < str_len) { + if (pat_len > 0) { + end = mrb_memsearch(RSTRING_PTR(spat), pat_len, RSTRING_PTR(str)+idx, str_len - idx); + if (end < 0) break; } - } - else { - char *sptr = RSTRING_PTR(spat); - int ai = mrb_gc_arena_save(mrb); - - while (ptr < eptr && - (end = mrb_memsearch(sptr, slen, ptr, eptr - ptr)) >= 0) { - mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, ptr - temp, end)); - mrb_gc_arena_restore(mrb, ai); - ptr += end + slen; - if (lim_p && lim <= ++i) break; + else { + end = chars2bytes(str, idx, 1); } + 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 = ptr - temp; - } - else { - mrb_noregexp(mrb, str); + beg = idx; } 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 = mrb_str_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); } @@ -1850,13 +2199,14 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) return result; } -MRB_API mrb_value -mrb_cstr_to_inum(mrb_state *mrb, const char *str, 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; + const char *p = str; + const char *pend = str + len; char sign = 1; - int c, uscore; - unsigned long n = 0; + int c; + mrb_int n = 0; mrb_int val; #define conv_digit(c) \ @@ -1865,26 +2215,23 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) ISUPPER(c) ? ((c) - 'A' + 10) : \ -1) - if (!str) { + if (!p) { if (badcheck) goto bad; return mrb_fixnum_value(0); } - while (ISSPACE(*str)) str++; + while (p<pend && ISSPACE(*p)) + p++; - if (str[0] == '+') { - str++; + if (p[0] == '+') { + p++; } - else if (str[0] == '-') { - str++; + else if (p[0] == '-') { + p++; sign = 0; } - if (str[0] == '+' || str[0] == '-') { - if (badcheck) goto bad; - return mrb_fixnum_value(0); - } if (base <= 0) { - if (str[0] == '0') { - switch (str[1]) { + if (p[0] == '0') { + switch (p[1]) { case 'x': case 'X': base = 16; break; @@ -1899,6 +2246,7 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) break; default: base = 8; + break; } } else if (base < -1) { @@ -1910,102 +2258,140 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) } switch (base) { case 2: - if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) { - str += 2; + if (p[0] == '0' && (p[1] == 'b'||p[1] == 'B')) { + p += 2; } break; case 3: break; case 8: - if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) { - str += 2; + if (p[0] == '0' && (p[1] == 'o'||p[1] == 'O')) { + p += 2; } case 4: case 5: case 6: case 7: break; case 10: - if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) { - str += 2; + if (p[0] == '0' && (p[1] == 'd'||p[1] == 'D')) { + p += 2; } case 9: case 11: case 12: case 13: case 14: case 15: break; case 16: - if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) { - str += 2; + if (p[0] == '0' && (p[1] == 'x'||p[1] == 'X')) { + p += 2; } 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) { */ - if (*str == '0') { /* squeeze preceeding 0s */ - uscore = 0; - while ((c = *++str) == '0' || c == '_') { + if (p>=pend) { + if (badcheck) goto bad; + return mrb_fixnum_value(0); + } + if (*p == '0') { /* squeeze preceding 0s */ + p++; + while (p<pend) { + c = *p++; if (c == '_') { - if (++uscore >= 2) + if (p<pend && *p == '_') { + if (badcheck) goto bad; break; + } + continue; + } + if (c != '0') { + p--; + break; } - else - uscore = 0; } - if (!(c = *str) || ISSPACE(c)) --str; + if (*(p - 1) == '0') + p--; } - c = *str; - c = conv_digit(c); - if (c < 0 || c >= base) { + if (p == pend || *p == '_') { if (badcheck) goto bad; return mrb_fixnum_value(0); } - - uscore = 0; - for (p=str;*p;p++) { + for ( ;p<pend;p++) { if (*p == '_') { - if (uscore == 0) { - uscore++; + p++; + if (p==pend) { + if (badcheck) goto bad; continue; } - if (badcheck) goto bad; - break; + if (*p == '_') { + if (badcheck) goto bad; + break; + } + } + if (badcheck && *p == '\0') { + goto nullbyte; } - uscore = 0; c = conv_digit(*p); if (c < 0 || c >= base) { - if (badcheck) goto bad; break; } - n *= base; + 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; } - if (n > MRB_INT_MAX) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new_cstr(mrb, str)); - } - val = n; + val = (mrb_int)n; if (badcheck) { - if (p == str) goto bad; /* no number */ - while (*p && ISSPACE(*p)) p++; - if (*p) goto bad; /* trailing garbage */ + 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 */ } - return mrb_fixnum_value(sign ? val : -val); -bad: - mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)", mrb_str_new_cstr(mrb, str)); + 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(%!l)", str, pend-str); /* not reached */ return mrb_fixnum_value(0); } +/* obslete: use RSTRING_CSTR() or mrb_string_cstr() */ MRB_API const char* mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr) { - struct RString *ps = mrb_str_ptr(*ptr); - 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') { - 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 @@ -2014,21 +2400,10 @@ mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) const char *s; mrb_int len; - str = mrb_str_to_str(mrb, str); - if (badcheck) { - s = mrb_string_value_cstr(mrb, &str); - } - else { - s = RSTRING_PTR(str); - } - if (s) { - len = RSTRING_LEN(str); - if (s[len]) { /* no sentinel somehow */ - struct RString *temp_str = str_new(mrb, s, len); - s = RSTR_PTR(temp_str); - } - } - return mrb_cstr_to_inum(mrb, s, base, badcheck); + mrb_to_str(mrb, str); + s = RSTRING_PTR(str); + len = RSTRING_LEN(str); + return mrb_str_len_to_inum(mrb, s, len, base, badcheck); } /* 15.2.10.5.38 */ @@ -2058,71 +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); } -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 *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; } - d = strtod(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)); - /* not reached */ + 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; } - return d; + if (!badcheck && *p == ' ') { + pend = p; + p = p2; + goto nocopy; + } + if (*p == '_') break; + p++; } - if (*end) { - char buf[DBL_DIG * 4 + 10]; - 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; - } + 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; } - 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; + 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; } - - d = strtod(p, &end); + prev = c; + } + *n = '\0'; + p = buf; + pend = n; +nocopy: + d = mrb_float_read(p, &end); + if (p == end) { if (badcheck) { - if (!end || p == end) goto bad; - while (*end && ISSPACE(*end)) end++; - if (*end) goto bad; +bad: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%!s)", s); + /* not reached */ } + return d; + } + if (badcheck) { + if (!end || p == end) goto bad; + while (end<pend && ISSPACE(*end)) end++; + if (end<pend) goto bad; } return d; } @@ -2130,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 */ @@ -2154,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. * @@ -2167,12 +2554,12 @@ mrb_str_to_f(mrb_state *mrb, mrb_value self) { return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE)); } +#endif /* 15.2.10.5.40 */ /* * call-seq: * str.to_s => str - * str.to_str => str * * Returns the receiver. */ @@ -2200,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) { @@ -2222,7 +2609,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str) * * Returns a copy of <i>str</i> with all lowercase letters replaced with their * uppercase counterparts. The operation is locale insensitive---only - * characters ``a'' to ``z'' are affected. + * characters 'a' to 'z' are affected. * * "hEllO".upcase #=> "HELLO" */ @@ -2236,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 @@ -2248,143 +2633,76 @@ 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; + return str_escape(mrb, str, FALSE); +} - case '#': - len += IS_EVSTR(p, pend) ? 2 : 1; - break; +MRB_API mrb_value +mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) +{ + struct RString *s = mrb_str_ptr(str); + size_t capa; + size_t total; + ptrdiff_t off = -1; - default: - if (ISPRINT(c)) { - len++; - } - else { - len += 4; /* \NNN */ - } - break; - } + if (len == 0) return str; + mrb_str_modify(mrb, s); + if (ptr >= RSTR_PTR(s) && ptr <= RSTR_PTR(s) + (size_t)RSTR_LEN(s)) { + off = ptr - RSTR_PTR(s); } - 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[2] = '0' + c % 8; c /= 8; - q[1] = '0' + c % 8; c /= 8; - q[0] = '0' + c % 8; - q += 3; - } + capa = RSTR_CAPA(s); + total = RSTR_LEN(s)+len; + 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_SSIZE_MAX / 2) { + capa *= 2; + } + else { + capa = total+1; + } } + if (capa <= total || capa > MRB_SSIZE_MAX) { + goto size_error; + } + resize_capa(mrb, s, capa); } - *q = '"'; - return mrb_obj_value(result); -} - -MRB_API mrb_value -mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) -{ - str_buf_cat(mrb, mrb_str_ptr(str), ptr, len); + if (off != -1) { + ptr = RSTR_PTR(s) + off; + } + memcpy(RSTR_PTR(s) + RSTR_LEN(s), ptr, len); + 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; } 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 mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2) { + if (mrb_str_ptr(str) == mrb_str_ptr(str2)) { + mrb_str_modify(mrb, mrb_str_ptr(str)); + } return mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(str2)); } MRB_API mrb_value -mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2) +mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) { - str2 = mrb_str_to_str(mrb, str2); - return mrb_str_cat_str(mrb, str, 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 @@ -2399,54 +2717,7 @@ mrb_str_append(mrb_state *mrb, mrb_value str, 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; - - c = *p; - if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p, 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[3] = '0' + c % 8; c /= 8; - buf[2] = '0' + c % 8; c /= 8; - buf[1] = '0' + c % 8; - mrb_str_cat(mrb, result, buf, 4); - continue; - } - } - mrb_str_cat_lit(mrb, result, "\""); - - return result; + return str_escape(mrb, str, TRUE); } /* @@ -2472,30 +2743,137 @@ 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"); - s = mrb->string_class = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */ + mrb->string_class = s = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */ MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); - mrb_define_method(mrb, s, "bytesize", mrb_str_size, MRB_ARGS_NONE()); + mrb_define_method(mrb, s, "bytesize", mrb_str_bytesize, MRB_ARGS_NONE()); mrb_define_method(mrb, s, "<=>", mrb_str_cmp_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.1 */ mrb_define_method(mrb, s, "==", mrb_str_equal_m, MRB_ARGS_REQ(1)); /* 15.2.10.5.2 */ 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 */ mrb_define_method(mrb, s, "chomp!", mrb_str_chomp_bang, MRB_ARGS_ANY()); /* 15.2.10.5.10 */ - mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_REQ(1)); /* 15.2.10.5.11 */ - mrb_define_method(mrb, s, "chop!", mrb_str_chop_bang, MRB_ARGS_REQ(1)); /* 15.2.10.5.12 */ + mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_NONE()); /* 15.2.10.5.11 */ + mrb_define_method(mrb, s, "chop!", mrb_str_chop_bang, MRB_ARGS_NONE()); /* 15.2.10.5.12 */ mrb_define_method(mrb, s, "downcase", mrb_str_downcase, MRB_ARGS_NONE()); /* 15.2.10.5.13 */ mrb_define_method(mrb, s, "downcase!", mrb_str_downcase_bang, MRB_ARGS_NONE()); /* 15.2.10.5.14 */ mrb_define_method(mrb, s, "empty?", mrb_str_empty_p, MRB_ARGS_NONE()); /* 15.2.10.5.16 */ @@ -2503,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 */ @@ -2511,12 +2889,14 @@ mrb_init_string(mrb_state *mrb) mrb_define_method(mrb, s, "replace", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */ mrb_define_method(mrb, s, "reverse", mrb_str_reverse, MRB_ARGS_NONE()); /* 15.2.10.5.29 */ mrb_define_method(mrb, s, "reverse!", mrb_str_reverse_bang, MRB_ARGS_NONE()); /* 15.2.10.5.30 */ - mrb_define_method(mrb, s, "rindex", mrb_str_rindex_m, MRB_ARGS_ANY()); /* 15.2.10.5.31 */ + mrb_define_method(mrb, s, "rindex", mrb_str_rindex, MRB_ARGS_ANY()); /* 15.2.10.5.31 */ mrb_define_method(mrb, s, "size", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.33 */ 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_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 */ mrb_define_method(mrb, s, "to_s", mrb_str_to_s, MRB_ARGS_NONE()); /* 15.2.10.5.40 */ mrb_define_method(mrb, s, "to_str", mrb_str_to_s, MRB_ARGS_NONE()); @@ -2525,4 +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()); + + 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)); } diff --git a/src/symbol.c b/src/symbol.c index 390d69c31..007b8c885 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -4,72 +4,202 @@ ** See Copyright Notice in mruby.h */ -#include <ctype.h> #include <limits.h> #include <string.h> -#include "mruby.h" -#include "mruby/khash.h" -#include "mruby/string.h" -#include "mruby/dump.h" +#include <mruby.h> +#include <mruby/khash.h> +#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 * 1.2); - 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 */ @@ -355,7 +529,9 @@ symname_p(const char *name) if (*++m == '*') ++m; break; case '!': - if (*++m == '=') ++m; + switch (*++m) { + case '=': case '~': ++m; + } break; case '+': case '-': if (*++m == '@') ++m; @@ -385,8 +561,8 @@ id: switch (*m) { case '!': case '?': case '=': ++m; default: break; - } } + } break; } return *m ? FALSE : TRUE; @@ -401,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); @@ -459,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) { @@ -479,12 +676,13 @@ mrb_init_symbol(mrb_state *mrb) { struct RClass *sym; - sym = mrb->symbol_class = 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 cabd2426d..6089b8aa0 100644 --- a/src/value_array.h +++ b/src/value_array.h @@ -1,11 +1,12 @@ #ifndef MRB_VALUE_ARRAY_H__ #define MRB_VALUE_ARRAY_H__ -#include "mruby.h" +#include <mruby.h> 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 c4f6fb830..d89295229 100644 --- a/src/variable.c +++ b/src/variable.c @@ -4,392 +4,234 @@ ** See Copyright Notice in mruby.h */ -#include <ctype.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/proc.h" -#include "mruby/string.h" - -typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*); - -#ifdef MRB_USE_IV_SEGLIST - -#ifndef MRB_SEGMENT_SIZE -#define MRB_SEGMENT_SIZE 4 -#endif - -typedef struct segment { - mrb_sym key[MRB_SEGMENT_SIZE]; - mrb_value val[MRB_SEGMENT_SIZE]; - struct segment *next; -} segment; +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/proc.h> +#include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/presym.h> + +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. - * - * Parameters - * mrb - * Returns - * the instance variable table. - */ +/* Creates the instance variable table. */ static iv_tbl* iv_new(mrb_state *mrb) { iv_tbl *t; - t = mrb_malloc(mrb, sizeof(iv_tbl)); + 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; } -/* - * Set the value for the symbol in the instance variable table. - * - * Parameters - * mrb - * t the instance variable table to be set in. - * sym the symbol to be used as the key. - * val the value to be set. - */ +static void iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val); + static void -iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) +iv_rehash(mrb_state *mrb, iv_tbl *t) { - segment *seg = t->rootseg; - segment *prev = NULL; - segment *matched_seg = NULL; - size_t matched_idx = 0; - size_t i; + size_t old_alloc = t->alloc; + size_t new_alloc = old_alloc+1; + struct iv_elem *old_table = t->table; - while (seg) { - for (i=0; i<MRB_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; - t->size++; - return; - } - if (!matched_seg && key == 0) { - matched_seg = seg; - matched_idx = i; - } - else if (key == sym) { - seg->val[i] = val; - return; - } + 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); } - prev = seg; - seg = seg->next; } + mrb_free(mrb, old_table); +} - /* Not found */ - t->size++; - if (matched_seg) { - matched_seg->key[matched_idx] = sym; - matched_seg->val[matched_idx] = val; - return; - } +#define slot_empty_p(slot) ((slot)->key == 0 && !mrb_undef_p((slot)->val)) - seg = 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; +/* 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) +{ + size_t hash, pos, start; + struct iv_elem *dslot = NULL; + + if (t == NULL) return; + if (t->alloc == 0) { + iv_rehash(mrb, t); } - else { - t->rootseg = seg; + 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; + } + /* no room */ + iv_rehash(mrb, t); + start = pos = hash & (t->alloc-1); + } } } -/* - * Get a value for a symbol from the instance variable table. - * - * Parameters - * mrb - * t the variable table to be searched. - * sym the symbol to be used as the key. - * vp the value pointer. Receives the value if the specified symbol is - * contained in the instance variable table. - * Returns - * true if the specified symbol is contained in the instance variable table. - */ +/* Get a value for a symbol from the instance variable table. */ 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; - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_SEGMENT_SIZE; i++) { - mrb_sym key = seg->key[i]; + if (t == NULL) return FALSE; + 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. - * - * Parameters - * t the variable table to be searched. - * sym the symbol to be used as the key. - * vp the value pointer. Receive the deleted value if the symbol is - * contained in the instance variable table. - * Returns - * true if the specified symbol is contained in the instance variable table. - */ +/* 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; - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_SEGMENT_SIZE; i++) { - mrb_sym key = seg->key[i]; + if (t == NULL) return FALSE; + 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; } -static mrb_bool -iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p) +/* Iterates over the instance variable table. */ +static void +iv_foreach(mrb_state *mrb, iv_tbl *t, mrb_iv_foreach_func *func, void *p) { - segment *seg; size_t i; - int n; - seg = t->rootseg; - while (seg) { - for (i=0; i<MRB_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) 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_SEGMENT_SIZE; - } - /* empty iv_tbl */ - return 0; + if (t == NULL) 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_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; } +/* Free memory of the instance variable table. */ 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); } -#else - -#include "mruby/khash.h" - -#ifndef MRB_IVHASH_INIT_SIZE -#define MRB_IVHASH_INIT_SIZE 8 -#endif - -KHASH_DECLARE(iv, mrb_sym, mrb_value, TRUE) -KHASH_DEFINE(iv, mrb_sym, mrb_value, TRUE, kh_int_hash_func, kh_int_hash_equal) - -typedef struct iv_tbl { - khash_t(iv) h; -} iv_tbl; - -static iv_tbl* -iv_new(mrb_state *mrb) -{ - return (iv_tbl*)kh_init_size(iv, mrb, MRB_IVHASH_INIT_SIZE); -} - -static void -iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val) -{ - khash_t(iv) *h = &t->h; - khiter_t k; - - k = kh_put(iv, mrb, h, sym); - kh_value(h, k) = val; -} - -static mrb_bool -iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) -{ - khash_t(iv) *h = &t->h; - khiter_t k; - - k = kh_get(iv, mrb, h, sym); - if (k != kh_end(h)) { - if (vp) *vp = kh_value(h, k); - return TRUE; - } - return FALSE; -} - -static mrb_bool -iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) -{ - khash_t(iv) *h = &t->h; - khiter_t k; - - if (h) { - k = kh_get(iv, mrb, h, sym); - if (k != kh_end(h)) { - mrb_value val = kh_value(h, k); - kh_del(iv, mrb, h, k); - if (vp) *vp = val; - return TRUE; - } - } - return FALSE; -} - -static mrb_bool -iv_foreach(mrb_state *mrb, iv_tbl *t, iv_foreach_func *func, void *p) -{ - khash_t(iv) *h = &t->h; - khiter_t k; - int n; - - if (h) { - for (k = kh_begin(h); k != kh_end(h); k++) { - if (kh_exist(h, k)) { - n = (*func)(mrb, kh_key(h, k), kh_value(h, k), p); - if (n > 0) return FALSE; - if (n < 0) { - kh_del(iv, mrb, h, k); - } - } - } - } - return TRUE; -} - -static size_t -iv_size(mrb_state *mrb, iv_tbl *t) -{ - khash_t(iv) *h; - - if (t && (h = &t->h)) { - return kh_size(h); - } - return 0; -} - -static iv_tbl* -iv_copy(mrb_state *mrb, iv_tbl *t) -{ - return (iv_tbl*)kh_copy(iv, mrb, &t->h); -} - -static void -iv_free(mrb_state *mrb, iv_tbl *t) -{ - kh_destroy(iv, mrb, &t->h); -} - -#endif - static int iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { @@ -400,9 +242,7 @@ iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) static void mark_tbl(mrb_state *mrb, iv_tbl *t) { - if (t) { - iv_foreach(mrb, t, iv_mark_i, 0); - } + iv_foreach(mrb, t, iv_mark_i, 0); } void @@ -485,31 +325,64 @@ mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym) return mrb_nil_value(); } +static inline void assign_class_name(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) +{ + assign_class_name(mrb, obj, sym, v); + if (!obj->iv) { + obj->iv = iv_new(mrb); + } + 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) { - iv_tbl *t = obj->iv; - - if (!t) { - t = obj->iv = iv_new(mrb); - } - mrb_write_barrier(mrb, (struct RBasic*)obj); - iv_put(mrb, t, sym, 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_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) +mrb_iv_foreach(mrb_state *mrb, mrb_value obj, mrb_iv_foreach_func *func, void *p) { - iv_tbl *t = obj->iv; + if (!obj_iv_p(obj)) return; + iv_foreach(mrb, mrb_obj_ptr(obj)->iv, func, p); +} - if (!t) { - t = obj->iv = iv_new(mrb); - } - else if (iv_get(mrb, t, sym, &v)) { - return; +static inline mrb_bool +namespace_p(enum mrb_vtype tt) +{ + return tt == MRB_TT_CLASS || tt == MRB_TT_MODULE ? TRUE : FALSE; +} + +static inline void +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_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_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_force(mrb, c, id_classname, mrb_symbol_value(sym)); + } + else { + mrb_obj_iv_set_force(mrb, c, id_outer, mrb_obj_value(obj)); + } + } + } + } } - mrb_write_barrier(mrb, (struct RBasic*)obj); - iv_put(mrb, t, sym, v); } MRB_API void @@ -542,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_p(mrb_state *mrb, mrb_sym iv_name) +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_check(mrb_state *mrb, mrb_sym iv_name) +mrb_iv_name_sym_check(mrb_state *mrb, mrb_sym iv_name) { - if (!mrb_iv_p(mrb, iv_name)) { - mrb_name_error(mrb, iv_name, "`%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name)); + if (!mrb_iv_name_sym_p(mrb, iv_name)) { + mrb_name_error(mrb, iv_name, "'%n' is not allowed as an instance variable name", iv_name); } } @@ -601,16 +469,16 @@ 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 { ins = mrb_inspect(mrb, v); } - mrb_str_append(mrb, str, ins); + mrb_str_cat_str(mrb, str, ins); return 0; } @@ -622,12 +490,12 @@ mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj) if (len > 0) { const char *cn = mrb_obj_classname(mrb, mrb_obj_value(obj)); - mrb_value str = mrb_str_buf_new(mrb, 30); + mrb_value str = mrb_str_new_capa(mrb, 30); 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, ">"); @@ -643,27 +511,14 @@ mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym) iv_tbl *t = mrb_obj_ptr(obj)->iv; mrb_value val; - if (t && iv_del(mrb, t, sym, &val)) { + mrb_check_frozen(mrb, mrb_obj_ptr(obj)); + if (iv_del(mrb, t, sym, &val)) { return val; } } return mrb_undef_value(); } -mrb_value -mrb_vm_iv_get(mrb_state *mrb, mrb_sym sym) -{ - /* get self */ - return mrb_iv_get(mrb, mrb->c->stack[0], sym); -} - -void -mrb_vm_iv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) -{ - /* get self */ - mrb_iv_set(mrb, mrb->c->stack[0], sym, v); -} - static int iv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) { @@ -672,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)); } @@ -702,7 +557,7 @@ mrb_obj_instance_variables(mrb_state *mrb, mrb_value self) mrb_value ary; ary = mrb_ary_new(mrb); - if (obj_iv_p(self) && mrb_obj_ptr(self)->iv) { + if (obj_iv_p(self)) { iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary); } return ary; @@ -716,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)); } @@ -726,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>. * @@ -744,35 +599,50 @@ 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) { - if (c->iv) { - iv_foreach(mrb, c->iv, cv_i, &ary); - } + iv_foreach(mrb, c->iv, cv_i, &ary); + if (!inherit) break; c = c->super; } return ary; } -MRB_API mrb_value -mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym) +mrb_value +mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym) { struct RClass * cls = c; + mrb_value v; + int given = FALSE; while (c) { - if (c->iv) { - iv_tbl *t = c->iv; - mrb_value v; - - if (iv_get(mrb, t, sym, &v)) - return v; + if (c->iv && iv_get(mrb, c->iv, sym, &v)) { + given = TRUE; } c = c->super; } - mrb_name_error(mrb, sym, "uninitialized class variable %S in %S", - mrb_sym2str(mrb, sym), mrb_obj_value(cls)); + if (given) return v; + if (cls && cls->tt == MRB_TT_SCLASS) { + mrb_value klass; + + 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; + while (c) { + if (c->iv && iv_get(mrb, c->iv, sym, &v)) { + given = TRUE; + } + c = c->super; + } + if (given) return v; + } + } + mrb_name_error(mrb, sym, "uninitialized class variable %n in %C", sym, cls); /* not reached */ return mrb_nil_value(); } @@ -789,24 +659,43 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) struct RClass * cls = c; while (c) { - if (c->iv) { - iv_tbl *t = c->iv; + iv_tbl *t = c->iv; - if (iv_get(mrb, t, sym, NULL)) { - mrb_write_barrier(mrb, (struct RBasic*)c); - iv_put(mrb, t, sym, v); - return; - } + if (iv_get(mrb, t, sym, NULL)) { + mrb_check_frozen(mrb, c); + iv_put(mrb, t, sym, v); + mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v); + return; } c = c->super; } - if (!cls->iv) { - cls->iv = iv_new(mrb); + if (cls && cls->tt == MRB_TT_SCLASS) { + mrb_value klass; + + klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__)); + switch (mrb_type(klass)) { + case MRB_TT_CLASS: + case MRB_TT_MODULE: + case MRB_TT_SCLASS: + c = mrb_class_ptr(klass); + break; + default: + c = cls; + break; + } + } + else{ + c = cls; + } + + mrb_check_frozen(mrb, c); + if (!c->iv) { + c->iv = iv_new(mrb); } - mrb_write_barrier(mrb, (struct RBasic*)cls); - iv_put(mrb, cls->iv, sym, v); + iv_put(mrb, c->iv, sym, v); + mrb_field_write_barrier_value(mrb, (struct RBasic*)c, v); } MRB_API void @@ -815,14 +704,12 @@ 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) { - if (c->iv) { - iv_tbl *t = c->iv; - if (iv_get(mrb, t, sym, NULL)) return TRUE; - } + iv_tbl *t = c->iv; + if (iv_get(mrb, t, sym, NULL)) return TRUE; c = c->super; } @@ -838,19 +725,29 @@ mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym) mrb_value mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym) { - struct RClass *c = mrb->c->ci->proc->target_class; + struct RClass *c; - if (!c) c = mrb->c->ci->target_class; + 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); } void mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { - struct RClass *c = mrb->c->ci->proc->target_class; + struct RClass *c; + const struct RProc *p = mrb->c->ci->proc; - if (!c) c = mrb->c->ci->target_class; + 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); } @@ -869,77 +766,90 @@ 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; - iv_tbl *t; mrb_bool retry = FALSE; mrb_value name; + if (skip) c = c->super; L_RETRY: while (c) { - if (c->iv) { - t = c->iv; - if (iv_get(mrb, t, sym, &v)) + if (!MRB_FLAG_TEST(c, MRB_FL_CLASS_IS_PREPENDED) && c->iv) { + if (iv_get(mrb, c->iv, sym, &v)) return v; } c = c->super; } - if (!retry && base && base->tt == MRB_TT_MODULE) { + if (!retry && base->tt == MRB_TT_MODULE) { c = mrb->object_class; retry = TRUE; 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 mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) { - struct RClass *c = mrb->c->ci->proc->target_class; + struct RClass *c; + struct RClass *c2; + mrb_value v; + const struct RProc *proc = mrb->c->ci->proc; - if (!c) c = mrb->c->ci->target_class; - if (c) { - struct RClass *c2; - mrb_value v; + c = MRB_PROC_TARGET_CLASS(proc); + if (!c) c = mrb->object_class; + if (iv_get(mrb, c->iv, sym, &v)) { + return v; + } + c2 = c; + while (c2 && c2->tt == MRB_TT_SCLASS) { + mrb_value klass; - if (c->iv && iv_get(mrb, c->iv, sym, &v)) { - return v; + if (!iv_get(mrb, c2->iv, MRB_SYM(__attached__), &klass)) { + c2 = NULL; + break; } - c2 = c; - for (;;) { - c2 = mrb_class_outer_module(mrb, c2); - if (!c2) break; - if (c2->iv && iv_get(mrb, c2->iv, sym, &v)) { - return v; - } + c2 = mrb_class_ptr(klass); + } + if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2; + proc = proc->upper; + while (proc) { + c2 = MRB_PROC_TARGET_CLASS(proc); + if (c2 && iv_get(mrb, c2->iv, sym, &v)) { + return v; } + proc = proc->upper; } - return const_get(mrb, c, sym); + return const_get(mrb, c, sym, TRUE); } MRB_API void mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v) { mod_const_check(mrb, mod); + if (mrb_type(v) == MRB_TT_CLASS || mrb_type(v) == MRB_TT_MODULE) { + mrb_class_name_class(mrb, mrb_class_ptr(mod), mrb_class_ptr(v), sym); + } mrb_iv_set(mrb, mod, sym, v); } void mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { - struct RClass *c = mrb->c->ci->proc->target_class; + struct RClass *c; - if (!c) c = mrb->c->ci->target_class; + 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); } @@ -951,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); @@ -970,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; } @@ -982,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) @@ -994,9 +918,7 @@ mrb_mod_constants(mrb_state *mrb, mrb_value mod) mrb_get_args(mrb, "|b", &inherit); ary = mrb_ary_new(mrb); while (c) { - if (c->iv) { - iv_foreach(mrb, c->iv, const_i, &ary); - } + iv_foreach(mrb, c->iv, const_i, &ary); if (!inherit) break; c = c->super; if (c == mrb->object_class) break; @@ -1009,9 +931,6 @@ mrb_gv_get(mrb_state *mrb, mrb_sym sym) { mrb_value v; - if (!mrb->globals) { - return mrb_nil_value(); - } if (iv_get(mrb, mrb->globals, sym, &v)) return v; return mrb_nil_value(); @@ -1023,20 +942,15 @@ mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) iv_tbl *t; if (!mrb->globals) { - t = mrb->globals = iv_new(mrb); - } - else { - t = mrb->globals; + mrb->globals = iv_new(mrb); } + t = mrb->globals; iv_put(mrb, t, sym, v); } MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym) { - if (!mrb->globals) { - return; - } iv_del(mrb, mrb->globals, sym, NULL); } @@ -1065,18 +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]; - if (t) { - 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))); - } + iv_foreach(mrb, t, gv_i, &ary); return ary; } @@ -1085,19 +989,19 @@ mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude, { struct RClass *klass = mrb_class_ptr(mod); struct RClass *tmp; - mrb_bool mod_retry = 0; + mrb_bool mod_retry = FALSE; tmp = klass; retry: while (tmp) { - if (tmp->iv && iv_get(mrb, tmp->iv, id, NULL)) { + if (iv_get(mrb, tmp->iv, id, NULL)) { return TRUE; } if (!recurse && (klass != mrb->object_class)) break; tmp = tmp->super; } if (!exclude && !mod_retry && (klass->tt == MRB_TT_MODULE)) { - mod_retry = 1; + mod_retry = TRUE; tmp = mrb->object_class; goto retry; } @@ -1140,23 +1044,99 @@ csym_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) return 0; } -mrb_sym -mrb_class_sym(mrb_state *mrb, struct RClass *c, struct RClass *outer) +static mrb_sym +find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c) { - mrb_value name; + struct csym_arg arg; - name = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__classid__")); - if (mrb_nil_p(name)) { + if (!outer) return 0; + if (outer == c) return 0; + arg.c = c; + arg.sym = 0; + iv_foreach(mrb, outer->iv, csym_i, &arg); + return arg.sym; +} - if (!outer) return 0; - else { - struct csym_arg arg; +static struct RClass* +outer_class(mrb_state *mrb, struct RClass *c) +{ + mrb_value ov; - arg.c = c; - arg.sym = 0; - iv_foreach(mrb, outer->iv, csym_i, &arg); - return arg.sym; - } + 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: + case MRB_TT_MODULE: + return mrb_class_ptr(ov); + default: + break; + } + return NULL; +} + +static mrb_bool +detect_outer_loop(mrb_state *mrb, struct RClass *c) +{ + struct RClass *t = c; /* tortoise */ + struct RClass *h = c; /* hare */ + + for (;;) { + if (h == NULL) return FALSE; + h = outer_class(mrb, h); + if (h == NULL) return FALSE; + h = outer_class(mrb, h); + t = outer_class(mrb, t); + if (t == h) return TRUE; + } +} + +mrb_value +mrb_class_find_path(mrb_state *mrb, struct RClass *c) +{ + struct RClass *outer; + mrb_value path; + mrb_sym name; + const char *str; + mrb_int len; + + if (detect_outer_loop(mrb, c)) return mrb_nil_value(); + outer = outer_class(mrb, c); + if (outer == NULL) return mrb_nil_value(); + name = find_class_sym(mrb, outer, c); + if (name == 0) return mrb_nil_value(); + str = mrb_class_name(mrb, outer); + path = mrb_str_new_capa(mrb, 40); + mrb_str_cat_cstr(mrb, path, str); + mrb_str_cat_cstr(mrb, path, "::"); + + 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_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 mrb_symbol(name); + 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; } diff --git a/src/version.c b/src/version.c index 7aac44d62..350bc1673 100644 --- a/src/version.c +++ b/src/version.c @@ -1,12 +1,16 @@ -#include "mruby.h" -#include "mruby/variable.h" +#include <mruby.h> +#include <mruby/variable.h> void mrb_init_version(mrb_state* mrb) { + mrb_value mruby_version = mrb_str_new_lit(mrb, MRUBY_VERSION); + mrb_define_global_const(mrb, "RUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_RUBY_VERSION)); mrb_define_global_const(mrb, "RUBY_ENGINE", mrb_str_new_lit(mrb, MRUBY_RUBY_ENGINE)); - mrb_define_global_const(mrb, "MRUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_VERSION)); + mrb_define_global_const(mrb, "RUBY_ENGINE_VERSION", mruby_version); + mrb_define_global_const(mrb, "MRUBY_VERSION", mruby_version); + mrb_define_global_const(mrb, "MRUBY_RELEASE_NO", mrb_fixnum_value(MRUBY_RELEASE_NO)); mrb_define_global_const(mrb, "MRUBY_RELEASE_DATE", mrb_str_new_lit(mrb, MRUBY_RELEASE_DATE)); mrb_define_global_const(mrb, "MRUBY_DESCRIPTION", mrb_str_new_lit(mrb, MRUBY_DESCRIPTION)); mrb_define_global_const(mrb, "MRUBY_COPYRIGHT", mrb_str_new_lit(mrb, MRUBY_COPYRIGHT)); @@ -6,23 +6,27 @@ #include <stddef.h> #include <stdarg.h> +#ifndef MRB_NO_FLOAT #include <math.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/hash.h" -#include "mruby/irep.h" -#include "mruby/numeric.h" -#include "mruby/proc.h" -#include "mruby/range.h" -#include "mruby/string.h" -#include "mruby/variable.h" -#include "mruby/error.h" -#include "mruby/opcode.h" +#endif +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/hash.h> +#include <mruby/irep.h> +#include <mruby/numeric.h> +#include <mruby/proc.h> +#include <mruby/range.h> +#include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/error.h> +#include <mruby/opcode.h> #include "value_array.h" -#include "mrb_throw.h" +#include <mruby/throw.h> +#include <mruby/dump.h> +#include <mruby/presym.h> -#ifndef ENABLE_STDIO +#ifdef MRB_NO_STDIO #if defined(__cplusplus) extern "C" { #endif @@ -40,6 +44,11 @@ void abort(void); #define MRB_STACK_GROWTH 128 #endif +/* Maximum mrb_funcall() depth. Should be set lower on memory constrained systems. */ +#ifndef MRB_FUNCALL_DEPTH_MAX +#define MRB_FUNCALL_DEPTH_MAX 512 +#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 @@ -52,31 +61,50 @@ The value below allows about 60000 recursive calls in the simplest case. */ # define DEBUG(x) #endif -#define ARENA_RESTORE(mrb,ai) (mrb)->arena_idx = (ai) -static inline void -stack_clear(mrb_value *from, size_t count) +#ifndef MRB_GC_FIXED_ARENA +static void +mrb_gc_arena_shrink(mrb_state *mrb, int idx) { -#ifndef MRB_NAN_BOXING - const mrb_value mrb_value_zero = { { 0 } }; + mrb_gc *gc = &mrb->gc; + int capa = gc->arena_capa; - while (count-- > 0) { - *from++ = mrb_value_zero; + if (idx < capa / 4) { + capa >>= 2; + if (capa < MRB_GC_ARENA_SIZE) { + capa = MRB_GC_ARENA_SIZE; + } + if (capa != gc->arena_capa) { + gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa); + gc->arena_capa = capa; + } } +} #else +#define mrb_gc_arena_shrink(mrb,idx) +#endif + +#define CALL_MAXARGS 127 + +void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args); + +static inline void +stack_clear(mrb_value *from, size_t count) +{ +#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 @@ -87,54 +115,62 @@ 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 -envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase) +envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t oldsize) { mrb_callinfo *ci = mrb->c->cibase; if (newbase == oldbase) return; while (ci <= mrb->c->ci) { - struct REnv *e = ci->env; - if (e && MRB_ENV_STACK_SHARED_P(e)) { + struct REnv *e = mrb_vm_ci_env(ci); + mrb_value *st; + + 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; } - ci->stackent = newbase + (ci->stackent - oldbase); - ci++; - } -} -static inline void -init_new_stack_space(mrb_state *mrb, int room, int keep) -{ - if (room > keep) { - /* do not leave uninitialized malloc region */ - stack_clear(&(mrb->c->stack[keep]), room - keep); + 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_ONSTACK_P(e) && + (st = e->stack) && oldbase <= st && st < oldbase+oldsize) { + ptrdiff_t off = e->stack - oldbase; + + e->stack = newbase + off; + } + } + + ci->stack = newbase + (ci->stack - oldbase); + ci++; } } /** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end */ static void -stack_extend_alloc(mrb_state *mrb, int room, int keep) +stack_extend_alloc(mrb_state *mrb, mrb_int room) { mrb_value *oldbase = mrb->c->stbase; - int size = mrb->c->stend - mrb->c->stbase; - int off = mrb->c->stack - mrb->c->stbase; + mrb_value *newstack; + size_t oldsize = mrb->c->stend - mrb->c->stbase; + size_t size = oldsize; + 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) + if ((size_t)room <= size) size *= 2; else size += room; @@ -148,143 +184,174 @@ stack_extend_alloc(mrb_state *mrb, int room, int keep) size += room; #endif - mrb->c->stbase = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size); - mrb->c->stack = mrb->c->stbase + off; + 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->stend = mrb->c->stbase + size; - envadjust(mrb, oldbase, mrb->c->stbase); /* Raise an exception if the new stack size will be too large, to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */ if (size > MRB_STACK_MAX) { - init_new_stack_space(mrb, room, keep); - mrb_raise(mrb, E_SYSSTACK_ERROR, "stack level too deep. (limit=" MRB_STRINGIZE(MRB_STACK_MAX) ")"); + mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } } -static inline void -stack_extend(mrb_state *mrb, int room, int keep) +MRB_API void +mrb_stack_extend(mrb_state *mrb, mrb_int room) { - if (mrb->c->stack + room >= mrb->c->stend) { - stack_extend_alloc(mrb, room, keep); + if (!mrb->c->ci->stack || mrb->c->ci->stack + room >= mrb->c->stend) { + stack_extend_alloc(mrb, room); } - init_new_stack_space(mrb, room, keep); } static inline struct REnv* -uvenv(mrb_state *mrb, int up) +uvenv(mrb_state *mrb, mrb_int up) { - struct REnv *e = mrb->c->ci->proc->env; + const struct RProc *proc = mrb->c->ci->proc; + struct REnv *e; while (up--) { - if (!e) return NULL; - e = (struct REnv*)e->c; + proc = proc->upper; + if (!proc) return NULL; } - return e; -} - -static inline mrb_bool -is_strict(mrb_state *mrb, struct REnv *e) -{ - int cioff = e->cioff; + e = MRB_PROC_ENV(proc); + if (e) return e; /* proc has enclosed env */ + else { + mrb_callinfo *ci = mrb->c->ci; + mrb_callinfo *cb = mrb->c->cibase; - if (MRB_ENV_STACK_SHARED_P(e) && mrb->c->cibase[cioff].proc && - MRB_PROC_STRICT_P(mrb->c->cibase[cioff].proc)) { - return TRUE; + while (cb <= ci) { + if (ci->proc == proc) { + return mrb_vm_ci_env(ci); + } + ci--; + } } - return FALSE; + return NULL; } -static inline struct REnv* -top_env(mrb_state *mrb, struct RProc *proc) +static inline const struct RProc* +top_proc(mrb_state *mrb, const struct RProc *proc) { - struct REnv *e = proc->env; - - if (is_strict(mrb, e)) return e; - while (e->c) { - e = (struct REnv*)e->c; - if (is_strict(mrb, e)) return e; + while (proc->upper) { + if (MRB_PROC_SCOPE_P(proc) || MRB_PROC_STRICT_P(proc)) + return proc; + proc = proc->upper; } - return e; + return proc; } #define CI_ACC_SKIP -1 #define CI_ACC_DIRECT -2 +#define CI_ACC_RESUMED -3 -static mrb_callinfo* -cipush(mrb_state *mrb) +static inline mrb_callinfo* +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; mrb_callinfo *ci = c->ci; - int eidx = ci->eidx; - int ridx = ci->ridx; - if (ci + 1 == c->ciend) { - size_t size = ci - c->cibase; + ptrdiff_t size = ci - c->cibase; c->cibase = (mrb_callinfo *)mrb_realloc(mrb, c->cibase, sizeof(mrb_callinfo)*size*2); c->ci = c->cibase + size; c->ciend = c->cibase + size * 2; } ci = ++c->ci; - ci->eidx = eidx; - ci->ridx = ridx; - ci->env = 0; - ci->pc = 0; - ci->err = 0; - ci->proc = 0; + 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; } -static void -cipop(mrb_state *mrb) +void +mrb_env_unshare(mrb_state *mrb, struct REnv *e) { - struct mrb_context *c = mrb->c; - - if (c->ci->env) { - struct REnv *e = c->ci->env; - size_t len = (size_t)MRB_ENV_STACK_LEN(e); - mrb_value *p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len); + if (e == NULL) return; + else { + size_t len = (size_t)MRB_ENV_LEN(e); + mrb_value *p; - MRB_ENV_UNSHARE_STACK(e); + if (!MRB_ENV_ONSTACK_P(e)) return; + if (e->cxt != mrb->c) return; + 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_CLOSE(e); mrb_write_barrier(mrb, (struct RBasic *)e); } +} + +static inline mrb_callinfo* +cipop(mrb_state *mrb) +{ + struct mrb_context *c = mrb->c; + struct REnv *env = mrb_vm_ci_env(c->ci); c->ci--; + if (env) mrb_env_unshare(mrb, env); + return c->ci; } -static void -ecall(mrb_state *mrb, int i) +MRB_API mrb_value +mrb_protect_error(mrb_state *mrb, mrb_protect_error_func *body, void *userdata, mrb_bool *error) { - struct RProc *p; - mrb_callinfo *ci; - mrb_value *self = mrb->c->stack; - struct RObject *exc; - - p = mrb->c->ensure[i]; - if (!p) return; - if (mrb->c->ci->eidx > i) - mrb->c->ci->eidx = i; - 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->nregs = p->body.irep->nregs; - ci->target_class = p->target_class; - mrb->c->stack = mrb->c->stack + ci[-1].nregs; - exc = mrb->exc; mrb->exc = 0; - mrb_run(mrb, p, *self); - mrb->c->ensure[i] = NULL; - if (!mrb->exc) mrb->exc = exc; + 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); + const struct mrb_context *c = mrb->c; + int ci_index = c->ci - c->cibase; + + if (error) { *error = FALSE; } + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + result = body(mrb, userdata); + mrb->jmp = prev_jmp; + } + 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 @@ -310,13 +377,57 @@ mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...) } 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) +{ + const struct RProc *p; + mrb_int n = 0; + + if (!ci) return 3; + p = ci->proc; + if (!p) { + if (ci->argc < 0) return 3; + return ci->argc+2; + } + if (!MRB_PROC_CFUNC_P(p) && p->body.irep) { + n = p->body.irep->nregs; + } + if (ci->argc < 0) { + if (n < 3) n = 3; /* self + args + blk */ + } + if (ci->argc > n) { + n = ci->argc + 2; /* self + blk */ + } + return n; +} + +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; - mrb_callinfo *old_ci = mrb->c->ci; + ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; @@ -325,78 +436,86 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc mrb->jmp = 0; } MRB_CATCH(&c_jmp) { /* error */ - while (old_ci != mrb->c->ci) { - mrb->c->stack = mrb->c->ci->stackent; + while (nth_ci < (mrb->c->ci - mrb->c->cibase)) { cipop(mrb); } mrb->jmp = 0; val = mrb_obj_value(mrb->exc); } MRB_END_EXC(&c_jmp); + mrb->jmp = 0; } else { - struct RProc *p; + mrb_method_t m; struct RClass *c; - mrb_sym undef = 0; mrb_callinfo *ci; - int n; + mrb_int n = ci_nregs(mrb->c->ci); + ptrdiff_t voff = -1; - if (!mrb->c->stack) { + if (!mrb->c->stbase) { stack_init(mrb); } - n = mrb->c->ci->nregs; 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); - p = mrb_method_search_vm(mrb, &c, mid); - if (!p) { - undef = mid; - mid = mrb_intern_lit(mrb, "method_missing"); - p = mrb_method_search_vm(mrb, &c, mid); - n++; argc++; - } - ci = cipush(mrb); - ci->mid = mid; - ci->proc = p; - ci->stackent = mrb->c->stack; - ci->argc = argc; - ci->target_class = c; - mrb->c->stack = mrb->c->stack + n; - if (MRB_PROC_CFUNC_P(p)) { - ci->nregs = argc + 2; - stack_extend(mrb, ci->nregs, 0); + m = mrb_method_search_vm(mrb, &c, mid); + if (MRB_METHOD_UNDEF_P(m)) { + 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)) { + mrb_method_missing(mrb, mid, self, args); + } + mrb_ary_unshift(mrb, args, mrb_symbol_value(mid)); + mrb_stack_extend(mrb, n+2); + mrb->c->ci->stack[n+1] = args; + argc = -1; } - else { - ci->nregs = p->body.irep->nregs + n; - stack_extend(mrb, ci->nregs, argc+2); + if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } - mrb->c->stack[0] = self; - if (undef) { - mrb->c->stack[1] = mrb_symbol_value(undef); - if (argc > 1) { - stack_copy(mrb->c->stack+2, argv, argc-1); - } + 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; } - else if (argc > 0) { - stack_copy(mrb->c->stack+1, argv, argc); + if (argc >= CALL_MAXARGS) { + mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); + + mrb->c->ci->stack[1] = args; + ci->argc = -1; + argc = 1; } - mrb->c->stack[argc+1] = blk; + mrb_stack_extend(mrb, argc + 2); + if (MRB_METHOD_PROC_P(m)) { + struct RProc *p = MRB_METHOD_PROC(m); - if (MRB_PROC_CFUNC_P(p)) { - int ai = mrb_gc_arena_save(mrb); + 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->ci->stack[0] = self; + if (ci->argc > 0) { + stack_copy(mrb->c->ci->stack+1, argv, argc); + } + mrb->c->ci->stack[argc+1] = blk; + if (MRB_METHOD_CFUNC_P(m)) { ci->acc = CI_ACC_DIRECT; - val = p->body.func(mrb, self); - mrb->c->stack = mrb->c->ci->stackent; + val = MRB_METHOD_CFUNC(m)(mrb, self); cipop(mrb); - mrb_gc_arena_restore(mrb, ai); } else { ci->acc = CI_ACC_SKIP; - val = mrb_run(mrb, p, self); + val = mrb_run(mrb, MRB_METHOD_PROC(m), self); } } + mrb_gc_arena_restore(mrb, ai); mrb_gc_protect(mrb, val); return val; } @@ -407,6 +526,58 @@ 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()); } +static mrb_value +exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) +{ + mrb_callinfo *ci = mrb->c->ci; + int keep, nregs; + + ci->stack[0] = self; + mrb_vm_ci_proc_set(ci, p); + if (MRB_PROC_CFUNC_P(p)) { + return MRB_PROC_CFUNC(p)(mrb, self); + } + nregs = p->body.irep->nregs; + if (ci->argc < 0) keep = 3; + else keep = ci->argc + 2; + if (nregs < keep) { + mrb_stack_extend(mrb, keep); + } + else { + mrb_stack_extend(mrb, nregs); + stack_clear(ci->stack+keep, nregs-keep); + } + + 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 */ /* @@ -426,30 +597,33 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons * 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; - struct RProc *p; + mrb_method_t m; struct RClass *c; mrb_callinfo *ci; mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block); + ci = mrb->c->ci; + if (ci->acc < 0) { + funcall: + return mrb_funcall_with_block(mrb, self, name, argc, argv, block); + } c = mrb_class(mrb, self); - p = mrb_method_search_vm(mrb, &c, name); - - if (!p) { /* call method_mising */ - return mrb_funcall_with_block(mrb, self, name, argc, argv, block); + m = mrb_method_search_vm(mrb, &c, name); + if (MRB_METHOD_UNDEF_P(m)) { /* call method_mising */ + goto funcall; } - ci = mrb->c->ci; ci->mid = name; - ci->target_class = c; - ci->proc = p; - 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++) { @@ -458,22 +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_PROC_CFUNC_P(p)) { - return p->body.func(mrb, self); + if (MRB_METHOD_CFUNC_P(m)) { + if (MRB_METHOD_PROC_P(m)) { + mrb_vm_ci_proc_set(ci, MRB_METHOD_PROC(m)); + } + return MRB_METHOD_CFUNC(m)(mrb, self); } - - ci->nregs = p->body.irep->nregs; - ci = cipush(mrb); - ci->nregs = 0; - ci->target_class = 0; - ci->pc = p->body.irep->iseq; - ci->stackent = mrb->c->stack; - ci->acc = 0; - - return self; + return exec_irep(mrb, self, MRB_METHOD_PROC(m)); } static mrb_value @@ -481,27 +649,34 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) { struct RProc *p; mrb_callinfo *ci; + int nregs; if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } ci = mrb->c->ci; if (ci->acc == CI_ACC_DIRECT) { - return mrb_yield_with_class(mrb, blk, 0, 0, self, c); + 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)) { - return p->body.func(mrb, self); + mrb_stack_extend(mrb, 3); + 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); } - ci->nregs = p->body.irep->nregs; - ci = cipush(mrb); - ci->nregs = 0; - ci->target_class = 0; - ci->pc = p->body.irep->iseq; - ci->stackent = mrb->c->stack; - ci->acc = 0; + nregs = p->body.irep->nregs; + 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; } @@ -552,24 +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: - case MRB_TT_FLOAT: - 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 @@ -578,39 +740,41 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value struct RProc *p; mrb_sym mid = mrb->c->ci->mid; mrb_callinfo *ci; - int n = mrb->c->ci->nregs; mrb_value val; + mrb_int n; if (mrb_nil_p(b)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); } + ci = mrb->c->ci; + n = ci_nregs(ci); + if (ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { + 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 = argc; - ci->target_class = c; - ci->acc = CI_ACC_SKIP; - mrb->c->stack = mrb->c->stack + n; - if (MRB_PROC_CFUNC_P(p)) { - ci->nregs = argc + 2; - stack_extend(mrb, ci->nregs, 0); + ci = cipush(mrb, n, CI_ACC_SKIP, c, p, mid, 0 /* dummy */); + if (argc >= CALL_MAXARGS) { + ci->argc = -1; + n = 3; } else { - ci->nregs = p->body.irep->nregs; - stack_extend(mrb, ci->nregs, argc+2); + ci->argc = (int)argc; + n = argc + 2; } - - mrb->c->stack[0] = self; - if (argc > 0) { - stack_copy(mrb->c->stack+1, argv, argc); + mrb_stack_extend(mrb, n); + 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; } - mrb->c->stack[argc+1] = mrb_nil_value(); + else if (argc > 0) { + stack_copy(mrb->c->ci->stack+1, argv, argc); + } + mrb->c->ci->stack[argc+1] = mrb_nil_value(); if (MRB_PROC_CFUNC_P(p)) { - val = p->body.func(mrb, self); - mrb->c->stack = mrb->c->ci->stackent; + ci->acc = CI_ACC_DIRECT; + val = MRB_PROC_CFUNC(p)(mrb, self); cipop(mrb); } else { @@ -624,7 +788,7 @@ mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv) { struct RProc *p = mrb_proc_ptr(b); - return mrb_yield_with_class(mrb, b, argc, argv, p->env->stack[0], p->target_class); + return mrb_yield_with_class(mrb, b, argc, argv, MRB_PROC_ENV(p)->stack[0], MRB_PROC_TARGET_CLASS(p)); } MRB_API mrb_value @@ -632,7 +796,81 @@ mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg) { struct RProc *p = mrb_proc_ptr(b); - return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class); + return mrb_yield_with_class(mrb, b, 1, &arg, MRB_PROC_ENV(p)->stack[0], MRB_PROC_TARGET_CLASS(p)); +} + +mrb_value +mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv) +{ + struct RProc *p; + mrb_callinfo *ci; + + if (mrb_nil_p(b)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); + } + if (!mrb_proc_p(b)) { + mrb_raise(mrb, E_TYPE_ERROR, "not a block"); + } + + p = mrb_proc_ptr(b); + ci = mrb->c->ci; + + mrb_stack_extend(mrb, 3); + 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 exec_irep(mrb, self, p); +} + +static struct RBreak* +break_new(mrb_state *mrb, uint32_t tag, const struct RProc *p, mrb_value val) +{ + struct RBreak *brk; + + 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); + + return brk; +} + +#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) +{ + 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; + } + } + +#undef catch_cover_p + + return NULL; } typedef enum { @@ -650,11 +888,11 @@ localjump_error(mrb_state *mrb, localjump_error_kind kind) mrb_value msg; mrb_value exc; - msg = mrb_str_buf_new(mrb, sizeof(lead) + 7); + msg = mrb_str_new_capa(mrb, sizeof(lead) + 7); mrb_str_cat(mrb, msg, lead, sizeof(lead) - 1); mrb_str_cat(mrb, msg, kind_str[kind], kind_str_len[kind]); exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg); - mrb->exc = mrb_obj_ptr(exc); + mrb_exc_set(mrb, exc); } static void @@ -662,92 +900,213 @@ argnum_error(mrb_state *mrb, mrb_int num) { mrb_value exc; mrb_value str; + mrb_int argc = mrb->c->ci->argc; + if (argc < 0) { + 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(mrb->c->ci->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(mrb->c->ci->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 = mrb_obj_ptr(exc); + mrb_exc_set(mrb, exc); +} + +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 ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc; -#define ERR_PC_CLR(mrb) mrb->c->ci->err = 0; -#ifdef ENABLE_DEBUG +#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) #endif +#ifdef MRB_BYTECODE_DECODE_OPTION +#define BYTECODE_DECODER(x) ((mrb)->bytecode_decoder)?(mrb)->bytecode_decoder((mrb), (x)):(x) +#else +#define BYTECODE_DECODER(x) (x) +#endif + +#ifndef MRB_NO_DIRECT_THREADING #if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER #define DIRECT_THREADED #endif +#endif /* ifndef MRB_NO_DIRECT_THREADING */ #ifndef DIRECT_THREADED -#define INIT_DISPATCH for (;;) { i = *pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); switch (GET_OPCODE(i)) { -#define CASE(op) case op: -#define NEXT pc++; break -#define JUMP break -#define END_DISPATCH }} +#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 L_END_DISPATCH:;}} #else #define INIT_DISPATCH JUMP; return mrb_nil_value(); -#define CASE(op) L_ ## op: -#define NEXT i=*++pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)] -#define JUMP i=*pc; CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)] +#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 #define END_DISPATCH #endif -mrb_value mrb_gv_val_get(mrb_state *mrb, mrb_sym sym); -void mrb_gv_val_set(mrb_state *mrb, mrb_sym sym, mrb_value val); +MRB_API mrb_value +mrb_vm_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int stack_keep) +{ + const mrb_irep *irep = proc->body.irep; + mrb_value result; + struct mrb_context *c = mrb->c; + ptrdiff_t cioff = c->ci - c->cibase; + mrb_int nregs = irep->nregs; -#define CALL_MAXARGS 127 + if (!c->stbase) { + stack_init(mrb); + } + if (stack_keep > nregs) + nregs = stack_keep; + mrb_stack_extend(mrb, nregs); + stack_clear(c->ci->stack + stack_keep, nregs - stack_keep); + c->ci->stack[0] = self; + result = mrb_vm_exec(mrb, proc, irep->iseq); + 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_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_context_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) +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_code *pc = irep->iseq; - mrb_value *pool = irep->pool; - mrb_sym *syms = irep->syms; - mrb_value *regs = NULL; - mrb_code i; + /* 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; + uint16_t c; + mrb_sym mid; + const struct mrb_irep_catch_handler *ch; #ifdef DIRECT_THREADED - static void *optable[] = { - &&L_OP_NOP, &&L_OP_MOVE, - &&L_OP_LOADL, &&L_OP_LOADI, &&L_OP_LOADSYM, &&L_OP_LOADNIL, - &&L_OP_LOADSELF, &&L_OP_LOADT, &&L_OP_LOADF, - &&L_OP_GETGLOBAL, &&L_OP_SETGLOBAL, &&L_OP_GETSPECIAL, &&L_OP_SETSPECIAL, - &&L_OP_GETIV, &&L_OP_SETIV, &&L_OP_GETCV, &&L_OP_SETCV, - &&L_OP_GETCONST, &&L_OP_SETCONST, &&L_OP_GETMCNST, &&L_OP_SETMCNST, - &&L_OP_GETUPVAR, &&L_OP_SETUPVAR, - &&L_OP_JMP, &&L_OP_JMPIF, &&L_OP_JMPNOT, - &&L_OP_ONERR, &&L_OP_RESCUE, &&L_OP_POPERR, &&L_OP_RAISE, &&L_OP_EPUSH, &&L_OP_EPOP, - &&L_OP_SEND, &&L_OP_SENDB, &&L_OP_FSEND, - &&L_OP_CALL, &&L_OP_SUPER, &&L_OP_ARGARY, &&L_OP_ENTER, - &&L_OP_KARG, &&L_OP_KDICT, &&L_OP_RETURN, &&L_OP_TAILCALL, &&L_OP_BLKPUSH, - &&L_OP_ADD, &&L_OP_ADDI, &&L_OP_SUB, &&L_OP_SUBI, &&L_OP_MUL, &&L_OP_DIV, - &&L_OP_EQ, &&L_OP_LT, &&L_OP_LE, &&L_OP_GT, &&L_OP_GE, - &&L_OP_ARRAY, &&L_OP_ARYCAT, &&L_OP_ARYPUSH, &&L_OP_AREF, &&L_OP_ASET, &&L_OP_APOST, - &&L_OP_STRING, &&L_OP_STRCAT, &&L_OP_HASH, - &&L_OP_LAMBDA, &&L_OP_RANGE, &&L_OP_OCLASS, - &&L_OP_CLASS, &&L_OP_MODULE, &&L_OP_EXEC, - &&L_OP_METHOD, &&L_OP_SCLASS, &&L_OP_TCLASS, - &&L_OP_DEBUG, &&L_OP_STOP, &&L_OP_ERR, + static const void * const optable[] = { +#define OPCODE(x,_) &&L_OP_ ## x, +#include "mruby/ops.h" +#undef OPCODE }; #endif @@ -758,425 +1117,483 @@ RETRY_TRY_BLOCK: if (exc_catched) { exc_catched = FALSE; + mrb_gc_arena_restore(mrb, ai); + if (mrb->exc && mrb->exc->tt == MRB_TT_BREAK) + goto L_BREAK; goto L_RAISE; } mrb->jmp = &c_jmp; - if (!mrb->c->stack) { - stack_init(mrb); - } - stack_extend(mrb, irep->nregs, stack_keep); - mrb->c->ci->proc = proc; - mrb->c->ci->nregs = irep->nregs; - regs = mrb->c->stack; - regs[0] = self; + mrb_vm_ci_proc_set(mrb->c->ci, proc); +#define regs (mrb->c->ci->stack) INIT_DISPATCH { - CASE(OP_NOP) { + CASE(OP_NOP, Z) { /* do nothing */ NEXT; } - CASE(OP_MOVE) { - /* A B R(A) := R(B) */ - regs[GETARG_A(i)] = regs[GETARG_B(i)]; + CASE(OP_MOVE, BB) { + regs[a] = regs[b]; + NEXT; + } + + CASE(OP_LOADL, BB) { + 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 +#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_LOADL) { - /* A Bx R(A) := Pool(Bx) */ - regs[GETARG_A(i)] = pool[GETARG_Bx(i)]; + CASE(OP_LOADI, BB) { + SET_FIXNUM_VALUE(regs[a], b); NEXT; } - CASE(OP_LOADI) { - /* A sBx R(A) := sBx */ - SET_INT_VALUE(regs[GETARG_A(i)], GETARG_sBx(i)); + CASE(OP_LOADINEG, BB) { + SET_FIXNUM_VALUE(regs[a], -b); NEXT; } - CASE(OP_LOADSYM) { - /* A Bx R(A) := Syms(Bx) */ - SET_SYM_VALUE(regs[GETARG_A(i)], syms[GETARG_Bx(i)]); + CASE(OP_LOADI__1,B) goto L_LOADI; + CASE(OP_LOADI_0,B) goto L_LOADI; + CASE(OP_LOADI_1,B) goto L_LOADI; + CASE(OP_LOADI_2,B) goto L_LOADI; + CASE(OP_LOADI_3,B) goto L_LOADI; + CASE(OP_LOADI_4,B) goto L_LOADI; + CASE(OP_LOADI_5,B) goto L_LOADI; + CASE(OP_LOADI_6,B) goto L_LOADI; + CASE(OP_LOADI_7, B) { + L_LOADI: + SET_FIXNUM_VALUE(regs[a], (mrb_int)insn - (mrb_int)OP_LOADI_0); NEXT; } - CASE(OP_LOADSELF) { - /* A R(A) := self */ - regs[GETARG_A(i)] = regs[0]; + CASE(OP_LOADI16, BS) { + SET_FIXNUM_VALUE(regs[a], (mrb_int)(int16_t)b); NEXT; } - CASE(OP_LOADT) { - /* A R(A) := true */ - SET_TRUE_VALUE(regs[GETARG_A(i)]); + CASE(OP_LOADI32, BSS) { + SET_INT_VALUE(mrb, regs[a], (int32_t)(((uint32_t)b<<16)+c)); NEXT; } - CASE(OP_LOADF) { - /* A R(A) := false */ - SET_FALSE_VALUE(regs[GETARG_A(i)]); + CASE(OP_LOADSYM, BB) { + SET_SYM_VALUE(regs[a], syms[b]); NEXT; } - CASE(OP_GETGLOBAL) { - /* A Bx R(A) := getglobal(Syms(Bx)) */ - regs[GETARG_A(i)] = mrb_gv_get(mrb, syms[GETARG_Bx(i)]); + CASE(OP_LOADNIL, B) { + SET_NIL_VALUE(regs[a]); NEXT; } - CASE(OP_SETGLOBAL) { - /* setglobal(Syms(Bx), R(A)) */ - mrb_gv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); + CASE(OP_LOADSELF, B) { + regs[a] = regs[0]; NEXT; } - CASE(OP_GETSPECIAL) { - /* A Bx R(A) := Special[Bx] */ - regs[GETARG_A(i)] = mrb_vm_special_get(mrb, GETARG_Bx(i)); + CASE(OP_LOADT, B) { + SET_TRUE_VALUE(regs[a]); NEXT; } - CASE(OP_SETSPECIAL) { - /* A Bx Special[Bx] := R(A) */ - mrb_vm_special_set(mrb, GETARG_Bx(i), regs[GETARG_A(i)]); + CASE(OP_LOADF, B) { + SET_FALSE_VALUE(regs[a]); NEXT; } - CASE(OP_GETIV) { - /* A Bx R(A) := ivget(Bx) */ - regs[GETARG_A(i)] = mrb_vm_iv_get(mrb, syms[GETARG_Bx(i)]); + CASE(OP_GETGV, BB) { + mrb_value val = mrb_gv_get(mrb, syms[b]); + regs[a] = val; NEXT; } - CASE(OP_SETIV) { - /* ivset(Syms(Bx),R(A)) */ - mrb_vm_iv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); + CASE(OP_SETGV, BB) { + mrb_gv_set(mrb, syms[b], regs[a]); NEXT; } - CASE(OP_GETCV) { - /* A Bx R(A) := cvget(Syms(Bx)) */ - ERR_PC_SET(mrb, pc); - regs[GETARG_A(i)] = mrb_vm_cv_get(mrb, syms[GETARG_Bx(i)]); - ERR_PC_CLR(mrb); + CASE(OP_GETSV, BB) { + mrb_value val = mrb_vm_special_get(mrb, syms[b]); + regs[a] = val; NEXT; } - CASE(OP_SETCV) { - /* cvset(Syms(Bx),R(A)) */ - mrb_vm_cv_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); + CASE(OP_SETSV, BB) { + mrb_vm_special_set(mrb, syms[b], regs[a]); NEXT; } - CASE(OP_GETCONST) { - /* A Bx R(A) := constget(Syms(Bx)) */ - mrb_value val; + CASE(OP_GETIV, BB) { + regs[a] = mrb_iv_get(mrb, regs[0], syms[b]); + NEXT; + } - ERR_PC_SET(mrb, pc); - val = mrb_vm_const_get(mrb, syms[GETARG_Bx(i)]); - ERR_PC_CLR(mrb); - regs = mrb->c->stack; - regs[GETARG_A(i)] = val; + CASE(OP_SETIV, BB) { + mrb_iv_set(mrb, regs[0], syms[b], regs[a]); NEXT; } - CASE(OP_SETCONST) { - /* A Bx constset(Syms(Bx),R(A)) */ - mrb_vm_const_set(mrb, syms[GETARG_Bx(i)], regs[GETARG_A(i)]); + CASE(OP_GETCV, BB) { + mrb_value val; + val = mrb_vm_cv_get(mrb, syms[b]); + regs[a] = val; NEXT; } - CASE(OP_GETMCNST) { - /* A Bx R(A) := R(A)::Syms(Bx) */ + CASE(OP_SETCV, BB) { + mrb_vm_cv_set(mrb, syms[b], regs[a]); + NEXT; + } + + CASE(OP_GETCONST, BB) { mrb_value val; - int a = GETARG_A(i); + mrb_sym sym = syms[b]; - ERR_PC_SET(mrb, pc); - val = mrb_const_get(mrb, regs[a], syms[GETARG_Bx(i)]); - ERR_PC_CLR(mrb); - regs = mrb->c->stack; + val = mrb_vm_const_get(mrb, sym); regs[a] = val; NEXT; } - CASE(OP_SETMCNST) { - /* A Bx R(A+1)::Syms(Bx) := R(A) */ - int a = GETARG_A(i); + CASE(OP_SETCONST, BB) { + mrb_vm_const_set(mrb, syms[b], regs[a]); + NEXT; + } + + CASE(OP_GETMCNST, BB) { + mrb_value val; - mrb_const_set(mrb, regs[a+1], syms[GETARG_Bx(i)], regs[a]); + val = mrb_const_get(mrb, regs[a], syms[b]); + regs[a] = val; NEXT; } - CASE(OP_GETUPVAR) { - /* A B C R(A) := uvget(B,C) */ - mrb_value *regs_a = regs + GETARG_A(i); - int up = GETARG_C(i); + CASE(OP_SETMCNST, BB) { + mrb_const_set(mrb, regs[a+1], syms[b], regs[a]); + NEXT; + } - struct REnv *e = uvenv(mrb, up); + CASE(OP_GETUPVAR, BBB) { + mrb_value *regs_a = regs + a; + struct REnv *e = uvenv(mrb, c); - if (!e) { - *regs_a = mrb_nil_value(); + if (e && b < MRB_ENV_LEN(e)) { + *regs_a = e->stack[b]; } else { - int idx = GETARG_B(i); - *regs_a = e->stack[idx]; + *regs_a = mrb_nil_value(); } NEXT; } - CASE(OP_SETUPVAR) { - /* A B C uvset(B,C,R(A)) */ - int up = GETARG_C(i); - - struct REnv *e = uvenv(mrb, up); + CASE(OP_SETUPVAR, BBB) { + struct REnv *e = uvenv(mrb, c); if (e) { - mrb_value *regs_a = regs + GETARG_A(i); - int idx = GETARG_B(i); - e->stack[idx] = *regs_a; - mrb_write_barrier(mrb, (struct RBasic*)e); + mrb_value *regs_a = regs + a; + + if (b < MRB_ENV_LEN(e)) { + e->stack[b] = *regs_a; + mrb_write_barrier(mrb, (struct RBasic*)e); + } } NEXT; } - CASE(OP_JMP) { - /* sBx pc+=sBx */ - pc += GETARG_sBx(i); + CASE(OP_JMP, S) { + pc += (int16_t)a; JUMP; } - - CASE(OP_JMPIF) { - /* A sBx if R(A) pc+=sBx */ - if (mrb_test(regs[GETARG_A(i)])) { - pc += GETARG_sBx(i); + CASE(OP_JMPIF, BS) { + if (mrb_test(regs[a])) { + pc += (int16_t)b; JUMP; } NEXT; } - - CASE(OP_JMPNOT) { - /* A sBx if !R(A) pc+=sBx */ - if (!mrb_test(regs[GETARG_A(i)])) { - pc += GETARG_sBx(i); + CASE(OP_JMPNOT, BS) { + if (!mrb_test(regs[a])) { + pc += (int16_t)b; JUMP; } NEXT; } - - CASE(OP_ONERR) { - /* sBx pc+=sBx on exception */ - if (mrb->c->rsize <= mrb->c->ci->ridx) { - if (mrb->c->rsize == 0) mrb->c->rsize = 16; - else mrb->c->rsize *= 2; - mrb->c->rescue = (mrb_code **)mrb_realloc(mrb, mrb->c->rescue, sizeof(mrb_code*) * mrb->c->rsize); + CASE(OP_JMPNIL, BS) { + if (mrb_nil_p(regs[a])) { + pc += (int16_t)b; + JUMP; } - mrb->c->rescue[mrb->c->ci->ridx++] = pc + GETARG_sBx(i); NEXT; } - CASE(OP_RESCUE) { - /* A R(A) := exc; clear(exc) */ - SET_OBJ_VALUE(regs[GETARG_A(i)], mrb->exc); - mrb->exc = 0; - NEXT; + 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); + } + 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)); + } + } + } + CHECKPOINT_END(RBREAK_TAG_JUMP); + + mrb->exc = NULL; /* clear break object */ + pc = irep->iseq + a; + JUMP; } - CASE(OP_POPERR) { - /* A A.times{rescue_pop()} */ - int a = GETARG_A(i); + CASE(OP_EXCEPT, B) { + mrb_value exc; - while (a--) { - mrb->c->ci->ridx--; + 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; } + CASE(OP_RESCUE, BB) { + mrb_value exc = regs[a]; /* exc on stack */ + mrb_value e = regs[b]; + struct RClass *ec; - CASE(OP_RAISE) { - /* A raise(R(A)) */ - mrb->exc = mrb_obj_ptr(regs[GETARG_A(i)]); - goto L_RAISE; - } - - CASE(OP_EPUSH) { - /* Bx ensure_push(SEQ[Bx]) */ - struct RProc *p; + switch (mrb_type(e)) { + case MRB_TT_CLASS: + case MRB_TT_MODULE: + break; + default: + { + mrb_value exc; - p = mrb_closure_new(mrb, irep->reps[GETARG_Bx(i)]); - /* push ensure_stack */ - if (mrb->c->esize <= mrb->c->ci->eidx) { - if (mrb->c->esize == 0) mrb->c->esize = 16; - else mrb->c->esize *= 2; - mrb->c->ensure = (struct RProc **)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*) * mrb->c->esize); + exc = mrb_exc_new_lit(mrb, E_TYPE_ERROR, + "class or module required for rescue clause"); + mrb_exc_set(mrb, exc); + goto L_RAISE; + } } - mrb->c->ensure[mrb->c->ci->eidx++] = p; - ARENA_RESTORE(mrb, ai); + ec = mrb_class_ptr(e); + regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec)); NEXT; } - CASE(OP_EPOP) { - /* A A.times{ensure_pop().call} */ - int a = GETARG_A(i); - mrb_callinfo *ci = mrb->c->ci; - int n, eidx = ci->eidx; - - for (n=0; n<a && eidx > ci[-1].eidx; n++) { - ecall(mrb, --eidx); - ARENA_RESTORE(mrb, ai); + CASE(OP_RAISEIF, B) { + mrb_value exc = regs[a]; + if (mrb_break_p(exc)) { + mrb->exc = mrb_obj_ptr(exc); + goto L_BREAK; + } + mrb_exc_set(mrb, exc); + if (mrb->exc) { + goto L_RAISE; } NEXT; } - CASE(OP_LOADNIL) { - /* A R(A) := nil */ - int a = GETARG_A(i); + CASE(OP_SENDV, BB) { + c = CALL_MAXARGS; + goto L_SEND; + }; - SET_NIL_VALUE(regs[a]); - NEXT; - } + CASE(OP_SENDVB, BB) { + c = CALL_MAXARGS; + goto L_SENDB; + }; - CASE(OP_SENDB) { - /* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C),&R(A+C+1))*/ - /* fall through */ + CASE(OP_SEND, BBB) + L_SEND: + { + /* push nil after arguments */ + int bidx = (c == CALL_MAXARGS) ? a+2 : a+c+1; + 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; }; - L_SEND: - CASE(OP_SEND) { - /* A B C R(A) := call(R(A),Syms(B),R(A+1),...,R(A+C)) */ - int a = GETARG_A(i); - int n = GETARG_C(i); - struct RProc *m; - struct RClass *c; - mrb_callinfo *ci; - mrb_value recv, result; - mrb_sym mid = syms[GETARG_B(i)]; + CASE(OP_SENDB, BBB) + L_SENDB: + mid = syms[b]; + L_SENDB_SYM: + { + 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; - recv = regs[a]; - if (GET_OPCODE(i) != OP_SENDB) { - if (n == CALL_MAXARGS) { - SET_NIL_VALUE(regs[a+2]); - } - else { - SET_NIL_VALUE(regs[a+n+1]); - } - } - c = mrb_class(mrb, recv); - m = mrb_method_search_vm(mrb, &c, mid); - if (!m) { - mrb_value sym = mrb_symbol_value(mid); + mrb_assert(bidx < irep->nregs); - mid = mrb_intern_lit(mrb, "method_missing"); - m = mrb_method_search_vm(mrb, &c, mid); - if (n == CALL_MAXARGS) { - mrb_ary_unshift(mrb, regs[a+1], sym); - } - else { - value_move(regs+a+2, regs+a+1, ++n); - regs[a+1] = sym; + recv = regs[a]; + blk = regs[bidx]; + 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_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); + } + regs[a+1] = mrb_ary_new_from_values(mrb, c, regs+a+1); + regs[a+2] = blk; + argc = -1; } + mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(mid)); + mid = missing; } /* push callinfo */ - ci = cipush(mrb); - ci->mid = mid; - ci->proc = m; - ci->stackent = mrb->c->stack; - if (c->tt == MRB_TT_ICLASS) { - ci->target_class = c->c; - } - else { - ci->target_class = c; - } + ci = cipush(mrb, a, a, cls, NULL, mid, argc); - ci->pc = pc + 1; - ci->acc = a; + if (MRB_METHOD_CFUNC_P(m)) { + if (MRB_METHOD_PROC_P(m)) { + struct RProc *p = MRB_METHOD_PROC(m); - /* prepare stack */ - mrb->c->stack += a; - - if (MRB_PROC_CFUNC_P(m)) { - if (n == CALL_MAXARGS) { - ci->argc = -1; - ci->nregs = 3; + 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 { - ci->argc = n; - ci->nregs = n + 2; + recv = MRB_METHOD_FUNC(m)(mrb, recv); } - result = m->body.func(mrb, recv); - mrb->c->stack[0] = result; mrb_gc_arena_restore(mrb, ai); + mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; - /* pop stackpos */ ci = mrb->c->ci; - if (!ci->target_class) { /* return from context modifying method (resume/yield) */ - if (!MRB_PROC_CFUNC_P(ci[-1].proc)) { + if (mrb_proc_p(blk)) { + struct RProc *p = mrb_proc_ptr(blk); + if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == mrb_vm_ci_env(&ci[-1])) { + p->flags |= MRB_PROC_ORPHAN; + } + } + if (!ci->u.target_class) { /* return from context modifying method (resume/yield) */ + if (ci->acc == CI_ACC_RESUMED) { + mrb->jmp = prev_jmp; + return recv; + } + else { + mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc)); proc = ci[-1].proc; irep = proc->body.irep; pool = irep->pool; syms = irep->syms; } } - regs = mrb->c->stack = ci->stackent; + mrb->c->ci->stack[0] = recv; + /* pop stackpos */ + ci = cipop(mrb); pc = ci->pc; - cipop(mrb); - JUMP; } else { /* setup environment for calling method */ - proc = mrb->c->ci->proc = m; - irep = m->body.irep; + mrb_vm_ci_proc_set(ci, (proc = MRB_METHOD_PROC(m))); + irep = proc->body.irep; pool = irep->pool; syms = irep->syms; - ci->nregs = irep->nregs; - if (n == CALL_MAXARGS) { - ci->argc = -1; - stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); - } - else { - ci->argc = n; - stack_extend(mrb, irep->nregs, n+2); - } - regs = mrb->c->stack; + mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : irep->nregs); pc = irep->iseq; - JUMP; } } + JUMP; - CASE(OP_FSEND) { - /* A B C R(A) := fcall(R(A),Syms(B),R(A+1),... ,R(A+C-1)) */ - NEXT; - } - - CASE(OP_CALL) { - /* A R(A) := self.call(frame.argc, frame.argv) */ + 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 = m->target_class; - ci->proc = m; - if (m->env) { - if (m->env->mid) { - ci->mid = m->env->mid; - } - if (!m->env->stack) { - m->env->stack = mrb->c->stack; - } + ci->u.target_class = MRB_PROC_TARGET_CLASS(m); + mrb_vm_ci_proc_set(ci, m); + if (MRB_PROC_ENV_P(m)) { + ci->mid = MRB_PROC_ENV(m)->mid; } /* prepare stack */ if (MRB_PROC_CFUNC_P(m)) { - recv = m->body.func(mrb, recv); + recv = MRB_PROC_CFUNC(m)(mrb, recv); mrb_gc_arena_restore(mrb, ai); + mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; /* pop stackpos */ - ci = mrb->c->ci; - regs = 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; @@ -1187,232 +1604,323 @@ RETRY_TRY_BLOCK: proc = m; irep = m->body.irep; if (!irep) { - mrb->c->stack[0] = mrb_nil_value(); - goto L_RETURN; + mrb->c->ci->stack[0] = mrb_nil_value(); + a = 0; + c = OP_R_NORMAL; + goto L_OP_RETURN_BODY; } pool = irep->pool; syms = irep->syms; - ci->nregs = irep->nregs; + mrb_stack_extend(mrb, irep->nregs); if (ci->argc < 0) { - stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); + if (irep->nregs > 3) { + stack_clear(regs+3, irep->nregs-3); + } } - else { - stack_extend(mrb, irep->nregs, ci->argc+2); + else if (ci->argc+2 < irep->nregs) { + stack_clear(regs+ci->argc+2, irep->nregs-ci->argc-2); + } + if (MRB_PROC_ENV_P(m)) { + regs[0] = MRB_PROC_ENV(m)->stack[0]; } - regs = mrb->c->stack; - regs[0] = m->env->stack[0]; pc = irep->iseq; JUMP; } } - CASE(OP_SUPER) { - /* A C R(A) := super(R(A+1),... ,R(A+C+1)) */ - mrb_value recv; + CASE(OP_SUPER, BB) { + 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; - struct RProc *m; - struct RClass *c; + mrb_value recv, blk; + const struct RProc *p = ci->proc; mrb_sym mid = ci->mid; - int a = GETARG_A(i); - int n = GETARG_C(i); + 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_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); + mrb_exc_set(mrb, exc); + goto L_RAISE; + } + 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) { + goto super_typeerror; + } + } recv = regs[0]; - c = mrb->c->ci->target_class->super; - m = mrb_method_search_vm(mrb, &c, mid); - if (!m) { - mid = mrb_intern_lit(mrb, "method_missing"); - m = mrb_method_search_vm(mrb, &c, mid); - if (n == CALL_MAXARGS) { - mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid)); + if (!mrb_obj_is_kind_of(mrb, recv, target_class)) { + 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_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_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_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); } - else { - value_move(regs+a+2, regs+a+1, ++n); - SET_SYM_VALUE(regs[a+1], ci->mid); + if (mid != missing) { + cls = mrb_class(mrb, recv); + } + m = mrb_method_search_vm(mrb, &cls, 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); + } + regs[a+1] = mrb_ary_new_from_values(mrb, b, regs+a+1); + regs[a+2] = blk; + argc = -1; } + mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(mid)); + mid = missing; } /* push callinfo */ - ci = cipush(mrb); - ci->mid = mid; - ci->proc = m; - ci->stackent = mrb->c->stack; - if (n == CALL_MAXARGS) { - ci->argc = -1; - } - else { - ci->argc = n; - } - ci->target_class = c; - ci->pc = pc + 1; + 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_PROC_CFUNC_P(m)) { - if (n == CALL_MAXARGS) { - ci->nregs = 3; - } - else { - ci->nregs = n + 2; + if (MRB_METHOD_CFUNC_P(m)) { + mrb_value v; + + if (MRB_METHOD_PROC_P(m)) { + mrb_vm_ci_proc_set(ci, MRB_METHOD_PROC(m)); } - mrb->c->stack[0] = m->body.func(mrb, recv); + v = MRB_METHOD_CFUNC(m)(mrb, recv); mrb_gc_arena_restore(mrb, ai); if (mrb->exc) goto L_RAISE; - /* pop stackpos */ - regs = mrb->c->stack = mrb->c->ci->stackent; - cipop(mrb); - NEXT; + ci = mrb->c->ci; + 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; + } + else { + mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc)); + proc = ci[-1].proc; + irep = proc->body.irep; + pool = irep->pool; + syms = irep->syms; + } + } + mrb->c->ci->stack[0] = v; + ci = cipop(mrb); + pc = ci->pc; + JUMP; } else { /* fill callinfo */ ci->acc = a; /* setup environment for calling method */ - ci->proc = m; - irep = m->body.irep; + mrb_vm_ci_proc_set(ci, (proc = MRB_METHOD_PROC(m))); + irep = proc->body.irep; pool = irep->pool; syms = irep->syms; - ci->nregs = irep->nregs; - if (n == CALL_MAXARGS) { - stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); - } - else { - stack_extend(mrb, irep->nregs, ci->argc+2); - } - regs = mrb->c->stack; + mrb_stack_extend(mrb, (argc < 0 && irep->nregs < 3) ? 3 : irep->nregs); pc = irep->iseq; JUMP; } } - CASE(OP_ARGARY) { - /* A Bx R(A) := argument array (16=6:1:5:4) */ - int a = GETARG_A(i); - int bx = GETARG_Bx(i); - int m1 = (bx>>10)&0x3f; - int r = (bx>>9)&0x1; - int m2 = (bx>>4)&0x1f; - int lv = (bx>>0)&0xf; + CASE(OP_ARGARY, BS) { + 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_vm_ci_target_class(mrb->c->ci) == NULL) { + mrb_value exc; + + L_NOSUPER: + exc = mrb_exc_new_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); + mrb_exc_set(mrb, exc); + goto L_RAISE; + } if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); - if (!e) { - mrb_value exc; - exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); - mrb->exc = mrb_obj_ptr(exc); - goto L_RAISE; - } + if (!e) goto L_NOSUPER; + if (MRB_ENV_LEN(e) <= m1+r+m2+kd+1) + goto L_NOSUPER; stack = e->stack + 1; } if (r == 0) { - regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack); + regs[a] = mrb_ary_new_from_values(mrb, m1+m2+kd, stack); } 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; - len = ary->len; + pp = ARY_PTR(ary); + len = ARY_LEN(ary); } - regs[a] = mrb_ary_new_capa(mrb, m1+len+m2); + regs[a] = mrb_ary_new_capa(mrb, m1+len+m2+kd); rest = mrb_ary_ptr(regs[a]); if (m1 > 0) { - stack_copy(rest->ptr, stack, m1); + stack_copy(ARY_PTR(rest), stack, m1); } if (len > 0) { - stack_copy(rest->ptr+m1, pp, len); + stack_copy(ARY_PTR(rest)+m1, pp, len); } if (m2 > 0) { - stack_copy(rest->ptr+m1+len, stack+m1+1, m2); + stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2); } - rest->len = m1+len+m2; + if (kd) { + stack_copy(ARY_PTR(rest)+m1+len+m2, stack+m1+m2+1, kd); + } + ARY_SET_LEN(rest, m1+len+m2+kd); } regs[a+1] = stack[m1+r+m2]; - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_ENTER) { - /* Ax arg setup according to flags (23=5:5:1:5:5:1:1) */ - /* number of optional arguments times OP_JMP should follow */ - mrb_aspec ax = GETARG_Ax(i); - int m1 = MRB_ASPEC_REQ(ax); - int o = MRB_ASPEC_OPT(ax); - int r = MRB_ASPEC_REST(ax); - int m2 = MRB_ASPEC_POST(ax); + CASE(OP_ENTER, W) { + 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 k = MRB_ASPEC_KEY(ax); - int kd = MRB_ASPEC_KDICT(ax); - int b = MRB_ASPEC_BLOCK(ax); + 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 *argv0 = argv; - int len = m1 + o + r + m2; + mrb_value * const argv0 = argv; + 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 = mrb_nil_value(); + mrb_int kargs = kd; - if (!mrb_nil_p(*blk) && mrb_type(*blk) != MRB_TT_PROC) { - *blk = mrb_convert_type(mrb, *blk, MRB_TT_PROC, "Proc", "to_proc"); - } + /* arguments is passed with Array */ if (argc < 0) { struct RArray *ary = mrb_ary_ptr(regs[1]); - argv = ary->ptr; - argc = ary->len; + argv = ARY_PTR(ary); + argc = (int)ARY_LEN(ary); mrb_gc_protect(mrb, regs[1]); } + + /* strict argument check */ if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) { - if (argc >= 0) { - if (argc < m1 + m2 || (r == 0 && argc > len)) { - 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 */ else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) { mrb_gc_protect(mrb, argv[0]); - argc = mrb_ary_ptr(argv[0])->len; - argv = mrb_ary_ptr(argv[0])->ptr; + argc = (int)RARRAY_LEN(argv[0]); + argv = RARRAY_PTR(argv[0]); } - mrb->c->ci->argc = len; - if (argc < len) { - int mlen = m2; + + if (kd) { + /* check last arguments is hash if method takes keyword arguments */ + if (argc == m1+m2) { + kdict = mrb_hash_new(mrb); + kargs = 0; + } + else { + if (argv && argc > 0 && mrb_hash_p(argv[argc-1])) { + kdict = argv[argc-1]; + mrb_hash_check_kdict(mrb, kdict); + } + 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 { + argnum_error(mrb, m1+m2); + goto L_RAISE; + } + if (MRB_ASPEC_KEY(a) > 0) { + kdict = mrb_hash_dup(mrb, kdict); + } + } + } + + /* no rest arguments */ + if (argc-kargs < len) { + mrb_int mlen = m2; if (argc < m1+m2) { - if (m1 < argc) - mlen = argc - m1; - else - mlen = 0; + mlen = m1 < argc ? argc - m1 : 0; } - regs[len+1] = *blk; /* move block */ - SET_NIL_VALUE(regs[argc+1]); - if (argv0 != argv) { + regs[blk_pos] = *blk; /* move block */ + if (kd) regs[len + 1] = kdict; + + /* copy mandatory and optional arguments */ + if (argv0 != argv && argv) { value_move(®s[1], argv, argc-mlen); /* m1 + o */ } + if (argc < m1) { + stack_clear(®s[argc+1], m1-argc); + } + /* copy post mandatory arguments */ if (mlen) { value_move(®s[len-m2+1], &argv[argc-mlen], mlen); } + if (mlen < m2) { + stack_clear(®s[len-m2+mlen+1], m2-mlen); + } + /* initialize rest arguments with empty Array */ if (r) { regs[m1+o+1] = mrb_ary_new_capa(mrb, 0); } - if (o == 0 || argc < m1+m2) pc++; - else - pc += argc - m1 - m2 + 1; + /* 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[len+1] = *blk; /* move block */ + regs[blk_pos] = *blk; /* move block */ + if (kd) regs[len + 1] = kdict; value_move(®s[1], argv, m1+o); } if (r) { - rnum = argc-m1-o-m2; - regs[m1+o+1] = mrb_ary_new_from_values(mrb, rnum, argv+m1+o); + mrb_value ary; + + rnum = argc-m1-o-m2-kargs; + ary = mrb_ary_new_from_values(mrb, rnum, argv+m1+o); + regs[m1+o+1] = ary; } if (m2) { if (argc-m2 > m1) { @@ -1420,68 +1928,122 @@ RETRY_TRY_BLOCK: } } if (argv0 == argv) { - regs[len+1] = *blk; /* move block */ + regs[blk_pos] = *blk; /* move block */ + if (kd) regs[len + 1] = kdict; } - pc += o + 1; + pc += o*3; + } + + /* format arguments for generated code */ + mrb->c->ci->argc = (int16_t)(len + kd); + + /* clear local (but non-argument) variables */ + if (irep->nlocals-blk_pos-1 > 0) { + stack_clear(®s[blk_pos+1], irep->nlocals-blk_pos-1); } JUMP; } - CASE(OP_KARG) { - /* A B C R(A) := kdict[Syms(B)]; if C kdict.rm(Syms(B)) */ - /* if C == 2; raise unless kdict.empty? */ - /* OP_JMP should follow to skip init code */ + CASE(OP_KARG, BB) { + mrb_value k = mrb_symbol_value(syms[b]); + mrb_value kdict = regs[mrb->c->ci->argc]; + + 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; + } + regs[a] = mrb_hash_get(mrb, kdict, k); + mrb_hash_delete_key(mrb, kdict, k); NEXT; } - CASE(OP_KDICT) { - /* A C R(A) := kdict */ + 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 = FALSE; + + if (mrb_hash_p(kdict)) { + key_p = mrb_hash_key_p(mrb, kdict, k); + } + regs[a] = mrb_bool_value(key_p); NEXT; } + CASE(OP_KEYEND, Z) { + mrb_value kdict = regs[mrb->c->ci->argc]; + + 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: %v", key1); + mrb_exc_set(mrb, mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str)); + goto L_RAISE; + } + NEXT; + } + + CASE(OP_BREAK, B) { + c = OP_R_BREAK; + goto L_RETURN; + } + CASE(OP_RETURN_BLK, B) { + c = OP_R_RETURN; + goto L_RETURN; + } + CASE(OP_RETURN, B) + c = OP_R_NORMAL; L_RETURN: - i = MKOP_AB(OP_RETURN, GETARG_A(i), OP_R_NORMAL); - /* fall through */ - CASE(OP_RETURN) { - /* A B return R(A) (B=normal,in-block return/break) */ - if (mrb->exc) { - mrb_callinfo *ci; - int eidx; + { + mrb_callinfo *ci; + + ci = mrb->c->ci; + if (ci->mid) { + mrb_value blk; + if (ci->argc < 0) { + blk = regs[2]; + } + else { + blk = regs[ci->argc+1]; + } + if (mrb_proc_p(blk)) { + struct RProc *p = mrb_proc_ptr(blk); + + if (!MRB_PROC_STRICT_P(p) && + ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb_vm_ci_env(&ci[-1])) { + p->flags |= MRB_PROC_ORPHAN; + } + } + } + + if (mrb->exc) { L_RAISE: ci = mrb->c->ci; - mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "lastpc"), mrb_cptr_value(mrb, pc)); - mrb_obj_iv_ifnone(mrb, mrb->exc, mrb_intern_lit(mrb, "ciidx"), mrb_fixnum_value(ci - mrb->c->cibase)); - eidx = ci->eidx; if (ci == mrb->c->cibase) { - if (ci->ridx == 0) goto L_STOP; - goto L_RESCUE; - } - while (eidx > ci[-1].eidx) { - ecall(mrb, --eidx); + 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); - ci = mrb->c->ci; - mrb->c->stack = ci[1].stackent; + 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); } - if (ci > mrb->c->cibase) { - while (eidx > ci[-1].eidx) { - ecall(mrb, --eidx); - } - } - else if (ci == mrb->c->cibase) { - if (ci->ridx == 0) { + pc = ci[0].pc; + if (ci == mrb->c->cibase) { + ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL); + if (ch == NULL) { + L_FTOP: /* fiber top */ if (mrb->c == mrb->root_c) { - regs = mrb->c->stack = mrb->c->stbase; + mrb->c->ci->stack = mrb->c->stbase; goto L_STOP; } else { struct mrb_context *c = mrb->c; + c->status = MRB_FIBER_TERMINATED; mrb->c = c->prev; c->prev = NULL; goto L_RAISE; @@ -1490,521 +2052,459 @@ RETRY_TRY_BLOCK: break; } } - 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; - regs = mrb->c->stack = ci[1].stackent; - pc = mrb->c->rescue[--ci->ridx]; + mrb_stack_extend(mrb, irep->nregs); + pc = irep->iseq + mrb_irep_catch_handler_unpack(ch->target); } else { - mrb_callinfo *ci = mrb->c->ci; - int acc, eidx = mrb->c->ci->eidx; - mrb_value v = regs[GETARG_A(i)]; + mrb_int acc; + mrb_value v; - switch (GETARG_B(i)) { + ci = mrb->c->ci; + v = regs[a]; + mrb_gc_protect(mrb, v); + switch (c) { case OP_R_RETURN: /* Fall through to OP_R_NORMAL otherwise */ - if (proc->env && !MRB_PROC_STRICT_P(proc)) { - struct REnv *e = top_env(mrb, proc); + if (ci->acc >=0 && MRB_PROC_ENV_P(proc) && !MRB_PROC_STRICT_P(proc)) { + const struct RProc *dst; + mrb_callinfo *cibase; + cibase = mrb->c->cibase; + dst = top_proc(mrb, proc); - if (!MRB_ENV_STACK_SHARED_P(e)) { - localjump_error(mrb, LOCALJUMP_ERROR_RETURN); - goto L_RAISE; + if (MRB_PROC_ENV_P(dst)) { + struct REnv *e = MRB_PROC_ENV(dst); + + 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) { /* jump cross C boundary */ + localjump_error(mrb, LOCALJUMP_ERROR_RETURN); + goto L_RAISE; + } + ci--; } - ci = mrb->c->cibase + e->cioff; - if (ci == mrb->c->cibase) { + if (ci <= cibase) { /* no jump destination */ localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } - mrb->c->ci = ci; + 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) { - if (!mrb->c->prev) { /* toplevel return */ - localjump_error(mrb, LOCALJUMP_ERROR_RETURN); - goto L_RAISE; + struct mrb_context *c; + c = mrb->c; + + if (!c->prev) { /* toplevel return */ + regs[irep->nlocals] = v; + goto CHECKPOINT_LABEL_MAKE(RBREAK_TAG_STOP); } - if (mrb->c->prev->ci == mrb->c->prev->cibase) { - mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume"); - mrb->exc = mrb_obj_ptr(exc); + 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; } + 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 */ - mrb->c->status = MRB_FIBER_TERMINATED; - mrb->c = mrb->c->prev; + c->status = MRB_FIBER_TERMINATED; + mrb->c = c->prev; 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; } - 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 (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) { - localjump_error(mrb, LOCALJUMP_ERROR_BREAK); + if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN; + if (MRB_PROC_ORPHAN_P(proc)) { + mrb_value exc; + + L_BREAK_ERROR: + exc = mrb_exc_new_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_ONSTACK_P(MRB_PROC_ENV(proc))) { + goto L_BREAK_ERROR; + } + else { + struct REnv *e = MRB_PROC_ENV(proc); + + if (e->cxt != mrb->c) { + goto L_BREAK_ERROR; + } + } + 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 (mrb->c->ci == mrb->c->cibase && mrb->c->ci->pc) { + if (ci == mrb->c->cibase && ci->pc) { struct mrb_context *c = mrb->c; mrb->c = c->prev; c->prev = NULL; + ci = mrb->c->ci; } - ci = mrb->c->ci; - mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1; - while (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, RBREAK_TAG_BREAK, proc, v); + mrb->jmp = prev_jmp; + MRB_THROW(prev_jmp); + } + if (FALSE) { + struct RBreak *brk; + + L_BREAK: + 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"); + } + } + while (mrb->c->cibase < ci && ci[-1].proc != proc->upper) { if (ci[-1].acc == CI_ACC_SKIP) { - mrb->c->ci = ci; - break; + goto L_BREAK_ERROR; + } + CHECKPOINT_RESTORE(RBREAK_TAG_BREAK_UPPER) { + /* do nothing */ } - ci--; + 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 (eidx > mrb->c->ci[-1].eidx) { - ecall(mrb, --eidx); + 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; } - cipop(mrb); acc = ci->acc; - pc = ci->pc; - regs = mrb->c->stack = ci->stackent; - if (acc == CI_ACC_SKIP) { + ci = cipop(mrb); + if (acc == CI_ACC_SKIP || acc == CI_ACC_DIRECT) { + mrb_gc_arena_restore(mrb, ai); mrb->jmp = prev_jmp; return v; } - DEBUG(printf("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; syms = irep->syms; regs[acc] = v; - } - JUMP; - } - - CASE(OP_TAILCALL) { - /* A B C return call(R(A),Syms(B),R(A+1),... ,R(A+C+1)) */ - int a = GETARG_A(i); - int n = GETARG_C(i); - struct RProc *m; - struct RClass *c; - mrb_callinfo *ci; - mrb_value recv; - mrb_sym mid = syms[GETARG_B(i)]; - - recv = regs[a]; - c = mrb_class(mrb, recv); - m = mrb_method_search_vm(mrb, &c, mid); - if (!m) { - mrb_value sym = mrb_symbol_value(mid); - - mid = mrb_intern_lit(mrb, "method_missing"); - m = mrb_method_search_vm(mrb, &c, mid); - if (n == CALL_MAXARGS) { - mrb_ary_unshift(mrb, regs[a+1], sym); - } - else { - value_move(regs+a+2, regs+a+1, ++n); - regs[a+1] = sym; - } - } - - /* replace callinfo */ - ci = mrb->c->ci; - ci->mid = mid; - ci->target_class = c; - if (n == CALL_MAXARGS) { - ci->argc = -1; - } - else { - ci->argc = n; - } - - /* move stack */ - value_move(mrb->c->stack, ®s[a], ci->argc+1); - - if (MRB_PROC_CFUNC_P(m)) { - mrb->c->stack[0] = m->body.func(mrb, recv); mrb_gc_arena_restore(mrb, ai); - goto L_RETURN; - } - else { - /* setup environment for calling method */ - irep = m->body.irep; - pool = irep->pool; - syms = irep->syms; - if (ci->argc < 0) { - stack_extend(mrb, (irep->nregs < 3) ? 3 : irep->nregs, 3); - } - else { - stack_extend(mrb, irep->nregs, ci->argc+2); - } - regs = mrb->c->stack; - pc = irep->iseq; } JUMP; } - CASE(OP_BLKPUSH) { - /* A Bx R(A) := block (16=6:1:5:4) */ - int a = GETARG_A(i); - int bx = GETARG_Bx(i); - int m1 = (bx>>10)&0x3f; - int r = (bx>>9)&0x1; - int m2 = (bx>>4)&0x1f; - int lv = (bx>>0)&0xf; + CASE(OP_BLKPUSH, 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_value *stack; if (lv == 0) stack = regs + 1; else { struct REnv *e = uvenv(mrb, lv-1); - if (!e) { + 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; } stack = e->stack + 1; } - regs[a] = stack[m1+r+m2]; - 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) { - /* A B C R(A) := R(A)+R(A+1) (Syms[B]=:+,C=1)*/ - int a = GETARG_A(i); - - /* 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)) { - SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y); - break; - } - SET_INT_VALUE(regs[a], z); - } - break; - 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; - case TYPES2(MRB_TT_STRING,MRB_TT_STRING): - regs[a] = mrb_str_plus(mrb, regs[a], regs[a+1]); - break; - default: - goto L_SEND; + if (mrb_nil_p(stack[m1+r+m2])) { + localjump_error(mrb, LOCALJUMP_ERROR_YIELD); + goto L_RAISE; } - ARENA_RESTORE(mrb, ai); + regs[a] = stack[m1+r+m2+kd]; NEXT; } - CASE(OP_SUB) { - /* A B C R(A) := R(A)-R(A+1) (Syms[B]=:-,C=1)*/ - int a = GETARG_A(i); - - /* 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; + L_INT_OVERFLOW: + { + mrb_value exc = mrb_exc_new_lit(mrb, E_RANGE_ERROR, "integer overflow"); + mrb_exc_set(mrb, exc); + } + goto L_RAISE; - x = mrb_fixnum(regs[a]); - y = mrb_fixnum(regs[a+1]); - if (mrb_int_sub_overflow(x, y, &z)) { - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y); - break; - } - SET_INT_VALUE(regs[a], z); - } - break; - 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; - default: - 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_MUL) { - /* A B C R(A) := R(A)*R(A+1) (Syms[B]=:*,C=1)*/ - int a = GETARG_A(i); - - /* 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_value z; + CASE(OP_ADD, B) { + OP_MATH(add); + } - z = mrb_fixnum_mul(mrb, regs[a], regs[a+1]); + CASE(OP_SUB, B) { + OP_MATH(sub); + } - switch (mrb_type(z)) { - case MRB_TT_FIXNUM: - { - SET_INT_VALUE(regs[a], mrb_fixnum(z)); - } - break; - case MRB_TT_FLOAT: - { - SET_FLOAT_VALUE(mrb, regs[a], mrb_float(z)); - } - break; - default: - /* cannot happen */ - break; - } - } - break; - 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; - default: - goto L_SEND; - } - NEXT; + CASE(OP_MUL, B) { + OP_MATH(mul); } - CASE(OP_DIV) { - /* A B C R(A) := R(A)/R(A+1) (Syms[B]=:/,C=1)*/ - int a = GETARG_A(i); + 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): + case TYPES2(MRB_TT_INTEGER,MRB_TT_INTEGER): { - mrb_int x = mrb_fixnum(regs[a]); - mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / (mrb_float)y); + 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); } + 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_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 + case TYPES2(MRB_TT_FLOAT,MRB_TT_INTEGER): + x = mrb_float(regs[a]); + y = (mrb_float)mrb_integer(regs[a+1]); 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 + x = mrb_float(regs[a]); + y = mrb_float(regs[a+1]); break; +#endif default: - goto L_SEND; - } -#ifdef MRB_NAN_BOXING - if (isnan(mrb_float(regs[a]))) { - regs[a] = mrb_float_value(mrb, mrb_float(regs[a])); + c = 1; + mid = MRB_OPSYM(div); + goto L_SEND_SYM; } + +#ifndef MRB_NO_FLOAT + f = mrb_div_float(x, y); + SET_FLOAT_VALUE(mrb, regs[a], f); #endif NEXT; } - CASE(OP_ADDI) { - /* A B C R(A) := R(A)+C (Syms[B]=:+)*/ - int a = GETARG_A(i); - - /* 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 = GETARG_C(i); - mrb_int z; - - if (mrb_int_add_overflow(x, y, &z)) { - SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y); - break; - } - mrb_fixnum(regs[a]) = z; - } - break; - case MRB_TT_FLOAT: -#ifdef MRB_WORD_BOXING - { - mrb_float x = mrb_float(regs[a]); - SET_FLOAT_VALUE(mrb, regs[a], x + GETARG_C(i)); - } +#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]) += GETARG_C(i); +#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; - default: - SET_INT_VALUE(regs[a+1], GETARG_C(i)); - i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 1); - goto L_SEND; - } - NEXT; - } - CASE(OP_SUBI) { - /* A B C R(A) := R(A)-C (Syms[B]=:-)*/ - int a = GETARG_A(i); - 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 = GETARG_C(i); - mrb_int z; + CASE(OP_ADDI, BB) { + OP_MATHI(add); + } - if (mrb_int_sub_overflow(x, y, &z)) { - SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y); - } - else { - mrb_fixnum(regs_a[0]) = z; - } - } - break; - case MRB_TT_FLOAT: -#ifdef MRB_WORD_BOXING - { - mrb_float x = mrb_float(regs[a]); - SET_FLOAT_VALUE(mrb, regs[a], x - GETARG_C(i)); - } -#else - mrb_float(regs_a[0]) -= GETARG_C(i); -#endif - break; - default: - SET_INT_VALUE(regs_a[1], GETARG_C(i)); - i = MKOP_ABC(OP_SEND, a, GETARG_B(i), 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])) -#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;\ - case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\ + default:\ + c = 1;\ + mid = MRB_OPSYM(sym);\ + goto L_SEND_SYM;\ + }\ + if (result) {\ + SET_TRUE_VALUE(regs[a]);\ + }\ + else {\ + SET_FALSE_VALUE(regs[a]);\ + }\ +} while(0) +#else +#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_INTEGER,MRB_TT_INTEGER):\ + result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\ + break;\ + 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):\ result = OP_CMP_BODY(op,mrb_float,mrb_float);\ break;\ default:\ - goto L_SEND;\ + c = 1;\ + mid = MRB_OPSYM(sym);\ + goto L_SEND_SYM;\ }\ if (result) {\ SET_TRUE_VALUE(regs[a]);\ @@ -2013,318 +2513,357 @@ RETRY_TRY_BLOCK: SET_FALSE_VALUE(regs[a]);\ }\ } while(0) +#endif - CASE(OP_EQ) { - /* A B C R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1)*/ - int a = GETARG_A(i); + 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) { - /* A B C R(A) := R(A)<R(A+1) (Syms[B]=:<,C=1)*/ - int a = GETARG_A(i); - OP_CMP(<); + CASE(OP_LT, B) { + OP_CMP(<,lt); NEXT; } - CASE(OP_LE) { - /* A B C R(A) := R(A)<=R(A+1) (Syms[B]=:<=,C=1)*/ - int a = GETARG_A(i); - OP_CMP(<=); + CASE(OP_LE, B) { + OP_CMP(<=,le); NEXT; } - CASE(OP_GT) { - /* A B C R(A) := R(A)>R(A+1) (Syms[B]=:>,C=1)*/ - int a = GETARG_A(i); - OP_CMP(>); + CASE(OP_GT, B) { + OP_CMP(>,gt); NEXT; } - CASE(OP_GE) { - /* A B C R(A) := R(A)>=R(A+1) (Syms[B]=:>=,C=1)*/ - int a = GETARG_A(i); - OP_CMP(>=); + CASE(OP_GE, B) { + OP_CMP(>=,ge); NEXT; } - CASE(OP_ARRAY) { - /* A B C R(A) := ary_new(R(B),R(B+1)..R(B+C)) */ - regs[GETARG_A(i)] = mrb_ary_new_from_values(mrb, GETARG_C(i), ®s[GETARG_B(i)]); - ARENA_RESTORE(mrb, ai); + CASE(OP_ARRAY, BB) { + mrb_value v = mrb_ary_new_from_values(mrb, b, ®s[a]); + regs[a] = v; + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + CASE(OP_ARRAY2, BBB) { + mrb_value v = mrb_ary_new_from_values(mrb, c, ®s[b]); + regs[a] = v; + mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_ARYCAT) { - /* A B mrb_ary_concat(R(A),R(B)) */ - mrb_ary_concat(mrb, regs[GETARG_A(i)], - mrb_ary_splat(mrb, regs[GETARG_B(i)])); - ARENA_RESTORE(mrb, ai); + CASE(OP_ARYCAT, B) { + mrb_value splat = mrb_ary_splat(mrb, regs[a+1]); + if (mrb_nil_p(regs[a])) { + regs[a] = splat; + } + else { + mrb_ary_concat(mrb, regs[a], splat); + } + mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_ARYPUSH) { - /* A B R(A).push(R(B)) */ - mrb_ary_push(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]); + CASE(OP_ARYPUSH, B) { + mrb_ary_push(mrb, regs[a], regs[a+1]); + NEXT; + } + + CASE(OP_ARYDUP, B) { + mrb_value ary = regs[a]; + if (mrb_array_p(ary)) { + ary = mrb_ary_new_from_values(mrb, RARRAY_LEN(ary), RARRAY_PTR(ary)); + } + else { + ary = mrb_ary_new_from_values(mrb, 1, &ary); + } + regs[a] = ary; NEXT; } - CASE(OP_AREF) { - /* A B C R(A) := R(B)[C] */ - int a = GETARG_A(i); - int c = GETARG_C(i); - mrb_value v = regs[GETARG_B(i)]; + CASE(OP_AREF, BBB) { + mrb_value v = regs[b]; if (!mrb_array_p(v)) { if (c == 0) { - regs[GETARG_A(i)] = v; + regs[a] = v; } else { SET_NIL_VALUE(regs[a]); } } else { - regs[GETARG_A(i)] = mrb_ary_ref(mrb, v, c); + v = mrb_ary_ref(mrb, v, c); + regs[a] = v; } NEXT; } - CASE(OP_ASET) { - /* A B C R(B)[C] := R(A) */ - mrb_ary_set(mrb, regs[GETARG_B(i)], GETARG_C(i), regs[GETARG_A(i)]); + CASE(OP_ASET, BBB) { + mrb_ary_set(mrb, regs[b], c, regs[a]); NEXT; } - CASE(OP_APOST) { - /* A B C *R(A),R(A+1)..R(A+C) := R(A) */ - int a = GETARG_A(i); + CASE(OP_APOST, BBB) { mrb_value v = regs[a]; - int pre = GETARG_B(i); - int post = GETARG_C(i); + int pre = b; + int post = c; + struct RArray *ary; + int len, idx; if (!mrb_array_p(v)) { - regs[a++] = mrb_ary_new_capa(mrb, 0); + v = mrb_ary_new_from_values(mrb, 1, ®s[a]); + } + ary = mrb_ary_ptr(v); + len = (int)ARY_LEN(ary); + if (len > pre + post) { + v = mrb_ary_new_from_values(mrb, len - pre - post, ARY_PTR(ary)+pre); + regs[a++] = v; while (post--) { - SET_NIL_VALUE(regs[a]); - a++; + regs[a++] = ARY_PTR(ary)[len-post-1]; } } else { - struct RArray *ary = mrb_ary_ptr(v); - int len = ary->len; - int idx; - - if (len > pre + post) { - regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre); - while (post--) { - regs[a++] = ary->ptr[len-post-1]; - } + v = mrb_ary_new_capa(mrb, 0); + regs[a++] = v; + for (idx=0; idx+pre<len; idx++) { + regs[a+idx] = ARY_PTR(ary)[pre+idx]; } - else { - regs[a++] = mrb_ary_new_capa(mrb, 0); - for (idx=0; idx+pre<len; idx++) { - regs[a+idx] = ary->ptr[pre+idx]; - } - while (idx < post) { - SET_NIL_VALUE(regs[a+idx]); - idx++; - } + while (idx < post) { + SET_NIL_VALUE(regs[a+idx]); + idx++; } } - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_STRING) { - /* A Bx R(A) := str_new(Lit(Bx)) */ - regs[GETARG_A(i)] = mrb_str_dup(mrb, pool[GETARG_Bx(i)]); - ARENA_RESTORE(mrb, ai); + CASE(OP_INTERN, B) { + mrb_sym sym = mrb_intern_str(mrb, regs[a]); + + regs[a] = mrb_symbol_value(sym); + mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_STRCAT) { - /* A B R(A).concat(R(B)) */ - mrb_str_concat(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]); + CASE(OP_STRING, BB) { + size_t len; + + 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; + } + + CASE(OP_STRCAT, B) { + mrb_str_concat(mrb, regs[a], regs[a+1]); + NEXT; + } + + CASE(OP_HASH, BB) { + mrb_value hash = mrb_hash_new_capa(mrb, b); + int i; + int lim = a+b*2; + + for (i=a; i<lim; i+=2) { + mrb_hash_set(mrb, hash, regs[i], regs[i+1]); + } + regs[a] = hash; + mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_HASH) { - /* A B C R(A) := hash_new(R(B),R(B+1)..R(B+C)) */ - int b = GETARG_B(i); - int c = GETARG_C(i); - int lim = b+c*2; - mrb_value hash = mrb_hash_new_capa(mrb, c); + CASE(OP_HASHADD, BB) { + mrb_value hash; + int i; + int lim = a+b*2+1; - while (b < lim) { - mrb_hash_set(mrb, hash, regs[b], regs[b+1]); - b+=2; + hash = mrb_ensure_hash_type(mrb, regs[a]); + for (i=a+1; i<lim; i+=2) { + mrb_hash_set(mrb, hash, regs[i], regs[i+1]); } - regs[GETARG_A(i)] = hash; - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } + CASE(OP_HASHCAT, B) { + mrb_value hash = mrb_ensure_hash_type(mrb, regs[a]); - CASE(OP_LAMBDA) { - /* A b c R(A) := lambda(SEQ[b],c) (b:c = 14:2) */ + mrb_hash_merge(mrb, hash, regs[a+1]); + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_LAMBDA, BB) + c = OP_L_LAMBDA; + L_MAKE_LAMBDA: + { struct RProc *p; - int c = GETARG_c(i); + const mrb_irep *nirep = irep->reps[b]; if (c & OP_L_CAPTURE) { - p = mrb_closure_new(mrb, irep->reps[GETARG_b(i)]); + p = mrb_closure_new(mrb, nirep); } else { - p = mrb_proc_new(mrb, irep->reps[GETARG_b(i)]); - if (c & OP_L_METHOD) { - if (p->target_class->tt == MRB_TT_SCLASS) { - mrb_value klass; - klass = mrb_obj_iv_get(mrb, - (struct RObject *)p->target_class, - mrb_intern_lit(mrb, "__attached__")); - p->target_class = mrb_class_ptr(klass); - } - } + p = mrb_proc_new(mrb, nirep); + p->flags |= MRB_PROC_SCOPE; } if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT; - regs[GETARG_A(i)] = mrb_obj_value(p); - ARENA_RESTORE(mrb, ai); + if (c == OP_L_METHOD) proc_adjust_upper(p); + regs[a] = mrb_obj_value(p); + mrb_gc_arena_restore(mrb, ai); NEXT; } + CASE(OP_BLOCK, BB) { + c = OP_L_BLOCK; + goto L_MAKE_LAMBDA; + } + CASE(OP_METHOD, BB) { + c = OP_L_METHOD; + goto L_MAKE_LAMBDA; + } - CASE(OP_OCLASS) { - /* A R(A) := ::Object */ - regs[GETARG_A(i)] = mrb_obj_value(mrb->object_class); + CASE(OP_RANGE_INC, B) { + mrb_value val = mrb_range_new(mrb, regs[a], regs[a+1], FALSE); + regs[a] = val; + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_RANGE_EXC, B) { + mrb_value val = mrb_range_new(mrb, regs[a], regs[a+1], TRUE); + regs[a] = val; + mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_CLASS) { - /* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */ - struct RClass *c = 0; - int a = GETARG_A(i); + CASE(OP_OCLASS, B) { + regs[a] = mrb_obj_value(mrb->object_class); + NEXT; + } + + CASE(OP_CLASS, BB) { + struct RClass *c = 0, *baseclass; mrb_value base, super; - mrb_sym id = syms[GETARG_B(i)]; + mrb_sym id = syms[b]; base = regs[a]; super = regs[a+1]; if (mrb_nil_p(base)) { - base = mrb_obj_value(mrb->c->ci->target_class); + 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); regs[a] = mrb_obj_value(c); - ARENA_RESTORE(mrb, ai); + mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_MODULE) { - /* A B R(A) := newmodule(R(A),Syms(B)) */ - struct RClass *c = 0; - int a = GETARG_A(i); + CASE(OP_MODULE, BB) { + struct RClass *cls = 0, *baseclass; mrb_value base; - mrb_sym id = syms[GETARG_B(i)]; + mrb_sym id = syms[b]; base = regs[a]; if (mrb_nil_p(base)) { - base = mrb_obj_value(mrb->c->ci->target_class); + baseclass = MRB_PROC_TARGET_CLASS(mrb->c->ci->proc); + if (!baseclass) baseclass = mrb->object_class; + base = mrb_obj_value(baseclass); } - c = mrb_vm_define_module(mrb, base, id); - regs[a] = mrb_obj_value(c); - ARENA_RESTORE(mrb, ai); + cls = mrb_vm_define_module(mrb, base, id); + regs[a] = mrb_obj_value(cls); + mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_EXEC) { - /* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */ - int a = GETARG_A(i); - mrb_callinfo *ci; + CASE(OP_EXEC, BB) + { mrb_value recv = regs[a]; struct RProc *p; - - /* prepare stack */ - ci = cipush(mrb); - ci->pc = pc + 1; - 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; - - p = mrb_proc_new(mrb, irep->reps[GETARG_Bx(i)]); - p->target_class = ci->target_class; - ci->proc = p; - - if (MRB_PROC_CFUNC_P(p)) { - ci->nregs = 0; - mrb->c->stack[0] = p->body.func(mrb, recv); - mrb_gc_arena_restore(mrb, ai); - if (mrb->exc) goto L_RAISE; - /* pop stackpos */ - regs = mrb->c->stack = mrb->c->ci->stackent; - cipop(mrb); - NEXT; - } - else { - irep = p->body.irep; - pool = irep->pool; - syms = irep->syms; - stack_extend(mrb, irep->nregs, 1); - ci->nregs = irep->nregs; - regs = mrb->c->stack; - pc = irep->iseq; - JUMP; - } + const mrb_irep *nirep = irep->reps[b]; + + /* prepare closure */ + p = mrb_proc_new(mrb, nirep); + p->c = NULL; + 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 */ + cipush(mrb, a, a, mrb_class_ptr(recv), p, 0, 0); + + irep = p->body.irep; + pool = irep->pool; + syms = irep->syms; + mrb_stack_extend(mrb, irep->nregs); + stack_clear(regs+1, irep->nregs-1); + pc = irep->iseq; + JUMP; } - CASE(OP_METHOD) { - /* A B R(A).newmethod(Syms(B),R(A+1)) */ - int a = GETARG_A(i); - struct RClass *c = mrb_class_ptr(regs[a]); + CASE(OP_DEF, BB) { + 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_define_method_vm(mrb, c, syms[GETARG_B(i)], regs[a+1]); - ARENA_RESTORE(mrb, ai); + MRB_METHOD_FROM_PROC(m, p); + 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; } - CASE(OP_SCLASS) { - /* A B R(A) := R(B).singleton_class */ - regs[GETARG_A(i)] = mrb_singleton_class(mrb, regs[GETARG_B(i)]); - ARENA_RESTORE(mrb, ai); + CASE(OP_SCLASS, B) { + regs[a] = mrb_singleton_class(mrb, regs[a]); + mrb_gc_arena_restore(mrb, ai); NEXT; } - CASE(OP_TCLASS) { - /* A R(A) := target_class */ - if (!mrb->c->ci->target_class) { - mrb_value exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, "no target class or module"); - mrb->exc = mrb_obj_ptr(exc); - goto L_RAISE; - } - regs[GETARG_A(i)] = mrb_obj_value(mrb->c->ci->target_class); + CASE(OP_TCLASS, B) { + if (!check_target_class(mrb)) goto L_RAISE; + regs[a] = mrb_obj_value(mrb_vm_ci_target_class(mrb->c->ci)); NEXT; } - CASE(OP_RANGE) { - /* A B C R(A) := range_new(R(B),R(B+1),C) */ - int b = GETARG_B(i); - regs[GETARG_A(i)] = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i)); - ARENA_RESTORE(mrb, ai); + CASE(OP_ALIAS, BB) { + struct RClass *target; + + if (!check_target_class(mrb)) goto L_RAISE; + target = mrb_vm_ci_target_class(mrb->c->ci); + mrb_alias_method(mrb, target, syms[a], syms[b]); + 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_vm_ci_target_class(mrb->c->ci); + mrb_undef_method_id(mrb, target, syms[a]); NEXT; } - CASE(OP_DEBUG) { - /* A B C debug print R(A),R(B),R(C) */ -#ifdef ENABLE_DEBUG + CASE(OP_DEBUG, Z) { + FETCH_BBB(); +#ifdef MRB_USE_DEBUG_HOOK mrb->debug_op_hook(mrb, irep, pc, regs); #else -#ifdef ENABLE_STDIO - printf("OP_DEBUG %d %d %d\n", GETARG_A(i), GETARG_B(i), GETARG_C(i)); +#ifndef MRB_NO_STDIO + printf("OP_DEBUG %d %d %d\n", a, b, c); #else abort(); #endif @@ -2332,76 +2871,119 @@ RETRY_TRY_BLOCK: NEXT; } - CASE(OP_STOP) { + CASE(OP_ERR, B) { + size_t len = pool[a].tt >> 2; + mrb_value exc; + + 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(); mrb->c->ci->pc = pc; goto L_OP_ ## insn ## _BODY; +#include "mruby/ops.h" +#undef OPCODE + } + pc--; + NEXT; + } + CASE(OP_EXT2, Z) { + insn = READ_B(); + switch (insn) { +#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 + } + pc--; + NEXT; + } + CASE(OP_EXT3, Z) { + uint8_t insn = READ_B(); + switch (insn) { +#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 + } + pc--; + NEXT; + } + + CASE(OP_STOP, Z) { /* stop VM */ - L_STOP: - { - int eidx_stop = mrb->c->ci == mrb->c->cibase ? 0 : mrb->c->ci[-1].eidx; - int eidx = mrb->c->ci->eidx; - while (eidx > eidx_stop) { - ecall(mrb, --eidx); - } + CHECKPOINT_RESTORE(RBREAK_TAG_STOP) { + /* do nothing */ } - 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]; } - - CASE(OP_ERR) { - /* Bx raise RuntimeError with message Lit(Bx) */ - mrb_value msg = mrb_str_dup(mrb, pool[GETARG_Bx(i)]); - mrb_value exc; - - if (GETARG_A(i) == 0) { - exc = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, msg); - } - else { - exc = mrb_exc_new_str(mrb, E_LOCALJUMP_ERROR, msg); - } - mrb->exc = mrb_obj_ptr(exc); - goto L_RAISE; - } } END_DISPATCH; - +#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) { - return mrb_context_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */ + if (mrb->c->ci->argc < 0) { + return mrb_vm_run(mrb, proc, self, 3); /* receiver, args and block) */ + } + else { + return mrb_vm_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */ + } } MRB_API mrb_value -mrb_toplevel_run_keep(mrb_state *mrb, struct RProc *proc, 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 || mrb->c->ci == mrb->c->cibase) { - return mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep); + if (!mrb->c->cibase) { + return mrb_vm_run(mrb, proc, self, stack_keep); } - ci = cipush(mrb); - ci->nregs = 1; /* protect the receiver */ - ci->acc = CI_ACC_SKIP; - ci->target_class = mrb->object_class; - v = mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep); - cipop(mrb); + 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); + } + cipush(mrb, 0, CI_ACC_SKIP, mrb->object_class, NULL, 0, 0); + v = mrb_vm_run(mrb, proc, self, stack_keep); return v; } -MRB_API mrb_value -mrb_toplevel_run(mrb_state *mrb, struct RProc *proc) -{ - return mrb_toplevel_run_keep(mrb, proc, 0); -} +#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_USE_CXX_ABI) +extern "C" { +# endif +#endif |
