UbiNodePostOrder.h 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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_UbiNodePostOrder_h
  7. #define js_UbiNodePostOrder_h
  8. #include "mozilla/Attributes.h"
  9. #include "mozilla/Maybe.h"
  10. #include "mozilla/Move.h"
  11. #include "jsalloc.h"
  12. #include "js/UbiNode.h"
  13. #include "js/Utility.h"
  14. #include "js/Vector.h"
  15. namespace JS {
  16. namespace ubi {
  17. /**
  18. * A post-order depth-first traversal of `ubi::Node` graphs.
  19. *
  20. * No GC may occur while an instance of `PostOrder` is live.
  21. *
  22. * The `NodeVisitor` type provided to `PostOrder::traverse` must have the
  23. * following member:
  24. *
  25. * bool operator()(Node& node)
  26. *
  27. * The node visitor method. This method is called once for each `node`
  28. * reachable from the start set in post-order.
  29. *
  30. * This visitor function should return true on success, or false if an error
  31. * occurs. A false return value terminates the traversal immediately, and
  32. * causes `PostOrder::traverse` to return false.
  33. *
  34. * The `EdgeVisitor` type provided to `PostOrder::traverse` must have the
  35. * following member:
  36. *
  37. * bool operator()(Node& origin, Edge& edge)
  38. *
  39. * The edge visitor method. This method is called once for each outgoing
  40. * `edge` from `origin` that is reachable from the start set.
  41. *
  42. * NB: UNLIKE NODES, THERE IS NO GUARANTEED ORDER IN WHICH EDGES AND THEIR
  43. * ORIGINS ARE VISITED!
  44. *
  45. * This visitor function should return true on success, or false if an error
  46. * occurs. A false return value terminates the traversal immediately, and
  47. * causes `PostOrder::traverse` to return false.
  48. */
  49. struct PostOrder {
  50. private:
  51. struct OriginAndEdges {
  52. Node origin;
  53. EdgeVector edges;
  54. OriginAndEdges(const Node& node, EdgeVector&& edges)
  55. : origin(node)
  56. , edges(mozilla::Move(edges))
  57. { }
  58. OriginAndEdges(const OriginAndEdges& rhs) = delete;
  59. OriginAndEdges& operator=(const OriginAndEdges& rhs) = delete;
  60. OriginAndEdges(OriginAndEdges&& rhs)
  61. : origin(rhs.origin)
  62. , edges(mozilla::Move(rhs.edges))
  63. {
  64. MOZ_ASSERT(&rhs != this, "self-move disallowed");
  65. }
  66. OriginAndEdges& operator=(OriginAndEdges&& rhs) {
  67. this->~OriginAndEdges();
  68. new (this) OriginAndEdges(mozilla::Move(rhs));
  69. return *this;
  70. }
  71. };
  72. using Stack = js::Vector<OriginAndEdges, 256, js::SystemAllocPolicy>;
  73. using Set = js::HashSet<Node, js::DefaultHasher<Node>, js::SystemAllocPolicy>;
  74. JSContext* cx;
  75. Set seen;
  76. Stack stack;
  77. #ifdef DEBUG
  78. bool traversed;
  79. #endif
  80. private:
  81. MOZ_MUST_USE bool fillEdgesFromRange(EdgeVector& edges, js::UniquePtr<EdgeRange>& range) {
  82. MOZ_ASSERT(range);
  83. for ( ; !range->empty(); range->popFront()) {
  84. if (!edges.append(mozilla::Move(range->front())))
  85. return false;
  86. }
  87. return true;
  88. }
  89. MOZ_MUST_USE bool pushForTraversing(const Node& node) {
  90. EdgeVector edges;
  91. auto range = node.edges(cx, /* wantNames */ false);
  92. return range &&
  93. fillEdgesFromRange(edges, range) &&
  94. stack.append(OriginAndEdges(node, mozilla::Move(edges)));
  95. }
  96. public:
  97. // Construct a post-order traversal object.
  98. //
  99. // The traversal asserts that no GC happens in its runtime during its
  100. // lifetime via the `AutoCheckCannotGC&` parameter. We do nothing with it,
  101. // other than require it to exist with a lifetime that encloses our own.
  102. PostOrder(JSContext* cx, AutoCheckCannotGC&)
  103. : cx(cx)
  104. , seen()
  105. , stack()
  106. #ifdef DEBUG
  107. , traversed(false)
  108. #endif
  109. { }
  110. // Initialize this traversal object. Return false on OOM.
  111. MOZ_MUST_USE bool init() { return seen.init(); }
  112. // Add `node` as a starting point for the traversal. You may add
  113. // as many starting points as you like. Returns false on OOM.
  114. MOZ_MUST_USE bool addStart(const Node& node) {
  115. if (!seen.put(node))
  116. return false;
  117. return pushForTraversing(node);
  118. }
  119. // Traverse the graph in post-order, starting with the set of nodes passed
  120. // to `addStart` and applying `onNode::operator()` for each node in the
  121. // graph and `onEdge::operator()` for each edge in the graph, as described
  122. // above.
  123. //
  124. // This should be called only once per instance of this class.
  125. //
  126. // Return false on OOM or error return from `onNode::operator()` or
  127. // `onEdge::operator()`.
  128. template<typename NodeVisitor, typename EdgeVisitor>
  129. MOZ_MUST_USE bool traverse(NodeVisitor onNode, EdgeVisitor onEdge) {
  130. #ifdef DEBUG
  131. MOZ_ASSERT(!traversed, "Can only traverse() once!");
  132. traversed = true;
  133. #endif
  134. while (!stack.empty()) {
  135. auto& origin = stack.back().origin;
  136. auto& edges = stack.back().edges;
  137. if (edges.empty()) {
  138. if (!onNode(origin))
  139. return false;
  140. stack.popBack();
  141. continue;
  142. }
  143. Edge edge = mozilla::Move(edges.back());
  144. edges.popBack();
  145. if (!onEdge(origin, edge))
  146. return false;
  147. auto ptr = seen.lookupForAdd(edge.referent);
  148. // We've already seen this node, don't follow its edges.
  149. if (ptr)
  150. continue;
  151. // Mark the referent as seen and follow its edges.
  152. if (!seen.add(ptr, edge.referent) ||
  153. !pushForTraversing(edge.referent))
  154. {
  155. return false;
  156. }
  157. }
  158. return true;
  159. }
  160. };
  161. } // namespace ubi
  162. } // namespace JS
  163. #endif // js_UbiNodePostOrder_h