| Index: test/cctest/test-api.cc | 
| diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc | 
| index 22e207785888f2857db01f6e20486a8a64c216d4..ae6dbfad33706eae42819747e6270ea222bc9db4 100644 | 
| --- a/test/cctest/test-api.cc | 
| +++ b/test/cctest/test-api.cc | 
| @@ -14323,129 +14323,6 @@ THREADED_TEST(CrossContextNew) { | 
| } | 
|  | 
|  | 
| -class RegExpInterruptTest { | 
| - public: | 
| -  RegExpInterruptTest() : block_(0) {} | 
| -  ~RegExpInterruptTest() {} | 
| -  void RunTest() { | 
| -    gc_count_ = 0; | 
| -    gc_during_regexp_ = 0; | 
| -    regexp_success_ = false; | 
| -    gc_success_ = false; | 
| -    GCThread gc_thread(this); | 
| -    gc_thread.Start(); | 
| -    v8::Isolate* isolate = CcTest::isolate(); | 
| -    v8::Locker::StartPreemption(isolate, 1); | 
| - | 
| -    LongRunningRegExp(); | 
| -    { | 
| -      v8::Unlocker unlock(isolate); | 
| -      gc_thread.Join(); | 
| -    } | 
| -    v8::Locker::StopPreemption(isolate); | 
| -    CHECK(regexp_success_); | 
| -    CHECK(gc_success_); | 
| -  } | 
| - | 
| - private: | 
| -  // Number of garbage collections required. | 
| -  static const int kRequiredGCs = 5; | 
| - | 
| -  class GCThread : public i::Thread { | 
| -   public: | 
| -    explicit GCThread(RegExpInterruptTest* test) | 
| -        : Thread("GCThread"), test_(test) {} | 
| -    virtual void Run() { | 
| -      test_->CollectGarbage(); | 
| -    } | 
| -   private: | 
| -     RegExpInterruptTest* test_; | 
| -  }; | 
| - | 
| -  void CollectGarbage() { | 
| -    block_.Wait(); | 
| -    while (gc_during_regexp_ < kRequiredGCs) { | 
| -      { | 
| -        v8::Locker lock(CcTest::isolate()); | 
| -        v8::Isolate::Scope isolate_scope(CcTest::isolate()); | 
| -        // TODO(lrn): Perhaps create some garbage before collecting. | 
| -        CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); | 
| -        gc_count_++; | 
| -      } | 
| -      i::OS::Sleep(1); | 
| -    } | 
| -    gc_success_ = true; | 
| -  } | 
| - | 
| -  void LongRunningRegExp() { | 
| -    block_.Signal();  // Enable garbage collection thread on next preemption. | 
| -    int rounds = 0; | 
| -    while (gc_during_regexp_ < kRequiredGCs) { | 
| -      int gc_before = gc_count_; | 
| -      { | 
| -        // Match 15-30 "a"'s against 14 and a "b". | 
| -        const char* c_source = | 
| -            "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" | 
| -            ".exec('aaaaaaaaaaaaaaab') === null"; | 
| -        Local<String> source = String::New(c_source); | 
| -        Local<Script> script = Script::Compile(source); | 
| -        Local<Value> result = script->Run(); | 
| -        if (!result->BooleanValue()) { | 
| -          gc_during_regexp_ = kRequiredGCs;  // Allow gc thread to exit. | 
| -          return; | 
| -        } | 
| -      } | 
| -      { | 
| -        // Match 15-30 "a"'s against 15 and a "b". | 
| -        const char* c_source = | 
| -            "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" | 
| -            ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; | 
| -        Local<String> source = String::New(c_source); | 
| -        Local<Script> script = Script::Compile(source); | 
| -        Local<Value> result = script->Run(); | 
| -        if (!result->BooleanValue()) { | 
| -          gc_during_regexp_ = kRequiredGCs; | 
| -          return; | 
| -        } | 
| -      } | 
| -      int gc_after = gc_count_; | 
| -      gc_during_regexp_ += gc_after - gc_before; | 
| -      rounds++; | 
| -      i::OS::Sleep(1); | 
| -    } | 
| -    regexp_success_ = true; | 
| -  } | 
| - | 
| -  i::Semaphore block_; | 
| -  int gc_count_; | 
| -  int gc_during_regexp_; | 
| -  bool regexp_success_; | 
| -  bool gc_success_; | 
| -}; | 
| - | 
| - | 
| -// Test that a regular expression execution can be interrupted and | 
| -// survive a garbage collection. | 
| -TEST(RegExpInterruption) { | 
| -  v8::Locker lock(CcTest::isolate()); | 
| -  v8::HandleScope scope(CcTest::isolate()); | 
| -  Local<Context> local_env; | 
| -  { | 
| -    LocalContext env; | 
| -    local_env = env.local(); | 
| -  } | 
| - | 
| -  // Local context should still be live. | 
| -  CHECK(!local_env.IsEmpty()); | 
| -  local_env->Enter(); | 
| - | 
| -  // Should complete without problems. | 
| -  RegExpInterruptTest().RunTest(); | 
| - | 
| -  local_env->Exit(); | 
| -} | 
| - | 
| - | 
| class ApplyInterruptTest { | 
| public: | 
| ApplyInterruptTest() : block_(0) {} | 
| @@ -14735,142 +14612,73 @@ TEST(CompileExternalTwoByteSource) { | 
| } | 
|  | 
|  | 
| -class RegExpStringModificationTest { | 
| - public: | 
| -  RegExpStringModificationTest() | 
| -      : block_(0), | 
| -        morphs_(0), | 
| -        morphs_during_regexp_(0), | 
| -        ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), | 
| -        uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} | 
| -  ~RegExpStringModificationTest() {} | 
| -  void RunTest() { | 
| -    v8::Isolate* isolate = CcTest::isolate(); | 
| -    i::Factory* factory = CcTest::i_isolate()->factory(); | 
| +struct RegExpInterruptionData { | 
| +  int loop_count; | 
| +  UC16VectorResource* string_resource; | 
| +  v8::Persistent<v8::String> string; | 
| +} regexp_interruption_data; | 
|  | 
| -    regexp_success_ = false; | 
| -    morph_success_ = false; | 
|  | 
| -    // Initialize the contents of two_byte_content_ to be a uc16 representation | 
| -    // of "aaaaaaaaaaaaaab". | 
| -    for (int i = 0; i < 14; i++) { | 
| -      two_byte_content_[i] = 'a'; | 
| -    } | 
| -    two_byte_content_[14] = 'b'; | 
| - | 
| -    // Create the input string for the regexp - the one we are going to change | 
| -    // properties of. | 
| -    input_ = factory->NewExternalStringFromAscii(&ascii_resource_); | 
| - | 
| -    // Inject the input as a global variable. | 
| -    i::Handle<i::String> input_name = | 
| -        factory->NewStringFromAscii(i::Vector<const char>("input", 5)); | 
| -    i::JSReceiver::SetProperty( | 
| -        i::handle(CcTest::i_isolate()->native_context()->global_object()), | 
| -        input_name, | 
| -        input_, | 
| -        NONE, | 
| -        i::kNonStrictMode); | 
| - | 
| -    MorphThread morph_thread(this); | 
| -    morph_thread.Start(); | 
| -    v8::Locker::StartPreemption(isolate, 1); | 
| -    LongRunningRegExp(); | 
| -    { | 
| -      v8::Unlocker unlock(isolate); | 
| -      morph_thread.Join(); | 
| +class RegExpInterruptionThread : public i::Thread { | 
| + public: | 
| +  explicit RegExpInterruptionThread(v8::Isolate* isolate) | 
| +      : Thread("TimeoutThread"), isolate_(isolate) {} | 
| + | 
| +  virtual void Run() { | 
| +    for (regexp_interruption_data.loop_count = 0; | 
| +         regexp_interruption_data.loop_count < 7; | 
| +         regexp_interruption_data.loop_count++) { | 
| +      i::OS::Sleep(50);  // Wait a bit before requesting GC. | 
| +      reinterpret_cast<i::Isolate*>(isolate_)->stack_guard()->RequestGC(); | 
| } | 
| -    v8::Locker::StopPreemption(isolate); | 
| -    CHECK(regexp_success_); | 
| -    CHECK(morph_success_); | 
| +    i::OS::Sleep(50);  // Wait a bit before terminating. | 
| +    v8::V8::TerminateExecution(isolate_); | 
| } | 
|  | 
| private: | 
| -  // Number of string modifications required. | 
| -  static const int kRequiredModifications = 5; | 
| -  static const int kMaxModifications = 100; | 
| +  v8::Isolate* isolate_; | 
| +}; | 
|  | 
| -  class MorphThread : public i::Thread { | 
| -   public: | 
| -    explicit MorphThread(RegExpStringModificationTest* test) | 
| -        : Thread("MorphThread"), test_(test) {} | 
| -    virtual void Run() { | 
| -      test_->MorphString(); | 
| -    } | 
| -   private: | 
| -     RegExpStringModificationTest* test_; | 
| -  }; | 
|  | 
| -  void MorphString() { | 
| -    block_.Wait(); | 
| -    while (morphs_during_regexp_ < kRequiredModifications && | 
| -           morphs_ < kMaxModifications) { | 
| -      { | 
| -        v8::Locker lock(CcTest::isolate()); | 
| -        v8::Isolate::Scope isolate_scope(CcTest::isolate()); | 
| -        // Swap string between ascii and two-byte representation. | 
| -        i::String* string = *input_; | 
| -        MorphAString(string, &ascii_resource_, &uc16_resource_); | 
| -        morphs_++; | 
| -      } | 
| -      i::OS::Sleep(1); | 
| -    } | 
| -    morph_success_ = true; | 
| -  } | 
| +void RunBeforeGC(v8::GCType type, v8::GCCallbackFlags flags) { | 
| +  if (regexp_interruption_data.loop_count != 2) return; | 
| +  v8::HandleScope scope(CcTest::isolate()); | 
| +  v8::Local<v8::String> string = v8::Local<v8::String>::New( | 
| +      CcTest::isolate(), regexp_interruption_data.string); | 
| +  string->MakeExternal(regexp_interruption_data.string_resource); | 
| +} | 
|  | 
| -  void LongRunningRegExp() { | 
| -    block_.Signal();  // Enable morphing thread on next preemption. | 
| -    while (morphs_during_regexp_ < kRequiredModifications && | 
| -           morphs_ < kMaxModifications) { | 
| -      int morphs_before = morphs_; | 
| -      { | 
| -        v8::HandleScope scope(CcTest::isolate()); | 
| -        // Match 15-30 "a"'s against 14 and a "b". | 
| -        const char* c_source = | 
| -            "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" | 
| -            ".exec(input) === null"; | 
| -        Local<String> source = String::New(c_source); | 
| -        Local<Script> script = Script::Compile(source); | 
| -        Local<Value> result = script->Run(); | 
| -        CHECK(result->IsTrue()); | 
| -      } | 
| -      int morphs_after = morphs_; | 
| -      morphs_during_regexp_ += morphs_after - morphs_before; | 
| -    } | 
| -    regexp_success_ = true; | 
| -  } | 
|  | 
| -  i::uc16 two_byte_content_[15]; | 
| -  i::Semaphore block_; | 
| -  int morphs_; | 
| -  int morphs_during_regexp_; | 
| -  bool regexp_success_; | 
| -  bool morph_success_; | 
| -  i::Handle<i::String> input_; | 
| -  AsciiVectorResource ascii_resource_; | 
| -  UC16VectorResource uc16_resource_; | 
| -}; | 
| +// Test that RegExp execution can be interrupted.  Specifically, we test | 
| +// * interrupting with GC | 
| +// * turn the subject string from one-byte internal to two-byte external string | 
| +// * force termination | 
| +TEST(RegExpInterruption) { | 
| +  v8::HandleScope scope(CcTest::isolate()); | 
| +  LocalContext env; | 
|  | 
| +  RegExpInterruptionThread timeout_thread(CcTest::isolate()); | 
|  | 
| -// Test that a regular expression execution can be interrupted and | 
| -// the string changed without failing. | 
| -TEST(RegExpStringModification) { | 
| -  v8::Locker lock(CcTest::isolate()); | 
| -  v8::HandleScope scope(CcTest::isolate()); | 
| -  Local<Context> local_env; | 
| -  { | 
| -    LocalContext env; | 
| -    local_env = env.local(); | 
| -  } | 
| +  v8::V8::AddGCPrologueCallback(RunBeforeGC); | 
| +  static const char* ascii_content = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; | 
| +  i::uc16* uc16_content = AsciiToTwoByteString(ascii_content); | 
| +  v8::Local<v8::String> string = v8_str(ascii_content); | 
|  | 
| -  // Local context should still be live. | 
| -  CHECK(!local_env.IsEmpty()); | 
| -  local_env->Enter(); | 
| +  CcTest::global()->Set(v8_str("a"), string); | 
| +  regexp_interruption_data.string.Reset(CcTest::isolate(), string); | 
| +  regexp_interruption_data.string_resource = new UC16VectorResource( | 
| +      i::Vector<const i::uc16>(uc16_content, i::StrLength(ascii_content))); | 
|  | 
| -  // Should complete without problems. | 
| -  RegExpStringModificationTest().RunTest(); | 
| +  v8::TryCatch try_catch; | 
| +  timeout_thread.Start(); | 
|  | 
| -  local_env->Exit(); | 
| +  CompileRun("/((a*)*)*b/.exec(a)"); | 
| +  CHECK(try_catch.HasTerminated()); | 
| + | 
| +  timeout_thread.Join(); | 
| + | 
| +  delete regexp_interruption_data.string_resource; | 
| +  regexp_interruption_data.string.Dispose(); | 
| } | 
|  | 
|  | 
|  |