UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

166 lines 9.13 kB
"use strict"; 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).entries().map(([id, info]) => [id, info]).toArray(); const rVert = ctx.right.vertices(false).entries().map(([id, info]) => [id, info]).toArray(); 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; } const lType = control_flow_graph_1.CfgVertex.getType(lInfo); const rType = control_flow_graph_1.CfgVertex.getType(rInfo); if (lType !== rType) { ctx.report.addComment(`Vertex ${id} differs in tags. ${ctx.leftname}: ${control_flow_graph_1.CfgVertex.typeToString(lType)} vs. ${ctx.rightname}: ${control_flow_graph_1.CfgVertex.typeToString(rType)}`, { tag: 'vertex', id }); } const lCt = control_flow_graph_1.CfgVertex.getCallTargets(lInfo); const rCt = control_flow_graph_1.CfgVertex.getCallTargets(rInfo); if (lCt !== undefined || rCt !== undefined) { (0, diff_1.setDifference)(new Set(lCt ?? []), new Set(rCt ?? []), { ...ctx, position: `${ctx.position}Vertex ${id} differs in call targets. ` }); } const lElems = control_flow_graph_1.CfgVertex.isBlock(lInfo) ? control_flow_graph_1.CfgVertex.getBasicBlockElements(lInfo) : undefined; const rElems = control_flow_graph_1.CfgVertex.isBlock(rInfo) ? control_flow_graph_1.CfgVertex.getBasicBlockElements(rInfo) : undefined; if (lElems !== undefined || rElems !== undefined) { if (!(0, arrays_1.arrayEqual)((lElems ?? []), (rElems ?? []), control_flow_graph_1.CfgVertex.equal)) { ctx.report.addComment(`Vertex ${id} differs in elems.\n ${ctx.leftname}: ${JSON.stringify(lElems)}\n vs\n ${ctx.rightname}: ${JSON.stringify(rElems)}`, { tag: 'vertex', id }); } } (0, diff_1.setDifference)(new Set(control_flow_graph_1.CfgVertex.getMid(lInfo) ?? []), new Set(control_flow_graph_1.CfgVertex.getMid(rInfo) ?? []), { ...ctx, position: `${ctx.position}Vertex ${id} differs in attached mid markers. ` }); (0, diff_1.setDifference)(new Set(control_flow_graph_1.CfgVertex.getEnd(lInfo) ?? []), new Set(control_flow_graph_1.CfgVertex.getEnd(rInfo) ?? []), { ...ctx, position: `${ctx.position}Vertex ${id} differs in attached end markers. ` }); const lRoot = control_flow_graph_1.CfgVertex.getRootId(lInfo); const rRoot = control_flow_graph_1.CfgVertex.getRootId(rInfo); if (lRoot !== rRoot) { ctx.report.addComment(`Vertex ${id} differs in root. ${ctx.leftname}: ${JSON.stringify(lRoot)} vs ${ctx.rightname}: ${JSON.stringify(rRoot)}`, { tag: 'vertex', id }); } (0, diff_1.setDifference)(new Set(control_flow_graph_1.CfgVertex.getChildren(lInfo)), new Set(control_flow_graph_1.CfgVertex.getChildren(rInfo)), { ...ctx, position: `${ctx.position}Vertex ${id} differs in children. ` }); } } 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) { const el = control_flow_graph_1.CfgEdge.getType(edge); const ol = control_flow_graph_1.CfgEdge.getType(otherEdge); if (el !== ol) { ctx.report.addComment(`Edge ${id}->${target} differs in labels. ${ctx.leftname}: ${el} vs ${ctx.rightname}: ${ol}`, { tag: 'edge', from: id, to: target }); } const ec = control_flow_graph_1.CfgEdge.getCause(edge); const oc = control_flow_graph_1.CfgEdge.getCause(otherEdge); if (ec !== oc) { ctx.report.addComment(`Edge ${id}->${target} differs in caused. ${ctx.leftname}: ${JSON.stringify(ec)} vs ${ctx.rightname}: ${JSON.stringify(oc)}`, { tag: 'edge', from: id, to: target }); } const ew = control_flow_graph_1.CfgEdge.getWhen(edge); const ow = control_flow_graph_1.CfgEdge.getWhen(otherEdge); if (ew !== ow) { ctx.report.addComment(`Edge ${id}->${target} differs in when. ${ctx.leftname}: ${JSON.stringify(ew)} vs ${ctx.rightname}: ${JSON.stringify(ow)}`, { 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