Bug #20230
closedFix crash when passing large keyword splat to method accepting keywords and keyword splat
Description
The following code causes a crash in Ruby 2.2-3.3 and master:
h = {}
1000000.times{|i| h[i.to_s.to_sym] = i}
def f(kw: 1, **kws) end
f(**h)
Inside a thread or fiber, the size of the keyword splat could be much smaller and still cause a crash. This is similar to the issue in #4040, but for keyword splats instead of positional splats.
I found this issue while optimizing method calling by reducing implicit allocations. Given the following code:
def f(kw: , **kws) end
kw = {kw: 1}
f(**kw)
The f(**kw)
call previously allocated two hashes callee side instead of a single hash. This is because setup_parameters_complex
would extract the keywords from the keyword splat hash to the C stack, to attempt to mirror the case when literal keywords are passed without a keyword splat. Then, make_rest_kw_hash
would build a new hash based on the extracted keywords that weren't used for literal keywords.
I have submitted a pull request which changes the implementation so that if a keyword splat is passed, literal keywords are deleted from the keyword splat hash (or a copy of the hash if the hash is not mutable): https://github.com/ruby/ruby/pull/9776
In addition to fixing the bug, the new implementation also eliminates the unnecessary hash allocation.