// 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. #include "test/inspector/inspector-impl.h" #include "include/v8.h" #include "src/vector.h" namespace { const int kInspectorClientIndex = v8::Context::kDebugIdIndex + 1; class ChannelImpl final : public v8_inspector::V8Inspector::Channel { public: explicit ChannelImpl(InspectorClientImpl::FrontendChannel* frontend_channel) : frontend_channel_(frontend_channel) {} virtual ~ChannelImpl() = default; private: void sendResponse( int callId, std::unique_ptr message) override { frontend_channel_->SendMessageToFrontend(message->string()); } void sendNotification( std::unique_ptr message) override { frontend_channel_->SendMessageToFrontend(message->string()); } void flushProtocolNotifications() override {} InspectorClientImpl::FrontendChannel* frontend_channel_; DISALLOW_COPY_AND_ASSIGN(ChannelImpl); }; InspectorClientImpl* InspectorClientFromContext( v8::Local context) { InspectorClientImpl* inspector_client = static_cast( context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex)); CHECK(inspector_client); return inspector_client; } v8::internal::Vector ToVector(v8::Local str) { v8::internal::Vector buffer = v8::internal::Vector::New(str->Length()); str->Write(buffer.start(), 0, str->Length()); return buffer; } void MessageHandler(v8::Local message, v8::Local exception) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Local context = isolate->GetEnteredContext(); if (context.IsEmpty()) return; v8_inspector::V8Inspector* inspector = InspectorClientImpl::InspectorFromContext(context); v8::Local stack = message->GetStackTrace(); int script_id = static_cast(message->GetScriptOrigin().ScriptID()->Value()); if (!stack.IsEmpty() && stack->GetFrameCount() > 0) { int top_script_id = stack->GetFrame(0)->GetScriptId(); if (top_script_id == script_id) script_id = 0; } int line_number = message->GetLineNumber(context).FromMaybe(0); int column_number = 0; if (message->GetStartColumn(context).IsJust()) column_number = message->GetStartColumn(context).FromJust() + 1; v8_inspector::StringView detailed_message; v8::internal::Vector message_text_string = ToVector(message->Get()); v8_inspector::StringView message_text(message_text_string.start(), message_text_string.length()); v8::internal::Vector url_string; if (message->GetScriptOrigin().ResourceName()->IsString()) { url_string = ToVector(message->GetScriptOrigin().ResourceName().As()); } v8_inspector::StringView url(url_string.start(), url_string.length()); inspector->exceptionThrown(context, message_text, exception, detailed_message, url, line_number, column_number, inspector->createStackTrace(stack), script_id); } } // 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; } void Run(v8::Isolate* isolate, const v8::Global& global_context) { v8::HandleScope handle_scope(isolate); v8::Local context = global_context.Get(isolate); client_->connect(context); if (ready_semaphore_) ready_semaphore_->Signal(); } private: InspectorClientImpl* client_; v8::base::Semaphore* ready_semaphore_; }; class DisconnectTask : public TaskRunner::Task { public: explicit DisconnectTask(InspectorClientImpl* client) : client_(client) {} virtual ~DisconnectTask() = default; bool is_inspector_task() final { return true; } void Run(v8::Isolate* isolate, const v8::Global& global_context) { client_->disconnect(); } private: InspectorClientImpl* client_; }; class CreateContextGroupTask : public TaskRunner::Task { public: CreateContextGroupTask(InspectorClientImpl* client, v8::ExtensionConfiguration* extensions, v8::base::Semaphore* ready_semaphore, int* context_group_id) : client_(client), extensions_(extensions), ready_semaphore_(ready_semaphore), context_group_id_(context_group_id) {} virtual ~CreateContextGroupTask() = default; bool is_inspector_task() final { return true; } void Run(v8::Isolate* isolate, const v8::Global& global_context) { *context_group_id_ = client_->createContextGroup(extensions_); if (ready_semaphore_) ready_semaphore_->Signal(); } private: InspectorClientImpl* client_; v8::ExtensionConfiguration* extensions_; 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), frontend_channel_(frontend_channel) { task_runner_->Append(new ConnectTask(this, ready_semaphore)); } InspectorClientImpl::~InspectorClientImpl() {} void InspectorClientImpl::connect(v8::Local context) { isolate_ = context->GetIsolate(); isolate_->AddMessageListener(MessageHandler); channel_.reset(new ChannelImpl(frontend_channel_)); inspector_ = v8_inspector::V8Inspector::create(isolate_, this); if (states_.empty()) { int context_group_id = TaskRunner::GetContextGroupId(context); v8_inspector::StringView state; sessions_[context_group_id] = inspector_->connect(context_group_id, channel_.get(), state); context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this); inspector_->contextCreated(v8_inspector::V8ContextInfo( context, context_group_id, v8_inspector::StringView())); } else { for (const auto& it : states_) { int context_group_id = it.first; v8::Local context = task_runner_->GetContext(context_group_id); v8_inspector::StringView state = it.second->string(); sessions_[context_group_id] = inspector_->connect(context_group_id, channel_.get(), state); context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this); inspector_->contextCreated(v8_inspector::V8ContextInfo( context, context_group_id, v8_inspector::StringView())); } } states_.clear(); } void InspectorClientImpl::scheduleReconnect( v8::base::Semaphore* ready_semaphore) { task_runner_->Append(new DisconnectTask(this)); task_runner_->Append(new ConnectTask(this, ready_semaphore)); } void InspectorClientImpl::disconnect() { for (const auto& it : sessions_) { states_[it.first] = it.second->stateJSON(); } sessions_.clear(); } void InspectorClientImpl::scheduleCreateContextGroup( v8::ExtensionConfiguration* extensions, v8::base::Semaphore* ready_semaphore, int* context_group_id) { task_runner_->Append(new CreateContextGroupTask( this, extensions, ready_semaphore, context_group_id)); } int InspectorClientImpl::createContextGroup( v8::ExtensionConfiguration* extensions) { v8::HandleScope handle_scope(isolate_); v8::Local context = task_runner_->NewContextGroup(); context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this); int context_group_id = TaskRunner::GetContextGroupId(context); 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; } bool InspectorClientImpl::formatAccessorsAsProperties( v8::Local object) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Local context = isolate->GetCurrentContext(); v8::Local shouldFormatAccessorsPrivate = v8::Private::ForApi( isolate, v8::String::NewFromUtf8(isolate, "allowAccessorFormatting", v8::NewStringType::kNormal) .ToLocalChecked()); CHECK(object->IsObject()); return object.As() ->HasPrivate(context, shouldFormatAccessorsPrivate) .FromMaybe(false); } v8::Local InspectorClientImpl::ensureDefaultContextInGroup( int context_group_id) { CHECK(isolate_); return task_runner_->GetContext(context_group_id); } void InspectorClientImpl::setCurrentTimeMSForTest(double time) { current_time_ = time; current_time_set_for_test_ = true; } double InspectorClientImpl::currentTimeMS() { if (current_time_set_for_test_) return current_time_; return v8::base::OS::TimeCurrentMillis(); } void InspectorClientImpl::runMessageLoopOnPause(int) { task_runner_->RunMessageLoop(true); } void InspectorClientImpl::quitMessageLoopOnPause() { task_runner_->QuitMessageLoop(); } v8_inspector::V8Inspector* InspectorClientImpl::InspectorFromContext( v8::Local context) { return InspectorClientFromContext(context)->inspector_.get(); } v8_inspector::V8InspectorSession* InspectorClientImpl::SessionFromContext( v8::Local context) { int context_group_id = TaskRunner::GetContextGroupId(context); return InspectorClientFromContext(context)->sessions_[context_group_id].get(); } v8_inspector::V8InspectorSession* InspectorClientImpl::session( int context_group_id) { if (context_group_id) { return sessions_[context_group_id].get(); } else { return sessions_.begin()->second.get(); } } class SendMessageToBackendTask : public TaskRunner::Task { public: explicit SendMessageToBackendTask( const v8::internal::Vector& message, int context_group_id) : message_(message), context_group_id_(context_group_id) {} bool is_inspector_task() final { return true; } void Run(v8::Isolate* isolate, const v8::Global& global_context) override { v8_inspector::V8InspectorSession* session = nullptr; { v8::HandleScope handle_scope(isolate); v8::Local context = global_context.Get(isolate); if (!context_group_id_) { session = InspectorClientImpl::SessionFromContext(context); } else { session = InspectorClientFromContext(context) ->sessions_[context_group_id_] .get(); } CHECK(session); } v8_inspector::StringView message_view(message_.start(), message_.length()); session->dispatchProtocolMessage(message_view); } private: v8::internal::Vector message_; int context_group_id_; }; TaskRunner* SendMessageToBackendExtension::backend_task_runner_ = nullptr; v8::Local SendMessageToBackendExtension::GetNativeFunctionTemplate( v8::Isolate* isolate, v8::Local name) { return v8::FunctionTemplate::New( isolate, SendMessageToBackendExtension::SendMessageToBackend); } void SendMessageToBackendExtension::SendMessageToBackend( const v8::FunctionCallbackInfo& args) { CHECK(backend_task_runner_); CHECK(args.Length() == 2 && args[0]->IsString() && args[1]->IsInt32()); v8::Local message = args[0].As(); backend_task_runner_->Append(new SendMessageToBackendTask( ToVector(message), args[1].As()->Value())); }