deps: update v8 to 4.3.61.21
[platform/upstream/nodejs.git] / deps / v8 / test / cctest / test-thread-termination.cc
index 190fc7b..0d20e0f 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-#include "v8.h"
-#include "platform.h"
-#include "cctest.h"
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
 
+#include "src/base/platform/platform.h"
 
-v8::internal::Semaphore* semaphore = NULL;
 
+v8::base::Semaphore* semaphore = NULL;
 
-v8::Handle<v8::Value> Signal(const v8::Arguments& args) {
+
+void Signal(const v8::FunctionCallbackInfo<v8::Value>& args) {
   semaphore->Signal();
-  return v8::Undefined();
 }
 
 
-v8::Handle<v8::Value> TerminateCurrentThread(const v8::Arguments& args) {
-  CHECK(!v8::V8::IsExecutionTerminating());
-  v8::V8::TerminateExecution();
-  return v8::Undefined();
+void TerminateCurrentThread(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
+  v8::V8::TerminateExecution(args.GetIsolate());
 }
 
 
-v8::Handle<v8::Value> Fail(const v8::Arguments& args) {
+void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) {
   CHECK(false);
-  return v8::Undefined();
 }
 
 
-v8::Handle<v8::Value> Loop(const v8::Arguments& args) {
-  CHECK(!v8::V8::IsExecutionTerminating());
-  v8::Handle<v8::String> source =
-      v8::String::New("try { doloop(); fail(); } catch(e) { fail(); }");
+void Loop(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
+  v8::Handle<v8::String> source = v8::String::NewFromUtf8(
+      args.GetIsolate(), "try { doloop(); fail(); } catch(e) { fail(); }");
   v8::Handle<v8::Value> result = v8::Script::Compile(source)->Run();
   CHECK(result.IsEmpty());
-  CHECK(v8::V8::IsExecutionTerminating());
-  return v8::Undefined();
+  CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
 }
 
 
-v8::Handle<v8::Value> DoLoop(const v8::Arguments& args) {
+void DoLoop(const v8::FunctionCallbackInfo<v8::Value>& args) {
   v8::TryCatch try_catch;
-  CHECK(!v8::V8::IsExecutionTerminating());
-  v8::Script::Compile(v8::String::New("function f() {"
-                                      "  var term = true;"
-                                      "  try {"
-                                      "    while(true) {"
-                                      "      if (term) terminate();"
-                                      "      term = false;"
-                                      "    }"
-                                      "    fail();"
-                                      "  } catch(e) {"
-                                      "    fail();"
-                                      "  }"
-                                      "}"
-                                      "f()"))->Run();
+  CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
+  v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
+                                              "function f() {"
+                                              "  var term = true;"
+                                              "  try {"
+                                              "    while(true) {"
+                                              "      if (term) terminate();"
+                                              "      term = false;"
+                                              "    }"
+                                              "    fail();"
+                                              "  } catch(e) {"
+                                              "    fail();"
+                                              "  }"
+                                              "}"
+                                              "f()"))->Run();
   CHECK(try_catch.HasCaught());
   CHECK(try_catch.Exception()->IsNull());
   CHECK(try_catch.Message().IsEmpty());
   CHECK(!try_catch.CanContinue());
-  CHECK(v8::V8::IsExecutionTerminating());
-  return v8::Undefined();
+  CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
 }
 
 
-v8::Handle<v8::Value> DoLoopNoCall(const v8::Arguments& args) {
+void DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value>& args) {
   v8::TryCatch try_catch;
-  CHECK(!v8::V8::IsExecutionTerminating());
-  v8::Script::Compile(v8::String::New("var term = true;"
-                                      "while(true) {"
-                                      "  if (term) terminate();"
-                                      "  term = false;"
-                                      "}"))->Run();
+  CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
+  v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
+                                              "var term = true;"
+                                              "while(true) {"
+                                              "  if (term) terminate();"
+                                              "  term = false;"
+                                              "}"))->Run();
   CHECK(try_catch.HasCaught());
   CHECK(try_catch.Exception()->IsNull());
   CHECK(try_catch.Message().IsEmpty());
   CHECK(!try_catch.CanContinue());
-  CHECK(v8::V8::IsExecutionTerminating());
-  return v8::Undefined();
+  CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
 }
 
 
 v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
-    v8::InvocationCallback terminate,
-    v8::InvocationCallback doloop) {
-  v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
-  global->Set(v8::String::New("terminate"),
-              v8::FunctionTemplate::New(terminate));
-  global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
-  global->Set(v8::String::New("loop"), v8::FunctionTemplate::New(Loop));
-  global->Set(v8::String::New("doloop"), v8::FunctionTemplate::New(doloop));
+    v8::Isolate* isolate,
+    v8::FunctionCallback terminate,
+    v8::FunctionCallback doloop) {
+  v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
+  global->Set(v8::String::NewFromUtf8(isolate, "terminate"),
+              v8::FunctionTemplate::New(isolate, terminate));
+  global->Set(v8::String::NewFromUtf8(isolate, "fail"),
+              v8::FunctionTemplate::New(isolate, Fail));
+  global->Set(v8::String::NewFromUtf8(isolate, "loop"),
+              v8::FunctionTemplate::New(isolate, Loop));
+  global->Set(v8::String::NewFromUtf8(isolate, "doloop"),
+              v8::FunctionTemplate::New(isolate, doloop));
   return global;
 }
 
@@ -121,48 +122,48 @@ v8::Handle<v8::ObjectTemplate> CreateGlobalTemplate(
 // Test that a single thread of JavaScript execution can terminate
 // itself.
 TEST(TerminateOnlyV8ThreadFromThreadItself) {
-  v8::HandleScope scope(v8::Isolate::GetCurrent());
+  v8::HandleScope scope(CcTest::isolate());
   v8::Handle<v8::ObjectTemplate> global =
-      CreateGlobalTemplate(TerminateCurrentThread, DoLoop);
-  v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+      CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
+  v8::Handle<v8::Context> context =
+      v8::Context::New(CcTest::isolate(), NULL, global);
   v8::Context::Scope context_scope(context);
-  CHECK(!v8::V8::IsExecutionTerminating());
+  CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
   // Run a loop that will be infinite if thread termination does not work.
-  v8::Handle<v8::String> source =
-      v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
+  v8::Handle<v8::String> source = v8::String::NewFromUtf8(
+      CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
   v8::Script::Compile(source)->Run();
   // Test that we can run the code again after thread termination.
-  CHECK(!v8::V8::IsExecutionTerminating());
+  CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
   v8::Script::Compile(source)->Run();
-  context.Dispose(context->GetIsolate());
 }
 
 
 // Test that a single thread of JavaScript execution can terminate
 // itself in a loop that performs no calls.
 TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) {
-  v8::HandleScope scope(v8::Isolate::GetCurrent());
-  v8::Handle<v8::ObjectTemplate> global =
-      CreateGlobalTemplate(TerminateCurrentThread, DoLoopNoCall);
-  v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+  v8::HandleScope scope(CcTest::isolate());
+  v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(
+      CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall);
+  v8::Handle<v8::Context> context =
+      v8::Context::New(CcTest::isolate(), NULL, global);
   v8::Context::Scope context_scope(context);
-  CHECK(!v8::V8::IsExecutionTerminating());
+  CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
   // Run a loop that will be infinite if thread termination does not work.
-  v8::Handle<v8::String> source =
-      v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
+  v8::Handle<v8::String> source = v8::String::NewFromUtf8(
+      CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
   v8::Script::Compile(source)->Run();
-  CHECK(!v8::V8::IsExecutionTerminating());
+  CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
   // Test that we can run the code again after thread termination.
   v8::Script::Compile(source)->Run();
-  context.Dispose(context->GetIsolate());
 }
 
 
-class TerminatorThread : public v8::internal::Thread {
+class TerminatorThread : public v8::base::Thread {
  public:
   explicit TerminatorThread(i::Isolate* isolate)
-      : Thread("TerminatorThread"),
-        isolate_(reinterpret_cast<v8::Isolate*>(isolate)) { }
+      : Thread(Options("TerminatorThread")),
+        isolate_(reinterpret_cast<v8::Isolate*>(isolate)) {}
   void Run() {
     semaphore->Wait();
     CHECK(!v8::V8::IsExecutionTerminating(isolate_));
@@ -177,162 +178,134 @@ class TerminatorThread : public v8::internal::Thread {
 // Test that a single thread of JavaScript execution can be terminated
 // from the side by another thread.
 TEST(TerminateOnlyV8ThreadFromOtherThread) {
-  semaphore = v8::internal::OS::CreateSemaphore(0);
-  TerminatorThread thread(i::Isolate::Current());
+  semaphore = new v8::base::Semaphore(0);
+  TerminatorThread thread(CcTest::i_isolate());
   thread.Start();
 
-  v8::HandleScope scope(v8::Isolate::GetCurrent());
-  v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(Signal, DoLoop);
-  v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+  v8::HandleScope scope(CcTest::isolate());
+  v8::Handle<v8::ObjectTemplate> global =
+      CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
+  v8::Handle<v8::Context> context =
+      v8::Context::New(CcTest::isolate(), NULL, global);
   v8::Context::Scope context_scope(context);
-  CHECK(!v8::V8::IsExecutionTerminating());
+  CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
   // Run a loop that will be infinite if thread termination does not work.
-  v8::Handle<v8::String> source =
-      v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
+  v8::Handle<v8::String> source = v8::String::NewFromUtf8(
+      CcTest::isolate(), "try { loop(); fail(); } catch(e) { fail(); }");
+  i::FLAG_turbo_osr = false;  // TODO(titzer): interrupts in TF loops.
   v8::Script::Compile(source)->Run();
 
   thread.Join();
   delete semaphore;
   semaphore = NULL;
-  context.Dispose(context->GetIsolate());
-}
-
-
-class LoopingThread : public v8::internal::Thread {
- public:
-  LoopingThread() : Thread("LoopingThread") { }
-  void Run() {
-    v8::Locker locker(CcTest::default_isolate());
-    v8::HandleScope scope(CcTest::default_isolate());
-    v8_thread_id_ = v8::V8::GetCurrentThreadId();
-    v8::Handle<v8::ObjectTemplate> global =
-        CreateGlobalTemplate(Signal, DoLoop);
-    v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
-    v8::Context::Scope context_scope(context);
-    CHECK(!v8::V8::IsExecutionTerminating());
-    // Run a loop that will be infinite if thread termination does not work.
-    v8::Handle<v8::String> source =
-        v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
-    v8::Script::Compile(source)->Run();
-    context.Dispose(context->GetIsolate());
-  }
-
-  int GetV8ThreadId() { return v8_thread_id_; }
-
- private:
-  int v8_thread_id_;
-};
-
-
-// Test that multiple threads using default isolate can be terminated
-// from another thread when using Lockers and preemption.
-TEST(TerminateMultipleV8ThreadsDefaultIsolate) {
-  {
-    v8::Locker locker(CcTest::default_isolate());
-    v8::V8::Initialize();
-    v8::Locker::StartPreemption(1);
-    semaphore = v8::internal::OS::CreateSemaphore(0);
-  }
-  const int kThreads = 2;
-  i::List<LoopingThread*> threads(kThreads);
-  for (int i = 0; i < kThreads; i++) {
-    threads.Add(new LoopingThread());
-  }
-  for (int i = 0; i < kThreads; i++) {
-    threads[i]->Start();
-  }
-  // Wait until all threads have signaled the semaphore.
-  for (int i = 0; i < kThreads; i++) {
-    semaphore->Wait();
-  }
-  {
-    v8::Locker locker(CcTest::default_isolate());
-    for (int i = 0; i < kThreads; i++) {
-      v8::V8::TerminateExecution(threads[i]->GetV8ThreadId());
-    }
-  }
-  for (int i = 0; i < kThreads; i++) {
-    threads[i]->Join();
-    delete threads[i];
-  }
-  {
-    v8::Locker locker(CcTest::default_isolate());
-    v8::Locker::StopPreemption();
-  }
-
-  delete semaphore;
-  semaphore = NULL;
 }
 
 
 int call_count = 0;
 
 
-v8::Handle<v8::Value> TerminateOrReturnObject(const v8::Arguments& args) {
+void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) {
   if (++call_count == 10) {
-    CHECK(!v8::V8::IsExecutionTerminating());
-    v8::V8::TerminateExecution();
-    return v8::Undefined();
+    CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
+    v8::V8::TerminateExecution(args.GetIsolate());
+    return;
   }
-  v8::Local<v8::Object> result = v8::Object::New();
-  result->Set(v8::String::New("x"), v8::Integer::New(42));
-  return result;
+  v8::Local<v8::Object> result = v8::Object::New(args.GetIsolate());
+  result->Set(v8::String::NewFromUtf8(args.GetIsolate(), "x"),
+              v8::Integer::New(args.GetIsolate(), 42));
+  args.GetReturnValue().Set(result);
 }
 
 
-v8::Handle<v8::Value> LoopGetProperty(const v8::Arguments& args) {
+void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) {
   v8::TryCatch try_catch;
-  CHECK(!v8::V8::IsExecutionTerminating());
-  v8::Script::Compile(v8::String::New("function f() {"
-                                      "  try {"
-                                      "    while(true) {"
-                                      "      terminate_or_return_object().x;"
-                                      "    }"
-                                      "    fail();"
-                                      "  } catch(e) {"
-                                      "    fail();"
-                                      "  }"
-                                      "}"
-                                      "f()"))->Run();
+  CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
+  v8::Script::Compile(
+      v8::String::NewFromUtf8(args.GetIsolate(),
+                              "function f() {"
+                              "  try {"
+                              "    while(true) {"
+                              "      terminate_or_return_object().x;"
+                              "    }"
+                              "    fail();"
+                              "  } catch(e) {"
+                              "    (function() {})();"  // trigger stack check.
+                              "    fail();"
+                              "  }"
+                              "}"
+                              "f()"))->Run();
   CHECK(try_catch.HasCaught());
   CHECK(try_catch.Exception()->IsNull());
   CHECK(try_catch.Message().IsEmpty());
   CHECK(!try_catch.CanContinue());
-  CHECK(v8::V8::IsExecutionTerminating());
-  return v8::Undefined();
+  CHECK(v8::V8::IsExecutionTerminating(args.GetIsolate()));
 }
 
 
 // Test that we correctly handle termination exceptions if they are
 // triggered by the creation of error objects in connection with ICs.
 TEST(TerminateLoadICException) {
-  v8::HandleScope scope(v8::Isolate::GetCurrent());
-  v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
-  global->Set(v8::String::New("terminate_or_return_object"),
-              v8::FunctionTemplate::New(TerminateOrReturnObject));
-  global->Set(v8::String::New("fail"), v8::FunctionTemplate::New(Fail));
-  global->Set(v8::String::New("loop"),
-              v8::FunctionTemplate::New(LoopGetProperty));
-
-  v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope scope(isolate);
+  v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
+  global->Set(
+      v8::String::NewFromUtf8(isolate, "terminate_or_return_object"),
+      v8::FunctionTemplate::New(isolate, TerminateOrReturnObject));
+  global->Set(v8::String::NewFromUtf8(isolate, "fail"),
+              v8::FunctionTemplate::New(isolate, Fail));
+  global->Set(v8::String::NewFromUtf8(isolate, "loop"),
+              v8::FunctionTemplate::New(isolate, LoopGetProperty));
+
+  v8::Handle<v8::Context> context =
+      v8::Context::New(isolate, NULL, global);
   v8::Context::Scope context_scope(context);
-  CHECK(!v8::V8::IsExecutionTerminating());
+  CHECK(!v8::V8::IsExecutionTerminating(isolate));
   // Run a loop that will be infinite if thread termination does not work.
-  v8::Handle<v8::String> source =
-      v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
+  v8::Handle<v8::String> source = v8::String::NewFromUtf8(
+      isolate, "try { loop(); fail(); } catch(e) { fail(); }");
   call_count = 0;
   v8::Script::Compile(source)->Run();
   // Test that we can run the code again after thread termination.
-  CHECK(!v8::V8::IsExecutionTerminating());
+  CHECK(!v8::V8::IsExecutionTerminating(isolate));
   call_count = 0;
   v8::Script::Compile(source)->Run();
-  context.Dispose(context->GetIsolate());
 }
 
-v8::Handle<v8::Value> ReenterAfterTermination(const v8::Arguments& args) {
+
+v8::Persistent<v8::String> reenter_script_1;
+v8::Persistent<v8::String> reenter_script_2;
+
+void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) {
   v8::TryCatch try_catch;
+  v8::Isolate* isolate = args.GetIsolate();
+  CHECK(!v8::V8::IsExecutionTerminating(isolate));
+  v8::Local<v8::String> script =
+      v8::Local<v8::String>::New(isolate, reenter_script_1);
+  v8::Script::Compile(script)->Run();
+  CHECK(try_catch.HasCaught());
+  CHECK(try_catch.Exception()->IsNull());
+  CHECK(try_catch.Message().IsEmpty());
+  CHECK(!try_catch.CanContinue());
+  CHECK(v8::V8::IsExecutionTerminating(isolate));
+  script = v8::Local<v8::String>::New(isolate, reenter_script_2);
+  v8::Script::Compile(script)->Run();
+}
+
+
+// Test that reentry into V8 while the termination exception is still pending
+// (has not yet unwound the 0-level JS frame) does not crash.
+TEST(TerminateAndReenterFromThreadItself) {
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope scope(isolate);
+  v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(
+      isolate, TerminateCurrentThread, ReenterAfterTermination);
+  v8::Handle<v8::Context> context =
+      v8::Context::New(isolate, NULL, global);
+  v8::Context::Scope context_scope(context);
   CHECK(!v8::V8::IsExecutionTerminating());
-  v8::Script::Compile(v8::String::New("function f() {"
+  // Create script strings upfront as it won't work when terminating.
+  reenter_script_1.Reset(isolate, v8_str(
+                                      "function f() {"
                                       "  var term = true;"
                                       "  try {"
                                       "    while(true) {"
@@ -344,31 +317,225 @@ v8::Handle<v8::Value> ReenterAfterTermination(const v8::Arguments& args) {
                                       "    fail();"
                                       "  }"
                                       "}"
-                                      "f()"))->Run();
+                                      "f()"));
+  reenter_script_2.Reset(isolate, v8_str("function f() { fail(); } f()"));
+  CompileRun("try { loop(); fail(); } catch(e) { fail(); }");
+  CHECK(!v8::V8::IsExecutionTerminating(isolate));
+  // Check we can run JS again after termination.
+  CHECK(CompileRun("function f() { return true; } f()")->IsTrue());
+  reenter_script_1.Reset();
+  reenter_script_2.Reset();
+}
+
+
+void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::TryCatch try_catch;
+  CHECK(!v8::V8::IsExecutionTerminating());
+  v8::Script::Compile(v8::String::NewFromUtf8(args.GetIsolate(),
+                                              "var term = true;"
+                                              "while(true) {"
+                                              "  if (term) terminate();"
+                                              "  term = false;"
+                                              "}"
+                                              "fail();"))->Run();
   CHECK(try_catch.HasCaught());
   CHECK(try_catch.Exception()->IsNull());
   CHECK(try_catch.Message().IsEmpty());
   CHECK(!try_catch.CanContinue());
   CHECK(v8::V8::IsExecutionTerminating());
-  v8::Script::Compile(v8::String::New("function f() { fail(); } f()"))->Run();
-  return v8::Undefined();
+  CHECK(try_catch.HasTerminated());
+  v8::V8::CancelTerminateExecution(CcTest::isolate());
+  CHECK(!v8::V8::IsExecutionTerminating());
 }
 
-// Test that reentry into V8 while the termination exception is still pending
-// (has not yet unwound the 0-level JS frame) does not crash.
-TEST(TerminateAndReenterFromThreadItself) {
-  v8::HandleScope scope(v8::Isolate::GetCurrent());
+
+// Test that a single thread of JavaScript execution can terminate
+// itself and then resume execution.
+TEST(TerminateCancelTerminateFromThreadItself) {
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope scope(isolate);
+  v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(
+      isolate, TerminateCurrentThread, DoLoopCancelTerminate);
+  v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL, global);
+  v8::Context::Scope context_scope(context);
+  CHECK(!v8::V8::IsExecutionTerminating(CcTest::isolate()));
+  v8::Handle<v8::String> source = v8::String::NewFromUtf8(
+      isolate, "try { doloop(); } catch(e) { fail(); } 'completed';");
+  // Check that execution completed with correct return value.
+  CHECK(v8::Script::Compile(source)->Run()->Equals(v8_str("completed")));
+}
+
+
+void MicrotaskShouldNotRun(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  CHECK(false);
+}
+
+
+void MicrotaskLoopForever(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::HandleScope scope(isolate);
+  // Enqueue another should-not-run task to ensure we clean out the queue
+  // when we terminate.
+  isolate->EnqueueMicrotask(v8::Function::New(isolate, MicrotaskShouldNotRun));
+  i::FLAG_turbo_osr = false;  // TODO(titzer): interrupts in TF loops.
+  CompileRun("terminate(); while (true) { }");
+  CHECK(v8::V8::IsExecutionTerminating());
+}
+
+
+TEST(TerminateFromOtherThreadWhileMicrotaskRunning) {
+  semaphore = new v8::base::Semaphore(0);
+  TerminatorThread thread(CcTest::i_isolate());
+  thread.Start();
+
+  v8::Isolate* isolate = CcTest::isolate();
+  isolate->SetAutorunMicrotasks(false);
+  v8::HandleScope scope(isolate);
   v8::Handle<v8::ObjectTemplate> global =
-      CreateGlobalTemplate(TerminateCurrentThread, ReenterAfterTermination);
-  v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+      CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
+  v8::Handle<v8::Context> context =
+      v8::Context::New(CcTest::isolate(), NULL, global);
   v8::Context::Scope context_scope(context);
+  isolate->EnqueueMicrotask(v8::Function::New(isolate, MicrotaskLoopForever));
+  // The second task should never be run because we bail out if we're
+  // terminating.
+  isolate->EnqueueMicrotask(v8::Function::New(isolate, MicrotaskShouldNotRun));
+  isolate->RunMicrotasks();
+
+  v8::V8::CancelTerminateExecution(isolate);
+  isolate->RunMicrotasks();  // should not run MicrotaskShouldNotRun
+
+  thread.Join();
+  delete semaphore;
+  semaphore = NULL;
+}
+
+
+static int callback_counter = 0;
+
+
+static void CounterCallback(v8::Isolate* isolate, void* data) {
+  callback_counter++;
+}
+
+
+TEST(PostponeTerminateException) {
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope scope(isolate);
+  v8::Handle<v8::ObjectTemplate> global =
+      CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop);
+  v8::Handle<v8::Context> context =
+      v8::Context::New(CcTest::isolate(), NULL, global);
+  v8::Context::Scope context_scope(context);
+
+  v8::TryCatch try_catch;
+  static const char* terminate_and_loop =
+      "terminate(); for (var i = 0; i < 10000; i++);";
+
+  { // Postpone terminate execution interrupts.
+    i::PostponeInterruptsScope p1(CcTest::i_isolate(),
+                                  i::StackGuard::TERMINATE_EXECUTION) ;
+
+    // API interrupts should still be triggered.
+    CcTest::isolate()->RequestInterrupt(&CounterCallback, NULL);
+    CHECK_EQ(0, callback_counter);
+    CompileRun(terminate_and_loop);
+    CHECK(!try_catch.HasTerminated());
+    CHECK_EQ(1, callback_counter);
+
+    { // Postpone API interrupts as well.
+      i::PostponeInterruptsScope p2(CcTest::i_isolate(),
+                                    i::StackGuard::API_INTERRUPT);
+
+      // None of the two interrupts should trigger.
+      CcTest::isolate()->RequestInterrupt(&CounterCallback, NULL);
+      CompileRun(terminate_and_loop);
+      CHECK(!try_catch.HasTerminated());
+      CHECK_EQ(1, callback_counter);
+    }
+
+    // Now the previously requested API interrupt should trigger.
+    CompileRun(terminate_and_loop);
+    CHECK(!try_catch.HasTerminated());
+    CHECK_EQ(2, callback_counter);
+  }
+
+  // Now the previously requested terminate execution interrupt should trigger.
+  CompileRun("for (var i = 0; i < 10000; i++);");
+  CHECK(try_catch.HasTerminated());
+  CHECK_EQ(2, callback_counter);
+}
+
+
+TEST(ErrorObjectAfterTermination) {
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope scope(isolate);
+  v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
+  v8::Context::Scope context_scope(context);
+  v8::V8::TerminateExecution(isolate);
+  v8::Local<v8::Value> error = v8::Exception::Error(v8_str("error"));
+  // TODO(yangguo): crbug/403509. Check for empty handle instead.
+  CHECK(error->IsUndefined());
+}
+
+
+void InnerTryCallTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
+  v8::Handle<v8::Object> global = CcTest::global();
+  v8::Handle<v8::Function> loop =
+      v8::Handle<v8::Function>::Cast(global->Get(v8_str("loop")));
+  i::MaybeHandle<i::Object> result =
+      i::Execution::TryCall(v8::Utils::OpenHandle((*loop)),
+                            v8::Utils::OpenHandle((*global)), 0, NULL, NULL);
+  CHECK(result.is_null());
+  // TryCall ignores terminate execution, but rerequests the interrupt.
+  CHECK(!v8::V8::IsExecutionTerminating(args.GetIsolate()));
+  CHECK(CompileRun("1 + 1;").IsEmpty());
+}
+
+
+TEST(TerminationInInnerTryCall) {
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope scope(isolate);
+  v8::Handle<v8::ObjectTemplate> global_template = CreateGlobalTemplate(
+      CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall);
+  global_template->Set(
+      v8_str("inner_try_call_terminate"),
+      v8::FunctionTemplate::New(isolate, InnerTryCallTerminate));
+  v8::Handle<v8::Context> context =
+      v8::Context::New(CcTest::isolate(), NULL, global_template);
+  v8::Context::Scope context_scope(context);
+  {
+    v8::TryCatch try_catch;
+    CompileRun("inner_try_call_terminate()");
+    CHECK(try_catch.HasTerminated());
+  }
+  CHECK_EQ(4, CompileRun("2 + 2")->ToInt32()->Int32Value());
   CHECK(!v8::V8::IsExecutionTerminating());
-  v8::Handle<v8::String> source =
-      v8::String::New("try { loop(); fail(); } catch(e) { fail(); }");
-  v8::Script::Compile(source)->Run();
-  CHECK(!v8::V8::IsExecutionTerminating());
-  // Check we can run JS again after termination.
-  CHECK(v8::Script::Compile(v8::String::New("function f() { return true; }"
-                                            "f()"))->Run()->IsTrue());
-  context.Dispose(context->GetIsolate());
+}
+
+
+TEST(TerminateAndTryCall) {
+  i::FLAG_allow_natives_syntax = true;
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope scope(isolate);
+  v8::Handle<v8::ObjectTemplate> global = CreateGlobalTemplate(
+      isolate, TerminateCurrentThread, DoLoopCancelTerminate);
+  v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL, global);
+  v8::Context::Scope context_scope(context);
+  CHECK(!v8::V8::IsExecutionTerminating(isolate));
+  v8::TryCatch try_catch;
+  CHECK(!v8::V8::IsExecutionTerminating(isolate));
+  // Terminate execution has been triggered inside TryCall, but re-requested
+  // to trigger later.
+  CHECK(CompileRun("terminate(); reference_error();").IsEmpty());
+  CHECK(try_catch.HasCaught());
+  CHECK(!v8::V8::IsExecutionTerminating(isolate));
+  CHECK(CcTest::global()->Get(v8_str("terminate"))->IsFunction());
+  // The first stack check after terminate has been re-requested fails.
+  CHECK(CompileRun("1 + 1").IsEmpty());
+  CHECK(!v8::V8::IsExecutionTerminating(isolate));
+  // V8 then recovers.
+  CHECK_EQ(4, CompileRun("2 + 2")->ToInt32()->Int32Value());
+  CHECK(!v8::V8::IsExecutionTerminating(isolate));
 }