Skip to content

Commit 17a07fb

Browse files
authored
fix: control flow graph (#154)
All loops needed an extra placeholder nodes as they have both an alternate and a break node. This was not possible in the current situation.
1 parent 4dba5dc commit 17a07fb

File tree

3 files changed

+66
-19
lines changed

3 files changed

+66
-19
lines changed

libraries/analysis-javascript/lib/cfg/ControlFlowGraphVisitor.ts

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,11 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
237237
* @param path
238238
* @returns
239239
*/
240-
private _createPlaceholderNode(path: NodePath): Node {
241-
const id = `placeholder:::${this._getPlaceholderNodeId(path)}`;
240+
private _createPlaceholderNode(path: NodePath, double = false): Node {
241+
let id = `placeholder:::${this._getPlaceholderNodeId(path)}`;
242+
if (double) {
243+
id = "placeholder:::" + id;
244+
}
242245
const location = this._getLocation(path);
243246
const node = new Node(
244247
id,
@@ -521,10 +524,15 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
521524
)
522525
);
523526

524-
// exit
527+
// false
525528
this._currentParents = [loopNode.id];
526529
this._edgeType = EdgeType.CONDITIONAL_FALSE;
527-
const loopExit = this._createPlaceholderNode(path);
530+
const alternate = this._createPlaceholderNode(path);
531+
this._connectToParents(alternate);
532+
533+
// exit
534+
this._currentParents = [alternate.id];
535+
const loopExit = this._createPlaceholderNode(path, true);
528536

529537
// connect all break nodes to loop exit
530538
this._currentParents.push(...this._breakNodesStack.pop());
@@ -561,12 +569,11 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
561569
this._continueNodesStack.push(new Set());
562570

563571
// loop
564-
const loopNode = this._createNode(path.get("test")); // or path.get("test") ??
565-
// TODO test
572+
const loopNode = this._createNode(path.get("test"));
566573

567574
this._connectToParents(loopNode);
568575

569-
// body
576+
// true body
570577
this._currentParents = [loopNode.id];
571578
this._edgeType = EdgeType.CONDITIONAL_TRUE;
572579
const beforeSize = this._nodes.size;
@@ -585,10 +592,15 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
585592
this._edgeType = EdgeType.BACK_EDGE;
586593
this._connectToParents(loopNode); // TODO should be label back edge too
587594

588-
// exit
595+
// false
589596
this._currentParents = [loopNode.id];
590597
this._edgeType = EdgeType.CONDITIONAL_FALSE;
591-
const loopExit = this._createPlaceholderNode(path);
598+
const alternate = this._createPlaceholderNode(path);
599+
this._connectToParents(alternate); // TODO should be label back edge too
600+
601+
// exit
602+
this._currentParents = [alternate.id];
603+
const loopExit = this._createPlaceholderNode(path, true);
592604

593605
// connect all break nodes to loop exit
594606
this._currentParents.push(...this._breakNodesStack.pop());
@@ -682,10 +694,15 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
682694
this._edgeType = EdgeType.BACK_EDGE;
683695
this._connectToParents(testNode);
684696

685-
// exit // false
697+
// false
686698
this._currentParents = [testNode.id];
687699
this._edgeType = EdgeType.CONDITIONAL_FALSE;
688-
const loopExit = this._createPlaceholderNode(path);
700+
const alternate = this._createPlaceholderNode(path);
701+
this._connectToParents(alternate);
702+
703+
// exit
704+
this._currentParents = [alternate.id];
705+
const loopExit = this._createPlaceholderNode(path, true);
689706

690707
// connect all break nodes to loop exit
691708
this._currentParents.push(...this._breakNodesStack.pop());
@@ -761,10 +778,15 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
761778
this._edgeType = EdgeType.BACK_EDGE;
762779
this._connectToParents(testNode);
763780

764-
// exit // false
781+
// false
765782
this._currentParents = [testNode.id];
766783
this._edgeType = EdgeType.CONDITIONAL_FALSE;
767-
const loopExit = this._createPlaceholderNode(path);
784+
const alternate = this._createPlaceholderNode(path);
785+
this._connectToParents(alternate);
786+
787+
// exit
788+
this._currentParents = [alternate.id];
789+
const loopExit = this._createPlaceholderNode(path, true);
768790

769791
// connect all break nodes to loop exit
770792
this._currentParents.push(...this._breakNodesStack.pop());
@@ -840,10 +862,15 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
840862
this._edgeType = EdgeType.BACK_EDGE;
841863
this._connectToParents(testNode);
842864

843-
// exit // false
865+
// false
844866
this._currentParents = [testNode.id];
845867
this._edgeType = EdgeType.CONDITIONAL_FALSE;
846-
const loopExit = this._createPlaceholderNode(path);
868+
const alternate = this._createPlaceholderNode(path);
869+
this._connectToParents(alternate);
870+
871+
// exit
872+
this._currentParents = [alternate.id];
873+
const loopExit = this._createPlaceholderNode(path, true);
847874

848875
// connect all break nodes to loop exit
849876
this._currentParents.push(...this._breakNodesStack.pop());

libraries/instrumentation-javascript/lib/instrumentation/Visitor.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,9 @@ function coverLoopBranch(path: NodePath<t.Loop>) {
399399
const T = this.types;
400400

401401
const increment = this.getBranchIncrement(path, branch, undefined);
402+
const index = this.cov.newStatement(path.node.loc, true, true);
403+
const secondIncrement = this.increase("s", index, null);
404+
path.insertAfter(T.expressionStatement(secondIncrement));
402405
path.insertAfter(T.expressionStatement(increment));
403406

404407
// TODO we should actually print what the just defined variable is set to

libraries/instrumentation-javascript/lib/instrumentation/source-coverage.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,28 @@ export class SourceCoverage extends classes.FileCoverage {
9595
};
9696
}
9797

98-
newStatement(loc) {
98+
newStatement(loc, placeholder = false, double = false) {
9999
const s = this.meta.last.s;
100-
this.data.statementMap[s] = this._cloneLocation(loc);
101-
this.data.s[s] = 0;
102-
this.meta.last.s += 1;
100+
101+
if (placeholder) {
102+
const clone = this._cloneLocation({
103+
start: loc.end,
104+
end: loc.end,
105+
});
106+
let id = this._getPlaceholderNodeId(loc);
107+
if (double) {
108+
id = "placeholder:::" + id;
109+
}
110+
clone.id = `placeholder:::${id}`;
111+
this.data.statementMap[s] = clone;
112+
this.data.s[s] = 0;
113+
this.meta.last.s += 1;
114+
} else {
115+
this.data.statementMap[s] = this._cloneLocation(loc);
116+
this.data.s[s] = 0;
117+
this.meta.last.s += 1;
118+
}
119+
103120
return s;
104121
}
105122

0 commit comments

Comments
 (0)