d46662c084
This patch moves the stop method from context group to session to enable stopping each session independently. This is useful for testing that stopping does not interact badly with other sessions. Bug: chromium:1354043 Change-Id: I885cf49f2d4cf006fa5228edf2954099e45cfc6b Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4100484 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Jaroslav Sevcik <jarin@chromium.org> Cr-Commit-Position: refs/heads/main@{#84797}
919 lines
37 KiB
C++
919 lines
37 KiB
C++
// Copyright 2016 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#if !defined(_WIN32) && !defined(_WIN64)
|
|
#include <unistd.h>
|
|
#endif // !defined(_WIN32) && !defined(_WIN64)
|
|
|
|
#include <locale.h>
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "include/libplatform/libplatform.h"
|
|
#include "include/v8-exception.h"
|
|
#include "include/v8-initialization.h"
|
|
#include "include/v8-local-handle.h"
|
|
#include "include/v8-snapshot.h"
|
|
#include "src/base/platform/platform.h"
|
|
#include "src/base/small-vector.h"
|
|
#include "src/flags/flags.h"
|
|
#include "src/utils/utils.h"
|
|
#include "test/inspector/frontend-channel.h"
|
|
#include "test/inspector/isolate-data.h"
|
|
#include "test/inspector/task-runner.h"
|
|
#include "test/inspector/tasks.h"
|
|
#include "test/inspector/utils.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
extern void DisableEmbeddedBlobRefcounting();
|
|
extern void FreeCurrentEmbeddedBlob();
|
|
|
|
extern v8::StartupData CreateSnapshotDataBlobInternal(
|
|
v8::SnapshotCreator::FunctionCodeHandling function_code_handling,
|
|
const char* embedded_source, v8::Isolate* isolate);
|
|
extern v8::StartupData WarmUpSnapshotDataBlobInternal(
|
|
v8::StartupData cold_snapshot_blob, const char* warmup_source);
|
|
|
|
namespace {
|
|
|
|
base::SmallVector<TaskRunner*, 2> task_runners;
|
|
|
|
class UtilsExtension : public InspectorIsolateData::SetupGlobalTask {
|
|
public:
|
|
~UtilsExtension() override = default;
|
|
void Run(v8::Isolate* isolate,
|
|
v8::Local<v8::ObjectTemplate> global) override {
|
|
v8::Local<v8::ObjectTemplate> utils = v8::ObjectTemplate::New(isolate);
|
|
utils->Set(isolate, "print",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::Print));
|
|
utils->Set(isolate, "quit",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::Quit));
|
|
utils->Set(isolate, "setlocale",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::Setlocale));
|
|
utils->Set(isolate, "read",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::Read));
|
|
utils->Set(isolate, "load",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::Load));
|
|
utils->Set(isolate, "compileAndRunWithOrigin",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &UtilsExtension::CompileAndRunWithOrigin));
|
|
utils->Set(isolate, "setCurrentTimeMSForTest",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &UtilsExtension::SetCurrentTimeMSForTest));
|
|
utils->Set(isolate, "setMemoryInfoForTest",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &UtilsExtension::SetMemoryInfoForTest));
|
|
utils->Set(isolate, "schedulePauseOnNextStatement",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &UtilsExtension::SchedulePauseOnNextStatement));
|
|
utils->Set(isolate, "cancelPauseOnNextStatement",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &UtilsExtension::CancelPauseOnNextStatement));
|
|
utils->Set(isolate, "stop",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::Stop));
|
|
utils->Set(isolate, "setLogConsoleApiMessageCalls",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &UtilsExtension::SetLogConsoleApiMessageCalls));
|
|
utils->Set(isolate, "setAdditionalConsoleApi",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &UtilsExtension::SetAdditionalConsoleApi));
|
|
utils->Set(
|
|
isolate, "setLogMaxAsyncCallStackDepthChanged",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &UtilsExtension::SetLogMaxAsyncCallStackDepthChanged));
|
|
utils->Set(isolate, "createContextGroup",
|
|
v8::FunctionTemplate::New(isolate,
|
|
&UtilsExtension::CreateContextGroup));
|
|
utils->Set(
|
|
isolate, "createContext",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::CreateContext));
|
|
utils->Set(
|
|
isolate, "resetContextGroup",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::ResetContextGroup));
|
|
utils->Set(
|
|
isolate, "connectSession",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::ConnectSession));
|
|
utils->Set(
|
|
isolate, "disconnectSession",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::DisconnectSession));
|
|
utils->Set(isolate, "sendMessageToBackend",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &UtilsExtension::SendMessageToBackend));
|
|
utils->Set(isolate, "interruptForMessages",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &UtilsExtension::InterruptForMessages));
|
|
utils->Set(
|
|
isolate, "waitForDebugger",
|
|
v8::FunctionTemplate::New(isolate, &UtilsExtension::WaitForDebugger));
|
|
global->Set(isolate, "utils", utils);
|
|
}
|
|
|
|
static void set_backend_task_runner(TaskRunner* runner) {
|
|
backend_runner_ = runner;
|
|
}
|
|
|
|
static void ClearAllSessions() { channels_.clear(); }
|
|
|
|
private:
|
|
static TaskRunner* backend_runner_;
|
|
|
|
static void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
for (int i = 0; i < args.Length(); i++) {
|
|
v8::HandleScope handle_scope(args.GetIsolate());
|
|
if (i != 0) {
|
|
printf(" ");
|
|
}
|
|
|
|
// Explicitly catch potential exceptions in toString().
|
|
v8::TryCatch try_catch(args.GetIsolate());
|
|
v8::Local<v8::Value> arg = args[i];
|
|
v8::Local<v8::String> str_obj;
|
|
|
|
if (arg->IsSymbol()) {
|
|
arg = v8::Local<v8::Symbol>::Cast(arg)->Description(args.GetIsolate());
|
|
}
|
|
if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
|
|
.ToLocal(&str_obj)) {
|
|
try_catch.ReThrow();
|
|
return;
|
|
}
|
|
|
|
v8::String::Utf8Value str(args.GetIsolate(), str_obj);
|
|
int n =
|
|
static_cast<int>(fwrite(*str, sizeof(**str), str.length(), stdout));
|
|
if (n != str.length()) {
|
|
FATAL("Error in fwrite\n");
|
|
}
|
|
}
|
|
printf("\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
// Only terminate, so not join the threads here, since joining concurrently
|
|
// from multiple threads can be undefined behaviour (see pthread_join).
|
|
for (TaskRunner* task_runner : task_runners) task_runner->Terminate();
|
|
}
|
|
|
|
static void Setlocale(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsString()) {
|
|
FATAL("Internal error: setlocale get one string argument.");
|
|
}
|
|
|
|
v8::String::Utf8Value str(args.GetIsolate(), args[1]);
|
|
setlocale(LC_NUMERIC, *str);
|
|
}
|
|
|
|
static bool ReadFile(v8::Isolate* isolate, v8::Local<v8::Value> name,
|
|
std::string* chars) {
|
|
v8::String::Utf8Value str(isolate, name);
|
|
bool exists = false;
|
|
std::string filename(*str, str.length());
|
|
*chars = v8::internal::ReadFile(filename.c_str(), &exists);
|
|
if (!exists) {
|
|
isolate->ThrowError("Error reading file");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsString()) {
|
|
FATAL("Internal error: read gets one string argument.");
|
|
}
|
|
std::string chars;
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
if (ReadFile(isolate, args[0], &chars)) {
|
|
args.GetReturnValue().Set(ToV8String(isolate, chars));
|
|
}
|
|
}
|
|
|
|
static void Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsString()) {
|
|
FATAL("Internal error: load gets one string argument.");
|
|
}
|
|
std::string chars;
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
int context_group_id = data->GetContextGroupId(context);
|
|
if (ReadFile(isolate, args[0], &chars)) {
|
|
ExecuteStringTask(chars, context_group_id).Run(data);
|
|
}
|
|
}
|
|
|
|
static void CompileAndRunWithOrigin(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 6 || !args[0]->IsInt32() || !args[1]->IsString() ||
|
|
!args[2]->IsString() || !args[3]->IsInt32() || !args[4]->IsInt32() ||
|
|
!args[5]->IsBoolean()) {
|
|
FATAL(
|
|
"Internal error: compileAndRunWithOrigin(context_group_id, source, "
|
|
"name, line, column, is_module).");
|
|
}
|
|
|
|
backend_runner_->Append(std::make_unique<ExecuteStringTask>(
|
|
args.GetIsolate(), args[0].As<v8::Int32>()->Value(),
|
|
ToVector(args.GetIsolate(), 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(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsNumber()) {
|
|
FATAL("Internal error: setCurrentTimeMSForTest(time).");
|
|
}
|
|
backend_runner_->data()->SetCurrentTimeMS(
|
|
args[0].As<v8::Number>()->Value());
|
|
}
|
|
|
|
static void SetMemoryInfoForTest(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1) {
|
|
FATAL("Internal error: setMemoryInfoForTest(value).");
|
|
}
|
|
backend_runner_->data()->SetMemoryInfo(args[0]);
|
|
}
|
|
|
|
static void SchedulePauseOnNextStatement(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 3 || !args[0]->IsInt32() || !args[1]->IsString() ||
|
|
!args[2]->IsString()) {
|
|
FATAL(
|
|
"Internal error: schedulePauseOnNextStatement(context_group_id, "
|
|
"'reason', 'details').");
|
|
}
|
|
std::vector<uint16_t> reason =
|
|
ToVector(args.GetIsolate(), args[1].As<v8::String>());
|
|
std::vector<uint16_t> details =
|
|
ToVector(args.GetIsolate(), args[2].As<v8::String>());
|
|
int context_group_id = args[0].As<v8::Int32>()->Value();
|
|
RunSyncTask(backend_runner_,
|
|
[&context_group_id, &reason,
|
|
&details](InspectorIsolateData* data) {
|
|
data->SchedulePauseOnNextStatement(
|
|
context_group_id,
|
|
v8_inspector::StringView(reason.data(), reason.size()),
|
|
v8_inspector::StringView(details.data(), details.size()));
|
|
});
|
|
}
|
|
|
|
static void CancelPauseOnNextStatement(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsInt32()) {
|
|
FATAL("Internal error: cancelPauseOnNextStatement(context_group_id).");
|
|
}
|
|
int context_group_id = args[0].As<v8::Int32>()->Value();
|
|
RunSyncTask(backend_runner_,
|
|
[&context_group_id](InspectorIsolateData* data) {
|
|
data->CancelPauseOnNextStatement(context_group_id);
|
|
});
|
|
}
|
|
|
|
static void Stop(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsInt32()) {
|
|
FATAL("Internal error: stop(session_id).");
|
|
}
|
|
int session_id = args[0].As<v8::Int32>()->Value();
|
|
RunSyncTask(backend_runner_, [&session_id](InspectorIsolateData* data) {
|
|
data->Stop(session_id);
|
|
});
|
|
}
|
|
|
|
static void SetLogConsoleApiMessageCalls(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsBoolean()) {
|
|
FATAL("Internal error: setLogConsoleApiMessageCalls(bool).");
|
|
}
|
|
backend_runner_->data()->SetLogConsoleApiMessageCalls(
|
|
args[0].As<v8::Boolean>()->Value());
|
|
}
|
|
|
|
static void SetLogMaxAsyncCallStackDepthChanged(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsBoolean()) {
|
|
FATAL("Internal error: setLogMaxAsyncCallStackDepthChanged(bool).");
|
|
}
|
|
backend_runner_->data()->SetLogMaxAsyncCallStackDepthChanged(
|
|
args[0].As<v8::Boolean>()->Value());
|
|
}
|
|
|
|
static void SetAdditionalConsoleApi(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsString()) {
|
|
FATAL("Internal error: SetAdditionalConsoleApi(string).");
|
|
}
|
|
std::vector<uint16_t> script =
|
|
ToVector(args.GetIsolate(), args[0].As<v8::String>());
|
|
RunSyncTask(backend_runner_, [&script](InspectorIsolateData* data) {
|
|
data->SetAdditionalConsoleApi(
|
|
v8_inspector::StringView(script.data(), script.size()));
|
|
});
|
|
}
|
|
|
|
static void CreateContextGroup(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 0) {
|
|
FATAL("Internal error: createContextGroup().");
|
|
}
|
|
int context_group_id = 0;
|
|
RunSyncTask(backend_runner_,
|
|
[&context_group_id](InspectorIsolateData* data) {
|
|
context_group_id = data->CreateContextGroup();
|
|
});
|
|
args.GetReturnValue().Set(
|
|
v8::Int32::New(args.GetIsolate(), context_group_id));
|
|
}
|
|
|
|
static void CreateContext(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 2) {
|
|
FATAL("Internal error: createContext(context, name).");
|
|
}
|
|
int context_group_id = args[0].As<v8::Int32>()->Value();
|
|
std::vector<uint16_t> name =
|
|
ToVector(args.GetIsolate(), args[1].As<v8::String>());
|
|
|
|
RunSyncTask(backend_runner_, [&context_group_id,
|
|
name](InspectorIsolateData* data) {
|
|
CHECK(data->CreateContext(
|
|
context_group_id,
|
|
v8_inspector::StringView(name.data(), name.size())));
|
|
});
|
|
}
|
|
|
|
static void ResetContextGroup(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsInt32()) {
|
|
FATAL("Internal error: resetContextGroup(context_group_id).");
|
|
}
|
|
int context_group_id = args[0].As<v8::Int32>()->Value();
|
|
RunSyncTask(backend_runner_,
|
|
[&context_group_id](InspectorIsolateData* data) {
|
|
data->ResetContextGroup(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()) {
|
|
FATAL(
|
|
"Internal error: connectionSession(context_group_id, state, "
|
|
"dispatch).");
|
|
}
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
FrontendChannelImpl* channel = new FrontendChannelImpl(
|
|
InspectorIsolateData::FromContext(context)->task_runner(),
|
|
InspectorIsolateData::FromContext(context)->GetContextGroupId(context),
|
|
args.GetIsolate(), args[2].As<v8::Function>());
|
|
|
|
std::vector<uint8_t> state =
|
|
ToBytes(args.GetIsolate(), args[1].As<v8::String>());
|
|
int context_group_id = args[0].As<v8::Int32>()->Value();
|
|
int session_id = 0;
|
|
RunSyncTask(backend_runner_, [&context_group_id, &session_id, &channel,
|
|
&state](InspectorIsolateData* data) {
|
|
session_id = data->ConnectSession(
|
|
context_group_id,
|
|
v8_inspector::StringView(state.data(), state.size()), channel);
|
|
channel->set_session_id(session_id);
|
|
});
|
|
|
|
channels_[session_id].reset(channel);
|
|
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()) {
|
|
FATAL("Internal error: disconnectionSession(session_id).");
|
|
}
|
|
int session_id = args[0].As<v8::Int32>()->Value();
|
|
std::vector<uint8_t> state;
|
|
RunSyncTask(backend_runner_,
|
|
[&session_id, &state](InspectorIsolateData* data) {
|
|
state = data->DisconnectSession(session_id);
|
|
});
|
|
channels_.erase(session_id);
|
|
args.GetReturnValue().Set(ToV8String(args.GetIsolate(), state));
|
|
}
|
|
|
|
static void SendMessageToBackend(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 2 || !args[0]->IsInt32() || !args[1]->IsString()) {
|
|
FATAL("Internal error: sendMessageToBackend(session_id, message).");
|
|
}
|
|
backend_runner_->Append(std::make_unique<SendMessageToBackendTask>(
|
|
args[0].As<v8::Int32>()->Value(),
|
|
ToVector(args.GetIsolate(), args[1].As<v8::String>())));
|
|
}
|
|
|
|
static void InterruptForMessages(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
backend_runner_->InterruptForMessages();
|
|
}
|
|
|
|
static void WaitForDebugger(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 2 || !args[0]->IsInt32() || !args[1]->IsFunction()) {
|
|
FATAL("Internal error: waitForDebugger(context_group_id, callback).");
|
|
}
|
|
int context_group_id = args[0].As<v8::Int32>()->Value();
|
|
RunSimpleAsyncTask(
|
|
backend_runner_,
|
|
[context_group_id](InspectorIsolateData* data) {
|
|
data->WaitForDebugger(context_group_id);
|
|
},
|
|
args[1].As<v8::Function>());
|
|
}
|
|
|
|
static std::map<int, std::unique_ptr<FrontendChannelImpl>> channels_;
|
|
};
|
|
|
|
TaskRunner* UtilsExtension::backend_runner_ = nullptr;
|
|
std::map<int, std::unique_ptr<FrontendChannelImpl>> UtilsExtension::channels_;
|
|
|
|
bool StrictAccessCheck(v8::Local<v8::Context> accessing_context,
|
|
v8::Local<v8::Object> accessed_object,
|
|
v8::Local<v8::Value> data) {
|
|
CHECK(accessing_context.IsEmpty());
|
|
return accessing_context.IsEmpty();
|
|
}
|
|
|
|
class ConsoleExtension : public InspectorIsolateData::SetupGlobalTask {
|
|
public:
|
|
~ConsoleExtension() override = default;
|
|
void Run(v8::Isolate* isolate,
|
|
v8::Local<v8::ObjectTemplate> global) override {
|
|
v8::Local<v8::String> name =
|
|
v8::String::NewFromUtf8Literal(isolate, "console");
|
|
global->SetAccessor(name, &ConsoleGetterCallback, nullptr, {}, v8::DEFAULT,
|
|
v8::DontEnum);
|
|
}
|
|
|
|
private:
|
|
static void ConsoleGetterCallback(
|
|
v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
v8::Local<v8::String> name =
|
|
v8::String::NewFromUtf8Literal(isolate, "console");
|
|
v8::Local<v8::Object> console = context->GetExtrasBindingObject()
|
|
->Get(context, name)
|
|
.ToLocalChecked()
|
|
.As<v8::Object>();
|
|
info.GetReturnValue().Set(console);
|
|
}
|
|
};
|
|
|
|
class InspectorExtension : public InspectorIsolateData::SetupGlobalTask {
|
|
public:
|
|
~InspectorExtension() override = default;
|
|
void Run(v8::Isolate* isolate,
|
|
v8::Local<v8::ObjectTemplate> global) override {
|
|
v8::Local<v8::ObjectTemplate> inspector = v8::ObjectTemplate::New(isolate);
|
|
inspector->Set(isolate, "fireContextCreated",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::FireContextCreated));
|
|
inspector->Set(isolate, "fireContextDestroyed",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::FireContextDestroyed));
|
|
inspector->Set(
|
|
isolate, "freeContext",
|
|
v8::FunctionTemplate::New(isolate, &InspectorExtension::FreeContext));
|
|
inspector->Set(isolate, "addInspectedObject",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::AddInspectedObject));
|
|
inspector->Set(isolate, "setMaxAsyncTaskStacks",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::SetMaxAsyncTaskStacks));
|
|
inspector->Set(
|
|
isolate, "dumpAsyncTaskStacksStateForTest",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::DumpAsyncTaskStacksStateForTest));
|
|
inspector->Set(
|
|
isolate, "breakProgram",
|
|
v8::FunctionTemplate::New(isolate, &InspectorExtension::BreakProgram));
|
|
inspector->Set(
|
|
isolate, "createObjectWithStrictCheck",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::CreateObjectWithStrictCheck));
|
|
inspector->Set(isolate, "callWithScheduledBreak",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::CallWithScheduledBreak));
|
|
inspector->Set(
|
|
isolate, "markObjectAsNotInspectable",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::MarkObjectAsNotInspectable));
|
|
inspector->Set(isolate, "createObjectWithAccessor",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::CreateObjectWithAccessor));
|
|
inspector->Set(isolate, "storeCurrentStackTrace",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::StoreCurrentStackTrace));
|
|
inspector->Set(isolate, "externalAsyncTaskStarted",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::ExternalAsyncTaskStarted));
|
|
inspector->Set(
|
|
isolate, "externalAsyncTaskFinished",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::ExternalAsyncTaskFinished));
|
|
inspector->Set(isolate, "scheduleWithAsyncStack",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::ScheduleWithAsyncStack));
|
|
inspector->Set(
|
|
isolate, "setAllowCodeGenerationFromStrings",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::SetAllowCodeGenerationFromStrings));
|
|
inspector->Set(isolate, "setResourceNamePrefix",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::SetResourceNamePrefix));
|
|
inspector->Set(isolate, "newExceptionWithMetaData",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::newExceptionWithMetaData));
|
|
inspector->Set(isolate, "callbackForTests",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::CallbackForTests));
|
|
inspector->Set(isolate, "runNestedMessageLoop",
|
|
v8::FunctionTemplate::New(
|
|
isolate, &InspectorExtension::RunNestedMessageLoop));
|
|
global->Set(isolate, "inspector", inspector);
|
|
}
|
|
|
|
private:
|
|
static void FireContextCreated(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
data->FireContextCreated(context, data->GetContextGroupId(context),
|
|
v8_inspector::StringView());
|
|
}
|
|
|
|
static void FireContextDestroyed(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
data->FireContextDestroyed(context);
|
|
}
|
|
|
|
static void FreeContext(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
data->FreeContext(context);
|
|
}
|
|
|
|
static void AddInspectedObject(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 2 || !args[0]->IsInt32()) {
|
|
FATAL("Internal error: addInspectedObject(session_id, object).");
|
|
}
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
data->AddInspectedObject(args[0].As<v8::Int32>()->Value(), args[1]);
|
|
}
|
|
|
|
static void SetMaxAsyncTaskStacks(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsInt32()) {
|
|
FATAL("Internal error: setMaxAsyncTaskStacks(max).");
|
|
}
|
|
InspectorIsolateData::FromContext(args.GetIsolate()->GetCurrentContext())
|
|
->SetMaxAsyncTaskStacksForTest(args[0].As<v8::Int32>()->Value());
|
|
}
|
|
|
|
static void DumpAsyncTaskStacksStateForTest(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 0) {
|
|
FATAL("Internal error: dumpAsyncTaskStacksStateForTest().");
|
|
}
|
|
InspectorIsolateData::FromContext(args.GetIsolate()->GetCurrentContext())
|
|
->DumpAsyncTaskStacksStateForTest();
|
|
}
|
|
|
|
static void BreakProgram(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) {
|
|
FATAL("Internal error: breakProgram('reason', 'details').");
|
|
}
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
std::vector<uint16_t> reason =
|
|
ToVector(args.GetIsolate(), args[0].As<v8::String>());
|
|
v8_inspector::StringView reason_view(reason.data(), reason.size());
|
|
std::vector<uint16_t> details =
|
|
ToVector(args.GetIsolate(), args[1].As<v8::String>());
|
|
v8_inspector::StringView details_view(details.data(), details.size());
|
|
data->BreakProgram(data->GetContextGroupId(context), reason_view,
|
|
details_view);
|
|
}
|
|
|
|
static void CreateObjectWithStrictCheck(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 0) {
|
|
FATAL("Internal error: createObjectWithStrictCheck().");
|
|
}
|
|
v8::Local<v8::ObjectTemplate> templ =
|
|
v8::ObjectTemplate::New(args.GetIsolate());
|
|
templ->SetAccessCheckCallback(&StrictAccessCheck);
|
|
args.GetReturnValue().Set(
|
|
templ->NewInstance(args.GetIsolate()->GetCurrentContext())
|
|
.ToLocalChecked());
|
|
}
|
|
|
|
static void CallWithScheduledBreak(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 3 || !args[0]->IsFunction() || !args[1]->IsString() ||
|
|
!args[2]->IsString()) {
|
|
FATAL("Internal error: callWithScheduledBreak('reason', 'details').");
|
|
}
|
|
std::vector<uint16_t> reason =
|
|
ToVector(args.GetIsolate(), args[1].As<v8::String>());
|
|
v8_inspector::StringView reason_view(reason.data(), reason.size());
|
|
std::vector<uint16_t> details =
|
|
ToVector(args.GetIsolate(), args[2].As<v8::String>());
|
|
v8_inspector::StringView details_view(details.data(), details.size());
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
int context_group_id = data->GetContextGroupId(context);
|
|
data->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);
|
|
data->CancelPauseOnNextStatement(context_group_id);
|
|
}
|
|
|
|
static void MarkObjectAsNotInspectable(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsObject()) {
|
|
FATAL("Internal error: markObjectAsNotInspectable(object).");
|
|
}
|
|
v8::Local<v8::Object> object = args[0].As<v8::Object>();
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::Local<v8::Private> notInspectablePrivate =
|
|
v8::Private::ForApi(isolate, ToV8String(isolate, "notInspectable"));
|
|
object
|
|
->SetPrivate(isolate->GetCurrentContext(), notInspectablePrivate,
|
|
v8::True(isolate))
|
|
.ToChecked();
|
|
}
|
|
|
|
static void CreateObjectWithAccessor(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsBoolean()) {
|
|
FATAL(
|
|
"Internal error: createObjectWithAccessor('accessor name', "
|
|
"hasSetter)\n");
|
|
}
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::Local<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(isolate);
|
|
if (args[1].As<v8::Boolean>()->Value()) {
|
|
templ->SetAccessor(v8::Local<v8::String>::Cast(args[0]), AccessorGetter,
|
|
AccessorSetter);
|
|
} else {
|
|
templ->SetAccessor(v8::Local<v8::String>::Cast(args[0]), AccessorGetter);
|
|
}
|
|
args.GetReturnValue().Set(
|
|
templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked());
|
|
}
|
|
|
|
static void AccessorGetter(v8::Local<v8::String> property,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
isolate->ThrowError("Getter is called");
|
|
}
|
|
|
|
static void AccessorSetter(v8::Local<v8::String> property,
|
|
v8::Local<v8::Value> value,
|
|
const v8::PropertyCallbackInfo<void>& info) {
|
|
v8::Isolate* isolate = info.GetIsolate();
|
|
isolate->ThrowError("Setter is called");
|
|
}
|
|
|
|
static void StoreCurrentStackTrace(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsString()) {
|
|
FATAL("Internal error: storeCurrentStackTrace('description')\n");
|
|
}
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
std::vector<uint16_t> description =
|
|
ToVector(isolate, args[0].As<v8::String>());
|
|
v8_inspector::StringView description_view(description.data(),
|
|
description.size());
|
|
v8_inspector::V8StackTraceId id =
|
|
data->StoreCurrentStackTrace(description_view);
|
|
v8::Local<v8::ArrayBuffer> buffer =
|
|
v8::ArrayBuffer::New(isolate, sizeof(id));
|
|
*static_cast<v8_inspector::V8StackTraceId*>(
|
|
buffer->GetBackingStore()->Data()) = id;
|
|
args.GetReturnValue().Set(buffer);
|
|
}
|
|
|
|
static void ExternalAsyncTaskStarted(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsArrayBuffer()) {
|
|
FATAL("Internal error: externalAsyncTaskStarted(id)\n");
|
|
}
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
v8_inspector::V8StackTraceId* id =
|
|
static_cast<v8_inspector::V8StackTraceId*>(
|
|
args[0].As<v8::ArrayBuffer>()->GetBackingStore()->Data());
|
|
data->ExternalAsyncTaskStarted(*id);
|
|
}
|
|
|
|
static void ExternalAsyncTaskFinished(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsArrayBuffer()) {
|
|
FATAL("Internal error: externalAsyncTaskFinished(id)\n");
|
|
}
|
|
v8::Local<v8::Context> context = args.GetIsolate()->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
v8_inspector::V8StackTraceId* id =
|
|
static_cast<v8_inspector::V8StackTraceId*>(
|
|
args[0].As<v8::ArrayBuffer>()->GetBackingStore()->Data());
|
|
data->ExternalAsyncTaskFinished(*id);
|
|
}
|
|
|
|
static void ScheduleWithAsyncStack(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 3 || !args[0]->IsFunction() || !args[1]->IsString() ||
|
|
!args[2]->IsBoolean()) {
|
|
FATAL(
|
|
"Internal error: scheduleWithAsyncStack(function, 'task-name', "
|
|
"with_empty_stack).");
|
|
}
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
int context_group_id = data->GetContextGroupId(context);
|
|
bool with_empty_stack = args[2].As<v8::Boolean>()->Value();
|
|
if (with_empty_stack) context->Exit();
|
|
|
|
std::vector<uint16_t> task_name =
|
|
ToVector(isolate, args[1].As<v8::String>());
|
|
v8_inspector::StringView task_name_view(task_name.data(), task_name.size());
|
|
|
|
RunAsyncTask(
|
|
data->task_runner(), task_name_view,
|
|
std::make_unique<SetTimeoutTask>(
|
|
context_group_id, isolate, v8::Local<v8::Function>::Cast(args[0])));
|
|
if (with_empty_stack) context->Enter();
|
|
}
|
|
|
|
static void SetAllowCodeGenerationFromStrings(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsBoolean()) {
|
|
FATAL("Internal error: setAllowCodeGenerationFromStrings(allow).");
|
|
}
|
|
args.GetIsolate()->GetCurrentContext()->AllowCodeGenerationFromStrings(
|
|
args[0].As<v8::Boolean>()->Value());
|
|
}
|
|
static void SetResourceNamePrefix(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsString()) {
|
|
FATAL("Internal error: setResourceNamePrefix('prefix').");
|
|
}
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
data->SetResourceNamePrefix(v8::Local<v8::String>::Cast(args[0]));
|
|
}
|
|
|
|
static void newExceptionWithMetaData(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 3 || !args[0]->IsString() || !args[1]->IsString() ||
|
|
!args[2]->IsString()) {
|
|
FATAL(
|
|
"Internal error: newExceptionWithMetaData('message', 'key', "
|
|
"'value').");
|
|
}
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
|
|
auto error = v8::Exception::Error(args[0].As<v8::String>());
|
|
CHECK(data->AssociateExceptionData(error, args[1].As<v8::String>(),
|
|
args[2].As<v8::String>()));
|
|
args.GetReturnValue().Set(error);
|
|
}
|
|
|
|
static void CallbackForTests(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
if (args.Length() != 1 || !args[0]->IsFunction()) {
|
|
FATAL("Internal error: callbackForTests(function).");
|
|
}
|
|
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
|
|
v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[0]);
|
|
v8::MaybeLocal<v8::Value> result =
|
|
callback->Call(context, v8::Undefined(isolate), 0, nullptr);
|
|
args.GetReturnValue().Set(result.ToLocalChecked());
|
|
}
|
|
|
|
static void RunNestedMessageLoop(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
InspectorIsolateData* data = InspectorIsolateData::FromContext(context);
|
|
|
|
data->task_runner()->RunMessageLoop(true);
|
|
}
|
|
};
|
|
|
|
int InspectorTestMain(int argc, char* argv[]) {
|
|
v8::V8::InitializeICUDefaultLocation(argv[0]);
|
|
std::unique_ptr<Platform> platform(platform::NewDefaultPlatform());
|
|
v8::V8::InitializePlatform(platform.get());
|
|
v8_flags.abort_on_contradictory_flags = true;
|
|
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
|
|
v8::V8::InitializeExternalStartupData(argv[0]);
|
|
v8::V8::Initialize();
|
|
i::DisableEmbeddedBlobRefcounting();
|
|
|
|
base::Semaphore ready_semaphore(0);
|
|
|
|
StartupData startup_data = {nullptr, 0};
|
|
for (int i = 1; i < argc; ++i) {
|
|
if (strcmp(argv[i], "--embed") == 0) {
|
|
argv[i++] = nullptr;
|
|
printf("Embedding script '%s'\n", argv[i]);
|
|
startup_data = i::CreateSnapshotDataBlobInternal(
|
|
SnapshotCreator::FunctionCodeHandling::kClear, argv[i], nullptr);
|
|
argv[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
{
|
|
InspectorIsolateData::SetupGlobalTasks frontend_extensions;
|
|
frontend_extensions.emplace_back(new UtilsExtension());
|
|
frontend_extensions.emplace_back(new ConsoleExtension());
|
|
TaskRunner frontend_runner(std::move(frontend_extensions),
|
|
kFailOnUncaughtExceptions, &ready_semaphore,
|
|
startup_data.data ? &startup_data : nullptr,
|
|
kNoInspector);
|
|
ready_semaphore.Wait();
|
|
|
|
int frontend_context_group_id = 0;
|
|
RunSyncTask(&frontend_runner,
|
|
[&frontend_context_group_id](InspectorIsolateData* data) {
|
|
frontend_context_group_id = data->CreateContextGroup();
|
|
});
|
|
|
|
InspectorIsolateData::SetupGlobalTasks backend_extensions;
|
|
backend_extensions.emplace_back(new SetTimeoutExtension());
|
|
backend_extensions.emplace_back(new ConsoleExtension());
|
|
backend_extensions.emplace_back(new InspectorExtension());
|
|
TaskRunner backend_runner(
|
|
std::move(backend_extensions), kStandardPropagateUncaughtExceptions,
|
|
&ready_semaphore, startup_data.data ? &startup_data : nullptr,
|
|
kWithInspector);
|
|
ready_semaphore.Wait();
|
|
UtilsExtension::set_backend_task_runner(&backend_runner);
|
|
|
|
task_runners = {&frontend_runner, &backend_runner};
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
// Ignore unknown flags.
|
|
if (argv[i] == nullptr || argv[i][0] == '-') continue;
|
|
|
|
bool exists = false;
|
|
std::string chars = ReadFile(argv[i], &exists, true);
|
|
if (!exists) {
|
|
FATAL("Internal error: script file doesn't exists: %s\n", argv[i]);
|
|
}
|
|
frontend_runner.Append(std::make_unique<ExecuteStringTask>(
|
|
chars, frontend_context_group_id));
|
|
}
|
|
|
|
frontend_runner.Join();
|
|
backend_runner.Join();
|
|
|
|
UtilsExtension::ClearAllSessions();
|
|
delete[] startup_data.data;
|
|
|
|
// TaskRunners go out of scope here, which causes Isolate teardown and all
|
|
// running background tasks to be properly joined.
|
|
}
|
|
|
|
i::FreeCurrentEmbeddedBlob();
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|
|
|
|
int main(int argc, char* argv[]) {
|
|
return v8::internal::InspectorTestMain(argc, argv);
|
|
}
|