Fix step in positions (include various calls and exclude current pc point), add a...
authorprybin@chromium.org <prybin@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 7 Aug 2013 12:27:22 +0000 (12:27 +0000)
committerprybin@chromium.org <prybin@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 7 Aug 2013 12:27:22 +0000 (12:27 +0000)
R=yangguo@chromium.org

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

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

src/debug.cc
src/execution.cc
src/runtime.cc
test/mjsunit/debug-stepin-positions.js [new file with mode: 0644]

index 03fa3a2..4966713 100644 (file)
@@ -409,6 +409,9 @@ bool BreakLocationIterator::IsStepInLocation(Isolate* isolate) {
     HandleScope scope(debug_info_->GetIsolate());
     Address target = rinfo()->target_address();
     Handle<Code> target_code(Code::GetCodeFromTargetAddress(target));
+    if (target_code->kind() == Code::STUB) {
+      return target_code->major_key() == CodeStub::CallFunction;
+    }
     return target_code->is_call_stub() || target_code->is_keyed_call_stub();
   } else {
     return false;
index d7b9cf5..ecfa1db 100644 (file)
@@ -206,10 +206,12 @@ Handle<Object> Execution::TryCall(Handle<JSFunction> func,
   catcher.SetCaptureMessage(false);
   *caught_exception = false;
 
+  // Get isolate now, because handle might be persistent
+  // and get destroyed in the next call.
+  Isolate* isolate = func->GetIsolate();
   Handle<Object> result = Invoke(false, func, receiver, argc, args,
                                  caught_exception);
 
-  Isolate* isolate = func->GetIsolate();
   if (*caught_exception) {
     ASSERT(catcher.HasCaught());
     ASSERT(isolate->has_pending_exception());
index a1c0d75..a9121ce 100644 (file)
@@ -12014,8 +12014,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetStepInPositions) {
   JavaScriptFrameIterator frame_it(isolate, id);
   JavaScriptFrame* frame = frame_it.frame();
 
+  Handle<JSFunction> fun =
+      Handle<JSFunction>(frame->function());
   Handle<SharedFunctionInfo> shared =
-      Handle<SharedFunctionInfo>(frame->function()->shared());
+      Handle<SharedFunctionInfo>(fun->shared());
+
+  if (!isolate->debug()->EnsureDebugInfo(shared, fun)) {
+    return isolate->heap()->undefined_value();
+  }
+
   Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared);
 
   int len = 0;
@@ -12028,12 +12035,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetStepInPositions) {
   int current_statement_pos = break_location_iterator.statement_position();
 
   while (!break_location_iterator.Done()) {
-    if (break_location_iterator.IsStepInLocation(isolate)) {
-      Smi* position_value = Smi::FromInt(break_location_iterator.position());
-      JSObject::SetElement(array, len,
-          Handle<Object>(position_value, isolate),
-          NONE, kNonStrictMode);
-      len++;
+    if (break_location_iterator.pc() > frame->pc()) {
+      if (break_location_iterator.IsStepInLocation(isolate)) {
+        Smi* position_value = Smi::FromInt(break_location_iterator.position());
+        JSObject::SetElement(array, len,
+            Handle<Object>(position_value, isolate),
+            NONE, kNonStrictMode);
+        len++;
+      }
     }
     // Advance iterator.
     break_location_iterator.Next();
diff --git a/test/mjsunit/debug-stepin-positions.js b/test/mjsunit/debug-stepin-positions.js
new file mode 100644 (file)
index 0000000..482e21b
--- /dev/null
@@ -0,0 +1,142 @@
+// Copyright 2008 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 --nocrankshaft
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+
+function DebuggerStatement() {
+  debugger;
+}
+
+function TestCase(fun) {
+  var exception = false;
+  var codeSnippet = undefined;
+  var resultPositions = undefined;
+
+  function listener(event, exec_state, event_data, data) {
+    try {
+      if (event == Debug.DebugEvent.Break) {
+        Debug.setListener(null);
+
+        var secondFrame = exec_state.frame(1);
+        codeSnippet = secondFrame.sourceLineText();
+        resultPositions = secondFrame.stepInPositions();
+      }
+    } catch (e) {
+      exception = e
+    }
+  }
+
+  Debug.setListener(listener);
+
+  fun();
+
+  Debug.setListener(null);
+
+  assertTrue(!exception, exception);
+
+  var expectedPositions = {};
+  var markPattern = new RegExp("/\\*#\\*/", "g");
+
+  var matchResult;
+  while ( (matchResult = markPattern.exec(codeSnippet)) ) {
+    expectedPositions[matchResult.index] = true;
+  }
+
+  print(codeSnippet);
+
+  var decoratedResult = codeSnippet;
+
+  function replaceStringRange(s, pos, substitute) {
+   return s.substring(0, pos) + substitute +
+       s.substring(pos + substitute.length);
+  }
+
+  var markLength = 5;
+  var unexpectedPositionFound = false;
+
+  for (var i = 0; i < resultPositions.length; i++) {
+    var col = resultPositions[i].position.column - markLength;
+    if (expectedPositions[col]) {
+      delete expectedPositions[col];
+      decoratedResult = replaceStringRange(decoratedResult, col, "*YES*");
+    } else {
+      decoratedResult = replaceStringRange(decoratedResult, col, "!BAD!");
+      unexpectedPositionFound = true;
+    }
+  }
+
+  print(decoratedResult);
+
+  for (var n in expectedPositions) {
+    assertTrue(false, "Some positions are not reported: " + decoratedResult);
+    break;
+  }
+  assertFalse(unexpectedPositionFound, "Found unexpected position: " +
+      decoratedResult);
+}
+
+
+// Test cases.
+
+// Method calls.
+var fun = function() {
+  var data = {
+    a: function() {}
+  };
+  var res = [ DebuggerStatement(), data./*#*/a(), data[/*#*/String("a")]/*#*/(), data["a"]/*#*/(), data.a, data["a"] ];
+};
+TestCase(fun);
+
+// Function call on a value.
+var fun = function() {
+  function g(p) {
+      return g;
+  }
+  var res = [ DebuggerStatement(), /*#*/g(2), /*#*/g(2)/*#*/(3), /*#*/g(0)/*#*/(0)/*#*/(g) ];
+};
+TestCase(fun);
+
+// Local function call, closure function call,
+// local function construction call.
+var fun = (function(p) {
+  return function() {
+    function f(a, b) {
+    }
+    var res = /*#*/f(DebuggerStatement(), /*#*/p(/*#*/new f()));
+  };
+})(Object);
+TestCase(fun);
+
+// Global function, global object construction, calls before pause point.
+var fun = (function(p) {
+  return function() {
+    var res = [ Math.abs(new Object()), DebuggerStatement(), Math./*#*/abs(4), /*#*/new Object()./*#*/toString() ];
+  };
+})(Object);
+TestCase(fun);