| Index: src/liveedit.cc
 | 
| diff --git a/src/liveedit.cc b/src/liveedit.cc
 | 
| index 513cc0233f18de5464080317b4a80027d1c6bdbd..4533f9c7ddab854446ab33ece7b4ed93685d2587 100644
 | 
| --- a/src/liveedit.cc
 | 
| +++ b/src/liveedit.cc
 | 
| @@ -346,8 +346,82 @@ void LiveEdit::WrapSharedFunctionInfos(Handle<JSArray> array) {
 | 
|  }
 | 
|  
 | 
|  
 | 
| +// Visitor that collects all references to a particular code object,
 | 
| +// including "CODE_TARGET" references in other code objects.
 | 
| +// It works in context of ZoneScope.
 | 
| +class ReferenceCollectorVisitor : public ObjectVisitor {
 | 
| + public:
 | 
| +  explicit ReferenceCollectorVisitor(Code* original)
 | 
| +      : original_(original), rvalues_(10), reloc_infos_(10) {
 | 
| +  }
 | 
| +
 | 
| +  virtual void VisitPointers(Object** start, Object** end) {
 | 
| +    for (Object** p = start; p < end; p++) {
 | 
| +      if (*p == original_) {
 | 
| +        rvalues_.Add(p);
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  void VisitCodeTarget(RelocInfo* rinfo) {
 | 
| +    ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
 | 
| +    if (Code::GetCodeFromTargetAddress(rinfo->target_address()) == original_) {
 | 
| +      reloc_infos_.Add(*rinfo);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  virtual void VisitDebugTarget(RelocInfo* rinfo) {
 | 
| +    VisitCodeTarget(rinfo);
 | 
| +  }
 | 
| +
 | 
| +  // Post-visiting method that iterates over all collected references and
 | 
| +  // modifies them.
 | 
| +  void Replace(Code* substitution) {
 | 
| +    for (int i = 0; i < rvalues_.length(); i++) {
 | 
| +      *(rvalues_[i]) = substitution;
 | 
| +    }
 | 
| +    for (int i = 0; i < reloc_infos_.length(); i++) {
 | 
| +      reloc_infos_[i].set_target_address(substitution->instruction_start());
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  Code* original_;
 | 
| +  ZoneList<Object**> rvalues_;
 | 
| +  ZoneList<RelocInfo> reloc_infos_;
 | 
| +};
 | 
| +
 | 
| +// Finds all references to original and replaces them with substitution.
 | 
| +static void ReplaceCodeObject(Code* original, Code* substitution) {
 | 
| +  ASSERT(!Heap::InNewSpace(substitution));
 | 
| +
 | 
| +  AssertNoAllocation no_allocations_please;
 | 
| +
 | 
| +  // A zone scope for ReferenceCollectorVisitor.
 | 
| +  ZoneScope scope(DELETE_ON_EXIT);
 | 
| +
 | 
| +  ReferenceCollectorVisitor visitor(original);
 | 
| +
 | 
| +  // Iterate over all roots. Stack frames may have pointer into original code,
 | 
| +  // so temporary replace the pointers with offset numbers
 | 
| +  // in prologue/epilogue.
 | 
| +  ThreadManager::MarkCompactPrologue(true);
 | 
| +  Heap::IterateStrongRoots(&visitor, VISIT_ALL);
 | 
| +  ThreadManager::MarkCompactEpilogue(true);
 | 
| +
 | 
| +  // Now iterate over all pointers of all objects, including code_target
 | 
| +  // implicit pointers.
 | 
| +  HeapIterator iterator;
 | 
| +  for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
 | 
| +    obj->Iterate(&visitor);
 | 
| +  }
 | 
| +
 | 
| +  visitor.Replace(substitution);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  void LiveEdit::ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
 | 
| -                                 Handle<JSArray> shared_info_array) {
 | 
| +                                   Handle<JSArray> shared_info_array) {
 | 
|    HandleScope scope;
 | 
|  
 | 
|    FunctionInfoWrapper compile_info_wrapper(new_compile_info_array);
 | 
| @@ -355,8 +429,9 @@ void LiveEdit::ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
 | 
|  
 | 
|    Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
 | 
|  
 | 
| -  shared_info->set_code(*(compile_info_wrapper.GetFunctionCode()),
 | 
| -                        UPDATE_WRITE_BARRIER);
 | 
| +  ReplaceCodeObject(shared_info->code(),
 | 
| +                       *(compile_info_wrapper.GetFunctionCode()));
 | 
| +
 | 
|    shared_info->set_start_position(compile_info_wrapper.GetStartPosition());
 | 
|    shared_info->set_end_position(compile_info_wrapper.GetEndPosition());
 | 
|    // update breakpoints, original code, constructor stub
 | 
| @@ -389,22 +464,137 @@ static int TranslatePosition(int original_position,
 | 
|    for (int i = 0; i < array_len; i += 3) {
 | 
|      int chunk_start =
 | 
|          Smi::cast(position_change_array->GetElement(i))->value();
 | 
| -    int chunk_end =
 | 
| -        Smi::cast(position_change_array->GetElement(i + 1))->value();
 | 
| -    int chunk_changed_end =
 | 
| -        Smi::cast(position_change_array->GetElement(i + 2))->value();
 | 
| -    position_diff = chunk_changed_end - chunk_end;
 | 
|      if (original_position < chunk_start) {
 | 
|        break;
 | 
|      }
 | 
| +    int chunk_end =
 | 
| +        Smi::cast(position_change_array->GetElement(i + 1))->value();
 | 
|      // Position mustn't be inside a chunk.
 | 
|      ASSERT(original_position >= chunk_end);
 | 
| +    int chunk_changed_end =
 | 
| +        Smi::cast(position_change_array->GetElement(i + 2))->value();
 | 
| +    position_diff = chunk_changed_end - chunk_end;
 | 
|    }
 | 
|  
 | 
|    return original_position + position_diff;
 | 
|  }
 | 
|  
 | 
|  
 | 
| +// Auto-growing buffer for writing relocation info code section. This buffer
 | 
| +// is a simplified version of buffer from Assembler. Unlike Assembler, this
 | 
| +// class is platform-independent and it works without dealing with instructions.
 | 
| +// As specified by RelocInfo format, the buffer is filled in reversed order:
 | 
| +// from upper to lower addresses.
 | 
| +// It uses NewArray/DeleteArray for memory management.
 | 
| +class RelocInfoBuffer {
 | 
| + public:
 | 
| +  RelocInfoBuffer(int buffer_initial_capicity, byte* pc) {
 | 
| +    buffer_size_ = buffer_initial_capicity + kBufferGap;
 | 
| +    buffer_ = NewArray<byte>(buffer_size_);
 | 
| +
 | 
| +    reloc_info_writer_.Reposition(buffer_ + buffer_size_, pc);
 | 
| +  }
 | 
| +  ~RelocInfoBuffer() {
 | 
| +    DeleteArray(buffer_);
 | 
| +  }
 | 
| +
 | 
| +  // As specified by RelocInfo format, the buffer is filled in reversed order:
 | 
| +  // from upper to lower addresses.
 | 
| +  void Write(const RelocInfo* rinfo) {
 | 
| +    if (buffer_ + kBufferGap >= reloc_info_writer_.pos()) {
 | 
| +      Grow();
 | 
| +    }
 | 
| +    reloc_info_writer_.Write(rinfo);
 | 
| +  }
 | 
| +
 | 
| +  Vector<byte> GetResult() {
 | 
| +    // Return the bytes from pos up to end of buffer.
 | 
| +    return Vector<byte>(reloc_info_writer_.pos(),
 | 
| +                        buffer_ + buffer_size_ - reloc_info_writer_.pos());
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  void Grow() {
 | 
| +    // Compute new buffer size.
 | 
| +    int new_buffer_size;
 | 
| +    if (buffer_size_ < 2 * KB) {
 | 
| +      new_buffer_size = 4 * KB;
 | 
| +    } else {
 | 
| +      new_buffer_size = 2 * buffer_size_;
 | 
| +    }
 | 
| +    // Some internal data structures overflow for very large buffers,
 | 
| +    // they must ensure that kMaximalBufferSize is not too large.
 | 
| +    if (new_buffer_size > kMaximalBufferSize) {
 | 
| +      V8::FatalProcessOutOfMemory("RelocInfoBuffer::GrowBuffer");
 | 
| +    }
 | 
| +
 | 
| +    // Setup new buffer.
 | 
| +    byte* new_buffer = NewArray<byte>(new_buffer_size);
 | 
| +
 | 
| +    // Copy the data.
 | 
| +    int curently_used_size = buffer_ + buffer_size_ - reloc_info_writer_.pos();
 | 
| +    memmove(new_buffer + new_buffer_size - curently_used_size,
 | 
| +            reloc_info_writer_.pos(), curently_used_size);
 | 
| +
 | 
| +    reloc_info_writer_.Reposition(
 | 
| +        new_buffer + new_buffer_size - curently_used_size,
 | 
| +        reloc_info_writer_.last_pc());
 | 
| +
 | 
| +    DeleteArray(buffer_);
 | 
| +    buffer_ = new_buffer;
 | 
| +    buffer_size_ = new_buffer_size;
 | 
| +  }
 | 
| +
 | 
| +  RelocInfoWriter reloc_info_writer_;
 | 
| +  byte* buffer_;
 | 
| +  int buffer_size_;
 | 
| +
 | 
| +  static const int kBufferGap = 8;
 | 
| +  static const int kMaximalBufferSize = 512*MB;
 | 
| +};
 | 
| +
 | 
| +// Patch positions in code (changes relocation info section) and possibly
 | 
| +// returns new instance of code.
 | 
| +static Handle<Code> PatchPositionsInCode(Handle<Code> code,
 | 
| +    Handle<JSArray> position_change_array) {
 | 
| +
 | 
| +  RelocInfoBuffer buffer_writer(code->relocation_size(),
 | 
| +                                code->instruction_start());
 | 
| +
 | 
| +  {
 | 
| +    AssertNoAllocation no_allocations_please;
 | 
| +    for (RelocIterator it(*code); !it.done(); it.next()) {
 | 
| +      RelocInfo* rinfo = it.rinfo();
 | 
| +      if (RelocInfo::IsPosition(rinfo->rmode())) {
 | 
| +        int position = static_cast<int>(rinfo->data());
 | 
| +        int new_position = TranslatePosition(position,
 | 
| +                                             position_change_array);
 | 
| +        if (position != new_position) {
 | 
| +          RelocInfo info_copy(rinfo->pc(), rinfo->rmode(), new_position);
 | 
| +          buffer_writer.Write(&info_copy);
 | 
| +          continue;
 | 
| +        }
 | 
| +      }
 | 
| +      buffer_writer.Write(it.rinfo());
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  Vector<byte> buffer = buffer_writer.GetResult();
 | 
| +
 | 
| +  if (buffer.length() == code->relocation_size()) {
 | 
| +    // Simply patch relocation area of code.
 | 
| +    memcpy(code->relocation_start(), buffer.start(), buffer.length());
 | 
| +    return code;
 | 
| +  } else {
 | 
| +    // Relocation info section now has different size. We cannot simply
 | 
| +    // rewrite it inside code object. Instead we have to create a new
 | 
| +    // code object.
 | 
| +    Handle<Code> result(Factory::CopyCode(code, buffer));
 | 
| +    return result;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
|  void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array,
 | 
|                                        Handle<JSArray> position_change_array) {
 | 
|    SharedInfoWrapper shared_info_wrapper(shared_info_array);
 | 
| @@ -415,7 +605,45 @@ void LiveEdit::PatchFunctionPositions(Handle<JSArray> shared_info_array,
 | 
|    info->set_end_position(TranslatePosition(info->end_position(),
 | 
|                                             position_change_array));
 | 
|  
 | 
| -  // Also patch rinfos (both in working code and original code), breakpoints.
 | 
| +  info->set_function_token_position(
 | 
| +      TranslatePosition(info->function_token_position(),
 | 
| +      position_change_array));
 | 
| +
 | 
| +  // Patch relocation info section of the code.
 | 
| +  Handle<Code> patched_code = PatchPositionsInCode(Handle<Code>(info->code()),
 | 
| +                                                   position_change_array);
 | 
| +  if (*patched_code != info->code()) {
 | 
| +    // Replace all references to the code across the heap. In particular,
 | 
| +    // some stubs may refer to this code and this code may be being executed
 | 
| +    // on stack (it is safe to substitute the code object on stack, because
 | 
| +    // we only change the structure of rinfo and leave instructions untouched).
 | 
| +    ReplaceCodeObject(info->code(), *patched_code);
 | 
| +  }
 | 
| +
 | 
| +  if (info->debug_info()->IsDebugInfo()) {
 | 
| +    Handle<DebugInfo> debug_info(DebugInfo::cast(info->debug_info()));
 | 
| +    Handle<Code> patched_orig_code =
 | 
| +        PatchPositionsInCode(Handle<Code>(debug_info->original_code()),
 | 
| +                             position_change_array);
 | 
| +    if (*patched_orig_code != debug_info->original_code()) {
 | 
| +      // Do not use expensive ReplaceCodeObject for original_code, because we
 | 
| +      // do not expect any other references except this one.
 | 
| +      debug_info->set_original_code(*patched_orig_code);
 | 
| +    }
 | 
| +
 | 
| +    Handle<FixedArray> break_point_infos(debug_info->break_points());
 | 
| +    for (int i = 0; i < break_point_infos->length(); i++) {
 | 
| +      if (!break_point_infos->get(i)->IsBreakPointInfo()) {
 | 
| +        continue;
 | 
| +      }
 | 
| +      Handle<BreakPointInfo> info(
 | 
| +          BreakPointInfo::cast(break_point_infos->get(i)));
 | 
| +      int new_position = TranslatePosition(info->source_position()->value(),
 | 
| +          position_change_array);
 | 
| +      info->set_source_position(Smi::FromInt(new_position));
 | 
| +    }
 | 
| +  }
 | 
| +  // TODO(635): Also patch breakpoint objects in JS.
 | 
|  }
 | 
|  
 | 
|  
 | 
| 
 |