summaryrefslogtreecommitdiffstats
path: root/js/public/GCVariant.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/public/GCVariant.h182
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..43610b898b
--- /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 JS::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