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

ruby-changes:58041

From: Jeremy <ko1@a...>
Date: Mon, 30 Sep 2019 11:08:14 +0900 (JST)
Subject: [ruby-changes:58041] 649a64ae29 (master): Add three more C-API functions for handling keywords

https://git.ruby-lang.org/ruby.git/commit/?id=649a64ae29

From 649a64ae29318501472f74798a12eb60871ab990 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Sun, 29 Sep 2019 17:47:17 -0700
Subject: Add three more C-API functions for handling keywords

This adds rb_funcall_passing_block_kw, rb_funcallv_public_kw,
and rb_yield_splat_kw.  This functions are necessary to easily
handle cases where rb_funcall_passing_block, rb_funcallv_public,
and rb_yield_splat are currently used and a keyword argument
separation warning is raised.

diff --git a/ext/-test-/funcall/funcall.c b/ext/-test-/funcall/funcall.c
index 4e13c95..43521bf 100644
--- a/ext/-test-/funcall/funcall.c
+++ b/ext/-test-/funcall/funcall.c
@@ -1,7 +1,5 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/funcall/funcall.c#L1
 #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) https://github.com/ruby/ruby/blob/trunk/ext/-test-/funcall/funcall.c#L13
 }
 
 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) https://github.com/ruby/ruby/blob/trunk/ext/-test-/funcall/funcall.c#L51
 			       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 ca238dd..2ec0cc8 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -1893,9 +1893,11 @@ VALUE rb_funcall(VALUE, ID, int, ...); https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1893
 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, ...); https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h#L1974
 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 5112bc0..c2d7639 100644
--- a/test/-ext-/funcall/test_passing_block.rb
+++ b/test/-ext-/funcall/test_passing_block.rb
@@ -3,8 +3,8 @@ require 'test/unit' https://github.com/ruby/ruby/blob/trunk/test/-ext-/funcall/test_passing_block.rb#L3
 
 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 https://github.com/ruby/ruby/blob/trunk/test/-ext-/funcall/test_passing_block.rb#L20
     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 f6a1cc0..c741773 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) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L989
     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) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1042
 }
 
 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) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1299
 }
 
 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);
-- 
cgit v0.10.2


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

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