// 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. #ifndef V8_V8_INSPECTOR_H_ #define V8_V8_INSPECTOR_H_ #include #include #include #include "v8-isolate.h" // NOLINT(build/include_directory) #include "v8-local-handle.h" // NOLINT(build/include_directory) namespace v8 { class Context; class Name; class Object; class StackTrace; class Value; } // namespace v8 namespace v8_inspector { namespace internal { class V8DebuggerId; } // namespace internal namespace protocol { namespace Debugger { namespace API { class SearchMatch; } } // namespace Debugger namespace Runtime { namespace API { class RemoteObject; class StackTrace; class StackTraceId; } // namespace API } // namespace Runtime namespace Schema { namespace API { class Domain; } } // namespace Schema } // namespace protocol class V8_EXPORT StringView { public: StringView() : m_is8Bit(true), m_length(0), m_characters8(nullptr) {} StringView(const uint8_t* characters, size_t length) : m_is8Bit(true), m_length(length), m_characters8(characters) {} StringView(const uint16_t* characters, size_t length) : m_is8Bit(false), m_length(length), m_characters16(characters) {} bool is8Bit() const { return m_is8Bit; } size_t length() const { return m_length; } // TODO(dgozman): add DCHECK(m_is8Bit) to accessors once platform can be used // here. const uint8_t* characters8() const { return m_characters8; } const uint16_t* characters16() const { return m_characters16; } private: bool m_is8Bit; size_t m_length; union { const uint8_t* m_characters8; const uint16_t* m_characters16; }; }; class V8_EXPORT StringBuffer { public: virtual ~StringBuffer() = default; virtual StringView string() const = 0; // This method copies contents. static std::unique_ptr create(StringView); }; class V8_EXPORT V8ContextInfo { public: V8ContextInfo(v8::Local context, int contextGroupId, StringView humanReadableName) : context(context), contextGroupId(contextGroupId), humanReadableName(humanReadableName), hasMemoryOnConsole(false) {} v8::Local context; // Each v8::Context is a part of a group. The group id must be non-zero. int contextGroupId; StringView humanReadableName; StringView origin; StringView auxData; bool hasMemoryOnConsole; static int executionContextId(v8::Local context); // Disallow copying and allocating this one. enum NotNullTagEnum { NotNullLiteral }; void* operator new(size_t) = delete; void* operator new(size_t, NotNullTagEnum, void*) = delete; void* operator new(size_t, void*) = delete; V8ContextInfo(const V8ContextInfo&) = delete; V8ContextInfo& operator=(const V8ContextInfo&) = delete; }; // This debugger id tries to be unique by generating two random // numbers, which should most likely avoid collisions. // Debugger id has a 1:1 mapping to context group. It is used to // attribute stack traces to a particular debugging, when doing any // cross-debugger operations (e.g. async step in). // See also Runtime.UniqueDebuggerId in the protocol. class V8_EXPORT V8DebuggerId { public: V8DebuggerId() = default; V8DebuggerId(const V8DebuggerId&) = default; V8DebuggerId& operator=(const V8DebuggerId&) = default; std::unique_ptr toString() const; bool isValid() const; std::pair pair() const; private: friend class internal::V8DebuggerId; explicit V8DebuggerId(std::pair); int64_t m_first = 0; int64_t m_second = 0; }; struct V8_EXPORT V8StackFrame { StringView sourceURL; StringView functionName; int lineNumber; int columnNumber; }; class V8_EXPORT V8StackTrace { public: virtual StringView firstNonEmptySourceURL() const = 0; virtual bool isEmpty() const = 0; virtual StringView topSourceURL() const = 0; virtual int topLineNumber() const = 0; virtual int topColumnNumber() const = 0; virtual int topScriptId() const = 0; virtual StringView topFunctionName() const = 0; virtual ~V8StackTrace() = default; virtual std::unique_ptr buildInspectorObject(int maxAsyncDepth) const = 0; virtual std::unique_ptr toString() const = 0; // Safe to pass between threads, drops async chain. virtual std::unique_ptr clone() = 0; virtual std::vector frames() const = 0; }; class V8_EXPORT V8InspectorSession { public: virtual ~V8InspectorSession() = default; // Cross-context inspectable values (DOM nodes in different worlds, etc.). class V8_EXPORT Inspectable { public: virtual v8::Local get(v8::Local) = 0; virtual ~Inspectable() = default; }; virtual void addInspectedObject(std::unique_ptr) = 0; // Dispatching protocol messages. static bool canDispatchMethod(StringView method); virtual void dispatchProtocolMessage(StringView message) = 0; virtual std::vector state() = 0; virtual std::vector> supportedDomains() = 0; // Debugger actions. virtual void schedulePauseOnNextStatement(StringView breakReason, StringView breakDetails) = 0; virtual void cancelPauseOnNextStatement() = 0; virtual void breakProgram(StringView breakReason, StringView breakDetails) = 0; virtual void setSkipAllPauses(bool) = 0; virtual void resume(bool setTerminateOnResume = false) = 0; virtual void stepOver() = 0; virtual std::vector> searchInTextByLines(StringView text, StringView query, bool caseSensitive, bool isRegex) = 0; // Remote objects. virtual std::unique_ptr wrapObject( v8::Local, v8::Local, StringView groupName, bool generatePreview) = 0; virtual bool unwrapObject(std::unique_ptr* error, StringView objectId, v8::Local*, v8::Local*, std::unique_ptr* objectGroup) = 0; virtual void releaseObjectGroup(StringView) = 0; virtual void triggerPreciseCoverageDeltaUpdate(StringView occasion) = 0; struct V8_EXPORT EvaluateResult { enum class ResultType { kNotRun, kSuccess, kException, }; ResultType type; v8::Local value; }; // Evalaute 'expression' in the provided context. Does the same as // Runtime#evaluate under-the-hood but exposed on the C++ side. virtual EvaluateResult evaluate(v8::Local context, StringView expression, bool includeCommandLineAPI = false) = 0; // Prepare for shutdown (disables debugger pausing, etc.). virtual void stop() = 0; }; struct V8_EXPORT DeepSerializedValue { explicit DeepSerializedValue(std::unique_ptr type, v8::MaybeLocal value = {}) : type(std::move(type)), value(value) {} std::unique_ptr type; v8::MaybeLocal value; }; struct V8_EXPORT DeepSerializationResult { explicit DeepSerializationResult( std::unique_ptr serializedValue) : serializedValue(std::move(serializedValue)), isSuccess(true) {} explicit DeepSerializationResult(std::unique_ptr errorMessage) : errorMessage(std::move(errorMessage)), isSuccess(false) {} // Use std::variant when available. std::unique_ptr serializedValue; std::unique_ptr errorMessage; bool isSuccess; }; class V8_EXPORT V8InspectorClient { public: virtual ~V8InspectorClient() = default; virtual void runMessageLoopOnPause(int contextGroupId) {} virtual void runMessageLoopOnInstrumentationPause(int contextGroupId) { runMessageLoopOnPause(contextGroupId); } virtual void quitMessageLoopOnPause() {} virtual void runIfWaitingForDebugger(int contextGroupId) {} virtual void muteMetrics(int contextGroupId) {} virtual void unmuteMetrics(int contextGroupId) {} virtual void beginUserGesture() {} virtual void endUserGesture() {} virtual std::unique_ptr deepSerialize( v8::Local v8Value, int maxDepth, v8::Local additionalParameters) { return nullptr; } virtual std::unique_ptr valueSubtype(v8::Local) { return nullptr; } virtual std::unique_ptr descriptionForValueSubtype( v8::Local, v8::Local) { return nullptr; } virtual bool isInspectableHeapObject(v8::Local) { return true; } virtual v8::Local ensureDefaultContextInGroup( int contextGroupId) { return v8::Local(); } virtual void beginEnsureAllContextsInGroup(int contextGroupId) {} virtual void endEnsureAllContextsInGroup(int contextGroupId) {} virtual void installAdditionalCommandLineAPI(v8::Local, v8::Local) {} virtual void consoleAPIMessage(int contextGroupId, v8::Isolate::MessageErrorLevel level, const StringView& message, const StringView& url, unsigned lineNumber, unsigned columnNumber, V8StackTrace*) {} virtual v8::MaybeLocal memoryInfo(v8::Isolate*, v8::Local) { return v8::MaybeLocal(); } virtual void consoleTime(v8::Isolate* isolate, v8::Local label); virtual void consoleTimeEnd(v8::Isolate* isolate, v8::Local label); virtual void consoleTimeStamp(v8::Isolate* isolate, v8::Local label); virtual void consoleClear(int contextGroupId) {} virtual double currentTimeMS() { return 0; } typedef void (*TimerCallback)(void*); virtual void startRepeatingTimer(double, TimerCallback, void* data) {} virtual void cancelTimer(void* data) {} // TODO(dgozman): this was added to support service worker shadow page. We // should not connect at all. virtual bool canExecuteScripts(int contextGroupId) { return true; } virtual void maxAsyncCallStackDepthChanged(int depth) {} virtual std::unique_ptr resourceNameToUrl( const StringView& resourceName) { return nullptr; } // The caller would defer to generating a random 64 bit integer if // this method returns 0. virtual int64_t generateUniqueId() { return 0; } virtual void dispatchError(v8::Local, v8::Local, v8::Local) {} }; // These stack trace ids are intended to be passed between debuggers and be // resolved later. This allows to track cross-debugger calls and step between // them if a single client connects to multiple debuggers. struct V8_EXPORT V8StackTraceId { uintptr_t id; std::pair debugger_id; bool should_pause = false; V8StackTraceId(); V8StackTraceId(const V8StackTraceId&) = default; V8StackTraceId(uintptr_t id, const std::pair debugger_id); V8StackTraceId(uintptr_t id, const std::pair debugger_id, bool should_pause); explicit V8StackTraceId(StringView); V8StackTraceId& operator=(const V8StackTraceId&) = default; V8StackTraceId& operator=(V8StackTraceId&&) noexcept = default; ~V8StackTraceId() = default; bool IsInvalid() const; std::unique_ptr ToString(); }; class V8_EXPORT V8Inspector { public: static std::unique_ptr create(v8::Isolate*, V8InspectorClient*); virtual ~V8Inspector() = default; // Contexts instrumentation. virtual void contextCreated(const V8ContextInfo&) = 0; virtual void contextDestroyed(v8::Local) = 0; virtual void resetContextGroup(int contextGroupId) = 0; virtual v8::MaybeLocal contextById(int contextId) = 0; virtual V8DebuggerId uniqueDebuggerId(int contextId) = 0; // Various instrumentation. virtual void idleStarted() = 0; virtual void idleFinished() = 0; // Async stack traces instrumentation. virtual void asyncTaskScheduled(StringView taskName, void* task, bool recurring) = 0; virtual void asyncTaskCanceled(void* task) = 0; virtual void asyncTaskStarted(void* task) = 0; virtual void asyncTaskFinished(void* task) = 0; virtual void allAsyncTasksCanceled() = 0; virtual V8StackTraceId storeCurrentStackTrace(StringView description) = 0; virtual void externalAsyncTaskStarted(const V8StackTraceId& parent) = 0; virtual void externalAsyncTaskFinished(const V8StackTraceId& parent) = 0; // Exceptions instrumentation. virtual unsigned exceptionThrown(v8::Local, StringView message, v8::Local exception, StringView detailedMessage, StringView url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr, int scriptId) = 0; virtual void exceptionRevoked(v8::Local, unsigned exceptionId, StringView message) = 0; virtual bool associateExceptionData(v8::Local, v8::Local exception, v8::Local key, v8::Local value) = 0; // Connection. class V8_EXPORT Channel { public: virtual ~Channel() = default; virtual void sendResponse(int callId, std::unique_ptr message) = 0; virtual void sendNotification(std::unique_ptr message) = 0; virtual void flushProtocolNotifications() = 0; }; enum ClientTrustLevel { kUntrusted, kFullyTrusted }; enum SessionPauseState { kWaitingForDebugger, kNotWaitingForDebugger }; // TODO(chromium:1352175): remove default value once downstream change lands. virtual std::unique_ptr connect( int contextGroupId, Channel*, StringView state, ClientTrustLevel client_trust_level, SessionPauseState = kNotWaitingForDebugger) { return nullptr; } // API methods. virtual std::unique_ptr createStackTrace( v8::Local) = 0; virtual std::unique_ptr captureStackTrace(bool fullStack) = 0; }; } // namespace v8_inspector #endif // V8_V8_INSPECTOR_H_