blob: cc8e55d9b37d9c676ff32eb1e8b3280229e117db [file] [log] [blame]
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_TEST_INSPECTOR_PROTOCOL_ISOLATE_DATA_H_
#define V8_TEST_INSPECTOR_PROTOCOL_ISOLATE_DATA_H_
#include <map>
#include <memory>
#include <set>
#include "include/v8-array-buffer.h"
#include "include/v8-inspector.h"
#include "include/v8-local-handle.h"
#include "include/v8-locker.h"
#include "include/v8-script.h"
#include "src/base/optional.h"
namespace v8 {
class Context;
class Isolate;
class ObjectTemplate;
class StartupData;
namespace internal {
class FrontendChannelImpl;
class TaskRunner;
enum WithInspector : bool { kWithInspector = true, kNoInspector = false };
class InspectorIsolateData : public v8_inspector::V8InspectorClient {
public:
class SetupGlobalTask {
public:
virtual ~SetupGlobalTask() = default;
virtual void Run(v8::Isolate* isolate,
v8::Local<v8::ObjectTemplate> global) = 0;
};
using SetupGlobalTasks = std::vector<std::unique_ptr<SetupGlobalTask>>;
InspectorIsolateData(const InspectorIsolateData&) = delete;
InspectorIsolateData& operator=(const InspectorIsolateData&) = delete;
InspectorIsolateData(TaskRunner* task_runner,
SetupGlobalTasks setup_global_tasks,
v8::StartupData* startup_data,
WithInspector with_inspector);
static InspectorIsolateData* FromContext(v8::Local<v8::Context> context);
~InspectorIsolateData() override;
v8::Isolate* isolate() const { return isolate_.get(); }
TaskRunner* task_runner() const { return task_runner_; }
// Setting things up.
int CreateContextGroup();
V8_NODISCARD bool CreateContext(int context_group_id,
v8_inspector::StringView name);
void ResetContextGroup(int context_group_id);
v8::Local<v8::Context> GetDefaultContext(int context_group_id);
int GetContextGroupId(v8::Local<v8::Context> context);
void RegisterModule(v8::Local<v8::Context> context,
std::vector<uint16_t> name,
v8::ScriptCompiler::Source* source);
// Working with V8Inspector api.
base::Optional<int> ConnectSession(
int context_group_id, const v8_inspector::StringView& state,
std::unique_ptr<FrontendChannelImpl> channel, bool is_fully_trusted);
std::vector<uint8_t> DisconnectSession(int session_id,
TaskRunner* context_task_runner);
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 Stop(int session_id);
void SchedulePauseOnNextStatement(int context_group_id,
const v8_inspector::StringView& reason,
const v8_inspector::StringView& details);
void CancelPauseOnNextStatement(int context_group_id);
void AsyncTaskScheduled(const v8_inspector::StringView& name, void* task,
bool recurring);
void AsyncTaskStarted(void* task);
void AsyncTaskFinished(void* task);
v8_inspector::V8StackTraceId StoreCurrentStackTrace(
const v8_inspector::StringView& description);
void ExternalAsyncTaskStarted(const v8_inspector::V8StackTraceId& parent);
void ExternalAsyncTaskFinished(const v8_inspector::V8StackTraceId& parent);
void AddInspectedObject(int session_id, v8::Local<v8::Value> object);
// Test utilities.
void SetCurrentTimeMS(double time);
void SetMemoryInfo(v8::Local<v8::Value> memory_info);
void SetLogConsoleApiMessageCalls(bool log);
void SetLogMaxAsyncCallStackDepthChanged(bool log);
void SetAdditionalConsoleApi(v8_inspector::StringView api_script);
void SetMaxAsyncTaskStacksForTest(int limit);
void DumpAsyncTaskStacksStateForTest();
void FireContextCreated(v8::Local<v8::Context> context, int context_group_id,
v8_inspector::StringView name);
void FireContextDestroyed(v8::Local<v8::Context> context);
void FreeContext(v8::Local<v8::Context> context);
void SetResourceNamePrefix(v8::Local<v8::String> prefix);
bool AssociateExceptionData(v8::Local<v8::Value> exception,
v8::Local<v8::Name> key,
v8::Local<v8::Value> value);
void WaitForDebugger(int context_group_id);
private:
static v8::MaybeLocal<v8::Module> ModuleResolveCallback(
v8::Local<v8::Context> context, v8::Local<v8::String> specifier,
v8::Local<v8::FixedArray> import_assertions,
v8::Local<v8::Module> referrer);
static void MessageHandler(v8::Local<v8::Message> message,
v8::Local<v8::Value> exception);
static void PromiseRejectHandler(v8::PromiseRejectMessage data);
static int HandleMessage(v8::Local<v8::Message> message,
v8::Local<v8::Value> exception);
std::vector<int> GetSessionIds(int context_group_id);
// V8InspectorClient implementation.
v8::Local<v8::Context> ensureDefaultContextInGroup(
int context_group_id) override;
double currentTimeMS() override;
v8::MaybeLocal<v8::Value> memoryInfo(v8::Isolate* isolate,
v8::Local<v8::Context>) override;
void runMessageLoopOnPause(int context_group_id) override;
void runIfWaitingForDebugger(int context_group_id) override;
void quitMessageLoopOnPause() override;
void installAdditionalCommandLineAPI(v8::Local<v8::Context>,
v8::Local<v8::Object>) override;
void consoleAPIMessage(int contextGroupId,
v8::Isolate::MessageErrorLevel level,
const v8_inspector::StringView& message,
const v8_inspector::StringView& url,
unsigned lineNumber, unsigned columnNumber,
v8_inspector::V8StackTrace*) override;
bool isInspectableHeapObject(v8::Local<v8::Object>) override;
void maxAsyncCallStackDepthChanged(int depth) override;
std::unique_ptr<v8_inspector::StringBuffer> resourceNameToUrl(
const v8_inspector::StringView& resourceName) override;
int64_t generateUniqueId() override;
// The isolate gets deleted by its {Dispose} method, not by the default
// deleter. Therefore we have to define a custom deleter for the unique_ptr to
// call {Dispose}. We have to use the unique_ptr so that the isolate get
// disposed in the right order, relative to other member variables.
struct IsolateDeleter {
void operator()(v8::Isolate* isolate) const {
// Exit the isolate after it was entered by ~InspectorIsolateData.
isolate->Exit();
isolate->Dispose();
}
};
TaskRunner* task_runner_;
SetupGlobalTasks setup_global_tasks_;
std::unique_ptr<v8::ArrayBuffer::Allocator> array_buffer_allocator_;
std::unique_ptr<v8::Isolate, IsolateDeleter> isolate_;
// The locker_ field has to come after isolate_ because the locker has to
// outlive the isolate.
base::Optional<v8::Locker> locker_;
std::unique_ptr<v8_inspector::V8Inspector> inspector_;
int last_context_group_id_ = 0;
std::map<int, std::vector<v8::Global<v8::Context>>> contexts_;
std::map<std::vector<uint16_t>, v8::Global<v8::Module>> modules_;
int last_session_id_ = 0;
std::map<int, std::unique_ptr<v8_inspector::V8InspectorSession>> sessions_;
std::map<v8_inspector::V8InspectorSession*, int> context_group_by_session_;
std::set<int> session_ids_for_cleanup_;
v8::Global<v8::Value> memory_info_;
bool current_time_set_ = false;
double current_time_ = 0.0;
bool log_console_api_message_calls_ = false;
bool log_max_async_call_stack_depth_changed_ = false;
bool waiting_for_debugger_ = false;
v8::Global<v8::Private> not_inspectable_private_;
v8::Global<v8::String> resource_name_prefix_;
v8::Global<v8::String> additional_console_api_;
};
// Stores all the channels.
//
// `InspectorIsolateData` is per isolate and a channel connects
// the backend Isolate with the frontend Isolate. The backend registers and
// sets up the isolate, but the frontend needs it to send responses and
// notifications. This is why we use a separate "class" (just a static wrapper
// around std::map).
class ChannelHolder {
public:
static void AddChannel(int session_id,
std::unique_ptr<FrontendChannelImpl> channel);
static FrontendChannelImpl* GetChannel(int session_id);
static void RemoveChannel(int session_id);
private:
static std::map<int, std::unique_ptr<FrontendChannelImpl>> channels_;
};
} // namespace internal
} // namespace v8
#endif // V8_TEST_INSPECTOR_PROTOCOL_ISOLATE_DATA_H_