aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ext/-test-/funcall/funcall.c32
-rw-r--r--include/ruby/ruby.h3
-rw-r--r--test/-ext-/funcall/test_passing_block.rb56
-rw-r--r--vm_eval.c33
4 files changed, 120 insertions, 4 deletions
diff --git a/ext/-test-/funcall/funcall.c b/ext/-test-/funcall/funcall.c
index 4e13c952e5..43521bf2e9 100644
--- a/ext/-test-/funcall/funcall.c
+++ b/ext/-test-/funcall/funcall.c
@@ -1,7 +1,5 @@
#include "ruby.h"
-VALUE rb_funcall_passing_block(VALUE, ID, int, const VALUE*);
-
static VALUE
with_funcall2(int argc, VALUE *argv, VALUE self)
{
@@ -15,6 +13,24 @@ with_funcall_passing_block(int argc, VALUE *argv, VALUE self)
}
static VALUE
+with_funcall_passing_block_kw(int argc, VALUE *argv, VALUE self)
+{
+ return rb_funcall_passing_block_kw(self, rb_intern("target"), argc-1, argv+1, FIX2INT(argv[0]));
+}
+
+static VALUE
+with_funcallv_public_kw(int argc, VALUE *argv, VALUE self)
+{
+ return rb_funcallv_public_kw(argv[0], SYM2ID(argv[1]), argc-3, argv+3, FIX2INT(argv[2]));
+}
+
+static VALUE
+with_yield_splat_kw(int argc, VALUE *argv, VALUE self)
+{
+ return rb_yield_splat_kw(argv[1], FIX2INT(argv[0]));
+}
+
+static VALUE
extra_args_name(VALUE self)
{
/*
@@ -35,9 +51,21 @@ Init_funcall(void)
with_funcall2,
-1);
rb_define_singleton_method(cRelay,
+ "with_funcall_passing_block_kw",
+ with_funcall_passing_block_kw,
+ -1);
+ rb_define_singleton_method(cRelay,
"with_funcall_passing_block",
with_funcall_passing_block,
-1);
+ rb_define_singleton_method(cRelay,
+ "with_funcallv_public_kw",
+ with_funcallv_public_kw,
+ -1);
+ rb_define_singleton_method(cRelay,
+ "with_yield_splat_kw",
+ with_yield_splat_kw,
+ -1);
rb_define_singleton_method(cTestFuncall, "extra_args_name",
extra_args_name,
0);
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index ca238dda64..2ec0cc84ae 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -1893,9 +1893,11 @@ VALUE rb_funcall(VALUE, ID, int, ...);
VALUE rb_funcallv(VALUE, ID, int, const VALUE*);
VALUE rb_funcallv_kw(VALUE, ID, int, const VALUE*, int);
VALUE rb_funcallv_public(VALUE, ID, int, const VALUE*);
+VALUE rb_funcallv_public_kw(VALUE, ID, int, const VALUE*, int);
#define rb_funcall2 rb_funcallv
#define rb_funcall3 rb_funcallv_public
VALUE rb_funcall_passing_block(VALUE, ID, int, const VALUE*);
+VALUE rb_funcall_passing_block_kw(VALUE, ID, int, const VALUE*, int);
VALUE rb_funcall_with_block(VALUE, ID, int, const VALUE*, VALUE);
VALUE rb_funcall_with_block_kw(VALUE, ID, int, const VALUE*, VALUE, int);
int rb_scan_args(int, const VALUE*, const char*, ...);
@@ -1972,6 +1974,7 @@ VALUE rb_yield_values(int n, ...);
VALUE rb_yield_values2(int n, const VALUE *argv);
VALUE rb_yield_values_kw(int n, const VALUE *argv, int kw_splat);
VALUE rb_yield_splat(VALUE);
+VALUE rb_yield_splat_kw(VALUE, int);
VALUE rb_yield_block(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)); /* rb_block_call_func */
#define RB_NO_KEYWORDS 0
#define RB_PASS_KEYWORDS 1
diff --git a/test/-ext-/funcall/test_passing_block.rb b/test/-ext-/funcall/test_passing_block.rb
index 5112bc0925..c2d7639b52 100644
--- a/test/-ext-/funcall/test_passing_block.rb
+++ b/test/-ext-/funcall/test_passing_block.rb
@@ -3,8 +3,8 @@ require 'test/unit'
class TestFuncall < Test::Unit::TestCase
module Relay
- def self.target(*args, &block)
- yield(*args) if block
+ def self.target(*args, **kw, &block)
+ yield(*args, **kw) if block
end
end
require '-test-/funcall'
@@ -20,4 +20,56 @@ class TestFuncall < Test::Unit::TestCase
Relay.with_funcall_passing_block("feature#4504") {|arg| ok = arg || true}
assert_equal("feature#4504", ok)
end
+
+ def test_with_funcall_passing_block_kw
+ block = ->(*a, **kw) { [a, kw] }
+ assert_equal([[1], {}], Relay.with_funcall_passing_block_kw(0, 1, &block))
+ assert_equal([[], {a: 1}], Relay.with_funcall_passing_block_kw(1, a: 1, &block))
+ assert_equal([[1], {a: 1}], Relay.with_funcall_passing_block_kw(1, 1, a: 1, &block))
+ assert_equal([[{}], {}], Relay.with_funcall_passing_block_kw(2, {}, **{}, &block))
+ assert_equal([[], {a: 1}], Relay.with_funcall_passing_block_kw(3, a: 1, &block))
+ assert_equal([[{a: 1}], {}], Relay.with_funcall_passing_block_kw(3, {a: 1}, **{}, &block))
+ assert_warn(/warning: The keyword argument is passed as the last hash parameter.*for method/m) do
+ assert_equal({}, Relay.with_funcall_passing_block_kw(3, **{}, &->(a){a}))
+ end
+ end
+
+ def test_with_funcallv_public_kw
+ o = Object.new
+ def o.foo(*args, **kw)
+ [args, kw]
+ end
+ def o.bar(*args, **kw)
+ [args, kw]
+ end
+ o.singleton_class.send(:private, :bar)
+ def o.baz(arg)
+ arg
+ end
+ assert_equal([[1], {}], Relay.with_funcallv_public_kw(o, :foo, 0, 1))
+ assert_equal([[], {a: 1}], Relay.with_funcallv_public_kw(o, :foo, 1, a: 1))
+ assert_equal([[1], {a: 1}], Relay.with_funcallv_public_kw(o, :foo, 1, 1, a: 1))
+ assert_equal([[{}], {}], Relay.with_funcallv_public_kw(o, :foo, 2, {}, **{}))
+ assert_equal([[], {a: 1}], Relay.with_funcallv_public_kw(o, :foo, 3, a: 1))
+ assert_equal([[{a: 1}], {}], Relay.with_funcallv_public_kw(o, :foo, 3, {a: 1}, **{}))
+ assert_raise(NoMethodError) { Relay.with_funcallv_public_kw(o, :bar, 3, {a: 1}, **{}) }
+ assert_warn(/warning: The keyword argument is passed as the last hash parameter.*for `baz'/m) do
+ assert_equal({}, Relay.with_funcallv_public_kw(o, :baz, 3, **{}))
+ end
+ end
+
+ def test_with_yield_splat_kw
+ block = ->(*a, **kw) { [a, kw] }
+ assert_equal([[1], {}], Relay.with_yield_splat_kw(0, [1], &block))
+ assert_equal([[], {a: 1}], Relay.with_yield_splat_kw(1, [{a: 1}], &block))
+ assert_equal([[1], {a: 1}], Relay.with_yield_splat_kw(1, [1, {a: 1}], &block))
+ assert_equal([[{}], {}], Relay.with_yield_splat_kw(2, [{}], **{}, &block))
+ assert_warn(/warning: The last argument is used as the keyword parameter.*for method/m) do
+ assert_equal([[], {a: 1}], Relay.with_yield_splat_kw(3, [{a: 1}], &block))
+ end
+ assert_equal([[{a: 1}], {}], Relay.with_yield_splat_kw(3, [{a: 1}], **{}, &block))
+ assert_warn(/warning: The keyword argument is passed as the last hash parameter/) do
+ assert_equal({}, Relay.with_yield_splat_kw(3, [], **{}, &->(a){a}))
+ end
+ end
end
diff --git a/vm_eval.c b/vm_eval.c
index f6a1cc035e..c741773657 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -989,6 +989,15 @@ rb_funcallv_public(VALUE recv, ID mid, int argc, const VALUE *argv)
return rb_call(recv, mid, argc, argv, CALL_PUBLIC);
}
+VALUE
+rb_funcallv_public_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
+{
+ VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
+ VALUE ret = rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
+ rb_free_tmp_buffer(&v);
+ return ret;
+}
+
/*!
* Calls a method
* \private
@@ -1033,6 +1042,17 @@ rb_funcall_passing_block(VALUE recv, ID mid, int argc, const VALUE *argv)
}
VALUE
+rb_funcall_passing_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
+{
+ VALUE v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
+ VALUE ret;
+ PASS_PASSED_BLOCK_HANDLER();
+ ret = rb_call(recv, mid, argc, argv, kw_splat ? CALL_PUBLIC_KW : CALL_PUBLIC);
+ rb_free_tmp_buffer(&v);
+ return ret;
+}
+
+VALUE
rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE passed_procval)
{
if (!NIL_P(passed_procval)) {
@@ -1279,6 +1299,19 @@ rb_yield_splat(VALUE values)
}
VALUE
+rb_yield_splat_kw(VALUE values, int kw_splat)
+{
+ VALUE tmp = rb_check_array_type(values);
+ VALUE v;
+ if (NIL_P(tmp)) {
+ rb_raise(rb_eArgError, "not an array");
+ }
+ v = rb_yield_0_kw(RARRAY_LENINT(tmp), RARRAY_CONST_PTR(tmp), kw_splat);
+ RB_GC_GUARD(tmp);
+ return v;
+}
+
+VALUE
rb_yield_force_blockarg(VALUE values)
{
return vm_yield_force_blockarg(GET_EC(), values);