diff --git a/include/hermes/VM/Handle.h b/include/hermes/VM/Handle.h index af7ccb17c2a..4fd675be4d1 100644 --- a/include/hermes/VM/Handle.h +++ b/include/hermes/VM/Handle.h @@ -30,6 +30,128 @@ class Handle; template class MutableHandle; +/// This class is used in performance-sensitive context in situations where we +/// want to encode in the function signature that allocations may be performed, +/// potentially moving the object, but we don't want to incur the cost of +/// always allocating a handle. The callee will allocate a handle internally if +/// it needs to. +/// +/// For example: +/// \code +/// bool checkFlag(PseudoHandle foo, Runtime *runtime) { +/// if (foo->cheapCheck()) +/// return true; +/// auto fooHandle = runtime->makeHandle(std::move(foo)); +/// return expensiveCheck(fooHandle, runtime); +/// } +/// \endcode +template +class PseudoHandle { + using traits_type = HermesValueTraits; + using value_type = typename traits_type::value_type; + using arrow_type = typename traits_type::arrow_type; + + template + friend class PseudoHandle; + + value_type value_; +#ifndef NDEBUG + bool valid_{true}; +#endif + + explicit PseudoHandle(value_type value) : value_(value) {} + + public: + PseudoHandle(const PseudoHandle &) = delete; + PseudoHandle &operator=(const PseudoHandle &) = delete; + +#ifndef NDEBUG + PseudoHandle(PseudoHandle &&hnd) : value_(hnd.value_), valid_(hnd.valid_) { + hnd.valid_ = false; + } + PseudoHandle &operator=(PseudoHandle &&hnd) { + value_ = hnd.value_; + valid_ = hnd.valid_; + hnd.valid_ = false; + return *this; + } +#else + PseudoHandle(PseudoHandle &&) = default; + PseudoHandle &operator=(PseudoHandle &&) = default; +#endif + + ~PseudoHandle() = default; + + PseudoHandle() : value_(traits_type::defaultValue()) {} + PseudoHandle(Handle handle) : value_(*handle) {} + + /// Conveniently construct PseudoHandle from a PinnedHermesValue + /// pointer. + explicit PseudoHandle(PinnedHermesValue *pvalue) : value_(*pvalue) { + static_assert( + std::is_same::value, + "This constructor can only be used for PseudoHandle"); + } + + PseudoHandle &operator=(Handle handle) { + value_ = *handle; +#ifndef NDEBUG + valid_ = true; +#endif + return *this; + } + + /// Zero-cost conversion between compatible types. + template < + typename U, + typename = typename std::enable_if::value_type, + typename PseudoHandle::value_type>::value>::type> + /* implicit */ PseudoHandle(PseudoHandle &&other) + : value_(static_cast(other.get())) { + other.invalidate(); + } + + void invalidate() { +#ifndef NDEBUG + valid_ = false; +#endif + } + + value_type get() const { + assert(valid_ && "Pseudo handle has been invalidated"); + return value_; + } + + arrow_type operator->() const { + assert(valid_ && "Pseudo handle has been invalidated"); + return traits_type::arrow(value_); + } + + explicit operator bool() const { + assert(valid_ && "Pseudo handle has been invalidated"); + return value_ != nullptr; + } + + /// \return the value encoded as HermesValue + HermesValue getHermesValue() const { + assert(valid_ && "Pseudo handle has been invalidated"); + return traits_type::encode(value_); + } + + /// \return value_ directly without going through HermesValue. + /// Used if the raw value_ was directly specified. + /// In particular, used by CallResult to check if a value is exceptional. + value_type unsafeGetValue() const { + return value_; + } + + /// Create a \c PseudoHandle from a value. + static PseudoHandle create(value_type value) { + return PseudoHandle(value); + } +}; + /// A HermesValue in the current GCScope which is trackable by the GC and will /// be correctly marked and updated if objects are moved. The value is valid /// while the owning GCScope object is alive. @@ -324,6 +446,12 @@ class MutableHandle : public Handle { return *this; } + MutableHandle &operator=(PseudoHandle &&other) { + set(other.get()); + other.invalidate(); + return *this; + } + MutableHandle &operator=(value_type value) { set(value); return *this; @@ -359,128 +487,6 @@ static_assert( sizeof(MutableHandle<>) == sizeof(HandleBase), "MutableHandle must be a thin wrapper on top of Handle"); -/// This class is used in performance-sensitive context in situations where we -/// want to encode in the function signature that allocations may be performed, -/// potentially moving the object, but we don't want to incur the cost of -/// always allocating a handle. The callee will allocate a handle internally if -/// it needs to. -/// -/// For example: -/// \code -/// bool checkFlag(PseudoHandle foo, Runtime *runtime) { -/// if (foo->cheapCheck()) -/// return true; -/// auto fooHandle = runtime->makeHandle(std::move(foo)); -/// return expensiveCheck(fooHandle, runtime); -/// } -/// \endcode -template -class PseudoHandle { - using traits_type = HermesValueTraits; - using value_type = typename traits_type::value_type; - using arrow_type = typename traits_type::arrow_type; - - template - friend class PseudoHandle; - - value_type value_; -#ifndef NDEBUG - bool valid_{true}; -#endif - - explicit PseudoHandle(value_type value) : value_(value) {} - - public: - PseudoHandle(const PseudoHandle &) = delete; - PseudoHandle &operator=(const PseudoHandle &) = delete; - -#ifndef NDEBUG - PseudoHandle(PseudoHandle &&hnd) : value_(hnd.value_), valid_(hnd.valid_) { - hnd.valid_ = false; - } - PseudoHandle &operator=(PseudoHandle &&hnd) { - value_ = hnd.value_; - valid_ = hnd.valid_; - hnd.valid_ = false; - return *this; - } -#else - PseudoHandle(PseudoHandle &&) = default; - PseudoHandle &operator=(PseudoHandle &&) = default; -#endif - - ~PseudoHandle() = default; - - PseudoHandle() : value_(traits_type::defaultValue()) {} - PseudoHandle(Handle handle) : value_(*handle) {} - - /// Conveniently construct PseudoHandle from a PinnedHermesValue - /// pointer. - explicit PseudoHandle(PinnedHermesValue *pvalue) : value_(*pvalue) { - static_assert( - std::is_same::value, - "This constructor can only be used for PseudoHandle"); - } - - PseudoHandle &operator=(Handle handle) { - value_ = *handle; -#ifndef NDEBUG - valid_ = true; -#endif - return *this; - } - - /// Zero-cost conversion between compatible types. - template < - typename U, - typename = typename std::enable_if::value_type, - typename PseudoHandle::value_type>::value>::type> - /* implicit */ PseudoHandle(PseudoHandle &&other) - : value_(static_cast(other.get())) { - other.invalidate(); - } - - void invalidate() { -#ifndef NDEBUG - valid_ = false; -#endif - } - - value_type get() const { - assert(valid_ && "Pseudo handle has been invalidated"); - return value_; - } - - arrow_type operator->() const { - assert(valid_ && "Pseudo handle has been invalidated"); - return traits_type::arrow(value_); - } - - explicit operator bool() const { - assert(valid_ && "Pseudo handle has been invalidated"); - return value_ != nullptr; - } - - /// \return the value encoded as HermesValue - HermesValue getHermesValue() const { - assert(valid_ && "Pseudo handle has been invalidated"); - return traits_type::encode(value_); - } - - /// \return value_ directly without going through HermesValue. - /// Used if the raw value_ was directly specified. - /// In particular, used by CallResult to check if a value is exceptional. - value_type unsafeGetValue() const { - return value_; - } - - /// Create a \c PseudoHandle from a value. - static PseudoHandle create(value_type value) { - return PseudoHandle(value); - } -}; - /// Create a \c PseudoHandle from a pointer. template inline PseudoHandle createPseudoHandle(T *ptr) {