Allow debugger evaluate expressions to mute local variables
authorpeter.rybin@gmail.com <peter.rybin@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 25 Jun 2013 13:48:43 +0000 (13:48 +0000)
committerpeter.rybin@gmail.com <peter.rybin@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 25 Jun 2013 13:48:43 +0000 (13:48 +0000)
R=yangguo@chromium.org

Review URL: https://codereview.chromium.org/17636007

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15323 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/mirror-debugger.js
src/runtime.cc
test/mjsunit/debug-evaluate-locals.js

index a997fce..91097ea 100644 (file)
@@ -1699,13 +1699,30 @@ FrameMirror.prototype.stepInPositions = function() {
 
 FrameMirror.prototype.evaluate = function(source, disable_break,
                                           opt_context_object) {
-  var result = %DebugEvaluate(this.break_id_,
-                              this.details_.frameId(),
-                              this.details_.inlinedFrameIndex(),
-                              source,
-                              Boolean(disable_break),
-                              opt_context_object);
-  return MakeMirror(result);
+  var result_array = %DebugEvaluate(this.break_id_,
+                                    this.details_.frameId(),
+                                    this.details_.inlinedFrameIndex(),
+                                    source,
+                                    Boolean(disable_break),
+                                    opt_context_object);
+  // Silently ignore local variables changes if the frame is optimized.
+  if (!this.isOptimizedFrame()) {
+    var local_scope_before = result_array[1];
+    var local_scope_after = result_array[2];
+    for (var n in local_scope_after) {
+      var value_before = local_scope_before[n];
+      var value_after = local_scope_after[n];
+      if (value_before !== value_after) {
+        %SetScopeVariableValue(this.break_id_,
+                               this.details_.frameId(),
+                               this.details_.inlinedFrameIndex(),
+                               0,
+                               n,
+                               value_after);
+      }
+    }
+  }
+  return MakeMirror(result_array[0]);
 };
 
 
index e42a9db..8917c58 100644 (file)
@@ -12424,6 +12424,13 @@ static MaybeObject* DebugEvaluate(Isolate* isolate,
 // the same view of the values of parameters and local variables as if the
 // piece of JavaScript was evaluated at the point where the function on the
 // stack frame is currently stopped when we compile and run the (direct) eval.
+// Returns array of
+// #0: evaluate result
+// #1: local variables scope materizalized as object before evaluation
+// #2: local variables scope materizalized as object after evaluation
+// Since user expression only reaches (and modifies) copies of local variables,
+// those copies are returned to the caller to allow tracking the changes and
+// manually updating the actual variables.
 RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
   HandleScope scope(isolate);
 
@@ -12523,7 +12530,23 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
   }
 
   Handle<Object> receiver(frame->receiver(), isolate);
-  return DebugEvaluate(isolate, context, context_extension, receiver, source);
+  Object* evaluate_result_object;
+  { MaybeObject* maybe_result =
+    DebugEvaluate(isolate, context, context_extension, receiver, source);
+    if (!maybe_result->ToObject(&evaluate_result_object)) return maybe_result;
+  }
+  Handle<Object> evaluate_result(evaluate_result_object, isolate);
+
+  Handle<JSObject> local_scope_after = MaterializeLocalScopeWithFrameInspector(
+      isolate, frame, &frame_inspector);
+
+  Handle<FixedArray> resultArray =
+      isolate->factory()->NewFixedArray(3);
+  resultArray->set(0, *evaluate_result);
+  resultArray->set(2, *local_scope);
+  resultArray->set(1, *local_scope_after);
+
+  return *(isolate->factory()->NewJSArrayWithElements(resultArray));
 }
 
 
index a68162d..e6326e1 100644 (file)
@@ -38,6 +38,7 @@ function h() {
   var b = 2;
   var eval = 5;  // Overriding eval should not break anything.
   debugger;  // Breakpoint.
+  return a;
 }
 
 function checkFrame0(frame) {
@@ -60,7 +61,7 @@ function checkFrame0(frame) {
 function g() {
   var a = 3;
   eval("var b = 4;");
-  h();
+  return h() + a;
 }
 
 function checkFrame1(frame) {
@@ -83,7 +84,7 @@ function f() {
   var a = 5;
   var b = 0;
   with ({b:6}) {
-    g();
+    return g();
   }
 }
 
@@ -125,6 +126,10 @@ function listener(event, exec_state, event_data, data) {
       assertEquals(6, exec_state.frame(2).evaluate('b').value());
       assertEquals("function",
                    typeof exec_state.frame(2).evaluate('eval').value());
+      assertEquals("foo",
+                   exec_state.frame(0).evaluate('a = "foo"').value());
+      assertEquals("bar",
+                   exec_state.frame(1).evaluate('a = "bar"').value());
       // Indicate that all was processed.
       listenerComplete = true;
     }
@@ -137,7 +142,9 @@ function listener(event, exec_state, event_data, data) {
 // Add the debug event listener.
 Debug.setListener(listener);
 
-f();
+var f_result = f();
+
+assertEquals('foobar', f_result);
 
 // Make sure that the debug event listener was invoked.
 assertFalse(exception, "exception in listener")