lib/contrast/agent/assess/policy/propagator/trim.rb in contrast-agent-4.5.0 vs lib/contrast/agent/assess/policy/propagator/trim.rb in contrast-agent-4.6.0

- old
+ new

@@ -6,53 +6,34 @@ module Assess module Policy module Propagator # This class is specifically for String#tr(_s) propagation # - # Disclaimer: there may be a better way, but we're - # in a 'get it work' state. hopefully, we'll be in - # a 'get it right' state soon. + # Disclaimer: there may be a better way, but we're in a 'get it work' state. hopefully, we'll be in a 'get it + # right' state soon. module Trim class << self - def tr_tagger patcher, preshift, ret, _block + + # @param policy_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this + # propagation event. + # @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to + # the invocation of the patched method. + # @param ret [nil, String] the target to which to propagate. + # @return [nil, String] ret + def tr_tagger policy_node, preshift, ret, _block return ret unless ret && !ret.empty? return ret unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret)) - source = preshift.object - args = preshift.args - properties.copy_from(source, ret) - replace_string = args[1] - source_chars = source.chars - # if the replace string is empty, then there's a bunch of deletes. this - # functions the same as the Removal propagation. - if replace_string == Contrast::Utils::ObjectShare::EMPTY_STRING - Contrast::Agent::Assess::Policy::Propagator::Remove.handle_removal(source_chars, ret) - else - remove_ranges = [] - ret_chars = ret.chars - start = nil - source_chars.each_with_index do |char, idx| - if ret_chars[idx] == char - next unless start + properties.copy_from(preshift.object, ret) + handle_tr(policy_node, preshift, ret, properties) - remove_ranges << (start...idx) - start = nil - else - start ||= idx - end - end - # account for the last char being different - remove_ranges << (start...source_chars.length) if start - properties.delete_tags_at_ranges(remove_ranges, false) - end - properties.build_event( - patcher, + policy_node, ret, - source, + preshift.object, ret, - args, + preshift.args, 1) ret end def tr_s_tagger patcher, preshift, ret, _block @@ -67,9 +48,59 @@ ret, source, ret, args) ret + end + + private + + # @param policy_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this + # propagation event. + # @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to + # the invocation of the patched method. + # @param ret [String] the target to which to propagate. + # @param properties [Contrast::Agent::Assess::Properties] the properties of the ret + def handle_tr policy_node, preshift, ret, properties + source = preshift.object + replace_string = preshift.args[1] + + # if the replace string is empty, then there's a bunch of deletes. this functions the same as the + # Removal propagation. + if replace_string == Contrast::Utils::ObjectShare::EMPTY_STRING + Contrast::Agent::Assess::Policy::Propagator::Remove.handle_removal(policy_node, source, ret) + return + end + + # Otherwise, we need to target each insertion point. Based on the spec for #tr & #tr_s, the find is + # treated as a regex range, excepting the `\` character, which we'll need to escape. This converts to + # that form, wrapping the input in `[]`. + find_string = preshift.args[0] + find_string += '\\' if find_string.end_with?('\\') + find_regexp = Regexp.new("[#{ find_string }]") + + # Find the first instance to be replaced. If there isn't one, than nothing changed here. + idx = source.index(find_regexp) + return unless idx + + # Iterate over each change and record where it happened. B/c this is a one to one replace, the index of + # the replacement is always one; however, there may be adjacent replacements which become a single + # range. + start = idx + stop = idx + 1 + remove_ranges = [] + while (idx = source.index(find_regexp, idx + 1)) + # If the previous range ends at this index, we can expand that range to include this index. + # Otherwise, we need to record the held range and start a new one. + if stop != idx + remove_ranges << (start...stop) + start = idx + end + stop = idx + 1 + end + # Be sure to capture the last range in the holder. + remove_ranges << (start...stop) + properties.delete_tags_at_ranges(remove_ranges, false) end end end end end