[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}
This commit is contained in:
dgozman 2017-05-18 16:11:20 -07:00 committed by Commit bot
parent c9756be93b
commit 55849b167c
23 changed files with 785 additions and 699 deletions

View File

@ -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");
`;

View File

@ -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();
})();

View File

@ -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'}))

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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<v8_inspector::StringBuffer> message) override {
frontend_channel_->SendMessageToFrontend(message->string());
frontend_channel_->SendMessageToFrontend(session_id_, message->string());
}
void sendNotification(
std::unique_ptr<v8_inspector::StringBuffer> 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<v8::Context> context) {
InspectorClientImpl* inspector_client = static_cast<InspectorClientImpl*>(
context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex));
CHECK(inspector_client);
return inspector_client;
}
v8::internal::Vector<uint16_t> ToVector(v8::Local<v8::String> str) {
v8::internal::Vector<uint16_t> buffer =
v8::internal::Vector<uint16_t>::New(str->Length());
@ -55,7 +49,7 @@ void MessageHandler(v8::Local<v8::Message> message,
v8::Local<v8::Context> context = isolate->GetEnteredContext();
if (context.IsEmpty()) return;
v8_inspector::V8Inspector* inspector =
InspectorClientImpl::InspectorFromContext(context);
IsolateData::FromContext(context)->inspector()->inspector();
v8::Local<v8::StackTrace> 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<v8::Context> 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<v8_inspector::StringBuffer>
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<v8_inspector::StringBuffer> 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<v8::Context> 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<v8::Context> 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<int> InspectorClientImpl::GetSessionIds(int context_group_id) {
std::vector<int> 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<v8::Context> 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<v8::Context> 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<v8::Value> 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<v8::Context> context) {
return InspectorClientFromContext(context)->inspector_.get();
}
v8_inspector::V8InspectorSession* InspectorClientImpl::SessionFromContext(
v8::Local<v8::Context> 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<uint16_t>& 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<uint16_t> message_;
int context_group_id_;
};
TaskRunner* SendMessageToBackendExtension::backend_task_runner_ = nullptr;
void SendMessageToBackendExtension::Run(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> global) {
global->Set(
v8::String::NewFromUtf8(isolate, "sendMessageToBackend",
v8::NewStringType::kNormal)
.ToLocalChecked(),
v8::FunctionTemplate::New(
isolate, &SendMessageToBackendExtension::SendMessageToBackend));
}
void SendMessageToBackendExtension::SendMessageToBackend(
const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK(backend_task_runner_);
CHECK(args.Length() == 2 && args[0]->IsString() && args[1]->IsInt32());
v8::Local<v8::String> message = args[0].As<v8::String>();
backend_task_runner_->Append(new SendMessageToBackendTask(
ToVector(message), args[1].As<v8::Int32>()->Value()));
}

View File

@ -5,11 +5,15 @@
#ifndef V8_TEST_INSPECTOR_PROTOCOL_INSPECTOR_IMPL_H_
#define V8_TEST_INSPECTOR_PROTOCOL_INSPECTOR_IMPL_H_
#include <map>
#include <vector>
#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<v8::Context> context);
static v8_inspector::V8InspectorSession* SessionFromContext(
v8::Local<v8::Context> 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<v8::Value> 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<v8_inspector::StringBuffer> 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<v8::Value> memory_info);
void SetLogConsoleApiMessageCalls(bool log);
void ContextCreated(v8::Local<v8::Context> context, int context_group_id);
void ContextDestroyed(v8::Local<v8::Context> 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<int> GetSessionIds(int context_group_id);
std::unique_ptr<v8_inspector::V8Inspector> inspector_;
std::unique_ptr<v8_inspector::V8Inspector::Channel> channel_;
int last_session_id_ = 0;
std::map<int, std::unique_ptr<v8_inspector::V8InspectorSession>> sessions_;
std::map<int, std::unique_ptr<v8_inspector::StringBuffer>> states_;
std::map<v8_inspector::V8InspectorSession*, int> context_group_by_session_;
std::map<int, std::unique_ptr<v8_inspector::V8Inspector::Channel>> channels_;
TaskRunner* task_runner_;
v8::Isolate* isolate_;
v8::Global<v8::Value> 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<v8::ObjectTemplate> global) override;
static void set_backend_task_runner(TaskRunner* task_runner) {
backend_task_runner_ = task_runner;
}
private:
static void SendMessageToBackend(
const v8::FunctionCallbackInfo<v8::Value>& args);
static TaskRunner* backend_task_runner_;
};
#endif // V8_TEST_INSPECTOR_PROTOCOL_INSPECTOR_IMPL_H_

View File

@ -51,6 +51,196 @@ v8::Local<v8::String> ToV8String(v8::Isolate* isolate, const char* str) {
.ToLocalChecked();
}
v8::internal::Vector<uint16_t> ToVector(
const v8_inspector::StringView& string) {
v8::internal::Vector<uint16_t> buffer =
v8::internal::Vector<uint16_t>::New(static_cast<int>(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<uint16_t>& 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<uint16_t>& state_;
int* session_id_;
};
class DisconnectSessionTask : public TaskRunner::Task {
public:
DisconnectSessionTask(v8::base::Semaphore* ready_semaphore, int session_id,
v8::internal::Vector<uint16_t>* 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<v8_inspector::StringBuffer> 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<uint16_t>* state_;
};
class SendMessageToBackendTask : public TaskRunner::Task {
public:
explicit SendMessageToBackendTask(
int session_id, const v8::internal::Vector<uint16_t>& 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<uint16_t> message_;
};
class SchedulePauseOnNextStatementTask : public TaskRunner::Task {
public:
SchedulePauseOnNextStatementTask(
v8::base::Semaphore* ready_semaphore, int context_group_id,
const v8::internal::Vector<uint16_t>& reason,
const v8::internal::Vector<uint16_t>& 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<uint16_t>& reason_;
const v8::internal::Vector<uint16_t>& 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<uint16_t>& 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<v8::Function> 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<v8::Context> context = data()->GetContext(context_group_id_);
v8::Context::Scope context_scope(context);
if (dispatchers_.find(session_id_) == dispatchers_.end()) return;
v8::Local<v8::Function> function = dispatchers_[session_id_].Get(isolate());
v8::Local<v8::Value> message =
v8::String::NewFromTwoByte(isolate(), message_.start(),
v8::NewStringType::kNormal,
static_cast<int>(message_.size()))
.ToLocalChecked();
v8::MaybeLocal<v8::Value> result;
result = function->Call(context, context->Global(), 1, &message);
}
static std::map<int, v8::Global<v8::Function>> dispatchers_;
int context_group_id_;
int session_id_;
v8::internal::Vector<uint16_t> message_;
};
std::map<int, v8::Global<v8::Function>> 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<v8::Value>& args) {
for (int i = 0; i < args.Length(); i++) {
@ -189,27 +379,31 @@ class UtilsExtension : public IsolateData::SetupGlobalTask {
}
v8::internal::Vector<const char> chars;
v8::Isolate* isolate = args.GetIsolate();
v8::Local<v8::Context> 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<v8::Value>& 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<v8::String>()), args[1].As<v8::String>(),
args[2].As<v8::Int32>(), args[3].As<v8::Int32>(),
args[4].As<v8::Boolean>(), nullptr, nullptr));
nullptr, args[0].As<v8::Int32>()->Value(), nullptr,
ToVector(args[1].As<v8::String>()), args[2].As<v8::String>(),
args[3].As<v8::Int32>(), args[4].As<v8::Int32>(),
args[5].As<v8::Boolean>()));
}
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<v8::Number>()->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<v8::Value>& 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<uint16_t> reason = ToVector(args[0].As<v8::String>());
v8_inspector::StringView reason_view(reason.start(), reason.length());
v8::internal::Vector<uint16_t> details = ToVector(args[1].As<v8::String>());
v8_inspector::StringView details_view(details.start(), details.length());
inspector_client_->session()->schedulePauseOnNextStatement(reason_view,
details_view);
v8::internal::Vector<uint16_t> reason = ToVector(args[1].As<v8::String>());
v8::internal::Vector<uint16_t> details = ToVector(args[2].As<v8::String>());
v8::base::Semaphore ready_semaphore(0);
backend_runner_->Append(new SchedulePauseOnNextStatementTask(
&ready_semaphore, args[0].As<v8::Int32>()->Value(), reason, details));
ready_semaphore.Wait();
}
static void CancelPauseOnNextStatement(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 0) {
fprintf(stderr, "Internal error: cancelPauseOnNextStatement().");
Exit();
}
inspector_client_->session()->cancelPauseOnNextStatement();
}
static void Reconnect(const v8::FunctionCallbackInfo<v8::Value>& 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<v8::Value>& 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<v8::Int32>()->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<v8::Boolean>()->Value());
}
static void CreateContextGroup(
const v8::FunctionCallbackInfo<v8::Value>& args);
const v8::FunctionCallbackInfo<v8::Value>& 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<v8::Value>& 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<uint16_t> state = ToVector(args[1].As<v8::String>());
v8::base::Semaphore ready_semaphore(0);
int session_id = 0;
backend_runner_->Append(new ConnectSessionTask(
&ready_semaphore, args[0].As<v8::Int32>()->Value(), state,
&session_id));
ready_semaphore.Wait();
SendMessageToFrontendTask::Register(session_id, args.GetIsolate(),
args[2].As<v8::Function>());
args.GetReturnValue().Set(v8::Int32::New(args.GetIsolate(), session_id));
}
static void DisconnectSession(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 1 || !args[0]->IsInt32()) {
fprintf(stderr, "Internal error: disconnectionSession(session_id).");
Exit();
}
int session_id = args[0].As<v8::Int32>()->Value();
SendMessageToFrontendTask::Unregister(session_id);
v8::base::Semaphore ready_semaphore(0);
v8::internal::Vector<uint16_t> 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<int>(state.size()))
.ToLocalChecked());
}
static void SendMessageToBackend(
const v8::FunctionCallbackInfo<v8::Value>& 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<v8::Int32>()->Value(), ToVector(args[1].As<v8::String>())));
}
};
TaskRunner* UtilsExtension::backend_runner_ = nullptr;
InspectorClientImpl* UtilsExtension::inspector_client_ = nullptr;
class SetTimeoutTask : public AsyncTask {
public:
SetTimeoutTask(v8::Isolate* isolate, v8::Local<v8::Function> 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<v8::Function> 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<v8::Context> context = default_context();
v8::Local<v8::Context> context = data()->GetContext(context_group_id_);
v8::Context::Scope context_scope(context);
v8::Local<v8::Function> function = function_.Get(isolate());
@ -316,6 +559,7 @@ class SetTimeoutTask : public AsyncTask {
}
v8::Global<v8::Function> 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<v8::Number>()->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<v8::Context> context = isolate->GetCurrentContext();
IsolateData* data = IsolateData::FromContext(context);
int context_group_id = data->GetContextGroupId(context);
std::unique_ptr<TaskRunner::Task> task;
v8_inspector::V8Inspector* inspector =
InspectorClientImpl::InspectorFromContext(context);
if (args[0]->IsFunction()) {
task.reset(new SetTimeoutTask(isolate,
v8::Local<v8::Function>::Cast(args[0]),
"setTimeout", inspector));
task.reset(new SetTimeoutTask(data, context_group_id, "setTimeout",
v8::Local<v8::Function>::Cast(args[0])));
} else {
task.reset(new ExecuteStringTask(
data, context_group_id, "setTimeout",
ToVector(args[0].As<v8::String>()), 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<v8::ObjectTemplate> global) override {
v8::Local<v8::ObjectTemplate> 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<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::Local<v8::Context> 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<v8::Value>& args) {
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
IsolateData* data = IsolateData::FromContext(context);
data->inspector()->ContextCreated(context,
data->GetContextGroupId(context));
}
static void Detach(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::Local<v8::Context> 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<v8::Value>& args) {
v8::Local<v8::Context> 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<v8::Int32>()->Value());
IsolateData::FromContext(args.GetIsolate()->GetCurrentContext())
->inspector()
->inspector(),
args[0].As<v8::Int32>()->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<v8::Value>& 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<v8::Context> context = args.GetIsolate()->GetCurrentContext();
IsolateData* data = IsolateData::FromContext(context);
v8::internal::Vector<uint16_t> reason = ToVector(args[0].As<v8::String>());
v8_inspector::StringView reason_view(reason.start(), reason.length());
v8::internal::Vector<uint16_t> details = ToVector(args[1].As<v8::String>());
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<v8::Value>& 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<uint16_t> reason = ToVector(args[1].As<v8::String>());
v8_inspector::StringView reason_view(reason.start(), reason.length());
v8::internal::Vector<uint16_t> details = ToVector(args[2].As<v8::String>());
v8_inspector::StringView details_view(details.start(), details.length());
session->schedulePauseOnNextStatement(reason_view, details_view);
v8::Local<v8::Context> 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<v8::Value> result;
result = args[0].As<v8::Function>()->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<v8::Value>& 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<v8::String> ToString(v8::Isolate* isolate,
const v8_inspector::StringView& string) {
if (string.is8Bit())
return v8::String::NewFromOneByte(isolate, string.characters8(),
v8::NewStringType::kNormal,
static_cast<int>(string.length()))
.ToLocalChecked();
else
return v8::String::NewFromTwoByte(isolate, string.characters16(),
v8::NewStringType::kNormal,
static_cast<int>(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<v8::String> prefix =
v8::String::NewFromUtf8(isolate, "InspectorTest._dispatchMessage(",
v8::NewStringType::kInternalized)
.ToLocalChecked();
v8::Local<v8::String> message_string = ToString(isolate, message);
v8::Local<v8::String> suffix =
v8::String::NewFromUtf8(isolate, ")", v8::NewStringType::kInternalized)
.ToLocalChecked();
v8::Local<v8::String> 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();

View File

@ -10,6 +10,7 @@
namespace {
const int kIsolateDataIndex = 2;
const int kContextGroupIdIndex = 3;
v8::internal::Vector<uint16_t> ToVector(v8::Local<v8::String> str) {
v8::internal::Vector<uint16_t> buffer =
@ -22,7 +23,8 @@ v8::internal::Vector<uint16_t> ToVector(v8::Local<v8::String> 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<v8::Context> context) {
@ -39,6 +43,7 @@ IsolateData* IsolateData::FromContext(v8::Local<v8::Context> context) {
}
int IsolateData::CreateContextGroup() {
v8::HandleScope handle_scope(isolate_);
v8::Local<v8::ObjectTemplate> 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<void*>(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<v8::Context> IsolateData::GetContext(int context_group_id) {
return contexts_[context_group_id].Get(isolate_);
}
int IsolateData::GetContextGroupId(v8::Local<v8::Context> context) {
return static_cast<int>(
reinterpret_cast<intptr_t>(
context->GetAlignedPointerFromEmbedderData(kContextGroupIdIndex)) /
2);
}
void IsolateData::RegisterModule(v8::Local<v8::Context> context,
v8::internal::Vector<uint16_t> name,
v8::ScriptCompiler::Source* source) {

View File

@ -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<std::unique_ptr<SetupGlobalTask>>;
IsolateData(TaskRunner* task_runner, SetupGlobalTasks setup_global_tasks,
v8::StartupData* startup_data);
v8::StartupData* startup_data,
InspectorClientImpl::FrontendChannel* channel);
static IsolateData* FromContext(v8::Local<v8::Context> context);
v8::Isolate* isolate() const { return isolate_; }
InspectorClientImpl* inspector() const { return inspector_.get(); }
TaskRunner* task_runner() const { return task_runner_; }
int CreateContextGroup();
v8::Local<v8::Context> GetContext(int context_group_id);
int GetContextGroupId(v8::Local<v8::Context> context);
void RegisterModule(v8::Local<v8::Context> context,
v8::internal::Vector<uint16_t> name,
v8::ScriptCompiler::Source* source);
@ -53,6 +57,7 @@ class IsolateData {
TaskRunner* task_runner_;
SetupGlobalTasks setup_global_tasks_;
v8::Isolate* isolate_;
std::unique_ptr<InspectorClientImpl> inspector_;
int last_context_group_id_ = 0;
std::map<int, v8::Global<v8::Context>> contexts_;
std::map<v8::internal::Vector<uint16_t>, v8::Global<v8::Module>,

View File

@ -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)
{

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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();
}

View File

@ -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<uint16_t> ToVector(v8::Local<v8::String> str) {
@ -32,10 +40,12 @@ v8::internal::Vector<uint16_t> ToVector(v8::Local<v8::String> 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<const uint8_t*>(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<const uint8_t*>(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<uint16_t>& expression,
v8::Local<v8::String> name, v8::Local<v8::Integer> line_offset,
v8::Local<v8::Integer> column_offset, v8::Local<v8::Boolean> is_module,
const char* task_name, v8_inspector::V8Inspector* inspector)
: AsyncTask(task_name, inspector),
v8::Local<v8::Integer> column_offset, v8::Local<v8::Boolean> is_module)
: AsyncTask(data, task_name),
expression_(expression),
name_(ToVector(name)),
line_offset_(line_offset.As<v8::Int32>()->Value()),
column_offset_(column_offset.As<v8::Int32>()->Value()),
is_module_(is_module->Value()) {}
is_module_(is_module->Value()),
context_group_id_(context_group_id) {}
ExecuteStringTask::ExecuteStringTask(
const v8::internal::Vector<const char>& expression)
: AsyncTask(nullptr, nullptr), expression_utf8_(expression) {}
const v8::internal::Vector<const char>& 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<v8::Context> context = default_context();
v8::Local<v8::Context> context = data()->GetContext(context_group_id_);
v8::Context::Scope context_scope(context);
v8::Local<v8::String> name =
@ -193,7 +199,6 @@ void ExecuteStringTask::AsyncRun() {
v8::MaybeLocal<v8::Value> result;
result = script->Run(context);
} else {
IsolateData::FromContext(context)->RegisterModule(context, name_,
&scriptSource);
data()->RegisterModule(context, name_, &scriptSource);
}
}

View File

@ -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<v8::Context> 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<IsolateData> 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<uint16_t>& expression,
ExecuteStringTask(IsolateData* data, int context_group_id,
const char* task_name,
const v8::internal::Vector<uint16_t>& expression,
v8::Local<v8::String> name,
v8::Local<v8::Integer> line_offset,
v8::Local<v8::Integer> column_offset,
v8::Local<v8::Boolean> is_module, const char* task_name,
v8_inspector::V8Inspector* inspector);
explicit ExecuteStringTask(
const v8::internal::Vector<const char>& expression);
v8::Local<v8::Boolean> is_module);
ExecuteStringTask(const v8::internal::Vector<const char>& 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);
};