More precise break points and stepping when debugging
authorsgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 8 Jun 2010 12:04:49 +0000 (12:04 +0000)
committersgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 8 Jun 2010 12:04:49 +0000 (12:04 +0000)
Added support for more precise break points when debugging and stepping. To achieve that additional nop instructions are inserted where breaking would otherwise be impossible. The number of nop instructions inserted are sufficient to make place for patching with a call to a debug break code stub. On Intel that is 5 nop's for 32-bit and 13 for 64-bit. Om ARM 3 nop instructions (12 bytes) are required.

In order to avoid inserting nop's in to many places a simple ast checker have been added to check whether there are breakable code in a statement or expression. If it is possible to break in an expression no additional break enabeling code is inserted.

Added break locations to the true and false part of a conditional expression.

Added stepping tests to cover more constructs.

These changes are only in the full compiler.

Changed the default value for the option --debugger in teh d8 shell from true to false. The reason for this is that with --debugger turned on the full compiler will be used for all code in when running d8, which can be unexpeceted.

Review URL: http://codereview.chromium.org/2693002

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

39 files changed:
src/arm/assembler-arm-inl.h
src/arm/assembler-arm.cc
src/arm/assembler-arm.h
src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/arm/debug-arm.cc
src/arm/full-codegen-arm.cc
src/assembler.cc
src/assembler.h
src/ast.h
src/builtins.cc
src/builtins.h
src/checks.h
src/codegen.cc
src/d8.cc
src/debug.cc
src/debug.h
src/flag-definitions.h
src/full-codegen.cc
src/full-codegen.h
src/ia32/assembler-ia32-inl.h
src/ia32/assembler-ia32.cc
src/ia32/assembler-ia32.h
src/ia32/codegen-ia32.h
src/ia32/debug-ia32.cc
src/mark-compact.cc
src/mips/assembler-mips.cc
src/mips/assembler-mips.h
src/objects.cc
src/parser.cc
src/serialize.cc
src/x64/assembler-x64-inl.h
src/x64/assembler-x64.cc
src/x64/assembler-x64.h
src/x64/codegen-x64.h
src/x64/debug-x64.cc
test/cctest/test-debug.cc
test/mjsunit/debug-conditional-breakpoints.js
test/mjsunit/debug-step.js

index e292cefabff8ac95a7533686a6b56de659612768..b7f1261b21d409d8441e2118b13ef54ad6100f2b 100644 (file)
@@ -168,6 +168,12 @@ bool RelocInfo::IsPatchedReturnSequence() {
 }
 
 
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
+  Instr current_instr = Assembler::instr_at(pc_);
+  return !Assembler::IsNop(current_instr, 2);
+}
+
+
 void RelocInfo::Visit(ObjectVisitor* visitor) {
   RelocInfo::Mode mode = rmode();
   if (mode == RelocInfo::EMBEDDED_OBJECT) {
@@ -178,8 +184,10 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
     visitor->VisitExternalReference(target_reference_address());
 #ifdef ENABLE_DEBUGGER_SUPPORT
   } else if (Debug::has_break_points() &&
-             RelocInfo::IsJSReturn(mode) &&
-             IsPatchedReturnSequence()) {
+             (RelocInfo::IsJSReturn(mode) &&
+              IsPatchedReturnSequence()) ||
+             (RelocInfo::IsDebugBreakSlot(mode) &&
+              IsPatchedDebugBreakSlotSequence())) {
     visitor->VisitDebugTarget(this);
 #endif
   } else if (mode == RelocInfo::RUNTIME_ENTRY) {
index 846464ab5a1f7786961b3d329cc5b5f46b58240e..025f28e551399d4e56b41c5eb5ee769044d9c22a 100644 (file)
@@ -2040,6 +2040,13 @@ void Assembler::RecordJSReturn() {
 }
 
 
+void Assembler::RecordDebugBreakSlot() {
+  WriteRecordedPositions();
+  CheckBuffer();
+  RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
+}
+
+
 void Assembler::RecordComment(const char* msg) {
   if (FLAG_debug_code) {
     CheckBuffer();
@@ -2062,13 +2069,16 @@ void Assembler::RecordStatementPosition(int pos) {
 }
 
 
-void Assembler::WriteRecordedPositions() {
+bool Assembler::WriteRecordedPositions() {
+  bool written = false;
+
   // Write the statement position if it is different from what was written last
   // time.
   if (current_statement_position_ != written_statement_position_) {
     CheckBuffer();
     RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
     written_statement_position_ = current_statement_position_;
+    written = true;
   }
 
   // Write the position if it is different from what was written last time and
@@ -2078,7 +2088,11 @@ void Assembler::WriteRecordedPositions() {
     CheckBuffer();
     RecordRelocInfo(RelocInfo::POSITION, current_position_);
     written_position_ = current_position_;
+    written = true;
   }
+
+  // Return whether something was written.
+  return written;
 }
 
 
@@ -2135,9 +2149,10 @@ void Assembler::GrowBuffer() {
 
 void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
   RelocInfo rinfo(pc_, rmode, data);  // we do not try to reuse pool constants
-  if (rmode >= RelocInfo::JS_RETURN && rmode <= RelocInfo::STATEMENT_POSITION) {
+  if (rmode >= RelocInfo::JS_RETURN && rmode <= RelocInfo::DEBUG_BREAK_SLOT) {
     // Adjust code for new modes.
-    ASSERT(RelocInfo::IsJSReturn(rmode)
+    ASSERT(RelocInfo::IsDebugBreakSlot(rmode)
+           || RelocInfo::IsJSReturn(rmode)
            || RelocInfo::IsComment(rmode)
            || RelocInfo::IsPosition(rmode));
     // These modes do not need an entry in the constant pool.
index d0dee561cf25c5dda510eb649115ec4a9a40a4b5..e5d42f9a5e241d3d5e1c55bf37650d29ff913ab4 100644 (file)
@@ -629,22 +629,39 @@ class Assembler : public Malloced {
   // Distance between start of patched return sequence and the emitted address
   // to jump to.
 #ifdef USE_BLX
-  // Return sequence is:
+  // Patched return sequence is:
   //  ldr  ip, [pc, #0]   @ emited address and start
   //  blx  ip
   static const int kPatchReturnSequenceAddressOffset =  0 * kInstrSize;
 #else
-  // Return sequence is:
+  // Patched return sequence is:
   //  mov  lr, pc         @ start of sequence
   //  ldr  pc, [pc, #-4]  @ emited address
   static const int kPatchReturnSequenceAddressOffset =  kInstrSize;
 #endif
 
+  // Distance between start of patched debug break slot and the emitted address
+  // to jump to.
+#ifdef USE_BLX
+  // Patched debug break slot code is:
+  //  ldr  ip, [pc, #0]   @ emited address and start
+  //  blx  ip
+  static const int kPatchDebugBreakSlotAddressOffset =  0 * kInstrSize;
+#else
+  // Patched debug break slot code is:
+  //  mov  lr, pc         @ start of sequence
+  //  ldr  pc, [pc, #-4]  @ emited address
+  static const int kPatchDebugBreakSlotAddressOffset =  kInstrSize;
+#endif
+
   // Difference between address of current opcode and value read from pc
   // register.
   static const int kPcLoadDelta = 8;
 
-  static const int kJSReturnSequenceLength = 4;
+  static const int kJSReturnSequenceInstructions = 4;
+  static const int kDebugBreakSlotInstructions = 3;
+  static const int kDebugBreakSlotLength =
+      kDebugBreakSlotInstructions * kInstrSize;
 
   // ---------------------------------------------------------------------------
   // Code generation
@@ -981,13 +998,16 @@ class Assembler : public Malloced {
   // Mark address of the ExitJSFrame code.
   void RecordJSReturn();
 
+  // Mark address of a debug break slot.
+  void RecordDebugBreakSlot();
+
   // Record a comment relocation entry that can be used by a disassembler.
   // Use --debug_code to enable.
   void RecordComment(const char* msg);
 
   void RecordPosition(int pos);
   void RecordStatementPosition(int pos);
-  void WriteRecordedPositions();
+  bool WriteRecordedPositions();
 
   int pc_offset() const { return pc_ - buffer_; }
   int current_position() const { return current_position_; }
index d6fdf459ce432d170b55d6abeec00144299b639e..8d4bf14c984cc199c25cf943e17fd60c77371c88 100644 (file)
@@ -386,8 +386,10 @@ void CodeGenerator::Generate(CompilationInfo* info) {
       // the add instruction the add will generate two instructions.
       int return_sequence_length =
           masm_->InstructionsGeneratedSince(&check_exit_codesize);
-      CHECK(return_sequence_length == Assembler::kJSReturnSequenceLength ||
-            return_sequence_length == Assembler::kJSReturnSequenceLength + 1);
+      CHECK(return_sequence_length ==
+            Assembler::kJSReturnSequenceInstructions ||
+            return_sequence_length ==
+            Assembler::kJSReturnSequenceInstructions + 1);
 #endif
     }
   }
index 6a33667eb411bed3f515d4dd616ef17c805f330f..cabdf007b91dc4f8ba1b7a6dbb39283660a12ca3 100644 (file)
@@ -226,7 +226,9 @@ class CodeGenerator: public AstVisitor {
                               bool is_toplevel,
                               Handle<Script> script);
 
-  static void RecordPositions(MacroAssembler* masm, int pos);
+  static bool RecordPositions(MacroAssembler* masm,
+                              int pos,
+                              bool right_here = false);
 
   // Accessors
   MacroAssembler* masm() { return masm_; }
index 69fc504e7f16611e7cb0eca122e38099561cbd5f..74fd05ba8f4e85539e7ce85afe07997d09df5af2 100644 (file)
@@ -57,7 +57,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
   // #endif
   //   <debug break return code entry point address>
   //   bktp 0
-  CodePatcher patcher(rinfo()->pc(), 4);
+  CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
 #ifdef USE_BLX
   patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
   patcher.masm()->blx(v8::internal::ip);
@@ -73,17 +73,59 @@ void BreakLocationIterator::SetDebugBreakAtReturn() {
 // Restore the JS frame exit code.
 void BreakLocationIterator::ClearDebugBreakAtReturn() {
   rinfo()->PatchCode(original_rinfo()->pc(),
-                     Assembler::kJSReturnSequenceLength);
+                     Assembler::kJSReturnSequenceInstructions);
 }
 
 
-// A debug break in the exit code is identified by a call.
+// A debug break in the frame exit code is identified by the JS frame exit code
+// having been patched with a call instruction.
 bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
   ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
   return rinfo->IsPatchedReturnSequence();
 }
 
 
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+  ASSERT(IsDebugBreakSlot());
+  // Check whether the debug break slot instructions have been patched.
+  return rinfo()->IsPatchedDebugBreakSlotSequence();
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+  ASSERT(IsDebugBreakSlot());
+  // Patch the code changing the debug break slot code from
+  //   mov r2, r2
+  //   mov r2, r2
+  //   mov r2, r2
+  // to a call to the debug break slot code.
+  // #if USE_BLX
+  //   ldr ip, [pc, #0]
+  //   blx ip
+  // #else
+  //   mov lr, pc
+  //   ldr pc, [pc, #-4]
+  // #endif
+  //   <debug break slot code entry point address>
+  CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
+#ifdef USE_BLX
+  patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
+  patcher.masm()->blx(v8::internal::ip);
+#else
+  patcher.masm()->mov(v8::internal::lr, v8::internal::pc);
+  patcher.masm()->ldr(v8::internal::pc, MemOperand(v8::internal::pc, -4));
+#endif
+  patcher.Emit(Debug::debug_break_return()->entry());
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+  ASSERT(IsDebugBreakSlot());
+  rinfo()->PatchCode(original_rinfo()->pc(),
+                     Assembler::kDebugBreakSlotInstructions);
+}
+
+
 #define __ ACCESS_MASM(masm)
 
 
@@ -220,10 +262,31 @@ void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
 }
 
 
+void Debug::GenerateSlot(MacroAssembler* masm) {
+  // Generate enough nop's to make space for a call instruction.
+  Label check_codesize;
+  __ bind(&check_codesize);
+  __ RecordDebugBreakSlot();
+  for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+    __ nop(2);
+  }
+  ASSERT_EQ(Assembler::kDebugBreakSlotInstructions,
+            masm->InstructionsGeneratedSince(&check_codesize));
+}
+
+
+void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
+  // In the places where a debug break slot is inserted no registers can contain
+  // object pointers.
+  Generate_DebugBreakCallHelper(masm, 0);
+}
+
+
 void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
   masm->Abort("LiveEdit frame dropping is not supported on arm");
 }
 
+
 void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
   masm->Abort("LiveEdit frame dropping is not supported on arm");
 }
index 5fa412ffc955782df60a659b8a4f167eac48d6b5..e2cd84d60b7e37b8f6f7992f41465588ab40a3b4 100644 (file)
@@ -238,8 +238,10 @@ void FullCodeGenerator::EmitReturnSequence(int position) {
     // add instruction the add will generate two instructions.
     int return_sequence_length =
         masm_->InstructionsGeneratedSince(&check_exit_codesize);
-    CHECK(return_sequence_length == Assembler::kJSReturnSequenceLength ||
-          return_sequence_length == Assembler::kJSReturnSequenceLength + 1);
+    CHECK(return_sequence_length ==
+          Assembler::kJSReturnSequenceInstructions ||
+          return_sequence_length ==
+          Assembler::kJSReturnSequenceInstructions + 1);
 #endif
   }
 }
index 871ca86eda24c4ccd66ceed124adcadb478ef3b1..6a46f6159502fb7a8cad04c5ba9f4b12d2f581b9 100644 (file)
@@ -449,6 +449,11 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
       return "external reference";
     case RelocInfo::INTERNAL_REFERENCE:
       return "internal reference";
+    case RelocInfo::DEBUG_BREAK_SLOT:
+#ifndef ENABLE_DEBUGGER_SUPPORT
+      UNREACHABLE();
+#endif
+      return "debug break slot";
     case RelocInfo::NUMBER_OF_MODES:
       UNREACHABLE();
       return "number_of_modes";
@@ -513,6 +518,7 @@ void RelocInfo::Verify() {
     case STATEMENT_POSITION:
     case EXTERNAL_REFERENCE:
     case INTERNAL_REFERENCE:
+    case DEBUG_BREAK_SLOT:
     case NONE:
       break;
     case NUMBER_OF_MODES:
index 74613b34274950acf41337c08e36074638eb888f..fb75d6dc2282f308a5fe15483cd7d3e230bf70b0 100644 (file)
@@ -118,9 +118,9 @@ class RelocInfo BASE_EMBEDDED {
   enum Mode {
     // Please note the order is important (see IsCodeTarget, IsGCRelocMode).
     CONSTRUCT_CALL,  // code target that is a call to a JavaScript constructor.
-    CODE_TARGET_CONTEXT,  // code target used for contextual loads.
-    DEBUG_BREAK,
-    CODE_TARGET,         // code target which is not any of the above.
+    CODE_TARGET_CONTEXT,  // Code target used for contextual loads.
+    DEBUG_BREAK,  // Code target for the debugger statement.
+    CODE_TARGET,  // Code target which is not any of the above.
     EMBEDDED_OBJECT,
 
     // Everything after runtime_entry (inclusive) is not GC'ed.
@@ -129,6 +129,7 @@ class RelocInfo BASE_EMBEDDED {
     COMMENT,
     POSITION,  // See comment for kNoPosition above.
     STATEMENT_POSITION,  // See comment for kNoPosition above.
+    DEBUG_BREAK_SLOT,  // Additional code inserted for debug break slot.
     EXTERNAL_REFERENCE,  // The address of an external C++ function.
     INTERNAL_REFERENCE,  // An address inside the same function.
 
@@ -174,6 +175,9 @@ class RelocInfo BASE_EMBEDDED {
   static inline bool IsInternalReference(Mode mode) {
     return mode == INTERNAL_REFERENCE;
   }
+  static inline bool IsDebugBreakSlot(Mode mode) {
+    return mode == DEBUG_BREAK_SLOT;
+  }
   static inline int ModeMask(Mode mode) { return 1 << mode; }
 
   // Accessors
@@ -243,6 +247,10 @@ class RelocInfo BASE_EMBEDDED {
   // with a call to the debugger.
   INLINE(bool IsPatchedReturnSequence());
 
+  // Check whether this debug break slot has been patched with a call to the
+  // debugger.
+  INLINE(bool IsPatchedDebugBreakSlotSequence());
+
 #ifdef ENABLE_DISASSEMBLER
   // Printing
   static const char* RelocModeName(Mode rmode);
index a3a97341dd34971f899017add3a4e08bca82df22..b9a7a3dd7a39d7d5032f3eea0f6852e6c47813d2 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1469,10 +1469,14 @@ class Conditional: public Expression {
  public:
   Conditional(Expression* condition,
               Expression* then_expression,
-              Expression* else_expression)
+              Expression* else_expression,
+              int then_expression_position,
+              int else_expression_position)
       : condition_(condition),
         then_expression_(then_expression),
-        else_expression_(else_expression) { }
+        else_expression_(else_expression),
+        then_expression_position_(then_expression_position),
+        else_expression_position_(else_expression_position) { }
 
   virtual void Accept(AstVisitor* v);
 
@@ -1482,10 +1486,15 @@ class Conditional: public Expression {
   Expression* then_expression() const { return then_expression_; }
   Expression* else_expression() const { return else_expression_; }
 
+  int then_expression_position() { return then_expression_position_; }
+  int else_expression_position() { return else_expression_position_; }
+
  private:
   Expression* condition_;
   Expression* then_expression_;
   Expression* else_expression_;
+  int then_expression_position_;
+  int else_expression_position_;
 };
 
 
index 0677809d9171c3ae2636dfc00e131460c04121f5..7116dc9042e06809b4fc40ce5c141655acbe3bd5 100644 (file)
@@ -1360,10 +1360,17 @@ static void Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm) {
   Debug::GenerateStubNoRegistersDebugBreak(masm);
 }
 
+
+static void Generate_Slot_DebugBreak(MacroAssembler* masm) {
+  Debug::GenerateSlotDebugBreak(masm);
+}
+
+
 static void Generate_PlainReturn_LiveEdit(MacroAssembler* masm) {
   Debug::GeneratePlainReturnLiveEdit(masm);
 }
 
+
 static void Generate_FrameDropper_LiveEdit(MacroAssembler* masm) {
   Debug::GenerateFrameDropperLiveEdit(masm);
 }
index dd2e3cbfc144959623fb24940bdfab7f029d677e..1fab3754764418c7394b6f56c475bc6d228299c8 100644 (file)
@@ -127,6 +127,7 @@ enum BuiltinExtraArguments {
   V(KeyedLoadIC_DebugBreak,     KEYED_LOAD_IC, DEBUG_BREAK)    \
   V(StoreIC_DebugBreak,         STORE_IC, DEBUG_BREAK)         \
   V(KeyedStoreIC_DebugBreak,    KEYED_STORE_IC, DEBUG_BREAK)   \
+  V(Slot_DebugBreak,            BUILTIN, DEBUG_BREAK)          \
   V(PlainReturn_LiveEdit,       BUILTIN, DEBUG_BREAK)          \
   V(FrameDropper_LiveEdit,      BUILTIN, DEBUG_BREAK)
 #else
index cdcd18ad224401c37b63ddc21ff2e6d8505d4cf4..c2e40ba9a4026f44e201289a4dac2419355abb8a 100644 (file)
@@ -285,14 +285,16 @@ template <int> class StaticAssertionHelper { };
 #define ASSERT_RESULT(expr)  CHECK(expr)
 #define ASSERT(condition)    CHECK(condition)
 #define ASSERT_EQ(v1, v2)    CHECK_EQ(v1, v2)
-#define ASSERT_NE(v1, v2)   CHECK_NE(v1, v2)
+#define ASSERT_NE(v1, v2)    CHECK_NE(v1, v2)
+#define ASSERT_GE(v1, v2)    CHECK_GE(v1, v2)
 #define STATIC_ASSERT(test)  STATIC_CHECK(test)
 #define SLOW_ASSERT(condition) if (FLAG_enable_slow_asserts) CHECK(condition)
 #else
 #define ASSERT_RESULT(expr)     (expr)
 #define ASSERT(condition)      ((void) 0)
 #define ASSERT_EQ(v1, v2)      ((void) 0)
-#define ASSERT_NE(v1, v2)     ((void) 0)
+#define ASSERT_NE(v1, v2)      ((void) 0)
+#define ASSERT_GE(v1, v2)      ((void) 0)
 #define STATIC_ASSERT(test)    ((void) 0)
 #define SLOW_ASSERT(condition) ((void) 0)
 #endif
index ff2fa0395b0ee93e22fcaf371f06cf8d55771e4e..686e173e55b088690a681f236366a00209adf052 100644 (file)
@@ -415,32 +415,41 @@ CodeGenerator::ConditionAnalysis CodeGenerator::AnalyzeCondition(
 }
 
 
-void CodeGenerator::RecordPositions(MacroAssembler* masm, int pos) {
+bool CodeGenerator::RecordPositions(MacroAssembler* masm,
+                                    int pos,
+                                    bool right_here) {
   if (pos != RelocInfo::kNoPosition) {
     masm->RecordStatementPosition(pos);
     masm->RecordPosition(pos);
+    if (right_here) {
+      return masm->WriteRecordedPositions();
+    }
   }
+  return false;
 }
 
 
 void CodeGenerator::CodeForFunctionPosition(FunctionLiteral* fun) {
-  if (FLAG_debug_info) RecordPositions(masm(), fun->start_position());
+  if (FLAG_debug_info) RecordPositions(masm(), fun->start_position(), false);
 }
 
 
 void CodeGenerator::CodeForReturnPosition(FunctionLiteral* fun) {
-  if (FLAG_debug_info) RecordPositions(masm(), fun->end_position());
+  if (FLAG_debug_info) RecordPositions(masm(), fun->end_position(), false);
 }
 
 
 void CodeGenerator::CodeForStatementPosition(Statement* stmt) {
-  if (FLAG_debug_info) RecordPositions(masm(), stmt->statement_pos());
+  if (FLAG_debug_info) RecordPositions(masm(), stmt->statement_pos(), false);
 }
 
+
 void CodeGenerator::CodeForDoWhileConditionPosition(DoWhileStatement* stmt) {
-  if (FLAG_debug_info) RecordPositions(masm(), stmt->condition_position());
+  if (FLAG_debug_info)
+    RecordPositions(masm(), stmt->condition_position(), false);
 }
 
+
 void CodeGenerator::CodeForSourcePosition(int pos) {
   if (FLAG_debug_info && pos != RelocInfo::kNoPosition) {
     masm()->RecordPosition(pos);
index a69320a2c6ffc4d88b60b825cfeb080da9eff19f..7fd7925baab095f2900ebee8dfe108523325cd08 100644 (file)
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -576,6 +576,9 @@ Handle<String> Shell::ReadFile(const char* name) {
 void Shell::RunShell() {
   LineEditor* editor = LineEditor::Get();
   printf("V8 version %s [console: %s]\n", V8::GetVersion(), editor->name());
+  if (i::FLAG_debugger) {
+    printf("JavaScript debugger enabled\n");
+  }
   editor->Open();
   while (true) {
     Locker locker;
index e279ee9eff5bf45aaaa3d71b2cf0562a9151469c..ca20a65b65200759cd065773365a04f776e30dfb 100644 (file)
@@ -129,10 +129,14 @@ void BreakLocationIterator::Next() {
       ASSERT(statement_position_ >= 0);
     }
 
-    // Check for breakable code target. Look in the original code as setting
-    // break points can cause the code targets in the running (debugged) code to
-    // be of a different kind than in the original code.
-    if (RelocInfo::IsCodeTarget(rmode())) {
+    if (IsDebugBreakSlot()) {
+      // There is always a possible break point at a debug break slot.
+      break_point_++;
+      return;
+    } else if (RelocInfo::IsCodeTarget(rmode())) {
+      // Check for breakable code target. Look in the original code as setting
+      // break points can cause the code targets in the running (debugged) code
+      // to be of a different kind than in the original code.
       Address target = original_rinfo()->target_address();
       Code* code = Code::GetCodeFromTargetAddress(target);
       if ((code->is_inline_cache_stub() &&
@@ -329,6 +333,9 @@ void BreakLocationIterator::SetDebugBreak() {
   if (RelocInfo::IsJSReturn(rmode())) {
     // Patch the frame exit code with a break point.
     SetDebugBreakAtReturn();
+  } else if (IsDebugBreakSlot()) {
+    // Patch the code in the break slot.
+    SetDebugBreakAtSlot();
   } else {
     // Patch the IC call.
     SetDebugBreakAtIC();
@@ -346,6 +353,9 @@ void BreakLocationIterator::ClearDebugBreak() {
   if (RelocInfo::IsJSReturn(rmode())) {
     // Restore the frame exit code.
     ClearDebugBreakAtReturn();
+  } else if (IsDebugBreakSlot()) {
+    // Restore the code in the break slot.
+    ClearDebugBreakAtSlot();
   } else {
     // Patch the IC call.
     ClearDebugBreakAtIC();
@@ -417,6 +427,8 @@ bool BreakLocationIterator::HasBreakPoint() {
 bool BreakLocationIterator::IsDebugBreak() {
   if (RelocInfo::IsJSReturn(rmode())) {
     return IsDebugBreakAtReturn();
+  } else if (IsDebugBreakSlot()) {
+    return IsDebugBreakAtSlot();
   } else {
     return Debug::IsDebugBreak(rinfo()->target_address());
   }
@@ -478,6 +490,11 @@ bool BreakLocationIterator::IsDebuggerStatement() {
 }
 
 
+bool BreakLocationIterator::IsDebugBreakSlot() {
+  return RelocInfo::DEBUG_BREAK_SLOT == rmode();
+}
+
+
 Object* BreakLocationIterator::BreakPointObjects() {
   return debug_info_->GetBreakPointObjects(code_position());
 }
@@ -573,6 +590,7 @@ bool Debug::break_on_uncaught_exception_ = true;
 
 Handle<Context> Debug::debug_context_ = Handle<Context>();
 Code* Debug::debug_break_return_ = NULL;
+Code* Debug::debug_break_slot_ = NULL;
 
 
 void ScriptCache::Add(Handle<Script> script) {
@@ -656,6 +674,10 @@ void Debug::Setup(bool create_heap_objects) {
     debug_break_return_ =
         Builtins::builtin(Builtins::Return_DebugBreak);
     ASSERT(debug_break_return_->IsCode());
+    // Get code to handle debug break in debug break slots.
+    debug_break_slot_ =
+        Builtins::builtin(Builtins::Slot_DebugBreak);
+    ASSERT(debug_break_slot_->IsCode());
   }
 }
 
@@ -824,6 +846,7 @@ void Debug::PreemptionWhileInDebugger() {
 
 void Debug::Iterate(ObjectVisitor* v) {
   v->VisitPointer(BitCast<Object**, Code**>(&(debug_break_return_)));
+  v->VisitPointer(BitCast<Object**, Code**>(&(debug_break_slot_)));
 }
 
 
@@ -1631,16 +1654,21 @@ void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
   // break point is still active after processing the break point.
   Address addr = frame->pc() - Assembler::kCallTargetAddressOffset;
 
-  // Check if the location is at JS exit.
+  // Check if the location is at JS exit or debug break slot.
   bool at_js_return = false;
   bool break_at_js_return_active = false;
+  bool at_debug_break_slot = false;
   RelocIterator it(debug_info->code());
-  while (!it.done()) {
+  while (!it.done() && !at_js_return && !at_debug_break_slot) {
     if (RelocInfo::IsJSReturn(it.rinfo()->rmode())) {
       at_js_return = (it.rinfo()->pc() ==
           addr - Assembler::kPatchReturnSequenceAddressOffset);
       break_at_js_return_active = it.rinfo()->IsPatchedReturnSequence();
     }
+    if (RelocInfo::IsDebugBreakSlot(it.rinfo()->rmode())) {
+      at_debug_break_slot = (it.rinfo()->pc() ==
+          addr - Assembler::kPatchDebugBreakSlotAddressOffset);
+    }
     it.next();
   }
 
@@ -1657,25 +1685,30 @@ void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
     // Move back to where the call instruction sequence started.
     thread_local_.after_break_target_ =
         addr - Assembler::kPatchReturnSequenceAddressOffset;
-  } else {
-    // Check if there still is a debug break call at the target address. If the
-    // break point has been removed it will have disappeared. If it have
-    // disappeared don't try to look in the original code as the running code
-    // will have the right address. This takes care of the case where the last
-    // break point is removed from the function and therefore no "original code"
-    // is available. If the debug break call is still there find the address in
-    // the original code.
-    if (IsDebugBreak(Assembler::target_address_at(addr))) {
-      // If the break point is still there find the call address which was
-      // overwritten in the original code by the call to DebugBreakXXX.
-
-      // Find the corresponding address in the original code.
-      addr += original_code->instruction_start() - code->instruction_start();
-    }
+  } else if (at_debug_break_slot) {
+    // Address of where the debug break slot starts.
+    addr = addr - Assembler::kPatchDebugBreakSlotAddressOffset;
+
+    // Continue just after the slot.
+    thread_local_.after_break_target_ = addr + Assembler::kDebugBreakSlotLength;
+  } else if (IsDebugBreak(Assembler::target_address_at(addr))) {
+    // We now know that there is still a debug break call at the target address,
+    // so the break point is still there and the original code will hold the
+    // address to jump to in order to complete the call which is replaced by a
+    // call to DebugBreakXXX.
+
+    // Find the corresponding address in the original code.
+    addr += original_code->instruction_start() - code->instruction_start();
 
     // Install jump to the call address in the original code. This will be the
     // call which was overwritten by the call to DebugBreakXXX.
     thread_local_.after_break_target_ = Assembler::target_address_at(addr);
+  } else {
+    // There is no longer a break point present. Don't try to look in the
+    // original code as the running code will have the right address. This takes
+    // care of the case where the last break point is removed from the function
+    // and therefore no "original code" is available.
+    thread_local_.after_break_target_ = Assembler::target_address_at(addr);
   }
 }
 
index e2eecb8bfbc1bc6d9be69ae3a5fc3150136ccbf5..0e12c76f05c9fc9475f3c593f64a32e484f48130 100644 (file)
@@ -146,6 +146,11 @@ class BreakLocationIterator {
   void SetDebugBreakAtReturn();
   void ClearDebugBreakAtReturn();
 
+  bool IsDebugBreakSlot();
+  bool IsDebugBreakAtSlot();
+  void SetDebugBreakAtSlot();
+  void ClearDebugBreakAtSlot();
+
   DISALLOW_COPY_AND_ASSIGN(BreakLocationIterator);
 };
 
@@ -323,6 +328,7 @@ class Debug {
   enum AddressId {
     k_after_break_target_address,
     k_debug_break_return_address,
+    k_debug_break_slot_address,
     k_register_address
   };
 
@@ -342,6 +348,12 @@ class Debug {
     return &debug_break_return_;
   }
 
+  // Access to the debug break in debug break slot code.
+  static Code* debug_break_slot() { return debug_break_slot_; }
+  static Code** debug_break_slot_address() {
+    return &debug_break_slot_;
+  }
+
   static const int kEstimatedNofDebugInfoEntries = 16;
   static const int kEstimatedNofBreakPointsInFunction = 16;
 
@@ -370,6 +382,7 @@ class Debug {
   static void AfterGarbageCollection();
 
   // Code generator routines.
+  static void GenerateSlot(MacroAssembler* masm);
   static void GenerateLoadICDebugBreak(MacroAssembler* masm);
   static void GenerateStoreICDebugBreak(MacroAssembler* masm);
   static void GenerateKeyedLoadICDebugBreak(MacroAssembler* masm);
@@ -377,6 +390,7 @@ class Debug {
   static void GenerateConstructCallDebugBreak(MacroAssembler* masm);
   static void GenerateReturnDebugBreak(MacroAssembler* masm);
   static void GenerateStubNoRegistersDebugBreak(MacroAssembler* masm);
+  static void GenerateSlotDebugBreak(MacroAssembler* masm);
   static void GeneratePlainReturnLiveEdit(MacroAssembler* masm);
   static void GenerateFrameDropperLiveEdit(MacroAssembler* masm);
 
@@ -472,6 +486,9 @@ class Debug {
   // Code to call for handling debug break on return.
   static Code* debug_break_return_;
 
+  // Code to call for handling debug break in debug break slots.
+  static Code* debug_break_slot_;
+
   DISALLOW_COPY_AND_ASSIGN(Debug);
 };
 
@@ -895,6 +912,8 @@ class Debug_Address {
         return reinterpret_cast<Address>(Debug::after_break_target_address());
       case Debug::k_debug_break_return_address:
         return reinterpret_cast<Address>(Debug::debug_break_return_address());
+      case Debug::k_debug_break_slot_address:
+        return reinterpret_cast<Address>(Debug::debug_break_slot_address());
       case Debug::k_register_address:
         return reinterpret_cast<Address>(Debug::register_address(reg_));
       default:
index 0e6dd880c2eea4ce0cddb20699324e8f82cb22e8..dc8efcc1067cc9b595ae05667a0c309e9ea23666 100644 (file)
@@ -277,7 +277,7 @@ DEFINE_string(testing_serialization_file, "/tmp/serdes",
 
 DEFINE_bool(help, false, "Print usage message, including flags, on console")
 DEFINE_bool(dump_counters, false, "Dump counters on exit")
-DEFINE_bool(debugger, true, "Enable JavaScript debugger")
+DEFINE_bool(debugger, false, "Enable JavaScript debugger")
 DEFINE_bool(remote_debugger, false, "Connect JavaScript debugger to the "
                                     "debugger agent in another process")
 DEFINE_bool(debugger_agent, false, "Enable debugger agent")
index f7a063aed8b9c1e65ae27178ad5abad0760b37d2..6316158cd94d2dfb85972c2a19eca283e6b1e0f1 100644 (file)
@@ -439,6 +439,231 @@ void FullCodeGenSyntaxChecker::VisitThisFunction(ThisFunction* expr) {
 #undef CHECK_BAILOUT
 
 
+void BreakableStatementChecker::Check(Statement* stmt) {
+  Visit(stmt);
+}
+
+
+void BreakableStatementChecker::Check(Expression* expr) {
+  Visit(expr);
+}
+
+
+void BreakableStatementChecker::VisitDeclaration(Declaration* decl) {
+}
+
+
+void BreakableStatementChecker::VisitBlock(Block* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitExpressionStatement(
+    ExpressionStatement* stmt) {
+  // Check if expression is breakable.
+  Visit(stmt->expression());
+}
+
+
+void BreakableStatementChecker::VisitEmptyStatement(EmptyStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitIfStatement(IfStatement* stmt) {
+  // If the condition is breakable the if statement is breakable.
+  Visit(stmt->condition());
+}
+
+
+void BreakableStatementChecker::VisitContinueStatement(
+    ContinueStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitBreakStatement(BreakStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitReturnStatement(ReturnStatement* stmt) {
+  // Return is breakable if the expression is.
+  Visit(stmt->expression());
+}
+
+
+void BreakableStatementChecker::VisitWithEnterStatement(
+    WithEnterStatement* stmt) {
+  Visit(stmt->expression());
+}
+
+
+void BreakableStatementChecker::VisitWithExitStatement(
+    WithExitStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitSwitchStatement(SwitchStatement* stmt) {
+  // Switch statements breakable if the tag expression is.
+  Visit(stmt->tag());
+}
+
+
+void BreakableStatementChecker::VisitDoWhileStatement(DoWhileStatement* stmt) {
+  // Mark do while as breakable to avoid adding a break slot in front of it.
+  is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitWhileStatement(WhileStatement* stmt) {
+  // Mark while statements breakable if the condition expression is.
+  Visit(stmt->cond());
+}
+
+
+void BreakableStatementChecker::VisitForStatement(ForStatement* stmt) {
+  // Mark for statements breakable if the condition expression is.
+  if (stmt->cond() != NULL) {
+    Visit(stmt->cond());
+  }
+}
+
+
+void BreakableStatementChecker::VisitForInStatement(ForInStatement* stmt) {
+  // Mark for in statements breakable if the enumerable expression is.
+  Visit(stmt->enumerable());
+}
+
+
+void BreakableStatementChecker::VisitTryCatchStatement(
+    TryCatchStatement* stmt) {
+  // Mark try catch as breakable to avoid adding a break slot in front of it.
+  is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitTryFinallyStatement(
+    TryFinallyStatement* stmt) {
+  // Mark try finally as breakable to avoid adding a break slot in front of it.
+  is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitDebuggerStatement(
+    DebuggerStatement* stmt) {
+  // The debugger statement is breakable.
+  is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitFunctionLiteral(FunctionLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitSharedFunctionInfoLiteral(
+    SharedFunctionInfoLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitConditional(Conditional* expr) {
+}
+
+
+void BreakableStatementChecker::VisitSlot(Slot* expr) {
+}
+
+
+void BreakableStatementChecker::VisitVariableProxy(VariableProxy* expr) {
+}
+
+
+void BreakableStatementChecker::VisitLiteral(Literal* expr) {
+}
+
+
+void BreakableStatementChecker::VisitRegExpLiteral(RegExpLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitObjectLiteral(ObjectLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitArrayLiteral(ArrayLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitCatchExtensionObject(
+    CatchExtensionObject* expr) {
+}
+
+
+void BreakableStatementChecker::VisitAssignment(Assignment* expr) {
+  // If assigning to a property (including a global property) the assignment is
+  // breakable.
+  Variable* var = expr->target()->AsVariableProxy()->AsVariable();
+  Property* prop = expr->target()->AsProperty();
+  if (prop != NULL || (var != NULL && var->is_global())) {
+    is_breakable_ = true;
+    return;
+  }
+
+  // Otherwise the assignment is breakable if the assigned value is.
+  Visit(expr->value());
+}
+
+
+void BreakableStatementChecker::VisitThrow(Throw* expr) {
+  // Throw is breakable if the expression is.
+  Visit(expr->exception());
+}
+
+
+void BreakableStatementChecker::VisitProperty(Property* expr) {
+  // Property load is breakable.
+  is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitCall(Call* expr) {
+  // Function calls both through IC and call stub are breakable.
+  is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitCallNew(CallNew* expr) {
+  // Function calls through new are breakable.
+  is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitCallRuntime(CallRuntime* expr) {
+}
+
+
+void BreakableStatementChecker::VisitUnaryOperation(UnaryOperation* expr) {
+  Visit(expr->expression());
+}
+
+
+void BreakableStatementChecker::VisitCountOperation(CountOperation* expr) {
+  Visit(expr->expression());
+}
+
+
+void BreakableStatementChecker::VisitBinaryOperation(BinaryOperation* expr) {
+  Visit(expr->left());
+  Visit(expr->right());
+}
+
+
+void BreakableStatementChecker::VisitCompareOperation(CompareOperation* expr) {
+  Visit(expr->left());
+  Visit(expr->right());
+}
+
+
+void BreakableStatementChecker::VisitThisFunction(ThisFunction* expr) {
+}
+
+
 #define __ ACCESS_MASM(masm())
 
 Handle<Code> FullCodeGenerator::MakeCode(CompilationInfo* info) {
@@ -552,7 +777,60 @@ void FullCodeGenerator::SetReturnPosition(FunctionLiteral* fun) {
 
 void FullCodeGenerator::SetStatementPosition(Statement* stmt) {
   if (FLAG_debug_info) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+    if (!Debugger::IsDebuggerActive()) {
+      CodeGenerator::RecordPositions(masm_, stmt->statement_pos());
+    } else {
+      // Check if the statement will be breakable without adding a debug break
+      // slot.
+      BreakableStatementChecker checker;
+      checker.Check(stmt);
+      // Record the statement position right here if the statement is not
+      // breakable. For breakable statements the actual recording of the
+      // position will be postponed to the breakable code (typically an IC).
+      bool position_recorded = CodeGenerator::RecordPositions(
+          masm_, stmt->statement_pos(), !checker.is_breakable());
+      // If the position recording did record a new position generate a debug
+      // break slot to make the statement breakable.
+      if (position_recorded) {
+        Debug::GenerateSlot(masm_);
+      }
+    }
+#else
     CodeGenerator::RecordPositions(masm_, stmt->statement_pos());
+#endif
+  }
+}
+
+
+void FullCodeGenerator::SetExpressionPosition(Expression* expr, int pos) {
+  if (FLAG_debug_info) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+    if (!Debugger::IsDebuggerActive()) {
+      CodeGenerator::RecordPositions(masm_, pos);
+    } else {
+      // Check if the expression will be breakable without adding a debug break
+      // slot.
+      BreakableStatementChecker checker;
+      checker.Check(expr);
+      // Record a statement position right here if the expression is not
+      // breakable. For breakable expressions the actual recording of the
+      // position will be postponed to the breakable code (typically an IC).
+      // NOTE this will record a statement position for something which might
+      // not be a statement. As stepping in the debugger will only stop at
+      // statement positions this is used for e.g. the condition expression of
+      // a do while loop.
+      bool position_recorded = CodeGenerator::RecordPositions(
+          masm_, pos, !checker.is_breakable());
+      // If the position recording did record a new position generate a debug
+      // break slot to make the statement breakable.
+      if (position_recorded) {
+        Debug::GenerateSlot(masm_);
+      }
+    }
+#else
+    CodeGenerator::RecordPositions(masm_, pos);
+#endif
   }
 }
 
@@ -848,7 +1126,11 @@ void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
   __ bind(&stack_check_success);
 
   __ bind(loop_statement.continue_target());
-  SetStatementPosition(stmt->condition_position());
+
+  // Record the position of the do while condition and make sure it is possible
+  // to break on the condition.
+  SetExpressionPosition(stmt->cond(), stmt->condition_position());
+
   VisitForControl(stmt->cond(), &body, loop_statement.break_target());
 
   __ bind(&stack_limit_hit);
@@ -864,7 +1146,6 @@ void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
 
 void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
   Comment cmnt(masm_, "[ WhileStatement");
-  SetStatementPosition(stmt);
   Label body, stack_limit_hit, stack_check_success;
 
   Iteration loop_statement(this, stmt);
@@ -877,6 +1158,9 @@ void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
   Visit(stmt->body());
 
   __ bind(loop_statement.continue_target());
+  // Emit the statement position here as this is where the while statement code
+  // starts.
+  SetStatementPosition(stmt);
 
   // Check stack before looping.
   __ StackLimitCheck(&stack_limit_hit);
@@ -896,7 +1180,6 @@ void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
 
 void FullCodeGenerator::VisitForStatement(ForStatement* stmt) {
   Comment cmnt(masm_, "[ ForStatement");
-  SetStatementPosition(stmt);
   Label test, body, stack_limit_hit, stack_check_success;
 
   Iteration loop_statement(this, stmt);
@@ -919,6 +1202,9 @@ void FullCodeGenerator::VisitForStatement(ForStatement* stmt) {
   }
 
   __ bind(&test);
+  // Emit the statement position here as this is where the for statement code
+  // starts.
+  SetStatementPosition(stmt);
 
   // Check stack before looping.
   __ StackLimitCheck(&stack_limit_hit);
@@ -1064,6 +1350,8 @@ void FullCodeGenerator::VisitConditional(Conditional* expr) {
   VisitForControl(expr->condition(), &true_case, &false_case);
 
   __ bind(&true_case);
+  SetExpressionPosition(expr->then_expression(),
+                        expr->then_expression_position());
   Visit(expr->then_expression());
   // If control flow falls through Visit, jump to done.
   if (context_ == Expression::kEffect || context_ == Expression::kValue) {
@@ -1071,6 +1359,8 @@ void FullCodeGenerator::VisitConditional(Conditional* expr) {
   }
 
   __ bind(&false_case);
+  SetExpressionPosition(expr->else_expression(),
+                        expr->else_expression_position());
   Visit(expr->else_expression());
   // If control flow falls through Visit, merge it with true case here.
   if (context_ == Expression::kEffect || context_ == Expression::kValue) {
index ac70d9986eaf302140fc08a8b3b68a22455bf899..0ce600b9f640f6e5c45811a4e73bb1ee6a3622b4 100644 (file)
@@ -59,6 +59,31 @@ class FullCodeGenSyntaxChecker: public AstVisitor {
 };
 
 
+// AST node visitor which can tell whether a given statement will be breakable
+// when the code is compiled by the full compiler in the debugger. This means
+// that there will be an IC (load/store/call) in the code generated for the
+// debugger to piggybag on.
+class BreakableStatementChecker: public AstVisitor {
+ public:
+  BreakableStatementChecker() : is_breakable_(false) {}
+
+  void Check(Statement* stmt);
+  void Check(Expression* stmt);
+
+  bool is_breakable() { return is_breakable_; }
+
+ private:
+  // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+  AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+  bool is_breakable_;
+
+  DISALLOW_COPY_AND_ASSIGN(BreakableStatementChecker);
+};
+
+
 // -----------------------------------------------------------------------------
 // Full code generator.
 
@@ -458,6 +483,7 @@ class FullCodeGenerator: public AstVisitor {
   void SetFunctionPosition(FunctionLiteral* fun);
   void SetReturnPosition(FunctionLiteral* fun);
   void SetStatementPosition(Statement* stmt);
+  void SetExpressionPosition(Expression* expr, int pos);
   void SetStatementPosition(int pos);
   void SetSourcePosition(int pos);
 
index 9c96e1977cd73106a4019d6d0078c2b0ab37496f..3b259af2d451249bc3b0f8e5135c9f7214915317 100644 (file)
@@ -52,16 +52,21 @@ Condition NegateCondition(Condition cc) {
 void RelocInfo::apply(intptr_t delta) {
   if (rmode_ == RUNTIME_ENTRY || IsCodeTarget(rmode_)) {
     int32_t* p = reinterpret_cast<int32_t*>(pc_);
-    *p -= delta;  // relocate entry
+    *p -= delta;  // Relocate entry.
   } else if (rmode_ == JS_RETURN && IsPatchedReturnSequence()) {
     // Special handling of js_return when a break point is set (call
     // instruction has been inserted).
     int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
-    *p -= delta;  // relocate entry
+    *p -= delta;  // Relocate entry.
+  } else if (rmode_ == DEBUG_BREAK_SLOT && IsPatchedDebugBreakSlotSequence()) {
+    // Special handling of a debug break slot when a break point is set (call
+    // instruction has been inserted).
+    int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
+    *p -= delta;  // Relocate entry.
   } else if (IsInternalReference(rmode_)) {
     // absolute code pointer inside code object moves with the code object.
     int32_t* p = reinterpret_cast<int32_t*>(pc_);
-    *p += delta;  // relocate entry
+    *p += delta;  // Relocate entry.
   }
 }
 
@@ -154,6 +159,11 @@ bool RelocInfo::IsPatchedReturnSequence() {
 }
 
 
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
+  return !Assembler::IsNop(pc());
+}
+
+
 void RelocInfo::Visit(ObjectVisitor* visitor) {
   RelocInfo::Mode mode = rmode();
   if (mode == RelocInfo::EMBEDDED_OBJECT) {
@@ -164,8 +174,10 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
     visitor->VisitExternalReference(target_reference_address());
 #ifdef ENABLE_DEBUGGER_SUPPORT
   } else if (Debug::has_break_points() &&
-             RelocInfo::IsJSReturn(mode) &&
-             IsPatchedReturnSequence()) {
+             (RelocInfo::IsJSReturn(mode) &&
+              IsPatchedReturnSequence()) ||
+             (RelocInfo::IsDebugBreakSlot(mode) &&
+              IsPatchedDebugBreakSlotSequence())) {
     visitor->VisitDebugTarget(this);
 #endif
   } else if (mode == RelocInfo::RUNTIME_ENTRY) {
index a436827e1c511f3dba2ea2092fd23cabf5bb91b5..d4dff330447843261cd076bebbb5144954f1ef09 100644 (file)
@@ -206,6 +206,7 @@ void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
             patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
 
   // Add the requested number of int3 instructions after the call.
+  ASSERT_GE(guard_bytes, 0);
   for (int i = 0; i < guard_bytes; i++) {
     patcher.masm()->int3();
   }
@@ -2371,6 +2372,13 @@ void Assembler::RecordJSReturn() {
 }
 
 
+void Assembler::RecordDebugBreakSlot() {
+  WriteRecordedPositions();
+  EnsureSpace ensure_space(this);
+  RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
+}
+
+
 void Assembler::RecordComment(const char* msg) {
   if (FLAG_debug_code) {
     EnsureSpace ensure_space(this);
@@ -2393,13 +2401,16 @@ void Assembler::RecordStatementPosition(int pos) {
 }
 
 
-void Assembler::WriteRecordedPositions() {
+bool Assembler::WriteRecordedPositions() {
+  bool written = false;
+
   // Write the statement position if it is different from what was written last
   // time.
   if (current_statement_position_ != written_statement_position_) {
     EnsureSpace ensure_space(this);
     RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
     written_statement_position_ = current_statement_position_;
+    written = true;
   }
 
   // Write the position if it is different from what was written last time and
@@ -2409,7 +2420,11 @@ void Assembler::WriteRecordedPositions() {
     EnsureSpace ensure_space(this);
     RecordRelocInfo(RelocInfo::POSITION, current_position_);
     written_position_ = current_position_;
+    written = true;
   }
+
+  // Return whether something was written.
+  return written;
 }
 
 
index 76dfe7cfbbddc3a01c0aed7bc5e4e547c6ef090e..7dcbab5cdb0cfcefe7e38c4049298553bdef1794 100644 (file)
@@ -468,9 +468,16 @@ class Assembler : public Malloced {
   // to jump to.
   static const int kPatchReturnSequenceAddressOffset = 1;  // JMP imm32.
 
+  // Distance between start of patched debug break slot and the emitted address
+  // to jump to.
+  static const int kPatchDebugBreakSlotAddressOffset = 1;  // JMP imm32.
+
   static const int kCallInstructionLength = 5;
   static const int kJSReturnSequenceLength = 6;
 
+  // The debug break slot must be able to contain a call instruction.
+  static const int kDebugBreakSlotLength = kCallInstructionLength;
+
   // ---------------------------------------------------------------------------
   // Code generation
   //
@@ -809,13 +816,16 @@ class Assembler : public Malloced {
   // Mark address of the ExitJSFrame code.
   void RecordJSReturn();
 
+  // Mark address of a debug break slot.
+  void RecordDebugBreakSlot();
+
   // Record a comment relocation entry that can be used by a disassembler.
   // Use --debug_code to enable.
   void RecordComment(const char* msg);
 
   void RecordPosition(int pos);
   void RecordStatementPosition(int pos);
-  void WriteRecordedPositions();
+  bool WriteRecordedPositions();
 
   // Writes a single word of data in the code stream.
   // Used for inline tables, e.g., jump-tables.
@@ -833,6 +843,8 @@ class Assembler : public Malloced {
   // Get the number of bytes available in the buffer.
   inline int available_space() const { return reloc_info_writer.pos() - pc_; }
 
+  static bool IsNop(Address addr) { return *addr == 0x90; }
+
   // Avoid overflows for displacements etc.
   static const int kMaximalBufferSize = 512*MB;
   static const int kMinimalBufferSize = 4*KB;
index b3ddad9d9aba4ad1883e01f85c93ba9e1501bd75..180b9a6bdf1b7dfe5563ef142f854dd69398d9a9 100644 (file)
@@ -316,7 +316,9 @@ class CodeGenerator: public AstVisitor {
   static bool ShouldGenerateLog(Expression* type);
 #endif
 
-  static void RecordPositions(MacroAssembler* masm, int pos);
+  static bool RecordPositions(MacroAssembler* masm,
+                              int pos,
+                              bool right_here = false);
 
   // Accessors
   MacroAssembler* masm() { return masm_; }
index 9780f3b09e6b794cfc5ab377098430ca5b20b07a..9b558bd90b5af27d8a97cbe085199916fad486a1 100644 (file)
@@ -69,6 +69,27 @@ bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
 }
 
 
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+  ASSERT(IsDebugBreakSlot());
+  // Check whether the debug break slot instructions have been patched.
+  return rinfo()->IsPatchedDebugBreakSlotSequence();
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+  ASSERT(IsDebugBreakSlot());
+  rinfo()->PatchCodeWithCall(
+      Debug::debug_break_slot()->entry(),
+      Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+  ASSERT(IsDebugBreakSlot());
+  rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
+}
+
+
 #define __ ACCESS_MASM(masm)
 
 
@@ -208,10 +229,31 @@ void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
 }
 
 
+void Debug::GenerateSlot(MacroAssembler* masm) {
+  // Generate enough nop's to make space for a call instruction.
+  Label check_codesize;
+  __ bind(&check_codesize);
+  __ RecordDebugBreakSlot();
+  for (int i = 0; i < Assembler::kDebugBreakSlotLength; i++) {
+    __ nop();
+  }
+  ASSERT_EQ(Assembler::kDebugBreakSlotLength,
+            masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
+  // In the places where a debug break slot is inserted no registers can contain
+  // object pointers.
+  Generate_DebugBreakCallHelper(masm, 0, true);
+}
+
+
 void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
   masm->ret(0);
 }
 
+
 // FrameDropper is a code replacement for a JavaScript frame with possibly
 // several frames above.
 // There is no calling conventions here, because it never actually gets called,
index 0dc3e0f0196abf4e32fe405ac68e68958cf97572..95afb4abe25906f8b96937997dd505bc67fdd8de 100644 (file)
@@ -273,8 +273,10 @@ class MarkingVisitor : public ObjectVisitor {
   }
 
   void VisitDebugTarget(RelocInfo* rinfo) {
-    ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
-           rinfo->IsPatchedReturnSequence());
+    ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
+            rinfo->IsPatchedReturnSequence()) ||
+           (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+            rinfo->IsPatchedDebugBreakSlotSequence()));
     HeapObject* code = Code::GetCodeFromTargetAddress(rinfo->call_address());
     MarkCompactCollector::MarkObject(code);
   }
@@ -1106,8 +1108,10 @@ class PointersToNewGenUpdatingVisitor: public ObjectVisitor {
   }
 
   void VisitDebugTarget(RelocInfo* rinfo) {
-    ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
-           rinfo->IsPatchedReturnSequence());
+    ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
+            rinfo->IsPatchedReturnSequence()) ||
+           (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+            rinfo->IsPatchedDebugBreakSlotSequence()));
     Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
     VisitPointer(&target);
     rinfo->set_call_address(Code::cast(target)->instruction_start());
@@ -1856,8 +1860,10 @@ class UpdatingVisitor: public ObjectVisitor {
   }
 
   void VisitDebugTarget(RelocInfo* rinfo) {
-    ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
-           rinfo->IsPatchedReturnSequence());
+    ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
+            rinfo->IsPatchedReturnSequence()) ||
+           (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+            rinfo->IsPatchedDebugBreakSlotSequence()));
     Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
     VisitPointer(&target);
     rinfo->set_call_address(
index d9617dc7d34d4361a959e774e1e1bfebedbd54c8..a3b316b14c4d84a9030f13593abb42f1da87fbe7 100644 (file)
@@ -1046,13 +1046,16 @@ void Assembler::RecordStatementPosition(int pos) {
 }
 
 
-void Assembler::WriteRecordedPositions() {
+bool Assembler::WriteRecordedPositions() {
+  bool written = false;
+
   // Write the statement position if it is different from what was written last
   // time.
   if (current_statement_position_ != written_statement_position_) {
     CheckBuffer();
     RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
     written_statement_position_ = current_statement_position_;
+    written = true;
   }
 
   // Write the position if it is different from what was written last time and
@@ -1062,7 +1065,11 @@ void Assembler::WriteRecordedPositions() {
     CheckBuffer();
     RecordRelocInfo(RelocInfo::POSITION, current_position_);
     written_position_ = current_position_;
+    written = true;
   }
+
+  // Return whether something was written.
+  return written;
 }
 
 
index cc730f2b39a29d7f527ff0e1d0683db522d49858..b7c3ebcbc01a2e034d4c70315096dc0e981c4ea9 100644 (file)
@@ -355,6 +355,9 @@ class Assembler : public Malloced {
   // to jump to.
   static const int kPatchReturnSequenceAddressOffset = kInstrSize;
 
+  // Distance between start of patched debug break slot and the emitted address
+  // to jump to.
+  static const int kPatchDebugBreakSlotAddressOffset = kInstrSize;
 
   // ---------------------------------------------------------------------------
   // Code generation.
@@ -518,7 +521,7 @@ class Assembler : public Malloced {
 
   void RecordPosition(int pos);
   void RecordStatementPosition(int pos);
-  void WriteRecordedPositions();
+  bool WriteRecordedPositions();
 
   int32_t pc_offset() const { return pc_ - buffer_; }
   int32_t current_position() const { return current_position_; }
index dffacf7bffeab08b44b7e244545a5d75ffa3a8d3..1e4d4a4c14d8550301f5ff09b9ed6a3b0c9e6fff 100644 (file)
@@ -5264,8 +5264,10 @@ void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
 
 
 void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
-  ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
-         rinfo->IsPatchedReturnSequence());
+  ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
+          rinfo->IsPatchedReturnSequence()) ||
+         (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+          rinfo->IsPatchedDebugBreakSlotSequence()));
   Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
   Object* old_target = target;
   VisitPointer(&target);
@@ -5278,6 +5280,7 @@ void Code::CodeIterateBody(ObjectVisitor* v) {
                   RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
                   RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
                   RelocInfo::ModeMask(RelocInfo::JS_RETURN) |
+                  RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) |
                   RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
 
   for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
index bbf71bcad4dd7b7ceb61f898c25049a81f95f506..31bac9184f95f6ba16081736dc79e33d5db1683e 100644 (file)
@@ -2867,10 +2867,13 @@ Expression* Parser::ParseConditionalExpression(bool accept_IN, bool* ok) {
   // In parsing the first assignment expression in conditional
   // expressions we always accept the 'in' keyword; see ECMA-262,
   // section 11.12, page 58.
+  int left_position = scanner().peek_location().beg_pos;
   Expression* left = ParseAssignmentExpression(true, CHECK_OK);
   Expect(Token::COLON, CHECK_OK);
+  int right_position = scanner().peek_location().beg_pos;
   Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK);
-  return NEW(Conditional(expression, left, right));
+  return NEW(Conditional(expression, left, right,
+                         left_position, right_position));
 }
 
 
index 06c6df724c0d01e5189f60cdb49000dc269cd22c..e610e28366d1431d911f788d8612867c9393bd10 100644 (file)
@@ -229,6 +229,10 @@ void ExternalReferenceTable::PopulateTable() {
       DEBUG_ADDRESS,
       Debug::k_after_break_target_address << kDebugIdShift,
       "Debug::after_break_target_address()");
+  Add(Debug_Address(Debug::k_debug_break_slot_address).address(),
+      DEBUG_ADDRESS,
+      Debug::k_debug_break_slot_address << kDebugIdShift,
+      "Debug::debug_break_slot_address()");
   Add(Debug_Address(Debug::k_debug_break_return_address).address(),
       DEBUG_ADDRESS,
       Debug::k_debug_break_return_address << kDebugIdShift,
index 4c69510caeacd0ded472833cfe228912661bcffa..0ed282bf085e3ed6afa029686daf0528f4532724 100644 (file)
@@ -210,6 +210,10 @@ void RelocInfo::apply(intptr_t delta) {
     // Special handling of js_return when a break point is set (call
     // instruction has been inserted).
     Memory::int32_at(pc_ + 1) -= static_cast<int32_t>(delta);  // relocate entry
+  } else if (rmode_ == DEBUG_BREAK_SLOT && IsPatchedDebugBreakSlotSequence()) {
+    // Special handling of debug break slot when a break point is set (call
+    // instruction has been inserted).
+    Memory::int32_at(pc_ + 1) -= static_cast<int32_t>(delta);  // relocate entry
   }
 }
 
@@ -298,6 +302,11 @@ bool RelocInfo::IsPatchedReturnSequence() {
 }
 
 
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
+  return !Assembler::IsNop(pc());
+}
+
+
 Address RelocInfo::call_address() {
   ASSERT(IsPatchedReturnSequence());
   return Memory::Address_at(
@@ -341,8 +350,10 @@ void RelocInfo::Visit(ObjectVisitor* visitor) {
     visitor->VisitExternalReference(target_reference_address());
 #ifdef ENABLE_DEBUGGER_SUPPORT
   } else if (Debug::has_break_points() &&
-             RelocInfo::IsJSReturn(mode) &&
-             IsPatchedReturnSequence()) {
+             (RelocInfo::IsJSReturn(mode) &&
+              IsPatchedReturnSequence()) ||
+             (RelocInfo::IsDebugBreakSlot(mode) &&
+              IsPatchedDebugBreakSlotSequence())) {
     visitor->VisitDebugTarget(this);
 #endif
   } else if (mode == RelocInfo::RUNTIME_ENTRY) {
index 9f264964b9d16eb8e1766f1f22b5316634301ad9..d77c09fd4987f0815492d7400b3c99e3b5c7dddd 100644 (file)
@@ -2800,6 +2800,13 @@ void Assembler::RecordJSReturn() {
 }
 
 
+void Assembler::RecordDebugBreakSlot() {
+  WriteRecordedPositions();
+  EnsureSpace ensure_space(this);
+  RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
+}
+
+
 void Assembler::RecordComment(const char* msg) {
   if (FLAG_debug_code) {
     EnsureSpace ensure_space(this);
@@ -2822,13 +2829,16 @@ void Assembler::RecordStatementPosition(int pos) {
 }
 
 
-void Assembler::WriteRecordedPositions() {
+bool Assembler::WriteRecordedPositions() {
+  bool written = false;
+
   // Write the statement position if it is different from what was written last
   // time.
   if (current_statement_position_ != written_statement_position_) {
     EnsureSpace ensure_space(this);
     RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
     written_statement_position_ = current_statement_position_;
+    written = true;
   }
 
   // Write the position if it is different from what was written last time and
@@ -2838,7 +2848,11 @@ void Assembler::WriteRecordedPositions() {
     EnsureSpace ensure_space(this);
     RecordRelocInfo(RelocInfo::POSITION, current_position_);
     written_position_ = current_position_;
+    written = true;
   }
+
+  // Return whether something was written.
+  return written;
 }
 
 
index cceaccfaa06468b0c020d975ed8c14abce082ff0..c7e737c614cab6f370d4de0b9ad0e519d758c9fb 100644 (file)
@@ -455,6 +455,11 @@ class Assembler : public Malloced {
   // return address.  TODO: Use return sequence length instead.
   // Should equal Debug::kX64JSReturnSequenceLength - kCallTargetAddressOffset;
   static const int kPatchReturnSequenceAddressOffset = 13 - 4;
+  // Distance between start of patched debug break slot and where the
+  // 32-bit displacement of a near call would be, relative to the pushed
+  // return address.  TODO: Use return sequence length instead.
+  // Should equal Debug::kX64JSReturnSequenceLength - kCallTargetAddressOffset;
+  static const int kPatchDebugBreakSlotAddressOffset = 13 - 4;
   // TODO(X64): Rename this, removing the "Real", after changing the above.
   static const int kRealPatchReturnSequenceAddressOffset = 2;
 
@@ -463,6 +468,10 @@ class Assembler : public Malloced {
   static const int kCallInstructionLength = 13;
   static const int kJSReturnSequenceLength = 13;
 
+  // The debug break slot must be able to contain a call instruction.
+  static const int kDebugBreakSlotLength = kCallInstructionLength;
+
+
   // ---------------------------------------------------------------------------
   // Code generation
   //
@@ -1135,13 +1144,16 @@ class Assembler : public Malloced {
   // Mark address of the ExitJSFrame code.
   void RecordJSReturn();
 
+  // Mark address of a debug break slot.
+  void RecordDebugBreakSlot();
+
   // Record a comment relocation entry that can be used by a disassembler.
   // Use --debug_code to enable.
   void RecordComment(const char* msg);
 
   void RecordPosition(int pos);
   void RecordStatementPosition(int pos);
-  void WriteRecordedPositions();
+  bool WriteRecordedPositions();
 
   int pc_offset() const  { return static_cast<int>(pc_ - buffer_); }
   int current_statement_position() const { return current_statement_position_; }
@@ -1159,6 +1171,8 @@ class Assembler : public Malloced {
     return static_cast<int>(reloc_info_writer.pos() - pc_);
   }
 
+  static bool IsNop(Address addr) { return *addr == 0x90; }
+
   // Avoid overflows for displacements etc.
   static const int kMaximalBufferSize = 512*MB;
   static const int kMinimalBufferSize = 4*KB;
index 823a2575f137149de17fe7eff9c819612ff73dd3..5b1c8af86afe983747fd4a1e482316151ae851b4 100644 (file)
@@ -314,7 +314,9 @@ class CodeGenerator: public AstVisitor {
   static bool ShouldGenerateLog(Expression* type);
 #endif
 
-  static void RecordPositions(MacroAssembler* masm, int pos);
+  static bool RecordPositions(MacroAssembler* masm,
+                              int pos,
+                              bool right_here = false);
 
   // Accessors
   MacroAssembler* masm() { return masm_; }
index 2f62cba73a3f804f378b7bd7b6dd6386935d4347..0252bb4616bee75c78c8dcb7aa4cb38277d6a5cd 100644 (file)
@@ -181,10 +181,31 @@ void Debug::GenerateStubNoRegistersDebugBreak(MacroAssembler* masm) {
 }
 
 
+void Debug::GenerateSlot(MacroAssembler* masm) {
+  // Generate enough nop's to make space for a call instruction.
+  Label check_codesize;
+  __ bind(&check_codesize);
+  __ RecordDebugBreakSlot();
+  for (int i = 0; i < Assembler::kDebugBreakSlotLength; i++) {
+    __ nop();
+  }
+  ASSERT_EQ(Assembler::kDebugBreakSlotLength,
+            masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
+  // In the places where a debug break slot is inserted no registers can contain
+  // object pointers.
+  Generate_DebugBreakCallHelper(masm, 0, false);
+}
+
+
 void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
   masm->Abort("LiveEdit frame dropping is not supported on x64");
 }
 
+
 void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
   masm->Abort("LiveEdit frame dropping is not supported on x64");
 }
@@ -217,6 +238,28 @@ void BreakLocationIterator::SetDebugBreakAtReturn()  {
       Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
 }
 
+
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+  ASSERT(IsDebugBreakSlot());
+  // Check whether the debug break slot instructions have been patched.
+  return !Assembler::IsNop(rinfo()->pc());
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+  ASSERT(IsDebugBreakSlot());
+  rinfo()->PatchCodeWithCall(
+      Debug::debug_break_slot()->entry(),
+      Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+  ASSERT(IsDebugBreakSlot());
+  rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
+}
+
+
 #endif  // ENABLE_DEBUGGER_SUPPORT
 
 } }  // namespace v8::internal
index 612e4fc94b5d6458d79dee49638ca64017a9d573..4c3ff5e3daf7ace2285c93bced67c9cf382188b0 100644 (file)
@@ -1231,6 +1231,11 @@ TEST(GCDuringBreakPointProcessing) {
   SetBreakPoint(foo, 0);
   CallWithBreakPoints(env->Global(), foo, 1, 25);
 
+  // Test debug break slot break point with garbage collection.
+  foo = CompileFunction(&env, "function foo(){var a;}", "foo");
+  SetBreakPoint(foo, 0);
+  CallWithBreakPoints(env->Global(), foo, 1, 25);
+
   v8::Debug::SetDebugEventListener(NULL);
   CheckDebuggerUnloaded();
 }
@@ -1660,7 +1665,7 @@ TEST(ConditionalScriptBreakPoint) {
   f->Call(env->Global(), 0, NULL);
   CHECK_EQ(1, break_point_hit_count);
 
-  ChangeScriptBreakPointConditionFromJS(sbp1, "a % 2 == 0");
+  ChangeScriptBreakPointConditionFromJS(sbp1, "x % 2 == 0");
   break_point_hit_count = 0;
   for (int i = 0; i < 10; i++) {
     f->Call(env->Global(), 0, NULL);
@@ -2144,17 +2149,19 @@ TEST(DebugEvaluate) {
   v8::Local<v8::Function> foo = CompileFunction(&env,
     "function foo(x) {"
     "  var a;"
-    "  y=0; /* To ensure break location.*/"
+    "  y=0;"  // To ensure break location 1.
     "  a=x;"
+    "  y=0;"  // To ensure break location 2.
     "}",
     "foo");
-  const int foo_break_position = 15;
+  const int foo_break_position_1 = 15;
+  const int foo_break_position_2 = 29;
 
   // Arguments with one parameter "Hello, world!"
   v8::Handle<v8::Value> argv_foo[1] = { v8::String::New("Hello, world!") };
 
   // Call foo with breakpoint set before a=x and undefined as parameter.
-  int bp = SetBreakPoint(foo, foo_break_position);
+  int bp = SetBreakPoint(foo, foo_break_position_1);
   checks = checks_uu;
   foo->Call(env->Global(), 0, NULL);
 
@@ -2164,7 +2171,7 @@ TEST(DebugEvaluate) {
 
   // Call foo with breakpoint set after a=x and parameter "Hello, world!".
   ClearBreakPoint(bp);
-  SetBreakPoint(foo, foo_break_position + 1);
+  SetBreakPoint(foo, foo_break_position_2);
   checks = checks_hh;
   foo->Call(env->Global(), 1, argv_foo);
 
@@ -2426,6 +2433,9 @@ TEST(DebugStepKeyedLoadLoop) {
   v8::HandleScope scope;
   DebugLocalContext env;
 
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
   // Create a function for testing stepping of keyed load. The statement 'y=1'
   // is there to have more than one breakable statement in the loop, TODO(315).
   v8::Local<v8::Function> foo = CompileFunction(
@@ -2451,9 +2461,6 @@ TEST(DebugStepKeyedLoadLoop) {
   v8::Handle<v8::Value> args[kArgc] = { a };
   foo->Call(env->Global(), kArgc, args);
 
-  // Register a debug event listener which steps and counts.
-  v8::Debug::SetDebugEventListener(DebugEventStep);
-
   // Setup break point and step through the function.
   SetBreakPoint(foo, 3);
   step_action = StepNext;
@@ -2461,7 +2468,7 @@ TEST(DebugStepKeyedLoadLoop) {
   foo->Call(env->Global(), kArgc, args);
 
   // With stepping all break locations are hit.
-  CHECK_EQ(22, break_point_hit_count);
+  CHECK_EQ(33, break_point_hit_count);
 
   v8::Debug::SetDebugEventListener(NULL);
   CheckDebuggerUnloaded();
@@ -2473,6 +2480,9 @@ TEST(DebugStepKeyedStoreLoop) {
   v8::HandleScope scope;
   DebugLocalContext env;
 
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
   // Create a function for testing stepping of keyed store. The statement 'y=1'
   // is there to have more than one breakable statement in the loop, TODO(315).
   v8::Local<v8::Function> foo = CompileFunction(
@@ -2497,9 +2507,6 @@ TEST(DebugStepKeyedStoreLoop) {
   v8::Handle<v8::Value> args[kArgc] = { a };
   foo->Call(env->Global(), kArgc, args);
 
-  // Register a debug event listener which steps and counts.
-  v8::Debug::SetDebugEventListener(DebugEventStep);
-
   // Setup break point and step through the function.
   SetBreakPoint(foo, 3);
   step_action = StepNext;
@@ -2507,7 +2514,7 @@ TEST(DebugStepKeyedStoreLoop) {
   foo->Call(env->Global(), kArgc, args);
 
   // With stepping all break locations are hit.
-  CHECK_EQ(22, break_point_hit_count);
+  CHECK_EQ(32, break_point_hit_count);
 
   v8::Debug::SetDebugEventListener(NULL);
   CheckDebuggerUnloaded();
@@ -2519,6 +2526,9 @@ TEST(DebugStepNamedLoadLoop) {
   v8::HandleScope scope;
   DebugLocalContext env;
 
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
   // Create a function for testing stepping of named load.
   v8::Local<v8::Function> foo = CompileFunction(
       &env,
@@ -2541,9 +2551,6 @@ TEST(DebugStepNamedLoadLoop) {
   // Call function without any break points to ensure inlining is in place.
   foo->Call(env->Global(), 0, NULL);
 
-  // Register a debug event listener which steps and counts.
-  v8::Debug::SetDebugEventListener(DebugEventStep);
-
   // Setup break point and step through the function.
   SetBreakPoint(foo, 4);
   step_action = StepNext;
@@ -2551,7 +2558,7 @@ TEST(DebugStepNamedLoadLoop) {
   foo->Call(env->Global(), 0, NULL);
 
   // With stepping all break locations are hit.
-  CHECK_EQ(41, break_point_hit_count);
+  CHECK_EQ(53, break_point_hit_count);
 
   v8::Debug::SetDebugEventListener(NULL);
   CheckDebuggerUnloaded();
@@ -2563,6 +2570,9 @@ TEST(DebugStepLinearMixedICs) {
   v8::HandleScope scope;
   DebugLocalContext env;
 
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
   // Create a function for testing stepping.
   v8::Local<v8::Function> foo = CompileFunction(&env,
       "function bar() {};"
@@ -2573,15 +2583,12 @@ TEST(DebugStepLinearMixedICs) {
       "  a=1;b=2;x=a;y[index]=3;x=y[index];bar();}", "foo");
   SetBreakPoint(foo, 0);
 
-  // Register a debug event listener which steps and counts.
-  v8::Debug::SetDebugEventListener(DebugEventStep);
-
   step_action = StepIn;
   break_point_hit_count = 0;
   foo->Call(env->Global(), 0, NULL);
 
   // With stepping all break locations are hit.
-  CHECK_EQ(8, break_point_hit_count);
+  CHECK_EQ(11, break_point_hit_count);
 
   v8::Debug::SetDebugEventListener(NULL);
   CheckDebuggerUnloaded();
@@ -2601,6 +2608,66 @@ TEST(DebugStepLinearMixedICs) {
 }
 
 
+TEST(DebugStepDeclarations) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
+  // Create a function for testing stepping.
+  const char* src = "function foo() { "
+                    "  var a;"
+                    "  var b = 1;"
+                    "  var c = foo;"
+                    "  var d = Math.floor;"
+                    "  var e = b + d(1.2);"
+                    "}";
+  v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+  SetBreakPoint(foo, 0);
+
+  // Stepping through the declarations.
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  foo->Call(env->Global(), 0, NULL);
+  CHECK_EQ(6, break_point_hit_count);
+
+  // Get rid of the debug event listener.
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepLocals) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
+  // Create a function for testing stepping.
+  const char* src = "function foo() { "
+                    "  var a,b;"
+                    "  a = 1;"
+                    "  b = a + 2;"
+                    "  b = 1 + 2 + 3;"
+                    "  a = Math.floor(b);"
+                    "}";
+  v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+  SetBreakPoint(foo, 0);
+
+  // Stepping through the declarations.
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  foo->Call(env->Global(), 0, NULL);
+  CHECK_EQ(6, break_point_hit_count);
+
+  // Get rid of the debug event listener.
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+}
+
+
 TEST(DebugStepIf) {
   v8::HandleScope scope;
   DebugLocalContext env;
@@ -2627,14 +2694,14 @@ TEST(DebugStepIf) {
   break_point_hit_count = 0;
   v8::Handle<v8::Value> argv_true[argc] = { v8::True() };
   foo->Call(env->Global(), argc, argv_true);
-  CHECK_EQ(3, break_point_hit_count);
+  CHECK_EQ(4, break_point_hit_count);
 
   // Stepping through the false part.
   step_action = StepIn;
   break_point_hit_count = 0;
   v8::Handle<v8::Value> argv_false[argc] = { v8::False() };
   foo->Call(env->Global(), argc, argv_false);
-  CHECK_EQ(4, break_point_hit_count);
+  CHECK_EQ(5, break_point_hit_count);
 
   // Get rid of the debug event listener.
   v8::Debug::SetDebugEventListener(NULL);
@@ -2662,6 +2729,7 @@ TEST(DebugStepSwitch) {
                     "    case 3:"
                     "      d = 1;"
                     "      e = 1;"
+                    "      f = 1;"
                     "      break;"
                     "  }"
                     "}";
@@ -2673,21 +2741,97 @@ TEST(DebugStepSwitch) {
   break_point_hit_count = 0;
   v8::Handle<v8::Value> argv_1[argc] = { v8::Number::New(1) };
   foo->Call(env->Global(), argc, argv_1);
-  CHECK_EQ(4, break_point_hit_count);
+  CHECK_EQ(6, break_point_hit_count);
 
   // Another case.
   step_action = StepIn;
   break_point_hit_count = 0;
   v8::Handle<v8::Value> argv_2[argc] = { v8::Number::New(2) };
   foo->Call(env->Global(), argc, argv_2);
-  CHECK_EQ(3, break_point_hit_count);
+  CHECK_EQ(5, break_point_hit_count);
 
   // Last case.
   step_action = StepIn;
   break_point_hit_count = 0;
   v8::Handle<v8::Value> argv_3[argc] = { v8::Number::New(3) };
   foo->Call(env->Global(), argc, argv_3);
-  CHECK_EQ(4, break_point_hit_count);
+  CHECK_EQ(7, break_point_hit_count);
+
+  // Get rid of the debug event listener.
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepWhile) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
+  // Create a function for testing stepping.
+  const int argc = 1;
+  const char* src = "function foo(x) { "
+                    "  var a = 0;"
+                    "  while (a < x) {"
+                    "    a++;"
+                    "  }"
+                    "}";
+  v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+  SetBreakPoint(foo, 8);  // "var a = 0;"
+
+  // Looping 10 times.
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(10) };
+  foo->Call(env->Global(), argc, argv_10);
+  CHECK_EQ(23, break_point_hit_count);
+
+  // Looping 100 times.
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(100) };
+  foo->Call(env->Global(), argc, argv_100);
+  CHECK_EQ(203, break_point_hit_count);
+
+  // Get rid of the debug event listener.
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepDoWhile) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
+  // Create a function for testing stepping.
+  const int argc = 1;
+  const char* src = "function foo(x) { "
+                    "  var a = 0;"
+                    "  do {"
+                    "    a++;"
+                    "  } while (a < x)"
+                    "}";
+  v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+  SetBreakPoint(foo, 8);  // "var a = 0;"
+
+  // Looping 10 times.
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(10) };
+  foo->Call(env->Global(), argc, argv_10);
+  CHECK_EQ(22, break_point_hit_count);
+
+  // Looping 100 times.
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(100) };
+  foo->Call(env->Global(), argc, argv_100);
+  CHECK_EQ(202, break_point_hit_count);
 
   // Get rid of the debug event listener.
   v8::Debug::SetDebugEventListener(NULL);
@@ -2733,6 +2877,210 @@ TEST(DebugStepFor) {
 }
 
 
+TEST(DebugStepForContinue) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
+  // Create a function for testing stepping.
+  const int argc = 1;
+  const char* src = "function foo(x) { "
+                    "  var a = 0;"
+                    "  var b = 0;"
+                    "  var c = 0;"
+                    "  for (var i = 0; i < x; i++) {"
+                    "    a++;"
+                    "    if (a % 2 == 0) continue;"
+                    "    b++;"
+                    "    c++;"
+                    "  }"
+                    "  return b;"
+                    "}";
+  v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+  v8::Handle<v8::Value> result;
+  SetBreakPoint(foo, 8);  // "var a = 0;"
+
+  // Each loop generates 4 or 5 steps depending on whether a is equal.
+
+  // Looping 10 times.
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(10) };
+  result = foo->Call(env->Global(), argc, argv_10);
+  CHECK_EQ(5, result->Int32Value());
+  CHECK_EQ(50, break_point_hit_count);
+
+  // Looping 100 times.
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(100) };
+  result = foo->Call(env->Global(), argc, argv_100);
+  CHECK_EQ(50, result->Int32Value());
+  CHECK_EQ(455, break_point_hit_count);
+
+  // Get rid of the debug event listener.
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepForBreak) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
+  // Create a function for testing stepping.
+  const int argc = 1;
+  const char* src = "function foo(x) { "
+                    "  var a = 0;"
+                    "  var b = 0;"
+                    "  var c = 0;"
+                    "  for (var i = 0; i < 1000; i++) {"
+                    "    a++;"
+                    "    if (a == x) break;"
+                    "    b++;"
+                    "    c++;"
+                    "  }"
+                    "  return b;"
+                    "}";
+  v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+  v8::Handle<v8::Value> result;
+  SetBreakPoint(foo, 8);  // "var a = 0;"
+
+  // Each loop generates 5 steps except for the last (when break is executed)
+  // which only generates 4.
+
+  // Looping 10 times.
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(10) };
+  result = foo->Call(env->Global(), argc, argv_10);
+  CHECK_EQ(9, result->Int32Value());
+  CHECK_EQ(53, break_point_hit_count);
+
+  // Looping 100 times.
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(100) };
+  result = foo->Call(env->Global(), argc, argv_100);
+  CHECK_EQ(99, result->Int32Value());
+  CHECK_EQ(503, break_point_hit_count);
+
+  // Get rid of the debug event listener.
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepForIn) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
+  v8::Local<v8::Function> foo;
+  const char* src_1 = "function foo() { "
+                      "  var a = [1, 2];"
+                      "  for (x in a) {"
+                      "    b = 0;"
+                      "  }"
+                      "}";
+  foo = CompileFunction(&env, src_1, "foo");
+  SetBreakPoint(foo, 0);  // "var a = ..."
+
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  foo->Call(env->Global(), 0, NULL);
+  CHECK_EQ(6, break_point_hit_count);
+
+  const char* src_2 = "function foo() { "
+                      "  var a = {a:[1, 2, 3]};"
+                      "  for (x in a.a) {"
+                      "    b = 0;"
+                      "  }"
+                      "}";
+  foo = CompileFunction(&env, src_2, "foo");
+  SetBreakPoint(foo, 0);  // "var a = ..."
+
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  foo->Call(env->Global(), 0, NULL);
+  CHECK_EQ(8, break_point_hit_count);
+
+  // Get rid of the debug event listener.
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepWith) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
+  // Create a function for testing stepping.
+  const char* src = "function foo(x) { "
+                    "  var a = {};"
+                    "  with (a) {}"
+                    "  with (b) {}"
+                    "}";
+  env->Global()->Set(v8::String::New("b"), v8::Object::New());
+  v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+  v8::Handle<v8::Value> result;
+  SetBreakPoint(foo, 8);  // "var a = {};"
+
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  foo->Call(env->Global(), 0, NULL);
+  CHECK_EQ(4, break_point_hit_count);
+
+  // Get rid of the debug event listener.
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugConditional) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventStep);
+
+  // Create a function for testing stepping.
+  const char* src = "function foo(x) { "
+                    "  var a;"
+                    "  a = x ? 1 : 2;"
+                    "  return a;"
+                    "}";
+  v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+  SetBreakPoint(foo, 0);  // "var a;"
+
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  foo->Call(env->Global(), 0, NULL);
+  CHECK_EQ(5, break_point_hit_count);
+
+  step_action = StepIn;
+  break_point_hit_count = 0;
+  const int argc = 1;
+  v8::Handle<v8::Value> argv_true[argc] = { v8::True() };
+  foo->Call(env->Global(), argc, argv_true);
+  CHECK_EQ(5, break_point_hit_count);
+
+  // Get rid of the debug event listener.
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+}
+
+
 TEST(StepInOutSimple) {
   v8::HandleScope scope;
   DebugLocalContext env;
@@ -2854,7 +3202,7 @@ TEST(StepInOutBranch) {
   // Step through invocation of a.
   step_action = StepIn;
   break_point_hit_count = 0;
-  expected_step_sequence = "abaca";
+  expected_step_sequence = "abbaca";
   a->Call(env->Global(), 0, NULL);
   CHECK_EQ(StrLength(expected_step_sequence),
            break_point_hit_count);
@@ -2923,7 +3271,7 @@ TEST(DebugStepFunctionApply) {
   foo->Call(env->Global(), 0, NULL);
 
   // With stepping all break locations are hit.
-  CHECK_EQ(6, break_point_hit_count);
+  CHECK_EQ(7, break_point_hit_count);
 
   v8::Debug::SetDebugEventListener(NULL);
   CheckDebuggerUnloaded();
@@ -2967,14 +3315,14 @@ TEST(DebugStepFunctionCall) {
   // Check stepping where the if condition in bar is false.
   break_point_hit_count = 0;
   foo->Call(env->Global(), 0, NULL);
-  CHECK_EQ(4, break_point_hit_count);
+  CHECK_EQ(6, break_point_hit_count);
 
   // Check stepping where the if condition in bar is true.
   break_point_hit_count = 0;
   const int argc = 1;
   v8::Handle<v8::Value> argv[argc] = { v8::True() };
   foo->Call(env->Global(), argc, argv);
-  CHECK_EQ(6, break_point_hit_count);
+  CHECK_EQ(8, break_point_hit_count);
 
   v8::Debug::SetDebugEventListener(NULL);
   CheckDebuggerUnloaded();
@@ -3267,14 +3615,13 @@ TEST(StepWithException) {
   b->Call(env->Global(), 0, NULL);
   CHECK_EQ(StrLength(expected_step_sequence),
            break_point_hit_count);
-
   // Step through invocation of d + e.
   v8::Local<v8::Function> d = CompileFunction(&env, src, "d");
   SetBreakPoint(d, 0);
   ChangeBreakOnException(false, true);
   step_action = StepIn;
   break_point_hit_count = 0;
-  expected_step_sequence = "dded";
+  expected_step_sequence = "ddedd";
   d->Call(env->Global(), 0, NULL);
   CHECK_EQ(StrLength(expected_step_sequence),
            break_point_hit_count);
@@ -3283,7 +3630,7 @@ TEST(StepWithException) {
   ChangeBreakOnException(true, true);
   step_action = StepIn;
   break_point_hit_count = 0;
-  expected_step_sequence = "ddeed";
+  expected_step_sequence = "ddeedd";
   d->Call(env->Global(), 0, NULL);
   CHECK_EQ(StrLength(expected_step_sequence),
            break_point_hit_count);
@@ -3294,7 +3641,7 @@ TEST(StepWithException) {
   ChangeBreakOnException(false, true);
   step_action = StepIn;
   break_point_hit_count = 0;
-  expected_step_sequence = "ffghf";
+  expected_step_sequence = "ffghhff";
   f->Call(env->Global(), 0, NULL);
   CHECK_EQ(StrLength(expected_step_sequence),
            break_point_hit_count);
@@ -3303,7 +3650,7 @@ TEST(StepWithException) {
   ChangeBreakOnException(true, true);
   step_action = StepIn;
   break_point_hit_count = 0;
-  expected_step_sequence = "ffghhf";
+  expected_step_sequence = "ffghhhff";
   f->Call(env->Global(), 0, NULL);
   CHECK_EQ(StrLength(expected_step_sequence),
            break_point_hit_count);
index 5859451ba4d6aef562928ecfd6d99b0b393abada..bd4cdd150c8d4ef06cb1ac9e0c525fbd7ad5a7df 100644 (file)
@@ -45,7 +45,7 @@ Debug.setListener(listener);
 count = 0;
 function f() {};
 function g() {h(count++)};
-function h(x) {var a=x;};
+function h(x) {var a=x; return a};
 
 
 // Conditional breakpoint which syntax error.
@@ -136,7 +136,7 @@ Debug.clearBreakPoint(bp);
 
 // Conditional breakpoint which checks a local variable.
 break_point_hit_count = 0;
-bp = Debug.setBreakPoint(h, 0, 0, 'a % 2 == 0');
+bp = Debug.setBreakPoint(h, 0, 23, 'a % 2 == 0');
 for (var i = 0; i < 10; i++) {
   g();
 }
@@ -146,8 +146,8 @@ Debug.clearBreakPoint(bp);
 
 // Multiple conditional breakpoint which the same condition.
 break_point_hit_count = 0;
-bp1 = Debug.setBreakPoint(h, 0, 0, 'a % 2 == 0');
-bp2 = Debug.setBreakPoint(h, 0, 0, 'a % 2 == 0');
+bp1 = Debug.setBreakPoint(h, 0, 23, 'a % 2 == 0');
+bp2 = Debug.setBreakPoint(h, 0, 23, 'a % 2 == 0');
 for (var i = 0; i < 10; i++) {
   g();
 }
@@ -159,8 +159,8 @@ Debug.clearBreakPoint(bp2);
 
 // Multiple conditional breakpoint which different conditions.
 break_point_hit_count = 0;
-bp1 = Debug.setBreakPoint(h, 0, 0, 'a % 2 == 0');
-bp2 = Debug.setBreakPoint(h, 0, 0, '(a + 1) % 2 == 0');
+bp1 = Debug.setBreakPoint(h, 0, 23, 'a % 2 == 0');
+bp2 = Debug.setBreakPoint(h, 0, 23, '(a + 1) % 2 == 0');
 for (var i = 0; i < 10; i++) {
   g();
 }
index a887514a0e48c3135d96128e10b65461fe03de47..2233e36c66db0fae422eb8068c7b048e388c4d6d 100644 (file)
@@ -55,8 +55,9 @@ Debug.setListener(listener);
 
 // Test debug event for break point.
 function f() {
-  for (i = 0; i < 1000; i++) {  //  Line 1.
-    x = 1;                      //  Line 2.
+  var i;                        // Line 1.
+  for (i = 0; i < 1000; i++) {  // Line 2.
+    x = 1;                      // Line 3.
   }
 };
 
@@ -74,7 +75,7 @@ assertEquals(499, result);
 // multiple steps have been requested.
 state = 0;
 result = -1;
-bp2 = Debug.setBreakPoint(f, 2);
+bp2 = Debug.setBreakPoint(f, 3);
 f();
 assertEquals(0, result);