Fix the stack limits setting API so it is usable.
authorerik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 1 Oct 2009 10:33:05 +0000 (10:33 +0000)
committererik.corry@gmail.com <erik.corry@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 1 Oct 2009 10:33:05 +0000 (10:33 +0000)
Review URL: http://codereview.chromium.org/242074

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

13 files changed:
include/v8.h
src/api.cc
src/arm/simulator-arm.cc
src/arm/simulator-arm.h
src/compiler.cc
src/execution.cc
src/execution.h
src/ia32/simulator-ia32.h
src/parser.cc
src/v8.cc
src/v8threads.cc
src/x64/simulator-x64.h
test/cctest/test-api.cc

index 2232cb9..4992d75 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2007-2008 the V8 project authors. All rights reserved.
+// Copyright 2007-2009 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
@@ -1981,8 +1981,13 @@ Handle<Boolean> V8EXPORT False();
 
 
 /**
- * A set of constraints that specifies the limits of the runtime's
- * memory use.
+ * A set of constraints that specifies the limits of the runtime's memory use.
+ * You must set the heap size before initializing the VM - the size cannot be
+ * adjusted after the VM is initialized.
+ *
+ * If you are using threads then you should hold the V8::Locker lock while
+ * setting the stack limit and you must set a non-default stack limit separately
+ * for each thread.
  */
 class V8EXPORT ResourceConstraints {
  public:
@@ -1992,6 +1997,7 @@ class V8EXPORT ResourceConstraints {
   int max_old_space_size() const { return max_old_space_size_; }
   void set_max_old_space_size(int value) { max_old_space_size_ = value; }
   uint32_t* stack_limit() const { return stack_limit_; }
+  // Sets an address beyond which the VM's stack may not grow.
   void set_stack_limit(uint32_t* value) { stack_limit_ = value; }
  private:
   int max_young_space_size_;
@@ -2199,7 +2205,8 @@ class V8EXPORT V8 {
 
   /**
    * Initializes from snapshot if possible. Otherwise, attempts to
-   * initialize from scratch.
+   * initialize from scratch.  This function is called implicitly if
+   * you use the API without calling it first.
    */
   static bool Initialize();
 
@@ -2749,15 +2756,15 @@ class Internals {
     return ((reinterpret_cast<intptr_t>(value) & kHeapObjectTagMask) ==
             kHeapObjectTag);
   }
-  
+
   static inline bool HasSmiTag(internal::Object* value) {
     return ((reinterpret_cast<intptr_t>(value) & kSmiTagMask) == kSmiTag);
   }
-  
+
   static inline int SmiValue(internal::Object* value) {
     return static_cast<int>(reinterpret_cast<intptr_t>(value)) >> kSmiTagSize;
   }
-  
+
   static inline bool IsExternalTwoByteString(int instance_type) {
     int representation = (instance_type & kFullStringRepresentationMask);
     return representation == kExternalTwoByteRepresentationTag;
index 7c1bd60..90da1bc 100644 (file)
@@ -342,9 +342,12 @@ ResourceConstraints::ResourceConstraints()
 
 
 bool SetResourceConstraints(ResourceConstraints* constraints) {
-  bool result = i::Heap::ConfigureHeap(constraints->max_young_space_size(),
-                                       constraints->max_old_space_size());
-  if (!result) return false;
+  int semispace_size = constraints->max_young_space_size();
+  int old_gen_size = constraints->max_old_space_size();
+  if (semispace_size != 0 || old_gen_size != 0) {
+    bool result = i::Heap::ConfigureHeap(semispace_size, old_gen_size);
+    if (!result) return false;
+  }
   if (constraints->stack_limit() != NULL) {
     uintptr_t limit = reinterpret_cast<uintptr_t>(constraints->stack_limit());
     i::StackGuard::SetStackLimit(limit);
index 70dfcd2..22bec82 100644 (file)
@@ -409,7 +409,7 @@ void Simulator::Initialize() {
 
 
 Simulator::Simulator() {
-  ASSERT(initialized_);
+  Initialize();
   // Setup simulator support first. Some of this information is needed to
   // setup the architecture state.
   size_t stack_size = 1 * 1024*1024;  // allocate 1MB for stack
@@ -501,6 +501,7 @@ void* Simulator::RedirectExternalReference(void* external_function,
 
 // Get the active Simulator for the current thread.
 Simulator* Simulator::current() {
+  Initialize();
   Simulator* sim = reinterpret_cast<Simulator*>(
       v8::internal::Thread::GetThreadLocal(simulator_key));
   if (sim == NULL) {
index 3917d6a..ff6bbf4 100644 (file)
 #ifndef V8_ARM_SIMULATOR_ARM_H_
 #define V8_ARM_SIMULATOR_ARM_H_
 
+#include "allocation.h"
+
 #if defined(__arm__)
 
 // When running without a simulator we call the entry directly.
 #define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
   (entry(p0, p1, p2, p3, p4))
 
-// Calculated the stack limit beyond which we will throw stack overflow errors.
-// This macro must be called from a C++ method. It relies on being able to take
-// the address of "this" to get a value on the current execution stack and then
-// calculates the stack limit based on that value.
-#define GENERATED_CODE_STACK_LIMIT(limit) \
-  (reinterpret_cast<uintptr_t>(this) - limit)
+// The stack limit beyond which we will throw stack overflow errors in
+// generated code. Because generated code on arm uses the C stack, we
+// just use the C stack limit.
+class SimulatorStack : public v8::internal::AllStatic {
+ public:
+  static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+    return c_limit;
+  }
+};
 
 
 // Call the generated regexp code directly. The entry function pointer should
       assembler::arm::Simulator::current()->Call(FUNCTION_ADDR(entry), 5, \
                                                  p0, p1, p2, p3, p4))
 
-// The simulator has its own stack. Thus it has a different stack limit from
-// the C-based native code.
-#define GENERATED_CODE_STACK_LIMIT(limit) \
-  (assembler::arm::Simulator::current()->StackLimit())
-
-
 #define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6) \
   assembler::arm::Simulator::current()->Call( \
     FUNCTION_ADDR(entry), 7, p0, p1, p2, p3, p4, p5, p6)
@@ -219,6 +218,20 @@ class Simulator {
 
 } }  // namespace assembler::arm
 
+
+// The simulator has its own stack. Thus it has a different stack limit from
+// the C-based native code.  Setting the c_limit to indicate a very small
+// stack cause stack overflow errors, since the simulator ignores the input.
+// This is unlikely to be an issue in practice, though it might cause testing
+// trouble down the line.
+class SimulatorStack : public v8::internal::AllStatic {
+ public:
+  static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+    return assembler::arm::Simulator::current()->StackLimit();
+  }
+};
+
+
 #endif  // defined(__arm__)
 
 #endif  // V8_ARM_SIMULATOR_ARM_H_
index bbb5b45..6ba7a9a 100644 (file)
@@ -104,8 +104,6 @@ static Handle<JSFunction> MakeFunction(bool is_global,
                                        ScriptDataImpl* pre_data) {
   CompilationZoneScope zone_scope(DELETE_ON_EXIT);
 
-  // Make sure we have an initial stack limit.
-  StackGuard guard;
   PostponeInterruptsScope postpone;
 
   ASSERT(!i::Top::global_context().is_null());
@@ -334,8 +332,6 @@ bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared,
   // The VM is in the COMPILER state until exiting this function.
   VMState state(COMPILER);
 
-  // Make sure we have an initial stack limit.
-  StackGuard guard;
   PostponeInterruptsScope postpone;
 
   // Compute name, source code and script data.
index 6a758a9..8bc6b74 100644 (file)
@@ -61,9 +61,6 @@ static Handle<Object> Invoke(bool construct,
   // Entering JavaScript.
   VMState state(JS);
 
-  // Guard the stack against too much recursion.
-  StackGuard guard;
-
   // Placeholder for return value.
   Object* value = reinterpret_cast<Object*>(kZapValue);
 
@@ -217,55 +214,6 @@ Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) {
 StackGuard::ThreadLocal StackGuard::thread_local_;
 
 
-StackGuard::StackGuard() {
-  // NOTE: Overall the StackGuard code assumes that the stack grows towards
-  // lower addresses.
-  ExecutionAccess access;
-  if (thread_local_.nesting_++ == 0) {
-    // Initial StackGuard is being set. We will set the stack limits based on
-    // the current stack pointer allowing the stack to grow kLimitSize from
-    // here.
-
-    // Ensure that either the stack limits are unset (kIllegalLimit) or that
-    // they indicate a pending interruption. The interrupt limit will be
-    // temporarily reset through the code below and reestablished if the
-    // interrupt flags indicate that an interrupt is pending.
-    ASSERT(thread_local_.jslimit_ == kIllegalLimit ||
-           (thread_local_.jslimit_ == kInterruptLimit &&
-            thread_local_.interrupt_flags_ != 0));
-    ASSERT(thread_local_.climit_ == kIllegalLimit ||
-           (thread_local_.climit_ == kInterruptLimit &&
-            thread_local_.interrupt_flags_ != 0));
-
-    uintptr_t limit = GENERATED_CODE_STACK_LIMIT(kLimitSize);
-    thread_local_.initial_jslimit_ = thread_local_.jslimit_ = limit;
-    Heap::SetStackLimit(limit);
-    // NOTE: The check for overflow is not safe as there is no guarantee that
-    // the running thread has its stack in all memory up to address 0x00000000.
-    thread_local_.initial_climit_ = thread_local_.climit_ =
-        reinterpret_cast<uintptr_t>(this) >= kLimitSize ?
-            reinterpret_cast<uintptr_t>(this) - kLimitSize : 0;
-
-    if (thread_local_.interrupt_flags_ != 0) {
-      set_limits(kInterruptLimit, access);
-    }
-  }
-  // Ensure that proper limits have been set.
-  ASSERT(thread_local_.jslimit_ != kIllegalLimit &&
-         thread_local_.climit_ != kIllegalLimit);
-  ASSERT(thread_local_.initial_jslimit_ != kIllegalLimit &&
-         thread_local_.initial_climit_ != kIllegalLimit);
-}
-
-
-StackGuard::~StackGuard() {
-  ExecutionAccess access;
-  if (--thread_local_.nesting_ == 0) {
-    set_limits(kIllegalLimit, access);
-  }
-}
-
-
 bool StackGuard::IsStackOverflow() {
   ExecutionAccess access;
   return (thread_local_.jslimit_ != kInterruptLimit &&
@@ -285,15 +233,16 @@ void StackGuard::SetStackLimit(uintptr_t limit) {
   ExecutionAccess access;
   // 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_) {
-    thread_local_.jslimit_ = limit;
-    Heap::SetStackLimit(limit);
+    thread_local_.jslimit_ = jslimit;
+    Heap::SetStackLimit(jslimit);
   }
   if (thread_local_.climit_ == thread_local_.initial_climit_) {
     thread_local_.climit_ = limit;
   }
   thread_local_.initial_climit_ = limit;
-  thread_local_.initial_jslimit_ = limit;
+  thread_local_.initial_jslimit_ = jslimit;
 }
 
 
@@ -407,7 +356,58 @@ char* StackGuard::RestoreStackGuard(char* from) {
 }
 
 
+static internal::Thread::LocalStorageKey stack_limit_key =
+    internal::Thread::CreateThreadLocalKey();
+
+
 void StackGuard::FreeThreadResources() {
+  Thread::SetThreadLocal(
+      stack_limit_key,
+      reinterpret_cast<void*>(thread_local_.initial_climit_));
+}
+
+
+void StackGuard::ThreadLocal::Clear() {
+  initial_jslimit_ = kIllegalLimit;
+  jslimit_ = kIllegalLimit;
+  initial_climit_ = kIllegalLimit;
+  climit_ = kIllegalLimit;
+  nesting_ = 0;
+  postpone_interrupts_nesting_ = 0;
+  interrupt_flags_ = 0;
+  Heap::SetStackLimit(kIllegalLimit);
+}
+
+
+void StackGuard::ThreadLocal::Initialize() {
+  if (initial_climit_ == kIllegalLimit) {
+    // Takes the address of the limit variable in order to find out where
+    // the top of stack is right now.
+    intptr_t limit = reinterpret_cast<intptr_t>(&limit) - kLimitSize;
+    initial_jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
+    jslimit_ = SimulatorStack::JsLimitFromCLimit(limit);
+    initial_climit_ = limit;
+    climit_ = limit;
+    Heap::SetStackLimit(SimulatorStack::JsLimitFromCLimit(limit));
+  }
+  nesting_ = 0;
+  postpone_interrupts_nesting_ = 0;
+  interrupt_flags_ = 0;
+}
+
+
+void StackGuard::ClearThread(const ExecutionAccess& lock) {
+  thread_local_.Clear();
+}
+
+
+void StackGuard::InitThread(const ExecutionAccess& lock) {
+  thread_local_.Initialize();
+  void* stored_limit = Thread::GetThreadLocal(stack_limit_key);
+  // You should hold the ExecutionAccess lock when you call this.
+  if (stored_limit != NULL) {
+    StackGuard::SetStackLimit(reinterpret_cast<intptr_t>(stored_limit));
+  }
 }
 
 
index d2e6ec0..55307f7 100644 (file)
@@ -141,14 +141,13 @@ class Execution : public AllStatic {
 class ExecutionAccess;
 
 
-// Stack guards are used to limit the number of nested invocations of
-// JavaScript and the stack size used in each invocation.
-class StackGuard BASE_EMBEDDED {
+// StackGuard contains the handling of the limits that are used to limit the
+// number of nested invocations of JavaScript and the stack size used in each
+// invocation.
+class StackGuard : public AllStatic {
  public:
-  StackGuard();
-
-  ~StackGuard();
-
+  // Pass the address beyond which the stack should not grow.  The stack
+  // is assumed to grow downwards.
   static void SetStackLimit(uintptr_t limit);
 
   static Address address_of_jslimit() {
@@ -160,6 +159,12 @@ class StackGuard BASE_EMBEDDED {
   static char* RestoreStackGuard(char* from);
   static int ArchiveSpacePerThread();
   static void FreeThreadResources();
+  // Sets up the default stack guard for this thread if it has not
+  // already been set up.
+  static void InitThread(const ExecutionAccess& lock);
+  // Clears the stack guard for this thread so it does not look as if
+  // it has been set up.
+  static void ClearThread(const ExecutionAccess& lock);
 
   static bool IsStackOverflow();
   static bool IsPreempted();
@@ -176,6 +181,13 @@ class StackGuard BASE_EMBEDDED {
 #endif
   static void Continue(InterruptFlag after_what);
 
+  // This provides an asynchronous read of the stack limit 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_;
   }
@@ -184,13 +196,6 @@ class StackGuard BASE_EMBEDDED {
   // You should hold the ExecutionAccess lock when calling this method.
   static bool IsSet(const ExecutionAccess& lock);
 
-  // This provides an asynchronous read of the stack limit 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_;
-  }
-
   // You should hold the ExecutionAccess lock when calling this method.
   static void set_limits(uintptr_t value, const ExecutionAccess& lock) {
     Heap::SetStackLimit(value);
@@ -201,14 +206,9 @@ class StackGuard BASE_EMBEDDED {
   // Reset limits to initial values. For example after handling interrupt.
   // You should hold the ExecutionAccess lock when calling this method.
   static void reset_limits(const ExecutionAccess& lock) {
-    if (thread_local_.nesting_ == 0) {
-      // No limits have been set yet.
-      set_limits(kIllegalLimit, lock);
-    } else {
-      thread_local_.jslimit_ = thread_local_.initial_jslimit_;
-      Heap::SetStackLimit(thread_local_.jslimit_);
-      thread_local_.climit_ = thread_local_.initial_climit_;
-    }
+    thread_local_.jslimit_ = thread_local_.initial_jslimit_;
+    Heap::SetStackLimit(thread_local_.jslimit_);
+    thread_local_.climit_ = thread_local_.initial_climit_;
   }
 
   // Enable or disable interrupts.
@@ -218,24 +218,19 @@ class StackGuard BASE_EMBEDDED {
   static const uintptr_t kLimitSize = kPointerSize * 128 * KB;
 #ifdef V8_TARGET_ARCH_X64
   static const uintptr_t kInterruptLimit = V8_UINT64_C(0xfffffffffffffffe);
-  static const uintptr_t kIllegalLimit = V8_UINT64_C(0xffffffffffffffff);
+  static const uintptr_t kIllegalLimit = V8_UINT64_C(0xfffffffffffffff8);
 #else
   static const uintptr_t kInterruptLimit = 0xfffffffe;
-  static const uintptr_t kIllegalLimit = 0xffffffff;
+  static const uintptr_t kIllegalLimit = 0xfffffff8;
 #endif
 
   class ThreadLocal {
    public:
-    ThreadLocal()
-      : initial_jslimit_(kIllegalLimit),
-        jslimit_(kIllegalLimit),
-        initial_climit_(kIllegalLimit),
-        climit_(kIllegalLimit),
-        nesting_(0),
-        postpone_interrupts_nesting_(0),
-        interrupt_flags_(0) {
-      Heap::SetStackLimit(kIllegalLimit);
-    }
+    ThreadLocal() { Clear(); }
+    // You should hold the ExecutionAccess lock when you call Initialize or
+    // Clear.
+    void Initialize();
+    void Clear();
     uintptr_t initial_jslimit_;
     uintptr_t jslimit_;
     uintptr_t initial_climit_;
index 3bed268..8fa4287 100644 (file)
 #ifndef V8_IA32_SIMULATOR_IA32_H_
 #define V8_IA32_SIMULATOR_IA32_H_
 
+#include "allocation.h"
 
 // Since there is no simulator for the ia32 architecture the only thing we can
 // do is to call the entry directly.
 #define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
   entry(p0, p1, p2, p3, p4);
 
-// Calculated the stack limit beyond which we will throw stack overflow errors.
-// This macro must be called from a C++ method. It relies on being able to take
-// the address of "this" to get a value on the current execution stack and then
-// calculates the stack limit based on that value.
-// NOTE: The check for overflow is not safe as there is no guarantee that the
-// running thread has its stack in all memory up to address 0x00000000.
-#define GENERATED_CODE_STACK_LIMIT(limit) \
-  (reinterpret_cast<uintptr_t>(this) >= limit ? \
-      reinterpret_cast<uintptr_t>(this) - limit : 0)
+// The stack limit beyond which we will throw stack overflow errors in
+// generated code. Because generated code on ia32 uses the C stack, we
+// just use the C stack limit.
+class SimulatorStack : public v8::internal::AllStatic {
+ public:
+  static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+    return c_limit;
+  }
+};
 
 // Call the generated regexp code directly. The entry function pointer should
 // expect seven int/pointer sized arguments and return an int.
index 757b042..4dbd472 100644 (file)
@@ -1184,7 +1184,6 @@ Parser::Parser(Handle<Script> script,
 bool Parser::PreParseProgram(Handle<String> source,
                              unibrow::CharacterStream* stream) {
   HistogramTimerScope timer(&Counters::pre_parse);
-  StackGuard guard;
   AssertNoZoneAllocation assert_no_zone_allocation;
   AssertNoAllocation assert_no_allocation;
   NoHandleAllocation no_handle_allocation;
@@ -4775,8 +4774,6 @@ bool ParseRegExp(FlatStringReader* input,
                  bool multiline,
                  RegExpCompileData* result) {
   ASSERT(result != NULL);
-  // Make sure we have a stack guard.
-  StackGuard guard;
   RegExpParser parser(input, &result->error, multiline);
   RegExpTree* tree = parser.ParsePattern();
   if (parser.failed()) {
index a204158..f976536 100644 (file)
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -71,6 +71,14 @@ bool V8::Initialize(Deserializer *des) {
   ::assembler::arm::Simulator::Initialize();
 #endif
 
+  { // NOLINT
+    // Ensure that the thread has a valid stack guard.  The v8::Locker object
+    // will ensure this too, but we don't have to use lockers if we are only
+    // using one thread.
+    ExecutionAccess lock;
+    StackGuard::InitThread(lock);
+  }
+
   // Setup the object heap
   ASSERT(!Heap::HasBeenSetup());
   if (!Heap::Setup(create_heap_objects)) {
index c774449..375afb1 100644 (file)
@@ -60,6 +60,10 @@ Locker::Locker() : has_lock_(false), top_level_(true) {
     // get the saved state for this thread and restore it.
     if (internal::ThreadManager::RestoreThread()) {
       top_level_ = false;
+    } else {
+      internal::ExecutionAccess access;
+      internal::StackGuard::ClearThread(access);
+      internal::StackGuard::InitThread(access);
     }
   }
   ASSERT(internal::ThreadManager::IsLockedByCurrentThread());
@@ -141,6 +145,8 @@ bool ThreadManager::RestoreThread() {
   ThreadState* state =
       reinterpret_cast<ThreadState*>(Thread::GetThreadLocal(thread_state_key));
   if (state == NULL) {
+    // This is a new thread.
+    StackGuard::InitThread(access);
     return false;
   }
   char* from = state->data();
index 184c166..998c909 100644 (file)
@@ -28,6 +28,7 @@
 #ifndef V8_X64_SIMULATOR_X64_H_
 #define V8_X64_SIMULATOR_X64_H_
 
+#include "allocation.h"
 
 // Since there is no simulator for the ia32 architecture the only thing we can
 // do is to call the entry directly.
 #define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
   entry(p0, p1, p2, p3, p4);
 
-// Calculated the stack limit beyond which we will throw stack overflow errors.
-// This macro must be called from a C++ method. It relies on being able to take
-// the address of "this" to get a value on the current execution stack and then
-// calculates the stack limit based on that value.
-// NOTE: The check for overflow is not safe as there is no guarantee that the
-// running thread has its stack in all memory up to address 0x00000000.
-#define GENERATED_CODE_STACK_LIMIT(limit) \
-  (reinterpret_cast<uintptr_t>(this) >= limit ? \
-      reinterpret_cast<uintptr_t>(this) - limit : 0)
+// The stack limit beyond which we will throw stack overflow errors in
+// generated code. Because generated code on x64 uses the C stack, we
+// just use the C stack limit.
+class SimulatorStack : public v8::internal::AllStatic {
+ public:
+  static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
+    return c_limit;
+  }
+};
 
 // Call the generated regexp code directly. The entry function pointer should
 // expect seven int/pointer sized arguments and return an int.
index b76525a..f430cbd 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2007-2008 the V8 project authors. All rights reserved.
+// Copyright 2007-2009 the V8 project authors. All rights reserved.
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions are
 // met:
@@ -31,6 +31,7 @@
 
 #include "api.h"
 #include "compilation-cache.h"
+#include "execution.h"
 #include "snapshot.h"
 #include "platform.h"
 #include "top.h"
@@ -7918,3 +7919,77 @@ THREADED_TEST(IdleNotification) {
   for (int i = 0; i < 100; i++) v8::V8::IdleNotification(true);
   for (int i = 0; i < 100; i++) v8::V8::IdleNotification(false);
 }
+
+
+static uint32_t* stack_limit;
+
+static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) {
+  stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit());
+  return v8::Undefined();
+}
+
+
+// Uses the address of a local variable to determine the stack top now.
+// Given a size, returns an address that is that far from the current
+// top of stack.
+static uint32_t* ComputeStackLimit(uint32_t size) {
+  uint32_t* answer = &size - (size / sizeof(size));
+  // If the size is very large and the stack is very near the bottom of
+  // memory then the calculation above may wrap around and give an address
+  // that is above the (downwards-growing) stack.  In that case we return
+  // a very low address.
+  if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size));
+  return answer;
+}
+
+
+TEST(SetResourceConstraints) {
+  static const int K = 1024;
+  uint32_t* set_limit = ComputeStackLimit(128 * K);
+
+  // Set stack limit.
+  v8::ResourceConstraints constraints;
+  constraints.set_stack_limit(set_limit);
+  CHECK(v8::SetResourceConstraints(&constraints));
+
+  // Execute a script.
+  v8::HandleScope scope;
+  LocalContext env;
+  Local<v8::FunctionTemplate> fun_templ =
+      v8::FunctionTemplate::New(GetStackLimitCallback);
+  Local<Function> fun = fun_templ->GetFunction();
+  env->Global()->Set(v8_str("get_stack_limit"), fun);
+  CompileRun("get_stack_limit();");
+
+  CHECK(stack_limit == set_limit);
+}
+
+
+TEST(SetResourceConstraintsInThread) {
+  uint32_t* set_limit;
+  {
+    v8::Locker locker;
+    static const int K = 1024;
+    set_limit = ComputeStackLimit(128 * K);
+
+    // Set stack limit.
+    v8::ResourceConstraints constraints;
+    constraints.set_stack_limit(set_limit);
+    CHECK(v8::SetResourceConstraints(&constraints));
+
+    // Execute a script.
+    v8::HandleScope scope;
+    LocalContext env;
+    Local<v8::FunctionTemplate> fun_templ =
+        v8::FunctionTemplate::New(GetStackLimitCallback);
+    Local<Function> fun = fun_templ->GetFunction();
+    env->Global()->Set(v8_str("get_stack_limit"), fun);
+    CompileRun("get_stack_limit();");
+
+    CHECK(stack_limit == set_limit);
+  }
+  {
+    v8::Locker locker;
+    CHECK(stack_limit == set_limit);
+  }
+}