text: // Copyright 2021 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 INCLUDE_V8_LOCAL_HANDLE_H_
  #define INCLUDE_V8_LOCAL_HANDLE_H_
  
  #include <stddef.h>
  
  #include <type_traits>
  
  #include "v8-internal.h"  // NOLINT(build/include_directory)
  
  namespace v8 {
  
  class Boolean;
  template <class T>
  class BasicTracedReference;
  class Context;
  class EscapableHandleScope;
  template <class F>
  class Eternal;
  template <class F>
  class FunctionCallbackInfo;
  class Isolate;
  template <class F>
  class MaybeLocal;
  template <class T>
  class NonCopyablePersistentTraits;
  class Object;
  template <class T, class M = NonCopyablePersistentTraits<T>>
  class Persistent;
  template <class T>
  class PersistentBase;
  template <class F1, class F2, class F3>
  class PersistentValueMapBase;
  template <class F1, class F2>
  class PersistentValueVector;
  class Primitive;
  class Private;
  template <class F>
  class PropertyCallbackInfo;
  template <class F>
  class ReturnValue;
  class String;
  template <class F>
  class Traced;
  template <class F>
  class TracedGlobal;
  template <class F>
  class TracedReference;
  class TracedReferenceBase;
  class Utils;
  
  namespace internal {
  template <typename T>
  class CustomArguments;
  }  // namespace internal
  
  namespace api_internal {
  // Called when ToLocalChecked is called on an empty Local.
  V8_EXPORT void ToLocalEmpty();
  }  // namespace api_internal
  
  /**
   * A stack-allocated class that governs a number of local handles.
   * After a handle scope has been created, all local handles will be
   * allocated within that handle scope until either the handle scope is
   * deleted or another handle scope is created.  If there is already a
   * handle scope and a new one is created, all allocations will take
   * place in the new handle scope until it is deleted.  After that,
   * new handles will again be allocated in the original handle scope.
   *
   * After the handle scope of a local handle has been deleted the
   * garbage collector will no longer track the object stored in the
   * handle and may deallocate it.  The behavior of accessing a handle
   * for which the handle scope has been deleted is undefined.
   */
  class V8_EXPORT V8_NODISCARD HandleScope {
   public:
    explicit HandleScope(Isolate* isolate);
  
    ~HandleScope();
  
    /**
     * Counts the number of allocated handles.
     */
    static int NumberOfHandles(Isolate* isolate);
  
    V8_INLINE Isolate* GetIsolate() const {
      return reinterpret_cast<Isolate*>(isolate_);
    }
  
    HandleScope(const HandleScope&) = delete;
    void operator=(const HandleScope&) = delete;
  
   protected:
    V8_INLINE HandleScope() = default;
  
    void Initialize(Isolate* isolate);
  
    static internal::Address* CreateHandle(internal::Isolate* isolate,
                                           internal::Address value);
  
   private:
    // Declaring operator new and delete as deleted is not spec compliant.
    // Therefore declare them private instead to disable dynamic alloc
    void* operator new(size_t size);
    void* operator new[](size_t size);
    void operator delete(void*, size_t);
    void operator delete[](void*, size_t);
  
    internal::Isolate* isolate_;
    internal::Address* prev_next_;
    internal::Address* prev_limit_;
  
    // Local::New uses CreateHandle with an Isolate* parameter.
    template <class F>
    friend class Local;
  
    // Object::GetInternalField and Context::GetEmbedderData use CreateHandle with
    // a HeapObject in their shortcuts.
    friend class Object;
    friend class Context;
  };
  
  /**
   * An object reference managed by the v8 garbage collector.
   *
   * All objects returned from v8 have to be tracked by the garbage collector so
   * that it knows that the objects are still alive.  Also, because the garbage
   * collector may move objects, it is unsafe to point directly to an object.
   * Instead, all objects are stored in handles which are known by the garbage
   * collector and updated whenever an object moves.  Handles should always be
   * passed by value (except in cases like out-parameters) and they should never
   * be allocated on the heap.
   *
   * There are two types of handles: local and persistent handles.
   *
   * Local handles are light-weight and transient and typically used in local
   * operations.  They are managed by HandleScopes. That means that a HandleScope
   * must exist on the stack when they are created and that they are only valid
   * inside of the HandleScope active during their creation. For passing a local
   * handle to an outer HandleScope, an EscapableHandleScope and its Escape()
   * method must be used.
   *
   * Persistent handles can be used when storing objects across several
   * independent operations and have to be explicitly deallocated when they're no
   * longer used.
   *
   * It is safe to extract the object stored in the handle by dereferencing the
   * handle (for instance, to extract the Object* from a Local<Object>); the value
   * will still be governed by a handle behind the scenes and the same rules apply
   * to these values as to their handles.
   */
  template <class T>
  class Local {
   public:
    V8_INLINE Local() : val_(nullptr) {}
    template <class S>
    V8_INLINE Local(Local<S> that) : val_(reinterpret_cast<T*>(*that)) {
      /**
       * This check fails when trying to convert between incompatible
       * handles. For example, converting from a Local<String> to a
       * Local<Number>.
       */
      static_assert(std::is_base_of<T, S>::value, "type check");
    }
  
    /**
     * Returns true if the handle is empty.
     */
    V8_INLINE bool IsEmpty() const { return val_ == nullptr; }
  
    /**
     * Sets the handle to be empty. IsEmpty() will then return true.
     */
    V8_INLINE void Clear() { val_ = nullptr; }
  
    V8_INLINE T* operator->() const { return val_; }
  
    V8_INLINE T* operator*() const { return val_; }
  
    /**
     * Checks whether two handles are the same.
     * Returns true if both are empty, or if the objects to which they refer
     * are identical.
     *
     * If both handles refer to JS objects, this is the same as strict equality.
     * For primitives, such as numbers or strings, a `false` return value does not
     * indicate that the values aren't equal in the JavaScript sense.
     * Use `Value::StrictEquals()` to check primitives for equality.
     */
    template <class S>
    V8_INLINE bool operator==(const Local<S>& that) const {
      internal::Address* a = reinterpret_cast<internal::Address*>(this->val_);
      internal::Address* b = reinterpret_cast<internal::Address*>(that.val_);
      if (a == nullptr) return b == nullptr;
      if (b == nullptr) return false;
      return *a == *b;
    }
  
    template <class S>
    V8_INLINE bool operator==(const PersistentBase<S>& that) const {
      internal::Address* a = reinterpret_cast<internal::Address*>(this->val_);
      internal::Address* b = reinterpret_cast<internal::Address*>(that.val_);
      if (a == nullptr) return b == nullptr;
      if (b == nullptr) return false;
      return *a == *b;
    }
  
    /**
     * Checks whether two handles are different.
     * Returns true if only one of the handles is empty, or if
     * the objects to which they refer are different.
     *
     * If both handles refer to JS objects, this is the same as strict
     * non-equality. For primitives, such as numbers or strings, a `true` return
     * value does not indicate that the values aren't equal in the JavaScript
     * sense. Use `Value::StrictEquals()` to check primitives for equality.
     */
    template <class S>
    V8_INLINE bool operator!=(const Local<S>& that) const {
      return !operator==(that);
    }
  
    template <class S>
    V8_INLINE bool operator!=(const Persistent<S>& that) const {
      return !operator==(that);
    }
  
    /**
     * Cast a handle to a subclass, e.g. Local<Value> to Local<Object>.
     * This is only valid if the handle actually refers to a value of the
     * target type.
     */
    template <class S>
    V8_INLINE static Local<T> Cast(Local<S> that) {
  #ifdef V8_ENABLE_CHECKS
      // If we're going to perform the type check then we have to check
      // that the handle isn't empty before doing the checked cast.
      if (that.IsEmpty()) return Local<T>();
  #endif
      return Local<T>(T::Cast(*that));
    }
  
    /**
     * Calling this is equivalent to Local<S>::Cast().
     * In particular, this is only valid if the handle actually refers to a value
     * of the target type.
     */
    template <class S>
    V8_INLINE Local<S> As() const {
      return Local<S>::Cast(*this);
    }
  
    /**
     * Create a local handle for the content of another handle.
     * The referee is kept alive by the local handle even when
     * the original handle is destroyed/disposed.
     */
    V8_INLINE static Local<T> New(Isolate* isolate, Local<T> that) {
      return New(isolate, that.val_);
    }
  
    V8_INLINE static Local<T> New(Isolate* isolate,
                                  const PersistentBase<T>& that) {
      return New(isolate, that.val_);
    }
  
    V8_INLINE static Local<T> New(Isolate* isolate,
                                  const BasicTracedReference<T>& that) {
      return New(isolate, *that);
    }
  
   private:
    friend class TracedReferenceBase;
    friend class Utils;
    template <class F>
    friend class Eternal;
    template <class F>
    friend class PersistentBase;
    template <class F, class M>
    friend class Persistent;
    template <class F>
    friend class Local;
    template <class F>
    friend class MaybeLocal;
    template <class F>
    friend class FunctionCallbackInfo;
    template <class F>
    friend class PropertyCallbackInfo;
    friend class String;
    friend class Object;
    friend class Context;
    friend class Isolate;
    friend class Private;
    template <class F>
    friend class internal::CustomArguments;
    friend Local<Primitive> Undefined(Isolate* isolate);
    friend Local<Primitive> Null(Isolate* isolate);
    friend Local<Boolean> True(Isolate* isolate);
    friend Local<Boolean> False(Isolate* isolate);
    friend class HandleScope;
    friend class EscapableHandleScope;
    template <class F1, class F2, class F3>
    friend class PersistentValueMapBase;
    template <class F1, class F2>
    friend class PersistentValueVector;
    template <class F>
    friend class ReturnValue;
    template <class F>
    friend class Traced;
    template <class F>
    friend class TracedGlobal;
    template <class F>
    friend class BasicTracedReference;
    template <class F>
    friend class TracedReference;
  
    explicit V8_INLINE Local(T* that) : val_(that) {}
    V8_INLINE static Local<T> New(Isolate* isolate, T* that) {
      if (that == nullptr) return Local<T>();
      T* that_ptr = that;
      internal::Address* p = reinterpret_cast<internal::Address*>(that_ptr);
      return Local<T>(reinterpret_cast<T*>(HandleScope::CreateHandle(
          reinterpret_cast<internal::Isolate*>(isolate), *p)));
    }
    T* val_;
  };
  
  #if !defined(V8_IMMINENT_DEPRECATION_WARNINGS)
  // Handle is an alias for Local for historical reasons.
  template <class T>
  using Handle = Local<T>;
  #endif
  
  /**
   * A MaybeLocal<> is a wrapper around Local<> that enforces a check whether
   * the Local<> is empty before it can be used.
   *
   * If an API method returns a MaybeLocal<>, the API method can potentially fail
   * either because an exception is thrown, or because an exception is pending,
   * e.g. because a previous API call threw an exception that hasn't been caught
   * yet, or because a TerminateExecution exception was thrown. In that case, an
   * empty MaybeLocal is returned.
   */
  template <class T>
  class MaybeLocal {
   public:
    V8_INLINE MaybeLocal() : val_(nullptr) {}
    template <class S>
    V8_INLINE MaybeLocal(Local<S> that) : val_(reinterpret_cast<T*>(*that)) {
      static_assert(std::is_base_of<T, S>::value, "type check");
    }
  
    V8_INLINE bool IsEmpty() const { return val_ == nullptr; }
  
    /**
     * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty,
     * |false| is returned and |out| is left untouched.
     */
    template <class S>
    V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local<S>* out) const {
      out->val_ = IsEmpty() ? nullptr : this->val_;
      return !IsEmpty();
    }
  
    /**
     * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty,
     * V8 will crash the process.
     */
    V8_INLINE Local<T> ToLocalChecked() {
      if (V8_UNLIKELY(val_ == nullptr)) api_internal::ToLocalEmpty();
      return Local<T>(val_);
    }
  
    /**
     * Converts this MaybeLocal<> to a Local<>, using a default value if this
     * MaybeLocal<> is empty.
     */
    template <class S>
    V8_INLINE Local<S> FromMaybe(Local<S> default_value) const {
      return IsEmpty() ? default_value : Local<S>(val_);
    }
  
   private:
    T* val_;
  };
  
  /**
   * A HandleScope which first allocates a handle in the current scope
   * which will be later filled with the escape value.
   */
  class V8_EXPORT V8_NODISCARD EscapableHandleScope : public HandleScope {
   public:
    explicit EscapableHandleScope(Isolate* isolate);
    V8_INLINE ~EscapableHandleScope() = default;
  
    /**
     * Pushes the value into the previous scope and returns a handle to it.
     * Cannot be called twice.
     */
    template <class T>
    V8_INLINE Local<T> Escape(Local<T> value) {
      internal::Address* slot =
          Escape(reinterpret_cast<internal::Address*>(*value));
      return Local<T>(reinterpret_cast<T*>(slot));
    }
  
    template <class T>
    V8_INLINE MaybeLocal<T> EscapeMaybe(MaybeLocal<T> value) {
      return Escape(value.FromMaybe(Local<T>()));
    }
  
    EscapableHandleScope(const EscapableHandleScope&) = delete;
    void operator=(const EscapableHandleScope&) = delete;
  
   private:
    // Declaring operator new and delete as deleted is not spec compliant.
    // Therefore declare them private instead to disable dynamic alloc
    void* operator new(size_t size);
    void* operator new[](size_t size);
    void operator delete(void*, size_t);
    void operator delete[](void*, size_t);
  
    internal::Address* Escape(internal::Address* escape_value);
    internal::Address* escape_slot_;
  };
  
  /**
   * A SealHandleScope acts like a handle scope in which no handle allocations
   * are allowed. It can be useful for debugging handle leaks.
   * Handles can be allocated within inner normal HandleScopes.
   */
  class V8_EXPORT V8_NODISCARD SealHandleScope {
   public:
    explicit SealHandleScope(Isolate* isolate);
    ~SealHandleScope();
  
    SealHandleScope(const SealHandleScope&) = delete;
    void operator=(const SealHandleScope&) = delete;
  
   private:
    // Declaring operator new and delete as deleted is not spec compliant.
    // Therefore declare them private instead to disable dynamic alloc
    void* operator new(size_t size);
    void* operator new[](size_t size);
    void operator delete(void*, size_t);
    void operator delete[](void*, size_t);
  
    internal::Isolate* const isolate_;
    internal::Address* prev_limit_;
    int prev_sealed_level_;
  };
  
  }  // namespace v8
  
  #endif  // INCLUDE_V8_LOCAL_HANDLE_H_
  