Skip to content

Commit b0b947e

Browse files
committed
[GR-18163] Fix Hash#to_h called with a block and pass key and value to the block as separate arguments
PullRequest: truffleruby/4305
2 parents 443914d + ca0c802 commit b0b947e

File tree

12 files changed

+97
-48
lines changed

12 files changed

+97
-48
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Compatibility:
3333
* Fix `Enumerable#reduce` to handle non-Symbol method name parameter (#2931, @andrykonchin).
3434
* Fix `RangeError` message to match CRuby for `Integer#chr` called with invalid codepoint argument (#2795, @andrykonchin).
3535
* Joni has been updated from 2.1.44 to 2.2.1 (@andrykonchin).
36+
* Fix `Hash#to_h` called with a block and pass key and value to the block as separate arguments (#3607, @andrykonchin).
3637

3738
Performance:
3839

spec/ruby/core/array/to_h_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@
4545
[:a, :b].to_h { |k| [k, k.to_s] }.should == { a: 'a', b: 'b' }
4646
end
4747

48+
it "passes to a block each element as a single argument" do
49+
ScratchPad.record []
50+
[[:a, 1], [:b, 2]].to_h { |*args| ScratchPad << args; [args[0], args[1]] }
51+
ScratchPad.recorded.sort.should == [[[:a, 1]], [[:b, 2]]]
52+
end
53+
4854
it "raises ArgumentError if block returns longer or shorter array" do
4955
-> do
5056
[:a, :b].to_h { |k| [k, k.to_s, 1] }

spec/ruby/core/enumerable/to_h_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ def enum.each(*args)
5353
@enum.to_h { |k| [k, k.to_s] }.should == { a: 'a', b: 'b' }
5454
end
5555

56+
it "passes to a block each element as a single argument" do
57+
enum_of_arrays = EnumerableSpecs::EachDefiner.new([:a, 1], [:b, 2])
58+
59+
ScratchPad.record []
60+
enum_of_arrays.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
61+
ScratchPad.recorded.sort.should == [[[:a, 1]], [[:b, 2]]]
62+
end
63+
5664
it "raises ArgumentError if block returns longer or shorter array" do
5765
-> do
5866
@enum.to_h { |k| [k, k.to_s, 1] }

spec/ruby/core/env/to_h_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@
1818
ENV.to_h { |k, v| [k, v.upcase] }.should == { 'a' => "B", 'c' => "D" }
1919
end
2020

21+
it "passes to a block each pair's key and value as separate arguments" do
22+
ENV.replace("a" => "b", "c" => "d")
23+
24+
ScratchPad.record []
25+
ENV.to_h { |k, v| ScratchPad << [k, v]; [k, v] }
26+
ScratchPad.recorded.sort.should == [["a", "b"], ["c", "d"]]
27+
28+
ScratchPad.record []
29+
ENV.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
30+
ScratchPad.recorded.sort.should == [["a", "b"], ["c", "d"]]
31+
end
32+
2133
it "does not require the array elements to be strings" do
2234
ENV.replace("a" => "b", "c" => "d")
2335
ENV.to_h { |k, v| [k.to_sym, v.to_sym] }.should == { :a => :b, :c => :d }

spec/ruby/core/hash/to_h_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@
3737
{ a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v]}.should == { "a" => 1, "b" => 4 }
3838
end
3939

40+
it "passes to a block each pair's key and value as separate arguments" do
41+
ScratchPad.record []
42+
{ a: 1, b: 2 }.to_h { |k, v| ScratchPad << [k, v]; [k, v] }
43+
ScratchPad.recorded.sort.should == [[:a, 1], [:b, 2]]
44+
45+
ScratchPad.record []
46+
{ a: 1, b: 2 }.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
47+
ScratchPad.recorded.sort.should == [[:a, 1], [:b, 2]]
48+
end
49+
4050
it "raises ArgumentError if block returns longer or shorter array" do
4151
-> do
4252
{ a: 1, b: 2 }.to_h { |k, v| [k.to_s, v*v, 1] }

spec/ruby/core/struct/to_h_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@
2121
h.should == { "make" => "ford", "model" => "ranger", "year" => "" }
2222
end
2323

24+
it "passes to a block each pair's key and value as separate arguments" do
25+
s = StructClasses::Ruby.new('3.2.4', 'macos')
26+
27+
ScratchPad.record []
28+
s.to_h { |k, v| ScratchPad << [k, v]; [k, v] }
29+
ScratchPad.recorded.sort.should == [[:platform, 'macos'], [:version, '3.2.4']]
30+
31+
ScratchPad.record []
32+
s.to_h { |*args| ScratchPad << args; [args[0], args[1]] }
33+
ScratchPad.recorded.sort.should == [[:platform, 'macos'], [:version, '3.2.4']]
34+
end
35+
2436
it "raises ArgumentError if block returns longer or shorter array" do
2537
-> do
2638
StructClasses::Car.new.to_h { |k, v| [k.to_s, "#{v}".downcase, 1] }

src/main/ruby/truffleruby/core/array.rb

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,19 +1296,10 @@ def to_ary
12961296
end
12971297

12981298
def to_h
1299-
h = Hash.new
1299+
h = {}
13001300
each_with_index do |elem, i|
13011301
elem = yield(elem) if block_given?
1302-
unless elem.respond_to?(:to_ary)
1303-
raise TypeError, "wrong element type #{Primitive.class(elem)} at #{i} (expected array)"
1304-
end
1305-
1306-
ary = elem.to_ary
1307-
if ary.size != 2
1308-
raise ArgumentError, "wrong array length at #{i} (expected 2, was #{ary.size})"
1309-
end
1310-
1311-
h[ary[0]] = ary[1]
1302+
Truffle::HashOperations.assoc_key_value_pair_with_position(h, elem, i)
13121303
end
13131304
h
13141305
end

src/main/ruby/truffleruby/core/enumerable.rb

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -314,20 +314,12 @@ def to_a(*args, **kwargs)
314314
end
315315
alias_method :entries, :to_a
316316

317-
def to_h(*arg)
317+
def to_h(*args)
318318
h = {}
319-
each_with_index(*arg) do |elem, i|
319+
each(*args) do
320+
elem = Primitive.single_block_arg
320321
elem = yield(elem) if block_given?
321-
unless elem.respond_to?(:to_ary)
322-
raise TypeError, "wrong element type #{Primitive.class(elem)} at #{i} (expected array)"
323-
end
324-
325-
ary = elem.to_ary
326-
if ary.size != 2
327-
raise ArgumentError, "element has wrong array length (expected 2, was #{ary.size})"
328-
end
329-
330-
h[ary[0]] = ary[1]
322+
Truffle::HashOperations.assoc_key_value_pair(h, elem)
331323
end
332324
h
333325
end

src/main/ruby/truffleruby/core/env.rb

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -286,18 +286,9 @@ def to_h
286286
return to_hash unless block_given?
287287

288288
h = {}
289-
each_pair do |*elem|
290-
elem = yield(elem)
291-
unless elem.respond_to?(:to_ary)
292-
raise TypeError, "wrong element type #{Primitive.class(elem)} (expected array)"
293-
end
294-
295-
ary = elem.to_ary
296-
if ary.size != 2
297-
raise ArgumentError, "element has wrong array length (expected 2, was #{ary.size})"
298-
end
299-
300-
h[ary[0]] = ary[1]
289+
each_pair do |k, v|
290+
pair = yield(k, v)
291+
Truffle::HashOperations.assoc_key_value_pair(h, pair)
301292
end
302293
h
303294
end

src/main/ruby/truffleruby/core/hash.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,12 @@ def slice(*keys)
355355

356356
def to_h
357357
if block_given?
358-
super
358+
h = {}
359+
each do |k, v|
360+
pair = yield(k, v)
361+
Truffle::HashOperations.assoc_key_value_pair(h, pair)
362+
end
363+
h
359364
elsif instance_of? Hash
360365
self
361366
else

0 commit comments

Comments
 (0)