blob: 6d66f3f7468d2ee5ea183e6da4f47cd74128eb4a [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CASTING_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CASTING_H_
#include <concepts>
#include <type_traits>
#include "third_party/blink/renderer/platform/wtf/assertions.h"
namespace blink {
// Helpers for downcasting in a class hierarchy.
//
// IsA<T>(x): returns true if |x| can be safely downcast to T*. Usage of this
// should not be common; if it is paired with a call to To<T>, consider
// using DynamicTo<T> instead (see below). Note that this also returns
// false if |x| is nullptr.
//
// To<T>(x): unconditionally downcasts and returns |x| as a T*. CHECKs if the
// downcast is unsafe. Use when IsA<T>(x) is known to be true due to
// external invariants and not on a performance sensitive path.
// If |x| is nullptr, returns nullptr.
//
// DynamicTo<T>(x): downcasts and returns |x| as a T* iff IsA<T>(x) is true,
// and nullptr otherwise. This is useful for combining a conditional
// branch on IsA<T>(x) and an invocation of To<T>(x), e.g.:
// if (IsA<DerivedClass>(x))
// To<DerivedClass>(x)->...
// can be written:
// if (auto* derived = DynamicTo<DerivedClass>(x))
// derived->...;
//
// UnsafeTo<T>(x): unconditionally downcasts and returns |x| as a T*. DCHECKs
// if the downcast is unsafe. Use when IsA<T>(x) is known to be true due
// to external invariants. Prefer To<T> over this method, but this is ok
// to use in performance sensitive code. If |x| is nullptr, returns
// nullptr.
//
// Marking downcasts as safe is done by specializing the DowncastTraits
// template:
//
// template <>
// struct DowncastTraits<DerivedClass> {
// static bool AllowFrom(const BaseClass& b) {
// return b.IsDerivedClass();
// }
// static bool AllowFrom(const AnotherBaseClass& b) {
// return b.type() == AnotherBaseClass::kDerivedClassTag;
// }
// };
//
// int main() {
// BaseClass* base = CreateDerived();
// AnotherBaseClass* another_base = CreateDerived();
// UnrelatedClass* unrelated = CreateUnrelated();
//
// std::cout << std::boolalpha;
// std::cout << IsA<Derived>(base) << '\n'; // prints true
// std::cout << IsA<Derived>(another_base) << '\n'; // prints true
// std::cout << IsA<Derived>(unrelated) << '\n'; // prints false
// }
template <typename Derived>
struct DowncastTraits;
namespace internal {
template <typename Derived, typename Base>
struct DowncastTraitsHelper {
static_assert(sizeof(Derived) == 0,
"Unknown type, this error typically means you need to include "
"the header of the type being cast to.");
};
template <typename Derived, typename Base>
requires(!std::is_base_of_v<Derived, Base>)
struct DowncastTraitsHelper<Derived, Base> {
static bool AllowFrom(const Base& from) {
return DowncastTraits<Derived>::AllowFrom(from);
}
};
// If Derived is actually a base class of Base, unconditionally return true to
// skip the type checks.
template <typename Derived, typename Base>
requires(std::is_base_of_v<Derived, Base>)
struct DowncastTraitsHelper<Derived, Base> {
static bool AllowFrom(const Base&) { return true; }
};
} // namespace internal
// Returns true iff the conversion from Base to Derived is allowed. For the
// pointer overloads, returns false if the input pointer is nullptr.
template <typename Derived, typename Base>
bool IsA(const Base& from) {
static_assert(std::is_base_of_v<Base, Derived>, "Unnecessary type check");
return internal::DowncastTraitsHelper<Derived, const Base>::AllowFrom(from);
}
template <typename Derived, typename Base>
bool IsA(const Base* from) {
static_assert(std::is_base_of_v<Base, Derived>, "Unnecessary type check");
return from && IsA<Derived>(*from);
}
template <typename Derived, typename Base>
bool IsA(Base& from) {
static_assert(std::is_base_of_v<Base, Derived>, "Unnecessary type check");
return internal::DowncastTraitsHelper<Derived, const Base>::AllowFrom(
const_cast<const Base&>(from));
}
template <typename Derived, typename Base>
bool IsA(Base* from) {
static_assert(std::is_base_of_v<Base, Derived>, "Unnecessary type check");
return from && IsA<Derived>(*from);
}
// Unconditionally downcasts from Base to Derived. Internally, this asserts that
// |from| is a Derived to help catch bad casts. For the pointer overloads,
// returns nullptr if the input pointer is nullptr.
template <typename Derived, typename Base>
const Derived& To(const Base& from) {
CHECK(IsA<Derived>(from));
return static_cast<const Derived&>(from);
}
template <typename Derived, typename Base>
const Derived* To(const Base* from) {
return from ? &To<Derived>(*from) : nullptr;
}
template <typename Derived, typename Base>
Derived& To(Base& from) {
CHECK(IsA<Derived>(from));
return static_cast<Derived&>(from);
}
template <typename Derived, typename Base>
Derived* To(Base* from) {
return from ? &To<Derived>(*from) : nullptr;
}
// Safely downcasts from Base to Derived. If |from| is not a Derived, returns
// nullptr; otherwise, downcasts from Base to Derived. For the pointer
// overloads, returns nullptr if the input pointer is nullptr.
template <typename Derived, typename Base>
const Derived* DynamicTo(const Base* from) {
// TOOD(https://crbug.com/1449302): Figure out why IsA<T> + To<T> does not
// optimize correctly.
return IsA<Derived>(from) ? static_cast<const Derived*>(from) : nullptr;
}
template <typename Derived, typename Base>
const Derived* DynamicTo(const Base& from) {
// TOOD(https://crbug.com/1449302): Figure out why IsA<T> + To<T> does not
// optimize correctly.
return IsA<Derived>(from) ? &static_cast<const Derived&>(from) : nullptr;
}
template <typename Derived, typename Base>
Derived* DynamicTo(Base* from) {
// TOOD(https://crbug.com/1449302): Figure out why IsA<T> + To<T> does not
// optimize correctly.
return IsA<Derived>(from) ? static_cast<Derived*>(from) : nullptr;
}
template <typename Derived, typename Base>
Derived* DynamicTo(Base& from) {
// TOOD(https://crbug.com/1449302): Figure out why IsA<T> + To<T> does not
// optimize correctly.
return IsA<Derived>(from) ? &static_cast<Derived&>(from) : nullptr;
}
// Unconditionally downcasts from Base to Derived. Internally, this asserts
// that |from| is a Derived to help catch bad casts in testing/fuzzing. For the
// pointer overloads, returns nullptr if the input pointer is nullptr.
template <typename Derived, typename Base>
const Derived& UnsafeTo(const Base& from) {
SECURITY_DCHECK(IsA<Derived>(from));
return static_cast<const Derived&>(from);
}
template <typename Derived, typename Base>
const Derived* UnsafeTo(const Base* from) {
return from ? &UnsafeTo<Derived>(*from) : nullptr;
}
template <typename Derived, typename Base>
Derived& UnsafeTo(Base& from) {
SECURITY_DCHECK(IsA<Derived>(from));
return static_cast<Derived&>(from);
}
template <typename Derived, typename Base>
Derived* UnsafeTo(Base* from) {
return from ? &UnsafeTo<Derived>(*from) : nullptr;
}
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CASTING_H_