Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 1 | // Copyright 2009 the V8 project authors. All rights reserved. |
| 2 | // Redistribution and use in source and binary forms, with or without |
| 3 | // modification, are permitted provided that the following conditions are |
| 4 | // met: |
| 5 | // |
| 6 | // * Redistributions of source code must retain the above copyright |
| 7 | // notice, this list of conditions and the following disclaimer. |
| 8 | // * Redistributions in binary form must reproduce the above |
| 9 | // copyright notice, this list of conditions and the following |
| 10 | // disclaimer in the documentation and/or other materials provided |
| 11 | // with the distribution. |
| 12 | // * Neither the name of Google Inc. nor the names of its |
| 13 | // contributors may be used to endorse or promote products derived |
| 14 | // from this software without specific prior written permission. |
| 15 | // |
| 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 28 | #include "src/v8.h" |
| 29 | #include "test/cctest/cctest.h" |
| 30 | |
| 31 | #include "src/base/platform/platform.h" |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 32 | |
| 33 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 34 | v8::base::Semaphore* semaphore = NULL; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 35 | |
| 36 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 37 | void Signal(const v8::FunctionCallbackInfo<v8::Value>& args) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 38 | semaphore->Signal(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 39 | } |
| 40 | |
| 41 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 42 | void TerminateCurrentThread(const v8::FunctionCallbackInfo<v8::Value>& args) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 43 | CHECK(!args.GetIsolate()->IsExecutionTerminating()); |
| 44 | args.GetIsolate()->TerminateExecution(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 45 | } |
| 46 | |
| 47 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 48 | void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 49 | CHECK(false); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 53 | void Loop(const v8::FunctionCallbackInfo<v8::Value>& args) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 54 | CHECK(!args.GetIsolate()->IsExecutionTerminating()); |
| 55 | v8::MaybeLocal<v8::Value> result = |
| 56 | CompileRun(args.GetIsolate()->GetCurrentContext(), |
| 57 | "try { doloop(); fail(); } catch(e) { fail(); }"); |
Steve Block | 6ded16b | 2010-05-10 14:33:55 +0100 | [diff] [blame] | 58 | CHECK(result.IsEmpty()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 59 | CHECK(args.GetIsolate()->IsExecutionTerminating()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 63 | void DoLoop(const v8::FunctionCallbackInfo<v8::Value>& args) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 64 | v8::TryCatch try_catch(args.GetIsolate()); |
| 65 | CHECK(!args.GetIsolate()->IsExecutionTerminating()); |
| 66 | v8::MaybeLocal<v8::Value> result = |
| 67 | CompileRun(args.GetIsolate()->GetCurrentContext(), |
| 68 | "function f() {" |
| 69 | " var term = true;" |
| 70 | " try {" |
| 71 | " while(true) {" |
| 72 | " if (term) terminate();" |
| 73 | " term = false;" |
| 74 | " }" |
| 75 | " fail();" |
| 76 | " } catch(e) {" |
| 77 | " fail();" |
| 78 | " }" |
| 79 | "}" |
| 80 | "f()"); |
| 81 | CHECK(result.IsEmpty()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 82 | CHECK(try_catch.HasCaught()); |
| 83 | CHECK(try_catch.Exception()->IsNull()); |
| 84 | CHECK(try_catch.Message().IsEmpty()); |
| 85 | CHECK(!try_catch.CanContinue()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 86 | CHECK(args.GetIsolate()->IsExecutionTerminating()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 90 | void DoLoopNoCall(const v8::FunctionCallbackInfo<v8::Value>& args) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 91 | v8::TryCatch try_catch(args.GetIsolate()); |
| 92 | CHECK(!args.GetIsolate()->IsExecutionTerminating()); |
| 93 | v8::MaybeLocal<v8::Value> result = |
| 94 | CompileRun(args.GetIsolate()->GetCurrentContext(), |
| 95 | "var term = true;" |
| 96 | "while(true) {" |
| 97 | " if (term) terminate();" |
| 98 | " term = false;" |
| 99 | "}"); |
| 100 | CHECK(result.IsEmpty()); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 101 | CHECK(try_catch.HasCaught()); |
| 102 | CHECK(try_catch.Exception()->IsNull()); |
| 103 | CHECK(try_catch.Message().IsEmpty()); |
| 104 | CHECK(!try_catch.CanContinue()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 105 | CHECK(args.GetIsolate()->IsExecutionTerminating()); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 106 | } |
| 107 | |
| 108 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 109 | v8::Local<v8::ObjectTemplate> CreateGlobalTemplate( |
| 110 | v8::Isolate* isolate, v8::FunctionCallback terminate, |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 111 | v8::FunctionCallback doloop) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 112 | v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); |
| 113 | global->Set(v8_str("terminate"), |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 114 | v8::FunctionTemplate::New(isolate, terminate)); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 115 | global->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail)); |
| 116 | global->Set(v8_str("loop"), v8::FunctionTemplate::New(isolate, Loop)); |
| 117 | global->Set(v8_str("doloop"), v8::FunctionTemplate::New(isolate, doloop)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 118 | return global; |
| 119 | } |
| 120 | |
| 121 | |
| 122 | // Test that a single thread of JavaScript execution can terminate |
| 123 | // itself. |
| 124 | TEST(TerminateOnlyV8ThreadFromThreadItself) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 125 | v8::HandleScope scope(CcTest::isolate()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 126 | v8::Local<v8::ObjectTemplate> global = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 127 | CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 128 | v8::Local<v8::Context> context = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 129 | v8::Context::New(CcTest::isolate(), NULL, global); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 130 | v8::Context::Scope context_scope(context); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 131 | CHECK(!CcTest::isolate()->IsExecutionTerminating()); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 132 | // Run a loop that will be infinite if thread termination does not work. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 133 | v8::MaybeLocal<v8::Value> result = |
| 134 | CompileRun(CcTest::isolate()->GetCurrentContext(), |
| 135 | "try { loop(); fail(); } catch(e) { fail(); }"); |
| 136 | CHECK(result.IsEmpty()); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 137 | // Test that we can run the code again after thread termination. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 138 | CHECK(!CcTest::isolate()->IsExecutionTerminating()); |
| 139 | result = CompileRun(CcTest::isolate()->GetCurrentContext(), |
| 140 | "try { loop(); fail(); } catch(e) { fail(); }"); |
| 141 | CHECK(result.IsEmpty()); |
Steve Block | d0582a6 | 2009-12-15 09:54:21 +0000 | [diff] [blame] | 142 | } |
| 143 | |
| 144 | |
| 145 | // Test that a single thread of JavaScript execution can terminate |
| 146 | // itself in a loop that performs no calls. |
| 147 | TEST(TerminateOnlyV8ThreadFromThreadItselfNoLoop) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 148 | v8::HandleScope scope(CcTest::isolate()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 149 | v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 150 | CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 151 | v8::Local<v8::Context> context = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 152 | v8::Context::New(CcTest::isolate(), NULL, global); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 153 | v8::Context::Scope context_scope(context); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 154 | CHECK(!CcTest::isolate()->IsExecutionTerminating()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 155 | // Run a loop that will be infinite if thread termination does not work. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 156 | static const char* source = "try { loop(); fail(); } catch(e) { fail(); }"; |
| 157 | v8::MaybeLocal<v8::Value> result = |
| 158 | CompileRun(CcTest::isolate()->GetCurrentContext(), source); |
| 159 | CHECK(result.IsEmpty()); |
| 160 | CHECK(!CcTest::isolate()->IsExecutionTerminating()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 161 | // Test that we can run the code again after thread termination. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 162 | result = CompileRun(CcTest::isolate()->GetCurrentContext(), source); |
| 163 | CHECK(result.IsEmpty()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 164 | } |
| 165 | |
| 166 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 167 | class TerminatorThread : public v8::base::Thread { |
Steve Block | 44f0eee | 2011-05-26 01:26:41 +0100 | [diff] [blame] | 168 | public: |
| 169 | explicit TerminatorThread(i::Isolate* isolate) |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 170 | : Thread(Options("TerminatorThread")), |
| 171 | isolate_(reinterpret_cast<v8::Isolate*>(isolate)) {} |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 172 | void Run() { |
| 173 | semaphore->Wait(); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 174 | CHECK(!isolate_->IsExecutionTerminating()); |
| 175 | isolate_->TerminateExecution(); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 176 | } |
Ben Murdoch | 3fb3ca8 | 2011-12-02 17:19:32 +0000 | [diff] [blame] | 177 | |
| 178 | private: |
| 179 | v8::Isolate* isolate_; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 180 | }; |
| 181 | |
| 182 | |
| 183 | // Test that a single thread of JavaScript execution can be terminated |
| 184 | // from the side by another thread. |
| 185 | TEST(TerminateOnlyV8ThreadFromOtherThread) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 186 | semaphore = new v8::base::Semaphore(0); |
| 187 | TerminatorThread thread(CcTest::i_isolate()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 188 | thread.Start(); |
| 189 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 190 | v8::HandleScope scope(CcTest::isolate()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 191 | v8::Local<v8::ObjectTemplate> global = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 192 | CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 193 | v8::Local<v8::Context> context = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 194 | v8::Context::New(CcTest::isolate(), NULL, global); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 195 | v8::Context::Scope context_scope(context); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 196 | CHECK(!CcTest::isolate()->IsExecutionTerminating()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 197 | // Run a loop that will be infinite if thread termination does not work. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 198 | v8::MaybeLocal<v8::Value> result = |
| 199 | CompileRun(CcTest::isolate()->GetCurrentContext(), |
| 200 | "try { loop(); fail(); } catch(e) { fail(); }"); |
| 201 | CHECK(result.IsEmpty()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 202 | thread.Join(); |
| 203 | delete semaphore; |
| 204 | semaphore = NULL; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 205 | } |
| 206 | |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 207 | // Test that execution can be terminated from within JSON.stringify. |
| 208 | TEST(TerminateJsonStringify) { |
| 209 | semaphore = new v8::base::Semaphore(0); |
| 210 | TerminatorThread thread(CcTest::i_isolate()); |
| 211 | thread.Start(); |
| 212 | |
| 213 | v8::HandleScope scope(CcTest::isolate()); |
| 214 | v8::Local<v8::ObjectTemplate> global = |
| 215 | CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop); |
| 216 | v8::Local<v8::Context> context = |
| 217 | v8::Context::New(CcTest::isolate(), NULL, global); |
| 218 | v8::Context::Scope context_scope(context); |
| 219 | CHECK(!CcTest::isolate()->IsExecutionTerminating()); |
| 220 | v8::MaybeLocal<v8::Value> result = |
| 221 | CompileRun(CcTest::isolate()->GetCurrentContext(), |
| 222 | "var x = [];" |
| 223 | "x[2**31]=1;" |
| 224 | "terminate();" |
| 225 | "JSON.stringify(x);" |
| 226 | "fail();"); |
| 227 | CHECK(result.IsEmpty()); |
| 228 | thread.Join(); |
| 229 | delete semaphore; |
| 230 | semaphore = NULL; |
| 231 | } |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 232 | |
| 233 | int call_count = 0; |
| 234 | |
| 235 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 236 | void TerminateOrReturnObject(const v8::FunctionCallbackInfo<v8::Value>& args) { |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 237 | if (++call_count == 10) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 238 | CHECK(!args.GetIsolate()->IsExecutionTerminating()); |
| 239 | args.GetIsolate()->TerminateExecution(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 240 | return; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 241 | } |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 242 | v8::Local<v8::Object> result = v8::Object::New(args.GetIsolate()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 243 | v8::Maybe<bool> val = |
| 244 | result->Set(args.GetIsolate()->GetCurrentContext(), v8_str("x"), |
| 245 | v8::Integer::New(args.GetIsolate(), 42)); |
| 246 | CHECK(val.FromJust()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 247 | args.GetReturnValue().Set(result); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 248 | } |
| 249 | |
| 250 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 251 | void LoopGetProperty(const v8::FunctionCallbackInfo<v8::Value>& args) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 252 | v8::TryCatch try_catch(args.GetIsolate()); |
| 253 | CHECK(!args.GetIsolate()->IsExecutionTerminating()); |
| 254 | v8::MaybeLocal<v8::Value> result = |
| 255 | CompileRun(args.GetIsolate()->GetCurrentContext(), |
| 256 | "function f() {" |
| 257 | " try {" |
| 258 | " while(true) {" |
| 259 | " terminate_or_return_object().x;" |
| 260 | " }" |
| 261 | " fail();" |
| 262 | " } catch(e) {" |
| 263 | " (function() {})();" // trigger stack check. |
| 264 | " fail();" |
| 265 | " }" |
| 266 | "}" |
| 267 | "f()"); |
| 268 | CHECK(result.IsEmpty()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 269 | CHECK(try_catch.HasCaught()); |
| 270 | CHECK(try_catch.Exception()->IsNull()); |
| 271 | CHECK(try_catch.Message().IsEmpty()); |
| 272 | CHECK(!try_catch.CanContinue()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 273 | CHECK(args.GetIsolate()->IsExecutionTerminating()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 274 | } |
| 275 | |
| 276 | |
| 277 | // Test that we correctly handle termination exceptions if they are |
| 278 | // triggered by the creation of error objects in connection with ICs. |
| 279 | TEST(TerminateLoadICException) { |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 280 | v8::Isolate* isolate = CcTest::isolate(); |
| 281 | v8::HandleScope scope(isolate); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 282 | v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate); |
| 283 | global->Set(v8_str("terminate_or_return_object"), |
| 284 | v8::FunctionTemplate::New(isolate, TerminateOrReturnObject)); |
| 285 | global->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail)); |
| 286 | global->Set(v8_str("loop"), |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 287 | v8::FunctionTemplate::New(isolate, LoopGetProperty)); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 288 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 289 | v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 290 | v8::Context::Scope context_scope(context); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 291 | CHECK(!isolate->IsExecutionTerminating()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 292 | // Run a loop that will be infinite if thread termination does not work. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 293 | static const char* source = "try { loop(); fail(); } catch(e) { fail(); }"; |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 294 | call_count = 0; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 295 | v8::MaybeLocal<v8::Value> result = |
| 296 | CompileRun(isolate->GetCurrentContext(), source); |
| 297 | CHECK(result.IsEmpty()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 298 | // Test that we can run the code again after thread termination. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 299 | CHECK(!isolate->IsExecutionTerminating()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 300 | call_count = 0; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 301 | result = CompileRun(isolate->GetCurrentContext(), source); |
| 302 | CHECK(result.IsEmpty()); |
Steve Block | a7e24c1 | 2009-10-30 11:49:00 +0000 | [diff] [blame] | 303 | } |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 304 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 305 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 306 | v8::Persistent<v8::String> reenter_script_1; |
| 307 | v8::Persistent<v8::String> reenter_script_2; |
| 308 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 309 | void ReenterAfterTermination(const v8::FunctionCallbackInfo<v8::Value>& args) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 310 | v8::TryCatch try_catch(args.GetIsolate()); |
| 311 | v8::Isolate* isolate = args.GetIsolate(); |
| 312 | CHECK(!isolate->IsExecutionTerminating()); |
| 313 | v8::Local<v8::String> script = |
| 314 | v8::Local<v8::String>::New(isolate, reenter_script_1); |
| 315 | v8::MaybeLocal<v8::Value> result = CompileRun(script); |
| 316 | CHECK(result.IsEmpty()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 317 | CHECK(try_catch.HasCaught()); |
| 318 | CHECK(try_catch.Exception()->IsNull()); |
| 319 | CHECK(try_catch.Message().IsEmpty()); |
| 320 | CHECK(!try_catch.CanContinue()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 321 | CHECK(isolate->IsExecutionTerminating()); |
| 322 | script = v8::Local<v8::String>::New(isolate, reenter_script_2); |
| 323 | v8::MaybeLocal<v8::Script> compiled_script = |
| 324 | v8::Script::Compile(isolate->GetCurrentContext(), script); |
| 325 | CHECK(compiled_script.IsEmpty()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 326 | } |
| 327 | |
| 328 | |
| 329 | // Test that reentry into V8 while the termination exception is still pending |
| 330 | // (has not yet unwound the 0-level JS frame) does not crash. |
| 331 | TEST(TerminateAndReenterFromThreadItself) { |
| 332 | v8::Isolate* isolate = CcTest::isolate(); |
| 333 | v8::HandleScope scope(isolate); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 334 | v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 335 | isolate, TerminateCurrentThread, ReenterAfterTermination); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 336 | v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 337 | v8::Context::Scope context_scope(context); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 338 | CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating()); |
| 339 | // Create script strings upfront as it won't work when terminating. |
| 340 | reenter_script_1.Reset(isolate, v8_str( |
| 341 | "function f() {" |
| 342 | " var term = true;" |
| 343 | " try {" |
| 344 | " while(true) {" |
| 345 | " if (term) terminate();" |
| 346 | " term = false;" |
| 347 | " }" |
| 348 | " fail();" |
| 349 | " } catch(e) {" |
| 350 | " fail();" |
| 351 | " }" |
| 352 | "}" |
| 353 | "f()")); |
| 354 | reenter_script_2.Reset(isolate, v8_str("function f() { fail(); } f()")); |
| 355 | CompileRun("try { loop(); fail(); } catch(e) { fail(); }"); |
| 356 | CHECK(!isolate->IsExecutionTerminating()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 357 | // Check we can run JS again after termination. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 358 | CHECK(CompileRun("function f() { return true; } f()")->IsTrue()); |
| 359 | reenter_script_1.Reset(); |
| 360 | reenter_script_2.Reset(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 361 | } |
| 362 | |
| 363 | |
| 364 | void DoLoopCancelTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 365 | v8::TryCatch try_catch(args.GetIsolate()); |
| 366 | CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating()); |
| 367 | v8::MaybeLocal<v8::Value> result = |
| 368 | CompileRun(args.GetIsolate()->GetCurrentContext(), |
| 369 | "var term = true;" |
| 370 | "while(true) {" |
| 371 | " if (term) terminate();" |
| 372 | " term = false;" |
| 373 | "}" |
| 374 | "fail();"); |
| 375 | CHECK(result.IsEmpty()); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 376 | CHECK(try_catch.HasCaught()); |
| 377 | CHECK(try_catch.Exception()->IsNull()); |
| 378 | CHECK(try_catch.Message().IsEmpty()); |
| 379 | CHECK(!try_catch.CanContinue()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 380 | CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 381 | CHECK(try_catch.HasTerminated()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 382 | CcTest::isolate()->CancelTerminateExecution(); |
| 383 | CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating()); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 384 | } |
| 385 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 386 | |
| 387 | // Test that a single thread of JavaScript execution can terminate |
| 388 | // itself and then resume execution. |
| 389 | TEST(TerminateCancelTerminateFromThreadItself) { |
| 390 | v8::Isolate* isolate = CcTest::isolate(); |
| 391 | v8::HandleScope scope(isolate); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 392 | v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate( |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 393 | isolate, TerminateCurrentThread, DoLoopCancelTerminate); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 394 | v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 395 | v8::Context::Scope context_scope(context); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 396 | CHECK(!CcTest::isolate()->IsExecutionTerminating()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 397 | // Check that execution completed with correct return value. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 398 | v8::Local<v8::Value> result = |
| 399 | CompileRun(isolate->GetCurrentContext(), |
| 400 | "try { doloop(); } catch(e) { fail(); } 'completed';") |
| 401 | .ToLocalChecked(); |
| 402 | CHECK(result->Equals(isolate->GetCurrentContext(), v8_str("completed")) |
| 403 | .FromJust()); |
Leon Clarke | f7060e2 | 2010-06-03 12:02:55 +0100 | [diff] [blame] | 404 | } |
| 405 | |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 406 | |
| 407 | void MicrotaskShouldNotRun(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 408 | CHECK(false); |
| 409 | } |
| 410 | |
| 411 | |
| 412 | void MicrotaskLoopForever(const v8::FunctionCallbackInfo<v8::Value>& info) { |
| 413 | v8::Isolate* isolate = info.GetIsolate(); |
| 414 | v8::HandleScope scope(isolate); |
| 415 | // Enqueue another should-not-run task to ensure we clean out the queue |
| 416 | // when we terminate. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 417 | isolate->EnqueueMicrotask( |
| 418 | v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun) |
| 419 | .ToLocalChecked()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 420 | CompileRun("terminate(); while (true) { }"); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 421 | CHECK(v8::Isolate::GetCurrent()->IsExecutionTerminating()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 422 | } |
| 423 | |
| 424 | |
| 425 | TEST(TerminateFromOtherThreadWhileMicrotaskRunning) { |
| 426 | semaphore = new v8::base::Semaphore(0); |
| 427 | TerminatorThread thread(CcTest::i_isolate()); |
| 428 | thread.Start(); |
| 429 | |
| 430 | v8::Isolate* isolate = CcTest::isolate(); |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 431 | isolate->SetMicrotasksPolicy(v8::MicrotasksPolicy::kExplicit); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 432 | v8::HandleScope scope(isolate); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 433 | v8::Local<v8::ObjectTemplate> global = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 434 | CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 435 | v8::Local<v8::Context> context = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 436 | v8::Context::New(CcTest::isolate(), NULL, global); |
| 437 | v8::Context::Scope context_scope(context); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 438 | isolate->EnqueueMicrotask( |
| 439 | v8::Function::New(isolate->GetCurrentContext(), MicrotaskLoopForever) |
| 440 | .ToLocalChecked()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 441 | // The second task should never be run because we bail out if we're |
| 442 | // terminating. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 443 | isolate->EnqueueMicrotask( |
| 444 | v8::Function::New(isolate->GetCurrentContext(), MicrotaskShouldNotRun) |
| 445 | .ToLocalChecked()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 446 | isolate->RunMicrotasks(); |
| 447 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 448 | isolate->CancelTerminateExecution(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 449 | isolate->RunMicrotasks(); // should not run MicrotaskShouldNotRun |
| 450 | |
| 451 | thread.Join(); |
| 452 | delete semaphore; |
| 453 | semaphore = NULL; |
| 454 | } |
| 455 | |
| 456 | |
| 457 | static int callback_counter = 0; |
| 458 | |
| 459 | |
| 460 | static void CounterCallback(v8::Isolate* isolate, void* data) { |
| 461 | callback_counter++; |
| 462 | } |
| 463 | |
| 464 | |
| 465 | TEST(PostponeTerminateException) { |
| 466 | v8::Isolate* isolate = CcTest::isolate(); |
| 467 | v8::HandleScope scope(isolate); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 468 | v8::Local<v8::ObjectTemplate> global = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 469 | CreateGlobalTemplate(CcTest::isolate(), TerminateCurrentThread, DoLoop); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 470 | v8::Local<v8::Context> context = |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 471 | v8::Context::New(CcTest::isolate(), NULL, global); |
| 472 | v8::Context::Scope context_scope(context); |
| 473 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 474 | v8::TryCatch try_catch(isolate); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 475 | static const char* terminate_and_loop = |
| 476 | "terminate(); for (var i = 0; i < 10000; i++);"; |
| 477 | |
| 478 | { // Postpone terminate execution interrupts. |
| 479 | i::PostponeInterruptsScope p1(CcTest::i_isolate(), |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 480 | i::StackGuard::TERMINATE_EXECUTION); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 481 | |
| 482 | // API interrupts should still be triggered. |
| 483 | CcTest::isolate()->RequestInterrupt(&CounterCallback, NULL); |
| 484 | CHECK_EQ(0, callback_counter); |
| 485 | CompileRun(terminate_and_loop); |
| 486 | CHECK(!try_catch.HasTerminated()); |
| 487 | CHECK_EQ(1, callback_counter); |
| 488 | |
| 489 | { // Postpone API interrupts as well. |
| 490 | i::PostponeInterruptsScope p2(CcTest::i_isolate(), |
| 491 | i::StackGuard::API_INTERRUPT); |
| 492 | |
| 493 | // None of the two interrupts should trigger. |
| 494 | CcTest::isolate()->RequestInterrupt(&CounterCallback, NULL); |
| 495 | CompileRun(terminate_and_loop); |
| 496 | CHECK(!try_catch.HasTerminated()); |
| 497 | CHECK_EQ(1, callback_counter); |
| 498 | } |
| 499 | |
| 500 | // Now the previously requested API interrupt should trigger. |
| 501 | CompileRun(terminate_and_loop); |
| 502 | CHECK(!try_catch.HasTerminated()); |
| 503 | CHECK_EQ(2, callback_counter); |
| 504 | } |
| 505 | |
| 506 | // Now the previously requested terminate execution interrupt should trigger. |
| 507 | CompileRun("for (var i = 0; i < 10000; i++);"); |
| 508 | CHECK(try_catch.HasTerminated()); |
| 509 | CHECK_EQ(2, callback_counter); |
| 510 | } |
| 511 | |
| 512 | |
| 513 | TEST(ErrorObjectAfterTermination) { |
| 514 | v8::Isolate* isolate = CcTest::isolate(); |
| 515 | v8::HandleScope scope(isolate); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 516 | v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 517 | v8::Context::Scope context_scope(context); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 518 | isolate->TerminateExecution(); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 519 | v8::Local<v8::Value> error = v8::Exception::Error(v8_str("error")); |
| 520 | // TODO(yangguo): crbug/403509. Check for empty handle instead. |
| 521 | CHECK(error->IsUndefined()); |
| 522 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 523 | |
| 524 | |
| 525 | void InnerTryCallTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 526 | CHECK(!args.GetIsolate()->IsExecutionTerminating()); |
| 527 | v8::Local<v8::Object> global = CcTest::global(); |
| 528 | v8::Local<v8::Function> loop = v8::Local<v8::Function>::Cast( |
| 529 | global->Get(CcTest::isolate()->GetCurrentContext(), v8_str("loop")) |
| 530 | .ToLocalChecked()); |
| 531 | i::MaybeHandle<i::Object> result = |
| 532 | i::Execution::TryCall(CcTest::i_isolate(), v8::Utils::OpenHandle((*loop)), |
| 533 | v8::Utils::OpenHandle((*global)), 0, NULL, NULL); |
| 534 | CHECK(result.is_null()); |
| 535 | // TryCall ignores terminate execution, but rerequests the interrupt. |
| 536 | CHECK(!args.GetIsolate()->IsExecutionTerminating()); |
| 537 | CHECK(CompileRun("1 + 1;").IsEmpty()); |
| 538 | } |
| 539 | |
| 540 | |
| 541 | TEST(TerminationInInnerTryCall) { |
| 542 | v8::Isolate* isolate = CcTest::isolate(); |
| 543 | v8::HandleScope scope(isolate); |
| 544 | v8::Local<v8::ObjectTemplate> global_template = CreateGlobalTemplate( |
| 545 | CcTest::isolate(), TerminateCurrentThread, DoLoopNoCall); |
| 546 | global_template->Set( |
| 547 | v8_str("inner_try_call_terminate"), |
| 548 | v8::FunctionTemplate::New(isolate, InnerTryCallTerminate)); |
| 549 | v8::Local<v8::Context> context = |
| 550 | v8::Context::New(CcTest::isolate(), NULL, global_template); |
| 551 | v8::Context::Scope context_scope(context); |
| 552 | { |
| 553 | v8::TryCatch try_catch(isolate); |
| 554 | CompileRun("inner_try_call_terminate()"); |
| 555 | CHECK(try_catch.HasTerminated()); |
| 556 | } |
| 557 | v8::Maybe<int32_t> result = CompileRun("2 + 2")->Int32Value( |
| 558 | v8::Isolate::GetCurrent()->GetCurrentContext()); |
| 559 | CHECK_EQ(4, result.FromJust()); |
| 560 | CHECK(!v8::Isolate::GetCurrent()->IsExecutionTerminating()); |
| 561 | } |
| 562 | |
| 563 | |
| 564 | TEST(TerminateAndTryCall) { |
| 565 | i::FLAG_allow_natives_syntax = true; |
| 566 | v8::Isolate* isolate = CcTest::isolate(); |
| 567 | v8::HandleScope scope(isolate); |
| 568 | v8::Local<v8::ObjectTemplate> global = CreateGlobalTemplate( |
| 569 | isolate, TerminateCurrentThread, DoLoopCancelTerminate); |
| 570 | v8::Local<v8::Context> context = v8::Context::New(isolate, NULL, global); |
| 571 | v8::Context::Scope context_scope(context); |
| 572 | CHECK(!isolate->IsExecutionTerminating()); |
| 573 | v8::TryCatch try_catch(isolate); |
| 574 | CHECK(!isolate->IsExecutionTerminating()); |
| 575 | // Terminate execution has been triggered inside TryCall, but re-requested |
| 576 | // to trigger later. |
| 577 | CHECK(CompileRun("terminate(); reference_error();").IsEmpty()); |
| 578 | CHECK(try_catch.HasCaught()); |
| 579 | CHECK(!isolate->IsExecutionTerminating()); |
| 580 | v8::Local<v8::Value> value = |
| 581 | CcTest::global() |
| 582 | ->Get(isolate->GetCurrentContext(), v8_str("terminate")) |
| 583 | .ToLocalChecked(); |
| 584 | CHECK(value->IsFunction()); |
| 585 | // The first stack check after terminate has been re-requested fails. |
| 586 | CHECK(CompileRun("1 + 1").IsEmpty()); |
| 587 | CHECK(!isolate->IsExecutionTerminating()); |
| 588 | // V8 then recovers. |
| 589 | v8::Maybe<int32_t> result = CompileRun("2 + 2")->Int32Value( |
| 590 | v8::Isolate::GetCurrent()->GetCurrentContext()); |
| 591 | CHECK_EQ(4, result.FromJust()); |
| 592 | CHECK(!isolate->IsExecutionTerminating()); |
| 593 | } |