whole_func_string: function MarkChangedFunctions(code_info_tree, chunks) {
  
      // A convenient iterator over diff chunks that also translates
      // positions from old to new in a current non-changed part of script.
      var chunk_it = new function() {
        var chunk_index = 0;
        var pos_diff = 0;
        this.current = function() { return chunks[chunk_index]; };
        this.next = function() {
          var chunk = chunks[chunk_index];
          pos_diff = chunk.pos2 + chunk.len2 - (chunk.pos1 + chunk.len1);
          chunk_index++;
        };
        this.done = function() { return chunk_index >= chunks.length; };
        this.TranslatePos = function(pos) { return pos + pos_diff; };
      };
  
      // A recursive function that processes internals of a function and all its
      // inner functions. Iterator chunk_it initially points to a chunk that is
      // below function start.
      function ProcessInternals(info_node) {
        info_node.new_start_pos = chunk_it.TranslatePos(
            info_node.info.start_position);
        var child_index = 0;
        var code_changed = false;
        var source_changed = false;
        // Simultaneously iterates over child functions and over chunks.
        while (!chunk_it.done() &&
            chunk_it.current().pos1 < info_node.info.end_position) {
          if (child_index < info_node.children.length) {
            var child = info_node.children[child_index];
  
            if (child.info.end_position <= chunk_it.current().pos1) {
              ProcessUnchangedChild(child);
              child_index++;
              continue;
            } else if (child.info.start_position >=
                chunk_it.current().pos1 + chunk_it.current().len1) {
              code_changed = true;
              chunk_it.next();
              continue;
            } else if (child.info.start_position <= chunk_it.current().pos1 &&
                child.info.end_position >= chunk_it.current().pos1 +
                chunk_it.current().len1) {
              ProcessInternals(child);
              source_changed = source_changed ||
                  ( child.status != FunctionStatus.UNCHANGED );
              code_changed = code_changed ||
                  ( child.status == FunctionStatus.DAMAGED );
              child_index++;
              continue;
            } else {
              code_changed = true;
              child.status = FunctionStatus.DAMAGED;
              child.status_explanation =
                  "Text diff overlaps with function boundary";
              child_index++;
              continue;
            }
          } else {
            if (chunk_it.current().pos1 + chunk_it.current().len1 <=
                info_node.info.end_position) {
              info_node.status = FunctionStatus.CHANGED;
              chunk_it.next();
              continue;
            } else {
              info_node.status = FunctionStatus.DAMAGED;
              info_node.status_explanation =
                  "Text diff overlaps with function boundary";
              return;
            }
          }
          Assert("Unreachable", false);
        }
        while (child_index < info_node.children.length) {
          var child = info_node.children[child_index];
          ProcessUnchangedChild(child);
          child_index++;
        }
        if (code_changed) {
          info_node.status = FunctionStatus.CHANGED;
        } else if (source_changed) {
          info_node.status = FunctionStatus.SOURCE_CHANGED;
        }
        info_node.new_end_pos =
            chunk_it.TranslatePos(info_node.info.end_position);
      }
  
      function ProcessUnchangedChild(node) {
        node.new_start_pos = chunk_it.TranslatePos(node.info.start_position);
        node.new_end_pos = chunk_it.TranslatePos(node.info.end_position);
      }
  
      ProcessInternals(code_info_tree);
    }
func_code_string: function MarkChangedFunctions(code_info_tree, chunks) {
  
      // A convenient iterator over diff chunks that also translates
      // positions from old to new in a current non-changed part of script.
      var chunk_it = new function() {
        var chunk_index = 0;
        var pos_diff = 0;
        this.current = function() { return chunks[chunk_index]; };
        this.next = function() {
          var chunk = chunks[chunk_index];
          pos_diff = chunk.pos2 + chunk.len2 - (chunk.pos1 + chunk.len1);
          chunk_index++;
        };
        this.done = function() { return chunk_index >= chunks.length; };
        this.TranslatePos = function(pos) { return pos + pos_diff; };
      };
  
      // A recursive function that processes internals of a function and all its
      // inner functions. Iterator chunk_it initially points to a chunk that is
      // below function start.
      function ProcessInternals(info_node) {
        info_node.new_start_pos = chunk_it.TranslatePos(
            info_node.info.start_position);
        var child_index = 0;
        var code_changed = false;
        var source_changed = false;
        // Simultaneously iterates over child functions and over chunks.
        while (!chunk_it.done() &&
            chunk_it.current().pos1 < info_node.info.end_position) {
          if (child_index < info_node.children.length) {
            var child = info_node.children[child_index];
  
            if (child.info.end_position <= chunk_it.current().pos1) {
              ProcessUnchangedChild(child);
              child_index++;
              continue;
            } else if (child.info.start_position >=
                chunk_it.current().pos1 + chunk_it.current().len1) {
              code_changed = true;
              chunk_it.next();
              continue;
            } else if (child.info.start_position <= chunk_it.current().pos1 &&
                child.info.end_position >= chunk_it.current().pos1 +
                chunk_it.current().len1) {
              ProcessInternals(child);
              source_changed = source_changed ||
                  ( child.status != FunctionStatus.UNCHANGED );
              code_changed = code_changed ||
                  ( child.status == FunctionStatus.DAMAGED );
              child_index++;
              continue;
            } else {
              code_changed = true;
              child.status = FunctionStatus.DAMAGED;
              child.status_explanation =
                  "Text diff overlaps with function boundary";
              child_index++;
              continue;
            }
          } else {
            if (chunk_it.current().pos1 + chunk_it.current().len1 <=
                info_node.info.end_position) {
              info_node.status = FunctionStatus.CHANGED;
              chunk_it.next();
              continue;
            } else {
              info_node.status = FunctionStatus.DAMAGED;
              info_node.status_explanation =
                  "Text diff overlaps with function boundary";
              return;
            }
          }
          Assert("Unreachable", false);
        }
        while (child_index < info_node.children.length) {
          var child = info_node.children[child_index];
          ProcessUnchangedChild(child);
          child_index++;
        }
        if (code_changed) {
          info_node.status = FunctionStatus.CHANGED;
        } else if (source_changed) {
          info_node.status = FunctionStatus.SOURCE_CHANGED;
        }
        info_node.new_end_pos =
            chunk_it.TranslatePos(info_node.info.end_position);
      }
  
      function ProcessUnchangedChild(node) {
        node.new_start_pos = chunk_it.TranslatePos(node.info.start_position);
        node.new_end_pos = chunk_it.TranslatePos(node.info.end_position);
      }
  
      ProcessInternals(code_info_tree);
    }
func_documentation_string: Applies a list of the textual diff chunks onto the tree of functions. Determines status of each function (from unchanged to damaged). However children of unchanged functions are ignored.