diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 32730ebc5f1332..218e919a167727 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -1006,6 +1006,24 @@ class ScriptOrigin { Handle script_id_; }; +class V8_EXPORT SealHandleScope { + public: + SealHandleScope(Isolate* isolate); + ~SealHandleScope(); + + private: + // Make it hard to create heap-allocated or illegal handle scopes by + // disallowing certain operations. + SealHandleScope(const SealHandleScope&); + void operator=(const SealHandleScope&); + void* operator new(size_t size); + void operator delete(void*, size_t); + + internal::Isolate* isolate_; + int prev_level_; + internal::Object** prev_limit_; +}; + /** * A compiled JavaScript script, not yet tied to a Context. diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 88d3c889b96cab..c3f84534b4b5df 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -515,6 +515,27 @@ i::Object** EscapableHandleScope::Escape(i::Object** escape_value) { } +SealHandleScope::SealHandleScope(Isolate* isolate) { + i::Isolate* internal_isolate = reinterpret_cast(isolate); + + isolate_ = internal_isolate; + i::HandleScopeData* current = internal_isolate->handle_scope_data(); + prev_limit_ = current->limit; + current->limit = current->next; + prev_level_ = current->level; + current->level = 0; +} + + +SealHandleScope::~SealHandleScope() { + i::HandleScopeData* current = isolate_->handle_scope_data(); + DCHECK_EQ(0, current->level); + current->level = prev_level_; + DCHECK_EQ(current->next, current->limit); + current->limit = prev_limit_; +} + + void Context::Enter() { i::Handle env = Utils::OpenHandle(this); i::Isolate* isolate = env->GetIsolate(); diff --git a/deps/v8/src/api.h b/deps/v8/src/api.h index 1d2a8c8e82829f..62f9f1e0b351b0 100644 --- a/deps/v8/src/api.h +++ b/deps/v8/src/api.h @@ -650,7 +650,7 @@ void HandleScopeImplementer::DeleteExtensions(internal::Object** prev_limit) { while (!blocks_.is_empty()) { internal::Object** block_start = blocks_.last(); internal::Object** block_limit = block_start + kHandleBlockSize; -#ifdef DEBUG + // SealHandleScope may make the prev_limit to point inside the block. if (block_start <= prev_limit && prev_limit <= block_limit) { #ifdef ENABLE_HANDLE_ZAPPING @@ -658,9 +658,6 @@ void HandleScopeImplementer::DeleteExtensions(internal::Object** prev_limit) { #endif break; } -#else - if (prev_limit == block_limit) break; -#endif blocks_.RemoveLast(); #ifdef ENABLE_HANDLE_ZAPPING diff --git a/deps/v8/test/cctest/cctest.status b/deps/v8/test/cctest/cctest.status index cc5414da27364e..95fb839aa6236d 100644 --- a/deps/v8/test/cctest/cctest.status +++ b/deps/v8/test/cctest/cctest.status @@ -40,6 +40,7 @@ # they don't fail then test.py has failed. 'test-serialize/TestThatAlwaysFails': [FAIL], 'test-serialize/DependentTestThatAlwaysFails': [FAIL], + 'test-api/SealHandleScope': [FAIL], # This test always fails. It tests that LiveEdit causes abort when turned off. 'test-debug/LiveEditDisabled': [FAIL], diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index 06cf5532e244e3..17de186b9c4c3d 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -21895,6 +21895,38 @@ void CallCompletedCallbackException() { } +TEST(SealHandleScope) { + v8::Isolate* isolate = CcTest::isolate(); + v8::HandleScope handle_scope(isolate); + LocalContext env; + + v8::SealHandleScope seal(isolate); + + // Should fail + v8::Local obj = v8::Object::New(isolate); + + USE(obj); +} + + +TEST(SealHandleScopeNested) { + v8::Isolate* isolate = CcTest::isolate(); + v8::HandleScope handle_scope(isolate); + LocalContext env; + + v8::SealHandleScope seal(isolate); + + { + v8::HandleScope handle_scope(isolate); + + // Should work + v8::Local obj = v8::Object::New(isolate); + + USE(obj); + } +} + + TEST(CallCompletedCallbackOneException) { LocalContext env; v8::HandleScope scope(env->GetIsolate()); diff --git a/src/node.cc b/src/node.cc index db16e0b49272f2..c2c7db06ef4172 100644 --- a/src/node.cc +++ b/src/node.cc @@ -101,6 +101,7 @@ using v8::ObjectTemplate; using v8::Promise; using v8::PromiseRejectMessage; using v8::PropertyCallbackInfo; +using v8::SealHandleScope; using v8::String; using v8::TryCatch; using v8::Uint32; @@ -3822,22 +3823,25 @@ static void StartNodeInstance(void* arg) { if (instance_data->use_debug_agent()) EnableDebug(env); - bool more; - do { - v8::platform::PumpMessageLoop(default_platform, isolate); - more = uv_run(env->event_loop(), UV_RUN_ONCE); - - if (more == false) { + { + SealHandleScope seal(isolate); + bool more; + do { v8::platform::PumpMessageLoop(default_platform, isolate); - EmitBeforeExit(env); + more = uv_run(env->event_loop(), UV_RUN_ONCE); - // Emit `beforeExit` if the loop became alive either after emitting - // event, or after running some callbacks. - more = uv_loop_alive(env->event_loop()); - if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0) - more = true; - } - } while (more == true); + if (more == false) { + v8::platform::PumpMessageLoop(default_platform, isolate); + EmitBeforeExit(env); + + // Emit `beforeExit` if the loop became alive either after emitting + // event, or after running some callbacks. + more = uv_loop_alive(env->event_loop()); + if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0) + more = true; + } + } while (more == true); + } int exit_code = EmitExit(env); if (instance_data->is_main())