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

ruby-changes:58151

From: Jeremy <ko1@a...>
Date: Mon, 7 Oct 2019 23:37:34 +0900 (JST)
Subject: [ruby-changes:58151] 468184a996 (master): Allow ruby2_keywords to be used with bmethods

https://git.ruby-lang.org/ruby.git/commit/?id=468184a996

From 468184a996c99d1f94f94d7468f65e386cf13564 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Sun, 6 Oct 2019 21:18:20 -0700
Subject: Allow ruby2_keywords to be used with bmethods

There are libraries that use define_method with argument splats
where they would like to pass keywords through the method. To
more easily allow such libraries to use ruby2_keywords to handle
backwards compatibility, it is necessary for ruby2_keywords to
support bmethods.

diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index 1a445cd..e5c6da4 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -2637,6 +2637,10 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2637
         send(meth, *args)
       end
 
+      ruby2_keywords(define_method(:bfoo) do |meth, *args|
+        send(meth, *args)
+      end)
+
       ruby2_keywords def foo_bar(*args)
         bar(*args)
       end
@@ -2743,6 +2747,8 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2747
 
     assert_equal([[1], h1], o.foo(:bar, 1, :a=>1))
     assert_equal([1, h1], o.foo(:baz, 1, :a=>1))
+    assert_equal([[1], h1], o.bfoo(:bar, 1, :a=>1))
+    assert_equal([1, h1], o.bfoo(:baz, 1, :a=>1))
     assert_equal([[1], h1], o.store_foo(:bar, 1, :a=>1))
     assert_equal([1, h1], o.store_foo(:baz, 1, :a=>1))
     assert_equal([[1], h1], o.foo_bar(1, :a=>1))
@@ -2750,6 +2756,8 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2756
 
     assert_equal([[1], h1], o.foo(:bar, 1, **h1))
     assert_equal([1, h1], o.foo(:baz, 1, **h1))
+    assert_equal([[1], h1], o.bfoo(:bar, 1, **h1))
+    assert_equal([1, h1], o.bfoo(:baz, 1, **h1))
     assert_equal([[1], h1], o.store_foo(:bar, 1, **h1))
     assert_equal([1, h1], o.store_foo(:baz, 1, **h1))
     assert_equal([[1], h1], o.foo_bar(1, **h1))
@@ -2757,6 +2765,8 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2765
 
     assert_equal([[h1], {}], o.foo(:bar, h1, **{}))
     assert_equal([h1], o.foo(:baz, h1, **{}))
+    assert_equal([[h1], {}], o.bfoo(:bar, h1, **{}))
+    assert_equal([h1], o.bfoo(:baz, h1, **{}))
     assert_equal([[h1], {}], o.store_foo(:bar, h1, **{}))
     assert_equal([h1], o.store_foo(:baz, h1, **{}))
     assert_equal([[h1], {}], o.foo_bar(h1, **{}))
@@ -2767,6 +2777,10 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2777
     end
     assert_equal([1, h1], o.foo(:baz, 1, h1))
     assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
+      assert_equal([[1], h1], o.bfoo(:bar, 1, h1))
+    end
+    assert_equal([1, h1], o.bfoo(:baz, 1, h1))
+    assert_warn(/The last argument is used as the keyword parameter.* for `bar'/m) do
       assert_equal([[1], h1], o.store_foo(:bar, 1, h1))
     end
     assert_equal([1, h1], o.store_foo(:baz, 1, h1))
@@ -2797,6 +2811,8 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2811
 
     assert_equal([[1], h1], o.foo(:dbar, 1, :a=>1))
     assert_equal([1, h1], o.foo(:dbaz, 1, :a=>1))
+    assert_equal([[1], h1], o.bfoo(:dbar, 1, :a=>1))
+    assert_equal([1, h1], o.bfoo(:dbaz, 1, :a=>1))
     assert_equal([[1], h1], o.store_foo(:dbar, 1, :a=>1))
     assert_equal([1, h1], o.store_foo(:dbaz, 1, :a=>1))
     assert_equal([[1], h1], o.foo_dbar(1, :a=>1))
@@ -2804,6 +2820,8 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2820
 
     assert_equal([[1], h1], o.foo(:dbar, 1, **h1))
     assert_equal([1, h1], o.foo(:dbaz, 1, **h1))
+    assert_equal([[1], h1], o.bfoo(:dbar, 1, **h1))
+    assert_equal([1, h1], o.bfoo(:dbaz, 1, **h1))
     assert_equal([[1], h1], o.store_foo(:dbar, 1, **h1))
     assert_equal([1, h1], o.store_foo(:dbaz, 1, **h1))
     assert_equal([[1], h1], o.foo_dbar(1, **h1))
@@ -2811,6 +2829,8 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2829
 
     assert_equal([[h1], {}], o.foo(:dbar, h1, **{}))
     assert_equal([h1], o.foo(:dbaz, h1, **{}))
+    assert_equal([[h1], {}], o.bfoo(:dbar, h1, **{}))
+    assert_equal([h1], o.bfoo(:dbaz, h1, **{}))
     assert_equal([[h1], {}], o.store_foo(:dbar, h1, **{}))
     assert_equal([h1], o.store_foo(:dbaz, h1, **{}))
     assert_equal([[h1], {}], o.foo_dbar(h1, **{}))
@@ -2821,6 +2841,10 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2841
     end
     assert_equal([1, h1], o.foo(:dbaz, 1, h1))
     assert_warn(/The last argument is used as the keyword parameter.* for method/m) do
+      assert_equal([[1], h1], o.bfoo(:dbar, 1, h1))
+    end
+    assert_equal([1, h1], o.bfoo(:dbaz, 1, h1))
+    assert_warn(/The last argument is used as the keyword parameter.* for method/m) do
       assert_equal([[1], h1], o.store_foo(:dbar, 1, h1))
     end
     assert_equal([1, h1], o.store_foo(:dbaz, 1, h1))
@@ -2883,10 +2907,17 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L2907
     assert_equal([1, h1], o.baz(1, h1))
     assert_equal([h1], o.baz(h1, **{}))
 
-    assert_warn(/Skipping set of ruby2_keywords flag for bar \(method not defined in Ruby, method accepts keywords, or method does not accept argument splat\)/) do
+    assert_warn(/Skipping set of ruby2_keywords flag for bar \(method accepts keywords or method does not accept argument splat\)/) do
       assert_nil(c.send(:ruby2_keywords, :bar))
     end
 
+    o = Object.new
+    class << o
+      alias bar p
+    end
+    assert_warn(/Skipping set of ruby2_keywords flag for bar \(method not defined in Ruby\)/) do
+      assert_nil(o.singleton_class.send(:ruby2_keywords, :bar))
+    end
     sc = Class.new(c)
     assert_warn(/Skipping set of ruby2_keywords flag for bar \(can only set in method defining module\)/) do
       sc.send(:ruby2_keywords, :bar)
diff --git a/vm_method.c b/vm_method.c
index 554d209..6465798 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -1805,15 +1805,43 @@ rb_mod_ruby2_keywords(int argc, VALUE *argv, VALUE module) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L1805
         }
 
         if (module == defined_class || origin_class == defined_class) {
-            if (me->def->type == VM_METHOD_TYPE_ISEQ &&
-                    me->def->body.iseq.iseqptr->body->param.flags.has_rest &&
-                    !me->def->body.iseq.iseqptr->body->param.flags.has_kw &&
-                    !me->def->body.iseq.iseqptr->body->param.flags.has_kwrest) {
-                me->def->body.iseq.iseqptr->body->param.flags.ruby2_keywords = 1;
-                rb_clear_method_cache_by_class(module);
-            }
-            else {
-                rb_warn("Skipping set of ruby2_keywords flag for %s (method not defined in Ruby, method accepts keywords, or method does not accept argument splat)", rb_id2name(name));
+            switch (me->def->type) {
+              case VM_METHOD_TYPE_ISEQ:
+                if (me->def->body.iseq.iseqptr->body->param.flags.has_rest &&
+                        !me->def->body.iseq.iseqptr->body->param.flags.has_kw &&
+                        !me->def->body.iseq.iseqptr->body->param.flags.has_kwrest) {
+                    me->def->body.iseq.iseqptr->body->param.flags.ruby2_keywords = 1;
+                    rb_clear_method_cache_by_class(module);
+                }
+                else {
+                    rb_warn("Skipping set of ruby2_keywords flag for %s (method accepts keywords or method does not accept argument splat)", rb_id2name(name));
+                }
+                break;
+              case VM_METHOD_TYPE_BMETHOD: {
+                VALUE procval = me->def->body.bmethod.proc;
+                if (vm_block_handler_type(procval) == block_handler_type_proc) {
+                    procval = vm_proc_to_block_handler(VM_BH_TO_PROC(procval));
+                }
+
+                if (vm_block_handler_type(procval) == block_handler_type_iseq) {
+                    const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(procval);
+                    const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq);
+                    if (iseq->body->param.flags.has_rest &&
+                            !iseq->body->param.flags.has_kw &&
+                            !iseq->body->param.flags.has_kwrest) {
+                        iseq->body->param.flags.ruby2_keywords = 1;
+                        rb_clear_method_cache_by_class(module);
+                    }
+                    else {
+                        rb_warn("Skipping set of ruby2_keywords flag for %s (method accepts keywords or method does not accept argument splat)", rb_id2name(name));
+                    }
+                    return Qnil;
+                }
+              }
+              /* fallthrough */
+              default:
+                rb_warn("Skipping set of ruby2_keywords flag for %s (method not defined in Ruby)", rb_id2name(name));
+                break;
             }
         }
         else {
-- 
cgit v0.10.2


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

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