/* -*- 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