GCVariant.h (5576B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef js_GCVariant_h 8 #define js_GCVariant_h 9 10 #include "mozilla/Variant.h" 11 12 #include <type_traits> 13 14 #include "js/GCPolicyAPI.h" 15 #include "js/RootingAPI.h" 16 #include "js/TracingAPI.h" 17 18 namespace JS { 19 20 // These template specializations allow Variant to be used inside GC wrappers. 21 // 22 // When matching on GC wrappers around Variants, matching should be done on 23 // the wrapper itself. The matcher class's methods should take Handles or 24 // MutableHandles. For example, 25 // 26 // struct MyMatcher 27 // { 28 // using ReturnType = const char*; 29 // ReturnType match(HandleObject o) { return "object"; } 30 // ReturnType match(HandleScript s) { return "script"; } 31 // }; 32 // 33 // Rooted<Variant<JSObject*, JSScript*>> v(cx, someScript); 34 // MyMatcher mm; 35 // v.match(mm); 36 // 37 // If you get compile errors about inability to upcast subclasses (e.g., from 38 // NativeObject* to JSObject*) and are inside js/src, be sure to also include 39 // "gc/Policy.h". 40 41 namespace detail { 42 43 template <typename... Ts> 44 struct GCVariantImplementation; 45 46 // The base case. 47 template <typename T> 48 struct GCVariantImplementation<T> { 49 template <typename ConcreteVariant> 50 static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) { 51 T& thing = v->template as<T>(); 52 GCPolicy<T>::trace(trc, &thing, name); 53 } 54 55 template <typename Matcher, typename ConcreteVariant> 56 static typename Matcher::ReturnType match(Matcher& matcher, 57 Handle<ConcreteVariant> v) { 58 const T& thing = v.get().template as<T>(); 59 return matcher.match(Handle<T>::fromMarkedLocation(&thing)); 60 } 61 62 template <typename Matcher, typename ConcreteVariant> 63 static typename Matcher::ReturnType match(Matcher& matcher, 64 MutableHandle<ConcreteVariant> v) { 65 T& thing = v.get().template as<T>(); 66 return matcher.match(MutableHandle<T>::fromMarkedLocation(&thing)); 67 } 68 }; 69 70 // The inductive case. 71 template <typename T, typename... Ts> 72 struct GCVariantImplementation<T, Ts...> { 73 using Next = GCVariantImplementation<Ts...>; 74 75 template <typename ConcreteVariant> 76 static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) { 77 if (v->template is<T>()) { 78 T& thing = v->template as<T>(); 79 GCPolicy<T>::trace(trc, &thing, name); 80 } else { 81 Next::trace(trc, v, name); 82 } 83 } 84 85 template <typename Matcher, typename ConcreteVariant> 86 static typename Matcher::ReturnType match(Matcher& matcher, 87 Handle<ConcreteVariant> v) { 88 if (v.get().template is<T>()) { 89 const T& thing = v.get().template as<T>(); 90 return matcher.match(Handle<T>::fromMarkedLocation(&thing)); 91 } 92 return Next::match(matcher, v); 93 } 94 95 template <typename Matcher, typename ConcreteVariant> 96 static typename Matcher::ReturnType match(Matcher& matcher, 97 MutableHandle<ConcreteVariant> v) { 98 if (v.get().template is<T>()) { 99 T& thing = v.get().template as<T>(); 100 return matcher.match(MutableHandle<T>::fromMarkedLocation(&thing)); 101 } 102 return Next::match(matcher, v); 103 } 104 }; 105 106 } // namespace detail 107 108 template <typename... Ts> 109 struct GCPolicy<mozilla::Variant<Ts...>> { 110 using Impl = detail::GCVariantImplementation<Ts...>; 111 112 static void trace(JSTracer* trc, mozilla::Variant<Ts...>* v, 113 const char* name) { 114 Impl::trace(trc, v, name); 115 } 116 117 static bool isValid(const mozilla::Variant<Ts...>& v) { 118 return v.match([](auto& v) { 119 return GCPolicy<std::remove_reference_t<decltype(v)>>::isValid(v); 120 }); 121 } 122 }; 123 124 } // namespace JS 125 126 namespace js { 127 128 template <typename Wrapper, typename... Ts> 129 class WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper> { 130 using Impl = JS::detail::GCVariantImplementation<Ts...>; 131 using Variant = mozilla::Variant<Ts...>; 132 133 const Variant& variant() const { 134 return static_cast<const Wrapper*>(this)->get(); 135 } 136 137 public: 138 template <typename T> 139 bool is() const { 140 return variant().template is<T>(); 141 } 142 143 template <typename T> 144 JS::Handle<T> as() const { 145 return JS::Handle<T>::fromMarkedLocation(&variant().template as<T>()); 146 } 147 148 template <typename Matcher> 149 typename Matcher::ReturnType match(Matcher& matcher) const { 150 return Impl::match(matcher, 151 JS::Handle<Variant>::fromMarkedLocation(&variant())); 152 } 153 }; 154 155 template <typename Wrapper, typename... Ts> 156 class MutableWrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper> 157 : public WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper> { 158 using Impl = JS::detail::GCVariantImplementation<Ts...>; 159 using Variant = mozilla::Variant<Ts...>; 160 161 const Variant& variant() const { 162 return static_cast<const Wrapper*>(this)->get(); 163 } 164 Variant& variant() { return static_cast<Wrapper*>(this)->get(); } 165 166 public: 167 template <typename T> 168 JS::MutableHandle<T> as() { 169 return JS::MutableHandle<T>::fromMarkedLocation( 170 &variant().template as<T>()); 171 } 172 173 template <typename Matcher> 174 typename Matcher::ReturnType match(Matcher& matcher) { 175 return Impl::match( 176 matcher, JS::MutableHandle<Variant>::fromMarkedLocation(&variant())); 177 } 178 }; 179 180 } // namespace js 181 182 #endif // js_GCVariant_h