Revision: 5015
Author: [email protected]
Date: Fri Jul  2 13:46:04 2010
Log: Describe LiveEdit changes and support preview mode

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

Modified:
 /branches/bleeding_edge/src/debug-debugger.js
 /branches/bleeding_edge/src/liveedit-debugger.js
 /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-newsource.js

=======================================
--- /branches/bleeding_edge/src/debug-debugger.js       Mon Jun 28 05:09:29 2010
+++ /branches/bleeding_edge/src/debug-debugger.js       Fri Jul  2 13:46:04 2010
@@ -2070,6 +2070,7 @@
     return response.failed('Missing arguments');
   }
   var script_id = request.arguments.script_id;
+  var preview_only = !!request.arguments.preview_only;

   var scripts = %DebugGetLoadedScripts();

@@ -2092,18 +2093,9 @@

   var new_source = request.arguments.new_source;

-  try {
-    Debug.LiveEdit.SetScriptSource(the_script, new_source, change_log);
-  } catch (e) {
-    if (e instanceof Debug.LiveEdit.Failure) {
-      // Let's treat it as a "success" so that body with change_log will be
-      // sent back. "change_log" will have "failure" field set.
-      change_log.push( { failure: true, message: e.toString() } );
-    } else {
-      throw e;
-    }
-  }
-  response.body = {change_log: change_log};
+  var result_description = Debug.LiveEdit.SetScriptSource(the_script,
+      new_source, preview_only, change_log);
+  response.body = {change_log: change_log, result: result_description};
 };


=======================================
--- /branches/bleeding_edge/src/liveedit-debugger.js Wed Apr 28 06:29:07 2010 +++ /branches/bleeding_edge/src/liveedit-debugger.js Fri Jul 2 13:46:04 2010
@@ -51,7 +51,8 @@
   // Applies the change to the script.
   // The change is in form of list of chunks encoded in a single array as
   // a series of triplets (pos1_start, pos1_end, pos2_end)
- function ApplyPatchMultiChunk(script, diff_array, new_source, change_log) { + function ApplyPatchMultiChunk(script, diff_array, new_source, preview_only,
+      change_log) {

     var old_source = script.source;

@@ -96,7 +97,7 @@
       }

// Recursively collects all newly compiled functions that are going into
-      // business and should be have link to the actual script updated.
+      // business and should 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]);
@@ -121,6 +122,20 @@
       }
     }

+    var preview_description = {
+        change_tree: DescribeChangeTree(root_old_node),
+        textual_diff: {
+          old_len: old_source.length,
+          new_len: new_source.length,
+          chunks: diff_array
+        },
+        updated: false
+    };
+
+    if (preview_only) {
+      return preview_description;
+    }
+
     HarvestTodo(root_old_node);

     // Collect shared infos for functions whose code need to be patched.
@@ -132,13 +147,15 @@
       }
     }

-    // Check that function being patched is not currently on stack.
-    CheckStackActivations(replaced_function_infos, change_log);
-
-
     // We haven't changed anything before this line yet.
     // Committing all changes.

+ // Check that function being patched is not currently on stack or drop them.
+    var dropped_functions_number =
+        CheckStackActivations(replaced_function_infos, change_log);
+
+    preview_description.stack_modified = dropped_functions_number != 0;
+
     // Start with breakpoints. Convert their line/column positions and
     // temporary remove.
var break_points_restorer = TemporaryRemoveBreakPoints(script, change_log);
@@ -166,6 +183,8 @@
         LinkToOldScript(link_to_old_script_list[i], old_script,
             link_to_old_script_report);
       }
+
+      preview_description.created_script_name = old_script_name;
     }

     // Link to an actual script all the functions that we are going to use.
@@ -189,6 +208,9 @@
     }

     break_points_restorer(pos_translator, old_script);
+
+    preview_description.updated = true;
+    return preview_description;
   }
   // Function is public.
   this.ApplyPatchMultiChunk = ApplyPatchMultiChunk;
@@ -494,6 +516,16 @@
     this.new_end_pos = void 0;
     this.corresponding_node = void 0;
     this.unmatched_new_nodes = void 0;
+
+    // 'Textual' correspondence/matching is weaker than 'pure'
+ // correspondence/matching. We need 'textual' level for visual presentation
+    // in UI, we use 'pure' level for actual code manipulation.
+ // Sometimes only function body is changed (functions in old and new script
+    // textually correspond), but we cannot patch the code, so we see them
+    // as an old function deleted and new function created.
+    this.textual_corresponding_node = void 0;
+    this.textually_unmatched_new_nodes = void 0;
+
     this.live_shared_info_wrapper = void 0;
   }

@@ -640,6 +672,7 @@
       var new_children = new_node.children;

       var unmatched_new_nodes_list = [];
+      var textually_unmatched_new_nodes_list = [];

       var old_index = 0;
       var new_index = 0;
@@ -650,6 +683,7 @@
           if (new_children[new_index].info.start_position <
               old_children[old_index].new_start_pos) {
             unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_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) {
@@ -657,6 +691,8 @@
                 old_children[old_index].new_end_pos) {
               old_children[old_index].corresponding_node =
                   new_children[new_index];
+              old_children[old_index].textual_corresponding_node =
+                  new_children[new_index];
if (old_children[old_index].status != FunctionStatus.UNCHANGED) {
                 ProcessChildren(old_children[old_index],
                     new_children[new_index]);
@@ -673,6 +709,7 @@
                   "No corresponding function in new script found";
               old_node.status = FunctionStatus.CHANGED;
               unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_unmatched_new_nodes_list.push(new_children[new_index]);
             }
             new_index++;
             old_index++;
@@ -694,21 +731,28 @@

       while (new_index < new_children.length) {
         unmatched_new_nodes_list.push(new_children[new_index]);
+        textually_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)) {
+        var why_wrong_expectations =
+            WhyFunctionExpectationsDiffer(old_node.info, new_node.info);
+        if (why_wrong_expectations) {
           old_node.status = FunctionStatus.DAMAGED;
-          old_node.status_explanation = "Changed code expectations";
+          old_node.status_explanation = why_wrong_expectations;
         }
       }
       old_node.unmatched_new_nodes = unmatched_new_nodes_list;
+      old_node.textually_unmatched_new_nodes =
+          textually_unmatched_new_nodes_list;
     }

     ProcessChildren(old_code_tree, new_code_tree);

     old_code_tree.corresponding_node = new_code_tree;
+    old_code_tree.textual_corresponding_node = new_code_tree;
+
     Assert(old_code_tree.status != FunctionStatus.DAMAGED,
         "Script became damaged");
   }
@@ -792,27 +836,37 @@
   }

   // Compares a function interface old and new version, whether it
-  // changed or not.
-  function CompareFunctionExpectations(function_info1, function_info2) {
+  // changed or not. Returns explanation if they differ.
+  function WhyFunctionExpectationsDiffer(function_info1, function_info2) {
// Check that function has the same number of parameters (there may exist
     // an adapter, that won't survive function parameter number change).
     if (function_info1.param_num != function_info2.param_num) {
-      return false;
+      return "Changed parameter number: " + function_info1.param_num +
+          " and " + function_info2.param_num;
     }
     var scope_info1 = function_info1.scope_info;
     var scope_info2 = function_info2.scope_info;
-
-    if (!scope_info1) {
-      return !scope_info2;
-    }
-
-    if (scope_info1.length != scope_info2.length) {
-      return false;
-    }
-
- // Check that outer scope structure is not changed. Otherwise the function
-    // will not properly work with existing scopes.
-    return scope_info1.toString() == scope_info2.toString();
+
+    var scope_info1_text;
+    var scope_info2_text;
+
+    if (scope_info1) {
+      scope_info1_text = scope_info1.toString();
+    } else {
+      scope_info1_text = "";
+    }
+    if (scope_info2) {
+      scope_info2_text = scope_info2.toString();
+    } else {
+      scope_info2_text = "";
+    }
+
+    if (scope_info1_text != scope_info2_text) {
+      return "Incompatible variable maps: [" + scope_info1_text +
+          "] and [" + scope_info2_text + "]";
+    }
+    // No differences. Return undefined.
+    return;
   }

   // Minifier forward declaration.
@@ -856,6 +910,8 @@
       change_log.push( { functions_on_stack: problems } );
       throw new Failure("Blocked by functions on stack");
     }
+
+    return dropped.length;
   }

   // A copy of the FunctionPatchabilityStatus enum from liveedit.h
@@ -897,14 +953,11 @@
   this.GetPcFromSourcePos = GetPcFromSourcePos;

   // LiveEdit main entry point: changes a script text to a new string.
-  function SetScriptSource(script, new_source, change_log) {
+  function SetScriptSource(script, new_source, preview_only, change_log) {
     var old_source = script.source;
     var diff = CompareStringsLinewise(old_source, new_source);
-    if (diff.length == 0) {
-      change_log.push( { empty_diff: true } );
-      return;
-    }
-    ApplyPatchMultiChunk(script, diff, new_source, change_log);
+    return ApplyPatchMultiChunk(script, diff, new_source, preview_only,
+        change_log);
   }
   // Function is public.
   this.SetScriptSource = SetScriptSource;
@@ -931,7 +984,67 @@

     return ApplyPatchMultiChunk(script,
[ change_pos, change_pos + change_len, change_pos + new_str.length],
-        new_source, change_log);
+        new_source, false, change_log);
+  }
+
+  // Creates JSON description for a change tree.
+  function DescribeChangeTree(old_code_tree) {
+
+    function ProcessOldNode(node) {
+      var child_infos = [];
+      for (var i = 0; i < node.children.length; i++) {
+        var child = node.children[i];
+        if (child.status != FunctionStatus.UNCHANGED) {
+          child_infos.push(ProcessOldNode(child));
+        }
+      }
+      var new_child_infos = [];
+      if (node.textually_unmatched_new_nodes) {
+ for (var i = 0; i < node.textually_unmatched_new_nodes.length; i++) {
+          var child = node.textually_unmatched_new_nodes[i];
+          new_child_infos.push(ProcessNewNode(child));
+        }
+      }
+      var res = {
+        name: node.info.function_name,
+        positions: DescribePositions(node),
+        status: node.status,
+        children: child_infos,
+        new_children: new_child_infos
+      };
+      if (node.status_explanation) {
+        res.status_explanation = node.status_explanation;
+      }
+      if (node.textual_corresponding_node) {
+ res.new_positions = DescribePositions(node.textual_corresponding_node);
+      }
+      return res;
+    }
+
+    function ProcessNewNode(node) {
+      var child_infos = [];
+      // Do not list ancestors.
+      if (false) {
+        for (var i = 0; i < node.children.length; i++) {
+          child_infos.push(ProcessNewNode(node.children[i]));
+        }
+      }
+      var res = {
+        name: node.info.function_name,
+        positions: DescribePositions(node),
+        children: child_infos,
+      };
+      return res;
+    }
+
+    function DescribePositions(node) {
+      return {
+        start_position: node.info.start_position,
+        end_position: node.info.end_position
+      };
+    }
+
+    return ProcessOldNode(old_code_tree);
   }


=======================================
--- /branches/bleeding_edge/test/mjsunit/debug-liveedit-3.js Wed Apr 28 04:38:43 2010 +++ /branches/bleeding_edge/test/mjsunit/debug-liveedit-3.js Fri Jul 2 13:46:04 2010
@@ -57,7 +57,8 @@
 print("new source: " + new_source);

 var change_log = new Array();
-Debug.LiveEdit.SetScriptSource(script, new_source, change_log);
+var result = Debug.LiveEdit.SetScriptSource(script, new_source, false, change_log);
+print("Result: " + JSON.stringify(result) + "\n");
 print("Change log: " + JSON.stringify(change_log) + "\n");

 assertEquals(8, z6());
=======================================
--- /branches/bleeding_edge/test/mjsunit/debug-liveedit-breakpoints.js Wed Apr 28 04:38:43 2010 +++ /branches/bleeding_edge/test/mjsunit/debug-liveedit-breakpoints.js Fri Jul 2 13:46:04 2010
@@ -72,7 +72,8 @@
 print("new source: " + new_source);

 var change_log = new Array();
-Debug.LiveEdit.SetScriptSource(script, new_source, change_log);
+var result = Debug.LiveEdit.SetScriptSource(script, new_source, false, change_log);
+print("Result: " + JSON.stringify(result) + "\n");
 print("Change log: " + JSON.stringify(change_log) + "\n");

 var breaks = Debug.scriptBreakPoints();
=======================================
--- /branches/bleeding_edge/test/mjsunit/debug-liveedit-newsource.js Wed Apr 21 09:59:58 2010 +++ /branches/bleeding_edge/test/mjsunit/debug-liveedit-newsource.js Fri Jul 2 13:46:04 2010
@@ -57,7 +57,8 @@
 print("new source: " + new_source);

 var change_log = new Array();
-Debug.LiveEdit.SetScriptSource(script, new_source, change_log);
+var result = Debug.LiveEdit.SetScriptSource(script, new_source, false, change_log);
+print("Result: " + JSON.stringify(result) + "\n");
 print("Change log: " + JSON.stringify(change_log) + "\n");

 assertEquals("Capybara", ChooseAnimal());

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

Reply via email to