Module: Contrast::Utils::Assess::PropagationMethodUtils

Included in:
Agent::Assess::Policy::PropagationMethod
Defined in:
lib/contrast/utils/assess/propagation_method_utils.rb

Overview

This module will include all methods for some internal validations in the PropagationMethod module and some other module methods from the same place, so we can ease the main module

Constant Summary collapse

APPEND_ACTION =
'APPEND'
CENTER_ACTION =
'CENTER'
INSERT_ACTION =
'INSERT'
BUFFER_ACTION =
'BUFFER'
KEEP_ACTION =
'KEEP'
NEXT_ACTION =
'NEXT'
NOOP_ACTION =
'NOOP'
PREPEND_ACTION =
'PREPEND'
REPLACE_ACTION =
'REPLACE'
REMOVE_ACTION =
'REMOVE'
REVERSE_ACTION =
'REVERSE'
RESPONSE_ACTION =
'RESPONSE'
SPLAT_ACTION =
'SPLAT'
SPLIT_ACTION =
'SPLIT'
DB_WRITE_ACTION =
'DB_WRITE'
CUSTOM_ACTION =
'CUSTOM'
ZERO_LENGTH_ACTIONS =
[DB_WRITE_ACTION, CUSTOM_ACTION, KEEP_ACTION, REPLACE_ACTION, SPLAT_ACTION].cs__freeze
PROPAGATION_ACTIONS =
{
    APPEND_ACTION => Contrast::Agent::Assess::Policy::Propagator::Append,
    CENTER_ACTION => Contrast::Agent::Assess::Policy::Propagator::Center,
    INSERT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Insert,
    KEEP_ACTION => Contrast::Agent::Assess::Policy::Propagator::Keep,
    NEXT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Next,
    BUFFER_ACTION => Contrast::Agent::Assess::Policy::Propagator::Buffer,
    NOOP_ACTION => nil,
    PREPEND_ACTION => Contrast::Agent::Assess::Policy::Propagator::Prepend,
    REPLACE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Replace,
    REMOVE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Remove,
    REVERSE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Reverse,
    RESPONSE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Response,
    SPLAT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Splat,
    SPLIT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Split
}.cs__freeze

Instance Method Summary collapse

Instance Method Details

#appropriate_source?(propagation_node, propagation_data, preshift) ⇒ Boolean

A source is appropriate if it is available for propagation

Parameters:

  • propagation_node (Contrast::Agent::Assess::Policy::PropagationNode)

    the node that governs this propagation event.

  • propagation_data (Contrast::Agent::Assess::Events::EventData)

    this will hold the object [Object] the Object on which the method was invoked args [Array] the Arguments with which the method was invoked

  • preshift (Contrast::Agent::Assess::PreShift)

    The capture of the state of the code just prior to the invocation of the patched method.

  • Returns:

    • (Boolean)
    
    
    149
    150
    151
    152
    153
    # File 'lib/contrast/utils/assess/propagation_method_utils.rb', line 149
    
    def appropriate_source? propagation_node, propagation_data, preshift
      return true if preshift
    
      propagation_node.use_original_object? && propagation_data&.object
    end

    #appropriate_target?(propagation_node, target) ⇒ Boolean

    We cannot propagate to frozen things that have not been updated to work with our property tracking, unless they're duplicable and the return. We probably shouldn't propagate to frozen things at all, as they're supposed to be immutable, but third parties do jenky things, so allow it as long as it is safe to do.

    Parameters:

    Returns:

    • (Boolean)

      if the target can be propagated to

    
    
    132
    133
    134
    135
    136
    137
    # File 'lib/contrast/utils/assess/propagation_method_utils.rb', line 132
    
    def appropriate_target? propagation_node, target
      # special handle Returns b/c we can do unfreezing magic during propagation
      return true if propagation_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
    
      Contrast::Agent::Assess::Tracker.trackable?(target)
    end

    #can_propagate?(propagation_node, preshift, target, propagation_data) ⇒ Boolean

    Before we do any work, we should check if we even need to. If the source and target of this patcher are not tracked, there's no need to do anything. A copy of nothing is still nothing.

    Parameters:

    • propagation_node (Contrast::Agent::Assess::Policy::PropagationNode)

      the node that governs this propagation event.

    • preshift (Contrast::Agent::Assess::PreShift)

      The capture of the state of the code just prior to the invocation of the patched method.

    • target (Object)

      the thing to which to propagate

    • propagation_data (Contrast::Agent::Assess::Events::EventData)

      this will hold the object [Object] the Object on which the method was invoked args [Array] the Arguments with which the method was invoked

      Returns:

      • (Boolean)
      
      
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      # File 'lib/contrast/utils/assess/propagation_method_utils.rb', line 101
      
      def can_propagate? propagation_node, preshift, target, propagation_data
        return false unless appropriate_target?(propagation_node, target)
        return true if Contrast::Utils::Assess::TrackingUtil.tracked?(target)
        return false unless appropriate_source?(propagation_node, propagation_data, preshift)
      
        propagation_node.sources.each do |source|
          case source
          when Contrast::Utils::ObjectShare::OBJECT_KEY
            source_object = if propagation_node.use_original_object?
                              propagation_data.object
                            else
                              preshift.object
                            end
            return true if Contrast::Utils::Assess::TrackingUtil.tracked?(source_object)
          else
            # has to be P, there's no ret source type (yet? ever?)
            return true if preshift.args && Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.args[source])
          end
        end
        false
      end

      #determine_target(propagation_node, ret, object, args) ⇒ Object

      
      
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      # File 'lib/contrast/utils/assess/propagation_method_utils.rb', line 46
      
      def determine_target propagation_node, ret, object, args
        target = propagation_node.targets[0]
        case target
        when Contrast::Utils::ObjectShare::OBJECT_KEY
          object
        when Contrast::Utils::ObjectShare::RETURN_KEY
          ret
        else
          args[target]
        end
      end

      #valid_length?(target, action) ⇒ Boolean

      If the action required needs a length and the target does not have one, the length is not valid

      Parameters:

      • target (Object)

        the thing to which to propagate

      • action (String)

        the name of the action taken during this propagation

      Returns:

      • (Boolean)
      
      
      79
      80
      81
      82
      83
      84
      85
      86
      87
      # File 'lib/contrast/utils/assess/propagation_method_utils.rb', line 79
      
      def valid_length? target, action
        return true if ZERO_LENGTH_ACTIONS.include?(action)
      
        if Contrast::Utils::DuckUtils.quacks_to?(target, :length)
          target.length != 0 # rubocop:disable Style/ZeroLengthPredicate
        else
          !target.to_s.empty?
        end
      end

      #valid_target?(target, propagation_node) ⇒ Boolean

      Custom actions tend to be the more complex of our propagations. Often, the method has to make decisions about the target based on the context with which the method was called. As such, defer determining if the target is valid to that method.

      In all other cases, a target is valid for propagation if it is not nil

      Parameters:

      Returns:

      • (Boolean)
      
      
      68
      69
      70
      71
      72
      # File 'lib/contrast/utils/assess/propagation_method_utils.rb', line 68
      
      def valid_target? target, propagation_node
        return true if propagation_node.action == CUSTOM_ACTION
      
        !!target
      end