// 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 #endif // !defined(_WIN32) && !defined(_WIN64) #include #include #include #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 task_runners; class UtilsExtension : public InspectorIsolateData::SetupGlobalTask { public: ~UtilsExtension() override = default; void Run(v8::Isolate* isolate, v8::Local global) override { v8::Local 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; } private: static TaskRunner* backend_runner_; static void Print(const v8::FunctionCallbackInfo& info) { for (int i = 0; i < info.Length(); i++) { v8::HandleScope handle_scope(info.GetIsolate()); if (i != 0) { printf(" "); } // Explicitly catch potential exceptions in toString(). v8::TryCatch try_catch(info.GetIsolate()); v8::Local arg = info[i]; v8::Local str_obj; if (arg->IsSymbol()) { arg = v8::Local::Cast(arg)->Description(info.GetIsolate()); } if (!arg->ToString(info.GetIsolate()->GetCurrentContext()) .ToLocal(&str_obj)) { try_catch.ReThrow(); return; } v8::String::Utf8Value str(info.GetIsolate(), str_obj); int n = static_cast(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& info) { 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& info) { if (info.Length() != 1 || !info[0]->IsString()) { FATAL("Internal error: setlocale get one string argument."); } v8::String::Utf8Value str(info.GetIsolate(), info[1]); setlocale(LC_NUMERIC, *str); } static bool ReadFile(v8::Isolate* isolate, v8::Local 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& info) { if (info.Length() != 1 || !info[0]->IsString()) { FATAL("Internal error: read gets one string argument."); } std::string chars; v8::Isolate* isolate = info.GetIsolate(); if (ReadFile(isolate, info[0], &chars)) { info.GetReturnValue().Set(ToV8String(isolate, chars)); } } static void Load(const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsString()) { FATAL("Internal error: load gets one string argument."); } std::string chars; v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); int context_group_id = data->GetContextGroupId(context); if (ReadFile(isolate, info[0], &chars)) { ExecuteStringTask(chars, context_group_id).Run(data); } } static void CompileAndRunWithOrigin( const v8::FunctionCallbackInfo& info) { if (info.Length() != 6 || !info[0]->IsInt32() || !info[1]->IsString() || !info[2]->IsString() || !info[3]->IsInt32() || !info[4]->IsInt32() || !info[5]->IsBoolean()) { FATAL( "Internal error: compileAndRunWithOrigin(context_group_id, source, " "name, line, column, is_module)."); } backend_runner_->Append(std::make_unique( info.GetIsolate(), info[0].As()->Value(), ToVector(info.GetIsolate(), info[1].As()), info[2].As(), info[3].As(), info[4].As(), info[5].As())); } static void SetCurrentTimeMSForTest( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsNumber()) { FATAL("Internal error: setCurrentTimeMSForTest(time)."); } backend_runner_->data()->SetCurrentTimeMS( info[0].As()->Value()); } static void SetMemoryInfoForTest( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1) { FATAL("Internal error: setMemoryInfoForTest(value)."); } backend_runner_->data()->SetMemoryInfo(info[0]); } static void SchedulePauseOnNextStatement( const v8::FunctionCallbackInfo& info) { if (info.Length() != 3 || !info[0]->IsInt32() || !info[1]->IsString() || !info[2]->IsString()) { FATAL( "Internal error: schedulePauseOnNextStatement(context_group_id, " "'reason', 'details')."); } std::vector reason = ToVector(info.GetIsolate(), info[1].As()); std::vector details = ToVector(info.GetIsolate(), info[2].As()); int context_group_id = info[0].As()->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& info) { if (info.Length() != 1 || !info[0]->IsInt32()) { FATAL("Internal error: cancelPauseOnNextStatement(context_group_id)."); } int context_group_id = info[0].As()->Value(); RunSyncTask(backend_runner_, [&context_group_id](InspectorIsolateData* data) { data->CancelPauseOnNextStatement(context_group_id); }); } static void Stop(const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsInt32()) { FATAL("Internal error: stop(session_id)."); } int session_id = info[0].As()->Value(); RunSyncTask(backend_runner_, [&session_id](InspectorIsolateData* data) { data->Stop(session_id); }); } static void SetLogConsoleApiMessageCalls( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsBoolean()) { FATAL("Internal error: setLogConsoleApiMessageCalls(bool)."); } backend_runner_->data()->SetLogConsoleApiMessageCalls( info[0].As()->Value()); } static void SetLogMaxAsyncCallStackDepthChanged( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsBoolean()) { FATAL("Internal error: setLogMaxAsyncCallStackDepthChanged(bool)."); } backend_runner_->data()->SetLogMaxAsyncCallStackDepthChanged( info[0].As()->Value()); } static void SetAdditionalConsoleApi( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsString()) { FATAL("Internal error: SetAdditionalConsoleApi(string)."); } std::vector script = ToVector(info.GetIsolate(), info[0].As()); RunSyncTask(backend_runner_, [&script](InspectorIsolateData* data) { data->SetAdditionalConsoleApi( v8_inspector::StringView(script.data(), script.size())); }); } static void CreateContextGroup( const v8::FunctionCallbackInfo& info) { if (info.Length() != 0) { FATAL("Internal error: createContextGroup()."); } int context_group_id = 0; RunSyncTask(backend_runner_, [&context_group_id](InspectorIsolateData* data) { context_group_id = data->CreateContextGroup(); }); info.GetReturnValue().Set( v8::Int32::New(info.GetIsolate(), context_group_id)); } static void CreateContext(const v8::FunctionCallbackInfo& info) { if (info.Length() != 2) { FATAL("Internal error: createContext(context, name)."); } int context_group_id = info[0].As()->Value(); std::vector name = ToVector(info.GetIsolate(), info[1].As()); 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& info) { if (info.Length() != 1 || !info[0]->IsInt32()) { FATAL("Internal error: resetContextGroup(context_group_id)."); } int context_group_id = info[0].As()->Value(); RunSyncTask(backend_runner_, [&context_group_id](InspectorIsolateData* data) { data->ResetContextGroup(context_group_id); }); } static void ConnectSession(const v8::FunctionCallbackInfo& info) { if (info.Length() != 3 || !info[0]->IsInt32() || !info[1]->IsString() || !info[2]->IsFunction()) { FATAL( "Internal error: connectionSession(context_group_id, state, " "dispatch)."); } v8::Local context = info.GetIsolate()->GetCurrentContext(); std::unique_ptr channel = std::make_unique( InspectorIsolateData::FromContext(context)->task_runner(), InspectorIsolateData::FromContext(context)->GetContextGroupId( context), info.GetIsolate(), info[2].As()); std::vector state = ToBytes(info.GetIsolate(), info[1].As()); int context_group_id = info[0].As()->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()), std::move(channel)); }); info.GetReturnValue().Set(v8::Int32::New(info.GetIsolate(), session_id)); } static void DisconnectSession( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsInt32()) { FATAL("Internal error: disconnectionSession(session_id)."); } v8::Local context = info.GetIsolate()->GetCurrentContext(); TaskRunner* context_task_runner = InspectorIsolateData::FromContext(context)->task_runner(); int session_id = info[0].As()->Value(); std::vector state; RunSyncTask(backend_runner_, [&session_id, &context_task_runner, &state](InspectorIsolateData* data) { state = data->DisconnectSession(session_id, context_task_runner); }); info.GetReturnValue().Set(ToV8String(info.GetIsolate(), state)); } static void SendMessageToBackend( const v8::FunctionCallbackInfo& info) { if (info.Length() != 2 || !info[0]->IsInt32() || !info[1]->IsString()) { FATAL("Internal error: sendMessageToBackend(session_id, message)."); } backend_runner_->Append(std::make_unique( info[0].As()->Value(), ToVector(info.GetIsolate(), info[1].As()))); } static void InterruptForMessages( const v8::FunctionCallbackInfo& info) { backend_runner_->InterruptForMessages(); } static void WaitForDebugger(const v8::FunctionCallbackInfo& info) { if (info.Length() != 2 || !info[0]->IsInt32() || !info[1]->IsFunction()) { FATAL("Internal error: waitForDebugger(context_group_id, callback)."); } int context_group_id = info[0].As()->Value(); RunSimpleAsyncTask( backend_runner_, [context_group_id](InspectorIsolateData* data) { data->WaitForDebugger(context_group_id); }, info[1].As()); } }; TaskRunner* UtilsExtension::backend_runner_ = nullptr; bool StrictAccessCheck(v8::Local accessing_context, v8::Local accessed_object, v8::Local 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 global) override { v8::Local name = v8::String::NewFromUtf8Literal(isolate, "console"); global->SetAccessor(name, &ConsoleGetterCallback, nullptr, {}, v8::DEFAULT, v8::DontEnum); } private: static void ConsoleGetterCallback( v8::Local, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); v8::HandleScope scope(isolate); v8::Local context = isolate->GetCurrentContext(); v8::Local name = v8::String::NewFromUtf8Literal(isolate, "console"); v8::Local console = context->GetExtrasBindingObject() ->Get(context, name) .ToLocalChecked() .As(); info.GetReturnValue().Set(console); } }; class InspectorExtension : public InspectorIsolateData::SetupGlobalTask { public: ~InspectorExtension() override = default; void Run(v8::Isolate* isolate, v8::Local global) override { v8::Local 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& info) { v8::Local context = info.GetIsolate()->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); data->FireContextCreated(context, data->GetContextGroupId(context), v8_inspector::StringView()); } static void FireContextDestroyed( const v8::FunctionCallbackInfo& info) { v8::Local context = info.GetIsolate()->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); data->FireContextDestroyed(context); } static void FreeContext(const v8::FunctionCallbackInfo& info) { v8::Local context = info.GetIsolate()->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); data->FreeContext(context); } static void AddInspectedObject( const v8::FunctionCallbackInfo& info) { if (info.Length() != 2 || !info[0]->IsInt32()) { FATAL("Internal error: addInspectedObject(session_id, object)."); } v8::Local context = info.GetIsolate()->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); data->AddInspectedObject(info[0].As()->Value(), info[1]); } static void SetMaxAsyncTaskStacks( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsInt32()) { FATAL("Internal error: setMaxAsyncTaskStacks(max)."); } InspectorIsolateData::FromContext(info.GetIsolate()->GetCurrentContext()) ->SetMaxAsyncTaskStacksForTest(info[0].As()->Value()); } static void DumpAsyncTaskStacksStateForTest( const v8::FunctionCallbackInfo& info) { if (info.Length() != 0) { FATAL("Internal error: dumpAsyncTaskStacksStateForTest()."); } InspectorIsolateData::FromContext(info.GetIsolate()->GetCurrentContext()) ->DumpAsyncTaskStacksStateForTest(); } static void BreakProgram(const v8::FunctionCallbackInfo& info) { if (info.Length() != 2 || !info[0]->IsString() || !info[1]->IsString()) { FATAL("Internal error: breakProgram('reason', 'details')."); } v8::Local context = info.GetIsolate()->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); std::vector reason = ToVector(info.GetIsolate(), info[0].As()); v8_inspector::StringView reason_view(reason.data(), reason.size()); std::vector details = ToVector(info.GetIsolate(), info[1].As()); v8_inspector::StringView details_view(details.data(), details.size()); data->BreakProgram(data->GetContextGroupId(context), reason_view, details_view); } static void CreateObjectWithStrictCheck( const v8::FunctionCallbackInfo& info) { if (info.Length() != 0) { FATAL("Internal error: createObjectWithStrictCheck()."); } v8::Local templ = v8::ObjectTemplate::New(info.GetIsolate()); templ->SetAccessCheckCallback(&StrictAccessCheck); info.GetReturnValue().Set( templ->NewInstance(info.GetIsolate()->GetCurrentContext()) .ToLocalChecked()); } static void CallWithScheduledBreak( const v8::FunctionCallbackInfo& info) { if (info.Length() != 3 || !info[0]->IsFunction() || !info[1]->IsString() || !info[2]->IsString()) { FATAL("Internal error: callWithScheduledBreak('reason', 'details')."); } std::vector reason = ToVector(info.GetIsolate(), info[1].As()); v8_inspector::StringView reason_view(reason.data(), reason.size()); std::vector details = ToVector(info.GetIsolate(), info[2].As()); v8_inspector::StringView details_view(details.data(), details.size()); v8::Local context = info.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 result; result = info[0].As()->Call(context, context->Global(), 0, nullptr); data->CancelPauseOnNextStatement(context_group_id); } static void MarkObjectAsNotInspectable( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsObject()) { FATAL("Internal error: markObjectAsNotInspectable(object)."); } v8::Local object = info[0].As(); v8::Isolate* isolate = info.GetIsolate(); v8::Local notInspectablePrivate = v8::Private::ForApi(isolate, ToV8String(isolate, "notInspectable")); object ->SetPrivate(isolate->GetCurrentContext(), notInspectablePrivate, v8::True(isolate)) .ToChecked(); } static void CreateObjectWithAccessor( const v8::FunctionCallbackInfo& info) { if (info.Length() != 2 || !info[0]->IsString() || !info[1]->IsBoolean()) { FATAL( "Internal error: createObjectWithAccessor('accessor name', " "hasSetter)\n"); } v8::Isolate* isolate = info.GetIsolate(); v8::Local templ = v8::ObjectTemplate::New(isolate); if (info[1].As()->Value()) { templ->SetAccessor(v8::Local::Cast(info[0]), AccessorGetter, AccessorSetter); } else { templ->SetAccessor(v8::Local::Cast(info[0]), AccessorGetter); } info.GetReturnValue().Set( templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked()); } static void AccessorGetter(v8::Local property, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); isolate->ThrowError("Getter is called"); } static void AccessorSetter(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); isolate->ThrowError("Setter is called"); } static void StoreCurrentStackTrace( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsString()) { FATAL("Internal error: storeCurrentStackTrace('description')\n"); } v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); std::vector description = ToVector(isolate, info[0].As()); v8_inspector::StringView description_view(description.data(), description.size()); v8_inspector::V8StackTraceId id = data->StoreCurrentStackTrace(description_view); v8::Local buffer = v8::ArrayBuffer::New(isolate, sizeof(id)); *static_cast( buffer->GetBackingStore()->Data()) = id; info.GetReturnValue().Set(buffer); } static void ExternalAsyncTaskStarted( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsArrayBuffer()) { FATAL("Internal error: externalAsyncTaskStarted(id)\n"); } v8::Local context = info.GetIsolate()->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); v8_inspector::V8StackTraceId* id = static_cast( info[0].As()->GetBackingStore()->Data()); data->ExternalAsyncTaskStarted(*id); } static void ExternalAsyncTaskFinished( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsArrayBuffer()) { FATAL("Internal error: externalAsyncTaskFinished(id)\n"); } v8::Local context = info.GetIsolate()->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); v8_inspector::V8StackTraceId* id = static_cast( info[0].As()->GetBackingStore()->Data()); data->ExternalAsyncTaskFinished(*id); } static void ScheduleWithAsyncStack( const v8::FunctionCallbackInfo& info) { if (info.Length() != 3 || !info[0]->IsFunction() || !info[1]->IsString() || !info[2]->IsBoolean()) { FATAL( "Internal error: scheduleWithAsyncStack(function, 'task-name', " "with_empty_stack)."); } v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); int context_group_id = data->GetContextGroupId(context); bool with_empty_stack = info[2].As()->Value(); if (with_empty_stack) context->Exit(); std::vector task_name = ToVector(isolate, info[1].As()); v8_inspector::StringView task_name_view(task_name.data(), task_name.size()); RunAsyncTask( data->task_runner(), task_name_view, std::make_unique( context_group_id, isolate, v8::Local::Cast(info[0]))); if (with_empty_stack) context->Enter(); } static void SetAllowCodeGenerationFromStrings( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsBoolean()) { FATAL("Internal error: setAllowCodeGenerationFromStrings(allow)."); } info.GetIsolate()->GetCurrentContext()->AllowCodeGenerationFromStrings( info[0].As()->Value()); } static void SetResourceNamePrefix( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsString()) { FATAL("Internal error: setResourceNamePrefix('prefix')."); } v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); data->SetResourceNamePrefix(v8::Local::Cast(info[0])); } static void newExceptionWithMetaData( const v8::FunctionCallbackInfo& info) { if (info.Length() != 3 || !info[0]->IsString() || !info[1]->IsString() || !info[2]->IsString()) { FATAL( "Internal error: newExceptionWithMetaData('message', 'key', " "'value')."); } v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); InspectorIsolateData* data = InspectorIsolateData::FromContext(context); auto error = v8::Exception::Error(info[0].As()); CHECK(data->AssociateExceptionData(error, info[1].As(), info[2].As())); info.GetReturnValue().Set(error); } static void CallbackForTests( const v8::FunctionCallbackInfo& info) { if (info.Length() != 1 || !info[0]->IsFunction()) { FATAL("Internal error: callbackForTests(function)."); } v8::Isolate* isolate = info.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); v8::Local callback = v8::Local::Cast(info[0]); v8::MaybeLocal result = callback->Call(context, v8::Undefined(isolate), 0, nullptr); info.GetReturnValue().Set(result.ToLocalChecked()); } static void RunNestedMessageLoop( const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); v8::Local 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::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( chars, frontend_context_group_id)); } frontend_runner.Join(); backend_runner.Join(); 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); }