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