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

ruby-changes:58055

From: Jeremy <ko1@a...>
Date: Mon, 30 Sep 2019 23:07:05 +0900 (JST)
Subject: [ruby-changes:58055] 3073404e74 (master): Add rb_enumeratorize_with_size_kw and related macros

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

From 3073404e741df19ae16248126640777ed36110e8 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Sun, 29 Sep 2019 21:33:59 -0700
Subject: Add rb_enumeratorize_with_size_kw and related macros

Currently, there is not a way to create a sized enumerator in C
with a different set of arguments than provided by Ruby, and
correctly handle keyword arguments.  This function allows that.

The need for this is fairly uncommon, but it occurs at least in
Enumerator.produce, which takes arugments from Ruby but calls
rb_enumeratorize_with_size with a different set of arguments.

diff --git a/enumerator.c b/enumerator.c
index 9eb530c..18d06bb 100644
--- a/enumerator.c
+++ b/enumerator.c
@@ -513,7 +513,7 @@ rb_enumeratorize(VALUE obj, VALUE meth, int argc, const VALUE *argv) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L513
 }
 
 static VALUE
-lazy_to_enum_i(VALUE self, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn);
+lazy_to_enum_i(VALUE self, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn, int kw_splat);
 
 VALUE
 rb_enumeratorize_with_size(VALUE obj, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn)
@@ -521,12 +521,24 @@ rb_enumeratorize_with_size(VALUE obj, VALUE meth, int argc, const VALUE *argv, r https://github.com/ruby/ruby/blob/trunk/enumerator.c#L521
     /* Similar effect as calling obj.to_enum, i.e. dispatching to either
        Kernel#to_enum vs Lazy#to_enum */
     if (RTEST(rb_obj_is_kind_of(obj, rb_cLazy)))
-	return lazy_to_enum_i(obj, meth, argc, argv, size_fn);
+        return lazy_to_enum_i(obj, meth, argc, argv, size_fn, PASS_KW_SPLAT);
     else
 	return enumerator_init(enumerator_allocate(rb_cEnumerator),
                                obj, meth, argc, argv, size_fn, Qnil, PASS_KW_SPLAT);
 }
 
+VALUE
+rb_enumeratorize_with_size_kw(VALUE obj, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn, int kw_splat)
+{
+    /* Similar effect as calling obj.to_enum, i.e. dispatching to either
+       Kernel#to_enum vs Lazy#to_enum */
+    if (RTEST(rb_obj_is_kind_of(obj, rb_cLazy)))
+        return lazy_to_enum_i(obj, meth, argc, argv, size_fn, kw_splat);
+    else
+        return enumerator_init(enumerator_allocate(rb_cEnumerator),
+                               obj, meth, argc, argv, size_fn, Qnil, kw_splat);
+}
+
 static VALUE
 enumerator_block_call(VALUE obj, rb_block_call_func *func, VALUE arg)
 {
@@ -1868,17 +1880,17 @@ lazy_add_method(VALUE obj, int argc, VALUE *argv, VALUE args, VALUE memo, https://github.com/ruby/ruby/blob/trunk/enumerator.c#L1880
 static VALUE
 enumerable_lazy(VALUE obj)
 {
-    VALUE result = lazy_to_enum_i(obj, sym_each, 0, 0, lazyenum_size);
+    VALUE result = lazy_to_enum_i(obj, sym_each, 0, 0, lazyenum_size, PASS_KW_SPLAT);
     /* Qfalse indicates that the Enumerator::Lazy has no method name */
     rb_ivar_set(result, id_method, Qfalse);
     return result;
 }
 
 static VALUE
-lazy_to_enum_i(VALUE obj, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn)
+lazy_to_enum_i(VALUE obj, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn, int kw_splat)
 {
     return enumerator_init(enumerator_allocate(rb_cLazy),
-                           obj, meth, argc, argv, size_fn, Qnil, PASS_KW_SPLAT);
+                           obj, meth, argc, argv, size_fn, Qnil, kw_splat);
 }
 
 /*
@@ -1916,7 +1928,7 @@ lazy_to_enum(int argc, VALUE *argv, VALUE self) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L1928
     if (RTEST((super_meth = rb_hash_aref(lazy_use_super_method, meth)))) {
         meth = super_meth;
     }
-    lazy = lazy_to_enum_i(self, meth, argc, argv, 0);
+    lazy = lazy_to_enum_i(self, meth, argc, argv, 0, PASS_KW_SPLAT);
     if (rb_block_given_p()) {
 	enumerator_ptr(lazy)->size = rb_block_proc();
     }
@@ -2919,7 +2931,7 @@ enumerator_s_produce(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/enumerator.c#L2931
 
     producer = producer_init(producer_allocate(rb_cEnumProducer), init, rb_block_proc());
 
-    return rb_enumeratorize_with_size(producer, sym_each, 0, 0, producer_size);
+    return rb_enumeratorize_with_size_kw(producer, sym_each, 0, 0, producer_size, RB_NO_KEYWORDS);
 }
 
 /*
diff --git a/ext/-test-/enumerator_kw/depend b/ext/-test-/enumerator_kw/depend
new file mode 100644
index 0000000..b7489ea
--- /dev/null
+++ b/ext/-test-/enumerator_kw/depend
@@ -0,0 +1,14 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/enumerator_kw/depend#L1
+# AUTOGENERATED DEPENDENCIES START
+enumerator_kw.o: $(RUBY_EXTCONF_H)
+enumerator_kw.o: $(arch_hdrdir)/ruby/config.h
+enumerator_kw.o: $(hdrdir)/ruby.h
+enumerator_kw.o: $(hdrdir)/ruby/assert.h
+enumerator_kw.o: $(hdrdir)/ruby/backward.h
+enumerator_kw.o: $(hdrdir)/ruby/defines.h
+enumerator_kw.o: $(hdrdir)/ruby/intern.h
+enumerator_kw.o: $(hdrdir)/ruby/missing.h
+enumerator_kw.o: $(hdrdir)/ruby/ruby.h
+enumerator_kw.o: $(hdrdir)/ruby/st.h
+enumerator_kw.o: $(hdrdir)/ruby/subst.h
+enumerator_kw.o: enumerator_kw.c
+# AUTOGENERATED DEPENDENCIES END
diff --git a/ext/-test-/enumerator_kw/enumerator_kw.c b/ext/-test-/enumerator_kw/enumerator_kw.c
new file mode 100644
index 0000000..cd6099f
--- /dev/null
+++ b/ext/-test-/enumerator_kw/enumerator_kw.c
@@ -0,0 +1,21 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/enumerator_kw/enumerator_kw.c#L1
+#include <ruby.h>
+
+static VALUE
+enumerator_kw(int argc, VALUE *argv, VALUE self)
+{
+    VALUE obj, opt, enum_args[4];
+    enum_args[0] = Qnil;
+    enum_args[1] = Qnil;
+    rb_scan_args(argc, argv, "01*:", enum_args, enum_args+1, &opt);
+    enum_args[3] = self;
+    enum_args[2] = opt;
+    RETURN_SIZED_ENUMERATOR_KW(self, 4, enum_args, 0, RB_NO_KEYWORDS);
+    return rb_yield_values_kw(4, enum_args, RB_NO_KEYWORDS);
+}
+
+void
+Init_enumerator_kw(void) {
+    VALUE module = rb_define_module("Bug");
+    module = rb_define_module_under(module, "EnumeratorKw");
+    rb_define_method(module, "m", enumerator_kw, -1);
+}
diff --git a/ext/-test-/enumerator_kw/extconf.rb b/ext/-test-/enumerator_kw/extconf.rb
new file mode 100755
index 0000000..ab2be73
--- /dev/null
+++ b/ext/-test-/enumerator_kw/extconf.rb
@@ -0,0 +1 @@
+create_makefile("-test-/enumerator_kw")
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index 14543a9..66c2ca6 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -251,18 +251,29 @@ VALUE rb_enum_values_pack(int, const VALUE*); https://github.com/ruby/ruby/blob/trunk/include/ruby/intern.h#L251
 VALUE rb_enumeratorize(VALUE, VALUE, int, const VALUE *);
 typedef VALUE rb_enumerator_size_func(VALUE, VALUE, VALUE);
 VALUE rb_enumeratorize_with_size(VALUE, VALUE, int, const VALUE *, rb_enumerator_size_func *);
+VALUE rb_enumeratorize_with_size_kw(VALUE, VALUE, int, const VALUE *, rb_enumerator_size_func *, int);
 #ifndef RUBY_EXPORT
 #define rb_enumeratorize_with_size(obj, id, argc, argv, size_fn) \
     rb_enumeratorize_with_size(obj, id, argc, argv, (rb_enumerator_size_func *)(size_fn))
+#define rb_enumeratorize_with_size_kw(obj, id, argc, argv, size_fn, kw_splat) \
+    rb_enumeratorize_with_size_kw(obj, id, argc, argv, (rb_enumerator_size_func *)(size_fn), kw_splat)
 #endif
 #define SIZED_ENUMERATOR(obj, argc, argv, size_fn) \
     rb_enumeratorize_with_size((obj), ID2SYM(rb_frame_this_func()), \
 			       (argc), (argv), (size_fn))
+#define SIZED_ENUMERATOR_KW(obj, argc, argv, size_fn, kw_splat) \
+    rb_enumeratorize_with_size_kw((obj), ID2SYM(rb_frame_this_func()), \
+                                  (argc), (argv), (size_fn), (kw_splat))
 #define RETURN_SIZED_ENUMERATOR(obj, argc, argv, size_fn) do {		\
 	if (!rb_block_given_p())					\
 	    return SIZED_ENUMERATOR(obj, argc, argv, size_fn);		\
     } while (0)
+#define RETURN_SIZED_ENUMERATOR_KW(obj, argc, argv, size_fn, kw_splat) do { \
+        if (!rb_block_given_p())                                            \
+            return SIZED_ENUMERATOR_KW(obj, argc, argv, size_fn, kw_splat);              \
+    } while (0)
 #define RETURN_ENUMERATOR(obj, argc, argv) RETURN_SIZED_ENUMERATOR(obj, argc, argv, 0)
+#define RETURN_ENUMERATOR_KW(obj, argc, argv, kw_splat) RETURN_SIZED_ENUMERATOR_KW(obj, argc, argv, 0, kw_splat)
 typedef struct {
     VALUE begin;
     VALUE end;
diff --git a/test/-ext-/test_enumerator_kw.rb b/test/-ext-/test_enumerator_kw.rb
new file mode 100644
index 0000000..fb47c2b
--- /dev/null
+++ b/test/-ext-/test_enumerator_kw.rb
@@ -0,0 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/test/-ext-/test_enumerator_kw.rb#L1
+require 'test/unit'
+require '-test-/enumerator_kw'
+
+class TestEnumeratorKw < Test::Unit::TestCase
+  def test_enumerator_kw
+    o = Object.new
+    o.extend Bug::EnumeratorKw
+    assert_equal([nil, [], {:a=>1}, o], o.m(a: 1) { |*a| a })
+    assert_equal([nil, [[], {:a=>1}, o], nil, o], o.m(a: 1).each { |*a| a })
+  end
+end
diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb
index 1e306c3..65adb5a 100644
--- a/test/ruby/test_enumerator.rb
+++ b/test/ruby/test_enumerator.rb
@@ -831,6 +831,14 @@ class TestEnumerator < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_enumerator.rb#L831
     assert_equal [1, 2, 3], enum.take(3)
     assert_equal [1, 2], passed_args
 
+    # With initial keyword arguments
+    passed_args = []
+    enum = Enumerator.produce(a: 1, b: 1) { |obj| passed_args << obj; obj.shift if obj.respond_to?(:shift)}
+    assert_instance_of(Enumerator, enum)
+    assert_equal Float::INFINITY, enum.size
+    assert_equal [{b: 1}, [1], :a, nil], enum.take(4)
+    assert_equal [{b: 1}, [1], :a], passed_args
+
     # Raising StopIteration
     words = "The quick brown fox jumps over the lazy dog.".scan(/\w+/)
     enum = Enumerator.produce { words.shift or raise StopIteration }
-- 
cgit v0.10.2


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

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