1
0
Fork 0
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:
Jeremy Evans 2019-09-23 08:44:38 -07:00
parent 9e4be78ea8
commit 74e33662fe
Notes: git 2019-09-24 01:29:00 +09:00
2 changed files with 127 additions and 2 deletions

View file

@ -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}

View file

@ -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 */