summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--doc/guides/compile.md29
-rw-r--r--include/mruby/common.h2
-rw-r--r--include/mruby/throw.h6
-rw-r--r--mrbgems/mruby-bin-mruby/mrbgem.rake6
-rw-r--r--mrbgems/mruby-bin-mruby/tools/mruby/mruby.c12
-rw-r--r--mrbgems/mruby-compiler/core/codegen.c2
-rw-r--r--mrbgems/mruby-compiler/mrbgem.rake4
-rw-r--r--mrbgems/mruby-error/mrbgem.rake2
-rw-r--r--src/backtrace.c4
-rw-r--r--src/error.c7
-rw-r--r--src/gc.c1
-rw-r--r--src/kernel.c4
-rw-r--r--src/mruby_core.rake4
-rw-r--r--src/vm.c40
-rw-r--r--tasks/mruby_build.rake32
-rw-r--r--test/t/nomethoderror.rb18
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);
}
diff --git a/src/gc.c b/src/gc.c
index 63eab8e00..5003f4b91 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -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
diff --git a/src/vm.c b/src/vm.c
index 043983a05..54877df23 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -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