diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/array.c | 139 | ||||
| -rw-r--r-- | src/backtrace.c | 243 | ||||
| -rw-r--r-- | src/class.c | 786 | ||||
| -rw-r--r-- | src/codedump.c | 453 | ||||
| -rw-r--r-- | src/codegen.c | 3129 | ||||
| -rw-r--r-- | src/compar.c | 2 | ||||
| -rw-r--r-- | src/debug.c | 16 | ||||
| -rw-r--r-- | src/dump.c | 295 | ||||
| -rw-r--r-- | src/enum.c | 2 | ||||
| -rw-r--r-- | src/error.c | 115 | ||||
| -rw-r--r-- | src/error.h | 2 | ||||
| -rw-r--r-- | src/etc.c | 133 | ||||
| -rw-r--r-- | src/fmt_fp.c | 371 | ||||
| -rw-r--r-- | src/gc.c | 798 | ||||
| -rw-r--r-- | src/hash.c | 154 | ||||
| -rw-r--r-- | src/init.c | 7 | ||||
| -rw-r--r-- | src/kernel.c | 189 | ||||
| -rw-r--r-- | src/keywords | 50 | ||||
| -rw-r--r-- | src/lex.def | 212 | ||||
| -rw-r--r-- | src/load.c | 391 | ||||
| -rw-r--r-- | src/mrb_throw.h | 41 | ||||
| -rw-r--r-- | src/mruby_core.rake | 63 | ||||
| -rw-r--r-- | src/node.h | 117 | ||||
| -rw-r--r-- | src/numeric.c | 274 | ||||
| -rw-r--r-- | src/object.c | 65 | ||||
| -rw-r--r-- | src/opcode.h | 2 | ||||
| -rw-r--r-- | src/parse.y | 6370 | ||||
| -rw-r--r-- | src/pool.c | 15 | ||||
| -rw-r--r-- | src/print.c | 58 | ||||
| -rw-r--r-- | src/proc.c | 107 | ||||
| -rw-r--r-- | src/range.c | 26 | ||||
| -rw-r--r-- | src/state.c | 88 | ||||
| -rw-r--r-- | src/string.c | 1384 | ||||
| -rw-r--r-- | src/symbol.c | 146 | ||||
| -rw-r--r-- | src/value_array.h | 2 | ||||
| -rw-r--r-- | src/variable.c | 156 | ||||
| -rw-r--r-- | src/version.c | 8 | ||||
| -rw-r--r-- | src/vm.c | 353 |
38 files changed, 4077 insertions, 12685 deletions
diff --git a/src/array.c b/src/array.c index d008e52cf..9b8a49584 100644 --- a/src/array.c +++ b/src/array.c @@ -4,25 +4,21 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/string.h" -#include "mruby/range.h" +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/string.h> +#include <mruby/range.h> #include "value_array.h" #define ARY_DEFAULT_LEN 4 #define ARY_SHRINK_RATIO 5 /* must be larger than 2 */ #define ARY_C_MAX_SIZE (SIZE_MAX / sizeof(mrb_value)) #define ARY_MAX_SIZE ((ARY_C_MAX_SIZE < (size_t)MRB_INT_MAX) ? (mrb_int)ARY_C_MAX_SIZE : MRB_INT_MAX-1) -#define ARY_SHARED_P(a) ((a)->flags & MRB_ARY_SHARED) -#define ARY_SET_SHARED_FLAG(a) ((a)->flags |= MRB_ARY_SHARED) -#define ARY_UNSET_SHARED_FLAG(a) ((a)->flags &= ~MRB_ARY_SHARED) static inline mrb_value ary_elt(mrb_value ary, mrb_int offset) { - if (RARRAY_LEN(ary) == 0) return mrb_nil_value(); if (offset < 0 || RARRAY_LEN(ary) <= offset) { return mrb_nil_value(); } @@ -51,14 +47,14 @@ ary_new_capa(mrb_state *mrb, mrb_int capa) return a; } -mrb_value +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_value +MRB_API mrb_value mrb_ary_new(mrb_state *mrb) { return mrb_ary_new_capa(mrb, 0); @@ -88,21 +84,18 @@ array_copy(mrb_value *dst, const mrb_value *src, mrb_int size) } } -mrb_value +MRB_API mrb_value mrb_ary_new_from_values(mrb_state *mrb, mrb_int size, const mrb_value *vals) { - mrb_value ary; - struct RArray *a; + struct RArray *a = ary_new_capa(mrb, size); - ary = mrb_ary_new_capa(mrb, size); - a = mrb_ary_ptr(ary); array_copy(a->ptr, vals, size); a->len = size; - return ary; + return mrb_obj_value(a); } -mrb_value +MRB_API mrb_value mrb_assoc_new(mrb_state *mrb, mrb_value car, mrb_value cdr) { struct RArray *a; @@ -153,7 +146,7 @@ ary_modify(mrb_state *mrb, struct RArray *a) } } -void +MRB_API void mrb_ary_modify(mrb_state *mrb, struct RArray* a) { mrb_write_barrier(mrb, (struct RBasic*)a); @@ -200,10 +193,6 @@ ary_expand_capa(mrb_state *mrb, struct RArray *a, mrb_int len) if (capa > a->aux.capa) { mrb_value *expanded_ptr = (mrb_value *)mrb_realloc(mrb, a->ptr, sizeof(mrb_value)*capa); - if (!expanded_ptr) { - mrb_raise(mrb, E_RUNTIME_ERROR, "out of memory"); - } - a->aux.capa = capa; a->ptr = expanded_ptr; } @@ -231,6 +220,28 @@ ary_shrink_capa(mrb_state *mrb, struct RArray *a) } } +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) { + a->len = new_len; + if (new_len < old_len) { + ary_shrink_capa(mrb, a); + } + else { + ary_expand_capa(mrb, a, new_len); + ary_fill_with_nil(a->ptr + old_len, new_len - old_len); + } + } + + return ary; +} + static mrb_value mrb_ary_s_create(mrb_state *mrb, mrb_value self) { @@ -254,7 +265,7 @@ ary_concat(mrb_state *mrb, struct RArray *a, mrb_value *ptr, mrb_int blen) a->len = len; } -void +MRB_API void mrb_ary_concat(mrb_state *mrb, mrb_value self, mrb_value other) { struct RArray *a2 = mrb_ary_ptr(other); @@ -278,18 +289,19 @@ mrb_ary_plus(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; - mrb_value ary; mrb_value *ptr; mrb_int blen; mrb_get_args(mrb, "a", &ptr, &blen); - ary = mrb_ary_new_capa(mrb, a1->len + blen); - a2 = mrb_ary_ptr(ary); + if (ARY_MAX_SIZE - blen < a1->len) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + } + a2 = ary_new_capa(mrb, a1->len + blen); array_copy(a2->ptr, a1->ptr, a1->len); array_copy(a2->ptr + a1->len, ptr, blen); a2->len = a1->len + blen; - return ary; + return mrb_obj_value(a2); } static void @@ -303,7 +315,7 @@ ary_replace(mrb_state *mrb, struct RArray *a, mrb_value *argv, mrb_int len) a->len = len; } -void +MRB_API void mrb_ary_replace(mrb_state *mrb, mrb_value self, mrb_value other) { struct RArray *a2 = mrb_ary_ptr(other); @@ -327,7 +339,6 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; - mrb_value ary; mrb_value *ptr; mrb_int times; @@ -336,9 +347,10 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); } if (times == 0) return mrb_ary_new(mrb); - - ary = mrb_ary_new_capa(mrb, a1->len * times); - a2 = mrb_ary_ptr(ary); + if (ARY_MAX_SIZE / times < a1->len) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "array size too big"); + } + a2 = ary_new_capa(mrb, a1->len * times); ptr = a2->ptr; while (times--) { array_copy(ptr, a1->ptr, a1->len); @@ -346,7 +358,7 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) a2->len += a1->len; } - return ary; + return mrb_obj_value(a2); } static mrb_value @@ -373,11 +385,8 @@ mrb_ary_reverse_bang(mrb_state *mrb, mrb_value self) static mrb_value mrb_ary_reverse(mrb_state *mrb, mrb_value self) { - struct RArray *a = mrb_ary_ptr(self), *b; - mrb_value ary; + struct RArray *a = mrb_ary_ptr(self), *b = ary_new_capa(mrb, a->len); - ary = mrb_ary_new_capa(mrb, a->len); - b = mrb_ary_ptr(ary); if (a->len > 0) { mrb_value *p1, *p2, *e; @@ -389,10 +398,10 @@ mrb_ary_reverse(mrb_state *mrb, mrb_value self) } b->len = a->len; } - return ary; + return mrb_obj_value(b); } -void +MRB_API void mrb_ary_push(mrb_state *mrb, mrb_value ary, mrb_value elem) { struct RArray *a = mrb_ary_ptr(ary); @@ -418,7 +427,7 @@ mrb_ary_push_m(mrb_state *mrb, mrb_value self) return self; } -mrb_value +MRB_API mrb_value mrb_ary_pop(mrb_state *mrb, mrb_value ary) { struct RArray *a = mrb_ary_ptr(ary); @@ -429,7 +438,7 @@ mrb_ary_pop(mrb_state *mrb, mrb_value ary) #define ARY_SHIFT_SHARED_MIN 10 -mrb_value +MRB_API mrb_value mrb_ary_shift(mrb_state *mrb, mrb_value self) { struct RArray *a = mrb_ary_ptr(self); @@ -465,7 +474,7 @@ mrb_ary_shift(mrb_state *mrb, mrb_value self) item = 0 self.unshift item p self #=> [0, 1, 2, 3] */ -mrb_value +MRB_API mrb_value mrb_ary_unshift(mrb_state *mrb, mrb_value self, mrb_value item) { struct RArray *a = mrb_ary_ptr(self); @@ -518,7 +527,7 @@ mrb_ary_unshift_m(mrb_state *mrb, mrb_value self) return self; } -mrb_value +MRB_API mrb_value mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n) { struct RArray *a = mrb_ary_ptr(ary); @@ -530,7 +539,7 @@ mrb_ary_ref(mrb_state *mrb, mrb_value ary, mrb_int n) return a->ptr[n]; } -void +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); @@ -554,12 +563,12 @@ mrb_ary_set(mrb_state *mrb, mrb_value ary, mrb_int n, mrb_value val) mrb_field_write_barrier_value(mrb, (struct RBasic*)a, val); } -mrb_value +MRB_API mrb_value mrb_ary_splice(mrb_state *mrb, mrb_value ary, mrb_int head, mrb_int len, mrb_value rpl) { struct RArray *a = mrb_ary_ptr(ary); mrb_int tail, size; - mrb_value *argv; + const mrb_value *argv; mrb_int i, argc; ary_modify(mrb, a); @@ -643,10 +652,14 @@ 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; + mrb_int i, argc; + mrb_value *argv; - mrb_get_args(mrb, "i", &i); + mrb_get_args(mrb, "i*", &i, &argv, &argc); return i; } } @@ -875,7 +888,7 @@ mrb_ary_rindex_m(mrb_state *mrb, mrb_value self) return mrb_nil_value(); } -mrb_value +MRB_API mrb_value mrb_ary_splat(mrb_state *mrb, mrb_value v) { if (mrb_array_p(v)) { @@ -897,15 +910,20 @@ mrb_ary_size(mrb_state *mrb, mrb_value self) return mrb_fixnum_value(a->len); } -mrb_value +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->aux.shared); + ARY_UNSET_SHARED_FLAG(a); + } + else { + mrb_free(mrb, a->ptr); + } a->len = 0; a->aux.capa = 0; - mrb_free(mrb, a->ptr); a->ptr = 0; return self; @@ -919,13 +937,13 @@ mrb_ary_empty_p(mrb_state *mrb, mrb_value self) return mrb_bool_value(a->len == 0); } -mrb_value +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_value +MRB_API mrb_value mrb_ary_entry(mrb_value ary, mrb_int offset) { if (offset < 0) { @@ -989,7 +1007,7 @@ join_ary(mrb_state *mrb, mrb_value ary, mrb_value sep, mrb_value list) return result; } -mrb_value +MRB_API mrb_value mrb_ary_join(mrb_state *mrb, mrb_value ary, mrb_value sep) { sep = mrb_obj_as_string(mrb, sep); @@ -1012,7 +1030,7 @@ mrb_ary_join_m(mrb_state *mrb, mrb_value ary) { mrb_value sep = mrb_nil_value(); - mrb_get_args(mrb, "|S", &sep); + mrb_get_args(mrb, "|S!", &sep); return mrb_ary_join(mrb, ary, sep); } @@ -1023,7 +1041,6 @@ mrb_ary_eq(mrb_state *mrb, mrb_value ary1) mrb_get_args(mrb, "o", &ary2); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_true_value(); - if (mrb_special_const_p(ary2)) return mrb_false_value(); if (!mrb_array_p(ary2)) { return mrb_false_value(); } @@ -1039,7 +1056,6 @@ mrb_ary_cmp(mrb_state *mrb, mrb_value ary1) mrb_get_args(mrb, "o", &ary2); if (mrb_obj_equal(mrb, ary1, ary2)) return mrb_fixnum_value(0); - if (mrb_special_const_p(ary2)) return mrb_nil_value(); if (!mrb_array_p(ary2)) { return mrb_nil_value(); } @@ -1052,7 +1068,7 @@ mrb_init_array(mrb_state *mrb) { struct RClass *a; - a = mrb->array_class = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */ + mrb->array_class = a = mrb_define_class(mrb, "Array", mrb->object_class); /* 15.2.12 */ MRB_SET_INSTANCE_TT(a, MRB_TT_ARRAY); mrb_define_class_method(mrb, a, "[]", mrb_ary_s_create, MRB_ARGS_ANY()); /* 15.2.12.4.1 */ @@ -1084,5 +1100,6 @@ mrb_init_array(mrb_state *mrb) mrb_define_method(mrb, a, "unshift", mrb_ary_unshift_m, MRB_ARGS_ANY()); /* 15.2.12.5.30 */ mrb_define_method(mrb, a, "__ary_eq", mrb_ary_eq, MRB_ARGS_REQ(1)); - mrb_define_method(mrb, a, "__ary_cmp", mrb_ary_cmp, MRB_ARGS_REQ(1)); + mrb_define_method(mrb, a, "__ary_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/src/backtrace.c b/src/backtrace.c index 1e1f9fa1a..80a5e2935 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -4,137 +4,158 @@ ** See Copyright Notice in mruby.h */ -#include <stdarg.h> -#include "mruby.h" -#include "mruby/variable.h" -#include "mruby/proc.h" -#include "mruby/array.h" -#include "mruby/string.h" -#include "mruby/class.h" -#include "mruby/debug.h" -#include "mruby/error.h" - -#ifdef ENABLE_STDIO - -typedef void (*output_stream_func)(mrb_state*, void*, int, const char*, ...); +#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> + +struct backtrace_location { + int i; + int lineno; + const char *filename; + const char *method; + const char *sep; + const char *class_name; +}; + +typedef void (*output_stream_func)(mrb_state*, struct backtrace_location*, void*); + +#ifndef MRB_DISABLE_STDIO + +struct print_backtrace_args { + FILE *stream; + int tracehead; +}; static void -print_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...) +print_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data) { - va_list ap; + struct print_backtrace_args *args; - va_start(ap, format); - vfprintf((FILE*)stream, format, ap); - va_end(ap); -} + args = (struct print_backtrace_args*)data; + + if (args->tracehead) { + fprintf(args->stream, "trace:\n"); + args->tracehead = FALSE; + } + + fprintf(args->stream, "\t[%d] %s:%d", loc->i, loc->filename, loc->lineno); + + if (loc->method) { + if (loc->class_name) { + fprintf(args->stream, ":in %s%s%s", loc->class_name, loc->sep, loc->method); + } + else { + fprintf(args->stream, ":in %s", loc->method); + } + } + fprintf(args->stream, "\n"); +} -#define MIN_BUFSIZE 127 +#endif static void -get_backtrace_i(mrb_state *mrb, void *stream, int level, const char *format, ...) +get_backtrace_i(mrb_state *mrb, struct backtrace_location *loc, void *data) { - va_list ap; mrb_value ary, str; int ai; - if (level > 0) { - return; - } - ai = mrb_gc_arena_save(mrb); - ary = mrb_obj_value((struct RArray*)stream); + ary = mrb_obj_value((struct RArray*)data); - va_start(ap, format); - str = mrb_str_new(mrb, 0, vsnprintf(NULL, 0, format, ap) + 1); - va_end(ap); + str = mrb_str_new_cstr(mrb, loc->filename); + mrb_str_cat_lit(mrb, str, ":"); + mrb_str_concat(mrb, str, mrb_fixnum_to_str(mrb, mrb_fixnum_value(loc->lineno), 10)); - va_start(ap, format); - vsnprintf(RSTRING_PTR(str), RSTRING_LEN(str), format, ap); - va_end(ap); + if (loc->method) { + mrb_str_cat_lit(mrb, str, ":in "); + + if (loc->class_name) { + mrb_str_cat_cstr(mrb, str, loc->class_name); + mrb_str_cat_cstr(mrb, str, loc->sep); + } + + mrb_str_cat_cstr(mrb, str, loc->method); + } - mrb_str_resize(mrb, str, RSTRING_LEN(str) - 1); mrb_ary_push(mrb, ary, str); mrb_gc_arena_restore(mrb, ai); } static void -output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *stream) +output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_func func, void *data) { - mrb_callinfo *ci; - const char *filename, *method, *sep; - int i, lineno, tracehead = 1; + int i; if (ciidx >= mrb->c->ciend - mrb->c->cibase) ciidx = 10; /* ciidx is broken... */ for (i = ciidx; i >= 0; i--) { + struct backtrace_location loc; + mrb_callinfo *ci; + mrb_irep *irep; + mrb_code *pc; + ci = &mrb->c->cibase[i]; - filename = NULL; if (!ci->proc) continue; - if (MRB_PROC_CFUNC_P(ci->proc)) { - continue; + if (MRB_PROC_CFUNC_P(ci->proc)) continue; + + irep = ci->proc->body.irep; + + if (mrb->c->cibase[i].err) { + pc = mrb->c->cibase[i].err; } - else { - mrb_irep *irep = ci->proc->body.irep; - mrb_code *pc; - - if (mrb->c->cibase[i].err) { - pc = mrb->c->cibase[i].err; - } - else if (i+1 <= ciidx) { - pc = mrb->c->cibase[i+1].pc - 1; - } - else { - pc = pc0; - } - filename = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq)); - lineno = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq)); + else if (i+1 <= ciidx) { + pc = mrb->c->cibase[i+1].pc - 1; } - if (lineno == -1) continue; - if (ci->target_class == ci->proc->target_class) - sep = "."; - else - sep = "#"; - - if (!filename) { - filename = "(unknown)"; + else { + pc = pc0; } + loc.filename = mrb_debug_get_filename(irep, (uint32_t)(pc - irep->iseq)); + loc.lineno = mrb_debug_get_line(irep, (uint32_t)(pc - irep->iseq)); - if (tracehead) { - func(mrb, stream, 1, "trace:\n"); - tracehead = 0; - } - method = mrb_sym2name(mrb, ci->mid); - if (method) { - const char *cn = mrb_class_name(mrb, ci->proc->target_class); - - if (cn) { - func(mrb, stream, 1, "\t[%d] ", i); - func(mrb, stream, 0, "%s:%d:in %s%s%s", filename, lineno, cn, sep, method); - func(mrb, stream, 1, "\n"); - } - else { - func(mrb, stream, 1, "\t[%d] ", i); - func(mrb, stream, 0, "%s:%d:in %s", filename, lineno, method); - func(mrb, stream, 1, "\n"); - } + if (loc.lineno == -1) continue; + + if (ci->target_class == ci->proc->target_class) { + loc.sep = "."; } else { - func(mrb, stream, 1, "\t[%d] ", i); - func(mrb, stream, 0, "%s:%d", filename, lineno); - func(mrb, stream, 1, "\n"); + loc.sep = "#"; } + + if (!loc.filename) { + loc.filename = "(unknown)"; + } + + loc.method = mrb_sym2name(mrb, ci->mid); + loc.class_name = mrb_class_name(mrb, ci->proc->target_class); + loc.i = i; + func(mrb, &loc, data); } } static void exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func func, void *stream) { + mrb_value lastpc; + mrb_code *code; + + lastpc = mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc")); + if (mrb_nil_p(lastpc)) { + code = NULL; + } else { + code = (mrb_code*)mrb_cptr(lastpc); + } + output_backtrace(mrb, mrb_fixnum(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "ciidx"))), - (mrb_code*)mrb_cptr(mrb_obj_iv_get(mrb, exc, mrb_intern_lit(mrb, "lastpc"))), - func, stream); + code, func, stream); } /* mrb_print_backtrace/mrb_get_backtrace: @@ -144,13 +165,32 @@ exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func fun overwritten. So invoke these functions just after detecting exceptions. */ -void +#ifndef MRB_DISABLE_STDIO + +MRB_API void mrb_print_backtrace(mrb_state *mrb) { - exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)stderr); + struct print_backtrace_args args; + + if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) { + return; + } + + args.stream = stderr; + args.tracehead = TRUE; + exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)&args); } -mrb_value +#else + +MRB_API void +mrb_print_backtrace(mrb_state *mrb) +{ +} + +#endif + +MRB_API mrb_value mrb_exc_backtrace(mrb_state *mrb, mrb_value self) { mrb_value ary; @@ -161,7 +201,7 @@ mrb_exc_backtrace(mrb_state *mrb, mrb_value self) return ary; } -mrb_value +MRB_API mrb_value mrb_get_backtrace(mrb_state *mrb) { mrb_value ary; @@ -175,24 +215,3 @@ mrb_get_backtrace(mrb_state *mrb) return ary; } - -#else - -void -mrb_print_backtrace(mrb_state *mrb) -{ -} - -mrb_value -mrb_exc_backtrace(mrb_state *mrb, mrb_value self) -{ - return mrb_ary_new(mrb); -} - -mrb_value -mrb_get_backtrace(mrb_state *mrb) -{ - return mrb_ary_new(mrb); -} - -#endif diff --git a/src/class.c b/src/class.c index ba078911b..ccbbe2544 100644 --- a/src/class.c +++ b/src/class.c @@ -4,17 +4,16 @@ ** See Copyright Notice in mruby.h */ -#include <ctype.h> #include <stdarg.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/numeric.h" -#include "mruby/proc.h" -#include "mruby/string.h" -#include "mruby/variable.h" -#include "mruby/error.h" -#include "mruby/data.h" +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/numeric.h> +#include <mruby/proc.h> +#include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/error.h> +#include <mruby/data.h> KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal) @@ -77,7 +76,7 @@ prepare_singleton_class(mrb_state *mrb, struct RBasic *o) if (o->c->tt == MRB_TT_SCLASS) return; sc = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_SCLASS, mrb->class_class); - sc->mt = 0; + sc->mt = kh_init(mt, mrb); sc->iv = 0; if (o->tt == MRB_TT_CLASS) { c = (struct RClass*)o; @@ -122,22 +121,35 @@ module_from_sym(mrb_state *mrb, struct RClass *klass, mrb_sym id) return mrb_class_ptr(c); } -struct RClass* +MRB_API struct RClass* mrb_class_outer_module(mrb_state *mrb, struct RClass *c) { mrb_value outer; outer = mrb_obj_iv_get(mrb, (struct RObject*)c, mrb_intern_lit(mrb, "__outer__")); - if (mrb_nil_p(outer)) return 0; + if (mrb_nil_p(outer)) return NULL; return mrb_class_ptr(outer); } +static void +check_if_class_or_module(mrb_state *mrb, mrb_value obj) +{ + switch (mrb_type(obj)) { + case MRB_TT_CLASS: + case MRB_TT_SCLASS: + case MRB_TT_MODULE: + return; + default: + 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, outer, name)) { + if (mrb_const_defined_at(mrb, mrb_obj_value(outer), name)) { return module_from_sym(mrb, outer, name); } m = mrb_module_new(mrb); @@ -146,25 +158,26 @@ define_module(mrb_state *mrb, mrb_sym name, struct RClass *outer) return m; } -struct RClass* +MRB_API struct RClass* mrb_define_module_id(mrb_state *mrb, mrb_sym name) { return define_module(mrb, name, mrb->object_class); } -struct RClass* +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); } -struct RClass* +MRB_API struct RClass* mrb_vm_define_module(mrb_state *mrb, mrb_value outer, mrb_sym id) { + check_if_class_or_module(mrb, outer); return define_module(mrb, id, mrb_class_ptr(outer)); } -struct RClass * +MRB_API struct RClass* mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name) { mrb_sym id = mrb_intern_cstr(mrb, name); @@ -175,12 +188,20 @@ mrb_define_module_under(mrb_state *mrb, struct RClass *outer, const char *name) } static struct RClass* +find_origin(struct RClass *c) +{ + MRB_CLASS_ORIGIN(c); + return c; +} + +static struct RClass* define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass *outer) { struct RClass * c; - if (mrb_const_defined_at(mrb, outer, name)) { + 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), @@ -195,16 +216,16 @@ define_class(mrb_state *mrb, mrb_sym name, struct RClass *super, struct RClass * return c; } -struct RClass* +MRB_API struct RClass* mrb_define_class_id(mrb_state *mrb, mrb_sym name, struct RClass *super) { if (!super) { - mrb_warn(mrb, "no super class for `%S', Object assumed", mrb_sym2str(mrb, name)); + mrb_warn(mrb, "no super class for '%S', Object assumed", mrb_sym2str(mrb, name)); } return define_class(mrb, name, super, mrb->object_class); } -struct RClass* +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); @@ -218,7 +239,7 @@ mrb_class_inherited(mrb_state *mrb, struct RClass *super, struct RClass *klass) mrb_funcall(mrb, mrb_obj_value(super), "inherited", 1, mrb_obj_value(klass)); } -struct RClass* +MRB_API struct RClass* mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id) { struct RClass *s; @@ -233,22 +254,14 @@ mrb_vm_define_class(mrb_state *mrb, mrb_value outer, mrb_value super, mrb_sym id else { s = 0; } - switch (mrb_type(outer)) { - case MRB_TT_CLASS: - case MRB_TT_SCLASS: - case MRB_TT_MODULE: - break; - default: - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a class/module", outer); - break; - } + check_if_class_or_module(mrb, outer); c = define_class(mrb, id, s, mrb_class_ptr(outer)); mrb_class_inherited(mrb, mrb_class_real(c->super), c); return c; } -mrb_bool +MRB_API mrb_bool mrb_class_defined(mrb_state *mrb, const char *name) { mrb_value sym = mrb_check_intern_cstr(mrb, name); @@ -258,25 +271,25 @@ mrb_class_defined(mrb_state *mrb, const char *name) return mrb_const_defined(mrb, mrb_obj_value(mrb->object_class), mrb_symbol(sym)); } -struct RClass * +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)); } -struct RClass * +MRB_API struct RClass * mrb_class_get(mrb_state *mrb, const char *name) { return mrb_class_get_under(mrb, mrb->object_class, name); } -struct RClass * +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)); } -struct RClass * +MRB_API struct RClass * mrb_module_get(mrb_state *mrb, const char *name) { return mrb_module_get_under(mrb, mrb->object_class, name); @@ -298,7 +311,7 @@ mrb_module_get(mrb_state *mrb, const char *name) * \note if a class named \a name is already defined and its superclass is * \a super, the function just returns the defined class. */ -struct RClass * +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); @@ -306,7 +319,7 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s #if 0 if (!super) { - mrb_warn(mrb, "no super class for `%S::%S', Object assumed", + mrb_warn(mrb, "no super class for '%S::%S', Object assumed", mrb_obj_value(outer), mrb_sym2str(mrb, id)); } #endif @@ -315,11 +328,13 @@ mrb_define_class_under(mrb_state *mrb, struct RClass *outer, const char *name, s return c; } -void +MRB_API void mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RProc *p) { - khash_t(mt) *h = c->mt; + khash_t(mt) *h; khiter_t k; + MRB_CLASS_ORIGIN(c); + h = c->mt; if (!h) h = c->mt = kh_init(mt, mrb); k = kh_put(mt, mrb, h, mid); @@ -329,7 +344,7 @@ mrb_define_method_raw(mrb_state *mrb, struct RClass *c, mrb_sym mid, struct RPro } } -void +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; @@ -341,28 +356,37 @@ mrb_define_method_id(mrb_state *mrb, struct RClass *c, mrb_sym mid, mrb_func_t f mrb_gc_arena_restore(mrb, ai); } -void +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); } -void -mrb_define_method_vm(mrb_state *mrb, struct RClass *c, mrb_sym name, mrb_value body) +/* a function to raise NotImplementedError with current method name */ +MRB_API void +mrb_notimplement(mrb_state *mrb) { - khash_t(mt) *h = c->mt; - khiter_t k; - struct RProc *p; + const char *str; + mrb_int len; + mrb_callinfo *ci = mrb->c->ci; - if (!h) h = c->mt = kh_init(mt, mrb); - k = kh_put(mt, mrb, h, name); - p = mrb_proc_ptr(body); - kh_value(h, k) = p; - if (p) { - mrb_field_write_barrier(mrb, (struct RBasic *)c, (struct RBasic *)p); + 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) { @@ -393,6 +417,21 @@ 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); + } +} + /* retrieve arguments from mrb_state. @@ -406,12 +445,12 @@ to_hash(mrb_state *mrb, mrb_value val) ---------------------------------------------------------------------------------------------- o: Object [mrb_value] C: class/module [mrb_value] - S: String [mrb_value] - A: Array [mrb_value] - H: Hash [mrb_value] - s: String [char*,mrb_int] Receive two arguments. - z: String [char*] NUL terminated string. - a: Array [mrb_value*,mrb_int] Receive two arguments. + 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] @@ -422,7 +461,7 @@ to_hash(mrb_state *mrb, mrb_value val) |: optional Next argument of '|' and later are optional. ?: optional given [mrb_bool] true if preceding argument (optional) is given. */ -mrb_int +MRB_API mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...) { char c; @@ -496,6 +535,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value *p; p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *p = *sp++; + i++; + break; + } + } if (i < argc) { *p = to_str(mrb, *sp++); i++; @@ -507,6 +554,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value *p; p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *p = *sp++; + i++; + break; + } + } if (i < argc) { *p = to_ary(mrb, *sp++); i++; @@ -518,6 +573,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value *p; p = va_arg(ap, mrb_value*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *p = *sp++; + i++; + break; + } + } if (i < argc) { *p = to_hash(mrb, *sp++); i++; @@ -532,6 +595,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) ps = va_arg(ap, char**); pl = va_arg(ap, mrb_int*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *ps = NULL; + *pl = 0; + i++; + break; + } + } if (i < argc) { ss = to_str(mrb, *sp++); *ps = RSTRING_PTR(ss); @@ -543,9 +615,17 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) case 'z': { mrb_value ss; - char **ps; - - ps = va_arg(ap, char**); + const char **ps; + + ps = va_arg(ap, const char**); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *ps = NULL; + i++; sp++; + break; + } + } if (i < argc) { ss = to_str(mrb, *sp++); *ps = mrb_string_value_cstr(mrb, &ss); @@ -562,6 +642,15 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) pb = va_arg(ap, mrb_value**); pl = va_arg(ap, mrb_int*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *pb = 0; + *pl = 0; + i++; sp++; + break; + } + } if (i < argc) { aa = to_ary(mrb, *sp++); a = mrb_ary_ptr(aa); @@ -635,16 +724,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) mrb_value ss; ss = *sp++; - if (mrb_type(ss) == MRB_TT_SYMBOL) { - *symp = mrb_symbol(ss); - } - else if (mrb_string_p(ss)) { - *symp = 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); - } + *symp = to_sym(mrb, ss); i++; } } @@ -656,6 +736,14 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) datap = va_arg(ap, void**); type = va_arg(ap, struct mrb_data_type const*); + if (*format == '!') { + format++; + if (i < argc && mrb_nil_p(*sp)) { + *datap = 0; + i++; sp++; + break; + } + } if (i < argc) { *datap = mrb_data_get_ptr(mrb, *sp++, type); ++i; @@ -739,47 +827,130 @@ boot_defclass(mrb_state *mrb, struct RClass *super) return c; } -void -mrb_include_module(mrb_state *mrb, struct RClass *c, struct RClass *m) +static void +boot_initmod(mrb_state *mrb, struct RClass *mod) +{ + mod->mt = kh_init(mt, mrb); +} + +static struct RClass* +include_class_new(mrb_state *mrb, struct RClass *m, struct RClass *super) { - struct RClass *ins_pos; + 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; - ins_pos = c; while (m) { - struct RClass *p = c, *ic; int superclass_seen = 0; - if (c->mt && c->mt == m->mt) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "cyclic include detected"); - } - while (p) { - if (c != p && p->tt == MRB_TT_CLASS) { - superclass_seen = 1; - } - else if (p->mt == m->mt) { - if (p->tt == MRB_TT_ICLASS && !superclass_seen) { - ins_pos = p; + if (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; } - goto skip; + } else if (p->tt == MRB_TT_CLASS) { + if (!search_super) break; + superclass_seen = 1; } p = p->super; } - ic = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_ICLASS, mrb->class_class); - if (m->tt == MRB_TT_ICLASS) { - ic->c = m->c; - } - else { - ic->c = m; - } - ic->mt = m->mt; - ic->iv = m->iv; - ic->super = ins_pos->super; + + ic = include_class_new(mrb, m, ins_pos->super); ins_pos->super = ic; - mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ic); + mrb_field_write_barrier(mrb, (struct RBasic*)ins_pos, (struct RBasic*)ins_pos->super); ins_pos = ic; skip: m = m->super; } + 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; + 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_prepend(mrb_state *mrb, mrb_value klass) +{ + mrb_value *argv; + mrb_int argc, i; + + mrb_get_args(mrb, "*", &argv, &argc); + for (i=0; i<argc; i++) { + mrb_check_type(mrb, argv[i], MRB_TT_MODULE); + } + while (argc--) { + mrb_funcall(mrb, argv[argc], "prepend_features", 1, klass); + mrb_funcall(mrb, argv[argc], "prepended", 1, klass); + } + + return klass; } static mrb_value @@ -853,15 +1024,12 @@ mrb_mod_ancestors(mrb_state *mrb, mrb_value self) { mrb_value result; struct RClass *c = mrb_class_ptr(self); - result = mrb_ary_new(mrb); - mrb_ary_push(mrb, result, mrb_obj_value(c)); - c = c->super; while (c) { if (c->tt == MRB_TT_ICLASS) { mrb_ary_push(mrb, result, mrb_obj_value(c->c)); } - else if (c->tt != MRB_TT_SCLASS) { + else if (!(c->flags & MRB_FLAG_IS_PREPENDED)) { mrb_ary_push(mrb, result, mrb_obj_value(c)); } c = c->super; @@ -886,11 +1054,15 @@ 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->tt == MRB_TT_ICLASS) { - mrb_ary_push(mrb, result, mrb_obj_value(c->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; } @@ -902,10 +1074,11 @@ static mrb_value mrb_mod_initialize(mrb_state *mrb, mrb_value mod) { mrb_value b; - - mrb_get_args(mrb, "&", &b); + struct RClass *m = mrb_class_ptr(mod); + boot_initmod(mrb, m); // bootstrap a newly initialized module + mrb_get_args(mrb, "|&", &b); if (!mrb_nil_p(b)) { - mrb_yield_with_class(mrb, b, 1, &mod, mod, mrb_class_ptr(mod)); + mrb_yield_with_class(mrb, b, 1, &mod, mod, m); } return mod; } @@ -958,7 +1131,7 @@ mrb_mod_dummy_visibility(mrb_state *mrb, mrb_value mod) return mod; } -mrb_value +MRB_API mrb_value mrb_singleton_class(mrb_state *mrb, mrb_value v) { struct RBasic *obj; @@ -989,27 +1162,27 @@ mrb_singleton_class(mrb_state *mrb, mrb_value v) return mrb_obj_value(obj->c); } -void +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); } -void +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); } -void +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); } -struct RProc* +MRB_API struct RProc* mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) { khiter_t k; @@ -1030,10 +1203,10 @@ mrb_method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid) } c = c->super; } - return 0; /* no method */ + return NULL; /* no method */ } -struct RProc* +MRB_API struct RProc* mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid) { struct RProc *m; @@ -1041,7 +1214,7 @@ mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid) m = mrb_method_search_vm(mrb, &c, mid); if (!m) { mrb_value inspect = mrb_funcall(mrb, mrb_obj_value(c), "inspect", 0); - if (RSTRING_LEN(inspect) > 64) { + 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", @@ -1051,6 +1224,91 @@ mrb_method_search(mrb_state *mrb, struct RClass* c, mrb_sym mid) } 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_buf_new(mrb, RSTRING_LEN(name)+1); + mrb_str_cat_lit(mrb, str, "@"); + mrb_str_cat_str(mrb, str, name); + sym = mrb_intern_str(mrb, str); + mrb_iv_check(mrb, sym); + name = mrb_symbol_value(sym); + mrb_define_method_raw(mrb, c, method, + mrb_proc_new_cfunc_with_env(mrb, attr_reader, 1, &name)); + 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_buf_new(mrb, RSTRING_LEN(name)+1); + mrb_str_cat_lit(mrb, str, "@"); + mrb_str_cat_str(mrb, str, name); + sym = mrb_intern_str(mrb, str); + mrb_iv_check(mrb, sym); + attr = mrb_symbol_value(sym); + + /* prepare method name (name=) */ + str = mrb_str_buf_new(mrb, RSTRING_LEN(str)); + mrb_str_cat_str(mrb, str, name); + mrb_str_cat_lit(mrb, str, "="); + method = mrb_intern_str(mrb, str); + + mrb_define_method_raw(mrb, c, method, + mrb_proc_new_cfunc_with_env(mrb, attr_writer, 1, &attr)); + mrb_gc_arena_restore(mrb, ai); + } + return mrb_nil_value(); +} + +static mrb_value mrb_instance_alloc(mrb_state *mrb, mrb_value cv) { struct RClass *c = mrb_class_ptr(cv); @@ -1069,15 +1327,15 @@ mrb_instance_alloc(mrb_state *mrb, mrb_value cv) * call-seq: * class.new(args, ...) -> obj * - * Calls <code>allocate</code> to create a new object of - * <i>class</i>'s class, then invokes that object's - * <code>initialize</code> method, passing it <i>args</i>. - * This is the method that ends up getting called whenever - * an object is constructed using .new. + * Creates a new object of <i>class</i>'s class, then + * invokes that object's <code>initialize</code> method, + * passing it <i>args</i>. This is the method that ends + * up getting called whenever an object is constructed using + * `.new`. * */ -mrb_value +MRB_API mrb_value mrb_instance_new(mrb_state *mrb, mrb_value cv) { mrb_value obj, blk; @@ -1091,7 +1349,7 @@ mrb_instance_new(mrb_state *mrb, mrb_value cv) return obj; } -mrb_value +MRB_API mrb_value mrb_obj_new(mrb_state *mrb, struct RClass *c, mrb_int argc, const mrb_value *argv) { mrb_value obj; @@ -1137,9 +1395,9 @@ mrb_class_superclass(mrb_state *mrb, mrb_value klass) struct RClass *c; c = mrb_class_ptr(klass); - c = c->super; + c = find_origin(c)->super; while (c && c->tt == MRB_TT_ICLASS) { - c = c->super; + c = find_origin(c)->super; } if (!c) return mrb_nil_value(); return mrb_obj_value(c); @@ -1157,6 +1415,31 @@ mrb_bob_not(mrb_state *mrb, mrb_value cv) return mrb_bool_value(!mrb_test(cv)); } +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 < 64) { + 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: @@ -1196,32 +1479,14 @@ mrb_bob_missing(mrb_state *mrb, mrb_value mod) mrb_sym name; mrb_value *a; mrb_int alen; - mrb_sym inspect; - mrb_value repr; mrb_get_args(mrb, "n*", &name, &a, &alen); - - inspect = mrb_intern_lit(mrb, "inspect"); - if (mrb->c->ci > mrb->c->cibase && mrb->c->ci[-1].mid == inspect) { - /* method missing in inspect; avoid recursion */ - repr = mrb_any_to_s(mrb, mod); - } - else if (mrb_respond_to(mrb, mod, inspect)) { - repr = mrb_funcall_argv(mrb, mod, inspect, 0, 0); - if (RSTRING_LEN(repr) > 64) { - repr = mrb_any_to_s(mrb, mod); - } - } - else { - repr = mrb_any_to_s(mrb, mod); - } - - mrb_no_method_error(mrb, name, alen, a, "undefined method '%S' for %S", mrb_sym2str(mrb, name), repr); + mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a)); /* not reached */ return mrb_nil_value(); } -mrb_bool +MRB_API mrb_bool mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid) { khiter_t k; @@ -1245,13 +1510,13 @@ mrb_obj_respond_to(mrb_state *mrb, struct RClass* c, mrb_sym mid) return FALSE; /* no method */ } -mrb_bool +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_value +MRB_API mrb_value mrb_class_path(mrb_state *mrb, struct RClass *c) { mrb_value path; @@ -1291,18 +1556,18 @@ mrb_class_path(mrb_state *mrb, struct RClass *c) return path; } -struct RClass * +MRB_API struct RClass * mrb_class_real(struct RClass* cl) { if (cl == 0) - return 0; + return NULL; while ((cl->tt == MRB_TT_SCLASS) || (cl->tt == MRB_TT_ICLASS)) { cl = cl->super; } return cl; } -const char* +MRB_API const char* mrb_class_name(mrb_state *mrb, struct RClass* c) { mrb_value path = mrb_class_path(mrb, c); @@ -1314,7 +1579,7 @@ mrb_class_name(mrb_state *mrb, struct RClass* c) return RSTRING_PTR(path); } -const char* +MRB_API const char* mrb_obj_classname(mrb_state *mrb, mrb_value obj) { return mrb_class_name(mrb, mrb_obj_class(mrb, obj)); @@ -1346,7 +1611,7 @@ mrb_check_inheritable(mrb_state *mrb, struct RClass *super) * \exception TypeError \a super is not inheritable. * \exception TypeError \a super is the Class class. */ -struct RClass * +MRB_API struct RClass* mrb_class_new(mrb_state *mrb, struct RClass *super) { struct RClass *c; @@ -1366,12 +1631,11 @@ mrb_class_new(mrb_state *mrb, struct RClass *super) /*! * Creates a new module. */ -struct RClass * +MRB_API struct RClass* mrb_module_new(mrb_state *mrb) { struct RClass *m = (struct RClass*)mrb_obj_alloc(mrb, MRB_TT_MODULE, mrb->module_class); - m->mt = kh_init(mt, mrb); - + boot_initmod(mrb, m); return m; } @@ -1389,18 +1653,18 @@ mrb_module_new(mrb_state *mrb) * self.class #=> Object */ -struct RClass* +MRB_API struct RClass* mrb_obj_class(mrb_state *mrb, mrb_value obj) { - return mrb_class_real(mrb_class(mrb, obj)); + return mrb_class_real(mrb_class(mrb, obj)); } -void +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_vm(mrb, c, a, mrb_obj_value(m)); + mrb_define_method_raw(mrb, c, a, m); } /*! @@ -1409,7 +1673,7 @@ mrb_alias_method(mrb_state *mrb, struct RClass *c, mrb_sym a, mrb_sym b) * \param name1 a new name for the method * \param name2 the original name of the method */ -void +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)); @@ -1438,13 +1702,13 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass) case MRB_TT_CLASS: case MRB_TT_MODULE: case MRB_TT_SCLASS: - mrb_str_append(mrb, str, mrb_inspect(mrb, v)); + mrb_str_cat_str(mrb, str, mrb_inspect(mrb, v)); break; default: - mrb_str_append(mrb, str, mrb_any_to_s(mrb, v)); + mrb_str_cat_str(mrb, str, mrb_any_to_s(mrb, v)); break; } - mrb_str_cat_lit(mrb, str, ">"); + return mrb_str_cat_lit(mrb, str, ">"); } else { struct RClass *c; @@ -1470,14 +1734,12 @@ mrb_mod_to_s(mrb_state *mrb, mrb_value klass) break; } mrb_str_concat(mrb, str, mrb_ptr_to_str(mrb, c)); - mrb_str_cat_lit(mrb, str, ">"); + return mrb_str_cat_lit(mrb, str, ">"); } else { - str = path; + return path; } } - - return str; } static mrb_value @@ -1494,24 +1756,21 @@ mrb_mod_alias(mrb_state *mrb, mrb_value mod) static void undef_method(mrb_state *mrb, struct RClass *c, mrb_sym a) { - mrb_value m; - 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_SET_VALUE(m, MRB_TT_PROC, value.p, 0); - mrb_define_method_vm(mrb, c, a, m); + mrb_define_method_raw(mrb, c, a, NULL); } } -void +MRB_API void mrb_undef_method(mrb_state *mrb, struct RClass *c, const char *name) { undef_method(mrb, c, mrb_intern_cstr(mrb, name)); } -void +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); @@ -1552,43 +1811,20 @@ mod_define_method(mrb_state *mrb, mrb_value self) } static void -check_cv_name_sym(mrb_state *mrb, mrb_sym id) -{ - const char *s; - mrb_int len; - - s = mrb_sym2name_len(mrb, id, &len); - if (len < 3 || !(s[0] == '@' && s[1] == '@')) { - mrb_name_error(mrb, id, "`%S' is not allowed as a class variable name", mrb_sym2str(mrb, id)); - } -} - -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); + mrb_name_error(mrb, mrb_intern_str(mrb, str), "'%S' is not allowed as a class variable name", str); } } -static mrb_value -get_sym_or_str_arg(mrb_state *mrb) +static void +check_cv_name_sym(mrb_state *mrb, mrb_sym id) { - mrb_value sym_or_str; - - mrb_get_args(mrb, "o", &sym_or_str); - - if (mrb_symbol_p(sym_or_str) || mrb_string_p(sym_or_str)) { - return sym_or_str; - } - else { - mrb_value obj = mrb_funcall(mrb, sym_or_str, "inspect", 0); - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", obj); - return mrb_nil_value(); - } + check_cv_name_str(mrb, mrb_sym2str(mrb, id)); } /* 15.2.2.4.16 */ @@ -1609,26 +1845,11 @@ get_sym_or_str_arg(mrb_state *mrb) static mrb_value mrb_mod_cvar_defined(mrb_state *mrb, mrb_value mod) { - mrb_value id; - mrb_bool defined_p; + mrb_sym id; - id = get_sym_or_str_arg(mrb); - if (mrb_symbol_p(id)) { - check_cv_name_sym(mrb, mrb_symbol(id)); - defined_p = mrb_cv_defined(mrb, mod, mrb_symbol(id)); - } - else { - mrb_value sym; - check_cv_name_str(mrb, id); - sym = mrb_check_intern_str(mrb, id); - if (mrb_nil_p(sym)) { - defined_p = FALSE; - } - else { - defined_p = mrb_cv_defined(mrb, mod, mrb_symbol(sym)); - } - } - return mrb_bool_value(defined_p); + 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 */ @@ -1763,41 +1984,29 @@ mrb_mod_remove_cvar(mrb_state *mrb, mrb_value mod) static mrb_value mrb_mod_method_defined(mrb_state *mrb, mrb_value mod) { - mrb_value id; - mrb_bool method_defined_p; + mrb_sym id; - id = get_sym_or_str_arg(mrb); - if (mrb_symbol_p(id)) { - method_defined_p = mrb_obj_respond_to(mrb, mrb_class_ptr(mod), mrb_symbol(id)); - } - else { - mrb_value sym = mrb_check_intern_str(mrb, id); - if (mrb_nil_p(sym)) { - method_defined_p = FALSE; - } - else { - method_defined_p = mrb_obj_respond_to(mrb, mrb_class_ptr(mod), mrb_symbol(sym)); - } - } - return mrb_bool_value(method_defined_p); + 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 = c->mt; + 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_name_error(mrb, mid, "method '%S' not defined in %S", mrb_sym2str(mrb, mid), mod); } @@ -1824,17 +2033,7 @@ mrb_mod_remove_method(mrb_state *mrb, mrb_value mod) return mod; } -static void -check_const_name_sym(mrb_state *mrb, mrb_sym id) -{ - const char *s; - mrb_int len; - s = mrb_sym2name_len(mrb, id, &len); - if (len < 1 || !ISUPPER(*s)) { - mrb_name_error(mrb, id, "wrong constant name %S", mrb_sym2str(mrb, id)); - } -} static void check_const_name_str(mrb_state *mrb, mrb_value str) @@ -1844,30 +2043,30 @@ check_const_name_str(mrb_state *mrb, mrb_value str) } } -static mrb_value -mrb_mod_const_defined(mrb_state *mrb, mrb_value mod) +static void +check_const_name_sym(mrb_state *mrb, mrb_sym id) { - mrb_value id; - mrb_bool const_defined_p; + check_const_name_str(mrb, mrb_sym2str(mrb, id)); +} - id = get_sym_or_str_arg(mrb); - if (mrb_type(id) == MRB_TT_SYMBOL) { - check_const_name_sym(mrb, mrb_symbol(id)); - const_defined_p = mrb_const_defined(mrb, mod, mrb_symbol(id)); - } - else { - mrb_value sym; - check_const_name_str(mrb, id); - sym = mrb_check_intern_str(mrb, id); - if (mrb_nil_p(sym)) { - const_defined_p = FALSE; - } - else { - const_defined_p = mrb_const_defined(mrb, mod, mrb_symbol(sym)); - } +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)); +} - return mrb_bool_value(const_defined_p); +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 @@ -1946,6 +2145,43 @@ mrb_mod_eqq(mrb_state *mrb, mrb_value 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; +} + void mrb_init_class(mrb_state *mrb) { @@ -1979,6 +2215,9 @@ mrb_init_class(mrb_state *mrb) name_class(mrb, mod, mrb_intern_lit(mrb, "Module")); /* 15.2.2 */ name_class(mrb, cls, mrb_intern_lit(mrb, "Class")); /* 15.2.3 */ + mrb->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()); @@ -1996,6 +2235,9 @@ mrb_init_class(mrb_state *mrb) 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, "prepend", mrb_mod_prepend, MRB_ARGS_ANY()); + 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, MRB_ARGS_ANY()); /* 15.2.2.4.27 */ 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 */ @@ -2006,17 +2248,21 @@ mrb_init_class(mrb_state *mrb) 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_REQ(1)); /* 15.2.2.4.20 */ + 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 */ diff --git a/src/codedump.c b/src/codedump.c new file mode 100644 index 000000000..4f4b6de57 --- /dev/null +++ b/src/codedump.c @@ -0,0 +1,453 @@ +#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: + case OP_R_RETURN: + printf("\treturn\t"); break; + case OP_R_BREAK: + printf("\tbreak\t"); break; + default: + printf("\tbroken\t"); break; + break; + } + print_lv(mrb, irep, c, RA); + break; + case OP_BLKPUSH: + printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d", GETARG_A(c), + (GETARG_Bx(c)>>10)&0x3f, + (GETARG_Bx(c)>>9)&0x1, + (GETARG_Bx(c)>>4)&0x1f, + (GETARG_Bx(c)>>0)&0xf); + print_lv(mrb, irep, c, RA); + break; + + case OP_LAMBDA: + printf("OP_LAMBDA\tR%d\tI(%+d)\t%d", GETARG_A(c), GETARG_b(c)+1, GETARG_c(c)); + print_lv(mrb, irep, c, RA); + break; + case OP_RANGE: + printf("OP_RANGE\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); + print_lv(mrb, irep, c, RAB); + break; + case OP_METHOD: + printf("OP_METHOD\tR%d\t:%s", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); + print_lv(mrb, irep, c, RA); + break; + + case OP_ADD: + printf("OP_ADD\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + case OP_ADDI: + printf("OP_ADDI\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + case OP_SUB: + printf("OP_SUB\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + case OP_SUBI: + printf("OP_SUBI\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + case OP_MUL: + printf("OP_MUL\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + case OP_DIV: + printf("OP_DIV\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + case OP_LT: + printf("OP_LT\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + case OP_LE: + printf("OP_LE\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + case OP_GT: + printf("OP_GT\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + case OP_GE: + printf("OP_GE\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + case OP_EQ: + printf("OP_EQ\tR%d\t:%s\t%d\n", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), + GETARG_C(c)); + break; + + case OP_STOP: + printf("OP_STOP\n"); + break; + + case OP_ARRAY: + printf("OP_ARRAY\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); + print_lv(mrb, irep, c, RAB); + break; + case OP_ARYCAT: + printf("OP_ARYCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); + print_lv(mrb, irep, c, RAB); + break; + case OP_ARYPUSH: + printf("OP_ARYPUSH\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); + print_lv(mrb, irep, c, RAB); + break; + case OP_AREF: + printf("OP_AREF\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); + print_lv(mrb, irep, c, RAB); + break; + case OP_APOST: + printf("OP_APOST\tR%d\t%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); + print_lv(mrb, irep, c, RA); + break; + case OP_STRING: + { + mrb_value v = irep->pool[GETARG_Bx(c)]; + mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); + printf("OP_STRING\tR%d\tL(%d)\t; %s", GETARG_A(c), GETARG_Bx(c), RSTRING_PTR(s)); + } + print_lv(mrb, irep, c, RA); + break; + case OP_STRCAT: + printf("OP_STRCAT\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); + print_lv(mrb, irep, c, RAB); + break; + case OP_HASH: + printf("OP_HASH\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); + print_lv(mrb, irep, c, RAB); + break; + + case OP_OCLASS: + printf("OP_OCLASS\tR%d\t\t", GETARG_A(c)); + print_lv(mrb, irep, c, RA); + break; + case OP_CLASS: + printf("OP_CLASS\tR%d\t:%s", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); + print_lv(mrb, irep, c, RA); + break; + case OP_MODULE: + printf("OP_MODULE\tR%d\t:%s", GETARG_A(c), + mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); + print_lv(mrb, irep, c, RA); + break; + case OP_EXEC: + printf("OP_EXEC\tR%d\tI(%+d)", GETARG_A(c), GETARG_Bx(c)+1); + print_lv(mrb, irep, c, RA); + break; + case OP_SCLASS: + printf("OP_SCLASS\tR%d\tR%d\t", GETARG_A(c), GETARG_B(c)); + print_lv(mrb, irep, c, RAB); + break; + case OP_TCLASS: + printf("OP_TCLASS\tR%d\t\t", GETARG_A(c)); + print_lv(mrb, irep, c, RA); + break; + case OP_ERR: + { + mrb_value v = irep->pool[GETARG_Bx(c)]; + mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); + printf("OP_ERR\t%s\n", RSTRING_PTR(s)); + } + break; + case OP_EPUSH: + printf("OP_EPUSH\t:I(%+d)\n", GETARG_Bx(c)+1); + break; + case OP_ONERR: + printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c)); + break; + case OP_RESCUE: + printf("OP_RESCUE\tR%d\t\t", GETARG_A(c)); + print_lv(mrb, irep, c, RA); + break; + case OP_RAISE: + printf("OP_RAISE\tR%d\t\t", GETARG_A(c)); + print_lv(mrb, irep, c, RA); + break; + case OP_POPERR: + printf("OP_POPERR\t%d\t\t", GETARG_A(c)); + print_lv(mrb, irep, c, RA); + break; + case OP_EPOP: + printf("OP_EPOP\t%d\n", GETARG_A(c)); + break; + + default: + printf("OP_unknown %d\t%d\t%d\t%d\n", GET_OPCODE(c), + GETARG_A(c), GETARG_B(c), GETARG_C(c)); + break; + } + mrb_gc_arena_restore(mrb, ai); + } + printf("\n"); +#endif +} + +static void +codedump_recur(mrb_state *mrb, mrb_irep *irep) +{ + size_t i; + + codedump(mrb, irep); + for (i=0; i<irep->rlen; i++) { + codedump_recur(mrb, irep->reps[i]); + } +} + +void +mrb_codedump_all(mrb_state *mrb, struct RProc *proc) +{ + codedump_recur(mrb, proc->body.irep); +} diff --git a/src/codegen.c b/src/codegen.c deleted file mode 100644 index 03c752826..000000000 --- a/src/codegen.c +++ /dev/null @@ -1,3129 +0,0 @@ -/* -** codegen.c - mruby code generator -** -** See Copyright Notice in mruby.h -*/ - -#include <ctype.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include "mruby.h" -#include "mruby/compile.h" -#include "mruby/proc.h" -#include "mruby/numeric.h" -#include "mruby/string.h" -#include "mruby/debug.h" -#include "node.h" -#include "mruby/opcode.h" -#include "mruby/re.h" -#include "mrb_throw.h" - -typedef mrb_ast_node node; -typedef struct mrb_parser_state parser_state; - -enum looptype { - LOOP_NORMAL, - LOOP_BLOCK, - LOOP_FOR, - LOOP_BEGIN, - LOOP_RESCUE, -}; - -struct loopinfo { - enum looptype type; - int pc1, pc2, pc3, acc; - int ensure_level; - struct loopinfo *prev; -}; - -typedef struct scope { - mrb_state *mrb; - mrb_pool *mpool; - struct mrb_jmpbuf jmp; - - struct scope *prev; - - node *lv; - - int sp; - int pc; - int lastlabel; - int ainfo:15; - mrb_bool mscope:1; - - struct loopinfo *loop; - int ensure_level; - char const *filename; - uint16_t lineno; - - mrb_code *iseq; - uint16_t *lines; - int icapa; - - mrb_irep *irep; - size_t pcapa; - size_t scapa; - size_t rcapa; - - uint16_t nlocals; - uint16_t nregs; - int ai; - - int debug_start_pos; - uint16_t filename_index; - parser_state* parser; -} codegen_scope; - -static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv); -static void scope_finish(codegen_scope *s); -static struct loopinfo *loop_push(codegen_scope *s, enum looptype t); -static void loop_break(codegen_scope *s, node *tree); -static void loop_pop(codegen_scope *s, int val); - -static void gen_assignment(codegen_scope *s, node *node, int sp, int val); -static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val); - -static void codegen(codegen_scope *s, node *tree, int val); - -static void -codegen_error(codegen_scope *s, const char *message) -{ - if (!s) return; - while (s->prev) { - codegen_scope *tmp = s->prev; - mrb_pool_close(s->mpool); - s = tmp; - } -#ifdef ENABLE_STDIO - if (s->filename && s->lineno) { - fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message); - } - else { - fprintf(stderr, "codegen error: %s\n", message); - } -#endif - MRB_THROW(&s->jmp); -} - -static void* -codegen_palloc(codegen_scope *s, size_t len) -{ - void *p = mrb_pool_alloc(s->mpool, len); - - if (!p) codegen_error(s, "pool memory allocation"); - return p; -} - -static void* -codegen_malloc(codegen_scope *s, size_t len) -{ - void *p = mrb_malloc_simple(s->mrb, len); - - if (!p) codegen_error(s, "mrb_malloc"); - return p; -} - -static void* -codegen_realloc(codegen_scope *s, void *p, size_t len) -{ - p = mrb_realloc_simple(s->mrb, p, len); - - if (!p && len > 0) codegen_error(s, "mrb_realloc"); - return p; -} - -static int -new_label(codegen_scope *s) -{ - s->lastlabel = s->pc; - return s->pc; -} - -static inline int -genop(codegen_scope *s, mrb_code i) -{ - if (s->pc == s->icapa) { - s->icapa *= 2; - s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa); - if (s->lines) { - s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa); - s->irep->lines = s->lines; - } - } - s->iseq[s->pc] = i; - if (s->lines) { - s->lines[s->pc] = s->lineno; - } - return s->pc++; -} - -#define NOVAL 0 -#define VAL 1 - -static int -genop_peep(codegen_scope *s, mrb_code i, int val) -{ - /* peephole optimization */ - if (s->lastlabel != s->pc && s->pc > 0) { - mrb_code i0 = s->iseq[s->pc-1]; - int c1 = GET_OPCODE(i); - int c0 = GET_OPCODE(i0); - - switch (c1) { - case OP_MOVE: - if (GETARG_A(i) == GETARG_B(i)) { - /* skip useless OP_MOVE */ - return 0; - } - if (val) break; - switch (c0) { - case OP_MOVE: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0) && GETARG_A(i) >= s->nlocals) { - /* skip swapping OP_MOVE */ - return 0; - } - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)); - return 0; - } - break; - case OP_LOADI: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0)); - return 0; - } - break; - case OP_ARRAY: - case OP_HASH: - case OP_RANGE: - case OP_AREF: - case OP_GETUPVAR: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0)); - return 0; - } - break; - case OP_LOADSYM: - case OP_GETGLOBAL: - case OP_GETIV: - case OP_GETCV: - case OP_GETCONST: - case OP_GETSPECIAL: - case OP_LOADL: - case OP_STRING: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0)); - return 0; - } - break; - case OP_SCLASS: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0)); - return 0; - } - break; - case OP_LOADNIL: - case OP_LOADSELF: - case OP_LOADT: - case OP_LOADF: - case OP_OCLASS: - if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i)); - return 0; - } - break; - default: - break; - } - break; - case OP_SETIV: - case OP_SETCV: - case OP_SETCONST: - case OP_SETMCNST: - case OP_SETGLOBAL: - if (val) break; - if (c0 == OP_MOVE) { - if (GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i)); - return 0; - } - } - break; - case OP_SETUPVAR: - if (val) break; - if (c0 == OP_MOVE) { - if (GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i)); - return 0; - } - } - break; - case OP_EPOP: - if (c0 == OP_EPOP) { - s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i)); - return 0; - } - break; - case OP_POPERR: - if (c0 == OP_POPERR) { - s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i)); - return 0; - } - break; - case OP_RETURN: - switch (c0) { - case OP_RETURN: - return 0; - case OP_MOVE: - if (GETARG_A(i0) >= s->nlocals) { - s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL); - return 0; - } - break; - case OP_SETIV: - case OP_SETCV: - case OP_SETCONST: - case OP_SETMCNST: - case OP_SETUPVAR: - case OP_SETGLOBAL: - s->pc--; - genop_peep(s, i0, NOVAL); - i0 = s->iseq[s->pc-1]; - return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL)); -#if 0 - case OP_SEND: - if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0)); - return; - } - break; -#endif - default: - break; - } - break; - case OP_ADD: - case OP_SUB: - if (c0 == OP_LOADI) { - int c = GETARG_sBx(i0); - - if (c1 == OP_SUB) c = -c; - if (c > 127 || c < -127) break; - if (0 <= c) - s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c); - else - s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c); - return 0; - } - case OP_STRCAT: - if (c0 == OP_STRING) { - int i = GETARG_Bx(i0); - - if (mrb_type(s->irep->pool[i]) == MRB_TT_STRING && - RSTRING_LEN(s->irep->pool[i]) == 0) { - s->pc--; - return 0; - } - } - break; - case OP_JMPIF: - case OP_JMPNOT: - if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) { - s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i)); - return s->pc-1; - } - break; - default: - break; - } - } - return genop(s, i); -} - -static void -scope_error(codegen_scope *s) -{ - exit(EXIT_FAILURE); -} - -static inline void -dispatch(codegen_scope *s, int pc) -{ - int diff = s->pc - pc; - mrb_code i = s->iseq[pc]; - int c = GET_OPCODE(i); - - s->lastlabel = s->pc; - switch (c) { - case OP_JMP: - case OP_JMPIF: - case OP_JMPNOT: - case OP_ONERR: - break; - default: -#ifdef ENABLE_STDIO - fprintf(stderr, "bug: dispatch on non JMP op\n"); -#endif - scope_error(s); - break; - } - s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff); -} - -static void -dispatch_linked(codegen_scope *s, int pc) -{ - mrb_code i; - int pos; - - if (!pc) return; - for (;;) { - i = s->iseq[pc]; - pos = GETARG_sBx(i); - dispatch(s, pc); - if (!pos) break; - pc = pos; - } -} - -#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0) -static void -push_(codegen_scope *s) -{ - if (s->sp > 511) { - codegen_error(s, "too complex expression"); - } - s->sp++; - nregs_update; -} - -#define push() push_(s) -#define pop_(s) ((s)->sp--) -#define pop() pop_(s) -#define pop_n(n) (s->sp-=(n)) -#define cursp() (s->sp) - -static inline int -new_lit(codegen_scope *s, mrb_value val) -{ - size_t i; - mrb_value *pv; - - switch (mrb_type(val)) { - case MRB_TT_STRING: - for (i=0; i<s->irep->plen; i++) { - mrb_int len; - pv = &s->irep->pool[i]; - - if (mrb_type(*pv) != MRB_TT_STRING) continue; - if ((len = RSTRING_LEN(*pv)) != RSTRING_LEN(val)) continue; - if (memcmp(RSTRING_PTR(*pv), RSTRING_PTR(val), len) == 0) - return i; - } - break; - case MRB_TT_FLOAT: - for (i=0; i<s->irep->plen; i++) { - pv = &s->irep->pool[i]; - if (mrb_type(*pv) != MRB_TT_FLOAT) continue; - if (mrb_float(*pv) == mrb_float(val)) return i; - } - break; - case MRB_TT_FIXNUM: - for (i=0; i<s->irep->plen; i++) { - pv = &s->irep->pool[i]; - if (!mrb_fixnum_p(*pv)) continue; - if (mrb_fixnum(*pv) == mrb_fixnum(val)) return i; - } - break; - default: - /* should not happen */ - return 0; - } - - if (s->irep->plen == s->pcapa) { - s->pcapa *= 2; - s->irep->pool = (mrb_value *)codegen_realloc(s, s->irep->pool, sizeof(mrb_value)*s->pcapa); - } - - pv = &s->irep->pool[s->irep->plen]; - i = s->irep->plen++; - - switch (mrb_type(val)) { - case MRB_TT_STRING: - *pv = mrb_str_pool(s->mrb, val); - break; - - case MRB_TT_FLOAT: -#ifdef MRB_WORD_BOXING - *pv = mrb_float_pool(s->mrb, mrb_float(val)); - break; -#endif - case MRB_TT_FIXNUM: - *pv = val; - break; - - default: - /* should not happen */ - break; - } - return i; -} - -static inline int -new_msym(codegen_scope *s, mrb_sym sym) -{ - size_t i, len; - - mrb_assert(s->irep); - - len = s->irep->slen; - if (len > 256) len = 256; - for (i=0; i<len; i++) { - if (s->irep->syms[i] == sym) return i; - if (s->irep->syms[i] == 0) break; - } - if (i == 256) { - codegen_error(s, "too many symbols (max 256)"); - } - s->irep->syms[i] = sym; - if (i == s->irep->slen) s->irep->slen++; - return i; -} - -static inline int -new_sym(codegen_scope *s, mrb_sym sym) -{ - size_t i; - - for (i=0; i<s->irep->slen; i++) { - if (s->irep->syms[i] == sym) return i; - } - if (s->irep->slen > 125 && s->irep->slen < 256) { - s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*65536); - for (i = 0; i < 256 - s->irep->slen; i++) { - static const mrb_sym mrb_sym_zero = { 0 }; - s->irep->syms[i + s->irep->slen] = mrb_sym_zero; - } - s->irep->slen = 256; - } - s->irep->syms[s->irep->slen] = sym; - return s->irep->slen++; -} - -static int -node_len(node *tree) -{ - int n = 0; - - while (tree) { - n++; - tree = tree->cdr; - } - return n; -} - -#define sym(x) ((mrb_sym)(intptr_t)(x)) -#define lv_name(lv) sym((lv)->car) -static int -lv_idx(codegen_scope *s, mrb_sym id) -{ - node *lv = s->lv; - int n = 1; - - while (lv) { - if (lv_name(lv) == id) return n; - n++; - lv = lv->cdr; - } - return 0; -} - -static void -for_body(codegen_scope *s, node *tree) -{ - codegen_scope *prev = s; - int idx; - struct loopinfo *lp; - node *n2; - mrb_code c; - - /* generate receiver */ - codegen(s, tree->cdr->car, VAL); - /* generate loop-block */ - s = scope_new(s->mrb, s, NULL); - push(); /* push for a block parameter */ - - lp = loop_push(s, LOOP_FOR); - lp->pc1 = new_label(s); - - /* generate loop variable */ - n2 = tree->car; - if (n2->car && !n2->car->cdr && !n2->cdr) { - genop(s, MKOP_Ax(OP_ENTER, 0x40000)); - gen_assignment(s, n2->car->car, 1, NOVAL); - } - else { - genop(s, MKOP_Ax(OP_ENTER, 0x40000)); - gen_vmassignment(s, n2, 1, VAL); - } - codegen(s, tree->cdr->cdr->car, VAL); - pop(); - if (s->pc > 0) { - c = s->iseq[s->pc-1]; - if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - loop_pop(s, NOVAL); - scope_finish(s); - s = prev; - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK)); - pop(); - idx = new_msym(s, mrb_intern_lit(s->mrb, "each")); - genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0)); -} - -static int -lambda_body(codegen_scope *s, node *tree, int blk) -{ - mrb_code c; - codegen_scope *parent = s; - s = scope_new(s->mrb, s, tree->car); - s->mscope = !blk; - - if (blk) { - struct loopinfo *lp = loop_push(s, LOOP_BLOCK); - lp->pc1 = new_label(s); - } - tree = tree->cdr; - if (tree->car) { - mrb_aspec a; - int ma, oa, ra, pa, ka, kd, ba; - int pos, i; - node *n, *opt; - - ma = node_len(tree->car->car); - n = tree->car->car; - while (n) { - n = n->cdr; - } - oa = node_len(tree->car->cdr->car); - ra = tree->car->cdr->cdr->car ? 1 : 0; - pa = node_len(tree->car->cdr->cdr->cdr->car); - ka = kd = 0; - ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0; - - a = ((mrb_aspec)(ma & 0x1f) << 18) - | ((mrb_aspec)(oa & 0x1f) << 13) - | ((ra & 1) << 12) - | ((pa & 0x1f) << 7) - | ((ka & 0x1f) << 2) - | ((kd & 1)<< 1) - | (ba & 1); - s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */ - | ((ra & 1) << 5) - | (pa & 0x1f); - genop(s, MKOP_Ax(OP_ENTER, a)); - pos = new_label(s); - for (i=0; i<oa; i++) { - new_label(s); - genop(s, MKOP_sBx(OP_JMP, 0)); - } - if (oa > 0) { - genop(s, MKOP_sBx(OP_JMP, 0)); - } - opt = tree->car->cdr->car; - i = 0; - while (opt) { - int idx; - - dispatch(s, pos+i); - codegen(s, opt->car->cdr, VAL); - idx = lv_idx(s, (mrb_sym)(intptr_t)opt->car->car); - pop(); - genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL); - i++; - opt = opt->cdr; - } - if (oa > 0) { - dispatch(s, pos+i); - } - } - codegen(s, tree->cdr->car, VAL); - pop(); - if (s->pc > 0) { - c = s->iseq[s->pc-1]; - if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) { - if (s->nregs == 0) { - genop(s, MKOP_A(OP_LOADNIL, 0)); - genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); - } - else { - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - } - } - if (blk) { - loop_pop(s, NOVAL); - } - scope_finish(s); - return parent->irep->rlen - 1; -} - -static int -scope_body(codegen_scope *s, node *tree, int val) -{ - codegen_scope *scope = scope_new(s->mrb, s, tree->car); - - codegen(scope, tree->cdr, VAL); - if (!s->iseq) { - genop(scope, MKOP_A(OP_STOP, 0)); - } - else if (!val) { - genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); - } - else { - if (scope->nregs == 0) { - genop(scope, MKOP_A(OP_LOADNIL, 0)); - genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL)); - } - else { - genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL); - } - } - scope_finish(scope); - if (!s->irep) { - /* should not happen */ - return 0; - } - return s->irep->rlen - 1; -} - -static mrb_bool -nosplat(node *t) -{ - while (t) { - if ((intptr_t)t->car->car == NODE_SPLAT) return FALSE; - t = t->cdr; - } - return TRUE; -} - -static mrb_sym -attrsym(codegen_scope *s, mrb_sym a) -{ - const char *name; - mrb_int len; - char *name2; - - name = mrb_sym2name_len(s->mrb, a, &len); - name2 = (char *)codegen_palloc(s, - (size_t)len - + 1 /* '=' */ - + 1 /* '\0' */ - ); - mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); - memcpy(name2, name, (size_t)len); - name2[len] = '='; - name2[len+1] = '\0'; - - return mrb_intern(s->mrb, name2, len+1); -} - -static int -gen_values(codegen_scope *s, node *t, int val) -{ - int n = 0; - int is_splat; - - while (t) { - is_splat = (intptr_t)t->car->car == NODE_SPLAT; /* splat mode */ - if (n >= 127 || is_splat) { - if (val) { - pop_n(n); - genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n)); - push(); - codegen(s, t->car, VAL); - pop(); pop(); - if (is_splat) { - genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); - } - else { - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); - } - t = t->cdr; - while (t) { - push(); - codegen(s, t->car, VAL); - pop(); pop(); - if ((intptr_t)t->car->car == NODE_SPLAT) { - genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1)); - } - else { - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1)); - } - t = t->cdr; - } - } - else { - codegen(s, t->car->cdr, NOVAL); - t = t->cdr; - while (t) { - codegen(s, t->car, NOVAL); - t = t->cdr; - } - } - return -1; - } - /* normal (no splat) mode */ - codegen(s, t->car, val); - n++; - t = t->cdr; - } - return n; -} - -#define CALL_MAXARGS 127 - -static void -gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val) -{ - mrb_sym sym = name ? name : sym(tree->cdr->car); - int idx; - int n = 0, noop = 0, sendv = 0, blk = 0; - - codegen(s, tree->car, VAL); /* receiver */ - idx = new_msym(s, sym); - tree = tree->cdr->cdr->car; - if (tree) { - n = gen_values(s, tree->car, VAL); - if (n < 0) { - n = noop = sendv = 1; - push(); - } - } - if (sp) { - if (sendv) { - pop(); - genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp)); - push(); - } - else { - genop(s, MKOP_AB(OP_MOVE, cursp(), sp)); - push(); - n++; - } - } - if (tree && tree->cdr) { - noop = 1; - codegen(s, tree->cdr, VAL); - pop(); - } - else { - blk = cursp(); - } - push();pop(); - pop_n(n+1); - { - mrb_int len; - const char *name = mrb_sym2name_len(s->mrb, sym, &len); - - if (!noop && len == 1 && name[0] == '+') { - genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val); - } - else if (!noop && len == 1 && name[0] == '-') { - genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val); - } - else if (!noop && len == 1 && name[0] == '*') { - genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n)); - } - else if (!noop && len == 1 && name[0] == '/') { - genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n)); - } - else if (!noop && len == 1 && name[0] == '<') { - genop(s, MKOP_ABC(OP_LT, cursp(), idx, n)); - } - else if (!noop && len == 2 && name[0] == '<' && name[1] == '=') { - genop(s, MKOP_ABC(OP_LE, cursp(), idx, n)); - } - else if (!noop && len == 1 && name[0] == '>') { - genop(s, MKOP_ABC(OP_GT, cursp(), idx, n)); - } - else if (!noop && len == 2 && name[0] == '>' && name[1] == '=') { - genop(s, MKOP_ABC(OP_GE, cursp(), idx, n)); - } - else if (!noop && len == 2 && name[0] == '=' && name[1] == '=') { - genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n)); - } - else { - if (sendv) n = CALL_MAXARGS; - if (blk > 0) { /* no block */ - genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n)); - } - else { - genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n)); - } - } - } - if (val) { - push(); - } -} - -static void -gen_assignment(codegen_scope *s, node *node, int sp, int val) -{ - int idx; - int type = (intptr_t)node->car; - - node = node->cdr; - switch ((intptr_t)type) { - case NODE_GVAR: - idx = new_sym(s, sym(node)); - genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val); - break; - case NODE_LVAR: - idx = lv_idx(s, sym(node)); - if (idx > 0) { - if (idx != sp) { - genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val); - } - break; - } - else { /* upvar */ - int lv = 0; - codegen_scope *up = s->prev; - - while (up) { - idx = lv_idx(up, sym(node)); - if (idx > 0) { - genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val); - break; - } - lv++; - up = up->prev; - } - } - break; - case NODE_IVAR: - idx = new_sym(s, sym(node)); - genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val); - break; - case NODE_CVAR: - idx = new_sym(s, sym(node)); - genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val); - break; - case NODE_CONST: - idx = new_sym(s, sym(node)); - genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val); - break; - case NODE_COLON2: - idx = new_sym(s, sym(node->cdr)); - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL); - push(); - codegen(s, node->car, VAL); - pop_n(2); - genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val); - break; - - case NODE_CALL: - push(); - gen_call(s, node, attrsym(s, sym(node->cdr->car)), sp, NOVAL); - pop(); - if (val) { - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val); - } - break; - - default: -#ifdef ENABLE_STDIO - printf("unknown lhs %d\n", type); -#endif - break; - } - if (val) push(); -} - -static void -gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val) -{ - int n = 0, post = 0; - node *t, *p; - - if (tree->car) { /* pre */ - t = tree->car; - n = 0; - while (t) { - genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n)); - gen_assignment(s, t->car, cursp(), NOVAL); - n++; - t = t->cdr; - } - } - t = tree->cdr; - if (t) { - if (t->cdr) { /* post count */ - p = t->cdr->car; - while (p) { - post++; - p = p->cdr; - } - } - if (val) { - genop(s, MKOP_AB(OP_MOVE, cursp(), rhs)); - push(); - } - pop(); - genop(s, MKOP_ABC(OP_APOST, cursp(), n, post)); - n = 1; - if (t->car) { /* rest */ - gen_assignment(s, t->car, cursp(), NOVAL); - } - if (t->cdr && t->cdr->car) { - t = t->cdr->car; - while (t) { - gen_assignment(s, t->car, cursp()+n, NOVAL); - t = t->cdr; - n++; - } - } - } - else { - pop(); - } -} - -static void -gen_send_intern(codegen_scope *s) -{ - pop(); - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0)); - push(); -} -static void -gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val) -{ - if (val) { - int i = 0, j = 0; - - while (tree) { - switch ((intptr_t)tree->car->car) { - case NODE_STR: - if ((tree->cdr == NULL) && ((intptr_t)tree->car->cdr->cdr == 0)) - break; - /* fall through */ - case NODE_BEGIN: - codegen(s, tree->car, VAL); - ++j; - break; - - case NODE_LITERAL_DELIM: - if (j > 0) { - j = 0; - ++i; - if (sym) - gen_send_intern(s); - } - break; - } - if (j >= 2) { - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - j = 1; - } - tree = tree->cdr; - } - if (j > 0) { - ++i; - if (sym) - gen_send_intern(s); - } - pop_n(i); - genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i)); - push(); - } - else { - while (tree) { - switch ((intptr_t)tree->car->car) { - case NODE_BEGIN: case NODE_BLOCK: - codegen(s, tree->car, NOVAL); - } - tree = tree->cdr; - } - } -} - -static void -raise_error(codegen_scope *s, const char *msg) -{ - int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg)); - - genop(s, MKOP_ABx(OP_ERR, 1, idx)); -} - -static double -readint_float(codegen_scope *s, const char *p, int base) -{ - const char *e = p + strlen(p); - double f = 0; - int n; - - if (*p == '+') p++; - while (p < e) { - char c = *p; - c = tolower((unsigned char)c); - for (n=0; n<base; n++) { - if (mrb_digitmap[n] == c) { - f *= base; - f += n; - break; - } - } - if (n == base) { - codegen_error(s, "malformed readint input"); - } - p++; - } - return f; -} - -static mrb_int -readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow) -{ - const char *e = p + strlen(p); - mrb_int result = 0; - int n; - - if (*p == '+') p++; - while (p < e) { - char c = *p; - c = tolower((unsigned char)c); - for (n=0; n<base; n++) { - if (mrb_digitmap[n] == c) { - break; - } - } - if (n == base) { - codegen_error(s, "malformed readint input"); - } - - if(base > 0) { - if (neg) { - if ((MRB_INT_MIN + n)/base > result) { - *overflow = TRUE; - return 0; - } - result *= base; - result -= n; - } - else { - if ((MRB_INT_MAX - n)/base < result) { - *overflow = TRUE; - return 0; - } - result *= base; - result += n; - } - } - p++; - } - *overflow = FALSE; - return result; -} - -static void -codegen(codegen_scope *s, node *tree, int val) -{ - int nt; - - if (!tree) return; - - if (s->irep && s->pc > 0 && s->filename_index != tree->filename_index) { - s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); - mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc); - s->debug_start_pos = s->pc; - s->filename_index = tree->filename_index; - s->filename = mrb_parser_get_filename(s->parser, tree->filename_index); - } - - nt = (intptr_t)tree->car; - s->lineno = tree->lineno; - tree = tree->cdr; - switch (nt) { - case NODE_BEGIN: - if (val && !tree) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - while (tree) { - codegen(s, tree->car, tree->cdr ? NOVAL : val); - tree = tree->cdr; - } - break; - - case NODE_RESCUE: - { - int onerr, noexc, exend, pos1, pos2, tmp; - struct loopinfo *lp; - - onerr = genop(s, MKOP_Bx(OP_ONERR, 0)); - lp = loop_push(s, LOOP_BEGIN); - lp->pc1 = onerr; - if (tree->car) { - codegen(s, tree->car, val); - if (val) pop(); - } - lp->type = LOOP_RESCUE; - noexc = genop(s, MKOP_Bx(OP_JMP, 0)); - dispatch(s, onerr); - tree = tree->cdr; - exend = 0; - pos1 = 0; - if (tree->car) { - node *n2 = tree->car; - int exc = cursp(); - - genop(s, MKOP_A(OP_RESCUE, exc)); - push(); - while (n2) { - node *n3 = n2->car; - node *n4 = n3->car; - - if (pos1) dispatch(s, pos1); - pos2 = 0; - do { - if (n4) { - codegen(s, n4->car, VAL); - } - else { - genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError")))); - push(); - } - genop(s, MKOP_AB(OP_MOVE, cursp(), exc)); - pop(); - if (n4 && n4->car && (intptr_t)n4->car->car == NODE_SPLAT) { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1)); - } - else { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); - } - tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); - pos2 = tmp; - if (n4) { - n4 = n4->cdr; - } - } while (n4); - pos1 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch_linked(s, pos2); - - pop(); - if (n3->cdr->car) { - gen_assignment(s, n3->cdr->car, exc, NOVAL); - } - if (n3->cdr->cdr->car) { - codegen(s, n3->cdr->cdr->car, val); - if (val) pop(); - } - tmp = genop(s, MKOP_sBx(OP_JMP, exend)); - exend = tmp; - n2 = n2->cdr; - push(); - } - if (pos1) { - dispatch(s, pos1); - genop(s, MKOP_A(OP_RAISE, exc)); - } - } - pop(); - tree = tree->cdr; - dispatch(s, noexc); - genop(s, MKOP_A(OP_POPERR, 1)); - if (tree->car) { - codegen(s, tree->car, val); - } - else if (val) { - push(); - } - dispatch_linked(s, exend); - loop_pop(s, NOVAL); - } - break; - - case NODE_ENSURE: - { - int idx; - int epush = s->pc; - - genop(s, MKOP_Bx(OP_EPUSH, 0)); - s->ensure_level++; - codegen(s, tree->car, val); - idx = scope_body(s, tree->cdr, NOVAL); - s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx); - s->ensure_level--; - genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL); - } - break; - - case NODE_LAMBDA: - { - int idx = lambda_body(s, tree, 1); - - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA)); - push(); - } - break; - - case NODE_BLOCK: - { - int idx = lambda_body(s, tree, 1); - - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK)); - push(); - } - break; - - case NODE_IF: - { - int pos1, pos2; - node *e = tree->cdr->cdr->car; - - codegen(s, tree->car, VAL); - pop(); - pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL); - - codegen(s, tree->cdr->car, val); - if (val && !(tree->cdr->car)) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - if (e) { - if (val) pop(); - pos2 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch(s, pos1); - codegen(s, e, val); - dispatch(s, pos2); - } - else { - if (val) { - pop(); - pos2 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch(s, pos1); - genop(s, MKOP_A(OP_LOADNIL, cursp())); - dispatch(s, pos2); - push(); - } - else { - dispatch(s, pos1); - } - } - } - break; - - case NODE_AND: - { - int pos; - - codegen(s, tree->car, VAL); - pop(); - pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0)); - codegen(s, tree->cdr, val); - dispatch(s, pos); - } - break; - - case NODE_OR: - { - int pos; - - codegen(s, tree->car, VAL); - pop(); - pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0)); - codegen(s, tree->cdr, val); - dispatch(s, pos); - } - break; - - case NODE_WHILE: - { - struct loopinfo *lp = loop_push(s, LOOP_NORMAL); - - lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0)); - lp->pc2 = new_label(s); - codegen(s, tree->cdr, NOVAL); - dispatch(s, lp->pc1); - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc)); - - loop_pop(s, val); - } - break; - - case NODE_UNTIL: - { - struct loopinfo *lp = loop_push(s, LOOP_NORMAL); - - lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0)); - lp->pc2 = new_label(s); - codegen(s, tree->cdr, NOVAL); - dispatch(s, lp->pc1); - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc)); - - loop_pop(s, val); - } - break; - - case NODE_FOR: - for_body(s, tree); - if (val) push(); - break; - - case NODE_CASE: - { - int head = 0; - int pos1, pos2, pos3, tmp; - node *n; - - pos3 = 0; - if (tree->car) { - head = cursp(); - codegen(s, tree->car, VAL); - } - tree = tree->cdr; - while (tree) { - n = tree->car->car; - pos1 = pos2 = 0; - while (n) { - codegen(s, n->car, VAL); - if (head) { - genop(s, MKOP_AB(OP_MOVE, cursp(), head)); - pop(); - if ((intptr_t)n->car->car == NODE_SPLAT) { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1)); - } - else { - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1)); - } - } - else { - pop(); - } - tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2)); - pos2 = tmp; - n = n->cdr; - } - if (tree->car->car) { - pos1 = genop(s, MKOP_sBx(OP_JMP, 0)); - dispatch_linked(s, pos2); - } - codegen(s, tree->car->cdr, val); - if (val) pop(); - tmp = genop(s, MKOP_sBx(OP_JMP, pos3)); - pos3 = tmp; - if (pos1) dispatch(s, pos1); - tree = tree->cdr; - } - if (val) { - int pos = cursp(); - genop(s, MKOP_A(OP_LOADNIL, cursp())); - if (pos3) dispatch_linked(s, pos3); - pop(); - genop(s, MKOP_AB(OP_MOVE, cursp(), pos)); - push(); - } - else if (pos3) { - dispatch_linked(s, pos3); - } - } - break; - - case NODE_SCOPE: - scope_body(s, tree, NOVAL); - break; - - case NODE_FCALL: - case NODE_CALL: - gen_call(s, tree, 0, 0, val); - break; - - case NODE_DOT2: - codegen(s, tree->car, val); - codegen(s, tree->cdr, val); - if (val) { - pop(); pop(); - genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE)); - push(); - } - break; - - case NODE_DOT3: - codegen(s, tree->car, val); - codegen(s, tree->cdr, val); - if (val) { - pop(); pop(); - genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE)); - push(); - } - break; - - case NODE_COLON2: - { - int sym = new_sym(s, sym(tree->cdr)); - - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - if (val) push(); - } - break; - - case NODE_COLON3: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - if (val) push(); - } - break; - - case NODE_ARRAY: - { - int n; - - n = gen_values(s, tree, val); - if (n >= 0) { - if (val) { - pop_n(n); - genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n)); - push(); - } - } - else if (val) { - push(); - } - } - break; - - case NODE_HASH: - { - int len = 0; - mrb_bool update = FALSE; - - while (tree) { - codegen(s, tree->car->car, val); - codegen(s, tree->car->cdr, val); - len++; - tree = tree->cdr; - if (val && len == 126) { - pop_n(len*2); - genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); - if (update) { - pop(); - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1)); - } - push(); - update = TRUE; - len = 0; - } - } - if (val) { - pop_n(len*2); - genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len)); - if (update) { - pop(); - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1)); - } - push(); - } - } - break; - - case NODE_SPLAT: - codegen(s, tree, VAL); - break; - - case NODE_ASGN: - codegen(s, tree->cdr, VAL); - pop(); - gen_assignment(s, tree->car, cursp(), val); - break; - - case NODE_MASGN: - { - int len = 0, n = 0, post = 0; - node *t = tree->cdr, *p; - int rhs = cursp(); - - if ((intptr_t)t->car == NODE_ARRAY && nosplat(t->cdr)) { - /* fixed rhs */ - t = t->cdr; - while (t) { - codegen(s, t->car, VAL); - len++; - t = t->cdr; - } - tree = tree->car; - if (tree->car) { /* pre */ - t = tree->car; - n = 0; - while (t) { - gen_assignment(s, t->car, rhs+n, NOVAL); - n++; - t = t->cdr; - } - } - t = tree->cdr; - if (t) { - if (t->cdr) { /* post count */ - p = t->cdr->car; - while (p) { - post++; - p = p->cdr; - } - } - if (t->car) { /* rest (len - pre - post) */ - int rn = len - post - n; - - genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn)); - gen_assignment(s, t->car, cursp(), NOVAL); - n += rn; - } - if (t->cdr && t->cdr->car) { - t = t->cdr->car; - while (n<len) { - gen_assignment(s, t->car, rhs+n, NOVAL); - t = t->cdr; - n++; - } - } - } - pop_n(len); - if (val) { - genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len)); - push(); - } - } - else { - /* variable rhs */ - codegen(s, t, VAL); - gen_vmassignment(s, tree->car, rhs, val); - } - } - break; - - case NODE_OP_ASGN: - { - mrb_sym sym = sym(tree->cdr->car); - mrb_int len; - const char *name = mrb_sym2name_len(s->mrb, sym, &len); - int idx; - - codegen(s, tree->car, VAL); - if (len == 2 && - ((name[0] == '|' && name[1] == '|') || - (name[0] == '&' && name[1] == '&'))) { - int pos; - - pop(); - pos = genop_peep(s, MKOP_AsBx(name[0] == '|' ? OP_JMPIF : OP_JMPNOT, cursp(), 0), NOVAL); - codegen(s, tree->cdr->cdr->car, VAL); - pop(); - gen_assignment(s, tree->car, cursp(), val); - dispatch(s, pos); - break; - } - codegen(s, tree->cdr->cdr->car, VAL); - push(); pop(); - pop(); pop(); - - idx = new_msym(s, sym); - if (len == 1 && name[0] == '+') { - genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val); - } - else if (len == 1 && name[0] == '-') { - genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val); - } - else if (len == 1 && name[0] == '*') { - genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1)); - } - else if (len == 1 && name[0] == '/') { - genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1)); - } - else if (len == 1 && name[0] == '<') { - genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1)); - } - else if (len == 2 && name[0] == '<' && name[1] == '=') { - genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1)); - } - else if (len == 1 && name[0] == '>') { - genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1)); - } - else if (len == 2 && name[0] == '>' && name[1] == '=') { - genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1)); - } - else { - genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1)); - } - } - gen_assignment(s, tree->car, cursp(), val); - break; - - case NODE_SUPER: - { - int n = 0, noop = 0, sendv = 0; - - push(); /* room for receiver */ - if (tree) { - node *args = tree->car; - if (args) { - n = gen_values(s, args, VAL); - if (n < 0) { - n = noop = sendv = 1; - push(); - } - } - } - if (tree && tree->cdr) { - codegen(s, tree->cdr, VAL); - pop(); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); pop(); - } - pop_n(n+1); - if (sendv) n = CALL_MAXARGS; - genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n)); - if (val) push(); - } - break; - - case NODE_ZSUPER: - { - codegen_scope *s2 = s; - int lv = 0, ainfo = 0; - - push(); /* room for receiver */ - while (!s2->mscope) { - lv++; - s2 = s2->prev; - if (!s2) break; - } - if (s2) ainfo = s2->ainfo; - genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf))); - push(); push(); pop(); /* ARGARY pushes two values */ - if (tree && tree->cdr) { - codegen(s, tree->cdr, VAL); - pop(); - } - pop(); pop(); - genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS)); - if (val) push(); - } - break; - - case NODE_RETURN: - if (tree) { - codegen(s, tree, VAL); - pop(); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - } - if (s->loop) { - genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN)); - } - else { - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - if (val) push(); - break; - - case NODE_YIELD: - { - codegen_scope *s2 = s; - int lv = 0, ainfo = 0; - int n = 0, sendv = 0; - - while (!s2->mscope) { - lv++; - s2 = s2->prev; - if (!s2) break; - } - if (s2) ainfo = s2->ainfo; - genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf))); - push(); - if (tree) { - n = gen_values(s, tree, VAL); - if (n < 0) { - n = sendv = 1; - push(); - } - } - pop_n(n+1); - if (sendv) n = CALL_MAXARGS; - genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n)); - if (val) push(); - } - break; - - case NODE_BREAK: - loop_break(s, tree); - if (val) push(); - break; - - case NODE_NEXT: - if (!s->loop) { - raise_error(s, "unexpected next"); - } - else if (s->loop->type == LOOP_NORMAL) { - if (s->ensure_level > s->loop->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); - } - codegen(s, tree, NOVAL); - genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc)); - } - else { - if (tree) { - codegen(s, tree, VAL); - pop(); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - } - genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL); - } - if (val) push(); - break; - - case NODE_REDO: - if (!s->loop) { - raise_error(s, "unexpected redo"); - } - else { - if (s->ensure_level > s->loop->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); - } - genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc)); - } - break; - - case NODE_RETRY: - { - const char *msg = "unexpected retry"; - - if (!s->loop) { - raise_error(s, msg); - } - else { - struct loopinfo *lp = s->loop; - int n = 0; - - while (lp && lp->type != LOOP_RESCUE) { - if (lp->type == LOOP_BEGIN) { - n++; - } - lp = lp->prev; - } - if (!lp) { - raise_error(s, msg); - } - else { - if (n > 0) { - while (n--) { - genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); - } - } - if (s->ensure_level > lp->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL); - } - genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc)); - } - } - } - break; - - case NODE_LVAR: - if (val) { - int idx = lv_idx(s, sym(tree)); - - if (idx > 0) { - genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL); - } - else { - int lv = 0; - codegen_scope *up = s->prev; - - while (up) { - idx = lv_idx(up, sym(tree)); - if (idx > 0) { - genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv)); - break; - } - lv++; - up = up->prev; - } - } - push(); - } - break; - - case NODE_GVAR: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); - push(); - } - break; - - case NODE_IVAR: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETIV, cursp(), sym)); - push(); - } - break; - - case NODE_CVAR: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETCV, cursp(), sym)); - push(); - } - break; - - case NODE_CONST: - { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym)); - push(); - } - break; - - case NODE_DEFINED: - codegen(s, tree, VAL); - break; - - case NODE_BACK_REF: - { - char buf[2] = { '$' }; - mrb_value str; - int sym; - - buf[1] = (char)(intptr_t)tree; - str = mrb_str_new(s->mrb, buf, 2); - sym = new_sym(s, mrb_intern_str(s->mrb, str)); - genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); - push(); - } - break; - - case NODE_NTH_REF: - { - int sym; - mrb_state *mrb = s->mrb; - mrb_value fix = mrb_fixnum_value((intptr_t)tree); - mrb_value str = mrb_str_buf_new(mrb, 4); - - mrb_str_cat_lit(mrb, str, "$"); - mrb_str_cat_str(mrb, str, mrb_fixnum_to_str(mrb, fix, 10)); - sym = new_sym(s, mrb_intern_str(mrb, str)); - genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym)); - push(); - } - break; - - case NODE_ARG: - /* should not happen */ - break; - - case NODE_BLOCK_ARG: - codegen(s, tree, VAL); - break; - - case NODE_INT: - if (val) { - char *p = (char*)tree->car; - int base = (intptr_t)tree->cdr->car; - mrb_int i; - mrb_code co; - mrb_bool overflow; - - i = readint_mrb_int(s, p, base, FALSE, &overflow); - if (overflow) { - double f = readint_float(s, p, base); - int off = new_lit(s, mrb_float_value(s->mrb, f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - } - else { - if (i < MAXARG_sBx && i > -MAXARG_sBx) { - co = MKOP_AsBx(OP_LOADI, cursp(), i); - } - else { - int off = new_lit(s, mrb_fixnum_value(i)); - co = MKOP_ABx(OP_LOADL, cursp(), off); - } - genop(s, co); - } - push(); - } - break; - - case NODE_FLOAT: - if (val) { - char *p = (char*)tree; - mrb_float f = str_to_mrb_float(p); - int off = new_lit(s, mrb_float_value(s->mrb, f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - push(); - } - break; - - case NODE_NEGATE: - { - nt = (intptr_t)tree->car; - tree = tree->cdr; - switch (nt) { - case NODE_FLOAT: - { - char *p = (char*)tree; - mrb_float f = str_to_mrb_float(p); - int off = new_lit(s, mrb_float_value(s->mrb, -f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - push(); - } - break; - - case NODE_INT: - { - char *p = (char*)tree->car; - int base = (intptr_t)tree->cdr->car; - mrb_int i; - mrb_code co; - mrb_bool overflow; - - i = readint_mrb_int(s, p, base, TRUE, &overflow); - if (overflow) { - double f = readint_float(s, p, base); - int off = new_lit(s, mrb_float_value(s->mrb, -f)); - - genop(s, MKOP_ABx(OP_LOADL, cursp(), off)); - } - else { - if (i < MAXARG_sBx && i > -MAXARG_sBx) { - co = MKOP_AsBx(OP_LOADI, cursp(), i); - } - else { - int off = new_lit(s, mrb_fixnum_value(i)); - co = MKOP_ABx(OP_LOADL, cursp(), off); - } - genop(s, co); - } - push(); - } - break; - - default: - { - int sym = new_msym(s, mrb_intern_lit(s->mrb, "-")); - - genop(s, MKOP_ABx(OP_LOADI, cursp(), 0)); - push(); - codegen(s, tree, VAL); - pop(); pop(); - genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2)); - } - break; - } - } - break; - - case NODE_STR: - if (val) { - char *p = (char*)tree->car; - size_t len = (intptr_t)tree->cdr; - int ai = mrb_gc_arena_save(s->mrb); - int off = new_lit(s, mrb_str_new(s->mrb, p, len)); - - mrb_gc_arena_restore(s->mrb, ai); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - push(); - } - break; - - case NODE_HEREDOC: - tree = ((struct mrb_parser_heredoc_info *)tree)->doc; - /* fall through */ - case NODE_DSTR: - if (val) { - node *n = tree; - - codegen(s, n->car, VAL); - n = n->cdr; - while (n) { - codegen(s, n->car, VAL); - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - n = n->cdr; - } - } - else { - node *n = tree; - - while (n) { - if ((intptr_t)n->car->car != NODE_STR) { - codegen(s, n->car, NOVAL); - } - n = n->cdr; - } - } - break; - - case NODE_WORDS: - gen_literal_array(s, tree, FALSE, val); - break; - - case NODE_SYMBOLS: - gen_literal_array(s, tree, TRUE, val); - break; - - case NODE_DXSTR: - { - node *n; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); - - if (val == NOVAL) { push(); } - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - codegen(s, tree->car, VAL); - n = tree->cdr; - while (n) { - if ((intptr_t)n->car->car == NODE_XSTR) { - n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR; - mrb_assert(!n->cdr); /* must be the end */ - } - codegen(s, n->car, VAL); - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - n = n->cdr; - } - pop(); - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); - if (val == NOVAL) { pop(); } - else { push(); } - mrb_gc_arena_restore(s->mrb, ai); - } - break; - - case NODE_XSTR: - { - char *p = (char*)tree->car; - size_t len = (intptr_t)tree->cdr; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel")); - int off = new_lit(s, mrb_str_new(s->mrb, p, len)); - - if (val == NOVAL) { push(); } - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "`")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1)); - if (val == NOVAL) { pop(); } - else { push(); } - mrb_gc_arena_restore(s->mrb, ai); - } - break; - - case NODE_REGX: - if (val) { - char *p1 = (char*)tree->car; - char *p2 = (char*)tree->cdr; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); - int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1)); - int argc = 1; - - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - if (p2) { - push(); - off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - argc++; - pop(); - } - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc)); - mrb_gc_arena_restore(s->mrb, ai); - push(); - } - break; - - case NODE_DREGX: - if (val) { - node *n = tree->car; - int ai = mrb_gc_arena_save(s->mrb); - int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS)); - int argc = 1; - int off; - char *p; - - genop(s, MKOP_A(OP_OCLASS, cursp())); - genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym)); - push(); - codegen(s, n->car, VAL); - n = n->cdr; - while (n) { - codegen(s, n->car, VAL); - pop(); pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - push(); - n = n->cdr; - } - n = tree->cdr->cdr; - if (n->car) { - p = (char*)n->car; - off = new_lit(s, mrb_str_new_cstr(s->mrb, p)); - codegen(s, tree->car, VAL); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - pop(); - genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL); - } - if (n->cdr) { - char *p2 = (char*)n->cdr; - int off; - - push(); - off = new_lit(s, mrb_str_new_cstr(s->mrb, p2)); - genop(s, MKOP_ABx(OP_STRING, cursp(), off)); - argc++; - pop(); - } - pop(); - sym = new_sym(s, mrb_intern_lit(s->mrb, "compile")); - genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc)); - mrb_gc_arena_restore(s->mrb, ai); - push(); - } - else { - node *n = tree->car; - - while (n) { - if ((intptr_t)n->car->car != NODE_STR) { - codegen(s, n->car, NOVAL); - } - n = n->cdr; - } - } - break; - - case NODE_SYM: - if (val) { - int sym = new_sym(s, sym(tree)); - - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); - push(); - } - break; - - case NODE_DSYM: - codegen(s, tree, val); - if (val) { - gen_send_intern(s); - } - break; - - case NODE_SELF: - if (val) { - genop(s, MKOP_A(OP_LOADSELF, cursp())); - push(); - } - break; - - case NODE_NIL: - if (val) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - break; - - case NODE_TRUE: - if (val) { - genop(s, MKOP_A(OP_LOADT, cursp())); - push(); - } - break; - - case NODE_FALSE: - if (val) { - genop(s, MKOP_A(OP_LOADF, cursp())); - push(); - } - break; - - case NODE_ALIAS: - { - int a = new_msym(s, sym(tree->car)); - int b = new_msym(s, sym(tree->cdr)); - int c = new_msym(s, mrb_intern_lit(s->mrb,"alias_method")); - - genop(s, MKOP_A(OP_TCLASS, cursp())); - push(); - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a)); - push(); - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b)); - push(); - genop(s, MKOP_A(OP_LOADNIL, cursp())); - pop_n(3); - genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2)); - if (val) { - push(); - } - } - break; - - case NODE_UNDEF: - { - int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method")); - int num = 0; - node *t = tree; - - genop(s, MKOP_A(OP_TCLASS, cursp())); - push(); - while (t) { - int symbol = new_msym(s, sym(t->car)); - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol)); - push(); - t = t->cdr; - num++; - } - pop_n(num + 1); - genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num)); - if (val) { - push(); - } - } - break; - - case NODE_CLASS: - { - int idx; - - if (tree->car->car == (node*)0) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - else if (tree->car->car == (node*)1) { - genop(s, MKOP_A(OP_OCLASS, cursp())); - push(); - } - else { - codegen(s, tree->car->car, VAL); - } - if (tree->cdr->car) { - codegen(s, tree->cdr->car, VAL); - } - else { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - pop(); pop(); - idx = new_msym(s, sym(tree->car->cdr)); - genop(s, MKOP_AB(OP_CLASS, cursp(), idx)); - idx = scope_body(s, tree->cdr->cdr->car, val); - genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); - if (val) { - push(); - } - } - break; - - case NODE_MODULE: - { - int idx; - - if (tree->car->car == (node*)0) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - push(); - } - else if (tree->car->car == (node*)1) { - genop(s, MKOP_A(OP_OCLASS, cursp())); - push(); - } - else { - codegen(s, tree->car->car, VAL); - } - pop(); - idx = new_msym(s, sym(tree->car->cdr)); - genop(s, MKOP_AB(OP_MODULE, cursp(), idx)); - idx = scope_body(s, tree->cdr->car, val); - genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); - if (val) { - push(); - } - } - break; - - case NODE_SCLASS: - { - int idx; - - codegen(s, tree->car, VAL); - pop(); - genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp())); - idx = scope_body(s, tree->cdr->car, val); - genop(s, MKOP_ABx(OP_EXEC, cursp(), idx)); - if (val) { - push(); - } - } - break; - - case NODE_DEF: - { - int sym = new_msym(s, sym(tree->car)); - int idx = lambda_body(s, tree->cdr, 0); - - genop(s, MKOP_A(OP_TCLASS, cursp())); - push(); - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD)); - push(); pop(); - pop(); - genop(s, MKOP_AB(OP_METHOD, cursp(), sym)); - if (val) { - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); - push(); - } - } - break; - - case NODE_SDEF: - { - node *recv = tree->car; - int sym = new_msym(s, sym(tree->cdr->car)); - int idx = lambda_body(s, tree->cdr->cdr, 0); - - codegen(s, recv, VAL); - pop(); - genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp())); - push(); - genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD)); - pop(); - genop(s, MKOP_AB(OP_METHOD, cursp(), sym)); - if (val) { - genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym)); - push(); - } - } - break; - - case NODE_POSTEXE: - codegen(s, tree, NOVAL); - break; - - default: - break; - } -} - -static void -scope_add_irep(codegen_scope *s, mrb_irep *irep) -{ - if (s->irep == NULL) { - s->irep = irep; - return; - } - if (s->irep->rlen == s->rcapa) { - s->rcapa *= 2; - s->irep->reps = (mrb_irep**)codegen_realloc(s, s->irep->reps, sizeof(mrb_irep*)*s->rcapa); - } - s->irep->reps[s->irep->rlen] = irep; - s->irep->rlen++; -} - -static codegen_scope* -scope_new(mrb_state *mrb, codegen_scope *prev, node *lv) -{ - static const codegen_scope codegen_scope_zero = { 0 }; - mrb_pool *pool = mrb_pool_open(mrb); - codegen_scope *p = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope)); - - if (!p) return 0; - *p = codegen_scope_zero; - p->mrb = mrb; - p->mpool = pool; - if (!prev) return p; - p->prev = prev; - p->ainfo = -1; - p->mscope = 0; - - p->irep = mrb_add_irep(mrb); - scope_add_irep(prev, p->irep); - - p->rcapa = 8; - p->irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*p->rcapa); - - p->icapa = 1024; - p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa); - p->irep->iseq = p->iseq; - - p->pcapa = 32; - p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa); - p->irep->plen = 0; - - p->scapa = 256; - p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa); - p->irep->slen = 0; - - p->lv = lv; - p->sp += node_len(lv)+1; /* add self */ - p->nlocals = p->sp; - if (lv) { - node *n = lv; - size_t i = 0; - - p->irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (p->nlocals - 1)); - for (i=0, n=lv; n; i++,n=n->cdr) { - p->irep->lv[i].name = lv_name(n); - if (lv_name(n)) { - p->irep->lv[i].r = lv_idx(p, lv_name(n)); - } - else { - p->irep->lv[i].r = 0; - } - } - mrb_assert(i + 1 == p->nlocals); - } - p->ai = mrb_gc_arena_save(mrb); - - p->filename = prev->filename; - if (p->filename) { - p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa); - } - p->lineno = prev->lineno; - - /* debug setting */ - p->debug_start_pos = 0; - if (p->filename) { - mrb_debug_info_alloc(mrb, p->irep); - p->irep->filename = p->filename; - p->irep->lines = p->lines; - } - else { - p->irep->debug_info = NULL; - } - p->parser = prev->parser; - p->filename_index = prev->filename_index; - - return p; -} - -static void -scope_finish(codegen_scope *s) -{ - mrb_state *mrb = s->mrb; - mrb_irep *irep = s->irep; - size_t fname_len; - char *fname; - - irep->flags = 0; - if (s->iseq) { - irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc); - irep->ilen = s->pc; - if (s->lines) { - irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc); - } - else { - irep->lines = 0; - } - } - irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen); - irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen); - irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen); - if (s->filename) { - s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index); - mrb_debug_info_append_file(mrb, s->irep, s->debug_start_pos, s->pc); - - fname_len = strlen(s->filename); - fname = (char*)codegen_malloc(s, fname_len + 1); - memcpy(fname, s->filename, fname_len); - fname[fname_len] = '\0'; - irep->filename = fname; - } - - irep->nlocals = s->nlocals; - irep->nregs = s->nregs; - - mrb_gc_arena_restore(mrb, s->ai); - mrb_pool_close(s->mpool); -} - -static struct loopinfo* -loop_push(codegen_scope *s, enum looptype t) -{ - struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo)); - - p->type = t; - p->pc1 = p->pc2 = p->pc3 = 0; - p->prev = s->loop; - p->ensure_level = s->ensure_level; - p->acc = cursp(); - s->loop = p; - - return p; -} - -static void -loop_break(codegen_scope *s, node *tree) -{ - if (!s->loop) { - codegen(s, tree, NOVAL); - raise_error(s, "unexpected break"); - } - else { - struct loopinfo *loop; - - if (tree) { - codegen(s, tree, VAL); - pop(); - } - - loop = s->loop; - while (loop->type == LOOP_BEGIN) { - genop_peep(s, MKOP_A(OP_POPERR, 1), NOVAL); - loop = loop->prev; - } - while (loop->type == LOOP_RESCUE) { - loop = loop->prev; - } - if (loop->type == LOOP_NORMAL) { - int tmp; - - if (s->ensure_level > s->loop->ensure_level) { - genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL); - } - if (tree) { - genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL); - } - tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3)); - loop->pc3 = tmp; - } - else { - genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK)); - } - } -} - -static void -loop_pop(codegen_scope *s, int val) -{ - if (val) { - genop(s, MKOP_A(OP_LOADNIL, cursp())); - } - dispatch_linked(s, s->loop->pc3); - s->loop = s->loop->prev; - if (val) push(); -} - -#ifdef ENABLE_STDIO -static int -print_r(mrb_state *mrb, mrb_irep *irep, size_t n, int pre) -{ - size_t i; - - if (n == 0) return 0; - - for (i=0; i+1<irep->nlocals; i++) { - if (irep->lv[i].r == n) { - mrb_sym sym = irep->lv[i].name; - if (pre) printf(" "); - printf("R%d:%s", (int)n, mrb_sym2name(mrb, sym)); - return 1; - } - } - return 0; -} - -#define RA 1 -#define RB 2 -#define RAB 3 - -static void -print_lv(mrb_state *mrb, mrb_irep *irep, mrb_code c, int r) -{ - int pre = 0; - - if (!irep->lv - || ((!(r & RA) || GETARG_A(c) >= irep->nlocals) - && (!(r & RB) || GETARG_B(c) >= irep->nlocals))) { - printf("\n"); - return; - } - printf("\t; "); - if (r & RA) { - pre = print_r(mrb, irep, GETARG_A(c), 0); - } - if (r & RB) { - print_r(mrb, irep, GETARG_B(c), pre); - } - printf("\n"); -} -#endif - -static void -codedump(mrb_state *mrb, mrb_irep *irep) -{ -#ifdef ENABLE_STDIO - int i; - int ai; - mrb_code c; - const char *file = NULL, *next_file; - int32_t line; - - if (!irep) return; - printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d\n", 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", 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", 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", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_LOADSELF: - printf("OP_LOADSELF\tR%d\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_LOADT: - printf("OP_LOADT\tR%d\t", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_LOADF: - printf("OP_LOADF\tR%d\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", - 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", - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), - GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_GETMCNST: - printf("OP_GETMCNST\tR%d\tR%d::%s", GETARG_A(c), GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); - print_lv(mrb, irep, c, RAB); - break; - case OP_SETMCNST: - printf("OP_SETMCNST\tR%d::%s\tR%d", GETARG_A(c)+1, - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), - GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_GETIV: - printf("OP_GETIV\tR%d\t%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); - print_lv(mrb, irep, c, RA); - break; - case OP_SETIV: - printf("OP_SETIV\t%s\tR%d", - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), - GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_GETUPVAR: - printf("OP_GETUPVAR\tR%d\t%d\t%d", - GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_SETUPVAR: - printf("OP_SETUPVAR\tR%d\t%d\t%d", - GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_GETCV: - printf("OP_GETCV\tR%d\t%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)])); - print_lv(mrb, irep, c, RA); - break; - case OP_SETCV: - printf("OP_SETCV\t%s\tR%d", - mrb_sym2name(mrb, irep->syms[GETARG_Bx(c)]), - GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_JMP: - printf("OP_JMP\t\t%03d\n", i+GETARG_sBx(c)); - break; - case OP_JMPIF: - printf("OP_JMPIF\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c)); - break; - case OP_JMPNOT: - printf("OP_JMPNOT\tR%d\t%03d\n", GETARG_A(c), i+GETARG_sBx(c)); - break; - case OP_SEND: - printf("OP_SEND\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_SENDB: - printf("OP_SENDB\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_TAILCALL: - printf("OP_TAILCALL\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_SUPER: - printf("OP_SUPER\tR%d\t%d\n", GETARG_A(c), - GETARG_C(c)); - break; - case OP_ARGARY: - printf("OP_ARGARY\tR%d\t%d:%d:%d:%d", GETARG_A(c), - (GETARG_Bx(c)>>10)&0x3f, - (GETARG_Bx(c)>>9)&0x1, - (GETARG_Bx(c)>>4)&0x1f, - (GETARG_Bx(c)>>0)&0xf); - print_lv(mrb, irep, c, RA); - break; - - case OP_ENTER: - printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n", - (GETARG_Ax(c)>>18)&0x1f, - (GETARG_Ax(c)>>13)&0x1f, - (GETARG_Ax(c)>>12)&0x1, - (GETARG_Ax(c)>>7)&0x1f, - (GETARG_Ax(c)>>2)&0x1f, - (GETARG_Ax(c)>>1)&0x1, - GETARG_Ax(c) & 0x1); - break; - case OP_RETURN: - printf("OP_RETURN\tR%d", GETARG_A(c)); - switch (GETARG_B(c)) { - case OP_R_NORMAL: - case OP_R_RETURN: - printf("\treturn"); break; - case OP_R_BREAK: - printf("\tbreak"); break; - default: - printf("\tbroken"); break; - break; - } - print_lv(mrb, irep, c, RA); - break; - case OP_BLKPUSH: - printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d", GETARG_A(c), - (GETARG_Bx(c)>>10)&0x3f, - (GETARG_Bx(c)>>9)&0x1, - (GETARG_Bx(c)>>4)&0x1f, - (GETARG_Bx(c)>>0)&0xf); - print_lv(mrb, irep, c, RA); - break; - - case OP_LAMBDA: - printf("OP_LAMBDA\tR%d\tI(%+d)\t%d", GETARG_A(c), GETARG_b(c)+1, GETARG_c(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_RANGE: - printf("OP_RANGE\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_METHOD: - printf("OP_METHOD\tR%d\t:%s", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)])); - print_lv(mrb, irep, c, RA); - break; - - case OP_ADD: - printf("OP_ADD\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_ADDI: - printf("OP_ADDI\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_SUB: - printf("OP_SUB\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_SUBI: - printf("OP_SUBI\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_MUL: - printf("OP_MUL\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_DIV: - printf("OP_DIV\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_LT: - printf("OP_LT\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_LE: - printf("OP_LE\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_GT: - printf("OP_GT\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_GE: - printf("OP_GE\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - case OP_EQ: - printf("OP_EQ\tR%d\t:%s\t%d\n", GETARG_A(c), - mrb_sym2name(mrb, irep->syms[GETARG_B(c)]), - GETARG_C(c)); - break; - - case OP_STOP: - printf("OP_STOP\n"); - break; - - case OP_ARRAY: - printf("OP_ARRAY\tR%d\tR%d\t%d", GETARG_A(c), GETARG_B(c), GETARG_C(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_ARYCAT: - printf("OP_ARYCAT\tR%d\tR%d", GETARG_A(c), GETARG_B(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_ARYPUSH: - printf("OP_ARYPUSH\tR%d\tR%d", 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, RAB); - 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", 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", 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", GETARG_A(c), GETARG_B(c)); - print_lv(mrb, irep, c, RAB); - break; - case OP_TCLASS: - printf("OP_TCLASS\tR%d", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_ERR: - { - mrb_value v = irep->pool[GETARG_Bx(c)]; - mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v))); - printf("OP_ERR\t%s\n", RSTRING_PTR(s)); - } - break; - case OP_EPUSH: - printf("OP_EPUSH\t:I(%+d)\n", GETARG_Bx(c)+1); - break; - case OP_ONERR: - printf("OP_ONERR\t%03d\n", i+GETARG_sBx(c)); - break; - case OP_RESCUE: - printf("OP_RESCUE\tR%d", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_RAISE: - printf("OP_RAISE\tR%d", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_POPERR: - printf("OP_POPERR\t%d", GETARG_A(c)); - print_lv(mrb, irep, c, RA); - break; - case OP_EPOP: - printf("OP_EPOP\t%d\n", GETARG_A(c)); - break; - - default: - printf("OP_unknown %d\t%d\t%d\t%d\n", GET_OPCODE(c), - GETARG_A(c), GETARG_B(c), GETARG_C(c)); - break; - } - mrb_gc_arena_restore(mrb, ai); - } - printf("\n"); -#endif -} - -static void -codedump_recur(mrb_state *mrb, mrb_irep *irep) -{ - size_t i; - - codedump(mrb, irep); - for (i=0; i<irep->rlen; i++) { - codedump_recur(mrb, irep->reps[i]); - } -} - -void -mrb_codedump_all(mrb_state *mrb, struct RProc *proc) -{ - codedump_recur(mrb, proc->body.irep); -} - -struct RProc* -mrb_generate_code(mrb_state *mrb, parser_state *p) -{ - codegen_scope *scope = scope_new(mrb, 0, 0); - struct RProc *proc; - - if (!scope) { - return NULL; - } - scope->mrb = mrb; - scope->parser = p; - scope->filename = p->filename; - scope->filename_index = p->current_filename_index; - - MRB_TRY(&scope->jmp) { - /* prepare irep */ - codegen(scope, p->tree, NOVAL); - proc = mrb_proc_new(mrb, scope->irep); - mrb_irep_decref(mrb, scope->irep); - mrb_pool_close(scope->mpool); - return proc; - } - MRB_CATCH(&scope->jmp) { - if (scope->filename == scope->irep->filename) { - scope->irep->filename = NULL; - } - mrb_irep_decref(mrb, scope->irep); - mrb_pool_close(scope->mpool); - return NULL; - } - MRB_END_EXC(&scope->jmp); -} diff --git a/src/compar.c b/src/compar.c index 0186b942f..0032fc859 100644 --- a/src/compar.c +++ b/src/compar.c @@ -4,7 +4,7 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" +#include <mruby.h> void mrb_init_comparable(mrb_state *mrb) diff --git a/src/debug.c b/src/debug.c index ea1aa1ddc..cc2d37034 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,7 +1,7 @@ #include <string.h> -#include "mruby.h" -#include "mruby/irep.h" -#include "mruby/debug.h" +#include <mruby.h> +#include <mruby/irep.h> +#include <mruby/debug.h> static mrb_irep_debug_info_file * get_file(mrb_irep_debug_info *info, uint32_t pc) @@ -49,7 +49,7 @@ select_line_type(const uint16_t *lines, size_t lines_len) ? mrb_debug_line_ary : mrb_debug_line_flat_map; } -char const* +MRB_API char const* mrb_debug_get_filename(mrb_irep *irep, uint32_t pc) { if (irep && pc < irep->ilen) { @@ -62,7 +62,7 @@ mrb_debug_get_filename(mrb_irep *irep, uint32_t pc) return NULL; } -int32_t +MRB_API int32_t mrb_debug_get_line(mrb_irep *irep, uint32_t pc) { if (irep && pc < irep->ilen) { @@ -106,7 +106,7 @@ mrb_debug_get_line(mrb_irep *irep, uint32_t pc) return -1; } -mrb_irep_debug_info * +MRB_API mrb_irep_debug_info * mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep) { static const mrb_irep_debug_info initial = { 0, 0, NULL }; @@ -119,7 +119,7 @@ mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep) return ret; } -mrb_irep_debug_info_file * +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) { @@ -198,7 +198,7 @@ mrb_debug_info_append_file(mrb_state *mrb, mrb_irep *irep, return ret; } -void +MRB_API void mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d) { uint32_t i; diff --git a/src/dump.c b/src/dump.c index b820f1a68..ea1cd5596 100644 --- a/src/dump.c +++ b/src/dump.c @@ -4,16 +4,22 @@ ** See Copyright Notice in mruby.h */ -#include <ctype.h> #include <string.h> #include <limits.h> -#include "mruby/dump.h" -#include "mruby/string.h" -#include "mruby/irep.h" -#include "mruby/numeric.h" -#include "mruby/debug.h" - -#ifdef ENABLE_STDIO +#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); @@ -22,6 +28,17 @@ static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep); #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; @@ -37,7 +54,7 @@ write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) { uint8_t *cur = buf; - cur += uint32_to_bin(get_irep_record_size_1(mrb, irep), cur); /* record size */ + cur += uint32_to_bin((uint32_t)get_irep_record_size_1(mrb, irep), cur); /* record size */ cur += uint16_to_bin((uint16_t)irep->nlocals, cur); /* number of local variable */ cur += uint16_to_bin((uint16_t)irep->nregs, cur); /* number of register variable */ cur += uint16_to_bin((uint16_t)irep->rlen, cur); /* number of child irep */ @@ -52,20 +69,39 @@ 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) +write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags) { uint8_t *cur = buf; uint32_t iseq_no; cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */ - for (iseq_no = 0; iseq_no < irep->ilen; iseq_no++) { - cur += uint32_to_bin(irep->iseq[iseq_no], cur); /* 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; @@ -78,7 +114,6 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep) size_t size = 0; size_t pool_no; mrb_value str; - char buf[32]; size += sizeof(uint32_t); /* plen */ size += irep->plen * (sizeof(uint8_t) + sizeof(uint16_t)); /* len(n) */ @@ -97,9 +132,9 @@ get_pool_block_size(mrb_state *mrb, mrb_irep *irep) break; case MRB_TT_FLOAT: + str = mrb_float_to_str(mrb, irep->pool[pool_no], MRB_FLOAT_FMT); { - int len; - len = mrb_float_to_str(buf, mrb_float(irep->pool[pool_no])); + mrb_int len = RSTRING_LEN(str); mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX); size += (size_t)len; } @@ -130,7 +165,6 @@ write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) uint16_t len; mrb_value str; const char *char_ptr; - char char_buf[30]; cur += uint32_to_bin(irep->plen, cur); /* number of pool */ @@ -141,43 +175,29 @@ write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf) case MRB_TT_FIXNUM: cur += uint8_to_bin(IREP_TT_FIXNUM, cur); /* data type */ str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10); - char_ptr = RSTRING_PTR(str); - { - mrb_int tlen; - - tlen = RSTRING_LEN(str); - mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX); - len = (uint16_t)tlen; - } break; case MRB_TT_FLOAT: cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */ - { - int tlen; - tlen = mrb_float_to_str(char_buf, mrb_float(irep->pool[pool_no])); - mrb_assert_int_fit(int, tlen, uint16_t, UINT16_MAX); - len = (uint16_t)tlen; - } - char_ptr = &char_buf[0]; + 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 */ - char_ptr = RSTRING_PTR(irep->pool[pool_no]); - { - mrb_int tlen; - - tlen = RSTRING_LEN(irep->pool[pool_no]); - mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX); - len = (uint16_t)tlen; - } + 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; @@ -263,9 +283,10 @@ get_irep_record_size(mrb_state *mrb, mrb_irep *irep) } static int -write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin, size_t *irep_record_size) +write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags) { uint32_t i; + uint8_t *src = bin; if (irep == NULL) { return MRB_DUMP_INVALID_IREP; @@ -276,10 +297,8 @@ write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin, size_t *irep_rec return MRB_DUMP_GENERAL_FAILURE; } - memset(bin, 0, *irep_record_size); - bin += write_irep_header(mrb, irep, bin); - bin += write_iseq_block(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); @@ -287,13 +306,13 @@ write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t* bin, size_t *irep_rec int result; size_t rsize; - result = write_irep_record(mrb, irep->reps[i], bin, &rsize); + result = write_irep_record(mrb, irep->reps[i], bin, &rsize, flags); if (result != MRB_DUMP_OK) { return result; } - *irep_record_size += rsize; bin += rsize; } + *irep_record_size = bin - src; return MRB_DUMP_OK; } @@ -302,7 +321,7 @@ write_footer(mrb_state *mrb, uint8_t *bin) { struct rite_binary_footer footer; - memcpy(footer.section_identify, RITE_BINARY_EOF, sizeof(footer.section_identify)); + memcpy(footer.section_ident, RITE_BINARY_EOF, sizeof(footer.section_ident)); uint32_to_bin(sizeof(struct rite_binary_footer), footer.section_size); memcpy(bin, &footer, sizeof(struct rite_binary_footer)); @@ -315,7 +334,7 @@ write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin) { struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin; - memcpy(header->section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(header->section_identify)); + memcpy(header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(header->section_ident)); mrb_assert_int_fit(size_t, section_size, uint32_t, UINT32_MAX); uint32_to_bin((uint32_t)section_size, header->section_size); @@ -325,10 +344,9 @@ write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin) } static int -write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin) +write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags) { int result; - size_t section_size = 0; /* size of irep record */ size_t rsize = 0; uint8_t *cur = bin; @@ -337,14 +355,13 @@ write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin) } cur += sizeof(struct rite_section_irep_header); - section_size += sizeof(struct rite_section_irep_header); - result = write_irep_record(mrb, irep, cur, &rsize); + result = write_irep_record(mrb, irep, cur, &rsize, flags); if (result != MRB_DUMP_OK) { return result; } - section_size += rsize; - write_section_irep_header(mrb, section_size, bin); + *len_p = cur - bin + rsize; + write_section_irep_header(mrb, *len_p, bin); return MRB_DUMP_OK; } @@ -354,7 +371,7 @@ write_section_lineno_header(mrb_state *mrb, size_t section_size, uint8_t *bin) { struct rite_section_lineno_header *header = (struct rite_section_lineno_header*)bin; - memcpy(header->section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(header->section_identify)); + 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; @@ -645,7 +662,7 @@ write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len); section_size += dlen; - memcpy(header->section_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(header->section_identify)); + memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident)); mrb_assert(section_size <= INT32_MAX); uint32_to_bin(section_size, header->section_size); @@ -784,7 +801,7 @@ write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const * goto lv_section_exit; } - memcpy(header->section_identify, RITE_SECTION_LV_IDENTIFIER, sizeof(header->section_identify)); + memcpy(header->section_ident, RITE_SECTION_LV_IDENT, sizeof(header->section_ident)); diff = cur - start; mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX); @@ -795,13 +812,28 @@ lv_section_exit: } static int -write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin) +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; - memcpy(header->binary_identify, RITE_BINARY_IDENTIFIER, sizeof(header->binary_identify)); + 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)); @@ -841,10 +873,26 @@ is_lv_defined(mrb_irep *irep) return FALSE; } -int -mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, size_t *bin_size) +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; @@ -861,7 +909,7 @@ mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, siz section_irep_size += get_irep_record_size(mrb, irep); /* DEBUG section size */ - if (debug_info) { + if (flags & DUMP_DEBUG_INFO) { if (debug_info_defined) { section_lineno_size += sizeof(struct rite_section_debug_header); /* filename table */ @@ -885,23 +933,23 @@ mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, siz section_lv_size += get_lv_section_size(mrb, irep, lv_syms, lv_syms_len); } - *bin_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, *bin_size); - if (cur == NULL) { - goto error_exit; - } + 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); + 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 (debug_info) { + if (flags & DUMP_DEBUG_INFO) { if (debug_info_defined) { result = write_section_debug(mrb, irep, cur, filenames, filenames_len); } @@ -923,24 +971,28 @@ mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, int debug_info, uint8_t **bin, siz } write_footer(mrb, cur); - write_rite_binary_header(mrb, *bin_size, *bin); + write_rite_binary_header(mrb, *bin_size, *bin, flags); error_exit: if (result != MRB_DUMP_OK) { mrb_free(mrb, *bin); *bin = NULL; } - if (lv_syms) { - mrb_free(mrb, lv_syms); - } - if (filenames) { - mrb_free(mrb, filenames); - } + mrb_free(mrb, lv_syms); + mrb_free(mrb, filenames); return result; } int -mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp) +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; @@ -950,9 +1002,11 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp) return MRB_DUMP_INVALID_ARGUMENT; } - result = mrb_dump_irep(mrb, irep, debug_info, &bin, &bin_size); + result = dump_irep(mrb, irep, dump_flags(flags, FLAG_BYTEORDER_NONATIVE), &bin, &bin_size); if (result == MRB_DUMP_OK) { - fwrite(bin, bin_size, 1, fp); + if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) { + result = MRB_DUMP_WRITE_FAULT; + } } mrb_free(mrb, bin); @@ -960,45 +1014,82 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp) } static mrb_bool -is_valid_c_symbol_name(const char *name) +dump_bigendian_p(uint8_t flags) { - const char *c = NULL; - - if (name == NULL || name[0] == '\0') return FALSE; - if (!ISALPHA(name[0]) && name[0] != '_') return FALSE; - - c = &name[1]; - for (; *c != '\0'; ++c) { - if (!ISALNUM(*c) && *c != '_') return FALSE; - } - - return TRUE; + 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, int debug_info, FILE *fp, const char *initname) +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 || !is_valid_c_symbol_name(initname)) { + if (fp == NULL || initname == NULL || initname[0] == '\0') { return MRB_DUMP_INVALID_ARGUMENT; } - - result = mrb_dump_irep(mrb, irep, debug_info, &bin, &bin_size); + flags = dump_flags(flags, FLAG_BYTEORDER_NATIVE); + result = dump_irep(mrb, irep, flags, &bin, &bin_size); if (result == MRB_DUMP_OK) { - fprintf(fp, "#include <stdint.h>\n"); /* for uint8_t under at least Darwin */ - fprintf(fp, "const uint8_t %s[] = {", initname); + 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, + "const uint8_t\n" + "#if defined __GNUC__\n" + "__attribute__((aligned(%u)))\n" + "#elif defined _MSC_VER\n" + "__declspec(align(%u))\n" + "#endif\n" + "%s[] = {", + (uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) { + mrb_free(mrb, bin); + return MRB_DUMP_WRITE_FAULT; + } while (bin_idx < bin_size) { - if (bin_idx % 16 == 0) fputs("\n", fp); - fprintf(fp, "0x%02x,", bin[bin_idx++]); + 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; } - fputs("\n};\n", fp); } mrb_free(mrb, bin); return result; } -#endif /* ENABLE_STDIO */ +#endif /* MRB_DISABLE_STDIO */ diff --git a/src/enum.c b/src/enum.c index 3def9e860..adb815bf1 100644 --- a/src/enum.c +++ b/src/enum.c @@ -4,7 +4,7 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" +#include <mruby.h> void mrb_init_enumerable(mrb_state *mrb) diff --git a/src/error.c b/src/error.c index 5ca013527..d15f9e76f 100644 --- a/src/error.c +++ b/src/error.c @@ -7,27 +7,29 @@ #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 "mrb_throw.h" - -mrb_value -mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, long len) +#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) { - return mrb_funcall(mrb, mrb_obj_value(c), "new", 1, mrb_str_new(mrb, ptr, len)); + mrb_value arg = mrb_str_new(mrb, ptr, len); + return mrb_obj_new(mrb, c, 1, &arg); } -mrb_value +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_funcall(mrb, mrb_obj_value(c), "new", 1, str); + return mrb_obj_new(mrb, c, 1, &str); } /* @@ -90,8 +92,15 @@ 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_nil_p(mesg)) return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc)); + 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; } @@ -143,7 +152,7 @@ exc_inspect(mrb_state *mrb, mrb_value exc) mrb_str_append(mrb, str, line); mrb_str_cat_lit(mrb, str, ": "); if (append_mesg) { - mrb_str_append(mrb, str, mesg); + mrb_str_cat_str(mrb, str, mesg); mrb_str_cat_lit(mrb, str, " ("); } mrb_str_cat_cstr(mrb, str, mrb_obj_classname(mrb, exc)); @@ -156,7 +165,7 @@ exc_inspect(mrb_state *mrb, mrb_value exc) str = mrb_str_new_cstr(mrb, cname); mrb_str_cat_lit(mrb, str, ": "); if (append_mesg) { - mrb_str_append(mrb, str, mesg); + mrb_str_cat_str(mrb, str, mesg); } else { mrb_str_cat_cstr(mrb, str, cname); @@ -166,36 +175,6 @@ exc_inspect(mrb_state *mrb, mrb_value exc) } -static mrb_value -exc_equal(mrb_state *mrb, mrb_value exc) -{ - mrb_value obj; - mrb_value mesg; - mrb_bool equal_p; - mrb_sym id_mesg = mrb_intern_lit(mrb, "mesg"); - - mrb_get_args(mrb, "o", &obj); - if (mrb_obj_equal(mrb, exc, obj)) { - equal_p = TRUE; - } - else { - if (mrb_obj_class(mrb, exc) != mrb_obj_class(mrb, obj)) { - if (mrb_respond_to(mrb, obj, mrb_intern_lit(mrb, "message"))) { - mesg = mrb_funcall(mrb, obj, "message", 0); - } - else - return mrb_false_value(); - } - else { - mesg = mrb_attr_get(mrb, obj, id_mesg); - } - - equal_p = mrb_equal(mrb, mrb_attr_get(mrb, exc, id_mesg), mesg); - } - - return mrb_bool_value(equal_p); -} - static void exc_debug_info(mrb_state *mrb, struct RObject *exc) { @@ -223,11 +202,11 @@ exc_debug_info(mrb_state *mrb, struct RObject *exc) } } -mrb_noreturn void +MRB_API mrb_noreturn void mrb_exc_raise(mrb_state *mrb, mrb_value exc) { mrb->exc = mrb_obj_ptr(exc); - if (!mrb->out_of_memory) { + if (!mrb->gc.out_of_memory) { exc_debug_info(mrb, mrb->exc); } if (!mrb->jmp) { @@ -237,7 +216,7 @@ mrb_exc_raise(mrb_state *mrb, mrb_value exc) MRB_THROW(mrb->jmp); } -mrb_noreturn void +MRB_API mrb_noreturn void mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg) { mrb_value mesg; @@ -245,7 +224,7 @@ mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg) mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg)); } -mrb_value +MRB_API mrb_value mrb_vformat(mrb_state *mrb, const char *format, va_list ap) { const char *p = format; @@ -282,11 +261,11 @@ mrb_vformat(mrb_state *mrb, const char *format, va_list ap) else { size = p - b; mrb_ary_push(mrb, ary, mrb_str_new(mrb, b, size)); - return mrb_ary_join(mrb, ary, mrb_str_new(mrb,NULL,0)); + return mrb_ary_join(mrb, ary, mrb_str_new(mrb, NULL, 0)); } } -mrb_value +MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...) { va_list ap; @@ -299,7 +278,7 @@ mrb_format(mrb_state *mrb, const char *format, ...) return str; } -mrb_noreturn void +MRB_API mrb_noreturn void mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...) { va_list args; @@ -311,7 +290,7 @@ mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...) mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mesg)); } -mrb_noreturn void +MRB_API mrb_noreturn void mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...) { mrb_value exc; @@ -327,10 +306,10 @@ mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...) mrb_exc_raise(mrb, exc); } -void +MRB_API void mrb_warn(mrb_state *mrb, const char *fmt, ...) { -#ifdef ENABLE_STDIO +#ifndef MRB_DISABLE_STDIO va_list ap; mrb_value str; @@ -342,10 +321,10 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...) #endif } -mrb_noreturn void +MRB_API mrb_noreturn void mrb_bug(mrb_state *mrb, const char *fmt, ...) { -#ifdef ENABLE_STDIO +#ifndef MRB_DISABLE_STDIO va_list ap; mrb_value str; @@ -417,13 +396,13 @@ exception_call: return mesg; } -mrb_value +MRB_API mrb_value mrb_make_exception(mrb_state *mrb, int argc, const mrb_value *argv) { return make_exception(mrb, argc, argv, TRUE); } -void +MRB_API void mrb_sys_fail(mrb_state *mrb, const char *mesg) { struct RClass *sce; @@ -444,16 +423,15 @@ mrb_sys_fail(mrb_state *mrb, const char *mesg) } } -mrb_noreturn void -mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_int argc, const mrb_value *argv, char const* fmt, ...) +MRB_API mrb_noreturn void +mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...) { mrb_value exc; va_list ap; va_start(ap, fmt); exc = mrb_funcall(mrb, mrb_obj_value(E_NOMETHOD_ERROR), "new", 3, - mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), - mrb_ary_new_from_values(mrb, argc, argv)); + mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), args); va_end(ap); mrb_exc_raise(mrb, exc); } @@ -464,10 +442,10 @@ mrb_init_exception(mrb_state *mrb) struct RClass *exception, *runtime_error, *script_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, "==", exc_equal, MRB_ARGS_REQ(1)); mrb_define_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "message", exc_message, MRB_ARGS_NONE()); mrb_define_method(mrb, exception, "inspect", exc_inspect, MRB_ARGS_NONE()); @@ -475,7 +453,8 @@ mrb_init_exception(mrb_state *mrb) mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ runtime_error = mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */ - mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str(mrb, runtime_error, mrb_str_new_lit(mrb, "Out of memory"))); + mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, runtime_error, "Out of memory")); script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */ mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ + mrb_define_class(mrb, "SystemStackError", exception); } diff --git a/src/error.h b/src/error.h index 0e0dacf63..eb755ec7f 100644 --- a/src/error.h +++ b/src/error.h @@ -1,3 +1,3 @@ /* this header file is to be removed soon. added for compatibility purpose (1.0.0) */ -#include "mruby/error.h" +#include <mruby/error.h> @@ -4,14 +4,14 @@ ** 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" - -struct RData* +#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; @@ -23,10 +23,10 @@ mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb return data; } -void +MRB_API void mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { - if (mrb_special_const_p(obj) || (mrb_type(obj) != MRB_TT_DATA)) { + if (mrb_type(obj) != MRB_TT_DATA) { mrb_check_type(mrb, obj, MRB_TT_DATA); } if (DATA_TYPE(obj) != type) { @@ -45,10 +45,10 @@ mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) } } -void * +MRB_API void* mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) { - if (mrb_special_const_p(obj) || (mrb_type(obj) != MRB_TT_DATA)) { + if (mrb_type(obj) != MRB_TT_DATA) { return NULL; } if (DATA_TYPE(obj) != type) { @@ -57,27 +57,25 @@ mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type) return DATA_PTR(obj); } -void * +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_sym +MRB_API mrb_sym mrb_obj_to_sym(mrb_state *mrb, mrb_value name) { - mrb_value tmp; mrb_sym id; switch (mrb_type(name)) { default: - tmp = mrb_check_string_type(mrb, name); - if (mrb_nil_p(tmp)) { - tmp = mrb_inspect(mrb, name); - mrb_raisef(mrb, E_TYPE_ERROR, "%S is not a symbol", tmp); + name = 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); } - name = tmp; /* fall through */ case MRB_TT_STRING: name = mrb_str_intern(mrb, name); @@ -88,7 +86,7 @@ mrb_obj_to_sym(mrb_state *mrb, mrb_value name) return id; } -mrb_int +MRB_API mrb_int mrb_float_id(mrb_float f) { const char *p = (const char*)&f; @@ -104,7 +102,7 @@ mrb_float_id(mrb_float f) return id; } -mrb_int +MRB_API mrb_int mrb_obj_id(mrb_value obj) { mrb_int tt = mrb_type(obj); @@ -113,42 +111,42 @@ mrb_obj_id(mrb_value obj) #define MakeID(p) MakeID2(p,tt) switch (tt) { - case MRB_TT_FREE: - case MRB_TT_UNDEF: + case MRB_TT_FREE: + case MRB_TT_UNDEF: return MakeID(0); /* not define */ - case MRB_TT_FALSE: + case MRB_TT_FALSE: if (mrb_nil_p(obj)) return MakeID(1); return MakeID(0); - case MRB_TT_TRUE: + case MRB_TT_TRUE: return MakeID(1); - case MRB_TT_SYMBOL: + case MRB_TT_SYMBOL: return MakeID(mrb_symbol(obj)); - case MRB_TT_FIXNUM: + case MRB_TT_FIXNUM: return MakeID2(mrb_float_id((mrb_float)mrb_fixnum(obj)), MRB_TT_FLOAT); - case 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_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: default: return MakeID(mrb_ptr(obj)); } } #ifdef MRB_WORD_BOXING -mrb_value -mrb_float_value(mrb_state *mrb, mrb_float f) +MRB_API mrb_value +mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f) { mrb_value v; @@ -157,8 +155,8 @@ mrb_float_value(mrb_state *mrb, mrb_float f) return v; } -mrb_value -mrb_float_pool(mrb_state *mrb, mrb_float f) +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; @@ -167,8 +165,8 @@ mrb_float_pool(mrb_state *mrb, mrb_float f) return mrb_obj_value(nf); } -mrb_value -mrb_cptr_value(mrb_state *mrb, void *p) +MRB_API mrb_value +mrb_word_boxing_cptr_value(mrb_state *mrb, void *p) { mrb_value v; @@ -178,8 +176,45 @@ mrb_cptr_value(mrb_state *mrb, void *p) } #endif /* MRB_WORD_BOXING */ -mrb_bool +MRB_API mrb_bool mrb_regexp_p(mrb_state *mrb, mrb_value v) { return mrb_class_defined(mrb, REGEXP_CLASS) && mrb_obj_is_kind_of(mrb, v, mrb_class_get(mrb, REGEXP_CLASS)); } + +#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/src/fmt_fp.c b/src/fmt_fp.c new file mode 100644 index 000000000..61c7a4cc9 --- /dev/null +++ b/src/fmt_fp.c @@ -0,0 +1,371 @@ +/* + +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, int w, int l, int 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, int w, int p, int 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, l; + char buf[9+LDBL_MANT_DIG/4], *s; + const char *prefix="-0X+0X 0X-0x+0x 0x"; + int 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, ' ', w, 3+pl, fl&~ZERO_PAD); + out(f, prefix, pl); + out(f, ss, 3); + pad(f, ' ', w, 3+pl, fl^LEFT_ADJ); + return MAX(w, 3+pl); + } + + y = frexp((double)y, &e2) * 2; + if (y) e2--; + + if ((t|32)=='a') { + long double round = 8.0; + int 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, ' ', w, pl+l, fl); + out(f, prefix, pl); + pad(f, '0', w, 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, ' ', w, pl+l, fl^LEFT_ADJ); + return MAX(w, pl+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+(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*(r-a); *a>=i; i*=10, e++); + else e=0; + + /* Perform rounding: j is precision after the radix (possibly neg) */ + j = 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*(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, ' ', w, pl+l, fl); + out(f, prefix, pl); + pad(f, '0', w, 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 -= buf+9-ss; + } + pad(f, '0', p+18, 18, 0); + out(f, estr, ebuf-estr); + } + + pad(f, ' ', w, pl+l, fl^LEFT_ADJ); + + return MAX(w, pl+l); +} + +static int +fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo) +{ + int 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, 0, 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_buf_new(mrb, 24); + if (fmt_core(&f, fmt, mrb_float(flo)) < 0) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string"); + } + return f.str; +} @@ -6,16 +6,17 @@ #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.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> /* = Tri-color Incremental Garbage Collection @@ -62,12 +63,13 @@ == Write Barrier - mruby implementer and C extension library writer must write a write - barrier when writing a pointer to an object on object's field. - Two different write barrier are available: + 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 - * mrb_write_barrier + * mrb_field_write_barrier - target B object for a mark. + * mrb_write_barrier - target A object for a mark. == Generational Mode @@ -107,6 +109,11 @@ typedef struct { struct RRange range; struct RData data; struct RProc proc; + struct RException exc; +#ifdef MRB_WORD_BOXING + struct RFloat floatv; + struct RCptr cptr; +#endif } as; } RVALUE; @@ -129,7 +136,7 @@ gettimeofday_time(void) #define GC_INVOKE_TIME_REPORT(with) do {\ fprintf(stderr, "%s\n", with);\ fprintf(stderr, "gc_invoke: %19.3f\n", gettimeofday_time() - program_invoke_time);\ - fprintf(stderr, "is_generational: %d\n", is_generational(mrb));\ + fprintf(stderr, "is_generational: %d\n", is_generational(gc));\ fprintf(stderr, "is_major_gc: %d\n", is_major_gc(mrb));\ } while(0) @@ -140,10 +147,10 @@ gettimeofday_time(void) #define GC_TIME_STOP_AND_REPORT do {\ gc_time = gettimeofday_time() - gc_time;\ gc_total_time += gc_time;\ - fprintf(stderr, "gc_state: %d\n", mrb->gc_state);\ - fprintf(stderr, "live: %zu\n", mrb->live);\ - fprintf(stderr, "majorgc_old_threshold: %zu\n", mrb->majorgc_old_threshold);\ - fprintf(stderr, "gc_threshold: %zu\n", mrb->gc_threshold);\ + fprintf(stderr, "gc_state: %d\n", gc->state);\ + fprintf(stderr, "live: %zu\n", gc->live);\ + fprintf(stderr, "majorgc_old_threshold: %zu\n", gc->majorgc_old_threshold);\ + fprintf(stderr, "gc_threshold: %zu\n", gc->threshold);\ fprintf(stderr, "gc_time: %30.20f\n", gc_time);\ fprintf(stderr, "gc_total_time: %30.20f\n\n", gc_total_time);\ } while(0) @@ -159,59 +166,82 @@ gettimeofday_time(void) #define DEBUG(x) #endif -#define GC_STEP_SIZE 1024 +#ifndef MRB_HEAP_PAGE_SIZE +#define MRB_HEAP_PAGE_SIZE 1024 +#endif +#define GC_STEP_SIZE 1024 -void* +/* 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->ud); - if (!p2 && len > 0 && mrb->heaps) { + 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->ud); + p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud); } return p2; } - -void* +MRB_API void* mrb_realloc(mrb_state *mrb, void *p, size_t len) { void *p2; p2 = mrb_realloc_simple(mrb, p, len); if (!p2 && len) { - if (mrb->out_of_memory) { + if (mrb->gc.out_of_memory) { /* mrb_panic(mrb); */ } else { - mrb->out_of_memory = TRUE; + mrb->gc.out_of_memory = TRUE; mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); } } else { - mrb->out_of_memory = FALSE; + mrb->gc.out_of_memory = FALSE; } return p2; } -void* +MRB_API void* mrb_malloc(mrb_state *mrb, size_t len) { return mrb_realloc(mrb, 0, len); } -void* +MRB_API void* mrb_malloc_simple(mrb_state *mrb, size_t len) { return mrb_realloc_simple(mrb, 0, len); } -void* +MRB_API void* mrb_calloc(mrb_state *mrb, size_t nelem, size_t len) { void *p; @@ -220,11 +250,9 @@ mrb_calloc(mrb_state *mrb, size_t nelem, size_t len) nelem <= SIZE_MAX / len) { size_t size; size = nelem * len; - p = mrb_realloc(mrb, 0, size); + p = mrb_malloc(mrb, size); - if (p) { - memset(p, 0, size); - } + memset(p, 0, size); } else { p = NULL; @@ -233,107 +261,104 @@ mrb_calloc(mrb_state *mrb, size_t nelem, size_t len) return p; } -void +MRB_API void mrb_free(mrb_state *mrb, void *p) { - (mrb->allocf)(mrb, p, 0, mrb->ud); + (mrb->allocf)(mrb, p, 0, mrb->allocf_ud); } -#ifndef MRB_HEAP_PAGE_SIZE -#define MRB_HEAP_PAGE_SIZE 1024 -#endif - -struct heap_page { - struct RBasic *freelist; - struct heap_page *prev; - struct heap_page *next; - struct heap_page *free_next; - struct heap_page *free_prev; - mrb_bool old:1; - RVALUE objects[MRB_HEAP_PAGE_SIZE]; -}; +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_state *mrb, struct heap_page *page) +link_heap_page(mrb_gc *gc, mrb_heap_page *page) { - page->next = mrb->heaps; - if (mrb->heaps) - mrb->heaps->prev = page; - mrb->heaps = page; + page->next = gc->heaps; + if (gc->heaps) + gc->heaps->prev = page; + gc->heaps = page; } static void -unlink_heap_page(mrb_state *mrb, struct heap_page *page) +unlink_heap_page(mrb_gc *gc, mrb_heap_page *page) { if (page->prev) page->prev->next = page->next; if (page->next) page->next->prev = page->prev; - if (mrb->heaps == page) - mrb->heaps = page->next; + if (gc->heaps == page) + gc->heaps = page->next; page->prev = NULL; page->next = NULL; } static void -link_free_heap_page(mrb_state *mrb, struct heap_page *page) +link_free_heap_page(mrb_gc *gc, mrb_heap_page *page) { - page->free_next = mrb->free_heaps; - if (mrb->free_heaps) { - mrb->free_heaps->free_prev = page; + page->free_next = gc->free_heaps; + if (gc->free_heaps) { + gc->free_heaps->free_prev = page; } - mrb->free_heaps = page; + gc->free_heaps = page; } static void -unlink_free_heap_page(mrb_state *mrb, struct heap_page *page) +unlink_free_heap_page(mrb_gc *gc, mrb_heap_page *page) { if (page->free_prev) page->free_prev->free_next = page->free_next; if (page->free_next) page->free_next->free_prev = page->free_prev; - if (mrb->free_heaps == page) - mrb->free_heaps = page->free_next; + if (gc->free_heaps == page) + gc->free_heaps = page->free_next; page->free_prev = NULL; page->free_next = NULL; } static void -add_heap(mrb_state *mrb) +add_heap(mrb_state *mrb, mrb_gc *gc) { - struct heap_page *page = (struct heap_page *)mrb_calloc(mrb, 1, sizeof(struct heap_page)); + mrb_heap_page *page = (mrb_heap_page *)mrb_calloc(mrb, 1, sizeof(mrb_heap_page) + MRB_HEAP_PAGE_SIZE * sizeof(RVALUE)); RVALUE *p, *e; struct RBasic *prev = NULL; - for (p = page->objects, e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) { + for (p = objects(page), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) { p->as.free.tt = MRB_TT_FREE; p->as.free.next = prev; prev = &p->as.basic; } page->freelist = prev; - link_heap_page(mrb, page); - link_free_heap_page(mrb, page); + link_heap_page(gc, page); + link_free_heap_page(gc, page); } #define DEFAULT_GC_INTERVAL_RATIO 200 #define DEFAULT_GC_STEP_RATIO 200 #define DEFAULT_MAJOR_GC_INC_RATIO 200 -#define is_generational(mrb) ((mrb)->is_generational_gc_mode) -#define is_major_gc(mrb) (is_generational(mrb) && (mrb)->gc_full) -#define is_minor_gc(mrb) (is_generational(mrb) && !(mrb)->gc_full) +#define is_generational(gc) ((gc)->generational) +#define is_major_gc(gc) (is_generational(gc) && (gc)->full) +#define is_minor_gc(gc) (is_generational(gc) && !(gc)->full) void -mrb_init_heap(mrb_state *mrb) +mrb_gc_init(mrb_state *mrb, mrb_gc *gc) { - mrb->heaps = NULL; - mrb->free_heaps = NULL; - add_heap(mrb); - mrb->gc_interval_ratio = DEFAULT_GC_INTERVAL_RATIO; - mrb->gc_step_ratio = DEFAULT_GC_STEP_RATIO; +#ifndef MRB_GC_FIXED_ARENA + gc->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE); + gc->arena_capa = MRB_GC_ARENA_SIZE; +#endif + + gc->current_white_part = GC_WHITE_A; + gc->heaps = NULL; + gc->free_heaps = NULL; + add_heap(mrb, gc); + gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO; + gc->step_ratio = DEFAULT_GC_STEP_RATIO; #ifndef MRB_GC_TURN_OFF_GENERATIONAL - mrb->is_generational_gc_mode = TRUE; - mrb->gc_full = TRUE; + gc->generational = TRUE; + gc->full = TRUE; #endif #ifdef GC_PROFILE @@ -344,16 +369,16 @@ mrb_init_heap(mrb_state *mrb) static void obj_free(mrb_state *mrb, struct RBasic *obj); void -mrb_free_heap(mrb_state *mrb) +free_heap(mrb_state *mrb, mrb_gc *gc) { - struct heap_page *page = mrb->heaps; - struct heap_page *tmp; + mrb_heap_page *page = gc->heaps; + mrb_heap_page *tmp; RVALUE *p, *e; while (page) { tmp = page; page = page->next; - for (p = tmp->objects, e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) { + for (p = objects(tmp), e=p+MRB_HEAP_PAGE_SIZE; p<e; p++) { if (p->as.free.tt != MRB_TT_FREE) obj_free(mrb, &p->as.basic); } @@ -361,65 +386,123 @@ mrb_free_heap(mrb_state *mrb) } } +void +mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc) +{ + free_heap(mrb, gc); +#ifndef MRB_GC_FIXED_ARENA + mrb_free(mrb, gc->arena); +#endif +} + static void -gc_protect(mrb_state *mrb, struct RBasic *p) +gc_protect(mrb_state *mrb, mrb_gc *gc, struct RBasic *p) { #ifdef MRB_GC_FIXED_ARENA - if (mrb->arena_idx >= MRB_GC_ARENA_SIZE) { + if (gc->arena_idx >= MRB_GC_ARENA_SIZE) { /* arena overflow error */ - mrb->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */ + gc->arena_idx = MRB_GC_ARENA_SIZE - 4; /* force room in arena */ mrb_raise(mrb, E_RUNTIME_ERROR, "arena overflow error"); } #else - if (mrb->arena_idx >= mrb->arena_capa) { + if (gc->arena_idx >= gc->arena_capa) { /* extend arena */ - mrb->arena_capa = (int)(mrb->arena_capa * 1.5); - mrb->arena = (struct RBasic**)mrb_realloc(mrb, mrb->arena, sizeof(struct RBasic*)*mrb->arena_capa); + gc->arena_capa = (int)(gc->arena_capa * 1.5); + gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*gc->arena_capa); } #endif - mrb->arena[mrb->arena_idx++] = p; + gc->arena[gc->arena_idx++] = p; } -void +/* mrb_gc_protect() leaves the object in the arena */ +MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj) { - if (mrb_special_const_p(obj)) return; - gc_protect(mrb, mrb_basic_ptr(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 obejct 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, j; + + 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=j=0; i<a->len; i++) { + if (!mrb_obj_eq(mrb, a->ptr[i], obj)) { + a->ptr[j++] = a->ptr[i]; + } + } + a->len = j; } -struct RBasic* +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; #ifdef MRB_GC_STRESS mrb_full_gc(mrb); #endif - if (mrb->gc_threshold < mrb->live) { + if (gc->threshold < gc->live) { mrb_incremental_gc(mrb); } - if (mrb->free_heaps == NULL) { - add_heap(mrb); + if (gc->free_heaps == NULL) { + add_heap(mrb, gc); } - p = mrb->free_heaps->freelist; - mrb->free_heaps->freelist = ((struct free_obj*)p)->next; - if (mrb->free_heaps->freelist == NULL) { - unlink_free_heap_page(mrb, mrb->free_heaps); + p = gc->free_heaps->freelist; + gc->free_heaps->freelist = ((struct free_obj*)p)->next; + if (gc->free_heaps->freelist == NULL) { + unlink_free_heap_page(gc, gc->free_heaps); } - mrb->live++; - gc_protect(mrb, p); + gc->live++; + gc_protect(mrb, gc, p); *(RVALUE *)p = RVALUE_zero; p->tt = ttype; p->c = cls; - paint_partial_white(mrb, p); + paint_partial_white(gc, p); return p; } static inline void -add_gray_list(mrb_state *mrb, struct RBasic *obj) +add_gray_list(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { #ifdef MRB_GC_STRESS if (obj->tt > MRB_TT_MAXDEFINE) { @@ -427,8 +510,8 @@ add_gray_list(mrb_state *mrb, struct RBasic *obj) } #endif paint_gray(obj); - obj->gcnext = mrb->gray_list; - mrb->gray_list = obj; + obj->gcnext = gc->gray_list; + gc->gray_list = obj; } static void @@ -485,15 +568,20 @@ mark_context(mrb_state *mrb, struct mrb_context *c) } static void -gc_mark_children(mrb_state *mrb, struct RBasic *obj) +gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { mrb_assert(is_gray(obj)); paint_black(obj); - mrb->gray_list = obj->gcnext; + gc->gray_list = obj->gcnext; mrb_gc_mark(mrb, (struct RBasic*)obj->c); switch (obj->tt) { case MRB_TT_ICLASS: - mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + { + struct RClass *c = (struct RClass*)obj; + if (MRB_FLAG_TEST(c, MRB_FLAG_IS_ORIGIN)) + mrb_gc_mark_mt(mrb, c); + mrb_gc_mark(mrb, (struct RBasic*)((struct RClass*)obj)->super); + } break; case MRB_TT_CLASS: @@ -509,6 +597,7 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj) case MRB_TT_OBJECT: case MRB_TT_DATA: + case MRB_TT_EXCEPTION: mrb_gc_mark_iv(mrb, (struct RObject*)obj); break; @@ -526,9 +615,9 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj) struct REnv *e = (struct REnv*)obj; if (!MRB_ENV_STACK_SHARED_P(e)) { - int i, len; + mrb_int i, len; - len = (int)MRB_ENV_STACK_LEN(e); + len = MRB_ENV_STACK_LEN(e); for (i=0; i<len; i++) { mrb_gc_mark_value(mrb, e->stack[i]); } @@ -579,13 +668,13 @@ gc_mark_children(mrb_state *mrb, struct RBasic *obj) } } -void +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, obj); + add_gray_list(mrb, &mrb->gc, obj); } static void @@ -608,6 +697,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj) #endif case MRB_TT_OBJECT: + case MRB_TT_EXCEPTION: mrb_gc_free_iv(mrb, (struct RObject*)obj); break; @@ -617,7 +707,10 @@ obj_free(mrb_state *mrb, struct RBasic *obj) 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; @@ -639,7 +732,7 @@ obj_free(mrb_state *mrb, struct RBasic *obj) break; case MRB_TT_ARRAY: - if (obj->flags & MRB_ARY_SHARED) + if (ARY_SHARED_P(obj)) mrb_ary_decref(mrb, ((struct RArray*)obj)->aux.shared); else mrb_free(mrb, ((struct RArray*)obj)->ptr); @@ -685,19 +778,19 @@ obj_free(mrb_state *mrb, struct RBasic *obj) } static void -root_scan_phase(mrb_state *mrb) +root_scan_phase(mrb_state *mrb, mrb_gc *gc) { size_t i, e; - if (!is_minor_gc(mrb)) { - mrb->gray_list = NULL; - mrb->atomic_gray_list = NULL; + if (!is_minor_gc(gc)) { + gc->gray_list = NULL; + gc->atomic_gray_list = NULL; } mrb_gc_mark_gv(mrb); /* mark arena */ - for (i=0,e=mrb->arena_idx; i<e; i++) { - mrb_gc_mark(mrb, mrb->arena[i]); + for (i=0,e=gc->arena_idx; i<e; i++) { + mrb_gc_mark(mrb, gc->arena[i]); } /* mark class hierarchy */ mrb_gc_mark(mrb, (struct RBasic*)mrb->object_class); @@ -718,11 +811,11 @@ root_scan_phase(mrb_state *mrb) } static size_t -gc_gray_mark(mrb_state *mrb, struct RBasic *obj) +gc_gray_mark(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) { size_t children = 0; - gc_mark_children(mrb, obj); + gc_mark_children(mrb, gc, obj); switch (obj->tt) { case MRB_TT_ICLASS: @@ -743,6 +836,7 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj) case MRB_TT_OBJECT: case MRB_TT_DATA: + case MRB_TT_EXCEPTION: children += mrb_gc_mark_iv_size(mrb, (struct RObject*)obj); break; @@ -800,68 +894,68 @@ gc_gray_mark(mrb_state *mrb, struct RBasic *obj) static void -gc_mark_gray_list(mrb_state *mrb) { - while (mrb->gray_list) { - if (is_gray(mrb->gray_list)) - gc_mark_children(mrb, mrb->gray_list); +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 - mrb->gray_list = mrb->gray_list->gcnext; + gc->gray_list = gc->gray_list->gcnext; } } static size_t -incremental_marking_phase(mrb_state *mrb, size_t limit) +incremental_marking_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) { size_t tried_marks = 0; - while (mrb->gray_list && tried_marks < limit) { - tried_marks += gc_gray_mark(mrb, mrb->gray_list); + while (gc->gray_list && tried_marks < limit) { + tried_marks += gc_gray_mark(mrb, gc, gc->gray_list); } return tried_marks; } static void -final_marking_phase(mrb_state *mrb) +final_marking_phase(mrb_state *mrb, mrb_gc *gc) { mark_context_stack(mrb, mrb->root_c); - gc_mark_gray_list(mrb); - mrb_assert(mrb->gray_list == NULL); - mrb->gray_list = mrb->atomic_gray_list; - mrb->atomic_gray_list = NULL; - gc_mark_gray_list(mrb); - mrb_assert(mrb->gray_list == NULL); + gc_mark_gray_list(mrb, gc); + mrb_assert(gc->gray_list == NULL); + gc->gray_list = gc->atomic_gray_list; + gc->atomic_gray_list = NULL; + gc_mark_gray_list(mrb, gc); + mrb_assert(gc->gray_list == NULL); } static void -prepare_incremental_sweep(mrb_state *mrb) +prepare_incremental_sweep(mrb_state *mrb, mrb_gc *gc) { - mrb->gc_state = GC_STATE_SWEEP; - mrb->sweeps = mrb->heaps; - mrb->gc_live_after_mark = mrb->live; + gc->state = MRB_GC_STATE_SWEEP; + gc->sweeps = gc->heaps; + gc->live_after_mark = gc->live; } static size_t -incremental_sweep_phase(mrb_state *mrb, size_t limit) +incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit) { - struct heap_page *page = mrb->sweeps; + mrb_heap_page *page = gc->sweeps; size_t tried_sweep = 0; while (page && (tried_sweep < limit)) { - RVALUE *p = page->objects; + RVALUE *p = objects(page); RVALUE *e = p + MRB_HEAP_PAGE_SIZE; size_t freed = 0; mrb_bool dead_slot = TRUE; - int full = (page->freelist == NULL); + mrb_bool full = (page->freelist == NULL); - if (is_minor_gc(mrb) && page->old) { + if (is_minor_gc(gc) && page->old) { /* skip a slot which doesn't contain any young object */ p = e; dead_slot = FALSE; } while (p<e) { - if (is_dead(mrb, &p->as.basic)) { + if (is_dead(gc, &p->as.basic)) { if (p->as.basic.tt != MRB_TT_FREE) { obj_free(mrb, &p->as.basic); p->as.free.next = page->freelist; @@ -870,8 +964,8 @@ incremental_sweep_phase(mrb_state *mrb, size_t limit) } } else { - if (!is_generational(mrb)) - paint_partial_white(mrb, &p->as.basic); /* next gc target */ + if (!is_generational(gc)) + paint_partial_white(gc, &p->as.basic); /* next gc target */ dead_slot = 0; } p++; @@ -879,54 +973,54 @@ incremental_sweep_phase(mrb_state *mrb, size_t limit) /* free dead slot */ if (dead_slot && freed < MRB_HEAP_PAGE_SIZE) { - struct heap_page *next = page->next; + mrb_heap_page *next = page->next; - unlink_heap_page(mrb, page); - unlink_free_heap_page(mrb, page); + unlink_heap_page(gc, page); + unlink_free_heap_page(gc, page); mrb_free(mrb, page); page = next; } else { if (full && freed > 0) { - link_free_heap_page(mrb, page); + link_free_heap_page(gc, page); } - if (page->freelist == NULL && is_minor_gc(mrb)) + if (page->freelist == NULL && is_minor_gc(gc)) page->old = TRUE; else page->old = FALSE; page = page->next; } tried_sweep += MRB_HEAP_PAGE_SIZE; - mrb->live -= freed; - mrb->gc_live_after_mark -= freed; + gc->live -= freed; + gc->live_after_mark -= freed; } - mrb->sweeps = page; + gc->sweeps = page; return tried_sweep; } static size_t -incremental_gc(mrb_state *mrb, size_t limit) +incremental_gc(mrb_state *mrb, mrb_gc *gc, size_t limit) { - switch (mrb->gc_state) { - case GC_STATE_NONE: - root_scan_phase(mrb); - mrb->gc_state = GC_STATE_MARK; - flip_white_part(mrb); + switch (gc->state) { + case MRB_GC_STATE_ROOT: + root_scan_phase(mrb, gc); + gc->state = MRB_GC_STATE_MARK; + flip_white_part(gc); return 0; - case GC_STATE_MARK: - if (mrb->gray_list) { - return incremental_marking_phase(mrb, limit); + case MRB_GC_STATE_MARK: + if (gc->gray_list) { + return incremental_marking_phase(mrb, gc, limit); } else { - final_marking_phase(mrb); - prepare_incremental_sweep(mrb); + final_marking_phase(mrb, gc); + prepare_incremental_sweep(mrb, gc); return 0; } - case GC_STATE_SWEEP: { + case MRB_GC_STATE_SWEEP: { size_t tried_sweep = 0; - tried_sweep = incremental_sweep_phase(mrb, limit); + tried_sweep = incremental_sweep_phase(mrb, gc, limit); if (tried_sweep == 0) - mrb->gc_state = GC_STATE_NONE; + gc->state = MRB_GC_STATE_ROOT; return tried_sweep; } default: @@ -937,79 +1031,81 @@ incremental_gc(mrb_state *mrb, size_t limit) } static void -incremental_gc_until(mrb_state *mrb, enum gc_state to_state) +incremental_gc_until(mrb_state *mrb, mrb_gc *gc, mrb_gc_state to_state) { do { - incremental_gc(mrb, ~0); - } while (mrb->gc_state != to_state); + incremental_gc(mrb, gc, SIZE_MAX); + } while (gc->state != to_state); } static void -incremental_gc_step(mrb_state *mrb) +incremental_gc_step(mrb_state *mrb, mrb_gc *gc) { size_t limit = 0, result = 0; - limit = (GC_STEP_SIZE/100) * mrb->gc_step_ratio; + limit = (GC_STEP_SIZE/100) * gc->step_ratio; while (result < limit) { - result += incremental_gc(mrb, limit); - if (mrb->gc_state == GC_STATE_NONE) + result += incremental_gc(mrb, gc, limit); + if (gc->state == MRB_GC_STATE_ROOT) break; } - mrb->gc_threshold = mrb->live + GC_STEP_SIZE; + gc->threshold = gc->live + GC_STEP_SIZE; } static void -clear_all_old(mrb_state *mrb) +clear_all_old(mrb_state *mrb, mrb_gc *gc) { - mrb_bool origin_mode = mrb->is_generational_gc_mode; + mrb_bool origin_mode = gc->generational; - mrb_assert(is_generational(mrb)); - if (is_major_gc(mrb)) { + mrb_assert(is_generational(gc)); + if (is_major_gc(gc)) { /* finish the half baked GC */ - incremental_gc_until(mrb, GC_STATE_NONE); + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } /* Sweep the dead objects, then reset all the live objects * (including all the old objects, of course) to white. */ - mrb->is_generational_gc_mode = FALSE; - prepare_incremental_sweep(mrb); - incremental_gc_until(mrb, GC_STATE_NONE); - mrb->is_generational_gc_mode = origin_mode; + gc->generational = FALSE; + prepare_incremental_sweep(mrb, gc); + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); + gc->generational = origin_mode; - /* The gray objects has already been painted as white */ - mrb->atomic_gray_list = mrb->gray_list = NULL; + /* The gray objects have already been painted as white */ + gc->atomic_gray_list = gc->gray_list = NULL; } -void +MRB_API void mrb_incremental_gc(mrb_state *mrb) { - if (mrb->gc_disabled) return; + mrb_gc *gc = &mrb->gc; + + if (gc->disabled) return; GC_INVOKE_TIME_REPORT("mrb_incremental_gc()"); GC_TIME_START; - if (is_minor_gc(mrb)) { - incremental_gc_until(mrb, GC_STATE_NONE); + if (is_minor_gc(gc)) { + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } else { - incremental_gc_step(mrb); + incremental_gc_step(mrb, gc); } - if (mrb->gc_state == GC_STATE_NONE) { - mrb_assert(mrb->live >= mrb->gc_live_after_mark); - mrb->gc_threshold = (mrb->gc_live_after_mark/100) * mrb->gc_interval_ratio; - if (mrb->gc_threshold < GC_STEP_SIZE) { - mrb->gc_threshold = GC_STEP_SIZE; + if (gc->state == MRB_GC_STATE_ROOT) { + mrb_assert(gc->live >= gc->live_after_mark); + gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; + if (gc->threshold < GC_STEP_SIZE) { + gc->threshold = GC_STEP_SIZE; } - if (is_major_gc(mrb)) { - mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; - mrb->gc_full = FALSE; + if (is_major_gc(gc)) { + gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; + gc->full = FALSE; } - else if (is_minor_gc(mrb)) { - if (mrb->live > mrb->majorgc_old_threshold) { - clear_all_old(mrb); - mrb->gc_full = TRUE; + else if (is_minor_gc(gc)) { + if (gc->live > gc->majorgc_old_threshold) { + clear_all_old(mrb, gc); + gc->full = TRUE; } } } @@ -1018,64 +1114,69 @@ mrb_incremental_gc(mrb_state *mrb) } /* Perform a full gc cycle */ -void +MRB_API void mrb_full_gc(mrb_state *mrb) { - if (mrb->gc_disabled) return; + mrb_gc *gc = &mrb->gc; + + if (gc->disabled) return; + GC_INVOKE_TIME_REPORT("mrb_full_gc()"); GC_TIME_START; - if (is_generational(mrb)) { + if (is_generational(gc)) { /* clear all the old objects back to young */ - clear_all_old(mrb); - mrb->gc_full = TRUE; + clear_all_old(mrb, gc); + gc->full = TRUE; } - else if (mrb->gc_state != GC_STATE_NONE) { + else if (gc->state != MRB_GC_STATE_ROOT) { /* finish half baked GC cycle */ - incremental_gc_until(mrb, GC_STATE_NONE); + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); } - incremental_gc_until(mrb, GC_STATE_NONE); - mrb->gc_threshold = (mrb->gc_live_after_mark/100) * mrb->gc_interval_ratio; + incremental_gc_until(mrb, gc, MRB_GC_STATE_ROOT); + gc->threshold = (gc->live_after_mark/100) * gc->interval_ratio; - if (is_generational(mrb)) { - mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; - mrb->gc_full = FALSE; + if (is_generational(gc)) { + gc->majorgc_old_threshold = gc->live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; + gc->full = FALSE; } GC_TIME_STOP_AND_REPORT; } -void +MRB_API void mrb_garbage_collect(mrb_state *mrb) { mrb_full_gc(mrb); } -int +MRB_API int mrb_gc_arena_save(mrb_state *mrb) { - return mrb->arena_idx; + return mrb->gc.arena_idx; } -void +MRB_API void mrb_gc_arena_restore(mrb_state *mrb, int idx) { + mrb_gc *gc = &mrb->gc; + #ifndef MRB_GC_FIXED_ARENA - int capa = mrb->arena_capa; + int capa = gc->arena_capa; if (idx < capa / 2) { capa = (int)(capa * 0.66); if (capa < MRB_GC_ARENA_SIZE) { capa = MRB_GC_ARENA_SIZE; } - if (capa != mrb->arena_capa) { - mrb->arena = (struct RBasic**)mrb_realloc(mrb, mrb->arena, sizeof(struct RBasic*)*capa); - mrb->arena_capa = capa; + if (capa != gc->arena_capa) { + gc->arena = (struct RBasic**)mrb_realloc(mrb, gc->arena, sizeof(struct RBasic*)*capa); + gc->arena_capa = capa; } } #endif - mrb->arena_idx = idx; + gc->arena_idx = idx; } /* @@ -1083,21 +1184,23 @@ mrb_gc_arena_restore(mrb_state *mrb, int idx) * Paint obj(Black) -> value(White) to obj(Black) -> value(Gray). */ -void +MRB_API void mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value) { + mrb_gc *gc = &mrb->gc; + if (!is_black(obj)) return; if (!is_white(value)) return; - mrb_assert(!is_dead(mrb, value) && !is_dead(mrb, obj)); - mrb_assert(is_generational(mrb) || mrb->gc_state != GC_STATE_NONE); + mrb_assert(gc->state == MRB_GC_STATE_MARK || (!is_dead(gc, value) && !is_dead(gc, obj))); + mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); - if (is_generational(mrb) || mrb->gc_state == GC_STATE_MARK) { - add_gray_list(mrb, value); + if (is_generational(gc) || gc->state == MRB_GC_STATE_MARK) { + add_gray_list(mrb, gc, value); } else { - mrb_assert(mrb->gc_state == GC_STATE_SWEEP); - paint_partial_white(mrb, obj); /* for never write barriers */ + mrb_assert(gc->state == MRB_GC_STATE_SWEEP); + paint_partial_white(gc, obj); /* for never write barriers */ } } @@ -1110,16 +1213,18 @@ mrb_field_write_barrier(mrb_state *mrb, struct RBasic *obj, struct RBasic *value * e.g. Set element on Array. */ -void +MRB_API void mrb_write_barrier(mrb_state *mrb, struct RBasic *obj) { + mrb_gc *gc = &mrb->gc; + if (!is_black(obj)) return; - mrb_assert(!is_dead(mrb, obj)); - mrb_assert(is_generational(mrb) || mrb->gc_state != GC_STATE_NONE); + mrb_assert(!is_dead(gc, obj)); + mrb_assert(is_generational(gc) || gc->state != MRB_GC_STATE_ROOT); paint_gray(obj); - obj->gcnext = mrb->atomic_gray_list; - mrb->atomic_gray_list = obj; + obj->gcnext = gc->atomic_gray_list; + gc->atomic_gray_list = obj; } /* @@ -1153,9 +1258,9 @@ gc_start(mrb_state *mrb, mrb_value obj) static mrb_value gc_enable(mrb_state *mrb, mrb_value obj) { - int old = mrb->gc_disabled; + mrb_bool old = mrb->gc.disabled; - mrb->gc_disabled = FALSE; + mrb->gc.disabled = FALSE; return mrb_bool_value(old); } @@ -1175,9 +1280,9 @@ gc_enable(mrb_state *mrb, mrb_value obj) static mrb_value gc_disable(mrb_state *mrb, mrb_value obj) { - int old = mrb->gc_disabled; + mrb_bool old = mrb->gc.disabled; - mrb->gc_disabled = TRUE; + mrb->gc.disabled = TRUE; return mrb_bool_value(old); } @@ -1193,7 +1298,7 @@ gc_disable(mrb_state *mrb, mrb_value obj) static mrb_value gc_interval_ratio_get(mrb_state *mrb, mrb_value obj) { - return mrb_fixnum_value(mrb->gc_interval_ratio); + return mrb_fixnum_value(mrb->gc.interval_ratio); } /* @@ -1211,7 +1316,7 @@ gc_interval_ratio_set(mrb_state *mrb, mrb_value obj) mrb_int ratio; mrb_get_args(mrb, "i", &ratio); - mrb->gc_interval_ratio = ratio; + mrb->gc.interval_ratio = ratio; return mrb_nil_value(); } @@ -1226,7 +1331,7 @@ gc_interval_ratio_set(mrb_state *mrb, mrb_value obj) static mrb_value gc_step_ratio_get(mrb_state *mrb, mrb_value obj) { - return mrb_fixnum_value(mrb->gc_step_ratio); + return mrb_fixnum_value(mrb->gc.step_ratio); } /* @@ -1244,24 +1349,24 @@ gc_step_ratio_set(mrb_state *mrb, mrb_value obj) mrb_int ratio; mrb_get_args(mrb, "i", &ratio); - mrb->gc_step_ratio = ratio; + mrb->gc.step_ratio = ratio; return mrb_nil_value(); } static void -change_gen_gc_mode(mrb_state *mrb, mrb_int enable) +change_gen_gc_mode(mrb_state *mrb, mrb_gc *gc, mrb_bool enable) { - if (is_generational(mrb) && !enable) { - clear_all_old(mrb); - mrb_assert(mrb->gc_state == GC_STATE_NONE); - mrb->gc_full = FALSE; + if (is_generational(gc) && !enable) { + clear_all_old(mrb, gc); + mrb_assert(gc->state == MRB_GC_STATE_ROOT); + gc->full = FALSE; } - else if (!is_generational(mrb) && enable) { - incremental_gc_until(mrb, GC_STATE_NONE); - mrb->majorgc_old_threshold = mrb->gc_live_after_mark/100 * DEFAULT_MAJOR_GC_INC_RATIO; - mrb->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; } - mrb->is_generational_gc_mode = enable; + gc->generational = enable; } /* @@ -1275,7 +1380,7 @@ change_gen_gc_mode(mrb_state *mrb, mrb_int enable) static mrb_value gc_generational_mode_get(mrb_state *mrb, mrb_value self) { - return mrb_bool_value(mrb->is_generational_gc_mode); + return mrb_bool_value(mrb->gc.generational); } /* @@ -1292,21 +1397,22 @@ gc_generational_mode_set(mrb_state *mrb, mrb_value self) mrb_bool enable; mrb_get_args(mrb, "b", &enable); - if (mrb->is_generational_gc_mode != enable) - change_gen_gc_mode(mrb, enable); + if (mrb->gc.generational != enable) + change_gen_gc_mode(mrb, &mrb->gc, enable); return mrb_bool_value(enable); } -void -mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data) + +static void +gc_each_objects(mrb_state *mrb, mrb_gc *gc, mrb_each_object_callback *callback, void *data) { - struct heap_page* page = mrb->heaps; + mrb_heap_page* page = gc->heaps; while (page != NULL) { RVALUE *p, *pend; - p = page->objects; + p = objects(page); pend = p + MRB_HEAP_PAGE_SIZE; for (;p < pend; p++) { (*callback)(mrb, &p->as.basic, data); @@ -1316,6 +1422,12 @@ mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, vo } } +void +mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data) +{ + gc_each_objects(mrb, &mrb->gc, callback, data); +} + #ifdef GC_TEST #ifdef GC_DEBUG static mrb_value gc_test(mrb_state *, mrb_value); @@ -1352,42 +1464,43 @@ 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"); - mrb->is_generational_gc_mode = FALSE; + 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(mrb,value); + paint_partial_white(gc, value); - puts(" in GC_STATE_MARK"); - mrb->gc_state = GC_STATE_MARK; + 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 GC_STATE_SWEEP"); - paint_partial_white(mrb,value); - mrb->gc_state = GC_STATE_SWEEP; + 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 & mrb->current_white_part); - mrb_assert(value->color & mrb->current_white_part); + mrb_assert(obj->color & gc->current_white_part); + mrb_assert(value->color & gc->current_white_part); puts(" fail with black"); - mrb->gc_state = GC_STATE_MARK; + gc->state = MRB_GC_STATE_MARK; paint_white(obj); - paint_partial_white(mrb,value); + paint_partial_white(gc, value); mrb_field_write_barrier(mrb, obj, value); - mrb_assert(obj->color & mrb->current_white_part); + mrb_assert(obj->color & gc->current_white_part); puts(" fail with gray"); - mrb->gc_state = GC_STATE_MARK; + gc->state = MRB_GC_STATE_MARK; paint_black(obj); paint_gray(value); mrb_field_write_barrier(mrb, obj, value); @@ -1400,9 +1513,9 @@ test_mrb_field_write_barrier(void) obj = mrb_basic_ptr(mrb_ary_new(mrb)); mrb_value value = mrb_str_new_lit(mrb, "value"); paint_black(obj); - paint_partial_white(mrb, mrb_basic_ptr(value)); + paint_partial_white(gc, mrb_basic_ptr(value)); - mrb->gc_state = GC_STATE_MARK; + gc->state = MRB_GC_STATE_MARK; mrb_field_write_barrier_value(mrb, obj, value); mrb_assert(is_gray(mrb_basic_ptr(value))); @@ -1416,17 +1529,18 @@ 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 GC_STATE_MARK"); - mrb->gc_state = GC_STATE_MARK; + puts(" in MRB_GC_STATE_MARK"); + gc->state = MRB_GC_STATE_MARK; mrb_write_barrier(mrb, obj); mrb_assert(is_gray(obj)); - mrb_assert(mrb->atomic_gray_list == obj); + mrb_assert(gc->atomic_gray_list == obj); puts(" fail with gray"); @@ -1443,19 +1557,20 @@ 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, FALSE); - mrb_assert(mrb->gray_list == NULL); + 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, obj1); - mrb_assert(mrb->gray_list == obj1); + 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, obj2); - mrb_assert(mrb->gray_list == obj2); - mrb_assert(mrb->gray_list->gcnext == obj1); + 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); @@ -1468,13 +1583,14 @@ test_gc_gray_mark(void) 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, obj); + gray_num = gc_gray_mark(mrb, gc, obj); mrb_assert(is_black(obj)); mrb_assert(gray_num > 1); @@ -1482,9 +1598,9 @@ test_gc_gray_mark(void) obj_v = mrb_ary_new(mrb); value_v = mrb_str_new_lit(mrb, "test"); paint_gray(mrb_basic_ptr(obj_v)); - paint_partial_white(mrb, mrb_basic_ptr(value_v)); + paint_partial_white(gc, mrb_basic_ptr(value_v)); mrb_ary_push(mrb, obj_v, value_v); - gray_num = gc_gray_mark(mrb, mrb_basic_ptr(obj_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); @@ -1498,32 +1614,33 @@ test_incremental_gc(void) mrb_state *mrb = mrb_open(); size_t max = ~0, live = 0, total = 0, freed = 0; RVALUE *free; - struct heap_page *page; + mrb_heap_page *page; + mrb_gc *gc = &mrb->gc; puts("test_incremental_gc"); - change_gen_gc_mode(mrb, FALSE); + change_gen_gc_mode(mrb, gc, FALSE); puts(" in mrb_full_gc"); mrb_full_gc(mrb); - mrb_assert(mrb->gc_state == GC_STATE_NONE); - puts(" in GC_STATE_NONE"); - incremental_gc(mrb, max); - mrb_assert(mrb->gc_state == GC_STATE_MARK); - puts(" in GC_STATE_MARK"); - incremental_gc_until(mrb, GC_STATE_SWEEP); - mrb_assert(mrb->gc_state == GC_STATE_SWEEP); + 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 GC_STATE_SWEEP"); - page = mrb->heaps; + puts(" in MRB_GC_STATE_SWEEP"); + page = gc->heaps; while (page) { - RVALUE *p = page->objects; + 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(mrb, &p->as.basic)) { + if (is_gray(&p->as.basic) && !is_dead(gc, &p->as.basic)) { printf("%p\n", &p->as.basic); } p++; @@ -1532,44 +1649,44 @@ test_incremental_gc(void) total += MRB_HEAP_PAGE_SIZE; } - mrb_assert(mrb->gray_list == NULL); + mrb_assert(gc->gray_list == NULL); - incremental_gc(mrb, max); - mrb_assert(mrb->gc_state == GC_STATE_SWEEP); + incremental_gc(mrb, gc, max); + mrb_assert(gc->state == MRB_GC_STATE_SWEEP); - incremental_gc(mrb, max); - mrb_assert(mrb->gc_state == GC_STATE_NONE); + incremental_gc(mrb, gc, max); + mrb_assert(gc->state == MRB_GC_STATE_ROOT); - free = (RVALUE*)mrb->heaps->freelist; + free = (RVALUE*)gc->heaps->freelist; while (free) { freed++; free = (RVALUE*)free->as.free.next; } - mrb_assert(mrb->live == live); - mrb_assert(mrb->live == total-freed); + mrb_assert(gc->live == live); + mrb_assert(gc->live == total-freed); puts("test_incremental_gc(gen)"); - incremental_gc_until(mrb, GC_STATE_SWEEP); - change_gen_gc_mode(mrb, TRUE); + incremental_gc_until(mrb, gc, MRB_GC_STATE_SWEEP); + change_gen_gc_mode(mrb, gc, TRUE); - mrb_assert(mrb->gc_full == FALSE); - mrb_assert(mrb->gc_state == GC_STATE_NONE); + mrb_assert(gc->full == FALSE); + mrb_assert(gc->state == MRB_GC_STATE_ROOT); puts(" in minor"); - mrb_assert(is_minor_gc(mrb)); - mrb_assert(mrb->majorgc_old_threshold > 0); - mrb->majorgc_old_threshold = 0; + mrb_assert(is_minor_gc(gc)); + mrb_assert(gc->majorgc_old_threshold > 0); + gc->majorgc_old_threshold = 0; mrb_incremental_gc(mrb); - mrb_assert(mrb->gc_full == TRUE); - mrb_assert(mrb->gc_state == GC_STATE_NONE); + mrb_assert(gc->full == TRUE); + mrb_assert(gc->state == MRB_GC_STATE_ROOT); puts(" in major"); - mrb_assert(is_major_gc(mrb)); + mrb_assert(is_major_gc(gc)); do { mrb_incremental_gc(mrb); - } while (mrb->gc_state != GC_STATE_NONE); - mrb_assert(mrb->gc_full == FALSE); + } while (gc->state != MRB_GC_STATE_ROOT); + mrb_assert(gc->full == FALSE); mrb_close(mrb); } @@ -1578,18 +1695,19 @@ void test_incremental_sweep_phase(void) { mrb_state *mrb = mrb_open(); + mrb_gc *gc = &mrb->gc; puts("test_incremental_sweep_phase"); - add_heap(mrb); - mrb->sweeps = mrb->heaps; + add_heap(mrb, gc); + gc->sweeps = gc->heaps; - mrb_assert(mrb->heaps->next->next == NULL); - mrb_assert(mrb->free_heaps->next->next == NULL); - incremental_sweep_phase(mrb, MRB_HEAP_PAGE_SIZE*3); + mrb_assert(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(mrb->heaps->next == NULL); - mrb_assert(mrb->heaps == mrb->free_heaps); + mrb_assert(gc->heaps->next == NULL); + mrb_assert(gc->heaps == gc->free_heaps); mrb_close(mrb); } diff --git a/src/hash.c b/src/hash.c index 1d449db3f..ac7256987 100644 --- a/src/hash.c +++ b/src/hash.c @@ -4,13 +4,13 @@ ** 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" +#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); @@ -36,20 +36,20 @@ mrb_hash_ht_hash_func(mrb_state *mrb, mrb_value key) case MRB_TT_SYMBOL: h = (khint_t)mrb_symbol(key); - return kh_int_hash_func(mrb,h); + return kh_int_hash_func(mrb, h); case MRB_TT_FIXNUM: h = (khint_t)mrb_float_id((mrb_float)mrb_fixnum(key)); - return kh_int_hash_func(mrb,h); + return kh_int_hash_func(mrb, h); case MRB_TT_FLOAT: h = (khint_t)mrb_float_id(mrb_float(key)); - return kh_int_hash_func(mrb,h); + return kh_int_hash_func(mrb, h); default: hv = mrb_funcall(mrb, key, "hash", 0); h = (khint_t)t ^ mrb_fixnum(hv); - return kh_int_hash_func(mrb,h); + return kh_int_hash_func(mrb, h); } } @@ -104,10 +104,11 @@ 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)) - return mrb_str_dup(mrb, key); - else - return key; + if (mrb_string_p(key) && !RSTR_FROZEN_P(mrb_str_ptr(key))) { + key = mrb_str_dup(mrb, key); + RSTR_SET_FROZEN_FLAG(mrb_str_ptr(key)); + } + return key; } #define KEY(key) mrb_hash_ht_key(mrb, key) @@ -144,7 +145,7 @@ mrb_gc_free_hash(mrb_state *mrb, struct RHash *hash) } -mrb_value +MRB_API mrb_value mrb_hash_new_capa(mrb_state *mrb, int capa) { struct RHash *h; @@ -158,13 +159,13 @@ mrb_hash_new_capa(mrb_state *mrb, int capa) return mrb_obj_value(h); } -mrb_value +MRB_API mrb_value mrb_hash_new(mrb_state *mrb) { return mrb_hash_new_capa(mrb, 0); } -mrb_value +MRB_API mrb_value mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key) { khash_t(ht) *h = RHASH_TBL(hash); @@ -183,7 +184,7 @@ mrb_hash_get(mrb_state *mrb, mrb_value hash, mrb_value key) return RHASH_IFNONE(hash); } -mrb_value +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); @@ -199,7 +200,7 @@ mrb_hash_fetch(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value def) return def; } -void +MRB_API void mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val) { khash_t(ht) *h; @@ -241,11 +242,11 @@ mrb_hash_dup(mrb_state *mrb, mrb_value hash) ret_h = ret->ht; for (k = kh_begin(h); k != kh_end(h); k++) { - if (kh_exist(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))); + ret_k = kh_put(ht, mrb, ret_h, KEY(kh_key(h, k))); mrb_gc_arena_restore(mrb, ai); - kh_val(ret_h, ret_k) = kh_val(h,k); + kh_val(ret_h, ret_k) = kh_val(h, k); } } } @@ -253,19 +254,19 @@ mrb_hash_dup(mrb_state *mrb, mrb_value hash) return mrb_obj_value(ret); } -mrb_value +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"); } -khash_t(ht) * +MRB_API khash_t(ht)* mrb_hash_tbl(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); if (!h) { - RHASH_TBL(hash) = kh_init(ht, mrb); + return RHASH_TBL(hash) = kh_init(ht, mrb); } return h; } @@ -293,22 +294,22 @@ mrb_hash_modify(mrb_state *mrb, mrb_value hash) * default value. It is the block's responsibility to store the value * in the hash if required. * - * h = Hash.new("Go Fish") - * h["a"] = 100 - * h["b"] = 200 - * h["a"] #=> 100 - * h["c"] #=> "Go Fish" - * # The following alters the single default object - * h["c"].upcase! #=> "GO FISH" - * h["d"] #=> "GO FISH" - * h.keys #=> ["a", "b"] - * - * # While this creates a new default object each time - * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" } - * h["c"] #=> "Go Fish: c" - * h["c"].upcase! #=> "GO FISH: C" - * h["d"] #=> "Go Fish: d" - * h.keys #=> ["c", "d"] + * h = Hash.new("Go Fish") + * h["a"] = 100 + * h["b"] = 200 + * h["a"] #=> 100 + * h["c"] #=> "Go Fish" + * # The following alters the single default object + * h["c"].upcase! #=> "GO FISH" + * h["d"] #=> "GO FISH" + * h.keys #=> ["a", "b"] + * + * # While this creates a new default object each time + * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" } + * h["c"] #=> "Go Fish: c" + * h["c"].upcase! #=> "GO FISH: C" + * h["d"] #=> "Go Fish: d" + * h.keys #=> ["c", "d"] * */ @@ -478,7 +479,7 @@ mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash) return ifnone; } -mrb_value +MRB_API mrb_value mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key) { khash_t(ht) *h = RHASH_TBL(hash); @@ -516,10 +517,10 @@ mrb_hash_delete_key(mrb_state *mrb, mrb_value hash, mrb_value key) * 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" + * 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 @@ -540,9 +541,9 @@ mrb_hash_delete(mrb_state *mrb, mrb_value self) * two-item array <code>[</code> <i>key, value</i> <code>]</code>, or * the hash's default value if the hash is empty. * - * h = { 1 => "a", 2 => "b", 3 => "c" } - * h.shift #=> [1, "a"] - * h #=> {2=>"b", 3=>"c"} + * h = { 1 => "a", 2 => "b", 3 => "c" } + * h.shift #=> [1, "a"] + * h #=> {2=>"b", 3=>"c"} */ static mrb_value @@ -553,18 +554,16 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash) mrb_value delKey, delVal; mrb_hash_modify(mrb, hash); - if (h) { - if (kh_size(h) > 0) { - for (k = kh_begin(h); k != kh_end(h); k++) { - if (!kh_exist(h,k)) continue; + 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); + 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); - } + return mrb_assoc_new(mrb, delKey, delVal); } } @@ -581,14 +580,14 @@ mrb_hash_shift(mrb_state *mrb, mrb_value hash) * call-seq: * hsh.clear -> hsh * - * Removes all key-value pairs from <i>hsh</i>. + * Removes all key-value pairs from `hsh`. * - * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200} - * h.clear #=> {} + * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200} + * h.clear #=> {} * */ -mrb_value +MRB_API mrb_value mrb_hash_clear(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); @@ -610,10 +609,10 @@ mrb_hash_clear(mrb_state *mrb, mrb_value hash) * use as a key (a <code>String</code> passed as a key will be * duplicated and frozen). * - * h = { "a" => 100, "b" => 200 } - * h["a"] = 9 - * h["c"] = 4 - * h #=> {"a"=>9, "b"=>200, "c"=>4} + * h = { "a" => 100, "b" => 200 } + * h["a"] = 9 + * h["c"] = 4 + * h #=> {"a"=>9, "b"=>200, "c"=>4} * */ static mrb_value @@ -659,7 +658,7 @@ mrb_hash_size_m(mrb_state *mrb, mrb_value self) * {}.empty? #=> true * */ -mrb_value +MRB_API mrb_value mrb_hash_empty_p(mrb_state *mrb, mrb_value self) { khash_t(ht) *h = RHASH_TBL(self); @@ -695,21 +694,22 @@ mrb_hash_to_hash(mrb_state *mrb, mrb_value hash) * */ -mrb_value +MRB_API mrb_value mrb_hash_keys(mrb_state *mrb, mrb_value hash) { khash_t(ht) *h = RHASH_TBL(hash); khiter_t k; - mrb_value ary, *p; + mrb_value ary; + mrb_value *p; if (!h || kh_size(h) == 0) return mrb_ary_new(mrb); ary = mrb_ary_new_capa(mrb, kh_size(h)); mrb_ary_set(mrb, ary, kh_size(h)-1, mrb_nil_value()); - p = RARRAY_PTR(ary); + p = mrb_ary_ptr(ary)->ptr; for (k = kh_begin(h); k != kh_end(h); k++) { if (kh_exist(h, k)) { - mrb_value kv = kh_key(h,k); - mrb_hash_value hv = kh_value(h,k); + mrb_value kv = kh_key(h, k); + mrb_hash_value hv = kh_value(h, k); p[hv.n] = kv; } @@ -741,7 +741,7 @@ mrb_hash_values(mrb_state *mrb, mrb_value hash) 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_hash_value hv = kh_value(h, k); mrb_ary_set(mrb, ary, hv.n, hv.v); } @@ -814,7 +814,7 @@ mrb_hash_has_value(mrb_state *mrb, mrb_value hash) 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)) { + if (mrb_equal(mrb, kh_value(h, k).v, val)) { return mrb_true_value(); } } @@ -827,7 +827,7 @@ mrb_init_hash(mrb_state *mrb) { struct RClass *h; - h = mrb->hash_class = mrb_define_class(mrb, "Hash", mrb->object_class); /* 15.2.13 */ + mrb->hash_class = h = mrb_define_class(mrb, "Hash", mrb->object_class); /* 15.2.13 */ MRB_SET_INSTANCE_TT(h, MRB_TT_HASH); mrb_define_method(mrb, h, "[]", mrb_hash_aget, MRB_ARGS_REQ(1)); /* 15.2.13.4.2 */ diff --git a/src/init.c b/src/init.c index 9489cea10..9a6496df1 100644 --- a/src/init.c +++ b/src/init.c @@ -4,7 +4,7 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" +#include <mruby.h> void mrb_init_symtbl(mrb_state*); void mrb_init_class(mrb_state*); @@ -24,8 +24,6 @@ void mrb_init_gc(mrb_state*); void mrb_init_math(mrb_state*); void mrb_init_version(mrb_state*); void mrb_init_mrblib(mrb_state*); -void mrb_init_mrbgems(mrb_state*); -void mrb_final_mrbgems(mrb_state*); #define DONE mrb_gc_arena_restore(mrb, 0); void @@ -50,7 +48,4 @@ mrb_init_core(mrb_state *mrb) mrb_init_gc(mrb); DONE; mrb_init_version(mrb); DONE; mrb_init_mrblib(mrb); DONE; -#ifndef DISABLE_GEMS - mrb_init_mrbgems(mrb); DONE; -#endif } diff --git a/src/kernel.c b/src/kernel.c index 0258e5c15..af6a49be1 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -4,13 +4,13 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/proc.h" -#include "mruby/string.h" -#include "mruby/variable.h" -#include "mruby/error.h" +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/proc.h> +#include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/error.h> typedef enum { NOEX_PUBLIC = 0x00, @@ -30,7 +30,7 @@ static mrb_bool mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj) { struct RProc *me = mrb_method_search(mrb, mrb_class(mrb, obj), mrb_intern_lit(mrb, "to_s")); - if (me && MRB_PROC_CFUNC_P(me) && (me->body.func == mrb_any_to_s)) + if (MRB_PROC_CFUNC_P(me) && (me->body.func == mrb_any_to_s)) return TRUE; return FALSE; } @@ -49,7 +49,7 @@ mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj) * [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]" * Time.new.inspect #=> "2008-03-08 19:43:39 +0900" */ -mrb_value +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)) { @@ -186,16 +186,26 @@ mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self) } else { /* block_given? called within block; check upper scope */ - if (ci->proc->env && ci->proc->env->stack) { - given_p = !(ci->proc->env->stack == mrb->c->stbase || - mrb_nil_p(ci->proc->env->stack[1])); - } - else { - if (ci->argc > 0) { - bp += ci->argc; + if (ci->proc->env) { + struct REnv *e = ci->proc->env; + mrb_value *sp; + + while (e->c) { + e = (struct REnv*)e->c; + } + sp = e->stack; + if (sp) { + /* top-level does not have block slot (alway false) */ + if (sp == mrb->c->stbase) + return mrb_false_value(); + ci = mrb->c->cibase + e->cioff; + bp = ci[1].stackent + 1; } - given_p = !mrb_nil_p(*bp); } + if (ci->argc > 0) { + bp += ci->argc; + } + given_p = !mrb_nil_p(*bp); } return mrb_bool_value(given_p); @@ -230,14 +240,12 @@ mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj) /* copy singleton(unnamed) class */ struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class); - if ((mrb_type(obj) == MRB_TT_CLASS) || - (mrb_type(obj) == MRB_TT_SCLASS)) { /* BUILTIN_TYPE(obj) == T_CLASS */ + if ((mrb_type(obj) == MRB_TT_CLASS) || (mrb_type(obj) == MRB_TT_SCLASS)) { clone->c = clone; } else { clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass)); } - clone->super = klass->super; if (klass->iv) { mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass)); @@ -259,6 +267,21 @@ 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; + } dc->mt = kh_copy(mt, mrb, sc->mt); dc->super = sc->super; } @@ -275,6 +298,7 @@ init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj) case MRB_TT_SCLASS: case MRB_TT_HASH: case MRB_TT_DATA: + case MRB_TT_EXCEPTION: mrb_iv_copy(mrb, dest, obj); break; @@ -310,15 +334,18 @@ init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj) * * Some Class(True False Nil Symbol Fixnum Float) Object cannot clone. */ -mrb_value +MRB_API mrb_value mrb_obj_clone(mrb_state *mrb, mrb_value self) { struct RObject *p; mrb_value clone; - if (mrb_special_const_p(self)) { + 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); clone = mrb_obj_value(p); @@ -346,15 +373,18 @@ mrb_obj_clone(mrb_state *mrb, mrb_value self) * the class. */ -mrb_value +MRB_API mrb_value mrb_obj_dup(mrb_state *mrb, mrb_value obj) { struct RBasic *p; mrb_value dup; - if (mrb_special_const_p(obj)) { + 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); @@ -426,7 +456,7 @@ mrb_obj_extend_m(mrb_state *mrb, mrb_value self) * <code>Hash</code>. Any hash value that exceeds the capacity of a * <code>Fixnum</code> will be truncated before being used. */ -mrb_value +MRB_API mrb_value mrb_obj_hash(mrb_state *mrb, mrb_value self) { return mrb_fixnum_value(mrb_obj_id(self)); @@ -450,7 +480,7 @@ mrb_obj_init_copy(mrb_state *mrb, mrb_value self) /* implementation of instance_eval */ mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value); -mrb_bool +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; @@ -469,51 +499,10 @@ static mrb_value obj_is_instance_of(mrb_state *mrb, mrb_value self) { mrb_value arg; - mrb_bool instance_of_p; mrb_get_args(mrb, "C", &arg); - instance_of_p = mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg)); - return mrb_bool_value(instance_of_p); -} - -static void -valid_iv_name(mrb_state *mrb, mrb_sym iv_name_id, const char* s, mrb_int len) -{ - if (len < 2 || !(s[0] == '@' && s[1] != '@')) { - mrb_name_error(mrb, iv_name_id, "`%S' is not allowed as an instance variable name", mrb_sym2str(mrb, iv_name_id)); - } -} - -static void -check_iv_name(mrb_state *mrb, mrb_sym iv_name_id) -{ - const char *s; - mrb_int len; - - s = mrb_sym2name_len(mrb, iv_name_id, &len); - valid_iv_name(mrb, iv_name_id, s, len); -} - -static mrb_sym -get_valid_iv_sym(mrb_state *mrb, mrb_value iv_name) -{ - mrb_sym iv_name_id; - - mrb_assert(mrb_symbol_p(iv_name) || mrb_string_p(iv_name)); - - if (mrb_string_p(iv_name)) { - char *p = RSTRING_PTR(iv_name); - mrb_int l = RSTRING_LEN(iv_name); - iv_name_id = mrb_intern(mrb, p, l); - valid_iv_name(mrb, iv_name_id, p, l); - } - else { - iv_name_id = mrb_symbol(iv_name); - check_iv_name(mrb, iv_name_id); - } - - return iv_name_id; + return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg))); } /* 15.3.1.3.20 */ @@ -537,15 +526,11 @@ get_valid_iv_sym(mrb_state *mrb, mrb_value iv_name) static mrb_value mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self) { - mrb_sym mid; - mrb_value sym; - mrb_bool defined_p; - - mrb_get_args(mrb, "o", &sym); - mid = get_valid_iv_sym(mrb, sym); - defined_p = mrb_obj_iv_defined(mrb, mrb_obj_ptr(self), mid); + mrb_sym sym; - return mrb_bool_value(defined_p); + 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 */ @@ -571,13 +556,11 @@ mrb_obj_ivar_defined(mrb_state *mrb, mrb_value self) static mrb_value mrb_obj_ivar_get(mrb_state *mrb, mrb_value self) { - mrb_sym iv_name_id; - mrb_value iv_name; - - mrb_get_args(mrb, "o", &iv_name); + mrb_sym iv_name; - iv_name_id = get_valid_iv_sym(mrb, iv_name); - return mrb_iv_get(mrb, self, iv_name_id); + 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 */ @@ -603,13 +586,12 @@ mrb_obj_ivar_get(mrb_state *mrb, mrb_value self) static mrb_value mrb_obj_ivar_set(mrb_state *mrb, mrb_value self) { - mrb_sym iv_name_id; - mrb_value iv_name, val; - - mrb_get_args(mrb, "oo", &iv_name, &val); + mrb_sym iv_name; + mrb_value val; - iv_name_id = get_valid_iv_sym(mrb, iv_name); - mrb_iv_set(mrb, self, iv_name_id, 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; } @@ -644,12 +626,10 @@ static mrb_value mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self) { mrb_value arg; - mrb_bool kind_of_p; mrb_get_args(mrb, "C", &arg); - kind_of_p = mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg)); - return mrb_bool_value(kind_of_p); + return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg))); } KHASH_DECLARE(st, mrb_sym, char, FALSE) @@ -663,8 +643,8 @@ method_entry_loop(mrb_state *mrb, struct RClass* klass, khash_t(st)* set) khash_t(mt) *h = klass->mt; if (!h) return; for (i=0;i<kh_end(h);i++) { - if (kh_exist(h, i)) { - kh_put(st, mrb, set, kh_key(h,i)); + if (kh_exist(h, i) && kh_value(h, i)) { + kh_put(st, mrb, set, kh_key(h, i)); } } } @@ -674,13 +654,19 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl { 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) || + if ((klass->tt == MRB_TT_ICLASS && !prepended) || (klass->tt == MRB_TT_SCLASS)) { } else { @@ -693,7 +679,7 @@ mrb_class_instance_method_list(mrb_state *mrb, mrb_bool recur, struct RClass* kl 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))); + mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i))); } } kh_destroy(st, mrb, set); @@ -725,7 +711,7 @@ mrb_obj_singleton_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj) 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))); + mrb_ary_push(mrb, ary, mrb_symbol_value(kh_key(set, i))); } } kh_destroy(st, mrb, set); @@ -737,9 +723,8 @@ static mrb_value mrb_obj_methods(mrb_state *mrb, mrb_bool recur, mrb_value obj, mrb_method_flag_t flag) { if (recur) - return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0); - else - return mrb_obj_singleton_methods(mrb, recur, obj); + return mrb_class_instance_method_list(mrb, recur, mrb_class(mrb, obj), 0); + return mrb_obj_singleton_methods(mrb, recur, obj); } /* 15.3.1.3.31 */ /* @@ -854,7 +839,7 @@ mrb_obj_public_methods(mrb_state *mrb, mrb_value self) * raise "Failed to create socket" * raise ArgumentError, "No parameters", caller */ -mrb_value +MRB_API mrb_value mrb_f_raise(mrb_state *mrb, mrb_value self) { mrb_value a[2], exc; @@ -911,7 +896,7 @@ mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self) mrb_value val; mrb_get_args(mrb, "n", &sym); - check_iv_name(mrb, 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)); @@ -1112,7 +1097,7 @@ mrb_init_kernel(mrb_state *mrb) { struct RClass *krn; - krn = mrb->kernel_module = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */ + mrb->kernel_module = krn = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */ mrb_define_class_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.2 */ mrb_define_class_method(mrb, krn, "global_variables", mrb_f_global_variables, MRB_ARGS_NONE()); /* 15.3.1.2.4 */ mrb_define_class_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.5 */ diff --git a/src/keywords b/src/keywords deleted file mode 100644 index 9cb86608c..000000000 --- a/src/keywords +++ /dev/null @@ -1,50 +0,0 @@ -%{ -struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; -const struct kwtable *mrb_reserved_word(const char *, unsigned int); -static const struct kwtable *reserved_word(const char *, unsigned int); -#define mrb_reserved_word(str, len) reserved_word(str, len) -%} - -struct kwtable; -%% -__ENCODING__, {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END -__FILE__, {keyword__FILE__, keyword__FILE__}, EXPR_END -__LINE__, {keyword__LINE__, keyword__LINE__}, EXPR_END -BEGIN, {keyword_BEGIN, keyword_BEGIN}, EXPR_END -END, {keyword_END, keyword_END}, EXPR_END -alias, {keyword_alias, keyword_alias}, EXPR_FNAME -and, {keyword_and, keyword_and}, EXPR_VALUE -begin, {keyword_begin, keyword_begin}, EXPR_BEG -break, {keyword_break, keyword_break}, EXPR_MID -case, {keyword_case, keyword_case}, EXPR_VALUE -class, {keyword_class, keyword_class}, EXPR_CLASS -def, {keyword_def, keyword_def}, EXPR_FNAME -do, {keyword_do, keyword_do}, EXPR_BEG -else, {keyword_else, keyword_else}, EXPR_BEG -elsif, {keyword_elsif, keyword_elsif}, EXPR_VALUE -end, {keyword_end, keyword_end}, EXPR_END -ensure, {keyword_ensure, keyword_ensure}, EXPR_BEG -false, {keyword_false, keyword_false}, EXPR_END -for, {keyword_for, keyword_for}, EXPR_VALUE -if, {keyword_if, modifier_if}, EXPR_VALUE -in, {keyword_in, keyword_in}, EXPR_VALUE -module, {keyword_module, keyword_module}, EXPR_VALUE -next, {keyword_next, keyword_next}, EXPR_MID -nil, {keyword_nil, keyword_nil}, EXPR_END -not, {keyword_not, keyword_not}, EXPR_ARG -or, {keyword_or, keyword_or}, EXPR_VALUE -redo, {keyword_redo, keyword_redo}, EXPR_END -rescue, {keyword_rescue, modifier_rescue}, EXPR_MID -retry, {keyword_retry, keyword_retry}, EXPR_END -return, {keyword_return, keyword_return}, EXPR_MID -self, {keyword_self, keyword_self}, EXPR_END -super, {keyword_super, keyword_super}, EXPR_ARG -then, {keyword_then, keyword_then}, EXPR_BEG -true, {keyword_true, keyword_true}, EXPR_END -undef, {keyword_undef, keyword_undef}, EXPR_FNAME -unless, {keyword_unless, modifier_unless}, EXPR_VALUE -until, {keyword_until, modifier_until}, EXPR_VALUE -when, {keyword_when, keyword_when}, EXPR_VALUE -while, {keyword_while, modifier_while}, EXPR_VALUE -yield, {keyword_yield, keyword_yield}, EXPR_ARG -%% diff --git a/src/lex.def b/src/lex.def deleted file mode 100644 index ea456a843..000000000 --- a/src/lex.def +++ /dev/null @@ -1,212 +0,0 @@ -/* ANSI-C code produced by gperf version 3.0.3 */ -/* Command-line: gperf -L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k'1,3,$' src/keywords */ - -#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ - && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ - && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ - && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ - && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ - && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ - && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ - && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ - && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ - && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ - && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ - && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ - && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ - && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ - && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ - && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ - && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ - && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ - && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ - && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ - && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ - && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ - && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) -/* The character set is not based on ISO-646. */ -#error "gperf generated tables don't work with this execution character set. Please report a bug to <[email protected]>." -#endif - -#line 1 "src/keywords" - -struct kwtable {const char *name; int id[2]; enum mrb_lex_state_enum state;}; -const struct kwtable *mrb_reserved_word(const char *, unsigned int); -static const struct kwtable *reserved_word(const char *, unsigned int); -#define mrb_reserved_word(str, len) reserved_word(str, len) -#line 8 "src/keywords" -struct kwtable; - -#define TOTAL_KEYWORDS 40 -#define MIN_WORD_LENGTH 2 -#define MAX_WORD_LENGTH 12 -#define MIN_HASH_VALUE 8 -#define MAX_HASH_VALUE 50 -/* maximum key range = 43, duplicates = 0 */ - -#ifdef __GNUC__ -__inline -#else -#ifdef __cplusplus -inline -#endif -#endif -static unsigned int -hash (register const char *str, register unsigned int len) -{ - static const unsigned char asso_values[] = - { - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 14, 51, 16, 8, - 11, 13, 51, 51, 51, 51, 10, 51, 13, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 11, 51, 13, 1, 26, - 4, 1, 8, 28, 51, 23, 51, 1, 1, 27, - 5, 19, 21, 51, 8, 3, 3, 11, 51, 21, - 24, 16, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51 - }; - register int hval = len; - - switch (hval) - { - default: - hval += asso_values[(unsigned char)str[2]]; - /*FALLTHROUGH*/ - case 2: - case 1: - hval += asso_values[(unsigned char)str[0]]; - break; - } - return hval + asso_values[(unsigned char)str[len - 1]]; -} - -#ifdef __GNUC__ -__inline -#ifdef __GNUC_STDC_INLINE__ -__attribute__ ((__gnu_inline__)) -#endif -#endif -const struct kwtable * -mrb_reserved_word (register const char *str, register unsigned int len) -{ - static const struct kwtable wordlist[] = - { - {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, -#line 18 "src/keywords" - {"break", {keyword_break, keyword_break}, EXPR_MID}, -#line 23 "src/keywords" - {"else", {keyword_else, keyword_else}, EXPR_BEG}, -#line 33 "src/keywords" - {"nil", {keyword_nil, keyword_nil}, EXPR_END}, -#line 26 "src/keywords" - {"ensure", {keyword_ensure, keyword_ensure}, EXPR_BEG}, -#line 25 "src/keywords" - {"end", {keyword_end, keyword_end}, EXPR_END}, -#line 42 "src/keywords" - {"then", {keyword_then, keyword_then}, EXPR_BEG}, -#line 34 "src/keywords" - {"not", {keyword_not, keyword_not}, EXPR_ARG}, -#line 27 "src/keywords" - {"false", {keyword_false, keyword_false}, EXPR_END}, -#line 40 "src/keywords" - {"self", {keyword_self, keyword_self}, EXPR_END}, -#line 24 "src/keywords" - {"elsif", {keyword_elsif, keyword_elsif}, EXPR_VALUE}, -#line 37 "src/keywords" - {"rescue", {keyword_rescue, modifier_rescue}, EXPR_MID}, -#line 43 "src/keywords" - {"true", {keyword_true, keyword_true}, EXPR_END}, -#line 46 "src/keywords" - {"until", {keyword_until, modifier_until}, EXPR_VALUE}, -#line 45 "src/keywords" - {"unless", {keyword_unless, modifier_unless}, EXPR_VALUE}, -#line 39 "src/keywords" - {"return", {keyword_return, keyword_return}, EXPR_MID}, -#line 21 "src/keywords" - {"def", {keyword_def, keyword_def}, EXPR_FNAME}, -#line 16 "src/keywords" - {"and", {keyword_and, keyword_and}, EXPR_VALUE}, -#line 22 "src/keywords" - {"do", {keyword_do, keyword_do}, EXPR_BEG}, -#line 49 "src/keywords" - {"yield", {keyword_yield, keyword_yield}, EXPR_ARG}, -#line 28 "src/keywords" - {"for", {keyword_for, keyword_for}, EXPR_VALUE}, -#line 44 "src/keywords" - {"undef", {keyword_undef, keyword_undef}, EXPR_FNAME}, -#line 35 "src/keywords" - {"or", {keyword_or, keyword_or}, EXPR_VALUE}, -#line 30 "src/keywords" - {"in", {keyword_in, keyword_in}, EXPR_VALUE}, -#line 47 "src/keywords" - {"when", {keyword_when, keyword_when}, EXPR_VALUE}, -#line 38 "src/keywords" - {"retry", {keyword_retry, keyword_retry}, EXPR_END}, -#line 29 "src/keywords" - {"if", {keyword_if, modifier_if}, EXPR_VALUE}, -#line 19 "src/keywords" - {"case", {keyword_case, keyword_case}, EXPR_VALUE}, -#line 36 "src/keywords" - {"redo", {keyword_redo, keyword_redo}, EXPR_END}, -#line 32 "src/keywords" - {"next", {keyword_next, keyword_next}, EXPR_MID}, -#line 41 "src/keywords" - {"super", {keyword_super, keyword_super}, EXPR_ARG}, -#line 31 "src/keywords" - {"module", {keyword_module, keyword_module}, EXPR_VALUE}, -#line 17 "src/keywords" - {"begin", {keyword_begin, keyword_begin}, EXPR_BEG}, -#line 12 "src/keywords" - {"__LINE__", {keyword__LINE__, keyword__LINE__}, EXPR_END}, -#line 11 "src/keywords" - {"__FILE__", {keyword__FILE__, keyword__FILE__}, EXPR_END}, -#line 10 "src/keywords" - {"__ENCODING__", {keyword__ENCODING__, keyword__ENCODING__}, EXPR_END}, -#line 14 "src/keywords" - {"END", {keyword_END, keyword_END}, EXPR_END}, -#line 15 "src/keywords" - {"alias", {keyword_alias, keyword_alias}, EXPR_FNAME}, -#line 13 "src/keywords" - {"BEGIN", {keyword_BEGIN, keyword_BEGIN}, EXPR_END}, - {""}, -#line 20 "src/keywords" - {"class", {keyword_class, keyword_class}, EXPR_CLASS}, - {""}, {""}, -#line 48 "src/keywords" - {"while", {keyword_while, modifier_while}, EXPR_VALUE} - }; - - if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) - { - register int key = hash (str, len); - - if (key <= MAX_HASH_VALUE && key >= 0) - { - register const char *s = wordlist[key].name; - - if (*str == *s && !strcmp (str + 1, s + 1)) - return &wordlist[key]; - } - } - return 0; -} -#line 50 "src/keywords" - diff --git a/src/load.c b/src/load.c index 8dea4a391..da88f0d3a 100644 --- a/src/load.c +++ b/src/load.c @@ -7,28 +7,31 @@ #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 !defined(_WIN32) && SIZE_MAX < UINT32_MAX -# define SIZE_ERROR_MUL(x, y) ((x) > SIZE_MAX / (y)) -# define SIZE_ERROR(x) ((x) > SIZE_MAX) -#else -# define SIZE_ERROR_MUL(x, y) (0) -# define SIZE_ERROR(x) (0) +#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 -#if CHAR_BIT != 8 -# error This code assumes CHAR_BIT == 8 -#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 -#if UINT32_MAX > SIZE_MAX -# error This code cannot be built on your environment. -#endif +#define SIZE_ERROR_MUL(nmemb, size) ((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) @@ -38,7 +41,7 @@ offset_crc_body(void) } static mrb_irep* -read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool alloc) +read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags) { size_t i; const uint8_t *src = bin; @@ -67,14 +70,36 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all /* 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(sizeof(mrb_code), irep->ilen)) { + if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) { return NULL; } - irep->iseq = (mrb_code *)mrb_malloc(mrb, sizeof(mrb_code) * irep->ilen); - for (i = 0; i < irep->ilen; i++) { - irep->iseq[i] = (size_t)bin_to_uint32(src); /* iseq */ - src += sizeof(uint32_t); + 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); + } + } } } @@ -82,7 +107,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all plen = (size_t)bin_to_uint32(src); /* number of pool */ src += sizeof(uint32_t); if (plen > 0) { - if (SIZE_ERROR_MUL(sizeof(mrb_value), plen)) { + if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) { return NULL; } irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen); @@ -93,7 +118,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all tt = *src++; /* pool TT */ pool_data_len = bin_to_uint16(src); /* pool data length */ src += sizeof(uint16_t); - if (alloc) { + if (flags & FLAG_SRC_MALLOC) { s = mrb_str_new(mrb, (char *)src, pool_data_len); } else { @@ -127,7 +152,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all irep->slen = (size_t)bin_to_uint32(src); /* syms length */ src += sizeof(uint32_t); if (irep->slen > 0) { - if (SIZE_ERROR_MUL(sizeof(mrb_sym), irep->slen)) { + if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) { return NULL; } irep->syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen); @@ -141,7 +166,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all continue; } - if (alloc) { + if (flags & FLAG_SRC_MALLOC) { irep->syms[i] = mrb_intern(mrb, (char *)src, snl); } else { @@ -163,16 +188,23 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool all } static mrb_irep* -read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool alloc) +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, alloc); + mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags); size_t 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, alloc); + irep->reps[i] = read_irep_record(mrb, bin, &rlen, flags); + if (irep->reps[i] == NULL) { + return NULL; + } bin += rlen; *len += rlen; } @@ -180,12 +212,12 @@ read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, mrb_bool alloc } static mrb_irep* -read_section_irep(mrb_state *mrb, const uint8_t *bin, mrb_bool alloc) +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, alloc); + return read_irep_record(mrb, bin, &len, flags); } static int @@ -201,9 +233,6 @@ read_lineno_record_1(mrb_state *mrb, const uint8_t *bin, mrb_irep *irep, size_t fname_len = bin_to_uint16(bin); bin += sizeof(uint16_t); *len += sizeof(uint16_t); - if (SIZE_ERROR(fname_len + 1)) { - return MRB_DUMP_GENERAL_FAILURE; - } fname = (char *)mrb_malloc(mrb, fname_len + 1); memcpy(fname, bin, fname_len); fname[fname_len] = '\0'; @@ -341,7 +370,7 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * size_t len; int ret; - ret =read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len); + ret = read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len); if (ret != MRB_DUMP_OK) return ret; bin += len; } @@ -354,7 +383,7 @@ read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t * } static int -read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, mrb_bool alloc) +read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags) { const uint8_t *bin; ptrdiff_t diff; @@ -375,7 +404,7 @@ read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, mrb_boo for (i = 0; i < filenames_len; ++i) { uint16_t f_len = bin_to_uint16(bin); bin += sizeof(uint16_t); - if (alloc) { + if (flags & FLAG_SRC_MALLOC) { filenames[i] = mrb_intern(mrb, (const char *)bin, (size_t)f_len); } else { @@ -443,7 +472,7 @@ read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *rec } static int -read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, mrb_bool alloc) +read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags) { const uint8_t *bin; ptrdiff_t diff; @@ -453,7 +482,8 @@ read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, mrb_bool a int result; uint32_t syms_len; mrb_sym *syms; - mrb_sym (*intern_func)(mrb_state*, const char*, size_t) = alloc? mrb_intern : mrb_intern_static; + 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; @@ -486,28 +516,36 @@ lv_exit: } static int -read_binary_header(const uint8_t *bin, size_t *bin_size, uint16_t *crc) +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_identify, RITE_BINARY_IDENTIFIER, sizeof(header->binary_identify)) != 0) { - return MRB_DUMP_INVALID_FILE_HEADER; + if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) == 0) { + if (bigendian_p()) + *flags |= FLAG_BYTEORDER_NATIVE; + else + *flags |= FLAG_BYTEORDER_BIG; } - - if (memcmp(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)) != 0) { + 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; } - *crc = bin_to_uint16(header->binary_crc); - if (bin_size) { - *bin_size = (size_t)bin_to_uint32(header->binary_size); + if (crc) { + *crc = bin_to_uint16(header->binary_crc); } + *bin_size = (size_t)bin_to_uint32(header->binary_size); return MRB_DUMP_OK; } -mrb_irep* -mrb_read_irep(mrb_state *mrb, const uint8_t *bin) +MRB_API mrb_irep* +read_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags) { int result; mrb_irep *irep = NULL; @@ -520,7 +558,7 @@ mrb_read_irep(mrb_state *mrb, const uint8_t *bin) return NULL; } - result = read_binary_header(bin, &bin_size, &crc); + result = read_binary_header(bin, &bin_size, &crc, &flags); if (result != MRB_DUMP_OK) { return NULL; } @@ -533,48 +571,59 @@ mrb_read_irep(mrb_state *mrb, const uint8_t *bin) bin += sizeof(struct rite_binary_header); do { section_header = (const struct rite_section_header *)bin; - if (memcmp(section_header->section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(section_header->section_identify)) == 0) { - irep = read_section_irep(mrb, bin, FALSE); + 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_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(section_header->section_identify)) == 0) { + 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_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(section_header->section_identify)) == 0) { + else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; /* corrupted data */ - result = read_section_debug(mrb, bin, irep, FALSE); + result = read_section_debug(mrb, bin, irep, flags); if (result < MRB_DUMP_OK) { return NULL; } } - else if (memcmp(section_header->section_identify, RITE_SECTION_LV_IDENTIFIER, sizeof(section_header->section_identify)) == 0) { + else if (memcmp(section_header->section_ident, RITE_SECTION_LV_IDENT, sizeof(section_header->section_ident)) == 0) { if (!irep) return NULL; - result = read_section_lv(mrb, bin, irep, FALSE); + 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_identify, RITE_BINARY_EOF, sizeof(section_header->section_identify)) != 0); + } while (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0); return irep; } +MRB_API 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); +} + static void irep_error(mrb_state *mrb) { mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error")); } -mrb_value +MRB_API mrb_value mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c) { mrb_irep *irep = mrb_read_irep(mrb, bin); - mrb_value val; struct RProc *proc; if (!irep) { @@ -584,224 +633,54 @@ mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c) proc = mrb_proc_new(mrb, irep); mrb_irep_decref(mrb, irep); if (c && c->no_exec) return mrb_obj_value(proc); - val = mrb_toplevel_run(mrb, proc); - return val; + return mrb_toplevel_run(mrb, proc); } -mrb_value +MRB_API mrb_value mrb_load_irep(mrb_state *mrb, const uint8_t *bin) { return mrb_load_irep_cxt(mrb, bin, NULL); } -#ifdef ENABLE_STDIO - -static int -read_lineno_record_file(mrb_state *mrb, FILE *fp, mrb_irep *irep) -{ - uint8_t header[4]; - const size_t record_header_size = sizeof(header); - int result; - size_t i, buf_size; - size_t len; - void *ptr; - uint8_t *buf; - - if (fread(header, record_header_size, 1, fp) == 0) { - return MRB_DUMP_READ_FAULT; - } - buf_size = (size_t)bin_to_uint32(&header[0]); - if (SIZE_ERROR(buf_size)) { - return MRB_DUMP_GENERAL_FAILURE; - } - ptr = mrb_malloc(mrb, buf_size); - buf = (uint8_t *)ptr; - - if (fread(&buf[record_header_size], buf_size - record_header_size, 1, fp) == 0) { - return MRB_DUMP_READ_FAULT; - } - result = read_lineno_record_1(mrb, buf, irep, &len); - mrb_free(mrb, ptr); - if (result != MRB_DUMP_OK) return result; - for (i = 0; i < irep->rlen; i++) { - result = read_lineno_record_file(mrb, fp, irep->reps[i]); - if (result != MRB_DUMP_OK) break; - } - return result; -} - -static int32_t -read_section_lineno_file(mrb_state *mrb, FILE *fp, mrb_irep *irep) -{ - struct rite_section_lineno_header header; - - if (fread(&header, sizeof(struct rite_section_lineno_header), 1, fp) == 0) { - return MRB_DUMP_READ_FAULT; - } - - /* Read Binary Data Section */ - return read_lineno_record_file(mrb, fp, irep); -} +#ifndef MRB_DISABLE_STDIO -static mrb_irep* -read_irep_record_file(mrb_state *mrb, FILE *fp) -{ - uint8_t header[1 + 4]; - const size_t record_header_size = sizeof(header); - size_t buf_size, i; - size_t len; - mrb_irep *irep = NULL; - void *ptr; - uint8_t *buf; - - if (fread(header, record_header_size, 1, fp) == 0) { - return NULL; - } - buf_size = (size_t)bin_to_uint32(&header[0]); - if (SIZE_ERROR(buf_size)) { - return NULL; - } - ptr = mrb_malloc(mrb, buf_size); - buf = (uint8_t *)ptr; - memcpy(buf, header, record_header_size); - if (fread(&buf[record_header_size], buf_size - record_header_size, 1, fp) == 0) { - return NULL; - } - irep = read_irep_record_1(mrb, buf, &len, TRUE); - mrb_free(mrb, ptr); - if (!irep) return NULL; - for (i=0; i<irep->rlen; i++) { - irep->reps[i] = read_irep_record_file(mrb, fp); - if (!irep->reps[i]) return NULL; - } - return irep; -} - -static mrb_irep* -read_section_irep_file(mrb_state *mrb, FILE *fp) -{ - struct rite_section_irep_header header; - - if (fread(&header, sizeof(struct rite_section_irep_header), 1, fp) == 0) { - return NULL; - } - return read_irep_record_file(mrb, fp); -} - -mrb_irep* +MRB_API mrb_irep* mrb_read_irep_file(mrb_state *mrb, FILE* fp) { mrb_irep *irep = NULL; - int result; uint8_t *buf; - uint16_t crc, crcwk = 0; - size_t section_size = 0; - size_t nbytes; - struct rite_section_header section_header; - long fpos; - size_t block_size = 1 << 14; - const uint8_t block_fallback_count = 4; - int i; - const size_t buf_size = sizeof(struct rite_binary_header); + 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; } - /* You don't need use SIZE_ERROR as buf_size is enough small. */ - buf = (uint8_t*)mrb_malloc(mrb, buf_size); - if (fread(buf, buf_size, 1, fp) == 0) { - mrb_free(mrb, buf); - 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, NULL, &crc); - mrb_free(mrb, buf); - if (result != MRB_DUMP_OK) { - return NULL; + result = read_binary_header(buf, &buf_size, NULL, &flags); + if (result != MRB_DUMP_OK || buf_size <= header_size) { + goto irep_exit; } - /* verify CRC */ - fpos = ftell(fp); - /* You don't need use SIZE_ERROR as block_size is enough small. */ - for (i = 0; i < block_fallback_count; i++,block_size >>= 1) { - buf = (uint8_t*)mrb_malloc_simple(mrb, block_size); - if (buf) break; + buf = (uint8_t*)mrb_realloc(mrb, buf, buf_size); + if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) { + goto irep_exit; } - if (!buf) { - return NULL; - } - fseek(fp, offset_crc_body(), SEEK_SET); - while ((nbytes = fread(buf, 1, block_size, fp)) > 0) { - crcwk = calc_crc_16_ccitt(buf, nbytes, crcwk); - } - mrb_free(mrb, buf); - if (nbytes == 0 && ferror(fp)) { - return NULL; - } - if (crcwk != crc) { - return NULL; - } - fseek(fp, fpos + section_size, SEEK_SET); - - /* read sections */ - do { - fpos = ftell(fp); - if (fread(§ion_header, sizeof(struct rite_section_header), 1, fp) == 0) { - return NULL; - } - section_size = (size_t)bin_to_uint32(section_header.section_size); - - if (memcmp(section_header.section_identify, RITE_SECTION_IREP_IDENTIFIER, sizeof(section_header.section_identify)) == 0) { - fseek(fp, fpos, SEEK_SET); - irep = read_section_irep_file(mrb, fp); - if (!irep) return NULL; - } - else if (memcmp(section_header.section_identify, RITE_SECTION_LINENO_IDENTIFIER, sizeof(section_header.section_identify)) == 0) { - if (!irep) return NULL; /* corrupted data */ - fseek(fp, fpos, SEEK_SET); - result = read_section_lineno_file(mrb, fp, irep); - if (result < MRB_DUMP_OK) return NULL; - } - else if (memcmp(section_header.section_identify, RITE_SECTION_DEBUG_IDENTIFIER, sizeof(section_header.section_identify)) == 0) { - if (!irep) return NULL; /* corrupted data */ - else { - uint8_t* const bin = (uint8_t*)mrb_malloc(mrb, section_size); - - fseek(fp, fpos, SEEK_SET); - if (fread((char*)bin, section_size, 1, fp) != 1) { - mrb_free(mrb, bin); - return NULL; - } - result = read_section_debug(mrb, bin, irep, TRUE); - mrb_free(mrb, bin); - } - if (result < MRB_DUMP_OK) return NULL; - } - else if (memcmp(section_header.section_identify, RITE_SECTION_LV_IDENTIFIER, sizeof(section_header.section_identify)) == 0) { - if (!irep) return NULL; - else { - uint8_t* const bin = (uint8_t*)mrb_malloc(mrb, section_size); - - fseek(fp, fpos, SEEK_SET); - if (fread((char*)bin, section_size, 1, fp) != 1) { - mrb_free(mrb, bin); - return NULL; - } - result = read_section_lv(mrb, bin, irep, TRUE); - mrb_free(mrb, bin); - } - if (result < MRB_DUMP_OK) return NULL; - } - - fseek(fp, fpos + section_size, SEEK_SET); - } while (memcmp(section_header.section_identify, RITE_BINARY_EOF, sizeof(section_header.section_identify)) != 0); + irep = read_irep(mrb, buf, FLAG_SRC_MALLOC); +irep_exit: + mrb_free(mrb, buf); return irep; } void mrb_codedump_all(mrb_state*, struct RProc*); -mrb_value +MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c) { mrb_irep *irep = mrb_read_irep_file(mrb, fp); @@ -820,9 +699,9 @@ mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c) return val; } -mrb_value +MRB_API mrb_value mrb_load_irep_file(mrb_state *mrb, FILE* fp) { return mrb_load_irep_file_cxt(mrb, fp, NULL); } -#endif /* ENABLE_STDIO */ +#endif /* MRB_DISABLE_STDIO */ diff --git a/src/mrb_throw.h b/src/mrb_throw.h deleted file mode 100644 index 3c7407a8d..000000000 --- a/src/mrb_throw.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -** mrb_throw.h - mruby exception throwing handler -** -** See Copyright Notice in mruby.h -*/ - -#ifndef MRB_THROW_H -#define MRB_THROW_H - -#ifdef MRB_ENABLE_CXX_EXCEPTION - -#define MRB_TRY(buf) do { try { -#define MRB_CATCH(buf) } catch(mrb_jmpbuf_impl e) { if (e != (buf)->impl) { throw e; } -#define MRB_END_EXC(buf) } } while(0) - -#define MRB_THROW(buf) throw((buf)->impl) -typedef mrb_int mrb_jmpbuf_impl; - -#else - -#include <setjmp.h> - -#define MRB_TRY(buf) do { if (setjmp((buf)->impl) == 0) { -#define MRB_CATCH(buf) } else { -#define MRB_END_EXC(buf) } } while(0) - -#define MRB_THROW(buf) longjmp((buf)->impl, 1); -#define mrb_jmpbuf_impl jmp_buf - -#endif - -struct mrb_jmpbuf { - mrb_jmpbuf_impl impl; - -#ifdef MRB_ENABLE_CXX_EXCEPTION - static mrb_int jmpbuf_id; - mrb_jmpbuf() : impl(jmpbuf_id++) {} -#endif -}; - -#endif /* MRB_THROW_H */ diff --git a/src/mruby_core.rake b/src/mruby_core.rake index 88fca83fc..abde441d5 100644 --- a/src/mruby_core.rake +++ b/src/mruby_core.rake @@ -3,76 +3,17 @@ MRuby.each_target do relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT) current_build_dir = "#{build_dir}/#{relative_from_root}" - lex_def = "#{current_dir}/lex.def" objs = Dir.glob("#{current_dir}/*.c").map { |f| - next nil if cxx_abi_enabled? and f =~ /(codegen|error|vm).c$/ + next nil if cxx_abi_enabled? and f =~ /(error|vm).c$/ objfile(f.pathmap("#{current_build_dir}/%n")) }.compact if cxx_abi_enabled? - cxx_abi_dependency = %w(codegen error vm) - cxx_abi_objs = cxx_abi_dependency.map { |v| - src = "#{current_build_dir}/#{v}.cxx" - file src => ["#{current_dir}/#{v}.c", __FILE__] do |t| - File.open(t.name, 'w') do |f| - f.write <<EOS -#define __STDC_CONSTANT_MACROS -#define __STDC_LIMIT_MACROS - -extern "C" { -#include "#{MRUBY_ROOT}/#{t.prerequisites.first}" -} - - -#{v == 'error'? 'mrb_int mrb_jmpbuf::jmpbuf_id = 0;' : ''} -EOS - end - end - - file objfile(src) => src do |t| - cxx.run t.name, t.prerequisites.first, [], [current_dir] - end - - objfile src - } - cxx_abi_objs << objfile("#{current_build_dir}/y.tab") - - file "#{current_build_dir}/y.tab.cxx" => ["#{current_build_dir}/y.tab.c", __FILE__] do |t| - File.open(t.name, 'w') do |f| - f.write <<EOS -#define __STDC_CONSTANT_MACROS -#define __STDC_LIMIT_MACROS - -extern "C" { -#include "#{t.prerequisites.first}" -} -EOS - end - end - file objfile("#{current_build_dir}/y.tab") => ["#{current_build_dir}/y.tab.cxx", lex_def] do |t| - cxx.run t.name, t.prerequisites.first, [], [current_dir] - end - - objs += cxx_abi_objs - else - objs += [objfile("#{current_build_dir}/y.tab")] - file objfile("#{current_build_dir}/y.tab") => ["#{current_build_dir}/y.tab.c", lex_def] do |t| - cc.run t.name, t.prerequisites.first, [], [current_dir] - end + 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 - - # Parser - file "#{current_build_dir}/y.tab.c" => ["#{current_dir}/parse.y"] do |t| - yacc.run t.name, t.prerequisites.first - end - - # Lexical analyzer - file lex_def => "#{current_dir}/keywords" do |t| - gperf.run t.name, t.prerequisites.first - end end diff --git a/src/node.h b/src/node.h deleted file mode 100644 index 532a8323a..000000000 --- a/src/node.h +++ /dev/null @@ -1,117 +0,0 @@ -/* -** node.h - nodes of abstract syntax tree -** -** See Copyright Notice in mruby.h -*/ - -#ifndef NODE_H -#define NODE_H - -enum node_type { - NODE_METHOD, - NODE_FBODY, - NODE_CFUNC, - NODE_SCOPE, - NODE_BLOCK, - NODE_IF, - NODE_CASE, - NODE_WHEN, - NODE_OPT_N, - NODE_WHILE, - NODE_UNTIL, - NODE_ITER, - NODE_FOR, - NODE_BREAK, - NODE_NEXT, - NODE_REDO, - NODE_RETRY, - NODE_BEGIN, - NODE_RESCUE, - NODE_ENSURE, - NODE_AND, - NODE_OR, - NODE_NOT, - NODE_MASGN, - NODE_ASGN, - NODE_CDECL, - NODE_CVASGN, - NODE_CVDECL, - NODE_OP_ASGN, - NODE_CALL, - NODE_FCALL, - NODE_VCALL, - NODE_SUPER, - NODE_ZSUPER, - NODE_ARRAY, - NODE_ZARRAY, - NODE_HASH, - NODE_RETURN, - NODE_YIELD, - NODE_LVAR, - NODE_DVAR, - NODE_GVAR, - NODE_IVAR, - NODE_CONST, - NODE_CVAR, - NODE_NTH_REF, - NODE_BACK_REF, - NODE_MATCH, - NODE_MATCH2, - NODE_MATCH3, - NODE_INT, - NODE_FLOAT, - NODE_NEGATE, - NODE_LAMBDA, - NODE_SYM, - NODE_STR, - NODE_DSTR, - NODE_XSTR, - NODE_DXSTR, - NODE_REGX, - NODE_DREGX, - NODE_DREGX_ONCE, - NODE_LIST, - NODE_ARG, - NODE_ARGSCAT, - NODE_ARGSPUSH, - NODE_SPLAT, - NODE_TO_ARY, - NODE_SVALUE, - NODE_BLOCK_ARG, - NODE_DEF, - NODE_SDEF, - NODE_ALIAS, - NODE_UNDEF, - NODE_CLASS, - NODE_MODULE, - NODE_SCLASS, - NODE_COLON2, - NODE_COLON3, - NODE_CREF, - NODE_DOT2, - NODE_DOT3, - NODE_FLIP2, - NODE_FLIP3, - NODE_ATTRSET, - NODE_SELF, - NODE_NIL, - NODE_TRUE, - NODE_FALSE, - NODE_DEFINED, - NODE_NEWLINE, - NODE_POSTEXE, - NODE_ALLOCA, - NODE_DMETHOD, - NODE_BMETHOD, - NODE_MEMO, - NODE_IFUNC, - NODE_DSYM, - NODE_ATTRASGN, - NODE_HEREDOC, - NODE_LITERAL_DELIM, - NODE_WORDS, - NODE_SYMBOLS, - NODE_LAST -}; - -#endif /* NODE_H */ diff --git a/src/numeric.c b/src/numeric.c index db1e7d0f4..7b49b29f7 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -9,25 +9,21 @@ #include <math.h> #include <stdlib.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/numeric.h" -#include "mruby/string.h" +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/numeric.h> +#include <mruby/string.h> #ifdef MRB_USE_FLOAT #define floor(f) floorf(f) #define ceil(f) ceilf(f) #define fmod(x,y) fmodf(x,y) -#define FLO_MAX_DIGITS 7 -#define FLO_MAX_SIGN_LENGTH 3 -#define FLO_EPSILON FLT_EPSILON +#define MRB_FLO_TO_STR_FMT "%.7g" #else -#define FLO_MAX_DIGITS 14 -#define FLO_MAX_SIGN_LENGTH 10 -#define FLO_EPSILON DBL_EPSILON +#define MRB_FLO_TO_STR_FMT "%.14g" #endif -mrb_float +MRB_API mrb_float mrb_to_flo(mrb_state *mrb, mrb_value val) { switch (mrb_type(val)) { @@ -54,13 +50,13 @@ static mrb_value num_pow(mrb_state *mrb, mrb_value x) { mrb_value y; - mrb_bool both_int = FALSE; - mrb_float d; + mrb_float d, yv; mrb_get_args(mrb, "o", &y); - if (mrb_fixnum_p(x) && mrb_fixnum_p(y)) both_int = TRUE; - d = pow(mrb_to_flo(mrb, x), mrb_to_flo(mrb, y)); - if (both_int && FIXABLE(d)) + yv = mrb_to_flo(mrb, y); + d = pow(mrb_to_flo(mrb, x), yv); + if (mrb_fixnum_p(x) && mrb_fixnum_p(y) && FIXABLE(d) && yv > 0 && + (d < 0 || (d > 0 && (mrb_int)d > 0))) return mrb_fixnum_value((mrb_int)d); return mrb_float_value(mrb, d); } @@ -108,142 +104,6 @@ num_div(mrb_state *mrb, mrb_value x) * representation. */ -static mrb_value -mrb_flo_to_str(mrb_state *mrb, mrb_float flo) -{ - double n = (double)flo; - int max_digits = FLO_MAX_DIGITS; - - if (isnan(n)) { - return mrb_str_new_lit(mrb, "NaN"); - } - else if (isinf(n)) { - if (n < 0) { - return mrb_str_new_lit(mrb, "-inf"); - } - else { - return mrb_str_new_lit(mrb, "inf"); - } - } - else { - int digit; - int m = 0; - int exp; - mrb_bool e = FALSE; - char s[48]; - char *c = &s[0]; - int length = 0; - - if (signbit(n)) { - n = -n; - *(c++) = '-'; - } - - if (n != 0.0) { - if (n > 1.0) { - exp = (int)floor(log10(n)); - } - else { - exp = (int)-ceil(-log10(n)); - } - } - else { - exp = 0; - } - - /* preserve significands */ - if (exp < 0) { - int i, beg = -1, end = 0; - double f = n; - double fd = 0; - for (i = 0; i < FLO_MAX_DIGITS; ++i) { - f = (f - fd) * 10.0; - fd = floor(f + FLO_EPSILON); - if (fd != 0) { - if (beg < 0) beg = i; - end = i + 1; - } - } - if (beg >= 0) length = end - beg; - if (length > FLO_MAX_SIGN_LENGTH) length = FLO_MAX_SIGN_LENGTH; - } - - if (abs(exp) + length >= FLO_MAX_DIGITS) { - /* exponent representation */ - e = TRUE; - n = n / pow(10.0, exp); - if (isinf(n)) { - if (s < c) { /* s[0] == '-' */ - return mrb_str_new_lit(mrb, "-0.0"); - } - else { - return mrb_str_new_lit(mrb, "0.0"); - } - } - } - else { - /* un-exponent (normal) representation */ - if (exp > 0) { - m = exp; - } - } - - /* puts digits */ - while (max_digits >= 0) { - double weight = pow(10.0, m); - double fdigit = n / weight; - - if (fdigit < 0) fdigit = n = 0; - if (m < -1 && fdigit < FLO_EPSILON) { - if (e || exp > 0 || m <= -abs(exp)) { - break; - } - } - digit = (int)floor(fdigit + FLO_EPSILON); - if (m == 0 && digit > 9) { - n /= 10.0; - exp++; - continue; - } - *(c++) = '0' + digit; - n -= (digit * weight); - max_digits--; - if (m-- == 0) { - *(c++) = '.'; - } - } - if (c[-1] == '0') { - while (&s[0] < c && c[-1] == '0') { - c--; - } - c++; - } - - if (e) { - *(c++) = 'e'; - if (exp > 0) { - *(c++) = '+'; - } - else { - *(c++) = '-'; - exp = -exp; - } - - if (exp >= 100) { - *(c++) = '0' + exp / 100; - exp -= exp / 100 * 100; - } - - *(c++) = '0' + exp / 10; - *(c++) = '0' + exp % 10; - } - - *c = '\0'; - - return mrb_str_new(mrb, &s[0], c - &s[0]); - } -} - /* 15.2.9.3.16(x) */ /* * call-seq: @@ -251,14 +111,17 @@ mrb_flo_to_str(mrb_state *mrb, mrb_float flo) * * Returns a string containing a representation of self. As well as a * fixed or exponential form of the number, the call may return - * ``<code>NaN</code>'', ``<code>Infinity</code>'', and - * ``<code>-Infinity</code>''. + * "<code>NaN</code>", "<code>Infinity</code>", and + * "<code>-Infinity</code>". */ static mrb_value flo_to_s(mrb_state *mrb, mrb_value flt) { - return mrb_flo_to_str(mrb, mrb_float(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 */ @@ -339,12 +202,11 @@ static mrb_value flo_mod(mrb_state *mrb, mrb_value x) { mrb_value y; - mrb_float fy, mod; + mrb_float mod; mrb_get_args(mrb, "o", &y); - fy = mrb_to_flo(mrb, y); - flodivmod(mrb, mrb_float(x), fy, 0, &mod); + flodivmod(mrb, mrb_float(x), mrb_to_flo(mrb, y), 0, &mod); return mrb_float_value(mrb, mod); } @@ -397,22 +259,16 @@ static mrb_value flo_eq(mrb_state *mrb, mrb_value x) { mrb_value y; - volatile mrb_float a, b; - mrb_get_args(mrb, "o", &y); switch (mrb_type(y)) { case MRB_TT_FIXNUM: - b = (mrb_float)mrb_fixnum(y); - break; + return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_fixnum(y)); case MRB_TT_FLOAT: - b = mrb_float(y); - break; + return mrb_bool_value(mrb_float(x) == mrb_float(y)); default: return mrb_false_value(); } - a = mrb_float(x); - return mrb_bool_value(a == b); } /* 15.2.8.3.18 */ @@ -474,7 +330,7 @@ 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_fixnum_value(value < 0 ? -1 : 1); } return mrb_nil_value(); } @@ -493,9 +349,7 @@ flo_infinite_p(mrb_state *mrb, mrb_value num) static mrb_value flo_finite_p(mrb_state *mrb, mrb_value num) { - mrb_float value = mrb_float(num); - - return mrb_bool_value(isfinite(value)); + return mrb_bool_value(isfinite(mrb_float(num))); } /* 15.2.9.3.10 */ @@ -583,7 +437,7 @@ flo_round(mrb_state *mrb, mrb_value num) { double number, f; mrb_int ndigits = 0; - int i; + mrb_int i; mrb_get_args(mrb, "|i", &ndigits); number = mrb_float(num); @@ -598,7 +452,7 @@ flo_round(mrb_state *mrb, mrb_value num) } f = 1.0; - i = abs(ndigits); + i = ndigits >= 0 ? ndigits : -ndigits; while (--i >= 0) f = f*10.0; @@ -613,12 +467,12 @@ flo_round(mrb_state *mrb, mrb_value num) /* home-made inline implementation of round(3) */ if (number > 0.0) { - d = floor(number); - number = d + (number - d >= 0.5); + d = floor(number); + number = d + (number - d >= 0.5); } else if (number < 0.0) { - d = ceil(number); - number = d - (d - number >= 0.5); + d = ceil(number); + number = d - (d - number >= 0.5); } if (ndigits < 0) number *= f; @@ -687,12 +541,8 @@ int_to_i(mrb_state *mrb, mrb_value num) return num; } -#ifdef MRB_FIXNUM_SHIFT -#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1-MRB_FIXNUM_SHIFT)/2)) -#else -#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1)/2)) -#endif /*tests if N*N would overflow*/ +#define SQRT_INT_MAX ((mrb_int)1<<((MRB_INT_BIT-1-MRB_FIXNUM_SHIFT)/2)) #define FIT_SQRT_INT(n) (((n)<SQRT_INT_MAX)&&((n)>=-SQRT_INT_MAX)) mrb_value @@ -713,7 +563,7 @@ mrb_fixnum_mul(mrb_state *mrb, mrb_value x, mrb_value y) if ((a != 0 && c/a != b) || !FIXABLE(c)) { return mrb_float_value(mrb, (mrb_float)a*(mrb_float)b); } - return mrb_fixnum_value(c); + return mrb_fixnum_value((mrb_int)c); } return mrb_float_value(mrb, (mrb_float)a * mrb_to_flo(mrb, y)); } @@ -970,19 +820,35 @@ fix_xor(mrb_state *mrb, mrb_value x) static mrb_value lshift(mrb_state *mrb, mrb_int val, mrb_int width) { - mrb_assert(width >= 0); - if (width > NUMERIC_SHIFT_WIDTH_MAX) { - mrb_raisef(mrb, E_RANGE_ERROR, "width(%S) > (%S:MRB_INT_BIT-1)", - mrb_fixnum_value(width), - mrb_fixnum_value(NUMERIC_SHIFT_WIDTH_MAX)); + mrb_assert(width > 0); + if (val > 0) { + if ((width > NUMERIC_SHIFT_WIDTH_MAX) || + (val > (MRB_INT_MAX >> width))) { + goto bit_overflow; + } + } else { + if ((width > NUMERIC_SHIFT_WIDTH_MAX) || + (val < (MRB_INT_MIN >> width))) { + goto bit_overflow; + } } + return mrb_fixnum_value(val << 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) { - mrb_assert(width >= 0); + mrb_assert(width > 0); if (width >= NUMERIC_SHIFT_WIDTH_MAX) { if (val < 0) { return mrb_fixnum_value(-1); @@ -1004,7 +870,7 @@ fix_shift_get_width(mrb_state *mrb, mrb_int *width) /* 15.2.8.3.12 */ /* * call-seq: - * fix << count -> integer + * fix << count -> integer or float * * Shifts _fix_ left _count_ positions (right if _count_ is negative). */ @@ -1029,7 +895,7 @@ fix_lshift(mrb_state *mrb, mrb_value x) /* 15.2.8.3.13 */ /* * call-seq: - * fix >> count -> integer + * fix >> count -> integer or float * * Shifts _fix_ right _count_ positions (left if _count_ is negative). */ @@ -1080,14 +946,14 @@ fix_to_f(mrb_state *mrb, mrb_value num) * FloatDomainError: Infinity */ /* ------------------------------------------------------------------------*/ -mrb_value +MRB_API mrb_value mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x) { mrb_int z; - if (mrb_float_p(x)) { - mrb_raise(mrb, E_TYPE_ERROR, "non float value"); - z = 0; /* not reached. just suppress warnings. */ + 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); @@ -1098,7 +964,12 @@ mrb_flo_to_fixnum(mrb_state *mrb, mrb_value x) if (isnan(d)) { mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN"); } - z = (mrb_int)d; + if (FIXABLE(d)) { + z = (mrb_int)d; + } + else { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "number (%S) too big for integer", x); + } } return mrb_fixnum_value(z); } @@ -1178,7 +1049,7 @@ fix_minus(mrb_state *mrb, mrb_value self) } -mrb_value +MRB_API mrb_value mrb_fixnum_to_str(mrb_state *mrb, mrb_value x, int base) { char buf[MRB_INT_BIT+1]; @@ -1309,7 +1180,7 @@ mrb_init_numeric(mrb_state *mrb) mrb_define_method(mrb, integer, "to_int", int_to_i, MRB_ARGS_NONE()); /* Fixnum Class */ - fixnum = mrb->fixnum_class = mrb_define_class(mrb, "Fixnum", integer); + mrb->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 */ @@ -1329,7 +1200,7 @@ mrb_init_numeric(mrb_state *mrb) mrb_define_method(mrb, fixnum, "divmod", fix_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30 (x) */ /* Float Class */ - fl = mrb->float_class = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */ + mrb->float_class = fl = mrb_define_class(mrb, "Float", numeric); /* 15.2.9 */ mrb_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 */ @@ -1351,4 +1222,11 @@ mrb_init_numeric(mrb_state *mrb) 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/src/object.c b/src/object.c index 6d39254dd..bb1a4ebc4 100644 --- a/src/object.c +++ b/src/object.c @@ -4,12 +4,12 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/class.h" -#include "mruby/numeric.h" -#include "mruby/string.h" +#include <mruby.h> +#include <mruby/class.h> +#include <mruby/numeric.h> +#include <mruby/string.h> -mrb_bool +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; @@ -19,9 +19,9 @@ mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2) case MRB_TT_FALSE: case MRB_TT_FIXNUM: - return (v1.value.i == v2.value.i); + return (mrb_fixnum(v1) == mrb_fixnum(v2)); case MRB_TT_SYMBOL: - return (v1.value.sym == v2.value.sym); + return (mrb_symbol(v1) == mrb_symbol(v2)); case MRB_TT_FLOAT: return (mrb_float(v1) == mrb_float(v2)); @@ -31,14 +31,14 @@ mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2) } } -mrb_bool +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_bool +MRB_API mrb_bool mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2) { mrb_value result; @@ -264,7 +264,7 @@ mrb_init_object(mrb_state *mrb) struct RClass *t; struct RClass *f; - n = mrb->nil_class = mrb_define_class(mrb, "NilClass", mrb->object_class); + mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class); mrb_undef_class_method(mrb, n, "new"); mrb_define_method(mrb, n, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.4.3.1 */ mrb_define_method(mrb, n, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.4.3.2 */ @@ -273,7 +273,7 @@ mrb_init_object(mrb_state *mrb) mrb_define_method(mrb, n, "to_s", nil_to_s, MRB_ARGS_NONE()); /* 15.2.4.3.5 */ mrb_define_method(mrb, n, "inspect", nil_inspect, MRB_ARGS_NONE()); - t = mrb->true_class = mrb_define_class(mrb, "TrueClass", mrb->object_class); + mrb->true_class = t = mrb_define_class(mrb, "TrueClass", mrb->object_class); mrb_undef_class_method(mrb, t, "new"); mrb_define_method(mrb, t, "&", true_and, MRB_ARGS_REQ(1)); /* 15.2.5.3.1 */ mrb_define_method(mrb, t, "^", true_xor, MRB_ARGS_REQ(1)); /* 15.2.5.3.2 */ @@ -281,7 +281,7 @@ mrb_init_object(mrb_state *mrb) mrb_define_method(mrb, t, "|", true_or, MRB_ARGS_REQ(1)); /* 15.2.5.3.4 */ mrb_define_method(mrb, t, "inspect", true_to_s, MRB_ARGS_NONE()); - f = mrb->false_class = mrb_define_class(mrb, "FalseClass", mrb->object_class); + mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class); mrb_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 */ @@ -310,16 +310,13 @@ convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *metho 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(); - } - else { - return mrb_nil_value(); } + return mrb_nil_value(); } return mrb_funcall_argv(mrb, val, m, 0, 0); } -mrb_value +MRB_API mrb_value mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method) { mrb_value v; @@ -332,7 +329,7 @@ mrb_check_to_integer(mrb_state *mrb, mrb_value val, const char *method) return v; } -mrb_value +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; @@ -346,7 +343,7 @@ mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char return v; } -mrb_value +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; @@ -386,7 +383,7 @@ static const struct types { {-1, 0} }; -void +MRB_API void mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) { const struct types *type = builtin_types; @@ -407,7 +404,7 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) else if (mrb_type(x) == MRB_TT_SYMBOL) { etype = "Symbol"; } - else if (mrb_special_const_p(x)) { + else if (mrb_immediate_p(x)) { etype = RSTRING_PTR(mrb_obj_as_string(mrb, x)); } else { @@ -431,10 +428,10 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t) * Returns a string representing <i>obj</i>. The default * <code>to_s</code> prints the object's class and an encoding of the * object id. As a special case, the top-level object that is the - * initial execution context of Ruby programs returns ``main.'' + * initial execution context of Ruby programs returns "main." */ -mrb_value +MRB_API mrb_value mrb_any_to_s(mrb_state *mrb, mrb_value obj) { mrb_value str = mrb_str_buf_new(mrb, 20); @@ -475,7 +472,7 @@ mrb_any_to_s(mrb_state *mrb, mrb_value obj) * b.kind_of? M #=> true */ -mrb_bool +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); @@ -484,12 +481,14 @@ mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c) case MRB_TT_MODULE: case MRB_TT_CLASS: case MRB_TT_ICLASS: + case MRB_TT_SCLASS: break; default: mrb_raise(mrb, E_TYPE_ERROR, "class or module required"); } + MRB_CLASS_ORIGIN(c); while (cl) { if (cl == c || cl->mt == c->mt) return TRUE; @@ -513,13 +512,13 @@ mrb_to_integer(mrb_state *mrb, mrb_value val, const char *method) return v; } -mrb_value +MRB_API mrb_value mrb_to_int(mrb_state *mrb, mrb_value val) { return mrb_to_integer(mrb, val, "to_int"); } -static mrb_value +MRB_API mrb_value mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base) { mrb_value tmp; @@ -540,13 +539,17 @@ mrb_convert_to_integer(mrb_state *mrb, mrb_value val, int base) 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)) { - return mrb_str_to_inum(mrb, val, base, TRUE); + goto string_conv; } arg_error: mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value"); @@ -558,13 +561,13 @@ arg_error: return tmp; } -mrb_value +MRB_API mrb_value mrb_Integer(mrb_state *mrb, mrb_value val) { return mrb_convert_to_integer(mrb, val, 0); } -mrb_value +MRB_API mrb_value mrb_Float(mrb_state *mrb, mrb_value val) { if (mrb_nil_p(val)) { @@ -585,13 +588,13 @@ mrb_Float(mrb_state *mrb, mrb_value val) } } -mrb_value +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_bool +MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2) { if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE; diff --git a/src/opcode.h b/src/opcode.h index 2446f92ed..fe4d17a21 100644 --- a/src/opcode.h +++ b/src/opcode.h @@ -1,2 +1,2 @@ /* this header file is to be removed soon. */ -#include "mruby/opcode.h" +#include <mruby/opcode.h> diff --git a/src/parse.y b/src/parse.y deleted file mode 100644 index 2c7e788d9..000000000 --- a/src/parse.y +++ /dev/null @@ -1,6370 +0,0 @@ -/* -** parse.y - mruby parser -** -** See Copyright Notice in mruby.h -*/ - -%{ -#undef PARSER_DEBUG - -#define YYDEBUG 1 -#define YYERROR_VERBOSE 1 -/* - * Force yacc to use our memory management. This is a little evil because - * the macros assume that "parser_state *p" is in scope - */ -#define YYMALLOC(n) mrb_malloc(p->mrb, (n)) -#define YYFREE(o) mrb_free(p->mrb, (o)) -#define YYSTACK_USE_ALLOCA 0 - -#include <ctype.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include "mruby.h" -#include "mruby/compile.h" -#include "mruby/proc.h" -#include "mruby/error.h" -#include "node.h" -#include "mrb_throw.h" - -#define YYLEX_PARAM p - -typedef mrb_ast_node node; -typedef struct mrb_parser_state parser_state; -typedef struct mrb_parser_heredoc_info parser_heredoc_info; - -static int yyparse(parser_state *p); -static int yylex(void *lval, parser_state *p); -static void yyerror(parser_state *p, const char *s); -static void yywarn(parser_state *p, const char *s); -static void yywarning(parser_state *p, const char *s); -static void backref_error(parser_state *p, node *n); -static void tokadd(parser_state *p, int32_t c); - -#define identchar(c) (ISALNUM(c) || (c) == '_' || !ISASCII(c)) - -typedef unsigned int stack_type; - -#define BITSTACK_PUSH(stack, n) ((stack) = ((stack)<<1)|((n)&1)) -#define BITSTACK_POP(stack) ((stack) = (stack) >> 1) -#define BITSTACK_LEXPOP(stack) ((stack) = ((stack) >> 1) | ((stack) & 1)) -#define BITSTACK_SET_P(stack) ((stack)&1) - -#define COND_PUSH(n) BITSTACK_PUSH(p->cond_stack, (n)) -#define COND_POP() BITSTACK_POP(p->cond_stack) -#define COND_LEXPOP() BITSTACK_LEXPOP(p->cond_stack) -#define COND_P() BITSTACK_SET_P(p->cond_stack) - -#define CMDARG_PUSH(n) BITSTACK_PUSH(p->cmdarg_stack, (n)) -#define CMDARG_POP() BITSTACK_POP(p->cmdarg_stack) -#define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack) -#define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack) - -#define SET_LINENO(c,n) ((c)->lineno = (n)) - -#define sym(x) ((mrb_sym)(intptr_t)(x)) -#define nsym(x) ((node*)(intptr_t)(x)) - -static inline mrb_sym -intern_cstr_gen(parser_state *p, const char *s) -{ - return mrb_intern_cstr(p->mrb, s); -} -#define intern_cstr(s) intern_cstr_gen(p,(s)) - -static inline mrb_sym -intern_gen(parser_state *p, const char *s, size_t len) -{ - return mrb_intern(p->mrb, s, len); -} -#define intern(s,len) intern_gen(p,(s),(len)) - -static inline mrb_sym -intern_gen_c(parser_state *p, const char c) -{ - return mrb_intern(p->mrb, &c, 1); -} -#define intern_c(c) intern_gen_c(p,(c)) - -static void -cons_free_gen(parser_state *p, node *cons) -{ - cons->cdr = p->cells; - p->cells = cons; -} -#define cons_free(c) cons_free_gen(p, (c)) - -static void* -parser_palloc(parser_state *p, size_t size) -{ - void *m = mrb_pool_alloc(p->pool, size); - - if (!m) { - MRB_THROW(p->jmp); - } - return m; -} - -static node* -cons_gen(parser_state *p, node *car, node *cdr) -{ - node *c; - - if (p->cells) { - c = p->cells; - p->cells = p->cells->cdr; - } - else { - c = (node *)parser_palloc(p, sizeof(mrb_ast_node)); - } - - c->car = car; - c->cdr = cdr; - c->lineno = p->lineno; - c->filename_index = p->current_filename_index; - return c; -} -#define cons(a,b) cons_gen(p,(a),(b)) - -static node* -list1_gen(parser_state *p, node *a) -{ - return cons(a, 0); -} -#define list1(a) list1_gen(p, (a)) - -static node* -list2_gen(parser_state *p, node *a, node *b) -{ - return cons(a, cons(b,0)); -} -#define list2(a,b) list2_gen(p, (a),(b)) - -static node* -list3_gen(parser_state *p, node *a, node *b, node *c) -{ - return cons(a, cons(b, cons(c,0))); -} -#define list3(a,b,c) list3_gen(p, (a),(b),(c)) - -static node* -list4_gen(parser_state *p, node *a, node *b, node *c, node *d) -{ - return cons(a, cons(b, cons(c, cons(d, 0)))); -} -#define list4(a,b,c,d) list4_gen(p, (a),(b),(c),(d)) - -static node* -list5_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e) -{ - return cons(a, cons(b, cons(c, cons(d, cons(e, 0))))); -} -#define list5(a,b,c,d,e) list5_gen(p, (a),(b),(c),(d),(e)) - -static node* -list6_gen(parser_state *p, node *a, node *b, node *c, node *d, node *e, node *f) -{ - return cons(a, cons(b, cons(c, cons(d, cons(e, cons(f, 0)))))); -} -#define list6(a,b,c,d,e,f) list6_gen(p, (a),(b),(c),(d),(e),(f)) - -static node* -append_gen(parser_state *p, node *a, node *b) -{ - node *c = a; - - if (!a) return b; - while (c->cdr) { - c = c->cdr; - } - if (b) { - c->cdr = b; - } - return a; -} -#define append(a,b) append_gen(p,(a),(b)) -#define push(a,b) append_gen(p,(a),list1(b)) - -static char* -parser_strndup(parser_state *p, const char *s, size_t len) -{ - char *b = (char *)parser_palloc(p, len+1); - - memcpy(b, s, len); - b[len] = '\0'; - return b; -} -#define strndup(s,len) parser_strndup(p, s, len) - -static char* -parser_strdup(parser_state *p, const char *s) -{ - return parser_strndup(p, s, strlen(s)); -} -#undef strdup -#define strdup(s) parser_strdup(p, s) - -/* xxx ----------------------------- */ - -static node* -local_switch(parser_state *p) -{ - node *prev = p->locals; - - p->locals = cons(0, 0); - return prev; -} - -static void -local_resume(parser_state *p, node *prev) -{ - p->locals = prev; -} - -static void -local_nest(parser_state *p) -{ - p->locals = cons(0, p->locals); -} - -static void -local_unnest(parser_state *p) -{ - p->locals = p->locals->cdr; -} - -static int -local_var_p(parser_state *p, mrb_sym sym) -{ - node *l = p->locals; - - while (l) { - node *n = l->car; - while (n) { - if (sym(n->car) == sym) return 1; - n = n->cdr; - } - l = l->cdr; - } - return 0; -} - -static void -local_add_f(parser_state *p, mrb_sym sym) -{ - if (p->locals->car && !p->locals->car->car) { - p->locals->car->car = nsym(sym); - } else { - p->locals->car = push(p->locals->car, nsym(sym)); - } -} - -static void -local_add(parser_state *p, mrb_sym sym) -{ - if (!local_var_p(p, sym)) { - local_add_f(p, sym); - } -} - -/* (:scope (vars..) (prog...)) */ -static node* -new_scope(parser_state *p, node *body) -{ - return cons((node*)NODE_SCOPE, cons(p->locals->car, body)); -} - -/* (:begin prog...) */ -static node* -new_begin(parser_state *p, node *body) -{ - if (body) - return list2((node*)NODE_BEGIN, body); - return cons((node*)NODE_BEGIN, 0); -} - -#define newline_node(n) (n) - -/* (:rescue body rescue else) */ -static node* -new_rescue(parser_state *p, node *body, node *resq, node *els) -{ - return list4((node*)NODE_RESCUE, body, resq, els); -} - -/* (:ensure body ensure) */ -static node* -new_ensure(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_ENSURE, cons(a, cons(0, b))); -} - -/* (:nil) */ -static node* -new_nil(parser_state *p) -{ - return list1((node*)NODE_NIL); -} - -/* (:true) */ -static node* -new_true(parser_state *p) -{ - return list1((node*)NODE_TRUE); -} - -/* (:false) */ -static node* -new_false(parser_state *p) -{ - return list1((node*)NODE_FALSE); -} - -/* (:alias new old) */ -static node* -new_alias(parser_state *p, mrb_sym a, mrb_sym b) -{ - return cons((node*)NODE_ALIAS, cons(nsym(a), nsym(b))); -} - -/* (:if cond then else) */ -static node* -new_if(parser_state *p, node *a, node *b, node *c) -{ - return list4((node*)NODE_IF, a, b, c); -} - -/* (:unless cond then else) */ -static node* -new_unless(parser_state *p, node *a, node *b, node *c) -{ - return list4((node*)NODE_IF, a, c, b); -} - -/* (:while cond body) */ -static node* -new_while(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_WHILE, cons(a, b)); -} - -/* (:until cond body) */ -static node* -new_until(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_UNTIL, cons(a, b)); -} - -/* (:for var obj body) */ -static node* -new_for(parser_state *p, node *v, node *o, node *b) -{ - return list4((node*)NODE_FOR, v, o, b); -} - -/* (:case a ((when ...) body) ((when...) body)) */ -static node* -new_case(parser_state *p, node *a, node *b) -{ - node *n = list2((node*)NODE_CASE, a); - node *n2 = n; - - while (n2->cdr) { - n2 = n2->cdr; - } - n2->cdr = b; - return n; -} - -/* (:postexe a) */ -static node* -new_postexe(parser_state *p, node *a) -{ - return cons((node*)NODE_POSTEXE, a); -} - -/* (:self) */ -static node* -new_self(parser_state *p) -{ - return list1((node*)NODE_SELF); -} - -/* (:call a b c) */ -static node* -new_call(parser_state *p, node *a, mrb_sym b, node *c) -{ - return list4((node*)NODE_CALL, a, nsym(b), c); -} - -/* (:fcall self mid args) */ -static node* -new_fcall(parser_state *p, mrb_sym b, node *c) -{ - return list4((node*)NODE_FCALL, new_self(p), nsym(b), c); -} - -/* (:super . c) */ -static node* -new_super(parser_state *p, node *c) -{ - return cons((node*)NODE_SUPER, c); -} - -/* (:zsuper) */ -static node* -new_zsuper(parser_state *p) -{ - return list1((node*)NODE_ZSUPER); -} - -/* (:yield . c) */ -static node* -new_yield(parser_state *p, node *c) -{ - if (c) { - if (c->cdr) { - yyerror(p, "both block arg and actual block given"); - } - return cons((node*)NODE_YIELD, c->car); - } - return cons((node*)NODE_YIELD, 0); -} - -/* (:return . c) */ -static node* -new_return(parser_state *p, node *c) -{ - return cons((node*)NODE_RETURN, c); -} - -/* (:break . c) */ -static node* -new_break(parser_state *p, node *c) -{ - return cons((node*)NODE_BREAK, c); -} - -/* (:next . c) */ -static node* -new_next(parser_state *p, node *c) -{ - return cons((node*)NODE_NEXT, c); -} - -/* (:redo) */ -static node* -new_redo(parser_state *p) -{ - return list1((node*)NODE_REDO); -} - -/* (:retry) */ -static node* -new_retry(parser_state *p) -{ - return list1((node*)NODE_RETRY); -} - -/* (:dot2 a b) */ -static node* -new_dot2(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_DOT2, cons(a, b)); -} - -/* (:dot3 a b) */ -static node* -new_dot3(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_DOT3, cons(a, b)); -} - -/* (:colon2 b c) */ -static node* -new_colon2(parser_state *p, node *b, mrb_sym c) -{ - return cons((node*)NODE_COLON2, cons(b, nsym(c))); -} - -/* (:colon3 . c) */ -static node* -new_colon3(parser_state *p, mrb_sym c) -{ - return cons((node*)NODE_COLON3, nsym(c)); -} - -/* (:and a b) */ -static node* -new_and(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_AND, cons(a, b)); -} - -/* (:or a b) */ -static node* -new_or(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_OR, cons(a, b)); -} - -/* (:array a...) */ -static node* -new_array(parser_state *p, node *a) -{ - return cons((node*)NODE_ARRAY, a); -} - -/* (:splat . a) */ -static node* -new_splat(parser_state *p, node *a) -{ - return cons((node*)NODE_SPLAT, a); -} - -/* (:hash (k . v) (k . v)...) */ -static node* -new_hash(parser_state *p, node *a) -{ - return cons((node*)NODE_HASH, a); -} - -/* (:sym . a) */ -static node* -new_sym(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_SYM, nsym(sym)); -} - -static mrb_sym -new_strsym(parser_state *p, node* str) -{ - const char *s = (const char*)str->cdr->car; - size_t len = (size_t)str->cdr->cdr; - - return mrb_intern(p->mrb, s, len); -} - -/* (:lvar . a) */ -static node* -new_lvar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_LVAR, nsym(sym)); -} - -/* (:gvar . a) */ -static node* -new_gvar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_GVAR, nsym(sym)); -} - -/* (:ivar . a) */ -static node* -new_ivar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_IVAR, nsym(sym)); -} - -/* (:cvar . a) */ -static node* -new_cvar(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_CVAR, nsym(sym)); -} - -/* (:const . a) */ -static node* -new_const(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_CONST, nsym(sym)); -} - -/* (:undef a...) */ -static node* -new_undef(parser_state *p, mrb_sym sym) -{ - return list2((node*)NODE_UNDEF, nsym(sym)); -} - -/* (:class class super body) */ -static node* -new_class(parser_state *p, node *c, node *s, node *b) -{ - return list4((node*)NODE_CLASS, c, s, cons(p->locals->car, b)); -} - -/* (:sclass obj body) */ -static node* -new_sclass(parser_state *p, node *o, node *b) -{ - return list3((node*)NODE_SCLASS, o, cons(p->locals->car, b)); -} - -/* (:module module body) */ -static node* -new_module(parser_state *p, node *m, node *b) -{ - return list3((node*)NODE_MODULE, m, cons(p->locals->car, b)); -} - -/* (:def m lv (arg . body)) */ -static node* -new_def(parser_state *p, mrb_sym m, node *a, node *b) -{ - return list5((node*)NODE_DEF, nsym(m), p->locals->car, a, b); -} - -/* (:sdef obj m lv (arg . body)) */ -static node* -new_sdef(parser_state *p, node *o, mrb_sym m, node *a, node *b) -{ - return list6((node*)NODE_SDEF, o, nsym(m), p->locals->car, a, b); -} - -/* (:arg . sym) */ -static node* -new_arg(parser_state *p, mrb_sym sym) -{ - return cons((node*)NODE_ARG, nsym(sym)); -} - -/* (m o r m2 b) */ -/* m: (a b c) */ -/* o: ((a . e1) (b . e2)) */ -/* r: a */ -/* m2: (a b c) */ -/* b: a */ -static node* -new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, mrb_sym blk) -{ - node *n; - - n = cons(m2, nsym(blk)); - n = cons(nsym(rest), n); - n = cons(opt, n); - return cons(m, n); -} - -/* (:block_arg . a) */ -static node* -new_block_arg(parser_state *p, node *a) -{ - return cons((node*)NODE_BLOCK_ARG, a); -} - -/* (:block arg body) */ -static node* -new_block(parser_state *p, node *a, node *b) -{ - return list4((node*)NODE_BLOCK, p->locals->car, a, b); -} - -/* (:lambda arg body) */ -static node* -new_lambda(parser_state *p, node *a, node *b) -{ - return list4((node*)NODE_LAMBDA, p->locals->car, a, b); -} - -/* (:asgn lhs rhs) */ -static node* -new_asgn(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_ASGN, cons(a, b)); -} - -/* (:masgn mlhs=(pre rest post) mrhs) */ -static node* -new_masgn(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_MASGN, cons(a, b)); -} - -/* (:asgn lhs rhs) */ -static node* -new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b) -{ - return list4((node*)NODE_OP_ASGN, a, nsym(op), b); -} - -/* (:int . i) */ -static node* -new_int(parser_state *p, const char *s, int base) -{ - return list3((node*)NODE_INT, (node*)strdup(s), (node*)(intptr_t)base); -} - -/* (:float . i) */ -static node* -new_float(parser_state *p, const char *s) -{ - return cons((node*)NODE_FLOAT, (node*)strdup(s)); -} - -/* (:str . (s . len)) */ -static node* -new_str(parser_state *p, const char *s, int len) -{ - return cons((node*)NODE_STR, cons((node*)strndup(s, len), (node*)(intptr_t)len)); -} - -/* (:dstr . a) */ -static node* -new_dstr(parser_state *p, node *a) -{ - return cons((node*)NODE_DSTR, a); -} - -/* (:str . (s . len)) */ -static node* -new_xstr(parser_state *p, const char *s, int len) -{ - return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), (node*)(intptr_t)len)); -} - -/* (:xstr . a) */ -static node* -new_dxstr(parser_state *p, node *a) -{ - return cons((node*)NODE_DXSTR, a); -} - -/* (:dsym . a) */ -static node* -new_dsym(parser_state *p, node *a) -{ - return cons((node*)NODE_DSYM, new_dstr(p, a)); -} - -/* (:str . (a . a)) */ -static node* -new_regx(parser_state *p, const char *p1, const char* p2) -{ - return cons((node*)NODE_REGX, cons((node*)p1, (node*)p2)); -} - -/* (:dregx . a) */ -static node* -new_dregx(parser_state *p, node *a, node *b) -{ - return cons((node*)NODE_DREGX, cons(a, b)); -} - -/* (:backref . n) */ -static node* -new_back_ref(parser_state *p, int n) -{ - return cons((node*)NODE_BACK_REF, (node*)(intptr_t)n); -} - -/* (:nthref . n) */ -static node* -new_nth_ref(parser_state *p, int n) -{ - return cons((node*)NODE_NTH_REF, (node*)(intptr_t)n); -} - -/* (:heredoc . a) */ -static node* -new_heredoc(parser_state *p) -{ - parser_heredoc_info *inf = (parser_heredoc_info *)parser_palloc(p, sizeof(parser_heredoc_info)); - return cons((node*)NODE_HEREDOC, (node*)inf); -} - -static void -new_bv(parser_state *p, mrb_sym id) -{ -} - -static node* -new_literal_delim(parser_state *p) -{ - return cons((node*)NODE_LITERAL_DELIM, 0); -} - -/* (:words . a) */ -static node* -new_words(parser_state *p, node *a) -{ - return cons((node*)NODE_WORDS, a); -} - -/* (:symbols . a) */ -static node* -new_symbols(parser_state *p, node *a) -{ - return cons((node*)NODE_SYMBOLS, a); -} - -/* xxx ----------------------------- */ - -/* (:call a op) */ -static node* -call_uni_op(parser_state *p, node *recv, const char *m) -{ - return new_call(p, recv, intern_cstr(m), 0); -} - -/* (:call a op b) */ -static node* -call_bin_op(parser_state *p, node *recv, const char *m, node *arg1) -{ - return new_call(p, recv, intern_cstr(m), list1(list1(arg1))); -} - -static void -args_with_block(parser_state *p, node *a, node *b) -{ - if (b) { - if (a->cdr) { - yyerror(p, "both block arg and actual block given"); - } - a->cdr = b; - } -} - -static void -call_with_block(parser_state *p, node *a, node *b) -{ - node *n; - - if (a->car == (node*)NODE_SUPER || - a->car == (node*)NODE_ZSUPER) { - if (!a->cdr) a->cdr = cons(0, b); - else { - args_with_block(p, a->cdr, b); - } - } - else { - n = a->cdr->cdr->cdr; - if (!n->car) n->car = cons(0, b); - else { - args_with_block(p, n->car, b); - } - } -} - -static node* -negate_lit(parser_state *p, node *n) -{ - return cons((node*)NODE_NEGATE, n); -} - -static node* -cond(node *n) -{ - return n; -} - -static node* -ret_args(parser_state *p, node *n) -{ - if (n->cdr) { - yyerror(p, "block argument should not be given"); - return NULL; - } - if (!n->car->cdr) return n->car->car; - return new_array(p, n->car); -} - -static void -assignable(parser_state *p, node *lhs) -{ - if ((int)(intptr_t)lhs->car == NODE_LVAR) { - local_add(p, sym(lhs->cdr)); - } -} - -static node* -var_reference(parser_state *p, node *lhs) -{ - node *n; - - if ((int)(intptr_t)lhs->car == NODE_LVAR) { - if (!local_var_p(p, sym(lhs->cdr))) { - n = new_fcall(p, sym(lhs->cdr), 0); - cons_free(lhs); - return n; - } - } - - return lhs; -} - -typedef enum mrb_string_type string_type; - -static node* -new_strterm(parser_state *p, string_type type, int term, int paren) -{ - return cons((node*)(intptr_t)type, cons((node*)0, cons((node*)(intptr_t)paren, (node*)(intptr_t)term))); -} - -static void -end_strterm(parser_state *p) -{ - cons_free(p->lex_strterm->cdr->cdr); - cons_free(p->lex_strterm->cdr); - cons_free(p->lex_strterm); - p->lex_strterm = NULL; -} - -static parser_heredoc_info * -parsing_heredoc_inf(parser_state *p) -{ - node *nd = p->parsing_heredoc; - if (nd == NULL) - return NULL; - /* mrb_assert(nd->car->car == NODE_HEREDOC); */ - return (parser_heredoc_info*)nd->car->cdr; -} - -static void -heredoc_treat_nextline(parser_state *p) -{ - if (p->heredocs_from_nextline == NULL) - return; - if (p->parsing_heredoc == NULL) { - node *n; - p->parsing_heredoc = p->heredocs_from_nextline; - p->lex_strterm_before_heredoc = p->lex_strterm; - p->lex_strterm = new_strterm(p, parsing_heredoc_inf(p)->type, 0, 0); - n = p->all_heredocs; - if (n) { - while (n->cdr) - n = n->cdr; - n->cdr = p->parsing_heredoc; - } - else { - p->all_heredocs = p->parsing_heredoc; - } - } - else { - node *n, *m; - m = p->heredocs_from_nextline; - while (m->cdr) - m = m->cdr; - n = p->all_heredocs; - mrb_assert(n != NULL); - if (n == p->parsing_heredoc) { - m->cdr = n; - p->all_heredocs = p->heredocs_from_nextline; - p->parsing_heredoc = p->heredocs_from_nextline; - } - else { - while (n->cdr != p->parsing_heredoc) { - n = n->cdr; - mrb_assert(n != NULL); - } - m->cdr = n->cdr; - n->cdr = p->heredocs_from_nextline; - p->parsing_heredoc = p->heredocs_from_nextline; - } - } - p->heredocs_from_nextline = NULL; -} - -static void -heredoc_end(parser_state *p) -{ - p->parsing_heredoc = p->parsing_heredoc->cdr; - if (p->parsing_heredoc == NULL) { - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - end_strterm(p); - p->lex_strterm = p->lex_strterm_before_heredoc; - p->lex_strterm_before_heredoc = NULL; - p->heredoc_end_now = TRUE; - } - else { - /* next heredoc */ - p->lex_strterm->car = (node*)(intptr_t)parsing_heredoc_inf(p)->type; - } -} -#define is_strterm_type(p,str_func) ((int)(intptr_t)((p)->lex_strterm->car) & (str_func)) - -/* xxx ----------------------------- */ - -%} - -%pure-parser -%parse-param {parser_state *p} -%lex-param {parser_state *p} - -%union { - node *nd; - mrb_sym id; - int num; - stack_type stack; - const struct vtable *vars; -} - -%token - keyword_class - keyword_module - keyword_def - keyword_undef - keyword_begin - keyword_rescue - keyword_ensure - keyword_end - keyword_if - keyword_unless - keyword_then - keyword_elsif - keyword_else - keyword_case - keyword_when - keyword_while - keyword_until - keyword_for - keyword_break - keyword_next - keyword_redo - keyword_retry - keyword_in - keyword_do - keyword_do_cond - keyword_do_block - keyword_do_LAMBDA - keyword_return - keyword_yield - keyword_super - keyword_self - keyword_nil - keyword_true - keyword_false - keyword_and - keyword_or - keyword_not - modifier_if - modifier_unless - modifier_while - modifier_until - modifier_rescue - keyword_alias - keyword_BEGIN - keyword_END - keyword__LINE__ - keyword__FILE__ - keyword__ENCODING__ - -%token <id> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR tLABEL -%token <nd> tINTEGER tFLOAT tCHAR tXSTRING tREGEXP -%token <nd> tSTRING tSTRING_PART tSTRING_MID -%token <nd> tNTH_REF tBACK_REF -%token <num> tREGEXP_END - -%type <nd> singleton string string_rep string_interp xstring regexp -%type <nd> literal numeric cpath symbol -%type <nd> top_compstmt top_stmts top_stmt -%type <nd> bodystmt compstmt stmts stmt expr arg primary command command_call method_call -%type <nd> expr_value arg_value primary_value -%type <nd> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure -%type <nd> args call_args opt_call_args -%type <nd> paren_args opt_paren_args variable -%type <nd> command_args aref_args opt_block_arg block_arg var_ref var_lhs -%type <nd> command_asgn mrhs superclass block_call block_command -%type <nd> f_block_optarg f_block_opt -%type <nd> f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs -%type <nd> assoc_list assocs assoc undef_list backref for_var -%type <nd> block_param opt_block_param block_param_def f_opt -%type <nd> bv_decls opt_bv_decl bvar f_larglist lambda_body -%type <nd> brace_block cmd_brace_block do_block lhs none f_bad_arg -%type <nd> mlhs mlhs_list mlhs_post mlhs_basic mlhs_item mlhs_node mlhs_inner -%type <id> fsym sym basic_symbol operation operation2 operation3 -%type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn -%type <nd> heredoc words symbols - -%token tUPLUS /* unary+ */ -%token tUMINUS /* unary- */ -%token tPOW /* ** */ -%token tCMP /* <=> */ -%token tEQ /* == */ -%token tEQQ /* === */ -%token tNEQ /* != */ -%token tGEQ /* >= */ -%token tLEQ /* <= */ -%token tANDOP tOROP /* && and || */ -%token tMATCH tNMATCH /* =~ and !~ */ -%token tDOT2 tDOT3 /* .. and ... */ -%token tAREF tASET /* [] and []= */ -%token tLSHFT tRSHFT /* << and >> */ -%token tCOLON2 /* :: */ -%token tCOLON3 /* :: at EXPR_BEG */ -%token <id> tOP_ASGN /* +=, -= etc. */ -%token tASSOC /* => */ -%token tLPAREN /* ( */ -%token tLPAREN_ARG /* ( */ -%token tRPAREN /* ) */ -%token tLBRACK /* [ */ -%token tLBRACE /* { */ -%token tLBRACE_ARG /* { */ -%token tSTAR /* * */ -%token tAMPER /* & */ -%token tLAMBDA /* -> */ -%token tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG -%token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG -%token <nd> tHEREDOC_BEG /* <<, <<- */ -%token tHEREDOC_END tLITERAL_DELIM tHD_LITERAL_DELIM -%token <nd> tHD_STRING_PART tHD_STRING_MID - -/* - * precedence table - */ - -%nonassoc tLOWEST -%nonassoc tLBRACE_ARG - -%nonassoc modifier_if modifier_unless modifier_while modifier_until -%left keyword_or keyword_and -%right keyword_not -%right '=' tOP_ASGN -%left modifier_rescue -%right '?' ':' -%nonassoc tDOT2 tDOT3 -%left tOROP -%left tANDOP -%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH -%left '>' tGEQ '<' tLEQ -%left '|' '^' -%left '&' -%left tLSHFT tRSHFT -%left '+' '-' -%left '*' '/' '%' -%right tUMINUS_NUM tUMINUS -%right tPOW -%right '!' '~' tUPLUS - -%nonassoc idNULL -%nonassoc idRespond_to -%nonassoc idIFUNC -%nonassoc idCFUNC -%nonassoc id_core_set_method_alias -%nonassoc id_core_set_variable_alias -%nonassoc id_core_undef_method -%nonassoc id_core_define_method -%nonassoc id_core_define_singleton_method -%nonassoc id_core_set_postexe - -%token tLAST_TOKEN - -%% -program : { - p->lstate = EXPR_BEG; - if (!p->locals) p->locals = cons(0,0); - } - top_compstmt - { - p->tree = new_scope(p, $2); - } - ; - -top_compstmt : top_stmts opt_terms - { - $$ = $1; - } - ; - -top_stmts : none - { - $$ = new_begin(p, 0); - } - | top_stmt - { - $$ = new_begin(p, $1); - } - | top_stmts terms top_stmt - { - $$ = push($1, newline_node($3)); - } - | error top_stmt - { - $$ = new_begin(p, 0); - } - ; - -top_stmt : stmt - | keyword_BEGIN - { - $<nd>$ = local_switch(p); - } - '{' top_compstmt '}' - { - yyerror(p, "BEGIN not supported"); - local_resume(p, $<nd>2); - $$ = 0; - } - ; - -bodystmt : compstmt - opt_rescue - opt_else - opt_ensure - { - if ($2) { - $$ = new_rescue(p, $1, $2, $3); - } - else if ($3) { - yywarn(p, "else without rescue is useless"); - $$ = push($1, $3); - } - else { - $$ = $1; - } - if ($4) { - if ($$) { - $$ = new_ensure(p, $$, $4); - } - else { - $$ = push($4, new_nil(p)); - } - } - } - ; - -compstmt : stmts opt_terms - { - $$ = $1; - } - ; - -stmts : none - { - $$ = new_begin(p, 0); - } - | stmt - { - $$ = new_begin(p, $1); - } - | stmts terms stmt - { - $$ = push($1, newline_node($3)); - } - | error stmt - { - $$ = new_begin(p, $2); - } - ; - -stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym - { - $$ = new_alias(p, $2, $4); - } - | keyword_undef undef_list - { - $$ = $2; - } - | stmt modifier_if expr_value - { - $$ = new_if(p, cond($3), $1, 0); - } - | stmt modifier_unless expr_value - { - $$ = new_unless(p, cond($3), $1, 0); - } - | stmt modifier_while expr_value - { - $$ = new_while(p, cond($3), $1); - } - | stmt modifier_until expr_value - { - $$ = new_until(p, cond($3), $1); - } - | stmt modifier_rescue stmt - { - $$ = new_rescue(p, $1, list1(list3(0, 0, $3)), 0); - } - | keyword_END '{' compstmt '}' - { - yyerror(p, "END not suported"); - $$ = new_postexe(p, $3); - } - | command_asgn - | mlhs '=' command_call - { - $$ = new_masgn(p, $1, $3); - } - | var_lhs tOP_ASGN command_call - { - $$ = new_op_asgn(p, $1, $2, $3); - } - | primary_value '[' opt_call_args rbracket tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); - } - | primary_value '.' tIDENTIFIER tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value '.' tCONSTANT tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call - { - yyerror(p, "constant re-assignment"); - $$ = 0; - } - | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | backref tOP_ASGN command_call - { - backref_error(p, $1); - $$ = new_begin(p, 0); - } - | lhs '=' mrhs - { - $$ = new_asgn(p, $1, new_array(p, $3)); - } - | mlhs '=' arg_value - { - $$ = new_masgn(p, $1, $3); - } - | mlhs '=' mrhs - { - $$ = new_masgn(p, $1, new_array(p, $3)); - } - | expr - ; - -command_asgn : lhs '=' command_call - { - $$ = new_asgn(p, $1, $3); - } - | lhs '=' command_asgn - { - $$ = new_asgn(p, $1, $3); - } - ; - - -expr : command_call - | expr keyword_and expr - { - $$ = new_and(p, $1, $3); - } - | expr keyword_or expr - { - $$ = new_or(p, $1, $3); - } - | keyword_not opt_nl expr - { - $$ = call_uni_op(p, cond($3), "!"); - } - | '!' command_call - { - $$ = call_uni_op(p, cond($2), "!"); - } - | arg - ; - -expr_value : expr - { - if (!$1) $$ = new_nil(p); - else $$ = $1; - } - ; - -command_call : command - | block_command - ; - -block_command : block_call - | block_call dot_or_colon operation2 command_args - ; - -cmd_brace_block : tLBRACE_ARG - { - local_nest(p); - } - opt_block_param - compstmt - '}' - { - $$ = new_block(p, $3, $4); - local_unnest(p); - } - ; - -command : operation command_args %prec tLOWEST - { - $$ = new_fcall(p, $1, $2); - } - | operation command_args cmd_brace_block - { - args_with_block(p, $2, $3); - $$ = new_fcall(p, $1, $2); - } - | primary_value '.' operation2 command_args %prec tLOWEST - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value '.' operation2 command_args cmd_brace_block - { - args_with_block(p, $4, $5); - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation2 command_args %prec tLOWEST - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation2 command_args cmd_brace_block - { - args_with_block(p, $4, $5); - $$ = new_call(p, $1, $3, $4); - } - | keyword_super command_args - { - $$ = new_super(p, $2); - } - | keyword_yield command_args - { - $$ = new_yield(p, $2); - } - | keyword_return call_args - { - $$ = new_return(p, ret_args(p, $2)); - } - | keyword_break call_args - { - $$ = new_break(p, ret_args(p, $2)); - } - | keyword_next call_args - { - $$ = new_next(p, ret_args(p, $2)); - } - ; - -mlhs : mlhs_basic - { - $$ = $1; - } - | tLPAREN mlhs_inner rparen - { - $$ = $2; - } - ; - -mlhs_inner : mlhs_basic - | tLPAREN mlhs_inner rparen - { - $$ = list1($2); - } - ; - -mlhs_basic : mlhs_list - { - $$ = list1($1); - } - | mlhs_list mlhs_item - { - $$ = list1(push($1,$2)); - } - | mlhs_list tSTAR mlhs_node - { - $$ = list2($1, $3); - } - | mlhs_list tSTAR mlhs_node ',' mlhs_post - { - $$ = list3($1, $3, $5); - } - | mlhs_list tSTAR - { - $$ = list2($1, new_nil(p)); - } - | mlhs_list tSTAR ',' mlhs_post - { - $$ = list3($1, new_nil(p), $4); - } - | tSTAR mlhs_node - { - $$ = list2(0, $2); - } - | tSTAR mlhs_node ',' mlhs_post - { - $$ = list3(0, $2, $4); - } - | tSTAR - { - $$ = list2(0, new_nil(p)); - } - | tSTAR ',' mlhs_post - { - $$ = list3(0, new_nil(p), $3); - } - ; - -mlhs_item : mlhs_node - | tLPAREN mlhs_inner rparen - { - $$ = $2; - } - ; - -mlhs_list : mlhs_item ',' - { - $$ = list1($1); - } - | mlhs_list mlhs_item ',' - { - $$ = push($1, $2); - } - ; - -mlhs_post : mlhs_item - { - $$ = list1($1); - } - | mlhs_list mlhs_item - { - $$ = push($1, $2); - } - ; - -mlhs_node : variable - { - assignable(p, $1); - } - | primary_value '[' opt_call_args rbracket - { - $$ = new_call(p, $1, intern("[]",2), $3); - } - | primary_value '.' tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value '.' tCONSTANT - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon2(p, $1, $3); - } - | tCOLON3 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon3(p, $2); - } - | backref - { - backref_error(p, $1); - $$ = 0; - } - ; - -lhs : variable - { - assignable(p, $1); - } - | primary_value '[' opt_call_args rbracket - { - $$ = new_call(p, $1, intern("[]",2), $3); - } - | primary_value '.' tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tIDENTIFIER - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value '.' tCONSTANT - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value tCOLON2 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon2(p, $1, $3); - } - | tCOLON3 tCONSTANT - { - if (p->in_def || p->in_single) - yyerror(p, "dynamic constant assignment"); - $$ = new_colon3(p, $2); - } - | backref - { - backref_error(p, $1); - $$ = 0; - } - ; - -cname : tIDENTIFIER - { - yyerror(p, "class/module name must be CONSTANT"); - } - | tCONSTANT - ; - -cpath : tCOLON3 cname - { - $$ = cons((node*)1, nsym($2)); - } - | cname - { - $$ = cons((node*)0, nsym($1)); - } - | primary_value tCOLON2 cname - { - $$ = cons($1, nsym($3)); - } - ; - -fname : tIDENTIFIER - | tCONSTANT - | tFID - | op - { - p->lstate = EXPR_ENDFN; - $$ = $1; - } - | reswords - { - p->lstate = EXPR_ENDFN; - $$ = $<id>1; - } - ; - -fsym : fname - | basic_symbol - ; - -undef_list : fsym - { - $$ = new_undef(p, $1); - } - | undef_list ',' {p->lstate = EXPR_FNAME;} fsym - { - $$ = push($1, nsym($4)); - } - ; - -op : '|' { $$ = intern_c('|'); } - | '^' { $$ = intern_c('^'); } - | '&' { $$ = intern_c('&'); } - | tCMP { $$ = intern("<=>",3); } - | tEQ { $$ = intern("==",2); } - | tEQQ { $$ = intern("===",3); } - | tMATCH { $$ = intern("=~",2); } - | tNMATCH { $$ = intern("!~",2); } - | '>' { $$ = intern_c('>'); } - | tGEQ { $$ = intern(">=",2); } - | '<' { $$ = intern_c('<'); } - | tLEQ { $$ = intern("<=",2); } - | tNEQ { $$ = intern("!=",2); } - | tLSHFT { $$ = intern("<<",2); } - | tRSHFT { $$ = intern(">>",2); } - | '+' { $$ = intern_c('+'); } - | '-' { $$ = intern_c('-'); } - | '*' { $$ = intern_c('*'); } - | tSTAR { $$ = intern_c('*'); } - | '/' { $$ = intern_c('/'); } - | '%' { $$ = intern_c('%'); } - | tPOW { $$ = intern("**",2); } - | '!' { $$ = intern_c('!'); } - | '~' { $$ = intern_c('~'); } - | tUPLUS { $$ = intern("+@",2); } - | tUMINUS { $$ = intern("-@",2); } - | tAREF { $$ = intern("[]",2); } - | tASET { $$ = intern("[]=",3); } - | '`' { $$ = intern_c('`'); } - ; - -reswords : keyword__LINE__ | keyword__FILE__ | keyword__ENCODING__ - | keyword_BEGIN | keyword_END - | keyword_alias | keyword_and | keyword_begin - | keyword_break | keyword_case | keyword_class | keyword_def - | keyword_do | keyword_else | keyword_elsif - | keyword_end | keyword_ensure | keyword_false - | keyword_for | keyword_in | keyword_module | keyword_next - | keyword_nil | keyword_not | keyword_or | keyword_redo - | keyword_rescue | keyword_retry | keyword_return | keyword_self - | keyword_super | keyword_then | keyword_true | keyword_undef - | keyword_when | keyword_yield | keyword_if | keyword_unless - | keyword_while | keyword_until - ; - -arg : lhs '=' arg - { - $$ = new_asgn(p, $1, $3); - } - | lhs '=' arg modifier_rescue arg - { - $$ = new_asgn(p, $1, new_rescue(p, $3, list1(list3(0, 0, $5)), 0)); - } - | var_lhs tOP_ASGN arg - { - $$ = new_op_asgn(p, $1, $2, $3); - } - | var_lhs tOP_ASGN arg modifier_rescue arg - { - $$ = new_op_asgn(p, $1, $2, new_rescue(p, $3, list1(list3(0, 0, $5)), 0)); - } - | primary_value '[' opt_call_args rbracket tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); - } - | primary_value '.' tIDENTIFIER tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value '.' tCONSTANT tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg - { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); - } - | primary_value tCOLON2 tCONSTANT tOP_ASGN arg - { - yyerror(p, "constant re-assignment"); - $$ = new_begin(p, 0); - } - | tCOLON3 tCONSTANT tOP_ASGN arg - { - yyerror(p, "constant re-assignment"); - $$ = new_begin(p, 0); - } - | backref tOP_ASGN arg - { - backref_error(p, $1); - $$ = new_begin(p, 0); - } - | arg tDOT2 arg - { - $$ = new_dot2(p, $1, $3); - } - | arg tDOT3 arg - { - $$ = new_dot3(p, $1, $3); - } - | arg '+' arg - { - $$ = call_bin_op(p, $1, "+", $3); - } - | arg '-' arg - { - $$ = call_bin_op(p, $1, "-", $3); - } - | arg '*' arg - { - $$ = call_bin_op(p, $1, "*", $3); - } - | arg '/' arg - { - $$ = call_bin_op(p, $1, "/", $3); - } - | arg '%' arg - { - $$ = call_bin_op(p, $1, "%", $3); - } - | arg tPOW arg - { - $$ = call_bin_op(p, $1, "**", $3); - } - | tUMINUS_NUM tINTEGER tPOW arg - { - $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@"); - } - | tUMINUS_NUM tFLOAT tPOW arg - { - $$ = call_uni_op(p, call_bin_op(p, $2, "**", $4), "-@"); - } - | tUPLUS arg - { - $$ = call_uni_op(p, $2, "+@"); - } - | tUMINUS arg - { - $$ = call_uni_op(p, $2, "-@"); - } - | arg '|' arg - { - $$ = call_bin_op(p, $1, "|", $3); - } - | arg '^' arg - { - $$ = call_bin_op(p, $1, "^", $3); - } - | arg '&' arg - { - $$ = call_bin_op(p, $1, "&", $3); - } - | arg tCMP arg - { - $$ = call_bin_op(p, $1, "<=>", $3); - } - | arg '>' arg - { - $$ = call_bin_op(p, $1, ">", $3); - } - | arg tGEQ arg - { - $$ = call_bin_op(p, $1, ">=", $3); - } - | arg '<' arg - { - $$ = call_bin_op(p, $1, "<", $3); - } - | arg tLEQ arg - { - $$ = call_bin_op(p, $1, "<=", $3); - } - | arg tEQ arg - { - $$ = call_bin_op(p, $1, "==", $3); - } - | arg tEQQ arg - { - $$ = call_bin_op(p, $1, "===", $3); - } - | arg tNEQ arg - { - $$ = call_bin_op(p, $1, "!=", $3); - } - | arg tMATCH arg - { - $$ = call_bin_op(p, $1, "=~", $3); - } - | arg tNMATCH arg - { - $$ = call_bin_op(p, $1, "!~", $3); - } - | '!' arg - { - $$ = call_uni_op(p, cond($2), "!"); - } - | '~' arg - { - $$ = call_uni_op(p, cond($2), "~"); - } - | arg tLSHFT arg - { - $$ = call_bin_op(p, $1, "<<", $3); - } - | arg tRSHFT arg - { - $$ = call_bin_op(p, $1, ">>", $3); - } - | arg tANDOP arg - { - $$ = new_and(p, $1, $3); - } - | arg tOROP arg - { - $$ = new_or(p, $1, $3); - } - | arg '?' arg opt_nl ':' arg - { - $$ = new_if(p, cond($1), $3, $6); - } - | primary - { - $$ = $1; - } - ; - -arg_value : arg - { - $$ = $1; - if (!$$) $$ = new_nil(p); - } - ; - -aref_args : none - | args trailer - { - $$ = $1; - } - | args ',' assocs trailer - { - $$ = push($1, new_hash(p, $3)); - } - | assocs trailer - { - $$ = cons(new_hash(p, $1), 0); - } - ; - -paren_args : '(' opt_call_args rparen - { - $$ = $2; - } - ; - -opt_paren_args : none - | paren_args - ; - -opt_call_args : none - | call_args - | args ',' - { - $$ = cons($1,0); - } - | args ',' assocs ',' - { - $$ = cons(push($1, new_hash(p, $3)), 0); - } - | assocs ',' - { - $$ = cons(list1(new_hash(p, $1)), 0); - } - ; - -call_args : command - { - $$ = cons(list1($1), 0); - } - | args opt_block_arg - { - $$ = cons($1, $2); - } - | assocs opt_block_arg - { - $$ = cons(list1(new_hash(p, $1)), $2); - } - | args ',' assocs opt_block_arg - { - $$ = cons(push($1, new_hash(p, $3)), $4); - } - | block_arg - { - $$ = cons(0, $1); - } - ; - -command_args : { - $<stack>$ = p->cmdarg_stack; - CMDARG_PUSH(1); - } - call_args - { - p->cmdarg_stack = $<stack>1; - $$ = $2; - } - ; - -block_arg : tAMPER arg_value - { - $$ = new_block_arg(p, $2); - } - ; - -opt_block_arg : ',' block_arg - { - $$ = $2; - } - | none - { - $$ = 0; - } - ; - -args : arg_value - { - $$ = cons($1, 0); - } - | tSTAR arg_value - { - $$ = cons(new_splat(p, $2), 0); - } - | args ',' arg_value - { - $$ = push($1, $3); - } - | args ',' tSTAR arg_value - { - $$ = push($1, new_splat(p, $4)); - } - | args ',' heredoc_bodies arg_value - { - $$ = push($1, $4); - } - | args ',' heredoc_bodies tSTAR arg_value - { - $$ = push($1, new_splat(p, $5)); - } - ; - -mrhs : args ',' arg_value - { - $$ = push($1, $3); - } - | args ',' tSTAR arg_value - { - $$ = push($1, new_splat(p, $4)); - } - | tSTAR arg_value - { - $$ = list1(new_splat(p, $2)); - } - ; - -primary : literal - | string - | xstring - | regexp - | heredoc - | var_ref - | backref - | tFID - { - $$ = new_fcall(p, $1, 0); - } - | keyword_begin - { - $<stack>1 = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - bodystmt - keyword_end - { - p->cmdarg_stack = $<stack>1; - $$ = $3; - } - | tLPAREN_ARG - { - $<stack>1 = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - expr {p->lstate = EXPR_ENDARG;} rparen - { - p->cmdarg_stack = $<stack>1; - $$ = $3; - } - | tLPAREN_ARG {p->lstate = EXPR_ENDARG;} rparen - { - $$ = 0; - } - | tLPAREN compstmt ')' - { - $$ = $2; - } - | primary_value tCOLON2 tCONSTANT - { - $$ = new_colon2(p, $1, $3); - } - | tCOLON3 tCONSTANT - { - $$ = new_colon3(p, $2); - } - | tLBRACK aref_args ']' - { - $$ = new_array(p, $2); - } - | tLBRACE assoc_list '}' - { - $$ = new_hash(p, $2); - } - | keyword_return - { - $$ = new_return(p, 0); - } - | keyword_yield '(' call_args rparen - { - $$ = new_yield(p, $3); - } - | keyword_yield '(' rparen - { - $$ = new_yield(p, 0); - } - | keyword_yield - { - $$ = new_yield(p, 0); - } - | keyword_not '(' expr rparen - { - $$ = call_uni_op(p, cond($3), "!"); - } - | keyword_not '(' rparen - { - $$ = call_uni_op(p, new_nil(p), "!"); - } - | operation brace_block - { - $$ = new_fcall(p, $1, cons(0, $2)); - } - | method_call - | method_call brace_block - { - call_with_block(p, $1, $2); - $$ = $1; - } - | tLAMBDA - { - local_nest(p); - $<num>$ = p->lpar_beg; - p->lpar_beg = ++p->paren_nest; - } - f_larglist - lambda_body - { - p->lpar_beg = $<num>2; - $$ = new_lambda(p, $3, $4); - local_unnest(p); - } - | keyword_if expr_value then - compstmt - if_tail - keyword_end - { - $$ = new_if(p, cond($2), $4, $5); - } - | keyword_unless expr_value then - compstmt - opt_else - keyword_end - { - $$ = new_unless(p, cond($2), $4, $5); - } - | keyword_while {COND_PUSH(1);} expr_value do {COND_POP();} - compstmt - keyword_end - { - $$ = new_while(p, cond($3), $6); - } - | keyword_until {COND_PUSH(1);} expr_value do {COND_POP();} - compstmt - keyword_end - { - $$ = new_until(p, cond($3), $6); - } - | keyword_case expr_value opt_terms - case_body - keyword_end - { - $$ = new_case(p, $2, $4); - } - | keyword_case opt_terms case_body keyword_end - { - $$ = new_case(p, 0, $3); - } - | keyword_for for_var keyword_in - {COND_PUSH(1);} - expr_value do - {COND_POP();} - compstmt - keyword_end - { - $$ = new_for(p, $2, $5, $8); - } - | keyword_class - { - $<num>$ = p->lineno; - } - cpath superclass - { - if (p->in_def || p->in_single) - yyerror(p, "class definition in method body"); - $<nd>$ = local_switch(p); - } - bodystmt - keyword_end - { - $$ = new_class(p, $3, $4, $6); - SET_LINENO($$, $<num>2); - local_resume(p, $<nd>5); - } - | keyword_class - { - $<num>$ = p->lineno; - } - tLSHFT expr - { - $<num>$ = p->in_def; - p->in_def = 0; - } - term - { - $<nd>$ = cons(local_switch(p), (node*)(intptr_t)p->in_single); - p->in_single = 0; - } - bodystmt - keyword_end - { - $$ = new_sclass(p, $4, $8); - SET_LINENO($$, $<num>2); - local_resume(p, $<nd>7->car); - p->in_def = $<num>5; - p->in_single = (int)(intptr_t)$<nd>7->cdr; - } - | keyword_module - { - $<num>$ = p->lineno; - } - cpath - { - if (p->in_def || p->in_single) - yyerror(p, "module definition in method body"); - $<nd>$ = local_switch(p); - } - bodystmt - keyword_end - { - $$ = new_module(p, $3, $5); - SET_LINENO($$, $<num>2); - local_resume(p, $<nd>4); - } - | keyword_def fname - { - p->in_def++; - $<nd>$ = local_switch(p); - $<stack>1 = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - f_arglist - bodystmt - keyword_end - { - $$ = new_def(p, $2, $4, $5); - local_resume(p, $<nd>3); - p->in_def--; - p->cmdarg_stack = $<stack>1; - } - | keyword_def singleton dot_or_colon {p->lstate = EXPR_FNAME;} fname - { - p->in_single++; - p->lstate = EXPR_ENDFN; /* force for args */ - $<nd>$ = local_switch(p); - $<stack>1 = p->cmdarg_stack; - p->cmdarg_stack = 0; - } - f_arglist - bodystmt - keyword_end - { - $$ = new_sdef(p, $2, $5, $7, $8); - local_resume(p, $<nd>6); - p->in_single--; - p->cmdarg_stack = $<stack>1; - } - | keyword_break - { - $$ = new_break(p, 0); - } - | keyword_next - { - $$ = new_next(p, 0); - } - | keyword_redo - { - $$ = new_redo(p); - } - | keyword_retry - { - $$ = new_retry(p); - } - ; - -primary_value : primary - { - $$ = $1; - if (!$$) $$ = new_nil(p); - } - ; - -then : term - | keyword_then - | term keyword_then - ; - -do : term - | keyword_do_cond - ; - -if_tail : opt_else - | keyword_elsif expr_value then - compstmt - if_tail - { - $$ = new_if(p, cond($2), $4, $5); - } - ; - -opt_else : none - | keyword_else compstmt - { - $$ = $2; - } - ; - -for_var : lhs - { - $$ = list1(list1($1)); - } - | mlhs - ; - -f_marg : f_norm_arg - { - $$ = new_arg(p, $1); - } - | tLPAREN f_margs rparen - { - $$ = new_masgn(p, $2, 0); - } - ; - -f_marg_list : f_marg - { - $$ = list1($1); - } - | f_marg_list ',' f_marg - { - $$ = push($1, $3); - } - ; - -f_margs : f_marg_list - { - $$ = list3($1,0,0); - } - | f_marg_list ',' tSTAR f_norm_arg - { - $$ = list3($1, new_arg(p, $4), 0); - } - | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list - { - $$ = list3($1, new_arg(p, $4), $6); - } - | f_marg_list ',' tSTAR - { - $$ = list3($1, (node*)-1, 0); - } - | f_marg_list ',' tSTAR ',' f_marg_list - { - $$ = list3($1, (node*)-1, $5); - } - | tSTAR f_norm_arg - { - $$ = list3(0, new_arg(p, $2), 0); - } - | tSTAR f_norm_arg ',' f_marg_list - { - $$ = list3(0, new_arg(p, $2), $4); - } - | tSTAR - { - $$ = list3(0, (node*)-1, 0); - } - | tSTAR ',' f_marg_list - { - $$ = list3(0, (node*)-1, $3); - } - ; - -block_param : f_arg ',' f_block_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, 0, $6); - } - | f_arg ',' f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, $7, $8); - } - | f_arg ',' f_block_optarg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, 0, $4); - } - | f_arg ',' f_block_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, $5, $6); - } - | f_arg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, 0, $4); - } - | f_arg ',' - { - $$ = new_args(p, $1, 0, 1, 0, 0); - } - | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, $5, $6); - } - | f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, 0, 0, $2); - } - | f_block_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, 0, $4); - } - | f_block_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, $5, $6); - } - | f_block_optarg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, 0, $2); - } - | f_block_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, $3, $4); - } - | f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, 0, $2); - } - | f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, $3, $4); - } - | f_block_arg - { - $$ = new_args(p, 0, 0, 0, 0, $1); - } - ; - -opt_block_param : none - | block_param_def - { - p->cmd_start = TRUE; - $$ = $1; - } - ; - -block_param_def : '|' opt_bv_decl '|' - { - local_add_f(p, 0); - $$ = 0; - } - | tOROP - { - local_add_f(p, 0); - $$ = 0; - } - | '|' block_param opt_bv_decl '|' - { - $$ = $2; - } - ; - - -opt_bv_decl : opt_nl - { - $$ = 0; - } - | opt_nl ';' bv_decls opt_nl - { - $$ = 0; - } - ; - -bv_decls : bvar - | bv_decls ',' bvar - ; - -bvar : tIDENTIFIER - { - local_add_f(p, $1); - new_bv(p, $1); - } - | f_bad_arg - ; - -f_larglist : '(' f_args opt_bv_decl ')' - { - $$ = $2; - } - | f_args - { - $$ = $1; - } - ; - -lambda_body : tLAMBEG compstmt '}' - { - $$ = $2; - } - | keyword_do_LAMBDA compstmt keyword_end - { - $$ = $2; - } - ; - -do_block : keyword_do_block - { - local_nest(p); - } - opt_block_param - compstmt - keyword_end - { - $$ = new_block(p,$3,$4); - local_unnest(p); - } - ; - -block_call : command do_block - { - if ($1->car == (node*)NODE_YIELD) { - yyerror(p, "block given to yield"); - } - else { - call_with_block(p, $1, $2); - } - $$ = $1; - } - | block_call dot_or_colon operation2 opt_paren_args - { - $$ = new_call(p, $1, $3, $4); - } - | block_call dot_or_colon operation2 opt_paren_args brace_block - { - $$ = new_call(p, $1, $3, $4); - call_with_block(p, $$, $5); - } - | block_call dot_or_colon operation2 command_args do_block - { - $$ = new_call(p, $1, $3, $4); - call_with_block(p, $$, $5); - } - ; - -method_call : operation paren_args - { - $$ = new_fcall(p, $1, $2); - } - | primary_value '.' operation2 opt_paren_args - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation2 paren_args - { - $$ = new_call(p, $1, $3, $4); - } - | primary_value tCOLON2 operation3 - { - $$ = new_call(p, $1, $3, 0); - } - | primary_value '.' paren_args - { - $$ = new_call(p, $1, intern("call",4), $3); - } - | primary_value tCOLON2 paren_args - { - $$ = new_call(p, $1, intern("call",4), $3); - } - | keyword_super paren_args - { - $$ = new_super(p, $2); - } - | keyword_super - { - $$ = new_zsuper(p); - } - | primary_value '[' opt_call_args rbracket - { - $$ = new_call(p, $1, intern("[]",2), $3); - } - ; - -brace_block : '{' - { - local_nest(p); - $<num>$ = p->lineno; - } - opt_block_param - compstmt '}' - { - $$ = new_block(p,$3,$4); - SET_LINENO($$, $<num>2); - local_unnest(p); - } - | keyword_do - { - local_nest(p); - $<num>$ = p->lineno; - } - opt_block_param - compstmt keyword_end - { - $$ = new_block(p,$3,$4); - SET_LINENO($$, $<num>2); - local_unnest(p); - } - ; - -case_body : keyword_when args then - compstmt - cases - { - $$ = cons(cons($2, $4), $5); - } - ; - -cases : opt_else - { - if ($1) { - $$ = cons(cons(0, $1), 0); - } - else { - $$ = 0; - } - } - | case_body - ; - -opt_rescue : keyword_rescue exc_list exc_var then - compstmt - opt_rescue - { - $$ = list1(list3($2, $3, $5)); - if ($6) $$ = append($$, $6); - } - | none - ; - -exc_list : arg_value - { - $$ = list1($1); - } - | mrhs - | none - ; - -exc_var : tASSOC lhs - { - $$ = $2; - } - | none - ; - -opt_ensure : keyword_ensure compstmt - { - $$ = $2; - } - | none - ; - -literal : numeric - | symbol - | words - | symbols - ; - -string : tCHAR - | tSTRING - | tSTRING_BEG tSTRING - { - $$ = $2; - } - | tSTRING_BEG string_rep tSTRING - { - $$ = new_dstr(p, push($2, $3)); - } - ; - -string_rep : string_interp - | string_rep string_interp - { - $$ = append($1, $2); - } - ; - -string_interp : tSTRING_MID - { - $$ = list1($1); - } - | tSTRING_PART - { - $<nd>$ = p->lex_strterm; - p->lex_strterm = NULL; - } - compstmt - '}' - { - p->lex_strterm = $<nd>2; - $$ = list2($1, $3); - } - | tLITERAL_DELIM - { - $$ = list1(new_literal_delim(p)); - } - | tHD_LITERAL_DELIM heredoc_bodies - { - $$ = list1(new_literal_delim(p)); - } - ; - -xstring : tXSTRING_BEG tXSTRING - { - $$ = $2; - } - | tXSTRING_BEG string_rep tXSTRING - { - $$ = new_dxstr(p, push($2, $3)); - } - ; - -regexp : tREGEXP_BEG tREGEXP - { - $$ = $2; - } - | tREGEXP_BEG string_rep tREGEXP - { - $$ = new_dregx(p, $2, $3); - } - ; - -heredoc : tHEREDOC_BEG - ; - -opt_heredoc_bodies : /* none */ - | heredoc_bodies - ; - -heredoc_bodies : heredoc_body - | heredoc_bodies heredoc_body - ; - -heredoc_body : tHEREDOC_END - { - parser_heredoc_info * inf = parsing_heredoc_inf(p); - inf->doc = push(inf->doc, new_str(p, "", 0)); - heredoc_end(p); - } - | heredoc_string_rep tHEREDOC_END - { - heredoc_end(p); - } - ; - -heredoc_string_rep : heredoc_string_interp - | heredoc_string_rep heredoc_string_interp - ; - -heredoc_string_interp : tHD_STRING_MID - { - parser_heredoc_info * inf = parsing_heredoc_inf(p); - inf->doc = push(inf->doc, $1); - heredoc_treat_nextline(p); - } - | tHD_STRING_PART - { - $<nd>$ = p->lex_strterm; - p->lex_strterm = NULL; - } - compstmt - '}' - { - parser_heredoc_info * inf = parsing_heredoc_inf(p); - p->lex_strterm = $<nd>2; - inf->doc = push(push(inf->doc, $1), $3); - } - ; - -words : tWORDS_BEG tSTRING - { - $$ = new_words(p, list1($2)); - } - | tWORDS_BEG string_rep tSTRING - { - $$ = new_words(p, push($2, $3)); - } - ; - - -symbol : basic_symbol - { - $$ = new_sym(p, $1); - } - | tSYMBEG tSTRING_BEG string_interp tSTRING - { - p->lstate = EXPR_END; - $$ = new_dsym(p, push($3, $4)); - } - ; - -basic_symbol : tSYMBEG sym - { - p->lstate = EXPR_END; - $$ = $2; - } - ; - -sym : fname - | tIVAR - | tGVAR - | tCVAR - | tSTRING - { - $$ = new_strsym(p, $1); - } - | tSTRING_BEG tSTRING - { - $$ = new_strsym(p, $2); - } - ; - -symbols : tSYMBOLS_BEG tSTRING - { - $$ = new_symbols(p, list1($2)); - } - | tSYMBOLS_BEG string_rep tSTRING - { - $$ = new_symbols(p, push($2, $3)); - } - ; - -numeric : tINTEGER - | tFLOAT - | tUMINUS_NUM tINTEGER %prec tLOWEST - { - $$ = negate_lit(p, $2); - } - | tUMINUS_NUM tFLOAT %prec tLOWEST - { - $$ = negate_lit(p, $2); - } - ; - -variable : tIDENTIFIER - { - $$ = new_lvar(p, $1); - } - | tIVAR - { - $$ = new_ivar(p, $1); - } - | tGVAR - { - $$ = new_gvar(p, $1); - } - | tCVAR - { - $$ = new_cvar(p, $1); - } - | tCONSTANT - { - $$ = new_const(p, $1); - } - ; - -var_lhs : variable - { - assignable(p, $1); - } - ; - -var_ref : variable - { - $$ = var_reference(p, $1); - } - | keyword_nil - { - $$ = new_nil(p); - } - | keyword_self - { - $$ = new_self(p); - } - | keyword_true - { - $$ = new_true(p); - } - | keyword_false - { - $$ = new_false(p); - } - | keyword__FILE__ - { - if (!p->filename) { - p->filename = "(null)"; - } - $$ = new_str(p, p->filename, strlen(p->filename)); - } - | keyword__LINE__ - { - char buf[16]; - - snprintf(buf, sizeof(buf), "%d", p->lineno); - $$ = new_int(p, buf, 10); - } - ; - -backref : tNTH_REF - | tBACK_REF - ; - -superclass : term - { - $$ = 0; - } - | '<' - { - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - } - expr_value term - { - $$ = $3; - } - | error term - { - yyerrok; - $$ = 0; - } - ; - -f_arglist : '(' f_args rparen - { - $$ = $2; - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - } - | f_args term - { - $$ = $1; - } - ; - -f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, 0, $6); - } - | f_arg ',' f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, $5, $7, $8); - } - | f_arg ',' f_optarg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, 0, $4); - } - | f_arg ',' f_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, $3, 0, $5, $6); - } - | f_arg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, 0, $4); - } - | f_arg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, $3, $5, $6); - } - | f_arg opt_f_block_arg - { - $$ = new_args(p, $1, 0, 0, 0, $2); - } - | f_optarg ',' f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, 0, $4); - } - | f_optarg ',' f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, $3, $5, $6); - } - | f_optarg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, 0, $2); - } - | f_optarg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, $1, 0, $3, $4); - } - | f_rest_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, 0, $2); - } - | f_rest_arg ',' f_arg opt_f_block_arg - { - $$ = new_args(p, 0, 0, $1, $3, $4); - } - | f_block_arg - { - $$ = new_args(p, 0, 0, 0, 0, $1); - } - | /* none */ - { - local_add_f(p, 0); - $$ = new_args(p, 0, 0, 0, 0, 0); - } - ; - -f_bad_arg : tCONSTANT - { - yyerror(p, "formal argument cannot be a constant"); - $$ = 0; - } - | tIVAR - { - yyerror(p, "formal argument cannot be an instance variable"); - $$ = 0; - } - | tGVAR - { - yyerror(p, "formal argument cannot be a global variable"); - $$ = 0; - } - | tCVAR - { - yyerror(p, "formal argument cannot be a class variable"); - $$ = 0; - } - ; - -f_norm_arg : f_bad_arg - { - $$ = 0; - } - | tIDENTIFIER - { - local_add_f(p, $1); - $$ = $1; - } - ; - -f_arg_item : f_norm_arg - { - $$ = new_arg(p, $1); - } - | tLPAREN f_margs rparen - { - $$ = new_masgn(p, $2, 0); - } - ; - -f_arg : f_arg_item - { - $$ = list1($1); - } - | f_arg ',' f_arg_item - { - $$ = push($1, $3); - } - ; - -f_opt_asgn : tIDENTIFIER '=' - { - local_add_f(p, $1); - $$ = $1; - } - ; - -f_opt : f_opt_asgn arg_value - { - $$ = cons(nsym($1), $2); - } - ; - -f_block_opt : f_opt_asgn primary_value - { - $$ = cons(nsym($1), $2); - } - ; - -f_block_optarg : f_block_opt - { - $$ = list1($1); - } - | f_block_optarg ',' f_block_opt - { - $$ = push($1, $3); - } - ; - -f_optarg : f_opt - { - $$ = list1($1); - } - | f_optarg ',' f_opt - { - $$ = push($1, $3); - } - ; - -restarg_mark : '*' - | tSTAR - ; - -f_rest_arg : restarg_mark tIDENTIFIER - { - local_add_f(p, $2); - $$ = $2; - } - | restarg_mark - { - local_add_f(p, 0); - $$ = -1; - } - ; - -blkarg_mark : '&' - | tAMPER - ; - -f_block_arg : blkarg_mark tIDENTIFIER - { - local_add_f(p, $2); - $$ = $2; - } - ; - -opt_f_block_arg : ',' f_block_arg - { - $$ = $2; - } - | none - { - local_add_f(p, 0); - $$ = 0; - } - ; - -singleton : var_ref - { - $$ = $1; - if (!$$) $$ = new_nil(p); - } - | '(' {p->lstate = EXPR_BEG;} expr rparen - { - if ($3 == 0) { - yyerror(p, "can't define singleton method for ()."); - } - else { - switch ((enum node_type)(int)(intptr_t)$3->car) { - case NODE_STR: - case NODE_DSTR: - case NODE_XSTR: - case NODE_DXSTR: - case NODE_DREGX: - case NODE_MATCH: - case NODE_FLOAT: - case NODE_ARRAY: - case NODE_HEREDOC: - yyerror(p, "can't define singleton method for literals"); - default: - break; - } - } - $$ = $3; - } - ; - -assoc_list : none - | assocs trailer - { - $$ = $1; - } - ; - -assocs : assoc - { - $$ = list1($1); - } - | assocs ',' assoc - { - $$ = push($1, $3); - } - ; - -assoc : arg_value tASSOC arg_value - { - $$ = cons($1, $3); - } - | tLABEL arg_value - { - $$ = cons(new_sym(p, $1), $2); - } - ; - -operation : tIDENTIFIER - | tCONSTANT - | tFID - ; - -operation2 : tIDENTIFIER - | tCONSTANT - | tFID - | op - ; - -operation3 : tIDENTIFIER - | tFID - | op - ; - -dot_or_colon : '.' - | tCOLON2 - ; - -opt_terms : /* none */ - | terms - ; - -opt_nl : /* none */ - | nl - ; - -rparen : opt_nl ')' - ; - -rbracket : opt_nl ']' - ; - -trailer : /* none */ - | nl - | ',' - ; - -term : ';' {yyerrok;} - | nl - ; - -nl : '\n' - { - p->lineno++; - p->column = 0; - } - opt_heredoc_bodies - -terms : term - | terms ';' {yyerrok;} - ; - -none : /* none */ - { - $$ = 0; - } - ; -%% -#define yylval (*((YYSTYPE*)(p->ylval))) - -static void -yyerror(parser_state *p, const char *s) -{ - char* c; - int n; - - if (! p->capture_errors) { -#ifdef ENABLE_STDIO - if (p->filename) { - fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s); - } - else { - fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); - } -#endif - } - else if (p->nerr < sizeof(p->error_buffer) / sizeof(p->error_buffer[0])) { - n = strlen(s); - c = (char *)parser_palloc(p, n + 1); - memcpy(c, s, n + 1); - p->error_buffer[p->nerr].message = c; - p->error_buffer[p->nerr].lineno = p->lineno; - p->error_buffer[p->nerr].column = p->column; - } - p->nerr++; -} - -static void -yyerror_i(parser_state *p, const char *fmt, int i) -{ - char buf[256]; - - snprintf(buf, sizeof(buf), fmt, i); - yyerror(p, buf); -} - -static void -yywarn(parser_state *p, const char *s) -{ - char* c; - int n; - - if (! p->capture_errors) { -#ifdef ENABLE_STDIO - if (p->filename) { - fprintf(stderr, "%s:%d:%d: %s\n", p->filename, p->lineno, p->column, s); - } - else { - fprintf(stderr, "line %d:%d: %s\n", p->lineno, p->column, s); - } -#endif - } - else if (p->nwarn < sizeof(p->warn_buffer) / sizeof(p->warn_buffer[0])) { - n = strlen(s); - c = (char *)parser_palloc(p, n + 1); - memcpy(c, s, n + 1); - p->warn_buffer[p->nwarn].message = c; - p->warn_buffer[p->nwarn].lineno = p->lineno; - p->warn_buffer[p->nwarn].column = p->column; - } - p->nwarn++; -} - -static void -yywarning(parser_state *p, const char *s) -{ - yywarn(p, s); -} - -static void -yywarning_s(parser_state *p, const char *fmt, const char *s) -{ - char buf[256]; - - snprintf(buf, sizeof(buf), fmt, s); - yywarning(p, buf); -} - -static void -backref_error(parser_state *p, node *n) -{ - int c; - - c = (int)(intptr_t)n->car; - - if (c == NODE_NTH_REF) { - yyerror_i(p, "can't set variable $%d", (int)(intptr_t)n->cdr); - } - else if (c == NODE_BACK_REF) { - yyerror_i(p, "can't set variable $%c", (int)(intptr_t)n->cdr); - } - else { - mrb_bug(p->mrb, "Internal error in backref_error() : n=>car == %S", mrb_fixnum_value(c)); - } -} - -static void pushback(parser_state *p, int c); -static mrb_bool peeks(parser_state *p, const char *s); -static mrb_bool skips(parser_state *p, const char *s); - -static inline int -nextc(parser_state *p) -{ - int c; - - if (p->pb) { - node *tmp; - - c = (int)(intptr_t)p->pb->car; - tmp = p->pb; - p->pb = p->pb->cdr; - cons_free(tmp); - } - else { -#ifdef ENABLE_STDIO - if (p->f) { - if (feof(p->f)) goto eof; - c = fgetc(p->f); - if (c == EOF) goto eof; - } - else -#endif - if (!p->s || p->s >= p->send) { - goto eof; - } - else { - c = (unsigned char)*p->s++; - } - } - if (c >= 0) { - p->column++; - } - if (c == '\r') { - c = nextc(p); - if (c != '\n') { - pushback(p, c); - return '\r'; - } - return c; - } - return c; - - eof: - if (!p->cxt) return -1; - else { - if (p->cxt->partial_hook(p) < 0) - return -1; /* end of program(s) */ - return -2; /* end of a file in the program files */ - } -} - -static void -pushback(parser_state *p, int c) -{ - if (c >= 0) { - p->column--; - } - p->pb = cons((node*)(intptr_t)c, p->pb); -} - -static void -skip(parser_state *p, char term) -{ - int c; - - for (;;) { - c = nextc(p); - if (c < 0) break; - if (c == term) break; - } -} - -static int -peekc_n(parser_state *p, int n) -{ - node *list = 0; - int c0; - - do { - c0 = nextc(p); - if (c0 == -1) return c0; /* do not skip partial EOF */ - list = push(list, (node*)(intptr_t)c0); - } while(n--); - if (p->pb) { - p->pb = append((node*)list, p->pb); - } - else { - p->pb = list; - } - return c0; -} - -static mrb_bool -peek_n(parser_state *p, int c, int n) -{ - return peekc_n(p, n) == c && c >= 0; -} -#define peek(p,c) peek_n((p), (c), 0) - -static mrb_bool -peeks(parser_state *p, const char *s) -{ - int len = strlen(s); - -#ifdef ENABLE_STDIO - if (p->f) { - int n = 0; - while (*s) { - if (!peek_n(p, *s++, n++)) return FALSE; - } - return TRUE; - } - else -#endif - if (p->s && p->s + len <= p->send) { - if (memcmp(p->s, s, len) == 0) return TRUE; - } - return FALSE; -} - -static mrb_bool -skips(parser_state *p, const char *s) -{ - int c; - - for (;;) { - /* skip until first char */ - for (;;) { - c = nextc(p); - if (c < 0) return c; - if (c == '\n') { - p->lineno++; - p->column = 0; - } - if (c == *s) break; - } - s++; - if (peeks(p, s)) { - int len = strlen(s); - - while (len--) { - if (nextc(p) == '\n') { - p->lineno++; - p->column = 0; - } - } - return TRUE; - } - else{ - s--; - } - } - return FALSE; -} - - -static int -newtok(parser_state *p) -{ - p->bidx = 0; - return p->column - 1; -} - -static void -tokadd(parser_state *p, int32_t c) -{ - char utf8[4]; - unsigned len; - - /* mrb_assert(-0x10FFFF <= c && c <= 0xFF); */ - if (c >= 0) { - /* Single byte from source or non-Unicode escape */ - utf8[0] = (char)c; - len = 1; - } - else { - /* Unicode character */ - c = -c; - if (c < 0x80) { - utf8[0] = (char)c; - len = 1; - } - else if (c < 0x800) { - utf8[0] = (char)(0xC0 | (c >> 6)); - utf8[1] = (char)(0x80 | (c & 0x3F)); - len = 2; - } - else if (c < 0x10000) { - utf8[0] = (char)(0xE0 | (c >> 12) ); - utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F)); - utf8[2] = (char)(0x80 | ( c & 0x3F)); - len = 3; - } - else { - utf8[0] = (char)(0xF0 | (c >> 18) ); - utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F)); - utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F)); - utf8[3] = (char)(0x80 | ( c & 0x3F)); - len = 4; - } - } - if (p->bidx+len <= MRB_PARSER_BUF_SIZE) { - unsigned i; - for (i = 0; i < len; i++) { - p->buf[p->bidx++] = utf8[i]; - } - } -} - -static int -toklast(parser_state *p) -{ - return p->buf[p->bidx-1]; -} - -static void -tokfix(parser_state *p) -{ - if (p->bidx >= MRB_PARSER_BUF_SIZE) { - yyerror(p, "string too long (truncated)"); - } - p->buf[p->bidx] = '\0'; -} - -static const char* -tok(parser_state *p) -{ - return p->buf; -} - -static int -toklen(parser_state *p) -{ - return p->bidx; -} - -#define IS_ARG() (p->lstate == EXPR_ARG || p->lstate == EXPR_CMDARG) -#define IS_END() (p->lstate == EXPR_END || p->lstate == EXPR_ENDARG || p->lstate == EXPR_ENDFN) -#define IS_BEG() (p->lstate == EXPR_BEG || p->lstate == EXPR_MID || p->lstate == EXPR_VALUE || p->lstate == EXPR_CLASS) -#define IS_SPCARG(c) (IS_ARG() && space_seen && !ISSPACE(c)) -#define IS_LABEL_POSSIBLE() ((p->lstate == EXPR_BEG && !cmd_state) || IS_ARG()) -#define IS_LABEL_SUFFIX(n) (peek_n(p, ':',(n)) && !peek_n(p, ':', (n)+1)) - -static int -scan_oct(const int *start, int len, int *retlen) -{ - const int *s = start; - int retval = 0; - - /* mrb_assert(len <= 3) */ - while (len-- && *s >= '0' && *s <= '7') { - retval <<= 3; - retval |= *s++ - '0'; - } - *retlen = s - start; - - return retval; -} - -static int32_t -scan_hex(const int *start, int len, int *retlen) -{ - static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; - const int *s = start; - int32_t retval = 0; - char *tmp; - - /* mrb_assert(len <= 8) */ - while (len-- && *s && (tmp = (char*)strchr(hexdigit, *s))) { - retval <<= 4; - retval |= (tmp - hexdigit) & 15; - s++; - } - *retlen = s - start; - - return retval; -} - -/* Return negative to indicate Unicode code point */ -static int32_t -read_escape(parser_state *p) -{ - int32_t c; - - switch (c = nextc(p)) { - case '\\':/* Backslash */ - return c; - - case 'n':/* newline */ - return '\n'; - - case 't':/* horizontal tab */ - return '\t'; - - case 'r':/* carriage-return */ - return '\r'; - - case 'f':/* form-feed */ - return '\f'; - - case 'v':/* vertical tab */ - return '\13'; - - case 'a':/* alarm(bell) */ - return '\007'; - - case 'e':/* escape */ - return 033; - - case '0': case '1': case '2': case '3': /* octal constant */ - case '4': case '5': case '6': case '7': - { - int buf[3]; - int i; - - buf[0] = c; - for (i=1; i<3; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (buf[i] < '0' || '7' < buf[i]) { - pushback(p, buf[i]); - break; - } - } - c = scan_oct(buf, i, &i); - } - return c; - - case 'x': /* hex constant */ - { - int buf[2]; - int i; - - for (i=0; i<2; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (!ISXDIGIT(buf[i])) { - pushback(p, buf[i]); - break; - } - } - c = scan_hex(buf, i, &i); - if (i == 0) { - yyerror(p, "Invalid escape character syntax"); - return 0; - } - } - return c; - - case 'u': /* Unicode */ - { - int buf[9]; - int i; - - /* Look for opening brace */ - i = 0; - buf[0] = nextc(p); - if (buf[0] < 0) goto eof; - if (buf[0] == '{') { - /* \u{xxxxxxxx} form */ - for (i=0; i<9; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (buf[i] == '}') { - break; - } - else if (!ISXDIGIT(buf[i])) { - yyerror(p, "Invalid escape character syntax"); - pushback(p, buf[i]); - return 0; - } - } - } - else if (ISXDIGIT(buf[0])) { - /* \uxxxx form */ - for (i=1; i<4; i++) { - buf[i] = nextc(p); - if (buf[i] < 0) goto eof; - if (!ISXDIGIT(buf[i])) { - pushback(p, buf[i]); - break; - } - } - } - else { - pushback(p, buf[0]); - } - c = scan_hex(buf, i, &i); - if (i == 0) { - yyerror(p, "Invalid escape character syntax"); - return 0; - } - if (c < 0 || c > 0x10FFFF || (c & 0xFFFFF800) == 0xD800) { - yyerror(p, "Invalid Unicode code point"); - return 0; - } - } - return -c; - - case 'b':/* backspace */ - return '\010'; - - case 's':/* space */ - return ' '; - - case 'M': - if ((c = nextc(p)) != '-') { - yyerror(p, "Invalid escape character syntax"); - pushback(p, c); - return '\0'; - } - if ((c = nextc(p)) == '\\') { - return read_escape(p) | 0x80; - } - else if (c < 0) goto eof; - else { - return ((c & 0xff) | 0x80); - } - - case 'C': - if ((c = nextc(p)) != '-') { - yyerror(p, "Invalid escape character syntax"); - pushback(p, c); - return '\0'; - } - case 'c': - if ((c = nextc(p))== '\\') { - c = read_escape(p); - } - else if (c == '?') - return 0177; - else if (c < 0) goto eof; - return c & 0x9f; - - eof: - case -1: - case -2: /* end of a file */ - yyerror(p, "Invalid escape character syntax"); - return '\0'; - - default: - return c; - } -} - -static int -parse_string(parser_state *p) -{ - int c; - string_type type = (string_type)(intptr_t)p->lex_strterm->car; - int nest_level = (intptr_t)p->lex_strterm->cdr->car; - int beg = (intptr_t)p->lex_strterm->cdr->cdr->car; - int end = (intptr_t)p->lex_strterm->cdr->cdr->cdr; - parser_heredoc_info *hinf = (type & STR_FUNC_HEREDOC) ? parsing_heredoc_inf(p) : NULL; - - newtok(p); - while ((c = nextc(p)) != end || nest_level != 0) { - if (hinf && (c == '\n' || c < 0)) { - int line_head; - tokadd(p, '\n'); - tokfix(p); - p->lineno++; - p->column = 0; - line_head = hinf->line_head; - hinf->line_head = TRUE; - if (line_head) { - /* check whether end of heredoc */ - const char *s = tok(p); - int len = toklen(p); - if (hinf->allow_indent) { - while (ISSPACE(*s) && len > 0) { - ++s; - --len; - } - } - if ((len-1 == hinf->term_len) && (strncmp(s, hinf->term, len-1) == 0)) { - return tHEREDOC_END; - } - } - if (c < 0) { - char buf[256]; - snprintf(buf, sizeof(buf), "can't find heredoc delimiter \"%s\" anywhere before EOF", hinf->term); - yyerror(p, buf); - return 0; - } - yylval.nd = new_str(p, tok(p), toklen(p)); - return tHD_STRING_MID; - } - if (c < 0) { - yyerror(p, "unterminated string meets end of file"); - return 0; - } - else if (c == beg) { - nest_level++; - p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level; - } - else if (c == end) { - nest_level--; - p->lex_strterm->cdr->car = (node*)(intptr_t)nest_level; - } - else if (c == '\\') { - c = nextc(p); - if (type & STR_FUNC_EXPAND) { - if (c == end || c == beg) { - tokadd(p, c); - } - else if (c == '\n') { - p->lineno++; - p->column = 0; - if (type & STR_FUNC_ARRAY) { - tokadd(p, '\n'); - } - } - else if (type & STR_FUNC_REGEXP) { - tokadd(p, '\\'); - tokadd(p, c); - } - else { - pushback(p, c); - tokadd(p, read_escape(p)); - if (hinf) - hinf->line_head = FALSE; - } - } - else { - if (c != beg && c != end) { - if (c == '\n') { - p->lineno++; - p->column = 0; - } - if (!(c == '\\' || ((type & STR_FUNC_ARRAY) && ISSPACE(c)))) { - tokadd(p, '\\'); - } - } - tokadd(p, c); - } - continue; - } - else if ((c == '#') && (type & STR_FUNC_EXPAND)) { - c = nextc(p); - if (c == '{') { - tokfix(p); - p->lstate = EXPR_BEG; - p->cmd_start = TRUE; - yylval.nd = new_str(p, tok(p), toklen(p)); - if (hinf) { - hinf->line_head = FALSE; - return tHD_STRING_PART; - } - return tSTRING_PART; - } - tokadd(p, '#'); - pushback(p, c); - continue; - } - if ((type & STR_FUNC_ARRAY) && ISSPACE(c)) { - if (toklen(p) == 0) { - do { - if (c == '\n') { - p->lineno++; - p->column = 0; - heredoc_treat_nextline(p); - if (p->parsing_heredoc != NULL) { - return tHD_LITERAL_DELIM; - } - } - c = nextc(p); - } while (ISSPACE(c)); - pushback(p, c); - return tLITERAL_DELIM; - } - else { - pushback(p, c); - tokfix(p); - yylval.nd = new_str(p, tok(p), toklen(p)); - return tSTRING_MID; - } - } - tokadd(p, c); - } - - tokfix(p); - p->lstate = EXPR_END; - end_strterm(p); - - if (type & STR_FUNC_XQUOTE) { - yylval.nd = new_xstr(p, tok(p), toklen(p)); - return tXSTRING; - } - - if (type & STR_FUNC_REGEXP) { - int f = 0; - int c; - char *s = strndup(tok(p), toklen(p)); - char flags[3]; - char *flag = flags; - char *dup; - - newtok(p); - while (c = nextc(p), c >= 0 && ISALPHA(c)) { - switch (c) { - case 'i': f |= 1; break; - case 'x': f |= 2; break; - case 'm': f |= 4; break; - default: tokadd(p, c); break; - } - } - pushback(p, c); - if (toklen(p)) { - char msg[128]; - tokfix(p); - snprintf(msg, sizeof(msg), "unknown regexp option%s - %s", - toklen(p) > 1 ? "s" : "", tok(p)); - yyerror(p, msg); - } - if (f != 0) { - if (f & 1) *flag++ = 'i'; - if (f & 2) *flag++ = 'x'; - if (f & 4) *flag++ = 'm'; - dup = strndup(flags, (size_t)(flag - flags)); - } - else { - dup = NULL; - } - yylval.nd = new_regx(p, s, dup); - - return tREGEXP; - } - - yylval.nd = new_str(p, tok(p), toklen(p)); - return tSTRING; -} - - -static int -heredoc_identifier(parser_state *p) -{ - int c; - int type = str_heredoc; - mrb_bool indent = FALSE; - mrb_bool quote = FALSE; - node *newnode; - parser_heredoc_info *info; - - c = nextc(p); - if (ISSPACE(c) || c == '=') { - pushback(p, c); - return 0; - } - if (c == '-') { - indent = TRUE; - c = nextc(p); - } - if (c == '\'' || c == '"') { - int term = c; - if (c == '\'') - quote = TRUE; - newtok(p); - while ((c = nextc(p)) >= 0 && c != term) { - if (c == '\n') { - c = -1; - break; - } - tokadd(p, c); - } - if (c < 0) { - yyerror(p, "unterminated here document identifier"); - return 0; - } - } - else { - if (c < 0) { - return 0; /* missing here document identifier */ - } - if (! identchar(c)) { - pushback(p, c); - if (indent) pushback(p, '-'); - return 0; - } - newtok(p); - do { - tokadd(p, c); - } while ((c = nextc(p)) >= 0 && identchar(c)); - pushback(p, c); - } - tokfix(p); - newnode = new_heredoc(p); - info = (parser_heredoc_info*)newnode->cdr; - info->term = strndup(tok(p), toklen(p)); - info->term_len = toklen(p); - if (! quote) - type |= STR_FUNC_EXPAND; - info->type = (string_type)type; - info->allow_indent = indent; - info->line_head = TRUE; - info->doc = NULL; - p->heredocs_from_nextline = push(p->heredocs_from_nextline, newnode); - p->lstate = EXPR_END; - - yylval.nd = newnode; - return tHEREDOC_BEG; -} - -static int -arg_ambiguous(parser_state *p) -{ - yywarning(p, "ambiguous first argument; put parentheses or even spaces"); - return 1; -} - -#include "lex.def" - -static int -parser_yylex(parser_state *p) -{ - int32_t c; - int space_seen = 0; - int cmd_state; - enum mrb_lex_state_enum last_state; - int token_column; - - if (p->lex_strterm) { - if (is_strterm_type(p, STR_FUNC_HEREDOC)) { - if (p->parsing_heredoc != NULL) - return parse_string(p); - } - else - return parse_string(p); - } - cmd_state = p->cmd_start; - p->cmd_start = FALSE; - retry: - last_state = p->lstate; - switch (c = nextc(p)) { - case '\0': /* NUL */ - case '\004': /* ^D */ - case '\032': /* ^Z */ - return 0; - case -1: /* end of script. */ - if (p->heredocs_from_nextline) - goto maybe_heredoc; - return 0; - - /* white spaces */ - case ' ': case '\t': case '\f': case '\r': - case '\13': /* '\v' */ - space_seen = 1; - goto retry; - - case '#': /* it's a comment */ - skip(p, '\n'); - /* fall through */ - case -2: /* end of a file */ - case '\n': - maybe_heredoc: - heredoc_treat_nextline(p); - switch (p->lstate) { - case EXPR_BEG: - case EXPR_FNAME: - case EXPR_DOT: - case EXPR_CLASS: - case EXPR_VALUE: - p->lineno++; - p->column = 0; - if (p->parsing_heredoc != NULL) { - return parse_string(p); - } - goto retry; - default: - break; - } - if (p->parsing_heredoc != NULL) { - return '\n'; - } - while ((c = nextc(p))) { - switch (c) { - case ' ': case '\t': case '\f': case '\r': - case '\13': /* '\v' */ - space_seen = 1; - break; - case '.': - if ((c = nextc(p)) != '.') { - pushback(p, c); - pushback(p, '.'); - goto retry; - } - case -1: /* EOF */ - case -2: /* end of a file */ - goto normal_newline; - default: - pushback(p, c); - goto normal_newline; - } - } - normal_newline: - p->cmd_start = TRUE; - p->lstate = EXPR_BEG; - return '\n'; - - case '*': - if ((c = nextc(p)) == '*') { - if ((c = nextc(p)) == '=') { - yylval.id = intern("**",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - c = tPOW; - } - else { - if (c == '=') { - yylval.id = intern_c('*'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - if (IS_SPCARG(c)) { - yywarning(p, "`*' interpreted as argument prefix"); - c = tSTAR; - } - else if (IS_BEG()) { - c = tSTAR; - } - else { - c = '*'; - } - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return c; - - case '!': - c = nextc(p); - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if (c == '@') { - return '!'; - } - } - else { - p->lstate = EXPR_BEG; - } - if (c == '=') { - return tNEQ; - } - if (c == '~') { - return tNMATCH; - } - pushback(p, c); - return '!'; - - case '=': - if (p->column == 1) { - static const char begin[] = "begin"; - static const char end[] = "\n=end"; - if (peeks(p, begin)) { - c = peekc_n(p, sizeof(begin)-1); - if (c < 0 || ISSPACE(c)) { - do { - if (!skips(p, end)) { - yyerror(p, "embedded document meets end of file"); - return 0; - } - c = nextc(p); - } while (!(c < 0 || ISSPACE(c))); - if (c != '\n') skip(p, '\n'); - p->lineno++; - p->column = 0; - goto retry; - } - } - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - if ((c = nextc(p)) == '=') { - if ((c = nextc(p)) == '=') { - return tEQQ; - } - pushback(p, c); - return tEQ; - } - if (c == '~') { - return tMATCH; - } - else if (c == '>') { - return tASSOC; - } - pushback(p, c); - return '='; - - case '<': - c = nextc(p); - if (c == '<' && - p->lstate != EXPR_DOT && - p->lstate != EXPR_CLASS && - !IS_END() && - (!IS_ARG() || space_seen)) { - int token = heredoc_identifier(p); - if (token) - return token; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - if (p->lstate == EXPR_CLASS) { - p->cmd_start = TRUE; - } - } - if (c == '=') { - if ((c = nextc(p)) == '>') { - return tCMP; - } - pushback(p, c); - return tLEQ; - } - if (c == '<') { - if ((c = nextc(p)) == '=') { - yylval.id = intern("<<",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tLSHFT; - } - pushback(p, c); - return '<'; - - case '>': - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - if ((c = nextc(p)) == '=') { - return tGEQ; - } - if (c == '>') { - if ((c = nextc(p)) == '=') { - yylval.id = intern(">>",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tRSHFT; - } - pushback(p, c); - return '>'; - - case '"': - p->lex_strterm = new_strterm(p, str_dquote, '"', 0); - return tSTRING_BEG; - - case '\'': - p->lex_strterm = new_strterm(p, str_squote, '\'', 0); - return parse_string(p); - - case '`': - if (p->lstate == EXPR_FNAME) { - p->lstate = EXPR_ENDFN; - return '`'; - } - if (p->lstate == EXPR_DOT) { - if (cmd_state) - p->lstate = EXPR_CMDARG; - else - p->lstate = EXPR_ARG; - return '`'; - } - p->lex_strterm = new_strterm(p, str_xquote, '`', 0); - return tXSTRING_BEG; - - case '?': - if (IS_END()) { - p->lstate = EXPR_VALUE; - return '?'; - } - c = nextc(p); - if (c < 0) { - yyerror(p, "incomplete character syntax"); - return 0; - } - if (ISSPACE(c)) { - if (!IS_ARG()) { - int c2; - switch (c) { - case ' ': - c2 = 's'; - break; - case '\n': - c2 = 'n'; - break; - case '\t': - c2 = 't'; - break; - case '\v': - c2 = 'v'; - break; - case '\r': - c2 = 'r'; - break; - case '\f': - c2 = 'f'; - break; - default: - c2 = 0; - break; - } - if (c2) { - char buf[256]; - snprintf(buf, sizeof(buf), "invalid character syntax; use ?\\%c", c2); - yyerror(p, buf); - } - } - ternary: - pushback(p, c); - p->lstate = EXPR_VALUE; - return '?'; - } - newtok(p); - /* need support UTF-8 if configured */ - if ((isalnum(c) || c == '_')) { - int c2 = nextc(p); - pushback(p, c2); - if ((isalnum(c2) || c2 == '_')) { - goto ternary; - } - } - if (c == '\\') { - c = read_escape(p); - tokadd(p, c); - } - else { - tokadd(p, c); - } - tokfix(p); - yylval.nd = new_str(p, tok(p), toklen(p)); - p->lstate = EXPR_END; - return tCHAR; - - case '&': - if ((c = nextc(p)) == '&') { - p->lstate = EXPR_BEG; - if ((c = nextc(p)) == '=') { - yylval.id = intern("&&",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tANDOP; - } - else if (c == '=') { - yylval.id = intern_c('&'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - if (IS_SPCARG(c)) { - yywarning(p, "`&' interpreted as argument prefix"); - c = tAMPER; - } - else if (IS_BEG()) { - c = tAMPER; - } - else { - c = '&'; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return c; - - case '|': - if ((c = nextc(p)) == '|') { - p->lstate = EXPR_BEG; - if ((c = nextc(p)) == '=') { - yylval.id = intern("||",2); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - return tOROP; - } - if (c == '=') { - yylval.id = intern_c('|'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - pushback(p, c); - return '|'; - - case '+': - c = nextc(p); - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if (c == '@') { - return tUPLUS; - } - pushback(p, c); - return '+'; - } - if (c == '=') { - yylval.id = intern_c('+'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { - p->lstate = EXPR_BEG; - pushback(p, c); - if (c >= 0 && ISDIGIT(c)) { - c = '+'; - goto start_num; - } - return tUPLUS; - } - p->lstate = EXPR_BEG; - pushback(p, c); - return '+'; - - case '-': - c = nextc(p); - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if (c == '@') { - return tUMINUS; - } - pushback(p, c); - return '-'; - } - if (c == '=') { - yylval.id = intern_c('-'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (c == '>') { - p->lstate = EXPR_ENDFN; - return tLAMBDA; - } - if (IS_BEG() || (IS_SPCARG(c) && arg_ambiguous(p))) { - p->lstate = EXPR_BEG; - pushback(p, c); - if (c >= 0 && ISDIGIT(c)) { - return tUMINUS_NUM; - } - return tUMINUS; - } - p->lstate = EXPR_BEG; - pushback(p, c); - return '-'; - - case '.': - p->lstate = EXPR_BEG; - if ((c = nextc(p)) == '.') { - if ((c = nextc(p)) == '.') { - return tDOT3; - } - pushback(p, c); - return tDOT2; - } - pushback(p, c); - if (c >= 0 && ISDIGIT(c)) { - yyerror(p, "no .<digit> floating literal anymore; put 0 before dot"); - } - p->lstate = EXPR_DOT; - return '.'; - - start_num: - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - { - int is_float, seen_point, seen_e, nondigit; - - is_float = seen_point = seen_e = nondigit = 0; - p->lstate = EXPR_END; - newtok(p); - if (c == '-' || c == '+') { - tokadd(p, c); - c = nextc(p); - } - if (c == '0') { -#define no_digits() do {yyerror(p,"numeric literal without digits"); return 0;} while (0) - int start = toklen(p); - c = nextc(p); - if (c == 'x' || c == 'X') { - /* hexadecimal */ - c = nextc(p); - if (c >= 0 && ISXDIGIT(c)) { - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (!ISXDIGIT(c)) break; - nondigit = 0; - tokadd(p, tolower(c)); - } while ((c = nextc(p)) >= 0); - } - pushback(p, c); - tokfix(p); - if (toklen(p) == start) { - no_digits(); - } - else if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 16); - return tINTEGER; - } - if (c == 'b' || c == 'B') { - /* binary */ - c = nextc(p); - if (c == '0' || c == '1') { - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (c != '0' && c != '1') break; - nondigit = 0; - tokadd(p, c); - } while ((c = nextc(p)) >= 0); - } - pushback(p, c); - tokfix(p); - if (toklen(p) == start) { - no_digits(); - } - else if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 2); - return tINTEGER; - } - if (c == 'd' || c == 'D') { - /* decimal */ - c = nextc(p); - if (c >= 0 && ISDIGIT(c)) { - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (!ISDIGIT(c)) break; - nondigit = 0; - tokadd(p, c); - } while ((c = nextc(p)) >= 0); - } - pushback(p, c); - tokfix(p); - if (toklen(p) == start) { - no_digits(); - } - else if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 10); - return tINTEGER; - } - if (c == '_') { - /* 0_0 */ - goto octal_number; - } - if (c == 'o' || c == 'O') { - /* prefixed octal */ - c = nextc(p); - if (c < 0 || c == '_' || !ISDIGIT(c)) { - no_digits(); - } - } - if (c >= '0' && c <= '7') { - /* octal */ - octal_number: - do { - if (c == '_') { - if (nondigit) break; - nondigit = c; - continue; - } - if (c < '0' || c > '9') break; - if (c > '7') goto invalid_octal; - nondigit = 0; - tokadd(p, c); - } while ((c = nextc(p)) >= 0); - - if (toklen(p) > start) { - pushback(p, c); - tokfix(p); - if (nondigit) goto trailing_uc; - yylval.nd = new_int(p, tok(p), 8); - return tINTEGER; - } - if (nondigit) { - pushback(p, c); - goto trailing_uc; - } - } - if (c > '7' && c <= '9') { - invalid_octal: - yyerror(p, "Invalid octal digit"); - } - else if (c == '.' || c == 'e' || c == 'E') { - tokadd(p, '0'); - } - else { - pushback(p, c); - yylval.nd = new_int(p, "0", 10); - return tINTEGER; - } - } - - for (;;) { - switch (c) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - nondigit = 0; - tokadd(p, c); - break; - - case '.': - if (nondigit) goto trailing_uc; - if (seen_point || seen_e) { - goto decode_num; - } - else { - int c0 = nextc(p); - if (c0 < 0 || !ISDIGIT(c0)) { - pushback(p, c0); - goto decode_num; - } - c = c0; - } - tokadd(p, '.'); - tokadd(p, c); - is_float++; - seen_point++; - nondigit = 0; - break; - - case 'e': - case 'E': - if (nondigit) { - pushback(p, c); - c = nondigit; - goto decode_num; - } - if (seen_e) { - goto decode_num; - } - tokadd(p, c); - seen_e++; - is_float++; - nondigit = c; - c = nextc(p); - if (c != '-' && c != '+') continue; - tokadd(p, c); - nondigit = c; - break; - - case '_': /* `_' in number just ignored */ - if (nondigit) goto decode_num; - nondigit = c; - break; - - default: - goto decode_num; - } - c = nextc(p); - } - - decode_num: - pushback(p, c); - if (nondigit) { - trailing_uc: - yyerror_i(p, "trailing `%c' in number", nondigit); - } - tokfix(p); - if (is_float) { - double d; - char *endp; - - errno = 0; - d = strtod(tok(p), &endp); - if (d == 0 && endp == tok(p)) { - yywarning_s(p, "corrupted float value %s", tok(p)); - } - else if (errno == ERANGE) { - yywarning_s(p, "float %s out of range", tok(p)); - errno = 0; - } - yylval.nd = new_float(p, tok(p)); - return tFLOAT; - } - yylval.nd = new_int(p, tok(p), 10); - return tINTEGER; - } - - case ')': - case ']': - p->paren_nest--; - case '}': - COND_LEXPOP(); - CMDARG_LEXPOP(); - if (c == ')') - p->lstate = EXPR_ENDFN; - else - p->lstate = EXPR_ENDARG; - return c; - - case ':': - c = nextc(p); - if (c == ':') { - if (IS_BEG() || p->lstate == EXPR_CLASS || IS_SPCARG(-1)) { - p->lstate = EXPR_BEG; - return tCOLON3; - } - p->lstate = EXPR_DOT; - return tCOLON2; - } - if (IS_END() || ISSPACE(c)) { - pushback(p, c); - p->lstate = EXPR_BEG; - return ':'; - } - pushback(p, c); - p->lstate = EXPR_FNAME; - return tSYMBEG; - - case '/': - if (IS_BEG()) { - p->lex_strterm = new_strterm(p, str_regexp, '/', 0); - return tREGEXP_BEG; - } - if ((c = nextc(p)) == '=') { - yylval.id = intern_c('/'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - pushback(p, c); - if (IS_SPCARG(c)) { - p->lex_strterm = new_strterm(p, str_regexp, '/', 0); - return tREGEXP_BEG; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return '/'; - - case '^': - if ((c = nextc(p)) == '=') { - yylval.id = intern_c('^'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - pushback(p, c); - return '^'; - - case ';': - p->lstate = EXPR_BEG; - return ';'; - - case ',': - p->lstate = EXPR_BEG; - return ','; - - case '~': - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - if ((c = nextc(p)) != '@') { - pushback(p, c); - } - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - return '~'; - - case '(': - if (IS_BEG()) { - c = tLPAREN; - } - else if (IS_SPCARG(-1)) { - c = tLPAREN_ARG; - } - p->paren_nest++; - COND_PUSH(0); - CMDARG_PUSH(0); - p->lstate = EXPR_BEG; - return c; - - case '[': - p->paren_nest++; - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - if ((c = nextc(p)) == ']') { - if ((c = nextc(p)) == '=') { - return tASET; - } - pushback(p, c); - return tAREF; - } - pushback(p, c); - return '['; - } - else if (IS_BEG()) { - c = tLBRACK; - } - else if (IS_ARG() && space_seen) { - c = tLBRACK; - } - p->lstate = EXPR_BEG; - COND_PUSH(0); - CMDARG_PUSH(0); - return c; - - case '{': - if (p->lpar_beg && p->lpar_beg == p->paren_nest) { - p->lstate = EXPR_BEG; - p->lpar_beg = 0; - p->paren_nest--; - COND_PUSH(0); - CMDARG_PUSH(0); - return tLAMBEG; - } - if (IS_ARG() || p->lstate == EXPR_END || p->lstate == EXPR_ENDFN) - c = '{'; /* block (primary) */ - else if (p->lstate == EXPR_ENDARG) - c = tLBRACE_ARG; /* block (expr) */ - else - c = tLBRACE; /* hash */ - COND_PUSH(0); - CMDARG_PUSH(0); - p->lstate = EXPR_BEG; - return c; - - case '\\': - c = nextc(p); - if (c == '\n') { - p->lineno++; - p->column = 0; - space_seen = 1; - goto retry; /* skip \\n */ - } - pushback(p, c); - return '\\'; - - case '%': - if (IS_BEG()) { - int term; - int paren; - - c = nextc(p); - quotation: - if (c < 0 || !ISALNUM(c)) { - term = c; - c = 'Q'; - } - else { - term = nextc(p); - if (isalnum(term)) { - yyerror(p, "unknown type of %string"); - return 0; - } - } - if (c < 0 || term < 0) { - yyerror(p, "unterminated quoted string meets end of file"); - return 0; - } - paren = term; - if (term == '(') term = ')'; - else if (term == '[') term = ']'; - else if (term == '{') term = '}'; - else if (term == '<') term = '>'; - else paren = 0; - - switch (c) { - case 'Q': - p->lex_strterm = new_strterm(p, str_dquote, term, paren); - return tSTRING_BEG; - - case 'q': - p->lex_strterm = new_strterm(p, str_squote, term, paren); - return parse_string(p); - - case 'W': - p->lex_strterm = new_strterm(p, str_dword, term, paren); - return tWORDS_BEG; - - case 'w': - p->lex_strterm = new_strterm(p, str_sword, term, paren); - return tWORDS_BEG; - - case 'x': - p->lex_strterm = new_strterm(p, str_xquote, term, paren); - return tXSTRING_BEG; - - case 'r': - p->lex_strterm = new_strterm(p, str_regexp, term, paren); - return tREGEXP_BEG; - - case 's': - p->lex_strterm = new_strterm(p, str_ssym, term, paren); - return tSYMBEG; - - case 'I': - p->lex_strterm = new_strterm(p, str_dsymbols, term, paren); - return tSYMBOLS_BEG; - - case 'i': - p->lex_strterm = new_strterm(p, str_ssymbols, term, paren); - return tSYMBOLS_BEG; - - default: - yyerror(p, "unknown type of %string"); - return 0; - } - } - if ((c = nextc(p)) == '=') { - yylval.id = intern_c('%'); - p->lstate = EXPR_BEG; - return tOP_ASGN; - } - if (IS_SPCARG(c)) { - goto quotation; - } - if (p->lstate == EXPR_FNAME || p->lstate == EXPR_DOT) { - p->lstate = EXPR_ARG; - } - else { - p->lstate = EXPR_BEG; - } - pushback(p, c); - return '%'; - - case '$': - p->lstate = EXPR_END; - token_column = newtok(p); - c = nextc(p); - if (c < 0) { - yyerror(p, "incomplete global variable syntax"); - return 0; - } - switch (c) { - case '_': /* $_: last read line string */ - c = nextc(p); - if (c >= 0 && identchar(c)) { /* if there is more after _ it is a variable */ - tokadd(p, '$'); - tokadd(p, c); - break; - } - pushback(p, c); - c = '_'; - /* fall through */ - case '~': /* $~: match-data */ - case '*': /* $*: argv */ - case '$': /* $$: pid */ - case '?': /* $?: last status */ - case '!': /* $!: error string */ - case '@': /* $@: error position */ - case '/': /* $/: input record separator */ - case '\\': /* $\: output record separator */ - case ';': /* $;: field separator */ - case ',': /* $,: output field separator */ - case '.': /* $.: last read line number */ - case '=': /* $=: ignorecase */ - case ':': /* $:: load path */ - case '<': /* $<: reading filename */ - case '>': /* $>: default output handle */ - case '\"': /* $": already loaded files */ - tokadd(p, '$'); - tokadd(p, c); - tokfix(p); - yylval.id = intern_cstr(tok(p)); - return tGVAR; - - case '-': - tokadd(p, '$'); - tokadd(p, c); - c = nextc(p); - pushback(p, c); - gvar: - tokfix(p); - yylval.id = intern_cstr(tok(p)); - return tGVAR; - - case '&': /* $&: last match */ - case '`': /* $`: string before last match */ - case '\'': /* $': string after last match */ - case '+': /* $+: string matches last pattern */ - if (last_state == EXPR_FNAME) { - tokadd(p, '$'); - tokadd(p, c); - goto gvar; - } - yylval.nd = new_back_ref(p, c); - return tBACK_REF; - - case '1': case '2': case '3': - case '4': case '5': case '6': - case '7': case '8': case '9': - do { - tokadd(p, c); - c = nextc(p); - } while (c >= 0 && isdigit(c)); - pushback(p, c); - if (last_state == EXPR_FNAME) goto gvar; - tokfix(p); - yylval.nd = new_nth_ref(p, atoi(tok(p))); - return tNTH_REF; - - default: - if (!identchar(c)) { - pushback(p, c); - return '$'; - } - case '0': - tokadd(p, '$'); - } - break; - - case '@': - c = nextc(p); - token_column = newtok(p); - tokadd(p, '@'); - if (c == '@') { - tokadd(p, '@'); - c = nextc(p); - } - if (c < 0) { - if (p->bidx == 1) { - yyerror(p, "incomplete instance variable syntax"); - } - else { - yyerror(p, "incomplete class variable syntax"); - } - return 0; - } - else if (isdigit(c)) { - if (p->bidx == 1) { - yyerror_i(p, "`@%c' is not allowed as an instance variable name", c); - } - else { - yyerror_i(p, "`@@%c' is not allowed as a class variable name", c); - } - return 0; - } - if (!identchar(c)) { - pushback(p, c); - return '@'; - } - break; - - case '_': - token_column = newtok(p); - break; - - default: - if (!identchar(c)) { - yyerror_i(p, "Invalid char `\\x%02X' in expression", c); - goto retry; - } - - token_column = newtok(p); - break; - } - - do { - tokadd(p, c); - c = nextc(p); - if (c < 0) break; - } while (identchar(c)); - if (token_column == 0 && toklen(p) == 7 && (c < 0 || c == '\n') && - strncmp(tok(p), "__END__", toklen(p)) == 0) - return -1; - - switch (tok(p)[0]) { - case '@': case '$': - pushback(p, c); - break; - default: - if ((c == '!' || c == '?') && !peek(p, '=')) { - tokadd(p, c); - } - else { - pushback(p, c); - } - } - tokfix(p); - { - int result = 0; - - switch (tok(p)[0]) { - case '$': - p->lstate = EXPR_END; - result = tGVAR; - break; - case '@': - p->lstate = EXPR_END; - if (tok(p)[1] == '@') - result = tCVAR; - else - result = tIVAR; - break; - - default: - if (toklast(p) == '!' || toklast(p) == '?') { - result = tFID; - } - else { - if (p->lstate == EXPR_FNAME) { - if ((c = nextc(p)) == '=' && !peek(p, '~') && !peek(p, '>') && - (!peek(p, '=') || (peek_n(p, '>', 1)))) { - result = tIDENTIFIER; - tokadd(p, c); - tokfix(p); - } - else { - pushback(p, c); - } - } - if (result == 0 && ISUPPER(tok(p)[0])) { - result = tCONSTANT; - } - else { - result = tIDENTIFIER; - } - } - - if (IS_LABEL_POSSIBLE()) { - if (IS_LABEL_SUFFIX(0)) { - p->lstate = EXPR_BEG; - nextc(p); - tokfix(p); - yylval.id = intern_cstr(tok(p)); - return tLABEL; - } - } - if (p->lstate != EXPR_DOT) { - const struct kwtable *kw; - - /* See if it is a reserved word. */ - kw = mrb_reserved_word(tok(p), toklen(p)); - if (kw) { - enum mrb_lex_state_enum state = p->lstate; - p->lstate = kw->state; - if (state == EXPR_FNAME) { - yylval.id = intern_cstr(kw->name); - return kw->id[0]; - } - if (p->lstate == EXPR_BEG) { - p->cmd_start = TRUE; - } - if (kw->id[0] == keyword_do) { - if (p->lpar_beg && p->lpar_beg == p->paren_nest) { - p->lpar_beg = 0; - p->paren_nest--; - return keyword_do_LAMBDA; - } - if (COND_P()) return keyword_do_cond; - if (CMDARG_P() && state != EXPR_CMDARG) - return keyword_do_block; - if (state == EXPR_ENDARG || state == EXPR_BEG) - return keyword_do_block; - return keyword_do; - } - if (state == EXPR_BEG || state == EXPR_VALUE) - return kw->id[0]; - else { - if (kw->id[0] != kw->id[1]) - p->lstate = EXPR_BEG; - return kw->id[1]; - } - } - } - - if (IS_BEG() || p->lstate == EXPR_DOT || IS_ARG()) { - if (cmd_state) { - p->lstate = EXPR_CMDARG; - } - else { - p->lstate = EXPR_ARG; - } - } - else if (p->lstate == EXPR_FNAME) { - p->lstate = EXPR_ENDFN; - } - else { - p->lstate = EXPR_END; - } - } - { - mrb_sym ident = intern_cstr(tok(p)); - - yylval.id = ident; -#if 0 - if (last_state != EXPR_DOT && islower(tok(p)[0]) && lvar_defined(ident)) { - p->lstate = EXPR_END; - } -#endif - } - return result; - } -} - -static int -yylex(void *lval, parser_state *p) -{ - int t; - - p->ylval = lval; - t = parser_yylex(p); - - return t; -} - -static void -parser_init_cxt(parser_state *p, mrbc_context *cxt) -{ - if (!cxt) return; - if (cxt->filename) mrb_parser_set_filename(p, cxt->filename); - if (cxt->lineno) p->lineno = cxt->lineno; - if (cxt->syms) { - int i; - - p->locals = cons(0,0); - for (i=0; i<cxt->slen; i++) { - local_add_f(p, cxt->syms[i]); - } - } - p->capture_errors = cxt->capture_errors; - if (cxt->partial_hook) { - p->cxt = cxt; - } -} - -static void -parser_update_cxt(parser_state *p, mrbc_context *cxt) -{ - node *n, *n0; - int i = 0; - - if (!cxt) return; - if ((int)(intptr_t)p->tree->car != NODE_SCOPE) return; - n0 = n = p->tree->cdr->car; - while (n) { - i++; - n = n->cdr; - } - cxt->syms = (mrb_sym *)mrb_realloc(p->mrb, cxt->syms, i*sizeof(mrb_sym)); - cxt->slen = i; - for (i=0, n=n0; n; i++,n=n->cdr) { - cxt->syms[i] = sym(n->car); - } -} - -void mrb_codedump_all(mrb_state*, struct RProc*); -void mrb_parser_dump(mrb_state *mrb, node *tree, int offset); - -void -mrb_parser_parse(parser_state *p, mrbc_context *c) -{ - struct mrb_jmpbuf buf; - p->jmp = &buf; - - MRB_TRY(p->jmp) { - - p->cmd_start = TRUE; - p->in_def = p->in_single = 0; - p->nerr = p->nwarn = 0; - p->lex_strterm = NULL; - - parser_init_cxt(p, c); - yyparse(p); - if (!p->tree) { - p->tree = new_nil(p); - } - parser_update_cxt(p, c); - if (c && c->dump_result) { - mrb_parser_dump(p->mrb, p->tree, 0); - } - - } - MRB_CATCH(p->jmp) { - yyerror(p, "memory allocation error"); - p->nerr++; - p->tree = 0; - return; - } - MRB_END_EXC(p->jmp); -} - -parser_state* -mrb_parser_new(mrb_state *mrb) -{ - mrb_pool *pool; - parser_state *p; - static const parser_state parser_state_zero = { 0 }; - - pool = mrb_pool_open(mrb); - if (!pool) return 0; - p = (parser_state *)mrb_pool_alloc(pool, sizeof(parser_state)); - if (!p) return 0; - - *p = parser_state_zero; - p->mrb = mrb; - p->pool = pool; - - p->s = p->send = NULL; -#ifdef ENABLE_STDIO - p->f = NULL; -#endif - - p->cmd_start = TRUE; - p->in_def = p->in_single = 0; - - p->capture_errors = FALSE; - p->lineno = 1; - p->column = 0; -#if defined(PARSER_TEST) || defined(PARSER_DEBUG) - yydebug = 1; -#endif - - p->lex_strterm = NULL; - p->all_heredocs = p->parsing_heredoc = NULL; - p->lex_strterm_before_heredoc = NULL; - - p->current_filename_index = -1; - p->filename_table = NULL; - p->filename_table_length = 0; - - return p; -} - -void -mrb_parser_free(parser_state *p) { - mrb_pool_close(p->pool); -} - -mrbc_context* -mrbc_context_new(mrb_state *mrb) -{ - mrbc_context *c; - - c = (mrbc_context *)mrb_calloc(mrb, 1, sizeof(mrbc_context)); - return c; -} - -void -mrbc_context_free(mrb_state *mrb, mrbc_context *cxt) -{ - mrb_free(mrb, cxt->syms); - mrb_free(mrb, cxt); -} - -const char* -mrbc_filename(mrb_state *mrb, mrbc_context *c, const char *s) -{ - if (s) { - int len = strlen(s); - char *p = (char *)mrb_alloca(mrb, len + 1); - - memcpy(p, s, len + 1); - c->filename = p; - } - return c->filename; -} - -void -mrbc_partial_hook(mrb_state *mrb, mrbc_context *c, int (*func)(struct mrb_parser_state*), void *data) -{ - c->partial_hook = func; - c->partial_data = data; -} - -void -mrb_parser_set_filename(struct mrb_parser_state *p, const char *f) -{ - mrb_sym sym; - size_t i; - mrb_sym* new_table; - - sym = mrb_intern_cstr(p->mrb, f); - p->filename = mrb_sym2name_len(p->mrb, sym, NULL); - p->lineno = (p->filename_table_length > 0)? 0 : 1; - - for (i = 0; i < p->filename_table_length; ++i) { - if (p->filename_table[i] == sym) { - p->current_filename_index = i; - return; - } - } - - p->current_filename_index = p->filename_table_length++; - - new_table = (mrb_sym*)parser_palloc(p, sizeof(mrb_sym) * p->filename_table_length); - if (p->filename_table) { - memmove(new_table, p->filename_table, sizeof(mrb_sym) * p->filename_table_length); - } - p->filename_table = new_table; - p->filename_table[p->filename_table_length - 1] = sym; -} - -char const* -mrb_parser_get_filename(struct mrb_parser_state* p, uint16_t idx) { - if (idx >= p->filename_table_length) { return NULL; } - else { - return mrb_sym2name_len(p->mrb, p->filename_table[idx], NULL); - } -} - -#ifdef ENABLE_STDIO -parser_state* -mrb_parse_file(mrb_state *mrb, FILE *f, mrbc_context *c) -{ - parser_state *p; - - p = mrb_parser_new(mrb); - if (!p) return 0; - p->s = p->send = NULL; - p->f = f; - - mrb_parser_parse(p, c); - return p; -} -#endif - -parser_state* -mrb_parse_nstring(mrb_state *mrb, const char *s, int len, mrbc_context *c) -{ - parser_state *p; - - p = mrb_parser_new(mrb); - if (!p) return 0; - p->s = s; - p->send = s + len; - - mrb_parser_parse(p, c); - return p; -} - -parser_state* -mrb_parse_string(mrb_state *mrb, const char *s, mrbc_context *c) -{ - return mrb_parse_nstring(mrb, s, strlen(s), c); -} - -static mrb_value -load_exec(mrb_state *mrb, parser_state *p, mrbc_context *c) -{ - struct RClass *target = mrb->object_class; - struct RProc *proc; - mrb_value v; - unsigned int keep = 0; - - if (!p) { - return mrb_undef_value(); - } - if (!p->tree || p->nerr) { - if (p->capture_errors) { - char buf[256]; - int n; - - n = snprintf(buf, sizeof(buf), "line %d: %s\n", - p->error_buffer[0].lineno, p->error_buffer[0].message); - mrb->exc = mrb_obj_ptr(mrb_exc_new(mrb, E_SYNTAX_ERROR, buf, n)); - mrb_parser_free(p); - return mrb_undef_value(); - } - else { - mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SYNTAX_ERROR, "syntax error")); - mrb_parser_free(p); - return mrb_undef_value(); - } - } - proc = mrb_generate_code(mrb, p); - mrb_parser_free(p); - if (proc == NULL) { - mrb->exc = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "codegen error")); - return mrb_undef_value(); - } - if (c) { - if (c->dump_result) mrb_codedump_all(mrb, proc); - if (c->no_exec) return mrb_obj_value(proc); - if (c->target_class) { - target = c->target_class; - } - keep = c->slen + 1; - } - proc->target_class = target; - if (mrb->c->ci) { - mrb->c->ci->target_class = target; - } - v = mrb_toplevel_run_keep(mrb, proc, keep); - if (mrb->exc) return mrb_nil_value(); - return v; -} - -#ifdef ENABLE_STDIO -mrb_value -mrb_load_file_cxt(mrb_state *mrb, FILE *f, mrbc_context *c) -{ - return load_exec(mrb, mrb_parse_file(mrb, f, c), c); -} - -mrb_value -mrb_load_file(mrb_state *mrb, FILE *f) -{ - return mrb_load_file_cxt(mrb, f, NULL); -} -#endif - -mrb_value -mrb_load_nstring_cxt(mrb_state *mrb, const char *s, int len, mrbc_context *c) -{ - return load_exec(mrb, mrb_parse_nstring(mrb, s, len, c), c); -} - -mrb_value -mrb_load_nstring(mrb_state *mrb, const char *s, int len) -{ - return mrb_load_nstring_cxt(mrb, s, len, NULL); -} - -mrb_value -mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *c) -{ - return mrb_load_nstring_cxt(mrb, s, strlen(s), c); -} - -mrb_value -mrb_load_string(mrb_state *mrb, const char *s) -{ - return mrb_load_string_cxt(mrb, s, NULL); -} - -#ifdef ENABLE_STDIO - -static void -dump_prefix(int offset) -{ - while (offset--) { - putc(' ', stdout); - putc(' ', stdout); - } -} - -static void -dump_recur(mrb_state *mrb, node *tree, int offset) -{ - while (tree) { - mrb_parser_dump(mrb, tree->car, offset); - tree = tree->cdr; - } -} - -#endif - -void -mrb_parser_dump(mrb_state *mrb, node *tree, int offset) -{ -#ifdef ENABLE_STDIO - int n; - - if (!tree) return; - again: - dump_prefix(offset); - n = (int)(intptr_t)tree->car; - tree = tree->cdr; - switch (n) { - case NODE_BEGIN: - printf("NODE_BEGIN:\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_RESCUE: - printf("NODE_RESCUE:\n"); - if (tree->car) { - dump_prefix(offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - } - tree = tree->cdr; - if (tree->car) { - node *n2 = tree->car; - - dump_prefix(offset+1); - printf("rescue:\n"); - while (n2) { - node *n3 = n2->car; - if (n3->car) { - dump_prefix(offset+2); - printf("handle classes:\n"); - dump_recur(mrb, n3->car, offset+3); - } - if (n3->cdr->car) { - dump_prefix(offset+2); - printf("exc_var:\n"); - mrb_parser_dump(mrb, n3->cdr->car, offset+3); - } - if (n3->cdr->cdr->car) { - dump_prefix(offset+2); - printf("rescue body:\n"); - mrb_parser_dump(mrb, n3->cdr->cdr->car, offset+3); - } - n2 = n2->cdr; - } - } - tree = tree->cdr; - if (tree->car) { - dump_prefix(offset+1); - printf("else:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - } - break; - - case NODE_ENSURE: - printf("NODE_ENSURE:\n"); - dump_prefix(offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(offset+1); - printf("ensure:\n"); - mrb_parser_dump(mrb, tree->cdr->cdr, offset+2); - break; - - case NODE_LAMBDA: - printf("NODE_BLOCK:\n"); - goto block; - - case NODE_BLOCK: - block: - printf("NODE_BLOCK:\n"); - tree = tree->cdr; - if (tree->car) { - node *n = tree->car; - - if (n->car) { - dump_prefix(offset+1); - printf("mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n->car) { - dump_prefix(offset+1); - printf("optional args:\n"); - { - node *n2 = n->car; - - while (n2) { - dump_prefix(offset+2); - printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); - mrb_parser_dump(mrb, n2->car->cdr, 0); - n2 = n2->cdr; - } - } - } - n = n->cdr; - if (n->car) { - dump_prefix(offset+1); - printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); - } - n = n->cdr; - if (n->car) { - dump_prefix(offset+1); - printf("post mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n) { - dump_prefix(offset+1); - printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); - } - } - dump_prefix(offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->car, offset+2); - break; - - case NODE_IF: - printf("NODE_IF:\n"); - dump_prefix(offset+1); - printf("cond:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(offset+1); - printf("then:\n"); - mrb_parser_dump(mrb, tree->cdr->car, offset+2); - if (tree->cdr->cdr->car) { - dump_prefix(offset+1); - printf("else:\n"); - mrb_parser_dump(mrb, tree->cdr->cdr->car, offset+2); - } - break; - - case NODE_AND: - printf("NODE_AND:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_OR: - printf("NODE_OR:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_CASE: - printf("NODE_CASE:\n"); - if (tree->car) { - mrb_parser_dump(mrb, tree->car, offset+1); - } - tree = tree->cdr; - while (tree) { - dump_prefix(offset+1); - printf("case:\n"); - dump_recur(mrb, tree->car->car, offset+2); - dump_prefix(offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->car->cdr, offset+2); - tree = tree->cdr; - } - break; - - case NODE_WHILE: - printf("NODE_WHILE:\n"); - dump_prefix(offset+1); - printf("cond:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_UNTIL: - printf("NODE_UNTIL:\n"); - dump_prefix(offset+1); - printf("cond:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_FOR: - printf("NODE_FOR:\n"); - dump_prefix(offset+1); - printf("var:\n"); - { - node *n2 = tree->car; - - if (n2->car) { - dump_prefix(offset+2); - printf("pre:\n"); - dump_recur(mrb, n2->car, offset+3); - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(offset+2); - printf("rest:\n"); - mrb_parser_dump(mrb, n2->car, offset+3); - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(offset+2); - printf("post:\n"); - dump_recur(mrb, n2->car, offset+3); - } - } - } - } - tree = tree->cdr; - dump_prefix(offset+1); - printf("in:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - tree = tree->cdr; - dump_prefix(offset+1); - printf("do:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - break; - - case NODE_SCOPE: - printf("NODE_SCOPE:\n"); - { - node *n2 = tree->car; - - if (n2 && (n2->car || n2->cdr)) { - dump_prefix(offset+1); - printf("local variables:\n"); - dump_prefix(offset+2); - while (n2) { - if (n2->car) { - if (n2 != tree->car) printf(", "); - printf("%s", mrb_sym2name(mrb, sym(n2->car))); - } - n2 = n2->cdr; - } - printf("\n"); - } - } - tree = tree->cdr; - offset++; - goto again; - - case NODE_FCALL: - case NODE_CALL: - printf("NODE_CALL:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - dump_prefix(offset+1); - printf("method='%s' (%d)\n", - mrb_sym2name(mrb, sym(tree->cdr->car)), - (int)(intptr_t)tree->cdr->car); - tree = tree->cdr->cdr->car; - if (tree) { - dump_prefix(offset+1); - printf("args:\n"); - dump_recur(mrb, tree->car, offset+2); - if (tree->cdr) { - dump_prefix(offset+1); - printf("block:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - } - } - break; - - case NODE_DOT2: - printf("NODE_DOT2:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_DOT3: - printf("NODE_DOT3:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - mrb_parser_dump(mrb, tree->cdr, offset+1); - break; - - case NODE_COLON2: - printf("NODE_COLON2:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - dump_prefix(offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->cdr))); - break; - - case NODE_COLON3: - printf("NODE_COLON3:\n"); - dump_prefix(offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_ARRAY: - printf("NODE_ARRAY:\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_HASH: - printf("NODE_HASH:\n"); - while (tree) { - dump_prefix(offset+1); - printf("key:\n"); - mrb_parser_dump(mrb, tree->car->car, offset+2); - dump_prefix(offset+1); - printf("value:\n"); - mrb_parser_dump(mrb, tree->car->cdr, offset+2); - tree = tree->cdr; - } - break; - - case NODE_SPLAT: - printf("NODE_SPLAT:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_ASGN: - printf("NODE_ASGN:\n"); - dump_prefix(offset+1); - printf("lhs:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - dump_prefix(offset+1); - printf("rhs:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_MASGN: - printf("NODE_MASGN:\n"); - dump_prefix(offset+1); - printf("mlhs:\n"); - { - node *n2 = tree->car; - - if (n2->car) { - dump_prefix(offset+2); - printf("pre:\n"); - dump_recur(mrb, n2->car, offset+3); - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(offset+2); - printf("rest:\n"); - if (n2->car == (node*)-1) { - dump_prefix(offset+2); - printf("(empty)\n"); - } - else { - mrb_parser_dump(mrb, n2->car, offset+3); - } - } - n2 = n2->cdr; - if (n2) { - if (n2->car) { - dump_prefix(offset+2); - printf("post:\n"); - dump_recur(mrb, n2->car, offset+3); - } - } - } - } - dump_prefix(offset+1); - printf("rhs:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - break; - - case NODE_OP_ASGN: - printf("NODE_OP_ASGN:\n"); - dump_prefix(offset+1); - printf("lhs:\n"); - mrb_parser_dump(mrb, tree->car, offset+2); - tree = tree->cdr; - dump_prefix(offset+1); - printf("op='%s' (%d)\n", mrb_sym2name(mrb, sym(tree->car)), (int)(intptr_t)tree->car); - tree = tree->cdr; - mrb_parser_dump(mrb, tree->car, offset+1); - break; - - case NODE_SUPER: - printf("NODE_SUPER:\n"); - if (tree) { - dump_prefix(offset+1); - printf("args:\n"); - dump_recur(mrb, tree->car, offset+2); - if (tree->cdr) { - dump_prefix(offset+1); - printf("block:\n"); - mrb_parser_dump(mrb, tree->cdr, offset+2); - } - } - break; - - case NODE_ZSUPER: - printf("NODE_ZSUPER\n"); - break; - - case NODE_RETURN: - printf("NODE_RETURN:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_YIELD: - printf("NODE_YIELD:\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_BREAK: - printf("NODE_BREAK:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_NEXT: - printf("NODE_NEXT:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_REDO: - printf("NODE_REDO\n"); - break; - - case NODE_RETRY: - printf("NODE_RETRY\n"); - break; - - case NODE_LVAR: - printf("NODE_LVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_GVAR: - printf("NODE_GVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_IVAR: - printf("NODE_IVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_CVAR: - printf("NODE_CVAR %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_CONST: - printf("NODE_CONST %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_MATCH: - printf("NODE_MATCH:\n"); - dump_prefix(offset + 1); - printf("lhs:\n"); - mrb_parser_dump(mrb, tree->car, offset + 2); - dump_prefix(offset + 1); - printf("rhs:\n"); - mrb_parser_dump(mrb, tree->cdr, offset + 2); - break; - - case NODE_BACK_REF: - printf("NODE_BACK_REF: $%c\n", (int)(intptr_t)tree); - break; - - case NODE_NTH_REF: - printf("NODE_NTH_REF: $%d\n", (int)(intptr_t)tree); - break; - - case NODE_ARG: - printf("NODE_ARG %s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_BLOCK_ARG: - printf("NODE_BLOCK_ARG:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_INT: - printf("NODE_INT %s base %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr->car); - break; - - case NODE_FLOAT: - printf("NODE_FLOAT %s\n", (char*)tree); - break; - - case NODE_NEGATE: - printf("NODE_NEGATE\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_STR: - printf("NODE_STR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr); - break; - - case NODE_DSTR: - printf("NODE_DSTR\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_XSTR: - printf("NODE_XSTR \"%s\" len %d\n", (char*)tree->car, (int)(intptr_t)tree->cdr); - break; - - case NODE_DXSTR: - printf("NODE_DXSTR\n"); - dump_recur(mrb, tree, offset+1); - break; - - case NODE_REGX: - printf("NODE_REGX /%s/%s\n", (char*)tree->car, (char*)tree->cdr); - break; - - case NODE_DREGX: - printf("NODE_DREGX\n"); - dump_recur(mrb, tree->car, offset+1); - dump_prefix(offset); - printf("tail: %s\n", (char*)tree->cdr->cdr->car); - dump_prefix(offset); - printf("opt: %s\n", (char*)tree->cdr->cdr->cdr); - break; - - case NODE_SYM: - printf("NODE_SYM :%s\n", mrb_sym2name(mrb, sym(tree))); - break; - - case NODE_SELF: - printf("NODE_SELF\n"); - break; - - case NODE_NIL: - printf("NODE_NIL\n"); - break; - - case NODE_TRUE: - printf("NODE_TRUE\n"); - break; - - case NODE_FALSE: - printf("NODE_FALSE\n"); - break; - - case NODE_ALIAS: - printf("NODE_ALIAS %s %s:\n", - mrb_sym2name(mrb, sym(tree->car)), - mrb_sym2name(mrb, sym(tree->cdr))); - break; - - case NODE_UNDEF: - printf("NODE_UNDEF"); - { - node *t = tree; - while (t) { - printf(" %s", mrb_sym2name(mrb, sym(t->car))); - t = t->cdr; - } - } - printf(":\n"); - break; - - case NODE_CLASS: - printf("NODE_CLASS:\n"); - if (tree->car->car == (node*)0) { - dump_prefix(offset+1); - printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else if (tree->car->car == (node*)1) { - dump_prefix(offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else { - mrb_parser_dump(mrb, tree->car->car, offset+1); - dump_prefix(offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - if (tree->cdr->car) { - dump_prefix(offset+1); - printf("super:\n"); - mrb_parser_dump(mrb, tree->cdr->car, offset+2); - } - dump_prefix(offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->cdr->car->cdr, offset+2); - break; - - case NODE_MODULE: - printf("NODE_MODULE:\n"); - if (tree->car->car == (node*)0) { - dump_prefix(offset+1); - printf(":%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else if (tree->car->car == (node*)1) { - dump_prefix(offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - else { - mrb_parser_dump(mrb, tree->car->car, offset+1); - dump_prefix(offset+1); - printf("::%s\n", mrb_sym2name(mrb, sym(tree->car->cdr))); - } - dump_prefix(offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); - break; - - case NODE_SCLASS: - printf("NODE_SCLASS:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - dump_prefix(offset+1); - printf("body:\n"); - mrb_parser_dump(mrb, tree->cdr->car->cdr, offset+2); - break; - - case NODE_DEF: - printf("NODE_DEF:\n"); - dump_prefix(offset+1); - printf("%s\n", mrb_sym2name(mrb, sym(tree->car))); - tree = tree->cdr; - { - node *n2 = tree->car; - - if (n2 && (n2->car || n2->cdr)) { - dump_prefix(offset+1); - printf("local variables:\n"); - dump_prefix(offset+2); - while (n2) { - if (n2->car) { - if (n2 != tree->car) printf(", "); - printf("%s", mrb_sym2name(mrb, sym(n2->car))); - } - n2 = n2->cdr; - } - printf("\n"); - } - } - tree = tree->cdr; - if (tree->car) { - node *n = tree->car; - - if (n->car) { - dump_prefix(offset+1); - printf("mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n->car) { - dump_prefix(offset+1); - printf("optional args:\n"); - { - node *n2 = n->car; - - while (n2) { - dump_prefix(offset+2); - printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); - mrb_parser_dump(mrb, n2->car->cdr, 0); - n2 = n2->cdr; - } - } - } - n = n->cdr; - if (n->car) { - dump_prefix(offset+1); - printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); - } - n = n->cdr; - if (n->car) { - dump_prefix(offset+1); - printf("post mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n) { - dump_prefix(offset+1); - printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); - } - } - mrb_parser_dump(mrb, tree->cdr->car, offset+1); - break; - - case NODE_SDEF: - printf("NODE_SDEF:\n"); - mrb_parser_dump(mrb, tree->car, offset+1); - tree = tree->cdr; - dump_prefix(offset+1); - printf(":%s\n", mrb_sym2name(mrb, sym(tree->car))); - tree = tree->cdr->cdr; - if (tree->car) { - node *n = tree->car; - - if (n->car) { - dump_prefix(offset+1); - printf("mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n->car) { - dump_prefix(offset+1); - printf("optional args:\n"); - { - node *n2 = n->car; - - while (n2) { - dump_prefix(offset+2); - printf("%s=", mrb_sym2name(mrb, sym(n2->car->car))); - mrb_parser_dump(mrb, n2->car->cdr, 0); - n2 = n2->cdr; - } - } - } - n = n->cdr; - if (n->car) { - dump_prefix(offset+1); - printf("rest=*%s\n", mrb_sym2name(mrb, sym(n->car))); - } - n = n->cdr; - if (n->car) { - dump_prefix(offset+1); - printf("post mandatory args:\n"); - dump_recur(mrb, n->car, offset+2); - } - n = n->cdr; - if (n) { - dump_prefix(offset+1); - printf("blk=&%s\n", mrb_sym2name(mrb, sym(n))); - } - } - tree = tree->cdr; - mrb_parser_dump(mrb, tree->car, offset+1); - break; - - case NODE_POSTEXE: - printf("NODE_POSTEXE:\n"); - mrb_parser_dump(mrb, tree, offset+1); - break; - - case NODE_HEREDOC: - printf("NODE_HEREDOC:\n"); - mrb_parser_dump(mrb, ((parser_heredoc_info*)tree)->doc, offset+1); - break; - - default: - printf("node type: %d (0x%x)\n", (int)n, (int)n); - break; - } -#endif -} diff --git a/src/pool.c b/src/pool.c index 4d8c42dd1..18f66fc27 100644 --- a/src/pool.c +++ b/src/pool.c @@ -7,7 +7,7 @@ #include <stddef.h> #include <stdint.h> #include <string.h> -#include "mruby.h" +#include <mruby.h> /* configuration section */ /* allocated memory address should be multiple of POOL_ALIGNMENT */ @@ -47,7 +47,7 @@ struct mrb_pool { # define ALIGN_PADDING(x) (0) #endif -mrb_pool* +MRB_API mrb_pool* mrb_pool_open(mrb_state *mrb) { mrb_pool *pool = (mrb_pool *)mrb_malloc_simple(mrb, sizeof(mrb_pool)); @@ -60,7 +60,7 @@ mrb_pool_open(mrb_state *mrb) return pool; } -void +MRB_API void mrb_pool_close(mrb_pool *pool) { struct mrb_pool_page *page, *tmp; @@ -91,7 +91,7 @@ page_alloc(mrb_pool *pool, size_t len) return page; } -void* +MRB_API void* mrb_pool_alloc(mrb_pool *pool, size_t len) { struct mrb_pool_page *page; @@ -119,7 +119,7 @@ mrb_pool_alloc(mrb_pool *pool, size_t len) return page->last; } -mrb_bool +MRB_API mrb_bool mrb_pool_can_realloc(mrb_pool *pool, void *p, size_t len) { struct mrb_pool_page *page; @@ -140,7 +140,7 @@ mrb_pool_can_realloc(mrb_pool *pool, void *p, size_t len) return FALSE; } -void* +MRB_API void* mrb_pool_realloc(mrb_pool *pool, void *p, size_t oldlen, size_t newlen) { struct mrb_pool_page *page; @@ -166,6 +166,9 @@ mrb_pool_realloc(mrb_pool *pool, void *p, size_t oldlen, size_t newlen) page = page->next; } np = mrb_pool_alloc(pool, newlen); + if (np == NULL) { + return NULL; + } memcpy(np, p, oldlen); return np; } diff --git a/src/print.c b/src/print.c index f4ed85601..03b5eadfa 100644 --- a/src/print.c +++ b/src/print.c @@ -4,66 +4,44 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/string.h" -#include "mruby/variable.h" +#include <mruby.h> +#include <mruby/string.h> +#include <mruby/variable.h> +#ifndef MRB_DISABLE_STDIO static void -printstr(mrb_state *mrb, mrb_value obj) +printstr(mrb_value obj, FILE *stream) { -#ifdef ENABLE_STDIO - char *s; - int len; - if (mrb_string_p(obj)) { - s = RSTRING_PTR(obj); - len = RSTRING_LEN(obj); - fwrite(s, len, 1, stdout); + fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stream); + putc('\n', stream); } -#endif } +#else +# define printstr(obj, stream) (void)0 +#endif -void +MRB_API void mrb_p(mrb_state *mrb, mrb_value obj) { -#ifdef ENABLE_STDIO - obj = mrb_funcall(mrb, obj, "inspect", 0); - printstr(mrb, obj); - putc('\n', stdout); -#endif + printstr(mrb_inspect(mrb, obj), stdout); } -void +MRB_API void mrb_print_error(mrb_state *mrb) { -#ifdef ENABLE_STDIO - mrb_value s; - mrb_print_backtrace(mrb); - s = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0); - if (mrb_string_p(s)) { - fwrite(RSTRING_PTR(s), RSTRING_LEN(s), 1, stderr); - putc('\n', stderr); - } -#endif + printstr(mrb_funcall(mrb, mrb_obj_value(mrb->exc), "inspect", 0), stderr); } -void +MRB_API void mrb_show_version(mrb_state *mrb) { - mrb_value msg; - - msg = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")); - printstr(mrb, msg); - printstr(mrb, mrb_str_new_lit(mrb, "\n")); + printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")), stdout); } -void +MRB_API void mrb_show_copyright(mrb_state *mrb) { - mrb_value msg; - - msg = mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")); - printstr(mrb, msg); - printstr(mrb, mrb_str_new_lit(mrb, "\n")); + printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")), stdout); } diff --git a/src/proc.c b/src/proc.c index d4fe86680..34037b167 100644 --- a/src/proc.c +++ b/src/proc.c @@ -4,10 +4,10 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/class.h" -#include "mruby/proc.h" -#include "mruby/opcode.h" +#include <mruby.h> +#include <mruby/class.h> +#include <mruby/proc.h> +#include <mruby/opcode.h> static mrb_code call_iseq[] = { MKOP_A(OP_CALL, 0), @@ -34,17 +34,27 @@ mrb_proc_new(mrb_state *mrb, mrb_irep *irep) return p; } -static inline void +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->mid = mrb->c->ci->mid; + 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 = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, (struct RClass*)mrb->c->ci->proc->env); - MRB_ENV_STACK_LEN(e)= (unsigned int)nlocals; - e->mid = mrb->c->ci->mid; - e->cioff = mrb->c->ci - mrb->c->cibase; - e->stack = mrb->c->stack; + e = env_new(mrb, nlocals); mrb->c->ci->env = e; } else { @@ -62,7 +72,7 @@ mrb_closure_new(mrb_state *mrb, mrb_irep *irep) return p; } -struct RProc * +MRB_API struct RProc * mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func) { struct RProc *p; @@ -75,16 +85,57 @@ mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func) return p; } -struct RProc * -mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals) +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); - - closure_setup(mrb, p, nlocals); + 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; } -void +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]; +} + +MRB_API void mrb_proc_copy(struct RProc *a, struct RProc *b) { a->flags = b->flags; @@ -149,13 +200,25 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self) { struct RProc *p = mrb_proc_ptr(self); mrb_code *iseq = mrb_proc_iseq(mrb, p); - mrb_aspec aspec = GETARG_Ax(*iseq); - int ma, ra, pa, arity; + 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); + } + + /* 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 ? -(ma + pa + 1) : ma + pa; + arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa; return mrb_fixnum_value(arity); } @@ -179,6 +242,9 @@ proc_lambda(mrb_state *mrb, mrb_value self) if (mrb_nil_p(blk)) { mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block"); } + if (mrb_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); @@ -201,9 +267,6 @@ mrb_init_proc(mrb_state *mrb) call_irep->iseq = call_iseq; call_irep->ilen = 1; - 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_define_method(mrb, mrb->proc_class, "initialize", mrb_proc_initialize, MRB_ARGS_NONE()); 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()); diff --git a/src/range.c b/src/range.c index 53000e660..bc8b11419 100644 --- a/src/range.c +++ b/src/range.c @@ -4,11 +4,11 @@ ** See Copyright Notice in mruby.h */ -#include "mruby.h" -#include "mruby/class.h" -#include "mruby/range.h" -#include "mruby/string.h" -#include "mruby/array.h" +#include <mruby.h> +#include <mruby/class.h> +#include <mruby/range.h> +#include <mruby/string.h> +#include <mruby/array.h> #define RANGE_CLASS (mrb_class_get(mrb, "Range")) @@ -33,7 +33,7 @@ range_check(mrb_state *mrb, mrb_value a, mrb_value b) } } -mrb_value +MRB_API mrb_value mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl) { struct RRange *r; @@ -234,16 +234,16 @@ mrb_range_include(mrb_state *mrb, mrb_value range) return mrb_bool_value(include_p); } -mrb_bool +static mrb_bool range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc) { - mrb_int beg, end, b, e; + mrb_int beg, end; struct RRange *r = mrb_range_ptr(range); if (mrb_type(range) != MRB_TT_RANGE) return FALSE; - beg = b = mrb_int(mrb, r->edges->beg); - end = e = mrb_int(mrb, r->edges->end); + beg = mrb_int(mrb, r->edges->beg); + end = mrb_int(mrb, r->edges->end); if (beg < 0) { beg += len; @@ -266,7 +266,7 @@ range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb return TRUE; } -mrb_bool +MRB_API mrb_bool mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len) { return range_beg_len(mrb, range, begp, lenp, len, TRUE); @@ -290,7 +290,7 @@ range_to_s(mrb_state *mrb, mrb_value range) 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_append(mrb, str, str2); + mrb_str_cat_str(mrb, str, str2); return str; } @@ -315,7 +315,7 @@ range_inspect(mrb_state *mrb, mrb_value range) str2 = mrb_inspect(mrb, r->edges->end); str = mrb_str_dup(mrb, str); mrb_str_cat(mrb, str, "...", r->excl ? 3 : 2); - mrb_str_append(mrb, str, str2); + mrb_str_cat_str(mrb, str, str2); return str; } diff --git a/src/state.c b/src/state.c index 3e82a159d..c8c0658e4 100644 --- a/src/state.c +++ b/src/state.c @@ -6,14 +6,17 @@ #include <stdlib.h> #include <string.h> -#include "mruby.h" -#include "mruby/irep.h" -#include "mruby/variable.h" -#include "mruby/debug.h" -#include "mruby/string.h" +#include <mruby.h> +#include <mruby/irep.h> +#include <mruby/variable.h> +#include <mruby/debug.h> +#include <mruby/string.h> -void mrb_init_heap(mrb_state*); 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) @@ -21,42 +24,33 @@ inspect_main(mrb_state *mrb, mrb_value mod) return mrb_str_new_lit(mrb, "main"); } -mrb_state* -mrb_open_allocf(mrb_allocf f, void *ud) +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; -#ifdef MRB_NAN_BOXING - mrb_static_assert(sizeof(void*) == 4, "when using NaN boxing sizeof pointer must be 4 byte"); -#endif - mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud); if (mrb == NULL) return NULL; *mrb = mrb_state_zero; - mrb->ud = ud; + mrb->allocf_ud = ud; mrb->allocf = f; - mrb->current_white_part = MRB_GC_WHITE_A; mrb->atexit_stack_len = 0; -#ifndef MRB_GC_FIXED_ARENA - mrb->arena = (struct RBasic**)mrb_malloc(mrb, sizeof(struct RBasic*)*MRB_GC_ARENA_SIZE); - mrb->arena_capa = MRB_GC_ARENA_SIZE; -#endif - - mrb_init_heap(mrb); + mrb_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; } -static void* -allocf(mrb_state *mrb, void *p, size_t size, void *ud) +void* +mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud) { if (size == 0) { free(p); @@ -72,7 +66,7 @@ struct alloca_header { char buf[]; }; -void* +MRB_API void* mrb_alloca(mrb_state *mrb, size_t size) { struct alloca_header *p; @@ -99,16 +93,31 @@ mrb_alloca_free(mrb_state *mrb) } } -mrb_state* +MRB_API mrb_state* mrb_open(void) { - mrb_state *mrb = mrb_open_allocf(allocf, NULL); + 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_free_heap(mrb_state *mrb); void mrb_irep_incref(mrb_state *mrb, mrb_irep *irep) @@ -168,7 +177,7 @@ mrb_str_pool(mrb_state *mrb, mrb_value str) ns->tt = MRB_TT_STRING; ns->c = mrb->string_class; - if (s->flags & MRB_STR_NOFREE) { + 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; @@ -176,9 +185,9 @@ mrb_str_pool(mrb_state *mrb, mrb_value str) } else { ns->flags = 0; - if (s->flags & MRB_STR_EMBED) { + if (RSTR_EMBED_P(s)) { ptr = s->as.ary; - len = (mrb_int)((s->flags & MRB_STR_EMBED_LEN_MASK) >> MRB_STR_EMBED_LEN_SHIFT); + len = RSTR_EMBED_LEN(s); } else { ptr = s->as.heap.ptr; @@ -186,9 +195,8 @@ mrb_str_pool(mrb_state *mrb, mrb_value str) } if (len < RSTRING_EMBED_LEN_MAX) { - ns->flags |= MRB_STR_EMBED; - ns->flags &= ~MRB_STR_EMBED_LEN_MASK; - ns->flags |= (size_t)len << MRB_STR_EMBED_LEN_SHIFT; + RSTR_SET_EMBED_FLAG(ns); + RSTR_SET_EMBED_LEN(ns, len); if (ptr) { memcpy(ns->as.ary, ptr, len); } @@ -207,7 +215,7 @@ mrb_str_pool(mrb_state *mrb, mrb_value str) return mrb_obj_value(ns); } -void +MRB_API void mrb_free_context(mrb_state *mrb, struct mrb_context *c) { if (!c) return; @@ -218,9 +226,10 @@ mrb_free_context(mrb_state *mrb, struct mrb_context *c) mrb_free(mrb, c); } -void +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) { @@ -235,15 +244,12 @@ mrb_close(mrb_state *mrb) mrb_gc_free_gv(mrb); mrb_free_context(mrb, mrb->root_c); mrb_free_symtbl(mrb); - mrb_free_heap(mrb); mrb_alloca_free(mrb); -#ifndef MRB_GC_FIXED_ARENA - mrb_free(mrb, mrb->arena); -#endif + mrb_gc_destroy(mrb, &mrb->gc); mrb_free(mrb, mrb); } -mrb_irep* +MRB_API mrb_irep* mrb_add_irep(mrb_state *mrb) { static const mrb_irep mrb_irep_zero = { 0 }; @@ -256,7 +262,7 @@ mrb_add_irep(mrb_state *mrb) return irep; } -mrb_value +MRB_API mrb_value mrb_top_self(mrb_state *mrb) { if (!mrb->top_self) { @@ -267,7 +273,7 @@ mrb_top_self(mrb_state *mrb) return mrb_obj_value(mrb->top_self); } -void +MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f) { #ifdef MRB_FIXED_STATE_ATEXIT_STACK diff --git a/src/string.c b/src/string.c index 6570c89fb..6664eabd6 100644 --- a/src/string.c +++ b/src/string.c @@ -4,20 +4,21 @@ ** See Copyright Notice in mruby.h */ -#include <ctype.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" - -const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; +#include <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/range.h> +#include <mruby/string.h> +#include <mruby/re.h> typedef struct mrb_shared_string { mrb_bool nofree : 1; @@ -26,129 +27,39 @@ typedef struct mrb_shared_string { mrb_int len; } mrb_shared_string; -static mrb_value str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2); -static mrb_value mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len); - -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; -} - -static inline void -resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) -{ - if (RSTR_EMBED_P(s)) { - if (RSTRING_EMBED_LEN_MAX < capacity) { - char *const tmp = (char *)mrb_malloc(mrb, capacity+1); - const mrb_int len = RSTR_EMBED_LEN(s); - memcpy(tmp, s->as.ary, len); - RSTR_UNSET_EMBED_FLAG(s); - s->as.heap.ptr = tmp; - s->as.heap.len = len; - s->as.heap.aux.capa = capacity; - } - } - else { - s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1); - s->as.heap.aux.capa = capacity; - } -} +const char mrb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; -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); - } -} +#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class)) -void -mrb_str_modify(mrb_state *mrb, struct RString *s) +static struct RString* +str_new_static(mrb_state *mrb, const char *p, size_t len) { - if (RSTR_SHARED_P(s)) { - mrb_shared_string *shared = s->as.heap.aux.shared; - - if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { - s->as.heap.ptr = shared->ptr; - s->as.heap.aux.capa = shared->len; - RSTR_PTR(s)[s->as.heap.len] = '\0'; - mrb_free(mrb, shared); - } - else { - char *ptr, *p; - mrb_int len; - - p = RSTR_PTR(s); - len = s->as.heap.len; - ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); - if (p) { - memcpy(ptr, p, len); - } - ptr[len] = '\0'; - s->as.heap.ptr = ptr; - s->as.heap.aux.capa = len; - str_decref(mrb, shared); - } - RSTR_UNSET_SHARED_FLAG(s); - return; - } - if (RSTR_NOFREE_P(s)) { - char *p = s->as.heap.ptr; + struct RString *s; - s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1); - if (p) { - memcpy(RSTR_PTR(s), p, s->as.heap.len); - } - RSTR_PTR(s)[s->as.heap.len] = '\0'; - s->as.heap.aux.capa = s->as.heap.len; - RSTR_UNSET_NOFREE_FLAG(s); - return; + if (len >= MRB_INT_MAX) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); } -} - -mrb_value -mrb_str_resize(mrb_state *mrb, mrb_value str, mrb_int len) -{ - mrb_int slen; - struct RString *s = mrb_str_ptr(str); + s = mrb_obj_alloc_string(mrb); + s->as.heap.len = len; + s->as.heap.aux.capa = 0; /* nofree */ + s->as.heap.ptr = (char *)p; + s->flags = MRB_STR_NOFREE; - 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; + return s; } -#define mrb_obj_alloc_string(mrb) ((struct RString*)mrb_obj_alloc((mrb), MRB_TT_STRING, (mrb)->string_class)) - 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); + RSTR_SET_EMBED_LEN(s, len); if (p) { memcpy(s->as.ary, p, len); } @@ -167,7 +78,7 @@ str_new(mrb_state *mrb, const char *p, size_t len) return s; } -static void +static inline void str_with_class(mrb_state *mrb, struct RString *s, mrb_value obj) { s->c = mrb_str_ptr(obj)->c; @@ -186,7 +97,7 @@ mrb_str_new_empty(mrb_state *mrb, mrb_value str) # define MRB_STR_BUF_MIN_SIZE 128 #endif -mrb_value +MRB_API mrb_value mrb_str_buf_new(mrb_state *mrb, size_t capa) { struct RString *s; @@ -207,6 +118,26 @@ mrb_str_buf_new(mrb_state *mrb, size_t capa) return mrb_obj_value(s); } +static inline void +resize_capa(mrb_state *mrb, struct RString *s, mrb_int capacity) +{ + if (RSTR_EMBED_P(s)) { + if (RSTRING_EMBED_LEN_MAX < capacity) { + char *const tmp = (char *)mrb_malloc(mrb, capacity+1); + const mrb_int len = RSTR_EMBED_LEN(s); + memcpy(tmp, s->as.ary, len); + RSTR_UNSET_EMBED_FLAG(s); + s->as.heap.ptr = tmp; + s->as.heap.len = len; + s->as.heap.aux.capa = capacity; + } + } + else { + s->as.heap.ptr = (char *)mrb_realloc(mrb, RSTR_PTR(s), capacity+1); + s->as.heap.aux.capa = capacity; + } +} + static void str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len) { @@ -248,7 +179,7 @@ str_buf_cat(mrb_state *mrb, struct RString *s, const char *ptr, size_t len) RSTR_PTR(s)[total] = '\0'; /* sentinel */ } -mrb_value +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)); @@ -261,7 +192,7 @@ mrb_str_new(mrb_state *mrb, const char *p, size_t len) * Returns a new string object containing a copy of <i>str</i>. */ -mrb_value +MRB_API mrb_value mrb_str_new_cstr(mrb_state *mrb, const char *p) { struct RString *s; @@ -279,20 +210,23 @@ mrb_str_new_cstr(mrb_state *mrb, const char *p) return mrb_obj_value(s); } -mrb_value +MRB_API mrb_value mrb_str_new_static(mrb_state *mrb, const char *p, size_t len) { - struct RString *s; + struct RString *s = str_new_static(mrb, p, len); + return mrb_obj_value(s); +} - if (len >= MRB_INT_MAX) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "string size too big"); +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); } - s = mrb_obj_alloc_string(mrb); - s->as.heap.len = len; - s->as.heap.aux.capa = 0; /* nofree */ - s->as.heap.ptr = (char *)p; - s->flags = MRB_STR_NOFREE; - return mrb_obj_value(s); } void @@ -306,20 +240,126 @@ mrb_gc_free_str(mrb_state *mrb, struct RString *str) mrb_free(mrb, str->as.heap.ptr); } -char * -mrb_str_to_cstr(mrb_state *mrb, mrb_value str0) +#ifdef MRB_UTF8_STRING +static const char utf8len_codepage[256] = { - struct RString *s; + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,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, +}; - if (!mrb_string_p(str0)) { - mrb_raise(mrb, E_TYPE_ERROR, "expected String"); +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; + e += len < 0 ? RSTRING_LEN(str) : len; + while (p<e) { + p += utf8len(p, e); + total++; } + return total; +} - 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"); +#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 RSTR_PTR(s); + 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(p, p+bi); + b += n; + p += n; + } + return i; +} + +#else +#define RSTRING_CHAR_LEN(s) RSTRING_LEN(s) +#define chars2bytes(p, off, ci) (ci) +#define bytes2chars(p, bi) (bi) +#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, 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 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 = y, *ye = ys + n; + for (; y < ye; ++y) { + if (*x == *y) + return y - ys; + } + return -1; + } + return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); } static void @@ -360,18 +400,336 @@ str_make_shared(mrb_state *mrb, struct RString *s) } } -/* - * call-seq: - * char* str = String("abcd"), len=strlen("abcd") - * - * Returns a new string object containing a copy of <i>str</i>. - */ -const char* -mrb_str_body(mrb_value str, int *len_p) +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)) { + 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 (beg + len > clen) + len = clen - beg; + if (len <= 0) { + len = 0; + } + return str_subseq(mrb, str, beg, len); +} + +static mrb_int +str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset) +{ + mrb_int pos; + char *s, *sptr; + mrb_int len, slen; + + len = RSTRING_LEN(str); + slen = RSTRING_LEN(sub); + if (offset < 0) { + offset += len; + if (offset < 0) return -1; + } + if (len - offset < slen) return -1; + s = RSTRING_PTR(str); + if (offset) { + s += offset; + } + if (slen == 0) return offset; + /* need proceed one character at a time */ + sptr = RSTRING_PTR(sub); + slen = RSTRING_LEN(sub); + len = RSTRING_LEN(str) - offset; + pos = mrb_memsearch(sptr, slen, s, len); + if (pos < 0) return pos; + return pos + offset; +} + +static void +check_frozen(mrb_state *mrb, struct RString *s) +{ + if (RSTR_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); + 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 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, size_t len) +{ + wchar_t* wcsp; + char* mbsp; + size_t mbssize, wcssize; + + if (len == 0) + return strdup(""); + if (len == -1) + len = 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, size_t len) +{ + wchar_t* wcsp; + char* mbsp; + size_t mbssize, wcssize; + + if (len == 0) + return strdup(""); + if (len == -1) + len = 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); + if (RSTR_SHARED_P(s)) { + mrb_shared_string *shared = s->as.heap.aux.shared; + + if (shared->refcnt == 1 && s->as.heap.ptr == shared->ptr) { + s->as.heap.ptr = shared->ptr; + s->as.heap.aux.capa = shared->len; + RSTR_PTR(s)[s->as.heap.len] = '\0'; + mrb_free(mrb, shared); + } + else { + char *ptr, *p; + mrb_int len; + + p = RSTR_PTR(s); + len = s->as.heap.len; + ptr = (char *)mrb_malloc(mrb, (size_t)len + 1); + if (p) { + memcpy(ptr, p, len); + } + ptr[len] = '\0'; + s->as.heap.ptr = ptr; + s->as.heap.aux.capa = len; + str_decref(mrb, shared); + } + RSTR_UNSET_SHARED_FLAG(s); + return; + } + if (RSTR_NOFREE_P(s)) { + char *p = s->as.heap.ptr; + + s->as.heap.ptr = (char *)mrb_malloc(mrb, (size_t)s->as.heap.len+1); + if (p) { + memcpy(RSTR_PTR(s), p, s->as.heap.len); + } + RSTR_PTR(s)[s->as.heap.len] = '\0'; + s->as.heap.aux.capa = s->as.heap.len; + RSTR_UNSET_NOFREE_FLAG(s); + return; + } +} + +static mrb_value +mrb_str_freeze(mrb_state *mrb, mrb_value str) +{ + struct RString *s = mrb_str_ptr(str); + + RSTR_SET_FROZEN_FLAG(s); + return str; +} + +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); - *len_p = RSTR_LEN(s); + mrb_str_modify(mrb, s); + slen = RSTR_LEN(s); + if (len != slen) { + if (slen < len || slen - len > 256) { + resize_capa(mrb, s, len); + } + RSTR_SET_LEN(s, len); + RSTR_PTR(s)[len] = '\0'; /* sentinel */ + } + 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); } @@ -381,7 +739,7 @@ mrb_str_body(mrb_value str, int *len_p) * * Returns a new string object containing a copy of <i>str</i>. */ -void +MRB_API void mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) { struct RString *s1 = mrb_str_ptr(self), *s2; @@ -408,7 +766,7 @@ mrb_str_concat(mrb_state *mrb, mrb_value self, mrb_value other) * * Returns a new string object containing a copy of <i>str</i>. */ -mrb_value +MRB_API mrb_value mrb_str_plus(mrb_state *mrb, mrb_value a, mrb_value b) { struct RString *s = mrb_str_ptr(a); @@ -439,32 +797,26 @@ mrb_str_plus_m(mrb_state *mrb, mrb_value self) return mrb_str_plus(mrb, self, str); } +/* 15.2.10.5.26 */ +/* 15.2.10.5.33 */ /* * call-seq: - * len = strlen(String("abcd")) + * "abcd".size => int * - * Returns a new string object containing a copy of <i>str</i>. + * Returns the length of string. */ static mrb_value -mrb_str_bytesize(mrb_state *mrb, mrb_value self) +mrb_str_size(mrb_state *mrb, mrb_value self) { - struct RString *s = mrb_str_ptr(self); - return mrb_fixnum_value(RSTR_LEN(s)); + mrb_int len = RSTRING_CHAR_LEN(self); + return mrb_fixnum_value(len); } -/* 15.2.10.5.26 */ -/* 15.2.10.5.33 */ -/* - * call-seq: - * len = strlen(String("abcd")) - * - * Returns a new string object containing a copy of <i>str</i>. - */ -mrb_value -mrb_str_size(mrb_state *mrb, mrb_value self) +static mrb_value +mrb_str_bytesize(mrb_state *mrb, mrb_value self) { - struct RString *s = mrb_str_ptr(self); - return mrb_fixnum_value(RSTR_LEN(s)); + mrb_int len = RSTRING_LEN(self); + return mrb_fixnum_value(len); } /* 15.2.10.5.1 */ @@ -521,7 +873,7 @@ mrb_str_times(mrb_state *mrb, mrb_value self) * = 0 * < -1 */ -int +MRB_API int mrb_str_cmp(mrb_state *mrb, mrb_value str1, mrb_value str2) { mrb_int len; @@ -607,7 +959,7 @@ str_eql(mrb_state *mrb, const mrb_value str1, const mrb_value str2) return FALSE; } -mrb_bool +MRB_API mrb_bool mrb_str_equal(mrb_state *mrb, mrb_value str1, mrb_value str2) { if (mrb_immediate_p(str2)) return FALSE; @@ -643,7 +995,7 @@ mrb_str_equal_m(mrb_state *mrb, mrb_value str1) return mrb_bool_value(mrb_str_equal(mrb, str1, str2)); } /* ---------------------------------- */ -mrb_value +MRB_API mrb_value mrb_str_to_str(mrb_state *mrb, mrb_value str) { mrb_value s; @@ -658,100 +1010,35 @@ mrb_str_to_str(mrb_state *mrb, mrb_value str) return str; } -char * +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); } -static mrb_value -noregexp(mrb_state *mrb, mrb_value self) -{ - mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented"); - return mrb_nil_value(); -} - -static void -regexp_check(mrb_state *mrb, mrb_value obj) +MRB_API mrb_int +mrb_string_value_len(mrb_state *mrb, mrb_value ptr) { - if (mrb_regexp_p(mrb, obj)) { - noregexp(mrb, obj); - } -} - -static inline mrb_int -mrb_memsearch_qs(const unsigned char *xs, mrb_int m, const unsigned char *ys, mrb_int n) -{ - const unsigned char *x = xs, *xe = xs + m; - const unsigned char *y = ys; - int i, qstable[256]; - - /* Preprocessing */ - for (i = 0; i < 256; ++i) - qstable[i] = m + 1; - for (; x < xe; ++x) - qstable[*x] = xe - x; - /* Searching */ - for (; y + m <= ys + n; y += *(qstable + y[m])) { - if (*xs == *y && memcmp(xs, y, m) == 0) - return y - ys; - } - return -1; + mrb_value str = mrb_str_to_str(mrb, ptr); + return RSTRING_LEN(str); } -static mrb_int -mrb_memsearch(const void *x0, mrb_int m, const void *y0, mrb_int n) +void +mrb_noregexp(mrb_state *mrb, mrb_value self) { - 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 = y, *ye = ys + n; - for (; y < ye; ++y) { - if (*x == *y) - return y - ys; - } - return -1; - } - return mrb_memsearch_qs((const unsigned char *)x0, m, (const unsigned char *)y0, n); + mrb_raise(mrb, E_NOTIMP_ERROR, "Regexp class not implemented"); } -static mrb_int -mrb_str_index(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int offset) +void +mrb_regexp_check(mrb_state *mrb, mrb_value obj) { - mrb_int pos; - char *s, *sptr; - mrb_int len, slen; - - len = RSTRING_LEN(str); - slen = RSTRING_LEN(sub); - if (offset < 0) { - offset += len; - if (offset < 0) return -1; - } - if (len - offset < slen) return -1; - s = RSTRING_PTR(str); - if (offset) { - s += offset; + if (mrb_regexp_p(mrb, obj)) { + mrb_noregexp(mrb, obj); } - if (slen == 0) return offset; - /* need proceed one character at a time */ - sptr = RSTRING_PTR(sub); - slen = RSTRING_LEN(sub); - len = RSTRING_LEN(str) - offset; - pos = mrb_memsearch(sptr, slen, s, len); - if (pos < 0) return pos; - return pos + offset; } -mrb_value +MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str) { struct RString *s = mrb_str_ptr(str); @@ -766,18 +1053,18 @@ mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx) { mrb_int idx; - regexp_check(mrb, indx); + mrb_regexp_check(mrb, indx); switch (mrb_type(indx)) { case MRB_TT_FIXNUM: idx = mrb_fixnum(indx); num_index: - str = mrb_str_substr(mrb, str, idx, 1); + 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 (mrb_str_index(mrb, str, indx, 0) != -1) + if (str_index(mrb, str, indx, 0) != -1) return mrb_str_dup(mrb, indx); return mrb_nil_value(); @@ -786,15 +1073,20 @@ num_index: { mrb_int beg, len; - len = RSTRING_LEN(str); + len = RSTRING_CHAR_LEN(str); if (mrb_range_beg_len(mrb, indx, &beg, &len, len)) { - return mrb_str_subseq(mrb, str, beg, len); + return str_subseq(mrb, str, beg, len); } else { return mrb_nil_value(); } } + case MRB_TT_FLOAT: default: + indx = mrb_Integer(mrb, indx); + if (mrb_nil_p(indx)) { + mrb_raise(mrb, E_TYPE_ERROR, "can't convert to Fixnum"); + } idx = mrb_fixnum(indx); goto num_index; } @@ -831,6 +1123,7 @@ num_index: * * 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" @@ -848,8 +1141,8 @@ mrb_str_aref_m(mrb_state *mrb, mrb_value str) argc = mrb_get_args(mrb, "o|o", &a1, &a2); if (argc == 2) { - regexp_check(mrb, a1); - return mrb_str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2)); + mrb_regexp_check(mrb, a1); + return str_substr(mrb, str, mrb_fixnum(a1), mrb_fixnum(a2)); } if (argc != 1) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "wrong number of arguments (%S for 1)", mrb_fixnum_value(argc)); @@ -919,7 +1212,7 @@ mrb_str_capitalize(mrb_state *mrb, mrb_value self) /* 15.2.10.5.10 */ /* * call-seq: - * str.chomp!(separator=$/) => str or nil + * str.chomp!(separator="\n") => str or nil * * Modifies <i>str</i> in place as described for <code>String#chomp</code>, * returning <i>str</i>, or <code>nil</code> if no modifications were made. @@ -993,7 +1286,7 @@ mrb_str_chomp_bang(mrb_state *mrb, mrb_value str) /* 15.2.10.5.9 */ /* * call-seq: - * str.chomp(separator=$/) => new_str + * str.chomp(separator="\n") => new_str * * Returns a new <code>String</code> with the given record separator removed * from the end of <i>str</i> (if present). If <code>$/</code> has not been @@ -1036,7 +1329,18 @@ mrb_str_chop_bang(mrb_state *mrb, mrb_value 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') { @@ -1113,7 +1417,7 @@ mrb_str_downcase_bang(mrb_state *mrb, mrb_value str) * * Returns a copy of <i>str</i> with all uppercase letters replaced with their * lowercase counterparts. The operation is locale insensitive---only - * characters ``A'' to ``Z'' are affected. + * characters 'A' to 'Z' are affected. * * "hEllO".downcase #=> "hello" */ @@ -1164,47 +1468,10 @@ mrb_str_eql(mrb_state *mrb, mrb_value self) return mrb_bool_value(eql_p); } -static mrb_value -mrb_str_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) -{ - struct RString *orig, *s; - mrb_shared_string *shared; - - orig = mrb_str_ptr(str); - if (RSTR_EMBED_P(orig)) { - s = str_new(mrb, orig->as.ary+beg, len); - } else { - str_make_shared(mrb, orig); - shared = orig->as.heap.aux.shared; - s = mrb_obj_alloc_string(mrb); - s->as.heap.ptr = orig->as.heap.ptr + beg; - s->as.heap.len = len; - s->as.heap.aux.shared = shared; - RSTR_SET_SHARED_FLAG(s); - shared->refcnt++; - } - - return mrb_obj_value(s); -} - -mrb_value +MRB_API mrb_value mrb_str_substr(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len) { - if (len < 0) return mrb_nil_value(); - if (!RSTRING_LEN(str)) { - len = 0; - } - if (beg > RSTRING_LEN(str)) return mrb_nil_value(); - if (beg < 0) { - beg += RSTRING_LEN(str); - if (beg < 0) return mrb_nil_value(); - } - if (beg + len > RSTRING_LEN(str)) - len = RSTRING_LEN(str) - beg; - if (len <= 0) { - len = 0; - } - return mrb_str_subseq(mrb, str, beg, len); + return str_substr(mrb, str, beg, len); } mrb_int @@ -1263,7 +1530,7 @@ mrb_str_include(mrb_state *mrb, mrb_value self) } else { str2 = mrb_str_to_str(mrb, str2); - i = mrb_str_index(mrb, self, str2, 0); + i = str_index(mrb, self, str2, 0); include_p = (i != -1); } @@ -1293,12 +1560,12 @@ mrb_str_include(mrb_state *mrb, mrb_value self) * "hello".index(/[aeiou]/, -3) #=> 4 */ static mrb_value -mrb_str_index_m(mrb_state *mrb, mrb_value str) +mrb_str_index(mrb_state *mrb, mrb_value str) { mrb_value *argv; mrb_int argc; mrb_value sub; - mrb_int pos; + mrb_int pos, clen; mrb_get_args(mrb, "*", &argv, &argc); if (argc == 2) { @@ -1312,26 +1579,18 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str) else sub = mrb_nil_value(); } - regexp_check(mrb, sub); + mrb_regexp_check(mrb, sub); + clen = RSTRING_CHAR_LEN(str); if (pos < 0) { - pos += RSTRING_LEN(str); + pos += clen; if (pos < 0) { return mrb_nil_value(); } } + if (pos >= clen) return mrb_nil_value(); + pos = chars2bytes(str, 0, pos); switch (mrb_type(sub)) { - case MRB_TT_FIXNUM: { - int c = mrb_fixnum(sub); - mrb_int len = RSTRING_LEN(str); - unsigned char *p = (unsigned char*)RSTRING_PTR(str); - - for (;pos<len;pos++) { - if (p[pos] == c) return mrb_fixnum_value(pos); - } - return mrb_nil_value(); - } - default: { mrb_value tmp; @@ -1343,56 +1602,17 @@ mrb_str_index_m(mrb_state *mrb, mrb_value str) } /* fall through */ case MRB_TT_STRING: - pos = mrb_str_index(mrb, str, sub, pos); + pos = str_index(mrb, str, sub, pos); break; } if (pos == -1) return mrb_nil_value(); + pos = bytes2chars(RSTRING_PTR(str), pos); return mrb_fixnum_value(pos); } #define STR_REPLACE_SHARED_MIN 10 -static mrb_value -str_replace(mrb_state *mrb, struct RString *s1, struct RString *s2) -{ - long len; - - len = RSTR_LEN(s2); - if (RSTR_SHARED_P(s1)) { - str_decref(mrb, s1->as.heap.aux.shared); - } - else if (!RSTR_EMBED_P(s1) && !RSTR_NOFREE_P(s1)) { - mrb_free(mrb, s1->as.heap.ptr); - } - - RSTR_UNSET_NOFREE_FLAG(s1); - - if (RSTR_SHARED_P(s2)) { -L_SHARE: - RSTR_UNSET_EMBED_FLAG(s1); - s1->as.heap.ptr = s2->as.heap.ptr; - s1->as.heap.len = len; - s1->as.heap.aux.shared = s2->as.heap.aux.shared; - RSTR_SET_SHARED_FLAG(s1); - s1->as.heap.aux.shared->refcnt++; - } - else { - if (len <= RSTRING_EMBED_LEN_MAX) { - RSTR_UNSET_SHARED_FLAG(s1); - RSTR_SET_EMBED_FLAG(s1); - memcpy(s1->as.ary, RSTR_PTR(s2), len); - RSTR_SET_EMBED_LEN(s1, len); - } - else { - str_make_shared(mrb, s2); - goto L_SHARE; - } - } - - return mrb_obj_value(s1); -} - /* 15.2.10.5.24 */ /* 15.2.10.5.28 */ /* @@ -1450,13 +1670,13 @@ mrb_str_init(mrb_state *mrb, mrb_value self) * * 'cat and dog'.to_sym #=> :"cat and dog" */ -mrb_value +MRB_API mrb_value mrb_str_intern(mrb_state *mrb, mrb_value self) { return mrb_symbol_value(mrb_intern_str(mrb, self)); } /* ---------------------------------- */ -mrb_value +MRB_API mrb_value mrb_obj_as_string(mrb_state *mrb, mrb_value obj) { mrb_value str; @@ -1470,7 +1690,7 @@ mrb_obj_as_string(mrb_state *mrb, mrb_value obj) return str; } -mrb_value +MRB_API mrb_value mrb_ptr_to_str(mrb_state *mrb, void *p) { struct RString *p_str; @@ -1500,119 +1720,93 @@ mrb_ptr_to_str(mrb_state *mrb, void *p) return mrb_obj_value(p_str); } -mrb_value +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_value +MRB_API mrb_value mrb_check_string_type(mrb_state *mrb, mrb_value str) { return mrb_check_convert_type(mrb, str, MRB_TT_STRING, "String", "to_str"); } -/* ---------------------------------- */ -/* 15.2.10.5.29 */ +/* 15.2.10.5.30 */ /* * call-seq: - * str.reverse => new_str - * - * Returns a new string with the characters from <i>str</i> in reverse order. + * str.reverse! => str * - * "stressed".reverse #=> "desserts" + * Reverses <i>str</i> in place. */ static mrb_value -mrb_str_reverse(mrb_state *mrb, mrb_value str) +mrb_str_reverse_bang(mrb_state *mrb, mrb_value str) { - struct RString *s2; - char *s, *e, *p; +#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; - if (RSTRING_LEN(str) <= 1) return mrb_str_dup(mrb, str); + mrb_str_modify(mrb, mrb_str_ptr(str)); + len = RSTRING_LEN(str); + buf = mrb_malloc(mrb, (size_t)len); + p = buf; + e = buf + len; - s2 = str_new(mrb, 0, RSTRING_LEN(str)); - str_with_class(mrb, s2, str); - s = RSTRING_PTR(str); e = RSTRING_END(str) - 1; - p = RSTR_PTR(s2); + memcpy(buf, RSTRING_PTR(str), len); + r = RSTRING_PTR(str) + len; - while (e >= s) { - *p++ = *e--; + while (p<e) { + mrb_int clen = utf8len(p, e); + r -= clen; + memcpy(r, p, clen); + p += clen; + } + mrb_free(mrb, buf); } - return mrb_obj_value(s2); -} + return 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) -{ - struct RString *s = mrb_str_ptr(str); - char *p, *e; - char c; + 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) { + 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; } - return str; } +/* ---------------------------------- */ +/* 15.2.10.5.29 */ /* * call-seq: - * str.rindex(substring [, fixnum]) => fixnum or nil - * str.rindex(fixnum [, fixnum]) => fixnum or nil - * str.rindex(regexp [, fixnum]) => fixnum or nil + * str.reverse => new_str * - * Returns the index of the last occurrence of the given <i>substring</i>, - * character (<i>fixnum</i>), or pattern (<i>regexp</i>) in <i>str</i>. Returns - * <code>nil</code> if not found. If the second parameter is present, it - * specifies the position in the string to end the search---characters beyond - * this point will not be considered. + * Returns a new string with the characters from <i>str</i> in reverse order. * - * "hello".rindex('e') #=> 1 - * "hello".rindex('l') #=> 3 - * "hello".rindex('a') #=> nil - * "hello".rindex(101) #=> 1 - * "hello".rindex(/[aeiou]/, -2) #=> 1 + * "stressed".reverse #=> "desserts" */ -static mrb_int -mrb_str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) +static mrb_value +mrb_str_reverse(mrb_state *mrb, mrb_value str) { - char *s, *sbeg, *t; - struct RString *ps = mrb_str_ptr(str); - mrb_int len = RSTRING_LEN(sub); - - /* substring longer than string */ - if (RSTR_LEN(ps) < len) return -1; - if (RSTR_LEN(ps) - pos < len) { - pos = RSTR_LEN(ps) - len; - } - sbeg = RSTR_PTR(ps); - s = RSTR_PTR(ps) + pos; - t = RSTRING_PTR(sub); - if (len) { - while (sbeg <= s) { - if (memcmp(s, t, len) == 0) { - return s - RSTR_PTR(ps); - } - s--; - } - return -1; - } - else { - return pos; - } + mrb_value str2 = mrb_str_dup(mrb, str); + mrb_str_reverse_bang(mrb, str2); + return str2; } /* 15.2.10.5.31 */ @@ -1635,13 +1829,13 @@ mrb_str_rindex(mrb_state *mrb, mrb_value str, mrb_value sub, mrb_int pos) * "hello".rindex(/[aeiou]/, -2) #=> 1 */ static mrb_value -mrb_str_rindex_m(mrb_state *mrb, mrb_value str) +mrb_str_rindex(mrb_state *mrb, mrb_value str) { mrb_value *argv; mrb_int argc; mrb_value sub; mrb_value vpos; - mrb_int pos, len = RSTRING_LEN(str); + mrb_int pos, len = RSTRING_CHAR_LEN(str); mrb_get_args(mrb, "*", &argv, &argc); if (argc == 2) { @@ -1651,7 +1845,7 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) if (pos < 0) { pos += len; if (pos < 0) { - regexp_check(mrb, sub); + mrb_regexp_check(mrb, sub); return mrb_nil_value(); } } @@ -1664,20 +1858,11 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) else sub = mrb_nil_value(); } - regexp_check(mrb, sub); + pos = chars2bytes(str, 0, pos); + len = chars2bytes(str, pos, len); + mrb_regexp_check(mrb, sub); switch (mrb_type(sub)) { - case MRB_TT_FIXNUM: { - int c = mrb_fixnum(sub); - mrb_int len = RSTRING_LEN(str); - unsigned char *p = (unsigned char*)RSTRING_PTR(str); - - for (pos=len-1;pos>=0;pos--) { - if (p[pos] == c) return mrb_fixnum_value(pos); - } - return mrb_nil_value(); - } - default: { mrb_value tmp; @@ -1689,8 +1874,11 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) } /* fall through */ case MRB_TT_STRING: - pos = mrb_str_rindex(mrb, str, sub, pos); - if (pos >= 0) return mrb_fixnum_value(pos); + pos = str_rindex(mrb, str, sub, pos); + if (pos >= 0) { + pos = bytes2chars(RSTRING_PTR(str), pos); + return mrb_fixnum_value(pos); + } break; } /* end of switch (TYPE(sub)) */ @@ -1701,7 +1889,7 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) /* * call-seq: - * str.split(pattern=$;, [limit]) => anArray + * str.split(pattern="\n", [limit]) => anArray * * Divides <i>str</i> into substrings based on a delimiter, returning an array * of these substrings. @@ -1717,7 +1905,7 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) * * 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. + * 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 @@ -1729,10 +1917,8 @@ mrb_str_rindex_m(mrb_state *mrb, mrb_value str) * " now's the time".split #=> ["now's", "the", "time"] * " now's the time".split(' ') #=> ["now's", "the", "time"] * " now's the time".split(/ /) #=> ["", "now's", "", "the", "time"] - * "1, 2.34,56, 7".split(%r{,\s*}) #=> ["1", "2.34", "56", "7"] * "hello".split(//) #=> ["h", "e", "l", "l", "o"] * "hello".split(//, 3) #=> ["h", "e", "llo"] - * "hi mom".split(%r{\s*}) #=> ["h", "i", "m", "o", "m"] * * "mellow yellow".split("ello") #=> ["m", "w y", "w"] * "1,2,,3,4,,".split(',') #=> ["1", "2", "", "3", "4"] @@ -1774,83 +1960,73 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) } } else { - noregexp(mrb, str); + mrb_noregexp(mrb, str); } } result = mrb_ary_new(mrb); beg = 0; if (split_type == awk) { - char *ptr = RSTRING_PTR(str); - char *eptr = RSTRING_END(str); - char *bptr = ptr; mrb_bool skip = TRUE; + mrb_int idx = 0; + mrb_int str_len = RSTRING_LEN(str); unsigned int c; + int ai = mrb_gc_arena_save(mrb); - end = beg; - while (ptr < eptr) { - int ai = mrb_gc_arena_save(mrb); - c = (unsigned char)*ptr++; + idx = end = beg; + while (idx < str_len) { + c = (unsigned char)RSTRING_PTR(str)[idx++]; if (skip) { if (ISSPACE(c)) { - beg = ptr - bptr; + beg = idx; } else { - end = ptr - bptr; + end = idx; skip = FALSE; if (lim_p && lim <= i) break; } } else if (ISSPACE(c)) { - mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, beg, end-beg)); + mrb_ary_push(mrb, result, byte_subseq(mrb, str, beg, end-beg)); mrb_gc_arena_restore(mrb, ai); skip = TRUE; - beg = ptr - bptr; + beg = idx; if (lim_p) ++i; } else { - end = ptr - bptr; + end = idx; } } } else if (split_type == string) { - char *ptr = RSTRING_PTR(str); // s->as.ary - char *temp = ptr; - char *eptr = RSTRING_END(str); - mrb_int slen = RSTRING_LEN(spat); - - if (slen == 0) { - int ai = mrb_gc_arena_save(mrb); - while (ptr < eptr) { - mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, ptr-temp, 1)); - mrb_gc_arena_restore(mrb, ai); - ptr++; - if (lim_p && lim <= ++i) break; + 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; } - else { - char *sptr = RSTRING_PTR(spat); - int ai = mrb_gc_arena_save(mrb); - - while (ptr < eptr && - (end = mrb_memsearch(sptr, slen, ptr, eptr - ptr)) >= 0) { - mrb_ary_push(mrb, result, mrb_str_subseq(mrb, str, ptr - temp, end)); - mrb_gc_arena_restore(mrb, ai); - ptr += end + slen; - if (lim_p && lim <= ++i) break; - } - } - beg = ptr - temp; + beg = idx; } else { - noregexp(mrb, str); + 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 = mrb_str_subseq(mrb, str, beg, RSTRING_LEN(str)-beg); + tmp = byte_subseq(mrb, str, beg, RSTRING_LEN(str)-beg); } mrb_ary_push(mrb, result, tmp); } @@ -1864,44 +2040,39 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) return result; } -mrb_value -mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) +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; + const char *p = str; + const char *pend = str + len; char sign = 1; - int c, uscore; - unsigned long n = 0; + int c; + uint64_t n = 0; mrb_int val; -#undef ISDIGIT -#define ISDIGIT(c) ('0' <= (c) && (c) <= '9') #define conv_digit(c) \ - (!ISASCII(c) ? -1 : \ - isdigit(c) ? ((c) - '0') : \ - islower(c) ? ((c) - 'a' + 10) : \ - isupper(c) ? ((c) - 'A' + 10) : \ + (ISDIGIT(c) ? ((c) - '0') : \ + ISLOWER(c) ? ((c) - 'a' + 10) : \ + ISUPPER(c) ? ((c) - 'A' + 10) : \ -1) - if (!str) { + if (!p) { if (badcheck) goto bad; return mrb_fixnum_value(0); } - while (ISSPACE(*str)) str++; + while (p<pend && ISSPACE(*p)) + p++; - if (str[0] == '+') { - str++; + if (p[0] == '+') { + p++; } - else if (str[0] == '-') { - str++; + else if (p[0] == '-') { + p++; sign = 0; } - if (str[0] == '+' || str[0] == '-') { - if (badcheck) goto bad; - return mrb_fixnum_value(0); - } if (base <= 0) { - if (str[0] == '0') { - switch (str[1]) { + if (p[0] == '0') { + switch (p[1]) { case 'x': case 'X': base = 16; break; @@ -1916,6 +2087,7 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) break; default: base = 8; + break; } } else if (base < -1) { @@ -1927,27 +2099,27 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) } switch (base) { case 2: - if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) { - str += 2; + if (p[0] == '0' && (p[1] == 'b'||p[1] == 'B')) { + p += 2; } break; case 3: break; case 8: - if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) { - str += 2; + if (p[0] == '0' && (p[1] == 'o'||p[1] == 'O')) { + p += 2; } case 4: case 5: case 6: case 7: break; case 10: - if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) { - str += 2; + if (p[0] == '0' && (p[1] == 'd'||p[1] == 'D')) { + p += 2; } case 9: case 11: case 12: case 13: case 14: case 15: break; case 16: - if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) { - str += 2; + if (p[0] == '0' && (p[1] == 'x'||p[1] == 'X')) { + p += 2; } break; default: @@ -1956,96 +2128,109 @@ mrb_cstr_to_inum(mrb_state *mrb, const char *str, int base, int badcheck) } break; } /* end of switch (base) { */ - if (*str == '0') { /* squeeze preceeding 0s */ - uscore = 0; - while ((c = *++str) == '0' || c == '_') { + if (p>=pend) { + if (badcheck) goto bad; + return mrb_fixnum_value(0); + } + if (*p == '0') { /* squeeze preceding 0s */ + p++; + while (p<pend) { + c = *p++; if (c == '_') { - if (++uscore >= 2) + if (p<pend && *p == '_') { + if (badcheck) goto bad; break; + } + continue; + } + if (c != '0') { + p--; + break; } - else - uscore = 0; } - if (!(c = *str) || ISSPACE(c)) --str; } - c = *str; - c = conv_digit(c); - if (c < 0 || c >= base) { + if (p == pend) { if (badcheck) goto bad; return mrb_fixnum_value(0); } - - uscore = 0; - for (p=str;*p;p++) { + for ( ;p<pend;p++) { if (*p == '_') { - if (uscore == 0) { - uscore++; + p++; + if (p==pend) { + if (badcheck) goto bad; continue; } - if (badcheck) goto bad; - break; + if (*p == '_') { + if (badcheck) goto bad; + break; + } + } + if (badcheck && *p == '\0') { + goto nullbyte; } - uscore = 0; c = conv_digit(*p); if (c < 0 || c >= base) { - if (badcheck) goto bad; break; } n *= base; 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)); + } } - if (n > MRB_INT_MAX) { - mrb_raisef(mrb, E_ARGUMENT_ERROR, "string (%S) too big for integer", mrb_str_new_cstr(mrb, str)); - } - val = n; + val = (mrb_int)n; if (badcheck) { if (p == str) goto bad; /* no number */ - while (*p && ISSPACE(*p)) p++; - if (*p) goto bad; /* trailing garbage */ + while (p<pend && ISSPACE(*p)) p++; + if (p<pend) goto bad; /* trailing garbage */ } return mrb_fixnum_value(sign ? val : -val); -bad: - mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid string for number(%S)", mrb_str_new_cstr(mrb, str)); + 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); } -char * +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) { - struct RString *ps = mrb_str_ptr(*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 (RSTR_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_value +MRB_API mrb_value mrb_str_to_inum(mrb_state *mrb, mrb_value str, mrb_int base, mrb_bool badcheck) { - char *s; + const char *s; mrb_int len; - str = mrb_str_to_str(mrb, str); - if (badcheck) { - s = mrb_string_value_cstr(mrb, &str); - } - else { - s = RSTRING_PTR(str); - } - if (s) { - len = RSTRING_LEN(str); - if (s[len]) { /* no sentinel somehow */ - struct RString *temp_str = str_new(mrb, s, len); - s = RSTR_PTR(temp_str); - } - } - return mrb_cstr_to_inum(mrb, s, base, badcheck); + 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 */ @@ -2081,16 +2266,14 @@ mrb_str_to_i(mrb_state *mrb, mrb_value self) return mrb_str_to_inum(mrb, self, base, FALSE); } -double +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}; -#define OutOfRange() (((w = end - p) > max_width) ? \ - (w = max_width, ellipsis = "...") : \ - (w = (int)(end - p), ellipsis = "")) if (!p) return 0.0; while (ISSPACE(*p)) p++; @@ -2108,7 +2291,6 @@ bad: return d; } if (*end) { - char buf[DBL_DIG * 4 + 10]; char *n = buf; char *e = buf + sizeof(buf) - 1; char prev = 0; @@ -2147,7 +2329,7 @@ bad: return d; } -double +MRB_API double mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck) { char *s; @@ -2242,7 +2424,7 @@ mrb_str_upcase_bang(mrb_state *mrb, mrb_value str) * * Returns a copy of <i>str</i> with all lowercase letters replaced with their * uppercase counterparts. The operation is locale insensitive---only - * characters ``a'' to ``z'' are affected. + * characters 'a' to 'z' are affected. * * "hEllO".upcase #=> "HELLO" */ @@ -2377,30 +2559,30 @@ mrb_str_dump(mrb_state *mrb, mrb_value str) return mrb_obj_value(result); } -mrb_value +MRB_API mrb_value mrb_str_cat(mrb_state *mrb, mrb_value str, const char *ptr, size_t len) { str_buf_cat(mrb, mrb_str_ptr(str), ptr, len); return str; } -mrb_value +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_value +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_value -mrb_str_append(mrb_state *mrb, mrb_value str, mrb_value str2) +MRB_API mrb_value +mrb_str_append(mrb_state *mrb, mrb_value str1, mrb_value str2) { str2 = mrb_str_to_str(mrb, str2); - return mrb_str_cat_str(mrb, str, str2); + return mrb_str_cat_str(mrb, str1, str2); } #define CHAR_ESC_LEN 13 /* sizeof(\x{ hex of 32bit unsigned int } \0) */ @@ -2426,7 +2608,21 @@ mrb_str_inspect(mrb_state *mrb, mrb_value str) 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, pend))) { buf[0] = '\\'; buf[1] = c; @@ -2500,7 +2696,7 @@ mrb_init_string(mrb_state *mrb) mrb_static_assert(RSTRING_EMBED_LEN_MAX < (1 << 5), "pointer size too big for embedded string"); - s = mrb->string_class = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */ + mrb->string_class = s = mrb_define_class(mrb, "String", mrb->object_class); /* 15.2.10 */ MRB_SET_INSTANCE_TT(s, MRB_TT_STRING); mrb_define_method(mrb, s, "bytesize", mrb_str_bytesize, MRB_ARGS_NONE()); @@ -2511,7 +2707,7 @@ mrb_init_string(mrb_state *mrb) 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_REQ(1)); /* 15.2.10.5.8 */ + mrb_define_method(mrb, s, "capitalize!", mrb_str_capitalize_bang, MRB_ARGS_NONE()); /* 15.2.10.5.8 */ mrb_define_method(mrb, s, "chomp", mrb_str_chomp, MRB_ARGS_ANY()); /* 15.2.10.5.9 */ mrb_define_method(mrb, s, "chomp!", mrb_str_chomp_bang, MRB_ARGS_ANY()); /* 15.2.10.5.10 */ mrb_define_method(mrb, s, "chop", mrb_str_chop, MRB_ARGS_REQ(1)); /* 15.2.10.5.11 */ @@ -2521,9 +2717,9 @@ mrb_init_string(mrb_state *mrb) 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_REQ(1)); /* 15.2.10.5.20 */ + mrb_define_method(mrb, s, "hash", mrb_str_hash_m, MRB_ARGS_NONE()); /* 15.2.10.5.20 */ mrb_define_method(mrb, s, "include?", mrb_str_include, MRB_ARGS_REQ(1)); /* 15.2.10.5.21 */ - mrb_define_method(mrb, s, "index", mrb_str_index_m, MRB_ARGS_ANY()); /* 15.2.10.5.22 */ + mrb_define_method(mrb, s, "index", mrb_str_index, 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 */ @@ -2531,7 +2727,7 @@ mrb_init_string(mrb_state *mrb) mrb_define_method(mrb, s, "replace", mrb_str_replace, MRB_ARGS_REQ(1)); /* 15.2.10.5.28 */ mrb_define_method(mrb, s, "reverse", mrb_str_reverse, MRB_ARGS_NONE()); /* 15.2.10.5.29 */ mrb_define_method(mrb, s, "reverse!", mrb_str_reverse_bang, MRB_ARGS_NONE()); /* 15.2.10.5.30 */ - mrb_define_method(mrb, s, "rindex", mrb_str_rindex_m, MRB_ARGS_ANY()); /* 15.2.10.5.31 */ + mrb_define_method(mrb, s, "rindex", mrb_str_rindex, MRB_ARGS_ANY()); /* 15.2.10.5.31 */ mrb_define_method(mrb, s, "size", mrb_str_size, MRB_ARGS_NONE()); /* 15.2.10.5.33 */ mrb_define_method(mrb, s, "slice", mrb_str_aref_m, MRB_ARGS_ANY()); /* 15.2.10.5.34 */ mrb_define_method(mrb, s, "split", mrb_str_split_m, MRB_ARGS_ANY()); /* 15.2.10.5.35 */ @@ -2541,8 +2737,10 @@ mrb_init_string(mrb_state *mrb) 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_REQ(1)); /* 15.2.10.5.42 */ - mrb_define_method(mrb, s, "upcase!", mrb_str_upcase_bang, MRB_ARGS_REQ(1)); /* 15.2.10.5.43 */ + 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()); + + mrb_define_method(mrb, s, "freeze", mrb_str_freeze, MRB_ARGS_NONE()); } diff --git a/src/symbol.c b/src/symbol.c index 148adc6fe..c39e88012 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -4,11 +4,12 @@ ** See Copyright Notice in mruby.h */ -#include <ctype.h> +#include <limits.h> #include <string.h> -#include "mruby.h" -#include "mruby/khash.h" -#include "mruby/string.h" +#include <mruby.h> +#include <mruby/khash.h> +#include <mruby/string.h> +#include <mruby/dump.h> /* ------------------------------------------------------ */ typedef struct symbol_name { @@ -18,148 +19,153 @@ typedef struct symbol_name { } symbol_name; static inline khint_t -sym_hash_func(mrb_state *mrb, const symbol_name s) +sym_hash_func(mrb_state *mrb, mrb_sym s) { khint_t h = 0; - size_t i; - const char *p = s.name; + size_t i, len = mrb->symtbl[s].len; + const char *p = mrb->symtbl[s].name; - for (i=0; i<s.len; i++) { + for (i=0; i<len; i++) { h = (h << 5) - h + *p++; } return h; } -#define sym_hash_equal(mrb,a, b) (a.len == b.len && memcmp(a.name, b.name, a.len) == 0) +#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, symbol_name, mrb_sym, TRUE) -KHASH_DEFINE (n2s, symbol_name, mrb_sym, TRUE, sym_hash_func, sym_hash_equal) +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; + symbol_name *sname = mrb->symtbl; /* symtbl[0] for working memory */ khiter_t k; mrb_sym sym; char *p; - if (len > (UINT16_MAX-1)) { /* UINT16_MAX is reverved */ - mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long"); + 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); } - sname.lit = lit; - sname.len = (uint16_t)len; - sname.name = name; - k = kh_get(n2s, mrb, h, sname); - if (k != kh_end(h)) - return kh_value(h, k); + /* registering a new symbol */ sym = ++mrb->symidx; - if (lit) { - sname.name = name; + 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->name = (const char*)p; + sname->lit = FALSE; } - k = kh_put(n2s, mrb, h, sname); - kh_value(h, k) = sym; + kh_put(n2s, mrb, h, sym); return sym; } -mrb_sym +MRB_API mrb_sym mrb_intern(mrb_state *mrb, const char *name, size_t len) { return sym_intern(mrb, name, len, FALSE); } -mrb_sym +MRB_API mrb_sym mrb_intern_static(mrb_state *mrb, const char *name, size_t len) { return sym_intern(mrb, name, len, TRUE); } -mrb_sym +MRB_API mrb_sym mrb_intern_cstr(mrb_state *mrb, const char *name) { return mrb_intern(mrb, name, strlen(name)); } -mrb_sym +MRB_API mrb_sym mrb_intern_str(mrb_state *mrb, mrb_value str) { return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str)); } -mrb_value +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 = { 0 }; + symbol_name *sname = mrb->symtbl; khiter_t k; - if (len > UINT16_MAX) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long"); - } - sname.len = (uint16_t)len; - sname.name = name; + sym_validate_len(mrb, len); + sname->len = (uint16_t)len; + sname->name = name; - k = kh_get(n2s, mrb, h, sname); + k = kh_get(n2s, mrb, h, 0); if (k != kh_end(h)) { - return mrb_symbol_value(kh_value(h, k)); + return mrb_symbol_value(kh_key(h, k)); } return mrb_nil_value(); } -mrb_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_value +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 */ -const char* +MRB_API const char* mrb_sym2name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp) { - khash_t(n2s) *h = mrb->name2sym; - khiter_t k; - symbol_name sname; - - for (k = kh_begin(h); k != kh_end(h); k++) { - if (kh_exist(h, k)) { - if (kh_value(h, k) == sym) { - sname = kh_key(h, k); - if (lenp) *lenp = sname.len; - return sname.name; - } - } + if (sym == 0 || mrb->symidx < sym) { + if (lenp) *lenp = 0; + return NULL; } - if (lenp) *lenp = 0; - return NULL; /* missing */ + + if (lenp) *lenp = mrb->symtbl[sym].len; + return mrb->symtbl[sym].name; } void mrb_free_symtbl(mrb_state *mrb) { - khash_t(n2s) *h = mrb->name2sym; - khiter_t k; - - for (k = kh_begin(h); k != kh_end(h); k++) - if (kh_exist(h, k)) { - symbol_name s = kh_key(h, k); + mrb_sym i, lim; - if (!s.lit) { - mrb_free(mrb, (char*)s.name); - } + 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); } @@ -216,12 +222,10 @@ static mrb_value sym_equal(mrb_state *mrb, mrb_value sym1) { mrb_value sym2; - mrb_bool equal_p; mrb_get_args(mrb, "o", &sym2); - equal_p = mrb_obj_equal(mrb, sym1, sym2); - return mrb_bool_value(equal_p); + return mrb_bool_value(mrb_obj_equal(mrb, sym1, sym2)); } /* 15.2.11.3.2 */ @@ -235,7 +239,7 @@ sym_equal(mrb_state *mrb, mrb_value sym1) * * :fred.id2name #=> "fred" */ -mrb_value +static mrb_value mrb_sym_to_s(mrb_state *mrb, mrb_value sym) { mrb_sym id = mrb_symbol(sym); @@ -411,7 +415,7 @@ sym_inspect(mrb_state *mrb, mrb_value sym) return str; } -mrb_value +MRB_API mrb_value mrb_sym2str(mrb_state *mrb, mrb_sym sym) { mrb_int len; @@ -421,7 +425,7 @@ mrb_sym2str(mrb_state *mrb, mrb_sym sym) return mrb_str_new_static(mrb, name, len); } -const char* +MRB_API const char* mrb_sym2name(mrb_state *mrb, mrb_sym sym) { mrb_int len; @@ -474,7 +478,7 @@ mrb_init_symbol(mrb_state *mrb) { struct RClass *sym; - sym = mrb->symbol_class = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */ + mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */ mrb_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 */ diff --git a/src/value_array.h b/src/value_array.h index cabd2426d..bc5f28b06 100644 --- a/src/value_array.h +++ b/src/value_array.h @@ -1,7 +1,7 @@ #ifndef MRB_VALUE_ARRAY_H__ #define MRB_VALUE_ARRAY_H__ -#include "mruby.h" +#include <mruby.h> static inline void value_move(mrb_value *s1, const mrb_value *s2, size_t n) diff --git a/src/variable.c b/src/variable.c index 74bb591cf..bda7b2a98 100644 --- a/src/variable.c +++ b/src/variable.c @@ -4,12 +4,11 @@ ** See Copyright Notice in mruby.h */ -#include <ctype.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/class.h" -#include "mruby/proc.h" -#include "mruby/string.h" +#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*); @@ -283,7 +282,7 @@ iv_free(mrb_state *mrb, iv_tbl *t) #else -#include "mruby/khash.h" +#include <mruby/khash.h> #ifndef MRB_IVHASH_INIT_SIZE #define MRB_IVHASH_INIT_SIZE 8 @@ -459,13 +458,14 @@ obj_iv_p(mrb_value obj) case MRB_TT_SCLASS: case MRB_TT_HASH: case MRB_TT_DATA: + case MRB_TT_EXCEPTION: return TRUE; default: return FALSE; } } -mrb_value +MRB_API mrb_value mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym) { mrb_value v; @@ -475,7 +475,7 @@ mrb_obj_iv_get(mrb_state *mrb, struct RObject *obj, mrb_sym sym) return mrb_nil_value(); } -mrb_value +MRB_API mrb_value mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (obj_iv_p(obj)) { @@ -484,7 +484,7 @@ mrb_iv_get(mrb_state *mrb, mrb_value obj, mrb_sym sym) return mrb_nil_value(); } -void +MRB_API void mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { iv_tbl *t = obj->iv; @@ -496,7 +496,7 @@ mrb_obj_iv_set(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) iv_put(mrb, t, sym, v); } -void +MRB_API void mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) { iv_tbl *t = obj->iv; @@ -511,7 +511,7 @@ mrb_obj_iv_ifnone(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v) iv_put(mrb, t, sym, v); } -void +MRB_API void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v) { if (obj_iv_p(obj)) { @@ -522,7 +522,7 @@ mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v) } } -mrb_bool +MRB_API mrb_bool mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym) { iv_tbl *t; @@ -534,14 +534,40 @@ mrb_obj_iv_defined(mrb_state *mrb, struct RObject *obj, mrb_sym sym) return FALSE; } -mrb_bool +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); } -void +#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); @@ -552,6 +578,7 @@ mrb_iv_copy(mrb_state *mrb, mrb_value dest, mrb_value src) d->iv = 0; } if (s->iv) { + mrb_write_barrier(mrb, (struct RBasic*)d); d->iv = iv_copy(mrb, s->iv); } } @@ -582,7 +609,7 @@ inspect_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p) else { ins = mrb_inspect(mrb, v); } - mrb_str_append(mrb, str, ins); + mrb_str_cat_str(mrb, str, ins); return 0; } @@ -608,7 +635,7 @@ mrb_obj_iv_inspect(mrb_state *mrb, struct RObject *obj) return mrb_any_to_s(mrb, mrb_obj_value(obj)); } -mrb_value +MRB_API mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym) { if (obj_iv_p(obj)) { @@ -728,34 +755,46 @@ mrb_mod_class_variables(mrb_state *mrb, mrb_value mod) return ary; } -mrb_value +MRB_API mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym) { struct RClass * cls = c; + mrb_value v; while (c) { - if (c->iv) { - iv_tbl *t = c->iv; - mrb_value v; - - if (iv_get(mrb, t, sym, &v)) - return v; + if (c->iv && iv_get(mrb, c->iv, sym, &v)) { + return v; } 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__")); + c = mrb_class_ptr(klass); + if (c->tt == MRB_TT_CLASS) { + while (c) { + if (c->iv && iv_get(mrb, c->iv, sym, &v)) { + return v; + } + c = c->super; + } + } + } 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_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); } -void +MRB_API void mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) { struct RClass * cls = c; @@ -781,13 +820,13 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v) iv_put(mrb, cls->iv, sym, v); } -void +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_bool +MRB_API mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym) { while (c) { @@ -801,7 +840,7 @@ mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym) return FALSE; } -mrb_bool +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); @@ -826,16 +865,6 @@ mrb_vm_cv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) mrb_mod_cv_set(mrb, c, sym, v); } -mrb_bool -mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym sym) -{ - struct RClass *m = mrb_class_ptr(mod); - iv_tbl *t = m->iv; - - if (!t) return FALSE; - return iv_get(mrb, t, sym, NULL); -} - static void mod_const_check(mrb_state *mrb, mrb_value mod) { @@ -856,7 +885,7 @@ const_get(mrb_state *mrb, struct RClass *base, mrb_sym sym) struct RClass *c = base; mrb_value v; iv_tbl *t; - mrb_bool retry = 0; + mrb_bool retry = FALSE; mrb_value name; L_RETRY: @@ -870,14 +899,14 @@ L_RETRY: } if (!retry && base && base->tt == MRB_TT_MODULE) { c = mrb->object_class; - retry = 1; + 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_value +MRB_API mrb_value mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym) { mod_const_check(mrb, mod); @@ -897,6 +926,14 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) if (c->iv && iv_get(mrb, c->iv, sym, &v)) { return v; } + if (c->tt == MRB_TT_SCLASS) { + mrb_value klass; + klass = mrb_obj_iv_get(mrb, (struct RObject *)c, + mrb_intern_lit(mrb, "__attached__")); + c2 = mrb_class_ptr(klass); + if (c2->tt == MRB_TT_CLASS) + c = c2; + } c2 = c; for (;;) { c2 = mrb_class_outer_module(mrb, c2); @@ -909,14 +946,14 @@ mrb_vm_const_get(mrb_state *mrb, mrb_sym sym) return const_get(mrb, c, sym); } -void +MRB_API void mrb_const_set(mrb_state *mrb, mrb_value mod, mrb_sym sym, mrb_value v) { mod_const_check(mrb, mod); mrb_iv_set(mrb, mod, sym, v); } - void +void mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { struct RClass *c = mrb->c->ci->proc->target_class; @@ -925,20 +962,20 @@ mrb_vm_const_set(mrb_state *mrb, mrb_sym sym, mrb_value v) mrb_obj_iv_set(mrb, (struct RObject*)c, sym, v); } -void +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); } -void +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); } -void +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); @@ -986,7 +1023,7 @@ mrb_mod_constants(mrb_state *mrb, mrb_value mod) return ary; } -mrb_value +MRB_API mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym) { mrb_value v; @@ -999,7 +1036,7 @@ mrb_gv_get(mrb_state *mrb, mrb_sym sym) return mrb_nil_value(); } -void +MRB_API void mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) { iv_tbl *t; @@ -1013,7 +1050,7 @@ mrb_gv_set(mrb_state *mrb, mrb_sym sym, mrb_value v) iv_put(mrb, t, sym, v); } -void +MRB_API void mrb_gv_remove(mrb_state *mrb, mrb_sym sym) { if (!mrb->globals) { @@ -1063,9 +1100,10 @@ mrb_f_global_variables(mrb_state *mrb, mrb_value self) } static mrb_bool -mrb_const_defined_0(mrb_state *mrb, struct RClass *klass, mrb_sym id, mrb_bool exclude, mrb_bool recurse) +mrb_const_defined_0(mrb_state *mrb, mrb_value mod, mrb_sym id, mrb_bool exclude, mrb_bool recurse) { - struct RClass * tmp; + struct RClass *klass = mrb_class_ptr(mod); + struct RClass *tmp; mrb_bool mod_retry = 0; tmp = klass; @@ -1085,13 +1123,19 @@ retry: return FALSE; } -int -mrb_const_defined_at(mrb_state *mrb, struct RClass *klass, mrb_sym id) +MRB_API mrb_bool +mrb_const_defined(mrb_state *mrb, mrb_value mod, mrb_sym id) { - return mrb_const_defined_0(mrb, klass, id, TRUE, FALSE); + return mrb_const_defined_0(mrb, mod, id, TRUE, TRUE); } -mrb_value +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); diff --git a/src/version.c b/src/version.c index 7aac44d62..5bcecb3aa 100644 --- a/src/version.c +++ b/src/version.c @@ -1,12 +1,16 @@ -#include "mruby.h" -#include "mruby/variable.h" +#include <mruby.h> +#include <mruby/variable.h> void mrb_init_version(mrb_state* mrb) { + mrb_value mruby_version = mrb_str_new_lit(mrb, MRUBY_VERSION); + mrb_define_global_const(mrb, "RUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_RUBY_VERSION)); mrb_define_global_const(mrb, "RUBY_ENGINE", mrb_str_new_lit(mrb, MRUBY_RUBY_ENGINE)); + mrb_define_global_const(mrb, "RUBY_ENGINE_VERSION", mruby_version); mrb_define_global_const(mrb, "MRUBY_VERSION", mrb_str_new_lit(mrb, 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)); @@ -1,4 +1,4 @@ - /* +/* ** vm.c - virtual machine for mruby ** ** See Copyright Notice in mruby.h @@ -7,22 +7,22 @@ #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 <mruby.h> +#include <mruby/array.h> +#include <mruby/class.h> +#include <mruby/hash.h> +#include <mruby/irep.h> +#include <mruby/numeric.h> +#include <mruby/proc.h> +#include <mruby/range.h> +#include <mruby/string.h> +#include <mruby/variable.h> +#include <mruby/error.h> +#include <mruby/opcode.h> #include "value_array.h" -#include "mrb_throw.h" +#include <mruby/throw.h> -#ifndef ENABLE_STDIO +#ifndef MRB_DISABLE_STDIO #if defined(__cplusplus) extern "C" { #endif @@ -32,20 +32,6 @@ void abort(void); #endif #endif -#define SET_TRUE_VALUE(r) MRB_SET_VALUE(r, MRB_TT_TRUE, value.i, 1) -#define SET_FALSE_VALUE(r) MRB_SET_VALUE(r, MRB_TT_FALSE, value.i, 1) -#define SET_NIL_VALUE(r) MRB_SET_VALUE(r, MRB_TT_FALSE, value.i, 0) -#define SET_INT_VALUE(r,n) MRB_SET_VALUE(r, MRB_TT_FIXNUM, value.i, (n)) -#define SET_SYM_VALUE(r,v) MRB_SET_VALUE(r, MRB_TT_SYMBOL, value.sym, (v)) -#define SET_OBJ_VALUE(r,v) MRB_SET_VALUE(r, (((struct RObject*)(v))->tt), value.p, (v)) -#ifdef MRB_NAN_BOXING -#define SET_FLT_VALUE(mrb,r,v) r.f = (v) -#elif defined(MRB_WORD_BOXING) -#define SET_FLT_VALUE(mrb,r,v) r = mrb_float_value(mrb, (v)) -#else -#define SET_FLT_VALUE(mrb,r,v) MRB_SET_VALUE(r, MRB_TT_FLOAT, value.f, (v)) -#endif - #define STACK_INIT_SIZE 128 #define CALLINFO_INIT_SIZE 32 @@ -66,10 +52,7 @@ The value below allows about 60000 recursive calls in the simplest case. */ # define DEBUG(x) #endif -#define TO_STR(x) TO_STR_(x) -#define TO_STR_(x) #x - -#define ARENA_RESTORE(mrb,ai) (mrb)->arena_idx = (ai) +#define ARENA_RESTORE(mrb,ai) (mrb)->gc.arena_idx = (ai) static inline void stack_clear(mrb_value *from, size_t count) @@ -174,7 +157,7 @@ stack_extend_alloc(mrb_state *mrb, int room, int keep) to prevent infinite recursion. However, do this only after resizing the stack, so mrb_raise has stack space to work with. */ if (size > MRB_STACK_MAX) { init_new_stack_space(mrb, room, keep); - mrb_raise(mrb, E_RUNTIME_ERROR, "stack level too deep. (limit=" TO_STR(MRB_STACK_MAX) ")"); + mrb_raise(mrb, E_SYSSTACK_ERROR, "stack level too deep. (limit=" MRB_STRINGIZE(MRB_STACK_MAX) ")"); } } @@ -237,14 +220,13 @@ cipush(mrb_state *mrb) int ridx = ci->ridx; if (ci + 1 == c->ciend) { - size_t size = ci - c->cibase; + ptrdiff_t size = ci - c->cibase; c->cibase = (mrb_callinfo *)mrb_realloc(mrb, c->cibase, sizeof(mrb_callinfo)*size*2); c->ci = c->cibase + size; c->ciend = c->cibase + size * 2; } ci = ++c->ci; - ci->nregs = 2; /* protect method_missing arg and block */ ci->eidx = eidx; ci->ridx = ridx; ci->env = 0; @@ -270,6 +252,7 @@ cipop(mrb_state *mrb) stack_copy(p, e->stack, len); } e->stack = p; + mrb_write_barrier(mrb, (struct RBasic *)e); } c->ci--; @@ -283,6 +266,7 @@ ecall(mrb_state *mrb, int i) mrb_value *self = mrb->c->stack; struct RObject *exc; + if (i<0) return; p = mrb->c->ensure[i]; if (!p) return; if (mrb->c->ci->eidx > i) @@ -306,49 +290,34 @@ ecall(mrb_state *mrb, int i) #define MRB_FUNCALL_ARGC_MAX 16 #endif -mrb_value +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 == 0) { - return mrb_funcall_argv(mrb, self, mid, 0, 0); - } - else if (argc == 1) { - mrb_value v; - va_list ap; - - va_start(ap, argc); - v = va_arg(ap, mrb_value); - va_end(ap); - return mrb_funcall_argv(mrb, self, mid, 1, &v); + if (argc > MRB_FUNCALL_ARGC_MAX) { + mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" MRB_STRINGIZE(MRB_FUNCALL_ARGC_MAX) ")"); } - else { - mrb_value argv[MRB_FUNCALL_ARGC_MAX]; - va_list ap; - mrb_int i; - - if (argc > MRB_FUNCALL_ARGC_MAX) { - mrb_raise(mrb, E_ARGUMENT_ERROR, "Too long arguments. (limit=" TO_STR(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); + 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_value +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; - mrb_callinfo *old_ci = mrb->c->ci; + ptrdiff_t nth_ci = mrb->c->ci - mrb->c->cibase; MRB_TRY(&c_jmp) { mrb->jmp = &c_jmp; @@ -357,7 +326,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc mrb->jmp = 0; } MRB_CATCH(&c_jmp) { /* error */ - while (old_ci != mrb->c->ci) { + while (nth_ci < (mrb->c->ci - mrb->c->cibase)) { mrb->c->stack = mrb->c->ci->stackent; cipop(mrb); } @@ -372,6 +341,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc mrb_sym undef = 0; mrb_callinfo *ci; int n; + ptrdiff_t voff = -1; if (!mrb->c->stack) { stack_init(mrb); @@ -395,6 +365,9 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc 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, 0); @@ -403,6 +376,9 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc ci->nregs = p->body.irep->nregs + n; stack_extend(mrb, ci->nregs, argc+2); } + if (voff >= 0) { + argv = mrb->c->stbase + voff; + } mrb->c->stack[0] = self; if (undef) { mrb->c->stack[1] = mrb_symbol_value(undef); @@ -433,7 +409,7 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc return val; } -mrb_value +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()); @@ -458,7 +434,7 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons * k = Klass.new * k.send :hello, "gentle", "readers" #=> "Hello gentle readers" */ -mrb_value +MRB_API mrb_value mrb_f_send(mrb_state *mrb, mrb_value self) { mrb_sym name; @@ -497,8 +473,16 @@ mrb_f_send(mrb_state *mrb, mrb_value self) return p->body.func(mrb, self); } + if (ci->argc < 0) { + stack_extend(mrb, (p->body.irep->nregs < 3) ? 3 : p->body.irep->nregs, 3); + } + else { + stack_extend(mrb, p->body.irep->nregs, ci->argc+2); + } + ci->nregs = p->body.irep->nregs; ci = cipush(mrb); + ci->nregs = 0; ci->target_class = 0; ci->pc = p->body.irep->iseq; ci->stackent = mrb->c->stack; @@ -513,6 +497,9 @@ 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) { return mrb_yield_with_class(mrb, blk, 0, 0, self, c); @@ -525,6 +512,7 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) } ci->nregs = p->body.irep->nregs; ci = cipush(mrb); + ci->nregs = 0; ci->target_class = 0; ci->pc = p->body.irep->iseq; ci->stackent = mrb->c->stack; @@ -599,7 +587,7 @@ mrb_obj_instance_eval(mrb_state *mrb, mrb_value self) return eval_under(mrb, self, b, c); } -mrb_value +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; @@ -646,7 +634,7 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value return val; } -mrb_value +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); @@ -654,7 +642,7 @@ mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv) return mrb_yield_with_class(mrb, b, argc, argv, p->env->stack[0], p->target_class); } -mrb_value +MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg) { struct RProc *p = mrb_proc_ptr(b); @@ -705,7 +693,7 @@ argnum_error(mrb_state *mrb, mrb_int num) #define ERR_PC_SET(mrb, pc) mrb->c->ci->err = pc; #define ERR_PC_CLR(mrb) mrb->c->ci->err = 0; -#ifdef ENABLE_DEBUG +#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) @@ -734,12 +722,11 @@ argnum_error(mrb_state *mrb, mrb_int num) #endif -mrb_value mrb_gv_val_get(mrb_state *mrb, mrb_sym sym); -void mrb_gv_val_set(mrb_state *mrb, mrb_sym sym, mrb_value val); - #define CALL_MAXARGS 127 -mrb_value +void mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args); + +MRB_API mrb_value mrb_context_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int stack_keep) { /* mrb_assert(mrb_proc_cfunc_p(proc)) */ @@ -1046,7 +1033,7 @@ RETRY_TRY_BLOCK: mrb_callinfo *ci = mrb->c->ci; int n, eidx = ci->eidx; - for (n=0; n<a && eidx > ci[-1].eidx; n++) { + for (n=0; n<a && (ci == mrb->c->cibase || eidx > ci[-1].eidx); n++) { ecall(mrb, --eidx); ARENA_RESTORE(mrb, ai); } @@ -1090,9 +1077,21 @@ RETRY_TRY_BLOCK: 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"); - mid = mrb_intern_lit(mrb, "method_missing"); - m = mrb_method_search_vm(mrb, &c, mid); + 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); + } + mrb_method_missing(mrb, mid, recv, args); + } + mid = missing; if (n == CALL_MAXARGS) { mrb_ary_unshift(mrb, regs[a+1], sym); } @@ -1107,12 +1106,7 @@ RETRY_TRY_BLOCK: ci->mid = mid; ci->proc = m; ci->stackent = mrb->c->stack; - if (c->tt == MRB_TT_ICLASS) { - ci->target_class = c->c; - } - else { - ci->target_class = c; - } + ci->target_class = c; ci->pc = pc + 1; ci->acc = a; @@ -1243,6 +1237,13 @@ RETRY_TRY_BLOCK: int a = GETARG_A(i); int n = GETARG_C(i); + if (mid == 0) { + mrb_value exc; + + exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); + mrb->exc = mrb_obj_ptr(exc); + goto L_RAISE; + } recv = regs[0]; c = mrb->c->ci->target_class->super; m = mrb_method_search_vm(mrb, &c, mid); @@ -1277,6 +1278,12 @@ RETRY_TRY_BLOCK: mrb->c->stack[0] = recv; if (MRB_PROC_CFUNC_P(m)) { + if (n == CALL_MAXARGS) { + ci->nregs = 3; + } + else { + ci->nregs = n + 2; + } mrb->c->stack[0] = m->body.func(mrb, recv); mrb_gc_arena_restore(mrb, ai); if (mrb->exc) goto L_RAISE; @@ -1322,6 +1329,7 @@ RETRY_TRY_BLOCK: struct REnv *e = uvenv(mrb, lv-1); if (!e) { mrb_value exc; + exc = mrb_exc_new_str_lit(mrb, E_NOMETHOD_ERROR, "super called outside of method"); mrb->exc = mrb_obj_ptr(exc); goto L_RAISE; @@ -1364,14 +1372,14 @@ RETRY_TRY_BLOCK: /* 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 = (ax>>18)&0x1f; - int o = (ax>>13)&0x1f; - int r = (ax>>12)&0x1; - int m2 = (ax>>7)&0x1f; + 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 = (ax>>2)&0x1f; - int kd = (ax>>1)&0x1; - int b = (ax>>0)& 0x1; + 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; @@ -1478,9 +1486,6 @@ RETRY_TRY_BLOCK: if (ci->ridx == 0) goto L_STOP; goto L_RESCUE; } - while (eidx > ci[-1].eidx) { - ecall(mrb, --eidx); - } while (ci[0].ridx == ci[-1].ridx) { cipop(mrb); ci = mrb->c->ci; @@ -1489,12 +1494,10 @@ RETRY_TRY_BLOCK: mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } - if (ci > mrb->c->cibase) { - while (eidx > ci[-1].eidx) { + if (ci == mrb->c->cibase) { + while (eidx > 0) { ecall(mrb, --eidx); } - } - else if (ci == mrb->c->cibase) { if (ci->ridx == 0) { if (mrb->c == mrb->root_c) { regs = mrb->c->stack = mrb->c->stbase; @@ -1510,6 +1513,12 @@ RETRY_TRY_BLOCK: } break; } + /* call ensure only when we skip this callinfo */ + if (ci[0].ridx == ci[-1].ridx) { + while (eidx > ci[-1].eidx) { + ecall(mrb, --eidx); + } + } } L_RESCUE: if (ci->ridx == 0) goto L_STOP; @@ -1540,6 +1549,7 @@ RETRY_TRY_BLOCK: localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } + mrb->c->stack = mrb->c->ci->stackent; mrb->c->ci = ci; break; } @@ -1574,6 +1584,7 @@ RETRY_TRY_BLOCK: c->prev = 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) { if (ci[-1].acc == CI_ACC_SKIP) { @@ -1695,18 +1706,9 @@ RETRY_TRY_BLOCK: NEXT; } -#define attr_i value.i -#ifdef MRB_NAN_BOXING -#define attr_f f -#elif defined(MRB_WORD_BOXING) -#define attr_f value.fp->f -#else -#define attr_f value.f -#endif - #define TYPES2(a,b) ((((uint16_t)(a))<<8)|(((uint16_t)(b))&0xff)) #define OP_MATH_BODY(op,v1,v2) do {\ - regs[a].v1 = regs[a].v1 op regs[a+1].v2;\ + v1(regs[a]) = v1(regs[a]) op v2(regs[a+1]);\ } while(0) CASE(OP_ADD) { @@ -1723,7 +1725,7 @@ RETRY_TRY_BLOCK: x = mrb_fixnum(regs_a[0]); y = mrb_fixnum(regs_a[1]); if (mrb_int_add_overflow(x, y, &z)) { - SET_FLT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y); + SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x + (mrb_float)y); break; } SET_INT_VALUE(regs[a], z); @@ -1733,7 +1735,7 @@ RETRY_TRY_BLOCK: { mrb_int x = mrb_fixnum(regs[a]); mrb_float y = mrb_float(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], (mrb_float)x + y); + SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): @@ -1741,10 +1743,10 @@ RETRY_TRY_BLOCK: { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], x + y); + SET_FLOAT_VALUE(mrb, regs[a], x + y); } #else - OP_MATH_BODY(+,attr_f,attr_i); + OP_MATH_BODY(+,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): @@ -1752,10 +1754,10 @@ RETRY_TRY_BLOCK: { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], x + y); + SET_FLOAT_VALUE(mrb, regs[a], x + y); } #else - OP_MATH_BODY(+,attr_f,attr_f); + OP_MATH_BODY(+,mrb_float,mrb_float); #endif break; case TYPES2(MRB_TT_STRING,MRB_TT_STRING): @@ -1781,7 +1783,7 @@ RETRY_TRY_BLOCK: x = mrb_fixnum(regs[a]); y = mrb_fixnum(regs[a+1]); if (mrb_int_sub_overflow(x, y, &z)) { - SET_FLT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y); + SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - (mrb_float)y); break; } SET_INT_VALUE(regs[a], z); @@ -1791,7 +1793,7 @@ RETRY_TRY_BLOCK: { mrb_int x = mrb_fixnum(regs[a]); mrb_float y = mrb_float(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], (mrb_float)x - y); + SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x - y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): @@ -1799,10 +1801,10 @@ RETRY_TRY_BLOCK: { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], x - y); + SET_FLOAT_VALUE(mrb, regs[a], x - y); } #else - OP_MATH_BODY(-,attr_f,attr_i); + OP_MATH_BODY(-,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): @@ -1810,10 +1812,10 @@ RETRY_TRY_BLOCK: { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], x - y); + SET_FLOAT_VALUE(mrb, regs[a], x - y); } #else - OP_MATH_BODY(-,attr_f,attr_f); + OP_MATH_BODY(-,mrb_float,mrb_float); #endif break; default: @@ -1842,7 +1844,7 @@ RETRY_TRY_BLOCK: break; case MRB_TT_FLOAT: { - SET_FLT_VALUE(mrb, regs[a], mrb_float(z)); + SET_FLOAT_VALUE(mrb, regs[a], mrb_float(z)); } break; default: @@ -1855,7 +1857,7 @@ RETRY_TRY_BLOCK: { mrb_int x = mrb_fixnum(regs[a]); mrb_float y = mrb_float(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], (mrb_float)x * y); + SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x * y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): @@ -1863,10 +1865,10 @@ RETRY_TRY_BLOCK: { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], x * y); + SET_FLOAT_VALUE(mrb, regs[a], x * y); } #else - OP_MATH_BODY(*,attr_f,attr_i); + OP_MATH_BODY(*,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): @@ -1874,10 +1876,10 @@ RETRY_TRY_BLOCK: { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], x * y); + SET_FLOAT_VALUE(mrb, regs[a], x * y); } #else - OP_MATH_BODY(*,attr_f,attr_f); + OP_MATH_BODY(*,mrb_float,mrb_float); #endif break; default: @@ -1896,14 +1898,14 @@ RETRY_TRY_BLOCK: { mrb_int x = mrb_fixnum(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], (mrb_float)x / (mrb_float)y); + SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / (mrb_float)y); } 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_FLT_VALUE(mrb, regs[a], (mrb_float)x / y); + SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x / y); } break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM): @@ -1911,10 +1913,10 @@ RETRY_TRY_BLOCK: { mrb_float x = mrb_float(regs[a]); mrb_int y = mrb_fixnum(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], x / y); + SET_FLOAT_VALUE(mrb, regs[a], x / y); } #else - OP_MATH_BODY(/,attr_f,attr_i); + OP_MATH_BODY(/,mrb_float,mrb_fixnum); #endif break; case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT): @@ -1922,18 +1924,18 @@ RETRY_TRY_BLOCK: { mrb_float x = mrb_float(regs[a]); mrb_float y = mrb_float(regs[a+1]); - SET_FLT_VALUE(mrb, regs[a], x / y); + SET_FLOAT_VALUE(mrb, regs[a], x / y); } #else - OP_MATH_BODY(/,attr_f,attr_f); + OP_MATH_BODY(/,mrb_float,mrb_float); #endif break; default: goto L_SEND; } #ifdef MRB_NAN_BOXING - if (isnan(regs[a].attr_f)) { - regs[a] = mrb_float_value(mrb, regs[a].attr_f); + if (isnan(mrb_float(regs[a]))) { + regs[a] = mrb_float_value(mrb, mrb_float(regs[a])); } #endif NEXT; @@ -1947,25 +1949,25 @@ RETRY_TRY_BLOCK: switch (mrb_type(regs[a])) { case MRB_TT_FIXNUM: { - mrb_int x = regs[a].attr_i; + 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_FLT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y); + SET_FLOAT_VALUE(mrb, regs[a], (mrb_float)x + (mrb_float)y); break; } - regs[a].attr_i = z; + SET_INT_VALUE(regs[a], z); } break; case MRB_TT_FLOAT: #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); - SET_FLT_VALUE(mrb, regs[a], x + GETARG_C(i)); + SET_FLOAT_VALUE(mrb, regs[a], x + GETARG_C(i)); } #else - regs[a].attr_f += GETARG_C(i); + mrb_float(regs[a]) += GETARG_C(i); #endif break; default: @@ -1985,15 +1987,15 @@ RETRY_TRY_BLOCK: switch (mrb_type(regs_a[0])) { case MRB_TT_FIXNUM: { - mrb_int x = regs_a[0].attr_i; + 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_FLT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y); + SET_FLOAT_VALUE(mrb, regs_a[0], (mrb_float)x - (mrb_float)y); } else { - regs_a[0].attr_i = z; + SET_INT_VALUE(regs_a[0], z); } } break; @@ -2001,10 +2003,10 @@ RETRY_TRY_BLOCK: #ifdef MRB_WORD_BOXING { mrb_float x = mrb_float(regs[a]); - SET_FLT_VALUE(mrb, regs[a], x - GETARG_C(i)); + SET_FLOAT_VALUE(mrb, regs[a], x - GETARG_C(i)); } #else - regs_a[0].attr_f -= GETARG_C(i); + mrb_float(regs_a[0]) -= GETARG_C(i); #endif break; default: @@ -2015,23 +2017,23 @@ RETRY_TRY_BLOCK: NEXT; } -#define OP_CMP_BODY(op,v1,v2) (regs[a].v1 op regs[a+1].v2) +#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,attr_i,attr_i);\ + result = OP_CMP_BODY(op,mrb_fixnum,mrb_fixnum);\ break;\ case TYPES2(MRB_TT_FIXNUM,MRB_TT_FLOAT):\ - result = OP_CMP_BODY(op,attr_i,attr_f);\ + result = OP_CMP_BODY(op,mrb_fixnum,mrb_float);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_FIXNUM):\ - result = OP_CMP_BODY(op,attr_f,attr_i);\ + result = OP_CMP_BODY(op,mrb_float,mrb_fixnum);\ break;\ case TYPES2(MRB_TT_FLOAT,MRB_TT_FLOAT):\ - result = OP_CMP_BODY(op,attr_f,attr_f);\ + result = OP_CMP_BODY(op,mrb_float,mrb_float);\ break;\ default:\ goto L_SEND;\ @@ -2138,33 +2140,28 @@ RETRY_TRY_BLOCK: int pre = GETARG_B(i); int post = GETARG_C(i); + struct RArray *ary; + int len, idx; + if (!mrb_array_p(v)) { - regs[a++] = mrb_ary_new_capa(mrb, 0); + v = mrb_ary_new_from_values(mrb, 1, ®s[a]); + } + ary = mrb_ary_ptr(v); + len = ary->len; + if (len > pre + post) { + regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre); while (post--) { - SET_NIL_VALUE(regs[a]); - a++; + regs[a++] = ary->ptr[len-post-1]; } } else { - struct RArray *ary = mrb_ary_ptr(v); - int len = ary->len; - int i; - - if (len > pre + post) { - regs[a++] = mrb_ary_new_from_values(mrb, len - pre - post, ary->ptr+pre); - while (post--) { - regs[a++] = ary->ptr[len-post-1]; - } + regs[a++] = mrb_ary_new_capa(mrb, 0); + for (idx=0; idx+pre<len; idx++) { + regs[a+idx] = ary->ptr[pre+idx]; } - else { - regs[a++] = mrb_ary_new_capa(mrb, 0); - for (i=0; i+pre<len; i++) { - regs[a+i] = ary->ptr[pre+i]; - } - while (i < post) { - SET_NIL_VALUE(regs[a+i]); - i++; - } + while (idx < post) { + SET_NIL_VALUE(regs[a+idx]); + idx++; } } ARENA_RESTORE(mrb, ai); @@ -2181,6 +2178,7 @@ RETRY_TRY_BLOCK: CASE(OP_STRCAT) { /* A B R(A).concat(R(B)) */ mrb_str_concat(mrb, regs[GETARG_A(i)], regs[GETARG_B(i)]); + regs = mrb->c->stack; NEXT; } @@ -2282,6 +2280,7 @@ RETRY_TRY_BLOCK: ci->proc = p; if (MRB_PROC_CFUNC_P(p)) { + ci->nregs = 0; mrb->c->stack[0] = p->body.func(mrb, recv); mrb_gc_arena_restore(mrb, ai); if (mrb->exc) goto L_RAISE; @@ -2306,8 +2305,9 @@ RETRY_TRY_BLOCK: /* 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_vm(mrb, c, syms[GETARG_B(i)], regs[a+1]); + mrb_define_method_raw(mrb, c, syms[GETARG_B(i)], p); ARENA_RESTORE(mrb, ai); NEXT; } @@ -2340,10 +2340,10 @@ RETRY_TRY_BLOCK: CASE(OP_DEBUG) { /* A B C debug print R(A),R(B),R(C) */ -#ifdef ENABLE_DEBUG +#ifdef MRB_ENABLE_DEBUG_HOOK mrb->debug_op_hook(mrb, irep, pc, regs); #else -#ifdef ENABLE_STDIO +#ifndef MRB_DISABLE_STDIO printf("OP_DEBUG %d %d %d\n", GETARG_A(i), GETARG_B(i), GETARG_C(i)); #else abort(); @@ -2395,13 +2395,13 @@ RETRY_TRY_BLOCK: MRB_END_EXC(&c_jmp); } -mrb_value +MRB_API mrb_value mrb_run(mrb_state *mrb, struct RProc *proc, mrb_value self) { return mrb_context_run(mrb, proc, self, mrb->c->ci->argc + 2); /* argc + 2 (receiver and block) */ } -mrb_value +MRB_API mrb_value mrb_toplevel_run_keep(mrb_state *mrb, struct RProc *proc, unsigned int stack_keep) { mrb_callinfo *ci; @@ -2411,6 +2411,7 @@ mrb_toplevel_run_keep(mrb_state *mrb, struct RProc *proc, unsigned int stack_kee return mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep); } ci = cipush(mrb); + ci->nregs = 1; /* protect the receiver */ ci->acc = CI_ACC_SKIP; ci->target_class = mrb->object_class; v = mrb_context_run(mrb, proc, mrb_top_self(mrb), stack_keep); @@ -2419,7 +2420,7 @@ mrb_toplevel_run_keep(mrb_state *mrb, struct RProc *proc, unsigned int stack_kee return v; } -mrb_value +MRB_API mrb_value mrb_toplevel_run(mrb_state *mrb, struct RProc *proc) { return mrb_toplevel_run_keep(mrb, proc, 0); |
