diff options
Diffstat (limited to 'js/public/GCVariant.h')
-rw-r--r-- | js/public/GCVariant.h | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/js/public/GCVariant.h b/js/public/GCVariant.h new file mode 100644 index 0000000000..0c62f17c22 --- /dev/null +++ b/js/public/GCVariant.h @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef js_GCVariant_h +#define js_GCVariant_h + +#include "mozilla/Variant.h" + +#include <type_traits> + +#include "js/GCPolicyAPI.h" +#include "js/RootingAPI.h" +#include "js/TracingAPI.h" + +namespace JS { + +// These template specializations allow Variant to be used inside GC wrappers. +// +// When matching on GC wrappers around Variants, matching should be done on +// the wrapper itself. The matcher class's methods should take Handles or +// MutableHandles. For example, +// +// struct MyMatcher +// { +// using ReturnType = const char*; +// ReturnType match(HandleObject o) { return "object"; } +// ReturnType match(HandleScript s) { return "script"; } +// }; +// +// Rooted<Variant<JSObject*, JSScript*>> v(cx, someScript); +// MyMatcher mm; +// v.match(mm); +// +// If you get compile errors about inability to upcast subclasses (e.g., from +// NativeObject* to JSObject*) and are inside js/src, be sure to also include +// "gc/Policy.h". + +namespace detail { + +template <typename... Ts> +struct GCVariantImplementation; + +// The base case. +template <typename T> +struct GCVariantImplementation<T> { + template <typename ConcreteVariant> + static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) { + T& thing = v->template as<T>(); + GCPolicy<T>::trace(trc, &thing, name); + } + + template <typename Matcher, typename ConcreteVariant> + static typename Matcher::ReturnType match(Matcher& matcher, + Handle<ConcreteVariant> v) { + const T& thing = v.get().template as<T>(); + return matcher.match(Handle<T>::fromMarkedLocation(&thing)); + } + + template <typename Matcher, typename ConcreteVariant> + static typename Matcher::ReturnType match(Matcher& matcher, + MutableHandle<ConcreteVariant> v) { + T& thing = v.get().template as<T>(); + return matcher.match(MutableHandle<T>::fromMarkedLocation(&thing)); + } +}; + +// The inductive case. +template <typename T, typename... Ts> +struct GCVariantImplementation<T, Ts...> { + using Next = GCVariantImplementation<Ts...>; + + template <typename ConcreteVariant> + static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) { + if (v->template is<T>()) { + T& thing = v->template as<T>(); + GCPolicy<T>::trace(trc, &thing, name); + } else { + Next::trace(trc, v, name); + } + } + + template <typename Matcher, typename ConcreteVariant> + static typename Matcher::ReturnType match(Matcher& matcher, + Handle<ConcreteVariant> v) { + if (v.get().template is<T>()) { + const T& thing = v.get().template as<T>(); + return matcher.match(Handle<T>::fromMarkedLocation(&thing)); + } + return Next::match(matcher, v); + } + + template <typename Matcher, typename ConcreteVariant> + static typename Matcher::ReturnType match(Matcher& matcher, + MutableHandle<ConcreteVariant> v) { + if (v.get().template is<T>()) { + T& thing = v.get().template as<T>(); + return matcher.match(MutableHandle<T>::fromMarkedLocation(&thing)); + } + return Next::match(matcher, v); + } +}; + +} // namespace detail + +template <typename... Ts> +struct GCPolicy<mozilla::Variant<Ts...>> { + using Impl = detail::GCVariantImplementation<Ts...>; + + static void trace(JSTracer* trc, mozilla::Variant<Ts...>* v, + const char* name) { + Impl::trace(trc, v, name); + } + + static bool isValid(const mozilla::Variant<Ts...>& v) { + return v.match([](auto& v) { + return GCPolicy<std::remove_reference_t<decltype(v)>>::isValid(v); + }); + } +}; + +} // namespace JS + +namespace js { + +template <typename Wrapper, typename... Ts> +class WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper> { + using Impl = JS::detail::GCVariantImplementation<Ts...>; + using Variant = mozilla::Variant<Ts...>; + + const Variant& variant() const { + return static_cast<const Wrapper*>(this)->get(); + } + + public: + template <typename T> + bool is() const { + return variant().template is<T>(); + } + + template <typename T> + JS::Handle<T> as() const { + return Handle<T>::fromMarkedLocation(&variant().template as<T>()); + } + + template <typename Matcher> + typename Matcher::ReturnType match(Matcher& matcher) const { + return Impl::match(matcher, + JS::Handle<Variant>::fromMarkedLocation(&variant())); + } +}; + +template <typename Wrapper, typename... Ts> +class MutableWrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper> + : public WrappedPtrOperations<mozilla::Variant<Ts...>, Wrapper> { + using Impl = JS::detail::GCVariantImplementation<Ts...>; + using Variant = mozilla::Variant<Ts...>; + + const Variant& variant() const { + return static_cast<const Wrapper*>(this)->get(); + } + Variant& variant() { return static_cast<Wrapper*>(this)->get(); } + + public: + template <typename T> + JS::MutableHandle<T> as() { + return JS::MutableHandle<T>::fromMarkedLocation( + &variant().template as<T>()); + } + + template <typename Matcher> + typename Matcher::ReturnType match(Matcher& matcher) { + return Impl::match( + matcher, JS::MutableHandle<Variant>::fromMarkedLocation(&variant())); + } +}; + +} // namespace js + +#endif // js_GCVariant_h |