Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2016 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.GwtIncompatible;
import com.google.javascript.jscomp.graph.DiGraph;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/** Instrument branch coverage for javascript. */
@GwtIncompatible("FileInstrumentationData")
public class BranchCoverageInstrumentationCallback extends NodeTraversal.AbstractPostOrderCallback {
private final AbstractCompiler compiler;
private final Map instrumentationData;
private static final String BRANCH_ARRAY_NAME_PREFIX = "JSCompiler_lcov_branch_data_";
/** Returns a string that can be used for the branch coverage data. */
private static String createArrayName(NodeTraversal traversal) {
return BRANCH_ARRAY_NAME_PREFIX
+ CoverageUtil.createIdentifierFromText(traversal.getSourceName());
}
public BranchCoverageInstrumentationCallback(
AbstractCompiler compiler, Map instrumentationData) {
this.compiler = compiler;
this.instrumentationData = instrumentationData;
}
@Override
public void visit(NodeTraversal traversal, Node node, Node parent) {
String fileName = traversal.getSourceName();
// If origin of node is not from sourceFile, do not instrument. This typically occurs when
// polyfill code is injected into the sourceFile AST and this check avoids instrumenting it. We
// avoid instrumentation as this callback does not distinguish between sourceFile code and
// injected code and can result in an error.
if (!Objects.equals(fileName, node.getSourceFileName())) {
return;
}
if (node.isScript()) {
if (instrumentationData.get(fileName) != null) {
Node toAddTo =
node.hasChildren() && node.getFirstChild().isModuleBody() ? node.getFirstChild() : node;
// Add instrumentation code
toAddTo.addChildrenToFront(newHeaderNode(traversal, toAddTo).removeChildren());
compiler.reportChangeToEnclosingScope(node);
instrumentBranchCoverage(traversal, instrumentationData.get(fileName));
}
}
if (node.isIf()) {
ControlFlowGraph cfg = traversal.getControlFlowGraph();
boolean hasDefaultBlock = false;
for (DiGraph.DiGraphEdge outEdge : cfg.getOutEdges(node)) {
if (outEdge.getValue() == ControlFlowGraph.Branch.ON_FALSE) {
Node destination = outEdge.getDestination().getValue();
if (destination != null
&& destination.isBlock()
&& destination.getParent() != null
&& destination.getParent().isIf()) {
hasDefaultBlock = true;
}
break;
}
}
if (!hasDefaultBlock) {
addDefaultBlock(node);
}
if (!instrumentationData.containsKey(fileName)) {
instrumentationData.put(
fileName, new FileInstrumentationData(fileName, createArrayName(traversal)));
}
processBranchInfo(node, instrumentationData.get(fileName), getChildrenBlocks(node));
} else if (NodeUtil.isLoopStructure(node)) {
List blocks = getChildrenBlocks(node);
ControlFlowGraph cfg = traversal.getControlFlowGraph();
for (DiGraph.DiGraphEdge outEdge : cfg.getOutEdges(node)) {
if (outEdge.getValue() == ControlFlowGraph.Branch.ON_FALSE) {
Node destination = outEdge.getDestination().getValue();
if (destination != null && destination.isBlock()) {
blocks.add(destination);
} else {
Node exitBlock = IR.block();
if (destination != null && destination.getParent().isBlock()) {
// When the destination of an outEdge of the CFG is not null and the source node's
// parent is a block. If parent is not a block it may result in an illegal state
// exception
destination.getParent().addChildBefore(exitBlock, destination);
} else {
// When the destination of an outEdge of the CFG is null then we need to add an empty
// block directly after the loop structure that we can later instrument
outEdge
.getSource()
.getValue()
.getParent()
.addChildAfter(exitBlock, outEdge.getSource().getValue());
}
blocks.add(exitBlock);
}
}
}
if (!instrumentationData.containsKey(fileName)) {
instrumentationData.put(
fileName, new FileInstrumentationData(fileName, createArrayName(traversal)));
}
processBranchInfo(node, instrumentationData.get(fileName), blocks);
}
}
private List getChildrenBlocks(Node node) {
List blocks = new ArrayList<>();
for (Node child : node.children()) {
if (child.isBlock()) {
blocks.add(child);
}
}
return blocks;
}
/**
* Add instrumentation code for branch coverage. For each block that correspond to a branch,
* insert an assignment of the branch coverage data to the front of the block.
*/
private void instrumentBranchCoverage(NodeTraversal traversal, FileInstrumentationData data) {
int maxLine = data.maxBranchPresentLine();
int branchCoverageOffset = 0;
for (int lineIdx = 1; lineIdx <= maxLine; ++lineIdx) {
Integer numBranches = data.getNumBranches(lineIdx);
if (numBranches != null) {
for (int branchIdx = 1; branchIdx <= numBranches; ++branchIdx) {
Node block = data.getBranchNode(lineIdx, branchIdx);
block.addChildToFront(
newBranchInstrumentationNode(traversal, block, branchCoverageOffset + branchIdx - 1));
compiler.reportChangeToEnclosingScope(block);
}
branchCoverageOffset += numBranches;
}
}
}
/**
* Create an assignment to the branch coverage data for the given index into the array.
*
* @return the newly constructed assignment node.
*/
private Node newBranchInstrumentationNode(NodeTraversal traversal, Node node, int idx) {
String arrayName = createArrayName(traversal);
// Create instrumentation Node
Node getElemNode = IR.getelem(IR.name(arrayName), IR.number(idx)); // Make line number 0-based
Node exprNode = IR.exprResult(IR.assign(getElemNode, IR.trueNode()));
// Note line as instrumented
String fileName = traversal.getSourceName();
if (!instrumentationData.containsKey(fileName)) {
instrumentationData.put(fileName, new FileInstrumentationData(fileName, arrayName));
}
return exprNode.useSourceInfoIfMissingFromForTree(node);
}
/** Add branch instrumentation information for each block. */
private void processBranchInfo(Node branchNode, FileInstrumentationData data, List blocks) {
int lineNumber = branchNode.getLineno();
data.setBranchPresent(lineNumber);
// Instrument for each block
int numBranches = 0;
for (Node child : blocks) {
data.putBranchNode(lineNumber, numBranches + 1, child);
numBranches++;
}
data.addBranches(lineNumber, numBranches);
}
/** Add a default block for conditional statements, e.g., If, Switch. */
private Node addDefaultBlock(Node node) {
Node defaultBlock = IR.block();
node.addChildToBack(defaultBlock);
return defaultBlock.useSourceInfoIfMissingFromForTree(node);
}
private Node newHeaderNode(NodeTraversal traversal, Node srcref) {
String fileName = traversal.getSourceName();
FileInstrumentationData data = instrumentationData.get(fileName);
checkNotNull(data);
// var JSCompiler_lcov_branch_data_xx = [];
// __jscov['branchesTaken'].push(JSCompiler_lcov_branch_data_xx);
String objName = CoverageInstrumentationPass.JS_INSTRUMENTATION_OBJECT_NAME;
List nodes = new ArrayList<>();
nodes.add(newArrayDeclarationNode(traversal));
nodes.add(
IR.exprResult(
IR.call(
IR.getprop(IR.getelem(IR.name(objName), IR.string("branchesTaken")), "push"),
IR.name(createArrayName(traversal)))));
// __jscov['branchPresent'].push(hex-data);
nodes.add(
IR.exprResult(
IR.call(
IR.getprop(IR.getelem(IR.name(objName), IR.string("branchPresent")), "push"),
IR.string(data.getBranchPresentAsHexString()))));
nodes.add(newBranchesInLineNode("JSCompiler_lcov_branchesInLine", data));
// __jscov['branchesInLine'].push(JSCompiler_lcov_branchesInLine);
nodes.add(
IR.exprResult(
IR.call(
IR.getprop(IR.getelem(IR.name(objName), IR.string("branchesInLine")), "push"),
IR.name("JSCompiler_lcov_branchesInLine"))));
// __jscov['fileNames'].push(filename);
nodes.add(
IR.exprResult(
IR.call(
IR.getprop(IR.getelem(IR.name(objName), IR.string("fileNames")), "push"),
IR.string(fileName))));
return IR.block(nodes).useSourceInfoIfMissingFromForTree(srcref);
}
private Node newArrayDeclarationNode(NodeTraversal traversal) {
return IR.var(IR.name(createArrayName(traversal)), IR.arraylit());
}
private Node newBranchesInLineNode(String name, FileInstrumentationData data) {
List assignments = new ArrayList<>();
// var JSCompiler_lcov_branchesInLine = [];
assignments.add(IR.var(IR.name(name), IR.arraylit()));
int lineWithBranch = 0;
for (int lineIdx = 1; lineIdx <= data.maxBranchPresentLine(); ++lineIdx) {
Integer numBranches = data.getNumBranches(lineIdx);
if (numBranches != null && numBranches > 0) {
// JSCompiler_lcov_branchesInLine[] = 2;
Node assignment =
IR.exprResult(
IR.assign(
IR.getelem(IR.name(name), IR.number(lineWithBranch++)),
IR.number(numBranches)));
assignments.add(assignment.useSourceInfoIfMissingFromForTree(assignment));
}
}
return IR.block(assignments);
}
}