Revision: 4533
Author: [email protected]
Date: Wed Apr 28 04:38:43 2010
Log: LiveEdit: breakpoints updates and fixes for related problems

Review URL: http://codereview.chromium.org/1800007
http://code.google.com/p/v8/source/detail?r=4533

Added:
 /branches/bleeding_edge/test/mjsunit/debug-liveedit-3.js
 /branches/bleeding_edge/test/mjsunit/debug-liveedit-breakpoints.js
 /branches/bleeding_edge/test/mjsunit/debug-liveedit-utils.js
Modified:
 /branches/bleeding_edge/src/codegen.cc
 /branches/bleeding_edge/src/compiler.cc
 /branches/bleeding_edge/src/debug-debugger.js
 /branches/bleeding_edge/src/full-codegen.cc
 /branches/bleeding_edge/src/liveedit-debugger.js
 /branches/bleeding_edge/src/liveedit.cc
 /branches/bleeding_edge/src/liveedit.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/src/runtime.h
 /branches/bleeding_edge/test/mjsunit/debug-liveedit-diff.js

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/debug-liveedit-3.js Wed Apr 28 04:38:43 2010
@@ -0,0 +1,69 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+
+// In this test case we edit a script so that techincally function text
+// hasen't been changed. However actually function became one level more nested
+// and must be recompiled because it uses variable from outer scope.
+
+
+Debug = debug.Debug
+
+var function_z_text =
+"  function Z() {\n"
++ "    return 2 + p;\n"
++ "  }\n";
+
+eval(
+"function Factory(p) {\n"
++ "return (\n"
++ function_z_text
++ ");\n"
++ "}\n"
+);
+
+var z6 = Factory(6);
+assertEquals(8, z6());
+
+var script = Debug.findScript(Factory);
+
+var new_source = script.source.replace(function_z_text, "function Intermediate() {\nreturn (\n" + function_z_text + ")\n;\n}\n");
+print("new source: " + new_source);
+
+var change_log = new Array();
+Debug.LiveEdit.SetScriptSource(script, new_source, change_log);
+print("Change log: " + JSON.stringify(change_log) + "\n");
+
+assertEquals(8, z6());
+
+var z100 = Factory(100)();
+
+assertEquals(102, z100());
+
+
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/debug-liveedit-breakpoints.js Wed Apr 28 04:38:43 2010
@@ -0,0 +1,97 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+
+Debug = debug.Debug
+
+var function_z_text =
+"  function Z() {\n"
++ "    return 'Z';\n" // Breakpoint line ( #6 )
++ "  }\n";
+
+eval(
+"function F25() {\n"
++ "  return 25;\n" // Breakpoint line ( #1 )
++ "}\n"
++ "function F26 () {\n"
++ "  var x = 20;\n"
++ function_z_text // function takes exactly 3 lines
+//                 // Breakpoint line ( #6 )
+//
++ "  var y = 6;\n"
++ "  return x + y;\n"
++ "}\n"
++ "function Nested() {\n"
++ "  var a = 30;\n"
++ "  return function F27() {\n"
++ "    var b = 3;\n" // Breakpoint line ( #14 )
++ "    return a - b;\n"
++ "  }\n"
++ "}\n"
+);
+
+
+assertEquals(25, F25());
+assertEquals(26, F26());
+
+var script = Debug.findScript(F25);
+
+Debug.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, script.id, 1, 1, "true || false || false"); +Debug.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, script.id, 6, 1, "true || false || false"); +Debug.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, script.id, 14, 1, "true || false || false");
+
+assertEquals(3, Debug.scriptBreakPoints().length);
+
+var new_source = script.source.replace(function_z_text, "");
+print("new source: " + new_source);
+
+var change_log = new Array();
+Debug.LiveEdit.SetScriptSource(script, new_source, change_log);
+print("Change log: " + JSON.stringify(change_log) + "\n");
+
+var breaks = Debug.scriptBreakPoints();
+
+// One breakpoint gets duplicated in a old version of script.
+assertTrue(breaks.length > 3 + 1);
+
+var breakpoints_in_script = 0;
+var break_position_map = {};
+for (var i = 0; i < breaks.length; i++) {
+  if (breaks[i].script_id() == script.id) {
+    break_position_map[breaks[i].line()] = true;
+    breakpoints_in_script++;
+  }
+}
+
+assertEquals(3, breakpoints_in_script);
+
+// Check 2 breakpoints. The one in deleted function should have been moved somewhere.
+assertTrue(break_position_map[1]);
+assertTrue(break_position_map[11]);
+
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/debug-liveedit-utils.js Wed Apr 28 04:38:43 2010
@@ -0,0 +1,97 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+
+Debug = debug.Debug
+
+function Return2010() {
+  return 2010;
+}
+
+
+// Diff it trivial: zero chunks
+var NoChunkTranslator = new Debug.LiveEdit.TestApi.PosTranslator([]);
+
+assertEquals(0, NoChunkTranslator.Translate(0));
+assertEquals(10, NoChunkTranslator.Translate(10));
+
+
+// Diff has one chunk
+var SingleChunkTranslator = new Debug.LiveEdit.TestApi.PosTranslator([20, 30, 25]);
+
+assertEquals(0, SingleChunkTranslator.Translate(0));
+assertEquals(5, SingleChunkTranslator.Translate(5));
+assertEquals(10, SingleChunkTranslator.Translate(10));
+assertEquals(19, SingleChunkTranslator.Translate(19));
+assertEquals(2010, SingleChunkTranslator.Translate(20, Return2010));
+assertEquals(25, SingleChunkTranslator.Translate(30));
+assertEquals(26, SingleChunkTranslator.Translate(31));
+assertEquals(2010, SingleChunkTranslator.Translate(26, Return2010));
+
+try {
+  SingleChunkTranslator.Translate(21);
+  assertTrue(false);
+} catch (ignore) {
+}
+try {
+  SingleChunkTranslator.Translate(24);
+  assertTrue(false);
+} catch (ignore) {
+}
+
+
+// Diff has several chunk (3). See the table below.
+
+/*
+chunks: (new <- old)
+ 10   10
+ 15   20
+
+ 35   40
+ 50   40
+
+ 70   60
+ 70   70
+*/
+
+var MultiChunkTranslator = new Debug.LiveEdit.TestApi.PosTranslator([10, 20, 15, 40, 40, 50, 60, 70, 70 ]);
+assertEquals(5, MultiChunkTranslator.Translate(5));
+assertEquals(9, MultiChunkTranslator.Translate(9));
+assertEquals(2010, MultiChunkTranslator.Translate(10, Return2010));
+assertEquals(15, MultiChunkTranslator.Translate(20));
+assertEquals(20, MultiChunkTranslator.Translate(25));
+assertEquals(34, MultiChunkTranslator.Translate(39));
+assertEquals(50, MultiChunkTranslator.Translate(40, Return2010));
+assertEquals(55, MultiChunkTranslator.Translate(45));
+assertEquals(69, MultiChunkTranslator.Translate(59));
+assertEquals(2010, MultiChunkTranslator.Translate(60, Return2010));
+assertEquals(70, MultiChunkTranslator.Translate(70));
+assertEquals(75, MultiChunkTranslator.Translate(75));
+
+
=======================================
--- /branches/bleeding_edge/src/codegen.cc      Tue Apr 27 02:09:51 2010
+++ /branches/bleeding_edge/src/codegen.cc      Wed Apr 28 04:38:43 2010
@@ -31,7 +31,6 @@
 #include "codegen-inl.h"
 #include "compiler.h"
 #include "debug.h"
-#include "liveedit.h"
 #include "oprofile-agent.h"
 #include "prettyprinter.h"
 #include "register-allocator-inl.h"
@@ -204,7 +203,6 @@
 // all the pieces into a Code object. This function is only to be called by
 // the compiler.cc code.
 Handle<Code> CodeGenerator::MakeCode(CompilationInfo* info) {
-  LiveEditFunctionTracker live_edit_tracker(info->function());
   Handle<Script> script = info->script();
   if (!script->IsUndefined() && !script->source()->IsUndefined()) {
     int len = String::cast(script->source())->length();
@@ -216,7 +214,6 @@
   MacroAssembler masm(NULL, kInitialBufferSize);
   CodeGenerator cgen(&masm);
   CodeGeneratorScope scope(&cgen);
-  live_edit_tracker.RecordFunctionScope(info->function()->scope());
   cgen.Generate(info);
   if (cgen.HasStackOverflow()) {
     ASSERT(!Top::has_pending_exception());
@@ -225,9 +222,7 @@

   InLoopFlag in_loop = (cgen.loop_nesting() != 0) ? IN_LOOP : NOT_IN_LOOP;
   Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop);
-  Handle<Code> result = MakeCodeEpilogue(cgen.masm(), flags, info);
-  live_edit_tracker.RecordFunctionCode(result);
-  return result;
+  return MakeCodeEpilogue(cgen.masm(), flags, info);
 }


=======================================
--- /branches/bleeding_edge/src/compiler.cc     Mon Apr 19 05:39:07 2010
+++ /branches/bleeding_edge/src/compiler.cc     Wed Apr 28 04:38:43 2010
@@ -192,6 +192,8 @@
   FunctionLiteral* lit =
       MakeAST(is_global, script, extension, pre_data, is_json);

+  LiveEditFunctionTracker live_edit_tracker(lit);
+
   // Check for parse errors.
   if (lit == NULL) {
     ASSERT(Top::has_pending_exception());
@@ -253,6 +255,8 @@
   Debugger::OnAfterCompile(script, Debugger::NO_AFTER_COMPILE_FLAGS);
 #endif

+  live_edit_tracker.RecordFunctionInfo(result, lit);
+
   return result;
 }

@@ -448,6 +452,7 @@
Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(FunctionLiteral* literal, Handle<Script> script, AstVisitor* caller) {
+  LiveEditFunctionTracker live_edit_tracker(literal);
 #ifdef DEBUG
   // We should not try to compile the same function literal more than
   // once.
@@ -552,6 +557,7 @@
   // the resulting function.
   SetExpectedNofPropertiesFromEstimate(result,
                                        literal->expected_property_count());
+  live_edit_tracker.RecordFunctionInfo(result, literal);
   return result;
 }

=======================================
--- /branches/bleeding_edge/src/debug-debugger.js       Wed Apr 14 07:53:00 2010
+++ /branches/bleeding_edge/src/debug-debugger.js       Wed Apr 28 04:38:43 2010
@@ -124,12 +124,6 @@
 };


-BreakPoint.prototype.updateSourcePosition = function(new_position, script) {
-  this.source_position_ = new_position;
-  // TODO(635): also update line and column.
-};
-
-
 BreakPoint.prototype.hit_count = function() {
   return this.hit_count_;
 };
@@ -243,6 +237,21 @@
   this.condition_ = null;
   this.ignoreCount_ = 0;
 }
+
+
+//Creates a clone of script breakpoint that is linked to another script.
+ScriptBreakPoint.prototype.cloneForOtherScript = function (other_script) {
+  var copy = new ScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
+      other_script.id, this.line_, this.column_, this.groupId_);
+  copy.number_ = next_break_point_number++;
+  script_break_points.push(copy);
+
+  copy.hit_count_ = this.hit_count_;
+  copy.active_ = this.active_;
+  copy.condition_ = this.condition_;
+  copy.ignoreCount_ = this.ignoreCount_;
+  return copy;
+}


 ScriptBreakPoint.prototype.number = function() {
@@ -280,6 +289,13 @@
 };


+ScriptBreakPoint.prototype.update_positions = function(line, column) {
+  this.line_ = line;
+  this.column_ = column;
+}
+
+
+
 ScriptBreakPoint.prototype.hit_count = function() {
   return this.hit_count_;
 };
@@ -404,6 +420,17 @@
     }
   }
 }
+
+
+function GetScriptBreakPoints(script) {
+  var result = [];
+  for (var i = 0; i < script_break_points.length; i++) {
+    if (script_break_points[i].matchesScript(script)) {
+      result.push(script_break_points[i]);
+    }
+  }
+  return result;
+}


 Debug.setListener = function(listener, opt_data) {
=======================================
--- /branches/bleeding_edge/src/full-codegen.cc Mon Mar 22 23:04:44 2010
+++ /branches/bleeding_edge/src/full-codegen.cc Wed Apr 28 04:38:43 2010
@@ -450,7 +450,6 @@
   CodeGenerator::MakeCodePrologue(info);
   const int kInitialBufferSize = 4 * KB;
   MacroAssembler masm(NULL, kInitialBufferSize);
-  LiveEditFunctionTracker live_edit_tracker(info->function());

   FullCodeGenerator cgen(&masm);
   cgen.Generate(info, PRIMARY);
@@ -459,9 +458,7 @@
     return Handle<Code>::null();
   }
   Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP);
- Handle<Code> result = CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
-  live_edit_tracker.RecordFunctionCode(result);
-  return result;
+  return CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
 }


=======================================
--- /branches/bleeding_edge/src/liveedit-debugger.js Wed Apr 21 09:59:58 2010 +++ /branches/bleeding_edge/src/liveedit-debugger.js Wed Apr 28 04:38:43 2010
@@ -83,7 +83,7 @@
     //
     // The script is used for compilation, because it produces code that
// needs to be linked with some particular script (for nested functions).
-    function DebugGatherCompileInfo(source) {
+    function GatherCompileInfo(source) {
       // Get function info, elements are partially sorted (it is a tree of
// nested functions serialized as parent followed by serialized children.
       var raw_compile_info = %LiveEditGatherCompileInfo(script, source);
@@ -92,8 +92,14 @@
       var compile_info = new Array();
       var old_index_map = new Array();
       for (var i = 0; i < raw_compile_info.length; i++) {
-          compile_info.push(new FunctionCompileInfo(raw_compile_info[i]));
-          old_index_map.push(i);
+        var info = new FunctionCompileInfo(raw_compile_info[i]);
+        // Remove all links to the actual script. Breakpoints system and
+ // LiveEdit itself believe that any function in heap that points to a
+        // particular script is a regular function.
+        // For some functions we will restore this link later.
+        %LiveEditFunctionSetScript(info.shared_function_info, void 0);
+        compile_info.push(info);
+        old_index_map.push(i);
       }

       for (var i = 0; i < compile_info.length; i++) {
@@ -148,6 +154,8 @@
     var shared_infos;
     // Finds SharedFunctionInfo that corresponds compile info with index
     // in old version of the script.
+    // TODO(LiveEdit): Redesign this part. Find live SharedFunctionInfo
+    //     about the time that FindCorrespondingFunctions is being run.
     function FindFunctionInfo(index) {
       var old_info = old_compile_info[index];
       for (var i = 0; i < shared_infos.length; i++) {
@@ -160,15 +168,36 @@
     }

     // Replaces function's Code.
-    function PatchCode(new_info, shared_info) {
+    function PatchCode(old_node) {
+      var new_info = old_node.corresponding_node.info;
+      var shared_info = FindFunctionInfo(old_node.array_index);
       if (shared_info) {
%LiveEditReplaceFunctionCode(new_info.raw_array, shared_info.raw_array);
+
+ // The function got a new code. However, this new code brings all new
+        // instances of SharedFunctionInfo for nested functions. However,
+        // we want the original instances to be used wherever possible.
+        // (This is because old instances and new instances will be both
+        // linked to a script and breakpoints subsystem does not really
+        // expects this; neither does LiveEdit subsystem on next call).
+        for (var i = 0; i < old_node.children.length; i++) {
+          if (old_node.children[i].corresponding_node) {
+ var corresponding_child = old_node.children[i].corresponding_node;
+            var child_shared_info =
+                FindFunctionInfo(old_node.children[i].array_index);
+            if (child_shared_info) {
+              %LiveEditReplaceRefToNestedFunction(shared_info.info,
+                  corresponding_child.info.shared_function_info,
+                  child_shared_info.info);
+            }
+          }
+        }
+
         change_log.push( {function_patched: new_info.function_name} );
       } else {
         change_log.push( {function_patched: new_info.function_name,
             function_info_not_found: true} );
       }
-
     }


@@ -180,15 +209,8 @@
             { name: old_info.function_name, info_not_found: true } );
         return;
       }
-      var breakpoint_position_update = %LiveEditPatchFunctionPositions(
+      %LiveEditPatchFunctionPositions(
           shared_info.raw_array, diff_array);
-      for (var i = 0; i < breakpoint_position_update.length; i += 2) {
-        var new_pos = breakpoint_position_update[i];
-        var break_point_object = breakpoint_position_update[i + 1];
-        change_log.push( { breakpoint_position_update:
- { from: break_point_object.source_position(), to: new_pos } } );
-        break_point_object.updateSourcePosition(new_pos, script);
-      }
       position_patch_report.push( { name: old_info.function_name } );
     }

@@ -199,7 +221,7 @@
     // may access its own text.
     function LinkToOldScript(shared_info, old_info_node) {
       if (shared_info) {
-        %LiveEditRelinkFunctionToScript(shared_info.raw_array, old_script);
+        %LiveEditFunctionSetScript(shared_info.info, old_script);
link_to_old_script_report.push( { name: shared_info.function_name } );
       } else {
         link_to_old_script_report.push(
@@ -220,12 +242,12 @@
     }

     // Gather compile information about old version of script.
-    var old_compile_info = DebugGatherCompileInfo(old_source);
+    var old_compile_info = GatherCompileInfo(old_source);

     // Gather compile information about new version of script.
     var new_compile_info;
     try {
-      new_compile_info = DebugGatherCompileInfo(new_source);
+      new_compile_info = GatherCompileInfo(new_source);
     } catch (e) {
       throw new Failure("Failed to compile new version of script: " + e);
     }
@@ -243,6 +265,7 @@
     // Prepare to-do lists.
     var replace_code_list = new Array();
     var link_to_old_script_list = new Array();
+    var link_to_original_script_list = new Array();
     var update_positions_list = new Array();

     function HarvestTodo(old_node) {
@@ -252,6 +275,15 @@
           CollectDamaged(node.children[i]);
         }
       }
+
+ // Recursively collects all newly compiled functions that are going into
+      // business and should be have link to the actual script updated.
+      function CollectNew(node_list) {
+        for (var i = 0; i < node_list.length; i++) {
+          link_to_original_script_list.push(node_list[i]);
+          CollectNew(node_list[i].children);
+        }
+      }

       if (old_node.status == FunctionStatus.DAMAGED) {
         CollectDamaged(old_node);
@@ -263,6 +295,7 @@
         update_positions_list.push(old_node);
       } else if (old_node.status == FunctionStatus.CHANGED) {
         replace_code_list.push(old_node);
+        CollectNew(old_node.unmatched_new_nodes);
       }
       for (var i = 0; i < old_node.children.length; i++) {
         HarvestTodo(old_node.children[i]);
@@ -286,14 +319,24 @@

     // We haven't changed anything before this line yet.
     // Committing all changes.
-
-    // Create old script if there are function linked to old version.
-    if (link_to_old_script_list.length > 0) {
+
+    // Start with breakpoints. Convert their line/column positions and
+    // temporary remove.
+ var break_points_restorer = TemporaryRemoveBreakPoints(script, change_log);
+
+    var old_script;
+
+ // Create an old script only if there are function that should be linked
+    // to old version.
+    if (link_to_old_script_list.length == 0) {
+      %LiveEditReplaceScript(script, new_source, null);
+      old_script = void 0;
+    } else {
       var old_script_name = CreateNameForOldScript(script);

       // Update the script text and create a new script representing an old
       // version of the script.
-      var old_script = %LiveEditReplaceScript(script, new_source,
+      old_script = %LiveEditReplaceScript(script, new_source,
           old_script_name);

       var link_to_old_script_report = new Array();
@@ -307,10 +350,14 @@
       }
     }

+    // Link to an actual script all the functions that we are going to use.
+    for (var i = 0; i < link_to_original_script_list.length; i++) {
+      %LiveEditFunctionSetScript(
+ link_to_original_script_list[i].info.shared_function_info, script);
+    }

     for (var i = 0; i < replace_code_list.length; i++) {
-      PatchCode(replace_code_list[i].corresponding_node.info,
-          FindFunctionInfo(replace_code_list[i].array_index));
+      PatchCode(replace_code_list[i]);
     }

     var position_patch_report = new Array();
@@ -322,9 +369,83 @@
       PatchPositions(update_positions_list[i].info,
           FindFunctionInfo(update_positions_list[i].array_index));
     }
+
+    break_points_restorer(pos_translator, old_script);
   }
   // Function is public.
   this.ApplyPatchMultiChunk = ApplyPatchMultiChunk;
+
+
+  // Returns function that restores breakpoints.
+  function TemporaryRemoveBreakPoints(original_script, change_log) {
+    var script_break_points = GetScriptBreakPoints(original_script);
+
+    var break_points_update_report = [];
+    change_log.push( { break_points_update: break_points_update_report } );
+
+    var break_point_old_positions = [];
+    for (var i = 0; i < script_break_points.length; i++) {
+      var break_point = script_break_points[i];
+
+      break_point.clear();
+
+      // TODO(LiveEdit): be careful with resource offset here.
+ var break_point_position = Debug.findScriptSourcePosition(original_script,
+          break_point.line(), break_point.column());
+
+      var old_position_description = {
+          position: break_point_position,
+          line: break_point.line(),
+          column: break_point.column()
+      }
+      break_point_old_positions.push(old_position_description);
+    }
+
+
+    // Restores breakpoints and creates their copies in the "old" copy of
+    // the script.
+    return function (pos_translator, old_script_copy_opt) {
+ // Update breakpoints (change positions and restore them in old version
+      // of script.
+      for (var i = 0; i < script_break_points.length; i++) {
+        var break_point = script_break_points[i];
+        if (old_script_copy_opt) {
+          var clone = break_point.cloneForOtherScript(old_script_copy_opt);
+          clone.set(old_script_copy_opt);
+
+          break_points_update_report.push( {
+            type: "copied_to_old",
+            id: break_point.number(),
+            new_id: clone.number(),
+            positions: break_point_old_positions[i]
+            } );
+        }
+
+        var updated_position = pos_translator.Translate(
+            break_point_old_positions[i].position,
+            PosTranslator.ShiftWithTopInsideChunkHandler);
+
+        var new_location =
+            original_script.locationFromPosition(updated_position, false);
+
+ break_point.update_positions(new_location.line, new_location.column);
+
+        var new_position_description = {
+            position: updated_position,
+            line: new_location.line,
+            column: new_location.column
+        }
+
+        break_point.set(original_script);
+
+        break_points_update_report.push( { type: "position_changed",
+          id: break_point.number(),
+          old_positions: break_point_old_positions[i],
+          new_positions: new_position_description
+          } );
+      }
+    }
+  }


   function Assert(condition, message) {
@@ -346,15 +467,15 @@

   function PosTranslator(diff_array) {
     var chunks = new Array();
-    var pos1 = 0;
-    var pos2 = 0;
+    var current_diff = 0;
     for (var i = 0; i < diff_array.length; i += 3) {
-      pos2 += diff_array[i] - pos1 + pos2;
-      pos1 = diff_array[i];
-      chunks.push(new DiffChunk(pos1, pos2, diff_array[i + 1] - pos1,
-          diff_array[i + 2] - pos2));
-      pos1 = diff_array[i + 1];
-      pos2 = diff_array[i + 2];
+      var pos1_begin = diff_array[i];
+      var pos2_begin = pos1_begin + current_diff;
+      var pos1_end = diff_array[i + 1];
+      var pos2_end = diff_array[i + 2];
+ chunks.push(new DiffChunk(pos1_begin, pos2_begin, pos1_end - pos1_begin,
+          pos2_end - pos2_begin));
+      current_diff = pos2_end - pos1_end;
     }
     this.chunks = chunks;
   }
@@ -364,14 +485,14 @@

   PosTranslator.prototype.Translate = function(pos, inside_chunk_handler) {
     var array = this.chunks;
-    if (array.length == 0 || pos < array[0]) {
+    if (array.length == 0 || pos < array[0].pos1) {
       return pos;
     }
     var chunk_index1 = 0;
     var chunk_index2 = array.length - 1;

     while (chunk_index1 < chunk_index2) {
-      var middle_index = (chunk_index1 + chunk_index2) / 2;
+      var middle_index = Math.floor((chunk_index1 + chunk_index2) / 2);
       if (pos < array[middle_index + 1].pos1) {
         chunk_index2 = middle_index;
       } else {
@@ -380,17 +501,24 @@
     }
     var chunk = array[chunk_index1];
     if (pos >= chunk.pos1 + chunk.len1) {
-      return pos += chunk.pos2 + chunk.len2 - chunk.pos1 - chunk.len1;
+      return pos + chunk.pos2 + chunk.len2 - chunk.pos1 - chunk.len1;
     }

     if (!inside_chunk_handler) {
-      inside_chunk_handler = PosTranslator.default_inside_chunk_handler;
-    }
-    inside_chunk_handler(pos, chunk);
+      inside_chunk_handler = PosTranslator.DefaultInsideChunkHandler;
+    }
+    return inside_chunk_handler(pos, chunk);
   }

-  PosTranslator.default_inside_chunk_handler = function() {
-    Assert(false, "Cannot translate position in chaged area");
+  PosTranslator.DefaultInsideChunkHandler = function(pos, diff_chunk) {
+    Assert(false, "Cannot translate position in changed area");
+  }
+
+  PosTranslator.ShiftWithTopInsideChunkHandler =
+      function(pos, diff_chunk) {
+    // We carelessly do not check whether we stay inside the chunk after
+    // translation.
+    return pos - diff_chunk.pos1 + diff_chunk.pos2;
   }

   var FunctionStatus = {
@@ -412,15 +540,16 @@
     this.children = children;
     // an index in array of compile_info
     this.array_index = array_index;
-    this.parent = void(0);
+    this.parent = void 0;

     this.status = FunctionStatus.UNCHANGED;
     // Status explanation is used for debugging purposes and will be shown
     // in user UI if some explanations are needed.
-    this.status_explanation = void(0);
-    this.new_start_pos = void(0);
-    this.new_end_pos = void(0);
-    this.corresponding_node = void(0);
+    this.status_explanation = void 0;
+    this.new_start_pos = void 0;
+    this.new_end_pos = void 0;
+    this.corresponding_node = void 0;
+    this.unmatched_new_nodes = void 0;
   }

   // From array of function infos that is implicitly a tree creates
@@ -564,6 +693,8 @@
     function ProcessChildren(old_node, new_node) {
       var old_children = old_node.children;
       var new_children = new_node.children;
+
+      var unmatched_new_nodes_list = [];

       var old_index = 0;
       var new_index = 0;
@@ -573,6 +704,7 @@
         } else if (new_index < new_children.length) {
           if (new_children[new_index].info.start_position <
               old_children[old_index].new_start_pos) {
+            unmatched_new_nodes_list.push(new_children[new_index]);
             new_index++;
           } else if (new_children[new_index].info.start_position ==
               old_children[old_index].new_start_pos) {
@@ -584,6 +716,9 @@
                 ProcessChildren(old_children[old_index],
                     new_children[new_index]);
if (old_children[old_index].status == FunctionStatus.DAMAGED) {
+                  unmatched_new_nodes_list.push(
+                      old_children[old_index].corresponding_node);
+                  old_children[old_index].corresponding_node = void 0;
                   old_node.status = FunctionStatus.CHANGED;
                 }
               }
@@ -592,6 +727,7 @@
               old_children[old_index].status_explanation =
                   "No corresponding function in new script found";
               old_node.status = FunctionStatus.CHANGED;
+              unmatched_new_nodes_list.push(new_children[new_index]);
             }
             new_index++;
             old_index++;
@@ -610,6 +746,11 @@
           old_index++;
         }
       }
+
+      while (new_index < new_children.length) {
+        unmatched_new_nodes_list.push(new_children[new_index]);
+        new_index++;
+      }

       if (old_node.status == FunctionStatus.CHANGED) {
         if (!CompareFunctionExpectations(old_node.info, new_node.info)) {
@@ -617,6 +758,7 @@
           old_node.status_explanation = "Changed code expectations";
         }
       }
+      old_node.unmatched_new_nodes = unmatched_new_nodes_list;
     }

     ProcessChildren(old_code_tree, new_code_tree);
@@ -637,6 +779,7 @@
     this.code = raw_array[4];
     this.scope_info = raw_array[5];
     this.outer_index = raw_array[6];
+    this.shared_function_info = raw_array[7];
     this.next_sibling_index = null;
     this.raw_array = raw_array;
   }
@@ -776,71 +919,10 @@
   function CompareStringsLinewise(s1, s2) {
     return %LiveEditCompareStringsLinewise(s1, s2);
   }
-  // Function is public (for tests).
-  this.CompareStringsLinewise = CompareStringsLinewise;
-
-
-  // Finds a difference between 2 strings in form of a single chunk.
-  // This is a temporary solution. We should calculate a read diff instead.
-  function FindSimpleDiff(old_source, new_source) {
-    var change_pos;
-    var old_len;
-    var new_len;
-
- // A find range block. Whenever control leaves it, it should set 3 local
-    // variables declared above.
-    find_range:
-    {
-      // First look from the beginning of strings.
-      var pos1;
-      {
-        var next_pos;
-        for (pos1 = 0; true; pos1 = next_pos) {
-          if (pos1 >= old_source.length) {
-            change_pos = pos1;
-            old_len = 0;
-            new_len = new_source.length - pos1;
-            break find_range;
-          }
-          if (pos1 >= new_source.length) {
-            change_pos = pos1;
-            old_len = old_source.length - pos1;
-            new_len = 0;
-            break find_range;
-          }
-          if (old_source[pos1] != new_source[pos1]) {
-            break;
-          }
-          next_pos = pos1 + 1;
-        }
-      }
-      // Now compare strings from the ends.
-      change_pos = pos1;
-      var pos_old;
-      var pos_new;
-      {
- for (pos_old = old_source.length - 1, pos_new = new_source.length - 1;
-            true;
-            pos_old--, pos_new--) {
- if (pos_old - change_pos + 1 < 0 || pos_new - change_pos + 1 < 0) {
-            old_len = pos_old - change_pos + 2;
-            new_len = pos_new - change_pos + 2;
-            break find_range;
-          }
-          if (old_source[pos_old] != new_source[pos_new]) {
-            old_len = pos_old - change_pos + 1;
-            new_len = pos_new - change_pos + 1;
-            break find_range;
-          }
-        }
-      }
-    }
-
-    if (old_len == 0 && new_len == 0) {
-      // no change
-      return;
-    }
-
- return { "change_pos": change_pos, "old_len": old_len, "new_len": new_len };
+
+  // Functions are public for tests.
+  this.TestApi = {
+    PosTranslator: PosTranslator,
+    CompareStringsLinewise: CompareStringsLinewise
   }
 }
=======================================
--- /branches/bleeding_edge/src/liveedit.cc     Tue Apr 27 14:20:02 2010
+++ /branches/bleeding_edge/src/liveedit.cc     Wed Apr 28 04:38:43 2010
@@ -417,6 +417,8 @@

   // Compile the code.
   CompilationInfo info(lit, script, is_eval);
+
+  LiveEditFunctionTracker tracker(lit);
   Handle<Code> code = MakeCodeForLiveEdit(&info);

   // Check for stack-overflow exceptions.
@@ -424,6 +426,7 @@
     Top::StackOverflow();
     return;
   }
+  tracker.RecordRootFunctionInfo(code);
 }

 // Unwraps JSValue object, returning its field "value"
@@ -501,9 +504,13 @@
     Handle<JSValue> wrapper = WrapInJSValue(*function_code);
     this->SetField(kCodeOffset_, wrapper);
   }
-  void SetScopeInfo(Handle<JSArray> scope_info_array) {
+  void SetScopeInfo(Handle<Object> scope_info_array) {
     this->SetField(kScopeInfoOffset_, scope_info_array);
   }
+  void SetSharedFunctionInfo(Handle<SharedFunctionInfo> info) {
+    Handle<JSValue> info_holder = WrapInJSValue(*info);
+    this->SetField(kSharedFunctionInfoOffset_, info_holder);
+  }
   int GetParentIndex() {
     return this->GetSmiValueField(kParentIndexOffset_);
   }
@@ -527,7 +534,8 @@
   static const int kCodeOffset_ = 4;
   static const int kScopeInfoOffset_ = 5;
   static const int kParentIndexOffset_ = 6;
-  static const int kSize_ = 7;
+  static const int kSharedFunctionInfoOffset_ = 7;
+  static const int kSize_ = 8;

   friend class JSArrayBasedStruct<FunctionInfoWrapper>;
 };
@@ -593,7 +601,11 @@
     current_parent_index_ = info.GetParentIndex();
   }

-  void FunctionScope(Scope* scope) {
+// TODO(LiveEdit): Move private method below.
+//     This private section was created here to avoid moving the function
+//      to keep already complex diff simpler.
+ private:
+  Object* SerializeFunctionScope(Scope* scope) {
     HandleScope handle_scope;

     Handle<JSArray> scope_info_list = Factory::NewJSArray(10);
@@ -604,7 +616,7 @@
     // scopes of this chain.
     Scope* outer_scope = scope->outer_scope();
     if (outer_scope == NULL) {
-      return;
+      return Heap::undefined_value();
     }
     do {
       ZoneList<Variable*> list(10);
@@ -645,16 +657,32 @@
       outer_scope = outer_scope->outer_scope();
     } while (outer_scope != NULL);

-    FunctionInfoWrapper info =
- FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
-    info.SetScopeInfo(scope_info_list);
+    return *scope_info_list;
   }

+ public:
+  // Saves only function code, because for a script function we
+  // may never create a SharedFunctionInfo object.
   void FunctionCode(Handle<Code> function_code) {
     FunctionInfoWrapper info =
FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
     info.SetFunctionCode(function_code);
   }
+
+  // Saves full information about a function: its code, its scope info
+  // and a SharedFunctionInfo object.
+  void FunctionInfo(Handle<SharedFunctionInfo> shared, Scope* scope) {
+    if (!shared->IsSharedFunctionInfo()) {
+      return;
+    }
+    FunctionInfoWrapper info =
+ FunctionInfoWrapper::cast(result_->GetElement(current_parent_index_));
+    info.SetFunctionCode(Handle<Code>(shared->code()));
+    info.SetSharedFunctionInfo(shared);
+
+    Handle<Object> scope_info_list(SerializeFunctionScope(scope));
+    info.SetScopeInfo(scope_info_list);
+  }

   Handle<JSArray> GetResult() {
     return result_;
@@ -815,7 +843,6 @@

   Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();

-
   if (IsJSFunctionCode(shared_info->code())) {
     ReplaceCodeObject(shared_info->code(),
                       *(compile_info_wrapper.GetFunctionCode()));
@@ -839,11 +866,10 @@

 // TODO(635): Eval caches its scripts (same text -- same compiled info).
 // Make sure we clear such caches.
-void LiveEdit::RelinkFunctionToScript(Handle<JSArray> shared_info_array,
-                                      Handle<Script> script_handle) {
-  SharedInfoWrapper shared_info_wrapper(shared_info_array);
-  Handle<SharedFunctionInfo> shared_info = shared_info_wrapper.GetInfo();
-
+void LiveEdit::SetFunctionScript(Handle<JSValue> function_wrapper,
+                                 Handle<Object> script_handle) {
+  Handle<SharedFunctionInfo> shared_info =
+      Handle<SharedFunctionInfo>::cast(UnwrapJSValue(function_wrapper));
   shared_info->set_script(*script_handle);
 }

@@ -998,20 +1024,7 @@
 }


-static Handle<Object> GetBreakPointObjectsForJS(
-    Handle<BreakPointInfo> break_point_info) {
-  if (break_point_info->break_point_objects()->IsFixedArray()) {
-    Handle<FixedArray> fixed_array(
-        FixedArray::cast(break_point_info->break_point_objects()));
-    Handle<Object> array = Factory::NewJSArrayWithElements(fixed_array);
-    return array;
-  } else {
-    return Handle<Object>(break_point_info->break_point_objects());
-  }
-}
-
-
-Handle<JSArray> LiveEdit::PatchFunctionPositions(
+void LiveEdit::PatchFunctionPositions(
Handle<JSArray> shared_info_array, Handle<JSArray> position_change_array) {
   SharedInfoWrapper shared_info_wrapper(shared_info_array);
   Handle<SharedFunctionInfo> info = shared_info_wrapper.GetInfo();
@@ -1040,45 +1053,71 @@
       ReplaceCodeObject(info->code(), *patched_code);
     }
   }
+}


-  Handle<JSArray> result = Factory::NewJSArray(0);
-  int result_len = 0;
-
-  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 old_in_script_position = info->source_position()->value() +
-          old_function_start;
- int new_in_script_position = TranslatePosition(old_in_script_position,
-          position_change_array);
-      info->set_source_position(
-          Smi::FromInt(new_in_script_position - new_function_start));
-      if (old_in_script_position != new_in_script_position) {
-        SetElement(result, result_len,
-                   Handle<Smi>(Smi::FromInt(new_in_script_position)));
-        SetElement(result, result_len + 1,
-                   GetBreakPointObjectsForJS(info));
-        result_len += 2;
+static Handle<Script> CreateScriptCopy(Handle<Script> original) {
+  Handle<String> original_source(String::cast(original->source()));
+
+  Handle<Script> copy = Factory::NewScript(original_source);
+
+  copy->set_name(original->name());
+  copy->set_line_offset(original->line_offset());
+  copy->set_column_offset(original->column_offset());
+  copy->set_data(original->data());
+  copy->set_type(original->type());
+  copy->set_context_data(original->context_data());
+  copy->set_compilation_type(original->compilation_type());
+  copy->set_eval_from_shared(original->eval_from_shared());
+  copy->set_eval_from_instructions_offset(
+      original->eval_from_instructions_offset());
+
+  return copy;
+}
+
+
+Object* LiveEdit::ChangeScriptSource(Handle<Script> original_script,
+                                     Handle<String> new_source,
+                                     Handle<Object> old_script_name) {
+  Handle<Object> old_script_object;
+  if (old_script_name->IsString()) {
+    Handle<Script> old_script = CreateScriptCopy(original_script);
+    old_script->set_name(String::cast(*old_script_name));
+    old_script_object = old_script;
+    Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
+  } else {
+    old_script_object = Handle<Object>(Heap::null_value());
+  }
+
+  original_script->set_source(*new_source);
+
+  // Drop line ends so that they will be recalculated.
+  original_script->set_line_ends(Heap::undefined_value());
+
+  return *old_script_object;
+}
+
+
+
+void LiveEdit::ReplaceRefToNestedFunction(
+    Handle<JSValue> parent_function_wrapper,
+    Handle<JSValue> orig_function_wrapper,
+    Handle<JSValue> subst_function_wrapper) {
+
+  Handle<SharedFunctionInfo> parent_shared =
+ Handle<SharedFunctionInfo>::cast(UnwrapJSValue(parent_function_wrapper));
+  Handle<SharedFunctionInfo> orig_shared =
+ Handle<SharedFunctionInfo>::cast(UnwrapJSValue(orig_function_wrapper));
+  Handle<SharedFunctionInfo> subst_shared =
+ Handle<SharedFunctionInfo>::cast(UnwrapJSValue(subst_function_wrapper));
+
+  for (RelocIterator it(parent_shared->code()); !it.done(); it.next()) {
+    if (it.rinfo()->rmode() == RelocInfo::EMBEDDED_OBJECT) {
+      if (it.rinfo()->target_object() == *orig_shared) {
+        it.rinfo()->set_target_object(*subst_shared);
       }
     }
   }
-  return result;
 }


@@ -1362,17 +1401,16 @@
 }


-void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) {
+void LiveEditFunctionTracker::RecordFunctionInfo(
+    Handle<SharedFunctionInfo> info, FunctionLiteral* lit) {
   if (active_function_info_listener != NULL) {
-    active_function_info_listener->FunctionCode(code);
+    active_function_info_listener->FunctionInfo(info, lit->scope());
   }
 }


-void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) {
-  if (active_function_info_listener != NULL) {
-    active_function_info_listener->FunctionScope(scope);
-  }
+void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
+  active_function_info_listener->FunctionCode(code);
 }


@@ -1393,11 +1431,12 @@
 }


-void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) {
+void LiveEditFunctionTracker::RecordFunctionInfo(
+    Handle<SharedFunctionInfo> info, FunctionLiteral* lit) {
 }


-void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) {
+void LiveEditFunctionTracker::RecordRootFunctionInfo(Handle<Code> code) {
 }


=======================================
--- /branches/bleeding_edge/src/liveedit.h      Tue Apr 27 14:20:02 2010
+++ /branches/bleeding_edge/src/liveedit.h      Wed Apr 28 04:38:43 2010
@@ -67,8 +67,9 @@
  public:
   explicit LiveEditFunctionTracker(FunctionLiteral* fun);
   ~LiveEditFunctionTracker();
-  void RecordFunctionCode(Handle<Code> code);
-  void RecordFunctionScope(Scope* scope);
+  void RecordFunctionInfo(Handle<SharedFunctionInfo> info,
+                          FunctionLiteral* lit);
+  void RecordRootFunctionInfo(Handle<Code> code);

   static bool IsActive();
 };
@@ -85,14 +86,26 @@
   static void ReplaceFunctionCode(Handle<JSArray> new_compile_info_array,
                                   Handle<JSArray> shared_info_array);

-  static void RelinkFunctionToScript(Handle<JSArray> shared_info_array,
-                                     Handle<Script> script_handle);
-
- // Returns an array of pairs (new source position, breakpoint_object/array)
-  // so that JS side could update positions in breakpoint objects.
-  static Handle<JSArray> PatchFunctionPositions(
+  // Updates script field in FunctionSharedInfo.
+  static void SetFunctionScript(Handle<JSValue> function_wrapper,
+                                Handle<Object> script_handle);
+
+  static void PatchFunctionPositions(
Handle<JSArray> shared_info_array, Handle<JSArray> position_change_array);

+  // For a script updates its source field. If old_script_name is provided
+ // (i.e. is a String), also creates a copy of the script with its original
+  // source and sends notification to debugger.
+  static Object* ChangeScriptSource(Handle<Script> original_script,
+                                    Handle<String> new_source,
+                                    Handle<Object> old_script_name);
+
+  // In a code of a parent function replaces original function as embedded
+  // object with a substitution one.
+ static void ReplaceRefToNestedFunction(Handle<JSValue> parent_function_shared, + Handle<JSValue> orig_function_shared, + Handle<JSValue> subst_function_shared);
+
   // Checks listed functions on stack and return array with corresponding
   // FunctionPatchabilityStatus statuses; extra array element may
   // contain general error message. Modifies the current stack and
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Tue Apr 27 04:45:08 2010
+++ /branches/bleeding_edge/src/runtime.cc      Wed Apr 28 04:38:43 2010
@@ -9674,38 +9674,30 @@
   return result;
 }

-// Changes the source of the script to a new_source and creates a new
-// script representing the old version of the script source.
+// Changes the source of the script to a new_source.
+// If old_script_name is provided (i.e. is a String), also creates a copy of
+// the script with its original source and sends notification to debugger.
 static Object* Runtime_LiveEditReplaceScript(Arguments args) {
   ASSERT(args.length() == 3);
   HandleScope scope;
   CONVERT_CHECKED(JSValue, original_script_value, args[0]);
   CONVERT_ARG_CHECKED(String, new_source, 1);
-  CONVERT_ARG_CHECKED(String, old_script_name, 2);
-  Handle<Script> original_script =
-      Handle<Script>(Script::cast(original_script_value->value()));
-
-  Handle<String> original_source(String::cast(original_script->source()));
-
-  original_script->set_source(*new_source);
-  Handle<Script> old_script = Factory::NewScript(original_source);
-  old_script->set_name(*old_script_name);
-  old_script->set_line_offset(original_script->line_offset());
-  old_script->set_column_offset(original_script->column_offset());
-  old_script->set_data(original_script->data());
-  old_script->set_type(original_script->type());
-  old_script->set_context_data(original_script->context_data());
-  old_script->set_compilation_type(original_script->compilation_type());
-  old_script->set_eval_from_shared(original_script->eval_from_shared());
-  old_script->set_eval_from_instructions_offset(
-      original_script->eval_from_instructions_offset());
-
-  // Drop line ends so that they will be recalculated.
-  original_script->set_line_ends(Heap::undefined_value());
-
-  Debugger::OnAfterCompile(old_script, Debugger::SEND_WHEN_DEBUGGING);
-
-  return *(GetScriptWrapper(old_script));
+  Handle<Object> old_script_name(args[2]);
+
+  CONVERT_CHECKED(Script, original_script_pointer,
+                  original_script_value->value());
+  Handle<Script> original_script(original_script_pointer);
+
+  Object* old_script = LiveEdit::ChangeScriptSource(original_script,
+                                                    new_source,
+                                                    old_script_name);
+
+  if (old_script->IsScript()) {
+    Handle<Script> script_handle(Script::cast(old_script));
+    return *(GetScriptWrapper(script_handle));
+  } else {
+    return Heap::null_value();
+  }
 }

 // Replaces code of SharedFunctionInfo with a new one.
@@ -9721,35 +9713,60 @@
 }

 // Connects SharedFunctionInfo to another script.
-static Object* Runtime_LiveEditRelinkFunctionToScript(Arguments args) {
+static Object* Runtime_LiveEditFunctionSetScript(Arguments args) {
   ASSERT(args.length() == 2);
   HandleScope scope;
-  CONVERT_ARG_CHECKED(JSArray, shared_info_array, 0);
-  CONVERT_ARG_CHECKED(JSValue, script_value, 1);
- Handle<Script> script = Handle<Script>(Script::cast(script_value->value()));
-
-  LiveEdit::RelinkFunctionToScript(shared_info_array, script);
+  Handle<Object> function_object(args[0]);
+  Handle<Object> script_object(args[1]);
+
+  if (function_object->IsJSValue()) {
+ Handle<JSValue> function_wrapper = Handle<JSValue>::cast(function_object);
+    if (script_object->IsJSValue()) {
+ CONVERT_CHECKED(Script, script, JSValue::cast(*script_object)->value());
+      script_object = Handle<Object>(script);
+    }
+
+    LiveEdit::SetFunctionScript(function_wrapper, script_object);
+  } else {
+ // Just ignore this. We may not have a SharedFunctionInfo for some functions
+    // and we check it in this function.
+  }

   return Heap::undefined_value();
 }
+
+
+// In a code of a parent function replaces original function as embedded object
+// with a substitution one.
+static Object* Runtime_LiveEditReplaceRefToNestedFunction(Arguments args) {
+  ASSERT(args.length() == 3);
+  HandleScope scope;
+
+  CONVERT_ARG_CHECKED(JSValue, parent_wrapper, 0);
+  CONVERT_ARG_CHECKED(JSValue, orig_wrapper, 1);
+  CONVERT_ARG_CHECKED(JSValue, subst_wrapper, 2);
+
+  LiveEdit::ReplaceRefToNestedFunction(parent_wrapper, orig_wrapper,
+                                       subst_wrapper);
+
+  return Heap::undefined_value();
+}
+

 // Updates positions of a shared function info (first parameter) according
 // to script source change. Text change is described in second parameter as
 // array of groups of 3 numbers:
 // (change_begin, change_end, change_end_new_position).
// Each group describes a change in text; groups are sorted by change_begin.
-// Returns an array of pairs (new source position, breakpoint_object/array)
-// so that JS side could update positions in breakpoint objects.
 static Object* Runtime_LiveEditPatchFunctionPositions(Arguments args) {
   ASSERT(args.length() == 2);
   HandleScope scope;
   CONVERT_ARG_CHECKED(JSArray, shared_array, 0);
   CONVERT_ARG_CHECKED(JSArray, position_change_array, 1);

-  Handle<Object> result =
- LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
-
-  return *result;
+  LiveEdit::PatchFunctionPositions(shared_array, position_change_array);
+
+  return Heap::undefined_value();
 }


=======================================
--- /branches/bleeding_edge/src/runtime.h       Mon Apr 19 09:08:26 2010
+++ /branches/bleeding_edge/src/runtime.h       Wed Apr 28 04:38:43 2010
@@ -337,7 +337,8 @@
   F(LiveEditGatherCompileInfo, 2, 1) \
   F(LiveEditReplaceScript, 3, 1) \
   F(LiveEditReplaceFunctionCode, 2, 1) \
-  F(LiveEditRelinkFunctionToScript, 2, 1) \
+  F(LiveEditFunctionSetScript, 2, 1) \
+  F(LiveEditReplaceRefToNestedFunction, 3, 1) \
   F(LiveEditPatchFunctionPositions, 2, 1) \
   F(LiveEditCheckAndDropActivations, 2, 1) \
   F(LiveEditCompareStringsLinewise, 2, 1) \
=======================================
--- /branches/bleeding_edge/test/mjsunit/debug-liveedit-diff.js Mon Apr 19 09:08:26 2010 +++ /branches/bleeding_edge/test/mjsunit/debug-liveedit-diff.js Wed Apr 28 04:38:43 2010
@@ -31,7 +31,7 @@
 Debug = debug.Debug

 function CheckCompareOneWay(s1, s2) {
-  var diff_array = Debug.LiveEdit.CompareStringsLinewise(s1, s2);
+  var diff_array = Debug.LiveEdit.TestApi.CompareStringsLinewise(s1, s2);

   var pos1 = 0;
   var pos2 = 0;

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to