diff options
| -rw-r--r-- | doc/guides/compile.md | 29 | ||||
| -rw-r--r-- | include/mruby/common.h | 2 | ||||
| -rw-r--r-- | include/mruby/throw.h | 6 | ||||
| -rw-r--r-- | mrbgems/mruby-bin-mruby/mrbgem.rake | 6 | ||||
| -rw-r--r-- | mrbgems/mruby-bin-mruby/tools/mruby/mruby.c | 12 | ||||
| -rw-r--r-- | mrbgems/mruby-compiler/core/codegen.c | 2 | ||||
| -rw-r--r-- | mrbgems/mruby-compiler/mrbgem.rake | 4 | ||||
| -rw-r--r-- | mrbgems/mruby-error/mrbgem.rake | 2 | ||||
| -rw-r--r-- | src/backtrace.c | 4 | ||||
| -rw-r--r-- | src/error.c | 7 | ||||
| -rw-r--r-- | src/gc.c | 1 | ||||
| -rw-r--r-- | src/kernel.c | 4 | ||||
| -rw-r--r-- | src/mruby_core.rake | 4 | ||||
| -rw-r--r-- | src/vm.c | 40 | ||||
| -rw-r--r-- | tasks/mruby_build.rake | 32 | ||||
| -rw-r--r-- | test/t/nomethoderror.rb | 18 |
16 files changed, 94 insertions, 79 deletions
diff --git a/doc/guides/compile.md b/doc/guides/compile.md index 9ca61c6fd..2aaf6f1fe 100644 --- a/doc/guides/compile.md +++ b/doc/guides/compile.md @@ -241,13 +241,15 @@ conf.enable_bintest ### C++ ABI -mruby can use C++ exception to raise exception internally. -By using C++ exception it can release C++ stack object correctly. +By default, mruby uses setjmp/longjmp to implement its +exceptions. But it doesn't release C++ stack object +correctly. To support mrbgems written in C++, mruby can be +configured to use C++ exception. There are two levels of C++ exception handling. The one is -C++ exception enabled (but still C files are compiled by C -compiler), and the other is C++ ABI mode where all files are -compiled by C++ compiler. +```enable_cxx_exception``` that enables C++ exception, but +uses C ABI. The other is ```enable_cxx_abi``` where all +files are compiled by C++ compiler. When you mix C++ code, C++ exception would be enabled automatically. If you need to enable C++ exception explicitly add the following: @@ -255,23 +257,16 @@ If you need to enable C++ exception explicitly add the following: conf.enable_cxx_exception ``` -If you need to enable C++ ABI mode explicitly add the following: -```ruby -conf.enable_cxx_abi -``` - #### C++ exception disabling. - -If you need to force C++ exception disable -(For example using a compiler option to disable C++ exception), -but still want to use C++ ABI mode, -add following: +If your compiler does not support C++ and you want to ensure +you don't use mrbgem written in C++, you can explicitly disable +C++ exception, add following: ```ruby conf.disable_cxx_exception ``` - -Note that it must be called before ```enable_cxx_abi``` or ```gem``` method. +and you will get an error when you try to use C++ gem. +Note that it must be called before ```enable_cxx_exception``` or ```gem``` method. ### Debugging mode diff --git a/include/mruby/common.h b/include/mruby/common.h index 338044c2f..160639922 100644 --- a/include/mruby/common.h +++ b/include/mruby/common.h @@ -9,7 +9,7 @@ #ifdef __cplusplus -#ifdef MRB_ENABLE_CXX_EXCEPTION +#ifdef MRB_ENABLE_CXX_ABI #define MRB_BEGIN_DECL #define MRB_END_DECL #else diff --git a/include/mruby/throw.h b/include/mruby/throw.h index 010793027..5d3d214e7 100644 --- a/include/mruby/throw.h +++ b/include/mruby/throw.h @@ -7,8 +7,10 @@ #ifndef MRB_THROW_H #define MRB_THROW_H -#if defined(MRB_ENABLE_CXX_EXCEPTION) && !defined(__cplusplus) -#error Trying to use C++ exception handling in C code +#if defined(MRB_ENABLE_CXX_ABI) +# if !defined(__cplusplus) +# error Trying to use C++ exception handling in C code +# endif #endif #if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus) diff --git a/mrbgems/mruby-bin-mruby/mrbgem.rake b/mrbgems/mruby-bin-mruby/mrbgem.rake index ba7fad1fa..243b413eb 100644 --- a/mrbgems/mruby-bin-mruby/mrbgem.rake +++ b/mrbgems/mruby-bin-mruby/mrbgem.rake @@ -4,4 +4,10 @@ MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec| spec.summary = 'mruby command' spec.bins = %w(mruby) spec.add_dependency('mruby-compiler', :core => 'mruby-compiler') + spec.add_dependency('mruby-error', :core => 'mruby-error') + + if build.cxx_exception_enabled? + @objs << build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c", "#{spec.build_dir}/tools/mruby/mruby.cxx") + @objs.delete_if { |v| v == objfile("#{spec.build_dir}/tools/mruby/mruby") } + end end diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c index d10535140..d0e5dc8dc 100644 --- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c @@ -6,7 +6,6 @@ #include <mruby/compile.h> #include <mruby/dump.h> #include <mruby/variable.h> -#include <mruby/throw.h> #ifdef MRB_DISABLE_STDIO static void @@ -177,8 +176,6 @@ main(int argc, char **argv) mrbc_context *c; mrb_value v; mrb_sym zero_sym; - struct mrb_jmpbuf c_jmp; - int ai; if (mrb == NULL) { fputs("Invalid mrb_state, exiting mruby\n", stderr); @@ -191,10 +188,8 @@ main(int argc, char **argv) usage(argv[0]); return n; } - - ai = mrb_gc_arena_save(mrb); - MRB_TRY(&c_jmp) { - mrb->jmp = &c_jmp; + else { + int ai = mrb_gc_arena_save(mrb); ARGV = mrb_ary_new_capa(mrb, args.argc); for (i = 0; i < args.argc; i++) { char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); @@ -250,9 +245,6 @@ main(int argc, char **argv) printf("Syntax OK\n"); } } - MRB_CATCH(&c_jmp) { /* error */ - } - MRB_END_EXC(&c_jmp); cleanup(mrb, &args); return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE; diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 37f8201ec..c53abde7f 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -1814,6 +1814,8 @@ codegen(codegen_scope *s, node *tree, int val) for (i=0; i<nargs; i++) { genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1)); } + push_n(nargs+1); + pop_n(nargs+1); callargs = nargs; } else { diff --git a/mrbgems/mruby-compiler/mrbgem.rake b/mrbgems/mruby-compiler/mrbgem.rake index 3a22762fa..3bf6d6ae3 100644 --- a/mrbgems/mruby-compiler/mrbgem.rake +++ b/mrbgems/mruby-compiler/mrbgem.rake @@ -8,11 +8,11 @@ MRuby::Gem::Specification.new 'mruby-compiler' do |spec| lex_def = "#{current_dir}/core/lex.def" core_objs = Dir.glob("#{current_dir}/core/*.c").map { |f| - next nil if build.cxx_abi_enabled? and f =~ /(codegen).c$/ + next nil if build.cxx_exception_enabled? and f =~ /(codegen).c$/ objfile(f.pathmap("#{current_build_dir}/core/%n")) }.compact - if build.cxx_abi_enabled? + if build.cxx_exception_enabled? core_objs << build.compile_as_cxx("#{current_build_dir}/core/y.tab.c", "#{current_build_dir}/core/y.tab.cxx", objfile("#{current_build_dir}/y.tab"), ["#{current_dir}/core"]) << diff --git a/mrbgems/mruby-error/mrbgem.rake b/mrbgems/mruby-error/mrbgem.rake index b8281b17e..30a4259a8 100644 --- a/mrbgems/mruby-error/mrbgem.rake +++ b/mrbgems/mruby-error/mrbgem.rake @@ -3,7 +3,7 @@ MRuby::Gem::Specification.new('mruby-error') do |spec| spec.author = 'mruby developers' spec.summary = 'extensional error handling' - if build.cxx_abi_enabled? + if build.cxx_exception_enabled? @objs << build.compile_as_cxx("#{spec.dir}/src/exception.c", "#{spec.build_dir}/src/exception.cxx") @objs.delete_if { |v| v == objfile("#{spec.build_dir}/src/exception") } end diff --git a/src/backtrace.c b/src/backtrace.c index 529b0b1c9..deb3f8469 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -229,10 +229,11 @@ print_backtrace(mrb_state *mrb, mrb_value backtrace) static void print_backtrace_saved(mrb_state *mrb) { - int i; + int i, ai; FILE *stream = stderr; fprintf(stream, "trace:\n"); + ai = mrb_gc_arena_save(mrb); for (i = 0; i < mrb->backtrace.n; i++) { mrb_backtrace_entry *entry; @@ -252,6 +253,7 @@ print_backtrace_saved(mrb_state *mrb) else { fprintf(stream, ":in %s", method_name); } + mrb_gc_arena_restore(mrb, ai); } fprintf(stream, "\n"); diff --git a/src/error.c b/src/error.c index 3fa18fcb3..931361763 100644 --- a/src/error.c +++ b/src/error.c @@ -519,12 +519,15 @@ MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...) { mrb_value exc; + mrb_value argv[3]; va_list ap; va_start(ap, fmt); - exc = mrb_funcall(mrb, mrb_obj_value(E_NOMETHOD_ERROR), "new", 3, - mrb_vformat(mrb, fmt, ap), mrb_symbol_value(id), args); + argv[0] = mrb_vformat(mrb, fmt, ap); + argv[1] = mrb_symbol_value(id); + argv[2] = args; va_end(ap); + exc = mrb_obj_new(mrb, E_NOMETHOD_ERROR, 3, argv); mrb_exc_raise(mrb, exc); } @@ -639,6 +639,7 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) struct REnv *e = (struct REnv*)obj; mrb_int i, len; + if MRB_ENV_STACK_SHARED_P(e) break; len = MRB_ENV_STACK_LEN(e); for (i=0; i<len; i++) { mrb_gc_mark_value(mrb, e->stack[i]); diff --git a/src/kernel.c b/src/kernel.c index 7497f955b..3377f5a11 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -969,7 +969,7 @@ mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args) * r.xxiii #=> 23 * r.mm #=> 2000 */ -#ifndef MRB_DEFAULT_METHOD_MISSING +#ifdef MRB_DEFAULT_METHOD_MISSING static mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod) { @@ -1216,7 +1216,7 @@ mrb_init_kernel(mrb_state *mrb) mrb_define_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.25 */ mrb_define_method(mrb, krn, "kind_of?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.26 */ mrb_define_method(mrb, krn, "local_variables", mrb_local_variables, MRB_ARGS_NONE()); /* 15.3.1.3.28 */ -#ifndef MRB_DEFAULT_METHOD_MISSING +#ifdef MRB_DEFAULT_METHOD_MISSING mrb_define_method(mrb, krn, "method_missing", mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */ #endif mrb_define_method(mrb, krn, "methods", mrb_obj_methods_m, MRB_ARGS_OPT(1)); /* 15.3.1.3.31 */ diff --git a/src/mruby_core.rake b/src/mruby_core.rake index abde441d5..4558493d9 100644 --- a/src/mruby_core.rake +++ b/src/mruby_core.rake @@ -4,11 +4,11 @@ MRuby.each_target do current_build_dir = "#{build_dir}/#{relative_from_root}" objs = Dir.glob("#{current_dir}/*.c").map { |f| - next nil if cxx_abi_enabled? and f =~ /(error|vm).c$/ + next nil if cxx_exception_enabled? and f =~ /(error|vm).c$/ objfile(f.pathmap("#{current_build_dir}/%n")) }.compact - if cxx_abi_enabled? + if cxx_exception_enabled? objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" } end self.libmruby << objs @@ -292,6 +292,9 @@ ecall(mrb_state *mrb, int i) ptrdiff_t nstk; if (i<0) return; + if (mrb->c->ci - mrb->c->cibase > MRB_FUNCALL_DEPTH_MAX) { + mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); + } p = mrb->c->ensure[i]; if (!p) return; if (mrb->c->ci->eidx > i) @@ -1188,19 +1191,13 @@ RETRY_TRY_BLOCK: mrb_method_missing(mrb, mid, recv, args); } mid = missing; - if (n == CALL_MAXARGS-1) { + if (n != CALL_MAXARGS) { + mrb_value blk = regs[bidx]; regs[a+1] = mrb_ary_new_from_values(mrb, n, regs+a+1); - SET_NIL_VALUE(regs[bidx]); - n++; - } - if (n == CALL_MAXARGS) { - mrb_ary_unshift(mrb, regs[a+1], sym); - } - else { - value_move(regs+a+2, regs+a+1, n+1); - regs[a+1] = sym; - n++; + regs[a+2] = blk; + n = CALL_MAXARGS; } + mrb_ary_unshift(mrb, regs[a+1], sym); } /* push callinfo */ @@ -1387,6 +1384,8 @@ RETRY_TRY_BLOCK: ci->mid = mid; ci->proc = m; ci->stackent = mrb->c->stack; + ci->target_class = c; + ci->pc = pc + 1; { int bidx; mrb_value blk; @@ -1402,10 +1401,9 @@ RETRY_TRY_BLOCK: blk = regs[bidx]; if (!mrb_nil_p(blk) && mrb_type(blk) != MRB_TT_PROC) { regs[bidx] = mrb_convert_type(mrb, blk, MRB_TT_PROC, "Proc", "to_proc"); + ci = mrb->c->ci; } } - ci->target_class = c; - ci->pc = pc + 1; /* prepare stack */ mrb->c->stack += a; @@ -1678,6 +1676,9 @@ RETRY_TRY_BLOCK: ce = mrb->c->cibase + e->cioff; while (--ci > ce) { + if (ci->env) { + mrb_env_unshare(mrb, ci->env); + } if (ci->acc < 0) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; @@ -1729,6 +1730,9 @@ RETRY_TRY_BLOCK: mrb->c->ci = ci; break; } + if (ci->env) { + mrb_env_unshare(mrb, ci->env); + } ci--; } break; @@ -2582,3 +2586,13 @@ mrb_top_run(mrb_state *mrb, struct RProc *proc, mrb_value self, unsigned int sta return v; } + +#if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus) +# if !defined(MRB_ENABLE_CXX_ABI) +} /* end of extern "C" */ +# endif +mrb_int mrb_jmpbuf::jmpbuf_id = 0; +# if !defined(MRB_ENABLE_CXX_ABI) +extern "C" { +# endif +#endif diff --git a/tasks/mruby_build.rake b/tasks/mruby_build.rake index 46459cf6c..f5d4374d1 100644 --- a/tasks/mruby_build.rake +++ b/tasks/mruby_build.rake @@ -83,8 +83,9 @@ module MRuby @bins = [] @gems, @libmruby = MRuby::Gem::List.new, [] @build_mrbtest_lib_only = false - @cxx_abi_enabled = false + @cxx_exception_enabled = false @cxx_exception_disabled = false + @cxx_abi_enabled = false @enable_bintest = false @enable_test = false @toolchains = [] @@ -110,15 +111,28 @@ module MRuby end def disable_cxx_exception + if @cxx_exception_enabled or @cxx_abi_enabled + raise "cxx_exception already enabled" + end @cxx_exception_disabled = true end def enable_cxx_exception - @cxx_exception_disabled = false + return if @cxx_exception_enabled + return if @cxx_abi_enabled + if @cxx_exception_disabled + raise "cxx_exception disabled" + end + @cxx_exception_enabled = true compilers.each { |c| c.defines += %w(MRB_ENABLE_CXX_EXCEPTION) c.flags << c.cxx_exception_flag } + linker.command = cxx.command if toolchains.find { |v| v == 'gcc' } + end + + def cxx_exception_enabled? + @cxx_exception_enabled end def cxx_abi_enabled? @@ -127,9 +141,13 @@ module MRuby def enable_cxx_abi return if @cxx_abi_enabled - unless @cxx_exception_disabled - enable_cxx_exception + if @cxx_exception_enabled + raise "cxx_exception already enabled" end + compilers.each { |c| + c.defines += %w(MRB_ENABLE_CXX_EXCEPTION MRB_ENABLE_CXX_ABI) + c.flags << c.cxx_compile_flag + } compilers.each { |c| c.flags << c.cxx_compile_flag } linker.command = cxx.command if toolchains.find { |v| v == 'gcc' } @cxx_abi_enabled = true @@ -146,15 +164,13 @@ module MRuby #define __STDC_CONSTANT_MACROS #define __STDC_LIMIT_MACROS -#ifndef MRB_ENABLE_CXX_EXCEPTION +#ifndef MRB_ENABLE_CXX_ABI extern "C" { #endif #include "#{src}" -#ifndef MRB_ENABLE_CXX_EXCEPTION +#ifndef MRB_ENABLE_CXX_ABI } #endif - -#{src == "#{MRUBY_ROOT}/src/error.c"? 'mrb_int mrb_jmpbuf::jmpbuf_id = 0;' : ''} EOS end diff --git a/test/t/nomethoderror.rb b/test/t/nomethoderror.rb index 22a5f8a0e..5fed79689 100644 --- a/test/t/nomethoderror.rb +++ b/test/t/nomethoderror.rb @@ -20,21 +20,3 @@ assert('NoMethodError#args', '15.2.32.2.1') do end end end - -assert("NoMethodError#new does not return an exception") do - begin - class << NoMethodError - def new(*) - nil - end - end - - assert_raise(TypeError) do - Object.q - end - ensure - class << NoMethodError - remove_method :new - end - end -end |
