This file is indexed.

/usr/lib/ruby/vendor_ruby/rspec/mocks/any_instance/recorder.rb is in ruby-rspec-mocks 3.5.0c3e0m0s0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
module RSpec
  module Mocks
    module AnyInstance
      # Given a class `TheClass`, `TheClass.any_instance` returns a `Recorder`,
      # which records stubs and message expectations for later playback on
      # instances of `TheClass`.
      #
      # Further constraints are stored in instances of [Chain](Chain).
      #
      # @see AnyInstance
      # @see Chain
      class Recorder
        # @private
        attr_reader :message_chains, :stubs, :klass

        def initialize(klass)
          @message_chains = MessageChains.new
          @stubs = Hash.new { |hash, key| hash[key] = [] }
          @observed_methods = []
          @played_methods = {}
          @backed_up_method_owner = {}
          @klass = klass
          @expectation_set = false
        end

        # Initializes the recording a stub to be played back against any
        # instance of this object that invokes the submitted method.
        #
        # @see Methods#stub
        def stub(method_name, &block)
          observe!(method_name)
          message_chains.add(method_name, StubChain.new(self, method_name, &block))
        end

        # Initializes the recording a stub chain to be played back against any
        # instance of this object that invokes the method matching the first
        # argument.
        #
        # @see Methods#stub_chain
        def stub_chain(*method_names_and_optional_return_values, &block)
          normalize_chain(*method_names_and_optional_return_values) do |method_name, args|
            observe!(method_name)
            message_chains.add(method_name, StubChainChain.new(self, *args, &block))
          end
        end

        # @private
        def expect_chain(*method_names_and_optional_return_values, &block)
          @expectation_set = true
          normalize_chain(*method_names_and_optional_return_values) do |method_name, args|
            observe!(method_name)
            message_chains.add(method_name, ExpectChainChain.new(self, *args, &block))
          end
        end

        # Initializes the recording a message expectation to be played back
        # against any instance of this object that invokes the submitted
        # method.
        #
        # @see Methods#should_receive
        def should_receive(method_name, &block)
          @expectation_set = true
          observe!(method_name)
          message_chains.add(method_name, PositiveExpectationChain.new(self, method_name, &block))
        end

        # The opposite of `should_receive`
        #
        # @see Methods#should_not_receive
        def should_not_receive(method_name, &block)
          should_receive(method_name, &block).never
        end

        # Removes any previously recorded stubs, stub_chains or message
        # expectations that use `method_name`.
        #
        # @see Methods#unstub
        def unstub(method_name)
          unless @observed_methods.include?(method_name.to_sym)
            AnyInstance.error_generator.raise_method_not_stubbed_error(method_name)
          end
          message_chains.remove_stub_chains_for!(method_name)
          stubs[method_name].clear
          stop_observing!(method_name) unless message_chains.has_expectation?(method_name)
        end

        # @api private
        #
        # Used internally to verify that message expectations have been
        # fulfilled.
        def verify
          return unless @expectation_set
          return if message_chains.all_expectations_fulfilled?

          AnyInstance.error_generator.raise_second_instance_received_message_error(message_chains.unfulfilled_expectations)
        end

        # @private
        def stop_all_observation!
          @observed_methods.each { |method_name| restore_method!(method_name) }
        end

        # @private
        def playback!(instance, method_name)
          RSpec::Mocks.space.ensure_registered(instance)
          message_chains.playback!(instance, method_name)
          @played_methods[method_name] = instance
          received_expected_message!(method_name) if message_chains.has_expectation?(method_name)
        end

        # @private
        def instance_that_received(method_name)
          @played_methods[method_name]
        end

        # @private
        def build_alias_method_name(method_name)
          "__#{method_name}_without_any_instance__"
        end

        # @private
        def already_observing?(method_name)
          @observed_methods.include?(method_name) || super_class_observing?(method_name)
        end

        # @private
        def notify_received_message(_object, message, args, _blk)
          has_expectation = false

          message_chains.each_unfulfilled_expectation_matching(message, *args) do |expectation|
            has_expectation = true
            expectation.expectation_fulfilled!
          end

          return unless has_expectation

          restore_method!(message)
          mark_invoked!(message)
        end

      protected

        def stop_observing!(method_name)
          restore_method!(method_name)
          @observed_methods.delete(method_name)
          super_class_observers_for(method_name).each do |ancestor|
            ::RSpec::Mocks.space.
              any_instance_recorder_for(ancestor).stop_observing!(method_name)
          end
        end

      private

        def ancestor_is_an_observer?(method_name)
          lambda do |ancestor|
            unless ancestor == @klass
              ::RSpec::Mocks.space.
                any_instance_recorder_for(ancestor).already_observing?(method_name)
            end
          end
        end

        def super_class_observers_for(method_name)
          @klass.ancestors.select(&ancestor_is_an_observer?(method_name))
        end

        def super_class_observing?(method_name)
          @klass.ancestors.any?(&ancestor_is_an_observer?(method_name))
        end

        def normalize_chain(*args)
          args.shift.to_s.split('.').map { |s| s.to_sym }.reverse.each { |a| args.unshift a }
          yield args.first, args
        end

        def received_expected_message!(method_name)
          message_chains.received_expected_message!(method_name)
          restore_method!(method_name)
          mark_invoked!(method_name)
        end

        def restore_method!(method_name)
          if public_protected_or_private_method_defined?(build_alias_method_name(method_name))
            restore_original_method!(method_name)
          else
            remove_dummy_method!(method_name)
          end
        end

        def restore_original_method!(method_name)
          return unless @klass.instance_method(method_name).owner == @klass

          alias_method_name = build_alias_method_name(method_name)
          @klass.class_exec(@backed_up_method_owner) do |backed_up_method_owner|
            remove_method method_name

            # A @klass can have methods implemented (see Method#owner) in @klass
            # or inherited from a superclass. In ruby 2.2 and earlier, we can copy
            # a method regardless of the 'owner' and restore it to @klass after
            # because a call to 'super' from @klass's copied method would end up
            # calling the original class's superclass's method.
            #
            # With the commit below, available starting in 2.3.0, ruby changed
            # this behavior and a call to 'super' from the method copied to @klass
            # will call @klass's superclass method, which is the original
            # implementer of this method!  This leads to very strange errors
            # if @klass's copied method calls 'super', since it would end up
            # calling itself, the original method implemented in @klass's
            # superclass.
            #
            # For ruby 2.3 and above, we need to only restore methods that
            # @klass originally owned.
            #
            # https://github.com/ruby/ruby/commit/c8854d2ca4be9ee6946e6d17b0e17d9ef130ee81
            if RUBY_VERSION < "2.3" || backed_up_method_owner[method_name.to_sym] == self
              alias_method method_name, alias_method_name
            end
            remove_method alias_method_name
          end
        end

        def remove_dummy_method!(method_name)
          @klass.class_exec do
            remove_method method_name
          end
        end

        def backup_method!(method_name)
          return unless public_protected_or_private_method_defined?(method_name)

          alias_method_name = build_alias_method_name(method_name)
          @backed_up_method_owner[method_name.to_sym] ||= @klass.instance_method(method_name).owner
          @klass.class_exec do
            alias_method alias_method_name, method_name
          end
        end

        def public_protected_or_private_method_defined?(method_name)
          MethodReference.method_defined_at_any_visibility?(@klass, method_name)
        end

        def observe!(method_name)
          allow_no_prepended_module_definition_of(method_name)

          if RSpec::Mocks.configuration.verify_partial_doubles?
            unless public_protected_or_private_method_defined?(method_name)
              AnyInstance.error_generator.raise_does_not_implement_error(@klass, method_name)
            end
          end

          stop_observing!(method_name) if already_observing?(method_name)
          @observed_methods << method_name
          backup_method!(method_name)
          recorder = self
          @klass.__send__(:define_method, method_name) do |*args, &blk|
            recorder.playback!(self, method_name)
            __send__(method_name, *args, &blk)
          end
        end

        def mark_invoked!(method_name)
          backup_method!(method_name)
          recorder = self
          @klass.__send__(:define_method, method_name) do |*_args, &_blk|
            invoked_instance = recorder.instance_that_received(method_name)
            inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>"
            AnyInstance.error_generator.raise_message_already_received_by_other_instance_error(
              method_name, inspect, invoked_instance
            )
          end
        end

        if Support::RubyFeatures.module_prepends_supported?
          def allow_no_prepended_module_definition_of(method_name)
            prepended_modules = RSpec::Mocks::Proxy.prepended_modules_of(@klass)
            problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) }
            return unless problem_mod

            AnyInstance.error_generator.raise_not_supported_with_prepend_error(method_name, problem_mod)
          end
        else
          def allow_no_prepended_module_definition_of(_method_name)
            # nothing to do; prepends aren't supported on this version of ruby
          end
        end
      end
    end
  end
end