mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Make public_send and rb_f_send handle keyword argument separation
Kernel#send takes a different optimized code path that was already handled.
This commit is contained in:
parent
9e4be78ea8
commit
74e33662fe
Notes:
git
2019-09-24 01:29:00 +09:00
2 changed files with 127 additions and 2 deletions
|
@ -1218,6 +1218,108 @@ class TestKeywordArguments < Test::Unit::TestCase
|
|||
assert_equal([1, h3], c.send(:m, a: 1, **h2))
|
||||
end
|
||||
|
||||
def test_public_send_kwsplat
|
||||
kw = {}
|
||||
h = {:a=>1}
|
||||
h2 = {'a'=>1}
|
||||
h3 = {'a'=>1, :a=>1}
|
||||
|
||||
c = Object.new
|
||||
def c.m(*args)
|
||||
args
|
||||
end
|
||||
assert_equal([], c.public_send(:m, **{}))
|
||||
assert_equal([], c.public_send(:m, **kw))
|
||||
assert_equal([h], c.public_send(:m, **h))
|
||||
assert_equal([h], c.public_send(:m, a: 1))
|
||||
assert_equal([h2], c.public_send(:m, **h2))
|
||||
assert_equal([h3], c.public_send(:m, **h3))
|
||||
assert_equal([h3], c.public_send(:m, a: 1, **h2))
|
||||
|
||||
c.singleton_class.remove_method(:m)
|
||||
def c.m; end
|
||||
assert_nil(c.public_send(:m, **{}))
|
||||
assert_nil(c.public_send(:m, **kw))
|
||||
assert_raise(ArgumentError) { c.public_send(:m, **h) }
|
||||
assert_raise(ArgumentError) { c.public_send(:m, a: 1) }
|
||||
assert_raise(ArgumentError) { c.public_send(:m, **h2) }
|
||||
assert_raise(ArgumentError) { c.public_send(:m, **h3) }
|
||||
assert_raise(ArgumentError) { c.public_send(:m, a: 1, **h2) }
|
||||
|
||||
c.singleton_class.remove_method(:m)
|
||||
def c.m(args)
|
||||
args
|
||||
end
|
||||
assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
|
||||
assert_equal(kw, c.public_send(:m, **{}))
|
||||
end
|
||||
assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
|
||||
assert_equal(kw, c.public_send(:m, **kw))
|
||||
end
|
||||
assert_equal(h, c.public_send(:m, **h))
|
||||
assert_equal(h, c.public_send(:m, a: 1))
|
||||
assert_equal(h2, c.public_send(:m, **h2))
|
||||
assert_equal(h3, c.public_send(:m, **h3))
|
||||
assert_equal(h3, c.public_send(:m, a: 1, **h2))
|
||||
|
||||
c.singleton_class.remove_method(:m)
|
||||
def c.m(**args)
|
||||
args
|
||||
end
|
||||
assert_equal(kw, c.public_send(:m, **{}))
|
||||
assert_equal(kw, c.public_send(:m, **kw))
|
||||
assert_equal(h, c.public_send(:m, **h))
|
||||
assert_equal(h, c.public_send(:m, a: 1))
|
||||
assert_equal(h2, c.public_send(:m, **h2))
|
||||
assert_equal(h3, c.public_send(:m, **h3))
|
||||
assert_equal(h3, c.public_send(:m, a: 1, **h2))
|
||||
assert_warn(/The last argument is used as the keyword parameter.*for `m'/m) do
|
||||
assert_equal(h, c.public_send(:m, h))
|
||||
end
|
||||
assert_raise(ArgumentError) { c.public_send(:m, h2) }
|
||||
assert_warn(/The last argument is split into positional and keyword parameters.*for `m'/m) do
|
||||
assert_raise(ArgumentError) { c.public_send(:m, h3) }
|
||||
end
|
||||
|
||||
c.singleton_class.remove_method(:m)
|
||||
def c.m(arg, **args)
|
||||
[arg, args]
|
||||
end
|
||||
assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
|
||||
c.public_send(:m, **{})
|
||||
end
|
||||
assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
|
||||
c.public_send(:m, **kw)
|
||||
end
|
||||
assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
|
||||
assert_equal([h, kw], c.public_send(:m, **h))
|
||||
end
|
||||
assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
|
||||
assert_equal([h, kw], c.public_send(:m, a: 1))
|
||||
end
|
||||
assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
|
||||
assert_equal([h2, kw], c.public_send(:m, **h2))
|
||||
end
|
||||
assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
|
||||
assert_equal([h3, kw], c.public_send(:m, **h3))
|
||||
end
|
||||
assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
|
||||
assert_equal([h3, kw], c.public_send(:m, a: 1, **h2))
|
||||
end
|
||||
|
||||
c.singleton_class.remove_method(:m)
|
||||
def c.m(arg=1, **args)
|
||||
[arg, args]
|
||||
end
|
||||
assert_equal([1, kw], c.public_send(:m, **{}))
|
||||
assert_equal([1, kw], c.public_send(:m, **kw))
|
||||
assert_equal([1, h], c.public_send(:m, **h))
|
||||
assert_equal([1, h], c.public_send(:m, a: 1))
|
||||
assert_equal([1, h2], c.public_send(:m, **h2))
|
||||
assert_equal([1, h3], c.public_send(:m, **h3))
|
||||
assert_equal([1, h3], c.public_send(:m, a: 1, **h2))
|
||||
end
|
||||
|
||||
def test_send_method_kwsplat
|
||||
kw = {}
|
||||
h = {:a=>1}
|
||||
|
|
27
vm_eval.c
27
vm_eval.c
|
@ -1123,6 +1123,29 @@ send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
send_internal_kw(int argc, const VALUE *argv, VALUE recv, call_type scope)
|
||||
{
|
||||
VALUE v=0, ret;
|
||||
int kw_splat = RB_PASS_CALLED_KEYWORDS;
|
||||
v = rb_adjust_argv_kw_splat(&argc, &argv, &kw_splat);
|
||||
if (kw_splat) {
|
||||
switch (scope) {
|
||||
case CALL_PUBLIC:
|
||||
scope = CALL_PUBLIC_KW;
|
||||
break;
|
||||
case CALL_FCALL:
|
||||
scope = CALL_FCALL_KW;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = send_internal(argc, argv, recv, scope);
|
||||
rb_free_tmp_buffer(&v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* foo.send(symbol [, args...]) -> obj
|
||||
|
@ -1150,7 +1173,7 @@ send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope)
|
|||
VALUE
|
||||
rb_f_send(int argc, VALUE *argv, VALUE recv)
|
||||
{
|
||||
return send_internal(argc, argv, recv, CALL_FCALL);
|
||||
return send_internal_kw(argc, argv, recv, CALL_FCALL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1170,7 +1193,7 @@ rb_f_send(int argc, VALUE *argv, VALUE recv)
|
|||
static VALUE
|
||||
rb_f_public_send(int argc, VALUE *argv, VALUE recv)
|
||||
{
|
||||
return send_internal(argc, argv, recv, CALL_PUBLIC);
|
||||
return send_internal_kw(argc, argv, recv, CALL_PUBLIC);
|
||||
}
|
||||
|
||||
/* yield */
|
||||
|
|
Loading…
Reference in a new issue