diff --git a/src/inspector/inspected-context.cc b/src/inspector/inspected-context.cc index d7a2f810db..5f0284385c 100644 --- a/src/inspector/inspected-context.cc +++ b/src/inspector/inspected-context.cc @@ -15,6 +15,38 @@ namespace v8_inspector { +class InspectedContext::WeakCallbackData { + public: + WeakCallbackData(InspectedContext* context, V8InspectorImpl* inspector, + int groupId, int contextId) + : m_context(context), + m_inspector(inspector), + m_groupId(groupId), + m_contextId(contextId) {} + + static void resetContext(const v8::WeakCallbackInfo& data) { + // InspectedContext is alive here because weak handler is still alive. + data.GetParameter()->m_context->m_context.Reset(); + data.SetSecondPassCallback(&callContextCollected); + } + + static void callContextCollected( + const v8::WeakCallbackInfo& data) { + // InspectedContext can be dead here since anything can happen between first + // and second pass callback. + WeakCallbackData* callbackData = data.GetParameter(); + callbackData->m_inspector->contextCollected(callbackData->m_groupId, + callbackData->m_contextId); + delete callbackData; + } + + private: + InspectedContext* m_context; + V8InspectorImpl* m_inspector; + int m_groupId; + int m_contextId; +}; + InspectedContext::InspectedContext(V8InspectorImpl* inspector, const V8ContextInfo& info, int contextId) : m_inspector(inspector), @@ -35,6 +67,10 @@ InspectedContext::InspectedContext(V8InspectorImpl* inspector, m_inspector->console()->installMemoryGetter( info.context, v8::Local::Cast(console)); } + m_context.SetWeak( + new WeakCallbackData(this, m_inspector, m_contextGroupId, m_contextId), + &InspectedContext::WeakCallbackData::resetContext, + v8::WeakCallbackType::kParameter); } InspectedContext::~InspectedContext() { diff --git a/src/inspector/inspected-context.h b/src/inspector/inspected-context.h index b32263bc2e..239e0f477b 100644 --- a/src/inspector/inspected-context.h +++ b/src/inspector/inspected-context.h @@ -47,6 +47,8 @@ class InspectedContext { friend class V8InspectorImpl; InspectedContext(V8InspectorImpl*, const V8ContextInfo&, int contextId); + class WeakCallbackData; + V8InspectorImpl* m_inspector; v8::Global m_context; int m_contextId; diff --git a/src/inspector/v8-inspector-impl.cc b/src/inspector/v8-inspector-impl.cc index 6b8e7324f5..0a7b19a36a 100644 --- a/src/inspector/v8-inspector-impl.cc +++ b/src/inspector/v8-inspector-impl.cc @@ -203,6 +203,10 @@ void V8InspectorImpl::contextCreated(const V8ContextInfo& info) { void V8InspectorImpl::contextDestroyed(v8::Local context) { int contextId = InspectedContext::contextId(context); int groupId = contextGroupId(context); + contextCollected(groupId, contextId); +} + +void V8InspectorImpl::contextCollected(int groupId, int contextId) { m_contextIdToGroupIdMap.erase(contextId); ConsoleStorageMap::iterator storageIt = m_consoleStorageMap.find(groupId); diff --git a/src/inspector/v8-inspector-impl.h b/src/inspector/v8-inspector-impl.h index 3effb39f7c..a7666dfd42 100644 --- a/src/inspector/v8-inspector-impl.h +++ b/src/inspector/v8-inspector-impl.h @@ -74,6 +74,7 @@ class V8InspectorImpl : public V8Inspector { const StringView& state) override; void contextCreated(const V8ContextInfo&) override; void contextDestroyed(v8::Local) override; + void contextCollected(int contextGroupId, int contextId); void resetContextGroup(int contextGroupId) override; void idleStarted() override; void idleFinished() override; diff --git a/test/inspector/inspector-test.cc b/test/inspector/inspector-test.cc index 930d6c9477..767168b297 100644 --- a/test/inspector/inspector-test.cc +++ b/test/inspector/inspector-test.cc @@ -642,6 +642,9 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { inspector->Set(ToV8String(isolate, "fireContextDestroyed"), v8::FunctionTemplate::New( isolate, &InspectorExtension::FireContextDestroyed)); + inspector->Set( + ToV8String(isolate, "freeContext"), + v8::FunctionTemplate::New(isolate, &InspectorExtension::FreeContext)); inspector->Set(ToV8String(isolate, "addInspectedObject"), v8::FunctionTemplate::New( isolate, &InspectorExtension::AddInspectedObject)); @@ -683,6 +686,12 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { data->FireContextDestroyed(context); } + static void FreeContext(const v8::FunctionCallbackInfo& args) { + v8::Local context = args.GetIsolate()->GetCurrentContext(); + IsolateData* data = IsolateData::FromContext(context); + data->FreeContext(context); + } + static void AddInspectedObject( const v8::FunctionCallbackInfo& args) { if (args.Length() != 2 || !args[0]->IsInt32()) { diff --git a/test/inspector/isolate-data.cc b/test/inspector/isolate-data.cc index 74c367a5e9..bd97a927e8 100644 --- a/test/inspector/isolate-data.cc +++ b/test/inspector/isolate-data.cc @@ -303,6 +303,13 @@ void IsolateData::FireContextDestroyed(v8::Local context) { inspector_->contextDestroyed(context); } +void IsolateData::FreeContext(v8::Local context) { + int context_group_id = GetContextGroupId(context); + auto it = contexts_.find(context_group_id); + if (it == contexts_.end()) return; + contexts_.erase(it); +} + std::vector IsolateData::GetSessionIds(int context_group_id) { std::vector result; for (auto& it : sessions_) { diff --git a/test/inspector/isolate-data.h b/test/inspector/isolate-data.h index a94316ff9b..c96a8d1bbd 100644 --- a/test/inspector/isolate-data.h +++ b/test/inspector/isolate-data.h @@ -68,6 +68,7 @@ class IsolateData : public v8_inspector::V8InspectorClient { void DumpAsyncTaskStacksStateForTest(); void FireContextCreated(v8::Local context, int context_group_id); void FireContextDestroyed(v8::Local context); + void FreeContext(v8::Local context); private: struct VectorCompare { diff --git a/test/inspector/runtime/context-destroyed-on-context-collected-expected.txt b/test/inspector/runtime/context-destroyed-on-context-collected-expected.txt new file mode 100644 index 0000000000..9a5e1708c1 --- /dev/null +++ b/test/inspector/runtime/context-destroyed-on-context-collected-expected.txt @@ -0,0 +1,7 @@ +Tests that contextDesrtoyed nofitication is fired when context is collected. +{ + method : Runtime.executionContextDestroyed + params : { + executionContextId : + } +} diff --git a/test/inspector/runtime/context-destroyed-on-context-collected.js b/test/inspector/runtime/context-destroyed-on-context-collected.js new file mode 100644 index 0000000000..9f715937c6 --- /dev/null +++ b/test/inspector/runtime/context-destroyed-on-context-collected.js @@ -0,0 +1,14 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +let {session, contextGroup, Protocol} = + InspectorTest.start('Tests that contextDesrtoyed nofitication is fired when context is collected.'); + +(async function test() { + await Protocol.Runtime.enable(); + Protocol.Runtime.onExecutionContextDestroyed(InspectorTest.logMessage); + contextGroup.addScript('inspector.freeContext()'); + await Protocol.HeapProfiler.collectGarbage(); + InspectorTest.completeTest(); +})();