Fix issue 493: Infinite loop when debug break is set when entering function.apply.
authorsgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 5 Nov 2009 13:59:40 +0000 (13:59 +0000)
committersgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 5 Nov 2009 13:59:40 +0000 (13:59 +0000)
In the generated code for function.apply there was a loop checking the stack limit for interruption. This loop would call into the runtime system to handle interuption and keep running until there was no interruption. However if the interuption was debug break the runtime system would never clear the interruption as debug break is prevented in builtins are prevented and the assumption here was that returning with the debug break flag set would move execution forward.

Renamed initial_jslimit and initial_climit to real_jslimit and real_climit. Renamed a few external references related to the stack limit as well.

Exposed the real stack limit to generated code to make the stack check when entering function.apply use the real stack limit and not the stack limit which is changed to signal interruption.

Added the real stack limit to the roots array.

BUG=http://code.google.com/p/v8/issues/detail?id=493
TEST=cctest/test-debug/DebugBreakFunctionApply
Review URL: http://codereview.chromium.org/345048

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

18 files changed:
src/arm/builtins-arm.cc
src/arm/regexp-macro-assembler-arm.cc
src/assembler.cc
src/assembler.h
src/execution.cc
src/execution.h
src/heap.cc
src/heap.h
src/ia32/builtins-ia32.cc
src/ia32/codegen-ia32.cc
src/ia32/fast-codegen-ia32.cc
src/ia32/regexp-macro-assembler-ia32.cc
src/serialize.cc
src/v8.cc
src/x64/builtins-x64.cc
src/x64/regexp-macro-assembler-x64.cc
test/cctest/test-debug.cc
test/cctest/test-serialize.cc

index 6db554a77ca50b0ce68dec44308be67a8b7a52c0..5689ddf01eb7ade3304e3066c3485aa2aa436177 100644 (file)
@@ -1029,44 +1029,24 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ push(r0);
   __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_JS);
 
-  Label no_preemption, retry_preemption;
-  __ bind(&retry_preemption);
-  ExternalReference stack_guard_limit_address =
-      ExternalReference::address_of_stack_guard_limit();
-  __ mov(r2, Operand(stack_guard_limit_address));
-  __ ldr(r2, MemOperand(r2));
-  __ cmp(sp, r2);
-  __ b(hi, &no_preemption);
-
-  // We have encountered a preemption or stack overflow already before we push
-  // the array contents.  Save r0 which is the Smi-tagged length of the array.
-  __ push(r0);
-
-  // Runtime routines expect at least one argument, so give it a Smi.
-  __ mov(r0, Operand(Smi::FromInt(0)));
-  __ push(r0);
-  __ CallRuntime(Runtime::kStackGuard, 1);
-
-  // Since we returned, it wasn't a stack overflow.  Restore r0 and try again.
-  __ pop(r0);
-  __ b(&retry_preemption);
-
-  __ bind(&no_preemption);
-
-  // Eagerly check for stack-overflow before starting to push the arguments.
-  // r0: number of arguments.
-  // r2: stack limit.
+  // Check the stack for overflow. We are not trying need to catch
+  // interruptions (e.g. debug break and preemption) here, so the "real stack
+  // limit" is checked.
   Label okay;
+  __ LoadRoot(r2, Heap::kRealStackLimitRootIndex);
+  // Make r2 the space we have left. The stack might already be overflowed
+  // here which will cause r2 to become negative.
   __ sub(r2, sp, r2);
-
+  // Check if the arguments will overflow the stack.
   __ cmp(r2, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
-  __ b(hi, &okay);
+  __ b(gt, &okay);  // Signed comparison.
 
   // Out of stack space.
   __ ldr(r1, MemOperand(fp, kFunctionOffset));
   __ push(r1);
   __ push(r0);
   __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_JS);
+  // End of stack check.
 
   // Push current limit and index.
   __ bind(&okay);
index bd50428d8b95b868cd700bc2b0cd98cf209ff8ab..24b6a9c81a8b2dd6e09040a2cfe65075c32437a9 100644 (file)
@@ -588,9 +588,9 @@ Handle<Object> RegExpMacroAssemblerARM::GetCode(Handle<String> source) {
   Label stack_limit_hit;
   Label stack_ok;
 
-  ExternalReference stack_guard_limit =
-      ExternalReference::address_of_stack_guard_limit();
-  __ mov(r0, Operand(stack_guard_limit));
+  ExternalReference stack_limit =
+      ExternalReference::address_of_stack_limit();
+  __ mov(r0, Operand(stack_limit));
   __ ldr(r0, MemOperand(r0));
   __ sub(r0, sp, r0, SetCC);
   // Handle it if the stack pointer is already below the stack limit.
@@ -1090,9 +1090,9 @@ void RegExpMacroAssemblerARM::Pop(Register target) {
 
 void RegExpMacroAssemblerARM::CheckPreemption() {
   // Check for preemption.
-  ExternalReference stack_guard_limit =
-      ExternalReference::address_of_stack_guard_limit();
-  __ mov(r0, Operand(stack_guard_limit));
+  ExternalReference stack_limit =
+      ExternalReference::address_of_stack_limit();
+  __ mov(r0, Operand(stack_limit));
   __ ldr(r0, MemOperand(r0));
   __ cmp(sp, r0);
   SafeCall(&check_preempt_label_, ls);
index 34346a9105d3377ce3fe6c1f2dc558f20d24cea9..1620fef00ceb72f26d4b1e81d87890f80b921ba4 100644 (file)
@@ -583,11 +583,16 @@ ExternalReference ExternalReference::roots_address() {
 }
 
 
-ExternalReference ExternalReference::address_of_stack_guard_limit() {
+ExternalReference ExternalReference::address_of_stack_limit() {
   return ExternalReference(StackGuard::address_of_jslimit());
 }
 
 
+ExternalReference ExternalReference::address_of_real_stack_limit() {
+  return ExternalReference(StackGuard::address_of_real_jslimit());
+}
+
+
 ExternalReference ExternalReference::address_of_regexp_stack_limit() {
   return ExternalReference(RegExpStack::limit_address());
 }
index 311dadd53c3cc4769efe9c7ec977eeed479a94ba..434e3bccde20d2a73d82b2444600b3433985a54f 100644 (file)
@@ -408,7 +408,10 @@ class ExternalReference BASE_EMBEDDED {
   static ExternalReference roots_address();
 
   // Static variable StackGuard::address_of_jslimit()
-  static ExternalReference address_of_stack_guard_limit();
+  static ExternalReference address_of_stack_limit();
+
+  // Static variable StackGuard::address_of_real_jslimit()
+  static ExternalReference address_of_real_stack_limit();
 
   // Static variable RegExpStack::limit_address()
   static ExternalReference address_of_regexp_stack_limit();
index 7b4ff8b175b7e3716669cf63cb7885e81d2aab93..2f646a56389ce0b7538cd71ddf0f01e804cd70f6 100644 (file)
@@ -227,15 +227,14 @@ void StackGuard::SetStackLimit(uintptr_t limit) {
   // If the current limits are special (eg due to a pending interrupt) then
   // leave them alone.
   uintptr_t jslimit = SimulatorStack::JsLimitFromCLimit(limit);
-  if (thread_local_.jslimit_ == thread_local_.initial_jslimit_) {
+  if (thread_local_.jslimit_ == thread_local_.real_jslimit_) {
     thread_local_.jslimit_ = jslimit;
-    Heap::SetStackLimit(jslimit);
   }
-  if (thread_local_.climit_ == thread_local_.initial_climit_) {
+  if (thread_local_.climit_ == thread_local_.real_climit_) {
     thread_local_.climit_ = limit;
   }
-  thread_local_.initial_climit_ = limit;
-  thread_local_.initial_jslimit_ = jslimit;
+  thread_local_.real_climit_ = limit;
+  thread_local_.real_jslimit_ = jslimit;
 }
 
 
@@ -344,7 +343,7 @@ char* StackGuard::ArchiveStackGuard(char* to) {
 char* StackGuard::RestoreStackGuard(char* from) {
   ExecutionAccess access;
   memcpy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal));
-  Heap::SetStackLimit(thread_local_.jslimit_);
+  Heap::SetStackLimits();
   return from + sizeof(ThreadLocal);
 }
 
@@ -356,33 +355,33 @@ static internal::Thread::LocalStorageKey stack_limit_key =
 void StackGuard::FreeThreadResources() {
   Thread::SetThreadLocal(
       stack_limit_key,
-      reinterpret_cast<void*>(thread_local_.initial_climit_));
+      reinterpret_cast<void*>(thread_local_.real_climit_));
 }
 
 
 void StackGuard::ThreadLocal::Clear() {
-  initial_jslimit_ = kIllegalLimit;
+  real_jslimit_ = kIllegalLimit;
   jslimit_ = kIllegalLimit;
-  initial_climit_ = kIllegalLimit;
+  real_climit_ = kIllegalLimit;
   climit_ = kIllegalLimit;
   nesting_ = 0;
   postpone_interrupts_nesting_ = 0;
   interrupt_flags_ = 0;
-  Heap::SetStackLimit(kIllegalLimit);
+  Heap::SetStackLimits();
 }
 
 
 void StackGuard::ThreadLocal::Initialize() {
-  if (initial_climit_ == kIllegalLimit) {
+  if (real_climit_ == kIllegalLimit) {
     // Takes the address of the limit variable in order to find out where
     // the top of stack is right now.
     uintptr_t limit = reinterpret_cast<uintptr_t>(&limit) - kLimitSize;
     ASSERT(reinterpret_cast<uintptr_t>(&limit) > kLimitSize);
-    initial_jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
+    real_jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
     jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
-    initial_climit_ = limit;
+    real_climit_ = limit;
     climit_ = limit;
-    Heap::SetStackLimit(SimulatorStack::JsLimitFromCLimit(limit));
+    Heap::SetStackLimits();
   }
   nesting_ = 0;
   postpone_interrupts_nesting_ = 0;
index ac00aa46fd2c32d4eed63718fa390f01b0c42271..52198c420d9ab9cde46e52cc7f51975c582baf46 100644 (file)
@@ -150,10 +150,6 @@ class StackGuard : public AllStatic {
   // is assumed to grow downwards.
   static void SetStackLimit(uintptr_t limit);
 
-  static Address address_of_jslimit() {
-    return reinterpret_cast<Address>(&thread_local_.jslimit_);
-  }
-
   // Threading support.
   static char* ArchiveStackGuard(char* to);
   static char* RestoreStackGuard(char* from);
@@ -181,16 +177,24 @@ class StackGuard : public AllStatic {
 #endif
   static void Continue(InterruptFlag after_what);
 
-  // This provides an asynchronous read of the stack limit for the current
+  // This provides an asynchronous read of the stack limits for the current
   // thread.  There are no locks protecting this, but it is assumed that you
   // have the global V8 lock if you are using multiple V8 threads.
   static uintptr_t climit() {
     return thread_local_.climit_;
   }
-
   static uintptr_t jslimit() {
     return thread_local_.jslimit_;
   }
+  static uintptr_t real_jslimit() {
+    return thread_local_.real_jslimit_;
+  }
+  static Address address_of_jslimit() {
+    return reinterpret_cast<Address>(&thread_local_.jslimit_);
+  }
+  static Address address_of_real_jslimit() {
+    return reinterpret_cast<Address>(&thread_local_.real_jslimit_);
+  }
 
  private:
   // You should hold the ExecutionAccess lock when calling this method.
@@ -198,17 +202,17 @@ class StackGuard : public AllStatic {
 
   // You should hold the ExecutionAccess lock when calling this method.
   static void set_limits(uintptr_t value, const ExecutionAccess& lock) {
-    Heap::SetStackLimit(value);
     thread_local_.jslimit_ = value;
     thread_local_.climit_ = value;
+    Heap::SetStackLimits();
   }
 
-  // Reset limits to initial values. For example after handling interrupt.
+  // Reset limits to actual values. For example after handling interrupt.
   // You should hold the ExecutionAccess lock when calling this method.
   static void reset_limits(const ExecutionAccess& lock) {
-    thread_local_.jslimit_ = thread_local_.initial_jslimit_;
-    Heap::SetStackLimit(thread_local_.jslimit_);
-    thread_local_.climit_ = thread_local_.initial_climit_;
+    thread_local_.jslimit_ = thread_local_.real_jslimit_;
+    thread_local_.climit_ = thread_local_.real_climit_;
+    Heap::SetStackLimits();
   }
 
   // Enable or disable interrupts.
@@ -232,10 +236,21 @@ class StackGuard : public AllStatic {
     // Clear.
     void Initialize();
     void Clear();
-    uintptr_t initial_jslimit_;
+
+    // The stack limit is split into a JavaScript and a C++ stack limit. These
+    // two are the same except when running on a simulator where the C++ and
+    // JavaScript stacks are separate. Each of the two stack limits have two
+    // values. The one eith the real_ prefix is the actual stack limit
+    // set for the VM. The one without the real_ prefix has the same value as
+    // the actual stack limit except when there is an interruption (e.g. debug
+    // break or preemption) in which case it is lowered to make stack checks
+    // fail. Both the generated code and the runtime system check against the
+    // one without the real_ prefix.
+    uintptr_t real_jslimit_;  // Actual JavaScript stack limit set for the VM.
     uintptr_t jslimit_;
-    uintptr_t initial_climit_;
+    uintptr_t real_climit_;  // Actual C++ stack limit set for the VM.
     uintptr_t climit_;
+
     int nesting_;
     int postpone_interrupts_nesting_;
     int interrupt_flags_;
index ae18fbe6b78666acf357eb67fffbb07edaa926a4..23e4b3b8fc2374139df55521e7b2607574a2130f 100644 (file)
@@ -3455,14 +3455,18 @@ bool Heap::Setup(bool create_heap_objects) {
 }
 
 
-void Heap::SetStackLimit(intptr_t limit) {
+void Heap::SetStackLimits() {
   // On 64 bit machines, pointers are generally out of range of Smis.  We write
   // something that looks like an out of range Smi to the GC.
 
-  // Set up the special root array entry containing the stack guard.
-  // This is actually an address, but the tag makes the GC ignore it.
+  // Set up the special root array entries containing the stack limits.
+  // These are actually addresses, but the tag makes the GC ignore it.
   roots_[kStackLimitRootIndex] =
-    reinterpret_cast<Object*>((limit & ~kSmiTagMask) | kSmiTag);
+      reinterpret_cast<Object*>(
+          (StackGuard::jslimit() & ~kSmiTagMask) | kSmiTag);
+  roots_[kRealStackLimitRootIndex] =
+      reinterpret_cast<Object*>(
+          (StackGuard::real_jslimit() & ~kSmiTagMask) | kSmiTag);
 }
 
 
index 285260594f373230322891b14a22d936456e1a5c..6639e641d993e7cea9320fc8bec1e4bfb6a66cc2 100644 (file)
@@ -148,6 +148,7 @@ namespace internal {
   V(FixedArray, single_character_string_cache, SingleCharacterStringCache)     \
   V(FixedArray, natives_source_cache, NativesSourceCache)                      \
   V(Object, last_script_id, LastScriptId)                                      \
+  V(Smi, real_stack_limit, RealStackLimit)                                     \
 
 #if V8_TARGET_ARCH_ARM && V8_NATIVE_REGEXP
 #define STRONG_ROOT_LIST(V)                                                    \
@@ -250,10 +251,10 @@ class Heap : public AllStatic {
   // Destroys all memory allocated by the heap.
   static void TearDown();
 
-  // Sets the stack limit in the roots_ array.  Some architectures generate code
-  // that looks here, because it is faster than loading from the static jslimit_
-  // variable.
-  static void SetStackLimit(intptr_t limit);
+  // Set the stack limit in the roots_ array.  Some architectures generate
+  // code that looks here, because it is faster than loading from the static
+  // jslimit_/real_jslimit_ variable in the StackGuard.
+  static void SetStackLimits();
 
   // Returns whether Setup has been called.
   static bool HasBeenSetup();
index 963b0e3ac884ab33ac16ab5276340b5d51edd01b..ee4d1149a923d26ad5b04a5c5d2060594b488b3e 100644 (file)
@@ -522,43 +522,26 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ push(Operand(ebp, 2 * kPointerSize));  // push arguments
   __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
 
-  // Check the stack for overflow or a break request.
-  // We need to catch preemptions right here, otherwise an unlucky preemption
-  // could show up as a failed apply.
-  ExternalReference stack_guard_limit =
-      ExternalReference::address_of_stack_guard_limit();
-  Label retry_preemption;
-  Label no_preemption;
-  __ bind(&retry_preemption);
-  __ mov(edi, Operand::StaticVariable(stack_guard_limit));
-  __ cmp(esp, Operand(edi));
-  __ j(above, &no_preemption, taken);
-
-  // Preemption!
-  // Because builtins always remove the receiver from the stack, we
-  // have to fake one to avoid underflowing the stack.
-  __ push(eax);
-  __ push(Immediate(Smi::FromInt(0)));
-
-  // Do call to runtime routine.
-  __ CallRuntime(Runtime::kStackGuard, 1);
-  __ pop(eax);
-  __ jmp(&retry_preemption);
-
-  __ bind(&no_preemption);
-
+  // Check the stack for overflow. We are not trying need to catch
+  // interruptions (e.g. debug break and preemption) here, so the "real stack
+  // limit" is checked.
   Label okay;
-  // Make ecx the space we have left.
+  ExternalReference real_stack_limit =
+      ExternalReference::address_of_real_stack_limit();
+  __ mov(edi, Operand::StaticVariable(real_stack_limit));
+  // Make ecx the space we have left. The stack might already be overflowed
+  // here which will cause ecx to become negative.
   __ mov(ecx, Operand(esp));
   __ sub(ecx, Operand(edi));
   // Make edx the space we need for the array when it is unrolled onto the
   // stack.
   __ mov(edx, Operand(eax));
   __ shl(edx, kPointerSizeLog2 - kSmiTagSize);
+  // Check if the arguments will overflow the stack.
   __ cmp(ecx, Operand(edx));
-  __ j(greater, &okay, taken);
+  __ j(greater, &okay, taken);  // Signed comparison.
 
-  // Too bad: Out of stack space.
+  // Out of stack space.
   __ push(Operand(ebp, 4 * kPointerSize));  // push this
   __ push(eax);
   __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
index cc05932a1512fb1e504c2bc7577382290f545979..d49718bfc6dd127bd077137d79db2b5e8f9f9c8b 100644 (file)
@@ -2181,9 +2181,9 @@ void DeferredStackCheck::Generate() {
 
 void CodeGenerator::CheckStack() {
   DeferredStackCheck* deferred = new DeferredStackCheck;
-  ExternalReference stack_guard_limit =
-      ExternalReference::address_of_stack_guard_limit();
-  __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
+  ExternalReference stack_limit =
+      ExternalReference::address_of_stack_limit();
+  __ cmp(esp, Operand::StaticVariable(stack_limit));
   deferred->Branch(below);
   deferred->BindExit();
 }
index 57b5646ba92a85286771a0bc829fc6700de7c9d7..638f90e4972ced47e2865e5bae047c609cbd1349 100644 (file)
@@ -91,9 +91,9 @@ void FastCodeGenerator::Generate(FunctionLiteral* fun) {
 
   { Comment cmnt(masm_, "[ Stack check");
     Label ok;
-    ExternalReference stack_guard_limit =
-        ExternalReference::address_of_stack_guard_limit();
-    __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
+    ExternalReference stack_limit =
+        ExternalReference::address_of_stack_limit();
+    __ cmp(esp, Operand::StaticVariable(stack_limit));
     __ j(above_equal, &ok, taken);
     StackCheckStub stub;
     __ CallStub(&stub);
index 76d36a939cd1526cbb9838f695b8208fc995f7e8..2e13d8aeed6e7ae768960bd667e084535d3f8a1c 100644 (file)
@@ -598,10 +598,10 @@ Handle<Object> RegExpMacroAssemblerIA32::GetCode(Handle<String> source) {
   Label stack_limit_hit;
   Label stack_ok;
 
-  ExternalReference stack_guard_limit =
-      ExternalReference::address_of_stack_guard_limit();
+  ExternalReference stack_limit =
+      ExternalReference::address_of_stack_limit();
   __ mov(ecx, esp);
-  __ sub(ecx, Operand::StaticVariable(stack_guard_limit));
+  __ sub(ecx, Operand::StaticVariable(stack_limit));
   // Handle it if the stack pointer is already below the stack limit.
   __ j(below_equal, &stack_limit_hit, not_taken);
   // Check if there is room for the variable number of registers above
@@ -1081,9 +1081,9 @@ void RegExpMacroAssemblerIA32::Pop(Register target) {
 void RegExpMacroAssemblerIA32::CheckPreemption() {
   // Check for preemption.
   Label no_preempt;
-  ExternalReference stack_guard_limit =
-      ExternalReference::address_of_stack_guard_limit();
-  __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
+  ExternalReference stack_limit =
+      ExternalReference::address_of_stack_limit();
+  __ cmp(esp, Operand::StaticVariable(stack_limit));
   __ j(above, &no_preempt, taken);
 
   SafeCall(&check_preempt_label_);
index 3c333d068893408078b321408c2f6e3646d297bf..17ed7b4a125f8cee03e3196656e11b3390e5ada9 100644 (file)
@@ -688,76 +688,80 @@ void ExternalReferenceTable::PopulateTable() {
       UNCLASSIFIED,
       3,
       "Heap::roots_address()");
-  Add(ExternalReference::address_of_stack_guard_limit().address(),
+  Add(ExternalReference::address_of_stack_limit().address(),
       UNCLASSIFIED,
       4,
       "StackGuard::address_of_jslimit()");
-  Add(ExternalReference::address_of_regexp_stack_limit().address(),
+  Add(ExternalReference::address_of_real_stack_limit().address(),
       UNCLASSIFIED,
       5,
+      "StackGuard::address_of_real_jslimit()");
+  Add(ExternalReference::address_of_regexp_stack_limit().address(),
+      UNCLASSIFIED,
+      6,
       "RegExpStack::limit_address()");
   Add(ExternalReference::new_space_start().address(),
       UNCLASSIFIED,
-      6,
+      7,
       "Heap::NewSpaceStart()");
   Add(ExternalReference::heap_always_allocate_scope_depth().address(),
       UNCLASSIFIED,
-      7,
+      8,
       "Heap::always_allocate_scope_depth()");
   Add(ExternalReference::new_space_allocation_limit_address().address(),
       UNCLASSIFIED,
-      8,
+      9,
       "Heap::NewSpaceAllocationLimitAddress()");
   Add(ExternalReference::new_space_allocation_top_address().address(),
       UNCLASSIFIED,
-      9,
+      10,
       "Heap::NewSpaceAllocationTopAddress()");
 #ifdef ENABLE_DEBUGGER_SUPPORT
   Add(ExternalReference::debug_break().address(),
       UNCLASSIFIED,
-      10,
+      11,
       "Debug::Break()");
   Add(ExternalReference::debug_step_in_fp_address().address(),
       UNCLASSIFIED,
-      11,
+      12,
       "Debug::step_in_fp_addr()");
 #endif
   Add(ExternalReference::double_fp_operation(Token::ADD).address(),
       UNCLASSIFIED,
-      12,
+      13,
       "add_two_doubles");
   Add(ExternalReference::double_fp_operation(Token::SUB).address(),
       UNCLASSIFIED,
-      13,
+      14,
       "sub_two_doubles");
   Add(ExternalReference::double_fp_operation(Token::MUL).address(),
       UNCLASSIFIED,
-      14,
+      15,
       "mul_two_doubles");
   Add(ExternalReference::double_fp_operation(Token::DIV).address(),
       UNCLASSIFIED,
-      15,
+      16,
       "div_two_doubles");
   Add(ExternalReference::double_fp_operation(Token::MOD).address(),
       UNCLASSIFIED,
-      16,
+      17,
       "mod_two_doubles");
   Add(ExternalReference::compare_doubles().address(),
       UNCLASSIFIED,
-      17,
+      18,
       "compare_doubles");
 #ifdef V8_NATIVE_REGEXP
   Add(ExternalReference::re_case_insensitive_compare_uc16().address(),
       UNCLASSIFIED,
-      18,
+      19,
       "NativeRegExpMacroAssembler::CaseInsensitiveCompareUC16()");
   Add(ExternalReference::re_check_stack_guard_state().address(),
       UNCLASSIFIED,
-      19,
+      20,
       "RegExpMacroAssembler*::CheckStackGuardState()");
   Add(ExternalReference::re_grow_stack().address(),
       UNCLASSIFIED,
-      20,
+      21,
       "NativeRegExpMacroAssembler::GrowStack()");
 #endif
 }
index 1646ab7cadeab7727f1772b291a74438356ca3e4..c216e37566b575aaa89218f8f5343ccc7c5ee5b7 100644 (file)
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -105,7 +105,7 @@ bool V8::Initialize(GenericDeserializer *des) {
 
   // Deserializing may put strange things in the root array's copy of the
   // stack guard.
-  Heap::SetStackLimit(StackGuard::jslimit());
+  Heap::SetStackLimits();
 
   // Setup the CPU support. Must be done after heap setup and after
   // any deserialization because we have to have the initial heap
index 8590365a175c3e4e07a2195df073e7b93b725619..f444d2cf85f5ca2d56dd14e531d7dd0fabfe85c9 100644 (file)
@@ -320,42 +320,23 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
   __ push(Operand(rbp, kArgumentsOffset));
   __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
 
-  // Check the stack for overflow or a break request.
-  // We need to catch preemptions right here, otherwise an unlucky preemption
-  // could show up as a failed apply.
-  Label retry_preemption;
-  Label no_preemption;
-  __ bind(&retry_preemption);
-  ExternalReference stack_guard_limit =
-      ExternalReference::address_of_stack_guard_limit();
-  __ movq(kScratchRegister, stack_guard_limit);
-  __ movq(rcx, rsp);
-  __ subq(rcx, Operand(kScratchRegister, 0));
-  // rcx contains the difference between the stack limit and the stack top.
-  // We use it below to check that there is enough room for the arguments.
-  __ j(above, &no_preemption);
-
-  // Preemption!
-  // Because runtime functions always remove the receiver from the stack, we
-  // have to fake one to avoid underflowing the stack.
-  __ push(rax);
-  __ Push(Smi::FromInt(0));
-
-  // Do call to runtime routine.
-  __ CallRuntime(Runtime::kStackGuard, 1);
-  __ pop(rax);
-  __ jmp(&retry_preemption);
-
-  __ bind(&no_preemption);
-
+  // Check the stack for overflow. We are not trying need to catch
+  // interruptions (e.g. debug break and preemption) here, so the "real stack
+  // limit" is checked.
   Label okay;
+  __ LoadRoot(kScratchRegister, Heap::kRealStackLimitRootIndex);
+  __ movq(rcx, rsp);
+  // Make rcx the space we have left. The stack might already be overflowed
+  // here which will cause rcx to become negative.
+  __ subq(rcx, kScratchRegister);
   // Make rdx the space we need for the array when it is unrolled onto the
   // stack.
   __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rax, kPointerSizeLog2);
+  // Check if the arguments will overflow the stack.
   __ cmpq(rcx, rdx);
-  __ j(greater, &okay);
+  __ j(greater, &okay);  // Signed comparison.
 
-  // Too bad: Out of stack space.
+  // Out of stack space.
   __ push(Operand(rbp, kFunctionOffset));
   __ push(rax);
   __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
index 88636f843e2835c3d6119fd5f96712266b3db177..43cf9b9b4a05a7b00dda4ec7e7ec282b23dc3463 100644 (file)
@@ -643,10 +643,10 @@ Handle<Object> RegExpMacroAssemblerX64::GetCode(Handle<String> source) {
   Label stack_limit_hit;
   Label stack_ok;
 
-  ExternalReference stack_guard_limit =
-      ExternalReference::address_of_stack_guard_limit();
+  ExternalReference stack_limit =
+      ExternalReference::address_of_stack_limit();
   __ movq(rcx, rsp);
-  __ movq(kScratchRegister, stack_guard_limit);
+  __ movq(kScratchRegister, stack_limit);
   __ subq(rcx, Operand(kScratchRegister, 0));
   // Handle it if the stack pointer is already below the stack limit.
   __ j(below_equal, &stack_limit_hit);
@@ -1196,9 +1196,9 @@ void RegExpMacroAssemblerX64::Drop() {
 void RegExpMacroAssemblerX64::CheckPreemption() {
   // Check for preemption.
   Label no_preempt;
-  ExternalReference stack_guard_limit =
-      ExternalReference::address_of_stack_guard_limit();
-  __ load_rax(stack_guard_limit);
+  ExternalReference stack_limit =
+      ExternalReference::address_of_stack_limit();
+  __ load_rax(stack_limit);
   __ cmpq(rsp, rax);
   __ j(above, &no_preempt);
 
index 656a456faa09d3c602da284225347bb18cc3df34..7b2b91dfac694e560045fb18586e690b54cd5f18 100644 (file)
@@ -866,6 +866,26 @@ static void DebugEventBreak(v8::DebugEvent event,
 }
 
 
+// Debug event handler which re-issues a debug break until a limit has been
+// reached.
+int max_break_point_hit_count = 0;
+static void DebugEventBreakMax(v8::DebugEvent event,
+                               v8::Handle<v8::Object> exec_state,
+                               v8::Handle<v8::Object> event_data,
+                               v8::Handle<v8::Value> data) {
+  // When hitting a debug event listener there must be a break set.
+  CHECK_NE(v8::internal::Debug::break_id(), 0);
+
+  if (event == v8::Break && break_point_hit_count < max_break_point_hit_count) {
+    // Count the number of breaks.
+    break_point_hit_count++;
+
+    // Set the break flag again to come back here as soon as possible.
+    v8::Debug::DebugBreak();
+  }
+}
+
+
 // --- M e s s a g e   C a l l b a c k
 
 
@@ -5438,3 +5458,36 @@ TEST(GetMirror) {
   v8::Handle<v8::Value> result = run_test->Call(env->Global(), 1, &obj);
   CHECK(result->IsTrue());
 }
+
+
+// Test that the debug break flag works with function.apply.
+TEST(DebugBreakFunctionApply) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Create a function for testing breaking in apply.
+  v8::Local<v8::Function> foo = CompileFunction(
+      &env,
+      "function baz(x) { }"
+      "function bar(x) { baz(); }"
+      "function foo(){ bar.apply(this, [1]); }",
+      "foo");
+
+  // Register a debug event listener which steps and counts.
+  v8::Debug::SetDebugEventListener(DebugEventBreakMax);
+
+  // Set the debug break flag before calling the code using function.apply.
+  v8::Debug::DebugBreak();
+
+  // Limit the number of debug breaks. This is a regression test for issue 493
+  // where this test would enter an infinite loop.
+  break_point_hit_count = 0;
+  max_break_point_hit_count = 10000;  // 10000 => infinite loop.
+  foo->Call(env->Global(), 0, NULL);
+
+  // When keeping the debug break several break will happen.
+  CHECK_EQ(3, break_point_hit_count);
+
+  v8::Debug::SetDebugEventListener(NULL);
+  CheckDebuggerUnloaded();
+}
index 2a58ac8747310943e05ad7684bd119135194dfde..4bf29ff14f0bd257122c4601b665c0322ef497fb 100644 (file)
@@ -123,13 +123,17 @@ TEST(ExternalReferenceEncoder) {
       ExternalReference::the_hole_value_location();
   CHECK_EQ(make_code(UNCLASSIFIED, 2),
            encoder.Encode(the_hole_value_location.address()));
-  ExternalReference stack_guard_limit_address =
-      ExternalReference::address_of_stack_guard_limit();
+  ExternalReference stack_limit_address =
+      ExternalReference::address_of_stack_limit();
   CHECK_EQ(make_code(UNCLASSIFIED, 4),
-           encoder.Encode(stack_guard_limit_address.address()));
-  CHECK_EQ(make_code(UNCLASSIFIED, 10),
+           encoder.Encode(stack_limit_address.address()));
+  ExternalReference real_stack_limit_address =
+      ExternalReference::address_of_real_stack_limit();
+  CHECK_EQ(make_code(UNCLASSIFIED, 5),
+           encoder.Encode(real_stack_limit_address.address()));
+  CHECK_EQ(make_code(UNCLASSIFIED, 11),
            encoder.Encode(ExternalReference::debug_break().address()));
-  CHECK_EQ(make_code(UNCLASSIFIED, 6),
+  CHECK_EQ(make_code(UNCLASSIFIED, 7),
            encoder.Encode(ExternalReference::new_space_start().address()));
   CHECK_EQ(make_code(UNCLASSIFIED, 3),
            encoder.Encode(ExternalReference::roots_address().address()));
@@ -158,12 +162,14 @@ TEST(ExternalReferenceDecoder) {
            decoder.Decode(make_code(UNCLASSIFIED, 1)));
   CHECK_EQ(ExternalReference::the_hole_value_location().address(),
            decoder.Decode(make_code(UNCLASSIFIED, 2)));
-  CHECK_EQ(ExternalReference::address_of_stack_guard_limit().address(),
+  CHECK_EQ(ExternalReference::address_of_stack_limit().address(),
            decoder.Decode(make_code(UNCLASSIFIED, 4)));
+  CHECK_EQ(ExternalReference::address_of_real_stack_limit().address(),
+           decoder.Decode(make_code(UNCLASSIFIED, 5)));
   CHECK_EQ(ExternalReference::debug_break().address(),
-           decoder.Decode(make_code(UNCLASSIFIED, 10)));
+           decoder.Decode(make_code(UNCLASSIFIED, 11)));
   CHECK_EQ(ExternalReference::new_space_start().address(),
-           decoder.Decode(make_code(UNCLASSIFIED, 6)));
+           decoder.Decode(make_code(UNCLASSIFIED, 7)));
 }