Bugs: Browse | Submit New | Admin

[#15303] Infinite loop when heckling Addressable::URI's extract_mapping method

Date:
2007-11-02 22:46
Priority:
3
Submitted By:
Bob Aman (sporkmonger)
Assigned To:
Ryan Davis (zenspider)
Category:
heckle
State:
Open
Summary:
Infinite loop when heckling Addressable::URI's extract_mapping method

Detailed description
It was really tough to get my hands on the verbose output; involved some hacking on RSpec, but:


........................................................................................................................
........................................................................................................................
........................................................................................................................
..........................................

Finished in 4.127994 seconds

402 examples, 0 failures
**********************************************************************
***  Addressable::URI#extract_mapping loaded with 59 possible mutations
**********************************************************************

59 mutations remaining...
Replacing Addressable::URI#extract_mapping with:

--- original
+++ mutation
-def extract_mapping(pattern, processor = nil)
+def extract_mapping(pattern, processor = 42)
   mapping = {  }
   variable_regexp = /\\{([#{Addressable::URI::CharacterClasses::UNRESERVED}]+)\}/
   variables = pattern.scan(variable_regexp).flatten
   variables.each { |v| mapping[v] = "" }
   escaped_pattern = Regexp.escape(pattern).gsub(/\\\{/, "{").gsub(/\\\}/, "}")
   regexp = Regexp.new(escaped_pattern.gsub(variable_regexp) do |v|
     capture_group = "(.*)"
     unless (processor == nil) then
       if processor.respond_to?(:match) then
         name = v.scan(variable_regexp).flatten[0]
         capture_group = "(#{processor.match(name)})"
       end
     end
     capture_group
   end)
   values = self.to_s.scan(regexp).flatten
   if (variables.size == values.size) then
     for i in (0...variables.size)
       (name = variables[i]
       value = values[i]
       unless (processor == nil) then
         value = processor.restore(name, value) if processor.respond_to?(:restore)
       end
       mapping[name] = value)
     end
     return mapping
   else
     return nil
   end
 end


*** SNIP ***



4 mutations remaining...
Replacing Addressable::URI#extract_mapping with:

--- original
+++ mutation
 def extract_mapping(pattern, processor = nil)
   mapping = {  }
   variable_regexp = /\\{([#{Addressable::URI::CharacterClasses::UNRESERVED}]+)\}/
   variables = pattern.scan(variable_regexp).flatten
   variables.each { |v| mapping[v] = "" }
   escaped_pattern = Regexp.escape(pattern).gsub(/\\\{/, "{").gsub(/\\\}/, "}")
   regexp = Regexp.new(escaped_pattern.gsub(variable_regexp) do |v|
     capture_group = "(.*)"
     unless (processor == nil) then
       if processor.respond_to?(:match) then
         name = v.scan(variable_regexp).flatten[0]
         capture_group = "(#{processor.match(name)})"
       end
     end
     capture_group
   end)
   values = self.to_s.scan(regexp).flatten
   if (variables.size == values.size) then
     for i in (0...variables.size)
       (name = variables[i]
       value = values[i]
-      unless (processor == nil) then
+      if (processor == nil) then
         value = processor.restore(name, value) if processor.respond_to?(:restore)
       end
       mapping[name] = value)
     end
     return mapping
   else
     return nil
   end
 end

3 mutations remaining...
Replacing Addressable::URI#extract_mapping with:

--- original
+++ mutation
 def extract_mapping(pattern, processor = nil)
   mapping = {  }
   variable_regexp = /\\{([#{Addressable::URI::CharacterClasses::UNRESERVED}]+)\}/
   variables = pattern.scan(variable_regexp).flatten
   variables.each { |v| mapping[v] = "" }
   escaped_pattern = Regexp.escape(pattern).gsub(/\\\{/, "{").gsub(/\\\}/, "}")
   regexp = Regexp.new(escaped_pattern.gsub(variable_regexp) do |v|
     capture_group = "(.*)"
     unless (processor == nil) then
       if processor.respond_to?(:match) then
         name = v.scan(variable_regexp).flatten[0]
         capture_group = "(#{processor.match(name)})"
       end
     end
     capture_group
   end)
   values = self.to_s.scan(regexp).flatten
   if (variables.size == values.size) then
+    return nil
+  else
     for i in (0...variables.size)
       (name = variables[i]
       value = values[i]
       unless (processor == nil) then
         value = processor.restore(name, value) if processor.respond_to?(:restore)
       end
       mapping[name] = value)
     end
     return mapping
-  else
-    return nil
   end
 end

2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



2 mutations remaining...
Replacing Addressable::URI#extract_mapping with:



Add A Comment: Notepad

Please login


Followup

Message
Date: 2012-06-04 21:03
Sender: Ryan Davis

Believe it or not, we've moved to github! If you think this issue
is still relevant, please re-file it under our project on github
(
https://github.com/seattlerb/$projectname ) and provide a link 
back to this ticket (if there is any comment stream). If not, 
please close this ticket.
Date: 2009-07-28 22:44
Sender: Bob Aman

I haven't the foggiest idea.  The code that originally
triggered this problem doesn't really exist anymore.
Date: 2009-07-28 21:52
Sender: Ryan Davis

I know it has been a long time... but we've addressed a number
of these. Does it still happen for you?

If not, please close.
Date: 2007-11-03 17:18
Sender: Bob Aman

This prevents the infinite loop.  I'm not sure it's the
right thing to do, but it works.

def heckle(exp)
  exp_copy = exp.deep_clone
  src = begin
          RubyToRuby.new.process(exp_copy)
        rescue => e
          puts "Error: #{e.message} with:
#{klass_name}##{method_name}: #{exp_copy.inspect}"
          raise e
        end

  original = RubyToRuby.new.process(@original_tree.deep_clone)
  
  @reporter.replacing(klass_name, method_name, original,
src) if @@debug

  if original == src
    puts "Mutation was identical to the original."
    mutatees.each do |mut|
      if mut.last.size > 0
        node = mut.last.delete(mut.last.first)
        mutation_count[node] += 1
        @mutated = true
        break
      end
    end
  end

  clean_name = method_name.to_s.gsub(/self\./, '')
  self.count += 1
  new_name = "h#{count}_#{clean_name}"

  klass = aliasing_class method_name
  klass.send :remove_method, new_name rescue nil
  klass.send :alias_method, new_name, clean_name
  klass.send :remove_method, clean_name rescue nil

  @klass.class_eval src, "(#{new_name})"
end
Date: 2007-11-03 15:44
Sender: Bob Aman

The content of mutatees at the point the infinite loop shows
up is:

{:if=>[], :false=>[], :dasgn=>[], :call=>[[:call,
[:lvar,
:variables], :each], [:call, [:lvar, :escaped_pattern],
:gsub, [:array, [:lvar, :variable_regexp]]]], :until=>[],
:lit=>[], :iasgn=>[], :while=>[], :cvasgn=>[],
:dasgn_curr=>[], :gasgn=>[], :lasgn=>[], :true=>[],
:str=>[]}
Date: 2007-11-03 15:22
Sender: Bob Aman

Nevermind, I was wrong.  Commenting out that line does not
prevent the infinite loop.
Date: 2007-11-03 14:15
Sender: Bob Aman

I could be wrong, but I think the first line of this method
is what's causing the infinite loop.  It looks like maybe
the tree never gets reset because the mutation is identical
to the original?


def reset_tree
  return unless original_tree != current_tree
  @mutated = false

  self.count += 1

  clean_name = method_name.to_s.gsub(/self\./, '')
  new_name = "h#{count}_#{clean_name}"

  klass = aliasing_class method_name

  klass.send :undef_method, new_name rescue nil
  klass.send :alias_method, new_name, clean_name
  klass.send :alias_method, clean_name,
"h1_#{clean_name}"
end
Date: 2007-11-02 23:54
Sender: Bob Aman

After adding some debug code to heckle, it appears that
perhaps the problem is caused by generating a mutation that
is exactly identical to the original source.

Attached Files:

Name Description Download
No Files Currently Attached

Changes:

Field Old Value Date By
category_idNone2009-07-24 09:49zenspider
assigned_tonone2007-11-02 22:49sporkmonger