// 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 "base/compiler_specific.h"
#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) {
  // This looks silly, but the body of this function is often inlined into
  // the caller, which does something like `if (auto* y = DynamicTo<Y>(x))`.
  // The result looks something like `if (auto* y = IsA<Y> ? &x : nullptr)`...
  // and with `-fno-delete-null-pointer-checks`, clang emits a test that `&x`
  // is not null even though that is never written explicitly.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-undefined-compare"
  UNSAFE_ASSUME(&from != nullptr);
#pragma clang diagnostic pop
  // 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) {
  // This looks silly, but the body of this function is often inlined into
  // the caller, which does something like `if (auto* y = DynamicTo<Y>(x))`.
  // The result looks something like `if (auto* y = IsA<Y> ? &x : nullptr)`...
  // and with `-fno-delete-null-pointer-checks`, clang emits a test that `&x`
  // is not null even though that is never written explicitly.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-undefined-compare"
  UNSAFE_ASSUME(&from != nullptr);
#pragma clang diagnostic pop
  // 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_
