Reviewers: Michael Starzinger,

Description:
Merged r12019 into 3.10 branch.

Original CL: http://codereview.chromium.org/10698123/

[email protected]
BUG=
TEST=


Please review this at https://chromiumcodereview.appspot.com/10690162/

SVN Base: https://v8.googlecode.com/svn/branches/3.10

Affected files:
  M src/debug.cc
  M src/execution.cc
  M src/runtime.cc
  M src/version.cc
  M test/cctest/test-debug.cc


Index: src/debug.cc
diff --git a/src/debug.cc b/src/debug.cc
index 99256ba21a88a8510978e5578ab0c4f541982b0a..5dfc88d62a4a255b2b80ab7a1ac5cc9ca17e891a 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -989,18 +989,17 @@ Object* Debug::Break(Arguments args) {
         it.Advance();
       }

-      // If we found original frame
-      if (it.frame()->fp() == thread_local_.last_fp_) {
-        if (step_count > 1) {
-          // Save old count and action to continue stepping after
-          // StepOut
-          thread_local_.queued_step_count_ = step_count - 1;
-        }
-
-        // Set up for StepOut to reach target frame
-        step_action = StepOut;
-        step_count = count;
+      // Check that we indeed found the frame we are looking for.
+      CHECK(!it.done() && (it.frame()->fp() == thread_local_.last_fp_));
+      if (step_count > 1) {
+        // Save old count and action to continue stepping after
+        // StepOut
+        thread_local_.queued_step_count_ = step_count - 1;
       }
+
+      // Set up for StepOut to reach target frame
+      step_action = StepOut;
+      step_count = count;
     }

     // Clear all current stepping setup.
Index: src/execution.cc
diff --git a/src/execution.cc b/src/execution.cc
index 561897567378e90e0d6011334aaf3ffbd65648cb..40ed7de4140d1ea5716c5bc625bd46c00d26fa9b 100644
--- a/src/execution.cc
+++ b/src/execution.cc
@@ -132,6 +132,12 @@ static Handle<Object> Invoke(bool is_construct,
         V8::FatalProcessOutOfMemory("JS", true);
       }
     }
+#ifdef ENABLE_DEBUGGER_SUPPORT
+    // Reset stepping state when script exits with uncaught exception.
+    if (isolate->debugger()->IsDebuggerActive()) {
+      isolate->debug()->ClearStepping();
+    }
+#endif  // ENABLE_DEBUGGER_SUPPORT
     return Handle<Object>();
   } else {
     isolate->clear_pending_message();
Index: src/runtime.cc
diff --git a/src/runtime.cc b/src/runtime.cc
index 6163ca8fa37152f28eb47cce131f01c6056590e8..9e9be41f619008e6c137d6a12aacc00b921c4672 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -4730,21 +4730,28 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StoreArrayLiteralElement) { // Check whether debugger and is about to step into the callback that is passed
 // to a built-in function such as Array.forEach.
 RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugCallbackSupportsStepping) {
-  if (!isolate->IsDebuggerActive()) return isolate->heap()->false_value();
+#ifdef ENABLE_DEBUGGER_SUPPORT
+  if (!isolate->IsDebuggerActive() || !isolate->debug()->StepInActive()) {
+    return isolate->heap()->false_value();
+  }
   CONVERT_ARG_CHECKED(Object, callback, 0);
// We do not step into the callback if it's a builtin or not even a function. if (!callback->IsJSFunction() || JSFunction::cast(callback)->IsBuiltin()) {
     return isolate->heap()->false_value();
   }
   return isolate->heap()->true_value();
+#else
+  return isolate->heap()->false_value();
+#endif  // ENABLE_DEBUGGER_SUPPORT
 }


 // Set one shot breakpoints for the callback function that is passed to a
// built-in function such as Array.forEach to enable stepping into the callback.
 RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrepareStepInIfStepping) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
   Debug* debug = isolate->debug();
-  if (!debug->IsStepping()) return NULL;
+  if (!debug->IsStepping()) return isolate->heap()->undefined_value();
   CONVERT_ARG_CHECKED(Object, callback, 0);
   HandleScope scope(isolate);
Handle<SharedFunctionInfo> shared_info(JSFunction::cast(callback)->shared()); @@ -4753,7 +4760,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrepareStepInIfStepping) {
   // again, we need to clear the step out at this point.
   debug->ClearStepOut();
   debug->FloodWithOneShot(shared_info);
-  return NULL;
+#endif  // ENABLE_DEBUGGER_SUPPORT
+  return isolate->heap()->undefined_value();
 }


Index: src/version.cc
diff --git a/src/version.cc b/src/version.cc
index b7fb207ed5e9b3960501dbd80a6f1af8ff591b7a..b260c3fe33ca5ffe0004f7605f2f17dcbccd9be5 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -35,7 +35,7 @@
 #define MAJOR_VERSION     3
 #define MINOR_VERSION     10
 #define BUILD_NUMBER      8
-#define PATCH_LEVEL       20
+#define PATCH_LEVEL       21
 // Use 1 for candidates and 0 otherwise.
 // (Boolean macro values are not supported by all preprocessors.)
 #define IS_CANDIDATE_VERSION 0
Index: test/cctest/test-debug.cc
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index e40f406c3c5f869743cb13e2f0d75b4afb95b480..166b762913f440527a29489dc27ce7e500d46c12 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -7350,4 +7350,47 @@ TEST(DebugBreakInline) {
 }


+static void DebugEventStepNext(v8::DebugEvent event,
+                               v8::Handle<v8::Object> exec_state,
+                               v8::Handle<v8::Object> event_data,
+                               v8::Handle<v8::Value> data) {
+  if (event == v8::Break) {
+    PrepareStep(StepNext);
+  }
+}
+
+
+static void RunScriptInANewCFrame(const char* source) {
+  v8::TryCatch try_catch;
+  CompileRun(source);
+  CHECK(try_catch.HasCaught());
+}
+
+
+TEST(Regress131642) {
+  // Bug description:
+ // When doing StepNext through the first script, the debugger is not reset
+  // after exiting through exception.  A flawed implementation enabling the
+ // debugger to step into Array.prototype.forEach breaks inside the callback
+  // for forEach in the second script under the assumption that we are in a
+ // recursive call. In an attempt to step out, we crawl the stack using the + // recorded frame pointer from the first script and fail when not finding it
+  // on the stack.
+  v8::HandleScope scope;
+  DebugLocalContext env;
+  v8::Debug::SetDebugEventListener(DebugEventStepNext);
+
+ // We step through the first script. It exits through an exception. We run + // this inside a new frame to record a different FP than the second script
+  // would expect.
+  const char* script_1 = "debugger; throw new Error();";
+  RunScriptInANewCFrame(script_1);
+
+  // The second script uses forEach.
+  const char* script_2 = "[0].forEach(function() { });";
+  CompileRun(script_2);
+
+  v8::Debug::SetDebugEventListener(NULL);
+}
+
 #endif  // ENABLE_DEBUGGER_SUPPORT


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

Reply via email to