From 7e4f1f0e645da41ca56d415277e52dfb1934940b Mon Sep 17 00:00:00 2001 From: nick evans Date: Wed, 17 Dec 2025 13:20:18 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20`#responses()`=20freezing?= =?UTF-8?q?=20internal=20arrays?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that `:frozen_dup` is the default behavior for `#responses` when it's called without any arguments, a critical bug has become apparent: it was not freezing the internal responses arrays directly, rather than copies of them. Freezing these arrays will, of course, lead to further issues. Ideally, code should be updated to use one of the other forms of `#responses`, since this form is less efficient and also (intentionally) incompatibile with old code that expects it to return mutable arrays. But this is still a major bug. Fixes #581, reported by @yurikoval. --- lib/net/imap.rb | 2 +- test/net/imap/test_imap_responses.rb | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/net/imap.rb b/lib/net/imap.rb index 5cc114781..a849d47e8 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -2691,7 +2691,7 @@ def responses(type = nil) warn(RESPONSES_DEPRECATION_MSG, uplevel: 1) when :frozen_dup synchronize { - responses = @responses.transform_values(&:freeze) + responses = @responses.transform_values { _1.dup.freeze } responses.default_proc = nil responses.default = [].freeze return responses.freeze diff --git a/test/net/imap/test_imap_responses.rb b/test/net/imap/test_imap_responses.rb index aaa61d436..3d1287635 100644 --- a/test/net/imap/test_imap_responses.rb +++ b/test/net/imap/test_imap_responses.rb @@ -166,13 +166,17 @@ def assert_responses_warn assert_equal [], imap.responses["FAKE"] end assert_empty stderr - # opt-in to future behavior + # default behavior since 0.6.0 imap.config.responses_without_block = :frozen_dup stderr = EnvUtil.verbose_warning do assert imap.responses.frozen? assert imap.responses["CAPABILITY"].frozen? assert_equal(%w[IMAP4REV1 NAMESPACE MOVE IDLE UTF8=ACCEPT], imap.responses["CAPABILITY"].last) + imap.responses do |r| + refute r.frozen? + refute r.values.any?(&:frozen?) + end end assert_empty stderr end