@syntest/cfg
Advanced tools
Comparing version 0.4.1-beta.3 to 0.5.0-beta.0
@@ -0,1 +1,2 @@ | ||
import { Result } from "@syntest/diagnostics"; | ||
import { ControlFlowProgram } from "../ControlFlowProgram"; | ||
@@ -13,4 +14,4 @@ import { ContractedControlFlowGraph } from "../graph/ContractedControlFlowGraph"; | ||
*/ | ||
export declare function edgeContraction(controlFlowGraph: ControlFlowGraph): ContractedControlFlowGraph; | ||
export declare function contractControlFlowProgram(program: ControlFlowProgram): ControlFlowProgram; | ||
export declare function edgeContraction(controlFlowGraph: ControlFlowGraph): Result<ContractedControlFlowGraph>; | ||
export declare function contractControlFlowProgram(program: ControlFlowProgram): Result<ControlFlowProgram>; | ||
//# sourceMappingURL=edgeContraction.d.ts.map |
@@ -21,4 +21,3 @@ "use strict"; | ||
*/ | ||
const logging_1 = require("@syntest/logging"); | ||
const diagnostics_1 = require("../diagnostics"); | ||
const diagnostics_1 = require("@syntest/diagnostics"); | ||
const ContractedControlFlowGraph_1 = require("../graph/ContractedControlFlowGraph"); | ||
@@ -41,6 +40,4 @@ const ControlFlowGraph_1 = require("../graph/ControlFlowGraph"); | ||
const nodeMapping = new Map(); | ||
let changed = false; | ||
// Perform edge contraction until no more changes are made | ||
do { | ||
changed = false; | ||
if (controlFlowGraph.nodes.size === 2) { | ||
@@ -50,3 +47,3 @@ // Only entry and exit nodes left, so we can stop | ||
} | ||
bfs(controlFlowGraph, (edge) => { | ||
const edge = bfs(controlFlowGraph, (edge) => { | ||
return (controlFlowGraph.getOutgoingEdges(edge.source).length === 1 && | ||
@@ -60,13 +57,19 @@ controlFlowGraph.getIncomingEdges(edge.target).length === 1 && | ||
edge.target !== controlFlowGraph.errorExit.id); | ||
}, (edge) => { | ||
controlFlowGraph = mergeNodes(controlFlowGraph, edge.source, edge.target); | ||
if (nodeMapping.has(edge.source)) { | ||
nodeMapping.get(edge.source).push(edge.target); | ||
} | ||
else { | ||
nodeMapping.set(edge.source, [edge.source, edge.target]); | ||
} | ||
changed = true; | ||
}); | ||
} while (changed); | ||
if (edge === undefined) { | ||
// done | ||
break; | ||
} | ||
const result = mergeNodes(controlFlowGraph, edge.source, edge.target); | ||
if ((0, diagnostics_1.isFailure)(result)) | ||
return result; | ||
controlFlowGraph = (0, diagnostics_1.unwrap)(result); | ||
if (nodeMapping.has(edge.source)) { | ||
nodeMapping.get(edge.source).push(edge.target); | ||
} | ||
else { | ||
nodeMapping.set(edge.source, [edge.source, edge.target]); | ||
} | ||
// eslint-disable-next-line no-constant-condition | ||
} while (true); | ||
// safety check | ||
@@ -77,7 +80,8 @@ for (const edge of controlFlowGraph.edges) { | ||
if (outgoingFromSource.length > 1 && incomingToTarget.length > 1) { | ||
(0, logging_1.getLogger)("edgeContration").warn((0, diagnostics_1.shouldNeverHappen)(`Missing placeholder node: \n${edge.source}\n${edge.target}`)); | ||
// throw new Error(shouldNeverHappen(`Missing placeholder node: \n${edge.source}\n${edge.target}`)) | ||
return (0, diagnostics_1.failure)(new diagnostics_1.IllegalStateError("Missing placeholder node between 2 nodes", { | ||
context: { node1: edge.source, node2: edge.target }, | ||
})); | ||
} | ||
} | ||
return new ContractedControlFlowGraph_1.ContractedControlFlowGraph(controlFlowGraph.entry, controlFlowGraph.successExit, controlFlowGraph.errorExit, controlFlowGraph.nodes, controlFlowGraph.edges, original, nodeMapping); | ||
return (0, diagnostics_1.success)(new ContractedControlFlowGraph_1.ContractedControlFlowGraph(controlFlowGraph.entry, controlFlowGraph.successExit, controlFlowGraph.errorExit, controlFlowGraph.nodes, controlFlowGraph.edges, original, nodeMapping)); | ||
} | ||
@@ -87,13 +91,22 @@ exports.edgeContraction = edgeContraction; | ||
function contractControlFlowProgram(program) { | ||
program.graph = edgeContraction(program.graph); | ||
const result = edgeContraction(program.graph); | ||
if ((0, diagnostics_1.isFailure)(result)) | ||
return result; | ||
program.graph = (0, diagnostics_1.unwrap)(result); | ||
for (const f of program.functions) { | ||
f.graph = edgeContraction(f.graph); | ||
const result = edgeContraction(f.graph); | ||
if ((0, diagnostics_1.isFailure)(result)) | ||
return result; | ||
f.graph = (0, diagnostics_1.unwrap)(result); | ||
} | ||
return program; | ||
return (0, diagnostics_1.success)(program); | ||
} | ||
exports.contractControlFlowProgram = contractControlFlowProgram; | ||
function bfs(controlFlowGraph, condition, callback) { | ||
/** | ||
* Perform BFS to find a node with only one incoming and one outgoing edge. | ||
*/ | ||
/** | ||
* BFS to find an edge in the graph that matches the provided condition | ||
* @param controlFlowGraph the graph to search in | ||
* @param condition the condition to match | ||
* @returns the matching edge | ||
*/ | ||
function bfs(controlFlowGraph, condition) { | ||
const queue = []; | ||
@@ -109,17 +122,17 @@ const visited = new Set(); | ||
if (condition(edge)) { | ||
callback(edge); | ||
break; | ||
return edge; | ||
} | ||
queue.push(...controlFlowGraph.getOutgoingEdges(edge.target)); | ||
} | ||
return undefined; | ||
} | ||
function beforeGuards(controlFlowGraph, source, target) { | ||
if (controlFlowGraph.getOutgoingEdges(source).length !== 1) { | ||
throw new Error((0, diagnostics_1.tooManyOutgoing)(source)); | ||
throw new diagnostics_1.IllegalStateError("Cannot merge nodes, node has more than one outgoing edge", { context: { node: source } }); | ||
} | ||
if (controlFlowGraph.getIncomingEdges(target).length !== 1) { | ||
throw new Error((0, diagnostics_1.tooManyIncoming)(target)); | ||
throw new diagnostics_1.IllegalStateError("Cannot merge nodes, node has more than one incoming edge", { context: { node: target } }); | ||
} | ||
if (controlFlowGraph.getOutgoingEdges(source)[0].target !== target) { | ||
throw new Error((0, diagnostics_1.notDirectlyConnected)(source, target)); | ||
throw new diagnostics_1.IllegalStateError("Cannot merge nodes, nodes are not directly connected", { context: { node1: source, node2: target } }); | ||
} | ||
@@ -130,3 +143,3 @@ const isEntry = source === controlFlowGraph.entry.id; | ||
if (isEntry && (isSuccessExit || isErrorExit)) { | ||
throw new Error((0, diagnostics_1.cannotMergeEntryAndExit)()); | ||
throw new diagnostics_1.IllegalStateError("Cannot merge entry and exit nodes"); | ||
} | ||
@@ -136,6 +149,18 @@ } | ||
if (newNodes.size !== controlFlowGraph.nodes.size - 1) { | ||
throw new Error((0, diagnostics_1.exactlyOneNodeShouldBeRemoved)(source, target, newNodes.size - controlFlowGraph.nodes.size)); | ||
throw new diagnostics_1.IllegalStateError("Exactly one node should be removed when merging nodes", { | ||
context: { | ||
mergeNode1: source, | ||
mergeNode2: target, | ||
removedAmount: newNodes.size - controlFlowGraph.nodes.size, | ||
}, | ||
}); | ||
} | ||
if (newEdges.length !== controlFlowGraph.edges.length - 1) { | ||
throw new Error((0, diagnostics_1.exactlyOneEdgeShouldBeRemoved)(source, target, newNodes.size - controlFlowGraph.nodes.size)); | ||
throw new diagnostics_1.IllegalStateError("Exactly one edge should be removed when merging nodes", { | ||
context: { | ||
mergeNode1: source, | ||
mergeNode2: target, | ||
removedAmount: newEdges.length - controlFlowGraph.edges.length, | ||
}, | ||
}); | ||
} | ||
@@ -157,3 +182,9 @@ } | ||
if (removedEdges.length !== 1) { | ||
throw new Error((0, diagnostics_1.exactlyOneEdgeShouldBeRemoved)(source, target, removedEdges.length)); | ||
return (0, diagnostics_1.failure)(new diagnostics_1.IllegalStateError("Exactly one edge should be removed when merging nodes", { | ||
context: { | ||
mergeNode1: source, | ||
mergeNode2: target, | ||
removedAmount: removedEdges.length, | ||
}, | ||
})); | ||
} | ||
@@ -174,4 +205,4 @@ const newEdges = controlFlowGraph.edges | ||
afterGuards(newNodes, newEdges, controlFlowGraph, source, target); | ||
return new ControlFlowGraph_1.ControlFlowGraph(controlFlowGraph.entry, controlFlowGraph.successExit, controlFlowGraph.errorExit, newNodes, newEdges); | ||
return (0, diagnostics_1.success)(new ControlFlowGraph_1.ControlFlowGraph(controlFlowGraph.entry, controlFlowGraph.successExit, controlFlowGraph.errorExit, newNodes, newEdges)); | ||
} | ||
//# sourceMappingURL=edgeContraction.js.map |
@@ -21,3 +21,3 @@ "use strict"; | ||
exports.ContractedControlFlowGraph = void 0; | ||
const diagnostics_1 = require("../diagnostics"); | ||
const diagnostics_1 = require("@syntest/diagnostics"); | ||
const ControlFlowGraph_1 = require("./ControlFlowGraph"); | ||
@@ -37,3 +37,5 @@ /** | ||
if (this._reverseNodeMapping.has(node)) | ||
throw new Error((0, diagnostics_1.duplicateNodeInMappping)()); | ||
throw new diagnostics_1.IllegalArgumentError("Duplicate node found in mapping", { | ||
context: { node: node }, | ||
}); | ||
this._reverseNodeMapping.set(node, key); | ||
@@ -61,3 +63,3 @@ } | ||
if (!this._reverseNodeMapping.has(node)) { | ||
throw new Error((0, diagnostics_1.nodeNotFoundInMapping)(node)); | ||
return undefined; | ||
} | ||
@@ -68,3 +70,3 @@ return this._reverseNodeMapping.get(node); | ||
if (!this._nodeMapping.has(node)) { | ||
throw new Error((0, diagnostics_1.nodeNotFoundInMapping)(node)); | ||
return undefined; | ||
} | ||
@@ -71,0 +73,0 @@ return this._nodeMapping.get(node); |
{ | ||
"name": "@syntest/cfg", | ||
"version": "0.4.1-beta.3", | ||
"version": "0.5.0-beta.0", | ||
"description": "SynTest library for control flow graphs", | ||
@@ -51,3 +51,4 @@ "keywords": [ | ||
"dependencies": { | ||
"@syntest/logging": "^0.2.0-beta.1", | ||
"@syntest/diagnostics": "^0.1.0-beta.0", | ||
"@syntest/logging": "^0.2.0-beta.2", | ||
"lodash.clonedeep": "^4.5.0" | ||
@@ -64,3 +65,3 @@ }, | ||
}, | ||
"gitHead": "62fc32cd877d4f37e335e5e21027b0913d7bcb75" | ||
"gitHead": "3f6b9612c030ffc79d5e79c5c1c126ca816a87a6" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
76258
3
56
854