Bug 1254453 - Support using Variant inside GC wrappers. (r=terrence)
authorShu-yu Guo <shu@rfrn.org>
Fri, 11 Mar 2016 21:43:20 -0800
changeset 339746 92e23bc3f31d2b4e33575bc5448e1f0e3ef13b4e
parent 339745 3e871f2d5b4bb29d788201568d68fe48a84113f9
child 339747 5ae46c02868df2b56ff6224554d9f2358a947188
push id12803
push userjbeich@FreeBSD.org
push dateSun, 13 Mar 2016 09:48:54 +0000
reviewersterrence
bugs1254453
milestone48.0a1
Bug 1254453 - Support using Variant inside GC wrappers. (r=terrence)
js/public/GCVariant.h
js/src/moz.build
new file mode 100644
--- /dev/null
+++ b/js/public/GCVariant.h
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "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>();
+        if (thing)
+            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>();
+            if (thing)
+                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...>;
+
+    // Variants do not provide initial(). They do not have a default initial
+    // value and one must be provided.
+
+    static void trace(JSTracer* trc, mozilla::Variant<Ts...>* v, const char* name) {
+        Impl::trace(trc, v, name);
+    }
+};
+
+template <typename Outer, typename... Ts>
+class GCVariantOperations
+{
+    using Impl = detail::GCVariantImplementation<Ts...>;
+    using Variant = mozilla::Variant<Ts...>;
+
+    const Variant& variant() const { return static_cast<const Outer*>(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 Outer, typename... Ts>
+class MutableGCVariantOperations
+  : public GCVariantOperations<Outer, Ts...>
+{
+    using Impl = detail::GCVariantImplementation<Ts...>;
+    using Variant = mozilla::Variant<Ts...>;
+
+    const Variant& variant() const { return static_cast<const Outer*>(this)->get(); }
+    Variant& variant() { return static_cast<Outer*>(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()));
+    }
+};
+
+template <typename... Ts>
+class RootedBase<mozilla::Variant<Ts...>>
+  : public MutableGCVariantOperations<JS::Rooted<mozilla::Variant<Ts...>>, Ts...>
+{ };
+
+template <typename... Ts>
+class MutableHandleBase<mozilla::Variant<Ts...>>
+  : public MutableGCVariantOperations<JS::MutableHandle<mozilla::Variant<Ts...>>, Ts...>
+{ };
+
+template <typename... Ts>
+class HandleBase<mozilla::Variant<Ts...>>
+  : public GCVariantOperations<JS::Handle<mozilla::Variant<Ts...>>, Ts...>
+{ };
+
+template <typename... Ts>
+class PersistentRootedBase<mozilla::Variant<Ts...>>
+  : public MutableGCVariantOperations<JS::PersistentRooted<mozilla::Variant<Ts...>>, Ts...>
+{ };
+
+} // namespace js
+
+#endif // js_GCVariant_h
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -106,16 +106,17 @@ EXPORTS.js += [
     '../public/CharacterEncoding.h',
     '../public/Class.h',
     '../public/Conversions.h',
     '../public/Date.h',
     '../public/Debug.h',
     '../public/GCAPI.h',
     '../public/GCHashTable.h',
     '../public/GCPolicyAPI.h',
+    '../public/GCVariant.h',
     '../public/GCVector.h',
     '../public/HashTable.h',
     '../public/HeapAPI.h',
     '../public/Id.h',
     '../public/Initialization.h',
     '../public/LegacyIntTypes.h',
     '../public/MemoryMetrics.h',
     '../public/Principals.h',