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