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/