#ifndef V8_OBJECTS_CASTING_H_
#define V8_OBJECTS_CASTING_H_
#include <type_traits>
#include "include/v8-source-location.h"
#include "src/base/logging.h"
#include "src/objects/tagged.h"
#include "src/sandbox/check.h"
namespace v8::internal {
template <typename To>
struct CastTraits;
template <typename T, typename U>
inline bool Is(Tagged<U> value) {
return CastTraits<T>::AllowFrom(value);
}
template <typename T, typename U>
inline bool Is(IndirectHandle<U> value);
template <typename T, typename U>
inline bool Is(DirectHandle<U> value);
template <typename T, typename U>
bool Is(MaybeIndirectHandle<U> value) = delete;
template <typename T, typename U>
bool Is(MaybeDirectHandle<U> value) = delete;
template <typename To, typename From>
inline Tagged<To> UncheckedCast(Tagged<From> value) {
return Tagged<To>(value.ptr());
}
template <typename To, typename From>
inline IndirectHandle<To> UncheckedCast(IndirectHandle<From> value);
template <typename To, typename From>
inline MaybeIndirectHandle<To> UncheckedCast(MaybeIndirectHandle<From> value);
template <typename To, typename From>
inline DirectHandle<To> UncheckedCast(DirectHandle<From> value);
template <typename To, typename From>
inline MaybeDirectHandle<To> UncheckedCast(MaybeDirectHandle<From> value);
template <template <typename> class Holder, typename To, typename From>
concept HasTryCastImplementation = requires(Holder<From> value) {
{ Is<To>(value) } -> std::same_as<bool>;
{ UncheckedCast<To>(value) } -> std::same_as<Holder<To>>;
};
template <typename To, typename From, template <typename> class Holder>
requires HasTryCastImplementation<Holder, To, From>
inline bool TryCast(Holder<From> value, Holder<To>* out) {
if (!Is<To>(value)) return false;
*out = UncheckedCast<To>(value);
return true;
}
#ifdef DEBUG
template <typename T>
bool GCAwareObjectTypeCheck(Tagged<Object> object, const Heap* heap);
#endif
template <typename T>
Tagged<T> GCSafeCast(Tagged<Object> object, const Heap* heap) {
DCHECK(GCAwareObjectTypeCheck<T>(object, heap));
return UncheckedCast<T>(object);
}
template <typename T, typename U>
inline bool NullOrIs(Tagged<U> value) {
return Is<T>(value);
}
template <typename T, typename U>
inline bool NullOrIs(IndirectHandle<U> value) {
return value.is_null() || Is<T>(value);
}
template <typename T, typename U>
inline bool NullOrIs(DirectHandle<U> value) {
return value.is_null() || Is<T>(value);
}
template <typename T, typename U>
inline bool NullOrIs(MaybeIndirectHandle<U> value) {
IndirectHandle<U> handle;
return !value.ToHandle(&handle) || Is<T>(handle);
}
template <typename T, typename U>
inline bool NullOrIs(MaybeDirectHandle<U> value) {
DirectHandle<U> handle;
return !value.ToHandle(&handle) || Is<T>(handle);
}
template <template <typename> class Holder, typename To, typename From>
concept HasCastImplementation = requires(Holder<From> value) {
{ NullOrIs<To>(value) } -> std::same_as<bool>;
{ UncheckedCast<To>(value) } -> std::same_as<Holder<To>>;
};
template <typename To, typename From, template <typename> class Holder>
requires HasCastImplementation<Holder, To, From>
inline Holder<To> TrustedCast(
Holder<From> value, SourceLocation loc = SourceLocation::CurrentIfDebug()) {
DCHECK_WITH_MSG_AND_LOC(NullOrIs<To>(value),
V8_PRETTY_FUNCTION_VALUE_OR("Cast type check"), loc);
return UncheckedCast<To>(value);
}
template <typename To, typename From, template <typename> class Holder>
requires HasCastImplementation<Holder, To, From>
inline Holder<To> SbxCast(
Holder<From> value, SourceLocation loc = SourceLocation::CurrentIfDebug()) {
static_assert(is_subtype_v<To, TrustedObject>,
"Type of untrusted objects cannot be checked reliably");
DCHECK_WITH_MSG_AND_LOC(NullOrIs<To>(value),
V8_PRETTY_FUNCTION_VALUE_OR("Cast type check"), loc);
SBXCHECK(NullOrIs<To>(value));
return UncheckedCast<To>(value);
}
template <typename To, typename From, template <typename> class Holder>
requires HasCastImplementation<Holder, To, From>
inline Holder<To> CheckedCast(
Holder<From> value, SourceLocation loc = SourceLocation::CurrentIfDebug()) {
DCHECK_WITH_MSG_AND_LOC(NullOrIs<To>(value),
V8_PRETTY_FUNCTION_VALUE_OR("Cast type check"), loc);
CHECK(NullOrIs<To>(value));
return UncheckedCast<To>(value);
}
template <typename To, typename From, template <typename> class Holder>
requires HasCastImplementation<Holder, To, From>
inline Holder<To> Cast(Holder<From> value,
SourceLocation loc = SourceLocation::CurrentIfDebug()) {
static_assert(
!is_subtype_v<To, TrustedObject> || is_subtype_v<From, To>,
"Down-casting to trusted objects is verboten. Pick one of:\n"
"* TryCast if the use is guarded by a test\n"
" if (Trusted t; TryCast(obj, &t)) { t->foo(); }\n"
"* (Checked|Sbx)Cast if the object is expected to be of a certain type\n"
" Tagged<Trusted> t = SbxCast<Trusted>(obj);\n"
"* TrustedCast if correct by construction (or other deprecated usage)\n"
" Tagged<Trusted> t = "
"TrustedCast<Trusted>(obj->ReadTrustedPointerField<kTrustedTag>(...);\n");
return TrustedCast<To>(value, loc);
}
template <typename To, typename From>
requires std::is_base_of_v<HeapObjectLayout, From>
inline Tagged<To> UncheckedCast(const From* value) {
return UncheckedCast<To>(Tagged(value));
}
template <typename To, typename From>
requires std::is_base_of_v<HeapObjectLayout, From>
inline Tagged<To> TrustedCast(const From* value) {
return TrustedCast<To>(Tagged(value));
}
template <typename To, typename From>
requires std::is_base_of_v<HeapObjectLayout, From>
inline Tagged<To> CheckedCast(const From* value) {
return CheckedCast<To>(Tagged(value));
}
template <typename To, typename From>
requires std::is_base_of_v<HeapObjectLayout, From>
inline Tagged<To> SbxCast(const From* value) {
return SbxCast<To>(Tagged(value));
}
template <typename To, typename From>
requires std::is_base_of_v<HeapObjectLayout, From>
inline Tagged<To> Cast(const From* value,
SourceLocation loc = SourceLocation::CurrentIfDebug()) {
return Cast<To>(Tagged(value), loc);
}
template <typename To, typename From>
requires(std::is_base_of_v<HeapObject, From>)
inline Tagged<To> UncheckedCast(From value) {
return UncheckedCast<To>(Tagged(value));
}
template <typename To, typename From>
requires(std::is_base_of_v<HeapObject, From>)
inline Tagged<To> TrustedCast(From value) {
return TrustedCast<To>(Tagged(value));
}
template <typename To, typename From>
requires(std::is_base_of_v<HeapObject, From>)
inline Tagged<To> CheckedCast(From value) {
return CheckedCast<To>(Tagged(value));
}
template <typename To, typename From>
requires(std::is_base_of_v<HeapObject, From>)
inline Tagged<To> SbxCast(From value) {
return SbxCast<To>(Tagged(value));
}
template <typename To, typename From>
requires(std::is_base_of_v<HeapObject, From>)
inline Tagged<To> Cast(From value,
SourceLocation loc = SourceLocation::CurrentIfDebug()) {
return Cast<To>(Tagged(value), loc);
}
template <typename T, typename U>
inline bool Is(Tagged<MaybeWeak<U>> value) {
if constexpr (!is_maybe_weak_v<T>) {
if (!value.IsStrongOrSmi()) return false;
return CastTraits<T>::AllowFrom(Tagged<U>(value.ptr()));
} else {
return CastTraits<T>::AllowFrom(value);
}
}
template <typename T, typename... U>
constexpr inline bool Is(Tagged<Union<U...>> value) {
using UnionU = Union<U...>;
if constexpr (is_subtype_v<UnionU, HeapObject>) {
return Is<T>(Tagged<HeapObject>(value));
} else if constexpr (is_subtype_v<UnionU, MaybeWeak<HeapObject>>) {
return Is<T>(Tagged<MaybeWeak<HeapObject>>(value));
} else if constexpr (is_subtype_v<UnionU, Object>) {
return Is<T>(Tagged<Object>(value));
} else {
static_assert(is_subtype_v<UnionU, MaybeWeak<Object>>);
return Is<T>(Tagged<MaybeWeak<Object>>(value));
}
}
template <typename T>
struct CastTraits<MaybeWeak<T>> {
template <typename U>
static bool AllowFrom(Tagged<U> value) {
if constexpr (is_maybe_weak_v<U>) {
if (value.IsCleared()) return true;
if (value.IsSmi()) {
return CastTraits<T>::AllowFrom(Tagged<Smi>(value.ptr()));
}
return CastTraits<T>::AllowFrom(MakeStrong(value));
} else {
return CastTraits<T>::AllowFrom(value);
}
}
};
template <>
struct CastTraits<Object> {
static inline bool AllowFrom(Tagged<Object> value) { return true; }
};
template <>
struct CastTraits<Smi> {
static inline bool AllowFrom(Tagged<Object> value) { return value.IsSmi(); }
static inline bool AllowFrom(Tagged<HeapObject> value) { return false; }
};
template <>
struct CastTraits<HeapObject> {
static inline bool AllowFrom(Tagged<Object> value) {
return value.IsHeapObject();
}
static inline bool AllowFrom(Tagged<HeapObject> value) { return true; }
};
}
#endif