Backed out changeset 09836ef7b0f6 (bug 961323) for bustage on a CLOSED TREE

--HG--
extra : rebase_source : 80a8d58487f1e56624bd06572e09cc07e4404fe2
This commit is contained in:
Carsten "Tomcat" Book 2016-02-12 08:41:24 +01:00
Родитель 6111439998
Коммит b054cb159a
8 изменённых файлов: 3 добавлений и 946 удалений

Просмотреть файл

@ -808,8 +808,6 @@ class Node {
};
};
using NodeSet = js::HashSet<Node, js::DefaultHasher<Node>, js::SystemAllocPolicy>;
using NodeSetPtr = mozilla::UniquePtr<NodeSet, JS::DeletePolicy<NodeSet>>;
/*** Edge and EdgeRange ***************************************************************************/

Просмотреть файл

@ -71,6 +71,8 @@ class JS_PUBLIC_API(DominatorTree)
private:
// Types.
using NodeSet = js::HashSet<Node, js::DefaultHasher<Node>, js::SystemAllocPolicy>;
using NodeSetPtr = mozilla::UniquePtr<NodeSet, JS::DeletePolicy<NodeSet>>;
using PredecessorSets = js::HashMap<Node, NodeSetPtr, js::DefaultHasher<Node>,
js::SystemAllocPolicy>;
using NodeToIndexMap = js::HashMap<Node, uint32_t, js::DefaultHasher<Node>,

Просмотреть файл

@ -1,331 +0,0 @@
/* -*- 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_UbiNodeShortestPaths_h
#define js_UbiNodeShortestPaths_h
#include "mozilla/Maybe.h"
#include "mozilla/Move.h"
#include "jsalloc.h"
#include "js/UbiNodeBreadthFirst.h"
#include "js/Vector.h"
namespace JS {
namespace ubi {
/**
* A back edge along a path in the heap graph.
*/
struct JS_PUBLIC_API(BackEdge)
{
private:
Node predecessor_;
EdgeName name_;
public:
using Ptr = mozilla::UniquePtr<BackEdge, JS::DeletePolicy<BackEdge>>;
BackEdge() : predecessor_(), name_(nullptr) { }
bool init(const Node& predecessor, Edge& edge) {
MOZ_ASSERT(!predecessor_);
MOZ_ASSERT(!name_);
predecessor_ = predecessor;
name_ = mozilla::Move(edge.name);
return true;
}
BackEdge(const BackEdge&) = delete;
BackEdge& operator=(const BackEdge&) = delete;
BackEdge(BackEdge&& rhs)
: predecessor_(rhs.predecessor_)
, name_(mozilla::Move(rhs.name_))
{
MOZ_ASSERT(&rhs != this);
}
BackEdge& operator=(BackEdge&& rhs) {
this->~BackEdge();
new(this) BackEdge(Move(rhs));
return *this;
}
Ptr clone() const;
const EdgeName& name() const { return name_; }
EdgeName& name() { return name_; }
const JS::ubi::Node& predecessor() const { return predecessor_; }
};
/**
* A path is a series of back edges from which we discovered a target node.
*/
using Path = mozilla::Vector<BackEdge*>;
/**
* The `JS::ubi::ShortestPaths` type represents a collection of up to N shortest
* retaining paths for each of a target set of nodes, starting from the same
* root node.
*/
struct JS_PUBLIC_API(ShortestPaths)
{
private:
// Types, type aliases, and data members.
using BackEdgeVector = mozilla::Vector<BackEdge::Ptr>;
using NodeToBackEdgeVectorMap = js::HashMap<Node, BackEdgeVector, js::DefaultHasher<Node>,
js::SystemAllocPolicy>;
struct Handler;
using Traversal = BreadthFirst<Handler>;
/**
* A `JS::ubi::BreadthFirst` traversal handler that records back edges for
* how we reached each node, allowing us to reconstruct the shortest
* retaining paths after the traversal.
*/
struct Handler
{
using NodeData = BackEdge;
ShortestPaths& shortestPaths;
size_t totalMaxPathsToRecord;
size_t totalPathsRecorded;
explicit Handler(ShortestPaths& shortestPaths)
: shortestPaths(shortestPaths)
, totalMaxPathsToRecord(shortestPaths.targets_.count() * shortestPaths.maxNumPaths_)
, totalPathsRecorded(0)
{
}
bool
operator()(Traversal& traversal, JS::ubi::Node origin, JS::ubi::Edge& edge,
BackEdge* back, bool first)
{
MOZ_ASSERT(back);
MOZ_ASSERT(traversal.visited.has(origin));
MOZ_ASSERT(totalPathsRecorded < totalMaxPathsToRecord);
if (first && !back->init(origin, edge))
return false;
if (!shortestPaths.targets_.has(edge.referent))
return true;
// If `first` is true, then we moved the edge's name into `back` in
// the above call to `init`. So clone that back edge to get the
// correct edge name. If `first` is not true, then our edge name is
// still in `edge`. This accounts for the asymmetry between
// `back->clone()` in the first branch, and the `init` call in the
// second branch.
auto ptr = shortestPaths.paths_.lookupForAdd(edge.referent);
if (first) {
MOZ_ASSERT(!ptr);
BackEdgeVector paths;
if (!paths.reserve(shortestPaths.maxNumPaths_))
return false;
auto cloned = back->clone();
if (!cloned)
return false;
paths.infallibleAppend(mozilla::Move(cloned));
if (!shortestPaths.paths_.add(ptr, edge.referent, mozilla::Move(paths)))
return false;
totalPathsRecorded++;
} else if (ptr->value().length() < shortestPaths.maxNumPaths_) {
MOZ_ASSERT(ptr);
BackEdge::Ptr thisBackEdge(js_new<BackEdge>());
if (!thisBackEdge || !thisBackEdge->init(origin, edge))
return false;
ptr->value().infallibleAppend(mozilla::Move(thisBackEdge));
totalPathsRecorded++;
}
MOZ_ASSERT(totalPathsRecorded <= totalMaxPathsToRecord);
if (totalPathsRecorded == totalMaxPathsToRecord)
traversal.stop();
return true;
}
};
// The maximum number of paths to record for each node.
uint32_t maxNumPaths_;
// The root node we are starting the search from.
Node root_;
// The set of nodes we are searching for paths to.
NodeSet targets_;
// The resulting paths.
NodeToBackEdgeVectorMap paths_;
// Need to keep alive the traversal's back edges so we can walk them later
// when the traversal is over when recreating the shortest paths.
Traversal::NodeMap backEdges_;
private:
// Private methods.
ShortestPaths(uint32_t maxNumPaths, const Node& root, NodeSet&& targets)
: maxNumPaths_(maxNumPaths)
, root_(root)
, targets_(mozilla::Move(targets))
, paths_()
, backEdges_()
{
MOZ_ASSERT(maxNumPaths_ > 0);
MOZ_ASSERT(root_);
MOZ_ASSERT(targets_.initialized());
}
bool initialized() const {
return targets_.initialized() &&
paths_.initialized() &&
backEdges_.initialized();
}
public:
// Public methods.
ShortestPaths(ShortestPaths&& rhs)
: maxNumPaths_(rhs.maxNumPaths_)
, root_(rhs.root_)
, targets_(mozilla::Move(rhs.targets_))
, paths_(mozilla::Move(rhs.paths_))
, backEdges_(mozilla::Move(rhs.backEdges_))
{
MOZ_ASSERT(this != &rhs, "self-move is not allowed");
}
ShortestPaths& operator=(ShortestPaths&& rhs) {
this->~ShortestPaths();
new (this) ShortestPaths(mozilla::Move(rhs));
return *this;
}
ShortestPaths(const ShortestPaths&) = delete;
ShortestPaths& operator=(const ShortestPaths&) = delete;
/**
* Construct a new `JS::ubi::ShortestPaths`, finding up to `maxNumPaths`
* shortest retaining paths for each target node in `targets` starting from
* `root`.
*
* The resulting `ShortestPaths` instance must not outlive the
* `JS::ubi::Node` graph it was constructed from.
*
* - For `JS::ubi::Node` graphs backed by the live heap graph, this means
* that the `ShortestPaths`'s lifetime _must_ be contained within the
* scope of the provided `AutoCheckCannotGC` reference because a GC will
* invalidate the nodes.
*
* - For `JS::ubi::Node` graphs backed by some other offline structure
* provided by the embedder, the resulting `ShortestPaths`'s lifetime is
* bounded by that offline structure's lifetime.
*
* Returns `mozilla::Nothing()` on OOM failure. It is the caller's
* responsibility to handle and report the OOM.
*/
static mozilla::Maybe<ShortestPaths>
Create(JSRuntime* rt, AutoCheckCannotGC& noGC, uint32_t maxNumPaths, const Node& root, NodeSet&& targets) {
MOZ_ASSERT(targets.count() > 0);
MOZ_ASSERT(maxNumPaths > 0);
size_t count = targets.count();
ShortestPaths paths(maxNumPaths, root, mozilla::Move(targets));
if (!paths.paths_.init(count))
return mozilla::Nothing();
Handler handler(paths);
Traversal traversal(rt, handler, noGC);
traversal.wantNames = true;
if (!traversal.init() || !traversal.addStartVisited(root) || !traversal.traverse())
return mozilla::Nothing();
// Take ownership of the back edges we created while traversing the
// graph so that we can follow them from `paths_` and don't
// use-after-free.
paths.backEdges_ = mozilla::Move(traversal.visited);
MOZ_ASSERT(paths.initialized());
return mozilla::Some(mozilla::Move(paths));
}
/**
* Get a range that iterates over each target node we searched for retaining
* paths for. The returned range must not outlive the `ShortestPaths`
* instance.
*/
NodeSet::Range eachTarget() const {
MOZ_ASSERT(initialized());
return targets_.all();
}
/**
* Invoke the provided functor/lambda/callable once for each retaining path
* discovered for `target`. The `func` is passed a single `JS::ubi::Path&`
* argument, which contains each edge along the path ordered starting from
* the root and ending at the target, and must not outlive the scope of the
* call.
*
* Note that it is possible that we did not find any paths from the root to
* the given target, in which case `func` will not be invoked.
*/
template <class Func>
bool forEachPath(const Node& target, Func func) {
MOZ_ASSERT(initialized());
MOZ_ASSERT(targets_.has(target));
auto ptr = paths_.lookup(target);
// We didn't find any paths to this target, so nothing to do here.
if (!ptr)
return true;
MOZ_ASSERT(ptr->value().length() <= maxNumPaths_);
Path path;
for (const auto& backEdge : ptr->value()) {
path.clear();
if (!path.append(backEdge.get()))
return false;
Node here = backEdge->predecessor();
MOZ_ASSERT(here);
while (here != root_) {
auto p = backEdges_.lookup(here);
MOZ_ASSERT(p);
if (!path.append(&p->value()))
return false;
here = p->value().predecessor();
MOZ_ASSERT(here);
}
path.reverse();
if (!func(path))
return false;
}
return true;
}
};
} // namespace ubi
} // namespace JS
#endif // js_UbiNodeShortestPaths_h

Просмотреть файл

@ -27,7 +27,6 @@
#include "js/StructuredClone.h"
#include "js/UbiNode.h"
#include "js/UbiNodeBreadthFirst.h"
#include "js/UbiNodeShortestPaths.h"
#include "js/UniquePtr.h"
#include "js/Vector.h"
#include "vm/GlobalObject.h"
@ -2542,177 +2541,6 @@ FindPath(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
ShortestPaths(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, "shortestPaths", 3))
return false;
// We don't ToString non-objects given as 'start' or 'target', because this
// test is all about object identity, and ToString doesn't preserve that.
// Non-GCThing endpoints don't make much sense.
if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
JSDVG_SEARCH_STACK, args[0], nullptr,
"not an object, string, or symbol", nullptr);
return false;
}
if (!args[1].isObject() || !args[1].toObject().is<ArrayObject>()) {
ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
JSDVG_SEARCH_STACK, args[1], nullptr,
"not an array object", nullptr);
return false;
}
RootedArrayObject objs(cx, &args[1].toObject().as<ArrayObject>());
size_t length = objs->getDenseInitializedLength();
if (length == 0) {
ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
JSDVG_SEARCH_STACK, args[1], nullptr,
"not a dense array object with one or more elements", nullptr);
return false;
}
for (size_t i = 0; i < length; i++) {
RootedValue el(cx, objs->getDenseElement(i));
if (!el.isObject() && !el.isString() && !el.isSymbol()) {
ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
JSDVG_SEARCH_STACK, el, nullptr,
"not an object, string, or symbol", nullptr);
return false;
}
}
int32_t maxNumPaths;
if (!JS::ToInt32(cx, args[2], &maxNumPaths))
return false;
if (maxNumPaths <= 0) {
ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
JSDVG_SEARCH_STACK, args[2], nullptr,
"not greater than 0", nullptr);
return false;
}
// We accumulate the results into a GC-stable form, due to the fact that the
// JS::ubi::ShortestPaths lifetime (when operating on the live heap graph)
// is bounded within an AutoCheckCannotGC.
Rooted<GCVector<GCVector<GCVector<Value>>>> values(cx, GCVector<GCVector<GCVector<Value>>>(cx));
Vector<Vector<Vector<JS::ubi::EdgeName>>> names(cx);
{
JS::AutoCheckCannotGC noGC(cx->runtime());
JS::ubi::NodeSet targets;
if (!targets.init()) {
ReportOutOfMemory(cx);
return false;
}
for (size_t i = 0; i < length; i++) {
RootedValue val(cx, objs->getDenseElement(i));
JS::ubi::Node node(val);
if (!targets.put(node)) {
ReportOutOfMemory(cx);
return false;
}
}
JS::ubi::Node root(args[0]);
auto maybeShortestPaths = JS::ubi::ShortestPaths::Create(cx->runtime(), noGC, maxNumPaths,
root, mozilla::Move(targets));
if (maybeShortestPaths.isNothing()) {
ReportOutOfMemory(cx);
return false;
}
auto& shortestPaths = *maybeShortestPaths;
for (size_t i = 0; i < length; i++) {
if (!values.append(GCVector<GCVector<Value>>(cx)) ||
!names.append(Vector<Vector<JS::ubi::EdgeName>>(cx)))
{
return false;
}
RootedValue val(cx, objs->getDenseElement(i));
JS::ubi::Node target(val);
bool ok = shortestPaths.forEachPath(target, [&](JS::ubi::Path& path) {
Rooted<GCVector<Value>> pathVals(cx, GCVector<Value>(cx));
Vector<JS::ubi::EdgeName> pathNames(cx);
for (auto& part : path) {
if (!pathVals.append(part->predecessor().exposeToJS()) ||
!pathNames.append(mozilla::Move(part->name())))
{
return false;
}
}
return values.back().append(mozilla::Move(pathVals.get())) &&
names.back().append(mozilla::Move(pathNames));
});
if (!ok)
return false;
}
}
MOZ_ASSERT(values.length() == names.length());
MOZ_ASSERT(values.length() == length);
RootedArrayObject results(cx, NewDenseFullyAllocatedArray(cx, length));
if (!results)
return false;
results->ensureDenseInitializedLength(cx, 0, length);
for (size_t i = 0; i < length; i++) {
size_t numPaths = values[i].length();
MOZ_ASSERT(names[i].length() == numPaths);
RootedArrayObject pathsArray(cx, NewDenseFullyAllocatedArray(cx, numPaths));
if (!pathsArray)
return false;
pathsArray->ensureDenseInitializedLength(cx, 0, numPaths);
for (size_t j = 0; j < numPaths; j++) {
size_t pathLength = values[i][j].length();
MOZ_ASSERT(names[i][j].length() == pathLength);
RootedArrayObject path(cx, NewDenseFullyAllocatedArray(cx, pathLength));
if (!path)
return false;
path->ensureDenseInitializedLength(cx, 0, pathLength);
for (size_t k = 0; k < pathLength; k++) {
RootedPlainObject part(cx, NewBuiltinClassInstance<PlainObject>(cx));
if (!part)
return false;
RootedValue predecessor(cx, values[i][j][k]);
if (!JS_DefineProperty(cx, part, "predecessor", predecessor, JSPROP_ENUMERATE))
return false;
if (names[i][j][k]) {
RootedString edge(cx, NewStringCopyZ<CanGC>(cx, names[i][j][k].get()));
if (!edge || !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE))
return false;
}
path->setDenseElement(k, ObjectValue(*part));
}
pathsArray->setDenseElement(j, ObjectValue(*path));
}
results->setDenseElement(i, ObjectValue(*pathsArray));
}
args.rval().setObject(*results);
return true;
}
static bool
EvalReturningScope(JSContext* cx, unsigned argc, Value* vp)
{
@ -3722,13 +3550,6 @@ gc::ZealModeHelpText),
" element's edge is the node of the i+1'th array element; the destination of\n"
" the last array element is implicitly |target|.\n"),
JS_FN_HELP("shortestPaths", ShortestPaths, 3, 0,
"shortestPaths(start, targets, maxNumPaths)",
" Return an array of arrays of shortest retaining paths. There is an array of\n"
" shortest retaining paths for each object in |targets|. The maximum number of\n"
" paths in each of those arrays is bounded by |maxNumPaths|. Each element in a\n"
" path is of the form |{ predecessor, edge }|."),
#ifdef DEBUG
JS_FN_HELP("dumpObject", DumpObject, 1, 0,
"dumpObject()",

Просмотреть файл

@ -1,44 +0,0 @@
// The shortestPaths function exists solely to let the fuzzers go to town and
// exercise the code paths it calls into, hence there is nothing to assert here.
//
// The actual behavior of JS::ubi::ShortestPaths is tested in
// js/src/jsapi-tests/testUbiNode.cpp, where we can actually control the
// structure of the heap graph to test specific shapes.
function f(x) {
return x + x;
}
var g = f.bind(null, 5);
var o = {
p: g
};
function dumpPaths(results) {
results = results.map(paths => {
return paths.map(path => {
return path.map(part => {
return {
predecessor: Object.prototype.toString.call(part.predecessor),
edge: part.edge
};
});
});
});
print(JSON.stringify(results, null, 2));
}
print("shortestPaths(this, [Object, f, o.p], 5)");
var paths = shortestPaths(this, [Object, f, o.p], 5);
dumpPaths(paths);
print();
print("shortestPaths(o, [f], 1)")
paths = shortestPaths(o, [f], 1);
dumpPaths(paths);
print();
print("shortestPaths(this, [f], 5)")
paths = shortestPaths(this, [f], 5);
dumpPaths(paths);

Просмотреть файл

@ -6,7 +6,6 @@
#include "js/UbiNode.h"
#include "js/UbiNodeDominatorTree.h"
#include "js/UbiNodePostOrder.h"
#include "js/UbiNodeShortestPaths.h"
#include "jsapi-tests/tests.h"
#include "vm/SavedFrame.h"
@ -24,15 +23,8 @@ struct FakeNode
explicit FakeNode(char name) : name(name), edges() { }
bool addEdgeTo(FakeNode& referent, const char16_t* edgeName = nullptr) {
bool addEdgeTo(FakeNode& referent) {
JS::ubi::Node node(&referent);
if (edgeName) {
auto ownedName = js::DuplicateString(edgeName);
MOZ_RELEASE_ASSERT(ownedName);
return edges.emplaceBack(ownedName.release(), node);
}
return edges.emplaceBack(nullptr, node);
}
};
@ -658,351 +650,3 @@ BEGIN_TEST(test_JS_ubi_Node_scriptFilename)
return true;
}
END_TEST(test_JS_ubi_Node_scriptFilename)
#define LAMBDA_CHECK(cond) \
do { \
if (!(cond)) { \
fprintf(stderr,"%s:%d:CHECK failed: " #cond "\n", __FILE__, __LINE__); \
return false; \
} \
} while (false)
static void
dumpPath(JS::ubi::Path& path)
{
for (size_t i = 0; i < path.length(); i++) {
fprintf(stderr, "path[%llu]->predecessor() = '%c'\n",
(long long unsigned) i,
path[i]->predecessor().as<FakeNode>()->name);
}
}
BEGIN_TEST(test_JS_ubi_ShortestPaths_no_path)
{
// Create the following graph:
//
// .---. .---. .---.
// | a | <--> | c | | b |
// '---' '---' '---'
FakeNode a('a');
FakeNode b('b');
FakeNode c('c');
CHECK(a.addEdgeTo(c));
CHECK(c.addEdgeTo(a));
mozilla::Maybe<JS::ubi::ShortestPaths> maybeShortestPaths;
{
JS::AutoCheckCannotGC noGC(rt);
JS::ubi::NodeSet targets;
CHECK(targets.init());
CHECK(targets.put(&b));
maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a,
mozilla::Move(targets));
}
CHECK(maybeShortestPaths);
auto& paths = *maybeShortestPaths;
size_t numPathsFound = 0;
bool ok = paths.forEachPath(&b, [&](JS::ubi::Path& path) {
numPathsFound++;
dumpPath(path);
return true;
});
CHECK(ok);
CHECK(numPathsFound == 0);
return true;
}
END_TEST(test_JS_ubi_ShortestPaths_no_path)
BEGIN_TEST(test_JS_ubi_ShortestPaths_one_path)
{
// Create the following graph:
//
// .---. .---. .---.
// | a | <--> | c | --> | b |
// '---' '---' '---'
FakeNode a('a');
FakeNode b('b');
FakeNode c('c');
CHECK(a.addEdgeTo(c));
CHECK(c.addEdgeTo(a));
CHECK(c.addEdgeTo(b));
mozilla::Maybe<JS::ubi::ShortestPaths> maybeShortestPaths;
{
JS::AutoCheckCannotGC noGC(rt);
JS::ubi::NodeSet targets;
CHECK(targets.init());
CHECK(targets.put(&b));
maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a,
mozilla::Move(targets));
}
CHECK(maybeShortestPaths);
auto& paths = *maybeShortestPaths;
size_t numPathsFound = 0;
bool ok = paths.forEachPath(&b, [&](JS::ubi::Path& path) {
numPathsFound++;
dumpPath(path);
LAMBDA_CHECK(path.length() == 2);
LAMBDA_CHECK(path[0]->predecessor() == JS::ubi::Node(&a));
LAMBDA_CHECK(path[1]->predecessor() == JS::ubi::Node(&c));
return true;
});
CHECK(ok);
CHECK(numPathsFound == 1);
return true;
}
END_TEST(test_JS_ubi_ShortestPaths_one_path)
BEGIN_TEST(test_JS_ubi_ShortestPaths_multiple_paths)
{
// Create the following graph:
//
// .---.
// .-----| a |-----.
// | '---' |
// V | V
// .---. | .---.
// | b | | | d |
// '---' | '---'
// | | |
// V | V
// .---. | .---.
// | c | | | e |
// '---' V '---'
// | .---. |
// '---->| f |<----'
// '---'
FakeNode a('a');
FakeNode b('b');
FakeNode c('c');
FakeNode d('d');
FakeNode e('e');
FakeNode f('f');
CHECK(a.addEdgeTo(b));
CHECK(a.addEdgeTo(f));
CHECK(a.addEdgeTo(d));
CHECK(b.addEdgeTo(c));
CHECK(c.addEdgeTo(f));
CHECK(d.addEdgeTo(e));
CHECK(e.addEdgeTo(f));
mozilla::Maybe<JS::ubi::ShortestPaths> maybeShortestPaths;
{
JS::AutoCheckCannotGC noGC(rt);
JS::ubi::NodeSet targets;
CHECK(targets.init());
CHECK(targets.put(&f));
maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a,
mozilla::Move(targets));
}
CHECK(maybeShortestPaths);
auto& paths = *maybeShortestPaths;
size_t numPathsFound = 0;
bool ok = paths.forEachPath(&f, [&](JS::ubi::Path& path) {
numPathsFound++;
dumpPath(path);
switch (path.back()->predecessor().as<FakeNode>()->name) {
case 'a': {
LAMBDA_CHECK(path.length() == 1);
break;
}
case 'c': {
LAMBDA_CHECK(path.length() == 3);
LAMBDA_CHECK(path[0]->predecessor() == JS::ubi::Node(&a));
LAMBDA_CHECK(path[1]->predecessor() == JS::ubi::Node(&b));
LAMBDA_CHECK(path[2]->predecessor() == JS::ubi::Node(&c));
break;
}
case 'e': {
LAMBDA_CHECK(path.length() == 3);
LAMBDA_CHECK(path[0]->predecessor() == JS::ubi::Node(&a));
LAMBDA_CHECK(path[1]->predecessor() == JS::ubi::Node(&d));
LAMBDA_CHECK(path[2]->predecessor() == JS::ubi::Node(&e));
break;
}
default: {
// Unexpected path!
LAMBDA_CHECK(false);
}
}
return true;
});
CHECK(ok);
fprintf(stderr, "numPathsFound = %llu\n", (long long unsigned) numPathsFound);
CHECK(numPathsFound == 3);
return true;
}
END_TEST(test_JS_ubi_ShortestPaths_multiple_paths)
BEGIN_TEST(test_JS_ubi_ShortestPaths_more_paths_than_max)
{
// Create the following graph:
//
// .---.
// .-----| a |-----.
// | '---' |
// V | V
// .---. | .---.
// | b | | | d |
// '---' | '---'
// | | |
// V | V
// .---. | .---.
// | c | | | e |
// '---' V '---'
// | .---. |
// '---->| f |<----'
// '---'
FakeNode a('a');
FakeNode b('b');
FakeNode c('c');
FakeNode d('d');
FakeNode e('e');
FakeNode f('f');
CHECK(a.addEdgeTo(b));
CHECK(a.addEdgeTo(f));
CHECK(a.addEdgeTo(d));
CHECK(b.addEdgeTo(c));
CHECK(c.addEdgeTo(f));
CHECK(d.addEdgeTo(e));
CHECK(e.addEdgeTo(f));
mozilla::Maybe<JS::ubi::ShortestPaths> maybeShortestPaths;
{
JS::AutoCheckCannotGC noGC(rt);
JS::ubi::NodeSet targets;
CHECK(targets.init());
CHECK(targets.put(&f));
maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 1, &a,
mozilla::Move(targets));
}
CHECK(maybeShortestPaths);
auto& paths = *maybeShortestPaths;
size_t numPathsFound = 0;
bool ok = paths.forEachPath(&f, [&](JS::ubi::Path& path) {
numPathsFound++;
dumpPath(path);
return true;
});
CHECK(ok);
fprintf(stderr, "numPathsFound = %llu\n", (long long unsigned) numPathsFound);
CHECK(numPathsFound == 1);
return true;
}
END_TEST(test_JS_ubi_ShortestPaths_more_paths_than_max)
BEGIN_TEST(test_JS_ubi_ShortestPaths_multiple_edges_to_target)
{
// Create the following graph:
//
// .---.
// .-----| a |-----.
// | '---' |
// | | |
// |x |y |z
// | | |
// | V |
// | .---. |
// '---->| b |<----'
// '---'
FakeNode a('a');
FakeNode b('b');
CHECK(a.addEdgeTo(b, MOZ_UTF16("x")));
CHECK(a.addEdgeTo(b, MOZ_UTF16("y")));
CHECK(a.addEdgeTo(b, MOZ_UTF16("z")));
mozilla::Maybe<JS::ubi::ShortestPaths> maybeShortestPaths;
{
JS::AutoCheckCannotGC noGC(rt);
JS::ubi::NodeSet targets;
CHECK(targets.init());
CHECK(targets.put(&b));
maybeShortestPaths = JS::ubi::ShortestPaths::Create(rt, noGC, 10, &a,
mozilla::Move(targets));
}
CHECK(maybeShortestPaths);
auto& paths = *maybeShortestPaths;
size_t numPathsFound = 0;
bool foundX = false;
bool foundY = false;
bool foundZ = false;
bool ok = paths.forEachPath(&b, [&](JS::ubi::Path& path) {
numPathsFound++;
dumpPath(path);
LAMBDA_CHECK(path.length() == 1);
LAMBDA_CHECK(path.back()->name());
LAMBDA_CHECK(js_strlen(path.back()->name().get()) == 1);
auto c = uint8_t(path.back()->name().get()[0]);
fprintf(stderr, "Edge name = '%c'\n", c);
switch (c) {
case 'x': {
foundX = true;
break;
}
case 'y': {
foundY = true;
break;
}
case 'z': {
foundZ = true;
break;
}
default: {
// Unexpected edge!
LAMBDA_CHECK(false);
}
}
return true;
});
CHECK(ok);
fprintf(stderr, "numPathsFound = %llu\n", (long long unsigned) numPathsFound);
CHECK(numPathsFound == 3);
CHECK(foundX);
CHECK(foundY);
CHECK(foundZ);
return true;
}
END_TEST(test_JS_ubi_ShortestPaths_multiple_edges_to_target)
#undef LAMBDA_CHECK

Просмотреть файл

@ -136,7 +136,6 @@ EXPORTS.js += [
'../public/UbiNodeCensus.h',
'../public/UbiNodeDominatorTree.h',
'../public/UbiNodePostOrder.h',
'../public/UbiNodeShortestPaths.h',
'../public/UniquePtr.h',
'../public/Utility.h',
'../public/Value.h',
@ -340,7 +339,6 @@ UNIFIED_SOURCES += [
'vm/TypeInference.cpp',
'vm/UbiNode.cpp',
'vm/UbiNodeCensus.cpp',
'vm/UbiNodeShortestPaths.cpp',
'vm/UnboxedObject.cpp',
'vm/Unicode.cpp',
'vm/Value.cpp',

Просмотреть файл

@ -1,31 +0,0 @@
/* -*- 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/. */
#include "js/UbiNodeShortestPaths.h"
#include "jsstr.h"
namespace JS {
namespace ubi {
JS_PUBLIC_API(BackEdge::Ptr)
BackEdge::clone() const
{
BackEdge::Ptr clone(js_new<BackEdge>());
if (!clone)
return nullptr;
clone->predecessor_ = predecessor();
if (name()) {
clone->name_ = js::DuplicateString(name().get());
if (!clone->name_)
return nullptr;
}
return mozilla::Move(clone);
}
} // namespace ubi
} // namespace JS