[前][次][番号順一覧][スレッド一覧]

ruby-changes:57874

From: Jeremy <ko1@a...>
Date: Tue, 24 Sep 2019 01:28:46 +0900 (JST)
Subject: [ruby-changes:57874] 74e33662fe (master): Make public_send and rb_f_send handle keyword argument separation

https://git.ruby-lang.org/ruby.git/commit/?id=74e33662fe

From 74e33662fe987e5418fc277c8a7ba1f9805f8673 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Mon, 23 Sep 2019 08:44:38 -0700
Subject: Make public_send and rb_f_send handle keyword argument separation

Kernel#send takes a different optimized code path that was already
handled.

diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index 62ba0bd..1dbde80 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -1218,6 +1218,108 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L1218
     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}
diff --git a/vm_eval.c b/vm_eval.c
index 4b3cb47..0fd4f57 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1123,6 +1123,29 @@ send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1123
     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) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1173
 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) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1193
 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 */
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]