/* -*- 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 #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> 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 struct GCVariantImplementation; // The base case. template struct GCVariantImplementation { template static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) { T& thing = v->template as(); GCPolicy::trace(trc, &thing, name); } template static typename Matcher::ReturnType match(Matcher& matcher, Handle v) { const T& thing = v.get().template as(); return matcher.match(Handle::fromMarkedLocation(&thing)); } template static typename Matcher::ReturnType match(Matcher& matcher, MutableHandle v) { T& thing = v.get().template as(); return matcher.match(MutableHandle::fromMarkedLocation(&thing)); } }; // The inductive case. template struct GCVariantImplementation { using Next = GCVariantImplementation; template static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) { if (v->template is()) { T& thing = v->template as(); GCPolicy::trace(trc, &thing, name); } else { Next::trace(trc, v, name); } } template static typename Matcher::ReturnType match(Matcher& matcher, Handle v) { if (v.get().template is()) { const T& thing = v.get().template as(); return matcher.match(Handle::fromMarkedLocation(&thing)); } return Next::match(matcher, v); } template static typename Matcher::ReturnType match(Matcher& matcher, MutableHandle v) { if (v.get().template is()) { T& thing = v.get().template as(); return matcher.match(MutableHandle::fromMarkedLocation(&thing)); } return Next::match(matcher, v); } }; } // namespace detail template struct GCPolicy> { using Impl = detail::GCVariantImplementation; static void trace(JSTracer* trc, mozilla::Variant* v, const char* name) { Impl::trace(trc, v, name); } static bool isValid(const mozilla::Variant& v) { return v.match([](auto& v) { return GCPolicy>::isValid(v); }); } }; } // namespace JS namespace js { template class WrappedPtrOperations, Wrapper> { using Impl = JS::detail::GCVariantImplementation; using Variant = mozilla::Variant; const Variant& variant() const { return static_cast(this)->get(); } public: template bool is() const { return variant().template is(); } template JS::Handle as() const { return Handle::fromMarkedLocation(&variant().template as()); } template typename Matcher::ReturnType match(Matcher& matcher) const { return Impl::match(matcher, JS::Handle::fromMarkedLocation(&variant())); } }; template class MutableWrappedPtrOperations, Wrapper> : public WrappedPtrOperations, Wrapper> { using Impl = JS::detail::GCVariantImplementation; using Variant = mozilla::Variant; const Variant& variant() const { return static_cast(this)->get(); } Variant& variant() { return static_cast(this)->get(); } public: template JS::MutableHandle as() { return JS::MutableHandle::fromMarkedLocation( &variant().template as()); } template typename Matcher::ReturnType match(Matcher& matcher) { return Impl::match( matcher, JS::MutableHandle::fromMarkedLocation(&variant())); } }; } // namespace js #endif // js_GCVariant_h