summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/mruby/proc.h2
-rw-r--r--mrbgems/mruby-method/src/method.c7
-rw-r--r--mrbgems/mruby-method/test/method.rb9
-rw-r--r--src/vm.c28
-rw-r--r--test/t/argumenterror.rb5
-rw-r--r--test/t/kernel.rb3
6 files changed, 52 insertions, 2 deletions
diff --git a/include/mruby/proc.h b/include/mruby/proc.h
index 31f40d379..1ba485783 100644
--- a/include/mruby/proc.h
+++ b/include/mruby/proc.h
@@ -83,6 +83,8 @@ struct RProc {
} while (0)
#define MRB_PROC_SCOPE 2048
#define MRB_PROC_SCOPE_P(p) (((p)->flags & MRB_PROC_SCOPE) != 0)
+#define MRB_PROC_NOARG 4096 /* for MRB_PROC_CFUNC_FL, it would be something like MRB_ARGS_NONE() or MRB_METHOD_NOARG_FL */
+#define MRB_PROC_NOARG_P(p) (((p)->flags & MRB_PROC_NOARG) != 0)
#define mrb_proc_ptr(v) ((struct RProc*)(mrb_ptr(v)))
diff --git a/mrbgems/mruby-method/src/method.c b/mrbgems/mruby-method/src/method.c
index b8a55e618..18fcaa03a 100644
--- a/mrbgems/mruby-method/src/method.c
+++ b/mrbgems/mruby-method/src/method.c
@@ -300,7 +300,12 @@ method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
return NULL;
if (MRB_METHOD_PROC_P(m))
return MRB_METHOD_PROC(m);
- return mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m));
+
+ struct RProc *proc = mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m));
+ if (MRB_METHOD_NOARG_P(m)) {
+ proc->flags |= MRB_PROC_NOARG;
+ }
+ return proc;
}
static mrb_value
diff --git a/mrbgems/mruby-method/test/method.rb b/mrbgems/mruby-method/test/method.rb
index ba4d05e3c..9089c96d6 100644
--- a/mrbgems/mruby-method/test/method.rb
+++ b/mrbgems/mruby-method/test/method.rb
@@ -98,6 +98,9 @@ assert 'Method#call' do
}.new
assert_raise(LocalJumpError) { i.method(:bar).call }
assert_equal 3, i.method(:bar).call { |i| i }
+
+ assert_raise(ArgumentError) { nil.method(:__id__).call nil, 1 }
+ assert_raise(ArgumentError) { nil.method(:__id__).call nil, opts: 1 }
end
assert 'Method#call for regression' do
@@ -218,6 +221,9 @@ assert 'Method#to_proc' do
assert_equal values, o.method(:baz).to_proc.call(1, 2, 3, 4, 5, 6, **{ u: 7, v: 8, s: 9, t: 10 }, &blk)
assert_equal values, o.method(:baz).to_proc.call(*[1, 2, 3, 4, 5, 6], u: 7, v: 8, s: 9, t: 10, &blk)
assert_equal values, o.method(:baz).to_proc.call(*[1, 2, 3, 4, 5, 6], **{ u: 7, v: 8, s: 9, t: 10 }, &blk)
+
+ assert_raise(ArgumentError) { nil.method(:__id__).to_proc.call nil, 1 }
+ assert_raise(ArgumentError) { nil.method(:__id__).to_proc.call nil, opts: 1 }
end
assert 'to_s' do
@@ -472,4 +478,7 @@ assert 'UnboundMethod#bind_call' do
assert_equal values, m.bind_call(o, *[1, 2, 3, 4, 5, 6], u: 7, v: 8, s: 9, t: 10, &blk)
assert_equal values, m.bind_call(o, *[1, 2, 3, 4, 5, 6], **{ u: 7, v: 8, s: 9, t: 10 }, &blk)
assert_raise(ArgumentError) { m.bind_call }
+
+ assert_raise(ArgumentError) { BasicObject.instance_method(:__id__).bind_call nil, 1 }
+ assert_raise(ArgumentError) { BasicObject.instance_method(:__id__).bind_call nil, opts: 1 }
end
diff --git a/src/vm.c b/src/vm.c
index dabf12240..65af8dcc0 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -541,6 +541,18 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, cons
return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value());
}
+static void
+check_method_noarg(mrb_state *mrb, const mrb_callinfo *ci)
+{
+ int argc = ci->n == CALL_MAXARGS ? RARRAY_LEN(ci->stack[1]) : ci->n;
+ if (ci->nk != 0 && !mrb_hash_empty_p(mrb, ci->stack[ci->n == CALL_MAXARGS ? 2 : ci->n + 1])) {
+ argc++;
+ }
+ if (argc > 0) {
+ mrb_argnum_error(mrb, argc, 0, 0);
+ }
+}
+
static mrb_value
exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
{
@@ -550,6 +562,9 @@ exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
ci->stack[0] = self;
mrb_vm_ci_proc_set(ci, p);
if (MRB_PROC_CFUNC_P(p)) {
+ if (MRB_PROC_NOARG_P(p)) {
+ check_method_noarg(mrb, ci);
+ }
return MRB_PROC_CFUNC(p)(mrb, self);
}
nregs = p->body.irep->nregs;
@@ -577,6 +592,9 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
else {
mrb_value ret;
if (MRB_PROC_CFUNC_P(p)) {
+ if (MRB_PROC_NOARG_P(p)) {
+ check_method_noarg(mrb, ci);
+ }
cipush(mrb, 0, CINFO_DIRECT, mrb_vm_ci_target_class(ci), p, ci->mid, ci->n|(ci->nk<<4));
ret = MRB_PROC_CFUNC(p)(mrb, self);
cipop(mrb);
@@ -665,6 +683,10 @@ mrb_f_send(mrb_state *mrb, mrb_value self)
}
if (MRB_METHOD_CFUNC_P(m)) {
+ if (MRB_METHOD_NOARG_P(m)) {
+ check_method_noarg(mrb, ci);
+ }
+
if (MRB_METHOD_PROC_P(m)) {
mrb_vm_ci_proc_set(ci, MRB_METHOD_PROC(m));
}
@@ -938,6 +960,9 @@ argnum_error(mrb_state *mrb, mrb_int num)
argc = RARRAY_LEN(args);
}
}
+ if (argc == 0 && mrb->c->ci->nk != 0 && !mrb_hash_empty_p(mrb, mrb->c->ci->stack[1])) {
+ argc++;
+ }
if (mrb->c->ci->mid) {
str = mrb_format(mrb, "'%n': wrong number of arguments (%i for %i)",
mrb->c->ci->mid, argc, num);
@@ -1617,7 +1642,8 @@ RETRY_TRY_BLOCK:
recv = p->body.func(mrb, recv);
}
else if (MRB_METHOD_NOARG_P(m) &&
- !(n == 0 || (n == CALL_MAXARGS && RARRAY_LEN(regs[1]) == 0))) {
+ (!(n == 0 || (n == CALL_MAXARGS && RARRAY_LEN(regs[1]) == 0)) ||
+ !(nk == 0 || mrb_hash_empty_p(mrb, regs[n == CALL_MAXARGS ? 2 : n + 1])))) {
argnum_error(mrb, 0);
goto L_RAISE;
}
diff --git a/test/t/argumenterror.rb b/test/t/argumenterror.rb
index 3dcb29a4b..3772f1b0f 100644
--- a/test/t/argumenterror.rb
+++ b/test/t/argumenterror.rb
@@ -30,3 +30,8 @@ assert("'wrong number of arguments' from mrb_get_args") do
assert_argnum_error(1, 2){Object.const_set(:B)}
assert_argnum_error(3, 2){Object.const_set(:C, 1, 2)}
end
+
+assert('Call to MRB_ARGS_NONE method') do
+ assert_raise(ArgumentError) { nil.__id__ 1 }
+ assert_raise(ArgumentError) { nil.__id__ opts: 1 }
+end
diff --git a/test/t/kernel.rb b/test/t/kernel.rb
index 0a70ec0d2..06551ae88 100644
--- a/test/t/kernel.rb
+++ b/test/t/kernel.rb
@@ -95,6 +95,9 @@ assert('Kernel#__send__', '15.3.1.3.4') do
args = [:respond_to?, :nil?]
assert_true __send__(*args)
assert_equal [:respond_to?, :nil?], args
+
+ assert_raise(ArgumentError) { nil.__send__(:__id__, 1) }
+ assert_raise(ArgumentError) { nil.__send__(:__id__, opts: 1) }
end
assert('Kernel#block_given?', '15.3.1.3.6') do