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..14c689ebcf61e --- /dev/null +++ b/Library/Homebrew/extend/pathname/eager_initialize_extension.rb @@ -0,0 +1,24 @@ +# 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 + + 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 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"