From 1f85559a69fb385b9fd7d6724c6113e743ce6fec Mon Sep 17 00:00:00 2001 From: fedor Date: Fri, 10 Apr 2015 16:17:09 -0700 Subject: [PATCH] api: introduce SealHandleScope When debugging Handle leaks in io.js we found it very convenient to be able to Seal some specific (root in our case) scope to prevent Handle allocations in it, and easily find leakage. R=yangguo BUG= Review URL: https://codereview.chromium.org/1079713002 Cr-Commit-Position: refs/heads/master@{#27766} --- include/v8.h | 18 ++++++++++++++++++ src/api.cc | 21 +++++++++++++++++++++ src/api.h | 5 +---- test/cctest/cctest.status | 1 + test/cctest/test-api.cc | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 4 deletions(-) diff --git a/include/v8.h b/include/v8.h index 8019a4522..d94e9b32b 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1018,6 +1018,24 @@ class V8_EXPORT EscapableHandleScope : public HandleScope { internal::Object** escape_slot_; }; +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_; +}; + // --- Special objects --- diff --git a/src/api.cc b/src/api.cc index 28c84106d..733214ec0 100644 --- a/src/api.cc +++ b/src/api.cc @@ -674,6 +674,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/src/api.h b/src/api.h index fa8682bf5..7fce3e3b0 100644 --- a/src/api.h +++ b/src/api.h @@ -661,7 +661,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 @@ -669,9 +669,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/test/cctest/cctest.status b/test/cctest/cctest.status index 34cbab1dc..2fcf8a5e9 100644 --- a/test/cctest/cctest.status +++ b/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/test/cctest/test-api.cc b/test/cctest/test-api.cc index f7cbb8294..5f9612ee4 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -21900,3 +21900,35 @@ TEST(NewStringRangeError) { } free(buffer); } + + +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); + } +} -- 2.34.1