diff options
37 files changed, 331 insertions, 165 deletions
diff --git a/include/mruby.h b/include/mruby.h index d91e024d1..c4466de4c 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -112,6 +112,7 @@ typedef struct mrb_state { struct mrb_jmpbuf *jmp; mrb_allocf allocf; /* memory allocation function */ + void *allocf_ud; /* auxiliary data of allocf */ struct mrb_context *c; struct mrb_context *root_c; @@ -385,6 +386,7 @@ void mrb_print_error(mrb_state *mrb); #define E_SYNTAX_ERROR (mrb_class_get(mrb, "SyntaxError")) #define E_LOCALJUMP_ERROR (mrb_class_get(mrb, "LocalJumpError")) #define E_REGEXP_ERROR (mrb_class_get(mrb, "RegexpError")) +#define E_SYSSTACK_ERROR (mrb_class_get(mrb, "SystemStackError")) #define E_NOTIMP_ERROR (mrb_class_get(mrb, "NotImplementedError")) #define E_FLOATDOMAIN_ERROR (mrb_class_get(mrb, "FloatDomainError")) diff --git a/include/mruby/boxing_word.h b/include/mruby/boxing_word.h index 6057311a8..92e6ae6de 100644 --- a/include/mruby/boxing_word.h +++ b/include/mruby/boxing_word.h @@ -11,6 +11,16 @@ # error MRB_INT16 is too small for MRB_WORD_BOXING. #endif +struct RFloat { + MRB_OBJECT_HEADER; + mrb_float f; +}; + +struct RCptr { + MRB_OBJECT_HEADER; + void *p; +}; + #define MRB_FIXNUM_SHIFT 1 #define MRB_TT_HAS_BASIC MRB_TT_FLOAT diff --git a/include/mruby/error.h b/include/mruby/error.h index 4d37f1701..210ae3449 100644 --- a/include/mruby/error.h +++ b/include/mruby/error.h @@ -11,6 +11,13 @@ extern "C" { #endif +struct RException { + MRB_OBJECT_HEADER; + struct iv_tbl *iv; +}; + +#define mrb_exc_ptr(v) ((struct RException*)mrb_ptr(v)) + void mrb_sys_fail(mrb_state *mrb, const char *mesg); mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str); #define mrb_exc_new_str_lit(mrb, c, lit) mrb_exc_new_str(mrb, c, mrb_str_new_lit(mrb, lit)) diff --git a/include/mruby/object.h b/include/mruby/object.h index f6a9f4081..56917a021 100644 --- a/include/mruby/object.h +++ b/include/mruby/object.h @@ -55,14 +55,4 @@ struct RFiber { struct mrb_context *cxt; }; -struct RFloat { - MRB_OBJECT_HEADER; - mrb_float f; -}; - -struct RCptr { - MRB_OBJECT_HEADER; - void *p; -}; - #endif /* MRUBY_OBJECT_H */ diff --git a/include/mruby/value.h b/include/mruby/value.h index f16fa20a6..ae75f9c23 100644 --- a/include/mruby/value.h +++ b/include/mruby/value.h @@ -122,6 +122,7 @@ enum mrb_vtype { #define mrb_string_p(o) (mrb_type(o) == MRB_TT_STRING) #define mrb_hash_p(o) (mrb_type(o) == MRB_TT_HASH) #define mrb_cptr_p(o) (mrb_type(o) == MRB_TT_CPTR) +#define mrb_exception_p(o) (mrb_type(o) == MRB_TT_EXCEPTION) #define mrb_test(o) mrb_bool(o) mrb_bool mrb_regexp_p(struct mrb_state*, mrb_value); @@ -129,6 +130,7 @@ static inline mrb_value mrb_float_value(struct mrb_state *mrb, mrb_float f) { mrb_value v; + (void) mrb; SET_FLOAT_VALUE(mrb, v, f); return v; } @@ -137,6 +139,7 @@ static inline mrb_value mrb_cptr_value(struct mrb_state *mrb, void *p) { mrb_value v; + (void) mrb; SET_CPTR_VALUE(mrb,v,p); return v; } diff --git a/include/mruby/variable.h b/include/mruby/variable.h index 68a4e5889..f3bf6ac8e 100644 --- a/include/mruby/variable.h +++ b/include/mruby/variable.h @@ -48,7 +48,7 @@ void mrb_iv_set(mrb_state *mrb, mrb_value obj, mrb_sym sym, mrb_value v); mrb_bool mrb_iv_defined(mrb_state*, mrb_value, mrb_sym); mrb_value mrb_iv_remove(mrb_state *mrb, mrb_value obj, mrb_sym sym); void mrb_iv_copy(mrb_state *mrb, mrb_value dst, mrb_value src); -int mrb_const_defined_at(mrb_state *mrb, struct RClass *klass, mrb_sym id); +mrb_bool mrb_const_defined_at(mrb_state *mrb, struct RClass *klass, mrb_sym id); mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod); mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self); mrb_value mrb_gv_get(mrb_state *mrb, mrb_sym sym); diff --git a/mrbgems/full-core.gembox b/mrbgems/full-core.gembox index 4a6ebbc80..4dbb13e92 100644 --- a/mrbgems/full-core.gembox +++ b/mrbgems/full-core.gembox @@ -2,8 +2,8 @@ MRuby::GemBox.new do |conf| conf.gem :core => "mruby-sprintf" conf.gem :core => "mruby-print" - Dir.glob("#{root}/mrbgems/mruby-*") do |x| - g = File.basename(x) + Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x| + g = File.basename File.dirname x conf.gem :core => g unless g =~ /^mruby-(print|sprintf)$/ end end diff --git a/mrbgems/mruby-enumerator/mrbgem.rake b/mrbgems/mruby-enumerator/mrbgem.rake index abcc54e7a..8757a15ea 100644 --- a/mrbgems/mruby-enumerator/mrbgem.rake +++ b/mrbgems/mruby-enumerator/mrbgem.rake @@ -2,5 +2,6 @@ MRuby::Gem::Specification.new('mruby-enumerator') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.add_dependency('mruby-fiber', :core => 'mruby-fiber') + spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext' spec.summary = 'Enumerator class' end diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c index 6ca7d4fc2..ece769faf 100644 --- a/mrbgems/mruby-eval/src/eval.c +++ b/mrbgems/mruby-eval/src/eval.c @@ -173,10 +173,39 @@ f_eval(mrb_state *mrb, mrb_value self) return ret; } +mrb_value mrb_obj_instance_eval(mrb_state *mrb, mrb_value self); + +#define CI_ACC_SKIP -1 + +static mrb_value +f_instance_eval(mrb_state *mrb, mrb_value self) +{ + mrb_value b; + mrb_int argc; mrb_value *argv; + + mrb_get_args(mrb, "*&", &argv, &argc, &b); + + if (mrb_nil_p(b)) { + char *s; + mrb_int len; + char *file = NULL; + mrb_int line = 1; + + mrb_get_args(mrb, "s|zi", &s, &len, &file, &line); + mrb->c->ci->acc = CI_ACC_SKIP; + return mrb_run(mrb, create_proc_from_string(mrb, s, len, mrb_nil_value(), file, line), self); + } + else { + mrb_get_args(mrb, "&", &b); + return mrb_obj_instance_eval(mrb, self); + } +} + void mrb_mruby_eval_gem_init(mrb_state* mrb) { mrb_define_module_function(mrb, mrb->kernel_module, "eval", f_eval, MRB_ARGS_ARG(1, 3)); + mrb_define_method(mrb, mrb->kernel_module, "instance_eval", f_instance_eval, MRB_ARGS_ARG(1, 2)); } void diff --git a/mrbgems/mruby-eval/test/eval.rb b/mrbgems/mruby-eval/test/eval.rb index eb51f396a..87c64732d 100644 --- a/mrbgems/mruby-eval/test/eval.rb +++ b/mrbgems/mruby-eval/test/eval.rb @@ -50,3 +50,13 @@ assert 'eval syntax error' do eval 'p "test' end end + +assert('String instance_eval') do + obj = Object.new + obj.instance_variable_set :@test, 'test' + assert_raise(ArgumentError) { obj.instance_eval(0) { } } + assert_raise(ArgumentError) { obj.instance_eval('0', 'test', 0, 'test') } + assert_equal(['test.rb', 10]) { obj.instance_eval('[__FILE__, __LINE__]', 'test.rb', 10)} + assert_equal('test') { obj.instance_eval('@test') } + assert_equal('test') { obj.instance_eval { @test } } +end diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c index ddb31ea38..824a564f6 100644 --- a/mrbgems/mruby-fiber/src/fiber.c +++ b/mrbgems/mruby-fiber/src/fiber.c @@ -69,6 +69,7 @@ fiber_init(mrb_state *mrb, mrb_value self) struct RProc *p; mrb_callinfo *ci; mrb_value blk; + size_t slen; mrb_get_args(mrb, "&", &blk); @@ -85,14 +86,18 @@ fiber_init(mrb_state *mrb, mrb_value self) c = f->cxt; /* initialize VM stack */ - c->stbase = (mrb_value *)mrb_malloc(mrb, FIBER_STACK_INIT_SIZE*sizeof(mrb_value)); - c->stend = c->stbase + FIBER_STACK_INIT_SIZE; + slen = FIBER_STACK_INIT_SIZE; + if (p->body.irep->nregs > slen) { + slen += p->body.irep->nregs; + } + c->stbase = (mrb_value *)mrb_malloc(mrb, slen*sizeof(mrb_value)); + c->stend = c->stbase + slen; c->stack = c->stbase; #ifdef MRB_NAN_BOXING { mrb_value *p = c->stbase; - mrb_value *pend = p + FIBER_STACK_INIT_SIZE; + mrb_value *pend = c->stend; while (p < pend) { SET_NIL_VALUE(*p); @@ -100,7 +105,7 @@ fiber_init(mrb_state *mrb, mrb_value self) } } #else - memset(c->stbase, 0, FIBER_STACK_INIT_SIZE * sizeof(mrb_value)); + memset(c->stbase, 0, slen * sizeof(mrb_value)); #endif /* copy receiver from a block */ @@ -274,7 +279,8 @@ fiber_transfer(mrb_state *mrb, mrb_value self) mrb->c->status = MRB_FIBER_TRANSFERRED; mrb->c = c; c->status = MRB_FIBER_RUNNING; - MARK_CONTEXT_MODIFY(c); + MARK_CONTEXT_MODIFY(c); + mrb_write_barrier(mrb, (struct RBasic*)c->fib); return fiber_result(mrb, a, len); } @@ -305,6 +311,7 @@ mrb_fiber_yield(mrb_state *mrb, mrb_int len, const mrb_value *a) mrb->c = c->prev; c->prev = NULL; MARK_CONTEXT_MODIFY(mrb->c); + mrb_write_barrier(mrb, (struct RBasic*)c->fib); return fiber_result(mrb, a, len); } diff --git a/mrbgems/mruby-hash-ext/mrbgem.rake b/mrbgems/mruby-hash-ext/mrbgem.rake index e1ce8e767..663de2166 100644 --- a/mrbgems/mruby-hash-ext/mrbgem.rake +++ b/mrbgems/mruby-hash-ext/mrbgem.rake @@ -2,4 +2,6 @@ MRuby::Gem::Specification.new('mruby-hash-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'extensional Hash class' + spec.add_dependency 'mruby-enum-ext', :core => 'mruby-enum-ext' + spec.add_dependency 'mruby-array-ext', :core => 'mruby-array-ext' end diff --git a/mrbgems/mruby-numeric-ext/test/numeric.rb b/mrbgems/mruby-numeric-ext/test/numeric.rb index 4d9e83113..96644da69 100644 --- a/mrbgems/mruby-numeric-ext/test/numeric.rb +++ b/mrbgems/mruby-numeric-ext/test/numeric.rb @@ -5,13 +5,8 @@ assert('Integer#chr') do assert_equal("A", 65.chr) assert_equal("B", 0x42.chr) - if "こんにちわ世界".size == 7 then - # UTF-8 gem is configured - assert_raise(RangeError) { 0x110000.chr } - else - # multibyte encoding (not support yet) - assert_raise(RangeError) { 256.chr } - end + # multibyte encoding (not support yet) + assert_raise(RangeError) { 256.chr } end assert('Integer#div') do diff --git a/mrbgems/mruby-string-utf8/src/string.c b/mrbgems/mruby-string-utf8/src/string.c index 67f54ee02..a1b779f95 100644 --- a/mrbgems/mruby-string-utf8/src/string.c +++ b/mrbgems/mruby-string-utf8/src/string.c @@ -608,7 +608,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) while (ptr < eptr && (end = mrb_memsearch(sptr, slen, ptr, eptr - ptr)) >= 0) { - // mrb_ary_push(mrb, result, str_subseq(mrb, str, ptr - temp, end)); + /* mrb_ary_push(mrb, result, str_subseq(mrb, str, ptr - temp, end)); */ mrb_ary_push(mrb, result, mrb_str_new(mrb, ptr, end)); mrb_gc_arena_restore(mrb, ai); ptr += end + slen; diff --git a/mrblib/array.rb b/mrblib/array.rb index 3218aa858..bd8d5930f 100644 --- a/mrblib/array.rb +++ b/mrblib/array.rb @@ -84,13 +84,20 @@ class Array self end + def _inspect + return "[]" if self.size == 0 + "["+self.map{|x|x.inspect}.join(", ")+"]" + end ## - # Private method for Array creation. + # Return the contents of this array as a string. # # ISO 15.2.12.5.31 (x) def inspect - return "[]" if self.size == 0 - "["+self.map{|x|x.inspect}.join(", ")+"]" + begin + self._inspect + rescue SystemStackError + "[...]" + end end # ISO 15.2.12.5.32 (x) alias to_s inspect diff --git a/mrblib/hash.rb b/mrblib/hash.rb index 9bb146b27..e7c51fb1f 100644 --- a/mrblib/hash.rb +++ b/mrblib/hash.rb @@ -192,16 +192,24 @@ class Hash h end - ## - # Return the contents of this hash as a string. - # - # ISO 15.2.13.4.30 (x) - def inspect + # internal method for Hash inspection + def _inspect return "{}" if self.size == 0 "{"+self.map {|k,v| - k.inspect + "=>" + v.inspect + k._inspect + "=>" + v._inspect }.join(", ")+"}" end + ## + # Return the contents of this hash as a string. + # + # ISO 15.2.13.4.30 (x) + def inspect + begin + self._inspect + rescue SystemStackError + "{...}" + end + end # ISO 15.2.13.4.31 (x) alias to_s inspect diff --git a/mrblib/kernel.rb b/mrblib/kernel.rb index d0fe47300..476ec8e18 100644 --- a/mrblib/kernel.rb +++ b/mrblib/kernel.rb @@ -39,6 +39,11 @@ module Kernel !(self =~ y) end + # internal method for inspect + def _inspect + self.inspect + end + def to_enum(*a) raise NotImplementedError.new("fiber required for enumerator") end diff --git a/src/backtrace.c b/src/backtrace.c index 1e1f9fa1a..d02d11350 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -147,6 +147,9 @@ exc_output_backtrace(mrb_state *mrb, struct RObject *exc, output_stream_func fun void mrb_print_backtrace(mrb_state *mrb) { + if (!mrb->exc || mrb_obj_is_kind_of(mrb, mrb_obj_value(mrb->exc), E_SYSSTACK_ERROR)) { + return; + } exc_output_backtrace(mrb, mrb->exc, print_backtrace_i, (void*)stderr); } diff --git a/src/class.c b/src/class.c index 2b42a6a92..4522e4529 100644 --- a/src/class.c +++ b/src/class.c @@ -1206,7 +1206,7 @@ mrb_bob_missing(mrb_state *mrb, mrb_value mod) /* method missing in inspect; avoid recursion */ repr = mrb_any_to_s(mrb, mod); } - else if (mrb_respond_to(mrb, mod, inspect)) { + else if (mrb_respond_to(mrb, mod, inspect) && mrb->c->ci - mrb->c->cibase < 64) { repr = mrb_funcall_argv(mrb, mod, inspect, 0, 0); if (RSTRING_LEN(repr) > 64) { repr = mrb_any_to_s(mrb, mod); @@ -1946,6 +1946,43 @@ mrb_mod_eqq(mrb_state *mrb, mrb_value mod) return mrb_bool_value(eqq); } +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) { @@ -2009,6 +2046,7 @@ 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 */ diff --git a/src/dump.c b/src/dump.c index b820f1a68..e019baa99 100644 --- a/src/dump.c +++ b/src/dump.c @@ -952,7 +952,9 @@ mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, int debug_info, FILE* fp) result = mrb_dump_irep(mrb, irep, debug_info, &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); diff --git a/src/error.c b/src/error.c index dcb6063f8..223e2137e 100644 --- a/src/error.c +++ b/src/error.c @@ -15,6 +15,7 @@ #include "mruby/variable.h" #include "mruby/debug.h" #include "mruby/error.h" +#include "mruby/class.h" #include "mrb_throw.h" mrb_value @@ -442,6 +443,7 @@ 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()); @@ -455,4 +457,5 @@ mrb_init_exception(mrb_state *mrb) mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str(mrb, runtime_error, mrb_str_new_lit(mrb, "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); } @@ -16,6 +16,7 @@ #include "mruby/string.h" #include "mruby/variable.h" #include "mruby/gc.h" +#include "mruby/error.h" /* = Tri-color Incremental Garbage Collection @@ -107,6 +108,7 @@ typedef struct { struct RRange range; struct RData data; struct RProc proc; + struct RException exc; #ifdef MRB_WORD_BOXING struct RFloat floatv; struct RCptr cptr; @@ -171,10 +173,10 @@ mrb_realloc_simple(mrb_state *mrb, void *p, size_t len) { void *p2; - p2 = (mrb->allocf)(mrb, p, len, mrb->ud); + p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud); if (!p2 && len > 0 && mrb->heaps) { mrb_full_gc(mrb); - p2 = (mrb->allocf)(mrb, p, len, mrb->ud); + p2 = (mrb->allocf)(mrb, p, len, mrb->allocf_ud); } return p2; @@ -240,7 +242,7 @@ mrb_calloc(mrb_state *mrb, size_t nelem, size_t len) 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 @@ -513,6 +515,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; @@ -612,6 +615,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; @@ -747,6 +751,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; diff --git a/src/kernel.c b/src/kernel.c index 0258e5c15..5ba318b1d 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -275,6 +275,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; diff --git a/src/proc.c b/src/proc.c index 1511725d9..9e63e5f08 100644 --- a/src/proc.c +++ b/src/proc.c @@ -153,11 +153,11 @@ mrb_proc_arity(mrb_state *mrb, mrb_value self) int ma, ra, pa, arity; if (MRB_PROC_CFUNC_P(p)) { - // TODO cfunc aspec not implemented yet + /* TODO cfunc aspec not implemented yet */ return mrb_fixnum_value(-1); } - // arity is depend on OP_ENTER + /* arity is depend on OP_ENTER */ if (GET_OPCODE(*iseq) != OP_ENTER) { return mrb_fixnum_value(0); } diff --git a/src/state.c b/src/state.c index ff0182f22..9c40c842c 100644 --- a/src/state.c +++ b/src/state.c @@ -29,15 +29,11 @@ mrb_open_core(mrb_allocf f, void *ud) 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; diff --git a/src/string.c b/src/string.c index a22060e37..16e7f9ffd 100644 --- a/src/string.c +++ b/src/string.c @@ -1799,7 +1799,7 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) } } else if (split_type == string) { - char *ptr = RSTRING_PTR(str); // s->as.ary + char *ptr = RSTRING_PTR(str); /* s->as.ary */ char *temp = ptr; char *eptr = RSTRING_END(str); mrb_int slen = RSTRING_LEN(spat); diff --git a/src/symbol.c b/src/symbol.c index 148adc6fe..cf4945185 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -216,12 +216,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 */ diff --git a/src/variable.c b/src/variable.c index 74bb591cf..58cd08304 100644 --- a/src/variable.c +++ b/src/variable.c @@ -459,6 +459,7 @@ 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; @@ -1085,7 +1086,7 @@ retry: return FALSE; } -int +mrb_bool mrb_const_defined_at(mrb_state *mrb, struct RClass *klass, mrb_sym id) { return mrb_const_defined_0(mrb, klass, id, TRUE, FALSE); @@ -160,7 +160,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=" TO_STR(MRB_STACK_MAX) ")"); } } @@ -484,6 +484,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); diff --git a/tasks/mrbgem_spec.rake b/tasks/mrbgem_spec.rake index 2a15a1f46..745b2f29d 100644 --- a/tasks/mrbgem_spec.rake +++ b/tasks/mrbgem_spec.rake @@ -45,10 +45,6 @@ module MRuby MRuby::Gem.current = self end - def run_test_in_other_mrb_state? - not test_preload.nil? or not test_objs.empty? or not test_args.empty? - end - def setup MRuby::Gem.current = self MRuby::Build::COMMANDS.each do |command| @@ -66,6 +62,7 @@ module MRuby @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X")) end + @custom_test_init = !@test_objs.empty? @test_preload = nil # 'test/assert.rb' @test_args = {} @@ -184,13 +181,19 @@ module MRuby print_gem_comment(f) f.puts %Q[#include <stdlib.h>] f.puts %Q[#include "mruby.h"] - f.puts %Q[#include "mruby/array.h"] f.puts %Q[#include "mruby/irep.h"] - f.puts %Q[#include "mruby/string.h"] f.puts %Q[#include "mruby/variable.h"] f.puts %Q[#include "mruby/hash.h"] unless test_args.empty? end + def test_dependencies + [@name] + end + + def custom_test_init? + @custom_test_init + end + def version_ok?(req_versions) req_versions.map do |req| cmp, ver = req.split @@ -290,7 +293,7 @@ module MRuby @ary.empty? end - def check(build) + def generate_gem_table gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res } default_gems = [] @@ -335,9 +338,29 @@ module MRuby fail "Conflicts of gem `#{g.name}` found: #{cfls.join ', '}" unless cfls.empty? end - class << gem_table + gem_table + end + + def tsort_dependencies ary, table, all_dependency_listed = false + unless all_dependency_listed + left = ary.dup + until left.empty? + v = left.pop + table[v].dependencies.each do |dep| + left.push dep[:gem] + ary.push dep[:gem] + end + end + end + + ary.uniq! + table.instance_variable_set :@root_gems, ary + class << table include TSort - alias tsort_each_node each_key + def tsort_each_node &b + @root_gems.each &b + end + def tsort_each_child(n, &b) fetch(n).dependencies.each do |v| b.call v[:gem] @@ -346,10 +369,16 @@ module MRuby end begin - @ary = gem_table.tsort.map { |v| gem_table[v] } + table.tsort.map { |v| table[v] } rescue TSort::Cyclic => e fail "Circular mrbgem dependency found: #{e.message}" end + end + + def check(build) + gem_table = generate_gem_table + + @ary = tsort_dependencies gem_table.keys, gem_table, true each do |g| import_include_paths(g) diff --git a/tasks/mrbgems_test.rake b/tasks/mrbgems_test.rake index 2fdf5f171..b732fa772 100644 --- a/tasks/mrbgems_test.rake +++ b/tasks/mrbgems_test.rake @@ -1,16 +1,13 @@ MRuby.each_target do - no_mrb_open_test_gem = [] + gem_table = gems.generate_gem_table gems.each do |g| - unless g.run_test_in_other_mrb_state? - no_mrb_open_test_gem << g - next - end - test_rbobj = g.test_rbireps.ext(exts.object) + g.test_objs << test_rbobj + dep_list = gems.tsort_dependencies g.test_dependencies, gem_table file test_rbobj => g.test_rbireps - file g.test_rbireps => [g.test_rbfiles].flatten + [g.build.mrbcfile, __FILE__] do |t| + file g.test_rbireps => [g.test_rbfiles].flatten + [File.join(g.dir, 'mrbgem.rake'), g.build.mrbcfile, __FILE__, "#{MRUBY_ROOT}/tasks/mrbgem_spec.rake"] do |t| open(t.name, 'w') do |f| g.print_gem_test_header(f) test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir| @@ -33,22 +30,27 @@ MRuby.each_target do g.build.mrbc.run f, rbfile, "gem_test_irep_#{g.funcname}_#{i}" end f.puts %Q[void mrb_#{g.funcname}_gem_test(mrb_state *mrb);] unless g.test_objs.empty? + dep_list.each do |d| + f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb_state *mrb);] + f.puts %Q[void GENERATED_TMP_mrb_#{d.funcname}_gem_final(mrb_state *mrb);] + end + f.puts %Q[void mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose);] + f.puts %Q[void mrb_t_pass_result(mrb_state *dst, mrb_state *src);] f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb) {] unless g.test_rbfiles.empty? f.puts %Q[ mrb_state *mrb2;] - if g.test_args.empty? - f.puts %Q[ mrb_value val1, val2, val3, ary1, ary2;] - else - f.puts %Q[ mrb_value val1, val2, val3, ary1, ary2, test_args_hash;] + unless g.test_args.empty? + f.puts %Q[ mrb_value test_args_hash;] end f.puts %Q[ int ai;] g.test_rbfiles.count.times do |i| f.puts %Q[ ai = mrb_gc_arena_save(mrb);] - f.puts %Q[ mrb2 = mrb_open();] - f.puts %Q[ val3 = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"));] - f.puts %Q[ if (mrb_test(val3)) {] - f.puts %Q[ mrb_gv_set(mrb2, mrb_intern_lit(mrb2, "$mrbtest_verbose"), val3);] - f.puts %Q[ }] + f.puts %Q[ mrb2 = mrb_open_core(mrb_default_allocf, NULL);] + dep_list.each do |d| + f.puts %Q[ GENERATED_TMP_mrb_#{d.funcname}_gem_init(mrb2);] + f.puts %Q[ mrb_state_atexit(mrb2, GENERATED_TMP_mrb_#{d.funcname}_gem_final);] + end + f.puts %Q[ mrb_init_test_driver(mrb2, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"))));] if test_preload.nil? f.puts %Q[ mrb_load_irep(mrb2, mrbtest_assert_irep);] else @@ -70,33 +72,12 @@ MRuby.each_target do f.puts %Q[ mrb_const_set(mrb2, mrb_obj_value(mrb2->object_class), mrb_intern_lit(mrb2, "TEST_ARGS"), test_args_hash); ] end - f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] unless g.test_objs.empty? + f.puts %Q[ mrb_#{g.funcname}_gem_test(mrb2);] if g.custom_test_init? f.puts %Q[ mrb_load_irep(mrb2, gem_test_irep_#{g.funcname}_#{i});] - f.puts %Q[ if (mrb2->exc) {] - f.puts %Q[ mrb_print_error(mrb2);] - f.puts %Q[ exit(EXIT_FAILURE);] - f.puts %Q[ }] f.puts %Q[ ] - %w(ok_test ko_test kill_test).each do |vname| - f.puts %Q[ val2 = mrb_gv_get(mrb2, mrb_intern_lit(mrb2, "$#{vname}"));] - f.puts %Q[ if (mrb_fixnum_p(val2)) {] - f.puts %Q[ val1 = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$#{vname}"));] - f.puts %Q[ mrb_gv_set(mrb, mrb_intern_lit(mrb, "$#{vname}"), mrb_fixnum_value(mrb_fixnum(val1) + mrb_fixnum(val2)));] - f.puts %Q[ }\n] - end - - f.puts %Q[ ary2 = mrb_gv_get(mrb2, mrb_intern_lit(mrb2, "$asserts"));] - f.puts %Q[ if (mrb_test(ary2)) {] - f.puts %Q[ ary1 = mrb_gv_get(mrb, mrb_intern_lit(mrb, "$asserts"));] - f.puts %Q[ val2 = mrb_ary_shift(mrb2, ary2);] - f.puts %Q[ ] - f.puts %Q[ while (mrb_test(val2)) {] - f.puts %Q[ mrb_ary_push(mrb, ary1, mrb_str_new(mrb, RSTRING_PTR(val2), RSTRING_LEN(val2)));] - f.puts %Q[ val2 = mrb_ary_shift(mrb2, ary2);] - f.puts %Q[ }] - f.puts %Q[ }] + f.puts %Q[ mrb_t_pass_result(mrb, mrb2);] f.puts %Q[ mrb_close(mrb2);] f.puts %Q[ mrb_gc_arena_restore(mrb, ai);] end @@ -104,44 +85,5 @@ MRuby.each_target do f.puts %Q[}] end end - - end - - no_mrb_open_test = "#{build_dir}/test/no_mrb_open_test" - no_mrb_open_test_rbfiles = no_mrb_open_test_gem.reduce([]) { |res, v| - res += v.test_rbfiles - } - if no_mrb_open_test_rbfiles.empty? - no_mrb_open_test_rbfiles << "#{MRUBY_ROOT}/test/no_mrb_open_test_dummy.rb" - end - - no_mrb_open_test_lib = no_mrb_open_test.ext(exts.object) - file no_mrb_open_test_lib => "#{no_mrb_open_test}.c" - file "#{no_mrb_open_test}.c" => no_mrb_open_test_rbfiles + [MRUBY_CONFIG, __FILE__] do |t| - open(t.name, 'w') do |f| - f.puts %Q[/*] - f.puts %Q[ * This file contains a test code for following gems:] - no_mrb_open_test_gem.each { |g| f.puts %Q[ * #{g.name}] } - f.puts %Q[ *] - f.puts %Q[ * IMPORTANT:] - f.puts %Q[ * This file was generated!] - f.puts %Q[ * All manual changes will get lost.] - f.puts %Q[ */] - - f.puts %Q[] - - f.puts %Q[\#include "mruby.h"] - f.puts %Q[\#include "mruby/irep.h"] - - f.puts %Q[] - - mrbc.run f, no_mrb_open_test_rbfiles, "no_mrb_open_gem_test_irep" - - f.puts %Q[] - - f.puts %Q[void no_mrb_open_mrbgem_test(mrb_state *mrb) {] - f.puts %Q[ mrb_load_irep(mrb, no_mrb_open_gem_test_irep);] - f.puts %Q[}] - end end end diff --git a/test/assert.rb b/test/assert.rb index a4ee00299..72e4d3b37 100644 --- a/test/assert.rb +++ b/test/assert.rb @@ -21,7 +21,7 @@ end ## # Create the assertion in a readable way -def assertion_string(err, str, iso=nil, e=nil) +def assertion_string(err, str, iso=nil, e=nil, bt=nil) msg = "#{err}#{str}" msg += " [#{iso}]" if iso && iso != '' msg += " => #{e.message}" if e @@ -31,6 +31,7 @@ def assertion_string(err, str, iso=nil, e=nil) msg += "\n - Assertion[#{idx}] Failed: #{str}\n#{diff}" end end + msg += "\nbacktrace:\n\t#{bt.join("\n\t")}" if bt msg end @@ -56,11 +57,12 @@ def assert(str = 'Assertion failed', iso = '') t_print('.') end rescue Exception => e + bt = e.backtrace if $mrbtest_verbose if e.class.to_s == 'MRubyTestSkip' $asserts.push "Skip: #{str} #{iso} #{e.cause}" t_print('?') else - $asserts.push(assertion_string("#{e.class}: ", str, iso, e)) + $asserts.push(assertion_string("#{e.class}: ", str, iso, e, bt)) $kill_test += 1 t_print('X') end diff --git a/test/driver.c b/test/driver.c index 7fd8ef720..66d64d3de 100644 --- a/test/driver.c +++ b/test/driver.c @@ -16,6 +16,7 @@ #include "mruby/compile.h" #include "mruby/string.h" #include "mruby/variable.h" +#include "mruby/array.h" void mrb_init_mrbtest(mrb_state *); @@ -81,13 +82,68 @@ mrb_t_printstr(mrb_state *mrb, mrb_value self) return argv; } +void +mrb_init_test_driver(mrb_state *mrb, mrb_bool verbose) +{ + struct RClass *krn, *mrbtest; + + krn = mrb->kernel_module; + mrb_define_method(mrb, krn, "__t_printstr__", mrb_t_printstr, MRB_ARGS_REQ(1)); + + mrbtest = mrb_define_module(mrb, "Mrbtest"); + + mrb_define_const(mrb, mrbtest, "FIXNUM_MAX", mrb_fixnum_value(MRB_INT_MAX)); + mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN)); + mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT)); + + if (verbose) { + mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value()); + } +} + +void +mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src) +{ + mrb_value res_src; + + if (mrb_src->exc) { + mrb_print_error(mrb_src); + exit(EXIT_FAILURE); + } + +#define TEST_COUNT_PASS(name) \ + do { \ + res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$" #name)); \ + if (mrb_fixnum_p(res_src)) { \ + mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name)); \ + mrb_gv_set(mrb_dst, mrb_intern_lit(mrb_dst, "$" #name), mrb_fixnum_value(mrb_fixnum(res_dst) + mrb_fixnum(res_src))); \ + } \ + } while (FALSE) \ + + TEST_COUNT_PASS(ok_test); + TEST_COUNT_PASS(ko_test); + TEST_COUNT_PASS(kill_test); + +#undef TEST_COUNT_PASS + + res_src = mrb_gv_get(mrb_src, mrb_intern_lit(mrb_src, "$asserts")); + + if (mrb_array_p(res_src)) { + mrb_int i; + mrb_value res_dst = mrb_gv_get(mrb_dst, mrb_intern_lit(mrb_dst, "$asserts")); + for (i = 0; i < RARRAY_LEN(res_src); ++i) { + mrb_value val_src = RARRAY_PTR(res_src)[i]; + mrb_ary_push(mrb_dst, res_dst, mrb_str_new(mrb_dst, RSTRING_PTR(val_src), RSTRING_LEN(val_src))); + } + } +} + int main(int argc, char **argv) { mrb_state *mrb; - struct RClass *krn; - struct RClass *mrbtest; int ret; + mrb_bool verbose = FALSE; print_hint(); @@ -100,18 +156,10 @@ main(int argc, char **argv) if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'v') { printf("verbose mode: enable\n\n"); - mrb_gv_set(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose"), mrb_true_value()); + verbose = TRUE; } - krn = mrb->kernel_module; - mrb_define_method(mrb, krn, "__t_printstr__", mrb_t_printstr, MRB_ARGS_REQ(1)); - - mrbtest = mrb_define_module(mrb, "Mrbtest"); - - mrb_define_const(mrb, mrbtest, "FIXNUM_MAX", mrb_fixnum_value(MRB_INT_MAX)); - mrb_define_const(mrb, mrbtest, "FIXNUM_MIN", mrb_fixnum_value(MRB_INT_MIN)); - mrb_define_const(mrb, mrbtest, "FIXNUM_BIT", mrb_fixnum_value(MRB_INT_BIT)); - + mrb_init_test_driver(mrb, verbose); mrb_init_mrbtest(mrb); ret = eval_test(mrb); mrb_close(mrb); diff --git a/test/init_mrbtest.c b/test/init_mrbtest.c index 3ce42f661..2a32f4930 100644 --- a/test/init_mrbtest.c +++ b/test/init_mrbtest.c @@ -1,23 +1,36 @@ #include <stdlib.h> #include "mruby.h" #include "mruby/irep.h" +#include "mruby/variable.h" extern const uint8_t mrbtest_assert_irep[]; extern const uint8_t mrbtest_irep[]; void mrbgemtest_init(mrb_state* mrb); +void mrb_init_test_driver(mrb_state* mrb, mrb_bool verbose); +void mrb_t_pass_result(mrb_state *mrb_dst, mrb_state *mrb_src); void mrb_init_mrbtest(mrb_state *mrb) { + mrb_state *core_test; + mrb_load_irep(mrb, mrbtest_assert_irep); - mrb_load_irep(mrb, mrbtest_irep); + + core_test = mrb_open_core(mrb_default_allocf, NULL); + mrb_init_test_driver(core_test, mrb_test(mrb_gv_get(mrb, mrb_intern_lit(mrb, "$mrbtest_verbose")))); + mrb_load_irep(core_test, mrbtest_assert_irep); + mrb_load_irep(core_test, mrbtest_irep); + mrb_t_pass_result(mrb, core_test); + #ifndef DISABLE_GEMS mrbgemtest_init(mrb); #endif + if (mrb->exc) { mrb_print_error(mrb); exit(EXIT_FAILURE); } + mrb_close(core_test); } diff --git a/test/mrbtest.rake b/test/mrbtest.rake index c28cf3577..b9616fe9d 100644 --- a/test/mrbtest.rake +++ b/test/mrbtest.rake @@ -12,11 +12,13 @@ MRuby.each_target do ass_lib = ass_c.ext(exts.object) mrbtest_lib = libfile("#{current_build_dir}/mrbtest") - gem_test_files = gems.select { |g| g.run_test_in_other_mrb_state? }.map { |g| g.test_rbireps.ext(exts.object) } - file mrbtest_lib => [mlib, ass_lib, gems.map(&:test_objs), gem_test_files].flatten do |t| + mrbtest_objs = [mlib, ass_lib] + gems.each do |v| + mrbtest_objs.concat v.test_objs + end + file mrbtest_lib => mrbtest_objs do |t| archiver.run t.name, t.prerequisites end - file mrbtest_lib => "#{build_dir}/test/no_mrb_open_test.c".ext(exts.object) unless build_mrbtest_lib_only? driver_obj = objfile("#{current_build_dir}/driver") @@ -55,16 +57,12 @@ MRuby.each_target do f.puts IO.read(init) mrbc.run f, mrbs, 'mrbtest_irep' gems.each do |g| - next unless g.run_test_in_other_mrb_state? f.puts %Q[void GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb_state *mrb);] end - f.puts %Q[void no_mrb_open_mrbgem_test(mrb_state *mrb);] f.puts %Q[void mrbgemtest_init(mrb_state* mrb) {] gems.each do |g| - next unless g.run_test_in_other_mrb_state? f.puts %Q[ GENERATED_TMP_mrb_#{g.funcname}_gem_test(mrb);] end - f.puts %Q[ no_mrb_open_mrbgem_test(mrb);] f.puts %Q[}] end end diff --git a/test/no_mrb_open_test_dummy.rb b/test/no_mrb_open_test_dummy.rb deleted file mode 100644 index 5181c0a45..000000000 --- a/test/no_mrb_open_test_dummy.rb +++ /dev/null @@ -1,2 +0,0 @@ -#dummy - diff --git a/test/t/module.rb b/test/t/module.rb index 5ac794330..2e19f350f 100644 --- a/test/t/module.rb +++ b/test/t/module.rb @@ -522,3 +522,13 @@ assert('clone Module') do B.new.foo end + +assert('Module#module_function') do + module M + def modfunc; end + module_function :modfunc + end + + assert_true M.respond_to?(:modfunc) +end + |
