Skip to content

Commit

Permalink
feat: default arg branches (#207)
Browse files Browse the repository at this point in the history
Adds the default arg as branches to the CFG and instrumentation
  • Loading branch information
dstallenberg committed Dec 11, 2023
1 parent 0632913 commit 71e1c26
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 13 deletions.
58 changes: 56 additions & 2 deletions libraries/analysis-javascript/lib/cfg/ControlFlowGraphVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,8 +332,9 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
this._edges.push(
this._createEdge(this._nodes.get(parent), node, this._edgeType)
);
this._edgeType = EdgeType.NORMAL;
}

this._edgeType = EdgeType.NORMAL;
}

public Block: (path: NodePath<t.Block>) => void = (path) => {
Expand Down Expand Up @@ -432,6 +433,59 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {
}
};

public AssignmentPattern: (path: NodePath<t.AssignmentPattern>) => void = (
path
) => {
ControlFlowGraphVisitor.LOGGER.debug(
`Entering AssignmentPattern at ${this._getNodeId(path)}`
);

const branchNode = this._createNode(path);
this._connectToParents(branchNode);
this._currentParents = [branchNode.id];

const testNode = this._createNode(path.get("left")); // bit odd because the test is essentially if the argument is given or not
this._connectToParents(testNode);
this._currentParents = [testNode.id];

// consequent
this._edgeType = EdgeType.CONDITIONAL_TRUE;
// there is no consequent since that is the usual case when an argument is give
const consequent = this._createPlaceholderNode(path);
this._connectToParents(consequent);
this._currentParents = [consequent.id];

const consequentNodes = this._currentParents;

// alternate (the default argument)
this._currentParents = [testNode.id];
this._edgeType = EdgeType.CONDITIONAL_FALSE;

const sizeBefore = this._nodesList.length;
path.get("right").visit();

// there either is no alternate or it is a literal
if (sizeBefore === this._nodesList.length) {
if (path.has("right")) {
// this probably means the "right"/default value is a literal (or something else this visitor does not pick up on)
const alternate = this._createNode(path.get("right"));
this._connectToParents(alternate);
this._currentParents = [alternate.id];
} else {
// there is no default value specified?? (should not be possible)
throw new ImplementationError(
"AssignmentPattern does not have a default value"
);
}
}

const alternateNodes = this._currentParents;

this._currentParents = [...alternateNodes, ...consequentNodes];

path.skip();
};

public Conditional: (path: NodePath<t.Conditional>) => void = (path) => {
ControlFlowGraphVisitor.LOGGER.debug(
`Entering IfStatement at ${this._getNodeId(path)}`
Expand Down Expand Up @@ -988,8 +1042,8 @@ export class ControlFlowGraphVisitor extends AbstractSyntaxTreeVisitor {

// alternate
// placeholder
this._edgeType = EdgeType.CONDITIONAL_FALSE;
this._currentParents = [caseTestNode.id];
this._edgeType = EdgeType.CONDITIONAL_FALSE;
const alternateNode = this._createPlaceholderNode(caseNode);
this._connectToParents(alternateNode);
this._currentParents = [alternateNode.id]; // normal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ export type InstrumentationData = {
[id: string]: number;
};
b: {
// 0 is true, 1 is false
[id: string]: [number, number];
// 0 is true, 1 is false (or more if switch for example)
[id: string]: number[];
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import { createHash } from "crypto";

import { SourceCoverage } from "./source-coverage";
import { NodePath } from "@babel/traverse";

const SHA = "sha1";

Expand Down Expand Up @@ -243,7 +244,7 @@ export class VisitState {
]);
}

insertCounter(path, increment) {
insertCounter(path: NodePath, increment) {
const T = this.types;
if (path.isBlockStatement()) {
path.node.body.unshift(T.expressionStatement(increment));
Expand All @@ -270,14 +271,11 @@ export class VisitState {
} /* istanbul ignore else: not expected */ else if (path.isExpression()) {
path.replaceWith(T.sequenceExpression([increment, path.node]));
} else {
console.error(
"Unable to insert counter for node identifierDescription:",
path.node.type
);
console.error("Unable to insert counter for node:", path.node.type);
}
}

insertStatementCounter(path) {
insertStatementCounter(path: NodePath) {
/* istanbul ignore if: paranoid check */
if (!(path.node && path.node.loc)) {
if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,40 @@ function entries(...enter) {
};
}

function coverStatement(path) {
function coverStatement(path: NodePath<t.Statement>) {
this.insertStatementCounter(path);
}

function coverExpression(path: NodePath<t.Expression>) {
if (path.isBinaryExpression()) {
this.insertStatementCounter(path.get("left"));
this.insertStatementCounter(path.get("right"));
}
}

/* istanbul ignore next: no node.js support */
function coverAssignmentPattern(path) {
function coverAssignmentPattern(path: NodePath<t.AssignmentPattern>) {
const index = this.cov.newStatement(path.node.loc);
const statementIncrement = this.increase("s", index, null);

const n = path.node;
const b = this.cov.newBranch("default-arg", n.loc);

const increment = this.getBranchIncrement(path, b, undefined);

const parent = path.getFunctionParent();

const body = parent.get("body");

if (body.isBlockStatement()) {
body.node.body.unshift(
t.expressionStatement(statementIncrement),
t.expressionStatement(increment)
);
} else {
console.error("Unable to process function body node:", path.node.type);
}

this.insertBranchCounter(path, path.get("right"), b);
}

Expand Down Expand Up @@ -667,6 +693,7 @@ const codeVisitor = {
BlockStatement: entries(), // ignore processing only
ExportDefaultDeclaration: entries(), // ignore processing only
ExportNamedDeclaration: entries(), // ignore processing only
Expression: entries(coverExpression),
ClassMethod: entries(coverFunction),
ClassDeclaration: entries(parenthesizedExpressionProp("superClass")),
ClassProperty: entries(coverClassPropDeclarator),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export class JavaScriptRunner implements EncodingRunner<JavaScriptTestCase> {
const traces: Trace[] = [];
for (const branchKey of Object.keys(instrumentationData.branchMap)) {
const branch = instrumentationData.branchMap[branchKey];
const hits = <number[]>instrumentationData.b[branchKey];
const hits = instrumentationData.b[branchKey];
let meta;

if (metaData !== undefined) {
Expand Down

0 comments on commit 71e1c26

Please sign in to comment.