GCVariant.h 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=8 sts=4 et sw=4 tw=99:
  3. * This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. #ifndef js_GCVariant_h
  7. #define js_GCVariant_h
  8. #include "mozilla/Variant.h"
  9. #include "js/GCPolicyAPI.h"
  10. #include "js/RootingAPI.h"
  11. #include "js/TracingAPI.h"
  12. namespace JS {
  13. // These template specializations allow Variant to be used inside GC wrappers.
  14. //
  15. // When matching on GC wrappers around Variants, matching should be done on
  16. // the wrapper itself. The matcher class's methods should take Handles or
  17. // MutableHandles. For example,
  18. //
  19. // struct MyMatcher
  20. // {
  21. // using ReturnType = const char*;
  22. // ReturnType match(HandleObject o) { return "object"; }
  23. // ReturnType match(HandleScript s) { return "script"; }
  24. // };
  25. //
  26. // Rooted<Variant<JSObject*, JSScript*>> v(cx, someScript);
  27. // MyMatcher mm;
  28. // v.match(mm);
  29. //
  30. // If you get compile errors about inability to upcast subclasses (e.g., from
  31. // NativeObject* to JSObject*) and are inside js/src, be sure to also include
  32. // "gc/Policy.h".
  33. namespace detail {
  34. template <typename... Ts>
  35. struct GCVariantImplementation;
  36. // The base case.
  37. template <typename T>
  38. struct GCVariantImplementation<T>
  39. {
  40. template <typename ConcreteVariant>
  41. static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) {
  42. T& thing = v->template as<T>();
  43. if (!mozilla::IsPointer<T>::value || thing)
  44. GCPolicy<T>::trace(trc, &thing, name);
  45. }
  46. template <typename Matcher, typename ConcreteVariant>
  47. static typename Matcher::ReturnType
  48. match(Matcher& matcher, Handle<ConcreteVariant> v) {
  49. const T& thing = v.get().template as<T>();
  50. return matcher.match(Handle<T>::fromMarkedLocation(&thing));
  51. }
  52. template <typename Matcher, typename ConcreteVariant>
  53. static typename Matcher::ReturnType
  54. match(Matcher& matcher, MutableHandle<ConcreteVariant> v) {
  55. T& thing = v.get().template as<T>();
  56. return matcher.match(MutableHandle<T>::fromMarkedLocation(&thing));
  57. }
  58. };
  59. // The inductive case.
  60. template <typename T, typename... Ts>
  61. struct GCVariantImplementation<T, Ts...>
  62. {
  63. using Next = GCVariantImplementation<Ts...>;
  64. template <typename ConcreteVariant>
  65. static void trace(JSTracer* trc, ConcreteVariant* v, const char* name) {
  66. if (v->template is<T>()) {
  67. T& thing = v->template as<T>();
  68. if (!mozilla::IsPointer<T>::value || thing)
  69. GCPolicy<T>::trace(trc, &thing, name);
  70. } else {
  71. Next::trace(trc, v, name);
  72. }
  73. }
  74. template <typename Matcher, typename ConcreteVariant>
  75. static typename Matcher::ReturnType
  76. match(Matcher& matcher, Handle<ConcreteVariant> v) {
  77. if (v.get().template is<T>()) {
  78. const T& thing = v.get().template as<T>();
  79. return matcher.match(Handle<T>::fromMarkedLocation(&thing));
  80. }
  81. return Next::match(matcher, v);
  82. }
  83. template <typename Matcher, typename ConcreteVariant>
  84. static typename Matcher::ReturnType
  85. match(Matcher& matcher, MutableHandle<ConcreteVariant> v) {
  86. if (v.get().template is<T>()) {
  87. T& thing = v.get().template as<T>();
  88. return matcher.match(MutableHandle<T>::fromMarkedLocation(&thing));
  89. }
  90. return Next::match(matcher, v);
  91. }
  92. };
  93. } // namespace detail
  94. template <typename... Ts>
  95. struct GCPolicy<mozilla::Variant<Ts...>>
  96. {
  97. using Impl = detail::GCVariantImplementation<Ts...>;
  98. // Variants do not provide initial(). They do not have a default initial
  99. // value and one must be provided.
  100. static void trace(JSTracer* trc, mozilla::Variant<Ts...>* v, const char* name) {
  101. Impl::trace(trc, v, name);
  102. }
  103. };
  104. } // namespace JS
  105. namespace js {
  106. template <typename Outer, typename... Ts>
  107. class GCVariantOperations
  108. {
  109. using Impl = JS::detail::GCVariantImplementation<Ts...>;
  110. using Variant = mozilla::Variant<Ts...>;
  111. const Variant& variant() const { return static_cast<const Outer*>(this)->get(); }
  112. public:
  113. template <typename T>
  114. bool is() const {
  115. return variant().template is<T>();
  116. }
  117. template <typename T>
  118. JS::Handle<T> as() const {
  119. return Handle<T>::fromMarkedLocation(&variant().template as<T>());
  120. }
  121. template <typename Matcher>
  122. typename Matcher::ReturnType
  123. match(Matcher& matcher) const {
  124. return Impl::match(matcher, JS::Handle<Variant>::fromMarkedLocation(&variant()));
  125. }
  126. };
  127. template <typename Outer, typename... Ts>
  128. class MutableGCVariantOperations
  129. : public GCVariantOperations<Outer, Ts...>
  130. {
  131. using Impl = JS::detail::GCVariantImplementation<Ts...>;
  132. using Variant = mozilla::Variant<Ts...>;
  133. const Variant& variant() const { return static_cast<const Outer*>(this)->get(); }
  134. Variant& variant() { return static_cast<Outer*>(this)->get(); }
  135. public:
  136. template <typename T>
  137. JS::MutableHandle<T> as() {
  138. return JS::MutableHandle<T>::fromMarkedLocation(&variant().template as<T>());
  139. }
  140. template <typename Matcher>
  141. typename Matcher::ReturnType
  142. match(Matcher& matcher) {
  143. return Impl::match(matcher, JS::MutableHandle<Variant>::fromMarkedLocation(&variant()));
  144. }
  145. };
  146. template <typename... Ts>
  147. class RootedBase<mozilla::Variant<Ts...>>
  148. : public MutableGCVariantOperations<JS::Rooted<mozilla::Variant<Ts...>>, Ts...>
  149. { };
  150. template <typename... Ts>
  151. class MutableHandleBase<mozilla::Variant<Ts...>>
  152. : public MutableGCVariantOperations<JS::MutableHandle<mozilla::Variant<Ts...>>, Ts...>
  153. { };
  154. template <typename... Ts>
  155. class HandleBase<mozilla::Variant<Ts...>>
  156. : public GCVariantOperations<JS::Handle<mozilla::Variant<Ts...>>, Ts...>
  157. { };
  158. template <typename... Ts>
  159. class PersistentRootedBase<mozilla::Variant<Ts...>>
  160. : public MutableGCVariantOperations<JS::PersistentRooted<mozilla::Variant<Ts...>>, Ts...>
  161. { };
  162. } // namespace js
  163. #endif // js_GCVariant_h