Warning: session_start(): Session cannot be started after headers have already been sent in /home/tvrreohg/public_html/manga.php on line 13
# frozen_string_literal: true
module Puppet::Pops
module Types
# Keeps track of self recursion of conceptual 'this' and 'that' instances using two separate maps and
# a state. The class is used when tracking self recursion in two objects ('this' and 'that') simultaneously.
# A typical example of when this is needed is when testing if 'that' Puppet Type is assignable to 'this'
# Puppet Type since both types may contain self references.
#
# All comparisons are made using the `object_id` of the instance rather than the instance itself.
#
# Hash#compare_by_identity is intentionally not used since we only need to keep
# track of object ids we've seen before, based on object_id, so we don't store
# entire objects in memory.
#
# @api private
class RecursionGuard
attr_reader :state
NO_SELF_RECURSION = 0
SELF_RECURSION_IN_THIS = 1
SELF_RECURSION_IN_THAT = 2
SELF_RECURSION_IN_BOTH = 3
def initialize
@state = NO_SELF_RECURSION
end
# Checks if recursion was detected for the given argument in the 'this' context
# @param instance [Object] the instance to check
# @return [Integer] the resulting state
def recursive_this?(instance)
instance_variable_defined?(:@recursive_this_map) && @recursive_this_map.has_key?(instance.object_id) # rubocop:disable Lint/HashCompareByIdentity
end
# Checks if recursion was detected for the given argument in the 'that' context
# @param instance [Object] the instance to check
# @return [Integer] the resulting state
def recursive_that?(instance)
instance_variable_defined?(:@recursive_that_map) && @recursive_that_map.has_key?(instance.object_id) # rubocop:disable Lint/HashCompareByIdentity
end
# Add the given argument as 'this' invoke the given block with the resulting state
# @param instance [Object] the instance to add
# @return [Object] the result of yielding
def with_this(instance)
if (@state & SELF_RECURSION_IN_THIS) == 0
tc = this_count
@state |= SELF_RECURSION_IN_THIS if this_put(instance)
if tc < this_count
# recursive state detected
result = yield(@state)
# pop state
@state &= ~SELF_RECURSION_IN_THIS
@this_map.delete(instance.object_id)
return result
end
end
yield(@state)
end
# Add the given argument as 'that' invoke the given block with the resulting state
# @param instance [Object] the instance to add
# @return [Object] the result of yielding
def with_that(instance)
if (@state & SELF_RECURSION_IN_THAT) == 0
tc = that_count
@state |= SELF_RECURSION_IN_THAT if that_put(instance)
if tc < that_count
# recursive state detected
result = yield(@state)
# pop state
@state &= ~SELF_RECURSION_IN_THAT
@that_map.delete(instance.object_id)
return result
end
end
yield(@state)
end
# Add the given argument as 'this' and return the resulting state
# @param instance [Object] the instance to add
# @return [Integer] the resulting state
def add_this(instance)
if (@state & SELF_RECURSION_IN_THIS) == 0
@state |= SELF_RECURSION_IN_THIS if this_put(instance)
end
@state
end
# Add the given argument as 'that' and return the resulting state
# @param instance [Object] the instance to add
# @return [Integer] the resulting state
def add_that(instance)
if (@state & SELF_RECURSION_IN_THAT) == 0
@state |= SELF_RECURSION_IN_THAT if that_put(instance)
end
@state
end
# @return the number of objects added to the `this` map
def this_count
instance_variable_defined?(:@this_map) ? @this_map.size : 0
end
# @return the number of objects added to the `that` map
def that_count
instance_variable_defined?(:@that_map) ? @that_map.size : 0
end
private
def this_put(o)
id = o.object_id
@this_map ||= {}
if @this_map.has_key?(id)
@recursive_this_map ||= {}
@recursive_this_map[id] = true
true
else
@this_map[id] = true
false
end
end
def that_put(o)
id = o.object_id
@that_map ||= {}
if @that_map.has_key?(id)
@recursive_that_map ||= {}
@recursive_that_map[id] = true
true
else
@that_map[id] = true
false
end
end
end
end
end