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

ruby-changes:57590

From: Yusuke <ko1@a...>
Date: Fri, 6 Sep 2019 11:45:11 +0900 (JST)
Subject: [ruby-changes:57590] ce04392d8d (master): Propagate kw_splat information

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

From ce04392d8d4f8cf14c70bbf1ad3544c7db4e1671 Mon Sep 17 00:00:00 2001
From: Yusuke Endoh <mame@r...>
Date: Wed, 4 Sep 2019 01:32:42 +0900
Subject: Propagate kw_splat information

The kw_splat flag is whether the original call passes keyword or not.
Some types of methods (e.g., bmethod and sym_proc) drops the
information.  This change tries to propagate the flag to the final
callee, as far as I can.

diff --git a/cont.c b/cont.c
index ea21b32..4b60c78 100644
--- a/cont.c
+++ b/cont.c
@@ -1802,7 +1802,7 @@ rb_fiber_start(void) https://github.com/ruby/ruby/blob/trunk/cont.c#L1802
         th->ec->root_svar = Qfalse;
 
         EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_FIBER_SWITCH, th->self, 0, 0, 0, Qnil);
-        cont->value = rb_vm_invoke_proc(th->ec, proc, argc, argv, VM_BLOCK_HANDLER_NONE);
+        cont->value = rb_vm_invoke_proc(th->ec, proc, argc, argv, 0 /* kw_splat */, VM_BLOCK_HANDLER_NONE);
     }
     EC_POP_TAG();
 
diff --git a/proc.c b/proc.c
index 19ce7a1..87ae422 100644
--- a/proc.c
+++ b/proc.c
@@ -942,7 +942,7 @@ rb_proc_call(VALUE self, VALUE args) https://github.com/ruby/ruby/blob/trunk/proc.c#L942
     GetProcPtr(self, proc);
     vret = rb_vm_invoke_proc(GET_EC(), proc,
 			     check_argc(RARRAY_LEN(args)), RARRAY_CONST_PTR(args),
-			     VM_BLOCK_HANDLER_NONE);
+			     0 /* kw_splat */, VM_BLOCK_HANDLER_NONE);
     RB_GC_GUARD(self);
     RB_GC_GUARD(args);
     return vret;
@@ -961,7 +961,7 @@ rb_proc_call_with_block(VALUE self, int argc, const VALUE *argv, VALUE passed_pr https://github.com/ruby/ruby/blob/trunk/proc.c#L961
     VALUE vret;
     rb_proc_t *proc;
     GetProcPtr(self, proc);
-    vret = rb_vm_invoke_proc(ec, proc, argc, argv, proc_to_block_handler(passed_procval));
+    vret = rb_vm_invoke_proc(ec, proc, argc, argv, 0 /* kw_splat */, proc_to_block_handler(passed_procval));
     RB_GC_GUARD(self);
     return vret;
 }
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index c3ad7b9..b8d8736 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -522,6 +522,81 @@ class TestKeywordArguments < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_keyword.rb#L522
     assert_equal([1, h3], c.send(:m, **h3))
   end
 
+  def test_define_method_kwsplat
+    kw = {}
+    h = {'a'=>1}
+    h2 = {'a'=>1}
+    h3 = {'a'=>1, :a=>1}
+
+    c = Object.new
+    class << c
+      define_method(:m) { }
+    end
+    assert_nil(c.m(**{}))
+    assert_nil(c.m(**kw))
+    assert_raise(ArgumentError) { c.m(**h) }
+    assert_raise(ArgumentError) { c.m(**h2) }
+    assert_raise(ArgumentError) { c.m(**h3) }
+
+    c = Object.new
+    class << c
+      define_method(:m) {|arg| }
+    end
+    assert_raise(ArgumentError) { c.m(**{}) }
+    assert_raise(ArgumentError) { c.m(**kw) }
+    assert_nil(c.m(**h))
+    assert_nil(c.m(**h2))
+    assert_nil(c.m(**h3))
+
+    c = Object.new
+    class << c
+      define_method(:m) {|*args| }
+    end
+    assert_nil(c.m(**{}))
+    assert_nil(c.m(**kw))
+    assert_nil(c.m(**h))
+    assert_nil(c.m(**h2))
+    assert_nil(c.m(**h3))
+
+    c = Object.new
+    class << c
+      define_method(:m) {|**opt| }
+    end
+    assert_nil(c.m(**{}))
+    assert_nil(c.m(**kw))
+    assert_nil(c.m(**h))
+    assert_nil(c.m(**h2))
+    assert_nil(c.m(**h3))
+
+    c = Object.new
+    class << c
+      define_method(:m) {|arg, **opt| [arg, opt] }
+    end
+    assert_raise(ArgumentError) { c.m(**{}) }
+    assert_warn(/The keyword argument is passed as the last hash parameter/m) do
+      assert_equal([kw, kw], c.m(**kw))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/m) do
+      assert_equal([h, kw], c.m(**h))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/m) do
+      assert_equal([h2, kw], c.m(**h2))
+    end
+    assert_warn(/The keyword argument is passed as the last hash parameter/m) do
+      assert_equal([h3, kw], c.m(**h3))
+    end
+
+    c = Object.new
+    class << c
+      define_method(:m) {|arg=1, **opt| }
+    end
+    assert_nil(c.m(**{}))
+    assert_nil(c.m(**kw))
+    assert_nil(c.m(**h))
+    assert_nil(c.m(**h2))
+    assert_nil(c.m(**h3))
+  end
+
   def p1
     Proc.new do |str: "foo", num: 424242|
       [str, num]
diff --git a/thread.c b/thread.c
index 8d1ca95..b5fa991 100644
--- a/thread.c
+++ b/thread.c
@@ -681,7 +681,7 @@ thread_do_start(rb_thread_t *th) https://github.com/ruby/ruby/blob/trunk/thread.c#L681
 
         th->value = rb_vm_invoke_proc(th->ec, proc,
                                       (int)args_len, args_ptr,
-                                      VM_BLOCK_HANDLER_NONE);
+                                      0 /* kw_splat */, VM_BLOCK_HANDLER_NONE);
 
         EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
     }
diff --git a/vm.c b/vm.c
index 88b76c4..59cceca 100644
--- a/vm.c
+++ b/vm.c
@@ -325,9 +325,9 @@ static void vm_collect_usage_register(int reg, int isset); https://github.com/ruby/ruby/blob/trunk/vm.c#L325
 
 static VALUE vm_make_env_object(const rb_execution_context_t *ec, rb_control_frame_t *cfp);
 extern VALUE rb_vm_invoke_bmethod(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
-                                  int argc, const VALUE *argv, VALUE block_handler,
+                                  int argc, const VALUE *argv, int kw_splat, VALUE block_handler,
                                   const rb_callable_method_entry_t *me);
-static VALUE vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, VALUE block_handler);
+static VALUE vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE block_handler);
 
 #include "mjit.h"
 #include "vm_insnhelper.h"
@@ -1076,12 +1076,12 @@ invoke_bmethod(rb_execution_context_t *ec, const rb_iseq_t *iseq, VALUE self, co https://github.com/ruby/ruby/blob/trunk/vm.c#L1076
 
 ALWAYS_INLINE(static VALUE
               invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_block *captured,
-                                       VALUE self, int argc, const VALUE *argv, VALUE passed_block_handler,
+                                       VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler,
                                        const rb_cref_t *cref, int is_lambda, const rb_callable_method_entry_t *me));
 
 static inline VALUE
 invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_block *captured,
-			 VALUE self, int argc, const VALUE *argv, VALUE passed_block_handler,
+			 VALUE self, int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler,
                          const rb_cref_t *cref, int is_lambda, const rb_callable_method_entry_t *me)
 {
     const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq);
@@ -1099,7 +1099,7 @@ invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_bl https://github.com/ruby/ruby/blob/trunk/vm.c#L1099
 	sp[i] = argv[i];
     }
 
-    opt_pc = vm_yield_setup_args(ec, iseq, argc, sp, passed_block_handler,
+    opt_pc = vm_yield_setup_args(ec, iseq, argc, sp, kw_splat, passed_block_handler,
 				 (is_lambda ? arg_setup_method : arg_setup_block));
     cfp->sp = sp;
 
@@ -1114,7 +1114,7 @@ invoke_iseq_block_from_c(rb_execution_context_t *ec, const struct rb_captured_bl https://github.com/ruby/ruby/blob/trunk/vm.c#L1114
 static inline VALUE
 invoke_block_from_c_bh(rb_execution_context_t *ec, VALUE block_handler,
 		       int argc, const VALUE *argv,
-		       VALUE passed_block_handler, const rb_cref_t *cref,
+		       int kw_splat, VALUE passed_block_handler, const rb_cref_t *cref,
 		       int is_lambda, int force_blockarg)
 {
   again:
@@ -1123,16 +1123,16 @@ invoke_block_from_c_bh(rb_execution_context_t *ec, VALUE block_handler, https://github.com/ruby/ruby/blob/trunk/vm.c#L1123
 	{
 	    const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(block_handler);
 	    return invoke_iseq_block_from_c(ec, captured, captured->self,
-					    argc, argv, passed_block_handler,
+					    argc, argv, kw_splat, passed_block_handler,
                                             cref, is_lambda, NULL);
 	}
       case block_handler_type_ifunc:
 	return vm_yield_with_cfunc(ec, VM_BH_TO_IFUNC_BLOCK(block_handler),
 				   VM_BH_TO_IFUNC_BLOCK(block_handler)->self,
-                                   argc, argv, passed_block_handler, NULL);
+                                   argc, argv, kw_splat, passed_block_handler, NULL);
       case block_handler_type_symbol:
 	return vm_yield_with_symbol(ec, VM_BH_TO_SYMBOL(block_handler),
-				    argc, argv, passed_block_handler);
+				    argc, argv, kw_splat, passed_block_handler);
       case block_handler_type_proc:
 	if (force_blockarg == FALSE) {
 	    is_lambda = block_proc_is_lambda(VM_BH_TO_PROC(block_handler));
@@ -1160,7 +1160,7 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/vm.c#L1160
 vm_yield_with_cref(rb_execution_context_t *ec, int argc, const VALUE *argv, const rb_cref_t *cref, int is_lambda)
 {
     return invoke_block_from_c_bh(ec, check_block_handler(ec),
-				  argc, argv, VM_BLOCK_HANDLER_NONE,
+				  argc, argv, 0 /* kw_splat */, VM_BLOCK_HANDLER_NONE,
 				  cref, is_lambda, FALSE);
 }
 
@@ -1168,7 +1168,7 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/vm.c#L1168
 vm_yield(rb_execution_context_t *ec, int argc, const VALUE *argv)
 {
     return invoke_block_from_c_bh(ec, check_block_handler(ec),
-				  argc, argv, VM_BLOCK_HANDLER_NONE,
+				  argc, argv, 0 /* kw_splat */, VM_BLOCK_HANDLER_NONE,
 				  NULL, FALSE, FALSE);
 }
 
@@ -1176,7 +1176,7 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/vm.c#L1176
 vm_yield_with_block(rb_execution_context_t *ec, int argc, const VALUE *argv, VALUE block_handler)
 {
     return invoke_block_from_c_bh(ec, check_block_handler(ec),
-			 (... truncated)

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

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