From 55849b167c16bb7f87b5c747b02d27f531bc4086 Mon Sep 17 00:00:00 2001 From: dgozman Date: Thu, 18 May 2017 16:11:20 -0700 Subject: [PATCH] [inspector] Refactor inspector test - moved all extensions to inspector_test.cc; - properly supported multiple context groups and sessions; - better isolation between components; - better infrastructure in protocol-test. BUG=chromium:590878 Review-Url: https://codereview.chromium.org/2890463004 Cr-Commit-Position: refs/heads/master@{#45409} --- .../console/destroy-context-during-log.js | 8 +- .../debugger/destory-in-break-program.js | 2 +- test/inspector/debugger/framework-break.js | 6 +- .../debugger/framework-precise-ranges.js | 2 +- test/inspector/debugger/framework-stepping.js | 6 +- .../debugger/get-possible-breakpoints.js | 2 +- .../inspector/debugger/inspector-break-api.js | 8 +- test/inspector/debugger/pause.js | 48 +- .../step-over-another-context-group.js | 20 +- .../stepping-ignores-injected-script.js | 2 +- .../stepping-with-exposed-injected-script.js | 2 +- test/inspector/debugger/wasm-scripts.js | 3 +- test/inspector/inspector-impl.cc | 305 +++------- test/inspector/inspector-impl.h | 84 +-- test/inspector/inspector-test.cc | 564 ++++++++++++------ test/inspector/isolate-data.cc | 18 +- test/inspector/isolate-data.h | 7 +- test/inspector/json-parse.js | 4 +- test/inspector/protocol-test.js | 242 ++++---- test/inspector/runtime/create-context.js | 39 +- test/inspector/runtime/runtime-restore.js | 8 +- test/inspector/task-runner.cc | 67 ++- test/inspector/task-runner.h | 37 +- 23 files changed, 785 insertions(+), 699 deletions(-) diff --git a/test/inspector/console/destroy-context-during-log.js b/test/inspector/console/destroy-context-during-log.js index 274165ca0c..0d3d13b697 100644 --- a/test/inspector/console/destroy-context-during-log.js +++ b/test/inspector/console/destroy-context-during-log.js @@ -10,11 +10,11 @@ const expression = ` delete Object.prototype.RemoteObject; this.RemoteObject = v; - inspector.detachInspector(); + inspector.fireContextDestroyed(); setTimeout(function() { // Attach the inspector again for the sake of establishing a // communication channel with the frontend test runner. - inspector.attachInspector(); + inspector.fireContextCreated(); console.log("End of test"); }, 0); }, @@ -23,8 +23,8 @@ const expression = ` // Before the whole script runs, the inspector is already attached. // Re-attach the inspector and trigger the console API to make sure that the // injected inspector script runs again (and triggers the above setter). - inspector.detachInspector(); - inspector.attachInspector(); + inspector.fireContextDestroyed(); + inspector.fireContextCreated(); console.log("First inspector activity after attaching inspector"); console.log("End of test"); `; diff --git a/test/inspector/debugger/destory-in-break-program.js b/test/inspector/debugger/destory-in-break-program.js index 6366b36250..799701da5a 100644 --- a/test/inspector/debugger/destory-in-break-program.js +++ b/test/inspector/debugger/destory-in-break-program.js @@ -8,6 +8,6 @@ InspectorTest.log('Check destroying agent inside of breakProgram'); await Protocol.Debugger.enable(); Protocol.Runtime.evaluate({expression: 'inspector.breakProgram(\'\', \'{}\')'}); await Protocol.Debugger.oncePaused(); - utils.disconnect(); + InspectorTest.session.disconnect(); utils.quit(); })(); diff --git a/test/inspector/debugger/framework-break.js b/test/inspector/debugger/framework-break.js index fc9eef166f..287de8a7b5 100644 --- a/test/inspector/debugger/framework-break.js +++ b/test/inspector/debugger/framework-break.js @@ -188,16 +188,16 @@ InspectorTest.runTestSuite([ }, function testAsyncDOMBreakpoint(next) { - utils.schedulePauseOnNextStatement('', ''); + InspectorTest.contextGroup.schedulePauseOnNextStatement('', ''); InspectorTest.log('> all frames in framework:'); Protocol.Runtime .evaluate( {expression: 'asyncDOMBreakpoint()//# sourceURL=framework.js'}) - .then(() => utils.cancelPauseOnNextStatement()) + .then(() => InspectorTest.contextGroup.cancelPauseOnNextStatement()) .then( () => Protocol.Runtime.evaluate( {expression: '42//# sourceURL=user.js'})) - .then(() => utils.schedulePauseOnNextStatement('', '')) + .then(() => InspectorTest.contextGroup.schedulePauseOnNextStatement('', '')) .then( () => Protocol.Runtime.evaluate( {expression: 'asyncDOMBreakpoint()//# sourceURL=user.js'})) diff --git a/test/inspector/debugger/framework-precise-ranges.js b/test/inspector/debugger/framework-precise-ranges.js index 3b23cf50df..bbc8ebdb9e 100644 --- a/test/inspector/debugger/framework-precise-ranges.js +++ b/test/inspector/debugger/framework-precise-ranges.js @@ -64,7 +64,7 @@ var testSuite = [ ]; function testPositions(positions) { - utils.schedulePauseOnNextStatement('', ''); + InspectorTest.contextGroup.schedulePauseOnNextStatement('', ''); return Protocol.Debugger .setBlackboxedRanges({scriptId: scriptId, positions: positions}) .then(InspectorTest.logMessage) diff --git a/test/inspector/debugger/framework-stepping.js b/test/inspector/debugger/framework-stepping.js index 6054228504..5135d0f81d 100644 --- a/test/inspector/debugger/framework-stepping.js +++ b/test/inspector/debugger/framework-stepping.js @@ -47,7 +47,7 @@ Protocol.Debugger.enable() var testSuite = [ function testStepIntoFromUser(next) { - utils.schedulePauseOnNextStatement('', ''); + InspectorTest.contextGroup.schedulePauseOnNextStatement('', ''); test('testStepFromUser()', [ 'print', // before testStepFromUser call 'stepInto', 'stepInto', 'print', // userFoo @@ -57,7 +57,7 @@ var testSuite = [ }, function testStepOverFromUser(next) { - utils.schedulePauseOnNextStatement('', ''); + InspectorTest.contextGroup.schedulePauseOnNextStatement('', ''); test('testStepFromUser()', [ 'print', // before testStepFromUser call 'stepInto', 'stepInto', 'print', // userFoo @@ -67,7 +67,7 @@ var testSuite = [ }, function testStepOutFromUser(next) { - utils.schedulePauseOnNextStatement('', ''); + InspectorTest.contextGroup.schedulePauseOnNextStatement('', ''); test('testStepFromUser()', [ 'print', // before testStepFromUser call 'stepInto', 'stepInto', 'print', // userFoo diff --git a/test/inspector/debugger/get-possible-breakpoints.js b/test/inspector/debugger/get-possible-breakpoints.js index ef996e910b..90872f8fa3 100644 --- a/test/inspector/debugger/get-possible-breakpoints.js +++ b/test/inspector/debugger/get-possible-breakpoints.js @@ -153,7 +153,7 @@ function foo6() { Promise.resolve().then(() => 42) }`; function compileScript(source, origin) { var promise = Protocol.Debugger.onceScriptParsed().then(message => message.params.scriptId); if (!origin) origin = { name: '', line_offset: 0, column_offset: 0 }; - utils.compileAndRunWithOrigin(source, origin.name, origin.line_offset, origin.column_offset, false); + InspectorTest.addScript(source, origin.line_offset, origin.column_offset, origin.name); return promise; } diff --git a/test/inspector/debugger/inspector-break-api.js b/test/inspector/debugger/inspector-break-api.js index 02f68ba40f..178da5b027 100644 --- a/test/inspector/debugger/inspector-break-api.js +++ b/test/inspector/debugger/inspector-break-api.js @@ -33,17 +33,17 @@ InspectorTest.runTestSuite([ }, function testSchedulePauseOnNextStatement(next) { - utils.schedulePauseOnNextStatement('reason', JSON.stringify({a: 42})); + InspectorTest.contextGroup.schedulePauseOnNextStatement('reason', JSON.stringify({a: 42})); Protocol.Runtime.evaluate({ expression: 'foo()//# sourceURL=expr1.js'}) .then(() => Protocol.Runtime.evaluate({ expression: 'foo()//# sourceURL=expr2.js'})) - .then(() => utils.cancelPauseOnNextStatement()) + .then(() => InspectorTest.contextGroup.cancelPauseOnNextStatement()) .then(next); }, function testCancelPauseOnNextStatement(next) { - utils.schedulePauseOnNextStatement('reason', JSON.stringify({a: 42})); - utils.cancelPauseOnNextStatement(); + InspectorTest.contextGroup.schedulePauseOnNextStatement('reason', JSON.stringify({a: 42})); + InspectorTest.contextGroup.cancelPauseOnNextStatement(); Protocol.Runtime.evaluate({ expression: 'foo()'}) .then(next); } diff --git a/test/inspector/debugger/pause.js b/test/inspector/debugger/pause.js index 1818e317c6..9ffa104ae2 100644 --- a/test/inspector/debugger/pause.js +++ b/test/inspector/debugger/pause.js @@ -24,50 +24,53 @@ InspectorTest.runAsyncTestSuite([ }, async function testSkipOtherContext1() { - let contextGroupId = utils.createContextGroup(); - Protocol.Debugger.enable({}, contextGroupId); + let contextGroup = InspectorTest.createContextGroup(); + let session = InspectorTest.createSession(contextGroup); + session.Protocol.Debugger.enable({}); Protocol.Debugger.pause(); Protocol.Runtime.evaluate({expression: 'var a = 42; //# sourceURL=framework.js'}); - Protocol.Runtime.evaluate({expression: 'var a = 239;'}, contextGroupId); + session.Protocol.Runtime.evaluate({expression: 'var a = 239;'}); Protocol.Runtime.evaluate({expression: 'var a = 1;'}); await waitPauseAndDumpLocation(); await Protocol.Debugger.resume(); - await Protocol.Debugger.disable({}, contextGroupId); + await session.Protocol.Debugger.disable({}); }, async function testSkipOtherContext2() { - let contextGroupId = utils.createContextGroup(); - Protocol.Debugger.enable({}, contextGroupId); - Protocol.Debugger.pause({}, contextGroupId); + let contextGroup = InspectorTest.createContextGroup(); + let session = InspectorTest.createSession(contextGroup); + InspectorTest.setupScriptMap(session); + session.Protocol.Debugger.enable({}); + session.Protocol.Debugger.pause({}); Protocol.Runtime.evaluate({expression: 'var a = 42; //# sourceURL=framework.js'}); - Protocol.Runtime.evaluate({expression: 'var a = 239;'}, contextGroupId); + session.Protocol.Runtime.evaluate({expression: 'var a = 239;'}); Protocol.Runtime.evaluate({expression: 'var a = 1;'}); - await waitPauseAndDumpLocation(); + await waitPauseAndDumpLocation(session); // should not resume pause from different context group id. Protocol.Debugger.resume(); - Protocol.Debugger.stepOver({}, contextGroupId); - await waitPauseAndDumpLocation(); - await Protocol.Debugger.resume({}, contextGroupId); - await Protocol.Debugger.disable({}, contextGroupId); + session.Protocol.Debugger.stepOver({}); + await waitPauseAndDumpLocation(session); + await session.Protocol.Debugger.resume({}); + await session.Protocol.Debugger.disable({}); }, async function testWithNativeBreakpoint() { - utils.schedulePauseOnNextStatement('', ''); + InspectorTest.contextGroup.schedulePauseOnNextStatement('', ''); await Protocol.Debugger.pause(); - utils.cancelPauseOnNextStatement(); + InspectorTest.contextGroup.cancelPauseOnNextStatement(); Protocol.Runtime.evaluate({expression: 'var a = 42;'}); await waitPauseAndDumpLocation(); await Protocol.Debugger.resume(); await Protocol.Debugger.pause(); - utils.schedulePauseOnNextStatement('', ''); - utils.cancelPauseOnNextStatement(); + InspectorTest.contextGroup.schedulePauseOnNextStatement('', ''); + InspectorTest.contextGroup.cancelPauseOnNextStatement(); Protocol.Runtime.evaluate({expression: 'var a = 42;'}); await waitPauseAndDumpLocation(); await Protocol.Debugger.resume(); - utils.schedulePauseOnNextStatement('', ''); - utils.cancelPauseOnNextStatement(); + InspectorTest.contextGroup.schedulePauseOnNextStatement('', ''); + InspectorTest.contextGroup.cancelPauseOnNextStatement(); await Protocol.Debugger.pause(); Protocol.Runtime.evaluate({expression: 'var a = 42;'}); await waitPauseAndDumpLocation(); @@ -85,9 +88,10 @@ InspectorTest.runAsyncTestSuite([ } ]); -async function waitPauseAndDumpLocation() { - var message = await Protocol.Debugger.oncePaused(); +async function waitPauseAndDumpLocation(session) { + session = session || InspectorTest.session; + var message = await session.Protocol.Debugger.oncePaused(); InspectorTest.log('paused at:'); - await InspectorTest.logSourceLocation(message.params.callFrames[0].location); + await InspectorTest.logSourceLocation(message.params.callFrames[0].location, session); return message; } diff --git a/test/inspector/debugger/step-over-another-context-group.js b/test/inspector/debugger/step-over-another-context-group.js index c860ef9f46..d262b4a758 100644 --- a/test/inspector/debugger/step-over-another-context-group.js +++ b/test/inspector/debugger/step-over-another-context-group.js @@ -7,24 +7,26 @@ InspectorTest.log('Checks stepping with more then one context group.'); (async function test() { InspectorTest.setupScriptMap(); await Protocol.Debugger.enable(); - let contextGroupId = utils.createContextGroup(); - await Protocol.Debugger.enable({}, contextGroupId); + let contextGroup = InspectorTest.createContextGroup(); + let session = InspectorTest.createSession(contextGroup); + InspectorTest.setupScriptMap(session); + await session.Protocol.Debugger.enable({}); Protocol.Runtime.evaluate({expression: 'debugger'}); - Protocol.Runtime.evaluate({expression: 'setTimeout(() => { debugger }, 0)'}, contextGroupId); + session.Protocol.Runtime.evaluate({expression: 'setTimeout(() => { debugger }, 0)'}); Protocol.Runtime.evaluate({expression: 'setTimeout(() => 42, 0)'}); - await waitPauseAndDumpLocation(); + await waitPauseAndDumpLocation(InspectorTest.session); Protocol.Debugger.stepOver(); await Protocol.Debugger.oncePaused(); Protocol.Debugger.stepOver(); - await waitPauseAndDumpLocation(); - await Protocol.Debugger.disable({}, contextGroupId); + await waitPauseAndDumpLocation(InspectorTest.session); + await session.Protocol.Debugger.disable({}); await Protocol.Debugger.disable(); InspectorTest.completeTest(); })(); -async function waitPauseAndDumpLocation() { - var message = await Protocol.Debugger.oncePaused(); +async function waitPauseAndDumpLocation(session) { + var message = await session.Protocol.Debugger.oncePaused(); InspectorTest.log('paused at:'); - await InspectorTest.logSourceLocation(message.params.callFrames[0].location); + await InspectorTest.logSourceLocation(message.params.callFrames[0].location, session); return message; } diff --git a/test/inspector/debugger/stepping-ignores-injected-script.js b/test/inspector/debugger/stepping-ignores-injected-script.js index 31c958084a..2b9e4aa7d7 100644 --- a/test/inspector/debugger/stepping-ignores-injected-script.js +++ b/test/inspector/debugger/stepping-ignores-injected-script.js @@ -3,7 +3,7 @@ // found in the LICENSE file. Protocol.Debugger.onPaused(message => { - let url = InspectorTest._scriptMap.get(message.params.callFrames[0].location.scriptId).url; + let url = InspectorTest.session._scriptMap.get(message.params.callFrames[0].location.scriptId).url; if (url !== 'test.js') { InspectorTest.log('InjectedSciptSource on stack.'); InspectorTest.completeTest(); diff --git a/test/inspector/debugger/stepping-with-exposed-injected-script.js b/test/inspector/debugger/stepping-with-exposed-injected-script.js index 499611c897..54b93d54a4 100644 --- a/test/inspector/debugger/stepping-with-exposed-injected-script.js +++ b/test/inspector/debugger/stepping-with-exposed-injected-script.js @@ -4,7 +4,7 @@ // Flags: --expose-inspector-scripts Protocol.Debugger.onPaused(message => { - let url = InspectorTest._scriptMap.get(message.params.callFrames[0].location.scriptId).url; + let url = InspectorTest.session._scriptMap.get(message.params.callFrames[0].location.scriptId).url; if (url !== 'test.js') { InspectorTest.log('InjectedSciptSource on stack.'); InspectorTest.completeTest(); diff --git a/test/inspector/debugger/wasm-scripts.js b/test/inspector/debugger/wasm-scripts.js index 9fe8c26e6a..d3ab1325ce 100644 --- a/test/inspector/debugger/wasm-scripts.js +++ b/test/inspector/debugger/wasm-scripts.js @@ -27,8 +27,7 @@ function testFunction(bytes) { new WebAssembly.Module(buffer); } -InspectorTest.addScriptWithUrl( - testFunction.toString(), 'v8://test/testFunction'); +InspectorTest.addScript(testFunction.toString(), 0, 0, 'v8://test/testFunction'); InspectorTest.addScript('var module_bytes = ' + JSON.stringify(module_bytes)); Protocol.Debugger.enable(); diff --git a/test/inspector/inspector-impl.cc b/test/inspector/inspector-impl.cc index 16048f1f25..664fdf8366 100644 --- a/test/inspector/inspector-impl.cc +++ b/test/inspector/inspector-impl.cc @@ -7,41 +7,35 @@ #include "include/v8.h" #include "src/vector.h" +#include "test/inspector/isolate-data.h" +#include "test/inspector/task-runner.h" namespace { -const int kInspectorClientIndex = 0; - class ChannelImpl final : public v8_inspector::V8Inspector::Channel { public: - explicit ChannelImpl(InspectorClientImpl::FrontendChannel* frontend_channel) - : frontend_channel_(frontend_channel) {} + ChannelImpl(InspectorClientImpl::FrontendChannel* frontend_channel, + int session_id) + : frontend_channel_(frontend_channel), session_id_(session_id) {} virtual ~ChannelImpl() = default; private: void sendResponse( int callId, std::unique_ptr message) override { - frontend_channel_->SendMessageToFrontend(message->string()); + frontend_channel_->SendMessageToFrontend(session_id_, message->string()); } void sendNotification( std::unique_ptr message) override { - frontend_channel_->SendMessageToFrontend(message->string()); + frontend_channel_->SendMessageToFrontend(session_id_, message->string()); } void flushProtocolNotifications() override {} InspectorClientImpl::FrontendChannel* frontend_channel_; + int session_id_; DISALLOW_COPY_AND_ASSIGN(ChannelImpl); }; -InspectorClientImpl* InspectorClientFromContext( - v8::Local context) { - InspectorClientImpl* inspector_client = static_cast( - context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex)); - CHECK(inspector_client); - return inspector_client; -} - v8::internal::Vector ToVector(v8::Local str) { v8::internal::Vector buffer = v8::internal::Vector::New(str->Length()); @@ -55,7 +49,7 @@ void MessageHandler(v8::Local message, v8::Local context = isolate->GetEnteredContext(); if (context.IsEmpty()) return; v8_inspector::V8Inspector* inspector = - InspectorClientImpl::InspectorFromContext(context); + IsolateData::FromContext(context)->inspector()->inspector(); v8::Local stack = message->GetStackTrace(); int script_id = @@ -106,154 +100,90 @@ void Print(v8::Isolate* isolate, const v8_inspector::StringView& string) { } } // namespace -class ConnectTask : public TaskRunner::Task { - public: - ConnectTask(InspectorClientImpl* client, v8::base::Semaphore* ready_semaphore) - : client_(client), ready_semaphore_(ready_semaphore) {} - virtual ~ConnectTask() = default; - - bool is_inspector_task() final { return true; } - - private: - void Run() override { - v8::HandleScope handle_scope(isolate()); - client_->connect(); - if (ready_semaphore_) ready_semaphore_->Signal(); - } - - InspectorClientImpl* client_; - v8::base::Semaphore* ready_semaphore_; -}; - -class DisconnectTask : public TaskRunner::Task { - public: - explicit DisconnectTask(InspectorClientImpl* client, bool reset_inspector, - v8::base::Semaphore* ready_semaphore) - : client_(client), - reset_inspector_(reset_inspector), - ready_semaphore_(ready_semaphore) {} - virtual ~DisconnectTask() = default; - - bool is_inspector_task() final { return true; } - - private: - void Run() override { - client_->disconnect(reset_inspector_); - if (ready_semaphore_) ready_semaphore_->Signal(); - } - - InspectorClientImpl* client_; - bool reset_inspector_; - v8::base::Semaphore* ready_semaphore_; -}; - -class CreateContextGroupTask : public TaskRunner::Task { - public: - CreateContextGroupTask(InspectorClientImpl* client, - IsolateData::SetupGlobalTasks setup_global_tasks, - v8::base::Semaphore* ready_semaphore, - int* context_group_id) - : client_(client), - setup_global_tasks_(std::move(setup_global_tasks)), - ready_semaphore_(ready_semaphore), - context_group_id_(context_group_id) {} - virtual ~CreateContextGroupTask() = default; - - bool is_inspector_task() final { return true; } - - private: - void Run() override { - *context_group_id_ = client_->createContextGroup(setup_global_tasks_); - if (ready_semaphore_) ready_semaphore_->Signal(); - } - - InspectorClientImpl* client_; - IsolateData::SetupGlobalTasks setup_global_tasks_; - v8::base::Semaphore* ready_semaphore_; - int* context_group_id_; -}; - -InspectorClientImpl::InspectorClientImpl(TaskRunner* task_runner, - FrontendChannel* frontend_channel, - v8::base::Semaphore* ready_semaphore) - : isolate_(nullptr), - task_runner_(task_runner), +InspectorClientImpl::InspectorClientImpl(v8::Isolate* isolate, + TaskRunner* task_runner, + FrontendChannel* frontend_channel) + : task_runner_(task_runner), + isolate_(isolate), frontend_channel_(frontend_channel) { - task_runner_->Append(new ConnectTask(this, ready_semaphore)); + isolate_->AddMessageListener(MessageHandler); + inspector_ = v8_inspector::V8Inspector::create(isolate_, this); } InspectorClientImpl::~InspectorClientImpl() {} -void InspectorClientImpl::connect() { - isolate_ = task_runner_->data()->isolate(); - isolate_->AddMessageListener(MessageHandler); - channel_.reset(new ChannelImpl(frontend_channel_)); - inspector_ = v8_inspector::V8Inspector::create(isolate_, this); - - if (states_.empty()) { - ConnectToContextGroup(task_runner_->default_context_group_id(), - v8_inspector::StringView()); - } else { - for (const auto& it : states_) - ConnectToContextGroup(it.first, it.second->string()); - } - states_.clear(); +int InspectorClientImpl::ConnectSession(int context_group_id, + const v8_inspector::StringView& state) { + int session_id = ++last_session_id_; + channels_[session_id].reset(new ChannelImpl(frontend_channel_, session_id)); + sessions_[session_id] = + inspector_->connect(context_group_id, channels_[session_id].get(), state); + context_group_by_session_[sessions_[session_id].get()] = context_group_id; + return session_id; } -void InspectorClientImpl::ConnectToContextGroup( - int context_group_id, v8_inspector::StringView state) { - v8::Local context = - task_runner_->data()->GetContext(context_group_id); - sessions_[context_group_id] = - inspector_->connect(context_group_id, channel_.get(), state); - context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this); +std::unique_ptr +InspectorClientImpl::DisconnectSession(int session_id) { + auto it = sessions_.find(session_id); + CHECK(it != sessions_.end()); + context_group_by_session_.erase(it->second.get()); + std::unique_ptr result = it->second->stateJSON(); + sessions_.erase(it); + channels_.erase(session_id); + return result; +} + +void InspectorClientImpl::SendMessage(int session_id, + const v8_inspector::StringView& message) { + auto it = sessions_.find(session_id); + if (it != sessions_.end()) it->second->dispatchProtocolMessage(message); +} + +void InspectorClientImpl::BreakProgram( + int context_group_id, const v8_inspector::StringView& reason, + const v8_inspector::StringView& details) { + for (int session_id : GetSessionIds(context_group_id)) { + auto it = sessions_.find(session_id); + if (it != sessions_.end()) it->second->breakProgram(reason, details); + } +} + +void InspectorClientImpl::SchedulePauseOnNextStatement( + int context_group_id, const v8_inspector::StringView& reason, + const v8_inspector::StringView& details) { + for (int session_id : GetSessionIds(context_group_id)) { + auto it = sessions_.find(session_id); + if (it != sessions_.end()) + it->second->schedulePauseOnNextStatement(reason, details); + } +} + +void InspectorClientImpl::CancelPauseOnNextStatement(int context_group_id) { + for (int session_id : GetSessionIds(context_group_id)) { + auto it = sessions_.find(session_id); + if (it != sessions_.end()) it->second->cancelPauseOnNextStatement(); + } +} + +void InspectorClientImpl::ContextCreated(v8::Local context, + int context_group_id) { v8_inspector::V8ContextInfo info(context, context_group_id, v8_inspector::StringView()); info.hasMemoryOnConsole = true; inspector_->contextCreated(info); } -void InspectorClientImpl::scheduleReconnect( - v8::base::Semaphore* ready_semaphore) { - task_runner_->Append( - new DisconnectTask(this, /* reset_inspector */ true, nullptr)); - task_runner_->Append(new ConnectTask(this, ready_semaphore)); +void InspectorClientImpl::ContextDestroyed(v8::Local context) { + inspector_->contextDestroyed(context); } -void InspectorClientImpl::scheduleDisconnect( - v8::base::Semaphore* ready_semaphore) { - task_runner_->Append( - new DisconnectTask(this, /* reset_inspector */ false, ready_semaphore)); -} - -void InspectorClientImpl::disconnect(bool reset_inspector) { - for (const auto& it : sessions_) { - states_[it.first] = it.second->stateJSON(); +std::vector InspectorClientImpl::GetSessionIds(int context_group_id) { + std::vector result; + for (auto& it : sessions_) { + if (context_group_by_session_[it.second.get()] == context_group_id) + result.push_back(it.first); } - sessions_.clear(); - if (reset_inspector) inspector_.reset(); -} - -void InspectorClientImpl::scheduleCreateContextGroup( - IsolateData::SetupGlobalTasks setup_global_tasks, - v8::base::Semaphore* ready_semaphore, int* context_group_id) { - task_runner_->Append(new CreateContextGroupTask( - this, std::move(setup_global_tasks), ready_semaphore, context_group_id)); -} - -int InspectorClientImpl::createContextGroup( - const IsolateData::SetupGlobalTasks& setup_global_tasks) { - v8::HandleScope handle_scope(isolate_); - int context_group_id = task_runner_->data()->CreateContextGroup(); - v8::Local context = - task_runner_->data()->GetContext(context_group_id); - context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this); - v8_inspector::StringView state; - sessions_[context_group_id] = - inspector_->connect(context_group_id, channel_.get(), state); - inspector_->contextCreated(v8_inspector::V8ContextInfo( - context, context_group_id, v8_inspector::StringView())); - return context_group_id; + return result; } bool InspectorClientImpl::formatAccessorsAsProperties( @@ -276,7 +206,7 @@ v8::Local InspectorClientImpl::ensureDefaultContextInGroup( return task_runner_->data()->GetContext(context_group_id); } -void InspectorClientImpl::setCurrentTimeMSForTest(double time) { +void InspectorClientImpl::SetCurrentTimeMSForTest(double time) { current_time_ = time; current_time_set_for_test_ = true; } @@ -286,12 +216,12 @@ double InspectorClientImpl::currentTimeMS() { return v8::base::OS::TimeCurrentMillis(); } -void InspectorClientImpl::setMemoryInfoForTest( +void InspectorClientImpl::SetMemoryInfoForTest( v8::Local memory_info) { memory_info_.Reset(isolate_, memory_info); } -void InspectorClientImpl::setLogConsoleApiMessageCalls(bool log) { +void InspectorClientImpl::SetLogConsoleApiMessageCalls(bool log) { log_console_api_message_calls_ = log; } @@ -322,78 +252,3 @@ void InspectorClientImpl::consoleAPIMessage( Print(isolate_, stack->toString()->string()); fprintf(stdout, "\n"); } - -v8_inspector::V8Inspector* InspectorClientImpl::InspectorFromContext( - v8::Local context) { - return InspectorClientFromContext(context)->inspector_.get(); -} - -v8_inspector::V8InspectorSession* InspectorClientImpl::SessionFromContext( - v8::Local context) { - InspectorClientImpl* client = InspectorClientFromContext(context); - for (auto& it : client->sessions_) { - if (client->task_runner_->data()->GetContext(it.first) == context) - return it.second.get(); - } - return nullptr; -} - -v8_inspector::V8InspectorSession* InspectorClientImpl::session( - int context_group_id) { - if (context_group_id) { - return sessions_[context_group_id].get(); - } else { - return sessions_.begin()->second.get(); - } -} - -class SendMessageToBackendTask : public TaskRunner::Task { - public: - explicit SendMessageToBackendTask( - const v8::internal::Vector& message, int context_group_id) - : message_(message), context_group_id_(context_group_id) {} - - bool is_inspector_task() final { return true; } - - private: - void Run() override { - v8_inspector::V8InspectorSession* session = nullptr; - { - v8::HandleScope handle_scope(isolate()); - if (!context_group_id_) { - session = InspectorClientImpl::SessionFromContext(default_context()); - } else { - session = InspectorClientFromContext(default_context()) - ->sessions_[context_group_id_] - .get(); - } - if (!session) return; - } - v8_inspector::StringView message_view(message_.start(), message_.length()); - session->dispatchProtocolMessage(message_view); - } - - v8::internal::Vector message_; - int context_group_id_; -}; - -TaskRunner* SendMessageToBackendExtension::backend_task_runner_ = nullptr; - -void SendMessageToBackendExtension::Run(v8::Isolate* isolate, - v8::Local global) { - global->Set( - v8::String::NewFromUtf8(isolate, "sendMessageToBackend", - v8::NewStringType::kNormal) - .ToLocalChecked(), - v8::FunctionTemplate::New( - isolate, &SendMessageToBackendExtension::SendMessageToBackend)); -} - -void SendMessageToBackendExtension::SendMessageToBackend( - const v8::FunctionCallbackInfo& args) { - CHECK(backend_task_runner_); - CHECK(args.Length() == 2 && args[0]->IsString() && args[1]->IsInt32()); - v8::Local message = args[0].As(); - backend_task_runner_->Append(new SendMessageToBackendTask( - ToVector(message), args[1].As()->Value())); -} diff --git a/test/inspector/inspector-impl.h b/test/inspector/inspector-impl.h index 92e83747f5..edbec72cfb 100644 --- a/test/inspector/inspector-impl.h +++ b/test/inspector/inspector-impl.h @@ -5,11 +5,15 @@ #ifndef V8_TEST_INSPECTOR_PROTOCOL_INSPECTOR_IMPL_H_ #define V8_TEST_INSPECTOR_PROTOCOL_INSPECTOR_IMPL_H_ +#include +#include + #include "include/v8-inspector.h" #include "include/v8.h" #include "src/base/macros.h" #include "src/base/platform/platform.h" -#include "test/inspector/task-runner.h" + +class TaskRunner; class InspectorClientImpl : public v8_inspector::V8InspectorClient { public: @@ -17,31 +21,30 @@ class InspectorClientImpl : public v8_inspector::V8InspectorClient { public: virtual ~FrontendChannel() = default; virtual void SendMessageToFrontend( - const v8_inspector::StringView& message) = 0; + int session_id, const v8_inspector::StringView& message) = 0; }; - InspectorClientImpl(TaskRunner* task_runner, - FrontendChannel* frontend_channel, - v8::base::Semaphore* ready_semaphore); + InspectorClientImpl(v8::Isolate* isolate, TaskRunner* task_runner, + FrontendChannel* frontend_channel); virtual ~InspectorClientImpl(); - void scheduleReconnect(v8::base::Semaphore* ready_semaphore); - void scheduleDisconnect(v8::base::Semaphore* ready_semaphore); - void scheduleCreateContextGroup( - IsolateData::SetupGlobalTasks setup_global_tasks, - v8::base::Semaphore* ready_semaphore, int* context_group_id); - - static v8_inspector::V8Inspector* InspectorFromContext( - v8::Local context); - static v8_inspector::V8InspectorSession* SessionFromContext( - v8::Local context); - - // context_group_id = 0 means default context group. - v8_inspector::V8InspectorSession* session(int context_group_id = 0); - - void setCurrentTimeMSForTest(double time); - void setMemoryInfoForTest(v8::Local memory_info); - void setLogConsoleApiMessageCalls(bool log); + v8_inspector::V8Inspector* inspector() const { return inspector_.get(); } + int ConnectSession(int context_group_id, + const v8_inspector::StringView& state); + std::unique_ptr DisconnectSession(int session_id); + void SendMessage(int session_id, const v8_inspector::StringView& message); + void BreakProgram(int context_group_id, + const v8_inspector::StringView& reason, + const v8_inspector::StringView& details); + void SchedulePauseOnNextStatement(int context_group_id, + const v8_inspector::StringView& reason, + const v8_inspector::StringView& details); + void CancelPauseOnNextStatement(int context_group_id); + void SetCurrentTimeMSForTest(double time); + void SetMemoryInfoForTest(v8::Local memory_info); + void SetLogConsoleApiMessageCalls(bool log); + void ContextCreated(v8::Local context, int context_group_id); + void ContextDestroyed(v8::Local context); private: // V8InspectorClient implementation. @@ -59,30 +62,18 @@ class InspectorClientImpl : public v8_inspector::V8InspectorClient { const v8_inspector::StringView& url, unsigned lineNumber, unsigned columnNumber, v8_inspector::V8StackTrace*) override; - friend class SendMessageToBackendTask; - friend class ConnectTask; - void connect(); - void ConnectToContextGroup(int context_group_id, - v8_inspector::StringView state); - friend class DisconnectTask; - void disconnect(bool reset_inspector); - friend class CreateContextGroupTask; - int createContextGroup( - const IsolateData::SetupGlobalTasks& setup_global_tasks); + std::vector GetSessionIds(int context_group_id); std::unique_ptr inspector_; - std::unique_ptr channel_; - + int last_session_id_ = 0; std::map> sessions_; - std::map> states_; - + std::map context_group_by_session_; + std::map> channels_; + TaskRunner* task_runner_; v8::Isolate* isolate_; v8::Global memory_info_; - - TaskRunner* task_runner_; FrontendChannel* frontend_channel_; - bool current_time_set_for_test_ = false; double current_time_ = 0.0; bool log_console_api_message_calls_ = false; @@ -90,19 +81,4 @@ class InspectorClientImpl : public v8_inspector::V8InspectorClient { DISALLOW_COPY_AND_ASSIGN(InspectorClientImpl); }; -class SendMessageToBackendExtension : public IsolateData::SetupGlobalTask { - public: - void Run(v8::Isolate* isolate, v8::Local global) override; - - static void set_backend_task_runner(TaskRunner* task_runner) { - backend_task_runner_ = task_runner; - } - - private: - static void SendMessageToBackend( - const v8::FunctionCallbackInfo& args); - - static TaskRunner* backend_task_runner_; -}; - #endif // V8_TEST_INSPECTOR_PROTOCOL_INSPECTOR_IMPL_H_ diff --git a/test/inspector/inspector-test.cc b/test/inspector/inspector-test.cc index f8e96ed90b..2e105c54d9 100644 --- a/test/inspector/inspector-test.cc +++ b/test/inspector/inspector-test.cc @@ -51,6 +51,196 @@ v8::Local ToV8String(v8::Isolate* isolate, const char* str) { .ToLocalChecked(); } +v8::internal::Vector ToVector( + const v8_inspector::StringView& string) { + v8::internal::Vector buffer = + v8::internal::Vector::New(static_cast(string.length())); + for (size_t i = 0; i < string.length(); i++) { + if (string.is8Bit()) + buffer[i] = string.characters8()[i]; + else + buffer[i] = string.characters16()[i]; + } + return buffer; +} + +class CreateContextGroupTask : public TaskRunner::Task { + public: + CreateContextGroupTask(v8::base::Semaphore* ready_semaphore, + int* context_group_id) + : ready_semaphore_(ready_semaphore), + context_group_id_(context_group_id) {} + virtual ~CreateContextGroupTask() = default; + bool is_inspector_task() final { return true; } + + private: + void Run() override { + *context_group_id_ = data()->CreateContextGroup(); + if (ready_semaphore_) ready_semaphore_->Signal(); + } + + v8::base::Semaphore* ready_semaphore_; + int* context_group_id_; +}; + +class ConnectSessionTask : public TaskRunner::Task { + public: + ConnectSessionTask(v8::base::Semaphore* ready_semaphore, int context_group_id, + const v8::internal::Vector& state, + int* session_id) + : ready_semaphore_(ready_semaphore), + context_group_id_(context_group_id), + state_(state), + session_id_(session_id) {} + virtual ~ConnectSessionTask() = default; + bool is_inspector_task() final { return true; } + + private: + void Run() override { + v8_inspector::StringView state(state_.start(), state_.length()); + *session_id_ = + data()->inspector()->ConnectSession(context_group_id_, state); + if (ready_semaphore_) ready_semaphore_->Signal(); + } + + v8::base::Semaphore* ready_semaphore_; + int context_group_id_; + const v8::internal::Vector& state_; + int* session_id_; +}; + +class DisconnectSessionTask : public TaskRunner::Task { + public: + DisconnectSessionTask(v8::base::Semaphore* ready_semaphore, int session_id, + v8::internal::Vector* state) + : ready_semaphore_(ready_semaphore), + session_id_(session_id), + state_(state) {} + virtual ~DisconnectSessionTask() = default; + bool is_inspector_task() final { return true; } + + private: + void Run() override { + std::unique_ptr state = + data()->inspector()->DisconnectSession(session_id_); + *state_ = ToVector(state->string()); + if (ready_semaphore_) ready_semaphore_->Signal(); + } + + v8::base::Semaphore* ready_semaphore_; + int session_id_; + v8::internal::Vector* state_; +}; + +class SendMessageToBackendTask : public TaskRunner::Task { + public: + explicit SendMessageToBackendTask( + int session_id, const v8::internal::Vector& message) + : session_id_(session_id), message_(message) {} + bool is_inspector_task() final { return true; } + + private: + void Run() override { + v8_inspector::StringView message_view(message_.start(), message_.length()); + data()->inspector()->SendMessage(session_id_, message_view); + } + + int session_id_; + v8::internal::Vector message_; +}; + +class SchedulePauseOnNextStatementTask : public TaskRunner::Task { + public: + SchedulePauseOnNextStatementTask( + v8::base::Semaphore* ready_semaphore, int context_group_id, + const v8::internal::Vector& reason, + const v8::internal::Vector& details) + : ready_semaphore_(ready_semaphore), + context_group_id_(context_group_id), + reason_(reason), + details_(details) {} + virtual ~SchedulePauseOnNextStatementTask() = default; + bool is_inspector_task() final { return true; } + + private: + void Run() override { + v8_inspector::StringView reason(reason_.start(), reason_.length()); + v8_inspector::StringView details(details_.start(), details_.length()); + data()->inspector()->SchedulePauseOnNextStatement(context_group_id_, reason, + details); + if (ready_semaphore_) ready_semaphore_->Signal(); + } + + v8::base::Semaphore* ready_semaphore_; + int context_group_id_; + const v8::internal::Vector& reason_; + const v8::internal::Vector& details_; +}; + +class CancelPauseOnNextStatementTask : public TaskRunner::Task { + public: + CancelPauseOnNextStatementTask(v8::base::Semaphore* ready_semaphore, + int context_group_id) + : ready_semaphore_(ready_semaphore), + context_group_id_(context_group_id) {} + virtual ~CancelPauseOnNextStatementTask() = default; + bool is_inspector_task() final { return true; } + + private: + void Run() override { + data()->inspector()->CancelPauseOnNextStatement(context_group_id_); + if (ready_semaphore_) ready_semaphore_->Signal(); + } + + v8::base::Semaphore* ready_semaphore_; + int context_group_id_; +}; + +class SendMessageToFrontendTask : public TaskRunner::Task { + public: + SendMessageToFrontendTask(int context_group_id, int session_id, + const v8::internal::Vector& message) + : context_group_id_(context_group_id), + session_id_(session_id), + message_(message) {} + virtual ~SendMessageToFrontendTask() {} + + bool is_inspector_task() final { return false; } + + static void Register(int session_id, v8::Isolate* isolate, + v8::Local dispatcher) { + dispatchers_[session_id].Reset(isolate, dispatcher); + } + + static void Unregister(int session_id) { dispatchers_.erase(session_id); } + + private: + void Run() override { + v8::MicrotasksScope microtasks_scope(isolate(), + v8::MicrotasksScope::kRunMicrotasks); + v8::HandleScope handle_scope(isolate()); + v8::Local context = data()->GetContext(context_group_id_); + v8::Context::Scope context_scope(context); + + if (dispatchers_.find(session_id_) == dispatchers_.end()) return; + v8::Local function = dispatchers_[session_id_].Get(isolate()); + v8::Local message = + v8::String::NewFromTwoByte(isolate(), message_.start(), + v8::NewStringType::kNormal, + static_cast(message_.size())) + .ToLocalChecked(); + v8::MaybeLocal result; + result = function->Call(context, context->Global(), 1, &message); + } + + static std::map> dispatchers_; + int context_group_id_; + int session_id_; + v8::internal::Vector message_; +}; + +std::map> SendMessageToFrontendTask::dispatchers_; + class UtilsExtension : public IsolateData::SetupGlobalTask { public: ~UtilsExtension() override = default; @@ -82,16 +272,21 @@ class UtilsExtension : public IsolateData::SetupGlobalTask { utils->Set(ToV8String(isolate, "cancelPauseOnNextStatement"), v8::FunctionTemplate::New( isolate, &UtilsExtension::CancelPauseOnNextStatement)); - utils->Set(ToV8String(isolate, "reconnect"), - v8::FunctionTemplate::New(isolate, &UtilsExtension::Reconnect)); - utils->Set(ToV8String(isolate, "disconnect"), - v8::FunctionTemplate::New(isolate, &UtilsExtension::Disconnect)); utils->Set(ToV8String(isolate, "setLogConsoleApiMessageCalls"), v8::FunctionTemplate::New( isolate, &UtilsExtension::SetLogConsoleApiMessageCalls)); utils->Set(ToV8String(isolate, "createContextGroup"), v8::FunctionTemplate::New(isolate, &UtilsExtension::CreateContextGroup)); + utils->Set( + ToV8String(isolate, "connectSession"), + v8::FunctionTemplate::New(isolate, &UtilsExtension::ConnectSession)); + utils->Set( + ToV8String(isolate, "disconnectSession"), + v8::FunctionTemplate::New(isolate, &UtilsExtension::DisconnectSession)); + utils->Set(ToV8String(isolate, "sendMessageToBackend"), + v8::FunctionTemplate::New( + isolate, &UtilsExtension::SendMessageToBackend)); global->Set(ToV8String(isolate, "utils"), utils); } @@ -99,13 +294,8 @@ class UtilsExtension : public IsolateData::SetupGlobalTask { backend_runner_ = runner; } - static void set_inspector_client(InspectorClientImpl* client) { - inspector_client_ = client; - } - private: static TaskRunner* backend_runner_; - static InspectorClientImpl* inspector_client_; static void Print(const v8::FunctionCallbackInfo& args) { for (int i = 0; i < args.Length(); i++) { @@ -189,27 +379,31 @@ class UtilsExtension : public IsolateData::SetupGlobalTask { } v8::internal::Vector chars; v8::Isolate* isolate = args.GetIsolate(); + v8::Local context = isolate->GetCurrentContext(); + IsolateData* data = IsolateData::FromContext(context); + int context_group_id = data->GetContextGroupId(context); if (ReadFile(isolate, args[0], &chars)) { - ExecuteStringTask(chars).RunOnTaskRunner( - IsolateData::FromContext(isolate->GetCurrentContext()) - ->task_runner()); + ExecuteStringTask(chars, context_group_id).RunOnIsolate(data); } } static void CompileAndRunWithOrigin( const v8::FunctionCallbackInfo& args) { - if (args.Length() != 5 || !args[0]->IsString() || !args[1]->IsString() || - !args[2]->IsInt32() || !args[3]->IsInt32() || !args[4]->IsBoolean()) { + if (args.Length() != 6 || !args[0]->IsInt32() || !args[1]->IsString() || + !args[2]->IsString() || !args[3]->IsInt32() || !args[4]->IsInt32() || + !args[5]->IsBoolean()) { fprintf(stderr, - "Internal error: compileAndRunWithOrigin(source, name, line, " + "Internal error: compileAndRunWithOrigin(context_group_id, " + "source, name, line, " "column, is_module)."); Exit(); } backend_runner_->Append(new ExecuteStringTask( - ToVector(args[0].As()), args[1].As(), - args[2].As(), args[3].As(), - args[4].As(), nullptr, nullptr)); + nullptr, args[0].As()->Value(), nullptr, + ToVector(args[1].As()), args[2].As(), + args[3].As(), args[4].As(), + args[5].As())); } static void SetCurrentTimeMSForTest( @@ -218,7 +412,7 @@ class UtilsExtension : public IsolateData::SetupGlobalTask { fprintf(stderr, "Internal error: setCurrentTimeMSForTest(time)."); Exit(); } - inspector_client_->setCurrentTimeMSForTest( + backend_runner_->data()->inspector()->SetCurrentTimeMSForTest( args[0].As()->Value()); } @@ -228,51 +422,36 @@ class UtilsExtension : public IsolateData::SetupGlobalTask { fprintf(stderr, "Internal error: setMemoryInfoForTest(value)."); Exit(); } - inspector_client_->setMemoryInfoForTest(args[0]); + backend_runner_->data()->inspector()->SetMemoryInfoForTest(args[0]); } static void SchedulePauseOnNextStatement( const v8::FunctionCallbackInfo& args) { - if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) { - fprintf( - stderr, - "Internal error: schedulePauseOnNextStatement('reason', 'details')."); + if (args.Length() != 3 || !args[0]->IsInt32() || !args[1]->IsString() || + !args[2]->IsString()) { + fprintf(stderr, + "Internal error: schedulePauseOnNextStatement(context_group_id, " + "'reason', 'details')."); Exit(); } - v8::internal::Vector reason = ToVector(args[0].As()); - v8_inspector::StringView reason_view(reason.start(), reason.length()); - v8::internal::Vector details = ToVector(args[1].As()); - v8_inspector::StringView details_view(details.start(), details.length()); - inspector_client_->session()->schedulePauseOnNextStatement(reason_view, - details_view); + v8::internal::Vector reason = ToVector(args[1].As()); + v8::internal::Vector details = ToVector(args[2].As()); + v8::base::Semaphore ready_semaphore(0); + backend_runner_->Append(new SchedulePauseOnNextStatementTask( + &ready_semaphore, args[0].As()->Value(), reason, details)); + ready_semaphore.Wait(); } static void CancelPauseOnNextStatement( const v8::FunctionCallbackInfo& args) { - if (args.Length() != 0) { - fprintf(stderr, "Internal error: cancelPauseOnNextStatement()."); - Exit(); - } - inspector_client_->session()->cancelPauseOnNextStatement(); - } - - static void Reconnect(const v8::FunctionCallbackInfo& args) { - if (args.Length() != 0) { - fprintf(stderr, "Internal error: reconnect()."); + if (args.Length() != 1 || !args[0]->IsInt32()) { + fprintf(stderr, + "Internal error: cancelPauseOnNextStatement(context_group_id)."); Exit(); } v8::base::Semaphore ready_semaphore(0); - inspector_client_->scheduleReconnect(&ready_semaphore); - ready_semaphore.Wait(); - } - - static void Disconnect(const v8::FunctionCallbackInfo& args) { - if (args.Length() != 0) { - fprintf(stderr, "Internal error: disconnect()."); - Exit(); - } - v8::base::Semaphore ready_semaphore(0); - inspector_client_->scheduleDisconnect(&ready_semaphore); + backend_runner_->Append(new CancelPauseOnNextStatementTask( + &ready_semaphore, args[0].As()->Value())); ready_semaphore.Wait(); } @@ -282,22 +461,86 @@ class UtilsExtension : public IsolateData::SetupGlobalTask { fprintf(stderr, "Internal error: setLogConsoleApiMessageCalls(bool)."); Exit(); } - inspector_client_->setLogConsoleApiMessageCalls( + backend_runner_->data()->inspector()->SetLogConsoleApiMessageCalls( args[0].As()->Value()); } static void CreateContextGroup( - const v8::FunctionCallbackInfo& args); + const v8::FunctionCallbackInfo& args) { + if (args.Length() != 0) { + fprintf(stderr, "Internal error: createContextGroup()."); + Exit(); + } + v8::base::Semaphore ready_semaphore(0); + int context_group_id = 0; + backend_runner_->Append( + new CreateContextGroupTask(&ready_semaphore, &context_group_id)); + ready_semaphore.Wait(); + args.GetReturnValue().Set( + v8::Int32::New(args.GetIsolate(), context_group_id)); + } + + static void ConnectSession(const v8::FunctionCallbackInfo& args) { + if (args.Length() != 3 || !args[0]->IsInt32() || !args[1]->IsString() || + !args[2]->IsFunction()) { + fprintf(stderr, + "Internal error: connectionSession(context_group_id, state, " + "dispatch)."); + Exit(); + } + v8::internal::Vector state = ToVector(args[1].As()); + v8::base::Semaphore ready_semaphore(0); + int session_id = 0; + backend_runner_->Append(new ConnectSessionTask( + &ready_semaphore, args[0].As()->Value(), state, + &session_id)); + ready_semaphore.Wait(); + SendMessageToFrontendTask::Register(session_id, args.GetIsolate(), + args[2].As()); + args.GetReturnValue().Set(v8::Int32::New(args.GetIsolate(), session_id)); + } + + static void DisconnectSession( + const v8::FunctionCallbackInfo& args) { + if (args.Length() != 1 || !args[0]->IsInt32()) { + fprintf(stderr, "Internal error: disconnectionSession(session_id)."); + Exit(); + } + int session_id = args[0].As()->Value(); + SendMessageToFrontendTask::Unregister(session_id); + v8::base::Semaphore ready_semaphore(0); + v8::internal::Vector state; + backend_runner_->Append( + new DisconnectSessionTask(&ready_semaphore, session_id, &state)); + ready_semaphore.Wait(); + args.GetReturnValue().Set( + v8::String::NewFromTwoByte(args.GetIsolate(), state.start(), + v8::NewStringType::kNormal, + static_cast(state.size())) + .ToLocalChecked()); + } + + static void SendMessageToBackend( + const v8::FunctionCallbackInfo& args) { + if (args.Length() != 2 || !args[0]->IsInt32() || !args[1]->IsString()) { + fprintf(stderr, + "Internal error: sendMessageToBackend(session_id, message)."); + Exit(); + } + backend_runner_->Append(new SendMessageToBackendTask( + args[0].As()->Value(), ToVector(args[1].As()))); + } }; TaskRunner* UtilsExtension::backend_runner_ = nullptr; -InspectorClientImpl* UtilsExtension::inspector_client_ = nullptr; class SetTimeoutTask : public AsyncTask { public: - SetTimeoutTask(v8::Isolate* isolate, v8::Local function, - const char* task_name, v8_inspector::V8Inspector* inspector) - : AsyncTask(task_name, inspector), function_(isolate, function) {} + SetTimeoutTask(IsolateData* data, int context_group_id, const char* task_name, + v8::Local function) + : AsyncTask(data, task_name), + function_(data->isolate(), function), + context_group_id_(context_group_id) {} virtual ~SetTimeoutTask() {} bool is_inspector_task() final { return false; } @@ -307,7 +550,7 @@ class SetTimeoutTask : public AsyncTask { v8::MicrotasksScope microtasks_scope(isolate(), v8::MicrotasksScope::kRunMicrotasks); v8::HandleScope handle_scope(isolate()); - v8::Local context = default_context(); + v8::Local context = data()->GetContext(context_group_id_); v8::Context::Scope context_scope(context); v8::Local function = function_.Get(isolate()); @@ -316,6 +559,7 @@ class SetTimeoutTask : public AsyncTask { } v8::Global function_; + int context_group_id_; }; class SetTimeoutExtension : public IsolateData::SetupGlobalTask { @@ -332,26 +576,27 @@ class SetTimeoutExtension : public IsolateData::SetupGlobalTask { if (args.Length() != 2 || !args[1]->IsNumber() || (!args[0]->IsFunction() && !args[0]->IsString()) || args[1].As()->Value() != 0.0) { - fprintf(stderr, - "Internal error: only setTimeout(function, 0) is supported."); + fprintf( + stderr, + "Internal error: only setTimeout(function|code, 0) is supported."); Exit(); } v8::Isolate* isolate = args.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); + IsolateData* data = IsolateData::FromContext(context); + int context_group_id = data->GetContextGroupId(context); std::unique_ptr task; - v8_inspector::V8Inspector* inspector = - InspectorClientImpl::InspectorFromContext(context); if (args[0]->IsFunction()) { - task.reset(new SetTimeoutTask(isolate, - v8::Local::Cast(args[0]), - "setTimeout", inspector)); + task.reset(new SetTimeoutTask(data, context_group_id, "setTimeout", + v8::Local::Cast(args[0]))); } else { task.reset(new ExecuteStringTask( + data, context_group_id, "setTimeout", ToVector(args[0].As()), v8::String::Empty(isolate), v8::Integer::New(isolate, 0), v8::Integer::New(isolate, 0), - v8::Boolean::New(isolate, false), "setTimeout", inspector)); + v8::Boolean::New(isolate, false))); } - IsolateData::FromContext(context)->task_runner()->Append(task.release()); + data->task_runner()->Append(task.release()); } }; @@ -368,12 +613,12 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { void Run(v8::Isolate* isolate, v8::Local global) override { v8::Local inspector = v8::ObjectTemplate::New(isolate); - inspector->Set( - ToV8String(isolate, "attachInspector"), - v8::FunctionTemplate::New(isolate, &InspectorExtension::Attach)); - inspector->Set( - ToV8String(isolate, "detachInspector"), - v8::FunctionTemplate::New(isolate, &InspectorExtension::Detach)); + inspector->Set(ToV8String(isolate, "fireContextCreated"), + v8::FunctionTemplate::New( + isolate, &InspectorExtension::FireContextCreated)); + inspector->Set(ToV8String(isolate, "fireContextDestroyed"), + v8::FunctionTemplate::New( + isolate, &InspectorExtension::FireContextDestroyed)); inspector->Set(ToV8String(isolate, "setMaxAsyncTaskStacks"), v8::FunctionTemplate::New( isolate, &InspectorExtension::SetMaxAsyncTaskStacks)); @@ -398,29 +643,19 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { } private: - static void Attach(const v8::FunctionCallbackInfo& args) { - v8::Isolate* isolate = args.GetIsolate(); - v8::Local context = isolate->GetCurrentContext(); - v8_inspector::V8Inspector* inspector = - InspectorClientImpl::InspectorFromContext(context); - if (!inspector) { - fprintf(stderr, "Inspector client not found - cannot attach!"); - Exit(); - } - inspector->contextCreated( - v8_inspector::V8ContextInfo(context, 1, v8_inspector::StringView())); + static void FireContextCreated( + const v8::FunctionCallbackInfo& args) { + v8::Local context = args.GetIsolate()->GetCurrentContext(); + IsolateData* data = IsolateData::FromContext(context); + data->inspector()->ContextCreated(context, + data->GetContextGroupId(context)); } - static void Detach(const v8::FunctionCallbackInfo& args) { - v8::Isolate* isolate = args.GetIsolate(); - v8::Local context = isolate->GetCurrentContext(); - v8_inspector::V8Inspector* inspector = - InspectorClientImpl::InspectorFromContext(context); - if (!inspector) { - fprintf(stderr, "Inspector client not found - cannot detach!"); - Exit(); - } - inspector->contextDestroyed(context); + static void FireContextDestroyed( + const v8::FunctionCallbackInfo& args) { + v8::Local context = args.GetIsolate()->GetCurrentContext(); + IsolateData* data = IsolateData::FromContext(context); + data->inspector()->ContextDestroyed(context); } static void SetMaxAsyncTaskStacks( @@ -429,12 +664,11 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { fprintf(stderr, "Internal error: setMaxAsyncTaskStacks(max)."); Exit(); } - v8_inspector::V8Inspector* inspector = - InspectorClientImpl::InspectorFromContext( - args.GetIsolate()->GetCurrentContext()); - CHECK(inspector); v8_inspector::SetMaxAsyncTaskStacksForTest( - inspector, args[0].As()->Value()); + IsolateData::FromContext(args.GetIsolate()->GetCurrentContext()) + ->inspector() + ->inspector(), + args[0].As()->Value()); } static void DumpAsyncTaskStacksStateForTest( @@ -443,11 +677,10 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { fprintf(stderr, "Internal error: dumpAsyncTaskStacksStateForTest()."); Exit(); } - v8_inspector::V8Inspector* inspector = - InspectorClientImpl::InspectorFromContext( - args.GetIsolate()->GetCurrentContext()); - CHECK(inspector); - v8_inspector::DumpAsyncTaskStacksStateForTest(inspector); + v8_inspector::DumpAsyncTaskStacksStateForTest( + IsolateData::FromContext(args.GetIsolate()->GetCurrentContext()) + ->inspector() + ->inspector()); } static void BreakProgram(const v8::FunctionCallbackInfo& args) { @@ -455,16 +688,14 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { fprintf(stderr, "Internal error: breakProgram('reason', 'details')."); Exit(); } - v8_inspector::V8InspectorSession* session = - InspectorClientImpl::SessionFromContext( - args.GetIsolate()->GetCurrentContext()); - CHECK(session); - + v8::Local context = args.GetIsolate()->GetCurrentContext(); + IsolateData* data = IsolateData::FromContext(context); v8::internal::Vector reason = ToVector(args[0].As()); v8_inspector::StringView reason_view(reason.start(), reason.length()); v8::internal::Vector details = ToVector(args[1].As()); v8_inspector::StringView details_view(details.start(), details.length()); - session->breakProgram(reason_view, details_view); + data->inspector()->BreakProgram(data->GetContextGroupId(context), + reason_view, details_view); } static void CreateObjectWithStrictCheck( @@ -485,24 +716,23 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { const v8::FunctionCallbackInfo& args) { if (args.Length() != 3 || !args[0]->IsFunction() || !args[1]->IsString() || !args[2]->IsString()) { - fprintf(stderr, "Internal error: breakProgram('reason', 'details')."); + fprintf(stderr, + "Internal error: callWithScheduledBreak('reason', 'details')."); Exit(); } - v8_inspector::V8InspectorSession* session = - InspectorClientImpl::SessionFromContext( - args.GetIsolate()->GetCurrentContext()); - CHECK(session); - v8::internal::Vector reason = ToVector(args[1].As()); v8_inspector::StringView reason_view(reason.start(), reason.length()); v8::internal::Vector details = ToVector(args[2].As()); v8_inspector::StringView details_view(details.start(), details.length()); - session->schedulePauseOnNextStatement(reason_view, details_view); v8::Local context = args.GetIsolate()->GetCurrentContext(); + IsolateData* data = IsolateData::FromContext(context); + int context_group_id = data->GetContextGroupId(context); + data->inspector()->SchedulePauseOnNextStatement(context_group_id, + reason_view, details_view); v8::MaybeLocal result; result = args[0].As()->Call(context, context->Global(), 0, nullptr); - session->cancelPauseOnNextStatement(); + data->inspector()->CancelPauseOnNextStatement(context_group_id); } static void AllowAccessorFormatting( @@ -524,68 +754,22 @@ class InspectorExtension : public IsolateData::SetupGlobalTask { } }; -void UtilsExtension::CreateContextGroup( - const v8::FunctionCallbackInfo& args) { - if (args.Length() != 0) { - fprintf(stderr, "Internal error: createContextGroup()."); - Exit(); - } - v8::base::Semaphore ready_semaphore(0); - int context_group_id = 0; - IsolateData::SetupGlobalTasks setup_global; - setup_global.emplace_back(new SetTimeoutExtension()); - setup_global.emplace_back(new InspectorExtension()); - inspector_client_->scheduleCreateContextGroup( - std::move(setup_global), &ready_semaphore, &context_group_id); - ready_semaphore.Wait(); - args.GetReturnValue().Set( - v8::Int32::New(args.GetIsolate(), context_group_id)); -} - -v8::Local ToString(v8::Isolate* isolate, - const v8_inspector::StringView& string) { - if (string.is8Bit()) - return v8::String::NewFromOneByte(isolate, string.characters8(), - v8::NewStringType::kNormal, - static_cast(string.length())) - .ToLocalChecked(); - else - return v8::String::NewFromTwoByte(isolate, string.characters16(), - v8::NewStringType::kNormal, - static_cast(string.length())) - .ToLocalChecked(); -} - class FrontendChannelImpl : public InspectorClientImpl::FrontendChannel { public: - explicit FrontendChannelImpl(TaskRunner* frontend_task_runner) - : frontend_task_runner_(frontend_task_runner) {} + FrontendChannelImpl(TaskRunner* frontend_task_runner, int context_group_id) + : frontend_task_runner_(frontend_task_runner), + context_group_id_(context_group_id) {} virtual ~FrontendChannelImpl() {} - void SendMessageToFrontend(const v8_inspector::StringView& message) final { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope scope(v8::Isolate::GetCurrent()); - - v8::Local prefix = - v8::String::NewFromUtf8(isolate, "InspectorTest._dispatchMessage(", - v8::NewStringType::kInternalized) - .ToLocalChecked(); - v8::Local message_string = ToString(isolate, message); - v8::Local suffix = - v8::String::NewFromUtf8(isolate, ")", v8::NewStringType::kInternalized) - .ToLocalChecked(); - - v8::Local result = v8::String::Concat(prefix, message_string); - result = v8::String::Concat(result, suffix); - - frontend_task_runner_->Append(new ExecuteStringTask( - ToVector(result), v8::String::Empty(isolate), - v8::Integer::New(isolate, 0), v8::Integer::New(isolate, 0), - v8::Boolean::New(isolate, false), nullptr, nullptr)); + void SendMessageToFrontend(int session_id, + const v8_inspector::StringView& message) final { + frontend_task_runner_->Append(new SendMessageToFrontendTask( + context_group_id_, session_id, ToVector(message))); } private: TaskRunner* frontend_task_runner_; + int context_group_id_; }; } // namespace @@ -610,29 +794,28 @@ int main(int argc, char* argv[]) { } } + IsolateData::SetupGlobalTasks frontend_extensions; + frontend_extensions.emplace_back(new UtilsExtension()); + TaskRunner frontend_runner(std::move(frontend_extensions), true, + &ready_semaphore, nullptr, nullptr); + ready_semaphore.Wait(); + + int frontend_context_group_id = 0; + frontend_runner.Append( + new CreateContextGroupTask(&ready_semaphore, &frontend_context_group_id)); + ready_semaphore.Wait(); + IsolateData::SetupGlobalTasks backend_extensions; backend_extensions.emplace_back(new SetTimeoutExtension()); backend_extensions.emplace_back(new InspectorExtension()); - TaskRunner backend_runner(std::move(backend_extensions), false, - &ready_semaphore, - startup_data.data ? &startup_data : nullptr); + FrontendChannelImpl frontend_channel(&frontend_runner, + frontend_context_group_id); + TaskRunner backend_runner( + std::move(backend_extensions), false, &ready_semaphore, + startup_data.data ? &startup_data : nullptr, &frontend_channel); ready_semaphore.Wait(); - SendMessageToBackendExtension::set_backend_task_runner(&backend_runner); UtilsExtension::set_backend_task_runner(&backend_runner); - IsolateData::SetupGlobalTasks frontend_extensions; - frontend_extensions.emplace_back(new UtilsExtension()); - frontend_extensions.emplace_back(new SendMessageToBackendExtension()); - TaskRunner frontend_runner(std::move(frontend_extensions), true, - &ready_semaphore, nullptr); - ready_semaphore.Wait(); - - FrontendChannelImpl frontend_channel(&frontend_runner); - InspectorClientImpl inspector_client(&backend_runner, &frontend_channel, - &ready_semaphore); - ready_semaphore.Wait(); - UtilsExtension::set_inspector_client(&inspector_client); - task_runners.push_back(&frontend_runner); task_runners.push_back(&backend_runner); @@ -648,7 +831,8 @@ int main(int argc, char* argv[]) { argv[i]); Exit(); } - frontend_runner.Append(new ExecuteStringTask(chars)); + frontend_runner.Append( + new ExecuteStringTask(chars, frontend_context_group_id)); } frontend_runner.Join(); diff --git a/test/inspector/isolate-data.cc b/test/inspector/isolate-data.cc index 06ba710b3e..927bd31ef4 100644 --- a/test/inspector/isolate-data.cc +++ b/test/inspector/isolate-data.cc @@ -10,6 +10,7 @@ namespace { const int kIsolateDataIndex = 2; +const int kContextGroupIdIndex = 3; v8::internal::Vector ToVector(v8::Local str) { v8::internal::Vector buffer = @@ -22,7 +23,8 @@ v8::internal::Vector ToVector(v8::Local str) { IsolateData::IsolateData(TaskRunner* task_runner, IsolateData::SetupGlobalTasks setup_global_tasks, - v8::StartupData* startup_data) + v8::StartupData* startup_data, + InspectorClientImpl::FrontendChannel* channel) : task_runner_(task_runner), setup_global_tasks_(std::move(setup_global_tasks)) { v8::Isolate::CreateParams params; @@ -31,6 +33,8 @@ IsolateData::IsolateData(TaskRunner* task_runner, params.snapshot_blob = startup_data; isolate_ = v8::Isolate::New(params); isolate_->SetMicrotasksPolicy(v8::MicrotasksPolicy::kScoped); + if (channel) + inspector_.reset(new InspectorClientImpl(isolate_, task_runner, channel)); } IsolateData* IsolateData::FromContext(v8::Local context) { @@ -39,6 +43,7 @@ IsolateData* IsolateData::FromContext(v8::Local context) { } int IsolateData::CreateContextGroup() { + v8::HandleScope handle_scope(isolate_); v8::Local global_template = v8::ObjectTemplate::New(isolate_); for (auto it = setup_global_tasks_.begin(); it != setup_global_tasks_.end(); @@ -49,7 +54,11 @@ int IsolateData::CreateContextGroup() { v8::Context::New(isolate_, nullptr, global_template); context->SetAlignedPointerInEmbedderData(kIsolateDataIndex, this); int context_group_id = ++last_context_group_id_; + // Should be 2-byte aligned. + context->SetAlignedPointerInEmbedderData( + kContextGroupIdIndex, reinterpret_cast(context_group_id * 2)); contexts_[context_group_id].Reset(isolate_, context); + if (inspector_) inspector_->ContextCreated(context, context_group_id); return context_group_id; } @@ -57,6 +66,13 @@ v8::Local IsolateData::GetContext(int context_group_id) { return contexts_[context_group_id].Get(isolate_); } +int IsolateData::GetContextGroupId(v8::Local context) { + return static_cast( + reinterpret_cast( + context->GetAlignedPointerFromEmbedderData(kContextGroupIdIndex)) / + 2); +} + void IsolateData::RegisterModule(v8::Local context, v8::internal::Vector name, v8::ScriptCompiler::Source* source) { diff --git a/test/inspector/isolate-data.h b/test/inspector/isolate-data.h index 8a5555d051..34f0ae8308 100644 --- a/test/inspector/isolate-data.h +++ b/test/inspector/isolate-data.h @@ -11,6 +11,7 @@ #include "include/v8-platform.h" #include "include/v8.h" #include "src/vector.h" +#include "test/inspector/inspector-impl.h" class TaskRunner; @@ -25,13 +26,16 @@ class IsolateData { using SetupGlobalTasks = std::vector>; IsolateData(TaskRunner* task_runner, SetupGlobalTasks setup_global_tasks, - v8::StartupData* startup_data); + v8::StartupData* startup_data, + InspectorClientImpl::FrontendChannel* channel); static IsolateData* FromContext(v8::Local context); v8::Isolate* isolate() const { return isolate_; } + InspectorClientImpl* inspector() const { return inspector_.get(); } TaskRunner* task_runner() const { return task_runner_; } int CreateContextGroup(); v8::Local GetContext(int context_group_id); + int GetContextGroupId(v8::Local context); void RegisterModule(v8::Local context, v8::internal::Vector name, v8::ScriptCompiler::Source* source); @@ -53,6 +57,7 @@ class IsolateData { TaskRunner* task_runner_; SetupGlobalTasks setup_global_tasks_; v8::Isolate* isolate_; + std::unique_ptr inspector_; int last_context_group_id_ = 0; std::map> contexts_; std::map, v8::Global, diff --git a/test/inspector/json-parse.js b/test/inspector/json-parse.js index 2d88fea0f0..00ad69b841 100644 --- a/test/inspector/json-parse.js +++ b/test/inspector/json-parse.js @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -const id = ++InspectorTest._requestId; +const id = ++InspectorTest.session._requestId; var command = { "method": "Runtime.evaluate", "params": { expression: "\"!!!\"" }, "id": id }; -InspectorTest.sendRawCommand(id, JSON.stringify(command).replace("!!!", "\\u041F\\u0440\\u0438\\u0432\\u0435\\u0442 \\u043C\\u0438\\u0440"), step2); +InspectorTest.session.sendRawCommand(id, JSON.stringify(command).replace("!!!", "\\u041F\\u0440\\u0438\\u0432\\u0435\\u0442 \\u043C\\u0438\\u0440"), step2); function step2(msg) { diff --git a/test/inspector/protocol-test.js b/test/inspector/protocol-test.js index 3884a16ed9..9b85e24672 100644 --- a/test/inspector/protocol-test.js +++ b/test/inspector/protocol-test.js @@ -3,33 +3,102 @@ // found in the LICENSE file. InspectorTest = {}; -InspectorTest._dispatchTable = new Map(); -InspectorTest._requestId = 0; InspectorTest._dumpInspectorProtocolMessages = false; -InspectorTest._eventHandler = {}; InspectorTest._commandsForLogging = new Set(); -Protocol = new Proxy({}, { - get: function(target, agentName, receiver) { - return new Proxy({}, { - get: function(target, methodName, receiver) { - const eventPattern = /^on(ce)?([A-Z][A-Za-z0-9]+)/; - var match = eventPattern.exec(methodName); - if (!match) { - return (args, contextGroupId) => InspectorTest._sendCommandPromise(`${agentName}.${methodName}`, args || {}, contextGroupId); - } else { - var eventName = match[2]; - eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1); - if (match[1]) - return () => InspectorTest._waitForEventPromise( - `${agentName}.${eventName}`); - else - return (listener) => { InspectorTest._eventHandler[`${agentName}.${eventName}`] = listener }; +InspectorTest.createContextGroup = function() { + var contextGroup = {}; + contextGroup.id = utils.createContextGroup(); + contextGroup.schedulePauseOnNextStatement = (reason, details) => utils.schedulePauseOnNextStatement(contextGroup.id, reason, details); + contextGroup.cancelPauseOnNextStatement = () => utils.cancelPauseOnNextStatement(contextGroup.id); + contextGroup.addScript = (string, lineOffset, columnOffset, url) => utils.compileAndRunWithOrigin(contextGroup.id, string, url || '', lineOffset || 0, columnOffset || 0, false); + contextGroup.addModule = (string, url, lineOffset, columnOffset) => utils.compileAndRunWithOrigin(contextGroup.id, string, url, lineOffset || 0, columnOffset || 0, true); + return contextGroup; +} + +InspectorTest._sessions = new Map(); +InspectorTest.createSession = function(contextGroup) { + var session = { + contextGroup: contextGroup, + _dispatchTable: new Map(), + _eventHandler: {}, + _requestId: 0, + }; + session.Protocol = new Proxy({}, { + get: function(target, agentName, receiver) { + return new Proxy({}, { + get: function(target, methodName, receiver) { + const eventPattern = /^on(ce)?([A-Z][A-Za-z0-9]+)/; + var match = eventPattern.exec(methodName); + if (!match) { + return args => session._sendCommandPromise(`${agentName}.${methodName}`, args || {}); + } else { + var eventName = match[2]; + eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1); + if (match[1]) + return () => InspectorTest._waitForEventPromise(session, `${agentName}.${eventName}`); + else + return (listener) => { session._eventHandler[`${agentName}.${eventName}`] = listener }; + } } + }); + } + }); + session._dispatchMessage = messageString => { + let messageObject = JSON.parse(messageString); + if (InspectorTest._dumpInspectorProtocolMessages) + utils.print("backend: " + JSON.stringify(messageObject)); + try { + var messageId = messageObject["id"]; + if (typeof messageId === "number") { + var handler = session._dispatchTable.get(messageId); + if (handler) { + handler(messageObject); + session._dispatchTable.delete(messageId); + } + } else { + var eventName = messageObject["method"]; + var eventHandler = session._eventHandler[eventName]; + if (session._scriptMap && eventName === "Debugger.scriptParsed") + session._scriptMap.set(messageObject.params.scriptId, JSON.parse(JSON.stringify(messageObject.params))); + if (eventName === "Debugger.scriptParsed" && messageObject.params.url === "wait-pending-tasks.js") + return; + if (eventHandler) + eventHandler(messageObject); } - }); + } catch (e) { + InspectorTest.log("Exception when dispatching message: " + e + "\n" + e.stack + "\n message = " + JSON.stringify(messageObject, null, 2)); + InspectorTest.completeTest(); + } + }; + session.id = utils.connectSession(contextGroup.id, '', session._dispatchMessage.bind(session)); + InspectorTest._sessions.set(session.id, session); + session.disconnect = () => utils.disconnectSession(session.id); + session.reconnect = () => { + InspectorTest._sessions.delete(session.id); + var state = utils.disconnectSession(session.id); + session.id = utils.connectSession(contextGroup.id, state, session._dispatchMessage.bind(session)); + InspectorTest._sessions.set(session.id, session); + }; + session.sendRawCommand = (requestId, command, handler) => { + if (InspectorTest._dumpInspectorProtocolMessages) + utils.print("frontend: " + command); + session._dispatchTable.set(requestId, handler); + utils.sendMessageToBackend(session.id, command); } -}); + session._sendCommandPromise = (method, params) => { + var requestId = ++session._requestId; + var messageObject = { "id": requestId, "method": method, "params": params }; + var fulfillCallback; + var promise = new Promise(fulfill => fulfillCallback = fulfill); + if (InspectorTest._commandsForLogging.has(method)) { + utils.print(method + ' called'); + } + session.sendRawCommand(requestId, JSON.stringify(messageObject), fulfillCallback); + return promise; + } + return session; +} InspectorTest.logProtocolCommandCalls = (command) => InspectorTest._commandsForLogging.add(command); @@ -108,28 +177,29 @@ InspectorTest.logObject = function(object, title) InspectorTest.log(lines.join("\n")); } -InspectorTest.logCallFrames = function(callFrames) +InspectorTest.logCallFrames = function(callFrames, session) { + session = session || InspectorTest.session; for (var frame of callFrames) { var functionName = frame.functionName || '(anonymous)'; - var url = frame.url ? frame.url : InspectorTest._scriptMap.get(frame.location.scriptId).url; + var url = frame.url ? frame.url : session._scriptMap.get(frame.location.scriptId).url; var lineNumber = frame.location ? frame.location.lineNumber : frame.lineNumber; var columnNumber = frame.location ? frame.location.columnNumber : frame.columnNumber; InspectorTest.log(`${functionName} (${url}:${lineNumber}:${columnNumber})`); } } -InspectorTest.logSourceLocation = function(location) +InspectorTest.logSourceLocation = function(location, session) { + session = session || InspectorTest.session; var scriptId = location.scriptId; - if (!InspectorTest._scriptMap || !InspectorTest._scriptMap.has(scriptId)) { + if (!session._scriptMap || !session._scriptMap.has(scriptId)) { InspectorTest.log("InspectorTest.setupScriptMap should be called before Protocol.Debugger.enable."); InspectorTest.completeTest(); } - var script = InspectorTest._scriptMap.get(scriptId); + var script = session._scriptMap.get(scriptId); if (!script.scriptSource) { - // TODO(kozyatinskiy): doesn't assume that contextId == contextGroupId. - return Protocol.Debugger.getScriptSource({ scriptId }, script.executionContextId) + return session.Protocol.Debugger.getScriptSource({ scriptId }) .then(message => script.scriptSource = message.result.scriptSource) .then(dumpSourceWithLocation); } @@ -146,14 +216,15 @@ InspectorTest.logSourceLocation = function(location) } } -InspectorTest.logSourceLocations = function(locations) { +InspectorTest.logSourceLocations = function(locations, session) { if (locations.length == 0) return Promise.resolve(); - return InspectorTest.logSourceLocation(locations[0]) - .then(() => InspectorTest.logSourceLocations(locations.splice(1))); + return InspectorTest.logSourceLocation(locations[0], session) + .then(() => InspectorTest.logSourceLocations(locations.splice(1), session)); } -InspectorTest.logAsyncStackTrace = function(asyncStackTrace) +InspectorTest.logAsyncStackTrace = function(asyncStackTrace, session) { + session = InspectorTest.session || session; while (asyncStackTrace) { if (asyncStackTrace.promiseCreationFrame) { var frame = asyncStackTrace.promiseCreationFrame; @@ -162,7 +233,7 @@ InspectorTest.logAsyncStackTrace = function(asyncStackTrace) } else { InspectorTest.log(`-- ${asyncStackTrace.description} --`); } - InspectorTest.logCallFrames(asyncStackTrace.callFrames); + InspectorTest.logCallFrames(asyncStackTrace.callFrames, session); asyncStackTrace = asyncStackTrace.parent; } } @@ -176,26 +247,17 @@ InspectorTest.completeTestAfterPendingTimeouts = function() InspectorTest.waitPendingTasks = function() { - return Protocol.Runtime.evaluate({ expression: "new Promise(r => setTimeout(r, 0))//# sourceURL=wait-pending-tasks.js", awaitPromise: true }); + var promises = []; + for (var session of InspectorTest._sessions.values()) + promises.push(session.Protocol.Runtime.evaluate({ expression: "new Promise(r => setTimeout(r, 0))//# sourceURL=wait-pending-tasks.js", awaitPromise: true })); + return Promise.all(promises); } -InspectorTest.addScript = (string, lineOffset, columnOffset) => utils.compileAndRunWithOrigin(string, "", lineOffset || 0, columnOffset || 0, false); -InspectorTest.addScriptWithUrl = (string, url) => utils.compileAndRunWithOrigin(string, url, 0, 0, false); -InspectorTest.addModule = (string, url, lineOffset, columnOffset) => utils.compileAndRunWithOrigin(string, url, lineOffset || 0, columnOffset || 0, true); - InspectorTest.startDumpingProtocolMessages = function() { InspectorTest._dumpInspectorProtocolMessages = true; } -InspectorTest.sendRawCommand = function(requestId, command, handler, contextGroupId) -{ - if (InspectorTest._dumpInspectorProtocolMessages) - utils.print("frontend: " + command); - InspectorTest._dispatchTable.set(requestId, handler); - sendMessageToBackend(command, contextGroupId || 0); -} - InspectorTest.checkExpectation = function(fail, name, messageObject) { if (fail === !!messageObject.error) { @@ -210,10 +272,11 @@ InspectorTest.checkExpectation = function(fail, name, messageObject) InspectorTest.expectedSuccess = InspectorTest.checkExpectation.bind(null, false); InspectorTest.expectedError = InspectorTest.checkExpectation.bind(null, true); -InspectorTest.setupScriptMap = function() { - if (InspectorTest._scriptMap) +InspectorTest.setupScriptMap = function(session) { + session = session || InspectorTest.session; + if (session._scriptMap) return; - InspectorTest._scriptMap = new Map(); + session._scriptMap = new Map(); } InspectorTest.runTestSuite = function(testSuite) @@ -234,68 +297,28 @@ InspectorTest.runTestSuite = function(testSuite) InspectorTest.runAsyncTestSuite = async function(testSuite) { for (var test of testSuite) { InspectorTest.log("\nRunning test: " + test.name); - await test(); + try { + await test(); + } catch (e) { + utils.print(e.stack); + } } InspectorTest.completeTest(); } -InspectorTest._sendCommandPromise = function(method, params, contextGroupId) +InspectorTest._waitForEventPromise = function(session, eventName) { - var requestId = ++InspectorTest._requestId; - var messageObject = { "id": requestId, "method": method, "params": params }; - var fulfillCallback; - var promise = new Promise(fulfill => fulfillCallback = fulfill); - if (InspectorTest._commandsForLogging.has(method)) { - utils.print(method + ' called'); - } - InspectorTest.sendRawCommand(requestId, JSON.stringify(messageObject), fulfillCallback, contextGroupId); - return promise; -} - -InspectorTest._waitForEventPromise = function(eventName) -{ - return new Promise(fulfill => InspectorTest._eventHandler[eventName] = fullfillAndClearListener.bind(null, fulfill)); + return new Promise(fulfill => session._eventHandler[eventName] = fullfillAndClearListener.bind(null, fulfill)); function fullfillAndClearListener(fulfill, result) { - delete InspectorTest._eventHandler[eventName]; + delete session._eventHandler[eventName]; fulfill(result); } } -InspectorTest._dispatchMessage = function(messageObject) -{ - if (InspectorTest._dumpInspectorProtocolMessages) - utils.print("backend: " + JSON.stringify(messageObject)); - try { - var messageId = messageObject["id"]; - if (typeof messageId === "number") { - var handler = InspectorTest._dispatchTable.get(messageId); - if (handler) { - handler(messageObject); - InspectorTest._dispatchTable.delete(messageId); - } - } else { - var eventName = messageObject["method"]; - var eventHandler = InspectorTest._eventHandler[eventName]; - if (InspectorTest._scriptMap && eventName === "Debugger.scriptParsed") - InspectorTest._scriptMap.set(messageObject.params.scriptId, JSON.parse(JSON.stringify(messageObject.params))); - if (eventName === "Debugger.scriptParsed" && messageObject.params.url === "wait-pending-tasks.js") - return; - if (eventHandler) - eventHandler(messageObject); - } - } catch (e) { - InspectorTest.log("Exception when dispatching message: " + e + "\n" + e.stack + "\n message = " + JSON.stringify(messageObject, null, 2)); - InspectorTest.completeTest(); - } -} - -InspectorTest.loadScript = function(fileName) { - InspectorTest.addScript(utils.read(fileName)); -} - -InspectorTest.setupInjectedScriptEnvironment = function(debug) { +InspectorTest.setupInjectedScriptEnvironment = function(debug, session) { + session = session || InspectorTest.session; let scriptSource = ''; // First define all getters on Object.prototype. let injectedScriptSource = utils.read('src/inspector/injected-script-source.js'); @@ -314,17 +337,28 @@ InspectorTest.setupInjectedScriptEnvironment = function(debug) { __proto__: null }); `).join('\n') + '})();'; - InspectorTest.addScript(scriptSource); + session.contextGroup.addScript(scriptSource); if (debug) { InspectorTest.log('WARNING: InspectorTest.setupInjectedScriptEnvironment with debug flag for debugging only and should not be landed.'); InspectorTest.log('WARNING: run test with --expose-inspector-scripts flag to get more details.'); InspectorTest.log('WARNING: you can additionally comment rjsmin in xxd.py to get unminified injected-script-source.js.'); - InspectorTest.setupScriptMap(); - Protocol.Debugger.enable(); - Protocol.Debugger.onPaused(message => { + InspectorTest.setupScriptMap(session); + sesison.Protocol.Debugger.enable(); + session.Protocol.Debugger.onPaused(message => { let callFrames = message.params.callFrames; - InspectorTest.logSourceLocations(callFrames.map(frame => frame.location)); + InspectorTest.logSourceLocations(callFrames.map(frame => frame.location), session); }) } } + +try { + InspectorTest.contextGroup = InspectorTest.createContextGroup(); + InspectorTest.session = InspectorTest.createSession(InspectorTest.contextGroup); + this.Protocol = InspectorTest.session.Protocol; + InspectorTest.addScript = InspectorTest.contextGroup.addScript.bind(InspectorTest.contextGroup); + InspectorTest.addModule = InspectorTest.contextGroup.addModule.bind(InspectorTest.contextGroup); + InspectorTest.loadScript = fileName => InspectorTest.addScript(utils.read(fileName)); +} catch (e) { + utils.print(e.stack); +} diff --git a/test/inspector/runtime/create-context.js b/test/inspector/runtime/create-context.js index 4f86e6ec34..8a7bd999da 100644 --- a/test/inspector/runtime/create-context.js +++ b/test/inspector/runtime/create-context.js @@ -4,24 +4,20 @@ InspectorTest.log('Checks createContext().'); -InspectorTest.setupScriptMap(); -Protocol.Runtime.onExecutionContextCreated(InspectorTest.logMessage); -Protocol.Debugger.onPaused((message) => { - InspectorTest.logSourceLocation(message.params.callFrames[0].location); - Protocol.Debugger.stepOut(); -}); var executionContextIds = new Set(); -Protocol.Debugger.onScriptParsed(message => executionContextIds.add(message.params.executionContextId)); -var contextGroupId; +var contextGroup = InspectorTest.createContextGroup(); +var session = InspectorTest.createSession(contextGroup); +setup(InspectorTest.session); +setup(session); + Protocol.Runtime.enable() - .then(() => contextGroupId = utils.createContextGroup()) - .then(() => Protocol.Runtime.enable({}, contextGroupId)) + .then(() => session.Protocol.Runtime.enable({})) .then(() => Protocol.Debugger.enable()) - .then(() => Protocol.Debugger.enable({}, contextGroupId)) + .then(() => session.Protocol.Debugger.enable({})) .then(InspectorTest.logMessage) .then(() => { - Protocol.Runtime.evaluate({ expression: 'debugger;' }) - Protocol.Runtime.evaluate({ expression: 'setTimeout(x => x * 2, 0)' }, contextGroupId); + Protocol.Runtime.evaluate({ expression: 'debugger;' }); + session.Protocol.Runtime.evaluate({expression: 'setTimeout(x => x * 2, 0)'}); Protocol.Runtime.evaluate({ expression: 'setTimeout(x => x * 3, 0)' }); }) .then(() => InspectorTest.waitPendingTasks()) @@ -29,14 +25,25 @@ Protocol.Runtime.enable() InspectorTest.log(`Reported script's execution id: ${executionContextIds.size}`); executionContextIds.clear(); }) - .then(() => utils.reconnect()) + .then(() => InspectorTest.session.reconnect()) + .then(() => session.reconnect()) .then(() => { Protocol.Runtime.evaluate({ expression: 'debugger;' }) - Protocol.Runtime.evaluate({ expression: 'setTimeout(x => x * 2, 0)' }, contextGroupId); + session.Protocol.Runtime.evaluate({ expression: 'setTimeout(x => x * 2, 0)' }); Protocol.Runtime.evaluate({ expression: 'setTimeout(x => x * 3, 0)' }); }) .then(() => InspectorTest.waitPendingTasks()) - .then(() => Protocol.Debugger.disable({}, contextGroupId)) + .then(() => session.Protocol.Debugger.disable({})) .then(() => Protocol.Debugger.disable({})) .then(() => InspectorTest.log(`Reported script's execution id: ${executionContextIds.size}`)) .then(InspectorTest.completeTest); + +function setup(session) { + session.Protocol.Runtime.onExecutionContextCreated(InspectorTest.logMessage); + InspectorTest.setupScriptMap(session); + session.Protocol.Debugger.onPaused((message) => { + InspectorTest.logSourceLocation(message.params.callFrames[0].location, session); + session.Protocol.Debugger.stepOut(); + }); + session.Protocol.Debugger.onScriptParsed(message => executionContextIds.add(message.params.executionContextId)); +} diff --git a/test/inspector/runtime/runtime-restore.js b/test/inspector/runtime/runtime-restore.js index 5c2fea5768..4020c19f81 100644 --- a/test/inspector/runtime/runtime-restore.js +++ b/test/inspector/runtime/runtime-restore.js @@ -56,9 +56,9 @@ InspectorTest.runTestSuite([ function testSetCustomObjectFormatterEnabled(next) { Protocol.Runtime.onConsoleAPICalled(InspectorTest.logMessage); - // cleanup console message storage - reconnect(); - Protocol.Runtime.enable() + Protocol.Runtime.discardConsoleEntries() + .then(reconnect) + .then(() => Protocol.Runtime.enable()) .then(() => Protocol.Runtime.setCustomObjectFormatterEnabled({ enabled: true })) .then(reconnect) .then(() => Protocol.Runtime.evaluate({ expression: 'console.log({ name: 42 })'})) @@ -73,5 +73,5 @@ InspectorTest.runTestSuite([ function reconnect() { InspectorTest.logMessage('will reconnect..'); - utils.reconnect(); + InspectorTest.session.reconnect(); } diff --git a/test/inspector/task-runner.cc b/test/inspector/task-runner.cc index b26680fdf6..79ccc8a8ec 100644 --- a/test/inspector/task-runner.cc +++ b/test/inspector/task-runner.cc @@ -17,7 +17,15 @@ void ReportUncaughtException(v8::Isolate* isolate, CHECK(try_catch.HasCaught()); v8::HandleScope handle_scope(isolate); std::string message = *v8::String::Utf8Value(try_catch.Message()->Get()); - fprintf(stderr, "Unhandle exception: %s\n", message.data()); + int line = try_catch.Message() + ->GetLineNumber(isolate->GetCurrentContext()) + .FromJust(); + std::string source_line = + *v8::String::Utf8Value(try_catch.Message() + ->GetSourceLine(isolate->GetCurrentContext()) + .ToLocalChecked()); + fprintf(stderr, "Unhandle exception: %s @%s[%d]\n", message.data(), + source_line.data(), line); } v8::internal::Vector ToVector(v8::Local str) { @@ -32,10 +40,12 @@ v8::internal::Vector ToVector(v8::Local str) { TaskRunner::TaskRunner(IsolateData::SetupGlobalTasks setup_global_tasks, bool catch_exceptions, v8::base::Semaphore* ready_semaphore, - v8::StartupData* startup_data) + v8::StartupData* startup_data, + InspectorClientImpl::FrontendChannel* channel) : Thread(Options("Task Runner")), setup_global_tasks_(std::move(setup_global_tasks)), startup_data_(startup_data), + channel_(channel), catch_exceptions_(catch_exceptions), ready_semaphore_(ready_semaphore), data_(nullptr), @@ -47,13 +57,8 @@ TaskRunner::TaskRunner(IsolateData::SetupGlobalTasks setup_global_tasks, TaskRunner::~TaskRunner() { Join(); } void TaskRunner::Run() { - data_.reset( - new IsolateData(this, std::move(setup_global_tasks_), startup_data_)); - - v8::Isolate::Scope isolate_scope(isolate()); - v8::HandleScope handle_scope(isolate()); - default_context_group_id_ = data_->CreateContextGroup(); - + data_.reset(new IsolateData(this, std::move(setup_global_tasks_), + startup_data_, channel_)); if (ready_semaphore_) ready_semaphore_->Signal(); RunMessageLoop(false); } @@ -66,7 +71,7 @@ void TaskRunner::RunMessageLoop(bool only_protocol) { v8::Isolate::Scope isolate_scope(isolate()); if (catch_exceptions_) { v8::TryCatch try_catch(isolate()); - task->RunOnTaskRunner(this); + task->RunOnIsolate(data_.get()); delete task; if (try_catch.HasCaught()) { ReportUncaughtException(isolate(), try_catch); @@ -75,7 +80,7 @@ void TaskRunner::RunMessageLoop(bool only_protocol) { _exit(0); } } else { - task->RunOnTaskRunner(this); + task->RunOnIsolate(data_.get()); delete task; } } @@ -115,44 +120,45 @@ TaskRunner::Task* TaskRunner::GetNext(bool only_protocol) { return nullptr; } -AsyncTask::AsyncTask(const char* task_name, - v8_inspector::V8Inspector* inspector) - : inspector_(task_name ? inspector : nullptr) { - if (inspector_) { - inspector_->asyncTaskScheduled( - v8_inspector::StringView(reinterpret_cast(task_name), - strlen(task_name)), - this, false); - } +AsyncTask::AsyncTask(IsolateData* data, const char* task_name) + : instrumenting_(data && task_name) { + if (!instrumenting_) return; + data->inspector()->inspector()->asyncTaskScheduled( + v8_inspector::StringView(reinterpret_cast(task_name), + strlen(task_name)), + this, false); } void AsyncTask::Run() { - if (inspector_) inspector_->asyncTaskStarted(this); + if (instrumenting_) data()->inspector()->inspector()->asyncTaskStarted(this); AsyncRun(); - if (inspector_) inspector_->asyncTaskFinished(this); + if (instrumenting_) data()->inspector()->inspector()->asyncTaskFinished(this); } ExecuteStringTask::ExecuteStringTask( + IsolateData* data, int context_group_id, const char* task_name, const v8::internal::Vector& expression, v8::Local name, v8::Local line_offset, - v8::Local column_offset, v8::Local is_module, - const char* task_name, v8_inspector::V8Inspector* inspector) - : AsyncTask(task_name, inspector), + v8::Local column_offset, v8::Local is_module) + : AsyncTask(data, task_name), expression_(expression), name_(ToVector(name)), line_offset_(line_offset.As()->Value()), column_offset_(column_offset.As()->Value()), - is_module_(is_module->Value()) {} + is_module_(is_module->Value()), + context_group_id_(context_group_id) {} ExecuteStringTask::ExecuteStringTask( - const v8::internal::Vector& expression) - : AsyncTask(nullptr, nullptr), expression_utf8_(expression) {} + const v8::internal::Vector& expression, int context_group_id) + : AsyncTask(nullptr, nullptr), + expression_utf8_(expression), + context_group_id_(context_group_id) {} void ExecuteStringTask::AsyncRun() { v8::MicrotasksScope microtasks_scope(isolate(), v8::MicrotasksScope::kRunMicrotasks); v8::HandleScope handle_scope(isolate()); - v8::Local context = default_context(); + v8::Local context = data()->GetContext(context_group_id_); v8::Context::Scope context_scope(context); v8::Local name = @@ -193,7 +199,6 @@ void ExecuteStringTask::AsyncRun() { v8::MaybeLocal result; result = script->Run(context); } else { - IsolateData::FromContext(context)->RegisterModule(context, name_, - &scriptSource); + data()->RegisterModule(context, name_, &scriptSource); } } diff --git a/test/inspector/task-runner.h b/test/inspector/task-runner.h index 2052c621d1..c99bc6c95a 100644 --- a/test/inspector/task-runner.h +++ b/test/inspector/task-runner.h @@ -23,30 +23,27 @@ class TaskRunner : public v8::base::Thread { public: virtual ~Task() {} virtual bool is_inspector_task() = 0; - void RunOnTaskRunner(TaskRunner* task_runner) { - task_runner_ = task_runner; + void RunOnIsolate(IsolateData* data) { + data_ = data; Run(); - task_runner_ = nullptr; + data_ = nullptr; } protected: virtual void Run() = 0; - v8::Isolate* isolate() const { return task_runner_->data_->isolate(); } - v8::Local default_context() const { - return task_runner_->data_->GetContext( - task_runner_->default_context_group_id_); - } + v8::Isolate* isolate() const { return data_->isolate(); } + IsolateData* data() const { return data_; } private: - TaskRunner* task_runner_ = nullptr; + IsolateData* data_ = nullptr; }; TaskRunner(IsolateData::SetupGlobalTasks setup_global_tasks, bool catch_exceptions, v8::base::Semaphore* ready_semaphore, - v8::StartupData* startup_data); + v8::StartupData* startup_data, + InspectorClientImpl::FrontendChannel* channel); virtual ~TaskRunner(); IsolateData* data() const { return data_.get(); } - int default_context_group_id() const { return default_context_group_id_; } // Thread implementation. void Run() override; @@ -66,10 +63,10 @@ class TaskRunner : public v8::base::Thread { IsolateData::SetupGlobalTasks setup_global_tasks_; v8::StartupData* startup_data_; + InspectorClientImpl::FrontendChannel* channel_; bool catch_exceptions_; v8::base::Semaphore* ready_semaphore_; std::unique_ptr data_; - int default_context_group_id_; // deferred_queue_ combined with queue_ (in this order) have all tasks in the // correct order. Sometimes we skip non-protocol tasks by moving them from @@ -87,26 +84,27 @@ class TaskRunner : public v8::base::Thread { class AsyncTask : public TaskRunner::Task { public: - AsyncTask(const char* task_name, v8_inspector::V8Inspector* inspector); + AsyncTask(IsolateData* data, const char* task_name); virtual ~AsyncTask() = default; protected: virtual void AsyncRun() = 0; void Run() override; - v8_inspector::V8Inspector* inspector_; + bool instrumenting_; }; class ExecuteStringTask : public AsyncTask { public: - ExecuteStringTask(const v8::internal::Vector& expression, + ExecuteStringTask(IsolateData* data, int context_group_id, + const char* task_name, + const v8::internal::Vector& expression, v8::Local name, v8::Local line_offset, v8::Local column_offset, - v8::Local is_module, const char* task_name, - v8_inspector::V8Inspector* inspector); - explicit ExecuteStringTask( - const v8::internal::Vector& expression); + v8::Local is_module); + ExecuteStringTask(const v8::internal::Vector& expression, + int context_group_id); bool is_inspector_task() override { return false; } private: @@ -118,6 +116,7 @@ class ExecuteStringTask : public AsyncTask { int32_t line_offset_ = 0; int32_t column_offset_ = 0; bool is_module_ = false; + int context_group_id_; DISALLOW_COPY_AND_ASSIGN(ExecuteStringTask); };