Skip to content

Commit

Permalink
Add MutableHandle operator= from PseudoHandle.
Browse files Browse the repository at this point in the history
Summary:
As we make more APIs return `PseudoHandle` instead of raw `HermesValue`s,
it's nice to have some more convenient means to assign to `MutableHandle`.

Add the `PseudoHandle &&` version of `operator=`. This requires
moving `PseudoHandle` above `MutableHandle` in the file, which can
be done without any complications.

Reviewed By: dulinriley

Differential Revision: D19623983

fbshipit-source-id: 9383b67ea4585096893245c3d5afc96e7d8310be
  • Loading branch information
avp authored and facebook-github-bot committed Feb 26, 2020
1 parent 8f71bce commit 5cae526
Showing 1 changed file with 128 additions and 122 deletions.
250 changes: 128 additions & 122 deletions include/hermes/VM/Handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,128 @@ class Handle;
template <typename T>
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> foo, Runtime *runtime) {
/// if (foo->cheapCheck())
/// return true;
/// auto fooHandle = runtime->makeHandle(std::move(foo));
/// return expensiveCheck(fooHandle, runtime);
/// }
/// \endcode
template <typename T = HermesValue>
class PseudoHandle {
using traits_type = HermesValueTraits<T>;
using value_type = typename traits_type::value_type;
using arrow_type = typename traits_type::arrow_type;

template <class U>
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<T> handle) : value_(*handle) {}

/// Conveniently construct PseudoHandle<HermesValue> from a PinnedHermesValue
/// pointer.
explicit PseudoHandle(PinnedHermesValue *pvalue) : value_(*pvalue) {
static_assert(
std::is_same<value_type, HermesValue>::value,
"This constructor can only be used for PseudoHandle<HermesValue>");
}

PseudoHandle &operator=(Handle<T> handle) {
value_ = *handle;
#ifndef NDEBUG
valid_ = true;
#endif
return *this;
}

/// Zero-cost conversion between compatible types.
template <
typename U,
typename = typename std::enable_if<std::is_convertible<
typename PseudoHandle<U>::value_type,
typename PseudoHandle<T>::value_type>::value>::type>
/* implicit */ PseudoHandle(PseudoHandle<U> &&other)
: value_(static_cast<value_type>(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<T> create(value_type value) {
return PseudoHandle<T>(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.
Expand Down Expand Up @@ -324,6 +446,12 @@ class MutableHandle : public Handle<T> {
return *this;
}

MutableHandle &operator=(PseudoHandle<T> &&other) {
set(other.get());
other.invalidate();
return *this;
}

MutableHandle &operator=(value_type value) {
set(value);
return *this;
Expand Down Expand Up @@ -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> foo, Runtime *runtime) {
/// if (foo->cheapCheck())
/// return true;
/// auto fooHandle = runtime->makeHandle(std::move(foo));
/// return expensiveCheck(fooHandle, runtime);
/// }
/// \endcode
template <typename T = HermesValue>
class PseudoHandle {
using traits_type = HermesValueTraits<T>;
using value_type = typename traits_type::value_type;
using arrow_type = typename traits_type::arrow_type;

template <class U>
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<T> handle) : value_(*handle) {}

/// Conveniently construct PseudoHandle<HermesValue> from a PinnedHermesValue
/// pointer.
explicit PseudoHandle(PinnedHermesValue *pvalue) : value_(*pvalue) {
static_assert(
std::is_same<value_type, HermesValue>::value,
"This constructor can only be used for PseudoHandle<HermesValue>");
}

PseudoHandle &operator=(Handle<T> handle) {
value_ = *handle;
#ifndef NDEBUG
valid_ = true;
#endif
return *this;
}

/// Zero-cost conversion between compatible types.
template <
typename U,
typename = typename std::enable_if<std::is_convertible<
typename PseudoHandle<U>::value_type,
typename PseudoHandle<T>::value_type>::value>::type>
/* implicit */ PseudoHandle(PseudoHandle<U> &&other)
: value_(static_cast<value_type>(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<T> create(value_type value) {
return PseudoHandle<T>(value);
}
};

/// Create a \c PseudoHandle from a pointer.
template <typename T>
inline PseudoHandle<T> createPseudoHandle(T *ptr) {
Expand Down

0 comments on commit 5cae526

Please sign in to comment.