Files
palemoon27/js/public/UbiNodeBreadthFirst.h
T
roytam1 75be9ceb09 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1240094 - nsDocShell should initialize mUserContextId to nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID, r=smaug (a3a8358561)
- Bug 1142768 - Return the original document URI from ServiceWorkerClient.url; r=bkelly (17892631ab)
- Bug 1233613 - Make RegisterFrameCaptureListener fallible. r=mt (0a68d5f4ac)
- Bug 1233613 - Locate parent document before getting presentation shell for canvas capture. r=mt (47a285deab)
- Bug 866513 - Non-empty MediaStreamTrack labels. r=jib (1b1f364811)
- Bug 1170958 - Don't create owned MediaStreamTracks in MetadataLoaded. r=roc (3f031298fb)
- fix misspatch of Bug 1131802 part 2 (3a1e0d2799)
- trivial fixes (bffbd65448)
- Bug 1218454 - part 2a - fix bootlegging of nsContentUtils.h includes from nsILoadContext.h; r=bz (a587b686cd)
- bit of Bug 1223078 - Release WrappedJS eagerly (1fcb1a490b)
- Bug 1245767 followup - Throw an error when gczeal argument is out of range. r=jonco on IRC (32de34e6b7)
- Bug 1241934 - Remove the unused validategc API; r=jonco (a80436895b)
- Bug 961323 - Add a method for finding shortest retaining paths of `JS::ubi::Node` heap graphs; r=jimb (b2d3735d7d)
- Bug 1240090 - Make owned copies of filenames in JS::ubi::ByFilename. r=jimb (049ab3c6ed)
- Bug 1247412 - Add a reverse method to mozilla::Vector; r=Waldo (ad417ff38b)
- Bug 1247413 - Give JS::ubi::BreadthFirst handlers a non-const reference; r=jimb (9baadfa50e)
- bit of Bug 1246061 (51b6ef81dc)
- Bug 1243198 - Use rvalue references for JS::ubi::ByFilename constructor; r=jimb (5b1dab61a5)
- Bug 1216001 part 1 - Optimize nsRange::IsNodeSelected. r=bz (39aad5f0d4)
- Bug 1216001 part 2 - Optimize nsRange::ExcludeNonSelectableNodes by counting ignorable whitespace text nodes next to an unselectable node as unselectable too. r=bz (c6589b508e)
- Bug 1216001 part 3 - Cache the result of IsSelected() for the duration of painting. r=bz (aa5c000b4c)
- Bug 1188364 - Supress GC while transplanting to prevent compacting GC observing intermediate state r=terrence (6963b58989)
- Bug 1246318 - Make the proxy enumerate trap non-standard. r=efaust (f34d0a7c2a)
- Bug 1254293. Fix dom::GetArrayIndexFromId to actually follow the spec for large indices (i.e. ones that don't fit in in int32_t). r=peterv (b71cffbbbd)
- Bug 1256688 - Change BPH::has to follow [[HasProperty]] for ordinary objects. r=jorendorff (39b8de1a3d)
2023-09-07 10:56:10 +08:00

245 lines
9.5 KiB
C++

/* -*- 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_UbiNodeBreadthFirst_h
#define js_UbiNodeBreadthFirst_h
#include "js/UbiNode.h"
#include "js/Utility.h"
#include "js/Vector.h"
namespace JS {
namespace ubi {
// A breadth-first traversal template for graphs of ubi::Nodes.
//
// No GC may occur while an instance of this template is live.
//
// The provided Handler type should have two members:
//
// typename NodeData;
//
// The value type of |BreadthFirst<Handler>::visited|, the HashMap of
// ubi::Nodes that have been visited so far. Since the algorithm needs a
// hash table like this for its own use anyway, it is simple to let
// Handler store its own metadata about each node in the same table.
//
// For example, if you want to find a shortest path to each node from any
// traversal starting point, your |NodeData| type could record the first
// edge to reach each node, and the node from which it originates. Then,
// when the traversal is complete, you can walk backwards from any node
// to some starting point, and the path recorded will be a shortest path.
//
// This type must have a default constructor. If this type owns any other
// resources, move constructors and assignment operators are probably a
// good idea, too.
//
// bool operator() (BreadthFirst& traversal,
// Node origin, const Edge& edge,
// Handler::NodeData* referentData, bool first);
//
// The visitor function, called to report that we have traversed
// |edge| from |origin|. This is called once for each edge we traverse.
// As this is a breadth-first search, any prior calls to the visitor function
// were for origin nodes not further from the start nodes than |origin|.
//
// |traversal| is this traversal object, passed along for convenience.
//
// |referentData| is a pointer to the value of the entry in
// |traversal.visited| for |edge.referent|; the visitor function can
// store whatever metadata it likes about |edge.referent| there.
//
// |first| is true if this is the first time we have visited an edge
// leading to |edge.referent|. This could be stored in NodeData, but
// the algorithm knows whether it has just created the entry in
// |traversal.visited|, so it passes it along for convenience.
//
// The visitor function may call |traversal.abandonReferent()| if it
// doesn't want to traverse the outgoing edges of |edge.referent|. You can
// use this to limit the traversal to a given portion of the graph: it will
// never visit nodes reachable only through nodes that you have abandoned.
// Note that |abandonReferent| must be called the first time the given node
// is reached; that is, |first| must be true.
//
// The visitor function may call |traversal.stop()| if it doesn't want
// to visit any more nodes at all.
//
// The visitor function may consult |traversal.visited| for information
// about other nodes, but it should not add or remove entries.
//
// The visitor function should return true on success, or false if an
// error occurs. A false return value terminates the traversal
// immediately, and causes BreadthFirst<Handler>::traverse to return
// false.
template<typename Handler>
struct BreadthFirst {
// Construct a breadth-first traversal object that reports the nodes it
// reaches to |handler|. The traversal asserts that no GC happens in its
// runtime during its lifetime.
//
// We do nothing with noGC, other than require it to exist, with a lifetime
// that encloses our own.
BreadthFirst(JSRuntime* rt, Handler& handler, const JS::AutoCheckCannotGC& noGC)
: wantNames(true), rt(rt), visited(), handler(handler), pending(),
traversalBegun(false), stopRequested(false), abandonRequested(false)
{ }
// Initialize this traversal object. Return false on OOM.
bool init() { return visited.init(); }
// Add |node| as a starting point for the traversal. You may add
// as many starting points as you like. Return false on OOM.
bool addStart(Node node) { return pending.append(node); }
// Add |node| as a starting point for the traversal (see addStart) and also
// add it to the |visited| set. Return false on OOM.
bool addStartVisited(Node node) {
typename NodeMap::AddPtr ptr = visited.lookupForAdd(node);
if (!ptr && !visited.add(ptr, node, typename Handler::NodeData()))
return false;
return addStart(node);
}
// True if the handler wants us to compute edge names; doing so can be
// expensive in time and memory. True by default.
bool wantNames;
// Traverse the graph in breadth-first order, starting at the given
// start nodes, applying |handler::operator()| for each edge traversed
// as described above.
//
// This should be called only once per instance of this class.
//
// Return false on OOM or error return from |handler::operator()|.
bool traverse()
{
MOZ_ASSERT(!traversalBegun);
traversalBegun = true;
// While there are pending nodes, visit them.
while (!pending.empty()) {
Node origin = pending.front();
pending.popFront();
// Get a range containing all origin's outgoing edges.
auto range = origin.edges(rt, wantNames);
if (!range)
return false;
// Traverse each edge.
for (; !range->empty(); range->popFront()) {
MOZ_ASSERT(!stopRequested);
Edge& edge = range->front();
typename NodeMap::AddPtr a = visited.lookupForAdd(edge.referent);
bool first = !a;
if (first) {
// This is the first time we've reached |edge.referent|.
// Mark it as visited.
if (!visited.add(a, edge.referent, typename Handler::NodeData()))
return false;
}
MOZ_ASSERT(a);
// Report this edge to the visitor function.
if (!handler(*this, origin, edge, &a->value(), first))
return false;
if (stopRequested)
return true;
// Arrange to traverse this edge's referent's outgoing edges
// later --- unless |handler| asked us not to.
if (abandonRequested) {
// Skip the enqueue; reset flag for future iterations.
abandonRequested = false;
} else if (first) {
if (!pending.append(edge.referent))
return false;
}
}
}
return true;
}
// Stop traversal, and return true from |traverse| without visiting any
// more nodes. Only |handler::operator()| should call this function; it
// may do so to stop the traversal early, without returning false and
// then making |traverse|'s caller disambiguate that result from a real
// error.
void stop() { stopRequested = true; }
// Request that the current edge's referent's outgoing edges not be
// traversed. This must be called the first time that referent is reached.
// Other edges *to* that referent will still be traversed.
void abandonReferent() { abandonRequested = true; }
// The runtime with which we were constructed.
JSRuntime* rt;
// A map associating each node N that we have reached with a
// Handler::NodeData, for |handler|'s use. This is public, so that
// |handler| can access it to see the traversal thus far.
using NodeMap = js::HashMap<Node, typename Handler::NodeData, js::DefaultHasher<Node>,
js::SystemAllocPolicy>;
NodeMap visited;
private:
// Our handler object.
Handler& handler;
// A queue template. Appending and popping the front are constant time.
// Wasted space is never more than some recent actual population plus the
// current population.
template <typename T>
class Queue {
js::Vector<T, 0, js::SystemAllocPolicy> head, tail;
size_t frontIndex;
public:
Queue() : head(), tail(), frontIndex(0) { }
bool empty() { return frontIndex >= head.length(); }
T& front() {
MOZ_ASSERT(!empty());
return head[frontIndex];
}
void popFront() {
MOZ_ASSERT(!empty());
frontIndex++;
if (frontIndex >= head.length()) {
head.clearAndFree();
head.swap(tail);
frontIndex = 0;
}
}
bool append(const T& elt) {
return frontIndex == 0 ? head.append(elt) : tail.append(elt);
}
};
// A queue of nodes that we have reached, but whose outgoing edges we
// have not yet traversed. Nodes reachable in fewer edges are enqueued
// earlier.
Queue<Node> pending;
// True if our traverse function has been called.
bool traversalBegun;
// True if we've been asked to stop the traversal.
bool stopRequested;
// True if we've been asked to abandon the current edge's referent.
bool abandonRequested;
};
} // namespace ubi
} // namespace JS
#endif // js_UbiNodeBreadthFirst_h