Debugger: consider try-finally scopes not catching wrt debug events.
authoryangguo <yangguo@chromium.org>
Fri, 29 May 2015 10:05:22 +0000 (03:05 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 29 May 2015 10:05:33 +0000 (10:05 +0000)
R=mstarzinger@chromium.org
BUG=chromium:492522
LOG=Y

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

Cr-Commit-Position: refs/heads/master@{#28696}

src/debug.cc
src/frames.cc
src/frames.h
src/full-codegen.cc
src/full-codegen.h
src/isolate.cc
src/objects.cc
src/objects.h
test/cctest/test-debug.cc

index b63e99d..49e136f 100644 (file)
@@ -1150,7 +1150,7 @@ void Debug::FloodHandlerWithOneShot() {
   for (JavaScriptFrameIterator it(isolate_, id); !it.done(); it.Advance()) {
     JavaScriptFrame* frame = it.frame();
     int stack_slots = 0;  // The computed stack slot count is not used.
-    if (frame->LookupExceptionHandlerInTable(&stack_slots) > 0) {
+    if (frame->LookupExceptionHandlerInTable(&stack_slots, NULL) > 0) {
       // Flood the function with the catch/finally block with break points.
       FloodWithOneShot(Handle<JSFunction>(frame->function()));
       return;
@@ -2486,6 +2486,7 @@ MaybeHandle<Object> Debug::PromiseHasUserDefinedRejectHandler(
 
 
 void Debug::OnException(Handle<Object> exception, Handle<Object> promise) {
+  // In our prediction, try-finally is not considered to catch.
   Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher();
   bool uncaught = (catch_type == Isolate::NOT_CAUGHT);
   if (promise->IsJSObject()) {
index 86f2a11..434cdb3 100644 (file)
@@ -761,12 +761,13 @@ void JavaScriptFrame::Summarize(List<FrameSummary>* functions) {
 }
 
 
-int JavaScriptFrame::LookupExceptionHandlerInTable(int* stack_slots) {
+int JavaScriptFrame::LookupExceptionHandlerInTable(
+    int* stack_slots, HandlerTable::CatchPrediction* prediction) {
   Code* code = LookupCode();
   DCHECK(!code->is_optimized_code());
   HandlerTable* table = HandlerTable::cast(code->handler_table());
   int pc_offset = static_cast<int>(pc() - code->entry());
-  return table->LookupRange(pc_offset, stack_slots);
+  return table->LookupRange(pc_offset, stack_slots, prediction);
 }
 
 
@@ -977,14 +978,14 @@ void OptimizedFrame::Summarize(List<FrameSummary>* frames) {
 }
 
 
-int OptimizedFrame::LookupExceptionHandlerInTable(int* stack_slots) {
+int OptimizedFrame::LookupExceptionHandlerInTable(
+    int* stack_slots, HandlerTable::CatchPrediction* prediction) {
   Code* code = LookupCode();
   DCHECK(code->is_optimized_code());
   HandlerTable* table = HandlerTable::cast(code->handler_table());
   int pc_offset = static_cast<int>(pc() - code->entry());
   *stack_slots = code->stack_slots();
-  HandlerTable::CatchPrediction prediction;  // TODO(yangguo): For debugger.
-  return table->LookupReturn(pc_offset, &prediction);
+  return table->LookupReturn(pc_offset, prediction);
 }
 
 
index 397c7b5..6acf3c6 100644 (file)
@@ -563,7 +563,8 @@ class JavaScriptFrame: public StandardFrame {
 
   // Lookup exception handler for current {pc}, returns -1 if none found. Also
   // returns the expected number of stack slots at the handler site.
-  virtual int LookupExceptionHandlerInTable(int* stack_slots);
+  virtual int LookupExceptionHandlerInTable(
+      int* stack_slots, HandlerTable::CatchPrediction* prediction);
 
   // Architecture-specific register description.
   static Register fp_register();
@@ -639,7 +640,8 @@ class OptimizedFrame : public JavaScriptFrame {
 
   // Lookup exception handler for current {pc}, returns -1 if none found. Also
   // returns the expected number of stack slots at the handler site.
-  virtual int LookupExceptionHandlerInTable(int* stack_slots);
+  virtual int LookupExceptionHandlerInTable(
+      int* stack_slots, HandlerTable::CatchPrediction* prediction);
 
   DeoptimizationInputData* GetDeoptimizationData(int* deopt_index);
 
index a256697..95957d6 100644 (file)
@@ -1209,11 +1209,14 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
 
   // Try block code. Sets up the exception handler chain.
   __ bind(&try_entry);
+
+  try_catch_depth_++;
   EnterTryBlock(stmt->index(), &handler_entry);
   { TryCatch try_body(this);
     Visit(stmt->try_block());
   }
   ExitTryBlock(stmt->index());
+  try_catch_depth_--;
   __ bind(&exit);
 }
 
@@ -1428,7 +1431,9 @@ void FullCodeGenerator::VisitThrow(Throw* expr) {
 
 void FullCodeGenerator::EnterTryBlock(int index, Label* handler) {
   handler_table()->SetRangeStart(index, masm()->pc_offset());
-  handler_table()->SetRangeHandler(index, handler->pos());
+  HandlerTable::CatchPrediction prediction =
+      try_catch_depth_ > 0 ? HandlerTable::CAUGHT : HandlerTable::UNCAUGHT;
+  handler_table()->SetRangeHandler(index, handler->pos(), prediction);
 
   // Determine expression stack depth of try statement.
   int stack_depth = info_->scope()->num_stack_slots();  // Include stack locals.
index 8869d8f..e81b44e 100644 (file)
@@ -69,10 +69,12 @@ class FullCodeGenerator: public AstVisitor {
         scope_(info->scope()),
         nesting_stack_(NULL),
         loop_depth_(0),
+        try_catch_depth_(0),
         globals_(NULL),
         context_(NULL),
         bailout_entries_(info->HasDeoptimizationSupport()
-                         ? info->function()->ast_node_count() : 0,
+                             ? info->function()->ast_node_count()
+                             : 0,
                          info->zone()),
         back_edges_(2, info->zone()),
         ic_total_count_(0) {
@@ -964,6 +966,7 @@ class FullCodeGenerator: public AstVisitor {
   Label return_label_;
   NestedStatement* nesting_stack_;
   int loop_depth_;
+  int try_catch_depth_;
   ZoneList<Handle<Object> >* globals_;
   Handle<FixedArray> modules_;
   int module_index_;
index dc5b536..a0ea930 100644 (file)
@@ -1067,7 +1067,7 @@ Object* Isolate::UnwindAndFindHandler() {
     if (frame->is_optimized() && catchable_by_js) {
       OptimizedFrame* js_frame = static_cast<OptimizedFrame*>(frame);
       int stack_slots = 0;  // Will contain stack slot count of frame.
-      offset = js_frame->LookupExceptionHandlerInTable(&stack_slots);
+      offset = js_frame->LookupExceptionHandlerInTable(&stack_slots, NULL);
       if (offset >= 0) {
         // Compute the stack pointer from the frame pointer. This ensures that
         // argument slots on the stack are dropped as returning would.
@@ -1087,7 +1087,7 @@ Object* Isolate::UnwindAndFindHandler() {
     if (frame->is_java_script() && catchable_by_js) {
       JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(frame);
       int stack_slots = 0;  // Will contain operand stack depth of handler.
-      offset = js_frame->LookupExceptionHandlerInTable(&stack_slots);
+      offset = js_frame->LookupExceptionHandlerInTable(&stack_slots, NULL);
       if (offset >= 0) {
         // Compute the stack pointer from the frame pointer. This ensures that
         // operand stack slots are dropped for nested statements. Also restore
@@ -1143,8 +1143,12 @@ Isolate::CatchType Isolate::PredictExceptionCatcher() {
     if (frame->is_java_script()) {
       JavaScriptFrame* js_frame = static_cast<JavaScriptFrame*>(frame);
       int stack_slots = 0;  // The computed stack slot count is not used.
-      if (js_frame->LookupExceptionHandlerInTable(&stack_slots) > 0) {
-        return CAUGHT_BY_JAVASCRIPT;
+      HandlerTable::CatchPrediction prediction;
+      if (js_frame->LookupExceptionHandlerInTable(&stack_slots, &prediction) >
+          0) {
+        // We are conservative with our prediction: try-finally is considered
+        // to always rethrow, to meet the expectation of the debugger.
+        if (prediction == HandlerTable::CAUGHT) return CAUGHT_BY_JAVASCRIPT;
       }
     }
 
@@ -1573,7 +1577,7 @@ Handle<Object> Isolate::GetPromiseOnStackOnThrow() {
   for (JavaScriptFrameIterator it(this); !it.done(); it.Advance()) {
     JavaScriptFrame* frame = it.frame();
     int stack_slots = 0;  // The computed stack slot count is not used.
-    if (frame->LookupExceptionHandlerInTable(&stack_slots) > 0) {
+    if (frame->LookupExceptionHandlerInTable(&stack_slots, NULL) > 0) {
       // Throwing inside a Promise only leads to a reject if not caught by an
       // inner try-catch or try-finally.
       if (frame->function() == *promise_function) {
index 91603ff..125a767 100644 (file)
@@ -8618,12 +8618,15 @@ Handle<DeoptimizationOutputData> DeoptimizationOutputData::New(
 }
 
 
-int HandlerTable::LookupRange(int pc_offset, int* stack_depth_out) {
+int HandlerTable::LookupRange(int pc_offset, int* stack_depth_out,
+                              CatchPrediction* prediction_out) {
   int innermost_handler = -1, innermost_start = -1;
   for (int i = 0; i < length(); i += kRangeEntrySize) {
     int start_offset = Smi::cast(get(i + kRangeStartIndex))->value();
     int end_offset = Smi::cast(get(i + kRangeEndIndex))->value();
-    int handler_offset = Smi::cast(get(i + kRangeHandlerIndex))->value();
+    int handler_field = Smi::cast(get(i + kRangeHandlerIndex))->value();
+    int handler_offset = HandlerOffsetField::decode(handler_field);
+    CatchPrediction prediction = HandlerPredictionField::decode(handler_field);
     int stack_depth = Smi::cast(get(i + kRangeDepthIndex))->value();
     if (pc_offset > start_offset && pc_offset <= end_offset) {
       DCHECK_NE(start_offset, innermost_start);
@@ -8631,6 +8634,7 @@ int HandlerTable::LookupRange(int pc_offset, int* stack_depth_out) {
       innermost_handler = handler_offset;
       innermost_start = start_offset;
       *stack_depth_out = stack_depth;
+      if (prediction_out) *prediction_out = prediction;
     }
   }
   return innermost_handler;
@@ -8638,12 +8642,14 @@ int HandlerTable::LookupRange(int pc_offset, int* stack_depth_out) {
 
 
 // TODO(turbofan): Make sure table is sorted and use binary search.
-int HandlerTable::LookupReturn(int pc_offset, CatchPrediction* prediction) {
+int HandlerTable::LookupReturn(int pc_offset, CatchPrediction* prediction_out) {
   for (int i = 0; i < length(); i += kReturnEntrySize) {
     int return_offset = Smi::cast(get(i + kReturnOffsetIndex))->value();
     int handler_field = Smi::cast(get(i + kReturnHandlerIndex))->value();
     if (pc_offset == return_offset) {
-      *prediction = HandlerPredictionField::decode(handler_field);
+      if (prediction_out) {
+        *prediction_out = HandlerPredictionField::decode(handler_field);
+      }
       return HandlerOffsetField::decode(handler_field);
     }
   }
@@ -11835,10 +11841,13 @@ void HandlerTable::HandlerTableRangePrint(std::ostream& os) {
   for (int i = 0; i < length(); i += kRangeEntrySize) {
     int pc_start = Smi::cast(get(i + kRangeStartIndex))->value();
     int pc_end = Smi::cast(get(i + kRangeEndIndex))->value();
-    int handler = Smi::cast(get(i + kRangeHandlerIndex))->value();
+    int handler_field = Smi::cast(get(i + kRangeHandlerIndex))->value();
+    int handler_offset = HandlerOffsetField::decode(handler_field);
+    CatchPrediction prediction = HandlerPredictionField::decode(handler_field);
     int depth = Smi::cast(get(i + kRangeDepthIndex))->value();
     os << "  (" << std::setw(4) << pc_start << "," << std::setw(4) << pc_end
-       << ")  ->  " << std::setw(4) << handler << " (depth=" << depth << ")\n";
+       << ")  ->  " << std::setw(4) << handler_offset
+       << " (prediction=" << prediction << ", depth=" << depth << ")\n";
   }
 }
 
@@ -11851,7 +11860,7 @@ void HandlerTable::HandlerTableReturnPrint(std::ostream& os) {
     int handler_offset = HandlerOffsetField::decode(handler_field);
     CatchPrediction prediction = HandlerPredictionField::decode(handler_field);
     os << "  " << std::setw(4) << pc_offset << "  ->  " << std::setw(4)
-       << handler_offset << " (" << prediction << ")\n";
+       << handler_offset << " (prediction=" << prediction << ")\n";
   }
 }
 
index 46b457c..1ac1b5e 100644 (file)
@@ -5171,7 +5171,9 @@ class HandlerTable : public FixedArray {
   void SetRangeEnd(int index, int value) {
     set(index * kRangeEntrySize + kRangeEndIndex, Smi::FromInt(value));
   }
-  void SetRangeHandler(int index, int value) {
+  void SetRangeHandler(int index, int offset, CatchPrediction prediction) {
+    int value = HandlerOffsetField::encode(offset) |
+                HandlerPredictionField::encode(prediction);
     set(index * kRangeEntrySize + kRangeHandlerIndex, Smi::FromInt(value));
   }
   void SetRangeDepth(int index, int value) {
@@ -5189,7 +5191,7 @@ class HandlerTable : public FixedArray {
   }
 
   // Lookup handler in a table based on ranges.
-  int LookupRange(int pc_offset, int* stack_depth);
+  int LookupRange(int pc_offset, int* stack_depth, CatchPrediction* prediction);
 
   // Lookup handler in a table based on return addresses.
   int LookupReturn(int pc_offset, CatchPrediction* prediction);
index 4de243b..89fdabb 100644 (file)
@@ -3856,6 +3856,13 @@ TEST(PauseInScript) {
 }
 
 
+static void DebugEventCounterCheck(int caught, int uncaught, int message) {
+  CHECK_EQ(caught, exception_hit_count);
+  CHECK_EQ(uncaught, uncaught_exception_hit_count);
+  CHECK_EQ(message, message_callback_count);
+}
+
+
 // Test break on exceptions. For each exception break combination the number
 // of debug event exception callbacks and message callbacks are collected. The
 // number of debug event exception callbacks are used to check that the
@@ -3875,6 +3882,15 @@ TEST(BreakOnException) {
                       "caught");
   v8::Local<v8::Function> notCaught =
       CompileFunction(&env, "function notCaught(){throws();}", "notCaught");
+  v8::Local<v8::Function> notCaughtFinally = CompileFunction(
+      &env, "function notCaughtFinally(){try{throws();}finally{}}",
+      "notCaughtFinally");
+  // In this edge case, even though this finally does not propagate the
+  // exception, the debugger considers this uncaught, since we want to break
+  // at the first throw for the general case where finally implicitly rethrows.
+  v8::Local<v8::Function> edgeCaseFinally = CompileFunction(
+      &env, "function caughtFinally(){L:try{throws();}finally{break L;}}",
+      "caughtFinally");
 
   v8::V8::AddMessageListener(MessageCallbackCount);
   v8::Debug::SetDebugEventListener(DebugEventCounter);
@@ -3883,117 +3899,117 @@ TEST(BreakOnException) {
   DebugEventCounterClear();
   MessageCallbackCountClear();
   caught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(0, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(0, message_callback_count);
+  DebugEventCounterCheck(0, 0, 0);
   notCaught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(0, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(1, message_callback_count);
+  DebugEventCounterCheck(0, 0, 1);
+  notCaughtFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(0, 0, 2);
+  edgeCaseFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(0, 0, 2);
 
   // No break on exception
   DebugEventCounterClear();
   MessageCallbackCountClear();
   ChangeBreakOnException(false, false);
   caught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(0, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(0, message_callback_count);
+  DebugEventCounterCheck(0, 0, 0);
   notCaught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(0, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(1, message_callback_count);
+  DebugEventCounterCheck(0, 0, 1);
+  notCaughtFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(0, 0, 2);
+  edgeCaseFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(0, 0, 2);
 
   // Break on uncaught exception
   DebugEventCounterClear();
   MessageCallbackCountClear();
   ChangeBreakOnException(false, true);
   caught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(0, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(0, message_callback_count);
+  DebugEventCounterCheck(0, 0, 0);
   notCaught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(1, exception_hit_count);
-  CHECK_EQ(1, uncaught_exception_hit_count);
-  CHECK_EQ(1, message_callback_count);
+  DebugEventCounterCheck(1, 1, 1);
+  notCaughtFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(2, 2, 2);
+  edgeCaseFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(3, 3, 2);
 
   // Break on exception and uncaught exception
   DebugEventCounterClear();
   MessageCallbackCountClear();
   ChangeBreakOnException(true, true);
   caught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(1, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(0, message_callback_count);
+  DebugEventCounterCheck(1, 0, 0);
   notCaught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(2, exception_hit_count);
-  CHECK_EQ(1, uncaught_exception_hit_count);
-  CHECK_EQ(1, message_callback_count);
+  DebugEventCounterCheck(2, 1, 1);
+  notCaughtFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(3, 2, 2);
+  edgeCaseFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(4, 3, 2);
 
   // Break on exception
   DebugEventCounterClear();
   MessageCallbackCountClear();
   ChangeBreakOnException(true, false);
   caught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(1, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(0, message_callback_count);
+  DebugEventCounterCheck(1, 0, 0);
   notCaught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(2, exception_hit_count);
-  CHECK_EQ(1, uncaught_exception_hit_count);
-  CHECK_EQ(1, message_callback_count);
+  DebugEventCounterCheck(2, 1, 1);
+  notCaughtFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(3, 2, 2);
+  edgeCaseFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(4, 3, 2);
 
   // No break on exception using JavaScript
   DebugEventCounterClear();
   MessageCallbackCountClear();
   ChangeBreakOnExceptionFromJS(env->GetIsolate(), false, false);
   caught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(0, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(0, message_callback_count);
+  DebugEventCounterCheck(0, 0, 0);
   notCaught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(0, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(1, message_callback_count);
+  DebugEventCounterCheck(0, 0, 1);
+  notCaughtFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(0, 0, 2);
+  edgeCaseFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(0, 0, 2);
 
   // Break on uncaught exception using JavaScript
   DebugEventCounterClear();
   MessageCallbackCountClear();
   ChangeBreakOnExceptionFromJS(env->GetIsolate(), false, true);
   caught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(0, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(0, message_callback_count);
+  DebugEventCounterCheck(0, 0, 0);
   notCaught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(1, exception_hit_count);
-  CHECK_EQ(1, uncaught_exception_hit_count);
-  CHECK_EQ(1, message_callback_count);
+  DebugEventCounterCheck(1, 1, 1);
+  notCaughtFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(2, 2, 2);
+  edgeCaseFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(3, 3, 2);
 
   // Break on exception and uncaught exception using JavaScript
   DebugEventCounterClear();
   MessageCallbackCountClear();
   ChangeBreakOnExceptionFromJS(env->GetIsolate(), true, true);
   caught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(1, exception_hit_count);
-  CHECK_EQ(0, message_callback_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
+  DebugEventCounterCheck(1, 0, 0);
   notCaught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(2, exception_hit_count);
-  CHECK_EQ(1, uncaught_exception_hit_count);
-  CHECK_EQ(1, message_callback_count);
+  DebugEventCounterCheck(2, 1, 1);
+  notCaughtFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(3, 2, 2);
+  edgeCaseFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(4, 3, 2);
 
   // Break on exception using JavaScript
   DebugEventCounterClear();
   MessageCallbackCountClear();
   ChangeBreakOnExceptionFromJS(env->GetIsolate(), true, false);
   caught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(1, exception_hit_count);
-  CHECK_EQ(0, uncaught_exception_hit_count);
-  CHECK_EQ(0, message_callback_count);
+  DebugEventCounterCheck(1, 0, 0);
   notCaught->Call(env->Global(), 0, NULL);
-  CHECK_EQ(2, exception_hit_count);
-  CHECK_EQ(1, uncaught_exception_hit_count);
-  CHECK_EQ(1, message_callback_count);
+  DebugEventCounterCheck(2, 1, 1);
+  notCaughtFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(3, 2, 2);
+  edgeCaseFinally->Call(env->Global(), 0, NULL);
+  DebugEventCounterCheck(4, 3, 2);
 
   v8::Debug::SetDebugEventListener(NULL);
   CheckDebuggerUnloaded();
@@ -4001,6 +4017,35 @@ TEST(BreakOnException) {
 }
 
 
+static void try_finally_original_message(v8::Handle<v8::Message> message,
+                                         v8::Handle<v8::Value> data) {
+  CHECK_EQ(2, message->GetLineNumber());
+  CHECK_EQ(2, message->GetStartColumn());
+  message_callback_count++;
+}
+
+
+TEST(TryFinallyOriginalMessage) {
+  // Test that the debugger plays nicely with the pending message.
+  message_callback_count = 0;
+  DebugEventCounterClear();
+  v8::V8::AddMessageListener(try_finally_original_message);
+  v8::Debug::SetDebugEventListener(DebugEventCounter);
+  ChangeBreakOnException(true, true);
+  DebugLocalContext env;
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope scope(isolate);
+  CompileRun(
+      "try {\n"
+      "  throw 1;\n"
+      "} finally {\n"
+      "}\n");
+  DebugEventCounterCheck(1, 1, 1);
+  v8::Debug::SetDebugEventListener(NULL);
+  v8::V8::RemoveMessageListeners(try_finally_original_message);
+}
+
+
 TEST(EvalJSInDebugEventListenerOnNativeReThrownException) {
   DebugLocalContext env;
   v8::HandleScope scope(env->GetIsolate());