1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CASTING_H_
6 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CASTING_H_
7
8 #include "third_party/blink/renderer/platform/wtf/assertions.h"
9
10 namespace blink {
11
12 // Helpers for downcasting in a class hierarchy.
13 //
14 // IsA<T>(x): returns true if |x| can be safely downcast to T*. Usage of this
15 // should not be common; if it is paired with a call to To<T>, consider
16 // using DynamicTo<T> instead (see below). Note that this also returns
17 // false if |x| is nullptr.
18 //
19 // To<T>(x): unconditionally downcasts and returns |x| as a T*. DCHECKs if the
20 // downcast is unsafe. Use when IsA<T>(x) is known to be true due to
21 // external invariants. If |x| is nullptr, returns nullptr.
22 //
23 // DynamicTo<T>(x): downcasts and returns |x| as a T* iff IsA<T>(x) is true,
24 // and nullptr otherwise. This is useful for combining a conditional
25 // branch on IsA<T>(x) and an invocation of To<T>(x), e.g.:
26 // if (IsA<DerivedClass>(x))
27 // To<DerivedClass>(x)->...
28 // can be written:
29 // if (auto* derived = DynamicTo<DerivedClass>(x))
30 // derived->...;
31 //
32 // Marking downcasts as safe is done by specializing the DowncastTraits
33 // template:
34 //
35 // template <>
36 // struct DowncastTraits<DerivedClass> {
37 // static bool AllowFrom(const BaseClass& b) {
38 // return b.IsDerivedClass();
39 // }
40 // static bool AllowFrom(const AnotherBaseClass& b) {
41 // return b.type() == AnotherBaseClass::kDerivedClassTag;
42 // }
43 // };
44 //
45 // int main() {
46 // BaseClass* base = CreateDerived();
47 // AnotherBaseClass* another_base = CreateDerived();
48 // UnrelatedClass* unrelated = CreateUnrelated();
49 //
50 // std::cout << std::boolalpha;
51 // std::cout << IsA<Derived>(base) << '\n'; // prints true
52 // std::cout << IsA<Derived>(another_base) << '\n'; // prints true
53 // std::cout << IsA<Derived>(unrelated) << '\n'; // prints false
54 // }
55 template <typename T>
56 struct DowncastTraits {
57 template <typename U>
AllowFromDowncastTraits58 static bool AllowFrom(const U&) {
59 static_assert(sizeof(U) == 0, "no downcast traits specialization for T");
60 }
61 };
62
63 // Returns true iff the conversion from Base to Derived is allowed. For the
64 // pointer overloads, returns false if the input pointer is nullptr.
65 template <typename Derived, typename Base>
IsA(const Base & from)66 bool IsA(const Base& from) {
67 return DowncastTraits<Derived>::AllowFrom(from);
68 }
69
70 template <typename Derived, typename Base>
IsA(const Base * from)71 bool IsA(const Base* from) {
72 return from && IsA<Derived>(*from);
73 }
74
75 template <typename Derived, typename Base>
IsA(Base & from)76 bool IsA(Base& from) {
77 return IsA<Derived>(static_cast<const Base&>(from));
78 }
79
80 template <typename Derived, typename Base>
IsA(Base * from)81 bool IsA(Base* from) {
82 return from && IsA<Derived>(*from);
83 }
84
85 // Unconditionally downcasts from Base to Derived. Internally, this asserts
86 // that |from| is a Derived to help catch bad casts in testing/fuzzing. For the
87 // pointer overloads, returns nullptr if the input pointer is nullptr.
88 template <typename Derived, typename Base>
To(const Base & from)89 const Derived& To(const Base& from) {
90 SECURITY_DCHECK(IsA<Derived>(from));
91 return static_cast<const Derived&>(from);
92 }
93
94 template <typename Derived, typename Base>
To(const Base * from)95 const Derived* To(const Base* from) {
96 return from ? &To<Derived>(*from) : nullptr;
97 }
98
99 template <typename Derived, typename Base>
To(Base & from)100 Derived& To(Base& from) {
101 SECURITY_DCHECK(IsA<Derived>(from));
102 return static_cast<Derived&>(from);
103 }
104 template <typename Derived, typename Base>
To(Base * from)105 Derived* To(Base* from) {
106 return from ? &To<Derived>(*from) : nullptr;
107 }
108
109 // Safely downcasts from Base to Derived. If |from| is not a Derived, returns
110 // nullptr; otherwise, downcasts from Base to Derived. For the pointer
111 // overloads, returns nullptr if the input pointer is nullptr.
112 template <typename Derived, typename Base>
DynamicTo(const Base * from)113 const Derived* DynamicTo(const Base* from) {
114 return IsA<Derived>(from) ? To<Derived>(from) : nullptr;
115 }
116
117 template <typename Derived, typename Base>
DynamicTo(const Base & from)118 const Derived* DynamicTo(const Base& from) {
119 return IsA<Derived>(from) ? &To<Derived>(from) : nullptr;
120 }
121
122 template <typename Derived, typename Base>
DynamicTo(Base * from)123 Derived* DynamicTo(Base* from) {
124 return IsA<Derived>(from) ? To<Derived>(from) : nullptr;
125 }
126
127 template <typename Derived, typename Base>
DynamicTo(Base & from)128 Derived* DynamicTo(Base& from) {
129 return IsA<Derived>(from) ? &To<Derived>(from) : nullptr;
130 }
131
132 } // namespace blink
133
134 #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_CASTING_H_
135