UbiNodePostOrder.h 5.9 KB

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