From 0ddb2c9acc2f0e594df4bf8eb5b52589b32a8835 Mon Sep 17 00:00:00 2001 From: Patrick Linnane Date: Wed, 20 May 2026 21:19:02 -0700 Subject: [PATCH 1/3] pathname: eagerly initialize lazy instance variables Continues d57efd9ea2 by initialising the lazy memoised instance variables defined in `extend/pathname.rb` and `DiskUsageExtension` (`@magic_number`, `@file_type`, `@zipinfo`, `@which_install_info`, `@disk_usage`, `@file_count`) up front via a new prepended `EagerInitializeExtension` module, so every `Pathname` instance shares the same object shape and we no longer hit: warning: The class Pathname reached 8 shape variations, instance variables accesses will be slower and memory usage increased. --- Library/Homebrew/extend/pathname.rb | 2 ++ .../pathname/eager_initialize_extension.rb | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 Library/Homebrew/extend/pathname/eager_initialize_extension.rb diff --git a/Library/Homebrew/extend/pathname.rb b/Library/Homebrew/extend/pathname.rb index e329ca97282a9..175f834c879d7 100644 --- a/Library/Homebrew/extend/pathname.rb +++ b/Library/Homebrew/extend/pathname.rb @@ -3,6 +3,7 @@ require "system_command" require "extend/pathname/disk_usage_extension" +require "extend/pathname/eager_initialize_extension" require "extend/pathname/observer_pathname_extension" require "extend/pathname/write_mkpath_extension" require "utils/output" @@ -29,6 +30,7 @@ class Pathname include SystemCommand::Mixin include DiskUsageExtension include Utils::Output::Mixin + prepend EagerInitializeExtension sig { void } def self.activate_extensions! diff --git a/Library/Homebrew/extend/pathname/eager_initialize_extension.rb b/Library/Homebrew/extend/pathname/eager_initialize_extension.rb new file mode 100644 index 0000000000000..f7d7c1c17bbee --- /dev/null +++ b/Library/Homebrew/extend/pathname/eager_initialize_extension.rb @@ -0,0 +1,19 @@ +# typed: strict +# frozen_string_literal: true + +module EagerInitializeExtension + extend T::Helpers + + requires_ancestor { Pathname } + + sig { params(args: T.untyped).void } + def initialize(*args) + @magic_number = T.let(nil, T.nilable(String)) + @file_type = T.let(nil, T.nilable(String)) + @zipinfo = T.let(nil, T.nilable(T::Array[String])) + @which_install_info = T.let(nil, T.nilable(String)) + @disk_usage = T.let(nil, T.nilable(Integer)) + @file_count = T.let(nil, T.nilable(Integer)) + super + end +end From 3af57b9692d9be50e58519970af752d737e20501 Mon Sep 17 00:00:00 2001 From: Patrick Linnane Date: Wed, 20 May 2026 21:48:11 -0700 Subject: [PATCH 2/3] pathname: spec EagerInitializeExtension ivar pre-allocation Lock the eager-init invariant so adding a new lazy memoised ivar to `Pathname`/`DiskUsageExtension` without updating `EagerInitializeExtension` (and thus reintroducing shape churn) is caught in CI. --- Library/Homebrew/test/pathname_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Library/Homebrew/test/pathname_spec.rb b/Library/Homebrew/test/pathname_spec.rb index 4f498505dd3de..71bbd9903314c 100644 --- a/Library/Homebrew/test/pathname_spec.rb +++ b/Library/Homebrew/test/pathname_spec.rb @@ -12,6 +12,16 @@ let(:file) { src/"foo" } let(:dir) { src/"bar" } + describe EagerInitializeExtension do + it "defines the lazy memoised ivars on every new Pathname" do + pathname = Pathname.new(file.to_s) + [:@magic_number, :@file_type, :@zipinfo, :@which_install_info, :@disk_usage, :@file_count].each do |ivar| + expect(pathname.instance_variable_defined?(ivar)).to be(true), "expected #{ivar} to be defined" + expect(pathname.instance_variable_get(ivar)).to be_nil + end + end + end + describe DiskUsageExtension do before do mkdir_p dir/"a-directory" From 9bc2ff4bae52a2af9e23064d9e7d9491b63c75e0 Mon Sep 17 00:00:00 2001 From: Patrick Linnane Date: Thu, 21 May 2026 08:13:14 -0700 Subject: [PATCH 3/3] pathname: document EagerInitializeExtension rationale Explain why the module exists (shape cache warning) and the contract for adding/removing lazy memoised ivars on `Pathname` or its mixed-in extensions, per review feedback. --- .../Homebrew/extend/pathname/eager_initialize_extension.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Library/Homebrew/extend/pathname/eager_initialize_extension.rb b/Library/Homebrew/extend/pathname/eager_initialize_extension.rb index f7d7c1c17bbee..14c689ebcf61e 100644 --- a/Library/Homebrew/extend/pathname/eager_initialize_extension.rb +++ b/Library/Homebrew/extend/pathname/eager_initialize_extension.rb @@ -1,6 +1,11 @@ # typed: strict # frozen_string_literal: true +# Eagerly initialises {Pathname}'s lazy memoised ivars so every instance +# shares one object shape, avoiding Ruby's shape-variation warning. +# +# Any new `@x ||= ...` ivar added to {Pathname} or its mixed-in extensions +# must also be added to `#initialize` below to keep the shape stable. module EagerInitializeExtension extend T::Helpers