Chromium Code Reviews| Index: compiler/java/com/google/dart/compiler/ast/CoverageInstrumenter.java |
| =================================================================== |
| --- compiler/java/com/google/dart/compiler/ast/CoverageInstrumenter.java (revision 0) |
| +++ compiler/java/com/google/dart/compiler/ast/CoverageInstrumenter.java (revision 0) |
| @@ -0,0 +1,730 @@ |
| +package com.google.dart.compiler.ast; |
| + |
| +import java.math.BigInteger; |
| +import java.net.URI; |
| +import java.util.ArrayList; |
| +import java.util.HashMap; |
| +import java.util.HashSet; |
| +import java.util.List; |
| +import java.util.Map; |
| +import java.util.Set; |
| + |
| +import com.google.dart.compiler.common.SourceInfo; |
| +import com.google.dart.compiler.CommandLineOptions.CompilerOptions; |
| +import com.google.dart.compiler.CompilerConfiguration; |
| + |
| +/** |
| + * CoverageInstrumenter contains specialized instrumenters to perform specific |
| + * instrumentation for obtaining coverage of different program entities |
| + */ |
| +public class CoverageInstrumenter { |
| + private List<BaseInstrumenter> instrumenters; |
| + private static final String[] ignoredLibs = { "corelib", "corelib_impl", |
| + "dom", "html", "htmlimpl", "base", "touch", "view", "utilslib", |
| + "observable", "layout.dart", "unittest", "dartest" }; |
| + |
| + // Only createInstance should be used to instantiate this class |
| + private CoverageInstrumenter() { |
| + } |
| + |
| + /** |
| + * Method to instantiate a CoverageInstrumenter containing multiple |
| + * instrumenters created based on command line flags. |
| + * |
| + * @param config |
| + * @return instance of this class |
| + */ |
| + public static CoverageInstrumenter createInstance( |
| + CompilerConfiguration config) { |
| + |
| + CompilerOptions compilerOptions = config.getCompilerOptions(); |
| + String coverageTypes = compilerOptions.getCoverageType(); |
| + |
| + CoverageInstrumenter instr = new CoverageInstrumenter(); |
| + if (!"".equals(coverageTypes)) { |
| + String outDir = compilerOptions.getWorkDirectory() |
| + .getAbsolutePath(); |
| + instr.createInstrumenters(coverageTypes, outDir); |
| + } |
| + return instr; |
| + } |
| + |
| + /** |
| + * Performs instrumentation of all classes |
| + * |
| + * @param libraries |
| + */ |
| + public void process(Map<URI, LibraryUnit> libraries) { |
| + |
| + if (instrumenters == null) { |
| + return; |
| + } |
| + |
| + // Instrument all dart units |
| + for (LibraryUnit lib : libraries.values()) { |
| + for (DartUnit unit : lib.getUnits()) { |
| + exec(unit); |
| + } |
| + } |
| + |
| + // Populate totals and initialize coverage |
| + for (LibraryUnit lib : libraries.values()) { |
| + for (DartUnit unit : lib.getUnits()) { |
| + init(unit); |
| + } |
| + } |
| + } |
| + |
| + /** |
| + * Runs all instrumenters on a particular Dart unit |
| + * |
| + * @param unit |
| + */ |
| + private void exec(DartUnit unit) { |
| + if (isIgnored(unit)) { |
| + return; |
| + } |
| + System.out.println("Instrumenting " + unit.getSourceName() + ", lib:" |
|
zundel
2011/12/16 20:49:05
this looks like debugging... for a real dart prog
|
| + + unit.getLibrary().getName()); |
| + for (BaseInstrumenter instrumenter : instrumenters) { |
| + instrumenter.accept(unit); |
| + } |
| + } |
| + |
| + /** |
| + * Ignores a Dart unit based on the library it belongs to |
| + * |
| + * @param unit |
| + * @return true is the unit should be ignored |
| + */ |
| + private boolean isIgnored(DartUnit unit) { |
| + LibraryUnit lu = unit.getLibrary(); |
| + if (lu != null) { |
| + String libName = lu.getName(); |
| + for (String ignoredLib : ignoredLibs) { |
| + if (ignoredLib.equals(libName)) { |
| + return true; |
| + } |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + /** |
| + * Runs the TotalSettingInstrumenter on the supplied Dart unit, which adds |
| + * totals of all coverable program entities |
| + * |
| + * @param unit |
| + */ |
| + private void init(DartUnit unit) { |
| + if (isIgnored(unit)) { |
| + return; |
| + } |
| + new TotalSettingInstrumenter().accept(unit); |
| + } |
| + |
| + /** |
| + * Factory method to create instrumenters based on supplied command line |
| + * configuration |
| + * |
| + * @param coverageTypes |
| + * @param outDir |
| + */ |
| + private void createInstrumenters(String coverageTypes, String outDir) { |
| + BaseInstrumenter.setOutDir(outDir); |
| + instrumenters = new ArrayList<BaseInstrumenter>(); |
| + if (coverageTypes.length() > 0) { |
| + for (String covType : coverageTypes.split(",")) { |
| + if ("all".equals(covType)) { |
|
zundel
2011/12/16 20:49:05
instead of inlined strings, I'd make constants out
|
| + instrumenters.add(new StatementInstrumenter()); |
| + instrumenters.add(new FunctionInstrumenter()); |
| + instrumenters.add(new BranchInstrumenter()); |
| + } |
| + if ("statement".equals(covType)) { |
| + instrumenters.add(new StatementInstrumenter()); |
|
zundel
2011/12/16 20:49:05
is it going to cause problems if you add the same
|
| + } |
| + if ("function".equals(covType)) { |
| + instrumenters.add(new FunctionInstrumenter()); |
| + } |
| + if ("branch".equals(covType)) { |
| + instrumenters.add(new BranchInstrumenter()); |
| + } |
| + } |
| + } |
| + } |
| + |
| + /** |
| + * The Base Instrumenter class that provides some common functionalities to |
| + * all Instrumenters |
| + */ |
| + private static class BaseInstrumenter extends DartModVisitor { |
|
zundel
2011/12/16 20:49:05
FYI, we've been trying to replace instances of Dar
|
| + private static String outputDir; |
| + protected DartUnit currentUnit; |
| + protected static Set<String> unitsVisited = new HashSet<String>(); |
| + protected static Map<String, Integer> numFunctionsMap = |
| + new HashMap<String, Integer>(), |
|
zundel
2011/12/16 20:49:05
style nit: don't use the comma for followon declar
|
| + numStatementsMap = new HashMap<String, Integer>(), |
| + numBranchesMap = new HashMap<String, Integer>(); |
| + |
| + public static void setOutDir(String outDir) { |
| + outputDir = outDir; |
| + } |
| + |
| + @Override |
| + public boolean visit(DartUnit x, DartContext ctx) { |
| + currentUnit = x; |
| + unitsVisited.add(x.getSourceName()); |
| + return super.visit(x, ctx); |
| + } |
| + |
| + /** |
| + * Prepend a function call to a Dart block, which is a body of a |
| + * function. |
| + * |
| + * @param oldBody |
| + * @param functionStmt |
| + * @return |
| + */ |
| + protected DartBlock prependToFnBody(DartBlock oldBody, |
| + DartExprStmt functionStmt) { |
| + List<DartStatement> blockStmts = new ArrayList<DartStatement>(); |
| + blockStmts.add(functionStmt); |
| + if (oldBody != null) { |
| + List<DartStatement> stmts = oldBody.getStatements(); |
| + if (stmts != null) { |
| + blockStmts.addAll(stmts); |
| + } |
| + } |
| + DartBlock newDB = new DartBlock(blockStmts); |
| + if (oldBody != null) { |
| + newDB.setSourceInfo(oldBody.getSourceInfo()); |
| + } |
| + return newDB; |
| + } |
| + |
| + protected void replaceFunction(DartContext ctx, DartFunction x, |
| + DartBlock newFnBody) { |
| + DartFunction newFn = new DartFunction(x.getParams(), newFnBody, |
| + x.getReturnTypeNode()); |
| + newFn.setSourceInfo(x.getSourceInfo()); |
| + ctx.replaceMe(newFn); |
| + } |
| + |
| + protected DartExprStmt createFunctionCall(String functionName, |
| + List<DartExpression> args, DartNode oldNode) { |
| + DartIdentifier funcName = new DartIdentifier(functionName); |
| + DartUnqualifiedInvocation funcInvocation = new DartUnqualifiedInvocation( |
| + funcName, args); |
| + DartExprStmt functionStmt = new DartExprStmt(funcInvocation); |
| + if (oldNode != null) { |
| + SourceInfo sourceInfo = oldNode.getSourceInfo(); |
| + setInstrumentedSourceInfo(functionStmt, sourceInfo); |
| + setInstrumentedSourceInfo(funcInvocation, sourceInfo); |
| + setInstrumentedSourceInfo(funcName, sourceInfo); |
| + for (DartExpression arg : args) { |
| + setInstrumentedSourceInfo(arg, sourceInfo); |
| + } |
| + } |
| + return functionStmt; |
| + } |
| + |
| + void setInstrumentedSourceInfo(DartNode node, SourceInfo info) { |
| + node.setInstrumentedNode(true); |
| + node.setSourceInfo(info); |
| + } |
| + } |
| + |
| + /** |
| + * TotalSettingInstrumenter instruments the main function to initialize |
| + * coverage variables and set totals for covered entities |
| + */ |
| + private class TotalSettingInstrumenter extends BaseInstrumenter { |
| + |
| + @Override |
| + public boolean visit(DartFunction x, DartContext ctx) { |
| + DartNode parent = x.getParent(); |
| + if (parent instanceof DartMethodDefinition) { |
| + String funcName = ((DartMethodDefinition) parent).getName() |
| + .toString(); |
| + if ("main".equals(funcName)) { |
| + DartBlock newFnBody = x.getBody(); |
| + |
| + for (String unitName : unitsVisited) { |
| + int numFunctions = nullCheckingUnBox(numFunctionsMap |
| + .get(unitName)); |
| + int numStatements = nullCheckingUnBox(numStatementsMap |
| + .get(unitName)); |
| + int numBranches = nullCheckingUnBox(numBranchesMap |
| + .get(unitName)); |
| + List<DartExpression> args = new ArrayList<DartExpression>(); |
| + args.add(DartStringLiteral.get(unitName)); |
| + args.add(DartIntegerLiteral.get(BigInteger |
| + .valueOf(numFunctions))); |
| + args.add(DartIntegerLiteral.get(BigInteger |
| + .valueOf(numStatements))); |
| + args.add(DartIntegerLiteral.get(BigInteger |
| + .valueOf(numBranches))); |
| + DartExprStmt callCovTotals = createFunctionCall( |
| + "setCoverageTotals", args, newFnBody); |
| + callCovTotals.setInstrumentedNode(true); |
| + newFnBody = prependToFnBody(newFnBody, callCovTotals); |
| + } |
| + |
| + replaceFunction(ctx, x, newFnBody); |
| + } |
| + } |
| + return super.visit(x, ctx); |
| + } |
| + |
| + int nullCheckingUnBox(Integer value) { |
| + int ret = 0; |
| + if (value != null) { |
| + ret = value.intValue(); |
| + } |
| + return ret; |
| + } |
| + } |
| + |
| + /** |
| + * FunctionInstrumenter adds instrumentation to track function coverage |
| + */ |
| + private class FunctionInstrumenter extends BaseInstrumenter { |
| + |
| + private int numFunctions; |
| + |
| + /** |
| + * This visitor method transforms: |
| + * |
| + * myFunction(){ stmts; ... } |
| + * |
| + * to: |
| + * |
| + * myFunction(){ coverFunction('unit.dart', 'myFunction'); stmts; ... } |
|
zundel
2011/12/16 20:49:05
You aren't covering closures here. Is that intenti
|
| + */ |
| + @Override |
| + public boolean visit(DartFunction x, DartContext ctx) { |
| + DartNode parent = x.getParent(); |
| + if (parent instanceof DartMethodDefinition) { |
| + String funcName = ((DartMethodDefinition) parent).getName() |
| + .toString(); |
| + DartBlock oldBody = x.getBody(); |
| + List<DartExpression> covArgs = new ArrayList<DartExpression>(); |
| + covArgs.add(DartStringLiteral.get(currentUnit.getSourceName())); |
| + covArgs.add(DartStringLiteral.get(funcName)); |
| + DartExprStmt callCovFunc = createFunctionCall("coverFunction", |
| + covArgs, oldBody); |
| + DartBlock newFnBody = prependToFnBody(oldBody, callCovFunc); |
| + replaceFunction(ctx, x, newFnBody); |
| + numFunctions++; |
| + } |
| + return super.visit(x, ctx); |
| + } |
| + |
| + /** |
| + * Initialize function counter |
| + */ |
| + @Override |
| + public boolean visit(DartUnit x, DartContext ctx) { |
| + numFunctions = 0; |
| + return super.visit(x, ctx); |
| + } |
| + |
| + /** |
| + * Populate number of functions for current unit |
| + */ |
| + @Override |
| + public void endVisit(DartUnit x, DartContext ctx) { |
| + numFunctionsMap.put(currentUnit.getSourceName(), numFunctions); |
| + super.endVisit(x, ctx); |
| + } |
| + |
| + } |
| + |
| + /** |
| + * StatementInstrumenter adds instrumentation before each statement to |
| + * capture runtime coverage of that statement |
| + */ |
| + private class StatementInstrumenter extends BaseInstrumenter { |
| + private int numStatements; |
| + |
| + /** |
| + * Initialize statement counter |
| + */ |
| + @Override |
| + public boolean visit(DartUnit x, DartContext ctx) { |
| + numStatements = 0; |
| + return super.visit(x, ctx); |
| + } |
| + |
| + /** |
| + * Populate number of statements for current unit |
| + */ |
| + @Override |
| + public void endVisit(DartUnit x, DartContext ctx) { |
| + numStatementsMap.put(currentUnit.getSourceName(), numStatements); |
| + super.endVisit(x, ctx); |
| + } |
| + |
| + @Override |
| + public boolean visit(DartBlock x, DartContext ctx) { |
|
zundel
2011/12/16 20:49:05
it would be nice to add a big fat comment like you
|
| + List<DartStatement> newStmts = new ArrayList<DartStatement>(); |
| + for (DartStatement stmt : x.getStatements()) { |
| + if (!stmt.isInstrumentedNode()) { |
| + List<DartExpression> covArgs = new ArrayList<DartExpression>(); |
| + covArgs.add(DartStringLiteral.get(currentUnit |
| + .getSourceName())); |
| + covArgs.add(DartIntegerLiteral.get(BigInteger.valueOf(stmt |
| + .getSourceLine()))); |
| + DartExprStmt callCoverStmt = createFunctionCall( |
| + "coverStatement", covArgs, stmt); |
| + newStmts.add(callCoverStmt); |
| + } |
| + newStmts.add(stmt); |
| + numStatements++; |
| + } |
| + DartBlock newBlock = new DartBlock(newStmts); |
| + ctx.replaceMe(newBlock); |
| + return super.visit(x, ctx); |
| + } |
| + |
| + } |
| + |
| + /** |
| + * BranchInstrumenter captures runtime execution of each branch in the |
| + * program by adding instrumentation at each branch point |
| + */ |
| + private class BranchInstrumenter extends BaseInstrumenter { |
| + private int numBranches; |
| + |
| + @Override |
| + public boolean visit(DartUnit x, DartContext ctx) { |
| + numBranches = 0; |
| + return super.visit(x, ctx); |
| + } |
| + |
| + @Override |
| + public void endVisit(DartUnit x, DartContext ctx) { |
| + numBranchesMap.put(currentUnit.getSourceName(), numBranches); |
| + super.endVisit(x, ctx); |
| + } |
| + |
| + /** |
| + * Post-order instrumentation is needed to handle nested ifs and |
| + * synthetic else |
| + */ |
| + @Override |
| + public void endVisit(DartIfStatement x, DartContext ctx) { |
|
zundel
2011/12/16 20:49:05
same comment as above - I'd appreciate seeing how
|
| + DartIfStatement newIfStmt; |
| + DartStatement thenStmt = x.getThenStatement(); |
| + DartStatement elseStmt = x.getElseStatement(); |
| + |
| + List<DartStatement> newThen = doCallCoverBranch(thenStmt); |
| + numBranches++; |
| + if (thenStmt instanceof DartBlock) { |
| + List<DartStatement> stmts = ((DartBlock) thenStmt) |
| + .getStatements(); |
| + if (stmts != null) { |
| + newThen.addAll(stmts); |
| + } |
| + } else { |
| + newThen.add(thenStmt); |
| + } |
| + |
| + if (elseStmt instanceof DartIfStatement) { |
| + newIfStmt = new DartIfStatement(x.getCondition(), |
| + new DartBlock(newThen), elseStmt); |
| + } else { |
| + List<DartStatement> newElse; |
| + if (elseStmt != null) { |
| + newElse = doCallCoverBranch(elseStmt); |
| + if (elseStmt instanceof DartBlock) { |
| + List<DartStatement> stmts = ((DartBlock) elseStmt) |
| + .getStatements(); |
| + if (stmts != null) { |
| + newElse.addAll(stmts); |
| + } |
| + } else { |
| + newElse.add(elseStmt); |
| + } |
| + } else { |
| + // Although else block is missing, there is a branch which |
| + // doesn't cover the IfStatement |
| + // So, we need to add a synthetic else block to track that |
| + newElse = doCallCoverBranch(x); |
| + } |
| + newIfStmt = new DartIfStatement(x.getCondition(), |
| + new DartBlock(newThen), new DartBlock(newElse)); |
| + numBranches++; |
| + } |
| + newIfStmt.setSourceInfo(x.getSourceInfo()); |
| + ctx.replaceMe(newIfStmt); |
| + super.endVisit(x, ctx); |
| + } |
| + |
| + /** |
| + * This method instruments all non-empty switch case blocks and the |
| + * default block. It inserts a synthetic default statement if one |
| + * doesn't exist. |
| + * |
| + * For tracking branch on empty cases, this logic won't work directly |
| + * since instrumenting empty cases will throw a FallThroughError at |
| + * runtime. For achieving this, the switch needs to be transformed into |
| + * an if. Its slightly complicated but here is what I think the |
| + * transformation should be: |
| + * |
| + * switch(expr) { |
| + * case 'a': // empty switch case |
| + * case 'b': do1(); break; |
|
zundel
2011/12/16 20:49:05
I didn't think you needed a break stmt in dart.
|
| + * case 'c': do2(); break; |
| + * default: doDefault(); |
| + * } |
| + * |
| + * should be transformed to: |
| + * |
| + * var tmp = expr; |
| + * if( (tmp == 'a' && coverBranch(..)) || (tmp == 'b' && |
| + * coverBranch(..))) { |
| + * do1(); |
| + * } else if (tmp == 'a' && coverBranch(..)) { |
| + * do2(); |
| + * } else { |
| + * coverBranch(..); |
| + * doDefault(); |
| + * } |
| + * |
| + */ |
| + @Override |
| + public void endVisit(DartSwitchStatement x, DartContext ctx) { |
| + List<DartSwitchMember> newSwitchMembers = |
| + new ArrayList<DartSwitchMember>(); |
| + boolean hasDefault = false; |
| + for (DartSwitchMember swMember : x.getMembers()) { |
| + List<DartStatement> stmts = doCallCoverBranch(swMember); |
| + List<DartStatement> oldStmts = swMember.getStatements(); |
| + if (oldStmts == null || oldStmts.size() == 0) { |
| + newSwitchMembers.add(swMember); |
| + continue; // Ignore empty cases |
| + } else { |
| + stmts.addAll(oldStmts); |
| + } |
| + |
| + assert (swMember instanceof DartCase || |
| + swMember instanceof DartDefault); |
| + |
| + DartSwitchMember newMember; |
| + if (swMember instanceof DartCase) { |
| + newMember = new DartCase(((DartCase) swMember).getExpr(), |
| + swMember.getLabel(), stmts); |
| + } else { |
| + hasDefault = true; |
| + newMember = new DartDefault(swMember.getLabel(), stmts); |
| + } |
| + setInstrumentedSourceInfo(newMember, swMember); |
| + newSwitchMembers.add(newMember); |
| + numBranches++; |
| + } |
| + |
| + if (!hasDefault) { |
| + List<DartStatement> statements = doCallCoverBranch(x); |
| + DartSwitchMember defaultMember = new DartDefault(null, |
| + statements); |
| + newSwitchMembers.add(defaultMember); |
| + numBranches++; |
| + } |
| + |
| + DartSwitchStatement newSwitch = new DartSwitchStatement( |
| + x.getExpression(), newSwitchMembers); |
| + newSwitch.setSourceInfo(x.getSourceInfo()); |
| + |
| + ctx.replaceMe(newSwitch); |
| + super.visit(x, ctx); |
| + } |
| + |
| + @Override |
| + public void endVisit(DartTryStatement x, DartContext ctx) { |
| + DartBlock tryBlock = x.getTryBlock(), finallyBlock = x |
| + .getFinallyBlock(); |
| + List<DartCatchBlock> catchBlocks = x.getCatchBlocks(); |
| + |
| + List<DartStatement> tryStmts = doCallCoverBranch(tryBlock); |
| + if (tryBlock != null && tryBlock.getStatements() != null) { |
| + tryStmts.addAll(tryBlock.getStatements()); |
| + } |
| + DartBlock newTryBlock = new DartBlock(tryStmts); |
| + setInstrumentedSourceInfo(newTryBlock, tryBlock.getSourceInfo()); |
| + numBranches++; |
| + |
| + List<DartCatchBlock> newCatchBlocks = null; |
| + if (catchBlocks != null) { |
| + newCatchBlocks = new ArrayList<DartCatchBlock>(); |
| + for (DartCatchBlock cBlock : catchBlocks) { |
| + DartBlock oldBlock = cBlock.getBlock(); |
| + |
| + List<DartStatement> cBlockStmts = doCallCoverBranch(cBlock); |
| + if (oldBlock != null && oldBlock.getStatements() != null) { |
| + cBlockStmts.addAll(oldBlock.getStatements()); |
| + } |
| + DartBlock newBlock = new DartBlock(cBlockStmts); |
| + setInstrumentedSourceInfo(newBlock, |
| + oldBlock.getSourceInfo()); |
| + |
| + DartCatchBlock newCatchBlock = new DartCatchBlock(newBlock, |
| + cBlock.getException(), cBlock.getStackTrace()); |
| + setInstrumentedSourceInfo(newCatchBlock, |
| + cBlock.getSourceInfo()); |
| + |
| + newCatchBlocks.add(newCatchBlock); |
| + numBranches++; |
| + } |
| + } |
| + |
| + DartBlock newFinallyBlock = null; |
| + if(finallyBlock != null){ |
| + List<DartStatement> finallyStmts = doCallCoverBranch(finallyBlock); |
| + if(finallyBlock.getStatements() != null){ |
| + finallyStmts.addAll(finallyBlock.getStatements()); |
| + } |
| + newFinallyBlock = new DartBlock(finallyStmts); |
| + setInstrumentedSourceInfo(newFinallyBlock, |
| + finallyBlock.getSourceInfo()); |
| + } else { |
| + List<DartStatement> finallyStmts = doCallCoverBranch(x); |
| + newFinallyBlock = new DartBlock(finallyStmts); |
| + setInstrumentedSourceInfo(newFinallyBlock, x.getSourceInfo()); |
| + } |
| + numBranches++; |
| + |
| + DartTryStatement newTry = new DartTryStatement(tryBlock, |
| + newCatchBlocks, newFinallyBlock); |
| + ctx.replaceMe(newTry); |
| + super.endVisit(x, ctx); |
| + } |
| + |
| + /** |
| + * Rewrite all loops in this block |
| + */ |
| + @Override |
| + public void endVisit(DartBlock x, DartContext ctx) { |
| + List<DartStatement> blockStmts = new ArrayList<DartStatement>(); |
| + for(DartStatement stmt : x.getStatements()) { |
| + if(stmt instanceof DartForInStatement) { |
| + doLoopBefore(stmt, blockStmts); |
| + |
| + DartForInStatement oldForIn = (DartForInStatement) stmt; |
| + DartStatement setup = null; |
| + if(oldForIn.introducesVariable()){ |
| + setup = oldForIn.getVariableStatement(); |
| + } else { |
| + setup = new DartExprStmt(oldForIn.getIdentifier()); |
| + } |
| + DartBlock body = doLoopInside(stmt, oldForIn.getBody()); |
| + DartForInStatement newForIn = |
| + new DartForInStatement(setup, oldForIn.getIterable(), body); |
| + setInstrumentedSourceInfo(newForIn, oldForIn.getSourceInfo()); |
| + |
| + doLoopAfter(stmt, blockStmts); |
| + numBranches+=2; |
| + } else if (stmt instanceof DartForStatement) { |
| + doLoopBefore(stmt, blockStmts); |
| + |
| + DartForStatement oldFor = (DartForStatement) stmt; |
| + DartBlock body = doLoopInside(stmt, oldFor.getBody()); |
| + DartForStatement newFor = new DartForStatement(oldFor.getInit(), |
| + oldFor.getCondition(), oldFor.getIncrement(), body); |
| + setInstrumentedSourceInfo(newFor, oldFor.getSourceInfo()); |
| + |
| + doLoopAfter(stmt, blockStmts); |
| + numBranches+=2; |
| + } else if (stmt instanceof DartWhileStatement) { |
| + doLoopBefore(stmt, blockStmts); |
| + |
| + DartWhileStatement oldWhile = (DartWhileStatement) stmt; |
| + DartBlock body = doLoopInside(stmt, oldWhile.getBody()); |
| + DartWhileStatement newWhile = |
| + new DartWhileStatement(oldWhile.getCondition(), body); |
| + setInstrumentedSourceInfo(newWhile, oldWhile.getSourceInfo()); |
| + |
| + doLoopAfter(stmt, blockStmts); |
| + numBranches+=2; |
| + } else if (stmt instanceof DartDoWhileStatement) { |
| + doLoopBefore(stmt, blockStmts); |
| + |
| + DartDoWhileStatement oldDoWhile = (DartDoWhileStatement) stmt; |
| + DartBlock body = doLoopInside(stmt, oldDoWhile.getBody()); |
| + DartDoWhileStatement newDoWhile = |
| + new DartDoWhileStatement(oldDoWhile.getCondition(), body); |
| + setInstrumentedSourceInfo(newDoWhile, oldDoWhile.getSourceInfo()); |
| + |
| + doLoopAfter(stmt, blockStmts); |
| + numBranches+=2; |
| + } else { |
| + blockStmts.add(stmt); |
| + } |
| + } |
| + |
| + super.visit(x, ctx); |
| + } |
| + |
| + /** |
| + * Adds function call to track coverage of a branch represented by |
| + * blockNode |
| + * |
| + * @param blockNode |
| + * @return |
| + */ |
| + private List<DartStatement> doCallCoverBranch(DartNode blockNode) { |
| + List<DartStatement> newBlockStmts = new ArrayList<DartStatement>(); |
| + List<DartExpression> covArgs = makeCoverageArgs(blockNode); |
| + DartExprStmt callCoverBranch = createFunctionCall("coverBranch", |
| + covArgs, blockNode); |
| + newBlockStmts.add(callCoverBranch); |
| + setInstrumentedSourceInfo(callCoverBranch, |
| + blockNode.getSourceInfo()); |
| + return newBlockStmts; |
| + } |
| + |
| + private void doLoopBefore(DartNode loopNode, List<DartStatement> stmts) { |
| + List<DartExpression> args = makeCoverageArgs(loopNode); |
| + DartExprStmt callLoopBefore = |
| + createFunctionCall("loopBranchBefore", args, loopNode); |
| + stmts.add(callLoopBefore); |
| + } |
| + |
| + private void doLoopAfter(DartNode loopNode, List<DartStatement> stmts) { |
| + List<DartExpression> args = makeCoverageArgs(loopNode); |
| + args.add(DartIntegerLiteral.get(BigInteger.valueOf( |
| + loopNode.getSourceStart() + loopNode.getSourceLength()))); |
| + DartExprStmt callLoopAfter = |
| + createFunctionCall("coverLoopBranch", args, loopNode); |
| + stmts.add(callLoopAfter); |
| + } |
| + |
| + private DartBlock doLoopInside(DartNode loopNode, DartStatement body) { |
| + List<DartStatement> newBlockStmts = new ArrayList<DartStatement>(); |
| + List<DartExpression> covArgs = makeCoverageArgs(loopNode); |
| + DartExprStmt callCoverBranch = createFunctionCall("loopBranchInside", |
| + covArgs, loopNode); |
| + newBlockStmts.add(callCoverBranch); |
| + setInstrumentedSourceInfo(callCoverBranch, |
| + loopNode.getSourceInfo()); |
| + |
| + if(body instanceof DartBlock) { |
| + newBlockStmts.addAll(((DartBlock) body).getStatements()); |
| + } else { |
| + newBlockStmts.add(body); |
| + } |
| + |
| + return new DartBlock(newBlockStmts); |
| + } |
| + private List<DartExpression> makeCoverageArgs(DartNode blockNode) { |
| + List<DartExpression> covArgs = new ArrayList<DartExpression>(); |
| + covArgs.add(DartStringLiteral.get(currentUnit.getSourceName())); |
| + covArgs.add(DartIntegerLiteral.get(BigInteger.valueOf(blockNode |
| + .getSourceLine()))); |
| + covArgs.add(DartIntegerLiteral.get(BigInteger.valueOf(blockNode |
| + .getSourceStart()))); |
| + return covArgs; |
| + } |
| + } |
| + |
| +} |