@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
161 lines • 8.34 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.diffOfControlFlowGraphs = diffOfControlFlowGraphs;
const json_1 = require("../util/json");
const diff_graph_1 = require("../util/diff-graph");
const diff_1 = require("../util/diff");
const control_flow_graph_1 = require("./control-flow-graph");
const arrays_1 = require("../util/collections/arrays");
/**
* Compare two control flow graphs and return a report on the differences.
* If you simply want to check whether they equal, use {@link GraphDifferenceReport#isEqual|`<result>.isEqual()`}.
*
* @see {@link diffOfDataflowGraphs} - for dataflow graphs
*/
function diffOfControlFlowGraphs(left, right, config) {
if (left.graph === right.graph) {
return new diff_graph_1.GraphDifferenceReport();
}
const ctx = (0, diff_graph_1.initDiffContext)(left, right, config);
diffDataflowGraphs(ctx);
return ctx.report;
}
function diffDataflowGraphs(ctx) {
diffRootVertices(ctx);
diffVertices(ctx);
diffOutgoingEdges(ctx);
}
function diffRootVertices(ctx) {
(0, diff_1.setDifference)(ctx.left.rootIds(), ctx.right.rootIds(), {
...ctx,
position: `${ctx.position}Root vertices differ in graphs. `
});
}
function diffVertices(ctx) {
const lVert = [...ctx.left.vertices(false)].map(([id, info]) => [id, info]);
const rVert = [...ctx.right.vertices(false)].map(([id, info]) => [id, info]);
if (lVert.length < rVert.length && !ctx.config.leftIsSubgraph
|| lVert.length > rVert.length && !ctx.config.rightIsSubgraph) {
ctx.report.addComment(`Detected different number of vertices! ${ctx.leftname} has ${lVert.length}, ${ctx.rightname} has ${rVert.length}`);
}
for (const [id, lInfo] of lVert) {
const rInfo = ctx.right.getVertex(id, false);
if (rInfo === undefined) {
if (!ctx.config.rightIsSubgraph) {
ctx.report.addComment(`Vertex ${id} is not present in ${ctx.rightname}`, { tag: 'vertex', id });
}
continue;
}
if (lInfo.type !== rInfo.type) {
ctx.report.addComment(`Vertex ${id} differs in tags. ${ctx.leftname}: ${lInfo.type} vs. ${ctx.rightname}: ${rInfo.type}`, {
tag: 'vertex',
id
});
}
if (lInfo.kind !== undefined || rInfo.kind !== undefined) {
if (lInfo.kind !== rInfo.kind) {
ctx.report.addComment(`Vertex ${id} differs in kinds. ${ctx.leftname}: ${String(lInfo.kind)} vs ${ctx.rightname}: ${String(rInfo.kind)}`, {
tag: 'vertex',
id
});
}
}
if (lInfo.callTargets !== undefined || rInfo.callTargets !== undefined) {
(0, diff_1.setDifference)(new Set(lInfo.callTargets ?? []), new Set(rInfo.callTargets ?? []), {
...ctx,
position: `${ctx.position}Vertex ${id} differs in call targets. `
});
}
if (lInfo.elems !== undefined || rInfo.elems !== undefined) {
if (!(0, arrays_1.arrayEqual)((lInfo.elems ?? []), (rInfo.elems ?? []), control_flow_graph_1.equalVertex)) {
ctx.report.addComment(`Vertex ${id} differs in elems.\n ${ctx.leftname}: ${JSON.stringify(lInfo.elems)}\n vs\n ${ctx.rightname}: ${JSON.stringify(rInfo.elems)}`, { tag: 'vertex', id });
}
}
(0, diff_1.setDifference)(new Set(lInfo.mid ?? []), new Set(rInfo.mid ?? []), {
...ctx,
position: `${ctx.position}Vertex ${id} differs in attached mid markers. `
});
(0, diff_1.setDifference)(new Set(lInfo.end ?? []), new Set(rInfo.end ?? []), {
...ctx,
position: `${ctx.position}Vertex ${id} differs in attached end markers. `
});
if (lInfo.root !== rInfo.root) {
ctx.report.addComment(`Vertex ${id} differs in root. ${ctx.leftname}: ${JSON.stringify(lInfo.root)} vs ${ctx.rightname}: ${JSON.stringify(rInfo.root)}`, {
tag: 'vertex',
id
});
}
(0, diff_1.setDifference)(new Set(lInfo.children), new Set(rInfo.children), {
...ctx,
position: `${ctx.position}Vertex ${id} differs in chilren. `
});
}
}
function diffOutgoingEdges(ctx) {
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 */
}
}
function diffEdge(edge, otherEdge, ctx, id, target) {
if (edge.label !== otherEdge.label) {
ctx.report.addComment(`Edge ${id}->${target} differs in labels. ${ctx.leftname}: ${edge.label} vs ${ctx.rightname}: ${otherEdge.label}`, { tag: 'edge', from: id, to: target });
}
if (edge.caused !== otherEdge.caused) {
ctx.report.addComment(`Edge ${id}->${target} differs in caused. ${ctx.leftname}: ${JSON.stringify(edge.caused)} vs ${ctx.rightname}: ${JSON.stringify(otherEdge.caused)}`, { tag: 'edge', from: id, to: target });
}
if (edge.when !== otherEdge.when) {
ctx.report.addComment(`Edge ${id}->${target} differs in when. ${ctx.leftname}: ${JSON.stringify(edge.when)} vs ${ctx.rightname}: ${JSON.stringify(otherEdge.when)}`, { tag: 'edge', from: id, to: target });
}
}
function diffEdges(ctx, id, lEdges, rEdges) {
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-cfg.js.map