diff options
Diffstat (limited to '')
34 files changed, 22412 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/deps/mruby/src/array.c b/web/server/h2o/libh2o/deps/mruby/src/array.c new file mode 100644 index 000000000..8f33defe6 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/array.c @@ -0,0 +1,1249 @@ +/* +** array.c - Array class +** +** 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 "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 ((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; + size_t blen; + + if (capa > ARY_MAX_SIZE) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + } + blen = capa * sizeof(mrb_value); + + a = (struct RArray*)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; +} + +MRB_API mrb_value +mrb_ary_new_capa(mrb_state *mrb, mrb_int capa) +{ + struct RArray *a = ary_new_capa(mrb, capa); + return mrb_obj_value(a); +} + +MRB_API mrb_value +mrb_ary_new(mrb_state *mrb) +{ + return mrb_ary_new_capa(mrb, 0); +} + +/* + * 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 + * * gcc on MIPS also fail + * http://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 + * + */ +static inline void +array_copy(mrb_value *dst, const mrb_value *src, mrb_int size) +{ + mrb_int i; + + for (i = 0; i < size; i++) { + dst[i] = src[i]; + } +} + +MRB_API mrb_value +mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) +{ + struct RArray *a = ary_new_capa(mrb, size); + + array_copy(ARY_PTR(a), vals, size); + ARY_SET_LEN(a, size); + + return mrb_obj_value(a); +} + +MRB_API mrb_value +mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr) +{ + struct RArray *a; + + a = ary_new_capa(mrb, 2); + ARY_PTR(a)[0] = car; + ARY_PTR(a)[1] = cdr; + ARY_SET_LEN(a, 2); + return mrb_obj_value(a); +} + +static void +ary_fill_with_nil(mrb_value *ptr, mrb_int size) +{ + mrb_value nil = mrb_nil_value(); + + while (size--) { + *ptr++ = nil; + } +} + +static void +ary_modify_check(mrb_state *mrb, struct RArray *a) +{ + if (MRB_FROZEN_P(a)) { + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen array"); + } +} + +static void +ary_modify(mrb_state *mrb, struct RArray *a) +{ + ary_modify_check(mrb, a); + + if (ARY_SHARED_P(a)) { + mrb_shared_array *shared = a->as.heap.aux.shared; + + 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->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->as.heap.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); + } +} + +MRB_API void +mrb_ary_modify(mrb_state *mrb, struct RArray* a) +{ + mrb_write_barrier(mrb, (struct RBasic*)a); + ary_modify(mrb, a); +} + +static void +ary_make_shared(mrb_state *mrb, struct RArray *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->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 = ptr; + } + shared->len = len; + a->as.heap.aux.shared = shared; + ARY_SET_SHARED_FLAG(a); + } +} + +static void +ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len) +{ + mrb_int capa = ARY_CAPA(a); + + if (len > ARY_MAX_SIZE || len < 0) { + size_error: + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + } + + if (capa < ARY_DEFAULT_LEN) { + capa = ARY_DEFAULT_LEN; + } + while (capa < len) { + if (capa <= ARY_MAX_SIZE / 2) { + capa *= 2; + } + else { + capa = len; + } + } + if (capa < len || capa > ARY_MAX_SIZE) { + goto size_error; + } + + 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); + + ARY_UNSET_EMBED_FLAG(a); + array_copy(expanded_ptr, ptr, len); + a->as.heap.len = len; + a->as.heap.aux.capa = capa; + a->as.heap.ptr = expanded_ptr; + } + else if (capa > a->as.heap.aux.capa) { + mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->as.heap.ptr, sizeof(mrb_value)*capa); + + a->as.heap.aux.capa = capa; + a->as.heap.ptr = expanded_ptr; + } +} + +static void +ary_shrink_capa(mrb_state *mrb, struct RArray *a) +{ + + mrb_int capa; + + if (ARY_EMBED_P(a)) return; + + capa = a->as.heap.aux.capa; + if (capa < ARY_DEFAULT_LEN * 2) return; + if (capa <= a->as.heap.len * ARY_SHRINK_RATIO) return; + + do { + capa /= 2; + if (capa < ARY_DEFAULT_LEN) { + capa = ARY_DEFAULT_LEN; + break; + } + } while (capa > a->as.heap.len * ARY_SHRINK_RATIO); + + 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); + } +} + +MRB_API mrb_value +mrb_ary_resize(mrb_state *mrb, mrb_value ary, mrb_int new_len) +{ + mrb_int old_len; + struct RArray *a = mrb_ary_ptr(ary); + + ary_modify(mrb, a); + old_len = RARRAY_LEN(ary); + if (old_len != new_len) { + ARY_SET_LEN(a, new_len); + if (new_len < old_len) { + ary_shrink_capa(mrb, a); + } + else { + ary_expand_capa(mrb, a, new_len); + ary_fill_with_nil(ARY_PTR(a) + old_len, new_len - old_len); + } + } + + return ary; +} + +static mrb_value +mrb_ary_s_create(mrb_state *mrb, mrb_value klass) +{ + mrb_value ary; + mrb_value *vals; + mrb_int len; + struct RArray *a; + + 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 ary; +} + +static void +ary_concat(mrb_state *mrb, struct RArray *a, struct RArray *a2) +{ + mrb_int len; + + 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 (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); + ARY_SET_LEN(a, len); +} + +MRB_API void +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); +} + +static mrb_value +mrb_ary_concat_m(mrb_state *mrb, mrb_value self) +{ + mrb_value ary; + + mrb_get_args(mrb, "A", &ary); + mrb_ary_concat(mrb, self, ary); + return self; +} + +static mrb_value +mrb_ary_plus(mrb_state *mrb, mrb_value self) +{ + struct RArray *a1 = mrb_ary_ptr(self); + struct RArray *a2; + mrb_value *ptr; + mrb_int blen, len1; + + mrb_get_args(mrb, "a", &ptr, &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 mrb_obj_value(a2); +} + +static void +ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len) +{ + ary_modify(mrb, a); + if (ARY_CAPA(a) < len) + ary_expand_capa(mrb, a, len); + array_copy(ARY_PTR(a), argv, len); + mrb_write_barrier(mrb, (struct RBasic*)a); + 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); + + if (a1 != a2) { + ary_replace(mrb, a1, ARY_PTR(a2), ARY_LEN(a2)); + } +} + +static mrb_value +mrb_ary_replace_m(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + + mrb_get_args(mrb, "A", &other); + mrb_ary_replace(mrb, self, other); + + return self; +} + +static mrb_value +mrb_ary_times(mrb_state *mrb, mrb_value self) +{ + struct RArray *a1 = mrb_ary_ptr(self); + struct RArray *a2; + mrb_value *ptr; + mrb_int times, len1; + + mrb_get_args(mrb, "i", ×); + if (times < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); + } + if (times == 0) return mrb_ary_new(mrb); + if (ARY_MAX_SIZE / times < 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, ARY_PTR(a1), len1); + ptr += len1; + } + + 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 (len > 1) { + mrb_value *p1, *p2; + + ary_modify(mrb, a); + p1 = ARY_PTR(a); + p2 = p1 + len - 1; + + while (p1 < p2) { + mrb_value tmp = *p1; + *p1++ = *p2; + *p2-- = tmp; + } + } + return self; +} + +static mrb_value +mrb_ary_reverse(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, ARY_LEN(a)); + mrb_int len = ARY_LEN(a); + + if (len > 0) { + mrb_value *p1, *p2, *e; + + p1 = ARY_PTR(a); + e = p1 + len; + p2 = ARY_PTR(b) + len - 1; + while (p1 < e) { + *p2-- = *p1++; + } + ARY_SET_LEN(b, len); + } + 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 (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, len2, alen; + struct RArray *a; + + mrb_get_args(mrb, "*!", &argv, &alen); + a = mrb_ary_ptr(self); + ary_modify(mrb, a); + len = ARY_LEN(a); + len2 = len + alen; + if (ARY_CAPA(a) < len2) { + ary_expand_capa(mrb, a, len2); + } + array_copy(ARY_PTR(a)+len, argv, alen); + ARY_SET_LEN(a, len2); + mrb_write_barrier(mrb, (struct RBasic*)a); + + return self; +} + +MRB_API mrb_value +mrb_ary_pop(mrb_state *mrb, mrb_value ary) +{ + struct RArray *a = mrb_ary_ptr(ary); + mrb_int len = ARY_LEN(a); + + ary_modify_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 + +MRB_API mrb_value +mrb_ary_shift(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + mrb_int len = ARY_LEN(a); + mrb_value val; + + ary_modify_check(mrb, a); + if (len == 0) return mrb_nil_value(); + if (ARY_SHARED_P(a)) { + L_SHIFT: + val = a->as.heap.ptr[0]; + a->as.heap.ptr++; + a->as.heap.len--; + return val; + } + if (len > ARY_SHIFT_SHARED_MIN) { + ary_make_shared(mrb, a); + goto L_SHIFT; + } + else { + mrb_value *ptr = ARY_PTR(a); + mrb_int size = len; + + val = *ptr; + while (--size) { + *ptr = *(ptr+1); + ++ptr; + } + ARY_SET_LEN(a, len-1); + } + return val; +} + +/* self = [1,2,3] + item = 0 + self.unshift item + p self #=> [0, 1, 2, 3] */ +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->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 (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; + } + ARY_SET_LEN(a, len+1); + mrb_field_write_barrier_value(mrb, (struct RBasic*)a, item); + + return self; +} + +static mrb_value +mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + mrb_value *vals, *ptr; + mrb_int alen, 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->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 { + ary_modify(mrb, a); + if (ARY_CAPA(a) < len + alen) + ary_expand_capa(mrb, a, len + alen); + ptr = ARY_PTR(a); + value_move(ptr + alen, ptr, len); + } + 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); + mrb_int len = ARY_LEN(a); + + /* range check */ + if (n < 0) n += len; + if (n < 0 || len <= n) return mrb_nil_value(); + + return ARY_PTR(a)[n]; +} + +MRB_API void +mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val) +{ + struct RArray *a = mrb_ary_ptr(ary); + mrb_int len = ARY_LEN(a); + + ary_modify(mrb, a); + /* range check */ + if (n < 0) { + n += len; + if (n < 0) { + mrb_raisef(mrb, E_INDEX_ERROR, "index %S out of array", mrb_fixnum_value(n - len)); + } + } + if (len <= n) { + if (ARY_CAPA(a) <= n) + ary_expand_capa(mrb, a, n + 1); + ary_fill_with_nil(ARY_PTR(a) + len, n + 1 - len); + ARY_SET_LEN(a, n+1); + } + + 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) +{ + mrb_int len = ARY_LEN(a); + struct RArray *d = ary_new_capa(mrb, len); + + ary_replace(mrb, d, ARY_PTR(a), len); + return d; +} + +MRB_API mrb_value +mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl) +{ + struct RArray *a = mrb_ary_ptr(ary); + mrb_int alen = ARY_LEN(a); + const mrb_value *argv; + mrb_int argc; + 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)); + + /* range check */ + if (head < 0) { + head += alen; + if (head < 0) { + mrb_raise(mrb, E_INDEX_ERROR, "index is out of array"); + } + } + 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 { + argc = 1; + argv = &rpl; + } + if (head >= alen) { + if (head > ARY_MAX_SIZE - argc) { + mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(head)); + } + 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; + + if (alen - len > ARY_MAX_SIZE - argc) { + mrb_raisef(mrb, E_INDEX_ERROR, "index %S too big", mrb_fixnum_value(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; +} + +void +mrb_ary_decref(mrb_state *mrb, mrb_shared_array *shared) +{ + shared->refcnt--; + if (shared->refcnt == 0) { + mrb_free(mrb, shared->ptr); + mrb_free(mrb, shared); + } +} + +static mrb_value +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->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); +} + +static mrb_int +aget_index(mrb_state *mrb, mrb_value index) +{ + if (mrb_fixnum_p(index)) { + return mrb_fixnum(index); + } + else if (mrb_float_p(index)) { + return (mrb_int)mrb_float(index); + } + else { + mrb_int i, argc; + mrb_value *argv; + + mrb_get_args(mrb, "i*!", &i, &argv, &argc); + return i; + } +} + +/* + * call-seq: + * ary[index] -> obj or nil + * ary[start, length] -> new_ary or nil + * ary[range] -> new_ary or nil + * ary.slice(index) -> obj or nil + * ary.slice(start, length) -> new_ary or nil + * ary.slice(range) -> new_ary or nil + * + * Element Reference --- Returns the element at +index+, or returns a + * subarray starting at the +start+ index and continuing for +length+ + * elements, or returns a subarray specified by +range+ of indices. + * + * Negative indices count backward from the end of the array (-1 is the last + * element). For +start+ and +range+ cases the starting index is just before + * an element. Additionally, an empty array is returned when the starting + * index for an element range is at the end of the array. + * + * Returns +nil+ if the index (or starting index) are out of range. + * + * a = [ "a", "b", "c", "d", "e" ] + * a[1] => "b" + * a[1,2] => ["b", "c"] + * a[1..-2] => ["b", "c", "d"] + * + */ + +static mrb_value +mrb_ary_aget(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + mrb_int i, len, alen = ARY_LEN(a); + mrb_value index; + + if (mrb_get_args(mrb, "o|i", &index, &len) == 1) { + switch (mrb_type(index)) { + /* a[n..m] */ + case MRB_TT_RANGE: + if (mrb_range_beg_len(mrb, index, &i, &len, alen, TRUE) == 1) { + 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)); + default: + return mrb_ary_ref(mrb, self, aget_index(mrb, index)); + } + } + + i = aget_index(mrb, index); + if (i < 0) i += alen; + if (i < 0 || alen < i) return mrb_nil_value(); + if (len < 0) return mrb_nil_value(); + if (alen == i) return mrb_ary_new(mrb); + if (len > alen - i) len = alen - i; + + return ary_subseq(mrb, a, i, len); +} + +/* + * call-seq: + * ary[index] = obj -> obj + * ary[start, length] = obj or other_ary or nil -> obj or other_ary or nil + * ary[range] = obj or other_ary or nil -> obj or other_ary or nil + * + * Element Assignment --- Sets the element at +index+, or replaces a subarray + * from the +start+ index for +length+ elements, or replaces a subarray + * specified by the +range+ of indices. + * + * If indices are greater than the current capacity of the array, the array + * grows automatically. Elements are inserted into the array at +start+ if + * +length+ is zero. + * + * Negative indices will count backward from the end of the array. For + * +start+ and +range+ cases the starting index is just before an element. + * + * An IndexError is raised if a negative index points past the beginning of + * the array. + * + * See also Array#push, and Array#unshift. + * + * a = Array.new + * a[4] = "4"; #=> [nil, nil, nil, nil, "4"] + * a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"] + * a[1..2] = [ 1, 2 ] #=> ["a", 1, 2, nil, "4"] + * a[0, 2] = "?" #=> ["?", 2, nil, "4"] + * a[0..2] = "A" #=> ["A", "4"] + * a[-1] = "Z" #=> ["A", "Z"] + * a[1..-1] = nil #=> ["A", nil] + * a[1..-1] = [] #=> ["A"] + * a[0, 0] = [ 1, 2 ] #=> [1, 2, "A"] + * a[3, 0] = "B" #=> [1, 2, "A", "B"] + */ + +static mrb_value +mrb_ary_aset(mrb_state *mrb, mrb_value self) +{ + mrb_value v1, v2, v3; + mrb_int i, len; + + mrb_ary_modify(mrb, mrb_ary_ptr(self)); + if (mrb_get_args(mrb, "oo|o", &v1, &v2, &v3) == 2) { + /* a[n..m] = v */ + switch (mrb_range_beg_len(mrb, v1, &i, &len, RARRAY_LEN(self), FALSE)) { + case 0: /* not range */ + mrb_ary_set(mrb, self, aget_index(mrb, v1), v2); + break; + case 1: /* range */ + mrb_ary_splice(mrb, self, i, len, v2); + break; + case 2: /* out of range */ + mrb_raisef(mrb, E_RANGE_ERROR, "%S out of range", v1); + break; + } + return v2; + } + + /* a[n,m] = v */ + mrb_ary_splice(mrb, self, aget_index(mrb, v1), aget_index(mrb, v2), v3); + return v3; +} + +static mrb_value +mrb_ary_delete_at(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + mrb_int index; + mrb_value val; + mrb_value *ptr; + mrb_int len, alen = ARY_LEN(a); + + mrb_get_args(mrb, "i", &index); + if (index < 0) index += alen; + if (index < 0 || alen <= index) return mrb_nil_value(); + + ary_modify(mrb, a); + ptr = ARY_PTR(a); + val = ptr[index]; + + ptr += index; + len = alen - index; + while (--len) { + *ptr = *(ptr+1); + ++ptr; + } + ARY_SET_LEN(a, alen-1); + + ary_shrink_capa(mrb, a); + + return val; +} + +static mrb_value +mrb_ary_first(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + mrb_int size, alen = ARY_LEN(a); + + if (mrb->c->ci->argc == 0) { + return (alen > 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 > alen) size = alen; + if (ARY_SHARED_P(a)) { + return ary_subseq(mrb, a, 0, size); + } + 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, alen = ARY_LEN(a); + + if (mrb_get_args(mrb, "|i", &size) == 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 > alen) size = alen; + if (ARY_SHARED_P(a) || size > ARY_DEFAULT_LEN) { + return ary_subseq(mrb, a, alen - size, 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_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); + } + } + return mrb_nil_value(); +} + +static mrb_value +mrb_ary_rindex_m(mrb_state *mrb, mrb_value self) +{ + mrb_value obj; + 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(); +} + +MRB_API mrb_value +mrb_ary_splat(mrb_state *mrb, mrb_value v) +{ + mrb_value a, recv_class; + + if (mrb_array_p(v)) { + return v; + } + + if (!mrb_respond_to(mrb, v, mrb_intern_lit(mrb, "to_a"))) { + return mrb_ary_new_from_values(mrb, 1, &v); + } + + a = mrb_funcall(mrb, v, "to_a", 0); + if (mrb_array_p(a)) { + return a; + } + else if (mrb_nil_p(a)) { + return mrb_ary_new_from_values(mrb, 1, &v); + } + else { + recv_class = mrb_obj_value(mrb_obj_class(mrb, v)); + mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S to Array (%S#to_a gives %S)", + recv_class, + recv_class, + mrb_obj_value(mrb_obj_class(mrb, a)) + ); + /* not reached */ + return mrb_undef_value(); + } +} + +static mrb_value +mrb_ary_size(mrb_state *mrb, mrb_value self) +{ + struct RArray *a = mrb_ary_ptr(self); + + return mrb_fixnum_value(ARY_LEN(a)); +} + +MRB_API mrb_value +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->as.heap.aux.shared); + ARY_UNSET_SHARED_FLAG(a); + } + else if (!ARY_EMBED_P(a)){ + mrb_free(mrb, a->as.heap.ptr); + } + ARY_SET_EMBED_LEN(a, 0); + + return 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(ARY_LEN(a) == 0); +} + +MRB_API mrb_value +mrb_check_array_type(mrb_state *mrb, mrb_value ary) +{ + return mrb_check_convert_type(mrb, ary, MRB_TT_ARRAY, "Array", "to_ary"); +} + +MRB_API mrb_value +mrb_ary_entry(mrb_value ary, mrb_int offset) +{ + if (offset < 0) { + offset += RARRAY_LEN(ary); + } + if (offset < 0 || RARRAY_LEN(ary) <= offset) { + return mrb_nil_value(); + } + return RARRAY_PTR(ary)[offset]; +} + +static mrb_value +join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list) +{ + mrb_int i; + mrb_value result, val, tmp; + + /* check recursive */ + for (i=0; i<RARRAY_LEN(list); i++) { + if (mrb_obj_equal(mrb, ary, RARRAY_PTR(list)[i])) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "recursive array join"); + } + } + + mrb_ary_push(mrb, list, ary); + + result = mrb_str_new_capa(mrb, 64); + + for (i=0; i<RARRAY_LEN(ary); i++) { + if (i > 0 && !mrb_nil_p(sep)) { + mrb_str_cat_str(mrb, result, sep); + } + + val = RARRAY_PTR(ary)[i]; + switch (mrb_type(val)) { + case MRB_TT_ARRAY: + ary_join: + val = join_ary(mrb, val, sep, list); + /* fall through */ + + case MRB_TT_STRING: + str_join: + mrb_str_cat_str(mrb, result, val); + break; + + default: + 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_convert_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary"); + if (!mrb_nil_p(tmp)) { + val = tmp; + goto ary_join; + } + } + val = mrb_obj_as_string(mrb, val); + goto str_join; + } + } + + mrb_ary_pop(mrb, list); + + return result; +} + +MRB_API mrb_value +mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep) +{ + if (!mrb_nil_p(sep)) { + sep = mrb_obj_as_string(mrb, sep); + } + return join_ary(mrb, ary, sep, mrb_ary_new(mrb)); +} + +/* + * call-seq: + * ary.join(sep="") -> str + * + * Returns a string created by converting each element of the array to + * a string, separated by <i>sep</i>. + * + * [ "a", "b", "c" ].join #=> "abc" + * [ "a", "b", "c" ].join("-") #=> "a-b-c" + */ + +static mrb_value +mrb_ary_join_m(mrb_state *mrb, mrb_value ary) +{ + mrb_value sep = mrb_nil_value(); + + 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_get_args(mrb, "o", &ary2); + if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value(); + if (!mrb_array_p(ary2)) { + return mrb_false_value(); + } + if (RARRAY_LEN(ary1) != RARRAY_LEN(ary2)) return mrb_false_value(); + + return ary2; +} + +static mrb_value +mrb_ary_cmp(mrb_state *mrb, mrb_value ary1) +{ + mrb_value ary2; + + mrb_get_args(mrb, "o", &ary2); + if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0); + if (!mrb_array_p(ary2)) { + return mrb_nil_value(); + } + + return ary2; +} + +void +mrb_init_array(mrb_state *mrb) +{ + struct RClass *a; + + mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */ + MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY); + + mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */ + + mrb_define_method(mrb, a, "+", mrb_ary_plus, MRB_ARGS_REQ(1)); /* 15.2.12.5.1 */ + mrb_define_method(mrb, a, "*", mrb_ary_times, MRB_ARGS_REQ(1)); /* 15.2.12.5.2 */ + mrb_define_method(mrb, a, "<<", mrb_ary_push_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.3 */ + mrb_define_method(mrb, a, "[]", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.4 */ + mrb_define_method(mrb, a, "[]=", mrb_ary_aset, MRB_ARGS_ANY()); /* 15.2.12.5.5 */ + mrb_define_method(mrb, a, "clear", mrb_ary_clear, MRB_ARGS_NONE()); /* 15.2.12.5.6 */ + mrb_define_method(mrb, a, "concat", mrb_ary_concat_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.8 */ + mrb_define_method(mrb, a, "delete_at", mrb_ary_delete_at, MRB_ARGS_REQ(1)); /* 15.2.12.5.9 */ + mrb_define_method(mrb, a, "empty?", mrb_ary_empty_p, MRB_ARGS_NONE()); /* 15.2.12.5.12 */ + mrb_define_method(mrb, a, "first", mrb_ary_first, MRB_ARGS_OPT(1)); /* 15.2.12.5.13 */ + mrb_define_method(mrb, a, "index", mrb_ary_index_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.14 */ + mrb_define_method(mrb, a, "initialize_copy", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.16 */ + mrb_define_method(mrb, a, "join", mrb_ary_join_m, MRB_ARGS_ANY()); /* 15.2.12.5.17 */ + mrb_define_method(mrb, a, "last", mrb_ary_last, MRB_ARGS_ANY()); /* 15.2.12.5.18 */ + mrb_define_method(mrb, a, "length", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.19 */ + mrb_define_method(mrb, a, "pop", mrb_ary_pop, MRB_ARGS_NONE()); /* 15.2.12.5.21 */ + mrb_define_method(mrb, a, "push", mrb_ary_push_m, MRB_ARGS_ANY()); /* 15.2.12.5.22 */ + mrb_define_method(mrb, a, "append", mrb_ary_push_m, MRB_ARGS_ANY()); + mrb_define_method(mrb, a, "replace", mrb_ary_replace_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.23 */ + mrb_define_method(mrb, a, "reverse", mrb_ary_reverse, MRB_ARGS_NONE()); /* 15.2.12.5.24 */ + mrb_define_method(mrb, a, "reverse!", mrb_ary_reverse_bang, MRB_ARGS_NONE()); /* 15.2.12.5.25 */ + mrb_define_method(mrb, a, "rindex", mrb_ary_rindex_m, MRB_ARGS_REQ(1)); /* 15.2.12.5.26 */ + mrb_define_method(mrb, a, "shift", mrb_ary_shift, MRB_ARGS_NONE()); /* 15.2.12.5.27 */ + mrb_define_method(mrb, a, "size", mrb_ary_size, MRB_ARGS_NONE()); /* 15.2.12.5.28 */ + mrb_define_method(mrb, a, "slice", mrb_ary_aget, MRB_ARGS_ANY()); /* 15.2.12.5.29 */ + mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */ + mrb_define_method(mrb, a, "prepend", mrb_ary_unshift_m, MRB_ARGS_ANY()); + + mrb_define_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 */ +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/backtrace.c b/web/server/h2o/libh2o/deps/mruby/src/backtrace.c new file mode 100644 index 000000000..3e4e1a438 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/backtrace.c @@ -0,0 +1,281 @@ +/* +** backtrace.c - +** +** See Copyright Notice in mruby.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> +#include <mruby/numeric.h> +#include <mruby/data.h> + +struct backtrace_location { + int lineno; + const char *filename; + mrb_sym method_id; +}; + +typedef void (*each_backtrace_func)(mrb_state*, struct backtrace_location*, void*); + +static const mrb_data_type bt_type = { "Backtrace", mrb_free }; + +static void +each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, mrb_code *pc0, each_backtrace_func func, void *data) +{ + ptrdiff_t i, j; + + if (ciidx >= mrb->c->ciend - mrb->c->cibase) + ciidx = 10; /* ciidx is broken... */ + + for (i=ciidx, j=0; i >= 0; i--,j++) { + struct backtrace_location loc; + mrb_callinfo *ci; + mrb_irep *irep; + mrb_code *pc; + + ci = &mrb->c->cibase[i]; + + if (!ci->proc) continue; + if (MRB_PROC_CFUNC_P(ci->proc)) continue; + + irep = ci->proc->body.irep; + if (!irep) continue; + + if (mrb->c->cibase[i].err) { + pc = mrb->c->cibase[i].err; + } + else if (i+1 <= ciidx) { + pc = mrb->c->cibase[i+1].pc - 1; + } + else { + pc = pc0; + } + loc.filename = mrb_debug_get_filename(irep, pc - irep->iseq); + loc.lineno = mrb_debug_get_line(irep, pc - irep->iseq); + + if (loc.lineno == -1) continue; + + if (!loc.filename) { + loc.filename = "(unknown)"; + } + + loc.method_id = ci->mid; + func(mrb, &loc, data); + } +} + +#ifndef MRB_DISABLE_STDIO + +static void +print_backtrace(mrb_state *mrb, mrb_value backtrace) +{ + int i, n; + FILE *stream = stderr; + + if (!mrb_array_p(backtrace)) return; + + n = RARRAY_LEN(backtrace) - 1; + if (n == 0) return; + + fprintf(stream, "trace:\n"); + for (i=0; i<n; i++) { + mrb_value entry = RARRAY_PTR(backtrace)[n-i-1]; + + if (mrb_string_p(entry)) { + fprintf(stream, "\t[%d] %.*s\n", i, (int)RSTRING_LEN(entry), RSTRING_PTR(entry)); + } + } +} + +static int +packed_bt_len(struct backtrace_location *bt, int n) +{ + int len = 0; + int i; + + for (i=0; i<n; i++) { + if (!bt[i].filename && !bt[i].lineno && !bt[i].method_id) + continue; + len++; + } + return len; +} + +static void +print_packed_backtrace(mrb_state *mrb, mrb_value packed) +{ + FILE *stream = stderr; + struct backtrace_location *bt; + int n, i; + int ai = mrb_gc_arena_save(mrb); + + bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, packed, &bt_type); + if (bt == NULL) return; + n = (mrb_int)RDATA(packed)->flags; + + if (packed_bt_len(bt, n) == 0) return; + fprintf(stream, "trace:\n"); + for (i = 0; i<n; i++) { + struct backtrace_location *entry = &bt[n-i-1]; + if (entry->filename == NULL) continue; + fprintf(stream, "\t[%d] %s:%d", i, entry->filename, entry->lineno); + if (entry->method_id != 0) { + const char *method_name; + + method_name = mrb_sym2name(mrb, entry->method_id); + fprintf(stream, ":in %s", method_name); + mrb_gc_arena_restore(mrb, ai); + } + fprintf(stream, "\n"); + } +} + +/* mrb_print_backtrace + + function to retrieve backtrace information from the last exception. +*/ + +MRB_API void +mrb_print_backtrace(mrb_state *mrb) +{ + mrb_value backtrace; + + if (!mrb->exc) { + return; + } + + backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace")); + if (mrb_nil_p(backtrace)) return; + if (mrb_array_p(backtrace)) { + print_backtrace(mrb, backtrace); + } + else { + print_packed_backtrace(mrb, backtrace); + } +} +#else + +MRB_API void +mrb_print_backtrace(mrb_state *mrb) +{ +} + +#endif + +static void +count_backtrace_i(mrb_state *mrb, + struct backtrace_location *loc, + void *data) +{ + int *lenp = (int*)data; + + if (loc->filename == NULL) return; + (*lenp)++; +} + +static void +pack_backtrace_i(mrb_state *mrb, + struct backtrace_location *loc, + void *data) +{ + struct backtrace_location **pptr = (struct backtrace_location**)data; + struct backtrace_location *ptr = *pptr; + + if (loc->filename == NULL) return; + *ptr = *loc; + *pptr = ptr+1; +} + +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, mrb->c->ci->pc, count_backtrace_i, &len); + size = len * sizeof(struct backtrace_location); + ptr = mrb_malloc(mrb, size); + if (ptr) memset(ptr, 0, size); + backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type); + backtrace->flags = (unsigned int)len; + each_backtrace(mrb, ciidx, mrb->c->ci->pc, pack_backtrace_i, &ptr); + return mrb_obj_value(backtrace); +} + +void +mrb_keep_backtrace(mrb_state *mrb, mrb_value exc) +{ + mrb_value backtrace; + int ai = mrb_gc_arena_save(mrb); + + backtrace = packed_backtrace(mrb); + mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace); + mrb_gc_arena_restore(mrb, ai); +} + +mrb_value +mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace) +{ + 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; + backtrace = mrb_ary_new_capa(mrb, n); + ai = mrb_gc_arena_save(mrb); + for (i = 0; i < n; i++) { + struct backtrace_location *entry = &bt[i]; + mrb_value btline; + + if (entry->filename == NULL) continue; + btline = mrb_format(mrb, "%S:%S", + mrb_str_new_cstr(mrb, entry->filename), + mrb_fixnum_value(entry->lineno)); + if (entry->method_id != 0) { + mrb_str_cat_lit(mrb, btline, ":in "); + mrb_str_cat_cstr(mrb, btline, mrb_sym2name(mrb, entry->method_id)); + } + mrb_ary_push(mrb, backtrace, btline); + mrb_gc_arena_restore(mrb, ai); + } + + return backtrace; +} + +MRB_API mrb_value +mrb_exc_backtrace(mrb_state *mrb, mrb_value exc) +{ + mrb_sym attr_name; + mrb_value backtrace; + + attr_name = mrb_intern_lit(mrb, "backtrace"); + backtrace = mrb_iv_get(mrb, exc, attr_name); + if (mrb_nil_p(backtrace) || mrb_array_p(backtrace)) { + return backtrace; + } + backtrace = mrb_unpack_backtrace(mrb, backtrace); + mrb_iv_set(mrb, exc, attr_name, backtrace); + return backtrace; +} + +MRB_API mrb_value +mrb_get_backtrace(mrb_state *mrb) +{ + return mrb_unpack_backtrace(mrb, packed_backtrace(mrb)); +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/class.c b/web/server/h2o/libh2o/deps/mruby/src/class.c new file mode 100644 index 000000000..e3888001b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/class.c @@ -0,0 +1,2474 @@ +/* +** class.c - Class class +** +** See Copyright Notice in mruby.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> +#include <mruby/istruct.h> + +KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal) + +void +mrb_gc_mark_mt(mrb_state *mrb, struct RClass *c) +{ + khiter_t k; + khash_t(mt) *h = c->mt; + + 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); + } + } + } +} + +size_t +mrb_gc_mark_mt_size(mrb_state *mrb, struct RClass *c) +{ + khash_t(mt) *h = c->mt; + + if (!h) return 0; + return kh_size(h); +} + +void +mrb_gc_free_mt(mrb_state *mrb, struct RClass *c) +{ + kh_destroy(mt, mrb, c->mt); +} + +void +mrb_class_name_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) +{ + mrb_value name; + mrb_sym nsym = mrb_intern_lit(mrb, "__classname__"); + + 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) { + mrb_obj_iv_set(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__"), + mrb_obj_value(outer)); + } + return; + } + mrb_str_cat_cstr(mrb, name, "::"); + mrb_str_cat_cstr(mrb, name, mrb_sym2name(mrb, id)); + } + mrb_obj_iv_set(mrb, (struct RObject*)c, nsym, name); +} + +static void +setup_class(mrb_state *mrb, struct RClass *outer, struct RClass *c, mrb_sym id) +{ + mrb_class_name_class(mrb, outer, c, id); + mrb_obj_iv_set(mrb, (struct RObject*)outer, id, mrb_obj_value(c)); +} + +#define make_metaclass(mrb, c) prepare_singleton_class((mrb), (struct RBasic*)(c)) + +static void +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->flags |= MRB_FLAG_IS_INHERITED; + sc->mt = kh_init(mt, mrb); + sc->iv = 0; + if (o->tt == MRB_TT_CLASS) { + c = (struct RClass*)o; + if (!c->super) { + sc->super = mrb->class_class; + } + else { + sc->super = c->super->c; + } + } + else if (o->tt == MRB_TT_SCLASS) { + c = (struct RClass*)o; + while (c->super->tt == MRB_TT_ICLASS) + c = c->super; + make_metaclass(mrb, c->super); + sc->super = c->super->c; + } + 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)); +} + +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); + + mrb_check_type(mrb, c, MRB_TT_CLASS); + return mrb_class_ptr(c); +} + +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); + + mrb_check_type(mrb, c, MRB_TT_MODULE); + return mrb_class_ptr(c); +} + +static mrb_bool +class_ptr_p(mrb_value obj) +{ + switch (mrb_type(obj)) { + case MRB_TT_CLASS: + case MRB_TT_SCLASS: + case MRB_TT_MODULE: + return TRUE; + default: + return FALSE; + } +} + +static void +check_if_class_or_module(mrb_state *mrb, mrb_value obj) +{ + if (!class_ptr_p(obj)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", mrb_inspect(mrb, obj)); + } +} + +static struct RClass* +define_module(mrb_state *mrb, mrb_sym name, struct RClass *outer) +{ + struct RClass *m; + + if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { + return module_from_sym(mrb, outer, name); + } + m = mrb_module_new(mrb); + setup_class(mrb, outer, m, name); + + return m; +} + +MRB_API struct RClass* +mrb_define_module_id(mrb_state *mrb, mrb_sym name) +{ + return define_module(mrb, name, mrb->object_class); +} + +MRB_API struct RClass* +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* +mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id) +{ + check_if_class_or_module(mrb, outer); + if (mrb_const_defined_at(mrb, outer, id)) { + mrb_value old = mrb_const_get(mrb, outer, id); + + if (mrb_type(old) != MRB_TT_MODULE) { + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a module", mrb_inspect(mrb, old)); + } + return mrb_class_ptr(old); + } + return define_module(mrb, id, mrb_class_ptr(outer)); +} + +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); + struct RClass * c = define_module(mrb, id, outer); + + setup_class(mrb, outer, c, id); + return c; +} + +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)); + } + return c; + } + + c = mrb_class_new(mrb, super); + setup_class(mrb, outer, c, name); + + return c; +} + +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)); + } + return define_class(mrb, name, super, mrb->object_class); +} + +MRB_API struct RClass* +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); +#ifdef MRB_METHOD_CACHE +static void mc_clear_all(mrb_state *mrb); +static void mc_clear_by_class(mrb_state *mrb, struct RClass*); +static void mc_clear_by_id(mrb_state *mrb, struct RClass*, mrb_sym); +#else +#define mc_clear_all(mrb) +#define mc_clear_by_class(mrb,c) +#define mc_clear_by_id(mrb,c,s) +#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; + super->flags |= MRB_FLAG_IS_INHERITED; + s = mrb_obj_value(super); + mc_clear_by_class(mrb, klass); + mid = mrb_intern_lit(mrb, "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* +mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id) +{ + struct RClass *s; + struct RClass *c; + + if (!mrb_nil_p(super)) { + if (mrb_type(super) != MRB_TT_CLASS) { + mrb_raisef(mrb, E_TYPE_ERROR, "superclass must be a Class (%S given)", + mrb_inspect(mrb, super)); + } + s = mrb_class_ptr(super); + } + else { + s = 0; + } + check_if_class_or_module(mrb, outer); + if (mrb_const_defined_at(mrb, outer, id)) { + mrb_value old = mrb_const_get(mrb, outer, id); + + if (mrb_type(old) != MRB_TT_CLASS) { + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class", mrb_inspect(mrb, old)); + } + c = mrb_class_ptr(old); + if (s) { + /* check super class */ + if (mrb_class_real(c->super) != s) { + mrb_raisef(mrb, E_TYPE_ERROR, "superclass mismatch for class %S", old); + } + } + return c; + } + c = define_class(mrb, id, s, mrb_class_ptr(outer)); + mrb_class_inherited(mrb, mrb_class_real(c->super), c); + + return c; +} + +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_API mrb_bool +mrb_class_defined_under(mrb_state *mrb, struct RClass *outer, const char *name) +{ + mrb_value sym = mrb_check_intern_cstr(mrb, name); + if (mrb_nil_p(sym)) { + return FALSE; + } + return mrb_const_defined_at(mrb, mrb_obj_value(outer), mrb_symbol(sym)); +} + +MRB_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_class_get(mrb_state *mrb, const char *name) +{ + return mrb_class_get_under(mrb, mrb->object_class, name); +} + +MRB_API struct RClass* +mrb_exc_get(mrb_state *mrb, const char *name) +{ + struct RClass *exc, *e; + mrb_value c = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), + mrb_intern_cstr(mrb, name)); + + if (mrb_type(c) != MRB_TT_CLASS) { + 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_module_get(mrb_state *mrb, const char *name) +{ + return mrb_module_get_under(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 super a class from which the new class will derive. + * NULL means \c Object class. + * \return the created class + * \throw TypeError if the constant name \a name is already taken but + * the constant is not a \c Class. + * \throw NameError if the class is already defined but the class can not + * be reopened because its superclass is not \a super. + * \post top-level constant named \a name refers the returned class. + * + * \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_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)); + } +#endif + c = define_class(mrb, id, super, outer); + setup_class(mrb, outer, c, id); + return c; +} + +MRB_API void +mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p) +{ + khash_t(mt) *h; + khiter_t k; + MRB_CLASS_ORIGIN(c); + h = c->mt; + + if (MRB_FROZEN_P(c)) { + if (c->tt == MRB_TT_MODULE) + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen module"); + else + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen class"); + } + if (!h) h = c->mt = kh_init(mt, mrb); + k = kh_put(mt, mrb, h, mid); + kh_value(h, k) = p; + if (p) { + p->c = NULL; + mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p); + } + mc_clear_by_id(mrb, c, mid); +} + +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; + 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_gc_arena_restore(mrb, ai); +} + +MRB_API void +mrb_define_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) +{ + mrb_define_method_id(mrb, c, mrb_intern_cstr(mrb, name), func, aspec); +} + +/* 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)); + } +} + +/* a function to be replacement of unimplemented method */ +MRB_API mrb_value +mrb_notimplement_m(mrb_state *mrb, mrb_value self) +{ + mrb_notimplement(mrb); + /* not reached */ + return mrb_nil_value(); +} + +static mrb_value +check_type(mrb_state *mrb, mrb_value val, enum mrb_vtype t, const char *c, const char *m) +{ + mrb_value tmp; + + tmp = mrb_check_convert_type(mrb, val, t, c, m); + if (mrb_nil_p(tmp)) { + mrb_raisef(mrb, E_TYPE_ERROR, "expected %S", mrb_str_new_cstr(mrb, c)); + } + return tmp; +} + +static mrb_value +to_str(mrb_state *mrb, mrb_value val) +{ + return check_type(mrb, val, MRB_TT_STRING, "String", "to_str"); +} + +static mrb_value +to_ary(mrb_state *mrb, mrb_value val) +{ + return check_type(mrb, val, MRB_TT_ARRAY, "Array", "to_ary"); +} + +static mrb_value +to_hash(mrb_state *mrb, mrb_value val) +{ + return check_type(mrb, val, MRB_TT_HASH, "Hash", "to_hash"); +} + +static mrb_sym +to_sym(mrb_state *mrb, mrb_value ss) +{ + if (mrb_type(ss) == MRB_TT_SYMBOL) { + return mrb_symbol(ss); + } + 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); + /* not reached */ + return 0; + } +} + +/* + retrieve arguments from mrb_state. + + mrb_get_args(mrb, format, ...) + + returns number of arguments parsed. + + format specifiers: + + string mruby type C type note + ---------------------------------------------------------------------------------------------- + o: Object [mrb_value] + C: class/module [mrb_value] + S: String [mrb_value] when ! follows, the value may be nil + A: Array [mrb_value] when ! follows, the value may be nil + H: Hash [mrb_value] when ! follows, the value may be nil + s: String [char*,mrb_int] Receive two arguments; s! gives (NULL,0) for nil + z: String [char*] NUL terminated string; z! gives NULL for nil + a: Array [mrb_value*,mrb_int] Receive two arguments; a! gives (NULL,0) for nil + f: Float [mrb_float] + i: Integer [mrb_int] + b: Boolean [mrb_bool] + n: Symbol [mrb_sym] + d: Data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified + I: Inline struct [void*] + &: Block [mrb_value] + *: rest argument [mrb_value*,mrb_int] The rest of the arguments as an array; *! avoid copy of the stack + |: optional Following arguments are optional + ?: optional given [mrb_bool] true if preceding argument (optional) is given + */ +MRB_API mrb_int +mrb_get_args(mrb_state *mrb, const char *format, ...) +{ + const char *fmt = format; + char c; + int i = 0; + va_list ap; + int argc = mrb->c->ci->argc; + int arg_i = 0; + mrb_value *array_argv; + mrb_bool opt = FALSE; + mrb_bool opt_skip = TRUE; + mrb_bool given = TRUE; + + va_start(ap, format); + if (argc < 0) { + struct RArray *a = mrb_ary_ptr(mrb->c->stack[1]); + + argc = ARY_LEN(a); + array_argv = ARY_PTR(a); + } + else { + array_argv = NULL; + } + +#define ARGV \ + (array_argv ? array_argv : (mrb->c->stack + 1)) + + while ((c = *fmt++)) { + switch (c) { + case '|': + opt = TRUE; + break; + case '*': + opt_skip = FALSE; + goto check_exit; + case '!': + break; + case '&': case '?': + if (opt) opt_skip = FALSE; + break; + default: + break; + } + } + + check_exit: + opt = FALSE; + i = 0; + while ((c = *format++)) { + switch (c) { + case '|': case '*': case '&': case '?': + break; + default: + if (argc <= i) { + if (opt) { + given = FALSE; + } + else { + mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); + } + } + break; + } + + switch (c) { + case 'o': + { + mrb_value *p; + + p = va_arg(ap, mrb_value*); + if (i < argc) { + *p = ARGV[arg_i++]; + i++; + } + } + break; + case 'C': + { + mrb_value *p; + + p = va_arg(ap, mrb_value*); + if (i < argc) { + mrb_value ss; + + ss = ARGV[arg_i++]; + if (!class_ptr_p(ss)) { + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not class/module", ss); + } + *p = ss; + i++; + } + } + break; + case 'S': + { + mrb_value *p; + + p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *p = ARGV[arg_i++]; + i++; + break; + } + } + if (i < argc) { + *p = to_str(mrb, ARGV[arg_i++]); + i++; + } + } + break; + case 'A': + { + mrb_value *p; + + p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *p = ARGV[arg_i++]; + i++; + break; + } + } + if (i < argc) { + *p = to_ary(mrb, ARGV[arg_i++]); + i++; + } + } + break; + case 'H': + { + mrb_value *p; + + p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *p = ARGV[arg_i++]; + i++; + break; + } + } + if (i < argc) { + *p = to_hash(mrb, ARGV[arg_i++]); + i++; + } + } + break; + case 's': + { + mrb_value ss; + char **ps = 0; + mrb_int *pl = 0; + + ps = va_arg(ap, char**); + pl = va_arg(ap, mrb_int*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *ps = NULL; + *pl = 0; + i++; arg_i++; + break; + } + } + if (i < argc) { + ss = to_str(mrb, ARGV[arg_i++]); + *ps = RSTRING_PTR(ss); + *pl = RSTRING_LEN(ss); + i++; + } + } + break; + case 'z': + { + mrb_value ss; + const char **ps; + + ps = va_arg(ap, const char**); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *ps = NULL; + i++; arg_i++; + break; + } + } + if (i < argc) { + ss = to_str(mrb, ARGV[arg_i++]); + *ps = mrb_string_value_cstr(mrb, &ss); + i++; + } + } + break; + case 'a': + { + mrb_value aa; + struct RArray *a; + mrb_value **pb; + mrb_int *pl; + + pb = va_arg(ap, mrb_value**); + pl = va_arg(ap, mrb_int*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *pb = 0; + *pl = 0; + i++; arg_i++; + break; + } + } + if (i < argc) { + aa = to_ary(mrb, ARGV[arg_i++]); + a = mrb_ary_ptr(aa); + *pb = ARY_PTR(a); + *pl = ARY_LEN(a); + i++; + } + } + break; + case 'I': + { + void* *p; + mrb_value ss; + + p = va_arg(ap, void**); + if (i < argc) { + ss = ARGV[arg_i]; + if (mrb_type(ss) != MRB_TT_ISTRUCT) + { + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not inline struct", ss); + } + *p = mrb_istruct_ptr(ss); + arg_i++; + i++; + } + } + break; + case 'f': + { + mrb_float *p; + + p = va_arg(ap, mrb_float*); + if (i < argc) { + *p = mrb_to_flo(mrb, ARGV[arg_i]); + arg_i++; + i++; + } + } + break; + case 'i': + { + mrb_int *p; + + p = va_arg(ap, mrb_int*); + if (i < argc) { + switch (mrb_type(ARGV[arg_i])) { + case MRB_TT_FIXNUM: + *p = mrb_fixnum(ARGV[arg_i]); + break; + case MRB_TT_FLOAT: + { + mrb_float f = mrb_float(ARGV[arg_i]); + + if (!FIXABLE_FLOAT(f)) { + mrb_raise(mrb, E_RANGE_ERROR, "float too big for int"); + } + *p = (mrb_int)f; + } + break; + case MRB_TT_STRING: + mrb_raise(mrb, E_TYPE_ERROR, "no implicit conversion of String into Integer"); + break; + default: + *p = mrb_fixnum(mrb_Integer(mrb, ARGV[arg_i])); + break; + } + arg_i++; + i++; + } + } + break; + case 'b': + { + mrb_bool *boolp = va_arg(ap, mrb_bool*); + + if (i < argc) { + mrb_value b = ARGV[arg_i++]; + *boolp = mrb_test(b); + i++; + } + } + break; + case 'n': + { + mrb_sym *symp; + + symp = va_arg(ap, mrb_sym*); + if (i < argc) { + mrb_value ss; + + ss = ARGV[arg_i++]; + *symp = to_sym(mrb, ss); + i++; + } + } + break; + case 'd': + { + void** datap; + struct mrb_data_type const* type; + + datap = va_arg(ap, void**); + type = va_arg(ap, struct mrb_data_type const*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(ARGV[arg_i])) { + *datap = 0; + i++; arg_i++; + break; + } + } + if (i < argc) { + *datap = mrb_data_get_ptr(mrb, ARGV[arg_i++], type); + ++i; + } + } + break; + + case '&': + { + mrb_value *p, *bp; + + p = va_arg(ap, mrb_value*); + if (mrb->c->ci->argc < 0) { + bp = mrb->c->stack + 2; + } + else { + bp = mrb->c->stack + mrb->c->ci->argc + 1; + } + *p = *bp; + } + break; + case '|': + if (opt_skip && i == argc) return argc; + opt = TRUE; + break; + case '?': + { + mrb_bool *p; + + p = va_arg(ap, mrb_bool*); + *p = given; + } + break; + + case '*': + { + mrb_value **var; + mrb_int *pl; + mrb_bool nocopy = array_argv ? TRUE : FALSE; + + if (*format == '!') { + format++; + nocopy = TRUE; + } + var = va_arg(ap, mrb_value**); + pl = va_arg(ap, mrb_int*); + if (argc > i) { + *pl = argc-i; + if (*pl > 0) { + if (nocopy) { + *var = ARGV+arg_i; + } + else { + mrb_value args = mrb_ary_new_from_values(mrb, *pl, ARGV+arg_i); + RARRAY(args)->c = NULL; + *var = RARRAY_PTR(args); + } + } + i = argc; + arg_i += *pl; + } + else { + *pl = 0; + *var = NULL; + } + } + break; + default: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid argument specifier %S", mrb_str_new(mrb, &c, 1)); + break; + } + } + +#undef ARGV + + if (!c && argc > i) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); + } + va_end(ap); + return i; +} + +static struct RClass* +boot_defclass(mrb_state *mrb, struct RClass *super) +{ + struct RClass *c; + + c = (struct RClass*)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); + } + else { + c->super = mrb->object_class; + } + c->mt = kh_init(mt, mrb); + return c; +} + +static void +boot_initmod(mrb_state *mrb, struct RClass *mod) +{ + if (!mod->mt) { + mod->mt = kh_init(mt, mrb); + } +} + +static struct RClass* +include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) +{ + struct RClass *ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); + 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; + + while (m) { + int superclass_seen = 0; + + if (m->flags & MRB_FLAG_IS_PREPENDED) + goto skip; + + if (klass_mt && klass_mt == m->mt) + return -1; + + p = c->super; + while (p) { + if (p->tt == MRB_TT_ICLASS) { + if (p->mt == m->mt) { + if (!superclass_seen) { + ins_pos = p; /* move insert point */ + } + goto skip; + } + } else if (p->tt == MRB_TT_CLASS) { + if (!search_super) break; + superclass_seen = 1; + } + p = p->super; + } + + ic = include_class_new(mrb, m, ins_pos->super); + m->flags |= MRB_FLAG_IS_INHERITED; + ins_pos->super = ic; + mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); + mc_clear_by_class(mrb, ins_pos); + ins_pos = ic; + skip: + m = m->super; + } + mc_clear_all(mrb); + return 0; +} + +MRB_API void +mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +{ + int changed = include_module_at(mrb, c, find_origin(c), m, 1); + if (changed < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); + } +} + +MRB_API void +mrb_prepend_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +{ + struct RClass *origin; + int changed = 0; + + if (!(c->flags & MRB_FLAG_IS_PREPENDED)) { + origin = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, c); + origin->flags |= MRB_FLAG_IS_ORIGIN | MRB_FLAG_IS_INHERITED; + origin->super = c->super; + c->super = origin; + origin->mt = c->mt; + c->mt = kh_init(mt, mrb); + mrb_field_write_barrier(mrb, (struct RBasic*)c, (struct RBasic*)origin); + c->flags |= MRB_FLAG_IS_PREPENDED; + } + changed = include_module_at(mrb, c, c, m, 0); + if (changed < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic prepend detected"); + } +} + +static mrb_value +mrb_mod_prepend_features(mrb_state *mrb, mrb_value mod) +{ + mrb_value klass; + + mrb_check_type(mrb, mod, MRB_TT_MODULE); + mrb_get_args(mrb, "C", &klass); + mrb_prepend_module(mrb, mrb_class_ptr(klass), mrb_class_ptr(mod)); + return mod; +} + +static mrb_value +mrb_mod_append_features(mrb_state *mrb, mrb_value mod) +{ + mrb_value klass; + + 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)); + return mod; +} + +/* 15.2.2.4.28 */ +/* + * call-seq: + * mod.include?(module) -> true or false + * + * Returns <code>true</code> if <i>module</i> is included in + * <i>mod</i> or one of <i>mod</i>'s ancestors. + * + * module A + * end + * class B + * include A + * end + * class C < B + * end + * B.include?(A) #=> true + * C.include?(A) #=> true + * A.include?(A) #=> false + */ +static mrb_value +mrb_mod_include_p(mrb_state *mrb, mrb_value mod) +{ + mrb_value mod2; + struct RClass *c = mrb_class_ptr(mod); + + mrb_get_args(mrb, "C", &mod2); + mrb_check_type(mrb, mod2, MRB_TT_MODULE); + + while (c) { + if (c->tt == MRB_TT_ICLASS) { + if (c->c == mrb_class_ptr(mod2)) return mrb_true_value(); + } + c = c->super; + } + return mrb_false_value(); +} + +static mrb_value +mrb_mod_ancestors(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)); + } + else if (!(c->flags & MRB_FLAG_IS_PREPENDED)) { + mrb_ary_push(mrb, result, mrb_obj_value(c)); + } + c = c->super; + } + + return result; +} + +static mrb_value +mrb_mod_extend_object(mrb_state *mrb, mrb_value mod) +{ + mrb_value obj; + + 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); + struct RClass *origin = c; + + MRB_CLASS_ORIGIN(origin); + result = mrb_ary_new(mrb); + while (c) { + if (c != origin && c->tt == MRB_TT_ICLASS) { + if (c->c->tt == MRB_TT_MODULE) { + 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; + 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, 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); + +static mrb_value +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) +{ + 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); + case MRB_TT_TRUE: + return mrb_obj_value(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_FLOAT: + mrb_raise(mrb, E_TYPE_ERROR, "can't define singleton"); + return mrb_nil_value(); /* not reached */ + default: + break; + } + obj = mrb_basic_ptr(v); + prepare_singleton_class(mrb, obj); + return mrb_obj_value(obj->c); +} + +MRB_API void +mrb_define_singleton_method(mrb_state *mrb, struct RObject *o, const char *name, mrb_func_t func, mrb_aspec aspec) +{ + prepare_singleton_class(mrb, (struct RBasic*)o); + mrb_define_method_id(mrb, o->c, mrb_intern_cstr(mrb, name), func, aspec); +} + +MRB_API void +mrb_define_class_method(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) +{ + mrb_define_singleton_method(mrb, (struct RObject*)c, name, func, aspec); +} + +MRB_API void +mrb_define_module_function(mrb_state *mrb, struct RClass *c, const char *name, mrb_func_t func, mrb_aspec aspec) +{ + mrb_define_class_method(mrb, c, name, func, aspec); + mrb_define_method(mrb, c, name, func, aspec); +} + +#ifdef MRB_METHOD_CACHE +static void +mc_clear_all(mrb_state *mrb) +{ + struct mrb_cache_entry *mc = mrb->cache; + int i; + + for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) { + mc[i].c = 0; + } +} + +static void +mc_clear_by_class(mrb_state *mrb, struct RClass *c) +{ + struct mrb_cache_entry *mc = mrb->cache; + int i; + + if (c->flags & MRB_FLAG_IS_INHERITED) { + mc_clear_all(mrb); + c->flags &= ~MRB_FLAG_IS_INHERITED; + return; + } + for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) { + if (mc[i].c == c) mc[i].c = 0; + } +} + +static void +mc_clear_by_id(mrb_state *mrb, struct RClass *c, mrb_sym mid) +{ + struct mrb_cache_entry *mc = mrb->cache; + int i; + + if (c->flags & MRB_FLAG_IS_INHERITED) { + mc_clear_all(mrb); + c->flags &= ~MRB_FLAG_IS_INHERITED; + return; + } + for (i=0; i<MRB_METHOD_CACHE_SIZE; i++) { + if (mc[i].c == c || mc[i].mid == mid) + mc[i].c = 0; + } +} +#endif + +MRB_API struct RProc* +mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) +{ + khiter_t k; + struct RProc *m; + struct RClass *c = *cp; +#ifdef MRB_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) { + return mc->m; + } +#endif + + while (c) { + khash_t(mt) *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; + *cp = c; +#ifdef MRB_METHOD_CACHE + mc->c = oc; + mc->mid = mid; + mc->m = m; +#endif + return m; + } + } + c = c->super; + } + return NULL; /* no method */ +} + +MRB_API struct RProc* +mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid) +{ + struct RProc *m; + + m = mrb_method_search_vm(mrb, &c, mid); + if (!m) { + mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0); + if (mrb_string_p(inspect) && RSTRING_LEN(inspect) > 64) { + inspect = mrb_any_to_s(mrb, mrb_obj_value(c)); + } + mrb_name_error(mrb, mid, "undefined method '%S' for class %S", + mrb_sym2str(mrb, mid), inspect); + } + return m; +} + +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) +{ + 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; + mrb_sym method, sym; + + method = to_sym(mrb, argv[i]); + name = mrb_sym2str(mrb, method); + str = mrb_str_new_capa(mrb, RSTRING_LEN(name)+1); + mrb_str_cat_lit(mrb, str, "@"); + mrb_str_cat_str(mrb, str, name); + sym = mrb_intern_str(mrb, str); + mrb_iv_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)); + mrb_gc_arena_restore(mrb, ai); + } + return mrb_nil_value(); +} + +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_get_args(mrb, "o", &val); + mrb_iv_set(mrb, obj, to_sym(mrb, name), val); + return val; +} + +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_new_capa(mrb, RSTRING_LEN(name)+1); + mrb_str_cat_lit(mrb, str, "@"); + mrb_str_cat_str(mrb, str, name); + sym = mrb_intern_str(mrb, str); + mrb_iv_check(mrb, sym); + attr = mrb_symbol_value(sym); + + /* prepare method name (name=) */ + str = mrb_str_new_capa(mrb, RSTRING_LEN(str)); + mrb_str_cat_str(mrb, str, name); + mrb_str_cat_lit(mrb, str, "="); + method = mrb_intern_str(mrb, str); + + 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(); +} + +static mrb_value +mrb_instance_alloc(mrb_state *mrb, mrb_value cv) +{ + struct RClass *c = mrb_class_ptr(cv); + struct RObject *o; + enum mrb_vtype ttype = MRB_INSTANCE_TT(c); + + if (c->tt == MRB_TT_SCLASS) + 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 %S", cv); + } + o = (struct RObject*)mrb_obj_alloc(mrb, ttype, c); + return mrb_obj_value(o); +} + +/* + * call-seq: + * class.new(args, ...) -> obj + * + * 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_instance_new(mrb_state *mrb, mrb_value cv) +{ + mrb_value obj, blk; + mrb_value *argv; + mrb_int argc; + mrb_sym init; + + mrb_get_args(mrb, "*&", &argv, &argc, &blk); + obj = mrb_instance_alloc(mrb, cv); + init = mrb_intern_lit(mrb, "initialize"); + if (!mrb_func_basic_p(mrb, obj, init, mrb_bob_init)) { + mrb_funcall_with_block(mrb, obj, init, argc, argv, blk); + } + + return obj; +} + +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)); + mid = mrb_intern_lit(mrb, "initialize"); + if (!mrb_func_basic_p(mrb, obj, mid, mrb_bob_init)) { + mrb_funcall_argv(mrb, obj, mid, argc, argv); + } + return obj; +} + +static mrb_value +mrb_class_initialize(mrb_state *mrb, mrb_value c) +{ + mrb_value a, b; + + mrb_get_args(mrb, "|C&", &a, &b); + if (!mrb_nil_p(b)) { + mrb_yield_with_class(mrb, b, 1, &c, c, mrb_class_ptr(c)); + } + return c; +} + +static mrb_value +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))); + mid = mrb_intern_lit(mrb, "initialize"); + if (!mrb_func_basic_p(mrb, new_class, mid, mrb_bob_init)) { + 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; +} + +static mrb_value +mrb_class_superclass(mrb_state *mrb, mrb_value klass) +{ + struct RClass *c; + + c = mrb_class_ptr(klass); + c = find_origin(c)->super; + while (c && c->tt == MRB_TT_ICLASS) { + c = find_origin(c)->super; + } + if (!c) return mrb_nil_value(); + return mrb_obj_value(c); +} + +static mrb_value +mrb_bob_init(mrb_state *mrb, mrb_value cv) +{ + return mrb_nil_value(); +} + +static mrb_value +mrb_bob_not(mrb_state *mrb, mrb_value cv) +{ + return mrb_bool_value(!mrb_test(cv)); +} + +/* 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 + */ +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)); +} + +MRB_API mrb_bool +mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid) +{ + struct RProc *m; + + m = mrb_method_search_vm(mrb, &c, mid); + if (!m) { + return FALSE; + } + return TRUE; +} + +MRB_API mrb_bool +mrb_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym mid) +{ + return mrb_obj_respond_to(mrb, mrb_class(mrb, obj), mid); +} + +MRB_API mrb_value +mrb_class_path(mrb_state *mrb, struct RClass *c) +{ + mrb_value path; + mrb_sym nsym = mrb_intern_lit(mrb, "__classname__"); + + path = mrb_obj_iv_get(mrb, (struct RObject*)c, nsym); + if (mrb_nil_p(path)) { + /* no name (yet) */ + return mrb_class_find_path(mrb, c); + } + else if (mrb_symbol_p(path)) { + /* topleve class/module */ + const char *str; + mrb_int len; + + str = mrb_sym2name_len(mrb, mrb_symbol(path), &len); + return mrb_str_new(mrb, str, len); + } + return mrb_str_dup(mrb, path); +} + +MRB_API struct RClass* +mrb_class_real(struct RClass* cl) +{ + if (cl == 0) + return NULL; + while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) { + cl = cl->super; + } + return 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_API const char* +mrb_obj_classname(mrb_state *mrb, mrb_value obj) +{ + return mrb_class_name(mrb, mrb_obj_class(mrb, obj)); +} + +/*! + * Ensures a class can be derived from super. + * + * \param super a reference to an object. + * \exception TypeError if \a super is not a Class or \a super is a singleton class. + */ +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)); + } + if (super->tt == MRB_TT_SCLASS) { + mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of singleton class"); + } + if (super == mrb->class_class) { + mrb_raise(mrb, E_TYPE_ERROR, "can't make subclass of Class"); + } +} + +/*! + * Creates a new class. + * \param super a class from which the new class derives. + * \exception TypeError \a super is not inheritable. + * \exception TypeError \a super is the Class class. + */ +MRB_API struct RClass* +mrb_class_new(mrb_state *mrb, struct RClass *super) +{ + struct RClass *c; + + if (super) { + mrb_check_inheritable(mrb, super); + } + c = boot_defclass(mrb, super); + if (super) { + MRB_SET_INSTANCE_TT(c, MRB_INSTANCE_TT(super)); + } + make_metaclass(mrb, c); + + return c; +} + +/*! + * Creates a new module. + */ +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); + boot_initmod(mrb, m); + return m; +} + +/* + * call-seq: + * obj.class => class + * + * Returns the class of <i>obj</i>, now preferred over + * <code>Object#type</code>, as an object's type in Ruby is only + * loosely tied to that object's class. This method must always be + * called with an explicit receiver, as <code>class</code> is also a + * reserved word in Ruby. + * + * 1.class #=> Fixnum + * self.class #=> Object + */ + +MRB_API struct RClass* +mrb_obj_class(mrb_state *mrb, mrb_value obj) +{ + return mrb_class_real(mrb_class(mrb, 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); + + mrb_define_method_raw(mrb, c, a, m); +} + +/*! + * Defines an alias of a method. + * \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 + */ +MRB_API void +mrb_define_alias(mrb_state *mrb, struct RClass *klass, const char *name1, const char *name2) +{ + mrb_alias_method(mrb, klass, mrb_intern_cstr(mrb, name1), mrb_intern_cstr(mrb, name2)); +} + +/* + * call-seq: + * mod.to_s -> string + * + * Return a string representing this module or class. For basic + * classes and modules, this is the name. For singletons, we + * show information on the thing we're attached to as well. + */ + +static mrb_value +mrb_mod_to_s(mrb_state *mrb, mrb_value klass) +{ + mrb_value str; + + if (mrb_type(klass) == MRB_TT_SCLASS) { + mrb_value v = mrb_iv_get(mrb, klass, mrb_intern_lit(mrb, "__attached__")); + + str = mrb_str_new_lit(mrb, "#<Class:"); + + if (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_new_capa(mrb, 32); + c = mrb_class_ptr(klass); + path = mrb_class_path(mrb, c); + + if (mrb_nil_p(path)) { + switch (mrb_type(klass)) { + case MRB_TT_CLASS: + mrb_str_cat_lit(mrb, str, "#<Class:"); + break; + + case MRB_TT_MODULE: + mrb_str_cat_lit(mrb, str, "#<Module:"); + break; + + default: + /* Shouldn't be happened? */ + mrb_str_cat_lit(mrb, str, "#<??????:"); + break; + } + mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, c)); + return mrb_str_cat_lit(mrb, str, ">"); + } + else { + return path; + } + } +} + +static mrb_value +mrb_mod_alias(mrb_state *mrb, mrb_value mod) +{ + struct RClass *c = mrb_class_ptr(mod); + mrb_sym new_name, old_name; + + mrb_get_args(mrb, "nn", &new_name, &old_name); + mrb_alias_method(mrb, c, new_name, old_name); + return mrb_nil_value(); +} + +static void +undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a) +{ + if (!mrb_obj_respond_to(mrb, c, a)) { + mrb_name_error(mrb, a, "undefined method '%S' for class '%S'", mrb_sym2str(mrb, a), mrb_obj_value(c)); + } + else { + mrb_define_method_raw(mrb, c, a, NULL); + } +} + +MRB_API void +mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name) +{ + undef_method(mrb, c, mrb_intern_cstr(mrb, 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); +} + +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; + + mrb_get_args(mrb, "*", &argv, &argc); + while (argc--) { + undef_method(mrb, c, to_sym(mrb, *argv)); + argv++; + } + return mrb_nil_value(); +} + +static mrb_value +mod_define_method(mrb_state *mrb, mrb_value self) +{ + struct RClass *c = mrb_class_ptr(self); + struct RProc *p; + mrb_sym mid; + mrb_value proc = mrb_undef_value(); + mrb_value blk; + + 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 %S (expected Proc)", mrb_obj_value(mrb_obj_class(mrb, proc))); + break; + } + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); + } + p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class); + 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) +{ + const char *s = RSTRING_PTR(str); + mrb_int len = RSTRING_LEN(str); + + 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); + } +} + +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_sym id; + + mrb_get_args(mrb, "n", &id); + check_cv_name_sym(mrb, id); + return mrb_bool_value(mrb_cv_defined(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_sym id; + + mrb_get_args(mrb, "n", &id); + check_cv_name_sym(mrb, id); + return mrb_cv_get(mrb, mod, id); +} + +/* 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_value value; + mrb_sym id; + + mrb_get_args(mrb, "no", &id, &value); + check_cv_name_sym(mrb, id); + mrb_cv_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_value val; + mrb_sym id; + + mrb_get_args(mrb, "n", &id); + check_cv_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); + } + + mrb_name_error(mrb, id, "class variable %S not defined for %S", + mrb_sym2str(mrb, id), mod); + + /* not reached */ + return mrb_nil_value(); +} + +/* 15.2.2.4.34 */ +/* + * call-seq: + * mod.method_defined?(symbol) -> true or false + * + * Returns +true+ if the named method is defined by + * _mod_ (or its included modules and, if _mod_ is a class, + * its ancestors). Public and protected methods are matched. + * + * module A + * def method1() end + * end + * class B + * def method2() end + * end + * class C < B + * include A + * def method3() end + * end + * + * A.method_defined? :method1 #=> true + * C.method_defined? "method1" #=> true + * C.method_defined? "method2" #=> true + * C.method_defined? "method3" #=> true + * C.method_defined? "method4" #=> false + */ + +static mrb_value +mrb_mod_method_defined(mrb_state *mrb, mrb_value mod) +{ + mrb_sym id; + + mrb_get_args(mrb, "n", &id); + 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) +{ + struct RClass *c = mrb_class_ptr(mod); + khash_t(mt) *h = find_origin(c)->mt; + khiter_t k; + + if (h) { + k = kh_get(mt, mrb, h, mid); + if (k != kh_end(h)) { + kh_del(mt, mrb, h, k); + mrb_funcall(mrb, mod, "method_removed", 1, mrb_symbol_value(mid)); + return; + } + } + + mrb_name_error(mrb, mid, "method '%S' not defined in %S", + mrb_sym2str(mrb, mid), mod); +} + +/* 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_int argc; + mrb_value *argv; + + mrb_get_args(mrb, "*", &argv, &argc); + while (argc--) { + remove_method(mrb, mod, to_sym(mrb, *argv)); + argv++; + } + 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); + } +} + +static void +check_const_name_sym(mrb_state *mrb, mrb_sym id) +{ + check_const_name_str(mrb, mrb_sym2str(mrb, id)); +} + +static mrb_value +const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool inherit) +{ + if (inherit) { + return mrb_bool_value(mrb_const_defined(mrb, mod, id)); + } + return mrb_bool_value(mrb_const_defined_at(mrb, mod, id)); +} + +static mrb_value +mrb_mod_const_defined(mrb_state *mrb, mrb_value mod) +{ + mrb_sym id; + mrb_bool inherit = TRUE; + + mrb_get_args(mrb, "n|b", &id, &inherit); + check_const_name_sym(mrb, id); + return const_defined(mrb, mod, id, inherit); +} + +static mrb_value +mrb_const_get_sym(mrb_state *mrb, mrb_value mod, mrb_sym id) +{ + check_const_name_sym(mrb, id); + return mrb_const_get(mrb, mod, id); +} + +static mrb_value +mrb_mod_const_get(mrb_state *mrb, mrb_value mod) +{ + mrb_value path; + mrb_sym id; + char *ptr; + mrb_int off, end, len; + + mrb_get_args(mrb, "o", &path); + + if (mrb_symbol_p(path)) { + /* const get with symbol */ + id = mrb_symbol(path); + return mrb_const_get_sym(mrb, mod, id); + } + + /* const get with class path string */ + path = mrb_string_type(mrb, path); + ptr = RSTRING_PTR(path); + len = RSTRING_LEN(path); + off = 0; + + while (off < len) { + end = mrb_str_index_lit(mrb, path, "::", off); + end = (end == -1) ? len : end; + id = mrb_intern(mrb, ptr+off, end-off); + mod = mrb_const_get_sym(mrb, mod, id); + off = (end == len) ? end : end+2; + } + + return mod; +} + +static mrb_value +mrb_mod_const_set(mrb_state *mrb, mrb_value mod) +{ + mrb_sym id; + mrb_value value; + + mrb_get_args(mrb, "no", &id, &value); + check_const_name_sym(mrb, id); + mrb_const_set(mrb, mod, id, value); + return value; +} + +static mrb_value +mrb_mod_remove_const(mrb_state *mrb, mrb_value mod) +{ + mrb_sym id; + mrb_value val; + + 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)); + } + return val; +} + +static mrb_value +mrb_mod_const_missing(mrb_state *mrb, mrb_value mod) +{ + mrb_sym sym; + + mrb_get_args(mrb, "n", &sym); + + if (mrb_class_real(mrb_class_ptr(mod)) != mrb->object_class) { + mrb_name_error(mrb, sym, "uninitialized constant %S::%S", + mod, + mrb_sym2str(mrb, sym)); + } + else { + mrb_name_error(mrb, sym, "uninitialized constant %S", + mrb_sym2str(mrb, sym)); + } + /* not reached */ + return mrb_nil_value(); +} + +static mrb_value +mrb_mod_s_constants(mrb_state *mrb, mrb_value mod) +{ + mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented"); + return mrb_nil_value(); /* not reached */ +} + +static mrb_value +mrb_mod_eqq(mrb_state *mrb, mrb_value mod) +{ + mrb_value obj; + mrb_bool eqq; + + mrb_get_args(mrb, "o", &obj); + eqq = mrb_obj_is_kind_of(mrb, obj, mrb_class_ptr(mod)); + + return mrb_bool_value(eqq); +} + +MRB_API mrb_value +mrb_mod_module_function(mrb_state *mrb, mrb_value mod) +{ + mrb_value *argv; + mrb_int argc, i; + mrb_sym mid; + struct RProc *method_rproc; + struct RClass *rclass; + int ai; + + mrb_check_type(mrb, mod, MRB_TT_MODULE); + + mrb_get_args(mrb, "*", &argv, &argc); + if (argc == 0) { + /* set MODFUNC SCOPE if implemented */ + return mod; + } + + /* 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); + method_rproc = 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, method_rproc); + mrb_gc_arena_restore(mrb, ai); + } + + return mod; +} + +/* 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); +/* implementation of Module.nesting */ +mrb_value mrb_mod_s_nesting(mrb_state*, mrb_value); + +void +mrb_init_class(mrb_state *mrb) +{ + struct RClass *bob; /* BasicObject */ + struct RClass *obj; /* Object */ + struct RClass *mod; /* Module */ + struct RClass *cls; /* Class */ + + /* boot class hierarchy */ + bob = boot_defclass(mrb, 0); + obj = boot_defclass(mrb, bob); mrb->object_class = obj; + mod = boot_defclass(mrb, obj); mrb->module_class = mod;/* obj -> mod */ + cls = boot_defclass(mrb, mod); mrb->class_class = cls; /* obj -> cls */ + /* fix-up loose ends */ + bob->c = obj->c = mod->c = cls->c = cls; + make_metaclass(mrb, bob); + make_metaclass(mrb, obj); + make_metaclass(mrb, mod); + 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)); + + /* name each classes */ + mrb_class_name_class(mrb, NULL, bob, mrb_intern_lit(mrb, "BasicObject")); + mrb_class_name_class(mrb, NULL, obj, mrb_intern_lit(mrb, "Object")); /* 15.2.1 */ + mrb_class_name_class(mrb, NULL, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */ + mrb_class_name_class(mrb, NULL, cls, mrb_intern_lit(mrb, "Class")); /* 15.2.3 */ + + mrb->proc_class = mrb_define_class(mrb, "Proc", mrb->object_class); /* 15.2.17 */ + MRB_SET_INSTANCE_TT(mrb->proc_class, MRB_TT_PROC); + + 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, "==", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.1 */ + mrb_define_method(mrb, bob, "!=", mrb_obj_not_equal_m, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, bob, "__id__", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.3 */ + mrb_define_method(mrb, bob, "__send__", mrb_f_send, MRB_ARGS_ANY()); /* 15.3.1.3.4 */ + mrb_define_method(mrb, bob, "instance_eval", mrb_obj_instance_eval, MRB_ARGS_ANY()); /* 15.3.1.3.18 */ + + mrb_define_class_method(mrb, cls, "new", mrb_class_new_class, MRB_ARGS_OPT(1)); + 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)); + + 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, "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, "method_removed", mrb_bob_init, MRB_ARGS_REQ(1)); + 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()); + mrb_define_method(mrb, mod, "inspect", mrb_mod_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, mod, "alias_method", mrb_mod_alias, MRB_ARGS_ANY()); /* 15.2.2.4.8 */ + mrb_define_method(mrb, mod, "ancestors", mrb_mod_ancestors, MRB_ARGS_NONE()); /* 15.2.2.4.9 */ + mrb_define_method(mrb, mod, "undef_method", mrb_mod_undef, MRB_ARGS_ANY()); /* 15.2.2.4.41 */ + 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_ARG(1,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_class_method(mrb, mod, "nesting", mrb_mod_s_nesting, MRB_ARGS_REQ(0)); /* 15.2.2.3.2 */ + + mrb_undef_method(mrb, cls, "append_features"); + mrb_undef_method(mrb, cls, "extend_object"); +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/codedump.c b/web/server/h2o/libh2o/deps/mruby/src/codedump.c new file mode 100644 index 000000000..e3a33419c --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/codedump.c @@ -0,0 +1,474 @@ +#include <mruby.h> +#include <mruby/irep.h> +#include <mruby/debug.h> +#include <mruby/opcode.h> +#include <mruby/string.h> +#include <mruby/proc.h> + +#ifndef MRB_DISABLE_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) +{ +#ifndef MRB_DISABLE_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%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: + printf("\tnormal\t"); break; + case OP_R_RETURN: + printf("\treturn\t"); break; + case OP_R_BREAK: + printf("\tbreak\t"); break; + default: + printf("\tbroken\t"); 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", GETARG_A(c), GETARG_b(c)+1); + switch (GETARG_c(c)) { + case OP_L_METHOD: + printf("method"); break; + case OP_L_BLOCK: + printf("block"); break; + case OP_L_LAMBDA: + printf("lambda"); break; + } + print_lv(mrb, irep, c, RA); + break; + case OP_RANGE: + 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\t\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: + { + int a = GETARG_A(c); + int b = GETARG_B(c); + int cnt = GETARG_C(c); + + if (b == 0) { + printf("OP_RESCUE\tR%d\t\t%s", a, cnt ? "cont" : ""); + print_lv(mrb, irep, c, RA); + break; + } + else { + printf("OP_RESCUE\tR%d\tR%d\t%s", a, b, cnt ? "cont" : ""); + print_lv(mrb, irep, c, RAB); + break; + } + } + 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\n", GETARG_A(c)); + 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) +{ + int 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); +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/compar.c b/web/server/h2o/libh2o/deps/mruby/src/compar.c new file mode 100644 index 000000000..0032fc859 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/compar.c @@ -0,0 +1,13 @@ +/* +** compar.c - Comparable module +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> + +void +mrb_init_comparable(mrb_state *mrb) +{ + mrb_define_module(mrb, "Comparable"); /* 15.3.3 */ +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/crc.c b/web/server/h2o/libh2o/deps/mruby/src/crc.c new file mode 100644 index 000000000..290b2ca0e --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/crc.c @@ -0,0 +1,39 @@ +/* +** 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/web/server/h2o/libh2o/deps/mruby/src/debug.c b/web/server/h2o/libh2o/deps/mruby/src/debug.c new file mode 100644 index 000000000..e55f11d4f --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/debug.c @@ -0,0 +1,217 @@ +#include <string.h> +#include <mruby.h> +#include <mruby/irep.h> +#include <mruby/debug.h> + +static mrb_irep_debug_info_file* +get_file(mrb_irep_debug_info *info, uint32_t pc) +{ + mrb_irep_debug_info_file **ret; + int32_t count; + + if (pc >= info->pc_count) { return NULL; } + /* get upper bound */ + ret = info->files; + count = info->flen; + while (count > 0) { + int32_t step = count / 2; + mrb_irep_debug_info_file **it = ret + step; + if (!(pc < (*it)->start_pos)) { + ret = it + 1; + count -= step + 1; + } + else { count = step; } + } + + --ret; + + /* check returning file exists inside debug info */ + mrb_assert(info->files <= ret && ret < (info->files + info->flen)); + /* check pc is within the range of returning file */ + mrb_assert((*ret)->start_pos <= pc && + pc < (((ret + 1 - info->files) < info->flen) + ? (*(ret+1))->start_pos : info->pc_count)); + + return *ret; +} + +static mrb_debug_line_type +select_line_type(const uint16_t *lines, size_t lines_len) +{ + size_t line_count = 0; + int prev_line = -1; + size_t i; + for (i = 0; i < lines_len; ++i) { + if (lines[i] != prev_line) { + ++line_count; + } + } + return (sizeof(uint16_t) * lines_len) <= (sizeof(mrb_irep_debug_info_line) * line_count) + ? mrb_debug_line_ary : mrb_debug_line_flat_map; +} + +MRB_API char const* +mrb_debug_get_filename(mrb_irep *irep, ptrdiff_t pc) +{ + if (irep && pc >= 0 && pc < irep->ilen) { + mrb_irep_debug_info_file* f = NULL; + if (!irep->debug_info) { return irep->filename; } + else if ((f = get_file(irep->debug_info, (uint32_t)pc))) { + return f->filename; + } + } + return NULL; +} + +MRB_API int32_t +mrb_debug_get_line(mrb_irep *irep, ptrdiff_t pc) +{ + if (irep && pc >= 0 && pc < irep->ilen) { + mrb_irep_debug_info_file* f = NULL; + if (!irep->debug_info) { + return irep->lines? irep->lines[pc] : -1; + } + else if ((f = get_file(irep->debug_info, (uint32_t)pc))) { + switch (f->line_type) { + case mrb_debug_line_ary: + mrb_assert(f->start_pos <= pc && pc < (f->start_pos + f->line_entry_count)); + return f->lines.ary[pc - f->start_pos]; + + case mrb_debug_line_flat_map: { + /* get upper bound */ + mrb_irep_debug_info_line *ret = f->lines.flat_map; + uint32_t count = f->line_entry_count; + while (count > 0) { + int32_t step = count / 2; + mrb_irep_debug_info_line *it = ret + step; + if (!(pc < it->start_pos)) { + ret = it + 1; + count -= step + 1; + } + else { count = step; } + } + + --ret; + + /* check line entry pointer range */ + mrb_assert(f->lines.flat_map <= ret && ret < (f->lines.flat_map + f->line_entry_count)); + /* check pc range */ + mrb_assert(ret->start_pos <= pc && + pc < (((uint32_t)(ret + 1 - f->lines.flat_map) < f->line_entry_count) + ? (ret+1)->start_pos : irep->debug_info->pc_count)); + + return ret->line; + } + } + } + } + return -1; +} + +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 }; + mrb_irep_debug_info *ret; + + mrb_assert(!irep->debug_info); + ret = (mrb_irep_debug_info *)mrb_malloc(mrb, sizeof(*ret)); + *ret = initial; + irep->debug_info = ret; + return ret; +} + +MRB_API mrb_irep_debug_info_file* +mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep, + uint32_t start_pos, uint32_t end_pos) +{ + mrb_irep_debug_info *info; + mrb_irep_debug_info_file *ret; + 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); + + info = irep->debug_info; + + if (info->flen > 0 && strcmp(irep->filename, info->files[info->flen - 1]->filename) == 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)) + : mrb_malloc(mrb, sizeof(mrb_irep_debug_info_file*))); + info->files[info->flen++] = ret; + + file_pc_count = end_pos - start_pos; + + ret->start_pos = start_pos; + info->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); + + ret->line_type = select_line_type(irep->lines + start_pos, end_pos - start_pos); + ret->lines.ptr = NULL; + + switch (ret->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); + for (i = 0; i < file_pc_count; ++i) { + ret->lines.ary[i] = irep->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; + for (i = 0; i < file_pc_count; ++i) { + if (irep->lines[start_pos + i] == prev_line) { continue; } + + ret->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc( + mrb, ret->lines.flat_map, + sizeof(mrb_irep_debug_info_line) * (ret->line_entry_count + 1)); + m.start_pos = start_pos + i; + m.line = irep->lines[start_pos + i]; + ret->lines.flat_map[ret->line_entry_count] = m; + + /* update */ + ++ret->line_entry_count; + prev_line = irep->lines[start_pos + i]; + } + } break; + + default: mrb_assert(0); break; + } + + return ret; +} + +MRB_API void +mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d) +{ + uint32_t i; + + 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]); + } + mrb_free(mrb, d->files); + mrb_free(mrb, d); +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/dump.c b/web/server/h2o/libh2o/deps/mruby/src/dump.c new file mode 100644 index 000000000..d479a1a4f --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/dump.c @@ -0,0 +1,1100 @@ +/* +** dump.c - mruby binary dumper (mrbc binary format) +** +** See Copyright Notice in mruby.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 MRB_USE_FLOAT +#define MRB_FLOAT_FMT "%.8e" +#else +#define MRB_FLOAT_FMT "%.16e" +#endif + +static size_t get_irep_record_size_1(mrb_state *mrb, 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; + + size += sizeof(uint32_t) * 1; + size += sizeof(uint16_t) * 3; + + return size; +} + +static ptrdiff_t +write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) +{ + uint8_t *cur = buf; + + 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 */ + + return cur - buf; +} + + +static size_t +get_iseq_block_size(mrb_state *mrb, 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) */ + + return size; +} + +static ptrdiff_t +write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags) +{ + uint8_t *cur = buf; + int iseq_no; + + cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */ + cur += write_padding(cur); + switch (flags & DUMP_ENDIAN_NAT) { + case DUMP_ENDIAN_BIG: + if (bigendian_p()) goto native; + for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { + cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* opcode */ + } + break; + case DUMP_ENDIAN_LIL: + if (!bigendian_p()) goto native; + for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { + cur += uint32l_to_bin(irep->iseq[iseq_no], cur); /* opcode */ + } + break; + + native: + case DUMP_ENDIAN_NAT: + memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code)); + cur += irep->ilen * sizeof(mrb_code); + break; + } + + return cur - buf; +} + + +static size_t +get_pool_block_size(mrb_state *mrb, mrb_irep *irep) +{ + int pool_no; + size_t size = 0; + mrb_value str; + + size += sizeof(uint32_t); /* plen */ + size += irep->plen * (sizeof(uint8_t) + sizeof(uint16_t)); /* len(n) */ + + 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); + { + mrb_int len = RSTRING_LEN(str); + mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); + size += (size_t)len; + } + break; + + case MRB_TT_FLOAT: + str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT); + { + mrb_int len = RSTRING_LEN(str); + mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); + size += (size_t)len; + } + break; + + case MRB_TT_STRING: + { + mrb_int len = RSTRING_LEN(irep->pool[pool_no]); + mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); + size += (size_t)len; + } + break; + + default: + break; + } + mrb_gc_arena_restore(mrb, ai); + } + + return size; +} + +static ptrdiff_t +write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) +{ + int pool_no; + uint8_t *cur = buf; + uint16_t len; + mrb_value str; + const char *char_ptr; + + cur += uint32_to_bin(irep->plen, cur); /* number of pool */ + + for (pool_no = 0; pool_no < irep->plen; pool_no++) { + int ai = mrb_gc_arena_save(mrb); + + switch (mrb_type(irep->pool[pool_no])) { + case MRB_TT_FIXNUM: + cur += uint8_to_bin(IREP_TT_FIXNUM, cur); /* data type */ + str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10); + break; + + case MRB_TT_FLOAT: + cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ + str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT); + break; + + case MRB_TT_STRING: + cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */ + str = irep->pool[pool_no]; + break; + + default: + continue; + } + + char_ptr = RSTRING_PTR(str); + { + mrb_int tlen = RSTRING_LEN(str); + mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX); + len = (uint16_t)tlen; + } + + 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) +{ + size_t size = 0; + int sym_no; + mrb_int len; + + size += sizeof(uint32_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); + size += len + 1; /* sn(n) + null char */ + } + } + + return size; +} + +static ptrdiff_t +write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) +{ + int sym_no; + uint8_t *cur = buf; + const char *name; + + cur += uint32_to_bin(irep->slen, cur); /* number of symbol */ + + 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); + + mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX); + cur += uint16_to_bin((uint16_t)len, cur); /* length of symbol name */ + memcpy(cur, name, len); /* symbol name */ + cur += (uint16_t)len; + *cur++ = '\0'; + } + else { + cur += uint16_to_bin(MRB_DUMP_NULL_SYM_LEN, cur); /* length of symbol name */ + } + } + + return cur - buf; +} + +static size_t +get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep) +{ + size_t size = 0; + + size += get_irep_header_size(mrb); + size += get_iseq_block_size(mrb, irep); + size += get_pool_block_size(mrb, irep); + size += get_syms_block_size(mrb, irep); + return size; +} + +static size_t +get_irep_record_size(mrb_state *mrb, mrb_irep *irep) +{ + size_t size = 0; + int irep_no; + + size = get_irep_record_size_1(mrb, irep); + for (irep_no = 0; irep_no < irep->rlen; irep_no++) { + size += get_irep_record_size(mrb, irep->reps[irep_no]); + } + return size; +} + +static int +write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags) +{ + 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); + bin += write_syms_block(mrb, irep, bin); + + for (i = 0; i < irep->rlen; i++) { + int result; + size_t rsize; + + result = write_irep_record(mrb, irep->reps[i], bin, &rsize, flags); + if (result != MRB_DUMP_OK) { + return result; + } + bin += rsize; + } + *irep_record_size = bin - src; + return MRB_DUMP_OK; +} + +static uint32_t +write_footer(mrb_state *mrb, uint8_t *bin) +{ + struct rite_binary_footer footer; + + 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)); + + return sizeof(struct rite_binary_footer); +} + + +static int +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_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); + memcpy(header->rite_version, RITE_VM_VER, sizeof(header->rite_version)); + + return MRB_DUMP_OK; +} + +static int +write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags) +{ + int result; + size_t rsize = 0; + uint8_t *cur = bin; + + if (mrb == NULL || bin == NULL) { + return MRB_DUMP_INVALID_ARGUMENT; + } + + cur += sizeof(struct rite_section_irep_header); + + result = write_irep_record(mrb, irep, cur, &rsize, flags); + if (result != MRB_DUMP_OK) { + return result; + } + *len_p = cur - bin + rsize; + write_section_irep_header(mrb, *len_p, bin); + + return MRB_DUMP_OK; +} + +static int +write_section_lineno_header(mrb_state *mrb, size_t section_size, uint8_t *bin) +{ + struct rite_section_lineno_header *header = (struct rite_section_lineno_header*)bin; + + memcpy(header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(header->section_ident)); + uint32_to_bin((uint32_t)section_size, header->section_size); + + return MRB_DUMP_OK; +} + +static size_t +get_lineno_record_size(mrb_state *mrb, mrb_irep *irep) +{ + size_t size = 0; + + size += sizeof(uint32_t); /* record size */ + size += sizeof(uint16_t); /* filename size */ + if (irep->filename) { + size += strlen(irep->filename); /* filename */ + } + size += sizeof(uint32_t); /* niseq */ + if (irep->lines) { + size += sizeof(uint16_t) * irep->ilen; /* lineno */ + } + + return size; +} + +static size_t +write_lineno_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t* bin) +{ + uint8_t *cur = bin; + int iseq_no; + size_t filename_len; + ptrdiff_t diff; + + cur += sizeof(uint32_t); /* record size */ + + if (irep->filename) { + filename_len = strlen(irep->filename); + } + else { + filename_len = 0; + } + mrb_assert_int_fit(size_t, filename_len, uint16_t, UINT16_MAX); + cur += uint16_to_bin((uint16_t)filename_len, cur); /* filename size */ + + if (filename_len) { + memcpy(cur, irep->filename, filename_len); + cur += filename_len; /* filename */ + } + + if (irep->lines) { + mrb_assert_int_fit(size_t, irep->ilen, uint32_t, UINT32_MAX); + cur += uint32_to_bin((uint32_t)(irep->ilen), cur); /* niseq */ + for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { + cur += uint16_to_bin(irep->lines[iseq_no], cur); /* opcode */ + } + } + else { + cur += uint32_to_bin(0, cur); /* niseq */ + } + + diff = cur - bin; + mrb_assert_int_fit(ptrdiff_t, diff, uint32_t, UINT32_MAX); + + uint32_to_bin((uint32_t)diff, bin); /* record size */ + + mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); + return (size_t)diff; +} + +static size_t +write_lineno_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin) +{ + size_t rlen, size = 0; + int i; + + rlen = write_lineno_record_1(mrb, irep, bin); + bin += rlen; + size += rlen; + for (i=0; i<irep->rlen; i++) { + rlen = write_lineno_record(mrb, irep, bin); + bin += rlen; + size += rlen; + } + return size; +} + +static int +write_section_lineno(mrb_state *mrb, mrb_irep *irep, uint8_t *bin) +{ + size_t section_size = 0; + size_t rlen = 0; /* size of irep record */ + uint8_t *cur = bin; + + if (mrb == NULL || bin == NULL) { + return MRB_DUMP_INVALID_ARGUMENT; + } + + cur += sizeof(struct rite_section_lineno_header); + section_size += sizeof(struct rite_section_lineno_header); + + rlen = write_lineno_record(mrb, irep, cur); + section_size += rlen; + + write_section_lineno_header(mrb, section_size, bin); + + return MRB_DUMP_OK; +} + +static size_t +get_debug_record_size(mrb_state *mrb, mrb_irep *irep) +{ + size_t ret = 0; + uint16_t f_idx; + int i; + + ret += sizeof(uint32_t); /* record size */ + ret += sizeof(uint16_t); /* file count */ + + for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { + mrb_irep_debug_info_file const* file = irep->debug_info->files[f_idx]; + + ret += sizeof(uint32_t); /* position */ + ret += sizeof(uint16_t); /* filename index */ + + /* lines */ + ret += sizeof(uint32_t); /* entry count */ + ret += sizeof(uint8_t); /* line type */ + switch (file->line_type) { + case mrb_debug_line_ary: + ret += sizeof(uint16_t) * (size_t)(file->line_entry_count); + break; + + case mrb_debug_line_flat_map: + ret += (sizeof(uint32_t) + sizeof(uint16_t)) * (size_t)(file->line_entry_count); + break; + + default: mrb_assert(0); break; + } + } + for (i=0; i<irep->rlen; i++) { + ret += get_debug_record_size(mrb, irep->reps[i]); + } + + return ret; +} + +static int +find_filename_index(const mrb_sym *ary, int ary_len, mrb_sym s) +{ + int i; + + for (i = 0; i < ary_len; ++i) { + if (ary[i] == s) { return i; } + } + return -1; +} + +static size_t +get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *lp) +{ + mrb_sym *filenames = *fp; + size_t size = 0; + mrb_irep_debug_info *di = irep->debug_info; + int i; + + mrb_assert(lp); + for (i = 0; i < di->flen; ++i) { + mrb_irep_debug_info_file *file; + mrb_int filename_len; + + file = di->files[i]; + if (find_filename_index(filenames, *lp, file->filename_sym) == -1) { + /* register filename */ + *lp += 1; + *fp = filenames = (mrb_sym *)mrb_realloc(mrb, filenames, sizeof(mrb_sym) * (*lp)); + filenames[*lp - 1] = file->filename_sym; + + /* filename */ + mrb_sym2name_len(mrb, file->filename_sym, &filename_len); + size += sizeof(uint16_t) + (size_t)filename_len; + } + } + for (i=0; i<irep->rlen; i++) { + size += get_filename_table_size(mrb, irep->reps[i], fp, lp); + } + return size; +} + +static size_t +write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len) +{ + uint8_t *cur; + uint16_t f_idx; + ptrdiff_t ret; + + cur = bin + sizeof(uint32_t); /* skip record size */ + cur += uint16_to_bin(irep->debug_info->flen, cur); /* file count */ + + for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) { + int filename_idx; + const mrb_irep_debug_info_file *file = irep->debug_info->files[f_idx]; + + /* position */ + cur += uint32_to_bin(file->start_pos, cur); + + /* filename index */ + filename_idx = find_filename_index(filenames, filenames_len, + file->filename_sym); + mrb_assert_int_fit(int, filename_idx, uint16_t, UINT16_MAX); + cur += uint16_to_bin((uint16_t)filename_idx, cur); + + /* lines */ + cur += uint32_to_bin(file->line_entry_count, cur); + cur += uint8_to_bin(file->line_type, cur); + switch (file->line_type) { + case mrb_debug_line_ary: { + uint32_t l; + for (l = 0; l < file->line_entry_count; ++l) { + cur += uint16_to_bin(file->lines.ary[l], cur); + } + } break; + + case mrb_debug_line_flat_map: { + uint32_t line; + for (line = 0; line < file->line_entry_count; ++line) { + cur += uint32_to_bin(file->lines.flat_map[line].start_pos, cur); + cur += uint16_to_bin(file->lines.flat_map[line].line, cur); + } + } break; + + default: mrb_assert(0); break; + } + } + + ret = cur - bin; + mrb_assert_int_fit(ptrdiff_t, ret, uint32_t, UINT32_MAX); + 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) +{ + size_t size, len; + int irep_no; + + size = len = write_debug_record_1(mrb, irep, bin, filenames, filenames_len); + bin += len; + for (irep_no = 0; irep_no < irep->rlen; irep_no++) { + len = write_debug_record(mrb, irep->reps[irep_no], bin, filenames, filenames_len); + bin += len; + size += len; + } + + mrb_assert(size == get_debug_record_size(mrb, irep)); + return size; +} + +static int +write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len) +{ + size_t section_size = 0; + const uint8_t *bin = cur; + struct rite_section_debug_header *header; + size_t dlen; + uint16_t i; + char const *sym; mrb_int sym_len; + + if (mrb == NULL || cur == NULL) { + return MRB_DUMP_INVALID_ARGUMENT; + } + + header = (struct rite_section_debug_header *)bin; + cur += sizeof(struct rite_section_debug_header); + section_size += sizeof(struct rite_section_debug_header); + + /* filename table */ + 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); + mrb_assert(sym); + cur += uint16_to_bin(sym_len, cur); + memcpy(cur, sym, sym_len); + cur += sym_len; + section_size += sizeof(uint16_t) + sym_len; + } + + /* debug records */ + dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len); + section_size += dlen; + + memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident)); + mrb_assert(section_size <= INT32_MAX); + uint32_to_bin((uint32_t)section_size, header->section_size); + + return MRB_DUMP_OK; +} + +static void +create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32_t *syms_len) +{ + 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; + if (name == 0) continue; + if (find_filename_index(*syms, *syms_len, name) != -1) continue; + + ++(*syms_len); + *syms = (mrb_sym*)mrb_realloc(mrb, *syms, sizeof(mrb_sym) * (*syms_len)); + (*syms)[*syms_len - 1] = name; + } + + for (i = 0; i < irep->rlen; ++i) { + create_lv_sym_table(mrb, irep->reps[i], syms, syms_len); + } +} + +static int +write_lv_sym_table(mrb_state *mrb, uint8_t **start, mrb_sym const *syms, uint32_t syms_len) +{ + uint8_t *cur = *start; + uint32_t i; + const char *str; + mrb_int str_len; + + 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); + memcpy(cur, str, str_len); + cur += str_len; + } + + *start = cur; + + return MRB_DUMP_OK; +} + +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; + int i; + + for (i = 0; i + 1 < irep->nlocals; ++i) { + if (irep->lv[i].name == 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); + 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); + } + } + + for (i = 0; i < irep->rlen; ++i) { + write_lv_record(mrb, irep->reps[i], &cur, syms, syms_len); + } + + *start = cur; + + return MRB_DUMP_OK; +} + +static size_t +get_lv_record_size(mrb_state *mrb, mrb_irep *irep) +{ + size_t ret = 0; + int i; + + ret += (sizeof(uint16_t) + sizeof(uint16_t)) * (irep->nlocals - 1); + + for (i = 0; i < irep->rlen; ++i) { + ret += get_lv_record_size(mrb, irep->reps[i]); + } + + return ret; +} + +static size_t +get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len) +{ + size_t ret = 0, i; + + ret += sizeof(uint32_t); /* syms_len */ + 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); + ret += str_len; + } + + ret += get_lv_record_size(mrb, irep); + + return ret; +} + +static int +write_section_lv(mrb_state *mrb, 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; + ptrdiff_t diff; + int result = MRB_DUMP_OK; + + if (mrb == NULL || cur == NULL) { + return MRB_DUMP_INVALID_ARGUMENT; + } + + header = (struct rite_section_lv_header*)cur; + cur += sizeof(struct rite_section_lv_header); + + result = write_lv_sym_table(mrb, &cur, syms, syms_len); + if (result != MRB_DUMP_OK) { + goto lv_section_exit; + } + + result = write_lv_record(mrb, irep, &cur, syms, syms_len); + if (result != MRB_DUMP_OK) { + goto lv_section_exit; + } + + 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((uint32_t)diff, header->section_size); + +lv_section_exit: + return result; +} + +static int +write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8_t flags) +{ + struct rite_binary_header *header = (struct rite_binary_header *)bin; + uint16_t crc; + uint32_t offset; + + switch (flags & DUMP_ENDIAN_NAT) { + endian_big: + case DUMP_ENDIAN_BIG: + memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)); + break; + endian_little: + case DUMP_ENDIAN_LIL: + memcpy(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)); + break; + + case DUMP_ENDIAN_NAT: + if (bigendian_p()) goto endian_big; + goto endian_little; + break; + } + + memcpy(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)); + memcpy(header->compiler_name, RITE_COMPILER_NAME, sizeof(header->compiler_name)); + memcpy(header->compiler_version, RITE_COMPILER_VERSION, sizeof(header->compiler_version)); + mrb_assert(binary_size <= UINT32_MAX); + uint32_to_bin((uint32_t)binary_size, header->binary_size); + + offset = (uint32_t)((&(header->binary_crc[0]) - bin) + sizeof(uint16_t)); + crc = calc_crc_16_ccitt(bin + offset, binary_size - offset, 0); + uint16_to_bin(crc, header->binary_crc); + + return MRB_DUMP_OK; +} + +static mrb_bool +is_debug_info_defined(mrb_irep *irep) +{ + 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; + } + return TRUE; +} + +static mrb_bool +is_lv_defined(mrb_irep *irep) +{ + int i; + + if (irep->lv) { return TRUE; } + + for (i = 0; i < irep->rlen; ++i) { + if (is_lv_defined(irep->reps[i])) { return TRUE; } + } + + return FALSE; +} + +static uint8_t +dump_flags(uint8_t flags, uint8_t native) +{ + if (native == FLAG_BYTEORDER_NATIVE) { + if ((flags & DUMP_ENDIAN_NAT) == 0) { + return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_NAT; + } + return flags; + } + if ((flags & DUMP_ENDIAN_NAT) == 0) { + return (flags & DUMP_DEBUG_INFO) | DUMP_ENDIAN_BIG; + } + return flags; +} + +static int +dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) +{ + 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_sym *lv_syms = NULL; uint32_t lv_syms_len = 0; + mrb_sym *filenames = NULL; uint16_t filenames_len = 0; + + if (mrb == NULL) { + *bin = NULL; + return MRB_DUMP_GENERAL_FAILURE; + } + + section_irep_size = sizeof(struct rite_section_irep_header); + section_irep_size += get_irep_record_size(mrb, irep); + + /* DEBUG section size */ + if (flags & DUMP_DEBUG_INFO) { + if (debug_info_defined) { + section_lineno_size += sizeof(struct rite_section_debug_header); + /* filename table */ + filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) + 1); + + /* filename table size */ + section_lineno_size += sizeof(uint16_t); + section_lineno_size += get_filename_table_size(mrb, irep, &filenames, &filenames_len); + + 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) { + section_lv_size += sizeof(struct rite_section_lv_header); + create_lv_sym_table(mrb, irep, &lv_syms, &lv_syms_len); + section_lv_size += get_lv_section_size(mrb, irep, lv_syms, lv_syms_len); + } + + malloc_size = sizeof(struct rite_binary_header) + + section_irep_size + section_lineno_size + section_lv_size + + sizeof(struct rite_binary_footer); + cur = *bin = (uint8_t*)mrb_malloc(mrb, malloc_size); + cur += sizeof(struct rite_binary_header); + + result = write_section_irep(mrb, irep, cur, §ion_irep_size, flags); + if (result != MRB_DUMP_OK) { + goto error_exit; + } + cur += section_irep_size; + *bin_size = sizeof(struct rite_binary_header) + + section_irep_size + section_lineno_size + section_lv_size + + sizeof(struct rite_binary_footer); + + /* write DEBUG section */ + if (flags & 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; + } + cur += section_lineno_size; + } + + if (lv_defined) { + result = write_section_lv(mrb, irep, cur, lv_syms, lv_syms_len); + if (result != MRB_DUMP_OK) { + goto error_exit; + } + cur += section_lv_size; + } + + write_footer(mrb, cur); + write_rite_binary_header(mrb, *bin_size, *bin, flags); + +error_exit: + if (result != MRB_DUMP_OK) { + mrb_free(mrb, *bin); + *bin = NULL; + } + mrb_free(mrb, lv_syms); + mrb_free(mrb, filenames); + return result; +} + +int +mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size) +{ + return dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), bin, bin_size); +} + +#ifndef MRB_DISABLE_STDIO + +int +mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp) +{ + uint8_t *bin = NULL; + size_t bin_size = 0; + int result; + + if (fp == NULL) { + return MRB_DUMP_INVALID_ARGUMENT; + } + + result = dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), &bin, &bin_size); + if (result == MRB_DUMP_OK) { + if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) { + result = MRB_DUMP_WRITE_FAULT; + } + } + + mrb_free(mrb, bin); + return result; +} + +static mrb_bool +dump_bigendian_p(uint8_t flags) +{ + switch (flags & DUMP_ENDIAN_NAT) { + case DUMP_ENDIAN_BIG: + return TRUE; + case DUMP_ENDIAN_LIL: + return FALSE; + default: + case DUMP_ENDIAN_NAT: + return bigendian_p(); + } +} + +int +mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname) +{ + uint8_t *bin = NULL; + size_t bin_size = 0, bin_idx = 0; + int result; + + if (fp == NULL || initname == NULL || initname[0] == '\0') { + return MRB_DUMP_INVALID_ARGUMENT; + } + flags = dump_flags(flags, FLAG_BYTEORDER_NATIVE); + result = dump_irep(mrb, irep, flags, &bin, &bin_size); + if (result == MRB_DUMP_OK) { + if (!dump_bigendian_p(flags)) { + if (fprintf(fp, "/* dumped in little endian order.\n" + " use `mrbc -E` option for big endian CPU. */\n") < 0) { + mrb_free(mrb, bin); + return MRB_DUMP_WRITE_FAULT; + } + } + else { + if (fprintf(fp, "/* dumped in big endian order.\n" + " use `mrbc -e` option for better performance on little endian CPU. */\n") < 0) { + mrb_free(mrb, bin); + return MRB_DUMP_WRITE_FAULT; + } + } + if (fprintf(fp, "#include <stdint.h>\n") < 0) { /* for uint8_t under at least Darwin */ + mrb_free(mrb, bin); + return MRB_DUMP_WRITE_FAULT; + } + if (fprintf(fp, + "extern const uint8_t %s[];\n" + "const uint8_t\n" + "#if defined __GNUC__\n" + "__attribute__((aligned(%u)))\n" + "#elif defined _MSC_VER\n" + "__declspec(align(%u))\n" + "#endif\n" + "%s[] = {", + initname, + (uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) { + mrb_free(mrb, bin); + return MRB_DUMP_WRITE_FAULT; + } + while (bin_idx < bin_size) { + if (bin_idx % 16 == 0) { + if (fputs("\n", fp) == EOF) { + mrb_free(mrb, bin); + return MRB_DUMP_WRITE_FAULT; + } + } + if (fprintf(fp, "0x%02x,", bin[bin_idx++]) < 0) { + mrb_free(mrb, bin); + return MRB_DUMP_WRITE_FAULT; + } + } + if (fputs("\n};\n", fp) == EOF) { + mrb_free(mrb, bin); + return MRB_DUMP_WRITE_FAULT; + } + } + + mrb_free(mrb, bin); + return result; +} + +#endif /* MRB_DISABLE_STDIO */ diff --git a/web/server/h2o/libh2o/deps/mruby/src/enum.c b/web/server/h2o/libh2o/deps/mruby/src/enum.c new file mode 100644 index 000000000..adb815bf1 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/enum.c @@ -0,0 +1,14 @@ +/* +** enum.c - Enumerable module +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> + +void +mrb_init_enumerable(mrb_state *mrb) +{ + mrb_define_module(mrb, "Enumerable"); /* 15.3.2 */ +} + diff --git a/web/server/h2o/libh2o/deps/mruby/src/error.c b/web/server/h2o/libh2o/deps/mruby/src/error.c new file mode 100644 index 000000000..2c4fd1a40 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/error.c @@ -0,0 +1,503 @@ +/* +** error.c - Exception class +** +** See Copyright Notice in mruby.h +*/ + +#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 <mruby/throw.h> + +MRB_API mrb_value +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); +} + +MRB_API mrb_value +mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str) +{ + str = mrb_str_to_str(mrb, str); + return mrb_obj_new(mrb, c, 1, &str); +} + +/* + * call-seq: + * Exception.new(msg = nil) -> exception + * + * Construct a new Exception object, optionally passing in + * a message. + */ + +static mrb_value +exc_initialize(mrb_state *mrb, mrb_value exc) +{ + mrb_value mesg; + mrb_int argc; + mrb_value *argv; + + if (mrb_get_args(mrb, "|o*!", &mesg, &argv, &argc) >= 1) { + mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg); + } + return exc; +} + +/* + * Document-method: exception + * + * call-seq: + * exc.exception(string) -> an_exception or 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</code>. + * + */ + +static mrb_value +exc_exception(mrb_state *mrb, mrb_value self) +{ + mrb_value exc; + mrb_value a; + 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); + + return exc; +} + +/* + * call-seq: + * exception.to_s -> string + * + * Returns exception's message (or the name of the exception if + * no message is set). + */ + +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")); + struct RObject *p; + + if (!mrb_string_p(mesg)) { + return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc)); + } + p = mrb_obj_ptr(mesg); + if (!p->c) { + p->c = mrb->string_class; + } + return mesg; +} + +/* + * call-seq: + * exception.message -> string + * + * Returns the result of invoking <code>exception.to_s</code>. + * 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); +} + +/* + * call-seq: + * exception.inspect -> string + * + * Returns this exception's file name, line number, + * message and class name. + * If file name or line number is not set, + * returns message and class name. + */ + +static mrb_value +exc_inspect(mrb_state *mrb, mrb_value exc) +{ + mrb_value str, mesg, file, line; + mrb_bool append_mesg; + const char *cname; + + mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg")); + file = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "file")); + line = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "line")); + + append_mesg = !mrb_nil_p(mesg); + if (append_mesg) { + mesg = mrb_obj_as_string(mrb, mesg); + append_mesg = RSTRING_LEN(mesg) > 0; + } + + cname = mrb_obj_classname(mrb, exc); + str = mrb_str_new_cstr(mrb, cname); + if (mrb_string_p(file) && mrb_fixnum_p(line)) { + if (append_mesg) { + str = mrb_format(mrb, "%S:%S: %S (%S)", file, line, mesg, str); + } + else { + str = mrb_format(mrb, "%S:%S: %S", file, line, str); + } + } + else if (append_mesg) { + str = mrb_format(mrb, "%S: %S", str, mesg); + } + return str; +} + +void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc); + +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); + + while (p < pend) { + if (!mrb_string_p(*p)) goto type_err; + p++; + } + } + mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace); +} + +static mrb_value +exc_set_backtrace(mrb_state *mrb, mrb_value exc) +{ + mrb_value backtrace; + + mrb_get_args(mrb, "o", &backtrace); + set_backtrace(mrb, exc, backtrace); + return backtrace; +} + +static void +exc_debug_info(mrb_state *mrb, struct RObject *exc) +{ + mrb_callinfo *ci = mrb->c->ci; + mrb_code *pc = ci->pc; + + while (ci >= mrb->c->cibase) { + mrb_code *err = ci->err; + + if (!err && pc) err = pc - 1; + if (err && ci->proc && !MRB_PROC_CFUNC_P(ci->proc)) { + mrb_irep *irep = ci->proc->body.irep; + + int32_t const line = mrb_debug_get_line(irep, err - irep->iseq); + char const* file = mrb_debug_get_filename(irep, err - irep->iseq); + if (line != -1 && file) { + mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "file"), mrb_str_new_cstr(mrb, file)); + mrb_obj_iv_set(mrb, exc, mrb_intern_lit(mrb, "line"), mrb_fixnum_value(line)); + return; + } + } + pc = ci->pc; + ci--; + } +} + +void +mrb_exc_set(mrb_state *mrb, mrb_value exc) +{ + if (mrb_nil_p(exc)) { + mrb->exc = 0; + } + else { + mrb->exc = mrb_obj_ptr(exc); + if (!mrb->gc.out_of_memory) { + exc_debug_info(mrb, mrb->exc); + mrb_keep_backtrace(mrb, exc); + } + } +} + +MRB_API mrb_noreturn void +mrb_exc_raise(mrb_state *mrb, mrb_value exc) +{ + if (!mrb_obj_is_kind_of(mrb, exc, mrb->eException_class)) { + mrb_raise(mrb, E_TYPE_ERROR, "exception object expected"); + } + mrb_exc_set(mrb, exc); + if (!mrb->jmp) { + mrb_p(mrb, exc); + abort(); + } + MRB_THROW(mrb->jmp); +} + +MRB_API mrb_noreturn void +mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg) +{ + mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mrb_str_new_cstr(mrb, msg))); +} + +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); + int ai = mrb_gc_arena_save(mrb); + + while (*p) { + const char c = *p++; + + if (c == '%') { + if (*p == 'S') { + mrb_value val; + + size = p - b - 1; + mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); + val = va_arg(ap, mrb_value); + mrb_ary_push(mrb, ary, mrb_obj_as_string(mrb, val)); + b = p + 1; + } + } + else if (c == '\\') { + if (*p) { + size = p - b - 1; + mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); + mrb_ary_push(mrb, ary, mrb_str_new(mrb, p, 1)); + b = ++p; + } + else { + break; + } + } + mrb_gc_arena_restore(mrb, ai); + } + if (b == format) { + return mrb_str_new_cstr(mrb, format); + } + else { + size = p - b; + if (size > 0) { + mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); + mrb_gc_arena_restore(mrb, ai); + } + return mrb_ary_join(mrb, ary, mrb_nil_value()); + } +} + +MRB_API mrb_value +mrb_format(mrb_state *mrb, const char *format, ...) +{ + va_list ap; + mrb_value str; + + va_start(ap, format); + str = mrb_vformat(mrb, format, ap); + va_end(ap); + + 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; + + va_start(args, fmt); + raise_va(mrb, c, fmt, args, 0, NULL); + va_end(args); +} + +MRB_API mrb_noreturn void +mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...) +{ + mrb_value argv[2]; + va_list args; + + va_start(args, fmt); + argv[1] = mrb_symbol_value(id); + raise_va(mrb, E_NAME_ERROR, fmt, args, 1, argv); + va_end(args); +} + +MRB_API void +mrb_warn(mrb_state *mrb, const char *fmt, ...) +{ +#ifndef MRB_DISABLE_STDIO + va_list ap; + mrb_value str; + + va_start(ap, fmt); + str = mrb_vformat(mrb, fmt, ap); + fputs("warning: ", stderr); + fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr); + va_end(ap); +#endif +} + +MRB_API mrb_noreturn void +mrb_bug(mrb_state *mrb, const char *fmt, ...) +{ +#ifndef MRB_DISABLE_STDIO + va_list ap; + mrb_value str; + + va_start(ap, fmt); + str = mrb_vformat(mrb, fmt, ap); + fputs("bug: ", stderr); + fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr); + va_end(ap); +#endif + exit(EXIT_FAILURE); +} + +MRB_API mrb_value +mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv) +{ + mrb_value mesg; + int n; + + mesg = mrb_nil_value(); + switch (argc) { + case 0: + break; + case 1: + if (mrb_nil_p(argv[0])) + break; + if (mrb_string_p(argv[0])) { + mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, argv[0]); + break; + } + n = 0; + goto exception_call; + + case 2: + case 3: + n = 1; +exception_call: + { + mrb_sym exc = mrb_intern_lit(mrb, "exception"); + if (mrb_respond_to(mrb, argv[0], exc)) { + mesg = mrb_funcall_argv(mrb, argv[0], exc, n, argv+1); + } + else { + /* undef */ + mrb_raise(mrb, E_TYPE_ERROR, "exception class/object expected"); + } + } + + break; + default: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 0..3)", mrb_fixnum_value(argc)); + break; + } + if (argc > 0) { + if (!mrb_obj_is_kind_of(mrb, mesg, mrb->eException_class)) + mrb_raise(mrb, mrb->eException_class, "exception object expected"); + if (argc > 2) + set_backtrace(mrb, mesg, argv[2]); + } + + return mesg; +} + +MRB_API void +mrb_sys_fail(mrb_state *mrb, const char *mesg) +{ + struct RClass *sce; + mrb_int no; + + no = (mrb_int)errno; + if (mrb_class_defined(mrb, "SystemCallError")) { + sce = mrb_class_get(mrb, "SystemCallError"); + if (mesg != NULL) { + mrb_funcall(mrb, mrb_obj_value(sce), "_sys_fail", 2, mrb_fixnum_value(no), mrb_str_new_cstr(mrb, mesg)); + } + else { + mrb_funcall(mrb, mrb_obj_value(sce), "_sys_fail", 1, mrb_fixnum_value(no)); + } + } + else { + mrb_raise(mrb, E_RUNTIME_ERROR, mesg); + } +} + +MRB_API mrb_noreturn void +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); + 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); +} + +void +mrb_init_exception(mrb_state *mrb) +{ + 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_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, exception, "message", exc_message, MRB_ARGS_NONE()); + mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE()); + mrb_define_method(mrb, exception, "backtrace", 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 */ + mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ + script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ + mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ + stack_error = mrb_define_class(mrb, "SystemStackError", exception); + mrb->stack_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, stack_error, "stack level too deep")); + + nomem_error = mrb_define_class(mrb, "NoMemoryError", exception); + mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "Out of memory")); +#ifdef MRB_GC_FIXED_ARENA + mrb->arena_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "arena overflow error")); +#endif +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/error.h b/web/server/h2o/libh2o/deps/mruby/src/error.h new file mode 100644 index 000000000..eb755ec7f --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/error.h @@ -0,0 +1,3 @@ +/* this header file is to be removed soon. + added for compatibility purpose (1.0.0) */ +#include <mruby/error.h> diff --git a/web/server/h2o/libh2o/deps/mruby/src/etc.c b/web/server/h2o/libh2o/deps/mruby/src/etc.c new file mode 100644 index 000000000..9475ae304 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/etc.c @@ -0,0 +1,234 @@ +/* +** 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> + +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->data = ptr; + data->type = type; + + return data; +} + +MRB_API void +mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) +{ + if (mrb_type(obj) != MRB_TT_DATA) { + 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)); + } + 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_API void* +mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) +{ + if (mrb_type(obj) != MRB_TT_DATA) { + return NULL; + } + if (DATA_TYPE(obj) != type) { + return NULL; + } + return DATA_PTR(obj); +} + +MRB_API void* +mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) +{ + mrb_data_check_type(mrb, obj, type); + return DATA_PTR(obj); +} + +MRB_API mrb_sym +mrb_obj_to_sym(mrb_state *mrb, mrb_value name) +{ + mrb_sym id; + + switch (mrb_type(name)) { + default: + name = mrb_check_string_type(mrb, name); + if (mrb_nil_p(name)) { + name = mrb_inspect(mrb, name); + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", name); + } + /* fall through */ + case MRB_TT_STRING: + name = mrb_str_intern(mrb, name); + /* fall through */ + case MRB_TT_SYMBOL: + id = mrb_symbol(name); + } + return id; +} + +MRB_API mrb_int +mrb_float_id(mrb_float f) +{ + const char *p = (const char*)&f; + int len = sizeof(f); + mrb_int id = 0; + + /* normalize -0.0 to 0.0 */ + if (f == 0) f = 0.0; + while (len--) { + id = id*65599 + *p; + p++; + } + id = id + (id>>5); + + return id; +} + +MRB_API mrb_int +mrb_obj_id(mrb_value obj) +{ + mrb_int tt = mrb_type(obj); + +#define MakeID2(p,t) (mrb_int)(((intptr_t)(p))^(t)) +#define MakeID(p) MakeID2(p,tt) + + switch (tt) { + case MRB_TT_FREE: + case MRB_TT_UNDEF: + return MakeID(0); /* not define */ + case MRB_TT_FALSE: + if (mrb_nil_p(obj)) + return MakeID(1); + return MakeID(0); + case MRB_TT_TRUE: + return MakeID(1); + 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_FLOAT: + return MakeID(mrb_float_id(mrb_float(obj))); + case MRB_TT_STRING: + case MRB_TT_OBJECT: + case MRB_TT_CLASS: + case MRB_TT_MODULE: + case MRB_TT_ICLASS: + case MRB_TT_SCLASS: + case MRB_TT_PROC: + case MRB_TT_ARRAY: + 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)); + } +} + +#ifdef MRB_WORD_BOXING +MRB_API mrb_value +mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f) +{ + mrb_value v; + + v.value.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class); + v.value.fp->f = f; + return v; +} + +MRB_API mrb_value +mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f) +{ + 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); +} + +MRB_API mrb_value +mrb_word_boxing_cptr_value(mrb_state *mrb, void *p) +{ + mrb_value v; + + v.value.p = mrb_obj_alloc(mrb, MRB_TT_CPTR, mrb->object_class); + v.value.vp->p = p; + return v; +} +#endif /* MRB_WORD_BOXING */ + +MRB_API mrb_bool +mrb_regexp_p(mrb_state *mrb, mrb_value v) +{ + if (mrb->flags & MRB_STATE_NO_REGEXP) { + return FALSE; + } + if ((mrb->flags & MRB_STATE_REGEXP) || mrb_class_defined(mrb, REGEXP_CLASS)) { + mrb->flags |= MRB_STATE_REGEXP; + return mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS)); + } + else { + mrb->flags |= MRB_STATE_REGEXP; + mrb->flags |= MRB_STATE_NO_REGEXP; + } + return FALSE; +} + +#if defined _MSC_VER && _MSC_VER < 1900 + +#ifndef va_copy +static void +mrb_msvc_va_copy(va_list *dest, va_list src) +{ + *dest = src; +} +#define va_copy(dest, src) mrb_msvc_va_copy(&(dest), src) +#endif + +MRB_API int +mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg) +{ + int cnt; + va_list argcp; + va_copy(argcp, arg); + if (n == 0 || (cnt = _vsnprintf_s(s, n, _TRUNCATE, format, argcp)) < 0) { + cnt = _vscprintf(format, arg); + } + va_end(argcp); + return cnt; +} + +MRB_API int +mrb_msvc_snprintf(char *s, size_t n, const char *format, ...) +{ + va_list arg; + int ret; + va_start(arg, format); + ret = mrb_msvc_vsnprintf(s, n, format, arg); + va_end(arg); + return ret; +} + +#endif /* defined _MSC_VER && _MSC_VER < 1900 */ diff --git a/web/server/h2o/libh2o/deps/mruby/src/ext/.gitkeep b/web/server/h2o/libh2o/deps/mruby/src/ext/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/ext/.gitkeep diff --git a/web/server/h2o/libh2o/deps/mruby/src/fmt_fp.c b/web/server/h2o/libh2o/deps/mruby/src/fmt_fp.c new file mode 100644 index 000000000..0a8b22b41 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/fmt_fp.c @@ -0,0 +1,372 @@ +/* + +Most code in this file originates from musl (src/stdio/vfprintf.c) +which, just like mruby itself, is licensed under the MIT license. + +Copyright (c) 2005-2014 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include <limits.h> +#include <string.h> +#include <stdint.h> +#include <math.h> +#include <float.h> +#include <ctype.h> + +#include <mruby.h> +#include <mruby/string.h> + +struct fmt_args { + mrb_state *mrb; + mrb_value str; +}; + +#define MAX(a,b) ((a)>(b) ? (a) : (b)) +#define MIN(a,b) ((a)<(b) ? (a) : (b)) + +/* Convenient bit representation for modifier flags, which all fall + * within 31 codepoints of the space character. */ + +#define ALT_FORM (1U<<('#'-' ')) +#define ZERO_PAD (1U<<('0'-' ')) +#define LEFT_ADJ (1U<<('-'-' ')) +#define PAD_POS (1U<<(' '-' ')) +#define MARK_POS (1U<<('+'-' ')) + +static void +out(struct fmt_args *f, const char *s, size_t l) +{ + mrb_str_cat(f->mrb, f->str, s, l); +} + +#define PAD_SIZE 256 +static void +pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint8_t fl) +{ + char pad[PAD_SIZE]; + if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return; + l = w - l; + memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l); + for (; l >= PAD_SIZE; l -= PAD_SIZE) + out(f, pad, PAD_SIZE); + out(f, pad, l); +} + +static const char xdigits[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static char* +fmt_u(uint32_t x, char *s) +{ + for (; x; x /= 10) *--s = '0' + x % 10; + return s; +} + +/* Do not override this check. The floating point printing code below + * depends on the float.h constants being right. If they are wrong, it + * may overflow the stack. */ +#if LDBL_MANT_DIG == 53 +typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)]; +#endif + +static int +fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t) +{ + uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion + + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion + uint32_t *a, *d, *r, *z; + uint32_t i; + int e2=0, e, j; + ptrdiff_t l; + char buf[9+LDBL_MANT_DIG/4], *s; + const char *prefix="-0X+0X 0X-0x+0x 0x"; + ptrdiff_t pl; + char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr; + + pl=1; + if (signbit(y)) { + y=-y; + } else if (fl & MARK_POS) { + prefix+=3; + } else if (fl & PAD_POS) { + prefix+=6; + } else prefix++, pl=0; + + if (!isfinite(y)) { + const char *ss = (t&32)?"inf":"INF"; + if (y!=y) ss=(t&32)?"nan":"NAN"; + pad(f, ' ', 0, 3+pl, fl&~ZERO_PAD); + out(f, prefix, pl); + out(f, ss, 3); + pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ); + return 3+(int)pl; + } + + y = frexp((double)y, &e2) * 2; + if (y) e2--; + + if ((t|32)=='a') { + long double round = 8.0; + ptrdiff_t re; + + if (t&32) prefix += 9; + pl += 2; + + if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0; + else re=LDBL_MANT_DIG/4-1-p; + + if (re) { + while (re--) round*=16; + if (*prefix=='-') { + y=-y; + y-=round; + y+=round; + y=-y; + } + else { + y+=round; + y-=round; + } + } + + estr=fmt_u(e2<0 ? -e2 : e2, ebuf); + if (estr==ebuf) *--estr='0'; + *--estr = (e2<0 ? '-' : '+'); + *--estr = t+('p'-'a'); + + s=buf; + do { + int x=(int)y; + *s++=xdigits[x]|(t&32); + y=16*(y-x); + if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.'; + } while (y); + + if (p && s-buf-2 < p) + l = (p+2) + (ebuf-estr); + else + l = (s-buf) + (ebuf-estr); + + pad(f, ' ', 0, pl+l, fl); + out(f, prefix, pl); + pad(f, '0', 0, pl+l, fl^ZERO_PAD); + out(f, buf, s-buf); + pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0); + out(f, estr, ebuf-estr); + pad(f, ' ', 0, pl+l, fl^LEFT_ADJ); + return (int)pl+(int)l; + } + if (p<0) p=6; + + if (y) y *= 268435456.0, e2-=28; + + if (e2<0) a=r=z=big; + else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1; + + do { + *z = (uint32_t)y; + y = 1000000000*(y-*z++); + } while (y); + + while (e2>0) { + uint32_t carry=0; + int sh=MIN(29,e2); + for (d=z-1; d>=a; d--) { + uint64_t x = ((uint64_t)*d<<sh)+carry; + *d = x % 1000000000; + carry = (uint32_t)(x / 1000000000); + } + if (carry) *--a = carry; + while (z>a && !z[-1]) z--; + e2-=sh; + } + while (e2<0) { + uint32_t carry=0, *b; + int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9; + for (d=a; d<z; d++) { + uint32_t rm = *d & ((1<<sh)-1); + *d = (*d>>sh) + carry; + carry = (1000000000>>sh) * rm; + } + if (!*a) a++; + if (carry) *z++ = carry; + /* Avoid (slow!) computation past requested precision */ + b = (t|32)=='f' ? r : a; + if (z-b > need) z = b+need; + e2+=sh; + } + + if (a<z) for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++); + else e=0; + + /* Perform rounding: j is precision after the radix (possibly neg) */ + j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p); + if (j < 9*(z-r-1)) { + uint32_t x; + /* We avoid C's broken division of negative numbers */ + d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP); + j += 9*LDBL_MAX_EXP; + j %= 9; + for (i=10, j++; j<9; i*=10, j++); + x = *d % i; + /* Are there any significant digits past j? */ + if (x || d+1!=z) { + long double round = 2/LDBL_EPSILON; + long double small; + if (*d/i & 1) round += 2; + if (x<i/2) small=0.5; + else if (x==i/2 && d+1==z) small=1.0; + else small=1.5; + if (pl && *prefix=='-') round*=-1, small*=-1; + *d -= x; + /* Decide whether to round by probing round+small */ + if (round+small != round) { + *d = *d + i; + while (*d > 999999999) { + *d--=0; + if (d<a) *--a=0; + (*d)++; + } + for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++); + } + } + if (z>d+1) z=d+1; + } + for (; z>a && !z[-1]; z--); + + if ((t|32)=='g') { + if (!p) p++; + if (p>e && e>=-4) { + t--; + p-=e+1; + } + else { + t-=2; + p--; + } + if (!(fl&ALT_FORM)) { + /* Count trailing zeros in last place */ + if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++); + else j=9; + if ((t|32)=='f') + p = MIN(p,MAX(0,9*(z-r-1)-j)); + else + p = MIN(p,MAX(0,9*(z-r-1)+e-j)); + } + } + l = 1 + p + (p || (fl&ALT_FORM)); + if ((t|32)=='f') { + if (e>0) l+=e; + } + else { + estr=fmt_u(e<0 ? -e : e, ebuf); + while(ebuf-estr<2) *--estr='0'; + *--estr = (e<0 ? '-' : '+'); + *--estr = t; + l += ebuf-estr; + } + + pad(f, ' ', 0, pl+l, fl); + out(f, prefix, pl); + pad(f, '0', 0, pl+l, fl^ZERO_PAD); + + if ((t|32)=='f') { + if (a>r) a=r; + for (d=a; d<=r; d++) { + char *ss = fmt_u(*d, buf+9); + if (d!=a) while (ss>buf) *--ss='0'; + else if (ss==buf+9) *--ss='0'; + out(f, ss, buf+9-ss); + } + if (p || (fl&ALT_FORM)) out(f, ".", 1); + for (; d<z && p>0; d++, p-=9) { + char *ss = fmt_u(*d, buf+9); + while (ss>buf) *--ss='0'; + out(f, ss, MIN(9,p)); + } + pad(f, '0', p+9, 9, 0); + } + else { + if (z<=a) z=a+1; + for (d=a; d<z && p>=0; d++) { + char *ss = fmt_u(*d, buf+9); + if (ss==buf+9) *--ss='0'; + if (d!=a) while (ss>buf) *--ss='0'; + else { + out(f, ss++, 1); + if (p>0||(fl&ALT_FORM)) out(f, ".", 1); + } + out(f, ss, MIN(buf+9-ss, p)); + p -= (int)(buf+9-ss); + } + pad(f, '0', p+18, 18, 0); + out(f, estr, ebuf-estr); + } + + pad(f, ' ', 0, pl+l, fl^LEFT_ADJ); + + return (int)pl+(int)l; +} + +static int +fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) +{ + ptrdiff_t p; + + if (*fmt != '%') { + return -1; + } + ++fmt; + + if (*fmt == '.') { + ++fmt; + for (p = 0; ISDIGIT(*fmt); ++fmt) { + p = 10 * p + (*fmt - '0'); + } + } + else { + p = -1; + } + + switch (*fmt) { + case 'e': case 'f': case 'g': case 'a': + case 'E': case 'F': case 'G': case 'A': + return fmt_fp(f, flo, p, 0, *fmt); + default: + return -1; + } +} + +mrb_value +mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt) +{ + struct fmt_args f; + + f.mrb = mrb; + f.str = mrb_str_new_capa(mrb, 24); + if (fmt_core(&f, fmt, mrb_float(flo)) < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string"); + } + return f.str; +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/gc.c b/web/server/h2o/libh2o/deps/mruby/src/gc.c new file mode 100644 index 000000000..d602bfb70 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/gc.c @@ -0,0 +1,1824 @@ +/* +** gc.c - garbage collector for mruby +** +** See Copyright Notice in mruby.h +*/ + +#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> +#include <mruby/throw.h> + +/* + = Tri-color Incremental Garbage Collection + + mruby's GC is Tri-color Incremental GC with Mark & Sweep. + Algorithm details are omitted. + Instead, the implementation part is described below. + + == Object's Color + + Each object can be painted in three colors: + + * White - Unmarked. + * Gray - Marked, But the child objects are unmarked. + * Black - Marked, the child objects are also marked. + + == Two White Types + + There're 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). + + A and B will be switched just at the beginning of the next GC cycle. At + that time, all the dead objects have been swept, while the newly created + objects in the current GC cycle which finally remains White are now + regarded as dead objects. Instead of traversing all the White-A objects and + painting them as White-B, just switch the meaning of White-A and White-B as + this will be much cheaper. + + As a result, the objects we sweep in the current GC cycle are always + left from the previous GC cycle. This allows us to sweep objects + incrementally, without the disturbance of the newly created objects. + + == Execution Timing + + GC Execution Time and Each step interval are decided by live objects count. + List of Adjustment API: + + * gc_interval_ratio_set + * gc_step_ratio_set + + For details, see the comments for each function. + + == Write Barrier + + 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, + two different types of write barrier are available: + + * mrb_field_write_barrier - target B object for a mark. + * mrb_write_barrier - target A object for a mark. + + == Generational Mode + + mruby's GC offers an Generational Mode while re-using the tri-color GC + infrastructure. It will treat the Black objects as Old objects after each + sweep phase, instead of painting them White. The key ideas are still the same + as traditional generational GC: + + * Minor GC - just traverse the Young objects (Gray objects) in the mark + phase, then only sweep the newly created objects, and leave + the Old objects live. + + * Major GC - same as a full regular GC cycle. + + The difference from "traditional" generational GC is, that the major GC + in mruby is triggered incrementally in a tri-color manner. + + + For details, see the comments for each function. + +*/ + +struct free_obj { + MRB_OBJECT_HEADER; + struct RBasic *next; +}; + +typedef struct { + union { + struct free_obj free; + struct RBasic basic; + struct RObject object; + struct RClass klass; + struct RString string; + struct RArray array; + struct RHash hash; + struct RRange range; + struct RData data; + struct RProc proc; + struct REnv env; + struct RException exc; + struct RBreak brk; +#ifdef MRB_WORD_BOXING + struct RFloat floatv; + struct RCptr cptr; +#endif + } as; +} RVALUE; + +#ifdef GC_PROFILE +#include <stdio.h> +#include <sys/time.h> + +static double program_invoke_time = 0; +static double gc_time = 0; +static double gc_total_time = 0; + +static double +gettimeofday_time(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec + tv.tv_usec * 1e-6; +} + +#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(gc));\ + fprintf(stderr, "is_major_gc: %d\n", is_major_gc(gc));\ +} while(0) + +#define GC_TIME_START do {\ + gc_time = gettimeofday_time();\ +} while(0) + +#define GC_TIME_STOP_AND_REPORT do {\ + gc_time = gettimeofday_time() - gc_time;\ + gc_total_time += gc_time;\ + 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) +#else +#define GC_INVOKE_TIME_REPORT(s) +#define GC_TIME_START +#define GC_TIME_STOP_AND_REPORT +#endif + +#ifdef GC_DEBUG +#define DEBUG(x) (x) +#else +#define DEBUG(x) +#endif + +#ifndef MRB_HEAP_PAGE_SIZE +#define MRB_HEAP_PAGE_SIZE 1024 +#endif + +#define GC_STEP_SIZE 1024 + +/* white: 011, 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_WHITES (GC_WHITE_A | GC_WHITE_B) +#define GC_COLOR_MASK 7 + +#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 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_API void* +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->gc.heaps) { + mrb_full_gc(mrb); + p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud); + } + + 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 (len == 0) return p2; + if (p2 == NULL) { + if (mrb->gc.out_of_memory) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); + /* mrb_panic(mrb); */ + } + else { + mrb->gc.out_of_memory = TRUE; + mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); + } + } + else { + mrb->gc.out_of_memory = FALSE; + } + + return p2; +} + +MRB_API void* +mrb_malloc(mrb_state *mrb, size_t len) +{ + return mrb_realloc(mrb, 0, len); +} + +MRB_API void* +mrb_malloc_simple(mrb_state *mrb, size_t len) +{ + return mrb_realloc_simple(mrb, 0, len); +} + +MRB_API void* +mrb_calloc(mrb_state *mrb, size_t nelem, size_t len) +{ + void *p; + + if (nelem > 0 && len > 0 && + nelem <= SIZE_MAX / len) { + size_t size; + size = nelem * len; + p = mrb_malloc(mrb, size); + + memset(p, 0, size); + } + else { + p = NULL; + } + + return p; +} + +MRB_API void +mrb_free(mrb_state *mrb, void *p) +{ + (mrb->allocf)(mrb, p, 0, mrb->allocf_ud); +} + +MRB_API mrb_bool +mrb_object_dead_p(mrb_state *mrb, struct RBasic *object) { + return is_dead(&mrb->gc, object); +} + +static void +link_heap_page(mrb_gc *gc, mrb_heap_page *page) +{ + page->next = gc->heaps; + if (gc->heaps) + gc->heaps->prev = page; + gc->heaps = page; +} + +static void +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 (gc->heaps == page) + gc->heaps = page->next; + page->prev = NULL; + page->next = NULL; +} + +static void +link_free_heap_page(mrb_gc *gc, mrb_heap_page *page) +{ + page->free_next = gc->free_heaps; + if (gc->free_heaps) { + gc->free_heaps->free_prev = page; + } + gc->free_heaps = page; +} + +static void +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 (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, mrb_gc *gc) +{ + 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 = 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(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(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_gc_init(mrb_state *mrb, mrb_gc *gc) +{ +#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 + gc->generational = TRUE; + gc->full = TRUE; +#endif + +#ifdef GC_PROFILE + program_invoke_time = gettimeofday_time(); +#endif +} + +static void obj_free(mrb_state *mrb, struct RBasic *obj, int end); + +void +free_heap(mrb_state *mrb, mrb_gc *gc) +{ + mrb_heap_page *page = gc->heaps; + mrb_heap_page *tmp; + RVALUE *p, *e; + + while (page) { + tmp = page; + page = page->next; + 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, 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, mrb_gc *gc, struct RBasic *p) +{ +#ifdef MRB_GC_FIXED_ARENA + if (gc->arena_idx >= MRB_GC_ARENA_SIZE) { + /* 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 (gc->arena_idx >= gc->arena_capa) { + /* extend arena */ + gc->arena_capa = (int)(gc->arena_capa * 1.5); + gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*gc->arena_capa); + } +#endif + 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->gc, mrb_basic_ptr(obj)); +} + +#define GC_ROOT_NAME "_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_intern_lit(mrb, GC_ROOT_NAME); + mrb_value table = mrb_gv_get(mrb, root); + + if (mrb_nil_p(table) || mrb_type(table) != MRB_TT_ARRAY) { + table = mrb_ary_new(mrb); + mrb_gv_set(mrb, root, table); + } + 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_intern_lit(mrb, GC_ROOT_NAME); + mrb_value table = mrb_gv_get(mrb, root); + struct RArray *a; + mrb_int i; + + if (mrb_nil_p(table)) return; + if (mrb_type(table) != MRB_TT_ARRAY) { + mrb_gv_set(mrb, root, mrb_nil_value()); + return; + } + a = mrb_ary_ptr(table); + mrb_ary_modify(mrb, a); + for (i = 0; i < ARY_LEN(a); i++) { + if (mrb_obj_eq(mrb, ARY_PTR(a)[i], obj)) { + 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 } } }; + 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 %S", mrb_obj_value(cls)); + } + } + +#ifdef MRB_GC_STRESS + mrb_full_gc(mrb); +#endif + if (gc->threshold < gc->live) { + mrb_incremental_gc(mrb); + } + if (gc->free_heaps == NULL) { + add_heap(mrb, gc); + } + + 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); + } + + gc->live++; + gc_protect(mrb, gc, p); + *(RVALUE *)p = RVALUE_zero; + p->tt = ttype; + p->c = cls; + paint_partial_white(gc, p); + return p; +} + +static inline void +add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) +{ +#ifdef MRB_GC_STRESS + if (obj->tt > MRB_TT_MAXDEFINE) { + abort(); + } +#endif + paint_gray(obj); + obj->gcnext = gc->gray_list; + gc->gray_list = obj; +} + +static void +mark_context_stack(mrb_state *mrb, struct mrb_context *c) +{ + size_t i; + size_t e; + mrb_value nil; + int nregs; + + if (c->stack == NULL) return; + e = c->stack - c->stbase; + if (c->ci) { + nregs = c->ci->argc + 2; + if (c->ci->nregs > nregs) + nregs = c->ci->nregs; + e += nregs; + } + if (c->stbase + e > c->stend) e = c->stend - c->stbase; + for (i=0; i<e; i++) { + mrb_value v = c->stbase[i]; + + if (!mrb_immediate_p(v)) { + 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; + mrb_callinfo *ci; + + if (c->status == MRB_FIBER_TERMINATED) return; + + /* mark VM stack */ + mark_context_stack(mrb, c); + + /* mark call stack */ + if (c->cibase) { + for (ci = c->cibase; ci <= c->ci; ci++) { + mrb_gc_mark(mrb, (struct RBasic*)ci->env); + mrb_gc_mark(mrb, (struct RBasic*)ci->proc); + mrb_gc_mark(mrb, (struct RBasic*)ci->target_class); + } + } + /* mark ensure stack */ + for (i=0; i<c->esize; i++) { + if (c->ensure[i] == NULL) break; + mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]); + } + /* mark fibers */ + mrb_gc_mark(mrb, (struct RBasic*)c->fib); + if (c->prev) { + mark_context(mrb, c->prev); + } +} + +static void +gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) +{ + mrb_assert(is_gray(obj)); + paint_black(obj); + gc->gray_list = obj->gcnext; + mrb_gc_mark(mrb, (struct RBasic*)obj->c); + switch (obj->tt) { + case MRB_TT_ICLASS: + { + struct RClass *c = (struct RClass*)obj; + if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN)) + mrb_gc_mark_mt(mrb, c); + mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + } + break; + + case MRB_TT_CLASS: + case MRB_TT_MODULE: + case MRB_TT_SCLASS: + { + struct RClass *c = (struct RClass*)obj; + + mrb_gc_mark_mt(mrb, c); + mrb_gc_mark(mrb, (struct RBasic*)c->super); + } + /* fall through */ + + case MRB_TT_OBJECT: + case MRB_TT_DATA: + case MRB_TT_EXCEPTION: + mrb_gc_mark_iv(mrb, (struct RObject*)obj); + break; + + case MRB_TT_PROC: + { + struct RProc *p = (struct RProc*)obj; + + mrb_gc_mark(mrb, (struct RBasic*)p->env); + mrb_gc_mark(mrb, (struct RBasic*)p->target_class); + if (!MRB_PROC_CFUNC_P(p) && p->body.irep) { + mrb_gc_mark(mrb, (struct RBasic*)p->body.irep->target_class); + } + } + break; + + case MRB_TT_ENV: + { + struct REnv *e = (struct REnv*)obj; + mrb_int i, len; + + if (MRB_ENV_STACK_SHARED_P(e)) { + if (e->cxt.c->fib) { + mrb_gc_mark(mrb, (struct RBasic*)e->cxt.c->fib); + } + break; + } + len = MRB_ENV_STACK_LEN(e); + for (i=0; i<len; i++) { + mrb_gc_mark_value(mrb, e->stack[i]); + } + } + break; + + case MRB_TT_FIBER: + { + struct mrb_context *c = ((struct RFiber*)obj)->cxt; + + if (c) mark_context(mrb, c); + } + break; + + case MRB_TT_ARRAY: + { + struct RArray *a = (struct RArray*)obj; + size_t i, e; + + for (i=0,e=ARY_LEN(a); i<e; i++) { + mrb_gc_mark_value(mrb, ARY_PTR(a)[i]); + } + } + break; + + case MRB_TT_HASH: + mrb_gc_mark_iv(mrb, (struct RObject*)obj); + mrb_gc_mark_hash(mrb, (struct RHash*)obj); + break; + + case MRB_TT_STRING: + break; + + case MRB_TT_RANGE: + { + struct RRange *r = (struct RRange*)obj; + + if (r->edges) { + mrb_gc_mark_value(mrb, r->edges->beg); + mrb_gc_mark_value(mrb, r->edges->end); + } + } + break; + + default: + break; + } +} + +MRB_API void +mrb_gc_mark(mrb_state *mrb, struct RBasic *obj) +{ + if (obj == 0) return; + if (!is_white(obj)) return; + mrb_assert((obj)->tt != MRB_TT_FREE); + add_gray_list(mrb, &mrb->gc, obj); +} + +static void +obj_free(mrb_state *mrb, struct RBasic *obj, int end) +{ + DEBUG(fprintf(stderr, "obj_free(%p,tt=%d)\n",obj,obj->tt)); + switch (obj->tt) { + /* immediate - no mark */ + case MRB_TT_TRUE: + case MRB_TT_FIXNUM: + case MRB_TT_SYMBOL: + /* cannot happen */ + return; + + case MRB_TT_FLOAT: +#ifdef MRB_WORD_BOXING + break; +#else + return; +#endif + + case MRB_TT_OBJECT: + mrb_gc_free_iv(mrb, (struct RObject*)obj); + break; + + case MRB_TT_EXCEPTION: + mrb_gc_free_iv(mrb, (struct RObject*)obj); + break; + + case MRB_TT_CLASS: + case MRB_TT_MODULE: + case MRB_TT_SCLASS: + mrb_gc_free_mt(mrb, (struct RClass*)obj); + mrb_gc_free_iv(mrb, (struct RObject*)obj); + break; + case MRB_TT_ICLASS: + if (MRB_FLAG_TEST(obj, MRB_FLAG_IS_ORIGIN)) + mrb_gc_free_mt(mrb, (struct RClass*)obj); + break; + case MRB_TT_ENV: + { + struct REnv *e = (struct REnv*)obj; + + if (MRB_ENV_STACK_SHARED_P(e)) { + /* cannot be freed */ + return; + } + mrb_free(mrb, e->stack); + e->stack = NULL; + } + break; + + case MRB_TT_FIBER: + { + struct mrb_context *c = ((struct RFiber*)obj)->cxt; + + if (!end && c && c != mrb->root_c) { + mrb_callinfo *ci = c->ci; + mrb_callinfo *ce = c->cibase; + + while (ce <= ci) { + struct REnv *e = ci->env; + if (e && !is_dead(&mrb->gc, e) && + e->tt == MRB_TT_ENV && MRB_ENV_STACK_SHARED_P(e)) { + 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)->as.heap.aux.shared); + else if (!ARY_EMBED_P(obj)) + mrb_free(mrb, ((struct RArray*)obj)->as.heap.ptr); + break; + + case MRB_TT_HASH: + mrb_gc_free_iv(mrb, (struct RObject*)obj); + mrb_gc_free_hash(mrb, (struct RHash*)obj); + break; + + case MRB_TT_STRING: + mrb_gc_free_str(mrb, (struct RString*)obj); + break; + + case MRB_TT_PROC: + { + struct RProc *p = (struct RProc*)obj; + + if (!MRB_PROC_CFUNC_P(p) && p->body.irep) { + mrb_irep_decref(mrb, p->body.irep); + } + } + break; + + case MRB_TT_RANGE: + mrb_free(mrb, ((struct RRange*)obj)->edges); + break; + + case MRB_TT_DATA: + { + struct RData *d = (struct RData*)obj; + if (d->type && d->type->dfree) { + d->type->dfree(mrb, d->data); + } + mrb_gc_free_iv(mrb, (struct RObject*)obj); + } + break; + + default: + break; + } + obj->tt = MRB_TT_FREE; +} + +static void +root_scan_phase(mrb_state *mrb, mrb_gc *gc) +{ + int i, e; + + 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=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); + + mrb_gc_mark(mrb, (struct RBasic*)mrb->float_class); + mrb_gc_mark(mrb, (struct RBasic*)mrb->fixnum_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->c); + if (mrb->root_c != mrb->c) { + mark_context(mrb, mrb->root_c); + } +} + +static size_t +gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) +{ + size_t children = 0; + + gc_mark_children(mrb, gc, obj); + + switch (obj->tt) { + case MRB_TT_ICLASS: + children++; + break; + + case MRB_TT_CLASS: + case MRB_TT_SCLASS: + case MRB_TT_MODULE: + { + struct RClass *c = (struct RClass*)obj; + + children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj); + children += mrb_gc_mark_mt_size(mrb, c); + children++; + } + break; + + case MRB_TT_OBJECT: + case MRB_TT_DATA: + case MRB_TT_EXCEPTION: + children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj); + break; + + case MRB_TT_ENV: + children += (int)obj->flags; + break; + + case MRB_TT_FIBER: + { + struct mrb_context *c = ((struct RFiber*)obj)->cxt; + size_t i; + mrb_callinfo *ci; + + if (!c) break; + /* mark stack */ + i = c->stack - c->stbase; + if (c->ci) i += c->ci->nregs; + if (c->stbase + i > c->stend) i = c->stend - c->stbase; + children += i; + + /* mark ensure stack */ + children += c->eidx; + + /* mark closure */ + if (c->cibase) { + for (i=0, ci = c->cibase; ci <= c->ci; i++, ci++) + ; + } + children += i; + } + break; + + case MRB_TT_ARRAY: + { + struct RArray *a = (struct RArray*)obj; + children += ARY_LEN(a); + } + break; + + case MRB_TT_HASH: + children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj); + children += mrb_gc_mark_hash_size(mrb, (struct RHash*)obj); + break; + + case MRB_TT_PROC: + case MRB_TT_RANGE: + children+=2; + break; + + default: + break; + } + return children; +} + + +static void +gc_mark_gray_list(mrb_state *mrb, mrb_gc *gc) { + while (gc->gray_list) { + if (is_gray(gc->gray_list)) + gc_mark_children(mrb, gc, gc->gray_list); + else + gc->gray_list = gc->gray_list->gcnext; + } +} + + +static size_t +incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) +{ + size_t tried_marks = 0; + + while (gc->gray_list && tried_marks < limit) { + tried_marks += gc_gray_mark(mrb, gc, gc->gray_list); + } + + return tried_marks; +} + +static void +final_marking_phase(mrb_state *mrb, mrb_gc *gc) +{ + int i, e; + + /* mark arena */ + for (i=0,e=gc->arena_idx; i<e; i++) { + mrb_gc_mark(mrb, gc->arena[i]); + } + mrb_gc_mark_gv(mrb); + mark_context(mrb, mrb->c); + mark_context(mrb, mrb->root_c); + mrb_gc_mark(mrb, (struct RBasic*)mrb->exc); + gc_mark_gray_list(mrb, gc); + mrb_assert(gc->gray_list == NULL); + gc->gray_list = gc->atomic_gray_list; + 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, mrb_gc *gc) +{ + 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, mrb_gc *gc, size_t limit) +{ + mrb_heap_page *page = gc->sweeps; + size_t tried_sweep = 0; + + while (page && (tried_sweep < limit)) { + 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(gc) && page->old) { + /* skip a slot which doesn't contain any young object */ + p = e; + dead_slot = FALSE; + } + while (p<e) { + if (is_dead(gc, &p->as.basic)) { + if (p->as.basic.tt != MRB_TT_FREE) { + 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(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) { + mrb_heap_page *next = page->next; + + 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(gc, page); + } + if (page->freelist == NULL && is_minor_gc(gc)) + page->old = TRUE; + else + page->old = FALSE; + page = page->next; + } + tried_sweep += MRB_HEAP_PAGE_SIZE; + gc->live -= freed; + gc->live_after_mark -= freed; + } + gc->sweeps = page; + return tried_sweep; +} + +static size_t +incremental_gc(mrb_state *mrb, mrb_gc *gc, size_t limit) +{ + 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 MRB_GC_STATE_MARK: + if (gc->gray_list) { + return incremental_marking_phase(mrb, gc, limit); + } + else { + final_marking_phase(mrb, gc); + prepare_incremental_sweep(mrb, gc); + return 0; + } + case MRB_GC_STATE_SWEEP: { + size_t tried_sweep = 0; + tried_sweep = incremental_sweep_phase(mrb, gc, limit); + if (tried_sweep == 0) + gc->state = MRB_GC_STATE_ROOT; + return tried_sweep; + } + default: + /* unknown state */ + mrb_assert(0); + return 0; + } +} + +static void +incremental_gc_until(mrb_state *mrb, mrb_gc *gc, mrb_gc_state to_state) +{ + do { + incremental_gc(mrb, gc, SIZE_MAX); + } while (gc->state != to_state); +} + +static void +incremental_gc_step(mrb_state *mrb, mrb_gc *gc) +{ + size_t limit = 0, result = 0; + limit = (GC_STEP_SIZE/100) * gc->step_ratio; + while (result < limit) { + result += incremental_gc(mrb, gc, limit); + if (gc->state == MRB_GC_STATE_ROOT) + break; + } + + gc->threshold = gc->live + GC_STEP_SIZE; +} + +static void +clear_all_old(mrb_state *mrb, mrb_gc *gc) +{ + mrb_bool origin_mode = gc->generational; + + mrb_assert(is_generational(gc)); + if (is_major_gc(gc)) { + /* finish the half baked GC */ + 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. */ + 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 */ + gc->atomic_gray_list = gc->gray_list = NULL; +} + +MRB_API void +mrb_incremental_gc(mrb_state *mrb) +{ + 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(gc)) { + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); + } + else { + incremental_gc_step(mrb, gc); + } + + 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(gc)) { + gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; + gc->full = FALSE; + } + else if (is_minor_gc(gc)) { + if (gc->live > gc->majorgc_old_threshold) { + clear_all_old(mrb, gc); + gc->full = TRUE; + } + } + } + + GC_TIME_STOP_AND_REPORT; +} + +/* Perform a full gc cycle */ +MRB_API void +mrb_full_gc(mrb_state *mrb) +{ + mrb_gc *gc = &mrb->gc; + + if (gc->disabled || gc->iterating) return; + + GC_INVOKE_TIME_REPORT("mrb_full_gc()"); + GC_TIME_START; + + if (is_generational(gc)) { + /* clear all the old objects back to young */ + clear_all_old(mrb, gc); + gc->full = TRUE; + } + else if (gc->state != MRB_GC_STATE_ROOT) { + /* finish half baked GC cycle */ + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); + } + + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); + gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; + + if (is_generational(gc)) { + gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; + gc->full = FALSE; + } + + GC_TIME_STOP_AND_REPORT; +} + +MRB_API void +mrb_garbage_collect(mrb_state *mrb) +{ + mrb_full_gc(mrb); +} + +/* + * Field write barrier + * Paint obj(Black) -> value(White) to obj(Black) -> value(Gray). + */ + +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(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(gc) || gc->state == MRB_GC_STATE_MARK) { + add_gray_list(mrb, gc, value); + } + else { + mrb_assert(gc->state == MRB_GC_STATE_SWEEP); + paint_partial_white(gc, obj); /* for never write barriers */ + } +} + +/* + * Write barrier + * Paint obj(Black) to obj(Gray). + * + * The object that is painted gray will be traversed atomically in final + * mark phase. So you use this write barrier if it's frequency written spot. + * e.g. Set element on Array. + */ + +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(gc, obj)); + mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); + paint_gray(obj); + obj->gcnext = gc->atomic_gray_list; + gc->atomic_gray_list = obj; +} + +/* + * call-seq: + * GC.start -> nil + * + * Initiates full garbage collection. + * + */ + +static mrb_value +gc_start(mrb_state *mrb, mrb_value obj) +{ + mrb_full_gc(mrb); + return mrb_nil_value(); +} + +/* + * call-seq: + * GC.enable -> true or false + * + * Enables garbage collection, returning <code>true</code> if garbage + * collection was previously disabled. + * + * GC.disable #=> false + * GC.enable #=> true + * GC.enable #=> false + * + */ + +static mrb_value +gc_enable(mrb_state *mrb, mrb_value obj) +{ + mrb_bool old = mrb->gc.disabled; + + mrb->gc.disabled = FALSE; + + return mrb_bool_value(old); +} + +/* + * call-seq: + * GC.disable -> true or false + * + * Disables garbage collection, returning <code>true</code> if garbage + * collection was already disabled. + * + * GC.disable #=> false + * GC.disable #=> true + * + */ + +static mrb_value +gc_disable(mrb_state *mrb, mrb_value obj) +{ + mrb_bool old = mrb->gc.disabled; + + mrb->gc.disabled = TRUE; + + return mrb_bool_value(old); +} + +/* + * call-seq: + * GC.interval_ratio -> fixnum + * + * Returns ratio of GC interval. Default value is 200(%). + * + */ + +static mrb_value +gc_interval_ratio_get(mrb_state *mrb, mrb_value obj) +{ + return mrb_fixnum_value(mrb->gc.interval_ratio); +} + +/* + * call-seq: + * GC.interval_ratio = fixnum -> nil + * + * Updates ratio of GC interval. Default value is 200(%). + * GC start as soon as after end all step of GC if you set 100(%). + * + */ + +static mrb_value +gc_interval_ratio_set(mrb_state *mrb, mrb_value obj) +{ + mrb_int ratio; + + mrb_get_args(mrb, "i", &ratio); + mrb->gc.interval_ratio = ratio; + return mrb_nil_value(); +} + +/* + * call-seq: + * GC.step_ratio -> fixnum + * + * Returns step span ratio of Incremental GC. Default value is 200(%). + * + */ + +static mrb_value +gc_step_ratio_get(mrb_state *mrb, mrb_value obj) +{ + return mrb_fixnum_value(mrb->gc.step_ratio); +} + +/* + * call-seq: + * GC.step_ratio = fixnum -> nil + * + * Updates step span ratio of Incremental GC. Default value is 200(%). + * 1 step of incrementalGC becomes long if a rate is big. + * + */ + +static mrb_value +gc_step_ratio_set(mrb_state *mrb, mrb_value obj) +{ + mrb_int ratio; + + mrb_get_args(mrb, "i", &ratio); + mrb->gc.step_ratio = ratio; + return mrb_nil_value(); +} + +static void +change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable) +{ + if (gc->disabled || gc->iterating) { + mrb_raise(mrb, E_RUNTIME_ERROR, "generational mode changed when GC disabled"); + return; + } + if (is_generational(gc) && !enable) { + clear_all_old(mrb, gc); + mrb_assert(gc->state == MRB_GC_STATE_ROOT); + gc->full = FALSE; + } + else if (!is_generational(gc) && enable) { + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); + gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; + gc->full = FALSE; + } + gc->generational = enable; +} + +/* + * call-seq: + * GC.generational_mode -> true or false + * + * Returns generational or normal gc mode. + * + */ + +static mrb_value +gc_generational_mode_get(mrb_state *mrb, mrb_value self) +{ + return mrb_bool_value(mrb->gc.generational); +} + +/* + * call-seq: + * GC.generational_mode = true or false -> true or false + * + * Changes to generational or normal gc mode. + * + */ + +static mrb_value +gc_generational_mode_set(mrb_state *mrb, mrb_value self) +{ + mrb_bool enable; + + mrb_get_args(mrb, "b", &enable); + if (mrb->gc.generational != enable) + change_gen_gc_mode(mrb, &mrb->gc, enable); + + return mrb_bool_value(enable); +} + + +static void +gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data) +{ + mrb_heap_page* page; + + mrb_full_gc(mrb); + page = gc->heaps; + while (page != NULL) { + RVALUE *p; + int i; + + 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; + } +} + +void +mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data) +{ + mrb_bool iterating = mrb->gc.iterating; + + mrb->gc.iterating = TRUE; + if (iterating) { + gc_each_objects(mrb, &mrb->gc, callback, data); + } + else { + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + gc_each_objects(mrb, &mrb->gc, callback, data); + mrb->jmp = prev_jmp; + mrb->gc.iterating = iterating; + } MRB_CATCH(&c_jmp) { + mrb->gc.iterating = iterating; + mrb->jmp = prev_jmp; + MRB_THROW(prev_jmp); + } MRB_END_EXC(&c_jmp); + } +} + +#ifdef GC_TEST +#ifdef GC_DEBUG +static mrb_value gc_test(mrb_state *, mrb_value); +#endif +#endif + +void +mrb_init_gc(mrb_state *mrb) +{ + struct RClass *gc; + + gc = mrb_define_module(mrb, "GC"); + + mrb_define_class_method(mrb, gc, "start", gc_start, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, gc, "enable", gc_enable, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, gc, "disable", gc_disable, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, gc, "interval_ratio", gc_interval_ratio_get, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, gc, "interval_ratio=", gc_interval_ratio_set, MRB_ARGS_REQ(1)); + mrb_define_class_method(mrb, gc, "step_ratio", gc_step_ratio_get, MRB_ARGS_NONE()); + mrb_define_class_method(mrb, gc, "step_ratio=", gc_step_ratio_set, MRB_ARGS_REQ(1)); + mrb_define_class_method(mrb, gc, "generational_mode=", gc_generational_mode_set, MRB_ARGS_REQ(1)); + mrb_define_class_method(mrb, gc, "generational_mode", gc_generational_mode_get, MRB_ARGS_NONE()); +#ifdef GC_TEST +#ifdef GC_DEBUG + mrb_define_class_method(mrb, gc, "test", gc_test, MRB_ARGS_NONE()); +#endif +#endif +} + +#ifdef GC_TEST +#ifdef GC_DEBUG +void +test_mrb_field_write_barrier(void) +{ + mrb_state *mrb = mrb_open(); + struct RBasic *obj, *value; + mrb_gc *gc = &mrb->gc; + + puts("test_mrb_field_write_barrier"); + gc->generational = FALSE; + obj = mrb_basic_ptr(mrb_ary_new(mrb)); + value = mrb_basic_ptr(mrb_str_new_lit(mrb, "value")); + paint_black(obj); + paint_partial_white(gc, value); + + + puts(" in MRB_GC_STATE_MARK"); + gc->state = MRB_GC_STATE_MARK; + mrb_field_write_barrier(mrb, obj, value); + + mrb_assert(is_gray(value)); + + + puts(" in MRB_GC_STATE_SWEEP"); + paint_partial_white(gc, value); + gc->state = MRB_GC_STATE_SWEEP; + mrb_field_write_barrier(mrb, obj, value); + + mrb_assert(obj->color & gc->current_white_part); + mrb_assert(value->color & gc->current_white_part); + + + puts(" fail with black"); + gc->state = MRB_GC_STATE_MARK; + paint_white(obj); + paint_partial_white(gc, value); + mrb_field_write_barrier(mrb, obj, value); + + mrb_assert(obj->color & gc->current_white_part); + + + puts(" fail with gray"); + gc->state = MRB_GC_STATE_MARK; + paint_black(obj); + paint_gray(value); + mrb_field_write_barrier(mrb, obj, value); + + mrb_assert(is_gray(value)); + + + { + puts("test_mrb_field_write_barrier_value"); + obj = mrb_basic_ptr(mrb_ary_new(mrb)); + mrb_value value = mrb_str_new_lit(mrb, "value"); + paint_black(obj); + paint_partial_white(gc, mrb_basic_ptr(value)); + + gc->state = MRB_GC_STATE_MARK; + mrb_field_write_barrier_value(mrb, obj, value); + + mrb_assert(is_gray(mrb_basic_ptr(value))); + } + + mrb_close(mrb); +} + +void +test_mrb_write_barrier(void) +{ + mrb_state *mrb = mrb_open(); + struct RBasic *obj; + mrb_gc *gc = &mrb->gc; + + puts("test_mrb_write_barrier"); + obj = mrb_basic_ptr(mrb_ary_new(mrb)); + paint_black(obj); + + puts(" in MRB_GC_STATE_MARK"); + gc->state = MRB_GC_STATE_MARK; + mrb_write_barrier(mrb, obj); + + mrb_assert(is_gray(obj)); + mrb_assert(gc->atomic_gray_list == obj); + + + puts(" fail with gray"); + paint_gray(obj); + mrb_write_barrier(mrb, obj); + + mrb_assert(is_gray(obj)); + + mrb_close(mrb); +} + +void +test_add_gray_list(void) +{ + mrb_state *mrb = mrb_open(); + struct RBasic *obj1, *obj2; + mrb_gc *gc = &mrb->gc; + + puts("test_add_gray_list"); + change_gen_gc_mode(mrb, gc, FALSE); + mrb_assert(gc->gray_list == NULL); + obj1 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test")); + add_gray_list(mrb, gc, obj1); + mrb_assert(gc->gray_list == obj1); + mrb_assert(is_gray(obj1)); + + obj2 = mrb_basic_ptr(mrb_str_new_lit(mrb, "test")); + add_gray_list(mrb, gc, obj2); + mrb_assert(gc->gray_list == obj2); + mrb_assert(gc->gray_list->gcnext == obj1); + mrb_assert(is_gray(obj2)); + + mrb_close(mrb); +} + +void +test_gc_gray_mark(void) +{ + mrb_state *mrb = mrb_open(); + mrb_value obj_v, value_v; + struct RBasic *obj; + size_t gray_num = 0; + mrb_gc *gc = &mrb->gc; + + puts("test_gc_gray_mark"); + + puts(" in MRB_TT_CLASS"); + obj = (struct RBasic*)mrb->object_class; + paint_gray(obj); + gray_num = gc_gray_mark(mrb, gc, obj); + mrb_assert(is_black(obj)); + mrb_assert(gray_num > 1); + + puts(" in MRB_TT_ARRAY"); + obj_v = mrb_ary_new(mrb); + value_v = mrb_str_new_lit(mrb, "test"); + paint_gray(mrb_basic_ptr(obj_v)); + paint_partial_white(gc, mrb_basic_ptr(value_v)); + mrb_ary_push(mrb, obj_v, value_v); + gray_num = gc_gray_mark(mrb, gc, mrb_basic_ptr(obj_v)); + mrb_assert(is_black(mrb_basic_ptr(obj_v))); + mrb_assert(is_gray(mrb_basic_ptr(value_v))); + mrb_assert(gray_num == 1); + + mrb_close(mrb); +} + +void +test_incremental_gc(void) +{ + mrb_state *mrb = mrb_open(); + size_t max = ~0, live = 0, total = 0, freed = 0; + RVALUE *free; + mrb_heap_page *page; + mrb_gc *gc = &mrb->gc; + + puts("test_incremental_gc"); + change_gen_gc_mode(mrb, gc, FALSE); + + puts(" in mrb_full_gc"); + mrb_full_gc(mrb); + + mrb_assert(gc->state == MRB_GC_STATE_ROOT); + puts(" in MRB_GC_STATE_ROOT"); + incremental_gc(mrb, gc, max); + mrb_assert(gc->state == MRB_GC_STATE_MARK); + puts(" in MRB_GC_STATE_MARK"); + incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP); + mrb_assert(gc->state == MRB_GC_STATE_SWEEP); + + puts(" in MRB_GC_STATE_SWEEP"); + page = gc->heaps; + while (page) { + RVALUE *p = objects(page); + RVALUE *e = p + MRB_HEAP_PAGE_SIZE; + while (p<e) { + if (is_black(&p->as.basic)) { + live++; + } + if (is_gray(&p->as.basic) && !is_dead(gc, &p->as.basic)) { + printf("%p\n", &p->as.basic); + } + p++; + } + page = page->next; + total += MRB_HEAP_PAGE_SIZE; + } + + mrb_assert(gc->gray_list == NULL); + + incremental_gc(mrb, gc, max); + mrb_assert(gc->state == MRB_GC_STATE_SWEEP); + + incremental_gc(mrb, gc, max); + mrb_assert(gc->state == MRB_GC_STATE_ROOT); + + free = (RVALUE*)gc->heaps->freelist; + while (free) { + freed++; + free = (RVALUE*)free->as.free.next; + } + + mrb_assert(gc->live == live); + mrb_assert(gc->live == total-freed); + + puts("test_incremental_gc(gen)"); + incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP); + change_gen_gc_mode(mrb, gc, TRUE); + + mrb_assert(gc->full == FALSE); + mrb_assert(gc->state == MRB_GC_STATE_ROOT); + + puts(" in minor"); + mrb_assert(is_minor_gc(gc)); + mrb_assert(gc->majorgc_old_threshold > 0); + gc->majorgc_old_threshold = 0; + mrb_incremental_gc(mrb); + mrb_assert(gc->full == TRUE); + mrb_assert(gc->state == MRB_GC_STATE_ROOT); + + puts(" in major"); + mrb_assert(is_major_gc(gc)); + do { + mrb_incremental_gc(mrb); + } while (gc->state != MRB_GC_STATE_ROOT); + mrb_assert(gc->full == FALSE); + + mrb_close(mrb); +} + +void +test_incremental_sweep_phase(void) +{ + mrb_state *mrb = mrb_open(); + mrb_gc *gc = &mrb->gc; + + puts("test_incremental_sweep_phase"); + + add_heap(mrb, gc); + gc->sweeps = gc->heaps; + + mrb_assert(gc->heaps->next->next == NULL); + mrb_assert(gc->free_heaps->next->next == NULL); + incremental_sweep_phase(mrb, gc, MRB_HEAP_PAGE_SIZE * 3); + + mrb_assert(gc->heaps->next == NULL); + mrb_assert(gc->heaps == gc->free_heaps); + + mrb_close(mrb); +} + +static mrb_value +gc_test(mrb_state *mrb, mrb_value self) +{ + test_mrb_field_write_barrier(); + test_mrb_write_barrier(); + test_add_gray_list(); + test_gc_gray_mark(); + test_incremental_gc(); + test_incremental_sweep_phase(); + return mrb_nil_value(); +} +#endif /* GC_DEBUG */ +#endif /* GC_TEST */ diff --git a/web/server/h2o/libh2o/deps/mruby/src/hash.c b/web/server/h2o/libh2o/deps/mruby/src/hash.c new file mode 100644 index 000000000..93d550187 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/hash.c @@ -0,0 +1,905 @@ +/* +** hash.c - Hash class +** +** 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; + khint_t h; + + switch (t) { + case MRB_TT_STRING: + h = mrb_str_hash(mrb, key); + break; + + case MRB_TT_TRUE: + case MRB_TT_FALSE: + case MRB_TT_SYMBOL: + case MRB_TT_FIXNUM: + case MRB_TT_FLOAT: + h = (khint_t)mrb_obj_id(key); + break; + + default: + hv = mrb_funcall(mrb, key, "hash", 0); + h = (khint_t)t ^ mrb_fixnum(hv); + break; + } + return kh_int_hash_func(mrb, h); +} + +static inline khint_t +mrb_hash_ht_hash_equal(mrb_state *mrb, mrb_value a, mrb_value b) +{ + enum mrb_vtype t = mrb_type(a); + + switch (t) { + case MRB_TT_STRING: + return mrb_str_equal(mrb, a, b); + + case MRB_TT_SYMBOL: + if (mrb_type(b) != MRB_TT_SYMBOL) 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_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; + } + + default: + return mrb_eql(mrb, a, b); + } +} + +KHASH_DEFINE (ht, mrb_value, mrb_hash_value, TRUE, mrb_hash_ht_hash_func, mrb_hash_ht_hash_equal) + +static void mrb_hash_modify(mrb_state *mrb, mrb_value hash); + +static inline mrb_value +mrb_hash_ht_key(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; +} + +#define KEY(key) mrb_hash_ht_key(mrb, key) + +void +mrb_gc_mark_hash(mrb_state *mrb, struct RHash *hash) +{ + khiter_t k; + khash_t(ht) *h = hash->ht; + + 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; + + mrb_gc_mark_value(mrb, key); + mrb_gc_mark_value(mrb, val); + } + } +} + +size_t +mrb_gc_mark_hash_size(mrb_state *mrb, struct RHash *hash) +{ + if (!hash->ht) return 0; + return kh_size(hash->ht)*2; +} + +void +mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash) +{ + if (hash->ht) kh_destroy(ht, mrb, hash->ht); +} + + +MRB_API mrb_value +mrb_hash_new_capa(mrb_state *mrb, mrb_int capa) +{ + struct RHash *h; + + h = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); + /* khash needs 1/4 empty space so it is not resized immediately */ + h->ht = kh_init_size(ht, mrb, capa*4/3); + h->iv = 0; + return mrb_obj_value(h); +} + +MRB_API mrb_value +mrb_hash_new(mrb_state *mrb) +{ + return mrb_hash_new_capa(mrb, 0); +} + +static mrb_value mrb_hash_default(mrb_state *mrb, mrb_value hash); +static mrb_value hash_default(mrb_state *mrb, mrb_value hash, mrb_value key); + +MRB_API mrb_value +mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key) +{ + khash_t(ht) *h = RHASH_TBL(hash); + khiter_t k; + mrb_sym mid; + + if (h) { + k = kh_get(ht, mrb, h, key); + if (k != kh_end(h)) + return kh_value(h, k).v; + } + + mid = mrb_intern_lit(mrb, "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) +{ + khash_t(ht) *h = RHASH_TBL(hash); + khiter_t k; + + if (h) { + k = kh_get(ht, mrb, h, key); + if (k != kh_end(h)) + return kh_value(h, k).v; + } + + /* not found */ + return def; +} + +MRB_API void +mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) +{ + khash_t(ht) *h; + khiter_t k; + int r; + + mrb_hash_modify(mrb, hash); + h = RHASH_TBL(hash); + + if (!h) h = RHASH_TBL(hash) = kh_init(ht, mrb); + k = kh_put2(ht, mrb, h, key, &r); + kh_value(h, k).v = val; + + 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; + } + + mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), key); + mrb_field_write_barrier_value(mrb, (struct RBasic*)RHASH(hash), val); + return; +} + +static mrb_value +mrb_hash_dup(mrb_state *mrb, mrb_value hash) +{ + struct RHash* ret; + khash_t(ht) *h, *ret_h; + khiter_t k, ret_k; + mrb_value ifnone, vret; + + h = RHASH_TBL(hash); + ret = (struct RHash*)mrb_obj_alloc(mrb, MRB_TT_HASH, mrb->hash_class); + ret->ht = kh_init(ht, mrb); + + if (h && kh_size(h) > 0) { + ret_h = ret->ht; + + 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).v = kh_val(h, k).v; + kh_val(ret_h, ret_k).n = kh_size(ret_h)-1; + } + } + } + + if (MRB_RHASH_DEFAULT_P(hash)) { + ret->flags |= MRB_HASH_DEFAULT; + } + if (MRB_RHASH_PROCDEFAULT_P(hash)) { + ret->flags |= MRB_HASH_PROC_DEFAULT; + } + vret = mrb_obj_value(ret); + ifnone = RHASH_IFNONE(hash); + if (!mrb_nil_p(ifnone)) { + mrb_iv_set(mrb, vret, mrb_intern_lit(mrb, "ifnone"), ifnone); + } + return vret; +} + +MRB_API mrb_value +mrb_check_hash_type(mrb_state *mrb, mrb_value hash) +{ + return mrb_check_convert_type(mrb, hash, MRB_TT_HASH, "Hash", "to_hash"); +} + +MRB_API khash_t(ht)* +mrb_hash_tbl(mrb_state *mrb, mrb_value hash) +{ + khash_t(ht) *h = RHASH_TBL(hash); + + if (!h) { + return RHASH_TBL(hash) = kh_init(ht, mrb); + } + return h; +} + +static void +mrb_hash_modify(mrb_state *mrb, mrb_value hash) +{ + if (MRB_FROZEN_P(mrb_hash_ptr(hash))) { + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen hash"); + } + mrb_hash_tbl(mrb, hash); +} + +/* 15.2.13.4.16 */ +/* + * call-seq: + * Hash.new -> new_hash + * Hash.new(obj) -> new_hash + * Hash.new {|hash, key| block } -> new_hash + * + * Returns a new, empty hash. If this hash is subsequently accessed by + * a key that doesn't correspond to a hash entry, the value returned + * depends on the style of <code>new</code> used to create the hash. In + * the first form, the access returns <code>nil</code>. If + * <i>obj</i> is specified, this single object will be used for + * all <em>default values</em>. If a block is specified, it will be + * called with the hash object and the key, and should return the + * 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"] + * + */ + +static mrb_value +mrb_hash_init(mrb_state *mrb, mrb_value hash) +{ + mrb_value block, ifnone; + mrb_bool ifnone_p; + + ifnone = mrb_nil_value(); + mrb_get_args(mrb, "&|o?", &block, &ifnone, &ifnone_p); + mrb_hash_modify(mrb, hash); + if (!mrb_nil_p(block)) { + if (ifnone_p) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); + } + RHASH(hash)->flags |= MRB_HASH_PROC_DEFAULT; + ifnone = block; + } + if (!mrb_nil_p(ifnone)) { + RHASH(hash)->flags |= MRB_HASH_DEFAULT; + mrb_iv_set(mrb, hash, mrb_intern_lit(mrb, "ifnone"), ifnone); + } + return hash; +} + +/* 15.2.13.4.2 */ +/* + * call-seq: + * hsh[key] -> value + * + * Element Reference---Retrieves the <i>value</i> object corresponding + * to the <i>key</i> object. If not found, returns the default value (see + * <code>Hash::new</code> for details). + * + * h = { "a" => 100, "b" => 200 } + * h["a"] #=> 100 + * h["c"] #=> nil + * + */ +static mrb_value +mrb_hash_aget(mrb_state *mrb, mrb_value self) +{ + mrb_value key; + + mrb_get_args(mrb, "o", &key); + return mrb_hash_get(mrb, self, key); +} + +static mrb_value +hash_default(mrb_state *mrb, mrb_value hash, mrb_value key) +{ + if (MRB_RHASH_DEFAULT_P(hash)) { + if (MRB_RHASH_PROCDEFAULT_P(hash)) { + return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); + } + else { + return RHASH_IFNONE(hash); + } + } + return mrb_nil_value(); +} + +/* 15.2.13.4.5 */ +/* + * call-seq: + * hsh.default(key=nil) -> obj + * + * Returns the default value, the value that would be returned by + * <i>hsh</i>[<i>key</i>] if <i>key</i> did not exist in <i>hsh</i>. + * See also <code>Hash::new</code> and <code>Hash#default=</code>. + * + * h = Hash.new #=> {} + * h.default #=> nil + * h.default(2) #=> nil + * + * h = Hash.new("cat") #=> {} + * h.default #=> "cat" + * h.default(2) #=> "cat" + * + * h = Hash.new {|h,k| h[k] = k.to_i*10} #=> {} + * h.default #=> nil + * h.default(2) #=> 20 + */ + +static mrb_value +mrb_hash_default(mrb_state *mrb, mrb_value hash) +{ + mrb_value key; + mrb_bool given; + + mrb_get_args(mrb, "|o?", &key, &given); + if (MRB_RHASH_DEFAULT_P(hash)) { + if (MRB_RHASH_PROCDEFAULT_P(hash)) { + if (!given) return mrb_nil_value(); + return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, key); + } + else { + return RHASH_IFNONE(hash); + } + } + return mrb_nil_value(); +} + +/* 15.2.13.4.6 */ +/* + * call-seq: + * hsh.default = obj -> obj + * + * Sets the default value, the value returned for a key that does not + * exist in the hash. It is not possible to set the default to a + * <code>Proc</code> that will be executed on each key lookup. + * + * h = { "a" => 100, "b" => 200 } + * h.default = "Go fish" + * h["a"] #=> 100 + * h["z"] #=> "Go fish" + * # This doesn't do what you might hope... + * h.default = proc do |hash, key| + * hash[key] = key + key + * end + * h[2] #=> #<Proc:0x401b3948@-:6> + * h["cat"] #=> #<Proc:0x401b3948@-:6> + */ + +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; + if (!mrb_nil_p(ifnone)) { + RHASH(hash)->flags |= MRB_HASH_DEFAULT; + } + else { + RHASH(hash)->flags &= ~MRB_HASH_DEFAULT; + } + return ifnone; +} + +/* 15.2.13.4.7 */ +/* + * call-seq: + * hsh.default_proc -> anObject + * + * If <code>Hash::new</code> was invoked with a block, return that + * block, otherwise return <code>nil</code>. + * + * h = Hash.new {|h,k| h[k] = k*k } #=> {} + * p = h.default_proc #=> #<Proc:0x401b3d08@-:1> + * a = [] #=> [] + * p.call(a, 2) + * a #=> [nil, nil, 4] + */ + + +static mrb_value +mrb_hash_default_proc(mrb_state *mrb, mrb_value hash) +{ + if (MRB_RHASH_PROCDEFAULT_P(hash)) { + return RHASH_PROCDEFAULT(hash); + } + return mrb_nil_value(); +} + +/* + * call-seq: + * hsh.default_proc = proc_obj -> proc_obj + * + * Sets the default proc to be executed on each key lookup. + * + * h.default_proc = proc do |hash, key| + * hash[key] = key + key + * end + * h[2] #=> 4 + * h["cat"] #=> "catcat" + */ + +static mrb_value +mrb_hash_set_default_proc(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); + 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; +} + +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; + } + } + + /* 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_hash_modify(mrb, self); + return mrb_hash_delete_key(mrb, self, key); +} + +/* 15.2.13.4.24 */ +/* + * call-seq: + * hsh.shift -> anArray or obj + * + * Removes a key-value pair from <i>hsh</i> and returns it as the + * 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"} + */ + +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); + + return mrb_assoc_new(mrb, delKey, delVal); + } + } + + if (MRB_RHASH_DEFAULT_P(hash)) { + if (MRB_RHASH_PROCDEFAULT_P(hash)) { + return mrb_funcall(mrb, RHASH_PROCDEFAULT(hash), "call", 2, hash, mrb_nil_value()); + } + else { + return RHASH_IFNONE(hash); + } + } + return mrb_nil_value(); +} + +/* 15.2.13.4.4 */ +/* + * call-seq: + * hsh.clear -> hsh + * + * Removes all key-value pairs from `hsh`. + * + * 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); + + mrb_hash_modify(mrb, hash); + if (h) kh_clear(ht, mrb, h); + return hash; +} + +/* 15.2.13.4.3 */ +/* 15.2.13.4.26 */ +/* + * call-seq: + * hsh[key] = value -> value + * hsh.store(key, value) -> value + * + * Element Assignment---Associates the value given by + * <i>value</i> with the key given by <i>key</i>. + * <i>key</i> should not have its value changed while it is in + * 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} + * + */ +static mrb_value +mrb_hash_aset(mrb_state *mrb, mrb_value self) +{ + mrb_value key, val; + + mrb_get_args(mrb, "oo", &key, &val); + mrb_hash_set(mrb, self, key, val); + return val; +} + +/* 15.2.13.4.20 */ +/* 15.2.13.4.25 */ +/* + * call-seq: + * hsh.length -> fixnum + * hsh.size -> fixnum + * + * Returns the number of key-value pairs in the hash. + * + * h = { "d" => 100, "a" => 200, "v" => 300, "e" => 400 } + * h.length #=> 4 + * h.delete("a") #=> 200 + * h.length #=> 3 + */ +static mrb_value +mrb_hash_size_m(mrb_state *mrb, mrb_value self) +{ + khash_t(ht) *h = RHASH_TBL(self); + + if (!h) return mrb_fixnum_value(0); + return mrb_fixnum_value(kh_size(h)); +} + +/* 15.2.13.4.12 */ +/* + * call-seq: + * hsh.empty? -> true or false + * + * Returns <code>true</code> if <i>hsh</i> contains no key-value pairs. + * + * {}.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) +{ + return hash; +} + +/* 15.2.13.4.19 */ +/* + * call-seq: + * hsh.keys -> array + * + * Returns a new array populated with the keys from this hash. See also + * <code>Hash#values</code>. + * + * h = { "a" => 100, "b" => 200, "c" => 300, "d" => 400 } + * h.keys #=> ["a", "b", "c", "d"] + * + */ + +MRB_API mrb_value +mrb_hash_keys(mrb_state *mrb, mrb_value hash) +{ + khash_t(ht) *h = RHASH_TBL(hash); + khiter_t k; + mrb_int end; + 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)); + end = kh_size(h)-1; + mrb_ary_set(mrb, ary, end, mrb_nil_value()); + p = RARRAY_PTR(ary); + for (k = kh_begin(h); k != kh_end(h); k++) { + if (kh_exist(h, k)) { + mrb_value kv = kh_key(h, k); + mrb_hash_value hv = kh_value(h, k); + + if (hv.n <= end) { + p[hv.n] = kv; + } + else { + p[end] = kv; + } + } + } + return ary; +} + +/* 15.2.13.4.28 */ +/* + * call-seq: + * hsh.values -> array + * + * Returns a new array populated with the values from <i>hsh</i>. See + * also <code>Hash#keys</code>. + * + * h = { "a" => 100, "b" => 200, "c" => 300 } + * h.values #=> [100, 200, 300] + * + */ + +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); + } + } + return ary; +} + +/* 15.2.13.4.13 */ +/* 15.2.13.4.15 */ +/* 15.2.13.4.18 */ +/* 15.2.13.4.21 */ +/* + * call-seq: + * hsh.has_key?(key) -> true or false + * hsh.include?(key) -> true or false + * hsh.key?(key) -> true or false + * hsh.member?(key) -> true or false + * + * Returns <code>true</code> if the given key is present in <i>hsh</i>. + * + * h = { "a" => 100, "b" => 200 } + * h.has_key?("a") #=> true + * h.has_key?("z") #=> false + * + */ + +static mrb_value +mrb_hash_has_key(mrb_state *mrb, mrb_value hash) +{ + mrb_value key; + khash_t(ht) *h; + khiter_t k; + + 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(); +} + +/* 15.2.13.4.14 */ +/* 15.2.13.4.27 */ +/* + * call-seq: + * hsh.has_value?(value) -> true or false + * hsh.value?(value) -> true or false + * + * Returns <code>true</code> if the given value is present for some key + * in <i>hsh</i>. + * + * h = { "a" => 100, "b" => 200 } + * h.has_value?(100) #=> true + * h.has_value?(999) #=> false + */ + +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); + + if (h) { + for (k = kh_begin(h); k != kh_end(h); k++) { + if (!kh_exist(h, k)) continue; + + if (mrb_equal(mrb, kh_value(h, k).v, val)) { + return mrb_true_value(); + } + } + } + return mrb_false_value(); +} + +void +mrb_init_hash(mrb_state *mrb) +{ + struct RClass *h; + + 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_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, "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, "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, "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)*/ +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/init.c b/web/server/h2o/libh2o/deps/mruby/src/init.c new file mode 100644 index 000000000..afd69975a --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/init.c @@ -0,0 +1,51 @@ +/* +** init.c - initialize mruby core +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> + +void mrb_init_symtbl(mrb_state*); +void mrb_init_class(mrb_state*); +void mrb_init_object(mrb_state*); +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_array(mrb_state*); +void mrb_init_hash(mrb_state*); +void mrb_init_numeric(mrb_state*); +void mrb_init_range(mrb_state*); +void mrb_init_gc(mrb_state*); +void mrb_init_math(mrb_state*); +void mrb_init_version(mrb_state*); +void mrb_init_mrblib(mrb_state*); + +#define DONE mrb_gc_arena_restore(mrb, 0); +void +mrb_init_core(mrb_state *mrb) +{ + mrb_init_symtbl(mrb); DONE; + + mrb_init_class(mrb); DONE; + mrb_init_object(mrb); DONE; + mrb_init_kernel(mrb); DONE; + mrb_init_comparable(mrb); DONE; + 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_array(mrb); DONE; + mrb_init_hash(mrb); DONE; + mrb_init_numeric(mrb); DONE; + mrb_init_range(mrb); DONE; + mrb_init_gc(mrb); DONE; + mrb_init_version(mrb); DONE; + mrb_init_mrblib(mrb); DONE; +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/kernel.c b/web/server/h2o/libh2o/deps/mruby/src/kernel.c new file mode 100644 index 000000000..4e95ab24b --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/kernel.c @@ -0,0 +1,1238 @@ +/* +** kernel.c - Kernel module +** +** See Copyright Notice in mruby.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> + +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 RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mid); + if (MRB_PROC_CFUNC_P(me) && (me->body.func == func)) + return TRUE; + return FALSE; +} + +static mrb_bool +mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj) +{ + return mrb_func_basic_p(mrb, obj, mrb_intern_lit(mrb, "to_s"), mrb_any_to_s); +} + +/* 15.3.1.3.17 */ +/* + * call-seq: + * obj.inspect -> string + * + * Returns a string containing a human-readable representation of + * <i>obj</i>. If not overridden and no instance variables, uses the + * <code>to_s</code> method to generate the string. + * <i>obj</i>. If not overridden, uses the <code>to_s</code> method to + * generate the string. + * + * [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]" + * Time.new.inspect #=> "2008-03-08 19:43:39 +0900" + */ +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)) { + return mrb_obj_iv_inspect(mrb, mrb_obj_ptr(obj)); + } + return mrb_any_to_s(mrb, obj); +} + +/* 15.3.1.3.2 */ +/* + * call-seq: + * obj === other -> true or false + * + * Case Equality---For class <code>Object</code>, effectively the same + * as calling <code>#==</code>, but typically overridden by descendants + * to provide meaningful semantics in <code>case</code> statements. + */ +static mrb_value +mrb_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.3 */ +/* 15.3.1.3.33 */ +/* + * Document-method: __id__ + * Document-method: object_id + * + * call-seq: + * obj.__id__ -> fixnum + * obj.object_id -> fixnum + * + * Returns an integer identifier for <i>obj</i>. The same number will + * be returned on all calls to <code>id</code> for a given object, and + * no two active objects will share an id. + * <code>Object#object_id</code> is a different concept from the + * <code>:name</code> notation, which returns the symbol id of + * <code>name</code>. Replaces the deprecated <code>Object#id</code>. + */ +mrb_value +mrb_obj_id_m(mrb_state *mrb, mrb_value self) +{ + return mrb_fixnum_value(mrb_obj_id(self)); +} + +/* 15.3.1.2.2 */ +/* 15.3.1.2.5 */ +/* 15.3.1.3.6 */ +/* 15.3.1.3.25 */ +/* + * call-seq: + * block_given? -> true or false + * iterator? -> true or false + * + * Returns <code>true</code> if <code>yield</code> would execute a + * block in the current context. The <code>iterator?</code> form + * is mildly deprecated. + * + * def try + * if block_given? + * yield + * else + * "no block" + * end + * end + * try #=> "no block" + * try { "hello" } #=> "hello" + * try do "hello" end #=> "hello" + */ +static mrb_value +mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) +{ + mrb_callinfo *ci = mrb->c->ci; + mrb_value *bp; + + bp = ci->stackent + 1; + ci--; + if (ci <= mrb->c->cibase) { + return mrb_false_value(); + } + /* block_given? called within block; check upper scope */ + if (ci->proc->env) { + struct REnv *e = ci->proc->env; + + while (e->c) { + e = (struct REnv*)e->c; + } + /* top-level does not have block slot (always false) */ + if (e->stack == mrb->c->stbase) + return mrb_false_value(); + if (e->stack && e->cioff < 0) { + /* use saved block arg position */ + bp = &e->stack[-e->cioff]; + ci = 0; /* no callinfo available */ + } + else { + ci = e->cxt.c->cibase + e->cioff; + bp = ci[1].stackent + 1; + } + } + if (ci && ci->argc > 0) { + bp += ci->argc; + } + if (mrb_nil_p(*bp)) + return mrb_false_value(); + return mrb_true_value(); +} + +/* 15.3.1.3.7 */ +/* + * call-seq: + * obj.class -> class + * + * Returns the class of <i>obj</i>. This method must always be + * called with an explicit receiver, as <code>class</code> is also a + * reserved word in Ruby. + * + * 1.class #=> Fixnum + * self.class #=> Object + */ +static mrb_value +mrb_obj_class_m(mrb_state *mrb, mrb_value self) +{ + return mrb_obj_value(mrb_obj_class(mrb, self)); +} + +static struct RClass* +mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) +{ + struct RClass *klass = mrb_basic_ptr(obj)->c; + + if (klass->tt != MRB_TT_SCLASS) + return klass; + else { + /* copy singleton(unnamed) class */ + struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class); + + switch (mrb_type(obj)) { + case MRB_TT_CLASS: + case MRB_TT_SCLASS: + break; + default: + clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); + break; + } + clone->super = klass->super; + if (klass->iv) { + mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); + mrb_obj_iv_set(mrb, (struct RObject*)clone, mrb_intern_lit(mrb, "__attached__"), obj); + } + if (klass->mt) { + clone->mt = kh_copy(mt, mrb, klass->mt); + } + else { + clone->mt = kh_init(mt, mrb); + } + clone->tt = MRB_TT_SCLASS; + return clone; + } +} + +static void +copy_class(mrb_state *mrb, mrb_value dst, mrb_value src) +{ + struct RClass *dc = mrb_class_ptr(dst); + struct RClass *sc = mrb_class_ptr(src); + /* if the origin is not the same as the class, then the origin and + the current class need to be copied */ + if (sc->flags & MRB_FLAG_IS_PREPENDED) { + struct RClass *c0 = sc->super; + struct RClass *c1 = dc; + + /* copy prepended iclasses */ + while (!(c0->flags & MRB_FLAG_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_FLAG_IS_ORIGIN; + } + if (sc->mt) { + dc->mt = kh_copy(mt, mrb, sc->mt); + } + else { + dc->mt = kh_init(mt, mrb); + } + dc->super = sc->super; + MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc)); +} + +static void +init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj) +{ + switch (mrb_type(obj)) { + case MRB_TT_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; + case MRB_TT_ISTRUCT: + mrb_istruct_copy(dest, obj); + break; + + default: + break; + } + mrb_funcall(mrb, dest, "initialize_copy", 1, obj); +} + +/* 15.3.1.3.8 */ +/* + * call-seq: + * obj.clone -> an_object + * + * Produces a shallow copy of <i>obj</i>---the instance variables of + * <i>obj</i> are copied, but not the objects they reference. Copies + * the frozen state of <i>obj</i>. See also the discussion + * under <code>Object#dup</code>. + * + * class Klass + * attr_accessor :str + * end + * s1 = Klass.new #=> #<Klass:0x401b3a38> + * s1.str = "Hello" #=> "Hello" + * s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello"> + * s2.str[1,4] = "i" #=> "i" + * s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">" + * s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">" + * + * This method may have class-specific behavior. If so, that + * behavior will be documented under the #+initialize_copy+ method of + * the class. + * + * Some Class(True False Nil Symbol Fixnum Float) Object cannot clone. + */ +MRB_API mrb_value +mrb_obj_clone(mrb_state *mrb, mrb_value self) +{ + struct RObject *p; + mrb_value clone; + + if (mrb_immediate_p(self)) { + mrb_raisef(mrb, E_TYPE_ERROR, "can't clone %S", self); + } + if (mrb_type(self) == MRB_TT_SCLASS) { + mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class"); + } + p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self)); + p->c = mrb_singleton_class_clone(mrb, self); + mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c); + clone = mrb_obj_value(p); + init_copy(mrb, clone, self); + + return clone; +} + +/* 15.3.1.3.9 */ +/* + * call-seq: + * obj.dup -> an_object + * + * Produces a shallow copy of <i>obj</i>---the instance variables of + * <i>obj</i> are copied, but not the objects they reference. + * <code>dup</code> copies the frozen state of <i>obj</i>. See also + * the discussion under <code>Object#clone</code>. In general, + * <code>clone</code> and <code>dup</code> may have different semantics + * in descendant classes. While <code>clone</code> is used to duplicate + * an object, including its internal state, <code>dup</code> typically + * uses the class of the descendant object to create the new instance. + * + * This method may have class-specific behavior. If so, that + * behavior will be documented under the #+initialize_copy+ method of + * the class. + */ + +MRB_API mrb_value +mrb_obj_dup(mrb_state *mrb, mrb_value obj) +{ + struct RBasic *p; + mrb_value dup; + + if (mrb_immediate_p(obj)) { + mrb_raisef(mrb, E_TYPE_ERROR, "can't dup %S", obj); + } + if (mrb_type(obj) == MRB_TT_SCLASS) { + mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class"); + } + p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj)); + dup = mrb_obj_value(p); + init_copy(mrb, dup, obj); + + return dup; +} + +static mrb_value +mrb_obj_extend(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value obj) +{ + mrb_int i; + + if (argc == 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (at least 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); + } + return obj; +} + +/* 15.3.1.3.13 */ +/* + * call-seq: + * obj.extend(module, ...) -> obj + * + * Adds to _obj_ the instance methods from each module given as a + * parameter. + * + * module Mod + * def hello + * "Hello from Mod.\n" + * end + * end + * + * class Klass + * def hello + * "Hello from Klass.\n" + * end + * end + * + * k = Klass.new + * k.hello #=> "Hello from Klass.\n" + * k.extend(Mod) #=> #<Klass:0x401b3bc8> + * k.hello #=> "Hello from Mod.\n" + */ +static mrb_value +mrb_obj_extend_m(mrb_state *mrb, mrb_value self) +{ + mrb_value *argv; + mrb_int argc; + + mrb_get_args(mrb, "*", &argv, &argc); + return mrb_obj_extend(mrb, argc, argv, self); +} + +static mrb_value +mrb_obj_freeze(mrb_state *mrb, mrb_value self) +{ + struct RBasic *b; + + switch (mrb_type(self)) { + case MRB_TT_FALSE: + case MRB_TT_TRUE: + case MRB_TT_FIXNUM: + case MRB_TT_SYMBOL: + case MRB_TT_FLOAT: + return self; + default: + break; + } + + b = mrb_basic_ptr(self); + if (!MRB_FROZEN_P(b)) { + MRB_SET_FROZEN_FLAG(b); + } + return self; +} + +static mrb_value +mrb_obj_frozen(mrb_state *mrb, mrb_value self) +{ + struct RBasic *b; + + switch (mrb_type(self)) { + case MRB_TT_FALSE: + case MRB_TT_TRUE: + case MRB_TT_FIXNUM: + case MRB_TT_SYMBOL: + case MRB_TT_FLOAT: + return mrb_true_value(); + default: + break; + } + + b = mrb_basic_ptr(self); + if (!MRB_FROZEN_P(b)) { + return mrb_false_value(); + } + return mrb_true_value(); +} + +/* 15.3.1.3.15 */ +/* + * call-seq: + * obj.hash -> fixnum + * + * Generates a <code>Fixnum</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. + */ +MRB_API mrb_value +mrb_obj_hash(mrb_state *mrb, mrb_value self) +{ + return mrb_fixnum_value(mrb_obj_id(self)); +} + +/* 15.3.1.3.16 */ +static mrb_value +mrb_obj_init_copy(mrb_state *mrb, mrb_value self) +{ + mrb_value orig; + + 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"); + } + return self; +} + + +MRB_API mrb_bool +mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c) +{ + if (mrb_obj_class(mrb, obj) == c) return TRUE; + return FALSE; +} + +/* 15.3.1.3.19 */ +/* + * call-seq: + * obj.instance_of?(class) -> true or false + * + * Returns <code>true</code> if <i>obj</i> is an instance of the given + * class. See also <code>Object#kind_of?</code>. + */ +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; + + mrb_get_args(mrb, "n", &sym); + mrb_iv_check(mrb, sym); + return mrb_bool_value(mrb_iv_defined(mrb, self, sym)); +} + +/* 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; +} + +/* 15.3.1.3.24 */ +/* 15.3.1.3.26 */ +/* + * call-seq: + * obj.is_a?(class) -> true or false + * obj.kind_of?(class) -> true or false + * + * Returns <code>true</code> if <i>class</i> is the class of + * <i>obj</i>, or if <i>class</i> is one of the superclasses of + * <i>obj</i> or modules included in <i>obj</i>. + * + * module M; end + * class A + * include M + * end + * class B < A; end + * class C < B; end + * b = B.new + * b.instance_of? A #=> false + * b.instance_of? B #=> true + * b.instance_of? C #=> false + * b.instance_of? M #=> false + * b.kind_of? A #=> true + * b.kind_of? B #=> true + * b.kind_of? C #=> false + * b.kind_of? M #=> true + */ +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; + mrb_bool prepended = FALSE; + struct RClass* oldklass; + khash_t(st)* set = kh_init(st, mrb); + + if (!recur && (klass->flags & MRB_FLAG_IS_PREPENDED)) { + MRB_CLASS_ORIGIN(klass); + prepended = TRUE; + } + + oldklass = 0; + while (klass && (klass != oldklass)) { + method_entry_loop(mrb, klass, set); + if ((klass->tt == MRB_TT_ICLASS && !prepended) || + (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); + + klass = mrb_class(mrb, obj); + + 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) +{ + return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0); +} +/* 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 */ +} + +/* 15.3.1.3.32 */ +/* + * call_seq: + * nil.nil? -> true + * <anything_else>.nil? -> false + * + * Only the object <i>nil</i> responds <code>true</code> to <code>nil?</code>. + */ +static mrb_value +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 */ +/* + * call-seq: + * raise + * raise(string) + * raise(exception [, string]) + * + * With no arguments, raises a <code>RuntimeError</code> + * With a single +String+ argument, raises a + * +RuntimeError+ with the string as a message. Otherwise, + * the first parameter should be the name of an +Exception+ + * class (or an object that returns an +Exception+ object when sent + * an +exception+ message). The optional second parameter sets the + * message associated with the exception, and the third parameter is an + * array of callback information. Exceptions are caught by the + * +rescue+ clause of <code>begin...end</code> blocks. + * + * raise "Failed to create socket" + * raise ArgumentError, "No parameters", caller + */ +MRB_API mrb_value +mrb_f_raise(mrb_state *mrb, mrb_value self) +{ + mrb_value a[2], exc; + int argc; + + + argc = mrb_get_args(mrb, "|oo", &a[0], &a[1]); + switch (argc) { + case 0: + mrb_raise(mrb, E_RUNTIME_ERROR, ""); + break; + case 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_exc_raise(mrb, exc); + break; + } + return mrb_nil_value(); /* not reached */ +} + +/* 15.3.1.3.41 */ +/* + * call-seq: + * obj.remove_instance_variable(symbol) -> obj + * + * Removes the named instance variable from <i>obj</i>, returning that + * variable's value. + * + * class Dummy + * attr_reader :var + * def initialize + * @var = 99 + * end + * def remove + * remove_instance_variable(:@var) + * end + * end + * d = Dummy.new + * d.var #=> 99 + * d.remove #=> 99 + * d.var #=> nil + */ +static mrb_value +mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self) +{ + mrb_sym sym; + mrb_value val; + + mrb_get_args(mrb, "n", &sym); + mrb_iv_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)); + } + return val; +} + +void +mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args) +{ + mrb_sym inspect; + mrb_value repr; + + 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, self); + } + else if (mrb_respond_to(mrb, self, inspect) && mrb->c->ci - mrb->c->cibase < 16) { + repr = mrb_funcall_argv(mrb, self, inspect, 0, 0); + if (mrb_string_p(repr) && RSTRING_LEN(repr) > 64) { + repr = mrb_any_to_s(mrb, self); + } + } + else { + repr = mrb_any_to_s(mrb, self); + } + + mrb_no_method_error(mrb, name, args, "undefined method '%S' for %S", + mrb_sym2str(mrb, name), repr); +} + +/* 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.id2name + * romanToInt(str) + * end + * end + * + * r = Roman.new + * r.iv #=> 4 + * r.xxiii #=> 23 + * r.mm #=> 2000 + */ +#ifdef MRB_DEFAULT_METHOD_MISSING +static mrb_value +mrb_obj_missing(mrb_state *mrb, mrb_value mod) +{ + mrb_sym name; + mrb_value *a; + mrb_int alen; + + mrb_get_args(mrb, "n*!", &name, &a, &alen); + mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a)); + /* not reached */ + return mrb_nil_value(); +} +#endif + +static inline mrb_bool +basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub) +{ + return mrb_respond_to(mrb, obj, id); +} +/* 15.3.1.3.43 */ +/* + * call-seq: + * obj.respond_to?(symbol, include_private=false) -> true or false + * + * Returns +true+ if _obj_ responds to the given + * method. Private methods are included in the search only if the + * optional second parameter evaluates to +true+. + * + * If the method is not implemented, + * as Process.fork on Windows, File.lchmod on GNU/Linux, etc., + * false is returned. + * + * If the method is not defined, <code>respond_to_missing?</code> + * method is called and the result is returned. + */ +static mrb_value +obj_respond_to(mrb_state *mrb, mrb_value self) +{ + mrb_value mid; + mrb_sym id, rtm_id; + mrb_bool priv = FALSE, respond_to_p = TRUE; + + mrb_get_args(mrb, "o|b", &mid, &priv); + + if (mrb_symbol_p(mid)) { + id = mrb_symbol(mid); + } + else { + mrb_value tmp; + if (mrb_string_p(mid)) { + tmp = mrb_check_intern_str(mrb, mid); + } + else { + tmp = mrb_check_string_type(mrb, mid); + if (mrb_nil_p(tmp)) { + tmp = mrb_inspect(mrb, mid); + mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp); + } + tmp = mrb_check_intern_str(mrb, tmp); + } + if (mrb_nil_p(tmp)) { + respond_to_p = FALSE; + } + else { + id = mrb_symbol(tmp); + } + } + + if (respond_to_p) { + respond_to_p = basic_obj_respond_to(mrb, self, id, !priv); + } + + if (!respond_to_p) { + rtm_id = mrb_intern_lit(mrb, "respond_to_missing?"); + if (basic_obj_respond_to(mrb, self, rtm_id, !priv)) { + mrb_value args[2], v; + args[0] = mid; + args[1] = mrb_bool_value(priv); + 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_int i, len; + mrb_sym eqq = mrb_intern_lit(mrb, "==="); + mrb_value ary = mrb_ary_splat(mrb, self); + + mrb_get_args(mrb, "o", &v); + 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); + if (mrb_test(c)) return mrb_true_value(); + } + return mrb_false_value(); +} + +/* 15.3.1.2.7 */ +/* + * call-seq: + * local_variables -> array + * + * Returns the names of local variables in the current scope. + * + * [mruby limitation] + * If variable symbol information was stripped out from + * compiled binary files using `mruby-strip -l`, this + * method always returns an empty array. + */ +static mrb_value +mrb_local_variables(mrb_state *mrb, mrb_value self) +{ + struct RProc *proc; + mrb_irep *irep; + mrb_value vars; + size_t i; + + proc = mrb->c->ci[-1].proc; + + if (MRB_PROC_CFUNC_P(proc)) { + return mrb_ary_new(mrb); + } + vars = mrb_hash_new(mrb); + irep = proc->body.irep; + while (irep) { + if (!irep->lv) break; + for (i = 0; i + 1 < irep->nlocals; ++i) { + if (irep->lv[i].name) { + mrb_hash_set(mrb, vars, mrb_symbol_value(irep->lv[i].name), mrb_true_value()); + } + } + if (!proc->env) break; + irep = irep->outer; + } + + return mrb_hash_keys(mrb, vars); +} + +mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value); +void +mrb_init_kernel(mrb_state *mrb) +{ + struct RClass *krn; + + 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_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.2 */ + 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, "freeze", mrb_obj_freeze, MRB_ARGS_NONE()); + mrb_define_method(mrb, krn, "frozen?", mrb_obj_frozen, MRB_ARGS_NONE()); + mrb_define_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.3.14 */ + mrb_define_method(mrb, krn, "hash", mrb_obj_hash, MRB_ARGS_NONE()); /* 15.3.1.3.15 */ + mrb_define_method(mrb, krn, "initialize_copy", mrb_obj_init_copy, MRB_ARGS_REQ(1)); /* 15.3.1.3.16 */ + mrb_define_method(mrb, krn, "inspect", mrb_obj_inspect, MRB_ARGS_NONE()); /* 15.3.1.3.17 */ + 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 */ +#ifdef MRB_DEFAULT_METHOD_MISSING + mrb_define_method(mrb, krn, "method_missing", mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */ +#endif + mrb_define_method(mrb, krn, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */ + 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, "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_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/web/server/h2o/libh2o/deps/mruby/src/load.c b/web/server/h2o/libh2o/deps/mruby/src/load.c new file mode 100644 index 000000000..8ae607ffa --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/load.c @@ -0,0 +1,704 @@ +/* +** load.c - mruby binary loader +** +** See Copyright Notice in mruby.h +*/ + +#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> + +#if SIZE_MAX < UINT32_MAX +# error size_t must be at least 32 bits wide +#endif + +#define FLAG_BYTEORDER_BIG 2 +#define FLAG_BYTEORDER_LIL 4 +#define FLAG_BYTEORDER_NATIVE 8 +#define FLAG_SRC_MALLOC 1 +#define FLAG_SRC_STATIC 0 + +#define SIZE_ERROR_MUL(nmemb, size) ((size_t)(nmemb) > SIZE_MAX / (size)) + +static size_t +skip_padding(const uint8_t *buf) +{ + const size_t align = MRB_DUMP_ALIGNMENT; + return -(intptr_t)buf & (align-1); +} + +static size_t +offset_crc_body(void) +{ + struct rite_binary_header header; + return ((uint8_t *)header.binary_crc - (uint8_t *)&header) + sizeof(header.binary_crc); +} + +static mrb_irep* +read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags) +{ + int i; + const uint8_t *src = bin; + ptrdiff_t diff; + uint16_t tt, pool_data_len, snl; + int plen; + int ai = mrb_gc_arena_save(mrb); + mrb_irep *irep = mrb_add_irep(mrb); + + /* skip record size */ + src += sizeof(uint32_t); + + /* number of local variable */ + irep->nlocals = bin_to_uint16(src); + src += sizeof(uint16_t); + + /* number of register variable */ + irep->nregs = bin_to_uint16(src); + src += sizeof(uint16_t); + + /* number of child irep */ + irep->rlen = (size_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); + + if (irep->ilen > 0) { + if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) { + return NULL; + } + if ((flags & FLAG_SRC_MALLOC) == 0 && + (flags & FLAG_BYTEORDER_NATIVE)) { + 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 if (flags & FLAG_BYTEORDER_BIG) { + for (i = 0; i < irep->ilen; i++) { + irep->iseq[i] = (mrb_code)bin_to_uint32(src); /* iseq */ + src += sizeof(uint32_t); + } + } + else { + for (i = 0; i < irep->ilen; i++) { + irep->iseq[i] = (mrb_code)bin_to_uint32l(src); /* iseq */ + src += sizeof(uint32_t); + } + } + } + } + + /* POOL BLOCK */ + plen = bin_to_uint32(src); /* number of pool */ + src += sizeof(uint32_t); + if (plen > 0) { + if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) { + return NULL; + } + irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen); + + for (i = 0; i < plen; i++) { + mrb_value s; + + 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); + break; + + case IREP_TT_FLOAT: + irep->pool[i] = mrb_float_pool(mrb, mrb_str_to_dbl(mrb, s, FALSE)); + break; + + case IREP_TT_STRING: + irep->pool[i] = mrb_str_pool(mrb, s); + break; + + default: + /* should not happen */ + irep->pool[i] = mrb_nil_value(); + break; + } + irep->plen++; + mrb_gc_arena_restore(mrb, ai); + } + } + + /* SYMS BLOCK */ + irep->slen = (size_t)bin_to_uint32(src); /* syms length */ + src += sizeof(uint32_t); + if (irep->slen > 0) { + if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) { + return NULL; + } + irep->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; + continue; + } + + if (flags & FLAG_SRC_MALLOC) { + irep->syms[i] = mrb_intern(mrb, (char *)src, snl); + } + else { + irep->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; +} + +static mrb_irep* +read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags) +{ + mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags); + int i; + + if (irep == NULL) { + return NULL; + } + + bin += *len; + for (i=0; i<irep->rlen; i++) { + size_t rlen; + + irep->reps[i] = read_irep_record(mrb, bin, &rlen, flags); + if (irep->reps[i] == NULL) { + return NULL; + } + bin += rlen; + *len += rlen; + } + return irep; +} + +static mrb_irep* +read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) +{ + 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); + fname = (char *)mrb_malloc(mrb, fname_len + 1); + memcpy(fname, bin, fname_len); + fname[fname_len] = '\0'; + bin += fname_len; + *len += fname_len; + + niseq = (size_t)bin_to_uint32(bin); + bin += sizeof(uint32_t); /* niseq */ + *len += sizeof(uint32_t); + + if (SIZE_ERROR_MUL(niseq, sizeof(uint16_t))) { + return MRB_DUMP_GENERAL_FAILURE; + } + lines = (uint16_t *)mrb_malloc(mrb, niseq * sizeof(uint16_t)); + for (i = 0; i < niseq; i++) { + lines[i] = bin_to_uint16(bin); + bin += sizeof(uint16_t); /* niseq */ + *len += sizeof(uint16_t); + } + + irep->filename = fname; + irep->lines = lines; + return MRB_DUMP_OK; +} + +static int +read_lineno_record(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t *lenp) +{ + int result = read_lineno_record_1(mrb, bin, irep, lenp); + int i; + + if (result != MRB_DUMP_OK) return result; + for (i = 0; i < irep->rlen; i++) { + size_t len; + + result = read_lineno_record(mrb, bin, irep->reps[i], &len); + if (result != MRB_DUMP_OK) break; + bin += len; + *lenp += len; + } + 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 +read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *record_len, const mrb_sym *filenames, size_t filenames_len) +{ + const uint8_t *bin = start; + ptrdiff_t diff; + size_t record_size; + uint16_t f_idx; + int i; + + if (irep->debug_info) { return MRB_DUMP_INVALID_IREP; } + + irep->debug_info = (mrb_irep_debug_info*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info)); + irep->debug_info->pc_count = (uint32_t)irep->ilen; + + 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); + bin += sizeof(uint16_t); + + for (f_idx = 0; f_idx < irep->debug_info->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->start_pos = bin_to_uint32(bin); + bin += sizeof(uint32_t); + + /* filename */ + filename_idx = bin_to_uint16(bin); + 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); + file->line_type = (mrb_debug_line_type)bin_to_uint8(bin); + bin += sizeof(uint8_t); + switch (file->line_type) { + case mrb_debug_line_ary: { + uint32_t l; + + file->lines.ary = (uint16_t *)mrb_malloc(mrb, sizeof(uint16_t) * (size_t)(file->line_entry_count)); + for (l = 0; l < file->line_entry_count; ++l) { + file->lines.ary[l] = bin_to_uint16(bin); + bin += sizeof(uint16_t); + } + } break; + + 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)); + for (l = 0; l < file->line_entry_count; ++l) { + file->lines.flat_map[l].start_pos = bin_to_uint32(bin); + bin += sizeof(uint32_t); + file->lines.flat_map[l].line = bin_to_uint16(bin); + bin += sizeof(uint16_t); + } + } break; + + default: return MRB_DUMP_GENERAL_FAILURE; + } + } + + diff = bin - start; + mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); + + if (record_size != (size_t)diff) { + return MRB_DUMP_GENERAL_FAILURE; + } + + for (i = 0; i < irep->rlen; i++) { + size_t len; + int ret; + + ret = read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len); + if (ret != MRB_DUMP_OK) return ret; + bin += len; + } + + diff = bin - start; + mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); + *record_len = (size_t)diff; + + return MRB_DUMP_OK; +} + +static int +read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags) +{ + const uint8_t *bin; + ptrdiff_t diff; + struct rite_section_debug_header *header; + uint16_t i; + size_t len = 0; + int result; + uint16_t filenames_len; + mrb_sym *filenames; + + bin = start; + header = (struct rite_section_debug_header *)bin; + bin += sizeof(struct rite_section_debug_header); + + filenames_len = bin_to_uint16(bin); + bin += sizeof(uint16_t); + filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)filenames_len); + for (i = 0; i < filenames_len; ++i) { + uint16_t f_len = bin_to_uint16(bin); + bin += sizeof(uint16_t); + if (flags & FLAG_SRC_MALLOC) { + filenames[i] = mrb_intern(mrb, (const char *)bin, (size_t)f_len); + } + else { + filenames[i] = mrb_intern_static(mrb, (const char *)bin, (size_t)f_len); + } + bin += f_len; + } + + result = read_debug_record(mrb, bin, irep, &len, filenames, filenames_len); + if (result != MRB_DUMP_OK) goto debug_exit; + + bin += len; + diff = bin - start; + mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); + if ((uint32_t)diff != bin_to_uint32(header->section_size)) { + result = MRB_DUMP_GENERAL_FAILURE; + } + +debug_exit: + mrb_free(mrb, filenames); + return result; +} + +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; + ptrdiff_t diff; + int i; + + irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (irep->nlocals - 1)); + + 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; + } + 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); + } + 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); + if (ret != MRB_DUMP_OK) return ret; + bin += len; + } + + diff = bin - start; + mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); + *record_len = (size_t)diff; + + return MRB_DUMP_OK; +} + +static int +read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags) +{ + const uint8_t *bin; + ptrdiff_t diff; + struct rite_section_lv_header const *header; + uint32_t i; + size_t len = 0; + int result; + uint32_t syms_len; + mrb_sym *syms; + mrb_sym (*intern_func)(mrb_state*, const char*, size_t) = + (flags & FLAG_SRC_MALLOC)? mrb_intern : mrb_intern_static; + + bin = start; + header = (struct rite_section_lv_header const*)bin; + bin += sizeof(struct rite_section_lv_header); + + syms_len = bin_to_uint32(bin); + bin += sizeof(uint32_t); + syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * (size_t)syms_len); + for (i = 0; i < syms_len; ++i) { + uint16_t const str_len = bin_to_uint16(bin); + bin += sizeof(uint16_t); + + syms[i] = intern_func(mrb, (const char*)bin, str_len); + bin += str_len; + } + + result = read_lv_record(mrb, bin, irep, &len, syms, syms_len); + if (result != MRB_DUMP_OK) goto lv_exit; + + bin += len; + diff = bin - start; + mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); + if ((uint32_t)diff != bin_to_uint32(header->section_size)) { + result = MRB_DUMP_GENERAL_FAILURE; + } + +lv_exit: + mrb_free(mrb, syms); + return result; +} + +static int +read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc, uint8_t *flags) +{ + const struct rite_binary_header *header = (const struct rite_binary_header *)bin; + + if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) == 0) { + if (bigendian_p()) + *flags |= FLAG_BYTEORDER_NATIVE; + else + *flags |= FLAG_BYTEORDER_BIG; + } + else if (memcmp(header->binary_ident, RITE_BINARY_IDENT_LIL, sizeof(header->binary_ident)) == 0) { + if (bigendian_p()) + *flags |= FLAG_BYTEORDER_LIL; + else + *flags |= FLAG_BYTEORDER_NATIVE; + } + else { + return MRB_DUMP_INVALID_FILE_HEADER; + } + + if (crc) { + *crc = bin_to_uint16(header->binary_crc); + } + *bin_size = (size_t)bin_to_uint32(header->binary_size); + + return MRB_DUMP_OK; +} + +static mrb_irep* +read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) +{ + int result; + 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); + if (result != MRB_DUMP_OK) { + return NULL; + } + + n = offset_crc_body(); + if (crc != calc_crc_16_ccitt(bin + n, bin_size - n, 0)) { + return NULL; + } + + bin += sizeof(struct rite_binary_header); + do { + section_header = (const struct rite_section_header *)bin; + if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) { + irep = read_section_irep(mrb, bin, flags); + if (!irep) return NULL; + } + else if (memcmp(section_header->section_ident, RITE_SECTION_LINENO_IDENT, sizeof(section_header->section_ident)) == 0) { + if (!irep) return NULL; /* corrupted data */ + result = read_section_lineno(mrb, bin, irep); + if (result < MRB_DUMP_OK) { + return NULL; + } + } + else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) { + if (!irep) return NULL; /* corrupted data */ + result = read_section_debug(mrb, bin, irep, flags); + if (result < MRB_DUMP_OK) { + return NULL; + } + } + 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) { + return NULL; + } + } + bin += bin_to_uint32(section_header->section_size); + } while (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0); + + return irep; +} + +mrb_irep* +mrb_read_irep(mrb_state *mrb, const uint8_t *bin) +{ +#ifdef MRB_USE_ETEXT_EDATA + 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); +} + +void mrb_exc_set(mrb_state *mrb, mrb_value exc); + +static void +irep_error(mrb_state *mrb) +{ + mrb_exc_set(mrb, mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error")); +} + +void mrb_codedump_all(mrb_state*, struct RProc*); + +static mrb_value +load_irep(mrb_state *mrb, mrb_irep *irep, mrbc_context *c) +{ + struct RProc *proc; + + if (!irep) { + irep_error(mrb); + return mrb_nil_value(); + } + proc = mrb_proc_new(mrb, irep); + proc->c = NULL; + mrb_irep_decref(mrb, irep); + if (c && c->dump_result) mrb_codedump_all(mrb, proc); + if (c && c->no_exec) return mrb_obj_value(proc); + return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0); +} + +MRB_API mrb_value +mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c) +{ + return load_irep(mrb, mrb_read_irep(mrb, bin), c); +} + +MRB_API mrb_value +mrb_load_irep(mrb_state *mrb, const uint8_t *bin) +{ + return mrb_load_irep_cxt(mrb, bin, NULL); +} + +#ifndef MRB_DISABLE_STDIO + +mrb_irep* +mrb_read_irep_file(mrb_state *mrb, FILE* fp) +{ + mrb_irep *irep = NULL; + uint8_t *buf; + const size_t header_size = sizeof(struct rite_binary_header); + size_t buf_size = 0; + uint8_t flags = 0; + int result; + + if ((mrb == NULL) || (fp == NULL)) { + return NULL; + } + + buf = (uint8_t*)mrb_malloc(mrb, header_size); + if (fread(buf, header_size, 1, fp) == 0) { + goto irep_exit; + } + result = read_binary_header(buf, &buf_size, NULL, &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) { + goto irep_exit; + } + irep = read_irep(mrb, buf, FLAG_SRC_MALLOC); + +irep_exit: + mrb_free(mrb, buf); + return irep; +} + +MRB_API mrb_value +mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c) +{ + return load_irep(mrb, mrb_read_irep_file(mrb, fp), c); +} + +MRB_API mrb_value +mrb_load_irep_file(mrb_state *mrb, FILE* fp) +{ + return mrb_load_irep_file_cxt(mrb, fp, NULL); +} +#endif /* MRB_DISABLE_STDIO */ diff --git a/web/server/h2o/libh2o/deps/mruby/src/mruby_core.rake b/web/server/h2o/libh2o/deps/mruby/src/mruby_core.rake new file mode 100644 index 000000000..4558493d9 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/mruby_core.rake @@ -0,0 +1,19 @@ +MRuby.each_target do + current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd) + relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT) + current_build_dir = "#{build_dir}/#{relative_from_root}" + + objs = Dir.glob("#{current_dir}/*.c").map { |f| + next nil if cxx_exception_enabled? and f =~ /(error|vm).c$/ + objfile(f.pathmap("#{current_build_dir}/%n")) + }.compact + + if cxx_exception_enabled? + objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" } + end + self.libmruby << objs + + file libfile("#{build_dir}/lib/libmruby_core") => objs do |t| + archiver.run t.name, t.prerequisites + end +end diff --git a/web/server/h2o/libh2o/deps/mruby/src/numeric.c b/web/server/h2o/libh2o/deps/mruby/src/numeric.c new file mode 100644 index 000000000..afb8415a0 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/numeric.c @@ -0,0 +1,1355 @@ +/* +** numeric.c - Numeric, Integer, Float, Fixnum class +** +** See Copyright Notice in mruby.h +*/ + +#include <float.h> +#include <limits.h> +#include <math.h> +#include <stdlib.h> + +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/numeric.h> +#include <mruby/string.h> +#include <mruby/class.h> + +#ifdef MRB_USE_FLOAT +#define trunc(f) truncf(f) +#define floor(f) floorf(f) +#define ceil(f) ceilf(f) +#define fmod(x,y) fmodf(x,y) +#define MRB_FLO_TO_STR_FMT "%.7g" +#else +#define MRB_FLO_TO_STR_FMT "%.14g" +#endif + +MRB_API mrb_float +mrb_to_flo(mrb_state *mrb, mrb_value val) +{ + switch (mrb_type(val)) { + case MRB_TT_FIXNUM: + return (mrb_float)mrb_fixnum(val); + case MRB_TT_FLOAT: + break; + default: + mrb_raise(mrb, E_TYPE_ERROR, "non float value"); + } + return mrb_float(val); +} + +/* + * call-seq: + * + * num ** other -> num + * + * Raises <code>num</code> the <code>other</code> power. + * + * 2.0**3 #=> 8.0 + */ +static mrb_value +num_pow(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + mrb_float d; + + mrb_get_args(mrb, "o", &y); + if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) { + /* try ipow() */ + mrb_int base = mrb_fixnum(x); + mrb_int exp = mrb_fixnum(y); + mrb_int result = 1; + + if (exp < 0) goto float_pow; + for (;;) { + if (exp & 1) { + if (mrb_int_mul_overflow(result, base, &result)) { + goto float_pow; + } + } + exp >>= 1; + if (exp == 0) break; + if (mrb_int_mul_overflow(base, base, &base)) { + goto float_pow; + } + } + return mrb_fixnum_value(result); + } + float_pow: + d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y)); + return mrb_float_value(mrb, d); +} + +/* 15.2.8.3.4 */ +/* 15.2.9.3.4 */ +/* + * call-seq: + * num / 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) +{ + return mrb_float_value(mrb, mrb_to_flo(mrb, x) / mrb_to_flo(mrb, y)); +} + +/* 15.2.9.3.19(x) */ +/* + * call-seq: + * num.quo(numeric) -> real + * + * Returns most exact division. + */ + +static mrb_value +num_div(mrb_state *mrb, mrb_value x) +{ + mrb_float y; + + mrb_get_args(mrb, "f", &y); + return mrb_float_value(mrb, mrb_to_flo(mrb, x) / y); +} + +/******************************************************************** + * + * Document-class: Float + * + * <code>Float</code> objects represent inexact real numbers using + * the native architecture's double-precision floating point + * representation. + */ + +/* 15.2.9.3.16(x) */ +/* + * call-seq: + * flt.to_s -> 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>". + */ + +static mrb_value +flo_to_s(mrb_state *mrb, mrb_value flt) +{ + if (isnan(mrb_float(flt))) { + return mrb_str_new_lit(mrb, "NaN"); + } + return mrb_float_to_str(mrb, flt, MRB_FLO_TO_STR_FMT); +} + +/* 15.2.9.3.2 */ +/* + * call-seq: + * float - other -> float + * + * Returns a new float which is the difference of <code>float</code> + * and <code>other</code>. + */ + +static mrb_value +flo_minus(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + + mrb_get_args(mrb, "o", &y); + return mrb_float_value(mrb, mrb_float(x) - mrb_to_flo(mrb, y)); +} + +/* 15.2.9.3.3 */ +/* + * call-seq: + * float * other -> float + * + * Returns a new float which is the product of <code>float</code> + * and <code>other</code>. + */ + +static mrb_value +flo_mul(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + + mrb_get_args(mrb, "o", &y); + return mrb_float_value(mrb, mrb_float(x) * mrb_to_flo(mrb, y)); +} + +static void +flodivmod(mrb_state *mrb, mrb_float x, mrb_float y, mrb_float *divp, mrb_float *modp) +{ + mrb_float div; + mrb_float mod; + + if (y == 0.0) { + if (x > 0.0) div = INFINITY; + else if (x < 0.0) div = -INFINITY; + else div = NAN; /* x == 0.0 */ + mod = NAN; + } + 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 (modp) *modp = mod; + if (divp) *divp = div; +} + +/* 15.2.9.3.5 */ +/* + * call-seq: + * flt % other -> float + * flt.modulo(other) -> float + * + * Return the modulo after division of <code>flt</code> by <code>other</code>. + * + * 6543.21.modulo(137) #=> 104.21 + * 6543.21.modulo(137.24) #=> 92.9299999999996 + */ + +static mrb_value +flo_mod(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + mrb_float mod; + + mrb_get_args(mrb, "o", &y); + + flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod); + return mrb_float_value(mrb, mod); +} + +/* 15.2.8.3.16 */ +/* + * call-seq: + * num.eql?(numeric) -> true or false + * + * Returns <code>true</code> if <i>num</i> and <i>numeric</i> are the + * same type and have equal values. + * + * 1 == 1.0 #=> true + * 1.eql?(1.0) #=> false + * (1.0).eql?(1.0) #=> true + */ +static mrb_value +fix_eql(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + + 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)); +} + +static mrb_value +flo_eql(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + + 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)); +} + +/* 15.2.9.3.7 */ +/* + * call-seq: + * flt == obj -> true or false + * + * Returns <code>true</code> only if <i>obj</i> has the same value + * as <i>flt</i>. Contrast this with <code>Float#eql?</code>, which + * requires <i>obj</i> to be a <code>Float</code>. + * + * 1.0 == 1 #=> true + * + */ + +static mrb_value +flo_eq(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + mrb_get_args(mrb, "o", &y); + + switch (mrb_type(y)) { + case MRB_TT_FIXNUM: + return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y)); + case MRB_TT_FLOAT: + return mrb_bool_value(mrb_float(x) == mrb_float(y)); + default: + return mrb_false_value(); + } +} + +static int64_t +value_int64(mrb_state *mrb, mrb_value x) +{ + switch (mrb_type(x)) { + case MRB_TT_FIXNUM: + return (int64_t)mrb_fixnum(x); + break; + case MRB_TT_FLOAT: + return (int64_t)mrb_float(x); + 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 (FIXABLE(v)) { + return mrb_fixnum_value((mrb_int)v); + } + return mrb_float_value(mrb, (mrb_float)v); +} + +static mrb_value +flo_rev(mrb_state *mrb, mrb_value x) +{ + int64_t v1; + mrb_get_args(mrb, ""); + v1 = (int64_t)mrb_float(x); + return int64_value(mrb, ~v1); +} + +static mrb_value +flo_and(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + int64_t v1, v2; + mrb_get_args(mrb, "o", &y); + + v1 = (int64_t)mrb_float(x); + v2 = value_int64(mrb, y); + return int64_value(mrb, v1 & v2); +} + +static mrb_value +flo_or(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + int64_t v1, v2; + mrb_get_args(mrb, "o", &y); + + v1 = (int64_t)mrb_float(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; + int64_t v1, v2; + mrb_get_args(mrb, "o", &y); + + v1 = (int64_t)mrb_float(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 < 0) { + while (width++) { + val /= 2; + } +#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); + } + } + else { + while (width--) { + val *= 2; + } + } + if (FIXABLE_FLOAT(val)) { + return mrb_fixnum_value((mrb_int)val); + } + return mrb_float_value(mrb, val); +} + +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); +} + +static mrb_value +flo_rshift(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 */ +/* + * call-seq: + * flt.to_f -> self + * + * As <code>flt</code> is already a float, returns +self+. + */ + +static mrb_value +flo_to_f(mrb_state *mrb, mrb_value num) +{ + return num; +} + +/* 15.2.9.3.11 */ +/* + * call-seq: + * flt.infinite? -> nil, -1, +1 + * + * Returns <code>nil</code>, -1, or +1 depending on whether <i>flt</i> + * is finite, -infinity, or +infinity. + * + * (0.0).infinite? #=> nil + * (-1.0/0.0).infinite? #=> -1 + * (+1.0/0.0).infinite? #=> 1 + */ + +static mrb_value +flo_infinite_p(mrb_state *mrb, mrb_value num) +{ + mrb_float value = mrb_float(num); + + if (isinf(value)) { + return mrb_fixnum_value(value < 0 ? -1 : 1); + } + return mrb_nil_value(); +} + +/* 15.2.9.3.9 */ +/* + * call-seq: + * flt.finite? -> true or false + * + * Returns <code>true</code> if <i>flt</i> is a valid IEEE floating + * point number (it is not infinite, and <code>nan?</code> is + * <code>false</code>). + * + */ + +static mrb_value +flo_finite_p(mrb_state *mrb, mrb_value num) +{ + return mrb_bool_value(isfinite(mrb_float(num))); +} + +void +mrb_check_num_exact(mrb_state *mrb, mrb_float num) +{ + if (isinf(num)) { + mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity"); + } + if (isnan(num)) { + mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); + } +} + +/* 15.2.9.3.10 */ +/* + * call-seq: + * flt.floor -> integer + * + * Returns the largest integer less than or equal to <i>flt</i>. + * + * 1.2.floor #=> 1 + * 2.0.floor #=> 2 + * (-1.2).floor #=> -2 + * (-2.0).floor #=> -2 + */ + +static mrb_value +flo_floor(mrb_state *mrb, mrb_value num) +{ + mrb_float f = floor(mrb_float(num)); + + mrb_check_num_exact(mrb, f); + if (!FIXABLE_FLOAT(f)) { + return mrb_float_value(mrb, f); + } + return mrb_fixnum_value((mrb_int)f); +} + +/* 15.2.9.3.8 */ +/* + * call-seq: + * flt.ceil -> integer + * + * Returns the smallest <code>Integer</code> greater than or equal to + * <i>flt</i>. + * + * 1.2.ceil #=> 2 + * 2.0.ceil #=> 2 + * (-1.2).ceil #=> -1 + * (-2.0).ceil #=> -2 + */ + +static mrb_value +flo_ceil(mrb_state *mrb, mrb_value num) +{ + mrb_float f = ceil(mrb_float(num)); + + mrb_check_num_exact(mrb, f); + if (!FIXABLE_FLOAT(f)) { + return mrb_float_value(mrb, f); + } + return mrb_fixnum_value((mrb_int)f); +} + +/* 15.2.9.3.12 */ +/* + * call-seq: + * 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 + * is more than zero. + * + * 1.4.round #=> 1 + * 1.5.round #=> 2 + * 1.6.round #=> 2 + * (-1.5).round #=> -2 + * + * 1.234567.round(2) #=> 1.23 + * 1.234567.round(3) #=> 1.235 + * 1.234567.round(4) #=> 1.2346 + * 1.234567.round(5) #=> 1.23457 + * + * 34567.89.round(-5) #=> 0 + * 34567.89.round(-4) #=> 30000 + * 34567.89.round(-3) #=> 35000 + * 34567.89.round(-2) #=> 34600 + * 34567.89.round(-1) #=> 34570 + * 34567.89.round(0) #=> 34568 + * 34567.89.round(1) #=> 34567.9 + * 34567.89.round(2) #=> 34567.89 + * 34567.89.round(3) #=> 34567.89 + * + */ + +static mrb_value +flo_round(mrb_state *mrb, mrb_value num) +{ + double number, f; + mrb_int ndigits = 0; + mrb_int i; + + mrb_get_args(mrb, "|i", &ndigits); + number = mrb_float(num); + + if (0 < ndigits && (isinf(number) || isnan(number))) { + return num; + } + mrb_check_num_exact(mrb, number); + + f = 1.0; + i = ndigits >= 0 ? ndigits : -ndigits; + while (--i >= 0) + f = f*10.0; + + if (isinf(f)) { + if (ndigits < 0) number = 0; + } + else { + double d; + + if (ndigits < 0) number /= f; + else number *= f; + + /* home-made inline implementation of round(3) */ + if (number > 0.0) { + d = floor(number); + number = d + (number - d >= 0.5); + } + else if (number < 0.0) { + d = ceil(number); + number = d - (d - number >= 0.5); + } + + if (ndigits < 0) number *= f; + else number /= f; + } + + if (ndigits > 0) { + if (!isfinite(number)) return num; + return mrb_float_value(mrb, number); + } + return mrb_fixnum_value((mrb_int)number); +} + +/* 15.2.9.3.14 */ +/* 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>. + */ + +static mrb_value +flo_truncate(mrb_state *mrb, mrb_value num) +{ + mrb_float f = mrb_float(num); + + if (f > 0.0) f = floor(f); + if (f < 0.0) f = ceil(f); + + mrb_check_num_exact(mrb, f); + if (!FIXABLE_FLOAT(f)) { + return mrb_float_value(mrb, f); + } + return mrb_fixnum_value((mrb_int)f); +} + +static mrb_value +flo_nan_p(mrb_state *mrb, mrb_value num) +{ + return mrb_bool_value(isnan(mrb_float(num))); +} + +/* + * Document-class: Integer + * + * <code>Integer</code> is the basis for the two concrete classes that + * hold whole numbers, <code>Bignum</code> and <code>Fixnum</code>. + * + */ + + +/* + * 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. + */ + +static mrb_value +int_to_i(mrb_state *mrb, mrb_value num) +{ + return num; +} + +mrb_value +mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y) +{ + mrb_int a; + + a = mrb_fixnum(x); + if (mrb_fixnum_p(y)) { + mrb_int b, c; + + if (a == 0) return x; + b = mrb_fixnum(y); + if (mrb_int_mul_overflow(a, b, &c)) { + return mrb_float_value(mrb, (mrb_float)a * (mrb_float)b); + } + return mrb_fixnum_value(c); + } + return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y)); +} + +/* 15.2.8.3.3 */ +/* + * call-seq: + * fix * numeric -> numeric_result + * + * Performs multiplication: the class of the resulting object depends on + * the class of <code>numeric</code> and on the magnitude of the + * result. + */ + +static mrb_value +fix_mul(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + + mrb_get_args(mrb, "o", &y); + return mrb_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); + } + else { + if (x < 0) + div = - (-x / y); + else + div = x / y; + } + mod = x - div*y; + if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) { + mod += y; + div -= 1; + } + if (divp) *divp = div; + if (modp) *modp = mod; +} + +/* 15.2.8.3.5 */ +/* + * call-seq: + * fix % other -> real + * fix.modulo(other) -> real + * + * Returns <code>fix</code> modulo <code>other</code>. + * See <code>numeric.divmod</code> for more information. + */ + +static mrb_value +fix_mod(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + mrb_int a; + + mrb_get_args(mrb, "o", &y); + a = mrb_fixnum(x); + if (mrb_fixnum_p(y)) { + mrb_int b, mod; + + if ((b=mrb_fixnum(y)) == 0) { + return mrb_float_value(mrb, NAN); + } + fixdivmod(mrb, a, b, 0, &mod); + return mrb_fixnum_value(mod); + } + else { + mrb_float mod; + + flodivmod(mrb, (mrb_float)a, mrb_to_flo(mrb, y), 0, &mod); + return mrb_float_value(mrb, mod); + } +} + +/* + * call-seq: + * fix.divmod(numeric) -> array + * + * See <code>Numeric#divmod</code>. + */ +static mrb_value +fix_divmod(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + + mrb_get_args(mrb, "o", &y); + + if (mrb_fixnum_p(y)) { + mrb_int div, mod; + + if (mrb_fixnum(y) == 0) { + return mrb_assoc_new(mrb, ((mrb_fixnum(x) == 0) ? + mrb_float_value(mrb, NAN): + mrb_float_value(mrb, INFINITY)), + mrb_float_value(mrb, NAN)); + } + fixdivmod(mrb, mrb_fixnum(x), mrb_fixnum(y), &div, &mod); + return mrb_assoc_new(mrb, mrb_fixnum_value(div), mrb_fixnum_value(mod)); + } + else { + mrb_float div, mod; + mrb_value a, b; + + flodivmod(mrb, (mrb_float)mrb_fixnum(x), mrb_to_flo(mrb, y), &div, &mod); + a = mrb_float_value(mrb, div); + b = mrb_float_value(mrb, mod); + return mrb_assoc_new(mrb, a, b); + } +} + +static mrb_value +flo_divmod(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + mrb_float div, mod; + mrb_value a, b; + + mrb_get_args(mrb, "o", &y); + + flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), &div, &mod); + a = mrb_float_value(mrb, div); + b = mrb_float_value(mrb, mod); + return mrb_assoc_new(mrb, a, b); +} + +/* 15.2.8.3.7 */ +/* + * call-seq: + * fix == other -> true or false + * + * Return <code>true</code> if <code>fix</code> equals <code>other</code> + * numerically. + * + * 1 == 2 #=> false + * 1 == 1.0 #=> true + */ + +static mrb_value +fix_equal(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + + 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_FLOAT: + return mrb_bool_value((mrb_float)mrb_fixnum(x) == mrb_float(y)); + default: + return mrb_false_value(); + } +} + +/* 15.2.8.3.8 */ +/* + * call-seq: + * ~fix -> integer + * + * One's complement: returns a number where each bit is flipped. + * ex.0---00001 (1)-> 1---11110 (-2) + * ex.0---00010 (2)-> 1---11101 (-3) + * ex.0---00100 (4)-> 1---11011 (-5) + */ + +static mrb_value +fix_rev(mrb_state *mrb, mrb_value num) +{ + mrb_int val = mrb_fixnum(num); + + return mrb_fixnum_value(~val); +} + +static mrb_value flo_and(mrb_state *mrb, mrb_value x); +static mrb_value flo_or(mrb_state *mrb, mrb_value x); +static mrb_value flo_xor(mrb_state *mrb, mrb_value x); +#define bit_op(x,y,op1,op2) do {\ + if (mrb_fixnum_p(y)) return mrb_fixnum_value(mrb_fixnum(x) op2 mrb_fixnum(y));\ + return flo_ ## op1(mrb, mrb_float_value(mrb, mrb_fixnum(x)));\ +} while(0) + +/* 15.2.8.3.9 */ +/* + * call-seq: + * fix & integer -> integer_result + * + * Bitwise AND. + */ + +static mrb_value +fix_and(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + + mrb_get_args(mrb, "o", &y); + bit_op(x, y, and, &); +} + +/* 15.2.8.3.10 */ +/* + * call-seq: + * fix | integer -> integer_result + * + * Bitwise OR. + */ + +static mrb_value +fix_or(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + + mrb_get_args(mrb, "o", &y); + bit_op(x, y, or, |); +} + +/* 15.2.8.3.11 */ +/* + * call-seq: + * fix ^ integer -> integer_result + * + * Bitwise EXCLUSIVE OR. + */ + +static mrb_value +fix_xor(mrb_state *mrb, mrb_value x) +{ + mrb_value y; + + mrb_get_args(mrb, "o", &y); + bit_op(x, y, or, ^); +} + +#define NUMERIC_SHIFT_WIDTH_MAX (MRB_INT_BIT-1) + +static mrb_value +lshift(mrb_state *mrb, mrb_int val, mrb_int width) +{ + if (width < 0) { /* mrb_int overflow */ + return mrb_float_value(mrb, INFINITY); + } + if (val > 0) { + if ((width > NUMERIC_SHIFT_WIDTH_MAX) || + (val > (MRB_INT_MAX >> width))) { + goto bit_overflow; + } + return mrb_fixnum_value(val << width); + } + else { + if ((width > NUMERIC_SHIFT_WIDTH_MAX) || + (val < (MRB_INT_MIN >> width))) { + goto bit_overflow; + } + return mrb_fixnum_value(val * (1u << width)); + } + +bit_overflow: + { + mrb_float f = (mrb_float)val; + while (width--) { + f *= 2; + } + return mrb_float_value(mrb, f); + } +} + +static mrb_value +rshift(mrb_int val, mrb_int width) +{ + if (width < 0) { /* mrb_int overflow */ + return mrb_fixnum_value(0); + } + if (width >= NUMERIC_SHIFT_WIDTH_MAX) { + if (val < 0) { + return mrb_fixnum_value(-1); + } + return mrb_fixnum_value(0); + } + return mrb_fixnum_value(val >> width); +} + +/* 15.2.8.3.12 */ +/* + * call-seq: + * 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) +{ + mrb_int width, val; + + mrb_get_args(mrb, "i", &width); + if (width == 0) { + return x; + } + val = mrb_fixnum(x); + if (val == 0) return x; + if (width < 0) { + return rshift(val, -width); + } + return lshift(mrb, val, width); +} + +/* 15.2.8.3.13 */ +/* + * call-seq: + * 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) +{ + mrb_int width, val; + + mrb_get_args(mrb, "i", &width); + if (width == 0) { + return x; + } + val = mrb_fixnum(x); + if (val == 0) return x; + if (width < 0) { + return lshift(mrb, val, -width); + } + return rshift(val, width); +} + +/* 15.2.8.3.23 */ +/* + * call-seq: + * fix.to_f -> float + * + * Converts <i>fix</i> to a <code>Float</code>. + * + */ + +static mrb_value +fix_to_f(mrb_state *mrb, mrb_value num) +{ + return mrb_float_value(mrb, (mrb_float)mrb_fixnum(num)); +} + +/* + * Document-class: FloatDomainError + * + * Raised when attempting to convert special float values + * (in particular infinite or NaN) + * to numerical classes which don't support them. + * + * Float::INFINITY.to_r + * + * <em>raises the exception:</em> + * + * FloatDomainError: Infinity + */ +/* ------------------------------------------------------------------------*/ +MRB_API mrb_value +mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x) +{ + mrb_int z = 0; + + if (!mrb_float_p(x)) { + mrb_raise(mrb, E_TYPE_ERROR, "non float value"); + z = 0; /* not reached. just suppress warnings. */ + } + else { + mrb_float d = mrb_float(x); + + if (isinf(d)) { + mrb_raise(mrb, E_FLOATDOMAIN_ERROR, d < 0 ? "-Infinity" : "Infinity"); + } + if (isnan(d)) { + mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); + } + if (FIXABLE_FLOAT(d)) { + z = (mrb_int)d; + } + else { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x); + } + } + return mrb_fixnum_value(z); +} + +mrb_value +mrb_fixnum_plus(mrb_state *mrb, mrb_value x, mrb_value y) +{ + mrb_int a; + + a = mrb_fixnum(x); + if (mrb_fixnum_p(y)) { + mrb_int b, c; + + if (a == 0) return y; + b = mrb_fixnum(y); + if (mrb_int_add_overflow(a, b, &c)) { + return mrb_float_value(mrb, (mrb_float)a + (mrb_float)b); + } + return mrb_fixnum_value(c); + } + return mrb_float_value(mrb, (mrb_float)a + mrb_to_flo(mrb, y)); +} + +/* 15.2.8.3.1 */ +/* + * call-seq: + * fix + numeric -> numeric_result + * + * Performs addition: the class of the resulting object depends on + * the class of <code>numeric</code> and on the magnitude of the + * result. + */ +static mrb_value +fix_plus(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + + mrb_get_args(mrb, "o", &other); + return mrb_fixnum_plus(mrb, self, other); +} + +mrb_value +mrb_fixnum_minus(mrb_state *mrb, mrb_value x, mrb_value y) +{ + mrb_int a; + + a = mrb_fixnum(x); + if (mrb_fixnum_p(y)) { + mrb_int b, c; + + b = mrb_fixnum(y); + if (mrb_int_sub_overflow(a, b, &c)) { + return mrb_float_value(mrb, (mrb_float)a - (mrb_float)b); + } + return mrb_fixnum_value(c); + } + return mrb_float_value(mrb, (mrb_float)a - mrb_to_flo(mrb, y)); +} + +/* 15.2.8.3.2 */ +/* 15.2.8.3.16 */ +/* + * call-seq: + * fix - numeric -> numeric_result + * + * Performs subtraction: the class of the resulting object depends on + * the class of <code>numeric</code> and on the magnitude of the + * result. + */ +static mrb_value +fix_minus(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + + mrb_get_args(mrb, "o", &other); + return mrb_fixnum_minus(mrb, self, other); +} + + +MRB_API mrb_value +mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base) +{ + char buf[MRB_INT_BIT+1]; + char *b = buf + sizeof buf; + mrb_int val = mrb_fixnum(x); + + if (base < 2 || 36 < base) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %S", mrb_fixnum_value(base)); + } + + if (val == 0) { + *--b = '0'; + } + else if (val < 0) { + do { + *--b = mrb_digitmap[-(val % base)]; + } while (val /= base); + *--b = '-'; + } + else { + do { + *--b = mrb_digitmap[(int)(val % base)]; + } while (val /= base); + } + + return mrb_str_new(mrb, b, buf + sizeof(buf) - b); +} + +/* 15.2.8.3.25 */ +/* + * call-seq: + * fix.to_s(base=10) -> string + * + * Returns a string containing the representation of <i>fix</i> radix + * <i>base</i> (between 2 and 36). + * + * 12345.to_s #=> "12345" + * 12345.to_s(2) #=> "11000000111001" + * 12345.to_s(8) #=> "30071" + * 12345.to_s(10) #=> "12345" + * 12345.to_s(16) #=> "3039" + * 12345.to_s(36) #=> "9ix" + * + */ +static mrb_value +fix_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); +} + +/* 15.2.9.3.6 */ +/* + * call-seq: + * self.f <=> other.f => -1, 0, +1 + * < => -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>. + */ +static mrb_value +num_cmp(mrb_state *mrb, mrb_value self) +{ + mrb_value other; + mrb_float x, y; + + mrb_get_args(mrb, "o", &other); + + x = mrb_to_flo(mrb, self); + switch (mrb_type(other)) { + case MRB_TT_FIXNUM: + y = (mrb_float)mrb_fixnum(other); + break; + case MRB_TT_FLOAT: + y = mrb_float(other); + break; + 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); + } +} + +/* 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) +{ + mrb_value y; + + mrb_get_args(mrb, "o", &y); + return mrb_float_value(mrb, mrb_float(x) + mrb_to_flo(mrb, y)); +} + +/* ------------------------------------------------------------------------*/ +void +mrb_init_numeric(mrb_state *mrb) +{ + struct RClass *numeric, *integer, *fixnum, *fl; + + /* 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 */ + + /* Integer Class */ + integer = mrb_define_class(mrb, "Integer", numeric); /* 15.2.8 */ + MRB_SET_INSTANCE_TT(integer, MRB_TT_FIXNUM); + mrb_undef_class_method(mrb, integer, "new"); + mrb_define_method(mrb, integer, "to_i", int_to_i, MRB_ARGS_NONE()); /* 15.2.8.3.24 */ + mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE()); + mrb_define_method(mrb, integer, "ceil", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.8 (x) */ + mrb_define_method(mrb, integer, "floor", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 (x) */ + mrb_define_method(mrb, integer, "round", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 (x) */ + mrb_define_method(mrb, integer, "truncate", int_to_i, MRB_ARGS_REQ(1)); /* 15.2.8.3.15 (x) */ + + /* Fixnum Class */ + mrb->fixnum_class = fixnum = mrb_define_class(mrb, "Fixnum", integer); + mrb_define_method(mrb, fixnum, "+", fix_plus, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */ + mrb_define_method(mrb, fixnum, "-", fix_minus, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */ + mrb_define_method(mrb, fixnum, "*", fix_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */ + mrb_define_method(mrb, fixnum, "%", fix_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */ + mrb_define_method(mrb, fixnum, "==", fix_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */ + mrb_define_method(mrb, fixnum, "~", fix_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */ + mrb_define_method(mrb, fixnum, "&", fix_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */ + mrb_define_method(mrb, fixnum, "|", fix_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */ + mrb_define_method(mrb, fixnum, "^", fix_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */ + mrb_define_method(mrb, fixnum, "<<", fix_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */ + mrb_define_method(mrb, fixnum, ">>", fix_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */ + mrb_define_method(mrb, fixnum, "eql?", fix_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ + mrb_define_method(mrb, fixnum, "to_f", fix_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */ + mrb_define_method(mrb, fixnum, "to_s", fix_to_s, MRB_ARGS_NONE()); /* 15.2.8.3.25 */ + mrb_define_method(mrb, fixnum, "inspect", fix_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */ + + /* Float Class */ + mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */ + MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT); + mrb_undef_class_method(mrb, fl, "new"); + mrb_define_method(mrb, fl, "+", flo_plus, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */ + mrb_define_method(mrb, fl, "-", flo_minus, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */ + mrb_define_method(mrb, fl, "*", flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */ + mrb_define_method(mrb, fl, "%", flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */ + mrb_define_method(mrb, fl, "==", flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */ + mrb_define_method(mrb, fl, "~", flo_rev, MRB_ARGS_NONE()); + mrb_define_method(mrb, fl, "&", flo_and, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "|", flo_or, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "^", flo_xor, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, ">>", flo_lshift, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "<<", flo_rshift, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "ceil", flo_ceil, MRB_ARGS_NONE()); /* 15.2.9.3.8 */ + mrb_define_method(mrb, fl, "finite?", flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */ + mrb_define_method(mrb, fl, "floor", flo_floor, MRB_ARGS_NONE()); /* 15.2.9.3.10 */ + mrb_define_method(mrb, fl, "infinite?", flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */ + mrb_define_method(mrb, fl, "round", flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */ + mrb_define_method(mrb, fl, "to_f", flo_to_f, MRB_ARGS_NONE()); /* 15.2.9.3.13 */ + mrb_define_method(mrb, fl, "to_i", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.14 */ + mrb_define_method(mrb, fl, "to_int", flo_truncate, MRB_ARGS_NONE()); + mrb_define_method(mrb, fl, "truncate", flo_truncate, MRB_ARGS_NONE()); /* 15.2.9.3.15 */ + mrb_define_method(mrb, fl, "divmod", flo_divmod, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, fl, "eql?", flo_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */ + + mrb_define_method(mrb, fl, "to_s", flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */ + mrb_define_method(mrb, fl, "inspect", flo_to_s, MRB_ARGS_NONE()); + mrb_define_method(mrb, fl, "nan?", flo_nan_p, MRB_ARGS_NONE()); + +#ifdef INFINITY + mrb_define_const(mrb, fl, "INFINITY", mrb_float_value(mrb, INFINITY)); +#endif +#ifdef NAN + mrb_define_const(mrb, fl, "NAN", mrb_float_value(mrb, NAN)); +#endif +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/object.c b/web/server/h2o/libh2o/deps/mruby/src/object.c new file mode 100644 index 000000000..368e90b9f --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/object.c @@ -0,0 +1,610 @@ +/* +** object.c - Object, NilClass, TrueClass, FalseClass class +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> +#include <mruby/class.h> +#include <mruby/numeric.h> +#include <mruby/string.h> +#include <mruby/class.h> + +MRB_API mrb_bool +mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2) +{ + if (mrb_type(v1) != mrb_type(v2)) return FALSE; + switch (mrb_type(v1)) { + case MRB_TT_TRUE: + return TRUE; + + case MRB_TT_FALSE: + case MRB_TT_FIXNUM: + return (mrb_fixnum(v1) == mrb_fixnum(v2)); + case MRB_TT_SYMBOL: + return (mrb_symbol(v1) == mrb_symbol(v2)); + + case MRB_TT_FLOAT: + return (mrb_float(v1) == mrb_float(v2)); + + default: + return (mrb_ptr(v1) == mrb_ptr(v2)); + } +} + +MRB_API mrb_bool +mrb_obj_equal(mrb_state *mrb, mrb_value v1, mrb_value v2) +{ + /* temporary definition */ + return mrb_obj_eq(mrb, v1, v2); +} + +MRB_API mrb_bool +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); + if (mrb_test(result)) return TRUE; + return FALSE; +} + +/* + * Document-class: NilClass + * + * The class of the singleton object <code>nil</code>. + */ + +/* 15.2.4.3.4 */ +/* + * call_seq: + * nil.nil? -> true + * + * Only the object <i>nil</i> responds <code>true</code> to <code>nil?</code>. + */ + +static mrb_value +mrb_true(mrb_state *mrb, mrb_value obj) +{ + return mrb_true_value(); +} + +/* 15.2.4.3.5 */ +/* + * call-seq: + * nil.to_s -> "" + * + * Always returns the empty string. + */ + +static mrb_value +nil_to_s(mrb_state *mrb, mrb_value obj) +{ + return mrb_str_new(mrb, 0, 0); +} + +static mrb_value +nil_inspect(mrb_state *mrb, mrb_value obj) +{ + return mrb_str_new_lit(mrb, "nil"); +} + +/*********************************************************************** + * Document-class: TrueClass + * + * The global value <code>true</code> is the only instance of class + * <code>TrueClass</code> and represents a logically true value in + * boolean expressions. The class provides operators allowing + * <code>true</code> to be used in logical expressions. + */ + +/* 15.2.5.3.1 */ +/* + * call-seq: + * true & obj -> true or false + * + * And---Returns <code>false</code> if <i>obj</i> is + * <code>nil</code> or <code>false</code>, <code>true</code> otherwise. + */ + +static mrb_value +true_and(mrb_state *mrb, mrb_value obj) +{ + mrb_bool obj2; + + mrb_get_args(mrb, "b", &obj2); + + return mrb_bool_value(obj2); +} + +/* 15.2.5.3.2 */ +/* + * call-seq: + * true ^ obj -> !obj + * + * Exclusive Or---Returns <code>true</code> if <i>obj</i> is + * <code>nil</code> or <code>false</code>, <code>false</code> + * otherwise. + */ + +static mrb_value +true_xor(mrb_state *mrb, mrb_value obj) +{ + mrb_bool obj2; + + mrb_get_args(mrb, "b", &obj2); + return mrb_bool_value(!obj2); +} + +/* 15.2.5.3.3 */ +/* + * call-seq: + * true.to_s -> "true" + * + * The string representation of <code>true</code> is "true". + */ + +static mrb_value +true_to_s(mrb_state *mrb, mrb_value obj) +{ + return mrb_str_new_lit(mrb, "true"); +} + +/* 15.2.5.3.4 */ +/* + * call-seq: + * true | obj -> true + * + * Or---Returns <code>true</code>. As <i>anObject</i> is an argument to + * a method call, it is always evaluated; there is no short-circuit + * evaluation in this case. + * + * true | puts("or") + * true || puts("logical or") + * + * <em>produces:</em> + * + * or + */ + +static mrb_value +true_or(mrb_state *mrb, mrb_value obj) +{ + return mrb_true_value(); +} + +/* + * Document-class: FalseClass + * + * The global value <code>false</code> is the only instance of class + * <code>FalseClass</code> and represents a logically false value in + * boolean expressions. The class provides operators allowing + * <code>false</code> to participate correctly in logical expressions. + * + */ + +/* 15.2.4.3.1 */ +/* 15.2.6.3.1 */ +/* + * call-seq: + * false & obj -> false + * nil & obj -> false + * + * And---Returns <code>false</code>. <i>obj</i> is always + * evaluated as it is the argument to a method call---there is no + * short-circuit evaluation in this case. + */ + +static mrb_value +false_and(mrb_state *mrb, mrb_value obj) +{ + return mrb_false_value(); +} + +/* 15.2.4.3.2 */ +/* 15.2.6.3.2 */ +/* + * call-seq: + * false ^ obj -> true or false + * nil ^ obj -> true or false + * + * Exclusive Or---If <i>obj</i> is <code>nil</code> or + * <code>false</code>, returns <code>false</code>; otherwise, returns + * <code>true</code>. + * + */ + +static mrb_value +false_xor(mrb_state *mrb, mrb_value obj) +{ + mrb_bool obj2; + + mrb_get_args(mrb, "b", &obj2); + return mrb_bool_value(obj2); +} + +/* 15.2.4.3.3 */ +/* 15.2.6.3.4 */ +/* + * call-seq: + * false | obj -> true or false + * nil | obj -> true or false + * + * Or---Returns <code>false</code> if <i>obj</i> is + * <code>nil</code> or <code>false</code>; <code>true</code> otherwise. + */ + +static mrb_value +false_or(mrb_state *mrb, mrb_value obj) +{ + mrb_bool obj2; + + mrb_get_args(mrb, "b", &obj2); + return mrb_bool_value(obj2); +} + +/* 15.2.6.3.3 */ +/* + * call-seq: + * false.to_s -> "false" + * + * 'nuf said... + */ + +static mrb_value +false_to_s(mrb_state *mrb, mrb_value obj) +{ + return mrb_str_new_lit(mrb, "false"); +} + +void +mrb_init_object(mrb_state *mrb) +{ + struct RClass *n; + struct RClass *t; + struct RClass *f; + + mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class); + MRB_SET_INSTANCE_TT(n, MRB_TT_TRUE); + 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 */ + mrb_define_method(mrb, n, "|", false_or, MRB_ARGS_REQ(1)); /* 15.2.4.3.3 */ + mrb_define_method(mrb, n, "nil?", mrb_true, MRB_ARGS_NONE()); /* 15.2.4.3.4 */ + 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()); + + 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 */ + mrb_define_method(mrb, t, "to_s", true_to_s, MRB_ARGS_NONE()); /* 15.2.5.3.3 */ + 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()); + + mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class); + MRB_SET_INSTANCE_TT(f, MRB_TT_TRUE); + 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 */ + mrb_define_method(mrb, f, "to_s", false_to_s, MRB_ARGS_NONE()); /* 15.2.6.3.3 */ + mrb_define_method(mrb, f, "|", false_or, MRB_ARGS_REQ(1)); /* 15.2.6.3.4 */ + mrb_define_method(mrb, f, "inspect", false_to_s, MRB_ARGS_NONE()); +} + +static mrb_value +inspect_type(mrb_state *mrb, mrb_value val) +{ + 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)); + } +} + +static mrb_value +convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *method, mrb_bool raise) +{ + mrb_sym m = 0; + + m = mrb_intern_cstr(mrb, method); + if (!mrb_respond_to(mrb, val, m)) { + if (raise) { + mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into %S", inspect_type(mrb, val), mrb_str_new_cstr(mrb, tname)); + } + return mrb_nil_value(); + } + return mrb_funcall_argv(mrb, val, m, 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_value v; + + if (mrb_type(val) == type) return val; + 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)); + } + 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_value v; + + if (mrb_type(val) == type && type != MRB_TT_DATA && type != MRB_TT_ISTRUCT) return val; + v = convert_type(mrb, val, tname, method, FALSE); + 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 */ + {MRB_TT_MAXDEFINE, 0} +}; + +MRB_API void +mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) +{ + const struct types *type = builtin_types; + enum mrb_vtype xt; + + xt = mrb_type(x); + if ((xt != t) || (xt == MRB_TT_DATA) || (xt == MRB_TT_ISTRUCT)) { + while (type->type < MRB_TT_MAXDEFINE) { + if (type->type == t) { + const char *etype; + + if (mrb_nil_p(x)) { + etype = "nil"; + } + else if (mrb_fixnum_p(x)) { + etype = "Fixnum"; + } + else if (mrb_type(x) == MRB_TT_SYMBOL) { + etype = "Symbol"; + } + else if (mrb_immediate_p(x)) { + etype = RSTRING_PTR(mrb_obj_as_string(mrb, x)); + } + else { + etype = mrb_obj_classname(mrb, x); + } + mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %S (expected %S)", + mrb_str_new_cstr(mrb, etype), mrb_str_new_cstr(mrb, type->name)); + } + type++; + } + mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %S (%S given)", + mrb_fixnum_value(t), mrb_fixnum_value(mrb_type(x))); + } +} + +/* 15.3.1.3.46 */ +/* + * call-seq: + * obj.to_s => string + * + * 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." + */ + +MRB_API mrb_value +mrb_any_to_s(mrb_state *mrb, mrb_value obj) +{ + 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_ptr(obj))); + mrb_str_cat_lit(mrb, str, ">"); + + return str; +} + +/* + * call-seq: + * obj.is_a?(class) => true or false + * obj.kind_of?(class) => true or false + * + * Returns <code>true</code> if <i>class</i> is the class of + * <i>obj</i>, or if <i>class</i> is one of the superclasses of + * <i>obj</i> or modules included in <i>obj</i>. + * + * module M; end + * class A + * include M + * end + * class B < A; end + * class C < B; end + * b = B.new + * b.instance_of? A #=> false + * b.instance_of? B #=> true + * b.instance_of? C #=> false + * b.instance_of? M #=> false + * b.kind_of? A #=> true + * b.kind_of? B #=> true + * b.kind_of? C #=> false + * b.kind_of? M #=> true + */ + +MRB_API mrb_bool +mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) +{ + struct RClass *cl = mrb_class(mrb, obj); + + switch (c->tt) { + 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; + cl = cl->super; + } + 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"); +} + +MRB_API mrb_value +mrb_convert_to_integer(mrb_state *mrb, mrb_value val, 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"); + } + switch (mrb_type(val)) { + case MRB_TT_FLOAT: + if (base != 0) goto arg_error; + else { + mrb_float f = mrb_float(val); + if (FIXABLE_FLOAT(f)) { + break; + } + } + return mrb_flo_to_fixnum(mrb, val); + + case MRB_TT_FIXNUM: + if (base != 0) goto arg_error; + return val; + + case MRB_TT_STRING: + string_conv: + return mrb_str_to_inum(mrb, val, base, TRUE); + + default: + break; + } + 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); +} + +MRB_API mrb_value +mrb_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_FLOAT: + return val; + + case MRB_TT_STRING: + 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"); + } +} + +MRB_API mrb_value +mrb_inspect(mrb_state *mrb, mrb_value obj) +{ + return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "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)); +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/opcode.h b/web/server/h2o/libh2o/deps/mruby/src/opcode.h new file mode 100644 index 000000000..fe4d17a21 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/opcode.h @@ -0,0 +1,2 @@ +/* this header file is to be removed soon. */ +#include <mruby/opcode.h> diff --git a/web/server/h2o/libh2o/deps/mruby/src/pool.c b/web/server/h2o/libh2o/deps/mruby/src/pool.c new file mode 100644 index 000000000..db4546abf --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/pool.c @@ -0,0 +1,198 @@ +/* +** pool.c - memory pool +** +** See Copyright Notice in mruby.h +*/ + +#include <stddef.h> +#include <stdint.h> +#include <string.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 */ + +struct mrb_pool_page { + struct mrb_pool_page *next; + size_t offset; + size_t len; + void *last; + char page[]; +}; + +struct mrb_pool { + mrb_state *mrb; + struct mrb_pool_page *pages; +}; + +#undef TEST_POOL +#ifdef TEST_POOL + +#define mrb_malloc_simple(m,s) malloc(s) +#define mrb_free(m,p) free(p) +#endif + +#ifdef POOL_ALIGNMENT +# define ALIGN_PADDING(x) ((SIZE_MAX - (x) + 1) & (POOL_ALIGNMENT - 1)) +#else +# define ALIGN_PADDING(x) (0) +#endif + +MRB_API mrb_pool* +mrb_pool_open(mrb_state *mrb) +{ + mrb_pool *pool = (mrb_pool *)mrb_malloc_simple(mrb, sizeof(mrb_pool)); + + if (pool) { + pool->mrb = mrb; + pool->pages = NULL; + } + + return pool; +} + +MRB_API void +mrb_pool_close(mrb_pool *pool) +{ + struct mrb_pool_page *page, *tmp; + + if (!pool) return; + page = pool->pages; + while (page) { + tmp = page; + page = page->next; + mrb_free(pool->mrb, tmp); + } + mrb_free(pool->mrb, pool); +} + +static struct mrb_pool_page* +page_alloc(mrb_pool *pool, size_t len) +{ + struct mrb_pool_page *page; + + if (len < POOL_PAGE_SIZE) + len = POOL_PAGE_SIZE; + page = (struct mrb_pool_page *)mrb_malloc_simple(pool->mrb, sizeof(struct mrb_pool_page)+len); + if (page) { + page->offset = 0; + page->len = len; + } + + return page; +} + +MRB_API void* +mrb_pool_alloc(mrb_pool *pool, size_t len) +{ + struct mrb_pool_page *page; + size_t n; + + if (!pool) return NULL; + len += ALIGN_PADDING(len); + page = pool->pages; + while (page) { + if (page->offset + len <= page->len) { + n = page->offset; + page->offset += len; + page->last = (char*)page->page+n; + return page->last; + } + page = page->next; + } + page = page_alloc(pool, len); + if (!page) return NULL; + page->offset = len; + page->next = pool->pages; + pool->pages = page; + + page->last = (void*)page->page; + return page->last; +} + +MRB_API mrb_bool +mrb_pool_can_realloc(mrb_pool *pool, void *p, size_t len) +{ + struct mrb_pool_page *page; + + if (!pool) return FALSE; + len += ALIGN_PADDING(len); + page = pool->pages; + while (page) { + if (page->last == p) { + size_t beg; + + beg = (char*)p - page->page; + if (beg + len > page->len) return FALSE; + return TRUE; + } + page = page->next; + } + return FALSE; +} + +MRB_API void* +mrb_pool_realloc(mrb_pool *pool, void *p, size_t oldlen, size_t newlen) +{ + struct mrb_pool_page *page; + void *np; + + if (!pool) return NULL; + oldlen += ALIGN_PADDING(oldlen); + newlen += ALIGN_PADDING(newlen); + page = pool->pages; + while (page) { + if (page->last == p) { + size_t beg; + + beg = (char*)p - page->page; + if (beg + oldlen != page->offset) break; + if (beg + newlen > page->len) { + page->offset = beg; + break; + } + page->offset = beg + newlen; + return p; + } + page = page->next; + } + np = mrb_pool_alloc(pool, newlen); + if (np == NULL) { + return NULL; + } + memcpy(np, p, oldlen); + return np; +} + +#ifdef TEST_POOL +int +main(void) +{ + int i, len = 250; + mrb_pool *pool; + void *p; + + pool = mrb_pool_open(NULL); + p = mrb_pool_alloc(pool, len); + for (i=1; i<20; i++) { + printf("%p (len=%d) %ud\n", p, len, mrb_pool_can_realloc(pool, p, len*2)); + p = mrb_pool_realloc(pool, p, len, len*2); + len *= 2; + } + mrb_pool_close(pool); + return 0; +} +#endif diff --git a/web/server/h2o/libh2o/deps/mruby/src/print.c b/web/server/h2o/libh2o/deps/mruby/src/print.c new file mode 100644 index 000000000..03b5eadfa --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/print.c @@ -0,0 +1,47 @@ +/* +** print.c - Kernel.#p +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> +#include <mruby/string.h> +#include <mruby/variable.h> + +#ifndef MRB_DISABLE_STDIO +static void +printstr(mrb_value obj, FILE *stream) +{ + if (mrb_string_p(obj)) { + fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stream); + putc('\n', stream); + } +} +#else +# define printstr(obj, stream) (void)0 +#endif + +MRB_API void +mrb_p(mrb_state *mrb, mrb_value obj) +{ + printstr(mrb_inspect(mrb, obj), stdout); +} + +MRB_API void +mrb_print_error(mrb_state *mrb) +{ + mrb_print_backtrace(mrb); + printstr(mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0), stderr); +} + +MRB_API void +mrb_show_version(mrb_state *mrb) +{ + printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")), stdout); +} + +MRB_API void +mrb_show_copyright(mrb_state *mrb) +{ + printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")), stdout); +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/proc.c b/web/server/h2o/libh2o/deps/mruby/src/proc.c new file mode 100644 index 000000000..10a2c4f33 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/proc.c @@ -0,0 +1,294 @@ +/* +** proc.c - Proc class +** +** See Copyright Notice in mruby.h +*/ + +#include <mruby.h> +#include <mruby/class.h> +#include <mruby/proc.h> +#include <mruby/opcode.h> + +static mrb_code call_iseq[] = { + MKOP_A(OP_CALL, 0), +}; + +struct RProc* +mrb_proc_new(mrb_state *mrb, 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; + if (ci) { + if (ci->proc) + p->target_class = ci->proc->target_class; + if (!p->target_class) + p->target_class = ci->target_class; + } + p->body.irep = irep; + p->env = 0; + mrb_irep_incref(mrb, irep); + + return p; +} + +static struct REnv* +env_new(mrb_state *mrb, int nlocals) +{ + 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->cxt.c = mrb->c; + e->cioff = mrb->c->ci - mrb->c->cibase; + e->stack = mrb->c->stack; + + return e; +} + +static void +closure_setup(mrb_state *mrb, struct RProc *p, int nlocals) +{ + struct REnv *e; + + if (!mrb->c->ci->env) { + e = env_new(mrb, nlocals); + mrb->c->ci->env = e; + } + else { + e = mrb->c->ci->env; + } + p->env = e; + mrb_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env); +} + +struct RProc* +mrb_closure_new(mrb_state *mrb, mrb_irep *irep) +{ + struct RProc *p = mrb_proc_new(mrb, irep); + + closure_setup(mrb, p, mrb->c->ci->proc->body.irep->nlocals); + return p; +} + +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->body.func = func; + p->flags |= MRB_PROC_CFUNC; + p->env = 0; + + return p; +} + +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_field_write_barrier(mrb, (struct RBasic *)p, (struct RBasic *)p->env); + MRB_ENV_UNSHARE_STACK(e); + e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc); + if (argv) { + for (i = 0; i < argc; ++i) { + e->stack[i] = argv[i]; + } + } + else { + for (i = 0; i < argc; ++i) { + SET_NIL_VALUE(e->stack[i]); + } + } + return p; +} + +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); +} + +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; + + if (!MRB_PROC_CFUNC_P(p)) { + mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc."); + } + if (!e) { + 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))); + } + + return e->stack[idx]; +} + +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) { + a->body.irep->refcnt++; + } + a->target_class = b->target_class; + a->env = b->env; +} + +static mrb_value +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"); + } + p = (struct RProc *)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class)); + mrb_proc_copy(p, mrb_proc_ptr(blk)); + proc = mrb_obj_value(p); + mrb_funcall_with_block(mrb, proc, mrb_intern_lit(mrb, "initialize"), 0, NULL, proc); + if (!MRB_PROC_STRICT_P(p) && + mrb->c->ci > mrb->c->cibase && p->env == mrb->c->ci[-1].env) { + p->flags |= MRB_PROC_ORPHAN; + } + return proc; +} + +static mrb_value +mrb_proc_init_copy(mrb_state *mrb, mrb_value self) +{ + mrb_value proc; + + mrb_get_args(mrb, "o", &proc); + if (mrb_type(proc) != MRB_TT_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); +} + +/* 15.2.17.4.2 */ +static mrb_value +mrb_proc_arity(mrb_state *mrb, mrb_value self) +{ + struct RProc *p = mrb_proc_ptr(self); + struct mrb_irep *irep; + mrb_code *iseq; + mrb_aspec aspec; + int ma, op, ra, pa, arity; + + if (MRB_PROC_CFUNC_P(p)) { + /* TODO cfunc aspec not implemented yet */ + return mrb_fixnum_value(-1); + } + + irep = p->body.irep; + if (!irep) { + return mrb_fixnum_value(0); + } + + iseq = irep->iseq; + /* 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); + 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 mrb_fixnum_value(arity); +} + +/* 15.3.1.2.6 */ +/* 15.3.1.3.27 */ +/* + * call-seq: + * lambda { |...| block } -> a_proc + * + * Equivalent to <code>Proc.new</code>, except the resulting Proc objects + * check the number of parameters passed when called. + */ +static mrb_value +proc_lambda(mrb_state *mrb, mrb_value self) +{ + mrb_value blk; + struct RProc *p; + + mrb_get_args(mrb, "&", &blk); + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); + } + if (mrb_type(blk) != MRB_TT_PROC) { + 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); + mrb_proc_copy(p2, p); + p2->flags |= MRB_PROC_STRICT; + return mrb_obj_value(p2); + } + return blk; +} + +void +mrb_init_proc(mrb_state *mrb) +{ + struct RProc *m; + mrb_irep *call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep)); + static const mrb_irep mrb_irep_zero = { 0 }; + + *call_irep = mrb_irep_zero; + call_irep->flags = MRB_ISEQ_NO_FREE; + call_irep->iseq = call_iseq; + call_irep->ilen = 1; + call_irep->nregs = 2; /* receiver and block */ + + mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_ANY()); + mrb_define_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()); + + 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_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 */ +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/range.c b/web/server/h2o/libh2o/deps/mruby/src/range.c new file mode 100644 index 000000000..eb9a9c61f --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/range.c @@ -0,0 +1,442 @@ +/* +** range.c - Range class +** +** 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> + +MRB_API struct RRange* +mrb_range_ptr(mrb_state *mrb, mrb_value v) +{ + struct RRange *r = (struct RRange*)mrb_ptr(v); + + if (r->edges == NULL) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range"); + } + return r; +} + +static void +range_check(mrb_state *mrb, mrb_value a, mrb_value b) +{ + mrb_value ans; + enum mrb_vtype ta; + enum mrb_vtype tb; + + 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)) { + return; + } + + ans = mrb_funcall(mrb, a, "<=>", 1, b); + if (mrb_nil_p(ans)) { + /* 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) +{ + struct RRange *r; + + range_check(mrb, beg, end); + r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class); + 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); +} + +/* + * call-seq: + * rng.first => obj + * rng.begin => obj + * + * Returns the first object in <i>rng</i>. + */ +mrb_value +mrb_range_beg(mrb_state *mrb, mrb_value range) +{ + struct RRange *r = mrb_range_ptr(mrb, range); + + return r->edges->beg; +} + +/* + * call-seq: + * rng.end => obj + * rng.last => obj + * + * Returns the object that defines the end of <i>rng</i>. + * + * (1..10).end #=> 10 + * (1...10).end #=> 10 + */ + +mrb_value +mrb_range_end(mrb_state *mrb, mrb_value range) +{ + struct RRange *r = mrb_range_ptr(mrb, range); + + return r->edges->end; +} + +/* + * call-seq: + * range.exclude_end? => true or false + * + * Returns <code>true</code> if <i>range</i> excludes its end value. + */ +mrb_value +mrb_range_excl(mrb_state *mrb, mrb_value range) +{ + struct RRange *r = mrb_range_ptr(mrb, range); + + return mrb_bool_value(r->excl); +} + +static void +range_init(mrb_state *mrb, mrb_value range, mrb_value beg, mrb_value end, mrb_bool exclude_end) +{ + struct RRange *r = mrb_range_raw_ptr(range); + + range_check(mrb, beg, end); + r->excl = exclude_end; + if (!r->edges) { + r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges)); + } + r->edges->beg = beg; + r->edges->end = end; +} +/* + * call-seq: + * Range.new(start, end, exclusive=false) => range + * + * Constructs a range using the given <i>start</i> and <i>end</i>. If the third + * 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) +{ + mrb_value beg, end; + mrb_bool exclusive; + int n; + + n = mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive); + if (n != 3) { + exclusive = FALSE; + } + /* Ranges are immutable, so that they should be initialized only once. */ + if (mrb_range_raw_ptr(range)->edges) { + mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice"); + } + range_init(mrb, range, beg, end, exclusive); + return range; +} +/* + * call-seq: + * range == obj => true or false + * + * Returns <code>true</code> only if + * 1) <i>obj</i> is a Range, + * 2) <i>obj</i> has equivalent beginning and end items (by comparing them with <code>==</code>), + * 3) <i>obj</i> has the same #exclude_end? setting as <i>rng</t>. + * + * (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) +{ + struct RRange *rr; + struct RRange *ro; + mrb_value obj, v1, v2; + + mrb_get_args(mrb, "o", &obj); + + 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(mrb, range); + ro = mrb_range_ptr(mrb, obj); + v1 = mrb_funcall(mrb, rr->edges->beg, "==", 1, ro->edges->beg); + v2 = mrb_funcall(mrb, rr->edges->end, "==", 1, ro->edges->end); + if (!mrb_bool(v1) || !mrb_bool(v2) || rr->excl != ro->excl) { + 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) +{ + mrb_value val; + struct RRange *r = mrb_range_ptr(mrb, range); + mrb_value beg, end; + mrb_bool include_p; + + mrb_get_args(mrb, "o", &val); + + beg = r->edges->beg; + end = r->edges->end; + include_p = r_le(mrb, beg, val) && /* beg <= val */ + (r->excl ? r_gt(mrb, end, val) /* end > val */ + : r_ge(mrb, end, val)); /* end >= val */ + + return mrb_bool_value(include_p); +} + +MRB_API mrb_int +mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc) +{ + mrb_int beg, end; + struct RRange *r; + + if (mrb_type(range) != MRB_TT_RANGE) return 0; + r = mrb_range_ptr(mrb, range); + + beg = mrb_int(mrb, r->edges->beg); + end = mrb_int(mrb, r->edges->end); + + if (beg < 0) { + beg += len; + if (beg < 0) return 2; + } + + if (trunc) { + if (beg > len) return 2; + if (end > len) end = len; + } + + if (end < 0) end += len; + if (!r->excl && (!trunc || end < len)) + end++; /* include end point */ + len = end - beg; + if (len < 0) len = 0; + + *begp = beg; + *lenp = len; + return 1; +} + +/* 15.2.14.4.12(x) */ +/* + * call-seq: + * rng.to_s -> string + * + * Convert this range object to a printable form. + */ + +static mrb_value +range_to_s(mrb_state *mrb, mrb_value range) +{ + mrb_value str, str2; + struct RRange *r = mrb_range_ptr(mrb, range); + + str = mrb_obj_as_string(mrb, r->edges->beg); + str2 = mrb_obj_as_string(mrb, r->edges->end); + str = mrb_str_dup(mrb, str); + mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); + mrb_str_cat_str(mrb, str, str2); + + return str; +} + +/* 15.2.14.4.13(x) */ +/* + * call-seq: + * rng.inspect -> string + * + * Convert this range object to a printable form (using + * <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(mrb, range); + + str = mrb_inspect(mrb, r->edges->beg); + str2 = mrb_inspect(mrb, r->edges->end); + str = mrb_str_dup(mrb, str); + mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); + mrb_str_cat_str(mrb, str, str2); + + return str; +} + +/* 15.2.14.4.14(x) */ +/* + * call-seq: + * rng.eql?(obj) -> true or false + * + * Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent + * beginning and end items (by comparing them with #eql?), and has the same + * #exclude_end? setting as <i>rng</i>. + * + * (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; + struct RRange *r, *o; + + mrb_get_args(mrb, "o", &obj); + + if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value(); + if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) { + return mrb_false_value(); + } + if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value(); + + r = mrb_range_ptr(mrb, range); + o = mrb_range_ptr(mrb, obj); + if (!mrb_eql(mrb, r->edges->beg, o->edges->beg) || + !mrb_eql(mrb, r->edges->end, o->edges->end) || + (r->excl != o->excl)) { + return mrb_false_value(); + } + return mrb_true_value(); +} + +/* 15.2.14.4.15(x) */ +static mrb_value +range_initialize_copy(mrb_state *mrb, mrb_value copy) +{ + mrb_value src; + struct RRange *r; + + mrb_get_args(mrb, "o", &src); + + if (mrb_obj_equal(mrb, copy, src)) return copy; + if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) { + mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class"); + } + + r = mrb_range_ptr(mrb, src); + range_init(mrb, copy, r->edges->beg, r->edges->end, r->excl); + + return copy; +} + +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)) +{ + mrb_int i, j, beg, len; + mrb_value result; + 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]))); + } + else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) { + 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)); + } + + for (; j < beg + len; ++j) { + mrb_ary_push(mrb, result, mrb_nil_value()); + } + } + else { + mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]); + } + } + + return result; +} + +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, "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) */ +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/state.c b/web/server/h2o/libh2o/deps/mruby/src/state.c new file mode 100644 index 000000000..039d67d57 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/state.c @@ -0,0 +1,303 @@ +/* +** state.c - mrb_state open/close functions +** +** See Copyright Notice in mruby.h +*/ + +#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> + +void mrb_init_core(mrb_state*); +void mrb_init_mrbgems(mrb_state*); + +void mrb_gc_init(mrb_state*, mrb_gc *gc); +void mrb_gc_destroy(mrb_state*, mrb_gc *gc); + +static mrb_value +inspect_main(mrb_state *mrb, mrb_value mod) +{ + return mrb_str_new_lit(mrb, "main"); +} + +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; + + 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->atexit_stack_len = 0; + + mrb_gc_init(mrb, &mrb->gc); + mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context)); + *mrb->c = mrb_context_zero; + mrb->root_c = mrb->c; + + mrb_init_core(mrb); + + return mrb; +} + +void* +mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud) +{ + if (size == 0) { + free(p); + return NULL; + } + else { + return realloc(p, size); + } +} + +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) +{ + mrb_state *mrb = mrb_open_allocf(mrb_default_allocf, NULL); + + return mrb; +} + +MRB_API mrb_state* +mrb_open_allocf(mrb_allocf f, void *ud) +{ + mrb_state *mrb = mrb_open_core(f, ud); + + if (mrb == NULL) { + return NULL; + } + +#ifndef DISABLE_GEMS + mrb_init_mrbgems(mrb); + mrb_gc_arena_restore(mrb, 0); +#endif + return mrb; +} + +void mrb_free_symtbl(mrb_state *mrb); + +void +mrb_irep_incref(mrb_state *mrb, mrb_irep *irep) +{ + irep->refcnt++; +} + +void +mrb_irep_decref(mrb_state *mrb, mrb_irep *irep) +{ + irep->refcnt--; + if (irep->refcnt == 0) { + mrb_irep_free(mrb, irep); + } +} + +void +mrb_irep_free(mrb_state *mrb, mrb_irep *irep) +{ + int i; + + if (!(irep->flags & MRB_ISEQ_NO_FREE)) + mrb_free(mrb, irep->iseq); + if (irep->pool) for (i=0; i<irep->plen; i++) { + if (mrb_type(irep->pool[i]) == MRB_TT_STRING) { + mrb_gc_free_str(mrb, RSTRING(irep->pool[i])); + mrb_free(mrb, mrb_obj_ptr(irep->pool[i])); + } +#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); + for (i=0; i<irep->rlen; i++) { + mrb_irep_decref(mrb, irep->reps[i]); + } + if (irep->outer) + mrb_irep_decref(mrb, irep->outer); + mrb_free(mrb, irep->reps); + mrb_free(mrb, irep->lv); + if (irep->own_filename) { + 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) +{ + struct RString *s = mrb_str_ptr(str); + struct RString *ns; + char *ptr; + mrb_int len; + + ns = (struct RString *)mrb_malloc(mrb, sizeof(struct RString)); + ns->tt = MRB_TT_STRING; + ns->c = mrb->string_class; + + if (RSTR_NOFREE_P(s)) { + ns->flags = MRB_STR_NOFREE; + ns->as.heap.ptr = s->as.heap.ptr; + ns->as.heap.len = s->as.heap.len; + ns->as.heap.aux.capa = 0; + } + else { + ns->flags = 0; + if (RSTR_EMBED_P(s)) { + ptr = s->as.ary; + len = RSTR_EMBED_LEN(s); + } + else { + ptr = s->as.heap.ptr; + len = s->as.heap.len; + } + + if (len < RSTRING_EMBED_LEN_MAX) { + RSTR_SET_EMBED_FLAG(ns); + RSTR_SET_EMBED_LEN(ns, len); + if (ptr) { + memcpy(ns->as.ary, ptr, len); + } + ns->as.ary[len] = '\0'; + } + else { + ns->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1); + ns->as.heap.len = len; + ns->as.heap.aux.capa = len; + if (ptr) { + memcpy(ns->as.heap.ptr, ptr, len); + } + ns->as.heap.ptr[len] = '\0'; + } + } + return mrb_obj_value(ns); +} + +void mrb_free_backtrace(mrb_state *mrb); + +MRB_API void +mrb_free_context(mrb_state *mrb, struct mrb_context *c) +{ + if (!c) return; + mrb_free(mrb, c->stbase); + mrb_free(mrb, c->cibase); + mrb_free(mrb, c->rescue); + mrb_free(mrb, c->ensure); + mrb_free(mrb, c); +} + +MRB_API void +mrb_close(mrb_state *mrb) +{ + if (!mrb) return; + if (mrb->atexit_stack_len > 0) { + mrb_int i; + for (i = mrb->atexit_stack_len; i > 0; --i) { + mrb->atexit_stack[i - 1](mrb); + } +#ifndef MRB_FIXED_STATE_ATEXIT_STACK + mrb_free(mrb, mrb->atexit_stack); +#endif + } + + /* free */ + mrb_gc_free_gv(mrb); + mrb_free_context(mrb, mrb->root_c); + mrb_free_symtbl(mrb); + mrb_alloca_free(mrb); + mrb_gc_destroy(mrb, &mrb->gc); + mrb_free(mrb, mrb); +} + +MRB_API mrb_irep* +mrb_add_irep(mrb_state *mrb) +{ + static const mrb_irep mrb_irep_zero = { 0 }; + mrb_irep *irep; + + irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep)); + *irep = mrb_irep_zero; + irep->refcnt = 1; + irep->own_filename = FALSE; + + return irep; +} + +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); +} + +MRB_API void +mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f) +{ +#ifdef MRB_FIXED_STATE_ATEXIT_STACK + if (mrb->atexit_stack_len + 1 > MRB_FIXED_STATE_ATEXIT_STACK_SIZE) { + mrb_raise(mrb, E_RUNTIME_ERROR, "exceeded fixed state atexit stack limit"); + } +#else + size_t stack_size; + + 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 { + mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size); + } +#endif + + mrb->atexit_stack[mrb->atexit_stack_len++] = f; +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/string.c b/web/server/h2o/libh2o/deps/mruby/src/string.c new file mode 100644 index 000000000..01d706fa3 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/string.c @@ -0,0 +1,3013 @@ +/* +** string.c - String class +** +** See Copyright Notice in mruby.h +*/ + +#ifdef _MSC_VER +# define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#include <float.h> +#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/re.h> + +typedef struct mrb_shared_string { + mrb_bool nofree : 1; + int refcnt; + char *ptr; + mrb_int len; +} mrb_shared_string; + +const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class)) + +static struct RString* +str_new_static(mrb_state *mrb, const char *p, size_t len) +{ + struct RString *s; + + if (len >= MRB_INT_MAX) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); + } + s = mrb_obj_alloc_string(mrb); + s->as.heap.len = (mrb_int)len; + s->as.heap.aux.capa = 0; /* nofree */ + s->as.heap.ptr = (char *)p; + s->flags = MRB_STR_NOFREE; + + return s; +} + +static struct RString* +str_new(mrb_state *mrb, const char *p, size_t len) +{ + struct RString *s; + + if (p && mrb_ro_data_p(p)) { + return str_new_static(mrb, p, len); + } + s = mrb_obj_alloc_string(mrb); + if (len < RSTRING_EMBED_LEN_MAX) { + RSTR_SET_EMBED_FLAG(s); + RSTR_SET_EMBED_LEN(s, len); + if (p) { + memcpy(s->as.ary, p, len); + } + } + else { + if (len >= MRB_INT_MAX) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); + } + s->as.heap.len = (mrb_int)len; + s->as.heap.aux.capa = (mrb_int)len; + s->as.heap.ptr = (char *)mrb_malloc(mrb, len+1); + if (p) { + memcpy(s->as.heap.ptr, p, len); + } + } + 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; +} + +static mrb_value +mrb_str_new_empty(mrb_state *mrb, mrb_value str) +{ + struct RString *s = str_new(mrb, 0, 0); + + str_with_class(mrb, s, str); + return mrb_obj_value(s); +} + +MRB_API mrb_value +mrb_str_new_capa(mrb_state *mrb, size_t capa) +{ + struct RString *s; + + s = mrb_obj_alloc_string(mrb); + + if (capa >= MRB_INT_MAX) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string capacity size too big"); + } + s->as.heap.len = 0; + s->as.heap.aux.capa = (mrb_int)capa; + s->as.heap.ptr = (char *)mrb_malloc(mrb, capa+1); + RSTR_PTR(s)[0] = '\0'; + + return mrb_obj_value(s); +} + +#ifndef MRB_STR_BUF_MIN_SIZE +# define MRB_STR_BUF_MIN_SIZE 128 +#endif + +MRB_API mrb_value +mrb_str_buf_new(mrb_state *mrb, size_t capa) +{ + if (capa < MRB_STR_BUF_MIN_SIZE) { + capa = MRB_STR_BUF_MIN_SIZE; + } + return mrb_str_new_capa(mrb, capa); +} + +static void +resize_capa(mrb_state *mrb, struct RString *s, size_t capacity) +{ +#if SIZE_MAX > MRB_INT_MAX + mrb_assert(capacity < MRB_INT_MAX); +#endif + if (RSTR_EMBED_P(s)) { + if (RSTRING_EMBED_LEN_MAX < capacity) { + char *const tmp = (char *)mrb_malloc(mrb, capacity+1); + const mrb_int len = RSTR_EMBED_LEN(s); + memcpy(tmp, s->as.ary, len); + RSTR_UNSET_EMBED_FLAG(s); + s->as.heap.ptr = tmp; + s->as.heap.len = len; + s->as.heap.aux.capa = (mrb_int)capacity; + } + } + else { + s->as.heap.ptr = (char*)mrb_realloc(mrb, RSTR_PTR(s), capacity+1); + s->as.heap.aux.capa = (mrb_int)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)); +} + +/* + * call-seq: (Caution! NULL string) + * String.new(str="") => new_str + * + * Returns a new string object containing a copy of <i>str</i>. + */ + +MRB_API mrb_value +mrb_str_new_cstr(mrb_state *mrb, const char *p) +{ + 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 +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); + } +} + +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)) + 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, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,1,1,1,1,1,1,1,1,1,1,1, +}; + +static mrb_int +utf8len(const char* p, const char* e) +{ + mrb_int len; + mrb_int i; + + len = utf8len_codepage[(unsigned char)*p]; + if (p + len > e) return 1; + for (i = 1; i < len; ++i) + if ((p[i] & 0xc0) != 0x80) + return 1; + return len; +} + +static mrb_int +utf8_strlen(mrb_value str, mrb_int len) +{ + mrb_int total = 0; + char* p = RSTRING_PTR(str); + char* e = p; + if (RSTRING(str)->flags & MRB_STR_NO_UTF) { + return RSTRING_LEN(str); + } + e += len < 0 ? RSTRING_LEN(str) : len; + while (p<e) { + p += utf8len(p, e); + total++; + } + if (RSTRING_LEN(str) == total) { + RSTRING(str)->flags |= MRB_STR_NO_UTF; + } + return total; +} + +#define RSTRING_CHAR_LEN(s) utf8_strlen(s, -1) + +/* map character index to byte offset index */ +static mrb_int +chars2bytes(mrb_value s, mrb_int off, mrb_int idx) +{ + mrb_int i, b, n; + const char *p = RSTRING_PTR(s) + off; + const char *e = RSTRING_END(s); + + for (b=i=0; p<e && i<idx; i++) { + n = utf8len(p, e); + b += n; + p += n; + } + return b; +} + +/* map byte offset to character index */ +static mrb_int +bytes2chars(char *p, mrb_int bi) +{ + mrb_int i, b, n; + + for (b=i=0; b<bi; i++) { + n = utf8len_codepage[(unsigned char)*p]; + b += n; + p += n; + } + if (b != bi) return -1; + return i; +} + +#define BYTES_ALIGN_CHECK(pos) if (pos < 0) return mrb_nil_value(); +#else +#define RSTRING_CHAR_LEN(s) RSTRING_LEN(s) +#define chars2bytes(p, off, ci) (ci) +#define bytes2chars(p, bi) (bi) +#define BYTES_ALIGN_CHECK(pos) +#endif + +static inline mrb_int +mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n) +{ + const unsigned char *x = xs, *xe = xs + m; + const unsigned char *y = ys; + int i; + ptrdiff_t qstable[256]; + + /* 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; +} + +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; + + 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); + + 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_make_shared(mrb_state *mrb, struct RString *s) +{ + if (!RSTR_SHARED_P(s)) { + mrb_shared_string *shared = (mrb_shared_string *)mrb_malloc(mrb, sizeof(mrb_shared_string)); + + 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; + } + } + shared->len = s->as.heap.len; + s->as.heap.aux.shared = shared; + RSTR_SET_SHARED_FLAG(s); + } +} + +static mrb_value +byte_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) || RSTR_LEN(orig) == 0) { + 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); +} +#ifdef MRB_UTF8_STRING +static inline mrb_value +str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) +{ + beg = chars2bytes(str, 0, beg); + len = chars2bytes(str, beg, len); + + return byte_subseq(mrb, str, beg, len); +} +#else +#define str_subseq(mrb, str, beg, len) byte_subseq(mrb, str, beg, len) +#endif + +static mrb_value +str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) +{ + mrb_int clen = RSTRING_CHAR_LEN(str); + + if (len < 0) return mrb_nil_value(); + if (clen == 0) { + len = 0; + } + else if (beg < 0) { + beg = clen + beg; + } + if (beg > clen) return mrb_nil_value(); + if (beg < 0) { + beg += clen; + if (beg < 0) return mrb_nil_value(); + } + if (len > clen - beg) + len = clen - beg; + if (len <= 0) { + len = 0; + } + return str_subseq(mrb, str, beg, len); +} + +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 void +check_frozen(mrb_state *mrb, struct RString *s) +{ + if (MRB_FROZEN_P(s)) { + mrb_raise(mrb, E_RUNTIME_ERROR, "can't modify frozen string"); + } +} + +static mrb_value +str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) +{ + long len; + + check_frozen(mrb, s1); + if (s1 == s2) return mrb_obj_value(s1); + s1->flags &= ~MRB_STR_NO_UTF; + s1->flags |= s2->flags&MRB_STR_NO_UTF; + 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); +} + +static mrb_int +str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) +{ + 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 (mrb_int)(s - RSTR_PTR(ps)); + } + s--; + } + return -1; + } + else { + return pos; + } +} + +MRB_API mrb_int +mrb_str_strlen(mrb_state *mrb, struct RString *s) +{ + mrb_int i, max = RSTR_LEN(s); + char *p = RSTR_PTR(s); + + if (!p) return 0; + for (i=0; i<max; i++) { + if (p[i] == '\0') { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); + } + } + 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(mrb_state *mrb, struct RString *s) +{ + check_frozen(mrb, s); + s->flags &= ~MRB_STR_NO_UTF; + if (RSTR_SHARED_P(s)) { + mrb_shared_string *shared = s->as.heap.aux.shared; + + if (shared->nofree == 0 && shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { + s->as.heap.ptr = shared->ptr; + s->as.heap.aux.capa = shared->len; + RSTR_PTR(s)[s->as.heap.len] = '\0'; + mrb_free(mrb, shared); + } + else { + char *ptr, *p; + mrb_int len; + + p = RSTR_PTR(s); + len = s->as.heap.len; + if (len < RSTRING_EMBED_LEN_MAX) { + RSTR_SET_EMBED_FLAG(s); + RSTR_SET_EMBED_LEN(s, len); + ptr = RSTR_PTR(s); + } + else { + ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); + s->as.heap.ptr = ptr; + s->as.heap.aux.capa = len; + } + if (p) { + memcpy(ptr, p, len); + } + ptr[len] = '\0'; + str_decref(mrb, shared); + } + RSTR_UNSET_SHARED_FLAG(s); + return; + } + if (RSTR_NOFREE_P(s)) { + char *p = s->as.heap.ptr; + mrb_int len = s->as.heap.len; + + RSTR_UNSET_NOFREE_FLAG(s); + if (len < RSTRING_EMBED_LEN_MAX) { + RSTR_SET_EMBED_FLAG(s); + RSTR_SET_EMBED_LEN(s, len); + } + else { + s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)len+1); + s->as.heap.aux.capa = len; + } + if (p) { + memcpy(RSTR_PTR(s), p, len); + } + RSTR_PTR(s)[len] = '\0'; + return; + } +} + +MRB_API mrb_value +mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) +{ + mrb_int slen; + struct RString *s = mrb_str_ptr(str); + + 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; + + if (!mrb_string_p(str0)) { + mrb_raise(mrb, E_TYPE_ERROR, "expected String"); + } + + s = str_new(mrb, RSTRING_PTR(str0), RSTRING_LEN(str0)); + if ((strlen(RSTR_PTR(s)) ^ RSTR_LEN(s)) != 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); + } + return RSTR_PTR(s); +} + +/* + * call-seq: (Caution! String("abcd") change) + * String("abcdefg") = String("abcd") + String("efg") + * + * Returns a new string object containing a copy of <i>str</i>. + */ +MRB_API void +mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) +{ + if (!mrb_string_p(other)) { + other = mrb_str_to_str(mrb, other); + } + 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) +{ + struct RString *s = mrb_str_ptr(a); + struct RString *s2 = mrb_str_ptr(b); + struct RString *t; + + t = str_new(mrb, 0, RSTR_LEN(s) + RSTR_LEN(s2)); + memcpy(RSTR_PTR(t), RSTR_PTR(s), RSTR_LEN(s)); + memcpy(RSTR_PTR(t) + RSTR_LEN(s), RSTR_PTR(s2), RSTR_LEN(s2)); + + return mrb_obj_value(t); +} + +/* 15.2.10.5.2 */ + +/* + * call-seq: (Caution! String("abcd") remain) for stack_argument + * String("abcdefg") = String("abcd") + String("efg") + * + * Returns a new string object containing a copy of <i>str</i>. + */ +static mrb_value +mrb_str_plus_m(mrb_state *mrb, mrb_value self) +{ + mrb_value str; + + mrb_get_args(mrb, "S", &str); + return mrb_str_plus(mrb, self, str); +} + +/* 15.2.10.5.26 */ +/* 15.2.10.5.33 */ +/* + * call-seq: + * "abcd".size => int + * + * Returns the length of string. + */ +static mrb_value +mrb_str_size(mrb_state *mrb, mrb_value self) +{ + 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 */ +/* + * call-seq: + * str * integer => new_str + * + * Copy---Returns a new <code>String</code> containing <i>integer</i> copies of + * the receiver. + * + * "Ho! " * 3 #=> "Ho! Ho! Ho! " + */ +static mrb_value +mrb_str_times(mrb_state *mrb, mrb_value self) +{ + mrb_int n,len,times; + struct RString *str2; + char *p; + + mrb_get_args(mrb, "i", ×); + if (times < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); + } + if (times && MRB_INT_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); + memcpy(p, RSTRING_PTR(self), n); + while (n <= len/2) { + memcpy(p + n, p, n); + n *= 2; + } + memcpy(p + n, p, len-n); + } + p[RSTR_LEN(str2)] = '\0'; + + return mrb_obj_value(str2); +} +/* -------------------------------------------------------------- */ + +#define lesser(a,b) (((a)>(b))?(b):(a)) + +/* ---------------------------*/ +/* + * call-seq: + * mrb_value str1 <=> mrb_value str2 => int + * > 1 + * = 0 + * < -1 + */ +MRB_API int +mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) +{ + mrb_int len; + mrb_int retval; + struct RString *s1 = mrb_str_ptr(str1); + struct RString *s2 = mrb_str_ptr(str2); + + len = lesser(RSTR_LEN(s1), RSTR_LEN(s2)); + retval = memcmp(RSTR_PTR(s1), RSTR_PTR(s2), len); + if (retval == 0) { + if (RSTR_LEN(s1) == RSTR_LEN(s2)) return 0; + if (RSTR_LEN(s1) > RSTR_LEN(s2)) return 1; + return -1; + } + if (retval > 0) return 1; + return -1; +} + +/* 15.2.10.5.3 */ + +/* + * call-seq: + * str <=> other_str => -1, 0, +1 + * + * Comparison---Returns -1 if <i>other_str</i> is less than, 0 if + * <i>other_str</i> is equal to, and +1 if <i>other_str</i> is greater than + * <i>str</i>. If the strings are of different lengths, and the strings are + * equal when compared up to the shortest length, then the longer string is + * considered greater than the shorter one. If the variable <code>$=</code> is + * <code>false</code>, the comparison is based on comparing the binary values + * of each character in the string. In older versions of Ruby, setting + * <code>$=</code> allowed case-insensitive comparisons; this is now deprecated + * in favor of using <code>String#casecmp</code>. + * + * <code><=></code> is the basis for the methods <code><</code>, + * <code><=</code>, <code>></code>, <code>>=</code>, and <code>between?</code>, + * included from module <code>Comparable</code>. The method + * <code>String#==</code> does not use <code>Comparable#==</code>. + * + * "abcdef" <=> "abcde" #=> 1 + * "abcdef" <=> "abcdef" #=> 0 + * "abcdef" <=> "abcdefg" #=> -1 + * "abcdef" <=> "ABCDEF" #=> 1 + */ +static mrb_value +mrb_str_cmp_m(mrb_state *mrb, mrb_value str1) +{ + mrb_value str2; + mrb_int result; + + mrb_get_args(mrb, "o", &str2); + if (!mrb_string_p(str2)) { + if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "to_s"))) { + return mrb_nil_value(); + } + else if (!mrb_respond_to(mrb, str2, mrb_intern_lit(mrb, "<=>"))) { + return mrb_nil_value(); + } + else { + mrb_value tmp = mrb_funcall(mrb, str2, "<=>", 1, str1); + + if (!mrb_nil_p(tmp)) return mrb_nil_value(); + if (!mrb_fixnum_p(tmp)) { + return mrb_funcall(mrb, mrb_fixnum_value(0), "-", 1, tmp); + } + result = -mrb_fixnum(tmp); + } + } + else { + result = mrb_str_cmp(mrb, str1, str2); + } + return mrb_fixnum_value(result); +} + +static mrb_bool +str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2) +{ + const mrb_int len = RSTRING_LEN(str1); + + if (len != RSTRING_LEN(str2)) return FALSE; + if (memcmp(RSTRING_PTR(str1), RSTRING_PTR(str2), (size_t)len) == 0) + return TRUE; + return FALSE; +} + +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); + } + return str_eql(mrb, str1, str2); +} + +/* 15.2.10.5.4 */ +/* + * call-seq: + * str == obj => true or false + * + * Equality--- + * If <i>obj</i> is not a <code>String</code>, returns <code>false</code>. + * Otherwise, returns <code>false</code> or <code>true</code> + * + * caution:if <i>str</i> <code><=></code> <i>obj</i> returns zero. + */ +static mrb_value +mrb_str_equal_m(mrb_state *mrb, mrb_value str1) +{ + mrb_value str2; + + mrb_get_args(mrb, "o", &str2); + + 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; +} + +MRB_API const char* +mrb_string_value_ptr(mrb_state *mrb, mrb_value ptr) +{ + mrb_value str = mrb_str_to_str(mrb, ptr); + return RSTRING_PTR(str); +} + +MRB_API mrb_int +mrb_string_value_len(mrb_state *mrb, mrb_value ptr) +{ + mrb_value str = mrb_str_to_str(mrb, ptr); + return RSTRING_LEN(str); +} + +void +mrb_noregexp(mrb_state *mrb, mrb_value self) +{ + mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented"); +} + +void +mrb_regexp_check(mrb_state *mrb, mrb_value obj) +{ + if (mrb_regexp_p(mrb, obj)) { + mrb_noregexp(mrb, obj); + } +} + +MRB_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); + + str_with_class(mrb, dup, str); + return str_replace(mrb, dup, s); +} + +static mrb_value +mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx) +{ + mrb_int idx; + + mrb_regexp_check(mrb, indx); + switch (mrb_type(indx)) { + case MRB_TT_FIXNUM: + idx = mrb_fixnum(indx); + +num_index: + str = str_substr(mrb, str, idx, 1); + if (!mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value(); + return str; + + case MRB_TT_STRING: + if (str_index_str(mrb, str, indx, 0) != -1) + return mrb_str_dup(mrb, indx); + return mrb_nil_value(); + + case MRB_TT_RANGE: + goto range_arg; + + default: + indx = mrb_Integer(mrb, indx); + if (mrb_nil_p(indx)) { + range_arg: + { + mrb_int beg, len; + + len = RSTRING_CHAR_LEN(str); + switch (mrb_range_beg_len(mrb, indx, &beg, &len, len, TRUE)) { + case 1: + return str_subseq(mrb, str, beg, len); + case 2: + return mrb_nil_value(); + default: + break; + } + } + mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum"); + } + idx = mrb_fixnum(indx); + goto num_index; + } + return mrb_nil_value(); /* not reached */ +} + +/* 15.2.10.5.6 */ +/* 15.2.10.5.34 */ +/* + * call-seq: + * 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> + * 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 + * 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. + * + * If a <code>String</code> is given, that string is returned if it occurs in + * <i>str</i>. In both cases, <code>nil</code> is returned if there is no + * match. + * + * a = "hello there" + * a[1] #=> 101(1.8.7) "e"(1.9.2) + * a[1.1] #=> "e"(1.9.2) + * a[1,3] #=> "ell" + * a[1..3] #=> "ell" + * a[-3,2] #=> "er" + * a[-4..-2] #=> "her" + * a[12..-1] #=> nil + * a[-2..-4] #=> "" + * a["lo"] #=> "lo" + * a["bye"] #=> nil + */ +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_int n1, n2; + + mrb_regexp_check(mrb, a1); + mrb_get_args(mrb, "ii", &n1, &n2); + return str_substr(mrb, str, n1, n2); + } + if (argc != 1) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc)); + } + return mrb_str_aref(mrb, str, a1); +} + +/* 15.2.10.5.8 */ +/* + * call-seq: + * str.capitalize! => str or nil + * + * Modifies <i>str</i> by converting the first character to uppercase and the + * remainder to lowercase. Returns <code>nil</code> if no changes are made. + * + * a = "hello" + * a.capitalize! #=> "Hello" + * a #=> "Hello" + * a.capitalize! #=> nil + */ +static mrb_value +mrb_str_capitalize_bang(mrb_state *mrb, mrb_value str) +{ + char *p, *pend; + mrb_bool modify = FALSE; + struct RString *s = mrb_str_ptr(str); + + mrb_str_modify(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)) { + *p = TOUPPER(*p); + modify = TRUE; + } + while (++p < pend) { + if (ISUPPER(*p)) { + *p = TOLOWER(*p); + modify = TRUE; + } + } + if (modify) return str; + return mrb_nil_value(); +} + +/* 15.2.10.5.7 */ +/* + * call-seq: + * str.capitalize => new_str + * + * Returns a copy of <i>str</i> with the first character converted to uppercase + * and the remainder to lowercase. + * + * "hello".capitalize #=> "Hello" + * "HELLO".capitalize #=> "Hello" + * "123ABC".capitalize #=> "123abc" + */ +static mrb_value +mrb_str_capitalize(mrb_state *mrb, mrb_value self) +{ + mrb_value str; + + str = mrb_str_dup(mrb, self); + mrb_str_capitalize_bang(mrb, str); + return str; +} + +/* 15.2.10.5.10 */ +/* + * call-seq: + * 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. + */ +static mrb_value +mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) +{ + mrb_value rs; + mrb_int newline; + 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); + len = RSTR_LEN(s); + if (argc == 0) { + if (len == 0) return mrb_nil_value(); + smart_chomp: + if (RSTR_PTR(s)[len-1] == '\n') { + RSTR_SET_LEN(s, RSTR_LEN(s) - 1); + if (RSTR_LEN(s) > 0 && + RSTR_PTR(s)[RSTR_LEN(s)-1] == '\r') { + RSTR_SET_LEN(s, RSTR_LEN(s) - 1); + } + } + else if (RSTR_PTR(s)[len-1] == '\r') { + RSTR_SET_LEN(s, RSTR_LEN(s) - 1); + } + else { + return mrb_nil_value(); + } + RSTR_PTR(s)[RSTR_LEN(s)] = '\0'; + return str; + } + + if (len == 0 || mrb_nil_p(rs)) return mrb_nil_value(); + p = RSTR_PTR(s); + rslen = RSTRING_LEN(rs); + if (rslen == 0) { + while (len>0 && p[len-1] == '\n') { + len--; + if (len>0 && p[len-1] == '\r') + len--; + } + if (len < RSTR_LEN(s)) { + RSTR_SET_LEN(s, len); + p[len] = '\0'; + return str; + } + return mrb_nil_value(); + } + if (rslen > len) return mrb_nil_value(); + newline = RSTRING_PTR(rs)[rslen-1]; + if (rslen == 1 && newline == '\n') + newline = RSTRING_PTR(rs)[rslen-1]; + if (rslen == 1 && newline == '\n') + goto smart_chomp; + + pp = p + len - rslen; + if (p[len-1] == newline && + (rslen <= 1 || + memcmp(RSTRING_PTR(rs), pp, rslen) == 0)) { + RSTR_SET_LEN(s, len - rslen); + p[RSTR_LEN(s)] = '\0'; + return str; + } + return mrb_nil_value(); +} + +/* 15.2.10.5.9 */ +/* + * call-seq: + * 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>, + * <code>\r</code>, and <code>\r\n</code>). + * + * "hello".chomp #=> "hello" + * "hello\n".chomp #=> "hello" + * "hello\r\n".chomp #=> "hello" + * "hello\n\r".chomp #=> "hello\n" + * "hello\r".chomp #=> "hello" + * "hello \n there".chomp #=> "hello \n there" + * "hello".chomp("llo") #=> "he" + */ +static mrb_value +mrb_str_chomp(mrb_state *mrb, mrb_value self) +{ + mrb_value str; + + str = mrb_str_dup(mrb, self); + mrb_str_chomp_bang(mrb, str); + return str; +} + +/* 15.2.10.5.12 */ +/* + * call-seq: + * str.chop! => str or nil + * + * Processes <i>str</i> as for <code>String#chop</code>, returning <i>str</i>, + * or <code>nil</code> if <i>str</i> is the empty string. See also + * <code>String#chomp!</code>. + */ +static mrb_value +mrb_str_chop_bang(mrb_state *mrb, mrb_value str) +{ + struct RString *s = mrb_str_ptr(str); + + mrb_str_modify(mrb, s); + if (RSTR_LEN(s) > 0) { + mrb_int len; +#ifdef MRB_UTF8_STRING + const char* t = RSTR_PTR(s), *p = t; + const char* e = p + RSTR_LEN(s); + while (p<e) { + mrb_int clen = utf8len(p, e); + 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') { + len--; + } + } + RSTR_SET_LEN(s, len); + RSTR_PTR(s)[len] = '\0'; + return str; + } + return mrb_nil_value(); +} + +/* 15.2.10.5.11 */ +/* + * call-seq: + * str.chop => new_str + * + * Returns a new <code>String</code> with the last character removed. If the + * string ends with <code>\r\n</code>, both characters are removed. Applying + * <code>chop</code> to an empty string returns an empty + * string. <code>String#chomp</code> is often a safer alternative, as it leaves + * the string unchanged if it doesn't end in a record separator. + * + * "string\r\n".chop #=> "string" + * "string\n\r".chop #=> "string\n" + * "string\n".chop #=> "string" + * "string".chop #=> "strin" + * "x".chop #=> "" + */ +static mrb_value +mrb_str_chop(mrb_state *mrb, mrb_value self) +{ + mrb_value str; + str = mrb_str_dup(mrb, self); + mrb_str_chop_bang(mrb, str); + return str; +} + +/* 15.2.10.5.14 */ +/* + * call-seq: + * str.downcase! => str or nil + * + * Downcases the contents of <i>str</i>, returning <code>nil</code> if no + * changes were made. + */ +static mrb_value +mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) +{ + char *p, *pend; + mrb_bool modify = FALSE; + struct RString *s = mrb_str_ptr(str); + + mrb_str_modify(mrb, s); + p = RSTR_PTR(s); + pend = RSTR_PTR(s) + RSTR_LEN(s); + while (p < pend) { + if (ISUPPER(*p)) { + *p = TOLOWER(*p); + modify = TRUE; + } + p++; + } + + if (modify) return str; + return mrb_nil_value(); +} + +/* 15.2.10.5.13 */ +/* + * call-seq: + * str.downcase => new_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. + * + * "hEllO".downcase #=> "hello" + */ +static mrb_value +mrb_str_downcase(mrb_state *mrb, mrb_value self) +{ + mrb_value str; + + str = mrb_str_dup(mrb, self); + mrb_str_downcase_bang(mrb, str); + return str; +} + +/* 15.2.10.5.16 */ +/* + * call-seq: + * str.empty? => true or false + * + * Returns <code>true</code> if <i>str</i> has a length of zero. + * + * "hello".empty? #=> false + * "".empty? #=> true + */ +static mrb_value +mrb_str_empty_p(mrb_state *mrb, mrb_value self) +{ + struct RString *s = mrb_str_ptr(self); + + return mrb_bool_value(RSTR_LEN(s) == 0); +} + +/* 15.2.10.5.17 */ +/* + * call-seq: + * str.eql?(other) => true or false + * + * Two strings are equal if the have the same length and content. + */ +static mrb_value +mrb_str_eql(mrb_state *mrb, mrb_value self) +{ + mrb_value str2; + mrb_bool eql_p; + + mrb_get_args(mrb, "o", &str2); + eql_p = (mrb_type(str2) == MRB_TT_STRING) && str_eql(mrb, self, str2); + + return mrb_bool_value(eql_p); +} + +MRB_API mrb_value +mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) +{ + return str_substr(mrb, str, beg, len); +} + +mrb_int +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); + uint64_t key = 0; + + while (len--) { + key = key*65599 + *p; + p++; + } + return (mrb_int)(key + (key>>5)); +} + +/* 15.2.10.5.20 */ +/* + * call-seq: + * str.hash => fixnum + * + * Return a hash based on the string's length and content. + */ +static mrb_value +mrb_str_hash_m(mrb_state *mrb, mrb_value self) +{ + mrb_int key = mrb_str_hash(mrb, self); + return mrb_fixnum_value(key); +} + +/* 15.2.10.5.21 */ +/* + * call-seq: + * str.include? other_str => true or false + * str.include? fixnum => true or false + * + * Returns <code>true</code> if <i>str</i> contains the given string or + * character. + * + * "hello".include? "lo" #=> true + * "hello".include? "ol" #=> false + * "hello".include? ?h #=> true + */ +static mrb_value +mrb_str_include(mrb_state *mrb, mrb_value self) +{ + mrb_value str2; + + 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. + * If the second parameter is present, it + * specifies the position in the string to begin the search. + * + * "hello".index('e') #=> 1 + * "hello".index('lo') #=> 3 + * "hello".index('a') #=> nil + * "hello".index(101) #=> 1(101=0x65='e') + * "hello".index(/[aeiou]/, -3) #=> 4 + */ +static mrb_value +mrb_str_index_m(mrb_state *mrb, mrb_value str) +{ + mrb_value *argv; + mrb_int argc; + mrb_value sub; + mrb_int pos, clen; + + mrb_get_args(mrb, "*!", &argv, &argc); + if (argc == 2) { + mrb_get_args(mrb, "oi", &sub, &pos); + } + else { + pos = 0; + if (argc > 0) + sub = argv[0]; + else + sub = mrb_nil_value(); + } + mrb_regexp_check(mrb, sub); + clen = RSTRING_CHAR_LEN(str); + if (pos < 0) { + pos += clen; + if (pos < 0) { + return mrb_nil_value(); + } + } + if (pos > clen) return mrb_nil_value(); + pos = chars2bytes(str, 0, pos); + + switch (mrb_type(sub)) { + default: { + mrb_value tmp; + + tmp = mrb_check_string_type(mrb, sub); + if (mrb_nil_p(tmp)) { + mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub); + } + sub = tmp; + } + /* fall through */ + case MRB_TT_STRING: + pos = str_index_str(mrb, str, sub, pos); + break; + } + + if (pos == -1) return mrb_nil_value(); + pos = bytes2chars(RSTRING_PTR(str), pos); + BYTES_ALIGN_CHECK(pos); + return mrb_fixnum_value(pos); +} + +#define STR_REPLACE_SHARED_MIN 10 + +/* 15.2.10.5.24 */ +/* 15.2.10.5.28 */ +/* + * call-seq: + * str.replace(other_str) => str + * + * s = "hello" #=> "hello" + * s.replace "world" #=> "world" + */ +static mrb_value +mrb_str_replace(mrb_state *mrb, mrb_value str) +{ + mrb_value str2; + + mrb_get_args(mrb, "S", &str2); + return str_replace(mrb, mrb_str_ptr(str), mrb_str_ptr(str2)); +} + +/* 15.2.10.5.23 */ +/* + * call-seq: + * String.new(str="") => new_str + * + * Returns a new string object containing a copy of <i>str</i>. + */ +static mrb_value +mrb_str_init(mrb_state *mrb, mrb_value self) +{ + mrb_value 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; +} + +/* 15.2.10.5.25 */ +/* 15.2.10.5.41 */ +/* + * call-seq: + * str.intern => symbol + * 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>. + * + * "Koala".intern #=> :Koala + * s = 'cat'.to_sym #=> :cat + * s == :cat #=> true + * s = '@cat'.to_sym #=> :@cat + * s == :@cat #=> true + * + * This can also be used to create symbols that cannot be represented using the + * <code>:xxx</code> notation. + * + * 'cat and dog'.to_sym #=> :"cat and dog" + */ +MRB_API mrb_value +mrb_str_intern(mrb_state *mrb, mrb_value self) +{ + return mrb_symbol_value(mrb_intern_str(mrb, self)); +} +/* ---------------------------------- */ +MRB_API mrb_value +mrb_obj_as_string(mrb_state *mrb, mrb_value obj) +{ + mrb_value str; + + if (mrb_string_p(obj)) { + return obj; + } + 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 +mrb_ptr_to_str(mrb_state *mrb, void *p) +{ + struct RString *p_str; + char *p1; + char *p2; + uintptr_t n = (uintptr_t)p; + + p_str = str_new(mrb, NULL, 2 + sizeof(uintptr_t) * CHAR_BIT / 4); + p1 = RSTR_PTR(p_str); + *p1++ = '0'; + *p1++ = 'x'; + p2 = p1; + + do { + *p2++ = mrb_digitmap[n % 16]; + n /= 16; + } while (n > 0); + *p2 = '\0'; + RSTR_SET_LEN(p_str, (mrb_int)(p2 - RSTR_PTR(p_str))); + + while (p1 < p2) { + const char c = *p1; + *p1++ = *--p2; + *p2 = c; + } + + 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.30 */ +/* + * call-seq: + * str.reverse! => str + * + * Reverses <i>str</i> in place. + */ +static mrb_value +mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) +{ +#ifdef MRB_UTF8_STRING + mrb_int utf8_len = RSTRING_CHAR_LEN(str); + mrb_int len = RSTRING_LEN(str); + + if (utf8_len == len) goto bytes; + if (utf8_len > 1) { + char *buf; + char *p, *e, *r; + + mrb_str_modify(mrb, mrb_str_ptr(str)); + len = RSTRING_LEN(str); + buf = (char*)mrb_malloc(mrb, (size_t)len); + p = buf; + e = buf + len; + + memcpy(buf, RSTRING_PTR(str), len); + r = RSTRING_PTR(str) + len; + + while (p<e) { + mrb_int clen = utf8len(p, e); + r -= clen; + memcpy(r, p, clen); + p += clen; + } + mrb_free(mrb, buf); + } + return str; + + bytes: +#endif + { + struct RString *s = mrb_str_ptr(str); + char *p, *e; + char c; + + mrb_str_modify(mrb, s); + if (RSTR_LEN(s) > 1) { + p = RSTR_PTR(s); + e = p + RSTR_LEN(s) - 1; + while (p < e) { + c = *p; + *p++ = *e; + *e-- = c; + } + } + return str; + } +} + +/* ---------------------------------- */ +/* 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) +{ + 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 + * + * 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. + * + * "hello".rindex('e') #=> 1 + * "hello".rindex('l') #=> 3 + * "hello".rindex('a') #=> nil + * "hello".rindex(101) #=> 1 + * "hello".rindex(/[aeiou]/, -2) #=> 1 + */ +static mrb_value +mrb_str_rindex(mrb_state *mrb, mrb_value str) +{ + mrb_value *argv; + mrb_int argc; + mrb_value sub; + mrb_int pos, len = RSTRING_CHAR_LEN(str); + + mrb_get_args(mrb, "*!", &argv, &argc); + if (argc == 2) { + mrb_get_args(mrb, "oi", &sub, &pos); + if (pos < 0) { + pos += len; + if (pos < 0) { + mrb_regexp_check(mrb, sub); + return mrb_nil_value(); + } + } + if (pos > len) pos = len; + } + else { + pos = len; + if (argc > 0) + sub = argv[0]; + else + sub = mrb_nil_value(); + } + pos = chars2bytes(str, 0, pos); + mrb_regexp_check(mrb, sub); + + switch (mrb_type(sub)) { + default: { + mrb_value tmp; + + tmp = mrb_check_string_type(mrb, sub); + if (mrb_nil_p(tmp)) { + mrb_raisef(mrb, E_TYPE_ERROR, "type mismatch: %S given", sub); + } + sub = tmp; + } + /* fall through */ + case MRB_TT_STRING: + pos = str_rindex(mrb, str, sub, pos); + if (pos >= 0) { + pos = bytes2chars(RSTRING_PTR(str), pos); + BYTES_ALIGN_CHECK(pos); + return mrb_fixnum_value(pos); + } + break; + + } /* end of switch (TYPE(sub)) */ + return mrb_nil_value(); +} + +/* 15.2.10.5.35 */ + +/* + * call-seq: + * str.split(pattern="\n", [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 + * 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 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 + * fields will be returned (if <i>limit</i> is <code>1</code>, the entire + * string is returned as the only entry in an array). If negative, there is no + * limit to the number of fields returned, and trailing null fields are not + * suppressed. + * + * " now's the time".split #=> ["now's", "the", "time"] + * " now's the time".split(' ') #=> ["now's", "the", "time"] + * " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"] + * "hello".split(//) #=> ["h", "e", "l", "l", "o"] + * "hello".split(//, 3) #=> ["h", "e", "llo"] + * + * "mellow yellow".split("ello") #=> ["m", "w y", "w"] + * "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"] + * "1,2,,3,4,,".split(',', 4) #=> ["1", "2", "", "3,4,,"] + * "1,2,,3,4,,".split(',', -4) #=> ["1", "2", "", "3", "4", "", ""] + */ + +static mrb_value +mrb_str_split_m(mrb_state *mrb, mrb_value str) +{ + int argc; + mrb_value spat = mrb_nil_value(); + enum {awk, string, regexp} split_type = string; + 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); + lim_p = (lim > 0 && argc == 2); + if (argc == 2) { + if (lim == 1) { + if (RSTRING_LEN(str) == 0) + return mrb_ary_new_capa(mrb, 0); + return mrb_ary_new_from_values(mrb, 1, &str); + } + i = 1; + } + + 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); + } + } + + result = mrb_ary_new(mrb); + beg = 0; + if (split_type == awk) { + 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); + + idx = end = beg; + while (idx < str_len) { + c = (unsigned char)RSTRING_PTR(str)[idx++]; + if (skip) { + if (ISSPACE(c)) { + beg = idx; + } + else { + end = idx; + skip = FALSE; + if (lim_p && lim <= i) break; + } + } + else if (ISSPACE(c)) { + mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg)); + mrb_gc_arena_restore(mrb, ai); + skip = TRUE; + beg = idx; + if (lim_p) ++i; + } + else { + end = idx; + } + } + } + else if (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); + + 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 { + end = chars2bytes(str, idx, 1); + } + mrb_ary_push(mrb, result, byte_subseq(mrb, str, idx, end)); + mrb_gc_arena_restore(mrb, ai); + idx += end + pat_len; + if (lim_p && lim <= ++i) break; + } + beg = idx; + } + else { + mrb_noregexp(mrb, str); + } + if (RSTRING_LEN(str) > 0 && (lim_p || RSTRING_LEN(str) > beg || lim < 0)) { + if (RSTRING_LEN(str) == beg) { + tmp = mrb_str_new_empty(mrb, str); + } + else { + tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg); + } + mrb_ary_push(mrb, result, tmp); + } + if (!lim_p && lim == 0) { + mrb_int len; + while ((len = RARRAY_LEN(result)) > 0 && + (tmp = RARRAY_PTR(result)[len-1], RSTRING_LEN(tmp) == 0)) + mrb_ary_pop(mrb, result); + } + + return result; +} + +MRB_API mrb_value +mrb_str_len_to_inum(mrb_state *mrb, const char *str, size_t len, int base, int badcheck) +{ + const char *p = str; + const char *pend = str + len; + char sign = 1; + int c; + uint64_t n = 0; + mrb_int val; + +#define conv_digit(c) \ + (ISDIGIT(c) ? ((c) - '0') : \ + ISLOWER(c) ? ((c) - 'a' + 10) : \ + ISUPPER(c) ? ((c) - 'A' + 10) : \ + -1) + + if (!p) { + if (badcheck) goto bad; + return mrb_fixnum_value(0); + } + while (p<pend && ISSPACE(*p)) + p++; + + if (p[0] == '+') { + p++; + } + else if (p[0] == '-') { + p++; + sign = 0; + } + if (base <= 0) { + if (p[0] == '0') { + switch (p[1]) { + case 'x': case 'X': + base = 16; + break; + case 'b': case 'B': + base = 2; + break; + case 'o': case 'O': + base = 8; + break; + case 'd': case 'D': + base = 10; + break; + default: + base = 8; + break; + } + } + else if (base < -1) { + base = -base; + } + else { + base = 10; + } + } + switch (base) { + case 2: + if (p[0] == '0' && (p[1] == 'b'||p[1] == 'B')) { + p += 2; + } + break; + case 3: + break; + case 8: + if (p[0] == '0' && (p[1] == 'o'||p[1] == 'O')) { + p += 2; + } + case 4: case 5: case 6: case 7: + break; + case 10: + 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 (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)); + } + break; + } /* end of switch (base) { */ + 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 (p<pend && *p == '_') { + if (badcheck) goto bad; + break; + } + continue; + } + if (c != '0') { + p--; + break; + } + } + if (*(p - 1) == '0') + p--; + } + if (p == pend) { + if (badcheck) goto bad; + return mrb_fixnum_value(0); + } + for ( ;p<pend;p++) { + if (*p == '_') { + p++; + if (p==pend) { + if (badcheck) goto bad; + continue; + } + if (*p == '_') { + if (badcheck) goto bad; + break; + } + } + if (badcheck && *p == '\0') { + goto nullbyte; + } + c = conv_digit(*p); + if (c < 0 || c >= base) { + break; + } + n *= base; + n += c; + if (n > (uint64_t)MRB_INT_MAX + (sign ? 0 : 1)) { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", + mrb_str_new(mrb, str, pend-str)); + } + } + val = (mrb_int)n; + if (badcheck) { + if (p == str) goto bad; /* no number */ + while (p<pend && ISSPACE(*p)) p++; + if (p<pend) goto bad; /* trailing garbage */ + } + + return mrb_fixnum_value(sign ? val : -val); + nullbyte: + mrb_raise(mrb, E_ARGUMENT_ERROR, "string contains null byte"); + /* not reached */ + bad: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)", + mrb_inspect(mrb, mrb_str_new(mrb, str, pend-str))); + /* not reached */ + return mrb_fixnum_value(0); +} + +MRB_API mrb_value +mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) +{ + return mrb_str_len_to_inum(mrb, str, strlen(str), base, badcheck); +} + +MRB_API const char* +mrb_string_value_cstr(mrb_state *mrb, mrb_value *ptr) +{ + mrb_value str = mrb_str_to_str(mrb, *ptr); + struct RString *ps = mrb_str_ptr(str); + mrb_int len = mrb_str_strlen(mrb, ps); + char *p = RSTR_PTR(ps); + + if (!p || p[len] != '\0') { + if (MRB_FROZEN_P(ps)) { + *ptr = str = mrb_str_dup(mrb, str); + ps = mrb_str_ptr(str); + } + mrb_str_modify(mrb, ps); + return RSTR_PTR(ps); + } + return p; +} + +MRB_API mrb_value +mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) +{ + const char *s; + mrb_int len; + + s = mrb_string_value_ptr(mrb, str); + len = RSTRING_LEN(str); + return mrb_str_len_to_inum(mrb, s, len, base, badcheck); +} + +/* 15.2.10.5.38 */ +/* + * call-seq: + * str.to_i(base=10) => integer + * + * Returns the result of interpreting leading characters in <i>str</i> as an + * integer base <i>base</i> (between 2 and 36). 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</code> is returned. This method never raises an + * exception. + * + * "12345".to_i #=> 12345 + * "99 red balloons".to_i #=> 99 + * "0a".to_i #=> 0 + * "0a".to_i(16) #=> 10 + * "hello".to_i #=> 0 + * "1100101".to_i(2) #=> 101 + * "1100101".to_i(8) #=> 294977 + * "1100101".to_i(10) #=> 1100101 + * "1100101".to_i(16) #=> 17826049 + */ +static mrb_value +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)); + } + 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) +{ + char *end; + char buf[DBL_DIG * 4 + 10]; + double d; + + enum {max_width = 20}; + + if (!p) return 0.0; + while (ISSPACE(*p)) p++; + + if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { + return 0.0; + } + d = mrb_float_read(p, &end); + if (p == end) { + if (badcheck) { +bad: + mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for float(%S)", mrb_str_new_cstr(mrb, p)); + /* not reached */ + } + return d; + } + if (*end) { + char *n = buf; + char *e = buf + sizeof(buf) - 1; + char prev = 0; + + while (p < end && n < e) prev = *n++ = *p++; + while (*p) { + if (*p == '_') { + /* remove underscores between digits */ + if (badcheck) { + if (n == buf || !ISDIGIT(prev)) goto bad; + ++p; + if (!ISDIGIT(*p)) goto bad; + } + else { + while (*++p == '_'); + continue; + } + } + prev = *p++; + if (n < e) *n++ = prev; + } + *n = '\0'; + p = buf; + + if (!badcheck && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { + return 0.0; + } + + d = mrb_float_read(p, &end); + if (badcheck) { + if (!end || p == end) goto bad; + while (*end && ISSPACE(*end)) end++; + if (*end) goto bad; + } + } + return d; +} + +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); +} + +/* 15.2.10.5.39 */ +/* + * call-seq: + * 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 + * 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. + * + * "123.45e1".to_f #=> 1234.5 + * "45.67 degrees".to_f #=> 45.67 + * "thx1138".to_f #=> 0.0 + */ +static mrb_value +mrb_str_to_f(mrb_state *mrb, mrb_value self) +{ + return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE)); +} + +/* 15.2.10.5.40 */ +/* + * call-seq: + * str.to_s => str + * str.to_str => str + * + * Returns the receiver. + */ +static mrb_value +mrb_str_to_s(mrb_state *mrb, mrb_value self) +{ + if (mrb_obj_class(mrb, self) != mrb->string_class) { + return mrb_str_dup(mrb, self); + } + return self; +} + +/* 15.2.10.5.43 */ +/* + * call-seq: + * str.upcase! => str or nil + * + * Upcases the contents of <i>str</i>, returning <code>nil</code> if no changes + * were made. + */ +static mrb_value +mrb_str_upcase_bang(mrb_state *mrb, mrb_value str) +{ + struct RString *s = mrb_str_ptr(str); + char *p, *pend; + mrb_bool modify = FALSE; + + mrb_str_modify(mrb, s); + p = RSTRING_PTR(str); + pend = RSTRING_END(str); + while (p < pend) { + if (ISLOWER(*p)) { + *p = TOUPPER(*p); + modify = TRUE; + } + p++; + } + + if (modify) return str; + return mrb_nil_value(); +} + +/* 15.2.10.5.42 */ +/* + * call-seq: + * str.upcase => new_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. + * + * "hEllO".upcase #=> "HELLO" + */ +static mrb_value +mrb_str_upcase(mrb_state *mrb, mrb_value self) +{ + mrb_value str; + + str = mrb_str_dup(mrb, self); + mrb_str_upcase_bang(mrb, str); + return str; +} + +#define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) + +/* + * call-seq: + * str.dump -> new_str + * + * Produces a version of <i>str</i> with all nonprinting characters replaced by + * <code>\nnn</code> notation and all special characters escaped. + */ +mrb_value +mrb_str_dump(mrb_state *mrb, mrb_value str) +{ + mrb_int len; + const char *p, *pend; + char *q; + struct RString *result; + + len = 2; /* "" */ + p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); + while (p < pend) { + unsigned char c = *p++; + switch (c) { + case '"': case '\\': + case '\n': case '\r': + case '\t': case '\f': + case '\013': case '\010': case '\007': case '\033': + len += 2; + break; + + case '#': + len += IS_EVSTR(p, pend) ? 2 : 1; + break; + + default: + if (ISPRINT(c)) { + len++; + } + else { + len += 4; /* \NNN */ + } + break; + } + } + + result = str_new(mrb, 0, len); + str_with_class(mrb, result, str); + p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); + q = RSTR_PTR(result); + *q++ = '"'; + while (p < pend) { + unsigned char c = *p++; + + switch (c) { + case '"': + case '\\': + *q++ = '\\'; + *q++ = c; + break; + + case '\n': + *q++ = '\\'; + *q++ = 'n'; + break; + + case '\r': + *q++ = '\\'; + *q++ = 'r'; + break; + + case '\t': + *q++ = '\\'; + *q++ = 't'; + break; + + case '\f': + *q++ = '\\'; + *q++ = 'f'; + break; + + case '\013': + *q++ = '\\'; + *q++ = 'v'; + break; + + case '\010': + *q++ = '\\'; + *q++ = 'b'; + break; + + case '\007': + *q++ = '\\'; + *q++ = 'a'; + break; + + case '\033': + *q++ = '\\'; + *q++ = 'e'; + break; + + case '#': + if (IS_EVSTR(p, pend)) *q++ = '\\'; + *q++ = '#'; + break; + + default: + if (ISPRINT(c)) { + *q++ = c; + } + else { + *q++ = '\\'; + q[2] = '0' + c % 8; c /= 8; + q[1] = '0' + c % 8; c /= 8; + q[0] = '0' + c % 8; + q += 3; + } + } + } + *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) +{ + struct RString *s = mrb_str_ptr(str); + size_t capa; + size_t total; + ptrdiff_t off = -1; + + 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); + } + + capa = RSTR_CAPA(s); + total = RSTR_LEN(s)+len; + if (total >= MRB_INT_MAX) { + size_error: + mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); + } + if (capa <= total) { + if (capa == 0) capa = 1; + while (capa <= total) { + if (capa <= MRB_INT_MAX / 2) { + capa *= 2; + } + else { + capa = total+1; + } + } + if (capa <= total || capa > MRB_INT_MAX) { + goto size_error; + } + resize_capa(mrb, s, capa); + } + 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_int, MRB_INT_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)); +} + +MRB_API mrb_value +mrb_str_cat_str(mrb_state *mrb, mrb_value str, mrb_value str2) +{ + return mrb_str_cat(mrb, str, RSTRING_PTR(str2), RSTRING_LEN(str2)); +} + +MRB_API mrb_value +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, str1, str2); +} + +#define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */ + +/* + * call-seq: + * str.inspect -> string + * + * Returns a printable version of _str_, surrounded by quote marks, + * with special characters escaped. + * + * str = "hello" + * str[3] = "\b" + * str.inspect #=> "\"hel\\bo\"" + */ +mrb_value +mrb_str_inspect(mrb_state *mrb, mrb_value str) +{ + const char *p, *pend; + char buf[CHAR_ESC_LEN + 1]; + mrb_value result = mrb_str_new_lit(mrb, "\""); + + p = RSTRING_PTR(str); pend = RSTRING_END(str); + for (;p < pend; p++) { + unsigned char c, cc; +#ifdef MRB_UTF8_STRING + mrb_int clen; + + clen = utf8len(p, pend); + if (clen > 1) { + mrb_int i; + + for (i=0; i<clen; i++) { + buf[i] = p[i]; + } + mrb_str_cat(mrb, result, buf, clen); + p += clen-1; + continue; + } +#endif + c = *p; + if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) { + buf[0] = '\\'; buf[1] = c; + mrb_str_cat(mrb, result, buf, 2); + continue; + } + if (ISPRINT(c)) { + buf[0] = c; + mrb_str_cat(mrb, result, buf, 1); + continue; + } + switch (c) { + case '\n': cc = 'n'; break; + case '\r': cc = 'r'; break; + case '\t': cc = 't'; break; + case '\f': cc = 'f'; break; + case '\013': cc = 'v'; break; + case '\010': cc = 'b'; break; + case '\007': cc = 'a'; break; + case 033: cc = 'e'; break; + default: cc = 0; break; + } + if (cc) { + buf[0] = '\\'; + buf[1] = (char)cc; + mrb_str_cat(mrb, result, buf, 2); + continue; + } + else { + buf[0] = '\\'; + buf[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; +} + +/* + * call-seq: + * str.bytes -> array of fixnums + * + * Returns an array of bytes in _str_. + * + * str = "hello" + * str.bytes #=> [104, 101, 108, 108, 111] + */ +static mrb_value +mrb_str_bytes(mrb_state *mrb, mrb_value str) +{ + struct RString *s = mrb_str_ptr(str); + mrb_value a = mrb_ary_new_capa(mrb, RSTR_LEN(s)); + unsigned char *p = (unsigned char *)(RSTR_PTR(s)), *pend = p + RSTR_LEN(s); + + while (p < pend) { + mrb_ary_push(mrb, a, mrb_fixnum_value(p[0])); + p++; + } + return a; +} + +/* ---------------------------*/ +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->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_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, "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_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 */ + mrb_define_method(mrb, s, "eql?", mrb_str_eql, MRB_ARGS_REQ(1)); /* 15.2.10.5.17 */ + + 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, "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 */ + mrb_define_method(mrb, s, "length", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.26 */ + 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, 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 */ + + mrb_define_method(mrb, s, "to_f", mrb_str_to_f, MRB_ARGS_NONE()); /* 15.2.10.5.38 */ + 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()); + mrb_define_method(mrb, s, "to_sym", mrb_str_intern, MRB_ARGS_NONE()); /* 15.2.10.5.41 */ + mrb_define_method(mrb, s, "upcase", mrb_str_upcase, MRB_ARGS_NONE()); /* 15.2.10.5.42 */ + 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()); +} + +/* + * Source code for the "strtod" library procedure. + * + * Copyright (c) 1988-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + * + * RCS: @(#) $Id: strtod.c 11708 2007-02-12 23:01:19Z shyouhei $ + */ + +#include <ctype.h> +#include <errno.h> + +static const int maxExponent = 511; /* Largest possible base 10 exponent. Any + * exponent larger than this will already + * produce underflow or overflow, so there's + * no need to worry about additional digits. + */ +static const double powersOf10[] = {/* Table giving binary powers of 10. Entry */ + 10., /* is 10^2^i. Used to convert decimal */ + 100., /* exponents into floating-point numbers. */ + 1.0e4, + 1.0e8, + 1.0e16, + 1.0e32, + 1.0e64, + 1.0e128, + 1.0e256 +}; + +MRB_API double +mrb_float_read(const char *string, char **endPtr) +/* const char *string; A decimal ASCII floating-point number, + * optionally preceded by white space. + * Must have form "-I.FE-X", where I is the + * integer part of the mantissa, F is the + * fractional part of the mantissa, and X + * is the exponent. Either of the signs + * may be "+", "-", or omitted. Either I + * or F may be omitted, or both. The decimal + * point isn't necessary unless F is present. + * The "E" may actually be an "e". E and X + * may both be omitted (but not just one). + */ +/* char **endPtr; If non-NULL, store terminating character's + * address here. */ +{ + int sign, expSign = FALSE; + double fraction, dblExp; + const double *d; + register const char *p; + register int c; + int exp = 0; /* Exponent read from "EX" field. */ + int fracExp = 0; /* Exponent that derives from the fractional + * part. Under normal circumstatnces, it is + * the negative of the number of digits in F. + * However, if I is very long, the last digits + * of I get dropped (otherwise a long I with a + * large negative exponent could cause an + * unnecessary overflow on I alone). In this + * case, fracExp is incremented one for each + * dropped digit. */ + int mantSize; /* Number of digits in mantissa. */ + int decPt; /* Number of mantissa digits BEFORE decimal + * point. */ + const char *pExp; /* Temporarily holds location of exponent + * in string. */ + + /* + * Strip off leading blanks and check for a sign. + */ + + p = string; + while (isspace(*p)) { + p += 1; + } + if (*p == '-') { + sign = TRUE; + p += 1; + } + else { + if (*p == '+') { + p += 1; + } + sign = FALSE; + } + + /* + * Count the number of digits in the mantissa (including the decimal + * point), and also locate the decimal point. + */ + + decPt = -1; + for (mantSize = 0; ; mantSize += 1) + { + c = *p; + if (!isdigit(c)) { + if ((c != '.') || (decPt >= 0)) { + break; + } + decPt = mantSize; + } + p += 1; + } + + /* + * Now suck up the digits in the mantissa. Use two integers to + * collect 9 digits each (this is faster than using floating-point). + * If the mantissa has more than 18 digits, ignore the extras, since + * they can't affect the value anyway. + */ + + pExp = p; + p -= mantSize; + if (decPt < 0) { + decPt = mantSize; + } + else { + mantSize -= 1; /* One of the digits was the point. */ + } + if (mantSize > 18) { + if (decPt - 18 > 29999) { + fracExp = 29999; + } + else { + fracExp = decPt - 18; + } + mantSize = 18; + } + else { + fracExp = decPt - mantSize; + } + if (mantSize == 0) { + fraction = 0.0; + p = string; + goto done; + } + else { + int frac1, frac2; + frac1 = 0; + for ( ; mantSize > 9; mantSize -= 1) + { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac1 = 10*frac1 + (c - '0'); + } + frac2 = 0; + for (; mantSize > 0; mantSize -= 1) + { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac2 = 10*frac2 + (c - '0'); + } + fraction = (1.0e9 * frac1) + frac2; + } + + /* + * Skim off the exponent. + */ + + p = pExp; + if ((*p == 'E') || (*p == 'e')) { + p += 1; + if (*p == '-') { + expSign = TRUE; + p += 1; + } + else { + if (*p == '+') { + p += 1; + } + expSign = FALSE; + } + while (isdigit(*p)) { + exp = exp * 10 + (*p - '0'); + if (exp > 19999) { + exp = 19999; + } + p += 1; + } + } + if (expSign) { + exp = fracExp - exp; + } + else { + exp = fracExp + exp; + } + + /* + * Generate a floating-point number that represents the exponent. + * Do this by processing the exponent one bit at a time to combine + * many powers of 2 of 10. Then combine the exponent with the + * fraction. + */ + + if (exp < 0) { + expSign = TRUE; + exp = -exp; + } + else { + expSign = FALSE; + } + if (exp > maxExponent) { + exp = maxExponent; + errno = ERANGE; + } + dblExp = 1.0; + for (d = powersOf10; exp != 0; exp >>= 1, d += 1) { + if (exp & 01) { + dblExp *= *d; + } + } + if (expSign) { + fraction /= dblExp; + } + else { + fraction *= dblExp; + } + +done: + if (endPtr != NULL) { + *endPtr = (char *) p; + } + + if (sign) { + return -fraction; + } + return fraction; +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/symbol.c b/web/server/h2o/libh2o/deps/mruby/src/symbol.c new file mode 100644 index 000000000..a3ab05c85 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/symbol.c @@ -0,0 +1,494 @@ +/* +** symbol.c - Symbol class +** +** See Copyright Notice in mruby.h +*/ + +#include <limits.h> +#include <string.h> +#include <mruby.h> +#include <mruby/khash.h> +#include <mruby/string.h> +#include <mruby/dump.h> +#include <mruby/class.h> + +/* ------------------------------------------------------ */ +typedef struct symbol_name { + mrb_bool lit : 1; + uint16_t len; + const char *name; +} symbol_name; + +static inline khint_t +sym_hash_func(mrb_state *mrb, mrb_sym s) +{ + khint_t h = 0; + size_t i, len = mrb->symtbl[s].len; + const char *p = mrb->symtbl[s].name; + + for (i=0; i<len; i++) { + h = (h << 5) - h + *p++; + } + return h; +} +#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) +{ + if (len >= RITE_LV_NULL_MARK) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long"); + } +} + +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; + + 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); + } + + /* registering a new symbol */ + sym = ++mrb->symidx; + 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)); + } + sname = &mrb->symtbl[sym]; + sname->len = (uint16_t)len; + if (lit || mrb_ro_data_p(name)) { + sname->name = name; + sname->lit = TRUE; + } + else { + 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); + + return sym; +} + +MRB_API mrb_sym +mrb_intern(mrb_state *mrb, const char *name, size_t len) +{ + return sym_intern(mrb, name, len, FALSE); +} + +MRB_API mrb_sym +mrb_intern_static(mrb_state *mrb, const char *name, size_t len) +{ + return sym_intern(mrb, name, len, TRUE); +} + +MRB_API mrb_sym +mrb_intern_cstr(mrb_state *mrb, const char *name) +{ + return mrb_intern(mrb, name, strlen(name)); +} + +MRB_API mrb_sym +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) +{ + khash_t(n2s) *h = mrb->name2sym; + symbol_name *sname = mrb->symtbl; + khiter_t k; + + sym_validate_len(mrb, len); + sname->len = (uint16_t)len; + sname->name = name; + + 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_cstr(mrb_state *mrb, const char *name) +{ + return mrb_check_intern(mrb, name, (mrb_int)strlen(name)); +} + +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)); +} + +/* 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) +{ + if (sym == 0 || mrb->symidx < sym) { + if (lenp) *lenp = 0; + return NULL; + } + + if (lenp) *lenp = mrb->symtbl[sym].len; + return mrb->symtbl[sym].name; +} + +void +mrb_free_symtbl(mrb_state *mrb) +{ + mrb_sym i, lim; + + for (i=1, lim=mrb->symidx+1; i<lim; i++) { + if (!mrb->symtbl[i].lit) { + mrb_free(mrb, (char*)mrb->symtbl[i].name); + } + } + mrb_free(mrb, mrb->symtbl); + kh_destroy(n2s, mrb, mrb->name2sym); +} + +void +mrb_init_symtbl(mrb_state *mrb) +{ + mrb->name2sym = kh_init(n2s, mrb); +} + +/********************************************************************** + * Document-class: Symbol + * + * <code>Symbol</code> objects represent names and some strings + * inside the Ruby + * interpreter. They are generated using the <code>:name</code> and + * <code>:"string"</code> literals + * syntax, and by the various <code>to_sym</code> methods. The same + * <code>Symbol</code> object will be created for a given name or string + * for the duration of a program's execution, regardless of the context + * or meaning of that name. Thus if <code>Fred</code> is a constant in + * one context, a method in another, and a class in a third, the + * <code>Symbol</code> <code>:Fred</code> will be the same object in + * all three contexts. + * + * module One + * class Fred + * end + * $f1 = :Fred + * end + * module Two + * Fred = 1 + * $f2 = :Fred + * end + * def Fred() + * end + * $f3 = :Fred + * $f1.object_id #=> 2514190 + * $f2.object_id #=> 2514190 + * $f3.object_id #=> 2514190 + * + */ + + +/* 15.2.11.3.1 */ +/* + * call-seq: + * sym == obj -> true or false + * + * Equality---If <i>sym</i> and <i>obj</i> are exactly the same + * symbol, returns <code>true</code>. + */ + +static mrb_value +sym_equal(mrb_state *mrb, mrb_value sym1) +{ + mrb_value sym2; + + mrb_get_args(mrb, "o", &sym2); + + return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2)); +} + +/* 15.2.11.3.2 */ +/* 15.2.11.3.3 */ +/* + * call-seq: + * sym.id2name -> string + * sym.to_s -> string + * + * Returns the name or string corresponding to <i>sym</i>. + * + * :fred.id2name #=> "fred" + */ +static mrb_value +mrb_sym_to_s(mrb_state *mrb, mrb_value sym) +{ + mrb_sym id = mrb_symbol(sym); + const char *p; + mrb_int len; + + p = mrb_sym2name_len(mrb, id, &len); + return mrb_str_new_static(mrb, p, len); +} + +/* 15.2.11.3.4 */ +/* + * call-seq: + * sym.to_sym -> sym + * sym.intern -> sym + * + * In general, <code>to_sym</code> returns the <code>Symbol</code> corresponding + * to an object. As <i>sym</i> is already a symbol, <code>self</code> is returned + * in this case. + */ + +static mrb_value +sym_to_sym(mrb_state *mrb, mrb_value sym) +{ + return sym; +} + +/* 15.2.11.3.5(x) */ +/* + * call-seq: + * sym.inspect -> string + * + * Returns the representation of <i>sym</i> as a symbol literal. + * + * :fred.inspect #=> ":fred" + */ + +#if __STDC__ +# define SIGN_EXTEND_CHAR(c) ((signed char)(c)) +#else /* not __STDC__ */ +/* As in Harbison and Steele. */ +# define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128) +#endif +#define is_identchar(c) (SIGN_EXTEND_CHAR(c)!=-1&&(ISALNUM(c) || (c) == '_')) + +static mrb_bool +is_special_global_name(const char* m) +{ + switch (*m) { + case '~': case '*': case '$': case '?': case '!': case '@': + case '/': case '\\': case ';': case ',': case '.': case '=': + case ':': case '<': case '>': case '\"': + case '&': case '`': case '\'': case '+': + case '0': + ++m; + break; + case '-': + ++m; + if (is_identchar(*m)) m += 1; + break; + default: + if (!ISDIGIT(*m)) return FALSE; + do ++m; while (ISDIGIT(*m)); + break; + } + return !*m; +} + +static mrb_bool +symname_p(const char *name) +{ + const char *m = name; + mrb_bool localid = FALSE; + + if (!m) return FALSE; + switch (*m) { + case '\0': + return FALSE; + + case '$': + if (is_special_global_name(++m)) return TRUE; + goto id; + + case '@': + if (*++m == '@') ++m; + goto id; + + case '<': + switch (*++m) { + case '<': ++m; break; + case '=': if (*++m == '>') ++m; break; + default: break; + } + break; + + case '>': + switch (*++m) { + case '>': case '=': ++m; break; + default: break; + } + break; + + case '=': + switch (*++m) { + case '~': ++m; break; + case '=': if (*++m == '=') ++m; break; + default: return FALSE; + } + break; + + case '*': + if (*++m == '*') ++m; + break; + case '!': + switch (*++m) { + case '=': case '~': ++m; + } + break; + case '+': case '-': + if (*++m == '@') ++m; + break; + case '|': + if (*++m == '|') ++m; + break; + case '&': + if (*++m == '&') ++m; + break; + + case '^': case '/': case '%': case '~': case '`': + ++m; + break; + + case '[': + if (*++m != ']') return FALSE; + if (*++m == '=') ++m; + break; + + default: + localid = !ISUPPER(*m); +id: + if (*m != '_' && !ISALPHA(*m)) return FALSE; + while (is_identchar(*m)) m += 1; + if (localid) { + switch (*m) { + case '!': case '?': case '=': ++m; + default: break; + } + } + break; + } + return *m ? FALSE : TRUE; +} + +static mrb_value +sym_inspect(mrb_state *mrb, mrb_value sym) +{ + mrb_value str; + const char *name; + mrb_int len; + mrb_sym id = mrb_symbol(sym); + char *sp; + + name = mrb_sym2name_len(mrb, id, &len); + str = mrb_str_new(mrb, 0, len+1); + sp = RSTRING_PTR(str); + RSTRING_PTR(str)[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); + sp = RSTRING_PTR(str); + sp[0] = ':'; + sp[1] = '"'; + } + return str; +} + +MRB_API mrb_value +mrb_sym2str(mrb_state *mrb, mrb_sym sym) +{ + mrb_int len; + const char *name = mrb_sym2name_len(mrb, sym, &len); + + if (!name) return mrb_undef_value(); /* can't happen */ + return mrb_str_new_static(mrb, name, len); +} + +MRB_API const char* +mrb_sym2name(mrb_state *mrb, mrb_sym sym) +{ + mrb_int len; + const char *name = mrb_sym2name_len(mrb, sym, &len); + + if (!name) return NULL; + if (symname_p(name) && strlen(name) == (size_t)len) { + return name; + } + else { + mrb_value str = mrb_str_dump(mrb, mrb_str_new_static(mrb, name, len)); + return RSTRING_PTR(str); + } +} + +#define lesser(a,b) (((a)>(b))?(b):(a)) + +static mrb_value +sym_cmp(mrb_state *mrb, mrb_value s1) +{ + mrb_value s2; + mrb_sym sym1, sym2; + + mrb_get_args(mrb, "o", &s2); + if (mrb_type(s2) != MRB_TT_SYMBOL) return mrb_nil_value(); + sym1 = mrb_symbol(s1); + sym2 = mrb_symbol(s2); + if (sym1 == sym2) return mrb_fixnum_value(0); + else { + const char *p1, *p2; + int retval; + mrb_int len, len1, len2; + + p1 = mrb_sym2name_len(mrb, sym1, &len1); + p2 = mrb_sym2name_len(mrb, sym2, &len2); + len = lesser(len1, len2); + retval = memcmp(p1, p2, len); + if (retval == 0) { + if (len1 == len2) return mrb_fixnum_value(0); + if (len1 > len2) return mrb_fixnum_value(1); + return mrb_fixnum_value(-1); + } + if (retval > 0) return mrb_fixnum_value(1); + return mrb_fixnum_value(-1); + } +} + +void +mrb_init_symbol(mrb_state *mrb) +{ + struct RClass *sym; + + mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */ + MRB_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)); +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/value_array.h b/web/server/h2o/libh2o/deps/mruby/src/value_array.h new file mode 100644 index 000000000..bc5f28b06 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/value_array.h @@ -0,0 +1,27 @@ +#ifndef MRB_VALUE_ARRAY_H__ +#define MRB_VALUE_ARRAY_H__ + +#include <mruby.h> + +static inline void +value_move(mrb_value *s1, const mrb_value *s2, size_t n) +{ + if (s1 > s2 && s1 < s2 + n) + { + s1 += n; + s2 += n; + while (n-- > 0) { + *--s1 = *--s2; + } + } + else if (s1 != s2) { + while (n-- > 0) { + *s1++ = *s2++; + } + } + else { + /* nothing to do. */ + } +} + +#endif /* MRB_VALUE_ARRAY_H__ */ diff --git a/web/server/h2o/libh2o/deps/mruby/src/variable.c b/web/server/h2o/libh2o/deps/mruby/src/variable.c new file mode 100644 index 000000000..50fc70682 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/variable.c @@ -0,0 +1,987 @@ +/* +** variable.c - mruby variables +** +** 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> + +typedef int (iv_foreach_func)(mrb_state*,mrb_sym,mrb_value,void*); + +#include <mruby/khash.h> + +#ifndef MRB_IVHASH_INIT_SIZE +#define MRB_IVHASH_INIT_SIZE KHASH_MIN_SIZE +#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) + +/* Instance variable table structure */ +typedef struct iv_tbl { + khash_t(iv) h; +} iv_tbl; + +/* + * Creates the instance variable table. + * + * Parameters + * mrb + * Returns + * the instance variable table. + */ +static iv_tbl* +iv_new(mrb_state *mrb) +{ + return (iv_tbl*)kh_init_size(iv, mrb, MRB_IVHASH_INIT_SIZE); +} + +/* + * 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) +{ + khash_t(iv) *h = &t->h; + khiter_t k; + + k = kh_put(iv, mrb, h, sym); + kh_value(h, k) = val; +} + +/* + * 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. + */ +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; +} + +/* + * 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. + */ +static mrb_bool +iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp) +{ + if (t == NULL) return FALSE; + else { + khash_t(iv) *h = &t->h; + khiter_t k; + + 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) +{ + if (t == NULL) { + return TRUE; + } + else { + khash_t(iv) *h = &t->h; + khiter_t k; + int n; + + 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) +{ + if (t) { + return kh_size(&t->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); +} + +static int +iv_mark_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) +{ + mrb_gc_mark_value(mrb, v); + return 0; +} + +static void +mark_tbl(mrb_state *mrb, iv_tbl *t) +{ + if (t) { + iv_foreach(mrb, t, iv_mark_i, 0); + } +} + +void +mrb_gc_mark_gv(mrb_state *mrb) +{ + mark_tbl(mrb, mrb->globals); +} + +void +mrb_gc_free_gv(mrb_state *mrb) +{ + if (mrb->globals) + iv_free(mrb, mrb->globals); +} + +void +mrb_gc_mark_iv(mrb_state *mrb, struct RObject *obj) +{ + mark_tbl(mrb, obj->iv); +} + +size_t +mrb_gc_mark_iv_size(mrb_state *mrb, struct RObject *obj) +{ + return iv_size(mrb, obj->iv); +} + +void +mrb_gc_free_iv(mrb_state *mrb, struct RObject *obj) +{ + if (obj->iv) { + iv_free(mrb, obj->iv); + } +} + +mrb_value +mrb_vm_special_get(mrb_state *mrb, mrb_sym i) +{ + return mrb_fixnum_value(0); +} + +void +mrb_vm_special_set(mrb_state *mrb, mrb_sym i, mrb_value v) +{ +} + +static mrb_bool +obj_iv_p(mrb_value obj) +{ + switch (mrb_type(obj)) { + case MRB_TT_OBJECT: + case MRB_TT_CLASS: + case MRB_TT_MODULE: + case MRB_TT_SCLASS: + case MRB_TT_HASH: + case MRB_TT_DATA: + case MRB_TT_EXCEPTION: + return TRUE; + default: + return FALSE; + } +} + +MRB_API mrb_value +mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym) +{ + mrb_value v; + + if (obj->iv && iv_get(mrb, obj->iv, sym, &v)) + return v; + return mrb_nil_value(); +} + +MRB_API mrb_value +mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym) +{ + if (obj_iv_p(obj)) { + return mrb_obj_iv_get(mrb, mrb_obj_ptr(obj), sym); + } + return mrb_nil_value(); +} + +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 (MRB_FROZEN_P(obj)) { + mrb_raisef(mrb, E_RUNTIME_ERROR, "can't modify frozen %S", mrb_obj_value(obj)); + } + if (!t) { + t = obj->iv = iv_new(mrb); + } + mrb_write_barrier(mrb, (struct RBasic*)obj); + iv_put(mrb, t, sym, v); +} + +MRB_API void +mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v) +{ + if (obj_iv_p(obj)) { + mrb_obj_iv_set(mrb, mrb_obj_ptr(obj), sym, v); + } + else { + mrb_raise(mrb, E_ARGUMENT_ERROR, "cannot set instance variable"); + } +} + +MRB_API mrb_bool +mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym) +{ + iv_tbl *t; + + t = obj->iv; + if (t) { + return iv_get(mrb, t, sym, NULL); + } + return FALSE; +} + +MRB_API mrb_bool +mrb_iv_defined(mrb_state *mrb, mrb_value obj, mrb_sym sym) +{ + if (!obj_iv_p(obj)) return FALSE; + 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) +{ + const char *s; + mrb_int i, len; + + s = mrb_sym2name_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; +} + +MRB_API void +mrb_iv_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)); + } +} + +MRB_API void +mrb_iv_copy(mrb_state *mrb, mrb_value dest, mrb_value src) +{ + struct RObject *d = mrb_obj_ptr(dest); + struct RObject *s = mrb_obj_ptr(src); + + if (d->iv) { + iv_free(mrb, d->iv); + d->iv = 0; + } + if (s->iv) { + mrb_write_barrier(mrb, (struct RBasic*)d); + d->iv = iv_copy(mrb, s->iv); + } +} + +static int +inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) +{ + mrb_value str = *(mrb_value*)p; + const char *s; + mrb_int len; + mrb_value ins; + char *sp = RSTRING_PTR(str); + + /* need not to show internal data */ + if (sp[0] == '-') { /* first element */ + sp[0] = '#'; + mrb_str_cat_lit(mrb, str, " "); + } + else { + mrb_str_cat_lit(mrb, str, ", "); + } + s = mrb_sym2name_len(mrb, sym, &len); + mrb_str_cat(mrb, str, s, len); + mrb_str_cat_lit(mrb, str, "="); + if (mrb_type(v) == MRB_TT_OBJECT) { + ins = mrb_any_to_s(mrb, v); + } + else { + ins = mrb_inspect(mrb, v); + } + mrb_str_cat_str(mrb, str, ins); + return 0; +} + +mrb_value +mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj) +{ + iv_tbl *t = obj->iv; + size_t len = iv_size(mrb, t); + + if (len > 0) { + const char *cn = mrb_obj_classname(mrb, mrb_obj_value(obj)); + 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)); + + iv_foreach(mrb, t, inspect_i, &str); + mrb_str_cat_lit(mrb, str, ">"); + return str; + } + return mrb_any_to_s(mrb, mrb_obj_value(obj)); +} + +MRB_API mrb_value +mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym) +{ + if (obj_iv_p(obj)) { + iv_tbl *t = mrb_obj_ptr(obj)->iv; + mrb_value val; + + if (t && 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) +{ + mrb_value ary; + const char* s; + mrb_int len; + + ary = *(mrb_value*)p; + s = mrb_sym2name_len(mrb, sym, &len); + if (len > 1 && s[0] == '@' && s[1] != '@') { + mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); + } + return 0; +} + +/* 15.3.1.3.23 */ +/* + * call-seq: + * obj.instance_variables -> array + * + * Returns an array of instance variable names for the receiver. Note + * that simply defining an accessor does not create the corresponding + * instance variable. + * + * class Fred + * attr_accessor :a1 + * def initialize + * @iv = 3 + * end + * end + * Fred.new.instance_variables #=> [:@iv] + */ +mrb_value +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) { + iv_foreach(mrb, mrb_obj_ptr(self)->iv, iv_i, &ary); + } + return ary; +} + +static int +cv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) +{ + mrb_value ary; + const char* s; + mrb_int len; + + ary = *(mrb_value*)p; + s = mrb_sym2name_len(mrb, sym, &len); + if (len > 2 && s[0] == '@' && s[1] == '@') { + mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); + } + return 0; +} + +/* 15.2.2.4.19 */ +/* + * call-seq: + * mod.class_variables -> array + * + * Returns an array of the names of class variables in <i>mod</i>. + * + * class One + * @@var1 = 1 + * end + * class Two < One + * @@var2 = 2 + * end + * One.class_variables #=> [:@@var1] + * Two.class_variables #=> [:@@var2] + */ +mrb_value +mrb_mod_class_variables(mrb_state *mrb, mrb_value mod) +{ + mrb_value ary; + struct RClass *c; + + ary = mrb_ary_new(mrb); + c = mrb_class_ptr(mod); + while (c) { + if (c->iv) { + iv_foreach(mrb, c->iv, cv_i, &ary); + } + c = c->super; + } + return ary; +} + +MRB_API 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_get(mrb, c->iv, sym, &v)) { + given = TRUE; + } + c = c->super; + } + if (given) return v; + if (cls && cls->tt == MRB_TT_SCLASS) { + mrb_value klass; + + klass = mrb_obj_iv_get(mrb, (struct RObject *)cls, + mrb_intern_lit(mrb, "__attached__")); + c = mrb_class_ptr(klass); + 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 %S in %S", + mrb_sym2str(mrb, sym), mrb_obj_value(cls)); + /* not reached */ + return mrb_nil_value(); +} + +MRB_API mrb_value +mrb_cv_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) +{ + return mrb_mod_cv_get(mrb, mrb_class_ptr(mod), sym); +} + +MRB_API void +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; + + if (iv_get(mrb, t, sym, NULL)) { + mrb_write_barrier(mrb, (struct RBasic*)c); + iv_put(mrb, t, sym, v); + return; + } + } + c = c->super; + } + + if (cls && cls->tt == MRB_TT_SCLASS) { + mrb_value klass; + + klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, + mrb_intern_lit(mrb, "__attached__")); + switch (mrb_type(klass)) { + case MRB_TT_CLASS: + case MRB_TT_MODULE: + case MRB_TT_SCLASS: + c = mrb_class_ptr(klass); + break; + default: + c = cls; + break; + } + } + else{ + c = cls; + } + + if (!c->iv) { + c->iv = iv_new(mrb); + } + + mrb_write_barrier(mrb, (struct RBasic*)c); + iv_put(mrb, c->iv, sym, v); +} + +MRB_API void +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_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; + } + c = c->super; + } + + return FALSE; +} + +MRB_API mrb_bool +mrb_cv_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym) +{ + return mrb_mod_cv_defined(mrb, mrb_class_ptr(mod), sym); +} + +mrb_value +mrb_vm_cv_get(mrb_state *mrb, mrb_sym sym) +{ + struct RClass *c = mrb->c->ci->proc->target_class; + + if (!c) c = mrb->c->ci->target_class; + + 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; + + if (!c) c = mrb->c->ci->target_class; + mrb_mod_cv_set(mrb, c, sym, v); +} + +static void +mod_const_check(mrb_state *mrb, mrb_value mod) +{ + switch (mrb_type(mod)) { + case MRB_TT_CLASS: + case MRB_TT_MODULE: + case MRB_TT_SCLASS: + break; + default: + mrb_raise(mrb, E_TYPE_ERROR, "constant look-up for non class/module"); + break; + } +} + +static mrb_value +const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym) +{ + struct RClass *c = base; + mrb_value v; + iv_tbl *t; + mrb_bool retry = FALSE; + mrb_value name; + +L_RETRY: + while (c) { + if (c->iv) { + t = c->iv; + if (iv_get(mrb, t, sym, &v)) + return v; + } + c = c->super; + } + if (!retry && base && 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); +} + +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); +} + +mrb_value +mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) +{ + struct RClass *c = mrb->c->ci->proc->target_class; + struct RClass *c2; + mrb_value v; + mrb_irep *irep; + + if (!c) c = mrb->c->ci->target_class; + mrb_assert(c != NULL); + + if (c->iv && iv_get(mrb, c->iv, sym, &v)) { + return v; + } + c2 = c; + while (c2 && c2->tt == MRB_TT_SCLASS) { + mrb_value klass; + klass = mrb_obj_iv_get(mrb, (struct RObject *)c2, + mrb_intern_lit(mrb, "__attached__")); + c2 = mrb_class_ptr(klass); + } + if (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE) c = c2; + mrb_assert(!MRB_PROC_CFUNC_P(mrb->c->ci->proc)); + irep = mrb->c->ci->proc->body.irep; + while (irep) { + if (irep->target_class) { + c2 = irep->target_class; + if (c2->iv && iv_get(mrb, c2->iv, sym, &v)) { + return v; + } + } + irep = irep->outer; + } + return const_get(mrb, c, sym); +} + +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; + + if (!c) c = mrb->c->ci->target_class; + mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v); +} + +MRB_API void +mrb_const_remove(mrb_state *mrb, mrb_value mod, mrb_sym sym) +{ + mod_const_check(mrb, mod); + mrb_iv_remove(mrb, mod, sym); +} + +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); +} + +MRB_API void +mrb_define_global_const(mrb_state *mrb, const char *name, mrb_value val) +{ + mrb_define_const(mrb, mrb->object_class, name, val); +} + +static int +const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) +{ + mrb_value ary; + const char* s; + mrb_int len; + + ary = *(mrb_value*)p; + s = mrb_sym2name_len(mrb, sym, &len); + if (len >= 1 && ISUPPER(s[0])) { + mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); + } + return 0; +} + +/* 15.2.2.4.24 */ +/* + * call-seq: + * mod.constants -> array + * + * Returns an array of all names of contants defined in the receiver. + */ +mrb_value +mrb_mod_constants(mrb_state *mrb, mrb_value mod) +{ + mrb_value ary; + mrb_bool inherit = TRUE; + struct RClass *c = mrb_class_ptr(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); + } + if (!inherit) break; + c = c->super; + if (c == mrb->object_class) break; + } + return ary; +} + +MRB_API mrb_value +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(); +} + +MRB_API void +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; + } + 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); +} + +static int +gv_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) +{ + mrb_value ary; + + ary = *(mrb_value*)p; + mrb_ary_push(mrb, ary, mrb_symbol_value(sym)); + return 0; +} + +/* 15.3.1.2.4 */ +/* 15.3.1.3.14 */ +/* + * call-seq: + * global_variables -> array + * + * Returns an array of the names of global variables. + * + * global_variables.grep /std/ #=> [:$stdin, :$stdout, :$stderr] + */ +mrb_value +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))); + } + return ary; +} + +static mrb_bool +mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude, mrb_bool recurse) +{ + struct RClass *klass = mrb_class_ptr(mod); + struct RClass *tmp; + mrb_bool mod_retry = 0; + + tmp = klass; +retry: + while (tmp) { + if (tmp->iv && 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; + tmp = mrb->object_class; + goto retry; + } + return FALSE; +} + +MRB_API mrb_bool +mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id) +{ + return mrb_const_defined_0(mrb, mod, id, TRUE, TRUE); +} + +MRB_API mrb_bool +mrb_const_defined_at(mrb_state *mrb, mrb_value mod, mrb_sym id) +{ + return mrb_const_defined_0(mrb, mod, id, TRUE, FALSE); +} + +MRB_API mrb_value +mrb_attr_get(mrb_state *mrb, mrb_value obj, mrb_sym id) +{ + return mrb_iv_get(mrb, obj, id); +} + +struct csym_arg { + struct RClass *c; + mrb_sym sym; +}; + +static int +csym_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) +{ + struct csym_arg *a = (struct csym_arg*)p; + struct RClass *c = a->c; + + if (mrb_type(v) == c->tt && mrb_class_ptr(v) == c) { + a->sym = sym; + return 1; /* stop iteration */ + } + return 0; +} + +static mrb_sym +find_class_sym(mrb_state *mrb, struct RClass *outer, struct RClass *c) +{ + struct csym_arg arg; + + if (!outer) return 0; + arg.c = c; + arg.sym = 0; + iv_foreach(mrb, outer->iv, csym_i, &arg); + return arg.sym; +} + +mrb_value +mrb_class_find_path(mrb_state *mrb, struct RClass *c) +{ + mrb_value outer, path; + mrb_sym name; + const char *str; + mrb_int len; + mrb_sym osym = mrb_intern_lit(mrb, "__outer__"); + + outer = mrb_obj_iv_get(mrb, (struct RObject*)c, osym); + if (mrb_nil_p(outer)) return outer; + name = find_class_sym(mrb, mrb_class_ptr(outer), c); + if (name == 0) return mrb_nil_value(); + str = mrb_class_name(mrb, mrb_class_ptr(outer)); + path = mrb_str_new_capa(mrb, 40); + mrb_str_cat_cstr(mrb, path, str); + mrb_str_cat_cstr(mrb, path, "::"); + + str = mrb_sym2name_len(mrb, name, &len); + mrb_str_cat(mrb, path, str, len); + iv_del(mrb, c->iv, osym, NULL); + iv_put(mrb, c->iv, mrb_intern_lit(mrb, "__classname__"), path); + mrb_field_write_barrier_value(mrb, (struct RBasic*)c, path); + return path; +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/version.c b/web/server/h2o/libh2o/deps/mruby/src/version.c new file mode 100644 index 000000000..350bc1673 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/version.c @@ -0,0 +1,17 @@ +#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, "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)); +} diff --git a/web/server/h2o/libh2o/deps/mruby/src/vm.c b/web/server/h2o/libh2o/deps/mruby/src/vm.c new file mode 100644 index 000000000..f413211e7 --- /dev/null +++ b/web/server/h2o/libh2o/deps/mruby/src/vm.c @@ -0,0 +1,2909 @@ +/* +** vm.c - virtual machine for mruby +** +** See Copyright Notice in mruby.h +*/ + +#include <stddef.h> +#include <stdarg.h> +#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> +#include "value_array.h" +#include <mruby/throw.h> + +#ifdef MRB_DISABLE_STDIO +#if defined(__cplusplus) +extern "C" { +#endif +void abort(void); +#if defined(__cplusplus) +} /* extern "C" { */ +#endif +#endif + +#define STACK_INIT_SIZE 128 +#define CALLINFO_INIT_SIZE 32 + +#ifndef ENSURE_STACK_INIT_SIZE +#define ENSURE_STACK_INIT_SIZE 16 +#endif + +#ifndef RESCUE_STACK_INIT_SIZE +#define RESCUE_STACK_INIT_SIZE 16 +#endif + +/* Define amount of linear stack growth. */ +#ifndef MRB_STACK_GROWTH +#define MRB_STACK_GROWTH 128 +#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 +#define MRB_STACK_MAX (0x40000 - MRB_STACK_GROWTH) +#endif + +#ifdef VM_DEBUG +# define DEBUG(x) (x) +#else +# define DEBUG(x) +#endif + + +#ifndef MRB_GC_FIXED_ARENA +static void +mrb_gc_arena_shrink(mrb_state *mrb, int idx) +{ + mrb_gc *gc = &mrb->gc; + int capa = gc->arena_capa; + + if (idx < capa / 4) { + capa >>= 2; + if (capa < MRB_GC_ARENA_SIZE) { + capa = MRB_GC_ARENA_SIZE; + } + if (capa != gc->arena_capa) { + gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa); + gc->arena_capa = capa; + } + } +} +#else +#define mrb_gc_arena_shrink(mrb,idx) +#endif + +#define CALL_MAXARGS 127 + +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) +{ +#ifndef MRB_NAN_BOXING + const mrb_value mrb_value_zero = { { 0 } }; + + while (count-- > 0) { + *from++ = mrb_value_zero; + } +#else + while (count-- > 0) { + SET_NIL_VALUE(*from); + from++; + } +#endif +} + +static inline void +stack_copy(mrb_value *dst, const mrb_value *src, size_t size) +{ + while (size-- > 0) { + *dst++ = *src++; + } +} + +static void +stack_init(mrb_state *mrb) +{ + struct mrb_context *c = mrb->c; + + /* 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; +} + +static inline void +envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase, size_t size) +{ + mrb_callinfo *ci = mrb->c->cibase; + + if (newbase == oldbase) return; + while (ci <= mrb->c->ci) { + struct REnv *e = ci->env; + mrb_value *st; + + if (e && MRB_ENV_STACK_SHARED_P(e) && + (st = e->stack) && oldbase <= st && st < oldbase+size) { + ptrdiff_t off = e->stack - oldbase; + + e->stack = newbase + off; + } + ci->stackent = newbase + (ci->stackent - oldbase); + ci++; + } +} + +/** def rec ; $deep =+ 1 ; if $deep > 1000 ; return 0 ; end ; rec ; end */ + +static void +stack_extend_alloc(mrb_state *mrb, int room) +{ + mrb_value *oldbase = mrb->c->stbase; + mrb_value *newstack; + size_t oldsize = mrb->c->stend - mrb->c->stbase; + size_t size = oldsize; + size_t off = mrb->c->stack - mrb->c->stbase; + + if (off > size) size = off; +#ifdef MRB_STACK_EXTEND_DOUBLING + if (room <= size) + size *= 2; + else + size += room; +#else + /* Use linear stack growth. + It is slightly slower than doubling the stack space, + but it saves memory on small devices. */ + if (room <= MRB_STACK_GROWTH) + size += MRB_STACK_GROWTH; + else + size += room; +#endif + + newstack = (mrb_value *)mrb_realloc(mrb, mrb->c->stbase, sizeof(mrb_value) * size); + if (newstack == NULL) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); + } + stack_clear(&(newstack[oldsize]), size - oldsize); + envadjust(mrb, oldbase, newstack, size); + mrb->c->stbase = newstack; + mrb->c->stack = mrb->c->stbase + off; + mrb->c->stend = mrb->c->stbase + size; + + /* 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) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); + } +} + +static inline void +stack_extend(mrb_state *mrb, int room) +{ + if (mrb->c->stack + room >= mrb->c->stend) { + stack_extend_alloc(mrb, room); + } +} + +static inline struct REnv* +uvenv(mrb_state *mrb, int up) +{ + struct REnv *e = mrb->c->ci->proc->env; + + while (up--) { + if (!e) return NULL; + e = (struct REnv*)e->c; + } + return e; +} + +static inline mrb_bool +is_strict(mrb_state *mrb, struct REnv *e) +{ + ptrdiff_t cioff = e->cioff; + + if (MRB_ENV_STACK_SHARED_P(e) && e->cxt.c->cibase[cioff].proc && + MRB_PROC_STRICT_P(e->cxt.c->cibase[cioff].proc)) { + return TRUE; + } + return FALSE; +} + +static inline struct REnv* +top_env(mrb_state *mrb, 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; + } + return e; +} + +#define CI_ACC_SKIP -1 +#define CI_ACC_DIRECT -2 +#define CI_ACC_RESUMED -3 + +static inline mrb_callinfo* +cipush(mrb_state *mrb) +{ + struct mrb_context *c = mrb->c; + static const mrb_callinfo ci_zero = { 0 }; + mrb_callinfo *ci = c->ci; + + int ridx = ci->ridx; + + if (ci + 1 == c->ciend) { + 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 = ci_zero; + ci->epos = mrb->c->eidx; + ci->ridx = ridx; + + return ci; +} + +MRB_API void +mrb_env_unshare(mrb_state *mrb, struct REnv *e) +{ + if (e == NULL) return; + else { + size_t len = (size_t)MRB_ENV_STACK_LEN(e); + ptrdiff_t cioff = e->cioff; + mrb_value *p; + + if (!MRB_ENV_STACK_SHARED_P(e)) return; + if (e->cxt.c != mrb->c) return; + if (e->cioff == 0 && e->cxt.c == mrb->root_c) return; + MRB_ENV_UNSHARE_STACK(e); + if (!e->c) { + /* save block argument position (negated) */ + e->cioff = -e->cxt.c->cibase[cioff].argc-1; + if (e->cioff == 0) e->cioff = -2; /* blkarg position for vararg (1:args, 2:blk) */ + } + e->cxt.mid = e->cxt.c->cibase[cioff].mid; + p = (mrb_value *)mrb_malloc(mrb, sizeof(mrb_value)*len); + if (len > 0) { + stack_copy(p, e->stack, len); + } + e->stack = p; + mrb_write_barrier(mrb, (struct RBasic *)e); + } +} + +static inline void +cipop(mrb_state *mrb) +{ + struct mrb_context *c = mrb->c; + struct REnv *env = c->ci->env; + + c->ci--; + mrb_env_unshare(mrb, env); +} + +void mrb_exc_set(mrb_state *mrb, mrb_value exc); + +static void +ecall(mrb_state *mrb, int i) +{ + struct RProc *p; + mrb_callinfo *ci = mrb->c->ci; + mrb_value *self = mrb->c->stack; + struct RObject *exc; + ptrdiff_t cioff; + int ai = mrb_gc_arena_save(mrb); + + if (i<0) return; + if (ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); + } + p = mrb->c->ensure[i]; + if (!p) return; + mrb->c->ensure[i] = NULL; + cioff = ci - mrb->c->cibase; + ci = cipush(mrb); + ci->stackent = mrb->c->stack; + ci->mid = ci[-1].mid; + ci->acc = CI_ACC_SKIP; + ci->argc = 0; + ci->proc = p; + ci->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; + if (exc) { + mrb_gc_protect(mrb, mrb_obj_value(exc)); + } + mrb_run(mrb, p, *self); + mrb->c->ci = mrb->c->cibase + cioff; + if (!mrb->exc) mrb->exc = exc; + mrb_gc_arena_restore(mrb, ai); +} + +#ifndef MRB_FUNCALL_ARGC_MAX +#define MRB_FUNCALL_ARGC_MAX 16 +#endif + +MRB_API mrb_value +mrb_funcall(mrb_state *mrb, mrb_value self, const char *name, mrb_int argc, ...) +{ + mrb_value argv[MRB_FUNCALL_ARGC_MAX]; + va_list ap; + mrb_int i; + mrb_sym mid = mrb_intern_cstr(mrb, name); + + 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); +} + +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; + + if (!mrb->jmp) { + struct mrb_jmpbuf c_jmp; + ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase; + + MRB_TRY(&c_jmp) { + mrb->jmp = &c_jmp; + /* recursive call */ + val = mrb_funcall_with_block(mrb, self, mid, argc, argv, blk); + mrb->jmp = 0; + } + MRB_CATCH(&c_jmp) { /* error */ + while (nth_ci < (mrb->c->ci - mrb->c->cibase)) { + mrb->c->stack = mrb->c->ci->stackent; + cipop(mrb); + } + mrb->jmp = 0; + val = mrb_obj_value(mrb->exc); + } + MRB_END_EXC(&c_jmp); + mrb->jmp = 0; + } + else { + struct RProc *p; + struct RClass *c; + mrb_callinfo *ci; + int n; + ptrdiff_t voff = -1; + + if (!mrb->c->stack) { + 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)); + } + c = mrb_class(mrb, self); + p = mrb_method_search_vm(mrb, &c, mid); + if (!p) { + mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); + mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); + p = mrb_method_search_vm(mrb, &c, missing); + if (!p) { + mrb_method_missing(mrb, mid, self, args); + } + mrb_ary_unshift(mrb, args, mrb_symbol_value(mid)); + stack_extend(mrb, n+2); + mrb->c->stack[n+1] = args; + argc = -1; + } + if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); + } + 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->c->stbase <= argv && argv < mrb->c->stend) { + voff = argv - mrb->c->stbase; + } + if (MRB_PROC_CFUNC_P(p)) { + ci->nregs = argc + 2; + stack_extend(mrb, ci->nregs); + } + else if (argc >= CALL_MAXARGS) { + mrb_value args = mrb_ary_new_from_values(mrb, argc, argv); + stack_extend(mrb, ci->nregs); + mrb->c->stack[1] = args; + ci->argc = -1; + argc = 1; + } + else { + if (argc < 0) argc = 1; + ci->nregs = p->body.irep->nregs + argc; + stack_extend(mrb, ci->nregs); + } + if (voff >= 0) { + argv = mrb->c->stbase + voff; + } + mrb->c->stack[0] = self; + if (ci->argc > 0) { + stack_copy(mrb->c->stack+1, argv, argc); + } + mrb->c->stack[argc+1] = blk; + + if (MRB_PROC_CFUNC_P(p)) { + int ai = mrb_gc_arena_save(mrb); + + ci->acc = CI_ACC_DIRECT; + val = p->body.func(mrb, self); + mrb->c->stack = mrb->c->ci->stackent; + cipop(mrb); + mrb_gc_arena_restore(mrb, ai); + } + else { + ci->acc = CI_ACC_SKIP; + val = mrb_run(mrb, p, self); + } + } + mrb_gc_protect(mrb, val); + return val; +} + +MRB_API mrb_value +mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv) +{ + return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value()); +} + +mrb_value +mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) +{ + mrb_callinfo *ci = mrb->c->ci; + + mrb->c->stack[0] = self; + ci->proc = p; + ci->target_class = p->target_class; + if (MRB_PROC_CFUNC_P(p)) { + return p->body.func(mrb, self); + } + ci->nregs = p->body.irep->nregs; + stack_extend(mrb, (ci->argc < 0 && ci->nregs < 3) ? 3 : ci->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; +} + +/* 15.3.1.3.4 */ +/* 15.3.1.3.44 */ +/* + * call-seq: + * obj.send(symbol [, args...]) -> obj + * obj.__send__(symbol [, args...]) -> obj + * + * Invokes the method identified by _symbol_, passing it any + * arguments specified. You can use <code>__send__</code> if the name + * +send+ clashes with an existing method in _obj_. + * + * class Klass + * def hello(*args) + * "Hello " + args.join(' ') + * end + * end + * k = Klass.new + * k.send :hello, "gentle", "readers" #=> "Hello gentle readers" + */ +MRB_API mrb_value +mrb_f_send(mrb_state *mrb, mrb_value self) +{ + mrb_sym name; + mrb_value block, *argv, *regs; + mrb_int argc, i, len; + struct RProc *p; + 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 */ + goto funcall; + } + + ci->mid = name; + ci->target_class = c; + regs = mrb->c->stack+1; + /* remove first symbol from arguments */ + if (ci->argc >= 0) { + for (i=0,len=ci->argc; i<len; i++) { + regs[i] = regs[i+1]; + } + ci->argc--; + } + else { /* variable length arguments */ + mrb_ary_shift(mrb, regs[0]); + } + + return mrb_exec_irep(mrb, self, p); +} + +static mrb_value +eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) +{ + struct RProc *p; + mrb_callinfo *ci; + + if (mrb_nil_p(blk)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); + } + ci = mrb->c->ci; + if (ci->acc == CI_ACC_DIRECT) { + ci->target_class = c; + return mrb_yield_cont(mrb, blk, self, 1, &self); + } + ci->target_class = c; + p = mrb_proc_ptr(blk); + ci->proc = p; + ci->argc = 1; + ci->mid = ci[-1].mid; + if (MRB_PROC_CFUNC_P(p)) { + stack_extend(mrb, 3); + mrb->c->stack[0] = self; + mrb->c->stack[1] = self; + mrb->c->stack[2] = mrb_nil_value(); + return p->body.func(mrb, self); + } + ci->nregs = p->body.irep->nregs; + stack_extend(mrb, (ci->nregs < 3) ? 3 : ci->nregs); + mrb->c->stack[0] = self; + mrb->c->stack[1] = self; + mrb->c->stack[2] = mrb_nil_value(); + 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; +} + +/* 15.2.2.4.35 */ +/* + * call-seq: + * mod.class_eval {| | block } -> obj + * mod.module_eval {| | block } -> obj + * + * Evaluates block in the context of _mod_. This can + * be used to add methods to a class. <code>module_eval</code> returns + * the result of evaluating its argument. + */ +mrb_value +mrb_mod_module_eval(mrb_state *mrb, mrb_value mod) +{ + mrb_value a, b; + + if (mrb_get_args(mrb, "|S&", &a, &b) == 1) { + mrb_raise(mrb, E_NOTIMP_ERROR, "module_eval/class_eval with string not implemented"); + } + return eval_under(mrb, mod, b, mrb_class_ptr(mod)); +} + +/* 15.3.1.3.18 */ +/* + * call-seq: + * obj.instance_eval {| | block } -> obj + * + * Evaluates the given block,within the context of the receiver (_obj_). + * In order to set the context, the variable +self+ is set to _obj_ while + * the code is executing, giving the code access to _obj_'s + * instance variables. In the version of <code>instance_eval</code> + * that takes a +String+, the optional second and third + * parameters supply a filename and starting line number that are used + * when reporting compilation errors. + * + * class KlassWithSecret + * def initialize + * @secret = 99 + * end + * end + * k = KlassWithSecret.new + * k.instance_eval { @secret } #=> 99 + */ +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); +} + +MRB_API mrb_value +mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c) +{ + struct RProc *p; + mrb_sym mid = mrb->c->ci->mid; + mrb_callinfo *ci; + int n = mrb->c->ci->nregs; + mrb_value val; + + if (mrb_nil_p(b)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); + } + if (mrb->c->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; + ci->nregs = MRB_PROC_CFUNC_P(p) ? argc+2 : p->body.irep->nregs; + stack_extend(mrb, ci->nregs); + + mrb->c->stack[0] = self; + if (argc > 0) { + stack_copy(mrb->c->stack+1, argv, argc); + } + mrb->c->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; + } + else { + ptrdiff_t cioff = mrb->c->ci - mrb->c->cibase; + val = mrb_run(mrb, p, self); + mrb->c->ci = mrb->c->cibase + cioff; + } + cipop(mrb); + return val; +} + +MRB_API mrb_value +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); +} + +MRB_API mrb_value +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); +} + +mrb_value +mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv) +{ + struct RProc *p; + mrb_callinfo *ci; + + if (mrb_nil_p(b)) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given"); + } + if (mrb_type(b) != MRB_TT_PROC) { + mrb_raise(mrb, E_TYPE_ERROR, "not a block"); + } + + p = mrb_proc_ptr(b); + ci = mrb->c->ci; + + stack_extend(mrb, 3); + mrb->c->stack[1] = mrb_ary_new_from_values(mrb, argc, argv); + mrb->c->stack[2] = mrb_nil_value(); + ci->argc = -1; + return mrb_exec_irep(mrb, self, p); +} + +mrb_value +mrb_mod_s_nesting(mrb_state *mrb, mrb_value mod) +{ + struct RProc *proc; + mrb_irep *irep; + mrb_value ary; + struct RClass *c; + + mrb_get_args(mrb, ""); + ary = mrb_ary_new(mrb); + proc = mrb->c->ci[-1].proc; /* callee proc */ + c = proc->target_class; + mrb_ary_push(mrb, ary, mrb_obj_value(c)); + mrb_assert(!MRB_PROC_CFUNC_P(proc)); + irep = proc->body.irep; + while (irep) { + if (irep->target_class && irep->target_class != c) { + c = irep->target_class; + mrb_ary_push(mrb, ary, mrb_obj_value(c)); + } + irep = irep->outer; + } + return ary; +} + +static struct RBreak* +break_new(mrb_state *mrb, struct RProc *p, mrb_value val) +{ + struct RBreak *brk; + + brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL); + brk->iv = NULL; + brk->proc = p; + brk->val = val; + + return brk; +} + +typedef enum { + LOCALJUMP_ERROR_RETURN = 0, + LOCALJUMP_ERROR_BREAK = 1, + LOCALJUMP_ERROR_YIELD = 2 +} localjump_error_kind; + +static void +localjump_error(mrb_state *mrb, localjump_error_kind kind) +{ + char kind_str[3][7] = { "return", "break", "yield" }; + char kind_str_len[] = { 6, 5, 5 }; + static const char lead[] = "unexpected "; + mrb_value msg; + mrb_value exc; + + 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_set(mrb, exc); +} + +static void +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->stack[1]; + if (mrb_array_p(args)) { + argc = RARRAY_LEN(args); + } + } + if (mrb->c->ci->mid) { + str = mrb_format(mrb, "'%S': wrong number of arguments (%S for %S)", + mrb_sym2str(mrb, mrb->c->ci->mid), + mrb_fixnum_value(argc), mrb_fixnum_value(num)); + } + else { + str = mrb_format(mrb, "wrong number of arguments (%S for %S)", + mrb_fixnum_value(argc), mrb_fixnum_value(num)); + } + exc = mrb_exc_new_str(mrb, E_ARGUMENT_ERROR, str); + mrb_exc_set(mrb, exc); +} + +void +irep_uplink(mrb_state *mrb, mrb_irep *outer, mrb_irep *irep) +{ + if (irep->outer != outer) { + if (irep->outer) { + mrb_irep_decref(mrb, irep->outer); + } + irep->outer = outer; + mrb_irep_incref(mrb, outer); + } +} + +#define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc; +#define ERR_PC_CLR(mrb) mrb->c->ci->err = 0; +#ifdef MRB_ENABLE_DEBUG_HOOK +#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 + + +#if defined __GNUC__ || defined __clang__ || defined __INTEL_COMPILER +#define DIRECT_THREADED +#endif + +#ifndef DIRECT_THREADED + +#define INIT_DISPATCH for (;;) { i = BYTECODE_DECODER(*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 }} + +#else + +#define INIT_DISPATCH JUMP; return mrb_nil_value(); +#define CASE(op) L_ ## op: +#define NEXT i=BYTECODE_DECODER(*++pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)] +#define JUMP i=BYTECODE_DECODER(*pc); CODE_FETCH_HOOK(mrb, irep, pc, regs); goto *optable[GET_OPCODE(i)] + +#define END_DISPATCH + +#endif + +MRB_API mrb_value +mrb_vm_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) +{ + mrb_irep *irep = proc->body.irep; + mrb_value result; + struct mrb_context *c = mrb->c; + ptrdiff_t cioff = c->ci - c->cibase; + unsigned int nregs = irep->nregs; + + if (!c->stack) { + stack_init(mrb); + } + if (stack_keep > nregs) + nregs = stack_keep; + stack_extend(mrb, nregs); + stack_clear(c->stack + stack_keep, nregs - stack_keep); + c->stack[0] = self; + result = mrb_vm_exec(mrb, proc, irep->iseq); + if (c->ci - c->cibase > cioff) { + c->ci = c->cibase + cioff; + } + if (mrb->c != c) { + if (mrb->c->fib) { + mrb_write_barrier(mrb, (struct RBasic*)mrb->c->fib); + } + mrb->c = c; + } + return result; +} + +MRB_API mrb_value +mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc) +{ + /* mrb_assert(mrb_proc_cfunc_p(proc)) */ + mrb_irep *irep = proc->body.irep; + mrb_value *pool = irep->pool; + mrb_sym *syms = irep->syms; + mrb_code i; + int ai = mrb_gc_arena_save(mrb); + struct mrb_jmpbuf *prev_jmp = mrb->jmp; + struct mrb_jmpbuf c_jmp; + +#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, + }; +#endif + + mrb_bool exc_catched = FALSE; +RETRY_TRY_BLOCK: + + MRB_TRY(&c_jmp) { + + if (exc_catched) { + exc_catched = FALSE; + if (mrb->exc && mrb->exc->tt == MRB_TT_BREAK) + goto L_BREAK; + goto L_RAISE; + } + mrb->jmp = &c_jmp; + mrb->c->ci->proc = proc; + mrb->c->ci->nregs = irep->nregs; + +#define regs (mrb->c->stack) + INIT_DISPATCH { + CASE(OP_NOP) { + /* do nothing */ + NEXT; + } + + CASE(OP_MOVE) { + /* A B R(A) := R(B) */ + int a = GETARG_A(i); + int b = GETARG_B(i); + regs[a] = regs[b]; + NEXT; + } + + CASE(OP_LOADL) { + /* A Bx R(A) := Pool(Bx) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); +#ifdef MRB_WORD_BOXING + mrb_value val = pool[bx]; + if (mrb_float_p(val)) { + val = mrb_float_value(mrb, mrb_float(val)); + } + regs[a] = val; +#else + regs[a] = pool[bx]; +#endif + NEXT; + } + + CASE(OP_LOADI) { + /* A sBx R(A) := sBx */ + SET_INT_VALUE(regs[GETARG_A(i)], GETARG_sBx(i)); + NEXT; + } + + CASE(OP_LOADSYM) { + /* A Bx R(A) := Syms(Bx) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + SET_SYM_VALUE(regs[a], syms[bx]); + NEXT; + } + + CASE(OP_LOADSELF) { + /* A R(A) := self */ + int a = GETARG_A(i); + regs[a] = regs[0]; + NEXT; + } + + CASE(OP_LOADT) { + /* A R(A) := true */ + int a = GETARG_A(i); + SET_TRUE_VALUE(regs[a]); + NEXT; + } + + CASE(OP_LOADF) { + /* A R(A) := false */ + int a = GETARG_A(i); + SET_FALSE_VALUE(regs[a]); + NEXT; + } + + CASE(OP_GETGLOBAL) { + /* A Bx R(A) := getglobal(Syms(Bx)) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_value val = mrb_gv_get(mrb, syms[bx]); + regs[a] = val; + NEXT; + } + + CASE(OP_SETGLOBAL) { + /* A Bx setglobal(Syms(Bx), R(A)) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_gv_set(mrb, syms[bx], regs[a]); + NEXT; + } + + CASE(OP_GETSPECIAL) { + /* A Bx R(A) := Special[Bx] */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_value val = mrb_vm_special_get(mrb, bx); + regs[a] = val; + NEXT; + } + + CASE(OP_SETSPECIAL) { + /* A Bx Special[Bx] := R(A) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_vm_special_set(mrb, bx, regs[a]); + NEXT; + } + + CASE(OP_GETIV) { + /* A Bx R(A) := ivget(Bx) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_value val = mrb_vm_iv_get(mrb, syms[bx]); + regs[a] = val; + NEXT; + } + + CASE(OP_SETIV) { + /* A Bx ivset(Syms(Bx),R(A)) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_vm_iv_set(mrb, syms[bx], regs[a]); + NEXT; + } + + CASE(OP_GETCV) { + /* A Bx R(A) := cvget(Syms(Bx)) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_value val; + ERR_PC_SET(mrb, pc); + val = mrb_vm_cv_get(mrb, syms[bx]); + ERR_PC_CLR(mrb); + regs[a] = val; + NEXT; + } + + CASE(OP_SETCV) { + /* A Bx cvset(Syms(Bx),R(A)) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_vm_cv_set(mrb, syms[bx], regs[a]); + NEXT; + } + + CASE(OP_GETCONST) { + /* A Bx R(A) := constget(Syms(Bx)) */ + mrb_value val; + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_sym sym = syms[bx]; + + ERR_PC_SET(mrb, pc); + val = mrb_vm_const_get(mrb, sym); + ERR_PC_CLR(mrb); + regs[a] = val; + NEXT; + } + + CASE(OP_SETCONST) { + /* A Bx constset(Syms(Bx),R(A)) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_vm_const_set(mrb, syms[bx], regs[a]); + NEXT; + } + + CASE(OP_GETMCNST) { + /* A Bx R(A) := R(A)::Syms(Bx) */ + mrb_value val; + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + + ERR_PC_SET(mrb, pc); + val = mrb_const_get(mrb, regs[a], syms[bx]); + ERR_PC_CLR(mrb); + regs[a] = val; + NEXT; + } + + CASE(OP_SETMCNST) { + /* A Bx R(A+1)::Syms(Bx) := R(A) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_const_set(mrb, regs[a+1], syms[bx], regs[a]); + NEXT; + } + + CASE(OP_GETUPVAR) { + /* A B C R(A) := uvget(B,C) */ + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + mrb_value *regs_a = regs + a; + struct REnv *e = uvenv(mrb, c); + + if (!e) { + *regs_a = mrb_nil_value(); + } + else { + *regs_a = e->stack[b]; + } + NEXT; + } + + CASE(OP_SETUPVAR) { + /* A B C uvset(B,C,R(A)) */ + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + + struct REnv *e = uvenv(mrb, c); + + if (e) { + mrb_value *regs_a = regs + a; + + if (b < MRB_ENV_STACK_LEN(e)) { + e->stack[b] = *regs_a; + mrb_write_barrier(mrb, (struct RBasic*)e); + } + } + NEXT; + } + + CASE(OP_JMP) { + /* sBx pc+=sBx */ + int sbx = GETARG_sBx(i); + pc += sbx; + JUMP; + } + + CASE(OP_JMPIF) { + /* A sBx if R(A) pc+=sBx */ + int a = GETARG_A(i); + int sbx = GETARG_sBx(i); + if (mrb_test(regs[a])) { + pc += sbx; + JUMP; + } + NEXT; + } + + CASE(OP_JMPNOT) { + /* A sBx if !R(A) pc+=sBx */ + int a = GETARG_A(i); + int sbx = GETARG_sBx(i); + if (!mrb_test(regs[a])) { + pc += sbx; + JUMP; + } + NEXT; + } + + CASE(OP_ONERR) { + /* sBx pc+=sBx on exception */ + int sbx = GETARG_sBx(i); + if (mrb->c->rsize <= mrb->c->ci->ridx) { + if (mrb->c->rsize == 0) mrb->c->rsize = RESCUE_STACK_INIT_SIZE; + else mrb->c->rsize *= 2; + mrb->c->rescue = (mrb_code **)mrb_realloc(mrb, mrb->c->rescue, sizeof(mrb_code*) * mrb->c->rsize); + } + mrb->c->rescue[mrb->c->ci->ridx++] = pc + sbx; + NEXT; + } + + CASE(OP_RESCUE) { + /* A B R(A) := exc; clear(exc); R(B) := matched (bool) */ + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + mrb_value exc; + + if (c == 0) { + exc = mrb_obj_value(mrb->exc); + mrb->exc = 0; + } + else { /* continued; exc taken from R(A) */ + exc = regs[a]; + } + if (b != 0) { + mrb_value e = regs[b]; + struct RClass *ec; + + switch (mrb_type(e)) { + case MRB_TT_CLASS: + case MRB_TT_MODULE: + break; + default: + { + mrb_value exc; + + exc = mrb_exc_new_str_lit(mrb, E_TYPE_ERROR, + "class or module required for rescue clause"); + mrb_exc_set(mrb, exc); + goto L_RAISE; + } + } + ec = mrb_class_ptr(e); + regs[b] = mrb_bool_value(mrb_obj_is_kind_of(mrb, exc, ec)); + } + if (a != 0 && c == 0) { + regs[a] = exc; + } + NEXT; + } + + CASE(OP_POPERR) { + /* A A.times{rescue_pop()} */ + int a = GETARG_A(i); + + mrb->c->ci->ridx -= a; + NEXT; + } + + CASE(OP_RAISE) { + /* A raise(R(A)) */ + int a = GETARG_A(i); + + mrb_exc_set(mrb, regs[a]); + goto L_RAISE; + } + + CASE(OP_EPUSH) { + /* Bx ensure_push(SEQ[Bx]) */ + int bx = GETARG_Bx(i); + struct RProc *p; + + p = mrb_closure_new(mrb, irep->reps[bx]); + /* push ensure_stack */ + if (mrb->c->esize <= mrb->c->eidx+1) { + if (mrb->c->esize == 0) mrb->c->esize = ENSURE_STACK_INIT_SIZE; + else mrb->c->esize *= 2; + mrb->c->ensure = (struct RProc **)mrb_realloc(mrb, mrb->c->ensure, sizeof(struct RProc*) * mrb->c->esize); + } + mrb->c->ensure[mrb->c->eidx++] = p; + mrb->c->ensure[mrb->c->eidx] = NULL; + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_EPOP) { + /* A A.times{ensure_pop().call} */ + int a = GETARG_A(i); + mrb_callinfo *ci = mrb->c->ci; + int n, epos = ci->epos; + + for (n=0; n<a && mrb->c->eidx > epos; n++) { + ecall(mrb, --mrb->c->eidx); + mrb_gc_arena_restore(mrb, ai); + } + NEXT; + } + + CASE(OP_LOADNIL) { + /* A R(A) := nil */ + int a = GETARG_A(i); + + SET_NIL_VALUE(regs[a]); + NEXT; + } + + 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 */ + }; + + 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); + int argc = (n == CALL_MAXARGS) ? -1 : n; + int bidx = (argc < 0) ? a+2 : a+n+1; + struct RProc *m; + struct RClass *c; + mrb_callinfo *ci = mrb->c->ci; + mrb_value recv, blk; + mrb_sym mid = syms[GETARG_B(i)]; + + mrb_assert(bidx < ci->nregs); + + recv = regs[a]; + if (GET_OPCODE(i) != OP_SENDB) { + SET_NIL_VALUE(regs[bidx]); + blk = regs[bidx]; + } + else { + blk = regs[bidx]; + if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { + blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); + /* The stack might have been reallocated during mrb_convert_type(), + see #3622 */ + regs[bidx] = blk; + } + } + c = mrb_class(mrb, recv); + m = mrb_method_search_vm(mrb, &c, mid); + if (!m) { + mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); + m = mrb_method_search_vm(mrb, &c, missing); + if (!m) { + mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, n, regs+a+1); + ERR_PC_SET(mrb, pc); + mrb_method_missing(mrb, mid, recv, args); + } + if (argc >= 0) { + if (a+2 >= irep->nregs) { + stack_extend(mrb, a+3); + } + regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1); + regs[a+2] = blk; + 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; + ci->target_class = c; + ci->argc = argc; + + ci->pc = pc + 1; + ci->acc = a; + + /* prepare stack */ + mrb->c->stack += a; + + if (MRB_PROC_CFUNC_P(m)) { + ci->nregs = (argc < 0) ? 3 : n+2; + recv = m->body.func(mrb, recv); + mrb_gc_arena_restore(mrb, ai); + mrb_gc_arena_shrink(mrb, ai); + if (mrb->exc) goto L_RAISE; + ci = mrb->c->ci; + if (GET_OPCODE(i) == OP_SENDB) { + if (mrb_type(blk) == MRB_TT_PROC) { + struct RProc *p = mrb_proc_ptr(blk); + if (p && !MRB_PROC_STRICT_P(p) && p->env == ci[-1].env) { + p->flags |= MRB_PROC_ORPHAN; + } + } + } + if (!ci->target_class) { /* return from context modifying method (resume/yield) */ + if (ci->acc == CI_ACC_RESUMED) { + mrb->jmp = prev_jmp; + 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; + } + } + mrb->c->stack[0] = recv; + /* pop stackpos */ + mrb->c->stack = ci->stackent; + pc = ci->pc; + cipop(mrb); + JUMP; + } + else { + /* setup environment for calling method */ + proc = mrb->c->ci->proc = m; + irep = m->body.irep; + pool = irep->pool; + syms = irep->syms; + ci->nregs = irep->nregs; + stack_extend(mrb, (argc < 0 && ci->nregs < 3) ? 3 : ci->nregs); + pc = irep->iseq; + JUMP; + } + } + + CASE(OP_FSEND) { + /* A B C R(A) := fcall(R(A),Syms(B),R(A+1),... ,R(A+C-1)) */ + /* not implemented yet */ + NEXT; + } + + CASE(OP_CALL) { + /* A R(A) := self.call(frame.argc, frame.argv) */ + mrb_callinfo *ci; + mrb_value recv = mrb->c->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) { + mrb_sym mid; + + if (MRB_ENV_STACK_SHARED_P(m->env)) { + mid = m->env->cxt.c->cibase[m->env->cioff].mid; + } + else { + mid = m->env->cxt.mid; + } + if (mid) ci->mid = mid; + if (!m->env->stack) { + m->env->stack = mrb->c->stack; + } + } + + /* prepare stack */ + if (MRB_PROC_CFUNC_P(m)) { + recv = m->body.func(mrb, recv); + mrb_gc_arena_restore(mrb, ai); + mrb_gc_arena_shrink(mrb, ai); + if (mrb->exc) goto L_RAISE; + /* pop stackpos */ + ci = mrb->c->ci; + mrb->c->stack = ci->stackent; + regs[ci->acc] = recv; + pc = ci->pc; + cipop(mrb); + irep = mrb->c->ci->proc->body.irep; + pool = irep->pool; + syms = irep->syms; + JUMP; + } + else { + /* setup environment for calling method */ + proc = m; + irep = m->body.irep; + if (!irep) { + mrb->c->stack[0] = mrb_nil_value(); + goto L_RETURN; + } + pool = irep->pool; + syms = irep->syms; + ci->nregs = irep->nregs; + stack_extend(mrb, ci->nregs); + if (ci->argc < 0) { + if (irep->nregs > 3) { + stack_clear(regs+3, irep->nregs-3); + } + } + else if (ci->argc+2 < irep->nregs) { + stack_clear(regs+ci->argc+2, irep->nregs-ci->argc-2); + } + if (m->env) { + regs[0] = m->env->stack[0]; + } + pc = irep->iseq; + JUMP; + } + } + + CASE(OP_SUPER) { + /* A C R(A) := super(R(A+1),... ,R(A+C+1)) */ + int a = GETARG_A(i); + int n = GETARG_C(i); + int argc = (n == CALL_MAXARGS) ? -1 : n; + int bidx = (argc < 0) ? a+2 : a+n+1; + struct RProc *m; + struct RClass *c; + mrb_callinfo *ci = mrb->c->ci; + mrb_value recv, blk; + mrb_sym mid = ci->mid; + + mrb_assert(bidx < ci->nregs); + + if (mid == 0 || !ci->target_class) { + mrb_value exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); + mrb_exc_set(mrb, exc); + goto L_RAISE; + } + recv = regs[0]; + blk = regs[bidx]; + if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { + blk = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); + /* The stack or ci stack might have been reallocated during + mrb_convert_type(), see #3622 and #3784 */ + regs[bidx] = blk; + ci = mrb->c->ci; + } + c = ci->target_class->super; + m = mrb_method_search_vm(mrb, &c, mid); + if (!m) { + mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); + m = mrb_method_search_vm(mrb, &c, missing); + if (!m) { + mrb_value args = (argc < 0) ? regs[a+1] : mrb_ary_new_from_values(mrb, n, regs+a+1); + ERR_PC_SET(mrb, pc); + mrb_method_missing(mrb, mid, recv, args); + } + mid = missing; + if (argc >= 0) { + if (a+2 >= ci->nregs) { + stack_extend(mrb, a+3); + } + regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1); + regs[a+2] = blk; + argc = -1; + } + mrb_ary_unshift(mrb, regs[a+1], mrb_symbol_value(ci->mid)); + } + + /* push callinfo */ + ci = cipush(mrb); + ci->mid = mid; + ci->proc = m; + ci->stackent = mrb->c->stack; + ci->target_class = c; + ci->pc = pc + 1; + ci->argc = argc; + + /* prepare stack */ + mrb->c->stack += a; + mrb->c->stack[0] = recv; + + if (MRB_PROC_CFUNC_P(m)) { + mrb_value v; + ci->nregs = (argc < 0) ? 3 : n+2; + v = m->body.func(mrb, recv); + mrb_gc_arena_restore(mrb, ai); + if (mrb->exc) goto L_RAISE; + ci = mrb->c->ci; + if (!ci->target_class) { /* return from context modifying method (resume/yield) */ + if (ci->acc == CI_ACC_RESUMED) { + mrb->jmp = prev_jmp; + return v; + } + else { + mrb_assert(!MRB_PROC_CFUNC_P(ci[-1].proc)); + proc = ci[-1].proc; + irep = proc->body.irep; + pool = irep->pool; + syms = irep->syms; + } + } + mrb->c->stack[0] = v; + /* pop stackpos */ + mrb->c->stack = ci->stackent; + pc = ci->pc; + cipop(mrb); + JUMP; + } + else { + /* fill callinfo */ + ci->acc = a; + + /* setup environment for calling method */ + ci->proc = m; + irep = m->body.irep; + pool = irep->pool; + syms = irep->syms; + ci->nregs = irep->nregs; + stack_extend(mrb, (argc < 0 && ci->nregs < 3) ? 3 : ci->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; + mrb_value *stack; + + if (mrb->c->ci->mid == 0 || mrb->c->ci->target_class == NULL) { + mrb_value exc; + + L_NOSUPER: + exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); + mrb_exc_set(mrb, exc); + goto L_RAISE; + } + if (lv == 0) stack = regs + 1; + else { + struct REnv *e = uvenv(mrb, lv-1); + if (!e) goto L_NOSUPER; + if (MRB_ENV_STACK_LEN(e) <= m1+r+m2+1) + goto L_NOSUPER; + stack = e->stack + 1; + } + if (r == 0) { + regs[a] = mrb_ary_new_from_values(mrb, m1+m2, stack); + } + else { + mrb_value *pp = NULL; + struct RArray *rest; + int len = 0; + + if (mrb_array_p(stack[m1])) { + struct RArray *ary = mrb_ary_ptr(stack[m1]); + + pp = ARY_PTR(ary); + len = ARY_LEN(ary); + } + regs[a] = mrb_ary_new_capa(mrb, m1+len+m2); + rest = mrb_ary_ptr(regs[a]); + if (m1 > 0) { + stack_copy(ARY_PTR(rest), stack, m1); + } + if (len > 0) { + stack_copy(ARY_PTR(rest)+m1, pp, len); + } + if (m2 > 0) { + stack_copy(ARY_PTR(rest)+m1+len, stack+m1+1, m2); + } + ARY_SET_LEN(rest, m1+len+m2); + } + regs[a+1] = stack[m1+r+m2]; + 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); + /* unused + int k = MRB_ASPEC_KEY(ax); + int kd = MRB_ASPEC_KDICT(ax); + int b = MRB_ASPEC_BLOCK(ax); + */ + int argc = mrb->c->ci->argc; + mrb_value *argv = regs+1; + mrb_value *argv0 = argv; + int len = m1 + o + r + m2; + mrb_value *blk = &argv[argc < 0 ? 1 : argc]; + + if (argc < 0) { + struct RArray *ary = mrb_ary_ptr(regs[1]); + argv = ARY_PTR(ary); + argc = ARY_LEN(ary); + mrb_gc_protect(mrb, regs[1]); + } + if (mrb->c->ci->proc && MRB_PROC_STRICT_P(mrb->c->ci->proc)) { + if (argc >= 0) { + if (argc < m1 + m2 || (r == 0 && argc > len)) { + argnum_error(mrb, m1+m2); + goto L_RAISE; + } + } + } + else if (len > 1 && argc == 1 && mrb_array_p(argv[0])) { + mrb_gc_protect(mrb, argv[0]); + argc = RARRAY_LEN(argv[0]); + argv = RARRAY_PTR(argv[0]); + } + if (argc < len) { + int mlen = m2; + if (argc < m1+m2) { + if (m1 < argc) + mlen = argc - m1; + else + mlen = 0; + } + regs[len+1] = *blk; /* move block */ + SET_NIL_VALUE(regs[argc+1]); + if (argv0 != argv) { + value_move(®s[1], argv, argc-mlen); /* m1 + o */ + } + if (argc < m1) { + stack_clear(®s[argc+1], m1-argc); + } + if (mlen) { + value_move(®s[len-m2+1], &argv[argc-mlen], mlen); + } + if (mlen < m2) { + stack_clear(®s[len-m2+mlen+1], m2-mlen); + } + if (r) { + regs[m1+o+1] = mrb_ary_new_capa(mrb, 0); + } + if (o == 0 || argc < m1+m2) pc++; + else + pc += argc - m1 - m2 + 1; + } + else { + int rnum = 0; + if (argv0 != argv) { + regs[len+1] = *blk; /* move block */ + 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); + } + if (m2) { + if (argc-m2 > m1) { + value_move(®s[m1+o+r+1], &argv[m1+o+rnum], m2); + } + } + if (argv0 == argv) { + regs[len+1] = *blk; /* move block */ + } + pc += o + 1; + } + mrb->c->ci->argc = len; + /* clear local (but non-argument) variables */ + if (irep->nlocals-len-2 > 0) { + stack_clear(®s[len+2], irep->nlocals-len-2); + } + JUMP; + } + + 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 */ + NEXT; + } + + CASE(OP_KDICT) { + /* A C R(A) := kdict */ + NEXT; + } + + 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) */ + mrb_callinfo *ci; + + ci = mrb->c->ci; + if (ci->mid) { + mrb_value blk; + + if (ci->argc < 0) { + blk = regs[2]; + } + else { + blk = regs[ci->argc+1]; + } + if (mrb_type(blk) == MRB_TT_PROC) { + struct RProc *p = mrb_proc_ptr(blk); + + if (!MRB_PROC_STRICT_P(p) && + ci > mrb->c->cibase && p->env == ci[-1].env) { + p->flags |= MRB_PROC_ORPHAN; + } + } + } + + if (mrb->exc) { + mrb_callinfo *ci0; + mrb_value *stk; + + L_RAISE: + ci0 = ci = mrb->c->ci; + if (ci == mrb->c->cibase) { + if (ci->ridx == 0) goto L_FTOP; + goto L_RESCUE; + } + stk = mrb->c->stack; + while (ci[0].ridx == ci[-1].ridx) { + cipop(mrb); + mrb->c->stack = ci->stackent; + if (ci->acc == CI_ACC_SKIP && prev_jmp) { + mrb->jmp = prev_jmp; + MRB_THROW(prev_jmp); + } + ci = mrb->c->ci; + if (ci == mrb->c->cibase) { + mrb->c->stack = stk; + if (ci->ridx == 0) { + L_FTOP: /* fiber top */ + if (mrb->c == mrb->root_c) { + mrb->c->stack = mrb->c->stbase; + goto L_STOP; + } + else { + struct mrb_context *c = mrb->c; + + if (c->fib) { + mrb_write_barrier(mrb, (struct RBasic*)c->fib); + } + mrb->c = c->prev; + c->prev = NULL; + goto L_RAISE; + } + } + break; + } + /* call ensure only when we skip this callinfo */ + if (ci[0].ridx == ci[-1].ridx) { + mrb_value *org_stbase = mrb->c->stbase; + while (mrb->c->eidx > ci->epos) { + ecall(mrb, --mrb->c->eidx); + ci = mrb->c->ci; + if (org_stbase != mrb->c->stbase) { + stk = mrb->c->stack; + } + } + } + } + L_RESCUE: + if (ci->ridx == 0) goto L_STOP; + proc = ci->proc; + irep = proc->body.irep; + pool = irep->pool; + syms = irep->syms; + if (ci != ci0) { + mrb->c->stack = ci[1].stackent; + } + stack_extend(mrb, irep->nregs); + pc = mrb->c->rescue[--ci->ridx]; + } + else { + int acc; + mrb_value v; + + v = regs[GETARG_A(i)]; + mrb_gc_protect(mrb, v); + switch (GETARG_B(i)) { + case OP_R_RETURN: + /* Fall through to OP_R_NORMAL otherwise */ + if (ci->acc >=0 && proc->env && !MRB_PROC_STRICT_P(proc)) { + struct REnv *e = top_env(mrb, proc); + mrb_callinfo *ce; + + if (!MRB_ENV_STACK_SHARED_P(e) || e->cxt.c != mrb->c) { + localjump_error(mrb, LOCALJUMP_ERROR_RETURN); + goto L_RAISE; + } + + ce = mrb->c->cibase + e->cioff; + while (ci > ce) { + mrb_env_unshare(mrb, ci->env); + if (ci->acc < 0) { + localjump_error(mrb, LOCALJUMP_ERROR_RETURN); + goto L_RAISE; + } + ci--; + } + mrb_env_unshare(mrb, ci->env); + if (ce == mrb->c->cibase) { + localjump_error(mrb, LOCALJUMP_ERROR_RETURN); + goto L_RAISE; + } + mrb->c->stack = mrb->c->ci->stackent; + mrb->c->ci = ce; + break; + } + 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; + } + 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_set(mrb, exc); + goto L_RAISE; + } + while (mrb->c->eidx > 0) { + ecall(mrb, --mrb->c->eidx); + } + /* automatic yield at the end */ + mrb->c->status = MRB_FIBER_TERMINATED; + mrb->c = mrb->c->prev; + mrb->c->status = MRB_FIBER_RUNNING; + } + ci = mrb->c->ci; + break; + case OP_R_BREAK: + if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN; + if (MRB_PROC_ORPHAN_P(proc)) { + mrb_value exc; + + L_BREAK_ERROR: + exc = mrb_exc_new_str_lit(mrb, E_LOCALJUMP_ERROR, + "break from proc-closure"); + mrb_exc_set(mrb, exc); + goto L_RAISE; + } + if (!proc->env || !MRB_ENV_STACK_SHARED_P(proc->env)) { + goto L_BREAK_ERROR; + } + if (proc->env->cxt.c != mrb->c) { + goto L_BREAK_ERROR; + } + while (mrb->c->eidx > mrb->c->ci->epos) { + ecall(mrb, --mrb->c->eidx); + } + /* break from fiber block */ + if (mrb->c->ci == mrb->c->cibase && mrb->c->ci->pc) { + struct mrb_context *c = mrb->c; + + mrb->c = c->prev; + c->prev = NULL; + } + ci = mrb->c->ci; + if (ci->acc < 0) { + mrb_gc_arena_restore(mrb, ai); + mrb->c->vmexec = FALSE; + mrb->exc = (struct RObject*)break_new(mrb, proc, v); + mrb->jmp = prev_jmp; + MRB_THROW(prev_jmp); + } + if (FALSE) { + L_BREAK: + v = ((struct RBreak*)mrb->exc)->val; + proc = ((struct RBreak*)mrb->exc)->proc; + mrb->exc = NULL; + ci = mrb->c->ci; + } + mrb->c->stack = ci->stackent; + mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1; + while (ci > mrb->c->ci) { + mrb_env_unshare(mrb, ci->env); + if (ci[-1].acc == CI_ACC_SKIP) { + mrb->c->ci = ci; + goto L_BREAK_ERROR; + } + ci--; + } + mrb_env_unshare(mrb, ci->env); + break; + default: + /* cannot happen */ + break; + } + while (mrb->c->eidx > mrb->c->ci->epos) { + ecall(mrb, --mrb->c->eidx); + } + if (mrb->c->vmexec && !mrb->c->ci->target_class) { + mrb_gc_arena_restore(mrb, ai); + mrb->c->vmexec = FALSE; + mrb->jmp = prev_jmp; + return v; + } + ci = mrb->c->ci; + acc = ci->acc; + mrb->c->stack = ci->stackent; + cipop(mrb); + if (acc == CI_ACC_SKIP || acc == CI_ACC_DIRECT) { + mrb_gc_arena_restore(mrb, ai); + mrb->jmp = prev_jmp; + return v; + } + pc = ci->pc; + DEBUG(fprintf(stderr, "from :%s\n", mrb_sym2name(mrb, ci->mid))); + proc = mrb->c->ci->proc; + irep = proc->body.irep; + pool = irep->pool; + syms = irep->syms; + + regs[acc] = v; + mrb_gc_arena_restore(mrb, ai); + } + JUMP; + } + + 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); + mrb_sym missing = mrb_intern_lit(mrb, "method_missing"); + m = mrb_method_search_vm(mrb, &c, missing); + if (!m) { + mrb_value args; + + if (n == CALL_MAXARGS) { + args = regs[a+1]; + } + else { + args = mrb_ary_new_from_values(mrb, n, regs+a+1); + } + ERR_PC_SET(mrb, pc); + mrb_method_missing(mrb, mid, recv, args); + } + mid = missing; + if (n == CALL_MAXARGS) { + mrb_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_value v = m->body.func(mrb, recv); + mrb->c->stack[0] = v; + 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); + } + else { + stack_extend(mrb, irep->nregs); + } + 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; + mrb_value *stack; + + if (lv == 0) stack = regs + 1; + else { + struct REnv *e = uvenv(mrb, lv-1); + if (!e || e->cioff == 0 || + (!MRB_ENV_STACK_SHARED_P(e) && e->cxt.mid == 0) || + MRB_ENV_STACK_LEN(e) <= m1+r+m2+1) { + localjump_error(mrb, LOCALJUMP_ERROR_YIELD); + goto L_RAISE; + } + stack = e->stack + 1; + } + if (mrb_nil_p(stack[m1+r+m2])) { + localjump_error(mrb, LOCALJUMP_ERROR_YIELD); + goto L_RAISE; + } + 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; + } + mrb_gc_arena_restore(mrb, ai); + 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; + + 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); + } +#else + OP_MATH_BODY(-,mrb_float,mrb_float); +#endif + break; + default: + goto L_SEND; + } + NEXT; + } + + 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_int x, y, z; + + x = mrb_fixnum(regs[a]); + y = mrb_fixnum(regs[a+1]); + if (mrb_int_mul_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); + } +#else + OP_MATH_BODY(*,mrb_float,mrb_float); +#endif + break; + default: + goto L_SEND; + } + NEXT; + } + + CASE(OP_DIV) { + /* 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 = mrb_fixnum(regs[a]); + mrb_int y = mrb_fixnum(regs[a+1]); + double f; + if (y == 0) { + if (x > 0) f = INFINITY; + else if (x < 0) f = -INFINITY; + else /* if (x == 0) */ f = NAN; + } + else { + f = (mrb_float)x / (mrb_float)y; + } + SET_FLOAT_VALUE(mrb, regs[a], f); + } + 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]); + double f; + if (y == 0) { + f = INFINITY; + } + else { + f = x / y; + } + SET_FLOAT_VALUE(mrb, regs[a], f); + } +#else + OP_MATH_BODY(/,mrb_float,mrb_fixnum); +#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; + } +#ifdef MRB_NAN_BOXING + if (isnan(mrb_float(regs[a]))) { + mrb_value v = mrb_float_value(mrb, mrb_float(regs[a])); + regs[a] = v; + } +#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; + } + SET_INT_VALUE(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)); + } +#else + mrb_float(regs[a]) += 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) { + /* 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; + + if (mrb_int_sub_overflow(x, y, &z)) { + SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y); + } + else { + SET_INT_VALUE(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; + } + +#define OP_CMP_BODY(op,v1,v2) (v1(regs[a]) op v2(regs[a+1])) + +#define OP_CMP(op) 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):\ + result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\ + break;\ + case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\ + result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\ + break;\ + case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\ + 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;\ + }\ + if (result) {\ + SET_TRUE_VALUE(regs[a]);\ + }\ + else {\ + SET_FALSE_VALUE(regs[a]);\ + }\ +} while(0) + + CASE(OP_EQ) { + /* A B C R(A) := R(A)==R(A+1) (Syms[B]=:==,C=1)*/ + int a = GETARG_A(i); + if (mrb_obj_eq(mrb, regs[a], regs[a+1])) { + SET_TRUE_VALUE(regs[a]); + } + else { + OP_CMP(==); + } + 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(<); + 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(<=); + 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(>); + 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(>=); + NEXT; + } + + CASE(OP_ARRAY) { + /* A B C R(A) := ary_new(R(B),R(B+1)..R(B+C)) */ + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + mrb_value v = mrb_ary_new_from_values(mrb, c, ®s[b]); + regs[a] = v; + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_ARYCAT) { + /* A B mrb_ary_concat(R(A),R(B)) */ + int a = GETARG_A(i); + int b = GETARG_B(i); + mrb_value splat = mrb_ary_splat(mrb, regs[b]); + mrb_ary_concat(mrb, regs[a], splat); + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_ARYPUSH) { + /* A B R(A).push(R(B)) */ + int a = GETARG_A(i); + int b = GETARG_B(i); + mrb_ary_push(mrb, regs[a], regs[b]); + NEXT; + } + + CASE(OP_AREF) { + /* A B C R(A) := R(B)[C] */ + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + mrb_value v = regs[b]; + + if (!mrb_array_p(v)) { + if (c == 0) { + regs[a] = v; + } + else { + SET_NIL_VALUE(regs[a]); + } + } + else { + v = mrb_ary_ref(mrb, v, c); + regs[a] = v; + } + NEXT; + } + + CASE(OP_ASET) { + /* A B C R(B)[C] := R(A) */ + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + mrb_ary_set(mrb, regs[b], c, regs[a]); + NEXT; + } + + CASE(OP_APOST) { + /* A B C *R(A),R(A+1)..R(A+C) := R(A) */ + int a = GETARG_A(i); + mrb_value v = regs[a]; + int pre = GETARG_B(i); + int post = GETARG_C(i); + struct RArray *ary; + int len, idx; + + if (!mrb_array_p(v)) { + v = mrb_ary_new_from_values(mrb, 1, ®s[a]); + } + ary = mrb_ary_ptr(v); + len = 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--) { + regs[a++] = ARY_PTR(ary)[len-post-1]; + } + } + else { + 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]; + } + while (idx < post) { + SET_NIL_VALUE(regs[a+idx]); + idx++; + } + } + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_STRING) { + /* A Bx R(A) := str_new(Lit(Bx)) */ + mrb_value str = mrb_str_dup(mrb, pool[GETARG_Bx(i)]); + regs[GETARG_A(i)] = str; + 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)]); + 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); + + while (b < lim) { + mrb_hash_set(mrb, hash, regs[b], regs[b+1]); + b+=2; + } + regs[GETARG_A(i)] = hash; + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_LAMBDA) { + /* A b c R(A) := lambda(SEQ[b],c) (b:c = 14:2) */ + struct RProc *p; + int a = GETARG_A(i); + int b = GETARG_b(i); + int c = GETARG_c(i); + mrb_irep *nirep = irep->reps[b]; + + irep_uplink(mrb, irep, nirep); + if (c & OP_L_CAPTURE) { + p = mrb_closure_new(mrb, nirep); + } + else { + p = mrb_proc_new(mrb, nirep); + } + if (c & OP_L_STRICT) p->flags |= MRB_PROC_STRICT; + regs[a] = mrb_obj_value(p); + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_OCLASS) { + /* A R(A) := ::Object */ + regs[GETARG_A(i)] = mrb_obj_value(mrb->object_class); + NEXT; + } + + CASE(OP_CLASS) { + /* A B R(A) := newclass(R(A),Syms(B),R(A+1)) */ + struct RClass *c = 0, *baseclass; + int a = GETARG_A(i); + mrb_value base, super; + mrb_sym id = syms[GETARG_B(i)]; + + base = regs[a]; + super = regs[a+1]; + if (mrb_nil_p(base)) { + baseclass = mrb->c->ci->proc->target_class; + if (!baseclass) baseclass = mrb->c->ci->target_class; + + base = mrb_obj_value(baseclass); + } + c = mrb_vm_define_class(mrb, base, super, id); + regs[a] = mrb_obj_value(c); + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_MODULE) { + /* A B R(A) := newmodule(R(A),Syms(B)) */ + struct RClass *c = 0, *baseclass; + int a = GETARG_A(i); + mrb_value base; + mrb_sym id = syms[GETARG_B(i)]; + + base = regs[a]; + if (mrb_nil_p(base)) { + baseclass = mrb->c->ci->proc->target_class; + if (!baseclass) baseclass = mrb->c->ci->target_class; + + base = mrb_obj_value(baseclass); + } + c = mrb_vm_define_module(mrb, base, id); + regs[a] = mrb_obj_value(c); + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_EXEC) { + /* A Bx R(A) := blockexec(R(A),SEQ[Bx]) */ + int a = GETARG_A(i); + int bx = GETARG_Bx(i); + mrb_callinfo *ci; + mrb_value recv = regs[a]; + struct RProc *p; + mrb_irep *nirep = irep->reps[bx]; + + irep_uplink(mrb, irep, nirep); + nirep->target_class = mrb_class_ptr(recv); + /* prepare closure */ + p = mrb_closure_new(mrb, nirep); + p->c = NULL; + + /* 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; + + /* setup closure */ + p->target_class = ci->target_class; + ci->proc = p; + + irep = p->body.irep; + pool = irep->pool; + syms = irep->syms; + ci->nregs = irep->nregs; + stack_extend(mrb, ci->nregs); + stack_clear(regs+1, ci->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]); + struct RProc *p = mrb_proc_ptr(regs[a+1]); + + mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], p); + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_SCLASS) { + /* A B R(A) := R(B).singleton_class */ + regs[GETARG_A(i)] = mrb_singleton_class(mrb, regs[GETARG_B(i)]); + 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_set(mrb, exc); + goto L_RAISE; + } + regs[GETARG_A(i)] = mrb_obj_value(mrb->c->ci->target_class); + NEXT; + } + + CASE(OP_RANGE) { + /* A B C R(A) := range_new(R(B),R(B+1),C) */ + int b = GETARG_B(i); + mrb_value val = mrb_range_new(mrb, regs[b], regs[b+1], GETARG_C(i)); + regs[GETARG_A(i)] = val; + mrb_gc_arena_restore(mrb, ai); + NEXT; + } + + CASE(OP_DEBUG) { + /* A B C debug print R(A),R(B),R(C) */ +#ifdef MRB_ENABLE_DEBUG_HOOK + mrb->debug_op_hook(mrb, irep, pc, regs); +#else +#ifndef MRB_DISABLE_STDIO + printf("OP_DEBUG %d %d %d\n", GETARG_A(i), GETARG_B(i), GETARG_C(i)); +#else + abort(); +#endif +#endif + NEXT; + } + + CASE(OP_STOP) { + /* stop VM */ + L_STOP: + { + int epos = mrb->c->ci->epos; + + while (mrb->c->eidx > epos) { + ecall(mrb, --mrb->c->eidx); + } + } + ERR_PC_CLR(mrb); + mrb->jmp = prev_jmp; + if (mrb->exc) { + 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); + } + ERR_PC_SET(mrb, pc); + mrb_exc_set(mrb, exc); + goto L_RAISE; + } + } + END_DISPATCH; +#undef regs + + } + MRB_CATCH(&c_jmp) { + exc_catched = TRUE; + goto RETRY_TRY_BLOCK; + } + MRB_END_EXC(&c_jmp); +} + +MRB_API mrb_value +mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self) +{ + 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_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) +{ + mrb_callinfo *ci; + mrb_value v; + + if (!mrb->c->cibase) { + return mrb_vm_run(mrb, proc, self, stack_keep); + } + if (mrb->c->ci == mrb->c->cibase) { + mrb->c->ci->env = NULL; + return mrb_vm_run(mrb, proc, self, stack_keep); + } + ci = cipush(mrb); + ci->mid = 0; + ci->nregs = 1; /* protect the receiver */ + ci->acc = CI_ACC_SKIP; + ci->target_class = mrb->object_class; + v = mrb_vm_run(mrb, proc, self, stack_keep); + cipop(mrb); + + return v; +} + +#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus) +# if !defined(MRB_ENABLE_CXX_ABI) +} /* end of extern "C" */ +# endif +mrb_int mrb_jmpbuf::jmpbuf_id = 0; +# if !defined(MRB_ENABLE_CXX_ABI) +extern "C" { +# endif +#endif |