gecko-dev/js/public/GCVariant.h

183 lines
5.4 KiB
C++

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