summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2019-09-03 14:54:37 -0700
committerJeremy Evans <code@jeremyevans.net>2019-09-05 17:47:12 -0700
commit6f9b86616a8ad60cfed2979e2a0f8398a12e7c85 (patch)
tree5fc2b4bd2ac4d30512df59efb39ee406248d0804
parent38dae1d510b931516ba1229a1ffbe5f6e470e292 (diff)
Make Symbol#to_proc calls handle keyword arguments
Make rb_sym_proc_call take a flag for whether a keyword argument is used, and use the new rb_funcall_with_block_kw function to pass that information.
-rw-r--r--internal.h2
-rw-r--r--string.c4
-rw-r--r--test/ruby/test_keyword.rb73
-rw-r--r--vm_insnhelper.c3
4 files changed, 77 insertions, 5 deletions
diff --git a/internal.h b/internal.h
index 14f78301fb..21491c317f 100644
--- a/internal.h
+++ b/internal.h
@@ -2140,7 +2140,7 @@ VALUE rb_str_initialize(VALUE str, const char *ptr, long len, rb_encoding *enc);
#define is_ascii_string(str) (rb_enc_str_coderange(str) == ENC_CODERANGE_7BIT)
#define is_broken_string(str) (rb_enc_str_coderange(str) == ENC_CODERANGE_BROKEN)
size_t rb_str_memsize(VALUE);
-VALUE rb_sym_proc_call(ID mid, int argc, const VALUE *argv, VALUE passed_proc);
+VALUE rb_sym_proc_call(ID mid, int argc, const VALUE *argv, int kw_splat, VALUE passed_proc);
VALUE rb_sym_to_proc(VALUE sym);
char *rb_str_to_cstr(VALUE str);
VALUE rb_str_eql(VALUE str1, VALUE str2);
diff --git a/string.c b/string.c
index d6ea7d6f91..05ce0ed8d6 100644
--- a/string.c
+++ b/string.c
@@ -10887,7 +10887,7 @@ sym_to_sym(VALUE sym)
}
MJIT_FUNC_EXPORTED VALUE
-rb_sym_proc_call(ID mid, int argc, const VALUE *argv, VALUE passed_proc)
+rb_sym_proc_call(ID mid, int argc, const VALUE *argv, int kw_splat, VALUE passed_proc)
{
VALUE obj;
@@ -10895,7 +10895,7 @@ rb_sym_proc_call(ID mid, int argc, const VALUE *argv, VALUE passed_proc)
rb_raise(rb_eArgError, "no receiver given");
}
obj = argv[0];
- return rb_funcall_with_block(obj, mid, argc - 1, argv + 1, passed_proc);
+ return rb_funcall_with_block_kw(obj, mid, argc - 1, argv + 1, passed_proc, kw_splat);
}
#if 0
diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb
index b8d8736c5f..91e53e1dee 100644
--- a/test/ruby/test_keyword.rb
+++ b/test/ruby/test_keyword.rb
@@ -447,6 +447,79 @@ class TestKeywordArguments < Test::Unit::TestCase
assert_equal([1, h3], c.send(:m, **h3))
end
+ def test_sym_proc_kwsplat
+ kw = {}
+ h = {'a'=>1}
+ h2 = {'a'=>1}
+ h3 = {'a'=>1, :a=>1}
+
+ c = Object.new
+ def c.m(*args)
+ args
+ end
+ assert_equal([], :m.to_proc.call(c, **{}))
+ assert_equal([], :m.to_proc.call(c, **kw))
+ assert_equal([h], :m.to_proc.call(c, **h))
+ assert_equal([h2], :m.to_proc.call(c, **h2))
+ assert_equal([h3], :m.to_proc.call(c, **h3))
+
+ c.singleton_class.remove_method(:m)
+ def c.m; end
+ assert_nil(:m.to_proc.call(c, **{}))
+ assert_nil(:m.to_proc.call(c, **kw))
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **h) }
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **h2) }
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **h3) }
+
+ c.singleton_class.remove_method(:m)
+ def c.m(args)
+ args
+ end
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **{}) }
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **kw) }
+ assert_equal(h, :m.to_proc.call(c, **h))
+ assert_equal(h2, :m.to_proc.call(c, **h2))
+ assert_equal(h3, :m.to_proc.call(c, **h3))
+
+ c.singleton_class.remove_method(:m)
+ def c.m(**args)
+ args
+ end
+ assert_equal(kw, :m.to_proc.call(c, **{}))
+ assert_equal(kw, :m.to_proc.call(c, **kw))
+ assert_equal(h, :m.to_proc.call(c, **h))
+ assert_equal(h2, :m.to_proc.call(c, **h2))
+ assert_equal(h3, :m.to_proc.call(c, **h3))
+
+ c.singleton_class.remove_method(:m)
+ def c.m(arg, **args)
+ [arg, args]
+ end
+ assert_raise(ArgumentError) { :m.to_proc.call(c, **{}) }
+ assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
+ assert_equal([kw, kw], :m.to_proc.call(c, **kw))
+ end
+ assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
+ assert_equal([h, kw], :m.to_proc.call(c, **h))
+ end
+ assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
+ assert_equal([h2, kw], :m.to_proc.call(c, **h2))
+ end
+ assert_warn(/The keyword argument is passed as the last hash parameter.* for `m'/m) do
+ assert_equal([h3, kw], :m.to_proc.call(c, **h3))
+ end
+
+ c.singleton_class.remove_method(:m)
+ def c.m(arg=1, **args)
+ [arg=1, args]
+ end
+ assert_equal([1, kw], :m.to_proc.call(c, **{}))
+ assert_equal([1, kw], :m.to_proc.call(c, **kw))
+ assert_equal([1, h], :m.to_proc.call(c, **h))
+ assert_equal([1, h2], :m.to_proc.call(c, **h2))
+ assert_equal([1, h3], :m.to_proc.call(c, **h3))
+ end
+
def test_method_missing_kwsplat
kw = {}
h = {'a'=>1}
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 7895c8a0eb..57300073d2 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -2884,8 +2884,7 @@ vm_yield_with_cfunc(rb_execution_context_t *ec,
static VALUE
vm_yield_with_symbol(rb_execution_context_t *ec, VALUE symbol, int argc, const VALUE *argv, int kw_splat, VALUE block_handler)
{
- /* XXX: need to pass kw_splat? */
- return rb_sym_proc_call(SYM2ID(symbol), argc, argv, rb_vm_bh_to_procval(ec, block_handler));
+ return rb_sym_proc_call(SYM2ID(symbol), argc, argv, kw_splat, rb_vm_bh_to_procval(ec, block_handler));
}
static inline int