/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/objects/casting.h
472 строки
18 KB
Michaël Zasso
deps: update V8 to 14.6.202.33
24 апр 2026, 19:01
Не верифицирован
24 апр 2026, 19:01
f1e0b83
Код
Авторство
О чём код?
// Copyright 2024 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 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 { // CastTraits<T> is a type trait that defines type checking behaviour for // tagged object casting. The expected specialization is: // // template<> // struct CastTraits<SomeObject> { // template<typename From> // static bool AllowFrom(Tagged<From> value) { // return IsSomeObject(value); // } // }; // // or, likely, just specializations of AllowFrom for Object and HeapObject, // under the assumption that the HeapObject implementation is the same for all // HeapObjects and the Object implementation has additional overhead in Smi // checks. // // struct CastTraits<Object> { // static bool AllowFrom(Tagged<HeapObject> value) { // return IsSomeObject(value); // } // static bool AllowFrom(Tagged<Object> value) { // return IsSomeObject(value); // } // }; // template <typename To> struct CastTraits; template <typename T> concept NotGCedType = !std::is_base_of_v<HeapObject, T> && !std::is_base_of_v<HeapObjectLayout, T>; // `Is<T>(value)` checks whether `value` is a tagged object of type `T`. 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); // Is<T> checks for MaybeHandles are forbidden -- you need to first convert to // a Handle with ToHandle or ToHandleChecked. template <typename T, typename U> bool Is(MaybeIndirectHandle<U> value) = delete; template <typename T, typename U> bool Is(MaybeDirectHandle<U> value) = delete; // C++ versions for `Is<T>` that accept pointers and references. template <typename To, typename From> requires NotGCedType<To> bool Is(const From& value) { return CastTraits<std::remove_const_t<To>>::AllowFrom(value); } template <typename To, typename From> requires NotGCedType<To> bool Is(From& value) { return Is<To>(const_cast<const From&>(value)); } template <typename To, typename From> requires NotGCedType<To> bool Is(const From* value) { return value && Is<To>(*value); } template <typename To, typename From> requires NotGCedType<To> bool Is(From* value) { return value && Is<To>(*static_cast<const From*>(value)); } // `UncheckedCast<T>(value)` casts `value` to a tagged object of type `T`, // without checking the type of value. 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); // HasCastImplementation is a concept that checks for the existence of // Is<To>(Holder<From>) and UncheckedCast<To>(Holder<From>). 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 Derived, typename Base> concept HasCppRefTryCastImplementation = NotGCedType<Derived> && requires(Base& value) { { Is<Derived>(value) } -> std::same_as<bool>; }; template <typename Derived, typename Base> concept HasCppPointerTryCastImplementation = NotGCedType<Derived> && requires(Base* value) { { Is<Derived>(value) } -> std::same_as<bool>; }; // `TryCast<T>(value, &out)` casts `value` to a tagged object of type `T` and // writes the value to `out`, returning true if the cast succeeded and false if // it failed. 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; } template <typename Derived, typename Base> requires HasCppRefTryCastImplementation<Derived, Base> inline bool TryCast(Base& value, Derived** out) { if (!Is<Derived>(value)) return false; *out = static_cast<Derived*>(&value); return true; } template <typename Derived, typename Base> requires HasCppPointerTryCastImplementation<Derived, Base> inline bool TryCast(Base* value, Derived** out) { if (!Is<Derived>(value)) return false; *out = static_cast<Derived*>(value); return true; } #ifdef DEBUG template <typename T> bool GCAwareObjectTypeCheck(Tagged<Object> object, const Heap* heap); #endif // DEBUG // `GCSafeCast<T>(value)` casts `object` to a tagged object of type `T` and // should be used when the cast can be called from within a GC. The case all // includes a debug check that `object` is either a tagged object of type `T`, // or one of few special cases possible during GC (see GCAwareObjectTypeCheck): // 1) `object` was already evacuated and the forwarding address refers to a // tagged object of type `T`. // 2) During Scavenger, `object` is a large object. // 3) During a conservative Scavenger, `object` is a pinned object. template <typename T> Tagged<T> GCSafeCast(Tagged<Object> object, const Heap* heap) { DCHECK(GCAwareObjectTypeCheck<T>(object, heap)); return UncheckedCast<T>(object); } // Check if a value holder is null -- used for Cast DCHECKs, so that null // handles pass the check. template <typename T, typename U> inline bool NullOrIs(Tagged<U> value) { // Raw tagged values are not allowed to be null, just check Is<T>. 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 <typename Derived, typename Base> bool NullOrIs(Base& value) { return Is<Derived>(value); } template <typename Derived, typename Base> bool NullOrIs(Base* value) { return !value || Is<Derived>(*value); } // HasCastImplementation is a concept that checks for the existence of // NullOrIs<To>(Holder<From>) and UncheckedCast<To>(Holder<From>). 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 Derived, typename Base> concept HasCppRefCastImplementation = NotGCedType<Derived> && requires(Base& value) { { Is<Derived>(value) } -> std::same_as<bool>; }; template <typename Derived, typename Base> concept HasCppPointerCastImplementation = NotGCedType<Derived> && requires(Base* value) { { Is<Derived>(value) } -> std::same_as<bool>; }; // `TrustedCast<T>(value)` casts `value` to a tagged object of type `T`, only // doing a debug check that `value` is a tagged object of type `T`. Down-casts // to trusted objects are allowed for callers which already ensured their // correctness. Null-valued holders are allowed. 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()) { // Casting pointers to in-sandbox objects is allowed even with an active // DisallowSandboxAccess scope (as casting itself doesn't access any memory). // However, in debug builds the DCHECK below will need to access the Map of // the object, so we need to temporarily enable sandbox access. If no // DisallowSandboxAccess scope is active, this is a no-op. DCHECK_WITH_SANDBOX_ACCESS_AND_MSG_AND_LOC( NullOrIs<To>(value), V8_PRETTY_FUNCTION_VALUE_OR("Cast type check"), loc); return UncheckedCast<To>(value); } // `SbxCast<T>(value)` casts `value` to a tagged object of trusted type `T`, // with a sandbox-only dynamic type check ensuring that `value` is a tagged // object of type `T`. Null-valued holders are allowed. 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()) { // Under the attacker model of the sandbox we cannot trust the type of // in-sandbox objects for sandbox checks as these objects can be arbitrarily // tampered with. 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 Derived, typename Base> requires HasCppRefCastImplementation<Derived, Base> inline Derived& SbxCast(Base& from, SourceLocation loc = SourceLocation::CurrentIfDebug()) { DCHECK_WITH_MSG_AND_LOC(NullOrIs<Derived>(from), V8_PRETTY_FUNCTION_VALUE_OR("Cast type check"), loc); SBXCHECK(NullOrIs<Derived>(from)); return static_cast<Derived&>(from); } template <typename Derived, typename Base> requires HasCppPointerCastImplementation<Derived, Base> inline Derived* SbxCast(Base* from, SourceLocation loc = SourceLocation::CurrentIfDebug()) { DCHECK_WITH_MSG_AND_LOC(NullOrIs<Derived>(from), V8_PRETTY_FUNCTION_VALUE_OR("Cast type check"), loc); SBXCHECK(NullOrIs<Derived>(from)); return static_cast<Derived*>(from); } // `CheckedCast<T>(value)` casts `value` to a tagged object of type `T`, // with a always-on dynamic type check ensuring that `value` is a tagged object // of type `T`. Null-valued holders are allowed. 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 Derived, typename Base> requires HasCppRefCastImplementation<Derived, Base> inline Derived& CheckedCast( Base& from, SourceLocation loc = SourceLocation::CurrentIfDebug()) { DCHECK_WITH_MSG_AND_LOC(NullOrIs<Derived>(from), V8_PRETTY_FUNCTION_VALUE_OR("Cast type check"), loc); CHECK(NullOrIs<Derived>(from)); return static_cast<Derived&>(from); } template <typename Derived, typename Base> requires HasCppPointerCastImplementation<Derived, Base> inline Derived* CheckedCast( Base* from, SourceLocation loc = SourceLocation::CurrentIfDebug()) { DCHECK_WITH_MSG_AND_LOC(NullOrIs<Derived>(from), V8_PRETTY_FUNCTION_VALUE_OR("Cast type check"), loc); CHECK(NullOrIs<Derived>(from)); return static_cast<Derived*>(from); } // `Cast<T>(value)` casts `value` to a tagged object of type `T`, with a debug // check that `value` is a tagged object of type `T`. Null-valued holders are // allowed. Down-casts to trusted objects are not allowed to prevent mistakes. 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); } // TODO(leszeks): Figure out a way to make these cast to actual pointers rather // than Tagged. 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 Derived, typename Base> requires HasCppRefCastImplementation<Derived, Base> inline Derived& Cast(Base& from, SourceLocation loc = SourceLocation::CurrentIfDebug()) { DCHECK_WITH_MSG_AND_LOC(NullOrIs<Derived>(from), V8_PRETTY_FUNCTION_VALUE_OR("Cast type check"), loc); return static_cast<Derived&>(from); } template <typename Derived, typename Base> requires HasCppPointerCastImplementation<Derived, Base> inline Derived* Cast(Base* from, SourceLocation loc = SourceLocation::CurrentIfDebug()) { DCHECK_WITH_MSG_AND_LOC(NullOrIs<Derived>(from), V8_PRETTY_FUNCTION_VALUE_OR("Cast type check"), loc); return static_cast<Derived*>(from); } // `Is<T>(maybe_weak_value)` specialization for possible weak values and strong // target `T`, that additionally first checks whether `maybe_weak_value` is // actually a strong value (or a Smi, which can't be weak). template <typename T, typename U> inline bool Is(Tagged<MaybeWeak<U>> value) { // Cast from maybe weak to strong needs to be strong or smi. if constexpr (!is_maybe_weak_v<T>) { if (!value.IsStrongOrSmi()) return false; return CastTraits<T>::AllowFrom(Tagged<U>(value.ptr())); } else { // Dispatches to CastTraits<MaybeWeak<T>> below. 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)); } } // Specialization for maybe weak cast targets, which first converts the incoming // value to a strong reference and then checks if the cast to the strong T // is allowed. Cleared weak references always return true. template <typename T> struct CastTraits<MaybeWeak<T>> { template <typename U> static bool AllowFrom(Tagged<U> value) { if constexpr (is_maybe_weak_v<U>) { // Cleared values are always ok. if (value.IsCleared()) return true; // TODO(leszeks): Skip Smi check for values that are known to not be Smi. 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; } }; } // namespace v8::internal #endif // V8_OBJECTS_CASTING_H_