From 182096d8c0d01121ed36baa3c4e2223b201ba22b Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Tue, 8 Feb 2022 09:03:07 +0900 Subject: [PATCH] Make `Array#*` the CRuby compatible behavior when giving a string argument ## Summary This following is a behavior from CRuby 1.8.7 to 3.1.0. ```console % ruby -ve "p ['a', 'b', 'c']*''" ruby 1.8.7 (2013-12-22 patchlevel 375) [i686-darwin13.0.2] "abc" % ruby -ve "p ['a', 'b', 'c']*''" ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-darwin19] "abc" ``` ### Before (mruby 3.0.0) mruby unexpectedly gives the TypeError. ```ruby ['a', 'b', 'c']*'' #=> String cannot be converted to Integer (TypeError) ``` ### After This PR makes mruby behave compatible with CRuby. ```ruby ['a', 'b', 'c']*'' #=> 'abc' ``` As far as I checked, the behavior is unspecified when `Array#*`'s argument is not an instance of Integer class in X 3017 : 2013 (ISO/IEC 30170 : 2012). ## Additional Information I noticed this difference by the following idiom when writing ASCII art code using Ruby. ```ruby %w(foo bar baz)*'' ``` e.g. TRICK (https://github.com/tric) --- src/array.c | 8 +++++++- test/t/array.rb | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/array.c b/src/array.c index 1a95b75c9..07ceefb7e 100644 --- a/src/array.c +++ b/src/array.c @@ -425,9 +425,15 @@ mrb_ary_times(mrb_state *mrb, mrb_value self) { struct RArray *a1 = mrb_ary_ptr(self); struct RArray *a2; - mrb_value *ptr; + mrb_value *ptr, sep, tmp; mrb_int times, len1; + mrb_get_args(mrb, "o", &sep); + tmp = mrb_check_string_type(mrb, sep); + if (!mrb_nil_p(tmp)) { + return mrb_ary_join(mrb, self, tmp); + } + mrb_get_args(mrb, "i", ×); if (times < 0) { mrb_raise(mrb, E_ARGUMENT_ERROR, "negative argument"); diff --git a/test/t/array.rb b/test/t/array.rb index 5f26259f2..eccb435ef 100644 --- a/test/t/array.rb +++ b/test/t/array.rb @@ -32,6 +32,8 @@ assert('Array#*', '15.2.12.5.2') do end assert_equal([1, 1, 1], [1].*(3)) assert_equal([], [1].*(0)) + assert_equal('abc', ['a', 'b', 'c'].*('')) + assert_equal('0, 0, 1, {:foo=>0}', [0, [0, 1], {foo: 0}].*(', ')) end assert('Array#<<', '15.2.12.5.3') do