@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
123 lines • 5.67 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.GraphDiff = exports.GraphDifferenceReport = void 0;
exports.initDiffContext = initDiffContext;
const json_1 = require("./json");
/**
* To be produced by a function differencing two graphs (e.g., {@link DataflowGraph|DFGs} or {@link ControlFlowGraph|CFGs}).
* @see {@link GraphDifferenceReport#isEqual|isEqual} - to check whether the graphs are equal
* @see {@link GraphDifferenceReport#addComment|addComment} - to add comments to the report
* @see {@link GraphDifferenceReport#comments|comments} - to get the attached comments
* @see {@link GraphDifferenceReport#problematic|problematic} - to get the problematic vertices/edges
*/
class GraphDifferenceReport {
_comments = undefined;
_problematic = undefined;
addComment(comment, ...related) {
if (this._comments === undefined) {
this._comments = [comment];
}
else {
this._comments.push(comment);
}
if (related.length > 0) {
if (this._problematic === undefined) {
this._problematic = [...related];
}
else {
this._problematic.push(...related);
}
}
}
comments() {
return this._comments;
}
problematic() {
return this._problematic;
}
isEqual() {
return this._comments === undefined;
}
}
exports.GraphDifferenceReport = GraphDifferenceReport;
/**
* Create the context for differencing two graphs
*/
function initDiffContext(left, right, config) {
return {
left: left.graph,
leftname: left.name,
right: right.graph,
rightname: right.name,
report: new GraphDifferenceReport(),
position: '',
config: {
rightIsSubgraph: false,
leftIsSubgraph: false,
...config
}
};
}
/** The edge-differencing steps shared by the dataflow and the control flow graph diff. */
exports.GraphDiff = {
name: 'GraphDiff',
/** Compares all edges of both graphs vertex by vertex, handing each pair to `diffEdges`. */
outgoingEdges(ctx, diffEdges) {
const lEdges = new Map(ctx.left.edges());
const rEdges = new Map(ctx.right.edges());
if (lEdges.size < rEdges.size && !ctx.config.leftIsSubgraph || lEdges.size > rEdges.size && !ctx.config.rightIsSubgraph) {
ctx.report.addComment(`Detected different number of edges! ${ctx.leftname} has ${lEdges.size} (${JSON.stringify(lEdges, json_1.jsonReplacer)}). ${ctx.rightname} has ${rEdges.size} ${JSON.stringify(rEdges, json_1.jsonReplacer)}`);
}
for (const [id, edge] of lEdges) {
/* This has nothing to do with the subset relation as we verify this in the same graph.
* Yet we still do the check as a subgraph may not have to have all source vertices for edges.
*/
if (!ctx.left.hasVertex(id)) {
if (!ctx.config.leftIsSubgraph) {
ctx.report.addComment(`The source ${id} of edges ${JSON.stringify(edge, json_1.jsonReplacer)} is not present in ${ctx.leftname}. This means that the graph contains an edge but not the corresponding vertex.`);
continue;
}
}
diffEdges(ctx, id, edge, rEdges.get(id));
}
// just to make it both ways in case the length differs
for (const [id, edge] of rEdges) {
if (!ctx.right.hasVertex(id)) {
if (!ctx.config.rightIsSubgraph) {
ctx.report.addComment(`The source ${id} of edges ${JSON.stringify(edge, json_1.jsonReplacer)} is not present in ${ctx.rightname}. This means that the graph contains an edge but not the corresponding vertex.`);
continue;
}
}
if (!ctx.config.leftIsSubgraph && !lEdges.has(id)) {
diffEdges(ctx, id, undefined, edge);
}
/* otherwise, we already cover the edge above */
}
},
/** Compares the outgoing edges of a single vertex, handing each pair of edges to `diffEdge`. */
edges(ctx, id, lEdges, rEdges, diffEdge) {
if (lEdges === undefined || rEdges === undefined) {
if ((lEdges === undefined && !ctx.config.leftIsSubgraph)
|| (rEdges === undefined && !ctx.config.rightIsSubgraph)) {
ctx.report.addComment(`Vertex ${id} has undefined outgoing edges. ${ctx.leftname}: ${JSON.stringify(lEdges, json_1.jsonReplacer)} vs ${ctx.rightname}: ${JSON.stringify(rEdges, json_1.jsonReplacer)}`, { tag: 'vertex', id });
}
return;
}
if (lEdges.size < rEdges.size && !ctx.config.leftIsSubgraph
|| lEdges.size > rEdges.size && !ctx.config.rightIsSubgraph) {
ctx.report.addComment(`Vertex ${id} differs in number of outgoing edges. ${ctx.leftname}: [${[...lEdges.keys()].join(',')}] vs ${ctx.rightname}: [${[...rEdges.keys()].join(',')}] `, { tag: 'vertex', id });
}
// order independent compare
for (const [target, edge] of lEdges) {
const otherEdge = rEdges.get(target);
if (otherEdge === undefined) {
if (!ctx.config.rightIsSubgraph) {
ctx.report.addComment(`Target of ${id}->${target} in ${ctx.leftname} is not present in ${ctx.rightname}`, { tag: 'edge', from: id, to: target });
}
continue;
}
diffEdge(edge, otherEdge, ctx, id, target);
}
}
};
//# sourceMappingURL=diff-graph.js.map