From dd93cfc8ca78f1208d6f492d105d7db0eea82b35 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 7249ab58d..1233df970 100644 --- a/lib/net/imap.rb +++ b/lib/net/imap.rb @@ -3233,7 +3233,7 @@ def responses(type = nil) warn(RESPONSES_DEPRECATION_MSG, uplevel: 1, category: :deprecated) 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 3ce0e48d9..f2ee65b5d 100644 --- a/test/net/imap/test_imap_responses.rb +++ b/test/net/imap/test_imap_responses.rb @@ -151,13 +151,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