%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/inspector/
Upload File :
Create Path :
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/inspector/injected-script.cc

/*
 * Copyright (C) 2012 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "src/inspector/injected-script.h"

#include <cmath>
#include <memory>
#include <unordered_set>

#include "../../third_party/inspector_protocol/crdtp/json.h"
#include "include/v8-container.h"
#include "include/v8-context.h"
#include "include/v8-function.h"
#include "include/v8-inspector.h"
#include "include/v8-microtask-queue.h"
#include "src/debug/debug-interface.h"
#include "src/inspector/custom-preview.h"
#include "src/inspector/inspected-context.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/remote-object-id.h"
#include "src/inspector/string-util.h"
#include "src/inspector/v8-console.h"
#include "src/inspector/v8-debugger.h"
#include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-inspector-session-impl.h"
#include "src/inspector/v8-serialization-duplicate-tracker.h"
#include "src/inspector/v8-stack-trace-impl.h"
#include "src/inspector/v8-value-utils.h"
#include "src/inspector/value-mirror.h"

namespace v8_inspector {

namespace {
const char kGlobalHandleLabel[] = "DevTools console";
bool isResolvableNumberLike(String16 query) {
  return query == "Infinity" || query == "-Infinity" || query == "NaN";
}
}  // namespace

using protocol::Array;
using protocol::Maybe;
using protocol::Runtime::InternalPropertyDescriptor;
using protocol::Runtime::PrivatePropertyDescriptor;
using protocol::Runtime::PropertyDescriptor;
using protocol::Runtime::RemoteObject;

// static
void EvaluateCallback::sendSuccess(
    std::weak_ptr<EvaluateCallback> callback, InjectedScript* injectedScript,
    std::unique_ptr<protocol::Runtime::RemoteObject> result,
    protocol::Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails) {
  std::shared_ptr<EvaluateCallback> cb = callback.lock();
  if (!cb) return;
  injectedScript->deleteEvaluateCallback(cb);
  CHECK_EQ(cb.use_count(), 1);
  cb->sendSuccess(std::move(result), std::move(exceptionDetails));
}

// static
void EvaluateCallback::sendFailure(std::weak_ptr<EvaluateCallback> callback,
                                   InjectedScript* injectedScript,
                                   const protocol::DispatchResponse& response) {
  std::shared_ptr<EvaluateCallback> cb = callback.lock();
  if (!cb) return;
  injectedScript->deleteEvaluateCallback(cb);
  CHECK_EQ(cb.use_count(), 1);
  cb->sendFailure(response);
}

class InjectedScript::ProtocolPromiseHandler {
 public:
  static void add(V8InspectorSessionImpl* session,
                  v8::Local<v8::Context> context, v8::Local<v8::Value> value,
                  int executionContextId, const String16& objectGroup,
                  std::unique_ptr<WrapOptions> wrapOptions, bool replMode,
                  bool throwOnSideEffect,
                  std::weak_ptr<EvaluateCallback> callback) {
    InjectedScript::ContextScope scope(session, executionContextId);
    Response response = scope.initialize();
    if (!response.IsSuccess()) return;

    v8::Local<v8::Promise> promise;
    v8::Local<v8::Promise::Resolver> resolver;
    if (value->IsPromise()) {
      // If value is a promise, we can chain the handlers directly onto `value`.
      promise = value.As<v8::Promise>();
    } else {
      // Otherwise we do `Promise.resolve(value)`.
      CHECK(!replMode);
      if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) {
        EvaluateCallback::sendFailure(callback, scope.injectedScript(),
                                      Response::InternalError());
        return;
      }
      if (!resolver->Resolve(context, value).FromMaybe(false)) {
        EvaluateCallback::sendFailure(callback, scope.injectedScript(),
                                      Response::InternalError());
        return;
      }
      promise = resolver->GetPromise();
    }

    V8InspectorImpl* inspector = session->inspector();
    PromiseHandlerTracker::Id handlerId =
        inspector->promiseHandlerTracker().create(
            session, executionContextId, objectGroup, std::move(wrapOptions),
            replMode, throwOnSideEffect, callback, promise);
    v8::Local<v8::Number> data =
        v8::Number::New(inspector->isolate(), handlerId);
    v8::Local<v8::Function> thenCallbackFunction =
        v8::Function::New(context, thenCallback, data, 0,
                          v8::ConstructorBehavior::kThrow)
            .ToLocalChecked();
    v8::Local<v8::Function> catchCallbackFunction =
        v8::Function::New(context, catchCallback, data, 0,
                          v8::ConstructorBehavior::kThrow)
            .ToLocalChecked();

    if (promise->Then(context, thenCallbackFunction, catchCallbackFunction)
            .IsEmpty()) {
      // Re-initialize after returning from JS.
      Response response = scope.initialize();
      if (!response.IsSuccess()) return;
      EvaluateCallback::sendFailure(callback, scope.injectedScript(),
                                    Response::InternalError());
    }
  }

 private:
  friend class PromiseHandlerTracker;

  static v8::Local<v8::String> GetDotReplResultString(v8::Isolate* isolate) {
    // TODO(szuend): Cache the string in a v8::Persistent handle.
    return v8::String::NewFromOneByte(
               isolate, reinterpret_cast<const uint8_t*>(".repl_result"))
        .ToLocalChecked();
  }

  static void thenCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    PromiseHandlerTracker::Id handlerId =
        static_cast<PromiseHandlerTracker::Id>(
            info.Data().As<v8::Number>()->Value());
    PromiseHandlerTracker& handlerTracker =
        static_cast<V8InspectorImpl*>(
            v8::debug::GetInspector(info.GetIsolate()))
            ->promiseHandlerTracker();
    // We currently store the handlers with the inspector. In rare cases the
    // inspector dies (discarding the handler) with the micro task queue
    // running after. Don't do anything in that case.
    ProtocolPromiseHandler* handler = handlerTracker.get(handlerId);
    if (!handler) return;
    v8::Local<v8::Value> value =
        info.Length() > 0 ? info[0]
                          : v8::Undefined(info.GetIsolate()).As<v8::Value>();
    handler->thenCallback(value);
    handlerTracker.discard(handlerId,
                           PromiseHandlerTracker::DiscardReason::kFulfilled);
  }

  static void catchCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    PromiseHandlerTracker::Id handlerId =
        static_cast<PromiseHandlerTracker::Id>(
            info.Data().As<v8::Number>()->Value());
    PromiseHandlerTracker& handlerTracker =
        static_cast<V8InspectorImpl*>(
            v8::debug::GetInspector(info.GetIsolate()))
            ->promiseHandlerTracker();
    // We currently store the handlers with the inspector. In rare cases the
    // inspector dies (discarding the handler) with the micro task queue
    // running after. Don't do anything in that case.
    ProtocolPromiseHandler* handler = handlerTracker.get(handlerId);
    if (!handler) return;
    v8::Local<v8::Value> value =
        info.Length() > 0 ? info[0]
                          : v8::Undefined(info.GetIsolate()).As<v8::Value>();
    handler->catchCallback(value);
    handlerTracker.discard(handlerId,
                           PromiseHandlerTracker::DiscardReason::kFulfilled);
  }

  ProtocolPromiseHandler(PromiseHandlerTracker::Id id,
                         V8InspectorSessionImpl* session,
                         int executionContextId, const String16& objectGroup,
                         std::unique_ptr<WrapOptions> wrapOptions,
                         bool replMode, bool throwOnSideEffect,
                         std::weak_ptr<EvaluateCallback> callback,
                         v8::Local<v8::Promise> evaluationResult)
      : m_inspector(session->inspector()),
        m_sessionId(session->sessionId()),
        m_contextGroupId(session->contextGroupId()),
        m_executionContextId(executionContextId),
        m_objectGroup(objectGroup),
        m_wrapOptions(std::move(wrapOptions)),
        m_replMode(replMode),
        m_throwOnSideEffect(throwOnSideEffect),
        m_callback(std::move(callback)),
        m_evaluationResult(m_inspector->isolate(), evaluationResult) {
    m_evaluationResult.SetWeak(reinterpret_cast<PromiseHandlerTracker::Id*>(id),
                               cleanup, v8::WeakCallbackType::kParameter);
  }

  static void cleanup(
      const v8::WeakCallbackInfo<PromiseHandlerTracker::Id>& data) {
    auto id = reinterpret_cast<PromiseHandlerTracker::Id>(data.GetParameter());
    PromiseHandlerTracker& handlerTracker =
        static_cast<V8InspectorImpl*>(
            v8::debug::GetInspector(data.GetIsolate()))
            ->promiseHandlerTracker();
    // {discard} deletes the {ProtocolPromiseHandler} which resets the handle.
    handlerTracker.discard(
        id, PromiseHandlerTracker::DiscardReason::kPromiseCollected);
  }

  void thenCallback(v8::Local<v8::Value> value) {
    // We don't need the m_evaluationResult in the `thenCallback`, but we also
    // don't want `cleanup` running in case we re-enter JS.
    m_evaluationResult.Reset();
    V8InspectorSessionImpl* session =
        m_inspector->sessionById(m_contextGroupId, m_sessionId);
    if (!session) return;
    InjectedScript::ContextScope scope(session, m_executionContextId);
    Response response = scope.initialize();
    if (!response.IsSuccess()) return;

    // In REPL mode the result is additionally wrapped in an object.
    // The evaluation result can be found at ".repl_result".
    v8::Local<v8::Value> result = value;
    if (m_replMode) {
      v8::Local<v8::Object> object;
      if (!result->ToObject(scope.context()).ToLocal(&object)) {
        EvaluateCallback::sendFailure(m_callback, scope.injectedScript(),
                                      response);
        return;
      }

      v8::Local<v8::String> name =
          GetDotReplResultString(m_inspector->isolate());
      if (!object->Get(scope.context(), name).ToLocal(&result)) {
        EvaluateCallback::sendFailure(m_callback, scope.injectedScript(),
                                      response);
        return;
      }
    }

    if (m_objectGroup == "console") {
      scope.injectedScript()->setLastEvaluationResult(result);
    }

    std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue;
    response = scope.injectedScript()->wrapObject(
        result, m_objectGroup, *m_wrapOptions.get(), &wrappedValue);
    if (!response.IsSuccess()) {
      EvaluateCallback::sendFailure(m_callback, scope.injectedScript(),
                                    response);
      return;
    }
    EvaluateCallback::sendSuccess(m_callback, scope.injectedScript(),
                                  std::move(wrappedValue),
                                  Maybe<protocol::Runtime::ExceptionDetails>());
  }

  void catchCallback(v8::Local<v8::Value> result) {
    // Hold strongly onto m_evaluationResult now to prevent `cleanup` from
    // running in case any code below triggers GC.
    m_evaluationResult.ClearWeak();
    V8InspectorSessionImpl* session =
        m_inspector->sessionById(m_contextGroupId, m_sessionId);
    if (!session) return;
    InjectedScript::ContextScope scope(session, m_executionContextId);
    Response response = scope.initialize();
    if (!response.IsSuccess()) return;
    std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue;
    response = scope.injectedScript()->wrapObject(
        result, m_objectGroup, *m_wrapOptions.get(), &wrappedValue);
    if (!response.IsSuccess()) {
      EvaluateCallback::sendFailure(m_callback, scope.injectedScript(),
                                    response);
      return;
    }
    v8::Isolate* isolate = session->inspector()->isolate();

    v8::MaybeLocal<v8::Message> maybeMessage =
        m_evaluationResult.IsEmpty()
            ? v8::MaybeLocal<v8::Message>()
            : v8::debug::GetMessageFromPromise(m_evaluationResult.Get(isolate));
    v8::Local<v8::Message> message;
    // In case a MessageObject was attached to the rejected promise, we
    // construct the exception details from the message object. Otherwise
    // we try to capture a fresh stack trace.
    if (maybeMessage.ToLocal(&message)) {
      v8::Local<v8::Value> exception = result;
      if (!m_throwOnSideEffect) {
        session->inspector()->client()->dispatchError(scope.context(), message,
                                                      exception);
      }
      protocol::PtrMaybe<protocol::Runtime::ExceptionDetails> exceptionDetails;
      response = scope.injectedScript()->createExceptionDetails(
          message, exception, m_objectGroup, &exceptionDetails);
      if (!response.IsSuccess()) {
        EvaluateCallback::sendFailure(m_callback, scope.injectedScript(),
                                      response);
        return;
      }

      EvaluateCallback::sendSuccess(m_callback, scope.injectedScript(),
                                    std::move(wrappedValue),
                                    std::move(exceptionDetails));
      return;
    }

    String16 messageString;
    std::unique_ptr<V8StackTraceImpl> stack;
    if (result->IsNativeError()) {
      messageString =
          " " +
          toProtocolString(isolate,
                           result->ToDetailString(isolate->GetCurrentContext())
                               .ToLocalChecked());
      v8::Local<v8::StackTrace> stackTrace =
          v8::Exception::GetStackTrace(result);
      if (!stackTrace.IsEmpty()) {
        stack = m_inspector->debugger()->createStackTrace(stackTrace);
      }
    }
    if (!stack) {
      stack = m_inspector->debugger()->captureStackTrace(true);
    }

    // REPL mode implicitly handles the script like an async function.
    // Do not prepend the '(in promise)' prefix for these exceptions since that
    // would be confusing for the user. The stringified error is part of the
    // exception and does not need to be added in REPL mode, otherwise it would
    // be printed twice.
    String16 exceptionDetailsText =
        m_replMode ? "Uncaught" : "Uncaught (in promise)" + messageString;
    std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
        protocol::Runtime::ExceptionDetails::create()
            .setExceptionId(m_inspector->nextExceptionId())
            .setText(exceptionDetailsText)
            .setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber()
                                                      : 0)
            .setColumnNumber(
                stack && !stack->isEmpty() ? stack->topColumnNumber() : 0)
            .build();
    response = scope.injectedScript()->addExceptionToDetails(
        result, exceptionDetails.get(), m_objectGroup);
    if (!response.IsSuccess()) {
      EvaluateCallback::sendFailure(m_callback, scope.injectedScript(),
                                    response);
      return;
    }
    if (stack)
      exceptionDetails->setStackTrace(
          stack->buildInspectorObjectImpl(m_inspector->debugger()));
    if (stack && !stack->isEmpty())
      exceptionDetails->setScriptId(
          String16::fromInteger(stack->topScriptId()));
    EvaluateCallback::sendSuccess(m_callback, scope.injectedScript(),
                                  std::move(wrappedValue),
                                  std::move(exceptionDetails));
  }

  V8InspectorImpl* m_inspector;
  int m_sessionId;
  int m_contextGroupId;
  int m_executionContextId;
  String16 m_objectGroup;
  std::unique_ptr<WrapOptions> m_wrapOptions;
  bool m_replMode;
  bool m_throwOnSideEffect;
  std::weak_ptr<EvaluateCallback> m_callback;
  v8::Global<v8::Promise> m_evaluationResult;
};

InjectedScript::InjectedScript(InspectedContext* context, int sessionId)
    : m_context(context), m_sessionId(sessionId) {}

InjectedScript::~InjectedScript() { discardEvaluateCallbacks(); }

namespace {
class PropertyAccumulator : public ValueMirror::PropertyAccumulator {
 public:
  explicit PropertyAccumulator(std::vector<PropertyMirror>* mirrors)
      : m_mirrors(mirrors) {}
  bool Add(PropertyMirror mirror) override {
    m_mirrors->push_back(std::move(mirror));
    return true;
  }

 private:
  std::vector<PropertyMirror>* m_mirrors;
};
}  // anonymous namespace

Response InjectedScript::getProperties(
    v8::Local<v8::Object> object, const String16& groupName, bool ownProperties,
    bool accessorPropertiesOnly, bool nonIndexedPropertiesOnly,
    const WrapOptions& wrapOptions,
    std::unique_ptr<Array<PropertyDescriptor>>* properties,
    Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
  v8::HandleScope handles(m_context->isolate());
  v8::Local<v8::Context> context = m_context->context();
  v8::Isolate* isolate = m_context->isolate();
  int sessionId = m_sessionId;
  v8::TryCatch tryCatch(isolate);

  *properties = std::make_unique<Array<PropertyDescriptor>>();
  std::vector<PropertyMirror> mirrors;
  PropertyAccumulator accumulator(&mirrors);
  if (!ValueMirror::getProperties(context, object, ownProperties,
                                  accessorPropertiesOnly,
                                  nonIndexedPropertiesOnly, &accumulator)) {
    return createExceptionDetails(tryCatch, groupName, exceptionDetails);
  }
  for (const PropertyMirror& mirror : mirrors) {
    std::unique_ptr<PropertyDescriptor> descriptor =
        PropertyDescriptor::create()
            .setName(mirror.name)
            .setConfigurable(mirror.configurable)
            .setEnumerable(mirror.enumerable)
            .setIsOwn(mirror.isOwn)
            .build();
    std::unique_ptr<RemoteObject> remoteObject;
    if (mirror.value) {
      Response response = wrapObjectMirror(
          *mirror.value, groupName, wrapOptions, v8::MaybeLocal<v8::Value>(),
          kMaxCustomPreviewDepth, &remoteObject);
      if (!response.IsSuccess()) return response;
      descriptor->setValue(std::move(remoteObject));
      descriptor->setWritable(mirror.writable);
    }
    if (mirror.getter) {
      Response response =
          mirror.getter->buildRemoteObject(context, wrapOptions, &remoteObject);
      if (!response.IsSuccess()) return response;
      response =
          bindRemoteObjectIfNeeded(sessionId, context, mirror.getter->v8Value(),
                                   groupName, remoteObject.get());
      if (!response.IsSuccess()) return response;
      descriptor->setGet(std::move(remoteObject));
    }
    if (mirror.setter) {
      Response response =
          mirror.setter->buildRemoteObject(context, wrapOptions, &remoteObject);
      if (!response.IsSuccess()) return response;
      response =
          bindRemoteObjectIfNeeded(sessionId, context, mirror.setter->v8Value(),
                                   groupName, remoteObject.get());
      if (!response.IsSuccess()) return response;
      descriptor->setSet(std::move(remoteObject));
    }
    if (mirror.symbol) {
      Response response =
          mirror.symbol->buildRemoteObject(context, wrapOptions, &remoteObject);
      if (!response.IsSuccess()) return response;
      response =
          bindRemoteObjectIfNeeded(sessionId, context, mirror.symbol->v8Value(),
                                   groupName, remoteObject.get());
      if (!response.IsSuccess()) return response;
      descriptor->setSymbol(std::move(remoteObject));
    }
    if (mirror.exception) {
      Response response = mirror.exception->buildRemoteObject(
          context, wrapOptions, &remoteObject);
      if (!response.IsSuccess()) return response;
      response = bindRemoteObjectIfNeeded(sessionId, context,
                                          mirror.exception->v8Value(),
                                          groupName, remoteObject.get());
      if (!response.IsSuccess()) return response;
      descriptor->setValue(std::move(remoteObject));
      descriptor->setWasThrown(true);
    }
    (*properties)->emplace_back(std::move(descriptor));
  }
  return Response::Success();
}

Response InjectedScript::getInternalAndPrivateProperties(
    v8::Local<v8::Value> value, const String16& groupName,
    bool accessorPropertiesOnly,
    std::unique_ptr<protocol::Array<InternalPropertyDescriptor>>*
        internalProperties,
    std::unique_ptr<protocol::Array<PrivatePropertyDescriptor>>*
        privateProperties) {
  *internalProperties = std::make_unique<Array<InternalPropertyDescriptor>>();
  *privateProperties = std::make_unique<Array<PrivatePropertyDescriptor>>();

  if (!value->IsObject()) return Response::Success();

  v8::Local<v8::Object> value_obj = value.As<v8::Object>();

  v8::Local<v8::Context> context = m_context->context();
  int sessionId = m_sessionId;

  if (!accessorPropertiesOnly) {
    std::vector<InternalPropertyMirror> internalPropertiesWrappers;
    ValueMirror::getInternalProperties(m_context->context(), value_obj,
                                       &internalPropertiesWrappers);
    for (const auto& internalProperty : internalPropertiesWrappers) {
      std::unique_ptr<RemoteObject> remoteObject;
      Response response = internalProperty.value->buildRemoteObject(
          m_context->context(), WrapOptions({WrapMode::kIdOnly}),
          &remoteObject);
      if (!response.IsSuccess()) return response;
      response = bindRemoteObjectIfNeeded(sessionId, context,
                                          internalProperty.value->v8Value(),
                                          groupName, remoteObject.get());
      if (!response.IsSuccess()) return response;
      (*internalProperties)
          ->emplace_back(InternalPropertyDescriptor::create()
                             .setName(internalProperty.name)
                             .setValue(std::move(remoteObject))
                             .build());
    }
  }

  std::vector<PrivatePropertyMirror> privatePropertyWrappers =
      ValueMirror::getPrivateProperties(context, value_obj,
                                        accessorPropertiesOnly);
  for (const auto& privateProperty : privatePropertyWrappers) {
    std::unique_ptr<PrivatePropertyDescriptor> descriptor =
        PrivatePropertyDescriptor::create()
            .setName(privateProperty.name)
            .build();

    std::unique_ptr<RemoteObject> remoteObject;
    DCHECK((privateProperty.getter || privateProperty.setter) ^
           (!!privateProperty.value));
    if (privateProperty.value) {
      Response response = privateProperty.value->buildRemoteObject(
          context, WrapOptions({WrapMode::kIdOnly}), &remoteObject);
      if (!response.IsSuccess()) return response;
      response = bindRemoteObjectIfNeeded(sessionId, context,
                                          privateProperty.value->v8Value(),
                                          groupName, remoteObject.get());
      if (!response.IsSuccess()) return response;
      descriptor->setValue(std::move(remoteObject));
    }

    if (privateProperty.getter) {
      Response response = privateProperty.getter->buildRemoteObject(
          context, WrapOptions({WrapMode::kIdOnly}), &remoteObject);
      if (!response.IsSuccess()) return response;
      response = bindRemoteObjectIfNeeded(sessionId, context,
                                          privateProperty.getter->v8Value(),
                                          groupName, remoteObject.get());
      if (!response.IsSuccess()) return response;
      descriptor->setGet(std::move(remoteObject));
    }

    if (privateProperty.setter) {
      Response response = privateProperty.setter->buildRemoteObject(
          context, WrapOptions({WrapMode::kIdOnly}), &remoteObject);
      if (!response.IsSuccess()) return response;
      response = bindRemoteObjectIfNeeded(sessionId, context,
                                          privateProperty.setter->v8Value(),
                                          groupName, remoteObject.get());
      if (!response.IsSuccess()) return response;
      descriptor->setSet(std::move(remoteObject));
    }

    (*privateProperties)->emplace_back(std::move(descriptor));
  }
  return Response::Success();
}

void InjectedScript::releaseObject(const String16& objectId) {
  std::unique_ptr<RemoteObjectId> remoteId;
  Response response = RemoteObjectId::parse(objectId, &remoteId);
  if (response.IsSuccess()) unbindObject(remoteId->id());
}

Response InjectedScript::wrapObject(
    v8::Local<v8::Value> value, const String16& groupName,
    const WrapOptions& wrapOptions,
    std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
  return wrapObject(value, groupName, wrapOptions, v8::MaybeLocal<v8::Value>(),
                    kMaxCustomPreviewDepth, result);
}

Response InjectedScript::wrapObject(
    v8::Local<v8::Value> value, const String16& groupName,
    const WrapOptions& wrapOptions,
    v8::MaybeLocal<v8::Value> customPreviewConfig, int maxCustomPreviewDepth,
    std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
  v8::Local<v8::Context> context = m_context->context();
  v8::Context::Scope contextScope(context);
  std::unique_ptr<ValueMirror> mirror = ValueMirror::create(context, value);
  if (!mirror) return Response::InternalError();
  return wrapObjectMirror(*mirror, groupName, wrapOptions, customPreviewConfig,
                          maxCustomPreviewDepth, result);
}

Response InjectedScript::wrapObjectMirror(
    const ValueMirror& mirror, const String16& groupName,
    const WrapOptions& wrapOptions,
    v8::MaybeLocal<v8::Value> customPreviewConfig, int maxCustomPreviewDepth,
    std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
  int customPreviewEnabled = m_customPreviewEnabled;
  int sessionId = m_sessionId;
  v8::Local<v8::Context> context = m_context->context();
  v8::Context::Scope contextScope(context);
  Response response = mirror.buildRemoteObject(context, wrapOptions, result);
  if (!response.IsSuccess()) return response;
  v8::Local<v8::Value> value = mirror.v8Value();
  response = bindRemoteObjectIfNeeded(sessionId, context, value, groupName,
                                      result->get());
  if (!response.IsSuccess()) return response;
  if (customPreviewEnabled && value->IsObject()) {
    std::unique_ptr<protocol::Runtime::CustomPreview> customPreview;
    generateCustomPreview(sessionId, groupName, value.As<v8::Object>(),
                          customPreviewConfig, maxCustomPreviewDepth,
                          &customPreview);
    if (customPreview) (*result)->setCustomPreview(std::move(customPreview));
  }
  if (wrapOptions.mode == WrapMode::kDeep) {
    V8SerializationDuplicateTracker duplicateTracker{context};

    std::unique_ptr<protocol::DictionaryValue> deepSerializedValueDict;
    response = mirror.buildDeepSerializedValue(
        context, wrapOptions.serializationOptions.maxDepth,
        wrapOptions.serializationOptions.additionalParameters.Get(
            m_context->isolate()),
        duplicateTracker, &deepSerializedValueDict);
    if (!response.IsSuccess()) return response;

    String16 type;
    deepSerializedValueDict->getString("type", &type);

    std::unique_ptr<protocol::Runtime::DeepSerializedValue>
        deepSerializedValue = protocol::Runtime::DeepSerializedValue::create()
                                  .setType(type)
                                  .build();

    protocol::Value* maybeValue = deepSerializedValueDict->get("value");
    if (maybeValue != nullptr) {
      deepSerializedValue->setValue(maybeValue->clone());
    }

    int weakLocalObjectReference;
    if (deepSerializedValueDict->getInteger("weakLocalObjectReference",
                                            &weakLocalObjectReference)) {
      deepSerializedValue->setWeakLocalObjectReference(
          weakLocalObjectReference);
    }

    if (!response.IsSuccess()) return response;
    (*result)->setDeepSerializedValue(std::move(deepSerializedValue));
  }

  return Response::Success();
}

std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapTable(
    v8::Local<v8::Object> table, v8::MaybeLocal<v8::Array> maybeColumns) {
  using protocol::Runtime::RemoteObject;
  using protocol::Runtime::ObjectPreview;
  using protocol::Runtime::PropertyPreview;
  using protocol::Array;

  v8::Isolate* isolate = m_context->isolate();
  v8::HandleScope handles(isolate);
  v8::Local<v8::Context> context = m_context->context();

  std::unique_ptr<RemoteObject> remoteObject;
  Response response = wrapObject(
      table, "console", WrapOptions({WrapMode::kIdOnly}), &remoteObject);
  if (!remoteObject || !response.IsSuccess()) return nullptr;

  auto mirror = ValueMirror::create(context, table);
  std::unique_ptr<ObjectPreview> preview;
  int limit = 1000;
  mirror->buildObjectPreview(context, true /* generatePreviewForTable */,
                             &limit, &limit, &preview);
  if (!preview) return nullptr;

  std::vector<String16> selectedColumns;
  std::unordered_set<String16> columnSet;
  v8::Local<v8::Array> v8Columns;
  if (maybeColumns.ToLocal(&v8Columns)) {
    for (uint32_t i = 0; i < v8Columns->Length(); ++i) {
      v8::Local<v8::Value> column;
      if (v8Columns->Get(context, i).ToLocal(&column) && column->IsString()) {
        String16 name = toProtocolString(isolate, column.As<v8::String>());
        if (columnSet.find(name) == columnSet.end()) {
          columnSet.insert(name);
          selectedColumns.push_back(name);
        }
      }
    }
  }
  if (!selectedColumns.empty()) {
    for (const std::unique_ptr<PropertyPreview>& prop :
         *preview->getProperties()) {
      ObjectPreview* columnPreview = prop->getValuePreview(nullptr);
      if (!columnPreview) continue;
      // Use raw pointer here since the lifetime of each PropertyPreview is
      // ensured by columnPreview. This saves an additional clone.
      std::unordered_map<String16, PropertyPreview*> columnMap;
      for (const std::unique_ptr<PropertyPreview>& property :
           *columnPreview->getProperties()) {
        if (columnSet.find(property->getName()) == columnSet.end()) continue;
        columnMap[property->getName()] = property.get();
      }
      auto filtered = std::make_unique<Array<PropertyPreview>>();
      for (const String16& column : selectedColumns) {
        if (columnMap.find(column) == columnMap.end()) continue;
        filtered->push_back(columnMap[column]->Clone());
      }
      columnPreview->setProperties(std::move(filtered));
    }
  }
  remoteObject->setPreview(std::move(preview));
  return remoteObject;
}

void InjectedScript::addPromiseCallback(
    V8InspectorSessionImpl* session, v8::MaybeLocal<v8::Value> value,
    const String16& objectGroup, std::unique_ptr<WrapOptions> wrapOptions,
    bool replMode, bool throwOnSideEffect,
    std::shared_ptr<EvaluateCallback> callback) {
  m_evaluateCallbacks.insert(callback);
  // After stashing the shared_ptr in `m_evaluateCallback`, we reset `callback`.
  // `ProtocolPromiseHandler:add` can take longer than the life time of this
  // `InjectedScript` and we don't want `callback` to survive that.
  std::weak_ptr<EvaluateCallback> weak_callback = callback;
  callback.reset();
  CHECK_EQ(weak_callback.use_count(), 1);

  if (value.IsEmpty()) {
    EvaluateCallback::sendFailure(weak_callback, this,
                                  Response::InternalError());
    return;
  }

  v8::MicrotasksScope microtasksScope(m_context->context(),
                                      v8::MicrotasksScope::kRunMicrotasks);
  ProtocolPromiseHandler::add(session, m_context->context(),
                              value.ToLocalChecked(), m_context->contextId(),
                              objectGroup, std::move(wrapOptions), replMode,
                              throwOnSideEffect, weak_callback);
  // Do not add any code here! `this` might be invalid.
  // `ProtocolPromiseHandler::add` calls into JS which could kill this
  // `InjectedScript`.
}

void InjectedScript::discardEvaluateCallbacks() {
  while (!m_evaluateCallbacks.empty()) {
    EvaluateCallback::sendFailure(
        *m_evaluateCallbacks.begin(), this,
        Response::ServerError("Execution context was destroyed."));
  }
  CHECK(m_evaluateCallbacks.empty());
}

void InjectedScript::deleteEvaluateCallback(
    std::shared_ptr<EvaluateCallback> callback) {
  auto it = m_evaluateCallbacks.find(callback);
  CHECK_NE(it, m_evaluateCallbacks.end());
  m_evaluateCallbacks.erase(it);
}

Response InjectedScript::findObject(const RemoteObjectId& objectId,
                                    v8::Local<v8::Value>* outObject) const {
  auto it = m_idToWrappedObject.find(objectId.id());
  if (it == m_idToWrappedObject.end())
    return Response::ServerError("Could not find object with given id");
  *outObject = it->second.Get(m_context->isolate());
  return Response::Success();
}

String16 InjectedScript::objectGroupName(const RemoteObjectId& objectId) const {
  if (objectId.id() <= 0) return String16();
  auto it = m_idToObjectGroupName.find(objectId.id());
  return it != m_idToObjectGroupName.end() ? it->second : String16();
}

void InjectedScript::releaseObjectGroup(const String16& objectGroup) {
  if (objectGroup == "console") m_lastEvaluationResult.Reset();
  if (objectGroup.isEmpty()) return;
  auto it = m_nameToObjectGroup.find(objectGroup);
  if (it == m_nameToObjectGroup.end()) return;
  for (int id : it->second) unbindObject(id);
  m_nameToObjectGroup.erase(it);
}

void InjectedScript::setCustomObjectFormatterEnabled(bool enabled) {
  m_customPreviewEnabled = enabled;
}

v8::Local<v8::Value> InjectedScript::lastEvaluationResult() const {
  if (m_lastEvaluationResult.IsEmpty())
    return v8::Undefined(m_context->isolate());
  return m_lastEvaluationResult.Get(m_context->isolate());
}

void InjectedScript::setLastEvaluationResult(v8::Local<v8::Value> result) {
  m_lastEvaluationResult.Reset(m_context->isolate(), result);
  m_lastEvaluationResult.AnnotateStrongRetainer(kGlobalHandleLabel);
}

Response InjectedScript::resolveCallArgument(
    protocol::Runtime::CallArgument* callArgument,
    v8::Local<v8::Value>* result) {
  if (callArgument->hasObjectId()) {
    std::unique_ptr<RemoteObjectId> remoteObjectId;
    Response response =
        RemoteObjectId::parse(callArgument->getObjectId(""), &remoteObjectId);
    if (!response.IsSuccess()) return response;
    if (remoteObjectId->contextId() != m_context->contextId() ||
        remoteObjectId->isolateId() != m_context->inspector()->isolateId()) {
      return Response::ServerError(
          "Argument should belong to the same JavaScript world as target "
          "object");
    }
    return findObject(*remoteObjectId, result);
  }
  if (callArgument->hasValue() || callArgument->hasUnserializableValue()) {
    String16 value;
    if (callArgument->hasValue()) {
      std::vector<uint8_t> json;
      v8_crdtp::json::ConvertCBORToJSON(
          v8_crdtp::SpanFrom(callArgument->getValue(nullptr)->Serialize()),
          &json);
      value =
          "(" +
          String16(reinterpret_cast<const char*>(json.data()), json.size()) +
          ")";
    } else {
      String16 unserializableValue = callArgument->getUnserializableValue("");
      // Protect against potential identifier resolution for NaN and Infinity.
      if (isResolvableNumberLike(unserializableValue))
        value = "Number(\"" + unserializableValue + "\")";
      else
        value = unserializableValue;
    }
    if (!m_context->inspector()
             ->compileAndRunInternalScript(
                 m_context->context(), toV8String(m_context->isolate(), value))
             .ToLocal(result)) {
      return Response::ServerError(
          "Couldn't parse value object in call argument");
    }
    return Response::Success();
  }
  *result = v8::Undefined(m_context->isolate());
  return Response::Success();
}

Response InjectedScript::addExceptionToDetails(
    v8::Local<v8::Value> exception,
    protocol::Runtime::ExceptionDetails* exceptionDetails,
    const String16& objectGroup) {
  if (exception.IsEmpty()) return Response::Success();
  std::unique_ptr<protocol::Runtime::RemoteObject> wrapped;
  Response response =
      wrapObject(exception, objectGroup,
                 exception->IsNativeError() ? WrapOptions({WrapMode::kIdOnly})
                                            : WrapOptions({WrapMode::kPreview}),
                 &wrapped);
  if (!response.IsSuccess()) return response;
  exceptionDetails->setException(std::move(wrapped));
  return Response::Success();
}

Response InjectedScript::createExceptionDetails(
    const v8::TryCatch& tryCatch, const String16& objectGroup,
    Maybe<protocol::Runtime::ExceptionDetails>* result) {
  if (!tryCatch.HasCaught()) return Response::InternalError();
  v8::Local<v8::Message> message = tryCatch.Message();
  v8::Local<v8::Value> exception = tryCatch.Exception();
  return createExceptionDetails(message, exception, objectGroup, result);
}

Response InjectedScript::createExceptionDetails(
    v8::Local<v8::Message> message, v8::Local<v8::Value> exception,
    const String16& objectGroup,
    Maybe<protocol::Runtime::ExceptionDetails>* result) {
  String16 messageText =
      message.IsEmpty()
          ? String16()
          : toProtocolString(m_context->isolate(), message->Get());
  std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
      protocol::Runtime::ExceptionDetails::create()
          .setExceptionId(m_context->inspector()->nextExceptionId())
          .setText(exception.IsEmpty() ? messageText : String16("Uncaught"))
          .setLineNumber(
              message.IsEmpty()
                  ? 0
                  : message->GetLineNumber(m_context->context()).FromMaybe(1) -
                        1)
          .setColumnNumber(
              message.IsEmpty()
                  ? 0
                  : message->GetStartColumn(m_context->context()).FromMaybe(0))
          .build();
  if (!message.IsEmpty()) {
    exceptionDetails->setScriptId(
        String16::fromInteger(message->GetScriptOrigin().ScriptId()));
    v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace();
    if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0) {
      std::unique_ptr<V8StackTraceImpl> v8StackTrace =
          m_context->inspector()->debugger()->createStackTrace(stackTrace);
      if (v8StackTrace) {
        exceptionDetails->setStackTrace(v8StackTrace->buildInspectorObjectImpl(
            m_context->inspector()->debugger()));
      }
    }
  }
  Response response =
      addExceptionToDetails(exception, exceptionDetails.get(), objectGroup);
  if (!response.IsSuccess()) return response;
  *result = std::move(exceptionDetails);
  return Response::Success();
}

Response InjectedScript::wrapEvaluateResult(
    v8::MaybeLocal<v8::Value> maybeResultValue, const v8::TryCatch& tryCatch,
    const String16& objectGroup, const WrapOptions& wrapOptions,
    bool throwOnSideEffect,
    std::unique_ptr<protocol::Runtime::RemoteObject>* result,
    Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
  v8::Local<v8::Value> resultValue;
  if (!tryCatch.HasCaught()) {
    if (!maybeResultValue.ToLocal(&resultValue))
      return Response::InternalError();
    Response response =
        wrapObject(resultValue, objectGroup, wrapOptions, result);
    if (!response.IsSuccess()) return response;
    if (objectGroup == "console") {
      m_lastEvaluationResult.Reset(m_context->isolate(), resultValue);
      m_lastEvaluationResult.AnnotateStrongRetainer(kGlobalHandleLabel);
    }
  } else {
    if (tryCatch.HasTerminated() || !tryCatch.CanContinue()) {
      return Response::ServerError("Execution was terminated");
    }
    v8::Local<v8::Value> exception = tryCatch.Exception();
    if (!throwOnSideEffect) {
      m_context->inspector()->client()->dispatchError(
          m_context->context(), tryCatch.Message(), exception);
    }
    Response response = wrapObject(exception, objectGroup,
                                   exception->IsNativeError()
                                       ? WrapOptions({WrapMode::kIdOnly})
                                       : WrapOptions({WrapMode::kPreview}),
                                   result);
    if (!response.IsSuccess()) return response;
    // We send exception in result for compatibility reasons, even though it's
    // accessible through exceptionDetails.exception.
    response = createExceptionDetails(tryCatch, objectGroup, exceptionDetails);
    if (!response.IsSuccess()) return response;
  }
  return Response::Success();
}

v8::Local<v8::Object> InjectedScript::commandLineAPI() {
  if (m_commandLineAPI.IsEmpty()) {
    v8::debug::DisableBreakScope disable_break(m_context->isolate());
    m_commandLineAPI.Reset(
        m_context->isolate(),
        m_context->inspector()->console()->createCommandLineAPI(
            m_context->context(), m_sessionId));
    m_commandLineAPI.AnnotateStrongRetainer(kGlobalHandleLabel);
  }
  return m_commandLineAPI.Get(m_context->isolate());
}

InjectedScript::Scope::Scope(V8InspectorSessionImpl* session)
    : m_inspector(session->inspector()),
      m_injectedScript(nullptr),
      m_handleScope(m_inspector->isolate()),
      m_tryCatch(m_inspector->isolate()),
      m_ignoreExceptionsAndMuteConsole(false),
      m_previousPauseOnExceptionsState(v8::debug::NoBreakOnException),
      m_userGesture(false),
      m_allowEval(false),
      m_contextGroupId(session->contextGroupId()),
      m_sessionId(session->sessionId()) {}

Response InjectedScript::Scope::initialize() {
  cleanup();
  V8InspectorSessionImpl* session =
      m_inspector->sessionById(m_contextGroupId, m_sessionId);
  if (!session) return Response::InternalError();
  Response response = findInjectedScript(session);
  if (!response.IsSuccess()) return response;
  m_context = m_injectedScript->context()->context();
  m_context->Enter();
  if (m_allowEval) m_context->AllowCodeGenerationFromStrings(true);
  return Response::Success();
}

void InjectedScript::Scope::installCommandLineAPI() {
  DCHECK(m_injectedScript && !m_context.IsEmpty() &&
         !m_commandLineAPIScope.get());
  V8InspectorSessionImpl* session =
      m_inspector->sessionById(m_contextGroupId, m_sessionId);
  if (session->clientTrustLevel() != V8Inspector::kFullyTrusted) {
    return;
  }
  m_commandLineAPIScope.reset(new V8Console::CommandLineAPIScope(
      m_context, m_injectedScript->commandLineAPI(), m_context->Global()));
}

void InjectedScript::Scope::ignoreExceptionsAndMuteConsole() {
  DCHECK(!m_ignoreExceptionsAndMuteConsole);
  m_ignoreExceptionsAndMuteConsole = true;
  m_inspector->client()->muteMetrics(m_contextGroupId);
  m_inspector->muteExceptions(m_contextGroupId);
  m_previousPauseOnExceptionsState =
      setPauseOnExceptionsState(v8::debug::NoBreakOnException);
}

v8::debug::ExceptionBreakState InjectedScript::Scope::setPauseOnExceptionsState(
    v8::debug::ExceptionBreakState newState) {
  if (!m_inspector->debugger()->enabled()) return newState;
  v8::debug::ExceptionBreakState presentState =
      m_inspector->debugger()->getPauseOnExceptionsState();
  if (presentState != newState)
    m_inspector->debugger()->setPauseOnExceptionsState(newState);
  return presentState;
}

void InjectedScript::Scope::pretendUserGesture() {
  DCHECK(!m_userGesture);
  m_userGesture = true;
  m_inspector->client()->beginUserGesture();
}

void InjectedScript::Scope::allowCodeGenerationFromStrings() {
  DCHECK(!m_allowEval);
  if (m_context->IsCodeGenerationFromStringsAllowed()) return;
  m_allowEval = true;
  m_context->AllowCodeGenerationFromStrings(true);
}

void InjectedScript::Scope::cleanup() {
  m_commandLineAPIScope.reset();
  if (!m_context.IsEmpty()) {
    if (m_allowEval) m_context->AllowCodeGenerationFromStrings(false);
    m_context->Exit();
    m_context.Clear();
  }
}

InjectedScript::Scope::~Scope() {
  if (m_ignoreExceptionsAndMuteConsole) {
    setPauseOnExceptionsState(m_previousPauseOnExceptionsState);
    m_inspector->client()->unmuteMetrics(m_contextGroupId);
    m_inspector->unmuteExceptions(m_contextGroupId);
  }
  if (m_userGesture) m_inspector->client()->endUserGesture();
  cleanup();
}

InjectedScript::ContextScope::ContextScope(V8InspectorSessionImpl* session,
                                           int executionContextId)
    : InjectedScript::Scope(session),
      m_executionContextId(executionContextId) {}

InjectedScript::ContextScope::~ContextScope() = default;

Response InjectedScript::ContextScope::findInjectedScript(
    V8InspectorSessionImpl* session) {
  return session->findInjectedScript(m_executionContextId, m_injectedScript);
}

InjectedScript::ObjectScope::ObjectScope(V8InspectorSessionImpl* session,
                                         const String16& remoteObjectId)
    : InjectedScript::Scope(session), m_remoteObjectId(remoteObjectId) {}

InjectedScript::ObjectScope::~ObjectScope() = default;

Response InjectedScript::ObjectScope::findInjectedScript(
    V8InspectorSessionImpl* session) {
  std::unique_ptr<RemoteObjectId> remoteId;
  Response response = RemoteObjectId::parse(m_remoteObjectId, &remoteId);
  if (!response.IsSuccess()) return response;
  InjectedScript* injectedScript = nullptr;
  response = session->findInjectedScript(remoteId.get(), injectedScript);
  if (!response.IsSuccess()) return response;
  m_objectGroupName = injectedScript->objectGroupName(*remoteId);
  response = injectedScript->findObject(*remoteId, &m_object);
  if (!response.IsSuccess()) return response;
  m_injectedScript = injectedScript;
  return Response::Success();
}

InjectedScript::CallFrameScope::CallFrameScope(V8InspectorSessionImpl* session,
                                               const String16& remoteObjectId)
    : InjectedScript::Scope(session), m_remoteCallFrameId(remoteObjectId) {}

InjectedScript::CallFrameScope::~CallFrameScope() = default;

Response InjectedScript::CallFrameScope::findInjectedScript(
    V8InspectorSessionImpl* session) {
  std::unique_ptr<RemoteCallFrameId> remoteId;
  Response response = RemoteCallFrameId::parse(m_remoteCallFrameId, &remoteId);
  if (!response.IsSuccess()) return response;
  m_frameOrdinal = static_cast<size_t>(remoteId->frameOrdinal());
  return session->findInjectedScript(remoteId.get(), m_injectedScript);
}

String16 InjectedScript::bindObject(v8::Local<v8::Value> value,
                                    const String16& groupName) {
  if (m_lastBoundObjectId <= 0) m_lastBoundObjectId = 1;
  int id = m_lastBoundObjectId++;
  m_idToWrappedObject[id].Reset(m_context->isolate(), value);
  m_idToWrappedObject[id].AnnotateStrongRetainer(kGlobalHandleLabel);
  if (!groupName.isEmpty() && id > 0) {
    m_idToObjectGroupName[id] = groupName;
    m_nameToObjectGroup[groupName].push_back(id);
  }
  return RemoteObjectId::serialize(m_context->inspector()->isolateId(),
                                   m_context->contextId(), id);
}

// static
Response InjectedScript::bindRemoteObjectIfNeeded(
    int sessionId, v8::Local<v8::Context> context, v8::Local<v8::Value> value,
    const String16& groupName, protocol::Runtime::RemoteObject* remoteObject) {
  if (!remoteObject) return Response::Success();
  if (remoteObject->hasValue()) return Response::Success();
  if (remoteObject->hasUnserializableValue()) return Response::Success();
  if (remoteObject->getType() != RemoteObject::TypeEnum::Undefined) {
    v8::Isolate* isolate = context->GetIsolate();
    V8InspectorImpl* inspector =
        static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate));
    InspectedContext* inspectedContext =
        inspector->getContext(InspectedContext::contextId(context));
    InjectedScript* injectedScript =
        inspectedContext ? inspectedContext->getInjectedScript(sessionId)
                         : nullptr;
    if (!injectedScript) {
      return Response::ServerError("Cannot find context with specified id");
    }
    remoteObject->setObjectId(injectedScript->bindObject(value, groupName));
  }
  return Response::Success();
}

void InjectedScript::unbindObject(int id) {
  m_idToWrappedObject.erase(id);
  m_idToObjectGroupName.erase(id);
}

PromiseHandlerTracker::PromiseHandlerTracker() = default;

PromiseHandlerTracker::~PromiseHandlerTracker() { discardAll(); }

template <typename... Args>
PromiseHandlerTracker::Id PromiseHandlerTracker::create(Args&&... args) {
  Id id = m_lastUsedId++;
  InjectedScript::ProtocolPromiseHandler* handler =
      new InjectedScript::ProtocolPromiseHandler(id,
                                                 std::forward<Args>(args)...);
  m_promiseHandlers.emplace(id, handler);
  return id;
}

void PromiseHandlerTracker::discard(Id id, DiscardReason reason) {
  auto iter = m_promiseHandlers.find(id);
  CHECK_NE(iter, m_promiseHandlers.end());
  InjectedScript::ProtocolPromiseHandler* handler = iter->second.get();

  switch (reason) {
    case DiscardReason::kPromiseCollected:
      sendFailure(handler, Response::ServerError("Promise was collected"));
      break;
    case DiscardReason::kTearDown:
      sendFailure(handler, Response::ServerError(
                               "Tearing down inspector/session/context"));
      break;
    case DiscardReason::kFulfilled:
      // Do nothing.
      break;
  }

  m_promiseHandlers.erase(id);
}

InjectedScript::ProtocolPromiseHandler* PromiseHandlerTracker::get(
    Id id) const {
  auto iter = m_promiseHandlers.find(id);
  if (iter == m_promiseHandlers.end()) return nullptr;

  return iter->second.get();
}

void PromiseHandlerTracker::sendFailure(
    InjectedScript::ProtocolPromiseHandler* handler,
    const protocol::DispatchResponse& response) const {
  V8InspectorImpl* inspector = handler->m_inspector;
  V8InspectorSessionImpl* session =
      inspector->sessionById(handler->m_contextGroupId, handler->m_sessionId);
  if (!session) return;
  InjectedScript::ContextScope scope(session, handler->m_executionContextId);
  Response res = scope.initialize();
  if (!res.IsSuccess()) return;
  EvaluateCallback::sendFailure(handler->m_callback, scope.injectedScript(),
                                response);
}

void PromiseHandlerTracker::discardAll() {
  while (!m_promiseHandlers.empty()) {
    discard(m_promiseHandlers.begin()->first, DiscardReason::kTearDown);
  }
  CHECK(m_promiseHandlers.empty());
}

}  // namespace v8_inspector

Zerion Mini Shell 1.0